bfd65a54fa
This CL covers simple ("simple") rest param cases. BUG=v8:5516 R=vogelheim@chromium.org Change-Id: I254c2eb81d759eb2ea2a3d5e7c46bcdc2ccef707 Reviewed-on: https://chromium-review.googlesource.com/440984 Reviewed-by: Daniel Vogelheim <vogelheim@chromium.org> Commit-Queue: Marja Hölttä <marja@chromium.org> Cr-Commit-Position: refs/heads/master@{#43106}
386 lines
15 KiB
C++
386 lines
15 KiB
C++
// 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.
|
|
|
|
#include "src/ast/ast.h"
|
|
#include "src/compiler.h"
|
|
#include "src/objects-inl.h"
|
|
#include "src/parsing/parse-info.h"
|
|
#include "src/parsing/parsing.h"
|
|
|
|
#include "test/cctest/cctest.h"
|
|
#include "test/cctest/scope-test-helper.h"
|
|
#include "test/cctest/unicode-helpers.h"
|
|
|
|
TEST(PreParserScopeAnalysis) {
|
|
i::FLAG_lazy_inner_functions = true;
|
|
i::FLAG_preparser_scope_analysis = true;
|
|
i::Isolate* isolate = CcTest::i_isolate();
|
|
i::Factory* factory = isolate->factory();
|
|
i::HandleScope scope(isolate);
|
|
LocalContext env;
|
|
|
|
// FIXME(marja): Add tests where the test cases are deeper (in a second-level
|
|
// inner function or second-level inner arrow function). Remove redundant test
|
|
// cases.
|
|
const char* prefix = "(function outer() { ";
|
|
const char* suffix = " })();";
|
|
int prefix_len = Utf8LengthHelper(prefix);
|
|
int suffix_len = Utf8LengthHelper(suffix);
|
|
|
|
// The scope start positions must match; note the extra space in lazy_inner.
|
|
const char* lazy_inner = " function inner(%s) { %s }";
|
|
const char* eager_inner = "(function inner(%s) { %s })()";
|
|
|
|
struct Inner {
|
|
Inner(const char* s) : source(s) {} // NOLINT
|
|
Inner(const char* s, bool precise)
|
|
: source(s), precise_maybe_assigned(precise) {}
|
|
|
|
Inner(const char* p, const char* s) : params(p), source(s) {}
|
|
Inner(const char* p, const char* s, bool precise)
|
|
: params(p), source(s), precise_maybe_assigned(precise) {}
|
|
|
|
const char* params = "";
|
|
const char* source;
|
|
bool precise_maybe_assigned = true;
|
|
} inners[] = {
|
|
// Simple cases
|
|
{"var1;"},
|
|
{"var1 = 5;"},
|
|
{"if (true) {}"},
|
|
{"function f1() {}"},
|
|
|
|
// Var declarations and assignments.
|
|
{"var var1;"},
|
|
{"var var1; var1 = 5;"},
|
|
{"if (true) { var var1; }", false},
|
|
{"if (true) { var var1; var1 = 5; }"},
|
|
{"var var1; function f() { var1; }"},
|
|
{"var var1; var1 = 5; function f() { var1; }"},
|
|
{"var var1; function f() { var1 = 5; }"},
|
|
|
|
// Let declarations and assignments.
|
|
{"let var1;"},
|
|
{"let var1; var1 = 5;"},
|
|
{"if (true) { let var1; }"},
|
|
{"if (true) { let var1; var1 = 5; }"},
|
|
{"let var1; function f() { var1; }"},
|
|
{"let var1; var1 = 5; function f() { var1; }"},
|
|
{"let var1; function f() { var1 = 5; }"},
|
|
|
|
// Const declarations.
|
|
{"const var1 = 5;"},
|
|
{"if (true) { const var1 = 5; }"},
|
|
{"const var1 = 5; function f() { var1; }"},
|
|
|
|
// Redeclarations.
|
|
{"var var1; var var1;"},
|
|
{"var var1; var var1; var1 = 5;"},
|
|
{"var var1; if (true) { var var1; }"},
|
|
{"if (true) { var var1; var var1; }"},
|
|
{"var var1; if (true) { var var1; var1 = 5; }"},
|
|
{"if (true) { var var1; var var1; var1 = 5; }"},
|
|
{"var var1; var var1; function f() { var1; }"},
|
|
{"var var1; var var1; function f() { var1 = 5; }"},
|
|
|
|
// Shadowing declarations.
|
|
{"var var1; if (true) { var var1; }"},
|
|
{"var var1; if (true) { let var1; }"},
|
|
{"let var1; if (true) { let var1; }"},
|
|
|
|
{"var var1; if (true) { const var1 = 0; }"},
|
|
{"const var1 = 0; if (true) { const var1 = 0; }"},
|
|
|
|
// Arguments and this.
|
|
{"arguments;"},
|
|
{"arguments = 5;"},
|
|
{"if (true) { arguments; }"},
|
|
{"if (true) { arguments = 5; }"},
|
|
{"function f() { arguments; }"},
|
|
{"function f() { arguments = 5; }"},
|
|
|
|
{"this;"},
|
|
{"if (true) { this; }"},
|
|
{"function f() { this; }"},
|
|
|
|
// Variable called "arguments"
|
|
{"var arguments;"},
|
|
{"var arguments; arguments = 5;"},
|
|
{"if (true) { var arguments; }", false},
|
|
{"if (true) { var arguments; arguments = 5; }"},
|
|
{"var arguments; function f() { arguments; }"},
|
|
{"var arguments; arguments = 5; function f() { arguments; }"},
|
|
{"var arguments; function f() { arguments = 5; }"},
|
|
|
|
{"let arguments;"},
|
|
{"let arguments; arguments = 5;"},
|
|
{"if (true) { let arguments; }"},
|
|
{"if (true) { let arguments; arguments = 5; }"},
|
|
{"let arguments; function f() { arguments; }"},
|
|
{"let arguments; arguments = 5; function f() { arguments; }"},
|
|
{"let arguments; function f() { arguments = 5; }"},
|
|
|
|
{"const arguments = 5;"},
|
|
{"if (true) { const arguments = 5; }"},
|
|
{"const arguments = 5; function f() { arguments; }"},
|
|
|
|
// Destructuring declarations.
|
|
{"var [var1, var2] = [1, 2];"},
|
|
{"var [var1, var2, [var3, var4]] = [1, 2, [3, 4]];"},
|
|
{"var [{var1: var2}, {var3: var4}] = [{var1: 1}, {var3: 2}];"},
|
|
{"var [var1, ...var2] = [1, 2, 3];"},
|
|
|
|
{"var {var1: var2, var3: var4} = {var1: 1, var3: 2};"},
|
|
{"var {var1: var2, var3: {var4: var5}} = {var1: 1, var3: {var4: 2}};"},
|
|
{"var {var1: var2, var3: [var4, var5]} = {var1: 1, var3: [2, 3]};"},
|
|
|
|
{"let [var1, var2] = [1, 2];"},
|
|
{"let [var1, var2, [var3, var4]] = [1, 2, [3, 4]];"},
|
|
{"let [{var1: var2}, {var3: var4}] = [{var1: 1}, {var3: 2}];"},
|
|
{"let [var1, ...var2] = [1, 2, 3];"},
|
|
|
|
{"let {var1: var2, var3: var4} = {var1: 1, var3: 2};"},
|
|
{"let {var1: var2, var3: {var4: var5}} = {var1: 1, var3: {var4: 2}};"},
|
|
{"let {var1: var2, var3: [var4, var5]} = {var1: 1, var3: [2, 3]};"},
|
|
|
|
{"const [var1, var2] = [1, 2];"},
|
|
{"const [var1, var2, [var3, var4]] = [1, 2, [3, 4]];"},
|
|
{"const [{var1: var2}, {var3: var4}] = [{var1: 1}, {var3: 2}];"},
|
|
{"const [var1, ...var2] = [1, 2, 3];"},
|
|
|
|
{"const {var1: var2, var3: var4} = {var1: 1, var3: 2};"},
|
|
{"const {var1: var2, var3: {var4: var5}} = {var1: 1, var3: {var4: 2}};"},
|
|
{"const {var1: var2, var3: [var4, var5]} = {var1: 1, var3: [2, 3]};"},
|
|
|
|
// Referencing the function variable.
|
|
{"inner;"},
|
|
{"function f1() { f1; }"},
|
|
{"function f1() { inner; }"},
|
|
{"function f1() { function f2() { f1; } }"},
|
|
{"function arguments() {}"},
|
|
{"function f1() {} function f1() {}"},
|
|
{"var f1; function f1() {}"},
|
|
|
|
// Assigning to the function variable.
|
|
{"inner = 3;"},
|
|
{"function f1() { f1 = 3; }"},
|
|
{"function f1() { f1; } f1 = 3;"},
|
|
{"function arguments() {} arguments = 8"},
|
|
{"function f1() {} f1 = 3; function f1() {}"},
|
|
|
|
// Evals.
|
|
{"var var1; eval('');"},
|
|
{"var var1; function f1() { eval(''); }"},
|
|
{"let var1; eval('');"},
|
|
{"let var1; function f1() { eval(''); }"},
|
|
{"const var1 = 10; eval('');"},
|
|
{"const var1 = 10; function f1() { eval(''); }"},
|
|
|
|
// Standard for loops.
|
|
{"for (var var1 = 0; var1 < 10; ++var1) { }"},
|
|
{"for (let var1 = 0; var1 < 10; ++var1) { }"},
|
|
{"for (const var1 = 0; var1 < 10; ++var1) { }"},
|
|
|
|
{"for (var var1 = 0; var1 < 10; ++var1) { function foo() { var1; } }"},
|
|
{"for (let var1 = 0; var1 < 10; ++var1) { function foo() { var1; } }"},
|
|
{"for (const var1 = 0; var1 < 10; ++var1) { function foo() { var1; } }"},
|
|
{"'use strict'; for (var var1 = 0; var1 < 10; ++var1) { function foo() { "
|
|
"var1; } }"},
|
|
{"'use strict'; for (let var1 = 0; var1 < 10; ++var1) { function foo() { "
|
|
"var1; } }"},
|
|
{"'use strict'; for (const var1 = 0; var1 < 10; ++var1) { function foo() "
|
|
"{ var1; } }"},
|
|
|
|
// For of loops
|
|
{"for (var1 of [1, 2]) { }"},
|
|
{"for (var var1 of [1, 2]) { }"},
|
|
{"for (let var1 of [1, 2]) { }"},
|
|
{"for (const var1 of [1, 2]) { }"},
|
|
|
|
{"for (var1 of [1, 2]) { var1; }"},
|
|
{"for (var var1 of [1, 2]) { var1; }"},
|
|
{"for (let var1 of [1, 2]) { var1; }"},
|
|
{"for (const var1 of [1, 2]) { var1; }"},
|
|
|
|
{"for (var1 of [1, 2]) { var1 = 0; }"},
|
|
{"for (var var1 of [1, 2]) { var1 = 0; }"},
|
|
{"for (let var1 of [1, 2]) { var1 = 0; }"},
|
|
{"for (const var1 of [1, 2]) { var1 = 0; }"},
|
|
|
|
{"for (var1 of [1, 2]) { function foo() { var1; } }"},
|
|
{"for (var var1 of [1, 2]) { function foo() { var1; } }"},
|
|
{"for (let var1 of [1, 2]) { function foo() { var1; } }"},
|
|
{"for (const var1 of [1, 2]) { function foo() { var1; } }"},
|
|
|
|
{"for (var1 of [1, 2]) { function foo() { var1 = 0; } }"},
|
|
{"for (var var1 of [1, 2]) { function foo() { var1 = 0; } }"},
|
|
{"for (let var1 of [1, 2]) { function foo() { var1 = 0; } }"},
|
|
{"for (const var1 of [1, 2]) { function foo() { var1 = 0; } }"},
|
|
|
|
// For in loops
|
|
{"for (var1 in {a: 6}) { }"},
|
|
{"for (var var1 in {a: 6}) { }"},
|
|
{"for (let var1 in {a: 6}) { }"},
|
|
{"for (const var1 in {a: 6}) { }"},
|
|
|
|
{"for (var1 in {a: 6}) { var1; }"},
|
|
{"for (var var1 in {a: 6}) { var1; }"},
|
|
{"for (let var1 in {a: 6}) { var1; }"},
|
|
{"for (const var1 in {a: 6}) { var1; }"},
|
|
|
|
{"for (var1 in {a: 6}) { var1 = 0; }"},
|
|
{"for (var var1 in {a: 6}) { var1 = 0; }"},
|
|
{"for (let var1 in {a: 6}) { var1 = 0; }"},
|
|
{"for (const var1 in {a: 6}) { var1 = 0; }"},
|
|
|
|
{"for (var1 in {a: 6}) { function foo() { var1; } }"},
|
|
{"for (var var1 in {a: 6}) { function foo() { var1; } }"},
|
|
{"for (let var1 in {a: 6}) { function foo() { var1; } }"},
|
|
{"for (const var1 in {a: 6}) { function foo() { var1; } }"},
|
|
|
|
{"for (var1 in {a: 6}) { function foo() { var1 = 0; } }"},
|
|
{"for (var var1 in {a: 6}) { function foo() { var1 = 0; } }"},
|
|
{"for (let var1 in {a: 6}) { function foo() { var1 = 0; } }"},
|
|
{"for (const var1 in {a: 6}) { function foo() { var1 = 0; } }"},
|
|
|
|
{"for (var1 in {a: 6}) { function foo() { var1 = 0; } }"},
|
|
{"for (var var1 in {a: 6}) { function foo() { var1 = 0; } }"},
|
|
{"for (let var1 in {a: 6}) { function foo() { var1 = 0; } }"},
|
|
{"for (const var1 in {a: 6}) { function foo() { var1 = 0; } }"},
|
|
|
|
// Loops without declarations
|
|
{"var var1 = 0; for ( ; var1 < 2; ++var1) { }"},
|
|
{"var var1 = 0; for ( ; var1 < 2; ++var1) { function foo() { var1; } }"},
|
|
{"var var1 = 0; for ( ; var1 > 2; ) { }"},
|
|
{"var var1 = 0; for ( ; var1 > 2; ) { function foo() { var1; } }"},
|
|
{"var var1 = 0; for ( ; var1 > 2; ) { function foo() { var1 = 6; } }"},
|
|
|
|
{"var var1 = 0; for(var1; var1 < 2; ++var1) { }"},
|
|
{"var var1 = 0; for (var1; var1 < 2; ++var1) { function foo() { var1; } "
|
|
"}"},
|
|
{"var var1 = 0; for (var1; var1 > 2; ) { }"},
|
|
{"var var1 = 0; for (var1; var1 > 2; ) { function foo() { var1; } }"},
|
|
{"var var1 = 0; for (var1; var1 > 2; ) { function foo() { var1 = 6; } }"},
|
|
|
|
// Sloppy block functions.
|
|
{"if (true) { function f1() {} }"},
|
|
{"if (true) { function f1() {} function f1() {} }"},
|
|
{"if (true) { if (true) { function f1() {} } }"},
|
|
{"if (true) { if (true) { function f1() {} function f1() {} } }"},
|
|
{"if (true) { function f1() {} f1 = 3; }"},
|
|
|
|
{"if (true) { function f1() {} function foo() { f1; } }"},
|
|
{"if (true) { function f1() {} } function foo() { f1; }"},
|
|
{"if (true) { function f1() {} function f1() {} function foo() { f1; } "
|
|
"}"},
|
|
{"if (true) { function f1() {} function f1() {} } function foo() { f1; "
|
|
"}"},
|
|
{"if (true) { if (true) { function f1() {} } function foo() { f1; } }"},
|
|
{"if (true) { if (true) { function f1() {} function f1() {} } function "
|
|
"foo() { f1; } }"},
|
|
{"if (true) { function f1() {} f1 = 3; function foo() { f1; } }"},
|
|
{"if (true) { function f1() {} f1 = 3; } function foo() { f1; }"},
|
|
|
|
{"function inner2() { if (true) { function f1() {} } }"},
|
|
{"function inner2() { if (true) { function f1() {} f1 = 3; } }"},
|
|
|
|
{"var f1 = 1; if (true) { function f1() {} }"},
|
|
{"var f1 = 1; if (true) { function f1() {} } function foo() { f1; }"},
|
|
|
|
// Simple parameters.
|
|
{"var1", ""},
|
|
{"var1", "var1;"},
|
|
{"var1", "var1 = 9;"},
|
|
{"var1", "function f1() { var1; }"},
|
|
{"var1", "function f1() { var1 = 9; }"},
|
|
|
|
// Duplicate parameters.
|
|
{"var1, var1", ""},
|
|
{"var1, var1", "var1;"},
|
|
{"var1, var1", "var1 = 9;"},
|
|
{"var1, var1", "function f1() { var1; }"},
|
|
{"var1, var1", "function f1() { var1 = 9; }"},
|
|
|
|
// Rest parameter.
|
|
{"...var2", ""},
|
|
{"...var2", "var2;"},
|
|
{"...var2", "var2 = 9;"},
|
|
{"...var2", "function f1() { var2; }"},
|
|
{"...var2", "function f1() { var2 = 9; }"},
|
|
|
|
// FIXME(marja): destructuring parameters, default parameters, shadowing
|
|
// parameters, default parameters referring to other parameters, arguments
|
|
// parameter, eval in default parameter, params and locals, multiple
|
|
// params, many params and rest, destructuring rest, rest with default
|
|
// value, locals shadowing params.
|
|
};
|
|
|
|
for (unsigned i = 0; i < arraysize(inners); ++i) {
|
|
// First compile with the lazy inner function and extract the scope data.
|
|
const char* inner_function = lazy_inner;
|
|
int inner_function_len = Utf8LengthHelper(inner_function) - 4;
|
|
|
|
int params_len = Utf8LengthHelper(inners[i].params);
|
|
int source_len = Utf8LengthHelper(inners[i].source);
|
|
int len =
|
|
prefix_len + inner_function_len + params_len + source_len + suffix_len;
|
|
|
|
i::ScopedVector<char> lazy_program(len + 1);
|
|
i::SNPrintF(lazy_program, "%s", prefix);
|
|
i::SNPrintF(lazy_program + prefix_len, inner_function, inners[i].params,
|
|
inners[i].source);
|
|
i::SNPrintF(lazy_program + prefix_len + inner_function_len + params_len +
|
|
source_len,
|
|
"%s", suffix);
|
|
|
|
i::Handle<i::String> source =
|
|
factory->InternalizeUtf8String(lazy_program.start());
|
|
source->PrintOn(stdout);
|
|
printf("\n");
|
|
|
|
i::Handle<i::Script> script = factory->NewScript(source);
|
|
i::ParseInfo lazy_info(script);
|
|
|
|
// No need to run scope analysis; preparser scope data is produced when
|
|
// parsing.
|
|
CHECK(i::parsing::ParseProgram(&lazy_info));
|
|
|
|
// Then parse eagerly and check against the scope data.
|
|
inner_function = eager_inner;
|
|
inner_function_len = Utf8LengthHelper(inner_function) - 4;
|
|
len =
|
|
prefix_len + inner_function_len + params_len + source_len + suffix_len;
|
|
|
|
i::ScopedVector<char> eager_program(len + 1);
|
|
i::SNPrintF(eager_program, "%s", prefix);
|
|
i::SNPrintF(eager_program + prefix_len, inner_function, inners[i].params,
|
|
inners[i].source);
|
|
i::SNPrintF(eager_program + prefix_len + inner_function_len + params_len +
|
|
source_len,
|
|
"%s", suffix);
|
|
|
|
source = factory->InternalizeUtf8String(eager_program.start());
|
|
source->PrintOn(stdout);
|
|
printf("\n");
|
|
|
|
script = factory->NewScript(source);
|
|
i::ParseInfo eager_info(script);
|
|
eager_info.set_allow_lazy_parsing(false);
|
|
|
|
CHECK(i::parsing::ParseProgram(&eager_info));
|
|
CHECK(i::Compiler::Analyze(&eager_info));
|
|
|
|
i::Scope* scope =
|
|
eager_info.literal()->scope()->inner_scope()->inner_scope();
|
|
DCHECK_NOT_NULL(scope);
|
|
DCHECK_NULL(scope->sibling());
|
|
DCHECK(scope->is_function_scope());
|
|
|
|
size_t index = 0;
|
|
i::ScopeTestHelper::CompareScopeToData(
|
|
scope, lazy_info.preparsed_scope_data(), index,
|
|
inners[i].precise_maybe_assigned);
|
|
}
|
|
}
|