Commit a33fcd66 authored by neis's avatar neis Committed by Commit bot

[parsing] Fix maybe-assigned for loop variables.

Due to hoisting, the value of a 'var'-declared variable may actually change even
if the code contains only the "initial" assignment, namely when that assignment
occurs inside a loop.  For example:

  let i = 10;
  do { var x = i } while (i--):

As a simple and very conservative approximation of this, we explicitly mark
as maybe-assigned any non-lexical variable whose "declaration" does not
syntactically occur in the function scope.  (In the example above, it
occurs in a block scope.)

BUG=v8:5636

Review-Url: https://codereview.chromium.org/2673403003
Cr-Commit-Position: refs/heads/master@{#42989}
parent 9458dc9e
......@@ -181,6 +181,7 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
bool* sloppy_mode_block_scope_function_redefinition,
bool* ok);
// The return value is meaningful only if FLAG_preparser_scope_analysis is on.
Variable* DeclareVariableName(const AstRawString* name, VariableMode mode);
// Declarations list.
......
......@@ -1347,6 +1347,24 @@ class ParserBase {
return expression->IsObjectLiteral() || expression->IsArrayLiteral();
}
// Due to hoisting, the value of a 'var'-declared variable may actually change
// even if the code contains only the "initial" assignment, namely when that
// assignment occurs inside a loop. For example:
//
// let i = 10;
// do { var x = i } while (i--):
//
// As a simple and very conservative approximation of this, we explicitly mark
// as maybe-assigned any non-lexical variable whose initializing "declaration"
// does not syntactically occur in the function scope. (In the example above,
// it occurs in a block scope.)
//
// Note that non-lexical variables include temporaries, which may also get
// assigned inside a loop due to the various rewritings that the parser
// performs.
//
static void MarkLoopVariableAsAssigned(Scope* scope, Variable* var);
// Keep track of eval() calls since they disable all local variable
// optimizations. This checks if expression is an eval call, and if yes,
// forwards the information to scope.
......@@ -5698,8 +5716,12 @@ typename ParserBase<Impl>::StatementT ParserBase<Impl>::ParseStandardForLoop(
return loop;
}
#undef CHECK_OK
#undef CHECK_OK_CUSTOM
template <typename Impl>
void ParserBase<Impl>::MarkLoopVariableAsAssigned(Scope* scope, Variable* var) {
if (!IsLexicalVariableMode(var->mode()) && !scope->is_function_scope()) {
var->set_maybe_assigned();
}
}
template <typename Impl>
void ParserBase<Impl>::ObjectLiteralChecker::CheckDuplicateProto(
......@@ -5751,6 +5773,8 @@ void ParserBase<Impl>::ClassLiteralChecker::CheckClassMethodName(
}
}
#undef CHECK_OK
#undef CHECK_OK_CUSTOM
#undef CHECK_OK_VOID
} // namespace internal
......
......@@ -177,6 +177,8 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
// If there's no initializer, we're done.
if (value == nullptr) return;
MarkLoopVariableAsAssigned(var_init_scope, proxy->var());
// A declaration of the form:
//
// var v = x;
......
......@@ -310,8 +310,14 @@ void PreParser::DeclareAndInitializeVariables(
DCHECK(track_unresolved_variables_);
for (auto variable : *(declaration->pattern.variables_)) {
declaration_descriptor->scope->RemoveUnresolved(variable);
scope()->DeclareVariableName(variable->raw_name(),
declaration_descriptor->mode);
Variable* var = scope()->DeclareVariableName(
variable->raw_name(), declaration_descriptor->mode);
if (FLAG_preparser_scope_analysis) {
MarkLoopVariableAsAssigned(declaration_descriptor->scope, var);
// This is only necessary if there is an initializer, but we don't have
// that information here. Consequently, the preparser sometimes says
// maybe-assigned where the parser (correctly) says never-assigned.
}
if (names) {
names->Add(variable->raw_name(), zone());
}
......
......@@ -30,268 +30,274 @@ TEST(PreParserScopeAnalysis) {
const char* eager_inner = "(function inner(%s) { %s })()";
struct {
bool precise_maybe_assigned;
const char* params;
const char* source;
} inners[] = {
// Simple cases
{"", "var1;"},
{"", "var1 = 5;"},
{"", "if (true) {}"},
{"", "function f1() {}"},
{1, "", "var1;"},
{1, "", "var1 = 5;"},
{1, "", "if (true) {}"},
{1, "", "function f1() {}"},
// Var declarations and assignments.
{"", "var var1;"},
{"", "var var1; var1 = 5;"},
{"", "if (true) { var var1; }"},
{"", "if (true) { var var1; var1 = 5; }"},
{"", "var var1; function f() { var1; }"},
{"", "var var1; var1 = 5; function f() { var1; }"},
{"", "var var1; function f() { var1 = 5; }"},
{1, "", "var var1;"},
{1, "", "var var1; var1 = 5;"},
{0, "", "if (true) { var var1; }"},
{1, "", "if (true) { var var1; var1 = 5; }"},
{1, "", "var var1; function f() { var1; }"},
{1, "", "var var1; var1 = 5; function f() { var1; }"},
{1, "", "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; }"},
{1, "", "let var1;"},
{1, "", "let var1; var1 = 5;"},
{1, "", "if (true) { let var1; }"},
{1, "", "if (true) { let var1; var1 = 5; }"},
{1, "", "let var1; function f() { var1; }"},
{1, "", "let var1; var1 = 5; function f() { var1; }"},
{1, "", "let var1; function f() { var1 = 5; }"},
// Const declarations.
{"", "const var1 = 5;"},
{"", "if (true) { const var1 = 5; }"},
{"", "const var1 = 5; function f() { var1; }"},
{1, "", "const var1 = 5;"},
{1, "", "if (true) { const var1 = 5; }"},
{1, "", "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; }"},
{1, "", "var var1; var var1;"},
{1, "", "var var1; var var1; var1 = 5;"},
{1, "", "var var1; if (true) { var var1; }"},
{1, "", "if (true) { var var1; var var1; }"},
{1, "", "var var1; if (true) { var var1; var1 = 5; }"},
{1, "", "if (true) { var var1; var var1; var1 = 5; }"},
{1, "", "var var1; var var1; function f() { var1; }"},
{1, "", "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; }"},
{1, "", "var var1; if (true) { var var1; }"},
{1, "", "var var1; if (true) { let var1; }"},
{1, "", "let var1; if (true) { let var1; }"},
{"", "var var1; if (true) { const var1 = 0; }"},
{"", "const var1 = 0; if (true) { const var1 = 0; }"},
{1, "", "var var1; if (true) { const var1 = 0; }"},
{1, "", "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; }"},
{1, "", "arguments;"},
{1, "", "arguments = 5;"},
{1, "", "if (true) { arguments; }"},
{1, "", "if (true) { arguments = 5; }"},
{1, "", "function f() { arguments; }"},
{1, "", "function f() { arguments = 5; }"},
{"", "this;"},
{"", "if (true) { this; }"},
{"", "function f() { this; }"},
{1, "", "this;"},
{1, "", "if (true) { this; }"},
{1, "", "function f() { this; }"},
// Variable called "arguments"
{"", "var arguments;"},
{"", "var arguments; arguments = 5;"},
{"", "if (true) { var arguments; }"},
{"", "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; }"},
{1, "", "var arguments;"},
{1, "", "var arguments; arguments = 5;"},
{0, "", "if (true) { var arguments; }"},
{1, "", "if (true) { var arguments; arguments = 5; }"},
{1, "", "var arguments; function f() { arguments; }"},
{1, "", "var arguments; arguments = 5; function f() { arguments; }"},
{1, "", "var arguments; function f() { arguments = 5; }"},
{1, "", "let arguments;"},
{1, "", "let arguments; arguments = 5;"},
{1, "", "if (true) { let arguments; }"},
{1, "", "if (true) { let arguments; arguments = 5; }"},
{1, "", "let arguments; function f() { arguments; }"},
{1, "", "let arguments; arguments = 5; function f() { arguments; }"},
{1, "", "let arguments; function f() { arguments = 5; }"},
{1, "", "const arguments = 5;"},
{1, "", "if (true) { const arguments = 5; }"},
{1, "", "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];"},
{1, "", "var [var1, var2] = [1, 2];"},
{1, "", "var [var1, var2, [var3, var4]] = [1, 2, [3, 4]];"},
{1, "", "var [{var1: var2}, {var3: var4}] = [{var1: 1}, {var3: 2}];"},
{1, "", "var [var1, ...var2] = [1, 2, 3];"},
{"", "var {var1: var2, var3: var4} = {var1: 1, var3: 2};"},
{"",
{1, "", "var {var1: var2, var3: var4} = {var1: 1, var3: 2};"},
{1, "",
"var {var1: var2, var3: {var4: var5}} = {var1: 1, var3: {var4: 2}};"},
{"", "var {var1: var2, var3: [var4, var5]} = {var1: 1, var3: [2, 3]};"},
{1, "",
"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];"},
{1, "", "let [var1, var2] = [1, 2];"},
{1, "", "let [var1, var2, [var3, var4]] = [1, 2, [3, 4]];"},
{1, "", "let [{var1: var2}, {var3: var4}] = [{var1: 1}, {var3: 2}];"},
{1, "", "let [var1, ...var2] = [1, 2, 3];"},
{"", "let {var1: var2, var3: var4} = {var1: 1, var3: 2};"},
{"",
{1, "", "let {var1: var2, var3: var4} = {var1: 1, var3: 2};"},
{1, "",
"let {var1: var2, var3: {var4: var5}} = {var1: 1, var3: {var4: 2}};"},
{"", "let {var1: var2, var3: [var4, var5]} = {var1: 1, var3: [2, 3]};"},
{1, "",
"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];"},
{1, "", "const [var1, var2] = [1, 2];"},
{1, "", "const [var1, var2, [var3, var4]] = [1, 2, [3, 4]];"},
{1, "", "const [{var1: var2}, {var3: var4}] = [{var1: 1}, {var3: 2}];"},
{1, "", "const [var1, ...var2] = [1, 2, 3];"},
{"", "const {var1: var2, var3: var4} = {var1: 1, var3: 2};"},
{"",
{1, "", "const {var1: var2, var3: var4} = {var1: 1, var3: 2};"},
{1, "",
"const {var1: var2, var3: {var4: var5}} = {var1: 1, var3: {var4: 2}};"},
{"", "const {var1: var2, var3: [var4, var5]} = {var1: 1, var3: [2, 3]};"},
{1, "",
"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() {}"},
{1, "", "inner;"},
{1, "", "function f1() { f1; }"},
{1, "", "function f1() { inner; }"},
{1, "", "function f1() { function f2() { f1; } }"},
{1, "", "function arguments() {}"},
{1, "", "function f1() {} function f1() {}"},
{1, "", "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() {}"},
{1, "", "inner = 3;"},
{1, "", "function f1() { f1 = 3; }"},
{1, "", "function f1() { f1; } f1 = 3;"},
{1, "", "function arguments() {} arguments = 8"},
{1, "", "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(''); }"},
{1, "", "var var1; eval('');"},
{1, "", "var var1; function f1() { eval(''); }"},
{1, "", "let var1; eval('');"},
{1, "", "let var1; function f1() { eval(''); }"},
{1, "", "const var1 = 10; eval('');"},
{1, "", "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) { }"},
{1, "", "for (var var1 = 0; var1 < 10; ++var1) { }"},
{1, "", "for (let var1 = 0; var1 < 10; ++var1) { }"},
{1, "", "for (const var1 = 0; var1 < 10; ++var1) { }"},
{"",
{1, "",
"for (var var1 = 0; var1 < 10; ++var1) { function foo() { var1; } }"},
{"",
{1, "",
"for (let var1 = 0; var1 < 10; ++var1) { function foo() { var1; } }"},
{"",
{1, "",
"for (const var1 = 0; var1 < 10; ++var1) { function foo() { var1; } }"},
{"",
{1, "",
"'use strict'; for (var var1 = 0; var1 < 10; ++var1) { function foo() { "
"var1; } }"},
{"",
{1, "",
"'use strict'; for (let var1 = 0; var1 < 10; ++var1) { function foo() { "
"var1; } }"},
{"",
{1, "",
"'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; } }"},
{1, "", "for (var1 of [1, 2]) { }"},
{1, "", "for (var var1 of [1, 2]) { }"},
{1, "", "for (let var1 of [1, 2]) { }"},
{1, "", "for (const var1 of [1, 2]) { }"},
{1, "", "for (var1 of [1, 2]) { var1; }"},
{1, "", "for (var var1 of [1, 2]) { var1; }"},
{1, "", "for (let var1 of [1, 2]) { var1; }"},
{1, "", "for (const var1 of [1, 2]) { var1; }"},
{1, "", "for (var1 of [1, 2]) { var1 = 0; }"},
{1, "", "for (var var1 of [1, 2]) { var1 = 0; }"},
{1, "", "for (let var1 of [1, 2]) { var1 = 0; }"},
{1, "", "for (const var1 of [1, 2]) { var1 = 0; }"},
{1, "", "for (var1 of [1, 2]) { function foo() { var1; } }"},
{1, "", "for (var var1 of [1, 2]) { function foo() { var1; } }"},
{1, "", "for (let var1 of [1, 2]) { function foo() { var1; } }"},
{1, "", "for (const var1 of [1, 2]) { function foo() { var1; } }"},
{1, "", "for (var1 of [1, 2]) { function foo() { var1 = 0; } }"},
{1, "", "for (var var1 of [1, 2]) { function foo() { var1 = 0; } }"},
{1, "", "for (let var1 of [1, 2]) { function foo() { var1 = 0; } }"},
{1, "", "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; } }"},
{1, "", "for (var1 in {a: 6}) { }"},
{1, "", "for (var var1 in {a: 6}) { }"},
{1, "", "for (let var1 in {a: 6}) { }"},
{1, "", "for (const var1 in {a: 6}) { }"},
{1, "", "for (var1 in {a: 6}) { var1; }"},
{1, "", "for (var var1 in {a: 6}) { var1; }"},
{1, "", "for (let var1 in {a: 6}) { var1; }"},
{1, "", "for (const var1 in {a: 6}) { var1; }"},
{1, "", "for (var1 in {a: 6}) { var1 = 0; }"},
{1, "", "for (var var1 in {a: 6}) { var1 = 0; }"},
{1, "", "for (let var1 in {a: 6}) { var1 = 0; }"},
{1, "", "for (const var1 in {a: 6}) { var1 = 0; }"},
{1, "", "for (var1 in {a: 6}) { function foo() { var1; } }"},
{1, "", "for (var var1 in {a: 6}) { function foo() { var1; } }"},
{1, "", "for (let var1 in {a: 6}) { function foo() { var1; } }"},
{1, "", "for (const var1 in {a: 6}) { function foo() { var1; } }"},
{1, "", "for (var1 in {a: 6}) { function foo() { var1 = 0; } }"},
{1, "", "for (var var1 in {a: 6}) { function foo() { var1 = 0; } }"},
{1, "", "for (let var1 in {a: 6}) { function foo() { var1 = 0; } }"},
{1, "", "for (const var1 in {a: 6}) { function foo() { var1 = 0; } }"},
{1, "", "for (var1 in {a: 6}) { function foo() { var1 = 0; } }"},
{1, "", "for (var var1 in {a: 6}) { function foo() { var1 = 0; } }"},
{1, "", "for (let var1 in {a: 6}) { function foo() { var1 = 0; } }"},
{1, "", "for (const var1 in {a: 6}) { function foo() { var1 = 0; } }"},
// Loops without declarations
{"", "var var1 = 0; for ( ; var1 < 2; ++var1) { }"},
{"",
{1, "", "var var1 = 0; for ( ; var1 < 2; ++var1) { }"},
{1, "",
"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; } }"},
{"",
{1, "", "var var1 = 0; for ( ; var1 > 2; ) { }"},
{1, "", "var var1 = 0; for ( ; var1 > 2; ) { function foo() { var1; } }"},
{1, "",
"var var1 = 0; for ( ; var1 > 2; ) { function foo() { var1 = 6; } }"},
{"", "var var1 = 0; for(var1; var1 < 2; ++var1) { }"},
{"",
{1, "", "var var1 = 0; for(var1; var1 < 2; ++var1) { }"},
{1, "",
"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; } }"},
{"",
{1, "", "var var1 = 0; for (var1; var1 > 2; ) { }"},
{1, "",
"var var1 = 0; for (var1; var1 > 2; ) { function foo() { var1; } }"},
{1, "",
"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; }"},
{"",
{1, "", "if (true) { function f1() {} }"},
{1, "", "if (true) { function f1() {} function f1() {} }"},
{1, "", "if (true) { if (true) { function f1() {} } }"},
{1, "", "if (true) { if (true) { function f1() {} function f1() {} } }"},
{1, "", "if (true) { function f1() {} f1 = 3; }"},
{1, "", "if (true) { function f1() {} function foo() { f1; } }"},
{1, "", "if (true) { function f1() {} } function foo() { f1; }"},
{1, "",
"if (true) { function f1() {} function f1() {} function foo() { f1; } "
"}"},
{"",
{1, "",
"if (true) { function f1() {} function f1() {} } function foo() { f1; "
"}"},
{"",
{1, "",
"if (true) { if (true) { function f1() {} } function foo() { f1; } }"},
{"",
{1, "",
"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; }"},
{1, "", "if (true) { function f1() {} f1 = 3; function foo() { f1; } }"},
{1, "", "if (true) { function f1() {} f1 = 3; } function foo() { f1; }"},
{"", "function inner2() { if (true) { function f1() {} } }"},
{"", "function inner2() { if (true) { function f1() {} f1 = 3; } }"},
{1, "", "function inner2() { if (true) { function f1() {} } }"},
{1, "", "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; }"},
{1, "", "var f1 = 1; if (true) { function f1() {} }"},
{1, "",
"var f1 = 1; if (true) { function f1() {} } function foo() { f1; }"},
};
for (unsigned i = 0; i < arraysize(inners); ++i) {
......@@ -358,6 +364,7 @@ TEST(PreParserScopeAnalysis) {
size_t index = 0;
i::ScopeTestHelper::CompareScopeToData(
scope, lazy_info.preparsed_scope_data(), index);
scope, lazy_info.preparsed_scope_data(), index,
inners[i].precise_maybe_assigned);
}
}
......@@ -32,7 +32,7 @@ class ScopeTestHelper {
}
static void CompareScopeToData(Scope* scope, const PreParsedScopeData* data,
size_t& index) {
size_t& index, bool precise_maybe_assigned) {
CHECK_EQ(data->backing_store_[index++], scope->scope_type());
CHECK_EQ(data->backing_store_[index++], scope->start_position());
CHECK_EQ(data->backing_store_[index++], scope->end_position());
......@@ -70,14 +70,19 @@ class ScopeTestHelper {
}
#endif
CHECK_EQ(data->backing_store_[index++], local->location());
if (precise_maybe_assigned) {
CHECK_EQ(data->backing_store_[index++], local->maybe_assigned());
} else {
STATIC_ASSERT(kMaybeAssigned > kNotAssigned);
CHECK_GE(data->backing_store_[index++], local->maybe_assigned());
}
}
}
for (Scope* inner = scope->inner_scope(); inner != nullptr;
inner = inner->sibling()) {
if (!ScopeTreeIsHidden(inner)) {
CompareScopeToData(inner, data, index);
CompareScopeToData(inner, data, index, precise_maybe_assigned);
}
}
}
......
......@@ -3495,6 +3495,365 @@ TEST(MaybeAssignedParameters) {
}
}
struct Input {
bool assigned;
std::string source;
std::vector<unsigned> location; // "Directions" to the relevant scope.
};
static void TestMaybeAssigned(i::Zone* zone, Input input, const char* variable,
bool module, bool allow_lazy_parsing) {
i::Factory* factory = CcTest::i_isolate()->factory();
i::Handle<i::String> string =
factory->InternalizeUtf8String(input.source.c_str());
string->PrintOn(stdout);
printf("\n");
i::Handle<i::Script> script = factory->NewScript(string);
std::unique_ptr<i::ParseInfo> info;
info = std::unique_ptr<i::ParseInfo>(new i::ParseInfo(zone, script));
info->set_module(module);
info->set_allow_lazy_parsing(allow_lazy_parsing);
CHECK(i::parsing::ParseProgram(info.get()));
CHECK(i::Compiler::Analyze(info.get()));
CHECK_NOT_NULL(info->literal());
i::Scope* scope = info->literal()->scope();
CHECK(!scope->AsDeclarationScope()->was_lazily_parsed());
CHECK_NULL(scope->sibling());
CHECK(module ? scope->is_module_scope() : scope->is_script_scope());
i::Variable* var;
{
// Find the variable.
for (auto it = input.location.begin(); it != input.location.end(); ++it) {
unsigned n = *it;
scope = scope->inner_scope();
while (n-- > 0) {
scope = scope->sibling();
}
}
CHECK_NOT_NULL(scope);
const i::AstRawString* var_name =
info->ast_value_factory()->GetOneByteString(variable);
var = scope->Lookup(var_name);
}
CHECK(var->is_used());
STATIC_ASSERT(true == i::kMaybeAssigned);
CHECK_EQ(input.assigned, var->maybe_assigned() == i::kMaybeAssigned);
}
static Input wrap(Input input) {
Input result;
result.assigned = input.assigned;
result.source = "function WRAPPED() { " + input.source + " }";
result.location.push_back(0);
for (auto n : input.location) {
result.location.push_back(n);
}
return result;
}
TEST(MaybeAssignedInsideLoop) {
i::Isolate* isolate = CcTest::i_isolate();
i::HandleScope scope(isolate);
LocalContext env;
i::Zone zone(isolate->allocator(), ZONE_NAME);
std::vector<unsigned> top; // Can't use {} in initializers below.
Input module_and_script_tests[] = {
{1, "for (j=x; j<10; ++j) { foo = j }", top},
{1, "for (j=x; j<10; ++j) { [foo] = [j] }", top},
{1, "for (j=x; j<10; ++j) { var foo = j }", top},
{1, "for (j=x; j<10; ++j) { var [foo] = [j] }", top},
{0, "for (j=x; j<10; ++j) { let foo = j }", {0}},
{0, "for (j=x; j<10; ++j) { let [foo] = [j] }", {0}},
{0, "for (j=x; j<10; ++j) { const foo = j }", {0}},
{0, "for (j=x; j<10; ++j) { const [foo] = [j] }", {0}},
{0, "for (j=x; j<10; ++j) { function foo() {return j} }", {0}},
{1, "for ({j}=x; j<10; ++j) { foo = j }", top},
{1, "for ({j}=x; j<10; ++j) { [foo] = [j] }", top},
{1, "for ({j}=x; j<10; ++j) { var foo = j }", top},
{1, "for ({j}=x; j<10; ++j) { var [foo] = [j] }", top},
{0, "for ({j}=x; j<10; ++j) { let foo = j }", {0}},
{0, "for ({j}=x; j<10; ++j) { let [foo] = [j] }", {0}},
{0, "for ({j}=x; j<10; ++j) { const foo = j }", {0}},
{0, "for ({j}=x; j<10; ++j) { const [foo] = [j] }", {0}},
{0, "for ({j}=x; j<10; ++j) { function foo() {return j} }", {0}},
{1, "for (var j=x; j<10; ++j) { foo = j }", top},
{1, "for (var j=x; j<10; ++j) { [foo] = [j] }", top},
{1, "for (var j=x; j<10; ++j) { var foo = j }", top},
{1, "for (var j=x; j<10; ++j) { var [foo] = [j] }", top},
{0, "for (var j=x; j<10; ++j) { let foo = j }", {0}},
{0, "for (var j=x; j<10; ++j) { let [foo] = [j] }", {0}},
{0, "for (var j=x; j<10; ++j) { const foo = j }", {0}},
{0, "for (var j=x; j<10; ++j) { const [foo] = [j] }", {0}},
{0, "for (var j=x; j<10; ++j) { function foo() {return j} }", {0}},
{1, "for (var {j}=x; j<10; ++j) { foo = j }", top},
{1, "for (var {j}=x; j<10; ++j) { [foo] = [j] }", top},
{1, "for (var {j}=x; j<10; ++j) { var foo = j }", top},
{1, "for (var {j}=x; j<10; ++j) { var [foo] = [j] }", top},
{0, "for (var {j}=x; j<10; ++j) { let foo = j }", {0}},
{0, "for (var {j}=x; j<10; ++j) { let [foo] = [j] }", {0}},
{0, "for (var {j}=x; j<10; ++j) { const foo = j }", {0}},
{0, "for (var {j}=x; j<10; ++j) { const [foo] = [j] }", {0}},
{0, "for (var {j}=x; j<10; ++j) { function foo() {return j} }", {0}},
{1, "for (let j=x; j<10; ++j) { foo = j }", top},
{1, "for (let j=x; j<10; ++j) { [foo] = [j] }", top},
{1, "for (let j=x; j<10; ++j) { var foo = j }", top},
{1, "for (let j=x; j<10; ++j) { var [foo] = [j] }", top},
{0, "for (let j=x; j<10; ++j) { let foo = j }", {0, 0, 0}},
{0, "for (let j=x; j<10; ++j) { let [foo] = [j] }", {0, 0, 0}},
{0, "for (let j=x; j<10; ++j) { const foo = j }", {0, 0, 0}},
{0, "for (let j=x; j<10; ++j) { const [foo] = [j] }", {0, 0, 0}},
{0, "for (let j=x; j<10; ++j) { function foo() {return j} }", {0, 0, 0}},
{1, "for (let {j}=x; j<10; ++j) { foo = j }", top},
{1, "for (let {j}=x; j<10; ++j) { [foo] = [j] }", top},
{1, "for (let {j}=x; j<10; ++j) { var foo = j }", top},
{1, "for (let {j}=x; j<10; ++j) { var [foo] = [j] }", top},
{0, "for (let {j}=x; j<10; ++j) { let foo = j }", {0, 0, 0}},
{0, "for (let {j}=x; j<10; ++j) { let [foo] = [j] }", {0, 0, 0}},
{0, "for (let {j}=x; j<10; ++j) { const foo = j }", {0, 0, 0}},
{0, "for (let {j}=x; j<10; ++j) { const [foo] = [j] }", {0, 0, 0}},
{0, "for (let {j}=x; j<10; ++j) { function foo(){return j} }", {0, 0, 0}},
{1, "for (j of x) { foo = j }", top},
{1, "for (j of x) { [foo] = [j] }", top},
{1, "for (j of x) { var foo = j }", top},
{1, "for (j of x) { var [foo] = [j] }", top},
{0, "for (j of x) { let foo = j }", {0}},
{0, "for (j of x) { let [foo] = [j] }", {0}},
{0, "for (j of x) { const foo = j }", {0}},
{0, "for (j of x) { const [foo] = [j] }", {0}},
{0, "for (j of x) { function foo() {return j} }", {0}},
{1, "for ({j} of x) { foo = j }", top},
{1, "for ({j} of x) { [foo] = [j] }", top},
{1, "for ({j} of x) { var foo = j }", top},
{1, "for ({j} of x) { var [foo] = [j] }", top},
{0, "for ({j} of x) { let foo = j }", {0}},
{0, "for ({j} of x) { let [foo] = [j] }", {0}},
{0, "for ({j} of x) { const foo = j }", {0}},
{0, "for ({j} of x) { const [foo] = [j] }", {0}},
{0, "for ({j} of x) { function foo() {return j} }", {0}},
{1, "for (var j of x) { foo = j }", top},
{1, "for (var j of x) { [foo] = [j] }", top},
{1, "for (var j of x) { var foo = j }", top},
{1, "for (var j of x) { var [foo] = [j] }", top},
{0, "for (var j of x) { let foo = j }", {0}},
{0, "for (var j of x) { let [foo] = [j] }", {0}},
{0, "for (var j of x) { const foo = j }", {0}},
{0, "for (var j of x) { const [foo] = [j] }", {0}},
{0, "for (var j of x) { function foo() {return j} }", {0}},
{1, "for (var {j} of x) { foo = j }", top},
{1, "for (var {j} of x) { [foo] = [j] }", top},
{1, "for (var {j} of x) { var foo = j }", top},
{1, "for (var {j} of x) { var [foo] = [j] }", top},
{0, "for (var {j} of x) { let foo = j }", {0}},
{0, "for (var {j} of x) { let [foo] = [j] }", {0}},
{0, "for (var {j} of x) { const foo = j }", {0}},
{0, "for (var {j} of x) { const [foo] = [j] }", {0}},
{0, "for (var {j} of x) { function foo() {return j} }", {0}},
{1, "for (let j of x) { foo = j }", top},
{1, "for (let j of x) { [foo] = [j] }", top},
{1, "for (let j of x) { var foo = j }", top},
{1, "for (let j of x) { var [foo] = [j] }", top},
{0, "for (let j of x) { let foo = j }", {0, 2, 0}},
{0, "for (let j of x) { let [foo] = [j] }", {0, 2, 0}},
{0, "for (let j of x) { const foo = j }", {0, 2, 0}},
{0, "for (let j of x) { const [foo] = [j] }", {0, 2, 0}},
{0, "for (let j of x) { function foo() {return j} }", {0, 2, 0}},
{1, "for (let {j} of x) { foo = j }", top},
{1, "for (let {j} of x) { [foo] = [j] }", top},
{1, "for (let {j} of x) { var foo = j }", top},
{1, "for (let {j} of x) { var [foo] = [j] }", top},
{0, "for (let {j} of x) { let foo = j }", {0, 2, 0}},
{0, "for (let {j} of x) { let [foo] = [j] }", {0, 2, 0}},
{0, "for (let {j} of x) { const foo = j }", {0, 2, 0}},
{0, "for (let {j} of x) { const [foo] = [j] }", {0, 2, 0}},
{0, "for (let {j} of x) { function foo() {return j} }", {0, 2, 0}},
{1, "for (const j of x) { foo = j }", top},
{1, "for (const j of x) { [foo] = [j] }", top},
{1, "for (const j of x) { var foo = j }", top},
{1, "for (const j of x) { var [foo] = [j] }", top},
{0, "for (const j of x) { let foo = j }", {0, 2, 0}},
{0, "for (const j of x) { let [foo] = [j] }", {0, 2, 0}},
{0, "for (const j of x) { const foo = j }", {0, 2, 0}},
{0, "for (const j of x) { const [foo] = [j] }", {0, 2, 0}},
{0, "for (const j of x) { function foo() {return j} }", {0, 2, 0}},
{1, "for (const {j} of x) { foo = j }", top},
{1, "for (const {j} of x) { [foo] = [j] }", top},
{1, "for (const {j} of x) { var foo = j }", top},
{1, "for (const {j} of x) { var [foo] = [j] }", top},
{0, "for (const {j} of x) { let foo = j }", {0, 2, 0}},
{0, "for (const {j} of x) { let [foo] = [j] }", {0, 2, 0}},
{0, "for (const {j} of x) { const foo = j }", {0, 2, 0}},
{0, "for (const {j} of x) { const [foo] = [j] }", {0, 2, 0}},
{0, "for (const {j} of x) { function foo() {return j} }", {0, 2, 0}},
{1, "for (j in x) { foo = j }", top},
{1, "for (j in x) { [foo] = [j] }", top},
{1, "for (j in x) { var foo = j }", top},
{1, "for (j in x) { var [foo] = [j] }", top},
{0, "for (j in x) { let foo = j }", {0}},
{0, "for (j in x) { let [foo] = [j] }", {0}},
{0, "for (j in x) { const foo = j }", {0}},
{0, "for (j in x) { const [foo] = [j] }", {0}},
{0, "for (j in x) { function foo() {return j} }", {0}},
{1, "for ({j} in x) { foo = j }", top},
{1, "for ({j} in x) { [foo] = [j] }", top},
{1, "for ({j} in x) { var foo = j }", top},
{1, "for ({j} in x) { var [foo] = [j] }", top},
{0, "for ({j} in x) { let foo = j }", {0}},
{0, "for ({j} in x) { let [foo] = [j] }", {0}},
{0, "for ({j} in x) { const foo = j }", {0}},
{0, "for ({j} in x) { const [foo] = [j] }", {0}},
{0, "for ({j} in x) { function foo() {return j} }", {0}},
{1, "for (var j in x) { foo = j }", top},
{1, "for (var j in x) { [foo] = [j] }", top},
{1, "for (var j in x) { var foo = j }", top},
{1, "for (var j in x) { var [foo] = [j] }", top},
{0, "for (var j in x) { let foo = j }", {0}},
{0, "for (var j in x) { let [foo] = [j] }", {0}},
{0, "for (var j in x) { const foo = j }", {0}},
{0, "for (var j in x) { const [foo] = [j] }", {0}},
{0, "for (var j in x) { function foo() {return j} }", {0}},
{1, "for (var {j} in x) { foo = j }", top},
{1, "for (var {j} in x) { [foo] = [j] }", top},
{1, "for (var {j} in x) { var foo = j }", top},
{1, "for (var {j} in x) { var [foo] = [j] }", top},
{0, "for (var {j} in x) { let foo = j }", {0}},
{0, "for (var {j} in x) { let [foo] = [j] }", {0}},
{0, "for (var {j} in x) { const foo = j }", {0}},
{0, "for (var {j} in x) { const [foo] = [j] }", {0}},
{0, "for (var {j} in x) { function foo() {return j} }", {0}},
{1, "for (let j in x) { foo = j }", top},
{1, "for (let j in x) { [foo] = [j] }", top},
{1, "for (let j in x) { var foo = j }", top},
{1, "for (let j in x) { var [foo] = [j] }", top},
{0, "for (let j in x) { let foo = j }", {0, 0, 0}},
{0, "for (let j in x) { let [foo] = [j] }", {0, 0, 0}},
{0, "for (let j in x) { const foo = j }", {0, 0, 0}},
{0, "for (let j in x) { const [foo] = [j] }", {0, 0, 0}},
{0, "for (let j in x) { function foo() {return j} }", {0, 0, 0}},
{1, "for (let {j} in x) { foo = j }", top},
{1, "for (let {j} in x) { [foo] = [j] }", top},
{1, "for (let {j} in x) { var foo = j }", top},
{1, "for (let {j} in x) { var [foo] = [j] }", top},
{0, "for (let {j} in x) { let foo = j }", {0, 0, 0}},
{0, "for (let {j} in x) { let [foo] = [j] }", {0, 0, 0}},
{0, "for (let {j} in x) { const foo = j }", {0, 0, 0}},
{0, "for (let {j} in x) { const [foo] = [j] }", {0, 0, 0}},
{0, "for (let {j} in x) { function foo() {return j} }", {0, 0, 0}},
{1, "for (const j in x) { foo = j }", top},
{1, "for (const j in x) { [foo] = [j] }", top},
{1, "for (const j in x) { var foo = j }", top},
{1, "for (const j in x) { var [foo] = [j] }", top},
{0, "for (const j in x) { let foo = j }", {0, 0, 0}},
{0, "for (const j in x) { let [foo] = [j] }", {0, 0, 0}},
{0, "for (const j in x) { const foo = j }", {0, 0, 0}},
{0, "for (const j in x) { const [foo] = [j] }", {0, 0, 0}},
{0, "for (const j in x) { function foo() {return j} }", {0, 0, 0}},
{1, "for (const {j} in x) { foo = j }", top},
{1, "for (const {j} in x) { [foo] = [j] }", top},
{1, "for (const {j} in x) { var foo = j }", top},
{1, "for (const {j} in x) { var [foo] = [j] }", top},
{0, "for (const {j} in x) { let foo = j }", {0, 0, 0}},
{0, "for (const {j} in x) { let [foo] = [j] }", {0, 0, 0}},
{0, "for (const {j} in x) { const foo = j }", {0, 0, 0}},
{0, "for (const {j} in x) { const [foo] = [j] }", {0, 0, 0}},
{0, "for (const {j} in x) { function foo() {return j} }", {0, 0, 0}},
{1, "while (j) { foo = j }", top},
{1, "while (j) { [foo] = [j] }", top},
{1, "while (j) { var foo = j }", top},
{1, "while (j) { var [foo] = [j] }", top},
{0, "while (j) { let foo = j }", {0}},
{0, "while (j) { let [foo] = [j] }", {0}},
{0, "while (j) { const foo = j }", {0}},
{0, "while (j) { const [foo] = [j] }", {0}},
{0, "while (j) { function foo() {return j} }", {0}},
{1, "do { foo = j } while (j)", top},
{1, "do { [foo] = [j] } while (j)", top},
{1, "do { var foo = j } while (j)", top},
{1, "do { var [foo] = [j] } while (j)", top},
{0, "do { let foo = j } while (j)", {0}},
{0, "do { let [foo] = [j] } while (j)", {0}},
{0, "do { const foo = j } while (j)", {0}},
{0, "do { const [foo] = [j] } while (j)", {0}},
{0, "do { function foo() {return j} } while (j)", {0}},
};
Input script_only_tests[] = {
{1, "for (j=x; j<10; ++j) { function foo() {return j} }", top},
{1, "for ({j}=x; j<10; ++j) { function foo() {return j} }", top},
{1, "for (var j=x; j<10; ++j) { function foo() {return j} }", top},
{1, "for (var {j}=x; j<10; ++j) { function foo() {return j} }", top},
{1, "for (let j=x; j<10; ++j) { function foo() {return j} }", top},
{1, "for (let {j}=x; j<10; ++j) { function foo() {return j} }", top},
{1, "for (j of x) { function foo() {return j} }", top},
{1, "for ({j} of x) { function foo() {return j} }", top},
{1, "for (var j of x) { function foo() {return j} }", top},
{1, "for (var {j} of x) { function foo() {return j} }", top},
{1, "for (let j of x) { function foo() {return j} }", top},
{1, "for (let {j} of x) { function foo() {return j} }", top},
{1, "for (const j of x) { function foo() {return j} }", top},
{1, "for (const {j} of x) { function foo() {return j} }", top},
{1, "for (j in x) { function foo() {return j} }", top},
{1, "for ({j} in x) { function foo() {return j} }", top},
{1, "for (var j in x) { function foo() {return j} }", top},
{1, "for (var {j} in x) { function foo() {return j} }", top},
{1, "for (let j in x) { function foo() {return j} }", top},
{1, "for (let {j} in x) { function foo() {return j} }", top},
{1, "for (const j in x) { function foo() {return j} }", top},
{1, "for (const {j} in x) { function foo() {return j} }", top},
{1, "while (j) { function foo() {return j} }", top},
{1, "do { function foo() {return j} } while (j)", top},
};
for (unsigned i = 0; i < arraysize(module_and_script_tests); ++i) {
Input input = module_and_script_tests[i];
for (unsigned module = 0; module <= 1; ++module) {
for (unsigned allow_lazy_parsing = 0; allow_lazy_parsing <= 1;
++allow_lazy_parsing) {
TestMaybeAssigned(&zone, input, "foo", module, allow_lazy_parsing);
}
TestMaybeAssigned(&zone, wrap(input), "foo", module, false);
}
}
for (unsigned i = 0; i < arraysize(script_only_tests); ++i) {
Input input = script_only_tests[i];
for (unsigned allow_lazy_parsing = 0; allow_lazy_parsing <= 1;
++allow_lazy_parsing) {
TestMaybeAssigned(&zone, input, "foo", false, allow_lazy_parsing);
}
TestMaybeAssigned(&zone, wrap(input), "foo", false, false);
}
}
TEST(MaybeAssignedTopLevel) {
i::Isolate* isolate = CcTest::i_isolate();
i::HandleScope scope(isolate);
......
// 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 --function-context-specialization
function f(n) {
var a = [];
function g() { return x }
for (var i = 0; i < n; ++i) {
var x = i;
a[i] = g;
%OptimizeFunctionOnNextCall(g);
g();
}
return a;
}
var a = f(3);
assertEquals(3, a.length);
assertEquals(2, a[0]());
assertEquals(2, a[1]());
assertEquals(2, a[2]());
// 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
function f(n) {
"use asm";
var a = [];
function g() { return x }
for (var i = 0; i < n; ++i) {
var x = i;
a[i] = g;
%OptimizeFunctionOnNextCall(g);
g();
}
return a;
}
var a = f(3);
assertEquals(3, a.length);
assertEquals(2, a[0]());
assertEquals(2, a[1]());
assertEquals(2, a[2]());
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment