[debugger] flood function for stepping before calling it.

R=verwaest@chromium.org

Committed: https://crrev.com/93eb633214e0f97bf70ae30d2a07b7fbbaa78266
Cr-Commit-Position: refs/heads/master@{#32285}

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

Cr-Commit-Position: refs/heads/master@{#32339}
This commit is contained in:
yangguo 2015-11-26 06:12:04 -08:00 committed by Commit bot
parent 69227cc192
commit 81e131ce48
41 changed files with 752 additions and 513 deletions

View File

@ -570,7 +570,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ Call(code, RelocInfo::CODE_TARGET);
} else {
ParameterCount actual(r0);
__ InvokeFunction(r1, r3, actual, CALL_FUNCTION, NullCallWrapper());
__ InvokeFunction(r1, r3, actual, CALL_FUNCTION,
CheckDebugStepCallWrapper());
}
// Store offset of return address for deoptimizer.
@ -1686,10 +1687,10 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
__ ldr(r2,
FieldMemOperand(r2, SharedFunctionInfo::kFormalParameterCountOffset));
__ SmiUntag(r2);
__ ldr(r4, FieldMemOperand(r1, JSFunction::kCodeEntryOffset));
ParameterCount actual(r0);
ParameterCount expected(r2);
__ InvokeCode(r4, no_reg, expected, actual, JUMP_FUNCTION, NullCallWrapper());
__ InvokeFunctionCode(r1, no_reg, expected, actual, JUMP_FUNCTION,
CheckDebugStepCallWrapper());
// The function is a "classConstructor", need to raise an exception.
__ bind(&class_constructor);

View File

@ -1304,18 +1304,65 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
}
void MacroAssembler::InvokeCode(Register code,
Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper) {
void MacroAssembler::FloodFunctionIfStepping(Register fun, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual) {
Label skip_flooding;
ExternalReference debug_step_action =
ExternalReference::debug_last_step_action_address(isolate());
mov(r4, Operand(debug_step_action));
ldrb(r4, MemOperand(r4));
cmp(r4, Operand(StepIn));
b(ne, &skip_flooding);
{
FrameScope frame(this,
has_frame() ? StackFrame::NONE : StackFrame::INTERNAL);
if (expected.is_reg()) {
SmiTag(expected.reg());
Push(expected.reg());
}
if (actual.is_reg()) {
SmiTag(actual.reg());
Push(actual.reg());
}
if (new_target.is_valid()) {
Push(new_target);
}
Push(fun);
Push(fun);
CallRuntime(Runtime::kDebugPrepareStepInIfStepping, 1);
Pop(fun);
if (new_target.is_valid()) {
Pop(new_target);
}
if (actual.is_reg()) {
Pop(actual.reg());
SmiUntag(actual.reg());
}
if (expected.is_reg()) {
Pop(expected.reg());
SmiUntag(expected.reg());
}
}
bind(&skip_flooding);
}
void MacroAssembler::InvokeFunctionCode(Register function, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper) {
// You can't call a function without a valid frame.
DCHECK(flag == JUMP_FUNCTION || has_frame());
// Ensure new target is passed in the correct register. Otherwise clear the
// appropriate register in case new target is not given.
DCHECK(function.is(r1));
DCHECK_IMPLIES(new_target.is_valid(), new_target.is(r3));
if (call_wrapper.NeedsDebugStepCheck()) {
FloodFunctionIfStepping(function, new_target, expected, actual);
}
// Clear the new.target register if not given.
if (!new_target.is_valid()) {
LoadRoot(r3, Heap::kUndefinedValueRootIndex);
}
@ -1325,6 +1372,11 @@ void MacroAssembler::InvokeCode(Register code,
InvokePrologue(expected, actual, &done, &definitely_mismatches, flag,
call_wrapper);
if (!definitely_mismatches) {
// We call indirectly through the code field in the function to
// allow recompilation to take effect without changing any of the
// call sites.
Register code = r4;
ldr(code, FieldMemOperand(function, JSFunction::kCodeEntryOffset));
if (flag == CALL_FUNCTION) {
call_wrapper.BeforeCall(CallSize(code));
Call(code);
@ -1353,19 +1405,17 @@ void MacroAssembler::InvokeFunction(Register fun,
DCHECK(fun.is(r1));
Register expected_reg = r2;
Register code_reg = r4;
Register temp_reg = r4;
ldr(code_reg, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
ldr(temp_reg, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
ldr(expected_reg,
FieldMemOperand(code_reg,
FieldMemOperand(temp_reg,
SharedFunctionInfo::kFormalParameterCountOffset));
SmiUntag(expected_reg);
ldr(code_reg,
FieldMemOperand(r1, JSFunction::kCodeEntryOffset));
ParameterCount expected(expected_reg);
InvokeCode(code_reg, new_target, expected, actual, flag, call_wrapper);
InvokeFunctionCode(fun, new_target, expected, actual, flag, call_wrapper);
}
@ -1383,11 +1433,7 @@ void MacroAssembler::InvokeFunction(Register function,
// Get the function and setup the context.
ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
// We call indirectly through the code field in the function to
// allow recompilation to take effect without changing any of the
// call sites.
ldr(r4, FieldMemOperand(r1, JSFunction::kCodeEntryOffset));
InvokeCode(r4, no_reg, expected, actual, flag, call_wrapper);
InvokeFunctionCode(r1, no_reg, expected, actual, flag, call_wrapper);
}

View File

@ -640,12 +640,10 @@ class MacroAssembler: public Assembler {
// JavaScript invokes
// Invoke the JavaScript function code by either calling or jumping.
void InvokeCode(Register code,
Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper);
void InvokeFunctionCode(Register function, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual, InvokeFlag flag,
const CallWrapper& call_wrapper);
// Invoke the JavaScript function in the given register. Changes the
// current context to the context in the function before invoking.
@ -1456,6 +1454,10 @@ class MacroAssembler: public Assembler {
InvokeFlag flag,
const CallWrapper& call_wrapper);
void FloodFunctionIfStepping(Register fun, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual);
void InitializeNewString(Register string,
Register length,
Heap::RootListIndex map_index,

View File

@ -579,7 +579,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
} else {
ParameterCount actual(argc);
__ InvokeFunction(constructor, new_target, actual, CALL_FUNCTION,
NullCallWrapper());
CheckDebugStepCallWrapper());
}
// Store offset of return address for deoptimizer.
@ -1674,10 +1674,10 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
__ Ldrsw(
x2, FieldMemOperand(x2, SharedFunctionInfo::kFormalParameterCountOffset));
__ Ldr(x4, FieldMemOperand(x1, JSFunction::kCodeEntryOffset));
ParameterCount actual(x0);
ParameterCount expected(x2);
__ InvokeCode(x4, no_reg, expected, actual, JUMP_FUNCTION, NullCallWrapper());
__ InvokeFunctionCode(x1, no_reg, expected, actual, JUMP_FUNCTION,
CheckDebugStepCallWrapper());
// The function is a "classConstructor", need to raise an exception.
__ bind(&class_constructor);

View File

@ -2393,7 +2393,7 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
call_wrapper.AfterCall();
if (!*definitely_mismatches) {
// If the arg counts don't match, no extra code is emitted by
// MAsm::InvokeCode and we can just fall through.
// MAsm::InvokeFunctionCode and we can just fall through.
B(done);
}
} else {
@ -2404,18 +2404,62 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
}
void MacroAssembler::InvokeCode(Register code,
Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper) {
void MacroAssembler::FloodFunctionIfStepping(Register fun, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual) {
Label skip_flooding;
ExternalReference debug_step_action =
ExternalReference::debug_last_step_action_address(isolate());
Mov(x4, Operand(debug_step_action));
ldrb(x4, MemOperand(x4));
CompareAndBranch(x4, Operand(StepIn), ne, &skip_flooding);
{
FrameScope frame(this,
has_frame() ? StackFrame::NONE : StackFrame::INTERNAL);
if (expected.is_reg()) {
SmiTag(expected.reg());
Push(expected.reg());
}
if (actual.is_reg()) {
SmiTag(actual.reg());
Push(actual.reg());
}
if (new_target.is_valid()) {
Push(new_target);
}
Push(fun);
Push(fun);
CallRuntime(Runtime::kDebugPrepareStepInIfStepping, 1);
Pop(fun);
if (new_target.is_valid()) {
Pop(new_target);
}
if (actual.is_reg()) {
Pop(actual.reg());
SmiUntag(actual.reg());
}
if (expected.is_reg()) {
Pop(expected.reg());
SmiUntag(expected.reg());
}
}
bind(&skip_flooding);
}
void MacroAssembler::InvokeFunctionCode(Register function, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper) {
// You can't call a function without a valid frame.
DCHECK(flag == JUMP_FUNCTION || has_frame());
// Ensure new target is passed in the correct register. Otherwise clear the
// appropriate register in case new target is not given.
DCHECK(function.is(x1));
DCHECK_IMPLIES(new_target.is_valid(), new_target.is(x3));
FloodFunctionIfStepping(function, new_target, expected, actual);
// Clear the new.target register if not given.
if (!new_target.is_valid()) {
LoadRoot(x3, Heap::kUndefinedValueRootIndex);
}
@ -2429,6 +2473,11 @@ void MacroAssembler::InvokeCode(Register code,
// have handled the call through the argument adaptor mechanism.
// The called function expects the call kind in x5.
if (!definitely_mismatches) {
// We call indirectly through the code field in the function to
// allow recompilation to take effect without changing any of the
// call sites.
Register code = x4;
Ldr(code, FieldMemOperand(function, JSFunction::kCodeEntryOffset));
if (flag == CALL_FUNCTION) {
call_wrapper.BeforeCall(CallSize(code));
Call(code);
@ -2458,7 +2507,6 @@ void MacroAssembler::InvokeFunction(Register function,
DCHECK(function.is(x1));
Register expected_reg = x2;
Register code_reg = x4;
Ldr(cp, FieldMemOperand(function, JSFunction::kContextOffset));
// The number of arguments is stored as an int32_t, and -1 is a marker
@ -2469,11 +2517,10 @@ void MacroAssembler::InvokeFunction(Register function,
Ldrsw(expected_reg,
FieldMemOperand(expected_reg,
SharedFunctionInfo::kFormalParameterCountOffset));
Ldr(code_reg,
FieldMemOperand(function, JSFunction::kCodeEntryOffset));
ParameterCount expected(expected_reg);
InvokeCode(code_reg, new_target, expected, actual, flag, call_wrapper);
InvokeFunctionCode(function, new_target, expected, actual, flag,
call_wrapper);
}
@ -2489,16 +2536,10 @@ void MacroAssembler::InvokeFunction(Register function,
// (See FullCodeGenerator::Generate().)
DCHECK(function.Is(x1));
Register code_reg = x4;
// Set up the context.
Ldr(cp, FieldMemOperand(function, JSFunction::kContextOffset));
// We call indirectly through the code field in the function to
// allow recompilation to take effect without changing any of the
// call sites.
Ldr(code_reg, FieldMemOperand(function, JSFunction::kCodeEntryOffset));
InvokeCode(code_reg, no_reg, expected, actual, flag, call_wrapper);
InvokeFunctionCode(function, no_reg, expected, actual, flag, call_wrapper);
}

View File

@ -1174,12 +1174,13 @@ class MacroAssembler : public Assembler {
InvokeFlag flag,
bool* definitely_mismatches,
const CallWrapper& call_wrapper);
void InvokeCode(Register code,
Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper);
void FloodFunctionIfStepping(Register fun, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual);
void InvokeFunctionCode(Register function, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual, InvokeFlag flag,
const CallWrapper& call_wrapper);
// Invoke the JavaScript function in the given register.
// Changes the current context to the context in the function before invoking.
void InvokeFunction(Register function,

View File

@ -1580,9 +1580,9 @@ ExternalReference ExternalReference::mod_two_doubles_operation(
}
ExternalReference ExternalReference::debug_step_in_fp_address(
ExternalReference ExternalReference::debug_last_step_action_address(
Isolate* isolate) {
return ExternalReference(isolate->debug()->step_in_fp_addr());
return ExternalReference(isolate->debug()->last_step_action_addr());
}

View File

@ -1004,7 +1004,7 @@ class ExternalReference BASE_EMBEDDED {
Address address() const { return reinterpret_cast<Address>(address_); }
// Used to check if single stepping is enabled in generated code.
static ExternalReference debug_step_in_fp_address(Isolate* isolate);
static ExternalReference debug_last_step_action_address(Isolate* isolate);
#ifndef V8_INTERPRETED_REGEXP
// C functions called from RegExp generated code.
@ -1154,8 +1154,11 @@ class CallWrapper {
virtual void BeforeCall(int call_size) const = 0;
// Called just after emitting a call, i.e., at the return site for the call.
virtual void AfterCall() const = 0;
// Return whether call needs to check for debug stepping.
virtual bool NeedsDebugStepCheck() const { return false; }
};
class NullCallWrapper : public CallWrapper {
public:
NullCallWrapper() { }
@ -1165,6 +1168,16 @@ class NullCallWrapper : public CallWrapper {
};
class CheckDebugStepCallWrapper : public CallWrapper {
public:
CheckDebugStepCallWrapper() {}
virtual ~CheckDebugStepCallWrapper() {}
virtual void BeforeCall(int call_size) const {}
virtual void AfterCall() const {}
virtual bool NeedsDebugStepCheck() const { return true; }
};
// -----------------------------------------------------------------------------
// Constant pool support

View File

@ -748,6 +748,13 @@ void Debug::ClearAllBreakPoints() {
void Debug::FloodWithOneShot(Handle<JSFunction> function,
BreakLocatorType type) {
if (!function->shared()->IsSubjectToDebugging()) {
// Builtin functions are not subject to stepping, but need to be
// deoptimized, because optimized code does not check for debug
// step in at call sites.
Deoptimizer::DeoptimizeFunction(*function);
return;
}
// Make sure the function is compiled and has set up the debug info.
Handle<SharedFunctionInfo> shared(function->shared());
if (!EnsureDebugInfo(shared, function)) {
@ -2298,6 +2305,9 @@ void Debug::HandleDebugBreak() {
isolate_->stack_guard()->ClearDebugBreak();
// Clear stepping to avoid duplicate breaks.
ClearStepping();
ProcessDebugMessages(debug_command_only);
}

View File

@ -31,7 +31,7 @@ class DebugScope;
// Step actions. NOTE: These values are in macros.py as well.
enum StepAction {
enum StepAction : int8_t {
StepNone = -1, // Stepping not prepared.
StepOut = 0, // Step out of the current function.
StepNext = 1, // Step to the next statement in the current function.
@ -524,8 +524,8 @@ class Debug {
return reinterpret_cast<Address>(address);
}
Address step_in_fp_addr() {
return reinterpret_cast<Address>(&thread_local_.step_into_fp_);
Address last_step_action_addr() {
return reinterpret_cast<Address>(&thread_local_.last_step_action_);
}
StepAction last_step_action() { return thread_local_.last_step_action_; }

View File

@ -341,7 +341,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ call(code, RelocInfo::CODE_TARGET);
} else {
ParameterCount actual(eax);
__ InvokeFunction(edi, edx, actual, CALL_FUNCTION, NullCallWrapper());
__ InvokeFunction(edi, edx, actual, CALL_FUNCTION,
CheckDebugStepCallWrapper());
}
// Store offset of return address for deoptimizer.
@ -1545,9 +1546,8 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
__ SmiUntag(ebx);
ParameterCount actual(eax);
ParameterCount expected(ebx);
__ InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset), no_reg,
expected, actual, JUMP_FUNCTION, NullCallWrapper());
__ InvokeFunctionCode(edi, no_reg, expected, actual, JUMP_FUNCTION,
CheckDebugStepCallWrapper());
// The function is a "classConstructor", need to raise an exception.
__ bind(&class_constructor);
{

View File

@ -1987,18 +1987,63 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
}
void MacroAssembler::InvokeCode(const Operand& code,
Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper) {
void MacroAssembler::FloodFunctionIfStepping(Register fun, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual) {
Label skip_flooding;
ExternalReference debug_step_action =
ExternalReference::debug_last_step_action_address(isolate());
cmpb(Operand::StaticVariable(debug_step_action), StepIn);
j(not_equal, &skip_flooding);
{
FrameScope frame(this,
has_frame() ? StackFrame::NONE : StackFrame::INTERNAL);
if (expected.is_reg()) {
SmiTag(expected.reg());
Push(expected.reg());
}
if (actual.is_reg()) {
SmiTag(actual.reg());
Push(actual.reg());
}
if (new_target.is_valid()) {
Push(new_target);
}
Push(fun);
Push(fun);
CallRuntime(Runtime::kDebugPrepareStepInIfStepping, 1);
Pop(fun);
if (new_target.is_valid()) {
Pop(new_target);
}
if (actual.is_reg()) {
Pop(actual.reg());
SmiUntag(actual.reg());
}
if (expected.is_reg()) {
Pop(expected.reg());
SmiUntag(expected.reg());
}
}
bind(&skip_flooding);
}
void MacroAssembler::InvokeFunctionCode(Register function, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper) {
// You can't call a function without a valid frame.
DCHECK(flag == JUMP_FUNCTION || has_frame());
// Ensure new target is passed in the correct register. Otherwise clear the
// appropriate register in case new target is not given.
DCHECK(function.is(edi));
DCHECK_IMPLIES(new_target.is_valid(), new_target.is(edx));
if (call_wrapper.NeedsDebugStepCheck()) {
FloodFunctionIfStepping(function, new_target, expected, actual);
}
// Clear the new.target register if not given.
if (!new_target.is_valid()) {
mov(edx, isolate()->factory()->undefined_value());
}
@ -2008,6 +2053,10 @@ void MacroAssembler::InvokeCode(const Operand& code,
InvokePrologue(expected, actual, &done, &definitely_mismatches, flag,
Label::kNear, call_wrapper);
if (!definitely_mismatches) {
// We call indirectly through the code field in the function to
// allow recompilation to take effect without changing any of the
// call sites.
Operand code = FieldOperand(function, JSFunction::kCodeEntryOffset);
if (flag == CALL_FUNCTION) {
call_wrapper.BeforeCall(CallSize(code));
call(code);
@ -2036,8 +2085,7 @@ void MacroAssembler::InvokeFunction(Register fun,
SmiUntag(ebx);
ParameterCount expected(ebx);
InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset), new_target,
expected, actual, flag, call_wrapper);
InvokeFunctionCode(edi, new_target, expected, actual, flag, call_wrapper);
}
@ -2052,8 +2100,7 @@ void MacroAssembler::InvokeFunction(Register fun,
DCHECK(fun.is(edi));
mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset), no_reg,
expected, actual, flag, call_wrapper);
InvokeFunctionCode(edi, no_reg, expected, actual, flag, call_wrapper);
}
@ -2077,8 +2124,7 @@ void MacroAssembler::InvokeBuiltin(int native_context_index, InvokeFlag flag,
// parameter count to avoid emitting code to do the check.
ParameterCount expected(0);
GetBuiltinFunction(edi, native_context_index);
InvokeCode(FieldOperand(edi, JSFunction::kCodeEntryOffset), no_reg,
expected, expected, flag, call_wrapper);
InvokeFunctionCode(edi, no_reg, expected, expected, flag, call_wrapper);
}
@ -2091,16 +2137,6 @@ void MacroAssembler::GetBuiltinFunction(Register target,
}
void MacroAssembler::GetBuiltinEntry(Register target,
int native_context_index) {
DCHECK(!target.is(edi));
// Load the JavaScript builtin function from the builtins object.
GetBuiltinFunction(edi, native_context_index);
// Load the code entry point from the function into the target register.
mov(target, FieldOperand(edi, JSFunction::kCodeEntryOffset));
}
void MacroAssembler::LoadContext(Register dst, int context_chain_length) {
if (context_chain_length > 0) {
// Move up the chain of contexts to the context containing the slot.

View File

@ -41,25 +41,15 @@ enum PointersToHereCheck {
kPointersToHereAreAlwaysInteresting
};
enum RegisterValueType {
REGISTER_VALUE_IS_SMI,
REGISTER_VALUE_IS_INT32
};
enum RegisterValueType { REGISTER_VALUE_IS_SMI, REGISTER_VALUE_IS_INT32 };
#ifdef DEBUG
bool AreAliased(Register reg1,
Register reg2,
Register reg3 = no_reg,
Register reg4 = no_reg,
Register reg5 = no_reg,
Register reg6 = no_reg,
Register reg7 = no_reg,
bool AreAliased(Register reg1, Register reg2, Register reg3 = no_reg,
Register reg4 = no_reg, Register reg5 = no_reg,
Register reg6 = no_reg, Register reg7 = no_reg,
Register reg8 = no_reg);
#endif
// MacroAssembler implements a collection of frequently used macros.
class MacroAssembler: public Assembler {
public:
@ -106,64 +96,44 @@ class MacroAssembler: public Assembler {
// ---------------------------------------------------------------------------
// GC Support
enum RememberedSetFinalAction {
kReturnAtEnd,
kFallThroughAtEnd
};
enum RememberedSetFinalAction { kReturnAtEnd, kFallThroughAtEnd };
// Record in the remembered set the fact that we have a pointer to new space
// at the address pointed to by the addr register. Only works if addr is not
// in new space.
void RememberedSetHelper(Register object, // Used for debug code.
Register addr,
Register scratch,
Register addr, Register scratch,
SaveFPRegsMode save_fp,
RememberedSetFinalAction and_then);
void CheckPageFlag(Register object,
Register scratch,
int mask,
Condition cc,
void CheckPageFlag(Register object, Register scratch, int mask, Condition cc,
Label* condition_met,
Label::Distance condition_met_distance = Label::kFar);
void CheckPageFlagForMap(
Handle<Map> map,
int mask,
Condition cc,
Label* condition_met,
Handle<Map> map, int mask, Condition cc, Label* condition_met,
Label::Distance condition_met_distance = Label::kFar);
// Check if object is in new space. Jumps if the object is not in new space.
// The register scratch can be object itself, but scratch will be clobbered.
void JumpIfNotInNewSpace(Register object,
Register scratch,
Label* branch,
void JumpIfNotInNewSpace(Register object, Register scratch, Label* branch,
Label::Distance distance = Label::kFar) {
InNewSpace(object, scratch, zero, branch, distance);
}
// Check if object is in new space. Jumps if the object is in new space.
// The register scratch can be object itself, but it will be clobbered.
void JumpIfInNewSpace(Register object,
Register scratch,
Label* branch,
void JumpIfInNewSpace(Register object, Register scratch, Label* branch,
Label::Distance distance = Label::kFar) {
InNewSpace(object, scratch, not_zero, branch, distance);
}
// Check if an object has a given incremental marking color. Also uses ecx!
void HasColor(Register object,
Register scratch0,
Register scratch1,
Label* has_color,
Label::Distance has_color_distance,
int first_bit,
int second_bit);
void HasColor(Register object, Register scratch0, Register scratch1,
Label* has_color, Label::Distance has_color_distance,
int first_bit, int second_bit);
void JumpIfBlack(Register object,
Register scratch0,
Register scratch1,
void JumpIfBlack(Register object, Register scratch0, Register scratch1,
Label* on_black,
Label::Distance on_black_distance = Label::kFar);
@ -172,9 +142,7 @@ class MacroAssembler: public Assembler {
// we can determine that it doesn't need to be scanned, then we just mark it
// black and fall through. For the rest we jump to the label so the
// incremental marker can fix its assumptions.
void EnsureNotWhite(Register object,
Register scratch1,
Register scratch2,
void EnsureNotWhite(Register object, Register scratch1, Register scratch2,
Label* object_is_white_and_not_data,
Label::Distance distance);
@ -184,10 +152,7 @@ class MacroAssembler: public Assembler {
// The offset is the offset from the start of the object, not the offset from
// the tagged HeapObject pointer. For use with FieldOperand(reg, off).
void RecordWriteField(
Register object,
int offset,
Register value,
Register scratch,
Register object, int offset, Register value, Register scratch,
SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
SmiCheck smi_check = INLINE_SMI_CHECK,
@ -197,22 +162,14 @@ class MacroAssembler: public Assembler {
// As above, but the offset has the tag presubtracted. For use with
// Operand(reg, off).
void RecordWriteContextSlot(
Register context,
int offset,
Register value,
Register scratch,
Register context, int offset, Register value, Register scratch,
SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
SmiCheck smi_check = INLINE_SMI_CHECK,
PointersToHereCheck pointers_to_here_check_for_value =
kPointersToHereMaybeInteresting) {
RecordWriteField(context,
offset + kHeapObjectTag,
value,
scratch,
save_fp,
remembered_set_action,
smi_check,
RecordWriteField(context, offset + kHeapObjectTag, value, scratch, save_fp,
remembered_set_action, smi_check,
pointers_to_here_check_for_value);
}
@ -223,10 +180,7 @@ class MacroAssembler: public Assembler {
// filters out smis so it does not update the write barrier if the
// value is a smi.
void RecordWriteArray(
Register array,
Register value,
Register index,
SaveFPRegsMode save_fp,
Register array, Register value, Register index, SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
SmiCheck smi_check = INLINE_SMI_CHECK,
PointersToHereCheck pointers_to_here_check_for_value =
@ -238,10 +192,7 @@ class MacroAssembler: public Assembler {
// operation. RecordWrite filters out smis so it does not update the
// write barrier if the value is a smi.
void RecordWrite(
Register object,
Register address,
Register value,
SaveFPRegsMode save_fp,
Register object, Register address, Register value, SaveFPRegsMode save_fp,
RememberedSetAction remembered_set_action = EMIT_REMEMBERED_SET,
SmiCheck smi_check = INLINE_SMI_CHECK,
PointersToHereCheck pointers_to_here_check_for_value =
@ -250,12 +201,8 @@ class MacroAssembler: public Assembler {
// For page containing |object| mark the region covering the object's map
// dirty. |object| is the object being stored into, |map| is the Map object
// that was stored.
void RecordWriteForMap(
Register object,
Handle<Map> map,
Register scratch1,
Register scratch2,
SaveFPRegsMode save_fp);
void RecordWriteForMap(Register object, Handle<Map> map, Register scratch1,
Register scratch2, SaveFPRegsMode save_fp);
// ---------------------------------------------------------------------------
// Debugger Support
@ -293,12 +240,11 @@ class MacroAssembler: public Assembler {
// transitioned_kind from the native context if the map in register
// map_in_out is the cached Array map in the native context of
// expected_kind.
void LoadTransitionedArrayMapConditional(
ElementsKind expected_kind,
ElementsKind transitioned_kind,
Register map_in_out,
Register scratch,
Label* no_map_match);
void LoadTransitionedArrayMapConditional(ElementsKind expected_kind,
ElementsKind transitioned_kind,
Register map_in_out,
Register scratch,
Label* no_map_match);
// Load the global function with the given index.
void LoadGlobalFunction(int index, Register function);
@ -351,39 +297,25 @@ class MacroAssembler: public Assembler {
// JavaScript invokes
// Invoke the JavaScript function code by either calling or jumping.
void InvokeCode(Register code,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper) {
InvokeCode(Operand(code), no_reg, expected, actual, flag, call_wrapper);
}
void InvokeCode(const Operand& code,
Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper);
void InvokeFunctionCode(Register function, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual, InvokeFlag flag,
const CallWrapper& call_wrapper);
// Invoke the JavaScript function in the given register. Changes the
// current context to the context in the function before invoking.
void InvokeFunction(Register function,
Register new_target,
const ParameterCount& actual,
InvokeFlag flag,
void InvokeFunction(Register function, Register new_target,
const ParameterCount& actual, InvokeFlag flag,
const CallWrapper& call_wrapper);
void InvokeFunction(Register function,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
void InvokeFunction(Register function, const ParameterCount& expected,
const ParameterCount& actual, InvokeFlag flag,
const CallWrapper& call_wrapper);
void InvokeFunction(Handle<JSFunction> function,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const ParameterCount& actual, InvokeFlag flag,
const CallWrapper& call_wrapper);
// Invoke specified builtin JavaScript function.
@ -393,9 +325,6 @@ class MacroAssembler: public Assembler {
// Store the function for the given builtin in the target register.
void GetBuiltinFunction(Register target, int native_context_index);
// Store the code object for the given builtin in the target register.
void GetBuiltinEntry(Register target, int native_context_index);
// Expression support
// cvtsi2sd instruction only writes to the low 64-bit of dst register, which
// hinders register renaming and makes dependence chains longer. So we use
@ -417,31 +346,25 @@ class MacroAssembler: public Assembler {
// Check if a map for a JSObject indicates that the object has fast elements.
// Jump to the specified label if it does not.
void CheckFastElements(Register map,
Label* fail,
void CheckFastElements(Register map, Label* fail,
Label::Distance distance = Label::kFar);
// Check if a map for a JSObject indicates that the object can have both smi
// and HeapObject elements. Jump to the specified label if it does not.
void CheckFastObjectElements(Register map,
Label* fail,
void CheckFastObjectElements(Register map, Label* fail,
Label::Distance distance = Label::kFar);
// Check if a map for a JSObject indicates that the object has fast smi only
// elements. Jump to the specified label if it does not.
void CheckFastSmiElements(Register map,
Label* fail,
void CheckFastSmiElements(Register map, Label* fail,
Label::Distance distance = Label::kFar);
// Check to see if maybe_number can be stored as a double in
// FastDoubleElements. If it can, store it at the index specified by key in
// the FastDoubleElements array elements, otherwise jump to fail.
void StoreNumberToDoubleElements(Register maybe_number,
Register elements,
Register key,
Register scratch1,
XMMRegister scratch2,
Label* fail,
void StoreNumberToDoubleElements(Register maybe_number, Register elements,
Register key, Register scratch1,
XMMRegister scratch2, Label* fail,
int offset = 0);
// Compare an object's map with the specified map.
@ -451,9 +374,7 @@ class MacroAssembler: public Assembler {
// label if not. Skip the smi check if not required (object is known to be a
// heap object). If mode is ALLOW_ELEMENT_TRANSITION_MAPS, then also match
// against maps that are ElementsKind transition maps of the specified map.
void CheckMap(Register obj,
Handle<Map> map,
Label* fail,
void CheckMap(Register obj, Handle<Map> map, Label* fail,
SmiCheckType smi_check_type);
// Check if the map of an object is equal to a specified weak map and branch
@ -468,8 +389,7 @@ class MacroAssembler: public Assembler {
// contains the instance_type. The registers map and instance_type can be the
// same in which case it contains the instance type afterwards. Either of the
// registers map and instance_type can be the same as heap_object.
Condition IsObjectStringType(Register heap_object,
Register map,
Condition IsObjectStringType(Register heap_object, Register map,
Register instance_type);
// Check if the object in register heap_object is a name. Afterwards the
@ -477,8 +397,7 @@ class MacroAssembler: public Assembler {
// contains the instance_type. The registers map and instance_type can be the
// same in which case it contains the instance type afterwards. Either of the
// registers map and instance_type can be the same as heap_object.
Condition IsObjectNameType(Register heap_object,
Register map,
Condition IsObjectNameType(Register heap_object, Register map,
Register instance_type);
// FCmp is similar to integer cmp, but requires unsigned
@ -487,8 +406,7 @@ class MacroAssembler: public Assembler {
void ClampUint8(Register reg);
void ClampDoubleToUint8(XMMRegister input_reg,
XMMRegister scratch_reg,
void ClampDoubleToUint8(XMMRegister input_reg, XMMRegister scratch_reg,
Register result_reg);
void SlowTruncateToI(Register result_reg, Register input_reg,
@ -526,22 +444,19 @@ class MacroAssembler: public Assembler {
void LoadUint32(XMMRegister dst, const Operand& src);
// Jump the register contains a smi.
inline void JumpIfSmi(Register value,
Label* smi_label,
inline void JumpIfSmi(Register value, Label* smi_label,
Label::Distance distance = Label::kFar) {
test(value, Immediate(kSmiTagMask));
j(zero, smi_label, distance);
}
// Jump if the operand is a smi.
inline void JumpIfSmi(Operand value,
Label* smi_label,
inline void JumpIfSmi(Operand value, Label* smi_label,
Label::Distance distance = Label::kFar) {
test(value, Immediate(kSmiTagMask));
j(zero, smi_label, distance);
}
// Jump if register contain a non-smi.
inline void JumpIfNotSmi(Register value,
Label* not_smi_label,
inline void JumpIfNotSmi(Register value, Label* not_smi_label,
Label::Distance distance = Label::kFar) {
test(value, Immediate(kSmiTagMask));
j(not_zero, not_smi_label, distance);
@ -616,22 +531,15 @@ class MacroAssembler: public Assembler {
// Generate code for checking access rights - used for security checks
// on access to global objects across environments. The holder register
// is left untouched, but the scratch register is clobbered.
void CheckAccessGlobalProxy(Register holder_reg,
Register scratch1,
Register scratch2,
Label* miss);
void CheckAccessGlobalProxy(Register holder_reg, Register scratch1,
Register scratch2, Label* miss);
void GetNumberHash(Register r0, Register scratch);
void LoadFromNumberDictionary(Label* miss,
Register elements,
Register key,
Register r0,
Register r1,
Register r2,
void LoadFromNumberDictionary(Label* miss, Register elements, Register key,
Register r0, Register r1, Register r2,
Register result);
// ---------------------------------------------------------------------------
// Allocation support
@ -645,48 +553,29 @@ class MacroAssembler: public Assembler {
// result is known to be the allocation top on entry (could be result_end
// from a previous call). If result_contains_top_on_entry is true scratch
// should be no_reg as it is never used.
void Allocate(int object_size,
Register result,
Register result_end,
Register scratch,
Label* gc_required,
AllocationFlags flags);
void Allocate(int object_size, Register result, Register result_end,
Register scratch, Label* gc_required, AllocationFlags flags);
void Allocate(int header_size,
ScaleFactor element_size,
Register element_count,
RegisterValueType element_count_type,
Register result,
Register result_end,
Register scratch,
Label* gc_required,
AllocationFlags flags);
void Allocate(int header_size, ScaleFactor element_size,
Register element_count, RegisterValueType element_count_type,
Register result, Register result_end, Register scratch,
Label* gc_required, AllocationFlags flags);
void Allocate(Register object_size,
Register result,
Register result_end,
Register scratch,
Label* gc_required,
AllocationFlags flags);
void Allocate(Register object_size, Register result, Register result_end,
Register scratch, Label* gc_required, AllocationFlags flags);
// Allocate a heap number in new space with undefined value. The
// register scratch2 can be passed as no_reg; the others must be
// valid registers. Returns tagged pointer in result register, or
// jumps to gc_required if new space is full.
void AllocateHeapNumber(Register result,
Register scratch1,
Register scratch2,
Label* gc_required,
MutableMode mode = IMMUTABLE);
void AllocateHeapNumber(Register result, Register scratch1, Register scratch2,
Label* gc_required, MutableMode mode = IMMUTABLE);
// Allocate a sequential string. All the header fields of the string object
// are initialized.
void AllocateTwoByteString(Register result,
Register length,
Register scratch1,
Register scratch2,
Register scratch3,
Label* gc_required);
void AllocateTwoByteString(Register result, Register length,
Register scratch1, Register scratch2,
Register scratch3, Label* gc_required);
void AllocateOneByteString(Register result, Register length,
Register scratch1, Register scratch2,
Register scratch3, Label* gc_required);
@ -695,28 +584,22 @@ class MacroAssembler: public Assembler {
// Allocate a raw cons string object. Only the map field of the result is
// initialized.
void AllocateTwoByteConsString(Register result,
Register scratch1,
Register scratch2,
Label* gc_required);
void AllocateTwoByteConsString(Register result, Register scratch1,
Register scratch2, Label* gc_required);
void AllocateOneByteConsString(Register result, Register scratch1,
Register scratch2, Label* gc_required);
// Allocate a raw sliced string object. Only the map field of the result is
// initialized.
void AllocateTwoByteSlicedString(Register result,
Register scratch1,
Register scratch2,
Label* gc_required);
void AllocateTwoByteSlicedString(Register result, Register scratch1,
Register scratch2, Label* gc_required);
void AllocateOneByteSlicedString(Register result, Register scratch1,
Register scratch2, Label* gc_required);
// Copy memory, byte-by-byte, from source to destination. Not optimized for
// long or aligned copies.
// The contents of index and scratch are destroyed.
void CopyBytes(Register source,
Register destination,
Register length,
void CopyBytes(Register source, Register destination, Register length,
Register scratch);
// Initialize fields with filler values. Fields starting at |current_address|
@ -770,8 +653,7 @@ class MacroAssembler: public Assembler {
void StubReturn(int argc);
// Call a runtime routine.
void CallRuntime(const Runtime::Function* f,
int num_arguments,
void CallRuntime(const Runtime::Function* f, int num_arguments,
SaveFPRegsMode save_doubles = kDontSaveFPRegs);
void CallRuntimeSaveDoubles(Runtime::FunctionId id) {
const Runtime::Function* function = Runtime::FunctionForId(id);
@ -779,8 +661,7 @@ class MacroAssembler: public Assembler {
}
// Convenience function: Same as above, but takes the fid instead.
void CallRuntime(Runtime::FunctionId id,
int num_arguments,
void CallRuntime(Runtime::FunctionId id, int num_arguments,
SaveFPRegsMode save_doubles = kDontSaveFPRegs) {
CallRuntime(Runtime::FunctionForId(id), num_arguments, save_doubles);
}
@ -792,12 +673,10 @@ class MacroAssembler: public Assembler {
// Like JumpToExternalReference, but also takes care of passing the number
// of parameters.
void TailCallExternalReference(const ExternalReference& ext,
int num_arguments,
int result_size);
int num_arguments, int result_size);
// Convenience function: tail call a runtime routine (jump).
void TailCallRuntime(Runtime::FunctionId fid,
int num_arguments,
void TailCallRuntime(Runtime::FunctionId fid, int num_arguments,
int result_size);
// Before calling a C-function from generated code, align arguments on stack.
@ -900,7 +779,6 @@ class MacroAssembler: public Assembler {
void IncrementCounter(Condition cc, StatsCounter* counter, int value);
void DecrementCounter(Condition cc, StatsCounter* counter, int value);
// ---------------------------------------------------------------------------
// Debugging
@ -951,10 +829,8 @@ class MacroAssembler: public Assembler {
void JumpIfNotUniqueNameInstanceType(Operand operand, Label* not_unique_name,
Label::Distance distance = Label::kFar);
void EmitSeqStringSetCharCheck(Register string,
Register index,
Register value,
uint32_t encoding_mask);
void EmitSeqStringSetCharCheck(Register string, Register index,
Register value, uint32_t encoding_mask);
static int SafepointRegisterStackIndex(Register reg) {
return SafepointRegisterStackIndex(reg.code());
@ -1004,31 +880,29 @@ class MacroAssembler: public Assembler {
// Helper functions for generating invokes.
void InvokePrologue(const ParameterCount& expected,
const ParameterCount& actual,
Label* done,
bool* definitely_mismatches,
InvokeFlag flag,
const ParameterCount& actual, Label* done,
bool* definitely_mismatches, InvokeFlag flag,
Label::Distance done_distance,
const CallWrapper& call_wrapper);
void FloodFunctionIfStepping(Register fun, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual);
void EnterExitFramePrologue();
void EnterExitFrameEpilogue(int argc, bool save_doubles);
void LeaveExitFrameEpilogue(bool restore_context);
// Allocation support helpers.
void LoadAllocationTopHelper(Register result,
Register scratch,
void LoadAllocationTopHelper(Register result, Register scratch,
AllocationFlags flags);
void UpdateAllocationTopHelper(Register result_end,
Register scratch,
void UpdateAllocationTopHelper(Register result_end, Register scratch,
AllocationFlags flags);
// Helper for implementing JumpIfNotInNewSpace and JumpIfInNewSpace.
void InNewSpace(Register object,
Register scratch,
Condition cc,
void InNewSpace(Register object, Register scratch, Condition cc,
Label* condition_met,
Label::Distance condition_met_distance = Label::kFar);
@ -1036,8 +910,7 @@ class MacroAssembler: public Assembler {
// bitmap register points at the word with the mark bits and the mask
// the position of the first bit. Uses ecx as scratch and leaves addr_reg
// unchanged.
inline void GetMarkBits(Register addr_reg,
Register bitmap_reg,
inline void GetMarkBits(Register addr_reg, Register bitmap_reg,
Register mask_reg);
// Compute memory operands for safepoint stack slots.
@ -1049,7 +922,6 @@ class MacroAssembler: public Assembler {
friend class StandardFrame;
};
// The code patcher is used to patch (typically) small parts of code e.g. for
// debugging and other types of instrumentation. When using the code patcher
// the exact number of bytes specified must be emitted. Is not legal to emit
@ -1064,12 +936,11 @@ class CodePatcher {
MacroAssembler* masm() { return &masm_; }
private:
byte* address_; // The address of the code being patched.
int size_; // Number of bytes of the expected patch size.
byte* address_; // The address of the code being patched.
int size_; // Number of bytes of the expected patch size.
MacroAssembler masm_; // Macro assembler used to generate the code.
};
// -----------------------------------------------------------------------------
// Static helper functions.
@ -1078,39 +949,30 @@ inline Operand FieldOperand(Register object, int offset) {
return Operand(object, offset - kHeapObjectTag);
}
// Generate an Operand for loading an indexed field from an object.
inline Operand FieldOperand(Register object,
Register index,
ScaleFactor scale,
inline Operand FieldOperand(Register object, Register index, ScaleFactor scale,
int offset) {
return Operand(object, index, scale, offset - kHeapObjectTag);
}
inline Operand FixedArrayElementOperand(Register array,
Register index_as_smi,
inline Operand FixedArrayElementOperand(Register array, Register index_as_smi,
int additional_offset = 0) {
int offset = FixedArray::kHeaderSize + additional_offset * kPointerSize;
return FieldOperand(array, index_as_smi, times_half_pointer_size, offset);
}
inline Operand ContextOperand(Register context, int index) {
return Operand(context, Context::SlotOffset(index));
}
inline Operand ContextOperand(Register context, Register index) {
return Operand(context, index, times_pointer_size, Context::SlotOffset(0));
}
inline Operand GlobalObjectOperand() {
return ContextOperand(esi, Context::GLOBAL_OBJECT_INDEX);
}
#ifdef GENERATED_CODE_COVERAGE
extern void LogGeneratedCodeCoverage(const char* file_line);
#define CODE_COVERAGE_STRINGIFY(x) #x
@ -1132,7 +994,6 @@ extern void LogGeneratedCodeCoverage(const char* file_line);
#define ACCESS_MASM(masm) masm->
#endif
} // namespace internal
} // namespace v8

View File

@ -40,7 +40,8 @@ void NamedLoadHandlerCompiler::GenerateLoadViaGetter(
ParameterCount actual(0);
ParameterCount expected(expected_arguments);
__ LoadAccessor(r1, holder, accessor_index, ACCESSOR_GETTER);
__ InvokeFunction(r1, expected, actual, CALL_FUNCTION, NullCallWrapper());
__ InvokeFunction(r1, expected, actual, CALL_FUNCTION,
CheckDebugStepCallWrapper());
} else {
// If we generate a global code snippet for deoptimization only, remember
// the place to continue after deoptimization.
@ -81,7 +82,8 @@ void NamedStoreHandlerCompiler::GenerateStoreViaSetter(
ParameterCount actual(1);
ParameterCount expected(expected_arguments);
__ LoadAccessor(r1, holder, accessor_index, ACCESSOR_SETTER);
__ InvokeFunction(r1, expected, actual, CALL_FUNCTION, NullCallWrapper());
__ InvokeFunction(r1, expected, actual, CALL_FUNCTION,
CheckDebugStepCallWrapper());
} else {
// If we generate a global code snippet for deoptimization only, remember
// the place to continue after deoptimization.

View File

@ -255,7 +255,8 @@ void NamedStoreHandlerCompiler::GenerateStoreViaSetter(
ParameterCount actual(1);
ParameterCount expected(expected_arguments);
__ LoadAccessor(x1, holder, accessor_index, ACCESSOR_SETTER);
__ InvokeFunction(x1, expected, actual, CALL_FUNCTION, NullCallWrapper());
__ InvokeFunction(x1, expected, actual, CALL_FUNCTION,
CheckDebugStepCallWrapper());
} else {
// If we generate a global code snippet for deoptimization only, remember
// the place to continue after deoptimization.
@ -292,7 +293,8 @@ void NamedLoadHandlerCompiler::GenerateLoadViaGetter(
ParameterCount actual(0);
ParameterCount expected(expected_arguments);
__ LoadAccessor(x1, holder, accessor_index, ACCESSOR_GETTER);
__ InvokeFunction(x1, expected, actual, CALL_FUNCTION, NullCallWrapper());
__ InvokeFunction(x1, expected, actual, CALL_FUNCTION,
CheckDebugStepCallWrapper());
} else {
// If we generate a global code snippet for deoptimization only, remember
// the place to continue after deoptimization.

View File

@ -36,7 +36,7 @@ void NamedLoadHandlerCompiler::GenerateLoadViaGetter(
ParameterCount expected(expected_arguments);
__ LoadAccessor(edi, holder, accessor_index, ACCESSOR_GETTER);
__ InvokeFunction(edi, expected, actual, CALL_FUNCTION,
NullCallWrapper());
CheckDebugStepCallWrapper());
} else {
// If we generate a global code snippet for deoptimization only, remember
// the place to continue after deoptimization.
@ -267,7 +267,7 @@ void NamedStoreHandlerCompiler::GenerateStoreViaSetter(
ParameterCount expected(expected_arguments);
__ LoadAccessor(edi, holder, accessor_index, ACCESSOR_SETTER);
__ InvokeFunction(edi, expected, actual, CALL_FUNCTION,
NullCallWrapper());
CheckDebugStepCallWrapper());
} else {
// If we generate a global code snippet for deoptimization only, remember
// the place to continue after deoptimization.

View File

@ -40,7 +40,8 @@ void NamedLoadHandlerCompiler::GenerateLoadViaGetter(
ParameterCount actual(0);
ParameterCount expected(expected_arguments);
__ LoadAccessor(a1, holder, accessor_index, ACCESSOR_GETTER);
__ InvokeFunction(a1, expected, actual, CALL_FUNCTION, NullCallWrapper());
__ InvokeFunction(a1, expected, actual, CALL_FUNCTION,
CheckDebugStepCallWrapper());
} else {
// If we generate a global code snippet for deoptimization only, remember
// the place to continue after deoptimization.
@ -81,7 +82,8 @@ void NamedStoreHandlerCompiler::GenerateStoreViaSetter(
ParameterCount actual(1);
ParameterCount expected(expected_arguments);
__ LoadAccessor(a1, holder, accessor_index, ACCESSOR_SETTER);
__ InvokeFunction(a1, expected, actual, CALL_FUNCTION, NullCallWrapper());
__ InvokeFunction(a1, expected, actual, CALL_FUNCTION,
CheckDebugStepCallWrapper());
} else {
// If we generate a global code snippet for deoptimization only, remember
// the place to continue after deoptimization.

View File

@ -40,7 +40,8 @@ void NamedLoadHandlerCompiler::GenerateLoadViaGetter(
ParameterCount actual(0);
ParameterCount expected(expected_arguments);
__ LoadAccessor(a1, holder, accessor_index, ACCESSOR_GETTER);
__ InvokeFunction(a1, expected, actual, CALL_FUNCTION, NullCallWrapper());
__ InvokeFunction(a1, expected, actual, CALL_FUNCTION,
CheckDebugStepCallWrapper());
} else {
// If we generate a global code snippet for deoptimization only, remember
// the place to continue after deoptimization.
@ -81,7 +82,8 @@ void NamedStoreHandlerCompiler::GenerateStoreViaSetter(
ParameterCount actual(1);
ParameterCount expected(expected_arguments);
__ LoadAccessor(a1, holder, accessor_index, ACCESSOR_SETTER);
__ InvokeFunction(a1, expected, actual, CALL_FUNCTION, NullCallWrapper());
__ InvokeFunction(a1, expected, actual, CALL_FUNCTION,
CheckDebugStepCallWrapper());
} else {
// If we generate a global code snippet for deoptimization only, remember
// the place to continue after deoptimization.

View File

@ -249,7 +249,7 @@ void NamedStoreHandlerCompiler::GenerateStoreViaSetter(
ParameterCount expected(expected_arguments);
__ LoadAccessor(rdi, holder, accessor_index, ACCESSOR_SETTER);
__ InvokeFunction(rdi, no_reg, expected, actual, CALL_FUNCTION,
NullCallWrapper());
CheckDebugStepCallWrapper());
} else {
// If we generate a global code snippet for deoptimization only, remember
// the place to continue after deoptimization.
@ -292,7 +292,7 @@ void NamedLoadHandlerCompiler::GenerateLoadViaGetter(
ParameterCount expected(expected_arguments);
__ LoadAccessor(rdi, holder, accessor_index, ACCESSOR_GETTER);
__ InvokeFunction(rdi, no_reg, expected, actual, CALL_FUNCTION,
NullCallWrapper());
CheckDebugStepCallWrapper());
} else {
// If we generate a global code snippet for deoptimization only, remember
// the place to continue after deoptimization.

View File

@ -1212,12 +1212,9 @@ function InnerArrayFilter(f, receiver, array, length) {
var accumulator = new InternalArray();
var accumulator_length = 0;
var is_array = IS_ARRAY(array);
var stepping = DEBUG_IS_STEPPING(f);
for (var i = 0; i < length; i++) {
if (HAS_INDEX(array, i, is_array)) {
var element = array[i];
// Prepare break slots for debugger step in.
if (stepping) %DebugPrepareStepInIfStepping(f);
if (%_Call(f, receiver, element, i, array)) {
accumulator[accumulator_length++] = element;
}
@ -1244,12 +1241,9 @@ function InnerArrayForEach(f, receiver, array, length) {
if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
var is_array = IS_ARRAY(array);
var stepping = DEBUG_IS_STEPPING(f);
for (var i = 0; i < length; i++) {
if (HAS_INDEX(array, i, is_array)) {
var element = array[i];
// Prepare break slots for debugger step in.
if (stepping) %DebugPrepareStepInIfStepping(f);
%_Call(f, receiver, element, i, array);
}
}
@ -1271,12 +1265,9 @@ function InnerArraySome(f, receiver, array, length) {
if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
var is_array = IS_ARRAY(array);
var stepping = DEBUG_IS_STEPPING(f);
for (var i = 0; i < length; i++) {
if (HAS_INDEX(array, i, is_array)) {
var element = array[i];
// Prepare break slots for debugger step in.
if (stepping) %DebugPrepareStepInIfStepping(f);
if (%_Call(f, receiver, element, i, array)) return true;
}
}
@ -1301,12 +1292,9 @@ function InnerArrayEvery(f, receiver, array, length) {
if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
var is_array = IS_ARRAY(array);
var stepping = DEBUG_IS_STEPPING(f);
for (var i = 0; i < length; i++) {
if (HAS_INDEX(array, i, is_array)) {
var element = array[i];
// Prepare break slots for debugger step in.
if (stepping) %DebugPrepareStepInIfStepping(f);
if (!%_Call(f, receiver, element, i, array)) return false;
}
}
@ -1329,12 +1317,9 @@ function InnerArrayMap(f, receiver, array, length) {
var accumulator = new InternalArray(length);
var is_array = IS_ARRAY(array);
var stepping = DEBUG_IS_STEPPING(f);
for (var i = 0; i < length; i++) {
if (HAS_INDEX(array, i, is_array)) {
var element = array[i];
// Prepare break slots for debugger step in.
if (stepping) %DebugPrepareStepInIfStepping(f);
accumulator[i] = %_Call(f, receiver, element, i, array);
}
}
@ -1497,12 +1482,9 @@ function InnerArrayReduce(callback, current, array, length, argumentsLength) {
throw MakeTypeError(kReduceNoInitial);
}
var stepping = DEBUG_IS_STEPPING(callback);
for (; i < length; i++) {
if (HAS_INDEX(array, i, is_array)) {
var element = array[i];
// Prepare break slots for debugger step in.
if (stepping) %DebugPrepareStepInIfStepping(callback);
current = callback(current, element, i, array);
}
}
@ -1540,12 +1522,9 @@ function InnerArrayReduceRight(callback, current, array, length,
throw MakeTypeError(kReduceNoInitial);
}
var stepping = DEBUG_IS_STEPPING(callback);
for (; i >= 0; i--) {
if (HAS_INDEX(array, i, is_array)) {
var element = array[i];
// Prepare break slots for debugger step in.
if (stepping) %DebugPrepareStepInIfStepping(callback);
current = callback(current, element, i, array);
}
}

View File

@ -248,10 +248,8 @@ function SetForEach(f, receiver) {
var iterator = new SetIterator(this, ITERATOR_KIND_VALUES);
var key;
var stepping = DEBUG_IS_STEPPING(f);
var value_array = [UNDEFINED];
while (%SetIteratorNext(iterator, value_array)) {
if (stepping) %DebugPrepareStepInIfStepping(f);
key = value_array[0];
%_Call(f, receiver, key, key, this);
}
@ -431,10 +429,8 @@ function MapForEach(f, receiver) {
if (!IS_CALLABLE(f)) throw MakeTypeError(kCalledNonCallable, f);
var iterator = new MapIterator(this, ITERATOR_KIND_ENTRIES);
var stepping = DEBUG_IS_STEPPING(f);
var value_array = [UNDEFINED, UNDEFINED];
while (%MapIteratorNext(iterator, value_array)) {
if (stepping) %DebugPrepareStepInIfStepping(f);
%_Call(f, receiver, value_array[1], value_array[0], this);
}
}

View File

@ -313,7 +313,6 @@ define NOT_FOUND = -1;
# Check whether debug is active.
define DEBUG_IS_ACTIVE = (%_DebugIsActive() != 0);
macro DEBUG_IS_STEPPING(function) = (%_DebugIsActive() != 0 && %DebugCallbackSupportsStepping(function));
macro DEBUG_PREPARE_STEP_IN_IF_STEPPING(function) = if (%_DebugIsActive() != 0) %DebugPrepareStepInIfStepping(function);
# SharedFlag equivalents

View File

@ -71,7 +71,7 @@ var GlobalPromise = function Promise(resolver) {
var promise = PromiseInit(%NewObject(GlobalPromise, new.target));
try {
%DebugPushPromise(promise, Promise, resolver);
%DebugPushPromise(promise, Promise);
var callbacks = CreateResolvingFunctions(promise);
resolver(callbacks.resolve, callbacks.reject);
} catch (e) {
@ -139,7 +139,7 @@ function PromiseCoerce(constructor, x) {
function PromiseHandle(value, handler, deferred) {
try {
%DebugPushPromise(deferred.promise, PromiseHandle, handler);
%DebugPushPromise(deferred.promise, PromiseHandle);
var result = handler(value);
if (result === deferred.promise)
throw MakeTypeError(kPromiseCyclic, result);
@ -340,12 +340,10 @@ function PromiseThen(onResolve, onReject) {
function(x) {
x = PromiseCoerce(constructor, x);
if (x === that) {
DEBUG_PREPARE_STEP_IN_IF_STEPPING(onReject);
return onReject(MakeTypeError(kPromiseCyclic, x));
} else if (IsPromise(x)) {
return x.then(onResolve, onReject);
} else {
DEBUG_PREPARE_STEP_IN_IF_STEPPING(onResolve);
return onResolve(x);
}
},

View File

@ -576,7 +576,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ Call(code, RelocInfo::CODE_TARGET);
} else {
ParameterCount actual(a0);
__ InvokeFunction(a1, a3, actual, CALL_FUNCTION, NullCallWrapper());
__ InvokeFunction(a1, a3, actual, CALL_FUNCTION,
CheckDebugStepCallWrapper());
}
// Store offset of return address for deoptimizer.
@ -1700,10 +1701,10 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
__ lw(a2,
FieldMemOperand(a2, SharedFunctionInfo::kFormalParameterCountOffset));
__ sra(a2, a2, kSmiTagSize); // Un-tag.
__ lw(t0, FieldMemOperand(a1, JSFunction::kCodeEntryOffset));
ParameterCount actual(a0);
ParameterCount expected(a2);
__ InvokeCode(t0, no_reg, expected, actual, JUMP_FUNCTION, NullCallWrapper());
__ InvokeFunctionCode(a1, no_reg, expected, actual, JUMP_FUNCTION,
CheckDebugStepCallWrapper());
// The function is a "classConstructor", need to raise an exception.
__ bind(&class_constructor);

View File

@ -4010,18 +4010,64 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
}
void MacroAssembler::InvokeCode(Register code,
Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper) {
void MacroAssembler::FloodFunctionIfStepping(Register fun, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual) {
Label skip_flooding;
ExternalReference debug_step_action =
ExternalReference::debug_last_step_action_address(isolate());
li(t0, Operand(debug_step_action));
lb(t0, MemOperand(t0));
Branch(&skip_flooding, ne, t0, Operand(StepIn));
{
FrameScope frame(this,
has_frame() ? StackFrame::NONE : StackFrame::INTERNAL);
if (expected.is_reg()) {
SmiTag(expected.reg());
Push(expected.reg());
}
if (actual.is_reg()) {
SmiTag(actual.reg());
Push(actual.reg());
}
if (new_target.is_valid()) {
Push(new_target);
}
Push(fun);
Push(fun);
CallRuntime(Runtime::kDebugPrepareStepInIfStepping, 1);
Pop(fun);
if (new_target.is_valid()) {
Pop(new_target);
}
if (actual.is_reg()) {
Pop(actual.reg());
SmiUntag(actual.reg());
}
if (expected.is_reg()) {
Pop(expected.reg());
SmiUntag(expected.reg());
}
}
bind(&skip_flooding);
}
void MacroAssembler::InvokeFunctionCode(Register function, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper) {
// You can't call a function without a valid frame.
DCHECK(flag == JUMP_FUNCTION || has_frame());
// Ensure new target is passed in the correct register. Otherwise clear the
// appropriate register in case new target is not given.
DCHECK(function.is(a1));
DCHECK_IMPLIES(new_target.is_valid(), new_target.is(a3));
if (call_wrapper.NeedsDebugStepCheck()) {
FloodFunctionIfStepping(function, new_target, expected, actual);
}
// Clear the new.target register if not given.
if (!new_target.is_valid()) {
LoadRoot(a3, Heap::kUndefinedValueRootIndex);
}
@ -4031,6 +4077,11 @@ void MacroAssembler::InvokeCode(Register code,
InvokePrologue(expected, actual, &done, &definitely_mismatches, flag,
call_wrapper);
if (!definitely_mismatches) {
// We call indirectly through the code field in the function to
// allow recompilation to take effect without changing any of the
// call sites.
Register code = t0;
lw(code, FieldMemOperand(function, JSFunction::kCodeEntryOffset));
if (flag == CALL_FUNCTION) {
call_wrapper.BeforeCall(CallSize(code));
Call(code);
@ -4057,18 +4108,18 @@ void MacroAssembler::InvokeFunction(Register function,
// Contract with called JS functions requires that function is passed in a1.
DCHECK(function.is(a1));
Register expected_reg = a2;
Register code_reg = t0;
Register temp_reg = t0;
lw(code_reg, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
lw(temp_reg, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
lw(expected_reg,
FieldMemOperand(code_reg,
SharedFunctionInfo::kFormalParameterCountOffset));
FieldMemOperand(temp_reg,
SharedFunctionInfo::kFormalParameterCountOffset));
sra(expected_reg, expected_reg, kSmiTagSize);
lw(code_reg, FieldMemOperand(a1, JSFunction::kCodeEntryOffset));
ParameterCount expected(expected_reg);
InvokeCode(code_reg, new_target, expected, actual, flag, call_wrapper);
InvokeFunctionCode(function, new_target, expected, actual, flag,
call_wrapper);
}
@ -4086,11 +4137,7 @@ void MacroAssembler::InvokeFunction(Register function,
// Get the function and setup the context.
lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
// We call indirectly through the code field in the function to
// allow recompilation to take effect without changing any of the
// call sites.
lw(t0, FieldMemOperand(a1, JSFunction::kCodeEntryOffset));
InvokeCode(t0, no_reg, expected, actual, flag, call_wrapper);
InvokeFunctionCode(a1, no_reg, expected, actual, flag, call_wrapper);
}

View File

@ -967,12 +967,11 @@ class MacroAssembler: public Assembler {
// JavaScript invokes.
// Invoke the JavaScript function code by either calling or jumping.
void InvokeCode(Register code,
Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper);
void InvokeFunctionCode(Register function, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual, InvokeFlag flag,
const CallWrapper& call_wrapper);
// Invoke the JavaScript function in the given register. Changes the
// current context to the context in the function before invoking.
@ -1671,6 +1670,10 @@ const Operand& rt = Operand(zero_reg), BranchDelaySlot bd = PROTECT
InvokeFlag flag,
const CallWrapper& call_wrapper);
void FloodFunctionIfStepping(Register fun, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual);
void InitializeNewString(Register string,
Register length,
Heap::RootListIndex map_index,

View File

@ -572,7 +572,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ Call(code, RelocInfo::CODE_TARGET);
} else {
ParameterCount actual(a0);
__ InvokeFunction(a1, a3, actual, CALL_FUNCTION, NullCallWrapper());
__ InvokeFunction(a1, a3, actual, CALL_FUNCTION,
CheckDebugStepCallWrapper());
}
// Store offset of return address for deoptimizer.
@ -1694,10 +1695,10 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
__ lw(a2,
FieldMemOperand(a2, SharedFunctionInfo::kFormalParameterCountOffset));
__ ld(t0, FieldMemOperand(a1, JSFunction::kCodeEntryOffset));
ParameterCount actual(a0);
ParameterCount expected(a2);
__ InvokeCode(t0, no_reg, expected, actual, JUMP_FUNCTION, NullCallWrapper());
__ InvokeFunctionCode(a1, no_reg, expected, actual, JUMP_FUNCTION,
CheckDebugStepCallWrapper());
// The function is a "classConstructor", need to raise an exception.
__ bind(&class_constructor);

View File

@ -4222,18 +4222,64 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
}
void MacroAssembler::InvokeCode(Register code,
Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper) {
void MacroAssembler::FloodFunctionIfStepping(Register fun, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual) {
Label skip_flooding;
ExternalReference debug_step_action =
ExternalReference::debug_last_step_action_address(isolate());
li(t0, Operand(debug_step_action));
lb(t0, MemOperand(t0));
Branch(&skip_flooding, ne, t0, Operand(StepIn));
{
FrameScope frame(this,
has_frame() ? StackFrame::NONE : StackFrame::INTERNAL);
if (expected.is_reg()) {
SmiTag(expected.reg());
Push(expected.reg());
}
if (actual.is_reg()) {
SmiTag(actual.reg());
Push(actual.reg());
}
if (new_target.is_valid()) {
Push(new_target);
}
Push(fun);
Push(fun);
CallRuntime(Runtime::kDebugPrepareStepInIfStepping, 1);
Pop(fun);
if (new_target.is_valid()) {
Pop(new_target);
}
if (actual.is_reg()) {
Pop(actual.reg());
SmiUntag(actual.reg());
}
if (expected.is_reg()) {
Pop(expected.reg());
SmiUntag(expected.reg());
}
}
bind(&skip_flooding);
}
void MacroAssembler::InvokeFunctionCode(Register function, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper) {
// You can't call a function without a valid frame.
DCHECK(flag == JUMP_FUNCTION || has_frame());
// Ensure new target is passed in the correct register. Otherwise clear the
// appropriate register in case new target is not given.
DCHECK(function.is(a1));
DCHECK_IMPLIES(new_target.is_valid(), new_target.is(a3));
if (call_wrapper.NeedsDebugStepCheck()) {
FloodFunctionIfStepping(function, new_target, expected, actual);
}
// Clear the new.target register if not given.
if (!new_target.is_valid()) {
LoadRoot(a3, Heap::kUndefinedValueRootIndex);
}
@ -4243,6 +4289,11 @@ void MacroAssembler::InvokeCode(Register code,
InvokePrologue(expected, actual, &done, &definitely_mismatches, flag,
call_wrapper);
if (!definitely_mismatches) {
// We call indirectly through the code field in the function to
// allow recompilation to take effect without changing any of the
// call sites.
Register code = t0;
ld(code, FieldMemOperand(function, JSFunction::kCodeEntryOffset));
if (flag == CALL_FUNCTION) {
call_wrapper.BeforeCall(CallSize(code));
Call(code);
@ -4269,17 +4320,16 @@ void MacroAssembler::InvokeFunction(Register function,
// Contract with called JS functions requires that function is passed in a1.
DCHECK(function.is(a1));
Register expected_reg = a2;
Register code_reg = t0;
ld(code_reg, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
Register temp_reg = t0;
ld(temp_reg, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
ld(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
// The argument count is stored as int32_t on 64-bit platforms.
// TODO(plind): Smi on 32-bit platforms.
lw(expected_reg,
FieldMemOperand(code_reg,
SharedFunctionInfo::kFormalParameterCountOffset));
ld(code_reg, FieldMemOperand(a1, JSFunction::kCodeEntryOffset));
FieldMemOperand(temp_reg,
SharedFunctionInfo::kFormalParameterCountOffset));
ParameterCount expected(expected_reg);
InvokeCode(code_reg, new_target, expected, actual, flag, call_wrapper);
InvokeFunctionCode(a1, new_target, expected, actual, flag, call_wrapper);
}
@ -4297,11 +4347,7 @@ void MacroAssembler::InvokeFunction(Register function,
// Get the function and setup the context.
ld(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
// We call indirectly through the code field in the function to
// allow recompilation to take effect without changing any of the
// call sites.
ld(t0, FieldMemOperand(a1, JSFunction::kCodeEntryOffset));
InvokeCode(t0, no_reg, expected, actual, flag, call_wrapper);
InvokeFunctionCode(a1, no_reg, expected, actual, flag, call_wrapper);
}

View File

@ -1017,12 +1017,10 @@ class MacroAssembler: public Assembler {
// JavaScript invokes.
// Invoke the JavaScript function code by either calling or jumping.
void InvokeCode(Register code,
Register new_code,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper);
void InvokeFunctionCode(Register function, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual, InvokeFlag flag,
const CallWrapper& call_wrapper);
// Invoke the JavaScript function in the given register. Changes the
// current context to the context in the function before invoking.
@ -1762,6 +1760,10 @@ const Operand& rt = Operand(zero_reg), BranchDelaySlot bd = PROTECT
InvokeFlag flag,
const CallWrapper& call_wrapper);
void FloodFunctionIfStepping(Register fun, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual);
void InitializeNewString(Register string,
Register length,
Heap::RootListIndex map_index,

View File

@ -1633,50 +1633,15 @@ bool DebugStepInIsActive(Debug* debug) {
}
// Check whether debugger is about to step into the callback that is passed
// to a built-in function such as Array.forEach. This check is done before
// %DebugPrepareStepInIfStepping and is not strictly necessary. However, if it
// returns false, we can skip %DebugPrepareStepInIfStepping, useful in loops.
RUNTIME_FUNCTION(Runtime_DebugCallbackSupportsStepping) {
SealHandleScope shs(isolate);
DCHECK(args.length() == 1);
if (!DebugStepInIsActive(isolate->debug())) {
return isolate->heap()->false_value();
}
CONVERT_ARG_CHECKED(Object, object, 0);
RUNTIME_ASSERT(object->IsJSFunction() || object->IsJSGeneratorObject());
// We do not step into the callback if it's a builtin other than a bound,
// or not even a function.
JSFunction* fun;
if (object->IsJSFunction()) {
fun = JSFunction::cast(object);
} else {
fun = JSGeneratorObject::cast(object)->function();
}
return isolate->heap()->ToBoolean(fun->shared()->IsSubjectToDebugging() ||
fun->shared()->bound());
}
void FloodDebugSubjectWithOneShot(Debug* debug, Handle<JSFunction> function) {
if (function->shared()->IsSubjectToDebugging() ||
function->shared()->bound()) {
// When leaving the function, step out has been activated, but not performed
// if we do not leave the builtin. To be able to step into the function
// again, we need to clear the step out at this point.
debug->ClearStepOut();
debug->FloodWithOneShotGeneric(function);
}
}
// Set one shot breakpoints for the callback function that is passed to a
// built-in function such as Array.forEach to enable stepping into the callback,
// if we are indeed stepping and the callback is subject to debugging.
RUNTIME_FUNCTION(Runtime_DebugPrepareStepInIfStepping) {
DCHECK(args.length() == 1);
Debug* debug = isolate->debug();
if (!DebugStepInIsActive(debug)) return isolate->heap()->undefined_value();
if (debug->in_debug_scope() || !DebugStepInIsActive(debug)) {
return isolate->heap()->undefined_value();
}
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
@ -1689,22 +1654,18 @@ RUNTIME_FUNCTION(Runtime_DebugPrepareStepInIfStepping) {
Handle<JSGeneratorObject>::cast(object)->function(), isolate);
}
FloodDebugSubjectWithOneShot(debug, fun);
debug->ClearStepOut();
debug->FloodWithOneShotGeneric(fun);
return isolate->heap()->undefined_value();
}
RUNTIME_FUNCTION(Runtime_DebugPushPromise) {
DCHECK(args.length() == 3);
DCHECK(args.length() == 2);
HandleScope scope(isolate);
CONVERT_ARG_HANDLE_CHECKED(JSObject, promise, 0);
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, handler, 2);
isolate->PushPromise(promise, function);
Debug* debug = isolate->debug();
if (handler->IsJSFunction() && DebugStepInIsActive(debug)) {
FloodDebugSubjectWithOneShot(debug, Handle<JSFunction>::cast(handler));
}
return isolate->heap()->undefined_value();
}

View File

@ -200,9 +200,8 @@ namespace internal {
F(CollectGarbage, 1, 1) \
F(GetHeapUsage, 0, 1) \
F(GetScript, 1, 1) \
F(DebugCallbackSupportsStepping, 1, 1) \
F(DebugPrepareStepInIfStepping, 1, 1) \
F(DebugPushPromise, 3, 1) \
F(DebugPushPromise, 2, 1) \
F(DebugPopPromise, 0, 1) \
F(DebugPromiseEvent, 1, 1) \
F(DebugAsyncTaskEvent, 1, 1) \

View File

@ -60,8 +60,8 @@ ExternalReferenceTable::ExternalReferenceTable(Isolate* isolate) {
"Heap::NewSpaceAllocationLimitAddress()");
Add(ExternalReference::new_space_allocation_top_address(isolate).address(),
"Heap::NewSpaceAllocationTopAddress()");
Add(ExternalReference::debug_step_in_fp_address(isolate).address(),
"Debug::step_in_fp_addr()");
Add(ExternalReference::debug_last_step_action_address(isolate).address(),
"Debug::last_step_action_addr()");
Add(ExternalReference::mod_two_doubles_operation(isolate).address(),
"mod_two_doubles");
// Keyed lookup cache.

View File

@ -332,7 +332,8 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
__ Call(code, RelocInfo::CODE_TARGET);
} else {
ParameterCount actual(rax);
__ InvokeFunction(rdi, rdx, actual, CALL_FUNCTION, NullCallWrapper());
__ InvokeFunction(rdi, rdx, actual, CALL_FUNCTION,
CheckDebugStepCallWrapper());
}
// Store offset of return address for deoptimizer.
@ -1735,10 +1736,11 @@ void Builtins::Generate_CallFunction(MacroAssembler* masm,
__ LoadSharedFunctionInfoSpecialField(
rbx, rdx, SharedFunctionInfo::kFormalParameterCountOffset);
__ movp(r8, FieldOperand(rdi, JSFunction::kCodeEntryOffset));
ParameterCount actual(rax);
ParameterCount expected(rbx);
__ InvokeCode(r8, no_reg, expected, actual, JUMP_FUNCTION, NullCallWrapper());
__ InvokeFunctionCode(rdi, no_reg, expected, actual, JUMP_FUNCTION,
CheckDebugStepCallWrapper());
// The function is a "classConstructor", need to raise an exception.
__ bind(&class_constructor);

View File

@ -708,8 +708,8 @@ void MacroAssembler::InvokeBuiltin(int native_context_index, InvokeFlag flag,
// arguments match the expected number of arguments. Fake a
// parameter count to avoid emitting code to do the check.
ParameterCount expected(0);
GetBuiltinEntry(r8, native_context_index);
InvokeCode(r8, no_reg, expected, expected, flag, call_wrapper);
GetBuiltinFunction(rdi, native_context_index);
InvokeFunctionCode(rdi, no_reg, expected, expected, flag, call_wrapper);
}
@ -722,15 +722,6 @@ void MacroAssembler::GetBuiltinFunction(Register target,
}
void MacroAssembler::GetBuiltinEntry(Register target,
int native_context_index) {
DCHECK(!target.is(rdi));
// Load the JavaScript builtin function from the builtins object.
GetBuiltinFunction(rdi, native_context_index);
movp(target, FieldOperand(rdi, JSFunction::kCodeEntryOffset));
}
#define REG(Name) \
{ Register::kCode_##Name }
@ -4039,25 +4030,25 @@ void MacroAssembler::InvokeFunction(Register function,
const CallWrapper& call_wrapper) {
DCHECK(function.is(rdi));
movp(rsi, FieldOperand(function, JSFunction::kContextOffset));
// Advances r8 to the end of the Code object header, to the start of
// the executable code.
movp(r8, FieldOperand(rdi, JSFunction::kCodeEntryOffset));
InvokeCode(r8, new_target, expected, actual, flag, call_wrapper);
InvokeFunctionCode(rdi, new_target, expected, actual, flag, call_wrapper);
}
void MacroAssembler::InvokeCode(Register code,
Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper) {
void MacroAssembler::InvokeFunctionCode(Register function, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper) {
// You can't call a function without a valid frame.
DCHECK(flag == JUMP_FUNCTION || has_frame());
// Ensure new target is passed in the correct register. Otherwise clear the
// appropriate register in case new target is not given.
DCHECK(function.is(rdi));
DCHECK_IMPLIES(new_target.is_valid(), new_target.is(rdx));
if (call_wrapper.NeedsDebugStepCheck()) {
FloodFunctionIfStepping(function, new_target, expected, actual);
}
// Clear the new.target register if not given.
if (!new_target.is_valid()) {
LoadRoot(rdx, Heap::kUndefinedValueRootIndex);
}
@ -4072,6 +4063,10 @@ void MacroAssembler::InvokeCode(Register code,
Label::kNear,
call_wrapper);
if (!definitely_mismatches) {
// We call indirectly through the code field in the function to
// allow recompilation to take effect without changing any of the
// call sites.
Operand code = FieldOperand(function, JSFunction::kCodeEntryOffset);
if (flag == CALL_FUNCTION) {
call_wrapper.BeforeCall(CallSize(code));
call(code);
@ -4151,6 +4146,49 @@ void MacroAssembler::InvokePrologue(const ParameterCount& expected,
}
void MacroAssembler::FloodFunctionIfStepping(Register fun, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual) {
Label skip_flooding;
ExternalReference debug_step_action =
ExternalReference::debug_last_step_action_address(isolate());
Operand debug_step_action_operand = ExternalOperand(debug_step_action);
cmpb(debug_step_action_operand, Immediate(StepIn));
j(not_equal, &skip_flooding);
{
FrameScope frame(this,
has_frame() ? StackFrame::NONE : StackFrame::INTERNAL);
if (expected.is_reg()) {
Integer32ToSmi(expected.reg(), expected.reg());
Push(expected.reg());
}
if (actual.is_reg()) {
Integer32ToSmi(actual.reg(), actual.reg());
Push(actual.reg());
}
if (new_target.is_valid()) {
Push(new_target);
}
Push(fun);
Push(fun);
CallRuntime(Runtime::kDebugPrepareStepInIfStepping, 1);
Pop(fun);
if (new_target.is_valid()) {
Pop(new_target);
}
if (actual.is_reg()) {
Pop(actual.reg());
SmiToInteger64(actual.reg(), actual.reg());
}
if (expected.is_reg()) {
Pop(expected.reg());
SmiToInteger64(expected.reg(), expected.reg());
}
}
bind(&skip_flooding);
}
void MacroAssembler::StubPrologue() {
pushq(rbp); // Caller's frame pointer.
movp(rbp, rsp);

View File

@ -368,12 +368,10 @@ class MacroAssembler: public Assembler {
// JavaScript invokes
// Invoke the JavaScript function code by either calling or jumping.
void InvokeCode(Register code,
Register new_target,
const ParameterCount& expected,
const ParameterCount& actual,
InvokeFlag flag,
const CallWrapper& call_wrapper);
void InvokeFunctionCode(Register function, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual, InvokeFlag flag,
const CallWrapper& call_wrapper);
// Invoke the JavaScript function in the given register. Changes the
// current context to the context in the function before invoking.
@ -403,10 +401,6 @@ class MacroAssembler: public Assembler {
// Store the function for the given builtin in the target register.
void GetBuiltinFunction(Register target, int native_context_index);
// Store the code object for the given builtin in the target register.
void GetBuiltinEntry(Register target, int native_context_index);
// ---------------------------------------------------------------------------
// Smi tagging, untagging and operations on tagged smis.
@ -1594,6 +1588,10 @@ class MacroAssembler: public Assembler {
Label::Distance near_jump,
const CallWrapper& call_wrapper);
void FloodFunctionIfStepping(Register fun, Register new_target,
const ParameterCount& expected,
const ParameterCount& actual);
void EnterExitFramePrologue(bool save_rax);
// Allocates arg_stack_space * kPointerSize memory (not GCed) on the stack

View File

@ -347,6 +347,9 @@ static void PrepareStep(StepAction step_action) {
}
static void ClearStepping() { CcTest::i_isolate()->debug()->ClearStepping(); }
// This function is in namespace v8::internal to be friend with class
// v8::internal::Debug.
namespace v8 {
@ -3838,7 +3841,7 @@ TEST(DebugStepFunctionCallApply) {
break_point_hit_count = 0;
foo->Call(context, env->Global(), 0, NULL).ToLocalChecked();
CHECK_EQ(5, break_point_hit_count);
CHECK_EQ(6, break_point_hit_count);
v8::Debug::SetDebugEventListener(NULL);
CheckDebuggerUnloaded();
@ -4211,6 +4214,7 @@ TEST(StepWithException) {
"function h() { x = 1; throw 1; }; ";
// Step through invocation of a.
ClearStepping();
v8::Local<v8::Function> a = CompileFunction(&env, src, "a");
SetBreakPoint(a, 0);
step_action = StepIn;
@ -4221,6 +4225,7 @@ TEST(StepWithException) {
break_point_hit_count);
// Step through invocation of b + c.
ClearStepping();
v8::Local<v8::Function> b = CompileFunction(&env, src, "b");
SetBreakPoint(b, 0);
step_action = StepIn;
@ -4229,7 +4234,9 @@ TEST(StepWithException) {
CHECK(b->Call(context, env->Global(), 0, NULL).IsEmpty());
CHECK_EQ(StrLength(expected_step_sequence),
break_point_hit_count);
// Step through invocation of d + e.
ClearStepping();
v8::Local<v8::Function> d = CompileFunction(&env, src, "d");
SetBreakPoint(d, 0);
ChangeBreakOnException(false, true);
@ -4250,6 +4257,7 @@ TEST(StepWithException) {
break_point_hit_count);
// Step through invocation of f + g + h.
ClearStepping();
v8::Local<v8::Function> f = CompileFunction(&env, src, "f");
SetBreakPoint(f, 0);
ChangeBreakOnException(false, true);

View File

@ -0,0 +1,35 @@
// Copyright 2015 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
Debug = debug.Debug
var exception = null;
var break_count = 0;
function listener(event, exec_state, event_data, data) {
if (event != Debug.DebugEvent.Break) return;
try {
print(event_data.sourceLineText());
assertTrue(event_data.sourceLineText().indexOf(`Break ${break_count++}.`) > 0);
exec_state.prepareStep(Debug.StepAction.StepIn, 1);
} catch (e) {
exception = e;
}
};
function toJsonCallback() {
return "x"; // Break 2.
} // Break 3.
var o = {};
o.toJSON = toJsonCallback;
Debug.setListener(listener);
debugger; // Break 0.
var result = JSON.stringify(o); // Break 1.
Debug.setListener(null); // Break 4.
assertEquals('"x"', result);
assertNull(exception);

View File

@ -0,0 +1,35 @@
// Copyright 2015 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
Debug = debug.Debug
var exception = null;
var break_count = 0;
function listener(event, exec_state, event_data, data) {
if (event != Debug.DebugEvent.Break) return;
try {
print(event_data.sourceLineText());
assertTrue(event_data.sourceLineText().indexOf(`Break ${break_count++}.`) > 0);
exec_state.prepareStep(Debug.StepAction.StepIn, 1);
} catch (e) {
exception = e;
}
};
function valueOfCallback() {
return 2; // Break 2.
} // Break 3.
var o = {};
o.valueOf = valueOfCallback;
Debug.setListener(listener);
debugger; // Break 0.
var result = 1 + o; // Break 1.
Debug.setListener(null); // Break 4.
assertEquals(3, result);
assertNull(exception);

View File

@ -0,0 +1,36 @@
// Copyright 2015 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
Debug = debug.Debug
var exception = null;
var break_count = 0;
function listener(event, exec_state, event_data, data) {
if (event != Debug.DebugEvent.Break) return;
try {
print(event_data.sourceLineText());
assertTrue(event_data.sourceLineText().indexOf(`Break ${break_count++}.`) > 0);
exec_state.prepareStep(Debug.StepAction.StepIn, 1);
} catch (e) {
exception = e;
}
};
function replaceCallback(a) {
return "x"; // Break 2.
} // Break 3.
var re = /x/g;
// Optimize the inner helper function for string replace.
for (var i = 0; i < 10000; i++) "x".replace(re, replaceCallback);
Debug.setListener(listener);
debugger; // Break 0.
var result = "x".replace(re, replaceCallback); // Break 1.
Debug.setListener(null); // Break 4.
assertNull(exception);

View File

@ -0,0 +1,36 @@
// Copyright 2015 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 --harmony-regexp-subclass
Debug = debug.Debug
var exception = null;
var break_count = 0;
function listener(event, exec_state, event_data, data) {
if (event != Debug.DebugEvent.Break) return;
try {
print(event_data.sourceLineText());
assertTrue(
event_data.sourceLineText().indexOf(`Break ${break_count++}.`) > 0);
exec_state.prepareStep(Debug.StepAction.StepIn, 1);
} catch (e) {
exception = e;
}
};
function customSplit() {
return "x"; // Break 2.
} // Break 3.
var o = {};
o[Symbol.split] = customSplit;
Debug.setListener(listener);
debugger; // Break 0.
var result = "".split(o); // Break 1.
Debug.setListener(null); // Break 4.
assertEquals("x", result);
assertNull(exception);

View File

@ -10,7 +10,7 @@ Debug.setBreakOnException();
try {
try {
%DebugPushPromise(new Promise(function() {}), function() {}, function() {});
%DebugPushPromise(new Promise(function() {}), function() {});
} catch (e) {
}
throw new Error();