summaryrefslogtreecommitdiff
path: root/deps/v8/src/interpreter/bytecode-generator.cc
diff options
context:
space:
mode:
authorMichaël Zasso <targos@protonmail.com>2019-03-12 09:01:49 +0100
committerMichaël Zasso <targos@protonmail.com>2019-03-14 18:49:21 +0100
commit7b48713334469818661fe276cf571de9c7899f2d (patch)
tree4dbda49ac88db76ce09dc330a0cb587e68e139ba /deps/v8/src/interpreter/bytecode-generator.cc
parent8549ac09b256666cf5275224ec58fab9939ff32e (diff)
downloadandroid-node-v8-7b48713334469818661fe276cf571de9c7899f2d.tar.gz
android-node-v8-7b48713334469818661fe276cf571de9c7899f2d.tar.bz2
android-node-v8-7b48713334469818661fe276cf571de9c7899f2d.zip
deps: update V8 to 7.3.492.25
PR-URL: https://github.com/nodejs/node/pull/25852 Reviewed-By: Ujjwal Sharma <usharma1998@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Ali Ijaz Sheikh <ofrobots@google.com>
Diffstat (limited to 'deps/v8/src/interpreter/bytecode-generator.cc')
-rw-r--r--deps/v8/src/interpreter/bytecode-generator.cc1613
1 files changed, 1130 insertions, 483 deletions
diff --git a/deps/v8/src/interpreter/bytecode-generator.cc b/deps/v8/src/interpreter/bytecode-generator.cc
index 48682439fb..00b1916c92 100644
--- a/deps/v8/src/interpreter/bytecode-generator.cc
+++ b/deps/v8/src/interpreter/bytecode-generator.cc
@@ -8,7 +8,6 @@
#include "src/ast/ast-source-ranges.h"
#include "src/ast/scopes.h"
#include "src/builtins/builtins-constructor.h"
-#include "src/code-stubs.h"
#include "src/compiler.h"
#include "src/interpreter/bytecode-flags.h"
#include "src/interpreter/bytecode-jump-table.h"
@@ -18,6 +17,7 @@
#include "src/objects-inl.h"
#include "src/objects/debug-objects.h"
#include "src/objects/literal-objects-inl.h"
+#include "src/objects/smi.h"
#include "src/parsing/parse-info.h"
#include "src/parsing/token.h"
#include "src/unoptimized-compilation-info.h"
@@ -97,7 +97,8 @@ class BytecodeGenerator::ContextScope {
class BytecodeGenerator::ControlScope {
public:
explicit ControlScope(BytecodeGenerator* generator)
- : generator_(generator), outer_(generator->execution_control()),
+ : generator_(generator),
+ outer_(generator->execution_control()),
context_(generator->execution_context()) {
generator_->set_execution_control(this);
}
@@ -158,6 +159,16 @@ class BytecodeGenerator::ControlScope {
// paths going through the finally-block to dispatch after leaving the block.
class BytecodeGenerator::ControlScope::DeferredCommands final {
public:
+ // Fixed value tokens for paths we know we need.
+ // Fallthrough is set to -1 to make it the fallthrough case of the jump table,
+ // where the remaining cases start at 0.
+ static const int kFallthroughToken = -1;
+ // TODO(leszeks): Rethrow being 0 makes it use up a valuable LdaZero, which
+ // means that other commands (such as break or return) have to use LdaSmi.
+ // This can very slightly bloat bytecode, so perhaps token values should all
+ // be shifted down by 1.
+ static const int kRethrowToken = 0;
+
DeferredCommands(BytecodeGenerator* generator, Register token_register,
Register result_register)
: generator_(generator),
@@ -165,8 +176,13 @@ class BytecodeGenerator::ControlScope::DeferredCommands final {
token_register_(token_register),
result_register_(result_register),
return_token_(-1),
- async_return_token_(-1),
- rethrow_token_(-1) {}
+ async_return_token_(-1) {
+ // There's always a rethrow path.
+ // TODO(leszeks): We could decouple deferred_ index and token to allow us
+ // to still push this lazily.
+ STATIC_ASSERT(kRethrowToken == 0);
+ deferred_.push_back({CMD_RETHROW, nullptr, kRethrowToken});
+ }
// One recorded control-flow command.
struct Entry {
@@ -211,7 +227,7 @@ class BytecodeGenerator::ControlScope::DeferredCommands final {
// Records the dispatch token to be used to identify the implicit fall-through
// path at the end of a try-block into the corresponding finally-block.
void RecordFallThroughPath() {
- builder()->LoadLiteral(Smi::FromInt(-1));
+ builder()->LoadLiteral(Smi::FromInt(kFallthroughToken));
builder()->StoreAccumulatorInRegister(token_register_);
// Since we're not saving the accumulator in the result register, shove a
// harmless value there instead so that it is still considered "killed" in
@@ -277,7 +293,7 @@ class BytecodeGenerator::ControlScope::DeferredCommands final {
case CMD_ASYNC_RETURN:
return GetAsyncReturnToken();
case CMD_RETHROW:
- return GetRethrowToken();
+ return kRethrowToken;
default:
// TODO(leszeks): We could also search for entries with the same
// command and statement.
@@ -299,13 +315,6 @@ class BytecodeGenerator::ControlScope::DeferredCommands final {
return async_return_token_;
}
- int GetRethrowToken() {
- if (rethrow_token_ == -1) {
- rethrow_token_ = GetNewTokenForCommand(CMD_RETHROW, nullptr);
- }
- return rethrow_token_;
- }
-
int GetNewTokenForCommand(Command command, Statement* statement) {
int token = static_cast<int>(deferred_.size());
deferred_.push_back({command, statement, token});
@@ -320,7 +329,6 @@ class BytecodeGenerator::ControlScope::DeferredCommands final {
// Tokens for commands that don't need a statement.
int return_token_;
int async_return_token_;
- int rethrow_token_;
};
// Scoped class for dealing with control flow reaching the function level.
@@ -552,6 +560,8 @@ class BytecodeGenerator::RegisterAllocationScope final {
outer_next_register_index_);
}
+ BytecodeGenerator* generator() const { return generator_; }
+
private:
BytecodeGenerator* generator_;
int outer_next_register_index_;
@@ -559,21 +569,47 @@ class BytecodeGenerator::RegisterAllocationScope final {
DISALLOW_COPY_AND_ASSIGN(RegisterAllocationScope);
};
+class BytecodeGenerator::AccumulatorPreservingScope final {
+ public:
+ explicit AccumulatorPreservingScope(BytecodeGenerator* generator,
+ AccumulatorPreservingMode mode)
+ : generator_(generator) {
+ if (mode == AccumulatorPreservingMode::kPreserve) {
+ saved_accumulator_register_ =
+ generator_->register_allocator()->NewRegister();
+ generator_->builder()->StoreAccumulatorInRegister(
+ saved_accumulator_register_);
+ }
+ }
+
+ ~AccumulatorPreservingScope() {
+ if (saved_accumulator_register_.is_valid()) {
+ generator_->builder()->LoadAccumulatorWithRegister(
+ saved_accumulator_register_);
+ }
+ }
+
+ private:
+ BytecodeGenerator* generator_;
+ Register saved_accumulator_register_;
+
+ DISALLOW_COPY_AND_ASSIGN(AccumulatorPreservingScope);
+};
+
// Scoped base class for determining how the result of an expression will be
// used.
class BytecodeGenerator::ExpressionResultScope {
public:
ExpressionResultScope(BytecodeGenerator* generator, Expression::Context kind)
- : generator_(generator),
- outer_(generator->execution_result()),
+ : outer_(generator->execution_result()),
allocator_(generator),
kind_(kind),
type_hint_(TypeHint::kAny) {
- generator_->set_execution_result(this);
+ generator->set_execution_result(this);
}
- virtual ~ExpressionResultScope() {
- generator_->set_execution_result(outer_);
+ ~ExpressionResultScope() {
+ allocator_.generator()->set_execution_result(outer_);
}
bool IsEffect() const { return kind_ == Expression::kEffect; }
@@ -599,7 +635,6 @@ class BytecodeGenerator::ExpressionResultScope {
TypeHint type_hint() const { return type_hint_; }
private:
- BytecodeGenerator* generator_;
ExpressionResultScope* outer_;
RegisterAllocationScope allocator_;
Expression::Context kind_;
@@ -639,9 +674,7 @@ class BytecodeGenerator::TestResultScope final : public ExpressionResultScope {
// Used when code special cases for TestResultScope and consumes any
// possible value by testing and jumping to a then/else label.
- void SetResultConsumedByTest() {
- result_consumed_by_test_ = true;
- }
+ void SetResultConsumedByTest() { result_consumed_by_test_ = true; }
bool result_consumed_by_test() { return result_consumed_by_test_; }
// Inverts the control flow of the operation, swapping the then and else
@@ -730,7 +763,7 @@ class BytecodeGenerator::GlobalDeclarationsBuilder final : public ZoneObject {
data->set(array_index++, *declaration.name->string());
data->set(array_index++, Smi::FromInt(declaration.slot.ToInt()));
- Object* undefined_or_literal_slot;
+ Object undefined_or_literal_slot;
if (declaration.literal_slot.IsInvalid()) {
undefined_or_literal_slot = ReadOnlyRoots(isolate).undefined_value();
} else {
@@ -871,7 +904,7 @@ class BytecodeGenerator::IteratorRecord final {
static bool IsInEagerLiterals(
FunctionLiteral* literal,
- const ZoneVector<FunctionLiteral*>& eager_literals) {
+ const std::vector<FunctionLiteral*>& eager_literals) {
for (FunctionLiteral* eager_literal : eager_literals) {
if (literal == eager_literal) return true;
}
@@ -883,7 +916,7 @@ static bool IsInEagerLiterals(
BytecodeGenerator::BytecodeGenerator(
UnoptimizedCompilationInfo* info,
const AstStringConstants* ast_string_constants,
- ZoneVector<FunctionLiteral*>* eager_inner_literals)
+ std::vector<FunctionLiteral*>* eager_inner_literals)
: zone_(info->zone()),
builder_(zone(), info->num_parameters_including_this(),
info->scope()->num_stack_slots(), info->feedback_vector_spec(),
@@ -922,6 +955,12 @@ BytecodeGenerator::BytecodeGenerator(
Handle<BytecodeArray> BytecodeGenerator::FinalizeBytecode(
Isolate* isolate, Handle<Script> script) {
DCHECK(ThreadId::Current().Equals(isolate->thread_id()));
+#ifdef DEBUG
+ // Unoptimized compilation should be context-independent. Verify that we don't
+ // access the native context by nulling it out during finalization.
+ SaveContext save(isolate);
+ isolate->set_context(Context());
+#endif
AllocateDeferredConstants(isolate, script);
@@ -1075,7 +1114,7 @@ void BytecodeGenerator::GenerateBytecodeBody() {
// Create a generator object if necessary and initialize the
// {.generator_object} variable.
- if (info()->literal()->CanSuspend()) {
+ if (IsResumableFunction(info()->literal()->kind())) {
BuildGeneratorObjectVariableInitialization();
}
@@ -1104,9 +1143,9 @@ void BytecodeGenerator::GenerateBytecodeBody() {
// The derived constructor case is handled in VisitCallSuper.
if (IsBaseConstructor(function_kind()) &&
- info()->literal()->requires_instance_fields_initializer()) {
- BuildInstanceFieldInitialization(Register::function_closure(),
- builder()->Receiver());
+ info()->literal()->requires_instance_members_initializer()) {
+ BuildInstanceMemberInitialization(Register::function_closure(),
+ builder()->Receiver());
}
// Visit statements in the function body.
@@ -1121,7 +1160,7 @@ void BytecodeGenerator::GenerateBytecodeBody() {
}
void BytecodeGenerator::AllocateTopLevelRegisters() {
- if (info()->literal()->CanSuspend()) {
+ if (IsResumableFunction(info()->literal()->kind())) {
// Either directly use generator_object_var or allocate a new register for
// the incoming generator object.
Variable* generator_object_var = closure_scope()->generator_object_var();
@@ -1181,7 +1220,7 @@ void BytecodeGenerator::VisitBlockDeclarationsAndStatements(Block* stmt) {
}
void BytecodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) {
- Variable* variable = decl->proxy()->var();
+ Variable* variable = decl->var();
switch (variable->location()) {
case VariableLocation::UNALLOCATED: {
DCHECK(!variable->binding_needs_init());
@@ -1210,7 +1249,7 @@ void BytecodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) {
}
break;
case VariableLocation::LOOKUP: {
- DCHECK_EQ(VariableMode::kVar, variable->mode());
+ DCHECK_EQ(VariableMode::kDynamic, variable->mode());
DCHECK(!variable->binding_needs_init());
Register name = register_allocator()->NewRegister();
@@ -1232,9 +1271,10 @@ void BytecodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) {
}
void BytecodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* decl) {
- Variable* variable = decl->proxy()->var();
+ Variable* variable = decl->var();
DCHECK(variable->mode() == VariableMode::kLet ||
- variable->mode() == VariableMode::kVar);
+ variable->mode() == VariableMode::kVar ||
+ variable->mode() == VariableMode::kDynamic);
switch (variable->location()) {
case VariableLocation::UNALLOCATED: {
FeedbackSlot slot =
@@ -1247,13 +1287,13 @@ void BytecodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* decl) {
}
case VariableLocation::PARAMETER:
case VariableLocation::LOCAL: {
- VisitForAccumulatorValue(decl->fun());
+ VisitFunctionLiteral(decl->fun());
BuildVariableAssignment(variable, Token::INIT, HoleCheckMode::kElided);
break;
}
case VariableLocation::CONTEXT: {
DCHECK_EQ(0, execution_context()->ContextChainDepth(variable->scope()));
- VisitForAccumulatorValue(decl->fun());
+ VisitFunctionLiteral(decl->fun());
builder()->StoreContextSlot(execution_context()->reg(), variable->index(),
0);
break;
@@ -1263,7 +1303,7 @@ void BytecodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* decl) {
builder()
->LoadLiteral(variable->raw_name())
.StoreAccumulatorInRegister(args[0]);
- VisitForAccumulatorValue(decl->fun());
+ VisitFunctionLiteral(decl->fun());
builder()->StoreAccumulatorInRegister(args[1]).CallRuntime(
Runtime::kDeclareEvalFunction, args);
break;
@@ -1291,8 +1331,7 @@ void BytecodeGenerator::VisitModuleNamespaceImports() {
->LoadLiteral(Smi::FromInt(entry->module_request))
.StoreAccumulatorInRegister(module_request)
.CallRuntime(Runtime::kGetModuleNamespace, module_request);
- Variable* var = closure_scope()->LookupLocal(entry->local_name);
- DCHECK_NOT_NULL(var);
+ Variable* var = closure_scope()->LookupInModule(entry->local_name);
BuildVariableAssignment(var, Token::INIT, HoleCheckMode::kElided);
}
}
@@ -1326,13 +1365,14 @@ void BytecodeGenerator::VisitDeclarations(Declaration::List* declarations) {
globals_builder_ = new (zone()) GlobalDeclarationsBuilder(zone());
}
-void BytecodeGenerator::VisitStatements(ZonePtrList<Statement>* statements) {
+void BytecodeGenerator::VisitStatements(
+ const ZonePtrList<Statement>* statements) {
for (int i = 0; i < statements->length(); i++) {
// Allocate an outer register allocations scope for the statement.
RegisterAllocationScope allocation_scope(this);
Statement* stmt = statements->at(i);
Visit(stmt);
- if (stmt->IsJump()) break;
+ if (builder()->RemainderOfBlockIsDead()) break;
}
}
@@ -1341,8 +1381,7 @@ void BytecodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) {
VisitForEffect(stmt->expression());
}
-void BytecodeGenerator::VisitEmptyStatement(EmptyStatement* stmt) {
-}
+void BytecodeGenerator::VisitEmptyStatement(EmptyStatement* stmt) {}
void BytecodeGenerator::VisitIfStatement(IfStatement* stmt) {
ConditionalControlFlowBuilder conditional_builder(
@@ -1463,6 +1502,107 @@ void BytecodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
}
}
+template <typename TryBodyFunc, typename CatchBodyFunc>
+void BytecodeGenerator::BuildTryCatch(
+ TryBodyFunc try_body_func, CatchBodyFunc catch_body_func,
+ HandlerTable::CatchPrediction catch_prediction,
+ TryCatchStatement* stmt_for_coverage) {
+ TryCatchBuilder try_control_builder(
+ builder(),
+ stmt_for_coverage == nullptr ? nullptr : block_coverage_builder_,
+ stmt_for_coverage, catch_prediction);
+
+ // Preserve the context in a dedicated register, so that it can be restored
+ // when the handler is entered by the stack-unwinding machinery.
+ // TODO(mstarzinger): Be smarter about register allocation.
+ Register context = register_allocator()->NewRegister();
+ builder()->MoveRegister(Register::current_context(), context);
+
+ // Evaluate the try-block inside a control scope. This simulates a handler
+ // that is intercepting 'throw' control commands.
+ try_control_builder.BeginTry(context);
+ {
+ ControlScopeForTryCatch scope(this, &try_control_builder);
+ try_body_func();
+ }
+ try_control_builder.EndTry();
+
+ catch_body_func(context);
+
+ try_control_builder.EndCatch();
+}
+
+template <typename TryBodyFunc, typename FinallyBodyFunc>
+void BytecodeGenerator::BuildTryFinally(
+ TryBodyFunc try_body_func, FinallyBodyFunc finally_body_func,
+ HandlerTable::CatchPrediction catch_prediction,
+ TryFinallyStatement* stmt_for_coverage) {
+ // We can't know whether the finally block will override ("catch") an
+ // exception thrown in the try block, so we just adopt the outer prediction.
+ TryFinallyBuilder try_control_builder(
+ builder(),
+ stmt_for_coverage == nullptr ? nullptr : block_coverage_builder_,
+ stmt_for_coverage, catch_prediction);
+
+ // We keep a record of all paths that enter the finally-block to be able to
+ // dispatch to the correct continuation point after the statements in the
+ // finally-block have been evaluated.
+ //
+ // The try-finally construct can enter the finally-block in three ways:
+ // 1. By exiting the try-block normally, falling through at the end.
+ // 2. By exiting the try-block with a function-local control flow transfer
+ // (i.e. through break/continue/return statements).
+ // 3. By exiting the try-block with a thrown exception.
+ //
+ // The result register semantics depend on how the block was entered:
+ // - ReturnStatement: It represents the return value being returned.
+ // - ThrowStatement: It represents the exception being thrown.
+ // - BreakStatement/ContinueStatement: Undefined and not used.
+ // - Falling through into finally-block: Undefined and not used.
+ Register token = register_allocator()->NewRegister();
+ Register result = register_allocator()->NewRegister();
+ ControlScope::DeferredCommands commands(this, token, result);
+
+ // Preserve the context in a dedicated register, so that it can be restored
+ // when the handler is entered by the stack-unwinding machinery.
+ // TODO(mstarzinger): Be smarter about register allocation.
+ Register context = register_allocator()->NewRegister();
+ builder()->MoveRegister(Register::current_context(), context);
+
+ // Evaluate the try-block inside a control scope. This simulates a handler
+ // that is intercepting all control commands.
+ try_control_builder.BeginTry(context);
+ {
+ ControlScopeForTryFinally scope(this, &try_control_builder, &commands);
+ try_body_func();
+ }
+ try_control_builder.EndTry();
+
+ // Record fall-through and exception cases.
+ commands.RecordFallThroughPath();
+ try_control_builder.LeaveTry();
+ try_control_builder.BeginHandler();
+ commands.RecordHandlerReThrowPath();
+
+ // Pending message object is saved on entry.
+ try_control_builder.BeginFinally();
+ Register message = context; // Reuse register.
+
+ // Clear message object as we enter the finally block.
+ builder()->LoadTheHole().SetPendingMessage().StoreAccumulatorInRegister(
+ message);
+
+ // Evaluate the finally-block.
+ finally_body_func(token);
+ try_control_builder.EndFinally();
+
+ // Pending message object is restored on exit.
+ builder()->LoadAccumulatorWithRegister(message).SetPendingMessage();
+
+ // Dynamic dispatch after the finally-block.
+ commands.ApplyDeferredCommands();
+}
+
void BytecodeGenerator::VisitIterationBody(IterationStatement* stmt,
LoopBuilder* loop_builder) {
loop_builder->LoopBody();
@@ -1540,76 +1680,6 @@ void BytecodeGenerator::VisitForStatement(ForStatement* stmt) {
loop_builder.JumpToHeader(loop_depth_);
}
-void BytecodeGenerator::VisitForInAssignment(Expression* expr) {
- DCHECK(expr->IsValidReferenceExpression());
-
- // Evaluate assignment starting with the value to be stored in the
- // accumulator.
- Property* property = expr->AsProperty();
- LhsKind assign_type = Property::GetAssignType(property);
- switch (assign_type) {
- case VARIABLE: {
- VariableProxy* proxy = expr->AsVariableProxy();
- BuildVariableAssignment(proxy->var(), Token::ASSIGN,
- proxy->hole_check_mode());
- break;
- }
- case NAMED_PROPERTY: {
- RegisterAllocationScope register_scope(this);
- Register value = register_allocator()->NewRegister();
- builder()->StoreAccumulatorInRegister(value);
- Register object = VisitForRegisterValue(property->obj());
- const AstRawString* name =
- property->key()->AsLiteral()->AsRawPropertyName();
- builder()->LoadAccumulatorWithRegister(value);
- FeedbackSlot slot = GetCachedStoreICSlot(property->obj(), name);
- builder()->StoreNamedProperty(object, name, feedback_index(slot),
- language_mode());
- builder()->LoadAccumulatorWithRegister(value);
- break;
- }
- case KEYED_PROPERTY: {
- RegisterAllocationScope register_scope(this);
- Register value = register_allocator()->NewRegister();
- builder()->StoreAccumulatorInRegister(value);
- Register object = VisitForRegisterValue(property->obj());
- Register key = VisitForRegisterValue(property->key());
- builder()->LoadAccumulatorWithRegister(value);
- FeedbackSlot slot = feedback_spec()->AddKeyedStoreICSlot(language_mode());
- builder()->StoreKeyedProperty(object, key, feedback_index(slot),
- language_mode());
- builder()->LoadAccumulatorWithRegister(value);
- break;
- }
- case NAMED_SUPER_PROPERTY: {
- RegisterAllocationScope register_scope(this);
- RegisterList args = register_allocator()->NewRegisterList(4);
- builder()->StoreAccumulatorInRegister(args[3]);
- SuperPropertyReference* super_property =
- property->obj()->AsSuperPropertyReference();
- VisitForRegisterValue(super_property->this_var(), args[0]);
- VisitForRegisterValue(super_property->home_object(), args[1]);
- builder()
- ->LoadLiteral(property->key()->AsLiteral()->AsRawPropertyName())
- .StoreAccumulatorInRegister(args[2])
- .CallRuntime(StoreToSuperRuntimeId(), args);
- break;
- }
- case KEYED_SUPER_PROPERTY: {
- RegisterAllocationScope register_scope(this);
- RegisterList args = register_allocator()->NewRegisterList(4);
- builder()->StoreAccumulatorInRegister(args[3]);
- SuperPropertyReference* super_property =
- property->obj()->AsSuperPropertyReference();
- VisitForRegisterValue(super_property->this_var(), args[0]);
- VisitForRegisterValue(super_property->home_object(), args[1]);
- VisitForRegisterValue(property->key(), args[2]);
- builder()->CallRuntime(StoreKeyedToSuperRuntimeId(), args);
- break;
- }
- }
-}
-
void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) {
if (stmt->subject()->IsNullLiteral() ||
stmt->subject()->IsUndefinedLiteral()) {
@@ -1636,7 +1706,7 @@ void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) {
// Set up loop counter
Register index = register_allocator()->NewRegister();
- builder()->LoadLiteral(Smi::kZero);
+ builder()->LoadLiteral(Smi::zero());
builder()->StoreAccumulatorInRegister(index);
// The loop
@@ -1649,7 +1719,18 @@ void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) {
builder()->ForInNext(receiver, index, triple.Truncate(2),
feedback_index(slot));
loop_builder.ContinueIfUndefined();
- VisitForInAssignment(stmt->each());
+
+ // Assign accumulator value to the 'each' target.
+ {
+ EffectResultScope scope(this);
+ // Make sure to preserve the accumulator across the PrepareAssignmentLhs
+ // call.
+ AssignmentLhsData lhs_data = PrepareAssignmentLhs(
+ stmt->each(), AccumulatorPreservingMode::kPreserve);
+ builder()->SetExpressionPosition(stmt->each());
+ BuildAssignment(lhs_data, Token::ASSIGN, LookupHoistingMode::kNormal);
+ }
+
VisitIterationBody(stmt, &loop_builder);
builder()->ForInStep(index);
builder()->StoreAccumulatorInRegister(index);
@@ -1659,22 +1740,94 @@ void BytecodeGenerator::VisitForInStatement(ForInStatement* stmt) {
builder()->Bind(&subject_undefined_label);
}
+// Desugar a for-of statement into an application of the iteration protocol.
+//
+// for (EACH of SUBJECT) BODY
+//
+// becomes
+//
+// iterator = %GetIterator(SUBJECT)
+// try {
+//
+// loop {
+// // Make sure we are considered 'done' if .next(), .done or .value fail.
+// done = true
+// value = iterator.next()
+// if (value.done) break;
+// value = value.value
+// done = false
+//
+// EACH = value
+// BODY
+// }
+// done = true
+//
+// } catch(e) {
+// iteration_continuation = RETHROW
+// } finally {
+// %FinalizeIteration(iterator, done, iteration_continuation)
+// }
void BytecodeGenerator::VisitForOfStatement(ForOfStatement* stmt) {
- LoopBuilder loop_builder(builder(), block_coverage_builder_, stmt);
+ EffectResultScope effect_scope(this);
- builder()->SetExpressionAsStatementPosition(stmt->assign_iterator());
- VisitForEffect(stmt->assign_iterator());
- VisitForEffect(stmt->assign_next());
+ builder()->SetExpressionAsStatementPosition(stmt->subject());
+ VisitForAccumulatorValue(stmt->subject());
- loop_builder.LoopHeader();
- builder()->SetExpressionAsStatementPosition(stmt->next_result());
- VisitForEffect(stmt->next_result());
- TypeHint type_hint = VisitForAccumulatorValue(stmt->result_done());
- loop_builder.BreakIfTrue(ToBooleanModeFromTypeHint(type_hint));
+ // Store the iterator in a dedicated register so that it can be closed on
+ // exit, and the 'done' value in a dedicated register so that it can be
+ // changed and accessed independently of the iteration result.
+ IteratorRecord iterator = BuildGetIteratorRecord(stmt->type());
+ Register done = register_allocator()->NewRegister();
+ builder()->LoadFalse();
+ builder()->StoreAccumulatorInRegister(done);
+
+ BuildTryFinally(
+ // Try block.
+ [&]() {
+ Register next_result = register_allocator()->NewRegister();
+
+ LoopBuilder loop_builder(builder(), block_coverage_builder_, stmt);
+ loop_builder.LoopHeader();
+
+ builder()->LoadTrue().StoreAccumulatorInRegister(done);
+
+ // Call the iterator's .next() method. Break from the loop if the `done`
+ // property is truthy, otherwise load the value from the iterator result
+ // and append the argument.
+ builder()->SetExpressionAsStatementPosition(stmt->each());
+ BuildIteratorNext(iterator, next_result);
+ builder()->LoadNamedProperty(
+ next_result, ast_string_constants()->done_string(),
+ feedback_index(feedback_spec()->AddLoadICSlot()));
+ loop_builder.BreakIfTrue(ToBooleanMode::kConvertToBoolean);
- VisitForEffect(stmt->assign_each());
- VisitIterationBody(stmt, &loop_builder);
- loop_builder.JumpToHeader(loop_depth_);
+ builder()
+ // value = value.value
+ ->LoadNamedProperty(
+ next_result, ast_string_constants()->value_string(),
+ feedback_index(feedback_spec()->AddLoadICSlot()));
+ // done = false, before the assignment to each happens, so that done is
+ // false if the assignment throws.
+ builder()
+ ->StoreAccumulatorInRegister(next_result)
+ .LoadFalse()
+ .StoreAccumulatorInRegister(done);
+
+ // Assign to the 'each' target.
+ AssignmentLhsData lhs_data = PrepareAssignmentLhs(stmt->each());
+ builder()->LoadAccumulatorWithRegister(next_result);
+ BuildAssignment(lhs_data, Token::ASSIGN, LookupHoistingMode::kNormal);
+
+ VisitIterationBody(stmt, &loop_builder);
+
+ loop_builder.JumpToHeader(loop_depth_);
+ },
+ // Finally block.
+ [&](Register iteration_continuation_token) {
+ // Finish the iteration in the finally block.
+ BuildFinalizeIteration(iterator, done, iteration_continuation_token);
+ },
+ HandlerTable::UNCAUGHT);
}
void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
@@ -1684,111 +1837,45 @@ void BytecodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
HandlerTable::CatchPrediction outer_catch_prediction = catch_prediction();
set_catch_prediction(stmt->GetCatchPrediction(outer_catch_prediction));
- TryCatchBuilder try_control_builder(builder(), block_coverage_builder_, stmt,
- catch_prediction());
-
- // Preserve the context in a dedicated register, so that it can be restored
- // when the handler is entered by the stack-unwinding machinery.
- // TODO(mstarzinger): Be smarter about register allocation.
- Register context = register_allocator()->NewRegister();
- builder()->MoveRegister(Register::current_context(), context);
-
- // Evaluate the try-block inside a control scope. This simulates a handler
- // that is intercepting 'throw' control commands.
- try_control_builder.BeginTry(context);
- {
- ControlScopeForTryCatch scope(this, &try_control_builder);
- Visit(stmt->try_block());
- set_catch_prediction(outer_catch_prediction);
- }
- try_control_builder.EndTry();
-
- if (stmt->scope()) {
- // Create a catch scope that binds the exception.
- BuildNewLocalCatchContext(stmt->scope());
- builder()->StoreAccumulatorInRegister(context);
- }
+ BuildTryCatch(
+ // Try body.
+ [&]() {
+ Visit(stmt->try_block());
+ set_catch_prediction(outer_catch_prediction);
+ },
+ // Catch body.
+ [&](Register context) {
+ if (stmt->scope()) {
+ // Create a catch scope that binds the exception.
+ BuildNewLocalCatchContext(stmt->scope());
+ builder()->StoreAccumulatorInRegister(context);
+ }
- // If requested, clear message object as we enter the catch block.
- if (stmt->ShouldClearPendingException(outer_catch_prediction)) {
- builder()->LoadTheHole().SetPendingMessage();
- }
+ // If requested, clear message object as we enter the catch block.
+ if (stmt->ShouldClearPendingException(outer_catch_prediction)) {
+ builder()->LoadTheHole().SetPendingMessage();
+ }
- // Load the catch context into the accumulator.
- builder()->LoadAccumulatorWithRegister(context);
+ // Load the catch context into the accumulator.
+ builder()->LoadAccumulatorWithRegister(context);
- // Evaluate the catch-block.
- if (stmt->scope()) {
- VisitInScope(stmt->catch_block(), stmt->scope());
- } else {
- VisitBlock(stmt->catch_block());
- }
- try_control_builder.EndCatch();
+ // Evaluate the catch-block.
+ if (stmt->scope()) {
+ VisitInScope(stmt->catch_block(), stmt->scope());
+ } else {
+ VisitBlock(stmt->catch_block());
+ }
+ },
+ catch_prediction(), stmt);
}
void BytecodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
- // We can't know whether the finally block will override ("catch") an
- // exception thrown in the try block, so we just adopt the outer prediction.
- TryFinallyBuilder try_control_builder(builder(), block_coverage_builder_,
- stmt, catch_prediction());
-
- // We keep a record of all paths that enter the finally-block to be able to
- // dispatch to the correct continuation point after the statements in the
- // finally-block have been evaluated.
- //
- // The try-finally construct can enter the finally-block in three ways:
- // 1. By exiting the try-block normally, falling through at the end.
- // 2. By exiting the try-block with a function-local control flow transfer
- // (i.e. through break/continue/return statements).
- // 3. By exiting the try-block with a thrown exception.
- //
- // The result register semantics depend on how the block was entered:
- // - ReturnStatement: It represents the return value being returned.
- // - ThrowStatement: It represents the exception being thrown.
- // - BreakStatement/ContinueStatement: Undefined and not used.
- // - Falling through into finally-block: Undefined and not used.
- Register token = register_allocator()->NewRegister();
- Register result = register_allocator()->NewRegister();
- ControlScope::DeferredCommands commands(this, token, result);
-
- // Preserve the context in a dedicated register, so that it can be restored
- // when the handler is entered by the stack-unwinding machinery.
- // TODO(mstarzinger): Be smarter about register allocation.
- Register context = register_allocator()->NewRegister();
- builder()->MoveRegister(Register::current_context(), context);
-
- // Evaluate the try-block inside a control scope. This simulates a handler
- // that is intercepting all control commands.
- try_control_builder.BeginTry(context);
- {
- ControlScopeForTryFinally scope(this, &try_control_builder, &commands);
- Visit(stmt->try_block());
- }
- try_control_builder.EndTry();
-
- // Record fall-through and exception cases.
- commands.RecordFallThroughPath();
- try_control_builder.LeaveTry();
- try_control_builder.BeginHandler();
- commands.RecordHandlerReThrowPath();
-
- // Pending message object is saved on entry.
- try_control_builder.BeginFinally();
- Register message = context; // Reuse register.
-
- // Clear message object as we enter the finally block.
- builder()->LoadTheHole().SetPendingMessage().StoreAccumulatorInRegister(
- message);
-
- // Evaluate the finally-block.
- Visit(stmt->finally_block());
- try_control_builder.EndFinally();
-
- // Pending message object is restored on exit.
- builder()->LoadAccumulatorWithRegister(message).SetPendingMessage();
-
- // Dynamic dispatch after the finally-block.
- commands.ApplyDeferredCommands();
+ BuildTryFinally(
+ // Try block.
+ [&]() { Visit(stmt->try_block()); },
+ // Finally block.
+ [&](Register body_continuation_token) { Visit(stmt->finally_block()); },
+ catch_prediction(), stmt);
}
void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) {
@@ -1799,7 +1886,8 @@ void BytecodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) {
void BytecodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
DCHECK(expr->scope()->outer_scope() == current_scope());
uint8_t flags = CreateClosureFlags::Encode(
- expr->pretenure(), closure_scope()->is_function_scope());
+ expr->pretenure(), closure_scope()->is_function_scope(),
+ info()->might_always_opt());
size_t entry = builder()->AllocateDeferredConstantPoolEntry();
FeedbackSlot slot = GetCachedCreateClosureSlot(expr);
builder()->CreateClosure(entry, feedback_index(slot), flags);
@@ -1819,14 +1907,11 @@ bool BytecodeGenerator::ShouldOptimizeAsOneShot() const {
if (loop_depth_ > 0) return false;
- // A non-top-level iife is likely to be executed multiple times and so
- // shouldn`t be optimized as one-shot.
- bool is_toplevel_iife = info()->literal()->is_iife() &&
- current_scope()->outer_scope()->is_script_scope();
- return info()->literal()->is_toplevel() || is_toplevel_iife;
+ return info()->literal()->is_toplevel() ||
+ info()->literal()->is_oneshot_iife();
}
-void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) {
+void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr, Register name) {
size_t class_boilerplate_entry =
builder()->AllocateDeferredConstantPoolEntry();
class_literals_.push_back(std::make_pair(expr, class_boilerplate_entry));
@@ -1859,7 +1944,6 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) {
for (int i = 0; i < expr->properties()->length(); i++) {
ClassLiteral::Property* property = expr->properties()->at(i);
if (property->is_computed_name()) {
- DCHECK_NE(property->kind(), ClassLiteral::Property::PRIVATE_FIELD);
Register key = register_allocator()->GrowRegisterList(&args);
builder()->SetExpressionAsStatementPosition(property->key());
@@ -1881,7 +1965,8 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) {
.Bind(&done);
}
- if (property->kind() == ClassLiteral::Property::PUBLIC_FIELD) {
+ if (property->kind() == ClassLiteral::Property::FIELD) {
+ DCHECK(!property->is_private());
// Initialize field's name variable with the computed name.
DCHECK_NOT_NULL(property->computed_name_var());
builder()->LoadAccumulatorWithRegister(key);
@@ -1890,16 +1975,22 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) {
}
}
- if (property->kind() == ClassLiteral::Property::PUBLIC_FIELD) {
+ if (property->kind() == ClassLiteral::Property::FIELD) {
+ if (property->is_private()) {
+ RegisterAllocationScope private_name_register_scope(this);
+ Register private_name = register_allocator()->NewRegister();
+ VisitForRegisterValue(property->key(), private_name);
+ builder()
+ ->LoadLiteral(property->key()->AsLiteral()->AsRawPropertyName())
+ .StoreAccumulatorInRegister(private_name)
+ .CallRuntime(Runtime::kCreatePrivateNameSymbol, private_name);
+ DCHECK_NOT_NULL(property->private_name_var());
+ BuildVariableAssignment(property->private_name_var(), Token::INIT,
+ HoleCheckMode::kElided);
+ }
// We don't compute field's value here, but instead do it in the
// initializer function.
continue;
- } else if (property->kind() == ClassLiteral::Property::PRIVATE_FIELD) {
- builder()->CallRuntime(Runtime::kCreatePrivateFieldSymbol);
- DCHECK_NOT_NULL(property->private_field_name_var());
- BuildVariableAssignment(property->private_field_name_var(), Token::INIT,
- HoleCheckMode::kElided);
- continue;
}
Register value = register_allocator()->GrowRegisterList(&args);
@@ -1920,12 +2011,12 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) {
HoleCheckMode::kElided);
}
- if (expr->instance_fields_initializer_function() != nullptr) {
+ if (expr->instance_members_initializer_function() != nullptr) {
Register initializer =
- VisitForRegisterValue(expr->instance_fields_initializer_function());
+ VisitForRegisterValue(expr->instance_members_initializer_function());
if (FunctionLiteral::NeedsHomeObject(
- expr->instance_fields_initializer_function())) {
+ expr->instance_members_initializer_function())) {
FeedbackSlot slot = feedback_spec()->AddStoreICSlot(language_mode());
builder()->LoadAccumulatorWithRegister(prototype).StoreHomeObjectProperty(
initializer, feedback_index(slot), language_mode());
@@ -1939,6 +2030,24 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) {
}
if (expr->static_fields_initializer() != nullptr) {
+ // TODO(gsathya): This can be optimized away to be a part of the
+ // class boilerplate in the future. The name argument can be
+ // passed to the DefineClass runtime function and have it set
+ // there.
+ if (name.is_valid()) {
+ Register key = register_allocator()->NewRegister();
+ builder()
+ ->LoadLiteral(ast_string_constants()->name_string())
+ .StoreAccumulatorInRegister(key);
+
+ DataPropertyInLiteralFlags data_property_flags =
+ DataPropertyInLiteralFlag::kNoFlags;
+ FeedbackSlot slot =
+ feedback_spec()->AddStoreDataPropertyInLiteralICSlot();
+ builder()->LoadAccumulatorWithRegister(name).StoreDataPropertyInLiteral(
+ class_constructor, key, data_property_flags, feedback_index(slot));
+ }
+
RegisterList args = register_allocator()->NewRegisterList(1);
Register initializer =
VisitForRegisterValue(expr->static_fields_initializer());
@@ -1960,19 +2069,23 @@ void BytecodeGenerator::BuildClassLiteral(ClassLiteral* expr) {
}
void BytecodeGenerator::VisitClassLiteral(ClassLiteral* expr) {
+ VisitClassLiteral(expr, Register::invalid_value());
+}
+
+void BytecodeGenerator::VisitClassLiteral(ClassLiteral* expr, Register name) {
CurrentScope current_scope(this, expr->scope());
DCHECK_NOT_NULL(expr->scope());
if (expr->scope()->NeedsContext()) {
BuildNewLocalBlockContext(expr->scope());
ContextScope scope(this, expr->scope());
- BuildClassLiteral(expr);
+ BuildClassLiteral(expr, name);
} else {
- BuildClassLiteral(expr);
+ BuildClassLiteral(expr, name);
}
}
-void BytecodeGenerator::VisitInitializeClassFieldsStatement(
- InitializeClassFieldsStatement* stmt) {
+void BytecodeGenerator::VisitInitializeClassMembersStatement(
+ InitializeClassMembersStatement* stmt) {
RegisterList args = register_allocator()->NewRegisterList(3);
Register constructor = args[0], key = args[1], value = args[2];
builder()->MoveRegister(builder()->Receiver(), constructor);
@@ -1981,17 +2094,19 @@ void BytecodeGenerator::VisitInitializeClassFieldsStatement(
ClassLiteral::Property* property = stmt->fields()->at(i);
if (property->is_computed_name()) {
- DCHECK_EQ(property->kind(), ClassLiteral::Property::PUBLIC_FIELD);
+ DCHECK_EQ(property->kind(), ClassLiteral::Property::FIELD);
+ DCHECK(!property->is_private());
Variable* var = property->computed_name_var();
DCHECK_NOT_NULL(var);
// The computed name is already evaluated and stored in a
// variable at class definition time.
BuildVariableLoad(var, HoleCheckMode::kElided);
builder()->StoreAccumulatorInRegister(key);
- } else if (property->kind() == ClassLiteral::Property::PRIVATE_FIELD) {
- Variable* private_field_name_var = property->private_field_name_var();
- DCHECK_NOT_NULL(private_field_name_var);
- BuildVariableLoad(private_field_name_var, HoleCheckMode::kElided);
+ } else if (property->kind() == ClassLiteral::Property::FIELD &&
+ property->is_private()) {
+ Variable* private_name_var = property->private_name_var();
+ DCHECK_NOT_NULL(private_name_var);
+ BuildVariableLoad(private_name_var, HoleCheckMode::kElided);
builder()->StoreAccumulatorInRegister(key);
} else {
BuildLoadPropertyKey(property, key);
@@ -2002,15 +2117,16 @@ void BytecodeGenerator::VisitInitializeClassFieldsStatement(
VisitSetHomeObject(value, constructor, property);
Runtime::FunctionId function_id =
- property->kind() == ClassLiteral::Property::PUBLIC_FIELD
+ property->kind() == ClassLiteral::Property::FIELD &&
+ !property->is_private()
? Runtime::kCreateDataProperty
: Runtime::kAddPrivateField;
builder()->CallRuntime(function_id, args);
}
}
-void BytecodeGenerator::BuildInstanceFieldInitialization(Register constructor,
- Register instance) {
+void BytecodeGenerator::BuildInstanceMemberInitialization(Register constructor,
+ Register instance) {
RegisterList args = register_allocator()->NewRegisterList(1);
Register initializer = register_allocator()->NewRegister();
@@ -2127,7 +2243,9 @@ void BytecodeGenerator::BuildCreateObjectLiteral(Register literal,
// optimize once the CreateShallowObjectLiteral stub is in sync with the TF
// optimizations.
int literal_index = feedback_index(feedback_spec()->AddLiteralSlot());
- builder()->CreateObjectLiteral(entry, literal_index, flags, literal);
+ builder()
+ ->CreateObjectLiteral(entry, literal_index, flags)
+ .StoreAccumulatorInRegister(literal);
}
}
@@ -2317,7 +2435,20 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
Register key = register_allocator()->NewRegister();
BuildLoadPropertyKey(property, key);
builder()->SetExpressionPosition(property->value());
- Register value = VisitForRegisterValue(property->value());
+ Register value;
+
+ // Static class fields require the name property to be set on
+ // the class, meaning we can't wait until the
+ // StoreDataPropertyInLiteral call later to set the name.
+ if (property->value()->IsClassLiteral() &&
+ property->value()->AsClassLiteral()->static_fields_initializer() !=
+ nullptr) {
+ value = register_allocator()->NewRegister();
+ VisitClassLiteral(property->value()->AsClassLiteral(), key);
+ builder()->StoreAccumulatorInRegister(value);
+ } else {
+ value = VisitForRegisterValue(property->value());
+ }
VisitSetHomeObject(value, literal, property);
DataPropertyInLiteralFlags data_property_flags =
@@ -2369,16 +2500,25 @@ void BytecodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
builder()->LoadAccumulatorWithRegister(literal);
}
-void BytecodeGenerator::BuildArrayLiteralSpread(Spread* spread, Register array,
- Register index,
- FeedbackSlot index_slot,
- FeedbackSlot element_slot) {
- RegisterAllocationScope register_scope(this);
- Register value = register_allocator()->NewRegister();
-
- builder()->SetExpressionAsStatementPosition(spread->expression());
- IteratorRecord iterator =
- BuildGetIteratorRecord(spread->expression(), IteratorType::kNormal);
+// Fill an array with values from an iterator, starting at a given index. It is
+// guaranteed that the loop will only terminate if the iterator is exhausted, or
+// if one of iterator.next(), value.done, or value.value fail.
+//
+// In pseudocode:
+//
+// loop {
+// value = iterator.next()
+// if (value.done) break;
+// value = value.value
+// array[index++] = value
+// }
+void BytecodeGenerator::BuildFillArrayWithIterator(
+ IteratorRecord iterator, Register array, Register index, Register value,
+ FeedbackSlot next_value_slot, FeedbackSlot next_done_slot,
+ FeedbackSlot index_slot, FeedbackSlot element_slot) {
+ DCHECK(array.is_valid());
+ DCHECK(index.is_valid());
+ DCHECK(value.is_valid());
LoopBuilder loop_builder(builder(), nullptr, nullptr);
loop_builder.LoopHeader();
@@ -2396,8 +2536,7 @@ void BytecodeGenerator::BuildArrayLiteralSpread(Spread* spread, Register array,
builder()
// value = value.value
->LoadNamedProperty(value, ast_string_constants()->value_string(),
- feedback_index(feedback_spec()->AddLoadICSlot()))
- .StoreAccumulatorInRegister(value)
+ feedback_index(next_value_slot))
// array[index] = value
.StoreInArrayLiteral(array, index, feedback_index(element_slot))
// index++
@@ -2409,7 +2548,7 @@ void BytecodeGenerator::BuildArrayLiteralSpread(Spread* spread, Register array,
}
void BytecodeGenerator::BuildCreateArrayLiteral(
- ZonePtrList<Expression>* elements, ArrayLiteral* expr) {
+ const ZonePtrList<Expression>* elements, ArrayLiteral* expr) {
RegisterAllocationScope register_scope(this);
Register index = register_allocator()->NewRegister();
Register array = register_allocator()->NewRegister();
@@ -2519,9 +2658,20 @@ void BytecodeGenerator::BuildCreateArrayLiteral(
for (; current != end; ++current) {
Expression* subexpr = *current;
if (subexpr->IsSpread()) {
+ RegisterAllocationScope scope(this);
+ builder()->SetExpressionAsStatementPosition(
+ subexpr->AsSpread()->expression());
+ VisitForAccumulatorValue(subexpr->AsSpread()->expression());
+ IteratorRecord iterator = BuildGetIteratorRecord(IteratorType::kNormal);
+
+ Register value = register_allocator()->NewRegister();
+ FeedbackSlot next_value_load_slot = feedback_spec()->AddLoadICSlot();
+ FeedbackSlot next_done_load_slot = feedback_spec()->AddLoadICSlot();
FeedbackSlot real_index_slot = index_slot.Get();
- BuildArrayLiteralSpread(subexpr->AsSpread(), array, index,
- real_index_slot, element_slot.Get());
+ FeedbackSlot real_element_slot = element_slot.Get();
+ BuildFillArrayWithIterator(iterator, array, index, value,
+ next_value_load_slot, next_done_load_slot,
+ real_index_slot, real_element_slot);
} else if (!subexpr->IsTheHoleLiteral()) {
// literal[index++] = subexpr
VisitForAccumulatorValue(subexpr);
@@ -2712,18 +2862,13 @@ void BytecodeGenerator::BuildAsyncReturn(int source_position) {
.CallRuntime(Runtime::kInlineAsyncGeneratorResolve, args);
} else {
DCHECK(IsAsyncFunction(info()->literal()->kind()));
- RegisterList args = register_allocator()->NewRegisterList(2);
- Register promise = args[0];
- Register return_value = args[1];
- builder()->StoreAccumulatorInRegister(return_value);
-
- Variable* var_promise = closure_scope()->promise_var();
- DCHECK_NOT_NULL(var_promise);
- BuildVariableLoad(var_promise, HoleCheckMode::kElided);
+ RegisterList args = register_allocator()->NewRegisterList(3);
builder()
- ->StoreAccumulatorInRegister(promise)
- .CallRuntime(Runtime::kInlineResolvePromise, args)
- .LoadAccumulatorWithRegister(promise);
+ ->MoveRegister(generator_object(), args[0]) // generator
+ .StoreAccumulatorInRegister(args[1]) // value
+ .LoadBoolean(info()->literal()->CanSuspend())
+ .StoreAccumulatorInRegister(args[2]) // can_suspend
+ .CallRuntime(Runtime::kInlineAsyncFunctionResolve, args);
}
BuildReturn(source_position);
@@ -2863,18 +3008,18 @@ void BytecodeGenerator::BuildVariableAssignment(
}
}
-void BytecodeGenerator::BuildLoadNamedProperty(Property* property,
+void BytecodeGenerator::BuildLoadNamedProperty(const Expression* object_expr,
Register object,
const AstRawString* name) {
if (ShouldOptimizeAsOneShot()) {
builder()->LoadNamedPropertyNoFeedback(object, name);
} else {
- FeedbackSlot slot = GetCachedLoadICSlot(property->obj(), name);
+ FeedbackSlot slot = GetCachedLoadICSlot(object_expr, name);
builder()->LoadNamedProperty(object, name, feedback_index(slot));
}
}
-void BytecodeGenerator::BuildStoreNamedProperty(Property* property,
+void BytecodeGenerator::BuildStoreNamedProperty(const Expression* object_expr,
Register object,
const AstRawString* name) {
Register value;
@@ -2886,7 +3031,7 @@ void BytecodeGenerator::BuildStoreNamedProperty(Property* property,
if (ShouldOptimizeAsOneShot()) {
builder()->StoreNamedPropertyNoFeedback(object, name, language_mode());
} else {
- FeedbackSlot slot = GetCachedStoreICSlot(property->obj(), name);
+ FeedbackSlot slot = GetCachedStoreICSlot(object_expr, name);
builder()->StoreNamedProperty(object, name, feedback_index(slot),
language_mode());
}
@@ -2896,35 +3041,69 @@ void BytecodeGenerator::BuildStoreNamedProperty(Property* property,
}
}
-void BytecodeGenerator::VisitAssignment(Assignment* expr) {
- DCHECK(expr->target()->IsValidReferenceExpression() ||
- (expr->op() == Token::INIT && expr->target()->IsVariableProxy() &&
- expr->target()->AsVariableProxy()->is_this()));
- Register object, key;
- RegisterList super_property_args;
- const AstRawString* name;
-
+// static
+BytecodeGenerator::AssignmentLhsData
+BytecodeGenerator::AssignmentLhsData::NonProperty(Expression* expr) {
+ return AssignmentLhsData(NON_PROPERTY, expr, RegisterList(), Register(),
+ Register(), nullptr, nullptr);
+}
+// static
+BytecodeGenerator::AssignmentLhsData
+BytecodeGenerator::AssignmentLhsData::NamedProperty(Expression* object_expr,
+ Register object,
+ const AstRawString* name) {
+ return AssignmentLhsData(NAMED_PROPERTY, nullptr, RegisterList(), object,
+ Register(), object_expr, name);
+}
+// static
+BytecodeGenerator::AssignmentLhsData
+BytecodeGenerator::AssignmentLhsData::KeyedProperty(Register object,
+ Register key) {
+ return AssignmentLhsData(KEYED_PROPERTY, nullptr, RegisterList(), object, key,
+ nullptr, nullptr);
+}
+// static
+BytecodeGenerator::AssignmentLhsData
+BytecodeGenerator::AssignmentLhsData::NamedSuperProperty(
+ RegisterList super_property_args) {
+ return AssignmentLhsData(NAMED_SUPER_PROPERTY, nullptr, super_property_args,
+ Register(), Register(), nullptr, nullptr);
+}
+// static
+BytecodeGenerator::AssignmentLhsData
+BytecodeGenerator::AssignmentLhsData::KeyedSuperProperty(
+ RegisterList super_property_args) {
+ return AssignmentLhsData(KEYED_SUPER_PROPERTY, nullptr, super_property_args,
+ Register(), Register(), nullptr, nullptr);
+}
+
+BytecodeGenerator::AssignmentLhsData BytecodeGenerator::PrepareAssignmentLhs(
+ Expression* lhs, AccumulatorPreservingMode accumulator_preserving_mode) {
// Left-hand side can only be a property, a global or a variable slot.
- Property* property = expr->target()->AsProperty();
- LhsKind assign_type = Property::GetAssignType(property);
+ Property* property = lhs->AsProperty();
+ AssignType assign_type = Property::GetAssignType(property);
// Evaluate LHS expression.
switch (assign_type) {
- case VARIABLE:
- // Nothing to do to evaluate variable assignment LHS.
- break;
+ case NON_PROPERTY:
+ return AssignmentLhsData::NonProperty(lhs);
case NAMED_PROPERTY: {
- object = VisitForRegisterValue(property->obj());
- name = property->key()->AsLiteral()->AsRawPropertyName();
- break;
+ AccumulatorPreservingScope scope(this, accumulator_preserving_mode);
+ Register object = VisitForRegisterValue(property->obj());
+ const AstRawString* name =
+ property->key()->AsLiteral()->AsRawPropertyName();
+ return AssignmentLhsData::NamedProperty(property->obj(), object, name);
}
case KEYED_PROPERTY: {
- object = VisitForRegisterValue(property->obj());
- key = VisitForRegisterValue(property->key());
- break;
+ AccumulatorPreservingScope scope(this, accumulator_preserving_mode);
+ Register object = VisitForRegisterValue(property->obj());
+ Register key = VisitForRegisterValue(property->key());
+ return AssignmentLhsData::KeyedProperty(object, key);
}
case NAMED_SUPER_PROPERTY: {
- super_property_args = register_allocator()->NewRegisterList(4);
+ AccumulatorPreservingScope scope(this, accumulator_preserving_mode);
+ RegisterList super_property_args =
+ register_allocator()->NewRegisterList(4);
SuperPropertyReference* super_property =
property->obj()->AsSuperPropertyReference();
VisitForRegisterValue(super_property->this_var(), super_property_args[0]);
@@ -2933,81 +3112,514 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
builder()
->LoadLiteral(property->key()->AsLiteral()->AsRawPropertyName())
.StoreAccumulatorInRegister(super_property_args[2]);
- break;
+ return AssignmentLhsData::NamedSuperProperty(super_property_args);
}
case KEYED_SUPER_PROPERTY: {
- super_property_args = register_allocator()->NewRegisterList(4);
+ AccumulatorPreservingScope scope(this, accumulator_preserving_mode);
+ RegisterList super_property_args =
+ register_allocator()->NewRegisterList(4);
SuperPropertyReference* super_property =
property->obj()->AsSuperPropertyReference();
VisitForRegisterValue(super_property->this_var(), super_property_args[0]);
VisitForRegisterValue(super_property->home_object(),
super_property_args[1]);
VisitForRegisterValue(property->key(), super_property_args[2]);
- break;
+ return AssignmentLhsData::KeyedSuperProperty(super_property_args);
}
}
+ UNREACHABLE();
+}
- // Evaluate the value and potentially handle compound assignments by loading
- // the left-hand side value and performing a binary operation.
- if (expr->IsCompoundAssignment()) {
- switch (assign_type) {
- case VARIABLE: {
- VariableProxy* proxy = expr->target()->AsVariableProxy();
- BuildVariableLoad(proxy->var(), proxy->hole_check_mode());
- break;
- }
- case NAMED_PROPERTY: {
- BuildLoadNamedProperty(property, object, name);
- break;
- }
- case KEYED_PROPERTY: {
- // Key is already in accumulator at this point due to evaluating the
- // LHS above.
- FeedbackSlot slot = feedback_spec()->AddKeyedLoadICSlot();
- builder()->LoadKeyedProperty(object, feedback_index(slot));
- break;
- }
- case NAMED_SUPER_PROPERTY: {
- builder()->CallRuntime(Runtime::kLoadFromSuper,
- super_property_args.Truncate(3));
- break;
+// Build the iteration finalizer called in the finally block of an iteration
+// protocol execution. This closes the iterator if needed, and suppresses any
+// exception it throws if necessary.
+//
+// In pseudo-code, this builds:
+//
+// if (!done) {
+// let method = iterator.return
+// if (method !== null && method !== undefined) {
+// if (typeof(method) !== "function") throw TypeError
+// try {
+// let return_val = method.call(iterator)
+// if (!%IsObject(return_val)) throw TypeError
+// } catch (e) {
+// if (iteration_continuation != RETHROW)
+// rethrow e
+// }
+// }
+// }
+//
+// For async iterators, iterator.close() becomes await iterator.close().
+void BytecodeGenerator::BuildFinalizeIteration(
+ IteratorRecord iterator, Register done,
+ Register iteration_continuation_token) {
+ RegisterAllocationScope register_scope(this);
+ BytecodeLabels iterator_is_done(zone());
+
+ // if (!done) {
+ builder()->LoadAccumulatorWithRegister(done).JumpIfTrue(
+ ToBooleanMode::kConvertToBoolean, iterator_is_done.New());
+
+ // method = iterator.return
+ // if (method !== null && method !== undefined) {
+ Register method = register_allocator()->NewRegister();
+ builder()
+ ->LoadNamedProperty(iterator.object(),
+ ast_string_constants()->return_string(),
+ feedback_index(feedback_spec()->AddLoadICSlot()))
+ .StoreAccumulatorInRegister(method)
+ .JumpIfUndefined(iterator_is_done.New())
+ .JumpIfNull(iterator_is_done.New());
+
+ // if (typeof(method) !== "function") throw TypeError
+ BytecodeLabel if_callable;
+ builder()
+ ->CompareTypeOf(TestTypeOfFlags::LiteralFlag::kFunction)
+ .JumpIfTrue(ToBooleanMode::kAlreadyBoolean, &if_callable);
+ {
+ // throw %NewTypeError(kReturnMethodNotCallable)
+ RegisterAllocationScope register_scope(this);
+ RegisterList new_type_error_args = register_allocator()->NewRegisterList(2);
+ builder()
+ ->LoadLiteral(Smi::FromEnum(MessageTemplate::kReturnMethodNotCallable))
+ .StoreAccumulatorInRegister(new_type_error_args[0])
+ .LoadLiteral(ast_string_constants()->empty_string())
+ .StoreAccumulatorInRegister(new_type_error_args[1])
+ .CallRuntime(Runtime::kNewTypeError, new_type_error_args)
+ .Throw();
+ }
+ builder()->Bind(&if_callable);
+
+ {
+ RegisterAllocationScope register_scope(this);
+ BuildTryCatch(
+ // try {
+ // let return_val = method.call(iterator)
+ // if (!%IsObject(return_val)) throw TypeError
+ // }
+ [&]() {
+ RegisterList args(iterator.object());
+ builder()->CallProperty(
+ method, args, feedback_index(feedback_spec()->AddCallICSlot()));
+ if (iterator.type() == IteratorType::kAsync) {
+ BuildAwait();
+ }
+ builder()->JumpIfJSReceiver(iterator_is_done.New());
+ {
+ // Throw this exception inside the try block so that it is
+ // suppressed by the iteration continuation if necessary.
+ RegisterAllocationScope register_scope(this);
+ Register return_result = register_allocator()->NewRegister();
+ builder()
+ ->StoreAccumulatorInRegister(return_result)
+ .CallRuntime(Runtime::kThrowIteratorResultNotAnObject,
+ return_result);
+ }
+ },
+
+ // catch (e) {
+ // if (iteration_continuation != RETHROW)
+ // rethrow e
+ // }
+ [&](Register context) {
+ // Reuse context register to store the exception.
+ Register close_exception = context;
+ builder()->StoreAccumulatorInRegister(close_exception);
+
+ BytecodeLabel suppress_close_exception;
+ builder()
+ ->LoadLiteral(
+ Smi::FromInt(ControlScope::DeferredCommands::kRethrowToken))
+ .CompareReference(iteration_continuation_token)
+ .JumpIfTrue(ToBooleanMode::kAlreadyBoolean,
+ &suppress_close_exception)
+ .LoadAccumulatorWithRegister(close_exception)
+ .ReThrow()
+ .Bind(&suppress_close_exception);
+ },
+ HandlerTable::UNCAUGHT);
+ }
+
+ iterator_is_done.Bind(builder());
+}
+
+// Get the default value of a destructuring target. Will mutate the
+// destructuring target expression if there is a default value.
+//
+// For
+// a = b
+// in
+// let {a = b} = c
+// returns b and mutates the input into a.
+Expression* BytecodeGenerator::GetDestructuringDefaultValue(
+ Expression** target) {
+ Expression* default_value = nullptr;
+ if ((*target)->IsAssignment()) {
+ Assignment* default_init = (*target)->AsAssignment();
+ DCHECK_EQ(default_init->op(), Token::ASSIGN);
+ default_value = default_init->value();
+ *target = default_init->target();
+ DCHECK((*target)->IsValidReferenceExpression() || (*target)->IsPattern());
+ }
+ return default_value;
+}
+
+// Convert a destructuring assignment to an array literal into a sequence of
+// iterator accesses into the value being assigned (in the accumulator).
+//
+// [a().x, ...b] = accumulator
+//
+// becomes
+//
+// iterator = %GetIterator(accumulator)
+// try {
+//
+// // Individual assignments read off the value from iterator.next() This gets
+// // repeated per destructuring element.
+// if (!done) {
+// // Make sure we are considered 'done' if .next(), .done or .value fail.
+// done = true
+// var next_result = iterator.next()
+// var tmp_done = next_result.done
+// if (!tmp_done) {
+// value = next_result.value
+// done = false
+// }
+// }
+// if (done)
+// value = undefined
+// a().x = value
+//
+// // A spread receives the remaining items in the iterator.
+// var array = []
+// var index = 0
+// %FillArrayWithIterator(iterator, array, index, done)
+// done = true
+// b = array
+//
+// } catch(e) {
+// iteration_continuation = RETHROW
+// } finally {
+// %FinalizeIteration(iterator, done, iteration_continuation)
+// }
+void BytecodeGenerator::BuildDestructuringArrayAssignment(
+ ArrayLiteral* pattern, Token::Value op,
+ LookupHoistingMode lookup_hoisting_mode) {
+ RegisterAllocationScope scope(this);
+
+ Register value = register_allocator()->NewRegister();
+ builder()->StoreAccumulatorInRegister(value);
+
+ // Store the iterator in a dedicated register so that it can be closed on
+ // exit, and the 'done' value in a dedicated register so that it can be
+ // changed and accessed independently of the iteration result.
+ IteratorRecord iterator = BuildGetIteratorRecord(IteratorType::kNormal);
+ Register done = register_allocator()->NewRegister();
+ builder()->LoadFalse();
+ builder()->StoreAccumulatorInRegister(done);
+
+ BuildTryFinally(
+ // Try block.
+ [&]() {
+ Register next_result = register_allocator()->NewRegister();
+ FeedbackSlot next_value_load_slot = feedback_spec()->AddLoadICSlot();
+ FeedbackSlot next_done_load_slot = feedback_spec()->AddLoadICSlot();
+
+ Spread* spread = nullptr;
+ for (Expression* target : *pattern->values()) {
+ if (target->IsSpread()) {
+ spread = target->AsSpread();
+ break;
+ }
+
+ Expression* default_value = GetDestructuringDefaultValue(&target);
+ if (!target->IsPattern()) {
+ builder()->SetExpressionAsStatementPosition(target);
+ }
+
+ AssignmentLhsData lhs_data = PrepareAssignmentLhs(target);
+
+ // if (!done) {
+ // // Make sure we are considered done if .next(), .done or .value
+ // // fail.
+ // done = true
+ // var next_result = iterator.next()
+ // var tmp_done = next_result.done
+ // if (!tmp_done) {
+ // value = next_result.value
+ // done = false
+ // }
+ // }
+ // if (done)
+ // value = undefined
+ BytecodeLabels is_done(zone());
+
+ builder()->LoadAccumulatorWithRegister(done);
+ builder()->JumpIfTrue(ToBooleanMode::kConvertToBoolean,
+ is_done.New());
+
+ builder()->LoadTrue().StoreAccumulatorInRegister(done);
+ BuildIteratorNext(iterator, next_result);
+ builder()
+ ->LoadNamedProperty(next_result,
+ ast_string_constants()->done_string(),
+ feedback_index(next_done_load_slot))
+ .JumpIfTrue(ToBooleanMode::kConvertToBoolean, is_done.New())
+ .LoadNamedProperty(next_result,
+ ast_string_constants()->value_string(),
+ feedback_index(next_value_load_slot))
+ .StoreAccumulatorInRegister(next_result)
+ .LoadFalse()
+ .StoreAccumulatorInRegister(done)
+ .LoadAccumulatorWithRegister(next_result);
+
+ // Only do the assignment if this is not a hole (i.e. 'elided').
+ if (!target->IsTheHoleLiteral()) {
+ // [<pattern> = <init>] = <value>
+ // becomes (roughly)
+ // temp = <value>.next();
+ // <pattern> = temp === undefined ? <init> : temp;
+ BytecodeLabel do_assignment;
+ if (default_value) {
+ builder()->JumpIfNotUndefined(&do_assignment);
+ // Since done == true => temp == undefined, jump directly to using
+ // the default value for that case.
+ is_done.Bind(builder());
+ VisitForAccumulatorValue(default_value);
+ } else {
+ builder()->Jump(&do_assignment);
+ is_done.Bind(builder());
+ builder()->LoadUndefined();
+ }
+ builder()->Bind(&do_assignment);
+
+ BuildAssignment(lhs_data, op, lookup_hoisting_mode);
+ } else {
+ DCHECK_EQ(lhs_data.assign_type(), NON_PROPERTY);
+ is_done.Bind(builder());
+ }
+ }
+
+ if (spread) {
+ RegisterAllocationScope scope(this);
+
+ // A spread is turned into a loop over the remainer of the iterator.
+ Expression* target = spread->expression();
+
+ if (!target->IsPattern()) {
+ builder()->SetExpressionAsStatementPosition(spread);
+ }
+
+ AssignmentLhsData lhs_data = PrepareAssignmentLhs(target);
+
+ // var array = [];
+ Register array = register_allocator()->NewRegister();
+ builder()->CreateEmptyArrayLiteral(
+ feedback_index(feedback_spec()->AddLiteralSlot()));
+ builder()->StoreAccumulatorInRegister(array);
+
+ // var index = 0;
+ Register index = register_allocator()->NewRegister();
+ builder()->LoadLiteral(Smi::zero());
+ builder()->StoreAccumulatorInRegister(index);
+
+ // Set done to true, since it's guaranteed to be true by the time the
+ // array fill completes.
+ builder()->LoadTrue().StoreAccumulatorInRegister(done);
+
+ // Fill the array with the iterator.
+ FeedbackSlot element_slot =
+ feedback_spec()->AddStoreInArrayLiteralICSlot();
+ FeedbackSlot index_slot = feedback_spec()->AddBinaryOpICSlot();
+ BuildFillArrayWithIterator(iterator, array, index, next_result,
+ next_value_load_slot, next_done_load_slot,
+ index_slot, element_slot);
+
+ // Assign the array to the LHS.
+ builder()->LoadAccumulatorWithRegister(array);
+ BuildAssignment(lhs_data, op, lookup_hoisting_mode);
+ }
+ },
+ // Finally block.
+ [&](Register iteration_continuation_token) {
+ // Finish the iteration in the finally block.
+ BuildFinalizeIteration(iterator, done, iteration_continuation_token);
+ },
+ HandlerTable::UNCAUGHT);
+
+ if (!execution_result()->IsEffect()) {
+ builder()->LoadAccumulatorWithRegister(value);
+ }
+}
+
+// Convert a destructuring assignment to an object literal into a sequence of
+// property accesses into the value being assigned (in the accumulator).
+//
+// { y, [x++]: a(), ...b.c } = value
+//
+// becomes
+//
+// var rest_runtime_callargs = new Array(3);
+// rest_runtime_callargs[0] = value;
+//
+// rest_runtime_callargs[1] = value;
+// y = value.y;
+//
+// var temp1 = %ToName(x++);
+// rest_runtime_callargs[2] = temp1;
+// a() = value[temp1];
+//
+// b.c = %CopyDataPropertiesWithExcludedProperties.call(rest_runtime_callargs);
+void BytecodeGenerator::BuildDestructuringObjectAssignment(
+ ObjectLiteral* pattern, Token::Value op,
+ LookupHoistingMode lookup_hoisting_mode) {
+ RegisterAllocationScope scope(this);
+
+ // if (value === null || value === undefined)
+ // throw new TypeError(kNonCoercible);
+ //
+ // TODO(leszeks): Eliminate check if value is known to be non-null (e.g.
+ // an object literal).
+ BytecodeLabel is_null_or_undefined, not_null_or_undefined;
+ builder()
+ ->JumpIfNull(&is_null_or_undefined)
+ .JumpIfNotUndefined(&not_null_or_undefined);
+
+ {
+ builder()->Bind(&is_null_or_undefined);
+ builder()->SetExpressionPosition(pattern);
+ builder()->CallRuntime(Runtime::kThrowPatternAssignmentNonCoercible);
+ }
+
+ // Store the assignment value in a register.
+ Register value;
+ RegisterList rest_runtime_callargs;
+ if (pattern->has_rest_property()) {
+ rest_runtime_callargs =
+ register_allocator()->NewRegisterList(pattern->properties()->length());
+ value = rest_runtime_callargs[0];
+ } else {
+ value = register_allocator()->NewRegister();
+ }
+ builder()->Bind(&not_null_or_undefined).StoreAccumulatorInRegister(value);
+
+ int i = 0;
+ for (ObjectLiteralProperty* pattern_property : *pattern->properties()) {
+ RegisterAllocationScope scope(this);
+
+ // The key of the pattern becomes the key into the RHS value, and the value
+ // of the pattern becomes the target of the assignment.
+ //
+ // e.g. { a: b } = o becomes b = o.a
+ Expression* pattern_key = pattern_property->key();
+ Expression* target = pattern_property->value();
+ Expression* default_value = GetDestructuringDefaultValue(&target);
+
+ if (!target->IsPattern()) {
+ builder()->SetExpressionAsStatementPosition(target);
+ }
+
+ // Calculate this property's key into the assignment RHS value, additionally
+ // storing the key for rest_runtime_callargs if needed.
+ //
+ // The RHS is accessed using the key either by LoadNamedProperty (if
+ // value_name is valid) or by LoadKeyedProperty (otherwise).
+ const AstRawString* value_name = nullptr;
+ Register value_key;
+
+ if (pattern_property->kind() != ObjectLiteralProperty::Kind::SPREAD) {
+ if (pattern_key->IsPropertyName()) {
+ value_name = pattern_key->AsLiteral()->AsRawPropertyName();
}
- case KEYED_SUPER_PROPERTY: {
- builder()->CallRuntime(Runtime::kLoadKeyedFromSuper,
- super_property_args.Truncate(3));
- break;
+ if (pattern->has_rest_property() || !value_name) {
+ if (pattern->has_rest_property()) {
+ value_key = rest_runtime_callargs[i + 1];
+ } else {
+ value_key = register_allocator()->NewRegister();
+ }
+ if (pattern_property->is_computed_name()) {
+ // { [a()]: b().x } = c
+ // becomes
+ // var tmp = a()
+ // b().x = c[tmp]
+ DCHECK(!pattern_key->IsPropertyName() ||
+ !pattern_key->IsNumberLiteral());
+ VisitForAccumulatorValue(pattern_key);
+ builder()->ToName(value_key);
+ } else {
+ // We only need the key for non-computed properties when it is numeric
+ // or is being saved for the rest_runtime_callargs.
+ DCHECK(
+ pattern_key->IsNumberLiteral() ||
+ (pattern->has_rest_property() && pattern_key->IsPropertyName()));
+ VisitForRegisterValue(pattern_key, value_key);
+ }
}
}
- BinaryOperation* binop = expr->AsCompoundAssignment()->binary_operation();
- FeedbackSlot slot = feedback_spec()->AddBinaryOpICSlot();
- if (expr->value()->IsSmiLiteral()) {
- builder()->BinaryOperationSmiLiteral(
- binop->op(), expr->value()->AsLiteral()->AsSmiLiteral(),
- feedback_index(slot));
+
+ AssignmentLhsData lhs_data = PrepareAssignmentLhs(target);
+
+ // Get the value from the RHS.
+ if (pattern_property->kind() == ObjectLiteralProperty::Kind::SPREAD) {
+ DCHECK_EQ(i, pattern->properties()->length() - 1);
+ DCHECK(!value_key.is_valid());
+ DCHECK_NULL(value_name);
+ builder()->CallRuntime(Runtime::kCopyDataPropertiesWithExcludedProperties,
+ rest_runtime_callargs);
+ } else if (value_name) {
+ builder()->LoadNamedProperty(
+ value, value_name, feedback_index(feedback_spec()->AddLoadICSlot()));
} else {
- Register old_value = register_allocator()->NewRegister();
- builder()->StoreAccumulatorInRegister(old_value);
- VisitForAccumulatorValue(expr->value());
- builder()->BinaryOperation(binop->op(), old_value, feedback_index(slot));
+ DCHECK(value_key.is_valid());
+ builder()->LoadAccumulatorWithRegister(value_key).LoadKeyedProperty(
+ value, feedback_index(feedback_spec()->AddKeyedLoadICSlot()));
}
- } else {
- VisitForAccumulatorValue(expr->value());
+
+ // {<pattern> = <init>} = <value>
+ // becomes
+ // temp = <value>;
+ // <pattern> = temp === undefined ? <init> : temp;
+ if (default_value) {
+ BytecodeLabel value_not_undefined;
+ builder()->JumpIfNotUndefined(&value_not_undefined);
+ VisitForAccumulatorValue(default_value);
+ builder()->Bind(&value_not_undefined);
+ }
+
+ BuildAssignment(lhs_data, op, lookup_hoisting_mode);
+
+ i++;
}
- // Store the value.
- builder()->SetExpressionPosition(expr);
- switch (assign_type) {
- case VARIABLE: {
- // TODO(oth): The BuildVariableAssignment() call is hard to reason about.
- // Is the value in the accumulator safe? Yes, but scary.
- VariableProxy* proxy = expr->target()->AsVariableProxy();
- BuildVariableAssignment(proxy->var(), expr->op(),
- proxy->hole_check_mode(),
- expr->lookup_hoisting_mode());
+ if (!execution_result()->IsEffect()) {
+ builder()->LoadAccumulatorWithRegister(value);
+ }
+}
+
+void BytecodeGenerator::BuildAssignment(
+ const AssignmentLhsData& lhs_data, Token::Value op,
+ LookupHoistingMode lookup_hoisting_mode) {
+ // Assign the value to the LHS.
+ switch (lhs_data.assign_type()) {
+ case NON_PROPERTY: {
+ if (ObjectLiteral* pattern = lhs_data.expr()->AsObjectLiteral()) {
+ // Split object literals into destructuring.
+ BuildDestructuringObjectAssignment(pattern, op, lookup_hoisting_mode);
+ } else if (ArrayLiteral* pattern = lhs_data.expr()->AsArrayLiteral()) {
+ // Split object literals into destructuring.
+ BuildDestructuringArrayAssignment(pattern, op, lookup_hoisting_mode);
+ } else {
+ DCHECK(lhs_data.expr()->IsVariableProxy());
+ VariableProxy* proxy = lhs_data.expr()->AsVariableProxy();
+ BuildVariableAssignment(proxy->var(), op, proxy->hole_check_mode(),
+ lookup_hoisting_mode);
+ }
break;
}
case NAMED_PROPERTY: {
- BuildStoreNamedProperty(property, object, name);
+ BuildStoreNamedProperty(lhs_data.object_expr(), lhs_data.object(),
+ lhs_data.name());
break;
}
case KEYED_PROPERTY: {
@@ -3017,8 +3629,8 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
value = register_allocator()->NewRegister();
builder()->StoreAccumulatorInRegister(value);
}
- builder()->StoreKeyedProperty(object, key, feedback_index(slot),
- language_mode());
+ builder()->StoreKeyedProperty(lhs_data.object(), lhs_data.key(),
+ feedback_index(slot), language_mode());
if (!execution_result()->IsEffect()) {
builder()->LoadAccumulatorWithRegister(value);
}
@@ -3026,34 +3638,91 @@ void BytecodeGenerator::VisitAssignment(Assignment* expr) {
}
case NAMED_SUPER_PROPERTY: {
builder()
- ->StoreAccumulatorInRegister(super_property_args[3])
- .CallRuntime(StoreToSuperRuntimeId(), super_property_args);
+ ->StoreAccumulatorInRegister(lhs_data.super_property_args()[3])
+ .CallRuntime(StoreToSuperRuntimeId(), lhs_data.super_property_args());
break;
}
case KEYED_SUPER_PROPERTY: {
builder()
- ->StoreAccumulatorInRegister(super_property_args[3])
- .CallRuntime(StoreKeyedToSuperRuntimeId(), super_property_args);
+ ->StoreAccumulatorInRegister(lhs_data.super_property_args()[3])
+ .CallRuntime(StoreKeyedToSuperRuntimeId(),
+ lhs_data.super_property_args());
break;
}
}
}
+void BytecodeGenerator::VisitAssignment(Assignment* expr) {
+ AssignmentLhsData lhs_data = PrepareAssignmentLhs(expr->target());
+
+ VisitForAccumulatorValue(expr->value());
+
+ builder()->SetExpressionPosition(expr);
+ BuildAssignment(lhs_data, expr->op(), expr->lookup_hoisting_mode());
+}
+
void BytecodeGenerator::VisitCompoundAssignment(CompoundAssignment* expr) {
- VisitAssignment(expr);
+ AssignmentLhsData lhs_data = PrepareAssignmentLhs(expr->target());
+
+ // Evaluate the value and potentially handle compound assignments by loading
+ // the left-hand side value and performing a binary operation.
+ switch (lhs_data.assign_type()) {
+ case NON_PROPERTY: {
+ VariableProxy* proxy = expr->target()->AsVariableProxy();
+ BuildVariableLoad(proxy->var(), proxy->hole_check_mode());
+ break;
+ }
+ case NAMED_PROPERTY: {
+ BuildLoadNamedProperty(lhs_data.object_expr(), lhs_data.object(),
+ lhs_data.name());
+ break;
+ }
+ case KEYED_PROPERTY: {
+ FeedbackSlot slot = feedback_spec()->AddKeyedLoadICSlot();
+ builder()
+ ->LoadAccumulatorWithRegister(lhs_data.key())
+ .LoadKeyedProperty(lhs_data.object(), feedback_index(slot));
+ break;
+ }
+ case NAMED_SUPER_PROPERTY: {
+ builder()->CallRuntime(Runtime::kLoadFromSuper,
+ lhs_data.super_property_args().Truncate(3));
+ break;
+ }
+ case KEYED_SUPER_PROPERTY: {
+ builder()->CallRuntime(Runtime::kLoadKeyedFromSuper,
+ lhs_data.super_property_args().Truncate(3));
+ break;
+ }
+ }
+ BinaryOperation* binop = expr->AsCompoundAssignment()->binary_operation();
+ FeedbackSlot slot = feedback_spec()->AddBinaryOpICSlot();
+ if (expr->value()->IsSmiLiteral()) {
+ builder()->BinaryOperationSmiLiteral(
+ binop->op(), expr->value()->AsLiteral()->AsSmiLiteral(),
+ feedback_index(slot));
+ } else {
+ Register old_value = register_allocator()->NewRegister();
+ builder()->StoreAccumulatorInRegister(old_value);
+ VisitForAccumulatorValue(expr->value());
+ builder()->BinaryOperation(binop->op(), old_value, feedback_index(slot));
+ }
+
+ builder()->SetExpressionPosition(expr);
+ BuildAssignment(lhs_data, expr->op(), expr->lookup_hoisting_mode());
}
// Suspends the generator to resume at the next suspend_id, with output stored
// in the accumulator. When the generator is resumed, the sent value is loaded
// in the accumulator.
-void BytecodeGenerator::BuildSuspendPoint(Expression* suspend_expr) {
+void BytecodeGenerator::BuildSuspendPoint(int position) {
const int suspend_id = suspend_count_++;
RegisterList registers = register_allocator()->AllLiveRegisters();
// Save context, registers, and state. This bytecode then returns the value
// in the accumulator.
- builder()->SetExpressionPosition(suspend_expr);
+ builder()->SetExpressionPosition(position);
builder()->SuspendGenerator(generator_object(), registers, suspend_id);
// Upon resume, we continue here.
@@ -3090,12 +3759,12 @@ void BytecodeGenerator::VisitYield(Yield* expr) {
builder()
->StoreAccumulatorInRegister(args[0]) // value
.LoadFalse()
- .StoreAccumulatorInRegister(args[1]) // done
+ .StoreAccumulatorInRegister(args[1]) // done
.CallRuntime(Runtime::kInlineCreateIterResultObject, args);
}
}
- BuildSuspendPoint(expr);
+ BuildSuspendPoint(expr->position());
// At this point, the generator has been resumed, with the received value in
// the accumulator.
@@ -3218,8 +3887,8 @@ void BytecodeGenerator::VisitYieldStar(YieldStar* expr) {
{
RegisterAllocationScope register_scope(this);
RegisterList iterator_and_input = register_allocator()->NewRegisterList(2);
+ VisitForAccumulatorValue(expr->expression());
IteratorRecord iterator = BuildGetIteratorRecord(
- expr->expression(),
register_allocator()->NewRegister() /* next method */,
iterator_and_input[0], iterator_type);
@@ -3306,7 +3975,7 @@ void BytecodeGenerator::VisitYieldStar(YieldStar* expr) {
if (iterator_type == IteratorType::kAsync) {
// Await the result of the method invocation.
- BuildAwait(expr);
+ BuildAwait(expr->position());
}
// Check that output is an object.
@@ -3346,7 +4015,7 @@ void BytecodeGenerator::VisitYieldStar(YieldStar* expr) {
.CallRuntime(Runtime::kInlineAsyncGeneratorYield, args);
}
- BuildSuspendPoint(expr);
+ BuildSuspendPoint(expr->position());
builder()->StoreAccumulatorInRegister(input);
builder()
->CallRuntime(Runtime::kInlineGeneratorGetResumeMode,
@@ -3382,7 +4051,7 @@ void BytecodeGenerator::VisitYieldStar(YieldStar* expr) {
builder()->LoadAccumulatorWithRegister(output_value);
}
-void BytecodeGenerator::BuildAwait(Expression* await_expr) {
+void BytecodeGenerator::BuildAwait(int position) {
// Rather than HandlerTable::UNCAUGHT, async functions use
// HandlerTable::ASYNC_AWAIT to communicate that top-level exceptions are
// transformed into promise rejections. This is necessary to prevent emitting
@@ -3395,38 +4064,24 @@ void BytecodeGenerator::BuildAwait(Expression* await_expr) {
// Await(operand) and suspend.
RegisterAllocationScope register_scope(this);
- int await_builtin_context_index;
- RegisterList args;
+ Runtime::FunctionId await_intrinsic_id;
if (IsAsyncGeneratorFunction(function_kind())) {
- await_builtin_context_index =
- catch_prediction() == HandlerTable::ASYNC_AWAIT
- ? Context::ASYNC_GENERATOR_AWAIT_UNCAUGHT
- : Context::ASYNC_GENERATOR_AWAIT_CAUGHT;
- args = register_allocator()->NewRegisterList(2);
- builder()
- ->MoveRegister(generator_object(), args[0])
- .StoreAccumulatorInRegister(args[1]);
+ await_intrinsic_id = catch_prediction() == HandlerTable::ASYNC_AWAIT
+ ? Runtime::kInlineAsyncGeneratorAwaitUncaught
+ : Runtime::kInlineAsyncGeneratorAwaitCaught;
} else {
- await_builtin_context_index =
- catch_prediction() == HandlerTable::ASYNC_AWAIT
- ? Context::ASYNC_FUNCTION_AWAIT_UNCAUGHT_INDEX
- : Context::ASYNC_FUNCTION_AWAIT_CAUGHT_INDEX;
- args = register_allocator()->NewRegisterList(3);
- builder()
- ->MoveRegister(generator_object(), args[0])
- .StoreAccumulatorInRegister(args[1]);
-
- // AsyncFunction Await builtins require a 3rd parameter to hold the outer
- // promise.
- Variable* var_promise = closure_scope()->promise_var();
- BuildVariableLoadForAccumulatorValue(var_promise, HoleCheckMode::kElided);
- builder()->StoreAccumulatorInRegister(args[2]);
+ await_intrinsic_id = catch_prediction() == HandlerTable::ASYNC_AWAIT
+ ? Runtime::kInlineAsyncFunctionAwaitUncaught
+ : Runtime::kInlineAsyncFunctionAwaitCaught;
}
-
- builder()->CallJSRuntime(await_builtin_context_index, args);
+ RegisterList args = register_allocator()->NewRegisterList(2);
+ builder()
+ ->MoveRegister(generator_object(), args[0])
+ .StoreAccumulatorInRegister(args[1])
+ .CallRuntime(await_intrinsic_id, args);
}
- BuildSuspendPoint(await_expr);
+ BuildSuspendPoint(position);
Register input = register_allocator()->NewRegister();
Register resume_mode = register_allocator()->NewRegister();
@@ -3454,7 +4109,7 @@ void BytecodeGenerator::BuildAwait(Expression* await_expr) {
void BytecodeGenerator::VisitAwait(Await* expr) {
builder()->SetExpressionPosition(expr);
VisitForAccumulatorValue(expr->expression());
- BuildAwait(expr);
+ BuildAwait(expr->position());
BuildIncrementBlockCoverageCounterIfEnabled(expr,
SourceRangeKind::kContinuation);
}
@@ -3467,15 +4122,15 @@ void BytecodeGenerator::VisitThrow(Throw* expr) {
}
void BytecodeGenerator::VisitPropertyLoad(Register obj, Property* property) {
- LhsKind property_kind = Property::GetAssignType(property);
+ AssignType property_kind = Property::GetAssignType(property);
switch (property_kind) {
- case VARIABLE:
+ case NON_PROPERTY:
UNREACHABLE();
case NAMED_PROPERTY: {
builder()->SetExpressionPosition(property);
const AstRawString* name =
property->key()->AsLiteral()->AsRawPropertyName();
- BuildLoadNamedProperty(property, obj, name);
+ BuildLoadNamedProperty(property->obj(), obj, name);
break;
}
case KEYED_PROPERTY: {
@@ -3541,7 +4196,7 @@ void BytecodeGenerator::VisitKeyedSuperPropertyLoad(Property* property,
}
void BytecodeGenerator::VisitProperty(Property* expr) {
- LhsKind property_kind = Property::GetAssignType(expr);
+ AssignType property_kind = Property::GetAssignType(expr);
if (property_kind != NAMED_SUPER_PROPERTY &&
property_kind != KEYED_SUPER_PROPERTY) {
Register obj = VisitForRegisterValue(expr->obj());
@@ -3556,7 +4211,7 @@ void BytecodeGenerator::VisitResolvedProperty(ResolvedProperty* expr) {
UNREACHABLE();
}
-void BytecodeGenerator::VisitArguments(ZonePtrList<Expression>* args,
+void BytecodeGenerator::VisitArguments(const ZonePtrList<Expression>* args,
RegisterList* arg_regs) {
// Visit arguments.
for (int i = 0; i < static_cast<int>(args->length()); i++) {
@@ -3733,7 +4388,7 @@ void BytecodeGenerator::VisitCall(Call* expr) {
void BytecodeGenerator::VisitCallSuper(Call* expr) {
RegisterAllocationScope register_scope(this);
SuperCallReference* super = expr->expression()->AsSuperCallReference();
- ZonePtrList<Expression>* args = expr->arguments();
+ const ZonePtrList<Expression>* args = expr->arguments();
int first_spread_index = 0;
for (; first_spread_index < args->length(); first_spread_index++) {
@@ -3810,11 +4465,11 @@ void BytecodeGenerator::VisitCallSuper(Call* expr) {
// TODO(gsathya): In the future, we could tag nested arrow functions
// or eval with the correct bit so that we do the load conditionally
// if required.
- if (info()->literal()->requires_instance_fields_initializer() ||
+ if (info()->literal()->requires_instance_members_initializer() ||
!IsDerivedConstructor(info()->literal()->kind())) {
Register instance = register_allocator()->NewRegister();
builder()->StoreAccumulatorInRegister(instance);
- BuildInstanceFieldInitialization(this_function, instance);
+ BuildInstanceMemberInitialization(this_function, instance);
builder()->LoadAccumulatorWithRegister(instance);
}
}
@@ -3918,56 +4573,51 @@ void BytecodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
}
}
-void BytecodeGenerator::VisitDelete(UnaryOperation* expr) {
- if (expr->expression()->IsProperty()) {
+void BytecodeGenerator::VisitDelete(UnaryOperation* unary) {
+ Expression* expr = unary->expression();
+ if (expr->IsProperty()) {
// Delete of an object property is allowed both in sloppy
// and strict modes.
- Property* property = expr->expression()->AsProperty();
+ Property* property = expr->AsProperty();
Register object = VisitForRegisterValue(property->obj());
VisitForAccumulatorValue(property->key());
builder()->Delete(object, language_mode());
- } else if (expr->expression()->IsVariableProxy()) {
+ } else if (expr->IsVariableProxy() && !expr->AsVariableProxy()->is_this() &&
+ !expr->AsVariableProxy()->is_new_target()) {
// Delete of an unqualified identifier is allowed in sloppy mode but is
- // not allowed in strict mode. Deleting 'this' and 'new.target' is allowed
- // in both modes.
- VariableProxy* proxy = expr->expression()->AsVariableProxy();
- DCHECK(is_sloppy(language_mode()) || proxy->is_this() ||
- proxy->is_new_target());
- if (proxy->is_this() || proxy->is_new_target()) {
- builder()->LoadTrue();
- } else {
- Variable* variable = proxy->var();
- switch (variable->location()) {
- case VariableLocation::PARAMETER:
- case VariableLocation::LOCAL:
- case VariableLocation::CONTEXT: {
- // Deleting local var/let/const, context variables, and arguments
- // does not have any effect.
- builder()->LoadFalse();
- break;
- }
- case VariableLocation::UNALLOCATED:
- // TODO(adamk): Falling through to the runtime results in correct
- // behavior, but does unnecessary context-walking (since scope
- // analysis has already proven that the variable doesn't exist in
- // any non-global scope). Consider adding a DeleteGlobal bytecode
- // that knows how to deal with ScriptContexts as well as global
- // object properties.
- case VariableLocation::LOOKUP: {
- Register name_reg = register_allocator()->NewRegister();
- builder()
- ->LoadLiteral(variable->raw_name())
- .StoreAccumulatorInRegister(name_reg)
- .CallRuntime(Runtime::kDeleteLookupSlot, name_reg);
- break;
- }
- default:
- UNREACHABLE();
+ // not allowed in strict mode.
+ DCHECK(is_sloppy(language_mode()));
+ Variable* variable = expr->AsVariableProxy()->var();
+ switch (variable->location()) {
+ case VariableLocation::PARAMETER:
+ case VariableLocation::LOCAL:
+ case VariableLocation::CONTEXT: {
+ // Deleting local var/let/const, context variables, and arguments
+ // does not have any effect.
+ builder()->LoadFalse();
+ break;
}
+ case VariableLocation::UNALLOCATED:
+ // TODO(adamk): Falling through to the runtime results in correct
+ // behavior, but does unnecessary context-walking (since scope
+ // analysis has already proven that the variable doesn't exist in
+ // any non-global scope). Consider adding a DeleteGlobal bytecode
+ // that knows how to deal with ScriptContexts as well as global
+ // object properties.
+ case VariableLocation::LOOKUP: {
+ Register name_reg = register_allocator()->NewRegister();
+ builder()
+ ->LoadLiteral(variable->raw_name())
+ .StoreAccumulatorInRegister(name_reg)
+ .CallRuntime(Runtime::kDeleteLookupSlot, name_reg);
+ break;
+ }
+ default:
+ UNREACHABLE();
}
} else {
- // Delete of an unresolvable reference returns true.
- VisitForEffect(expr->expression());
+ // Delete of an unresolvable reference, new.target, and this returns true.
+ VisitForEffect(expr);
builder()->LoadTrue();
}
}
@@ -3977,7 +4627,7 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
// Left-hand side can only be a property, a global or a variable slot.
Property* property = expr->expression()->AsProperty();
- LhsKind assign_type = Property::GetAssignType(property);
+ AssignType assign_type = Property::GetAssignType(property);
bool is_postfix = expr->is_postfix() && !execution_result()->IsEffect();
@@ -3986,7 +4636,7 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
RegisterList super_property_args;
const AstRawString* name;
switch (assign_type) {
- case VARIABLE: {
+ case NON_PROPERTY: {
VariableProxy* proxy = expr->expression()->AsVariableProxy();
BuildVariableLoadForAccumulatorValue(proxy->var(),
proxy->hole_check_mode());
@@ -4054,7 +4704,7 @@ void BytecodeGenerator::VisitCountOperation(CountOperation* expr) {
// Store the value.
builder()->SetExpressionPosition(expr);
switch (assign_type) {
- case VARIABLE: {
+ case NON_PROPERTY: {
VariableProxy* proxy = expr->expression()->AsVariableProxy();
BuildVariableAssignment(proxy->var(), expr->op(),
proxy->hole_check_mode());
@@ -4208,7 +4858,7 @@ void BytecodeGenerator::VisitCompareOperation(CompareOperation* expr) {
void BytecodeGenerator::VisitArithmeticExpression(BinaryOperation* expr) {
FeedbackSlot slot = feedback_spec()->AddBinaryOpICSlot();
Expression* subexpr;
- Smi* literal;
+ Smi literal;
if (expr->IsSmiLiteralOperation(&subexpr, &literal)) {
TypeHint type_hint = VisitForAccumulatorValue(subexpr);
builder()->SetExpressionPosition(expr);
@@ -4277,14 +4927,11 @@ void BytecodeGenerator::VisitImportCallExpression(ImportCallExpression* expr) {
.CallRuntime(Runtime::kDynamicImportCall, args);
}
-void BytecodeGenerator::BuildGetIterator(Expression* iterable,
- IteratorType hint) {
+void BytecodeGenerator::BuildGetIterator(IteratorType hint) {
RegisterList args = register_allocator()->NewRegisterList(1);
Register method = register_allocator()->NewRegister();
Register obj = args[0];
- VisitForAccumulatorValue(iterable);
-
if (hint == IteratorType::kAsync) {
// Set method to GetMethod(obj, @@asyncIterator)
builder()->StoreAccumulatorInRegister(obj).LoadAsyncIteratorProperty(
@@ -4346,9 +4993,9 @@ void BytecodeGenerator::BuildGetIterator(Expression* iterable,
// Returns an IteratorRecord which is valid for the lifetime of the current
// register_allocation_scope.
BytecodeGenerator::IteratorRecord BytecodeGenerator::BuildGetIteratorRecord(
- Expression* iterable, Register next, Register object, IteratorType hint) {
+ Register next, Register object, IteratorType hint) {
DCHECK(next.is_valid() && object.is_valid());
- BuildGetIterator(iterable, hint);
+ BuildGetIterator(hint);
builder()
->StoreAccumulatorInRegister(object)
@@ -4359,10 +5006,10 @@ BytecodeGenerator::IteratorRecord BytecodeGenerator::BuildGetIteratorRecord(
}
BytecodeGenerator::IteratorRecord BytecodeGenerator::BuildGetIteratorRecord(
- Expression* iterable, IteratorType hint) {
+ IteratorType hint) {
Register next = register_allocator()->NewRegister();
Register object = register_allocator()->NewRegister();
- return BuildGetIteratorRecord(iterable, next, object, hint);
+ return BuildGetIteratorRecord(next, object, hint);
}
void BytecodeGenerator::BuildIteratorNext(const IteratorRecord& iterator,
@@ -4371,7 +5018,9 @@ void BytecodeGenerator::BuildIteratorNext(const IteratorRecord& iterator,
builder()->CallProperty(iterator.next(), RegisterList(iterator.object()),
feedback_index(feedback_spec()->AddCallICSlot()));
- // TODO(caitp): support async IteratorNext here.
+ if (iterator.type() == IteratorType::kAsync) {
+ BuildAwait();
+ }
BytecodeLabel is_object;
builder()
@@ -4413,7 +5062,7 @@ void BytecodeGenerator::BuildIteratorClose(const IteratorRecord& iterator,
if (iterator.type() == IteratorType::kAsync) {
DCHECK_NOT_NULL(expr);
- BuildAwait(expr);
+ BuildAwait(expr->position());
}
builder()->JumpIfJSReceiver(done.New());
@@ -4428,11 +5077,6 @@ void BytecodeGenerator::BuildIteratorClose(const IteratorRecord& iterator,
done.Bind(builder());
}
-void BytecodeGenerator::VisitGetIterator(GetIterator* expr) {
- builder()->SetExpressionPosition(expr);
- BuildGetIterator(expr->iterable(), expr->hint());
-}
-
void BytecodeGenerator::VisitGetTemplateObject(GetTemplateObject* expr) {
builder()->SetExpressionPosition(expr);
size_t entry = builder()->AllocateDeferredConstantPoolEntry();
@@ -4730,10 +5374,6 @@ void BytecodeGenerator::VisitNaryLogicalAndExpression(NaryOperation* expr) {
}
}
-void BytecodeGenerator::VisitRewritableExpression(RewritableExpression* expr) {
- Visit(expr->expression());
-}
-
void BytecodeGenerator::BuildNewLocalActivationContext() {
ValueResultScope value_execution_result(this);
Scope* scope = closure_scope();
@@ -4888,7 +5528,7 @@ void BytecodeGenerator::VisitNewTargetVariable(Variable* variable) {
// to pass in the generator object. In ordinary calls, new.target is always
// undefined because generator functions are non-constructible, so don't
// assign anything to the new.target variable.
- if (info()->literal()->CanSuspend()) return;
+ if (IsResumableFunction(info()->literal()->kind())) return;
if (variable->location() == VariableLocation::LOCAL) {
// The new.target register was already assigned by entry trampoline.
@@ -4908,10 +5548,15 @@ void BytecodeGenerator::BuildGeneratorObjectVariableInitialization() {
Variable* generator_object_var = closure_scope()->generator_object_var();
RegisterAllocationScope register_scope(this);
RegisterList args = register_allocator()->NewRegisterList(2);
+ Runtime::FunctionId function_id =
+ (IsAsyncFunction(info()->literal()->kind()) &&
+ !IsAsyncGeneratorFunction(info()->literal()->kind()))
+ ? Runtime::kInlineAsyncFunctionEnter
+ : Runtime::kInlineCreateJSGeneratorObject;
builder()
->MoveRegister(Register::function_closure(), args[0])
.MoveRegister(builder()->Receiver(), args[1])
- .CallRuntime(Runtime::kInlineCreateJSGeneratorObject, args)
+ .CallRuntime(function_id, args)
.StoreAccumulatorInRegister(generator_object());
if (generator_object_var->location() == VariableLocation::LOCAL) {
@@ -4934,7 +5579,9 @@ void BytecodeGenerator::BuildPushUndefinedIntoRegisterList(
void BytecodeGenerator::BuildLoadPropertyKey(LiteralProperty* property,
Register out_reg) {
if (property->key()->IsStringLiteral()) {
- VisitForRegisterValue(property->key(), out_reg);
+ builder()
+ ->LoadLiteral(property->key()->AsLiteral()->AsRawString())
+ .StoreAccumulatorInRegister(out_reg);
} else {
VisitForAccumulatorValue(property->key());
builder()->ToName(out_reg);
@@ -5107,7 +5754,7 @@ LanguageMode BytecodeGenerator::language_mode() const {
}
Register BytecodeGenerator::generator_object() const {
- DCHECK(info()->literal()->CanSuspend());
+ DCHECK(IsResumableFunction(info()->literal()->kind()));
return incoming_new_target_or_generator_;
}