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