blob: 978f02ad026b8814f81eba786472b1976cbd0aa6 [file] [log] [blame]
// 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: --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();