2012-01-26 21:47:57 +00:00
|
|
|
// Copyright 2012 the V8 project authors. All rights reserved.
|
2014-04-29 06:42:26 +00:00
|
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
|
|
// found in the LICENSE file.
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2015-11-26 16:22:34 +00:00
|
|
|
#include "src/parsing/parser.h"
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2016-07-25 10:24:45 +00:00
|
|
|
#include <memory>
|
|
|
|
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "src/api.h"
|
Add spread rewriting
In short, array literals containing spreads, when used as expressions,
are rewritten using do expressions. E.g.
[1, 2, 3, ...x, 4, ...y, 5]
is roughly rewritten as:
do {
$R = [1, 2, 3];
for ($i of x) %AppendElement($R, $i);
%AppendElement($R, 4);
for ($j of y) %AppendElement($R, $j);
%AppendElement($R, 5);
$R
}
where $R, $i and $j are fresh temporary variables.
R=rossberg@chromium.org
BUG=
Review URL: https://codereview.chromium.org/1564083002
Cr-Commit-Position: refs/heads/master@{#33307}
2016-01-14 17:49:54 +00:00
|
|
|
#include "src/ast/ast-expression-rewriter.h"
|
2015-11-26 16:22:34 +00:00
|
|
|
#include "src/ast/ast-literal-reindexer.h"
|
2016-07-25 08:25:49 +00:00
|
|
|
#include "src/ast/ast-traversal-visitor.h"
|
2016-08-22 11:33:30 +00:00
|
|
|
#include "src/ast/ast.h"
|
2014-09-24 07:08:27 +00:00
|
|
|
#include "src/bailout-reason.h"
|
2014-06-30 13:25:46 +00:00
|
|
|
#include "src/base/platform/platform.h"
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "src/char-predicates-inl.h"
|
|
|
|
#include "src/messages.h"
|
2016-08-25 11:58:07 +00:00
|
|
|
#include "src/parsing/duplicate-finder.h"
|
2015-11-26 16:22:34 +00:00
|
|
|
#include "src/parsing/parameter-initializer-rewriter.h"
|
2016-08-22 11:33:30 +00:00
|
|
|
#include "src/parsing/parse-info.h"
|
2015-11-26 16:22:34 +00:00
|
|
|
#include "src/parsing/rewriter.h"
|
|
|
|
#include "src/parsing/scanner-character-streams.h"
|
2014-09-25 07:16:15 +00:00
|
|
|
#include "src/runtime/runtime.h"
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "src/string-stream.h"
|
2016-02-18 06:12:45 +00:00
|
|
|
#include "src/tracing/trace-event.h"
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-05-25 10:05:56 +00:00
|
|
|
namespace v8 {
|
|
|
|
namespace internal {
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2015-03-09 14:51:13 +00:00
|
|
|
ScriptData::ScriptData(const byte* data, int length)
|
|
|
|
: owns_data_(false), rejected_(false), data_(data), length_(length) {
|
|
|
|
if (!IsAligned(reinterpret_cast<intptr_t>(data), kPointerAlignment)) {
|
|
|
|
byte* copy = NewArray<byte>(length);
|
|
|
|
DCHECK(IsAligned(reinterpret_cast<intptr_t>(copy), kPointerAlignment));
|
|
|
|
CopyBytes(copy, data, length);
|
|
|
|
data_ = copy;
|
|
|
|
AcquireDataOwnership();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-10 10:28:05 +00:00
|
|
|
FunctionEntry ParseData::GetFunctionEntry(int start) {
|
2010-08-27 08:26:29 +00:00
|
|
|
// The current pre-data entry must be a FunctionEntry with the given
|
|
|
|
// start position.
|
2014-07-10 10:28:05 +00:00
|
|
|
if ((function_index_ + FunctionEntry::kSize <= Length()) &&
|
|
|
|
(static_cast<int>(Data()[function_index_]) == start)) {
|
2010-09-07 12:52:16 +00:00
|
|
|
int index = function_index_;
|
|
|
|
function_index_ += FunctionEntry::kSize;
|
2014-07-10 10:28:05 +00:00
|
|
|
Vector<unsigned> subvector(&(Data()[index]), FunctionEntry::kSize);
|
|
|
|
return FunctionEntry(subvector);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
return FunctionEntry();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-10 10:28:05 +00:00
|
|
|
int ParseData::FunctionCount() {
|
|
|
|
int functions_size = FunctionsSize();
|
|
|
|
if (functions_size < 0) return 0;
|
|
|
|
if (functions_size % FunctionEntry::kSize != 0) return 0;
|
|
|
|
return functions_size / FunctionEntry::kSize;
|
2010-09-07 12:52:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-10 10:28:05 +00:00
|
|
|
bool ParseData::IsSane() {
|
2014-11-17 12:16:27 +00:00
|
|
|
if (!IsAligned(script_data_->length(), sizeof(unsigned))) return false;
|
2010-09-07 12:52:16 +00:00
|
|
|
// Check that the header data is valid and doesn't specify
|
|
|
|
// point to positions outside the store.
|
2014-07-10 10:28:05 +00:00
|
|
|
int data_length = Length();
|
|
|
|
if (data_length < PreparseDataConstants::kHeaderSize) return false;
|
|
|
|
if (Magic() != PreparseDataConstants::kMagicNumber) return false;
|
|
|
|
if (Version() != PreparseDataConstants::kCurrentVersion) return false;
|
|
|
|
if (HasError()) return false;
|
2010-09-07 12:52:16 +00:00
|
|
|
// Check that the space allocated for function entries is sane.
|
2014-07-10 10:28:05 +00:00
|
|
|
int functions_size = FunctionsSize();
|
2010-09-07 12:52:16 +00:00
|
|
|
if (functions_size < 0) return false;
|
|
|
|
if (functions_size % FunctionEntry::kSize != 0) return false;
|
2010-09-15 10:54:35 +00:00
|
|
|
// Check that the total size has room for header and function entries.
|
2010-09-07 12:52:16 +00:00
|
|
|
int minimum_size =
|
2010-11-23 11:46:36 +00:00
|
|
|
PreparseDataConstants::kHeaderSize + functions_size;
|
2014-07-10 10:28:05 +00:00
|
|
|
if (data_length < minimum_size) return false;
|
2008-07-03 15:10:15 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-10 10:28:05 +00:00
|
|
|
void ParseData::Initialize() {
|
|
|
|
// Prepares state for use.
|
|
|
|
int data_length = Length();
|
|
|
|
if (data_length >= PreparseDataConstants::kHeaderSize) {
|
|
|
|
function_index_ = PreparseDataConstants::kHeaderSize;
|
2010-08-25 06:46:53 +00:00
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-10 10:28:05 +00:00
|
|
|
bool ParseData::HasError() {
|
|
|
|
return Data()[PreparseDataConstants::kHasErrorOffset];
|
2014-04-01 14:17:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-10 10:28:05 +00:00
|
|
|
unsigned ParseData::Magic() {
|
|
|
|
return Data()[PreparseDataConstants::kMagicOffset];
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-10 10:28:05 +00:00
|
|
|
unsigned ParseData::Version() {
|
|
|
|
return Data()[PreparseDataConstants::kVersionOffset];
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-07-10 10:28:05 +00:00
|
|
|
int ParseData::FunctionsSize() {
|
|
|
|
return static_cast<int>(Data()[PreparseDataConstants::kFunctionsSizeOffset]);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2016-07-19 11:13:27 +00:00
|
|
|
// Helper for putting parts of the parse results into a temporary zone when
|
|
|
|
// parsing inner function bodies.
|
|
|
|
class DiscardableZoneScope {
|
|
|
|
public:
|
|
|
|
DiscardableZoneScope(Parser* parser, Zone* temp_zone, bool use_temp_zone)
|
|
|
|
: ast_node_factory_scope_(parser->factory(), temp_zone, use_temp_zone),
|
|
|
|
fni_(parser->ast_value_factory_, temp_zone),
|
|
|
|
parser_(parser),
|
2016-08-04 19:15:18 +00:00
|
|
|
prev_fni_(parser->fni_),
|
|
|
|
prev_zone_(parser->zone_) {
|
2016-07-19 11:13:27 +00:00
|
|
|
if (use_temp_zone) {
|
|
|
|
parser_->fni_ = &fni_;
|
2016-08-04 19:15:18 +00:00
|
|
|
parser_->zone_ = temp_zone;
|
2016-09-27 09:48:17 +00:00
|
|
|
if (parser_->reusable_preparser_ != nullptr) {
|
|
|
|
parser_->reusable_preparser_->zone_ = temp_zone;
|
2016-10-10 09:22:22 +00:00
|
|
|
parser_->reusable_preparser_->factory()->set_zone(temp_zone);
|
2016-09-27 09:48:17 +00:00
|
|
|
}
|
2016-07-19 11:13:27 +00:00
|
|
|
}
|
|
|
|
}
|
2016-09-28 15:58:22 +00:00
|
|
|
void Reset() {
|
2016-08-04 19:15:18 +00:00
|
|
|
parser_->fni_ = prev_fni_;
|
|
|
|
parser_->zone_ = prev_zone_;
|
2016-09-27 09:48:17 +00:00
|
|
|
if (parser_->reusable_preparser_ != nullptr) {
|
|
|
|
parser_->reusable_preparser_->zone_ = prev_zone_;
|
2016-10-10 09:22:22 +00:00
|
|
|
parser_->reusable_preparser_->factory()->set_zone(prev_zone_);
|
2016-09-27 09:48:17 +00:00
|
|
|
}
|
2016-09-28 15:58:22 +00:00
|
|
|
ast_node_factory_scope_.Reset();
|
2016-08-04 19:15:18 +00:00
|
|
|
}
|
2016-09-28 15:58:22 +00:00
|
|
|
~DiscardableZoneScope() { Reset(); }
|
2016-07-19 11:13:27 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
AstNodeFactory::BodyScope ast_node_factory_scope_;
|
|
|
|
FuncNameInferrer fni_;
|
|
|
|
Parser* parser_;
|
|
|
|
FuncNameInferrer* prev_fni_;
|
2016-08-04 19:15:18 +00:00
|
|
|
Zone* prev_zone_;
|
|
|
|
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(DiscardableZoneScope);
|
2016-07-19 11:13:27 +00:00
|
|
|
};
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2015-03-09 14:51:13 +00:00
|
|
|
void Parser::SetCachedData(ParseInfo* info) {
|
2016-10-13 14:01:44 +00:00
|
|
|
DCHECK_NULL(cached_parse_data_);
|
|
|
|
if (consume_cached_parse_data()) {
|
|
|
|
cached_parse_data_ = ParseData::FromCachedData(*info->cached_data());
|
|
|
|
if (cached_parse_data_ == nullptr) {
|
|
|
|
compile_options_ = ScriptCompiler::kNoCompileOptions;
|
2014-07-10 10:28:05 +00:00
|
|
|
}
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2016-09-16 00:46:00 +00:00
|
|
|
Expression* Parser::CallClassFieldInitializer(Scope* scope,
|
|
|
|
Expression* this_expr) {
|
|
|
|
// This produces the expression
|
|
|
|
// `.class_field_intializer(this_expr)`, where '.class_field_intializer' is
|
|
|
|
// the name
|
|
|
|
// of a synthetic variable.
|
|
|
|
// 'this_expr' will be 'this' in a base constructor and the result of calling
|
|
|
|
// 'super' in a derived one.
|
|
|
|
const AstRawString* init_fn_name =
|
|
|
|
ast_value_factory()->dot_class_field_init_string();
|
|
|
|
VariableProxy* init_fn_proxy = scope->NewUnresolved(factory(), init_fn_name);
|
|
|
|
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
|
|
|
|
args->Add(init_fn_proxy, zone());
|
|
|
|
args->Add(this_expr, zone());
|
|
|
|
return factory()->NewCallRuntime(Runtime::kInlineCall, args,
|
|
|
|
kNoSourcePosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
Expression* Parser::RewriteSuperCall(Expression* super_call) {
|
|
|
|
// TODO(bakkot) find a way to avoid this for classes without fields.
|
|
|
|
if (!allow_harmony_class_fields()) {
|
|
|
|
return super_call;
|
|
|
|
}
|
|
|
|
// This turns a super call `super()` into a do expression of the form
|
|
|
|
// do {
|
|
|
|
// tmp x = super();
|
|
|
|
// if (.class-field-init)
|
|
|
|
// .class-field-init(x)
|
|
|
|
// x; // This isn't actually present; our do-expression representation
|
|
|
|
// allows specifying that the expression returns x directly.
|
|
|
|
// }
|
|
|
|
Variable* var_tmp =
|
|
|
|
scope()->NewTemporary(ast_value_factory()->empty_string());
|
|
|
|
Block* block = factory()->NewBlock(nullptr, 1, false, kNoSourcePosition);
|
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, factory()->NewVariableProxy(var_tmp), super_call,
|
|
|
|
kNoSourcePosition);
|
|
|
|
block->statements()->Add(
|
|
|
|
factory()->NewExpressionStatement(assignment, kNoSourcePosition), zone());
|
|
|
|
const AstRawString* init_fn_name =
|
|
|
|
ast_value_factory()->dot_class_field_init_string();
|
|
|
|
VariableProxy* init_fn_proxy =
|
|
|
|
scope()->NewUnresolved(factory(), init_fn_name);
|
|
|
|
Expression* condition = init_fn_proxy;
|
|
|
|
Statement* initialize = factory()->NewExpressionStatement(
|
|
|
|
CallClassFieldInitializer(scope(), factory()->NewVariableProxy(var_tmp)),
|
|
|
|
kNoSourcePosition);
|
|
|
|
IfStatement* if_statement = factory()->NewIfStatement(
|
|
|
|
condition, initialize, factory()->NewEmptyStatement(kNoSourcePosition),
|
|
|
|
kNoSourcePosition);
|
|
|
|
block->statements()->Add(if_statement, zone());
|
|
|
|
return factory()->NewDoExpression(block, var_tmp, kNoSourcePosition);
|
|
|
|
}
|
|
|
|
|
2016-02-01 17:44:23 +00:00
|
|
|
FunctionLiteral* Parser::DefaultConstructor(const AstRawString* name,
|
2016-09-16 00:42:30 +00:00
|
|
|
bool call_super,
|
|
|
|
bool requires_class_field_init,
|
|
|
|
int pos, int end_pos,
|
2015-07-15 09:14:49 +00:00
|
|
|
LanguageMode language_mode) {
|
2014-11-07 16:39:00 +00:00
|
|
|
int materialized_literal_count = -1;
|
|
|
|
int expected_property_count = -1;
|
2016-10-13 12:34:37 +00:00
|
|
|
const int parameter_count = 0;
|
2016-02-01 17:44:23 +00:00
|
|
|
if (name == nullptr) name = ast_value_factory()->empty_string();
|
2015-02-11 17:22:50 +00:00
|
|
|
|
2015-02-12 20:06:52 +00:00
|
|
|
FunctionKind kind = call_super ? FunctionKind::kDefaultSubclassConstructor
|
|
|
|
: FunctionKind::kDefaultBaseConstructor;
|
2016-08-05 14:30:54 +00:00
|
|
|
DeclarationScope* function_scope = NewFunctionScope(kind);
|
2015-11-05 19:52:36 +00:00
|
|
|
SetLanguageMode(function_scope,
|
|
|
|
static_cast<LanguageMode>(language_mode | STRICT));
|
2014-11-07 16:39:00 +00:00
|
|
|
// Set start and end position to the same value
|
|
|
|
function_scope->set_start_position(pos);
|
|
|
|
function_scope->set_end_position(pos);
|
|
|
|
ZoneList<Statement*>* body = NULL;
|
|
|
|
|
|
|
|
{
|
2016-07-19 10:06:38 +00:00
|
|
|
FunctionState function_state(&function_state_, &scope_state_,
|
2016-09-27 11:41:00 +00:00
|
|
|
function_scope);
|
2014-11-07 16:39:00 +00:00
|
|
|
|
2015-02-11 17:22:50 +00:00
|
|
|
body = new (zone()) ZoneList<Statement*>(call_super ? 2 : 1, zone());
|
2014-11-07 16:39:00 +00:00
|
|
|
if (call_super) {
|
2015-12-11 16:38:44 +00:00
|
|
|
// $super_constructor = %_GetSuperConstructor(<this-function>)
|
2016-06-13 18:07:18 +00:00
|
|
|
// %reflect_construct(
|
|
|
|
// $super_constructor, InternalArray(...args), new.target)
|
|
|
|
auto constructor_args_name = ast_value_factory()->empty_string();
|
|
|
|
bool is_duplicate;
|
|
|
|
bool is_rest = true;
|
|
|
|
bool is_optional = false;
|
2016-07-20 08:57:53 +00:00
|
|
|
Variable* constructor_args = function_scope->DeclareParameter(
|
|
|
|
constructor_args_name, TEMPORARY, is_optional, is_rest, &is_duplicate,
|
|
|
|
ast_value_factory());
|
2016-06-13 18:07:18 +00:00
|
|
|
|
2014-11-07 16:39:00 +00:00
|
|
|
ZoneList<Expression*>* args =
|
2015-06-02 22:04:25 +00:00
|
|
|
new (zone()) ZoneList<Expression*>(2, zone());
|
2016-08-11 12:04:02 +00:00
|
|
|
VariableProxy* this_function_proxy =
|
|
|
|
NewUnresolved(ast_value_factory()->this_function_string(), pos);
|
2015-09-09 17:45:43 +00:00
|
|
|
ZoneList<Expression*>* tmp =
|
|
|
|
new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
tmp->Add(this_function_proxy, zone());
|
2015-12-11 15:48:48 +00:00
|
|
|
Expression* super_constructor = factory()->NewCallRuntime(
|
|
|
|
Runtime::kInlineGetSuperConstructor, tmp, pos);
|
|
|
|
args->Add(super_constructor, zone());
|
2016-06-13 18:07:18 +00:00
|
|
|
Spread* spread_args = factory()->NewSpread(
|
|
|
|
factory()->NewVariableProxy(constructor_args), pos, pos);
|
|
|
|
ZoneList<Expression*>* spread_args_expr =
|
|
|
|
new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
spread_args_expr->Add(spread_args, zone());
|
|
|
|
args->AddAll(*PrepareSpreadArguments(spread_args_expr), zone());
|
2016-08-11 12:04:02 +00:00
|
|
|
VariableProxy* new_target_proxy =
|
|
|
|
NewUnresolved(ast_value_factory()->new_target_string(), pos);
|
2015-12-11 16:38:44 +00:00
|
|
|
args->Add(new_target_proxy, zone());
|
2016-09-16 00:46:00 +00:00
|
|
|
Expression* call = factory()->NewCallRuntime(
|
2015-12-11 16:38:44 +00:00
|
|
|
Context::REFLECT_CONSTRUCT_INDEX, args, pos);
|
2016-09-16 00:46:00 +00:00
|
|
|
if (requires_class_field_init) {
|
|
|
|
call = CallClassFieldInitializer(scope(), call);
|
|
|
|
}
|
2015-02-12 20:06:52 +00:00
|
|
|
body->Add(factory()->NewReturnStatement(call, pos), zone());
|
2014-11-07 16:39:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
materialized_literal_count = function_state.materialized_literal_count();
|
|
|
|
expected_property_count = function_state.expected_property_count();
|
|
|
|
}
|
|
|
|
|
|
|
|
FunctionLiteral* function_literal = factory()->NewFunctionLiteral(
|
2016-01-08 20:38:07 +00:00
|
|
|
name, function_scope, body, materialized_literal_count,
|
2016-10-13 12:34:37 +00:00
|
|
|
expected_property_count, parameter_count, parameter_count,
|
2015-06-08 18:19:32 +00:00
|
|
|
FunctionLiteral::kNoDuplicateParameters,
|
2016-10-07 09:12:48 +00:00
|
|
|
FunctionLiteral::kAnonymousExpression, default_eager_compile_hint(), pos);
|
2014-11-07 16:39:00 +00:00
|
|
|
|
2016-09-16 00:42:30 +00:00
|
|
|
function_literal->set_requires_class_field_init(requires_class_field_init);
|
|
|
|
|
2014-11-07 16:39:00 +00:00
|
|
|
return function_literal;
|
|
|
|
}
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// The CHECK_OK macro is a convenient macro to enforce error
|
|
|
|
// handling for functions that may fail (by returning !*ok).
|
|
|
|
//
|
|
|
|
// CAUTION: This macro appends extra statements after a call,
|
|
|
|
// thus it must never be used where only a single statement
|
|
|
|
// is correct (e.g. an if statement branch w/o braces)!
|
|
|
|
|
2016-09-01 10:22:54 +00:00
|
|
|
#define CHECK_OK_VALUE(x) ok); \
|
|
|
|
if (!*ok) return x; \
|
2008-07-03 15:10:15 +00:00
|
|
|
((void)0
|
|
|
|
#define DUMMY ) // to make indentation work
|
|
|
|
#undef DUMMY
|
|
|
|
|
2016-09-01 10:22:54 +00:00
|
|
|
#define CHECK_OK CHECK_OK_VALUE(nullptr)
|
|
|
|
#define CHECK_OK_VOID CHECK_OK_VALUE(this->Void())
|
2016-07-19 08:03:43 +00:00
|
|
|
|
|
|
|
#define CHECK_FAILED /**/); \
|
2016-07-19 19:45:50 +00:00
|
|
|
if (failed_) return nullptr; \
|
2008-12-01 15:32:20 +00:00
|
|
|
((void)0
|
|
|
|
#define DUMMY ) // to make indentation work
|
|
|
|
#undef DUMMY
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Implementation of Parser
|
|
|
|
|
2016-08-24 10:08:34 +00:00
|
|
|
bool Parser::ShortcutNumericLiteralBinaryExpression(Expression** x,
|
|
|
|
Expression* y,
|
|
|
|
Token::Value op, int pos) {
|
2014-06-24 14:03:24 +00:00
|
|
|
if ((*x)->AsLiteral() && (*x)->AsLiteral()->raw_value()->IsNumber() &&
|
|
|
|
y->AsLiteral() && y->AsLiteral()->raw_value()->IsNumber()) {
|
|
|
|
double x_val = (*x)->AsLiteral()->raw_value()->AsNumber();
|
|
|
|
double y_val = y->AsLiteral()->raw_value()->AsNumber();
|
2015-12-03 18:14:08 +00:00
|
|
|
bool x_has_dot = (*x)->AsLiteral()->raw_value()->ContainsDot();
|
|
|
|
bool y_has_dot = y->AsLiteral()->raw_value()->ContainsDot();
|
|
|
|
bool has_dot = x_has_dot || y_has_dot;
|
2014-03-17 13:54:42 +00:00
|
|
|
switch (op) {
|
|
|
|
case Token::ADD:
|
2016-08-24 10:08:34 +00:00
|
|
|
*x = factory()->NewNumberLiteral(x_val + y_val, pos, has_dot);
|
2014-03-17 13:54:42 +00:00
|
|
|
return true;
|
|
|
|
case Token::SUB:
|
2016-08-24 10:08:34 +00:00
|
|
|
*x = factory()->NewNumberLiteral(x_val - y_val, pos, has_dot);
|
2014-03-17 13:54:42 +00:00
|
|
|
return true;
|
|
|
|
case Token::MUL:
|
2016-08-24 10:08:34 +00:00
|
|
|
*x = factory()->NewNumberLiteral(x_val * y_val, pos, has_dot);
|
2014-03-17 13:54:42 +00:00
|
|
|
return true;
|
|
|
|
case Token::DIV:
|
2016-08-24 10:08:34 +00:00
|
|
|
*x = factory()->NewNumberLiteral(x_val / y_val, pos, has_dot);
|
2014-03-17 13:54:42 +00:00
|
|
|
return true;
|
|
|
|
case Token::BIT_OR: {
|
|
|
|
int value = DoubleToInt32(x_val) | DoubleToInt32(y_val);
|
2016-08-24 10:08:34 +00:00
|
|
|
*x = factory()->NewNumberLiteral(value, pos, has_dot);
|
2014-03-17 13:54:42 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case Token::BIT_AND: {
|
|
|
|
int value = DoubleToInt32(x_val) & DoubleToInt32(y_val);
|
2016-08-24 10:08:34 +00:00
|
|
|
*x = factory()->NewNumberLiteral(value, pos, has_dot);
|
2014-03-17 13:54:42 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case Token::BIT_XOR: {
|
|
|
|
int value = DoubleToInt32(x_val) ^ DoubleToInt32(y_val);
|
2016-08-24 10:08:34 +00:00
|
|
|
*x = factory()->NewNumberLiteral(value, pos, has_dot);
|
2014-03-17 13:54:42 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case Token::SHL: {
|
|
|
|
int value = DoubleToInt32(x_val) << (DoubleToInt32(y_val) & 0x1f);
|
2016-08-24 10:08:34 +00:00
|
|
|
*x = factory()->NewNumberLiteral(value, pos, has_dot);
|
2014-03-17 13:54:42 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case Token::SHR: {
|
|
|
|
uint32_t shift = DoubleToInt32(y_val) & 0x1f;
|
|
|
|
uint32_t value = DoubleToUint32(x_val) >> shift;
|
2016-08-24 10:08:34 +00:00
|
|
|
*x = factory()->NewNumberLiteral(value, pos, has_dot);
|
2014-03-17 13:54:42 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
case Token::SAR: {
|
|
|
|
uint32_t shift = DoubleToInt32(y_val) & 0x1f;
|
|
|
|
int value = ArithmeticShiftRight(DoubleToInt32(x_val), shift);
|
2016-08-24 10:08:34 +00:00
|
|
|
*x = factory()->NewNumberLiteral(value, pos, has_dot);
|
2014-03-17 13:54:42 +00:00
|
|
|
return true;
|
|
|
|
}
|
2016-03-18 13:53:52 +00:00
|
|
|
case Token::EXP: {
|
2016-04-25 19:33:09 +00:00
|
|
|
double value = Pow(x_val, y_val);
|
2016-03-18 13:53:52 +00:00
|
|
|
int int_value = static_cast<int>(value);
|
2016-08-24 10:08:34 +00:00
|
|
|
*x = factory()->NewNumberLiteral(
|
2016-03-18 13:53:52 +00:00
|
|
|
int_value == value && value != -0.0 ? int_value : value, pos,
|
|
|
|
has_dot);
|
|
|
|
return true;
|
|
|
|
}
|
2014-03-17 13:54:42 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-08-24 10:22:12 +00:00
|
|
|
Expression* Parser::BuildUnaryExpression(Expression* expression,
|
|
|
|
Token::Value op, int pos) {
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(expression != NULL);
|
2014-06-03 07:40:43 +00:00
|
|
|
if (expression->IsLiteral()) {
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstValue* literal = expression->AsLiteral()->raw_value();
|
2014-03-19 14:08:47 +00:00
|
|
|
if (op == Token::NOT) {
|
|
|
|
// Convert the literal to a boolean condition and negate it.
|
|
|
|
bool condition = literal->BooleanValue();
|
2016-08-24 10:22:12 +00:00
|
|
|
return factory()->NewBooleanLiteral(!condition, pos);
|
2014-03-19 14:08:47 +00:00
|
|
|
} else if (literal->IsNumber()) {
|
|
|
|
// Compute some expressions involving only number literals.
|
2014-06-24 14:03:24 +00:00
|
|
|
double value = literal->AsNumber();
|
2015-12-03 18:14:08 +00:00
|
|
|
bool has_dot = literal->ContainsDot();
|
2014-03-19 14:08:47 +00:00
|
|
|
switch (op) {
|
|
|
|
case Token::ADD:
|
|
|
|
return expression;
|
|
|
|
case Token::SUB:
|
2016-08-24 10:22:12 +00:00
|
|
|
return factory()->NewNumberLiteral(-value, pos, has_dot);
|
2014-03-19 14:08:47 +00:00
|
|
|
case Token::BIT_NOT:
|
2016-08-24 10:22:12 +00:00
|
|
|
return factory()->NewNumberLiteral(~DoubleToInt32(value), pos,
|
|
|
|
has_dot);
|
2014-03-19 14:08:47 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Desugar '+foo' => 'foo*1'
|
|
|
|
if (op == Token::ADD) {
|
2016-08-24 10:22:12 +00:00
|
|
|
return factory()->NewBinaryOperation(
|
|
|
|
Token::MUL, expression, factory()->NewNumberLiteral(1, pos, true), pos);
|
2014-03-19 14:08:47 +00:00
|
|
|
}
|
|
|
|
// The same idea for '-foo' => 'foo*(-1)'.
|
|
|
|
if (op == Token::SUB) {
|
2016-08-24 10:22:12 +00:00
|
|
|
return factory()->NewBinaryOperation(
|
|
|
|
Token::MUL, expression, factory()->NewNumberLiteral(-1, pos), pos);
|
2014-03-19 14:08:47 +00:00
|
|
|
}
|
|
|
|
// ...and one more time for '~foo' => 'foo^(~0)'.
|
|
|
|
if (op == Token::BIT_NOT) {
|
2016-08-24 10:22:12 +00:00
|
|
|
return factory()->NewBinaryOperation(
|
|
|
|
Token::BIT_XOR, expression, factory()->NewNumberLiteral(~0, pos), pos);
|
2014-03-19 14:08:47 +00:00
|
|
|
}
|
2016-08-24 10:22:12 +00:00
|
|
|
return factory()->NewUnaryOperation(op, expression, pos);
|
2014-03-19 14:08:47 +00:00
|
|
|
}
|
|
|
|
|
2016-08-24 10:22:12 +00:00
|
|
|
Expression* Parser::BuildIteratorResult(Expression* value, bool done) {
|
2016-06-30 09:26:30 +00:00
|
|
|
int pos = kNoSourcePosition;
|
2016-03-06 09:19:14 +00:00
|
|
|
|
2016-08-24 10:22:12 +00:00
|
|
|
if (value == nullptr) value = factory()->NewUndefinedLiteral(pos);
|
2016-03-06 09:19:14 +00:00
|
|
|
|
2016-08-24 10:22:12 +00:00
|
|
|
auto args = new (zone()) ZoneList<Expression*>(2, zone());
|
|
|
|
args->Add(value, zone());
|
|
|
|
args->Add(factory()->NewBooleanLiteral(done, pos), zone());
|
2014-04-02 11:03:05 +00:00
|
|
|
|
2016-08-24 10:22:12 +00:00
|
|
|
return factory()->NewCallRuntime(Runtime::kInlineCreateIterResultObject, args,
|
|
|
|
pos);
|
2014-04-02 11:03:05 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* Parser::NewThrowError(Runtime::FunctionId id,
|
|
|
|
MessageTemplate::Template message,
|
|
|
|
const AstRawString* arg, int pos) {
|
|
|
|
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
|
|
|
|
args->Add(factory()->NewSmiLiteral(message, pos), zone());
|
|
|
|
args->Add(factory()->NewStringLiteral(arg, pos), zone());
|
|
|
|
CallRuntime* call_constructor = factory()->NewCallRuntime(id, args, pos);
|
|
|
|
return factory()->NewThrow(call_constructor, pos);
|
2014-04-02 11:03:05 +00:00
|
|
|
}
|
|
|
|
|
2016-08-25 08:44:59 +00:00
|
|
|
Expression* Parser::NewSuperPropertyReference(int pos) {
|
2015-06-04 16:22:29 +00:00
|
|
|
// this_function[home_object_symbol]
|
2016-08-25 08:44:59 +00:00
|
|
|
VariableProxy* this_function_proxy =
|
|
|
|
NewUnresolved(ast_value_factory()->this_function_string(), pos);
|
2015-06-04 16:22:29 +00:00
|
|
|
Expression* home_object_symbol_literal =
|
2016-08-25 08:44:59 +00:00
|
|
|
factory()->NewSymbolLiteral("home_object_symbol", kNoSourcePosition);
|
|
|
|
Expression* home_object = factory()->NewProperty(
|
2015-06-04 16:22:29 +00:00
|
|
|
this_function_proxy, home_object_symbol_literal, pos);
|
2016-08-25 08:44:59 +00:00
|
|
|
return factory()->NewSuperPropertyReference(
|
2016-08-11 12:04:02 +00:00
|
|
|
ThisExpression(pos)->AsVariableProxy(), home_object, pos);
|
2014-08-18 12:35:34 +00:00
|
|
|
}
|
2014-02-14 11:24:26 +00:00
|
|
|
|
2016-08-25 08:44:59 +00:00
|
|
|
Expression* Parser::NewSuperCallReference(int pos) {
|
|
|
|
VariableProxy* new_target_proxy =
|
|
|
|
NewUnresolved(ast_value_factory()->new_target_string(), pos);
|
|
|
|
VariableProxy* this_function_proxy =
|
|
|
|
NewUnresolved(ast_value_factory()->this_function_string(), pos);
|
|
|
|
return factory()->NewSuperCallReference(
|
|
|
|
ThisExpression(pos)->AsVariableProxy(), new_target_proxy,
|
|
|
|
this_function_proxy, pos);
|
2016-08-11 12:04:02 +00:00
|
|
|
}
|
|
|
|
|
2016-08-25 08:44:59 +00:00
|
|
|
Expression* Parser::NewTargetExpression(int pos) {
|
2015-06-09 15:43:07 +00:00
|
|
|
static const int kNewTargetStringLength = 10;
|
2016-08-25 08:44:59 +00:00
|
|
|
auto proxy = NewUnresolved(ast_value_factory()->new_target_string(), pos,
|
|
|
|
pos + kNewTargetStringLength);
|
2015-08-13 18:06:04 +00:00
|
|
|
proxy->set_is_new_target();
|
|
|
|
return proxy;
|
2015-06-09 15:43:07 +00:00
|
|
|
}
|
|
|
|
|
2016-08-25 08:44:59 +00:00
|
|
|
Expression* Parser::FunctionSentExpression(int pos) {
|
2016-06-21 12:12:47 +00:00
|
|
|
// We desugar function.sent into %_GeneratorGetInputOrDebugPos(generator).
|
2016-08-25 08:44:59 +00:00
|
|
|
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
VariableProxy* generator =
|
|
|
|
factory()->NewVariableProxy(function_state_->generator_object_variable());
|
|
|
|
args->Add(generator, zone());
|
|
|
|
return factory()->NewCallRuntime(Runtime::kInlineGeneratorGetInputOrDebugPos,
|
|
|
|
args, pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
Literal* Parser::ExpressionFromLiteral(Token::Value token, int pos) {
|
2014-02-14 11:24:26 +00:00
|
|
|
switch (token) {
|
|
|
|
case Token::NULL_LITERAL:
|
2016-08-25 08:44:59 +00:00
|
|
|
return factory()->NewNullLiteral(pos);
|
2014-02-14 11:24:26 +00:00
|
|
|
case Token::TRUE_LITERAL:
|
2016-08-25 08:44:59 +00:00
|
|
|
return factory()->NewBooleanLiteral(true, pos);
|
2014-02-14 11:24:26 +00:00
|
|
|
case Token::FALSE_LITERAL:
|
2016-08-25 08:44:59 +00:00
|
|
|
return factory()->NewBooleanLiteral(false, pos);
|
2015-03-03 11:04:49 +00:00
|
|
|
case Token::SMI: {
|
2016-08-25 08:44:59 +00:00
|
|
|
int value = scanner()->smi_value();
|
|
|
|
return factory()->NewSmiLiteral(value, pos);
|
2015-03-03 11:04:49 +00:00
|
|
|
}
|
2014-02-14 11:24:26 +00:00
|
|
|
case Token::NUMBER: {
|
2016-08-25 08:44:59 +00:00
|
|
|
bool has_dot = scanner()->ContainsDot();
|
|
|
|
double value = scanner()->DoubleValue();
|
|
|
|
return factory()->NewNumberLiteral(value, pos, has_dot);
|
2014-02-14 11:24:26 +00:00
|
|
|
}
|
|
|
|
default:
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(false);
|
2014-02-14 11:24:26 +00:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2016-08-25 08:44:59 +00:00
|
|
|
Expression* Parser::GetIterator(Expression* iterable, int pos) {
|
2014-08-05 13:17:49 +00:00
|
|
|
Expression* iterator_symbol_literal =
|
2016-08-25 08:44:59 +00:00
|
|
|
factory()->NewSymbolLiteral("iterator_symbol", kNoSourcePosition);
|
2014-08-05 13:17:49 +00:00
|
|
|
Expression* prop =
|
2016-08-25 08:44:59 +00:00
|
|
|
factory()->NewProperty(iterable, iterator_symbol_literal, pos);
|
|
|
|
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(0, zone());
|
|
|
|
return factory()->NewCall(prop, args, pos);
|
2014-08-05 13:17:49 +00:00
|
|
|
}
|
|
|
|
|
2016-08-23 16:34:39 +00:00
|
|
|
void Parser::MarkTailPosition(Expression* expression) {
|
2016-04-26 09:05:48 +00:00
|
|
|
expression->MarkTail();
|
|
|
|
}
|
2016-03-21 17:48:29 +00:00
|
|
|
|
2016-09-08 11:03:46 +00:00
|
|
|
Expression* Parser::NewV8Intrinsic(const AstRawString* name,
|
|
|
|
ZoneList<Expression*>* args, int pos,
|
|
|
|
bool* ok) {
|
|
|
|
if (extension_ != nullptr) {
|
|
|
|
// The extension structures are only accessible while parsing the
|
|
|
|
// very first time, not when reparsing because of lazy compilation.
|
|
|
|
GetClosureScope()->ForceEagerCompilation();
|
|
|
|
}
|
|
|
|
|
2016-09-12 11:35:40 +00:00
|
|
|
DCHECK(name->is_one_byte());
|
|
|
|
const Runtime::Function* function =
|
|
|
|
Runtime::FunctionForName(name->raw_data(), name->length());
|
2016-09-08 11:03:46 +00:00
|
|
|
|
|
|
|
if (function != nullptr) {
|
|
|
|
// Check for possible name clash.
|
|
|
|
DCHECK_EQ(Context::kNotFound,
|
2016-09-12 11:35:40 +00:00
|
|
|
Context::IntrinsicIndexForName(name->raw_data(), name->length()));
|
2016-09-08 11:03:46 +00:00
|
|
|
// Check for built-in IS_VAR macro.
|
|
|
|
if (function->function_id == Runtime::kIS_VAR) {
|
|
|
|
DCHECK_EQ(Runtime::RUNTIME, function->intrinsic_type);
|
|
|
|
// %IS_VAR(x) evaluates to x if x is a variable,
|
|
|
|
// leads to a parse error otherwise. Could be implemented as an
|
|
|
|
// inline function %_IS_VAR(x) to eliminate this special case.
|
|
|
|
if (args->length() == 1 && args->at(0)->AsVariableProxy() != nullptr) {
|
|
|
|
return args->at(0);
|
|
|
|
} else {
|
|
|
|
ReportMessage(MessageTemplate::kNotIsvar);
|
|
|
|
*ok = false;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the expected number of arguments are being passed.
|
|
|
|
if (function->nargs != -1 && function->nargs != args->length()) {
|
|
|
|
ReportMessage(MessageTemplate::kRuntimeWrongNumArgs);
|
|
|
|
*ok = false;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return factory()->NewCallRuntime(function, args, pos);
|
|
|
|
}
|
|
|
|
|
2016-09-12 11:35:40 +00:00
|
|
|
int context_index =
|
|
|
|
Context::IntrinsicIndexForName(name->raw_data(), name->length());
|
2016-09-08 11:03:46 +00:00
|
|
|
|
|
|
|
// Check that the function is defined.
|
|
|
|
if (context_index == Context::kNotFound) {
|
|
|
|
ReportMessage(MessageTemplate::kNotDefined, name);
|
|
|
|
*ok = false;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
return factory()->NewCallRuntime(context_index, args, pos);
|
|
|
|
}
|
|
|
|
|
2015-03-09 14:51:13 +00:00
|
|
|
Parser::Parser(ParseInfo* info)
|
2016-08-23 12:54:32 +00:00
|
|
|
: ParserBase<Parser>(info->zone(), &scanner_, info->stack_limit(),
|
|
|
|
info->extension(), info->ast_value_factory(), NULL),
|
2016-03-21 17:48:29 +00:00
|
|
|
scanner_(info->unicode_cache()),
|
2011-11-25 09:36:31 +00:00
|
|
|
reusable_preparser_(NULL),
|
2013-08-23 09:25:37 +00:00
|
|
|
original_scope_(NULL),
|
2008-07-03 15:10:15 +00:00
|
|
|
target_stack_(NULL),
|
2015-02-12 13:02:30 +00:00
|
|
|
compile_options_(info->compile_options()),
|
2016-10-13 14:01:44 +00:00
|
|
|
cached_parse_data_(nullptr),
|
2014-09-02 11:36:21 +00:00
|
|
|
total_preparse_skipped_(0),
|
2015-02-12 13:02:30 +00:00
|
|
|
parsing_on_main_thread_(true) {
|
2015-03-09 14:51:13 +00:00
|
|
|
// Even though we were passed ParseInfo, we should not store it in
|
2015-02-12 13:02:30 +00:00
|
|
|
// Parser - this makes sure that Isolate is not accidentally accessed via
|
2015-03-09 14:51:13 +00:00
|
|
|
// ParseInfo during background parsing.
|
2016-07-29 09:17:08 +00:00
|
|
|
DCHECK(!info->script().is_null() || info->source_stream() != nullptr ||
|
|
|
|
info->character_stream() != nullptr);
|
2016-10-07 09:12:48 +00:00
|
|
|
// Determine if functions can be lazily compiled. This is necessary to
|
|
|
|
// allow some of our builtin JS files to be lazily compiled. These
|
|
|
|
// builtins cannot be handled lazily by the parser, since we have to know
|
|
|
|
// if a function uses the special natives syntax, which is something the
|
|
|
|
// parser records.
|
|
|
|
// If the debugger requests compilation for break points, we cannot be
|
|
|
|
// aggressive about lazy compilation, because it might trigger compilation
|
|
|
|
// of functions without an outer context when setting a breakpoint through
|
|
|
|
// Debug::FindSharedFunctionInfoInScript
|
|
|
|
bool can_compile_lazily = FLAG_lazy && !info->is_debug();
|
|
|
|
|
|
|
|
// Consider compiling eagerly when targeting the code cache.
|
|
|
|
can_compile_lazily &= !(FLAG_serialize_eager && info->will_serialize());
|
|
|
|
|
|
|
|
// Consider compiling eagerly when compiling bytecode for Ignition.
|
|
|
|
can_compile_lazily &= !(FLAG_ignition && FLAG_ignition_eager &&
|
|
|
|
info->isolate()->serializer_enabled());
|
|
|
|
|
|
|
|
set_default_eager_compile_hint(can_compile_lazily
|
|
|
|
? FunctionLiteral::kShouldLazyCompile
|
|
|
|
: FunctionLiteral::kShouldEagerCompile);
|
2016-09-19 11:46:03 +00:00
|
|
|
set_allow_lazy(FLAG_lazy && info->allow_lazy_parsing() &&
|
2016-10-13 14:01:44 +00:00
|
|
|
!info->is_native() && info->extension() == nullptr &&
|
|
|
|
can_compile_lazily);
|
2014-11-20 10:51:49 +00:00
|
|
|
set_allow_natives(FLAG_allow_natives_syntax || info->is_native());
|
2016-03-30 11:04:17 +00:00
|
|
|
set_allow_tailcalls(FLAG_harmony_tailcalls && !info->is_native() &&
|
|
|
|
info->isolate()->is_tail_call_elimination_enabled());
|
2015-10-21 02:55:47 +00:00
|
|
|
set_allow_harmony_do_expressions(FLAG_harmony_do_expressions);
|
2016-02-17 00:18:38 +00:00
|
|
|
set_allow_harmony_function_sent(FLAG_harmony_function_sent);
|
Restrict FunctionDeclarations in Statement position
ES2015 generally bans FunctionDeclarations in positions which expect a Statement,
as opposed to a StatementListItem, such as a FunctionDeclaration which constitutes
the body of a for loop. However, Annex B 3.2 and 3.4 make exceptions for labeled
function declarations and function declarations as the body of an if statement in
sloppy mode, in the latter case specifying that the semantics are as if the
function declaration occurred in a block. Chrome has historically permitted
further extensions, for the body of any flow control construct.
This patch addresses both the syntactic and semantic mismatches between V8 and
the spec. For the semantic mismatch, function declarations as the body of if
statements change from unconditionally hoisting in certain cases to acquiring
the sloppy mode function in block semantics (based on Annex B 3.3). For the
extra syntax permitted, this patch adds a flag,
--harmony-restrictive-declarations, which excludes disallowed function declaration
cases. A new UseCounter, LegacyFunctionDeclaration, is added to count how often
function declarations occur as the body of other constructs in sloppy mode. With
this patch, the code generally follows the form of the specification with respect
to parsing FunctionDeclarations, rather than allowing them in arbitrary Statement
positions, and makes it more clear where our extensions occur.
BUG=v8:4647
R=adamk
LOG=Y
Review URL: https://codereview.chromium.org/1757543003
Cr-Commit-Position: refs/heads/master@{#34470}
2016-03-03 21:33:53 +00:00
|
|
|
set_allow_harmony_restrictive_declarations(
|
|
|
|
FLAG_harmony_restrictive_declarations);
|
2016-05-16 23:17:13 +00:00
|
|
|
set_allow_harmony_async_await(FLAG_harmony_async_await);
|
2016-05-27 18:22:29 +00:00
|
|
|
set_allow_harmony_restrictive_generators(FLAG_harmony_restrictive_generators);
|
Allow trailing commas in function parameter lists
Add a flag harmony_trailing_commas_in_parameters that allows trailing
commas in function parameter declaration lists and function call
parameter lists. Trailing commas are allowed in parenthetical lists like
`(a, b, c,)` only if the next token is `=>`, thereby making it an arrow
function declaration. Only 1 trailing comma is allowed, not `(a,,)`. A
trailing comma must follow a non-rest parameter, so `(,)` and `(...a,)`
are still SyntaxErrors. However, a trailing comma is allowed after a
spread parameter, e.g. `a(...b,);`.
Add parser tests for all of the above.
BUG=v8:5051
LOG=y
Review-Url: https://codereview.chromium.org/2094463002
Cr-Commit-Position: refs/heads/master@{#37355}
2016-06-29 01:37:57 +00:00
|
|
|
set_allow_harmony_trailing_commas(FLAG_harmony_trailing_commas);
|
2016-09-16 00:42:30 +00:00
|
|
|
set_allow_harmony_class_fields(FLAG_harmony_class_fields);
|
2014-06-30 13:35:16 +00:00
|
|
|
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
|
|
|
|
++feature) {
|
|
|
|
use_counts_[feature] = 0;
|
|
|
|
}
|
2014-09-11 09:52:36 +00:00
|
|
|
if (info->ast_value_factory() == NULL) {
|
|
|
|
// info takes ownership of AstValueFactory.
|
2015-03-09 14:51:13 +00:00
|
|
|
info->set_ast_value_factory(new AstValueFactory(zone(), info->hash_seed()));
|
|
|
|
info->set_ast_value_factory_owned();
|
2015-02-06 15:58:36 +00:00
|
|
|
ast_value_factory_ = info->ast_value_factory();
|
2016-07-21 11:02:54 +00:00
|
|
|
ast_node_factory_.set_ast_value_factory(ast_value_factory_);
|
2014-09-02 11:36:21 +00:00
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2016-09-15 19:47:11 +00:00
|
|
|
void Parser::DeserializeScopeChain(
|
|
|
|
ParseInfo* info, MaybeHandle<ScopeInfo> maybe_outer_scope_info) {
|
2016-08-23 11:45:59 +00:00
|
|
|
DCHECK(ThreadId::Current().Equals(info->isolate()->thread_id()));
|
2016-08-03 13:30:23 +00:00
|
|
|
// TODO(wingo): Add an outer SCRIPT_SCOPE corresponding to the native
|
|
|
|
// context, which will have the "this" binding for script scopes.
|
2016-08-05 14:30:54 +00:00
|
|
|
DeclarationScope* script_scope = NewScriptScope();
|
|
|
|
info->set_script_scope(script_scope);
|
|
|
|
Scope* scope = script_scope;
|
2016-09-15 19:47:11 +00:00
|
|
|
Handle<ScopeInfo> outer_scope_info;
|
|
|
|
if (maybe_outer_scope_info.ToHandle(&outer_scope_info)) {
|
2016-09-15 16:41:03 +00:00
|
|
|
scope = Scope::DeserializeScopeChain(
|
2016-09-15 19:47:11 +00:00
|
|
|
info->isolate(), zone(), *outer_scope_info, script_scope,
|
|
|
|
ast_value_factory(), Scope::DeserializationMode::kScopesOnly);
|
2016-08-31 14:25:28 +00:00
|
|
|
DCHECK(!info->is_module() || scope->is_module_scope());
|
2016-08-03 13:30:23 +00:00
|
|
|
}
|
|
|
|
original_scope_ = scope;
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2015-03-09 14:51:13 +00:00
|
|
|
FunctionLiteral* Parser::ParseProgram(Isolate* isolate, ParseInfo* info) {
|
2013-10-29 11:44:04 +00:00
|
|
|
// TODO(bmeurer): We temporarily need to pass allow_nesting = true here,
|
|
|
|
// see comment for HistogramTimerScope class.
|
2014-09-02 11:36:21 +00:00
|
|
|
|
2015-02-12 13:02:30 +00:00
|
|
|
// It's OK to use the Isolate & counters here, since this function is only
|
|
|
|
// called in the main thread.
|
|
|
|
DCHECK(parsing_on_main_thread_);
|
|
|
|
|
2016-10-14 14:08:53 +00:00
|
|
|
RuntimeCallTimerScope runtime_timer(isolate, &RuntimeCallStats::ParseProgram);
|
|
|
|
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.ParseProgram");
|
2015-02-12 13:02:30 +00:00
|
|
|
Handle<String> source(String::cast(info->script()->source()));
|
|
|
|
isolate->counters()->total_parse_size()->Increment(source->length());
|
2014-06-30 13:25:46 +00:00
|
|
|
base::ElapsedTimer timer;
|
2013-08-29 09:15:13 +00:00
|
|
|
if (FLAG_trace_parse) {
|
|
|
|
timer.Start();
|
|
|
|
}
|
2014-09-11 09:52:36 +00:00
|
|
|
fni_ = new (zone()) FuncNameInferrer(ast_value_factory(), zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Initialize parser state.
|
2014-03-19 13:24:13 +00:00
|
|
|
CompleteParserRecorder recorder;
|
Change ScriptCompiler::CompileOptions to allow for two 'cache' modes
(parser or code) and to be explicit about cache consumption or production
(rather than making presence of cached_data imply one or the other.)
Also add a --cache flag to d8, to allow testing the functionality.
-----------------------------
API change
Reason: Currently, V8 supports a 'parser cache' for repeatedly executing the same script. We'd like to add a 2nd mode that would cache code, and would like to let the embedder decide which mode they chose (if any).
Note: Previously, the 'use cached data' property was implied by the presence of the cached data itself. (That is, kNoCompileOptions and source->cached_data != NULL.) That is no longer sufficient, since the presence of data is no longer sufficient to determine /which kind/ of data is present.
Changes from old behaviour:
- If you previously didn't use caching, nothing changes.
Example:
v8::CompileUnbound(isolate, source, kNoCompileOptions);
- If you previously used caching, it worked like this:
- 1st run:
v8::CompileUnbound(isolate, source, kProduceToCache);
Then, source->cached_data would contain the
data-to-be cached. This remains the same, except you
need to tell V8 which type of data you want.
v8::CompileUnbound(isolate, source, kProduceParserCache);
- 2nd run:
v8::CompileUnbound(isolate, source, kNoCompileOptions);
with source->cached_data set to the data you received in
the first run. This will now ignore the cached data, and
you need to explicitly tell V8 to use it:
v8::CompileUnbound(isolate, source, kConsumeParserCache);
-----------------------------
BUG=
R=marja@chromium.org, yangguo@chromium.org
Review URL: https://codereview.chromium.org/389573006
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22431 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2014-07-16 12:18:33 +00:00
|
|
|
|
2014-11-17 12:16:27 +00:00
|
|
|
if (produce_cached_parse_data()) {
|
2014-03-19 13:24:13 +00:00
|
|
|
log_ = &recorder;
|
2014-11-17 12:16:27 +00:00
|
|
|
} else if (consume_cached_parse_data()) {
|
2014-07-10 10:28:05 +00:00
|
|
|
cached_parse_data_->Initialize();
|
2014-03-19 13:24:13 +00:00
|
|
|
}
|
|
|
|
|
2016-09-15 19:47:11 +00:00
|
|
|
DeserializeScopeChain(info, info->maybe_outer_scope_info());
|
2016-08-03 13:30:23 +00:00
|
|
|
|
2014-04-08 09:49:49 +00:00
|
|
|
source = String::Flatten(source);
|
2012-07-18 11:22:46 +00:00
|
|
|
FunctionLiteral* result;
|
2014-09-12 09:12:08 +00:00
|
|
|
|
2016-07-29 06:12:03 +00:00
|
|
|
{
|
Rework scanner-character-streams.
- Smaller, more consistent streams API (Advance, Back, pos, Seek)
- Remove implementations from the header, in favor of creation functions.
Observe:
- Performance:
- All Utf16CharacterStream methods have an inlinable V8_LIKELY w/ a
body of only a few instructions. I expect most calls to end up there.
- There used to be performance problems w/ bookmarking, particularly
with copying too much data on SetBookmark w/ UTF-8 streaming streams.
All those copies are gone.
- The old streaming streams implementation used to copy data even for
2-byte input. It no longer does.
- The only remaining 'slow' method is the Seek(.) slow case for utf-8
streaming streams. I don't expect this to be called a lot; and even if,
I expect it to be offset by the gains in the (vastly more frequent)
calls to the other methods or the 'fast path'.
- If it still bothers us, there are several ways to speed it up.
- API & code cleanliness:
- I want to remove the 'old' API in a follow-up CL, which should mostly
delete code, or replace it 1:1.
- In a 2nd follow-up I want to delete much of the UTF-8 handling in Blink
for streaming streams.
- The "bookmark" is now always implemented (and mostly very fast), so we
should be able to use it for more things.
- Testing & correctness:
- The unit tests now cover all stream implementations,
and are pretty good and triggering all the edge cases.
- Vastly more DCHECKs of the invariants.
BUG=v8:4947
Review-Url: https://codereview.chromium.org/2314663002
Cr-Commit-Position: refs/heads/master@{#39464}
2016-09-16 08:29:41 +00:00
|
|
|
std::unique_ptr<Utf16CharacterStream> stream(ScannerStream::For(source));
|
2016-07-29 06:12:03 +00:00
|
|
|
scanner_.Initialize(stream.get());
|
2015-04-16 12:42:43 +00:00
|
|
|
result = DoParseProgram(info);
|
2015-04-15 17:20:29 +00:00
|
|
|
}
|
2015-04-16 12:42:43 +00:00
|
|
|
if (result != NULL) {
|
|
|
|
DCHECK_EQ(scanner_.peek_location().beg_pos, source->length());
|
2010-12-07 14:03:59 +00:00
|
|
|
}
|
2015-02-20 09:39:32 +00:00
|
|
|
HandleSourceURLComments(isolate, info->script());
|
2012-07-18 11:22:46 +00:00
|
|
|
|
|
|
|
if (FLAG_trace_parse && result != NULL) {
|
2013-08-29 09:15:13 +00:00
|
|
|
double ms = timer.Elapsed().InMillisecondsF();
|
2015-02-12 13:02:30 +00:00
|
|
|
if (info->is_eval()) {
|
2012-07-18 11:22:46 +00:00
|
|
|
PrintF("[parsing eval");
|
2015-02-12 13:02:30 +00:00
|
|
|
} else if (info->script()->name()->IsString()) {
|
|
|
|
String* name = String::cast(info->script()->name());
|
2016-07-25 10:24:45 +00:00
|
|
|
std::unique_ptr<char[]> name_chars = name->ToCString();
|
2013-12-09 07:41:20 +00:00
|
|
|
PrintF("[parsing script: %s", name_chars.get());
|
2012-07-18 11:22:46 +00:00
|
|
|
} else {
|
|
|
|
PrintF("[parsing script");
|
|
|
|
}
|
|
|
|
PrintF(" - took %0.3f ms]\n", ms);
|
|
|
|
}
|
2014-11-17 12:16:27 +00:00
|
|
|
if (produce_cached_parse_data()) {
|
2015-02-12 13:02:30 +00:00
|
|
|
if (result != NULL) *info->cached_data() = recorder.GetScriptData();
|
2014-03-19 13:24:13 +00:00
|
|
|
log_ = NULL;
|
|
|
|
}
|
2012-07-18 11:22:46 +00:00
|
|
|
return result;
|
2010-12-07 14:03:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-16 12:42:43 +00:00
|
|
|
FunctionLiteral* Parser::DoParseProgram(ParseInfo* info) {
|
2015-02-12 13:02:30 +00:00
|
|
|
// Note that this function can be called from the main thread or from a
|
|
|
|
// background thread. We should not access anything Isolate / heap dependent
|
2015-03-09 14:51:13 +00:00
|
|
|
// via ParseInfo, and also not pass it forward.
|
2016-07-19 10:06:38 +00:00
|
|
|
DCHECK_NULL(scope_state_);
|
|
|
|
DCHECK_NULL(target_stack_);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2016-10-13 13:33:41 +00:00
|
|
|
ParsingModeScope mode(this, allow_lazy() ? PARSE_LAZILY : PARSE_EAGERLY);
|
2015-04-27 12:13:45 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
FunctionLiteral* result = NULL;
|
2014-09-12 09:12:08 +00:00
|
|
|
{
|
2016-08-05 14:30:54 +00:00
|
|
|
Scope* outer = original_scope_;
|
2016-08-30 11:52:58 +00:00
|
|
|
DCHECK_NOT_NULL(outer);
|
2016-09-30 07:53:43 +00:00
|
|
|
parsing_module_ = info->is_module();
|
2012-03-15 13:02:21 +00:00
|
|
|
if (info->is_eval()) {
|
2016-08-05 14:30:54 +00:00
|
|
|
outer = NewEvalScope(outer);
|
2016-09-30 07:53:43 +00:00
|
|
|
} else if (parsing_module_) {
|
2016-08-18 08:50:55 +00:00
|
|
|
DCHECK_EQ(outer, info->script_scope());
|
|
|
|
outer = NewModuleScope(info->script_scope());
|
2011-11-15 13:48:40 +00:00
|
|
|
}
|
2015-04-16 12:42:43 +00:00
|
|
|
|
2016-08-05 14:30:54 +00:00
|
|
|
DeclarationScope* scope = outer->AsDeclarationScope();
|
|
|
|
|
2015-04-16 12:42:43 +00:00
|
|
|
scope->set_start_position(0);
|
2012-08-28 11:25:08 +00:00
|
|
|
|
2016-09-27 11:41:00 +00:00
|
|
|
FunctionState function_state(&function_state_, &scope_state_, scope);
|
2013-04-02 17:34:59 +00:00
|
|
|
|
2012-06-11 12:42:31 +00:00
|
|
|
ZoneList<Statement*>* body = new(zone()) ZoneList<Statement*>(16, zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
bool ok = true;
|
2014-02-14 12:13:33 +00:00
|
|
|
int beg_pos = scanner()->location().beg_pos;
|
2016-04-29 18:12:57 +00:00
|
|
|
if (parsing_module_) {
|
2016-09-12 12:54:47 +00:00
|
|
|
// Declare the special module parameter.
|
|
|
|
auto name = ast_value_factory()->empty_string();
|
|
|
|
bool is_duplicate;
|
|
|
|
bool is_rest = false;
|
|
|
|
bool is_optional = false;
|
|
|
|
auto var = scope->DeclareParameter(name, VAR, is_optional, is_rest,
|
|
|
|
&is_duplicate, ast_value_factory());
|
|
|
|
DCHECK(!is_duplicate);
|
|
|
|
var->AllocateTo(VariableLocation::PARAMETER, 0);
|
|
|
|
|
2016-09-30 07:53:43 +00:00
|
|
|
PrepareGeneratorVariables(&function_state);
|
|
|
|
Expression* initial_yield =
|
|
|
|
BuildInitialYield(kNoSourcePosition, kGeneratorFunction);
|
|
|
|
body->Add(
|
|
|
|
factory()->NewExpressionStatement(initial_yield, kNoSourcePosition),
|
|
|
|
zone());
|
|
|
|
|
2015-02-25 23:00:10 +00:00
|
|
|
ParseModuleItemList(body, &ok);
|
2016-07-18 07:27:10 +00:00
|
|
|
ok = ok &&
|
2016-08-18 08:50:55 +00:00
|
|
|
module()->Validate(this->scope()->AsModuleScope(),
|
2016-08-05 14:30:54 +00:00
|
|
|
&pending_error_handler_, zone());
|
2015-01-27 21:06:36 +00:00
|
|
|
} else {
|
2016-04-22 13:37:10 +00:00
|
|
|
// Don't count the mode in the use counters--give the program a chance
|
|
|
|
// to enable script-wide strict mode below.
|
2016-07-19 10:06:38 +00:00
|
|
|
this->scope()->SetLanguageMode(info->language_mode());
|
2015-04-27 12:13:45 +00:00
|
|
|
ParseStatementList(body, Token::EOS, &ok);
|
2015-01-27 21:06:36 +00:00
|
|
|
}
|
2014-07-02 07:01:31 +00:00
|
|
|
|
2015-04-16 12:42:43 +00:00
|
|
|
// The parser will peek but not consume EOS. Our scope logically goes all
|
|
|
|
// the way to the EOS, though.
|
|
|
|
scope->set_end_position(scanner()->peek_location().beg_pos);
|
|
|
|
|
2015-02-04 09:34:05 +00:00
|
|
|
if (ok && is_strict(language_mode())) {
|
2014-12-18 22:01:25 +00:00
|
|
|
CheckStrictOctalLiteral(beg_pos, scanner()->location().end_pos, &ok);
|
2016-09-08 11:03:46 +00:00
|
|
|
CheckDecimalLiteralWithLeadingZero(beg_pos,
|
2016-05-16 23:20:29 +00:00
|
|
|
scanner()->location().end_pos);
|
2015-07-10 16:39:47 +00:00
|
|
|
}
|
2016-04-08 00:29:37 +00:00
|
|
|
if (ok && is_sloppy(language_mode())) {
|
2015-09-30 23:48:26 +00:00
|
|
|
// TODO(littledan): Function bindings on the global object that modify
|
|
|
|
// pre-existing bindings should be made writable, enumerable and
|
|
|
|
// nonconfigurable if possible, whereas this code will leave attributes
|
|
|
|
// unchanged if the property already exists.
|
2016-09-15 16:41:03 +00:00
|
|
|
InsertSloppyBlockFunctionVarBindings(scope);
|
2015-09-30 23:48:26 +00:00
|
|
|
}
|
2016-03-10 23:19:31 +00:00
|
|
|
if (ok) {
|
2016-08-05 14:30:54 +00:00
|
|
|
CheckConflictingVarDeclarations(scope, &ok);
|
2011-09-01 12:31:18 +00:00
|
|
|
}
|
|
|
|
|
2013-03-07 15:46:14 +00:00
|
|
|
if (ok && info->parse_restriction() == ONLY_SINGLE_FUNCTION_LITERAL) {
|
|
|
|
if (body->length() != 1 ||
|
|
|
|
!body->at(0)->IsExpressionStatement() ||
|
|
|
|
!body->at(0)->AsExpressionStatement()->
|
|
|
|
expression()->IsFunctionLiteral()) {
|
2015-05-18 08:34:05 +00:00
|
|
|
ReportMessage(MessageTemplate::kSingleFunctionLiteral);
|
2013-03-07 15:46:14 +00:00
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
if (ok) {
|
2016-08-23 16:34:39 +00:00
|
|
|
RewriteDestructuringAssignments();
|
2016-09-12 12:54:47 +00:00
|
|
|
int parameter_count = parsing_module_ ? 1 : 0;
|
2016-02-19 02:50:58 +00:00
|
|
|
result = factory()->NewScriptOrEvalFunctionLiteral(
|
2016-08-05 14:30:54 +00:00
|
|
|
scope, body, function_state.materialized_literal_count(),
|
2016-09-12 12:54:47 +00:00
|
|
|
function_state.expected_property_count(), parameter_count);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the target stack is empty.
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(target_stack_ == NULL);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-10-14 14:08:53 +00:00
|
|
|
FunctionLiteral* Parser::ParseFunction(Isolate* isolate, ParseInfo* info) {
|
2015-02-12 13:02:30 +00:00
|
|
|
// It's OK to use the Isolate & counters here, since this function is only
|
|
|
|
// called in the main thread.
|
|
|
|
DCHECK(parsing_on_main_thread_);
|
2016-10-14 14:08:53 +00:00
|
|
|
RuntimeCallTimerScope runtime_timer(isolate,
|
|
|
|
&RuntimeCallStats::ParseFunction);
|
|
|
|
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.ParseFunction");
|
2015-02-12 13:02:30 +00:00
|
|
|
Handle<String> source(String::cast(info->script()->source()));
|
2015-02-20 09:39:32 +00:00
|
|
|
isolate->counters()->total_parse_size()->Increment(source->length());
|
2014-06-30 13:25:46 +00:00
|
|
|
base::ElapsedTimer timer;
|
2013-08-29 09:15:13 +00:00
|
|
|
if (FLAG_trace_parse) {
|
|
|
|
timer.Start();
|
|
|
|
}
|
2015-02-12 13:02:30 +00:00
|
|
|
Handle<SharedFunctionInfo> shared_info = info->shared_info();
|
2016-09-15 19:47:11 +00:00
|
|
|
DeserializeScopeChain(info, info->maybe_outer_scope_info());
|
2012-07-18 11:22:46 +00:00
|
|
|
|
2010-12-07 14:03:59 +00:00
|
|
|
// Initialize parser state.
|
2014-04-08 09:49:49 +00:00
|
|
|
source = String::Flatten(source);
|
2012-07-18 11:22:46 +00:00
|
|
|
FunctionLiteral* result;
|
2016-07-29 07:35:46 +00:00
|
|
|
{
|
Rework scanner-character-streams.
- Smaller, more consistent streams API (Advance, Back, pos, Seek)
- Remove implementations from the header, in favor of creation functions.
Observe:
- Performance:
- All Utf16CharacterStream methods have an inlinable V8_LIKELY w/ a
body of only a few instructions. I expect most calls to end up there.
- There used to be performance problems w/ bookmarking, particularly
with copying too much data on SetBookmark w/ UTF-8 streaming streams.
All those copies are gone.
- The old streaming streams implementation used to copy data even for
2-byte input. It no longer does.
- The only remaining 'slow' method is the Seek(.) slow case for utf-8
streaming streams. I don't expect this to be called a lot; and even if,
I expect it to be offset by the gains in the (vastly more frequent)
calls to the other methods or the 'fast path'.
- If it still bothers us, there are several ways to speed it up.
- API & code cleanliness:
- I want to remove the 'old' API in a follow-up CL, which should mostly
delete code, or replace it 1:1.
- In a 2nd follow-up I want to delete much of the UTF-8 handling in Blink
for streaming streams.
- The "bookmark" is now always implemented (and mostly very fast), so we
should be able to use it for more things.
- Testing & correctness:
- The unit tests now cover all stream implementations,
and are pretty good and triggering all the edge cases.
- Vastly more DCHECKs of the invariants.
BUG=v8:4947
Review-Url: https://codereview.chromium.org/2314663002
Cr-Commit-Position: refs/heads/master@{#39464}
2016-09-16 08:29:41 +00:00
|
|
|
std::unique_ptr<Utf16CharacterStream> stream(ScannerStream::For(
|
|
|
|
source, shared_info->start_position(), shared_info->end_position()));
|
2016-08-04 08:51:29 +00:00
|
|
|
Handle<String> name(String::cast(shared_info->name()));
|
2016-10-14 14:08:53 +00:00
|
|
|
result = DoParseFunction(info, ast_value_factory()->GetString(name),
|
|
|
|
stream.get());
|
2016-08-04 08:51:29 +00:00
|
|
|
if (result != nullptr) {
|
|
|
|
Handle<String> inferred_name(shared_info->inferred_name());
|
|
|
|
result->set_inferred_name(inferred_name);
|
|
|
|
}
|
2010-12-07 14:03:59 +00:00
|
|
|
}
|
2012-07-18 11:22:46 +00:00
|
|
|
|
|
|
|
if (FLAG_trace_parse && result != NULL) {
|
2013-08-29 09:15:13 +00:00
|
|
|
double ms = timer.Elapsed().InMillisecondsF();
|
2016-09-20 10:30:01 +00:00
|
|
|
// We need to make sure that the debug-name is available.
|
|
|
|
ast_value_factory()->Internalize(isolate);
|
2016-07-25 10:24:45 +00:00
|
|
|
std::unique_ptr<char[]> name_chars = result->debug_name()->ToCString();
|
2013-12-09 07:41:20 +00:00
|
|
|
PrintF("[parsing function: %s - took %0.3f ms]\n", name_chars.get(), ms);
|
2012-07-18 11:22:46 +00:00
|
|
|
}
|
|
|
|
return result;
|
2010-12-07 14:03:59 +00:00
|
|
|
}
|
|
|
|
|
2016-08-04 08:51:29 +00:00
|
|
|
static FunctionLiteral::FunctionType ComputeFunctionType(ParseInfo* info) {
|
|
|
|
if (info->is_declaration()) {
|
2016-02-19 02:50:58 +00:00
|
|
|
return FunctionLiteral::kDeclaration;
|
2016-08-04 08:51:29 +00:00
|
|
|
} else if (info->is_named_expression()) {
|
2016-02-19 02:50:58 +00:00
|
|
|
return FunctionLiteral::kNamedExpression;
|
2016-08-04 08:51:29 +00:00
|
|
|
} else if (IsConciseMethod(info->function_kind()) ||
|
|
|
|
IsAccessorFunction(info->function_kind())) {
|
2016-02-19 02:50:58 +00:00
|
|
|
return FunctionLiteral::kAccessorOrMethod;
|
|
|
|
}
|
|
|
|
return FunctionLiteral::kAnonymousExpression;
|
|
|
|
}
|
2010-12-07 14:03:59 +00:00
|
|
|
|
2016-10-14 14:08:53 +00:00
|
|
|
FunctionLiteral* Parser::DoParseFunction(ParseInfo* info,
|
|
|
|
const AstRawString* raw_name,
|
|
|
|
Utf16CharacterStream* source) {
|
2010-12-22 20:14:19 +00:00
|
|
|
scanner_.Initialize(source);
|
2016-07-19 10:06:38 +00:00
|
|
|
DCHECK_NULL(scope_state_);
|
|
|
|
DCHECK_NULL(target_stack_);
|
2010-12-07 14:03:59 +00:00
|
|
|
|
2014-09-11 09:52:36 +00:00
|
|
|
DCHECK(ast_value_factory());
|
|
|
|
fni_ = new (zone()) FuncNameInferrer(ast_value_factory(), zone());
|
2014-06-24 14:03:24 +00:00
|
|
|
fni_->PushEnclosingName(raw_name);
|
2010-08-23 13:26:03 +00:00
|
|
|
|
2012-08-07 14:47:36 +00:00
|
|
|
ParsingModeScope parsing_mode(this, PARSE_EAGERLY);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Place holder for the result.
|
2016-07-19 10:06:38 +00:00
|
|
|
FunctionLiteral* result = nullptr;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
// Parse the function literal.
|
2016-08-30 11:52:58 +00:00
|
|
|
Scope* outer = original_scope_;
|
2016-09-27 09:49:26 +00:00
|
|
|
DeclarationScope* outer_function = outer->GetClosureScope();
|
2016-08-30 11:52:58 +00:00
|
|
|
DCHECK(outer);
|
2016-09-27 09:49:26 +00:00
|
|
|
FunctionState function_state(&function_state_, &scope_state_,
|
2016-09-27 11:41:00 +00:00
|
|
|
outer_function);
|
2016-09-27 09:49:26 +00:00
|
|
|
BlockState block_state(&scope_state_, outer);
|
2016-08-30 11:52:58 +00:00
|
|
|
DCHECK(is_sloppy(outer->language_mode()) ||
|
2015-02-12 13:02:30 +00:00
|
|
|
is_strict(info->language_mode()));
|
2016-08-04 08:51:29 +00:00
|
|
|
FunctionLiteral::FunctionType function_type = ComputeFunctionType(info);
|
2016-09-28 21:23:53 +00:00
|
|
|
FunctionKind kind = info->function_kind();
|
2008-07-03 15:10:15 +00:00
|
|
|
bool ok = true;
|
2014-07-21 09:58:01 +00:00
|
|
|
|
2016-09-28 21:23:53 +00:00
|
|
|
if (IsArrowFunction(kind)) {
|
|
|
|
if (allow_harmony_async_await() && IsAsyncFunction(kind)) {
|
2016-05-16 23:17:13 +00:00
|
|
|
DCHECK(!scanner()->HasAnyLineTerminatorAfterNext());
|
2016-07-11 19:28:56 +00:00
|
|
|
if (!Check(Token::ASYNC)) {
|
|
|
|
CHECK(stack_overflow());
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (!(peek_any_identifier() || peek() == Token::LPAREN)) {
|
|
|
|
CHECK(stack_overflow());
|
|
|
|
return nullptr;
|
|
|
|
}
|
2016-05-16 23:17:13 +00:00
|
|
|
}
|
|
|
|
|
2016-01-14 19:20:56 +00:00
|
|
|
// TODO(adamk): We should construct this scope from the ScopeInfo.
|
2016-09-28 21:23:53 +00:00
|
|
|
DeclarationScope* scope = NewFunctionScope(kind);
|
2016-01-14 19:20:56 +00:00
|
|
|
|
|
|
|
// These two bits only need to be explicitly set because we're
|
|
|
|
// not passing the ScopeInfo to the Scope constructor.
|
|
|
|
// TODO(adamk): Remove these calls once the above NewScope call
|
|
|
|
// passes the ScopeInfo.
|
2016-08-04 08:51:29 +00:00
|
|
|
if (info->calls_eval()) {
|
2016-01-14 19:20:56 +00:00
|
|
|
scope->RecordEvalCall();
|
|
|
|
}
|
2016-08-04 08:51:29 +00:00
|
|
|
SetLanguageMode(scope, info->language_mode());
|
2016-01-14 19:20:56 +00:00
|
|
|
|
2016-08-04 08:51:29 +00:00
|
|
|
scope->set_start_position(info->start_position());
|
2016-02-19 15:58:57 +00:00
|
|
|
ExpressionClassifier formals_classifier(this);
|
2015-07-23 11:53:31 +00:00
|
|
|
ParserFormalParameters formals(scope);
|
2015-06-26 21:39:43 +00:00
|
|
|
Checkpoint checkpoint(this);
|
2015-06-15 17:06:34 +00:00
|
|
|
{
|
|
|
|
// Parsing patterns as variable reference expression creates
|
|
|
|
// NewUnresolved references in current scope. Entrer arrow function
|
|
|
|
// scope for formal parameter parsing.
|
2016-07-19 10:06:38 +00:00
|
|
|
BlockState block_state(&scope_state_, scope);
|
2015-06-15 17:06:34 +00:00
|
|
|
if (Check(Token::LPAREN)) {
|
|
|
|
// '(' StrictFormalParameters ')'
|
2016-09-01 08:58:15 +00:00
|
|
|
ParseFormalParameterList(&formals, &ok);
|
2015-06-15 17:06:34 +00:00
|
|
|
if (ok) ok = Check(Token::RPAREN);
|
|
|
|
} else {
|
|
|
|
// BindingIdentifier
|
2016-09-01 08:58:15 +00:00
|
|
|
ParseFormalParameter(&formals, &ok);
|
|
|
|
if (ok) DeclareFormalParameter(formals.scope, formals.at(0));
|
2015-06-15 17:06:34 +00:00
|
|
|
}
|
2015-04-21 14:44:18 +00:00
|
|
|
}
|
|
|
|
|
2015-03-20 00:17:41 +00:00
|
|
|
if (ok) {
|
2015-07-23 11:53:31 +00:00
|
|
|
checkpoint.Restore(&formals.materialized_literals_count);
|
2015-10-14 17:39:37 +00:00
|
|
|
// Pass `accept_IN=true` to ParseArrowFunctionLiteral --- This should
|
|
|
|
// not be observable, or else the preparser would have failed.
|
2016-09-27 11:41:00 +00:00
|
|
|
Expression* expression = ParseArrowFunctionLiteral(true, formals, &ok);
|
2015-04-21 14:44:18 +00:00
|
|
|
if (ok) {
|
|
|
|
// Scanning must end at the same position that was recorded
|
|
|
|
// previously. If not, parsing has been interrupted due to a stack
|
|
|
|
// overflow, at which point the partially parsed arrow function
|
|
|
|
// concise body happens to be a valid expression. This is a problem
|
|
|
|
// only for arrow functions with single expression bodies, since there
|
|
|
|
// is no end token such as "}" for normal functions.
|
2016-08-04 08:51:29 +00:00
|
|
|
if (scanner()->location().end_pos == info->end_position()) {
|
2015-04-21 14:44:18 +00:00
|
|
|
// The pre-parser saw an arrow function here, so the full parser
|
|
|
|
// must produce a FunctionLiteral.
|
|
|
|
DCHECK(expression->IsFunctionLiteral());
|
|
|
|
result = expression->AsFunctionLiteral();
|
|
|
|
} else {
|
|
|
|
ok = false;
|
|
|
|
}
|
2015-03-20 00:17:41 +00:00
|
|
|
}
|
|
|
|
}
|
2016-09-28 21:23:53 +00:00
|
|
|
} else if (IsDefaultConstructor(kind)) {
|
2016-08-30 11:52:58 +00:00
|
|
|
DCHECK_EQ(scope(), outer);
|
2016-09-28 21:23:53 +00:00
|
|
|
bool is_subclass_constructor = IsSubclassConstructor(kind);
|
2016-02-01 17:44:23 +00:00
|
|
|
result = DefaultConstructor(
|
2016-09-16 00:46:00 +00:00
|
|
|
raw_name, is_subclass_constructor, info->requires_class_field_init(),
|
|
|
|
info->start_position(), info->end_position(), info->language_mode());
|
|
|
|
if (!is_subclass_constructor && info->requires_class_field_init()) {
|
|
|
|
result = InsertClassFieldInitializer(result);
|
|
|
|
}
|
|
|
|
} else if (info->is_class_field_initializer()) {
|
|
|
|
Handle<SharedFunctionInfo> shared_info = info->shared_info();
|
|
|
|
DCHECK(!shared_info.is_null());
|
|
|
|
if (shared_info->length() == 0) {
|
|
|
|
result = ParseClassFieldForInitializer(
|
|
|
|
info->start_position() != info->end_position(), &ok);
|
|
|
|
} else {
|
|
|
|
result = SynthesizeClassFieldInitializer(shared_info->length());
|
|
|
|
}
|
2014-07-21 09:58:01 +00:00
|
|
|
} else {
|
2016-09-28 21:23:53 +00:00
|
|
|
result = ParseFunctionLiteral(
|
|
|
|
raw_name, Scanner::Location::invalid(), kSkipFunctionNameCheck, kind,
|
|
|
|
kNoSourcePosition, function_type, info->language_mode(), &ok);
|
2016-09-16 00:46:00 +00:00
|
|
|
if (info->requires_class_field_init()) {
|
|
|
|
result = InsertClassFieldInitializer(result);
|
|
|
|
}
|
2014-07-21 09:58:01 +00:00
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
// Make sure the results agree.
|
2016-07-19 10:06:38 +00:00
|
|
|
DCHECK(ok == (result != nullptr));
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure the target stack is empty.
|
2016-07-19 10:06:38 +00:00
|
|
|
DCHECK_NULL(target_stack_);
|
2008-07-03 15:10:15 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2015-01-27 21:06:36 +00:00
|
|
|
Statement* Parser::ParseModuleItem(bool* ok) {
|
2016-07-18 07:27:10 +00:00
|
|
|
// ecma262/#prod-ModuleItem
|
2015-01-27 21:06:36 +00:00
|
|
|
// ModuleItem :
|
|
|
|
// ImportDeclaration
|
|
|
|
// ExportDeclaration
|
|
|
|
// StatementListItem
|
2012-02-20 14:02:59 +00:00
|
|
|
|
|
|
|
switch (peek()) {
|
2015-01-27 21:06:36 +00:00
|
|
|
case Token::IMPORT:
|
2016-07-18 07:27:10 +00:00
|
|
|
ParseImportDeclaration(CHECK_OK);
|
|
|
|
return factory()->NewEmptyStatement(kNoSourcePosition);
|
2015-01-27 21:06:36 +00:00
|
|
|
case Token::EXPORT:
|
|
|
|
return ParseExportDeclaration(ok);
|
|
|
|
default:
|
|
|
|
return ParseStatementListItem(ok);
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-19 19:45:50 +00:00
|
|
|
void Parser::ParseModuleItemList(ZoneList<Statement*>* body, bool* ok) {
|
2016-07-18 07:27:10 +00:00
|
|
|
// ecma262/#prod-Module
|
2015-01-27 21:06:36 +00:00
|
|
|
// Module :
|
|
|
|
// ModuleBody?
|
|
|
|
//
|
2016-07-18 07:27:10 +00:00
|
|
|
// ecma262/#prod-ModuleItemList
|
2015-01-27 21:06:36 +00:00
|
|
|
// ModuleBody :
|
|
|
|
// ModuleItem*
|
2012-02-20 14:02:59 +00:00
|
|
|
|
2016-07-19 10:06:38 +00:00
|
|
|
DCHECK(scope()->is_module_scope());
|
2015-02-24 22:39:26 +00:00
|
|
|
while (peek() != Token::EOS) {
|
2016-07-19 19:45:50 +00:00
|
|
|
Statement* stat = ParseModuleItem(CHECK_OK_VOID);
|
2015-02-24 22:39:26 +00:00
|
|
|
if (stat && !stat->IsEmpty()) {
|
|
|
|
body->Add(stat, zone());
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-26 18:40:50 +00:00
|
|
|
const AstRawString* Parser::ParseModuleSpecifier(bool* ok) {
|
2015-02-06 17:52:20 +00:00
|
|
|
// ModuleSpecifier :
|
|
|
|
// StringLiteral
|
2012-02-20 14:02:59 +00:00
|
|
|
|
|
|
|
Expect(Token::STRING, CHECK_OK);
|
2016-08-25 08:44:59 +00:00
|
|
|
return GetSymbol();
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-19 19:45:50 +00:00
|
|
|
void Parser::ParseExportClause(ZoneList<const AstRawString*>* export_names,
|
|
|
|
ZoneList<Scanner::Location>* export_locations,
|
|
|
|
ZoneList<const AstRawString*>* local_names,
|
|
|
|
Scanner::Location* reserved_loc, bool* ok) {
|
2015-01-30 03:26:50 +00:00
|
|
|
// ExportClause :
|
2015-01-28 19:18:37 +00:00
|
|
|
// '{' '}'
|
2015-01-30 03:26:50 +00:00
|
|
|
// '{' ExportsList '}'
|
|
|
|
// '{' ExportsList ',' '}'
|
2015-01-28 19:18:37 +00:00
|
|
|
//
|
2015-01-30 03:26:50 +00:00
|
|
|
// ExportsList :
|
|
|
|
// ExportSpecifier
|
|
|
|
// ExportsList ',' ExportSpecifier
|
2015-01-28 19:18:37 +00:00
|
|
|
//
|
2015-01-30 03:26:50 +00:00
|
|
|
// ExportSpecifier :
|
2015-01-28 19:18:37 +00:00
|
|
|
// IdentifierName
|
|
|
|
// IdentifierName 'as' IdentifierName
|
|
|
|
|
2016-07-19 19:45:50 +00:00
|
|
|
Expect(Token::LBRACE, CHECK_OK_VOID);
|
2015-01-28 19:18:37 +00:00
|
|
|
|
2015-01-30 03:26:50 +00:00
|
|
|
Token::Value name_tok;
|
|
|
|
while ((name_tok = peek()) != Token::RBRACE) {
|
|
|
|
// Keep track of the first reserved word encountered in case our
|
|
|
|
// caller needs to report an error.
|
|
|
|
if (!reserved_loc->IsValid() &&
|
2016-04-29 18:12:57 +00:00
|
|
|
!Token::IsIdentifier(name_tok, STRICT, false, parsing_module_)) {
|
2015-01-30 03:26:50 +00:00
|
|
|
*reserved_loc = scanner()->location();
|
|
|
|
}
|
2016-07-19 19:45:50 +00:00
|
|
|
const AstRawString* local_name = ParseIdentifierName(CHECK_OK_VOID);
|
2015-01-28 19:18:37 +00:00
|
|
|
const AstRawString* export_name = NULL;
|
|
|
|
if (CheckContextualKeyword(CStrVector("as"))) {
|
2016-07-19 19:45:50 +00:00
|
|
|
export_name = ParseIdentifierName(CHECK_OK_VOID);
|
2015-01-28 19:18:37 +00:00
|
|
|
}
|
2015-02-19 20:14:55 +00:00
|
|
|
if (export_name == NULL) {
|
|
|
|
export_name = local_name;
|
|
|
|
}
|
|
|
|
export_names->Add(export_name, zone());
|
|
|
|
local_names->Add(local_name, zone());
|
|
|
|
export_locations->Add(scanner()->location(), zone());
|
2015-01-28 19:18:37 +00:00
|
|
|
if (peek() == Token::RBRACE) break;
|
2016-07-19 19:45:50 +00:00
|
|
|
Expect(Token::COMMA, CHECK_OK_VOID);
|
2015-01-28 19:18:37 +00:00
|
|
|
}
|
|
|
|
|
2016-07-19 19:45:50 +00:00
|
|
|
Expect(Token::RBRACE, CHECK_OK_VOID);
|
2015-01-28 19:18:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-18 07:27:10 +00:00
|
|
|
ZoneList<const Parser::NamedImport*>* Parser::ParseNamedImports(
|
|
|
|
int pos, bool* ok) {
|
2015-01-30 03:26:50 +00:00
|
|
|
// NamedImports :
|
|
|
|
// '{' '}'
|
|
|
|
// '{' ImportsList '}'
|
|
|
|
// '{' ImportsList ',' '}'
|
|
|
|
//
|
|
|
|
// ImportsList :
|
|
|
|
// ImportSpecifier
|
|
|
|
// ImportsList ',' ImportSpecifier
|
|
|
|
//
|
|
|
|
// ImportSpecifier :
|
|
|
|
// BindingIdentifier
|
|
|
|
// IdentifierName 'as' BindingIdentifier
|
|
|
|
|
|
|
|
Expect(Token::LBRACE, CHECK_OK);
|
|
|
|
|
2016-07-18 07:27:10 +00:00
|
|
|
auto result = new (zone()) ZoneList<const NamedImport*>(1, zone());
|
2015-02-25 19:40:39 +00:00
|
|
|
while (peek() != Token::RBRACE) {
|
|
|
|
const AstRawString* import_name = ParseIdentifierName(CHECK_OK);
|
|
|
|
const AstRawString* local_name = import_name;
|
2015-01-30 03:26:50 +00:00
|
|
|
// In the presence of 'as', the left-side of the 'as' can
|
|
|
|
// be any IdentifierName. But without 'as', it must be a valid
|
2015-02-25 19:40:39 +00:00
|
|
|
// BindingIdentifier.
|
2015-01-30 03:26:50 +00:00
|
|
|
if (CheckContextualKeyword(CStrVector("as"))) {
|
2015-02-25 19:40:39 +00:00
|
|
|
local_name = ParseIdentifierName(CHECK_OK);
|
|
|
|
}
|
2016-04-29 18:12:57 +00:00
|
|
|
if (!Token::IsIdentifier(scanner()->current_token(), STRICT, false,
|
|
|
|
parsing_module_)) {
|
2015-01-30 03:26:50 +00:00
|
|
|
*ok = false;
|
2015-05-18 08:34:05 +00:00
|
|
|
ReportMessage(MessageTemplate::kUnexpectedReserved);
|
2016-07-18 07:27:10 +00:00
|
|
|
return nullptr;
|
2015-02-25 19:40:39 +00:00
|
|
|
} else if (IsEvalOrArguments(local_name)) {
|
2015-01-30 03:26:50 +00:00
|
|
|
*ok = false;
|
2015-05-18 08:34:05 +00:00
|
|
|
ReportMessage(MessageTemplate::kStrictEvalArguments);
|
2016-07-18 07:27:10 +00:00
|
|
|
return nullptr;
|
2015-01-30 03:26:50 +00:00
|
|
|
}
|
2016-07-18 07:27:10 +00:00
|
|
|
|
2016-10-20 17:31:36 +00:00
|
|
|
DeclareModuleImport(local_name, position(), CHECK_OK);
|
2016-07-18 07:27:10 +00:00
|
|
|
|
|
|
|
NamedImport* import = new (zone()) NamedImport(
|
|
|
|
import_name, local_name, scanner()->location());
|
|
|
|
result->Add(import, zone());
|
|
|
|
|
2015-01-30 03:26:50 +00:00
|
|
|
if (peek() == Token::RBRACE) break;
|
|
|
|
Expect(Token::COMMA, CHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
Expect(Token::RBRACE, CHECK_OK);
|
2015-02-26 18:40:50 +00:00
|
|
|
return result;
|
2015-01-30 03:26:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-19 19:45:50 +00:00
|
|
|
void Parser::ParseImportDeclaration(bool* ok) {
|
2015-01-30 03:26:50 +00:00
|
|
|
// ImportDeclaration :
|
|
|
|
// 'import' ImportClause 'from' ModuleSpecifier ';'
|
|
|
|
// 'import' ModuleSpecifier ';'
|
2012-02-29 12:12:52 +00:00
|
|
|
//
|
2015-01-30 03:26:50 +00:00
|
|
|
// ImportClause :
|
2016-07-18 07:27:10 +00:00
|
|
|
// ImportedDefaultBinding
|
2015-01-30 03:26:50 +00:00
|
|
|
// NameSpaceImport
|
|
|
|
// NamedImports
|
|
|
|
// ImportedDefaultBinding ',' NameSpaceImport
|
|
|
|
// ImportedDefaultBinding ',' NamedImports
|
|
|
|
//
|
|
|
|
// NameSpaceImport :
|
|
|
|
// '*' 'as' ImportedBinding
|
2012-02-29 12:12:52 +00:00
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2016-07-19 19:45:50 +00:00
|
|
|
Expect(Token::IMPORT, CHECK_OK_VOID);
|
2015-01-30 03:26:50 +00:00
|
|
|
|
|
|
|
Token::Value tok = peek();
|
|
|
|
|
|
|
|
// 'import' ModuleSpecifier ';'
|
|
|
|
if (tok == Token::STRING) {
|
2016-07-19 19:45:50 +00:00
|
|
|
const AstRawString* module_specifier = ParseModuleSpecifier(CHECK_OK_VOID);
|
|
|
|
ExpectSemicolon(CHECK_OK_VOID);
|
2016-09-23 19:35:46 +00:00
|
|
|
module()->AddEmptyImport(module_specifier);
|
2016-07-19 19:45:50 +00:00
|
|
|
return;
|
2015-01-30 03:26:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse ImportedDefaultBinding if present.
|
2016-07-18 07:27:10 +00:00
|
|
|
const AstRawString* import_default_binding = nullptr;
|
|
|
|
Scanner::Location import_default_binding_loc;
|
2015-01-30 03:26:50 +00:00
|
|
|
if (tok != Token::MUL && tok != Token::LBRACE) {
|
2016-07-18 07:27:10 +00:00
|
|
|
import_default_binding =
|
2016-07-19 19:45:50 +00:00
|
|
|
ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK_VOID);
|
2016-07-18 07:27:10 +00:00
|
|
|
import_default_binding_loc = scanner()->location();
|
2016-10-20 17:31:36 +00:00
|
|
|
DeclareModuleImport(import_default_binding, pos, CHECK_OK_VOID);
|
2015-01-30 03:26:50 +00:00
|
|
|
}
|
|
|
|
|
2016-07-18 07:27:10 +00:00
|
|
|
// Parse NameSpaceImport or NamedImports if present.
|
|
|
|
const AstRawString* module_namespace_binding = nullptr;
|
|
|
|
Scanner::Location module_namespace_binding_loc;
|
|
|
|
const ZoneList<const NamedImport*>* named_imports = nullptr;
|
|
|
|
if (import_default_binding == nullptr || Check(Token::COMMA)) {
|
2015-01-30 03:26:50 +00:00
|
|
|
switch (peek()) {
|
|
|
|
case Token::MUL: {
|
|
|
|
Consume(Token::MUL);
|
2016-07-19 19:45:50 +00:00
|
|
|
ExpectContextualKeyword(CStrVector("as"), CHECK_OK_VOID);
|
2016-07-18 07:27:10 +00:00
|
|
|
module_namespace_binding =
|
2016-07-19 19:45:50 +00:00
|
|
|
ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK_VOID);
|
2016-07-18 07:27:10 +00:00
|
|
|
module_namespace_binding_loc = scanner()->location();
|
2016-08-09 08:48:34 +00:00
|
|
|
DeclareVariable(module_namespace_binding, CONST, kCreatedInitialized,
|
|
|
|
pos, CHECK_OK_VOID);
|
2015-01-30 03:26:50 +00:00
|
|
|
break;
|
|
|
|
}
|
2012-02-29 12:12:52 +00:00
|
|
|
|
2015-01-30 03:26:50 +00:00
|
|
|
case Token::LBRACE:
|
2016-07-19 19:45:50 +00:00
|
|
|
named_imports = ParseNamedImports(pos, CHECK_OK_VOID);
|
2015-01-30 03:26:50 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
*ok = false;
|
|
|
|
ReportUnexpectedToken(scanner()->current_token());
|
2016-07-19 19:45:50 +00:00
|
|
|
return;
|
2015-01-30 03:26:50 +00:00
|
|
|
}
|
2012-02-29 12:12:52 +00:00
|
|
|
}
|
|
|
|
|
2016-07-19 19:45:50 +00:00
|
|
|
ExpectContextualKeyword(CStrVector("from"), CHECK_OK_VOID);
|
|
|
|
const AstRawString* module_specifier = ParseModuleSpecifier(CHECK_OK_VOID);
|
|
|
|
ExpectSemicolon(CHECK_OK_VOID);
|
2015-04-09 22:09:44 +00:00
|
|
|
|
2016-07-18 07:27:10 +00:00
|
|
|
// Now that we have all the information, we can make the appropriate
|
|
|
|
// declarations.
|
|
|
|
|
2016-08-09 08:48:34 +00:00
|
|
|
// TODO(neis): Would prefer to call DeclareVariable for each case below rather
|
|
|
|
// than above and in ParseNamedImports, but then a possible error message
|
|
|
|
// would point to the wrong location. Maybe have a DeclareAt version of
|
|
|
|
// Declare that takes a location?
|
2016-07-18 07:27:10 +00:00
|
|
|
|
2016-08-08 09:46:51 +00:00
|
|
|
if (module_namespace_binding != nullptr) {
|
|
|
|
module()->AddStarImport(module_namespace_binding, module_specifier,
|
|
|
|
module_namespace_binding_loc, zone());
|
|
|
|
}
|
|
|
|
|
2016-07-18 07:27:10 +00:00
|
|
|
if (import_default_binding != nullptr) {
|
2016-07-19 10:06:38 +00:00
|
|
|
module()->AddImport(ast_value_factory()->default_string(),
|
|
|
|
import_default_binding, module_specifier,
|
|
|
|
import_default_binding_loc, zone());
|
2015-01-30 03:26:50 +00:00
|
|
|
}
|
2015-01-27 21:06:36 +00:00
|
|
|
|
2016-07-18 07:27:10 +00:00
|
|
|
if (named_imports != nullptr) {
|
|
|
|
if (named_imports->length() == 0) {
|
2016-09-23 19:35:46 +00:00
|
|
|
module()->AddEmptyImport(module_specifier);
|
2016-07-18 07:27:10 +00:00
|
|
|
} else {
|
|
|
|
for (int i = 0; i < named_imports->length(); ++i) {
|
|
|
|
const NamedImport* import = named_imports->at(i);
|
2016-07-19 10:06:38 +00:00
|
|
|
module()->AddImport(import->import_name, import->local_name,
|
|
|
|
module_specifier, import->location, zone());
|
2016-07-18 07:27:10 +00:00
|
|
|
}
|
2015-02-26 18:40:50 +00:00
|
|
|
}
|
2012-02-29 12:12:52 +00:00
|
|
|
}
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-28 19:18:37 +00:00
|
|
|
Statement* Parser::ParseExportDefault(bool* ok) {
|
|
|
|
// Supports the following productions, starting after the 'default' token:
|
2016-07-18 07:27:10 +00:00
|
|
|
// 'export' 'default' HoistableDeclaration
|
2015-01-28 19:18:37 +00:00
|
|
|
// 'export' 'default' ClassDeclaration
|
|
|
|
// 'export' 'default' AssignmentExpression[In] ';'
|
|
|
|
|
2015-02-19 20:14:55 +00:00
|
|
|
Expect(Token::DEFAULT, CHECK_OK);
|
|
|
|
Scanner::Location default_loc = scanner()->location();
|
|
|
|
|
2016-07-18 07:27:10 +00:00
|
|
|
ZoneList<const AstRawString*> local_names(1, zone());
|
2016-01-15 20:38:16 +00:00
|
|
|
Statement* result = nullptr;
|
2015-01-28 19:18:37 +00:00
|
|
|
switch (peek()) {
|
2016-07-01 09:20:23 +00:00
|
|
|
case Token::FUNCTION:
|
2016-07-18 07:27:10 +00:00
|
|
|
result = ParseHoistableDeclaration(&local_names, true, CHECK_OK);
|
2015-01-28 19:18:37 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Token::CLASS:
|
2016-01-15 20:38:16 +00:00
|
|
|
Consume(Token::CLASS);
|
2016-07-18 07:27:10 +00:00
|
|
|
result = ParseClassDeclaration(&local_names, true, CHECK_OK);
|
2015-01-28 19:18:37 +00:00
|
|
|
break;
|
|
|
|
|
2016-05-16 23:17:13 +00:00
|
|
|
case Token::ASYNC:
|
|
|
|
if (allow_harmony_async_await() && PeekAhead() == Token::FUNCTION &&
|
|
|
|
!scanner()->HasAnyLineTerminatorAfterNext()) {
|
|
|
|
Consume(Token::ASYNC);
|
2016-07-18 07:27:10 +00:00
|
|
|
result = ParseAsyncFunctionDeclaration(&local_names, true, CHECK_OK);
|
2016-05-16 23:17:13 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* falls through */
|
|
|
|
|
2015-01-28 19:18:37 +00:00
|
|
|
default: {
|
2016-07-18 07:27:10 +00:00
|
|
|
int pos = position();
|
2016-02-19 15:58:57 +00:00
|
|
|
ExpressionClassifier classifier(this);
|
2016-09-01 08:58:15 +00:00
|
|
|
Expression* value = ParseAssignmentExpression(true, CHECK_OK);
|
|
|
|
RewriteNonPattern(CHECK_OK);
|
2016-07-18 07:27:10 +00:00
|
|
|
SetFunctionName(value, ast_value_factory()->default_string());
|
|
|
|
|
|
|
|
const AstRawString* local_name =
|
|
|
|
ast_value_factory()->star_default_star_string();
|
|
|
|
local_names.Add(local_name, zone());
|
|
|
|
|
|
|
|
// It's fine to declare this as CONST because the user has no way of
|
|
|
|
// writing to it.
|
2016-08-09 08:48:34 +00:00
|
|
|
Declaration* decl = DeclareVariable(local_name, CONST, pos, CHECK_OK);
|
|
|
|
decl->proxy()->var()->set_initializer_position(position());
|
2016-07-18 07:27:10 +00:00
|
|
|
|
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
2016-08-09 08:48:34 +00:00
|
|
|
Token::INIT, decl->proxy(), value, kNoSourcePosition);
|
2016-07-18 07:27:10 +00:00
|
|
|
result = factory()->NewExpressionStatement(assignment, kNoSourcePosition);
|
2015-04-22 12:35:05 +00:00
|
|
|
|
2015-01-28 19:18:37 +00:00
|
|
|
ExpectSemicolon(CHECK_OK);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-18 07:27:10 +00:00
|
|
|
DCHECK_EQ(local_names.length(), 1);
|
2016-07-19 10:06:38 +00:00
|
|
|
module()->AddExport(local_names.first(),
|
|
|
|
ast_value_factory()->default_string(), default_loc,
|
|
|
|
zone());
|
2015-01-28 19:18:37 +00:00
|
|
|
|
2016-07-18 07:27:10 +00:00
|
|
|
DCHECK_NOT_NULL(result);
|
2015-01-28 19:18:37 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2012-02-29 12:12:52 +00:00
|
|
|
Statement* Parser::ParseExportDeclaration(bool* ok) {
|
|
|
|
// ExportDeclaration:
|
2015-01-28 19:18:37 +00:00
|
|
|
// 'export' '*' 'from' ModuleSpecifier ';'
|
|
|
|
// 'export' ExportClause ('from' ModuleSpecifier)? ';'
|
|
|
|
// 'export' VariableStatement
|
|
|
|
// 'export' Declaration
|
|
|
|
// 'export' 'default' ... (handled in ParseExportDefault)
|
2012-02-29 12:12:52 +00:00
|
|
|
|
2015-01-28 19:18:37 +00:00
|
|
|
int pos = peek_position();
|
2012-02-29 12:12:52 +00:00
|
|
|
Expect(Token::EXPORT, CHECK_OK);
|
|
|
|
|
2016-07-18 07:27:10 +00:00
|
|
|
Statement* result = nullptr;
|
2014-06-24 14:03:24 +00:00
|
|
|
ZoneList<const AstRawString*> names(1, zone());
|
2012-02-29 12:12:52 +00:00
|
|
|
switch (peek()) {
|
2015-01-28 19:18:37 +00:00
|
|
|
case Token::DEFAULT:
|
|
|
|
return ParseExportDefault(ok);
|
|
|
|
|
|
|
|
case Token::MUL: {
|
|
|
|
Consume(Token::MUL);
|
|
|
|
ExpectContextualKeyword(CStrVector("from"), CHECK_OK);
|
2015-02-26 18:40:50 +00:00
|
|
|
const AstRawString* module_specifier = ParseModuleSpecifier(CHECK_OK);
|
2015-04-17 22:45:15 +00:00
|
|
|
ExpectSemicolon(CHECK_OK);
|
2016-07-19 10:06:38 +00:00
|
|
|
module()->AddStarExport(module_specifier, scanner()->location(), zone());
|
2015-02-19 20:14:55 +00:00
|
|
|
return factory()->NewEmptyStatement(pos);
|
2012-02-29 12:12:52 +00:00
|
|
|
}
|
|
|
|
|
2015-01-30 03:26:50 +00:00
|
|
|
case Token::LBRACE: {
|
|
|
|
// There are two cases here:
|
|
|
|
//
|
|
|
|
// 'export' ExportClause ';'
|
|
|
|
// and
|
|
|
|
// 'export' ExportClause FromClause ';'
|
|
|
|
//
|
|
|
|
// In the first case, the exported identifiers in ExportClause must
|
|
|
|
// not be reserved words, while in the latter they may be. We
|
|
|
|
// pass in a location that gets filled with the first reserved word
|
|
|
|
// encountered, and then throw a SyntaxError if we are in the
|
|
|
|
// non-FromClause case.
|
|
|
|
Scanner::Location reserved_loc = Scanner::Location::invalid();
|
2015-02-19 20:14:55 +00:00
|
|
|
ZoneList<const AstRawString*> export_names(1, zone());
|
|
|
|
ZoneList<Scanner::Location> export_locations(1, zone());
|
2016-07-18 07:27:10 +00:00
|
|
|
ZoneList<const AstRawString*> original_names(1, zone());
|
|
|
|
ParseExportClause(&export_names, &export_locations, &original_names,
|
2015-02-19 20:14:55 +00:00
|
|
|
&reserved_loc, CHECK_OK);
|
2016-07-18 07:27:10 +00:00
|
|
|
const AstRawString* module_specifier = nullptr;
|
2015-01-28 19:18:37 +00:00
|
|
|
if (CheckContextualKeyword(CStrVector("from"))) {
|
2016-07-18 07:27:10 +00:00
|
|
|
module_specifier = ParseModuleSpecifier(CHECK_OK);
|
2015-01-30 03:26:50 +00:00
|
|
|
} else if (reserved_loc.IsValid()) {
|
|
|
|
// No FromClause, so reserved words are invalid in ExportClause.
|
|
|
|
*ok = false;
|
2015-05-18 08:34:05 +00:00
|
|
|
ReportMessageAt(reserved_loc, MessageTemplate::kUnexpectedReserved);
|
2016-07-18 07:27:10 +00:00
|
|
|
return nullptr;
|
2015-01-28 19:18:37 +00:00
|
|
|
}
|
|
|
|
ExpectSemicolon(CHECK_OK);
|
2015-02-19 20:14:55 +00:00
|
|
|
const int length = export_names.length();
|
2016-07-18 07:27:10 +00:00
|
|
|
DCHECK_EQ(length, original_names.length());
|
2015-02-19 20:14:55 +00:00
|
|
|
DCHECK_EQ(length, export_locations.length());
|
2016-07-18 07:27:10 +00:00
|
|
|
if (module_specifier == nullptr) {
|
2015-02-19 20:14:55 +00:00
|
|
|
for (int i = 0; i < length; ++i) {
|
2016-07-19 10:06:38 +00:00
|
|
|
module()->AddExport(original_names[i], export_names[i],
|
|
|
|
export_locations[i], zone());
|
2015-02-19 20:14:55 +00:00
|
|
|
}
|
2016-07-18 07:27:10 +00:00
|
|
|
} else if (length == 0) {
|
2016-09-23 19:35:46 +00:00
|
|
|
module()->AddEmptyImport(module_specifier);
|
2015-02-19 20:14:55 +00:00
|
|
|
} else {
|
|
|
|
for (int i = 0; i < length; ++i) {
|
2016-07-19 10:06:38 +00:00
|
|
|
module()->AddExport(original_names[i], export_names[i],
|
|
|
|
module_specifier, export_locations[i], zone());
|
2015-02-19 20:14:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return factory()->NewEmptyStatement(pos);
|
2015-01-30 03:26:50 +00:00
|
|
|
}
|
2015-01-28 19:18:37 +00:00
|
|
|
|
2012-02-29 12:12:52 +00:00
|
|
|
case Token::FUNCTION:
|
2016-07-01 09:20:23 +00:00
|
|
|
result = ParseHoistableDeclaration(&names, false, CHECK_OK);
|
2012-02-29 12:12:52 +00:00
|
|
|
break;
|
|
|
|
|
2014-09-16 22:15:39 +00:00
|
|
|
case Token::CLASS:
|
2016-01-15 20:38:16 +00:00
|
|
|
Consume(Token::CLASS);
|
2016-07-01 09:20:23 +00:00
|
|
|
result = ParseClassDeclaration(&names, false, CHECK_OK);
|
2014-09-16 22:15:39 +00:00
|
|
|
break;
|
|
|
|
|
2012-02-29 12:12:52 +00:00
|
|
|
case Token::VAR:
|
|
|
|
case Token::LET:
|
|
|
|
case Token::CONST:
|
2015-01-27 21:06:36 +00:00
|
|
|
result = ParseVariableStatement(kStatementListItem, &names, CHECK_OK);
|
2012-02-29 12:12:52 +00:00
|
|
|
break;
|
|
|
|
|
2016-05-16 23:17:13 +00:00
|
|
|
case Token::ASYNC:
|
|
|
|
if (allow_harmony_async_await()) {
|
2016-07-18 07:27:10 +00:00
|
|
|
// TODO(neis): Why don't we have the same check here as in
|
|
|
|
// ParseStatementListItem?
|
2016-05-16 23:17:13 +00:00
|
|
|
Consume(Token::ASYNC);
|
2016-07-01 09:20:23 +00:00
|
|
|
result = ParseAsyncFunctionDeclaration(&names, false, CHECK_OK);
|
2016-05-16 23:17:13 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* falls through */
|
|
|
|
|
2012-02-29 12:12:52 +00:00
|
|
|
default:
|
|
|
|
*ok = false;
|
2014-02-14 12:13:33 +00:00
|
|
|
ReportUnexpectedToken(scanner()->current_token());
|
2016-07-18 07:27:10 +00:00
|
|
|
return nullptr;
|
2012-02-29 12:12:52 +00:00
|
|
|
}
|
|
|
|
|
2016-07-19 10:06:38 +00:00
|
|
|
ModuleDescriptor* descriptor = module();
|
2014-07-30 13:54:45 +00:00
|
|
|
for (int i = 0; i < names.length(); ++i) {
|
2016-07-18 07:27:10 +00:00
|
|
|
// TODO(neis): Provide better location.
|
|
|
|
descriptor->AddExport(names[i], names[i], scanner()->location(), zone());
|
2012-02-29 12:12:52 +00:00
|
|
|
}
|
|
|
|
|
2015-02-19 20:14:55 +00:00
|
|
|
DCHECK_NOT_NULL(result);
|
2012-02-29 12:12:52 +00:00
|
|
|
return result;
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
|
2016-08-11 12:04:02 +00:00
|
|
|
VariableProxy* Parser::NewUnresolved(const AstRawString* name, int begin_pos,
|
2016-09-09 10:55:31 +00:00
|
|
|
int end_pos, VariableKind kind) {
|
2016-08-11 12:04:02 +00:00
|
|
|
return scope()->NewUnresolved(factory(), name, begin_pos, end_pos, kind);
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2016-08-11 12:04:02 +00:00
|
|
|
VariableProxy* Parser::NewUnresolved(const AstRawString* name) {
|
|
|
|
return scope()->NewUnresolved(factory(), name, scanner()->location().beg_pos,
|
|
|
|
scanner()->location().end_pos);
|
2012-02-28 10:12:39 +00:00
|
|
|
}
|
|
|
|
|
2016-08-09 08:48:34 +00:00
|
|
|
Declaration* Parser::DeclareVariable(const AstRawString* name,
|
|
|
|
VariableMode mode, int pos, bool* ok) {
|
2016-09-07 08:47:48 +00:00
|
|
|
return DeclareVariable(name, mode, Variable::DefaultInitializationFlag(mode),
|
|
|
|
pos, ok);
|
2016-08-09 08:48:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Declaration* Parser::DeclareVariable(const AstRawString* name,
|
|
|
|
VariableMode mode, InitializationFlag init,
|
|
|
|
int pos, bool* ok) {
|
2016-08-08 10:23:34 +00:00
|
|
|
DCHECK_NOT_NULL(name);
|
2016-09-19 20:07:01 +00:00
|
|
|
VariableProxy* proxy = factory()->NewVariableProxy(
|
|
|
|
name, NORMAL_VARIABLE, scanner()->location().beg_pos,
|
|
|
|
scanner()->location().end_pos);
|
2016-07-18 07:27:10 +00:00
|
|
|
Declaration* declaration =
|
2016-08-11 12:04:02 +00:00
|
|
|
factory()->NewVariableDeclaration(proxy, this->scope(), pos);
|
2016-08-10 08:10:18 +00:00
|
|
|
Declare(declaration, DeclarationDescriptor::NORMAL, mode, init, CHECK_OK);
|
2016-08-09 08:48:34 +00:00
|
|
|
return declaration;
|
2016-07-18 07:27:10 +00:00
|
|
|
}
|
|
|
|
|
2016-10-20 17:31:36 +00:00
|
|
|
Declaration* Parser::DeclareModuleImport(const AstRawString* name, int pos,
|
|
|
|
bool* ok) {
|
|
|
|
DCHECK_EQ(MODULE_SCOPE, scope()->scope_type());
|
|
|
|
Declaration* decl =
|
|
|
|
DeclareVariable(name, CONST, kNeedsInitialization, pos, CHECK_OK);
|
|
|
|
// Allocate imports eagerly as hole check elimination logic in scope
|
|
|
|
// analisys depends on identifying imports.
|
|
|
|
// TODO(adamk): It's weird to allocate imports long before everything
|
|
|
|
// else. We should find a different way of filtering out imports
|
|
|
|
// during hole check elimination.
|
|
|
|
decl->proxy()->var()->AllocateTo(VariableLocation::MODULE,
|
|
|
|
Variable::kModuleImportIndex);
|
|
|
|
return decl;
|
|
|
|
}
|
|
|
|
|
2015-06-22 14:15:53 +00:00
|
|
|
Variable* Parser::Declare(Declaration* declaration,
|
|
|
|
DeclarationDescriptor::Kind declaration_kind,
|
2016-08-10 08:10:18 +00:00
|
|
|
VariableMode mode, InitializationFlag init, bool* ok,
|
|
|
|
Scope* scope) {
|
2016-08-29 12:36:30 +00:00
|
|
|
if (scope == nullptr) {
|
|
|
|
scope = this->scope();
|
|
|
|
}
|
|
|
|
bool sloppy_mode_block_scope_function_redefinition = false;
|
|
|
|
Variable* variable = scope->DeclareVariable(
|
|
|
|
declaration, mode, init, allow_harmony_restrictive_generators(),
|
|
|
|
&sloppy_mode_block_scope_function_redefinition, ok);
|
|
|
|
if (!*ok) {
|
|
|
|
if (declaration_kind == DeclarationDescriptor::NORMAL) {
|
|
|
|
ReportMessage(MessageTemplate::kVarRedeclaration,
|
|
|
|
declaration->proxy()->raw_name());
|
|
|
|
} else {
|
|
|
|
ReportMessage(MessageTemplate::kParamDupe);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2016-08-31 18:58:21 +00:00
|
|
|
return nullptr;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2016-08-29 12:36:30 +00:00
|
|
|
if (sloppy_mode_block_scope_function_redefinition) {
|
|
|
|
++use_counts_[v8::Isolate::kSloppyModeBlockScopedFunctionRedefinition];
|
|
|
|
}
|
|
|
|
return variable;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2016-08-31 08:31:36 +00:00
|
|
|
Block* Parser::BuildInitializationBlock(
|
|
|
|
DeclarationParsingResult* parsing_result,
|
2015-05-15 09:56:31 +00:00
|
|
|
ZoneList<const AstRawString*>* names, bool* ok) {
|
2016-08-31 08:31:36 +00:00
|
|
|
Block* result = factory()->NewBlock(
|
|
|
|
NULL, 1, true, parsing_result->descriptor.declaration_pos);
|
|
|
|
for (auto declaration : parsing_result->declarations) {
|
2015-05-15 09:56:31 +00:00
|
|
|
PatternRewriter::DeclareAndInitializeVariables(
|
2016-08-31 08:31:36 +00:00
|
|
|
this, result, &(parsing_result->descriptor), &declaration, names,
|
|
|
|
CHECK_OK);
|
2015-05-15 09:56:31 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-09-01 11:56:29 +00:00
|
|
|
void Parser::DeclareAndInitializeVariables(
|
|
|
|
Block* block, const DeclarationDescriptor* declaration_descriptor,
|
|
|
|
const DeclarationParsingResult::Declaration* declaration,
|
|
|
|
ZoneList<const AstRawString*>* names, bool* ok) {
|
|
|
|
DCHECK_NOT_NULL(block);
|
|
|
|
PatternRewriter::DeclareAndInitializeVariables(
|
|
|
|
this, block, declaration_descriptor, declaration, names, ok);
|
|
|
|
}
|
2015-05-15 09:56:31 +00:00
|
|
|
|
2016-09-09 07:57:59 +00:00
|
|
|
Statement* Parser::DeclareFunction(const AstRawString* variable_name,
|
|
|
|
FunctionLiteral* function, int pos,
|
|
|
|
bool is_generator, bool is_async,
|
|
|
|
ZoneList<const AstRawString*>* names,
|
|
|
|
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;
|
2016-09-19 20:07:01 +00:00
|
|
|
VariableProxy* proxy =
|
|
|
|
factory()->NewVariableProxy(variable_name, NORMAL_VARIABLE);
|
2016-09-09 07:57:59 +00:00
|
|
|
Declaration* declaration =
|
|
|
|
factory()->NewFunctionDeclaration(proxy, function, scope(), pos);
|
|
|
|
Declare(declaration, DeclarationDescriptor::NORMAL, mode, kCreatedInitialized,
|
|
|
|
CHECK_OK);
|
|
|
|
if (names) names->Add(variable_name, zone());
|
|
|
|
// 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.
|
|
|
|
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();
|
|
|
|
target_scope->DeclareSloppyBlockFunction(variable_name, delegate);
|
|
|
|
return delegate;
|
|
|
|
}
|
|
|
|
return factory()->NewEmptyStatement(kNoSourcePosition);
|
|
|
|
}
|
|
|
|
|
2016-09-28 13:42:20 +00:00
|
|
|
Statement* Parser::DeclareClass(const AstRawString* variable_name,
|
|
|
|
Expression* value,
|
|
|
|
ZoneList<const AstRawString*>* names,
|
|
|
|
int class_token_pos, int end_pos, bool* ok) {
|
|
|
|
Declaration* decl =
|
|
|
|
DeclareVariable(variable_name, LET, class_token_pos, CHECK_OK);
|
|
|
|
decl->proxy()->var()->set_initializer_position(end_pos);
|
|
|
|
Assignment* assignment = factory()->NewAssignment(Token::INIT, decl->proxy(),
|
|
|
|
value, class_token_pos);
|
|
|
|
Statement* assignment_statement =
|
|
|
|
factory()->NewExpressionStatement(assignment, kNoSourcePosition);
|
|
|
|
if (names) names->Add(variable_name, zone());
|
|
|
|
return assignment_statement;
|
|
|
|
}
|
|
|
|
|
|
|
|
Statement* Parser::DeclareNative(const AstRawString* name, int pos, bool* ok) {
|
|
|
|
// Make sure that the function containing the native declaration
|
|
|
|
// isn't lazily compiled. The extension structures are only
|
|
|
|
// accessible while parsing the first time not when reparsing
|
|
|
|
// because of lazy compilation.
|
|
|
|
GetClosureScope()->ForceEagerCompilation();
|
|
|
|
|
|
|
|
// TODO(1240846): It's weird that native function declarations are
|
|
|
|
// introduced dynamically when we meet their declarations, whereas
|
|
|
|
// other functions are set up when entering the surrounding scope.
|
|
|
|
Declaration* decl = DeclareVariable(name, VAR, pos, CHECK_OK);
|
|
|
|
NativeFunctionLiteral* lit =
|
|
|
|
factory()->NewNativeFunctionLiteral(name, extension_, kNoSourcePosition);
|
|
|
|
return factory()->NewExpressionStatement(
|
|
|
|
factory()->NewAssignment(Token::INIT, decl->proxy(), lit,
|
|
|
|
kNoSourcePosition),
|
|
|
|
pos);
|
|
|
|
}
|
|
|
|
|
2016-09-10 17:04:50 +00:00
|
|
|
ZoneList<const AstRawString*>* Parser::DeclareLabel(
|
|
|
|
ZoneList<const AstRawString*>* labels, VariableProxy* var, bool* ok) {
|
|
|
|
const AstRawString* label = var->raw_name();
|
|
|
|
// TODO(1240780): We don't check for redeclaration of labels
|
|
|
|
// during preparsing since keeping track of the set of active
|
|
|
|
// labels requires nontrivial changes to the way scopes are
|
|
|
|
// structured. However, these are probably changes we want to
|
|
|
|
// make later anyway so we should go back and fix this then.
|
|
|
|
if (ContainsLabel(labels, label) || TargetStackContainsLabel(label)) {
|
|
|
|
ReportMessage(MessageTemplate::kLabelRedeclaration, label);
|
|
|
|
*ok = false;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
if (labels == nullptr) {
|
|
|
|
labels = new (zone()) ZoneList<const AstRawString*>(1, zone());
|
|
|
|
}
|
|
|
|
labels->Add(label, zone());
|
|
|
|
// Remove the "ghost" variable that turned out to be a label
|
|
|
|
// from the top scope. This way, we don't try to resolve it
|
|
|
|
// during the scope processing.
|
|
|
|
scope()->RemoveUnresolved(var);
|
|
|
|
return labels;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Parser::ContainsLabel(ZoneList<const AstRawString*>* labels,
|
|
|
|
const AstRawString* label) {
|
|
|
|
DCHECK_NOT_NULL(label);
|
|
|
|
if (labels != nullptr) {
|
|
|
|
for (int i = labels->length(); i-- > 0;) {
|
|
|
|
if (labels->at(i) == label) return true;
|
2014-06-05 08:41:29 +00:00
|
|
|
}
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-09-10 17:04:50 +00:00
|
|
|
Expression* Parser::RewriteReturn(Expression* return_value, int pos) {
|
|
|
|
if (IsSubclassConstructor(function_state_->kind())) {
|
|
|
|
// For subclass constructors we need to return this in case of undefined
|
|
|
|
// return a Smi (transformed into an exception in the ConstructStub)
|
|
|
|
// for a non object.
|
|
|
|
//
|
|
|
|
// return expr;
|
|
|
|
//
|
|
|
|
// Is rewritten as:
|
|
|
|
//
|
|
|
|
// return (temp = expr) === undefined ? this :
|
|
|
|
// %_IsJSReceiver(temp) ? temp : 1;
|
|
|
|
|
|
|
|
// temp = expr
|
|
|
|
Variable* temp = NewTemporary(ast_value_factory()->empty_string());
|
|
|
|
Assignment* assign = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, factory()->NewVariableProxy(temp), return_value, pos);
|
|
|
|
|
|
|
|
// %_IsJSReceiver(temp)
|
|
|
|
ZoneList<Expression*>* is_spec_object_args =
|
|
|
|
new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
is_spec_object_args->Add(factory()->NewVariableProxy(temp), zone());
|
|
|
|
Expression* is_spec_object_call = factory()->NewCallRuntime(
|
|
|
|
Runtime::kInlineIsJSReceiver, is_spec_object_args, pos);
|
|
|
|
|
|
|
|
// %_IsJSReceiver(temp) ? temp : 1;
|
|
|
|
Expression* is_object_conditional = factory()->NewConditional(
|
|
|
|
is_spec_object_call, factory()->NewVariableProxy(temp),
|
|
|
|
factory()->NewSmiLiteral(1, pos), pos);
|
|
|
|
|
|
|
|
// temp === undefined
|
|
|
|
Expression* is_undefined = factory()->NewCompareOperation(
|
|
|
|
Token::EQ_STRICT, assign,
|
|
|
|
factory()->NewUndefinedLiteral(kNoSourcePosition), pos);
|
|
|
|
|
|
|
|
// is_undefined ? this : is_object_conditional
|
|
|
|
return_value = factory()->NewConditional(is_undefined, ThisExpression(pos),
|
|
|
|
is_object_conditional, pos);
|
|
|
|
}
|
|
|
|
if (is_generator()) {
|
|
|
|
return_value = BuildIteratorResult(return_value, true);
|
|
|
|
} else if (is_async_function()) {
|
|
|
|
return_value = BuildResolvePromise(return_value, return_value->position());
|
|
|
|
}
|
|
|
|
return return_value;
|
|
|
|
}
|
|
|
|
|
2016-09-10 18:04:54 +00:00
|
|
|
Expression* Parser::RewriteDoExpression(Block* body, int pos, bool* ok) {
|
|
|
|
Variable* result = NewTemporary(ast_value_factory()->dot_result_string());
|
|
|
|
DoExpression* expr = factory()->NewDoExpression(body, result, pos);
|
|
|
|
if (!Rewriter::Rewrite(this, GetClosureScope(), expr, ast_value_factory())) {
|
|
|
|
*ok = false;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
2016-09-12 09:39:34 +00:00
|
|
|
Statement* Parser::RewriteSwitchStatement(Expression* tag,
|
|
|
|
SwitchStatement* switch_statement,
|
|
|
|
ZoneList<CaseClause*>* cases,
|
|
|
|
Scope* scope) {
|
2015-08-24 18:57:08 +00:00
|
|
|
// In order to get the CaseClauses to execute in their own lexical scope,
|
|
|
|
// but without requiring downstream code to have special scope handling
|
|
|
|
// code for switch statements, desugar into blocks as follows:
|
|
|
|
// { // To group the statements--harmless to evaluate Expression in scope
|
|
|
|
// .tag_variable = Expression;
|
|
|
|
// { // To give CaseClauses a scope
|
|
|
|
// switch (.tag_variable) { CaseClause* }
|
|
|
|
// }
|
|
|
|
// }
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2016-06-30 09:26:30 +00:00
|
|
|
Block* switch_block = factory()->NewBlock(NULL, 2, false, kNoSourcePosition);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2015-08-24 18:57:08 +00:00
|
|
|
Variable* tag_variable =
|
2016-08-09 19:49:25 +00:00
|
|
|
NewTemporary(ast_value_factory()->dot_switch_tag_string());
|
2015-08-24 18:57:08 +00:00
|
|
|
Assignment* tag_assign = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, factory()->NewVariableProxy(tag_variable), tag,
|
|
|
|
tag->position());
|
|
|
|
Statement* tag_statement =
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewExpressionStatement(tag_assign, kNoSourcePosition);
|
2015-10-01 13:59:36 +00:00
|
|
|
switch_block->statements()->Add(tag_statement, zone());
|
2015-08-24 18:57:08 +00:00
|
|
|
|
2015-08-28 22:43:00 +00:00
|
|
|
// make statement: undefined;
|
|
|
|
// This is needed so the tag isn't returned as the value, in case the switch
|
|
|
|
// statements don't have a value.
|
2015-10-01 13:59:36 +00:00
|
|
|
switch_block->statements()->Add(
|
2015-08-28 22:43:00 +00:00
|
|
|
factory()->NewExpressionStatement(
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewUndefinedLiteral(kNoSourcePosition), kNoSourcePosition),
|
2015-08-28 22:43:00 +00:00
|
|
|
zone());
|
|
|
|
|
2016-09-12 09:39:34 +00:00
|
|
|
Expression* tag_read = factory()->NewVariableProxy(tag_variable);
|
|
|
|
switch_statement->Initialize(tag_read, cases);
|
2016-06-30 09:26:30 +00:00
|
|
|
Block* cases_block = factory()->NewBlock(NULL, 1, false, kNoSourcePosition);
|
2016-09-12 09:39:34 +00:00
|
|
|
cases_block->statements()->Add(switch_statement, zone());
|
|
|
|
cases_block->set_scope(scope);
|
2015-10-01 13:59:36 +00:00
|
|
|
switch_block->statements()->Add(cases_block, zone());
|
2015-08-24 18:57:08 +00:00
|
|
|
return switch_block;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2016-09-16 09:12:14 +00:00
|
|
|
void Parser::RewriteCatchPattern(CatchInfo* catch_info, bool* ok) {
|
|
|
|
if (catch_info->name == nullptr) {
|
|
|
|
DCHECK_NOT_NULL(catch_info->pattern);
|
|
|
|
catch_info->name = ast_value_factory()->dot_catch_string();
|
|
|
|
}
|
|
|
|
catch_info->variable = catch_info->scope->DeclareLocal(
|
|
|
|
catch_info->name, VAR, kCreatedInitialized, NORMAL_VARIABLE);
|
|
|
|
if (catch_info->pattern != nullptr) {
|
|
|
|
DeclarationDescriptor descriptor;
|
|
|
|
descriptor.declaration_kind = DeclarationDescriptor::NORMAL;
|
|
|
|
descriptor.scope = scope();
|
|
|
|
descriptor.hoist_scope = nullptr;
|
|
|
|
descriptor.mode = LET;
|
|
|
|
descriptor.declaration_pos = catch_info->pattern->position();
|
|
|
|
descriptor.initialization_pos = catch_info->pattern->position();
|
|
|
|
|
|
|
|
// Initializer position for variables declared by the pattern.
|
|
|
|
const int initializer_position = position();
|
|
|
|
|
|
|
|
DeclarationParsingResult::Declaration decl(
|
|
|
|
catch_info->pattern, initializer_position,
|
|
|
|
factory()->NewVariableProxy(catch_info->variable));
|
|
|
|
|
|
|
|
catch_info->init_block =
|
|
|
|
factory()->NewBlock(nullptr, 8, true, kNoSourcePosition);
|
|
|
|
PatternRewriter::DeclareAndInitializeVariables(
|
|
|
|
this, catch_info->init_block, &descriptor, &decl,
|
|
|
|
&catch_info->bound_names, ok);
|
|
|
|
} else {
|
|
|
|
catch_info->bound_names.Add(catch_info->name, zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2016-09-16 09:12:14 +00:00
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2016-09-16 09:12:14 +00:00
|
|
|
void Parser::ValidateCatchBlock(const CatchInfo& catch_info, bool* ok) {
|
|
|
|
// Check for `catch(e) { let e; }` and similar errors.
|
|
|
|
Scope* inner_block_scope = catch_info.inner_block->scope();
|
|
|
|
if (inner_block_scope != nullptr) {
|
|
|
|
Declaration* decl = inner_block_scope->CheckLexDeclarationsConflictingWith(
|
|
|
|
catch_info.bound_names);
|
|
|
|
if (decl != nullptr) {
|
|
|
|
const AstRawString* name = decl->proxy()->raw_name();
|
|
|
|
int position = decl->proxy()->position();
|
|
|
|
Scanner::Location location =
|
|
|
|
position == kNoSourcePosition
|
|
|
|
? Scanner::Location::invalid()
|
|
|
|
: Scanner::Location(position, position + 1);
|
|
|
|
ReportMessageAt(location, MessageTemplate::kVarRedeclaration, name);
|
|
|
|
*ok = false;
|
2015-11-05 20:21:20 +00:00
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2016-09-16 09:12:14 +00:00
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2016-09-16 09:12:14 +00:00
|
|
|
Statement* Parser::RewriteTryStatement(Block* try_block, Block* catch_block,
|
|
|
|
Block* finally_block,
|
|
|
|
const CatchInfo& catch_info, int pos) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// Simplify the AST nodes by converting:
|
2011-06-08 13:55:33 +00:00
|
|
|
// 'try B0 catch B1 finally B2'
|
2008-07-03 15:10:15 +00:00
|
|
|
// to:
|
2011-06-08 13:55:33 +00:00
|
|
|
// 'try { try B0 catch B1 } finally B2'
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2016-09-16 09:12:14 +00:00
|
|
|
if (catch_block != nullptr && finally_block != nullptr) {
|
2011-06-30 14:37:55 +00:00
|
|
|
// If we have both, create an inner try/catch.
|
2016-09-16 09:12:14 +00:00
|
|
|
DCHECK_NOT_NULL(catch_info.scope);
|
|
|
|
DCHECK_NOT_NULL(catch_info.variable);
|
2016-07-22 06:04:07 +00:00
|
|
|
TryCatchStatement* statement;
|
2016-09-16 09:12:14 +00:00
|
|
|
if (catch_info.for_promise_reject) {
|
2016-07-22 06:04:07 +00:00
|
|
|
statement = factory()->NewTryCatchStatementForPromiseReject(
|
2016-09-16 09:12:14 +00:00
|
|
|
try_block, catch_info.scope, catch_info.variable, catch_block,
|
2016-07-22 06:04:07 +00:00
|
|
|
kNoSourcePosition);
|
|
|
|
} else {
|
2016-09-16 09:12:14 +00:00
|
|
|
statement = factory()->NewTryCatchStatement(
|
|
|
|
try_block, catch_info.scope, catch_info.variable, catch_block,
|
|
|
|
kNoSourcePosition);
|
2016-07-22 06:04:07 +00:00
|
|
|
}
|
|
|
|
|
2016-09-16 09:12:14 +00:00
|
|
|
try_block = factory()->NewBlock(nullptr, 1, false, kNoSourcePosition);
|
2015-10-01 13:59:36 +00:00
|
|
|
try_block->statements()->Add(statement, zone());
|
2016-09-16 09:12:14 +00:00
|
|
|
catch_block = nullptr; // Clear to indicate it's been handled.
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2016-09-16 09:12:14 +00:00
|
|
|
if (catch_block != nullptr) {
|
2016-02-11 17:39:43 +00:00
|
|
|
// For a try-catch construct append return expressions from the catch block
|
|
|
|
// to the list of return expressions.
|
2016-05-04 13:42:52 +00:00
|
|
|
function_state_->tail_call_expressions().Append(
|
2016-09-16 09:12:14 +00:00
|
|
|
catch_info.tail_call_expressions);
|
2016-02-11 17:39:43 +00:00
|
|
|
|
2016-09-16 09:12:14 +00:00
|
|
|
DCHECK_NULL(finally_block);
|
|
|
|
DCHECK_NOT_NULL(catch_info.scope);
|
|
|
|
DCHECK_NOT_NULL(catch_info.variable);
|
|
|
|
return factory()->NewTryCatchStatement(
|
|
|
|
try_block, catch_info.scope, catch_info.variable, catch_block, pos);
|
2010-11-02 11:45:47 +00:00
|
|
|
} else {
|
2016-09-16 09:12:14 +00:00
|
|
|
DCHECK_NOT_NULL(finally_block);
|
|
|
|
return factory()->NewTryFinallyStatement(try_block, finally_block, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-30 11:56:11 +00:00
|
|
|
// !%_IsJSReceiver(result = iterator.next()) &&
|
2015-05-20 08:08:26 +00:00
|
|
|
// %ThrowIteratorResultNotAnObject(result)
|
|
|
|
Expression* Parser::BuildIteratorNextResult(Expression* iterator,
|
|
|
|
Variable* result, int pos) {
|
|
|
|
Expression* next_literal = factory()->NewStringLiteral(
|
2016-06-30 09:26:30 +00:00
|
|
|
ast_value_factory()->next_string(), kNoSourcePosition);
|
2015-05-20 08:08:26 +00:00
|
|
|
Expression* next_property =
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewProperty(iterator, next_literal, kNoSourcePosition);
|
2015-05-20 08:08:26 +00:00
|
|
|
ZoneList<Expression*>* next_arguments =
|
|
|
|
new (zone()) ZoneList<Expression*>(0, zone());
|
|
|
|
Expression* next_call =
|
|
|
|
factory()->NewCall(next_property, next_arguments, pos);
|
|
|
|
Expression* result_proxy = factory()->NewVariableProxy(result);
|
|
|
|
Expression* left =
|
|
|
|
factory()->NewAssignment(Token::ASSIGN, result_proxy, next_call, pos);
|
|
|
|
|
2015-11-30 11:56:11 +00:00
|
|
|
// %_IsJSReceiver(...)
|
2015-05-20 08:08:26 +00:00
|
|
|
ZoneList<Expression*>* is_spec_object_args =
|
|
|
|
new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
is_spec_object_args->Add(left, zone());
|
|
|
|
Expression* is_spec_object_call = factory()->NewCallRuntime(
|
2015-11-30 11:56:11 +00:00
|
|
|
Runtime::kInlineIsJSReceiver, is_spec_object_args, pos);
|
2015-05-20 08:08:26 +00:00
|
|
|
|
|
|
|
// %ThrowIteratorResultNotAnObject(result)
|
|
|
|
Expression* result_proxy_again = factory()->NewVariableProxy(result);
|
|
|
|
ZoneList<Expression*>* throw_arguments =
|
|
|
|
new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
throw_arguments->Add(result_proxy_again, zone());
|
|
|
|
Expression* throw_call = factory()->NewCallRuntime(
|
2015-08-26 11:16:38 +00:00
|
|
|
Runtime::kThrowIteratorResultNotAnObject, throw_arguments, pos);
|
2015-05-20 08:08:26 +00:00
|
|
|
|
|
|
|
return factory()->NewBinaryOperation(
|
|
|
|
Token::AND,
|
|
|
|
factory()->NewUnaryOperation(Token::NOT, is_spec_object_call, pos),
|
|
|
|
throw_call, pos);
|
|
|
|
}
|
|
|
|
|
2016-07-07 08:15:11 +00:00
|
|
|
Statement* Parser::InitializeForEachStatement(ForEachStatement* stmt,
|
|
|
|
Expression* each,
|
|
|
|
Expression* subject,
|
|
|
|
Statement* body,
|
|
|
|
int each_keyword_pos) {
|
2013-06-07 11:12:21 +00:00
|
|
|
ForOfStatement* for_of = stmt->AsForOfStatement();
|
|
|
|
if (for_of != NULL) {
|
2016-07-07 08:15:11 +00:00
|
|
|
const bool finalize = true;
|
|
|
|
return InitializeForOfStatement(for_of, each, subject, body, finalize,
|
|
|
|
each_keyword_pos);
|
2013-06-07 11:12:21 +00:00
|
|
|
} else {
|
2016-03-07 19:52:12 +00:00
|
|
|
if (each->IsArrayLiteral() || each->IsObjectLiteral()) {
|
2016-08-09 19:49:25 +00:00
|
|
|
Variable* temp = NewTemporary(ast_value_factory()->empty_string());
|
2015-12-11 19:38:57 +00:00
|
|
|
VariableProxy* temp_proxy = factory()->NewVariableProxy(temp);
|
|
|
|
Expression* assign_each = PatternRewriter::RewriteDestructuringAssignment(
|
|
|
|
this, factory()->NewAssignment(Token::ASSIGN, each, temp_proxy,
|
2016-06-30 09:26:30 +00:00
|
|
|
kNoSourcePosition),
|
2016-07-19 10:06:38 +00:00
|
|
|
scope());
|
2016-06-30 09:26:30 +00:00
|
|
|
auto block = factory()->NewBlock(nullptr, 2, false, kNoSourcePosition);
|
|
|
|
block->statements()->Add(
|
|
|
|
factory()->NewExpressionStatement(assign_each, kNoSourcePosition),
|
|
|
|
zone());
|
2015-12-11 19:38:57 +00:00
|
|
|
block->statements()->Add(body, zone());
|
|
|
|
body = block;
|
|
|
|
each = factory()->NewVariableProxy(temp);
|
|
|
|
}
|
2016-05-12 16:24:59 +00:00
|
|
|
stmt->AsForInStatement()->Initialize(each, subject, body);
|
2013-06-07 11:12:21 +00:00
|
|
|
}
|
2016-07-07 08:15:11 +00:00
|
|
|
return stmt;
|
2013-06-07 11:12:21 +00:00
|
|
|
}
|
|
|
|
|
2016-09-21 10:38:56 +00:00
|
|
|
// Special case for legacy for
|
|
|
|
//
|
|
|
|
// for (var x = initializer in enumerable) body
|
|
|
|
//
|
|
|
|
// An initialization block of the form
|
|
|
|
//
|
|
|
|
// {
|
|
|
|
// x = initializer;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// is returned in this case. It has reserved space for two statements,
|
|
|
|
// so that (later on during parsing), the equivalent of
|
|
|
|
//
|
|
|
|
// for (x in enumerable) body
|
|
|
|
//
|
|
|
|
// is added as a second statement to it.
|
|
|
|
Block* Parser::RewriteForVarInLegacy(const ForInfo& for_info) {
|
|
|
|
const DeclarationParsingResult::Declaration& decl =
|
|
|
|
for_info.parsing_result.declarations[0];
|
|
|
|
if (!IsLexicalVariableMode(for_info.parsing_result.descriptor.mode) &&
|
|
|
|
decl.pattern->IsVariableProxy() && decl.initializer != nullptr) {
|
|
|
|
++use_counts_[v8::Isolate::kForInInitializer];
|
|
|
|
const AstRawString* name = decl.pattern->AsVariableProxy()->raw_name();
|
|
|
|
VariableProxy* single_var = NewUnresolved(name);
|
|
|
|
Block* init_block = factory()->NewBlock(
|
|
|
|
nullptr, 2, true, for_info.parsing_result.descriptor.declaration_pos);
|
|
|
|
init_block->statements()->Add(
|
|
|
|
factory()->NewExpressionStatement(
|
|
|
|
factory()->NewAssignment(Token::ASSIGN, single_var,
|
|
|
|
decl.initializer, kNoSourcePosition),
|
|
|
|
kNoSourcePosition),
|
|
|
|
zone());
|
|
|
|
return init_block;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Rewrite a for-in/of statement of the form
|
|
|
|
//
|
|
|
|
// for (let/const/var x in/of e) b
|
|
|
|
//
|
|
|
|
// into
|
|
|
|
//
|
|
|
|
// {
|
|
|
|
// <let x' be a temporary variable>
|
|
|
|
// for (x' in/of e) {
|
|
|
|
// let/const/var x;
|
|
|
|
// x = x';
|
|
|
|
// b;
|
|
|
|
// }
|
|
|
|
// let x; // for TDZ
|
|
|
|
// }
|
|
|
|
void Parser::DesugarBindingInForEachStatement(ForInfo* for_info,
|
|
|
|
Block** body_block,
|
|
|
|
Expression** each_variable,
|
|
|
|
bool* ok) {
|
|
|
|
DeclarationParsingResult::Declaration& decl =
|
|
|
|
for_info->parsing_result.declarations[0];
|
|
|
|
Variable* temp = NewTemporary(ast_value_factory()->dot_for_string());
|
|
|
|
auto each_initialization_block =
|
|
|
|
factory()->NewBlock(nullptr, 1, true, kNoSourcePosition);
|
|
|
|
{
|
|
|
|
auto descriptor = for_info->parsing_result.descriptor;
|
|
|
|
descriptor.declaration_pos = kNoSourcePosition;
|
|
|
|
descriptor.initialization_pos = kNoSourcePosition;
|
|
|
|
decl.initializer = factory()->NewVariableProxy(temp);
|
|
|
|
|
|
|
|
bool is_for_var_of =
|
|
|
|
for_info->mode == ForEachStatement::ITERATE &&
|
|
|
|
for_info->parsing_result.descriptor.mode == VariableMode::VAR;
|
|
|
|
|
|
|
|
PatternRewriter::DeclareAndInitializeVariables(
|
|
|
|
this, each_initialization_block, &descriptor, &decl,
|
|
|
|
(IsLexicalVariableMode(for_info->parsing_result.descriptor.mode) ||
|
|
|
|
is_for_var_of)
|
|
|
|
? &for_info->bound_names
|
|
|
|
: nullptr,
|
|
|
|
CHECK_OK_VOID);
|
|
|
|
|
|
|
|
// Annex B.3.5 prohibits the form
|
|
|
|
// `try {} catch(e) { for (var e of {}); }`
|
|
|
|
// So if we are parsing a statement like `for (var ... of ...)`
|
|
|
|
// we need to walk up the scope chain and look for catch scopes
|
|
|
|
// which have a simple binding, then compare their binding against
|
|
|
|
// all of the names declared in the init of the for-of we're
|
|
|
|
// parsing.
|
|
|
|
if (is_for_var_of) {
|
|
|
|
Scope* catch_scope = scope();
|
|
|
|
while (catch_scope != nullptr && !catch_scope->is_declaration_scope()) {
|
|
|
|
if (catch_scope->is_catch_scope()) {
|
|
|
|
auto name = catch_scope->catch_variable_name();
|
|
|
|
// If it's a simple binding and the name is declared in the for loop.
|
|
|
|
if (name != ast_value_factory()->dot_catch_string() &&
|
|
|
|
for_info->bound_names.Contains(name)) {
|
|
|
|
ReportMessageAt(for_info->parsing_result.bindings_loc,
|
|
|
|
MessageTemplate::kVarRedeclaration, name);
|
|
|
|
*ok = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch_scope = catch_scope->outer_scope();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*body_block = factory()->NewBlock(nullptr, 3, false, kNoSourcePosition);
|
|
|
|
(*body_block)->statements()->Add(each_initialization_block, zone());
|
|
|
|
*each_variable = factory()->NewVariableProxy(temp, for_info->each_loc.beg_pos,
|
|
|
|
for_info->each_loc.end_pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a TDZ for any lexically-bound names in for in/of statements.
|
|
|
|
Block* Parser::CreateForEachStatementTDZ(Block* init_block,
|
|
|
|
const ForInfo& for_info, bool* ok) {
|
|
|
|
if (IsLexicalVariableMode(for_info.parsing_result.descriptor.mode)) {
|
|
|
|
DCHECK_NULL(init_block);
|
|
|
|
|
|
|
|
init_block = factory()->NewBlock(nullptr, 1, false, kNoSourcePosition);
|
|
|
|
|
|
|
|
for (int i = 0; i < for_info.bound_names.length(); ++i) {
|
|
|
|
// TODO(adamk): This needs to be some sort of special
|
|
|
|
// INTERNAL variable that's invisible to the debugger
|
|
|
|
// but visible to everything else.
|
|
|
|
Declaration* tdz_decl = DeclareVariable(for_info.bound_names[i], LET,
|
|
|
|
kNoSourcePosition, CHECK_OK);
|
|
|
|
tdz_decl->proxy()->var()->set_initializer_position(position());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return init_block;
|
|
|
|
}
|
|
|
|
|
2016-07-07 08:15:11 +00:00
|
|
|
Statement* Parser::InitializeForOfStatement(ForOfStatement* for_of,
|
|
|
|
Expression* each,
|
|
|
|
Expression* iterable,
|
|
|
|
Statement* body, bool finalize,
|
|
|
|
int next_result_pos) {
|
|
|
|
// Create the auxiliary expressions needed for iterating over the iterable,
|
|
|
|
// and initialize the given ForOfStatement with them.
|
|
|
|
// If finalize is true, also instrument the loop with code that performs the
|
|
|
|
// proper ES6 iterator finalization. In that case, the result is not
|
|
|
|
// immediately a ForOfStatement.
|
|
|
|
|
|
|
|
const int nopos = kNoSourcePosition;
|
|
|
|
auto avfactory = ast_value_factory();
|
|
|
|
|
2016-08-09 19:49:25 +00:00
|
|
|
Variable* iterator = NewTemporary(ast_value_factory()->dot_iterator_string());
|
|
|
|
Variable* result = NewTemporary(ast_value_factory()->dot_result_string());
|
|
|
|
Variable* completion = NewTemporary(avfactory->empty_string());
|
2016-03-07 19:52:12 +00:00
|
|
|
|
|
|
|
// iterator = iterable[Symbol.iterator]()
|
2016-07-07 08:15:11 +00:00
|
|
|
Expression* assign_iterator;
|
|
|
|
{
|
|
|
|
assign_iterator = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, factory()->NewVariableProxy(iterator),
|
2016-08-25 08:44:59 +00:00
|
|
|
GetIterator(iterable, iterable->position()), iterable->position());
|
2016-07-07 08:15:11 +00:00
|
|
|
}
|
2016-03-07 19:52:12 +00:00
|
|
|
|
|
|
|
// !%_IsJSReceiver(result = iterator.next()) &&
|
|
|
|
// %ThrowIteratorResultNotAnObject(result)
|
2016-07-07 08:15:11 +00:00
|
|
|
Expression* next_result;
|
2016-03-07 19:52:12 +00:00
|
|
|
{
|
|
|
|
Expression* iterator_proxy = factory()->NewVariableProxy(iterator);
|
|
|
|
next_result =
|
|
|
|
BuildIteratorNextResult(iterator_proxy, result, next_result_pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// result.done
|
2016-07-07 08:15:11 +00:00
|
|
|
Expression* result_done;
|
2016-03-07 19:52:12 +00:00
|
|
|
{
|
|
|
|
Expression* done_literal = factory()->NewStringLiteral(
|
2016-06-30 09:26:30 +00:00
|
|
|
ast_value_factory()->done_string(), kNoSourcePosition);
|
2016-03-07 19:52:12 +00:00
|
|
|
Expression* result_proxy = factory()->NewVariableProxy(result);
|
2016-06-30 09:26:30 +00:00
|
|
|
result_done =
|
|
|
|
factory()->NewProperty(result_proxy, done_literal, kNoSourcePosition);
|
2016-03-07 19:52:12 +00:00
|
|
|
}
|
|
|
|
|
2016-07-07 08:15:11 +00:00
|
|
|
// result.value
|
|
|
|
Expression* result_value;
|
2016-03-07 19:52:12 +00:00
|
|
|
{
|
2016-07-07 08:15:11 +00:00
|
|
|
Expression* value_literal =
|
|
|
|
factory()->NewStringLiteral(avfactory->value_string(), nopos);
|
2016-03-07 19:52:12 +00:00
|
|
|
Expression* result_proxy = factory()->NewVariableProxy(result);
|
2016-07-07 08:15:11 +00:00
|
|
|
result_value = factory()->NewProperty(result_proxy, value_literal, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// {{completion = kAbruptCompletion;}}
|
|
|
|
Statement* set_completion_abrupt;
|
|
|
|
if (finalize) {
|
|
|
|
Expression* proxy = factory()->NewVariableProxy(completion);
|
|
|
|
Expression* assignment = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, proxy,
|
|
|
|
factory()->NewSmiLiteral(Parser::kAbruptCompletion, nopos), nopos);
|
|
|
|
|
|
|
|
Block* block = factory()->NewBlock(nullptr, 1, true, nopos);
|
|
|
|
block->statements()->Add(
|
|
|
|
factory()->NewExpressionStatement(assignment, nopos), zone());
|
|
|
|
set_completion_abrupt = block;
|
|
|
|
}
|
|
|
|
|
|
|
|
// do { let tmp = #result_value; #set_completion_abrupt; tmp }
|
|
|
|
// Expression* result_value (gets overwritten)
|
|
|
|
if (finalize) {
|
2016-08-09 19:49:25 +00:00
|
|
|
Variable* var_tmp = NewTemporary(avfactory->empty_string());
|
2016-07-07 08:15:11 +00:00
|
|
|
Expression* tmp = factory()->NewVariableProxy(var_tmp);
|
|
|
|
Expression* assignment =
|
|
|
|
factory()->NewAssignment(Token::ASSIGN, tmp, result_value, nopos);
|
|
|
|
|
|
|
|
Block* block = factory()->NewBlock(nullptr, 2, false, nopos);
|
|
|
|
block->statements()->Add(
|
|
|
|
factory()->NewExpressionStatement(assignment, nopos), zone());
|
|
|
|
block->statements()->Add(set_completion_abrupt, zone());
|
|
|
|
|
|
|
|
result_value = factory()->NewDoExpression(block, var_tmp, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// each = #result_value;
|
|
|
|
Expression* assign_each;
|
|
|
|
{
|
|
|
|
assign_each =
|
|
|
|
factory()->NewAssignment(Token::ASSIGN, each, result_value, nopos);
|
2016-03-07 19:52:12 +00:00
|
|
|
if (each->IsArrayLiteral() || each->IsObjectLiteral()) {
|
|
|
|
assign_each = PatternRewriter::RewriteDestructuringAssignment(
|
2016-07-19 10:06:38 +00:00
|
|
|
this, assign_each->AsAssignment(), scope());
|
2016-03-07 19:52:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-07 08:15:11 +00:00
|
|
|
// {{completion = kNormalCompletion;}}
|
|
|
|
Statement* set_completion_normal;
|
|
|
|
if (finalize) {
|
|
|
|
Expression* proxy = factory()->NewVariableProxy(completion);
|
|
|
|
Expression* assignment = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, proxy,
|
|
|
|
factory()->NewSmiLiteral(Parser::kNormalCompletion, nopos), nopos);
|
|
|
|
|
|
|
|
Block* block = factory()->NewBlock(nullptr, 1, true, nopos);
|
|
|
|
block->statements()->Add(
|
|
|
|
factory()->NewExpressionStatement(assignment, nopos), zone());
|
|
|
|
set_completion_normal = block;
|
|
|
|
}
|
|
|
|
|
|
|
|
// { #loop-body; #set_completion_normal }
|
|
|
|
// Statement* body (gets overwritten)
|
|
|
|
if (finalize) {
|
|
|
|
Block* block = factory()->NewBlock(nullptr, 2, false, nopos);
|
|
|
|
block->statements()->Add(body, zone());
|
|
|
|
block->statements()->Add(set_completion_normal, zone());
|
|
|
|
body = block;
|
|
|
|
}
|
|
|
|
|
2016-05-12 16:24:59 +00:00
|
|
|
for_of->Initialize(body, iterator, assign_iterator, next_result, result_done,
|
|
|
|
assign_each);
|
2016-08-19 08:11:04 +00:00
|
|
|
return finalize ? FinalizeForOfStatement(for_of, completion, nopos) : for_of;
|
2016-03-07 19:52:12 +00:00
|
|
|
}
|
|
|
|
|
2015-03-03 18:34:30 +00:00
|
|
|
Statement* Parser::DesugarLexicalBindingsInForStatement(
|
2014-06-24 14:03:24 +00:00
|
|
|
ForStatement* loop, Statement* init, Expression* cond, Statement* next,
|
2016-09-21 10:38:56 +00:00
|
|
|
Statement* body, Scope* inner_scope, const ForInfo& for_info, bool* ok) {
|
2015-10-08 13:56:49 +00:00
|
|
|
// ES6 13.7.4.8 specifies that on each loop iteration the let variables are
|
|
|
|
// copied into a new environment. Moreover, the "next" statement must be
|
|
|
|
// evaluated not in the environment of the just completed iteration but in
|
|
|
|
// that of the upcoming one. We achieve this with the following desugaring.
|
|
|
|
// Extra care is needed to preserve the completion value of the original loop.
|
2014-05-26 08:07:02 +00:00
|
|
|
//
|
2015-10-08 13:56:49 +00:00
|
|
|
// We are given a for statement of the form
|
2014-05-26 08:07:02 +00:00
|
|
|
//
|
2015-03-03 18:34:30 +00:00
|
|
|
// labels: for (let/const x = i; cond; next) body
|
2014-05-26 08:07:02 +00:00
|
|
|
//
|
2015-10-08 13:56:49 +00:00
|
|
|
// and rewrite it as follows. Here we write {{ ... }} for init-blocks, ie.,
|
|
|
|
// blocks whose ignore_completion_value_ flag is set.
|
2014-05-26 08:07:02 +00:00
|
|
|
//
|
|
|
|
// {
|
2015-03-03 18:34:30 +00:00
|
|
|
// let/const x = i;
|
2014-11-14 19:32:53 +00:00
|
|
|
// temp_x = x;
|
|
|
|
// first = 1;
|
2015-06-18 11:54:03 +00:00
|
|
|
// undefined;
|
2014-11-14 19:32:53 +00:00
|
|
|
// outer: for (;;) {
|
2015-10-08 13:56:49 +00:00
|
|
|
// let/const x = temp_x;
|
|
|
|
// {{ if (first == 1) {
|
|
|
|
// first = 0;
|
|
|
|
// } else {
|
|
|
|
// next;
|
|
|
|
// }
|
|
|
|
// flag = 1;
|
|
|
|
// if (!cond) break;
|
|
|
|
// }}
|
2014-11-14 19:32:53 +00:00
|
|
|
// labels: for (; flag == 1; flag = 0, temp_x = x) {
|
2015-10-08 13:56:49 +00:00
|
|
|
// body
|
2014-11-14 19:32:53 +00:00
|
|
|
// }
|
2015-10-08 13:56:49 +00:00
|
|
|
// {{ if (flag == 1) // Body used break.
|
|
|
|
// break;
|
|
|
|
// }}
|
2014-11-14 19:32:53 +00:00
|
|
|
// }
|
2014-05-26 08:07:02 +00:00
|
|
|
// }
|
|
|
|
|
2016-09-21 10:38:56 +00:00
|
|
|
DCHECK(for_info.bound_names.length() > 0);
|
|
|
|
ZoneList<Variable*> temps(for_info.bound_names.length(), zone());
|
2014-05-26 08:07:02 +00:00
|
|
|
|
2016-09-21 10:38:56 +00:00
|
|
|
Block* outer_block = factory()->NewBlock(
|
|
|
|
nullptr, for_info.bound_names.length() + 4, false, kNoSourcePosition);
|
2014-11-14 19:32:53 +00:00
|
|
|
|
2015-03-03 18:34:30 +00:00
|
|
|
// Add statement: let/const x = i.
|
2015-10-01 13:59:36 +00:00
|
|
|
outer_block->statements()->Add(init, zone());
|
2014-05-26 08:07:02 +00:00
|
|
|
|
2014-09-11 09:52:36 +00:00
|
|
|
const AstRawString* temp_name = ast_value_factory()->dot_for_string();
|
2014-05-26 08:07:02 +00:00
|
|
|
|
2015-03-03 18:34:30 +00:00
|
|
|
// For each lexical variable x:
|
2014-05-26 08:07:02 +00:00
|
|
|
// make statement: temp_x = x.
|
2016-09-21 10:38:56 +00:00
|
|
|
for (int i = 0; i < for_info.bound_names.length(); i++) {
|
|
|
|
VariableProxy* proxy = NewUnresolved(for_info.bound_names[i]);
|
2016-08-09 19:49:25 +00:00
|
|
|
Variable* temp = NewTemporary(temp_name);
|
2014-05-26 08:07:02 +00:00
|
|
|
VariableProxy* temp_proxy = factory()->NewVariableProxy(temp);
|
2016-06-30 09:26:30 +00:00
|
|
|
Assignment* assignment = factory()->NewAssignment(Token::ASSIGN, temp_proxy,
|
|
|
|
proxy, kNoSourcePosition);
|
|
|
|
Statement* assignment_statement =
|
|
|
|
factory()->NewExpressionStatement(assignment, kNoSourcePosition);
|
2015-10-01 13:59:36 +00:00
|
|
|
outer_block->statements()->Add(assignment_statement, zone());
|
2014-05-26 08:07:02 +00:00
|
|
|
temps.Add(temp, zone());
|
|
|
|
}
|
|
|
|
|
2014-11-14 19:32:53 +00:00
|
|
|
Variable* first = NULL;
|
|
|
|
// Make statement: first = 1.
|
|
|
|
if (next) {
|
2016-08-09 19:49:25 +00:00
|
|
|
first = NewTemporary(temp_name);
|
2014-11-14 19:32:53 +00:00
|
|
|
VariableProxy* first_proxy = factory()->NewVariableProxy(first);
|
2016-06-30 09:26:30 +00:00
|
|
|
Expression* const1 = factory()->NewSmiLiteral(1, kNoSourcePosition);
|
2014-05-26 08:07:02 +00:00
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
2016-06-30 09:26:30 +00:00
|
|
|
Token::ASSIGN, first_proxy, const1, kNoSourcePosition);
|
2014-11-14 19:32:53 +00:00
|
|
|
Statement* assignment_statement =
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewExpressionStatement(assignment, kNoSourcePosition);
|
2015-10-01 13:59:36 +00:00
|
|
|
outer_block->statements()->Add(assignment_statement, zone());
|
2014-05-26 08:07:02 +00:00
|
|
|
}
|
|
|
|
|
2015-06-18 11:54:03 +00:00
|
|
|
// make statement: undefined;
|
2015-10-01 13:59:36 +00:00
|
|
|
outer_block->statements()->Add(
|
2015-06-18 11:54:03 +00:00
|
|
|
factory()->NewExpressionStatement(
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewUndefinedLiteral(kNoSourcePosition), kNoSourcePosition),
|
2015-06-18 11:54:03 +00:00
|
|
|
zone());
|
|
|
|
|
2014-11-14 19:32:53 +00:00
|
|
|
// Make statement: outer: for (;;)
|
|
|
|
// Note that we don't actually create the label, or set this loop up as an
|
|
|
|
// explicit break target, instead handing it directly to those nodes that
|
|
|
|
// need to know about it. This should be safe because we don't run any code
|
|
|
|
// in this function that looks up break targets.
|
|
|
|
ForStatement* outer_loop =
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewForStatement(NULL, kNoSourcePosition);
|
2015-10-01 13:59:36 +00:00
|
|
|
outer_block->statements()->Add(outer_loop, zone());
|
2016-07-19 10:06:38 +00:00
|
|
|
outer_block->set_scope(scope());
|
2014-05-26 08:07:02 +00:00
|
|
|
|
2016-06-30 09:26:30 +00:00
|
|
|
Block* inner_block = factory()->NewBlock(NULL, 3, false, kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
{
|
2016-07-19 10:06:38 +00:00
|
|
|
BlockState block_state(&scope_state_, inner_scope);
|
2016-01-14 19:25:09 +00:00
|
|
|
|
2016-09-21 10:38:56 +00:00
|
|
|
Block* ignore_completion_block = factory()->NewBlock(
|
|
|
|
nullptr, for_info.bound_names.length() + 3, true, kNoSourcePosition);
|
|
|
|
ZoneList<Variable*> inner_vars(for_info.bound_names.length(), zone());
|
2016-01-14 19:25:09 +00:00
|
|
|
// For each let variable x:
|
|
|
|
// make statement: let/const x = temp_x.
|
2016-09-21 10:38:56 +00:00
|
|
|
for (int i = 0; i < for_info.bound_names.length(); i++) {
|
|
|
|
Declaration* decl = DeclareVariable(
|
|
|
|
for_info.bound_names[i], for_info.parsing_result.descriptor.mode,
|
|
|
|
kNoSourcePosition, CHECK_OK);
|
2016-08-09 08:48:34 +00:00
|
|
|
inner_vars.Add(decl->proxy()->var(), zone());
|
2016-01-14 19:25:09 +00:00
|
|
|
VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i));
|
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
2016-08-09 08:48:34 +00:00
|
|
|
Token::INIT, decl->proxy(), temp_proxy, kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
Statement* assignment_statement =
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewExpressionStatement(assignment, kNoSourcePosition);
|
|
|
|
DCHECK(init->position() != kNoSourcePosition);
|
2016-08-09 08:48:34 +00:00
|
|
|
decl->proxy()->var()->set_initializer_position(init->position());
|
2016-01-14 19:25:09 +00:00
|
|
|
ignore_completion_block->statements()->Add(assignment_statement, zone());
|
|
|
|
}
|
2014-05-26 08:07:02 +00:00
|
|
|
|
2016-01-14 19:25:09 +00:00
|
|
|
// Make statement: if (first == 1) { first = 0; } else { next; }
|
|
|
|
if (next) {
|
|
|
|
DCHECK(first);
|
|
|
|
Expression* compare = NULL;
|
|
|
|
// Make compare expression: first == 1.
|
|
|
|
{
|
2016-06-30 09:26:30 +00:00
|
|
|
Expression* const1 = factory()->NewSmiLiteral(1, kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
VariableProxy* first_proxy = factory()->NewVariableProxy(first);
|
|
|
|
compare = factory()->NewCompareOperation(Token::EQ, first_proxy, const1,
|
2016-06-30 09:26:30 +00:00
|
|
|
kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
}
|
|
|
|
Statement* clear_first = NULL;
|
|
|
|
// Make statement: first = 0.
|
|
|
|
{
|
|
|
|
VariableProxy* first_proxy = factory()->NewVariableProxy(first);
|
2016-06-30 09:26:30 +00:00
|
|
|
Expression* const0 = factory()->NewSmiLiteral(0, kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
2016-06-30 09:26:30 +00:00
|
|
|
Token::ASSIGN, first_proxy, const0, kNoSourcePosition);
|
|
|
|
clear_first =
|
|
|
|
factory()->NewExpressionStatement(assignment, kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
}
|
|
|
|
Statement* clear_first_or_next = factory()->NewIfStatement(
|
2016-06-30 09:26:30 +00:00
|
|
|
compare, clear_first, next, kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
ignore_completion_block->statements()->Add(clear_first_or_next, zone());
|
2014-05-26 08:07:02 +00:00
|
|
|
}
|
2016-01-14 19:25:09 +00:00
|
|
|
|
2016-08-09 19:49:25 +00:00
|
|
|
Variable* flag = NewTemporary(temp_name);
|
2016-01-14 19:25:09 +00:00
|
|
|
// Make statement: flag = 1.
|
2014-05-26 08:07:02 +00:00
|
|
|
{
|
2016-01-14 19:25:09 +00:00
|
|
|
VariableProxy* flag_proxy = factory()->NewVariableProxy(flag);
|
2016-06-30 09:26:30 +00:00
|
|
|
Expression* const1 = factory()->NewSmiLiteral(1, kNoSourcePosition);
|
2014-05-26 08:07:02 +00:00
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
2016-06-30 09:26:30 +00:00
|
|
|
Token::ASSIGN, flag_proxy, const1, kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
Statement* assignment_statement =
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewExpressionStatement(assignment, kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
ignore_completion_block->statements()->Add(assignment_statement, zone());
|
2014-05-26 08:07:02 +00:00
|
|
|
}
|
2015-10-08 13:56:49 +00:00
|
|
|
|
2016-01-14 19:25:09 +00:00
|
|
|
// Make statement: if (!cond) break.
|
|
|
|
if (cond) {
|
|
|
|
Statement* stop =
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewBreakStatement(outer_loop, kNoSourcePosition);
|
|
|
|
Statement* noop = factory()->NewEmptyStatement(kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
ignore_completion_block->statements()->Add(
|
|
|
|
factory()->NewIfStatement(cond, noop, stop, cond->position()),
|
|
|
|
zone());
|
|
|
|
}
|
2014-05-26 08:07:02 +00:00
|
|
|
|
2016-01-14 19:25:09 +00:00
|
|
|
inner_block->statements()->Add(ignore_completion_block, zone());
|
|
|
|
// Make cond expression for main loop: flag == 1.
|
|
|
|
Expression* flag_cond = NULL;
|
2014-11-14 19:32:53 +00:00
|
|
|
{
|
2016-06-30 09:26:30 +00:00
|
|
|
Expression* const1 = factory()->NewSmiLiteral(1, kNoSourcePosition);
|
2014-11-14 19:32:53 +00:00
|
|
|
VariableProxy* flag_proxy = factory()->NewVariableProxy(flag);
|
2016-01-14 19:25:09 +00:00
|
|
|
flag_cond = factory()->NewCompareOperation(Token::EQ, flag_proxy, const1,
|
2016-06-30 09:26:30 +00:00
|
|
|
kNoSourcePosition);
|
2014-11-14 19:32:53 +00:00
|
|
|
}
|
2014-05-26 08:07:02 +00:00
|
|
|
|
2016-01-14 19:25:09 +00:00
|
|
|
// Create chain of expressions "flag = 0, temp_x = x, ..."
|
|
|
|
Statement* compound_next_statement = NULL;
|
|
|
|
{
|
|
|
|
Expression* compound_next = NULL;
|
|
|
|
// Make expression: flag = 0.
|
|
|
|
{
|
|
|
|
VariableProxy* flag_proxy = factory()->NewVariableProxy(flag);
|
2016-06-30 09:26:30 +00:00
|
|
|
Expression* const0 = factory()->NewSmiLiteral(0, kNoSourcePosition);
|
|
|
|
compound_next = factory()->NewAssignment(Token::ASSIGN, flag_proxy,
|
|
|
|
const0, kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
}
|
2014-11-14 19:32:53 +00:00
|
|
|
|
2016-01-14 19:25:09 +00:00
|
|
|
// Make the comma-separated list of temp_x = x assignments.
|
|
|
|
int inner_var_proxy_pos = scanner()->location().beg_pos;
|
2016-09-21 10:38:56 +00:00
|
|
|
for (int i = 0; i < for_info.bound_names.length(); i++) {
|
2016-01-14 19:25:09 +00:00
|
|
|
VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i));
|
|
|
|
VariableProxy* proxy =
|
|
|
|
factory()->NewVariableProxy(inner_vars.at(i), inner_var_proxy_pos);
|
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
2016-06-30 09:26:30 +00:00
|
|
|
Token::ASSIGN, temp_proxy, proxy, kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
compound_next = factory()->NewBinaryOperation(
|
2016-06-30 09:26:30 +00:00
|
|
|
Token::COMMA, compound_next, assignment, kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
}
|
2014-11-14 19:32:53 +00:00
|
|
|
|
2016-06-30 09:26:30 +00:00
|
|
|
compound_next_statement =
|
|
|
|
factory()->NewExpressionStatement(compound_next, kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
}
|
2014-05-26 08:07:02 +00:00
|
|
|
|
2016-01-14 19:25:09 +00:00
|
|
|
// Make statement: labels: for (; flag == 1; flag = 0, temp_x = x)
|
|
|
|
// Note that we re-use the original loop node, which retains its labels
|
|
|
|
// and ensures that any break or continue statements in body point to
|
|
|
|
// the right place.
|
|
|
|
loop->Initialize(NULL, flag_cond, compound_next_statement, body);
|
|
|
|
inner_block->statements()->Add(loop, zone());
|
|
|
|
|
|
|
|
// Make statement: {{if (flag == 1) break;}}
|
2014-11-14 19:32:53 +00:00
|
|
|
{
|
2016-01-14 19:25:09 +00:00
|
|
|
Expression* compare = NULL;
|
|
|
|
// Make compare expresion: flag == 1.
|
|
|
|
{
|
2016-06-30 09:26:30 +00:00
|
|
|
Expression* const1 = factory()->NewSmiLiteral(1, kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
VariableProxy* flag_proxy = factory()->NewVariableProxy(flag);
|
|
|
|
compare = factory()->NewCompareOperation(Token::EQ, flag_proxy, const1,
|
2016-06-30 09:26:30 +00:00
|
|
|
kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
}
|
|
|
|
Statement* stop =
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewBreakStatement(outer_loop, kNoSourcePosition);
|
|
|
|
Statement* empty = factory()->NewEmptyStatement(kNoSourcePosition);
|
|
|
|
Statement* if_flag_break =
|
|
|
|
factory()->NewIfStatement(compare, stop, empty, kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
Block* ignore_completion_block =
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewBlock(NULL, 1, true, kNoSourcePosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
ignore_completion_block->statements()->Add(if_flag_break, zone());
|
|
|
|
inner_block->statements()->Add(ignore_completion_block, zone());
|
|
|
|
}
|
2014-05-26 08:07:02 +00:00
|
|
|
|
2016-01-14 19:25:09 +00:00
|
|
|
inner_scope->set_end_position(scanner()->location().end_pos);
|
|
|
|
inner_block->set_scope(inner_scope);
|
|
|
|
}
|
2014-05-26 08:07:02 +00:00
|
|
|
|
2014-11-14 19:32:53 +00:00
|
|
|
outer_loop->Initialize(NULL, NULL, NULL, inner_block);
|
2014-05-26 08:07:02 +00:00
|
|
|
return outer_block;
|
|
|
|
}
|
|
|
|
|
2016-09-27 18:01:58 +00:00
|
|
|
void Parser::AddArrowFunctionFormalParameters(
|
2016-04-20 20:48:32 +00:00
|
|
|
ParserFormalParameters* parameters, Expression* expr, int end_pos,
|
|
|
|
bool* ok) {
|
2015-04-21 14:44:18 +00:00
|
|
|
// ArrowFunctionFormals ::
|
2015-06-10 16:11:25 +00:00
|
|
|
// Binary(Token::COMMA, NonTailArrowFunctionFormals, Tail)
|
|
|
|
// Tail
|
|
|
|
// NonTailArrowFunctionFormals ::
|
|
|
|
// Binary(Token::COMMA, NonTailArrowFunctionFormals, VariableProxy)
|
|
|
|
// VariableProxy
|
|
|
|
// Tail ::
|
2015-04-21 14:44:18 +00:00
|
|
|
// VariableProxy
|
2015-06-10 16:11:25 +00:00
|
|
|
// Spread(VariableProxy)
|
2015-04-21 14:44:18 +00:00
|
|
|
//
|
|
|
|
// As we need to visit the parameters in left-to-right order, we recurse on
|
|
|
|
// the left-hand side of comma expressions.
|
|
|
|
//
|
|
|
|
if (expr->IsBinaryOperation()) {
|
|
|
|
BinaryOperation* binop = expr->AsBinaryOperation();
|
2015-05-13 13:32:27 +00:00
|
|
|
// The classifier has already run, so we know that the expression is a valid
|
|
|
|
// arrow function formals production.
|
|
|
|
DCHECK_EQ(binop->op(), Token::COMMA);
|
2015-04-21 14:44:18 +00:00
|
|
|
Expression* left = binop->left();
|
|
|
|
Expression* right = binop->right();
|
2016-04-20 20:48:32 +00:00
|
|
|
int comma_pos = binop->position();
|
2016-09-27 18:01:58 +00:00
|
|
|
AddArrowFunctionFormalParameters(parameters, left, comma_pos,
|
|
|
|
CHECK_OK_VOID);
|
2015-04-21 14:44:18 +00:00
|
|
|
// LHS of comma expression should be unparenthesized.
|
|
|
|
expr = right;
|
|
|
|
}
|
2015-04-21 11:09:53 +00:00
|
|
|
|
2015-06-10 16:11:25 +00:00
|
|
|
// Only the right-most expression may be a rest parameter.
|
2015-07-23 11:53:31 +00:00
|
|
|
DCHECK(!parameters->has_rest);
|
2015-06-10 16:11:25 +00:00
|
|
|
|
2015-07-23 11:53:31 +00:00
|
|
|
bool is_rest = expr->IsSpread();
|
2015-07-23 14:28:59 +00:00
|
|
|
if (is_rest) {
|
|
|
|
expr = expr->AsSpread()->expression();
|
|
|
|
parameters->has_rest = true;
|
|
|
|
}
|
|
|
|
if (parameters->is_simple) {
|
|
|
|
parameters->is_simple = !is_rest && expr->IsVariableProxy();
|
|
|
|
}
|
2015-06-10 16:11:25 +00:00
|
|
|
|
2015-08-24 18:00:59 +00:00
|
|
|
Expression* initializer = nullptr;
|
2016-07-22 23:28:10 +00:00
|
|
|
if (expr->IsAssignment()) {
|
2015-08-24 18:00:59 +00:00
|
|
|
Assignment* assignment = expr->AsAssignment();
|
|
|
|
DCHECK(!assignment->is_compound());
|
|
|
|
initializer = assignment->value();
|
|
|
|
expr = assignment->target();
|
2015-08-17 12:01:55 +00:00
|
|
|
}
|
|
|
|
|
2016-04-20 20:48:32 +00:00
|
|
|
AddFormalParameter(parameters, expr, initializer, end_pos, is_rest);
|
2015-08-05 12:00:41 +00:00
|
|
|
}
|
|
|
|
|
2016-09-27 18:01:58 +00:00
|
|
|
void Parser::DeclareArrowFunctionFormalParameters(
|
2015-08-05 12:00:41 +00:00
|
|
|
ParserFormalParameters* parameters, Expression* expr,
|
2016-07-22 23:28:10 +00:00
|
|
|
const Scanner::Location& params_loc, Scanner::Location* duplicate_loc,
|
2016-09-27 18:01:58 +00:00
|
|
|
bool* ok) {
|
2015-08-26 09:36:39 +00:00
|
|
|
if (expr->IsEmptyParentheses()) return;
|
|
|
|
|
2016-09-27 18:01:58 +00:00
|
|
|
AddArrowFunctionFormalParameters(parameters, expr, params_loc.end_pos,
|
|
|
|
CHECK_OK_VOID);
|
2016-07-22 23:28:10 +00:00
|
|
|
|
2016-10-13 12:34:37 +00:00
|
|
|
if (parameters->arity > Code::kMaxArguments) {
|
2016-08-25 08:48:45 +00:00
|
|
|
ReportMessageAt(params_loc, MessageTemplate::kMalformedArrowFunParamList);
|
2016-04-20 20:48:32 +00:00
|
|
|
*ok = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-26 07:45:53 +00:00
|
|
|
ExpressionClassifier classifier(this);
|
2015-08-26 14:59:05 +00:00
|
|
|
if (!parameters->is_simple) {
|
2016-09-01 08:58:15 +00:00
|
|
|
this->classifier()->RecordNonSimpleParameter();
|
2015-08-26 14:59:05 +00:00
|
|
|
}
|
2016-10-13 12:34:37 +00:00
|
|
|
for (int i = 0; i < parameters->arity; ++i) {
|
2015-08-05 12:00:41 +00:00
|
|
|
auto parameter = parameters->at(i);
|
2016-09-01 08:58:15 +00:00
|
|
|
DeclareFormalParameter(parameters->scope, parameter);
|
2016-09-24 02:07:03 +00:00
|
|
|
if (!this->classifier()
|
|
|
|
->is_valid_formal_parameter_list_without_duplicates() &&
|
|
|
|
!duplicate_loc->IsValid()) {
|
2016-09-01 08:58:15 +00:00
|
|
|
*duplicate_loc =
|
|
|
|
this->classifier()->duplicate_formal_parameter_error().location;
|
2015-08-05 12:00:41 +00:00
|
|
|
}
|
2015-06-10 16:11:25 +00:00
|
|
|
}
|
2015-08-07 11:38:20 +00:00
|
|
|
DCHECK_EQ(parameters->is_simple, parameters->scope->has_simple_parameters());
|
Implement handling of arrow functions in the parser
Arrow functions are parsed from ParseAssignmentExpression(). Handling the
parameter list is done by letting ParseConditionalExpression() parse a comma
separated list of identifiers, and it returns a tree of BinaryOperation nodes
with VariableProxy leaves, or a single VariableProxy if there is only one
parameter. When the arrow token "=>" is found, the VariableProxy nodes are
passed to ParseArrowFunctionLiteral(), which will then skip parsing the
paramaeter list. This avoids having to rewind when the arrow is found and
restart parsing the parameter list.
Note that the empty parameter list "()" is handled directly in
ParsePrimaryExpression(): after is has consumed the opening parenthesis,
if a closing parenthesis follows, then the only valid input is an arrow
function. In this case, ParsePrimaryExpression() directly calls
ParseArrowFunctionLiteral(), to avoid needing to return a sentinel value
to signal the empty parameter list. Because it will consume the body of
the arrow function, ParseAssignmentExpression() will not see the arrow
"=>" token as next, and return the already-parser expression.
The implementation is done in ParserBase, so it was needed to do some
additions to ParserBase, ParserTraits and PreParserTraits. Some of the
glue code can be removed later on when more more functionality is moved
to ParserBase.
Additionally, this adds a runtime flag "harmony_arrow_functions"
(disabled by default); enabling "harmony" will enable it as well.
BUG=v8:2700
LOG=N
R=marja@chromium.org
Review URL: https://codereview.chromium.org/383983002
Patch from Adrián Pérez de Castro <aperez@igalia.com>.
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22366 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2014-07-14 07:55:45 +00:00
|
|
|
}
|
|
|
|
|
2016-08-25 08:48:45 +00:00
|
|
|
void Parser::ReindexLiterals(const ParserFormalParameters& parameters) {
|
|
|
|
if (function_state_->materialized_literal_count() > 0) {
|
2015-06-26 21:39:43 +00:00
|
|
|
AstLiteralReindexer reindexer;
|
|
|
|
|
2015-07-23 11:53:31 +00:00
|
|
|
for (const auto p : parameters.params) {
|
2015-06-26 21:39:43 +00:00
|
|
|
if (p.pattern != nullptr) reindexer.Reindex(p.pattern);
|
2015-11-13 17:10:37 +00:00
|
|
|
if (p.initializer != nullptr) reindexer.Reindex(p.initializer);
|
2015-06-26 21:39:43 +00:00
|
|
|
}
|
2015-09-02 21:10:51 +00:00
|
|
|
|
2016-08-25 08:48:45 +00:00
|
|
|
DCHECK(reindexer.count() <= function_state_->materialized_literal_count());
|
2015-06-26 21:39:43 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-30 07:53:43 +00:00
|
|
|
void Parser::PrepareGeneratorVariables(FunctionState* function_state) {
|
|
|
|
// For generators, allocating variables in contexts is currently a win
|
|
|
|
// because it minimizes the work needed to suspend and resume an
|
|
|
|
// activation. The machine code produced for generators (by full-codegen)
|
|
|
|
// relies on this forced context allocation, but not in an essential way.
|
|
|
|
scope()->ForceContextAllocation();
|
|
|
|
|
|
|
|
// Calling a generator returns a generator object. That object is stored
|
|
|
|
// in a temporary variable, a definition that is used by "yield"
|
|
|
|
// expressions.
|
|
|
|
Variable* temp =
|
|
|
|
NewTemporary(ast_value_factory()->dot_generator_object_string());
|
|
|
|
function_state->set_generator_object_variable(temp);
|
|
|
|
}
|
2015-06-26 21:39:43 +00:00
|
|
|
|
2013-06-06 13:28:22 +00:00
|
|
|
FunctionLiteral* Parser::ParseFunctionLiteral(
|
2014-09-10 16:39:42 +00:00
|
|
|
const AstRawString* function_name, Scanner::Location function_name_location,
|
2015-07-09 21:31:11 +00:00
|
|
|
FunctionNameValidity function_name_validity, FunctionKind kind,
|
|
|
|
int function_token_pos, FunctionLiteral::FunctionType function_type,
|
2015-07-15 09:14:49 +00:00
|
|
|
LanguageMode language_mode, bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// Function ::
|
|
|
|
// '(' FormalParameterList? ')' '{' FunctionBody '}'
|
2014-06-17 07:23:26 +00:00
|
|
|
//
|
|
|
|
// Getter ::
|
|
|
|
// '(' ')' '{' FunctionBody '}'
|
|
|
|
//
|
|
|
|
// Setter ::
|
|
|
|
// '(' PropertySetParameterList ')' '{' FunctionBody '}'
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2016-06-30 09:26:30 +00:00
|
|
|
int pos = function_token_pos == kNoSourcePosition ? peek_position()
|
|
|
|
: function_token_pos;
|
2013-10-14 09:24:58 +00:00
|
|
|
|
2014-09-10 16:39:42 +00:00
|
|
|
bool is_generator = IsGeneratorFunction(kind);
|
|
|
|
|
2011-08-08 16:14:46 +00:00
|
|
|
// Anonymous functions were passed either the empty symbol or a null
|
|
|
|
// handle as the function name. Remember if we were passed a non-empty
|
|
|
|
// handle to decide whether to invoke function name inference.
|
2014-06-24 14:03:24 +00:00
|
|
|
bool should_infer_name = function_name == NULL;
|
2011-08-08 16:14:46 +00:00
|
|
|
|
|
|
|
// We want a non-null handle as the function name.
|
|
|
|
if (should_infer_name) {
|
2014-09-11 09:52:36 +00:00
|
|
|
function_name = ast_value_factory()->empty_string();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2016-08-04 19:15:18 +00:00
|
|
|
FunctionLiteral::EagerCompileHint eager_compile_hint =
|
|
|
|
function_state_->next_function_is_parenthesized()
|
|
|
|
? FunctionLiteral::kShouldEagerCompile
|
2016-10-07 09:12:48 +00:00
|
|
|
: default_eager_compile_hint();
|
2016-08-04 19:15:18 +00:00
|
|
|
|
|
|
|
// Determine if the function can be parsed lazily. Lazy parsing is
|
|
|
|
// different from lazy compilation; we need to parse more eagerly than we
|
|
|
|
// compile.
|
|
|
|
|
|
|
|
// We can only parse lazily if we also compile lazily. The heuristics for lazy
|
|
|
|
// compilation are:
|
|
|
|
// - It must not have been prohibited by the caller to Parse (some callers
|
|
|
|
// need a full AST).
|
|
|
|
// - The outer scope must allow lazy compilation of inner functions.
|
|
|
|
// - The function mustn't be a function expression with an open parenthesis
|
|
|
|
// before; we consider that a hint that the function will be called
|
|
|
|
// immediately, and it would be a waste of time to make it lazily
|
|
|
|
// compiled.
|
|
|
|
// These are all things we can know at this point, without looking at the
|
|
|
|
// function itself.
|
|
|
|
|
2016-09-27 09:48:17 +00:00
|
|
|
// We separate between lazy parsing top level functions and lazy parsing inner
|
|
|
|
// functions, because the latter needs to do more work. In particular, we need
|
|
|
|
// to track unresolved variables to distinguish between these cases:
|
2016-08-04 19:15:18 +00:00
|
|
|
// (function foo() {
|
|
|
|
// bar = function() { return 1; }
|
|
|
|
// })();
|
|
|
|
// and
|
|
|
|
// (function foo() {
|
|
|
|
// var a = 1;
|
|
|
|
// bar = function() { return a; }
|
|
|
|
// })();
|
|
|
|
|
|
|
|
// Now foo will be parsed eagerly and compiled eagerly (optimization: assume
|
|
|
|
// parenthesis before the function means that it will be called
|
2016-09-27 09:48:17 +00:00
|
|
|
// immediately). bar can be parsed lazily, but we need to parse it in a mode
|
|
|
|
// that tracks unresolved variables.
|
|
|
|
DCHECK_IMPLIES(mode() == PARSE_LAZILY, FLAG_lazy);
|
|
|
|
DCHECK_IMPLIES(mode() == PARSE_LAZILY, allow_lazy());
|
|
|
|
DCHECK_IMPLIES(mode() == PARSE_LAZILY, extension_ == nullptr);
|
|
|
|
|
2016-10-11 12:13:37 +00:00
|
|
|
bool can_preparse = mode() == PARSE_LAZILY &&
|
|
|
|
eager_compile_hint == FunctionLiteral::kShouldLazyCompile;
|
|
|
|
|
2016-09-27 09:48:17 +00:00
|
|
|
bool is_lazy_top_level_function =
|
2016-10-13 12:21:43 +00:00
|
|
|
can_preparse && impl()->AllowsLazyParsingWithoutUnresolvedVariables();
|
2016-09-27 09:48:17 +00:00
|
|
|
|
|
|
|
// Determine whether we can still lazy parse the inner function.
|
2016-08-04 19:15:18 +00:00
|
|
|
// The preconditions are:
|
|
|
|
// - Lazy compilation has to be enabled.
|
|
|
|
// - Neither V8 natives nor native function declarations can be allowed,
|
|
|
|
// since parsing one would retroactively force the function to be
|
|
|
|
// eagerly compiled.
|
|
|
|
// - The invoker of this parser can't depend on the AST being eagerly
|
|
|
|
// built (either because the function is about to be compiled, or
|
|
|
|
// because the AST is going to be inspected for some reason).
|
|
|
|
// - Because of the above, we can't be attempting to parse a
|
|
|
|
// FunctionExpression; even without enclosing parentheses it might be
|
|
|
|
// immediately invoked.
|
|
|
|
// - The function literal shouldn't be hinted to eagerly compile.
|
|
|
|
// - For asm.js functions the body needs to be available when module
|
|
|
|
// validation is active, because we examine the entire module at once.
|
2016-09-27 09:48:17 +00:00
|
|
|
|
|
|
|
// Inner functions will be parsed using a temporary Zone. After parsing, we
|
|
|
|
// will migrate unresolved variable into a Scope in the main Zone.
|
|
|
|
// TODO(marja): Refactor parsing modes: simplify this.
|
2016-09-16 10:43:20 +00:00
|
|
|
bool use_temp_zone =
|
2016-10-11 12:13:37 +00:00
|
|
|
(FLAG_lazy_inner_functions
|
|
|
|
? can_preparse
|
2016-10-14 08:20:45 +00:00
|
|
|
: (is_lazy_top_level_function ||
|
|
|
|
(allow_lazy() && function_type == FunctionLiteral::kDeclaration &&
|
|
|
|
eager_compile_hint == FunctionLiteral::kShouldLazyCompile))) &&
|
2016-08-19 13:29:12 +00:00
|
|
|
!(FLAG_validate_asm && scope()->IsAsmModule());
|
2016-09-28 15:58:22 +00:00
|
|
|
bool is_lazy_inner_function =
|
|
|
|
use_temp_zone && FLAG_lazy_inner_functions && !is_lazy_top_level_function;
|
2016-08-04 19:15:18 +00:00
|
|
|
|
2016-09-28 13:36:30 +00:00
|
|
|
// This Scope lives in the main zone. We'll migrate data into that zone later.
|
|
|
|
DeclarationScope* scope = NewFunctionScope(kind);
|
|
|
|
SetLanguageMode(scope, language_mode);
|
2016-08-04 19:15:18 +00:00
|
|
|
|
|
|
|
ZoneList<Statement*>* body = nullptr;
|
2011-11-25 09:36:31 +00:00
|
|
|
int materialized_literal_count = -1;
|
|
|
|
int expected_property_count = -1;
|
2015-06-22 14:15:53 +00:00
|
|
|
DuplicateFinder duplicate_finder(scanner()->unicode_cache());
|
2015-05-06 10:21:20 +00:00
|
|
|
bool should_be_used_once_hint = false;
|
2016-02-19 15:58:57 +00:00
|
|
|
bool has_duplicate_parameters;
|
2016-04-20 09:33:01 +00:00
|
|
|
|
2016-09-28 15:58:22 +00:00
|
|
|
FunctionState function_state(&function_state_, &scope_state_, scope);
|
|
|
|
#ifdef DEBUG
|
|
|
|
scope->SetScopeName(function_name);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ExpressionClassifier formals_classifier(this, &duplicate_finder);
|
|
|
|
|
2016-09-30 07:53:43 +00:00
|
|
|
if (is_generator) PrepareGeneratorVariables(&function_state);
|
2016-09-28 15:58:22 +00:00
|
|
|
|
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
|
|
|
int start_position = scanner()->location().beg_pos;
|
|
|
|
this->scope()->set_start_position(start_position);
|
|
|
|
ParserFormalParameters formals(scope);
|
|
|
|
ParseFormalParameterList(&formals, CHECK_OK);
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
int formals_end_position = scanner()->location().end_pos;
|
|
|
|
|
2016-10-13 12:34:37 +00:00
|
|
|
CheckArityRestrictions(formals.arity, kind, formals.has_rest, start_position,
|
2016-09-28 15:58:22 +00:00
|
|
|
formals_end_position, CHECK_OK);
|
|
|
|
Expect(Token::LBRACE, CHECK_OK);
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
{
|
2016-08-04 19:15:18 +00:00
|
|
|
// Temporary zones can nest. When we migrate free variables (see below), we
|
|
|
|
// need to recreate them in the previous Zone.
|
|
|
|
AstNodeFactory previous_zone_ast_node_factory(ast_value_factory());
|
|
|
|
previous_zone_ast_node_factory.set_zone(zone());
|
|
|
|
|
|
|
|
// Open a new zone scope, which sets our AstNodeFactory to allocate in the
|
|
|
|
// new temporary zone if the preconditions are satisfied, and ensures that
|
|
|
|
// the previous zone is always restored after parsing the body. To be able
|
|
|
|
// to do scope analysis correctly after full parsing, we migrate needed
|
2016-09-28 13:36:30 +00:00
|
|
|
// information when the function is parsed.
|
2016-10-17 12:12:30 +00:00
|
|
|
Zone temp_zone(zone()->allocator(), ZONE_NAME);
|
2016-09-16 10:43:20 +00:00
|
|
|
DiscardableZoneScope zone_scope(this, &temp_zone, use_temp_zone);
|
2016-08-01 13:25:39 +00:00
|
|
|
#ifdef DEBUG
|
2016-09-28 13:36:30 +00:00
|
|
|
if (use_temp_zone) scope->set_needs_migration();
|
2016-08-01 13:25:39 +00:00
|
|
|
#endif
|
[runtime] Optimize and unify rest parameters.
Replace the somewhat awkward RestParamAccessStub, which would always
call into the runtime anyway with a proper FastNewRestParameterStub,
which is basically based on the code that was already there for strict
arguments object materialization. But for rest parameters we could
optimize even further (leading to 8-10x improvements for functions with
rest parameters), by fixing the internal formal parameter count:
Every SharedFunctionInfo has a formal_parameter_count field, which
specifies the number of formal parameters, and is used to decide whether
we need to create an arguments adaptor frame when calling a function
(i.e. if there's a mismatch between the actual and expected parameters).
Previously the formal_parameter_count included the rest parameter, which
was sort of unfortunate, as that meant that calling a function with only
the non-rest parameters still required an arguments adaptor (plus some
other oddities). Now with this CL we fix, so that we do no longer
include the rest parameter in that count. Thereby checking for rest
parameters is very efficient, as we only need to check whether there is
an arguments adaptor frame, and if not create an empty array, otherwise
check whether the arguments adaptor frame has more parameters than
specified by the formal_parameter_count.
The FastNewRestParameterStub is written in a way that it can be directly
used by Ignition as well, and with some tweaks to the TurboFan backends
and the CodeStubAssembler, we should be able to rewrite it as
TurboFanCodeStub in the near future.
Drive-by-fix: Refactor and unify the CreateArgumentsType which was
different in TurboFan and Ignition; now we have a single enum class
which is used in both TurboFan and Ignition.
R=jarin@chromium.org, rmcilroy@chromium.org
TBR=rossberg@chromium.org
BUG=v8:2159
LOG=n
Review URL: https://codereview.chromium.org/1676883002
Cr-Commit-Position: refs/heads/master@{#33809}
2016-02-08 10:08:21 +00:00
|
|
|
|
2016-09-27 09:48:17 +00:00
|
|
|
// Eager or lazy parse? If is_lazy_top_level_function, we'll parse
|
|
|
|
// lazily. We'll call SkipLazyFunctionBody, which may decide to abort lazy
|
|
|
|
// parsing if it suspects that wasn't a good idea. If so (in which case the
|
|
|
|
// parser is expected to have backtracked), or if we didn't try to lazy
|
|
|
|
// parse in the first place, we'll have to parse eagerly.
|
|
|
|
if (is_lazy_top_level_function || is_lazy_inner_function) {
|
2016-09-01 10:22:54 +00:00
|
|
|
Scanner::BookmarkScope bookmark(scanner());
|
2016-09-20 09:43:41 +00:00
|
|
|
bookmark.Set();
|
2016-09-27 09:48:17 +00:00
|
|
|
LazyParsingResult result = SkipLazyFunctionBody(
|
|
|
|
&materialized_literal_count, &expected_property_count,
|
|
|
|
is_lazy_inner_function, is_lazy_top_level_function, CHECK_OK);
|
2015-05-06 10:21:20 +00:00
|
|
|
|
2015-09-22 17:43:26 +00:00
|
|
|
materialized_literal_count += formals.materialized_literals_count +
|
|
|
|
function_state.materialized_literal_count();
|
2015-09-02 21:10:51 +00:00
|
|
|
|
2016-09-01 10:22:54 +00:00
|
|
|
if (result == kLazyParsingAborted) {
|
2016-09-27 09:48:17 +00:00
|
|
|
DCHECK(is_lazy_top_level_function);
|
2016-09-20 13:47:31 +00:00
|
|
|
bookmark.Apply();
|
2015-05-06 10:21:20 +00:00
|
|
|
// Trigger eager (re-)parsing, just below this block.
|
2016-09-27 09:48:17 +00:00
|
|
|
is_lazy_top_level_function = false;
|
2015-05-06 10:21:20 +00:00
|
|
|
|
|
|
|
// This is probably an initialization function. Inform the compiler it
|
|
|
|
// should also eager-compile this function, and that we expect it to be
|
|
|
|
// used once.
|
|
|
|
eager_compile_hint = FunctionLiteral::kShouldEagerCompile;
|
|
|
|
should_be_used_once_hint = true;
|
2016-09-29 13:29:04 +00:00
|
|
|
scope->ResetAfterPreparsing(ast_value_factory(), true);
|
2016-09-28 15:58:22 +00:00
|
|
|
zone_scope.Reset();
|
|
|
|
use_temp_zone = false;
|
2015-05-06 10:21:20 +00:00
|
|
|
}
|
|
|
|
}
|
2016-09-28 13:36:30 +00:00
|
|
|
|
2016-09-27 09:48:17 +00:00
|
|
|
if (!is_lazy_top_level_function && !is_lazy_inner_function) {
|
2016-08-04 19:15:18 +00:00
|
|
|
body = ParseEagerFunctionBody(function_name, pos, formals, kind,
|
|
|
|
function_type, CHECK_OK);
|
|
|
|
|
2011-11-09 13:54:26 +00:00
|
|
|
materialized_literal_count = function_state.materialized_literal_count();
|
|
|
|
expected_property_count = function_state.expected_property_count();
|
2016-09-28 13:36:30 +00:00
|
|
|
}
|
|
|
|
|
2016-10-18 08:00:03 +00:00
|
|
|
DCHECK(use_temp_zone || !is_lazy_top_level_function);
|
|
|
|
if (use_temp_zone) {
|
2016-09-28 13:36:30 +00:00
|
|
|
// If the preconditions are correct the function body should never be
|
|
|
|
// accessed, but do this anyway for better behaviour if they're wrong.
|
|
|
|
body = nullptr;
|
|
|
|
scope->AnalyzePartially(&previous_zone_ast_node_factory);
|
2015-07-15 09:14:49 +00:00
|
|
|
}
|
2015-04-22 11:04:25 +00:00
|
|
|
|
2016-10-18 08:00:03 +00:00
|
|
|
if (FLAG_trace_preparse) {
|
|
|
|
PrintF(" [%s]: %i-%i %.*s\n",
|
|
|
|
is_lazy_top_level_function
|
|
|
|
? "Preparse no-resolution"
|
|
|
|
: (use_temp_zone ? "Preparse resolution" : "Full parse"),
|
|
|
|
scope->start_position(), scope->end_position(),
|
|
|
|
function_name->byte_length(), function_name->raw_data());
|
|
|
|
}
|
|
|
|
|
2015-07-15 09:14:49 +00:00
|
|
|
// Parsing the body may change the language mode in our scope.
|
|
|
|
language_mode = scope->language_mode();
|
|
|
|
|
2015-02-06 18:04:11 +00:00
|
|
|
// Validate name and parameter names. We can do this only after parsing the
|
|
|
|
// function, since the function can declare itself strict.
|
2015-07-15 09:14:49 +00:00
|
|
|
CheckFunctionName(language_mode, function_name, function_name_validity,
|
2015-07-09 21:31:11 +00:00
|
|
|
function_name_location, CHECK_OK);
|
2015-05-13 11:45:04 +00:00
|
|
|
const bool allow_duplicate_parameters =
|
2015-07-23 11:53:31 +00:00
|
|
|
is_sloppy(language_mode) && formals.is_simple && !IsConciseMethod(kind);
|
2016-09-01 08:58:15 +00:00
|
|
|
ValidateFormalParameters(language_mode, allow_duplicate_parameters,
|
|
|
|
CHECK_OK);
|
2015-02-06 18:04:11 +00:00
|
|
|
|
2015-07-15 09:14:49 +00:00
|
|
|
if (is_strict(language_mode)) {
|
2014-12-18 22:01:25 +00:00
|
|
|
CheckStrictOctalLiteral(scope->start_position(), scope->end_position(),
|
|
|
|
CHECK_OK);
|
2016-09-08 11:03:46 +00:00
|
|
|
CheckDecimalLiteralWithLeadingZero(scope->start_position(),
|
2016-05-16 23:20:29 +00:00
|
|
|
scope->end_position());
|
2015-07-10 16:39:47 +00:00
|
|
|
}
|
2016-03-10 23:19:31 +00:00
|
|
|
CheckConflictingVarDeclarations(scope, CHECK_OK);
|
2015-12-04 17:20:10 +00:00
|
|
|
|
|
|
|
if (body) {
|
|
|
|
// If body can be inspected, rewrite queued destructuring assignments
|
2016-08-23 16:34:39 +00:00
|
|
|
RewriteDestructuringAssignments();
|
2015-12-04 17:20:10 +00:00
|
|
|
}
|
2016-02-19 15:58:57 +00:00
|
|
|
has_duplicate_parameters =
|
2016-09-01 08:58:15 +00:00
|
|
|
!classifier()->is_valid_formal_parameter_list_without_duplicates();
|
2016-08-04 19:15:18 +00:00
|
|
|
} // DiscardableZoneScope goes out of scope.
|
2011-09-01 12:31:18 +00:00
|
|
|
|
2015-04-21 11:09:53 +00:00
|
|
|
FunctionLiteral::ParameterFlag duplicate_parameters =
|
2015-05-13 11:45:04 +00:00
|
|
|
has_duplicate_parameters ? FunctionLiteral::kHasDuplicateParameters
|
|
|
|
: FunctionLiteral::kNoDuplicateParameters;
|
2015-04-21 11:09:53 +00:00
|
|
|
|
2016-08-04 19:15:18 +00:00
|
|
|
// Note that the FunctionLiteral needs to be created in the main Zone again.
|
2014-07-21 09:58:01 +00:00
|
|
|
FunctionLiteral* function_literal = factory()->NewFunctionLiteral(
|
2016-09-28 13:36:30 +00:00
|
|
|
function_name, scope, body, materialized_literal_count,
|
2016-10-13 12:34:37 +00:00
|
|
|
expected_property_count, formals.num_parameters(),
|
|
|
|
formals.function_length, duplicate_parameters, function_type,
|
2016-09-28 01:20:44 +00:00
|
|
|
eager_compile_hint, pos);
|
2013-10-14 09:24:58 +00:00
|
|
|
function_literal->set_function_token_position(function_token_pos);
|
2015-05-06 10:21:20 +00:00
|
|
|
if (should_be_used_once_hint)
|
|
|
|
function_literal->set_should_be_used_once_hint();
|
2011-04-07 14:45:34 +00:00
|
|
|
|
2016-09-02 11:44:48 +00:00
|
|
|
if (should_infer_name) {
|
|
|
|
DCHECK_NOT_NULL(fni_);
|
|
|
|
fni_->AddFunction(function_literal);
|
|
|
|
}
|
2011-04-07 14:45:34 +00:00
|
|
|
return function_literal;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2016-09-01 10:22:54 +00:00
|
|
|
Parser::LazyParsingResult Parser::SkipLazyFunctionBody(
|
|
|
|
int* materialized_literal_count, int* expected_property_count,
|
2016-09-27 09:48:17 +00:00
|
|
|
bool is_inner_function, bool may_abort, bool* ok) {
|
2014-11-17 12:16:27 +00:00
|
|
|
if (produce_cached_parse_data()) CHECK(log_);
|
2014-10-01 16:54:42 +00:00
|
|
|
|
2014-04-15 08:29:24 +00:00
|
|
|
int function_block_pos = position();
|
2016-09-27 09:49:26 +00:00
|
|
|
DeclarationScope* scope = function_state_->scope();
|
2016-08-23 14:18:12 +00:00
|
|
|
DCHECK(scope->is_function_scope());
|
2016-09-27 09:48:17 +00:00
|
|
|
// Inner functions are not part of the cached data.
|
|
|
|
if (!is_inner_function && consume_cached_parse_data() &&
|
|
|
|
!cached_parse_data_->rejected()) {
|
2014-04-15 08:29:24 +00:00
|
|
|
// If we have cached data, we use it to skip parsing the function body. The
|
|
|
|
// data contains the information we need to construct the lazy function.
|
|
|
|
FunctionEntry entry =
|
2014-07-10 10:28:05 +00:00
|
|
|
cached_parse_data_->GetFunctionEntry(function_block_pos);
|
2014-12-08 11:47:44 +00:00
|
|
|
// Check that cached data is valid. If not, mark it as invalid (the embedder
|
|
|
|
// handles it). Note that end position greater than end of stream is safe,
|
|
|
|
// and hard to check.
|
|
|
|
if (entry.is_valid() && entry.end_pos() > function_block_pos) {
|
|
|
|
scanner()->SeekForward(entry.end_pos() - 1);
|
|
|
|
|
2016-08-23 14:18:12 +00:00
|
|
|
scope->set_end_position(entry.end_pos());
|
2016-09-01 10:22:54 +00:00
|
|
|
Expect(Token::RBRACE, CHECK_OK_VALUE(kLazyParsingComplete));
|
2016-08-23 14:18:12 +00:00
|
|
|
total_preparse_skipped_ += scope->end_position() - function_block_pos;
|
2014-12-08 11:47:44 +00:00
|
|
|
*materialized_literal_count = entry.literal_count();
|
|
|
|
*expected_property_count = entry.property_count();
|
2016-08-23 14:18:12 +00:00
|
|
|
SetLanguageMode(scope, entry.language_mode());
|
|
|
|
if (entry.uses_super_property()) scope->RecordSuperPropertyUsage();
|
|
|
|
if (entry.calls_eval()) scope->RecordEvalCall();
|
2016-09-01 10:22:54 +00:00
|
|
|
return kLazyParsingComplete;
|
2014-04-15 08:29:24 +00:00
|
|
|
}
|
2014-12-08 11:47:44 +00:00
|
|
|
cached_parse_data_->Reject();
|
|
|
|
}
|
|
|
|
// With no cached data, we partially parse the function, without building an
|
|
|
|
// AST. This gathers the data needed to build a lazy function.
|
|
|
|
SingletonLogger logger;
|
|
|
|
PreParser::PreParseResult result =
|
2016-10-14 14:08:53 +00:00
|
|
|
ParseFunctionBodyWithPreParser(&logger, is_inner_function, may_abort);
|
2016-09-27 09:49:26 +00:00
|
|
|
|
2016-09-01 10:22:54 +00:00
|
|
|
// Return immediately if pre-parser decided to abort parsing.
|
2016-09-28 13:36:30 +00:00
|
|
|
if (result == PreParser::kPreParseAbort) return kLazyParsingAborted;
|
2014-12-08 11:47:44 +00:00
|
|
|
if (result == PreParser::kPreParseStackOverflow) {
|
|
|
|
// Propagate stack overflow.
|
|
|
|
set_stack_overflow();
|
|
|
|
*ok = false;
|
2016-09-01 10:22:54 +00:00
|
|
|
return kLazyParsingComplete;
|
2014-12-08 11:47:44 +00:00
|
|
|
}
|
|
|
|
if (logger.has_error()) {
|
2016-08-19 08:11:04 +00:00
|
|
|
ReportMessageAt(Scanner::Location(logger.start(), logger.end()),
|
|
|
|
logger.message(), logger.argument_opt(),
|
|
|
|
logger.error_type());
|
2014-12-08 11:47:44 +00:00
|
|
|
*ok = false;
|
2016-09-01 10:22:54 +00:00
|
|
|
return kLazyParsingComplete;
|
2014-12-08 11:47:44 +00:00
|
|
|
}
|
2016-08-23 14:18:12 +00:00
|
|
|
scope->set_end_position(logger.end());
|
2016-09-01 10:22:54 +00:00
|
|
|
Expect(Token::RBRACE, CHECK_OK_VALUE(kLazyParsingComplete));
|
2016-08-23 14:18:12 +00:00
|
|
|
total_preparse_skipped_ += scope->end_position() - function_block_pos;
|
2014-12-08 11:47:44 +00:00
|
|
|
*materialized_literal_count = logger.literals();
|
|
|
|
*expected_property_count = logger.properties();
|
2016-08-23 14:18:12 +00:00
|
|
|
SetLanguageMode(scope, logger.language_mode());
|
|
|
|
if (logger.uses_super_property()) scope->RecordSuperPropertyUsage();
|
|
|
|
if (logger.calls_eval()) scope->RecordEvalCall();
|
2016-09-27 09:48:17 +00:00
|
|
|
if (!is_inner_function && produce_cached_parse_data()) {
|
2014-12-08 11:47:44 +00:00
|
|
|
DCHECK(log_);
|
|
|
|
// Position right after terminal '}'.
|
|
|
|
int body_end = scanner()->location().end_pos;
|
|
|
|
log_->LogFunction(function_block_pos, body_end, *materialized_literal_count,
|
2016-08-08 18:53:06 +00:00
|
|
|
*expected_property_count, language_mode(),
|
2016-08-23 14:18:12 +00:00
|
|
|
scope->uses_super_property(), scope->calls_eval());
|
2014-04-15 08:29:24 +00:00
|
|
|
}
|
2016-09-01 10:22:54 +00:00
|
|
|
return kLazyParsingComplete;
|
2014-04-15 08:29:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-05-18 20:15:08 +00:00
|
|
|
Statement* Parser::BuildAssertIsCoercible(Variable* var) {
|
|
|
|
// if (var === null || var === undefined)
|
|
|
|
// throw /* type error kNonCoercible) */;
|
|
|
|
|
|
|
|
Expression* condition = factory()->NewBinaryOperation(
|
2016-06-30 09:26:30 +00:00
|
|
|
Token::OR,
|
2015-05-18 20:15:08 +00:00
|
|
|
factory()->NewCompareOperation(
|
|
|
|
Token::EQ_STRICT, factory()->NewVariableProxy(var),
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewUndefinedLiteral(kNoSourcePosition), kNoSourcePosition),
|
|
|
|
factory()->NewCompareOperation(
|
|
|
|
Token::EQ_STRICT, factory()->NewVariableProxy(var),
|
|
|
|
factory()->NewNullLiteral(kNoSourcePosition), kNoSourcePosition),
|
|
|
|
kNoSourcePosition);
|
2016-08-25 08:48:45 +00:00
|
|
|
Expression* throw_type_error =
|
|
|
|
NewThrowTypeError(MessageTemplate::kNonCoercible,
|
|
|
|
ast_value_factory()->empty_string(), kNoSourcePosition);
|
2015-05-18 20:15:08 +00:00
|
|
|
IfStatement* if_statement = factory()->NewIfStatement(
|
2016-06-30 09:26:30 +00:00
|
|
|
condition,
|
|
|
|
factory()->NewExpressionStatement(throw_type_error, kNoSourcePosition),
|
|
|
|
factory()->NewEmptyStatement(kNoSourcePosition), kNoSourcePosition);
|
2015-05-18 20:15:08 +00:00
|
|
|
return if_statement;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-07-25 08:25:49 +00:00
|
|
|
class InitializerRewriter final
|
|
|
|
: public AstTraversalVisitor<InitializerRewriter> {
|
2015-12-04 17:20:10 +00:00
|
|
|
public:
|
|
|
|
InitializerRewriter(uintptr_t stack_limit, Expression* root, Parser* parser,
|
|
|
|
Scope* scope)
|
2016-07-25 08:25:49 +00:00
|
|
|
: AstTraversalVisitor(stack_limit, root),
|
2015-12-04 17:20:10 +00:00
|
|
|
parser_(parser),
|
|
|
|
scope_(scope) {}
|
|
|
|
|
|
|
|
private:
|
2016-07-25 08:25:49 +00:00
|
|
|
// This is required so that the overriden Visit* methods can be
|
|
|
|
// called by the base class (template).
|
|
|
|
friend class AstTraversalVisitor<InitializerRewriter>;
|
2015-12-04 17:20:10 +00:00
|
|
|
|
2016-07-25 08:25:49 +00:00
|
|
|
// Just rewrite destructuring assignments wrapped in RewritableExpressions.
|
|
|
|
void VisitRewritableExpression(RewritableExpression* to_rewrite) {
|
|
|
|
if (to_rewrite->is_rewritten()) return;
|
2015-12-04 17:20:10 +00:00
|
|
|
Parser::PatternRewriter::RewriteDestructuringAssignment(parser_, to_rewrite,
|
2015-12-11 19:38:57 +00:00
|
|
|
scope_);
|
2015-12-04 17:20:10 +00:00
|
|
|
}
|
|
|
|
|
2016-04-05 18:41:44 +00:00
|
|
|
// Code in function literals does not need to be eagerly rewritten, it will be
|
|
|
|
// rewritten when scheduled.
|
2016-07-25 08:25:49 +00:00
|
|
|
void VisitFunctionLiteral(FunctionLiteral* expr) {}
|
2016-04-05 18:41:44 +00:00
|
|
|
|
2015-12-04 17:20:10 +00:00
|
|
|
Parser* parser_;
|
|
|
|
Scope* scope_;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
void Parser::RewriteParameterInitializer(Expression* expr, Scope* scope) {
|
|
|
|
InitializerRewriter rewriter(stack_limit_, expr, this, scope);
|
|
|
|
rewriter.Run();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-22 14:15:53 +00:00
|
|
|
Block* Parser::BuildParameterInitializationBlock(
|
2015-07-23 11:53:31 +00:00
|
|
|
const ParserFormalParameters& parameters, bool* ok) {
|
2015-07-23 14:28:59 +00:00
|
|
|
DCHECK(!parameters.is_simple);
|
2016-07-19 10:06:38 +00:00
|
|
|
DCHECK(scope()->is_function_scope());
|
2016-06-30 09:26:30 +00:00
|
|
|
Block* init_block = factory()->NewBlock(NULL, 1, true, kNoSourcePosition);
|
2015-08-04 14:24:13 +00:00
|
|
|
for (int i = 0; i < parameters.params.length(); ++i) {
|
|
|
|
auto parameter = parameters.params[i];
|
2016-01-08 20:22:18 +00:00
|
|
|
if (parameter.is_rest && parameter.pattern->IsVariableProxy()) break;
|
2015-06-22 14:15:53 +00:00
|
|
|
DeclarationDescriptor descriptor;
|
|
|
|
descriptor.declaration_kind = DeclarationDescriptor::PARAMETER;
|
2016-07-19 10:06:38 +00:00
|
|
|
descriptor.scope = scope();
|
[es6] Parameter scopes for sloppy eval
This CL is a nightmare! For the utterly irrelevant edge case of a sloppy function with non-simple parameters and a call to direct eval, like here,
let x = 1;
function f(g = () => x) {
var y
eval("var x = 2")
return g() + x // f() = 3
}
we have to do all of the following, on top of the declaration block ("varblock") contexts we already introduce around the body:
- Introduce the ability for varblock contexts to have both a ScopeInfo and an extension object (e.g., the body varblock in the example will contain both a static var y and a dynamic var x). No other scope needs that. Since there are no context slots left, a special new struct is introduced that pairs up scope info and extension object.
- When declaring lookup slots in the runtime, this new struct is allocated in the case where an extension object has to be added to a block scope (at which point the block's extension slot still contains a plain ScopeInfo).
- While at it, introduce some abstraction to access context extension slots in a more controlled manner, in order to keep special-casing to a minimum.
- Make sure that even empty varblock contexts do not get optimised away when they contain a sloppy eval, so that they can host the potential extension object.
- Extend dynamic search for declaration contexts (used by sloppy direct eval) to recognize varblock contexts.
- In the parser, if a function has a sloppy direct eval, introduce an additional varblock scope around each non-simple (desugared) parameter, as required by the spec to contain possible dynamic var bindings.
- In the pattern rewriter, add the ability to hoist the named variables the pattern declares to an outer scope. That is required because the actual destructuring has to be evaluated inside the protecting varblock scope, but the bindings that the desugaring introduces are in the outer scope.
- ScopeInfos need to save the information whether a block is a varblock, to make sloppy eval calls work correctly that deserialise them as part of the scope chain.
- Add the ability to materialize block scopes with extension objects in the debugger. Likewise, enable setting extension variables in block scopes via the debugger interface.
- While at it, refactor and unify some respective code in the debugger.
Sorry, this CL is large. I could try to split it up, but everything is rather entangled.
@mstarzinger: Please review the changes to contexts.
@yangguo: Please have a look at the debugger stuff.
R=littledan@chromium.org, mstarzinger@chromium.org, yangguo@chromium.org
BUG=v8:811,v8:2160
LOG=N
Review URL: https://codereview.chromium.org/1292753007
Cr-Commit-Position: refs/heads/master@{#30295}
2015-08-21 10:58:35 +00:00
|
|
|
descriptor.hoist_scope = nullptr;
|
2015-06-22 14:15:53 +00:00
|
|
|
descriptor.mode = LET;
|
|
|
|
descriptor.declaration_pos = parameter.pattern->position();
|
2015-11-25 01:30:33 +00:00
|
|
|
// The position that will be used by the AssignmentExpression
|
|
|
|
// which copies from the temp parameter to the pattern.
|
|
|
|
//
|
2016-06-30 09:26:30 +00:00
|
|
|
// TODO(adamk): Should this be kNoSourcePosition, since
|
2015-11-25 01:30:33 +00:00
|
|
|
// it's just copying from a temp var to the real param var?
|
2015-06-22 14:15:53 +00:00
|
|
|
descriptor.initialization_pos = parameter.pattern->position();
|
2015-08-17 12:01:55 +00:00
|
|
|
Expression* initial_value =
|
|
|
|
factory()->NewVariableProxy(parameters.scope->parameter(i));
|
|
|
|
if (parameter.initializer != nullptr) {
|
|
|
|
// IS_UNDEFINED($param) ? initializer : $param
|
2015-12-04 17:20:10 +00:00
|
|
|
|
|
|
|
// Ensure initializer is rewritten
|
2016-07-19 10:06:38 +00:00
|
|
|
RewriteParameterInitializer(parameter.initializer, scope());
|
2015-12-04 17:20:10 +00:00
|
|
|
|
2015-08-17 12:01:55 +00:00
|
|
|
auto condition = factory()->NewCompareOperation(
|
|
|
|
Token::EQ_STRICT,
|
|
|
|
factory()->NewVariableProxy(parameters.scope->parameter(i)),
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewUndefinedLiteral(kNoSourcePosition), kNoSourcePosition);
|
2015-08-17 12:01:55 +00:00
|
|
|
initial_value = factory()->NewConditional(
|
2016-06-30 09:26:30 +00:00
|
|
|
condition, parameter.initializer, initial_value, kNoSourcePosition);
|
2015-08-17 12:01:55 +00:00
|
|
|
descriptor.initialization_pos = parameter.initializer->position();
|
|
|
|
}
|
[es6] Parameter scopes for sloppy eval
This CL is a nightmare! For the utterly irrelevant edge case of a sloppy function with non-simple parameters and a call to direct eval, like here,
let x = 1;
function f(g = () => x) {
var y
eval("var x = 2")
return g() + x // f() = 3
}
we have to do all of the following, on top of the declaration block ("varblock") contexts we already introduce around the body:
- Introduce the ability for varblock contexts to have both a ScopeInfo and an extension object (e.g., the body varblock in the example will contain both a static var y and a dynamic var x). No other scope needs that. Since there are no context slots left, a special new struct is introduced that pairs up scope info and extension object.
- When declaring lookup slots in the runtime, this new struct is allocated in the case where an extension object has to be added to a block scope (at which point the block's extension slot still contains a plain ScopeInfo).
- While at it, introduce some abstraction to access context extension slots in a more controlled manner, in order to keep special-casing to a minimum.
- Make sure that even empty varblock contexts do not get optimised away when they contain a sloppy eval, so that they can host the potential extension object.
- Extend dynamic search for declaration contexts (used by sloppy direct eval) to recognize varblock contexts.
- In the parser, if a function has a sloppy direct eval, introduce an additional varblock scope around each non-simple (desugared) parameter, as required by the spec to contain possible dynamic var bindings.
- In the pattern rewriter, add the ability to hoist the named variables the pattern declares to an outer scope. That is required because the actual destructuring has to be evaluated inside the protecting varblock scope, but the bindings that the desugaring introduces are in the outer scope.
- ScopeInfos need to save the information whether a block is a varblock, to make sloppy eval calls work correctly that deserialise them as part of the scope chain.
- Add the ability to materialize block scopes with extension objects in the debugger. Likewise, enable setting extension variables in block scopes via the debugger interface.
- While at it, refactor and unify some respective code in the debugger.
Sorry, this CL is large. I could try to split it up, but everything is rather entangled.
@mstarzinger: Please review the changes to contexts.
@yangguo: Please have a look at the debugger stuff.
R=littledan@chromium.org, mstarzinger@chromium.org, yangguo@chromium.org
BUG=v8:811,v8:2160
LOG=N
Review URL: https://codereview.chromium.org/1292753007
Cr-Commit-Position: refs/heads/master@{#30295}
2015-08-21 10:58:35 +00:00
|
|
|
|
2016-07-19 10:06:38 +00:00
|
|
|
Scope* param_scope = scope();
|
[es6] Parameter scopes for sloppy eval
This CL is a nightmare! For the utterly irrelevant edge case of a sloppy function with non-simple parameters and a call to direct eval, like here,
let x = 1;
function f(g = () => x) {
var y
eval("var x = 2")
return g() + x // f() = 3
}
we have to do all of the following, on top of the declaration block ("varblock") contexts we already introduce around the body:
- Introduce the ability for varblock contexts to have both a ScopeInfo and an extension object (e.g., the body varblock in the example will contain both a static var y and a dynamic var x). No other scope needs that. Since there are no context slots left, a special new struct is introduced that pairs up scope info and extension object.
- When declaring lookup slots in the runtime, this new struct is allocated in the case where an extension object has to be added to a block scope (at which point the block's extension slot still contains a plain ScopeInfo).
- While at it, introduce some abstraction to access context extension slots in a more controlled manner, in order to keep special-casing to a minimum.
- Make sure that even empty varblock contexts do not get optimised away when they contain a sloppy eval, so that they can host the potential extension object.
- Extend dynamic search for declaration contexts (used by sloppy direct eval) to recognize varblock contexts.
- In the parser, if a function has a sloppy direct eval, introduce an additional varblock scope around each non-simple (desugared) parameter, as required by the spec to contain possible dynamic var bindings.
- In the pattern rewriter, add the ability to hoist the named variables the pattern declares to an outer scope. That is required because the actual destructuring has to be evaluated inside the protecting varblock scope, but the bindings that the desugaring introduces are in the outer scope.
- ScopeInfos need to save the information whether a block is a varblock, to make sloppy eval calls work correctly that deserialise them as part of the scope chain.
- Add the ability to materialize block scopes with extension objects in the debugger. Likewise, enable setting extension variables in block scopes via the debugger interface.
- While at it, refactor and unify some respective code in the debugger.
Sorry, this CL is large. I could try to split it up, but everything is rather entangled.
@mstarzinger: Please review the changes to contexts.
@yangguo: Please have a look at the debugger stuff.
R=littledan@chromium.org, mstarzinger@chromium.org, yangguo@chromium.org
BUG=v8:811,v8:2160
LOG=N
Review URL: https://codereview.chromium.org/1292753007
Cr-Commit-Position: refs/heads/master@{#30295}
2015-08-21 10:58:35 +00:00
|
|
|
Block* param_block = init_block;
|
2016-07-19 10:06:38 +00:00
|
|
|
if (!parameter.is_simple() && scope()->calls_sloppy_eval()) {
|
2016-08-05 14:30:54 +00:00
|
|
|
param_scope = NewVarblockScope();
|
2016-04-22 10:46:42 +00:00
|
|
|
param_scope->set_start_position(descriptor.initialization_pos);
|
|
|
|
param_scope->set_end_position(parameter.initializer_end_position);
|
2016-06-14 16:46:32 +00:00
|
|
|
param_scope->RecordEvalCall();
|
2016-06-30 09:26:30 +00:00
|
|
|
param_block = factory()->NewBlock(NULL, 8, true, kNoSourcePosition);
|
[es6] Parameter scopes for sloppy eval
This CL is a nightmare! For the utterly irrelevant edge case of a sloppy function with non-simple parameters and a call to direct eval, like here,
let x = 1;
function f(g = () => x) {
var y
eval("var x = 2")
return g() + x // f() = 3
}
we have to do all of the following, on top of the declaration block ("varblock") contexts we already introduce around the body:
- Introduce the ability for varblock contexts to have both a ScopeInfo and an extension object (e.g., the body varblock in the example will contain both a static var y and a dynamic var x). No other scope needs that. Since there are no context slots left, a special new struct is introduced that pairs up scope info and extension object.
- When declaring lookup slots in the runtime, this new struct is allocated in the case where an extension object has to be added to a block scope (at which point the block's extension slot still contains a plain ScopeInfo).
- While at it, introduce some abstraction to access context extension slots in a more controlled manner, in order to keep special-casing to a minimum.
- Make sure that even empty varblock contexts do not get optimised away when they contain a sloppy eval, so that they can host the potential extension object.
- Extend dynamic search for declaration contexts (used by sloppy direct eval) to recognize varblock contexts.
- In the parser, if a function has a sloppy direct eval, introduce an additional varblock scope around each non-simple (desugared) parameter, as required by the spec to contain possible dynamic var bindings.
- In the pattern rewriter, add the ability to hoist the named variables the pattern declares to an outer scope. That is required because the actual destructuring has to be evaluated inside the protecting varblock scope, but the bindings that the desugaring introduces are in the outer scope.
- ScopeInfos need to save the information whether a block is a varblock, to make sloppy eval calls work correctly that deserialise them as part of the scope chain.
- Add the ability to materialize block scopes with extension objects in the debugger. Likewise, enable setting extension variables in block scopes via the debugger interface.
- While at it, refactor and unify some respective code in the debugger.
Sorry, this CL is large. I could try to split it up, but everything is rather entangled.
@mstarzinger: Please review the changes to contexts.
@yangguo: Please have a look at the debugger stuff.
R=littledan@chromium.org, mstarzinger@chromium.org, yangguo@chromium.org
BUG=v8:811,v8:2160
LOG=N
Review URL: https://codereview.chromium.org/1292753007
Cr-Commit-Position: refs/heads/master@{#30295}
2015-08-21 10:58:35 +00:00
|
|
|
param_block->set_scope(param_scope);
|
2016-07-19 10:06:38 +00:00
|
|
|
descriptor.hoist_scope = scope();
|
2016-06-22 21:07:53 +00:00
|
|
|
// Pass the appropriate scope in so that PatternRewriter can appropriately
|
|
|
|
// rewrite inner initializers of the pattern to param_scope
|
|
|
|
descriptor.scope = param_scope;
|
|
|
|
// Rewrite the outer initializer to point to param_scope
|
2016-07-22 23:28:10 +00:00
|
|
|
ReparentParameterExpressionScope(stack_limit(), initial_value,
|
2016-06-22 21:07:53 +00:00
|
|
|
param_scope);
|
[es6] Parameter scopes for sloppy eval
This CL is a nightmare! For the utterly irrelevant edge case of a sloppy function with non-simple parameters and a call to direct eval, like here,
let x = 1;
function f(g = () => x) {
var y
eval("var x = 2")
return g() + x // f() = 3
}
we have to do all of the following, on top of the declaration block ("varblock") contexts we already introduce around the body:
- Introduce the ability for varblock contexts to have both a ScopeInfo and an extension object (e.g., the body varblock in the example will contain both a static var y and a dynamic var x). No other scope needs that. Since there are no context slots left, a special new struct is introduced that pairs up scope info and extension object.
- When declaring lookup slots in the runtime, this new struct is allocated in the case where an extension object has to be added to a block scope (at which point the block's extension slot still contains a plain ScopeInfo).
- While at it, introduce some abstraction to access context extension slots in a more controlled manner, in order to keep special-casing to a minimum.
- Make sure that even empty varblock contexts do not get optimised away when they contain a sloppy eval, so that they can host the potential extension object.
- Extend dynamic search for declaration contexts (used by sloppy direct eval) to recognize varblock contexts.
- In the parser, if a function has a sloppy direct eval, introduce an additional varblock scope around each non-simple (desugared) parameter, as required by the spec to contain possible dynamic var bindings.
- In the pattern rewriter, add the ability to hoist the named variables the pattern declares to an outer scope. That is required because the actual destructuring has to be evaluated inside the protecting varblock scope, but the bindings that the desugaring introduces are in the outer scope.
- ScopeInfos need to save the information whether a block is a varblock, to make sloppy eval calls work correctly that deserialise them as part of the scope chain.
- Add the ability to materialize block scopes with extension objects in the debugger. Likewise, enable setting extension variables in block scopes via the debugger interface.
- While at it, refactor and unify some respective code in the debugger.
Sorry, this CL is large. I could try to split it up, but everything is rather entangled.
@mstarzinger: Please review the changes to contexts.
@yangguo: Please have a look at the debugger stuff.
R=littledan@chromium.org, mstarzinger@chromium.org, yangguo@chromium.org
BUG=v8:811,v8:2160
LOG=N
Review URL: https://codereview.chromium.org/1292753007
Cr-Commit-Position: refs/heads/master@{#30295}
2015-08-21 10:58:35 +00:00
|
|
|
}
|
|
|
|
|
2016-08-08 19:19:30 +00:00
|
|
|
BlockState block_state(&scope_state_, param_scope);
|
|
|
|
DeclarationParsingResult::Declaration decl(
|
2016-10-04 17:04:11 +00:00
|
|
|
parameter.pattern, parameter.initializer_end_position, initial_value);
|
2016-08-31 08:31:36 +00:00
|
|
|
PatternRewriter::DeclareAndInitializeVariables(
|
|
|
|
this, param_block, &descriptor, &decl, nullptr, CHECK_OK);
|
2016-08-08 19:19:30 +00:00
|
|
|
|
|
|
|
if (param_block != init_block) {
|
|
|
|
param_scope = block_state.FinalizedBlockScope();
|
[es6] Parameter scopes for sloppy eval
This CL is a nightmare! For the utterly irrelevant edge case of a sloppy function with non-simple parameters and a call to direct eval, like here,
let x = 1;
function f(g = () => x) {
var y
eval("var x = 2")
return g() + x // f() = 3
}
we have to do all of the following, on top of the declaration block ("varblock") contexts we already introduce around the body:
- Introduce the ability for varblock contexts to have both a ScopeInfo and an extension object (e.g., the body varblock in the example will contain both a static var y and a dynamic var x). No other scope needs that. Since there are no context slots left, a special new struct is introduced that pairs up scope info and extension object.
- When declaring lookup slots in the runtime, this new struct is allocated in the case where an extension object has to be added to a block scope (at which point the block's extension slot still contains a plain ScopeInfo).
- While at it, introduce some abstraction to access context extension slots in a more controlled manner, in order to keep special-casing to a minimum.
- Make sure that even empty varblock contexts do not get optimised away when they contain a sloppy eval, so that they can host the potential extension object.
- Extend dynamic search for declaration contexts (used by sloppy direct eval) to recognize varblock contexts.
- In the parser, if a function has a sloppy direct eval, introduce an additional varblock scope around each non-simple (desugared) parameter, as required by the spec to contain possible dynamic var bindings.
- In the pattern rewriter, add the ability to hoist the named variables the pattern declares to an outer scope. That is required because the actual destructuring has to be evaluated inside the protecting varblock scope, but the bindings that the desugaring introduces are in the outer scope.
- ScopeInfos need to save the information whether a block is a varblock, to make sloppy eval calls work correctly that deserialise them as part of the scope chain.
- Add the ability to materialize block scopes with extension objects in the debugger. Likewise, enable setting extension variables in block scopes via the debugger interface.
- While at it, refactor and unify some respective code in the debugger.
Sorry, this CL is large. I could try to split it up, but everything is rather entangled.
@mstarzinger: Please review the changes to contexts.
@yangguo: Please have a look at the debugger stuff.
R=littledan@chromium.org, mstarzinger@chromium.org, yangguo@chromium.org
BUG=v8:811,v8:2160
LOG=N
Review URL: https://codereview.chromium.org/1292753007
Cr-Commit-Position: refs/heads/master@{#30295}
2015-08-21 10:58:35 +00:00
|
|
|
if (param_scope != nullptr) {
|
|
|
|
CheckConflictingVarDeclarations(param_scope, CHECK_OK);
|
|
|
|
}
|
2015-10-01 13:59:36 +00:00
|
|
|
init_block->statements()->Add(param_block, zone());
|
[es6] Parameter scopes for sloppy eval
This CL is a nightmare! For the utterly irrelevant edge case of a sloppy function with non-simple parameters and a call to direct eval, like here,
let x = 1;
function f(g = () => x) {
var y
eval("var x = 2")
return g() + x // f() = 3
}
we have to do all of the following, on top of the declaration block ("varblock") contexts we already introduce around the body:
- Introduce the ability for varblock contexts to have both a ScopeInfo and an extension object (e.g., the body varblock in the example will contain both a static var y and a dynamic var x). No other scope needs that. Since there are no context slots left, a special new struct is introduced that pairs up scope info and extension object.
- When declaring lookup slots in the runtime, this new struct is allocated in the case where an extension object has to be added to a block scope (at which point the block's extension slot still contains a plain ScopeInfo).
- While at it, introduce some abstraction to access context extension slots in a more controlled manner, in order to keep special-casing to a minimum.
- Make sure that even empty varblock contexts do not get optimised away when they contain a sloppy eval, so that they can host the potential extension object.
- Extend dynamic search for declaration contexts (used by sloppy direct eval) to recognize varblock contexts.
- In the parser, if a function has a sloppy direct eval, introduce an additional varblock scope around each non-simple (desugared) parameter, as required by the spec to contain possible dynamic var bindings.
- In the pattern rewriter, add the ability to hoist the named variables the pattern declares to an outer scope. That is required because the actual destructuring has to be evaluated inside the protecting varblock scope, but the bindings that the desugaring introduces are in the outer scope.
- ScopeInfos need to save the information whether a block is a varblock, to make sloppy eval calls work correctly that deserialise them as part of the scope chain.
- Add the ability to materialize block scopes with extension objects in the debugger. Likewise, enable setting extension variables in block scopes via the debugger interface.
- While at it, refactor and unify some respective code in the debugger.
Sorry, this CL is large. I could try to split it up, but everything is rather entangled.
@mstarzinger: Please review the changes to contexts.
@yangguo: Please have a look at the debugger stuff.
R=littledan@chromium.org, mstarzinger@chromium.org, yangguo@chromium.org
BUG=v8:811,v8:2160
LOG=N
Review URL: https://codereview.chromium.org/1292753007
Cr-Commit-Position: refs/heads/master@{#30295}
2015-08-21 10:58:35 +00:00
|
|
|
}
|
2015-06-22 14:15:53 +00:00
|
|
|
}
|
|
|
|
return init_block;
|
|
|
|
}
|
|
|
|
|
2016-08-26 22:17:52 +00:00
|
|
|
Block* Parser::BuildRejectPromiseOnException(Block* inner_block, bool* ok) {
|
2016-09-23 22:23:50 +00:00
|
|
|
// .promise = %AsyncFunctionPromiseCreate();
|
2016-08-26 22:17:52 +00:00
|
|
|
// try {
|
|
|
|
// <inner_block>
|
|
|
|
// } catch (.catch) {
|
|
|
|
// %RejectPromise(.promise, .catch);
|
|
|
|
// return .promise;
|
|
|
|
// } finally {
|
2016-09-23 22:23:50 +00:00
|
|
|
// %AsyncFunctionPromiseRelease(.promise);
|
2016-08-26 22:17:52 +00:00
|
|
|
// }
|
2016-09-23 22:23:50 +00:00
|
|
|
Block* result = factory()->NewBlock(nullptr, 2, true, kNoSourcePosition);
|
2016-08-26 22:17:52 +00:00
|
|
|
|
2016-09-23 22:23:50 +00:00
|
|
|
// .promise = %AsyncFunctionPromiseCreate();
|
2016-08-26 22:17:52 +00:00
|
|
|
Statement* set_promise;
|
|
|
|
{
|
|
|
|
Expression* create_promise = factory()->NewCallRuntime(
|
2016-09-23 22:23:50 +00:00
|
|
|
Context::ASYNC_FUNCTION_PROMISE_CREATE_INDEX,
|
2016-08-26 22:17:52 +00:00
|
|
|
new (zone()) ZoneList<Expression*>(0, zone()), kNoSourcePosition);
|
|
|
|
Assignment* assign_promise = factory()->NewAssignment(
|
2016-09-20 21:30:45 +00:00
|
|
|
Token::INIT, factory()->NewVariableProxy(PromiseVariable()),
|
|
|
|
create_promise, kNoSourcePosition);
|
2016-08-26 22:17:52 +00:00
|
|
|
set_promise =
|
|
|
|
factory()->NewExpressionStatement(assign_promise, kNoSourcePosition);
|
|
|
|
}
|
|
|
|
result->statements()->Add(set_promise, zone());
|
|
|
|
|
|
|
|
// catch (.catch) { return %RejectPromise(.promise, .catch), .promise }
|
2016-07-20 14:42:02 +00:00
|
|
|
Scope* catch_scope = NewScope(CATCH_SCOPE);
|
2016-05-17 00:26:53 +00:00
|
|
|
catch_scope->set_is_hidden();
|
|
|
|
Variable* catch_variable =
|
|
|
|
catch_scope->DeclareLocal(ast_value_factory()->dot_catch_string(), VAR,
|
2016-09-09 10:55:31 +00:00
|
|
|
kCreatedInitialized, NORMAL_VARIABLE);
|
2016-06-30 09:26:30 +00:00
|
|
|
Block* catch_block = factory()->NewBlock(nullptr, 1, true, kNoSourcePosition);
|
2016-05-17 00:26:53 +00:00
|
|
|
|
2016-08-26 22:17:52 +00:00
|
|
|
Expression* promise_reject = BuildRejectPromise(
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewVariableProxy(catch_variable), kNoSourcePosition);
|
2016-05-17 00:26:53 +00:00
|
|
|
ReturnStatement* return_promise_reject =
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewReturnStatement(promise_reject, kNoSourcePosition);
|
2016-05-17 00:26:53 +00:00
|
|
|
catch_block->statements()->Add(return_promise_reject, zone());
|
2016-08-26 22:17:52 +00:00
|
|
|
|
2016-09-13 20:49:54 +00:00
|
|
|
TryStatement* try_catch_statement =
|
|
|
|
factory()->NewTryCatchStatementForAsyncAwait(inner_block, catch_scope,
|
|
|
|
catch_variable, catch_block,
|
|
|
|
kNoSourcePosition);
|
2016-08-26 22:17:52 +00:00
|
|
|
|
|
|
|
// There is no TryCatchFinally node, so wrap it in an outer try/finally
|
|
|
|
Block* outer_try_block =
|
|
|
|
factory()->NewBlock(nullptr, 1, true, kNoSourcePosition);
|
|
|
|
outer_try_block->statements()->Add(try_catch_statement, zone());
|
|
|
|
|
2016-09-23 22:23:50 +00:00
|
|
|
// finally { %AsyncFunctionPromiseRelease(.promise) }
|
2016-08-26 22:17:52 +00:00
|
|
|
Block* finally_block =
|
|
|
|
factory()->NewBlock(nullptr, 1, true, kNoSourcePosition);
|
|
|
|
{
|
2016-09-23 22:23:50 +00:00
|
|
|
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(PromiseVariable()), zone());
|
|
|
|
Expression* call_promise_release = factory()->NewCallRuntime(
|
|
|
|
Context::ASYNC_FUNCTION_PROMISE_RELEASE_INDEX, args, kNoSourcePosition);
|
|
|
|
Statement* promise_release = factory()->NewExpressionStatement(
|
|
|
|
call_promise_release, kNoSourcePosition);
|
|
|
|
finally_block->statements()->Add(promise_release, zone());
|
2016-08-26 22:17:52 +00:00
|
|
|
}
|
2016-05-17 00:26:53 +00:00
|
|
|
|
2016-08-26 22:17:52 +00:00
|
|
|
Statement* try_finally_statement = factory()->NewTryFinallyStatement(
|
|
|
|
outer_try_block, finally_block, kNoSourcePosition);
|
|
|
|
|
|
|
|
result->statements()->Add(try_finally_statement, zone());
|
|
|
|
return result;
|
2016-05-17 00:26:53 +00:00
|
|
|
}
|
|
|
|
|
2016-05-17 01:14:27 +00:00
|
|
|
Expression* Parser::BuildCreateJSGeneratorObject(int pos, FunctionKind kind) {
|
2016-05-17 00:26:53 +00:00
|
|
|
DCHECK_NOT_NULL(function_state_->generator_object_variable());
|
|
|
|
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
|
|
|
|
args->Add(factory()->NewThisFunction(pos), zone());
|
2016-08-11 12:04:02 +00:00
|
|
|
args->Add(IsArrowFunction(kind) ? GetLiteralUndefined(pos)
|
|
|
|
: ThisExpression(kNoSourcePosition),
|
2016-05-17 01:14:27 +00:00
|
|
|
zone());
|
2016-05-17 00:26:53 +00:00
|
|
|
return factory()->NewCallRuntime(Runtime::kCreateJSGeneratorObject, args,
|
|
|
|
pos);
|
|
|
|
}
|
|
|
|
|
2016-08-26 22:17:52 +00:00
|
|
|
Expression* Parser::BuildResolvePromise(Expression* value, int pos) {
|
|
|
|
// %ResolvePromise(.promise, value), .promise
|
|
|
|
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
|
2016-09-20 21:30:45 +00:00
|
|
|
args->Add(factory()->NewVariableProxy(PromiseVariable()), zone());
|
2016-05-17 00:26:53 +00:00
|
|
|
args->Add(value, zone());
|
2016-08-26 22:17:52 +00:00
|
|
|
Expression* call_runtime =
|
|
|
|
factory()->NewCallRuntime(Context::PROMISE_RESOLVE_INDEX, args, pos);
|
2016-09-20 21:30:45 +00:00
|
|
|
return factory()->NewBinaryOperation(
|
|
|
|
Token::COMMA, call_runtime,
|
|
|
|
factory()->NewVariableProxy(PromiseVariable()), pos);
|
2016-05-17 00:26:53 +00:00
|
|
|
}
|
|
|
|
|
2016-08-26 22:17:52 +00:00
|
|
|
Expression* Parser::BuildRejectPromise(Expression* value, int pos) {
|
|
|
|
// %RejectPromiseNoDebugEvent(.promise, value, true), .promise
|
|
|
|
// The NoDebugEvent variant disables the additional debug event for the
|
|
|
|
// rejection since a debug event already happened for the exception that got
|
|
|
|
// us here.
|
|
|
|
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
|
2016-09-20 21:30:45 +00:00
|
|
|
args->Add(factory()->NewVariableProxy(PromiseVariable()), zone());
|
2016-05-17 00:26:53 +00:00
|
|
|
args->Add(value, zone());
|
2016-08-26 22:17:52 +00:00
|
|
|
Expression* call_runtime = factory()->NewCallRuntime(
|
|
|
|
Context::REJECT_PROMISE_NO_DEBUG_EVENT_INDEX, args, pos);
|
2016-09-20 21:30:45 +00:00
|
|
|
return factory()->NewBinaryOperation(
|
|
|
|
Token::COMMA, call_runtime,
|
|
|
|
factory()->NewVariableProxy(PromiseVariable()), pos);
|
2016-08-26 22:17:52 +00:00
|
|
|
}
|
|
|
|
|
2016-09-20 21:30:45 +00:00
|
|
|
Variable* Parser::PromiseVariable() {
|
|
|
|
// Based on the various compilation paths, there are many different code
|
|
|
|
// paths which may be the first to access the Promise temporary. Whichever
|
|
|
|
// comes first should create it and stash it in the FunctionState.
|
|
|
|
Variable* promise = function_state_->promise_variable();
|
|
|
|
if (function_state_->promise_variable() == nullptr) {
|
|
|
|
promise = scope()->NewTemporary(ast_value_factory()->empty_string());
|
|
|
|
function_state_->set_promise_variable(promise);
|
|
|
|
}
|
|
|
|
return promise;
|
2016-05-17 00:26:53 +00:00
|
|
|
}
|
2015-06-22 14:15:53 +00:00
|
|
|
|
2016-09-30 07:53:43 +00:00
|
|
|
Expression* Parser::BuildInitialYield(int pos, FunctionKind kind) {
|
|
|
|
Expression* allocation = BuildCreateJSGeneratorObject(pos, kind);
|
|
|
|
VariableProxy* init_proxy =
|
|
|
|
factory()->NewVariableProxy(function_state_->generator_object_variable());
|
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
|
|
|
Token::INIT, init_proxy, allocation, kNoSourcePosition);
|
|
|
|
VariableProxy* get_proxy =
|
|
|
|
factory()->NewVariableProxy(function_state_->generator_object_variable());
|
|
|
|
// The position of the yield is important for reporting the exception
|
|
|
|
// caused by calling the .throw method on a generator suspended at the
|
|
|
|
// initial yield (i.e. right after generator instantiation).
|
|
|
|
return factory()->NewYield(get_proxy, assignment, scope()->start_position(),
|
|
|
|
Yield::kOnExceptionThrow);
|
|
|
|
}
|
|
|
|
|
2014-04-15 08:29:24 +00:00
|
|
|
ZoneList<Statement*>* Parser::ParseEagerFunctionBody(
|
2015-06-22 14:15:53 +00:00
|
|
|
const AstRawString* function_name, int pos,
|
2015-08-03 09:14:19 +00:00
|
|
|
const ParserFormalParameters& parameters, FunctionKind kind,
|
|
|
|
FunctionLiteral::FunctionType function_type, bool* ok) {
|
2016-10-11 12:13:37 +00:00
|
|
|
ParsingModeScope mode(this, allow_lazy() ? PARSE_LAZILY : PARSE_EAGERLY);
|
2015-07-15 10:59:52 +00:00
|
|
|
ZoneList<Statement*>* result = new(zone()) ZoneList<Statement*>(8, zone());
|
2016-09-16 10:43:20 +00:00
|
|
|
|
2015-08-03 09:14:19 +00:00
|
|
|
static const int kFunctionNameAssignmentIndex = 0;
|
2016-01-08 20:38:07 +00:00
|
|
|
if (function_type == FunctionLiteral::kNamedExpression) {
|
2015-08-03 09:14:19 +00:00
|
|
|
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());
|
|
|
|
}
|
2015-02-06 10:34:50 +00:00
|
|
|
|
2015-07-15 10:59:52 +00:00
|
|
|
ZoneList<Statement*>* body = result;
|
2016-08-05 14:30:54 +00:00
|
|
|
DeclarationScope* function_scope = scope()->AsDeclarationScope();
|
|
|
|
DeclarationScope* inner_scope = function_scope;
|
2015-07-15 10:59:52 +00:00
|
|
|
Block* inner_block = nullptr;
|
2015-07-23 14:28:59 +00:00
|
|
|
if (!parameters.is_simple) {
|
2016-08-05 14:30:54 +00:00
|
|
|
inner_scope = NewVarblockScope();
|
2015-07-15 10:59:52 +00:00
|
|
|
inner_scope->set_start_position(scanner()->location().beg_pos);
|
2016-06-30 09:26:30 +00:00
|
|
|
inner_block = factory()->NewBlock(NULL, 8, true, kNoSourcePosition);
|
2015-07-16 16:44:58 +00:00
|
|
|
inner_block->set_scope(inner_scope);
|
|
|
|
body = inner_block->statements();
|
2015-06-22 14:15:53 +00:00
|
|
|
}
|
|
|
|
|
2015-07-15 10:59:52 +00:00
|
|
|
{
|
2016-07-19 10:06:38 +00:00
|
|
|
BlockState block_state(&scope_state_, inner_scope);
|
2014-04-15 08:29:24 +00:00
|
|
|
|
2015-07-15 10:59:52 +00:00
|
|
|
if (IsGeneratorFunction(kind)) {
|
2016-01-27 08:12:30 +00:00
|
|
|
// We produce:
|
|
|
|
//
|
2016-03-06 09:19:14 +00:00
|
|
|
// try { InitialYield; ...body...; return {value: undefined, done: true} }
|
2016-05-20 09:40:24 +00:00
|
|
|
// finally { %_GeneratorClose(generator) }
|
2016-01-27 08:12:30 +00:00
|
|
|
//
|
|
|
|
// - InitialYield yields the actual generator object.
|
2016-03-06 09:19:14 +00:00
|
|
|
// - Any return statement inside the body will have its argument wrapped
|
|
|
|
// in a "done" iterator result object.
|
2016-01-27 08:12:30 +00:00
|
|
|
// - If the generator terminates for whatever reason, we must close it.
|
|
|
|
// Hence the finally clause.
|
|
|
|
|
|
|
|
Block* try_block =
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewBlock(nullptr, 3, false, kNoSourcePosition);
|
2016-09-30 07:53:43 +00:00
|
|
|
Expression* initial_yield = BuildInitialYield(pos, kind);
|
|
|
|
try_block->statements()->Add(
|
|
|
|
factory()->NewExpressionStatement(initial_yield, kNoSourcePosition),
|
|
|
|
zone());
|
2016-01-27 08:12:30 +00:00
|
|
|
ParseStatementList(try_block->statements(), Token::RBRACE, CHECK_OK);
|
2015-07-15 10:59:52 +00:00
|
|
|
|
2016-03-06 09:19:14 +00:00
|
|
|
Statement* final_return = factory()->NewReturnStatement(
|
2016-06-30 09:26:30 +00:00
|
|
|
BuildIteratorResult(nullptr, true), kNoSourcePosition);
|
2016-03-06 09:19:14 +00:00
|
|
|
try_block->statements()->Add(final_return, zone());
|
2016-01-27 08:12:30 +00:00
|
|
|
|
|
|
|
Block* finally_block =
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewBlock(nullptr, 1, false, kNoSourcePosition);
|
2016-01-27 08:12:30 +00:00
|
|
|
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(
|
2016-06-30 09:26:30 +00:00
|
|
|
Runtime::kInlineGeneratorClose, args, kNoSourcePosition);
|
2016-01-27 08:12:30 +00:00
|
|
|
finally_block->statements()->Add(
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewExpressionStatement(call, kNoSourcePosition), zone());
|
2016-01-27 08:12:30 +00:00
|
|
|
|
|
|
|
body->Add(factory()->NewTryFinallyStatement(try_block, finally_block,
|
2016-06-30 09:26:30 +00:00
|
|
|
kNoSourcePosition),
|
2016-01-27 08:12:30 +00:00
|
|
|
zone());
|
2016-05-17 00:26:53 +00:00
|
|
|
} else if (IsAsyncFunction(kind)) {
|
|
|
|
const bool accept_IN = true;
|
2016-09-27 18:01:58 +00:00
|
|
|
ParseAsyncFunctionBody(inner_scope, body, kind, FunctionBodyType::kNormal,
|
|
|
|
accept_IN, pos, CHECK_OK);
|
2016-01-27 08:12:30 +00:00
|
|
|
} else {
|
|
|
|
ParseStatementList(body, Token::RBRACE, CHECK_OK);
|
2015-07-15 10:59:52 +00:00
|
|
|
}
|
2014-04-15 08:29:24 +00:00
|
|
|
|
2015-07-15 10:59:52 +00:00
|
|
|
if (IsSubclassConstructor(kind)) {
|
2016-08-25 08:48:45 +00:00
|
|
|
body->Add(factory()->NewReturnStatement(ThisExpression(kNoSourcePosition),
|
|
|
|
kNoSourcePosition),
|
2016-06-30 09:26:30 +00:00
|
|
|
zone());
|
2015-07-15 10:59:52 +00:00
|
|
|
}
|
2015-02-06 10:34:50 +00:00
|
|
|
}
|
|
|
|
|
2014-04-15 08:29:24 +00:00
|
|
|
Expect(Token::RBRACE, CHECK_OK);
|
2016-07-19 10:06:38 +00:00
|
|
|
scope()->set_end_position(scanner()->location().end_pos);
|
2015-07-16 16:44:58 +00:00
|
|
|
|
2015-07-23 14:28:59 +00:00
|
|
|
if (!parameters.is_simple) {
|
2015-07-16 16:44:58 +00:00
|
|
|
DCHECK_NOT_NULL(inner_scope);
|
2016-08-05 14:30:54 +00:00
|
|
|
DCHECK_EQ(function_scope, scope());
|
|
|
|
DCHECK_EQ(function_scope, inner_scope->outer_scope());
|
2015-07-16 16:44:58 +00:00
|
|
|
DCHECK_EQ(body, inner_block->statements());
|
2016-08-05 14:30:54 +00:00
|
|
|
SetLanguageMode(function_scope, inner_scope->language_mode());
|
2015-07-23 11:53:31 +00:00
|
|
|
Block* init_block = BuildParameterInitializationBlock(parameters, CHECK_OK);
|
2016-05-17 00:26:53 +00:00
|
|
|
|
2016-06-29 20:52:28 +00:00
|
|
|
if (is_sloppy(inner_scope->language_mode())) {
|
2016-09-15 16:41:03 +00:00
|
|
|
InsertSloppyBlockFunctionVarBindings(inner_scope);
|
2016-06-29 20:52:28 +00:00
|
|
|
}
|
|
|
|
|
2016-08-26 22:17:52 +00:00
|
|
|
// TODO(littledan): Merge the two rejection blocks into one
|
2016-05-17 00:26:53 +00:00
|
|
|
if (IsAsyncFunction(kind)) {
|
2016-08-26 22:17:52 +00:00
|
|
|
init_block = BuildRejectPromiseOnException(init_block, CHECK_OK);
|
2016-05-17 00:26:53 +00:00
|
|
|
}
|
|
|
|
|
2015-07-16 16:44:58 +00:00
|
|
|
DCHECK_NOT_NULL(init_block);
|
|
|
|
|
2015-07-15 10:59:52 +00:00
|
|
|
inner_scope->set_end_position(scanner()->location().end_pos);
|
2016-08-05 14:30:54 +00:00
|
|
|
if (inner_scope->FinalizeBlockScope() != nullptr) {
|
2015-07-20 13:48:57 +00:00
|
|
|
CheckConflictingVarDeclarations(inner_scope, CHECK_OK);
|
2015-10-01 10:42:23 +00:00
|
|
|
InsertShadowingVarBindingInitializers(inner_block);
|
2015-07-20 13:48:57 +00:00
|
|
|
}
|
2016-08-05 14:30:54 +00:00
|
|
|
inner_scope = nullptr;
|
2015-07-16 16:44:58 +00:00
|
|
|
|
|
|
|
result->Add(init_block, zone());
|
|
|
|
result->Add(inner_block, zone());
|
2016-06-29 20:52:28 +00:00
|
|
|
} else {
|
2016-08-05 14:30:54 +00:00
|
|
|
DCHECK_EQ(inner_scope, function_scope);
|
|
|
|
if (is_sloppy(function_scope->language_mode())) {
|
2016-09-15 16:41:03 +00:00
|
|
|
InsertSloppyBlockFunctionVarBindings(function_scope);
|
2016-06-29 20:52:28 +00:00
|
|
|
}
|
2015-07-15 10:59:52 +00:00
|
|
|
}
|
2014-04-15 08:29:24 +00:00
|
|
|
|
2016-09-22 19:16:23 +00:00
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2016-01-08 20:38:07 +00:00
|
|
|
if (function_type == FunctionLiteral::kNamedExpression) {
|
2016-09-21 09:15:12 +00:00
|
|
|
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);
|
2015-08-03 09:14:19 +00:00
|
|
|
}
|
|
|
|
|
2016-05-04 13:42:52 +00:00
|
|
|
MarkCollectedTailCallExpressions();
|
2015-07-15 10:59:52 +00:00
|
|
|
return result;
|
2014-04-15 08:29:24 +00:00
|
|
|
}
|
|
|
|
|
2016-10-14 14:08:53 +00:00
|
|
|
PreParser::PreParseResult Parser::ParseFunctionBodyWithPreParser(
|
2016-09-27 09:48:17 +00:00
|
|
|
SingletonLogger* logger, bool is_inner_function, bool may_abort) {
|
2016-07-07 11:18:13 +00:00
|
|
|
TRACE_EVENT0(TRACE_DISABLED_BY_DEFAULT("v8.compile"), "V8.PreParse");
|
2016-02-18 06:12:45 +00:00
|
|
|
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK_EQ(Token::LBRACE, scanner()->current_token());
|
2011-11-25 09:36:31 +00:00
|
|
|
|
|
|
|
if (reusable_preparser_ == NULL) {
|
2015-02-12 13:02:30 +00:00
|
|
|
reusable_preparser_ = new PreParser(zone(), &scanner_, ast_value_factory(),
|
|
|
|
NULL, stack_limit_);
|
Refactor parser mode configuration for correctness
This patch refactors the parser and preparser interface to be more
readable and type-safe. It has no behavior changes.
Previously, parsers and preparsers were configured via bitfield called
parser_flags in the Parser constructor, and flags in
PreParser::PreParseProgram, ParserApi::Parse, and ParserApi::PreParse.
This was error-prone in practice: six call sites passed incorrectly
typed values to this interface (a boolean FLAG value, a boolean false
and a boolean true value). None of these errors were caught by the
compiler because it's just an "int".
The parser flags interface was also awkward because it encoded a
language mode, but the language mode was only used to turn on harmony
scoping or not -- it wasn't used to actually set the parser's language
mode.
Fundamentally these errors came in because of the desire for a
procedural parser interface, in ParserApi. Because we need to be able
to configure the parser in various ways, the flags argument got added;
but no one understood how to use the flags properly. Also they were
only used by constructors: callers packed bits, and the constructors
unpacked them into booleans on the parser or preparser.
The solution is to allow parser construction, configuration, and
invocation to be separated. This patch does that.
It passes the existing tests.
BUG=
Review URL: https://codereview.chromium.org/13450007
Patch from Andy Wingo <wingo@igalia.com>.
git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@14151 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
2013-04-05 13:01:06 +00:00
|
|
|
reusable_preparser_->set_allow_lazy(true);
|
2015-06-09 15:43:07 +00:00
|
|
|
#define SET_ALLOW(name) reusable_preparser_->set_allow_##name(allow_##name());
|
|
|
|
SET_ALLOW(natives);
|
2015-10-21 02:55:47 +00:00
|
|
|
SET_ALLOW(harmony_do_expressions);
|
2016-02-17 00:18:38 +00:00
|
|
|
SET_ALLOW(harmony_function_sent);
|
2016-03-19 00:53:30 +00:00
|
|
|
SET_ALLOW(harmony_restrictive_declarations);
|
2016-05-16 23:17:13 +00:00
|
|
|
SET_ALLOW(harmony_async_await);
|
Allow trailing commas in function parameter lists
Add a flag harmony_trailing_commas_in_parameters that allows trailing
commas in function parameter declaration lists and function call
parameter lists. Trailing commas are allowed in parenthetical lists like
`(a, b, c,)` only if the next token is `=>`, thereby making it an arrow
function declaration. Only 1 trailing comma is allowed, not `(a,,)`. A
trailing comma must follow a non-rest parameter, so `(,)` and `(...a,)`
are still SyntaxErrors. However, a trailing comma is allowed after a
spread parameter, e.g. `a(...b,);`.
Add parser tests for all of the above.
BUG=v8:5051
LOG=y
Review-Url: https://codereview.chromium.org/2094463002
Cr-Commit-Position: refs/heads/master@{#37355}
2016-06-29 01:37:57 +00:00
|
|
|
SET_ALLOW(harmony_trailing_commas);
|
2016-09-16 00:42:30 +00:00
|
|
|
SET_ALLOW(harmony_class_fields);
|
2015-06-09 15:43:07 +00:00
|
|
|
#undef SET_ALLOW
|
2011-11-25 09:36:31 +00:00
|
|
|
}
|
2016-09-27 09:48:17 +00:00
|
|
|
// Aborting inner function preparsing would leave scopes in an inconsistent
|
|
|
|
// state; we don't parse inner functions in the abortable mode anyway.
|
|
|
|
DCHECK(!is_inner_function || !may_abort);
|
|
|
|
|
2016-09-27 09:49:26 +00:00
|
|
|
DeclarationScope* function_scope = function_state_->scope();
|
2016-10-14 14:08:53 +00:00
|
|
|
PreParser::PreParseResult result = reusable_preparser_->PreParseFunction(
|
2016-09-27 09:49:26 +00:00
|
|
|
function_scope, parsing_module_, logger, is_inner_function, may_abort,
|
|
|
|
use_counts_);
|
2011-11-25 09:36:31 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-09-16 00:46:00 +00:00
|
|
|
Expression* Parser::InstallHomeObject(Expression* function_literal,
|
|
|
|
Expression* home_object) {
|
|
|
|
Block* do_block = factory()->NewBlock(nullptr, 1, false, kNoSourcePosition);
|
|
|
|
Variable* result_var =
|
|
|
|
scope()->NewTemporary(ast_value_factory()->empty_string());
|
|
|
|
DoExpression* do_expr =
|
|
|
|
factory()->NewDoExpression(do_block, result_var, kNoSourcePosition);
|
|
|
|
Assignment* init = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, factory()->NewVariableProxy(result_var), function_literal,
|
|
|
|
kNoSourcePosition);
|
|
|
|
do_block->statements()->Add(
|
|
|
|
factory()->NewExpressionStatement(init, kNoSourcePosition), zone());
|
|
|
|
Property* home_object_property = factory()->NewProperty(
|
|
|
|
factory()->NewVariableProxy(result_var),
|
|
|
|
factory()->NewSymbolLiteral("home_object_symbol", kNoSourcePosition),
|
|
|
|
kNoSourcePosition);
|
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, home_object_property, home_object, kNoSourcePosition);
|
|
|
|
do_block->statements()->Add(
|
|
|
|
factory()->NewExpressionStatement(assignment, kNoSourcePosition), zone());
|
|
|
|
return do_expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
const AstRawString* ClassFieldVariableName(bool is_name,
|
|
|
|
AstValueFactory* ast_value_factory,
|
|
|
|
int index) {
|
|
|
|
std::string name =
|
|
|
|
".class-field-" + std::to_string(index) + (is_name ? "-name" : "-func");
|
|
|
|
return ast_value_factory->GetOneByteString(name.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
FunctionLiteral* Parser::SynthesizeClassFieldInitializer(int count) {
|
|
|
|
DCHECK(count > 0);
|
|
|
|
// Makes a function which reads the names and initializers for each class
|
|
|
|
// field out of deterministically named local variables and sets each property
|
|
|
|
// to the result of evaluating its corresponding initializer in turn.
|
|
|
|
|
|
|
|
// This produces a function which looks like
|
|
|
|
// function () {
|
|
|
|
// this[.class-field-0-name] = .class-field-0-func();
|
|
|
|
// this[.class-field-1-name] = .class-field-1-func();
|
|
|
|
// [...]
|
|
|
|
// this[.class-field-n-name] = .class-field-n-func();
|
|
|
|
// return this;
|
|
|
|
// }
|
|
|
|
// except that it performs defineProperty, so that instead of '=' it has
|
|
|
|
// %DefineDataPropertyInLiteral(this, .class-field-0-name,
|
|
|
|
// .class-field-0-func(),
|
|
|
|
// DONT_ENUM, false)
|
|
|
|
|
|
|
|
RaiseLanguageMode(STRICT);
|
|
|
|
FunctionKind kind = FunctionKind::kConciseMethod;
|
|
|
|
DeclarationScope* initializer_scope = NewFunctionScope(kind);
|
|
|
|
SetLanguageMode(initializer_scope, language_mode());
|
|
|
|
initializer_scope->set_start_position(scanner()->location().end_pos);
|
|
|
|
initializer_scope->set_end_position(scanner()->location().end_pos);
|
|
|
|
FunctionState initializer_state(&function_state_, &scope_state_,
|
2016-09-27 11:41:00 +00:00
|
|
|
initializer_scope);
|
2016-09-16 00:46:00 +00:00
|
|
|
ZoneList<Statement*>* body = new (zone()) ZoneList<Statement*>(count, zone());
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
|
const AstRawString* name =
|
|
|
|
ClassFieldVariableName(true, ast_value_factory(), i);
|
|
|
|
VariableProxy* name_proxy = scope()->NewUnresolved(factory(), name);
|
|
|
|
const AstRawString* function_name =
|
|
|
|
ClassFieldVariableName(false, ast_value_factory(), i);
|
|
|
|
VariableProxy* function_proxy =
|
|
|
|
scope()->NewUnresolved(factory(), function_name);
|
|
|
|
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
|
|
|
|
args->Add(function_proxy, zone());
|
|
|
|
args->Add(ThisExpression(kNoSourcePosition), zone());
|
|
|
|
Expression* call = factory()->NewCallRuntime(Runtime::kInlineCall, args,
|
|
|
|
kNoSourcePosition);
|
|
|
|
ZoneList<Expression*>* define_property_args =
|
|
|
|
new (zone()) ZoneList<Expression*>(5, zone());
|
|
|
|
define_property_args->Add(ThisExpression(kNoSourcePosition), zone());
|
|
|
|
define_property_args->Add(name_proxy, zone());
|
|
|
|
define_property_args->Add(call, zone());
|
|
|
|
define_property_args->Add(
|
|
|
|
factory()->NewNumberLiteral(DONT_ENUM, kNoSourcePosition), zone());
|
|
|
|
define_property_args->Add(
|
|
|
|
factory()->NewNumberLiteral(
|
|
|
|
false, // TODO(bakkot) function name inference a la class { x =
|
|
|
|
// function(){}; static y = function(){}; }
|
|
|
|
kNoSourcePosition),
|
|
|
|
zone());
|
|
|
|
body->Add(factory()->NewExpressionStatement(
|
|
|
|
factory()->NewCallRuntime(
|
|
|
|
Runtime::kDefineDataProperty,
|
|
|
|
define_property_args, // TODO(bakkot) verify that this is
|
|
|
|
// the same as object_define_property
|
|
|
|
kNoSourcePosition),
|
|
|
|
kNoSourcePosition),
|
|
|
|
zone());
|
|
|
|
}
|
|
|
|
body->Add(factory()->NewReturnStatement(ThisExpression(kNoSourcePosition),
|
|
|
|
kNoSourcePosition),
|
|
|
|
zone());
|
|
|
|
FunctionLiteral* function_literal = factory()->NewFunctionLiteral(
|
|
|
|
ast_value_factory()->empty_string(), initializer_scope, body,
|
|
|
|
initializer_state.materialized_literal_count(),
|
2016-10-13 12:34:37 +00:00
|
|
|
initializer_state.expected_property_count(), 0, count,
|
2016-09-16 00:46:00 +00:00
|
|
|
FunctionLiteral::kNoDuplicateParameters,
|
|
|
|
FunctionLiteral::kAnonymousExpression,
|
2016-09-28 01:20:44 +00:00
|
|
|
FunctionLiteral::kShouldLazyCompile, initializer_scope->start_position());
|
2016-09-16 00:46:00 +00:00
|
|
|
function_literal->set_is_class_field_initializer(true);
|
|
|
|
return function_literal;
|
|
|
|
}
|
|
|
|
|
|
|
|
FunctionLiteral* Parser::InsertClassFieldInitializer(
|
|
|
|
FunctionLiteral* constructor) {
|
|
|
|
Statement* call_initializer = factory()->NewExpressionStatement(
|
|
|
|
CallClassFieldInitializer(
|
|
|
|
constructor->scope(),
|
|
|
|
constructor->scope()->NewUnresolved(
|
|
|
|
factory(), ast_value_factory()->this_string(), kNoSourcePosition,
|
|
|
|
kNoSourcePosition + 4, THIS_VARIABLE)),
|
|
|
|
kNoSourcePosition);
|
|
|
|
constructor->body()->InsertAt(0, call_initializer, zone());
|
|
|
|
return constructor;
|
|
|
|
}
|
|
|
|
|
2016-09-28 13:42:20 +00:00
|
|
|
// If a class name is specified, this method declares the class variable
|
|
|
|
// and sets class_info->proxy to point to that name.
|
|
|
|
void Parser::DeclareClassVariable(const AstRawString* name, Scope* block_scope,
|
|
|
|
ClassInfo* class_info, int class_token_pos,
|
|
|
|
bool* ok) {
|
2016-08-01 13:25:39 +00:00
|
|
|
#ifdef DEBUG
|
2016-07-19 10:06:38 +00:00
|
|
|
scope()->SetScopeName(name);
|
2016-08-01 13:25:39 +00:00
|
|
|
#endif
|
2014-11-14 15:05:05 +00:00
|
|
|
|
2016-08-01 09:04:13 +00:00
|
|
|
if (name != nullptr) {
|
2016-09-28 13:42:20 +00:00
|
|
|
class_info->proxy = factory()->NewVariableProxy(name, NORMAL_VARIABLE);
|
|
|
|
Declaration* declaration = factory()->NewVariableDeclaration(
|
|
|
|
class_info->proxy, block_scope, class_token_pos);
|
2016-08-10 08:10:18 +00:00
|
|
|
Declare(declaration, DeclarationDescriptor::NORMAL, CONST,
|
2016-09-28 13:42:20 +00:00
|
|
|
Variable::DefaultInitializationFlag(CONST), ok);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This method declares a property of the given class. It updates the
|
|
|
|
// following fields of class_info, as appropriate:
|
|
|
|
// - constructor
|
|
|
|
// - static_initializer_var
|
|
|
|
// - instance_field_initializers
|
|
|
|
// - properties
|
|
|
|
void Parser::DeclareClassProperty(const AstRawString* class_name,
|
|
|
|
ClassLiteralProperty* property,
|
|
|
|
ClassInfo* class_info, bool* ok) {
|
|
|
|
if (class_info->has_seen_constructor && class_info->constructor == nullptr) {
|
|
|
|
class_info->constructor = GetPropertyValue(property)->AsFunctionLiteral();
|
|
|
|
DCHECK_NOT_NULL(class_info->constructor);
|
|
|
|
class_info->constructor->set_raw_name(
|
|
|
|
class_name != nullptr ? class_name
|
|
|
|
: ast_value_factory()->empty_string());
|
|
|
|
return;
|
2016-09-28 11:16:08 +00:00
|
|
|
}
|
|
|
|
|
2016-09-28 13:42:20 +00:00
|
|
|
if (property->kind() == ClassLiteralProperty::FIELD) {
|
|
|
|
DCHECK(allow_harmony_class_fields());
|
|
|
|
if (property->is_static()) {
|
|
|
|
if (class_info->static_initializer_var == nullptr) {
|
|
|
|
class_info->static_initializer_var =
|
|
|
|
NewTemporary(ast_value_factory()->empty_string());
|
|
|
|
}
|
|
|
|
// TODO(bakkot) only do this conditionally
|
|
|
|
Expression* function = InstallHomeObject(
|
|
|
|
property->value(),
|
|
|
|
factory()->NewVariableProxy(class_info->static_initializer_var));
|
|
|
|
ZoneList<Expression*>* args =
|
|
|
|
new (zone()) ZoneList<Expression*>(2, zone());
|
|
|
|
args->Add(function, zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(class_info->static_initializer_var),
|
|
|
|
zone());
|
|
|
|
Expression* call = factory()->NewCallRuntime(Runtime::kInlineCall, args,
|
|
|
|
kNoSourcePosition);
|
|
|
|
property->set_value(call);
|
|
|
|
} else {
|
|
|
|
// if (is_computed_name) { // TODO(bakkot) figure out why this is
|
|
|
|
// necessary for non-computed names in full-codegen
|
|
|
|
ZoneList<Expression*>* to_name_args =
|
|
|
|
new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
to_name_args->Add(property->key(), zone());
|
|
|
|
property->set_key(factory()->NewCallRuntime(
|
|
|
|
Runtime::kToName, to_name_args, kNoSourcePosition));
|
|
|
|
//}
|
|
|
|
const AstRawString* name = ClassFieldVariableName(
|
|
|
|
true, ast_value_factory(),
|
|
|
|
class_info->instance_field_initializers->length());
|
|
|
|
VariableProxy* name_proxy =
|
|
|
|
factory()->NewVariableProxy(name, NORMAL_VARIABLE);
|
|
|
|
Declaration* name_declaration = factory()->NewVariableDeclaration(
|
|
|
|
name_proxy, scope(), kNoSourcePosition);
|
|
|
|
Variable* name_var =
|
|
|
|
Declare(name_declaration, DeclarationDescriptor::NORMAL, CONST,
|
|
|
|
kNeedsInitialization, ok, scope());
|
|
|
|
DCHECK(*ok);
|
|
|
|
if (!*ok) return;
|
|
|
|
class_info->instance_field_initializers->Add(property->value(), zone());
|
|
|
|
property->set_value(factory()->NewVariableProxy(name_var));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
class_info->properties->Add(property, zone());
|
|
|
|
}
|
|
|
|
|
|
|
|
// This method rewrites a class literal into a do-expression.
|
|
|
|
// It uses the following fields of class_info:
|
|
|
|
// - constructor (if missing, it updates it with a default constructor)
|
|
|
|
// - proxy
|
|
|
|
// - extends
|
|
|
|
// - static_initializer_var
|
|
|
|
// - instance_field_initializers
|
|
|
|
// - properties
|
|
|
|
Expression* Parser::RewriteClassLiteral(const AstRawString* name,
|
|
|
|
ClassInfo* class_info, int pos,
|
|
|
|
bool* ok) {
|
|
|
|
int end_pos = scanner()->location().end_pos;
|
2016-09-16 00:42:30 +00:00
|
|
|
Block* do_block = factory()->NewBlock(nullptr, 1, false, pos);
|
|
|
|
Variable* result_var = NewTemporary(ast_value_factory()->empty_string());
|
|
|
|
DoExpression* do_expr = factory()->NewDoExpression(do_block, result_var, pos);
|
2014-11-14 15:05:05 +00:00
|
|
|
|
2016-09-28 13:42:20 +00:00
|
|
|
bool has_extends = class_info->extends != nullptr;
|
|
|
|
bool has_instance_fields =
|
|
|
|
class_info->instance_field_initializers->length() > 0;
|
2016-09-16 00:42:30 +00:00
|
|
|
DCHECK(!has_instance_fields || allow_harmony_class_fields());
|
2016-09-28 13:42:20 +00:00
|
|
|
bool has_default_constructor = class_info->constructor == nullptr;
|
2016-09-16 00:42:30 +00:00
|
|
|
if (has_default_constructor) {
|
2016-09-28 13:42:20 +00:00
|
|
|
class_info->constructor =
|
|
|
|
DefaultConstructor(name, has_extends, has_instance_fields, pos, end_pos,
|
|
|
|
scope()->language_mode());
|
2014-11-14 15:05:05 +00:00
|
|
|
}
|
|
|
|
|
2016-09-28 13:42:20 +00:00
|
|
|
if (has_instance_fields && !has_extends) {
|
|
|
|
class_info->constructor =
|
|
|
|
InsertClassFieldInitializer(class_info->constructor);
|
|
|
|
class_info->constructor->set_requires_class_field_init(true);
|
2016-09-16 00:42:30 +00:00
|
|
|
} // The derived case is handled by rewriting super calls.
|
|
|
|
|
2016-09-28 13:42:20 +00:00
|
|
|
scope()->set_end_position(end_pos);
|
2014-11-14 15:05:05 +00:00
|
|
|
|
2016-08-01 09:04:13 +00:00
|
|
|
if (name != nullptr) {
|
2016-09-28 13:42:20 +00:00
|
|
|
DCHECK_NOT_NULL(class_info->proxy);
|
|
|
|
class_info->proxy->var()->set_initializer_position(end_pos);
|
2014-11-14 15:05:05 +00:00
|
|
|
}
|
|
|
|
|
2016-07-25 19:16:21 +00:00
|
|
|
ClassLiteral* class_literal = factory()->NewClassLiteral(
|
2016-09-28 13:42:20 +00:00
|
|
|
class_info->proxy, class_info->extends, class_info->constructor,
|
|
|
|
class_info->properties, pos, end_pos);
|
2016-07-25 19:16:21 +00:00
|
|
|
|
2016-09-28 13:42:20 +00:00
|
|
|
if (class_info->static_initializer_var != nullptr) {
|
2016-09-16 00:42:30 +00:00
|
|
|
class_literal->set_static_initializer_proxy(
|
2016-09-28 13:42:20 +00:00
|
|
|
factory()->NewVariableProxy(class_info->static_initializer_var));
|
2016-09-16 00:42:30 +00:00
|
|
|
}
|
|
|
|
|
2016-07-25 19:16:21 +00:00
|
|
|
do_block->statements()->Add(
|
2016-09-16 00:42:30 +00:00
|
|
|
factory()->NewExpressionStatement(
|
|
|
|
factory()->NewAssignment(Token::ASSIGN,
|
|
|
|
factory()->NewVariableProxy(result_var),
|
|
|
|
class_literal, kNoSourcePosition),
|
|
|
|
pos),
|
|
|
|
zone());
|
2016-09-16 00:46:00 +00:00
|
|
|
if (allow_harmony_class_fields() &&
|
2016-09-28 13:42:20 +00:00
|
|
|
(has_instance_fields || (has_extends && !has_default_constructor))) {
|
2016-09-16 00:46:00 +00:00
|
|
|
// Default constructors for derived classes without fields will not try to
|
|
|
|
// read this variable, so there's no need to create it.
|
|
|
|
const AstRawString* init_fn_name =
|
|
|
|
ast_value_factory()->dot_class_field_init_string();
|
|
|
|
Variable* init_fn_var = scope()->DeclareLocal(
|
|
|
|
init_fn_name, CONST, kCreatedInitialized, NORMAL_VARIABLE);
|
|
|
|
Expression* initializer =
|
|
|
|
has_instance_fields
|
|
|
|
? static_cast<Expression*>(SynthesizeClassFieldInitializer(
|
2016-09-28 13:42:20 +00:00
|
|
|
class_info->instance_field_initializers->length()))
|
2016-09-16 00:46:00 +00:00
|
|
|
: factory()->NewBooleanLiteral(false, kNoSourcePosition);
|
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
|
|
|
Token::INIT, factory()->NewVariableProxy(init_fn_var), initializer,
|
|
|
|
kNoSourcePosition);
|
|
|
|
do_block->statements()->Add(
|
|
|
|
factory()->NewExpressionStatement(assignment, kNoSourcePosition),
|
|
|
|
zone());
|
|
|
|
}
|
2016-09-28 13:42:20 +00:00
|
|
|
for (int i = 0; i < class_info->instance_field_initializers->length(); ++i) {
|
2016-09-16 00:46:00 +00:00
|
|
|
const AstRawString* function_name =
|
|
|
|
ClassFieldVariableName(false, ast_value_factory(), i);
|
2016-09-19 20:07:01 +00:00
|
|
|
VariableProxy* function_proxy =
|
|
|
|
factory()->NewVariableProxy(function_name, NORMAL_VARIABLE);
|
2016-09-16 00:46:00 +00:00
|
|
|
Declaration* function_declaration = factory()->NewVariableDeclaration(
|
|
|
|
function_proxy, scope(), kNoSourcePosition);
|
|
|
|
Variable* function_var =
|
|
|
|
Declare(function_declaration, DeclarationDescriptor::NORMAL, CONST,
|
|
|
|
kNeedsInitialization, ok, scope());
|
2016-09-28 13:42:20 +00:00
|
|
|
if (!*ok) return nullptr;
|
2016-09-16 00:46:00 +00:00
|
|
|
Property* prototype_property = factory()->NewProperty(
|
|
|
|
factory()->NewVariableProxy(result_var),
|
|
|
|
factory()->NewStringLiteral(ast_value_factory()->prototype_string(),
|
|
|
|
kNoSourcePosition),
|
|
|
|
kNoSourcePosition);
|
|
|
|
Expression* function_value = InstallHomeObject(
|
2016-09-28 13:42:20 +00:00
|
|
|
class_info->instance_field_initializers->at(i),
|
2016-09-16 00:46:00 +00:00
|
|
|
prototype_property); // TODO(bakkot) ideally this would be conditional,
|
|
|
|
// especially in trivial cases
|
|
|
|
Assignment* function_assignment = factory()->NewAssignment(
|
|
|
|
Token::INIT, factory()->NewVariableProxy(function_var), function_value,
|
|
|
|
kNoSourcePosition);
|
|
|
|
do_block->statements()->Add(factory()->NewExpressionStatement(
|
|
|
|
function_assignment, kNoSourcePosition),
|
|
|
|
zone());
|
|
|
|
}
|
2016-09-28 13:42:20 +00:00
|
|
|
do_block->set_scope(scope()->FinalizeBlockScope());
|
|
|
|
do_expr->set_represented_function(class_info->constructor);
|
2016-07-25 19:16:21 +00:00
|
|
|
|
|
|
|
return do_expr;
|
2014-11-14 15:05:05 +00:00
|
|
|
}
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
Literal* Parser::GetLiteralUndefined(int position) {
|
2014-06-24 14:03:24 +00:00
|
|
|
return factory()->NewUndefinedLiteral(position);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-09-01 12:31:18 +00:00
|
|
|
void Parser::CheckConflictingVarDeclarations(Scope* scope, bool* ok) {
|
|
|
|
Declaration* decl = scope->CheckConflictingVarDeclarations();
|
|
|
|
if (decl != NULL) {
|
2015-10-01 10:42:23 +00:00
|
|
|
// In ES6, conflicting variable bindings are early errors.
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name = decl->proxy()->raw_name();
|
2011-09-01 12:31:18 +00:00
|
|
|
int position = decl->proxy()->position();
|
2016-06-30 09:26:30 +00:00
|
|
|
Scanner::Location location =
|
|
|
|
position == kNoSourcePosition
|
|
|
|
? Scanner::Location::invalid()
|
|
|
|
: Scanner::Location(position, position + 1);
|
2016-08-19 08:11:04 +00:00
|
|
|
ReportMessageAt(location, MessageTemplate::kVarRedeclaration, name);
|
2011-09-01 12:31:18 +00:00
|
|
|
*ok = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-01 10:42:23 +00:00
|
|
|
void Parser::InsertShadowingVarBindingInitializers(Block* inner_block) {
|
|
|
|
// For each var-binding that shadows a parameter, insert an assignment
|
|
|
|
// initializing the variable with the parameter.
|
|
|
|
Scope* inner_scope = inner_block->scope();
|
|
|
|
DCHECK(inner_scope->is_declaration_scope());
|
|
|
|
Scope* function_scope = inner_scope->outer_scope();
|
|
|
|
DCHECK(function_scope->is_function_scope());
|
|
|
|
ZoneList<Declaration*>* decls = inner_scope->declarations();
|
2016-08-05 14:30:54 +00:00
|
|
|
BlockState block_state(&scope_state_, inner_scope);
|
2015-10-01 10:42:23 +00:00
|
|
|
for (int i = 0; i < decls->length(); ++i) {
|
|
|
|
Declaration* decl = decls->at(i);
|
2016-08-10 08:10:18 +00:00
|
|
|
if (decl->proxy()->var()->mode() != VAR || !decl->IsVariableDeclaration()) {
|
|
|
|
continue;
|
|
|
|
}
|
2015-10-01 10:42:23 +00:00
|
|
|
const AstRawString* name = decl->proxy()->raw_name();
|
|
|
|
Variable* parameter = function_scope->LookupLocal(name);
|
|
|
|
if (parameter == nullptr) continue;
|
2016-08-11 12:04:02 +00:00
|
|
|
VariableProxy* to = NewUnresolved(name);
|
2015-10-01 10:42:23 +00:00
|
|
|
VariableProxy* from = factory()->NewVariableProxy(parameter);
|
2016-06-30 09:26:30 +00:00
|
|
|
Expression* assignment =
|
|
|
|
factory()->NewAssignment(Token::ASSIGN, to, from, kNoSourcePosition);
|
|
|
|
Statement* statement =
|
|
|
|
factory()->NewExpressionStatement(assignment, kNoSourcePosition);
|
2015-10-01 10:42:23 +00:00
|
|
|
inner_block->statements()->InsertAt(0, statement, zone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-09-15 16:41:03 +00:00
|
|
|
void Parser::InsertSloppyBlockFunctionVarBindings(DeclarationScope* scope) {
|
|
|
|
// For the outermost eval scope, we cannot hoist during parsing: let
|
|
|
|
// declarations in the surrounding scope may prevent hoisting, but the
|
|
|
|
// information is unaccessible during parsing. In this case, we hoist later in
|
|
|
|
// DeclarationScope::Analyze.
|
|
|
|
if (scope->is_eval_scope() && scope->outer_scope() == original_scope_) {
|
|
|
|
return;
|
2015-09-21 04:30:50 +00:00
|
|
|
}
|
2016-09-15 16:41:03 +00:00
|
|
|
scope->HoistSloppyBlockFunctions(factory());
|
2015-09-21 04:30:50 +00:00
|
|
|
}
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Parser support
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
bool Parser::TargetStackContainsLabel(const AstRawString* label) {
|
2016-09-05 13:42:01 +00:00
|
|
|
for (ParserTarget* t = target_stack_; t != NULL; t = t->previous()) {
|
2015-01-15 19:18:05 +00:00
|
|
|
if (ContainsLabel(t->statement()->labels(), label)) return true;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
BreakableStatement* Parser::LookupBreakTarget(const AstRawString* label,
|
|
|
|
bool* ok) {
|
|
|
|
bool anonymous = label == NULL;
|
2016-09-05 13:42:01 +00:00
|
|
|
for (ParserTarget* t = target_stack_; t != NULL; t = t->previous()) {
|
2015-01-15 19:18:05 +00:00
|
|
|
BreakableStatement* stat = t->statement();
|
2008-07-03 15:10:15 +00:00
|
|
|
if ((anonymous && stat->is_target_for_anonymous()) ||
|
|
|
|
(!anonymous && ContainsLabel(stat->labels(), label))) {
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
IterationStatement* Parser::LookupContinueTarget(const AstRawString* label,
|
2008-07-03 15:10:15 +00:00
|
|
|
bool* ok) {
|
2014-06-24 14:03:24 +00:00
|
|
|
bool anonymous = label == NULL;
|
2016-09-05 13:42:01 +00:00
|
|
|
for (ParserTarget* t = target_stack_; t != NULL; t = t->previous()) {
|
2015-01-15 19:18:05 +00:00
|
|
|
IterationStatement* stat = t->statement()->AsIterationStatement();
|
2008-07-03 15:10:15 +00:00
|
|
|
if (stat == NULL) continue;
|
|
|
|
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(stat->is_target_for_anonymous());
|
2008-07-03 15:10:15 +00:00
|
|
|
if (anonymous || ContainsLabel(stat->labels(), label)) {
|
|
|
|
return stat;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-20 09:39:32 +00:00
|
|
|
void Parser::HandleSourceURLComments(Isolate* isolate, Handle<Script> script) {
|
2016-08-10 08:40:24 +00:00
|
|
|
Handle<String> source_url = scanner_.SourceUrl(isolate);
|
|
|
|
if (!source_url.is_null()) {
|
2015-02-20 09:39:32 +00:00
|
|
|
script->set_source_url(*source_url);
|
2014-07-02 07:01:31 +00:00
|
|
|
}
|
2016-08-10 08:40:24 +00:00
|
|
|
Handle<String> source_mapping_url = scanner_.SourceMappingUrl(isolate);
|
|
|
|
if (!source_mapping_url.is_null()) {
|
2015-02-20 09:39:32 +00:00
|
|
|
script->set_source_mapping_url(*source_mapping_url);
|
2014-07-02 07:01:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-20 09:39:32 +00:00
|
|
|
void Parser::Internalize(Isolate* isolate, Handle<Script> script, bool error) {
|
2016-09-20 09:32:16 +00:00
|
|
|
// Internalize strings and values.
|
2015-02-20 09:39:32 +00:00
|
|
|
ast_value_factory()->Internalize(isolate);
|
2014-09-02 11:36:21 +00:00
|
|
|
|
|
|
|
// Error processing.
|
2015-02-20 09:39:32 +00:00
|
|
|
if (error) {
|
2014-09-02 11:36:21 +00:00
|
|
|
if (stack_overflow()) {
|
2015-02-20 09:39:32 +00:00
|
|
|
isolate->StackOverflow();
|
2014-09-02 11:36:21 +00:00
|
|
|
} else {
|
2015-02-25 14:17:39 +00:00
|
|
|
DCHECK(pending_error_handler_.has_pending_error());
|
|
|
|
pending_error_handler_.ThrowPendingError(isolate, script);
|
2014-09-02 11:36:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move statistics to Isolate.
|
2014-06-30 13:35:16 +00:00
|
|
|
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
|
|
|
|
++feature) {
|
2016-06-14 21:39:36 +00:00
|
|
|
if (use_counts_[feature] > 0) {
|
2015-02-20 09:39:32 +00:00
|
|
|
isolate->CountUsage(v8::Isolate::UseCounterFeature(feature));
|
2014-06-30 13:35:16 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-27 18:18:25 +00:00
|
|
|
if (scanner_.FoundHtmlComment()) {
|
|
|
|
isolate->CountUsage(v8::Isolate::kHtmlComment);
|
|
|
|
if (script->line_offset() == 0 && script->column_offset() == 0) {
|
|
|
|
isolate->CountUsage(v8::Isolate::kHtmlCommentInExternalScript);
|
|
|
|
}
|
|
|
|
}
|
2015-02-20 09:39:32 +00:00
|
|
|
isolate->counters()->total_preparse_skipped()->Increment(
|
2014-09-02 11:36:21 +00:00
|
|
|
total_preparse_skipped_);
|
2014-06-30 13:35:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// The Parser interface.
|
|
|
|
|
2008-11-25 11:07:48 +00:00
|
|
|
|
2015-03-09 14:51:13 +00:00
|
|
|
bool Parser::ParseStatic(ParseInfo* info) {
|
|
|
|
Parser parser(info);
|
2015-02-12 13:02:30 +00:00
|
|
|
if (parser.Parse(info)) {
|
2015-08-19 16:51:37 +00:00
|
|
|
info->set_language_mode(info->literal()->language_mode());
|
2015-02-12 11:56:57 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-09 14:51:13 +00:00
|
|
|
bool Parser::Parse(ParseInfo* info) {
|
2015-08-19 16:51:37 +00:00
|
|
|
DCHECK(info->literal() == NULL);
|
2010-10-04 11:35:46 +00:00
|
|
|
FunctionLiteral* result = NULL;
|
2015-02-12 13:02:30 +00:00
|
|
|
// Ok to use Isolate here; this function is only called in the main thread.
|
|
|
|
DCHECK(parsing_on_main_thread_);
|
|
|
|
Isolate* isolate = info->isolate();
|
2014-06-24 14:03:24 +00:00
|
|
|
|
2016-10-12 15:22:10 +00:00
|
|
|
if (info->is_toplevel()) {
|
2015-02-12 13:02:30 +00:00
|
|
|
SetCachedData(info);
|
2015-02-20 09:39:32 +00:00
|
|
|
result = ParseProgram(isolate, info);
|
2016-10-12 15:22:10 +00:00
|
|
|
} else {
|
2016-10-14 14:08:53 +00:00
|
|
|
result = ParseFunction(isolate, info);
|
2010-02-01 10:31:55 +00:00
|
|
|
}
|
2015-03-09 14:51:13 +00:00
|
|
|
info->set_literal(result);
|
2014-09-02 11:36:21 +00:00
|
|
|
|
2015-02-20 09:39:32 +00:00
|
|
|
Internalize(isolate, info->script(), result == NULL);
|
2010-10-04 11:35:46 +00:00
|
|
|
return (result != NULL);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2014-09-12 09:12:08 +00:00
|
|
|
|
2015-03-09 14:51:13 +00:00
|
|
|
void Parser::ParseOnBackground(ParseInfo* info) {
|
2015-02-12 13:02:30 +00:00
|
|
|
parsing_on_main_thread_ = false;
|
|
|
|
|
2015-08-19 16:51:37 +00:00
|
|
|
DCHECK(info->literal() == NULL);
|
2014-09-12 09:12:08 +00:00
|
|
|
FunctionLiteral* result = NULL;
|
|
|
|
|
|
|
|
CompleteParserRecorder recorder;
|
2014-11-17 12:16:27 +00:00
|
|
|
if (produce_cached_parse_data()) log_ = &recorder;
|
2014-09-12 09:12:08 +00:00
|
|
|
|
2016-07-29 09:17:08 +00:00
|
|
|
std::unique_ptr<Utf16CharacterStream> stream;
|
|
|
|
Utf16CharacterStream* stream_ptr;
|
|
|
|
if (info->character_stream()) {
|
|
|
|
DCHECK(info->source_stream() == nullptr);
|
|
|
|
stream_ptr = info->character_stream();
|
|
|
|
} else {
|
|
|
|
DCHECK(info->character_stream() == nullptr);
|
Rework scanner-character-streams.
- Smaller, more consistent streams API (Advance, Back, pos, Seek)
- Remove implementations from the header, in favor of creation functions.
Observe:
- Performance:
- All Utf16CharacterStream methods have an inlinable V8_LIKELY w/ a
body of only a few instructions. I expect most calls to end up there.
- There used to be performance problems w/ bookmarking, particularly
with copying too much data on SetBookmark w/ UTF-8 streaming streams.
All those copies are gone.
- The old streaming streams implementation used to copy data even for
2-byte input. It no longer does.
- The only remaining 'slow' method is the Seek(.) slow case for utf-8
streaming streams. I don't expect this to be called a lot; and even if,
I expect it to be offset by the gains in the (vastly more frequent)
calls to the other methods or the 'fast path'.
- If it still bothers us, there are several ways to speed it up.
- API & code cleanliness:
- I want to remove the 'old' API in a follow-up CL, which should mostly
delete code, or replace it 1:1.
- In a 2nd follow-up I want to delete much of the UTF-8 handling in Blink
for streaming streams.
- The "bookmark" is now always implemented (and mostly very fast), so we
should be able to use it for more things.
- Testing & correctness:
- The unit tests now cover all stream implementations,
and are pretty good and triggering all the edge cases.
- Vastly more DCHECKs of the invariants.
BUG=v8:4947
Review-Url: https://codereview.chromium.org/2314663002
Cr-Commit-Position: refs/heads/master@{#39464}
2016-09-16 08:29:41 +00:00
|
|
|
stream.reset(ScannerStream::For(info->source_stream(),
|
|
|
|
info->source_stream_encoding()));
|
2016-07-29 09:17:08 +00:00
|
|
|
stream_ptr = stream.get();
|
|
|
|
}
|
2016-09-15 19:47:11 +00:00
|
|
|
DCHECK(info->maybe_outer_scope_info().is_null());
|
2014-09-12 09:12:08 +00:00
|
|
|
|
2016-08-03 13:30:23 +00:00
|
|
|
DCHECK(original_scope_);
|
|
|
|
|
2014-09-12 09:12:08 +00:00
|
|
|
// When streaming, we don't know the length of the source until we have parsed
|
|
|
|
// it. The raw data can be UTF-8, so we wouldn't know the source length until
|
|
|
|
// we have decoded it anyway even if we knew the raw data length (which we
|
|
|
|
// don't). We work around this by storing all the scopes which need their end
|
|
|
|
// position set at the end of the script (the top scope and possible eval
|
|
|
|
// scopes) and set their end position after we know the script length.
|
2016-10-11 10:36:56 +00:00
|
|
|
if (info->is_toplevel()) {
|
2016-08-05 13:18:42 +00:00
|
|
|
fni_ = new (zone()) FuncNameInferrer(ast_value_factory(), zone());
|
|
|
|
scanner_.Initialize(stream_ptr);
|
|
|
|
result = DoParseProgram(info);
|
2016-10-11 10:36:56 +00:00
|
|
|
} else {
|
2016-10-14 14:08:53 +00:00
|
|
|
result = DoParseFunction(info, info->function_name(), stream_ptr);
|
2016-08-05 13:18:42 +00:00
|
|
|
}
|
2014-09-12 09:12:08 +00:00
|
|
|
|
2015-03-09 14:51:13 +00:00
|
|
|
info->set_literal(result);
|
2014-09-12 09:12:08 +00:00
|
|
|
|
|
|
|
// We cannot internalize on a background thread; a foreground task will take
|
|
|
|
// care of calling Parser::Internalize just before compilation.
|
|
|
|
|
2014-11-17 12:16:27 +00:00
|
|
|
if (produce_cached_parse_data()) {
|
2015-02-12 13:02:30 +00:00
|
|
|
if (result != NULL) *info->cached_data() = recorder.GetScriptData();
|
2014-09-12 09:12:08 +00:00
|
|
|
log_ = NULL;
|
|
|
|
}
|
|
|
|
}
|
2014-11-14 18:53:41 +00:00
|
|
|
|
2016-08-23 16:34:39 +00:00
|
|
|
Parser::TemplateLiteralState Parser::OpenTemplateLiteral(int pos) {
|
|
|
|
return new (zone()) TemplateLiteral(zone(), pos);
|
2014-11-14 18:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Parser::AddTemplateSpan(TemplateLiteralState* state, bool tail) {
|
|
|
|
int pos = scanner()->location().beg_pos;
|
|
|
|
int end = scanner()->location().end_pos - (tail ? 1 : 2);
|
|
|
|
const AstRawString* tv = scanner()->CurrentSymbol(ast_value_factory());
|
2014-12-03 14:17:16 +00:00
|
|
|
const AstRawString* trv = scanner()->CurrentRawSymbol(ast_value_factory());
|
2014-11-14 18:53:41 +00:00
|
|
|
Literal* cooked = factory()->NewStringLiteral(tv, pos);
|
2014-12-03 14:17:16 +00:00
|
|
|
Literal* raw = factory()->NewStringLiteral(trv, pos);
|
|
|
|
(*state)->AddTemplateSpan(cooked, raw, end, zone());
|
2014-11-14 18:53:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Parser::AddTemplateExpression(TemplateLiteralState* state,
|
|
|
|
Expression* expression) {
|
|
|
|
(*state)->AddExpression(expression, zone());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Expression* Parser::CloseTemplateLiteral(TemplateLiteralState* state, int start,
|
|
|
|
Expression* tag) {
|
|
|
|
TemplateLiteral* lit = *state;
|
|
|
|
int pos = lit->position();
|
|
|
|
const ZoneList<Expression*>* cooked_strings = lit->cooked();
|
2014-12-03 14:17:16 +00:00
|
|
|
const ZoneList<Expression*>* raw_strings = lit->raw();
|
2014-11-14 18:53:41 +00:00
|
|
|
const ZoneList<Expression*>* expressions = lit->expressions();
|
2014-12-03 14:17:16 +00:00
|
|
|
DCHECK_EQ(cooked_strings->length(), raw_strings->length());
|
|
|
|
DCHECK_EQ(cooked_strings->length(), expressions->length() + 1);
|
2014-11-14 18:53:41 +00:00
|
|
|
|
|
|
|
if (!tag) {
|
|
|
|
// Build tree of BinaryOps to simplify code-generation
|
2015-03-24 12:43:50 +00:00
|
|
|
Expression* expr = cooked_strings->at(0);
|
|
|
|
int i = 0;
|
|
|
|
while (i < expressions->length()) {
|
|
|
|
Expression* sub = expressions->at(i++);
|
|
|
|
Expression* cooked_str = cooked_strings->at(i);
|
|
|
|
|
|
|
|
// Let middle be ToString(sub).
|
|
|
|
ZoneList<Expression*>* args =
|
|
|
|
new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
args->Add(sub, zone());
|
2015-09-23 21:46:37 +00:00
|
|
|
Expression* middle = factory()->NewCallRuntime(Runtime::kInlineToString,
|
|
|
|
args, sub->position());
|
2014-11-14 18:53:41 +00:00
|
|
|
|
|
|
|
expr = factory()->NewBinaryOperation(
|
2015-03-24 12:43:50 +00:00
|
|
|
Token::ADD, factory()->NewBinaryOperation(
|
|
|
|
Token::ADD, expr, middle, expr->position()),
|
|
|
|
cooked_str, sub->position());
|
2014-11-14 18:53:41 +00:00
|
|
|
}
|
|
|
|
return expr;
|
|
|
|
} else {
|
2014-12-03 14:17:16 +00:00
|
|
|
uint32_t hash = ComputeTemplateLiteralHash(lit);
|
2014-11-14 18:53:41 +00:00
|
|
|
|
|
|
|
int cooked_idx = function_state_->NextMaterializedLiteralIndex();
|
|
|
|
int raw_idx = function_state_->NextMaterializedLiteralIndex();
|
|
|
|
|
2015-04-16 12:17:46 +00:00
|
|
|
// $getTemplateCallSite
|
2014-11-14 18:53:41 +00:00
|
|
|
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(4, zone());
|
|
|
|
args->Add(factory()->NewArrayLiteral(
|
|
|
|
const_cast<ZoneList<Expression*>*>(cooked_strings),
|
2016-02-26 17:44:34 +00:00
|
|
|
cooked_idx, pos),
|
2014-11-14 18:53:41 +00:00
|
|
|
zone());
|
|
|
|
args->Add(
|
|
|
|
factory()->NewArrayLiteral(
|
2016-02-26 17:44:34 +00:00
|
|
|
const_cast<ZoneList<Expression*>*>(raw_strings), raw_idx, pos),
|
2014-11-14 18:53:41 +00:00
|
|
|
zone());
|
2014-11-20 22:37:31 +00:00
|
|
|
|
2014-12-03 14:17:16 +00:00
|
|
|
// Ensure hash is suitable as a Smi value
|
2014-11-20 22:37:31 +00:00
|
|
|
Smi* hash_obj = Smi::cast(Internals::IntToSmi(static_cast<int>(hash)));
|
|
|
|
args->Add(factory()->NewSmiLiteral(hash_obj->value(), pos), zone());
|
|
|
|
|
2014-11-14 18:53:41 +00:00
|
|
|
Expression* call_site = factory()->NewCallRuntime(
|
2015-08-26 11:16:38 +00:00
|
|
|
Context::GET_TEMPLATE_CALL_SITE_INDEX, args, start);
|
2014-11-14 18:53:41 +00:00
|
|
|
|
|
|
|
// Call TagFn
|
|
|
|
ZoneList<Expression*>* call_args =
|
|
|
|
new (zone()) ZoneList<Expression*>(expressions->length() + 1, zone());
|
|
|
|
call_args->Add(call_site, zone());
|
|
|
|
call_args->AddAll(*expressions, zone());
|
|
|
|
return factory()->NewCall(tag, call_args, pos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-12-03 14:17:16 +00:00
|
|
|
uint32_t Parser::ComputeTemplateLiteralHash(const TemplateLiteral* lit) {
|
|
|
|
const ZoneList<Expression*>* raw_strings = lit->raw();
|
|
|
|
int total = raw_strings->length();
|
2014-11-14 18:53:41 +00:00
|
|
|
DCHECK(total);
|
|
|
|
|
2014-11-27 15:47:42 +00:00
|
|
|
uint32_t running_hash = 0;
|
2014-11-20 22:37:31 +00:00
|
|
|
|
2014-11-14 18:53:41 +00:00
|
|
|
for (int index = 0; index < total; ++index) {
|
2014-11-20 22:37:31 +00:00
|
|
|
if (index) {
|
2014-11-27 15:47:42 +00:00
|
|
|
running_hash = StringHasher::ComputeRunningHashOneByte(
|
|
|
|
running_hash, "${}", 3);
|
2014-11-20 22:37:31 +00:00
|
|
|
}
|
|
|
|
|
2014-12-03 14:17:16 +00:00
|
|
|
const AstRawString* raw_string =
|
|
|
|
raw_strings->at(index)->AsLiteral()->raw_value()->AsString();
|
|
|
|
if (raw_string->is_one_byte()) {
|
|
|
|
const char* data = reinterpret_cast<const char*>(raw_string->raw_data());
|
|
|
|
running_hash = StringHasher::ComputeRunningHashOneByte(
|
|
|
|
running_hash, data, raw_string->length());
|
2014-11-26 17:15:47 +00:00
|
|
|
} else {
|
2014-12-03 14:17:16 +00:00
|
|
|
const uc16* data = reinterpret_cast<const uc16*>(raw_string->raw_data());
|
|
|
|
running_hash = StringHasher::ComputeRunningHash(running_hash, data,
|
|
|
|
raw_string->length());
|
2014-11-26 17:15:47 +00:00
|
|
|
}
|
2014-11-14 18:53:41 +00:00
|
|
|
}
|
|
|
|
|
2014-12-03 14:17:16 +00:00
|
|
|
return running_hash;
|
2014-11-14 18:53:41 +00:00
|
|
|
}
|
2015-04-09 19:37:14 +00:00
|
|
|
|
2016-08-26 07:45:53 +00:00
|
|
|
ZoneList<Expression*>* Parser::PrepareSpreadArguments(
|
|
|
|
ZoneList<Expression*>* list) {
|
|
|
|
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(1, zone());
|
2015-04-09 19:37:14 +00:00
|
|
|
if (list->length() == 1) {
|
|
|
|
// Spread-call with single spread argument produces an InternalArray
|
|
|
|
// containing the values from the array.
|
|
|
|
//
|
|
|
|
// Function is called or constructed with the produced array of arguments
|
|
|
|
//
|
|
|
|
// EG: Apply(Func, Spread(spread0))
|
|
|
|
ZoneList<Expression*>* spread_list =
|
|
|
|
new (zone()) ZoneList<Expression*>(0, zone());
|
|
|
|
spread_list->Add(list->at(0)->AsSpread()->expression(), zone());
|
2015-08-26 11:16:38 +00:00
|
|
|
args->Add(factory()->NewCallRuntime(Context::SPREAD_ITERABLE_INDEX,
|
2016-06-30 09:26:30 +00:00
|
|
|
spread_list, kNoSourcePosition),
|
2015-08-26 11:16:38 +00:00
|
|
|
zone());
|
2015-04-09 19:37:14 +00:00
|
|
|
return args;
|
|
|
|
} else {
|
|
|
|
// Spread-call with multiple arguments produces array literals for each
|
|
|
|
// sequences of unspread arguments, and converts each spread iterable to
|
|
|
|
// an Internal array. Finally, all of these produced arrays are flattened
|
|
|
|
// into a single InternalArray, containing the arguments for the call.
|
|
|
|
//
|
|
|
|
// EG: Apply(Func, Flatten([unspread0, unspread1], Spread(spread0),
|
|
|
|
// Spread(spread1), [unspread2, unspread3]))
|
|
|
|
int i = 0;
|
|
|
|
int n = list->length();
|
|
|
|
while (i < n) {
|
|
|
|
if (!list->at(i)->IsSpread()) {
|
2016-08-26 07:45:53 +00:00
|
|
|
ZoneList<Expression*>* unspread =
|
|
|
|
new (zone()) ZoneList<Expression*>(1, zone());
|
2015-04-09 19:37:14 +00:00
|
|
|
|
|
|
|
// Push array of unspread parameters
|
|
|
|
while (i < n && !list->at(i)->IsSpread()) {
|
|
|
|
unspread->Add(list->at(i++), zone());
|
|
|
|
}
|
|
|
|
int literal_index = function_state_->NextMaterializedLiteralIndex();
|
|
|
|
args->Add(factory()->NewArrayLiteral(unspread, literal_index,
|
2016-06-30 09:26:30 +00:00
|
|
|
kNoSourcePosition),
|
2015-04-09 19:37:14 +00:00
|
|
|
zone());
|
|
|
|
|
|
|
|
if (i == n) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push eagerly spread argument
|
2016-08-26 07:45:53 +00:00
|
|
|
ZoneList<Expression*>* spread_list =
|
|
|
|
new (zone()) ZoneList<Expression*>(1, zone());
|
2015-04-09 19:37:14 +00:00
|
|
|
spread_list->Add(list->at(i++)->AsSpread()->expression(), zone());
|
2015-08-26 11:16:38 +00:00
|
|
|
args->Add(factory()->NewCallRuntime(Context::SPREAD_ITERABLE_INDEX,
|
2016-06-30 09:26:30 +00:00
|
|
|
spread_list, kNoSourcePosition),
|
2015-04-09 19:37:14 +00:00
|
|
|
zone());
|
|
|
|
}
|
|
|
|
|
2016-08-26 07:45:53 +00:00
|
|
|
list = new (zone()) ZoneList<Expression*>(1, zone());
|
2015-08-26 11:16:38 +00:00
|
|
|
list->Add(factory()->NewCallRuntime(Context::SPREAD_ARGUMENTS_INDEX, args,
|
2016-06-30 09:26:30 +00:00
|
|
|
kNoSourcePosition),
|
2015-04-09 19:37:14 +00:00
|
|
|
zone());
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
|
|
|
|
Expression* Parser::SpreadCall(Expression* function,
|
2016-08-26 07:45:53 +00:00
|
|
|
ZoneList<Expression*>* args, int pos) {
|
2015-06-02 22:04:25 +00:00
|
|
|
if (function->IsSuperCallReference()) {
|
2015-04-09 19:37:14 +00:00
|
|
|
// Super calls
|
2015-12-11 16:38:44 +00:00
|
|
|
// $super_constructor = %_GetSuperConstructor(<this-function>)
|
|
|
|
// %reflect_construct($super_constructor, args, new.target)
|
2015-07-16 15:07:47 +00:00
|
|
|
ZoneList<Expression*>* tmp = new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
tmp->Add(function->AsSuperCallReference()->this_function_var(), zone());
|
2015-12-11 15:48:48 +00:00
|
|
|
Expression* super_constructor = factory()->NewCallRuntime(
|
|
|
|
Runtime::kInlineGetSuperConstructor, tmp, pos);
|
|
|
|
args->InsertAt(0, super_constructor, zone());
|
2015-06-02 22:04:25 +00:00
|
|
|
args->Add(function->AsSuperCallReference()->new_target_var(), zone());
|
2015-08-26 11:16:38 +00:00
|
|
|
return factory()->NewCallRuntime(Context::REFLECT_CONSTRUCT_INDEX, args,
|
|
|
|
pos);
|
2015-04-09 19:37:14 +00:00
|
|
|
} else {
|
|
|
|
if (function->IsProperty()) {
|
|
|
|
// Method calls
|
2015-05-14 22:59:22 +00:00
|
|
|
if (function->AsProperty()->IsSuperAccess()) {
|
2016-08-11 12:04:02 +00:00
|
|
|
Expression* home = ThisExpression(kNoSourcePosition);
|
2015-05-14 22:59:22 +00:00
|
|
|
args->InsertAt(0, function, zone());
|
|
|
|
args->InsertAt(1, home, zone());
|
|
|
|
} else {
|
2016-08-09 19:49:25 +00:00
|
|
|
Variable* temp = NewTemporary(ast_value_factory()->empty_string());
|
2015-05-14 22:59:22 +00:00
|
|
|
VariableProxy* obj = factory()->NewVariableProxy(temp);
|
|
|
|
Assignment* assign_obj = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, obj, function->AsProperty()->obj(),
|
2016-06-30 09:26:30 +00:00
|
|
|
kNoSourcePosition);
|
2015-05-14 22:59:22 +00:00
|
|
|
function = factory()->NewProperty(
|
2016-06-30 09:26:30 +00:00
|
|
|
assign_obj, function->AsProperty()->key(), kNoSourcePosition);
|
2015-05-14 22:59:22 +00:00
|
|
|
args->InsertAt(0, function, zone());
|
|
|
|
obj = factory()->NewVariableProxy(temp);
|
|
|
|
args->InsertAt(1, obj, zone());
|
|
|
|
}
|
2015-04-09 19:37:14 +00:00
|
|
|
} else {
|
|
|
|
// Non-method calls
|
|
|
|
args->InsertAt(0, function, zone());
|
2016-06-30 09:26:30 +00:00
|
|
|
args->InsertAt(1, factory()->NewUndefinedLiteral(kNoSourcePosition),
|
2015-04-09 19:37:14 +00:00
|
|
|
zone());
|
|
|
|
}
|
2015-08-26 11:16:38 +00:00
|
|
|
return factory()->NewCallRuntime(Context::REFLECT_APPLY_INDEX, args, pos);
|
2015-04-09 19:37:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Expression* Parser::SpreadCallNew(Expression* function,
|
2016-08-26 07:45:53 +00:00
|
|
|
ZoneList<Expression*>* args, int pos) {
|
2015-04-09 19:37:14 +00:00
|
|
|
args->InsertAt(0, function, zone());
|
|
|
|
|
2015-08-26 11:16:38 +00:00
|
|
|
return factory()->NewCallRuntime(Context::REFLECT_CONSTRUCT_INDEX, args, pos);
|
2015-04-09 19:37:14 +00:00
|
|
|
}
|
2015-11-05 19:52:36 +00:00
|
|
|
|
|
|
|
|
|
|
|
void Parser::SetLanguageMode(Scope* scope, LanguageMode mode) {
|
|
|
|
v8::Isolate::UseCounterFeature feature;
|
|
|
|
if (is_sloppy(mode))
|
|
|
|
feature = v8::Isolate::kSloppyMode;
|
|
|
|
else if (is_strict(mode))
|
|
|
|
feature = v8::Isolate::kStrictMode;
|
|
|
|
else
|
|
|
|
UNREACHABLE();
|
|
|
|
++use_counts_[feature];
|
|
|
|
scope->SetLanguageMode(mode);
|
|
|
|
}
|
|
|
|
|
2016-09-05 13:42:01 +00:00
|
|
|
void Parser::SetAsmModule() {
|
|
|
|
// Store the usage count; The actual use counter on the isolate is
|
|
|
|
// incremented after parsing is done.
|
|
|
|
++use_counts_[v8::Isolate::kUseAsm];
|
|
|
|
DCHECK(scope()->is_declaration_scope());
|
|
|
|
scope()->AsDeclarationScope()->set_asm_module();
|
2015-11-05 19:52:36 +00:00
|
|
|
}
|
|
|
|
|
2016-05-04 13:42:52 +00:00
|
|
|
void Parser::MarkCollectedTailCallExpressions() {
|
|
|
|
const ZoneList<Expression*>& tail_call_expressions =
|
|
|
|
function_state_->tail_call_expressions().expressions();
|
|
|
|
for (int i = 0; i < tail_call_expressions.length(); ++i) {
|
2016-09-28 08:25:28 +00:00
|
|
|
MarkTailPosition(tail_call_expressions[i]);
|
2016-05-04 13:42:52 +00:00
|
|
|
}
|
|
|
|
}
|
2015-12-04 17:20:10 +00:00
|
|
|
|
2016-08-25 08:48:45 +00:00
|
|
|
Expression* Parser::ExpressionListToExpression(ZoneList<Expression*>* args) {
|
2016-05-16 23:17:13 +00:00
|
|
|
Expression* expr = args->at(0);
|
|
|
|
for (int i = 1; i < args->length(); ++i) {
|
2016-08-25 08:48:45 +00:00
|
|
|
expr = factory()->NewBinaryOperation(Token::COMMA, expr, args->at(i),
|
|
|
|
expr->position());
|
2016-05-16 23:17:13 +00:00
|
|
|
}
|
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
2016-09-27 18:01:58 +00:00
|
|
|
// This method intoduces the line initializing the generator object
|
|
|
|
// when desugaring the body of async_function.
|
|
|
|
void Parser::PrepareAsyncFunctionBody(ZoneList<Statement*>* body,
|
|
|
|
FunctionKind kind, int pos) {
|
|
|
|
// function async_function() {
|
|
|
|
// .generator_object = %CreateGeneratorObject();
|
|
|
|
// BuildRejectPromiseOnException({
|
|
|
|
// ... block ...
|
|
|
|
// return %ResolvePromise(.promise, expr), .promise;
|
|
|
|
// })
|
|
|
|
// }
|
|
|
|
|
|
|
|
Variable* temp =
|
|
|
|
NewTemporary(ast_value_factory()->dot_generator_object_string());
|
|
|
|
function_state_->set_generator_object_variable(temp);
|
|
|
|
|
|
|
|
Expression* init_generator_variable = factory()->NewAssignment(
|
|
|
|
Token::INIT, factory()->NewVariableProxy(temp),
|
|
|
|
BuildCreateJSGeneratorObject(pos, kind), kNoSourcePosition);
|
|
|
|
body->Add(factory()->NewExpressionStatement(init_generator_variable,
|
|
|
|
kNoSourcePosition),
|
|
|
|
zone());
|
|
|
|
}
|
|
|
|
|
|
|
|
// This method completes the desugaring of the body of async_function.
|
|
|
|
void Parser::RewriteAsyncFunctionBody(ZoneList<Statement*>* body, Block* block,
|
|
|
|
Expression* return_value, bool* ok) {
|
|
|
|
// function async_function() {
|
|
|
|
// .generator_object = %CreateGeneratorObject();
|
|
|
|
// BuildRejectPromiseOnException({
|
|
|
|
// ... block ...
|
|
|
|
// return %ResolvePromise(.promise, expr), .promise;
|
|
|
|
// })
|
|
|
|
// }
|
|
|
|
|
|
|
|
return_value = BuildResolvePromise(return_value, return_value->position());
|
|
|
|
block->statements()->Add(
|
|
|
|
factory()->NewReturnStatement(return_value, return_value->position()),
|
|
|
|
zone());
|
|
|
|
block = BuildRejectPromiseOnException(block, CHECK_OK_VOID);
|
|
|
|
body->Add(block, zone());
|
|
|
|
}
|
|
|
|
|
2016-08-23 16:34:39 +00:00
|
|
|
Expression* Parser::RewriteAwaitExpression(Expression* value, int await_pos) {
|
2016-09-09 21:49:58 +00:00
|
|
|
// yield do {
|
|
|
|
// tmp = <operand>;
|
2016-09-20 21:30:45 +00:00
|
|
|
// %AsyncFunctionAwait(.generator_object, tmp, .promise);
|
|
|
|
// .promise
|
2016-09-09 21:49:58 +00:00
|
|
|
// }
|
2016-09-19 23:51:30 +00:00
|
|
|
// The value of the expression is returned to the caller of the async
|
|
|
|
// function for the first yield statement; for this, .promise is the
|
|
|
|
// appropriate return value, being a Promise that will be fulfilled or
|
|
|
|
// rejected with the appropriate value by the desugaring. Subsequent yield
|
|
|
|
// occurrences will return to the AsyncFunctionNext call within the
|
|
|
|
// implemementation of the intermediate throwaway Promise's then handler.
|
|
|
|
// This handler has nothing useful to do with the value, as the Promise is
|
|
|
|
// ignored. If we yielded the value of the throwawayPromise that
|
|
|
|
// AsyncFunctionAwait creates as an intermediate, it would create a memory
|
|
|
|
// leak; we must return .promise instead;
|
|
|
|
// The operand needs to be evaluated on a separate statement in order to get
|
|
|
|
// a break location, and the .promise needs to be read earlier so that it
|
|
|
|
// doesn't insert a false location.
|
|
|
|
// TODO(littledan): investigate why this ordering is needed in more detail.
|
2016-05-17 00:26:53 +00:00
|
|
|
Variable* generator_object_variable =
|
2016-08-25 08:48:45 +00:00
|
|
|
function_state_->generator_object_variable();
|
2016-05-17 00:26:53 +00:00
|
|
|
|
|
|
|
// If generator_object_variable is null,
|
2016-09-19 23:51:30 +00:00
|
|
|
// TODO(littledan): Is this necessary?
|
2016-05-17 00:26:53 +00:00
|
|
|
if (!generator_object_variable) return value;
|
|
|
|
|
2016-06-30 09:26:30 +00:00
|
|
|
const int nopos = kNoSourcePosition;
|
2016-06-03 15:30:42 +00:00
|
|
|
|
2016-09-20 21:30:45 +00:00
|
|
|
Block* do_block = factory()->NewBlock(nullptr, 2, false, nopos);
|
2016-09-19 23:51:30 +00:00
|
|
|
|
2016-09-20 21:30:45 +00:00
|
|
|
Variable* promise = PromiseVariable();
|
2016-06-03 15:30:42 +00:00
|
|
|
|
|
|
|
// Wrap value evaluation to provide a break location.
|
2016-09-19 23:51:30 +00:00
|
|
|
Variable* temp_var = NewTemporary(ast_value_factory()->empty_string());
|
2016-09-09 21:49:58 +00:00
|
|
|
Expression* value_assignment = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, factory()->NewVariableProxy(temp_var), value, nopos);
|
2016-06-03 15:30:42 +00:00
|
|
|
do_block->statements()->Add(
|
2016-08-25 08:48:45 +00:00
|
|
|
factory()->NewExpressionStatement(value_assignment, value->position()),
|
2016-06-03 15:30:42 +00:00
|
|
|
zone());
|
2016-05-17 00:26:53 +00:00
|
|
|
|
|
|
|
ZoneList<Expression*>* async_function_await_args =
|
2016-09-20 19:03:19 +00:00
|
|
|
new (zone()) ZoneList<Expression*>(3, zone());
|
2016-06-03 15:30:42 +00:00
|
|
|
Expression* generator_object =
|
2016-08-25 08:48:45 +00:00
|
|
|
factory()->NewVariableProxy(generator_object_variable);
|
2016-05-17 00:26:53 +00:00
|
|
|
async_function_await_args->Add(generator_object, zone());
|
2016-09-09 21:49:58 +00:00
|
|
|
async_function_await_args->Add(factory()->NewVariableProxy(temp_var), zone());
|
2016-09-20 21:30:45 +00:00
|
|
|
async_function_await_args->Add(factory()->NewVariableProxy(promise), zone());
|
2016-09-13 20:49:54 +00:00
|
|
|
|
|
|
|
// The parser emits calls to AsyncFunctionAwaitCaught, but the
|
|
|
|
// AstNumberingVisitor will rewrite this to AsyncFunctionAwaitUncaught
|
|
|
|
// if there is no local enclosing try/catch block.
|
|
|
|
Expression* async_function_await =
|
|
|
|
factory()->NewCallRuntime(Context::ASYNC_FUNCTION_AWAIT_CAUGHT_INDEX,
|
|
|
|
async_function_await_args, nopos);
|
2016-09-19 23:51:30 +00:00
|
|
|
do_block->statements()->Add(
|
|
|
|
factory()->NewExpressionStatement(async_function_await, await_pos),
|
|
|
|
zone());
|
2016-09-09 21:49:58 +00:00
|
|
|
|
2016-06-03 15:30:42 +00:00
|
|
|
// Wrap await to provide a break location between value evaluation and yield.
|
2016-09-20 21:30:45 +00:00
|
|
|
Expression* do_expr = factory()->NewDoExpression(do_block, promise, nopos);
|
2016-02-19 14:03:35 +00:00
|
|
|
|
2016-08-25 08:48:45 +00:00
|
|
|
generator_object = factory()->NewVariableProxy(generator_object_variable);
|
|
|
|
return factory()->NewYield(generator_object, do_expr, nopos,
|
|
|
|
Yield::kOnExceptionRethrow);
|
2016-02-19 15:58:57 +00:00
|
|
|
}
|
|
|
|
|
Add spread rewriting
In short, array literals containing spreads, when used as expressions,
are rewritten using do expressions. E.g.
[1, 2, 3, ...x, 4, ...y, 5]
is roughly rewritten as:
do {
$R = [1, 2, 3];
for ($i of x) %AppendElement($R, $i);
%AppendElement($R, 4);
for ($j of y) %AppendElement($R, $j);
%AppendElement($R, 5);
$R
}
where $R, $i and $j are fresh temporary variables.
R=rossberg@chromium.org
BUG=
Review URL: https://codereview.chromium.org/1564083002
Cr-Commit-Position: refs/heads/master@{#33307}
2016-01-14 17:49:54 +00:00
|
|
|
class NonPatternRewriter : public AstExpressionRewriter {
|
|
|
|
public:
|
|
|
|
NonPatternRewriter(uintptr_t stack_limit, Parser* parser)
|
|
|
|
: AstExpressionRewriter(stack_limit), parser_(parser) {}
|
|
|
|
~NonPatternRewriter() override {}
|
|
|
|
|
|
|
|
private:
|
|
|
|
bool RewriteExpression(Expression* expr) override {
|
2016-02-19 15:58:57 +00:00
|
|
|
if (expr->IsRewritableExpression()) return true;
|
Add spread rewriting
In short, array literals containing spreads, when used as expressions,
are rewritten using do expressions. E.g.
[1, 2, 3, ...x, 4, ...y, 5]
is roughly rewritten as:
do {
$R = [1, 2, 3];
for ($i of x) %AppendElement($R, $i);
%AppendElement($R, 4);
for ($j of y) %AppendElement($R, $j);
%AppendElement($R, 5);
$R
}
where $R, $i and $j are fresh temporary variables.
R=rossberg@chromium.org
BUG=
Review URL: https://codereview.chromium.org/1564083002
Cr-Commit-Position: refs/heads/master@{#33307}
2016-01-14 17:49:54 +00:00
|
|
|
// Rewrite only what could have been a pattern but is not.
|
|
|
|
if (expr->IsArrayLiteral()) {
|
|
|
|
// Spread rewriting in array literals.
|
|
|
|
ArrayLiteral* lit = expr->AsArrayLiteral();
|
|
|
|
VisitExpressions(lit->values());
|
|
|
|
replacement_ = parser_->RewriteSpreads(lit);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (expr->IsObjectLiteral()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (expr->IsBinaryOperation() &&
|
|
|
|
expr->AsBinaryOperation()->op() == Token::COMMA) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Everything else does not need rewriting.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-09-06 17:43:17 +00:00
|
|
|
void VisitLiteralProperty(LiteralProperty* property) override {
|
2016-02-01 09:18:09 +00:00
|
|
|
if (property == nullptr) return;
|
|
|
|
// Do not rewrite (computed) key expressions
|
|
|
|
AST_REWRITE_PROPERTY(Expression, property, value);
|
|
|
|
}
|
|
|
|
|
Add spread rewriting
In short, array literals containing spreads, when used as expressions,
are rewritten using do expressions. E.g.
[1, 2, 3, ...x, 4, ...y, 5]
is roughly rewritten as:
do {
$R = [1, 2, 3];
for ($i of x) %AppendElement($R, $i);
%AppendElement($R, 4);
for ($j of y) %AppendElement($R, $j);
%AppendElement($R, 5);
$R
}
where $R, $i and $j are fresh temporary variables.
R=rossberg@chromium.org
BUG=
Review URL: https://codereview.chromium.org/1564083002
Cr-Commit-Position: refs/heads/master@{#33307}
2016-01-14 17:49:54 +00:00
|
|
|
Parser* parser_;
|
|
|
|
};
|
|
|
|
|
2016-09-01 08:58:15 +00:00
|
|
|
void Parser::RewriteNonPattern(bool* ok) {
|
|
|
|
ValidateExpression(CHECK_OK_VOID);
|
2016-02-19 15:58:57 +00:00
|
|
|
auto non_patterns_to_rewrite = function_state_->non_patterns_to_rewrite();
|
2016-09-01 08:58:15 +00:00
|
|
|
int begin = classifier()->GetNonPatternBegin();
|
2016-02-19 15:58:57 +00:00
|
|
|
int end = non_patterns_to_rewrite->length();
|
|
|
|
if (begin < end) {
|
|
|
|
NonPatternRewriter rewriter(stack_limit_, this);
|
|
|
|
for (int i = begin; i < end; i++) {
|
|
|
|
DCHECK(non_patterns_to_rewrite->at(i)->IsRewritableExpression());
|
|
|
|
rewriter.Rewrite(non_patterns_to_rewrite->at(i));
|
|
|
|
}
|
|
|
|
non_patterns_to_rewrite->Rewind(begin);
|
2016-01-14 15:46:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-12-04 17:20:10 +00:00
|
|
|
void Parser::RewriteDestructuringAssignments() {
|
2016-02-19 15:58:57 +00:00
|
|
|
const auto& assignments =
|
|
|
|
function_state_->destructuring_assignments_to_rewrite();
|
2015-12-04 17:20:10 +00:00
|
|
|
for (int i = assignments.length() - 1; i >= 0; --i) {
|
|
|
|
// Rewrite list in reverse, so that nested assignment patterns are rewritten
|
|
|
|
// correctly.
|
2016-02-19 15:58:57 +00:00
|
|
|
const DestructuringAssignment& pair = assignments.at(i);
|
|
|
|
RewritableExpression* to_rewrite =
|
|
|
|
pair.assignment->AsRewritableExpression();
|
2015-12-04 17:20:10 +00:00
|
|
|
DCHECK_NOT_NULL(to_rewrite);
|
|
|
|
if (!to_rewrite->is_rewritten()) {
|
2016-02-19 15:58:57 +00:00
|
|
|
PatternRewriter::RewriteDestructuringAssignment(this, to_rewrite,
|
|
|
|
pair.scope);
|
2015-12-04 17:20:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-18 13:53:52 +00:00
|
|
|
Expression* Parser::RewriteExponentiation(Expression* left, Expression* right,
|
|
|
|
int pos) {
|
|
|
|
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
|
|
|
|
args->Add(left, zone());
|
|
|
|
args->Add(right, zone());
|
[builtins] Unify most of the remaining Math builtins.
Import fdlibm versions of acos, acosh, asin and asinh, which are more
precise and produce the same result across platforms (we were using
libm versions for asin and acos so far, where both speed and precision
depended on the operating system so far). Introduce appropriate TurboFan
operators for these functions and use them both for inlining and for the
generic builtin.
Also migrate the Math.imul and Math.fround builtins to TurboFan builtins
to ensure that their behavior is always exactly the same as the inlined
TurboFan version (i.e. C++ truncation semantics for double to float
don't necessarily meet the JavaScript semantics).
For completeness, also migrate Math.sign, which can even get some nice
love in TurboFan.
Drive-by-fix: Some alpha-sorting on the Math related functions, and
cleanup the list of Math intrinsics that we have to export via the
native context currently.
BUG=v8:3266,v8:3496,v8:3509,v8:3952,v8:5169,v8:5170,v8:5171,v8:5172
TBR=rossberg@chromium.org
R=franzih@chromium.org
Review-Url: https://codereview.chromium.org/2116753002
Cr-Commit-Position: refs/heads/master@{#37476}
2016-07-01 11:11:33 +00:00
|
|
|
return factory()->NewCallRuntime(Context::MATH_POW_INDEX, args, pos);
|
2016-03-18 13:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Expression* Parser::RewriteAssignExponentiation(Expression* left,
|
|
|
|
Expression* right, int pos) {
|
|
|
|
ZoneList<Expression*>* args = new (zone()) ZoneList<Expression*>(2, zone());
|
|
|
|
if (left->IsVariableProxy()) {
|
|
|
|
VariableProxy* lhs = left->AsVariableProxy();
|
|
|
|
|
|
|
|
Expression* result;
|
|
|
|
DCHECK_NOT_NULL(lhs->raw_name());
|
2016-08-25 08:48:45 +00:00
|
|
|
result = ExpressionFromIdentifier(lhs->raw_name(), lhs->position(),
|
|
|
|
lhs->end_position());
|
2016-03-18 13:53:52 +00:00
|
|
|
args->Add(left, zone());
|
|
|
|
args->Add(right, zone());
|
|
|
|
Expression* call =
|
[builtins] Unify most of the remaining Math builtins.
Import fdlibm versions of acos, acosh, asin and asinh, which are more
precise and produce the same result across platforms (we were using
libm versions for asin and acos so far, where both speed and precision
depended on the operating system so far). Introduce appropriate TurboFan
operators for these functions and use them both for inlining and for the
generic builtin.
Also migrate the Math.imul and Math.fround builtins to TurboFan builtins
to ensure that their behavior is always exactly the same as the inlined
TurboFan version (i.e. C++ truncation semantics for double to float
don't necessarily meet the JavaScript semantics).
For completeness, also migrate Math.sign, which can even get some nice
love in TurboFan.
Drive-by-fix: Some alpha-sorting on the Math related functions, and
cleanup the list of Math intrinsics that we have to export via the
native context currently.
BUG=v8:3266,v8:3496,v8:3509,v8:3952,v8:5169,v8:5170,v8:5171,v8:5172
TBR=rossberg@chromium.org
R=franzih@chromium.org
Review-Url: https://codereview.chromium.org/2116753002
Cr-Commit-Position: refs/heads/master@{#37476}
2016-07-01 11:11:33 +00:00
|
|
|
factory()->NewCallRuntime(Context::MATH_POW_INDEX, args, pos);
|
2016-03-18 13:53:52 +00:00
|
|
|
return factory()->NewAssignment(Token::ASSIGN, result, call, pos);
|
|
|
|
} else if (left->IsProperty()) {
|
|
|
|
Property* prop = left->AsProperty();
|
2016-08-09 19:49:25 +00:00
|
|
|
auto temp_obj = NewTemporary(ast_value_factory()->empty_string());
|
|
|
|
auto temp_key = NewTemporary(ast_value_factory()->empty_string());
|
2016-03-18 13:53:52 +00:00
|
|
|
Expression* assign_obj = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, factory()->NewVariableProxy(temp_obj), prop->obj(),
|
2016-06-30 09:26:30 +00:00
|
|
|
kNoSourcePosition);
|
2016-03-18 13:53:52 +00:00
|
|
|
Expression* assign_key = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, factory()->NewVariableProxy(temp_key), prop->key(),
|
2016-06-30 09:26:30 +00:00
|
|
|
kNoSourcePosition);
|
2016-03-18 13:53:52 +00:00
|
|
|
args->Add(factory()->NewProperty(factory()->NewVariableProxy(temp_obj),
|
|
|
|
factory()->NewVariableProxy(temp_key),
|
|
|
|
left->position()),
|
|
|
|
zone());
|
|
|
|
args->Add(right, zone());
|
|
|
|
Expression* call =
|
[builtins] Unify most of the remaining Math builtins.
Import fdlibm versions of acos, acosh, asin and asinh, which are more
precise and produce the same result across platforms (we were using
libm versions for asin and acos so far, where both speed and precision
depended on the operating system so far). Introduce appropriate TurboFan
operators for these functions and use them both for inlining and for the
generic builtin.
Also migrate the Math.imul and Math.fround builtins to TurboFan builtins
to ensure that their behavior is always exactly the same as the inlined
TurboFan version (i.e. C++ truncation semantics for double to float
don't necessarily meet the JavaScript semantics).
For completeness, also migrate Math.sign, which can even get some nice
love in TurboFan.
Drive-by-fix: Some alpha-sorting on the Math related functions, and
cleanup the list of Math intrinsics that we have to export via the
native context currently.
BUG=v8:3266,v8:3496,v8:3509,v8:3952,v8:5169,v8:5170,v8:5171,v8:5172
TBR=rossberg@chromium.org
R=franzih@chromium.org
Review-Url: https://codereview.chromium.org/2116753002
Cr-Commit-Position: refs/heads/master@{#37476}
2016-07-01 11:11:33 +00:00
|
|
|
factory()->NewCallRuntime(Context::MATH_POW_INDEX, args, pos);
|
2016-03-18 13:53:52 +00:00
|
|
|
Expression* target = factory()->NewProperty(
|
|
|
|
factory()->NewVariableProxy(temp_obj),
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewVariableProxy(temp_key), kNoSourcePosition);
|
2016-03-18 13:53:52 +00:00
|
|
|
Expression* assign =
|
|
|
|
factory()->NewAssignment(Token::ASSIGN, target, call, pos);
|
|
|
|
return factory()->NewBinaryOperation(
|
|
|
|
Token::COMMA, assign_obj,
|
|
|
|
factory()->NewBinaryOperation(Token::COMMA, assign_key, assign, pos),
|
|
|
|
pos);
|
|
|
|
}
|
|
|
|
UNREACHABLE();
|
|
|
|
return nullptr;
|
|
|
|
}
|
2015-12-04 17:20:10 +00:00
|
|
|
|
Add spread rewriting
In short, array literals containing spreads, when used as expressions,
are rewritten using do expressions. E.g.
[1, 2, 3, ...x, 4, ...y, 5]
is roughly rewritten as:
do {
$R = [1, 2, 3];
for ($i of x) %AppendElement($R, $i);
%AppendElement($R, 4);
for ($j of y) %AppendElement($R, $j);
%AppendElement($R, 5);
$R
}
where $R, $i and $j are fresh temporary variables.
R=rossberg@chromium.org
BUG=
Review URL: https://codereview.chromium.org/1564083002
Cr-Commit-Position: refs/heads/master@{#33307}
2016-01-14 17:49:54 +00:00
|
|
|
Expression* Parser::RewriteSpreads(ArrayLiteral* lit) {
|
|
|
|
// Array literals containing spreads are rewritten using do expressions, e.g.
|
|
|
|
// [1, 2, 3, ...x, 4, ...y, 5]
|
|
|
|
// is roughly rewritten as:
|
|
|
|
// do {
|
|
|
|
// $R = [1, 2, 3];
|
|
|
|
// for ($i of x) %AppendElement($R, $i);
|
|
|
|
// %AppendElement($R, 4);
|
|
|
|
// for ($j of y) %AppendElement($R, $j);
|
|
|
|
// %AppendElement($R, 5);
|
|
|
|
// $R
|
|
|
|
// }
|
|
|
|
// where $R, $i and $j are fresh temporary variables.
|
|
|
|
ZoneList<Expression*>::iterator s = lit->FirstSpread();
|
|
|
|
if (s == lit->EndValue()) return nullptr; // no spread, no rewriting...
|
2016-08-09 19:49:25 +00:00
|
|
|
Variable* result = NewTemporary(ast_value_factory()->dot_result_string());
|
Add spread rewriting
In short, array literals containing spreads, when used as expressions,
are rewritten using do expressions. E.g.
[1, 2, 3, ...x, 4, ...y, 5]
is roughly rewritten as:
do {
$R = [1, 2, 3];
for ($i of x) %AppendElement($R, $i);
%AppendElement($R, 4);
for ($j of y) %AppendElement($R, $j);
%AppendElement($R, 5);
$R
}
where $R, $i and $j are fresh temporary variables.
R=rossberg@chromium.org
BUG=
Review URL: https://codereview.chromium.org/1564083002
Cr-Commit-Position: refs/heads/master@{#33307}
2016-01-14 17:49:54 +00:00
|
|
|
// NOTE: The value assigned to R is the whole original array literal,
|
|
|
|
// spreads included. This will be fixed before the rewritten AST is returned.
|
|
|
|
// $R = lit
|
2016-06-30 09:26:30 +00:00
|
|
|
Expression* init_result = factory()->NewAssignment(
|
|
|
|
Token::INIT, factory()->NewVariableProxy(result), lit, kNoSourcePosition);
|
|
|
|
Block* do_block = factory()->NewBlock(nullptr, 16, false, kNoSourcePosition);
|
Add spread rewriting
In short, array literals containing spreads, when used as expressions,
are rewritten using do expressions. E.g.
[1, 2, 3, ...x, 4, ...y, 5]
is roughly rewritten as:
do {
$R = [1, 2, 3];
for ($i of x) %AppendElement($R, $i);
%AppendElement($R, 4);
for ($j of y) %AppendElement($R, $j);
%AppendElement($R, 5);
$R
}
where $R, $i and $j are fresh temporary variables.
R=rossberg@chromium.org
BUG=
Review URL: https://codereview.chromium.org/1564083002
Cr-Commit-Position: refs/heads/master@{#33307}
2016-01-14 17:49:54 +00:00
|
|
|
do_block->statements()->Add(
|
2016-06-30 09:26:30 +00:00
|
|
|
factory()->NewExpressionStatement(init_result, kNoSourcePosition),
|
Add spread rewriting
In short, array literals containing spreads, when used as expressions,
are rewritten using do expressions. E.g.
[1, 2, 3, ...x, 4, ...y, 5]
is roughly rewritten as:
do {
$R = [1, 2, 3];
for ($i of x) %AppendElement($R, $i);
%AppendElement($R, 4);
for ($j of y) %AppendElement($R, $j);
%AppendElement($R, 5);
$R
}
where $R, $i and $j are fresh temporary variables.
R=rossberg@chromium.org
BUG=
Review URL: https://codereview.chromium.org/1564083002
Cr-Commit-Position: refs/heads/master@{#33307}
2016-01-14 17:49:54 +00:00
|
|
|
zone());
|
|
|
|
// Traverse the array literal starting from the first spread.
|
|
|
|
while (s != lit->EndValue()) {
|
|
|
|
Expression* value = *s++;
|
|
|
|
Spread* spread = value->AsSpread();
|
|
|
|
if (spread == nullptr) {
|
|
|
|
// If the element is not a spread, we're adding a single:
|
|
|
|
// %AppendElement($R, value)
|
2016-09-08 18:50:17 +00:00
|
|
|
// or, in case of a hole,
|
|
|
|
// ++($R.length)
|
|
|
|
if (!value->IsLiteral() ||
|
|
|
|
!value->AsLiteral()->raw_value()->IsTheHole()) {
|
|
|
|
ZoneList<Expression*>* append_element_args = NewExpressionList(2);
|
|
|
|
append_element_args->Add(factory()->NewVariableProxy(result), zone());
|
|
|
|
append_element_args->Add(value, zone());
|
|
|
|
do_block->statements()->Add(
|
|
|
|
factory()->NewExpressionStatement(
|
|
|
|
factory()->NewCallRuntime(Runtime::kAppendElement,
|
|
|
|
append_element_args,
|
|
|
|
kNoSourcePosition),
|
|
|
|
kNoSourcePosition),
|
|
|
|
zone());
|
|
|
|
} else {
|
|
|
|
Property* length_property = factory()->NewProperty(
|
|
|
|
factory()->NewVariableProxy(result),
|
|
|
|
factory()->NewStringLiteral(ast_value_factory()->length_string(),
|
|
|
|
kNoSourcePosition),
|
|
|
|
kNoSourcePosition);
|
|
|
|
CountOperation* count_op = factory()->NewCountOperation(
|
|
|
|
Token::INC, true /* prefix */, length_property, kNoSourcePosition);
|
|
|
|
do_block->statements()->Add(
|
|
|
|
factory()->NewExpressionStatement(count_op, kNoSourcePosition),
|
|
|
|
zone());
|
|
|
|
}
|
Add spread rewriting
In short, array literals containing spreads, when used as expressions,
are rewritten using do expressions. E.g.
[1, 2, 3, ...x, 4, ...y, 5]
is roughly rewritten as:
do {
$R = [1, 2, 3];
for ($i of x) %AppendElement($R, $i);
%AppendElement($R, 4);
for ($j of y) %AppendElement($R, $j);
%AppendElement($R, 5);
$R
}
where $R, $i and $j are fresh temporary variables.
R=rossberg@chromium.org
BUG=
Review URL: https://codereview.chromium.org/1564083002
Cr-Commit-Position: refs/heads/master@{#33307}
2016-01-14 17:49:54 +00:00
|
|
|
} else {
|
|
|
|
// If it's a spread, we're adding a for/of loop iterating through it.
|
2016-08-09 19:49:25 +00:00
|
|
|
Variable* each = NewTemporary(ast_value_factory()->dot_for_string());
|
Add spread rewriting
In short, array literals containing spreads, when used as expressions,
are rewritten using do expressions. E.g.
[1, 2, 3, ...x, 4, ...y, 5]
is roughly rewritten as:
do {
$R = [1, 2, 3];
for ($i of x) %AppendElement($R, $i);
%AppendElement($R, 4);
for ($j of y) %AppendElement($R, $j);
%AppendElement($R, 5);
$R
}
where $R, $i and $j are fresh temporary variables.
R=rossberg@chromium.org
BUG=
Review URL: https://codereview.chromium.org/1564083002
Cr-Commit-Position: refs/heads/master@{#33307}
2016-01-14 17:49:54 +00:00
|
|
|
Expression* subject = spread->expression();
|
|
|
|
// %AppendElement($R, each)
|
|
|
|
Statement* append_body;
|
|
|
|
{
|
2016-08-25 08:44:59 +00:00
|
|
|
ZoneList<Expression*>* append_element_args = NewExpressionList(2);
|
Add spread rewriting
In short, array literals containing spreads, when used as expressions,
are rewritten using do expressions. E.g.
[1, 2, 3, ...x, 4, ...y, 5]
is roughly rewritten as:
do {
$R = [1, 2, 3];
for ($i of x) %AppendElement($R, $i);
%AppendElement($R, 4);
for ($j of y) %AppendElement($R, $j);
%AppendElement($R, 5);
$R
}
where $R, $i and $j are fresh temporary variables.
R=rossberg@chromium.org
BUG=
Review URL: https://codereview.chromium.org/1564083002
Cr-Commit-Position: refs/heads/master@{#33307}
2016-01-14 17:49:54 +00:00
|
|
|
append_element_args->Add(factory()->NewVariableProxy(result), zone());
|
|
|
|
append_element_args->Add(factory()->NewVariableProxy(each), zone());
|
|
|
|
append_body = factory()->NewExpressionStatement(
|
|
|
|
factory()->NewCallRuntime(Runtime::kAppendElement,
|
2016-06-30 09:26:30 +00:00
|
|
|
append_element_args, kNoSourcePosition),
|
|
|
|
kNoSourcePosition);
|
Add spread rewriting
In short, array literals containing spreads, when used as expressions,
are rewritten using do expressions. E.g.
[1, 2, 3, ...x, 4, ...y, 5]
is roughly rewritten as:
do {
$R = [1, 2, 3];
for ($i of x) %AppendElement($R, $i);
%AppendElement($R, 4);
for ($j of y) %AppendElement($R, $j);
%AppendElement($R, 5);
$R
}
where $R, $i and $j are fresh temporary variables.
R=rossberg@chromium.org
BUG=
Review URL: https://codereview.chromium.org/1564083002
Cr-Commit-Position: refs/heads/master@{#33307}
2016-01-14 17:49:54 +00:00
|
|
|
}
|
|
|
|
// for (each of spread) %AppendElement($R, each)
|
|
|
|
ForEachStatement* loop = factory()->NewForEachStatement(
|
2016-06-30 09:26:30 +00:00
|
|
|
ForEachStatement::ITERATE, nullptr, kNoSourcePosition);
|
2016-07-07 08:15:11 +00:00
|
|
|
const bool finalize = false;
|
2016-03-07 19:52:12 +00:00
|
|
|
InitializeForOfStatement(loop->AsForOfStatement(),
|
|
|
|
factory()->NewVariableProxy(each), subject,
|
2016-07-07 08:15:11 +00:00
|
|
|
append_body, finalize);
|
2016-03-07 19:52:12 +00:00
|
|
|
do_block->statements()->Add(loop, zone());
|
Add spread rewriting
In short, array literals containing spreads, when used as expressions,
are rewritten using do expressions. E.g.
[1, 2, 3, ...x, 4, ...y, 5]
is roughly rewritten as:
do {
$R = [1, 2, 3];
for ($i of x) %AppendElement($R, $i);
%AppendElement($R, 4);
for ($j of y) %AppendElement($R, $j);
%AppendElement($R, 5);
$R
}
where $R, $i and $j are fresh temporary variables.
R=rossberg@chromium.org
BUG=
Review URL: https://codereview.chromium.org/1564083002
Cr-Commit-Position: refs/heads/master@{#33307}
2016-01-14 17:49:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Now, rewind the original array literal to truncate everything from the
|
|
|
|
// first spread (included) until the end. This fixes $R's initialization.
|
|
|
|
lit->RewindSpreads();
|
|
|
|
return factory()->NewDoExpression(do_block, result, lit->position());
|
|
|
|
}
|
|
|
|
|
2016-08-23 16:34:39 +00:00
|
|
|
void Parser::QueueDestructuringAssignmentForRewriting(Expression* expr) {
|
2016-02-19 15:58:57 +00:00
|
|
|
DCHECK(expr->IsRewritableExpression());
|
2016-08-23 16:34:39 +00:00
|
|
|
function_state_->AddDestructuringAssignment(
|
2016-08-25 08:48:45 +00:00
|
|
|
DestructuringAssignment(expr, scope()));
|
2015-12-04 17:20:10 +00:00
|
|
|
}
|
|
|
|
|
2016-08-23 16:34:39 +00:00
|
|
|
void Parser::QueueNonPatternForRewriting(Expression* expr, bool* ok) {
|
2016-02-19 15:58:57 +00:00
|
|
|
DCHECK(expr->IsRewritableExpression());
|
2016-08-23 16:34:39 +00:00
|
|
|
function_state_->AddNonPatternForRewriting(expr, ok);
|
2016-02-19 15:58:57 +00:00
|
|
|
}
|
|
|
|
|
2016-09-06 18:49:04 +00:00
|
|
|
void Parser::AddAccessorPrefixToFunctionName(bool is_get,
|
|
|
|
FunctionLiteral* function,
|
|
|
|
const AstRawString* name) {
|
|
|
|
DCHECK_NOT_NULL(name);
|
|
|
|
const AstRawString* prefix = is_get ? ast_value_factory()->get_space_string()
|
|
|
|
: ast_value_factory()->set_space_string();
|
|
|
|
function->set_raw_name(ast_value_factory()->NewConsString(prefix, name));
|
|
|
|
}
|
|
|
|
|
2016-08-25 08:48:45 +00:00
|
|
|
void Parser::SetFunctionNameFromPropertyName(ObjectLiteralProperty* property,
|
|
|
|
const AstRawString* name) {
|
2016-09-06 18:49:04 +00:00
|
|
|
DCHECK(property->kind() != ObjectLiteralProperty::GETTER);
|
|
|
|
DCHECK(property->kind() != ObjectLiteralProperty::SETTER);
|
2016-01-06 23:38:28 +00:00
|
|
|
|
2016-02-04 22:36:15 +00:00
|
|
|
// Computed name setting must happen at runtime.
|
2016-09-06 18:49:04 +00:00
|
|
|
DCHECK(!property->is_computed_name());
|
2016-02-19 02:50:58 +00:00
|
|
|
|
2016-01-06 23:38:28 +00:00
|
|
|
// Ignore "__proto__" as a name when it's being used to set the [[Prototype]]
|
|
|
|
// of an object literal.
|
|
|
|
if (property->kind() == ObjectLiteralProperty::PROTOTYPE) return;
|
|
|
|
|
2016-09-06 17:43:17 +00:00
|
|
|
Expression* value = property->value();
|
|
|
|
|
2016-09-06 18:49:04 +00:00
|
|
|
DCHECK(!value->IsAnonymousFunctionDefinition() ||
|
|
|
|
property->kind() == ObjectLiteralProperty::COMPUTED);
|
2016-09-06 17:43:17 +00:00
|
|
|
SetFunctionName(value, name);
|
|
|
|
}
|
|
|
|
|
2016-08-25 08:48:45 +00:00
|
|
|
void Parser::SetFunctionNameFromIdentifierRef(Expression* value,
|
|
|
|
Expression* identifier) {
|
2016-01-13 23:35:32 +00:00
|
|
|
if (!identifier->IsVariableProxy()) return;
|
2016-08-25 08:48:45 +00:00
|
|
|
SetFunctionName(value, identifier->AsVariableProxy()->raw_name());
|
2016-07-18 07:27:10 +00:00
|
|
|
}
|
2016-01-13 23:35:32 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
void Parser::SetFunctionName(Expression* value, const AstRawString* name) {
|
2016-07-18 07:27:10 +00:00
|
|
|
DCHECK_NOT_NULL(name);
|
|
|
|
if (!value->IsAnonymousFunctionDefinition()) return;
|
2016-02-01 17:44:23 +00:00
|
|
|
auto function = value->AsFunctionLiteral();
|
|
|
|
if (function != nullptr) {
|
|
|
|
function->set_raw_name(name);
|
2016-01-13 23:35:32 +00:00
|
|
|
} else {
|
2016-07-25 19:16:21 +00:00
|
|
|
DCHECK(value->IsDoExpression());
|
|
|
|
value->AsDoExpression()->represented_function()->set_raw_name(name);
|
2016-01-13 23:35:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-02-04 14:12:37 +00:00
|
|
|
// Desugaring of yield*
|
|
|
|
// ====================
|
|
|
|
//
|
|
|
|
// With the help of do-expressions and function.sent, we desugar yield* into a
|
|
|
|
// loop containing a "raw" yield (a yield that doesn't wrap an iterator result
|
|
|
|
// object around its argument). Concretely, "yield* iterable" turns into
|
|
|
|
// roughly the following code:
|
|
|
|
//
|
|
|
|
// do {
|
|
|
|
// const kNext = 0;
|
|
|
|
// const kReturn = 1;
|
|
|
|
// const kThrow = 2;
|
|
|
|
//
|
|
|
|
// let input = function.sent;
|
|
|
|
// let mode = kNext;
|
|
|
|
// let output = undefined;
|
|
|
|
//
|
|
|
|
// let iterator = iterable[Symbol.iterator]();
|
|
|
|
// if (!IS_RECEIVER(iterator)) throw MakeTypeError(kSymbolIteratorInvalid);
|
|
|
|
//
|
|
|
|
// while (true) {
|
|
|
|
// // From the generator to the iterator:
|
|
|
|
// // Forward input according to resume mode and obtain output.
|
|
|
|
// switch (mode) {
|
|
|
|
// case kNext:
|
|
|
|
// output = iterator.next(input);
|
|
|
|
// if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output);
|
|
|
|
// break;
|
|
|
|
// case kReturn:
|
2016-02-04 16:36:11 +00:00
|
|
|
// IteratorClose(iterator, input, output); // See below.
|
2016-02-04 14:12:37 +00:00
|
|
|
// break;
|
|
|
|
// case kThrow:
|
|
|
|
// let iteratorThrow = iterator.throw;
|
|
|
|
// if (IS_NULL_OR_UNDEFINED(iteratorThrow)) {
|
|
|
|
// IteratorClose(iterator); // See below.
|
|
|
|
// throw MakeTypeError(kThrowMethodMissing);
|
|
|
|
// }
|
|
|
|
// output = %_Call(iteratorThrow, iterator, input);
|
|
|
|
// if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output);
|
|
|
|
// break;
|
|
|
|
// }
|
|
|
|
// if (output.done) break;
|
|
|
|
//
|
|
|
|
// // From the generator to its user:
|
|
|
|
// // Forward output, receive new input, and determine resume mode.
|
|
|
|
// mode = kReturn;
|
|
|
|
// try {
|
|
|
|
// try {
|
|
|
|
// RawYield(output); // See explanation above.
|
|
|
|
// mode = kNext;
|
|
|
|
// } catch (error) {
|
|
|
|
// mode = kThrow;
|
|
|
|
// }
|
|
|
|
// } finally {
|
|
|
|
// input = function.sent;
|
|
|
|
// continue;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
2016-06-28 07:08:54 +00:00
|
|
|
// if (mode === kReturn) {
|
|
|
|
// return {value: output.value, done: true};
|
|
|
|
// }
|
|
|
|
// output.value
|
2016-02-04 14:12:37 +00:00
|
|
|
// }
|
|
|
|
//
|
|
|
|
// IteratorClose(iterator) expands to the following:
|
|
|
|
//
|
|
|
|
// let iteratorReturn = iterator.return;
|
2016-06-28 07:43:38 +00:00
|
|
|
// if (!IS_NULL_OR_UNDEFINED(iteratorReturn)) {
|
|
|
|
// let output = %_Call(iteratorReturn, iterator);
|
|
|
|
// if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output);
|
|
|
|
// }
|
2016-02-04 14:12:37 +00:00
|
|
|
//
|
2016-02-04 16:36:11 +00:00
|
|
|
// IteratorClose(iterator, input, output) expands to the following:
|
2016-02-04 14:12:37 +00:00
|
|
|
//
|
|
|
|
// let iteratorReturn = iterator.return;
|
|
|
|
// if (IS_NULL_OR_UNDEFINED(iteratorReturn)) return input;
|
2016-02-04 16:36:11 +00:00
|
|
|
// output = %_Call(iteratorReturn, iterator, input);
|
|
|
|
// if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output);
|
2016-02-04 14:12:37 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* Parser::RewriteYieldStar(Expression* generator,
|
|
|
|
Expression* iterable, int pos) {
|
2016-06-30 09:26:30 +00:00
|
|
|
const int nopos = kNoSourcePosition;
|
2016-02-04 14:12:37 +00:00
|
|
|
|
|
|
|
// Forward definition for break/continue statements.
|
2016-08-19 08:11:04 +00:00
|
|
|
WhileStatement* loop = factory()->NewWhileStatement(nullptr, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
|
|
|
|
// let input = undefined;
|
2016-08-19 08:11:04 +00:00
|
|
|
Variable* var_input = NewTemporary(ast_value_factory()->empty_string());
|
2016-02-04 14:12:37 +00:00
|
|
|
Statement* initialize_input;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* input_proxy = factory()->NewVariableProxy(var_input);
|
|
|
|
Expression* assignment =
|
|
|
|
factory()->NewAssignment(Token::ASSIGN, input_proxy,
|
|
|
|
factory()->NewUndefinedLiteral(nopos), nopos);
|
|
|
|
initialize_input = factory()->NewExpressionStatement(assignment, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// let mode = kNext;
|
2016-08-19 08:11:04 +00:00
|
|
|
Variable* var_mode = NewTemporary(ast_value_factory()->empty_string());
|
2016-02-04 14:12:37 +00:00
|
|
|
Statement* initialize_mode;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* mode_proxy = factory()->NewVariableProxy(var_mode);
|
|
|
|
Expression* knext =
|
|
|
|
factory()->NewSmiLiteral(JSGeneratorObject::kNext, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* assignment =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewAssignment(Token::ASSIGN, mode_proxy, knext, nopos);
|
|
|
|
initialize_mode = factory()->NewExpressionStatement(assignment, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// let output = undefined;
|
2016-08-19 08:11:04 +00:00
|
|
|
Variable* var_output = NewTemporary(ast_value_factory()->empty_string());
|
2016-02-04 14:12:37 +00:00
|
|
|
Statement* initialize_output;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* output_proxy = factory()->NewVariableProxy(var_output);
|
|
|
|
Expression* assignment =
|
|
|
|
factory()->NewAssignment(Token::ASSIGN, output_proxy,
|
|
|
|
factory()->NewUndefinedLiteral(nopos), nopos);
|
|
|
|
initialize_output = factory()->NewExpressionStatement(assignment, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// let iterator = iterable[Symbol.iterator];
|
2016-08-19 08:11:04 +00:00
|
|
|
Variable* var_iterator = NewTemporary(ast_value_factory()->empty_string());
|
2016-02-04 14:12:37 +00:00
|
|
|
Statement* get_iterator;
|
|
|
|
{
|
2016-08-25 08:44:59 +00:00
|
|
|
Expression* iterator = GetIterator(iterable, nopos);
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* iterator_proxy = factory()->NewVariableProxy(var_iterator);
|
|
|
|
Expression* assignment = factory()->NewAssignment(
|
2016-02-04 14:12:37 +00:00
|
|
|
Token::ASSIGN, iterator_proxy, iterator, nopos);
|
2016-08-19 08:11:04 +00:00
|
|
|
get_iterator = factory()->NewExpressionStatement(assignment, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if (!IS_RECEIVER(iterator)) throw MakeTypeError(kSymbolIteratorInvalid);
|
|
|
|
Statement* validate_iterator;
|
|
|
|
{
|
|
|
|
Expression* is_receiver_call;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
auto args = new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(var_iterator), zone());
|
2016-02-04 14:12:37 +00:00
|
|
|
is_receiver_call =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Statement* throw_call;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* call =
|
|
|
|
NewThrowTypeError(MessageTemplate::kSymbolIteratorInvalid,
|
|
|
|
ast_value_factory()->empty_string(), nopos);
|
|
|
|
throw_call = factory()->NewExpressionStatement(call, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
validate_iterator = factory()->NewIfStatement(
|
|
|
|
is_receiver_call, factory()->NewEmptyStatement(nopos), throw_call,
|
|
|
|
nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// output = iterator.next(input);
|
|
|
|
Statement* call_next;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* iterator_proxy = factory()->NewVariableProxy(var_iterator);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* literal =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewStringLiteral(ast_value_factory()->next_string(), nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* next_property =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewProperty(iterator_proxy, literal, nopos);
|
|
|
|
Expression* input_proxy = factory()->NewVariableProxy(var_input);
|
|
|
|
auto args = new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
args->Add(input_proxy, zone());
|
|
|
|
Expression* call = factory()->NewCall(next_property, args, nopos);
|
|
|
|
Expression* output_proxy = factory()->NewVariableProxy(var_output);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* assignment =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewAssignment(Token::ASSIGN, output_proxy, call, nopos);
|
|
|
|
call_next = factory()->NewExpressionStatement(assignment, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output);
|
|
|
|
Statement* validate_next_output;
|
|
|
|
{
|
|
|
|
Expression* is_receiver_call;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
auto args = new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(var_output), zone());
|
2016-02-04 14:12:37 +00:00
|
|
|
is_receiver_call =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Statement* throw_call;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
auto args = new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(var_output), zone());
|
|
|
|
Expression* call = factory()->NewCallRuntime(
|
2016-02-04 14:12:37 +00:00
|
|
|
Runtime::kThrowIteratorResultNotAnObject, args, nopos);
|
2016-08-19 08:11:04 +00:00
|
|
|
throw_call = factory()->NewExpressionStatement(call, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
validate_next_output = factory()->NewIfStatement(
|
|
|
|
is_receiver_call, factory()->NewEmptyStatement(nopos), throw_call,
|
|
|
|
nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// let iteratorThrow = iterator.throw;
|
2016-08-19 08:11:04 +00:00
|
|
|
Variable* var_throw = NewTemporary(ast_value_factory()->empty_string());
|
2016-02-04 14:12:37 +00:00
|
|
|
Statement* get_throw;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* iterator_proxy = factory()->NewVariableProxy(var_iterator);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* literal =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewStringLiteral(ast_value_factory()->throw_string(), nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* property =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewProperty(iterator_proxy, literal, nopos);
|
|
|
|
Expression* throw_proxy = factory()->NewVariableProxy(var_throw);
|
|
|
|
Expression* assignment =
|
|
|
|
factory()->NewAssignment(Token::ASSIGN, throw_proxy, property, nopos);
|
|
|
|
get_throw = factory()->NewExpressionStatement(assignment, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if (IS_NULL_OR_UNDEFINED(iteratorThrow) {
|
|
|
|
// IteratorClose(iterator);
|
|
|
|
// throw MakeTypeError(kThrowMethodMissing);
|
|
|
|
// }
|
|
|
|
Statement* check_throw;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* condition = factory()->NewCompareOperation(
|
|
|
|
Token::EQ, factory()->NewVariableProxy(var_throw),
|
|
|
|
factory()->NewNullLiteral(nopos), nopos);
|
|
|
|
Expression* call =
|
|
|
|
NewThrowTypeError(MessageTemplate::kThrowMethodMissing,
|
|
|
|
ast_value_factory()->empty_string(), nopos);
|
|
|
|
Statement* throw_call = factory()->NewExpressionStatement(call, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Block* then = factory()->NewBlock(nullptr, 4 + 1, false, nopos);
|
|
|
|
BuildIteratorCloseForCompletion(
|
2016-06-28 07:43:38 +00:00
|
|
|
then->statements(), var_iterator,
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewSmiLiteral(Parser::kNormalCompletion, nopos));
|
|
|
|
then->statements()->Add(throw_call, zone());
|
|
|
|
check_throw = factory()->NewIfStatement(
|
|
|
|
condition, then, factory()->NewEmptyStatement(nopos), nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// output = %_Call(iteratorThrow, iterator, input);
|
|
|
|
Statement* call_throw;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
auto args = new (zone()) ZoneList<Expression*>(3, zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(var_throw), zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(var_iterator), zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(var_input), zone());
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* call =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewCallRuntime(Runtime::kInlineCall, args, nopos);
|
|
|
|
Expression* assignment = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, factory()->NewVariableProxy(var_output), call, nopos);
|
|
|
|
call_throw = factory()->NewExpressionStatement(assignment, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output);
|
|
|
|
Statement* validate_throw_output;
|
|
|
|
{
|
|
|
|
Expression* is_receiver_call;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
auto args = new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(var_output), zone());
|
2016-02-04 14:12:37 +00:00
|
|
|
is_receiver_call =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Statement* throw_call;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
auto args = new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(var_output), zone());
|
|
|
|
Expression* call = factory()->NewCallRuntime(
|
2016-02-04 14:12:37 +00:00
|
|
|
Runtime::kThrowIteratorResultNotAnObject, args, nopos);
|
2016-08-19 08:11:04 +00:00
|
|
|
throw_call = factory()->NewExpressionStatement(call, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
validate_throw_output = factory()->NewIfStatement(
|
|
|
|
is_receiver_call, factory()->NewEmptyStatement(nopos), throw_call,
|
|
|
|
nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if (output.done) break;
|
|
|
|
Statement* if_done;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* output_proxy = factory()->NewVariableProxy(var_output);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* literal =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewStringLiteral(ast_value_factory()->done_string(), nopos);
|
|
|
|
Expression* property = factory()->NewProperty(output_proxy, literal, nopos);
|
|
|
|
BreakStatement* break_loop = factory()->NewBreakStatement(loop, nopos);
|
|
|
|
if_done = factory()->NewIfStatement(
|
|
|
|
property, break_loop, factory()->NewEmptyStatement(nopos), nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// mode = kReturn;
|
|
|
|
Statement* set_mode_return;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* mode_proxy = factory()->NewVariableProxy(var_mode);
|
2016-02-04 17:13:40 +00:00
|
|
|
Expression* kreturn =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewSmiLiteral(JSGeneratorObject::kReturn, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* assignment =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewAssignment(Token::ASSIGN, mode_proxy, kreturn, nopos);
|
|
|
|
set_mode_return = factory()->NewExpressionStatement(assignment, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
2016-03-06 09:19:14 +00:00
|
|
|
// Yield(output);
|
2016-02-04 14:12:37 +00:00
|
|
|
Statement* yield_output;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* output_proxy = factory()->NewVariableProxy(var_output);
|
|
|
|
Yield* yield = factory()->NewYield(generator, output_proxy, nopos,
|
|
|
|
Yield::kOnExceptionThrow);
|
|
|
|
yield_output = factory()->NewExpressionStatement(yield, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// mode = kNext;
|
|
|
|
Statement* set_mode_next;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* mode_proxy = factory()->NewVariableProxy(var_mode);
|
|
|
|
Expression* knext =
|
|
|
|
factory()->NewSmiLiteral(JSGeneratorObject::kNext, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* assignment =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewAssignment(Token::ASSIGN, mode_proxy, knext, nopos);
|
|
|
|
set_mode_next = factory()->NewExpressionStatement(assignment, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// mode = kThrow;
|
|
|
|
Statement* set_mode_throw;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* mode_proxy = factory()->NewVariableProxy(var_mode);
|
2016-02-04 17:13:40 +00:00
|
|
|
Expression* kthrow =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewSmiLiteral(JSGeneratorObject::kThrow, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* assignment =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewAssignment(Token::ASSIGN, mode_proxy, kthrow, nopos);
|
|
|
|
set_mode_throw = factory()->NewExpressionStatement(assignment, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// input = function.sent;
|
|
|
|
Statement* get_input;
|
|
|
|
{
|
2016-08-25 08:44:59 +00:00
|
|
|
Expression* function_sent = FunctionSentExpression(nopos);
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* input_proxy = factory()->NewVariableProxy(var_input);
|
|
|
|
Expression* assignment = factory()->NewAssignment(
|
2016-02-04 14:12:37 +00:00
|
|
|
Token::ASSIGN, input_proxy, function_sent, nopos);
|
2016-08-19 08:11:04 +00:00
|
|
|
get_input = factory()->NewExpressionStatement(assignment, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
2016-06-28 07:08:54 +00:00
|
|
|
// if (mode === kReturn) {
|
|
|
|
// return {value: output.value, done: true};
|
|
|
|
// }
|
|
|
|
Statement* maybe_return_value;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* mode_proxy = factory()->NewVariableProxy(var_mode);
|
2016-06-28 07:08:54 +00:00
|
|
|
Expression* kreturn =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewSmiLiteral(JSGeneratorObject::kReturn, nopos);
|
|
|
|
Expression* condition = factory()->NewCompareOperation(
|
2016-06-28 07:08:54 +00:00
|
|
|
Token::EQ_STRICT, mode_proxy, kreturn, nopos);
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* output_proxy = factory()->NewVariableProxy(var_output);
|
2016-06-28 07:08:54 +00:00
|
|
|
Expression* literal =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewStringLiteral(ast_value_factory()->value_string(), nopos);
|
|
|
|
Expression* property = factory()->NewProperty(output_proxy, literal, nopos);
|
|
|
|
Statement* return_value = factory()->NewReturnStatement(
|
|
|
|
BuildIteratorResult(property, true), nopos);
|
2016-06-28 07:08:54 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
maybe_return_value = factory()->NewIfStatement(
|
|
|
|
condition, return_value, factory()->NewEmptyStatement(nopos), nopos);
|
2016-06-28 07:08:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// output.value
|
2016-02-04 14:12:37 +00:00
|
|
|
Statement* get_value;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* output_proxy = factory()->NewVariableProxy(var_output);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* literal =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewStringLiteral(ast_value_factory()->value_string(), nopos);
|
|
|
|
Expression* property = factory()->NewProperty(output_proxy, literal, nopos);
|
|
|
|
get_value = factory()->NewExpressionStatement(property, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Now put things together.
|
|
|
|
|
|
|
|
// try { ... } catch(e) { ... }
|
|
|
|
Statement* try_catch;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Block* try_block = factory()->NewBlock(nullptr, 2, false, nopos);
|
|
|
|
try_block->statements()->Add(yield_output, zone());
|
|
|
|
try_block->statements()->Add(set_mode_next, zone());
|
2016-02-04 14:12:37 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Block* catch_block = factory()->NewBlock(nullptr, 1, false, nopos);
|
|
|
|
catch_block->statements()->Add(set_mode_throw, zone());
|
2016-02-04 14:12:37 +00:00
|
|
|
|
2016-08-09 19:49:25 +00:00
|
|
|
Scope* catch_scope = NewScope(CATCH_SCOPE);
|
2016-04-22 10:46:42 +00:00
|
|
|
catch_scope->set_is_hidden();
|
2016-08-19 08:11:04 +00:00
|
|
|
const AstRawString* name = ast_value_factory()->dot_catch_string();
|
2016-09-09 10:55:31 +00:00
|
|
|
Variable* catch_variable = catch_scope->DeclareLocal(
|
|
|
|
name, VAR, kCreatedInitialized, NORMAL_VARIABLE);
|
2016-02-04 14:12:37 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
try_catch = factory()->NewTryCatchStatementForDesugaring(
|
2016-02-04 14:12:37 +00:00
|
|
|
try_block, catch_scope, catch_variable, catch_block, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// try { ... } finally { ... }
|
|
|
|
Statement* try_finally;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Block* try_block = factory()->NewBlock(nullptr, 1, false, nopos);
|
|
|
|
try_block->statements()->Add(try_catch, zone());
|
2016-02-04 14:12:37 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Block* finally = factory()->NewBlock(nullptr, 2, false, nopos);
|
|
|
|
finally->statements()->Add(get_input, zone());
|
|
|
|
finally->statements()->Add(factory()->NewContinueStatement(loop, nopos),
|
|
|
|
zone());
|
2016-02-04 14:12:37 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
try_finally = factory()->NewTryFinallyStatement(try_block, finally, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// switch (mode) { ... }
|
2016-08-19 08:11:04 +00:00
|
|
|
SwitchStatement* switch_mode = factory()->NewSwitchStatement(nullptr, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
auto case_next = new (zone()) ZoneList<Statement*>(3, zone());
|
|
|
|
case_next->Add(call_next, zone());
|
|
|
|
case_next->Add(validate_next_output, zone());
|
|
|
|
case_next->Add(factory()->NewBreakStatement(switch_mode, nopos), zone());
|
2016-02-04 14:12:37 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
auto case_return = new (zone()) ZoneList<Statement*>(5, zone());
|
2016-06-28 07:43:38 +00:00
|
|
|
BuildIteratorClose(case_return, var_iterator, var_input, var_output);
|
2016-08-19 08:11:04 +00:00
|
|
|
case_return->Add(factory()->NewBreakStatement(switch_mode, nopos), zone());
|
|
|
|
|
|
|
|
auto case_throw = new (zone()) ZoneList<Statement*>(5, zone());
|
|
|
|
case_throw->Add(get_throw, zone());
|
|
|
|
case_throw->Add(check_throw, zone());
|
|
|
|
case_throw->Add(call_throw, zone());
|
|
|
|
case_throw->Add(validate_throw_output, zone());
|
|
|
|
case_throw->Add(factory()->NewBreakStatement(switch_mode, nopos), zone());
|
|
|
|
|
|
|
|
auto cases = new (zone()) ZoneList<CaseClause*>(3, zone());
|
|
|
|
Expression* knext =
|
|
|
|
factory()->NewSmiLiteral(JSGeneratorObject::kNext, nopos);
|
2016-02-04 17:13:40 +00:00
|
|
|
Expression* kreturn =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewSmiLiteral(JSGeneratorObject::kReturn, nopos);
|
2016-02-04 17:13:40 +00:00
|
|
|
Expression* kthrow =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewSmiLiteral(JSGeneratorObject::kThrow, nopos);
|
|
|
|
cases->Add(factory()->NewCaseClause(knext, case_next, nopos), zone());
|
|
|
|
cases->Add(factory()->NewCaseClause(kreturn, case_return, nopos), zone());
|
|
|
|
cases->Add(factory()->NewCaseClause(kthrow, case_throw, nopos), zone());
|
2016-02-04 14:12:37 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
switch_mode->Initialize(factory()->NewVariableProxy(var_mode), cases);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// while (true) { ... }
|
|
|
|
// Already defined earlier: WhileStatement* loop = ...
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Block* loop_body = factory()->NewBlock(nullptr, 4, false, nopos);
|
|
|
|
loop_body->statements()->Add(switch_mode, zone());
|
|
|
|
loop_body->statements()->Add(if_done, zone());
|
|
|
|
loop_body->statements()->Add(set_mode_return, zone());
|
|
|
|
loop_body->statements()->Add(try_finally, zone());
|
2016-02-04 14:12:37 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
loop->Initialize(factory()->NewBooleanLiteral(true, nopos), loop_body);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// do { ... }
|
|
|
|
DoExpression* yield_star;
|
|
|
|
{
|
|
|
|
// The rewriter needs to process the get_value statement only, hence we
|
|
|
|
// put the preceding statements into an init block.
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Block* do_block_ = factory()->NewBlock(nullptr, 7, true, nopos);
|
|
|
|
do_block_->statements()->Add(initialize_input, zone());
|
|
|
|
do_block_->statements()->Add(initialize_mode, zone());
|
|
|
|
do_block_->statements()->Add(initialize_output, zone());
|
|
|
|
do_block_->statements()->Add(get_iterator, zone());
|
|
|
|
do_block_->statements()->Add(validate_iterator, zone());
|
|
|
|
do_block_->statements()->Add(loop, zone());
|
|
|
|
do_block_->statements()->Add(maybe_return_value, zone());
|
2016-02-04 14:12:37 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Block* do_block = factory()->NewBlock(nullptr, 2, false, nopos);
|
|
|
|
do_block->statements()->Add(do_block_, zone());
|
|
|
|
do_block->statements()->Add(get_value, zone());
|
2016-02-04 14:12:37 +00:00
|
|
|
|
2016-08-09 19:49:25 +00:00
|
|
|
Variable* dot_result =
|
2016-08-19 08:11:04 +00:00
|
|
|
NewTemporary(ast_value_factory()->dot_result_string());
|
|
|
|
yield_star = factory()->NewDoExpression(do_block, dot_result, nopos);
|
|
|
|
Rewriter::Rewrite(this, GetClosureScope(), yield_star, ast_value_factory());
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return yield_star;
|
|
|
|
}
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Statement* Parser::CheckCallable(Variable* var, Expression* error, int pos) {
|
2016-06-30 09:26:30 +00:00
|
|
|
const int nopos = kNoSourcePosition;
|
2016-02-19 19:19:52 +00:00
|
|
|
Statement* validate_var;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* type_of = factory()->NewUnaryOperation(
|
|
|
|
Token::TYPEOF, factory()->NewVariableProxy(var), nopos);
|
|
|
|
Expression* function_literal = factory()->NewStringLiteral(
|
|
|
|
ast_value_factory()->function_string(), nopos);
|
|
|
|
Expression* condition = factory()->NewCompareOperation(
|
2016-02-19 19:19:52 +00:00
|
|
|
Token::EQ_STRICT, type_of, function_literal, nopos);
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Statement* throw_call = factory()->NewExpressionStatement(error, pos);
|
2016-02-19 19:19:52 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
validate_var = factory()->NewIfStatement(
|
|
|
|
condition, factory()->NewEmptyStatement(nopos), throw_call, nopos);
|
2016-02-19 19:19:52 +00:00
|
|
|
}
|
|
|
|
return validate_var;
|
|
|
|
}
|
2016-02-04 14:12:37 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
void Parser::BuildIteratorClose(ZoneList<Statement*>* statements,
|
|
|
|
Variable* iterator, Variable* input,
|
|
|
|
Variable* var_output) {
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
//
|
|
|
|
// This function adds four statements to [statements], corresponding to the
|
|
|
|
// following code:
|
|
|
|
//
|
|
|
|
// let iteratorReturn = iterator.return;
|
2016-06-02 09:42:34 +00:00
|
|
|
// if (IS_NULL_OR_UNDEFINED(iteratorReturn) {
|
|
|
|
// return {value: input, done: true};
|
|
|
|
// }
|
|
|
|
// output = %_Call(iteratorReturn, iterator, input);
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
// if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output);
|
|
|
|
//
|
|
|
|
|
2016-06-30 09:26:30 +00:00
|
|
|
const int nopos = kNoSourcePosition;
|
2016-02-04 14:12:37 +00:00
|
|
|
|
|
|
|
// let iteratorReturn = iterator.return;
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
Variable* var_return = var_output; // Reusing the output variable.
|
2016-02-04 14:12:37 +00:00
|
|
|
Statement* get_return;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* iterator_proxy = factory()->NewVariableProxy(iterator);
|
|
|
|
Expression* literal = factory()->NewStringLiteral(
|
|
|
|
ast_value_factory()->return_string(), nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* property =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewProperty(iterator_proxy, literal, nopos);
|
|
|
|
Expression* return_proxy = factory()->NewVariableProxy(var_return);
|
|
|
|
Expression* assignment =
|
|
|
|
factory()->NewAssignment(Token::ASSIGN, return_proxy, property, nopos);
|
|
|
|
get_return = factory()->NewExpressionStatement(assignment, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
2016-06-02 09:42:34 +00:00
|
|
|
// if (IS_NULL_OR_UNDEFINED(iteratorReturn) {
|
|
|
|
// return {value: input, done: true};
|
|
|
|
// }
|
2016-02-04 14:12:37 +00:00
|
|
|
Statement* check_return;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* condition = factory()->NewCompareOperation(
|
|
|
|
Token::EQ, factory()->NewVariableProxy(var_return),
|
|
|
|
factory()->NewNullLiteral(nopos), nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* value = factory()->NewVariableProxy(input);
|
2016-03-01 09:38:54 +00:00
|
|
|
|
2016-06-02 09:42:34 +00:00
|
|
|
Statement* return_input =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewReturnStatement(BuildIteratorResult(value, true), nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
check_return = factory()->NewIfStatement(
|
|
|
|
condition, return_input, factory()->NewEmptyStatement(nopos), nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
2016-06-02 09:42:34 +00:00
|
|
|
// output = %_Call(iteratorReturn, iterator, input);
|
2016-02-04 14:12:37 +00:00
|
|
|
Statement* call_return;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
auto args = new (zone()) ZoneList<Expression*>(3, zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(var_return), zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(iterator), zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(input), zone());
|
2016-02-04 14:12:37 +00:00
|
|
|
|
|
|
|
Expression* call =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewCallRuntime(Runtime::kInlineCall, args, nopos);
|
|
|
|
Expression* output_proxy = factory()->NewVariableProxy(var_output);
|
|
|
|
Expression* assignment =
|
|
|
|
factory()->NewAssignment(Token::ASSIGN, output_proxy, call, nopos);
|
|
|
|
call_return = factory()->NewExpressionStatement(assignment, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
// if (!IS_RECEIVER(output)) %ThrowIteratorResultNotAnObject(output);
|
2016-02-04 16:36:11 +00:00
|
|
|
Statement* validate_output;
|
2016-02-04 14:12:37 +00:00
|
|
|
{
|
|
|
|
Expression* is_receiver_call;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
auto args = new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(var_output), zone());
|
2016-02-04 14:12:37 +00:00
|
|
|
is_receiver_call =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Statement* throw_call;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
auto args = new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(var_output), zone());
|
|
|
|
Expression* call = factory()->NewCallRuntime(
|
2016-02-04 14:12:37 +00:00
|
|
|
Runtime::kThrowIteratorResultNotAnObject, args, nopos);
|
2016-08-19 08:11:04 +00:00
|
|
|
throw_call = factory()->NewExpressionStatement(call, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
validate_output = factory()->NewIfStatement(
|
|
|
|
is_receiver_call, factory()->NewEmptyStatement(nopos), throw_call,
|
|
|
|
nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
statements->Add(get_return, zone());
|
|
|
|
statements->Add(check_return, zone());
|
|
|
|
statements->Add(call_return, zone());
|
|
|
|
statements->Add(validate_output, zone());
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
void Parser::FinalizeIteratorUse(Variable* completion, Expression* condition,
|
|
|
|
Variable* iter, Block* iterator_use,
|
|
|
|
Block* target) {
|
2016-03-10 09:33:29 +00:00
|
|
|
//
|
|
|
|
// This function adds two statements to [target], corresponding to the
|
|
|
|
// following code:
|
|
|
|
//
|
|
|
|
// completion = kNormalCompletion;
|
|
|
|
// try {
|
|
|
|
// try {
|
|
|
|
// iterator_use
|
|
|
|
// } catch(e) {
|
|
|
|
// if (completion === kAbruptCompletion) completion = kThrowCompletion;
|
2016-04-04 08:14:57 +00:00
|
|
|
// %ReThrow(e);
|
2016-03-10 09:33:29 +00:00
|
|
|
// }
|
|
|
|
// } finally {
|
|
|
|
// if (condition) {
|
|
|
|
// #BuildIteratorCloseForCompletion(iter, completion)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
|
2016-06-30 09:26:30 +00:00
|
|
|
const int nopos = kNoSourcePosition;
|
2016-03-10 09:33:29 +00:00
|
|
|
|
|
|
|
// completion = kNormalCompletion;
|
|
|
|
Statement* initialize_completion;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* proxy = factory()->NewVariableProxy(completion);
|
|
|
|
Expression* assignment = factory()->NewAssignment(
|
2016-03-10 09:33:29 +00:00
|
|
|
Token::ASSIGN, proxy,
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewSmiLiteral(Parser::kNormalCompletion, nopos), nopos);
|
|
|
|
initialize_completion =
|
|
|
|
factory()->NewExpressionStatement(assignment, nopos);
|
2016-03-10 09:33:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if (completion === kAbruptCompletion) completion = kThrowCompletion;
|
|
|
|
Statement* set_completion_throw;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* condition = factory()->NewCompareOperation(
|
|
|
|
Token::EQ_STRICT, factory()->NewVariableProxy(completion),
|
|
|
|
factory()->NewSmiLiteral(Parser::kAbruptCompletion, nopos), nopos);
|
2016-03-10 09:33:29 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* proxy = factory()->NewVariableProxy(completion);
|
|
|
|
Expression* assignment = factory()->NewAssignment(
|
2016-03-10 09:33:29 +00:00
|
|
|
Token::ASSIGN, proxy,
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewSmiLiteral(Parser::kThrowCompletion, nopos), nopos);
|
|
|
|
Statement* statement = factory()->NewExpressionStatement(assignment, nopos);
|
|
|
|
set_completion_throw = factory()->NewIfStatement(
|
|
|
|
condition, statement, factory()->NewEmptyStatement(nopos), nopos);
|
2016-03-10 09:33:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if (condition) {
|
|
|
|
// #BuildIteratorCloseForCompletion(iter, completion)
|
|
|
|
// }
|
|
|
|
Block* maybe_close;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Block* block = factory()->NewBlock(nullptr, 2, true, nopos);
|
|
|
|
Expression* proxy = factory()->NewVariableProxy(completion);
|
|
|
|
BuildIteratorCloseForCompletion(block->statements(), iter, proxy);
|
2016-03-10 09:33:29 +00:00
|
|
|
DCHECK(block->statements()->length() == 2);
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
maybe_close = factory()->NewBlock(nullptr, 1, true, nopos);
|
2016-03-10 09:33:29 +00:00
|
|
|
maybe_close->statements()->Add(
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewIfStatement(condition, block,
|
|
|
|
factory()->NewEmptyStatement(nopos), nopos),
|
|
|
|
zone());
|
2016-03-10 09:33:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// try { #try_block }
|
|
|
|
// catch(e) {
|
|
|
|
// #set_completion_throw;
|
2016-04-04 08:14:57 +00:00
|
|
|
// %ReThrow(e);
|
2016-03-10 09:33:29 +00:00
|
|
|
// }
|
|
|
|
Statement* try_catch;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Scope* catch_scope = NewScopeWithParent(scope(), CATCH_SCOPE);
|
2016-03-10 09:33:29 +00:00
|
|
|
Variable* catch_variable =
|
2016-08-19 08:11:04 +00:00
|
|
|
catch_scope->DeclareLocal(ast_value_factory()->dot_catch_string(), VAR,
|
2016-09-09 10:55:31 +00:00
|
|
|
kCreatedInitialized, NORMAL_VARIABLE);
|
2016-04-22 10:46:42 +00:00
|
|
|
catch_scope->set_is_hidden();
|
2016-03-10 09:33:29 +00:00
|
|
|
|
|
|
|
Statement* rethrow;
|
2016-04-04 08:14:57 +00:00
|
|
|
// We use %ReThrow rather than the ordinary throw because we want to
|
|
|
|
// preserve the original exception message. This is also why we create a
|
|
|
|
// TryCatchStatementForReThrow below (which does not clear the pending
|
|
|
|
// message), rather than a TryCatchStatement.
|
2016-03-10 09:33:29 +00:00
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
auto args = new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(catch_variable), zone());
|
|
|
|
rethrow = factory()->NewExpressionStatement(
|
|
|
|
factory()->NewCallRuntime(Runtime::kReThrow, args, nopos), nopos);
|
2016-03-10 09:33:29 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Block* catch_block = factory()->NewBlock(nullptr, 2, false, nopos);
|
|
|
|
catch_block->statements()->Add(set_completion_throw, zone());
|
|
|
|
catch_block->statements()->Add(rethrow, zone());
|
2016-03-10 09:33:29 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
try_catch = factory()->NewTryCatchStatementForReThrow(
|
2016-03-10 09:33:29 +00:00
|
|
|
iterator_use, catch_scope, catch_variable, catch_block, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// try { #try_catch } finally { #maybe_close }
|
|
|
|
Statement* try_finally;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Block* try_block = factory()->NewBlock(nullptr, 1, false, nopos);
|
|
|
|
try_block->statements()->Add(try_catch, zone());
|
2016-03-10 09:33:29 +00:00
|
|
|
|
|
|
|
try_finally =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewTryFinallyStatement(try_block, maybe_close, nopos);
|
2016-03-10 09:33:29 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
target->statements()->Add(initialize_completion, zone());
|
|
|
|
target->statements()->Add(try_finally, zone());
|
2016-03-10 09:33:29 +00:00
|
|
|
}
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
void Parser::BuildIteratorCloseForCompletion(ZoneList<Statement*>* statements,
|
|
|
|
Variable* iterator,
|
|
|
|
Expression* completion) {
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
//
|
|
|
|
// This function adds two statements to [statements], corresponding to the
|
|
|
|
// following code:
|
|
|
|
//
|
|
|
|
// let iteratorReturn = iterator.return;
|
|
|
|
// if (!IS_NULL_OR_UNDEFINED(iteratorReturn)) {
|
2016-03-10 09:33:29 +00:00
|
|
|
// if (completion === kThrowCompletion) {
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
// if (!IS_CALLABLE(iteratorReturn)) {
|
|
|
|
// throw MakeTypeError(kReturnMethodNotCallable);
|
|
|
|
// }
|
2016-02-24 18:21:32 +00:00
|
|
|
// try { %_Call(iteratorReturn, iterator) } catch (_) { }
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
// } else {
|
2016-02-24 18:21:32 +00:00
|
|
|
// let output = %_Call(iteratorReturn, iterator);
|
|
|
|
// if (!IS_RECEIVER(output)) {
|
|
|
|
// %ThrowIterResultNotAnObject(output);
|
|
|
|
// }
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
|
2016-06-30 09:26:30 +00:00
|
|
|
const int nopos = kNoSourcePosition;
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
// let iteratorReturn = iterator.return;
|
2016-08-19 08:11:04 +00:00
|
|
|
Variable* var_return = NewTemporary(ast_value_factory()->empty_string());
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
Statement* get_return;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* iterator_proxy = factory()->NewVariableProxy(iterator);
|
|
|
|
Expression* literal = factory()->NewStringLiteral(
|
|
|
|
ast_value_factory()->return_string(), nopos);
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
Expression* property =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewProperty(iterator_proxy, literal, nopos);
|
|
|
|
Expression* return_proxy = factory()->NewVariableProxy(var_return);
|
|
|
|
Expression* assignment =
|
|
|
|
factory()->NewAssignment(Token::ASSIGN, return_proxy, property, nopos);
|
|
|
|
get_return = factory()->NewExpressionStatement(assignment, nopos);
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if (!IS_CALLABLE(iteratorReturn)) {
|
|
|
|
// throw MakeTypeError(kReturnMethodNotCallable);
|
|
|
|
// }
|
|
|
|
Statement* check_return_callable;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* throw_expr =
|
|
|
|
NewThrowTypeError(MessageTemplate::kReturnMethodNotCallable,
|
|
|
|
ast_value_factory()->empty_string(), nopos);
|
2016-03-18 10:39:09 +00:00
|
|
|
check_return_callable = CheckCallable(var_return, throw_expr, nopos);
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
}
|
|
|
|
|
2016-02-24 18:21:32 +00:00
|
|
|
// try { %_Call(iteratorReturn, iterator) } catch (_) { }
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
Statement* try_call_return;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
auto args = new (zone()) ZoneList<Expression*>(2, zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(var_return), zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(iterator), zone());
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
|
|
|
|
Expression* call =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewCallRuntime(Runtime::kInlineCall, args, nopos);
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Block* try_block = factory()->NewBlock(nullptr, 1, false, nopos);
|
|
|
|
try_block->statements()->Add(factory()->NewExpressionStatement(call, nopos),
|
|
|
|
zone());
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Block* catch_block = factory()->NewBlock(nullptr, 0, false, nopos);
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
|
2016-08-09 19:49:25 +00:00
|
|
|
Scope* catch_scope = NewScope(CATCH_SCOPE);
|
2016-08-19 08:11:04 +00:00
|
|
|
Variable* catch_variable =
|
|
|
|
catch_scope->DeclareLocal(ast_value_factory()->dot_catch_string(), VAR,
|
2016-09-09 10:55:31 +00:00
|
|
|
kCreatedInitialized, NORMAL_VARIABLE);
|
2016-04-22 10:46:42 +00:00
|
|
|
catch_scope->set_is_hidden();
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
try_call_return = factory()->NewTryCatchStatement(
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
try_block, catch_scope, catch_variable, catch_block, nopos);
|
|
|
|
}
|
|
|
|
|
2016-02-24 18:21:32 +00:00
|
|
|
// let output = %_Call(iteratorReturn, iterator);
|
|
|
|
// if (!IS_RECEIVER(output)) {
|
|
|
|
// %ThrowIteratorResultNotAnObject(output);
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
// }
|
2016-02-24 18:21:32 +00:00
|
|
|
Block* validate_return;
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Variable* var_output = NewTemporary(ast_value_factory()->empty_string());
|
2016-02-24 18:21:32 +00:00
|
|
|
Statement* call_return;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
auto args = new (zone()) ZoneList<Expression*>(2, zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(var_return), zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(iterator), zone());
|
2016-02-24 18:21:32 +00:00
|
|
|
Expression* call =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewCallRuntime(Runtime::kInlineCall, args, nopos);
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* output_proxy = factory()->NewVariableProxy(var_output);
|
2016-02-24 18:21:32 +00:00
|
|
|
Expression* assignment =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewAssignment(Token::ASSIGN, output_proxy, call, nopos);
|
|
|
|
call_return = factory()->NewExpressionStatement(assignment, nopos);
|
2016-02-24 18:21:32 +00:00
|
|
|
}
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
|
|
|
|
Expression* is_receiver_call;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
auto args = new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(var_output), zone());
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
is_receiver_call =
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Statement* throw_call;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
auto args = new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
args->Add(factory()->NewVariableProxy(var_output), zone());
|
|
|
|
Expression* call = factory()->NewCallRuntime(
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
Runtime::kThrowIteratorResultNotAnObject, args, nopos);
|
2016-08-19 08:11:04 +00:00
|
|
|
throw_call = factory()->NewExpressionStatement(call, nopos);
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Statement* check_return = factory()->NewIfStatement(
|
|
|
|
is_receiver_call, factory()->NewEmptyStatement(nopos), throw_call,
|
|
|
|
nopos);
|
2016-02-24 18:21:32 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
validate_return = factory()->NewBlock(nullptr, 2, false, nopos);
|
|
|
|
validate_return->statements()->Add(call_return, zone());
|
|
|
|
validate_return->statements()->Add(check_return, zone());
|
2016-02-24 18:21:32 +00:00
|
|
|
}
|
|
|
|
|
2016-03-10 09:33:29 +00:00
|
|
|
// if (completion === kThrowCompletion) {
|
2016-02-24 18:21:32 +00:00
|
|
|
// #check_return_callable;
|
|
|
|
// #try_call_return;
|
|
|
|
// } else {
|
|
|
|
// #validate_return;
|
|
|
|
// }
|
|
|
|
Statement* call_return_carefully;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* condition = factory()->NewCompareOperation(
|
2016-06-28 07:43:38 +00:00
|
|
|
Token::EQ_STRICT, completion,
|
2016-08-19 08:11:04 +00:00
|
|
|
factory()->NewSmiLiteral(Parser::kThrowCompletion, nopos), nopos);
|
2016-02-24 18:21:32 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Block* then_block = factory()->NewBlock(nullptr, 2, false, nopos);
|
|
|
|
then_block->statements()->Add(check_return_callable, zone());
|
|
|
|
then_block->statements()->Add(try_call_return, zone());
|
2016-02-24 18:21:32 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
call_return_carefully = factory()->NewIfStatement(condition, then_block,
|
|
|
|
validate_return, nopos);
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if (!IS_NULL_OR_UNDEFINED(iteratorReturn)) { ... }
|
|
|
|
Statement* maybe_call_return;
|
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* condition = factory()->NewCompareOperation(
|
|
|
|
Token::EQ, factory()->NewVariableProxy(var_return),
|
|
|
|
factory()->NewNullLiteral(nopos), nopos);
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
maybe_call_return = factory()->NewIfStatement(
|
|
|
|
condition, factory()->NewEmptyStatement(nopos), call_return_carefully,
|
|
|
|
nopos);
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
statements->Add(get_return, zone());
|
|
|
|
statements->Add(maybe_call_return, zone());
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Statement* Parser::FinalizeForOfStatement(ForOfStatement* loop,
|
|
|
|
Variable* var_completion, int pos) {
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
//
|
|
|
|
// This function replaces the loop with the following wrapping:
|
|
|
|
//
|
2016-07-07 08:15:11 +00:00
|
|
|
// completion = kNormalCompletion;
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
// try {
|
2016-03-10 09:33:29 +00:00
|
|
|
// try {
|
|
|
|
// #loop;
|
|
|
|
// } catch(e) {
|
|
|
|
// if (completion === kAbruptCompletion) completion = kThrowCompletion;
|
2016-04-04 08:14:57 +00:00
|
|
|
// %ReThrow(e);
|
2016-03-10 09:33:29 +00:00
|
|
|
// }
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
// } finally {
|
2016-03-10 09:33:29 +00:00
|
|
|
// if (!(completion === kNormalCompletion || IS_UNDEFINED(#iterator))) {
|
2016-02-24 18:21:32 +00:00
|
|
|
// #BuildIteratorCloseForCompletion(#iterator, completion)
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
2016-07-07 08:15:11 +00:00
|
|
|
// Note that the loop's body and its assign_each already contain appropriate
|
|
|
|
// assignments to completion (see InitializeForOfStatement).
|
2016-03-10 09:33:29 +00:00
|
|
|
//
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
|
2016-06-30 09:26:30 +00:00
|
|
|
const int nopos = kNoSourcePosition;
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
|
2016-03-10 09:33:29 +00:00
|
|
|
// !(completion === kNormalCompletion || IS_UNDEFINED(#iterator))
|
|
|
|
Expression* closing_condition;
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Expression* lhs = factory()->NewCompareOperation(
|
|
|
|
Token::EQ_STRICT, factory()->NewVariableProxy(var_completion),
|
|
|
|
factory()->NewSmiLiteral(Parser::kNormalCompletion, nopos), nopos);
|
|
|
|
Expression* rhs = factory()->NewCompareOperation(
|
|
|
|
Token::EQ_STRICT, factory()->NewVariableProxy(loop->iterator()),
|
|
|
|
factory()->NewUndefinedLiteral(nopos), nopos);
|
|
|
|
closing_condition = factory()->NewUnaryOperation(
|
|
|
|
Token::NOT, factory()->NewBinaryOperation(Token::OR, lhs, rhs, nopos),
|
2016-03-10 09:33:29 +00:00
|
|
|
nopos);
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 08:11:04 +00:00
|
|
|
Block* final_loop = factory()->NewBlock(nullptr, 2, false, nopos);
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
{
|
2016-08-19 08:11:04 +00:00
|
|
|
Block* try_block = factory()->NewBlock(nullptr, 1, false, nopos);
|
|
|
|
try_block->statements()->Add(loop, zone());
|
2016-03-10 09:33:29 +00:00
|
|
|
|
|
|
|
FinalizeIteratorUse(var_completion, closing_condition, loop->iterator(),
|
2016-07-07 08:15:11 +00:00
|
|
|
try_block, final_loop);
|
2016-03-10 09:33:29 +00:00
|
|
|
}
|
2016-02-24 18:52:17 +00:00
|
|
|
|
[es6] Implement for-of iterator finalization
Implements iterator finalisation by desugaring for-of loops with an additional try-finally wrapper. See comment in parser.cc for details.
Also improved some AST printing facilities while there.
@Ross, I had to disable the bytecode generation test for for-of, because it got completely out of hand after this change (the new bytecode has 150+ lines). See the TODO that I assigned to you.
Patch set 1 is WIP patch by Georg (http://crrev.com/1695583003), patch set 2 relative changes.
@Georg, FYI, I changed the following:
- Moved try-finally out of the loop body, for performance, and in order to be able to handle `continue` correctly.
- Fixed scope management in ParseForStatement, which was the cause for the variable allocation failure.
- Fixed pre-existing zone initialisation bug in rewriter, which caused the crashes.
- Enabled all tests, adjusted a few others, added a couple more.
BUG=v8:2214
LOG=Y
Review URL: https://codereview.chromium.org/1695393003
Cr-Commit-Position: refs/heads/master@{#34111}
2016-02-18 10:49:07 +00:00
|
|
|
return final_loop;
|
|
|
|
}
|
|
|
|
|
2016-07-19 08:03:43 +00:00
|
|
|
#undef CHECK_OK
|
2016-07-19 19:45:50 +00:00
|
|
|
#undef CHECK_OK_VOID
|
2016-07-19 08:03:43 +00:00
|
|
|
#undef CHECK_FAILED
|
|
|
|
|
2015-06-01 22:46:54 +00:00
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|