1// Copyright 2015 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// Flags: --harmony-species --harmony-proxies 6 7// Test the ES2015 @@species feature 8 9'use strict'; 10 11// Subclasses of Array construct themselves under map, etc 12 13class MyArray extends Array { } 14 15assertEquals(MyArray, new MyArray().map(()=>{}).constructor); 16assertEquals(MyArray, new MyArray().filter(()=>{}).constructor); 17assertEquals(MyArray, new MyArray().slice().constructor); 18assertEquals(MyArray, new MyArray().splice().constructor); 19 20// Subclasses can override @@species to return the another class 21 22class MyOtherArray extends Array { 23 static get [Symbol.species]() { return MyArray; } 24} 25 26assertEquals(MyArray, new MyOtherArray().map(()=>{}).constructor); 27assertEquals(MyArray, new MyOtherArray().filter(()=>{}).constructor); 28assertEquals(MyArray, new MyOtherArray().slice().constructor); 29assertEquals(MyArray, new MyOtherArray().splice().constructor); 30 31// Array methods on non-arrays return arrays 32 33class MyNonArray extends Array { 34 static get [Symbol.species]() { return MyObject; } 35} 36 37class MyObject { } 38 39assertEquals(MyObject, 40 Array.prototype.map.call(new MyNonArray(), ()=>{}).constructor); 41assertEquals(MyObject, 42 Array.prototype.filter.call(new MyNonArray(), ()=>{}).constructor); 43assertEquals(MyObject, 44 Array.prototype.slice.call(new MyNonArray()).constructor); 45assertEquals(MyObject, 46 Array.prototype.splice.call(new MyNonArray()).constructor); 47 48assertEquals(undefined, 49 Array.prototype.map.call(new MyNonArray(), ()=>{}).length); 50assertEquals(undefined, 51 Array.prototype.filter.call(new MyNonArray(), ()=>{}).length); 52// slice and splice actually do explicitly define the length for some reason 53assertEquals(0, Array.prototype.slice.call(new MyNonArray()).length); 54assertEquals(0, Array.prototype.splice.call(new MyNonArray()).length); 55 56// Cross-realm Arrays build same-realm arrays 57 58var realm = Realm.create(); 59assertEquals(Array, 60 Array.prototype.map.call( 61 Realm.eval(realm, "[]"), ()=>{}).constructor); 62assertFalse(Array === Realm.eval(realm, "[]").map(()=>{}).constructor); 63assertFalse(Array === Realm.eval(realm, "[].map(()=>{}).constructor")); 64 65// Defaults when constructor or @@species is missing or non-constructor 66 67class MyDefaultArray extends Array { 68 static get [Symbol.species]() { return undefined; } 69} 70assertEquals(Array, new MyDefaultArray().map(()=>{}).constructor); 71 72class MyOtherDefaultArray extends Array { } 73assertEquals(MyOtherDefaultArray, 74 new MyOtherDefaultArray().map(()=>{}).constructor); 75MyOtherDefaultArray.prototype.constructor = undefined; 76assertEquals(Array, new MyOtherDefaultArray().map(()=>{}).constructor); 77 78// Exceptions propagated when getting constructor @@species throws 79 80class SpeciesError extends Error { } 81class ConstructorError extends Error { } 82class MyThrowingArray extends Array { 83 static get [Symbol.species]() { throw new SpeciesError; } 84} 85assertThrows(() => new MyThrowingArray().map(()=>{}), SpeciesError); 86Object.defineProperty(MyThrowingArray.prototype, 'constructor', { 87 get() { throw new ConstructorError; } 88}); 89assertThrows(() => new MyThrowingArray().map(()=>{}), ConstructorError); 90 91// Previously unexpected errors from setting properties in arrays throw 92 93class FrozenArray extends Array { 94 constructor(...args) { 95 super(...args); 96 Object.freeze(this); 97 } 98} 99assertThrows(() => new FrozenArray([1]).map(()=>0), TypeError); 100assertThrows(() => new FrozenArray([1]).filter(()=>true), TypeError); 101assertThrows(() => new FrozenArray([1]).slice(0, 1), TypeError); 102assertThrows(() => new FrozenArray([1]).splice(0, 1), TypeError); 103 104// Verify call counts and constructor parameters 105 106var count; 107var params; 108class MyObservedArray extends Array { 109 constructor(...args) { 110 super(...args); 111 params = args; 112 } 113 static get [Symbol.species]() { 114 count++ 115 return this; 116 } 117} 118 119count = 0; 120params = undefined; 121assertEquals(MyObservedArray, 122 new MyObservedArray().map(()=>{}).constructor); 123assertEquals(1, count); 124assertArrayEquals([0], params); 125 126count = 0; 127params = undefined; 128assertEquals(MyObservedArray, 129 new MyObservedArray().filter(()=>{}).constructor); 130assertEquals(1, count); 131assertArrayEquals([0], params); 132 133count = 0; 134params = undefined; 135assertEquals(MyObservedArray, 136 new MyObservedArray().slice().constructor); 137// TODO(littledan): Should be 1 138assertEquals(2, count); 139assertArrayEquals([0], params); 140 141count = 0; 142params = undefined; 143assertEquals(MyObservedArray, 144 new MyObservedArray().splice().constructor); 145// TODO(littledan): Should be 1 146assertEquals(2, count); 147assertArrayEquals([0], params); 148 149// @@species constructor can be a Proxy, and the realm access doesn't 150// crash 151 152class MyProxyArray extends Array { } 153let ProxyArray = new Proxy(MyProxyArray, {}); 154MyProxyArray.constructor = ProxyArray; 155 156assertEquals(MyProxyArray, new ProxyArray().map(()=>{}).constructor); 157