Version 2.2.16.
Remove the SetExternalStringDiposeCallback API. Changed the disposal of external string resources to call a virtual Dispose method on the resource.
Added support for more precise break points when debugging and stepping.
Memory usage improvements on all platforms.
git-svn-id: http://v8.googlecode.com/svn/trunk@4829 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/ChangeLog b/ChangeLog
index 321ba00..e277c7a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,15 @@
+2010-06-07: Version 2.2.16
+
+ Remove the SetExternalStringDiposeCallback API. Changed the
+ disposal of external string resources to call a virtual Dispose
+ method on the resource.
+
+ Added support for more precise break points when debugging and
+ stepping.
+
+ Memory usage improvements on all platforms.
+
+
2010-06-07: Version 2.2.15
Add an API to control the disposal of external string resources.
diff --git a/include/v8.h b/include/v8.h
index fe01e30..24b4cbe 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -134,6 +134,7 @@
class Arguments;
class Object;
+class Heap;
class Top;
}
@@ -513,6 +514,7 @@
class V8EXPORT ScriptData { // NOLINT
public:
virtual ~ScriptData() { }
+
/**
* Pre-compiles the specified script (context-independent).
*
@@ -522,6 +524,16 @@
static ScriptData* PreCompile(const char* input, int length);
/**
+ * Pre-compiles the specified script (context-independent).
+ *
+ * NOTE: Pre-compilation using this method cannot happen on another thread
+ * without using Lockers.
+ *
+ * \param source Script source code.
+ */
+ static ScriptData* PreCompile(Handle<String> source);
+
+ /**
* Load previous pre-compilation data.
*
* \param data Pointer to data returned by a call to Data() of a previous
@@ -1026,12 +1038,24 @@
class V8EXPORT ExternalStringResourceBase {
public:
virtual ~ExternalStringResourceBase() {}
+
protected:
ExternalStringResourceBase() {}
+
+ /**
+ * Internally V8 will call this Dispose method when the external string
+ * resource is no longer needed. The default implementation will use the
+ * delete operator. This method can be overridden in subclasses to
+ * control how allocated external string resources are disposed.
+ */
+ virtual void Dispose() { delete this; }
+
private:
// Disallow copying and assigning.
ExternalStringResourceBase(const ExternalStringResourceBase&);
void operator=(const ExternalStringResourceBase&);
+
+ friend class v8::internal::Heap;
};
/**
@@ -1048,10 +1072,17 @@
* buffer.
*/
virtual ~ExternalStringResource() {}
- /** The string data from the underlying buffer.*/
+
+ /**
+ * The string data from the underlying buffer.
+ */
virtual const uint16_t* data() const = 0;
- /** The length of the string. That is, the number of two-byte characters.*/
+
+ /**
+ * The length of the string. That is, the number of two-byte characters.
+ */
virtual size_t length() const = 0;
+
protected:
ExternalStringResource() {}
};
@@ -1123,12 +1154,10 @@
/**
* Creates a new external string using the data defined in the given
* resource. When the external string is no longer live on V8's heap the
- * resource will be disposed. If a disposal callback has been set using
- * SetExternalStringDiposeCallback this callback will be called to dispose
- * the resource. Otherwise, V8 will dispose the resource using the C++ delete
- * operator. The caller of this function should not otherwise delete or
- * modify the resource. Neither should the underlying buffer be deallocated
- * or modified except through the destructor of the external string resource.
+ * resource will be disposed by calling its Dispose method. The caller of
+ * this function should not otherwise delete or modify the resource. Neither
+ * should the underlying buffer be deallocated or modified except through the
+ * destructor of the external string resource.
*/
static Local<String> NewExternal(ExternalStringResource* resource);
@@ -1146,12 +1175,10 @@
/**
* Creates a new external string using the ascii data defined in the given
* resource. When the external string is no longer live on V8's heap the
- * resource will be disposed. If a disposal callback has been set using
- * SetExternalStringDiposeCallback this callback will be called to dispose
- * the resource. Otherwise, V8 will dispose the resource using the C++ delete
- * operator. The caller of this function should not otherwise delete or
- * modify the resource. Neither should the underlying buffer be deallocated
- * or modified except through the destructor of the external string resource.
+ * resource will be disposed by calling its Dispose method. The caller of
+ * this function should not otherwise delete or modify the resource. Neither
+ * should the underlying buffer be deallocated or modified except through the
+ * destructor of the external string resource.
*/
static Local<String> NewExternal(ExternalAsciiStringResource* resource);
@@ -1251,10 +1278,6 @@
};
-typedef void (*ExternalStringDiposeCallback)
- (String::ExternalStringResourceBase* resource);
-
-
/**
* A JavaScript number value (ECMA-262, 4.3.20)
*/
@@ -2472,15 +2495,6 @@
static void RemoveMessageListeners(MessageCallback that);
/**
- * Set a callback to be called when an external string is no longer live on
- * V8's heap. The resource will no longer be needed by V8 and the embedder
- * can dispose of if. If this callback is not set V8 will free the resource
- * using the C++ delete operator.
- */
- static void SetExternalStringDiposeCallback(
- ExternalStringDiposeCallback that);
-
- /**
* Sets V8 flags from a string.
*/
static void SetFlagsFromString(const char* str, int length);
diff --git a/src/api.cc b/src/api.cc
index dfdd7bd..cb5e96d 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -1120,6 +1120,12 @@
}
+ScriptData* ScriptData::PreCompile(v8::Handle<String> source) {
+ i::Handle<i::String> str = Utils::OpenHandle(*source);
+ return i::PreParse(str, NULL, NULL);
+}
+
+
ScriptData* ScriptData::New(const char* data, int length) {
// Return an empty ScriptData if the length is obviously invalid.
if (length % sizeof(unsigned) != 0) {
@@ -3692,14 +3698,6 @@
}
-void V8::SetExternalStringDiposeCallback(
- ExternalStringDiposeCallback callback) {
- if (IsDeadCheck("v8::V8::SetExternalStringDiposeCallback()"))
- return;
- i::Heap::SetExternalStringDiposeCallback(callback);
-}
-
-
void V8::SetCounterFunction(CounterLookupCallback callback) {
if (IsDeadCheck("v8::V8::SetCounterFunction()")) return;
i::StatsTable::SetCounterFunction(callback);
diff --git a/src/arm/assembler-arm-inl.h b/src/arm/assembler-arm-inl.h
index e292cef..8ca9126 100644
--- a/src/arm/assembler-arm-inl.h
+++ b/src/arm/assembler-arm-inl.h
@@ -116,9 +116,10 @@
Address RelocInfo::call_address() {
- ASSERT(IsPatchedReturnSequence());
- // The 2 instructions offset assumes patched return sequence.
- ASSERT(IsJSReturn(rmode()));
+ // The 2 instructions offset assumes patched debug break slot or return
+ // sequence.
+ ASSERT((IsJSReturn(rmode()) && IsPatchedReturnSequence()) ||
+ (IsDebugBreakSlot(rmode()) && IsPatchedDebugBreakSlotSequence()));
return Memory::Address_at(pc_ + 2 * Assembler::kInstrSize);
}
@@ -168,6 +169,12 @@
}
+bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
+ Instr current_instr = Assembler::instr_at(pc_);
+ return !Assembler::IsNop(current_instr, 2);
+}
+
+
void RelocInfo::Visit(ObjectVisitor* visitor) {
RelocInfo::Mode mode = rmode();
if (mode == RelocInfo::EMBEDDED_OBJECT) {
@@ -178,8 +185,10 @@
visitor->VisitExternalReference(target_reference_address());
#ifdef ENABLE_DEBUGGER_SUPPORT
} else if (Debug::has_break_points() &&
- RelocInfo::IsJSReturn(mode) &&
- IsPatchedReturnSequence()) {
+ ((RelocInfo::IsJSReturn(mode) &&
+ IsPatchedReturnSequence()) ||
+ (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence()))) {
visitor->VisitDebugTarget(this);
#endif
} else if (mode == RelocInfo::RUNTIME_ENTRY) {
diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc
index 846464a..025f28e 100644
--- a/src/arm/assembler-arm.cc
+++ b/src/arm/assembler-arm.cc
@@ -2040,6 +2040,13 @@
}
+void Assembler::RecordDebugBreakSlot() {
+ WriteRecordedPositions();
+ CheckBuffer();
+ RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
+}
+
+
void Assembler::RecordComment(const char* msg) {
if (FLAG_debug_code) {
CheckBuffer();
@@ -2062,13 +2069,16 @@
}
-void Assembler::WriteRecordedPositions() {
+bool Assembler::WriteRecordedPositions() {
+ bool written = false;
+
// Write the statement position if it is different from what was written last
// time.
if (current_statement_position_ != written_statement_position_) {
CheckBuffer();
RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_);
written_statement_position_ = current_statement_position_;
+ written = true;
}
// Write the position if it is different from what was written last time and
@@ -2078,7 +2088,11 @@
CheckBuffer();
RecordRelocInfo(RelocInfo::POSITION, current_position_);
written_position_ = current_position_;
+ written = true;
}
+
+ // Return whether something was written.
+ return written;
}
@@ -2135,9 +2149,10 @@
void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
RelocInfo rinfo(pc_, rmode, data); // we do not try to reuse pool constants
- if (rmode >= RelocInfo::JS_RETURN && rmode <= RelocInfo::STATEMENT_POSITION) {
+ if (rmode >= RelocInfo::JS_RETURN && rmode <= RelocInfo::DEBUG_BREAK_SLOT) {
// Adjust code for new modes.
- ASSERT(RelocInfo::IsJSReturn(rmode)
+ ASSERT(RelocInfo::IsDebugBreakSlot(rmode)
+ || RelocInfo::IsJSReturn(rmode)
|| RelocInfo::IsComment(rmode)
|| RelocInfo::IsPosition(rmode));
// These modes do not need an entry in the constant pool.
diff --git a/src/arm/assembler-arm.h b/src/arm/assembler-arm.h
index d0dee56..e5d42f9 100644
--- a/src/arm/assembler-arm.h
+++ b/src/arm/assembler-arm.h
@@ -629,22 +629,39 @@
// Distance between start of patched return sequence and the emitted address
// to jump to.
#ifdef USE_BLX
- // Return sequence is:
+ // Patched return sequence is:
// ldr ip, [pc, #0] @ emited address and start
// blx ip
static const int kPatchReturnSequenceAddressOffset = 0 * kInstrSize;
#else
- // Return sequence is:
+ // Patched return sequence is:
// mov lr, pc @ start of sequence
// ldr pc, [pc, #-4] @ emited address
static const int kPatchReturnSequenceAddressOffset = kInstrSize;
#endif
+ // Distance between start of patched debug break slot and the emitted address
+ // to jump to.
+#ifdef USE_BLX
+ // Patched debug break slot code is:
+ // ldr ip, [pc, #0] @ emited address and start
+ // blx ip
+ static const int kPatchDebugBreakSlotAddressOffset = 0 * kInstrSize;
+#else
+ // Patched debug break slot code is:
+ // mov lr, pc @ start of sequence
+ // ldr pc, [pc, #-4] @ emited address
+ static const int kPatchDebugBreakSlotAddressOffset = kInstrSize;
+#endif
+
// Difference between address of current opcode and value read from pc
// register.
static const int kPcLoadDelta = 8;
- static const int kJSReturnSequenceLength = 4;
+ static const int kJSReturnSequenceInstructions = 4;
+ static const int kDebugBreakSlotInstructions = 3;
+ static const int kDebugBreakSlotLength =
+ kDebugBreakSlotInstructions * kInstrSize;
// ---------------------------------------------------------------------------
// Code generation
@@ -981,13 +998,16 @@
// Mark address of the ExitJSFrame code.
void RecordJSReturn();
+ // Mark address of a debug break slot.
+ void RecordDebugBreakSlot();
+
// Record a comment relocation entry that can be used by a disassembler.
// Use --debug_code to enable.
void RecordComment(const char* msg);
void RecordPosition(int pos);
void RecordStatementPosition(int pos);
- void WriteRecordedPositions();
+ bool WriteRecordedPositions();
int pc_offset() const { return pc_ - buffer_; }
int current_position() const { return current_position_; }
diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc
index d6fdf45..8d4bf14 100644
--- a/src/arm/codegen-arm.cc
+++ b/src/arm/codegen-arm.cc
@@ -386,8 +386,10 @@
// the add instruction the add will generate two instructions.
int return_sequence_length =
masm_->InstructionsGeneratedSince(&check_exit_codesize);
- CHECK(return_sequence_length == Assembler::kJSReturnSequenceLength ||
- return_sequence_length == Assembler::kJSReturnSequenceLength + 1);
+ CHECK(return_sequence_length ==
+ Assembler::kJSReturnSequenceInstructions ||
+ return_sequence_length ==
+ Assembler::kJSReturnSequenceInstructions + 1);
#endif
}
}
diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h
index 6a33667..cabdf00 100644
--- a/src/arm/codegen-arm.h
+++ b/src/arm/codegen-arm.h
@@ -226,7 +226,9 @@
bool is_toplevel,
Handle<Script> script);
- static void RecordPositions(MacroAssembler* masm, int pos);
+ static bool RecordPositions(MacroAssembler* masm,
+ int pos,
+ bool right_here = false);
// Accessors
MacroAssembler* masm() { return masm_; }
diff --git a/src/arm/debug-arm.cc b/src/arm/debug-arm.cc
index 69fc504..65f5eea 100644
--- a/src/arm/debug-arm.cc
+++ b/src/arm/debug-arm.cc
@@ -57,7 +57,7 @@
// #endif
// <debug break return code entry point address>
// bktp 0
- CodePatcher patcher(rinfo()->pc(), 4);
+ CodePatcher patcher(rinfo()->pc(), Assembler::kJSReturnSequenceInstructions);
#ifdef USE_BLX
patcher.masm()->ldr(v8::internal::ip, MemOperand(v8::internal::pc, 0));
patcher.masm()->blx(v8::internal::ip);
@@ -73,17 +73,59 @@
// Restore the JS frame exit code.
void BreakLocationIterator::ClearDebugBreakAtReturn() {
rinfo()->PatchCode(original_rinfo()->pc(),
- Assembler::kJSReturnSequenceLength);
+ Assembler::kJSReturnSequenceInstructions);
}
-// A debug break in the exit code is identified by a call.
+// A debug break in the frame exit code is identified by the JS frame exit code
+// having been patched with a call instruction.
bool Debug::IsDebugBreakAtReturn(RelocInfo* rinfo) {
ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()));
return rinfo->IsPatchedReturnSequence();
}
+bool BreakLocationIterator::IsDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ // Check whether the debug break slot instructions have been patched.
+ return rinfo()->IsPatchedDebugBreakSlotSequence();
+}
+
+
+void BreakLocationIterator::SetDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ // Patch the code changing the debug break slot code from
+ // mov r2, r2
+ // mov r2, r2
+ // mov r2, r2
+ // to a call to the debug break slot code.
+ // #if USE_BLX
+ // ldr ip, [pc, #0]
+ // blx ip
+ // #else
+ // mov lr, pc
+ // ldr pc, [pc, #-4]
+ // #endif
+ // <debug break slot code entry point address>
+ CodePatcher patcher(rinfo()->pc(), Assembler::kDebugBreakSlotInstructions);
+#ifdef USE_BLX
+ patcher.masm()->ldr(v8::internal::ip, MemOperand(v8::internal::pc, 0));
+ patcher.masm()->blx(v8::internal::ip);
+#else
+ patcher.masm()->mov(v8::internal::lr, v8::internal::pc);
+ patcher.masm()->ldr(v8::internal::pc, MemOperand(v8::internal::pc, -4));
+#endif
+ patcher.Emit(Debug::debug_break_return()->entry());
+}
+
+
+void BreakLocationIterator::ClearDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ rinfo()->PatchCode(original_rinfo()->pc(),
+ Assembler::kDebugBreakSlotInstructions);
+}
+
+
#define __ ACCESS_MASM(masm)
@@ -220,10 +262,33 @@
}
+void Debug::GenerateSlot(MacroAssembler* masm) {
+ // Generate enough nop's to make space for a call instruction. Avoid emitting
+ // the constant pool in the debug break slot code.
+ Assembler::BlockConstPoolScope block_const_pool(masm);
+ Label check_codesize;
+ __ bind(&check_codesize);
+ __ RecordDebugBreakSlot();
+ for (int i = 0; i < Assembler::kDebugBreakSlotInstructions; i++) {
+ __ nop(2);
+ }
+ ASSERT_EQ(Assembler::kDebugBreakSlotInstructions,
+ masm->InstructionsGeneratedSince(&check_codesize));
+}
+
+
+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);
+}
+
+
void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
masm->Abort("LiveEdit frame dropping is not supported on arm");
}
+
void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
masm->Abort("LiveEdit frame dropping is not supported on arm");
}
diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc
index 5fa412f..e2cd84d 100644
--- a/src/arm/full-codegen-arm.cc
+++ b/src/arm/full-codegen-arm.cc
@@ -238,8 +238,10 @@
// add instruction the add will generate two instructions.
int return_sequence_length =
masm_->InstructionsGeneratedSince(&check_exit_codesize);
- CHECK(return_sequence_length == Assembler::kJSReturnSequenceLength ||
- return_sequence_length == Assembler::kJSReturnSequenceLength + 1);
+ CHECK(return_sequence_length ==
+ Assembler::kJSReturnSequenceInstructions ||
+ return_sequence_length ==
+ Assembler::kJSReturnSequenceInstructions + 1);
#endif
}
}
diff --git a/src/assembler.cc b/src/assembler.cc
index 871ca86..6a46f61 100644
--- a/src/assembler.cc
+++ b/src/assembler.cc
@@ -449,6 +449,11 @@
return "external reference";
case RelocInfo::INTERNAL_REFERENCE:
return "internal reference";
+ case RelocInfo::DEBUG_BREAK_SLOT:
+#ifndef ENABLE_DEBUGGER_SUPPORT
+ UNREACHABLE();
+#endif
+ return "debug break slot";
case RelocInfo::NUMBER_OF_MODES:
UNREACHABLE();
return "number_of_modes";
@@ -513,6 +518,7 @@
case STATEMENT_POSITION:
case EXTERNAL_REFERENCE:
case INTERNAL_REFERENCE:
+ case DEBUG_BREAK_SLOT:
case NONE:
break;
case NUMBER_OF_MODES:
diff --git a/src/assembler.h b/src/assembler.h
index 74613b3..fb75d6d 100644
--- a/src/assembler.h
+++ b/src/assembler.h
@@ -118,9 +118,9 @@
enum Mode {
// Please note the order is important (see IsCodeTarget, IsGCRelocMode).
CONSTRUCT_CALL, // code target that is a call to a JavaScript constructor.
- CODE_TARGET_CONTEXT, // code target used for contextual loads.
- DEBUG_BREAK,
- CODE_TARGET, // code target which is not any of the above.
+ CODE_TARGET_CONTEXT, // Code target used for contextual loads.
+ DEBUG_BREAK, // Code target for the debugger statement.
+ CODE_TARGET, // Code target which is not any of the above.
EMBEDDED_OBJECT,
// Everything after runtime_entry (inclusive) is not GC'ed.
@@ -129,6 +129,7 @@
COMMENT,
POSITION, // See comment for kNoPosition above.
STATEMENT_POSITION, // See comment for kNoPosition above.
+ DEBUG_BREAK_SLOT, // Additional code inserted for debug break slot.
EXTERNAL_REFERENCE, // The address of an external C++ function.
INTERNAL_REFERENCE, // An address inside the same function.
@@ -174,6 +175,9 @@
static inline bool IsInternalReference(Mode mode) {
return mode == INTERNAL_REFERENCE;
}
+ static inline bool IsDebugBreakSlot(Mode mode) {
+ return mode == DEBUG_BREAK_SLOT;
+ }
static inline int ModeMask(Mode mode) { return 1 << mode; }
// Accessors
@@ -243,6 +247,10 @@
// with a call to the debugger.
INLINE(bool IsPatchedReturnSequence());
+ // Check whether this debug break slot has been patched with a call to the
+ // debugger.
+ INLINE(bool IsPatchedDebugBreakSlotSequence());
+
#ifdef ENABLE_DISASSEMBLER
// Printing
static const char* RelocModeName(Mode rmode);
diff --git a/src/ast.h b/src/ast.h
index a3a9734..b9a7a3d 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -1469,10 +1469,14 @@
public:
Conditional(Expression* condition,
Expression* then_expression,
- Expression* else_expression)
+ Expression* else_expression,
+ int then_expression_position,
+ int else_expression_position)
: condition_(condition),
then_expression_(then_expression),
- else_expression_(else_expression) { }
+ else_expression_(else_expression),
+ then_expression_position_(then_expression_position),
+ else_expression_position_(else_expression_position) { }
virtual void Accept(AstVisitor* v);
@@ -1482,10 +1486,15 @@
Expression* then_expression() const { return then_expression_; }
Expression* else_expression() const { return else_expression_; }
+ int then_expression_position() { return then_expression_position_; }
+ int else_expression_position() { return else_expression_position_; }
+
private:
Expression* condition_;
Expression* then_expression_;
Expression* else_expression_;
+ int then_expression_position_;
+ int else_expression_position_;
};
diff --git a/src/builtins.cc b/src/builtins.cc
index 0677809..7116dc9 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -1360,10 +1360,17 @@
Debug::GenerateStubNoRegistersDebugBreak(masm);
}
+
+static void Generate_Slot_DebugBreak(MacroAssembler* masm) {
+ Debug::GenerateSlotDebugBreak(masm);
+}
+
+
static void Generate_PlainReturn_LiveEdit(MacroAssembler* masm) {
Debug::GeneratePlainReturnLiveEdit(masm);
}
+
static void Generate_FrameDropper_LiveEdit(MacroAssembler* masm) {
Debug::GenerateFrameDropperLiveEdit(masm);
}
diff --git a/src/builtins.h b/src/builtins.h
index dd2e3cb..1fab375 100644
--- a/src/builtins.h
+++ b/src/builtins.h
@@ -127,6 +127,7 @@
V(KeyedLoadIC_DebugBreak, KEYED_LOAD_IC, DEBUG_BREAK) \
V(StoreIC_DebugBreak, STORE_IC, DEBUG_BREAK) \
V(KeyedStoreIC_DebugBreak, KEYED_STORE_IC, DEBUG_BREAK) \
+ V(Slot_DebugBreak, BUILTIN, DEBUG_BREAK) \
V(PlainReturn_LiveEdit, BUILTIN, DEBUG_BREAK) \
V(FrameDropper_LiveEdit, BUILTIN, DEBUG_BREAK)
#else
diff --git a/src/checks.h b/src/checks.h
index cdcd18a..c2e40ba 100644
--- a/src/checks.h
+++ b/src/checks.h
@@ -285,14 +285,16 @@
#define ASSERT_RESULT(expr) CHECK(expr)
#define ASSERT(condition) CHECK(condition)
#define ASSERT_EQ(v1, v2) CHECK_EQ(v1, v2)
-#define ASSERT_NE(v1, v2) CHECK_NE(v1, v2)
+#define ASSERT_NE(v1, v2) CHECK_NE(v1, v2)
+#define ASSERT_GE(v1, v2) CHECK_GE(v1, v2)
#define STATIC_ASSERT(test) STATIC_CHECK(test)
#define SLOW_ASSERT(condition) if (FLAG_enable_slow_asserts) CHECK(condition)
#else
#define ASSERT_RESULT(expr) (expr)
#define ASSERT(condition) ((void) 0)
#define ASSERT_EQ(v1, v2) ((void) 0)
-#define ASSERT_NE(v1, v2) ((void) 0)
+#define ASSERT_NE(v1, v2) ((void) 0)
+#define ASSERT_GE(v1, v2) ((void) 0)
#define STATIC_ASSERT(test) ((void) 0)
#define SLOW_ASSERT(condition) ((void) 0)
#endif
diff --git a/src/codegen.cc b/src/codegen.cc
index ff2fa03..686e173 100644
--- a/src/codegen.cc
+++ b/src/codegen.cc
@@ -415,32 +415,41 @@
}
-void CodeGenerator::RecordPositions(MacroAssembler* masm, int pos) {
+bool CodeGenerator::RecordPositions(MacroAssembler* masm,
+ int pos,
+ bool right_here) {
if (pos != RelocInfo::kNoPosition) {
masm->RecordStatementPosition(pos);
masm->RecordPosition(pos);
+ if (right_here) {
+ return masm->WriteRecordedPositions();
+ }
}
+ return false;
}
void CodeGenerator::CodeForFunctionPosition(FunctionLiteral* fun) {
- if (FLAG_debug_info) RecordPositions(masm(), fun->start_position());
+ if (FLAG_debug_info) RecordPositions(masm(), fun->start_position(), false);
}
void CodeGenerator::CodeForReturnPosition(FunctionLiteral* fun) {
- if (FLAG_debug_info) RecordPositions(masm(), fun->end_position());
+ if (FLAG_debug_info) RecordPositions(masm(), fun->end_position(), false);
}
void CodeGenerator::CodeForStatementPosition(Statement* stmt) {
- if (FLAG_debug_info) RecordPositions(masm(), stmt->statement_pos());
+ if (FLAG_debug_info) RecordPositions(masm(), stmt->statement_pos(), false);
}
+
void CodeGenerator::CodeForDoWhileConditionPosition(DoWhileStatement* stmt) {
- if (FLAG_debug_info) RecordPositions(masm(), stmt->condition_position());
+ if (FLAG_debug_info)
+ RecordPositions(masm(), stmt->condition_position(), false);
}
+
void CodeGenerator::CodeForSourcePosition(int pos) {
if (FLAG_debug_info && pos != RelocInfo::kNoPosition) {
masm()->RecordPosition(pos);
diff --git a/src/compilation-cache.cc b/src/compilation-cache.cc
index cec10fd..14252a5 100644
--- a/src/compilation-cache.cc
+++ b/src/compilation-cache.cc
@@ -79,6 +79,8 @@
// young generation.
void Age();
+ bool HasFunction(SharedFunctionInfo* function_info);
+
// GC support.
void Iterate(ObjectVisitor* v);
@@ -204,6 +206,27 @@
}
+bool CompilationSubCache::HasFunction(SharedFunctionInfo* function_info) {
+ if (function_info->script()->IsUndefined() ||
+ Script::cast(function_info->script())->source()->IsUndefined()) {
+ return false;
+ }
+
+ String* source =
+ String::cast(Script::cast(function_info->script())->source());
+ // Check all generations.
+ for (int generation = 0; generation < generations(); generation++) {
+ if (tables_[generation]->IsUndefined()) continue;
+
+ CompilationCacheTable* table =
+ CompilationCacheTable::cast(tables_[generation]);
+ Object* object = table->Lookup(source);
+ if (object->IsSharedFunctionInfo()) return true;
+ }
+ return false;
+}
+
+
void CompilationSubCache::Age() {
// Age the generations implicitly killing off the oldest.
for (int i = generations_ - 1; i > 0; i--) {
@@ -506,6 +529,11 @@
}
+bool CompilationCache::HasFunction(SharedFunctionInfo* function_info) {
+ return script.HasFunction(function_info);
+}
+
+
void CompilationCache::Iterate(ObjectVisitor* v) {
for (int i = 0; i < kSubCacheCount; i++) {
subcaches[i]->Iterate(v);
diff --git a/src/compilation-cache.h b/src/compilation-cache.h
index 6358a26..583f04c 100644
--- a/src/compilation-cache.h
+++ b/src/compilation-cache.h
@@ -79,6 +79,9 @@
// Clear the cache - also used to initialize the cache at startup.
static void Clear();
+
+ static bool HasFunction(SharedFunctionInfo* function_info);
+
// GC support.
static void Iterate(ObjectVisitor* v);
diff --git a/src/compiler.cc b/src/compiler.cc
index ca92ed9..ebb9743 100755
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -601,6 +601,7 @@
lit->has_only_simple_this_property_assignments(),
*lit->this_property_assignments());
function_info->set_try_full_codegen(lit->try_full_codegen());
+ function_info->set_allows_lazy_compilation(lit->AllowsLazyCompilation());
}
diff --git a/src/cpu-profiler.cc b/src/cpu-profiler.cc
index 2968081..c8d29f8 100644
--- a/src/cpu-profiler.cc
+++ b/src/cpu-profiler.cc
@@ -294,7 +294,8 @@
int CpuProfiler::GetProfilesCount() {
ASSERT(singleton_ != NULL);
// The count of profiles doesn't depend on a security token.
- return singleton_->profiles_->Profiles(CodeEntry::kNoSecurityToken)->length();
+ return singleton_->profiles_->Profiles(
+ TokenEnumerator::kNoSecurityToken)->length();
}
@@ -380,7 +381,7 @@
void CpuProfiler::FunctionCreateEvent(JSFunction* function) {
- int security_token_id = CodeEntry::kNoSecurityToken;
+ int security_token_id = TokenEnumerator::kNoSecurityToken;
if (function->unchecked_context()->IsContext()) {
security_token_id = singleton_->token_enumerator_->GetTokenId(
function->context()->global_context()->security_token());
@@ -476,9 +477,10 @@
CpuProfile* CpuProfiler::StopCollectingProfile(const char* title) {
const double actual_sampling_rate = generator_->actual_sampling_rate();
StopProcessorIfLastProfile();
- CpuProfile* result = profiles_->StopProfiling(CodeEntry::kNoSecurityToken,
- title,
- actual_sampling_rate);
+ CpuProfile* result =
+ profiles_->StopProfiling(TokenEnumerator::kNoSecurityToken,
+ title,
+ actual_sampling_rate);
if (result != NULL) {
result->Print();
}
diff --git a/src/d8.cc b/src/d8.cc
index a69320a..7fd7925 100644
--- a/src/d8.cc
+++ b/src/d8.cc
@@ -576,6 +576,9 @@
void Shell::RunShell() {
LineEditor* editor = LineEditor::Get();
printf("V8 version %s [console: %s]\n", V8::GetVersion(), editor->name());
+ if (i::FLAG_debugger) {
+ printf("JavaScript debugger enabled\n");
+ }
editor->Open();
while (true) {
Locker locker;
diff --git a/src/debug.cc b/src/debug.cc
index e279ee9..ca20a65 100644
--- a/src/debug.cc
+++ b/src/debug.cc
@@ -129,10 +129,14 @@
ASSERT(statement_position_ >= 0);
}
- // Check for breakable code target. Look in the original code as setting
- // break points can cause the code targets in the running (debugged) code to
- // be of a different kind than in the original code.
- if (RelocInfo::IsCodeTarget(rmode())) {
+ if (IsDebugBreakSlot()) {
+ // There is always a possible break point at a debug break slot.
+ break_point_++;
+ return;
+ } else if (RelocInfo::IsCodeTarget(rmode())) {
+ // Check for breakable code target. Look in the original code as setting
+ // break points can cause the code targets in the running (debugged) code
+ // to be of a different kind than in the original code.
Address target = original_rinfo()->target_address();
Code* code = Code::GetCodeFromTargetAddress(target);
if ((code->is_inline_cache_stub() &&
@@ -329,6 +333,9 @@
if (RelocInfo::IsJSReturn(rmode())) {
// Patch the frame exit code with a break point.
SetDebugBreakAtReturn();
+ } else if (IsDebugBreakSlot()) {
+ // Patch the code in the break slot.
+ SetDebugBreakAtSlot();
} else {
// Patch the IC call.
SetDebugBreakAtIC();
@@ -346,6 +353,9 @@
if (RelocInfo::IsJSReturn(rmode())) {
// Restore the frame exit code.
ClearDebugBreakAtReturn();
+ } else if (IsDebugBreakSlot()) {
+ // Restore the code in the break slot.
+ ClearDebugBreakAtSlot();
} else {
// Patch the IC call.
ClearDebugBreakAtIC();
@@ -417,6 +427,8 @@
bool BreakLocationIterator::IsDebugBreak() {
if (RelocInfo::IsJSReturn(rmode())) {
return IsDebugBreakAtReturn();
+ } else if (IsDebugBreakSlot()) {
+ return IsDebugBreakAtSlot();
} else {
return Debug::IsDebugBreak(rinfo()->target_address());
}
@@ -478,6 +490,11 @@
}
+bool BreakLocationIterator::IsDebugBreakSlot() {
+ return RelocInfo::DEBUG_BREAK_SLOT == rmode();
+}
+
+
Object* BreakLocationIterator::BreakPointObjects() {
return debug_info_->GetBreakPointObjects(code_position());
}
@@ -573,6 +590,7 @@
Handle<Context> Debug::debug_context_ = Handle<Context>();
Code* Debug::debug_break_return_ = NULL;
+Code* Debug::debug_break_slot_ = NULL;
void ScriptCache::Add(Handle<Script> script) {
@@ -656,6 +674,10 @@
debug_break_return_ =
Builtins::builtin(Builtins::Return_DebugBreak);
ASSERT(debug_break_return_->IsCode());
+ // Get code to handle debug break in debug break slots.
+ debug_break_slot_ =
+ Builtins::builtin(Builtins::Slot_DebugBreak);
+ ASSERT(debug_break_slot_->IsCode());
}
}
@@ -824,6 +846,7 @@
void Debug::Iterate(ObjectVisitor* v) {
v->VisitPointer(BitCast<Object**, Code**>(&(debug_break_return_)));
+ v->VisitPointer(BitCast<Object**, Code**>(&(debug_break_slot_)));
}
@@ -1631,16 +1654,21 @@
// break point is still active after processing the break point.
Address addr = frame->pc() - Assembler::kCallTargetAddressOffset;
- // Check if the location is at JS exit.
+ // Check if the location is at JS exit or debug break slot.
bool at_js_return = false;
bool break_at_js_return_active = false;
+ bool at_debug_break_slot = false;
RelocIterator it(debug_info->code());
- while (!it.done()) {
+ while (!it.done() && !at_js_return && !at_debug_break_slot) {
if (RelocInfo::IsJSReturn(it.rinfo()->rmode())) {
at_js_return = (it.rinfo()->pc() ==
addr - Assembler::kPatchReturnSequenceAddressOffset);
break_at_js_return_active = it.rinfo()->IsPatchedReturnSequence();
}
+ if (RelocInfo::IsDebugBreakSlot(it.rinfo()->rmode())) {
+ at_debug_break_slot = (it.rinfo()->pc() ==
+ addr - Assembler::kPatchDebugBreakSlotAddressOffset);
+ }
it.next();
}
@@ -1657,25 +1685,30 @@
// Move back to where the call instruction sequence started.
thread_local_.after_break_target_ =
addr - Assembler::kPatchReturnSequenceAddressOffset;
- } else {
- // Check if there still is a debug break call at the target address. If the
- // break point has been removed it will have disappeared. If it have
- // disappeared don't try to look in the original code as the running code
- // will have the right address. This takes care of the case where the last
- // break point is removed from the function and therefore no "original code"
- // is available. If the debug break call is still there find the address in
- // the original code.
- if (IsDebugBreak(Assembler::target_address_at(addr))) {
- // If the break point is still there find the call address which was
- // overwritten in the original code by the call to DebugBreakXXX.
+ } else if (at_debug_break_slot) {
+ // Address of where the debug break slot starts.
+ addr = addr - Assembler::kPatchDebugBreakSlotAddressOffset;
- // Find the corresponding address in the original code.
- addr += original_code->instruction_start() - code->instruction_start();
- }
+ // Continue just after the slot.
+ thread_local_.after_break_target_ = addr + Assembler::kDebugBreakSlotLength;
+ } else if (IsDebugBreak(Assembler::target_address_at(addr))) {
+ // We now know that there is still a debug break call at the target address,
+ // so the break point is still there and the original code will hold the
+ // address to jump to in order to complete the call which is replaced by a
+ // call to DebugBreakXXX.
+
+ // Find the corresponding address in the original code.
+ addr += original_code->instruction_start() - code->instruction_start();
// Install jump to the call address in the original code. This will be the
// call which was overwritten by the call to DebugBreakXXX.
thread_local_.after_break_target_ = Assembler::target_address_at(addr);
+ } else {
+ // There is no longer a break point present. Don't try to look in the
+ // original code as the running code will have the right address. This takes
+ // care of the case where the last break point is removed from the function
+ // and therefore no "original code" is available.
+ thread_local_.after_break_target_ = Assembler::target_address_at(addr);
}
}
diff --git a/src/debug.h b/src/debug.h
index e2eecb8..0e12c76 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -146,6 +146,11 @@
void SetDebugBreakAtReturn();
void ClearDebugBreakAtReturn();
+ bool IsDebugBreakSlot();
+ bool IsDebugBreakAtSlot();
+ void SetDebugBreakAtSlot();
+ void ClearDebugBreakAtSlot();
+
DISALLOW_COPY_AND_ASSIGN(BreakLocationIterator);
};
@@ -323,6 +328,7 @@
enum AddressId {
k_after_break_target_address,
k_debug_break_return_address,
+ k_debug_break_slot_address,
k_register_address
};
@@ -342,6 +348,12 @@
return &debug_break_return_;
}
+ // Access to the debug break in debug break slot code.
+ static Code* debug_break_slot() { return debug_break_slot_; }
+ static Code** debug_break_slot_address() {
+ return &debug_break_slot_;
+ }
+
static const int kEstimatedNofDebugInfoEntries = 16;
static const int kEstimatedNofBreakPointsInFunction = 16;
@@ -370,6 +382,7 @@
static void AfterGarbageCollection();
// Code generator routines.
+ static void GenerateSlot(MacroAssembler* masm);
static void GenerateLoadICDebugBreak(MacroAssembler* masm);
static void GenerateStoreICDebugBreak(MacroAssembler* masm);
static void GenerateKeyedLoadICDebugBreak(MacroAssembler* masm);
@@ -377,6 +390,7 @@
static void GenerateConstructCallDebugBreak(MacroAssembler* masm);
static void GenerateReturnDebugBreak(MacroAssembler* masm);
static void GenerateStubNoRegistersDebugBreak(MacroAssembler* masm);
+ static void GenerateSlotDebugBreak(MacroAssembler* masm);
static void GeneratePlainReturnLiveEdit(MacroAssembler* masm);
static void GenerateFrameDropperLiveEdit(MacroAssembler* masm);
@@ -472,6 +486,9 @@
// Code to call for handling debug break on return.
static Code* debug_break_return_;
+ // Code to call for handling debug break in debug break slots.
+ static Code* debug_break_slot_;
+
DISALLOW_COPY_AND_ASSIGN(Debug);
};
@@ -895,6 +912,8 @@
return reinterpret_cast<Address>(Debug::after_break_target_address());
case Debug::k_debug_break_return_address:
return reinterpret_cast<Address>(Debug::debug_break_return_address());
+ case Debug::k_debug_break_slot_address:
+ return reinterpret_cast<Address>(Debug::debug_break_slot_address());
case Debug::k_register_address:
return reinterpret_cast<Address>(Debug::register_address(reg_));
default:
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index 0e6dd88..dc8efcc 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -277,7 +277,7 @@
DEFINE_bool(help, false, "Print usage message, including flags, on console")
DEFINE_bool(dump_counters, false, "Dump counters on exit")
-DEFINE_bool(debugger, true, "Enable JavaScript debugger")
+DEFINE_bool(debugger, false, "Enable JavaScript debugger")
DEFINE_bool(remote_debugger, false, "Connect JavaScript debugger to the "
"debugger agent in another process")
DEFINE_bool(debugger_agent, false, "Enable debugger agent")
diff --git a/src/full-codegen.cc b/src/full-codegen.cc
index f7a063a..6316158 100644
--- a/src/full-codegen.cc
+++ b/src/full-codegen.cc
@@ -439,6 +439,231 @@
#undef CHECK_BAILOUT
+void BreakableStatementChecker::Check(Statement* stmt) {
+ Visit(stmt);
+}
+
+
+void BreakableStatementChecker::Check(Expression* expr) {
+ Visit(expr);
+}
+
+
+void BreakableStatementChecker::VisitDeclaration(Declaration* decl) {
+}
+
+
+void BreakableStatementChecker::VisitBlock(Block* stmt) {
+}
+
+
+void BreakableStatementChecker::VisitExpressionStatement(
+ ExpressionStatement* stmt) {
+ // Check if expression is breakable.
+ Visit(stmt->expression());
+}
+
+
+void BreakableStatementChecker::VisitEmptyStatement(EmptyStatement* stmt) {
+}
+
+
+void BreakableStatementChecker::VisitIfStatement(IfStatement* stmt) {
+ // If the condition is breakable the if statement is breakable.
+ Visit(stmt->condition());
+}
+
+
+void BreakableStatementChecker::VisitContinueStatement(
+ ContinueStatement* stmt) {
+}
+
+
+void BreakableStatementChecker::VisitBreakStatement(BreakStatement* stmt) {
+}
+
+
+void BreakableStatementChecker::VisitReturnStatement(ReturnStatement* stmt) {
+ // Return is breakable if the expression is.
+ Visit(stmt->expression());
+}
+
+
+void BreakableStatementChecker::VisitWithEnterStatement(
+ WithEnterStatement* stmt) {
+ Visit(stmt->expression());
+}
+
+
+void BreakableStatementChecker::VisitWithExitStatement(
+ WithExitStatement* stmt) {
+}
+
+
+void BreakableStatementChecker::VisitSwitchStatement(SwitchStatement* stmt) {
+ // Switch statements breakable if the tag expression is.
+ Visit(stmt->tag());
+}
+
+
+void BreakableStatementChecker::VisitDoWhileStatement(DoWhileStatement* stmt) {
+ // Mark do while as breakable to avoid adding a break slot in front of it.
+ is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitWhileStatement(WhileStatement* stmt) {
+ // Mark while statements breakable if the condition expression is.
+ Visit(stmt->cond());
+}
+
+
+void BreakableStatementChecker::VisitForStatement(ForStatement* stmt) {
+ // Mark for statements breakable if the condition expression is.
+ if (stmt->cond() != NULL) {
+ Visit(stmt->cond());
+ }
+}
+
+
+void BreakableStatementChecker::VisitForInStatement(ForInStatement* stmt) {
+ // Mark for in statements breakable if the enumerable expression is.
+ Visit(stmt->enumerable());
+}
+
+
+void BreakableStatementChecker::VisitTryCatchStatement(
+ TryCatchStatement* stmt) {
+ // Mark try catch as breakable to avoid adding a break slot in front of it.
+ is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitTryFinallyStatement(
+ TryFinallyStatement* stmt) {
+ // Mark try finally as breakable to avoid adding a break slot in front of it.
+ is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitDebuggerStatement(
+ DebuggerStatement* stmt) {
+ // The debugger statement is breakable.
+ is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitFunctionLiteral(FunctionLiteral* expr) {
+}
+
+
+void BreakableStatementChecker::VisitSharedFunctionInfoLiteral(
+ SharedFunctionInfoLiteral* expr) {
+}
+
+
+void BreakableStatementChecker::VisitConditional(Conditional* expr) {
+}
+
+
+void BreakableStatementChecker::VisitSlot(Slot* expr) {
+}
+
+
+void BreakableStatementChecker::VisitVariableProxy(VariableProxy* expr) {
+}
+
+
+void BreakableStatementChecker::VisitLiteral(Literal* expr) {
+}
+
+
+void BreakableStatementChecker::VisitRegExpLiteral(RegExpLiteral* expr) {
+}
+
+
+void BreakableStatementChecker::VisitObjectLiteral(ObjectLiteral* expr) {
+}
+
+
+void BreakableStatementChecker::VisitArrayLiteral(ArrayLiteral* expr) {
+}
+
+
+void BreakableStatementChecker::VisitCatchExtensionObject(
+ CatchExtensionObject* expr) {
+}
+
+
+void BreakableStatementChecker::VisitAssignment(Assignment* expr) {
+ // If assigning to a property (including a global property) the assignment is
+ // breakable.
+ Variable* var = expr->target()->AsVariableProxy()->AsVariable();
+ Property* prop = expr->target()->AsProperty();
+ if (prop != NULL || (var != NULL && var->is_global())) {
+ is_breakable_ = true;
+ return;
+ }
+
+ // Otherwise the assignment is breakable if the assigned value is.
+ Visit(expr->value());
+}
+
+
+void BreakableStatementChecker::VisitThrow(Throw* expr) {
+ // Throw is breakable if the expression is.
+ Visit(expr->exception());
+}
+
+
+void BreakableStatementChecker::VisitProperty(Property* expr) {
+ // Property load is breakable.
+ is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitCall(Call* expr) {
+ // Function calls both through IC and call stub are breakable.
+ is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitCallNew(CallNew* expr) {
+ // Function calls through new are breakable.
+ is_breakable_ = true;
+}
+
+
+void BreakableStatementChecker::VisitCallRuntime(CallRuntime* expr) {
+}
+
+
+void BreakableStatementChecker::VisitUnaryOperation(UnaryOperation* expr) {
+ Visit(expr->expression());
+}
+
+
+void BreakableStatementChecker::VisitCountOperation(CountOperation* expr) {
+ Visit(expr->expression());
+}
+
+
+void BreakableStatementChecker::VisitBinaryOperation(BinaryOperation* expr) {
+ Visit(expr->left());
+ Visit(expr->right());
+}
+
+
+void BreakableStatementChecker::VisitCompareOperation(CompareOperation* expr) {
+ Visit(expr->left());
+ Visit(expr->right());
+}
+
+
+void BreakableStatementChecker::VisitThisFunction(ThisFunction* expr) {
+}
+
+
#define __ ACCESS_MASM(masm())
Handle<Code> FullCodeGenerator::MakeCode(CompilationInfo* info) {
@@ -552,7 +777,60 @@
void FullCodeGenerator::SetStatementPosition(Statement* stmt) {
if (FLAG_debug_info) {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ if (!Debugger::IsDebuggerActive()) {
+ CodeGenerator::RecordPositions(masm_, stmt->statement_pos());
+ } else {
+ // Check if the statement will be breakable without adding a debug break
+ // slot.
+ BreakableStatementChecker checker;
+ checker.Check(stmt);
+ // Record the statement position right here if the statement is not
+ // breakable. For breakable statements the actual recording of the
+ // position will be postponed to the breakable code (typically an IC).
+ bool position_recorded = CodeGenerator::RecordPositions(
+ masm_, stmt->statement_pos(), !checker.is_breakable());
+ // If the position recording did record a new position generate a debug
+ // break slot to make the statement breakable.
+ if (position_recorded) {
+ Debug::GenerateSlot(masm_);
+ }
+ }
+#else
CodeGenerator::RecordPositions(masm_, stmt->statement_pos());
+#endif
+ }
+}
+
+
+void FullCodeGenerator::SetExpressionPosition(Expression* expr, int pos) {
+ if (FLAG_debug_info) {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ if (!Debugger::IsDebuggerActive()) {
+ CodeGenerator::RecordPositions(masm_, pos);
+ } else {
+ // Check if the expression will be breakable without adding a debug break
+ // slot.
+ BreakableStatementChecker checker;
+ checker.Check(expr);
+ // Record a statement position right here if the expression is not
+ // breakable. For breakable expressions the actual recording of the
+ // position will be postponed to the breakable code (typically an IC).
+ // NOTE this will record a statement position for something which might
+ // not be a statement. As stepping in the debugger will only stop at
+ // statement positions this is used for e.g. the condition expression of
+ // a do while loop.
+ bool position_recorded = CodeGenerator::RecordPositions(
+ masm_, pos, !checker.is_breakable());
+ // If the position recording did record a new position generate a debug
+ // break slot to make the statement breakable.
+ if (position_recorded) {
+ Debug::GenerateSlot(masm_);
+ }
+ }
+#else
+ CodeGenerator::RecordPositions(masm_, pos);
+#endif
}
}
@@ -848,7 +1126,11 @@
__ bind(&stack_check_success);
__ bind(loop_statement.continue_target());
- SetStatementPosition(stmt->condition_position());
+
+ // Record the position of the do while condition and make sure it is possible
+ // to break on the condition.
+ SetExpressionPosition(stmt->cond(), stmt->condition_position());
+
VisitForControl(stmt->cond(), &body, loop_statement.break_target());
__ bind(&stack_limit_hit);
@@ -864,7 +1146,6 @@
void FullCodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
Comment cmnt(masm_, "[ WhileStatement");
- SetStatementPosition(stmt);
Label body, stack_limit_hit, stack_check_success;
Iteration loop_statement(this, stmt);
@@ -877,6 +1158,9 @@
Visit(stmt->body());
__ bind(loop_statement.continue_target());
+ // Emit the statement position here as this is where the while statement code
+ // starts.
+ SetStatementPosition(stmt);
// Check stack before looping.
__ StackLimitCheck(&stack_limit_hit);
@@ -896,7 +1180,6 @@
void FullCodeGenerator::VisitForStatement(ForStatement* stmt) {
Comment cmnt(masm_, "[ ForStatement");
- SetStatementPosition(stmt);
Label test, body, stack_limit_hit, stack_check_success;
Iteration loop_statement(this, stmt);
@@ -919,6 +1202,9 @@
}
__ bind(&test);
+ // Emit the statement position here as this is where the for statement code
+ // starts.
+ SetStatementPosition(stmt);
// Check stack before looping.
__ StackLimitCheck(&stack_limit_hit);
@@ -1064,6 +1350,8 @@
VisitForControl(expr->condition(), &true_case, &false_case);
__ bind(&true_case);
+ SetExpressionPosition(expr->then_expression(),
+ expr->then_expression_position());
Visit(expr->then_expression());
// If control flow falls through Visit, jump to done.
if (context_ == Expression::kEffect || context_ == Expression::kValue) {
@@ -1071,6 +1359,8 @@
}
__ bind(&false_case);
+ SetExpressionPosition(expr->else_expression(),
+ expr->else_expression_position());
Visit(expr->else_expression());
// If control flow falls through Visit, merge it with true case here.
if (context_ == Expression::kEffect || context_ == Expression::kValue) {
diff --git a/src/full-codegen.h b/src/full-codegen.h
index ac70d99..0ce600b 100644
--- a/src/full-codegen.h
+++ b/src/full-codegen.h
@@ -59,6 +59,31 @@
};
+// AST node visitor which can tell whether a given statement will be breakable
+// when the code is compiled by the full compiler in the debugger. This means
+// that there will be an IC (load/store/call) in the code generated for the
+// debugger to piggybag on.
+class BreakableStatementChecker: public AstVisitor {
+ public:
+ BreakableStatementChecker() : is_breakable_(false) {}
+
+ void Check(Statement* stmt);
+ void Check(Expression* stmt);
+
+ bool is_breakable() { return is_breakable_; }
+
+ private:
+ // AST node visit functions.
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ bool is_breakable_;
+
+ DISALLOW_COPY_AND_ASSIGN(BreakableStatementChecker);
+};
+
+
// -----------------------------------------------------------------------------
// Full code generator.
@@ -458,6 +483,7 @@
void SetFunctionPosition(FunctionLiteral* fun);
void SetReturnPosition(FunctionLiteral* fun);
void SetStatementPosition(Statement* stmt);
+ void SetExpressionPosition(Expression* expr, int pos);
void SetStatementPosition(int pos);
void SetSourcePosition(int pos);
diff --git a/src/heap-inl.h b/src/heap-inl.h
index feda2d1..cc558b8 100644
--- a/src/heap-inl.h
+++ b/src/heap-inl.h
@@ -118,11 +118,9 @@
ExternalString::kResourceOffset -
kHeapObjectTag);
- // Dispose of the C++ object.
- if (external_string_dispose_callback_ != NULL) {
- external_string_dispose_callback_(*resource_addr);
- } else {
- delete *resource_addr;
+ // Dispose of the C++ object if it has not already been disposed.
+ if (*resource_addr != NULL) {
+ (*resource_addr)->Dispose();
}
// Clear the resource pointer in the string.
diff --git a/src/heap.cc b/src/heap.cc
index 9f811c0..3115cb3 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -98,8 +98,6 @@
// set up by ConfigureHeap otherwise.
int Heap::reserved_semispace_size_ = Heap::max_semispace_size_;
-ExternalStringDiposeCallback Heap::external_string_dispose_callback_ = NULL;
-
List<Heap::GCPrologueCallbackPair> Heap::gc_prologue_callbacks_;
List<Heap::GCEpilogueCallbackPair> Heap::gc_epilogue_callbacks_;
@@ -607,6 +605,9 @@
EnsureFromSpaceIsCommitted();
if (collector == MARK_COMPACTOR) {
+ // Flush all potentially unused code.
+ FlushCode();
+
// Perform mark-sweep with optional compaction.
MarkCompact(tracer);
@@ -659,18 +660,19 @@
void Heap::MarkCompact(GCTracer* tracer) {
gc_state_ = MARK_COMPACT;
- if (MarkCompactCollector::IsCompacting()) {
- mc_count_++;
- } else {
- ms_count_++;
- }
- tracer->set_full_gc_count(mc_count_);
LOG(ResourceEvent("markcompact", "begin"));
MarkCompactCollector::Prepare(tracer);
bool is_compacting = MarkCompactCollector::IsCompacting();
+ if (is_compacting) {
+ mc_count_++;
+ } else {
+ ms_count_++;
+ }
+ tracer->set_full_gc_count(mc_count_ + ms_count_);
+
MarkCompactPrologue(is_compacting);
MarkCompactCollector::CollectGarbage();
@@ -2185,6 +2187,87 @@
}
+// The StackVisitor is used to traverse all the archived threads to see if
+// there are activations on any of the stacks corresponding to the code.
+class FlushingStackVisitor : public ThreadVisitor {
+ public:
+ explicit FlushingStackVisitor(Code* code) : found_(false), code_(code) {}
+
+ void VisitThread(ThreadLocalTop* top) {
+ // If we already found the code in a previous traversed thread we return.
+ if (found_) return;
+
+ for (StackFrameIterator it(top); !it.done(); it.Advance()) {
+ if (code_->contains(it.frame()->pc())) {
+ found_ = true;
+ return;
+ }
+ }
+ }
+ bool FoundCode() {return found_;}
+
+ private:
+ bool found_;
+ Code* code_;
+};
+
+
+static void FlushCodeForFunction(SharedFunctionInfo* function_info) {
+ // The function must be compiled and have the source code available,
+ // to be able to recompile it in case we need the function again.
+ if (!(function_info->is_compiled() && function_info->HasSourceCode())) return;
+
+ // We never flush code for Api functions.
+ if (function_info->IsApiFunction()) return;
+
+ // Only flush code for functions.
+ if (!function_info->code()->kind() == Code::FUNCTION) return;
+
+ // Function must be lazy compilable.
+ if (!function_info->allows_lazy_compilation()) return;
+
+ // If this is a full script wrapped in a function we do no flush the code.
+ if (function_info->is_toplevel()) return;
+
+ // If this function is in the compilation cache we do not flush the code.
+ if (CompilationCache::HasFunction(function_info)) return;
+
+ // Make sure we are not referencing the code from the stack.
+ for (StackFrameIterator it; !it.done(); it.Advance()) {
+ if (function_info->code()->contains(it.frame()->pc())) return;
+ }
+ // Iterate the archived stacks in all threads to check if
+ // the code is referenced.
+ FlushingStackVisitor threadvisitor(function_info->code());
+ ThreadManager::IterateArchivedThreads(&threadvisitor);
+ if (threadvisitor.FoundCode()) return;
+
+ HandleScope scope;
+ // Compute the lazy compilable version of the code.
+ function_info->set_code(*ComputeLazyCompile(function_info->length()));
+}
+
+
+void Heap::FlushCode() {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Do not flush code if the debugger is loaded or there are breakpoints.
+ if (Debug::IsLoaded() || Debug::has_break_points()) return;
+#endif
+ HeapObjectIterator it(old_pointer_space());
+ for (HeapObject* obj = it.next(); obj != NULL; obj = it.next()) {
+ if (obj->IsJSFunction()) {
+ JSFunction* jsfunction = JSFunction::cast(obj);
+
+ // The function must have a valid context and not be a builtin.
+ if (jsfunction->unchecked_context()->IsContext() &&
+ !jsfunction->IsBuiltin()) {
+ FlushCodeForFunction(jsfunction->shared());
+ }
+ }
+ }
+}
+
+
Object* Heap::CreateCode(const CodeDesc& desc,
ZoneScopeInfo* sinfo,
Code::Flags flags,
diff --git a/src/heap.h b/src/heap.h
index e99c538..0db4008 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -690,11 +690,6 @@
static bool GarbageCollectionGreedyCheck();
#endif
- static void SetExternalStringDiposeCallback(
- ExternalStringDiposeCallback callback) {
- external_string_dispose_callback_ = callback;
- }
-
static void AddGCPrologueCallback(
GCEpilogueCallback callback, GCType gc_type_filter);
static void RemoveGCPrologueCallback(GCEpilogueCallback callback);
@@ -1143,9 +1138,6 @@
// any string when looked up in properties.
static String* hidden_symbol_;
- static ExternalStringDiposeCallback
- external_string_dispose_callback_;
-
// GC callback function, called before and after mark-compact GC.
// Allocations in the callback function are disallowed.
struct GCPrologueCallbackPair {
@@ -1274,6 +1266,10 @@
// Flush the number to string cache.
static void FlushNumberStringCache();
+ // Flush code from functions we do not expect to use again. The code will
+ // be replaced with a lazy compilable version.
+ static void FlushCode();
+
static const int kInitialSymbolTableSize = 2048;
static const int kInitialEvalCacheSize = 64;
diff --git a/src/ia32/assembler-ia32-inl.h b/src/ia32/assembler-ia32-inl.h
index 9c96e19..a851b42 100644
--- a/src/ia32/assembler-ia32-inl.h
+++ b/src/ia32/assembler-ia32-inl.h
@@ -52,16 +52,21 @@
void RelocInfo::apply(intptr_t delta) {
if (rmode_ == RUNTIME_ENTRY || IsCodeTarget(rmode_)) {
int32_t* p = reinterpret_cast<int32_t*>(pc_);
- *p -= delta; // relocate entry
+ *p -= delta; // Relocate entry.
} else if (rmode_ == JS_RETURN && IsPatchedReturnSequence()) {
// Special handling of js_return when a break point is set (call
// instruction has been inserted).
int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1);
- *p -= delta; // relocate entry
+ *p -= delta; // Relocate entry.
+ } else if (rmode_ == DEBUG_BREAK_SLOT && IsPatchedDebugBreakSlotSequence()) {
+ // Special handling of a debug break slot when a break point is set (call
+ // instruction has been inserted).
+ int32_t* p = reinterpret_cast<int32_t*>(pc_ + 1);
+ *p -= delta; // Relocate entry.
} else if (IsInternalReference(rmode_)) {
// absolute code pointer inside code object moves with the code object.
int32_t* p = reinterpret_cast<int32_t*>(pc_);
- *p += delta; // relocate entry
+ *p += delta; // Relocate entry.
}
}
@@ -154,6 +159,11 @@
}
+bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
+ return !Assembler::IsNop(pc());
+}
+
+
void RelocInfo::Visit(ObjectVisitor* visitor) {
RelocInfo::Mode mode = rmode();
if (mode == RelocInfo::EMBEDDED_OBJECT) {
@@ -164,8 +174,10 @@
visitor->VisitExternalReference(target_reference_address());
#ifdef ENABLE_DEBUGGER_SUPPORT
} else if (Debug::has_break_points() &&
- RelocInfo::IsJSReturn(mode) &&
- IsPatchedReturnSequence()) {
+ ((RelocInfo::IsJSReturn(mode) &&
+ IsPatchedReturnSequence()) ||
+ (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence()))) {
visitor->VisitDebugTarget(this);
#endif
} else if (mode == RelocInfo::RUNTIME_ENTRY) {
diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc
index a436827..d4dff33 100644
--- a/src/ia32/assembler-ia32.cc
+++ b/src/ia32/assembler-ia32.cc
@@ -206,6 +206,7 @@
patcher.masm()->SizeOfCodeGeneratedSince(&check_codesize));
// Add the requested number of int3 instructions after the call.
+ ASSERT_GE(guard_bytes, 0);
for (int i = 0; i < guard_bytes; i++) {
patcher.masm()->int3();
}
@@ -2371,6 +2372,13 @@
}
+void Assembler::RecordDebugBreakSlot() {
+ WriteRecordedPositions();
+ EnsureSpace ensure_space(this);
+ RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
+}
+
+
void Assembler::RecordComment(const char* msg) {
if (FLAG_debug_code) {
EnsureSpace ensure_space(this);
@@ -2393,13 +2401,16 @@
}
-void Assembler::WriteRecordedPositions() {
+bool Assembler::WriteRecordedPositions() {
+ bool written = false;
+
// Write the statement position if it is different from what was written last
// time.
if (current_statement_position_ != written_statement_position_) {
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_);
written_statement_position_ = current_statement_position_;
+ written = true;
}
// Write the position if it is different from what was written last time and
@@ -2409,7 +2420,11 @@
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::POSITION, current_position_);
written_position_ = current_position_;
+ written = true;
}
+
+ // Return whether something was written.
+ return written;
}
diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h
index 76dfe7c..7dcbab5 100644
--- a/src/ia32/assembler-ia32.h
+++ b/src/ia32/assembler-ia32.h
@@ -468,9 +468,16 @@
// to jump to.
static const int kPatchReturnSequenceAddressOffset = 1; // JMP imm32.
+ // Distance between start of patched debug break slot and the emitted address
+ // to jump to.
+ static const int kPatchDebugBreakSlotAddressOffset = 1; // JMP imm32.
+
static const int kCallInstructionLength = 5;
static const int kJSReturnSequenceLength = 6;
+ // The debug break slot must be able to contain a call instruction.
+ static const int kDebugBreakSlotLength = kCallInstructionLength;
+
// ---------------------------------------------------------------------------
// Code generation
//
@@ -809,13 +816,16 @@
// Mark address of the ExitJSFrame code.
void RecordJSReturn();
+ // Mark address of a debug break slot.
+ void RecordDebugBreakSlot();
+
// Record a comment relocation entry that can be used by a disassembler.
// Use --debug_code to enable.
void RecordComment(const char* msg);
void RecordPosition(int pos);
void RecordStatementPosition(int pos);
- void WriteRecordedPositions();
+ bool WriteRecordedPositions();
// Writes a single word of data in the code stream.
// Used for inline tables, e.g., jump-tables.
@@ -833,6 +843,8 @@
// Get the number of bytes available in the buffer.
inline int available_space() const { return reloc_info_writer.pos() - pc_; }
+ static bool IsNop(Address addr) { return *addr == 0x90; }
+
// Avoid overflows for displacements etc.
static const int kMaximalBufferSize = 512*MB;
static const int kMinimalBufferSize = 4*KB;
diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h
index b3ddad9..180b9a6 100644
--- a/src/ia32/codegen-ia32.h
+++ b/src/ia32/codegen-ia32.h
@@ -316,7 +316,9 @@
static bool ShouldGenerateLog(Expression* type);
#endif
- static void RecordPositions(MacroAssembler* masm, int pos);
+ static bool RecordPositions(MacroAssembler* masm,
+ int pos,
+ bool right_here = false);
// Accessors
MacroAssembler* masm() { return masm_; }
diff --git a/src/ia32/debug-ia32.cc b/src/ia32/debug-ia32.cc
index 9780f3b..9b558bd 100644
--- a/src/ia32/debug-ia32.cc
+++ b/src/ia32/debug-ia32.cc
@@ -69,6 +69,27 @@
}
+bool BreakLocationIterator::IsDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ // Check whether the debug break slot instructions have been patched.
+ return rinfo()->IsPatchedDebugBreakSlotSequence();
+}
+
+
+void BreakLocationIterator::SetDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ rinfo()->PatchCodeWithCall(
+ Debug::debug_break_slot()->entry(),
+ Assembler::kDebugBreakSlotLength - Assembler::kCallInstructionLength);
+}
+
+
+void BreakLocationIterator::ClearDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ rinfo()->PatchCode(original_rinfo()->pc(), Assembler::kDebugBreakSlotLength);
+}
+
+
#define __ ACCESS_MASM(masm)
@@ -208,10 +229,31 @@
}
+void Debug::GenerateSlot(MacroAssembler* masm) {
+ // Generate enough nop's to make space for a call instruction.
+ Label check_codesize;
+ __ bind(&check_codesize);
+ __ RecordDebugBreakSlot();
+ for (int i = 0; i < Assembler::kDebugBreakSlotLength; i++) {
+ __ nop();
+ }
+ ASSERT_EQ(Assembler::kDebugBreakSlotLength,
+ masm->SizeOfCodeGeneratedSince(&check_codesize));
+}
+
+
+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);
+}
+
+
void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
masm->ret(0);
}
+
// FrameDropper is a code replacement for a JavaScript frame with possibly
// several frames above.
// There is no calling conventions here, because it never actually gets called,
diff --git a/src/ia32/disasm-ia32.cc b/src/ia32/disasm-ia32.cc
index 44afdd6..dc4c27e 100644
--- a/src/ia32/disasm-ia32.cc
+++ b/src/ia32/disasm-ia32.cc
@@ -924,14 +924,18 @@
break;
case 0xF6:
- { int mod, regop, rm;
- get_modrm(*(data+1), &mod, ®op, &rm);
- if (mod == 3 && regop == eax) {
- AppendToBuffer("test_b %s,%d", NameOfCPURegister(rm), *(data+2));
+ { data++;
+ int mod, regop, rm;
+ get_modrm(*data, &mod, ®op, &rm);
+ if (regop == eax) {
+ AppendToBuffer("test_b ");
+ data += PrintRightOperand(data);
+ int32_t imm = *data;
+ AppendToBuffer(",0x%x", imm);
+ data++;
} else {
UnimplementedInstruction();
}
- data += 3;
}
break;
diff --git a/src/mark-compact.cc b/src/mark-compact.cc
index 0dc3e0f..95afb4a 100644
--- a/src/mark-compact.cc
+++ b/src/mark-compact.cc
@@ -273,8 +273,10 @@
}
void VisitDebugTarget(RelocInfo* rinfo) {
- ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()) &&
- rinfo->IsPatchedReturnSequence());
+ ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
+ rinfo->IsPatchedReturnSequence()) ||
+ (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
+ rinfo->IsPatchedDebugBreakSlotSequence()));
HeapObject* code = Code::GetCodeFromTargetAddress(rinfo->call_address());
MarkCompactCollector::MarkObject(code);
}
@@ -1106,8 +1108,10 @@
}
void VisitDebugTarget(RelocInfo* rinfo) {
- ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()) &&
- rinfo->IsPatchedReturnSequence());
+ ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
+ rinfo->IsPatchedReturnSequence()) ||
+ (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
+ rinfo->IsPatchedDebugBreakSlotSequence()));
Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
VisitPointer(&target);
rinfo->set_call_address(Code::cast(target)->instruction_start());
@@ -1856,8 +1860,10 @@
}
void VisitDebugTarget(RelocInfo* rinfo) {
- ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()) &&
- rinfo->IsPatchedReturnSequence());
+ ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
+ rinfo->IsPatchedReturnSequence()) ||
+ (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
+ rinfo->IsPatchedDebugBreakSlotSequence()));
Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
VisitPointer(&target);
rinfo->set_call_address(
diff --git a/src/mips/assembler-mips.cc b/src/mips/assembler-mips.cc
index d9617dc..a3b316b 100644
--- a/src/mips/assembler-mips.cc
+++ b/src/mips/assembler-mips.cc
@@ -1046,13 +1046,16 @@
}
-void Assembler::WriteRecordedPositions() {
+bool Assembler::WriteRecordedPositions() {
+ bool written = false;
+
// Write the statement position if it is different from what was written last
// time.
if (current_statement_position_ != written_statement_position_) {
CheckBuffer();
RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_);
written_statement_position_ = current_statement_position_;
+ written = true;
}
// Write the position if it is different from what was written last time and
@@ -1062,7 +1065,11 @@
CheckBuffer();
RecordRelocInfo(RelocInfo::POSITION, current_position_);
written_position_ = current_position_;
+ written = true;
}
+
+ // Return whether something was written.
+ return written;
}
diff --git a/src/mips/assembler-mips.h b/src/mips/assembler-mips.h
index cc730f2..b7c3ebc 100644
--- a/src/mips/assembler-mips.h
+++ b/src/mips/assembler-mips.h
@@ -355,6 +355,9 @@
// to jump to.
static const int kPatchReturnSequenceAddressOffset = kInstrSize;
+ // Distance between start of patched debug break slot and the emitted address
+ // to jump to.
+ static const int kPatchDebugBreakSlotAddressOffset = kInstrSize;
// ---------------------------------------------------------------------------
// Code generation.
@@ -518,7 +521,7 @@
void RecordPosition(int pos);
void RecordStatementPosition(int pos);
- void WriteRecordedPositions();
+ bool WriteRecordedPositions();
int32_t pc_offset() const { return pc_ - buffer_; }
int32_t current_position() const { return current_position_; }
diff --git a/src/objects-inl.h b/src/objects-inl.h
index fceb76f..4112f93 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -2468,6 +2468,10 @@
compiler_hints,
try_full_codegen,
kTryFullCodegen)
+BOOL_ACCESSORS(SharedFunctionInfo,
+ compiler_hints,
+ allows_lazy_compilation,
+ kAllowLazyCompilation)
#if V8_HOST_ARCH_32_BIT
SMI_ACCESSORS(SharedFunctionInfo, length, kLengthOffset)
diff --git a/src/objects.cc b/src/objects.cc
index dffacf7..1e4d4a4 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -5264,8 +5264,10 @@
void ObjectVisitor::VisitDebugTarget(RelocInfo* rinfo) {
- ASSERT(RelocInfo::IsJSReturn(rinfo->rmode()) &&
- rinfo->IsPatchedReturnSequence());
+ ASSERT((RelocInfo::IsJSReturn(rinfo->rmode()) &&
+ rinfo->IsPatchedReturnSequence()) ||
+ (RelocInfo::IsDebugBreakSlot(rinfo->rmode()) &&
+ rinfo->IsPatchedDebugBreakSlotSequence()));
Object* target = Code::GetCodeFromTargetAddress(rinfo->call_address());
Object* old_target = target;
VisitPointer(&target);
@@ -5278,6 +5280,7 @@
RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT) |
RelocInfo::ModeMask(RelocInfo::EXTERNAL_REFERENCE) |
RelocInfo::ModeMask(RelocInfo::JS_RETURN) |
+ RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT) |
RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
diff --git a/src/objects.h b/src/objects.h
index 94b2253..095dd98 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -3308,6 +3308,12 @@
inline bool try_full_codegen();
inline void set_try_full_codegen(bool flag);
+ // Indicates if this function can be lazy compiled.
+ // This is used to determine if we can safely flush code from a function
+ // when doing GC if we expect that the function will no longer be used.
+ inline bool allows_lazy_compilation();
+ inline void set_allows_lazy_compilation(bool flag);
+
// Check whether a inlined constructor can be generated with the given
// prototype.
bool CanGenerateInlineConstructor(Object* prototype);
@@ -3433,6 +3439,7 @@
// Bit positions in compiler_hints.
static const int kHasOnlySimpleThisPropertyAssignments = 0;
static const int kTryFullCodegen = 1;
+ static const int kAllowLazyCompilation = 2;
DISALLOW_IMPLICIT_CONSTRUCTORS(SharedFunctionInfo);
};
diff --git a/src/parser.cc b/src/parser.cc
index bbf71bc..31bac91 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -2867,10 +2867,13 @@
// In parsing the first assignment expression in conditional
// expressions we always accept the 'in' keyword; see ECMA-262,
// section 11.12, page 58.
+ int left_position = scanner().peek_location().beg_pos;
Expression* left = ParseAssignmentExpression(true, CHECK_OK);
Expect(Token::COLON, CHECK_OK);
+ int right_position = scanner().peek_location().beg_pos;
Expression* right = ParseAssignmentExpression(accept_IN, CHECK_OK);
- return NEW(Conditional(expression, left, right));
+ return NEW(Conditional(expression, left, right,
+ left_position, right_position));
}
diff --git a/src/profile-generator.cc b/src/profile-generator.cc
index 105c1a8..44163a0 100644
--- a/src/profile-generator.cc
+++ b/src/profile-generator.cc
@@ -55,7 +55,7 @@
int TokenEnumerator::GetTokenId(Object* token) {
- if (token == NULL) return CodeEntry::kNoSecurityToken;
+ if (token == NULL) return TokenEnumerator::kNoSecurityToken;
for (int i = 0; i < token_locations_.length(); ++i) {
if (*token_locations_[i] == token && !token_removed_[i]) return i;
}
@@ -86,6 +86,37 @@
}
+StringsStorage::StringsStorage()
+ : names_(StringsMatch) {
+}
+
+
+StringsStorage::~StringsStorage() {
+ for (HashMap::Entry* p = names_.Start();
+ p != NULL;
+ p = names_.Next(p)) {
+ DeleteArray(reinterpret_cast<const char*>(p->value));
+ }
+}
+
+
+const char* StringsStorage::GetName(String* name) {
+ if (name->IsString()) {
+ char* c_name =
+ name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach();
+ HashMap::Entry* cache_entry = names_.Lookup(c_name, name->Hash(), true);
+ if (cache_entry->value == NULL) {
+ // New entry added.
+ cache_entry->value = c_name;
+ } else {
+ DeleteArray(c_name);
+ }
+ return reinterpret_cast<const char*>(cache_entry->value);
+ }
+ return "";
+}
+
+
const char* CodeEntry::kEmptyNamePrefix = "";
unsigned CodeEntry::next_call_uid_ = 1;
@@ -171,7 +202,7 @@
"(root)",
"",
0,
- CodeEntry::kNoSecurityToken),
+ TokenEnumerator::kNoSecurityToken),
root_(new ProfileNode(this, &root_entry_)) {
}
@@ -248,11 +279,11 @@
private:
bool IsTokenAcceptable(int token, int parent_token) {
- if (token == CodeEntry::kNoSecurityToken
+ if (token == TokenEnumerator::kNoSecurityToken
|| token == security_token_id_) return true;
- if (token == CodeEntry::kInheritsSecurityToken) {
- ASSERT(parent_token != CodeEntry::kInheritsSecurityToken);
- return parent_token == CodeEntry::kNoSecurityToken
+ if (token == TokenEnumerator::kInheritsSecurityToken) {
+ ASSERT(parent_token != TokenEnumerator::kInheritsSecurityToken);
+ return parent_token == TokenEnumerator::kNoSecurityToken
|| parent_token == security_token_id_;
}
return false;
@@ -373,7 +404,7 @@
CpuProfile* CpuProfile::FilteredClone(int security_token_id) {
- ASSERT(security_token_id != CodeEntry::kNoSecurityToken);
+ ASSERT(security_token_id != TokenEnumerator::kNoSecurityToken);
CpuProfile* clone = new CpuProfile(title_, uid_);
clone->top_down_.FilteredClone(&top_down_, security_token_id);
clone->bottom_up_.FilteredClone(&bottom_up_, security_token_id);
@@ -438,8 +469,7 @@
CpuProfilesCollection::CpuProfilesCollection()
- : function_and_resource_names_(StringsMatch),
- profiles_uids_(UidsMatch),
+ : profiles_uids_(UidsMatch),
current_profiles_semaphore_(OS::CreateSemaphore(1)) {
// Create list of unabridged profiles.
profiles_by_token_.Add(new List<CpuProfile*>());
@@ -470,11 +500,6 @@
profiles_by_token_.Iterate(DeleteProfilesList);
code_entries_.Iterate(DeleteCodeEntry);
args_count_names_.Iterate(DeleteArgsCountName);
- for (HashMap::Entry* p = function_and_resource_names_.Start();
- p != NULL;
- p = function_and_resource_names_.Next(p)) {
- DeleteArray(reinterpret_cast<const char*>(p->value));
- }
}
@@ -517,7 +542,7 @@
profile->CalculateTotalTicks();
profile->SetActualSamplingRate(actual_sampling_rate);
List<CpuProfile*>* unabridged_list =
- profiles_by_token_[TokenToIndex(CodeEntry::kNoSecurityToken)];
+ profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
unabridged_list->Add(profile);
HashMap::Entry* entry =
profiles_uids_.Lookup(reinterpret_cast<void*>(profile->uid()),
@@ -550,8 +575,8 @@
return NULL;
}
List<CpuProfile*>* unabridged_list =
- profiles_by_token_[TokenToIndex(CodeEntry::kNoSecurityToken)];
- if (security_token_id == CodeEntry::kNoSecurityToken) {
+ profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
+ if (security_token_id == TokenEnumerator::kNoSecurityToken) {
return unabridged_list->at(index);
}
List<CpuProfile*>* list = GetProfilesList(security_token_id);
@@ -564,7 +589,7 @@
int CpuProfilesCollection::TokenToIndex(int security_token_id) {
- ASSERT(CodeEntry::kNoSecurityToken == -1);
+ ASSERT(TokenEnumerator::kNoSecurityToken == -1);
return security_token_id + 1; // kNoSecurityToken -> 0, 0 -> 1, ...
}
@@ -575,7 +600,7 @@
const int lists_to_add = index - profiles_by_token_.length() + 1;
if (lists_to_add > 0) profiles_by_token_.AddBlock(NULL, lists_to_add);
List<CpuProfile*>* unabridged_list =
- profiles_by_token_[TokenToIndex(CodeEntry::kNoSecurityToken)];
+ profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
const int current_count = unabridged_list->length();
if (profiles_by_token_[index] == NULL) {
profiles_by_token_[index] = new List<CpuProfile*>(current_count);
@@ -589,8 +614,8 @@
List<CpuProfile*>* CpuProfilesCollection::Profiles(int security_token_id) {
List<CpuProfile*>* unabridged_list =
- profiles_by_token_[TokenToIndex(CodeEntry::kNoSecurityToken)];
- if (security_token_id == CodeEntry::kNoSecurityToken) {
+ profiles_by_token_[TokenToIndex(TokenEnumerator::kNoSecurityToken)];
+ if (security_token_id == TokenEnumerator::kNoSecurityToken) {
return unabridged_list;
}
List<CpuProfile*>* list = GetProfilesList(security_token_id);
@@ -613,7 +638,7 @@
GetFunctionName(name),
GetName(resource_name),
line_number,
- CodeEntry::kNoSecurityToken);
+ TokenEnumerator::kNoSecurityToken);
code_entries_.Add(entry);
return entry;
}
@@ -626,7 +651,7 @@
GetFunctionName(name),
"",
v8::CpuProfileNode::kNoLineNumberInfo,
- CodeEntry::kNoSecurityToken);
+ TokenEnumerator::kNoSecurityToken);
code_entries_.Add(entry);
return entry;
}
@@ -640,7 +665,7 @@
GetName(name),
"",
v8::CpuProfileNode::kNoLineNumberInfo,
- CodeEntry::kInheritsSecurityToken);
+ TokenEnumerator::kInheritsSecurityToken);
code_entries_.Add(entry);
return entry;
}
@@ -653,7 +678,7 @@
GetName(args_count),
"",
v8::CpuProfileNode::kNoLineNumberInfo,
- CodeEntry::kInheritsSecurityToken);
+ TokenEnumerator::kInheritsSecurityToken);
code_entries_.Add(entry);
return entry;
}
@@ -666,27 +691,6 @@
}
-const char* CpuProfilesCollection::GetName(String* name) {
- if (name->IsString()) {
- char* c_name =
- name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL).Detach();
- HashMap::Entry* cache_entry =
- function_and_resource_names_.Lookup(c_name,
- name->Hash(),
- true);
- if (cache_entry->value == NULL) {
- // New entry added.
- cache_entry->value = c_name;
- } else {
- DeleteArray(c_name);
- }
- return reinterpret_cast<const char*>(cache_entry->value);
- } else {
- return "";
- }
-}
-
-
const char* CpuProfilesCollection::GetName(int args_count) {
ASSERT(args_count >= 0);
if (args_count_names_.length() <= args_count) {
diff --git a/src/profile-generator.h b/src/profile-generator.h
index 18265f1..be0e94e 100644
--- a/src/profile-generator.h
+++ b/src/profile-generator.h
@@ -41,6 +41,9 @@
~TokenEnumerator();
int GetTokenId(Object* token);
+ static const int kNoSecurityToken = -1;
+ static const int kInheritsSecurityToken = -2;
+
private:
static void TokenRemovedCallback(v8::Persistent<v8::Value> handle,
void* parameter);
@@ -53,6 +56,28 @@
};
+// Provides a storage of strings allocated in C++ heap, to hold them
+// forever, even if they disappear from JS heap or external storage.
+class StringsStorage {
+ public:
+ StringsStorage();
+ ~StringsStorage();
+
+ const char* GetName(String* name);
+
+ private:
+ INLINE(static bool StringsMatch(void* key1, void* key2)) {
+ return strcmp(reinterpret_cast<char*>(key1),
+ reinterpret_cast<char*>(key2)) == 0;
+ }
+
+ // String::Hash -> const char*
+ HashMap names_;
+
+ DISALLOW_COPY_AND_ASSIGN(StringsStorage);
+};
+
+
class CodeEntry {
public:
explicit INLINE(CodeEntry(int security_token_id));
@@ -78,8 +103,6 @@
void CopyData(const CodeEntry& source);
static const char* kEmptyNamePrefix;
- static const int kNoSecurityToken = -1;
- static const int kInheritsSecurityToken = -2;
private:
unsigned call_uid_;
@@ -257,10 +280,12 @@
String* title,
double actual_sampling_rate);
List<CpuProfile*>* Profiles(int security_token_id);
+ const char* GetName(String* name) {
+ return function_and_resource_names_.GetName(name);
+ }
CpuProfile* GetProfile(int security_token_id, unsigned uid);
inline bool is_last_profile();
- const char* GetName(String* name);
CodeEntry* NewCodeEntry(Logger::LogEventsAndTags tag,
String* name, String* resource_name, int line_number);
CodeEntry* NewCodeEntry(Logger::LogEventsAndTags tag, const char* name);
@@ -279,17 +304,11 @@
List<CpuProfile*>* GetProfilesList(int security_token_id);
int TokenToIndex(int security_token_id);
- INLINE(static bool StringsMatch(void* key1, void* key2)) {
- return strcmp(reinterpret_cast<char*>(key1),
- reinterpret_cast<char*>(key2)) == 0;
- }
-
INLINE(static bool UidsMatch(void* key1, void* key2)) {
return key1 == key2;
}
- // String::Hash -> const char*
- HashMap function_and_resource_names_;
+ StringsStorage function_and_resource_names_;
// args_count -> char*
List<char*> args_count_names_;
List<CodeEntry*> code_entries_;
diff --git a/src/serialize.cc b/src/serialize.cc
index 06c6df7..e610e28 100644
--- a/src/serialize.cc
+++ b/src/serialize.cc
@@ -229,6 +229,10 @@
DEBUG_ADDRESS,
Debug::k_after_break_target_address << kDebugIdShift,
"Debug::after_break_target_address()");
+ Add(Debug_Address(Debug::k_debug_break_slot_address).address(),
+ DEBUG_ADDRESS,
+ Debug::k_debug_break_slot_address << kDebugIdShift,
+ "Debug::debug_break_slot_address()");
Add(Debug_Address(Debug::k_debug_break_return_address).address(),
DEBUG_ADDRESS,
Debug::k_debug_break_return_address << kDebugIdShift,
diff --git a/src/version.cc b/src/version.cc
index 7fa25a2..2172524 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,7 +34,7 @@
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 2
#define MINOR_VERSION 2
-#define BUILD_NUMBER 15
+#define BUILD_NUMBER 16
#define PATCH_LEVEL 0
#define CANDIDATE_VERSION false
diff --git a/src/x64/assembler-x64-inl.h b/src/x64/assembler-x64-inl.h
index 4c69510..70bcdb1 100644
--- a/src/x64/assembler-x64-inl.h
+++ b/src/x64/assembler-x64-inl.h
@@ -210,6 +210,10 @@
// Special handling of js_return when a break point is set (call
// instruction has been inserted).
Memory::int32_at(pc_ + 1) -= static_cast<int32_t>(delta); // relocate entry
+ } else if (rmode_ == DEBUG_BREAK_SLOT && IsPatchedDebugBreakSlotSequence()) {
+ // Special handling of debug break slot when a break point is set (call
+ // instruction has been inserted).
+ Memory::int32_at(pc_ + 1) -= static_cast<int32_t>(delta); // relocate entry
}
}
@@ -298,6 +302,11 @@
}
+bool RelocInfo::IsPatchedDebugBreakSlotSequence() {
+ return !Assembler::IsNop(pc());
+}
+
+
Address RelocInfo::call_address() {
ASSERT(IsPatchedReturnSequence());
return Memory::Address_at(
@@ -341,8 +350,10 @@
visitor->VisitExternalReference(target_reference_address());
#ifdef ENABLE_DEBUGGER_SUPPORT
} else if (Debug::has_break_points() &&
- RelocInfo::IsJSReturn(mode) &&
- IsPatchedReturnSequence()) {
+ ((RelocInfo::IsJSReturn(mode) &&
+ IsPatchedReturnSequence()) ||
+ (RelocInfo::IsDebugBreakSlot(mode) &&
+ IsPatchedDebugBreakSlotSequence()))) {
visitor->VisitDebugTarget(this);
#endif
} else if (mode == RelocInfo::RUNTIME_ENTRY) {
diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc
index 9f26496..d77c09f 100644
--- a/src/x64/assembler-x64.cc
+++ b/src/x64/assembler-x64.cc
@@ -2800,6 +2800,13 @@
}
+void Assembler::RecordDebugBreakSlot() {
+ WriteRecordedPositions();
+ EnsureSpace ensure_space(this);
+ RecordRelocInfo(RelocInfo::DEBUG_BREAK_SLOT);
+}
+
+
void Assembler::RecordComment(const char* msg) {
if (FLAG_debug_code) {
EnsureSpace ensure_space(this);
@@ -2822,13 +2829,16 @@
}
-void Assembler::WriteRecordedPositions() {
+bool Assembler::WriteRecordedPositions() {
+ bool written = false;
+
// Write the statement position if it is different from what was written last
// time.
if (current_statement_position_ != written_statement_position_) {
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::STATEMENT_POSITION, current_statement_position_);
written_statement_position_ = current_statement_position_;
+ written = true;
}
// Write the position if it is different from what was written last time and
@@ -2838,7 +2848,11 @@
EnsureSpace ensure_space(this);
RecordRelocInfo(RelocInfo::POSITION, current_position_);
written_position_ = current_position_;
+ written = true;
}
+
+ // Return whether something was written.
+ return written;
}
diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h
index cceaccf..c7e737c 100644
--- a/src/x64/assembler-x64.h
+++ b/src/x64/assembler-x64.h
@@ -455,6 +455,11 @@
// return address. TODO: Use return sequence length instead.
// Should equal Debug::kX64JSReturnSequenceLength - kCallTargetAddressOffset;
static const int kPatchReturnSequenceAddressOffset = 13 - 4;
+ // Distance between start of patched debug break slot and where the
+ // 32-bit displacement of a near call would be, relative to the pushed
+ // return address. TODO: Use return sequence length instead.
+ // Should equal Debug::kX64JSReturnSequenceLength - kCallTargetAddressOffset;
+ static const int kPatchDebugBreakSlotAddressOffset = 13 - 4;
// TODO(X64): Rename this, removing the "Real", after changing the above.
static const int kRealPatchReturnSequenceAddressOffset = 2;
@@ -463,6 +468,10 @@
static const int kCallInstructionLength = 13;
static const int kJSReturnSequenceLength = 13;
+ // The debug break slot must be able to contain a call instruction.
+ static const int kDebugBreakSlotLength = kCallInstructionLength;
+
+
// ---------------------------------------------------------------------------
// Code generation
//
@@ -1135,13 +1144,16 @@
// Mark address of the ExitJSFrame code.
void RecordJSReturn();
+ // Mark address of a debug break slot.
+ void RecordDebugBreakSlot();
+
// Record a comment relocation entry that can be used by a disassembler.
// Use --debug_code to enable.
void RecordComment(const char* msg);
void RecordPosition(int pos);
void RecordStatementPosition(int pos);
- void WriteRecordedPositions();
+ bool WriteRecordedPositions();
int pc_offset() const { return static_cast<int>(pc_ - buffer_); }
int current_statement_position() const { return current_statement_position_; }
@@ -1159,6 +1171,8 @@
return static_cast<int>(reloc_info_writer.pos() - pc_);
}
+ static bool IsNop(Address addr) { return *addr == 0x90; }
+
// Avoid overflows for displacements etc.
static const int kMaximalBufferSize = 512*MB;
static const int kMinimalBufferSize = 4*KB;
diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h
index 823a257..5b1c8af 100644
--- a/src/x64/codegen-x64.h
+++ b/src/x64/codegen-x64.h
@@ -314,7 +314,9 @@
static bool ShouldGenerateLog(Expression* type);
#endif
- static void RecordPositions(MacroAssembler* masm, int pos);
+ static bool RecordPositions(MacroAssembler* masm,
+ int pos,
+ bool right_here = false);
// Accessors
MacroAssembler* masm() { return masm_; }
diff --git a/src/x64/debug-x64.cc b/src/x64/debug-x64.cc
index 2f62cba..9659254 100644
--- a/src/x64/debug-x64.cc
+++ b/src/x64/debug-x64.cc
@@ -181,10 +181,31 @@
}
+void Debug::GenerateSlot(MacroAssembler* masm) {
+ // Generate enough nop's to make space for a call instruction.
+ Label check_codesize;
+ __ bind(&check_codesize);
+ __ RecordDebugBreakSlot();
+ for (int i = 0; i < Assembler::kDebugBreakSlotLength; i++) {
+ __ nop();
+ }
+ ASSERT_EQ(Assembler::kDebugBreakSlotLength,
+ masm->SizeOfCodeGeneratedSince(&check_codesize));
+}
+
+
+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);
+}
+
+
void Debug::GeneratePlainReturnLiveEdit(MacroAssembler* masm) {
masm->Abort("LiveEdit frame dropping is not supported on x64");
}
+
void Debug::GenerateFrameDropperLiveEdit(MacroAssembler* masm) {
masm->Abort("LiveEdit frame dropping is not supported on x64");
}
@@ -217,6 +238,28 @@
Assembler::kJSReturnSequenceLength - Assembler::kCallInstructionLength);
}
+
+bool BreakLocationIterator::IsDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ // Check whether the debug break slot instructions have been patched.
+ return !Assembler::IsNop(rinfo()->pc());
+}
+
+
+void BreakLocationIterator::SetDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ rinfo()->PatchCodeWithCall(
+ Debug::debug_break_slot()->entry(),
+ Assembler::kDebugBreakSlotLength - Assembler::kCallInstructionLength);
+}
+
+
+void BreakLocationIterator::ClearDebugBreakAtSlot() {
+ ASSERT(IsDebugBreakSlot());
+ rinfo()->PatchCode(original_rinfo()->pc(), Assembler::kDebugBreakSlotLength);
+}
+
+
#endif // ENABLE_DEBUGGER_SUPPORT
} } // namespace v8::internal
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index b520e56..9123125 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -612,30 +612,33 @@
}
-static int dispose_count = 0;
-static void DisposeExternalStringCount(
- String::ExternalStringResourceBase* resource) {
- dispose_count++;
-}
+class TestAsciiResourceWithDisposeControl: public TestAsciiResource {
+ public:
+ static int dispose_calls;
+
+ TestAsciiResourceWithDisposeControl(const char* data, bool dispose)
+ : TestAsciiResource(data),
+ dispose_(dispose) { }
+
+ void Dispose() {
+ ++dispose_calls;
+ if (dispose_) delete this;
+ }
+ private:
+ bool dispose_;
+};
-static void DisposeExternalStringDeleteAndCount(
- String::ExternalStringResourceBase* resource) {
- delete resource;
- dispose_count++;
-}
+int TestAsciiResourceWithDisposeControl::dispose_calls = 0;
-TEST(ExternalStringWithResourceDisposeCallback) {
+TEST(ExternalStringWithDisposeHandling) {
const char* c_source = "1 + 2 * 3";
- // Set an external string collected callback which does not delete the object.
- v8::V8::SetExternalStringDiposeCallback(DisposeExternalStringCount);
-
// Use a stack allocated external string resource allocated object.
- dispose_count = 0;
TestAsciiResource::dispose_count = 0;
- TestAsciiResource res_stack(i::StrDup(c_source));
+ TestAsciiResourceWithDisposeControl::dispose_calls = 0;
+ TestAsciiResourceWithDisposeControl res_stack(i::StrDup(c_source), false);
{
v8::HandleScope scope;
LocalContext env;
@@ -649,16 +652,14 @@
}
v8::internal::CompilationCache::Clear();
v8::internal::Heap::CollectAllGarbage(false);
- CHECK_EQ(1, dispose_count);
+ CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
CHECK_EQ(0, TestAsciiResource::dispose_count);
- // Set an external string collected callback which does delete the object.
- v8::V8::SetExternalStringDiposeCallback(DisposeExternalStringDeleteAndCount);
-
// Use a heap allocated external string resource allocated object.
- dispose_count = 0;
TestAsciiResource::dispose_count = 0;
- TestAsciiResource* res_heap = new TestAsciiResource(i::StrDup(c_source));
+ TestAsciiResourceWithDisposeControl::dispose_calls = 0;
+ TestAsciiResource* res_heap =
+ new TestAsciiResourceWithDisposeControl(i::StrDup(c_source), true);
{
v8::HandleScope scope;
LocalContext env;
@@ -672,7 +673,7 @@
}
v8::internal::CompilationCache::Clear();
v8::internal::Heap::CollectAllGarbage(false);
- CHECK_EQ(1, dispose_count);
+ CHECK_EQ(1, TestAsciiResourceWithDisposeControl::dispose_calls);
CHECK_EQ(1, TestAsciiResource::dispose_count);
}
@@ -8497,6 +8498,30 @@
}
+// Verifies that the Handle<String> and const char* versions of the API produce
+// the same results (at least for one trivial case).
+TEST(PreCompileAPIVariationsAreSame) {
+ v8::V8::Initialize();
+ v8::HandleScope scope;
+
+ const char* cstring = "function foo(a) { return a+1; }";
+ v8::ScriptData* sd_from_cstring =
+ v8::ScriptData::PreCompile(cstring, i::StrLength(cstring));
+
+ TestAsciiResource* resource = new TestAsciiResource(cstring);
+ v8::ScriptData* sd_from_istring = v8::ScriptData::PreCompile(
+ v8::String::NewExternal(resource));
+
+ CHECK_EQ(sd_from_cstring->Length(), sd_from_istring->Length());
+ CHECK_EQ(0, memcmp(sd_from_cstring->Data(),
+ sd_from_istring->Data(),
+ sd_from_cstring->Length()));
+
+ delete sd_from_cstring;
+ delete sd_from_istring;
+}
+
+
// This tests that we do not allow dictionary load/call inline caches
// to use functions that have not yet been compiled. The potential
// problem of loading a function that has not yet been compiled can
diff --git a/test/cctest/test-cpu-profiler.cc b/test/cctest/test-cpu-profiler.cc
index f587fc8..0e6f09d 100644
--- a/test/cctest/test-cpu-profiler.cc
+++ b/test/cctest/test-cpu-profiler.cc
@@ -16,6 +16,7 @@
using i::ProfileGenerator;
using i::ProfileNode;
using i::ProfilerEventsProcessor;
+using i::TokenEnumerator;
TEST(StartStop) {
@@ -115,7 +116,7 @@
processor.CodeCreateEvent(i::Logger::STUB_TAG, 3, ToAddress(0x1600), 0x10);
processor.CodeDeleteEvent(ToAddress(0x1600));
processor.FunctionCreateEvent(ToAddress(0x1700), ToAddress(0x1000),
- CodeEntry::kNoSecurityToken);
+ TokenEnumerator::kNoSecurityToken);
// Enqueue a tick event to enable code events processing.
EnqueueTickSampleEvent(&processor, ToAddress(0x1000));
@@ -178,7 +179,7 @@
processor.Stop();
processor.Join();
CpuProfile* profile =
- profiles.StopProfiling(CodeEntry::kNoSecurityToken, "", 1);
+ profiles.StopProfiling(TokenEnumerator::kNoSecurityToken, "", 1);
CHECK_NE(NULL, profile);
// Check call trees.
diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc
index 612e4fc..4c3ff5e 100644
--- a/test/cctest/test-debug.cc
+++ b/test/cctest/test-debug.cc
@@ -1231,6 +1231,11 @@
SetBreakPoint(foo, 0);
CallWithBreakPoints(env->Global(), foo, 1, 25);
+ // Test debug break slot break point with garbage collection.
+ foo = CompileFunction(&env, "function foo(){var a;}", "foo");
+ SetBreakPoint(foo, 0);
+ CallWithBreakPoints(env->Global(), foo, 1, 25);
+
v8::Debug::SetDebugEventListener(NULL);
CheckDebuggerUnloaded();
}
@@ -1660,7 +1665,7 @@
f->Call(env->Global(), 0, NULL);
CHECK_EQ(1, break_point_hit_count);
- ChangeScriptBreakPointConditionFromJS(sbp1, "a % 2 == 0");
+ ChangeScriptBreakPointConditionFromJS(sbp1, "x % 2 == 0");
break_point_hit_count = 0;
for (int i = 0; i < 10; i++) {
f->Call(env->Global(), 0, NULL);
@@ -2144,17 +2149,19 @@
v8::Local<v8::Function> foo = CompileFunction(&env,
"function foo(x) {"
" var a;"
- " y=0; /* To ensure break location.*/"
+ " y=0;" // To ensure break location 1.
" a=x;"
+ " y=0;" // To ensure break location 2.
"}",
"foo");
- const int foo_break_position = 15;
+ const int foo_break_position_1 = 15;
+ const int foo_break_position_2 = 29;
// Arguments with one parameter "Hello, world!"
v8::Handle<v8::Value> argv_foo[1] = { v8::String::New("Hello, world!") };
// Call foo with breakpoint set before a=x and undefined as parameter.
- int bp = SetBreakPoint(foo, foo_break_position);
+ int bp = SetBreakPoint(foo, foo_break_position_1);
checks = checks_uu;
foo->Call(env->Global(), 0, NULL);
@@ -2164,7 +2171,7 @@
// Call foo with breakpoint set after a=x and parameter "Hello, world!".
ClearBreakPoint(bp);
- SetBreakPoint(foo, foo_break_position + 1);
+ SetBreakPoint(foo, foo_break_position_2);
checks = checks_hh;
foo->Call(env->Global(), 1, argv_foo);
@@ -2426,6 +2433,9 @@
v8::HandleScope scope;
DebugLocalContext env;
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
// Create a function for testing stepping of keyed load. The statement 'y=1'
// is there to have more than one breakable statement in the loop, TODO(315).
v8::Local<v8::Function> foo = CompileFunction(
@@ -2451,9 +2461,6 @@
v8::Handle<v8::Value> args[kArgc] = { a };
foo->Call(env->Global(), kArgc, args);
- // Register a debug event listener which steps and counts.
- v8::Debug::SetDebugEventListener(DebugEventStep);
-
// Setup break point and step through the function.
SetBreakPoint(foo, 3);
step_action = StepNext;
@@ -2461,7 +2468,7 @@
foo->Call(env->Global(), kArgc, args);
// With stepping all break locations are hit.
- CHECK_EQ(22, break_point_hit_count);
+ CHECK_EQ(33, break_point_hit_count);
v8::Debug::SetDebugEventListener(NULL);
CheckDebuggerUnloaded();
@@ -2473,6 +2480,9 @@
v8::HandleScope scope;
DebugLocalContext env;
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
// Create a function for testing stepping of keyed store. The statement 'y=1'
// is there to have more than one breakable statement in the loop, TODO(315).
v8::Local<v8::Function> foo = CompileFunction(
@@ -2497,9 +2507,6 @@
v8::Handle<v8::Value> args[kArgc] = { a };
foo->Call(env->Global(), kArgc, args);
- // Register a debug event listener which steps and counts.
- v8::Debug::SetDebugEventListener(DebugEventStep);
-
// Setup break point and step through the function.
SetBreakPoint(foo, 3);
step_action = StepNext;
@@ -2507,7 +2514,7 @@
foo->Call(env->Global(), kArgc, args);
// With stepping all break locations are hit.
- CHECK_EQ(22, break_point_hit_count);
+ CHECK_EQ(32, break_point_hit_count);
v8::Debug::SetDebugEventListener(NULL);
CheckDebuggerUnloaded();
@@ -2519,6 +2526,9 @@
v8::HandleScope scope;
DebugLocalContext env;
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
// Create a function for testing stepping of named load.
v8::Local<v8::Function> foo = CompileFunction(
&env,
@@ -2541,9 +2551,6 @@
// Call function without any break points to ensure inlining is in place.
foo->Call(env->Global(), 0, NULL);
- // Register a debug event listener which steps and counts.
- v8::Debug::SetDebugEventListener(DebugEventStep);
-
// Setup break point and step through the function.
SetBreakPoint(foo, 4);
step_action = StepNext;
@@ -2551,7 +2558,7 @@
foo->Call(env->Global(), 0, NULL);
// With stepping all break locations are hit.
- CHECK_EQ(41, break_point_hit_count);
+ CHECK_EQ(53, break_point_hit_count);
v8::Debug::SetDebugEventListener(NULL);
CheckDebuggerUnloaded();
@@ -2563,6 +2570,9 @@
v8::HandleScope scope;
DebugLocalContext env;
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
// Create a function for testing stepping.
v8::Local<v8::Function> foo = CompileFunction(&env,
"function bar() {};"
@@ -2573,15 +2583,12 @@
" a=1;b=2;x=a;y[index]=3;x=y[index];bar();}", "foo");
SetBreakPoint(foo, 0);
- // Register a debug event listener which steps and counts.
- v8::Debug::SetDebugEventListener(DebugEventStep);
-
step_action = StepIn;
break_point_hit_count = 0;
foo->Call(env->Global(), 0, NULL);
// With stepping all break locations are hit.
- CHECK_EQ(8, break_point_hit_count);
+ CHECK_EQ(11, break_point_hit_count);
v8::Debug::SetDebugEventListener(NULL);
CheckDebuggerUnloaded();
@@ -2601,6 +2608,66 @@
}
+TEST(DebugStepDeclarations) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
+ // Create a function for testing stepping.
+ const char* src = "function foo() { "
+ " var a;"
+ " var b = 1;"
+ " var c = foo;"
+ " var d = Math.floor;"
+ " var e = b + d(1.2);"
+ "}";
+ v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+ SetBreakPoint(foo, 0);
+
+ // Stepping through the declarations.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(6, break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugStepLocals) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
+ // Create a function for testing stepping.
+ const char* src = "function foo() { "
+ " var a,b;"
+ " a = 1;"
+ " b = a + 2;"
+ " b = 1 + 2 + 3;"
+ " a = Math.floor(b);"
+ "}";
+ v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+ SetBreakPoint(foo, 0);
+
+ // Stepping through the declarations.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(6, break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
TEST(DebugStepIf) {
v8::HandleScope scope;
DebugLocalContext env;
@@ -2627,14 +2694,14 @@
break_point_hit_count = 0;
v8::Handle<v8::Value> argv_true[argc] = { v8::True() };
foo->Call(env->Global(), argc, argv_true);
- CHECK_EQ(3, break_point_hit_count);
+ CHECK_EQ(4, break_point_hit_count);
// Stepping through the false part.
step_action = StepIn;
break_point_hit_count = 0;
v8::Handle<v8::Value> argv_false[argc] = { v8::False() };
foo->Call(env->Global(), argc, argv_false);
- CHECK_EQ(4, break_point_hit_count);
+ CHECK_EQ(5, break_point_hit_count);
// Get rid of the debug event listener.
v8::Debug::SetDebugEventListener(NULL);
@@ -2662,6 +2729,7 @@
" case 3:"
" d = 1;"
" e = 1;"
+ " f = 1;"
" break;"
" }"
"}";
@@ -2673,21 +2741,97 @@
break_point_hit_count = 0;
v8::Handle<v8::Value> argv_1[argc] = { v8::Number::New(1) };
foo->Call(env->Global(), argc, argv_1);
- CHECK_EQ(4, break_point_hit_count);
+ CHECK_EQ(6, break_point_hit_count);
// Another case.
step_action = StepIn;
break_point_hit_count = 0;
v8::Handle<v8::Value> argv_2[argc] = { v8::Number::New(2) };
foo->Call(env->Global(), argc, argv_2);
- CHECK_EQ(3, break_point_hit_count);
+ CHECK_EQ(5, break_point_hit_count);
// Last case.
step_action = StepIn;
break_point_hit_count = 0;
v8::Handle<v8::Value> argv_3[argc] = { v8::Number::New(3) };
foo->Call(env->Global(), argc, argv_3);
- CHECK_EQ(4, break_point_hit_count);
+ CHECK_EQ(7, break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugStepWhile) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
+ // Create a function for testing stepping.
+ const int argc = 1;
+ const char* src = "function foo(x) { "
+ " var a = 0;"
+ " while (a < x) {"
+ " a++;"
+ " }"
+ "}";
+ v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+ SetBreakPoint(foo, 8); // "var a = 0;"
+
+ // Looping 10 times.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ v8::Handle<v8::Value> argv_10[argc] = { v8::Number::New(10) };
+ foo->Call(env->Global(), argc, argv_10);
+ CHECK_EQ(23, break_point_hit_count);
+
+ // Looping 100 times.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ v8::Handle<v8::Value> argv_100[argc] = { v8::Number::New(100) };
+ foo->Call(env->Global(), argc, argv_100);
+ CHECK_EQ(203, break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugStepDoWhile) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
+ // Create a function for testing stepping.
+ const int argc = 1;
+ const char* src = "function foo(x) { "
+ " var a = 0;"
+ " do {"
+ " a++;"
+ " } while (a < x)"
+ "}";
+ v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+ SetBreakPoint(foo, 8); // "var a = 0;"
+
+ // Looping 10 times.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ v8::Handle<v8::Value> argv_10[argc] = { v8::Number::New(10) };
+ foo->Call(env->Global(), argc, argv_10);
+ CHECK_EQ(22, break_point_hit_count);
+
+ // Looping 100 times.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ v8::Handle<v8::Value> argv_100[argc] = { v8::Number::New(100) };
+ foo->Call(env->Global(), argc, argv_100);
+ CHECK_EQ(202, break_point_hit_count);
// Get rid of the debug event listener.
v8::Debug::SetDebugEventListener(NULL);
@@ -2733,6 +2877,210 @@
}
+TEST(DebugStepForContinue) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
+ // Create a function for testing stepping.
+ const int argc = 1;
+ const char* src = "function foo(x) { "
+ " var a = 0;"
+ " var b = 0;"
+ " var c = 0;"
+ " for (var i = 0; i < x; i++) {"
+ " a++;"
+ " if (a % 2 == 0) continue;"
+ " b++;"
+ " c++;"
+ " }"
+ " return b;"
+ "}";
+ v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+ v8::Handle<v8::Value> result;
+ SetBreakPoint(foo, 8); // "var a = 0;"
+
+ // Each loop generates 4 or 5 steps depending on whether a is equal.
+
+ // Looping 10 times.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ v8::Handle<v8::Value> argv_10[argc] = { v8::Number::New(10) };
+ result = foo->Call(env->Global(), argc, argv_10);
+ CHECK_EQ(5, result->Int32Value());
+ CHECK_EQ(50, break_point_hit_count);
+
+ // Looping 100 times.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ v8::Handle<v8::Value> argv_100[argc] = { v8::Number::New(100) };
+ result = foo->Call(env->Global(), argc, argv_100);
+ CHECK_EQ(50, result->Int32Value());
+ CHECK_EQ(455, break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugStepForBreak) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
+ // Create a function for testing stepping.
+ const int argc = 1;
+ const char* src = "function foo(x) { "
+ " var a = 0;"
+ " var b = 0;"
+ " var c = 0;"
+ " for (var i = 0; i < 1000; i++) {"
+ " a++;"
+ " if (a == x) break;"
+ " b++;"
+ " c++;"
+ " }"
+ " return b;"
+ "}";
+ v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+ v8::Handle<v8::Value> result;
+ SetBreakPoint(foo, 8); // "var a = 0;"
+
+ // Each loop generates 5 steps except for the last (when break is executed)
+ // which only generates 4.
+
+ // Looping 10 times.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ v8::Handle<v8::Value> argv_10[argc] = { v8::Number::New(10) };
+ result = foo->Call(env->Global(), argc, argv_10);
+ CHECK_EQ(9, result->Int32Value());
+ CHECK_EQ(53, break_point_hit_count);
+
+ // Looping 100 times.
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ v8::Handle<v8::Value> argv_100[argc] = { v8::Number::New(100) };
+ result = foo->Call(env->Global(), argc, argv_100);
+ CHECK_EQ(99, result->Int32Value());
+ CHECK_EQ(503, break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugStepForIn) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
+ v8::Local<v8::Function> foo;
+ const char* src_1 = "function foo() { "
+ " var a = [1, 2];"
+ " for (x in a) {"
+ " b = 0;"
+ " }"
+ "}";
+ foo = CompileFunction(&env, src_1, "foo");
+ SetBreakPoint(foo, 0); // "var a = ..."
+
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(6, break_point_hit_count);
+
+ const char* src_2 = "function foo() { "
+ " var a = {a:[1, 2, 3]};"
+ " for (x in a.a) {"
+ " b = 0;"
+ " }"
+ "}";
+ foo = CompileFunction(&env, src_2, "foo");
+ SetBreakPoint(foo, 0); // "var a = ..."
+
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(8, break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugStepWith) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
+ // Create a function for testing stepping.
+ const char* src = "function foo(x) { "
+ " var a = {};"
+ " with (a) {}"
+ " with (b) {}"
+ "}";
+ env->Global()->Set(v8::String::New("b"), v8::Object::New());
+ v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+ v8::Handle<v8::Value> result;
+ SetBreakPoint(foo, 8); // "var a = {};"
+
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(4, break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
+TEST(DebugConditional) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // Register a debug event listener which steps and counts.
+ v8::Debug::SetDebugEventListener(DebugEventStep);
+
+ // Create a function for testing stepping.
+ const char* src = "function foo(x) { "
+ " var a;"
+ " a = x ? 1 : 2;"
+ " return a;"
+ "}";
+ v8::Local<v8::Function> foo = CompileFunction(&env, src, "foo");
+ SetBreakPoint(foo, 0); // "var a;"
+
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ foo->Call(env->Global(), 0, NULL);
+ CHECK_EQ(5, break_point_hit_count);
+
+ step_action = StepIn;
+ break_point_hit_count = 0;
+ const int argc = 1;
+ v8::Handle<v8::Value> argv_true[argc] = { v8::True() };
+ foo->Call(env->Global(), argc, argv_true);
+ CHECK_EQ(5, break_point_hit_count);
+
+ // Get rid of the debug event listener.
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
TEST(StepInOutSimple) {
v8::HandleScope scope;
DebugLocalContext env;
@@ -2854,7 +3202,7 @@
// Step through invocation of a.
step_action = StepIn;
break_point_hit_count = 0;
- expected_step_sequence = "abaca";
+ expected_step_sequence = "abbaca";
a->Call(env->Global(), 0, NULL);
CHECK_EQ(StrLength(expected_step_sequence),
break_point_hit_count);
@@ -2923,7 +3271,7 @@
foo->Call(env->Global(), 0, NULL);
// With stepping all break locations are hit.
- CHECK_EQ(6, break_point_hit_count);
+ CHECK_EQ(7, break_point_hit_count);
v8::Debug::SetDebugEventListener(NULL);
CheckDebuggerUnloaded();
@@ -2967,14 +3315,14 @@
// Check stepping where the if condition in bar is false.
break_point_hit_count = 0;
foo->Call(env->Global(), 0, NULL);
- CHECK_EQ(4, break_point_hit_count);
+ CHECK_EQ(6, break_point_hit_count);
// Check stepping where the if condition in bar is true.
break_point_hit_count = 0;
const int argc = 1;
v8::Handle<v8::Value> argv[argc] = { v8::True() };
foo->Call(env->Global(), argc, argv);
- CHECK_EQ(6, break_point_hit_count);
+ CHECK_EQ(8, break_point_hit_count);
v8::Debug::SetDebugEventListener(NULL);
CheckDebuggerUnloaded();
@@ -3267,14 +3615,13 @@
b->Call(env->Global(), 0, NULL);
CHECK_EQ(StrLength(expected_step_sequence),
break_point_hit_count);
-
// Step through invocation of d + e.
v8::Local<v8::Function> d = CompileFunction(&env, src, "d");
SetBreakPoint(d, 0);
ChangeBreakOnException(false, true);
step_action = StepIn;
break_point_hit_count = 0;
- expected_step_sequence = "dded";
+ expected_step_sequence = "ddedd";
d->Call(env->Global(), 0, NULL);
CHECK_EQ(StrLength(expected_step_sequence),
break_point_hit_count);
@@ -3283,7 +3630,7 @@
ChangeBreakOnException(true, true);
step_action = StepIn;
break_point_hit_count = 0;
- expected_step_sequence = "ddeed";
+ expected_step_sequence = "ddeedd";
d->Call(env->Global(), 0, NULL);
CHECK_EQ(StrLength(expected_step_sequence),
break_point_hit_count);
@@ -3294,7 +3641,7 @@
ChangeBreakOnException(false, true);
step_action = StepIn;
break_point_hit_count = 0;
- expected_step_sequence = "ffghf";
+ expected_step_sequence = "ffghhff";
f->Call(env->Global(), 0, NULL);
CHECK_EQ(StrLength(expected_step_sequence),
break_point_hit_count);
@@ -3303,7 +3650,7 @@
ChangeBreakOnException(true, true);
step_action = StepIn;
break_point_hit_count = 0;
- expected_step_sequence = "ffghhf";
+ expected_step_sequence = "ffghhhff";
f->Call(env->Global(), 0, NULL);
CHECK_EQ(StrLength(expected_step_sequence),
break_point_hit_count);
diff --git a/test/cctest/test-disasm-ia32.cc b/test/cctest/test-disasm-ia32.cc
index f94cd45..c8e0197 100644
--- a/test/cctest/test-disasm-ia32.cc
+++ b/test/cctest/test-disasm-ia32.cc
@@ -244,6 +244,9 @@
__ test(edx, Immediate(12345));
__ test(edx, Operand(ebx, ecx, times_8, 10000));
+ __ test(Operand(esi, edi, times_1, -20000000), Immediate(300000000));
+ __ test_b(edx, Operand(ecx, ebx, times_2, 1000));
+ __ test_b(Operand(eax, -20), 0x9A);
__ nop();
__ xor_(edx, 12345);
diff --git a/test/cctest/test-heap.cc b/test/cctest/test-heap.cc
index 0a919a1..d30b5ab 100644
--- a/test/cctest/test-heap.cc
+++ b/test/cctest/test-heap.cc
@@ -957,3 +957,42 @@
// Check that region covering inobject property 1 is marked dirty.
CHECK(page->IsRegionDirty(clone_addr + (object_size - kPointerSize)));
}
+
+TEST(TestCodeFlushing) {
+ i::FLAG_allow_natives_syntax = true;
+ InitializeVM();
+ v8::HandleScope scope;
+ const char* source = "function foo() {"
+ " var x = 42;"
+ " var y = 42;"
+ " var z = x + y;"
+ "};"
+ "foo()";
+ Handle<String> foo_name = Factory::LookupAsciiSymbol("foo");
+
+ // This compile will add the code to the compilation cache.
+ CompileRun(source);
+
+ // Check function is compiled.
+ Object* func_value = Top::context()->global()->GetProperty(*foo_name);
+ CHECK(func_value->IsJSFunction());
+ Handle<JSFunction> function(JSFunction::cast(func_value));
+ CHECK(function->shared()->is_compiled());
+
+ Heap::CollectAllGarbage(true);
+ Heap::CollectAllGarbage(true);
+
+ // foo should still be in the compilation cache and therefore not
+ // have been removed.
+ CHECK(function->shared()->is_compiled());
+ Heap::CollectAllGarbage(true);
+ Heap::CollectAllGarbage(true);
+ Heap::CollectAllGarbage(true);
+ Heap::CollectAllGarbage(true);
+
+ // foo should no longer be in the compilation cache
+ CHECK(!function->shared()->is_compiled());
+ // Call foo to get it recompiled.
+ CompileRun("foo()");
+ CHECK(function->shared()->is_compiled());
+}
diff --git a/test/cctest/test-profile-generator.cc b/test/cctest/test-profile-generator.cc
index 418fd36..308f764 100644
--- a/test/cctest/test-profile-generator.cc
+++ b/test/cctest/test-profile-generator.cc
@@ -39,7 +39,7 @@
TEST(TokenEnumerator) {
TokenEnumerator te;
- CHECK_EQ(CodeEntry::kNoSecurityToken, te.GetTokenId(NULL));
+ CHECK_EQ(TokenEnumerator::kNoSecurityToken, te.GetTokenId(NULL));
v8::HandleScope hs;
v8::Local<v8::String> token1(v8::String::New("1"));
CHECK_EQ(0, te.GetTokenId(*v8::Utils::OpenHandle(*token1)));
@@ -65,20 +65,20 @@
TEST(ProfileNodeFindOrAddChild) {
ProfileNode node(NULL, NULL);
- CodeEntry entry1(
- i::Logger::FUNCTION_TAG, "", "aaa", "", 0, CodeEntry::kNoSecurityToken);
+ CodeEntry entry1(i::Logger::FUNCTION_TAG, "", "aaa", "", 0,
+ TokenEnumerator::kNoSecurityToken);
ProfileNode* childNode1 = node.FindOrAddChild(&entry1);
CHECK_NE(NULL, childNode1);
CHECK_EQ(childNode1, node.FindOrAddChild(&entry1));
- CodeEntry entry2(
- i::Logger::FUNCTION_TAG, "", "bbb", "", 0, CodeEntry::kNoSecurityToken);
+ CodeEntry entry2(i::Logger::FUNCTION_TAG, "", "bbb", "", 0,
+ TokenEnumerator::kNoSecurityToken);
ProfileNode* childNode2 = node.FindOrAddChild(&entry2);
CHECK_NE(NULL, childNode2);
CHECK_NE(childNode1, childNode2);
CHECK_EQ(childNode1, node.FindOrAddChild(&entry1));
CHECK_EQ(childNode2, node.FindOrAddChild(&entry2));
- CodeEntry entry3(
- i::Logger::FUNCTION_TAG, "", "ccc", "", 0, CodeEntry::kNoSecurityToken);
+ CodeEntry entry3(i::Logger::FUNCTION_TAG, "", "ccc", "", 0,
+ TokenEnumerator::kNoSecurityToken);
ProfileNode* childNode3 = node.FindOrAddChild(&entry3);
CHECK_NE(NULL, childNode3);
CHECK_NE(childNode1, childNode3);
@@ -119,12 +119,12 @@
} // namespace
TEST(ProfileTreeAddPathFromStart) {
- CodeEntry entry1(
- i::Logger::FUNCTION_TAG, "", "aaa", "", 0, CodeEntry::kNoSecurityToken);
- CodeEntry entry2(
- i::Logger::FUNCTION_TAG, "", "bbb", "", 0, CodeEntry::kNoSecurityToken);
- CodeEntry entry3(
- i::Logger::FUNCTION_TAG, "", "ccc", "", 0, CodeEntry::kNoSecurityToken);
+ CodeEntry entry1(i::Logger::FUNCTION_TAG, "", "aaa", "", 0,
+ TokenEnumerator::kNoSecurityToken);
+ CodeEntry entry2(i::Logger::FUNCTION_TAG, "", "bbb", "", 0,
+ TokenEnumerator::kNoSecurityToken);
+ CodeEntry entry3(i::Logger::FUNCTION_TAG, "", "ccc", "", 0,
+ TokenEnumerator::kNoSecurityToken);
ProfileTree tree;
ProfileTreeTestHelper helper(&tree);
CHECK_EQ(NULL, helper.Walk(&entry1));
@@ -189,12 +189,12 @@
TEST(ProfileTreeAddPathFromEnd) {
- CodeEntry entry1(
- i::Logger::FUNCTION_TAG, "", "aaa", "", 0, CodeEntry::kNoSecurityToken);
- CodeEntry entry2(
- i::Logger::FUNCTION_TAG, "", "bbb", "", 0, CodeEntry::kNoSecurityToken);
- CodeEntry entry3(
- i::Logger::FUNCTION_TAG, "", "ccc", "", 0, CodeEntry::kNoSecurityToken);
+ CodeEntry entry1(i::Logger::FUNCTION_TAG, "", "aaa", "", 0,
+ TokenEnumerator::kNoSecurityToken);
+ CodeEntry entry2(i::Logger::FUNCTION_TAG, "", "bbb", "", 0,
+ TokenEnumerator::kNoSecurityToken);
+ CodeEntry entry3(i::Logger::FUNCTION_TAG, "", "ccc", "", 0,
+ TokenEnumerator::kNoSecurityToken);
ProfileTree tree;
ProfileTreeTestHelper helper(&tree);
CHECK_EQ(NULL, helper.Walk(&entry1));
@@ -272,8 +272,8 @@
CHECK_EQ(1, empty_tree.root()->total_ticks());
CHECK_EQ(1, empty_tree.root()->self_ticks());
- CodeEntry entry1(
- i::Logger::FUNCTION_TAG, "", "aaa", "", 0, CodeEntry::kNoSecurityToken);
+ CodeEntry entry1(i::Logger::FUNCTION_TAG, "", "aaa", "", 0,
+ TokenEnumerator::kNoSecurityToken);
CodeEntry* e1_path[] = {&entry1};
Vector<CodeEntry*> e1_path_vec(
e1_path, sizeof(e1_path) / sizeof(e1_path[0]));
@@ -294,8 +294,8 @@
CHECK_EQ(1, node1->total_ticks());
CHECK_EQ(1, node1->self_ticks());
- CodeEntry entry2(
- i::Logger::FUNCTION_TAG, "", "bbb", "", 0, CodeEntry::kNoSecurityToken);
+ CodeEntry entry2(i::Logger::FUNCTION_TAG, "", "bbb", "", 0,
+ TokenEnumerator::kNoSecurityToken);
CodeEntry* e1_e2_path[] = {&entry1, &entry2};
Vector<CodeEntry*> e1_e2_path_vec(
e1_e2_path, sizeof(e1_e2_path) / sizeof(e1_e2_path[0]));
@@ -330,8 +330,8 @@
CodeEntry* e2_path[] = {&entry2};
Vector<CodeEntry*> e2_path_vec(
e2_path, sizeof(e2_path) / sizeof(e2_path[0]));
- CodeEntry entry3(
- i::Logger::FUNCTION_TAG, "", "ccc", "", 0, CodeEntry::kNoSecurityToken);
+ CodeEntry entry3(i::Logger::FUNCTION_TAG, "", "ccc", "", 0,
+ TokenEnumerator::kNoSecurityToken);
CodeEntry* e3_path[] = {&entry3};
Vector<CodeEntry*> e3_path_vec(
e3_path, sizeof(e3_path) / sizeof(e3_path[0]));
@@ -394,7 +394,7 @@
CodeEntry entry3(i::Logger::FUNCTION_TAG, "", "ccc", "", 0, token0);
CodeEntry entry4(
i::Logger::FUNCTION_TAG, "", "ddd", "", 0,
- CodeEntry::kInheritsSecurityToken);
+ TokenEnumerator::kInheritsSecurityToken);
{
CodeEntry* e1_e2_path[] = {&entry1, &entry2};
@@ -491,14 +491,14 @@
TEST(CodeMapAddCode) {
CodeMap code_map;
- CodeEntry entry1(
- i::Logger::FUNCTION_TAG, "", "aaa", "", 0, CodeEntry::kNoSecurityToken);
- CodeEntry entry2(
- i::Logger::FUNCTION_TAG, "", "bbb", "", 0, CodeEntry::kNoSecurityToken);
- CodeEntry entry3(
- i::Logger::FUNCTION_TAG, "", "ccc", "", 0, CodeEntry::kNoSecurityToken);
- CodeEntry entry4(
- i::Logger::FUNCTION_TAG, "", "ddd", "", 0, CodeEntry::kNoSecurityToken);
+ CodeEntry entry1(i::Logger::FUNCTION_TAG, "", "aaa", "", 0,
+ TokenEnumerator::kNoSecurityToken);
+ CodeEntry entry2(i::Logger::FUNCTION_TAG, "", "bbb", "", 0,
+ TokenEnumerator::kNoSecurityToken);
+ CodeEntry entry3(i::Logger::FUNCTION_TAG, "", "ccc", "", 0,
+ TokenEnumerator::kNoSecurityToken);
+ CodeEntry entry4(i::Logger::FUNCTION_TAG, "", "ddd", "", 0,
+ TokenEnumerator::kNoSecurityToken);
code_map.AddCode(ToAddress(0x1500), &entry1, 0x200);
code_map.AddCode(ToAddress(0x1700), &entry2, 0x100);
code_map.AddCode(ToAddress(0x1900), &entry3, 0x50);
@@ -525,10 +525,10 @@
TEST(CodeMapMoveAndDeleteCode) {
CodeMap code_map;
- CodeEntry entry1(
- i::Logger::FUNCTION_TAG, "", "aaa", "", 0, CodeEntry::kNoSecurityToken);
- CodeEntry entry2(
- i::Logger::FUNCTION_TAG, "", "bbb", "", 0, CodeEntry::kNoSecurityToken);
+ CodeEntry entry1(i::Logger::FUNCTION_TAG, "", "aaa", "", 0,
+ TokenEnumerator::kNoSecurityToken);
+ CodeEntry entry2(i::Logger::FUNCTION_TAG, "", "bbb", "", 0,
+ TokenEnumerator::kNoSecurityToken);
code_map.AddCode(ToAddress(0x1500), &entry1, 0x200);
code_map.AddCode(ToAddress(0x1700), &entry2, 0x100);
CHECK_EQ(&entry1, code_map.FindEntry(ToAddress(0x1500)));
@@ -601,7 +601,7 @@
generator.RecordTickSample(sample3);
CpuProfile* profile =
- profiles.StopProfiling(CodeEntry::kNoSecurityToken, "", 1);
+ profiles.StopProfiling(TokenEnumerator::kNoSecurityToken, "", 1);
CHECK_NE(NULL, profile);
ProfileTreeTestHelper top_down_test_helper(profile->top_down());
CHECK_EQ(NULL, top_down_test_helper.Walk(entry2));
diff --git a/test/mjsunit/const-eval-init.js b/test/mjsunit/const-eval-init.js
index d3636de..5bcd917 100644
--- a/test/mjsunit/const-eval-init.js
+++ b/test/mjsunit/const-eval-init.js
@@ -91,7 +91,7 @@
var source = "";
// Introduce 100 properties on the context extension object to force
// it in slow case.
- for (var i = 0; i < 100; i++) source += ("var a" + i + " = i;");
+ for (var i = 0; i < 100; i++) source += ("var a" + i + " = " + i + ";");
source += "const x = 10; assertEquals(10, x); x = 11; assertEquals(10, x)";
eval(source);
}
diff --git a/test/mjsunit/debug-conditional-breakpoints.js b/test/mjsunit/debug-conditional-breakpoints.js
index 5859451..bd4cdd1 100644
--- a/test/mjsunit/debug-conditional-breakpoints.js
+++ b/test/mjsunit/debug-conditional-breakpoints.js
@@ -45,7 +45,7 @@
count = 0;
function f() {};
function g() {h(count++)};
-function h(x) {var a=x;};
+function h(x) {var a=x; return a};
// Conditional breakpoint which syntax error.
@@ -136,7 +136,7 @@
// Conditional breakpoint which checks a local variable.
break_point_hit_count = 0;
-bp = Debug.setBreakPoint(h, 0, 0, 'a % 2 == 0');
+bp = Debug.setBreakPoint(h, 0, 23, 'a % 2 == 0');
for (var i = 0; i < 10; i++) {
g();
}
@@ -146,8 +146,8 @@
// Multiple conditional breakpoint which the same condition.
break_point_hit_count = 0;
-bp1 = Debug.setBreakPoint(h, 0, 0, 'a % 2 == 0');
-bp2 = Debug.setBreakPoint(h, 0, 0, 'a % 2 == 0');
+bp1 = Debug.setBreakPoint(h, 0, 23, 'a % 2 == 0');
+bp2 = Debug.setBreakPoint(h, 0, 23, 'a % 2 == 0');
for (var i = 0; i < 10; i++) {
g();
}
@@ -159,8 +159,8 @@
// Multiple conditional breakpoint which different conditions.
break_point_hit_count = 0;
-bp1 = Debug.setBreakPoint(h, 0, 0, 'a % 2 == 0');
-bp2 = Debug.setBreakPoint(h, 0, 0, '(a + 1) % 2 == 0');
+bp1 = Debug.setBreakPoint(h, 0, 23, 'a % 2 == 0');
+bp2 = Debug.setBreakPoint(h, 0, 23, '(a + 1) % 2 == 0');
for (var i = 0; i < 10; i++) {
g();
}
diff --git a/test/mjsunit/debug-step.js b/test/mjsunit/debug-step.js
index a887514..2233e36 100644
--- a/test/mjsunit/debug-step.js
+++ b/test/mjsunit/debug-step.js
@@ -55,8 +55,9 @@
// Test debug event for break point.
function f() {
- for (i = 0; i < 1000; i++) { // Line 1.
- x = 1; // Line 2.
+ var i; // Line 1.
+ for (i = 0; i < 1000; i++) { // Line 2.
+ x = 1; // Line 3.
}
};
@@ -74,7 +75,7 @@
// multiple steps have been requested.
state = 0;
result = -1;
-bp2 = Debug.setBreakPoint(f, 2);
+bp2 = Debug.setBreakPoint(f, 3);
f();
assertEquals(0, result);