[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}
This commit is contained in:
parent
bfc8dc12e9
commit
32842802b5
@ -218,6 +218,7 @@ void VariableProxy::BindTo(Variable* var) {
|
||||
set_var(var);
|
||||
set_is_resolved();
|
||||
var->set_is_used();
|
||||
if (is_assigned()) var->set_maybe_assigned();
|
||||
}
|
||||
|
||||
void VariableProxy::AssignFeedbackVectorSlots(FeedbackVectorSpec* spec,
|
||||
|
@ -1844,7 +1844,6 @@ void Scope::ResolveTo(ParseInfo* info, VariableProxy* proxy, Variable* var) {
|
||||
#endif
|
||||
|
||||
DCHECK_NOT_NULL(var);
|
||||
if (proxy->is_assigned()) var->set_maybe_assigned();
|
||||
if (AccessNeedsHoleCheck(var, proxy, this)) proxy->set_needs_hole_check();
|
||||
proxy->BindTo(var);
|
||||
}
|
||||
|
@ -1480,6 +1480,10 @@ Statement* Parser::DeclareFunction(const AstRawString* variable_name,
|
||||
bool* ok) {
|
||||
VariableProxy* proxy =
|
||||
factory()->NewVariableProxy(variable_name, NORMAL_VARIABLE);
|
||||
|
||||
DeclarationScope* target_scope = GetDeclarationScope();
|
||||
MarkTopLevelVariableAsAssigned(target_scope, proxy);
|
||||
|
||||
Declaration* declaration =
|
||||
factory()->NewFunctionDeclaration(proxy, function, scope(), pos);
|
||||
Declare(declaration, DeclarationDescriptor::NORMAL, mode, kCreatedInitialized,
|
||||
@ -1488,7 +1492,6 @@ Statement* Parser::DeclareFunction(const AstRawString* variable_name,
|
||||
if (is_sloppy_block_function) {
|
||||
SloppyBlockFunctionStatement* statement =
|
||||
factory()->NewSloppyBlockFunctionStatement();
|
||||
DeclarationScope* target_scope = GetDeclarationScope();
|
||||
target_scope->DeclareSloppyBlockFunction(variable_name, scope(), statement);
|
||||
return statement;
|
||||
}
|
||||
|
@ -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
|
||||
// literals. In that case, *x will be changed to an expression which is the
|
||||
// computed value.
|
||||
|
@ -162,6 +162,9 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
|
||||
names_->Add(name, zone());
|
||||
}
|
||||
|
||||
Scope* var_init_scope = descriptor_->scope;
|
||||
MarkTopLevelVariableAsAssigned(var_init_scope, proxy);
|
||||
|
||||
// If there's no initializer, we're done.
|
||||
if (value == nullptr) return;
|
||||
|
||||
@ -177,7 +180,6 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
|
||||
// 'v' than the 'v' in the declaration (e.g., if we are inside a
|
||||
// 'with' statement or 'catch' block). Global var declarations
|
||||
// also need special treatment.
|
||||
Scope* var_init_scope = descriptor_->scope;
|
||||
|
||||
if (descriptor_->mode == VAR && var_init_scope->is_script_scope()) {
|
||||
// Global variable declarations must be compiled in a specific
|
||||
@ -220,19 +222,9 @@ void Parser::PatternRewriter::VisitVariableProxy(VariableProxy* pattern) {
|
||||
// But for var declarations we need to do a new lookup.
|
||||
if (descriptor_->mode == VAR) {
|
||||
proxy = var_init_scope->NewUnresolved(factory(), name);
|
||||
// TODO(neis): Set is_assigned on proxy.
|
||||
} else {
|
||||
DCHECK_NOT_NULL(proxy);
|
||||
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.
|
||||
int pos = IsSubPattern() ? pattern->position() : value->position();
|
||||
|
@ -3525,7 +3525,10 @@ TEST(MaybeAssignedTopLevel) {
|
||||
const char* prefixes[] = {
|
||||
"let foo; ", "let foo = 0; ",
|
||||
"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[] = {
|
||||
"function bar() {foo = 42}; ext(bar); ext(foo)",
|
||||
|
Loading…
Reference in New Issue
Block a user