| //@ skip if $architecture == "x86" |
| |
| var createBuiltin = $vm.createBuiltin; |
| |
| // This is pretty bad but I need a private name. |
| var putFuncToPrivateName = createBuiltin(`(function (func) { @arrayIteratorIsDone = func })`) |
| putFuncToPrivateName(function (a,b) { return b; }) |
| |
| function createTailCallForwardingFuncWith(body, thisValue) { |
| return createBuiltin(`(function (a) { |
| "use strict"; |
| |
| ${body} |
| |
| return @tailCallForwardArguments(@arrayIteratorIsDone, ${thisValue}); |
| })`); |
| } |
| |
| var foo = createTailCallForwardingFuncWith("", "@undefined"); |
| |
| function baz() { |
| return foo.call(true, 7); |
| } |
| noInline(baz); |
| |
| |
| |
| var fooNoInline = createTailCallForwardingFuncWith("", "@undefined"); |
| noInline(foo); |
| |
| for (let i = 0; i < 100000; i++) { |
| if (baz.call() !== undefined) |
| throw new Error(i); |
| if (fooNoInline.call(undefined, 3) !== undefined) |
| throw new Error(i); |
| } |
| |
| putFuncToPrivateName(function () { "use strict"; return { thisValue: this, argumentsValue: arguments}; }); |
| var foo2 = createTailCallForwardingFuncWith("", "this"); |
| var fooNI2 = createTailCallForwardingFuncWith("", "this"); |
| noInline(fooNI2); |
| |
| function baz2() { |
| return foo2.call(true, 7); |
| } |
| noInline(baz2); |
| |
| for (let i = 0; i < 100000; i++) { |
| let result = foo2.call(true, 7); |
| if (result.thisValue !== true || result.argumentsValue.length !== 1 || result.argumentsValue[0] !== 7) |
| throw new Error(i); |
| result = baz2.call(); |
| if (result.thisValue !== true || result.argumentsValue.length !== 1 || result.argumentsValue[0] !== 7) |
| throw new Error(i); |
| result = fooNI2.call(true, 7); |
| if (result.thisValue !== true || result.argumentsValue.length !== 1 || result.argumentsValue[0] !== 7) |
| throw new Error(i); |
| } |
| |
| putFuncToPrivateName(function () { "use strict"; return this; }); |
| var foo3 = createTailCallForwardingFuncWith("", "{ thisValue: this, otherValue: 'hello'} "); |
| var fooNI3 = createTailCallForwardingFuncWith("", "{ thisValue: this, otherValue: 'hello'} "); |
| noInline(fooNI3); |
| function baz3() { |
| return foo3.call(true, 7); |
| } |
| noInline(baz3); |
| |
| for (let i = 0; i < 100000; i++) { |
| let result = foo3.call(true, 7); |
| if (result.thisValue !== true) |
| throw new Error(i); |
| result = baz3.call(); |
| if (result.thisValue !== true) |
| throw new Error(i); |
| result = fooNI3.call(true, 7); |
| if (result.thisValue !== true) |
| throw new Error(i); |
| } |
| |
| |
| putFuncToPrivateName(function () { "use strict"; return this; }); |
| let bodyText = ` |
| for (let i = 0; i < 100; i++) { |
| if (a + i === 100) |
| return a; |
| } |
| `; |
| var foo4 = createTailCallForwardingFuncWith(bodyText, "{ thisValue: this, otherValue: 'hello'} "); |
| var fooNI4 = createTailCallForwardingFuncWith(bodyText, "{ thisValue: this, otherValue: 'hello'} "); |
| noInline(fooNI4); |
| function baz4() { |
| return foo4.call(true, 0); |
| } |
| noInline(baz4); |
| |
| for (let i = 0; i < 100000; i++) { |
| let result = foo4.call(true, 0); |
| if (result.thisValue !== true || result.otherValue !== "hello") |
| throw new Error(i); |
| result = baz4.call(); |
| if (result.thisValue !== true || result.otherValue !== "hello") |
| throw new Error(i); |
| result = fooNI4.call(true, 0); |
| if (result.thisValue !== true || result.otherValue !== "hello") |
| throw new Error(i); |
| result = fooNI4.call(true, 1); |
| if (result !== 1) |
| throw new Error(i); |
| result = fooNI4.call(true, ""); |
| if (result.thisValue !== true || result.otherValue !== "hello") |
| throw new Error(i); |
| } |
| |
| var testFunc = function () { "use strict"; return this; } |
| noInline(testFunc); |
| putFuncToPrivateName(testFunc); |
| |
| var foo5 = createTailCallForwardingFuncWith(bodyText, "{ thisValue: this, otherValue: 'hello'} "); |
| var fooNI5 = createTailCallForwardingFuncWith(bodyText, "{ thisValue: this, otherValue: 'hello'} "); |
| noInline(fooNI5); |
| function baz5() { |
| return foo5.call(true, 0); |
| } |
| noInline(baz5); |
| |
| for (let i = 0; i < 100000; i++) { |
| let result = foo5.call(true, 0); |
| if (result.thisValue !== true || result.otherValue !== "hello") |
| throw new Error(i); |
| result = baz5.call(); |
| if (result.thisValue !== true || result.otherValue !== "hello") |
| throw new Error(i); |
| result = fooNI5.call(true, 0); |
| if (result.thisValue !== true || result.otherValue !== "hello") |
| throw new Error(i); |
| result = fooNI5.call(true, 1); |
| if (result !== 1) |
| throw new Error(i); |
| result = fooNI5.call(true, ""); |
| if (result.thisValue !== true || result.otherValue !== "hello") |
| throw new Error(i); |
| } |
| |
| putFuncToPrivateName(function() { return arguments; }); |
| var foo6 = createTailCallForwardingFuncWith(bodyText, "{ thisValue: this, otherValue: 'hello'} "); |
| function baz6() { |
| "use strict" |
| return foo6.apply(this, arguments); |
| } |
| noInline(baz6); |
| |
| function arrayEq(a, b) { |
| if (a.length !== b.length) |
| throw new Error(); |
| for (let i = 0; i < a.length; i++) { |
| if (a[i] !== b[i]) |
| throw new Error(); |
| } |
| } |
| let args = ["a", {}, [], Symbol(), 1, 1.234, undefined, null]; |
| for (let i = 0; i < 100000; i++) { |
| let result = baz6.apply(undefined, args); |
| arrayEq(result, args); |
| } |