| function assert(b) { |
| if (!b) |
| throw new Error("Bad assertion"); |
| } |
| |
| function test(f) { f(); } |
| |
| function shallowEq(a, b) { |
| if (a.length !== b.length) |
| return false; |
| for (let i = 0; i < a.length; ++i) { |
| if (a[i] !== b[i]) |
| return false; |
| } |
| return true; |
| } |
| |
| function makeArrayIterator(arr, f) { |
| let i = 0; |
| return { |
| next() { |
| f(); |
| if (i >= arr.length) |
| return {done: true}; |
| return {value: arr[i++], done: false}; |
| } |
| }; |
| } |
| |
| test(function() { |
| let arr = [10, 20]; |
| arr.__proto__ = {[Symbol.iterator]: Array.prototype[Symbol.iterator]}; |
| function bar(a) { |
| a.x; |
| return [...a]; |
| } |
| noInline(bar); |
| for (let i = 0; i < 10000; i++) { |
| assert(shallowEq(bar(arr), arr)); |
| } |
| }); |
| |
| test(function() { |
| let arr = [10, 20]; |
| let count = 0; |
| function callback() { |
| count++; |
| } |
| |
| arr.__proto__ = { |
| [Symbol.iterator]: function() { |
| return makeArrayIterator(this, callback); |
| } |
| }; |
| |
| function bar(a) { |
| a.x; |
| return [...a]; |
| } |
| noInline(bar); |
| for (let i = 0; i < 10000; i++) { |
| let t = bar(arr); |
| assert(count === 3); |
| count = 0; |
| assert(shallowEq(t, arr)); |
| } |
| }); |
| |
| test(function() { |
| let arr = [10, 20]; |
| let count = 0; |
| function callback() { |
| count++; |
| } |
| |
| arr[Symbol.iterator] = function() { |
| return makeArrayIterator(this, callback); |
| }; |
| |
| function bar(a) { |
| a.x; |
| return [...a]; |
| } |
| noInline(bar); |
| for (let i = 0; i < 10000; i++) { |
| let t = bar(arr); |
| assert(count === 3); |
| count = 0; |
| assert(shallowEq(t, arr)); |
| } |
| }); |
| |
| test(function() { |
| let arr = [10, 20]; |
| arr[Symbol.iterator] = Array.prototype[Symbol.iterator]; |
| function bar(a) { |
| a.x; |
| return [...a]; |
| } |
| noInline(bar); |
| for (let i = 0; i < 10000; i++) { |
| assert(shallowEq(bar(arr), arr)); |
| } |
| }); |
| |
| test(function() { |
| let arr = [, 20]; |
| let callCount = 0; |
| Object.defineProperty(arr, 0, {get() { ++callCount; return 10; }}); |
| function bar(a) { |
| a.x; |
| return [...a]; |
| } |
| noInline(bar); |
| for (let i = 0; i < 10000; i++) { |
| let t = bar(arr); |
| assert(callCount === 1); |
| assert(shallowEq(t, arr)); |
| assert(callCount === 2); |
| callCount = 0; |
| } |
| }); |
| |
| // We run this test last since it fires watchpoints for the protocol. |
| test(function() { |
| let iter = [][Symbol.iterator](); |
| let iterProto = Object.getPrototypeOf(iter); |
| let oldNext = iterProto.next; |
| |
| function hackedNext() { |
| let val = oldNext.call(this); |
| if ("value" in val) { |
| val.value++; |
| } |
| return val; |
| } |
| |
| function test(a) { |
| a.x; |
| return [...a]; |
| } |
| |
| for (let i = 0; i < 10000; ++i) { |
| let arr = [1,,3]; |
| let callCount = 0; |
| Object.defineProperty(arr, 1, { get: function() { ++callCount; iterProto.next = hackedNext; return 2; } }); |
| let t = test(arr); |
| assert(callCount === 1); |
| assert(t.length === 3); |
| assert(t[0] === 1); |
| assert(t[1] === 2); |
| assert(t[2] === 3); |
| iterProto.next = oldNext; |
| } |
| }); |