1// Copyright 2016 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// Flags: --allow-natives-syntax --harmony-tailcalls 6 7 8Error.prepareStackTrace = (error,stack) => { 9 error.strace = stack; 10 return error.message + "\n at " + stack.join("\n at "); 11} 12 13var verbose = typeof(arguments) !== "undefined" && arguments.indexOf("-v") >= 0; 14 15function checkStackTrace(expected) { 16 var e = new Error(); 17 e.stack; // prepare stack trace 18 var stack = e.strace; 19 assertEquals("checkStackTrace", stack[0].getFunctionName()); 20 for (var i = 0; i < expected.length; i++) { 21 assertEquals(expected[i].name, stack[i + 1].getFunctionName()); 22 } 23} 24 25 26var CAN_INLINE_COMMENT = "// Let it be inlined."; 27var DONT_INLINE_COMMENT = (function() { 28 var line = "// Don't inline. Don't inline. Don't inline. Don't inline."; 29 for (var i = 0; i < 4; i++) { 30 line += "\n " + line; 31 } 32 return line; 33})(); 34 35 36function ident_source(source, ident) { 37 ident = " ".repeat(ident); 38 return ident + source.replace(/\n/gi, "\n" + ident); 39} 40 41var SHARDS_COUNT = 10; 42 43function run_tests(shard) { 44 function inlinable_comment(inlinable) { 45 return inlinable ? CAN_INLINE_COMMENT : DONT_INLINE_COMMENT; 46 } 47 48 // Check arguments manually to avoid bailing out with reason "bad value 49 // context for arguments value". 50 function check_arguments_template(expected_name) { 51 var lines = [ 52 ` assertEquals_(${expected_name}.length, arguments.length);`, 53 ` for (var i = 0; i < ${expected_name}.length; i++) {`, 54 ` assertEquals_(${expected_name}[i], arguments[i]);`, 55 ` }`, 56 ]; 57 return lines.join("\n"); 58 } 59 var check_arguments = check_arguments_template("expected_args"); 60 61 function deopt_template(deopt_mode) { 62 switch(deopt_mode) { 63 case "none": 64 return " // Don't deoptimize"; 65 case "f": 66 case "g": 67 case "test": 68 return ` %DeoptimizeFunction(${deopt_mode});`; 69 default: 70 assertUnreachable(); 71 } 72 } 73 74 var f_cfg_sloppy = { 75 func_name: 'f', 76 source_template: function(cfg) { 77 var receiver = cfg.f_receiver != undefined ? cfg.f_receiver 78 : "global"; 79 var do_checks = [ 80 ` assertEquals_(${receiver}, this);`, 81 ` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`, 82 check_arguments, 83 ` checkStackTrace_([f, test]);`, 84 ].join("\n"); 85 86 var lines = [ 87 `function f(a) {`, 88 ` ${inlinable_comment(cfg.f_inlinable)}`, 89 ` counter++;`, 90 ` var expected_args = [${cfg.f_args}];`, 91 do_checks, 92 deopt_template(cfg.deopt_mode), 93 do_checks, 94 ` return 42;`, 95 `}`, 96 ]; 97 return lines.join("\n"); 98 }, 99 }; 100 101 var f_cfg_strict = { 102 func_name: 'f', 103 source_template: function(cfg) { 104 var receiver = cfg.f_receiver != undefined ? cfg.f_receiver 105 : "undefined"; 106 var do_checks = [ 107 ` assertEquals_(${receiver}, this);`, 108 ` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`, 109 check_arguments, 110 ` checkStackTrace_([f, test]);`, 111 ].join("\n"); 112 113 var lines = [ 114 `function f(a) {`, 115 ` "use strict";`, 116 ` ${inlinable_comment(cfg.f_inlinable)}`, 117 ` counter++;`, 118 ` var expected_args = [${cfg.f_args}];`, 119 do_checks, 120 deopt_template(cfg.deopt_mode), 121 do_checks, 122 ` return 42;`, 123 `}`, 124 ]; 125 return lines.join("\n"); 126 }, 127 }; 128 129 var f_cfg_possibly_eval = { 130 func_name: 'eval', 131 source_template: function(cfg) { 132 var receiver = cfg.f_receiver != undefined ? cfg.f_receiver 133 : "global"; 134 var do_checks = [ 135 ` assertEquals_(${receiver}, this);`, 136 ` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`, 137 check_arguments, 138 ` checkStackTrace_([f, test]);`, 139 ].join("\n"); 140 141 var lines = [ 142 `function f(a) {`, 143 ` ${inlinable_comment(cfg.f_inlinable)}`, 144 ` counter++;`, 145 ` var expected_args = [${cfg.f_args}];`, 146 do_checks, 147 deopt_template(cfg.deopt_mode), 148 do_checks, 149 ` return 42;`, 150 `}`, 151 `var eval = f;`, 152 ]; 153 return lines.join("\n"); 154 }, 155 }; 156 157 var f_cfg_bound = { 158 func_name: 'bound', 159 source_template: function(cfg) { 160 var do_checks = [ 161 ` assertEquals_(receiver, this);`, 162 ` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`, 163 check_arguments, 164 ` checkStackTrace_([f, test]);`, 165 ].join("\n"); 166 167 var lines = [ 168 `function f(a) {`, 169 ` "use strict";`, 170 ` ${inlinable_comment(cfg.f_inlinable)}`, 171 ` counter++;`, 172 ` var expected_args = [${cfg.f_args}];`, 173 do_checks, 174 deopt_template(cfg.deopt_mode), 175 do_checks, 176 ` return 42;`, 177 `}`, 178 `var receiver = {a: 153};`, 179 `var bound = f.bind(receiver);`, 180 ]; 181 return lines.join("\n"); 182 }, 183 }; 184 185 var f_cfg_proxy = { 186 func_name: 'p', 187 source_template: function(cfg) { 188 var receiver = cfg.f_receiver != undefined ? cfg.f_receiver 189 : "global"; 190 var do_checks = [ 191 ` assertEquals_(${receiver}, this);`, 192 ` ${!cfg.check_new_target ? "// " : ""}assertEquals_(undefined, new.target);`, 193 check_arguments, 194 ` checkStackTrace_([f, test]);`, 195 ].join("\n"); 196 197 var lines = [ 198 `function f(a) {`, 199 ` ${inlinable_comment(cfg.f_inlinable)}`, 200 ` counter++;`, 201 ` var expected_args = [${cfg.f_args}];`, 202 do_checks, 203 deopt_template(cfg.deopt_mode), 204 do_checks, 205 ` return 42;`, 206 `}`, 207 `var p = new Proxy(f, {});`, 208 ]; 209 return lines.join("\n"); 210 }, 211 }; 212 213 var g_cfg_normal = { 214 receiver: undefined, 215 source_template: function(cfg) { 216 var lines = [ 217 `function g(a) {`, 218 ` "use strict";`, 219 ` ${inlinable_comment(cfg.g_inlinable)}`, 220 ` var expected_args = [${cfg.g_args}];`, 221 check_arguments, 222 ` return ${cfg.f_name}(${cfg.f_args});`, 223 `}`, 224 ]; 225 return lines.join("\n"); 226 }, 227 }; 228 229 230 var g_cfg_reflect_apply = { 231 receiver: "the_receiver", 232 source_template: function(cfg) { 233 var lines = [ 234 `function g(a) {`, 235 ` "use strict";`, 236 ` ${inlinable_comment(cfg.g_inlinable)}`, 237 ` var expected_args = [${cfg.g_args}];`, 238 check_arguments, 239 ` return Reflect.apply(${cfg.f_name}, the_receiver, [${cfg.f_args}]);`, 240 `}`, 241 ]; 242 return lines.join("\n"); 243 }, 244 }; 245 246 247 var g_cfg_function_apply = { 248 receiver: "the_receiver", 249 source_template: function(cfg) { 250 var lines = [ 251 `function g(a) {`, 252 ` "use strict";`, 253 ` ${inlinable_comment(cfg.g_inlinable)}`, 254 ` var expected_args = [${cfg.g_args}];`, 255 check_arguments, 256 ` return ${cfg.f_name}.apply(the_receiver, [${cfg.f_args}]);`, 257 `}`, 258 ]; 259 return lines.join("\n"); 260 }, 261 }; 262 263 264 var g_cfg_function_apply_arguments_object = { 265 receiver: "the_receiver", 266 source_template: function(cfg) { 267 cfg.f_args = cfg.g_args; 268 var lines = [ 269 `function g(a) {`, 270 ` "use strict";`, 271 ` ${inlinable_comment(cfg.g_inlinable)}`, 272 ` var expected_args = [${cfg.g_args}];`, 273 check_arguments, 274 ` return ${cfg.f_name}.apply(the_receiver, arguments);`, 275 `}`, 276 ]; 277 return lines.join("\n"); 278 }, 279 }; 280 281 282 var g_cfg_function_call = { 283 receiver: "the_receiver", 284 source_template: function(cfg) { 285 var f_args = "the_receiver"; 286 if (cfg.f_args !== "") f_args += ", "; 287 f_args += cfg.f_args; 288 289 var lines = [ 290 `function g(a) {`, 291 ` "use strict";`, 292 ` ${inlinable_comment(cfg.g_inlinable)}`, 293 ` var expected_args = [${cfg.g_args}];`, 294 check_arguments, 295 ` return ${cfg.f_name}.call(${f_args});`, 296 `}`, 297 ]; 298 return lines.join("\n"); 299 }, 300 }; 301 302 303 function test_template(cfg) { 304 // Note: g_source_template modifies cfg.f_args in some cases. 305 var g_source = cfg.g_source_template(cfg); 306 g_source = ident_source(g_source, 2); 307 308 var f_source = cfg.f_source_template(cfg); 309 f_source = ident_source(f_source, 2); 310 311 var lines = [ 312 `(function() {`, 313 ` // Avoid bailing out because of "Reference to a variable which requires dynamic lookup".`, 314 ` var assertEquals_ = assertEquals;`, 315 ` var checkStackTrace_ = checkStackTrace;`, 316 ` var undefined = void 0;`, 317 ` var global = Function('return this')();`, 318 ` var the_receiver = {receiver: 1};`, 319 ` var counter = 0;`, 320 ``, 321 ` // Don't inline helper functions`, 322 ` %NeverOptimizeFunction(assertEquals);`, 323 ` %NeverOptimizeFunction(checkStackTrace);`, 324 ``, 325 f_source, 326 g_source, 327 ` function test() {`, 328 ` "use strict";`, 329 ` assertEquals_(42, g(${cfg.g_args}));`, 330 ` }`, 331 ` ${"test();".repeat(cfg.test_warmup_count)}`, 332 ` ${cfg.f_inlinable ? "%SetForceInlineFlag(f)" : "%OptimizeFunctionOnNextCall(f)"};`, 333 ` ${cfg.g_inlinable ? "%SetForceInlineFlag(g)" : "%OptimizeFunctionOnNextCall(g)"};`, 334 ` %OptimizeFunctionOnNextCall(test);`, 335 ` test();`, 336 ` assertEquals(${1 + cfg.test_warmup_count}, counter);`, 337 `})();`, 338 ``, 339 ]; 340 var source = lines.join("\n"); 341 return source; 342 } 343 344 var f_args_variants = [/*"", "1",*/ "1, 2"]; 345 var g_args_variants = [/*"", "10",*/ "10, 20"]; 346 var f_inlinable_variants = [true, false]; 347 var g_inlinable_variants = [true, false]; 348 // This is to avoid bailing out because of referencing new.target. 349 var check_new_target_variants = [/*true,*/ false]; 350 var deopt_mode_variants = ["none", "f", "g", "test"]; 351 var f_variants = [ 352 f_cfg_sloppy, 353 f_cfg_strict, 354 f_cfg_bound, 355 f_cfg_proxy, 356// f_cfg_possibly_eval, 357 ]; 358 var g_variants = [ 359 g_cfg_normal, 360// g_cfg_reflect_apply, 361 g_cfg_function_apply, 362// g_cfg_function_apply_arguments_object, 363 g_cfg_function_call, 364 ]; 365 var test_warmup_counts = [0, 1, 2]; 366 367 var iter = 0; 368 var tests_executed = 0; 369 if (verbose && shard !== undefined) { 370 print("Running shard #" + shard); 371 } 372 f_variants.forEach((f_cfg) => { 373 check_new_target_variants.forEach((check_new_target) => { 374 deopt_mode_variants.forEach((deopt_mode) => { 375 g_variants.forEach((g_cfg) => { 376 f_args_variants.forEach((f_args) => { 377 g_args_variants.forEach((g_args) => { 378 f_inlinable_variants.forEach((f_inlinable) => { 379 g_inlinable_variants.forEach((g_inlinable) => { 380 test_warmup_counts.forEach((test_warmup_count) => { 381 if (shard !== undefined && (iter++) % SHARDS_COUNT != shard) { 382 if (verbose) { 383 print("skipping..."); 384 } 385 return; 386 } 387 tests_executed++; 388 var cfg = { 389 f_source_template: f_cfg.source_template, 390 f_inlinable, 391 f_args, 392 f_name: f_cfg.func_name, 393 f_receiver: g_cfg.receiver, 394 g_source_template: g_cfg.source_template, 395 g_inlinable, 396 g_args, 397 test_warmup_count, 398 check_new_target, 399 deopt_mode, 400 }; 401 var source = test_template(cfg); 402 if (verbose) { 403 // print("===================="); 404 // print(source); 405 } 406 eval(source); 407 }); 408 }); 409 }); 410 }); 411 }); 412 }); 413 }); 414 }); 415 }); 416 if (verbose) { 417 print("Number of tests executed: " + tests_executed); 418 } 419} 420 421// Uncomment to run all the tests at once or use shard runners. 422//run_tests(); 423