diff --git a/tests/ecmascript/test-bi-function-call-apply-bind-combinations.js b/tests/ecmascript/test-bi-function-call-apply-bind-combinations.js new file mode 100644 index 00000000..317894fb --- /dev/null +++ b/tests/ecmascript/test-bi-function-call-apply-bind-combinations.js @@ -0,0 +1,91 @@ +/* + * Test that .bind(), .call(), and .apply() can be used in various + * combinations. + */ + +/*=== +[object global] 1 2 3 undefined undefined undefined undefined undefined undefined undefined +arguments: 3 1 2 3 undefined undefined undefined undefined undefined undefined undefined undefined undefined +mythis 101 102 undefined undefined undefined undefined undefined undefined undefined undefined +arguments: 2 101 102 undefined undefined undefined undefined undefined undefined undefined undefined undefined undefined +mythis 101 102 103 undefined undefined undefined undefined undefined undefined undefined +arguments: 3 101 102 103 undefined undefined undefined undefined undefined undefined undefined undefined undefined +mythis 101 102 103 104 undefined undefined undefined undefined undefined undefined +arguments: 4 101 102 103 104 undefined undefined undefined undefined undefined undefined undefined undefined +mythis 101 102 103 104 105 undefined undefined undefined undefined undefined +arguments: 5 101 102 103 104 105 undefined undefined undefined undefined undefined undefined undefined +mythis 101 102 103 104 105 106 undefined undefined undefined undefined +arguments: 6 101 102 103 104 105 106 undefined undefined undefined undefined undefined undefined +mythis 101 102 103 104 105 106 107 undefined undefined undefined +arguments: 7 101 102 103 104 105 106 107 undefined undefined undefined undefined undefined +boundcallthis 201 202 303 304 undefined undefined undefined undefined undefined undefined +arguments: 4 201 202 303 304 undefined undefined undefined undefined undefined undefined undefined undefined +boundcallthis 201 202 303 304 undefined undefined undefined undefined undefined undefined +arguments: 4 201 202 303 304 undefined undefined undefined undefined undefined undefined undefined undefined +mythis 101 202 203 304 305 undefined undefined undefined undefined undefined +arguments: 5 101 202 203 304 305 undefined undefined undefined undefined undefined undefined undefined +mythis 101 202 203 304 305 undefined undefined undefined undefined undefined +arguments: 5 101 202 203 304 305 undefined undefined undefined undefined undefined undefined undefined +boundapplythis 201 202 undefined undefined undefined undefined undefined undefined undefined undefined +arguments: 2 201 202 undefined undefined undefined undefined undefined undefined undefined undefined undefined undefined +boundapplythis 201 202 undefined undefined undefined undefined undefined undefined undefined undefined +arguments: 2 201 202 undefined undefined undefined undefined undefined undefined undefined undefined undefined undefined +mythis 101 202 203 undefined undefined undefined undefined undefined undefined undefined +arguments: 3 101 202 203 undefined undefined undefined undefined undefined undefined undefined undefined undefined +mythis 101 202 203 undefined undefined undefined undefined undefined undefined undefined +arguments: 3 101 202 203 undefined undefined undefined undefined undefined undefined undefined undefined undefined +===*/ + +function test() { + var f0 = function f0(a, b, c, d, e, f, g, h, i, j) { + print(this, a, b, c, d, e, f, g, h, i, j); + print('arguments:', arguments.length, + arguments[0], arguments[1], arguments[2], arguments[3], + arguments[4], arguments[5], arguments[6], arguments[7], + arguments[8], arguments[9], arguments[10], arguments[11]); + }; + + // Direct call. + f0(1, 2, 3); + + // Bound call. + var f1 = f0.bind('mythis', 101); + f1(102); + + // Bound call, call via .call(). + f1.call('callthis', 102, 103); + + // Bound call, call via .call(); but use .apply() to call .call(). + f1.call.apply(f1, [ 'callthis', 102, 103, 104 ]); + + // Bound call, call via .apply(). + f1.apply('applythis', [ 102, 103, 104, 105 ]); + + // Bound call, call via .apply(); but use .call() to call .apply(). + f1.apply.call(f1, 'applythis', [ 102, 103, 104, 105, 106 ]); + + // Bound call, call via .apply(); but use .apply() to call .apply(). + f1.apply.apply(f1, [ 'applythis', [ 102, 103, 104, 105, 106, 107 ] ]); + + // Use .bind() on .call(). + var f2 = f0.call.bind(f0, 'boundcallthis', 201, 202); + f2(303, 304); + f2.call('ignored', 303, 304); + var f3 = f1.call.bind(f1, 'boundcallthis', 202, 203); + f3(304, 305); + f3.apply('ignored', [ 304, 305 ]); + + // Use .bind() on .apply(). + var f4 = f0.apply.bind(f0, 'boundapplythis', [ 201, 202 ]); + f4(303, 304); + f4.call('ignored', 303, 304); // these args get ignored because they don't affect [ 201, 202 ] + var f5 = f1.apply.bind(f1, 'boundapplythis', [ 202, 203 ]); + f5(304, 305); + f5.apply('ignored', [ 304, 305 ]); // same here, [ 304, 305 ] is ignored by .apply() +} + +try { + test(); +} catch (e) { + print(e.stack || e); +} diff --git a/tests/ecmascript/test-bi-function-proto-apply-hugeargs.js b/tests/ecmascript/test-bi-function-proto-apply-hugeargs.js new file mode 100644 index 00000000..acff3e56 --- /dev/null +++ b/tests/ecmascript/test-bi-function-proto-apply-hugeargs.js @@ -0,0 +1,33 @@ +/*=== +f0 called +applythis arg-0 arg-1 arg-2 +500000 +arg-499998 +arg-499999 +undefined +done +===*/ + +function test() { + function f0(a, b, c) { + print('f0 called'); + print(this, a, b, c); + print(arguments.length); + print(arguments[499998]); + print(arguments[499999]); + print(arguments[500000]); + } + + var args = []; + while (args.length < 500000) { + args.push('arg-' + args.length); + } + + f0.apply('applythis', args); + print('done'); +} +try { + test(); +} catch (e) { + print(e.stack || e); +} diff --git a/tests/ecmascript/test-bi-function-proto-apply-tail.js b/tests/ecmascript/test-bi-function-proto-apply-tail.js new file mode 100644 index 00000000..237cf9b9 --- /dev/null +++ b/tests/ecmascript/test-bi-function-proto-apply-tail.js @@ -0,0 +1,30 @@ +/*=== +0 +100000 +200000 +300000 +400000 +500000 +600000 +700000 +800000 +900000 +1000000 +done +===*/ + +function test(count) { + if ((count % 1e5) == 0) { + print(count); + } + if (count < 1e6) { + return test.apply(null, [ count + 1 ]); + } + return 'done'; +} + +try { + print(test(0)); +} catch (e) { + print(e.stack || e); +} diff --git a/tests/ecmascript/test-bi-function-proto-call-hugeargs.js b/tests/ecmascript/test-bi-function-proto-call-hugeargs.js new file mode 100644 index 00000000..7f9047dc --- /dev/null +++ b/tests/ecmascript/test-bi-function-proto-call-hugeargs.js @@ -0,0 +1,36 @@ +/*=== +f0 called +callthis arg-0 arg-1 arg-2 +500000 +arg-499998 +arg-499999 +undefined +done +===*/ + +function test() { + function f0(a, b, c) { + print('f0 called'); + print(this, a, b, c); + print(arguments.length); + print(arguments[499998]); + print(arguments[499999]); + print(arguments[500000]); + } + + var args = []; + while (args.length < 500000) { + args.push('arg-' + args.length); + } + + args.unshift('callthis'); + + // Call .call() via .apply() to ensure huge argument count makes it. + f0.call.apply(f0, args); + print('done'); +} +try { + test(); +} catch (e) { + print(e.stack || e); +} diff --git a/tests/ecmascript/test-bi-function-proto-call-tail.js b/tests/ecmascript/test-bi-function-proto-call-tail.js new file mode 100644 index 00000000..5e57d44e --- /dev/null +++ b/tests/ecmascript/test-bi-function-proto-call-tail.js @@ -0,0 +1,30 @@ +/*=== +0 +100000 +200000 +300000 +400000 +500000 +600000 +700000 +800000 +900000 +1000000 +done +===*/ + +function test(count) { + if ((count % 1e5) == 0) { + print(count); + } + if (count < 1e6) { + return test.call(null, count + 1); + } + return 'done'; +} + +try { + print(test(0)); +} catch (e) { + print(e.stack || e); +} diff --git a/tests/ecmascript/test-bi-reflect-apply-tail.js b/tests/ecmascript/test-bi-reflect-apply-tail.js new file mode 100644 index 00000000..aba00a35 --- /dev/null +++ b/tests/ecmascript/test-bi-reflect-apply-tail.js @@ -0,0 +1,30 @@ +/*=== +0 +100000 +200000 +300000 +400000 +500000 +600000 +700000 +800000 +900000 +1000000 +done +===*/ + +function test(count) { + if ((count % 1e5) == 0) { + print(count); + } + if (count < 1e6) { + return Reflect.apply(test, null, [ count + 1 ]); + } + return 'done'; +} + +try { + print(test(0)); +} catch (e) { + print(e.stack || e); +} diff --git a/tests/ecmascript/test-dev-call-apply-not-in-callstack.js b/tests/ecmascript/test-dev-call-apply-not-in-callstack.js new file mode 100644 index 00000000..01b84e58 --- /dev/null +++ b/tests/ecmascript/test-dev-call-apply-not-in-callstack.js @@ -0,0 +1,40 @@ +/* + * Since Duktape 2.2 .call() and .apply() no longer visible in call stack. + */ + +/*=== +f0 called +-1 act +-2 f0 +-3 f1 +-4 f2 +-5 f3 +-6 test +-7 global +===*/ + +function test() { + var f0 = function f0() { + print('f0 called'); + for (var i = -1; ; i--) { + var act = Duktape.act(i); + if (!act) { break; } + print(i, act.function.name); + } + }; + var f1 = function f1() { + Reflect.apply(f0, 'dummy'); + } + var f2 = function f2() { + f1.call('dummy'); + } + var f3 = function f3() { + f2.apply('dummy'); + } + f3(); +} +try { + test(); +} catch (e) { + print(e.stack || e); +} diff --git a/tests/ecmascript/test-dev-func-call-apply-missing-args.js b/tests/ecmascript/test-dev-func-call-apply-missing-args.js new file mode 100644 index 00000000..64f9fa4c --- /dev/null +++ b/tests/ecmascript/test-dev-func-call-apply-missing-args.js @@ -0,0 +1,111 @@ +/* + * Some tests for .call() and .apply() with missing arguments. + */ + +/*=== +- Function.prototype.call() +undefined undefined undefined undefined +mythis undefined undefined undefined +mythis arg undefined undefined +- Function.prototype.apply() +undefined undefined undefined undefined +mythis undefined undefined undefined +mythis undefined undefined undefined +mythis undefined undefined undefined +mythis undefined undefined undefined +mythis arg undefined undefined +mythis undefined undefined undefined +mythis foo bar undefined +mythis FOO BAR undefined +TypeError +- Reflect.apply() +TypeError +TypeError +undefined undefined undefined undefined +mythis undefined undefined undefined +mythis undefined undefined undefined +mythis undefined undefined undefined +mythis undefined undefined undefined +mythis arg undefined undefined +mythis undefined undefined undefined +mythis foo bar undefined +mythis FOO BAR undefined +TypeError +done +===*/ + +function test() { + function func(a,b,c) { + 'use strict'; // avoid 'this' coercion + print(this, a, b, c); + } + + var dummyFunc = function dummy(a,b) {}; // length: 2 + dummyFunc[0] = 'FOO'; + dummyFunc[1] = 'BAR'; + dummyFunc[2] = 'QUUX'; + + // For .call() there are no required arguments. + // This binding will be 'undefined'. + print('- Function.prototype.call()'); + func.call(); + func.call('mythis'); + func.call('mythis', 'arg'); + + // For .apply() there are similarly no required arguments. + // argArray can be null/undefined and is treated like empty + // array. Array-like object is accepted (.length is respected); + // other types cause a TypeError. A function is an acceptable + // input because it's an object and even has a .length! + print('- Function.prototype.apply()'); + func.apply(); + func.apply('mythis'); + func.apply('mythis', void 0); + func.apply('mythis', null); + func.apply('mythis', []); + func.apply('mythis', [ 'arg' ]); + func.apply('mythis', {}); // no .length -> same as [] + func.apply('mythis', { length: 2, 0: 'foo', 1: 'bar', 2: 'quux-ignored' }); + func.apply('mythis', dummyFunc); + try { + func.apply('mythis', 123); + } catch (e) { + print(e.name); + } + + // Reflect.apply() requires a callable first argument, + // but is otherwise similar to Function.prototype.apply(). + print('- Reflect.apply()'); + try { + Reflect.apply(); + } catch (e) { + print(e.name); + } + try { + Reflect.apply(123); + } catch (e) { + print(e.name); + } + Reflect.apply(func); + Reflect.apply(func, 'mythis'); + Reflect.apply(func, 'mythis', void 0); + Reflect.apply(func, 'mythis', null); + Reflect.apply(func, 'mythis', []); + Reflect.apply(func, 'mythis', [ 'arg' ]); + Reflect.apply(func, 'mythis', {}); + Reflect.apply(func, 'mythis', { length: 2, 0: 'foo', 1: 'bar', 2: 'quux-ignored' }); + Reflect.apply(func, 'mythis', dummyFunc); + try { + Reflect.apply(func, 'mythis', 123); + } catch (e) { + print(e.name); + } + + print('done'); +} + +try { + test(); +} catch (e) { + print(e.stack || e); +} diff --git a/tests/ecmascript/test-dev-func-call-apply-no-native-stack.js b/tests/ecmascript/test-dev-func-call-apply-no-native-stack.js new file mode 100644 index 00000000..da0d3fe5 --- /dev/null +++ b/tests/ecmascript/test-dev-func-call-apply-no-native-stack.js @@ -0,0 +1,84 @@ +/* + * Demonstrate that in Duktape 2.2 .call() and .apply() no longer + * consume native call stack when the resolved call is an Ecma-to-Ecma + * call. + * + * Can't test against a specific limit but the native stack limit is + * ~500 calls while the default call stack limit is ~10000 calls. + */ + +/*=== +RangeError +true +RangeError +true +RangeError +true +RangeError +true +done +===*/ + +function test() { + var count; + function f1() { + count++; + f1(); // not a tail call + void count; + } + function f2() { + count++; + f2.call(); // not a tail call + void count; + } + function f3() { + count++; + f3.apply(); // not a tail call + void count; + } + function f4() { + count++; + Reflect.apply(f4); // not a tail call + void count; + } + + try { + count = 0; + f1(); + } catch (e) { + print(e.name); + } + print(count > 2000); + + try { + count = 0; + f2(); + } catch (e) { + print(e.name); + } + print(count > 2000); + + try { + count = 0; + f3(); + } catch (e) { + print(e.name); + } + print(count > 2000); + + try { + count = 0; + f4(); + } catch (e) { + print(e.name); + } + print(count > 2000); + + print('done'); +} + +try { + test(); +} catch (e) { + print(e.stack || e); +} diff --git a/tests/ecmascript/test-dev-yield-after-callapply.js b/tests/ecmascript/test-dev-yield-after-callapply.js index d050e161..36d6053c 100644 --- a/tests/ecmascript/test-dev-yield-after-callapply.js +++ b/tests/ecmascript/test-dev-yield-after-callapply.js @@ -10,10 +10,11 @@ var res; /*=== 123 123 +123 ===*/ /* Calling via Function.prototype.call() or Function.prototype.apply() - * currently prevents a yield. + * no longer prevents a yield in Duktape 2.2. */ function innerfunc() { @@ -21,15 +22,17 @@ function innerfunc() { } function coroutine1() { - // This is a native call so the current (naive) handling prevents a later yield innerfunc.call(); } function coroutine2() { - // Same here innerfunc.apply(); } +function coroutine3() { + Reflect.apply(innerfunc); +} + try { thread = new Duktape.Thread(coroutine1); res = Duktape.Thread.resume(thread, 0); @@ -45,3 +48,11 @@ try { } catch (e) { print(e.name); } + +try { + thread = new Duktape.Thread(coroutine3); + res = Duktape.Thread.resume(thread, 0); + print(res); +} catch (e) { + print(e.name); +}