v8/test/mjsunit/skipping-inner-functions.js
Marja Hölttä e7a46253f2 [parser] Skipping inner funcs: implement a bailout.
In some cases, PreParser cannot replicate the Scope structure created by
Parser. It happens esp. with arrow function parameters, since the relevant
information is already lost by the time we figure out it's an arrow function.

In these cases, PreParser should bail out of trying to create data for skipping
inner functions.

Implementation notes:

- The arrow function case is more fundamental; the non-arrow case could be
  hacked together somehow if we implemented tracking is_simple for each param
  separately; but now that it's possible to bail out consistently from both
  cases, I don't think the is_simple complication is worth it.

- The added mjsunit test cases are based on the test262 test cases which exposed
  the problem.

- cctest/preparser/PreParserScopeAnalysis was exercising similar cases, but the
  problem didn't show up because the function parameters didn't contain
  skippable functions. Those test cases have been repurposed for testing the
  bailout.

- Extra precaution: the bailout tests are in a separate file, to guard from the
  bug that a bailout case results in bailing out of *all* data creation, which
  would make all skipping tests in the same file useless.

BUG=v8:5516

Change-Id: I4324749a5ec602fa5d7dc27647ade0284a6842fe
Reviewed-on: https://chromium-review.googlesource.com/599849
Reviewed-by: Adam Klein <adamk@chromium.org>
Commit-Queue: Marja Hölttä <marja@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47170}
2017-08-04 18:22:32 +00:00

251 lines
6.1 KiB
JavaScript

