Commit fb65527b authored by mythria's avatar mythria Committed by Commit bot

[Interpreter] Adds support to fetch return value on break at return.

Debugger fetches the return value of a function when we break at return.
Interpreter holds the return value in accumulator. This is not stored in a
specified location on stack and hence it is not possible to look it up from
stack similar to full-codegen or optimized frames. This cl adds support to
store the value of accumulator on debug breaks. The value of accumulator is
passed to the runtime function and is then stored in thread local data.

Also changes full-codegen implementation to match that of ignition.
The return value from full-codegen is also stored in thread local data.
The return value is fetched directly thread local data instead of
finding it by iterating over frames.

BUG=v8:4280, v8:4690
LOG=N

Review URL: https://codereview.chromium.org/1818873003

Cr-Commit-Position: refs/heads/master@{#35060}
parent bdf953b5
......@@ -81,9 +81,15 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
__ mov(ip, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
__ push(ip);
if (mode == SAVE_RESULT_REGISTER) __ push(r0);
__ mov(r0, Operand::Zero()); // no arguments
// Push arguments for DebugBreak call.
if (mode == SAVE_RESULT_REGISTER) {
// Break on return.
__ push(r0);
} else {
// Non-return breaks.
__ Push(masm->isolate()->factory()->the_hole_value());
}
__ mov(r0, Operand(1));
__ mov(r1,
Operand(ExternalReference(
Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
......@@ -94,12 +100,14 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; i++) {
Register reg = {JSCallerSavedCode(i)};
__ mov(reg, Operand(kDebugZapValue));
// Do not clobber r0 if SAVE_RESULT_REGISTER is set. It will
// contain return value of the function.
if (!(reg.is(r0) && SAVE_RESULT_REGISTER)) {
__ mov(reg, Operand(kDebugZapValue));
}
}
}
if (mode == SAVE_RESULT_REGISTER) __ pop(r0);
// Don't bother removing padding bytes pushed on the stack
// as the frame is going to be restored right away.
......
......@@ -92,9 +92,15 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
__ Mov(scratch, Smi::FromInt(LiveEdit::kFramePaddingInitialSize));
__ Push(scratch);
if (mode == SAVE_RESULT_REGISTER) __ Push(x0);
__ Mov(x0, 0); // No arguments.
// Push arguments for DebugBreak call.
if (mode == SAVE_RESULT_REGISTER) {
// Break on return.
__ Push(x0);
} else {
// Non-return breaks.
__ Push(masm->isolate()->factory()->the_hole_value());
}
__ Mov(x0, 1);
__ Mov(x1, ExternalReference(Runtime::FunctionForId(Runtime::kDebugBreak),
masm->isolate()));
......@@ -104,13 +110,14 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; i++) {
Register reg = Register::XRegFromCode(JSCallerSavedCode(i));
__ Mov(reg, Operand(kDebugZapValue));
// Do not clobber x0 if SAVE_RESULT_REGISTER is set. It will
// contain return value of the function.
if (!(reg.is(x0) && SAVE_RESULT_REGISTER)) {
__ Mov(reg, Operand(kDebugZapValue));
}
}
}
// Restore the register values from the expression stack.
if (mode == SAVE_RESULT_REGISTER) __ Pop(x0);
// Don't bother removing padding bytes pushed on the stack
// as the frame is going to be restored right away.
......
......@@ -16,7 +16,6 @@
#include "src/frames-inl.h"
#include "src/full-codegen/full-codegen.h"
#include "src/global-handles.h"
#include "src/interpreter/bytecodes.h"
#include "src/interpreter/interpreter.h"
#include "src/isolate-inl.h"
#include "src/list.h"
......@@ -474,6 +473,7 @@ void Debug::ThreadInit() {
thread_local_.last_fp_ = 0;
thread_local_.target_fp_ = 0;
thread_local_.step_in_enabled_ = false;
thread_local_.return_value_ = Handle<Object>();
// TODO(isolates): frames_are_dropped_?
base::NoBarrier_Store(&thread_local_.current_debug_scope_,
static_cast<base::AtomicWord>(0));
......@@ -560,10 +560,8 @@ void Debug::Unload() {
debug_context_ = Handle<Context>();
}
void Debug::Break(Arguments args, JavaScriptFrame* frame) {
void Debug::Break(JavaScriptFrame* frame) {
HandleScope scope(isolate_);
DCHECK(args.length() == 0);
// Initialize LiveEdit.
LiveEdit::InitializeThreadLocal(this);
......@@ -1569,25 +1567,11 @@ void Debug::RemoveDebugInfoAndClearFromShared(Handle<DebugInfo> debug_info) {
UNREACHABLE();
}
Object* Debug::SetAfterBreakTarget(JavaScriptFrame* frame) {
if (frame->is_interpreted()) {
// Find the handler from the original bytecode array.
InterpretedFrame* interpreted_frame =
reinterpret_cast<InterpretedFrame*>(frame);
SharedFunctionInfo* shared = interpreted_frame->function()->shared();
BytecodeArray* bytecode_array = shared->bytecode_array();
int bytecode_offset = interpreted_frame->GetBytecodeOffset();
interpreter::Bytecode bytecode =
interpreter::Bytecodes::FromByte(bytecode_array->get(bytecode_offset));
return isolate_->interpreter()->GetBytecodeHandler(
bytecode, interpreter::OperandScale::kSingle);
} else {
after_break_target_ = NULL;
if (!LiveEdit::SetAfterBreakTarget(this)) {
// Continue just after the slot.
after_break_target_ = frame->pc();
}
return isolate_->heap()->undefined_value();
void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) {
after_break_target_ = NULL;
if (!LiveEdit::SetAfterBreakTarget(this)) {
// Continue just after the slot.
after_break_target_ = frame->pc();
}
}
......@@ -2328,9 +2312,10 @@ DebugScope::DebugScope(Debug* debug)
base::NoBarrier_Store(&debug_->thread_local_.current_debug_scope_,
reinterpret_cast<base::AtomicWord>(this));
// Store the previous break id and frame id.
// Store the previous break id, frame id and return value.
break_id_ = debug_->break_id();
break_frame_id_ = debug_->break_frame_id();
return_value_ = debug_->return_value();
// Create the new break info. If there is no JavaScript frames there is no
// break frame id.
......@@ -2368,6 +2353,7 @@ DebugScope::~DebugScope() {
// Restore to the previous break state.
debug_->thread_local_.break_frame_id_ = break_frame_id_;
debug_->thread_local_.break_id_ = break_id_;
debug_->thread_local_.return_value_ = return_value_;
debug_->UpdateState();
}
......
......@@ -427,8 +427,8 @@ class Debug {
// Internal logic
bool Load();
void Break(Arguments args, JavaScriptFrame*);
Object* SetAfterBreakTarget(JavaScriptFrame* frame);
void Break(JavaScriptFrame* frame);
void SetAfterBreakTarget(JavaScriptFrame* frame);
// Scripts handling.
Handle<FixedArray> GetLoadedScripts();
......@@ -524,6 +524,11 @@ class Debug {
StackFrame::Id break_frame_id() { return thread_local_.break_frame_id_; }
int break_id() { return thread_local_.break_id_; }
Handle<Object> return_value() { return thread_local_.return_value_; }
void set_return_value(Handle<Object> value) {
thread_local_.return_value_ = value;
}
// Support for embedding into generated code.
Address is_active_address() {
return reinterpret_cast<Address>(&is_active_);
......@@ -678,6 +683,10 @@ class Debug {
// Stores the way how LiveEdit has patched the stack. It is used when
// debugger returns control back to user script.
LiveEdit::FrameDropMode frame_drop_mode_;
// Value of accumulator in interpreter frames. In non-interpreter frames
// this value will be the hole.
Handle<Object> return_value_;
};
// Storage location for registers when handling debug break calls
......@@ -719,6 +728,7 @@ class DebugScope BASE_EMBEDDED {
DebugScope* prev_; // Previous scope if entered recursively.
StackFrame::Id break_frame_id_; // Previous break frame id.
int break_id_; // Previous break id.
Handle<Object> return_value_; // Previous result.
bool failed_; // Did the debug context fail to load?
SaveContext save_; // Saves previous context.
PostponeInterruptsScope no_termination_exceptons_;
......
......@@ -68,9 +68,15 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
}
__ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
if (mode == SAVE_RESULT_REGISTER) __ push(eax);
__ Move(eax, Immediate(0)); // No arguments.
// Push arguments for DebugBreak call.
if (mode == SAVE_RESULT_REGISTER) {
// Break on return.
__ push(eax);
} else {
// Non-return breaks.
__ Push(masm->isolate()->factory()->the_hole_value());
}
__ Move(eax, Immediate(1));
__ mov(ebx,
Immediate(ExternalReference(
Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
......@@ -81,12 +87,14 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; ++i) {
Register reg = {JSCallerSavedCode(i)};
__ Move(reg, Immediate(kDebugZapValue));
// Do not clobber eax if SAVE_RESULT_REGISTER is set. It will
// contain return value of the function.
if (!(reg.is(eax) && SAVE_RESULT_REGISTER)) {
__ Move(reg, Immediate(kDebugZapValue));
}
}
}
if (mode == SAVE_RESULT_REGISTER) __ pop(eax);
__ pop(ebx);
// We divide stored value by 2 (untagging) and multiply it by word's size.
STATIC_ASSERT(kSmiTagSize == 1 && kSmiShiftSize == 0);
......
......@@ -77,9 +77,15 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
__ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
__ push(at);
if (mode == SAVE_RESULT_REGISTER) __ push(v0);
__ PrepareCEntryArgs(0); // No arguments.
// Push arguments for DebugBreak call.
if (mode == SAVE_RESULT_REGISTER) {
// Break on return.
__ push(v0);
} else {
// Non-return breaks.
__ Push(masm->isolate()->factory()->the_hole_value());
}
__ PrepareCEntryArgs(1);
__ PrepareCEntryFunction(ExternalReference(
Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate()));
......@@ -89,12 +95,14 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; i++) {
Register reg = {JSCallerSavedCode(i)};
__ li(reg, kDebugZapValue);
// Do not clobber v0 if SAVE_RESULT_REGISTER is set. It will
// contain return value of the function returned by DebugBreak.
if (!(reg.is(v0) && SAVE_RESULT_REGISTER)) {
__ li(reg, kDebugZapValue);
}
}
}
if (mode == SAVE_RESULT_REGISTER) __ pop(v0);
// Don't bother removing padding bytes pushed on the stack
// as the frame is going to be restored right away.
......
......@@ -79,9 +79,15 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
__ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
__ push(at);
if (mode == SAVE_RESULT_REGISTER) __ push(v0);
__ PrepareCEntryArgs(0); // No arguments.
// Push arguments for DebugBreak call.
if (mode == SAVE_RESULT_REGISTER) {
// Break on return.
__ push(v0);
} else {
// Non-return breaks.
__ Push(masm->isolate()->factory()->the_hole_value());
}
__ PrepareCEntryArgs(1);
__ PrepareCEntryFunction(ExternalReference(
Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate()));
......@@ -91,12 +97,14 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; i++) {
Register reg = {JSCallerSavedCode(i)};
__ li(reg, kDebugZapValue);
// Do not clobber v0 if SAVE_RESULT_REGISTER is set. It will
// contain return value of the function returned by DebugBreak.
if (!(reg.is(v0) && SAVE_RESULT_REGISTER)) {
__ li(reg, kDebugZapValue);
}
}
}
if (mode == SAVE_RESULT_REGISTER) __ pop(v0);
// Don't bother removing padding bytes pushed on the stack
// as the frame is going to be restored right away.
......
......@@ -83,9 +83,15 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
__ LoadSmiLiteral(ip, Smi::FromInt(LiveEdit::kFramePaddingInitialSize));
__ push(ip);
if (mode == SAVE_RESULT_REGISTER) __ push(r3);
__ mov(r3, Operand::Zero()); // no arguments
// Push arguments for DebugBreak call.
if (mode == SAVE_RESULT_REGISTER) {
// Break on return.
__ push(r3);
} else {
// Non-return breaks.
__ Push(masm->isolate()->factory()->the_hole_value());
}
__ mov(r3, Operand(1));
__ mov(r4,
Operand(ExternalReference(
Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
......@@ -96,12 +102,14 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; i++) {
Register reg = {JSCallerSavedCode(i)};
__ mov(reg, Operand(kDebugZapValue));
// Do not clobber r3 if SAVE_RESULT_REGISTER is set. It will
// contain return value of the function.
if (!(reg.is(r3) && SAVE_RESULT_REGISTER)) {
__ mov(reg, Operand(kDebugZapValue));
}
}
}
if (mode == SAVE_RESULT_REGISTER) __ pop(r3);
// Don't bother removing padding bytes pushed on the stack
// as the frame is going to be restored right away.
......
......@@ -88,9 +88,15 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
__ LoadSmiLiteral(ip, Smi::FromInt(LiveEdit::kFramePaddingInitialSize));
__ push(ip);
if (mode == SAVE_RESULT_REGISTER) __ push(r2);
__ mov(r2, Operand::Zero()); // no arguments
// Push arguments for DebugBreak call.
if (mode == SAVE_RESULT_REGISTER) {
// Break on return.
__ push(r2);
} else {
// Non-return breaks.
__ Push(masm->isolate()->factory()->the_hole_value());
}
__ mov(r2, Operand(1));
__ mov(r3,
Operand(ExternalReference(
Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
......@@ -101,12 +107,14 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; i++) {
Register reg = {JSCallerSavedCode(i)};
__ mov(reg, Operand(kDebugZapValue));
// Do not clobber r2 if SAVE_RESULT_REGISTER is set. It will
// contain return value of the function.
if (!(reg.is(r2) && SAVE_RESULT_REGISTER)) {
__ mov(reg, Operand(kDebugZapValue));
}
}
}
if (mode == SAVE_RESULT_REGISTER) __ pop(r2);
// Don't bother removing padding bytes pushed on the stack
// as the frame is going to be restored right away.
......
......@@ -69,9 +69,15 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
}
__ Push(Smi::FromInt(LiveEdit::kFramePaddingInitialSize));
if (mode == SAVE_RESULT_REGISTER) __ Push(rax);
__ Set(rax, 0); // No arguments (argc == 0).
// Push arguments for DebugBreak call.
if (mode == SAVE_RESULT_REGISTER) {
// Break on return.
__ Push(rax);
} else {
// Non-return breaks.
__ Push(masm->isolate()->factory()->the_hole_value());
}
__ Set(rax, 1);
__ Move(rbx, ExternalReference(Runtime::FunctionForId(Runtime::kDebugBreak),
masm->isolate()));
......@@ -81,12 +87,14 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; ++i) {
Register reg = {JSCallerSavedCode(i)};
__ Set(reg, kDebugZapValue);
// Do not clobber rax if SAVE_RESULT_REGISTER is set. It will
// contain return value of the function.
if (!(reg.is(rax) && SAVE_RESULT_REGISTER)) {
__ Set(reg, kDebugZapValue);
}
}
}
if (mode == SAVE_RESULT_REGISTER) __ Pop(rax);
// Read current padding counter and skip corresponding number of words.
__ Pop(kScratchRegister);
__ SmiToInteger32(kScratchRegister, kScratchRegister);
......
......@@ -68,9 +68,15 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
}
__ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
if (mode == SAVE_RESULT_REGISTER) __ push(eax);
__ Move(eax, Immediate(0)); // No arguments.
// Push arguments for DebugBreak call.
if (mode == SAVE_RESULT_REGISTER) {
// Break on return.
__ push(eax);
} else {
// Non-return breaks.
__ Push(masm->isolate()->factory()->the_hole_value());
}
__ Move(eax, Immediate(1));
__ mov(ebx,
Immediate(ExternalReference(
Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
......@@ -81,12 +87,14 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; ++i) {
Register reg = {JSCallerSavedCode(i)};
__ Move(reg, Immediate(kDebugZapValue));
// Do not clobber eax if SAVE_RESULT_REGISTER is set. It will
// contain return value of the function.
if (!(reg.is(eax) && SAVE_RESULT_REGISTER)) {
__ Move(reg, Immediate(kDebugZapValue));
}
}
}
if (mode == SAVE_RESULT_REGISTER) __ pop(eax);
__ pop(ebx);
// We divide stored value by 2 (untagging) and multiply it by word's size.
STATIC_ASSERT(kSmiTagSize == 1 && kSmiShiftSize == 0);
......
......@@ -1485,11 +1485,13 @@ void Interpreter::DoDebugger(InterpreterAssembler* assembler) {
// DebugBreak
//
// Call runtime to handle a debug break.
#define DEBUG_BREAK(Name, ...) \
void Interpreter::Do##Name(InterpreterAssembler* assembler) { \
Node* context = __ GetContext(); \
Node* original_handler = __ CallRuntime(Runtime::kDebugBreak, context); \
__ DispatchToBytecodeHandler(original_handler); \
#define DEBUG_BREAK(Name, ...) \
void Interpreter::Do##Name(InterpreterAssembler* assembler) { \
Node* context = __ GetContext(); \
Node* accumulator = __ GetAccumulator(); \
Node* original_handler = \
__ CallRuntime(Runtime::kDebugBreakOnBytecode, context, accumulator); \
__ DispatchToBytecodeHandler(original_handler); \
}
DEBUG_BREAK_BYTECODE_LIST(DEBUG_BREAK);
#undef DEBUG_BREAK
......
......@@ -5,11 +5,13 @@
#include "src/runtime/runtime-utils.h"
#include "src/arguments.h"
#include "src/debug/debug.h"
#include "src/debug/debug-evaluate.h"
#include "src/debug/debug-frames.h"
#include "src/debug/debug-scopes.h"
#include "src/debug/debug.h"
#include "src/frames-inl.h"
#include "src/interpreter/bytecodes.h"
#include "src/interpreter/interpreter.h"
#include "src/isolate-inl.h"
#include "src/runtime/runtime.h"
......@@ -18,11 +20,39 @@ namespace internal {
RUNTIME_FUNCTION(Runtime_DebugBreak) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 0);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(Object, value, 0);
isolate->debug()->set_return_value(value);
// Get the top-most JavaScript frame.
JavaScriptFrameIterator it(isolate);
isolate->debug()->Break(args, it.frame());
return isolate->debug()->SetAfterBreakTarget(it.frame());
isolate->debug()->Break(it.frame());
isolate->debug()->SetAfterBreakTarget(it.frame());
return *isolate->debug()->return_value();
}
RUNTIME_FUNCTION(Runtime_DebugBreakOnBytecode) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(Object, value, 0);
isolate->debug()->set_return_value(value);
// Get the top-most JavaScript frame.
JavaScriptFrameIterator it(isolate);
isolate->debug()->Break(it.frame());
// Return the handler from the original bytecode array.
DCHECK(it.frame()->is_interpreted());
InterpretedFrame* interpreted_frame =
reinterpret_cast<InterpretedFrame*>(it.frame());
SharedFunctionInfo* shared = interpreted_frame->function()->shared();
BytecodeArray* bytecode_array = shared->bytecode_array();
int bytecode_offset = interpreted_frame->GetBytecodeOffset();
interpreter::Bytecode bytecode =
interpreter::Bytecodes::FromByte(bytecode_array->get(bytecode_offset));
return isolate->interpreter()->GetBytecodeHandler(
bytecode, interpreter::OperandScale::kSingle);
}
......@@ -591,31 +621,7 @@ RUNTIME_FUNCTION(Runtime_GetFrameDetails) {
// to the frame information.
Handle<Object> return_value = isolate->factory()->undefined_value();
if (at_return) {
StackFrameIterator it2(isolate);
Address internal_frame_sp = NULL;
while (!it2.done()) {
if (it2.frame()->is_internal()) {
internal_frame_sp = it2.frame()->sp();
} else {
if (it2.frame()->is_java_script()) {
if (it2.frame()->id() == it.frame()->id()) {
// The internal frame just before the JavaScript frame contains the
// value to return on top. A debug break at return will create an
// internal frame to store the return value (eax/rax/r0) before
// entering the debug break exit frame.
if (internal_frame_sp != NULL) {
return_value =
Handle<Object>(Memory::Object_at(internal_frame_sp), isolate);
break;
}
}
}
// Indicate that the previous frame was not an internal frame.
internal_frame_sp = NULL;
}
it2.Advance();
}
return_value = isolate->debug()->return_value();
}
// Now advance to the arguments adapter frame (if any). It contains all
......
......@@ -137,10 +137,10 @@ namespace internal {
F(DateCurrentTime, 0, 1) \
F(ThrowNotDateError, 0, 1)
#define FOR_EACH_INTRINSIC_DEBUG(F) \
F(HandleDebuggerStatement, 0, 1) \
F(DebugBreak, 0, 1) \
F(DebugBreak, 1, 1) \
F(DebugBreakOnBytecode, 1, 1) \
F(SetDebugEventListener, 2, 1) \
F(ScheduleBreak, 0, 1) \
F(DebugGetInternalProperties, 1, 1) \
......@@ -195,7 +195,6 @@ namespace internal {
F(DebugIsActive, 0, 1) \
F(DebugBreakInOptimizedCode, 0, 1)
#define FOR_EACH_INTRINSIC_FORIN(F) \
F(ForInDone, 2, 1) \
F(ForInEnumerate, 1, 1) \
......
......@@ -755,7 +755,6 @@
['ignition == True', {
# TODO(yangguo,4690): assertion failures in debugger tests.
'debug-allscopes-on-debugger': [FAIL],
'debug-return-value': [FAIL],
'es6/debug-stepnext-for': [FAIL],
'es6/debug-stepin-string-template': [FAIL],
'es6/debug-promises/stepin-constructor': [FAIL],
......
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