// Copyright 2016 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 --harmony-tailcalls Error.prepareStackTrace = (error,stack) => { error.strace = stack; return error.message + "\n at " + stack.join("\n at "); } var verbose = typeof(arguments) !== "undefined" && arguments.indexOf("-v") >= 0; function checkStackTrace(expected) { var e = new Error(); e.stack; // prepare stack trace var stack = e.strace; assertEquals("checkStackTrace", stack[0].getFunctionName()); for (var i = 0; i < expected.length; i++) { assertEquals(expected[i].name, stack[i + 1].getFunctionName()); } } var CAN_INLINE_COMMENT = "// Let it be inlined."; var DONT_INLINE_COMMENT = (function() { var line = "// Don't inline. Don't inline. Don't inline. Don't inline."; for (var i = 0; i < 4; i++) { line += "\n " + line; } return line; })(); function ident_source(source, ident) { ident = " ".repeat(ident); return ident + source.replace(/\n/gi, "\n" + ident); } var SHARDS_COUNT = 10; function run_tests(shard) { function inlinable_comment(inlinable) { return inlinable ? CAN_INLINE_COMMENT : DONT_INLINE_COMMENT; } // Check arguments manually to avoid bailing out with reason "bad value // context for arguments value". function check_arguments_template(expected_name) { var lines = [ ` assertEquals_(${expected_name}.length, arguments.length);`, ` for (var i = 0; i < ${expected_name}.length; i++) {`, ` assertEquals_(${expected_name}[i], arguments[i]);`, ` }`, ]; return lines.join("\n"); } var check_arguments = check_arguments_template("expected_args"); function deopt_template(deopt_mode) { switch(deopt_mode) { case "none": return " // Don't deoptimize"; case "f": case "g": case "test": return ` %DeoptimizeFunction(${deopt_mode});`; default: assertUnreachable(); } } var f_cfg_sloppy = { func_name: 'f', source_template: function(cfg) { var receiver = cfg.f_receiver != undefined ? cfg.f_receiver : "global"; var do_checks = [ ` assertEquals_(${receiver}, this);`, ` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`, check_arguments, ` checkStackTrace_([f, test]);`, ].join("\n"); var lines = [ `function f(a) {`, ` ${inlinable_comment(cfg.f_inlinable)}`, ` counter++;`, ` var expected_args = [${cfg.f_args}];`, do_checks, deopt_template(cfg.deopt_mode), do_checks, ` return 42;`, `}`, ]; return lines.join("\n"); }, }; var f_cfg_strict = { func_name: 'f', source_template: function(cfg) { var receiver = cfg.f_receiver != undefined ? cfg.f_receiver : "undefined"; var do_checks = [ ` assertEquals_(${receiver}, this);`, ` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`, check_arguments, ` checkStackTrace_([f, test]);`, ].join("\n"); var lines = [ `function f(a) {`, ` "use strict";`, ` ${inlinable_comment(cfg.f_inlinable)}`, ` counter++;`, ` var expected_args = [${cfg.f_args}];`, do_checks, deopt_template(cfg.deopt_mode), do_checks, ` return 42;`, `}`, ]; return lines.join("\n"); }, }; var f_cfg_possibly_eval = { func_name: 'eval', source_template: function(cfg) { var receiver = cfg.f_receiver != undefined ? cfg.f_receiver : "global"; var do_checks = [ ` assertEquals_(${receiver}, this);`, ` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`, check_arguments, ` checkStackTrace_([f, test]);`, ].join("\n"); var lines = [ `function f(a) {`, ` ${inlinable_comment(cfg.f_inlinable)}`, ` counter++;`, ` var expected_args = [${cfg.f_args}];`, do_checks, deopt_template(cfg.deopt_mode), do_checks, ` return 42;`, `}`, `var eval = f;`, ]; return lines.join("\n"); }, }; var f_cfg_bound = { func_name: 'bound', source_template: function(cfg) { var do_checks = [ ` assertEquals_(receiver, this);`, ` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`, check_arguments, ` checkStackTrace_([f, test]);`, ].join("\n"); var lines = [ `function f(a) {`, ` "use strict";`, ` ${inlinable_comment(cfg.f_inlinable)}`, ` counter++;`, ` var expected_args = [${cfg.f_args}];`, do_checks, deopt_template(cfg.deopt_mode), do_checks, ` return 42;`, `}`, `var receiver = {a: 153};`, `var bound = f.bind(receiver);`, ]; return lines.join("\n"); }, }; var f_cfg_proxy = { func_name: 'p', source_template: function(cfg) { var receiver = cfg.f_receiver != undefined ? cfg.f_receiver : "global"; var do_checks = [ ` assertEquals_(${receiver}, this);`, ` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`, check_arguments, ` checkStackTrace_([f, test]);`, ].join("\n"); var lines = [ `function f(a) {`, ` ${inlinable_comment(cfg.f_inlinable)}`, ` counter++;`, ` var expected_args = [${cfg.f_args}];`, do_checks, deopt_template(cfg.deopt_mode), do_checks, ` return 42;`, `}`, `var p = new Proxy(f, {});`, ]; return lines.join("\n"); }, }; var g_cfg_normal = { receiver: undefined, source_template: function(cfg) { var lines = [ `function g(a) {`, ` "use strict";`, ` ${inlinable_comment(cfg.g_inlinable)}`, ` var expected_args = [${cfg.g_args}];`, check_arguments, ` return ${cfg.f_name}(${cfg.f_args});`, `}`, ]; return lines.join("\n"); }, }; var g_cfg_reflect_apply = { receiver: "the_receiver", source_template: function(cfg) { var lines = [ `function g(a) {`, ` "use strict";`, ` ${inlinable_comment(cfg.g_inlinable)}`, ` var expected_args = [${cfg.g_args}];`, check_arguments, ` return Reflect.apply(${cfg.f_name}, the_receiver, [${cfg.f_args}]);`, `}`, ]; return lines.join("\n"); }, }; var g_cfg_function_apply = { receiver: "the_receiver", source_template: function(cfg) { var lines = [ `function g(a) {`, ` "use strict";`, ` ${inlinable_comment(cfg.g_inlinable)}`, ` var expected_args = [${cfg.g_args}];`, check_arguments, ` return ${cfg.f_name}.apply(the_receiver, [${cfg.f_args}]);`, `}`, ]; return lines.join("\n"); }, }; var g_cfg_function_apply_arguments_object = { receiver: "the_receiver", source_template: function(cfg) { cfg.f_args = cfg.g_args; var lines = [ `function g(a) {`, ` "use strict";`, ` ${inlinable_comment(cfg.g_inlinable)}`, ` var expected_args = [${cfg.g_args}];`, check_arguments, ` return ${cfg.f_name}.apply(the_receiver, arguments);`, `}`, ]; return lines.join("\n"); }, }; var g_cfg_function_call = { receiver: "the_receiver", source_template: function(cfg) { var f_args = "the_receiver"; if (cfg.f_args !== "") f_args += ", "; f_args += cfg.f_args; var lines = [ `function g(a) {`, ` "use strict";`, ` ${inlinable_comment(cfg.g_inlinable)}`, ` var expected_args = [${cfg.g_args}];`, check_arguments, ` return ${cfg.f_name}.call(${f_args});`, `}`, ]; return lines.join("\n"); }, }; function test_template(cfg) { // Note: g_source_template modifies cfg.f_args in some cases. var g_source = cfg.g_source_template(cfg); g_source = ident_source(g_source, 2); var f_source = cfg.f_source_template(cfg); f_source = ident_source(f_source, 2); var lines = [ `(function() {`, ` // Avoid bailing out because of "Reference to a variable which requires dynamic lookup".`, ` var assertEquals_ = assertEquals;`, ` var checkStackTrace_ = checkStackTrace;`, ` var undefined = void 0;`, ` var global = Function('return this')();`, ` var the_receiver = {receiver: 1};`, ` var counter = 0;`, ``, ` // Don't inline helper functions`, ` %NeverOptimizeFunction(assertEquals);`, ` %NeverOptimizeFunction(checkStackTrace);`, ``, f_source, g_source, ` function test() {`, ` "use strict";`, ` assertEquals_(42, g(${cfg.g_args}));`, ` }`, ` ${"test();".repeat(cfg.test_warmup_count)}`, ` ${cfg.f_inlinable ? "%SetForceInlineFlag(f)" : "%OptimizeFunctionOnNextCall(f)"};`, ` ${cfg.g_inlinable ? "%SetForceInlineFlag(g)" : "%OptimizeFunctionOnNextCall(g)"};`, ` %OptimizeFunctionOnNextCall(test);`, ` test();`, ` assertEquals(${1 + cfg.test_warmup_count}, counter);`, `})();`, ``, ]; var source = lines.join("\n"); return source; } var f_args_variants = [/*"", "1",*/ "1, 2"]; var g_args_variants = [/*"", "10",*/ "10, 20"]; var f_inlinable_variants = [true, false]; var g_inlinable_variants = [true, false]; // This is to avoid bailing out because of referencing new.target. var check_new_target_variants = [/*true,*/ false]; var deopt_mode_variants = ["none", "f", "g", "test"]; var f_variants = [ f_cfg_sloppy, f_cfg_strict, f_cfg_bound, f_cfg_proxy, // f_cfg_possibly_eval, ]; var g_variants = [ g_cfg_normal, // g_cfg_reflect_apply, g_cfg_function_apply, // g_cfg_function_apply_arguments_object, g_cfg_function_call, ]; var test_warmup_counts = [0, 1, 2]; var iter = 0; var tests_executed = 0; if (verbose && shard !== undefined) { print("Running shard #" + shard); } f_variants.forEach((f_cfg) => { check_new_target_variants.forEach((check_new_target) => { deopt_mode_variants.forEach((deopt_mode) => { g_variants.forEach((g_cfg) => { f_args_variants.forEach((f_args) => { g_args_variants.forEach((g_args) => { f_inlinable_variants.forEach((f_inlinable) => { g_inlinable_variants.forEach((g_inlinable) => { test_warmup_counts.forEach((test_warmup_count) => { if (shard !== undefined && (iter++) % SHARDS_COUNT != shard) { if (verbose) { print("skipping..."); } return; } tests_executed++; var cfg = { f_source_template: f_cfg.source_template, f_inlinable, f_args, f_name: f_cfg.func_name, f_receiver: g_cfg.receiver, g_source_template: g_cfg.source_template, g_inlinable, g_args, test_warmup_count, check_new_target, deopt_mode, }; var source = test_template(cfg); if (verbose) { // print("===================="); // print(source); } eval(source); }); }); }); }); }); }); }); }); }); if (verbose) { print("Number of tests executed: " + tests_executed); } } // Uncomment to run all the tests at once or use shard runners. //run_tests();