// Copyright 2017 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: --experimental-preparser-scope-analysis
(function TestBasicSkipping() {
var result = 0;
function lazy(ctxt_alloc_param) {
var ctxt_alloc_var = 10;
function skip_me() {
result = ctxt_alloc_param + ctxt_alloc_var;
}
return skip_me;
}
// Test that parameters and variables of the outer function get context
// allocated even if we skip the inner function.
lazy(9)();
assertEquals(19, result);
})();
(function TestSkippingFunctionWithEval() {
var result = 0;
function lazy(ctxt_alloc_param) {
var ctxt_alloc_var = 10;
function skip_me() {
eval('result = ctxt_alloc_param + ctxt_alloc_var');
}
return skip_me;
}
// Test that parameters and variables of the outer function get context
// allocated even if we skip the inner function.
lazy(9)();
assertEquals(19, result);
})();
(function TestCtxtAllocatingNonSimpleParams1() {
var result = 0;
function lazy([other_param1, ctxt_alloc_param, other_param2]) {
function skip_me() {
result = ctxt_alloc_param;
}
return skip_me;
}
// Test that parameters and variables of the outer function get context
// allocated even if we skip the inner function.
lazy([30, 29, 28])();
assertEquals(29, result);
})();
(function TestCtxtAllocatingNonSimpleParams2() {
var result = 0;
function lazy({a: other_param1, b: ctxt_alloc_param, c: other_param2}) {
function skip_me() {
result = ctxt_alloc_param;
}
return skip_me;
}
// Test that parameters and variables of the outer function get context
// allocated even if we skip the inner function.
lazy({a: 31, b: 32, c: 33})();
assertEquals(32, result);
})();
(function TestCtxtAllocatingNonSimpleParams3() {
var result = 0;
function lazy(...ctxt_alloc_param) {
function skip_me() {
result = ctxt_alloc_param;
}
return skip_me;
}
// Test that parameters and variables of the outer function get context
// allocated even if we skip the inner function.
lazy(34, 35)();
assertEquals([34, 35], result);
})();
// Skippable top level functions.
let result = 0;
function lazy_top_level(ctxt_alloc_param) {
let ctxt_alloc_var = 24;
function skip_me() {
result = ctxt_alloc_param + ctxt_alloc_var;
}
skip_me();
}
lazy_top_level(10);
assertEquals(34, result);
// Tests for using a function name in an inner function.
var TestUsingNamedExpressionName1 = function this_is_the_name() {
function inner() {
this_is_the_name;
}
inner();
}
TestUsingNamedExpressionName1();
function TestUsingNamedExpressionName2() {
let f = function this_is_the_name() {
function inner() {
this_is_the_name;
}
inner();
}
f();
}
TestUsingNamedExpressionName2();
function TestSkippedFunctionInsideLoopInitializer() {
let saved_func;
for (let i = 0, f = function() { return i }; i < 1; ++i) {
saved_func = f;
}
assertEquals(0, saved_func());
}
TestSkippedFunctionInsideLoopInitializer();
(function TestSkippedFunctionWithParameters() {
var result = 0;
function lazy(ctxt_alloc_param) {
var ctxt_alloc_var = 10;
function skip_me(param1, param2) {
result = ctxt_alloc_param + ctxt_alloc_var + param1 + param2;
}
return skip_me;
}
lazy(9)(8, 7);
assertEquals(34, result);
})();
function TestSkippingDeeperLazyFunctions() {
let result = 0;
function inner_lazy(ctxt_alloc_param) {
let ctxt_alloc_var = 13;
function skip_me() {
result = ctxt_alloc_param + ctxt_alloc_var;
}
return skip_me;
}
let f = inner_lazy(12);
f();
assertEquals(25, result);
}
TestSkippingDeeperLazyFunctions();
function TestEagerFunctionsBetweenLazyFunctions() {
let result = 0;
// We produce one data set for TestEagerFunctionsBetweenLazyFunctions and
// another one for inner. The variable data for eager belongs to the former
// data set.
let ctxt_allocated1 = 3;
(function eager() {
let ctxt_allocated2 = 4;
function inner() {
result = ctxt_allocated1 + ctxt_allocated2;
}
return inner;
})()();
assertEquals(7, result);
}
TestEagerFunctionsBetweenLazyFunctions();
function TestEagerNotIifeFunctionsBetweenLazyFunctions() {
let result = 0;
// We produce one data set for TestEagerFunctionsBetweenLazyFunctions and
// another one for inner. The variable data for eager belongs to the former
// data set.
let ctxt_allocated1 = 3;
(function eager_not_iife() {
let ctxt_allocated2 = 4;
function inner() {
result = ctxt_allocated1 + ctxt_allocated2;
}
return inner;
}); // Function not called; not an iife.
// This is just a regression test. We cannot test that the context allocation
// was done correctly (since there's no way to call eager_not_iife), but code
// like this used to trigger some DCHECKs.
}
TestEagerNotIifeFunctionsBetweenLazyFunctions();
// Regression test for functions inside a lazy arrow function. (Only top-level
// arrow functions are lazy, so this cannot be wrapped in a function.)
result = 0;
let f1 = (ctxt_alloc_param) => {
let ctxt_alloc_var = 10;
function inner() {
result = ctxt_alloc_param + ctxt_alloc_var;
}
return inner;
}
f1(9)();
assertEquals(19, result);
function TestStrictEvalInParams() {
"use strict";
var result = 0;
function lazy(a = function() { return 2; }, b = eval('3')) {
function skip_me() {
result = a() + b;
}
return skip_me;
}
lazy()();
assertEquals(5, result);
function not_skippable_either() {}
}
TestStrictEvalInParams();
function TestSloppyEvalInFunctionWithComplexParams() {
var result = 0;
function lazy1(ctxt_alloc_param = 2) {
var ctxt_alloc_var = 3;
function skip_me() {
result = ctxt_alloc_param + ctxt_alloc_var;
}
eval('');
return skip_me;
}
lazy1()();
assertEquals(5, result);
function lazy2(ctxt_alloc_param = 4) {
var ctxt_alloc_var = 5;
function skip_me() {
eval('result = ctxt_alloc_param + ctxt_alloc_var;');
}
return skip_me;
}
lazy2()();
assertEquals(9, result);
}
TestSloppyEvalInFunctionWithComplexParams();