Commit 8d90210a authored by yangguo's avatar yangguo Committed by Commit bot

[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}
parent cfe77e13
......@@ -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,
......
// 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);
// 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);
// 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);
// 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);
// 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);
// 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);
// 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);
// 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);
// 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++) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment