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