[debug] implement intuitive semantics for stepping over await call.
R=*bmeurer@chromium.org, caitpotter88@gmail.com, *littledan@chromium.org, *ulan@chromium.org BUG=v8:4483 Review-Url: https://codereview.chromium.org/2033223003 Cr-Commit-Position: refs/heads/master@{#36718}
This commit is contained in:
parent
cfe77e1317
commit
8d90210a1e
@ -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) {
|
||||
|
@ -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 };
|
||||
|
@ -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<void*>(
|
||||
|
@ -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.
|
||||
|
||||
|
@ -479,6 +479,7 @@ void Debug::ThreadInit() {
|
||||
thread_local_.target_fp_ = 0;
|
||||
thread_local_.step_in_enabled_ = false;
|
||||
thread_local_.return_value_ = Handle<Object>();
|
||||
clear_suspended_generator();
|
||||
// TODO(isolates): frames_are_dropped_?
|
||||
base::NoBarrier_Store(&thread_local_.current_debug_scope_,
|
||||
static_cast<base::AtomicWord>(0));
|
||||
@ -486,25 +487,24 @@ void Debug::ThreadInit() {
|
||||
|
||||
|
||||
char* Debug::ArchiveDebug(char* storage) {
|
||||
char* to = storage;
|
||||
MemCopy(to, reinterpret_cast<char*>(&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<char*>(&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<JSFunction> 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<JSFunction> 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<SharedFunctionInfo> shared) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Debug::RecordAsyncFunction(Handle<JSGeneratorObject> 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:
|
||||
|
@ -456,6 +456,7 @@ class Debug {
|
||||
// Stepping handling.
|
||||
void PrepareStep(StepAction step_action);
|
||||
void PrepareStepIn(Handle<JSFunction> function);
|
||||
void PrepareStepInSuspendedGenerator();
|
||||
void PrepareStepOnThrow();
|
||||
void ClearStepping();
|
||||
void ClearStepOut();
|
||||
@ -463,6 +464,8 @@ class Debug {
|
||||
|
||||
bool PrepareFunctionForBreakPoints(Handle<SharedFunctionInfo> shared);
|
||||
|
||||
void RecordAsyncFunction(Handle<JSGeneratorObject> 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<Address>(&thread_local_.step_in_enabled_);
|
||||
}
|
||||
|
||||
Address suspended_generator_address() {
|
||||
return reinterpret_cast<Address>(&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<Object> exception, Handle<Object> 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<Object> return_value_;
|
||||
|
||||
Object* suspended_generator_;
|
||||
};
|
||||
|
||||
// Storage location for registers when handling debug break calls
|
||||
|
@ -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(),
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -2670,6 +2670,8 @@ ParserBase<Traits>::ParseUnaryExpression(ExpressionClassifier* classifier,
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
int await_pos = peek_position();
|
||||
Consume(Token::AWAIT);
|
||||
|
||||
ExpressionT value = ParseUnaryExpression(classifier, CHECK_OK);
|
||||
@ -2677,7 +2679,7 @@ ParserBase<Traits>::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);
|
||||
}
|
||||
|
@ -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, <operand>)
|
||||
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<Expression*>* async_function_await_args =
|
||||
new (zone()) ZoneList<Expression*>(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 {
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
|
@ -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) \
|
||||
|
@ -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,
|
||||
|
55
test/mjsunit/harmony/async-debug-step-abort-at-break.js
Normal file
55
test/mjsunit/harmony/async-debug-step-abort-at-break.js
Normal file
@ -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);
|
55
test/mjsunit/harmony/async-debug-step-continue-at-break.js
Normal file
55
test/mjsunit/harmony/async-debug-step-continue-at-break.js
Normal file
@ -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);
|
51
test/mjsunit/harmony/async-debug-step-in-and-out.js
Normal file
51
test/mjsunit/harmony/async-debug-step-in-and-out.js
Normal file
@ -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);
|
51
test/mjsunit/harmony/async-debug-step-in-out-out.js
Normal file
51
test/mjsunit/harmony/async-debug-step-in-out-out.js
Normal file
@ -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);
|
51
test/mjsunit/harmony/async-debug-step-in.js
Normal file
51
test/mjsunit/harmony/async-debug-step-in.js
Normal file
@ -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);
|
58
test/mjsunit/harmony/async-debug-step-nested.js
Normal file
58
test/mjsunit/harmony/async-debug-step-nested.js
Normal file
@ -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);
|
39
test/mjsunit/harmony/async-debug-step-next-constant.js
Normal file
39
test/mjsunit/harmony/async-debug-step-next-constant.js
Normal file
@ -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);
|
51
test/mjsunit/harmony/async-debug-step-next.js
Normal file
51
test/mjsunit/harmony/async-debug-step-next.js
Normal file
@ -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);
|
49
test/mjsunit/harmony/async-debug-step-out.js
Normal file
49
test/mjsunit/harmony/async-debug-step-out.js
Normal file
@ -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);
|
@ -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++) {
|
||||
|
Loading…
Reference in New Issue
Block a user