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

[ast/parsing] Pessimistically assume all top-level variables will be assigned.

A previous CL (https://codereview.chromium.org/2634123002) did that for
let-declared variables.  This CL also does it for var- and function-declared
variables.

BUG=v8:5636

Review-Url: https://codereview.chromium.org/2656753003
Cr-Commit-Position: refs/heads/master@{#42813}
parent bfc8dc12
...@@ -218,6 +218,7 @@ void VariableProxy::BindTo(Variable* var) { ...@@ -218,6 +218,7 @@ void VariableProxy::BindTo(Variable* var) {
set_var(var); set_var(var);
set_is_resolved(); set_is_resolved();
var->set_is_used(); var->set_is_used();
if (is_assigned()) var->set_maybe_assigned();
} }
void VariableProxy::AssignFeedbackVectorSlots(FeedbackVectorSpec* spec, void VariableProxy::AssignFeedbackVectorSlots(FeedbackVectorSpec* spec,
......
...@@ -1844,7 +1844,6 @@ void Scope::ResolveTo(ParseInfo* info, VariableProxy* proxy, Variable* var) { ...@@ -1844,7 +1844,6 @@ void Scope::ResolveTo(ParseInfo* info, VariableProxy* proxy, Variable* var) {
#endif #endif
DCHECK_NOT_NULL(var); DCHECK_NOT_NULL(var);
if (proxy->is_assigned()) var->set_maybe_assigned();
if (AccessNeedsHoleCheck(var, proxy, this)) proxy->set_needs_hole_check(); if (AccessNeedsHoleCheck(var, proxy, this)) proxy->set_needs_hole_check();
proxy->BindTo(var); proxy->BindTo(var);
} }
......
...@@ -1480,6 +1480,10 @@ Statement* Parser::DeclareFunction(const AstRawString* variable_name, ...@@ -1480,6 +1480,10 @@ Statement* Parser::DeclareFunction(const AstRawString* variable_name,
bool* ok) { bool* ok) {
VariableProxy* proxy = VariableProxy* proxy =
factory()->NewVariableProxy(variable_name, NORMAL_VARIABLE); factory()->NewVariableProxy(variable_name, NORMAL_VARIABLE);
DeclarationScope* target_scope = GetDeclarationScope();
MarkTopLevelVariableAsAssigned(target_scope, proxy);
Declaration* declaration = Declaration* declaration =
factory()->NewFunctionDeclaration(proxy, function, scope(), pos); factory()->NewFunctionDeclaration(proxy, function, scope(), pos);
Declare(declaration, DeclarationDescriptor::NORMAL, mode, kCreatedInitialized, Declare(declaration, DeclarationDescriptor::NORMAL, mode, kCreatedInitialized,
...@@ -1488,7 +1492,6 @@ Statement* Parser::DeclareFunction(const AstRawString* variable_name, ...@@ -1488,7 +1492,6 @@ Statement* Parser::DeclareFunction(const AstRawString* variable_name,
if (is_sloppy_block_function) { if (is_sloppy_block_function) {
SloppyBlockFunctionStatement* statement = SloppyBlockFunctionStatement* statement =
factory()->NewSloppyBlockFunctionStatement(); factory()->NewSloppyBlockFunctionStatement();
DeclarationScope* target_scope = GetDeclarationScope();
target_scope->DeclareSloppyBlockFunction(variable_name, scope(), statement); target_scope->DeclareSloppyBlockFunction(variable_name, scope(), statement);
return statement; return statement;
} }
......
...@@ -824,6 +824,20 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { ...@@ -824,6 +824,20 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
} }
} }
// Pessimistically assume that top-level variables will be assigned.
//
// Top-level variables in a script can be accessed by other scripts or even
// become global properties. While this does not apply to top-level variables
// in a module (assuming they are not exported), we must still mark these as
// assigned because they might be accessed by a lazily parsed top-level
// function, which, for efficiency, we preparse without variable tracking.
V8_INLINE static void MarkTopLevelVariableAsAssigned(Scope* scope,
VariableProxy* proxy) {
if (scope->is_script_scope() || scope->is_module_scope()) {
proxy->set_is_assigned();
}
}
// Returns true if we have a binary expression between two numeric // Returns true if we have a binary expression between two numeric
// literals. In that case, *x will be changed to an expression which is the // literals. In that case, *x will be changed to an expression which is the
// computed value. // computed value.
......
...@@ -162,6 +162,9 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) { ...@@ -162,6 +162,9 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
names_->Add(name, zone()); names_->Add(name, zone());
} }
Scope* var_init_scope = descriptor_->scope;
MarkTopLevelVariableAsAssigned(var_init_scope, proxy);
// If there's no initializer, we're done. // If there's no initializer, we're done.
if (value == nullptr) return; if (value == nullptr) return;
...@@ -177,7 +180,6 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) { ...@@ -177,7 +180,6 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
// 'v' than the 'v' in the declaration (e.g., if we are inside a // 'v' than the 'v' in the declaration (e.g., if we are inside a
// 'with' statement or 'catch' block). Global var declarations // 'with' statement or 'catch' block). Global var declarations
// also need special treatment. // also need special treatment.
Scope* var_init_scope = descriptor_->scope;
if (descriptor_->mode == VAR && var_init_scope->is_script_scope()) { if (descriptor_->mode == VAR && var_init_scope->is_script_scope()) {
// Global variable declarations must be compiled in a specific // Global variable declarations must be compiled in a specific
...@@ -220,19 +222,9 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) { ...@@ -220,19 +222,9 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
// But for var declarations we need to do a new lookup. // But for var declarations we need to do a new lookup.
if (descriptor_->mode == VAR) { if (descriptor_->mode == VAR) {
proxy = var_init_scope->NewUnresolved(factory(), name); proxy = var_init_scope->NewUnresolved(factory(), name);
// TODO(neis): Set is_assigned on proxy.
} else { } else {
DCHECK_NOT_NULL(proxy); DCHECK_NOT_NULL(proxy);
DCHECK_NOT_NULL(proxy->var()); DCHECK_NOT_NULL(proxy->var());
if (var_init_scope->is_script_scope() ||
var_init_scope->is_module_scope()) {
// We have to pessimistically assume that top-level variables will be
// assigned. This is because they might be accessed by a lazily parsed
// top-level function, which, for efficiency, we preparse without
// variable tracking. In the case of a script (not a module), they
// might also get accessed by another script.
proxy->set_is_assigned();
}
} }
// Add break location for destructured sub-pattern. // Add break location for destructured sub-pattern.
int pos = IsSubPattern() ? pattern->position() : value->position(); int pos = IsSubPattern() ? pattern->position() : value->position();
......
...@@ -3525,7 +3525,10 @@ TEST(MaybeAssignedTopLevel) { ...@@ -3525,7 +3525,10 @@ TEST(MaybeAssignedTopLevel) {
const char* prefixes[] = { const char* prefixes[] = {
"let foo; ", "let foo = 0; ", "let foo; ", "let foo = 0; ",
"let [foo] = [1]; ", "let {foo} = {foo: 2}; ", "let [foo] = [1]; ", "let {foo} = {foo: 2}; ",
"let {foo=3} = {}; ", "let {foo=3} = {}; ", "function foo() {}; ",
"var foo; ", "var foo = 0; ",
"var [foo] = [1]; ", "var {foo} = {foo: 2}; ",
"var {foo=3} = {}; ", "function* foo() {}; ",
}; };
const char* sources[] = { const char* sources[] = {
"function bar() {foo = 42}; ext(bar); ext(foo)", "function bar() {foo = 42}; ext(bar); ext(foo)",
......
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