Commit 25d2b247 authored by machenbach's avatar machenbach Committed by Commit bot

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

Revert of [Interpreter] Adds support to fetch return value on break at return. (patchset #9 id:160001 of https://codereview.chromium.org/1818873003/ )

Reason for revert:
[Sheriff] Seems to break nosnap debug:
https://build.chromium.org/p/client.v8/builders/V8%20Linux%20-%20nosnap%20-%20debug/builds/6019

Original issue's description:
> [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
>
> Committed: https://crrev.com/fb65527b75754bcf3b173f16f5d0b04a1c6d9b99
> Cr-Commit-Position: refs/heads/master@{#35060}

TBR=rmcilroy@chromium.org,yangguo@chromium.org,weiliang.lin@intel.com,balazs.kilvady@imgtec.com,jyan@ca.ibm.com,mythria@chromium.org
# Skipping CQ checks because original CL landed less than 1 days ago.
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=v8:4280, v8:4690

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

Cr-Commit-Position: refs/heads/master@{#35065}
parent 2e87c7f5
......@@ -81,15 +81,9 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
__ mov(ip, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
__ push(ip);
// 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));
if (mode == SAVE_RESULT_REGISTER) __ push(r0);
__ mov(r0, Operand::Zero()); // no arguments
__ mov(r1,
Operand(ExternalReference(
Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
......@@ -100,13 +94,11 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; i++) {
Register reg = {JSCallerSavedCode(i)};
// 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,15 +92,9 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
__ Mov(scratch, Smi::FromInt(LiveEdit::kFramePaddingInitialSize));
__ Push(scratch);
// 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);
if (mode == SAVE_RESULT_REGISTER) __ Push(x0);
__ Mov(x0, 0); // No arguments.
__ Mov(x1, ExternalReference(Runtime::FunctionForId(Runtime::kDebugBreak),
masm->isolate()));
......@@ -110,13 +104,12 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; i++) {
Register reg = Register::XRegFromCode(JSCallerSavedCode(i));
// 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,6 +16,7 @@
#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"
......@@ -473,7 +474,6 @@ 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,8 +560,10 @@ void Debug::Unload() {
debug_context_ = Handle<Context>();
}
void Debug::Break(JavaScriptFrame* frame) {
void Debug::Break(Arguments args, JavaScriptFrame* frame) {
HandleScope scope(isolate_);
DCHECK(args.length() == 0);
// Initialize LiveEdit.
LiveEdit::InitializeThreadLocal(this);
......@@ -1567,12 +1569,26 @@ void Debug::RemoveDebugInfoAndClearFromShared(Handle<DebugInfo> debug_info) {
UNREACHABLE();
}
void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) {
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();
}
}
......@@ -2312,10 +2328,9 @@ DebugScope::DebugScope(Debug* debug)
base::NoBarrier_Store(&debug_->thread_local_.current_debug_scope_,
reinterpret_cast<base::AtomicWord>(this));
// Store the previous break id, frame id and return value.
// Store the previous break id and frame id.
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.
......@@ -2353,7 +2368,6 @@ 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(JavaScriptFrame* frame);
void SetAfterBreakTarget(JavaScriptFrame* frame);
void Break(Arguments args, JavaScriptFrame*);
Object* SetAfterBreakTarget(JavaScriptFrame* frame);
// Scripts handling.
Handle<FixedArray> GetLoadedScripts();
......@@ -524,11 +524,6 @@ 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_);
......@@ -683,10 +678,6 @@ 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
......@@ -728,7 +719,6 @@ 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,15 +68,9 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
}
__ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
// 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));
if (mode == SAVE_RESULT_REGISTER) __ push(eax);
__ Move(eax, Immediate(0)); // No arguments.
__ mov(ebx,
Immediate(ExternalReference(
Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
......@@ -87,13 +81,11 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; ++i) {
Register reg = {JSCallerSavedCode(i)};
// 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.
......
......@@ -77,15 +77,9 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
__ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
__ push(at);
// 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);
if (mode == SAVE_RESULT_REGISTER) __ push(v0);
__ PrepareCEntryArgs(0); // No arguments.
__ PrepareCEntryFunction(ExternalReference(
Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate()));
......@@ -95,13 +89,11 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; i++) {
Register reg = {JSCallerSavedCode(i)};
// 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,15 +79,9 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
__ li(at, Operand(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
__ push(at);
// 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);
if (mode == SAVE_RESULT_REGISTER) __ push(v0);
__ PrepareCEntryArgs(0); // No arguments.
__ PrepareCEntryFunction(ExternalReference(
Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate()));
......@@ -97,13 +91,11 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; i++) {
Register reg = {JSCallerSavedCode(i)};
// 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,15 +83,9 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
__ LoadSmiLiteral(ip, Smi::FromInt(LiveEdit::kFramePaddingInitialSize));
__ push(ip);
// 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));
if (mode == SAVE_RESULT_REGISTER) __ push(r3);
__ mov(r3, Operand::Zero()); // no arguments
__ mov(r4,
Operand(ExternalReference(
Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
......@@ -102,13 +96,11 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; i++) {
Register reg = {JSCallerSavedCode(i)};
// 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,15 +88,9 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
__ LoadSmiLiteral(ip, Smi::FromInt(LiveEdit::kFramePaddingInitialSize));
__ push(ip);
// 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));
if (mode == SAVE_RESULT_REGISTER) __ push(r2);
__ mov(r2, Operand::Zero()); // no arguments
__ mov(r3,
Operand(ExternalReference(
Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
......@@ -107,13 +101,11 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; i++) {
Register reg = {JSCallerSavedCode(i)};
// 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,15 +69,9 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
}
__ Push(Smi::FromInt(LiveEdit::kFramePaddingInitialSize));
// 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);
if (mode == SAVE_RESULT_REGISTER) __ Push(rax);
__ Set(rax, 0); // No arguments (argc == 0).
__ Move(rbx, ExternalReference(Runtime::FunctionForId(Runtime::kDebugBreak),
masm->isolate()));
......@@ -87,13 +81,11 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; ++i) {
Register reg = {JSCallerSavedCode(i)};
// 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);
......
......@@ -68,15 +68,9 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
}
__ push(Immediate(Smi::FromInt(LiveEdit::kFramePaddingInitialSize)));
// 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));
if (mode == SAVE_RESULT_REGISTER) __ push(eax);
__ Move(eax, Immediate(0)); // No arguments.
__ mov(ebx,
Immediate(ExternalReference(
Runtime::FunctionForId(Runtime::kDebugBreak), masm->isolate())));
......@@ -87,13 +81,11 @@ void DebugCodegen::GenerateDebugBreakStub(MacroAssembler* masm,
if (FLAG_debug_code) {
for (int i = 0; i < kNumJSCallerSaved; ++i) {
Register reg = {JSCallerSavedCode(i)};
// 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.
......
......@@ -1488,9 +1488,7 @@ void Interpreter::DoDebugger(InterpreterAssembler* assembler) {
#define DEBUG_BREAK(Name, ...) \
void Interpreter::Do##Name(InterpreterAssembler* assembler) { \
Node* context = __ GetContext(); \
Node* accumulator = __ GetAccumulator(); \
Node* original_handler = \
__ CallRuntime(Runtime::kDebugBreakOnBytecode, context, accumulator); \
Node* original_handler = __ CallRuntime(Runtime::kDebugBreak, context); \
__ DispatchToBytecodeHandler(original_handler); \
}
DEBUG_BREAK_BYTECODE_LIST(DEBUG_BREAK);
......
......@@ -5,13 +5,11 @@
#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"
......@@ -20,39 +18,11 @@ namespace internal {
RUNTIME_FUNCTION(Runtime_DebugBreak) {
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());
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);
DCHECK(args.length() == 0);
// 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);
isolate->debug()->Break(args, it.frame());
return isolate->debug()->SetAfterBreakTarget(it.frame());
}
......@@ -621,7 +591,31 @@ RUNTIME_FUNCTION(Runtime_GetFrameDetails) {
// to the frame information.
Handle<Object> return_value = isolate->factory()->undefined_value();
if (at_return) {
return_value = isolate->debug()->return_value();
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();
}
}
// 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, 1, 1) \
F(DebugBreakOnBytecode, 1, 1) \
F(DebugBreak, 0, 1) \
F(SetDebugEventListener, 2, 1) \
F(ScheduleBreak, 0, 1) \
F(DebugGetInternalProperties, 1, 1) \
......@@ -195,6 +195,7 @@ namespace internal {
F(DebugIsActive, 0, 1) \
F(DebugBreakInOptimizedCode, 0, 1)
#define FOR_EACH_INTRINSIC_FORIN(F) \
F(ForInDone, 2, 1) \
F(ForInEnumerate, 1, 1) \
......
......@@ -756,6 +756,7 @@
['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