Commit 9884930b authored by Toon Verwaest's avatar Toon Verwaest Committed by Commit Bot

[parser] Simplify Scope::DeclareVariable

Restructure the code a little, and change how we detect sloppy block function
redeclaration so we don't dereference a possibly nullptr function.

Bug: chromium:900786
Change-Id: Ief124fe767603ca36f4dc8865c4aeb3e0635b4cf
Reviewed-on: https://chromium-review.googlesource.com/c/1314331Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#57206}
parent ea27a244
...@@ -444,14 +444,26 @@ inline NestedVariableDeclaration* VariableDeclaration::AsNested() { ...@@ -444,14 +444,26 @@ inline NestedVariableDeclaration* VariableDeclaration::AsNested() {
class FunctionDeclaration final : public Declaration { class FunctionDeclaration final : public Declaration {
public: public:
FunctionLiteral* fun() const { return fun_; } FunctionLiteral* fun() const { return fun_; }
bool declares_sloppy_block_function() const {
return DeclaresSloppyBlockFunction::decode(bit_field_);
}
private: private:
friend class AstNodeFactory; friend class AstNodeFactory;
FunctionDeclaration(VariableProxy* proxy, FunctionLiteral* fun, int pos) class DeclaresSloppyBlockFunction
: Declaration(proxy, pos, kFunctionDeclaration), fun_(fun) {} : public BitField<bool, Declaration::kNextBitFieldIndex, 1> {};
FunctionDeclaration(VariableProxy* proxy, FunctionLiteral* fun,
bool declares_sloppy_block_function, int pos)
: Declaration(proxy, pos, kFunctionDeclaration), fun_(fun) {
bit_field_ = DeclaresSloppyBlockFunction::update(
bit_field_, declares_sloppy_block_function);
}
FunctionLiteral* fun_; FunctionLiteral* fun_;
static const uint8_t kNextBitFieldIndex = DeclaresSloppyBlockFunction::kNext;
}; };
...@@ -2883,8 +2895,11 @@ class AstNodeFactory final { ...@@ -2883,8 +2895,11 @@ class AstNodeFactory final {
} }
FunctionDeclaration* NewFunctionDeclaration(VariableProxy* proxy, FunctionDeclaration* NewFunctionDeclaration(VariableProxy* proxy,
FunctionLiteral* fun, int pos) { FunctionLiteral* fun,
return new (zone_) FunctionDeclaration(proxy, fun, pos); bool is_sloppy_block_function,
int pos) {
return new (zone_)
FunctionDeclaration(proxy, fun, is_sloppy_block_function, pos);
} }
Block* NewBlock(int capacity, bool ignore_completion_value, Block* NewBlock(int capacity, bool ignore_completion_value,
......
...@@ -1058,8 +1058,7 @@ Variable* DeclarationScope::DeclareParameterName( ...@@ -1058,8 +1058,7 @@ Variable* DeclarationScope::DeclareParameterName(
} }
Variable* Scope::DeclareLocal(const AstRawString* name, VariableMode mode, Variable* Scope::DeclareLocal(const AstRawString* name, VariableMode mode,
InitializationFlag init_flag, VariableKind kind, InitializationFlag init_flag) {
MaybeAssignedFlag maybe_assigned_flag) {
DCHECK(!already_resolved_); DCHECK(!already_resolved_);
// This function handles VariableMode::kVar, VariableMode::kLet, and // This function handles VariableMode::kVar, VariableMode::kLet, and
// VariableMode::kConst modes. VariableMode::kDynamic variables are // VariableMode::kConst modes. VariableMode::kDynamic variables are
...@@ -1070,7 +1069,7 @@ Variable* Scope::DeclareLocal(const AstRawString* name, VariableMode mode, ...@@ -1070,7 +1069,7 @@ Variable* Scope::DeclareLocal(const AstRawString* name, VariableMode mode,
mode == VariableMode::kVar || mode == VariableMode::kLet || mode == VariableMode::kVar || mode == VariableMode::kLet ||
mode == VariableMode::kConst); mode == VariableMode::kConst);
DCHECK(!GetDeclarationScope()->was_lazily_parsed()); DCHECK(!GetDeclarationScope()->was_lazily_parsed());
return Declare(zone(), name, mode, kind, init_flag, maybe_assigned_flag); return Declare(zone(), name, mode, NORMAL_VARIABLE, init_flag);
} }
Variable* Scope::DeclareVariable( Variable* Scope::DeclareVariable(
...@@ -1094,7 +1093,6 @@ Variable* Scope::DeclareVariable( ...@@ -1094,7 +1093,6 @@ Variable* Scope::DeclareVariable(
VariableProxy* proxy = declaration->proxy(); VariableProxy* proxy = declaration->proxy();
DCHECK_NOT_NULL(proxy->raw_name()); DCHECK_NOT_NULL(proxy->raw_name());
const AstRawString* name = proxy->raw_name(); const AstRawString* name = proxy->raw_name();
bool is_function_declaration = declaration->IsFunctionDeclaration();
// Pessimistically assume that top-level variables will be assigned. // Pessimistically assume that top-level variables will be assigned.
// //
...@@ -1107,70 +1105,54 @@ Variable* Scope::DeclareVariable( ...@@ -1107,70 +1105,54 @@ Variable* Scope::DeclareVariable(
if (mode != VariableMode::kConst) proxy->set_is_assigned(); if (mode != VariableMode::kConst) proxy->set_is_assigned();
} }
Variable* var = nullptr; Variable* var = LookupLocal(name);
if (is_eval_scope() && is_sloppy(language_mode()) && // Declare the variable in the declaration scope.
mode == VariableMode::kVar) { if (V8_LIKELY(var == nullptr)) {
// In a var binding in a sloppy direct eval, pollute the enclosing scope if (V8_UNLIKELY(is_eval_scope() && is_sloppy(language_mode()) &&
// with this new binding by doing the following: mode == VariableMode::kVar)) {
// The proxy is bound to a lookup variable to force a dynamic declaration // In a var binding in a sloppy direct eval, pollute the enclosing scope
// using the DeclareEvalVar or DeclareEvalFunction runtime functions. // with this new binding by doing the following:
var = new (zone()) // The proxy is bound to a lookup variable to force a dynamic declaration
Variable(this, name, mode, NORMAL_VARIABLE, init, kMaybeAssigned); // using the DeclareEvalVar or DeclareEvalFunction runtime functions.
var->AllocateTo(VariableLocation::LOOKUP, -1); var = new (zone())
} else { Variable(this, name, mode, NORMAL_VARIABLE, init, kMaybeAssigned);
// Declare the variable in the declaration scope. var->AllocateTo(VariableLocation::LOOKUP, -1);
var = LookupLocal(name); } else {
if (var == nullptr) {
// Declare the name. // Declare the name.
VariableKind kind = NORMAL_VARIABLE; var = DeclareLocal(name, mode, init);
if (is_function_declaration) { }
kind = FUNCTION_VARIABLE; } else {
} var->set_maybe_assigned();
var = DeclareLocal(name, mode, init, kind, kNotAssigned); if (V8_UNLIKELY(IsLexicalVariableMode(mode) ||
} else if (IsLexicalVariableMode(mode) || IsLexicalVariableMode(var->mode()))) {
IsLexicalVariableMode(var->mode())) { // The name was declared in this scope before; check for conflicting
// Allow duplicate function decls for web compat, see bug 4693. // re-declarations. We have a conflict if either of the declarations is
bool duplicate_allowed = false; // not a var (in script scope, we also have to ignore legacy const for
if (is_sloppy(language_mode()) && is_function_declaration && // compatibility). There is similar code in runtime.cc in the Declare
var->is_function()) { // functions. The function CheckConflictingVarDeclarations checks for
DCHECK(IsLexicalVariableMode(mode) && // var and let bindings from different scopes whereas this is a check
IsLexicalVariableMode(var->mode())); // for conflicting declarations within the same scope. This check also
// If the duplication is allowed, then the var will show up // covers the special case
// in the SloppyBlockFunctionMap and the new FunctionKind //
// will be a permitted duplicate. // function () { let x; { var x; } }
FunctionKind function_kind = //
declaration->AsFunctionDeclaration()->fun()->kind(); // because the var declaration is hoisted to the function scope where
SloppyBlockFunctionMap* map = // 'x' is already bound.
GetDeclarationScope()->sloppy_block_function_map(); //
duplicate_allowed = map != nullptr && // In harmony we treat re-declarations as early errors. See ES5 16 for a
map->Lookup(const_cast<AstRawString*>(name), // definition of early errors.
name->Hash()) != nullptr && //
!IsAsyncFunction(function_kind) && // Allow duplicate function decls for web compat, see bug 4693. If the
!IsGeneratorFunction(function_kind); // duplication is allowed, then the var will show up in the
} // SloppyBlockFunctionMap.
if (duplicate_allowed) { SloppyBlockFunctionMap* map =
*sloppy_mode_block_scope_function_redefinition = true; GetDeclarationScope()->sloppy_block_function_map();
} else { *ok =
// The name was declared in this scope before; check for conflicting map != nullptr && declaration->IsFunctionDeclaration() &&
// re-declarations. We have a conflict if either of the declarations declaration->AsFunctionDeclaration()
// is not a var (in script scope, we also have to ignore legacy const ->declares_sloppy_block_function() &&
// for compatibility). There is similar code in runtime.cc in the map->Lookup(const_cast<AstRawString*>(name), name->Hash()) != nullptr;
// Declare functions. The function CheckConflictingVarDeclarations *sloppy_mode_block_scope_function_redefinition = *ok;
// checks for var and let bindings from different scopes whereas this
// is a check for conflicting declarations within the same scope. This
// check also covers the special case
//
// function () { let x; { var x; } }
//
// because the var declaration is hoisted to the function scope where
// 'x' is already bound.
DCHECK(IsDeclaredVariableMode(var->mode()));
// In harmony we treat re-declarations as early errors. See
// ES5 16 for a definition of early errors.
*ok = false;
}
} else if (mode == VariableMode::kVar) {
var->set_maybe_assigned();
} }
} }
DCHECK_NOT_NULL(var); DCHECK_NOT_NULL(var);
......
...@@ -194,9 +194,7 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) { ...@@ -194,9 +194,7 @@ class V8_EXPORT_PRIVATE Scope : public NON_EXPORTED_BASE(ZoneObject) {
// Declare a local variable in this scope. If the variable has been // Declare a local variable in this scope. If the variable has been
// declared before, the previously declared variable is returned. // declared before, the previously declared variable is returned.
Variable* DeclareLocal(const AstRawString* name, VariableMode mode, Variable* DeclareLocal(const AstRawString* name, VariableMode mode,
InitializationFlag init_flag = kCreatedInitialized, InitializationFlag init_flag = kCreatedInitialized);
VariableKind kind = NORMAL_VARIABLE,
MaybeAssignedFlag maybe_assigned_flag = kNotAssigned);
Variable* DeclareVariable(Declaration* declaration, VariableMode mode, Variable* DeclareVariable(Declaration* declaration, VariableMode mode,
InitializationFlag init, InitializationFlag init,
......
...@@ -131,7 +131,6 @@ class Variable final : public ZoneObject { ...@@ -131,7 +131,6 @@ class Variable final : public ZoneObject {
return kind() != SLOPPY_FUNCTION_NAME_VARIABLE || is_strict(language_mode); return kind() != SLOPPY_FUNCTION_NAME_VARIABLE || is_strict(language_mode);
} }
bool is_function() const { return kind() == FUNCTION_VARIABLE; }
bool is_this() const { return kind() == THIS_VARIABLE; } bool is_this() const { return kind() == THIS_VARIABLE; }
bool is_sloppy_function_name() const { bool is_sloppy_function_name() const {
return kind() == SLOPPY_FUNCTION_NAME_VARIABLE; return kind() == SLOPPY_FUNCTION_NAME_VARIABLE;
......
...@@ -1030,7 +1030,6 @@ inline const char* VariableMode2String(VariableMode mode) { ...@@ -1030,7 +1030,6 @@ inline const char* VariableMode2String(VariableMode mode) {
enum VariableKind : uint8_t { enum VariableKind : uint8_t {
NORMAL_VARIABLE, NORMAL_VARIABLE,
FUNCTION_VARIABLE,
THIS_VARIABLE, THIS_VARIABLE,
SLOPPY_FUNCTION_NAME_VARIABLE SLOPPY_FUNCTION_NAME_VARIABLE
}; };
......
...@@ -1441,8 +1441,8 @@ Statement* Parser::DeclareFunction(const AstRawString* variable_name, ...@@ -1441,8 +1441,8 @@ Statement* Parser::DeclareFunction(const AstRawString* variable_name,
ZonePtrList<const AstRawString>* names) { ZonePtrList<const AstRawString>* names) {
VariableProxy* proxy = VariableProxy* proxy =
factory()->NewVariableProxy(variable_name, NORMAL_VARIABLE, pos); factory()->NewVariableProxy(variable_name, NORMAL_VARIABLE, pos);
Declaration* declaration = Declaration* declaration = factory()->NewFunctionDeclaration(
factory()->NewFunctionDeclaration(proxy, function, pos); proxy, function, is_sloppy_block_function, pos);
Declare(declaration, DeclarationDescriptor::NORMAL, mode, Declare(declaration, DeclarationDescriptor::NORMAL, mode,
kCreatedInitialized); kCreatedInitialized);
if (names) names->Add(variable_name, zone()); if (names) names->Add(variable_name, zone());
......
// Copyright 2018 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.
assertThrows("{function g(){}function g(){+", SyntaxError);
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