diff options
author | Michaël Zasso <targos@protonmail.com> | 2019-03-12 09:01:49 +0100 |
---|---|---|
committer | Michaël Zasso <targos@protonmail.com> | 2019-03-14 18:49:21 +0100 |
commit | 7b48713334469818661fe276cf571de9c7899f2d (patch) | |
tree | 4dbda49ac88db76ce09dc330a0cb587e68e139ba /deps/v8/src/interpreter/bytecode-generator.cc | |
parent | 8549ac09b256666cf5275224ec58fab9939ff32e (diff) | |
download | android-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.cc | 1613 |
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(¬_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(¬_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_; } |