// r0:r1: result
// sp: stack pointer
// fp: frame pointer
- __ LeaveExitFrame(mode_);
+ __ LeaveExitFrame();
// check if we should retry or throw exception
Label retry;
// builtin once.
// Enter the exit frame that transitions from JavaScript to C++.
- __ EnterExitFrame(mode_);
+ __ EnterExitFrame();
// r4: number of arguments (C callee-saved)
// r5: pointer to builtin function (C callee-saved)
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
- RegList pointer_regs) {
- // Save the content of all general purpose registers in memory. This copy in
- // memory is later pushed onto the JS expression stack for the fake JS frame
- // generated and also to the C frame generated on top of that. In the JS
- // frame ONLY the registers containing pointers will be pushed on the
- // expression stack. This causes the GC to update these pointers so that
- // they will have the correct value when returning from the debugger.
- __ SaveRegistersToMemory(kJSCallerSaved);
-
+ RegList object_regs,
+ RegList non_object_regs) {
__ EnterInternalFrame();
- // Store the registers containing object pointers on the expression stack to
- // make sure that these are correctly updated during GC.
- // Use sp as base to push.
- __ CopyRegistersFromMemoryToStack(sp, pointer_regs);
+ // Store the registers containing live values on the expression stack to
+ // make sure that these are correctly updated during GC. Non object values
+ // are stored as a smi causing it to be untouched by GC.
+ ASSERT((object_regs & ~kJSCallerSaved) == 0);
+ ASSERT((non_object_regs & ~kJSCallerSaved) == 0);
+ ASSERT((object_regs & non_object_regs) == 0);
+ if ((object_regs | non_object_regs) != 0) {
+ for (int i = 0; i < kNumJSCallerSaved; i++) {
+ int r = JSCallerSavedCode(i);
+ Register reg = { r };
+ if ((non_object_regs & (1 << r)) != 0) {
+ if (FLAG_debug_code) {
+ __ tst(reg, Operand(0xc0000000));
+ __ Assert(eq, "Unable to encode value as smi");
+ }
+ __ mov(reg, Operand(reg, LSL, kSmiTagSize));
+ }
+ }
+ __ stm(db_w, sp, object_regs | non_object_regs);
+ }
#ifdef DEBUG
__ RecordComment("// Calling from debug break to runtime - come in - over");
__ mov(r0, Operand(0)); // no arguments
__ mov(r1, Operand(ExternalReference::debug_break()));
- CEntryStub ceb(1, ExitFrame::MODE_DEBUG);
+ CEntryStub ceb(1);
__ CallStub(&ceb);
- // Restore the register values containing object pointers from the expression
- // stack in the reverse order as they where pushed.
- // Use sp as base to pop.
- __ CopyRegistersFromStackToMemory(sp, r3, pointer_regs);
+ // Restore the register values from the expression stack.
+ if ((object_regs | non_object_regs) != 0) {
+ __ ldm(ia_w, sp, object_regs | non_object_regs);
+ for (int i = 0; i < kNumJSCallerSaved; i++) {
+ int r = JSCallerSavedCode(i);
+ Register reg = { r };
+ if ((non_object_regs & (1 << r)) != 0) {
+ __ mov(reg, Operand(reg, LSR, kSmiTagSize));
+ }
+ if (FLAG_debug_code &&
+ (((object_regs |non_object_regs) & (1 << r)) == 0)) {
+ __ mov(reg, Operand(kDebugZapValue));
+ }
+ }
+ }
__ LeaveInternalFrame();
- // Finally restore all registers.
- __ RestoreRegistersFromMemory(kJSCallerSaved);
-
// Now that the break point has been handled, resume normal execution by
// jumping to the target address intended by the caller and that was
// overwritten by the address of DebugBreakXXX.
// -----------------------------------
// Registers r0 and r2 contain objects that need to be pushed on the
// expression stack of the fake JS frame.
- Generate_DebugBreakCallHelper(masm, r0.bit() | r2.bit());
+ Generate_DebugBreakCallHelper(masm, r0.bit() | r2.bit(), 0);
}
// -----------------------------------
// Registers r0, r1, and r2 contain objects that need to be pushed on the
// expression stack of the fake JS frame.
- Generate_DebugBreakCallHelper(masm, r0.bit() | r1.bit() | r2.bit());
+ Generate_DebugBreakCallHelper(masm, r0.bit() | r1.bit() | r2.bit(), 0);
}
// ---------- S t a t e --------------
// -- lr : return address
// -- r0 : key
- // -- sp[0] : key
- // -- sp[4] : receiver
- Generate_DebugBreakCallHelper(masm, r0.bit());
+ // -- r1 : receiver
+ Generate_DebugBreakCallHelper(masm, r0.bit() | r1.bit(), 0);
}
// -- r1 : key
// -- r2 : receiver
// -- lr : return address
- Generate_DebugBreakCallHelper(masm, r0.bit() | r1.bit() | r2.bit());
+ Generate_DebugBreakCallHelper(masm, r0.bit() | r1.bit() | r2.bit(), 0);
}
void Debug::GenerateCallICDebugBreak(MacroAssembler* masm) {
// Calling convention for IC call (from ic-arm.cc)
// ----------- S t a t e -------------
- // -- r2: name
+ // -- r2 : name
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, r2.bit());
+ Generate_DebugBreakCallHelper(masm, r2.bit(), 0);
}
void Debug::GenerateConstructCallDebugBreak(MacroAssembler* masm) {
- // In places other than IC call sites it is expected that r0 is TOS which
- // is an object - this is not generally the case so this should be used with
- // care.
- Generate_DebugBreakCallHelper(masm, r0.bit());
+ // Calling convention for construct call (from builtins-arm.cc)
+ // -- r0 : number of arguments (not smi)
+ // -- r1 : constructor function
+ Generate_DebugBreakCallHelper(masm, r1.bit(), r0.bit());
}
// In places other than IC call sites it is expected that r0 is TOS which
// is an object - this is not generally the case so this should be used with
// care.
- Generate_DebugBreakCallHelper(masm, r0.bit());
+ Generate_DebugBreakCallHelper(masm, r0.bit(), 0);
}
// ----------- S t a t e -------------
// No registers used on entry.
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, 0);
+ Generate_DebugBreakCallHelper(masm, 0, 0);
}
void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) {
// In the places where a debug break slot is inserted no registers can contain
// object pointers.
- Generate_DebugBreakCallHelper(masm, 0);
+ Generate_DebugBreakCallHelper(masm, 0, 0);
}
StackFrame::Type ExitFrame::GetStateForFramePointer(Address fp, State* state) {
if (fp == 0) return NONE;
// Compute frame type and stack pointer.
- Address sp = fp + ExitFrameConstants::kSPDisplacement;
- const int offset = ExitFrameConstants::kCodeOffset;
- Object* code = Memory::Object_at(fp + offset);
- bool is_debug_exit = code->IsSmi();
- if (is_debug_exit) {
- sp -= kNumJSCallerSaved * kPointerSize;
- }
+ Address sp = fp + ExitFrameConstants::kSPOffset;
+
// Fill in the state.
state->sp = sp;
state->fp = fp;
state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize);
+ ASSERT(*state->pc_address != NULL);
return EXIT;
}
class ExitFrameConstants : public AllStatic {
public:
- // Exit frames have a debug marker on the stack.
- static const int kSPDisplacement = -1 * kPointerSize;
-
- // The debug marker is just above the frame pointer.
static const int kCodeOffset = -1 * kPointerSize;
+ static const int kSPOffset = -1 * kPointerSize;
static const int kSavedRegistersOffset = 0 * kPointerSize;
}
-void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode) {
+void MacroAssembler::EnterExitFrame() {
// Compute the argv pointer and keep it in a callee-saved register.
// r0 is argc.
add(r6, sp, Operand(r0, LSL, kPointerSizeLog2));
// Setup argc and the builtin function in callee-saved registers.
mov(r4, Operand(r0));
mov(r5, Operand(r1));
-
-
-#ifdef ENABLE_DEBUGGER_SUPPORT
- // Save the state of all registers to the stack from the memory
- // location. This is needed to allow nested break points.
- if (mode == ExitFrame::MODE_DEBUG) {
- // Use sp as base to push.
- CopyRegistersFromMemoryToStack(sp, kJSCallerSaved);
- }
-#endif
}
}
-void MacroAssembler::LeaveExitFrame(ExitFrame::Mode mode) {
-#ifdef ENABLE_DEBUGGER_SUPPORT
- // Restore the memory copy of the registers by digging them out from
- // the stack. This is needed to allow nested break points.
- if (mode == ExitFrame::MODE_DEBUG) {
- // This code intentionally clobbers r2 and r3.
- const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize;
- const int kOffset = ExitFrameConstants::kCodeOffset - kCallerSavedSize;
- add(r3, fp, Operand(kOffset));
- CopyRegistersFromStackToMemory(r3, r2, kJSCallerSaved);
- }
-#endif
-
+void MacroAssembler::LeaveExitFrame() {
// Clear top frame.
mov(r3, Operand(0));
mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address)));
InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET, flag);
}
-#ifdef ENABLE_DEBUGGER_SUPPORT
-void MacroAssembler::SaveRegistersToMemory(RegList regs) {
- ASSERT((regs & ~kJSCallerSaved) == 0);
- // Copy the content of registers to memory location.
- for (int i = 0; i < kNumJSCallerSaved; i++) {
- int r = JSCallerSavedCode(i);
- if ((regs & (1 << r)) != 0) {
- Register reg = { r };
- mov(ip, Operand(ExternalReference(Debug_Address::Register(i))));
- str(reg, MemOperand(ip));
- }
- }
-}
-
-
-void MacroAssembler::RestoreRegistersFromMemory(RegList regs) {
- ASSERT((regs & ~kJSCallerSaved) == 0);
- // Copy the content of memory location to registers.
- for (int i = kNumJSCallerSaved; --i >= 0;) {
- int r = JSCallerSavedCode(i);
- if ((regs & (1 << r)) != 0) {
- Register reg = { r };
- mov(ip, Operand(ExternalReference(Debug_Address::Register(i))));
- ldr(reg, MemOperand(ip));
- }
- }
-}
-
-
-void MacroAssembler::CopyRegistersFromMemoryToStack(Register base,
- RegList regs) {
- ASSERT((regs & ~kJSCallerSaved) == 0);
- // Copy the content of the memory location to the stack and adjust base.
- for (int i = kNumJSCallerSaved; --i >= 0;) {
- int r = JSCallerSavedCode(i);
- if ((regs & (1 << r)) != 0) {
- mov(ip, Operand(ExternalReference(Debug_Address::Register(i))));
- ldr(ip, MemOperand(ip));
- str(ip, MemOperand(base, 4, NegPreIndex));
- }
- }
-}
-
-
-void MacroAssembler::CopyRegistersFromStackToMemory(Register base,
- Register scratch,
- RegList regs) {
- ASSERT((regs & ~kJSCallerSaved) == 0);
- // Copy the content of the stack to the memory location and adjust base.
- for (int i = 0; i < kNumJSCallerSaved; i++) {
- int r = JSCallerSavedCode(i);
- if ((regs & (1 << r)) != 0) {
- mov(ip, Operand(ExternalReference(Debug_Address::Register(i))));
- ldr(scratch, MemOperand(base, 4, PostIndex));
- str(scratch, MemOperand(ip));
- }
- }
-}
-
+#ifdef ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::DebugBreak() {
ASSERT(allow_stub_calls());
mov(r0, Operand(0));
void EnterConstructFrame() { EnterFrame(StackFrame::CONSTRUCT); }
void LeaveConstructFrame() { LeaveFrame(StackFrame::CONSTRUCT); }
- // Enter specific kind of exit frame; either normal or debug mode.
+ // Enter exit frame.
// Expects the number of arguments in register r0 and
// the builtin function to call in register r1. Exits with argc in
// r4, argv in r6, and and the builtin function to call in r5.
- void EnterExitFrame(ExitFrame::Mode mode);
+ void EnterExitFrame();
// Leave the current exit frame. Expects the return value in r0.
- void LeaveExitFrame(ExitFrame::Mode mode);
+ void LeaveExitFrame();
// Get the actual activation frame alignment for target environment.
static int ActivationFrameAlignment();
// ---------------------------------------------------------------------------
// Debugger Support
- void SaveRegistersToMemory(RegList regs);
- void RestoreRegistersFromMemory(RegList regs);
- void CopyRegistersFromMemoryToStack(Register base, RegList regs);
- void CopyRegistersFromStackToMemory(Register base,
- Register scratch,
- RegList regs);
void DebugBreak();
#endif
int CEntryStub::MinorKey() {
- ASSERT(result_size_ <= 2);
+ ASSERT(result_size_ == 1 || result_size_ == 2);
#ifdef _WIN64
- return ExitFrameModeBits::encode(mode_)
- | IndirectResultBits::encode(result_size_ > 1);
+ return result_size_ == 1 ? 0 : 1;
#else
- return ExitFrameModeBits::encode(mode_);
+ return 0;
#endif
}
class CEntryStub : public CodeStub {
public:
- explicit CEntryStub(int result_size,
- ExitFrame::Mode mode = ExitFrame::MODE_NORMAL)
- : result_size_(result_size), mode_(mode) { }
+ explicit CEntryStub(int result_size) : result_size_(result_size) { }
void Generate(MacroAssembler* masm);
// Number of pointers/values returned.
const int result_size_;
- const ExitFrame::Mode mode_;
// Minor key encoding
- class ExitFrameModeBits: public BitField<ExitFrame::Mode, 0, 1> {};
class IndirectResultBits: public BitField<bool, 1, 1> {};
Major MajorKey() { return CEntry; }
k_after_break_target_address,
k_debug_break_return_address,
k_debug_break_slot_address,
- k_restarter_frame_function_pointer,
- k_register_address
+ k_restarter_frame_function_pointer
};
// Support for setting the address to jump to when returning from break point.
// code.
class Debug_Address {
public:
- Debug_Address(Debug::AddressId id, int reg = 0)
- : id_(id), reg_(reg) {
- ASSERT(reg == 0 || id == Debug::k_register_address);
- }
+ Debug_Address(Debug::AddressId id) : id_(id) { }
static Debug_Address AfterBreakTarget() {
return Debug_Address(Debug::k_after_break_target_address);
return Debug_Address(Debug::k_restarter_frame_function_pointer);
}
- static Debug_Address Register(int reg) {
- return Debug_Address(Debug::k_register_address, reg);
- }
-
Address address() const {
switch (id_) {
case Debug::k_after_break_target_address:
case Debug::k_restarter_frame_function_pointer:
return reinterpret_cast<Address>(
Debug::restarter_frame_function_pointer_address());
- case Debug::k_register_address:
- return reinterpret_cast<Address>(Debug::register_address(reg_));
default:
UNREACHABLE();
return NULL;
}
private:
Debug::AddressId id_;
- int reg_;
};
// The optional thread that Debug Agent may use to temporary call V8 to process
// Exit frames are used to exit JavaScript execution and go to C.
class ExitFrame: public StackFrame {
public:
- enum Mode { MODE_NORMAL, MODE_DEBUG };
virtual Type type() const { return EXIT; }
virtual Code* unchecked_code() const;
Label empty_handle;
Label prologue;
Label promote_scheduled_exception;
- __ EnterApiExitFrame(ExitFrame::MODE_NORMAL, kStackSpace, kArgc);
+ __ EnterApiExitFrame(kStackSpace, kArgc);
STATIC_ASSERT(kArgc == 4);
if (kPassHandlesDirectly) {
// When handles as passed directly we don't have to allocate extra
// It was non-zero. Dereference to get the result value.
__ mov(eax, Operand(eax, 0));
__ bind(&prologue);
- __ LeaveExitFrame(ExitFrame::MODE_NORMAL);
+ __ LeaveExitFrame();
__ ret(0);
__ bind(&promote_scheduled_exception);
__ TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1);
__ j(zero, &failure_returned, not_taken);
// Exit the JavaScript to C++ exit frame.
- __ LeaveExitFrame(mode_);
+ __ LeaveExitFrame();
__ ret(0);
// Handling of failure.
// a garbage collection and retrying the builtin (twice).
// Enter the exit frame that transitions from JavaScript to C++.
- __ EnterExitFrame(mode_);
+ __ EnterExitFrame();
// eax: result parameter for PerformGC, if any (setup below)
// ebx: pointer to builtin function (C callee-saved)
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
- RegList pointer_regs,
+ RegList object_regs,
+ RegList non_object_regs,
bool convert_call_to_jmp) {
- // Save the content of all general purpose registers in memory. This copy in
- // memory is later pushed onto the JS expression stack for the fake JS frame
- // generated and also to the C frame generated on top of that. In the JS
- // frame ONLY the registers containing pointers will be pushed on the
- // expression stack. This causes the GC to update these pointers so that
- // they will have the correct value when returning from the debugger.
- __ SaveRegistersToMemory(kJSCallerSaved);
-
// Enter an internal frame.
__ EnterInternalFrame();
- // Store the registers containing object pointers on the expression stack to
- // make sure that these are correctly updated during GC.
- __ PushRegistersFromMemory(pointer_regs);
+ // Store the registers containing live values on the expression stack to
+ // make sure that these are correctly updated during GC. Non object values
+ // are stored as a smi causing it to be untouched by GC.
+ ASSERT((object_regs & ~kJSCallerSaved) == 0);
+ ASSERT((non_object_regs & ~kJSCallerSaved) == 0);
+ ASSERT((object_regs & non_object_regs) == 0);
+ for (int i = 0; i < kNumJSCallerSaved; i++) {
+ int r = JSCallerSavedCode(i);
+ Register reg = { r };
+ if ((object_regs & (1 << r)) != 0) {
+ __ push(reg);
+ }
+ if ((non_object_regs & (1 << r)) != 0) {
+ if (FLAG_debug_code) {
+ __ test(reg, Immediate(0xc0000000));
+ __ Assert(zero, "Unable to encode value as smi");
+ }
+ __ SmiTag(reg);
+ __ push(reg);
+ }
+ }
#ifdef DEBUG
__ RecordComment("// Calling from debug break to runtime - come in - over");
__ Set(eax, Immediate(0)); // no arguments
__ mov(ebx, Immediate(ExternalReference::debug_break()));
- CEntryStub ceb(1, ExitFrame::MODE_DEBUG);
+ CEntryStub ceb(1);
__ CallStub(&ceb);
// Restore the register values containing object pointers from the expression
- // stack in the reverse order as they where pushed.
- __ PopRegistersToMemory(pointer_regs);
+ // stack.
+ for (int i = kNumJSCallerSaved; --i >= 0;) {
+ int r = JSCallerSavedCode(i);
+ Register reg = { r };
+ if (FLAG_debug_code) {
+ __ Set(reg, Immediate(kDebugZapValue));
+ }
+ if ((object_regs & (1 << r)) != 0) {
+ __ pop(reg);
+ }
+ if ((non_object_regs & (1 << r)) != 0) {
+ __ pop(reg);
+ __ SmiUntag(reg);
+ }
+ }
// Get rid of the internal frame.
__ LeaveInternalFrame();
// If this call did not replace a call but patched other code then there will
// be an unwanted return address left on the stack. Here we get rid of that.
if (convert_call_to_jmp) {
- __ pop(eax);
+ __ add(Operand(esp), Immediate(kPointerSize));
}
- // Finally restore all registers.
- __ RestoreRegistersFromMemory(kJSCallerSaved);
-
// Now that the break point has been handled, resume normal execution by
// jumping to the target address intended by the caller and that was
// overwritten by the address of DebugBreakXXX.
// -- eax : receiver
// -- ecx : name
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, eax.bit() | ecx.bit(), false);
+ Generate_DebugBreakCallHelper(masm, eax.bit() | ecx.bit(), 0, false);
}
// -- ecx : name
// -- edx : receiver
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, eax.bit() | ecx.bit() | edx.bit(), false);
+ Generate_DebugBreakCallHelper(
+ masm, eax.bit() | ecx.bit() | edx.bit(), 0, false);
}
// -- edx : receiver
// -- eax : key
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, eax.bit() | edx.bit(), false);
+ Generate_DebugBreakCallHelper(masm, eax.bit() | edx.bit(), 0, false);
}
// -- ecx : key
// -- edx : receiver
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, eax.bit() | ecx.bit() | edx.bit(), false);
+ Generate_DebugBreakCallHelper(
+ masm, eax.bit() | ecx.bit() | edx.bit(), 0, false);
}
// ----------- S t a t e -------------
// -- ecx: name
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, ecx.bit(), false);
+ Generate_DebugBreakCallHelper(masm, ecx.bit(), 0, false);
}
// eax is the actual number of arguments not encoded as a smi see comment
// above IC call.
// ----------- S t a t e -------------
- // -- eax: number of arguments
+ // -- eax: number of arguments (not smi)
+ // -- edi: constructor function
// -----------------------------------
// The number of arguments in eax is not smi encoded.
- Generate_DebugBreakCallHelper(masm, 0, false);
+ Generate_DebugBreakCallHelper(masm, edi.bit(), eax.bit(), false);
}
// ----------- S t a t e -------------
// -- eax: return value
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, eax.bit(), true);
+ Generate_DebugBreakCallHelper(masm, eax.bit(), 0, true);
}
// ----------- S t a t e -------------
// No registers used on entry.
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, 0, false);
+ Generate_DebugBreakCallHelper(masm, 0, 0, false);
}
void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) {
// In the places where a debug break slot is inserted no registers can contain
// object pointers.
- Generate_DebugBreakCallHelper(masm, 0, true);
+ Generate_DebugBreakCallHelper(masm, 0, 0, true);
}
state->fp = fp;
state->sp = sp;
state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize);
+ ASSERT(*state->pc_address != NULL);
return EXIT;
}
#ifdef ENABLE_DEBUGGER_SUPPORT
-void MacroAssembler::SaveRegistersToMemory(RegList regs) {
- ASSERT((regs & ~kJSCallerSaved) == 0);
- // Copy the content of registers to memory location.
- for (int i = 0; i < kNumJSCallerSaved; i++) {
- int r = JSCallerSavedCode(i);
- if ((regs & (1 << r)) != 0) {
- Register reg = { r };
- ExternalReference reg_addr =
- ExternalReference(Debug_Address::Register(i));
- mov(Operand::StaticVariable(reg_addr), reg);
- }
- }
-}
-
-
-void MacroAssembler::RestoreRegistersFromMemory(RegList regs) {
- ASSERT((regs & ~kJSCallerSaved) == 0);
- // Copy the content of memory location to registers.
- for (int i = kNumJSCallerSaved; --i >= 0;) {
- int r = JSCallerSavedCode(i);
- if ((regs & (1 << r)) != 0) {
- Register reg = { r };
- ExternalReference reg_addr =
- ExternalReference(Debug_Address::Register(i));
- mov(reg, Operand::StaticVariable(reg_addr));
- }
- }
-}
-
-
-void MacroAssembler::PushRegistersFromMemory(RegList regs) {
- ASSERT((regs & ~kJSCallerSaved) == 0);
- // Push the content of the memory location to the stack.
- for (int i = 0; i < kNumJSCallerSaved; i++) {
- int r = JSCallerSavedCode(i);
- if ((regs & (1 << r)) != 0) {
- ExternalReference reg_addr =
- ExternalReference(Debug_Address::Register(i));
- push(Operand::StaticVariable(reg_addr));
- }
- }
-}
-
-
-void MacroAssembler::PopRegistersToMemory(RegList regs) {
- ASSERT((regs & ~kJSCallerSaved) == 0);
- // Pop the content from the stack to the memory location.
- for (int i = kNumJSCallerSaved; --i >= 0;) {
- int r = JSCallerSavedCode(i);
- if ((regs & (1 << r)) != 0) {
- ExternalReference reg_addr =
- ExternalReference(Debug_Address::Register(i));
- pop(Operand::StaticVariable(reg_addr));
- }
- }
-}
-
-
-void MacroAssembler::CopyRegistersFromStackToMemory(Register base,
- Register scratch,
- RegList regs) {
- ASSERT((regs & ~kJSCallerSaved) == 0);
- // Copy the content of the stack to the memory location and adjust base.
- for (int i = kNumJSCallerSaved; --i >= 0;) {
- int r = JSCallerSavedCode(i);
- if ((regs & (1 << r)) != 0) {
- mov(scratch, Operand(base, 0));
- ExternalReference reg_addr =
- ExternalReference(Debug_Address::Register(i));
- mov(Operand::StaticVariable(reg_addr), scratch);
- lea(base, Operand(base, kPointerSize));
- }
- }
-}
-
void MacroAssembler::DebugBreak() {
Set(eax, Immediate(0));
mov(ebx, Immediate(ExternalReference(Runtime::kDebugBreak)));
}
#endif
+
void MacroAssembler::Set(Register dst, const Immediate& x) {
if (x.is_zero()) {
xor_(dst, Operand(dst)); // shorter than mov
leave();
}
-void MacroAssembler::EnterExitFramePrologue(ExitFrame::Mode mode) {
+
+void MacroAssembler::EnterExitFramePrologue() {
// Setup the frame structure on the stack.
ASSERT(ExitFrameConstants::kCallerSPDisplacement == +2 * kPointerSize);
ASSERT(ExitFrameConstants::kCallerPCOffset == +1 * kPointerSize);
push(ebp);
mov(ebp, Operand(esp));
- // Reserve room for entry stack pointer and push the debug marker.
+ // Reserve room for entry stack pointer and push the code object.
ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize);
push(Immediate(0)); // Saved entry sp, patched before call.
push(Immediate(CodeObject())); // Accessed from ExitFrame::code_slot.
mov(Operand::StaticVariable(context_address), esi);
}
-void MacroAssembler::EnterExitFrameEpilogue(ExitFrame::Mode mode, int argc) {
-#ifdef ENABLE_DEBUGGER_SUPPORT
- // Save the state of all registers to the stack from the memory
- // location. This is needed to allow nested break points.
- if (mode == ExitFrame::MODE_DEBUG) {
- // TODO(1243899): This should be symmetric to
- // CopyRegistersFromStackToMemory() but it isn't! esp is assumed
- // correct here, but computed for the other call. Very error
- // prone! FIX THIS. Actually there are deeper problems with
- // register saving than this asymmetry (see the bug report
- // associated with this issue).
- PushRegistersFromMemory(kJSCallerSaved);
- }
-#endif
+void MacroAssembler::EnterExitFrameEpilogue(int argc) {
// Reserve space for arguments.
sub(Operand(esp), Immediate(argc * kPointerSize));
}
-void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode) {
- EnterExitFramePrologue(mode);
+void MacroAssembler::EnterExitFrame() {
+ EnterExitFramePrologue();
// Setup argc and argv in callee-saved registers.
int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
mov(edi, Operand(eax));
lea(esi, Operand(ebp, eax, times_4, offset));
- EnterExitFrameEpilogue(mode, 2);
+ EnterExitFrameEpilogue(2);
}
-void MacroAssembler::EnterApiExitFrame(ExitFrame::Mode mode,
- int stack_space,
+void MacroAssembler::EnterApiExitFrame(int stack_space,
int argc) {
- EnterExitFramePrologue(mode);
+ EnterExitFramePrologue();
int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
lea(esi, Operand(ebp, (stack_space * kPointerSize) + offset));
- EnterExitFrameEpilogue(mode, argc);
+ EnterExitFrameEpilogue(argc);
}
-void MacroAssembler::LeaveExitFrame(ExitFrame::Mode mode) {
-#ifdef ENABLE_DEBUGGER_SUPPORT
- // Restore the memory copy of the registers by digging them out from
- // the stack. This is needed to allow nested break points.
- if (mode == ExitFrame::MODE_DEBUG) {
- // It's okay to clobber register ebx below because we don't need
- // the function pointer after this.
- const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize;
- int kOffset = ExitFrameConstants::kCodeOffset - kCallerSavedSize;
- lea(ebx, Operand(ebp, kOffset));
- CopyRegistersFromStackToMemory(ebx, ecx, kJSCallerSaved);
- }
-#endif
-
+void MacroAssembler::LeaveExitFrame() {
// Get the return address from the stack and restore the frame pointer.
mov(ecx, Operand(ebp, 1 * kPointerSize));
mov(ebp, Operand(ebp, 0 * kPointerSize));
// ---------------------------------------------------------------------------
// Debugger Support
- void SaveRegistersToMemory(RegList regs);
- void RestoreRegistersFromMemory(RegList regs);
- void PushRegistersFromMemory(RegList regs);
- void PopRegistersToMemory(RegList regs);
- void CopyRegistersFromStackToMemory(Register base,
- Register scratch,
- RegList regs);
void DebugBreak();
#endif
// Expects the number of arguments in register eax and
// sets up the number of arguments in register edi and the pointer
// to the first argument in register esi.
- void EnterExitFrame(ExitFrame::Mode mode);
+ void EnterExitFrame();
- void EnterApiExitFrame(ExitFrame::Mode mode, int stack_space, int argc);
+ void EnterApiExitFrame(int stack_space, int argc);
// Leave the current exit frame. Expects the return value in
// register eax:edx (untouched) and the pointer to the first
// argument in register esi.
- void LeaveExitFrame(ExitFrame::Mode mode);
+ void LeaveExitFrame();
// Find the function context up the context chain.
void LoadContext(Register dst, int context_chain_length);
void EnterFrame(StackFrame::Type type);
void LeaveFrame(StackFrame::Type type);
- void EnterExitFramePrologue(ExitFrame::Mode mode);
- void EnterExitFrameEpilogue(ExitFrame::Mode mode, int argc);
+ void EnterExitFramePrologue();
+ void EnterExitFrameEpilogue(int argc);
// Allocation support helpers.
void LoadAllocationTopHelper(Register result,
DEBUG_ADDRESS,
Debug::k_restarter_frame_function_pointer << kDebugIdShift,
"Debug::restarter_frame_function_pointer_address()");
- const char* debug_register_format = "Debug::register_address(%i)";
- int dr_format_length = StrLength(debug_register_format);
- for (int i = 0; i < kNumJSCallerSaved; ++i) {
- Vector<char> name = Vector<char>::New(dr_format_length + 1);
- OS::SNPrintF(name, debug_register_format, i);
- Add(Debug_Address(Debug::k_register_address, i).address(),
- DEBUG_ADDRESS,
- Debug::k_register_address << kDebugIdShift | i,
- name.start());
- }
#endif
// Stat counters
Label empty_result;
Label prologue;
Label promote_scheduled_exception;
- __ EnterApiExitFrame(ExitFrame::MODE_NORMAL, kStackSpace, 0);
+ __ EnterApiExitFrame(kStackSpace, 0);
ASSERT_EQ(kArgc, 4);
#ifdef _WIN64
// All the parameters should be set up by a caller.
// It was non-zero. Dereference to get the result value.
__ movq(rax, Operand(rax, 0));
__ bind(&prologue);
- __ LeaveExitFrame(ExitFrame::MODE_NORMAL);
+ __ LeaveExitFrame();
__ ret(0);
__ bind(&promote_scheduled_exception);
__ TailCallRuntime(Runtime::kPromoteScheduledException, 0, 1);
__ j(zero, &failure_returned);
// Exit the JavaScript to C++ exit frame.
- __ LeaveExitFrame(mode_, result_size_);
+ __ LeaveExitFrame(result_size_);
__ ret(0);
// Handling of failure.
// builtin once.
// Enter the exit frame that transitions from JavaScript to C++.
- __ EnterExitFrame(mode_, result_size_);
+ __ EnterExitFrame(result_size_);
// rax: Holds the context at this point, but should not be used.
// On entry to code generated by GenerateCore, it must hold
#define __ ACCESS_MASM(masm)
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
- RegList pointer_regs,
+ RegList object_regs,
+ RegList non_object_regs,
bool convert_call_to_jmp) {
- // Save the content of all general purpose registers in memory. This copy in
- // memory is later pushed onto the JS expression stack for the fake JS frame
- // generated and also to the C frame generated on top of that. In the JS
- // frame ONLY the registers containing pointers will be pushed on the
- // expression stack. This causes the GC to update these pointers so that
- // they will have the correct value when returning from the debugger.
- __ SaveRegistersToMemory(kJSCallerSaved);
-
// Enter an internal frame.
__ EnterInternalFrame();
- // Store the registers containing object pointers on the expression stack to
- // make sure that these are correctly updated during GC.
- __ PushRegistersFromMemory(pointer_regs);
+ // Store the registers containing live values on the expression stack to
+ // make sure that these are correctly updated during GC. Non object values
+ // are stored as as two smi causing it to be untouched by GC.
+ ASSERT((object_regs & ~kJSCallerSaved) == 0);
+ ASSERT((non_object_regs & ~kJSCallerSaved) == 0);
+ ASSERT((object_regs & non_object_regs) == 0);
+ for (int i = 0; i < kNumJSCallerSaved; i++) {
+ int r = JSCallerSavedCode(i);
+ Register reg = { r };
+ ASSERT(!reg.is(kScratchRegister));
+ if ((object_regs & (1 << r)) != 0) {
+ __ push(reg);
+ }
+ // Store the 64-bit value as two smis.
+ if ((non_object_regs & (1 << r)) != 0) {
+ __ movq(kScratchRegister, reg);
+ __ Integer32ToSmi(reg, reg);
+ __ push(reg);
+ __ sar(kScratchRegister, Immediate(32));
+ __ Integer32ToSmi(kScratchRegister, kScratchRegister);
+ __ push(kScratchRegister);
+ }
+ }
#ifdef DEBUG
__ RecordComment("// Calling from debug break to runtime - come in - over");
__ xor_(rax, rax); // No arguments (argc == 0).
__ movq(rbx, ExternalReference::debug_break());
- CEntryStub ceb(1, ExitFrame::MODE_DEBUG);
+ CEntryStub ceb(1);
__ CallStub(&ceb);
- // Restore the register values containing object pointers from the expression
- // stack in the reverse order as they where pushed.
- __ PopRegistersToMemory(pointer_regs);
+ // Restore the register values from the expression stack.
+ for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
+ int r = JSCallerSavedCode(i);
+ Register reg = { r };
+ if (FLAG_debug_code) {
+ __ Set(reg, kDebugZapValue);
+ }
+ if ((object_regs & (1 << r)) != 0) {
+ __ pop(reg);
+ }
+ // Reconstruct the 64-bit value from two smis.
+ if ((non_object_regs & (1 << r)) != 0) {
+ __ pop(kScratchRegister);
+ __ SmiToInteger32(kScratchRegister, kScratchRegister);
+ __ shl(kScratchRegister, Immediate(32));
+ __ pop(reg);
+ __ SmiToInteger32(reg, reg);
+ __ or_(reg, kScratchRegister);
+ }
+ }
// Get rid of the internal frame.
__ LeaveInternalFrame();
// If this call did not replace a call but patched other code then there will
// be an unwanted return address left on the stack. Here we get rid of that.
if (convert_call_to_jmp) {
- __ pop(rax);
+ __ addq(rsp, Immediate(kPointerSize));
}
- // Finally restore all registers.
- __ RestoreRegistersFromMemory(kJSCallerSaved);
-
// Now that the break point has been handled, resume normal execution by
// jumping to the target address intended by the caller and that was
// overwritten by the address of DebugBreakXXX.
// ----------- S t a t e -------------
// -- rcx: function name
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, rcx.bit(), false);
+ Generate_DebugBreakCallHelper(masm, rcx.bit(), 0, false);
}
// -- rax: number of arguments
// -----------------------------------
// The number of arguments in rax is not smi encoded.
- Generate_DebugBreakCallHelper(masm, 0, false);
+ Generate_DebugBreakCallHelper(masm, rdi.bit(), rax.bit(), false);
}
// -- rax : key
// -- rdx : receiver
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, rax.bit() | rdx.bit(), false);
+ Generate_DebugBreakCallHelper(masm, rax.bit() | rdx.bit(), 0, false);
}
// -- rcx : key
// -- rdx : receiver
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, rax.bit() | rcx.bit() | rdx.bit(), false);
+ Generate_DebugBreakCallHelper(
+ masm, rax.bit() | rcx.bit() | rdx.bit(), 0, false);
}
// -- rax : receiver
// -- rcx : name
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, rax.bit() | rcx.bit(), false);
+ Generate_DebugBreakCallHelper(masm, rax.bit() | rcx.bit(), 0, false);
}
// ----------- S t a t e -------------
// -- rax: return value
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, rax.bit(), true);
+ Generate_DebugBreakCallHelper(masm, rax.bit(), 0, true);
}
// -- rcx : name
// -- rdx : receiver
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, rax.bit() | rcx.bit() | rdx.bit(), false);
+ Generate_DebugBreakCallHelper(
+ masm, rax.bit() | rcx.bit() | rdx.bit(), 0, false);
}
// ----------- S t a t e -------------
// No registers used on entry.
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, 0, false);
+ Generate_DebugBreakCallHelper(masm, 0, 0, false);
}
void Debug::GenerateSlotDebugBreak(MacroAssembler* masm) {
// In the places where a debug break slot is inserted no registers can contain
// object pointers.
- Generate_DebugBreakCallHelper(masm, 0, true);
+ Generate_DebugBreakCallHelper(masm, 0, 0, true);
}
state->fp = fp;
state->sp = sp;
state->pc_address = reinterpret_cast<Address*>(sp - 1 * kPointerSize);
- // Determine frame type.
+ ASSERT(*state->pc_address != NULL);
return EXIT;
}
}
}
-#ifdef ENABLE_DEBUGGER_SUPPORT
-
-void MacroAssembler::PushRegistersFromMemory(RegList regs) {
- ASSERT((regs & ~kJSCallerSaved) == 0);
- // Push the content of the memory location to the stack.
- for (int i = 0; i < kNumJSCallerSaved; i++) {
- int r = JSCallerSavedCode(i);
- if ((regs & (1 << r)) != 0) {
- ExternalReference reg_addr =
- ExternalReference(Debug_Address::Register(i));
- movq(kScratchRegister, reg_addr);
- push(Operand(kScratchRegister, 0));
- }
- }
-}
-
-
-void MacroAssembler::SaveRegistersToMemory(RegList regs) {
- ASSERT((regs & ~kJSCallerSaved) == 0);
- // Copy the content of registers to memory location.
- for (int i = 0; i < kNumJSCallerSaved; i++) {
- int r = JSCallerSavedCode(i);
- if ((regs & (1 << r)) != 0) {
- Register reg = { r };
- ExternalReference reg_addr =
- ExternalReference(Debug_Address::Register(i));
- movq(kScratchRegister, reg_addr);
- movq(Operand(kScratchRegister, 0), reg);
- }
- }
-}
-
-
-void MacroAssembler::RestoreRegistersFromMemory(RegList regs) {
- ASSERT((regs & ~kJSCallerSaved) == 0);
- // Copy the content of memory location to registers.
- for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
- int r = JSCallerSavedCode(i);
- if ((regs & (1 << r)) != 0) {
- Register reg = { r };
- ExternalReference reg_addr =
- ExternalReference(Debug_Address::Register(i));
- movq(kScratchRegister, reg_addr);
- movq(reg, Operand(kScratchRegister, 0));
- }
- }
-}
-
-
-void MacroAssembler::PopRegistersToMemory(RegList regs) {
- ASSERT((regs & ~kJSCallerSaved) == 0);
- // Pop the content from the stack to the memory location.
- for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
- int r = JSCallerSavedCode(i);
- if ((regs & (1 << r)) != 0) {
- ExternalReference reg_addr =
- ExternalReference(Debug_Address::Register(i));
- movq(kScratchRegister, reg_addr);
- pop(Operand(kScratchRegister, 0));
- }
- }
-}
-
-
-void MacroAssembler::CopyRegistersFromStackToMemory(Register base,
- Register scratch,
- RegList regs) {
- ASSERT(!scratch.is(kScratchRegister));
- ASSERT(!base.is(kScratchRegister));
- ASSERT(!base.is(scratch));
- ASSERT((regs & ~kJSCallerSaved) == 0);
- // Copy the content of the stack to the memory location and adjust base.
- for (int i = kNumJSCallerSaved - 1; i >= 0; i--) {
- int r = JSCallerSavedCode(i);
- if ((regs & (1 << r)) != 0) {
- movq(scratch, Operand(base, 0));
- ExternalReference reg_addr =
- ExternalReference(Debug_Address::Register(i));
- movq(kScratchRegister, reg_addr);
- movq(Operand(kScratchRegister, 0), scratch);
- lea(base, Operand(base, kPointerSize));
- }
- }
-}
+#ifdef ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::DebugBreak() {
ASSERT(allow_stub_calls());
xor_(rax, rax); // no arguments
}
-void MacroAssembler::EnterExitFramePrologue(ExitFrame::Mode mode,
- bool save_rax) {
+void MacroAssembler::EnterExitFramePrologue(bool save_rax) {
// Setup the frame structure on the stack.
// All constants are relative to the frame pointer of the exit frame.
ASSERT(ExitFrameConstants::kCallerSPDisplacement == +2 * kPointerSize);
push(rbp);
movq(rbp, rsp);
- // Reserve room for entry stack pointer and push the debug marker.
+ // Reserve room for entry stack pointer and push the code object.
ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize);
push(Immediate(0)); // Saved entry sp, patched before call.
movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT);
store_rax(context_address);
}
-void MacroAssembler::EnterExitFrameEpilogue(ExitFrame::Mode mode,
- int result_size,
+void MacroAssembler::EnterExitFrameEpilogue(int result_size,
int argc) {
-#ifdef ENABLE_DEBUGGER_SUPPORT
- // Save the state of all registers to the stack from the memory
- // location. This is needed to allow nested break points.
- if (mode == ExitFrame::MODE_DEBUG) {
- // TODO(1243899): This should be symmetric to
- // CopyRegistersFromStackToMemory() but it isn't! esp is assumed
- // correct here, but computed for the other call. Very error
- // prone! FIX THIS. Actually there are deeper problems with
- // register saving than this asymmetry (see the bug report
- // associated with this issue).
- PushRegistersFromMemory(kJSCallerSaved);
- }
-#endif
-
#ifdef _WIN64
// Reserve space on stack for result and argument structures, if necessary.
int result_stack_space = (result_size < 2) ? 0 : result_size * kPointerSize;
}
-void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode, int result_size) {
- EnterExitFramePrologue(mode, true);
+void MacroAssembler::EnterExitFrame(int result_size) {
+ EnterExitFramePrologue(true);
// Setup argv in callee-saved register r12. It is reused in LeaveExitFrame,
// so it must be retained across the C-call.
int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
lea(r12, Operand(rbp, r14, times_pointer_size, offset));
- EnterExitFrameEpilogue(mode, result_size, 2);
+ EnterExitFrameEpilogue(result_size, 2);
}
-void MacroAssembler::EnterApiExitFrame(ExitFrame::Mode mode,
- int stack_space,
+void MacroAssembler::EnterApiExitFrame(int stack_space,
int argc,
int result_size) {
- EnterExitFramePrologue(mode, false);
+ EnterExitFramePrologue(false);
// Setup argv in callee-saved register r12. It is reused in LeaveExitFrame,
// so it must be retained across the C-call.
int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
lea(r12, Operand(rbp, (stack_space * kPointerSize) + offset));
- EnterExitFrameEpilogue(mode, result_size, argc);
+ EnterExitFrameEpilogue(result_size, argc);
}
-void MacroAssembler::LeaveExitFrame(ExitFrame::Mode mode, int result_size) {
+void MacroAssembler::LeaveExitFrame(int result_size) {
// Registers:
// r12 : argv
-#ifdef ENABLE_DEBUGGER_SUPPORT
- // Restore the memory copy of the registers by digging them out from
- // the stack. This is needed to allow nested break points.
- if (mode == ExitFrame::MODE_DEBUG) {
- // It's okay to clobber register rbx below because we don't need
- // the function pointer after this.
- const int kCallerSavedSize = kNumJSCallerSaved * kPointerSize;
- int kOffset = ExitFrameConstants::kCodeOffset - kCallerSavedSize;
- lea(rbx, Operand(rbp, kOffset));
- CopyRegistersFromStackToMemory(rbx, rcx, kJSCallerSaved);
- }
-#endif
// Get the return address from the stack and restore the frame pointer.
movq(rcx, Operand(rbp, 1 * kPointerSize));
// ---------------------------------------------------------------------------
// Debugger Support
- void SaveRegistersToMemory(RegList regs);
- void RestoreRegistersFromMemory(RegList regs);
- void PushRegistersFromMemory(RegList regs);
- void PopRegistersToMemory(RegList regs);
- void CopyRegistersFromStackToMemory(Register base,
- Register scratch,
- RegList regs);
void DebugBreak();
#endif
// debug mode. Expects the number of arguments in register rax and
// sets up the number of arguments in register rdi and the pointer
// to the first argument in register rsi.
- void EnterExitFrame(ExitFrame::Mode mode, int result_size = 1);
+ void EnterExitFrame(int result_size = 1);
- void EnterApiExitFrame(ExitFrame::Mode mode,
- int stack_space,
+ void EnterApiExitFrame(int stack_space,
int argc,
int result_size = 1);
// Leave the current exit frame. Expects/provides the return value in
// register rax:rdx (untouched) and the pointer to the first
// argument in register rsi.
- void LeaveExitFrame(ExitFrame::Mode mode, int result_size = 1);
+ void LeaveExitFrame(int result_size = 1);
// ---------------------------------------------------------------------------
void EnterFrame(StackFrame::Type type);
void LeaveFrame(StackFrame::Type type);
- void EnterExitFramePrologue(ExitFrame::Mode mode, bool save_rax);
- void EnterExitFrameEpilogue(ExitFrame::Mode mode, int result_size, int argc);
+ void EnterExitFramePrologue(bool save_rax);
+ void EnterExitFrameEpilogue(int result_size, int argc);
// Allocation support helpers.
// Loads the top of new-space into the result register.
# BUG(355): Test crashes on ARM.
test-log/ProfLazyMode: SKIP
-# BUG(845)
-test-debug/GCDuringBreakPointProcessing: SKIP
-test-debug/BreakPointICCallWithGC: SKIP
-
[ $arch == mips ]
test-accessors: SKIP
test-alloc: SKIP
// Scavenge.
Heap::CollectGarbage(0, v8::internal::NEW_SPACE);
} else {
- // Mark sweep (and perhaps compact).
+ // Mark sweep compact.
Heap::CollectAllGarbage(true);
}
}
}
+// Test that a break point can be set at an IC call location and survive a GC.
+TEST(BreakPointConstructCallWithGC) {
+ break_point_hit_count = 0;
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointCollectGarbage,
+ v8::Undefined());
+ v8::Script::Compile(v8::String::New("function bar(){ this.x = 1;}"))->Run();
+ v8::Script::Compile(v8::String::New(
+ "function foo(){return new bar(1).x;}"))->Run();
+ v8::Local<v8::Function> foo =
+ v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("foo")));
+
+ // Run without breakpoints.
+ CHECK_EQ(1, foo->Call(env->Global(), 0, NULL)->Int32Value());
+ CHECK_EQ(0, break_point_hit_count);
+
+ // Run with breakpoint.
+ int bp = SetBreakPoint(foo, 0);
+ CHECK_EQ(1, foo->Call(env->Global(), 0, NULL)->Int32Value());
+ CHECK_EQ(1, break_point_hit_count);
+ CHECK_EQ(1, foo->Call(env->Global(), 0, NULL)->Int32Value());
+ CHECK_EQ(2, break_point_hit_count);
+
+ // Run without breakpoints.
+ ClearBreakPoint(bp);
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
// Test that a break point can be set at a return store location.
TEST(BreakPointReturn) {
break_point_hit_count = 0;
}
-#ifdef ENABLE_DEBUGGER_SUPPORT
-static int register_code(int reg) {
- return Debug::k_register_address << kDebugIdShift | reg;
-}
-#endif // ENABLE_DEBUGGER_SUPPORT
-
-
TEST(ExternalReferenceEncoder) {
StatsTable::SetCounterFunction(counter_function);
Heap::Setup(false);
Encode(encoder, Runtime::kAbort));
CHECK_EQ(make_code(IC_UTILITY, IC::kLoadCallbackProperty),
Encode(encoder, IC_Utility(IC::kLoadCallbackProperty)));
-#ifdef ENABLE_DEBUGGER_SUPPORT
- CHECK_EQ(make_code(DEBUG_ADDRESS, register_code(3)),
- Encode(encoder, Debug_Address(Debug::k_register_address, 3)));
-#endif // ENABLE_DEBUGGER_SUPPORT
ExternalReference keyed_load_function_prototype =
ExternalReference(&Counters::keyed_load_function_prototype);
CHECK_EQ(make_code(STATS_COUNTER, Counters::k_keyed_load_function_prototype),
decoder.Decode(make_code(RUNTIME_FUNCTION, Runtime::kAbort)));
CHECK_EQ(AddressOf(IC_Utility(IC::kLoadCallbackProperty)),
decoder.Decode(make_code(IC_UTILITY, IC::kLoadCallbackProperty)));
-#ifdef ENABLE_DEBUGGER_SUPPORT
- CHECK_EQ(AddressOf(Debug_Address(Debug::k_register_address, 3)),
- decoder.Decode(make_code(DEBUG_ADDRESS, register_code(3))));
-#endif // ENABLE_DEBUGGER_SUPPORT
ExternalReference keyed_load_function =
ExternalReference(&Counters::keyed_load_function_prototype);
CHECK_EQ(keyed_load_function.address(),