2d889aa9a4
https://github.com/tc39/ecma262/pull/988 gained concensus during the september 2017 TC39 meetings. This moves the load of the "next" method to the very beginning of the iteration protocol, rather than during each iteration step. This impacts: - yield* - for-of loops - spread arguments - array spreads In the v8 implementation, this also affects async iteration versions of these things (the sole exception being the Async-From-Sync iterator, which requires a few more changes to work with this, likely done in a followup patch). This change introduces a new AST node, ResolvedProperty, which can be used as a callee by Call nodes to produce the same bytecode as Property calls, without observably re-loading the property. This is used in several AST-desugarings involving the iteration protocol. BUG=v8:6861, v8:5699 R=rmcilroy@chromium.org TBR=neis@chromium.org, adamk@chromium.org Cq-Include-Trybots: luci.v8.try:v8_linux_noi18n_rel_ng Change-Id: I9685db6e85315ba8a2df87a4537c2bf491e1e35b Reviewed-on: https://chromium-review.googlesource.com/857593 Commit-Queue: Caitlin Potter <caitp@igalia.com> Reviewed-by: Ross McIlroy <rmcilroy@chromium.org> Cr-Commit-Position: refs/heads/master@{#50518}
510 lines
15 KiB
JavaScript
510 lines
15 KiB
JavaScript
// Copyright 2015 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// Flags: --allow-natives-syntax
|
|
|
|
function testSpreadCallsStrict() {
|
|
"use strict"
|
|
function countArgs() { return arguments.length; }
|
|
|
|
// Test this argument
|
|
function returnThis() { return this; }
|
|
assertEquals(void 0, returnThis(..."test"));
|
|
|
|
// Test argument counting with different iterables
|
|
assertEquals(0, countArgs(...""));
|
|
assertEquals(4, countArgs(..."test"));
|
|
assertEquals(4, countArgs(..."tes", ..."t"));
|
|
assertEquals(4, countArgs("t", ..."es", "t"));
|
|
assertEquals(4, countArgs("tes", ..."t!!"));
|
|
|
|
assertEquals(1, countArgs(...[1]));
|
|
assertEquals(2, countArgs(...[1, 2]));
|
|
assertEquals(3, countArgs(...[1, 2, 3]));
|
|
assertEquals(4, countArgs(...[1, 2, 3, 4]));
|
|
assertEquals(5, countArgs(...[1, 2, 3, 4, 5]));
|
|
assertEquals(6, countArgs(...[1, 2, 3, 4, 5, 6]));
|
|
|
|
assertEquals(1, countArgs(...[1.1]));
|
|
assertEquals(2, countArgs(...[1.1, 2.2]));
|
|
assertEquals(3, countArgs(...[1.1, 2.2, 3.3]));
|
|
assertEquals(4, countArgs(...[1.1, 2.2, 3.3, 4.4]));
|
|
assertEquals(5, countArgs(...[1.1, 2.2, 3.3, 4.4, 5.5]));
|
|
assertEquals(6, countArgs(...[1.1, 2.2, 3.3, 4.4, 5.5, 6.6]));
|
|
|
|
assertEquals(1, countArgs(...new Set([1])));
|
|
assertEquals(2, countArgs(...new Set([1, 2])));
|
|
assertEquals(3, countArgs(...new Set([1, 2, 3])));
|
|
assertEquals(4, countArgs(...new Set([1, 2, 3, 4])));
|
|
assertEquals(5, countArgs(...new Set([1, 2, 3, 4, 5])));
|
|
assertEquals(6, countArgs(...new Set([1, 2, 3, 4, 5, 6])));
|
|
|
|
assertEquals(3, countArgs(...(function*(){ yield 1; yield 2; yield 3; })()));
|
|
|
|
// Test values
|
|
function sum() {
|
|
var sum = arguments[0];
|
|
for (var i = 1; i < arguments.length; ++i) {
|
|
sum += arguments[i];
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
assertThrows(function() {
|
|
sum(...0);
|
|
}, TypeError);
|
|
assertEquals(void 0, sum(...""));
|
|
assertEquals(void 0, sum(...[]));
|
|
assertEquals(void 0, sum(...new Set));
|
|
assertEquals(void 0, sum(...(function*() { })()));
|
|
|
|
assertEquals("test", sum(..."test"));
|
|
assertEquals(10, sum(...[1, 2, 3, 4]));
|
|
assertEquals(10, sum(...[1, 2, 3], 4));
|
|
assertEquals(10, sum(1, ...[2, 3], 4));
|
|
assertEquals(10, sum(1, ...[2, 3], ...[4]));
|
|
assertEquals(10, sum(1, 2, ...[3, 4]));
|
|
assertEquals(10, sum(...new Set([1, 2, 3, 4])));
|
|
assertEquals(10, sum(...(function*() {
|
|
yield 1;
|
|
yield 2;
|
|
yield 3;
|
|
yield 4;
|
|
})()));
|
|
|
|
// nested spreads
|
|
function makeArray() {
|
|
var result = [];
|
|
for (var i = 0; i < arguments.length; ++i) {
|
|
result.push(arguments[i]);
|
|
}
|
|
return result;
|
|
}
|
|
assertEquals(10, sum(...makeArray(...[1, 2, 3, 4])));
|
|
assertEquals("test!!!", sum(...makeArray(..."test!!!")));
|
|
|
|
// Interleaved spread/unspread args
|
|
assertEquals(36, sum(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8));
|
|
assertEquals(45, sum(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8, ...[9]));
|
|
|
|
// Methods
|
|
var O = {
|
|
returnThis: returnThis,
|
|
countArgs: countArgs,
|
|
sum: sum,
|
|
makeArray: makeArray,
|
|
|
|
nested: {
|
|
returnThis: returnThis,
|
|
countArgs: countArgs,
|
|
sum: sum,
|
|
makeArray: makeArray
|
|
}
|
|
};
|
|
|
|
// Test this argument
|
|
assertEquals(O, O.returnThis(..."test"));
|
|
assertEquals(O, O["returnThis"](..."test"));
|
|
assertEquals(O.nested, O.nested.returnThis(..."test"));
|
|
assertEquals(O.nested, O.nested["returnThis"](..."test"));
|
|
|
|
// Test argument counting with different iterables
|
|
assertEquals(0, O.countArgs(...""));
|
|
assertEquals(4, O.countArgs(..."test"));
|
|
assertEquals(4, O.countArgs(..."tes", ..."t"));
|
|
assertEquals(4, O.countArgs("t", ..."es", "t"));
|
|
assertEquals(4, O.countArgs("tes", ..."t!!"));
|
|
|
|
assertEquals(1, O.countArgs(...[1]));
|
|
assertEquals(2, O.countArgs(...[1, 2]));
|
|
assertEquals(3, O.countArgs(...[1, 2, 3]));
|
|
assertEquals(4, O.countArgs(...[1, 2, 3, 4]));
|
|
assertEquals(5, O.countArgs(...[1, 2, 3, 4, 5]));
|
|
assertEquals(6, O.countArgs(...[1, 2, 3, 4, 5, 6]));
|
|
|
|
assertEquals(1, O.countArgs(...new Set([1])));
|
|
assertEquals(2, O.countArgs(...new Set([1, 2])));
|
|
assertEquals(3, O.countArgs(...new Set([1, 2, 3])));
|
|
assertEquals(4, O.countArgs(...new Set([1, 2, 3, 4])));
|
|
assertEquals(5, O.countArgs(...new Set([1, 2, 3, 4, 5])));
|
|
assertEquals(6, O.countArgs(...new Set([1, 2, 3, 4, 5, 6])));
|
|
|
|
assertEquals(3, O.countArgs(
|
|
...(function*(){ yield 1; yield 2; yield 3; })()));
|
|
|
|
// Test values
|
|
assertEquals(void 0, O.sum(...""));
|
|
assertEquals(void 0, O.sum(...[]));
|
|
assertEquals(void 0, O.sum(...new Set));
|
|
assertEquals(void 0, O.sum(...(function*() { })()));
|
|
|
|
assertEquals("test", O.sum(..."test"));
|
|
assertEquals(10, O.sum(...[1, 2, 3, 4]));
|
|
assertEquals(10, O.sum(...[1, 2, 3], 4));
|
|
assertEquals(10, O.sum(1, ...[2, 3], 4));
|
|
assertEquals(10, O.sum(1, ...[2, 3], ...[4]));
|
|
assertEquals(10, O.sum(1, 2, ...[3, 4]));
|
|
assertEquals(10, O.sum(...new Set([1, 2, 3, 4])));
|
|
assertEquals(10, O.sum(...(function*() {
|
|
yield 1;
|
|
yield 2;
|
|
yield 3;
|
|
yield 4;
|
|
})()));
|
|
|
|
// nested spreads
|
|
assertEquals(10, O.sum(...O.makeArray(...[1, 2, 3, 4])));
|
|
assertEquals("test!!!", O.sum(...O.makeArray(..."test!!!")));
|
|
|
|
// Interleaved spread/unspread args
|
|
assertEquals(36, O.sum(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8));
|
|
assertEquals(45, O.sum(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8, ...[9]));
|
|
};
|
|
testSpreadCallsStrict();
|
|
%OptimizeFunctionOnNextCall(testSpreadCallsStrict);
|
|
testSpreadCallsStrict();
|
|
|
|
|
|
(function testSpreadCallsSloppy() {
|
|
// Test this argument
|
|
function returnThis() { return this; }
|
|
assertEquals(this, returnThis(..."test"));
|
|
|
|
function countArgs() { return arguments.length; }
|
|
|
|
// Test argument counting with different iterables
|
|
assertEquals(0, countArgs(...""));
|
|
assertEquals(4, countArgs(..."test"));
|
|
assertEquals(4, countArgs(..."tes", ..."t"));
|
|
assertEquals(4, countArgs("t", ..."es", "t"));
|
|
assertEquals(4, countArgs("tes", ..."t!!"));
|
|
|
|
assertEquals(1, countArgs(...[1]));
|
|
assertEquals(2, countArgs(...[1, 2]));
|
|
assertEquals(3, countArgs(...[1, 2, 3]));
|
|
assertEquals(4, countArgs(...[1, 2, 3, 4]));
|
|
assertEquals(5, countArgs(...[1, 2, 3, 4, 5]));
|
|
assertEquals(6, countArgs(...[1, 2, 3, 4, 5, 6]));
|
|
|
|
assertEquals(1, countArgs(...new Set([1])));
|
|
assertEquals(2, countArgs(...new Set([1, 2])));
|
|
assertEquals(3, countArgs(...new Set([1, 2, 3])));
|
|
assertEquals(4, countArgs(...new Set([1, 2, 3, 4])));
|
|
assertEquals(5, countArgs(...new Set([1, 2, 3, 4, 5])));
|
|
assertEquals(6, countArgs(...new Set([1, 2, 3, 4, 5, 6])));
|
|
|
|
assertEquals(3, countArgs(...(function*(){
|
|
yield 1;
|
|
yield 2;
|
|
yield 3;
|
|
})()));
|
|
|
|
// Test values
|
|
function sum() {
|
|
var sum = arguments[0];
|
|
for (var i = 1; i < arguments.length; ++i) {
|
|
sum += arguments[i];
|
|
}
|
|
return sum;
|
|
}
|
|
|
|
assertThrows(function() {
|
|
sum(...0);
|
|
}, TypeError);
|
|
assertEquals(void 0, sum(...""));
|
|
assertEquals(void 0, sum(...[]));
|
|
assertEquals(void 0, sum(...new Set));
|
|
assertEquals(void 0, sum(...(function*() { })()));
|
|
|
|
assertEquals("test", sum(..."test"));
|
|
assertEquals(10, sum(...[1, 2, 3, 4]));
|
|
assertEquals(10, sum(...[1, 2, 3], 4));
|
|
assertEquals(10, sum(1, ...[2, 3], 4));
|
|
assertEquals(10, sum(1, ...[2, 3], ...[4]));
|
|
assertEquals(10, sum(1, 2, ...[3, 4]));
|
|
assertEquals(10, sum(...new Set([1, 2, 3, 4])));
|
|
assertEquals(10, sum(...(function*() {
|
|
yield 1;
|
|
yield 2;
|
|
yield 3;
|
|
yield 4;
|
|
})()));
|
|
|
|
// nested spreads
|
|
function makeArray() {
|
|
var result = [];
|
|
for (var i = 0; i < arguments.length; ++i) {
|
|
result.push(arguments[i]);
|
|
}
|
|
return result;
|
|
}
|
|
assertEquals(10, sum(...makeArray(...[1, 2, 3, 4])));
|
|
assertEquals("test!!!", sum(...makeArray(..."test!!!")));
|
|
|
|
// Interleaved spread/unspread args
|
|
assertEquals(36, sum(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8));
|
|
assertEquals(45, sum(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8, ...[9]));
|
|
|
|
// Methods
|
|
var O = {
|
|
returnThis: returnThis,
|
|
countArgs: countArgs,
|
|
sum: sum,
|
|
makeArray: makeArray,
|
|
|
|
nested: {
|
|
returnThis: returnThis,
|
|
countArgs: countArgs,
|
|
sum: sum,
|
|
makeArray: makeArray
|
|
}
|
|
};
|
|
|
|
// Test this argument
|
|
assertEquals(O, O.returnThis(..."test"));
|
|
assertEquals(O, O["returnThis"](..."test"));
|
|
assertEquals(O.nested, O.nested.returnThis(..."test"));
|
|
assertEquals(O.nested, O.nested["returnThis"](..."test"));
|
|
|
|
// Test argument counting with different iterables
|
|
assertEquals(0, O.countArgs(...""));
|
|
assertEquals(4, O.countArgs(..."test"));
|
|
assertEquals(4, O.countArgs(..."tes", ..."t"));
|
|
assertEquals(4, O.countArgs("t", ..."es", "t"));
|
|
assertEquals(4, O.countArgs("tes", ..."t!!"));
|
|
|
|
assertEquals(1, O.countArgs(...[1]));
|
|
assertEquals(2, O.countArgs(...[1, 2]));
|
|
assertEquals(3, O.countArgs(...[1, 2, 3]));
|
|
assertEquals(4, O.countArgs(...[1, 2, 3, 4]));
|
|
assertEquals(5, O.countArgs(...[1, 2, 3, 4, 5]));
|
|
assertEquals(6, O.countArgs(...[1, 2, 3, 4, 5, 6]));
|
|
|
|
assertEquals(1, O.countArgs(...new Set([1])));
|
|
assertEquals(2, O.countArgs(...new Set([1, 2])));
|
|
assertEquals(3, O.countArgs(...new Set([1, 2, 3])));
|
|
assertEquals(4, O.countArgs(...new Set([1, 2, 3, 4])));
|
|
assertEquals(5, O.countArgs(...new Set([1, 2, 3, 4, 5])));
|
|
assertEquals(6, O.countArgs(...new Set([1, 2, 3, 4, 5, 6])));
|
|
|
|
assertEquals(3, O.countArgs(...(function*(){
|
|
yield 1;
|
|
yield 2;
|
|
yield 3;
|
|
})()));
|
|
|
|
// Test values
|
|
assertEquals(void 0, O.sum(...""));
|
|
assertEquals(void 0, O.sum(...[]));
|
|
assertEquals(void 0, O.sum(...new Set));
|
|
assertEquals(void 0, O.sum(...(function*() { })()));
|
|
|
|
assertEquals("test", O.sum(..."test"));
|
|
assertEquals(10, O.sum(...[1, 2, 3, 4]));
|
|
assertEquals(10, O.sum(...[1, 2, 3], 4));
|
|
assertEquals(10, O.sum(1, ...[2, 3], 4));
|
|
assertEquals(10, O.sum(1, ...[2, 3], ...[4]));
|
|
assertEquals(10, O.sum(1, 2, ...[3, 4]));
|
|
assertEquals(10, O.sum(...new Set([1, 2, 3, 4])));
|
|
assertEquals(10, O.sum(...(function*() {
|
|
yield 1;
|
|
yield 2;
|
|
yield 3;
|
|
yield 4;
|
|
})()));
|
|
|
|
// nested spreads
|
|
assertEquals(10, O.sum(...O.makeArray(...[1, 2, 3, 4])));
|
|
assertEquals("test!!!", O.sum(...O.makeArray(..."test!!!")));
|
|
|
|
// Interleaved spread/unspread args
|
|
assertEquals(36, O.sum(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8));
|
|
assertEquals(45, O.sum(0, ...[1], 2, 3, ...[4, 5], 6, 7, 8, ...[9]));
|
|
})();
|
|
|
|
|
|
(function testSpreadEvaluationOrder() {
|
|
"use strict";
|
|
var log = "";
|
|
function* gen() { log += "X"; yield 0; log += "Y"; }
|
|
function a() { log += "A"; }
|
|
function b() { log += "B"; return gen(); }
|
|
function* c() { log += 'C1'; yield 1; log += 'C2'; }
|
|
function d() { log += "D"; }
|
|
function e() { log += "E"; }
|
|
function fn() {
|
|
var args = [];
|
|
for (var i = 0; i < arguments.length; ++i) args.push(arguments[i]);
|
|
return args;
|
|
}
|
|
|
|
var result = fn(a(), ...b(), d());
|
|
assertEquals([undefined, 0, undefined], result);
|
|
assertEquals("ABXYD", log);
|
|
|
|
log = "";
|
|
result = fn(...b(), d());
|
|
assertEquals([0, undefined], result);
|
|
assertEquals("BXYD", log);
|
|
|
|
log = "";
|
|
result = fn(a(), ...b());
|
|
assertEquals([undefined, 0], result);
|
|
assertEquals("ABXY", log);
|
|
|
|
log = "";
|
|
result = fn(a(), ...b(), ...c(), d(), e());
|
|
assertEquals([undefined, 0, 1, undefined, undefined], result);
|
|
assertEquals("ABXYC1C2DE", log);
|
|
|
|
log = "";
|
|
result = fn(a(), ...b(), ...c(), d(), e(), ...b(), ...c());
|
|
assertEquals([undefined, 0, 1, undefined, undefined, 0, 1], result);
|
|
assertEquals("ABXYC1C2DEBXYC1C2", log);
|
|
})();
|
|
|
|
(function testArrayPrototypeHoleGetterModifiesIteratorPrototypeNext() {
|
|
function sum() {
|
|
var sum = arguments[0];
|
|
for (var i = 1; i < arguments.length; ++i) {
|
|
sum += arguments[i];
|
|
}
|
|
return sum;
|
|
}
|
|
var a = [1, 2];
|
|
a[3] = 4;
|
|
var called = 0;
|
|
|
|
// .next method is only accessed during iteration prologue (see
|
|
// https://github.com/tc39/ecma262/pull/988)
|
|
let ArrayIteratorPrototype = Array.prototype[Symbol.iterator]().__proto__;
|
|
let ArrayIteratorPrototypeNextDescriptor =
|
|
Object.getOwnPropertyDescriptor(ArrayIteratorPrototype, 'next');
|
|
Object.defineProperty(Array.prototype, 2, {
|
|
get: function() {
|
|
var ai = a[Symbol.iterator]();
|
|
var original_next = ai.__proto__["next"];
|
|
Object.defineProperty(ai.__proto__, "next", {
|
|
get: function() {
|
|
called++;
|
|
return original_next;
|
|
},
|
|
configurable: true
|
|
});
|
|
return 3;
|
|
},
|
|
configurable: true
|
|
});
|
|
|
|
assertEquals(10, sum(...a));
|
|
assertEquals(0, called);
|
|
|
|
Object.defineProperty(ArrayIteratorPrototype, 'next',
|
|
ArrayIteratorPrototypeNextDescriptor);
|
|
Object.defineProperty(Array.prototype, 2, {});
|
|
})();
|
|
|
|
(function testArrayHasOtherPrototype() {
|
|
function countArgs() { return arguments.length; }
|
|
var a = [1, 2, 3];
|
|
var b = {};
|
|
Object.defineProperty(b, Symbol.iterator, {
|
|
value: function*() {
|
|
yield 4;
|
|
},
|
|
configurable: true
|
|
});
|
|
|
|
Object.setPrototypeOf(a, b);
|
|
|
|
assertEquals(1, countArgs(...a));
|
|
})();
|
|
|
|
(function testArrayIteratorPrototypeGetter() {
|
|
function countArgs() { return arguments.length; }
|
|
var a = [1, 2, 3];
|
|
var ai = a[Symbol.iterator]();
|
|
var called = 0;
|
|
|
|
var original_next = ai.__proto__["next"];
|
|
|
|
Object.defineProperty(ai.__proto__, "next", {
|
|
get: function() {
|
|
called++;
|
|
return original_next;
|
|
}
|
|
});
|
|
|
|
countArgs(...a);
|
|
|
|
// .next method is only accessed during iteration prologue (see
|
|
// https://github.com/tc39/ecma262/pull/988)
|
|
assertEquals(1, called);
|
|
})();
|
|
|
|
(function testArrayIteratorPrototypeModified() {
|
|
function countArgs() { return arguments.length; }
|
|
var a = [1,2,3];
|
|
var ai = a[Symbol.iterator]();
|
|
Object.defineProperty(ai.__proto__, "next", {
|
|
value: function() {
|
|
return {value: undefined, done: true};
|
|
},
|
|
configurable: true
|
|
});
|
|
|
|
assertEquals(0, countArgs(...a));
|
|
|
|
})();
|
|
|
|
(function testCustomArrayPrototypeIterator() {
|
|
var origIterator =
|
|
Object.getOwnPropertyDescriptor(Array.prototype, Symbol.iterator);
|
|
Object.defineProperty(Array.prototype, Symbol.iterator, {
|
|
value: function*() {
|
|
yield 1;
|
|
yield 2;
|
|
yield 3;
|
|
},
|
|
configurable: true
|
|
});
|
|
function returnCountStrict() { 'use strict'; return arguments.length; }
|
|
function returnCountSloppy() { return arguments.length; }
|
|
|
|
assertEquals(3, returnCountStrict(...[1]));
|
|
assertEquals(4, returnCountStrict(1, ...[2]));
|
|
assertEquals(5, returnCountStrict(1, ...[2], 3));
|
|
assertEquals(3, returnCountSloppy(...[1]));
|
|
assertEquals(4, returnCountSloppy(1, ...[2]));
|
|
assertEquals(5, returnCountSloppy(1, ...[2], 3));
|
|
|
|
Object.defineProperty(Array.prototype, Symbol.iterator, origIterator);
|
|
})();
|
|
|
|
(function testGetPropertyIteratorCalledExactlyOnce() {
|
|
function countArgs() { return arguments.length; }
|
|
var a = [1, 2, 3];
|
|
var called = 0;
|
|
|
|
Object.defineProperty(Array.prototype, Symbol.iterator, {
|
|
value: function*() {
|
|
yield 1;
|
|
yield 2;
|
|
},
|
|
configurable: true
|
|
});
|
|
|
|
var it = a[Symbol.iterator];
|
|
Object.defineProperty(a, Symbol.iterator, {
|
|
get: function() {
|
|
called++;
|
|
return it;
|
|
}
|
|
});
|
|
|
|
countArgs(...a);
|
|
|
|
assertEquals(1, called);
|
|
})();
|