• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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