blob: 75a45aaf598d94f51861e227334250dc3c31c415 [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);
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);