Commit bbcb33c7 authored by marja's avatar marja Committed by Commit bot

PreParser scope analysis: sloppy block funcs.

- Generalize the sloppy block function data structures to allow
  PreParser adding and hoisting sloppy block funcs.
- This completes PreParser scope analysis.

BUG=v8:5501, v8:5516
R=verwaest@chromium.org

Review-Url: https://codereview.chromium.org/2636543002
Cr-Commit-Position: refs/heads/master@{#42368}
parent 064c584a
...@@ -1185,22 +1185,15 @@ class SloppyBlockFunctionStatement final : public Statement { ...@@ -1185,22 +1185,15 @@ 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 scope_; }
SloppyBlockFunctionStatement* next() { return next_; }
void set_next(SloppyBlockFunctionStatement* next) { next_ = next; }
private: private:
friend class AstNodeFactory; friend class AstNodeFactory;
SloppyBlockFunctionStatement(Statement* statement, Scope* scope) explicit SloppyBlockFunctionStatement(Statement* statement)
: Statement(kNoSourcePosition, kSloppyBlockFunctionStatement), : Statement(kNoSourcePosition, kSloppyBlockFunctionStatement),
statement_(statement), statement_(statement) {}
scope_(scope),
next_(nullptr) {}
Statement* statement_; Statement* statement_;
Scope* const scope_;
SloppyBlockFunctionStatement* next_;
}; };
...@@ -3290,9 +3283,9 @@ class AstNodeFactory final BASE_EMBEDDED { ...@@ -3290,9 +3283,9 @@ class AstNodeFactory final BASE_EMBEDDED {
return new (zone_) EmptyStatement(pos); return new (zone_) EmptyStatement(pos);
} }
SloppyBlockFunctionStatement* NewSloppyBlockFunctionStatement(Scope* scope) { SloppyBlockFunctionStatement* NewSloppyBlockFunctionStatement() {
return new (zone_) SloppyBlockFunctionStatement( return new (zone_)
NewEmptyStatement(kNoSourcePosition), scope); SloppyBlockFunctionStatement(NewEmptyStatement(kNoSourcePosition));
} }
CaseClause* NewCaseClause( CaseClause* NewCaseClause(
......
...@@ -20,6 +20,14 @@ namespace internal { ...@@ -20,6 +20,14 @@ namespace internal {
namespace { namespace {
void* kDummyPreParserVariable = reinterpret_cast<void*>(0x1); void* kDummyPreParserVariable = reinterpret_cast<void*>(0x1);
void* kDummyPreParserLexicalVariable = reinterpret_cast<void*>(0x2);
bool IsLexical(Variable* variable) {
if (variable == kDummyPreParserLexicalVariable) return true;
if (variable == kDummyPreParserVariable) return false;
return IsLexicalVariableMode(variable->mode());
}
} // namespace } // namespace
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
...@@ -56,14 +64,16 @@ Variable* VariableMap::Declare(Zone* zone, Scope* scope, ...@@ -56,14 +64,16 @@ Variable* VariableMap::Declare(Zone* zone, Scope* scope,
return reinterpret_cast<Variable*>(p->value); return reinterpret_cast<Variable*>(p->value);
} }
void VariableMap::DeclareName(Zone* zone, const AstRawString* name) { void VariableMap::DeclareName(Zone* zone, const AstRawString* name,
VariableMode mode) {
Entry* p = Entry* p =
ZoneHashMap::LookupOrInsert(const_cast<AstRawString*>(name), name->hash(), ZoneHashMap::LookupOrInsert(const_cast<AstRawString*>(name), name->hash(),
ZoneAllocationPolicy(zone)); ZoneAllocationPolicy(zone));
if (p->value == nullptr) { if (p->value == nullptr) {
// The variable has not been declared yet -> insert it. // The variable has not been declared yet -> insert it.
DCHECK_EQ(name, p->key); DCHECK_EQ(name, p->key);
p->value = kDummyPreParserVariable; p->value =
mode == VAR ? kDummyPreParserVariable : kDummyPreParserLexicalVariable;
} }
} }
...@@ -92,21 +102,27 @@ Variable* VariableMap::Lookup(const AstRawString* name) { ...@@ -92,21 +102,27 @@ Variable* VariableMap::Lookup(const AstRawString* name) {
return NULL; return NULL;
} }
void SloppyBlockFunctionMap::Delegate::set_statement(Statement* statement) {
if (statement_ != nullptr) {
statement_->set_statement(statement);
}
}
SloppyBlockFunctionMap::SloppyBlockFunctionMap(Zone* zone) SloppyBlockFunctionMap::SloppyBlockFunctionMap(Zone* zone)
: ZoneHashMap(8, ZoneAllocationPolicy(zone)) {} : ZoneHashMap(8, ZoneAllocationPolicy(zone)) {}
void SloppyBlockFunctionMap::Declare(Zone* zone, const AstRawString* name, void SloppyBlockFunctionMap::Declare(
SloppyBlockFunctionStatement* stmt) { Zone* zone, const AstRawString* name,
SloppyBlockFunctionMap::Delegate* delegate) {
// AstRawStrings are unambiguous, i.e., the same string is always represented // AstRawStrings are unambiguous, i.e., the same string is always represented
// by the same AstRawString*. // by the same AstRawString*.
Entry* p = Entry* p =
ZoneHashMap::LookupOrInsert(const_cast<AstRawString*>(name), name->hash(), ZoneHashMap::LookupOrInsert(const_cast<AstRawString*>(name), name->hash(),
ZoneAllocationPolicy(zone)); ZoneAllocationPolicy(zone));
stmt->set_next(static_cast<SloppyBlockFunctionStatement*>(p->value)); delegate->set_next(static_cast<SloppyBlockFunctionMap::Delegate*>(p->value));
p->value = stmt; p->value = delegate;
} }
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Implementation of Scope // Implementation of Scope
...@@ -448,11 +464,21 @@ int Scope::num_parameters() const { ...@@ -448,11 +464,21 @@ int Scope::num_parameters() const {
return is_declaration_scope() ? AsDeclarationScope()->num_parameters() : 0; return is_declaration_scope() ? AsDeclarationScope()->num_parameters() : 0;
} }
void DeclarationScope::DeclareSloppyBlockFunction(
const AstRawString* name, Scope* scope,
SloppyBlockFunctionStatement* statement) {
auto* delegate =
new (zone()) SloppyBlockFunctionMap::Delegate(scope, statement);
sloppy_block_function_map_.Declare(zone(), name, delegate);
}
void DeclarationScope::HoistSloppyBlockFunctions(AstNodeFactory* factory) { void DeclarationScope::HoistSloppyBlockFunctions(AstNodeFactory* factory) {
DCHECK(is_sloppy(language_mode())); DCHECK(is_sloppy(language_mode()));
DCHECK(is_function_scope() || is_eval_scope() || is_script_scope() || DCHECK(is_function_scope() || is_eval_scope() || is_script_scope() ||
(is_block_scope() && outer_scope()->is_function_scope())); (is_block_scope() && outer_scope()->is_function_scope()));
DCHECK(HasSimpleParameters() || is_block_scope()); DCHECK(HasSimpleParameters() || is_block_scope() || is_being_lazily_parsed_);
DCHECK_EQ(factory == nullptr, is_being_lazily_parsed_);
bool has_simple_parameters = HasSimpleParameters(); bool has_simple_parameters = HasSimpleParameters();
// For each variable which is used as a function declaration in a sloppy // For each variable which is used as a function declaration in a sloppy
// block, // block,
...@@ -484,7 +510,7 @@ void DeclarationScope::HoistSloppyBlockFunctions(AstNodeFactory* factory) { ...@@ -484,7 +510,7 @@ void DeclarationScope::HoistSloppyBlockFunctions(AstNodeFactory* factory) {
bool var_created = false; bool var_created = false;
// Write in assignments to var for each block-scoped function declaration // Write in assignments to var for each block-scoped function declaration
auto delegates = static_cast<SloppyBlockFunctionStatement*>(p->value); auto delegates = static_cast<SloppyBlockFunctionMap::Delegate*>(p->value);
DeclarationScope* decl_scope = this; DeclarationScope* decl_scope = this;
while (decl_scope->is_eval_scope()) { while (decl_scope->is_eval_scope()) {
...@@ -492,7 +518,7 @@ void DeclarationScope::HoistSloppyBlockFunctions(AstNodeFactory* factory) { ...@@ -492,7 +518,7 @@ void DeclarationScope::HoistSloppyBlockFunctions(AstNodeFactory* factory) {
} }
Scope* outer_scope = decl_scope->outer_scope(); Scope* outer_scope = decl_scope->outer_scope();
for (SloppyBlockFunctionStatement* delegate = delegates; for (SloppyBlockFunctionMap::Delegate* delegate = delegates;
delegate != nullptr; delegate = delegate->next()) { delegate != nullptr; delegate = delegate->next()) {
// Check if there's a conflict with a lexical declaration // Check if there's a conflict with a lexical declaration
Scope* query_scope = delegate->scope()->outer_scope(); Scope* query_scope = delegate->scope()->outer_scope();
...@@ -506,7 +532,7 @@ void DeclarationScope::HoistSloppyBlockFunctions(AstNodeFactory* factory) { ...@@ -506,7 +532,7 @@ void DeclarationScope::HoistSloppyBlockFunctions(AstNodeFactory* factory) {
// `{ let e; try {} catch (e) { function e(){} } }` // `{ let e; try {} catch (e) { function e(){} } }`
do { do {
var = query_scope->LookupLocal(name); var = query_scope->LookupLocal(name);
if (var != nullptr && IsLexicalVariableMode(var->mode())) { if (var != nullptr && IsLexical(var)) {
should_hoist = false; should_hoist = false;
break; break;
} }
...@@ -518,25 +544,32 @@ void DeclarationScope::HoistSloppyBlockFunctions(AstNodeFactory* factory) { ...@@ -518,25 +544,32 @@ void DeclarationScope::HoistSloppyBlockFunctions(AstNodeFactory* factory) {
// Declare a var-style binding for the function in the outer scope // Declare a var-style binding for the function in the outer scope
if (!var_created) { if (!var_created) {
var_created = true; var_created = true;
VariableProxy* proxy = factory->NewVariableProxy(name, NORMAL_VARIABLE); if (factory) {
Declaration* declaration = VariableProxy* proxy =
factory->NewVariableDeclaration(proxy, this, kNoSourcePosition); factory->NewVariableProxy(name, NORMAL_VARIABLE);
// Based on the preceding check, it doesn't matter what we pass as auto declaration =
// allow_harmony_restrictive_generators and factory->NewVariableDeclaration(proxy, this, kNoSourcePosition);
// sloppy_mode_block_scope_function_redefinition. // Based on the preceding check, it doesn't matter what we pass as
bool ok = true; // allow_harmony_restrictive_generators and
DeclareVariable(declaration, VAR, // sloppy_mode_block_scope_function_redefinition.
Variable::DefaultInitializationFlag(VAR), false, bool ok = true;
nullptr, &ok); DeclareVariable(declaration, VAR,
CHECK(ok); // Based on the preceding check, this should not fail Variable::DefaultInitializationFlag(VAR), false,
nullptr, &ok);
CHECK(ok); // Based on the preceding check, this should not fail
} else {
DeclareVariableName(name, VAR);
}
} }
Expression* assignment = factory->NewAssignment( if (factory) {
Token::ASSIGN, NewUnresolved(factory, name), Expression* assignment = factory->NewAssignment(
delegate->scope()->NewUnresolved(factory, name), kNoSourcePosition); Token::ASSIGN, NewUnresolved(factory, name),
Statement* statement = delegate->scope()->NewUnresolved(factory, name), kNoSourcePosition);
factory->NewExpressionStatement(assignment, kNoSourcePosition); Statement* statement =
delegate->set_statement(statement); factory->NewExpressionStatement(assignment, kNoSourcePosition);
delegate->set_statement(statement);
}
} }
} }
} }
...@@ -1048,7 +1081,7 @@ void Scope::DeclareVariableName(const AstRawString* name, VariableMode mode) { ...@@ -1048,7 +1081,7 @@ void Scope::DeclareVariableName(const AstRawString* name, VariableMode mode) {
DCHECK(scope_info_.is_null()); DCHECK(scope_info_.is_null());
// Declare the variable in the declaration scope. // Declare the variable in the declaration scope.
variables_.DeclareName(zone(), name); variables_.DeclareName(zone(), name, mode);
} }
VariableProxy* Scope::NewUnresolved(AstNodeFactory* factory, VariableProxy* Scope::NewUnresolved(AstNodeFactory* factory,
...@@ -1647,7 +1680,7 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy, Scope* outer_scope_end) { ...@@ -1647,7 +1680,7 @@ Variable* Scope::LookupRecursive(VariableProxy* proxy, Scope* outer_scope_end) {
if (var == nullptr) return var; if (var == nullptr) return var;
// TODO(marja): Separate LookupRecursive for preparsed scopes better. // TODO(marja): Separate LookupRecursive for preparsed scopes better.
if (var == kDummyPreParserVariable) { if (var == kDummyPreParserVariable || var == kDummyPreParserLexicalVariable) {
DCHECK(GetDeclarationScope()->is_being_lazily_parsed()); DCHECK(GetDeclarationScope()->is_being_lazily_parsed());
DCHECK(FLAG_lazy_inner_functions); DCHECK(FLAG_lazy_inner_functions);
return var; return var;
...@@ -1845,7 +1878,8 @@ VariableProxy* Scope::FetchFreeVariables(DeclarationScope* max_outer_scope, ...@@ -1845,7 +1878,8 @@ VariableProxy* Scope::FetchFreeVariables(DeclarationScope* max_outer_scope,
if (var == nullptr) { if (var == nullptr) {
proxy->set_next_unresolved(stack); proxy->set_next_unresolved(stack);
stack = proxy; stack = proxy;
} else if (var != kDummyPreParserVariable) { } else if (var != kDummyPreParserVariable &&
var != kDummyPreParserLexicalVariable) {
if (info != nullptr) { if (info != nullptr) {
// In this case we need to leave scopes in a way that they can be // In this case we need to leave scopes in a way that they can be
// allocated. If we resolved variables from lazy parsed scopes, we need // allocated. If we resolved variables from lazy parsed scopes, we need
......
...@@ -21,6 +21,7 @@ class AstRawString; ...@@ -21,6 +21,7 @@ class AstRawString;
class Declaration; class Declaration;
class ParseInfo; class ParseInfo;
class SloppyBlockFunctionStatement; class SloppyBlockFunctionStatement;
class Statement;
class StringSet; class StringSet;
class VariableProxy; class VariableProxy;
...@@ -37,7 +38,7 @@ class VariableMap: public ZoneHashMap { ...@@ -37,7 +38,7 @@ class VariableMap: public ZoneHashMap {
// Records that "name" exists (if not recorded yet) but doesn't create a // Records that "name" exists (if not recorded yet) but doesn't create a
// Variable. Useful for preparsing. // Variable. Useful for preparsing.
void DeclareName(Zone* zone, const AstRawString* name); void DeclareName(Zone* zone, const AstRawString* name, VariableMode mode);
Variable* Lookup(const AstRawString* name); Variable* Lookup(const AstRawString* name);
void Remove(Variable* var); void Remove(Variable* var);
...@@ -48,9 +49,24 @@ class VariableMap: public ZoneHashMap { ...@@ -48,9 +49,24 @@ class VariableMap: public ZoneHashMap {
// Sloppy block-scoped function declarations to var-bind // Sloppy block-scoped function declarations to var-bind
class SloppyBlockFunctionMap : public ZoneHashMap { class SloppyBlockFunctionMap : public ZoneHashMap {
public: public:
class Delegate : public ZoneObject {
public:
explicit Delegate(Scope* scope,
SloppyBlockFunctionStatement* statement = nullptr)
: scope_(scope), statement_(statement), next_(nullptr) {}
void set_statement(Statement* statement);
void set_next(Delegate* next) { next_ = next; }
Delegate* next() const { return next_; }
Scope* scope() const { return scope_; }
private:
Scope* scope_;
SloppyBlockFunctionStatement* statement_;
Delegate* next_;
};
explicit SloppyBlockFunctionMap(Zone* zone); explicit SloppyBlockFunctionMap(Zone* zone);
void Declare(Zone* zone, const AstRawString* name, void Declare(Zone* zone, const AstRawString* name, Delegate* delegate);
SloppyBlockFunctionStatement* statement);
}; };
enum class AnalyzeMode { kRegular, kDebugger }; enum class AnalyzeMode { kRegular, kDebugger };
...@@ -747,10 +763,9 @@ class DeclarationScope : public Scope { ...@@ -747,10 +763,9 @@ class DeclarationScope : public Scope {
// initializers. // initializers.
void AddLocal(Variable* var); void AddLocal(Variable* var);
void DeclareSloppyBlockFunction(const AstRawString* name, void DeclareSloppyBlockFunction(
SloppyBlockFunctionStatement* statement) { const AstRawString* name, Scope* scope,
sloppy_block_function_map_.Declare(zone(), name, statement); SloppyBlockFunctionStatement* statement = nullptr);
}
// Go through sloppy_block_function_map_ and hoist those (into this scope) // Go through sloppy_block_function_map_ and hoist those (into this scope)
// which should be hoisted. // which should be hoisted.
......
...@@ -3780,8 +3780,23 @@ ParserBase<Impl>::ParseHoistableDeclaration( ...@@ -3780,8 +3780,23 @@ ParserBase<Impl>::ParseHoistableDeclaration(
pos, FunctionLiteral::kDeclaration, language_mode(), pos, FunctionLiteral::kDeclaration, language_mode(),
CHECK_OK_CUSTOM(NullStatement)); CHECK_OK_CUSTOM(NullStatement));
return impl()->DeclareFunction(variable_name, function, pos, is_generator, // In ES6, a function behaves as a lexical binding, except in
is_async, names, ok); // a script scope, or the initial scope of eval or another function.
VariableMode mode =
(!scope()->is_declaration_scope() || scope()->is_module_scope()) ? LET
: VAR;
// Async functions don't undergo sloppy mode block scoped hoisting, and don't
// allow duplicates in a block. Both are represented by the
// sloppy_block_function_map. Don't add them to the map for async functions.
// Generators are also supposed to be prohibited; currently doing this behind
// a flag and UseCounting violations to assess web compatibility.
bool is_sloppy_block_function =
is_sloppy(language_mode()) && !scope()->is_declaration_scope() &&
!is_async && !(allow_harmony_restrictive_generators() && is_generator);
return impl()->DeclareFunction(variable_name, function, mode, pos,
is_generator, is_async,
is_sloppy_block_function, names, ok);
} }
template <typename Impl> template <typename Impl>
......
...@@ -1472,15 +1472,11 @@ void Parser::DeclareAndInitializeVariables( ...@@ -1472,15 +1472,11 @@ void Parser::DeclareAndInitializeVariables(
} }
Statement* Parser::DeclareFunction(const AstRawString* variable_name, Statement* Parser::DeclareFunction(const AstRawString* variable_name,
FunctionLiteral* function, int pos, FunctionLiteral* function, VariableMode mode,
bool is_generator, bool is_async, int pos, bool is_generator, bool is_async,
bool is_sloppy_block_function,
ZoneList<const AstRawString*>* names, ZoneList<const AstRawString*>* names,
bool* ok) { bool* ok) {
// In ES6, a function behaves as a lexical binding, except in
// a script scope, or the initial scope of eval or another function.
VariableMode mode =
(!scope()->is_declaration_scope() || scope()->is_module_scope()) ? LET
: VAR;
VariableProxy* proxy = VariableProxy* proxy =
factory()->NewVariableProxy(variable_name, NORMAL_VARIABLE); factory()->NewVariableProxy(variable_name, NORMAL_VARIABLE);
Declaration* declaration = Declaration* declaration =
...@@ -1488,18 +1484,12 @@ Statement* Parser::DeclareFunction(const AstRawString* variable_name, ...@@ -1488,18 +1484,12 @@ Statement* Parser::DeclareFunction(const AstRawString* variable_name,
Declare(declaration, DeclarationDescriptor::NORMAL, mode, kCreatedInitialized, Declare(declaration, DeclarationDescriptor::NORMAL, mode, kCreatedInitialized,
CHECK_OK); CHECK_OK);
if (names) names->Add(variable_name, zone()); if (names) names->Add(variable_name, zone());
// Async functions don't undergo sloppy mode block scoped hoisting, and don't if (is_sloppy_block_function) {
// allow duplicates in a block. Both are represented by the SloppyBlockFunctionStatement* statement =
// sloppy_block_function_map. Don't add them to the map for async functions. factory()->NewSloppyBlockFunctionStatement();
// Generators are also supposed to be prohibited; currently doing this behind
// a flag and UseCounting violations to assess web compatibility.
if (is_sloppy(language_mode()) && !scope()->is_declaration_scope() &&
!is_async && !(allow_harmony_restrictive_generators() && is_generator)) {
SloppyBlockFunctionStatement* delegate =
factory()->NewSloppyBlockFunctionStatement(scope());
DeclarationScope* target_scope = GetDeclarationScope(); DeclarationScope* target_scope = GetDeclarationScope();
target_scope->DeclareSloppyBlockFunction(variable_name, delegate); target_scope->DeclareSloppyBlockFunction(variable_name, scope(), statement);
return delegate; return statement;
} }
return factory()->NewEmptyStatement(kNoSourcePosition); return factory()->NewEmptyStatement(kNoSourcePosition);
} }
......
...@@ -341,8 +341,9 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) { ...@@ -341,8 +341,9 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
const CatchInfo& catch_info, int pos); const CatchInfo& catch_info, int pos);
Statement* DeclareFunction(const AstRawString* variable_name, Statement* DeclareFunction(const AstRawString* variable_name,
FunctionLiteral* function, int pos, FunctionLiteral* function, VariableMode mode,
bool is_generator, bool is_async, int pos, bool is_generator, bool is_async,
bool is_sloppy_block_function,
ZoneList<const AstRawString*>* names, bool* ok); ZoneList<const AstRawString*>* names, bool* ok);
V8_INLINE Statement* DeclareClass(const AstRawString* variable_name, V8_INLINE Statement* DeclareClass(const AstRawString* variable_name,
Expression* value, Expression* value,
......
...@@ -144,6 +144,10 @@ PreParser::PreParseResult PreParser::PreParseFunction( ...@@ -144,6 +144,10 @@ PreParser::PreParseResult PreParser::PreParseFunction(
LazyParsingResult result = ParseStatementListAndLogFunction( LazyParsingResult result = ParseStatementListAndLogFunction(
&formals, has_duplicate_parameters, may_abort, ok); &formals, has_duplicate_parameters, may_abort, ok);
if (is_sloppy(function_scope->language_mode())) {
function_scope->HoistSloppyBlockFunctions(nullptr);
}
use_counts_ = nullptr; use_counts_ = nullptr;
track_unresolved_variables_ = false; track_unresolved_variables_ = false;
...@@ -232,6 +236,10 @@ PreParser::Expression PreParser::ParseFunctionLiteral( ...@@ -232,6 +236,10 @@ PreParser::Expression PreParser::ParseFunctionLiteral(
// Parsing the body may change the language mode in our scope. // Parsing the body may change the language mode in our scope.
language_mode = function_scope->language_mode(); language_mode = function_scope->language_mode();
if (is_sloppy(language_mode)) {
function_scope->HoistSloppyBlockFunctions(nullptr);
}
// Validate name and parameter names. We can do this only after parsing the // Validate name and parameter names. We can do this only after parsing the
// function, since the function can declare itself strict. // function, since the function can declare itself strict.
CheckFunctionName(language_mode, function_name, function_name_validity, CheckFunctionName(language_mode, function_name, function_name_validity,
......
...@@ -886,6 +886,9 @@ class PreParser : public ParserBase<PreParser> { ...@@ -886,6 +886,9 @@ class PreParser : public ParserBase<PreParser> {
bool is_module = false) { bool is_module = false) {
DCHECK_NULL(scope_state_); DCHECK_NULL(scope_state_);
DeclarationScope* scope = NewScriptScope(); DeclarationScope* scope = NewScriptScope();
#ifdef DEBUG
scope->set_is_being_lazily_parsed(true);
#endif
// ModuleDeclarationInstantiation for Source Text Module Records creates a // ModuleDeclarationInstantiation for Source Text Module Records creates a
// new Module Environment Record whose outer lexical environment record is // new Module Environment Record whose outer lexical environment record is
...@@ -1089,9 +1092,19 @@ class PreParser : public ParserBase<PreParser> { ...@@ -1089,9 +1092,19 @@ class PreParser : public ParserBase<PreParser> {
} }
V8_INLINE PreParserStatement DeclareFunction( V8_INLINE PreParserStatement DeclareFunction(
PreParserIdentifier variable_name, PreParserExpression function, int pos, PreParserIdentifier variable_name, PreParserExpression function,
bool is_generator, bool is_async, ZoneList<const AstRawString*>* names, VariableMode mode, int pos, bool is_generator, bool is_async,
bool is_sloppy_block_function, ZoneList<const AstRawString*>* names,
bool* ok) { bool* ok) {
DCHECK_NULL(names);
if (variable_name.string_ != nullptr) {
DCHECK(track_unresolved_variables_);
scope()->DeclareVariableName(variable_name.string_, mode);
if (is_sloppy_block_function) {
GetDeclarationScope()->DeclareSloppyBlockFunction(variable_name.string_,
scope());
}
}
return Statement::Default(); return Statement::Default();
} }
...@@ -1616,8 +1629,8 @@ PreParserStatementList PreParser::ParseEagerFunctionBody( ...@@ -1616,8 +1629,8 @@ PreParserStatementList PreParser::ParseEagerFunctionBody(
FunctionLiteral::FunctionType function_type, bool* ok) { FunctionLiteral::FunctionType function_type, bool* ok) {
PreParserStatementList result; PreParserStatementList result;
Scope* inner_scope = scope(); DeclarationScope* inner_scope = scope()->AsDeclarationScope();
if (!parameters.is_simple) inner_scope = NewScope(BLOCK_SCOPE); if (!parameters.is_simple) inner_scope = NewVarblockScope();
{ {
BlockState block_state(&scope_state_, inner_scope); BlockState block_state(&scope_state_, inner_scope);
...@@ -1626,6 +1639,10 @@ PreParserStatementList PreParser::ParseEagerFunctionBody( ...@@ -1626,6 +1639,10 @@ PreParserStatementList PreParser::ParseEagerFunctionBody(
} }
Expect(Token::RBRACE, ok); Expect(Token::RBRACE, ok);
if (is_sloppy(inner_scope->language_mode())) {
inner_scope->HoistSloppyBlockFunctions(nullptr);
}
return result; return result;
} }
......
...@@ -8615,6 +8615,21 @@ TEST(NoPessimisticContextAllocation) { ...@@ -8615,6 +8615,21 @@ TEST(NoPessimisticContextAllocation) {
{"function inner() { for (let my_var = 0; my_var < 1; ++my_var) { } " {"function inner() { for (let my_var = 0; my_var < 1; ++my_var) { } "
"my_var }", "my_var }",
true}, true},
{"function inner() { 'use strict'; if (true) { function my_var() {} } "
"my_var; }",
true},
{"function inner() { 'use strict'; function inner2() { if (true) { "
"function my_var() {} } my_var; } }",
true},
{"function inner() { function inner2() { 'use strict'; if (true) { "
"function my_var() {} } my_var; } }",
true},
{"function inner() { () => { 'use strict'; if (true) { function my_var() "
"{} } my_var; } }",
true},
{"function inner() { if (true) { let my_var; if (true) { function "
"my_var() {} } } my_var; }",
true},
// No pessimistic context allocation: // No pessimistic context allocation:
{"function inner() { var my_var; my_var; }", false}, {"function inner() { var my_var; my_var; }", false},
{"function inner() { var my_var; }", false}, {"function inner() { var my_var; }", false},
...@@ -8806,10 +8821,18 @@ TEST(NoPessimisticContextAllocation) { ...@@ -8806,10 +8821,18 @@ TEST(NoPessimisticContextAllocation) {
"my_var } }", "my_var } }",
false}, false},
{"function inner() { class my_var {}; my_var }", false}, {"function inner() { class my_var {}; my_var }", false},
// In the following cases we still context allocate pessimistically: {"function inner() { function my_var() {} my_var; }", false},
{"function inner() { function my_var() {} my_var; }", true},
{"function inner() { if (true) { function my_var() {} } my_var; }", {"function inner() { if (true) { function my_var() {} } my_var; }",
true}, false},
{"function inner() { function inner2() { if (true) { function my_var() "
"{} } my_var; } }",
false},
{"function inner() { () => { if (true) { function my_var() {} } my_var; "
"} }",
false},
{"function inner() { if (true) { var my_var; if (true) { function "
"my_var() {} } } my_var; }",
false},
}; };
for (unsigned i = 0; i < arraysize(inners); ++i) { for (unsigned i = 0; i < arraysize(inners); ++i) {
......
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