Index: test/mjsunit/harmony/species.js |
diff --git a/test/mjsunit/harmony/species.js b/test/mjsunit/harmony/species.js |
index da1df4331fc948e7f9b927dacbda1a9b320f8f31..c6c355c34da7cfb4b74ff8b7283a922fb7665568 100644 |
--- a/test/mjsunit/harmony/species.js |
+++ b/test/mjsunit/harmony/species.js |
@@ -35,3 +35,172 @@ assertEquals(classesWithSpecies.length, |
for (let constructor of classesWithoutSpecies) |
assertEquals(undefined, constructor[Symbol.species]); |
+ |
+// Subclasses of Array and %TypedArray% 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().concat().constructor); |
+assertEquals(MyArray, new MyArray().slice().constructor); |
+assertEquals(MyArray, new MyArray().splice().constructor); |
+ |
+var typedArrayConstructors = [ |
+ Uint8Array, |
+ Int8Array, |
+ Uint16Array, |
+ Int16Array, |
+ Uint32Array, |
+ Int32Array, |
+ Uint8ClampedArray, |
+ Float32Array, |
+ Float64Array |
+]; |
+ |
+for (let constructor of typedArrayConstructors) { |
+ class MyTypedArray extends constructor { } |
+ assertEquals(MyTypedArray, new MyTypedArray().map(()=>0).constructor); |
+ assertEquals(MyTypedArray, new MyTypedArray().filter(()=>{}).constructor); |
+ assertEquals(MyTypedArray, new MyTypedArray().slice().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().concat().constructor); |
+assertEquals(MyArray, new MyOtherArray().slice().constructor); |
+assertEquals(MyArray, new MyOtherArray().splice().constructor); |
+ |
+for (let constructor of typedArrayConstructors) { |
+ class MyTypedArray extends constructor { } |
+ class MyOtherTypedArray extends constructor { |
+ static get [Symbol.species]() { return MyTypedArray; } |
+ } |
+ assertEquals(MyTypedArray, new MyOtherTypedArray().map(()=>0).constructor); |
+ assertEquals(MyTypedArray, new MyOtherTypedArray().filter(()=>{}).constructor); |
+ assertEquals(MyTypedArray, new MyOtherTypedArray().slice().constructor); |
+} |
+ |
+// TypedArray too-short and non-TypedArray error checking |
+ |
+for (let constructor of typedArrayConstructors) { |
+ class MyShortTypedArray extends constructor { |
+ constructor(length) { super(length - 1); } |
+ } |
+ assertThrows(() => new MyShortTypedArray(5).map(()=>0), TypeError); |
+ assertThrows(() => new MyShortTypedArray(5).filter(()=>true), TypeError); |
+ assertThrows(() => new MyShortTypedArray(5).slice(), TypeError); |
+ |
+ class MyNonTypedArray extends constructor { |
+ static get [Symbol.species]() { return Array; } |
+ } |
+ assertThrows(() => new MyNonTypedArray().map(()=>0), TypeError); |
+ assertThrows(() => new MyNonTypedArray().filter(()=>{}), TypeError); |
+ assertThrows(() => new MyNonTypedArray().slice(), TypeError); |
+} |
+ |
+// 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.concat.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); |
+assertEquals(undefined, Array.prototype.concat.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); |
+ |
+for (let constructor of typedArrayConstructors) { |
+ class MyDefaultTypedArray extends constructor { |
+ static get [Symbol.species]() { return undefined; } |
+ } |
+ assertEquals(constructor, new MyDefaultTypedArray().map(()=>0).constructor); |
+ |
+ class MyOtherDefaultTypedArray extends constructor { } |
+ assertEquals(MyOtherDefaultTypedArray, new MyOtherDefaultTypedArray().map(()=>0).constructor); |
+ MyOtherDefaultTypedArray.prototype.constructor = undefined; |
+ assertEquals(constructor, new MyOtherDefaultTypedArray().map(()=>0).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); |
+ |
+for (let constructor of typedArrayConstructors) { |
+ class MyThrowingArray extends constructor { |
+ 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); |
+assertThrows(() => new FrozenArray([1]).concat(), TypeError); |
+ |
+// ArrayBuffer.prototype.slice makes subclass and checks length |
+ |
+class MyArrayBuffer extends ArrayBuffer { } |
+assertEquals(MyArrayBuffer, new MyArrayBuffer(0).slice().constructor); |
+ |
+class MyShortArrayBuffer extends ArrayBuffer { |
+ constructor(length) { super(length - 1); } |
+} |
+assertThrows(() => new MyShortArrayBuffer(5).slice(0, 4), TypeError); |