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
|
|
|
|
2014-06-03 08:12:43 +00:00
|
|
|
#include "src/api.h"
|
2015-11-26 16:22:34 +00:00
|
|
|
#include "src/ast/ast.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-12-04 17:20:10 +00:00
|
|
|
#include "src/ast/ast-expression-visitor.h"
|
2015-11-26 16:22:34 +00:00
|
|
|
#include "src/ast/ast-literal-reindexer.h"
|
|
|
|
#include "src/ast/scopeinfo.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/bootstrapper.h"
|
|
|
|
#include "src/char-predicates-inl.h"
|
|
|
|
#include "src/codegen.h"
|
|
|
|
#include "src/compiler.h"
|
|
|
|
#include "src/messages.h"
|
2015-11-26 16:22:34 +00:00
|
|
|
#include "src/parsing/parameter-initializer-rewriter.h"
|
2015-12-07 14:26:25 +00:00
|
|
|
#include "src/parsing/parser-base.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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-12 11:46:12 +00:00
|
|
|
ParseInfo::ParseInfo(Zone* zone)
|
|
|
|
: zone_(zone),
|
|
|
|
flags_(0),
|
|
|
|
source_stream_(nullptr),
|
|
|
|
source_stream_encoding_(ScriptCompiler::StreamedSource::ONE_BYTE),
|
|
|
|
extension_(nullptr),
|
|
|
|
compile_options_(ScriptCompiler::kNoCompileOptions),
|
|
|
|
script_scope_(nullptr),
|
|
|
|
unicode_cache_(nullptr),
|
|
|
|
stack_limit_(0),
|
|
|
|
hash_seed_(0),
|
|
|
|
cached_data_(nullptr),
|
|
|
|
ast_value_factory_(nullptr),
|
|
|
|
literal_(nullptr),
|
|
|
|
scope_(nullptr) {}
|
|
|
|
|
|
|
|
|
|
|
|
ParseInfo::ParseInfo(Zone* zone, Handle<JSFunction> function)
|
|
|
|
: ParseInfo(zone, Handle<SharedFunctionInfo>(function->shared())) {
|
2015-03-09 14:51:13 +00:00
|
|
|
set_closure(function);
|
|
|
|
set_context(Handle<Context>(function->context()));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-12 11:46:12 +00:00
|
|
|
ParseInfo::ParseInfo(Zone* zone, Handle<SharedFunctionInfo> shared)
|
|
|
|
: ParseInfo(zone) {
|
2015-03-09 14:51:13 +00:00
|
|
|
isolate_ = shared->GetIsolate();
|
|
|
|
|
|
|
|
set_lazy();
|
|
|
|
set_hash_seed(isolate_->heap()->HashSeed());
|
|
|
|
set_stack_limit(isolate_->stack_guard()->real_climit());
|
|
|
|
set_unicode_cache(isolate_->unicode_cache());
|
|
|
|
set_language_mode(shared->language_mode());
|
|
|
|
set_shared_info(shared);
|
|
|
|
|
|
|
|
Handle<Script> script(Script::cast(shared->script()));
|
|
|
|
set_script(script);
|
2015-09-28 13:10:13 +00:00
|
|
|
if (!script.is_null() && script->type() == Script::TYPE_NATIVE) {
|
2015-03-09 14:51:13 +00:00
|
|
|
set_native();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-12 11:46:12 +00:00
|
|
|
ParseInfo::ParseInfo(Zone* zone, Handle<Script> script) : ParseInfo(zone) {
|
2015-03-09 14:51:13 +00:00
|
|
|
isolate_ = script->GetIsolate();
|
|
|
|
|
|
|
|
set_hash_seed(isolate_->heap()->HashSeed());
|
|
|
|
set_stack_limit(isolate_->stack_guard()->real_climit());
|
|
|
|
set_unicode_cache(isolate_->unicode_cache());
|
|
|
|
set_script(script);
|
|
|
|
|
2015-09-28 13:10:13 +00:00
|
|
|
if (script->type() == Script::TYPE_NATIVE) {
|
2015-03-09 14:51:13 +00:00
|
|
|
set_native();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-09 14:51:13 +00:00
|
|
|
void Parser::SetCachedData(ParseInfo* info) {
|
2015-02-12 13:02:30 +00:00
|
|
|
if (compile_options_ == ScriptCompiler::kNoCompileOptions) {
|
2014-07-10 10:28:05 +00:00
|
|
|
cached_parse_data_ = NULL;
|
|
|
|
} else {
|
2015-02-12 13:02:30 +00:00
|
|
|
DCHECK(info->cached_data() != NULL);
|
|
|
|
if (compile_options_ == ScriptCompiler::kConsumeParserCache) {
|
|
|
|
cached_parse_data_ = ParseData::FromCachedData(*info->cached_data());
|
2014-07-10 10:28:05 +00:00
|
|
|
}
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2016-02-01 17:44:23 +00:00
|
|
|
FunctionLiteral* Parser::DefaultConstructor(const AstRawString* name,
|
|
|
|
bool call_super, Scope* scope,
|
2015-07-15 09:14:49 +00:00
|
|
|
int pos, int end_pos,
|
|
|
|
LanguageMode language_mode) {
|
2014-11-07 16:39:00 +00:00
|
|
|
int materialized_literal_count = -1;
|
|
|
|
int expected_property_count = -1;
|
|
|
|
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;
|
2015-02-11 17:22:50 +00:00
|
|
|
Scope* function_scope = NewScope(scope, FUNCTION_SCOPE, 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;
|
|
|
|
|
|
|
|
{
|
2014-11-14 13:13:09 +00:00
|
|
|
AstNodeFactory function_factory(ast_value_factory());
|
2014-11-07 16:39:00 +00:00
|
|
|
FunctionState function_state(&function_state_, &scope_, function_scope,
|
2015-02-11 17:22:50 +00:00
|
|
|
kind, &function_factory);
|
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>)
|
|
|
|
// %reflect_construct($super_constructor, arguments, new.target)
|
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());
|
|
|
|
VariableProxy* this_function_proxy = scope_->NewUnresolved(
|
|
|
|
factory(), ast_value_factory()->this_function_string(),
|
|
|
|
Variable::NORMAL, 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());
|
2015-12-11 16:38:44 +00:00
|
|
|
VariableProxy* arguments_proxy = scope_->NewUnresolved(
|
|
|
|
factory(), ast_value_factory()->arguments_string(), Variable::NORMAL,
|
|
|
|
pos);
|
|
|
|
args->Add(arguments_proxy, zone());
|
|
|
|
VariableProxy* new_target_proxy = scope_->NewUnresolved(
|
|
|
|
factory(), ast_value_factory()->new_target_string(), Variable::NORMAL,
|
|
|
|
pos);
|
|
|
|
args->Add(new_target_proxy, zone());
|
2015-02-12 20:06:52 +00:00
|
|
|
CallRuntime* call = factory()->NewCallRuntime(
|
2015-12-11 16:38:44 +00:00
|
|
|
Context::REFLECT_CONSTRUCT_INDEX, args, pos);
|
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,
|
|
|
|
expected_property_count, parameter_count,
|
2015-06-08 18:19:32 +00:00
|
|
|
FunctionLiteral::kNoDuplicateParameters,
|
2016-01-08 20:38:07 +00:00
|
|
|
FunctionLiteral::kAnonymousExpression,
|
2015-04-24 11:07:50 +00:00
|
|
|
FunctionLiteral::kShouldLazyCompile, kind, pos);
|
2014-11-07 16:39:00 +00:00
|
|
|
|
|
|
|
return function_literal;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Target is a support class to facilitate manipulation of the
|
|
|
|
// Parser's target_stack_ (the stack of potential 'break' and
|
|
|
|
// 'continue' statement targets). Upon construction, a new target is
|
|
|
|
// added; it is removed upon destruction.
|
|
|
|
|
|
|
|
class Target BASE_EMBEDDED {
|
|
|
|
public:
|
2015-01-15 19:18:05 +00:00
|
|
|
Target(Target** variable, BreakableStatement* statement)
|
|
|
|
: variable_(variable), statement_(statement), previous_(*variable) {
|
2010-10-27 12:33:48 +00:00
|
|
|
*variable = this;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
~Target() {
|
2010-10-27 12:33:48 +00:00
|
|
|
*variable_ = previous_;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2009-05-15 14:58:02 +00:00
|
|
|
Target* previous() { return previous_; }
|
2015-01-15 19:18:05 +00:00
|
|
|
BreakableStatement* statement() { return statement_; }
|
2009-05-15 14:58:02 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
private:
|
2010-10-27 12:33:48 +00:00
|
|
|
Target** variable_;
|
2015-01-15 19:18:05 +00:00
|
|
|
BreakableStatement* statement_;
|
2009-05-15 14:58:02 +00:00
|
|
|
Target* previous_;
|
2008-07-03 15:10:15 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
class TargetScope BASE_EMBEDDED {
|
|
|
|
public:
|
2010-10-27 12:33:48 +00:00
|
|
|
explicit TargetScope(Target** variable)
|
|
|
|
: variable_(variable), previous_(*variable) {
|
|
|
|
*variable = NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
~TargetScope() {
|
2010-10-27 12:33:48 +00:00
|
|
|
*variable_ = previous_;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2010-10-27 12:33:48 +00:00
|
|
|
Target** variable_;
|
2009-05-15 14:58:02 +00:00
|
|
|
Target* previous_;
|
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)!
|
|
|
|
|
|
|
|
#define CHECK_OK ok); \
|
|
|
|
if (!*ok) return NULL; \
|
|
|
|
((void)0
|
|
|
|
#define DUMMY ) // to make indentation work
|
|
|
|
#undef DUMMY
|
|
|
|
|
2008-12-01 15:42:35 +00:00
|
|
|
#define CHECK_FAILED /**/); \
|
2008-12-01 15:32:20 +00:00
|
|
|
if (failed_) return NULL; \
|
|
|
|
((void)0
|
|
|
|
#define DUMMY ) // to make indentation work
|
|
|
|
#undef DUMMY
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Implementation of Parser
|
|
|
|
|
2015-02-19 14:58:32 +00:00
|
|
|
bool ParserTraits::IsEval(const AstRawString* identifier) const {
|
|
|
|
return identifier == parser_->ast_value_factory()->eval_string();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ParserTraits::IsArguments(const AstRawString* identifier) const {
|
|
|
|
return identifier == parser_->ast_value_factory()->arguments_string();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
bool ParserTraits::IsEvalOrArguments(const AstRawString* identifier) const {
|
2015-02-19 14:58:32 +00:00
|
|
|
return IsEval(identifier) || IsArguments(identifier);
|
2014-02-11 09:35:32 +00:00
|
|
|
}
|
|
|
|
|
2015-04-10 12:04:51 +00:00
|
|
|
bool ParserTraits::IsUndefined(const AstRawString* identifier) const {
|
|
|
|
return identifier == parser_->ast_value_factory()->undefined_string();
|
|
|
|
}
|
2014-02-11 09:35:32 +00:00
|
|
|
|
2014-09-16 22:15:39 +00:00
|
|
|
bool ParserTraits::IsPrototype(const AstRawString* identifier) const {
|
|
|
|
return identifier == parser_->ast_value_factory()->prototype_string();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool ParserTraits::IsConstructor(const AstRawString* identifier) const {
|
|
|
|
return identifier == parser_->ast_value_factory()->constructor_string();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-14 09:43:04 +00:00
|
|
|
bool ParserTraits::IsThisProperty(Expression* expression) {
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(expression != NULL);
|
2014-03-14 09:43:04 +00:00
|
|
|
Property* property = expression->AsProperty();
|
2015-01-15 20:02:20 +00:00
|
|
|
return property != NULL && property->obj()->IsVariableProxy() &&
|
|
|
|
property->obj()->AsVariableProxy()->is_this();
|
2014-03-14 09:43:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-19 14:08:47 +00:00
|
|
|
bool ParserTraits::IsIdentifier(Expression* expression) {
|
|
|
|
VariableProxy* operand = expression->AsVariableProxy();
|
|
|
|
return operand != NULL && !operand->is_this();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-21 10:34:51 +00:00
|
|
|
void ParserTraits::PushPropertyName(FuncNameInferrer* fni,
|
|
|
|
Expression* expression) {
|
|
|
|
if (expression->IsPropertyName()) {
|
2014-06-24 14:03:24 +00:00
|
|
|
fni->PushLiteralName(expression->AsLiteral()->AsRawPropertyName());
|
2014-03-21 10:34:51 +00:00
|
|
|
} else {
|
|
|
|
fni->PushLiteralName(
|
2014-09-11 09:52:36 +00:00
|
|
|
parser_->ast_value_factory()->anonymous_function_string());
|
2014-03-21 10:34:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-14 09:43:04 +00:00
|
|
|
void ParserTraits::CheckAssigningFunctionLiteralToProperty(Expression* left,
|
|
|
|
Expression* right) {
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(left != NULL);
|
2015-01-15 20:02:20 +00:00
|
|
|
if (left->IsProperty() && right->IsFunctionLiteral()) {
|
2014-03-14 09:43:04 +00:00
|
|
|
right->AsFunctionLiteral()->set_pretenure();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-26 11:59:42 +00:00
|
|
|
Expression* ParserTraits::MarkExpressionAsAssigned(Expression* expression) {
|
|
|
|
VariableProxy* proxy =
|
|
|
|
expression != NULL ? expression->AsVariableProxy() : NULL;
|
|
|
|
if (proxy != NULL) proxy->set_is_assigned();
|
2014-03-14 09:43:04 +00:00
|
|
|
return expression;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-17 13:54:42 +00:00
|
|
|
bool ParserTraits::ShortcutNumericLiteralBinaryExpression(
|
|
|
|
Expression** x, Expression* y, Token::Value op, int pos,
|
2014-11-14 13:13:09 +00:00
|
|
|
AstNodeFactory* factory) {
|
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:
|
2015-12-03 18:14:08 +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:
|
2015-12-03 18:14:08 +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:
|
2015-12-03 18:14:08 +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:
|
2015-12-03 18:14:08 +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);
|
2015-12-03 18:14:08 +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);
|
2015-12-03 18:14:08 +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);
|
2015-12-03 18:14:08 +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);
|
2015-12-03 18:14:08 +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;
|
2015-12-03 18:14:08 +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);
|
2015-12-03 18:14:08 +00:00
|
|
|
*x = factory->NewNumberLiteral(value, pos, has_dot);
|
2014-03-17 13:54:42 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-14 13:13:09 +00:00
|
|
|
Expression* ParserTraits::BuildUnaryExpression(Expression* expression,
|
|
|
|
Token::Value op, int pos,
|
|
|
|
AstNodeFactory* factory) {
|
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();
|
2014-06-24 14:03:24 +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:
|
2015-12-03 18:14:08 +00:00
|
|
|
return factory->NewNumberLiteral(-value, pos, has_dot);
|
2014-03-19 14:08:47 +00:00
|
|
|
case Token::BIT_NOT:
|
2015-12-03 18:14:08 +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) {
|
|
|
|
return factory->NewBinaryOperation(
|
2015-08-31 16:35:51 +00:00
|
|
|
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) {
|
|
|
|
return factory->NewBinaryOperation(
|
|
|
|
Token::MUL, expression, factory->NewNumberLiteral(-1, pos), pos);
|
|
|
|
}
|
|
|
|
// ...and one more time for '~foo' => 'foo^(~0)'.
|
|
|
|
if (op == Token::BIT_NOT) {
|
|
|
|
return factory->NewBinaryOperation(
|
|
|
|
Token::BIT_XOR, expression, factory->NewNumberLiteral(~0, pos), pos);
|
|
|
|
}
|
|
|
|
return factory->NewUnaryOperation(op, expression, pos);
|
|
|
|
}
|
|
|
|
|
2016-03-06 09:19:14 +00:00
|
|
|
Expression* ParserTraits::BuildIteratorResult(Expression* value, bool done) {
|
|
|
|
int pos = RelocInfo::kNoPosition;
|
|
|
|
AstNodeFactory* factory = parser_->factory();
|
|
|
|
Zone* zone = parser_->zone();
|
|
|
|
|
|
|
|
if (value == nullptr) value = factory->NewUndefinedLiteral(pos);
|
|
|
|
|
|
|
|
auto args = new (zone) ZoneList<Expression*>(2, zone);
|
|
|
|
args->Add(value, zone);
|
|
|
|
args->Add(factory->NewBooleanLiteral(done, pos), zone);
|
|
|
|
|
|
|
|
return factory->NewCallRuntime(Runtime::kInlineCreateIterResultObject, args,
|
|
|
|
pos);
|
|
|
|
}
|
2014-03-19 14:08:47 +00:00
|
|
|
|
2015-05-18 08:34:05 +00:00
|
|
|
Expression* ParserTraits::NewThrowReferenceError(
|
|
|
|
MessageTemplate::Template message, int pos) {
|
2015-06-24 16:54:47 +00:00
|
|
|
return NewThrowError(Runtime::kNewReferenceError, message,
|
|
|
|
parser_->ast_value_factory()->empty_string(), pos);
|
2014-04-02 11:03:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-05-18 08:34:05 +00:00
|
|
|
Expression* ParserTraits::NewThrowSyntaxError(MessageTemplate::Template message,
|
|
|
|
const AstRawString* arg,
|
|
|
|
int pos) {
|
2015-06-24 16:54:47 +00:00
|
|
|
return NewThrowError(Runtime::kNewSyntaxError, message, arg, pos);
|
2014-04-02 11:03:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-05-18 08:34:05 +00:00
|
|
|
Expression* ParserTraits::NewThrowTypeError(MessageTemplate::Template message,
|
|
|
|
const AstRawString* arg, int pos) {
|
2015-06-24 16:54:47 +00:00
|
|
|
return NewThrowError(Runtime::kNewTypeError, message, arg, pos);
|
2014-04-02 11:03:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-24 16:54:47 +00:00
|
|
|
Expression* ParserTraits::NewThrowError(Runtime::FunctionId id,
|
2015-05-18 08:34:05 +00:00
|
|
|
MessageTemplate::Template message,
|
|
|
|
const AstRawString* arg, int pos) {
|
2014-04-02 11:03:05 +00:00
|
|
|
Zone* zone = parser_->zone();
|
2014-06-24 14:03:24 +00:00
|
|
|
ZoneList<Expression*>* args = new (zone) ZoneList<Expression*>(2, zone);
|
2015-05-18 08:34:05 +00:00
|
|
|
args->Add(parser_->factory()->NewSmiLiteral(message, pos), zone);
|
2014-10-30 14:21:27 +00:00
|
|
|
args->Add(parser_->factory()->NewStringLiteral(arg, pos), zone);
|
2015-08-26 11:16:38 +00:00
|
|
|
CallRuntime* call_constructor =
|
|
|
|
parser_->factory()->NewCallRuntime(id, args, pos);
|
2014-04-02 11:03:05 +00:00
|
|
|
return parser_->factory()->NewThrow(call_constructor, pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-02-11 09:35:32 +00:00
|
|
|
void ParserTraits::ReportMessageAt(Scanner::Location source_location,
|
2015-05-18 08:34:05 +00:00
|
|
|
MessageTemplate::Template message,
|
|
|
|
const char* arg, ParseErrorType error_type) {
|
2014-03-12 13:27:32 +00:00
|
|
|
if (parser_->stack_overflow()) {
|
|
|
|
// Suppress the error message (syntax error or such) in the presence of a
|
|
|
|
// stack overflow. The isolate allows only one pending exception at at time
|
|
|
|
// and we want to report the stack overflow later.
|
|
|
|
return;
|
|
|
|
}
|
2015-02-25 14:17:39 +00:00
|
|
|
parser_->pending_error_handler_.ReportMessageAt(source_location.beg_pos,
|
|
|
|
source_location.end_pos,
|
|
|
|
message, arg, error_type);
|
2014-02-11 09:35:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-05-18 08:34:05 +00:00
|
|
|
void ParserTraits::ReportMessage(MessageTemplate::Template message,
|
|
|
|
const char* arg, ParseErrorType error_type) {
|
2014-06-24 14:03:24 +00:00
|
|
|
Scanner::Location source_location = parser_->scanner()->location();
|
2015-02-20 21:19:43 +00:00
|
|
|
ReportMessageAt(source_location, message, arg, error_type);
|
2014-06-24 14:03:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-05-18 08:34:05 +00:00
|
|
|
void ParserTraits::ReportMessage(MessageTemplate::Template message,
|
|
|
|
const AstRawString* arg,
|
2015-02-20 21:19:43 +00:00
|
|
|
ParseErrorType error_type) {
|
2014-02-14 12:13:33 +00:00
|
|
|
Scanner::Location source_location = parser_->scanner()->location();
|
2015-02-20 21:19:43 +00:00
|
|
|
ReportMessageAt(source_location, message, arg, error_type);
|
2014-02-11 09:35:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void ParserTraits::ReportMessageAt(Scanner::Location source_location,
|
2015-05-18 08:34:05 +00:00
|
|
|
MessageTemplate::Template message,
|
|
|
|
const AstRawString* arg,
|
2015-02-20 21:19:43 +00:00
|
|
|
ParseErrorType error_type) {
|
2014-03-12 13:27:32 +00:00
|
|
|
if (parser_->stack_overflow()) {
|
|
|
|
// Suppress the error message (syntax error or such) in the presence of a
|
|
|
|
// stack overflow. The isolate allows only one pending exception at at time
|
|
|
|
// and we want to report the stack overflow later.
|
|
|
|
return;
|
|
|
|
}
|
2015-02-25 14:17:39 +00:00
|
|
|
parser_->pending_error_handler_.ReportMessageAt(source_location.beg_pos,
|
|
|
|
source_location.end_pos,
|
|
|
|
message, arg, error_type);
|
2014-02-11 09:35:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* ParserTraits::GetSymbol(Scanner* scanner) {
|
|
|
|
const AstRawString* result =
|
2014-09-11 09:52:36 +00:00
|
|
|
parser_->scanner()->CurrentSymbol(parser_->ast_value_factory());
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(result != NULL);
|
2014-03-25 09:09:24 +00:00
|
|
|
return result;
|
2014-02-11 09:35:32 +00:00
|
|
|
}
|
|
|
|
|
2014-02-11 11:51:01 +00:00
|
|
|
|
2014-08-22 14:40:38 +00:00
|
|
|
const AstRawString* ParserTraits::GetNumberAsSymbol(Scanner* scanner) {
|
|
|
|
double double_value = parser_->scanner()->DoubleValue();
|
|
|
|
char array[100];
|
|
|
|
const char* string =
|
2014-08-26 09:19:24 +00:00
|
|
|
DoubleToCString(double_value, Vector<char>(array, arraysize(array)));
|
2015-02-06 15:58:36 +00:00
|
|
|
return parser_->ast_value_factory()->GetOneByteString(string);
|
2014-08-22 14:40:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* ParserTraits::GetNextSymbol(Scanner* scanner) {
|
2014-09-11 09:52:36 +00:00
|
|
|
return parser_->scanner()->NextSymbol(parser_->ast_value_factory());
|
2014-02-11 11:51:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-14 13:13:09 +00:00
|
|
|
Expression* ParserTraits::ThisExpression(Scope* scope, AstNodeFactory* factory,
|
|
|
|
int pos) {
|
2015-05-19 14:51:10 +00:00
|
|
|
return scope->NewUnresolved(factory,
|
|
|
|
parser_->ast_value_factory()->this_string(),
|
|
|
|
Variable::THIS, pos, pos + 4);
|
2014-02-14 11:24:26 +00:00
|
|
|
}
|
|
|
|
|
2015-06-09 15:43:07 +00:00
|
|
|
|
2015-06-02 22:04:25 +00:00
|
|
|
Expression* ParserTraits::SuperPropertyReference(Scope* scope,
|
|
|
|
AstNodeFactory* factory,
|
|
|
|
int pos) {
|
2015-06-04 16:22:29 +00:00
|
|
|
// this_function[home_object_symbol]
|
|
|
|
VariableProxy* this_function_proxy = scope->NewUnresolved(
|
|
|
|
factory, parser_->ast_value_factory()->this_function_string(),
|
2015-05-26 20:29:47 +00:00
|
|
|
Variable::NORMAL, pos);
|
2015-06-04 16:22:29 +00:00
|
|
|
Expression* home_object_symbol_literal =
|
|
|
|
factory->NewSymbolLiteral("home_object_symbol", RelocInfo::kNoPosition);
|
|
|
|
Expression* home_object = factory->NewProperty(
|
|
|
|
this_function_proxy, home_object_symbol_literal, pos);
|
2015-06-02 22:04:25 +00:00
|
|
|
return factory->NewSuperPropertyReference(
|
2015-06-04 16:22:29 +00:00
|
|
|
ThisExpression(scope, factory, pos)->AsVariableProxy(), home_object, pos);
|
2014-08-18 12:35:34 +00:00
|
|
|
}
|
2014-02-14 11:24:26 +00:00
|
|
|
|
2014-09-18 17:39:49 +00:00
|
|
|
|
2015-06-02 22:04:25 +00:00
|
|
|
Expression* ParserTraits::SuperCallReference(Scope* scope,
|
|
|
|
AstNodeFactory* factory, int pos) {
|
|
|
|
VariableProxy* new_target_proxy = scope->NewUnresolved(
|
|
|
|
factory, parser_->ast_value_factory()->new_target_string(),
|
|
|
|
Variable::NORMAL, pos);
|
|
|
|
VariableProxy* this_function_proxy = scope->NewUnresolved(
|
|
|
|
factory, parser_->ast_value_factory()->this_function_string(),
|
|
|
|
Variable::NORMAL, pos);
|
|
|
|
return factory->NewSuperCallReference(
|
|
|
|
ThisExpression(scope, factory, pos)->AsVariableProxy(), new_target_proxy,
|
|
|
|
this_function_proxy, pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-09 15:43:07 +00:00
|
|
|
Expression* ParserTraits::NewTargetExpression(Scope* scope,
|
|
|
|
AstNodeFactory* factory,
|
|
|
|
int pos) {
|
|
|
|
static const int kNewTargetStringLength = 10;
|
2015-08-13 18:06:04 +00:00
|
|
|
auto proxy = scope->NewUnresolved(
|
2015-06-09 15:43:07 +00:00
|
|
|
factory, parser_->ast_value_factory()->new_target_string(),
|
|
|
|
Variable::NORMAL, 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-01-28 08:54:29 +00:00
|
|
|
Expression* ParserTraits::FunctionSentExpression(Scope* scope,
|
|
|
|
AstNodeFactory* factory,
|
|
|
|
int pos) {
|
|
|
|
// We desugar function.sent into %GeneratorGetInput(generator).
|
|
|
|
Zone* zone = parser_->zone();
|
|
|
|
ZoneList<Expression*>* args = new (zone) ZoneList<Expression*>(1, zone);
|
|
|
|
VariableProxy* generator = factory->NewVariableProxy(
|
|
|
|
parser_->function_state_->generator_object_variable());
|
|
|
|
args->Add(generator, zone);
|
|
|
|
return factory->NewCallRuntime(Runtime::kGeneratorGetInput, args, pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-14 13:13:09 +00:00
|
|
|
Literal* ParserTraits::ExpressionFromLiteral(Token::Value token, int pos,
|
|
|
|
Scanner* scanner,
|
|
|
|
AstNodeFactory* factory) {
|
2014-02-14 11:24:26 +00:00
|
|
|
switch (token) {
|
|
|
|
case Token::NULL_LITERAL:
|
2014-06-24 14:03:24 +00:00
|
|
|
return factory->NewNullLiteral(pos);
|
2014-02-14 11:24:26 +00:00
|
|
|
case Token::TRUE_LITERAL:
|
2014-06-24 14:03:24 +00:00
|
|
|
return factory->NewBooleanLiteral(true, pos);
|
2014-02-14 11:24:26 +00:00
|
|
|
case Token::FALSE_LITERAL:
|
2014-06-24 14:03:24 +00:00
|
|
|
return factory->NewBooleanLiteral(false, pos);
|
2015-03-03 11:04:49 +00:00
|
|
|
case Token::SMI: {
|
|
|
|
int value = scanner->smi_value();
|
|
|
|
return factory->NewSmiLiteral(value, pos);
|
|
|
|
}
|
2014-02-14 11:24:26 +00:00
|
|
|
case Token::NUMBER: {
|
2015-06-30 21:12:12 +00:00
|
|
|
bool has_dot = scanner->ContainsDot();
|
2014-03-12 14:03:25 +00:00
|
|
|
double value = scanner->DoubleValue();
|
2015-06-30 21:12:12 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-14 13:13:09 +00:00
|
|
|
Expression* ParserTraits::ExpressionFromIdentifier(const AstRawString* name,
|
2015-02-26 13:48:10 +00:00
|
|
|
int start_position,
|
|
|
|
int end_position,
|
|
|
|
Scope* scope,
|
2014-11-14 13:13:09 +00:00
|
|
|
AstNodeFactory* factory) {
|
2014-02-14 11:24:26 +00:00
|
|
|
if (parser_->fni_ != NULL) parser_->fni_->PushVariableName(name);
|
2015-05-19 14:51:10 +00:00
|
|
|
return scope->NewUnresolved(factory, name, Variable::NORMAL, start_position,
|
|
|
|
end_position);
|
2014-02-14 11:24:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-14 13:13:09 +00:00
|
|
|
Expression* ParserTraits::ExpressionFromString(int pos, Scanner* scanner,
|
|
|
|
AstNodeFactory* factory) {
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* symbol = GetSymbol(scanner);
|
2014-02-14 11:24:26 +00:00
|
|
|
if (parser_->fni_ != NULL) parser_->fni_->PushLiteralName(symbol);
|
2014-06-24 14:03:24 +00:00
|
|
|
return factory->NewStringLiteral(symbol, pos);
|
2014-02-14 11:24:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-14 13:13:09 +00:00
|
|
|
Expression* ParserTraits::GetIterator(Expression* iterable,
|
2015-12-02 18:36:39 +00:00
|
|
|
AstNodeFactory* factory, int pos) {
|
2014-08-05 13:17:49 +00:00
|
|
|
Expression* iterator_symbol_literal =
|
2014-11-13 08:47:52 +00:00
|
|
|
factory->NewSymbolLiteral("iterator_symbol", RelocInfo::kNoPosition);
|
2014-08-05 13:17:49 +00:00
|
|
|
Expression* prop =
|
|
|
|
factory->NewProperty(iterable, iterator_symbol_literal, pos);
|
|
|
|
Zone* zone = parser_->zone();
|
|
|
|
ZoneList<Expression*>* args = new (zone) ZoneList<Expression*>(0, zone);
|
|
|
|
return factory->NewCall(prop, args, pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-14 13:13:09 +00:00
|
|
|
Literal* ParserTraits::GetLiteralTheHole(int position,
|
|
|
|
AstNodeFactory* factory) {
|
2014-06-24 14:03:24 +00:00
|
|
|
return factory->NewTheHoleLiteral(RelocInfo::kNoPosition);
|
2014-02-14 11:24:26 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Expression* ParserTraits::ParseV8Intrinsic(bool* ok) {
|
|
|
|
return parser_->ParseV8Intrinsic(ok);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-03-11 15:40:41 +00:00
|
|
|
FunctionLiteral* ParserTraits::ParseFunctionLiteral(
|
2014-09-10 16:39:42 +00:00
|
|
|
const AstRawString* name, Scanner::Location function_name_location,
|
2015-07-09 21:31:11 +00:00
|
|
|
FunctionNameValidity function_name_validity, FunctionKind kind,
|
2014-09-10 16:39:42 +00:00
|
|
|
int function_token_position, FunctionLiteral::FunctionType type,
|
2015-07-15 09:14:49 +00:00
|
|
|
LanguageMode language_mode, bool* ok) {
|
2014-09-10 16:39:42 +00:00
|
|
|
return parser_->ParseFunctionLiteral(
|
2015-07-09 21:31:11 +00:00
|
|
|
name, function_name_location, function_name_validity, kind,
|
2016-02-19 02:50:58 +00:00
|
|
|
function_token_position, type, language_mode, ok);
|
2014-03-11 15:40:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-14 15:05:05 +00:00
|
|
|
ClassLiteral* ParserTraits::ParseClassLiteral(
|
|
|
|
const AstRawString* name, Scanner::Location class_name_location,
|
|
|
|
bool name_is_strict_reserved, int pos, bool* ok) {
|
|
|
|
return parser_->ParseClassLiteral(name, class_name_location,
|
|
|
|
name_is_strict_reserved, pos, ok);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-09 14:51:13 +00:00
|
|
|
Parser::Parser(ParseInfo* info)
|
|
|
|
: ParserBase<ParserTraits>(info->zone(), &scanner_, info->stack_limit(),
|
2015-02-12 13:02:30 +00:00
|
|
|
info->extension(), info->ast_value_factory(),
|
|
|
|
NULL, this),
|
2015-03-09 14:51:13 +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()),
|
2014-07-10 10:28:05 +00:00
|
|
|
cached_parse_data_(NULL),
|
2014-09-02 11:36:21 +00:00
|
|
|
total_preparse_skipped_(0),
|
2015-02-12 13:02:30 +00:00
|
|
|
pre_parse_timer_(NULL),
|
|
|
|
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.
|
2015-02-12 13:02:30 +00:00
|
|
|
DCHECK(!info->script().is_null() || info->source_stream() != NULL);
|
2015-03-09 14:51:13 +00:00
|
|
|
set_allow_lazy(info->allow_lazy_parsing());
|
2014-11-20 10:51:49 +00:00
|
|
|
set_allow_natives(FLAG_allow_natives_syntax || info->is_native());
|
|
|
|
set_allow_harmony_sloppy(FLAG_harmony_sloppy);
|
2015-08-11 23:59:47 +00:00
|
|
|
set_allow_harmony_sloppy_function(FLAG_harmony_sloppy_function);
|
2015-07-25 00:05:08 +00:00
|
|
|
set_allow_harmony_sloppy_let(FLAG_harmony_sloppy_let);
|
2015-07-07 21:57:09 +00:00
|
|
|
set_allow_legacy_const(FLAG_legacy_const);
|
2015-10-21 02:55:47 +00:00
|
|
|
set_allow_harmony_do_expressions(FLAG_harmony_do_expressions);
|
2015-12-12 00:20:01 +00:00
|
|
|
set_allow_harmony_function_name(FLAG_harmony_function_name);
|
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);
|
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();
|
2014-09-02 11:36:21 +00:00
|
|
|
}
|
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_);
|
|
|
|
|
|
|
|
HistogramTimerScope timer_scope(isolate->counters()->parse(), true);
|
2016-02-18 06:12:45 +00:00
|
|
|
TRACE_EVENT0("v8", "V8.Parse");
|
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
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2010-12-07 14:03:59 +00:00
|
|
|
if (source->IsExternalTwoByteString()) {
|
|
|
|
// Notice that the stream is destroyed at the end of the branch block.
|
|
|
|
// The last line of the blocks can't be moved outside, even though they're
|
|
|
|
// identical calls.
|
2012-03-12 12:35:28 +00:00
|
|
|
ExternalTwoByteStringUtf16CharacterStream stream(
|
2010-12-07 14:03:59 +00:00
|
|
|
Handle<ExternalTwoByteString>::cast(source), 0, source->length());
|
2010-12-22 20:14:19 +00:00
|
|
|
scanner_.Initialize(&stream);
|
2015-04-16 12:42:43 +00:00
|
|
|
result = DoParseProgram(info);
|
2010-12-07 14:03:59 +00:00
|
|
|
} else {
|
2012-03-12 12:35:28 +00:00
|
|
|
GenericStringUtf16CharacterStream stream(source, 0, source->length());
|
2010-12-22 20:14:19 +00:00
|
|
|
scanner_.Initialize(&stream);
|
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());
|
2015-07-13 12:38:06 +00:00
|
|
|
base::SmartArrayPointer<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.
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(scope_ == NULL);
|
|
|
|
DCHECK(target_stack_ == NULL);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2015-04-27 12:13:45 +00:00
|
|
|
Mode parsing_mode = FLAG_lazy && allow_lazy() ? PARSE_LAZILY : PARSE_EAGERLY;
|
|
|
|
if (allow_natives() || extension_ != NULL) parsing_mode = PARSE_EAGERLY;
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
FunctionLiteral* result = NULL;
|
2014-09-12 09:12:08 +00:00
|
|
|
{
|
2015-07-15 10:59:52 +00:00
|
|
|
// TODO(wingo): Add an outer SCRIPT_SCOPE corresponding to the native
|
2015-05-19 14:51:10 +00:00
|
|
|
// context, which will have the "this" binding for script scopes.
|
2015-04-16 12:42:43 +00:00
|
|
|
Scope* scope = NewScope(scope_, SCRIPT_SCOPE);
|
|
|
|
info->set_script_scope(scope);
|
2014-07-09 14:50:23 +00:00
|
|
|
if (!info->context().is_null() && !info->context()->IsNativeContext()) {
|
2015-04-16 12:42:43 +00:00
|
|
|
scope = Scope::DeserializeScopeChain(info->isolate(), zone(),
|
|
|
|
*info->context(), scope);
|
2014-06-24 14:03:24 +00:00
|
|
|
// The Scope is backed up by ScopeInfo (which is in the V8 heap); this
|
|
|
|
// means the Parser cannot operate independent of the V8 heap. Tell the
|
|
|
|
// string table to internalize strings and values right after they're
|
2015-02-12 13:02:30 +00:00
|
|
|
// created. This kind of parsing can only be done in the main thread.
|
|
|
|
DCHECK(parsing_on_main_thread_);
|
|
|
|
ast_value_factory()->Internalize(info->isolate());
|
2012-08-28 11:25:08 +00:00
|
|
|
}
|
2015-04-16 12:42:43 +00:00
|
|
|
original_scope_ = scope;
|
2012-03-15 13:02:21 +00:00
|
|
|
if (info->is_eval()) {
|
2015-04-24 05:55:39 +00:00
|
|
|
if (!scope->is_script_scope() || is_strict(info->language_mode())) {
|
2015-04-27 12:13:45 +00:00
|
|
|
parsing_mode = PARSE_EAGERLY;
|
2015-04-24 05:55:39 +00:00
|
|
|
}
|
2015-04-27 12:13:45 +00:00
|
|
|
scope = NewScope(scope, EVAL_SCOPE);
|
2015-02-24 22:39:26 +00:00
|
|
|
} else if (info->is_module()) {
|
2015-04-16 12:42:43 +00:00
|
|
|
scope = NewScope(scope, MODULE_SCOPE);
|
2011-11-15 13:48:40 +00:00
|
|
|
}
|
2015-04-16 12:42:43 +00:00
|
|
|
|
|
|
|
scope->set_start_position(0);
|
2012-08-28 11:25:08 +00:00
|
|
|
|
2015-04-27 12:13:45 +00:00
|
|
|
// Enter 'scope' with the given parsing mode.
|
|
|
|
ParsingModeScope parsing_mode_scope(this, parsing_mode);
|
2014-11-14 13:13:09 +00:00
|
|
|
AstNodeFactory function_factory(ast_value_factory());
|
2015-04-16 12:42:43 +00:00
|
|
|
FunctionState function_state(&function_state_, &scope_, scope,
|
2015-02-06 10:34:50 +00:00
|
|
|
kNormalFunction, &function_factory);
|
2013-04-02 17:34:59 +00:00
|
|
|
|
2015-11-05 19:52:36 +00:00
|
|
|
// Don't count the mode in the use counters--give the program a chance
|
2016-03-10 12:43:51 +00:00
|
|
|
// to enable script/module-wide strict mode below.
|
2015-02-04 09:34:05 +00:00
|
|
|
scope_->SetLanguageMode(info->language_mode());
|
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;
|
2015-01-27 21:06:36 +00:00
|
|
|
if (info->is_module()) {
|
2015-02-25 23:00:10 +00:00
|
|
|
ParseModuleItemList(body, &ok);
|
2015-01-27 21:06:36 +00:00
|
|
|
} else {
|
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);
|
2015-07-10 16:39:47 +00:00
|
|
|
}
|
2015-09-30 23:48:26 +00:00
|
|
|
if (ok && is_sloppy(language_mode()) && allow_harmony_sloppy_function()) {
|
|
|
|
// 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.
|
|
|
|
InsertSloppyBlockFunctionVarBindings(scope, &ok);
|
|
|
|
}
|
2016-03-10 23:19:31 +00:00
|
|
|
if (ok) {
|
2014-02-12 12:02:07 +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) {
|
2015-12-04 17:20:10 +00:00
|
|
|
ParserTraits::RewriteDestructuringAssignments();
|
2016-02-19 02:50:58 +00:00
|
|
|
result = factory()->NewScriptOrEvalFunctionLiteral(
|
|
|
|
scope_, body, function_state.materialized_literal_count(),
|
|
|
|
function_state.expected_property_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;
|
|
|
|
}
|
|
|
|
|
2012-02-20 14:02:59 +00:00
|
|
|
|
2015-03-09 14:51:13 +00:00
|
|
|
FunctionLiteral* Parser::ParseLazy(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_);
|
2015-02-20 09:39:32 +00:00
|
|
|
HistogramTimerScope timer_scope(isolate->counters()->parse_lazy());
|
2016-02-18 06:12:45 +00:00
|
|
|
TRACE_EVENT0("v8", "V8.ParseLazy");
|
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();
|
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;
|
2010-12-07 14:03:59 +00:00
|
|
|
if (source->IsExternalTwoByteString()) {
|
2012-03-12 12:35:28 +00:00
|
|
|
ExternalTwoByteStringUtf16CharacterStream stream(
|
2010-12-07 14:03:59 +00:00
|
|
|
Handle<ExternalTwoByteString>::cast(source),
|
2011-03-09 16:57:03 +00:00
|
|
|
shared_info->start_position(),
|
|
|
|
shared_info->end_position());
|
2015-02-20 09:39:32 +00:00
|
|
|
result = ParseLazy(isolate, info, &stream);
|
2010-12-07 14:03:59 +00:00
|
|
|
} else {
|
2012-03-12 12:35:28 +00:00
|
|
|
GenericStringUtf16CharacterStream stream(source,
|
|
|
|
shared_info->start_position(),
|
|
|
|
shared_info->end_position());
|
2015-02-20 09:39:32 +00:00
|
|
|
result = ParseLazy(isolate, info, &stream);
|
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();
|
2015-07-13 12:38:06 +00:00
|
|
|
base::SmartArrayPointer<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-02-19 02:50:58 +00:00
|
|
|
static FunctionLiteral::FunctionType ComputeFunctionType(
|
|
|
|
Handle<SharedFunctionInfo> shared_info) {
|
|
|
|
if (shared_info->is_declaration()) {
|
|
|
|
return FunctionLiteral::kDeclaration;
|
|
|
|
} else if (shared_info->is_named_expression()) {
|
|
|
|
return FunctionLiteral::kNamedExpression;
|
|
|
|
} else if (IsConciseMethod(shared_info->kind()) ||
|
|
|
|
IsAccessorFunction(shared_info->kind())) {
|
|
|
|
return FunctionLiteral::kAccessorOrMethod;
|
|
|
|
}
|
|
|
|
return FunctionLiteral::kAnonymousExpression;
|
|
|
|
}
|
2010-12-07 14:03:59 +00:00
|
|
|
|
2015-03-09 14:51:13 +00:00
|
|
|
FunctionLiteral* Parser::ParseLazy(Isolate* isolate, ParseInfo* info,
|
2015-02-12 13:02:30 +00:00
|
|
|
Utf16CharacterStream* source) {
|
|
|
|
Handle<SharedFunctionInfo> shared_info = info->shared_info();
|
2010-12-22 20:14:19 +00:00
|
|
|
scanner_.Initialize(source);
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(scope_ == NULL);
|
|
|
|
DCHECK(target_stack_ == NULL);
|
2010-12-07 14:03:59 +00:00
|
|
|
|
2011-03-09 16:57:03 +00:00
|
|
|
Handle<String> name(String::cast(shared_info->name()));
|
2014-09-11 09:52:36 +00:00
|
|
|
DCHECK(ast_value_factory());
|
|
|
|
fni_ = new (zone()) FuncNameInferrer(ast_value_factory(), zone());
|
|
|
|
const AstRawString* raw_name = ast_value_factory()->GetString(name);
|
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.
|
|
|
|
FunctionLiteral* result = NULL;
|
|
|
|
|
|
|
|
{
|
|
|
|
// Parse the function literal.
|
2014-11-12 11:34:09 +00:00
|
|
|
Scope* scope = NewScope(scope_, SCRIPT_SCOPE);
|
2015-03-09 14:51:13 +00:00
|
|
|
info->set_script_scope(scope);
|
2015-02-12 13:02:30 +00:00
|
|
|
if (!info->closure().is_null()) {
|
|
|
|
// Ok to use Isolate here, since lazy function parsing is only done in the
|
|
|
|
// main thread.
|
|
|
|
DCHECK(parsing_on_main_thread_);
|
2015-02-20 09:39:32 +00:00
|
|
|
scope = Scope::DeserializeScopeChain(isolate, zone(),
|
2015-02-12 13:02:30 +00:00
|
|
|
info->closure()->context(), scope);
|
2011-03-09 16:57:03 +00:00
|
|
|
}
|
2013-08-23 09:25:37 +00:00
|
|
|
original_scope_ = scope;
|
2014-11-14 13:13:09 +00:00
|
|
|
AstNodeFactory function_factory(ast_value_factory());
|
2014-10-09 08:16:13 +00:00
|
|
|
FunctionState function_state(&function_state_, &scope_, scope,
|
2015-02-06 10:34:50 +00:00
|
|
|
shared_info->kind(), &function_factory);
|
2015-02-04 09:34:05 +00:00
|
|
|
DCHECK(is_sloppy(scope->language_mode()) ||
|
2015-02-12 13:02:30 +00:00
|
|
|
is_strict(info->language_mode()));
|
|
|
|
DCHECK(info->language_mode() == shared_info->language_mode());
|
2016-01-08 20:38:07 +00:00
|
|
|
FunctionLiteral::FunctionType function_type =
|
2016-02-19 02:50:58 +00:00
|
|
|
ComputeFunctionType(shared_info);
|
2008-07-03 15:10:15 +00:00
|
|
|
bool ok = true;
|
2014-07-21 09:58:01 +00:00
|
|
|
|
|
|
|
if (shared_info->is_arrow()) {
|
2016-01-14 19:20:56 +00:00
|
|
|
// TODO(adamk): We should construct this scope from the ScopeInfo.
|
2015-05-26 20:29:47 +00:00
|
|
|
Scope* scope =
|
2015-10-07 14:55:38 +00:00
|
|
|
NewScope(scope_, FUNCTION_SCOPE, FunctionKind::kArrowFunction);
|
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.
|
|
|
|
if (shared_info->scope_info()->CallsEval()) {
|
|
|
|
scope->RecordEvalCall();
|
|
|
|
}
|
2015-11-05 19:52:36 +00:00
|
|
|
SetLanguageMode(scope, shared_info->language_mode());
|
2016-01-14 19:20:56 +00:00
|
|
|
|
2015-04-21 14:44:18 +00:00
|
|
|
scope->set_start_position(shared_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.
|
|
|
|
BlockState block_state(&scope_, scope);
|
|
|
|
if (Check(Token::LPAREN)) {
|
|
|
|
// '(' StrictFormalParameters ')'
|
2015-07-23 11:53:31 +00:00
|
|
|
ParseFormalParameterList(&formals, &formals_classifier, &ok);
|
2015-06-15 17:06:34 +00:00
|
|
|
if (ok) ok = Check(Token::RPAREN);
|
|
|
|
} else {
|
|
|
|
// BindingIdentifier
|
2015-08-13 18:36:34 +00:00
|
|
|
ParseFormalParameter(&formals, &formals_classifier, &ok);
|
2015-08-05 12:00:41 +00:00
|
|
|
if (ok) {
|
2015-08-26 14:59:05 +00:00
|
|
|
DeclareFormalParameter(formals.scope, formals.at(0),
|
|
|
|
&formals_classifier);
|
2015-08-05 12:00:41 +00:00
|
|
|
}
|
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.
|
2015-05-13 11:45:04 +00:00
|
|
|
Expression* expression =
|
2015-10-14 17:39:37 +00:00
|
|
|
ParseArrowFunctionLiteral(true, formals, formals_classifier, &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.
|
|
|
|
if (scanner()->location().end_pos == shared_info->end_position()) {
|
|
|
|
// 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
|
|
|
}
|
|
|
|
}
|
2014-11-13 16:50:03 +00:00
|
|
|
} else if (shared_info->is_default_constructor()) {
|
2016-02-01 17:44:23 +00:00
|
|
|
result = DefaultConstructor(
|
|
|
|
raw_name, IsSubclassConstructor(shared_info->kind()), scope,
|
|
|
|
shared_info->start_position(), shared_info->end_position(),
|
|
|
|
shared_info->language_mode());
|
2014-07-21 09:58:01 +00:00
|
|
|
} else {
|
2016-02-19 02:50:58 +00:00
|
|
|
result = ParseFunctionLiteral(raw_name, Scanner::Location::invalid(),
|
|
|
|
kSkipFunctionNameCheck, shared_info->kind(),
|
|
|
|
RelocInfo::kNoPosition, function_type,
|
|
|
|
shared_info->language_mode(), &ok);
|
2014-07-21 09:58:01 +00:00
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
// Make sure the results agree.
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(ok == (result != NULL));
|
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
|
|
|
|
2014-09-02 11:36:21 +00:00
|
|
|
if (result != NULL) {
|
2011-03-09 16:57:03 +00:00
|
|
|
Handle<String> inferred_name(shared_info->inferred_name());
|
2010-12-07 11:31:57 +00:00
|
|
|
result->set_inferred_name(inferred_name);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2010-09-30 09:28:58 +00:00
|
|
|
|
2015-01-27 21:06:36 +00:00
|
|
|
void* Parser::ParseStatementList(ZoneList<Statement*>* body, int end_token,
|
2015-04-27 12:13:45 +00:00
|
|
|
bool* ok) {
|
2015-01-27 21:06:36 +00:00
|
|
|
// StatementList ::
|
|
|
|
// (StatementListItem)* <end_token>
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Allocate a target stack to use for this set of source
|
|
|
|
// elements. This way, all scripts and functions get their own
|
|
|
|
// target stack thus avoiding illegal breaks and continues across
|
|
|
|
// functions.
|
2010-10-27 12:33:48 +00:00
|
|
|
TargetScope scope(&this->target_stack_);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2015-01-27 21:06:36 +00:00
|
|
|
DCHECK(body != NULL);
|
2011-01-20 18:51:47 +00:00
|
|
|
bool directive_prologue = true; // Parsing directive prologue.
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
while (peek() != end_token) {
|
2011-01-20 18:51:47 +00:00
|
|
|
if (directive_prologue && peek() != Token::STRING) {
|
|
|
|
directive_prologue = false;
|
|
|
|
}
|
|
|
|
|
2014-02-14 12:13:33 +00:00
|
|
|
Scanner::Location token_loc = scanner()->peek_location();
|
2015-01-27 21:06:36 +00:00
|
|
|
Statement* stat = ParseStatementListItem(CHECK_OK);
|
2011-01-20 18:51:47 +00:00
|
|
|
if (stat == NULL || stat->IsEmpty()) {
|
|
|
|
directive_prologue = false; // End of directive prologue.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (directive_prologue) {
|
|
|
|
// A shot at a directive.
|
2012-01-13 13:09:52 +00:00
|
|
|
ExpressionStatement* e_stat;
|
|
|
|
Literal* literal;
|
2011-01-20 18:51:47 +00:00
|
|
|
// Still processing directive prologue?
|
|
|
|
if ((e_stat = stat->AsExpressionStatement()) != NULL &&
|
|
|
|
(literal = e_stat->expression()->AsLiteral()) != NULL &&
|
2014-06-24 14:03:24 +00:00
|
|
|
literal->raw_value()->IsString()) {
|
2016-03-10 12:43:51 +00:00
|
|
|
// Check "use strict" directive (ES5 14.1), "use asm" directive.
|
2015-02-05 14:11:34 +00:00
|
|
|
bool use_strict_found =
|
2014-06-24 14:03:24 +00:00
|
|
|
literal->raw_value()->AsString() ==
|
2014-09-11 09:52:36 +00:00
|
|
|
ast_value_factory()->use_strict_string() &&
|
2014-06-30 13:35:16 +00:00
|
|
|
token_loc.end_pos - token_loc.beg_pos ==
|
2015-02-05 14:11:34 +00:00
|
|
|
ast_value_factory()->use_strict_string()->length() + 2;
|
2016-03-10 12:43:51 +00:00
|
|
|
if (use_strict_found) {
|
2015-09-01 18:29:23 +00:00
|
|
|
if (is_sloppy(scope_->language_mode())) {
|
2015-11-05 19:52:36 +00:00
|
|
|
RaiseLanguageMode(STRICT);
|
2015-09-01 18:29:23 +00:00
|
|
|
}
|
|
|
|
|
2015-08-26 14:59:05 +00:00
|
|
|
if (!scope_->HasSimpleParameters()) {
|
|
|
|
// TC39 deemed "use strict" directives to be an error when occurring
|
|
|
|
// in the body of a function with non-simple parameter list, on
|
|
|
|
// 29/7/2015. https://goo.gl/ueA7Ln
|
|
|
|
const AstRawString* string = literal->raw_value()->AsString();
|
|
|
|
ParserTraits::ReportMessageAt(
|
|
|
|
token_loc, MessageTemplate::kIllegalLanguageModeDirective,
|
|
|
|
string);
|
|
|
|
*ok = false;
|
|
|
|
return nullptr;
|
|
|
|
}
|
2015-04-27 12:13:45 +00:00
|
|
|
// Because declarations in strict eval code don't leak into the scope
|
|
|
|
// of the eval call, it is likely that functions declared in strict
|
|
|
|
// eval code will be used within the eval code, so lazy parsing is
|
2015-08-11 18:43:34 +00:00
|
|
|
// probably not a win.
|
2015-04-27 12:13:45 +00:00
|
|
|
if (scope_->is_eval_scope()) mode_ = PARSE_EAGERLY;
|
2014-06-30 13:35:16 +00:00
|
|
|
} else if (literal->raw_value()->AsString() ==
|
2014-09-11 09:52:36 +00:00
|
|
|
ast_value_factory()->use_asm_string() &&
|
2014-06-30 13:35:16 +00:00
|
|
|
token_loc.end_pos - token_loc.beg_pos ==
|
2014-09-11 09:52:36 +00:00
|
|
|
ast_value_factory()->use_asm_string()->length() + 2) {
|
2014-06-30 13:35:16 +00:00
|
|
|
// Store the usage count; The actual use counter on the isolate is
|
|
|
|
// incremented after parsing is done.
|
|
|
|
++use_counts_[v8::Isolate::kUseAsm];
|
2014-09-19 12:50:50 +00:00
|
|
|
scope_->SetAsmModule();
|
2015-11-05 19:52:36 +00:00
|
|
|
} else {
|
|
|
|
// Should not change mode, but will increment UseCounter
|
|
|
|
// if appropriate. Ditto usages below.
|
|
|
|
RaiseLanguageMode(SLOPPY);
|
2011-01-20 18:51:47 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// End of the directive prologue.
|
|
|
|
directive_prologue = false;
|
2015-11-05 19:52:36 +00:00
|
|
|
RaiseLanguageMode(SLOPPY);
|
2011-01-20 18:51:47 +00:00
|
|
|
}
|
2015-11-05 19:52:36 +00:00
|
|
|
} else {
|
|
|
|
RaiseLanguageMode(SLOPPY);
|
2011-01-20 18:51:47 +00:00
|
|
|
}
|
|
|
|
|
2015-01-27 21:06:36 +00:00
|
|
|
body->Add(stat, zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2009-08-19 07:30:20 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-27 21:06:36 +00:00
|
|
|
Statement* Parser::ParseStatementListItem(bool* ok) {
|
|
|
|
// (Ecma 262 6th Edition, 13.1):
|
|
|
|
// StatementListItem:
|
2012-02-20 14:02:59 +00:00
|
|
|
// Statement
|
2015-01-27 21:06:36 +00:00
|
|
|
// Declaration
|
2012-02-20 14:02:59 +00:00
|
|
|
|
|
|
|
switch (peek()) {
|
|
|
|
case Token::FUNCTION:
|
2012-02-29 12:12:52 +00:00
|
|
|
return ParseFunctionDeclaration(NULL, ok);
|
2014-09-16 22:15:39 +00:00
|
|
|
case Token::CLASS:
|
2016-01-15 20:38:16 +00:00
|
|
|
Consume(Token::CLASS);
|
2014-09-16 22:15:39 +00:00
|
|
|
return ParseClassDeclaration(NULL, ok);
|
2014-07-10 14:06:37 +00:00
|
|
|
case Token::CONST:
|
2015-07-07 21:57:09 +00:00
|
|
|
if (allow_const()) {
|
|
|
|
return ParseVariableStatement(kStatementListItem, NULL, ok);
|
|
|
|
}
|
|
|
|
break;
|
2015-01-27 21:06:36 +00:00
|
|
|
case Token::VAR:
|
|
|
|
return ParseVariableStatement(kStatementListItem, NULL, ok);
|
2014-07-10 14:06:37 +00:00
|
|
|
case Token::LET:
|
2015-08-28 18:47:30 +00:00
|
|
|
if (IsNextLetKeyword()) {
|
2015-01-27 21:06:36 +00:00
|
|
|
return ParseVariableStatement(kStatementListItem, NULL, ok);
|
2014-07-10 14:06:37 +00:00
|
|
|
}
|
2015-07-07 21:57:09 +00:00
|
|
|
break;
|
2015-01-27 21:06:36 +00:00
|
|
|
default:
|
2015-07-07 21:57:09 +00:00
|
|
|
break;
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
2015-07-07 21:57:09 +00:00
|
|
|
return ParseStatement(NULL, ok);
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-27 21:06:36 +00:00
|
|
|
Statement* Parser::ParseModuleItem(bool* ok) {
|
|
|
|
// (Ecma 262 6th Edition, 15.2):
|
|
|
|
// 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:
|
|
|
|
return ParseImportDeclaration(ok);
|
|
|
|
case Token::EXPORT:
|
|
|
|
return ParseExportDeclaration(ok);
|
|
|
|
default:
|
|
|
|
return ParseStatementListItem(ok);
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-25 23:00:10 +00:00
|
|
|
void* Parser::ParseModuleItemList(ZoneList<Statement*>* body, bool* ok) {
|
2015-01-27 21:06:36 +00:00
|
|
|
// (Ecma 262 6th Edition, 15.2):
|
|
|
|
// Module :
|
|
|
|
// ModuleBody?
|
|
|
|
//
|
|
|
|
// ModuleBody :
|
|
|
|
// ModuleItem*
|
2012-02-20 14:02:59 +00:00
|
|
|
|
2015-02-24 22:39:26 +00:00
|
|
|
DCHECK(scope_->is_module_scope());
|
2015-11-05 19:52:36 +00:00
|
|
|
RaiseLanguageMode(STRICT);
|
2012-02-20 14:02:59 +00:00
|
|
|
|
2015-02-24 22:39:26 +00:00
|
|
|
while (peek() != Token::EOS) {
|
|
|
|
Statement* stat = ParseModuleItem(CHECK_OK);
|
|
|
|
if (stat && !stat->IsEmpty()) {
|
|
|
|
body->Add(stat, zone());
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-09 08:59:03 +00:00
|
|
|
// Check that all exports are bound.
|
2015-02-24 22:39:26 +00:00
|
|
|
ModuleDescriptor* descriptor = scope_->module();
|
2015-02-18 18:25:00 +00:00
|
|
|
for (ModuleDescriptor::Iterator it = descriptor->iterator(); !it.done();
|
|
|
|
it.Advance()) {
|
2015-02-24 22:39:26 +00:00
|
|
|
if (scope_->LookupLocal(it.local_name()) == NULL) {
|
2015-02-19 20:14:55 +00:00
|
|
|
// TODO(adamk): Pass both local_name and export_name once ParserTraits
|
|
|
|
// supports multiple arg error messages.
|
|
|
|
// Also try to report this at a better location.
|
2015-05-18 08:34:05 +00:00
|
|
|
ParserTraits::ReportMessage(MessageTemplate::kModuleExportUndefined,
|
|
|
|
it.local_name());
|
2012-07-09 08:59:03 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-24 22:39:26 +00:00
|
|
|
return NULL;
|
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);
|
2015-02-26 18:40:50 +00:00
|
|
|
return GetSymbol(scanner());
|
2012-02-20 14:02:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-19 20:14:55 +00:00
|
|
|
void* Parser::ParseExportClause(ZoneList<const AstRawString*>* export_names,
|
|
|
|
ZoneList<Scanner::Location>* export_locations,
|
|
|
|
ZoneList<const AstRawString*>* local_names,
|
2015-01-30 03:26:50 +00:00
|
|
|
Scanner::Location* reserved_loc, bool* ok) {
|
|
|
|
// 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
|
|
|
|
|
|
|
|
Expect(Token::LBRACE, CHECK_OK);
|
|
|
|
|
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() &&
|
|
|
|
!Token::IsIdentifier(name_tok, STRICT, false)) {
|
|
|
|
*reserved_loc = scanner()->location();
|
|
|
|
}
|
2015-02-19 20:14:55 +00:00
|
|
|
const AstRawString* local_name = ParseIdentifierName(CHECK_OK);
|
2015-01-28 19:18:37 +00:00
|
|
|
const AstRawString* export_name = NULL;
|
|
|
|
if (CheckContextualKeyword(CStrVector("as"))) {
|
|
|
|
export_name = ParseIdentifierName(CHECK_OK);
|
|
|
|
}
|
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;
|
|
|
|
Expect(Token::COMMA, CHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
Expect(Token::RBRACE, CHECK_OK);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-02-26 18:40:50 +00:00
|
|
|
ZoneList<ImportDeclaration*>* 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);
|
|
|
|
|
2015-02-26 18:40:50 +00:00
|
|
|
ZoneList<ImportDeclaration*>* result =
|
|
|
|
new (zone()) ZoneList<ImportDeclaration*>(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);
|
|
|
|
}
|
|
|
|
if (!Token::IsIdentifier(scanner()->current_token(), STRICT, false)) {
|
2015-01-30 03:26:50 +00:00
|
|
|
*ok = false;
|
2015-05-18 08:34:05 +00:00
|
|
|
ReportMessage(MessageTemplate::kUnexpectedReserved);
|
2015-01-30 03:26:50 +00:00
|
|
|
return NULL;
|
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);
|
2015-01-30 03:26:50 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
2015-02-26 18:40:50 +00:00
|
|
|
VariableProxy* proxy = NewUnresolved(local_name, IMPORT);
|
|
|
|
ImportDeclaration* declaration =
|
|
|
|
factory()->NewImportDeclaration(proxy, import_name, NULL, scope_, pos);
|
2015-06-22 14:15:53 +00:00
|
|
|
Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK);
|
2015-02-26 18:40:50 +00:00
|
|
|
result->Add(declaration, 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
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-27 21:06:36 +00:00
|
|
|
Statement* 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 :
|
|
|
|
// NameSpaceImport
|
|
|
|
// NamedImports
|
|
|
|
// ImportedDefaultBinding
|
|
|
|
// 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();
|
2012-02-29 12:12:52 +00:00
|
|
|
Expect(Token::IMPORT, CHECK_OK);
|
2015-01-30 03:26:50 +00:00
|
|
|
|
|
|
|
Token::Value tok = peek();
|
|
|
|
|
|
|
|
// 'import' ModuleSpecifier ';'
|
|
|
|
if (tok == Token::STRING) {
|
2015-02-26 18:40:50 +00:00
|
|
|
const AstRawString* module_specifier = ParseModuleSpecifier(CHECK_OK);
|
2015-04-17 22:45:15 +00:00
|
|
|
scope_->module()->AddModuleRequest(module_specifier, zone());
|
2015-01-30 03:26:50 +00:00
|
|
|
ExpectSemicolon(CHECK_OK);
|
|
|
|
return factory()->NewEmptyStatement(pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse ImportedDefaultBinding if present.
|
2015-02-27 18:05:56 +00:00
|
|
|
ImportDeclaration* import_default_declaration = NULL;
|
2015-01-30 03:26:50 +00:00
|
|
|
if (tok != Token::MUL && tok != Token::LBRACE) {
|
2015-02-27 18:05:56 +00:00
|
|
|
const AstRawString* local_name =
|
2015-04-10 12:04:51 +00:00
|
|
|
ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
|
2015-02-27 18:05:56 +00:00
|
|
|
VariableProxy* proxy = NewUnresolved(local_name, IMPORT);
|
|
|
|
import_default_declaration = factory()->NewImportDeclaration(
|
|
|
|
proxy, ast_value_factory()->default_string(), NULL, scope_, pos);
|
2015-06-22 14:15:53 +00:00
|
|
|
Declare(import_default_declaration, DeclarationDescriptor::NORMAL, true,
|
|
|
|
CHECK_OK);
|
2015-01-30 03:26:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const AstRawString* module_instance_binding = NULL;
|
2015-02-26 18:40:50 +00:00
|
|
|
ZoneList<ImportDeclaration*>* named_declarations = NULL;
|
2015-02-27 18:05:56 +00:00
|
|
|
if (import_default_declaration == NULL || Check(Token::COMMA)) {
|
2015-01-30 03:26:50 +00:00
|
|
|
switch (peek()) {
|
|
|
|
case Token::MUL: {
|
|
|
|
Consume(Token::MUL);
|
|
|
|
ExpectContextualKeyword(CStrVector("as"), CHECK_OK);
|
|
|
|
module_instance_binding =
|
2015-04-10 12:04:51 +00:00
|
|
|
ParseIdentifier(kDontAllowRestrictedIdentifiers, CHECK_OK);
|
2015-02-26 18:40:50 +00:00
|
|
|
// TODO(ES6): Add an appropriate declaration.
|
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:
|
2015-02-26 18:40:50 +00:00
|
|
|
named_declarations = ParseNamedImports(pos, CHECK_OK);
|
2015-01-30 03:26:50 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
*ok = false;
|
|
|
|
ReportUnexpectedToken(scanner()->current_token());
|
|
|
|
return NULL;
|
|
|
|
}
|
2012-02-29 12:12:52 +00:00
|
|
|
}
|
|
|
|
|
2013-06-06 14:38:26 +00:00
|
|
|
ExpectContextualKeyword(CStrVector("from"), CHECK_OK);
|
2015-02-26 18:40:50 +00:00
|
|
|
const AstRawString* module_specifier = ParseModuleSpecifier(CHECK_OK);
|
2015-04-09 22:09:44 +00:00
|
|
|
scope_->module()->AddModuleRequest(module_specifier, zone());
|
|
|
|
|
2015-01-30 03:26:50 +00:00
|
|
|
if (module_instance_binding != NULL) {
|
2015-02-26 18:40:50 +00:00
|
|
|
// TODO(ES6): Set the module specifier for the module namespace binding.
|
2015-01-30 03:26:50 +00:00
|
|
|
}
|
|
|
|
|
2015-02-27 18:05:56 +00:00
|
|
|
if (import_default_declaration != NULL) {
|
|
|
|
import_default_declaration->set_module_specifier(module_specifier);
|
2015-01-30 03:26:50 +00:00
|
|
|
}
|
2015-01-27 21:06:36 +00:00
|
|
|
|
2015-02-26 18:40:50 +00:00
|
|
|
if (named_declarations != NULL) {
|
|
|
|
for (int i = 0; i < named_declarations->length(); ++i) {
|
|
|
|
named_declarations->at(i)->set_module_specifier(module_specifier);
|
|
|
|
}
|
2012-02-29 12:12:52 +00:00
|
|
|
}
|
|
|
|
|
2015-04-17 22:45:15 +00:00
|
|
|
ExpectSemicolon(CHECK_OK);
|
2015-01-27 21:06:36 +00:00
|
|
|
return factory()->NewEmptyStatement(pos);
|
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:
|
|
|
|
// 'export' 'default' FunctionDeclaration
|
|
|
|
// '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-01-15 20:38:16 +00:00
|
|
|
const AstRawString* default_string = ast_value_factory()->default_string();
|
2015-02-19 20:14:55 +00:00
|
|
|
ZoneList<const AstRawString*> names(1, zone());
|
2016-01-15 20:38:16 +00:00
|
|
|
Statement* result = nullptr;
|
|
|
|
Expression* default_export = nullptr;
|
2015-01-28 19:18:37 +00:00
|
|
|
switch (peek()) {
|
2016-01-15 20:38:16 +00:00
|
|
|
case Token::FUNCTION: {
|
|
|
|
Consume(Token::FUNCTION);
|
|
|
|
int pos = position();
|
|
|
|
bool is_generator = Check(Token::MUL);
|
|
|
|
if (peek() == Token::LPAREN) {
|
|
|
|
// FunctionDeclaration[+Default] ::
|
|
|
|
// 'function' '(' FormalParameters ')' '{' FunctionBody '}'
|
|
|
|
//
|
|
|
|
// GeneratorDeclaration[+Default] ::
|
|
|
|
// 'function' '*' '(' FormalParameters ')' '{' FunctionBody '}'
|
|
|
|
default_export = ParseFunctionLiteral(
|
|
|
|
default_string, Scanner::Location::invalid(),
|
|
|
|
kSkipFunctionNameCheck,
|
|
|
|
is_generator ? FunctionKind::kGeneratorFunction
|
|
|
|
: FunctionKind::kNormalFunction,
|
2016-02-19 02:50:58 +00:00
|
|
|
pos, FunctionLiteral::kDeclaration, language_mode(), CHECK_OK);
|
2016-01-15 20:38:16 +00:00
|
|
|
result = factory()->NewEmptyStatement(RelocInfo::kNoPosition);
|
|
|
|
} else {
|
|
|
|
result = ParseFunctionDeclaration(pos, is_generator, &names, CHECK_OK);
|
|
|
|
}
|
2015-01-28 19:18:37 +00:00
|
|
|
break;
|
2016-01-15 20:38:16 +00:00
|
|
|
}
|
2015-01-28 19:18:37 +00:00
|
|
|
|
|
|
|
case Token::CLASS:
|
2016-01-15 20:38:16 +00:00
|
|
|
Consume(Token::CLASS);
|
|
|
|
if (peek() == Token::EXTENDS || peek() == Token::LBRACE) {
|
|
|
|
// ClassDeclaration[+Default] ::
|
|
|
|
// 'class' ('extends' LeftHandExpression)? '{' ClassBody '}'
|
|
|
|
default_export =
|
|
|
|
ParseClassLiteral(default_string, Scanner::Location::invalid(),
|
|
|
|
false, position(), CHECK_OK);
|
|
|
|
result = factory()->NewEmptyStatement(RelocInfo::kNoPosition);
|
|
|
|
} else {
|
|
|
|
result = ParseClassDeclaration(&names, CHECK_OK);
|
|
|
|
}
|
2015-01-28 19:18:37 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default: {
|
|
|
|
int pos = peek_position();
|
2016-02-19 15:58:57 +00:00
|
|
|
ExpressionClassifier classifier(this);
|
2015-04-22 12:35:05 +00:00
|
|
|
Expression* expr = ParseAssignmentExpression(true, &classifier, CHECK_OK);
|
2016-02-19 15:58:57 +00:00
|
|
|
RewriteNonPattern(&classifier, CHECK_OK);
|
2015-04-22 12:35:05 +00:00
|
|
|
|
2015-01-28 19:18:37 +00:00
|
|
|
ExpectSemicolon(CHECK_OK);
|
|
|
|
result = factory()->NewExpressionStatement(expr, pos);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-19 20:14:55 +00:00
|
|
|
DCHECK_LE(names.length(), 1);
|
|
|
|
if (names.length() == 1) {
|
|
|
|
scope_->module()->AddLocalExport(default_string, names.first(), zone(), ok);
|
|
|
|
if (!*ok) {
|
2015-05-18 08:34:05 +00:00
|
|
|
ParserTraits::ReportMessageAt(
|
|
|
|
default_loc, MessageTemplate::kDuplicateExport, default_string);
|
2016-01-15 20:38:16 +00:00
|
|
|
return nullptr;
|
2015-02-19 20:14:55 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// TODO(ES6): Assign result to a const binding with the name "*default*"
|
|
|
|
// and add an export entry with "*default*" as the local name.
|
2016-01-15 20:38:16 +00:00
|
|
|
USE(default_export);
|
2015-02-19 20:14:55 +00:00
|
|
|
}
|
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);
|
|
|
|
|
|
|
|
Statement* result = NULL;
|
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
|
|
|
scope_->module()->AddModuleRequest(module_specifier, zone());
|
2015-02-19 20:14:55 +00:00
|
|
|
// TODO(ES6): scope_->module()->AddStarExport(...)
|
2015-04-17 22:45:15 +00:00
|
|
|
ExpectSemicolon(CHECK_OK);
|
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());
|
|
|
|
ZoneList<const AstRawString*> local_names(1, zone());
|
|
|
|
ParseExportClause(&export_names, &export_locations, &local_names,
|
|
|
|
&reserved_loc, CHECK_OK);
|
2015-02-26 18:40:50 +00:00
|
|
|
const AstRawString* indirect_export_module_specifier = NULL;
|
2015-01-28 19:18:37 +00:00
|
|
|
if (CheckContextualKeyword(CStrVector("from"))) {
|
2015-02-19 20:14:55 +00:00
|
|
|
indirect_export_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);
|
2015-01-30 03:26:50 +00:00
|
|
|
return NULL;
|
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();
|
|
|
|
DCHECK_EQ(length, local_names.length());
|
|
|
|
DCHECK_EQ(length, export_locations.length());
|
|
|
|
if (indirect_export_module_specifier == NULL) {
|
|
|
|
for (int i = 0; i < length; ++i) {
|
|
|
|
scope_->module()->AddLocalExport(export_names[i], local_names[i],
|
|
|
|
zone(), ok);
|
|
|
|
if (!*ok) {
|
|
|
|
ParserTraits::ReportMessageAt(export_locations[i],
|
2015-05-18 08:34:05 +00:00
|
|
|
MessageTemplate::kDuplicateExport,
|
|
|
|
export_names[i]);
|
2015-02-19 20:14:55 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2015-04-09 22:09:44 +00:00
|
|
|
scope_->module()->AddModuleRequest(indirect_export_module_specifier,
|
|
|
|
zone());
|
2015-02-19 20:14:55 +00:00
|
|
|
for (int i = 0; i < length; ++i) {
|
|
|
|
// TODO(ES6): scope_->module()->AddIndirectExport(...);(
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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:
|
|
|
|
result = ParseFunctionDeclaration(&names, CHECK_OK);
|
|
|
|
break;
|
|
|
|
|
2014-09-16 22:15:39 +00:00
|
|
|
case Token::CLASS:
|
2016-01-15 20:38:16 +00:00
|
|
|
Consume(Token::CLASS);
|
2014-09-16 22:15:39 +00:00
|
|
|
result = ParseClassDeclaration(&names, CHECK_OK);
|
|
|
|
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;
|
|
|
|
|
|
|
|
default:
|
|
|
|
*ok = false;
|
2014-02-14 12:13:33 +00:00
|
|
|
ReportUnexpectedToken(scanner()->current_token());
|
2012-02-29 12:12:52 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-02-19 20:14:55 +00:00
|
|
|
// Extract declared names into export declarations.
|
|
|
|
ModuleDescriptor* descriptor = scope_->module();
|
2014-07-30 13:54:45 +00:00
|
|
|
for (int i = 0; i < names.length(); ++i) {
|
2015-02-19 20:14:55 +00:00
|
|
|
descriptor->AddLocalExport(names[i], names[i], zone(), ok);
|
|
|
|
if (!*ok) {
|
|
|
|
// TODO(adamk): Possibly report this error at the right place.
|
2015-05-18 08:34:05 +00:00
|
|
|
ParserTraits::ReportMessage(MessageTemplate::kDuplicateExport, names[i]);
|
2015-02-19 20:14:55 +00:00
|
|
|
return NULL;
|
2015-01-28 19:18:37 +00:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
Statement* Parser::ParseStatement(ZoneList<const AstRawString*>* labels,
|
|
|
|
bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// Statement ::
|
2015-02-17 16:25:49 +00:00
|
|
|
// EmptyStatement
|
|
|
|
// ...
|
|
|
|
|
|
|
|
if (peek() == Token::SEMICOLON) {
|
|
|
|
Next();
|
|
|
|
return factory()->NewEmptyStatement(RelocInfo::kNoPosition);
|
|
|
|
}
|
|
|
|
return ParseSubStatement(labels, ok);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Statement* Parser::ParseSubStatement(ZoneList<const AstRawString*>* labels,
|
|
|
|
bool* ok) {
|
|
|
|
// Statement ::
|
2008-07-03 15:10:15 +00:00
|
|
|
// Block
|
|
|
|
// VariableStatement
|
|
|
|
// EmptyStatement
|
|
|
|
// ExpressionStatement
|
|
|
|
// IfStatement
|
|
|
|
// IterationStatement
|
|
|
|
// ContinueStatement
|
|
|
|
// BreakStatement
|
|
|
|
// ReturnStatement
|
|
|
|
// WithStatement
|
|
|
|
// LabelledStatement
|
|
|
|
// SwitchStatement
|
|
|
|
// ThrowStatement
|
|
|
|
// TryStatement
|
|
|
|
// DebuggerStatement
|
|
|
|
|
|
|
|
// Note: Since labels can only be used by 'break' and 'continue'
|
|
|
|
// statements, which themselves are only valid within blocks,
|
|
|
|
// iterations or 'switch' statements (i.e., BreakableStatements),
|
|
|
|
// labels can be simply ignored in all other cases; except for
|
2009-01-15 19:08:34 +00:00
|
|
|
// trivial labeled break statements 'label: break label' which is
|
2008-07-03 15:10:15 +00:00
|
|
|
// parsed into an empty statement.
|
|
|
|
switch (peek()) {
|
|
|
|
case Token::LBRACE:
|
|
|
|
return ParseBlock(labels, ok);
|
|
|
|
|
|
|
|
case Token::SEMICOLON:
|
|
|
|
Next();
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewEmptyStatement(RelocInfo::kNoPosition);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::IF:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseIfStatement(labels, ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::DO:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseDoWhileStatement(labels, ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::WHILE:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseWhileStatement(labels, ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::FOR:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseForStatement(labels, ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::CONTINUE:
|
|
|
|
case Token::BREAK:
|
|
|
|
case Token::RETURN:
|
2015-04-16 13:29:29 +00:00
|
|
|
case Token::THROW:
|
|
|
|
case Token::TRY: {
|
|
|
|
// These statements must have their labels preserved in an enclosing
|
|
|
|
// block
|
|
|
|
if (labels == NULL) {
|
|
|
|
return ParseStatementAsUnlabelled(labels, ok);
|
|
|
|
} else {
|
|
|
|
Block* result =
|
|
|
|
factory()->NewBlock(labels, 1, false, RelocInfo::kNoPosition);
|
|
|
|
Target target(&this->target_stack_, result);
|
|
|
|
Statement* statement = ParseStatementAsUnlabelled(labels, CHECK_OK);
|
2015-10-01 13:59:36 +00:00
|
|
|
if (result) result->statements()->Add(statement, zone());
|
2015-04-16 13:29:29 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::WITH:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseWithStatement(labels, ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::SWITCH:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseSwitchStatement(labels, ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
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
|
|
|
case Token::FUNCTION:
|
|
|
|
// FunctionDeclaration only allowed as a StatementListItem, not in
|
|
|
|
// an arbitrary Statement position. Exceptions such as
|
|
|
|
// ES#sec-functiondeclarations-in-ifstatement-statement-clauses
|
|
|
|
// are handled by calling ParseScopedStatement rather than
|
|
|
|
// ParseSubStatement directly.
|
|
|
|
ReportMessageAt(scanner()->peek_location(),
|
|
|
|
is_strict(language_mode())
|
|
|
|
? MessageTemplate::kStrictFunction
|
|
|
|
: MessageTemplate::kSloppyFunction);
|
|
|
|
*ok = false;
|
|
|
|
return nullptr;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
case Token::DEBUGGER:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseDebuggerStatement(ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2014-07-10 14:06:37 +00:00
|
|
|
case Token::VAR:
|
|
|
|
return ParseVariableStatement(kStatement, NULL, ok);
|
|
|
|
|
2015-01-30 03:09:57 +00:00
|
|
|
case Token::CONST:
|
|
|
|
// In ES6 CONST is not allowed as a Statement, only as a
|
|
|
|
// LexicalDeclaration, however we continue to allow it in sloppy mode for
|
|
|
|
// backwards compatibility.
|
2015-07-07 21:57:09 +00:00
|
|
|
if (is_sloppy(language_mode()) && allow_legacy_const()) {
|
2014-07-10 14:06:37 +00:00
|
|
|
return ParseVariableStatement(kStatement, NULL, ok);
|
|
|
|
}
|
2015-01-30 03:09:57 +00:00
|
|
|
|
|
|
|
// Fall through.
|
2008-07-03 15:10:15 +00:00
|
|
|
default:
|
2013-10-14 09:24:58 +00:00
|
|
|
return ParseExpressionOrLabelledStatement(labels, ok);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-16 13:29:29 +00:00
|
|
|
Statement* Parser::ParseStatementAsUnlabelled(
|
|
|
|
ZoneList<const AstRawString*>* labels, bool* ok) {
|
|
|
|
switch (peek()) {
|
|
|
|
case Token::CONTINUE:
|
|
|
|
return ParseContinueStatement(ok);
|
|
|
|
|
|
|
|
case Token::BREAK:
|
|
|
|
return ParseBreakStatement(labels, ok);
|
|
|
|
|
|
|
|
case Token::RETURN:
|
|
|
|
return ParseReturnStatement(ok);
|
|
|
|
|
|
|
|
case Token::THROW:
|
|
|
|
return ParseThrowStatement(ok);
|
|
|
|
|
|
|
|
case Token::TRY:
|
|
|
|
return ParseTryStatement(ok);
|
|
|
|
|
|
|
|
default:
|
|
|
|
UNREACHABLE();
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
VariableProxy* Parser::NewUnresolved(const AstRawString* name,
|
2015-02-17 20:51:24 +00:00
|
|
|
VariableMode mode) {
|
2011-08-16 14:24:12 +00:00
|
|
|
// If we are inside a function, a declaration of a var/const variable is a
|
|
|
|
// truly local variable, and the scope of the variable is always the function
|
|
|
|
// scope.
|
2011-10-25 08:33:08 +00:00
|
|
|
// Let/const variables in harmony mode are always added to the immediately
|
|
|
|
// enclosing scope.
|
2015-12-02 23:57:27 +00:00
|
|
|
Scope* scope =
|
|
|
|
IsLexicalVariableMode(mode) ? scope_ : scope_->DeclarationScope();
|
|
|
|
return scope->NewUnresolved(factory(), name, Variable::NORMAL,
|
|
|
|
scanner()->location().beg_pos,
|
|
|
|
scanner()->location().end_pos);
|
2012-02-28 10:12:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-22 14:15:53 +00:00
|
|
|
Variable* Parser::Declare(Declaration* declaration,
|
|
|
|
DeclarationDescriptor::Kind declaration_kind,
|
[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
|
|
|
bool resolve, bool* ok, Scope* scope) {
|
2012-03-08 13:03:07 +00:00
|
|
|
VariableProxy* proxy = declaration->proxy();
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(proxy->raw_name() != NULL);
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name = proxy->raw_name();
|
2012-02-28 10:12:39 +00:00
|
|
|
VariableMode mode = declaration->mode();
|
2016-01-23 00:40:15 +00:00
|
|
|
bool is_function_declaration = declaration->IsFunctionDeclaration();
|
[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 (scope == nullptr) scope = scope_;
|
|
|
|
Scope* declaration_scope =
|
|
|
|
IsLexicalVariableMode(mode) ? scope : scope->DeclarationScope();
|
2012-02-28 10:12:39 +00:00
|
|
|
Variable* var = NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2012-07-09 08:59:03 +00:00
|
|
|
// If a suitable scope exists, then we can statically declare this
|
2008-07-03 15:10:15 +00:00
|
|
|
// variable and also set its mode. In any case, a Declaration node
|
|
|
|
// will be added to the scope so that the declaration can be added
|
|
|
|
// to the corresponding activation frame at runtime if necessary.
|
2015-08-11 21:30:26 +00:00
|
|
|
// For instance, var declarations inside a sloppy eval scope need
|
|
|
|
// to be added to the calling function context. Similarly, strict
|
|
|
|
// mode eval scope and lexical eval bindings do not leak variable
|
|
|
|
// declarations to the caller's scope so we declare all locals, too.
|
2011-06-30 14:37:55 +00:00
|
|
|
if (declaration_scope->is_function_scope() ||
|
2012-02-20 14:02:59 +00:00
|
|
|
declaration_scope->is_block_scope() ||
|
2012-03-08 13:03:07 +00:00
|
|
|
declaration_scope->is_module_scope() ||
|
2015-08-11 21:30:26 +00:00
|
|
|
declaration_scope->is_script_scope() ||
|
|
|
|
(declaration_scope->is_eval_scope() &&
|
|
|
|
(is_strict(declaration_scope->language_mode()) ||
|
|
|
|
IsLexicalVariableMode(mode)))) {
|
2012-07-09 08:59:03 +00:00
|
|
|
// Declare the variable in the declaration scope.
|
2014-11-12 11:34:09 +00:00
|
|
|
var = declaration_scope->LookupLocal(name);
|
2008-07-03 15:10:15 +00:00
|
|
|
if (var == NULL) {
|
|
|
|
// Declare the name.
|
2015-04-16 14:13:03 +00:00
|
|
|
Variable::Kind kind = Variable::NORMAL;
|
2016-01-23 00:40:15 +00:00
|
|
|
if (is_function_declaration) {
|
2015-04-16 14:13:03 +00:00
|
|
|
kind = Variable::FUNCTION;
|
|
|
|
}
|
2015-02-17 20:51:24 +00:00
|
|
|
var = declaration_scope->DeclareLocal(
|
2016-02-18 17:19:35 +00:00
|
|
|
name, mode, declaration->initialization(), kind, kNotAssigned);
|
2016-02-01 23:01:13 +00:00
|
|
|
} else if ((mode == CONST_LEGACY || var->mode() == CONST_LEGACY) &&
|
|
|
|
!declaration_scope->is_script_scope()) {
|
|
|
|
// Duplicate legacy const definitions throw at runtime.
|
|
|
|
DCHECK(is_sloppy(language_mode()));
|
|
|
|
Expression* expression = NewThrowSyntaxError(
|
|
|
|
MessageTemplate::kVarRedeclaration, name, declaration->position());
|
|
|
|
declaration_scope->SetIllegalRedeclaration(expression);
|
|
|
|
} else if ((IsLexicalVariableMode(mode) ||
|
|
|
|
IsLexicalVariableMode(var->mode())) &&
|
|
|
|
// Lexical bindings may appear for some parameters in sloppy
|
|
|
|
// mode even with --harmony-sloppy off.
|
|
|
|
(is_strict(language_mode()) || allow_harmony_sloppy())) {
|
|
|
|
// Allow duplicate function decls for web compat, see bug 4693.
|
|
|
|
if (is_sloppy(language_mode()) && is_function_declaration &&
|
|
|
|
var->is_function()) {
|
|
|
|
DCHECK(IsLexicalVariableMode(mode) &&
|
|
|
|
IsLexicalVariableMode(var->mode()));
|
|
|
|
++use_counts_[v8::Isolate::kSloppyModeBlockScopedFunctionRedefinition];
|
|
|
|
} else {
|
|
|
|
// The name was declared in this scope before; check for conflicting
|
|
|
|
// re-declarations. We have a conflict if either of the declarations
|
|
|
|
// is not a var (in script scope, we also have to ignore legacy const
|
|
|
|
// for compatibility). There is similar code in runtime.cc in the
|
|
|
|
// Declare functions. The function CheckConflictingVarDeclarations
|
|
|
|
// checks for var and let bindings from different scopes whereas this
|
|
|
|
// is a check for conflicting declarations within the same scope. This
|
|
|
|
// check also covers the special case
|
|
|
|
//
|
|
|
|
// function () { let x; { var x; } }
|
|
|
|
//
|
|
|
|
// because the var declaration is hoisted to the function scope where
|
|
|
|
// 'x' is already bound.
|
|
|
|
DCHECK(IsDeclaredVariableMode(var->mode()));
|
2014-03-11 14:41:22 +00:00
|
|
|
// In harmony we treat re-declarations as early errors. See
|
2012-08-28 11:25:08 +00:00
|
|
|
// ES5 16 for a definition of early errors.
|
2015-06-22 14:15:53 +00:00
|
|
|
if (declaration_kind == DeclarationDescriptor::NORMAL) {
|
|
|
|
ParserTraits::ReportMessage(MessageTemplate::kVarRedeclaration, name);
|
|
|
|
} else {
|
2015-07-14 21:58:49 +00:00
|
|
|
ParserTraits::ReportMessage(MessageTemplate::kParamDupe);
|
2015-06-22 14:15:53 +00:00
|
|
|
}
|
2012-08-28 11:25:08 +00:00
|
|
|
*ok = false;
|
2015-02-26 13:48:10 +00:00
|
|
|
return nullptr;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2014-06-26 11:59:42 +00:00
|
|
|
} else if (mode == VAR) {
|
|
|
|
var->set_maybe_assigned();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2015-08-11 21:30:26 +00:00
|
|
|
} else if (declaration_scope->is_eval_scope() &&
|
|
|
|
is_sloppy(declaration_scope->language_mode()) &&
|
|
|
|
!IsLexicalVariableMode(mode)) {
|
|
|
|
// In a var binding in a sloppy direct eval, pollute the enclosing scope
|
|
|
|
// with this new binding by doing the following:
|
|
|
|
// The proxy is bound to a lookup variable to force a dynamic declaration
|
|
|
|
// using the DeclareLookupSlot runtime function.
|
|
|
|
Variable::Kind kind = Variable::NORMAL;
|
|
|
|
// TODO(sigurds) figure out if kNotAssigned is OK here
|
|
|
|
var = new (zone()) Variable(declaration_scope, name, mode, kind,
|
|
|
|
declaration->initialization(), kNotAssigned);
|
|
|
|
var->AllocateTo(VariableLocation::LOOKUP, -1);
|
2015-10-12 14:30:46 +00:00
|
|
|
var->SetFromEval();
|
2015-08-11 21:30:26 +00:00
|
|
|
resolve = true;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2015-08-11 21:30:26 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// We add a declaration node for every declaration. The compiler
|
|
|
|
// will only generate code if necessary. In particular, declarations
|
|
|
|
// for inner local variables that do not represent functions won't
|
|
|
|
// result in any generated code.
|
|
|
|
//
|
|
|
|
// Note that we always add an unresolved proxy even if it's not
|
|
|
|
// used, simply because we don't know in this method (w/o extra
|
|
|
|
// parameters) if the proxy is needed or not. The proxy will be
|
|
|
|
// bound during variable resolution time unless it was pre-bound
|
|
|
|
// below.
|
|
|
|
//
|
|
|
|
// WARNING: This will lead to multiple declaration nodes for the
|
|
|
|
// same variable if it is declared several times. This is not a
|
|
|
|
// semantic issue as long as we keep the source order, but it may be
|
|
|
|
// a performance issue since it may lead to repeated
|
2014-07-14 14:01:04 +00:00
|
|
|
// RuntimeHidden_DeclareLookupSlot calls.
|
2012-02-28 10:12:39 +00:00
|
|
|
declaration_scope->AddDeclaration(declaration);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2014-11-12 11:34:09 +00:00
|
|
|
if (mode == CONST_LEGACY && declaration_scope->is_script_scope()) {
|
2011-11-15 13:48:40 +00:00
|
|
|
// For global const variables we bind the proxy to a variable.
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(resolve); // should be set by all callers
|
2009-06-24 08:01:38 +00:00
|
|
|
Variable::Kind kind = Variable::NORMAL;
|
2015-03-24 13:08:26 +00:00
|
|
|
var = new (zone()) Variable(declaration_scope, name, mode, kind,
|
2015-02-17 20:51:24 +00:00
|
|
|
kNeedsInitialization, kNotAssigned);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// If requested and we have a local variable, bind the proxy to the variable
|
|
|
|
// at parse-time. This is used for functions (and consts) declared inside
|
|
|
|
// statements: the corresponding function (or const) variable must be in the
|
|
|
|
// function scope and not a statement-local scope, e.g. as provided with a
|
|
|
|
// 'with' statement:
|
|
|
|
//
|
|
|
|
// with (obj) {
|
|
|
|
// function f() {}
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// which is translated into:
|
|
|
|
//
|
|
|
|
// with (obj) {
|
|
|
|
// // in this case this is not: 'var f; f = function () {};'
|
|
|
|
// var f = function () {};
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// Note that if 'f' is accessed from inside the 'with' statement, it
|
|
|
|
// will be allocated in the context (because we must be able to look
|
|
|
|
// it up dynamically) but it will also be accessed statically, i.e.,
|
|
|
|
// with a context slot index and a context chain length for this
|
|
|
|
// initialization code. Thus, inside the 'with' statement, we need
|
|
|
|
// both access to the static and the dynamic context chain; the
|
|
|
|
// runtime needs to provide both.
|
2012-03-08 13:03:07 +00:00
|
|
|
if (resolve && var != NULL) {
|
|
|
|
proxy->BindTo(var);
|
|
|
|
}
|
2015-02-26 13:48:10 +00:00
|
|
|
return var;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Language extension which is only enabled for source files loaded
|
|
|
|
// through the API's extension mechanism. A native function
|
|
|
|
// declaration is resolved by looking up the function through a
|
|
|
|
// callback provided by the extension.
|
|
|
|
Statement* Parser::ParseNativeDeclaration(bool* ok) {
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::FUNCTION, CHECK_OK);
|
2014-02-05 16:26:48 +00:00
|
|
|
// Allow "eval" or "arguments" for backward compatibility.
|
2015-04-10 12:04:51 +00:00
|
|
|
const AstRawString* name =
|
|
|
|
ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
|
|
|
bool done = (peek() == Token::RPAREN);
|
|
|
|
while (!done) {
|
2015-04-10 12:04:51 +00:00
|
|
|
ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
done = (peek() == Token::RPAREN);
|
2010-11-02 07:21:37 +00:00
|
|
|
if (!done) {
|
|
|
|
Expect(Token::COMMA, CHECK_OK);
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
Expect(Token::SEMICOLON, CHECK_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.
|
2015-12-02 23:57:27 +00:00
|
|
|
// TODO(adamk): Should this be ClosureScope()?
|
|
|
|
scope_->DeclarationScope()->ForceEagerCompilation();
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// TODO(1240846): It's weird that native function declarations are
|
|
|
|
// introduced dynamically when we meet their declarations, whereas
|
2012-01-13 13:09:52 +00:00
|
|
|
// other functions are set up when entering the surrounding scope.
|
2015-02-17 20:51:24 +00:00
|
|
|
VariableProxy* proxy = NewUnresolved(name, VAR);
|
2012-02-28 10:12:39 +00:00
|
|
|
Declaration* declaration =
|
2014-02-12 12:02:07 +00:00
|
|
|
factory()->NewVariableDeclaration(proxy, VAR, scope_, pos);
|
2015-06-22 14:15:53 +00:00
|
|
|
Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
NativeFunctionLiteral* lit = factory()->NewNativeFunctionLiteral(
|
|
|
|
name, extension_, RelocInfo::kNoPosition);
|
2012-02-08 09:56:33 +00:00
|
|
|
return factory()->NewExpressionStatement(
|
2015-11-16 17:07:46 +00:00
|
|
|
factory()->NewAssignment(Token::INIT, proxy, lit, RelocInfo::kNoPosition),
|
2013-10-14 09:24:58 +00:00
|
|
|
pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
Statement* Parser::ParseFunctionDeclaration(
|
|
|
|
ZoneList<const AstRawString*>* names, bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::FUNCTION, CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = position();
|
2014-09-16 12:30:39 +00:00
|
|
|
bool is_generator = Check(Token::MUL);
|
2016-01-15 20:38:16 +00:00
|
|
|
return ParseFunctionDeclaration(pos, is_generator, names, ok);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Statement* Parser::ParseFunctionDeclaration(
|
|
|
|
int pos, bool is_generator, ZoneList<const AstRawString*>* names,
|
|
|
|
bool* ok) {
|
|
|
|
// FunctionDeclaration ::
|
|
|
|
// 'function' Identifier '(' FormalParameters ')' '{' FunctionBody '}'
|
|
|
|
// GeneratorDeclaration ::
|
|
|
|
// 'function' '*' Identifier '(' FormalParameters ')' '{' FunctionBody '}'
|
|
|
|
//
|
|
|
|
// 'function' and '*' (if present) have been consumed by the caller.
|
2011-06-24 14:59:51 +00:00
|
|
|
bool is_strict_reserved = false;
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* name = ParseIdentifierOrStrictReservedWord(
|
2011-06-24 14:59:51 +00:00
|
|
|
&is_strict_reserved, CHECK_OK);
|
2015-07-30 10:43:05 +00:00
|
|
|
|
2015-12-10 19:18:54 +00:00
|
|
|
FuncNameInferrer::State fni_state(fni_);
|
|
|
|
if (fni_ != NULL) fni_->PushEnclosingName(name);
|
2015-07-15 09:14:49 +00:00
|
|
|
FunctionLiteral* fun = ParseFunctionLiteral(
|
|
|
|
name, scanner()->location(),
|
|
|
|
is_strict_reserved ? kFunctionNameIsStrictReserved
|
|
|
|
: kFunctionNameValidityUnknown,
|
|
|
|
is_generator ? FunctionKind::kGeneratorFunction
|
|
|
|
: FunctionKind::kNormalFunction,
|
2016-02-19 02:50:58 +00:00
|
|
|
pos, FunctionLiteral::kDeclaration, language_mode(), CHECK_OK);
|
2015-07-30 10:43:05 +00:00
|
|
|
|
2009-10-02 12:47:15 +00:00
|
|
|
// Even if we're not at the top-level of the global or a function
|
2012-08-28 11:25:08 +00:00
|
|
|
// scope, we treat it as such and introduce the function with its
|
2009-10-02 12:47:15 +00:00
|
|
|
// initial value upon entering the corresponding scope.
|
2014-11-12 11:34:09 +00:00
|
|
|
// In ES6, a function behaves as a lexical binding, except in
|
|
|
|
// a script scope, or the initial scope of eval or another function.
|
2012-08-28 11:25:08 +00:00
|
|
|
VariableMode mode =
|
2016-03-10 12:43:51 +00:00
|
|
|
(is_strict(language_mode()) || allow_harmony_sloppy_function()) &&
|
|
|
|
!scope_->is_declaration_scope()
|
|
|
|
? LET
|
|
|
|
: VAR;
|
2015-02-17 20:51:24 +00:00
|
|
|
VariableProxy* proxy = NewUnresolved(name, mode);
|
2012-02-28 10:12:39 +00:00
|
|
|
Declaration* declaration =
|
2014-02-12 12:02:07 +00:00
|
|
|
factory()->NewFunctionDeclaration(proxy, mode, fun, scope_, pos);
|
2015-06-22 14:15:53 +00:00
|
|
|
Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK);
|
2012-06-11 12:42:31 +00:00
|
|
|
if (names) names->Add(name, zone());
|
2015-09-21 04:30:50 +00:00
|
|
|
EmptyStatement* empty = factory()->NewEmptyStatement(RelocInfo::kNoPosition);
|
|
|
|
if (is_sloppy(language_mode()) && allow_harmony_sloppy_function() &&
|
|
|
|
!scope_->is_declaration_scope()) {
|
|
|
|
SloppyBlockFunctionStatement* delegate =
|
|
|
|
factory()->NewSloppyBlockFunctionStatement(empty, scope_);
|
|
|
|
scope_->DeclarationScope()->sloppy_block_function_map()->Declare(name,
|
|
|
|
delegate);
|
|
|
|
return delegate;
|
|
|
|
}
|
|
|
|
return empty;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-09-16 22:15:39 +00:00
|
|
|
Statement* Parser::ParseClassDeclaration(ZoneList<const AstRawString*>* names,
|
|
|
|
bool* ok) {
|
|
|
|
// ClassDeclaration ::
|
|
|
|
// 'class' Identifier ('extends' LeftHandExpression)? '{' ClassBody '}'
|
|
|
|
//
|
2016-01-15 20:38:16 +00:00
|
|
|
// 'class' is expected to be consumed by the caller.
|
|
|
|
//
|
2014-09-16 22:15:39 +00:00
|
|
|
// A ClassDeclaration
|
|
|
|
//
|
|
|
|
// class C { ... }
|
|
|
|
//
|
|
|
|
// has the same semantics as:
|
|
|
|
//
|
|
|
|
// let C = class C { ... };
|
|
|
|
//
|
|
|
|
// so rewrite it as such.
|
|
|
|
|
2015-02-04 09:34:05 +00:00
|
|
|
if (!allow_harmony_sloppy() && is_sloppy(language_mode())) {
|
2015-05-18 08:34:05 +00:00
|
|
|
ReportMessage(MessageTemplate::kSloppyLexical);
|
2014-11-20 10:51:49 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2014-09-16 22:15:39 +00:00
|
|
|
int pos = position();
|
|
|
|
bool is_strict_reserved = false;
|
|
|
|
const AstRawString* name =
|
|
|
|
ParseIdentifierOrStrictReservedWord(&is_strict_reserved, CHECK_OK);
|
2014-11-14 15:05:05 +00:00
|
|
|
ClassLiteral* value = ParseClassLiteral(name, scanner()->location(),
|
|
|
|
is_strict_reserved, pos, CHECK_OK);
|
2014-09-16 22:15:39 +00:00
|
|
|
|
2016-03-10 12:43:51 +00:00
|
|
|
VariableProxy* proxy = NewUnresolved(name, LET);
|
2016-02-18 17:19:35 +00:00
|
|
|
Declaration* declaration =
|
2016-03-10 12:43:51 +00:00
|
|
|
factory()->NewVariableDeclaration(proxy, LET, scope_, pos);
|
2016-02-18 17:19:35 +00:00
|
|
|
Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK);
|
2015-02-26 13:48:10 +00:00
|
|
|
proxy->var()->set_initializer_position(position());
|
2015-11-16 17:07:46 +00:00
|
|
|
Assignment* assignment =
|
|
|
|
factory()->NewAssignment(Token::INIT, proxy, value, pos);
|
2014-10-07 16:24:59 +00:00
|
|
|
Statement* assignment_statement =
|
|
|
|
factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition);
|
2014-09-16 22:15:39 +00:00
|
|
|
if (names) names->Add(name, zone());
|
2014-10-07 16:24:59 +00:00
|
|
|
return assignment_statement;
|
2014-09-16 22:15:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-12 19:53:34 +00:00
|
|
|
Block* Parser::ParseBlock(ZoneList<const AstRawString*>* labels,
|
|
|
|
bool finalize_block_scope, bool* ok) {
|
2012-02-20 14:02:59 +00:00
|
|
|
// The harmony mode uses block elements instead of statements.
|
2011-09-21 12:27:07 +00:00
|
|
|
//
|
|
|
|
// Block ::
|
2015-02-06 23:26:18 +00:00
|
|
|
// '{' StatementList '}'
|
2011-09-21 12:27:07 +00:00
|
|
|
|
2011-08-11 16:29:28 +00:00
|
|
|
// Construct block expecting 16 statements.
|
2013-10-14 09:24:58 +00:00
|
|
|
Block* body =
|
|
|
|
factory()->NewBlock(labels, 16, false, RelocInfo::kNoPosition);
|
2014-02-12 12:02:07 +00:00
|
|
|
Scope* block_scope = NewScope(scope_, BLOCK_SCOPE);
|
2011-08-11 16:29:28 +00:00
|
|
|
|
|
|
|
// Parse the statements and collect escaping labels.
|
|
|
|
Expect(Token::LBRACE, CHECK_OK);
|
2014-02-14 12:13:33 +00:00
|
|
|
block_scope->set_start_position(scanner()->location().beg_pos);
|
2014-02-12 12:02:07 +00:00
|
|
|
{ BlockState block_state(&scope_, block_scope);
|
2015-01-15 19:18:05 +00:00
|
|
|
Target target(&this->target_stack_, body);
|
2011-08-11 16:29:28 +00:00
|
|
|
|
|
|
|
while (peek() != Token::RBRACE) {
|
2015-01-27 21:06:36 +00:00
|
|
|
Statement* stat = ParseStatementListItem(CHECK_OK);
|
2011-08-11 16:29:28 +00:00
|
|
|
if (stat && !stat->IsEmpty()) {
|
2015-10-01 13:59:36 +00:00
|
|
|
body->statements()->Add(stat, zone());
|
2011-08-11 16:29:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Expect(Token::RBRACE, CHECK_OK);
|
2014-02-14 12:13:33 +00:00
|
|
|
block_scope->set_end_position(scanner()->location().end_pos);
|
2016-01-12 19:53:34 +00:00
|
|
|
if (finalize_block_scope) {
|
|
|
|
block_scope = block_scope->FinalizeBlockScope();
|
|
|
|
}
|
2012-04-16 14:43:27 +00:00
|
|
|
body->set_scope(block_scope);
|
2011-09-06 22:00:59 +00:00
|
|
|
return body;
|
2011-08-11 16:29:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-12 19:53:34 +00:00
|
|
|
Block* Parser::ParseBlock(ZoneList<const AstRawString*>* labels, bool* ok) {
|
|
|
|
return ParseBlock(labels, true, ok);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-05-15 09:56:31 +00:00
|
|
|
Block* Parser::DeclarationParsingResult::BuildInitializationBlock(
|
|
|
|
ZoneList<const AstRawString*>* names, bool* ok) {
|
2015-05-21 17:47:07 +00:00
|
|
|
Block* result = descriptor.parser->factory()->NewBlock(
|
|
|
|
NULL, 1, true, descriptor.declaration_pos);
|
2015-05-15 09:56:31 +00:00
|
|
|
for (auto declaration : declarations) {
|
|
|
|
PatternRewriter::DeclareAndInitializeVariables(
|
|
|
|
result, &descriptor, &declaration, names, CHECK_OK);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2011-08-16 14:24:12 +00:00
|
|
|
Block* Parser::ParseVariableStatement(VariableDeclarationContext var_context,
|
2014-06-24 14:03:24 +00:00
|
|
|
ZoneList<const AstRawString*>* names,
|
2011-08-16 14:24:12 +00:00
|
|
|
bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// VariableStatement ::
|
|
|
|
// VariableDeclarations ';'
|
|
|
|
|
2015-05-15 09:56:31 +00:00
|
|
|
// The scope of a var/const declared variable anywhere inside a function
|
|
|
|
// is the entire function (ECMA-262, 3rd, 10.1.3, and 12.2). Thus we can
|
|
|
|
// transform a source-level var/const declaration into a (Function)
|
|
|
|
// Scope declaration, and rewrite the source-level initialization into an
|
|
|
|
// assignment statement. We use a block to collect multiple assignments.
|
|
|
|
//
|
|
|
|
// We mark the block as initializer block because we don't want the
|
|
|
|
// rewriter to add a '.result' assignment to such a block (to get compliant
|
|
|
|
// behavior for code such as print(eval('var x = 7')), and for cosmetic
|
|
|
|
// reasons when pretty-printing. Also, unless an assignment (initialization)
|
|
|
|
// is inside an initializer block, it is ignored.
|
|
|
|
|
|
|
|
DeclarationParsingResult parsing_result;
|
2016-02-16 19:09:03 +00:00
|
|
|
Block* result =
|
|
|
|
ParseVariableDeclarations(var_context, &parsing_result, names, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
ExpectSemicolon(CHECK_OK);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2016-02-16 19:09:03 +00:00
|
|
|
Block* Parser::ParseVariableDeclarations(
|
|
|
|
VariableDeclarationContext var_context,
|
|
|
|
DeclarationParsingResult* parsing_result,
|
|
|
|
ZoneList<const AstRawString*>* names, bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// VariableDeclarations ::
|
2011-10-25 08:33:08 +00:00
|
|
|
// ('var' | 'const' | 'let') (Identifier ('=' AssignmentExpression)?)+[',']
|
|
|
|
//
|
|
|
|
// The ES6 Draft Rev3 specifies the following grammar for const declarations
|
|
|
|
//
|
|
|
|
// ConstDeclaration ::
|
|
|
|
// const ConstBinding (',' ConstBinding)* ';'
|
|
|
|
// ConstBinding ::
|
|
|
|
// Identifier '=' AssignmentExpression
|
|
|
|
//
|
|
|
|
// TODO(ES6):
|
|
|
|
// ConstBinding ::
|
|
|
|
// BindingPattern '=' AssignmentExpression
|
2013-10-14 09:24:58 +00:00
|
|
|
|
2015-05-15 09:56:31 +00:00
|
|
|
parsing_result->descriptor.parser = this;
|
2015-06-22 14:15:53 +00:00
|
|
|
parsing_result->descriptor.declaration_kind = DeclarationDescriptor::NORMAL;
|
2015-05-21 17:47:07 +00:00
|
|
|
parsing_result->descriptor.declaration_pos = peek_position();
|
|
|
|
parsing_result->descriptor.initialization_pos = peek_position();
|
2015-05-15 09:56:31 +00:00
|
|
|
parsing_result->descriptor.mode = VAR;
|
2016-02-16 19:09:03 +00:00
|
|
|
|
|
|
|
Block* init_block = nullptr;
|
|
|
|
if (var_context != kForStatement) {
|
|
|
|
init_block = factory()->NewBlock(
|
|
|
|
NULL, 1, true, parsing_result->descriptor.declaration_pos);
|
|
|
|
}
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
if (peek() == Token::VAR) {
|
|
|
|
Consume(Token::VAR);
|
2015-07-07 21:57:09 +00:00
|
|
|
} else if (peek() == Token::CONST && allow_const()) {
|
2008-07-03 15:10:15 +00:00
|
|
|
Consume(Token::CONST);
|
2015-07-07 21:57:09 +00:00
|
|
|
if (is_sloppy(language_mode()) && allow_legacy_const()) {
|
2015-05-15 09:56:31 +00:00
|
|
|
parsing_result->descriptor.mode = CONST_LEGACY;
|
2015-03-30 16:20:53 +00:00
|
|
|
++use_counts_[v8::Isolate::kLegacyConst];
|
2015-02-04 09:34:05 +00:00
|
|
|
} else {
|
2015-07-08 15:04:04 +00:00
|
|
|
DCHECK(is_strict(language_mode()) || allow_harmony_sloppy());
|
2015-02-04 09:34:05 +00:00
|
|
|
DCHECK(var_context != kStatement);
|
2015-05-15 09:56:31 +00:00
|
|
|
parsing_result->descriptor.mode = CONST;
|
2011-02-28 18:38:17 +00:00
|
|
|
}
|
2015-07-08 15:04:04 +00:00
|
|
|
} else if (peek() == Token::LET && allow_let()) {
|
2011-08-16 14:24:12 +00:00
|
|
|
Consume(Token::LET);
|
2015-01-30 03:09:57 +00:00
|
|
|
DCHECK(var_context != kStatement);
|
2015-05-15 09:56:31 +00:00
|
|
|
parsing_result->descriptor.mode = LET;
|
2008-07-03 15:10:15 +00:00
|
|
|
} else {
|
|
|
|
UNREACHABLE(); // by current callers
|
|
|
|
}
|
|
|
|
|
2015-05-15 09:56:31 +00:00
|
|
|
parsing_result->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
|
|
|
parsing_result->descriptor.hoist_scope = nullptr;
|
2015-05-11 16:28:28 +00:00
|
|
|
|
2012-02-28 10:12:39 +00:00
|
|
|
|
2015-05-15 09:56:31 +00:00
|
|
|
bool first_declaration = true;
|
2015-04-08 18:47:36 +00:00
|
|
|
int bindings_start = peek_position();
|
2008-07-03 15:10:15 +00:00
|
|
|
do {
|
2015-12-10 19:18:54 +00:00
|
|
|
FuncNameInferrer::State fni_state(fni_);
|
2010-08-23 13:26:03 +00:00
|
|
|
|
2015-05-15 09:56:31 +00:00
|
|
|
// Parse name.
|
|
|
|
if (!first_declaration) Consume(Token::COMMA);
|
2015-04-27 14:35:45 +00:00
|
|
|
|
2015-05-15 09:56:31 +00:00
|
|
|
Expression* pattern;
|
2015-11-04 19:26:13 +00:00
|
|
|
int decl_pos = peek_position();
|
2015-04-27 14:35:45 +00:00
|
|
|
{
|
2016-02-19 15:58:57 +00:00
|
|
|
ExpressionClassifier pattern_classifier(this);
|
2016-02-16 19:09:03 +00:00
|
|
|
pattern = ParsePrimaryExpression(&pattern_classifier, CHECK_OK);
|
|
|
|
ValidateBindingPattern(&pattern_classifier, CHECK_OK);
|
2015-10-05 20:28:45 +00:00
|
|
|
if (IsLexicalVariableMode(parsing_result->descriptor.mode)) {
|
2016-02-16 19:09:03 +00:00
|
|
|
ValidateLetPattern(&pattern_classifier, CHECK_OK);
|
2015-10-05 20:28:45 +00:00
|
|
|
}
|
2015-04-27 14:35:45 +00:00
|
|
|
}
|
|
|
|
|
2015-04-07 19:28:33 +00:00
|
|
|
Scanner::Location variable_loc = scanner()->location();
|
2015-05-15 09:56:31 +00:00
|
|
|
const AstRawString* single_name =
|
|
|
|
pattern->IsVariableProxy() ? pattern->AsVariableProxy()->raw_name()
|
|
|
|
: nullptr;
|
|
|
|
if (single_name != nullptr) {
|
|
|
|
if (fni_ != NULL) fni_->PushVariableName(single_name);
|
2015-05-11 16:28:28 +00:00
|
|
|
}
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Expression* value = NULL;
|
2015-05-15 09:56:31 +00:00
|
|
|
int initializer_position = RelocInfo::kNoPosition;
|
2015-11-04 19:26:13 +00:00
|
|
|
if (Check(Token::ASSIGN)) {
|
2016-02-19 15:58:57 +00:00
|
|
|
ExpressionClassifier classifier(this);
|
2015-04-22 12:35:05 +00:00
|
|
|
value = ParseAssignmentExpression(var_context != kForStatement,
|
2016-02-16 19:09:03 +00:00
|
|
|
&classifier, CHECK_OK);
|
2016-02-19 15:58:57 +00:00
|
|
|
RewriteNonPattern(&classifier, CHECK_OK);
|
2015-04-07 19:28:33 +00:00
|
|
|
variable_loc.end_pos = scanner()->location().end_pos;
|
|
|
|
|
2015-05-15 09:56:31 +00:00
|
|
|
if (!parsing_result->first_initializer_loc.IsValid()) {
|
|
|
|
parsing_result->first_initializer_loc = variable_loc;
|
2015-04-07 19:28:33 +00:00
|
|
|
}
|
|
|
|
|
2010-08-23 13:26:03 +00:00
|
|
|
// Don't infer if it is "a = function(){...}();"-like expression.
|
2015-05-11 16:28:28 +00:00
|
|
|
if (single_name) {
|
|
|
|
if (fni_ != NULL && value->AsCall() == NULL &&
|
|
|
|
value->AsCallNew() == NULL) {
|
|
|
|
fni_->Infer();
|
|
|
|
} else {
|
|
|
|
fni_->RemoveLastFunction();
|
|
|
|
}
|
2011-06-22 20:23:48 +00:00
|
|
|
}
|
2015-12-12 00:20:01 +00:00
|
|
|
|
2016-02-01 17:44:23 +00:00
|
|
|
if (allow_harmony_function_name()) {
|
|
|
|
ParserTraits::SetFunctionNameFromIdentifierRef(value, pattern);
|
2015-12-12 00:20:01 +00:00
|
|
|
}
|
|
|
|
|
2015-02-26 13:48:10 +00:00
|
|
|
// End position of the initializer is after the assignment expression.
|
2015-05-15 09:56:31 +00:00
|
|
|
initializer_position = scanner()->location().end_pos;
|
2015-02-26 13:48:10 +00:00
|
|
|
} else {
|
2016-02-04 18:43:55 +00:00
|
|
|
// Initializers may be either required or implied unless this is a
|
|
|
|
// for-in/of iteration variable.
|
|
|
|
if (var_context != kForStatement || !PeekInOrOf()) {
|
|
|
|
// ES6 'const' and binding patterns require initializers.
|
|
|
|
if (parsing_result->descriptor.mode == CONST ||
|
|
|
|
!pattern->IsVariableProxy()) {
|
|
|
|
ParserTraits::ReportMessageAt(
|
|
|
|
Scanner::Location(decl_pos, scanner()->location().end_pos),
|
|
|
|
MessageTemplate::kDeclarationMissingInitializer,
|
|
|
|
!pattern->IsVariableProxy() ? "destructuring" : "const");
|
|
|
|
*ok = false;
|
2016-02-16 19:09:03 +00:00
|
|
|
return nullptr;
|
2016-02-04 18:43:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// 'let x' and (legacy) 'const x' initialize 'x' to undefined.
|
|
|
|
if (parsing_result->descriptor.mode == LET ||
|
|
|
|
parsing_result->descriptor.mode == CONST_LEGACY) {
|
|
|
|
value = GetLiteralUndefined(position());
|
|
|
|
}
|
2015-11-04 19:26:13 +00:00
|
|
|
}
|
2016-02-04 18:43:55 +00:00
|
|
|
|
2015-02-26 13:48:10 +00:00
|
|
|
// End position of the initializer is after the variable.
|
2015-05-15 09:56:31 +00:00
|
|
|
initializer_position = position();
|
2011-11-08 13:28:53 +00:00
|
|
|
}
|
|
|
|
|
2016-02-16 19:09:03 +00:00
|
|
|
DeclarationParsingResult::Declaration decl(pattern, initializer_position,
|
|
|
|
value);
|
|
|
|
if (var_context == kForStatement) {
|
|
|
|
// Save the declaration for further handling in ParseForStatement.
|
|
|
|
parsing_result->declarations.Add(decl);
|
|
|
|
} else {
|
|
|
|
// Immediately declare the variable otherwise. This avoids O(N^2)
|
|
|
|
// behavior (where N is the number of variables in a single
|
|
|
|
// declaration) in the PatternRewriter having to do with removing
|
|
|
|
// and adding VariableProxies to the Scope (see bug 4699).
|
|
|
|
DCHECK_NOT_NULL(init_block);
|
|
|
|
PatternRewriter::DeclareAndInitializeVariables(
|
|
|
|
init_block, &parsing_result->descriptor, &decl, names, CHECK_OK);
|
|
|
|
}
|
2015-05-15 09:56:31 +00:00
|
|
|
first_declaration = false;
|
2008-07-03 15:10:15 +00:00
|
|
|
} while (peek() == Token::COMMA);
|
|
|
|
|
2015-05-15 09:56:31 +00:00
|
|
|
parsing_result->bindings_loc =
|
|
|
|
Scanner::Location(bindings_start, scanner()->location().end_pos);
|
2016-02-16 19:09:03 +00:00
|
|
|
|
|
|
|
DCHECK(*ok);
|
|
|
|
return init_block;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
static bool ContainsLabel(ZoneList<const AstRawString*>* labels,
|
|
|
|
const AstRawString* label) {
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(label != NULL);
|
2014-06-05 08:41:29 +00:00
|
|
|
if (labels != NULL) {
|
|
|
|
for (int i = labels->length(); i-- > 0; ) {
|
2014-06-24 14:03:24 +00:00
|
|
|
if (labels->at(i) == label) {
|
2008-07-03 15:10:15 +00:00
|
|
|
return true;
|
2014-06-05 08:41:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
Statement* Parser::ParseExpressionOrLabelledStatement(
|
|
|
|
ZoneList<const AstRawString*>* labels, bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// ExpressionStatement | LabelledStatement ::
|
|
|
|
// Expression ';'
|
|
|
|
// Identifier ':' Statement
|
2015-01-30 03:09:57 +00:00
|
|
|
//
|
|
|
|
// ExpressionStatement[Yield] :
|
|
|
|
// [lookahead ∉ {{, function, class, let [}] Expression[In, ?Yield] ;
|
|
|
|
|
2015-04-22 11:04:25 +00:00
|
|
|
int pos = peek_position();
|
|
|
|
|
2015-01-30 03:09:57 +00:00
|
|
|
switch (peek()) {
|
|
|
|
case Token::FUNCTION:
|
|
|
|
case Token::LBRACE:
|
|
|
|
UNREACHABLE(); // Always handled by the callers.
|
|
|
|
case Token::CLASS:
|
|
|
|
ReportUnexpectedToken(Next());
|
|
|
|
*ok = false;
|
|
|
|
return nullptr;
|
|
|
|
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-02-04 18:36:37 +00:00
|
|
|
bool starts_with_idenfifier = peek_any_identifier();
|
2008-07-03 15:10:15 +00:00
|
|
|
Expression* expr = ParseExpression(true, CHECK_OK);
|
2011-06-20 10:20:57 +00:00
|
|
|
if (peek() == Token::COLON && starts_with_idenfifier && expr != NULL &&
|
2014-02-06 11:59:16 +00:00
|
|
|
expr->AsVariableProxy() != NULL &&
|
|
|
|
!expr->AsVariableProxy()->is_this()) {
|
2010-11-16 12:10:48 +00:00
|
|
|
// Expression is a single identifier, and not, e.g., a parenthesized
|
|
|
|
// identifier.
|
2008-07-03 15:10:15 +00:00
|
|
|
VariableProxy* var = expr->AsVariableProxy();
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* label = var->raw_name();
|
2008-07-03 15:10:15 +00:00
|
|
|
// 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.
|
2010-11-02 11:45:47 +00:00
|
|
|
if (ContainsLabel(labels, label) || TargetStackContainsLabel(label)) {
|
2015-05-18 08:34:05 +00:00
|
|
|
ParserTraits::ReportMessage(MessageTemplate::kLabelRedeclaration, label);
|
2010-11-02 11:45:47 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2012-06-11 12:42:31 +00:00
|
|
|
if (labels == NULL) {
|
2014-06-24 14:03:24 +00:00
|
|
|
labels = new(zone()) ZoneList<const AstRawString*>(4, zone());
|
2012-06-11 12:42:31 +00:00
|
|
|
}
|
|
|
|
labels->Add(label, zone());
|
2010-11-02 11:45:47 +00:00
|
|
|
// 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.
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_->RemoveUnresolved(var);
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::COLON, CHECK_OK);
|
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
|
|
|
// ES#sec-labelled-function-declarations Labelled Function Declarations
|
|
|
|
if (peek() == Token::FUNCTION && is_sloppy(language_mode())) {
|
|
|
|
return ParseFunctionDeclaration(labels, ok);
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
return ParseStatement(labels, ok);
|
|
|
|
}
|
|
|
|
|
2011-06-20 10:20:57 +00:00
|
|
|
// If we have an extension, we allow a native function declaration.
|
|
|
|
// A native function declaration starts with "native function" with
|
|
|
|
// no line-terminator between the two words.
|
2014-09-11 09:52:36 +00:00
|
|
|
if (extension_ != NULL && peek() == Token::FUNCTION &&
|
|
|
|
!scanner()->HasAnyLineTerminatorBeforeNext() && expr != NULL &&
|
2014-02-06 11:59:16 +00:00
|
|
|
expr->AsVariableProxy() != NULL &&
|
2014-06-24 14:03:24 +00:00
|
|
|
expr->AsVariableProxy()->raw_name() ==
|
2014-09-11 09:52:36 +00:00
|
|
|
ast_value_factory()->native_string() &&
|
2014-02-14 12:13:33 +00:00
|
|
|
!scanner()->literal_contains_escapes()) {
|
2011-06-20 10:20:57 +00:00
|
|
|
return ParseNativeDeclaration(ok);
|
|
|
|
}
|
|
|
|
|
2015-01-27 21:06:36 +00:00
|
|
|
// Parsed expression statement, followed by semicolon.
|
|
|
|
// Detect attempts at 'let' declarations in sloppy mode.
|
2015-09-18 18:19:53 +00:00
|
|
|
if (!allow_harmony_sloppy_let() && peek() == Token::IDENTIFIER &&
|
|
|
|
expr->AsVariableProxy() != NULL &&
|
2015-01-27 21:06:36 +00:00
|
|
|
expr->AsVariableProxy()->raw_name() ==
|
|
|
|
ast_value_factory()->let_string()) {
|
2015-05-18 08:34:05 +00:00
|
|
|
ReportMessage(MessageTemplate::kSloppyLexical, NULL);
|
2015-01-27 21:06:36 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
2012-02-24 15:53:09 +00:00
|
|
|
}
|
2015-01-27 21:06:36 +00:00
|
|
|
ExpectSemicolon(CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewExpressionStatement(expr, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
IfStatement* Parser::ParseIfStatement(ZoneList<const AstRawString*>* labels,
|
|
|
|
bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// IfStatement ::
|
|
|
|
// 'if' '(' Expression ')' Statement ('else' Statement)?
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::IF, CHECK_OK);
|
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
|
|
|
Expression* condition = ParseExpression(true, CHECK_OK);
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
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
|
|
|
Statement* then_statement = ParseScopedStatement(labels, false, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
Statement* else_statement = NULL;
|
|
|
|
if (peek() == Token::ELSE) {
|
|
|
|
Next();
|
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
|
|
|
else_statement = ParseScopedStatement(labels, false, CHECK_OK);
|
2010-11-02 11:45:47 +00:00
|
|
|
} else {
|
2013-10-14 09:24:58 +00:00
|
|
|
else_statement = factory()->NewEmptyStatement(RelocInfo::kNoPosition);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewIfStatement(
|
|
|
|
condition, then_statement, else_statement, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Statement* Parser::ParseContinueStatement(bool* ok) {
|
|
|
|
// ContinueStatement ::
|
|
|
|
// 'continue' Identifier? ';'
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::CONTINUE, CHECK_OK);
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* label = NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
Token::Value tok = peek();
|
2014-02-14 12:13:33 +00:00
|
|
|
if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
|
2008-09-08 07:58:54 +00:00
|
|
|
tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) {
|
2014-02-05 16:26:48 +00:00
|
|
|
// ECMA allows "eval" or "arguments" as labels even in strict mode.
|
2015-04-10 12:04:51 +00:00
|
|
|
label = ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2014-06-24 14:03:24 +00:00
|
|
|
IterationStatement* target = LookupContinueTarget(label, CHECK_OK);
|
2010-11-02 11:45:47 +00:00
|
|
|
if (target == NULL) {
|
2011-01-17 09:36:10 +00:00
|
|
|
// Illegal continue statement.
|
2015-05-18 08:34:05 +00:00
|
|
|
MessageTemplate::Template message = MessageTemplate::kIllegalContinue;
|
2014-06-24 14:03:24 +00:00
|
|
|
if (label != NULL) {
|
2015-05-18 08:34:05 +00:00
|
|
|
message = MessageTemplate::kUnknownLabel;
|
2011-01-17 09:36:10 +00:00
|
|
|
}
|
2014-06-03 16:12:48 +00:00
|
|
|
ParserTraits::ReportMessage(message, label);
|
2011-01-17 09:36:10 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
ExpectSemicolon(CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewContinueStatement(target, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
Statement* Parser::ParseBreakStatement(ZoneList<const AstRawString*>* labels,
|
|
|
|
bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// BreakStatement ::
|
|
|
|
// 'break' Identifier? ';'
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::BREAK, CHECK_OK);
|
2014-06-24 14:03:24 +00:00
|
|
|
const AstRawString* label = NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
Token::Value tok = peek();
|
2014-02-14 12:13:33 +00:00
|
|
|
if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
|
2008-09-08 07:58:54 +00:00
|
|
|
tok != Token::SEMICOLON && tok != Token::RBRACE && tok != Token::EOS) {
|
2014-02-05 16:26:48 +00:00
|
|
|
// ECMA allows "eval" or "arguments" as labels even in strict mode.
|
2015-04-10 12:04:51 +00:00
|
|
|
label = ParseIdentifier(kAllowRestrictedIdentifiers, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2009-01-15 19:08:34 +00:00
|
|
|
// Parse labeled break statements that target themselves into
|
2008-07-03 15:10:15 +00:00
|
|
|
// empty statements, e.g. 'l1: l2: l3: break l2;'
|
2014-06-24 14:03:24 +00:00
|
|
|
if (label != NULL && ContainsLabel(labels, label)) {
|
2012-02-08 08:40:11 +00:00
|
|
|
ExpectSemicolon(CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewEmptyStatement(pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
BreakableStatement* target = NULL;
|
2010-11-02 11:45:47 +00:00
|
|
|
target = LookupBreakTarget(label, CHECK_OK);
|
|
|
|
if (target == NULL) {
|
2011-01-17 09:36:10 +00:00
|
|
|
// Illegal break statement.
|
2015-05-18 08:34:05 +00:00
|
|
|
MessageTemplate::Template message = MessageTemplate::kIllegalBreak;
|
2014-06-24 14:03:24 +00:00
|
|
|
if (label != NULL) {
|
2015-05-18 08:34:05 +00:00
|
|
|
message = MessageTemplate::kUnknownLabel;
|
2011-01-17 09:36:10 +00:00
|
|
|
}
|
2014-06-03 16:12:48 +00:00
|
|
|
ParserTraits::ReportMessage(message, label);
|
2011-01-17 09:36:10 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
ExpectSemicolon(CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewBreakStatement(target, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Statement* Parser::ParseReturnStatement(bool* ok) {
|
|
|
|
// ReturnStatement ::
|
|
|
|
// 'return' Expression? ';'
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
// Consume the return token. It is necessary to do that before
|
2008-07-03 15:10:15 +00:00
|
|
|
// reporting any errors on it, because of the way errors are
|
|
|
|
// reported (underlining).
|
|
|
|
Expect(Token::RETURN, CHECK_OK);
|
2014-04-02 12:38:01 +00:00
|
|
|
Scanner::Location loc = scanner()->location();
|
2015-03-19 19:39:53 +00:00
|
|
|
function_state_->set_return_location(loc);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2011-12-07 16:03:29 +00:00
|
|
|
Token::Value tok = peek();
|
|
|
|
Statement* result;
|
2013-04-19 14:11:23 +00:00
|
|
|
Expression* return_value;
|
2014-02-14 12:13:33 +00:00
|
|
|
if (scanner()->HasAnyLineTerminatorBeforeNext() ||
|
2011-12-07 16:03:29 +00:00
|
|
|
tok == Token::SEMICOLON ||
|
|
|
|
tok == Token::RBRACE ||
|
|
|
|
tok == Token::EOS) {
|
2015-02-12 20:06:52 +00:00
|
|
|
if (IsSubclassConstructor(function_state_->kind())) {
|
2015-02-06 10:34:50 +00:00
|
|
|
return_value = ThisExpression(scope_, factory(), loc.beg_pos);
|
|
|
|
} else {
|
|
|
|
return_value = GetLiteralUndefined(position());
|
|
|
|
}
|
2011-12-07 16:03:29 +00:00
|
|
|
} else {
|
2015-04-28 16:09:21 +00:00
|
|
|
int pos = peek_position();
|
2013-04-19 14:11:23 +00:00
|
|
|
return_value = ParseExpression(true, CHECK_OK);
|
2015-04-28 16:09:21 +00:00
|
|
|
|
|
|
|
if (IsSubclassConstructor(function_state_->kind())) {
|
|
|
|
// For subclass constructors we need to return this in case of undefined
|
2016-01-15 15:30:58 +00:00
|
|
|
// return a Smi (transformed into an exception in the ConstructStub)
|
|
|
|
// for a non object.
|
2015-04-28 16:09:21 +00:00
|
|
|
//
|
|
|
|
// return expr;
|
|
|
|
//
|
|
|
|
// Is rewritten as:
|
|
|
|
//
|
|
|
|
// return (temp = expr) === undefined ? this :
|
2016-01-15 15:30:58 +00:00
|
|
|
// %_IsJSReceiver(temp) ? temp : 1;
|
|
|
|
|
|
|
|
// temp = expr
|
2015-07-23 13:51:27 +00:00
|
|
|
Variable* temp = scope_->NewTemporary(
|
2015-04-28 16:09:21 +00:00
|
|
|
ast_value_factory()->empty_string());
|
|
|
|
Assignment* assign = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, factory()->NewVariableProxy(temp), return_value, pos);
|
|
|
|
|
2015-11-30 11:56:11 +00:00
|
|
|
// %_IsJSReceiver(temp)
|
2015-04-28 16:09:21 +00:00
|
|
|
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(
|
2015-11-30 11:56:11 +00:00
|
|
|
Runtime::kInlineIsJSReceiver, is_spec_object_args, pos);
|
2015-04-28 16:09:21 +00:00
|
|
|
|
2015-11-30 11:56:11 +00:00
|
|
|
// %_IsJSReceiver(temp) ? temp : throw_expression
|
2015-04-28 16:09:21 +00:00
|
|
|
Expression* is_object_conditional = factory()->NewConditional(
|
|
|
|
is_spec_object_call, factory()->NewVariableProxy(temp),
|
2016-01-15 15:30:58 +00:00
|
|
|
factory()->NewSmiLiteral(1, pos), pos);
|
2015-04-28 16:09:21 +00:00
|
|
|
|
|
|
|
// temp === undefined
|
|
|
|
Expression* is_undefined = factory()->NewCompareOperation(
|
|
|
|
Token::EQ_STRICT, assign,
|
|
|
|
factory()->NewUndefinedLiteral(RelocInfo::kNoPosition), pos);
|
|
|
|
|
|
|
|
// is_undefined ? this : is_object_conditional
|
|
|
|
return_value = factory()->NewConditional(
|
|
|
|
is_undefined, ThisExpression(scope_, factory(), pos),
|
|
|
|
is_object_conditional, pos);
|
|
|
|
}
|
2016-02-11 17:39:43 +00:00
|
|
|
|
|
|
|
// ES6 14.6.1 Static Semantics: IsInTailPosition
|
|
|
|
if (FLAG_harmony_tailcalls && !is_sloppy(language_mode())) {
|
|
|
|
function_state_->AddExpressionInTailPosition(return_value);
|
|
|
|
}
|
2013-04-19 14:11:23 +00:00
|
|
|
}
|
|
|
|
ExpectSemicolon(CHECK_OK);
|
2015-02-06 10:34:50 +00:00
|
|
|
|
2013-04-19 14:11:23 +00:00
|
|
|
if (is_generator()) {
|
2016-03-06 09:19:14 +00:00
|
|
|
return_value = BuildIteratorResult(return_value, true);
|
2011-12-07 16:03:29 +00:00
|
|
|
}
|
|
|
|
|
2016-03-06 09:19:14 +00:00
|
|
|
result = factory()->NewReturnStatement(return_value, loc.beg_pos);
|
|
|
|
|
2014-04-02 12:38:01 +00:00
|
|
|
Scope* decl_scope = scope_->DeclarationScope();
|
2014-11-12 11:34:09 +00:00
|
|
|
if (decl_scope->is_script_scope() || decl_scope->is_eval_scope()) {
|
2015-05-18 08:34:05 +00:00
|
|
|
ReportMessageAt(loc, MessageTemplate::kIllegalReturn);
|
2014-04-02 12:38:01 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2011-12-07 16:03:29 +00:00
|
|
|
return result;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
Statement* Parser::ParseWithStatement(ZoneList<const AstRawString*>* labels,
|
|
|
|
bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// WithStatement ::
|
|
|
|
// 'with' '(' Expression ')' Statement
|
|
|
|
|
|
|
|
Expect(Token::WITH, CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = position();
|
2011-01-20 18:51:47 +00:00
|
|
|
|
2015-02-04 09:34:05 +00:00
|
|
|
if (is_strict(language_mode())) {
|
2015-05-18 08:34:05 +00:00
|
|
|
ReportMessage(MessageTemplate::kStrictWith);
|
2011-01-20 18:51:47 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
|
|
|
Expression* expr = ParseExpression(true, CHECK_OK);
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
|
2014-02-12 12:02:07 +00:00
|
|
|
Scope* with_scope = NewScope(scope_, WITH_SCOPE);
|
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
|
|
|
Statement* body;
|
2014-02-12 12:02:07 +00:00
|
|
|
{ BlockState block_state(&scope_, with_scope);
|
2014-02-14 12:13:33 +00:00
|
|
|
with_scope->set_start_position(scanner()->peek_location().beg_pos);
|
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
|
|
|
body = ParseScopedStatement(labels, true, CHECK_OK);
|
2014-02-14 12:13:33 +00:00
|
|
|
with_scope->set_end_position(scanner()->location().end_pos);
|
2011-10-17 09:29:37 +00:00
|
|
|
}
|
2015-10-14 17:37:15 +00:00
|
|
|
return factory()->NewWithStatement(with_scope, expr, body, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CaseClause* Parser::ParseCaseClause(bool* default_seen_ptr, bool* ok) {
|
|
|
|
// CaseClause ::
|
2015-03-13 16:57:25 +00:00
|
|
|
// 'case' Expression ':' StatementList
|
|
|
|
// 'default' ':' StatementList
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
Expression* label = NULL; // NULL expression indicates default case
|
|
|
|
if (peek() == Token::CASE) {
|
|
|
|
Expect(Token::CASE, CHECK_OK);
|
|
|
|
label = ParseExpression(true, CHECK_OK);
|
|
|
|
} else {
|
|
|
|
Expect(Token::DEFAULT, CHECK_OK);
|
|
|
|
if (*default_seen_ptr) {
|
2015-05-18 08:34:05 +00:00
|
|
|
ReportMessage(MessageTemplate::kMultipleDefaultsInSwitch);
|
2008-07-03 15:10:15 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
*default_seen_ptr = true;
|
|
|
|
}
|
|
|
|
Expect(Token::COLON, CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = position();
|
2012-06-11 12:42:31 +00:00
|
|
|
ZoneList<Statement*>* statements =
|
|
|
|
new(zone()) ZoneList<Statement*>(5, zone());
|
2015-04-16 13:29:29 +00:00
|
|
|
Statement* stat = NULL;
|
2008-07-03 15:10:15 +00:00
|
|
|
while (peek() != Token::CASE &&
|
|
|
|
peek() != Token::DEFAULT &&
|
|
|
|
peek() != Token::RBRACE) {
|
2015-04-16 13:29:29 +00:00
|
|
|
stat = ParseStatementListItem(CHECK_OK);
|
2012-06-11 12:42:31 +00:00
|
|
|
statements->Add(stat, zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
2013-10-14 11:06:15 +00:00
|
|
|
return factory()->NewCaseClause(label, statements, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-24 18:57:08 +00:00
|
|
|
Statement* Parser::ParseSwitchStatement(ZoneList<const AstRawString*>* labels,
|
|
|
|
bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// SwitchStatement ::
|
|
|
|
// 'switch' '(' Expression ')' '{' CaseClause* '}'
|
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
|
|
|
|
2015-08-24 18:57:08 +00:00
|
|
|
Block* switch_block =
|
2015-08-28 22:43:00 +00:00
|
|
|
factory()->NewBlock(NULL, 2, false, RelocInfo::kNoPosition);
|
2015-08-24 18:57:08 +00:00
|
|
|
int switch_pos = peek_position();
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
Expect(Token::SWITCH, CHECK_OK);
|
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
|
|
|
Expression* tag = ParseExpression(true, CHECK_OK);
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
|
2015-08-24 18:57:08 +00:00
|
|
|
Variable* tag_variable =
|
|
|
|
scope_->NewTemporary(ast_value_factory()->dot_switch_tag_string());
|
|
|
|
Assignment* tag_assign = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, factory()->NewVariableProxy(tag_variable), tag,
|
|
|
|
tag->position());
|
|
|
|
Statement* tag_statement =
|
|
|
|
factory()->NewExpressionStatement(tag_assign, RelocInfo::kNoPosition);
|
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(
|
|
|
|
factory()->NewUndefinedLiteral(RelocInfo::kNoPosition),
|
|
|
|
RelocInfo::kNoPosition),
|
|
|
|
zone());
|
|
|
|
|
2015-08-24 18:57:08 +00:00
|
|
|
Block* cases_block =
|
2015-08-28 22:43:00 +00:00
|
|
|
factory()->NewBlock(NULL, 1, false, RelocInfo::kNoPosition);
|
2015-08-24 18:57:08 +00:00
|
|
|
Scope* cases_scope = NewScope(scope_, BLOCK_SCOPE);
|
2015-08-28 18:49:48 +00:00
|
|
|
cases_scope->SetNonlinear();
|
2015-08-24 18:57:08 +00:00
|
|
|
|
|
|
|
SwitchStatement* switch_statement =
|
|
|
|
factory()->NewSwitchStatement(labels, switch_pos);
|
|
|
|
|
|
|
|
cases_scope->set_start_position(scanner()->location().beg_pos);
|
|
|
|
{
|
|
|
|
BlockState cases_block_state(&scope_, cases_scope);
|
|
|
|
Target target(&this->target_stack_, switch_statement);
|
|
|
|
|
|
|
|
Expression* tag_read = factory()->NewVariableProxy(tag_variable);
|
|
|
|
|
|
|
|
bool default_seen = false;
|
|
|
|
ZoneList<CaseClause*>* cases =
|
|
|
|
new (zone()) ZoneList<CaseClause*>(4, zone());
|
|
|
|
Expect(Token::LBRACE, CHECK_OK);
|
|
|
|
while (peek() != Token::RBRACE) {
|
|
|
|
CaseClause* clause = ParseCaseClause(&default_seen, CHECK_OK);
|
|
|
|
cases->Add(clause, zone());
|
|
|
|
}
|
|
|
|
switch_statement->Initialize(tag_read, cases);
|
2015-10-01 13:59:36 +00:00
|
|
|
cases_block->statements()->Add(switch_statement, zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
Expect(Token::RBRACE, CHECK_OK);
|
|
|
|
|
2015-08-24 18:57:08 +00:00
|
|
|
cases_scope->set_end_position(scanner()->location().end_pos);
|
|
|
|
cases_scope = cases_scope->FinalizeBlockScope();
|
|
|
|
cases_block->set_scope(cases_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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Statement* Parser::ParseThrowStatement(bool* ok) {
|
|
|
|
// ThrowStatement ::
|
|
|
|
// 'throw' Expression ';'
|
|
|
|
|
|
|
|
Expect(Token::THROW, CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = position();
|
2014-02-14 12:13:33 +00:00
|
|
|
if (scanner()->HasAnyLineTerminatorBeforeNext()) {
|
2015-05-18 08:34:05 +00:00
|
|
|
ReportMessage(MessageTemplate::kNewlineAfterThrow);
|
2008-07-03 15:10:15 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
Expression* exception = ParseExpression(true, CHECK_OK);
|
|
|
|
ExpectSemicolon(CHECK_OK);
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewExpressionStatement(
|
|
|
|
factory()->NewThrow(exception, pos), pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2016-02-11 17:39:43 +00:00
|
|
|
class Parser::DontCollectExpressionsInTailPositionScope {
|
|
|
|
public:
|
|
|
|
DontCollectExpressionsInTailPositionScope(
|
|
|
|
Parser::FunctionState* function_state)
|
|
|
|
: function_state_(function_state),
|
|
|
|
old_value_(function_state->collect_expressions_in_tail_position()) {
|
|
|
|
function_state->set_collect_expressions_in_tail_position(false);
|
|
|
|
}
|
|
|
|
~DontCollectExpressionsInTailPositionScope() {
|
|
|
|
function_state_->set_collect_expressions_in_tail_position(old_value_);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Parser::FunctionState* function_state_;
|
|
|
|
bool old_value_;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Collects all return expressions at tail call position in this scope
|
|
|
|
// to a separate list.
|
|
|
|
class Parser::CollectExpressionsInTailPositionToListScope {
|
|
|
|
public:
|
|
|
|
CollectExpressionsInTailPositionToListScope(
|
|
|
|
Parser::FunctionState* function_state, List<Expression*>* list)
|
|
|
|
: function_state_(function_state), list_(list) {
|
|
|
|
function_state->expressions_in_tail_position().Swap(list_);
|
|
|
|
}
|
|
|
|
~CollectExpressionsInTailPositionToListScope() {
|
|
|
|
function_state_->expressions_in_tail_position().Swap(list_);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
Parser::FunctionState* function_state_;
|
|
|
|
List<Expression*>* list_;
|
|
|
|
};
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
TryStatement* Parser::ParseTryStatement(bool* ok) {
|
|
|
|
// TryStatement ::
|
|
|
|
// 'try' Block Catch
|
|
|
|
// 'try' Block Finally
|
|
|
|
// 'try' Block Catch Finally
|
|
|
|
//
|
|
|
|
// Catch ::
|
|
|
|
// 'catch' '(' Identifier ')' Block
|
|
|
|
//
|
|
|
|
// Finally ::
|
|
|
|
// 'finally' Block
|
|
|
|
|
|
|
|
Expect(Token::TRY, CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = position();
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2016-02-11 17:39:43 +00:00
|
|
|
Block* try_block;
|
|
|
|
{
|
|
|
|
DontCollectExpressionsInTailPositionScope no_tail_calls(function_state_);
|
|
|
|
try_block = ParseBlock(NULL, CHECK_OK);
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
Token::Value tok = peek();
|
|
|
|
if (tok != Token::CATCH && tok != Token::FINALLY) {
|
2015-05-18 08:34:05 +00:00
|
|
|
ReportMessage(MessageTemplate::kNoCatchOrFinally);
|
2008-07-03 15:10:15 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-06-30 14:37:55 +00:00
|
|
|
Scope* catch_scope = NULL;
|
|
|
|
Variable* catch_variable = NULL;
|
2011-06-08 13:55:33 +00:00
|
|
|
Block* catch_block = NULL;
|
2016-02-11 17:39:43 +00:00
|
|
|
List<Expression*> expressions_in_tail_position_in_catch_block;
|
2008-07-03 15:10:15 +00:00
|
|
|
if (tok == Token::CATCH) {
|
|
|
|
Consume(Token::CATCH);
|
|
|
|
|
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
2014-02-12 12:02:07 +00:00
|
|
|
catch_scope = NewScope(scope_, CATCH_SCOPE);
|
2014-02-14 12:13:33 +00:00
|
|
|
catch_scope->set_start_position(scanner()->location().beg_pos);
|
2011-01-20 18:51:47 +00:00
|
|
|
|
2016-02-19 15:58:57 +00:00
|
|
|
ExpressionClassifier pattern_classifier(this);
|
2015-11-05 20:21:20 +00:00
|
|
|
Expression* pattern = ParsePrimaryExpression(&pattern_classifier, CHECK_OK);
|
|
|
|
ValidateBindingPattern(&pattern_classifier, CHECK_OK);
|
|
|
|
|
|
|
|
const AstRawString* name = ast_value_factory()->dot_catch_string();
|
|
|
|
bool is_simple = pattern->IsVariableProxy();
|
|
|
|
if (is_simple) {
|
|
|
|
auto proxy = pattern->AsVariableProxy();
|
|
|
|
scope_->RemoveUnresolved(proxy);
|
|
|
|
name = proxy->raw_name();
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2015-02-26 13:48:10 +00:00
|
|
|
catch_variable = catch_scope->DeclareLocal(name, VAR, kCreatedInitialized,
|
|
|
|
Variable::NORMAL);
|
2015-11-05 20:21:20 +00:00
|
|
|
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
|
|
|
|
{
|
2016-02-11 17:39:43 +00:00
|
|
|
CollectExpressionsInTailPositionToListScope
|
|
|
|
collect_expressions_in_tail_position_scope(
|
|
|
|
function_state_, &expressions_in_tail_position_in_catch_block);
|
2015-11-05 20:21:20 +00:00
|
|
|
BlockState block_state(&scope_, catch_scope);
|
|
|
|
|
2015-11-12 17:41:27 +00:00
|
|
|
// TODO(adamk): Make a version of ParseBlock that takes a scope and
|
2015-11-05 20:21:20 +00:00
|
|
|
// a block.
|
|
|
|
catch_block =
|
|
|
|
factory()->NewBlock(nullptr, 16, false, RelocInfo::kNoPosition);
|
|
|
|
Scope* block_scope = NewScope(scope_, BLOCK_SCOPE);
|
|
|
|
|
|
|
|
block_scope->set_start_position(scanner()->location().beg_pos);
|
|
|
|
{
|
|
|
|
BlockState block_state(&scope_, block_scope);
|
|
|
|
Target target(&this->target_stack_, catch_block);
|
|
|
|
|
|
|
|
if (!is_simple) {
|
|
|
|
DeclarationDescriptor descriptor;
|
|
|
|
descriptor.declaration_kind = DeclarationDescriptor::NORMAL;
|
|
|
|
descriptor.parser = this;
|
|
|
|
descriptor.scope = scope_;
|
|
|
|
descriptor.hoist_scope = nullptr;
|
|
|
|
descriptor.mode = LET;
|
|
|
|
descriptor.declaration_pos = pattern->position();
|
|
|
|
descriptor.initialization_pos = pattern->position();
|
|
|
|
|
|
|
|
DeclarationParsingResult::Declaration decl(
|
|
|
|
pattern, pattern->position(),
|
|
|
|
factory()->NewVariableProxy(catch_variable));
|
|
|
|
|
2016-03-08 09:35:11 +00:00
|
|
|
Block* init_block =
|
|
|
|
factory()->NewBlock(nullptr, 8, true, RelocInfo::kNoPosition);
|
2015-11-05 20:21:20 +00:00
|
|
|
PatternRewriter::DeclareAndInitializeVariables(
|
2016-03-08 09:35:11 +00:00
|
|
|
init_block, &descriptor, &decl, nullptr, CHECK_OK);
|
|
|
|
catch_block->statements()->Add(init_block, zone());
|
2015-11-05 20:21:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Expect(Token::LBRACE, CHECK_OK);
|
|
|
|
while (peek() != Token::RBRACE) {
|
|
|
|
Statement* stat = ParseStatementListItem(CHECK_OK);
|
|
|
|
if (stat && !stat->IsEmpty()) {
|
|
|
|
catch_block->statements()->Add(stat, zone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Consume(Token::RBRACE);
|
|
|
|
}
|
|
|
|
block_scope->set_end_position(scanner()->location().end_pos);
|
|
|
|
block_scope = block_scope->FinalizeBlockScope();
|
|
|
|
catch_block->set_scope(block_scope);
|
|
|
|
}
|
2014-02-10 08:45:13 +00:00
|
|
|
|
2014-02-14 12:13:33 +00:00
|
|
|
catch_scope->set_end_position(scanner()->location().end_pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
tok = peek();
|
|
|
|
}
|
|
|
|
|
2011-06-08 13:55:33 +00:00
|
|
|
Block* finally_block = NULL;
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(tok == Token::FINALLY || catch_block != NULL);
|
2014-02-10 08:45:13 +00:00
|
|
|
if (tok == Token::FINALLY) {
|
2008-07-03 15:10:15 +00:00
|
|
|
Consume(Token::FINALLY);
|
|
|
|
finally_block = ParseBlock(NULL, CHECK_OK);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
2010-11-02 11:45:47 +00:00
|
|
|
if (catch_block != NULL && finally_block != NULL) {
|
2011-06-30 14:37:55 +00:00
|
|
|
// If we have both, create an inner try/catch.
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(catch_scope != NULL && catch_variable != NULL);
|
2015-06-08 18:19:32 +00:00
|
|
|
TryCatchStatement* statement =
|
|
|
|
factory()->NewTryCatchStatement(try_block, catch_scope, catch_variable,
|
|
|
|
catch_block, RelocInfo::kNoPosition);
|
2013-10-14 09:24:58 +00:00
|
|
|
try_block = factory()->NewBlock(NULL, 1, false, RelocInfo::kNoPosition);
|
2015-10-01 13:59:36 +00:00
|
|
|
try_block->statements()->Add(statement, zone());
|
2011-06-30 14:37:55 +00:00
|
|
|
catch_block = NULL; // Clear to indicate it's been handled.
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TryStatement* result = NULL;
|
2010-11-02 11:45:47 +00:00
|
|
|
if (catch_block != NULL) {
|
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.
|
|
|
|
function_state_->expressions_in_tail_position().AddAll(
|
|
|
|
expressions_in_tail_position_in_catch_block);
|
|
|
|
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(finally_block == NULL);
|
|
|
|
DCHECK(catch_scope != NULL && catch_variable != NULL);
|
2015-06-08 18:19:32 +00:00
|
|
|
result = factory()->NewTryCatchStatement(try_block, catch_scope,
|
|
|
|
catch_variable, catch_block, pos);
|
2010-11-02 11:45:47 +00:00
|
|
|
} else {
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(finally_block != NULL);
|
2015-06-08 18:19:32 +00:00
|
|
|
result = factory()->NewTryFinallyStatement(try_block, finally_block, pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
DoWhileStatement* Parser::ParseDoWhileStatement(
|
|
|
|
ZoneList<const AstRawString*>* labels, bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// DoStatement ::
|
|
|
|
// 'do' Statement 'while' '(' Expression ')' ';'
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
DoWhileStatement* loop =
|
|
|
|
factory()->NewDoWhileStatement(labels, peek_position());
|
2010-10-27 12:33:48 +00:00
|
|
|
Target target(&this->target_stack_, loop);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
Expect(Token::DO, CHECK_OK);
|
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
|
|
|
Statement* body = ParseScopedStatement(NULL, true, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::WHILE, CHECK_OK);
|
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
2009-11-16 21:59:31 +00:00
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Expression* cond = ParseExpression(true, CHECK_OK);
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
|
|
|
|
// Allow do-statements to be terminated with and without
|
|
|
|
// semi-colons. This allows code such as 'do;while(0)return' to
|
|
|
|
// parse, which would not be the case if we had used the
|
|
|
|
// ExpectSemicolon() functionality here.
|
|
|
|
if (peek() == Token::SEMICOLON) Consume(Token::SEMICOLON);
|
|
|
|
|
2009-10-12 13:14:06 +00:00
|
|
|
if (loop != NULL) loop->Initialize(cond, body);
|
2008-07-03 15:10:15 +00:00
|
|
|
return loop;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
WhileStatement* Parser::ParseWhileStatement(
|
|
|
|
ZoneList<const AstRawString*>* labels, bool* ok) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// WhileStatement ::
|
|
|
|
// 'while' '(' Expression ')' Statement
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
WhileStatement* loop = factory()->NewWhileStatement(labels, peek_position());
|
2010-10-27 12:33:48 +00:00
|
|
|
Target target(&this->target_stack_, loop);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
Expect(Token::WHILE, CHECK_OK);
|
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
|
|
|
Expression* cond = ParseExpression(true, CHECK_OK);
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
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
|
|
|
Statement* body = ParseScopedStatement(NULL, true, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2009-10-12 13:14:06 +00:00
|
|
|
if (loop != NULL) loop->Initialize(cond, body);
|
2008-07-03 15:10:15 +00:00
|
|
|
return loop;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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(
|
|
|
|
ast_value_factory()->next_string(), RelocInfo::kNoPosition);
|
|
|
|
Expression* next_property =
|
|
|
|
factory()->NewProperty(iterator, next_literal, RelocInfo::kNoPosition);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2013-06-07 11:12:21 +00:00
|
|
|
void Parser::InitializeForEachStatement(ForEachStatement* stmt,
|
2015-12-11 19:38:57 +00:00
|
|
|
Expression* each, Expression* subject,
|
2016-03-07 19:52:12 +00:00
|
|
|
Statement* body) {
|
2013-06-07 11:12:21 +00:00
|
|
|
ForOfStatement* for_of = stmt->AsForOfStatement();
|
|
|
|
if (for_of != NULL) {
|
2016-03-07 19:52:12 +00:00
|
|
|
InitializeForOfStatement(for_of, each, subject, body,
|
|
|
|
RelocInfo::kNoPosition);
|
2013-06-07 11:12:21 +00:00
|
|
|
} else {
|
2016-03-07 19:52:12 +00:00
|
|
|
if (each->IsArrayLiteral() || each->IsObjectLiteral()) {
|
2015-12-11 19:38:57 +00:00
|
|
|
Variable* temp =
|
|
|
|
scope_->NewTemporary(ast_value_factory()->empty_string());
|
|
|
|
VariableProxy* temp_proxy = factory()->NewVariableProxy(temp);
|
|
|
|
Expression* assign_each = PatternRewriter::RewriteDestructuringAssignment(
|
|
|
|
this, factory()->NewAssignment(Token::ASSIGN, each, temp_proxy,
|
|
|
|
RelocInfo::kNoPosition),
|
|
|
|
scope_);
|
|
|
|
auto block =
|
|
|
|
factory()->NewBlock(nullptr, 2, false, RelocInfo::kNoPosition);
|
|
|
|
block->statements()->Add(factory()->NewExpressionStatement(
|
|
|
|
assign_each, RelocInfo::kNoPosition),
|
|
|
|
zone());
|
|
|
|
block->statements()->Add(body, zone());
|
|
|
|
body = block;
|
|
|
|
each = factory()->NewVariableProxy(temp);
|
|
|
|
}
|
2013-06-07 11:12:21 +00:00
|
|
|
stmt->Initialize(each, subject, body);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-07 19:52:12 +00:00
|
|
|
void Parser::InitializeForOfStatement(ForOfStatement* for_of, Expression* each,
|
|
|
|
Expression* iterable, Statement* body,
|
|
|
|
int iterable_pos) {
|
|
|
|
Variable* iterator =
|
|
|
|
scope_->NewTemporary(ast_value_factory()->dot_iterator_string());
|
|
|
|
Variable* result =
|
|
|
|
scope_->NewTemporary(ast_value_factory()->dot_result_string());
|
|
|
|
|
|
|
|
Expression* assign_iterator;
|
|
|
|
Expression* next_result;
|
|
|
|
Expression* result_done;
|
|
|
|
Expression* assign_each;
|
|
|
|
|
|
|
|
// Hackily disambiguate o from o.next and o [Symbol.iterator]().
|
|
|
|
// TODO(verwaest): Come up with a better solution.
|
|
|
|
int get_iterator_pos = iterable_pos != RelocInfo::kNoPosition
|
|
|
|
? iterable_pos
|
|
|
|
: iterable->position() - 2;
|
|
|
|
int next_result_pos = iterable_pos != RelocInfo::kNoPosition
|
|
|
|
? iterable_pos
|
|
|
|
: iterable->position() - 1;
|
|
|
|
|
|
|
|
// iterator = iterable[Symbol.iterator]()
|
|
|
|
assign_iterator = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, factory()->NewVariableProxy(iterator),
|
|
|
|
GetIterator(iterable, factory(), get_iterator_pos), iterable->position());
|
|
|
|
|
|
|
|
// !%_IsJSReceiver(result = iterator.next()) &&
|
|
|
|
// %ThrowIteratorResultNotAnObject(result)
|
|
|
|
{
|
|
|
|
// result = iterator.next()
|
|
|
|
Expression* iterator_proxy = factory()->NewVariableProxy(iterator);
|
|
|
|
next_result =
|
|
|
|
BuildIteratorNextResult(iterator_proxy, result, next_result_pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// result.done
|
|
|
|
{
|
|
|
|
Expression* done_literal = factory()->NewStringLiteral(
|
|
|
|
ast_value_factory()->done_string(), RelocInfo::kNoPosition);
|
|
|
|
Expression* result_proxy = factory()->NewVariableProxy(result);
|
|
|
|
result_done = factory()->NewProperty(result_proxy, done_literal,
|
|
|
|
RelocInfo::kNoPosition);
|
|
|
|
}
|
|
|
|
|
|
|
|
// each = result.value
|
|
|
|
{
|
|
|
|
Expression* value_literal = factory()->NewStringLiteral(
|
|
|
|
ast_value_factory()->value_string(), RelocInfo::kNoPosition);
|
|
|
|
Expression* result_proxy = factory()->NewVariableProxy(result);
|
|
|
|
Expression* result_value = factory()->NewProperty(
|
|
|
|
result_proxy, value_literal, RelocInfo::kNoPosition);
|
|
|
|
assign_each = factory()->NewAssignment(Token::ASSIGN, each, result_value,
|
|
|
|
RelocInfo::kNoPosition);
|
|
|
|
if (each->IsArrayLiteral() || each->IsObjectLiteral()) {
|
|
|
|
assign_each = PatternRewriter::RewriteDestructuringAssignment(
|
|
|
|
this, assign_each->AsAssignment(), scope_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for_of->Initialize(each, iterable, body, iterator, assign_iterator,
|
|
|
|
next_result, result_done, assign_each);
|
|
|
|
}
|
|
|
|
|
2015-03-03 18:34:30 +00:00
|
|
|
Statement* Parser::DesugarLexicalBindingsInForStatement(
|
2016-02-04 18:39:05 +00:00
|
|
|
Scope* inner_scope, VariableMode mode, ZoneList<const AstRawString*>* names,
|
2014-06-24 14:03:24 +00:00
|
|
|
ForStatement* loop, Statement* init, Expression* cond, Statement* next,
|
|
|
|
Statement* body, 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
|
|
|
// }
|
|
|
|
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(names->length() > 0);
|
2014-05-26 08:07:02 +00:00
|
|
|
ZoneList<Variable*> temps(names->length(), zone());
|
|
|
|
|
2015-10-08 13:56:49 +00:00
|
|
|
Block* outer_block = factory()->NewBlock(NULL, names->length() + 4, false,
|
2014-05-26 08:07:02 +00:00
|
|
|
RelocInfo::kNoPosition);
|
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.
|
|
|
|
for (int i = 0; i < names->length(); i++) {
|
2015-02-17 20:51:24 +00:00
|
|
|
VariableProxy* proxy = NewUnresolved(names->at(i), LET);
|
2015-07-23 13:51:27 +00:00
|
|
|
Variable* temp = scope_->NewTemporary(temp_name);
|
2014-05-26 08:07:02 +00:00
|
|
|
VariableProxy* temp_proxy = factory()->NewVariableProxy(temp);
|
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, temp_proxy, proxy, RelocInfo::kNoPosition);
|
|
|
|
Statement* assignment_statement = factory()->NewExpressionStatement(
|
|
|
|
assignment, RelocInfo::kNoPosition);
|
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) {
|
2015-07-23 13:51:27 +00:00
|
|
|
first = scope_->NewTemporary(temp_name);
|
2014-11-14 19:32:53 +00:00
|
|
|
VariableProxy* first_proxy = factory()->NewVariableProxy(first);
|
2014-06-24 14:03:24 +00:00
|
|
|
Expression* const1 = factory()->NewSmiLiteral(1, RelocInfo::kNoPosition);
|
2014-05-26 08:07:02 +00:00
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
2014-11-14 19:32:53 +00:00
|
|
|
Token::ASSIGN, first_proxy, const1, RelocInfo::kNoPosition);
|
|
|
|
Statement* assignment_statement =
|
|
|
|
factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition);
|
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(
|
|
|
|
factory()->NewUndefinedLiteral(RelocInfo::kNoPosition),
|
|
|
|
RelocInfo::kNoPosition),
|
|
|
|
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 =
|
|
|
|
factory()->NewForStatement(NULL, RelocInfo::kNoPosition);
|
2015-10-01 13:59:36 +00:00
|
|
|
outer_block->statements()->Add(outer_loop, zone());
|
2016-01-14 19:25:09 +00:00
|
|
|
outer_block->set_scope(scope_);
|
2014-05-26 08:07:02 +00:00
|
|
|
|
2015-06-18 11:54:03 +00:00
|
|
|
Block* inner_block =
|
|
|
|
factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition);
|
2016-01-14 19:25:09 +00:00
|
|
|
{
|
|
|
|
BlockState block_state(&scope_, inner_scope);
|
|
|
|
|
|
|
|
Block* ignore_completion_block = factory()->NewBlock(
|
|
|
|
NULL, names->length() + 3, true, RelocInfo::kNoPosition);
|
|
|
|
ZoneList<Variable*> inner_vars(names->length(), zone());
|
|
|
|
// For each let variable x:
|
|
|
|
// make statement: let/const x = temp_x.
|
|
|
|
for (int i = 0; i < names->length(); i++) {
|
|
|
|
VariableProxy* proxy = NewUnresolved(names->at(i), mode);
|
|
|
|
Declaration* declaration = factory()->NewVariableDeclaration(
|
|
|
|
proxy, mode, scope_, RelocInfo::kNoPosition);
|
|
|
|
Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK);
|
|
|
|
inner_vars.Add(declaration->proxy()->var(), zone());
|
|
|
|
VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i));
|
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
|
|
|
Token::INIT, proxy, temp_proxy, RelocInfo::kNoPosition);
|
|
|
|
Statement* assignment_statement =
|
|
|
|
factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition);
|
|
|
|
DCHECK(init->position() != RelocInfo::kNoPosition);
|
|
|
|
proxy->var()->set_initializer_position(init->position());
|
|
|
|
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.
|
|
|
|
{
|
|
|
|
Expression* const1 =
|
|
|
|
factory()->NewSmiLiteral(1, RelocInfo::kNoPosition);
|
|
|
|
VariableProxy* first_proxy = factory()->NewVariableProxy(first);
|
|
|
|
compare = factory()->NewCompareOperation(Token::EQ, first_proxy, const1,
|
|
|
|
RelocInfo::kNoPosition);
|
|
|
|
}
|
|
|
|
Statement* clear_first = NULL;
|
|
|
|
// Make statement: first = 0.
|
|
|
|
{
|
|
|
|
VariableProxy* first_proxy = factory()->NewVariableProxy(first);
|
|
|
|
Expression* const0 =
|
|
|
|
factory()->NewSmiLiteral(0, RelocInfo::kNoPosition);
|
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, first_proxy, const0, RelocInfo::kNoPosition);
|
|
|
|
clear_first = factory()->NewExpressionStatement(assignment,
|
|
|
|
RelocInfo::kNoPosition);
|
|
|
|
}
|
|
|
|
Statement* clear_first_or_next = factory()->NewIfStatement(
|
|
|
|
compare, clear_first, next, RelocInfo::kNoPosition);
|
|
|
|
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
|
|
|
|
|
|
|
Variable* flag = scope_->NewTemporary(temp_name);
|
|
|
|
// 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);
|
|
|
|
Expression* const1 = factory()->NewSmiLiteral(1, RelocInfo::kNoPosition);
|
2014-05-26 08:07:02 +00:00
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
2016-01-14 19:25:09 +00:00
|
|
|
Token::ASSIGN, flag_proxy, const1, RelocInfo::kNoPosition);
|
|
|
|
Statement* assignment_statement =
|
2014-11-14 19:32:53 +00:00
|
|
|
factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition);
|
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 =
|
|
|
|
factory()->NewBreakStatement(outer_loop, RelocInfo::kNoPosition);
|
|
|
|
Statement* noop = factory()->NewEmptyStatement(RelocInfo::kNoPosition);
|
|
|
|
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-01-14 19:25:09 +00:00
|
|
|
Expression* const1 = factory()->NewSmiLiteral(1, RelocInfo::kNoPosition);
|
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,
|
|
|
|
RelocInfo::kNoPosition);
|
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);
|
|
|
|
Expression* const0 =
|
|
|
|
factory()->NewSmiLiteral(0, RelocInfo::kNoPosition);
|
|
|
|
compound_next = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, flag_proxy, const0, RelocInfo::kNoPosition);
|
|
|
|
}
|
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;
|
|
|
|
for (int i = 0; i < names->length(); i++) {
|
|
|
|
VariableProxy* temp_proxy = factory()->NewVariableProxy(temps.at(i));
|
|
|
|
VariableProxy* proxy =
|
|
|
|
factory()->NewVariableProxy(inner_vars.at(i), inner_var_proxy_pos);
|
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, temp_proxy, proxy, RelocInfo::kNoPosition);
|
|
|
|
compound_next = factory()->NewBinaryOperation(
|
|
|
|
Token::COMMA, compound_next, assignment, RelocInfo::kNoPosition);
|
|
|
|
}
|
2014-11-14 19:32:53 +00:00
|
|
|
|
2016-01-14 19:25:09 +00:00
|
|
|
compound_next_statement = factory()->NewExpressionStatement(
|
|
|
|
compound_next, RelocInfo::kNoPosition);
|
|
|
|
}
|
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.
|
|
|
|
{
|
|
|
|
Expression* const1 =
|
|
|
|
factory()->NewSmiLiteral(1, RelocInfo::kNoPosition);
|
|
|
|
VariableProxy* flag_proxy = factory()->NewVariableProxy(flag);
|
|
|
|
compare = factory()->NewCompareOperation(Token::EQ, flag_proxy, const1,
|
|
|
|
RelocInfo::kNoPosition);
|
|
|
|
}
|
|
|
|
Statement* stop =
|
|
|
|
factory()->NewBreakStatement(outer_loop, RelocInfo::kNoPosition);
|
|
|
|
Statement* empty = factory()->NewEmptyStatement(RelocInfo::kNoPosition);
|
|
|
|
Statement* if_flag_break = factory()->NewIfStatement(
|
|
|
|
compare, stop, empty, RelocInfo::kNoPosition);
|
|
|
|
Block* ignore_completion_block =
|
|
|
|
factory()->NewBlock(NULL, 1, true, RelocInfo::kNoPosition);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
Statement* Parser::ParseScopedStatement(ZoneList<const AstRawString*>* labels,
|
|
|
|
bool legacy, bool* ok) {
|
|
|
|
if (is_strict(language_mode()) || peek() != Token::FUNCTION ||
|
|
|
|
(legacy && allow_harmony_restrictive_declarations())) {
|
|
|
|
return ParseSubStatement(labels, ok);
|
|
|
|
} else {
|
|
|
|
if (legacy) {
|
|
|
|
++use_counts_[v8::Isolate::kLegacyFunctionDeclaration];
|
|
|
|
}
|
|
|
|
// Make a block around the statement for a lexical binding
|
|
|
|
// is introduced by a FunctionDeclaration.
|
|
|
|
Scope* body_scope = NewScope(scope_, BLOCK_SCOPE);
|
|
|
|
BlockState block_state(&scope_, body_scope);
|
|
|
|
Block* block = factory()->NewBlock(NULL, 1, false, RelocInfo::kNoPosition);
|
|
|
|
Statement* body = ParseFunctionDeclaration(NULL, CHECK_OK);
|
|
|
|
block->statements()->Add(body, zone());
|
|
|
|
body_scope->set_end_position(scanner()->location().end_pos);
|
|
|
|
body_scope = body_scope->FinalizeBlockScope();
|
|
|
|
block->set_scope(body_scope);
|
|
|
|
return block;
|
|
|
|
}
|
|
|
|
}
|
2014-05-26 08:07:02 +00:00
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
Statement* Parser::ParseForStatement(ZoneList<const AstRawString*>* labels,
|
|
|
|
bool* ok) {
|
2014-11-12 08:25:59 +00:00
|
|
|
int stmt_pos = peek_position();
|
2008-07-03 15:10:15 +00:00
|
|
|
Statement* init = NULL;
|
2015-03-03 18:34:30 +00:00
|
|
|
ZoneList<const AstRawString*> lexical_bindings(1, zone());
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2011-10-17 12:19:06 +00:00
|
|
|
// Create an in-between scope for let-bound iteration variables.
|
2014-02-12 12:02:07 +00:00
|
|
|
Scope* for_scope = NewScope(scope_, BLOCK_SCOPE);
|
2016-01-14 19:25:09 +00:00
|
|
|
|
|
|
|
BlockState block_state(&scope_, for_scope);
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::FOR, CHECK_OK);
|
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
2014-02-14 12:13:33 +00:00
|
|
|
for_scope->set_start_position(scanner()->location().beg_pos);
|
2014-11-18 18:51:20 +00:00
|
|
|
bool is_let_identifier_expression = false;
|
2015-05-15 09:56:31 +00:00
|
|
|
DeclarationParsingResult parsing_result;
|
2008-07-03 15:10:15 +00:00
|
|
|
if (peek() != Token::SEMICOLON) {
|
2015-07-07 21:57:09 +00:00
|
|
|
if (peek() == Token::VAR || (peek() == Token::CONST && allow_const()) ||
|
2015-08-28 18:47:30 +00:00
|
|
|
(peek() == Token::LET && IsNextLetKeyword())) {
|
2016-02-16 19:09:03 +00:00
|
|
|
ParseVariableDeclarations(kForStatement, &parsing_result, nullptr,
|
|
|
|
CHECK_OK);
|
2015-05-15 09:56:31 +00:00
|
|
|
|
2016-03-10 14:07:12 +00:00
|
|
|
ForEachStatement::VisitMode mode = ForEachStatement::ENUMERATE;
|
2015-02-26 13:48:10 +00:00
|
|
|
int each_beg_pos = scanner()->location().beg_pos;
|
|
|
|
int each_end_pos = scanner()->location().end_pos;
|
2011-06-30 14:37:55 +00:00
|
|
|
|
2016-02-04 18:39:05 +00:00
|
|
|
if (CheckInOrOf(&mode, ok)) {
|
2015-02-19 13:50:33 +00:00
|
|
|
if (!*ok) return nullptr;
|
2016-02-04 18:39:05 +00:00
|
|
|
if (parsing_result.declarations.length() != 1) {
|
2015-05-18 08:34:05 +00:00
|
|
|
ParserTraits::ReportMessageAt(
|
|
|
|
parsing_result.bindings_loc,
|
2016-02-04 18:39:05 +00:00
|
|
|
MessageTemplate::kForInOfLoopMultiBindings,
|
|
|
|
ForEachStatement::VisitModeString(mode));
|
2015-04-08 18:47:36 +00:00
|
|
|
*ok = false;
|
|
|
|
return nullptr;
|
|
|
|
}
|
2015-11-25 01:14:58 +00:00
|
|
|
DeclarationParsingResult::Declaration& decl =
|
|
|
|
parsing_result.declarations[0];
|
2015-05-15 09:56:31 +00:00
|
|
|
if (parsing_result.first_initializer_loc.IsValid() &&
|
2015-09-01 20:03:14 +00:00
|
|
|
(is_strict(language_mode()) || mode == ForEachStatement::ITERATE ||
|
2015-11-25 01:14:58 +00:00
|
|
|
IsLexicalVariableMode(parsing_result.descriptor.mode) ||
|
|
|
|
!decl.pattern->IsVariableProxy())) {
|
2016-02-04 18:39:05 +00:00
|
|
|
ParserTraits::ReportMessageAt(
|
|
|
|
parsing_result.first_initializer_loc,
|
|
|
|
MessageTemplate::kForInOfLoopInitializer,
|
|
|
|
ForEachStatement::VisitModeString(mode));
|
2015-04-07 19:28:33 +00:00
|
|
|
*ok = false;
|
|
|
|
return nullptr;
|
|
|
|
}
|
2015-05-15 09:56:31 +00:00
|
|
|
|
2015-05-21 17:47:07 +00:00
|
|
|
Block* init_block = nullptr;
|
|
|
|
|
|
|
|
// special case for legacy for (var/const x =.... in)
|
2015-07-07 21:57:09 +00:00
|
|
|
if (!IsLexicalVariableMode(parsing_result.descriptor.mode) &&
|
2015-11-25 01:14:58 +00:00
|
|
|
decl.pattern->IsVariableProxy() && decl.initializer != nullptr) {
|
2016-02-01 23:01:13 +00:00
|
|
|
++use_counts_[v8::Isolate::kForInInitializer];
|
2015-11-25 01:14:58 +00:00
|
|
|
const AstRawString* name =
|
|
|
|
decl.pattern->AsVariableProxy()->raw_name();
|
2015-05-21 17:47:07 +00:00
|
|
|
VariableProxy* single_var = scope_->NewUnresolved(
|
2015-11-25 01:14:58 +00:00
|
|
|
factory(), name, Variable::NORMAL, each_beg_pos, each_end_pos);
|
2015-05-21 17:47:07 +00:00
|
|
|
init_block = factory()->NewBlock(
|
|
|
|
nullptr, 2, true, parsing_result.descriptor.declaration_pos);
|
2015-10-01 13:59:36 +00:00
|
|
|
init_block->statements()->Add(
|
2015-05-21 17:47:07 +00:00
|
|
|
factory()->NewExpressionStatement(
|
2015-11-25 01:14:58 +00:00
|
|
|
factory()->NewAssignment(Token::ASSIGN, single_var,
|
|
|
|
decl.initializer,
|
|
|
|
RelocInfo::kNoPosition),
|
2015-05-21 17:47:07 +00:00
|
|
|
RelocInfo::kNoPosition),
|
|
|
|
zone());
|
2015-04-08 18:47:36 +00:00
|
|
|
}
|
2015-05-21 17:47:07 +00:00
|
|
|
|
|
|
|
// Rewrite a for-in/of statement of the form
|
2011-10-17 12:19:06 +00:00
|
|
|
//
|
2015-05-21 17:47:07 +00:00
|
|
|
// for (let/const/var x in/of e) b
|
2011-10-17 12:19:06 +00:00
|
|
|
//
|
|
|
|
// into
|
|
|
|
//
|
2015-07-01 00:27:15 +00:00
|
|
|
// {
|
|
|
|
// <let x' be a temporary variable>
|
|
|
|
// for (x' in/of e) {
|
|
|
|
// let/const/var x;
|
|
|
|
// x = x';
|
|
|
|
// b;
|
|
|
|
// }
|
|
|
|
// let x; // for TDZ
|
2011-10-17 12:19:06 +00:00
|
|
|
// }
|
|
|
|
|
2016-01-14 19:25:09 +00:00
|
|
|
Variable* temp =
|
|
|
|
scope_->NewTemporary(ast_value_factory()->dot_for_string());
|
2013-10-14 09:24:58 +00:00
|
|
|
ForEachStatement* loop =
|
2014-11-12 08:25:59 +00:00
|
|
|
factory()->NewForEachStatement(mode, labels, stmt_pos);
|
2011-10-17 12:19:06 +00:00
|
|
|
Target target(&this->target_stack_, loop);
|
|
|
|
|
2016-01-20 22:04:58 +00:00
|
|
|
Expression* enumerable;
|
|
|
|
if (mode == ForEachStatement::ITERATE) {
|
2016-02-19 15:58:57 +00:00
|
|
|
ExpressionClassifier classifier(this);
|
2016-01-20 22:04:58 +00:00
|
|
|
enumerable = ParseAssignmentExpression(true, &classifier, CHECK_OK);
|
2016-02-19 15:58:57 +00:00
|
|
|
RewriteNonPattern(&classifier, CHECK_OK);
|
2016-01-20 22:04:58 +00:00
|
|
|
} else {
|
|
|
|
enumerable = ParseExpression(true, CHECK_OK);
|
|
|
|
}
|
2015-07-01 00:27:15 +00:00
|
|
|
|
2011-10-17 12:19:06 +00:00
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
|
2015-07-01 00:27:15 +00:00
|
|
|
Scope* body_scope = NewScope(scope_, BLOCK_SCOPE);
|
|
|
|
body_scope->set_start_position(scanner()->location().beg_pos);
|
2015-05-21 17:47:07 +00:00
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
Block* body_block =
|
|
|
|
factory()->NewBlock(NULL, 3, false, RelocInfo::kNoPosition);
|
2015-05-21 12:36:11 +00:00
|
|
|
|
|
|
|
{
|
2016-01-14 19:25:09 +00:00
|
|
|
BlockState block_state(&scope_, body_scope);
|
|
|
|
|
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
|
|
|
Statement* body = ParseScopedStatement(NULL, true, CHECK_OK);
|
2016-01-14 19:25:09 +00:00
|
|
|
|
|
|
|
auto each_initialization_block =
|
|
|
|
factory()->NewBlock(nullptr, 1, true, RelocInfo::kNoPosition);
|
|
|
|
{
|
|
|
|
auto descriptor = parsing_result.descriptor;
|
|
|
|
descriptor.declaration_pos = RelocInfo::kNoPosition;
|
|
|
|
descriptor.initialization_pos = RelocInfo::kNoPosition;
|
|
|
|
decl.initializer = factory()->NewVariableProxy(temp);
|
|
|
|
|
|
|
|
PatternRewriter::DeclareAndInitializeVariables(
|
|
|
|
each_initialization_block, &descriptor, &decl,
|
|
|
|
IsLexicalVariableMode(descriptor.mode) ? &lexical_bindings
|
|
|
|
: nullptr,
|
|
|
|
CHECK_OK);
|
|
|
|
}
|
2015-05-21 17:47:07 +00:00
|
|
|
|
2016-01-14 19:25:09 +00:00
|
|
|
body_block->statements()->Add(each_initialization_block, zone());
|
|
|
|
body_block->statements()->Add(body, zone());
|
|
|
|
VariableProxy* temp_proxy =
|
|
|
|
factory()->NewVariableProxy(temp, each_beg_pos, each_end_pos);
|
2016-03-07 19:52:12 +00:00
|
|
|
InitializeForEachStatement(loop, temp_proxy, enumerable, body_block);
|
2015-05-21 12:36:11 +00:00
|
|
|
}
|
2015-07-01 00:27:15 +00:00
|
|
|
body_scope->set_end_position(scanner()->location().end_pos);
|
|
|
|
body_scope = body_scope->FinalizeBlockScope();
|
[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
|
|
|
body_block->set_scope(body_scope);
|
|
|
|
|
|
|
|
// Create a TDZ for any lexically-bound names.
|
|
|
|
if (IsLexicalVariableMode(parsing_result.descriptor.mode)) {
|
|
|
|
DCHECK_NULL(init_block);
|
|
|
|
|
|
|
|
init_block =
|
|
|
|
factory()->NewBlock(nullptr, 1, false, RelocInfo::kNoPosition);
|
|
|
|
|
|
|
|
for (int i = 0; i < lexical_bindings.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.
|
|
|
|
VariableProxy* tdz_proxy =
|
|
|
|
NewUnresolved(lexical_bindings[i], LET);
|
|
|
|
Declaration* tdz_decl = factory()->NewVariableDeclaration(
|
|
|
|
tdz_proxy, LET, scope_, RelocInfo::kNoPosition);
|
|
|
|
Variable* tdz_var = Declare(
|
|
|
|
tdz_decl, DeclarationDescriptor::NORMAL, true, CHECK_OK);
|
|
|
|
tdz_var->set_initializer_position(position());
|
2015-07-01 00:27:15 +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
|
|
|
}
|
|
|
|
|
|
|
|
Statement* final_loop = loop->IsForOfStatement()
|
|
|
|
? FinalizeForOfStatement(
|
|
|
|
loop->AsForOfStatement(), RelocInfo::kNoPosition)
|
|
|
|
: loop;
|
2015-07-01 00:27:15 +00:00
|
|
|
|
2014-02-14 12:13:33 +00:00
|
|
|
for_scope->set_end_position(scanner()->location().end_pos);
|
2011-10-17 12:19:06 +00:00
|
|
|
for_scope = for_scope->FinalizeBlockScope();
|
2015-05-21 17:47:07 +00:00
|
|
|
// Parsed for-in loop w/ variable declarations.
|
|
|
|
if (init_block != nullptr) {
|
[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
|
|
|
init_block->statements()->Add(final_loop, zone());
|
2016-01-14 19:25:09 +00:00
|
|
|
init_block->set_scope(for_scope);
|
2015-05-21 17:47:07 +00:00
|
|
|
return init_block;
|
|
|
|
} else {
|
2015-07-01 00:27:15 +00:00
|
|
|
DCHECK_NULL(for_scope);
|
[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;
|
2015-05-21 17:47:07 +00:00
|
|
|
}
|
2011-10-17 12:19:06 +00:00
|
|
|
} else {
|
2015-05-21 17:47:07 +00:00
|
|
|
init = parsing_result.BuildInitializationBlock(
|
|
|
|
IsLexicalVariableMode(parsing_result.descriptor.mode)
|
|
|
|
? &lexical_bindings
|
|
|
|
: nullptr,
|
|
|
|
CHECK_OK);
|
2011-10-17 12:19:06 +00:00
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
} else {
|
2015-08-13 18:06:04 +00:00
|
|
|
int lhs_beg_pos = peek_position();
|
2016-02-19 15:58:57 +00:00
|
|
|
ExpressionClassifier classifier(this);
|
2015-12-11 19:38:57 +00:00
|
|
|
Expression* expression = ParseExpression(false, &classifier, CHECK_OK);
|
2015-08-13 18:06:04 +00:00
|
|
|
int lhs_end_pos = scanner()->location().end_pos;
|
2016-03-10 14:07:12 +00:00
|
|
|
ForEachStatement::VisitMode mode = ForEachStatement::ENUMERATE;
|
2014-11-18 18:51:20 +00:00
|
|
|
is_let_identifier_expression =
|
2015-05-21 17:47:07 +00:00
|
|
|
expression->IsVariableProxy() &&
|
|
|
|
expression->AsVariableProxy()->raw_name() ==
|
|
|
|
ast_value_factory()->let_string();
|
2013-06-06 14:38:26 +00:00
|
|
|
|
2015-12-11 19:38:57 +00:00
|
|
|
bool is_for_each = CheckInOrOf(&mode, ok);
|
|
|
|
if (!*ok) return nullptr;
|
2016-03-10 23:19:31 +00:00
|
|
|
bool is_destructuring = is_for_each && (expression->IsArrayLiteral() ||
|
|
|
|
expression->IsObjectLiteral());
|
2015-12-11 19:38:57 +00:00
|
|
|
|
|
|
|
if (is_destructuring) {
|
|
|
|
ValidateAssignmentPattern(&classifier, CHECK_OK);
|
|
|
|
} else {
|
2016-02-19 15:58:57 +00:00
|
|
|
RewriteNonPattern(&classifier, CHECK_OK);
|
2015-12-11 19:38:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (is_for_each) {
|
|
|
|
if (!is_destructuring) {
|
|
|
|
expression = this->CheckAndRewriteReferenceExpression(
|
|
|
|
expression, lhs_beg_pos, lhs_end_pos,
|
|
|
|
MessageTemplate::kInvalidLhsInFor, kSyntaxError, CHECK_OK);
|
|
|
|
}
|
2014-04-02 11:03:05 +00:00
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
ForEachStatement* loop =
|
2014-11-12 08:25:59 +00:00
|
|
|
factory()->NewForEachStatement(mode, labels, stmt_pos);
|
2010-10-27 12:33:48 +00:00
|
|
|
Target target(&this->target_stack_, loop);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2016-01-20 22:04:58 +00:00
|
|
|
Expression* enumerable;
|
|
|
|
if (mode == ForEachStatement::ITERATE) {
|
2016-02-19 15:58:57 +00:00
|
|
|
ExpressionClassifier classifier(this);
|
2016-01-20 22:04:58 +00:00
|
|
|
enumerable = ParseAssignmentExpression(true, &classifier, CHECK_OK);
|
2016-02-19 15:58:57 +00:00
|
|
|
RewriteNonPattern(&classifier, CHECK_OK);
|
2016-01-20 22:04:58 +00:00
|
|
|
} else {
|
|
|
|
enumerable = ParseExpression(true, CHECK_OK);
|
|
|
|
}
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
|
|
|
|
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
|
|
|
// For legacy compat reasons, give for loops similar treatment to
|
|
|
|
// if statements in allowing a function declaration for a body
|
|
|
|
Statement* body = ParseScopedStatement(NULL, true, CHECK_OK);
|
2016-03-07 19:52:12 +00:00
|
|
|
InitializeForEachStatement(loop, expression, enumerable, body);
|
[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* final_loop = loop->IsForOfStatement()
|
|
|
|
? FinalizeForOfStatement(
|
|
|
|
loop->AsForOfStatement(), RelocInfo::kNoPosition)
|
|
|
|
: loop;
|
|
|
|
|
2014-02-14 12:13:33 +00:00
|
|
|
for_scope->set_end_position(scanner()->location().end_pos);
|
2011-10-17 12:19:06 +00:00
|
|
|
for_scope = for_scope->FinalizeBlockScope();
|
2015-10-14 17:36:06 +00:00
|
|
|
DCHECK(for_scope == nullptr);
|
[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;
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
} else {
|
2015-08-13 18:06:04 +00:00
|
|
|
init = factory()->NewExpressionStatement(expression, lhs_beg_pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Standard 'for' loop
|
2014-11-12 08:25:59 +00:00
|
|
|
ForStatement* loop = factory()->NewForStatement(labels, stmt_pos);
|
2010-10-27 12:33:48 +00:00
|
|
|
Target target(&this->target_stack_, loop);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
|
|
|
// Parsed initializer at this point.
|
2014-11-18 18:51:20 +00:00
|
|
|
// Detect attempts at 'let' declarations in sloppy mode.
|
2015-09-18 18:19:53 +00:00
|
|
|
if (!allow_harmony_sloppy_let() && peek() == Token::IDENTIFIER &&
|
|
|
|
is_sloppy(language_mode()) && is_let_identifier_expression) {
|
2015-05-18 08:34:05 +00:00
|
|
|
ReportMessage(MessageTemplate::kSloppyLexical, NULL);
|
2014-11-18 18:51:20 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::SEMICOLON, CHECK_OK);
|
|
|
|
|
2016-01-14 19:25:09 +00:00
|
|
|
Expression* cond = NULL;
|
|
|
|
Statement* next = NULL;
|
|
|
|
Statement* body = NULL;
|
|
|
|
|
2014-05-26 08:07:02 +00:00
|
|
|
// If there are let bindings, then condition and the next statement of the
|
|
|
|
// for loop must be parsed in a new scope.
|
2016-01-14 19:25:09 +00:00
|
|
|
Scope* inner_scope = scope_;
|
2015-03-03 18:34:30 +00:00
|
|
|
if (lexical_bindings.length() > 0) {
|
2014-05-26 08:07:02 +00:00
|
|
|
inner_scope = NewScope(for_scope, BLOCK_SCOPE);
|
|
|
|
inner_scope->set_start_position(scanner()->location().beg_pos);
|
|
|
|
}
|
2016-01-14 19:25:09 +00:00
|
|
|
{
|
|
|
|
BlockState block_state(&scope_, inner_scope);
|
2014-05-26 08:07:02 +00:00
|
|
|
|
2016-01-14 19:25:09 +00:00
|
|
|
if (peek() != Token::SEMICOLON) {
|
|
|
|
cond = ParseExpression(true, CHECK_OK);
|
|
|
|
}
|
|
|
|
Expect(Token::SEMICOLON, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2016-01-14 19:25:09 +00:00
|
|
|
if (peek() != Token::RPAREN) {
|
|
|
|
Expression* exp = ParseExpression(true, CHECK_OK);
|
|
|
|
next = factory()->NewExpressionStatement(exp, exp->position());
|
|
|
|
}
|
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
|
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
|
|
|
body = ParseScopedStatement(NULL, true, CHECK_OK);
|
2016-01-14 19:25:09 +00:00
|
|
|
}
|
2014-05-26 08:07:02 +00:00
|
|
|
|
|
|
|
Statement* result = NULL;
|
2015-03-03 18:34:30 +00:00
|
|
|
if (lexical_bindings.length() > 0) {
|
2016-01-14 19:25:09 +00:00
|
|
|
BlockState block_state(&scope_, for_scope);
|
2015-03-03 18:34:30 +00:00
|
|
|
result = DesugarLexicalBindingsInForStatement(
|
2016-02-04 18:39:05 +00:00
|
|
|
inner_scope, parsing_result.descriptor.mode, &lexical_bindings, loop,
|
|
|
|
init, cond, next, body, CHECK_OK);
|
2014-05-26 08:07:02 +00:00
|
|
|
for_scope->set_end_position(scanner()->location().end_pos);
|
2011-10-17 12:19:06 +00:00
|
|
|
} else {
|
2014-05-26 08:07:02 +00:00
|
|
|
for_scope->set_end_position(scanner()->location().end_pos);
|
2014-07-09 07:50:11 +00:00
|
|
|
for_scope = for_scope->FinalizeBlockScope();
|
|
|
|
if (for_scope) {
|
|
|
|
// Rewrite a for statement of the form
|
|
|
|
// for (const x = i; c; n) b
|
|
|
|
//
|
|
|
|
// into
|
|
|
|
//
|
|
|
|
// {
|
|
|
|
// const x = i;
|
|
|
|
// for (; c; n) b
|
|
|
|
// }
|
2015-10-05 18:52:48 +00:00
|
|
|
//
|
|
|
|
// or, desugar
|
|
|
|
// for (; c; n) b
|
|
|
|
// into
|
|
|
|
// {
|
|
|
|
// for (; c; n) b
|
|
|
|
// }
|
|
|
|
// just in case b introduces a lexical binding some other way, e.g., if b
|
|
|
|
// is a FunctionDeclaration.
|
2014-07-09 07:50:11 +00:00
|
|
|
Block* block =
|
|
|
|
factory()->NewBlock(NULL, 2, false, RelocInfo::kNoPosition);
|
2015-10-05 18:52:48 +00:00
|
|
|
if (init != nullptr) {
|
|
|
|
block->statements()->Add(init, zone());
|
|
|
|
}
|
2015-10-01 13:59:36 +00:00
|
|
|
block->statements()->Add(loop, zone());
|
2014-07-09 07:50:11 +00:00
|
|
|
block->set_scope(for_scope);
|
|
|
|
loop->Initialize(NULL, cond, next, body);
|
|
|
|
result = block;
|
|
|
|
} else {
|
|
|
|
loop->Initialize(init, cond, next, body);
|
|
|
|
result = loop;
|
|
|
|
}
|
2011-10-17 12:19:06 +00:00
|
|
|
}
|
2014-05-26 08:07:02 +00:00
|
|
|
return result;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
DebuggerStatement* Parser::ParseDebuggerStatement(bool* ok) {
|
|
|
|
// In ECMA-262 'debugger' is defined as a reserved keyword. In some browser
|
|
|
|
// contexts this is used as a statement which invokes the debugger as i a
|
|
|
|
// break point is present.
|
|
|
|
// DebuggerStatement ::
|
|
|
|
// 'debugger' ';'
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::DEBUGGER, CHECK_OK);
|
|
|
|
ExpectSemicolon(CHECK_OK);
|
2013-10-14 09:24:58 +00:00
|
|
|
return factory()->NewDebuggerStatement(pos);
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-03-23 07:27:47 +00:00
|
|
|
bool CompileTimeValue::IsCompileTimeValue(Expression* expression) {
|
2014-06-03 07:40:43 +00:00
|
|
|
if (expression->IsLiteral()) return true;
|
2009-03-23 07:27:47 +00:00
|
|
|
MaterializedLiteral* lit = expression->AsMaterializedLiteral();
|
|
|
|
return lit != NULL && lit->is_simple();
|
|
|
|
}
|
|
|
|
|
2010-08-16 16:06:46 +00:00
|
|
|
|
2013-09-04 07:05:11 +00:00
|
|
|
Handle<FixedArray> CompileTimeValue::GetValue(Isolate* isolate,
|
|
|
|
Expression* expression) {
|
|
|
|
Factory* factory = isolate->factory();
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(IsCompileTimeValue(expression));
|
2013-06-04 10:30:05 +00:00
|
|
|
Handle<FixedArray> result = factory->NewFixedArray(2, TENURED);
|
2009-03-23 07:27:47 +00:00
|
|
|
ObjectLiteral* object_literal = expression->AsObjectLiteral();
|
|
|
|
if (object_literal != NULL) {
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(object_literal->is_simple());
|
2010-03-11 10:34:29 +00:00
|
|
|
if (object_literal->fast_elements()) {
|
2013-06-06 13:28:22 +00:00
|
|
|
result->set(kLiteralTypeSlot, Smi::FromInt(OBJECT_LITERAL_FAST_ELEMENTS));
|
2010-03-11 10:34:29 +00:00
|
|
|
} else {
|
2013-06-06 13:28:22 +00:00
|
|
|
result->set(kLiteralTypeSlot, Smi::FromInt(OBJECT_LITERAL_SLOW_ELEMENTS));
|
2010-03-11 10:34:29 +00:00
|
|
|
}
|
2009-03-23 07:27:47 +00:00
|
|
|
result->set(kElementsSlot, *object_literal->constant_properties());
|
|
|
|
} else {
|
|
|
|
ArrayLiteral* array_literal = expression->AsArrayLiteral();
|
2014-08-04 11:34:54 +00:00
|
|
|
DCHECK(array_literal != NULL && array_literal->is_simple());
|
2013-06-06 13:28:22 +00:00
|
|
|
result->set(kLiteralTypeSlot, Smi::FromInt(ARRAY_LITERAL));
|
2009-12-22 12:41:45 +00:00
|
|
|
result->set(kElementsSlot, *array_literal->constant_elements());
|
2009-03-23 07:27:47 +00:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-06-06 13:28:22 +00:00
|
|
|
CompileTimeValue::LiteralType CompileTimeValue::GetLiteralType(
|
|
|
|
Handle<FixedArray> value) {
|
|
|
|
Smi* literal_type = Smi::cast(value->get(kLiteralTypeSlot));
|
|
|
|
return static_cast<LiteralType>(literal_type->value());
|
2009-03-23 07:27:47 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Handle<FixedArray> CompileTimeValue::GetElements(Handle<FixedArray> value) {
|
|
|
|
return Handle<FixedArray>(FixedArray::cast(value->get(kElementsSlot)));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-10 16:11:25 +00:00
|
|
|
void ParserTraits::ParseArrowFunctionFormalParameters(
|
2015-07-23 11:53:31 +00:00
|
|
|
ParserFormalParameters* parameters, Expression* expr,
|
2015-08-26 14:59:05 +00:00
|
|
|
const Scanner::Location& params_loc, bool* ok) {
|
2015-08-05 12:00:41 +00:00
|
|
|
if (parameters->Arity() >= Code::kMaxArguments) {
|
2015-05-18 08:34:05 +00:00
|
|
|
ReportMessageAt(params_loc, MessageTemplate::kMalformedArrowFunParamList);
|
2015-04-21 14:44:18 +00:00
|
|
|
*ok = false;
|
|
|
|
return;
|
|
|
|
}
|
2015-04-17 09:51:22 +00:00
|
|
|
|
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();
|
2015-08-26 14:59:05 +00:00
|
|
|
ParseArrowFunctionFormalParameters(parameters, left, params_loc, ok);
|
2015-04-21 14:44:18 +00:00
|
|
|
if (!*ok) return;
|
|
|
|
// 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;
|
2015-06-22 14:15:53 +00:00
|
|
|
if (expr->IsVariableProxy()) {
|
|
|
|
// When the formal parameter was originally seen, it was parsed as a
|
|
|
|
// VariableProxy and recorded as unresolved in the scope. Here we undo that
|
|
|
|
// parse-time side-effect for parameters that are single-names (not
|
|
|
|
// patterns; for patterns that happens uniformly in
|
|
|
|
// PatternRewriter::VisitVariableProxy).
|
|
|
|
parser_->scope_->RemoveUnresolved(expr->AsVariableProxy());
|
2015-08-24 18:00:59 +00:00
|
|
|
} else if (expr->IsAssignment()) {
|
|
|
|
Assignment* assignment = expr->AsAssignment();
|
|
|
|
DCHECK(!assignment->is_compound());
|
|
|
|
initializer = assignment->value();
|
|
|
|
expr = assignment->target();
|
2015-10-21 12:04:10 +00:00
|
|
|
|
|
|
|
// TODO(adamk): Only call this if necessary.
|
|
|
|
RewriteParameterInitializerScope(parser_->stack_limit(), initializer,
|
|
|
|
parser_->scope_, parameters->scope);
|
2015-08-17 12:01:55 +00:00
|
|
|
}
|
|
|
|
|
2015-11-25 01:30:33 +00:00
|
|
|
// TODO(adamk): params_loc.end_pos is not the correct initializer position,
|
|
|
|
// but it should be conservative enough to trigger hole checks for variables
|
|
|
|
// referenced in the initializer (if any).
|
|
|
|
AddFormalParameter(parameters, expr, initializer, params_loc.end_pos,
|
|
|
|
is_rest);
|
2015-08-05 12:00:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-10-21 02:55:47 +00:00
|
|
|
DoExpression* Parser::ParseDoExpression(bool* ok) {
|
|
|
|
// AssignmentExpression ::
|
|
|
|
// do '{' StatementList '}'
|
|
|
|
int pos = peek_position();
|
|
|
|
|
|
|
|
Expect(Token::DO, CHECK_OK);
|
|
|
|
Variable* result =
|
|
|
|
scope_->NewTemporary(ast_value_factory()->dot_result_string());
|
2016-01-12 19:53:34 +00:00
|
|
|
Block* block = ParseBlock(nullptr, false, CHECK_OK);
|
2015-10-21 02:55:47 +00:00
|
|
|
DoExpression* expr = factory()->NewDoExpression(block, result, pos);
|
|
|
|
if (!Rewriter::Rewrite(this, expr, ast_value_factory())) {
|
|
|
|
*ok = false;
|
|
|
|
return nullptr;
|
|
|
|
}
|
2016-01-12 19:53:34 +00:00
|
|
|
block->set_scope(block->scope()->FinalizeBlockScope());
|
2015-10-21 02:55:47 +00:00
|
|
|
return expr;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-08-05 12:00:41 +00:00
|
|
|
void ParserTraits::ParseArrowFunctionFormalParameterList(
|
|
|
|
ParserFormalParameters* parameters, Expression* expr,
|
|
|
|
const Scanner::Location& params_loc,
|
|
|
|
Scanner::Location* duplicate_loc, bool* ok) {
|
2015-08-26 09:36:39 +00:00
|
|
|
if (expr->IsEmptyParentheses()) return;
|
|
|
|
|
2015-08-26 14:59:05 +00:00
|
|
|
ParseArrowFunctionFormalParameters(parameters, expr, params_loc, ok);
|
2015-08-05 12:00:41 +00:00
|
|
|
if (!*ok) return;
|
|
|
|
|
2016-02-19 15:58:57 +00:00
|
|
|
Type::ExpressionClassifier classifier(parser_);
|
2015-08-26 14:59:05 +00:00
|
|
|
if (!parameters->is_simple) {
|
|
|
|
classifier.RecordNonSimpleParameter();
|
|
|
|
}
|
2015-08-05 12:00:41 +00:00
|
|
|
for (int i = 0; i < parameters->Arity(); ++i) {
|
|
|
|
auto parameter = parameters->at(i);
|
2015-08-26 14:59:05 +00:00
|
|
|
DeclareFormalParameter(parameters->scope, parameter, &classifier);
|
2015-08-05 12:00:41 +00:00
|
|
|
if (!duplicate_loc->IsValid()) {
|
|
|
|
*duplicate_loc = classifier.duplicate_formal_parameter_error().location;
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-07-23 11:53:31 +00:00
|
|
|
void ParserTraits::ReindexLiterals(const ParserFormalParameters& parameters) {
|
2015-06-26 21:39:43 +00:00
|
|
|
if (parser_->function_state_->materialized_literal_count() > 0) {
|
|
|
|
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
|
|
|
|
2015-06-26 21:39:43 +00:00
|
|
|
DCHECK(reindexer.count() <=
|
|
|
|
parser_->function_state_->materialized_literal_count());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = function_token_pos == RelocInfo::kNoPosition
|
|
|
|
? peek_position() : function_token_pos;
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2011-08-16 14:24:12 +00:00
|
|
|
// Function declarations are function scoped in normal mode, so they are
|
|
|
|
// hoisted. In harmony block scoping mode they are block scoped, so they
|
|
|
|
// are not hoisted.
|
2013-08-23 09:25:37 +00:00
|
|
|
//
|
|
|
|
// One tricky case are function declarations in a local sloppy-mode eval:
|
|
|
|
// their declaration is hoisted, but they still see the local scope. E.g.,
|
|
|
|
//
|
|
|
|
// function() {
|
|
|
|
// var x = 0
|
|
|
|
// try { throw 1 } catch (x) { eval("function g() { return x }") }
|
|
|
|
// return g()
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// needs to return 1. To distinguish such cases, we need to detect
|
|
|
|
// (1) whether a function stems from a sloppy eval, and
|
|
|
|
// (2) whether it actually hoists across the eval.
|
|
|
|
// Unfortunately, we do not represent sloppy eval scopes, so we do not have
|
|
|
|
// either information available directly, especially not when lazily compiling
|
|
|
|
// a function like 'g'. We hence rely on the following invariants:
|
|
|
|
// - (1) is the case iff the innermost scope of the deserialized scope chain
|
|
|
|
// under which we compile is _not_ a declaration scope. This holds because
|
|
|
|
// in all normal cases, function declarations are fully hoisted to a
|
|
|
|
// declaration scope and compiled relative to that.
|
|
|
|
// - (2) is the case iff the current declaration scope is still the original
|
|
|
|
// one relative to the deserialized scope chain. Otherwise we must be
|
|
|
|
// compiling a function in an inner declaration scope in the eval, e.g. a
|
|
|
|
// nested function, and hoisting works normally relative to that.
|
2014-02-12 12:02:07 +00:00
|
|
|
Scope* declaration_scope = scope_->DeclarationScope();
|
2013-08-23 09:25:37 +00:00
|
|
|
Scope* original_declaration_scope = original_scope_->DeclarationScope();
|
2016-01-08 20:38:07 +00:00
|
|
|
Scope* scope = function_type == FunctionLiteral::kDeclaration &&
|
2015-08-22 00:18:12 +00:00
|
|
|
is_sloppy(language_mode) &&
|
|
|
|
!allow_harmony_sloppy_function() &&
|
2015-03-13 15:15:42 +00:00
|
|
|
(original_scope_ == original_declaration_scope ||
|
|
|
|
declaration_scope != original_declaration_scope)
|
|
|
|
? NewScope(declaration_scope, FUNCTION_SCOPE, kind)
|
|
|
|
: NewScope(scope_, FUNCTION_SCOPE, kind);
|
2015-11-05 19:52:36 +00:00
|
|
|
SetLanguageMode(scope, language_mode);
|
2011-11-11 13:48:14 +00:00
|
|
|
ZoneList<Statement*>* body = NULL;
|
2015-08-04 14:24:13 +00:00
|
|
|
int arity = -1;
|
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-04-24 11:07:50 +00:00
|
|
|
FunctionLiteral::EagerCompileHint eager_compile_hint =
|
|
|
|
parenthesized_function_ ? FunctionLiteral::kShouldEagerCompile
|
|
|
|
: FunctionLiteral::kShouldLazyCompile;
|
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;
|
2015-07-23 11:53:31 +00:00
|
|
|
// Parse function.
|
2014-06-24 14:03:24 +00:00
|
|
|
{
|
2014-11-14 13:13:09 +00:00
|
|
|
AstNodeFactory function_factory(ast_value_factory());
|
2015-02-06 10:34:50 +00:00
|
|
|
FunctionState function_state(&function_state_, &scope_, scope, kind,
|
2014-10-09 08:16:13 +00:00
|
|
|
&function_factory);
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_->SetScopeName(function_name);
|
2016-02-19 15:58:57 +00:00
|
|
|
ExpressionClassifier formals_classifier(this, &duplicate_finder);
|
2013-04-15 12:29:44 +00:00
|
|
|
|
|
|
|
if (is_generator) {
|
|
|
|
// For generators, allocating variables in contexts is currently a win
|
|
|
|
// because it minimizes the work needed to suspend and resume an
|
2016-03-17 14:43:31 +00:00
|
|
|
// activation. The machine code produced for generators (by full-codegen)
|
|
|
|
// relies on this forced context allocation, but not in an essential way.
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_->ForceContextAllocation();
|
2013-04-15 12:29:44 +00:00
|
|
|
|
|
|
|
// Calling a generator returns a generator object. That object is stored
|
|
|
|
// in a temporary variable, a definition that is used by "yield"
|
2014-02-13 16:17:55 +00:00
|
|
|
// expressions. This also marks the FunctionState as a generator.
|
2015-07-23 13:51:27 +00:00
|
|
|
Variable* temp = scope_->NewTemporary(
|
2014-09-11 09:52:36 +00:00
|
|
|
ast_value_factory()->dot_generator_object_string());
|
2013-04-15 12:29:44 +00:00
|
|
|
function_state.set_generator_object_variable(temp);
|
|
|
|
}
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2015-04-13 08:07:05 +00:00
|
|
|
Expect(Token::LPAREN, CHECK_OK);
|
2015-04-21 11:09:53 +00:00
|
|
|
int start_position = scanner()->location().beg_pos;
|
|
|
|
scope_->set_start_position(start_position);
|
2015-07-23 11:53:31 +00:00
|
|
|
ParserFormalParameters formals(scope);
|
2015-08-04 14:24:13 +00:00
|
|
|
ParseFormalParameterList(&formals, &formals_classifier, CHECK_OK);
|
2015-08-05 12:00:41 +00:00
|
|
|
arity = formals.Arity();
|
2015-04-17 09:51:22 +00:00
|
|
|
Expect(Token::RPAREN, CHECK_OK);
|
2015-04-21 11:09:53 +00:00
|
|
|
int formals_end_position = scanner()->location().end_pos;
|
|
|
|
|
2016-02-19 02:50:58 +00:00
|
|
|
CheckArityRestrictions(arity, kind, formals.has_rest, start_position,
|
2015-06-22 14:15:53 +00:00
|
|
|
formals_end_position, CHECK_OK);
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::LBRACE, CHECK_OK);
|
|
|
|
|
[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
|
|
|
// Don't include the rest parameter into the function's formal parameter
|
|
|
|
// count (esp. the SharedFunctionInfo::internal_formal_parameter_count,
|
|
|
|
// which says whether we need to create an arguments adaptor frame).
|
|
|
|
if (formals.has_rest) arity--;
|
|
|
|
|
2014-02-19 14:50:33 +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:
|
2011-11-25 09:36:31 +00:00
|
|
|
// - It must not have been prohibited by the caller to Parse (some callers
|
|
|
|
// need a full AST).
|
2012-06-28 14:56:28 +00:00
|
|
|
// - The outer scope must allow lazy compilation of inner functions.
|
2011-11-25 09:36:31 +00:00
|
|
|
// - 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.
|
2014-02-19 14:50:33 +00:00
|
|
|
|
|
|
|
// In addition, we need to distinguish between these cases:
|
|
|
|
// (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
|
|
|
|
// immediately). The inner function *must* be parsed eagerly to resolve the
|
|
|
|
// possible reference to the variable in foo's scope. However, it's possible
|
|
|
|
// that it will be compiled lazily.
|
|
|
|
|
|
|
|
// To make this additional case work, both Parser and PreParser implement a
|
|
|
|
// logic where only top-level functions will be parsed lazily.
|
2015-06-29 16:16:05 +00:00
|
|
|
bool is_lazily_parsed = mode() == PARSE_LAZILY &&
|
|
|
|
scope_->AllowsLazyParsing() &&
|
|
|
|
!parenthesized_function_;
|
2011-01-14 10:50:13 +00:00
|
|
|
parenthesized_function_ = false; // The bit was set for this function only.
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2015-05-06 10:21:20 +00:00
|
|
|
// Eager or lazy parse?
|
|
|
|
// If is_lazily_parsed, we'll parse lazy. If we can set a bookmark, we'll
|
|
|
|
// pass it to SkipLazyFunctionBody, which may use it to abort lazy
|
|
|
|
// parsing if it suspect that wasn't a good idea. If so, or if we didn't
|
|
|
|
// try to lazy parse in the first place, we'll have to parse eagerly.
|
|
|
|
Scanner::BookmarkScope bookmark(scanner());
|
2014-02-19 14:50:33 +00:00
|
|
|
if (is_lazily_parsed) {
|
2015-09-07 07:49:56 +00:00
|
|
|
Scanner::BookmarkScope* maybe_bookmark =
|
|
|
|
bookmark.Set() ? &bookmark : nullptr;
|
2015-04-22 11:04:25 +00:00
|
|
|
SkipLazyFunctionBody(&materialized_literal_count,
|
2015-05-06 10:21:20 +00:00
|
|
|
&expected_property_count, /*CHECK_OK*/ ok,
|
|
|
|
maybe_bookmark);
|
|
|
|
|
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
|
|
|
|
2015-05-06 10:21:20 +00:00
|
|
|
if (bookmark.HasBeenReset()) {
|
|
|
|
// Trigger eager (re-)parsing, just below this block.
|
|
|
|
is_lazily_parsed = false;
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!is_lazily_parsed) {
|
2015-09-10 14:40:57 +00:00
|
|
|
// Determine whether the function body can be discarded after parsing.
|
|
|
|
// 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.
|
2015-09-17 09:38:22 +00:00
|
|
|
bool use_temp_zone =
|
2015-09-10 14:40:57 +00:00
|
|
|
FLAG_lazy && !allow_natives() && extension_ == NULL && allow_lazy() &&
|
2016-01-08 20:38:07 +00:00
|
|
|
function_type == FunctionLiteral::kDeclaration &&
|
2015-09-10 14:40:57 +00:00
|
|
|
eager_compile_hint != FunctionLiteral::kShouldEagerCompile;
|
|
|
|
// Open a new BodyScope, 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.
|
|
|
|
// For the purpose of scope analysis, some ZoneObjects allocated by the
|
|
|
|
// factory must persist after the function body is thrown away and
|
|
|
|
// temp_zone is deallocated. These objects are instead allocated in a
|
|
|
|
// parser-persistent zone (see parser_zone_ in AstNodeFactory).
|
|
|
|
{
|
|
|
|
Zone temp_zone;
|
2015-09-17 09:38:22 +00:00
|
|
|
AstNodeFactory::BodyScope inner(factory(), &temp_zone, use_temp_zone);
|
2015-09-10 14:40:57 +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();
|
2015-09-17 09:38:22 +00:00
|
|
|
if (use_temp_zone) {
|
2015-09-10 14:40:57 +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 = NULL;
|
|
|
|
}
|
2015-07-15 09:14:49 +00:00
|
|
|
}
|
2015-04-22 11:04:25 +00:00
|
|
|
|
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);
|
2015-07-15 09:14:49 +00:00
|
|
|
ValidateFormalParameters(&formals_classifier, language_mode,
|
2015-05-13 11:45:04 +00:00
|
|
|
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);
|
2015-07-10 16:39:47 +00:00
|
|
|
}
|
2015-09-21 04:30:50 +00:00
|
|
|
if (is_sloppy(language_mode) && allow_harmony_sloppy_function()) {
|
|
|
|
InsertSloppyBlockFunctionVarBindings(scope, CHECK_OK);
|
|
|
|
}
|
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
|
|
|
|
ParserTraits::RewriteDestructuringAssignments();
|
|
|
|
}
|
2016-02-19 15:58:57 +00:00
|
|
|
has_duplicate_parameters =
|
|
|
|
!formals_classifier.is_valid_formal_parameter_list_without_duplicates();
|
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
|
|
|
|
2014-07-21 09:58:01 +00:00
|
|
|
FunctionLiteral* function_literal = factory()->NewFunctionLiteral(
|
2016-01-08 20:38:07 +00:00
|
|
|
function_name, scope, body, materialized_literal_count,
|
|
|
|
expected_property_count, arity, duplicate_parameters, function_type,
|
2015-06-08 18:19:32 +00:00
|
|
|
eager_compile_hint, kind, 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
|
|
|
|
2011-08-08 16:14:46 +00:00
|
|
|
if (fni_ != NULL && should_infer_name) fni_->AddFunction(function_literal);
|
2011-04-07 14:45:34 +00:00
|
|
|
return function_literal;
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-04-22 11:04:25 +00:00
|
|
|
void Parser::SkipLazyFunctionBody(int* materialized_literal_count,
|
2015-05-06 10:21:20 +00:00
|
|
|
int* expected_property_count, bool* ok,
|
|
|
|
Scanner::BookmarkScope* bookmark) {
|
|
|
|
DCHECK_IMPLIES(bookmark, bookmark->HasBeenSet());
|
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();
|
2014-12-08 11:47:44 +00:00
|
|
|
if (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);
|
|
|
|
|
|
|
|
scope_->set_end_position(entry.end_pos());
|
|
|
|
Expect(Token::RBRACE, ok);
|
|
|
|
if (!*ok) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
total_preparse_skipped_ += scope_->end_position() - function_block_pos;
|
|
|
|
*materialized_literal_count = entry.literal_count();
|
|
|
|
*expected_property_count = entry.property_count();
|
2015-11-05 19:52:36 +00:00
|
|
|
SetLanguageMode(scope_, entry.language_mode());
|
2015-02-13 18:34:52 +00:00
|
|
|
if (entry.uses_super_property()) scope_->RecordSuperPropertyUsage();
|
2015-06-04 21:16:18 +00:00
|
|
|
if (entry.calls_eval()) scope_->RecordEvalCall();
|
2014-04-15 08:29:24 +00:00
|
|
|
return;
|
|
|
|
}
|
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 =
|
2015-05-06 10:21:20 +00:00
|
|
|
ParseLazyFunctionBodyWithPreParser(&logger, bookmark);
|
|
|
|
if (bookmark && bookmark->HasBeenReset()) {
|
|
|
|
return; // Return immediately if pre-parser devided to abort parsing.
|
|
|
|
}
|
2014-12-08 11:47:44 +00:00
|
|
|
if (result == PreParser::kPreParseStackOverflow) {
|
|
|
|
// Propagate stack overflow.
|
|
|
|
set_stack_overflow();
|
|
|
|
*ok = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (logger.has_error()) {
|
|
|
|
ParserTraits::ReportMessageAt(
|
|
|
|
Scanner::Location(logger.start(), logger.end()), logger.message(),
|
2015-02-20 21:19:43 +00:00
|
|
|
logger.argument_opt(), logger.error_type());
|
2014-12-08 11:47:44 +00:00
|
|
|
*ok = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
scope_->set_end_position(logger.end());
|
|
|
|
Expect(Token::RBRACE, ok);
|
|
|
|
if (!*ok) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
total_preparse_skipped_ += scope_->end_position() - function_block_pos;
|
|
|
|
*materialized_literal_count = logger.literals();
|
|
|
|
*expected_property_count = logger.properties();
|
2015-11-05 19:52:36 +00:00
|
|
|
SetLanguageMode(scope_, logger.language_mode());
|
2015-06-04 21:16:18 +00:00
|
|
|
if (logger.uses_super_property()) {
|
2015-02-13 18:34:52 +00:00
|
|
|
scope_->RecordSuperPropertyUsage();
|
|
|
|
}
|
2015-06-04 21:16:18 +00:00
|
|
|
if (logger.calls_eval()) {
|
|
|
|
scope_->RecordEvalCall();
|
|
|
|
}
|
2014-12-08 11:47:44 +00:00
|
|
|
if (produce_cached_parse_data()) {
|
|
|
|
DCHECK(log_);
|
|
|
|
// Position right after terminal '}'.
|
|
|
|
int body_end = scanner()->location().end_pos;
|
|
|
|
log_->LogFunction(function_block_pos, body_end, *materialized_literal_count,
|
2015-02-13 18:34:52 +00:00
|
|
|
*expected_property_count, scope_->language_mode(),
|
2015-06-04 21:16:18 +00:00
|
|
|
scope_->uses_super_property(), scope_->calls_eval());
|
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(
|
|
|
|
Token::OR, factory()->NewCompareOperation(
|
|
|
|
Token::EQ_STRICT, factory()->NewVariableProxy(var),
|
|
|
|
factory()->NewUndefinedLiteral(RelocInfo::kNoPosition),
|
|
|
|
RelocInfo::kNoPosition),
|
|
|
|
factory()->NewCompareOperation(
|
|
|
|
Token::EQ_STRICT, factory()->NewVariableProxy(var),
|
|
|
|
factory()->NewNullLiteral(RelocInfo::kNoPosition),
|
|
|
|
RelocInfo::kNoPosition),
|
|
|
|
RelocInfo::kNoPosition);
|
|
|
|
Expression* throw_type_error = this->NewThrowTypeError(
|
|
|
|
MessageTemplate::kNonCoercible, ast_value_factory()->empty_string(),
|
|
|
|
RelocInfo::kNoPosition);
|
|
|
|
IfStatement* if_statement = factory()->NewIfStatement(
|
|
|
|
condition, factory()->NewExpressionStatement(throw_type_error,
|
|
|
|
RelocInfo::kNoPosition),
|
|
|
|
factory()->NewEmptyStatement(RelocInfo::kNoPosition),
|
|
|
|
RelocInfo::kNoPosition);
|
|
|
|
return if_statement;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-12-04 17:20:10 +00:00
|
|
|
class InitializerRewriter : public AstExpressionVisitor {
|
|
|
|
public:
|
|
|
|
InitializerRewriter(uintptr_t stack_limit, Expression* root, Parser* parser,
|
|
|
|
Scope* scope)
|
|
|
|
: AstExpressionVisitor(stack_limit, root),
|
|
|
|
parser_(parser),
|
|
|
|
scope_(scope) {}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void VisitExpression(Expression* expr) {
|
2016-02-19 15:58:57 +00:00
|
|
|
RewritableExpression* to_rewrite = expr->AsRewritableExpression();
|
2015-12-04 17:20:10 +00:00
|
|
|
if (to_rewrite == nullptr || to_rewrite->is_rewritten()) return;
|
|
|
|
|
|
|
|
Parser::PatternRewriter::RewriteDestructuringAssignment(parser_, to_rewrite,
|
2015-12-11 19:38:57 +00:00
|
|
|
scope_);
|
2015-12-04 17:20:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
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);
|
2015-06-22 14:15:53 +00:00
|
|
|
DCHECK(scope_->is_function_scope());
|
2015-07-16 16:44:58 +00:00
|
|
|
Block* init_block =
|
|
|
|
factory()->NewBlock(NULL, 1, true, RelocInfo::kNoPosition);
|
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;
|
|
|
|
descriptor.parser = this;
|
|
|
|
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.
|
|
|
|
//
|
|
|
|
// TODO(adamk): Should this be RelocInfo::kNoPosition, since
|
|
|
|
// 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-11-25 01:30:33 +00:00
|
|
|
// The initializer position which will end up in,
|
|
|
|
// Variable::initializer_position(), used for hole check elimination.
|
|
|
|
int initializer_position = 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
|
|
|
|
RewriteParameterInitializer(parameter.initializer, scope_);
|
|
|
|
|
2015-08-17 12:01:55 +00:00
|
|
|
auto condition = factory()->NewCompareOperation(
|
|
|
|
Token::EQ_STRICT,
|
|
|
|
factory()->NewVariableProxy(parameters.scope->parameter(i)),
|
|
|
|
factory()->NewUndefinedLiteral(RelocInfo::kNoPosition),
|
|
|
|
RelocInfo::kNoPosition);
|
|
|
|
initial_value = factory()->NewConditional(
|
|
|
|
condition, parameter.initializer, initial_value,
|
|
|
|
RelocInfo::kNoPosition);
|
|
|
|
descriptor.initialization_pos = parameter.initializer->position();
|
2015-11-25 01:30:33 +00:00
|
|
|
initializer_position = parameter.initializer_end_position;
|
2015-08-17 12:01:55 +00:00
|
|
|
}
|
[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
|
|
|
|
|
|
|
Scope* param_scope = scope_;
|
|
|
|
Block* param_block = init_block;
|
2015-08-25 22:24:17 +00:00
|
|
|
if (!parameter.is_simple() && scope_->calls_sloppy_eval()) {
|
[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_scope = NewScope(scope_, BLOCK_SCOPE);
|
|
|
|
param_scope->set_is_declaration_scope();
|
|
|
|
param_scope->set_start_position(parameter.pattern->position());
|
|
|
|
param_scope->set_end_position(RelocInfo::kNoPosition);
|
|
|
|
param_scope->RecordEvalCall();
|
|
|
|
param_block = factory()->NewBlock(NULL, 8, true, RelocInfo::kNoPosition);
|
|
|
|
param_block->set_scope(param_scope);
|
|
|
|
descriptor.hoist_scope = scope_;
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
BlockState block_state(&scope_, param_scope);
|
|
|
|
DeclarationParsingResult::Declaration decl(
|
2015-11-25 01:30:33 +00:00
|
|
|
parameter.pattern, initializer_position, initial_value);
|
[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
|
|
|
PatternRewriter::DeclareAndInitializeVariables(param_block, &descriptor,
|
|
|
|
&decl, nullptr, CHECK_OK);
|
|
|
|
}
|
|
|
|
|
2015-08-25 22:24:17 +00:00
|
|
|
if (!parameter.is_simple() && scope_->calls_sloppy_eval()) {
|
[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_scope = param_scope->FinalizeBlockScope();
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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) {
|
2014-04-15 08:29:24 +00:00
|
|
|
// Everything inside an eagerly parsed function will be parsed eagerly
|
|
|
|
// (see comment above).
|
|
|
|
ParsingModeScope parsing_mode(this, PARSE_EAGERLY);
|
2015-07-15 10:59:52 +00:00
|
|
|
ZoneList<Statement*>* result = new(zone()) ZoneList<Statement*>(8, zone());
|
2014-04-15 08:29:24 +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;
|
[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
|
|
|
Scope* inner_scope = 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) {
|
2015-07-15 10:59:52 +00:00
|
|
|
inner_scope = NewScope(scope_, BLOCK_SCOPE);
|
|
|
|
inner_scope->set_is_declaration_scope();
|
|
|
|
inner_scope->set_start_position(scanner()->location().beg_pos);
|
2015-07-16 16:44:58 +00:00
|
|
|
inner_block = factory()->NewBlock(NULL, 8, true, RelocInfo::kNoPosition);
|
|
|
|
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
|
|
|
{
|
[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
|
|
|
BlockState block_state(&scope_, 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-01-27 08:12:30 +00:00
|
|
|
// finally { %GeneratorClose(generator) }
|
|
|
|
//
|
|
|
|
// - 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 =
|
|
|
|
factory()->NewBlock(nullptr, 3, false, RelocInfo::kNoPosition);
|
|
|
|
|
|
|
|
{
|
|
|
|
ZoneList<Expression*>* arguments =
|
|
|
|
new (zone()) ZoneList<Expression*>(0, zone());
|
|
|
|
CallRuntime* allocation = factory()->NewCallRuntime(
|
|
|
|
Runtime::kCreateJSGeneratorObject, arguments, pos);
|
|
|
|
VariableProxy* init_proxy = factory()->NewVariableProxy(
|
|
|
|
function_state_->generator_object_variable());
|
|
|
|
Assignment* assignment = factory()->NewAssignment(
|
|
|
|
Token::INIT, init_proxy, allocation, RelocInfo::kNoPosition);
|
|
|
|
VariableProxy* get_proxy = factory()->NewVariableProxy(
|
|
|
|
function_state_->generator_object_variable());
|
2016-03-06 09:19:14 +00:00
|
|
|
Yield* yield =
|
|
|
|
factory()->NewYield(get_proxy, assignment, RelocInfo::kNoPosition);
|
2016-01-27 08:12:30 +00:00
|
|
|
try_block->statements()->Add(
|
|
|
|
factory()->NewExpressionStatement(yield, RelocInfo::kNoPosition),
|
|
|
|
zone());
|
|
|
|
}
|
2014-04-15 08:29:24 +00:00
|
|
|
|
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(
|
|
|
|
BuildIteratorResult(nullptr, true), RelocInfo::kNoPosition);
|
|
|
|
try_block->statements()->Add(final_return, zone());
|
2016-01-27 08:12:30 +00:00
|
|
|
|
|
|
|
Block* finally_block =
|
|
|
|
factory()->NewBlock(nullptr, 1, false, RelocInfo::kNoPosition);
|
|
|
|
ZoneList<Expression*>* args =
|
|
|
|
new (zone()) ZoneList<Expression*>(1, zone());
|
|
|
|
VariableProxy* call_proxy = factory()->NewVariableProxy(
|
|
|
|
function_state_->generator_object_variable());
|
|
|
|
args->Add(call_proxy, zone());
|
|
|
|
Expression* call = factory()->NewCallRuntime(
|
|
|
|
Runtime::kGeneratorClose, args, RelocInfo::kNoPosition);
|
|
|
|
finally_block->statements()->Add(
|
|
|
|
factory()->NewExpressionStatement(call, RelocInfo::kNoPosition),
|
|
|
|
zone());
|
|
|
|
|
|
|
|
body->Add(factory()->NewTryFinallyStatement(try_block, finally_block,
|
|
|
|
RelocInfo::kNoPosition),
|
|
|
|
zone());
|
|
|
|
} 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)) {
|
|
|
|
body->Add(
|
|
|
|
factory()->NewReturnStatement(
|
|
|
|
this->ThisExpression(scope_, factory(), RelocInfo::kNoPosition),
|
|
|
|
RelocInfo::kNoPosition),
|
|
|
|
zone());
|
|
|
|
}
|
2015-02-06 10:34:50 +00:00
|
|
|
}
|
|
|
|
|
2014-04-15 08:29:24 +00:00
|
|
|
Expect(Token::RBRACE, CHECK_OK);
|
|
|
|
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);
|
|
|
|
DCHECK_EQ(body, inner_block->statements());
|
2015-11-05 19:52:36 +00:00
|
|
|
SetLanguageMode(scope_, inner_scope->language_mode());
|
2015-07-23 11:53:31 +00:00
|
|
|
Block* init_block = BuildParameterInitializationBlock(parameters, CHECK_OK);
|
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);
|
|
|
|
inner_scope = inner_scope->FinalizeBlockScope();
|
2015-07-20 13:48:57 +00:00
|
|
|
if (inner_scope != nullptr) {
|
|
|
|
CheckConflictingVarDeclarations(inner_scope, CHECK_OK);
|
2015-10-01 10:42:23 +00:00
|
|
|
InsertShadowingVarBindingInitializers(inner_block);
|
2015-07-20 13:48:57 +00:00
|
|
|
}
|
2015-07-16 16:44:58 +00:00
|
|
|
|
|
|
|
result->Add(init_block, zone());
|
|
|
|
result->Add(inner_block, zone());
|
2015-07-15 10:59:52 +00:00
|
|
|
}
|
2014-04-15 08:29:24 +00:00
|
|
|
|
2016-01-08 20:38:07 +00:00
|
|
|
if (function_type == FunctionLiteral::kNamedExpression) {
|
2015-08-03 09:14:19 +00:00
|
|
|
// Now that we know the language mode, we can create the const assignment
|
|
|
|
// in the previously reserved spot.
|
|
|
|
// NOTE: We create a proxy and resolve it here so that in the
|
|
|
|
// future we can change the AST to only refer to VariableProxies
|
|
|
|
// instead of Variables and Proxies as is the case now.
|
2015-11-16 17:07:46 +00:00
|
|
|
VariableMode fvar_mode = is_strict(language_mode()) ? CONST : CONST_LEGACY;
|
2015-08-03 09:14:19 +00:00
|
|
|
Variable* fvar = new (zone())
|
|
|
|
Variable(scope_, function_name, fvar_mode, Variable::NORMAL,
|
|
|
|
kCreatedInitialized, kNotAssigned);
|
|
|
|
VariableProxy* proxy = factory()->NewVariableProxy(fvar);
|
|
|
|
VariableDeclaration* fvar_declaration = factory()->NewVariableDeclaration(
|
|
|
|
proxy, fvar_mode, scope_, RelocInfo::kNoPosition);
|
|
|
|
scope_->DeclareFunctionVar(fvar_declaration);
|
|
|
|
|
2015-10-09 21:47:40 +00:00
|
|
|
VariableProxy* fproxy = factory()->NewVariableProxy(fvar);
|
2015-08-03 09:14:19 +00:00
|
|
|
result->Set(kFunctionNameAssignmentIndex,
|
|
|
|
factory()->NewExpressionStatement(
|
2015-11-16 17:07:46 +00:00
|
|
|
factory()->NewAssignment(Token::INIT, fproxy,
|
2015-08-03 09:14:19 +00:00
|
|
|
factory()->NewThisFunction(pos),
|
|
|
|
RelocInfo::kNoPosition),
|
|
|
|
RelocInfo::kNoPosition));
|
|
|
|
}
|
|
|
|
|
2016-02-05 08:46:25 +00:00
|
|
|
// ES6 14.6.1 Static Semantics: IsInTailPosition
|
2016-02-11 17:39:43 +00:00
|
|
|
// Mark collected return expressions that are in tail call position.
|
|
|
|
const List<Expression*>& expressions_in_tail_position =
|
|
|
|
function_state_->expressions_in_tail_position();
|
|
|
|
for (int i = 0; i < expressions_in_tail_position.length(); ++i) {
|
|
|
|
expressions_in_tail_position[i]->MarkTail();
|
2016-02-05 08:46:25 +00:00
|
|
|
}
|
2015-07-15 10:59:52 +00:00
|
|
|
return result;
|
2014-04-15 08:29:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
|
2015-05-06 10:21:20 +00:00
|
|
|
SingletonLogger* logger, Scanner::BookmarkScope* bookmark) {
|
2014-09-02 11:36:21 +00:00
|
|
|
// This function may be called on a background thread too; record only the
|
|
|
|
// main thread preparse times.
|
|
|
|
if (pre_parse_timer_ != NULL) {
|
|
|
|
pre_parse_timer_->Start();
|
|
|
|
}
|
2016-02-18 06:12:45 +00:00
|
|
|
TRACE_EVENT0("v8", "V8.PreParse");
|
|
|
|
|
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);
|
2016-03-18 01:09:00 +00:00
|
|
|
SET_ALLOW(legacy_const);
|
2015-06-09 15:43:07 +00:00
|
|
|
SET_ALLOW(harmony_sloppy);
|
2015-07-25 00:05:08 +00:00
|
|
|
SET_ALLOW(harmony_sloppy_let);
|
2015-10-21 02:55:47 +00:00
|
|
|
SET_ALLOW(harmony_do_expressions);
|
2015-12-12 00:20:01 +00:00
|
|
|
SET_ALLOW(harmony_function_name);
|
2016-02-17 00:18:38 +00:00
|
|
|
SET_ALLOW(harmony_function_sent);
|
2015-06-09 15:43:07 +00:00
|
|
|
#undef SET_ALLOW
|
2011-11-25 09:36:31 +00:00
|
|
|
}
|
2015-02-04 09:34:05 +00:00
|
|
|
PreParser::PreParseResult result = reusable_preparser_->PreParseLazyFunction(
|
2015-08-26 14:59:05 +00:00
|
|
|
language_mode(), function_state_->kind(), scope_->has_simple_parameters(),
|
|
|
|
logger, bookmark);
|
2014-09-02 11:36:21 +00:00
|
|
|
if (pre_parse_timer_ != NULL) {
|
|
|
|
pre_parse_timer_->Stop();
|
|
|
|
}
|
2011-11-25 09:36:31 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-11-14 15:05:05 +00:00
|
|
|
ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name,
|
|
|
|
Scanner::Location class_name_location,
|
|
|
|
bool name_is_strict_reserved, int pos,
|
|
|
|
bool* ok) {
|
|
|
|
// All parts of a ClassDeclaration and ClassExpression are strict code.
|
|
|
|
if (name_is_strict_reserved) {
|
2015-05-18 08:34:05 +00:00
|
|
|
ReportMessageAt(class_name_location,
|
|
|
|
MessageTemplate::kUnexpectedStrictReserved);
|
2014-11-14 15:05:05 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (IsEvalOrArguments(name)) {
|
2015-05-18 08:34:05 +00:00
|
|
|
ReportMessageAt(class_name_location, MessageTemplate::kStrictEvalArguments);
|
2014-11-14 15:05:05 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
Scope* block_scope = NewScope(scope_, BLOCK_SCOPE);
|
|
|
|
BlockState block_state(&scope_, block_scope);
|
2015-11-05 19:52:36 +00:00
|
|
|
RaiseLanguageMode(STRICT);
|
2014-11-14 15:05:05 +00:00
|
|
|
scope_->SetScopeName(name);
|
|
|
|
|
|
|
|
VariableProxy* proxy = NULL;
|
|
|
|
if (name != NULL) {
|
2015-02-17 20:51:24 +00:00
|
|
|
proxy = NewUnresolved(name, CONST);
|
2016-02-18 17:19:35 +00:00
|
|
|
Declaration* declaration =
|
|
|
|
factory()->NewVariableDeclaration(proxy, CONST, block_scope, pos);
|
2015-06-22 14:15:53 +00:00
|
|
|
Declare(declaration, DeclarationDescriptor::NORMAL, true, CHECK_OK);
|
2014-11-14 15:05:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Expression* extends = NULL;
|
|
|
|
if (Check(Token::EXTENDS)) {
|
|
|
|
block_scope->set_start_position(scanner()->location().end_pos);
|
2016-02-19 15:58:57 +00:00
|
|
|
ExpressionClassifier classifier(this);
|
2015-04-22 12:35:05 +00:00
|
|
|
extends = ParseLeftHandSideExpression(&classifier, CHECK_OK);
|
2016-02-19 15:58:57 +00:00
|
|
|
RewriteNonPattern(&classifier, CHECK_OK);
|
2014-11-14 15:05:05 +00:00
|
|
|
} else {
|
|
|
|
block_scope->set_start_position(scanner()->location().end_pos);
|
|
|
|
}
|
|
|
|
|
2015-01-29 23:12:25 +00:00
|
|
|
|
|
|
|
ClassLiteralChecker checker(this);
|
2014-11-14 15:05:05 +00:00
|
|
|
ZoneList<ObjectLiteral::Property*>* properties = NewPropertyList(4, zone());
|
2015-01-22 06:17:11 +00:00
|
|
|
FunctionLiteral* constructor = NULL;
|
2014-11-14 15:05:05 +00:00
|
|
|
bool has_seen_constructor = false;
|
|
|
|
|
|
|
|
Expect(Token::LBRACE, CHECK_OK);
|
2015-02-26 13:48:10 +00:00
|
|
|
|
2015-02-03 17:42:41 +00:00
|
|
|
const bool has_extends = extends != nullptr;
|
2014-11-14 15:05:05 +00:00
|
|
|
while (peek() != Token::RBRACE) {
|
|
|
|
if (Check(Token::SEMICOLON)) continue;
|
2015-12-10 19:18:54 +00:00
|
|
|
FuncNameInferrer::State fni_state(fni_);
|
2014-11-14 15:05:05 +00:00
|
|
|
const bool in_class = true;
|
|
|
|
const bool is_static = false;
|
2015-01-15 20:02:20 +00:00
|
|
|
bool is_computed_name = false; // Classes do not care about computed
|
|
|
|
// property names here.
|
2016-02-19 15:58:57 +00:00
|
|
|
ExpressionClassifier classifier(this);
|
2016-02-01 17:44:23 +00:00
|
|
|
const AstRawString* property_name = nullptr;
|
2015-01-29 23:12:25 +00:00
|
|
|
ObjectLiteral::Property* property = ParsePropertyDefinition(
|
2015-02-03 17:42:41 +00:00
|
|
|
&checker, in_class, has_extends, is_static, &is_computed_name,
|
2016-02-01 17:44:23 +00:00
|
|
|
&has_seen_constructor, &classifier, &property_name, CHECK_OK);
|
2016-02-19 15:58:57 +00:00
|
|
|
RewriteNonPattern(&classifier, CHECK_OK);
|
2014-11-14 15:05:05 +00:00
|
|
|
|
|
|
|
if (has_seen_constructor && constructor == NULL) {
|
2015-01-22 06:17:11 +00:00
|
|
|
constructor = GetPropertyValue(property)->AsFunctionLiteral();
|
|
|
|
DCHECK_NOT_NULL(constructor);
|
2016-02-01 17:44:23 +00:00
|
|
|
constructor->set_raw_name(
|
|
|
|
name != nullptr ? name : ast_value_factory()->empty_string());
|
2014-11-14 15:05:05 +00:00
|
|
|
} else {
|
|
|
|
properties->Add(property, zone());
|
|
|
|
}
|
|
|
|
|
2015-12-10 19:18:54 +00:00
|
|
|
if (fni_ != NULL) fni_->Infer();
|
2016-01-06 23:38:28 +00:00
|
|
|
|
2016-02-01 17:44:23 +00:00
|
|
|
if (allow_harmony_function_name() &&
|
|
|
|
property_name != ast_value_factory()->constructor_string()) {
|
|
|
|
SetFunctionNameFromPropertyName(property, property_name);
|
2016-01-06 23:38:28 +00:00
|
|
|
}
|
2014-11-14 15:05:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Expect(Token::RBRACE, CHECK_OK);
|
|
|
|
int end_pos = scanner()->location().end_pos;
|
|
|
|
|
|
|
|
if (constructor == NULL) {
|
2016-02-01 17:44:23 +00:00
|
|
|
constructor = DefaultConstructor(name, extends != NULL, block_scope, pos,
|
|
|
|
end_pos, block_scope->language_mode());
|
2014-11-14 15:05:05 +00:00
|
|
|
}
|
|
|
|
|
2016-03-10 12:43:51 +00:00
|
|
|
// Note that we do not finalize this block scope because it is
|
|
|
|
// used as a sentinel value indicating an anonymous class.
|
2014-11-14 15:05:05 +00:00
|
|
|
block_scope->set_end_position(end_pos);
|
|
|
|
|
|
|
|
if (name != NULL) {
|
|
|
|
DCHECK_NOT_NULL(proxy);
|
2015-02-26 18:36:59 +00:00
|
|
|
proxy->var()->set_initializer_position(end_pos);
|
2014-11-14 15:05:05 +00:00
|
|
|
}
|
|
|
|
|
2016-02-01 17:44:23 +00:00
|
|
|
return factory()->NewClassLiteral(block_scope, proxy, extends, constructor,
|
|
|
|
properties, pos, end_pos);
|
2014-11-14 15:05:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
Expression* Parser::ParseV8Intrinsic(bool* ok) {
|
|
|
|
// CallRuntime ::
|
|
|
|
// '%' Identifier Arguments
|
|
|
|
|
2013-10-14 09:24:58 +00:00
|
|
|
int pos = peek_position();
|
2008-07-03 15:10:15 +00:00
|
|
|
Expect(Token::MOD, CHECK_OK);
|
2014-02-05 16:26:48 +00:00
|
|
|
// Allow "eval" or "arguments" for backward compatibility.
|
2015-04-10 12:04:51 +00:00
|
|
|
const AstRawString* name = ParseIdentifier(kAllowRestrictedIdentifiers,
|
|
|
|
CHECK_OK);
|
2015-04-09 19:37:14 +00:00
|
|
|
Scanner::Location spread_pos;
|
2016-02-19 15:58:57 +00:00
|
|
|
ExpressionClassifier classifier(this);
|
2015-04-22 12:35:05 +00:00
|
|
|
ZoneList<Expression*>* args =
|
|
|
|
ParseArguments(&spread_pos, &classifier, CHECK_OK);
|
2015-04-09 19:37:14 +00:00
|
|
|
|
|
|
|
DCHECK(!spread_pos.IsValid());
|
2010-09-14 14:52:53 +00:00
|
|
|
|
|
|
|
if (extension_ != NULL) {
|
2008-07-03 15:10:15 +00:00
|
|
|
// The extension structures are only accessible while parsing the
|
|
|
|
// very first time not when reparsing because of lazy compilation.
|
2014-02-12 12:02:07 +00:00
|
|
|
scope_->DeclarationScope()->ForceEagerCompilation();
|
2008-07-03 15:10:15 +00:00
|
|
|
}
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
const Runtime::Function* function = Runtime::FunctionForName(name->string());
|
2008-07-03 15:10:15 +00:00
|
|
|
|
2015-08-26 11:16:38 +00:00
|
|
|
if (function != NULL) {
|
2015-08-27 11:13:56 +00:00
|
|
|
// Check for possible name clash.
|
|
|
|
DCHECK_EQ(Context::kNotFound,
|
|
|
|
Context::IntrinsicIndexForName(name->string()));
|
2015-08-26 11:16:38 +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() != NULL) {
|
|
|
|
return args->at(0);
|
|
|
|
} else {
|
|
|
|
ReportMessage(MessageTemplate::kNotIsvar);
|
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check that the expected number of arguments are being passed.
|
|
|
|
if (function->nargs != -1 && function->nargs != args->length()) {
|
2016-03-01 17:21:06 +00:00
|
|
|
ReportMessage(MessageTemplate::kRuntimeWrongNumArgs);
|
2010-09-14 14:52:53 +00:00
|
|
|
*ok = false;
|
2008-07-03 15:10:15 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2015-08-26 11:16:38 +00:00
|
|
|
return factory()->NewCallRuntime(function, args, pos);
|
2012-08-14 10:06:34 +00:00
|
|
|
}
|
|
|
|
|
2015-08-26 11:16:38 +00:00
|
|
|
int context_index = Context::IntrinsicIndexForName(name->string());
|
|
|
|
|
|
|
|
// Check that the function is defined.
|
|
|
|
if (context_index == Context::kNotFound) {
|
2015-05-18 08:34:05 +00:00
|
|
|
ParserTraits::ReportMessage(MessageTemplate::kNotDefined, name);
|
2012-08-14 10:06:34 +00:00
|
|
|
*ok = false;
|
|
|
|
return NULL;
|
2010-03-11 09:27:12 +00:00
|
|
|
}
|
|
|
|
|
2015-08-26 11:16:38 +00:00
|
|
|
return factory()->NewCallRuntime(context_index, args, pos);
|
2008-07-03 15:10:15 +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();
|
|
|
|
Scanner::Location location = position == RelocInfo::kNoPosition
|
|
|
|
? Scanner::Location::invalid()
|
|
|
|
: Scanner::Location(position, position + 1);
|
2015-05-18 08:34:05 +00:00
|
|
|
ParserTraits::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();
|
|
|
|
for (int i = 0; i < decls->length(); ++i) {
|
|
|
|
Declaration* decl = decls->at(i);
|
|
|
|
if (decl->mode() != VAR || !decl->IsVariableDeclaration()) continue;
|
|
|
|
const AstRawString* name = decl->proxy()->raw_name();
|
|
|
|
Variable* parameter = function_scope->LookupLocal(name);
|
|
|
|
if (parameter == nullptr) continue;
|
|
|
|
VariableProxy* to = inner_scope->NewUnresolved(factory(), name);
|
|
|
|
VariableProxy* from = factory()->NewVariableProxy(parameter);
|
|
|
|
Expression* assignment = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, to, from, RelocInfo::kNoPosition);
|
|
|
|
Statement* statement = factory()->NewExpressionStatement(
|
|
|
|
assignment, RelocInfo::kNoPosition);
|
|
|
|
inner_block->statements()->InsertAt(0, statement, zone());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-09-21 04:30:50 +00:00
|
|
|
void Parser::InsertSloppyBlockFunctionVarBindings(Scope* scope, bool* ok) {
|
|
|
|
// For each variable which is used as a function declaration in a sloppy
|
|
|
|
// block,
|
|
|
|
DCHECK(scope->is_declaration_scope());
|
|
|
|
SloppyBlockFunctionMap* map = scope->sloppy_block_function_map();
|
|
|
|
for (ZoneHashMap::Entry* p = map->Start(); p != nullptr; p = map->Next(p)) {
|
|
|
|
AstRawString* name = static_cast<AstRawString*>(p->key);
|
|
|
|
// If the variable wouldn't conflict with a lexical declaration,
|
|
|
|
Variable* var = scope->LookupLocal(name);
|
|
|
|
if (var == nullptr || !IsLexicalVariableMode(var->mode())) {
|
|
|
|
// Declare a var-style binding for the function in the outer scope
|
|
|
|
VariableProxy* proxy = scope->NewUnresolved(factory(), name);
|
|
|
|
Declaration* declaration = factory()->NewVariableDeclaration(
|
|
|
|
proxy, VAR, scope, RelocInfo::kNoPosition);
|
|
|
|
Declare(declaration, DeclarationDescriptor::NORMAL, true, ok, scope);
|
|
|
|
DCHECK(ok); // Based on the preceding check, this should not fail
|
|
|
|
if (!ok) return;
|
|
|
|
|
|
|
|
// Write in assignments to var for each block-scoped function declaration
|
|
|
|
auto delegates = static_cast<SloppyBlockFunctionMap::Vector*>(p->value);
|
|
|
|
for (SloppyBlockFunctionStatement* delegate : *delegates) {
|
|
|
|
// Read from the local lexical scope and write to the function scope
|
|
|
|
VariableProxy* to = scope->NewUnresolved(factory(), name);
|
|
|
|
VariableProxy* from = delegate->scope()->NewUnresolved(factory(), name);
|
|
|
|
Expression* assignment = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, to, from, RelocInfo::kNoPosition);
|
|
|
|
Statement* statement = factory()->NewExpressionStatement(
|
|
|
|
assignment, RelocInfo::kNoPosition);
|
|
|
|
delegate->set_statement(statement);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-07-03 15:10:15 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Parser support
|
|
|
|
|
2014-06-24 14:03:24 +00:00
|
|
|
bool Parser::TargetStackContainsLabel(const AstRawString* label) {
|
2009-05-15 14:58:02 +00:00
|
|
|
for (Target* 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;
|
2009-05-15 14:58:02 +00:00
|
|
|
for (Target* 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;
|
2009-05-15 14:58:02 +00:00
|
|
|
for (Target* 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) {
|
2014-07-02 07:01:31 +00:00
|
|
|
if (scanner_.source_url()->length() > 0) {
|
2015-02-20 09:39:32 +00:00
|
|
|
Handle<String> source_url = scanner_.source_url()->Internalize(isolate);
|
|
|
|
script->set_source_url(*source_url);
|
2014-07-02 07:01:31 +00:00
|
|
|
}
|
|
|
|
if (scanner_.source_mapping_url()->length() > 0) {
|
2014-07-02 12:01:56 +00:00
|
|
|
Handle<String> source_mapping_url =
|
2015-02-20 09:39:32 +00:00
|
|
|
scanner_.source_mapping_url()->Internalize(isolate);
|
|
|
|
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) {
|
2014-09-02 11:36:21 +00:00
|
|
|
// Internalize strings.
|
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) {
|
|
|
|
for (int i = 0; i < use_counts_[feature]; ++i) {
|
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();
|
|
|
|
pre_parse_timer_ = isolate->counters()->pre_parse();
|
2014-11-20 10:51:49 +00:00
|
|
|
if (FLAG_trace_parse || allow_natives() || extension_ != NULL) {
|
2014-06-24 14:03:24 +00:00
|
|
|
// If intrinsics are allowed, the Parser cannot operate independent of the
|
2014-09-02 11:36:21 +00:00
|
|
|
// V8 heap because of Runtime. Tell the string table to internalize strings
|
2014-06-24 14:03:24 +00:00
|
|
|
// and values right after they're created.
|
2015-02-12 13:02:30 +00:00
|
|
|
ast_value_factory()->Internalize(isolate);
|
2014-06-24 14:03:24 +00:00
|
|
|
}
|
|
|
|
|
2015-02-12 13:02:30 +00:00
|
|
|
if (info->is_lazy()) {
|
|
|
|
DCHECK(!info->is_eval());
|
|
|
|
if (info->shared_info()->is_function()) {
|
2015-02-20 09:39:32 +00:00
|
|
|
result = ParseLazy(isolate, info);
|
2012-02-14 14:14:51 +00:00
|
|
|
} else {
|
2015-02-20 09:39:32 +00:00
|
|
|
result = ParseProgram(isolate, info);
|
2012-02-14 14:14:51 +00:00
|
|
|
}
|
2010-02-01 10:31:55 +00:00
|
|
|
} else {
|
2015-02-12 13:02:30 +00:00
|
|
|
SetCachedData(info);
|
2015-02-20 09:39:32 +00:00
|
|
|
result = ParseProgram(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);
|
2014-09-11 09:52:36 +00:00
|
|
|
DCHECK(ast_value_factory()->IsInternalized());
|
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;
|
|
|
|
fni_ = new (zone()) FuncNameInferrer(ast_value_factory(), zone());
|
|
|
|
|
|
|
|
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
|
|
|
|
2015-02-12 13:02:30 +00:00
|
|
|
DCHECK(info->source_stream() != NULL);
|
|
|
|
ExternalStreamingStream stream(info->source_stream(),
|
|
|
|
info->source_stream_encoding());
|
2014-09-12 09:12:08 +00:00
|
|
|
scanner_.Initialize(&stream);
|
2015-02-12 13:02:30 +00:00
|
|
|
DCHECK(info->context().is_null() || info->context()->IsNativeContext());
|
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.
|
2015-04-16 12:42:43 +00:00
|
|
|
result = DoParseProgram(info);
|
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
|
|
|
|
|
|
|
|
|
|
|
ParserTraits::TemplateLiteralState Parser::OpenTemplateLiteral(int pos) {
|
|
|
|
return new (zone()) ParserTraits::TemplateLiteral(zone(), pos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
ZoneList<v8::internal::Expression*>* Parser::PrepareSpreadArguments(
|
|
|
|
ZoneList<v8::internal::Expression*>* list) {
|
|
|
|
ZoneList<v8::internal::Expression*>* args =
|
|
|
|
new (zone()) ZoneList<v8::internal::Expression*>(1, zone());
|
|
|
|
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,
|
|
|
|
spread_list, RelocInfo::kNoPosition),
|
|
|
|
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()) {
|
|
|
|
ZoneList<v8::internal::Expression*>* unspread =
|
|
|
|
new (zone()) ZoneList<v8::internal::Expression*>(1, zone());
|
|
|
|
|
|
|
|
// 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,
|
|
|
|
RelocInfo::kNoPosition),
|
|
|
|
zone());
|
|
|
|
|
|
|
|
if (i == n) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push eagerly spread argument
|
|
|
|
ZoneList<v8::internal::Expression*>* spread_list =
|
|
|
|
new (zone()) ZoneList<v8::internal::Expression*>(1, zone());
|
|
|
|
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,
|
|
|
|
spread_list, RelocInfo::kNoPosition),
|
2015-04-09 19:37:14 +00:00
|
|
|
zone());
|
|
|
|
}
|
|
|
|
|
|
|
|
list = new (zone()) ZoneList<v8::internal::Expression*>(1, zone());
|
2015-08-26 11:16:38 +00:00
|
|
|
list->Add(factory()->NewCallRuntime(Context::SPREAD_ARGUMENTS_INDEX, args,
|
|
|
|
RelocInfo::kNoPosition),
|
2015-04-09 19:37:14 +00:00
|
|
|
zone());
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
UNREACHABLE();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Expression* Parser::SpreadCall(Expression* function,
|
|
|
|
ZoneList<v8::internal::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()) {
|
2015-05-19 14:51:10 +00:00
|
|
|
Expression* home =
|
|
|
|
ThisExpression(scope_, factory(), RelocInfo::kNoPosition);
|
2015-05-14 22:59:22 +00:00
|
|
|
args->InsertAt(0, function, zone());
|
|
|
|
args->InsertAt(1, home, zone());
|
|
|
|
} else {
|
|
|
|
Variable* temp =
|
|
|
|
scope_->NewTemporary(ast_value_factory()->empty_string());
|
|
|
|
VariableProxy* obj = factory()->NewVariableProxy(temp);
|
|
|
|
Assignment* assign_obj = factory()->NewAssignment(
|
|
|
|
Token::ASSIGN, obj, function->AsProperty()->obj(),
|
|
|
|
RelocInfo::kNoPosition);
|
|
|
|
function = factory()->NewProperty(
|
|
|
|
assign_obj, function->AsProperty()->key(), RelocInfo::kNoPosition);
|
|
|
|
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());
|
|
|
|
args->InsertAt(1, factory()->NewUndefinedLiteral(RelocInfo::kNoPosition),
|
|
|
|
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,
|
|
|
|
ZoneList<v8::internal::Expression*>* args,
|
|
|
|
int pos) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Parser::RaiseLanguageMode(LanguageMode mode) {
|
2016-03-10 12:43:51 +00:00
|
|
|
LanguageMode old = scope_->language_mode();
|
|
|
|
SetLanguageMode(scope_, old > mode ? old : mode);
|
2015-11-05 19:52:36 +00:00
|
|
|
}
|
|
|
|
|
2015-12-04 17:20:10 +00:00
|
|
|
|
|
|
|
void ParserTraits::RewriteDestructuringAssignments() {
|
|
|
|
parser_->RewriteDestructuringAssignments();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-02-19 15:58:57 +00:00
|
|
|
void ParserTraits::RewriteNonPattern(Type::ExpressionClassifier* classifier,
|
|
|
|
bool* ok) {
|
|
|
|
parser_->RewriteNonPattern(classifier, ok);
|
2016-02-19 14:03:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-02-19 15:58:57 +00:00
|
|
|
Zone* ParserTraits::zone() const {
|
|
|
|
return parser_->function_state_->scope()->zone();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ZoneList<Expression*>* ParserTraits::GetNonPatternList() const {
|
|
|
|
return parser_->function_state_->non_patterns_to_rewrite();
|
2016-01-14 15:46:45 +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-02-01 09:18:09 +00:00
|
|
|
void VisitObjectLiteralProperty(ObjectLiteralProperty* property) override {
|
|
|
|
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-02-19 15:58:57 +00:00
|
|
|
void Parser::RewriteNonPattern(ExpressionClassifier* classifier, bool* ok) {
|
2016-01-14 15:46:45 +00:00
|
|
|
ValidateExpression(classifier, ok);
|
2016-02-19 15:58:57 +00:00
|
|
|
if (!*ok) return;
|
|
|
|
auto non_patterns_to_rewrite = function_state_->non_patterns_to_rewrite();
|
|
|
|
int begin = classifier->GetNonPatternBegin();
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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...
|
|
|
|
Variable* result =
|
|
|
|
scope_->NewTemporary(ast_value_factory()->dot_result_string());
|
|
|
|
// 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
|
|
|
|
Expression* init_result =
|
|
|
|
factory()->NewAssignment(Token::INIT, factory()->NewVariableProxy(result),
|
|
|
|
lit, RelocInfo::kNoPosition);
|
|
|
|
Block* do_block =
|
|
|
|
factory()->NewBlock(nullptr, 16, false, RelocInfo::kNoPosition);
|
|
|
|
do_block->statements()->Add(
|
|
|
|
factory()->NewExpressionStatement(init_result, RelocInfo::kNoPosition),
|
|
|
|
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)
|
|
|
|
ZoneList<Expression*>* append_element_args = NewExpressionList(2, zone());
|
|
|
|
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,
|
|
|
|
RelocInfo::kNoPosition),
|
|
|
|
RelocInfo::kNoPosition),
|
|
|
|
zone());
|
|
|
|
} else {
|
|
|
|
// If it's a spread, we're adding a for/of loop iterating through it.
|
|
|
|
Variable* each =
|
|
|
|
scope_->NewTemporary(ast_value_factory()->dot_for_string());
|
|
|
|
Expression* subject = spread->expression();
|
|
|
|
// %AppendElement($R, each)
|
|
|
|
Statement* append_body;
|
|
|
|
{
|
|
|
|
ZoneList<Expression*>* append_element_args =
|
|
|
|
NewExpressionList(2, zone());
|
|
|
|
append_element_args->Add(factory()->NewVariableProxy(result), zone());
|
|
|
|
append_element_args->Add(factory()->NewVariableProxy(each), zone());
|
|
|
|
append_body = factory()->NewExpressionStatement(
|
|
|
|
factory()->NewCallRuntime(Runtime::kAppendElement,
|
|
|
|
append_element_args,
|
|
|
|
RelocInfo::kNoPosition),
|
|
|
|
RelocInfo::kNoPosition);
|
|
|
|
}
|
|
|
|
// for (each of spread) %AppendElement($R, each)
|
|
|
|
ForEachStatement* loop = factory()->NewForEachStatement(
|
|
|
|
ForEachStatement::ITERATE, nullptr, RelocInfo::kNoPosition);
|
2016-03-07 19:52:12 +00:00
|
|
|
InitializeForOfStatement(loop->AsForOfStatement(),
|
|
|
|
factory()->NewVariableProxy(each), subject,
|
|
|
|
append_body, spread->expression_position());
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-12-04 17:20:10 +00:00
|
|
|
void ParserTraits::QueueDestructuringAssignmentForRewriting(Expression* expr) {
|
2016-02-19 15:58:57 +00:00
|
|
|
DCHECK(expr->IsRewritableExpression());
|
2015-12-04 17:20:10 +00:00
|
|
|
parser_->function_state_->AddDestructuringAssignment(
|
|
|
|
Parser::DestructuringAssignment(expr, parser_->scope_));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-02-19 15:58:57 +00:00
|
|
|
void ParserTraits::QueueNonPatternForRewriting(Expression* expr) {
|
|
|
|
DCHECK(expr->IsRewritableExpression());
|
|
|
|
parser_->function_state_->AddNonPatternForRewriting(expr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-06 23:38:28 +00:00
|
|
|
void ParserTraits::SetFunctionNameFromPropertyName(
|
|
|
|
ObjectLiteralProperty* property, const AstRawString* name) {
|
|
|
|
Expression* value = property->value();
|
|
|
|
|
2016-02-04 22:36:15 +00:00
|
|
|
// Computed name setting must happen at runtime.
|
2016-01-06 23:38:28 +00:00
|
|
|
if (property->is_computed_name()) return;
|
2016-02-04 22:36:15 +00:00
|
|
|
|
2016-02-19 02:50:58 +00:00
|
|
|
// Getter and setter names are handled here because their names
|
|
|
|
// change in ES2015, even though they are not anonymous.
|
|
|
|
auto function = value->AsFunctionLiteral();
|
|
|
|
if (function != nullptr) {
|
|
|
|
bool is_getter = property->kind() == ObjectLiteralProperty::GETTER;
|
|
|
|
bool is_setter = property->kind() == ObjectLiteralProperty::SETTER;
|
|
|
|
if (is_getter || is_setter) {
|
|
|
|
DCHECK_NOT_NULL(name);
|
|
|
|
const AstRawString* prefix =
|
|
|
|
is_getter ? parser_->ast_value_factory()->get_space_string()
|
|
|
|
: parser_->ast_value_factory()->set_space_string();
|
|
|
|
function->set_raw_name(
|
|
|
|
parser_->ast_value_factory()->NewConsString(prefix, name));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!value->IsAnonymousFunctionDefinition()) return;
|
2016-01-06 23:38:28 +00:00
|
|
|
DCHECK_NOT_NULL(name);
|
|
|
|
|
|
|
|
// 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-02-01 17:44:23 +00:00
|
|
|
if (function != nullptr) {
|
2016-02-19 02:50:58 +00:00
|
|
|
function->set_raw_name(name);
|
|
|
|
DCHECK_EQ(ObjectLiteralProperty::COMPUTED, property->kind());
|
2016-01-06 23:38:28 +00:00
|
|
|
} else {
|
|
|
|
DCHECK(value->IsClassLiteral());
|
|
|
|
DCHECK_EQ(ObjectLiteralProperty::COMPUTED, property->kind());
|
2016-02-01 17:44:23 +00:00
|
|
|
value->AsClassLiteral()->constructor()->set_raw_name(name);
|
2016-01-06 23:38:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-13 23:35:32 +00:00
|
|
|
void ParserTraits::SetFunctionNameFromIdentifierRef(Expression* value,
|
|
|
|
Expression* identifier) {
|
2016-02-01 17:44:23 +00:00
|
|
|
if (!value->IsAnonymousFunctionDefinition()) return;
|
2016-01-13 23:35:32 +00:00
|
|
|
if (!identifier->IsVariableProxy()) return;
|
|
|
|
|
|
|
|
auto name = identifier->AsVariableProxy()->raw_name();
|
|
|
|
DCHECK_NOT_NULL(name);
|
|
|
|
|
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 {
|
|
|
|
DCHECK(value->IsClassLiteral());
|
2016-02-01 17:44:23 +00:00
|
|
|
value->AsClassLiteral()->constructor()->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;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// output.value;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// IteratorClose(iterator) expands to the following:
|
|
|
|
//
|
|
|
|
// let iteratorReturn = iterator.return;
|
|
|
|
// if (IS_NULL_OR_UNDEFINED(iteratorReturn)) return;
|
2016-02-04 16:36:11 +00:00
|
|
|
// 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
|
|
|
|
|
|
|
|
|
|
|
Expression* ParserTraits::RewriteYieldStar(
|
|
|
|
Expression* generator, Expression* iterable, int pos) {
|
|
|
|
|
|
|
|
const int nopos = RelocInfo::kNoPosition;
|
|
|
|
|
|
|
|
auto factory = parser_->factory();
|
|
|
|
auto avfactory = parser_->ast_value_factory();
|
|
|
|
auto scope = parser_->scope_;
|
|
|
|
auto zone = parser_->zone();
|
|
|
|
|
|
|
|
|
|
|
|
// Forward definition for break/continue statements.
|
|
|
|
WhileStatement* loop = factory->NewWhileStatement(nullptr, nopos);
|
|
|
|
|
|
|
|
|
|
|
|
// let input = undefined;
|
|
|
|
Variable* var_input = scope->NewTemporary(avfactory->empty_string());
|
|
|
|
Statement* initialize_input;
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// let mode = kNext;
|
|
|
|
Variable* var_mode = scope->NewTemporary(avfactory->empty_string());
|
|
|
|
Statement* initialize_mode;
|
|
|
|
{
|
|
|
|
Expression* mode_proxy = factory->NewVariableProxy(var_mode);
|
2016-02-04 17:13:40 +00:00
|
|
|
Expression* knext = factory->NewSmiLiteral(JSGeneratorObject::NEXT, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* assignment =
|
|
|
|
factory->NewAssignment(Token::ASSIGN, mode_proxy, knext, nopos);
|
|
|
|
initialize_mode = factory->NewExpressionStatement(assignment, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// let output = undefined;
|
|
|
|
Variable* var_output = scope->NewTemporary(avfactory->empty_string());
|
|
|
|
Statement* initialize_output;
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// let iterator = iterable[Symbol.iterator];
|
|
|
|
Variable* var_iterator = scope->NewTemporary(avfactory->empty_string());
|
|
|
|
Statement* get_iterator;
|
|
|
|
{
|
|
|
|
Expression* iterator = GetIterator(iterable, factory, nopos);
|
|
|
|
Expression* iterator_proxy = factory->NewVariableProxy(var_iterator);
|
|
|
|
Expression* assignment = factory->NewAssignment(
|
|
|
|
Token::ASSIGN, iterator_proxy, iterator, nopos);
|
|
|
|
get_iterator = factory->NewExpressionStatement(assignment, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// if (!IS_RECEIVER(iterator)) throw MakeTypeError(kSymbolIteratorInvalid);
|
|
|
|
Statement* validate_iterator;
|
|
|
|
{
|
|
|
|
Expression* is_receiver_call;
|
|
|
|
{
|
|
|
|
auto args = new (zone) ZoneList<Expression*>(1, zone);
|
|
|
|
args->Add(factory->NewVariableProxy(var_iterator), zone);
|
|
|
|
is_receiver_call =
|
|
|
|
factory->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
Statement* throw_call;
|
|
|
|
{
|
|
|
|
Expression* call = NewThrowTypeError(
|
|
|
|
MessageTemplate::kSymbolIteratorInvalid, avfactory->empty_string(),
|
|
|
|
nopos);
|
|
|
|
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
|
|
|
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;
|
|
|
|
{
|
|
|
|
Expression* iterator_proxy = factory->NewVariableProxy(var_iterator);
|
|
|
|
Expression* literal =
|
|
|
|
factory->NewStringLiteral(avfactory->next_string(), nopos);
|
|
|
|
Expression* next_property =
|
|
|
|
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);
|
|
|
|
Expression* assignment =
|
|
|
|
factory->NewAssignment(Token::ASSIGN, output_proxy, call, nopos);
|
|
|
|
call_next = factory->NewExpressionStatement(assignment, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output);
|
|
|
|
Statement* validate_next_output;
|
|
|
|
{
|
|
|
|
Expression* is_receiver_call;
|
|
|
|
{
|
|
|
|
auto args = new (zone) ZoneList<Expression*>(1, zone);
|
|
|
|
args->Add(factory->NewVariableProxy(var_output), zone);
|
|
|
|
is_receiver_call =
|
|
|
|
factory->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
Statement* throw_call;
|
|
|
|
{
|
|
|
|
auto args = new (zone) ZoneList<Expression*>(1, zone);
|
|
|
|
args->Add(factory->NewVariableProxy(var_output), zone);
|
|
|
|
Expression* call = factory->NewCallRuntime(
|
|
|
|
Runtime::kThrowIteratorResultNotAnObject, args, nopos);
|
|
|
|
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
|
|
|
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;
|
|
|
|
Variable* var_throw = scope->NewTemporary(avfactory->empty_string());
|
|
|
|
Statement* get_throw;
|
|
|
|
{
|
|
|
|
Expression* iterator_proxy = factory->NewVariableProxy(var_iterator);
|
|
|
|
Expression* literal =
|
|
|
|
factory->NewStringLiteral(avfactory->throw_string(), nopos);
|
|
|
|
Expression* property =
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// if (IS_NULL_OR_UNDEFINED(iteratorThrow) {
|
|
|
|
// IteratorClose(iterator);
|
|
|
|
// throw MakeTypeError(kThrowMethodMissing);
|
|
|
|
// }
|
|
|
|
Statement* check_throw;
|
|
|
|
{
|
|
|
|
Expression* condition = factory->NewCompareOperation(
|
|
|
|
Token::EQ, factory->NewVariableProxy(var_throw),
|
|
|
|
factory->NewNullLiteral(nopos), nopos);
|
|
|
|
|
|
|
|
Expression* call = NewThrowTypeError(
|
|
|
|
MessageTemplate::kThrowMethodMissing,
|
|
|
|
avfactory->empty_string(), nopos);
|
|
|
|
Statement* throw_call = factory->NewExpressionStatement(call, nopos);
|
|
|
|
|
|
|
|
Block* then = factory->NewBlock(nullptr, 4+1, 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
|
|
|
Variable* var_tmp = scope->NewTemporary(avfactory->empty_string());
|
2016-03-01 09:38:54 +00:00
|
|
|
BuildIteratorClose(then->statements(), var_iterator, Nothing<Variable*>(),
|
|
|
|
var_tmp);
|
2016-02-04 14:12:37 +00:00
|
|
|
then->statements()->Add(throw_call, 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
|
|
|
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;
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
Expression* call =
|
|
|
|
factory->NewCallRuntime(Runtime::kInlineCall, args, nopos);
|
|
|
|
Expression* assignment = factory->NewAssignment(
|
|
|
|
Token::ASSIGN, factory->NewVariableProxy(var_output), call, nopos);
|
|
|
|
call_throw = factory->NewExpressionStatement(assignment, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// if (!IS_RECEIVER(output)) %ThrowIterResultNotAnObject(output);
|
|
|
|
Statement* validate_throw_output;
|
|
|
|
{
|
|
|
|
Expression* is_receiver_call;
|
|
|
|
{
|
|
|
|
auto args = new (zone) ZoneList<Expression*>(1, zone);
|
|
|
|
args->Add(factory->NewVariableProxy(var_output), zone);
|
|
|
|
is_receiver_call =
|
|
|
|
factory->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
Statement* throw_call;
|
|
|
|
{
|
|
|
|
auto args = new (zone) ZoneList<Expression*>(1, zone);
|
|
|
|
args->Add(factory->NewVariableProxy(var_output), zone);
|
|
|
|
Expression* call = factory->NewCallRuntime(
|
|
|
|
Runtime::kThrowIteratorResultNotAnObject, args, nopos);
|
|
|
|
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
|
|
|
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;
|
|
|
|
{
|
|
|
|
Expression* output_proxy = factory->NewVariableProxy(var_output);
|
|
|
|
Expression* literal =
|
|
|
|
factory->NewStringLiteral(avfactory->done_string(), nopos);
|
|
|
|
Expression* property = factory->NewProperty(output_proxy, literal, nopos);
|
|
|
|
BreakStatement* break_loop = factory->NewBreakStatement(loop, 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_done = factory->NewIfStatement(
|
|
|
|
property, break_loop, factory->NewEmptyStatement(nopos), nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// mode = kReturn;
|
|
|
|
Statement* set_mode_return;
|
|
|
|
{
|
|
|
|
Expression* mode_proxy = factory->NewVariableProxy(var_mode);
|
2016-02-04 17:13:40 +00:00
|
|
|
Expression* kreturn =
|
|
|
|
factory->NewSmiLiteral(JSGeneratorObject::RETURN, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* assignment =
|
|
|
|
factory->NewAssignment(Token::ASSIGN, mode_proxy, kreturn, nopos);
|
|
|
|
set_mode_return = factory->NewExpressionStatement(assignment, nopos);
|
|
|
|
}
|
|
|
|
|
2016-03-06 09:19:14 +00:00
|
|
|
// Yield(output);
|
2016-02-04 14:12:37 +00:00
|
|
|
Statement* yield_output;
|
|
|
|
{
|
|
|
|
Expression* output_proxy = factory->NewVariableProxy(var_output);
|
2016-03-06 09:19:14 +00:00
|
|
|
Yield* yield = factory->NewYield(generator, output_proxy, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
yield_output = factory->NewExpressionStatement(yield, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// mode = kNext;
|
|
|
|
Statement* set_mode_next;
|
|
|
|
{
|
|
|
|
Expression* mode_proxy = factory->NewVariableProxy(var_mode);
|
2016-02-04 17:13:40 +00:00
|
|
|
Expression* knext = factory->NewSmiLiteral(JSGeneratorObject::NEXT, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* assignment =
|
|
|
|
factory->NewAssignment(Token::ASSIGN, mode_proxy, knext, nopos);
|
|
|
|
set_mode_next = factory->NewExpressionStatement(assignment, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// mode = kThrow;
|
|
|
|
Statement* set_mode_throw;
|
|
|
|
{
|
|
|
|
Expression* mode_proxy = factory->NewVariableProxy(var_mode);
|
2016-02-04 17:13:40 +00:00
|
|
|
Expression* kthrow =
|
|
|
|
factory->NewSmiLiteral(JSGeneratorObject::THROW, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* assignment =
|
|
|
|
factory->NewAssignment(Token::ASSIGN, mode_proxy, kthrow, nopos);
|
|
|
|
set_mode_throw = factory->NewExpressionStatement(assignment, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// input = function.sent;
|
|
|
|
Statement* get_input;
|
|
|
|
{
|
|
|
|
Expression* function_sent = FunctionSentExpression(scope, factory, nopos);
|
|
|
|
Expression* input_proxy = factory->NewVariableProxy(var_input);
|
|
|
|
Expression* assignment = factory->NewAssignment(
|
|
|
|
Token::ASSIGN, input_proxy, function_sent, nopos);
|
|
|
|
get_input = factory->NewExpressionStatement(assignment, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// output.value;
|
|
|
|
Statement* get_value;
|
|
|
|
{
|
|
|
|
Expression* output_proxy = factory->NewVariableProxy(var_output);
|
|
|
|
Expression* literal =
|
|
|
|
factory->NewStringLiteral(avfactory->value_string(), nopos);
|
|
|
|
Expression* property = factory->NewProperty(output_proxy, literal, nopos);
|
|
|
|
get_value = factory->NewExpressionStatement(property, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Now put things together.
|
|
|
|
|
|
|
|
|
|
|
|
// try { ... } catch(e) { ... }
|
|
|
|
Statement* try_catch;
|
|
|
|
{
|
|
|
|
Block* try_block = factory->NewBlock(nullptr, 2, false, nopos);
|
|
|
|
try_block->statements()->Add(yield_output, zone);
|
|
|
|
try_block->statements()->Add(set_mode_next, zone);
|
|
|
|
|
|
|
|
Block* catch_block = factory->NewBlock(nullptr, 1, false, nopos);
|
|
|
|
catch_block->statements()->Add(set_mode_throw, zone);
|
|
|
|
|
|
|
|
Scope* catch_scope = NewScope(scope, CATCH_SCOPE);
|
|
|
|
const AstRawString* name = avfactory->dot_catch_string();
|
|
|
|
Variable* catch_variable =
|
|
|
|
catch_scope->DeclareLocal(name, VAR, kCreatedInitialized,
|
|
|
|
Variable::NORMAL);
|
|
|
|
|
|
|
|
try_catch = factory->NewTryCatchStatement(
|
|
|
|
try_block, catch_scope, catch_variable, catch_block, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// try { ... } finally { ... }
|
|
|
|
Statement* try_finally;
|
|
|
|
{
|
|
|
|
Block* try_block = factory->NewBlock(nullptr, 1, false, nopos);
|
|
|
|
try_block->statements()->Add(try_catch, zone);
|
|
|
|
|
|
|
|
Block* finally = factory->NewBlock(nullptr, 2, false, nopos);
|
|
|
|
finally->statements()->Add(get_input, zone);
|
|
|
|
finally->statements()->Add(
|
|
|
|
factory->NewContinueStatement(loop, nopos), zone);
|
|
|
|
|
|
|
|
try_finally = factory->NewTryFinallyStatement(try_block, finally, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// switch (mode) { ... }
|
|
|
|
SwitchStatement* switch_mode = factory->NewSwitchStatement(nullptr, nopos);
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
|
|
|
auto case_return = new (zone) ZoneList<Statement*>(5, zone);
|
2016-03-01 09:38:54 +00:00
|
|
|
BuildIteratorClose(case_return, var_iterator, Just(var_input), var_output);
|
2016-02-04 14:12:37 +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);
|
2016-02-04 17:13:40 +00:00
|
|
|
Expression* knext = factory->NewSmiLiteral(JSGeneratorObject::NEXT, nopos);
|
|
|
|
Expression* kreturn =
|
|
|
|
factory->NewSmiLiteral(JSGeneratorObject::RETURN, nopos);
|
|
|
|
Expression* kthrow =
|
|
|
|
factory->NewSmiLiteral(JSGeneratorObject::THROW, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
switch_mode->Initialize(factory->NewVariableProxy(var_mode), cases);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// while (true) { ... }
|
|
|
|
// Already defined earlier: WhileStatement* loop = ...
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
|
|
|
|
loop->Initialize(factory->NewBooleanLiteral(true, nopos), loop_body);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// do { ... }
|
|
|
|
DoExpression* yield_star;
|
|
|
|
{
|
|
|
|
// The rewriter needs to process the get_value statement only, hence we
|
|
|
|
// put the preceding statements into an init block.
|
|
|
|
|
|
|
|
Block* do_block_ = factory->NewBlock(nullptr, 6, 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);
|
|
|
|
|
|
|
|
Block* do_block = factory->NewBlock(nullptr, 2, false, nopos);
|
|
|
|
do_block->statements()->Add(do_block_, zone);
|
|
|
|
do_block->statements()->Add(get_value, zone);
|
|
|
|
|
|
|
|
Variable* dot_result = scope->NewTemporary(avfactory->dot_result_string());
|
|
|
|
yield_star = factory->NewDoExpression(do_block, dot_result, nopos);
|
|
|
|
Rewriter::Rewrite(parser_, yield_star, avfactory);
|
|
|
|
}
|
|
|
|
|
|
|
|
return yield_star;
|
|
|
|
}
|
|
|
|
|
2016-02-19 19:19:52 +00:00
|
|
|
// Desugaring of (lhs) instanceof (rhs)
|
|
|
|
// ====================================
|
|
|
|
//
|
|
|
|
// We desugar instanceof into a load of property @@hasInstance on the rhs.
|
|
|
|
// We end up with roughly the following code (O, C):
|
|
|
|
//
|
|
|
|
// do {
|
|
|
|
// let O = lhs;
|
|
|
|
// let C = rhs;
|
|
|
|
// if (!IS_RECEIVER(C)) throw MakeTypeError(kNonObjectInInstanceOfCheck);
|
|
|
|
// let handler_result = C[Symbol.hasInstance];
|
|
|
|
// if (handler_result === undefined) {
|
|
|
|
// if (!IS_CALLABLE(C)) {
|
|
|
|
// throw MakeTypeError(kCalledNonCallableInstanceOf);
|
|
|
|
// }
|
2016-03-17 12:40:05 +00:00
|
|
|
// handler_result = %_GetOrdinaryHasInstance()
|
|
|
|
// handler_result = %_Call(handler_result, C, O);
|
2016-02-19 19:19:52 +00:00
|
|
|
// } else {
|
|
|
|
// handler_result = !!(%_Call(handler_result, C, O));
|
|
|
|
// }
|
|
|
|
// handler_result;
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
Expression* ParserTraits::RewriteInstanceof(Expression* lhs, Expression* rhs,
|
|
|
|
int pos) {
|
|
|
|
const int nopos = RelocInfo::kNoPosition;
|
|
|
|
|
|
|
|
auto factory = parser_->factory();
|
|
|
|
auto avfactory = parser_->ast_value_factory();
|
|
|
|
auto scope = parser_->scope_;
|
|
|
|
auto zone = parser_->zone();
|
|
|
|
|
|
|
|
// let O = lhs;
|
|
|
|
Variable* var_O = scope->NewTemporary(avfactory->empty_string());
|
|
|
|
Statement* get_O;
|
|
|
|
{
|
|
|
|
Expression* O_proxy = factory->NewVariableProxy(var_O);
|
|
|
|
Expression* assignment =
|
|
|
|
factory->NewAssignment(Token::ASSIGN, O_proxy, lhs, nopos);
|
|
|
|
get_O = factory->NewExpressionStatement(assignment, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// let C = lhs;
|
|
|
|
Variable* var_C = scope->NewTemporary(avfactory->empty_string());
|
|
|
|
Statement* get_C;
|
|
|
|
{
|
|
|
|
Expression* C_proxy = factory->NewVariableProxy(var_C);
|
|
|
|
Expression* assignment =
|
|
|
|
factory->NewAssignment(Token::ASSIGN, C_proxy, rhs, nopos);
|
|
|
|
get_C = factory->NewExpressionStatement(assignment, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if (!IS_RECEIVER(C)) throw MakeTypeError(kNonObjectInInstanceOfCheck);
|
|
|
|
Statement* validate_C;
|
|
|
|
{
|
|
|
|
auto args = new (zone) ZoneList<Expression*>(1, zone);
|
|
|
|
args->Add(factory->NewVariableProxy(var_C), zone);
|
|
|
|
Expression* is_receiver_call =
|
|
|
|
factory->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
|
|
|
|
Expression* call =
|
|
|
|
NewThrowTypeError(MessageTemplate::kNonObjectInInstanceOfCheck,
|
2016-03-17 12:40:05 +00:00
|
|
|
avfactory->empty_string(), pos);
|
2016-02-19 19:19:52 +00:00
|
|
|
Statement* throw_call = factory->NewExpressionStatement(call, nopos);
|
|
|
|
|
|
|
|
validate_C =
|
|
|
|
factory->NewIfStatement(is_receiver_call,
|
|
|
|
factory->NewEmptyStatement(nopos),
|
|
|
|
throw_call,
|
|
|
|
nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// let handler_result = C[Symbol.hasInstance];
|
|
|
|
Variable* var_handler_result = scope->NewTemporary(avfactory->empty_string());
|
|
|
|
Statement* initialize_handler;
|
|
|
|
{
|
|
|
|
Expression* hasInstance_symbol_literal =
|
|
|
|
factory->NewSymbolLiteral("hasInstance_symbol", RelocInfo::kNoPosition);
|
|
|
|
Expression* prop = factory->NewProperty(factory->NewVariableProxy(var_C),
|
|
|
|
hasInstance_symbol_literal, pos);
|
|
|
|
Expression* handler_proxy = factory->NewVariableProxy(var_handler_result);
|
|
|
|
Expression* assignment =
|
|
|
|
factory->NewAssignment(Token::ASSIGN, handler_proxy, prop, nopos);
|
|
|
|
initialize_handler = factory->NewExpressionStatement(assignment, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if (handler_result === undefined) {
|
|
|
|
// if (!IS_CALLABLE(C)) {
|
|
|
|
// throw MakeTypeError(kCalledNonCallableInstanceOf);
|
|
|
|
// }
|
2016-03-17 12:40:05 +00:00
|
|
|
// handler_result = %_GetOrdinaryHasInstance()
|
|
|
|
// handler_result = %_Call(handler_result, C, O);
|
2016-02-19 19:19:52 +00:00
|
|
|
// } else {
|
|
|
|
// handler_result = !!%_Call(handler_result, C, O);
|
|
|
|
// }
|
|
|
|
Statement* call_handler;
|
|
|
|
{
|
|
|
|
Expression* condition = factory->NewCompareOperation(
|
|
|
|
Token::EQ_STRICT, factory->NewVariableProxy(var_handler_result),
|
|
|
|
factory->NewUndefinedLiteral(nopos), nopos);
|
|
|
|
|
2016-03-17 12:40:05 +00:00
|
|
|
Block* then_side = factory->NewBlock(nullptr, 3, false, nopos);
|
2016-02-19 19:19:52 +00:00
|
|
|
{
|
|
|
|
Expression* throw_expr =
|
|
|
|
NewThrowTypeError(MessageTemplate::kCalledNonCallableInstanceOf,
|
2016-03-17 12:40:05 +00:00
|
|
|
avfactory->empty_string(), pos);
|
2016-02-19 19:19:52 +00:00
|
|
|
Statement* validate_C = CheckCallable(var_C, throw_expr);
|
2016-03-17 12:40:05 +00:00
|
|
|
|
|
|
|
ZoneList<Expression*>* empty_args =
|
|
|
|
new (zone) ZoneList<Expression*>(0, zone);
|
|
|
|
Expression* ordinary_has_instance = factory->NewCallRuntime(
|
|
|
|
Runtime::kInlineGetOrdinaryHasInstance, empty_args, pos);
|
|
|
|
Expression* handler_proxy = factory->NewVariableProxy(var_handler_result);
|
|
|
|
Expression* assignment_handler = factory->NewAssignment(
|
|
|
|
Token::ASSIGN, handler_proxy, ordinary_has_instance, nopos);
|
|
|
|
Statement* assignment_get_handler =
|
|
|
|
factory->NewExpressionStatement(assignment_handler, nopos);
|
|
|
|
|
|
|
|
ZoneList<Expression*>* args = new (zone) ZoneList<Expression*>(3, zone);
|
|
|
|
args->Add(factory->NewVariableProxy(var_handler_result), zone);
|
2016-02-19 19:19:52 +00:00
|
|
|
args->Add(factory->NewVariableProxy(var_C), zone);
|
|
|
|
args->Add(factory->NewVariableProxy(var_O), zone);
|
2016-03-17 12:40:05 +00:00
|
|
|
Expression* call =
|
|
|
|
factory->NewCallRuntime(Runtime::kInlineCall, args, pos);
|
2016-02-19 19:19:52 +00:00
|
|
|
Expression* result_proxy = factory->NewVariableProxy(var_handler_result);
|
|
|
|
Expression* assignment =
|
|
|
|
factory->NewAssignment(Token::ASSIGN, result_proxy, call, nopos);
|
|
|
|
Statement* assignment_return =
|
|
|
|
factory->NewExpressionStatement(assignment, nopos);
|
|
|
|
|
|
|
|
then_side->statements()->Add(validate_C, zone);
|
2016-03-17 12:40:05 +00:00
|
|
|
then_side->statements()->Add(assignment_get_handler, zone);
|
2016-02-19 19:19:52 +00:00
|
|
|
then_side->statements()->Add(assignment_return, zone);
|
|
|
|
}
|
|
|
|
|
|
|
|
Statement* else_side;
|
|
|
|
{
|
|
|
|
auto args = new (zone) ZoneList<Expression*>(3, zone);
|
|
|
|
args->Add(factory->NewVariableProxy(var_handler_result), zone);
|
|
|
|
args->Add(factory->NewVariableProxy(var_C), zone);
|
|
|
|
args->Add(factory->NewVariableProxy(var_O), zone);
|
|
|
|
Expression* call =
|
|
|
|
factory->NewCallRuntime(Runtime::kInlineCall, args, nopos);
|
|
|
|
Expression* inner_not =
|
|
|
|
factory->NewUnaryOperation(Token::NOT, call, nopos);
|
|
|
|
Expression* outer_not =
|
|
|
|
factory->NewUnaryOperation(Token::NOT, inner_not, nopos);
|
|
|
|
Expression* result_proxy = factory->NewVariableProxy(var_handler_result);
|
|
|
|
Expression* assignment =
|
|
|
|
factory->NewAssignment(Token::ASSIGN, result_proxy, outer_not, nopos);
|
|
|
|
|
|
|
|
else_side = factory->NewExpressionStatement(assignment, nopos);
|
|
|
|
}
|
|
|
|
call_handler =
|
|
|
|
factory->NewIfStatement(condition, then_side, else_side, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// do { ... }
|
|
|
|
DoExpression* instanceof;
|
|
|
|
{
|
|
|
|
Block* block = factory->NewBlock(nullptr, 5, true, nopos);
|
|
|
|
block->statements()->Add(get_O, zone);
|
|
|
|
block->statements()->Add(get_C, zone);
|
|
|
|
block->statements()->Add(validate_C, zone);
|
|
|
|
block->statements()->Add(initialize_handler, zone);
|
|
|
|
block->statements()->Add(call_handler, zone);
|
|
|
|
|
|
|
|
// Here is the desugared instanceof.
|
|
|
|
instanceof = factory->NewDoExpression(block, var_handler_result, nopos);
|
|
|
|
Rewriter::Rewrite(parser_, instanceof, avfactory);
|
|
|
|
}
|
|
|
|
|
|
|
|
return instanceof;
|
|
|
|
}
|
|
|
|
|
|
|
|
Statement* ParserTraits::CheckCallable(Variable* var, Expression* error) {
|
|
|
|
auto factory = parser_->factory();
|
|
|
|
auto avfactory = parser_->ast_value_factory();
|
|
|
|
const int nopos = RelocInfo::kNoPosition;
|
|
|
|
Statement* validate_var;
|
|
|
|
{
|
|
|
|
Expression* type_of = factory->NewUnaryOperation(
|
|
|
|
Token::TYPEOF, factory->NewVariableProxy(var), nopos);
|
|
|
|
Expression* function_literal =
|
|
|
|
factory->NewStringLiteral(avfactory->function_string(), nopos);
|
|
|
|
Expression* condition = factory->NewCompareOperation(
|
|
|
|
Token::EQ_STRICT, type_of, function_literal, nopos);
|
|
|
|
|
|
|
|
Statement* throw_call = factory->NewExpressionStatement(error, nopos);
|
|
|
|
|
|
|
|
validate_var = factory->NewIfStatement(
|
|
|
|
condition, factory->NewEmptyStatement(nopos), throw_call, nopos);
|
|
|
|
}
|
|
|
|
return validate_var;
|
|
|
|
}
|
2016-02-04 14:12:37 +00:00
|
|
|
|
|
|
|
void ParserTraits::BuildIteratorClose(ZoneList<Statement*>* statements,
|
|
|
|
Variable* iterator,
|
2016-03-01 09:38:54 +00:00
|
|
|
Maybe<Variable*> 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
|
|
|
Variable* var_output) {
|
|
|
|
//
|
|
|
|
// This function adds four statements to [statements], corresponding to the
|
|
|
|
// following code:
|
|
|
|
//
|
|
|
|
// let iteratorReturn = iterator.return;
|
2016-03-01 09:38:54 +00:00
|
|
|
// if (IS_NULL_OR_UNDEFINED(iteratorReturn) return |input|;
|
|
|
|
// 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-03-01 09:38:54 +00:00
|
|
|
// Here, |...| denotes optional parts, depending on the presence of the
|
|
|
|
// input variable. The reason for allowing input is that BuildIteratorClose
|
|
|
|
// can then be reused to handle the return case in yield*.
|
|
|
|
//
|
[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-04 14:12:37 +00:00
|
|
|
const int nopos = RelocInfo::kNoPosition;
|
|
|
|
auto factory = parser_->factory();
|
|
|
|
auto avfactory = parser_->ast_value_factory();
|
|
|
|
auto zone = parser_->zone();
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
{
|
|
|
|
Expression* iterator_proxy = factory->NewVariableProxy(iterator);
|
|
|
|
Expression* literal =
|
|
|
|
factory->NewStringLiteral(avfactory->return_string(), nopos);
|
|
|
|
Expression* property =
|
|
|
|
factory->NewProperty(iterator_proxy, literal, 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* return_proxy = factory->NewVariableProxy(var_return);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* assignment = factory->NewAssignment(
|
|
|
|
Token::ASSIGN, return_proxy, property, nopos);
|
|
|
|
get_return = factory->NewExpressionStatement(assignment, nopos);
|
|
|
|
}
|
|
|
|
|
2016-03-01 09:38:54 +00:00
|
|
|
// if (IS_NULL_OR_UNDEFINED(iteratorReturn) return |input|;
|
2016-02-04 14:12:37 +00:00
|
|
|
Statement* check_return;
|
|
|
|
{
|
|
|
|
Expression* condition = factory->NewCompareOperation(
|
[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
|
|
|
Token::EQ, factory->NewVariableProxy(var_return),
|
2016-02-04 14:12:37 +00:00
|
|
|
factory->NewNullLiteral(nopos), nopos);
|
|
|
|
|
2016-03-01 09:38:54 +00:00
|
|
|
Expression* value = input.IsJust()
|
|
|
|
? static_cast<Expression*>(
|
|
|
|
factory->NewVariableProxy(input.FromJust()))
|
|
|
|
: factory->NewUndefinedLiteral(nopos);
|
|
|
|
|
|
|
|
Statement* return_input = factory->NewReturnStatement(value, 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
|
|
|
check_return = factory->NewIfStatement(
|
|
|
|
condition, return_input, factory->NewEmptyStatement(nopos), nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
2016-03-01 09:38:54 +00:00
|
|
|
// output = %_Call(iteratorReturn, iterator, |input|);
|
2016-02-04 14:12:37 +00:00
|
|
|
Statement* call_return;
|
|
|
|
{
|
|
|
|
auto args = new (zone) ZoneList<Expression*>(3, 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
|
|
|
args->Add(factory->NewVariableProxy(var_return), zone);
|
2016-02-04 14:12:37 +00:00
|
|
|
args->Add(factory->NewVariableProxy(iterator), zone);
|
2016-03-01 09:38:54 +00:00
|
|
|
if (input.IsJust()) {
|
|
|
|
args->Add(factory->NewVariableProxy(input.FromJust()), zone);
|
|
|
|
}
|
2016-02-04 14:12:37 +00:00
|
|
|
|
|
|
|
Expression* call =
|
|
|
|
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
|
|
|
Expression* output_proxy = factory->NewVariableProxy(var_output);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* assignment = factory->NewAssignment(
|
2016-02-04 16:36:11 +00:00
|
|
|
Token::ASSIGN, output_proxy, call, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
call_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_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;
|
|
|
|
{
|
|
|
|
auto args = new (zone) ZoneList<Expression*>(1, 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
|
|
|
args->Add(factory->NewVariableProxy(var_output), zone);
|
2016-02-04 14:12:37 +00:00
|
|
|
is_receiver_call =
|
|
|
|
factory->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
Statement* throw_call;
|
|
|
|
{
|
|
|
|
auto args = new (zone) ZoneList<Expression*>(1, 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
|
|
|
args->Add(factory->NewVariableProxy(var_output), zone);
|
2016-02-04 14:12:37 +00:00
|
|
|
Expression* call = factory->NewCallRuntime(
|
|
|
|
Runtime::kThrowIteratorResultNotAnObject, args, nopos);
|
|
|
|
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
|
|
|
validate_output = factory->NewIfStatement(
|
|
|
|
is_receiver_call, factory->NewEmptyStatement(nopos), throw_call, nopos);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
statements->Add(get_return, zone);
|
|
|
|
statements->Add(check_return, zone);
|
|
|
|
statements->Add(call_return, zone);
|
2016-02-04 16:36:11 +00:00
|
|
|
statements->Add(validate_output, zone);
|
2016-02-04 14:12:37 +00:00
|
|
|
}
|
|
|
|
|
2016-03-10 09:33:29 +00:00
|
|
|
void ParserTraits::FinalizeIteratorUse(Variable* completion,
|
|
|
|
Expression* condition, Variable* iter,
|
|
|
|
Block* iterator_use, Block* target) {
|
|
|
|
if (!FLAG_harmony_iterator_close) return;
|
2016-02-04 14:12:37 +00:00
|
|
|
|
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;
|
|
|
|
// throw e;
|
|
|
|
// }
|
|
|
|
// } finally {
|
|
|
|
// if (condition) {
|
|
|
|
// #BuildIteratorCloseForCompletion(iter, completion)
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
|
|
|
|
const int nopos = RelocInfo::kNoPosition;
|
|
|
|
auto factory = parser_->factory();
|
|
|
|
auto avfactory = parser_->ast_value_factory();
|
|
|
|
auto scope = parser_->scope_;
|
|
|
|
auto zone = parser_->zone();
|
|
|
|
|
|
|
|
// completion = kNormalCompletion;
|
|
|
|
Statement* initialize_completion;
|
|
|
|
{
|
|
|
|
Expression* proxy = factory->NewVariableProxy(completion);
|
|
|
|
Expression* assignment = factory->NewAssignment(
|
|
|
|
Token::ASSIGN, proxy,
|
|
|
|
factory->NewSmiLiteral(Parser::kNormalCompletion, nopos), nopos);
|
|
|
|
initialize_completion = factory->NewExpressionStatement(assignment, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if (completion === kAbruptCompletion) completion = kThrowCompletion;
|
|
|
|
Statement* set_completion_throw;
|
|
|
|
{
|
|
|
|
Expression* condition = factory->NewCompareOperation(
|
|
|
|
Token::EQ_STRICT, factory->NewVariableProxy(completion),
|
|
|
|
factory->NewSmiLiteral(Parser::kAbruptCompletion, nopos), nopos);
|
|
|
|
|
|
|
|
Expression* proxy = factory->NewVariableProxy(completion);
|
|
|
|
Expression* assignment = factory->NewAssignment(
|
|
|
|
Token::ASSIGN, proxy,
|
|
|
|
factory->NewSmiLiteral(Parser::kThrowCompletion, nopos), nopos);
|
|
|
|
Statement* statement = factory->NewExpressionStatement(assignment, nopos);
|
|
|
|
set_completion_throw = factory->NewIfStatement(
|
|
|
|
condition, statement, factory->NewEmptyStatement(nopos), nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if (condition) {
|
|
|
|
// #BuildIteratorCloseForCompletion(iter, completion)
|
|
|
|
// }
|
|
|
|
Block* maybe_close;
|
|
|
|
{
|
|
|
|
Block* block = factory->NewBlock(nullptr, 2, true, nopos);
|
|
|
|
parser_->BuildIteratorCloseForCompletion(block->statements(), iter,
|
|
|
|
completion);
|
|
|
|
DCHECK(block->statements()->length() == 2);
|
|
|
|
|
|
|
|
maybe_close = factory->NewBlock(nullptr, 1, true, nopos);
|
|
|
|
maybe_close->statements()->Add(
|
|
|
|
factory->NewIfStatement(condition, block,
|
|
|
|
factory->NewEmptyStatement(nopos), nopos),
|
|
|
|
zone);
|
|
|
|
}
|
|
|
|
|
|
|
|
// try { #try_block }
|
|
|
|
// catch(e) {
|
|
|
|
// #set_completion_throw;
|
|
|
|
// throw e;
|
|
|
|
// }
|
|
|
|
Statement* try_catch;
|
|
|
|
{
|
|
|
|
Scope* catch_scope = parser_->NewScope(scope, CATCH_SCOPE);
|
|
|
|
Variable* catch_variable =
|
|
|
|
catch_scope->DeclareLocal(avfactory->dot_catch_string(), VAR,
|
|
|
|
kCreatedInitialized, Variable::NORMAL);
|
|
|
|
|
|
|
|
Statement* rethrow;
|
|
|
|
{
|
|
|
|
Expression* proxy = factory->NewVariableProxy(catch_variable);
|
|
|
|
rethrow = factory->NewExpressionStatement(factory->NewThrow(proxy, nopos),
|
|
|
|
nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
Block* catch_block = factory->NewBlock(nullptr, 2, false, nopos);
|
|
|
|
catch_block->statements()->Add(set_completion_throw, zone);
|
|
|
|
catch_block->statements()->Add(rethrow, zone);
|
|
|
|
|
|
|
|
try_catch = factory->NewTryCatchStatement(
|
|
|
|
iterator_use, catch_scope, catch_variable, catch_block, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// try { #try_catch } finally { #maybe_close }
|
|
|
|
Statement* try_finally;
|
|
|
|
{
|
|
|
|
Block* try_block = factory->NewBlock(nullptr, 1, false, nopos);
|
|
|
|
try_block->statements()->Add(try_catch, zone);
|
|
|
|
|
|
|
|
try_finally =
|
|
|
|
factory->NewTryFinallyStatement(try_block, maybe_close, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
target->statements()->Add(initialize_completion, zone);
|
|
|
|
target->statements()->Add(try_finally, 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
|
|
|
|
|
|
|
void ParserTraits::BuildIteratorCloseForCompletion(
|
|
|
|
ZoneList<Statement*>* statements, Variable* iterator,
|
|
|
|
Variable* completion) {
|
|
|
|
//
|
|
|
|
// 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
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
|
|
|
|
const int nopos = RelocInfo::kNoPosition;
|
|
|
|
auto factory = parser_->factory();
|
|
|
|
auto avfactory = parser_->ast_value_factory();
|
|
|
|
auto scope = parser_->scope_;
|
|
|
|
auto zone = parser_->zone();
|
|
|
|
|
|
|
|
|
|
|
|
// let iteratorReturn = iterator.return;
|
2016-02-24 18:21:32 +00:00
|
|
|
Variable* var_return = scope->NewTemporary(avfactory->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;
|
|
|
|
{
|
|
|
|
Expression* iterator_proxy = factory->NewVariableProxy(iterator);
|
|
|
|
Expression* literal =
|
|
|
|
factory->NewStringLiteral(avfactory->return_string(), nopos);
|
|
|
|
Expression* property =
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if (!IS_CALLABLE(iteratorReturn)) {
|
|
|
|
// throw MakeTypeError(kReturnMethodNotCallable);
|
|
|
|
// }
|
|
|
|
Statement* check_return_callable;
|
|
|
|
{
|
2016-02-19 19:19:52 +00:00
|
|
|
Expression* throw_expr = NewThrowTypeError(
|
[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
|
|
|
MessageTemplate::kReturnMethodNotCallable,
|
|
|
|
avfactory->empty_string(), nopos);
|
2016-02-19 19:19:52 +00:00
|
|
|
check_return_callable = CheckCallable(var_return, throw_expr);
|
[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;
|
|
|
|
{
|
|
|
|
auto args = new (zone) ZoneList<Expression*>(2, zone);
|
|
|
|
args->Add(factory->NewVariableProxy(var_return), zone);
|
|
|
|
args->Add(factory->NewVariableProxy(iterator), zone);
|
|
|
|
|
|
|
|
Expression* call =
|
|
|
|
factory->NewCallRuntime(Runtime::kInlineCall, args, nopos);
|
|
|
|
|
|
|
|
Block* try_block = factory->NewBlock(nullptr, 1, false, nopos);
|
2016-02-24 18:21:32 +00:00
|
|
|
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
|
|
|
|
|
|
|
Block* catch_block = factory->NewBlock(nullptr, 0, false, nopos);
|
|
|
|
|
|
|
|
Scope* catch_scope = NewScope(scope, CATCH_SCOPE);
|
|
|
|
Variable* catch_variable = catch_scope->DeclareLocal(
|
|
|
|
avfactory->dot_catch_string(), VAR, kCreatedInitialized,
|
|
|
|
Variable::NORMAL);
|
|
|
|
|
|
|
|
try_call_return = factory->NewTryCatchStatement(
|
|
|
|
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-02-24 18:21:32 +00:00
|
|
|
Variable* var_output = scope->NewTemporary(avfactory->empty_string());
|
|
|
|
Statement* call_return;
|
|
|
|
{
|
|
|
|
auto args = new (zone) ZoneList<Expression*>(2, zone);
|
|
|
|
args->Add(factory->NewVariableProxy(var_return), zone);
|
|
|
|
args->Add(factory->NewVariableProxy(iterator), zone);
|
|
|
|
Expression* call =
|
|
|
|
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-02-24 18:21:32 +00:00
|
|
|
Expression* output_proxy = factory->NewVariableProxy(var_output);
|
|
|
|
Expression* assignment =
|
|
|
|
factory->NewAssignment(Token::ASSIGN, output_proxy, call, nopos);
|
|
|
|
call_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
|
|
|
|
|
|
|
Expression* is_receiver_call;
|
|
|
|
{
|
|
|
|
auto args = new (zone) ZoneList<Expression*>(1, zone);
|
|
|
|
args->Add(factory->NewVariableProxy(var_output), zone);
|
|
|
|
is_receiver_call =
|
|
|
|
factory->NewCallRuntime(Runtime::kInlineIsJSReceiver, args, nopos);
|
|
|
|
}
|
|
|
|
|
|
|
|
Statement* throw_call;
|
|
|
|
{
|
|
|
|
auto args = new (zone) ZoneList<Expression*>(1, zone);
|
|
|
|
args->Add(factory->NewVariableProxy(var_output), zone);
|
|
|
|
Expression* call = factory->NewCallRuntime(
|
|
|
|
Runtime::kThrowIteratorResultNotAnObject, args, nopos);
|
|
|
|
throw_call = factory->NewExpressionStatement(call, nopos);
|
|
|
|
}
|
|
|
|
|
2016-02-24 18:21:32 +00:00
|
|
|
Statement* check_return = factory->NewIfStatement(
|
[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, factory->NewEmptyStatement(nopos), throw_call, nopos);
|
2016-02-24 18:21:32 +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-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;
|
|
|
|
{
|
|
|
|
Expression* condition = factory->NewCompareOperation(
|
|
|
|
Token::EQ_STRICT, factory->NewVariableProxy(completion),
|
2016-03-10 09:33:29 +00:00
|
|
|
factory->NewSmiLiteral(Parser::kThrowCompletion, nopos), nopos);
|
2016-02-24 18:21:32 +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);
|
|
|
|
|
|
|
|
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;
|
|
|
|
{
|
|
|
|
Expression* condition = factory->NewCompareOperation(
|
|
|
|
Token::EQ, factory->NewVariableProxy(var_return),
|
|
|
|
factory->NewNullLiteral(nopos), nopos);
|
|
|
|
|
2016-02-24 18:21:32 +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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
statements->Add(get_return, zone);
|
|
|
|
statements->Add(maybe_call_return, zone);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Statement* ParserTraits::FinalizeForOfStatement(ForOfStatement* loop, int pos) {
|
|
|
|
if (!FLAG_harmony_iterator_close) return loop;
|
|
|
|
|
|
|
|
//
|
|
|
|
// This function replaces the loop with the following wrapping:
|
|
|
|
//
|
2016-02-24 18:52:17 +00:00
|
|
|
// let each;
|
2016-03-10 09:33:29 +00:00
|
|
|
// let 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;
|
|
|
|
// throw e;
|
|
|
|
// }
|
[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
|
|
|
// }
|
|
|
|
// }
|
|
|
|
//
|
|
|
|
// where the loop's body is wrapped as follows:
|
|
|
|
//
|
|
|
|
// {
|
|
|
|
// #loop-body
|
2016-03-10 09:33:29 +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
|
|
|
// }
|
2016-02-24 18:52:17 +00:00
|
|
|
//
|
2016-03-10 09:33:29 +00:00
|
|
|
// and the loop's assign_each is wrapped as follows
|
2016-02-24 18:52:17 +00:00
|
|
|
//
|
|
|
|
// do {
|
2016-03-10 09:33:29 +00:00
|
|
|
// {{completion = kAbruptCompletion;}}
|
2016-02-24 18:52:17 +00:00
|
|
|
// #assign-each
|
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
|
|
|
|
|
|
|
const int nopos = RelocInfo::kNoPosition;
|
|
|
|
auto factory = parser_->factory();
|
|
|
|
auto avfactory = parser_->ast_value_factory();
|
|
|
|
auto scope = parser_->scope_;
|
|
|
|
auto zone = parser_->zone();
|
|
|
|
|
|
|
|
Variable* var_completion = scope->NewTemporary(avfactory->empty_string());
|
|
|
|
|
2016-02-24 18:52:17 +00:00
|
|
|
// let each;
|
|
|
|
Variable* var_each = scope->NewTemporary(avfactory->empty_string());
|
|
|
|
Statement* initialize_each;
|
|
|
|
{
|
|
|
|
Expression* proxy = factory->NewVariableProxy(var_each);
|
|
|
|
Expression* assignment = factory->NewAssignment(
|
|
|
|
Token::ASSIGN, proxy,
|
|
|
|
factory->NewUndefinedLiteral(nopos), nopos);
|
|
|
|
initialize_each =
|
|
|
|
factory->NewExpressionStatement(assignment, nopos);
|
|
|
|
}
|
|
|
|
|
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-03-10 09:33:29 +00:00
|
|
|
Expression* lhs = factory->NewCompareOperation(
|
[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
|
|
|
Token::EQ_STRICT, factory->NewVariableProxy(var_completion),
|
2016-03-10 09:33:29 +00:00
|
|
|
factory->NewSmiLiteral(Parser::kNormalCompletion, nopos), nopos);
|
|
|
|
Expression* rhs = factory->NewCompareOperation(
|
[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
|
|
|
Token::EQ_STRICT, factory->NewVariableProxy(loop->iterator()),
|
|
|
|
factory->NewUndefinedLiteral(nopos), nopos);
|
2016-03-10 09:33:29 +00:00
|
|
|
closing_condition = factory->NewUnaryOperation(
|
|
|
|
Token::NOT, factory->NewBinaryOperation(Token::OR, lhs, rhs, 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-03-10 09:33:29 +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
|
|
|
Statement* set_completion_normal;
|
|
|
|
{
|
|
|
|
Expression* proxy = factory->NewVariableProxy(var_completion);
|
|
|
|
Expression* assignment = factory->NewAssignment(
|
2016-03-10 09:33:29 +00:00
|
|
|
Token::ASSIGN, proxy,
|
|
|
|
factory->NewSmiLiteral(Parser::kNormalCompletion, 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
|
|
|
|
|
|
|
Block* block = factory->NewBlock(nullptr, 1, true, nopos);
|
|
|
|
block->statements()->Add(
|
|
|
|
factory->NewExpressionStatement(assignment, nopos), zone);
|
|
|
|
set_completion_normal = block;
|
|
|
|
}
|
|
|
|
|
2016-03-10 09:33:29 +00:00
|
|
|
// {{completion = kAbruptCompletion;}}
|
|
|
|
Statement* set_completion_abrupt;
|
2016-02-24 18:52:17 +00:00
|
|
|
{
|
|
|
|
Expression* proxy = factory->NewVariableProxy(var_completion);
|
|
|
|
Expression* assignment = factory->NewAssignment(
|
2016-03-10 09:33:29 +00:00
|
|
|
Token::ASSIGN, proxy,
|
|
|
|
factory->NewSmiLiteral(Parser::kAbruptCompletion, nopos), nopos);
|
2016-02-24 18:52:17 +00:00
|
|
|
|
|
|
|
Block* block = factory->NewBlock(nullptr, 1, true, nopos);
|
|
|
|
block->statements()->Add(factory->NewExpressionStatement(assignment, nopos),
|
|
|
|
zone);
|
2016-03-10 09:33:29 +00:00
|
|
|
set_completion_abrupt = block;
|
2016-02-24 18:52:17 +00:00
|
|
|
}
|
|
|
|
|
2016-03-10 09:33:29 +00:00
|
|
|
// { #loop-body; #set_completion_normal }
|
|
|
|
Block* new_body = factory->NewBlock(nullptr, 2, false, nopos);
|
|
|
|
{
|
|
|
|
new_body->statements()->Add(loop->body(), zone);
|
|
|
|
new_body->statements()->Add(set_completion_normal, zone);
|
|
|
|
}
|
|
|
|
|
|
|
|
// { #set_completion_abrupt; #assign-each }
|
2016-02-24 18:52:17 +00:00
|
|
|
Block* new_assign_each = factory->NewBlock(nullptr, 2, false, nopos);
|
2016-03-10 09:33:29 +00:00
|
|
|
{
|
|
|
|
new_assign_each->statements()->Add(set_completion_abrupt, zone);
|
|
|
|
new_assign_each->statements()->Add(
|
|
|
|
factory->NewExpressionStatement(loop->assign_each(), nopos), zone);
|
|
|
|
}
|
2016-02-24 18:52:17 +00:00
|
|
|
|
2016-03-10 09:33:29 +00:00
|
|
|
// Now put things together.
|
|
|
|
|
|
|
|
loop->set_body(new_body);
|
|
|
|
loop->set_assign_each(
|
|
|
|
factory->NewDoExpression(new_assign_each, var_each, nopos));
|
|
|
|
|
|
|
|
Statement* final_loop;
|
|
|
|
{
|
|
|
|
Block* target = factory->NewBlock(nullptr, 3, false, nopos);
|
|
|
|
target->statements()->Add(initialize_each, zone);
|
|
|
|
|
|
|
|
Block* try_block = factory->NewBlock(nullptr, 1, false, nopos);
|
|
|
|
try_block->statements()->Add(loop, zone);
|
|
|
|
|
|
|
|
FinalizeIteratorUse(var_completion, closing_condition, loop->iterator(),
|
|
|
|
try_block, target);
|
|
|
|
final_loop = target;
|
|
|
|
}
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-01 22:46:54 +00:00
|
|
|
} // namespace internal
|
|
|
|
} // namespace v8
|