// 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 --no-turbo-inlining // TODO(v8:4698), TODO(ishell): support these cases. // Flags: --no-turbo --nostress-opt Error.prepareStackTrace = (error,stack) => { error.strace = stack; return error.message + "\n at " + stack.join("\n at "); } 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); } function run_tests() { 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"); 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);`, ` assertEquals_(undefined, new.target);`, check_arguments, ` checkStackTrace_([f, test]);`, ].join("\n"); var lines = [ `function f(a) {`, ` ${inlinable_comment(cfg.f_inlinable)}`, ` var expected_args = [${cfg.f_args}];`, do_checks, ` %DeoptimizeNow();`, 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);`, ` assertEquals_(undefined, new.target);`, check_arguments, ` checkStackTrace_([f, test]);`, ].join("\n"); var lines = [ `function f(a) {`, ` "use strict";`, ` ${inlinable_comment(cfg.f_inlinable)}`, ` var expected_args = [${cfg.f_args}];`, do_checks, ` %DeoptimizeNow();`, 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);`, ` assertEquals_(undefined, new.target);`, check_arguments, ` checkStackTrace_([f, test]);`, ].join("\n"); var lines = [ `function f(a) {`, ` ${inlinable_comment(cfg.f_inlinable)}`, ` var expected_args = [${cfg.f_args}];`, do_checks, ` %DeoptimizeNow();`, 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);`, ` assertEquals_(undefined, new.target);`, check_arguments, ` checkStackTrace_([f, test]);`, ].join("\n"); var lines = [ `function f(a) {`, ` "use strict";`, ` ${inlinable_comment(cfg.f_inlinable)}`, ` var expected_args = [${cfg.f_args}];`, do_checks, ` %DeoptimizeNow();`, 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);`, ` assertEquals_(undefined, new.target);`, check_arguments, ` checkStackTrace_([f, test]);`, ].join("\n"); var lines = [ `function f(a) {`, ` ${inlinable_comment(cfg.f_inlinable)}`, ` var expected_args = [${cfg.f_args}];`, do_checks, ` %DeoptimizeNow();`, 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_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};`, ``, ` // Don't inline helper functions`, ` %NeverOptimizeFunction(assertEquals);`, ` %NeverOptimizeFunction(checkStackTrace);`, ``, f_source, g_source, ` function test() {`, ` "use strict";`, ` assertEquals_(42, g(${cfg.g_args}));`, ` }`, ` ${cfg.f_inlinable ? "%SetForceInlineFlag(f)" : ""};`, ` ${cfg.g_inlinable ? "%SetForceInlineFlag(g)" : ""};`, ` ${"test();".repeat(cfg.test_warmup_count)}`, ` %OptimizeFunctionOnNextCall(test);`, ` %OptimizeFunctionOnNextCall(f);`, ` %OptimizeFunctionOnNextCall(g);`, ` test();`, `})();`, ``, ]; var source = lines.join("\n"); return source; } // TODO(v8:4698), TODO(ishell): support all commented cases. 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]; 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_function_call, g_cfg_function_apply, g_cfg_function_apply_arguments_object, ]; var test_warmup_counts = [0, 1, 2]; f_variants.forEach((f_cfg) => { 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) => { 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, }; var source = test_template(cfg); print("===================="); print(source); eval(source); }); }); }); }); }); }); }); } run_tests();