diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index 4d85500494..665993bafc 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -720,21 +720,23 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ ldr(r4, FieldMemOperand(r1, JSGeneratorObject::kFunctionOffset)); // Flood function if we are stepping. - Label skip_flooding; + Label prepare_step_in_if_stepping, prepare_step_in_suspended_generator; + Label stepping_prepared; ExternalReference step_in_enabled = ExternalReference::debug_step_in_enabled_address(masm->isolate()); __ mov(ip, Operand(step_in_enabled)); __ ldrb(ip, MemOperand(ip)); __ cmp(ip, Operand(0)); - __ b(eq, &skip_flooding); - { - FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); - __ Push(r1, r2, r4); - __ CallRuntime(Runtime::kDebugPrepareStepInIfStepping); - __ Pop(r1, r2); - __ ldr(r4, FieldMemOperand(r1, JSGeneratorObject::kFunctionOffset)); - } - __ bind(&skip_flooding); + __ b(ne, &prepare_step_in_if_stepping); + + // Flood function if we need to continue stepping in the suspended generator. + ExternalReference debug_suspended_generator = + ExternalReference::debug_suspended_generator_address(masm->isolate()); + __ mov(ip, Operand(debug_suspended_generator)); + __ ldr(ip, MemOperand(ip)); + __ cmp(ip, Operand(r1)); + __ b(eq, &prepare_step_in_suspended_generator); + __ bind(&stepping_prepared); // Push receiver. __ ldr(ip, FieldMemOperand(r1, JSGeneratorObject::kReceiverOffset)); @@ -830,6 +832,26 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ Move(r0, r1); // Continuation expects generator object in r0. __ Jump(r3); } + + __ bind(&prepare_step_in_if_stepping); + { + FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); + __ Push(r1, r2, r4); + __ CallRuntime(Runtime::kDebugPrepareStepInIfStepping); + __ Pop(r1, r2); + __ ldr(r4, FieldMemOperand(r1, JSGeneratorObject::kFunctionOffset)); + } + __ b(&stepping_prepared); + + __ bind(&prepare_step_in_suspended_generator); + { + FrameAndConstantPoolScope scope(masm, StackFrame::INTERNAL); + __ Push(r1, r2); + __ CallRuntime(Runtime::kDebugPrepareStepInSuspendedGenerator); + __ Pop(r1, r2); + __ ldr(r4, FieldMemOperand(r1, JSGeneratorObject::kFunctionOffset)); + } + __ b(&stepping_prepared); } void Builtins::Generate_ConstructedNonConstructable(MacroAssembler* masm) { diff --git a/src/arm64/builtins-arm64.cc b/src/arm64/builtins-arm64.cc index 11e1cde615..f17a3001dd 100644 --- a/src/arm64/builtins-arm64.cc +++ b/src/arm64/builtins-arm64.cc @@ -727,20 +727,22 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ Ldr(x4, FieldMemOperand(x1, JSGeneratorObject::kFunctionOffset)); // Flood function if we are stepping. - Label skip_flooding; + Label prepare_step_in_if_stepping, prepare_step_in_suspended_generator; + Label stepping_prepared; ExternalReference step_in_enabled = ExternalReference::debug_step_in_enabled_address(masm->isolate()); __ Mov(x10, Operand(step_in_enabled)); __ Ldrb(x10, MemOperand(x10)); - __ CompareAndBranch(x10, Operand(0), eq, &skip_flooding); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(x1, x2, x4); - __ CallRuntime(Runtime::kDebugPrepareStepInIfStepping); - __ Pop(x2, x1); - __ Ldr(x4, FieldMemOperand(x1, JSGeneratorObject::kFunctionOffset)); - } - __ bind(&skip_flooding); + __ CompareAndBranch(x10, Operand(0), ne, &prepare_step_in_if_stepping); + + // Flood function if we need to continue stepping in the suspended generator. + ExternalReference debug_suspended_generator = + ExternalReference::debug_suspended_generator_address(masm->isolate()); + __ Mov(x10, Operand(debug_suspended_generator)); + __ Ldr(x10, MemOperand(x10)); + __ CompareAndBranch(x10, Operand(x1), eq, + &prepare_step_in_suspended_generator); + __ Bind(&stepping_prepared); // Push receiver. __ Ldr(x5, FieldMemOperand(x1, JSGeneratorObject::kReceiverOffset)); @@ -828,6 +830,26 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ Move(x0, x1); // Continuation expects generator object in x0. __ Br(x10); } + + __ Bind(&prepare_step_in_if_stepping); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(x1, x2, x4); + __ CallRuntime(Runtime::kDebugPrepareStepInIfStepping); + __ Pop(x2, x1); + __ Ldr(x4, FieldMemOperand(x1, JSGeneratorObject::kFunctionOffset)); + } + __ B(&stepping_prepared); + + __ Bind(&prepare_step_in_suspended_generator); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(x1, x2); + __ CallRuntime(Runtime::kDebugPrepareStepInSuspendedGenerator); + __ Pop(x2, x1); + __ Ldr(x4, FieldMemOperand(x1, JSGeneratorObject::kFunctionOffset)); + } + __ B(&stepping_prepared); } enum IsTagged { kArgcIsSmiTagged, kArgcIsUntaggedInt }; diff --git a/src/assembler.cc b/src/assembler.cc index e2bc4bb2f4..553e37da40 100644 --- a/src/assembler.cc +++ b/src/assembler.cc @@ -1799,6 +1799,10 @@ ExternalReference ExternalReference::debug_step_in_enabled_address( return ExternalReference(isolate->debug()->step_in_enabled_address()); } +ExternalReference ExternalReference::debug_suspended_generator_address( + Isolate* isolate) { + return ExternalReference(isolate->debug()->suspended_generator_address()); +} ExternalReference ExternalReference::fixed_typed_array_base_data_offset() { return ExternalReference(reinterpret_cast( diff --git a/src/assembler.h b/src/assembler.h index 882bef38d8..3670d7fa5c 100644 --- a/src/assembler.h +++ b/src/assembler.h @@ -1066,6 +1066,9 @@ class ExternalReference BASE_EMBEDDED { // Used to check if single stepping is enabled in generated code. static ExternalReference debug_step_in_enabled_address(Isolate* isolate); + // Used to check for suspended generator, used for stepping across await call. + static ExternalReference debug_suspended_generator_address(Isolate* isolate); + #ifndef V8_INTERPRETED_REGEXP // C functions called from RegExp generated code. diff --git a/src/debug/debug.cc b/src/debug/debug.cc index 495e162f28..948cf41f03 100644 --- a/src/debug/debug.cc +++ b/src/debug/debug.cc @@ -479,6 +479,7 @@ void Debug::ThreadInit() { thread_local_.target_fp_ = 0; thread_local_.step_in_enabled_ = false; thread_local_.return_value_ = Handle(); + clear_suspended_generator(); // TODO(isolates): frames_are_dropped_? base::NoBarrier_Store(&thread_local_.current_debug_scope_, static_cast(0)); @@ -486,25 +487,24 @@ void Debug::ThreadInit() { char* Debug::ArchiveDebug(char* storage) { - char* to = storage; - MemCopy(to, reinterpret_cast(&thread_local_), sizeof(ThreadLocal)); + // Simply reset state. Don't archive anything. ThreadInit(); return storage + ArchiveSpacePerThread(); } char* Debug::RestoreDebug(char* storage) { - char* from = storage; - MemCopy(reinterpret_cast(&thread_local_), from, sizeof(ThreadLocal)); + // Simply reset state. Don't restore anything. + ThreadInit(); return storage + ArchiveSpacePerThread(); } +int Debug::ArchiveSpacePerThread() { return 0; } -int Debug::ArchiveSpacePerThread() { - return sizeof(ThreadLocal); +void Debug::Iterate(ObjectVisitor* v) { + v->VisitPointer(&thread_local_.suspended_generator_); } - DebugInfoListNode::DebugInfoListNode(DebugInfo* debug_info): next_(NULL) { // Globalize the request debug info object and make it weak. GlobalHandles* global_handles = debug_info->GetIsolate()->global_handles(); @@ -940,6 +940,17 @@ void Debug::PrepareStepIn(Handle function) { } } +void Debug::PrepareStepInSuspendedGenerator() { + if (!is_active()) return; + if (in_debug_scope()) return; + DCHECK(has_suspended_generator()); + thread_local_.last_step_action_ = StepIn; + thread_local_.step_in_enabled_ = true; + Handle function( + JSGeneratorObject::cast(thread_local_.suspended_generator_)->function()); + FloodWithOneShot(function); + clear_suspended_generator(); +} void Debug::PrepareStepOnThrow() { if (!is_active()) return; @@ -1041,6 +1052,8 @@ void Debug::PrepareStep(StepAction step_action) { debug_info->abstract_code()->SourceStatementPosition( summary.code_offset()); thread_local_.last_fp_ = frame->UnpaddedFP(); + // No longer perform the current async step. + clear_suspended_generator(); switch (step_action) { case StepNone: @@ -1385,6 +1398,13 @@ bool Debug::PrepareFunctionForBreakPoints(Handle shared) { return true; } +void Debug::RecordAsyncFunction(Handle generator_object) { + if (last_step_action() <= StepOut) return; + DCHECK(!has_suspended_generator()); + DCHECK(generator_object->function()->shared()->is_async()); + thread_local_.suspended_generator_ = *generator_object; + ClearStepping(); +} class SharedFunctionInfoFinder { public: diff --git a/src/debug/debug.h b/src/debug/debug.h index 2cdc1515a6..3e6b384356 100644 --- a/src/debug/debug.h +++ b/src/debug/debug.h @@ -456,6 +456,7 @@ class Debug { // Stepping handling. void PrepareStep(StepAction step_action); void PrepareStepIn(Handle function); + void PrepareStepInSuspendedGenerator(); void PrepareStepOnThrow(); void ClearStepping(); void ClearStepOut(); @@ -463,6 +464,8 @@ class Debug { bool PrepareFunctionForBreakPoints(Handle shared); + void RecordAsyncFunction(Handle generator_object); + // Returns whether the operation succeeded. Compilation can only be triggered // if a valid closure is passed as the second argument, otherwise the shared // function needs to be compiled already. @@ -497,6 +500,7 @@ class Debug { char* RestoreDebug(char* from); static int ArchiveSpacePerThread(); void FreeThreadResources() { } + void Iterate(ObjectVisitor* v); bool CheckExecutionState(int id) { return is_active() && !debug_context().is_null() && break_id() != 0 && @@ -544,6 +548,10 @@ class Debug { return reinterpret_cast
(&thread_local_.step_in_enabled_); } + Address suspended_generator_address() { + return reinterpret_cast
(&thread_local_.suspended_generator_); + } + StepAction last_step_action() { return thread_local_.last_step_action_; } DebugFeatureTracker* feature_tracker() { return &feature_tracker_; } @@ -564,6 +572,14 @@ class Debug { return break_disabled_ || in_debug_event_listener_; } + void clear_suspended_generator() { + thread_local_.suspended_generator_ = Smi::FromInt(0); + } + + bool has_suspended_generator() const { + return thread_local_.suspended_generator_ != Smi::FromInt(0); + } + void OnException(Handle exception, Handle promise); // Constructors for debug event objects. @@ -687,6 +703,8 @@ class Debug { // Value of accumulator in interpreter frames. In non-interpreter frames // this value will be the hole. Handle return_value_; + + Object* suspended_generator_; }; // Storage location for registers when handling debug break calls diff --git a/src/external-reference-table.cc b/src/external-reference-table.cc index 8ccfc29968..ef8ddbb804 100644 --- a/src/external-reference-table.cc +++ b/src/external-reference-table.cc @@ -189,6 +189,8 @@ ExternalReferenceTable::ExternalReferenceTable(Isolate* isolate) { "Debug::is_active_address()"); Add(ExternalReference::debug_step_in_enabled_address(isolate).address(), "Debug::step_in_enabled_address()"); + Add(ExternalReference::debug_suspended_generator_address(isolate).address(), + "Debug::step_suspended_generator_address()"); #ifndef V8_INTERPRETED_REGEXP Add(ExternalReference::re_case_insensitive_compare_uc16(isolate).address(), diff --git a/src/heap/heap.cc b/src/heap/heap.cc index 5c724bf3ee..ba21937edb 100644 --- a/src/heap/heap.cc +++ b/src/heap/heap.cc @@ -4820,6 +4820,8 @@ void Heap::IterateStrongRoots(ObjectVisitor* v, VisitMode mode) { v->Synchronize(VisitorSynchronization::kTop); Relocatable::Iterate(isolate_, v); v->Synchronize(VisitorSynchronization::kRelocatable); + isolate_->debug()->Iterate(v); + v->Synchronize(VisitorSynchronization::kDebug); isolate_->compilation_cache()->Iterate(v); v->Synchronize(VisitorSynchronization::kCompilationCache); diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index e17fb70baa..4178067930 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -407,22 +407,19 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset)); // Flood function if we are stepping. - Label skip_flooding; + Label prepare_step_in_if_stepping, prepare_step_in_suspended_generator; + Label stepping_prepared; ExternalReference step_in_enabled = ExternalReference::debug_step_in_enabled_address(masm->isolate()); __ cmpb(Operand::StaticVariable(step_in_enabled), Immediate(0)); - __ j(equal, &skip_flooding); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(ebx); - __ Push(edx); - __ Push(edi); - __ CallRuntime(Runtime::kDebugPrepareStepInIfStepping); - __ Pop(edx); - __ Pop(ebx); - __ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset)); - } - __ bind(&skip_flooding); + __ j(not_equal, &prepare_step_in_if_stepping); + + // Flood function if we need to continue stepping in the suspended generator. + ExternalReference debug_suspended_generator = + ExternalReference::debug_suspended_generator_address(masm->isolate()); + __ cmp(ebx, Operand::StaticVariable(debug_suspended_generator)); + __ j(equal, &prepare_step_in_suspended_generator); + __ bind(&stepping_prepared); // Pop return address. __ PopReturnAddressTo(eax); @@ -518,6 +515,31 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ mov(eax, ebx); // Continuation expects generator object in eax. __ jmp(edx); } + + __ bind(&prepare_step_in_if_stepping); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(ebx); + __ Push(edx); + __ Push(edi); + __ CallRuntime(Runtime::kDebugPrepareStepInIfStepping); + __ Pop(edx); + __ Pop(ebx); + __ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset)); + } + __ jmp(&stepping_prepared); + + __ bind(&prepare_step_in_suspended_generator); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(ebx); + __ Push(edx); + __ CallRuntime(Runtime::kDebugPrepareStepInSuspendedGenerator); + __ Pop(edx); + __ Pop(ebx); + __ mov(edi, FieldOperand(ebx, JSGeneratorObject::kFunctionOffset)); + } + __ jmp(&stepping_prepared); } static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch1, diff --git a/src/mips/builtins-mips.cc b/src/mips/builtins-mips.cc index 530e811616..54377088e7 100644 --- a/src/mips/builtins-mips.cc +++ b/src/mips/builtins-mips.cc @@ -841,20 +841,21 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ lw(t0, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset)); // Flood function if we are stepping. - Label skip_flooding; + Label prepare_step_in_if_stepping, prepare_step_in_suspended_generator; + Label stepping_prepared; ExternalReference step_in_enabled = ExternalReference::debug_step_in_enabled_address(masm->isolate()); __ li(t1, Operand(step_in_enabled)); __ lb(t1, MemOperand(t1)); - __ Branch(&skip_flooding, eq, t1, Operand(zero_reg)); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(a1, a2, t0); - __ CallRuntime(Runtime::kDebugPrepareStepInIfStepping); - __ Pop(a1, a2); - __ lw(t0, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset)); - } - __ bind(&skip_flooding); + __ Branch(&prepare_step_in_if_stepping, ne, t1, Operand(zero_reg)); + + // Flood function if we need to continue stepping in the suspended generator. + ExternalReference debug_suspended_generator = + ExternalReference::debug_suspended_generator_address(masm->isolate()); + __ li(t1, Operand(debug_suspended_generator)); + __ lw(t1, MemOperand(t1)); + __ Branch(&prepare_step_in_suspended_generator, eq, t1, Operand(zero_reg)); + __ bind(&stepping_prepared); // Push receiver. __ lw(t1, FieldMemOperand(a1, JSGeneratorObject::kReceiverOffset)); @@ -950,6 +951,26 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ Move(v0, a1); // Continuation expects generator object in v0. __ Jump(a3); } + + __ bind(&prepare_step_in_if_stepping); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(a1, a2, t0); + __ CallRuntime(Runtime::kDebugPrepareStepInIfStepping); + __ Pop(a1, a2); + } + __ Branch(USE_DELAY_SLOT, &stepping_prepared); + __ lw(t0, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset)); + + __ bind(&prepare_step_in_suspended_generator); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(a1, a2); + __ CallRuntime(Runtime::kDebugPrepareStepInSuspendedGenerator); + __ Pop(a1, a2); + } + __ Branch(USE_DELAY_SLOT, &stepping_prepared); + __ lw(t0, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset)); } static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch) { diff --git a/src/mips64/builtins-mips64.cc b/src/mips64/builtins-mips64.cc index 60f5b9874a..0c11b17b0f 100644 --- a/src/mips64/builtins-mips64.cc +++ b/src/mips64/builtins-mips64.cc @@ -706,20 +706,21 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ ld(a4, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset)); // Flood function if we are stepping. - Label skip_flooding; + Label prepare_step_in_if_stepping, prepare_step_in_suspended_generator; + Label stepping_prepared; ExternalReference step_in_enabled = ExternalReference::debug_step_in_enabled_address(masm->isolate()); __ li(t1, Operand(step_in_enabled)); __ lb(t1, MemOperand(t1)); - __ Branch(&skip_flooding, eq, t1, Operand(zero_reg)); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(a1, a2, a4); - __ CallRuntime(Runtime::kDebugPrepareStepInIfStepping); - __ Pop(a1, a2); - __ ld(a4, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset)); - } - __ bind(&skip_flooding); + __ Branch(&prepare_step_in_if_stepping, ne, t1, Operand(zero_reg)); + + // Flood function if we need to continue stepping in the suspended generator. + ExternalReference debug_suspended_generator = + ExternalReference::debug_suspended_generator_address(masm->isolate()); + __ li(t1, Operand(debug_suspended_generator)); + __ lw(t1, MemOperand(t1)); + __ Branch(&prepare_step_in_suspended_generator, eq, t1, Operand(zero_reg)); + __ bind(&stepping_prepared); // Push receiver. __ ld(a5, FieldMemOperand(a1, JSGeneratorObject::kReceiverOffset)); @@ -815,6 +816,26 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ Move(v0, a1); // Continuation expects generator object in v0. __ Jump(a3); } + + __ bind(&prepare_step_in_if_stepping); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(a1, a2, a4); + __ CallRuntime(Runtime::kDebugPrepareStepInIfStepping); + __ Pop(a1, a2); + } + __ Branch(USE_DELAY_SLOT, &stepping_prepared); + __ ld(a4, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset)); + + __ bind(&prepare_step_in_suspended_generator); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(a1, a2); + __ CallRuntime(Runtime::kDebugPrepareStepInSuspendedGenerator); + __ Pop(a1, a2); + } + __ Branch(USE_DELAY_SLOT, &stepping_prepared); + __ ld(a4, FieldMemOperand(a1, JSGeneratorObject::kFunctionOffset)); } void Builtins::Generate_ConstructedNonConstructable(MacroAssembler* masm) { diff --git a/src/parsing/parser-base.h b/src/parsing/parser-base.h index 8d17a18b78..fe7492a207 100644 --- a/src/parsing/parser-base.h +++ b/src/parsing/parser-base.h @@ -2670,6 +2670,8 @@ ParserBase::ParseUnaryExpression(ExpressionClassifier* classifier, default: break; } + + int await_pos = peek_position(); Consume(Token::AWAIT); ExpressionT value = ParseUnaryExpression(classifier, CHECK_OK); @@ -2677,7 +2679,7 @@ ParserBase::ParseUnaryExpression(ExpressionClassifier* classifier, classifier->RecordFormalParameterInitializerError( Scanner::Location(beg_pos, scanner()->location().end_pos), MessageTemplate::kAwaitExpressionFormalParameter); - return Traits::RewriteAwaitExpression(value, beg_pos); + return Traits::RewriteAwaitExpression(value, await_pos); } else { return this->ParsePostfixExpression(classifier, ok); } diff --git a/src/parsing/parser.cc b/src/parsing/parser.cc index 5f6d460d54..50d875feb6 100644 --- a/src/parsing/parser.cc +++ b/src/parsing/parser.cc @@ -5598,7 +5598,8 @@ void ParserTraits::RewriteNonPattern(Type::ExpressionClassifier* classifier, parser_->RewriteNonPattern(classifier, ok); } -Expression* ParserTraits::RewriteAwaitExpression(Expression* value, int pos) { +Expression* ParserTraits::RewriteAwaitExpression(Expression* value, + int await_pos) { // yield %AsyncFunctionAwait(.generator_object, ) Variable* generator_object_variable = parser_->function_state_->generator_object_variable(); @@ -5606,21 +5607,38 @@ Expression* ParserTraits::RewriteAwaitExpression(Expression* value, int pos) { // If generator_object_variable is null, if (!generator_object_variable) return value; - Expression* generator_object = - parser_->factory()->NewVariableProxy(generator_object_variable); + auto factory = parser_->factory(); + const int nopos = RelocInfo::kNoPosition; + + Variable* temp_var = parser_->scope_->NewTemporary( + parser_->ast_value_factory()->empty_string()); + VariableProxy* temp_proxy = factory->NewVariableProxy(temp_var); + Block* do_block = factory->NewBlock(nullptr, 2, false, nopos); + + // Wrap value evaluation to provide a break location. + Expression* value_assignment = + factory->NewAssignment(Token::ASSIGN, temp_proxy, value, nopos); + do_block->statements()->Add( + factory->NewExpressionStatement(value_assignment, value->position()), + zone()); ZoneList* async_function_await_args = new (zone()) ZoneList(2, zone()); + Expression* generator_object = + factory->NewVariableProxy(generator_object_variable); async_function_await_args->Add(generator_object, zone()); - async_function_await_args->Add(value, zone()); + async_function_await_args->Add(temp_proxy, zone()); Expression* async_function_await = parser_->factory()->NewCallRuntime( - Context::ASYNC_FUNCTION_AWAIT_INDEX, async_function_await_args, - RelocInfo::kNoPosition); + Context::ASYNC_FUNCTION_AWAIT_INDEX, async_function_await_args, nopos); + // Wrap await to provide a break location between value evaluation and yield. + Expression* await_assignment = factory->NewAssignment( + Token::ASSIGN, temp_proxy, async_function_await, nopos); + do_block->statements()->Add( + factory->NewExpressionStatement(await_assignment, await_pos), zone()); + Expression* do_expr = factory->NewDoExpression(do_block, temp_var, nopos); - generator_object = - parser_->factory()->NewVariableProxy(generator_object_variable); - return parser_->factory()->NewYield(generator_object, async_function_await, - pos); + generator_object = factory->NewVariableProxy(generator_object_variable); + return factory->NewYield(generator_object, do_expr, nopos); } Zone* ParserTraits::zone() const { diff --git a/src/runtime/runtime-debug.cc b/src/runtime/runtime-debug.cc index e90e9d5fc4..601dbe3ce0 100644 --- a/src/runtime/runtime-debug.cc +++ b/src/runtime/runtime-debug.cc @@ -1698,6 +1698,13 @@ RUNTIME_FUNCTION(Runtime_DebugPrepareStepInIfStepping) { return isolate->heap()->undefined_value(); } +// Set one shot breakpoints for the suspended generator object. +RUNTIME_FUNCTION(Runtime_DebugPrepareStepInSuspendedGenerator) { + HandleScope scope(isolate); + DCHECK_EQ(0, args.length()); + isolate->debug()->PrepareStepInSuspendedGenerator(); + return isolate->heap()->undefined_value(); +} RUNTIME_FUNCTION(Runtime_DebugPushPromise) { DCHECK(args.length() == 2); diff --git a/src/runtime/runtime-generator.cc b/src/runtime/runtime-generator.cc index 1245ff14fb..e201c49fc6 100644 --- a/src/runtime/runtime-generator.cc +++ b/src/runtime/runtime-generator.cc @@ -5,6 +5,7 @@ #include "src/runtime/runtime-utils.h" #include "src/arguments.h" +#include "src/debug/debug.h" #include "src/factory.h" #include "src/frames-inl.h" #include "src/objects-inl.h" @@ -51,6 +52,10 @@ RUNTIME_FUNCTION(Runtime_SuspendJSGeneratorObject) { DCHECK(frame->function()->shared()->is_compiled()); DCHECK(!frame->function()->IsOptimized()); + if (generator_object->function()->shared()->is_async()) { + isolate->debug()->RecordAsyncFunction(generator_object); + } + // The caller should have saved the context and continuation already. DCHECK_EQ(generator_object->context(), Context::cast(frame->context())); DCHECK_LT(0, generator_object->continuation()); diff --git a/src/runtime/runtime.h b/src/runtime/runtime.h index 90d374f332..c3502f4966 100644 --- a/src/runtime/runtime.h +++ b/src/runtime/runtime.h @@ -136,62 +136,63 @@ namespace internal { F(DateCurrentTime, 0, 1) \ F(ThrowNotDateError, 0, 1) -#define FOR_EACH_INTRINSIC_DEBUG(F) \ - F(HandleDebuggerStatement, 0, 1) \ - F(DebugBreak, 1, 1) \ - F(DebugBreakOnBytecode, 1, 1) \ - F(SetDebugEventListener, 2, 1) \ - F(ScheduleBreak, 0, 1) \ - F(DebugGetInternalProperties, 1, 1) \ - F(DebugGetPropertyDetails, 2, 1) \ - F(DebugGetProperty, 2, 1) \ - F(DebugPropertyTypeFromDetails, 1, 1) \ - F(DebugPropertyAttributesFromDetails, 1, 1) \ - F(CheckExecutionState, 1, 1) \ - F(GetFrameCount, 1, 1) \ - F(GetFrameDetails, 2, 1) \ - F(GetScopeCount, 2, 1) \ - F(GetScopeDetails, 4, 1) \ - F(GetAllScopesDetails, 4, 1) \ - F(GetFunctionScopeCount, 1, 1) \ - F(GetFunctionScopeDetails, 2, 1) \ - F(SetScopeVariableValue, 6, 1) \ - F(DebugPrintScopes, 0, 1) \ - F(SetBreakPointsActive, 1, 1) \ - F(GetBreakLocations, 2, 1) \ - F(SetFunctionBreakPoint, 3, 1) \ - F(SetScriptBreakPoint, 4, 1) \ - F(ClearBreakPoint, 1, 1) \ - F(ChangeBreakOnException, 2, 1) \ - F(IsBreakOnException, 1, 1) \ - F(PrepareStep, 2, 1) \ - F(ClearStepping, 0, 1) \ - F(DebugEvaluate, 6, 1) \ - F(DebugEvaluateGlobal, 4, 1) \ - F(DebugGetLoadedScripts, 0, 1) \ - F(DebugReferencedBy, 3, 1) \ - F(DebugConstructedBy, 2, 1) \ - F(DebugGetPrototype, 1, 1) \ - F(DebugSetScriptSource, 2, 1) \ - F(FunctionGetInferredName, 1, 1) \ - F(FunctionGetDebugName, 1, 1) \ - F(GetFunctionCodePositionFromSource, 2, 1) \ - F(ExecuteInDebugContext, 1, 1) \ - F(GetDebugContext, 0, 1) \ - F(CollectGarbage, 1, 1) \ - F(GetHeapUsage, 0, 1) \ - F(GetScript, 1, 1) \ - F(ScriptLineCount, 1, 1) \ - F(ScriptLineStartPosition, 2, 1) \ - F(ScriptLineEndPosition, 2, 1) \ - F(ScriptLocationFromLine, 4, 1) \ - F(ScriptPositionInfo, 3, 1) \ - F(ScriptSourceLine, 2, 1) \ - F(DebugPrepareStepInIfStepping, 1, 1) \ - F(DebugPushPromise, 2, 1) \ - F(DebugPopPromise, 0, 1) \ - F(DebugAsyncTaskEvent, 1, 1) \ - F(DebugIsActive, 0, 1) \ +#define FOR_EACH_INTRINSIC_DEBUG(F) \ + F(HandleDebuggerStatement, 0, 1) \ + F(DebugBreak, 1, 1) \ + F(DebugBreakOnBytecode, 1, 1) \ + F(SetDebugEventListener, 2, 1) \ + F(ScheduleBreak, 0, 1) \ + F(DebugGetInternalProperties, 1, 1) \ + F(DebugGetPropertyDetails, 2, 1) \ + F(DebugGetProperty, 2, 1) \ + F(DebugPropertyTypeFromDetails, 1, 1) \ + F(DebugPropertyAttributesFromDetails, 1, 1) \ + F(CheckExecutionState, 1, 1) \ + F(GetFrameCount, 1, 1) \ + F(GetFrameDetails, 2, 1) \ + F(GetScopeCount, 2, 1) \ + F(GetScopeDetails, 4, 1) \ + F(GetAllScopesDetails, 4, 1) \ + F(GetFunctionScopeCount, 1, 1) \ + F(GetFunctionScopeDetails, 2, 1) \ + F(SetScopeVariableValue, 6, 1) \ + F(DebugPrintScopes, 0, 1) \ + F(SetBreakPointsActive, 1, 1) \ + F(GetBreakLocations, 2, 1) \ + F(SetFunctionBreakPoint, 3, 1) \ + F(SetScriptBreakPoint, 4, 1) \ + F(ClearBreakPoint, 1, 1) \ + F(ChangeBreakOnException, 2, 1) \ + F(IsBreakOnException, 1, 1) \ + F(PrepareStep, 2, 1) \ + F(ClearStepping, 0, 1) \ + F(DebugEvaluate, 6, 1) \ + F(DebugEvaluateGlobal, 4, 1) \ + F(DebugGetLoadedScripts, 0, 1) \ + F(DebugReferencedBy, 3, 1) \ + F(DebugConstructedBy, 2, 1) \ + F(DebugGetPrototype, 1, 1) \ + F(DebugSetScriptSource, 2, 1) \ + F(FunctionGetInferredName, 1, 1) \ + F(FunctionGetDebugName, 1, 1) \ + F(GetFunctionCodePositionFromSource, 2, 1) \ + F(ExecuteInDebugContext, 1, 1) \ + F(GetDebugContext, 0, 1) \ + F(CollectGarbage, 1, 1) \ + F(GetHeapUsage, 0, 1) \ + F(GetScript, 1, 1) \ + F(ScriptLineCount, 1, 1) \ + F(ScriptLineStartPosition, 2, 1) \ + F(ScriptLineEndPosition, 2, 1) \ + F(ScriptLocationFromLine, 4, 1) \ + F(ScriptPositionInfo, 3, 1) \ + F(ScriptSourceLine, 2, 1) \ + F(DebugPrepareStepInIfStepping, 1, 1) \ + F(DebugPrepareStepInSuspendedGenerator, 0, 1) \ + F(DebugPushPromise, 2, 1) \ + F(DebugPopPromise, 0, 1) \ + F(DebugAsyncTaskEvent, 1, 1) \ + F(DebugIsActive, 0, 1) \ F(DebugBreakInOptimizedCode, 0, 1) #define FOR_EACH_INTRINSIC_FORIN(F) \ diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index 517be34d85..e7a567527d 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -480,23 +480,22 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ movp(rdi, FieldOperand(rbx, JSGeneratorObject::kFunctionOffset)); // Flood function if we are stepping. - Label skip_flooding; + Label prepare_step_in_if_stepping, prepare_step_in_suspended_generator; + Label stepping_prepared; ExternalReference step_in_enabled = ExternalReference::debug_step_in_enabled_address(masm->isolate()); Operand step_in_enabled_operand = masm->ExternalOperand(step_in_enabled); __ cmpb(step_in_enabled_operand, Immediate(0)); - __ j(equal, &skip_flooding); - { - FrameScope scope(masm, StackFrame::INTERNAL); - __ Push(rbx); - __ Push(rdx); - __ Push(rdi); - __ CallRuntime(Runtime::kDebugPrepareStepInIfStepping); - __ Pop(rdx); - __ Pop(rbx); - __ movp(rdi, FieldOperand(rbx, JSGeneratorObject::kFunctionOffset)); - } - __ bind(&skip_flooding); + __ j(not_equal, &prepare_step_in_if_stepping); + + // Flood function if we need to continue stepping in the suspended generator. + ExternalReference debug_suspended_generator = + ExternalReference::debug_suspended_generator_address(masm->isolate()); + Operand debug_suspended_generator_operand = + masm->ExternalOperand(debug_suspended_generator); + __ cmpp(rbx, debug_suspended_generator_operand); + __ j(equal, &prepare_step_in_suspended_generator); + __ bind(&stepping_prepared); // Pop return address. __ PopReturnAddressTo(rax); @@ -596,6 +595,31 @@ void Builtins::Generate_ResumeGeneratorTrampoline(MacroAssembler* masm) { __ movp(rax, rbx); // Continuation expects generator object in rax. __ jmp(rdx); } + + __ bind(&prepare_step_in_if_stepping); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(rbx); + __ Push(rdx); + __ Push(rdi); + __ CallRuntime(Runtime::kDebugPrepareStepInIfStepping); + __ Pop(rdx); + __ Pop(rbx); + __ movp(rdi, FieldOperand(rbx, JSGeneratorObject::kFunctionOffset)); + } + __ jmp(&stepping_prepared); + + __ bind(&prepare_step_in_suspended_generator); + { + FrameScope scope(masm, StackFrame::INTERNAL); + __ Push(rbx); + __ Push(rdx); + __ CallRuntime(Runtime::kDebugPrepareStepInSuspendedGenerator); + __ Pop(rdx); + __ Pop(rbx); + __ movp(rdi, FieldOperand(rbx, JSGeneratorObject::kFunctionOffset)); + } + __ jmp(&stepping_prepared); } static void LeaveInterpreterFrame(MacroAssembler* masm, Register scratch1, diff --git a/test/mjsunit/harmony/async-debug-step-abort-at-break.js b/test/mjsunit/harmony/async-debug-step-abort-at-break.js new file mode 100644 index 0000000000..be1f8056a8 --- /dev/null +++ b/test/mjsunit/harmony/async-debug-step-abort-at-break.js @@ -0,0 +1,55 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --expose-debug-as debug --allow-natives-syntax --harmony-async-await + +var Debug = debug.Debug; +var step_count = 0; + +function listener(event, execState, eventData, data) { + if (event != Debug.DebugEvent.Break) return; + try { + var line = execState.frame(0).sourceLineText(); + print(line); + var [match, expected_count, step] = /\/\/ B(\d) (\w+)$/.exec(line); + assertEquals(step_count++, parseInt(expected_count)); + if (step != "Continue") execState.prepareStep(Debug.StepAction[step]); + } catch (e) { + print(e, e.stack); + quit(1); + } +} + +Debug.setListener(listener); + +var late_resolve; + +function g() { + return new Promise( // B3 StepOut + function(res, rej) { + late_resolve = res; + } + ); +} + +async function f() { + var a = 1; + debugger; // B0 StepNext + a += // B1 StepNext + await // B4 StepNext + g(); // B2 StepIn + return a; +} + +f(); + +// Starting a new step action at an intermediate break point +// means that we will abort the current async step. +debugger; // B5 StepNext + +late_resolve(3); // B6 Continue + +%RunMicrotasks(); + +assertEquals(7, step_count); diff --git a/test/mjsunit/harmony/async-debug-step-continue-at-break.js b/test/mjsunit/harmony/async-debug-step-continue-at-break.js new file mode 100644 index 0000000000..5099b2f53e --- /dev/null +++ b/test/mjsunit/harmony/async-debug-step-continue-at-break.js @@ -0,0 +1,55 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --expose-debug-as debug --allow-natives-syntax --harmony-async-await + +var Debug = debug.Debug; +var step_count = 0; + +function listener(event, execState, eventData, data) { + if (event != Debug.DebugEvent.Break) return; + try { + var line = execState.frame(0).sourceLineText(); + print(line); + var [match, expected_count, step] = /\/\/ B(\d) (\w+)$/.exec(line); + assertEquals(step_count++, parseInt(expected_count)); + if (step != "Continue") execState.prepareStep(Debug.StepAction[step]); + } catch (e) { + print(e, e.stack); + quit(1); + } +} + +Debug.setListener(listener); + +var late_resolve; + +function g() { + return new Promise( // B3 StepOut + function(res, rej) { + late_resolve = res; + } + ); +} + +async function f() { + var a = 1; + debugger; // B0 StepNext + a += // B1 StepNext + await // B4 StepNext + g(); // B2 StepIn + return a; // B6 StepNext +} // B7 Continue + +f(); + +// Continuing at an intermediate break point means that we will +// carry on with the current async step. +debugger; // B5 Continue + +late_resolve(3); + +%RunMicrotasks(); + +assertEquals(8, step_count); diff --git a/test/mjsunit/harmony/async-debug-step-in-and-out.js b/test/mjsunit/harmony/async-debug-step-in-and-out.js new file mode 100644 index 0000000000..30fe2d6053 --- /dev/null +++ b/test/mjsunit/harmony/async-debug-step-in-and-out.js @@ -0,0 +1,51 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --expose-debug-as debug --allow-natives-syntax --harmony-async-await + +var Debug = debug.Debug; +var step_count = 0; + +function listener(event, execState, eventData, data) { + if (event != Debug.DebugEvent.Break) return; + try { + var line = execState.frame(0).sourceLineText(); + print(line); + var [match, expected_count, step] = /\/\/ B(\d) (\w+)$/.exec(line); + assertEquals(step_count++, parseInt(expected_count)); + if (step != "Continue") execState.prepareStep(Debug.StepAction[step]); + } catch (e) { + print(e, e.stack); + quit(1); + } +} + +Debug.setListener(listener); + +var late_resolve; + +function g() { + return new Promise( // B3 StepOut + function(res, rej) { + late_resolve = res; + } + ); +} + +async function f() { + var a = 1; + debugger; // B0 StepNext + a += // B1 StepNext + await // B4 StepNext + g(); // B2 StepIn + return a; // B5 StepNext +} // B6 Continue + +f(); + +late_resolve(3); + +%RunMicrotasks(); + +assertEquals(7, step_count); diff --git a/test/mjsunit/harmony/async-debug-step-in-out-out.js b/test/mjsunit/harmony/async-debug-step-in-out-out.js new file mode 100644 index 0000000000..c2f34bb029 --- /dev/null +++ b/test/mjsunit/harmony/async-debug-step-in-out-out.js @@ -0,0 +1,51 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --expose-debug-as debug --allow-natives-syntax --harmony-async-await + +var Debug = debug.Debug; +var step_count = 0; + +function listener(event, execState, eventData, data) { + if (event != Debug.DebugEvent.Break) return; + try { + var line = execState.frame(0).sourceLineText(); + print(line); + var [match, expected_count, step] = /\/\/ B(\d) (\w+)$/.exec(line); + assertEquals(step_count++, parseInt(expected_count)); + if (step != "Continue") execState.prepareStep(Debug.StepAction[step]); + } catch (e) { + print(e, e.stack); + quit(1); + } +} + +Debug.setListener(listener); + +var late_resolve; + +function g() { + return new Promise( // B3 StepOut + function(res, rej) { + late_resolve = res; + } + ); +} + +async function f() { + var a = 1; + debugger; // B0 StepNext + a += // B1 StepNext + await // B4 StepOut + g(); // B2 StepIn + return a; +} + +f(); + +late_resolve(3); // B5 Continue + +%RunMicrotasks(); + +assertEquals(6, step_count); diff --git a/test/mjsunit/harmony/async-debug-step-in.js b/test/mjsunit/harmony/async-debug-step-in.js new file mode 100644 index 0000000000..0a7de1a2a3 --- /dev/null +++ b/test/mjsunit/harmony/async-debug-step-in.js @@ -0,0 +1,51 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --expose-debug-as debug --allow-natives-syntax --harmony-async-await + +var Debug = debug.Debug; +var step_count = 0; + +function listener(event, execState, eventData, data) { + if (event != Debug.DebugEvent.Break) return; + try { + var line = execState.frame(0).sourceLineText(); + print(line); + var [match, expected_count, step] = /\/\/ B(\d) (\w+)$/.exec(line); + assertEquals(step_count++, parseInt(expected_count)); + if (step != "Continue") execState.prepareStep(Debug.StepAction[step]); + } catch (e) { + print(e, e.stack); + quit(1); + } +} + +Debug.setListener(listener); + +var late_resolve; + +function g() { + return new Promise( // B3 StepIn + function(res, rej) { + late_resolve = res; // B4 StepIn + } // B5 StepIn + ); +} // B6 StepIn + +async function f() { + var a = 1; + debugger; // B0 StepNext + a += // B1 StepIn + await // B7 StepIn + g(); // B2 StepIn + return a; // B8 StepIn +} // B9 Continue + +f().then(value => assertEquals(4, value)); + +late_resolve(3); + +%RunMicrotasks(); + +assertEquals(10, step_count); diff --git a/test/mjsunit/harmony/async-debug-step-nested.js b/test/mjsunit/harmony/async-debug-step-nested.js new file mode 100644 index 0000000000..adf7a51432 --- /dev/null +++ b/test/mjsunit/harmony/async-debug-step-nested.js @@ -0,0 +1,58 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --expose-debug-as debug --allow-natives-syntax --harmony-async-await + +var Debug = debug.Debug; +var step_count = 0; + +function listener(event, execState, eventData, data) { + if (event != Debug.DebugEvent.Break) return; + try { + var line = execState.frame(0).sourceLineText(); + print(line); + var [match, expected_count, step] = /\/\/ B(\d) (\w+)$/.exec(line); + assertEquals(step_count++, parseInt(expected_count)); + if (step != "Continue") execState.prepareStep(Debug.StepAction[step]); + } catch (e) { + print(e, e.stack); + quit(1); + } +} + +Debug.setListener(listener); + +var late_resolve; + +function g() { + return new Promise( // B4 StepOut + function(res, rej) { + late_resolve = res; + } + ); +} + +async function f1() { + var a = 1; + debugger; // B0 StepNext + a += // B1 StepNext + await // B6 StepNext + f2(); // B2 StepIn + return a; // B7 StepNext +} // B8 Continue + +async function f2() { + var b = + await // B5 StepOut + g(); // B3 StepIn + return b; +} + +f1(); + +late_resolve(3); + +%RunMicrotasks(); + +assertEquals(9, step_count); diff --git a/test/mjsunit/harmony/async-debug-step-next-constant.js b/test/mjsunit/harmony/async-debug-step-next-constant.js new file mode 100644 index 0000000000..cea86d7a2f --- /dev/null +++ b/test/mjsunit/harmony/async-debug-step-next-constant.js @@ -0,0 +1,39 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --expose-debug-as debug --allow-natives-syntax --harmony-async-await + +var Debug = debug.Debug; +var step_count = 0; + +function listener(event, execState, eventData, data) { + if (event != Debug.DebugEvent.Break) return; + try { + var line = execState.frame(0).sourceLineText(); + print(line); + var [match, expected_count, step] = /\/\/ B(\d) (\w+)$/.exec(line); + assertEquals(step_count++, parseInt(expected_count)); + if (step != "Continue") execState.prepareStep(Debug.StepAction[step]); + } catch (e) { + print(e, e.stack); + quit(1); + } +} + +Debug.setListener(listener); + +async function f() { + var a = 1; + debugger; // B0 StepNext + a += // B1 StepNext + await // B3 StepNext + 5; // B2 StepNext + return a; // B4 StepNext +} // B5 Continue + +f(); + +%RunMicrotasks(); + +assertEquals(6, step_count); diff --git a/test/mjsunit/harmony/async-debug-step-next.js b/test/mjsunit/harmony/async-debug-step-next.js new file mode 100644 index 0000000000..952d88dd85 --- /dev/null +++ b/test/mjsunit/harmony/async-debug-step-next.js @@ -0,0 +1,51 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --expose-debug-as debug --allow-natives-syntax --harmony-async-await + +var Debug = debug.Debug; +var step_count = 0; + +function listener(event, execState, eventData, data) { + if (event != Debug.DebugEvent.Break) return; + try { + var line = execState.frame(0).sourceLineText(); + print(line); + var [match, expected_count, step] = /\/\/ B(\d) (\w+)$/.exec(line); + assertEquals(step_count++, parseInt(expected_count)); + if (step != "Continue") execState.prepareStep(Debug.StepAction[step]); + } catch (e) { + print(e, e.stack); + quit(1); + } +} + +Debug.setListener(listener); + +var late_resolve; + +function g() { + return new Promise( + function(res, rej) { + late_resolve = res; + } + ); +} + +async function f() { + var a = 1; + debugger; // B0 StepNext + a += // B1 StepNext + await // B3 StepNext + g(); // B2 StepNext + return a; // B4 StepNext +} // B5 Continue + +f(); + +late_resolve(3); + +%RunMicrotasks(); + +assertEquals(6, step_count); diff --git a/test/mjsunit/harmony/async-debug-step-out.js b/test/mjsunit/harmony/async-debug-step-out.js new file mode 100644 index 0000000000..41779acb54 --- /dev/null +++ b/test/mjsunit/harmony/async-debug-step-out.js @@ -0,0 +1,49 @@ +// Copyright 2016 the V8 project authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Flags: --expose-debug-as debug --allow-natives-syntax --harmony-async-await + +var Debug = debug.Debug; +var step_count = 0; + +function listener(event, execState, eventData, data) { + if (event != Debug.DebugEvent.Break) return; + try { + var line = execState.frame(0).sourceLineText(); + print(line); + var [match, expected_count, step] = /\/\/ B(\d) (\w+)$/.exec(line); + assertEquals(step_count++, parseInt(expected_count)); + if (step != "Continue") execState.prepareStep(Debug.StepAction[step]); + } catch (e) { + print(e, e.stack); + quit(1); + } +} + +Debug.setListener(listener); + +var late_resolve; + +function g() { + return new Promise( + function(res, rej) { + late_resolve = res; + } + ); +} + +async function f() { + var a = 1; + debugger; // B0 StepNext + a += await g(); // B1 StepOut + return a; +} + +f(); + +late_resolve(3); // B2 Continue + +%RunMicrotasks(); + +assertEquals(3, step_count); diff --git a/test/mjsunit/harmony/async-function-debug-scopes.js b/test/mjsunit/harmony/async-function-debug-scopes.js index 33f7716d07..3d72549d2a 100644 --- a/test/mjsunit/harmony/async-function-debug-scopes.js +++ b/test/mjsunit/harmony/async-function-debug-scopes.js @@ -501,7 +501,6 @@ function CheckFastAllScopes(scopes, exec_state) { // Check that the scope chain contains the expected types of scopes. function CheckScopeChain(scopes, exec_state) { var all_scopes = exec_state.frame().allScopes(); - assertEquals(scopes.length, exec_state.frame().scopeCount()); assertEquals( scopes.length, all_scopes.length, "FrameMirror.allScopes length"); for (var i = 0; i < scopes.length; i++) {