Add Array support for @@species and subclassing
This patch implements @@species, guarded behind the --harmony-species flag, on Arrays. Methods which return an Array will instead return the appropriate instance based on the ArraySpeciesCreate algorithm. The algorithm is implemented in C++ to get access to realm information and to implement some Array methods in C++, but it is also accessed from JavaScript through a new runtime function. A couple interactive Octane runs show no performance regression with the flag turned off, but turning --harmony-species on will surely have a significant regression, as Array methods now heavily use ObjectDefineProperty. BUG=v8:4093 LOG=Y R=adamk,cbruni Review URL: https://codereview.chromium.org/1560763002 Cr-Commit-Position: refs/heads/master@{#33144}
This commit is contained in:
parent
48bc94253f
commit
6e96223750
@ -458,6 +458,14 @@ BUILTIN(ArraySlice) {
|
||||
int relative_end = 0;
|
||||
bool is_sloppy_arguments = false;
|
||||
|
||||
// TODO(littledan): Look up @@species only once, not once here and
|
||||
// again in the JS builtin. Pass the species out?
|
||||
Handle<Object> species;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, species, Object::ArraySpeciesConstructor(isolate, receiver));
|
||||
if (*species != isolate->context()->native_context()->array_function()) {
|
||||
return CallJsIntrinsic(isolate, isolate->array_slice(), args);
|
||||
}
|
||||
if (receiver->IsJSArray()) {
|
||||
DisallowHeapAllocation no_gc;
|
||||
JSArray* array = JSArray::cast(*receiver);
|
||||
@ -543,6 +551,14 @@ BUILTIN(ArraySplice) {
|
||||
if (!maybe_elms_obj.ToHandle(&elms_obj)) {
|
||||
return CallJsIntrinsic(isolate, isolate->array_splice(), args);
|
||||
}
|
||||
// TODO(littledan): Look up @@species only once, not once here and
|
||||
// again in the JS builtin. Pass the species out?
|
||||
Handle<Object> species;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, species, Object::ArraySpeciesConstructor(isolate, receiver));
|
||||
if (*species != isolate->context()->native_context()->array_function()) {
|
||||
return CallJsIntrinsic(isolate, isolate->array_splice(), args);
|
||||
}
|
||||
Handle<JSArray> array = Handle<JSArray>::cast(receiver);
|
||||
DCHECK(!array->map()->is_observed());
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
var AddIndexedProperty;
|
||||
var FLAG_harmony_tolength;
|
||||
var FLAG_harmony_species;
|
||||
var GetIterator;
|
||||
var GetMethod;
|
||||
var GlobalArray = global.Array;
|
||||
@ -48,10 +49,35 @@ utils.Import(function(from) {
|
||||
|
||||
utils.ImportFromExperimental(function(from) {
|
||||
FLAG_harmony_tolength = from.FLAG_harmony_tolength;
|
||||
FLAG_harmony_species = from.FLAG_harmony_species;
|
||||
});
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
|
||||
|
||||
function ArraySpeciesCreate(array, length) {
|
||||
var constructor;
|
||||
if (FLAG_harmony_species) {
|
||||
constructor = %ArraySpeciesConstructor(array);
|
||||
} else {
|
||||
constructor = GlobalArray;
|
||||
}
|
||||
return new constructor(length);
|
||||
}
|
||||
|
||||
|
||||
function DefineIndexedProperty(array, i, value) {
|
||||
if (FLAG_harmony_species) {
|
||||
var result = ObjectDefineProperty(array, i, {
|
||||
value: value, writable: true, configurable: true, enumerable: true
|
||||
});
|
||||
if (!result) throw MakeTypeError(kStrictCannotAssign, i);
|
||||
} else {
|
||||
AddIndexedProperty(array, i, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Global list of arrays visited during toString, toLocaleString and
|
||||
// join invocations.
|
||||
var visited_arrays = new InternalArray();
|
||||
@ -251,7 +277,7 @@ function SparseSlice(array, start_i, del_count, len, deleted_elements) {
|
||||
for (var i = start_i; i < limit; ++i) {
|
||||
var current = array[i];
|
||||
if (!IS_UNDEFINED(current) || i in array) {
|
||||
AddIndexedProperty(deleted_elements, i - start_i, current);
|
||||
DefineIndexedProperty(deleted_elements, i - start_i, current);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -262,7 +288,7 @@ function SparseSlice(array, start_i, del_count, len, deleted_elements) {
|
||||
if (key >= start_i) {
|
||||
var current = array[key];
|
||||
if (!IS_UNDEFINED(current) || key in array) {
|
||||
AddIndexedProperty(deleted_elements, key - start_i, current);
|
||||
DefineIndexedProperty(deleted_elements, key - start_i, current);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -342,9 +368,7 @@ function SimpleSlice(array, start_i, del_count, len, deleted_elements) {
|
||||
var index = start_i + i;
|
||||
if (HAS_INDEX(array, index, is_array)) {
|
||||
var current = array[index];
|
||||
// The spec requires [[DefineOwnProperty]] here, AddIndexedProperty is
|
||||
// close enough (in that it ignores the prototype).
|
||||
AddIndexedProperty(deleted_elements, i, current);
|
||||
DefineIndexedProperty(deleted_elements, i, current);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -759,7 +783,7 @@ function ArraySlice(start, end) {
|
||||
if (end_i > len) end_i = len;
|
||||
}
|
||||
|
||||
var result = [];
|
||||
var result = ArraySpeciesCreate(array, MaxSimple(end_i - start_i, 0));
|
||||
|
||||
if (end_i < start_i) return result;
|
||||
|
||||
@ -861,7 +885,7 @@ function ArraySplice(start, delete_count) {
|
||||
var start_i = ComputeSpliceStartIndex(TO_INTEGER(start), len);
|
||||
var del_count = ComputeSpliceDeleteCount(delete_count, num_arguments, len,
|
||||
start_i);
|
||||
var deleted_elements = [];
|
||||
var deleted_elements = ArraySpeciesCreate(array, del_count);
|
||||
deleted_elements.length = del_count;
|
||||
var num_elements_to_add = num_arguments > 2 ? num_arguments - 2 : 0;
|
||||
|
||||
@ -1201,26 +1225,23 @@ function ArraySort(comparefn) {
|
||||
// The following functions cannot be made efficient on sparse arrays while
|
||||
// preserving the semantics, since the calls to the receiver function can add
|
||||
// or delete elements from the array.
|
||||
function InnerArrayFilter(f, receiver, array, length) {
|
||||
if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
|
||||
|
||||
var accumulator = new InternalArray();
|
||||
var accumulator_length = 0;
|
||||
function InnerArrayFilter(f, receiver, array, length, result) {
|
||||
var result_length = 0;
|
||||
var is_array = IS_ARRAY(array);
|
||||
for (var i = 0; i < length; i++) {
|
||||
if (HAS_INDEX(array, i, is_array)) {
|
||||
var element = array[i];
|
||||
if (%_Call(f, receiver, element, i, array)) {
|
||||
accumulator[accumulator_length++] = element;
|
||||
DefineIndexedProperty(result, result_length, element);
|
||||
result_length++;
|
||||
}
|
||||
}
|
||||
}
|
||||
var result = new GlobalArray();
|
||||
%MoveArrayContents(accumulator, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
function ArrayFilter(f, receiver) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.filter");
|
||||
|
||||
@ -1228,7 +1249,9 @@ function ArrayFilter(f, receiver) {
|
||||
// loop will not affect the looping and side effects are visible.
|
||||
var array = TO_OBJECT(this);
|
||||
var length = TO_LENGTH_OR_UINT32(array.length);
|
||||
return InnerArrayFilter(f, receiver, array, length);
|
||||
if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
|
||||
var result = ArraySpeciesCreate(array, 0);
|
||||
return InnerArrayFilter(f, receiver, array, length, result);
|
||||
}
|
||||
|
||||
|
||||
@ -1307,23 +1330,6 @@ function ArrayEvery(f, receiver) {
|
||||
}
|
||||
|
||||
|
||||
function InnerArrayMap(f, receiver, array, length) {
|
||||
if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
|
||||
|
||||
var accumulator = new InternalArray(length);
|
||||
var is_array = IS_ARRAY(array);
|
||||
for (var i = 0; i < length; i++) {
|
||||
if (HAS_INDEX(array, i, is_array)) {
|
||||
var element = array[i];
|
||||
accumulator[i] = %_Call(f, receiver, element, i, array);
|
||||
}
|
||||
}
|
||||
var result = new GlobalArray();
|
||||
%MoveArrayContents(accumulator, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
function ArrayMap(f, receiver) {
|
||||
CHECK_OBJECT_COERCIBLE(this, "Array.prototype.map");
|
||||
|
||||
@ -1331,7 +1337,16 @@ function ArrayMap(f, receiver) {
|
||||
// loop will not affect the looping and side effects are visible.
|
||||
var array = TO_OBJECT(this);
|
||||
var length = TO_LENGTH_OR_UINT32(array.length);
|
||||
return InnerArrayMap(f, receiver, array, length);
|
||||
if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
|
||||
var result = ArraySpeciesCreate(array, length);
|
||||
var is_array = IS_ARRAY(array);
|
||||
for (var i = 0; i < length; i++) {
|
||||
if (HAS_INDEX(array, i, is_array)) {
|
||||
var element = array[i];
|
||||
DefineIndexedProperty(result, i, %_Call(f, receiver, element, i, array));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@ -1948,7 +1963,6 @@ utils.Export(function(to) {
|
||||
to.InnerArrayIndexOf = InnerArrayIndexOf;
|
||||
to.InnerArrayJoin = InnerArrayJoin;
|
||||
to.InnerArrayLastIndexOf = InnerArrayLastIndexOf;
|
||||
to.InnerArrayMap = InnerArrayMap;
|
||||
to.InnerArrayReduce = InnerArrayReduce;
|
||||
to.InnerArrayReduceRight = InnerArrayReduceRight;
|
||||
to.InnerArraySome = InnerArraySome;
|
||||
|
@ -173,19 +173,6 @@ function PostNatives(utils) {
|
||||
"ErrorToString",
|
||||
"GetIterator",
|
||||
"GetMethod",
|
||||
"InnerArrayEvery",
|
||||
"InnerArrayFilter",
|
||||
"InnerArrayForEach",
|
||||
"InnerArrayIndexOf",
|
||||
"InnerArrayJoin",
|
||||
"InnerArrayLastIndexOf",
|
||||
"InnerArrayMap",
|
||||
"InnerArrayReduce",
|
||||
"InnerArrayReduceRight",
|
||||
"InnerArrayReverse",
|
||||
"InnerArraySome",
|
||||
"InnerArraySort",
|
||||
"InnerArrayToLocaleString",
|
||||
"IsNaN",
|
||||
"MakeError",
|
||||
"MakeTypeError",
|
||||
|
@ -29,7 +29,6 @@ var InnerArrayIncludes;
|
||||
var InnerArrayIndexOf;
|
||||
var InnerArrayJoin;
|
||||
var InnerArrayLastIndexOf;
|
||||
var InnerArrayMap;
|
||||
var InnerArrayReduce;
|
||||
var InnerArrayReduceRight;
|
||||
var InnerArraySome;
|
||||
@ -80,7 +79,6 @@ utils.Import(function(from) {
|
||||
InnerArrayIndexOf = from.InnerArrayIndexOf;
|
||||
InnerArrayJoin = from.InnerArrayJoin;
|
||||
InnerArrayLastIndexOf = from.InnerArrayLastIndexOf;
|
||||
InnerArrayMap = from.InnerArrayMap;
|
||||
InnerArrayReduce = from.InnerArrayReduce;
|
||||
InnerArrayReduceRight = from.InnerArrayReduceRight;
|
||||
InnerArraySome = from.InnerArraySome;
|
||||
@ -493,12 +491,19 @@ function TypedArrayFill(value, start, end) {
|
||||
|
||||
|
||||
// ES6 draft 07-15-13, section 22.2.3.9
|
||||
function TypedArrayFilter(predicate, thisArg) {
|
||||
function TypedArrayFilter(f, thisArg) {
|
||||
if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
|
||||
|
||||
var length = %_TypedArrayGetLength(this);
|
||||
var array = InnerArrayFilter(predicate, thisArg, this, length);
|
||||
return ConstructTypedArrayLike(this, array);
|
||||
if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
|
||||
var result = new InternalArray();
|
||||
InnerArrayFilter(f, thisArg, this, length, result);
|
||||
var captured = result.length;
|
||||
var output = ConstructTypedArrayLike(this, captured);
|
||||
for (var i = 0; i < captured; i++) {
|
||||
output[i] = result[i];
|
||||
}
|
||||
return output;
|
||||
}
|
||||
%FunctionSetLength(TypedArrayFilter, 1);
|
||||
|
||||
@ -592,14 +597,17 @@ function TypedArrayLastIndexOf(element, index) {
|
||||
|
||||
|
||||
// ES6 draft 07-15-13, section 22.2.3.18
|
||||
function TypedArrayMap(predicate, thisArg) {
|
||||
function TypedArrayMap(f, thisArg) {
|
||||
if (!%_IsTypedArray(this)) throw MakeTypeError(kNotTypedArray);
|
||||
|
||||
// TODO(littledan): Preallocate rather than making an intermediate
|
||||
// InternalArray, for better performance.
|
||||
var length = %_TypedArrayGetLength(this);
|
||||
var array = InnerArrayMap(predicate, thisArg, this, length);
|
||||
return ConstructTypedArrayLike(this, array);
|
||||
var result = ConstructTypedArrayLike(this, length);
|
||||
if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
|
||||
for (var i = 0; i < length; i++) {
|
||||
var element = this[i];
|
||||
result[i] = %_Call(f, thisArg, element, i, this);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
%FunctionSetLength(TypedArrayMap, 1);
|
||||
|
||||
|
@ -423,6 +423,8 @@ class CallSite {
|
||||
T(SloppyLexical, \
|
||||
"Block-scoped declarations (let, const, function, class) not yet " \
|
||||
"supported outside strict mode") \
|
||||
T(SpeciesNotConstructor, \
|
||||
"object.constructor[Symbol.species] is not a constructor") \
|
||||
T(StrictDelete, "Delete of an unqualified identifier in strict mode.") \
|
||||
T(StrictEvalArguments, "Unexpected eval or arguments in strict mode") \
|
||||
T(StrictFunction, \
|
||||
|
@ -1605,6 +1605,56 @@ bool Object::SameValueZero(Object* other) {
|
||||
}
|
||||
|
||||
|
||||
MaybeHandle<Object> Object::ArraySpeciesConstructor(
|
||||
Isolate* isolate, Handle<Object> original_array) {
|
||||
Handle<Context> native_context = isolate->native_context();
|
||||
if (!FLAG_harmony_species) {
|
||||
return Handle<Object>(native_context->array_function(), isolate);
|
||||
}
|
||||
Handle<Object> constructor = isolate->factory()->undefined_value();
|
||||
Maybe<bool> is_array = Object::IsArray(original_array);
|
||||
MAYBE_RETURN_NULL(is_array);
|
||||
if (is_array.FromJust()) {
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
isolate, constructor,
|
||||
Object::GetProperty(original_array,
|
||||
isolate->factory()->constructor_string()),
|
||||
Object);
|
||||
if (constructor->IsConstructor()) {
|
||||
Handle<Context> constructor_context;
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
isolate, constructor_context,
|
||||
JSReceiver::GetFunctionRealm(Handle<JSReceiver>::cast(constructor)),
|
||||
Object);
|
||||
if (*constructor_context != *native_context &&
|
||||
*constructor == constructor_context->array_function()) {
|
||||
constructor = isolate->factory()->undefined_value();
|
||||
}
|
||||
}
|
||||
if (constructor->IsJSReceiver()) {
|
||||
ASSIGN_RETURN_ON_EXCEPTION(
|
||||
isolate, constructor,
|
||||
Object::GetProperty(constructor,
|
||||
isolate->factory()->species_symbol()),
|
||||
Object);
|
||||
if (constructor->IsNull()) {
|
||||
constructor = isolate->factory()->undefined_value();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (constructor->IsUndefined()) {
|
||||
return Handle<Object>(native_context->array_function(), isolate);
|
||||
} else {
|
||||
if (!constructor->IsConstructor()) {
|
||||
THROW_NEW_ERROR(isolate,
|
||||
NewTypeError(MessageTemplate::kSpeciesNotConstructor),
|
||||
Object);
|
||||
}
|
||||
return constructor;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Object::ShortPrint(FILE* out) {
|
||||
OFStream os(out);
|
||||
os << Brief(this);
|
||||
|
@ -1346,6 +1346,10 @@ class Object {
|
||||
// by ES6 Map and Set.
|
||||
bool SameValueZero(Object* other);
|
||||
|
||||
// ES6 section 9.4.2.3 ArraySpeciesCreate (part of it)
|
||||
MUST_USE_RESULT static MaybeHandle<Object> ArraySpeciesConstructor(
|
||||
Isolate* isolate, Handle<Object> original_array);
|
||||
|
||||
// Tries to convert an object to an array length. Returns true and sets the
|
||||
// output parameter if it succeeds.
|
||||
inline bool ToArrayLength(uint32_t* index);
|
||||
|
@ -487,5 +487,18 @@ RUNTIME_FUNCTION(Runtime_FastOneByteArrayJoin) {
|
||||
// to a slow path.
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ArraySpeciesConstructor) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, original_array, 0);
|
||||
Handle<Object> constructor;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, constructor,
|
||||
Object::ArraySpeciesConstructor(isolate, original_array));
|
||||
return *constructor;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -50,7 +50,8 @@ namespace internal {
|
||||
F(GetCachedArrayIndex, 1, 1) \
|
||||
F(FixedArrayGet, 2, 1) \
|
||||
F(FixedArraySet, 3, 1) \
|
||||
F(FastOneByteArrayJoin, 2, 1)
|
||||
F(FastOneByteArrayJoin, 2, 1) \
|
||||
F(ArraySpeciesConstructor, 1, 1)
|
||||
|
||||
|
||||
#define FOR_EACH_INTRINSIC_ATOMICS(F) \
|
||||
|
156
test/mjsunit/harmony/array-species.js
Normal file
156
test/mjsunit/harmony/array-species.js
Normal file
@ -0,0 +1,156 @@
|
||||
// Copyright 2015 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-species --harmony-proxies
|
||||
|
||||
// Test the ES2015 @@species feature
|
||||
|
||||
'use strict';
|
||||
|
||||
// Subclasses of Array construct themselves under map, etc
|
||||
|
||||
class MyArray extends Array { }
|
||||
|
||||
assertEquals(MyArray, new MyArray().map(()=>{}).constructor);
|
||||
assertEquals(MyArray, new MyArray().filter(()=>{}).constructor);
|
||||
assertEquals(MyArray, new MyArray().slice().constructor);
|
||||
assertEquals(MyArray, new MyArray().splice().constructor);
|
||||
|
||||
// Subclasses can override @@species to return the another class
|
||||
|
||||
class MyOtherArray extends Array {
|
||||
static get [Symbol.species]() { return MyArray; }
|
||||
}
|
||||
|
||||
assertEquals(MyArray, new MyOtherArray().map(()=>{}).constructor);
|
||||
assertEquals(MyArray, new MyOtherArray().filter(()=>{}).constructor);
|
||||
assertEquals(MyArray, new MyOtherArray().slice().constructor);
|
||||
assertEquals(MyArray, new MyOtherArray().splice().constructor);
|
||||
|
||||
// Array methods on non-arrays return arrays
|
||||
|
||||
class MyNonArray extends Array {
|
||||
static get [Symbol.species]() { return MyObject; }
|
||||
}
|
||||
|
||||
class MyObject { }
|
||||
|
||||
assertEquals(MyObject,
|
||||
Array.prototype.map.call(new MyNonArray(), ()=>{}).constructor);
|
||||
assertEquals(MyObject,
|
||||
Array.prototype.filter.call(new MyNonArray(), ()=>{}).constructor);
|
||||
assertEquals(MyObject,
|
||||
Array.prototype.slice.call(new MyNonArray()).constructor);
|
||||
assertEquals(MyObject,
|
||||
Array.prototype.splice.call(new MyNonArray()).constructor);
|
||||
|
||||
assertEquals(undefined,
|
||||
Array.prototype.map.call(new MyNonArray(), ()=>{}).length);
|
||||
assertEquals(undefined,
|
||||
Array.prototype.filter.call(new MyNonArray(), ()=>{}).length);
|
||||
// slice and splice actually do explicitly define the length for some reason
|
||||
assertEquals(0, Array.prototype.slice.call(new MyNonArray()).length);
|
||||
assertEquals(0, Array.prototype.splice.call(new MyNonArray()).length);
|
||||
|
||||
// Cross-realm Arrays build same-realm arrays
|
||||
|
||||
var realm = Realm.create();
|
||||
assertEquals(Array,
|
||||
Array.prototype.map.call(
|
||||
Realm.eval(realm, "[]"), ()=>{}).constructor);
|
||||
assertFalse(Array === Realm.eval(realm, "[]").map(()=>{}).constructor);
|
||||
assertFalse(Array === Realm.eval(realm, "[].map(()=>{}).constructor"));
|
||||
|
||||
// Defaults when constructor or @@species is missing or non-constructor
|
||||
|
||||
class MyDefaultArray extends Array {
|
||||
static get [Symbol.species]() { return undefined; }
|
||||
}
|
||||
assertEquals(Array, new MyDefaultArray().map(()=>{}).constructor);
|
||||
|
||||
class MyOtherDefaultArray extends Array { }
|
||||
assertEquals(MyOtherDefaultArray,
|
||||
new MyOtherDefaultArray().map(()=>{}).constructor);
|
||||
MyOtherDefaultArray.prototype.constructor = undefined;
|
||||
assertEquals(Array, new MyOtherDefaultArray().map(()=>{}).constructor);
|
||||
|
||||
// Exceptions propagated when getting constructor @@species throws
|
||||
|
||||
class SpeciesError extends Error { }
|
||||
class ConstructorError extends Error { }
|
||||
class MyThrowingArray extends Array {
|
||||
static get [Symbol.species]() { throw new SpeciesError; }
|
||||
}
|
||||
assertThrows(() => new MyThrowingArray().map(()=>{}), SpeciesError);
|
||||
Object.defineProperty(MyThrowingArray.prototype, 'constructor', {
|
||||
get() { throw new ConstructorError; }
|
||||
});
|
||||
assertThrows(() => new MyThrowingArray().map(()=>{}), ConstructorError);
|
||||
|
||||
// Previously unexpected errors from setting properties in arrays throw
|
||||
|
||||
class FrozenArray extends Array {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
Object.freeze(this);
|
||||
}
|
||||
}
|
||||
assertThrows(() => new FrozenArray([1]).map(()=>0), TypeError);
|
||||
assertThrows(() => new FrozenArray([1]).filter(()=>true), TypeError);
|
||||
assertThrows(() => new FrozenArray([1]).slice(0, 1), TypeError);
|
||||
assertThrows(() => new FrozenArray([1]).splice(0, 1), TypeError);
|
||||
|
||||
// Verify call counts and constructor parameters
|
||||
|
||||
var count;
|
||||
var params;
|
||||
class MyObservedArray extends Array {
|
||||
constructor(...args) {
|
||||
super(...args);
|
||||
params = args;
|
||||
}
|
||||
static get [Symbol.species]() {
|
||||
count++
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
count = 0;
|
||||
params = undefined;
|
||||
assertEquals(MyObservedArray,
|
||||
new MyObservedArray().map(()=>{}).constructor);
|
||||
assertEquals(1, count);
|
||||
assertArrayEquals([0], params);
|
||||
|
||||
count = 0;
|
||||
params = undefined;
|
||||
assertEquals(MyObservedArray,
|
||||
new MyObservedArray().filter(()=>{}).constructor);
|
||||
assertEquals(1, count);
|
||||
assertArrayEquals([0], params);
|
||||
|
||||
count = 0;
|
||||
params = undefined;
|
||||
assertEquals(MyObservedArray,
|
||||
new MyObservedArray().slice().constructor);
|
||||
// TODO(littledan): Should be 1
|
||||
assertEquals(2, count);
|
||||
assertArrayEquals([0], params);
|
||||
|
||||
count = 0;
|
||||
params = undefined;
|
||||
assertEquals(MyObservedArray,
|
||||
new MyObservedArray().splice().constructor);
|
||||
// TODO(littledan): Should be 1
|
||||
assertEquals(2, count);
|
||||
assertArrayEquals([0], params);
|
||||
|
||||
// @@species constructor can be a Proxy, and the realm access doesn't
|
||||
// crash
|
||||
|
||||
class MyProxyArray extends Array { }
|
||||
let ProxyArray = new Proxy(MyProxyArray, {});
|
||||
MyProxyArray.constructor = ProxyArray;
|
||||
|
||||
assertEquals(MyProxyArray, new ProxyArray().map(()=>{}).constructor);
|
@ -56,6 +56,10 @@
|
||||
'es6/debug-promises/reject-with-invalid-reject': [FAIL],
|
||||
'es6/debug-promises/throw-with-undefined-reject': [FAIL],
|
||||
|
||||
# Issue 4093: This test will be updated in the course of implementing
|
||||
# @@species, when TypedArray support is added.
|
||||
'regress/regress-544991': [SKIP],
|
||||
|
||||
##############################################################################
|
||||
# TurboFan compiler failures.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user