blob: 3cef50cc4c9eb3e5778f33d7e7aa4871d85c315a [file] [log] [blame]
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00001// 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);
Ben Murdoch097c5b22016-05-18 11:27:45 +010019assertEquals(MyArray, new MyArray().concat([1]).constructor);
20assertEquals(1, new MyArray().concat([1])[0]);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000021
22// Subclasses can override @@species to return the another class
23
24class MyOtherArray extends Array {
25 static get [Symbol.species]() { return MyArray; }
26}
27
28assertEquals(MyArray, new MyOtherArray().map(()=>{}).constructor);
29assertEquals(MyArray, new MyOtherArray().filter(()=>{}).constructor);
30assertEquals(MyArray, new MyOtherArray().slice().constructor);
31assertEquals(MyArray, new MyOtherArray().splice().constructor);
Ben Murdoch097c5b22016-05-18 11:27:45 +010032assertEquals(MyArray, new MyOtherArray().concat().constructor);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000033
34// Array methods on non-arrays return arrays
35
36class MyNonArray extends Array {
37 static get [Symbol.species]() { return MyObject; }
38}
39
40class MyObject { }
41
42assertEquals(MyObject,
43 Array.prototype.map.call(new MyNonArray(), ()=>{}).constructor);
44assertEquals(MyObject,
45 Array.prototype.filter.call(new MyNonArray(), ()=>{}).constructor);
46assertEquals(MyObject,
47 Array.prototype.slice.call(new MyNonArray()).constructor);
48assertEquals(MyObject,
49 Array.prototype.splice.call(new MyNonArray()).constructor);
Ben Murdoch097c5b22016-05-18 11:27:45 +010050assertEquals(MyObject,
51 Array.prototype.concat.call(new MyNonArray()).constructor);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000052
53assertEquals(undefined,
54 Array.prototype.map.call(new MyNonArray(), ()=>{}).length);
55assertEquals(undefined,
56 Array.prototype.filter.call(new MyNonArray(), ()=>{}).length);
Ben Murdoch097c5b22016-05-18 11:27:45 +010057assertEquals(undefined,
58 Array.prototype.concat.call(new MyNonArray(), ()=>{}).length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000059// slice and splice actually do explicitly define the length for some reason
60assertEquals(0, Array.prototype.slice.call(new MyNonArray()).length);
61assertEquals(0, Array.prototype.splice.call(new MyNonArray()).length);
62
63// Cross-realm Arrays build same-realm arrays
64
65var realm = Realm.create();
66assertEquals(Array,
67 Array.prototype.map.call(
68 Realm.eval(realm, "[]"), ()=>{}).constructor);
69assertFalse(Array === Realm.eval(realm, "[]").map(()=>{}).constructor);
70assertFalse(Array === Realm.eval(realm, "[].map(()=>{}).constructor"));
Ben Murdoch097c5b22016-05-18 11:27:45 +010071assertEquals(Array,
72 Array.prototype.concat.call(
73 Realm.eval(realm, "[]")).constructor);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000074
75// Defaults when constructor or @@species is missing or non-constructor
76
77class MyDefaultArray extends Array {
78 static get [Symbol.species]() { return undefined; }
79}
80assertEquals(Array, new MyDefaultArray().map(()=>{}).constructor);
81
82class MyOtherDefaultArray extends Array { }
83assertEquals(MyOtherDefaultArray,
84 new MyOtherDefaultArray().map(()=>{}).constructor);
85MyOtherDefaultArray.prototype.constructor = undefined;
86assertEquals(Array, new MyOtherDefaultArray().map(()=>{}).constructor);
Ben Murdoch097c5b22016-05-18 11:27:45 +010087assertEquals(Array, new MyOtherDefaultArray().concat().constructor);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000088
89// Exceptions propagated when getting constructor @@species throws
90
91class SpeciesError extends Error { }
92class ConstructorError extends Error { }
93class MyThrowingArray extends Array {
94 static get [Symbol.species]() { throw new SpeciesError; }
95}
96assertThrows(() => new MyThrowingArray().map(()=>{}), SpeciesError);
97Object.defineProperty(MyThrowingArray.prototype, 'constructor', {
98 get() { throw new ConstructorError; }
99});
100assertThrows(() => new MyThrowingArray().map(()=>{}), ConstructorError);
101
102// Previously unexpected errors from setting properties in arrays throw
103
104class FrozenArray extends Array {
105 constructor(...args) {
106 super(...args);
107 Object.freeze(this);
108 }
109}
110assertThrows(() => new FrozenArray([1]).map(()=>0), TypeError);
111assertThrows(() => new FrozenArray([1]).filter(()=>true), TypeError);
112assertThrows(() => new FrozenArray([1]).slice(0, 1), TypeError);
113assertThrows(() => new FrozenArray([1]).splice(0, 1), TypeError);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100114assertThrows(() => new FrozenArray([]).concat([1]), TypeError);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000115
116// Verify call counts and constructor parameters
117
118var count;
119var params;
120class MyObservedArray extends Array {
121 constructor(...args) {
122 super(...args);
123 params = args;
124 }
125 static get [Symbol.species]() {
126 count++
127 return this;
128 }
129}
130
131count = 0;
132params = undefined;
133assertEquals(MyObservedArray,
134 new MyObservedArray().map(()=>{}).constructor);
135assertEquals(1, count);
136assertArrayEquals([0], params);
137
138count = 0;
139params = undefined;
140assertEquals(MyObservedArray,
141 new MyObservedArray().filter(()=>{}).constructor);
142assertEquals(1, count);
143assertArrayEquals([0], params);
144
145count = 0;
146params = undefined;
147assertEquals(MyObservedArray,
Ben Murdoch097c5b22016-05-18 11:27:45 +0100148 new MyObservedArray().concat().constructor);
149assertEquals(1, count);
150assertArrayEquals([0], params);
151
152count = 0;
153params = undefined;
154assertEquals(MyObservedArray,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000155 new MyObservedArray().slice().constructor);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100156assertEquals(1, count);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000157assertArrayEquals([0], params);
158
159count = 0;
160params = undefined;
161assertEquals(MyObservedArray,
162 new MyObservedArray().splice().constructor);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100163assertEquals(1, count);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000164assertArrayEquals([0], params);
165
166// @@species constructor can be a Proxy, and the realm access doesn't
167// crash
168
169class MyProxyArray extends Array { }
170let ProxyArray = new Proxy(MyProxyArray, {});
171MyProxyArray.constructor = ProxyArray;
172
173assertEquals(MyProxyArray, new ProxyArray().map(()=>{}).constructor);