[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:
parent
69227cc192
commit
81e131ce48
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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_; }
|
||||
|
@ -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);
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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) \
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
35
test/mjsunit/debug-step-into-json.js
Normal file
35
test/mjsunit/debug-step-into-json.js
Normal 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);
|
35
test/mjsunit/debug-step-into-valueof.js
Normal file
35
test/mjsunit/debug-step-into-valueof.js
Normal 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);
|
36
test/mjsunit/debug-stepin-builtin-callback-opt.js
Normal file
36
test/mjsunit/debug-stepin-builtin-callback-opt.js
Normal 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);
|
36
test/mjsunit/es6/debug-step-into-regexp-subclass.js
Normal file
36
test/mjsunit/es6/debug-step-into-regexp-subclass.js
Normal 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);
|
@ -10,7 +10,7 @@ Debug.setBreakOnException();
|
||||
|
||||
try {
|
||||
try {
|
||||
%DebugPushPromise(new Promise(function() {}), function() {}, function() {});
|
||||
%DebugPushPromise(new Promise(function() {}), function() {});
|
||||
} catch (e) {
|
||||
}
|
||||
throw new Error();
|
||||
|
Loading…
Reference in New Issue
Block a user