blob: 25edf55104bfaf344060880bd76f86b026988147 [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
Ben Murdoch4a90d5f2016-03-22 12:00:34 +00005// Test the ES2015 @@species feature
6
7'use strict';
8
9// Subclasses of Array construct themselves under map, etc
10
11class MyArray extends Array { }
12
13assertEquals(MyArray, new MyArray().map(()=>{}).constructor);
14assertEquals(MyArray, new MyArray().filter(()=>{}).constructor);
15assertEquals(MyArray, new MyArray().slice().constructor);
16assertEquals(MyArray, new MyArray().splice().constructor);
Ben Murdoch097c5b22016-05-18 11:27:45 +010017assertEquals(MyArray, new MyArray().concat([1]).constructor);
18assertEquals(1, new MyArray().concat([1])[0]);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000019
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);
Ben Murdoch097c5b22016-05-18 11:27:45 +010030assertEquals(MyArray, new MyOtherArray().concat().constructor);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000031
32// Array methods on non-arrays return arrays
33
34class MyNonArray extends Array {
35 static get [Symbol.species]() { return MyObject; }
36}
37
38class MyObject { }
39
40assertEquals(MyObject,
41 Array.prototype.map.call(new MyNonArray(), ()=>{}).constructor);
42assertEquals(MyObject,
43 Array.prototype.filter.call(new MyNonArray(), ()=>{}).constructor);
44assertEquals(MyObject,
45 Array.prototype.slice.call(new MyNonArray()).constructor);
46assertEquals(MyObject,
47 Array.prototype.splice.call(new MyNonArray()).constructor);
Ben Murdoch097c5b22016-05-18 11:27:45 +010048assertEquals(MyObject,
49 Array.prototype.concat.call(new MyNonArray()).constructor);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000050
51assertEquals(undefined,
52 Array.prototype.map.call(new MyNonArray(), ()=>{}).length);
53assertEquals(undefined,
54 Array.prototype.filter.call(new MyNonArray(), ()=>{}).length);
Ben Murdoch097c5b22016-05-18 11:27:45 +010055assertEquals(undefined,
56 Array.prototype.concat.call(new MyNonArray(), ()=>{}).length);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000057// slice and splice actually do explicitly define the length for some reason
58assertEquals(0, Array.prototype.slice.call(new MyNonArray()).length);
59assertEquals(0, Array.prototype.splice.call(new MyNonArray()).length);
60
61// Cross-realm Arrays build same-realm arrays
62
63var realm = Realm.create();
64assertEquals(Array,
65 Array.prototype.map.call(
66 Realm.eval(realm, "[]"), ()=>{}).constructor);
67assertFalse(Array === Realm.eval(realm, "[]").map(()=>{}).constructor);
68assertFalse(Array === Realm.eval(realm, "[].map(()=>{}).constructor"));
Ben Murdoch097c5b22016-05-18 11:27:45 +010069assertEquals(Array,
70 Array.prototype.concat.call(
71 Realm.eval(realm, "[]")).constructor);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000072
73// Defaults when constructor or @@species is missing or non-constructor
74
75class MyDefaultArray extends Array {
76 static get [Symbol.species]() { return undefined; }
77}
78assertEquals(Array, new MyDefaultArray().map(()=>{}).constructor);
79
80class MyOtherDefaultArray extends Array { }
81assertEquals(MyOtherDefaultArray,
82 new MyOtherDefaultArray().map(()=>{}).constructor);
83MyOtherDefaultArray.prototype.constructor = undefined;
84assertEquals(Array, new MyOtherDefaultArray().map(()=>{}).constructor);
Ben Murdoch097c5b22016-05-18 11:27:45 +010085assertEquals(Array, new MyOtherDefaultArray().concat().constructor);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +000086
87// Exceptions propagated when getting constructor @@species throws
88
89class SpeciesError extends Error { }
90class ConstructorError extends Error { }
91class MyThrowingArray extends Array {
92 static get [Symbol.species]() { throw new SpeciesError; }
93}
94assertThrows(() => new MyThrowingArray().map(()=>{}), SpeciesError);
95Object.defineProperty(MyThrowingArray.prototype, 'constructor', {
96 get() { throw new ConstructorError; }
97});
98assertThrows(() => new MyThrowingArray().map(()=>{}), ConstructorError);
99
100// Previously unexpected errors from setting properties in arrays throw
101
102class FrozenArray extends Array {
103 constructor(...args) {
104 super(...args);
105 Object.freeze(this);
106 }
107}
108assertThrows(() => new FrozenArray([1]).map(()=>0), TypeError);
109assertThrows(() => new FrozenArray([1]).filter(()=>true), TypeError);
110assertThrows(() => new FrozenArray([1]).slice(0, 1), TypeError);
111assertThrows(() => new FrozenArray([1]).splice(0, 1), TypeError);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100112assertThrows(() => new FrozenArray([]).concat([1]), TypeError);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000113
114// Verify call counts and constructor parameters
115
116var count;
117var params;
118class MyObservedArray extends Array {
119 constructor(...args) {
120 super(...args);
121 params = args;
122 }
123 static get [Symbol.species]() {
124 count++
125 return this;
126 }
127}
128
129count = 0;
130params = undefined;
131assertEquals(MyObservedArray,
132 new MyObservedArray().map(()=>{}).constructor);
133assertEquals(1, count);
134assertArrayEquals([0], params);
135
136count = 0;
137params = undefined;
138assertEquals(MyObservedArray,
139 new MyObservedArray().filter(()=>{}).constructor);
140assertEquals(1, count);
141assertArrayEquals([0], params);
142
143count = 0;
144params = undefined;
145assertEquals(MyObservedArray,
Ben Murdoch097c5b22016-05-18 11:27:45 +0100146 new MyObservedArray().concat().constructor);
147assertEquals(1, count);
148assertArrayEquals([0], params);
149
150count = 0;
151params = undefined;
152assertEquals(MyObservedArray,
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000153 new MyObservedArray().slice().constructor);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100154assertEquals(1, count);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000155assertArrayEquals([0], params);
156
157count = 0;
158params = undefined;
159assertEquals(MyObservedArray,
160 new MyObservedArray().splice().constructor);
Ben Murdoch097c5b22016-05-18 11:27:45 +0100161assertEquals(1, count);
Ben Murdoch4a90d5f2016-03-22 12:00:34 +0000162assertArrayEquals([0], params);
163
164// @@species constructor can be a Proxy, and the realm access doesn't
165// crash
166
167class MyProxyArray extends Array { }
168let ProxyArray = new Proxy(MyProxyArray, {});
169MyProxyArray.constructor = ProxyArray;
170
171assertEquals(MyProxyArray, new ProxyArray().map(()=>{}).constructor);