Commit 5a72c6b6 authored by Toon Verwaest's avatar Toon Verwaest Committed by Commit Bot

[parser] Use Token::INIT for hoisted sloppy block functions when possible

Change-Id: I83dc3bed644361be1b94063daefd890b10ba50cd
Reviewed-on: https://chromium-review.googlesource.com/c/1433772
Commit-Queue: Toon Verwaest <verwaest@chromium.org>
Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/master@{#59095}
parent 592aeefa
...@@ -491,26 +491,14 @@ inline NestedVariableDeclaration* VariableDeclaration::AsNested() { ...@@ -491,26 +491,14 @@ 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;
class DeclaresSloppyBlockFunction FunctionDeclaration(FunctionLiteral* fun, int pos)
: public BitField<bool, Declaration::kNextBitFieldIndex, 1> {}; : Declaration(pos, kFunctionDeclaration), fun_(fun) {}
FunctionDeclaration(FunctionLiteral* fun, bool declares_sloppy_block_function,
int pos)
: Declaration(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;
}; };
...@@ -994,14 +982,30 @@ class SloppyBlockFunctionStatement final : public Statement { ...@@ -994,14 +982,30 @@ class SloppyBlockFunctionStatement final : public Statement {
public: public:
Statement* statement() const { return statement_; } Statement* statement() const { return statement_; }
void set_statement(Statement* statement) { statement_ = statement; } void set_statement(Statement* statement) { statement_ = statement; }
Scope* scope() const { return var_->scope(); }
Variable* var() const { return var_; }
Token::Value init() const { return TokenField::decode(bit_field_); }
const AstRawString* name() const { return var_->raw_name(); }
SloppyBlockFunctionStatement** next() { return &next_; }
private: private:
friend class AstNodeFactory; friend class AstNodeFactory;
SloppyBlockFunctionStatement(int pos, Statement* statement) class TokenField
: Statement(pos, kSloppyBlockFunctionStatement), statement_(statement) {} : public BitField<Token::Value, Statement::kNextBitFieldIndex, 8> {};
SloppyBlockFunctionStatement(int pos, Variable* var, Token::Value init,
Statement* statement)
: Statement(pos, kSloppyBlockFunctionStatement),
var_(var),
statement_(statement),
next_(nullptr) {
bit_field_ = TokenField::update(bit_field_, init);
}
Variable* var_;
Statement* statement_; Statement* statement_;
SloppyBlockFunctionStatement* next_;
}; };
...@@ -2812,10 +2816,8 @@ class AstNodeFactory final { ...@@ -2812,10 +2816,8 @@ class AstNodeFactory final {
return new (zone_) NestedVariableDeclaration(scope, pos); return new (zone_) NestedVariableDeclaration(scope, pos);
} }
FunctionDeclaration* NewFunctionDeclaration(FunctionLiteral* fun, FunctionDeclaration* NewFunctionDeclaration(FunctionLiteral* fun, int pos) {
bool is_sloppy_block_function, return new (zone_) FunctionDeclaration(fun, pos);
int pos) {
return new (zone_) FunctionDeclaration(fun, is_sloppy_block_function, pos);
} }
Block* NewBlock(int capacity, bool ignore_completion_value) { Block* NewBlock(int capacity, bool ignore_completion_value) {
...@@ -2958,8 +2960,10 @@ class AstNodeFactory final { ...@@ -2958,8 +2960,10 @@ class AstNodeFactory final {
return failure_expression_; return failure_expression_;
} }
SloppyBlockFunctionStatement* NewSloppyBlockFunctionStatement(int pos) { SloppyBlockFunctionStatement* NewSloppyBlockFunctionStatement(
return new (zone_) SloppyBlockFunctionStatement(pos, EmptyStatement()); int pos, Variable* var, Token::Value init) {
return new (zone_)
SloppyBlockFunctionStatement(pos, var, init, EmptyStatement());
} }
CaseClause* NewCaseClause(Expression* label, CaseClause* NewCaseClause(Expression* label,
......
This diff is collapsed.
...@@ -43,37 +43,6 @@ class VariableMap: public ZoneHashMap { ...@@ -43,37 +43,6 @@ class VariableMap: public ZoneHashMap {
void Add(Zone* zone, Variable* var); void Add(Zone* zone, Variable* var);
}; };
// Sloppy block-scoped function declarations to var-bind
class SloppyBlockFunctionMap : public ZoneHashMap {
public:
class Delegate : public ZoneObject {
public:
Delegate(Scope* scope, SloppyBlockFunctionStatement* statement, int index)
: scope_(scope), statement_(statement), next_(nullptr), index_(index) {}
void set_statement(Statement* statement);
void set_next(Delegate* next) { next_ = next; }
Delegate* next() const { return next_; }
Scope* scope() const { return scope_; }
int index() const { return index_; }
int position() const { return statement_->position(); }
private:
Scope* scope_;
SloppyBlockFunctionStatement* statement_;
Delegate* next_;
int index_;
};
explicit SloppyBlockFunctionMap(Zone* zone);
void Declare(Zone* zone, const AstRawString* name, Scope* scope,
SloppyBlockFunctionStatement* statement);
private:
int count_;
};
class Scope; class Scope;
template <> template <>
...@@ -940,17 +909,12 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope { ...@@ -940,17 +909,12 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
void AddLocal(Variable* var); void AddLocal(Variable* var);
void DeclareSloppyBlockFunction( void DeclareSloppyBlockFunction(
const AstRawString* name, Scope* scope, SloppyBlockFunctionStatement* sloppy_block_function);
SloppyBlockFunctionStatement* statement = nullptr);
// Go through sloppy_block_function_map_ and hoist those (into this scope) // Go through sloppy_block_functions_ and hoist those (into this scope)
// which should be hoisted. // which should be hoisted.
void HoistSloppyBlockFunctions(AstNodeFactory* factory); void HoistSloppyBlockFunctions(AstNodeFactory* factory);
SloppyBlockFunctionMap* sloppy_block_function_map() {
return sloppy_block_function_map_;
}
// Compute top scope and allocate variables. For lazy compilation the top // Compute top scope and allocate variables. For lazy compilation the top
// scope only contains the single lazily compiled function, so this // scope only contains the single lazily compiled function, so this
// doesn't re-allocate variables repeatedly. // doesn't re-allocate variables repeatedly.
...@@ -1069,7 +1033,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope { ...@@ -1069,7 +1033,7 @@ class V8_EXPORT_PRIVATE DeclarationScope : public Scope {
// Parameter list in source order. // Parameter list in source order.
ZonePtrList<Variable> params_; ZonePtrList<Variable> params_;
// Map of function names to lists of functions defined in sloppy blocks // Map of function names to lists of functions defined in sloppy blocks
SloppyBlockFunctionMap* sloppy_block_function_map_; base::ThreadedList<SloppyBlockFunctionStatement> sloppy_block_functions_;
// Convenience variable. // Convenience variable.
Variable* receiver_; Variable* receiver_;
// Function variable, if any; function scopes only. // Function variable, if any; function scopes only.
......
...@@ -137,6 +137,9 @@ class Variable final : public ZoneObject { ...@@ -137,6 +137,9 @@ class Variable final : public ZoneObject {
} }
bool is_parameter() const { return kind() == PARAMETER_VARIABLE; } bool is_parameter() const { return kind() == PARAMETER_VARIABLE; }
bool is_sloppy_block_function() {
return kind() == SLOPPY_BLOCK_FUNCTION_VARIABLE;
}
Variable* local_if_not_shadowed() const { Variable* local_if_not_shadowed() const {
DCHECK(mode() == VariableMode::kDynamicLocal && DCHECK(mode() == VariableMode::kDynamicLocal &&
...@@ -207,7 +210,7 @@ class Variable final : public ZoneObject { ...@@ -207,7 +210,7 @@ class Variable final : public ZoneObject {
class VariableModeField : public BitField16<VariableMode, 0, 3> {}; class VariableModeField : public BitField16<VariableMode, 0, 3> {};
class VariableKindField class VariableKindField
: public BitField16<VariableKind, VariableModeField::kNext, 2> {}; : public BitField16<VariableKind, VariableModeField::kNext, 3> {};
class LocationField class LocationField
: public BitField16<VariableLocation, VariableKindField::kNext, 3> {}; : public BitField16<VariableLocation, VariableKindField::kNext, 3> {};
class ForceContextAllocationField class ForceContextAllocationField
......
...@@ -1099,6 +1099,7 @@ enum VariableKind : uint8_t { ...@@ -1099,6 +1099,7 @@ enum VariableKind : uint8_t {
NORMAL_VARIABLE, NORMAL_VARIABLE,
PARAMETER_VARIABLE, PARAMETER_VARIABLE,
THIS_VARIABLE, THIS_VARIABLE,
SLOPPY_BLOCK_FUNCTION_VARIABLE,
SLOPPY_FUNCTION_NAME_VARIABLE SLOPPY_FUNCTION_NAME_VARIABLE
}; };
......
...@@ -3648,10 +3648,10 @@ ParserBase<Impl>::ParseHoistableDeclaration( ...@@ -3648,10 +3648,10 @@ ParserBase<Impl>::ParseHoistableDeclaration(
FuncNameInferrerState fni_state(&fni_); FuncNameInferrerState fni_state(&fni_);
impl()->PushEnclosingName(name); impl()->PushEnclosingName(name);
FunctionKind kind = FunctionKindFor(flags); FunctionKind function_kind = FunctionKindFor(flags);
FunctionLiteralT function = impl()->ParseFunctionLiteral( FunctionLiteralT function = impl()->ParseFunctionLiteral(
name, scanner()->location(), name_validity, kind, pos, name, scanner()->location(), name_validity, function_kind, pos,
FunctionLiteral::kDeclaration, language_mode(), nullptr); FunctionLiteral::kDeclaration, language_mode(), nullptr);
// In ES6, a function behaves as a lexical binding, except in // In ES6, a function behaves as a lexical binding, except in
...@@ -3662,16 +3662,17 @@ ParserBase<Impl>::ParseHoistableDeclaration( ...@@ -3662,16 +3662,17 @@ ParserBase<Impl>::ParseHoistableDeclaration(
: VariableMode::kVar; : VariableMode::kVar;
// Async functions don't undergo sloppy mode block scoped hoisting, and don't // Async functions don't undergo sloppy mode block scoped hoisting, and don't
// allow duplicates in a block. Both are represented by the // allow duplicates in a block. Both are represented by the
// sloppy_block_function_map. Don't add them to the map for async functions. // sloppy_block_functions_. Don't add them to the map for async functions.
// Generators are also supposed to be prohibited; currently doing this behind // Generators are also supposed to be prohibited; currently doing this behind
// a flag and UseCounting violations to assess web compatibility. // a flag and UseCounting violations to assess web compatibility.
bool is_sloppy_block_function = is_sloppy(language_mode()) && VariableKind kind = is_sloppy(language_mode()) &&
!scope()->is_declaration_scope() && !scope()->is_declaration_scope() &&
flags == ParseFunctionFlag::kIsNormal; flags == ParseFunctionFlag::kIsNormal
? SLOPPY_BLOCK_FUNCTION_VARIABLE
return impl()->DeclareFunction(variable_name, function, mode, pos, : NORMAL_VARIABLE;
end_position(), is_sloppy_block_function,
names); return impl()->DeclareFunction(variable_name, function, mode, kind, pos,
end_position(), names);
} }
template <typename Impl> template <typename Impl>
......
...@@ -1438,20 +1438,20 @@ Statement* Parser::BuildInitializationBlock( ...@@ -1438,20 +1438,20 @@ Statement* Parser::BuildInitializationBlock(
Statement* Parser::DeclareFunction(const AstRawString* variable_name, Statement* Parser::DeclareFunction(const AstRawString* variable_name,
FunctionLiteral* function, VariableMode mode, FunctionLiteral* function, VariableMode mode,
int beg_pos, int end_pos, VariableKind kind, int beg_pos, int end_pos,
bool is_sloppy_block_function,
ZonePtrList<const AstRawString>* names) { ZonePtrList<const AstRawString>* names) {
Declaration* declaration = factory()->NewFunctionDeclaration( Declaration* declaration =
function, is_sloppy_block_function, beg_pos); factory()->NewFunctionDeclaration(function, beg_pos);
bool was_added; bool was_added;
Declare(declaration, variable_name, NORMAL_VARIABLE, mode, Declare(declaration, variable_name, kind, mode, kCreatedInitialized, scope(),
kCreatedInitialized, scope(), &was_added, beg_pos); &was_added, beg_pos);
if (names) names->Add(variable_name, zone()); if (names) names->Add(variable_name, zone());
if (is_sloppy_block_function) { if (kind == SLOPPY_BLOCK_FUNCTION_VARIABLE) {
Token::Value init = loop_nesting_depth() > 0 ? Token::ASSIGN : Token::INIT;
SloppyBlockFunctionStatement* statement = SloppyBlockFunctionStatement* statement =
factory()->NewSloppyBlockFunctionStatement(end_pos); factory()->NewSloppyBlockFunctionStatement(end_pos, declaration->var(),
GetDeclarationScope()->DeclareSloppyBlockFunction(variable_name, scope(), init);
statement); GetDeclarationScope()->DeclareSloppyBlockFunction(statement);
return statement; return statement;
} }
return factory()->EmptyStatement(); return factory()->EmptyStatement();
......
...@@ -341,8 +341,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { ...@@ -341,8 +341,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
Statement* DeclareFunction(const AstRawString* variable_name, Statement* DeclareFunction(const AstRawString* variable_name,
FunctionLiteral* function, VariableMode mode, FunctionLiteral* function, VariableMode mode,
int beg_pos, int end_pos, VariableKind kind, int beg_pos, int end_pos,
bool is_sloppy_block_function,
ZonePtrList<const AstRawString>* names); ZonePtrList<const AstRawString>* names);
Variable* CreateSyntheticContextVariable(const AstRawString* synthetic_name); Variable* CreateSyntheticContextVariable(const AstRawString* synthetic_name);
FunctionLiteral* CreateInitializerFunction( FunctionLiteral* CreateInitializerFunction(
......
...@@ -1101,16 +1101,14 @@ class PreParser : public ParserBase<PreParser> { ...@@ -1101,16 +1101,14 @@ class PreParser : public ParserBase<PreParser> {
// Don't bother actually binding the proxy. // Don't bother actually binding the proxy.
} }
void DeclareVariableName(const AstRawString* name, VariableMode mode, Variable* DeclareVariableName(const AstRawString* name, VariableMode mode,
Scope* scope, bool* was_added, Scope* scope, bool* was_added,
int position = kNoSourcePosition, int position = kNoSourcePosition,
VariableKind kind = NORMAL_VARIABLE) { VariableKind kind = NORMAL_VARIABLE) {
Variable* var = scope->DeclareVariableName(name, mode, was_added, kind); Variable* var = scope->DeclareVariableName(name, mode, was_added, kind);
if (var == nullptr) { if (var == nullptr) {
ReportUnidentifiableError(); ReportUnidentifiableError();
return; } else if (var->scope() != scope) {
}
if (var->scope() != scope) {
DCHECK_NE(kNoSourcePosition, position); DCHECK_NE(kNoSourcePosition, position);
DCHECK_EQ(VariableMode::kVar, mode); DCHECK_EQ(VariableMode::kVar, mode);
Declaration* nested_declaration = Declaration* nested_declaration =
...@@ -1119,6 +1117,7 @@ class PreParser : public ParserBase<PreParser> { ...@@ -1119,6 +1117,7 @@ class PreParser : public ParserBase<PreParser> {
nested_declaration->set_var(var); nested_declaration->set_var(var);
var->scope()->declarations()->Add(nested_declaration); var->scope()->declarations()->Add(nested_declaration);
} }
return var;
} }
V8_INLINE PreParserBlock RewriteCatchPattern(CatchInfo* catch_info) { V8_INLINE PreParserBlock RewriteCatchPattern(CatchInfo* catch_info) {
...@@ -1183,22 +1182,22 @@ class PreParser : public ParserBase<PreParser> { ...@@ -1183,22 +1182,22 @@ class PreParser : public ParserBase<PreParser> {
return PreParserStatement::Default(); return PreParserStatement::Default();
} }
V8_INLINE PreParserStatement V8_INLINE PreParserStatement DeclareFunction(
DeclareFunction(const PreParserIdentifier& variable_name, const PreParserIdentifier& variable_name,
const PreParserExpression& function, VariableMode mode, const PreParserExpression& function, VariableMode mode, VariableKind kind,
int beg_pos, int end_pos, bool is_sloppy_block_function, int beg_pos, int end_pos, ZonePtrList<const AstRawString>* names) {
ZonePtrList<const AstRawString>* names) {
DCHECK_NULL(names); DCHECK_NULL(names);
if (variable_name.string_ != nullptr) { if (variable_name.string_ != nullptr) {
bool was_added; bool was_added;
if (is_strict(language_mode())) { Variable* var = DeclareVariableName(variable_name.string_, mode, scope(),
DeclareVariableName(variable_name.string_, mode, scope(), &was_added); &was_added, beg_pos, kind);
} else { if (kind == SLOPPY_BLOCK_FUNCTION_VARIABLE) {
scope()->DeclareVariableName(variable_name.string_, mode, &was_added); Token::Value init =
} loop_nesting_depth() > 0 ? Token::ASSIGN : Token::INIT;
if (is_sloppy_block_function) { SloppyBlockFunctionStatement* statement =
GetDeclarationScope()->DeclareSloppyBlockFunction(variable_name.string_, factory()->ast_node_factory()->NewSloppyBlockFunctionStatement(
scope()); end_pos, var, init);
GetDeclarationScope()->DeclareSloppyBlockFunction(statement);
} }
} }
return Statement::Default(); return Statement::Default();
......
...@@ -2,11 +2,16 @@ ...@@ -2,11 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
assertThrows(`
{ {
function a() {} function a() {}
} }
{ {
// Duplicate lexical declarations are only allowed if they are both sloppy
// block functions (see bug 4693). In this case the sloppy block function
// conflicts with the lexical variable declaration, causing a syntax error.
let a; let a;
function a() {}; function a() {};
} }
`, 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