Parsing: Create the same scopes for non-simple params in PreParser & Parser.

Rationale:

- To do scope analysis based on PreParser, and use the result again when parsing
  later, PreParser and Parser need to produce the same Scopes and variable
  declarations in them.

- This is not the case for non-simple parameters: Parser creates an additional
  inner Scope where the declarations were, whereas PreParser does
  DeclareVariableName directly in the function Scope.

- So this CL fixes that by moving the Scope creation for non-simple parameters
  into ParserBase.

- As a side product (and a partial proof that this change makes sense),
  PreParser::ParseEagerFunctionBody is now gone.

BUG=v8:5516

Review-Url: https://codereview.chromium.org/2638333002
Cr-Commit-Position: refs/heads/master@{#42537}
This commit is contained in:
marja 2017-01-20 00:58:54 -08:00 committed by Commit bot
parent 8b8c8df05b
commit 3534091756
4 changed files with 226 additions and 202 deletions

View File

@ -101,6 +101,12 @@ struct FormalParametersBase {
// Used in functions where the return type is ExpressionT.
#define CHECK_OK CHECK_OK_CUSTOM(EmptyExpression)
#define CHECK_OK_VOID ok); \
if (!*ok) return; \
((void)0
#define DUMMY ) // to make indentation work
#undef DUMMY
// Common base class template shared between parser and pre-parser.
// The Impl parameter is the actual class of the parser/pre-parser,
// following the Curiously Recurring Template Pattern (CRTP).
@ -1224,6 +1230,12 @@ class ParserBase {
bool default_export, bool* ok);
StatementT ParseNativeDeclaration(bool* ok);
// Consumes the ending }.
void ParseFunctionBody(StatementListT result, IdentifierT function_name,
int pos, const FormalParametersT& parameters,
FunctionKind kind,
FunctionLiteral::FunctionType function_type, bool* ok);
// Under some circumstances, we allow preparsing to abort if the preparsed
// function is "long and trivial", and fully parse instead. Our current
// definition of "long and trivial" is:
@ -3901,6 +3913,105 @@ ParserBase<Impl>::ParseAsyncFunctionDeclaration(
return ParseHoistableDeclaration(pos, flags, names, default_export, ok);
}
template <typename Impl>
void ParserBase<Impl>::ParseFunctionBody(
typename ParserBase<Impl>::StatementListT result, IdentifierT function_name,
int pos, const FormalParametersT& parameters, FunctionKind kind,
FunctionLiteral::FunctionType function_type, bool* ok) {
static const int kFunctionNameAssignmentIndex = 0;
if (function_type == FunctionLiteral::kNamedExpression) {
DCHECK(!impl()->IsEmptyIdentifier(function_name));
// If we have a named function expression, we add a local variable
// declaration to the body of the function with the name of the
// function and let it refer to the function itself (closure).
// Not having parsed the function body, the language mode may still change,
// so we reserve a spot and create the actual const assignment later.
DCHECK_EQ(kFunctionNameAssignmentIndex, result->length());
result->Add(impl()->NullStatement(), zone());
}
DeclarationScope* function_scope = scope()->AsDeclarationScope();
DeclarationScope* inner_scope = function_scope;
BlockT inner_block = impl()->NullBlock();
StatementListT body = result;
if (!parameters.is_simple) {
inner_scope = NewVarblockScope();
inner_scope->set_start_position(scanner()->location().beg_pos);
inner_block = factory()->NewBlock(NULL, 8, true, kNoSourcePosition);
inner_block->set_scope(inner_scope);
body = inner_block->statements();
}
{
BlockState block_state(&scope_state_, inner_scope);
if (IsGeneratorFunction(kind)) {
impl()->ParseAndRewriteGeneratorFunctionBody(pos, kind, body, ok);
} else if (IsAsyncFunction(kind)) {
const bool accept_IN = true;
ParseAsyncFunctionBody(inner_scope, body, kind, FunctionBodyType::kNormal,
accept_IN, pos, CHECK_OK_VOID);
} else {
ParseStatementList(body, Token::RBRACE, CHECK_OK_VOID);
}
if (IsDerivedConstructor(kind)) {
body->Add(factory()->NewReturnStatement(impl()->ThisExpression(),
kNoSourcePosition),
zone());
}
}
Expect(Token::RBRACE, CHECK_OK_VOID);
scope()->set_end_position(scanner()->location().end_pos);
if (!parameters.is_simple) {
DCHECK_NOT_NULL(inner_scope);
DCHECK_EQ(function_scope, scope());
DCHECK_EQ(function_scope, inner_scope->outer_scope());
impl()->SetLanguageMode(function_scope, inner_scope->language_mode());
BlockT init_block =
impl()->BuildParameterInitializationBlock(parameters, CHECK_OK_VOID);
if (is_sloppy(inner_scope->language_mode())) {
impl()->InsertSloppyBlockFunctionVarBindings(inner_scope);
}
// TODO(littledan): Merge the two rejection blocks into one
if (IsAsyncFunction(kind)) {
init_block = impl()->BuildRejectPromiseOnException(init_block);
}
inner_scope->set_end_position(scanner()->location().end_pos);
if (inner_scope->FinalizeBlockScope() != nullptr) {
impl()->CheckConflictingVarDeclarations(inner_scope, CHECK_OK_VOID);
impl()->InsertShadowingVarBindingInitializers(inner_block);
}
inner_scope = nullptr;
result->Add(init_block, zone());
result->Add(inner_block, zone());
} else {
DCHECK_EQ(inner_scope, function_scope);
if (is_sloppy(function_scope->language_mode())) {
impl()->InsertSloppyBlockFunctionVarBindings(function_scope);
}
}
if (!IsArrowFunction(kind)) {
// Declare arguments after parsing the function since lexical 'arguments'
// masks the arguments object. Declare arguments before declaring the
// function var since the arguments object masks 'function arguments'.
function_scope->DeclareArguments(ast_value_factory());
}
impl()->CreateFunctionNameAssignment(function_name, pos, function_type,
function_scope, result,
kFunctionNameAssignmentIndex);
impl()->MarkCollectedTailCallExpressions();
}
template <typename Impl>
void ParserBase<Impl>::CheckArityRestrictions(int param_count,
FunctionKind function_kind,
@ -4064,9 +4175,11 @@ ParserBase<Impl>::ParseArrowFunctionLiteral(
}
if (!is_lazy_top_level_function) {
Consume(Token::LBRACE);
body = impl()->ParseEagerFunctionBody(
impl()->EmptyIdentifier(), kNoSourcePosition, formal_parameters,
kind, FunctionLiteral::kAnonymousExpression, CHECK_OK);
body = impl()->NewStatementList(8);
impl()->ParseFunctionBody(body, impl()->EmptyIdentifier(),
kNoSourcePosition, formal_parameters, kind,
FunctionLiteral::kAnonymousExpression,
CHECK_OK);
materialized_literal_count =
function_state.materialized_literal_count();
expected_property_count = function_state.expected_property_count();
@ -5563,6 +5676,7 @@ void ParserBase<Impl>::ClassLiteralChecker::CheckClassMethodName(
}
}
#undef CHECK_OK_VOID
} // namespace internal
} // namespace v8

View File

@ -1754,6 +1754,70 @@ Statement* Parser::RewriteTryStatement(Block* try_block, Block* catch_block,
}
}
void Parser::ParseAndRewriteGeneratorFunctionBody(int pos, FunctionKind kind,
ZoneList<Statement*>* body,
bool* ok) {
// We produce:
//
// try { InitialYield; ...body...; return {value: undefined, done: true} }
// finally { %_GeneratorClose(generator) }
//
// - InitialYield yields the actual generator object.
// - Any return statement inside the body will have its argument wrapped
// in a "done" iterator result object.
// - If the generator terminates for whatever reason, we must close it.
// Hence the finally clause.
Block* try_block = factory()->NewBlock(nullptr, 3, false, kNoSourcePosition);
Expression* initial_yield = BuildInitialYield(pos, kind);
try_block->statements()->Add(
factory()->NewExpressionStatement(initial_yield, kNoSourcePosition),
zone());
ParseStatementList(try_block->statements(), Token::RBRACE, ok);
if (!*ok) return;
Statement* final_return = factory()->NewReturnStatement(
BuildIteratorResult(nullptr, true), kNoSourcePosition);
try_block->statements()->Add(final_return, zone());
Block* finally_block =
factory()->NewBlock(nullptr, 1, false, kNoSourcePosition);
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone());
VariableProxy* call_proxy =
factory()->NewVariableProxy(function_state_->generator_object_variable());
args->Add(call_proxy, zone());
Expression* call = factory()->NewCallRuntime(Runtime::kInlineGeneratorClose,
args, kNoSourcePosition);
finally_block->statements()->Add(
factory()->NewExpressionStatement(call, kNoSourcePosition), zone());
body->Add(factory()->NewTryFinallyStatement(try_block, finally_block,
kNoSourcePosition),
zone());
}
void Parser::CreateFunctionNameAssignment(
const AstRawString* function_name, int pos,
FunctionLiteral::FunctionType function_type,
DeclarationScope* function_scope, ZoneList<Statement*>* result, int index) {
if (function_type == FunctionLiteral::kNamedExpression) {
StatementT statement = factory()->NewEmptyStatement(kNoSourcePosition);
if (function_scope->LookupLocal(function_name) == nullptr) {
// Now that we know the language mode, we can create the const assignment
// in the previously reserved spot.
DCHECK_EQ(function_scope, scope());
Variable* fvar = function_scope->DeclareFunctionVar(function_name);
VariableProxy* fproxy = factory()->NewVariableProxy(fvar);
statement = factory()->NewExpressionStatement(
factory()->NewAssignment(Token::INIT, fproxy,
factory()->NewThisFunction(pos),
kNoSourcePosition),
kNoSourcePosition);
}
result->Set(index, statement);
}
}
// !%_IsJSReceiver(result = iterator.next()) &&
// %ThrowIteratorResultNotAnObject(result)
Expression* Parser::BuildIteratorNextResult(Expression* iterator,
@ -2920,7 +2984,7 @@ Block* Parser::BuildParameterInitializationBlock(
return init_block;
}
Block* Parser::BuildRejectPromiseOnException(Block* inner_block, bool* ok) {
Block* Parser::BuildRejectPromiseOnException(Block* inner_block) {
// .promise = %AsyncFunctionPromiseCreate();
// try {
// <inner_block>
@ -3082,8 +3146,8 @@ ZoneList<Statement*>* Parser::ParseFunction(
CHECK_OK);
Expect(Token::LBRACE, CHECK_OK);
ZoneList<Statement*>* body = ParseEagerFunctionBody(
function_name, pos, formals, kind, function_type, ok);
ZoneList<Statement*>* body = new (zone()) ZoneList<Statement*>(8, zone());
ParseFunctionBody(body, function_name, pos, formals, kind, function_type, ok);
// Validate parameter names. We can do this only after parsing the function,
// since the function can declare itself strict.
@ -3103,161 +3167,6 @@ ZoneList<Statement*>* Parser::ParseFunction(
return body;
}
ZoneList<Statement*>* Parser::ParseEagerFunctionBody(
const AstRawString* function_name, int pos,
const ParserFormalParameters& parameters, FunctionKind kind,
FunctionLiteral::FunctionType function_type, bool* ok) {
ZoneList<Statement*>* result = new(zone()) ZoneList<Statement*>(8, zone());
static const int kFunctionNameAssignmentIndex = 0;
if (function_type == FunctionLiteral::kNamedExpression) {
DCHECK(function_name != NULL);
// If we have a named function expression, we add a local variable
// declaration to the body of the function with the name of the
// function and let it refer to the function itself (closure).
// Not having parsed the function body, the language mode may still change,
// so we reserve a spot and create the actual const assignment later.
DCHECK_EQ(kFunctionNameAssignmentIndex, result->length());
result->Add(NULL, zone());
}
ZoneList<Statement*>* body = result;
DeclarationScope* function_scope = scope()->AsDeclarationScope();
DeclarationScope* inner_scope = function_scope;
Block* inner_block = nullptr;
if (!parameters.is_simple) {
inner_scope = NewVarblockScope();
inner_scope->set_start_position(scanner()->location().beg_pos);
inner_block = factory()->NewBlock(NULL, 8, true, kNoSourcePosition);
inner_block->set_scope(inner_scope);
body = inner_block->statements();
}
{
BlockState block_state(&scope_state_, inner_scope);
if (IsGeneratorFunction(kind)) {
// We produce:
//
// try { InitialYield; ...body...; return {value: undefined, done: true} }
// finally { %_GeneratorClose(generator) }
//
// - InitialYield yields the actual generator object.
// - Any return statement inside the body will have its argument wrapped
// in a "done" iterator result object.
// - If the generator terminates for whatever reason, we must close it.
// Hence the finally clause.
Block* try_block =
factory()->NewBlock(nullptr, 3, false, kNoSourcePosition);
Expression* initial_yield = BuildInitialYield(pos, kind);
try_block->statements()->Add(
factory()->NewExpressionStatement(initial_yield, kNoSourcePosition),
zone());
ParseStatementList(try_block->statements(), Token::RBRACE, CHECK_OK);
Statement* final_return = factory()->NewReturnStatement(
BuildIteratorResult(nullptr, true), kNoSourcePosition);
try_block->statements()->Add(final_return, zone());
Block* finally_block =
factory()->NewBlock(nullptr, 1, false, kNoSourcePosition);
ZoneList<Expression*>* args =
new (zone()) ZoneList<Expression*>(1, zone());
VariableProxy* call_proxy = factory()->NewVariableProxy(
function_state_->generator_object_variable());
args->Add(call_proxy, zone());
Expression* call = factory()->NewCallRuntime(
Runtime::kInlineGeneratorClose, args, kNoSourcePosition);
finally_block->statements()->Add(
factory()->NewExpressionStatement(call, kNoSourcePosition), zone());
body->Add(factory()->NewTryFinallyStatement(try_block, finally_block,
kNoSourcePosition),
zone());
} else if (IsAsyncFunction(kind)) {
const bool accept_IN = true;
ParseAsyncFunctionBody(inner_scope, body, kind, FunctionBodyType::kNormal,
accept_IN, pos, CHECK_OK);
} else {
ParseStatementList(body, Token::RBRACE, CHECK_OK);
}
if (IsDerivedConstructor(kind)) {
body->Add(factory()->NewReturnStatement(ThisExpression(kNoSourcePosition),
kNoSourcePosition),
zone());
}
}
Expect(Token::RBRACE, CHECK_OK);
scope()->set_end_position(scanner()->location().end_pos);
if (!parameters.is_simple) {
DCHECK_NOT_NULL(inner_scope);
DCHECK_EQ(function_scope, scope());
DCHECK_EQ(function_scope, inner_scope->outer_scope());
DCHECK_EQ(body, inner_block->statements());
SetLanguageMode(function_scope, inner_scope->language_mode());
Block* init_block = BuildParameterInitializationBlock(parameters, CHECK_OK);
if (is_sloppy(inner_scope->language_mode())) {
InsertSloppyBlockFunctionVarBindings(inner_scope);
}
// TODO(littledan): Merge the two rejection blocks into one
if (IsAsyncFunction(kind)) {
init_block = BuildRejectPromiseOnException(init_block, CHECK_OK);
}
DCHECK_NOT_NULL(init_block);
inner_scope->set_end_position(scanner()->location().end_pos);
if (inner_scope->FinalizeBlockScope() != nullptr) {
CheckConflictingVarDeclarations(inner_scope, CHECK_OK);
InsertShadowingVarBindingInitializers(inner_block);
}
inner_scope = nullptr;
result->Add(init_block, zone());
result->Add(inner_block, zone());
} else {
DCHECK_EQ(inner_scope, function_scope);
if (is_sloppy(function_scope->language_mode())) {
InsertSloppyBlockFunctionVarBindings(function_scope);
}
}
if (!IsArrowFunction(kind)) {
// Declare arguments after parsing the function since lexical 'arguments'
// masks the arguments object. Declare arguments before declaring the
// function var since the arguments object masks 'function arguments'.
function_scope->DeclareArguments(ast_value_factory());
}
if (function_type == FunctionLiteral::kNamedExpression) {
Statement* statement;
if (function_scope->LookupLocal(function_name) == nullptr) {
// Now that we know the language mode, we can create the const assignment
// in the previously reserved spot.
DCHECK_EQ(function_scope, scope());
Variable* fvar = function_scope->DeclareFunctionVar(function_name);
VariableProxy* fproxy = factory()->NewVariableProxy(fvar);
statement = factory()->NewExpressionStatement(
factory()->NewAssignment(Token::INIT, fproxy,
factory()->NewThisFunction(pos),
kNoSourcePosition),
kNoSourcePosition);
} else {
statement = factory()->NewEmptyStatement(kNoSourcePosition);
}
result->Set(kFunctionNameAssignmentIndex, statement);
}
MarkCollectedTailCallExpressions();
return result;
}
void Parser::DeclareClassVariable(const AstRawString* name, Scope* block_scope,
ClassInfo* class_info, int class_token_pos,
bool* ok) {
@ -3880,7 +3789,7 @@ void Parser::RewriteAsyncFunctionBody(ZoneList<Statement*>* body, Block* block,
block->statements()->Add(
factory()->NewReturnStatement(return_value, return_value->position()),
zone());
block = BuildRejectPromiseOnException(block, CHECK_OK_VOID);
block = BuildRejectPromiseOnException(block);
body->Add(block, zone());
}

View File

@ -340,6 +340,14 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
Block* finally_block,
const CatchInfo& catch_info, int pos);
void ParseAndRewriteGeneratorFunctionBody(int pos, FunctionKind kind,
ZoneList<Statement*>* body,
bool* ok);
void CreateFunctionNameAssignment(const AstRawString* function_name, int pos,
FunctionLiteral::FunctionType function_type,
DeclarationScope* function_scope,
ZoneList<Statement*>* result, int index);
Statement* DeclareFunction(const AstRawString* variable_name,
FunctionLiteral* function, VariableMode mode,
int pos, bool is_generator, bool is_async,
@ -533,13 +541,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
Block* BuildParameterInitializationBlock(
const ParserFormalParameters& parameters, bool* ok);
Block* BuildRejectPromiseOnException(Block* block, bool* ok);
// Consumes the ending }.
ZoneList<Statement*>* ParseEagerFunctionBody(
const AstRawString* function_name, int pos,
const ParserFormalParameters& parameters, FunctionKind kind,
FunctionLiteral::FunctionType function_type, bool* ok);
Block* BuildRejectPromiseOnException(Block* block);
ZoneList<Statement*>* ParseFunction(
const AstRawString* function_name, int pos, FunctionKind kind,
@ -1025,8 +1027,7 @@ class V8_EXPORT_PRIVATE Parser : public NON_EXPORTED_BASE(ParserBase<Parser>) {
auto* init_block = BuildParameterInitializationBlock(parameters, ok);
if (!*ok) return;
if (is_async) {
init_block = BuildRejectPromiseOnException(init_block, ok);
if (!*ok) return;
init_block = BuildRejectPromiseOnException(init_block);
}
if (init_block != nullptr) body->Add(init_block, zone());
}

View File

@ -414,10 +414,11 @@ class PreParserList {
// These functions make list->Add(some_expression) work (and do nothing).
PreParserList() : length_(0), variables_(nullptr) {}
PreParserList* operator->() { return this; }
void Add(T, Zone* zone);
void Add(const T& element, Zone* zone);
int length() const { return length_; }
static PreParserList Null() { return PreParserList(-1); }
bool IsNull() const { return length_ == -1; }
void Set(int index, const T& element) {}
private:
explicit PreParserList(int n) : length_(n), variables_(nullptr) {}
@ -430,7 +431,7 @@ class PreParserList {
template <>
inline void PreParserList<PreParserExpression>::Add(
PreParserExpression expression, Zone* zone) {
const PreParserExpression& expression, Zone* zone) {
if (expression.variables_ != nullptr) {
DCHECK(FLAG_lazy_inner_functions);
DCHECK(zone != nullptr);
@ -445,7 +446,7 @@ inline void PreParserList<PreParserExpression>::Add(
}
template <typename T>
void PreParserList<T>::Add(T, Zone* zone) {
void PreParserList<T>::Add(const T& element, Zone* zone) {
++length_;
}
@ -939,11 +940,6 @@ class PreParser : public ParserBase<PreParser> {
// By making the 'exception handling' explicit, we are forced to check
// for failure at the call sites.
V8_INLINE PreParserStatementList ParseEagerFunctionBody(
PreParserIdentifier function_name, int pos,
const PreParserFormalParameters& parameters, FunctionKind kind,
FunctionLiteral::FunctionType function_type, bool* ok);
// Indicates that we won't switch from the preparser to the preparser; we'll
// just stay where we are.
bool AllowsLazyParsingWithoutUnresolvedVariables() const { return false; }
@ -1075,6 +1071,16 @@ class PreParser : public ParserBase<PreParser> {
return PreParserStatement::Default();
}
V8_INLINE void ParseAndRewriteGeneratorFunctionBody(
int pos, FunctionKind kind, PreParserStatementList body, bool* ok) {
ParseStatementList(body, Token::RBRACE, ok);
}
V8_INLINE void CreateFunctionNameAssignment(
PreParserIdentifier function_name, int pos,
FunctionLiteral::FunctionType function_type,
DeclarationScope* function_scope, PreParserStatementList result,
int index) {}
V8_INLINE PreParserExpression RewriteDoExpression(PreParserStatement body,
int pos, bool* ok) {
return PreParserExpression::Default();
@ -1328,6 +1334,23 @@ class PreParser : public ParserBase<PreParser> {
return loop;
}
V8_INLINE PreParserStatement BuildParameterInitializationBlock(
const PreParserFormalParameters& parameters, bool* ok) {
return PreParserStatement::Default();
}
V8_INLINE PreParserStatement
BuildRejectPromiseOnException(PreParserStatement init_block) {
return PreParserStatement::Default();
}
V8_INLINE void InsertSloppyBlockFunctionVarBindings(DeclarationScope* scope) {
scope->HoistSloppyBlockFunctions(nullptr);
}
V8_INLINE void InsertShadowingVarBindingInitializers(
PreParserStatement block) {}
V8_INLINE PreParserExpression
NewThrowReferenceError(MessageTemplate::Template message, int pos) {
return PreParserExpression::Default();
@ -1623,29 +1646,6 @@ PreParserExpression PreParser::SpreadCallNew(PreParserExpression function,
return factory()->NewCallNew(function, args, pos);
}
PreParserStatementList PreParser::ParseEagerFunctionBody(
PreParserIdentifier function_name, int pos,
const PreParserFormalParameters& parameters, FunctionKind kind,
FunctionLiteral::FunctionType function_type, bool* ok) {
PreParserStatementList result;
DeclarationScope* inner_scope = scope()->AsDeclarationScope();
if (!parameters.is_simple) inner_scope = NewVarblockScope();
{
BlockState block_state(&scope_state_, inner_scope);
ParseStatementList(result, Token::RBRACE, ok);
if (!*ok) return PreParserStatementList();
}
Expect(Token::RBRACE, ok);
if (is_sloppy(inner_scope->language_mode())) {
inner_scope->HoistSloppyBlockFunctions(nullptr);
}
return result;
}
PreParserExpression PreParser::CloseTemplateLiteral(TemplateLiteralState* state,
int start,
PreParserExpression tag) {