• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* Copyright JS Foundation and other contributors, http://js.foundation
2 *
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16// Copyright 2015 the V8 project authors. All rights reserved.
17// Use of this source code is governed by a BSD-style license that can be
18// found in the LICENSE file.
19
20// Test the ES2015 @@species feature
21
22'use strict';
23
24// Subclasses of Array construct themselves under map, etc
25
26class MyArray extends Array { }
27
28function assertEquals(a, b) {
29  assert(a === b);
30}
31
32assertEquals(MyArray, new MyArray().map(()=>{}).constructor);
33assertEquals(MyArray, new MyArray().filter(()=>{}).constructor);
34assertEquals(MyArray, new MyArray().slice().constructor);
35assertEquals(MyArray, new MyArray().splice().constructor);
36assertEquals(MyArray, new MyArray().concat([1]).constructor);
37assertEquals(1, new MyArray().concat([1])[0]);
38
39// Subclasses can override @@species to return the another class
40
41class MyOtherArray extends Array {
42  static get [Symbol.species]() { return MyArray; }
43}
44
45assertEquals(MyArray, new MyOtherArray().map(()=>{}).constructor);
46assertEquals(MyArray, new MyOtherArray().filter(()=>{}).constructor);
47assertEquals(MyArray, new MyOtherArray().slice().constructor);
48assertEquals(MyArray, new MyOtherArray().splice().constructor);
49assertEquals(MyArray, new MyOtherArray().concat().constructor);
50
51// Array  methods on non-arrays return arrays
52
53class MyNonArray extends Array {
54  static get [Symbol.species]() { return MyObject; }
55}
56
57class MyObject { }
58
59assertEquals(MyObject,
60             Array.prototype.map.call(new MyNonArray(), ()=>{}).constructor);
61assertEquals(MyObject,
62             Array.prototype.filter.call(new MyNonArray(), ()=>{}).constructor);
63assertEquals(MyObject,
64             Array.prototype.slice.call(new MyNonArray()).constructor);
65assertEquals(MyObject,
66             Array.prototype.splice.call(new MyNonArray()).constructor);
67assertEquals(MyObject,
68             Array.prototype.concat.call(new MyNonArray()).constructor);
69
70assertEquals(undefined,
71             Array.prototype.map.call(new MyNonArray(), ()=>{}).length);
72assertEquals(undefined,
73             Array.prototype.filter.call(new MyNonArray(), ()=>{}).length);
74// slice, splice, and concat actually do explicitly define the length.
75assertEquals(0, Array.prototype.slice.call(new MyNonArray()).length);
76assertEquals(0, Array.prototype.splice.call(new MyNonArray()).length);
77assertEquals(1, Array.prototype.concat.call(new MyNonArray(), ()=>{}).length);
78
79// Defaults when constructor or @@species is missing or non-constructor
80
81class MyDefaultArray extends Array {
82  static get [Symbol.species]() { return undefined; }
83}
84assertEquals(Array, new MyDefaultArray().map(()=>{}).constructor);
85
86class MyOtherDefaultArray extends Array { }
87assertEquals(MyOtherDefaultArray,
88             new MyOtherDefaultArray().map(()=>{}).constructor);
89MyOtherDefaultArray.prototype.constructor = undefined;
90assertEquals(Array, new MyOtherDefaultArray().map(()=>{}).constructor);
91assertEquals(Array, new MyOtherDefaultArray().concat().constructor);
92
93// Exceptions propagated when getting constructor @@species throws
94
95class SpeciesError extends Error { }
96class ConstructorError extends Error { }
97class MyThrowingArray extends Array {
98  static get [Symbol.species]() { throw new SpeciesError; }
99}
100
101function assertThrows (a, b) {
102  try {
103    a();
104  } catch (e) {
105    assert(e instanceof b);
106  }
107}
108
109assertThrows(() => new MyThrowingArray().map(()=>{}), SpeciesError);
110Object.defineProperty(MyThrowingArray.prototype, 'constructor', {
111    get() { throw new ConstructorError; }
112});
113assertThrows(() => new MyThrowingArray().map(()=>{}), ConstructorError);
114
115// Previously unexpected errors from setting properties in arrays throw
116
117class FrozenArray extends Array {
118  constructor(...args) {
119    super(...args);
120    Object.freeze(this);
121  }
122}
123assertThrows(() => new FrozenArray([1]).map(()=>0), TypeError);
124assertThrows(() => new FrozenArray([1]).filter(()=>true), TypeError);
125assertThrows(() => new FrozenArray([1]).slice(0, 1), TypeError);
126assertThrows(() => new FrozenArray([1]).splice(0, 1), TypeError);
127assertThrows(() => new FrozenArray([]).concat([1]), TypeError);
128
129// Verify call counts and constructor parameters
130
131var count;
132var params;
133class MyObservedArray extends Array {
134  constructor(...args) {
135    super(...args);
136    params = args;
137  }
138  static get [Symbol.species]() {
139    count++
140    return this;
141  }
142}
143
144function assertArrayEquals(value, expected, type) {
145  assert(expected.length === value.length);
146  for (var i=0; i<value.length; ++i) {
147    assertEquals(expected[i], value[i]);
148  }
149}
150
151count = 0;
152params = undefined;
153assertEquals(MyObservedArray,
154             new MyObservedArray().map(()=>{}).constructor);
155assertEquals(1, count);
156assertArrayEquals([0], params);
157
158count = 0;
159params = undefined;
160assertEquals(MyObservedArray,
161             new MyObservedArray().filter(()=>{}).constructor);
162assertEquals(1, count);
163assertArrayEquals([0], params);
164
165count = 0;
166params = undefined;
167assertEquals(MyObservedArray,
168             new MyObservedArray().concat().constructor);
169assertEquals(1, count);
170assertArrayEquals([0], params);
171
172count = 0;
173params = undefined;
174assertEquals(MyObservedArray,
175             new MyObservedArray().slice().constructor);
176assertEquals(1, count);
177assertArrayEquals([0], params);
178
179count = 0;
180params = undefined;
181assertEquals(MyObservedArray,
182             new MyObservedArray().splice().constructor);
183assertEquals(1, count);
184assertArrayEquals([0], params);
185