Push version 1.2.0 to trunk.
Optimized floating-point operations on ARM.
Added a number of extensions to the debugger API.
Changed the enumeration order for unsigned integer keys to always be numerical order.
Added a "read" extension to the shell sample.
Added support for Array.prototype.reduce and Array.prototype.reduceRight.
Added an option to the SCons build to control Microsoft Visual C++ link-time code generation.
Fixed a number of bugs (in particular issue 315, issue 316, issue 317 and issue 318).
git-svn-id: http://v8.googlecode.com/svn/trunk@1781 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/ChangeLog b/ChangeLog
index d5c30c7..7e28abd 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2009-04-23: Version 1.2.0
+
+ Optimized floating-point operations on ARM.
+
+ Added a number of extensions to the debugger API.
+
+ Changed the enumeration order for unsigned integer keys to always
+ be numerical order.
+
+ Added a "read" extension to the shell sample.
+
+ Added support for Array.prototype.reduce and
+ Array.prototype.reduceRight.
+
+ Added an option to the SCons build to control Microsoft Visual C++
+ link-time code generation.
+
+ Fixed a number of bugs (in particular issue 315, issue 316,
+ issue 317 and issue 318).
+
+
2009-04-15: Version 1.1.10
Fixed crash bug that occurred when loading a const variable in the
@@ -27,13 +48,13 @@
Changed test-debug/ThreadedDebugging to be non-flaky (issue 96).
- Fixed step-in handling for Function.prototype.apply and call in
+ Fixed step-in handling for Function.prototype.apply and call in
the debugger (issue 269).
Fixed v8::Object::DeleteHiddenValue to not bail out when there
are no hidden properties.
- Added workaround for crash bug, where external symbol table
+ Added workaround for crash bug, where external symbol table
entries with deleted resources would lead to NPEs when looking
up in the symbol table.
diff --git a/SConstruct b/SConstruct
index 01083cc..0c8e3c0 100644
--- a/SConstruct
+++ b/SConstruct
@@ -94,6 +94,7 @@
'CCFLAGS': ['-g', '-O0'],
'CPPDEFINES': ['ENABLE_DISASSEMBLER', 'DEBUG'],
'os:android': {
+ 'CPPDEFINES': ['ENABLE_DEBUGGER_SUPPORT'],
'CCFLAGS': ['-mthumb']
}
},
@@ -102,13 +103,13 @@
'-ffunction-sections'],
'os:android': {
'CCFLAGS': ['-mthumb', '-Os'],
- 'CPPDEFINES': ['SK_RELEASE', 'NDEBUG']
+ 'CPPDEFINES': ['SK_RELEASE', 'NDEBUG', 'ENABLE_DEBUGGER_SUPPORT']
}
},
'os:linux': {
'CCFLAGS': ['-ansi'],
'library:shared': {
- 'LIBS': ['pthread', 'rt']
+ 'LIBS': ['pthread']
}
},
'os:macos': {
@@ -160,14 +161,18 @@
}
},
'mode:release': {
- 'CCFLAGS': ['/O2', '/GL'],
- 'LINKFLAGS': ['/OPT:REF', '/OPT:ICF', '/LTCG'],
- 'ARFLAGS': ['/LTCG'],
+ 'CCFLAGS': ['/O2'],
+ 'LINKFLAGS': ['/OPT:REF', '/OPT:ICF'],
'msvcrt:static': {
'CCFLAGS': ['/MT']
},
'msvcrt:shared': {
'CCFLAGS': ['/MD']
+ },
+ 'msvcltcg:on': {
+ 'CCFLAGS': ['/GL'],
+ 'LINKFLAGS': ['/LTCG'],
+ 'ARFLAGS': ['/LTCG'],
}
},
}
@@ -224,7 +229,7 @@
MKSNAPSHOT_EXTRA_FLAGS = {
'gcc': {
'os:linux': {
- 'LIBS': ['pthread', 'rt'],
+ 'LIBS': ['pthread'],
},
'os:macos': {
'LIBS': ['pthread'],
@@ -238,6 +243,7 @@
},
'msvc': {
'all': {
+ 'CPPDEFINES': ['_HAS_EXCEPTIONS=0'],
'LIBS': ['winmm', 'ws2_32']
}
}
@@ -268,7 +274,7 @@
'LIBPATH': [abspath('.')]
},
'os:linux': {
- 'LIBS': ['pthread', 'rt'],
+ 'LIBS': ['pthread'],
},
'os:macos': {
'LIBS': ['pthread'],
@@ -307,7 +313,7 @@
'CCFLAGS': ['-fno-rtti', '-fno-exceptions']
},
'os:linux': {
- 'LIBS': ['pthread', 'rt'],
+ 'LIBS': ['pthread'],
},
'os:macos': {
'LIBS': ['pthread'],
@@ -359,12 +365,16 @@
},
'mode:release': {
'CCFLAGS': ['/O2'],
- 'LINKFLAGS': ['/OPT:REF', '/OPT:ICF', '/LTCG'],
+ 'LINKFLAGS': ['/OPT:REF', '/OPT:ICF'],
'msvcrt:static': {
'CCFLAGS': ['/MT']
},
'msvcrt:shared': {
'CCFLAGS': ['/MD']
+ },
+ 'msvcltcg:on': {
+ 'CCFLAGS': ['/GL'],
+ 'LINKFLAGS': ['/LTCG'],
}
},
'mode:debug': {
@@ -387,7 +397,7 @@
'LIBS': ['readline']
},
'os:linux': {
- 'LIBS': ['pthread', 'rt'],
+ 'LIBS': ['pthread'],
},
'os:macos': {
'LIBS': ['pthread'],
@@ -473,7 +483,12 @@
'msvcrt': {
'values': ['static', 'shared'],
'default': 'static',
- 'help': 'the type of MSVCRT library to use'
+ 'help': 'the type of Microsoft Visual C++ runtime library to use'
+ },
+ 'msvcltcg': {
+ 'values': ['on', 'off'],
+ 'default': 'on',
+ 'help': 'use Microsoft Visual C++ link-time code generation'
},
'wordsize': {
'values': ['64', '32'],
diff --git a/include/v8-debug.h b/include/v8-debug.h
index 4c639c1..debeac1 100644
--- a/include/v8-debug.h
+++ b/include/v8-debug.h
@@ -79,48 +79,56 @@
};
-/**
- * Debug event callback function.
- *
- * \param event the type of the debug event that triggered the callback
- * (enum DebugEvent)
- * \param exec_state execution state (JavaScript object)
- * \param event_data event specific data (JavaScript object)
- * \param data value passed by the user to SetDebugEventListener
- */
-typedef void (*DebugEventCallback)(DebugEvent event,
- Handle<Object> exec_state,
- Handle<Object> event_data,
- Handle<Value> data);
-
-
-/**
- * Debug message callback function.
- *
- * \param message the debug message
- * \param length length of the message
- * \param data the data value passed when registering the message handler
- * A DebugMessageHandler does not take posession of the message string,
- * and must not rely on the data persisting after the handler returns.
- */
-typedef void (*DebugMessageHandler)(const uint16_t* message, int length,
- void* data);
-
-/**
- * Debug host dispatch callback function.
- *
- * \param dispatch the dispatch value
- * \param data the data value passed when registering the dispatch handler
- */
-typedef void (*DebugHostDispatchHandler)(void* dispatch,
- void* data);
-
-
-
class EXPORT Debug {
public:
+ /**
+ * A client object passed to the v8 debugger whose ownership will be taken by
+ * it. v8 is always responsible for deleting the object.
+ */
+ class ClientData {
+ public:
+ virtual ~ClientData() {}
+ };
+
+
+ /**
+ * Debug event callback function.
+ *
+ * \param event the type of the debug event that triggered the callback
+ * (enum DebugEvent)
+ * \param exec_state execution state (JavaScript object)
+ * \param event_data event specific data (JavaScript object)
+ * \param data value passed by the user to SetDebugEventListener
+ */
+ typedef void (*EventCallback)(DebugEvent event,
+ Handle<Object> exec_state,
+ Handle<Object> event_data,
+ Handle<Value> data);
+
+
+ /**
+ * Debug message callback function.
+ *
+ * \param message the debug message
+ * \param length length of the message
+ * \param data the data value passed when registering the message handler
+ * \param client_data the data value passed into Debug::SendCommand along
+ * with the request that led to the message or NULL if the message is an
+ * asynchronous event. The debugger takes ownership of the data and will
+ * delete it before dying even if there is no message handler.
+ * A MessageHandler does not take posession of the message string,
+ * and must not rely on the data persisting after the handler returns.
+ */
+ typedef void (*MessageHandler)(const uint16_t* message, int length,
+ ClientData* client_data);
+
+ /**
+ * Debug host dispatch callback function.
+ */
+ typedef void (*HostDispatchHandler)();
+
// Set a C debug event listener.
- static bool SetDebugEventListener(DebugEventCallback that,
+ static bool SetDebugEventListener(EventCallback that,
Handle<Value> data = Handle<Value>());
// Set a JavaScript debug event listener.
@@ -131,14 +139,14 @@
static void DebugBreak();
// Message based interface. The message protocol is JSON.
- static void SetMessageHandler(DebugMessageHandler handler, void* data = NULL,
+ static void SetMessageHandler(MessageHandler handler,
bool message_handler_thread = true);
- static void SendCommand(const uint16_t* command, int length);
+ static void SendCommand(const uint16_t* command, int length,
+ ClientData* client_data = NULL);
// Dispatch interface.
- static void SetHostDispatchHandler(DebugHostDispatchHandler handler,
- void* data = NULL);
- static void SendHostDispatch(void* dispatch);
+ static void SetHostDispatchHandler(HostDispatchHandler handler,
+ int period = 100);
/**
* Run a JavaScript function in the debugger.
diff --git a/include/v8.h b/include/v8.h
index 1ddaee0..1013cab 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -529,6 +529,13 @@
* Returns the script id value.
*/
Local<Value> Id();
+
+ /**
+ * Associate an additional data object with the script. This is mainly used
+ * with the debugger as this data object is only available through the
+ * debugger API.
+ */
+ void SetData(Handle<Value> data);
};
@@ -540,9 +547,19 @@
Local<String> Get() const;
Local<String> GetSourceLine() const;
+ /**
+ * Returns the resource name for the script from where the function causing
+ * the error originates.
+ */
Handle<Value> GetScriptResourceName() const;
/**
+ * Returns the resource data for the script from where the function causing
+ * the error originates.
+ */
+ Handle<Value> GetScriptData() const;
+
+ /**
* Returns the number, 1-based, of the line where the error occurred.
*/
int GetLineNumber() const;
@@ -1028,6 +1045,18 @@
bool Set(Handle<Value> key,
Handle<Value> value,
PropertyAttribute attribs = None);
+
+ // Sets a local property on this object, bypassing interceptors and
+ // overriding accessors or read-only properties.
+ //
+ // Note that if the object has an interceptor the property will be set
+ // locally, but since the interceptor takes precedence the local property
+ // will only be returned if the interceptor doesn't return a value.
+ //
+ // Note also that this only works for named properties.
+ bool ForceSet(Handle<Value> key,
+ Handle<Value> value,
+ PropertyAttribute attribs = None);
Local<Value> Get(Handle<Value> key);
// TODO(1245389): Replace the type-specific versions of these
diff --git a/samples/shell.cc b/samples/shell.cc
index 087d4fd..27ed293 100644
--- a/samples/shell.cc
+++ b/samples/shell.cc
@@ -38,6 +38,7 @@
bool print_result,
bool report_exceptions);
v8::Handle<v8::Value> Print(const v8::Arguments& args);
+v8::Handle<v8::Value> Read(const v8::Arguments& args);
v8::Handle<v8::Value> Load(const v8::Arguments& args);
v8::Handle<v8::Value> Quit(const v8::Arguments& args);
v8::Handle<v8::Value> Version(const v8::Arguments& args);
@@ -52,6 +53,8 @@
v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New();
// Bind the global 'print' function to the C++ Print callback.
global->Set(v8::String::New("print"), v8::FunctionTemplate::New(Print));
+ // Bind the global 'read' function to the C++ Read callback.
+ global->Set(v8::String::New("read"), v8::FunctionTemplate::New(Read));
// Bind the global 'load' function to the C++ Load callback.
global->Set(v8::String::New("load"), v8::FunctionTemplate::New(Load));
// Bind the 'quit' function
@@ -135,6 +138,25 @@
}
+// The callback that is invoked by v8 whenever the JavaScript 'read'
+// function is called. This function loads the content of the file named in
+// the argument into a JavaScript string.
+v8::Handle<v8::Value> Read(const v8::Arguments& args) {
+ if (args.Length() != 1) {
+ return v8::ThrowException(v8::String::New("Bad parameters"));
+ }
+ v8::String::Utf8Value file(args[0]);
+ if (*file == NULL) {
+ return v8::ThrowException(v8::String::New("Error loading file"));
+ }
+ v8::Handle<v8::String> source = ReadFile(*file);
+ if (source.IsEmpty()) {
+ return v8::ThrowException(v8::String::New("Error loading file"));
+ }
+ return source;
+}
+
+
// The callback that is invoked by v8 whenever the JavaScript 'load'
// function is called. Loads, compiles and executes its argument
// JavaScript file.
diff --git a/src/accessors.cc b/src/accessors.cc
index d779eb2..2d6a3a1 100644
--- a/src/accessors.cc
+++ b/src/accessors.cc
@@ -252,6 +252,24 @@
//
+// Accessors::ScriptData
+//
+
+
+Object* Accessors::ScriptGetData(Object* object, void*) {
+ Object* script = JSValue::cast(object)->value();
+ return Script::cast(script)->data();
+}
+
+
+const AccessorDescriptor Accessors::ScriptData = {
+ ScriptGetData,
+ IllegalSetter,
+ 0
+};
+
+
+//
// Accessors::ScriptType
//
diff --git a/src/accessors.h b/src/accessors.h
index 938b014..d174c90 100644
--- a/src/accessors.h
+++ b/src/accessors.h
@@ -45,6 +45,7 @@
V(ScriptId) \
V(ScriptLineOffset) \
V(ScriptColumnOffset) \
+ V(ScriptData) \
V(ScriptType) \
V(ScriptLineEnds) \
V(ObjectPrototype)
@@ -84,6 +85,7 @@
static Object* ScriptGetSource(Object* object, void*);
static Object* ScriptGetLineOffset(Object* object, void*);
static Object* ScriptGetColumnOffset(Object* object, void*);
+ static Object* ScriptGetData(Object* object, void*);
static Object* ScriptGetType(Object* object, void*);
static Object* ScriptGetLineEnds(Object* object, void*);
static Object* ObjectGetPrototype(Object* receiver, void*);
diff --git a/src/api.cc b/src/api.cc
index a190b9c..449926c 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -1108,6 +1108,19 @@
}
+void Script::SetData(v8::Handle<Value> data) {
+ ON_BAILOUT("v8::Script::SetData()", return);
+ LOG_API("Script::SetData");
+ {
+ HandleScope scope;
+ i::Handle<i::JSFunction> fun = Utils::OpenHandle(this);
+ i::Handle<i::Object> raw_data = Utils::OpenHandle(*data);
+ i::Handle<i::Script> script(i::Script::cast(fun->shared()->script()));
+ script->set_data(*raw_data);
+ }
+}
+
+
// --- E x c e p t i o n s ---
@@ -1199,6 +1212,22 @@
}
+v8::Handle<Value> Message::GetScriptData() const {
+ if (IsDeadCheck("v8::Message::GetScriptResourceData()")) {
+ return Local<Value>();
+ }
+ ENTER_V8;
+ HandleScope scope;
+ i::Handle<i::JSObject> obj =
+ i::Handle<i::JSObject>::cast(Utils::OpenHandle(this));
+ // Return this.script.data.
+ i::Handle<i::JSValue> script =
+ i::Handle<i::JSValue>::cast(GetProperty(obj, "script"));
+ i::Handle<i::Object> data(i::Script::cast(script->value())->data());
+ return scope.Close(Utils::ToLocal(data));
+}
+
+
static i::Handle<i::Object> CallV8HeapFunction(const char* name,
i::Handle<i::Object> recv,
int argc,
@@ -1806,6 +1835,26 @@
}
+bool v8::Object::ForceSet(v8::Handle<Value> key,
+ v8::Handle<Value> value,
+ v8::PropertyAttribute attribs) {
+ ON_BAILOUT("v8::Object::ForceSet()", return false);
+ ENTER_V8;
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> key_obj = Utils::OpenHandle(*key);
+ i::Handle<i::Object> value_obj = Utils::OpenHandle(*value);
+ EXCEPTION_PREAMBLE();
+ i::Handle<i::Object> obj = i::ForceSetProperty(
+ self,
+ key_obj,
+ value_obj,
+ static_cast<PropertyAttributes>(attribs));
+ has_pending_exception = obj.is_null();
+ EXCEPTION_BAILOUT_CHECK(false);
+ return true;
+}
+
+
Local<Value> v8::Object::Get(v8::Handle<Value> key) {
ON_BAILOUT("v8::Object::Get()", return Local<v8::Value>());
ENTER_V8;
@@ -2373,7 +2422,7 @@
const char* v8::V8::GetVersion() {
- return "1.1.10.4";
+ return "1.2.0";
}
@@ -3180,8 +3229,8 @@
// --- D e b u g S u p p o r t ---
-
-bool Debug::SetDebugEventListener(DebugEventCallback that, Handle<Value> data) {
+#ifdef ENABLE_DEBUGGER_SUPPORT
+bool Debug::SetDebugEventListener(EventCallback that, Handle<Value> data) {
EnsureInitialized("v8::Debug::SetDebugEventListener()");
ON_BAILOUT("v8::Debug::SetDebugEventListener()", return false);
ENTER_V8;
@@ -3211,31 +3260,27 @@
}
-void Debug::SetMessageHandler(v8::DebugMessageHandler handler, void* data,
+void Debug::SetMessageHandler(v8::Debug::MessageHandler handler,
bool message_handler_thread) {
EnsureInitialized("v8::Debug::SetMessageHandler");
ENTER_V8;
- i::Debugger::SetMessageHandler(handler, data, message_handler_thread);
+ i::Debugger::SetMessageHandler(handler, message_handler_thread);
}
-void Debug::SendCommand(const uint16_t* command, int length) {
+void Debug::SendCommand(const uint16_t* command, int length,
+ ClientData* client_data) {
if (!i::V8::HasBeenSetup()) return;
- i::Debugger::ProcessCommand(i::Vector<const uint16_t>(command, length));
+ i::Debugger::ProcessCommand(i::Vector<const uint16_t>(command, length),
+ client_data);
}
-void Debug::SetHostDispatchHandler(v8::DebugHostDispatchHandler handler,
- void* data) {
+void Debug::SetHostDispatchHandler(HostDispatchHandler handler,
+ int period) {
EnsureInitialized("v8::Debug::SetHostDispatchHandler");
ENTER_V8;
- i::Debugger::SetHostDispatchHandler(handler, data);
-}
-
-
-void Debug::SendHostDispatch(void* dispatch) {
- if (!i::V8::HasBeenSetup()) return;
- i::Debugger::ProcessHostDispatch(dispatch);
+ i::Debugger::SetHostDispatchHandler(handler, period);
}
@@ -3263,7 +3308,7 @@
bool Debug::EnableAgent(const char* name, int port) {
return i::Debugger::StartAgent(name, port);
}
-
+#endif // ENABLE_DEBUGGER_SUPPORT
namespace internal {
diff --git a/src/array.js b/src/array.js
index d30a989..0adbf9d 100644
--- a/src/array.js
+++ b/src/array.js
@@ -879,6 +879,62 @@
}
+function ArrayReduce(callback, current) {
+ if (!IS_FUNCTION(callback)) {
+ throw MakeTypeError('called_non_callable', [callback]);
+ }
+ // Pull out the length so that modifications to the length in the
+ // loop will not affect the looping.
+ var length = this.length;
+ var i = 0;
+
+ find_initial: if (%_ArgumentsLength() < 2) {
+ for (; i < length; i++) {
+ current = this[i];
+ if (!IS_UNDEFINED(current) || i in this) {
+ i++;
+ break find_initial;
+ }
+ }
+ throw MakeTypeError('reduce_no_initial', []);
+ }
+
+ for (; i < length; i++) {
+ var element = this[i];
+ if (!IS_UNDEFINED(element) || i in this) {
+ current = callback.call(null, current, element, i, this);
+ }
+ }
+ return current;
+}
+
+function ArrayReduceRight(callback, current) {
+ if (!IS_FUNCTION(callback)) {
+ throw MakeTypeError('called_non_callable', [callback]);
+ }
+ var i = this.length - 1;
+
+ find_initial: if (%_ArgumentsLength() < 2) {
+ for (; i >= 0; i--) {
+ current = this[i];
+ if (!IS_UNDEFINED(current) || i in this) {
+ i--;
+ break find_initial;
+ }
+ }
+ throw MakeTypeError('reduce_no_initial', []);
+ }
+
+ for (; i >= 0; i--) {
+ var element = this[i];
+ if (!IS_UNDEFINED(element) || i in this) {
+ current = callback.call(null, current, element, i, this);
+ }
+ }
+ return current;
+}
+
+
// -------------------------------------------------------------------
@@ -917,7 +973,9 @@
"every", ArrayEvery,
"map", ArrayMap,
"indexOf", ArrayIndexOf,
- "lastIndexOf", ArrayLastIndexOf
+ "lastIndexOf", ArrayLastIndexOf,
+ "reduce", ArrayReduce,
+ "reduceRight", ArrayReduceRight
));
// Manipulate the length of some of the functions to meet
@@ -930,7 +988,9 @@
ArrayMap: 1,
ArrayIndexOf: 1,
ArrayLastIndexOf: 1,
- ArrayPush: 1
+ ArrayPush: 1,
+ ArrayReduce: 1,
+ ArrayReduceRight: 1
});
}
diff --git a/src/assembler-ia32.cc b/src/assembler-ia32.cc
index 8549261..3a2d3f8 100644
--- a/src/assembler-ia32.cc
+++ b/src/assembler-ia32.cc
@@ -283,6 +283,10 @@
*pc_++ = (x)
+#ifdef GENERATED_CODE_COVERAGE
+static void InitCoverageLog();
+#endif
+
// spare_buffer_
static byte* spare_buffer_ = NULL;
@@ -315,9 +319,11 @@
// Clear the buffer in debug mode unless it was provided by the
// caller in which case we can't be sure it's okay to overwrite
// existing code in it; see CodePatcher::CodePatcher(...).
- if (kDebug && own_buffer_) {
+#ifdef DEBUG
+ if (own_buffer_) {
memset(buffer_, 0xCC, buffer_size); // int3
}
+#endif
// setup buffer pointers
ASSERT(buffer_ != NULL);
@@ -329,6 +335,9 @@
current_position_ = RelocInfo::kNoPosition;
written_statement_position_ = current_statement_position_;
written_position_ = current_position_;
+#ifdef GENERATED_CODE_COVERAGE
+ InitCoverageLog();
+#endif
}
@@ -2073,9 +2082,9 @@
// Clear the buffer in debug mode. Use 'int3' instructions to make
// sure to get into problems if we ever run uninitialized code.
- if (kDebug) {
- memset(desc.buffer, 0xCC, desc.buffer_size);
- }
+#ifdef DEBUG
+ memset(desc.buffer, 0xCC, desc.buffer_size);
+#endif
// copy the data
int pc_delta = desc.buffer - buffer_;
@@ -2202,4 +2211,30 @@
long_at_put(position, label_loc);
}
+
+#ifdef GENERATED_CODE_COVERAGE
+static FILE* coverage_log = NULL;
+
+
+static void InitCoverageLog() {
+ char* file_name = getenv("V8_GENERATED_CODE_COVERAGE_LOG");
+ if (file_name != NULL) {
+ coverage_log = fopen(file_name, "aw+");
+ }
+}
+
+
+void LogGeneratedCodeCoverage(const char* file_line) {
+ const char* return_address = (&file_line)[-1];
+ char* push_insn = const_cast<char*>(return_address - 12);
+ push_insn[0] = 0xeb; // Relative branch insn.
+ push_insn[1] = 13; // Skip over coverage insns.
+ if (coverage_log != NULL) {
+ fprintf(coverage_log, "%s\n", file_line);
+ fflush(coverage_log);
+ }
+}
+
+#endif
+
} } // namespace v8::internal
diff --git a/src/assembler.cc b/src/assembler.cc
index ad4b24c..ec0e4fd 100644
--- a/src/assembler.cc
+++ b/src/assembler.cc
@@ -521,10 +521,10 @@
ExternalReference::ExternalReference(const IC_Utility& ic_utility)
: address_(ic_utility.address()) {}
-
+#ifdef ENABLE_DEBUGGER_SUPPORT
ExternalReference::ExternalReference(const Debug_Address& debug_address)
: address_(debug_address.address()) {}
-
+#endif
ExternalReference::ExternalReference(StatsCounter* counter)
: address_(reinterpret_cast<Address>(counter->GetInternalPointer())) {}
@@ -557,29 +557,71 @@
}
-ExternalReference ExternalReference::debug_break() {
- return ExternalReference(FUNCTION_ADDR(Debug::Break));
-}
-
-
ExternalReference ExternalReference::new_space_start() {
return ExternalReference(Heap::NewSpaceStart());
}
+
ExternalReference ExternalReference::new_space_allocation_top_address() {
return ExternalReference(Heap::NewSpaceAllocationTopAddress());
}
+
ExternalReference ExternalReference::heap_always_allocate_scope_depth() {
return ExternalReference(Heap::always_allocate_scope_depth_address());
}
+
ExternalReference ExternalReference::new_space_allocation_limit_address() {
return ExternalReference(Heap::NewSpaceAllocationLimitAddress());
}
+
+static double add_two_doubles(double x, double y) {
+ return x + y;
+}
+
+
+static double sub_two_doubles(double x, double y) {
+ return x - y;
+}
+
+
+static double mul_two_doubles(double x, double y) {
+ return x * y;
+}
+
+
+ExternalReference ExternalReference::double_fp_operation(
+ Token::Value operation) {
+ typedef double BinaryFPOperation(double x, double y);
+ BinaryFPOperation* function = NULL;
+ switch (operation) {
+ case Token::ADD:
+ function = &add_two_doubles;
+ break;
+ case Token::SUB:
+ function = &sub_two_doubles;
+ break;
+ case Token::MUL:
+ function = &mul_two_doubles;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ return ExternalReference(FUNCTION_ADDR(function));
+}
+
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ExternalReference ExternalReference::debug_break() {
+ return ExternalReference(FUNCTION_ADDR(Debug::Break));
+}
+
+
ExternalReference ExternalReference::debug_step_in_fp_address() {
return ExternalReference(Debug::step_in_fp_addr());
}
+#endif
} } // namespace v8::internal
diff --git a/src/assembler.h b/src/assembler.h
index 49c9b90..8abdbc7 100644
--- a/src/assembler.h
+++ b/src/assembler.h
@@ -38,6 +38,7 @@
#include "runtime.h"
#include "top.h"
#include "zone-inl.h"
+#include "token.h"
namespace v8 { namespace internal {
@@ -340,29 +341,15 @@
};
-// A stack-allocated code region logs a name for the code generated
-// while the region is in effect. This information is used by the
-// profiler to categorize ticks within generated code.
-class CodeRegion BASE_EMBEDDED {
- public:
- inline CodeRegion(Assembler* assm, const char *name) : assm_(assm) {
- LOG(BeginCodeRegionEvent(this, assm, name));
- }
- inline ~CodeRegion() {
- LOG(EndCodeRegionEvent(this, assm_));
- }
- private:
- Assembler* assm_;
-};
-
-
//------------------------------------------------------------------------------
// External function
//----------------------------------------------------------------------------
class IC_Utility;
-class Debug_Address;
class SCTableReference;
+#ifdef ENABLE_DEBUGGER_SUPPORT
+class Debug_Address;
+#endif
// An ExternalReference represents a C++ address called from the generated
// code. All references to C++ functions and must be encapsulated in an
@@ -380,7 +367,9 @@
explicit ExternalReference(const IC_Utility& ic_utility);
+#ifdef ENABLE_DEBUGGER_SUPPORT
explicit ExternalReference(const Debug_Address& debug_address);
+#endif
explicit ExternalReference(StatsCounter* counter);
@@ -403,9 +392,6 @@
// Static variable RegExpStack::limit_address()
static ExternalReference address_of_regexp_stack_limit();
- // Function Debug::Break()
- static ExternalReference debug_break();
-
// Static variable Heap::NewSpaceStart()
static ExternalReference new_space_start();
static ExternalReference heap_always_allocate_scope_depth();
@@ -414,11 +400,18 @@
static ExternalReference new_space_allocation_top_address();
static ExternalReference new_space_allocation_limit_address();
- // Used to check if single stepping is enabled in generated code.
- static ExternalReference debug_step_in_fp_address();
+ static ExternalReference double_fp_operation(Token::Value operation);
Address address() const {return address_;}
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ // Function Debug::Break()
+ static ExternalReference debug_break();
+
+ // Used to check if single stepping is enabled in generated code.
+ static ExternalReference debug_step_in_fp_address();
+#endif
+
private:
explicit ExternalReference(void* address)
: address_(reinterpret_cast<Address>(address)) {}
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index 0a0ed83..eebec74 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -832,12 +832,16 @@
bool Genesis::CompileNative(Vector<const char> name, Handle<String> source) {
HandleScope scope;
+#ifdef ENABLE_DEBUGGER_SUPPORT
Debugger::set_compiling_natives(true);
+#endif
bool result =
CompileScriptCached(name, source, &natives_cache, NULL, true);
ASSERT(Top::has_pending_exception() != result);
if (!result) Top::clear_pending_exception();
+#ifdef ENABLE_DEBUGGER_SUPPORT
Debugger::set_compiling_natives(false);
+#endif
return result;
}
@@ -1015,6 +1019,13 @@
Factory::LookupAsciiSymbol("column_offset"),
proxy_column_offset,
common_attributes);
+ Handle<Proxy> proxy_data = Factory::NewProxy(&Accessors::ScriptData);
+ script_descriptors =
+ Factory::CopyAppendProxyDescriptor(
+ script_descriptors,
+ Factory::LookupAsciiSymbol("data"),
+ proxy_data,
+ common_attributes);
Handle<Proxy> proxy_type = Factory::NewProxy(&Accessors::ScriptType);
script_descriptors =
Factory::CopyAppendProxyDescriptor(
@@ -1132,6 +1143,7 @@
Handle<JSObject>(js_global->builtins()), DONT_ENUM);
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Expose the debug global object in global if a name for it is specified.
if (FLAG_expose_debug_as != NULL && strlen(FLAG_expose_debug_as) != 0) {
// If loading fails we just bail out without installing the
@@ -1149,6 +1161,7 @@
SetProperty(js_global, debug_string,
Handle<Object>(Debug::debug_context()->global_proxy()), DONT_ENUM);
}
+#endif
return true;
}
diff --git a/src/builtins-arm.cc b/src/builtins-arm.cc
index 2e6f255..58eeca8 100644
--- a/src/builtins-arm.cc
+++ b/src/builtins-arm.cc
@@ -34,7 +34,7 @@
namespace v8 { namespace internal {
-#define __ masm->
+#define __ ACCESS_MASM(masm)
void Builtins::Generate_Adaptor(MacroAssembler* masm, CFunctionId id) {
@@ -218,8 +218,9 @@
__ mov(r5, Operand(r4));
__ mov(r6, Operand(r4));
__ mov(r7, Operand(r4));
- if (kR9Available == 1)
+ if (kR9Available == 1) {
__ mov(r9, Operand(r4));
+ }
// Invoke the code and pass argc as r0.
__ mov(r0, Operand(r3));
diff --git a/src/builtins-ia32.cc b/src/builtins-ia32.cc
index 0e9de8c..9baeb5e 100644
--- a/src/builtins-ia32.cc
+++ b/src/builtins-ia32.cc
@@ -32,7 +32,7 @@
namespace v8 { namespace internal {
-#define __ masm->
+#define __ ACCESS_MASM(masm)
void Builtins::Generate_Adaptor(MacroAssembler* masm, CFunctionId id) {
@@ -69,10 +69,12 @@
Label rt_call, allocated;
if (FLAG_inline_new) {
Label undo_allocation;
+#ifdef ENABLE_DEBUGGER_SUPPORT
ExternalReference debug_step_in_fp =
ExternalReference::debug_step_in_fp_address();
__ cmp(Operand::StaticVariable(debug_step_in_fp), Immediate(0));
__ j(not_equal, &rt_call);
+#endif
// Check that function is not a Smi.
__ test(edi, Immediate(kSmiTagMask));
__ j(zero, &rt_call);
diff --git a/src/builtins.cc b/src/builtins.cc
index c3935f1..b27974f 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -559,6 +559,7 @@
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
static void Generate_LoadIC_DebugBreak(MacroAssembler* masm) {
Debug::GenerateLoadICDebugBreak(masm);
}
@@ -597,7 +598,7 @@
static void Generate_StubNoRegisters_DebugBreak(MacroAssembler* masm) {
Debug::GenerateStubNoRegistersDebugBreak(masm);
}
-
+#endif
Object* Builtins::builtins_[builtin_count] = { NULL, };
const char* Builtins::names_[builtin_count] = { NULL, };
diff --git a/src/builtins.h b/src/builtins.h
index 853c90e..4e74a3c 100644
--- a/src/builtins.h
+++ b/src/builtins.h
@@ -83,6 +83,7 @@
V(FunctionApply, BUILTIN, UNINITIALIZED)
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Define list of builtins used by the debugger implemented in assembly.
#define BUILTIN_LIST_DEBUG_A(V) \
V(Return_DebugBreak, BUILTIN, DEBUG_BREAK) \
@@ -93,7 +94,9 @@
V(KeyedLoadIC_DebugBreak, KEYED_LOAD_IC, DEBUG_BREAK) \
V(StoreIC_DebugBreak, STORE_IC, DEBUG_BREAK) \
V(KeyedStoreIC_DebugBreak, KEYED_STORE_IC, DEBUG_BREAK)
-
+#else
+#define BUILTIN_LIST_DEBUG_A(V)
+#endif
// Define list of builtins implemented in JavaScript.
#define BUILTINS_LIST_JS(V) \
diff --git a/src/codegen-arm.cc b/src/codegen-arm.cc
index 6fdabc3..9337454 100644
--- a/src/codegen-arm.cc
+++ b/src/codegen-arm.cc
@@ -38,7 +38,8 @@
namespace v8 { namespace internal {
-#define __ masm_->
+#define __ ACCESS_MASM(masm_)
+
// -------------------------------------------------------------------------
// CodeGenState implementation.
@@ -146,13 +147,13 @@
frame_->EmitPush(r0);
frame_->CallRuntime(Runtime::kNewContext, 1); // r0 holds the result
- if (kDebug) {
- JumpTarget verified_true(this);
- __ cmp(r0, Operand(cp));
- verified_true.Branch(eq);
- __ stop("NewContext: r0 is expected to be the same as cp");
- verified_true.Bind();
- }
+#ifdef DEBUG
+ JumpTarget verified_true(this);
+ __ cmp(r0, Operand(cp));
+ verified_true.Branch(eq);
+ __ stop("NewContext: r0 is expected to be the same as cp");
+ verified_true.Bind();
+#endif
// Update context local.
__ str(cp, frame_->Context());
}
@@ -653,37 +654,27 @@
}
-class GetPropertyStub : public CodeStub {
- public:
- GetPropertyStub() { }
-
- private:
- Major MajorKey() { return GetProperty; }
- int MinorKey() { return 0; }
- void Generate(MacroAssembler* masm);
-};
-
-
-class SetPropertyStub : public CodeStub {
- public:
- SetPropertyStub() { }
-
- private:
- Major MajorKey() { return SetProperty; }
- int MinorKey() { return 0; }
- void Generate(MacroAssembler* masm);
-};
-
-
class GenericBinaryOpStub : public CodeStub {
public:
- explicit GenericBinaryOpStub(Token::Value op) : op_(op) { }
+ GenericBinaryOpStub(Token::Value op,
+ OverwriteMode mode)
+ : op_(op), mode_(mode) { }
private:
Token::Value op_;
+ OverwriteMode mode_;
+
+ // Minor key encoding in 16 bits.
+ class ModeBits: public BitField<OverwriteMode, 0, 2> {};
+ class OpBits: public BitField<Token::Value, 2, 14> {};
Major MajorKey() { return GenericBinaryOp; }
- int MinorKey() { return static_cast<int>(op_); }
+ int MinorKey() {
+ // Encode the parameters in a unique 16 bit value.
+ return OpBits::encode(op_)
+ | ModeBits::encode(mode_);
+ }
+
void Generate(MacroAssembler* masm);
const char* GetName() {
@@ -708,7 +699,8 @@
};
-void CodeGenerator::GenericBinaryOperation(Token::Value op) {
+void CodeGenerator::GenericBinaryOperation(Token::Value op,
+ OverwriteMode overwrite_mode) {
VirtualFrame::SpilledScope spilled_scope(this);
// sp[0] : y
// sp[1] : x
@@ -727,7 +719,7 @@
case Token::SAR: {
frame_->EmitPop(r0); // r0 : y
frame_->EmitPop(r1); // r1 : x
- GenericBinaryOpStub stub(op);
+ GenericBinaryOpStub stub(op, overwrite_mode);
frame_->CallStub(&stub, 0);
break;
}
@@ -767,11 +759,13 @@
DeferredInlineSmiOperation(CodeGenerator* generator,
Token::Value op,
int value,
- bool reversed)
+ bool reversed,
+ OverwriteMode overwrite_mode)
: DeferredCode(generator),
op_(op),
value_(value),
- reversed_(reversed) {
+ reversed_(reversed),
+ overwrite_mode_(overwrite_mode) {
set_comment("[ DeferredInlinedSmiOperation");
}
@@ -781,6 +775,7 @@
Token::Value op_;
int value_;
bool reversed_;
+ OverwriteMode overwrite_mode_;
};
@@ -844,7 +839,7 @@
break;
}
- GenericBinaryOpStub igostub(op_);
+ GenericBinaryOpStub igostub(op_, overwrite_mode_);
Result arg0 = generator()->allocator()->Allocate(r1);
ASSERT(arg0.is_valid());
Result arg1 = generator()->allocator()->Allocate(r0);
@@ -856,7 +851,8 @@
void CodeGenerator::SmiOperation(Token::Value op,
Handle<Object> value,
- bool reversed) {
+ bool reversed,
+ OverwriteMode mode) {
VirtualFrame::SpilledScope spilled_scope(this);
// NOTE: This is an attempt to inline (a bit) more of the code for
// some possible smi operations (like + and -) when (at least) one
@@ -875,7 +871,7 @@
switch (op) {
case Token::ADD: {
DeferredCode* deferred =
- new DeferredInlineSmiOperation(this, op, int_value, reversed);
+ new DeferredInlineSmiOperation(this, op, int_value, reversed, mode);
__ add(r0, r0, Operand(value), SetCC);
deferred->enter()->Branch(vs);
@@ -887,7 +883,7 @@
case Token::SUB: {
DeferredCode* deferred =
- new DeferredInlineSmiOperation(this, op, int_value, reversed);
+ new DeferredInlineSmiOperation(this, op, int_value, reversed, mode);
if (!reversed) {
__ sub(r0, r0, Operand(value), SetCC);
@@ -905,7 +901,7 @@
case Token::BIT_XOR:
case Token::BIT_AND: {
DeferredCode* deferred =
- new DeferredInlineSmiOperation(this, op, int_value, reversed);
+ new DeferredInlineSmiOperation(this, op, int_value, reversed, mode);
__ tst(r0, Operand(kSmiTagMask));
deferred->enter()->Branch(ne);
switch (op) {
@@ -925,12 +921,12 @@
__ mov(ip, Operand(value));
frame_->EmitPush(ip);
frame_->EmitPush(r0);
- GenericBinaryOperation(op);
+ GenericBinaryOperation(op, mode);
} else {
int shift_value = int_value & 0x1f; // least significant 5 bits
DeferredCode* deferred =
- new DeferredInlineSmiOperation(this, op, shift_value, false);
+ new DeferredInlineSmiOperation(this, op, shift_value, false, mode);
__ tst(r0, Operand(kSmiTagMask));
deferred->enter()->Branch(ne);
__ mov(r2, Operand(r0, ASR, kSmiTagSize)); // remove tags
@@ -982,7 +978,7 @@
frame_->EmitPush(ip);
frame_->EmitPush(r0);
}
- GenericBinaryOperation(op);
+ GenericBinaryOperation(op, mode);
break;
}
@@ -1427,13 +1423,13 @@
} else {
frame_->CallRuntime(Runtime::kPushContext, 1);
}
- if (kDebug) {
- JumpTarget verified_true(this);
- __ cmp(r0, Operand(cp));
- verified_true.Branch(eq);
- __ stop("PushContext: r0 is expected to be the same as cp");
- verified_true.Bind();
- }
+#ifdef DEBUG
+ JumpTarget verified_true(this);
+ __ cmp(r0, Operand(cp));
+ verified_true.Branch(eq);
+ __ stop("PushContext: r0 is expected to be the same as cp");
+ verified_true.Bind();
+#endif
// Update context local.
__ str(cp, frame_->Context());
ASSERT(frame_->height() == original_height);
@@ -1487,8 +1483,8 @@
// Test for a Smi value in a HeapNumber.
__ tst(r0, Operand(kSmiTagMask));
is_smi.Branch(eq);
- __ ldr(r1, MemOperand(r0, HeapObject::kMapOffset - kHeapObjectTag));
- __ ldrb(r1, MemOperand(r1, Map::kInstanceTypeOffset - kHeapObjectTag));
+ __ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ ldrb(r1, FieldMemOperand(r1, Map::kInstanceTypeOffset));
__ cmp(r1, Operand(HEAP_NUMBER_TYPE));
default_target->Branch(ne);
frame_->EmitPush(r0);
@@ -2339,7 +2335,9 @@
VirtualFrame::SpilledScope spilled_scope(this);
Comment cmnt(masm_, "[ DebuggerStatament");
CodeForStatementPosition(node);
+#ifdef ENABLE_DEBUGGER_SUPPORT
frame_->CallRuntime(Runtime::kDebugBreak, 0);
+#endif
// Ignore the return value.
ASSERT(frame_->height() == original_height);
}
@@ -2523,7 +2521,9 @@
if (s->is_eval_scope()) {
Label next, fast;
- if (!context.is(tmp)) __ mov(tmp, Operand(context));
+ if (!context.is(tmp)) {
+ __ mov(tmp, Operand(context));
+ }
__ bind(&next);
// Terminate at global context.
__ ldr(tmp2, FieldMemOperand(tmp, HeapObject::kMapOffset));
@@ -2934,15 +2934,24 @@
LoadAndSpill(node->value());
} else {
+ // +=, *= and similar binary assignments.
+ // Get the old value of the lhs.
target.GetValueAndSpill(NOT_INSIDE_TYPEOF);
Literal* literal = node->value()->AsLiteral();
+ bool overwrite =
+ (node->value()->AsBinaryOperation() != NULL &&
+ node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
if (literal != NULL && literal->handle()->IsSmi()) {
- SmiOperation(node->binary_op(), literal->handle(), false);
+ SmiOperation(node->binary_op(),
+ literal->handle(),
+ false,
+ overwrite ? OVERWRITE_RIGHT : NO_OVERWRITE);
frame_->EmitPush(r0);
} else {
LoadAndSpill(node->value());
- GenericBinaryOperation(node->binary_op());
+ GenericBinaryOperation(node->binary_op(),
+ overwrite ? OVERWRITE_RIGHT : NO_OVERWRITE);
frame_->EmitPush(r0);
}
}
@@ -3822,19 +3831,39 @@
// is a literal small integer.
Literal* lliteral = node->left()->AsLiteral();
Literal* rliteral = node->right()->AsLiteral();
+ // NOTE: The code below assumes that the slow cases (calls to runtime)
+ // never return a constant/immutable object.
+ bool overwrite_left =
+ (node->left()->AsBinaryOperation() != NULL &&
+ node->left()->AsBinaryOperation()->ResultOverwriteAllowed());
+ bool overwrite_right =
+ (node->right()->AsBinaryOperation() != NULL &&
+ node->right()->AsBinaryOperation()->ResultOverwriteAllowed());
if (rliteral != NULL && rliteral->handle()->IsSmi()) {
LoadAndSpill(node->left());
- SmiOperation(node->op(), rliteral->handle(), false);
+ SmiOperation(node->op(),
+ rliteral->handle(),
+ false,
+ overwrite_right ? OVERWRITE_RIGHT : NO_OVERWRITE);
} else if (lliteral != NULL && lliteral->handle()->IsSmi()) {
LoadAndSpill(node->right());
- SmiOperation(node->op(), lliteral->handle(), true);
+ SmiOperation(node->op(),
+ lliteral->handle(),
+ true,
+ overwrite_left ? OVERWRITE_LEFT : NO_OVERWRITE);
} else {
+ OverwriteMode overwrite_mode = NO_OVERWRITE;
+ if (overwrite_left) {
+ overwrite_mode = OVERWRITE_LEFT;
+ } else if (overwrite_right) {
+ overwrite_mode = OVERWRITE_RIGHT;
+ }
LoadAndSpill(node->left());
LoadAndSpill(node->right());
- GenericBinaryOperation(node->op());
+ GenericBinaryOperation(node->op(), overwrite_mode);
}
frame_->EmitPush(r0);
}
@@ -4067,7 +4096,8 @@
#undef __
-#define __ masm->
+#define __ ACCESS_MASM(masm)
+
Handle<String> Reference::GetName() {
ASSERT(type_ == NAMED);
@@ -4305,167 +4335,80 @@
}
-void GetPropertyStub::Generate(MacroAssembler* masm) {
- // sp[0]: key
- // sp[1]: receiver
- Label slow, fast;
- // Get the key and receiver object from the stack.
- __ ldm(ia, sp, r0.bit() | r1.bit());
- // Check that the key is a smi.
- __ tst(r0, Operand(kSmiTagMask));
- __ b(ne, &slow);
- __ mov(r0, Operand(r0, ASR, kSmiTagSize));
- // Check that the object isn't a smi.
- __ tst(r1, Operand(kSmiTagMask));
- __ b(eq, &slow);
-
- // Check that the object is some kind of JS object EXCEPT JS Value type.
- // In the case that the object is a value-wrapper object,
- // we enter the runtime system to make sure that indexing into string
- // objects work as intended.
- ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
- __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
- __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
- __ cmp(r2, Operand(JS_OBJECT_TYPE));
- __ b(lt, &slow);
-
- // Get the elements array of the object.
- __ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset));
- // Check that the object is in fast mode (not dictionary).
- __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
- __ cmp(r3, Operand(Factory::hash_table_map()));
- __ b(eq, &slow);
- // Check that the key (index) is within bounds.
- __ ldr(r3, FieldMemOperand(r1, Array::kLengthOffset));
- __ cmp(r0, Operand(r3));
- __ b(lo, &fast);
-
- // Slow case: Push extra copies of the arguments (2).
+static void HandleBinaryOpSlowCases(MacroAssembler* masm,
+ Label* not_smi,
+ const Builtins::JavaScript& builtin,
+ Token::Value operation,
+ int swi_number,
+ OverwriteMode mode) {
+ Label slow;
+ if (mode == NO_OVERWRITE) {
+ __ bind(not_smi);
+ }
__ bind(&slow);
- __ ldm(ia, sp, r0.bit() | r1.bit());
- __ stm(db_w, sp, r0.bit() | r1.bit());
- // Do tail-call to runtime routine.
- __ TailCallRuntime(ExternalReference(Runtime::kGetProperty), 2);
+ __ push(r1);
+ __ push(r0);
+ __ mov(r0, Operand(1)); // Set number of arguments.
+ __ InvokeBuiltin(builtin, JUMP_JS); // Tail call.
- // Fast case: Do the load.
- __ bind(&fast);
- __ add(r3, r1, Operand(Array::kHeaderSize - kHeapObjectTag));
- __ ldr(r0, MemOperand(r3, r0, LSL, kPointerSizeLog2));
- __ cmp(r0, Operand(Factory::the_hole_value()));
- // In case the loaded value is the_hole we have to consult GetProperty
- // to ensure the prototype chain is searched.
- __ b(eq, &slow);
-
- __ StubReturn(1);
-}
-
-
-void SetPropertyStub::Generate(MacroAssembler* masm) {
- // r0 : value
- // sp[0] : key
- // sp[1] : receiver
-
- Label slow, fast, array, extra, exit;
- // Get the key and the object from the stack.
- __ ldm(ia, sp, r1.bit() | r3.bit()); // r1 = key, r3 = receiver
- // Check that the key is a smi.
- __ tst(r1, Operand(kSmiTagMask));
- __ b(ne, &slow);
- // Check that the object isn't a smi.
- __ tst(r3, Operand(kSmiTagMask));
- __ b(eq, &slow);
- // Get the type of the object from its map.
- __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
- __ ldrb(r2, FieldMemOperand(r2, Map::kInstanceTypeOffset));
- // Check if the object is a JS array or not.
- __ cmp(r2, Operand(JS_ARRAY_TYPE));
- __ b(eq, &array);
- // Check that the object is some kind of JS object.
- __ cmp(r2, Operand(FIRST_JS_OBJECT_TYPE));
- __ b(lt, &slow);
-
-
- // Object case: Check key against length in the elements array.
- __ ldr(r3, FieldMemOperand(r3, JSObject::kElementsOffset));
- // Check that the object is in fast mode (not dictionary).
- __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
- __ cmp(r2, Operand(Factory::hash_table_map()));
- __ b(eq, &slow);
- // Untag the key (for checking against untagged length in the fixed array).
- __ mov(r1, Operand(r1, ASR, kSmiTagSize));
- // Compute address to store into and check array bounds.
- __ add(r2, r3, Operand(Array::kHeaderSize - kHeapObjectTag));
- __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2));
- __ ldr(ip, FieldMemOperand(r3, Array::kLengthOffset));
- __ cmp(r1, Operand(ip));
- __ b(lo, &fast);
-
-
- // Slow case: Push extra copies of the arguments (3).
- __ bind(&slow);
- __ ldm(ia, sp, r1.bit() | r3.bit()); // r0 == value, r1 == key, r3 == object
- __ stm(db_w, sp, r0.bit() | r1.bit() | r3.bit());
- // Do tail-call to runtime routine.
- __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3);
-
-
- // Extra capacity case: Check if there is extra capacity to
- // perform the store and update the length. Used for adding one
- // element to the array by writing to array[array.length].
- // r0 == value, r1 == key, r2 == elements, r3 == object
- __ bind(&extra);
- __ b(ne, &slow); // do not leave holes in the array
- __ mov(r1, Operand(r1, ASR, kSmiTagSize)); // untag
- __ ldr(ip, FieldMemOperand(r2, Array::kLengthOffset));
- __ cmp(r1, Operand(ip));
- __ b(hs, &slow);
- __ mov(r1, Operand(r1, LSL, kSmiTagSize)); // restore tag
- __ add(r1, r1, Operand(1 << kSmiTagSize)); // and increment
- __ str(r1, FieldMemOperand(r3, JSArray::kLengthOffset));
- __ mov(r3, Operand(r2));
- // NOTE: Computing the address to store into must take the fact
- // that the key has been incremented into account.
- int displacement = Array::kHeaderSize - kHeapObjectTag -
- ((1 << kSmiTagSize) * 2);
- __ add(r2, r2, Operand(displacement));
- __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize));
- __ b(&fast);
-
-
- // Array case: Get the length and the elements array from the JS
- // array. Check that the array is in fast mode; if it is the
- // length is always a smi.
- // r0 == value, r3 == object
- __ bind(&array);
- __ ldr(r2, FieldMemOperand(r3, JSObject::kElementsOffset));
- __ ldr(r1, FieldMemOperand(r2, HeapObject::kMapOffset));
- __ cmp(r1, Operand(Factory::hash_table_map()));
- __ b(eq, &slow);
-
- // Check the key against the length in the array, compute the
- // address to store into and fall through to fast case.
- __ ldr(r1, MemOperand(sp));
- // r0 == value, r1 == key, r2 == elements, r3 == object.
- __ ldr(ip, FieldMemOperand(r3, JSArray::kLengthOffset));
- __ cmp(r1, Operand(ip));
- __ b(hs, &extra);
- __ mov(r3, Operand(r2));
- __ add(r2, r2, Operand(Array::kHeaderSize - kHeapObjectTag));
- __ add(r2, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize));
-
-
- // Fast case: Do the store.
- // r0 == value, r2 == address to store into, r3 == elements
- __ bind(&fast);
- __ str(r0, MemOperand(r2));
- // Skip write barrier if the written value is a smi.
- __ tst(r0, Operand(kSmiTagMask));
- __ b(eq, &exit);
- // Update write barrier for the elements array address.
- __ sub(r1, r2, Operand(r3));
- __ RecordWrite(r3, r1, r2);
- __ bind(&exit);
- __ StubReturn(1);
+ // Could it be a double-double op? If we already have a place to put
+ // the answer then we can do the op and skip the builtin and runtime call.
+ if (mode != NO_OVERWRITE) {
+ __ bind(not_smi);
+ __ tst(r0, Operand(kSmiTagMask));
+ __ b(eq, &slow); // We can't handle a Smi-double combination yet.
+ __ tst(r1, Operand(kSmiTagMask));
+ __ b(eq, &slow); // We can't handle a Smi-double combination yet.
+ // Get map of r0 into r2.
+ __ ldr(r2, FieldMemOperand(r0, HeapObject::kMapOffset));
+ // Get type of r0 into r3.
+ __ ldrb(r3, FieldMemOperand(r2, Map::kInstanceTypeOffset));
+ __ cmp(r3, Operand(HEAP_NUMBER_TYPE));
+ __ b(ne, &slow);
+ // Get type of r1 into r3.
+ __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
+ // Check they are both the same map (heap number map).
+ __ cmp(r2, r3);
+ __ b(ne, &slow);
+ // Both are doubles.
+ // Calling convention says that second double is in r2 and r3.
+ __ ldr(r2, FieldMemOperand(r0, HeapNumber::kValueOffset));
+ __ ldr(r3, FieldMemOperand(r0, HeapNumber::kValueOffset + kPointerSize));
+ __ push(lr);
+ if (mode == OVERWRITE_LEFT) {
+ __ push(r1);
+ } else {
+ __ push(r0);
+ }
+ // Calling convention says that first double is in r0 and r1.
+ __ ldr(r0, FieldMemOperand(r1, HeapNumber::kValueOffset));
+ __ ldr(r1, FieldMemOperand(r1, HeapNumber::kValueOffset + kPointerSize));
+ // Call C routine that may not cause GC or other trouble.
+ __ mov(r5, Operand(ExternalReference::double_fp_operation(operation)));
+#if !defined(__arm__)
+ // Notify the simulator that we are calling an add routine in C.
+ __ swi(swi_number);
+#else
+ // Actually call the add routine written in C.
+ __ Call(r5);
+#endif
+ // Store answer in the overwritable heap number.
+ __ pop(r4);
+#if !defined(__ARM_EABI__) && defined(__arm__)
+ // Double returned in fp coprocessor register 0 and 1, encoded as register
+ // cr8. Offsets must be divisible by 4 for coprocessor so we need to
+ // substract the tag from r4.
+ __ sub(r5, r4, Operand(kHeapObjectTag));
+ __ stc(p1, cr8, MemOperand(r5, HeapNumber::kValueOffset));
+#else
+ // Double returned in fp coprocessor register 0 and 1.
+ __ str(r0, FieldMemOperand(r4, HeapNumber::kValueOffset));
+ __ str(r1, FieldMemOperand(r4, HeapNumber::kValueOffset + kPointerSize));
+#endif
+ __ mov(r0, Operand(r4));
+ // And we are done.
+ __ pop(pc);
+ }
}
@@ -4474,89 +4417,84 @@
// r0 : y
// result : r0
+ // All ops need to know whether we are dealing with two Smis. Set up r2 to
+ // tell us that.
+ __ orr(r2, r1, Operand(r0)); // r2 = x | y;
+
switch (op_) {
case Token::ADD: {
- Label slow, exit;
- // fast path
- __ orr(r2, r1, Operand(r0)); // r2 = x | y;
- __ add(r0, r1, Operand(r0), SetCC); // add y optimistically
- // go slow-path in case of overflow
- __ b(vs, &slow);
- // go slow-path in case of non-smi operands
- ASSERT(kSmiTag == 0); // adjust code below
+ Label not_smi;
+ // Fast path.
+ ASSERT(kSmiTag == 0); // Adjust code below.
__ tst(r2, Operand(kSmiTagMask));
- __ b(eq, &exit);
- // slow path
- __ bind(&slow);
- __ sub(r0, r0, Operand(r1)); // revert optimistic add
- __ push(r1);
- __ push(r0);
- __ mov(r0, Operand(1)); // set number of arguments
- __ InvokeBuiltin(Builtins::ADD, JUMP_JS);
- // done
- __ bind(&exit);
+ __ b(ne, ¬_smi);
+ __ add(r0, r1, Operand(r0), SetCC); // Add y optimistically.
+ // Return if no overflow.
+ __ Ret(vc);
+ __ sub(r0, r0, Operand(r1)); // Revert optimistic add.
+
+ HandleBinaryOpSlowCases(masm,
+ ¬_smi,
+ Builtins::ADD,
+ Token::ADD,
+ assembler::arm::simulator_fp_add,
+ mode_);
break;
}
case Token::SUB: {
- Label slow, exit;
- // fast path
- __ orr(r2, r1, Operand(r0)); // r2 = x | y;
- __ sub(r3, r1, Operand(r0), SetCC); // subtract y optimistically
- // go slow-path in case of overflow
- __ b(vs, &slow);
- // go slow-path in case of non-smi operands
- ASSERT(kSmiTag == 0); // adjust code below
+ Label not_smi;
+ // Fast path.
+ ASSERT(kSmiTag == 0); // Adjust code below.
__ tst(r2, Operand(kSmiTagMask));
- __ mov(r0, Operand(r3), LeaveCC, eq); // conditionally set r0 to result
- __ b(eq, &exit);
- // slow path
- __ bind(&slow);
- __ push(r1);
- __ push(r0);
- __ mov(r0, Operand(1)); // set number of arguments
- __ InvokeBuiltin(Builtins::SUB, JUMP_JS);
- // done
- __ bind(&exit);
+ __ b(ne, ¬_smi);
+ __ sub(r0, r1, Operand(r0), SetCC); // Subtract y optimistically.
+ // Return if no overflow.
+ __ Ret(vc);
+ __ sub(r0, r1, Operand(r0)); // Revert optimistic subtract.
+
+ HandleBinaryOpSlowCases(masm,
+ ¬_smi,
+ Builtins::SUB,
+ Token::SUB,
+ assembler::arm::simulator_fp_sub,
+ mode_);
break;
}
case Token::MUL: {
- Label slow, exit;
- // tag check
- __ orr(r2, r1, Operand(r0)); // r2 = x | y;
+ Label not_smi, slow;
ASSERT(kSmiTag == 0); // adjust code below
__ tst(r2, Operand(kSmiTagMask));
- __ b(ne, &slow);
- // remove tag from one operand (but keep sign), so that result is smi
+ __ b(ne, ¬_smi);
+ // Remove tag from one operand (but keep sign), so that result is Smi.
__ mov(ip, Operand(r0, ASR, kSmiTagSize));
- // do multiplication
- __ smull(r3, r2, r1, ip); // r3 = lower 32 bits of ip*r1
- // go slow on overflows (overflow bit is not set)
+ // Do multiplication
+ __ smull(r3, r2, r1, ip); // r3 = lower 32 bits of ip*r1.
+ // Go slow on overflows (overflow bit is not set).
__ mov(ip, Operand(r3, ASR, 31));
__ cmp(ip, Operand(r2)); // no overflow if higher 33 bits are identical
__ b(ne, &slow);
- // go slow on zero result to handle -0
+ // Go slow on zero result to handle -0.
__ tst(r3, Operand(r3));
__ mov(r0, Operand(r3), LeaveCC, ne);
- __ b(ne, &exit);
- // slow case
+ __ Ret(ne);
+ // Slow case.
__ bind(&slow);
- __ push(r1);
- __ push(r0);
- __ mov(r0, Operand(1)); // set number of arguments
- __ InvokeBuiltin(Builtins::MUL, JUMP_JS);
- // done
- __ bind(&exit);
+
+ HandleBinaryOpSlowCases(masm,
+ ¬_smi,
+ Builtins::MUL,
+ Token::MUL,
+ assembler::arm::simulator_fp_mul,
+ mode_);
break;
}
case Token::BIT_OR:
case Token::BIT_AND:
case Token::BIT_XOR: {
- Label slow, exit;
- // tag check
- __ orr(r2, r1, Operand(r0)); // r2 = x | y;
+ Label slow;
ASSERT(kSmiTag == 0); // adjust code below
__ tst(r2, Operand(kSmiTagMask));
__ b(ne, &slow);
@@ -4566,7 +4504,7 @@
case Token::BIT_XOR: __ eor(r0, r0, Operand(r1)); break;
default: UNREACHABLE();
}
- __ b(&exit);
+ __ Ret();
__ bind(&slow);
__ push(r1); // restore stack
__ push(r0);
@@ -4584,16 +4522,13 @@
default:
UNREACHABLE();
}
- __ bind(&exit);
break;
}
case Token::SHL:
case Token::SHR:
case Token::SAR: {
- Label slow, exit;
- // tag check
- __ orr(r2, r1, Operand(r0)); // r2 = x | y;
+ Label slow;
ASSERT(kSmiTag == 0); // adjust code below
__ tst(r2, Operand(kSmiTagMask));
__ b(ne, &slow);
@@ -4633,7 +4568,7 @@
// tag result and store it in r0
ASSERT(kSmiTag == 0); // adjust code below
__ mov(r0, Operand(r3, LSL, kSmiTagSize));
- __ b(&exit);
+ __ Ret();
// slow case
__ bind(&slow);
__ push(r1); // restore stack
@@ -4645,13 +4580,13 @@
case Token::SHL: __ InvokeBuiltin(Builtins::SHL, JUMP_JS); break;
default: UNREACHABLE();
}
- __ bind(&exit);
break;
}
default: UNREACHABLE();
}
- __ Ret();
+ // This code should be unreachable.
+ __ stop("Unreachable");
}
@@ -4721,7 +4656,11 @@
__ mov(cp, Operand(0), LeaveCC, eq);
// Restore cp otherwise.
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
- if (kDebug && FLAG_debug_code) __ mov(lr, Operand(pc));
+#ifdef DEBUG
+ if (FLAG_debug_code) {
+ __ mov(lr, Operand(pc));
+ }
+#endif
__ pop(pc);
}
@@ -4784,7 +4723,11 @@
__ mov(cp, Operand(0), LeaveCC, eq);
// Restore cp otherwise.
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset), ne);
- if (kDebug && FLAG_debug_code) __ mov(lr, Operand(pc));
+#ifdef DEBUG
+ if (FLAG_debug_code) {
+ __ mov(lr, Operand(pc));
+ }
+#endif
__ pop(pc);
}
@@ -5043,9 +4986,11 @@
}
__ ldr(ip, MemOperand(ip)); // deref address
- // Branch and link to JSEntryTrampoline
+ // Branch and link to JSEntryTrampoline. We don't use the double underscore
+ // macro for the add instruction because we don't want the coverage tool
+ // inserting instructions here after we read the pc.
__ mov(lr, Operand(pc));
- __ add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
+ masm->add(pc, ip, Operand(Code::kHeaderSize - kHeapObjectTag));
// Unlink this frame from the handler chain. When reading the
// address of the next handler, there is no need to use the address
@@ -5057,6 +5002,7 @@
// No need to restore registers
__ add(sp, sp, Operand(StackHandlerConstants::kSize));
+
__ bind(&exit); // r0 holds result
// Restore the top frame descriptors from the stack.
__ pop(r3);
@@ -5068,7 +5014,9 @@
// Restore callee-saved registers and return.
#ifdef DEBUG
- if (FLAG_debug_code) __ mov(lr, Operand(pc));
+ if (FLAG_debug_code) {
+ __ mov(lr, Operand(pc));
+ }
#endif
__ ldm(ia_w, sp, kCalleeSaved | pc.bit());
}
diff --git a/src/codegen-arm.h b/src/codegen-arm.h
index b072f75..48b92ea 100644
--- a/src/codegen-arm.h
+++ b/src/codegen-arm.h
@@ -35,9 +35,6 @@
class RegisterAllocator;
class RegisterFile;
-// Mode to overwrite BinaryExpression values.
-enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT };
-
enum InitState { CONST_INIT, NOT_CONST_INIT };
enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
@@ -292,10 +289,13 @@
void ToBoolean(JumpTarget* true_target, JumpTarget* false_target);
- void GenericBinaryOperation(Token::Value op);
+ void GenericBinaryOperation(Token::Value op, OverwriteMode overwrite_mode);
void Comparison(Condition cc, bool strict = false);
- void SmiOperation(Token::Value op, Handle<Object> value, bool reversed);
+ void SmiOperation(Token::Value op,
+ Handle<Object> value,
+ bool reversed,
+ OverwriteMode mode);
void CallWithArguments(ZoneList<Expression*>* arguments, int position);
diff --git a/src/codegen-ia32.cc b/src/codegen-ia32.cc
index 663f235..fb2f8bf 100644
--- a/src/codegen-ia32.cc
+++ b/src/codegen-ia32.cc
@@ -37,7 +37,7 @@
namespace v8 { namespace internal {
-#define __ masm_->
+#define __ ACCESS_MASM(masm_)
// -------------------------------------------------------------------------
// CodeGenState implementation.
@@ -1274,12 +1274,9 @@
smi_value,
overwrite_mode);
__ Set(answer.reg(), Immediate(value));
- if (operand->is_register()) {
- __ sub(answer.reg(), Operand(operand->reg()));
- } else {
- ASSERT(operand->is_constant());
- __ sub(Operand(answer.reg()), Immediate(operand->handle()));
- }
+ // We are in the reversed case so they can't both be Smi constants.
+ ASSERT(operand->is_register());
+ __ sub(answer.reg(), Operand(operand->reg()));
} else {
operand->ToRegister();
frame_->Spill(operand->reg());
@@ -1374,23 +1371,26 @@
operand->ToRegister();
__ test(operand->reg(), Immediate(kSmiTagMask));
deferred->enter()->Branch(not_zero, operand, not_taken);
- Result answer = allocator()->Allocate();
- ASSERT(answer.is_valid());
- __ mov(answer.reg(), operand->reg());
- ASSERT(kSmiTag == 0); // adjust code if not the case
- // We do no shifts, only the Smi conversion, if shift_value is 1.
- if (shift_value == 0) {
- __ sar(answer.reg(), kSmiTagSize);
- } else if (shift_value > 1) {
- __ shl(answer.reg(), shift_value - 1);
+ if (shift_value != 0) {
+ Result answer = allocator()->Allocate();
+ ASSERT(answer.is_valid());
+ __ mov(answer.reg(), operand->reg());
+ ASSERT(kSmiTag == 0); // adjust code if not the case
+ // We do no shifts, only the Smi conversion, if shift_value is 1.
+ if (shift_value > 1) {
+ __ shl(answer.reg(), shift_value - 1);
+ }
+ // Convert int result to Smi, checking that it is in int range.
+ ASSERT(kSmiTagSize == times_2); // adjust code if not the case
+ __ add(answer.reg(), Operand(answer.reg()));
+ deferred->enter()->Branch(overflow, operand, not_taken);
+ operand->Unuse();
+ deferred->BindExit(&answer);
+ frame_->Push(&answer);
+ } else {
+ deferred->BindExit(operand);
+ frame_->Push(operand);
}
- // Convert int result to Smi, checking that it is in int range.
- ASSERT(kSmiTagSize == times_2); // adjust code if not the case
- __ add(answer.reg(), Operand(answer.reg()));
- deferred->enter()->Branch(overflow, operand, not_taken);
- operand->Unuse();
- deferred->BindExit(&answer);
- frame_->Push(&answer);
}
break;
}
@@ -1411,11 +1411,7 @@
deferred->enter()->Branch(not_zero, operand, not_taken);
frame_->Spill(operand->reg());
if (op == Token::BIT_AND) {
- if (int_value == 0) {
- __ xor_(Operand(operand->reg()), operand->reg());
- } else {
- __ and_(Operand(operand->reg()), Immediate(value));
- }
+ __ and_(Operand(operand->reg()), Immediate(value));
} else if (op == Token::BIT_XOR) {
if (int_value != 0) {
__ xor_(Operand(operand->reg()), Immediate(value));
@@ -2009,18 +2005,18 @@
// Add a label for checking the size of the code used for returning.
Label check_exit_codesize;
- __ bind(&check_exit_codesize);
+ masm_->bind(&check_exit_codesize);
// Leave the frame and return popping the arguments and the
// receiver.
frame_->Exit();
- __ ret((scope_->num_parameters() + 1) * kPointerSize);
+ masm_->ret((scope_->num_parameters() + 1) * kPointerSize);
DeleteFrame();
// Check that the size of the code used for returning matches what is
// expected by the debugger.
ASSERT_EQ(Debug::kIa32JSReturnSequenceLength,
- __ SizeOfCodeGeneratedSince(&check_exit_codesize));
+ masm_->SizeOfCodeGeneratedSince(&check_exit_codesize));
}
@@ -2143,7 +2139,7 @@
times_1, 0x0, RelocInfo::INTERNAL_REFERENCE));
smi_value.Unuse();
// Calculate address to overwrite later with actual address of table.
- int32_t jump_table_ref = __ pc_offset() - sizeof(int32_t);
+ int32_t jump_table_ref = masm_->pc_offset() - sizeof(int32_t);
__ Align(4);
Label table_start;
__ bind(&table_start);
@@ -3179,10 +3175,12 @@
ASSERT(!in_spilled_code());
Comment cmnt(masm_, "[ DebuggerStatement");
CodeForStatementPosition(node);
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Spill everything, even constants, to the frame.
frame_->SpillAll();
frame_->CallRuntime(Runtime::kDebugBreak, 0);
// Ignore the return value.
+#endif
}
@@ -3384,7 +3382,9 @@
// Loop up the context chain. There is no frame effect so it is
// safe to use raw labels here.
Label next, fast;
- if (!context.reg().is(tmp.reg())) __ mov(tmp.reg(), context.reg());
+ if (!context.reg().is(tmp.reg())) {
+ __ mov(tmp.reg(), context.reg());
+ }
__ bind(&next);
// Terminate at global context.
__ cmp(FieldOperand(tmp.reg(), HeapObject::kMapOffset),
@@ -3933,6 +3933,9 @@
} else {
Literal* literal = node->value()->AsLiteral();
+ bool overwrite_value =
+ (node->value()->AsBinaryOperation() != NULL &&
+ node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
Variable* right_var = node->value()->AsVariableProxy()->AsVariable();
// There are two cases where the target is not read in the right hand
// side, that are easy to test for: the right hand side is a literal,
@@ -3945,7 +3948,9 @@
target.GetValue(NOT_INSIDE_TYPEOF);
}
Load(node->value());
- GenericBinaryOperation(node->binary_op(), node->type());
+ GenericBinaryOperation(node->binary_op(),
+ node->type(),
+ overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
}
if (var != NULL &&
@@ -5268,8 +5273,11 @@
// instruction.
ASSERT(value.is_register() && value.reg().is(eax));
// The delta from the start of the map-compare instruction to the
- // test eax instruction.
- int delta_to_patch_site = __ SizeOfCodeGeneratedSince(patch_site());
+ // test eax instruction. We use masm_ directly here instead of the
+ // double underscore macro because the macro sometimes uses macro
+ // expansion to turn into something that can't return a value. This
+ // is encountered when doing generated code coverage tests.
+ int delta_to_patch_site = masm_->SizeOfCodeGeneratedSince(patch_site());
__ test(value.reg(), Immediate(-delta_to_patch_site));
__ IncrementCounter(&Counters::keyed_load_inline_miss, 1);
@@ -5284,7 +5292,7 @@
#undef __
-#define __ masm->
+#define __ ACCESS_MASM(masm)
Handle<String> Reference::GetName() {
ASSERT(type_ == NAMED);
@@ -5369,7 +5377,9 @@
// Initially, use an invalid map. The map is patched in the IC
// initialization code.
__ bind(deferred->patch_site());
- __ cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
+ // Use masm-> here instead of the double underscore macro since extra
+ // coverage code can interfere with the patching.
+ masm->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
Immediate(Factory::null_value()));
deferred->enter()->Branch(not_equal, &receiver, &key, not_taken);
@@ -5566,7 +5576,7 @@
#undef __
-#define __ masm_->
+#define __ ACCESS_MASM(masm_)
Result DeferredInlineBinaryOperation::GenerateInlineCode(Result* left,
Result* right) {
@@ -5900,7 +5910,7 @@
#undef __
-#define __ masm->
+#define __ ACCESS_MASM(masm)
void GenericBinaryOpStub::GenerateSmiCode(MacroAssembler* masm, Label* slow) {
// Perform fast-case smi code for the operation (eax <op> ebx) and
@@ -6225,7 +6235,9 @@
}
// SHR should return uint32 - go to runtime for non-smi/negative result.
- if (op_ == Token::SHR) __ bind(&non_smi_result);
+ if (op_ == Token::SHR) {
+ __ bind(&non_smi_result);
+ }
__ mov(eax, Operand(esp, 1 * kPointerSize));
__ mov(edx, Operand(esp, 2 * kPointerSize));
break;
diff --git a/src/codegen-ia32.h b/src/codegen-ia32.h
index 24a57a0..a39bc58 100644
--- a/src/codegen-ia32.h
+++ b/src/codegen-ia32.h
@@ -35,9 +35,6 @@
class RegisterAllocator;
class RegisterFile;
-// Mode to overwrite BinaryExpression values.
-enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT };
-
enum InitState { CONST_INIT, NOT_CONST_INIT };
enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
@@ -435,7 +432,7 @@
void GenericBinaryOperation(
Token::Value op,
SmiAnalysis* type,
- const OverwriteMode overwrite_mode = NO_OVERWRITE);
+ OverwriteMode overwrite_mode);
// If possible, combine two constant smi values using op to produce
// a smi result, and push it on the virtual frame, all at compile time.
diff --git a/src/codegen.cc b/src/codegen.cc
index edc498d..ed7ee2f 100644
--- a/src/codegen.cc
+++ b/src/codegen.cc
@@ -304,8 +304,10 @@
node->is_expression(), false, script_,
node->inferred_name());
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Notify debugger that a new function has been added.
Debugger::OnNewFunction(function);
+#endif
// Set the expected number of properties for instances and return
// the resulting function.
diff --git a/src/codegen.h b/src/codegen.h
index dd43cc0..54fe330 100644
--- a/src/codegen.h
+++ b/src/codegen.h
@@ -71,6 +71,11 @@
// CodeForStatementPosition
// CodeForSourcePosition
+
+// Mode to overwrite BinaryExpression values.
+enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT };
+
+
#ifdef ARM
#include "codegen-arm.h"
#else
diff --git a/src/compiler.cc b/src/compiler.cc
index 63fed4a..62e838e 100644
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -92,8 +92,10 @@
StackGuard guard;
PostponeInterruptsScope postpone;
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Notify debugger
Debugger::OnBeforeCompile(script);
+#endif
// Only allow non-global compiles for eval.
ASSERT(is_eval || is_global);
@@ -160,8 +162,10 @@
// the instances of the function.
SetExpectedNofPropertiesFromEstimate(fun, lit->expected_property_count());
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Notify debugger
Debugger::OnAfterCompile(script, fun);
+#endif
return fun;
}
diff --git a/src/constants-arm.h b/src/constants-arm.h
index f553963..919a892 100644
--- a/src/constants-arm.h
+++ b/src/constants-arm.h
@@ -106,7 +106,12 @@
call_rt_r5 = 0x10,
call_rt_r2 = 0x11,
// break point
- break_point = 0x20
+ break_point = 0x20,
+ // FP operations. These simulate calling into C for a moment to do fp ops.
+ // They should trash all caller-save registers.
+ simulator_fp_add = 0x21,
+ simulator_fp_sub = 0x22,
+ simulator_fp_mul = 0x23
};
diff --git a/src/d8.cc b/src/d8.cc
index 9648168..70143c3 100644
--- a/src/d8.cc
+++ b/src/d8.cc
@@ -163,6 +163,22 @@
}
+Handle<Value> Shell::Read(const Arguments& args) {
+ if (args.Length() != 1) {
+ return ThrowException(String::New("Bad parameters"));
+ }
+ String::Utf8Value file(args[0]);
+ if (*file == NULL) {
+ return ThrowException(String::New("Error loading file"));
+ }
+ Handle<String> source = ReadFile(*file);
+ if (source.IsEmpty()) {
+ return ThrowException(String::New("Error loading file"));
+ }
+ return source;
+}
+
+
Handle<Value> Shell::Load(const Arguments& args) {
for (int i = 0; i < args.Length(); i++) {
HandleScope handle_scope;
@@ -246,6 +262,7 @@
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
Handle<Object> Shell::DebugMessageDetails(Handle<String> message) {
Context::Scope context_scope(utility_context_);
Handle<Object> global = utility_context_->Global();
@@ -266,6 +283,7 @@
Handle<Value> val = Handle<Function>::Cast(fun)->Call(global, kArgc, argv);
return val;
}
+#endif
int32_t* Counter::Bind(const char* name, bool is_histogram) {
@@ -381,6 +399,7 @@
HandleScope scope;
Handle<ObjectTemplate> global_template = ObjectTemplate::New();
global_template->Set(String::New("print"), FunctionTemplate::New(Print));
+ global_template->Set(String::New("read"), FunctionTemplate::New(Read));
global_template->Set(String::New("load"), FunctionTemplate::New(Load));
global_template->Set(String::New("quit"), FunctionTemplate::New(Quit));
global_template->Set(String::New("version"), FunctionTemplate::New(Version));
@@ -406,11 +425,13 @@
global_template->Set(String::New("arguments"),
Utils::ToLocal(arguments_jsarray));
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Install the debugger object in the utility scope
i::Debug::Load();
i::JSObject* debug = i::Debug::debug_context()->global();
utility_context_->Global()->Set(String::New("$debug"),
Utils::ToLocal(&debug));
+#endif
// Run the d8 shell utility script in the utility context
int source_index = i::NativesCollection<i::D8>::GetIndex("d8");
@@ -436,8 +457,10 @@
evaluation_context_ = Context::New(NULL, global_template);
evaluation_context_->SetSecurityToken(Undefined());
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Set the security token of the debug context to allow access.
i::Debug::debug_context()->set_security_token(i::Heap::undefined_value());
+#endif
}
@@ -555,6 +578,8 @@
Handle<ObjectTemplate> global_template = ObjectTemplate::New();
global_template->Set(String::New("print"),
FunctionTemplate::New(Shell::Print));
+ global_template->Set(String::New("read"),
+ FunctionTemplate::New(Shell::Read));
global_template->Set(String::New("load"),
FunctionTemplate::New(Shell::Load));
global_template->Set(String::New("yield"),
@@ -690,6 +715,7 @@
Locker::StartPreemption(preemption_interval);
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Run the remote debugger if requested.
if (i::FLAG_remote_debugger) {
RunRemoteDebugger(i::FLAG_debugger_port);
@@ -705,6 +731,7 @@
if (i::FLAG_debugger && !i::FLAG_debugger_agent) {
v8::Debug::SetDebugEventListener(HandleDebugEvent);
}
+#endif
}
if (run_shell)
RunShell();
diff --git a/src/d8.h b/src/d8.h
index 342a0d2..092e3a3 100644
--- a/src/d8.h
+++ b/src/d8.h
@@ -132,13 +132,16 @@
static int Main(int argc, char* argv[]);
static Handle<Array> GetCompletions(Handle<String> text,
Handle<String> full);
+#ifdef ENABLE_DEBUGGER_SUPPORT
static Handle<Object> DebugMessageDetails(Handle<String> message);
static Handle<Value> DebugCommandToJSONRequest(Handle<String> command);
+#endif
static Handle<Value> Print(const Arguments& args);
static Handle<Value> Yield(const Arguments& args);
static Handle<Value> Quit(const Arguments& args);
static Handle<Value> Version(const Arguments& args);
+ static Handle<Value> Read(const Arguments& args);
static Handle<Value> Load(const Arguments& args);
// The OS object on the global object contains methods for performing
// operating system calls:
diff --git a/src/d8.js b/src/d8.js
index e2766d0..ea2fb44 100644
--- a/src/d8.js
+++ b/src/d8.js
@@ -653,17 +653,47 @@
// Process arguments if any.
if (args && args.length > 0) {
var target = args;
+ var type = 'function';
+ var line;
+ var column;
var condition;
+ var pos;
- var pos = args.indexOf(' ');
+ // Check for breakpoint condition.
+ pos = args.indexOf(' ');
if (pos > 0) {
target = args.substring(0, pos);
condition = args.substring(pos + 1, args.length);
}
+ // Check for script breakpoint (name:line[:column]). If no ':' in break
+ // specification it is considered a function break point.
+ pos = target.indexOf(':');
+ if (pos > 0) {
+ type = 'script';
+ var tmp = target.substring(pos + 1, target.length);
+ target = target.substring(0, pos);
+
+ // Check for both line and column.
+ pos = tmp.indexOf(':');
+ if (pos > 0) {
+ column = parseInt(tmp.substring(pos + 1, tmp.length)) - 1;
+ line = parseInt(tmp.substring(0, pos)) - 1;
+ } else {
+ line = parseInt(tmp) - 1;
+ }
+ } else if (target[0] == '#' && target[target.length - 1] == '#') {
+ type = 'handle';
+ target = target.substring(1, target.length - 1);
+ } else {
+ type = 'function';
+ }
+
request.arguments = {};
- request.arguments.type = 'function';
+ request.arguments.type = type;
request.arguments.target = target;
+ request.arguments.line = line;
+ request.arguments.column = column;
request.arguments.condition = condition;
} else {
throw new Error('Invalid break arguments.');
@@ -721,6 +751,9 @@
}
print('break location [condition]');
+ print(' break on named function: location is a function name');
+ print(' break on function: location is #<id>#');
+ print(' break on script position: location is name:line[:column]');
print('clear <breakpoint #>');
print('backtrace [from frame #] [to frame #]]');
print('frame <frame #>');
diff --git a/src/debug-agent.cc b/src/debug-agent.cc
index e865e0e..9838746 100644
--- a/src/debug-agent.cc
+++ b/src/debug-agent.cc
@@ -29,16 +29,18 @@
#include "v8.h"
#include "debug-agent.h"
+#ifdef ENABLE_DEBUGGER_SUPPORT
namespace v8 { namespace internal {
-
// Public V8 debugger API message handler function. This function just delegates
// to the debugger agent through it's data parameter.
void DebuggerAgentMessageHandler(const uint16_t* message, int length,
- void *data) {
- reinterpret_cast<DebuggerAgent*>(data)->DebuggerMessage(message, length);
+ v8::Debug::ClientData* client_data) {
+ DebuggerAgent::instance_->DebuggerMessage(message, length);
}
+// static
+DebuggerAgent* DebuggerAgent::instance_ = NULL;
// Debugger agent main thread.
void DebuggerAgent::Run() {
@@ -410,5 +412,6 @@
return total_received;
}
-
} } // namespace v8::internal
+
+#endif // ENABLE_DEBUGGER_SUPPORT
diff --git a/src/debug-agent.h b/src/debug-agent.h
index 177af0c..08f1372 100644
--- a/src/debug-agent.h
+++ b/src/debug-agent.h
@@ -28,12 +28,12 @@
#ifndef V8_V8_DEBUG_AGENT_H_
#define V8_V8_DEBUG_AGENT_H_
+#ifdef ENABLE_DEBUGGER_SUPPORT
#include "../include/v8-debug.h"
#include "platform.h"
namespace v8 { namespace internal {
-
// Forward decelrations.
class DebuggerAgentSession;
@@ -46,8 +46,14 @@
: name_(StrDup(name)), port_(port),
server_(OS::CreateSocket()), terminate_(false),
session_access_(OS::CreateMutex()), session_(NULL),
- terminate_now_(OS::CreateSemaphore(0)) {}
- ~DebuggerAgent() { delete server_; }
+ terminate_now_(OS::CreateSemaphore(0)) {
+ ASSERT(instance_ == NULL);
+ instance_ = this;
+ }
+ ~DebuggerAgent() {
+ instance_ = NULL;
+ delete server_;
+ }
void Shutdown();
@@ -66,9 +72,11 @@
DebuggerAgentSession* session_; // Current active session if any.
Semaphore* terminate_now_; // Semaphore to signal termination.
+ static DebuggerAgent* instance_;
+
friend class DebuggerAgentSession;
friend void DebuggerAgentMessageHandler(const uint16_t* message, int length,
- void *data);
+ v8::Debug::ClientData* client_data);
DISALLOW_COPY_AND_ASSIGN(DebuggerAgent);
};
@@ -111,7 +119,8 @@
static int ReceiveAll(const Socket* conn, char* data, int len);
};
-
} } // namespace v8::internal
+#endif // ENABLE_DEBUGGER_SUPPORT
+
#endif // V8_V8_DEBUG_AGENT_H_
diff --git a/src/debug-arm.cc b/src/debug-arm.cc
index 9fb77b7..f86f981 100644
--- a/src/debug-arm.cc
+++ b/src/debug-arm.cc
@@ -32,7 +32,7 @@
namespace v8 { namespace internal {
-
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Currently debug break is not supported in frame exit code on ARM.
bool BreakLocationIterator::IsDebugBreakAtReturn() {
return false;
@@ -58,7 +58,7 @@
}
-#define __ masm->
+#define __ ACCESS_MASM(masm)
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
@@ -191,5 +191,6 @@
#undef __
+#endif // ENABLE_DEBUGGER_SUPPORT
} } // namespace v8::internal
diff --git a/src/debug-delay.js b/src/debug-delay.js
index 9944fb3..6cb5e7f 100644
--- a/src/debug-delay.js
+++ b/src/debug-delay.js
@@ -1021,6 +1021,9 @@
columnOffset: script.columnOffset(),
lineCount: script.lineCount(),
};
+ if (!IS_UNDEFINED(script.data())) {
+ o.data = script.data();
+ }
if (include_source) {
o.source = script.source();
}
@@ -1270,11 +1273,12 @@
var ignoreCount = request.arguments.ignoreCount;
// Check for legal arguments.
- if (!type || !target) {
+ if (!type || IS_UNDEFINED(target)) {
response.failed('Missing argument "type" or "target"');
return;
}
- if (type != 'function' && type != 'script' && type != 'scriptId') {
+ if (type != 'function' && type != 'handle' &&
+ type != 'script' && type != 'scriptId') {
response.failed('Illegal type "' + type + '"');
return;
}
@@ -1303,6 +1307,20 @@
// Set function break point.
break_point_number = Debug.setBreakPoint(f, line, column, condition);
+ } else if (type == 'handle') {
+ // Find the object pointed by the specified handle.
+ var handle = parseInt(target, 10);
+ var mirror = LookupMirror(handle);
+ if (!mirror) {
+ return response.failed('Object #' + handle + '# not found');
+ }
+ if (!mirror.isFunction()) {
+ return response.failed('Object #' + handle + '# is not a function');
+ }
+
+ // Set function break point.
+ break_point_number = Debug.setBreakPoint(mirror.value(),
+ line, column, condition);
} else if (type == 'script') {
// set script break point.
break_point_number =
@@ -1547,20 +1565,24 @@
}
// Pull out arguments.
- var handle = request.arguments.handle;
+ var handles = request.arguments.handles;
// Check for legal arguments.
- if (IS_UNDEFINED(handle)) {
- return response.failed('Argument "handle" missing');
+ if (IS_UNDEFINED(handles)) {
+ return response.failed('Argument "handles" missing');
}
- // Lookup handle.
- var mirror = LookupMirror(handle);
- if (mirror) {
- response.body = mirror;
- } else {
- return response.failed('Object #' + handle + '# not found');
+ // Lookup handles.
+ var mirrors = {};
+ for (var i = 0; i < handles.length; i++) {
+ var handle = handles[i];
+ var mirror = LookupMirror(handle);
+ if (!mirror) {
+ return response.failed('Object #' + handle + '# not found');
+ }
+ mirrors[handle] = mirror;
}
+ response.body = mirrors;
};
@@ -1675,6 +1697,9 @@
script.lineOffset = scripts[i].line_offset;
script.columnOffset = scripts[i].column_offset;
script.lineCount = scripts[i].lineCount();
+ if (scripts[i].data) {
+ script.data = scripts[i].data;
+ }
if (includeSource) {
script.source = scripts[i].source;
} else {
diff --git a/src/debug-ia32.cc b/src/debug-ia32.cc
index 6d59889..9503cfc 100644
--- a/src/debug-ia32.cc
+++ b/src/debug-ia32.cc
@@ -33,6 +33,7 @@
namespace v8 { namespace internal {
+#ifdef ENABLE_DEBUGGER_SUPPORT
// A debug break in the frame exit code is identified by a call instruction.
bool BreakLocationIterator::IsDebugBreakAtReturn() {
@@ -67,7 +68,7 @@
}
-#define __ masm->
+#define __ ACCESS_MASM(masm)
static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
@@ -214,5 +215,6 @@
#undef __
+#endif // ENABLE_DEBUGGER_SUPPORT
} } // namespace v8::internal
diff --git a/src/debug.cc b/src/debug.cc
index a4bb04d..32a96a8 100644
--- a/src/debug.cc
+++ b/src/debug.cc
@@ -35,12 +35,15 @@
#include "debug.h"
#include "execution.h"
#include "global-handles.h"
+#include "ic.h"
+#include "ic-inl.h"
#include "natives.h"
#include "stub-cache.h"
#include "log.h"
namespace v8 { namespace internal {
+#ifdef ENABLE_DEBUGGER_SUPPORT
static void PrintLn(v8::Local<v8::Value> value) {
v8::Local<v8::String> s = value->ToString();
char* data = NewArray<char>(s->Length() + 1);
@@ -288,14 +291,8 @@
// Patch the frame exit code with a break point.
SetDebugBreakAtReturn();
} else {
- // Patch the original code with the current address as the current address
- // might have changed by the inline caching since the code was copied.
- original_rinfo()->set_target_address(rinfo()->target_address());
-
- // Patch the code to invoke the builtin debug break function matching the
- // calling convention used by the call site.
- Handle<Code> dbgbrk_code(Debug::FindDebugBreak(rinfo()));
- rinfo()->set_target_address(dbgbrk_code->entry());
+ // Patch the IC call.
+ SetDebugBreakAtIC();
}
ASSERT(IsDebugBreak());
}
@@ -306,8 +303,8 @@
// Restore the frame exit code.
ClearDebugBreakAtReturn();
} else {
- // Patch the code to the original invoke.
- rinfo()->set_target_address(original_rinfo()->target_address());
+ // Patch the IC call.
+ ClearDebugBreakAtIC();
}
ASSERT(!IsDebugBreak());
}
@@ -360,6 +357,39 @@
}
+void BreakLocationIterator::SetDebugBreakAtIC() {
+ // Patch the original code with the current address as the current address
+ // might have changed by the inline caching since the code was copied.
+ original_rinfo()->set_target_address(rinfo()->target_address());
+
+ RelocInfo::Mode mode = rmode();
+ if (RelocInfo::IsCodeTarget(mode)) {
+ Address target = rinfo()->target_address();
+ Handle<Code> code(Code::GetCodeFromTargetAddress(target));
+
+ // Patch the code to invoke the builtin debug break function matching the
+ // calling convention used by the call site.
+ Handle<Code> dbgbrk_code(Debug::FindDebugBreak(code, mode));
+ rinfo()->set_target_address(dbgbrk_code->entry());
+
+ // For stubs that refer back to an inlined version clear the cached map for
+ // the inlined case to always go through the IC. As long as the break point
+ // is set the patching performed by the runtime system will take place in
+ // the code copy and will therefore have no effect on the running code
+ // keeping it from using the inlined code.
+ if (code->is_keyed_load_stub() && KeyedLoadIC::HasInlinedVersion(pc())) {
+ KeyedLoadIC::ClearInlinedVersion(pc());
+ }
+ }
+}
+
+
+void BreakLocationIterator::ClearDebugBreakAtIC() {
+ // Patch the code to the original invoke.
+ rinfo()->set_target_address(original_rinfo()->target_address());
+}
+
+
Object* BreakLocationIterator::BreakPointObjects() {
return debug_info_->GetBreakPointObjects(code_position());
}
@@ -1055,48 +1085,42 @@
// Find the builtin to use for invoking the debug break
-Handle<Code> Debug::FindDebugBreak(RelocInfo* rinfo) {
+Handle<Code> Debug::FindDebugBreak(Handle<Code> code, RelocInfo::Mode mode) {
// Find the builtin debug break function matching the calling convention
// used by the call site.
- RelocInfo::Mode mode = rinfo->rmode();
-
- if (RelocInfo::IsCodeTarget(mode)) {
- Address target = rinfo->target_address();
- Code* code = Code::GetCodeFromTargetAddress(target);
- if (code->is_inline_cache_stub()) {
- if (code->is_call_stub()) {
- return ComputeCallDebugBreak(code->arguments_count());
- }
- if (code->is_load_stub()) {
- return Handle<Code>(Builtins::builtin(Builtins::LoadIC_DebugBreak));
- }
- if (code->is_store_stub()) {
- return Handle<Code>(Builtins::builtin(Builtins::StoreIC_DebugBreak));
- }
- if (code->is_keyed_load_stub()) {
- Handle<Code> result =
- Handle<Code>(Builtins::builtin(Builtins::KeyedLoadIC_DebugBreak));
- return result;
- }
- if (code->is_keyed_store_stub()) {
- Handle<Code> result =
- Handle<Code>(Builtins::builtin(Builtins::KeyedStoreIC_DebugBreak));
- return result;
- }
+ if (code->is_inline_cache_stub()) {
+ if (code->is_call_stub()) {
+ return ComputeCallDebugBreak(code->arguments_count());
}
- if (RelocInfo::IsConstructCall(mode)) {
+ if (code->is_load_stub()) {
+ return Handle<Code>(Builtins::builtin(Builtins::LoadIC_DebugBreak));
+ }
+ if (code->is_store_stub()) {
+ return Handle<Code>(Builtins::builtin(Builtins::StoreIC_DebugBreak));
+ }
+ if (code->is_keyed_load_stub()) {
Handle<Code> result =
- Handle<Code>(Builtins::builtin(Builtins::ConstructCall_DebugBreak));
+ Handle<Code>(Builtins::builtin(Builtins::KeyedLoadIC_DebugBreak));
return result;
}
- if (code->kind() == Code::STUB) {
- ASSERT(code->major_key() == CodeStub::CallFunction ||
- code->major_key() == CodeStub::StackCheck);
+ if (code->is_keyed_store_stub()) {
Handle<Code> result =
- Handle<Code>(Builtins::builtin(Builtins::StubNoRegisters_DebugBreak));
+ Handle<Code>(Builtins::builtin(Builtins::KeyedStoreIC_DebugBreak));
return result;
}
}
+ if (RelocInfo::IsConstructCall(mode)) {
+ Handle<Code> result =
+ Handle<Code>(Builtins::builtin(Builtins::ConstructCall_DebugBreak));
+ return result;
+ }
+ if (code->kind() == Code::STUB) {
+ ASSERT(code->major_key() == CodeStub::CallFunction ||
+ code->major_key() == CodeStub::StackCheck);
+ Handle<Code> result =
+ Handle<Code>(Builtins::builtin(Builtins::StubNoRegisters_DebugBreak));
+ return result;
+ }
UNREACHABLE();
return Handle<Code>::null();
@@ -1397,11 +1421,10 @@
bool Debugger::is_loading_debugger_ = false;
bool Debugger::never_unload_debugger_ = false;
DebugMessageThread* Debugger::message_thread_ = NULL;
-v8::DebugMessageHandler Debugger::message_handler_ = NULL;
+v8::Debug::MessageHandler Debugger::message_handler_ = NULL;
bool Debugger::message_handler_cleared_ = false;
-void* Debugger::message_handler_data_ = NULL;
-v8::DebugHostDispatchHandler Debugger::host_dispatch_handler_ = NULL;
-void* Debugger::host_dispatch_handler_data_ = NULL;
+v8::Debug::HostDispatchHandler Debugger::host_dispatch_handler_ = NULL;
+int Debugger::host_dispatch_micros_ = 100 * 1000;
DebuggerAgent* Debugger::agent_ = NULL;
LockingMessageQueue Debugger::command_queue_(kQueueInitialSize);
LockingMessageQueue Debugger::message_queue_(kQueueInitialSize);
@@ -1703,8 +1726,8 @@
if (event_listener_->IsProxy()) {
// C debug event listener.
Handle<Proxy> callback_obj(Handle<Proxy>::cast(event_listener_));
- v8::DebugEventCallback callback =
- FUNCTION_CAST<v8::DebugEventCallback>(callback_obj->proxy());
+ v8::Debug::EventCallback callback =
+ FUNCTION_CAST<v8::Debug::EventCallback>(callback_obj->proxy());
callback(event,
v8::Utils::ToLocal(Handle<JSObject>::cast(exec_state)),
v8::Utils::ToLocal(Handle<JSObject>::cast(event_data)),
@@ -1805,32 +1828,31 @@
// Process requests from the debugger.
while (true) {
// Wait for new command in the queue.
- command_received_->Wait();
+ if (Debugger::host_dispatch_handler_) {
+ // In case there is a host dispatch - do periodic dispatches.
+ if (!command_received_->Wait(host_dispatch_micros_)) {
+ // Timout expired, do the dispatch.
+ Debugger::host_dispatch_handler_();
+ continue;
+ }
+ } else {
+ // In case there is no host dispatch - just wait.
+ command_received_->Wait();
+ }
// The debug command interrupt flag might have been set when the command was
// added.
StackGuard::Continue(DEBUGCOMMAND);
// Get the command from the queue.
- Vector<uint16_t> command = command_queue_.Get();
+ Message command = command_queue_.Get();
Logger::DebugTag("Got request from command queue, in interactive loop.");
if (!Debugger::IsDebuggerActive()) {
+ // Delete command text and user data.
+ command.Dispose();
return;
}
- // Check if the command is a host dispatch.
- if (command[0] == 0) {
- if (Debugger::host_dispatch_handler_) {
- int32_t dispatch = (command[1] << 16) | command[2];
- Debugger::host_dispatch_handler_(reinterpret_cast<void*>(dispatch),
- Debugger::host_dispatch_handler_data_);
- }
- if (auto_continue && !HasCommands()) {
- return;
- }
- continue;
- }
-
// Invoke JavaScript to process the debug request.
v8::Local<v8::String> fun_name;
v8::Local<v8::Function> fun;
@@ -1838,8 +1860,10 @@
v8::TryCatch try_catch;
fun_name = v8::String::New("processDebugRequest");
fun = v8::Function::Cast(*cmd_processor->Get(fun_name));
- request = v8::String::New(reinterpret_cast<uint16_t*>(command.start()),
- command.length());
+
+ request = v8::String::New(command.text().start(),
+ command.text().length());
+ command.text().Dispose();
static const int kArgc = 1;
v8::Handle<Value> argv[kArgc] = { request };
v8::Local<v8::Value> response_val = fun->Call(cmd_processor, kArgc, argv);
@@ -1875,13 +1899,8 @@
response = try_catch.Exception()->ToString();
}
- // Convert text result to C string.
- v8::String::Value val(response);
- Vector<uint16_t> str(reinterpret_cast<uint16_t*>(*val),
- response->Length());
-
// Return the result.
- SendMessage(str);
+ SendMessage(Message::NewOutput(response, command.client_data()));
// Return from debug event processing if either the VM is put into the
// runnning state (through a continue command) or auto continue is active
@@ -1927,12 +1946,11 @@
}
-void Debugger::SetMessageHandler(v8::DebugMessageHandler handler, void* data,
+void Debugger::SetMessageHandler(v8::Debug::MessageHandler handler,
bool message_handler_thread) {
ScopedLock with(debugger_access_);
message_handler_ = handler;
- message_handler_data_ = data;
if (handler != NULL) {
if (!message_thread_ && message_handler_thread) {
message_thread_ = new DebugMessageThread();
@@ -1951,10 +1969,10 @@
}
-void Debugger::SetHostDispatchHandler(v8::DebugHostDispatchHandler handler,
- void* data) {
+void Debugger::SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler,
+ int period) {
host_dispatch_handler_ = handler;
- host_dispatch_handler_data_ = data;
+ host_dispatch_micros_ = period * 1000;
}
@@ -1962,26 +1980,28 @@
// public API. Messages are kept internally as Vector<uint16_t> strings, which
// are allocated in various places and deallocated by the calling function
// sometime after this call.
-void Debugger::InvokeMessageHandler(Vector<uint16_t> message) {
+void Debugger::InvokeMessageHandler(Message message) {
ScopedLock with(debugger_access_);
if (message_handler_ != NULL) {
- message_handler_(message.start(), message.length(), message_handler_data_);
+ message_handler_(message.text().start(),
+ message.text().length(),
+ message.client_data());
}
+ message.Dispose();
}
-void Debugger::SendMessage(Vector<uint16_t> message) {
+void Debugger::SendMessage(Message message) {
if (message_thread_ == NULL) {
// If there is no message thread just invoke the message handler from the
// V8 thread.
InvokeMessageHandler(message);
} else {
- // Put a copy of the message coming from V8 on the queue. The new copy of
- // the event string is destroyed by the message thread.
- Vector<uint16_t> message_copy = message.Clone();
+ // Put the message coming from V8 on the queue. The text and user data will
+ // be destroyed by the message thread.
Logger::DebugTag("Put message on event message_queue.");
- message_queue_.Put(message_copy);
+ message_queue_.Put(message);
message_received_->Signal();
}
}
@@ -2004,12 +2024,11 @@
if (FLAG_trace_debug_json) {
PrintLn(json_event_string);
}
- v8::String::Value val(json_event_string);
- Vector<uint16_t> str(reinterpret_cast<uint16_t*>(*val),
- json_event_string->Length());
- SendMessage(str);
+ SendMessage(Message::NewOutput(
+ json_event_string,
+ NULL /* no user data since there was no request */));
} else {
- SendMessage(Vector<uint16_t>::empty());
+ SendMessage(Message::NewEmptyMessage());
}
} else {
PrintLn(try_catch.Exception());
@@ -2025,13 +2044,15 @@
// by the API client thread. This is where the API client hands off
// processing of the command to the DebugMessageThread thread.
// The new copy of the command is destroyed in HandleCommand().
-void Debugger::ProcessCommand(Vector<const uint16_t> command) {
- // Make a copy of the command. Need to cast away const for Clone to work.
- Vector<uint16_t> command_copy =
+void Debugger::ProcessCommand(Vector<const uint16_t> command,
+ v8::Debug::ClientData* client_data) {
+ // Need to cast away const.
+ Message message = Message::NewCommand(
Vector<uint16_t>(const_cast<uint16_t*>(command.start()),
- command.length()).Clone();
+ command.length()),
+ client_data);
Logger::DebugTag("Put command on command_queue.");
- command_queue_.Put(command_copy);
+ command_queue_.Put(message);
command_received_->Signal();
// Set the debug command break flag to have the command processed.
@@ -2046,23 +2067,6 @@
}
-void Debugger::ProcessHostDispatch(void* dispatch) {
- // Puts a host dispatch comming from the public API on the queue.
- uint16_t hack[3];
- hack[0] = 0;
- hack[1] = reinterpret_cast<uint32_t>(dispatch) >> 16;
- hack[2] = reinterpret_cast<uint32_t>(dispatch) & 0xFFFF;
- Logger::DebugTag("Put dispatch on command_queue.");
- command_queue_.Put(Vector<uint16_t>(hack, 3).Clone());
- command_received_->Signal();
-
- // Set the debug command break flag to have the host dispatch processed.
- if (!Debug::InDebugger()) {
- StackGuard::DebugCommand();
- }
-}
-
-
bool Debugger::IsDebuggerActive() {
ScopedLock with(debugger_access_);
@@ -2133,9 +2137,11 @@
// Wait and Get are paired so that semaphore count equals queue length.
Debugger::message_received_->Wait();
Logger::DebugTag("Get message from event message_queue.");
- Vector<uint16_t> message = Debugger::message_queue_.Get();
- if (message.length() > 0) {
+ Message message = Debugger::message_queue_.Get();
+ if (message.text().length() > 0) {
Debugger::InvokeMessageHandler(message);
+ } else {
+ message.Dispose();
}
}
}
@@ -2143,22 +2149,72 @@
void DebugMessageThread::Stop() {
keep_running_ = false;
- Debugger::SendMessage(Vector<uint16_t>(NULL, 0));
+ Debugger::SendMessage(Message::NewEmptyMessage());
Join();
}
+Message::Message() : text_(Vector<uint16_t>::empty()),
+ client_data_(NULL) {
+}
+
+
+Message::Message(const Vector<uint16_t>& text,
+ v8::Debug::ClientData* data)
+ : text_(text),
+ client_data_(data) {
+}
+
+
+Message::~Message() {
+}
+
+
+void Message::Dispose() {
+ text_.Dispose();
+ delete client_data_;
+ client_data_ = NULL;
+}
+
+
+Message Message::NewCommand(const Vector<uint16_t>& command,
+ v8::Debug::ClientData* data) {
+ return Message(command.Clone(), data);
+}
+
+
+Message Message::NewOutput(v8::Handle<v8::String> output,
+ v8::Debug::ClientData* data) {
+ Vector<uint16_t> text;
+ if (!output.IsEmpty()) {
+ // Do not include trailing '\0'.
+ text = Vector<uint16_t>::New(output->Length());
+ output->Write(text.start(), 0, output->Length());
+ }
+ return Message(text, data);
+}
+
+
+Message Message::NewEmptyMessage() {
+ return Message();
+}
+
+
MessageQueue::MessageQueue(int size) : start_(0), end_(0), size_(size) {
- messages_ = NewArray<Vector<uint16_t> >(size);
+ messages_ = NewArray<Message>(size);
}
MessageQueue::~MessageQueue() {
+ while (!IsEmpty()) {
+ Message m = Get();
+ m.Dispose();
+ }
DeleteArray(messages_);
}
-Vector<uint16_t> MessageQueue::Get() {
+Message MessageQueue::Get() {
ASSERT(!IsEmpty());
int result = start_;
start_ = (start_ + 1) % size_;
@@ -2166,7 +2222,7 @@
}
-void MessageQueue::Put(const Vector<uint16_t>& message) {
+void MessageQueue::Put(const Message& message) {
if ((end_ + 1) % size_ == start_) {
Expand();
}
@@ -2180,9 +2236,11 @@
while (!IsEmpty()) {
new_queue.Put(Get());
}
- Vector<uint16_t>* array_to_free = messages_;
+ Message* array_to_free = messages_;
*this = new_queue;
new_queue.messages_ = array_to_free;
+ // Make the new_queue empty so that it doesn't call Dispose on any messages.
+ new_queue.start_ = new_queue.end_;
// Automatic destructor called on new_queue, freeing array_to_free.
}
@@ -2203,18 +2261,18 @@
}
-Vector<uint16_t> LockingMessageQueue::Get() {
+Message LockingMessageQueue::Get() {
ScopedLock sl(lock_);
- Vector<uint16_t> result = queue_.Get();
- Logger::DebugEvent("Get", result);
+ Message result = queue_.Get();
+ Logger::DebugEvent("Get", result.text());
return result;
}
-void LockingMessageQueue::Put(const Vector<uint16_t>& message) {
+void LockingMessageQueue::Put(const Message& message) {
ScopedLock sl(lock_);
queue_.Put(message);
- Logger::DebugEvent("Put", message);
+ Logger::DebugEvent("Put", message.text());
}
@@ -2223,5 +2281,6 @@
queue_.Clear();
}
+#endif // ENABLE_DEBUGGER_SUPPORT
} } // namespace v8::internal
diff --git a/src/debug.h b/src/debug.h
index 8822c50..ffd3da9 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -28,7 +28,6 @@
#ifndef V8_V8_DEBUG_H_
#define V8_V8_DEBUG_H_
-#include "../include/v8-debug.h"
#include "assembler.h"
#include "code-stubs.h"
#include "debug-agent.h"
@@ -38,6 +37,8 @@
#include "string-stream.h"
#include "v8threads.h"
+#ifdef ENABLE_DEBUGGER_SUPPORT
+#include "../include/v8-debug.h"
namespace v8 { namespace internal {
@@ -131,6 +132,10 @@
private:
void SetDebugBreak();
void ClearDebugBreak();
+
+ void SetDebugBreakAtIC();
+ void ClearDebugBreakAtIC();
+
bool IsDebugBreakAtReturn();
void SetDebugBreakAtReturn();
void ClearDebugBreakAtReturn();
@@ -204,7 +209,7 @@
static bool IsBreakStub(Code* code);
// Find the builtin to use for invoking the debug break
- static Handle<Code> FindDebugBreak(RelocInfo* rinfo);
+ static Handle<Code> FindDebugBreak(Handle<Code> code, RelocInfo::Mode mode);
static Handle<Object> GetSourceBreakLocations(
Handle<SharedFunctionInfo> shared);
@@ -396,6 +401,32 @@
};
+// Message send by user to v8 debugger or debugger output message.
+// In addition to command text it may contain a pointer to some user data
+// which are expected to be passed along with the command reponse to message
+// handler.
+class Message {
+ public:
+ static Message NewCommand(const Vector<uint16_t>& command,
+ v8::Debug::ClientData* data);
+ static Message NewOutput(v8::Handle<v8::String> output,
+ v8::Debug::ClientData* data);
+ static Message NewEmptyMessage();
+ Message();
+ ~Message();
+
+ // Deletes user data and disposes of the text.
+ void Dispose();
+ Vector<uint16_t> text() const { return text_; }
+ v8::Debug::ClientData* client_data() const { return client_data_; }
+ private:
+ Message(const Vector<uint16_t>& text,
+ v8::Debug::ClientData* data);
+
+ Vector<uint16_t> text_;
+ v8::Debug::ClientData* client_data_;
+};
+
// A Queue of Vector<uint16_t> objects. A thread-safe version is
// LockingMessageQueue, based on this class.
class MessageQueue BASE_EMBEDDED {
@@ -403,14 +434,14 @@
explicit MessageQueue(int size);
~MessageQueue();
bool IsEmpty() const { return start_ == end_; }
- Vector<uint16_t> Get();
- void Put(const Vector<uint16_t>& message);
+ Message Get();
+ void Put(const Message& message);
void Clear() { start_ = end_ = 0; } // Queue is empty after Clear().
private:
// Doubles the size of the message queue, and copies the messages.
void Expand();
- Vector<uint16_t>* messages_;
+ Message* messages_;
int start_;
int end_;
int size_; // The size of the queue buffer. Queue can hold size-1 messages.
@@ -426,8 +457,8 @@
explicit LockingMessageQueue(int size);
~LockingMessageQueue();
bool IsEmpty() const;
- Vector<uint16_t> Get();
- void Put(const Vector<uint16_t>& message);
+ Message Get();
+ void Put(const Message& message);
void Clear();
private:
MessageQueue queue_;
@@ -472,29 +503,29 @@
Handle<Object> event_data,
bool auto_continue);
static void SetEventListener(Handle<Object> callback, Handle<Object> data);
- static void SetMessageHandler(v8::DebugMessageHandler handler, void* data,
+ static void SetMessageHandler(v8::Debug::MessageHandler handler,
bool message_handler_thread);
static void TearDown();
- static void SetHostDispatchHandler(v8::DebugHostDispatchHandler handler,
- void* data);
+ static void SetHostDispatchHandler(v8::Debug::HostDispatchHandler handler,
+ int period);
// Invoke the message handler function.
- static void InvokeMessageHandler(Vector< uint16_t> message);
+ static void InvokeMessageHandler(Message message);
// Send a message to the message handler eiher through the message thread or
// directly.
- static void SendMessage(Vector<uint16_t> message);
+ static void SendMessage(Message message);
// Send the JSON message for a debug event.
static bool SendEventMessage(Handle<Object> event_data);
// Add a debugger command to the command queue.
- static void ProcessCommand(Vector<const uint16_t> command);
+ static void ProcessCommand(Vector<const uint16_t> command,
+ v8::Debug::ClientData* client_data = NULL);
// Check whether there are commands in the command queue.
static bool HasCommands();
- static void ProcessHostDispatch(void* dispatch);
static Handle<Object> Call(Handle<JSFunction> fun,
Handle<Object> data,
bool* pending_exception);
@@ -538,11 +569,10 @@
static bool is_loading_debugger_; // Are we loading the debugger?
static bool never_unload_debugger_; // Can we unload the debugger?
static DebugMessageThread* message_thread_;
- static v8::DebugMessageHandler message_handler_;
+ static v8::Debug::MessageHandler message_handler_;
static bool message_handler_cleared_; // Was message handler cleared?
- static void* message_handler_data_;
- static v8::DebugHostDispatchHandler host_dispatch_handler_;
- static void* host_dispatch_handler_data_;
+ static v8::Debug::HostDispatchHandler host_dispatch_handler_;
+ static int host_dispatch_micros_;
static DebuggerAgent* agent_;
@@ -719,4 +749,6 @@
} } // namespace v8::internal
+#endif // ENABLE_DEBUGGER_SUPPORT
+
#endif // V8_V8_DEBUG_H_
diff --git a/src/disasm-arm.cc b/src/disasm-arm.cc
index d19e042..3b7474d 100644
--- a/src/disasm-arm.cc
+++ b/src/disasm-arm.cc
@@ -261,6 +261,15 @@
case break_point:
Print("break_point");
return;
+ case simulator_fp_add:
+ Print("simulator_fp_add");
+ return;
+ case simulator_fp_mul:
+ Print("simulator_fp_mul");
+ return;
+ case simulator_fp_sub:
+ Print("simulator_fp_sub");
+ return;
default:
out_buffer_pos_ += v8i::OS::SNPrintF(out_buffer_ + out_buffer_pos_,
"%d",
diff --git a/src/execution.cc b/src/execution.cc
index 03017d0..1d4d9ef 100644
--- a/src/execution.cc
+++ b/src/execution.cc
@@ -305,6 +305,7 @@
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
bool StackGuard::IsDebugBreak() {
ExecutionAccess access;
return thread_local_.interrupt_flags_ & DEBUGBREAK;
@@ -331,7 +332,7 @@
set_limits(kInterruptLimit, access);
}
}
-
+#endif
void StackGuard::Continue(InterruptFlag after_what) {
ExecutionAccess access;
@@ -539,6 +540,7 @@
ContextSwitcher::PreemptionReceived();
+#ifdef ENABLE_DEBUGGER_SUPPORT
if (Debug::InDebugger()) {
// If currently in the debugger don't do any actual preemption but record
// that preemption occoured while in the debugger.
@@ -548,11 +550,17 @@
v8::Unlocker unlocker;
Thread::YieldCPU();
}
+#else
+ // Perform preemption.
+ v8::Unlocker unlocker;
+ Thread::YieldCPU();
+#endif
return Heap::undefined_value();
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
Object* Execution::DebugBreakHelper() {
// Just continue if breaks are disabled.
if (Debug::disable_break()) {
@@ -598,12 +606,14 @@
// Return to continue execution.
return Heap::undefined_value();
}
-
+#endif
Object* Execution::HandleStackGuardInterrupt() {
+#ifdef ENABLE_DEBUGGER_SUPPORT
if (StackGuard::IsDebugBreak() || StackGuard::IsDebugCommand()) {
DebugBreakHelper();
}
+#endif
if (StackGuard::IsPreempted()) RuntimePreempt();
if (StackGuard::IsInterrupted()) {
// interrupt
@@ -626,7 +636,7 @@
v8::Handle<v8::Value> GCExtension::GC(const v8::Arguments& args) {
// All allocation spaces other than NEW_SPACE have the same effect.
- Heap::CollectGarbage(0, OLD_DATA_SPACE);
+ Heap::CollectAllGarbage();
return v8::Undefined();
}
diff --git a/src/execution.h b/src/execution.h
index 6531572..6f2f689 100644
--- a/src/execution.h
+++ b/src/execution.h
@@ -118,8 +118,9 @@
Handle<JSFunction> fun,
Handle<Object> pos,
Handle<Object> is_global);
-
+#ifdef ENABLE_DEBUGGER_SUPPORT
static Object* DebugBreakHelper();
+#endif
// If the stack guard is triggered, but it is not an actual
// stack overflow, then handle the interruption accordingly.
@@ -158,11 +159,13 @@
static void Preempt();
static bool IsInterrupted();
static void Interrupt();
- static bool IsDebugBreak();
- static void DebugBreak();
- static bool IsDebugCommand();
- static void DebugCommand();
static void Continue(InterruptFlag after_what);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ static void DebugBreak();
+ static void DebugCommand();
+ static bool IsDebugBreak();
+ static bool IsDebugCommand();
+#endif
private:
// You should hold the ExecutionAccess lock when calling this method.
diff --git a/src/factory.cc b/src/factory.cc
index e29c84d..c849ab7 100644
--- a/src/factory.cc
+++ b/src/factory.cc
@@ -173,6 +173,7 @@
script->set_id(Heap::last_script_id());
script->set_line_offset(Smi::FromInt(0));
script->set_column_offset(Smi::FromInt(0));
+ script->set_data(Heap::undefined_value());
script->set_type(Smi::FromInt(SCRIPT_TYPE_NORMAL));
script->set_wrapper(*Factory::NewProxy(0, TENURED));
script->set_line_ends(Heap::undefined_value());
@@ -671,6 +672,7 @@
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
Handle<DebugInfo> Factory::NewDebugInfo(Handle<SharedFunctionInfo> shared) {
// Get the original code of the function.
Handle<Code> code(shared->code());
@@ -700,6 +702,7 @@
return debug_info;
}
+#endif
Handle<JSObject> Factory::NewArgumentsObject(Handle<Object> callee,
diff --git a/src/factory.h b/src/factory.h
index 2564c3c..54f2089 100644
--- a/src/factory.h
+++ b/src/factory.h
@@ -310,8 +310,9 @@
uint32_t key,
Handle<Object> value);
+#ifdef ENABLE_DEBUGGER_SUPPORT
static Handle<DebugInfo> NewDebugInfo(Handle<SharedFunctionInfo> shared);
-
+#endif
// Return a map using the map cache in the global context.
// The key the an ordered set of property names.
diff --git a/src/frames.cc b/src/frames.cc
index 88c723d..1eedbf6 100644
--- a/src/frames.cc
+++ b/src/frames.cc
@@ -647,10 +647,10 @@
handler->Iterate(v);
// Make sure that there's the entry frame does not contain more than
// one stack handler.
- if (kDebug) {
- it.Advance();
- ASSERT(it.done());
- }
+#ifdef DEBUG
+ it.Advance();
+ ASSERT(it.done());
+#endif
}
diff --git a/src/func-name-inferrer.cc b/src/func-name-inferrer.cc
index ef0c7db..75f7a99 100644
--- a/src/func-name-inferrer.cc
+++ b/src/func-name-inferrer.cc
@@ -63,11 +63,12 @@
}
-void FuncNameInferrer::MaybeInferFunctionName() {
- if (func_to_infer_ != NULL) {
- func_to_infer_->set_inferred_name(MakeNameFromStack());
- func_to_infer_ = NULL;
+void FuncNameInferrer::InferFunctionsNames() {
+ Handle<String> func_name = MakeNameFromStack();
+ for (int i = 0; i < funcs_to_infer_.length(); ++i) {
+ funcs_to_infer_[i]->set_inferred_name(func_name);
}
+ funcs_to_infer_.Rewind(0);
}
diff --git a/src/func-name-inferrer.h b/src/func-name-inferrer.h
index 9dcf7c5..d8270c3 100644
--- a/src/func-name-inferrer.h
+++ b/src/func-name-inferrer.h
@@ -45,7 +45,7 @@
FuncNameInferrer() :
entries_stack_(10),
names_stack_(5),
- func_to_infer_(NULL),
+ funcs_to_infer_(4),
dot_(Factory::NewStringFromAscii(CStrVector("."))) {
}
@@ -57,39 +57,34 @@
entries_stack_.Add(names_stack_.length());
}
- void Leave() {
- ASSERT(IsOpen());
- names_stack_.Rewind(entries_stack_.RemoveLast());
- }
-
void PushName(Handle<String> name) {
if (IsOpen()) {
names_stack_.Add(name);
}
}
- void SetFuncToInfer(FunctionLiteral* func_to_infer) {
+ void AddFunction(FunctionLiteral* func_to_infer) {
if (IsOpen()) {
- // If we encounter another function literal after already having
- // encountered one, the second one replaces the first.
- func_to_infer_ = func_to_infer;
+ funcs_to_infer_.Add(func_to_infer);
}
}
void InferAndLeave() {
ASSERT(IsOpen());
- MaybeInferFunctionName();
- Leave();
+ if (!funcs_to_infer_.is_empty()) {
+ InferFunctionsNames();
+ }
+ names_stack_.Rewind(entries_stack_.RemoveLast());
}
private:
Handle<String> MakeNameFromStack();
Handle<String> MakeNameFromStackHelper(int pos, Handle<String> prev);
- void MaybeInferFunctionName();
+ void InferFunctionsNames();
List<int> entries_stack_;
List<Handle<String> > names_stack_;
- FunctionLiteral* func_to_infer_;
+ List<FunctionLiteral*> funcs_to_infer_;
Handle<String> dot_;
DISALLOW_COPY_AND_ASSIGN(FuncNameInferrer);
diff --git a/src/globals.h b/src/globals.h
index 1579c3d..fbbe7c9 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -80,12 +80,6 @@
// -----------------------------------------------------------------------------
// Constants
-#ifdef DEBUG
-const bool kDebug = true;
-#else
-const bool kDebug = false;
-#endif // DEBUG
-
const int KB = 1024;
const int MB = KB * KB;
const int GB = KB * KB * KB;
@@ -146,7 +140,6 @@
class BreakableStatement;
class Code;
class CodeGenerator;
-class CodeRegion;
class CodeStub;
class Context;
class Debug;
diff --git a/src/handles.cc b/src/handles.cc
index 60d8236..99161ce 100644
--- a/src/handles.cc
+++ b/src/handles.cc
@@ -212,10 +212,20 @@
}
-Handle<Object> IgnoreAttributesAndSetLocalProperty(Handle<JSObject> object,
- Handle<String> key,
- Handle<Object> value,
- PropertyAttributes attributes) {
+Handle<Object> ForceSetProperty(Handle<JSObject> object,
+ Handle<Object> key,
+ Handle<Object> value,
+ PropertyAttributes attributes) {
+ CALL_HEAP_FUNCTION(
+ Runtime::ForceSetObjectProperty(object, key, value, attributes), Object);
+}
+
+
+Handle<Object> IgnoreAttributesAndSetLocalProperty(
+ Handle<JSObject> object,
+ Handle<String> key,
+ Handle<Object> value,
+ PropertyAttributes attributes) {
CALL_HEAP_FUNCTION(object->
IgnoreAttributesAndSetLocalProperty(*key, *value, attributes), Object);
}
@@ -491,17 +501,6 @@
break;
}
- // Compute the property keys.
- content = UnionOfKeys(content, GetEnumPropertyKeys(current));
-
- // Add the property keys from the interceptor.
- if (current->HasNamedInterceptor()) {
- v8::Handle<v8::Array> result =
- GetKeysForNamedInterceptor(object, current);
- if (!result.IsEmpty())
- content = AddKeysFromJSArray(content, v8::Utils::OpenHandle(*result));
- }
-
// Compute the element keys.
Handle<FixedArray> element_keys =
Factory::NewFixedArray(current->NumberOfEnumElements());
@@ -515,6 +514,17 @@
if (!result.IsEmpty())
content = AddKeysFromJSArray(content, v8::Utils::OpenHandle(*result));
}
+
+ // Compute the property keys.
+ content = UnionOfKeys(content, GetEnumPropertyKeys(current));
+
+ // Add the property keys from the interceptor.
+ if (current->HasNamedInterceptor()) {
+ v8::Handle<v8::Array> result =
+ GetKeysForNamedInterceptor(object, current);
+ if (!result.IsEmpty())
+ content = AddKeysFromJSArray(content, v8::Utils::OpenHandle(*result));
+ }
}
}
return content;
@@ -549,7 +559,7 @@
index++;
}
}
- (*storage)->SortPairs(*sort_array);
+ (*storage)->SortPairs(*sort_array, sort_array->length());
Handle<FixedArray> bridge_storage =
Factory::NewFixedArray(DescriptorArray::kEnumCacheBridgeLength);
DescriptorArray* desc = object->map()->instance_descriptors();
@@ -651,6 +661,7 @@
// We shouldn't get here if compiling the script failed.
ASSERT(!boilerplate.is_null());
+#ifdef ENABLE_DEBUGGER_SUPPORT
// When the debugger running in its own context touches lazy loaded
// functions loading can be triggered. In that case ensure that the
// execution of the boilerplate is in the correct context.
@@ -659,6 +670,7 @@
Top::context() == *Debug::debug_context()) {
Top::set_context(*compile_context);
}
+#endif
// Reset the lazy load data before running the script to make sure
// not to get recursive lazy loading.
diff --git a/src/handles.h b/src/handles.h
index c8e534e..9cc1db4 100644
--- a/src/handles.h
+++ b/src/handles.h
@@ -196,6 +196,11 @@
Handle<Object> value,
PropertyAttributes attributes);
+Handle<Object> ForceSetProperty(Handle<JSObject> object,
+ Handle<Object> key,
+ Handle<Object> value,
+ PropertyAttributes attributes);
+
Handle<Object> IgnoreAttributesAndSetLocalProperty(Handle<JSObject> object,
Handle<String> key,
Handle<Object> value,
diff --git a/src/heap.cc b/src/heap.cc
index 7a8b728..a57884c 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -2679,7 +2679,10 @@
SYNCHRONIZE_TAG("bootstrapper");
Top::Iterate(v);
SYNCHRONIZE_TAG("top");
+
+#ifdef ENABLE_DEBUGGER_SUPPORT
Debug::Iterate(v);
+#endif
SYNCHRONIZE_TAG("debug");
CompilationCache::Iterate(v);
SYNCHRONIZE_TAG("compilationcache");
diff --git a/src/ic-arm.cc b/src/ic-arm.cc
index 4db3980..ad6eb2c 100644
--- a/src/ic-arm.cc
+++ b/src/ic-arm.cc
@@ -39,7 +39,7 @@
// Static IC stub generators.
//
-#define __ masm->
+#define __ ACCESS_MASM(masm)
// Helper function used from LoadIC/CallIC GenerateNormal.
@@ -96,7 +96,9 @@
// Compute the masked index: (hash + i + i * i) & mask.
__ ldr(t1, FieldMemOperand(r2, String::kLengthOffset));
__ mov(t1, Operand(t1, LSR, String::kHashShift));
- if (i > 0) __ add(t1, t1, Operand(Dictionary::GetProbeOffset(i)));
+ if (i > 0) {
+ __ add(t1, t1, Operand(Dictionary::GetProbeOffset(i)));
+ }
__ and_(t1, t1, Operand(r3));
// Scale the index by multiplying by the element size.
@@ -505,6 +507,8 @@
// TODO(181): Implement map patching once loop nesting is tracked on
// the ARM platform so we can generate inlined fast-case code for
// array indexing in loops.
+bool KeyedLoadIC::HasInlinedVersion(Address address) { return false; }
+void KeyedLoadIC::ClearInlinedVersion(Address address) { }
void KeyedLoadIC::PatchInlinedMapCheck(Address address, Object* value) { }
diff --git a/src/ic-ia32.cc b/src/ic-ia32.cc
index 9060f2d..664303f 100644
--- a/src/ic-ia32.cc
+++ b/src/ic-ia32.cc
@@ -38,7 +38,7 @@
// Static IC stub generators.
//
-#define __ masm->
+#define __ ACCESS_MASM(masm)
// Helper function used to load a property from a dictionary backing storage.
@@ -91,7 +91,9 @@
// Compute the masked index: (hash + i + i * i) & mask.
__ mov(r1, FieldOperand(name, String::kLengthOffset));
__ shr(r1, String::kHashShift);
- if (i > 0) __ add(Operand(r1), Immediate(Dictionary::GetProbeOffset(i)));
+ if (i > 0) {
+ __ add(Operand(r1), Immediate(Dictionary::GetProbeOffset(i)));
+ }
__ and_(r1, Operand(r2));
// Scale the index by multiplying by the element size.
@@ -727,8 +729,24 @@
}
+// One byte opcode for test eax,0xXXXXXXXX.
+static const byte kTestEaxByte = 0xA9;
+
+
+bool KeyedLoadIC::HasInlinedVersion(Address address) {
+ Address test_instruction_address = address + 4; // 4 = stub address
+ return *test_instruction_address == kTestEaxByte;
+}
+
+
+void KeyedLoadIC::ClearInlinedVersion(Address address) {
+ // Insert null as the map to check for to make sure the map check fails
+ // sending control flow to the IC instead of the inlined version.
+ PatchInlinedMapCheck(address, Heap::null_value());
+}
+
+
void KeyedLoadIC::PatchInlinedMapCheck(Address address, Object* value) {
- static const byte kTestEaxByte = 0xA9;
Address test_instruction_address = address + 4; // 4 = stub address
// The keyed load has a fast inlined case if the IC call instruction
// is immediately followed by a test instruction.
@@ -742,7 +760,7 @@
// bytes of the 7-byte operand-immediate compare instruction, so
// we add 3 to the offset to get the map address.
Address map_address = test_instruction_address + offset_value + 3;
- // patch the map check.
+ // Patch the map check.
(*(reinterpret_cast<Object**>(map_address))) = value;
}
}
diff --git a/src/ic-inl.h b/src/ic-inl.h
index f5ce0ad..bb56962 100644
--- a/src/ic-inl.h
+++ b/src/ic-inl.h
@@ -39,6 +39,7 @@
// Get the address of the call.
Address result = pc() - Assembler::kTargetAddrToReturnAddrDist;
+#ifdef ENABLE_DEBUGGER_SUPPORT
// First check if any break points are active if not just return the address
// of the call.
if (!Debug::has_break_points()) return result;
@@ -55,6 +56,9 @@
// No break point here just return the address of the call.
return result;
}
+#else
+ return result;
+#endif
}
diff --git a/src/ic.cc b/src/ic.cc
index d7bd764..51768d7 100644
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -100,6 +100,7 @@
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
Address IC::OriginalCodeAddress() {
HandleScope scope;
// Compute the JavaScript frame for the frame pointer of this IC
@@ -126,7 +127,7 @@
int delta = original_code->instruction_start() - code->instruction_start();
return addr + delta;
}
-
+#endif
IC::State IC::StateFrom(Code* target, Object* receiver) {
IC::State state = target->ic_state();
@@ -236,7 +237,7 @@
// Make sure to also clear the map used in inline fast cases. If we
// do not clear these maps, cached code can keep objects alive
// through the embedded maps.
- PatchInlinedMapCheck(address, Heap::null_value());
+ ClearInlinedVersion(address);
SetTargetAtAddress(address, initialize_stub());
}
@@ -356,6 +357,7 @@
if (opt->IsJSFunction()) return opt;
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Handle stepping into a function if step into is active.
if (Debug::StepInActive()) {
// Protect the result in a handle as the debugger can allocate and might
@@ -365,6 +367,7 @@
Debug::HandleStepIn(function, fp(), false);
return *function;
}
+#endif
return result;
}
diff --git a/src/ic.h b/src/ic.h
index bbe1f6d..11d47ae 100644
--- a/src/ic.h
+++ b/src/ic.h
@@ -107,9 +107,11 @@
Address fp() const { return fp_; }
Address pc() const { return *pc_address_; }
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Computes the address in the original code when the code running is
// containing break points (calls to DebugBreakXXX builtins).
Address OriginalCodeAddress();
+#endif
// Set the call-site target.
void set_target(Code* code) { SetTargetAtAddress(address(), code); }
@@ -252,6 +254,12 @@
static void GeneratePreMonomorphic(MacroAssembler* masm);
static void GenerateGeneric(MacroAssembler* masm);
+ // Check if this IC corresponds to an inlined version.
+ static bool HasInlinedVersion(Address address);
+
+ // Clear the use of the inlined version.
+ static void ClearInlinedVersion(Address address);
+
private:
static void Generate(MacroAssembler* masm, const ExternalReference& f);
diff --git a/src/jump-target-arm.cc b/src/jump-target-arm.cc
index 3ce5f30..6d375e5 100644
--- a/src/jump-target-arm.cc
+++ b/src/jump-target-arm.cc
@@ -35,7 +35,7 @@
// -------------------------------------------------------------------------
// JumpTarget implementation.
-#define __ masm_->
+#define __ ACCESS_MASM(masm_)
void JumpTarget::DoJump() {
ASSERT(cgen_ != NULL);
diff --git a/src/jump-target-ia32.cc b/src/jump-target-ia32.cc
index 8afb0a8..a1049b2 100644
--- a/src/jump-target-ia32.cc
+++ b/src/jump-target-ia32.cc
@@ -35,7 +35,7 @@
// -------------------------------------------------------------------------
// JumpTarget implementation.
-#define __ masm_->
+#define __ ACCESS_MASM(masm_)
void JumpTarget::DoJump() {
ASSERT(cgen_ != NULL);
diff --git a/src/log.cc b/src/log.cc
index d9e304d..f23b73b 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -814,35 +814,6 @@
}
-void Logger::BeginCodeRegionEvent(CodeRegion* region,
- Assembler* masm,
- const char* name) {
-#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_code) return;
- LogMessageBuilder msg;
- msg.Append("begin-code-region,0x%x,0x%x,0x%x,%s\n",
- reinterpret_cast<unsigned int>(region),
- reinterpret_cast<unsigned int>(masm),
- masm->pc_offset(),
- name);
- msg.WriteToLogFile();
-#endif
-}
-
-
-void Logger::EndCodeRegionEvent(CodeRegion* region, Assembler* masm) {
-#ifdef ENABLE_LOGGING_AND_PROFILING
- if (logfile_ == NULL || !FLAG_log_code) return;
- LogMessageBuilder msg;
- msg.Append("end-code-region,0x%x,0x%x,0x%x\n",
- reinterpret_cast<unsigned int>(region),
- reinterpret_cast<unsigned int>(masm),
- masm->pc_offset());
- msg.WriteToLogFile();
-#endif
-}
-
-
void Logger::ResourceEvent(const char* name, const char* tag) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (logfile_ == NULL || !FLAG_log) return;
diff --git a/src/log.h b/src/log.h
index bbcfa42..44c1957 100644
--- a/src/log.h
+++ b/src/log.h
@@ -174,11 +174,6 @@
static void CodeMoveEvent(Address from, Address to);
// Emits a code delete event.
static void CodeDeleteEvent(Address from);
- // Emits region delimiters
- static void BeginCodeRegionEvent(CodeRegion* region,
- Assembler* masm,
- const char* name);
- static void EndCodeRegionEvent(CodeRegion* region, Assembler* masm);
// ==== Events logged by --log-gc. ====
// Heap sampling events: start, end, and individual types.
diff --git a/src/macro-assembler-arm.cc b/src/macro-assembler-arm.cc
index 88a300b..365c1ad 100644
--- a/src/macro-assembler-arm.cc
+++ b/src/macro-assembler-arm.cc
@@ -168,11 +168,11 @@
}
-void MacroAssembler::Ret() {
+void MacroAssembler::Ret(Condition cond) {
#if USE_BX
- bx(lr);
+ bx(lr, cond);
#else
- mov(pc, Operand(lr));
+ mov(pc, Operand(lr), LeaveCC, cond);
#endif
}
@@ -320,16 +320,19 @@
add(r6, fp, Operand(r4, LSL, kPointerSizeLog2));
add(r6, r6, Operand(ExitFrameConstants::kPPDisplacement - kPointerSize));
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Save the state of all registers to the stack from the memory
// location. This is needed to allow nested break points.
if (type == StackFrame::EXIT_DEBUG) {
// Use sp as base to push.
CopyRegistersFromMemoryToStack(sp, kJSCallerSaved);
}
+#endif
}
void MacroAssembler::LeaveExitFrame(StackFrame::Type type) {
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Restore the memory copy of the registers by digging them out from
// the stack. This is needed to allow nested break points.
if (type == StackFrame::EXIT_DEBUG) {
@@ -339,6 +342,7 @@
add(r3, fp, Operand(kOffset));
CopyRegistersFromStackToMemory(r3, r2, kJSCallerSaved);
}
+#endif
// Clear top frame.
mov(r3, Operand(0));
@@ -348,9 +352,9 @@
// Restore current context from top and clear it in debug mode.
mov(ip, Operand(ExternalReference(Top::k_context_address)));
ldr(cp, MemOperand(ip));
- if (kDebug) {
- str(r3, MemOperand(ip));
- }
+#ifdef DEBUG
+ str(r3, MemOperand(ip));
+#endif
// Pop the arguments, restore registers, and return.
mov(sp, Operand(fp)); // respect ABI stack constraint
@@ -491,6 +495,7 @@
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::SaveRegistersToMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Copy the content of registers to memory location.
@@ -548,7 +553,7 @@
}
}
}
-
+#endif
void MacroAssembler::PushTryHandler(CodeLocation try_location,
HandlerType type) {
@@ -674,10 +679,10 @@
// Load current lexical context from the stack frame.
ldr(scratch, MemOperand(fp, StandardFrameConstants::kContextOffset));
// In debug mode, make sure the lexical context is set.
- if (kDebug) {
- cmp(scratch, Operand(0));
- Check(ne, "we should not have an empty lexical context");
- }
+#ifdef DEBUG
+ cmp(scratch, Operand(0));
+ Check(ne, "we should not have an empty lexical context");
+#endif
// Load the global context of the current context.
int offset = Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
diff --git a/src/macro-assembler-arm.h b/src/macro-assembler-arm.h
index 4b999fd..d3835be 100644
--- a/src/macro-assembler-arm.h
+++ b/src/macro-assembler-arm.h
@@ -86,7 +86,7 @@
void Call(Register target, Condition cond = al);
void Call(byte* target, RelocInfo::Mode rmode, Condition cond = al);
void Call(Handle<Code> code, RelocInfo::Mode rmode, Condition cond = al);
- void Ret();
+ void Ret(Condition cond = al);
// Jumps to the label at the index given by the Smi in "index".
void SmiJumpTable(Register index, Vector<Label*> targets);
@@ -138,6 +138,7 @@
InvokeFlag flag);
+#ifdef ENABLE_DEBUGGER_SUPPORT
// ---------------------------------------------------------------------------
// Debugger Support
@@ -147,7 +148,7 @@
void CopyRegistersFromStackToMemory(Register base,
Register scratch,
RegList regs);
-
+#endif
// ---------------------------------------------------------------------------
// Exception handling
@@ -297,6 +298,15 @@
}
+#ifdef GENERATED_CODE_COVERAGE
+#define CODE_COVERAGE_STRINGIFY(x) #x
+#define CODE_COVERAGE_TOSTRING(x) CODE_COVERAGE_STRINGIFY(x)
+#define __FILE_LINE__ __FILE__ ":" CODE_COVERAGE_TOSTRING(__LINE__)
+#define ACCESS_MASM(masm) masm->stop(__FILE_LINE__); masm->
+#else
+#define ACCESS_MASM(masm) masm->
+#endif
+
} } // namespace v8::internal
diff --git a/src/macro-assembler-ia32.cc b/src/macro-assembler-ia32.cc
index 4fad3be..d6d5800 100644
--- a/src/macro-assembler-ia32.cc
+++ b/src/macro-assembler-ia32.cc
@@ -216,6 +216,7 @@
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::SaveRegistersToMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
// Copy the content of registers to memory location.
@@ -290,7 +291,7 @@
}
}
}
-
+#endif
void MacroAssembler::Set(Register dst, const Immediate& x) {
if (x.is_zero()) {
@@ -378,6 +379,7 @@
mov(edi, Operand(eax));
lea(esi, Operand(ebp, eax, times_4, offset));
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Save the state of all registers to the stack from the memory
// location. This is needed to allow nested break points.
if (type == StackFrame::EXIT_DEBUG) {
@@ -389,6 +391,7 @@
// associated with this issue).
PushRegistersFromMemory(kJSCallerSaved);
}
+#endif
// Reserve space for two arguments: argc and argv.
sub(Operand(esp), Immediate(2 * kPointerSize));
@@ -406,6 +409,7 @@
void MacroAssembler::LeaveExitFrame(StackFrame::Type type) {
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Restore the memory copy of the registers by digging them out from
// the stack. This is needed to allow nested break points.
if (type == StackFrame::EXIT_DEBUG) {
@@ -416,6 +420,7 @@
lea(ebx, Operand(ebp, kOffset));
CopyRegistersFromStackToMemory(ebx, ecx, kJSCallerSaved);
}
+#endif
// Get the return address from the stack and restore the frame pointer.
mov(ecx, Operand(ebp, 1 * kPointerSize));
@@ -427,9 +432,9 @@
// Restore current context from top and clear it in debug mode.
ExternalReference context_address(Top::k_context_address);
mov(esi, Operand::StaticVariable(context_address));
- if (kDebug) {
- mov(Operand::StaticVariable(context_address), Immediate(0));
- }
+#ifdef DEBUG
+ mov(Operand::StaticVariable(context_address), Immediate(0));
+#endif
// Push the return address to get ready to return.
push(ecx);
diff --git a/src/macro-assembler-ia32.h b/src/macro-assembler-ia32.h
index 40aa84a..ae3a853 100644
--- a/src/macro-assembler-ia32.h
+++ b/src/macro-assembler-ia32.h
@@ -73,7 +73,7 @@
Register value,
Register scratch);
-
+#ifdef ENABLE_DEBUGGER_SUPPORT
// ---------------------------------------------------------------------------
// Debugger Support
@@ -84,7 +84,7 @@
void CopyRegistersFromStackToMemory(Register base,
Register scratch,
RegList regs);
-
+#endif
// ---------------------------------------------------------------------------
// Activation frames
@@ -343,6 +343,29 @@
return Operand(object, index, scale, offset - kHeapObjectTag);
}
+
+#ifdef GENERATED_CODE_COVERAGE
+extern void LogGeneratedCodeCoverage(const char* file_line);
+#define CODE_COVERAGE_STRINGIFY(x) #x
+#define CODE_COVERAGE_TOSTRING(x) CODE_COVERAGE_STRINGIFY(x)
+#define __FILE_LINE__ __FILE__ ":" CODE_COVERAGE_TOSTRING(__LINE__)
+#define ACCESS_MASM(masm) { \
+ byte* ia32_coverage_function = \
+ reinterpret_cast<byte*>(FUNCTION_ADDR(LogGeneratedCodeCoverage)); \
+ masm->pushfd(); \
+ masm->pushad(); \
+ masm->push(Immediate(reinterpret_cast<int>(&__FILE_LINE__))); \
+ masm->call(ia32_coverage_function, RelocInfo::RUNTIME_ENTRY); \
+ masm->pop(eax); \
+ masm->popad(); \
+ masm->popfd(); \
+ } \
+ masm->
+#else
+#define ACCESS_MASM(masm) masm->
+#endif
+
+
} } // namespace v8::internal
#endif // V8_MACRO_ASSEMBLER_IA32_H_
diff --git a/src/messages.js b/src/messages.js
index cd9a1e8..fa6fb1f 100644
--- a/src/messages.js
+++ b/src/messages.js
@@ -98,6 +98,7 @@
instanceof_function_expected: "Expecting a function in instanceof check, but got %0",
instanceof_nonobject_proto: "Function has non-object prototype '%0' in instanceof check",
null_to_object: "Cannot convert null to object",
+ reduce_no_initial: "Reduce of empty array with no initial value",
// RangeError
invalid_array_length: "Invalid array length",
invalid_array_apply_length: "Function.prototype.apply supports only up to 1024 arguments",
diff --git a/src/mirror-delay.js b/src/mirror-delay.js
index 9c9d713..dc4d7eb 100644
--- a/src/mirror-delay.js
+++ b/src/mirror-delay.js
@@ -1582,6 +1582,11 @@
};
+ScriptMirror.prototype.data = function() {
+ return this.script_.data;
+};
+
+
ScriptMirror.prototype.scriptType = function() {
return this.script_.type;
};
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index f40fd3e..635ef0f 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -924,7 +924,11 @@
VerifyPointer(name());
line_offset()->SmiVerify();
column_offset()->SmiVerify();
+ VerifyPointer(data());
+ VerifyPointer(wrapper());
type()->SmiVerify();
+ VerifyPointer(line_ends());
+ VerifyPointer(id());
}
diff --git a/src/objects-inl.h b/src/objects-inl.h
index c2143ea..73b9c84 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -2053,10 +2053,12 @@
ACCESSORS(Script, id, Object, kIdOffset)
ACCESSORS(Script, line_offset, Smi, kLineOffsetOffset)
ACCESSORS(Script, column_offset, Smi, kColumnOffsetOffset)
+ACCESSORS(Script, data, Object, kDataOffset)
ACCESSORS(Script, wrapper, Proxy, kWrapperOffset)
ACCESSORS(Script, type, Smi, kTypeOffset)
ACCESSORS(Script, line_ends, Object, kLineEndsOffset)
+#ifdef ENABLE_DEBUGGER_SUPPORT
ACCESSORS(DebugInfo, shared, SharedFunctionInfo, kSharedFunctionInfoIndex)
ACCESSORS(DebugInfo, original_code, Code, kOriginalCodeIndex)
ACCESSORS(DebugInfo, code, Code, kPatchedCodeIndex)
@@ -2066,6 +2068,7 @@
ACCESSORS(BreakPointInfo, source_position, Smi, kSourcePositionIndex)
ACCESSORS(BreakPointInfo, statement_position, Smi, kStatementPositionIndex)
ACCESSORS(BreakPointInfo, break_point_objects, Object, kBreakPointObjectsIndex)
+#endif
ACCESSORS(SharedFunctionInfo, name, Object, kNameOffset)
ACCESSORS(SharedFunctionInfo, instance_class_name, Object,
diff --git a/src/objects.cc b/src/objects.cc
index 9c640f4..31c5bab 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -43,22 +43,6 @@
namespace v8 { namespace internal {
-#define FIELD_ADDR(p, offset) \
- (reinterpret_cast<byte*>(p) + offset - kHeapObjectTag)
-
-
-#define WRITE_FIELD(p, offset, value) \
- (*reinterpret_cast<Object**>(FIELD_ADDR(p, offset)) = value)
-
-
-#define WRITE_INT_FIELD(p, offset, value) \
- (*reinterpret_cast<int*>(FIELD_ADDR(p, offset)) = value)
-
-
-#define WRITE_BARRIER(object, offset) \
- Heap::RecordWrite(object->address(), offset);
-
-
// Getters and setters are stored in a fixed array property. These are
// constants for their indices.
const int kGetterIndex = 0;
@@ -1800,6 +1784,17 @@
&& !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
return SetPropertyWithFailedAccessCheck(result, name, value);
}
+
+ if (IsJSGlobalProxy()) {
+ Object* proto = GetPrototype();
+ if (proto->IsNull()) return value;
+ ASSERT(proto->IsJSGlobalObject());
+ return JSObject::cast(proto)->IgnoreAttributesAndSetLocalProperty(
+ name,
+ value,
+ attributes);
+ }
+
// Check for accessor in prototype chain removed here in clone.
if (result->IsNotFound()) {
return AddProperty(name, value, attributes);
@@ -1820,20 +1815,16 @@
return AddFastPropertyUsingMap(result->GetTransitionMap(),
name,
value);
- } else {
- return ConvertDescriptorToField(name, value, attributes);
}
+ return ConvertDescriptorToField(name, value, attributes);
case CONSTANT_FUNCTION:
if (value == result->GetConstantFunction()) return value;
// Only replace the function if necessary.
return ConvertDescriptorToFieldAndMapTransition(name, value, attributes);
case CALLBACKS:
- return SetPropertyWithCallback(result->GetCallbackObject(),
- name,
- value,
- result->holder());
case INTERCEPTOR:
- return SetPropertyWithInterceptor(name, value, attributes);
+ // Override callback in clone
+ return ConvertDescriptorToField(name, value, attributes);
case CONSTANT_TRANSITION:
// Replace with a MAP_TRANSITION to a new map with a FIELD, even
// if the value is a function.
@@ -4678,6 +4669,7 @@
it.rinfo()->set_target_object(code);
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
if (Debug::has_break_points()) {
for (RelocIterator it(this, RelocInfo::ModeMask(RelocInfo::JS_RETURN));
!it.done();
@@ -4691,6 +4683,7 @@
}
}
}
+#endif
set_ic_flag(IC_TARGET_IS_OBJECT);
}
@@ -4712,10 +4705,12 @@
v->VisitCodeTarget(it.rinfo());
} else if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
v->VisitExternalReference(it.rinfo()->target_reference_address());
+#ifdef ENABLE_DEBUGGER_SUPPORT
} else if (Debug::has_break_points() &&
RelocInfo::IsJSReturn(rmode) &&
it.rinfo()->IsCallInstruction()) {
v->VisitDebugTarget(it.rinfo());
+#endif
} else if (rmode == RelocInfo::RUNTIME_ENTRY) {
v->VisitRuntimeEntry(it.rinfo());
}
@@ -4740,6 +4735,7 @@
it.rinfo()->set_target_address(code->instruction_start());
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
if (Debug::has_break_points()) {
for (RelocIterator it(this, RelocInfo::ModeMask(RelocInfo::JS_RETURN));
!it.done();
@@ -4751,6 +4747,7 @@
}
}
}
+#endif
set_ic_flag(IC_TARGET_IS_ADDRESS);
}
@@ -5138,7 +5135,7 @@
VMState state(EXTERNAL);
result = getter(index, info);
}
- if (!result.IsEmpty()) return !result->IsUndefined();
+ if (!result.IsEmpty()) return true;
}
return holder_handle->HasElementPostInterceptor(*receiver_handle, index);
}
@@ -5861,43 +5858,46 @@
}
-void FixedArray::Swap(int i, int j) {
+void FixedArray::SwapPairs(FixedArray* numbers, int i, int j) {
Object* temp = get(i);
set(i, get(j));
set(j, temp);
+ if (this != numbers) {
+ temp = numbers->get(i);
+ numbers->set(i, numbers->get(j));
+ numbers->set(j, temp);
+ }
}
-static void InsertionSortPairs(FixedArray* content, FixedArray* smis) {
- int len = smis->length();
+static void InsertionSortPairs(FixedArray* content,
+ FixedArray* numbers,
+ int len) {
for (int i = 1; i < len; i++) {
int j = i;
while (j > 0 &&
- Smi::cast(smis->get(j-1))->value() >
- Smi::cast(smis->get(j))->value()) {
- smis->Swap(j-1, j);
- content->Swap(j-1, j);
+ (NumberToUint32(numbers->get(j - 1)) >
+ NumberToUint32(numbers->get(j)))) {
+ content->SwapPairs(numbers, j - 1, j);
j--;
}
}
}
-void HeapSortPairs(FixedArray* content, FixedArray* smis) {
+void HeapSortPairs(FixedArray* content, FixedArray* numbers, int len) {
// In-place heap sort.
- ASSERT(content->length() == smis->length());
- int len = smis->length();
+ ASSERT(content->length() == numbers->length());
// Bottom-up max-heap construction.
for (int i = 1; i < len; ++i) {
int child_index = i;
while (child_index > 0) {
int parent_index = ((child_index + 1) >> 1) - 1;
- int parent_value = Smi::cast(smis->get(parent_index))->value();
- int child_value = Smi::cast(smis->get(child_index))->value();
+ uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
+ uint32_t child_value = NumberToUint32(numbers->get(child_index));
if (parent_value < child_value) {
- content->Swap(parent_index, child_index);
- smis->Swap(parent_index, child_index);
+ content->SwapPairs(numbers, parent_index, child_index);
} else {
break;
}
@@ -5908,25 +5908,22 @@
// Extract elements and create sorted array.
for (int i = len - 1; i > 0; --i) {
// Put max element at the back of the array.
- content->Swap(0, i);
- smis->Swap(0, i);
+ content->SwapPairs(numbers, 0, i);
// Sift down the new top element.
int parent_index = 0;
while (true) {
int child_index = ((parent_index + 1) << 1) - 1;
if (child_index >= i) break;
- uint32_t child1_value = Smi::cast(smis->get(child_index))->value();
- uint32_t child2_value = Smi::cast(smis->get(child_index + 1))->value();
- uint32_t parent_value = Smi::cast(smis->get(parent_index))->value();
+ uint32_t child1_value = NumberToUint32(numbers->get(child_index));
+ uint32_t child2_value = NumberToUint32(numbers->get(child_index + 1));
+ uint32_t parent_value = NumberToUint32(numbers->get(parent_index));
if (child_index + 1 >= i || child1_value > child2_value) {
if (parent_value > child1_value) break;
- content->Swap(parent_index, child_index);
- smis->Swap(parent_index, child_index);
+ content->SwapPairs(numbers, parent_index, child_index);
parent_index = child_index;
} else {
if (parent_value > child2_value) break;
- content->Swap(parent_index, child_index + 1);
- smis->Swap(parent_index, child_index + 1);
+ content->SwapPairs(numbers, parent_index, child_index + 1);
parent_index = child_index + 1;
}
}
@@ -5934,43 +5931,41 @@
}
-// Sort this array and the smis as pairs wrt. the (distinct) smis.
-void FixedArray::SortPairs(FixedArray* smis) {
- ASSERT(this->length() == smis->length());
- int len = smis->length();
+// Sort this array and the numbers as pairs wrt. the (distinct) numbers.
+void FixedArray::SortPairs(FixedArray* numbers, uint32_t len) {
+ ASSERT(this->length() == numbers->length());
// For small arrays, simply use insertion sort.
if (len <= 10) {
- InsertionSortPairs(this, smis);
+ InsertionSortPairs(this, numbers, len);
return;
}
// Check the range of indices.
- int min_index = Smi::cast(smis->get(0))->value();
- int max_index = min_index;
- int i;
+ uint32_t min_index = NumberToUint32(numbers->get(0));
+ uint32_t max_index = min_index;
+ uint32_t i;
for (i = 1; i < len; i++) {
- if (Smi::cast(smis->get(i))->value() < min_index) {
- min_index = Smi::cast(smis->get(i))->value();
- } else if (Smi::cast(smis->get(i))->value() > max_index) {
- max_index = Smi::cast(smis->get(i))->value();
+ if (NumberToUint32(numbers->get(i)) < min_index) {
+ min_index = NumberToUint32(numbers->get(i));
+ } else if (NumberToUint32(numbers->get(i)) > max_index) {
+ max_index = NumberToUint32(numbers->get(i));
}
}
if (max_index - min_index + 1 == len) {
// Indices form a contiguous range, unless there are duplicates.
- // Do an in-place linear time sort assuming distinct smis, but
+ // Do an in-place linear time sort assuming distinct numbers, but
// avoid hanging in case they are not.
for (i = 0; i < len; i++) {
- int p;
- int j = 0;
+ uint32_t p;
+ uint32_t j = 0;
// While the current element at i is not at its correct position p,
// swap the elements at these two positions.
- while ((p = Smi::cast(smis->get(i))->value() - min_index) != i &&
+ while ((p = NumberToUint32(numbers->get(i)) - min_index) != i &&
j++ < len) {
- this->Swap(i, p);
- smis->Swap(i, p);
+ SwapPairs(numbers, i, p);
}
}
} else {
- HeapSortPairs(this, smis);
+ HeapSortPairs(this, numbers, len);
return;
}
}
@@ -6758,7 +6753,7 @@
}
// Sort the arrays wrt. enumeration order.
- iteration_order->SortPairs(enumeration_order);
+ iteration_order->SortPairs(enumeration_order, enumeration_order->length());
// Overwrite the enumeration_order with the enumeration indices.
for (int i = 0; i < length; i++) {
@@ -7010,6 +7005,7 @@
if ((attr & filter) == 0) storage->set(index++, k);
}
}
+ storage->SortPairs(storage, index);
ASSERT(storage->length() >= index);
}
@@ -7031,7 +7027,7 @@
}
}
}
- storage->SortPairs(sort_array);
+ storage->SortPairs(sort_array, sort_array->length());
ASSERT(storage->length() >= index);
}
@@ -7176,6 +7172,7 @@
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Check if there is a break point at this code position.
bool DebugInfo::HasBreakPoint(int code_position) {
// Get the break point info object for this code position.
@@ -7419,6 +7416,6 @@
// Multiple break points.
return FixedArray::cast(break_point_objects())->length();
}
-
+#endif
} } // namespace v8::internal
diff --git a/src/objects.h b/src/objects.h
index ade282b..db3c449 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -389,7 +389,7 @@
// Note that for subtle reasons related to the ordering or numerical values of
// type tags, elements in this list have to be added to the INSTANCE_TYPE_LIST
// manually.
-#define STRUCT_LIST(V) \
+#define STRUCT_LIST_ALL(V) \
V(ACCESSOR_INFO, AccessorInfo, accessor_info) \
V(ACCESS_CHECK_INFO, AccessCheckInfo, access_check_info) \
V(INTERCEPTOR_INFO, InterceptorInfo, interceptor_info) \
@@ -398,10 +398,19 @@
V(OBJECT_TEMPLATE_INFO, ObjectTemplateInfo, object_template_info) \
V(SIGNATURE_INFO, SignatureInfo, signature_info) \
V(TYPE_SWITCH_INFO, TypeSwitchInfo, type_switch_info) \
- V(DEBUG_INFO, DebugInfo, debug_info) \
- V(BREAK_POINT_INFO, BreakPointInfo, break_point_info) \
V(SCRIPT, Script, script)
+#ifdef ENABLE_DEBUGGER_SUPPORT
+#define STRUCT_LIST_DEBUGGER(V) \
+ V(DEBUG_INFO, DebugInfo, debug_info) \
+ V(BREAK_POINT_INFO, BreakPointInfo, break_point_info)
+#else
+#define STRUCT_LIST_DEBUGGER(V)
+#endif
+
+#define STRUCT_LIST(V) \
+ STRUCT_LIST_ALL(V) \
+ STRUCT_LIST_DEBUGGER(V)
// We use the full 8 bits of the instance_type field to encode heap object
// instance types. The high-order bit (bit 7) is set if the object is not a
@@ -1585,11 +1594,15 @@
bool IsEqualTo(FixedArray* other);
#endif
- // Swap two elements.
- void Swap(int i, int j);
+ // Swap two elements in a pair of arrays. If this array and the
+ // numbers array are the same object, the elements are only swapped
+ // once.
+ void SwapPairs(FixedArray* numbers, int i, int j);
- // Sort this array and the smis as pairs wrt. the smis.
- void SortPairs(FixedArray* smis);
+ // Sort prefix of this array and the numbers array as pairs wrt. the
+ // numbers. If the numbers array and the this array are the same
+ // object, the prefix of this array is sorted.
+ void SortPairs(FixedArray* numbers, uint32_t len);
protected:
// Set operation on FixedArray without using write barriers.
@@ -2590,6 +2603,9 @@
// extracted.
DECL_ACCESSORS(column_offset, Smi)
+ // [data]: additional data associated with this script.
+ DECL_ACCESSORS(data, Object)
+
// [wrapper]: the wrapper cache.
DECL_ACCESSORS(wrapper, Proxy)
@@ -2610,7 +2626,8 @@
static const int kNameOffset = kSourceOffset + kPointerSize;
static const int kLineOffsetOffset = kNameOffset + kPointerSize;
static const int kColumnOffsetOffset = kLineOffsetOffset + kPointerSize;
- static const int kWrapperOffset = kColumnOffsetOffset + kPointerSize;
+ static const int kDataOffset = kColumnOffsetOffset + kPointerSize;
+ static const int kWrapperOffset = kDataOffset + kPointerSize;
static const int kTypeOffset = kWrapperOffset + kPointerSize;
static const int kLineEndsOffset = kTypeOffset + kPointerSize;
static const int kIdOffset = kLineEndsOffset + kPointerSize;
@@ -4147,6 +4164,7 @@
};
+#ifdef ENABLE_DEBUGGER_SUPPORT
// The DebugInfo class holds additional information for a function being
// debugged.
class DebugInfo: public Struct {
@@ -4252,6 +4270,7 @@
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(BreakPointInfo);
};
+#endif // ENABLE_DEBUGGER_SUPPORT
#undef DECL_BOOLEAN_ACCESSORS
diff --git a/src/platform-freebsd.cc b/src/platform-freebsd.cc
index 9209990..1e71704 100644
--- a/src/platform-freebsd.cc
+++ b/src/platform-freebsd.cc
@@ -262,7 +262,8 @@
}
-int OS::StackWalk(OS::StackFrame* frames, int frames_size) {
+int OS::StackWalk(Vector<OS::StackFrame> frames) {
+ int frames_size = frames.length();
void** addresses = NewArray<void*>(frames_size);
int frames_count = backtrace(addresses, frames_size);
@@ -502,27 +503,24 @@
bool FreeBSDSemaphore::Wait(int timeout) {
const long kOneSecondMicros = 1000000; // NOLINT
- const long kOneSecondNanos = 1000000000; // NOLINT
// Split timeout into second and nanosecond parts.
- long nanos = (timeout % kOneSecondMicros) * 1000; // NOLINT
- time_t secs = timeout / kOneSecondMicros;
+ struct timeval delta;
+ delta.tv_usec = timeout % kOneSecondMicros;
+ delta.tv_sec = timeout / kOneSecondMicros;
- // Get the current real time clock.
- struct timespec ts;
- if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
+ struct timeval current_time;
+ // Get the current time.
+ if (gettimeofday(¤t_time, NULL) == -1) {
return false;
}
- // Calculate realtime for end of timeout.
- ts.tv_nsec += nanos;
- if (ts.tv_nsec >= kOneSecondNanos) {
- ts.tv_nsec -= kOneSecondNanos;
- ts.tv_nsec++;
- }
- ts.tv_sec += secs;
+ // Calculate time for end of timeout.
+ struct timeval end_time;
+ timeradd(¤t_time, &delta, &end_time);
- // Wait for semaphore signalled or timeout.
+ struct timespec ts;
+ TIMEVAL_TO_TIMESPEC(&end_time, &ts);
while (true) {
int result = sem_timedwait(&sem_, &ts);
if (result == 0) return true; // Successfully got semaphore.
diff --git a/src/platform-linux.cc b/src/platform-linux.cc
index f9a5dea..9f5b9ac 100644
--- a/src/platform-linux.cc
+++ b/src/platform-linux.cc
@@ -262,9 +262,10 @@
}
-int OS::StackWalk(OS::StackFrame* frames, int frames_size) {
+int OS::StackWalk(Vector<OS::StackFrame> frames) {
// backtrace is a glibc extension.
#ifdef __GLIBC__
+ int frames_size = frames.length();
void** addresses = NewArray<void*>(frames_size);
int frames_count = backtrace(addresses, frames_size);
@@ -506,28 +507,34 @@
}
+#ifndef TIMEVAL_TO_TIMESPEC
+#define TIMEVAL_TO_TIMESPEC(tv, ts) do { \
+ (ts)->tv_sec = (tv)->tv_sec; \
+ (ts)->tv_nsec = (tv)->tv_usec * 1000; \
+} while (false)
+#endif
+
+
bool LinuxSemaphore::Wait(int timeout) {
const long kOneSecondMicros = 1000000; // NOLINT
- const long kOneSecondNanos = 1000000000; // NOLINT
// Split timeout into second and nanosecond parts.
- long nanos = (timeout % kOneSecondMicros) * 1000; // NOLINT
- time_t secs = timeout / kOneSecondMicros;
+ struct timeval delta;
+ delta.tv_usec = timeout % kOneSecondMicros;
+ delta.tv_sec = timeout / kOneSecondMicros;
- // Get the current realtime clock.
- struct timespec ts;
- if (clock_gettime(CLOCK_REALTIME, &ts) == -1) {
+ struct timeval current_time;
+ // Get the current time.
+ if (gettimeofday(¤t_time, NULL) == -1) {
return false;
}
- // Calculate real time for end of timeout.
- ts.tv_nsec += nanos;
- if (ts.tv_nsec >= kOneSecondNanos) {
- ts.tv_nsec -= kOneSecondNanos;
- ts.tv_nsec++;
- }
- ts.tv_sec += secs;
+ // Calculate time for end of timeout.
+ struct timeval end_time;
+ timeradd(¤t_time, &delta, &end_time);
+ struct timespec ts;
+ TIMEVAL_TO_TIMESPEC(&end_time, &ts);
// Wait for semaphore signalled or timeout.
while (true) {
int result = sem_timedwait(&sem_, &ts);
diff --git a/src/platform-macos.cc b/src/platform-macos.cc
index b13122f..7951543 100644
--- a/src/platform-macos.cc
+++ b/src/platform-macos.cc
@@ -212,10 +212,11 @@
}
-int OS::StackWalk(StackFrame* frames, int frames_size) {
+int OS::StackWalk(Vector<StackFrame> frames) {
#ifndef MAC_OS_X_VERSION_10_5
return 0;
#else
+ int frames_size = frames.length();
void** addresses = NewArray<void*>(frames_size);
int frames_count = backtrace(addresses, frames_size);
diff --git a/src/platform-nullos.cc b/src/platform-nullos.cc
index 3a50b9d..42583f1 100644
--- a/src/platform-nullos.cc
+++ b/src/platform-nullos.cc
@@ -215,7 +215,7 @@
}
-int OS::StackWalk(OS::StackFrame* frames, int frames_size) {
+int OS::StackWalk(Vector<OS::StackFrame> frames) {
UNIMPLEMENTED();
return 0;
}
diff --git a/src/platform-win32.cc b/src/platform-win32.cc
index 7177870..597a217 100644
--- a/src/platform-win32.cc
+++ b/src/platform-win32.cc
@@ -1161,7 +1161,7 @@
// it is triggered by the use of inline assembler.
#pragma warning(push)
#pragma warning(disable : 4748)
-int OS::StackWalk(OS::StackFrame* frames, int frames_size) {
+int OS::StackWalk(Vector<OS::StackFrame> frames) {
BOOL ok;
// Load the required functions from DLL's.
@@ -1201,6 +1201,7 @@
int frames_count = 0;
// Collect stack frames.
+ int frames_size = frames.length();
while (frames_count < frames_size) {
ok = _StackWalk64(
IMAGE_FILE_MACHINE_I386, // MachineType
@@ -1284,7 +1285,7 @@
#else // __MINGW32__
void OS::LogSharedLibraryAddresses() { }
-int OS::StackWalk(OS::StackFrame* frames, int frames_size) { return 0; }
+int OS::StackWalk(Vector<OS::StackFrame> frames) { return 0; }
#endif // __MINGW32__
diff --git a/src/platform.h b/src/platform.h
index db4f2fd..b70095b 100644
--- a/src/platform.h
+++ b/src/platform.h
@@ -207,7 +207,7 @@
char text[kStackWalkMaxTextLen];
};
- static int StackWalk(StackFrame* frames, int frames_size);
+ static int StackWalk(Vector<StackFrame> frames);
// Factory method for creating platform dependent Mutex.
// Please use delete to reclaim the storage for the returned Mutex.
diff --git a/src/regexp-macro-assembler-ia32.cc b/src/regexp-macro-assembler-ia32.cc
index c2f76b4..8c92091 100644
--- a/src/regexp-macro-assembler-ia32.cc
+++ b/src/regexp-macro-assembler-ia32.cc
@@ -86,7 +86,7 @@
* byte* stack_area_top)
*/
-#define __ masm_->
+#define __ ACCESS_MASM(masm_)
RegExpMacroAssemblerIA32::RegExpMacroAssemblerIA32(
Mode mode,
diff --git a/src/rewriter.cc b/src/rewriter.cc
index 6641f26..4e3676b 100644
--- a/src/rewriter.cc
+++ b/src/rewriter.cc
@@ -194,7 +194,7 @@
if (node->name()->length() == 0) {
// Anonymous function.
- func_name_inferrer_.SetFuncToInfer(node);
+ func_name_inferrer_.AddFunction(node);
}
}
@@ -282,10 +282,7 @@
case Token::ASSIGN:
// No type can be infered from the general assignment.
- if (node->value()->AsFunctionLiteral() != NULL ||
- node->value()->AsObjectLiteral() != NULL) {
- scoped_fni.Enter();
- }
+ scoped_fni.Enter();
break;
case Token::ASSIGN_BIT_OR:
case Token::ASSIGN_BIT_XOR:
diff --git a/src/runtime.cc b/src/runtime.cc
index ab25c9e..2b350a5 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -2688,6 +2688,59 @@
}
+Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
+ Handle<Object> key,
+ Handle<Object> value,
+ PropertyAttributes attr) {
+ HandleScope scope;
+
+ // Check if the given key is an array index.
+ uint32_t index;
+ if (Array::IndexFromObject(*key, &index)) {
+ ASSERT(attr == NONE);
+
+ // In Firefox/SpiderMonkey, Safari and Opera you can access the characters
+ // of a string using [] notation. We need to support this too in
+ // JavaScript.
+ // In the case of a String object we just need to redirect the assignment to
+ // the underlying string if the index is in range. Since the underlying
+ // string does nothing with the assignment then we can ignore such
+ // assignments.
+ if (js_object->IsStringObjectWithCharacterAt(index)) {
+ return *value;
+ }
+
+ return js_object->SetElement(index, *value);
+ }
+
+ if (key->IsString()) {
+ if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
+ ASSERT(attr == NONE);
+ return js_object->SetElement(index, *value);
+ } else {
+ Handle<String> key_string = Handle<String>::cast(key);
+ key_string->TryFlattenIfNotFlat();
+ return js_object->IgnoreAttributesAndSetLocalProperty(*key_string,
+ *value,
+ attr);
+ }
+ }
+
+ // Call-back into JavaScript to convert the key to a string.
+ bool has_pending_exception = false;
+ Handle<Object> converted = Execution::ToString(key, &has_pending_exception);
+ if (has_pending_exception) return Failure::Exception();
+ Handle<String> name = Handle<String>::cast(converted);
+
+ if (name->AsArrayIndex(&index)) {
+ ASSERT(attr == NONE);
+ return js_object->SetElement(index, *value);
+ } else {
+ return js_object->IgnoreAttributesAndSetLocalProperty(*name, *value, attr);
+ }
+}
+
+
static Object* Runtime_SetProperty(Arguments args) {
NoHandleAllocation ha;
RUNTIME_ASSERT(args.length() == 3 || args.length() == 4);
@@ -3482,6 +3535,7 @@
return Heap::nan_value();
}
+
static Object* Runtime_NumberAdd(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
@@ -4158,10 +4212,12 @@
JSFunction* function = JSFunction::cast(constructor);
// Handle stepping into constructors if step into is active.
+#ifdef ENABLE_DEBUGGER_SUPPORT
if (Debug::StepInActive()) {
HandleScope scope;
Debug::HandleStepIn(Handle<JSFunction>(function), 0, true);
}
+#endif
if (function->has_initial_map() &&
function->initial_map()->instance_type() == JS_FUNCTION_TYPE) {
@@ -4525,12 +4581,6 @@
}
-static Object* Runtime_DebugBreak(Arguments args) {
- ASSERT(args.length() == 0);
- return Execution::DebugBreakHelper();
-}
-
-
static Object* Runtime_StackGuard(Arguments args) {
ASSERT(args.length() == 1);
@@ -4892,26 +4942,6 @@
}
-static Object* Runtime_CompileScript(Arguments args) {
- HandleScope scope;
- ASSERT(args.length() == 4);
-
- CONVERT_ARG_CHECKED(String, source, 0);
- CONVERT_ARG_CHECKED(String, script, 1);
- CONVERT_CHECKED(Smi, line_attrs, args[2]);
- int line = line_attrs->value();
- CONVERT_CHECKED(Smi, col_attrs, args[3]);
- int col = col_attrs->value();
- Handle<JSFunction> boilerplate =
- Compiler::Compile(source, script, line, col, NULL, NULL);
- if (boilerplate.is_null()) return Failure::Exception();
- Handle<JSFunction> fun =
- Factory::NewFunctionFromBoilerplate(boilerplate,
- Handle<Context>(Top::context()));
- return *fun;
-}
-
-
static Object* Runtime_SetNewFunctionAttributes(Arguments args) {
// This utility adjusts the property attributes for newly created Function
// object ("new Function(...)") by changing the map.
@@ -5316,6 +5346,13 @@
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
+static Object* Runtime_DebugBreak(Arguments args) {
+ ASSERT(args.length() == 0);
+ return Execution::DebugBreakHelper();
+}
+
+
// Helper functions for wrapping and unwrapping stack frame ids.
static Smi* WrapFrameId(StackFrame::Id id) {
ASSERT(IsAligned(OffsetFrom(id), 4));
@@ -5928,8 +5965,8 @@
if (result->IsFailure()) return result;
static const int kMaxCFramesSize = 200;
- OS::StackFrame frames[kMaxCFramesSize];
- int frames_count = OS::StackWalk(frames, kMaxCFramesSize);
+ ScopedVector<OS::StackFrame> frames(kMaxCFramesSize);
+ int frames_count = OS::StackWalk(frames);
if (frames_count == OS::kStackWalkError) {
return Heap::undefined_value();
}
@@ -6796,6 +6833,22 @@
}
+static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
+#ifdef DEBUG
+ HandleScope scope;
+ ASSERT(args.length() == 1);
+ // Get the function and make sure it is compiled.
+ CONVERT_ARG_CHECKED(JSFunction, func, 0);
+ if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
+ return Failure::Exception();
+ }
+ func->code()->PrintLn();
+#endif // DEBUG
+ return Heap::undefined_value();
+}
+#endif // ENABLE_DEBUGGER_SUPPORT
+
+
// Finds the script object from the script data. NOTE: This operation uses
// heap traversal to find the function generated for the source position
// for the requested break point. For lazily compiled functions several heap
@@ -6845,21 +6898,6 @@
}
-static Object* Runtime_FunctionGetAssemblerCode(Arguments args) {
-#ifdef DEBUG
- HandleScope scope;
- ASSERT(args.length() == 1);
- // Get the function and make sure it is compiled.
- CONVERT_ARG_CHECKED(JSFunction, func, 0);
- if (!func->is_compiled() && !CompileLazy(func, KEEP_EXCEPTION)) {
- return Failure::Exception();
- }
- func->code()->PrintLn();
-#endif // DEBUG
- return Heap::undefined_value();
-}
-
-
static Object* Runtime_Abort(Arguments args) {
ASSERT(args.length() == 2);
OS::PrintError("abort: %s\n", reinterpret_cast<char*>(args[0]) +
diff --git a/src/runtime.h b/src/runtime.h
index 657e5c5..6bd19f6 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -195,7 +195,6 @@
\
/* Globals */ \
F(CompileString, 2) \
- F(CompileScript, 4) \
F(GlobalPrint, 1) \
\
/* Eval */ \
@@ -215,42 +214,6 @@
F(DefineAccessor, -1 /* 4 or 5 */) \
F(LookupAccessor, 3) \
\
- /* Debugging */ \
- F(SetDebugEventListener, 2) \
- F(Break, 0) \
- F(DebugGetPropertyDetails, 2) \
- F(DebugGetProperty, 2) \
- F(DebugLocalPropertyNames, 1) \
- F(DebugLocalElementNames, 1) \
- F(DebugPropertyTypeFromDetails, 1) \
- F(DebugPropertyAttributesFromDetails, 1) \
- F(DebugPropertyIndexFromDetails, 1) \
- F(DebugInterceptorInfo, 1) \
- F(DebugNamedInterceptorPropertyNames, 1) \
- F(DebugIndexedInterceptorElementNames, 1) \
- F(DebugNamedInterceptorPropertyValue, 2) \
- F(DebugIndexedInterceptorElementValue, 2) \
- F(CheckExecutionState, 1) \
- F(GetFrameCount, 1) \
- F(GetFrameDetails, 2) \
- F(GetCFrames, 1) \
- F(GetThreadCount, 1) \
- F(GetThreadDetails, 2) \
- F(GetBreakLocations, 1) \
- F(SetFunctionBreakPoint, 3) \
- F(SetScriptBreakPoint, 3) \
- F(ClearBreakPoint, 1) \
- F(ChangeBreakOnException, 2) \
- F(PrepareStep, 3) \
- F(ClearStepping, 0) \
- F(DebugEvaluate, 4) \
- F(DebugEvaluateGlobal, 3) \
- F(DebugGetLoadedScripts, 0) \
- F(DebugReferencedBy, 3) \
- F(DebugConstructedBy, 2) \
- F(DebugGetPrototype, 1) \
- F(SystemBreak, 0) \
- \
/* Literals */ \
F(MaterializeRegExpLiteral, 4)\
F(CreateArrayLiteralBoilerplate, 3) \
@@ -290,8 +253,6 @@
F(DebugTrace, 0) \
F(TraceEnter, 0) \
F(TraceExit, 1) \
- F(DebugBreak, 0) \
- F(FunctionGetAssemblerCode, 1) \
F(Abort, 2) \
/* Logging */ \
F(Log, 2) \
@@ -299,6 +260,48 @@
/* Pseudo functions - handled as macros by parser */ \
F(IS_VAR, 1)
+#ifdef ENABLE_DEBUGGER_SUPPORT
+#define RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F) \
+ /* Debugger support*/ \
+ F(DebugBreak, 0) \
+ F(SetDebugEventListener, 2) \
+ F(Break, 0) \
+ F(DebugGetPropertyDetails, 2) \
+ F(DebugGetProperty, 2) \
+ F(DebugLocalPropertyNames, 1) \
+ F(DebugLocalElementNames, 1) \
+ F(DebugPropertyTypeFromDetails, 1) \
+ F(DebugPropertyAttributesFromDetails, 1) \
+ F(DebugPropertyIndexFromDetails, 1) \
+ F(DebugInterceptorInfo, 1) \
+ F(DebugNamedInterceptorPropertyNames, 1) \
+ F(DebugIndexedInterceptorElementNames, 1) \
+ F(DebugNamedInterceptorPropertyValue, 2) \
+ F(DebugIndexedInterceptorElementValue, 2) \
+ F(CheckExecutionState, 1) \
+ F(GetFrameCount, 1) \
+ F(GetFrameDetails, 2) \
+ F(GetCFrames, 1) \
+ F(GetThreadCount, 1) \
+ F(GetThreadDetails, 2) \
+ F(GetBreakLocations, 1) \
+ F(SetFunctionBreakPoint, 3) \
+ F(SetScriptBreakPoint, 3) \
+ F(ClearBreakPoint, 1) \
+ F(ChangeBreakOnException, 2) \
+ F(PrepareStep, 3) \
+ F(ClearStepping, 0) \
+ F(DebugEvaluate, 4) \
+ F(DebugEvaluateGlobal, 3) \
+ F(DebugGetLoadedScripts, 0) \
+ F(DebugReferencedBy, 3) \
+ F(DebugConstructedBy, 2) \
+ F(DebugGetPrototype, 1) \
+ F(SystemBreak, 0) \
+ F(FunctionGetAssemblerCode, 1)
+#else
+#define RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F)
+#endif
#ifdef DEBUG
#define RUNTIME_FUNCTION_LIST_DEBUG(F) \
@@ -316,7 +319,8 @@
#define RUNTIME_FUNCTION_LIST(F) \
RUNTIME_FUNCTION_LIST_ALWAYS(F) \
- RUNTIME_FUNCTION_LIST_DEBUG(F)
+ RUNTIME_FUNCTION_LIST_DEBUG(F) \
+ RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F)
// ----------------------------------------------------------------------------
// Runtime provides access to all C++ runtime functions.
@@ -369,6 +373,11 @@
Handle<Object> value,
PropertyAttributes attr);
+ static Object* ForceSetObjectProperty(Handle<JSObject> object,
+ Handle<Object> key,
+ Handle<Object> value,
+ PropertyAttributes attr);
+
static Object* GetObjectProperty(Handle<Object> object, Handle<Object> key);
// This function is used in FunctionNameUsing* tests.
diff --git a/src/serialize.cc b/src/serialize.cc
index 9a2be23..e15c003 100644
--- a/src/serialize.cc
+++ b/src/serialize.cc
@@ -548,6 +548,7 @@
AddFromId(ref_table[i].type, ref_table[i].id, ref_table[i].name);
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Debug addresses
Add(Debug_Address(Debug::k_after_break_target_address).address(),
DEBUG_ADDRESS,
@@ -567,6 +568,7 @@
Debug::k_register_address << kDebugIdShift | i,
name.start());
}
+#endif
// Stat counters
struct StatsRefTableEntry {
@@ -659,10 +661,6 @@
UNCLASSIFIED,
4,
"RegExpStack::limit_address()");
- Add(ExternalReference::debug_break().address(),
- UNCLASSIFIED,
- 5,
- "Debug::Break()");
Add(ExternalReference::new_space_start().address(),
UNCLASSIFIED,
6,
@@ -679,10 +677,28 @@
UNCLASSIFIED,
9,
"Heap::NewSpaceAllocationTopAddress()");
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ Add(ExternalReference::debug_break().address(),
+ UNCLASSIFIED,
+ 5,
+ "Debug::Break()");
Add(ExternalReference::debug_step_in_fp_address().address(),
UNCLASSIFIED,
10,
"Debug::step_in_fp_addr()");
+ Add(ExternalReference::double_fp_operation(Token::ADD).address(),
+ UNCLASSIFIED,
+ 11,
+ "add_two_doubles");
+ Add(ExternalReference::double_fp_operation(Token::SUB).address(),
+ UNCLASSIFIED,
+ 12,
+ "sub_two_doubles");
+ Add(ExternalReference::double_fp_operation(Token::MUL).address(),
+ UNCLASSIFIED,
+ 13,
+ "mul_two_doubles");
+#endif
}
diff --git a/src/simulator-arm.cc b/src/simulator-arm.cc
index 5a61107..7d96b3a 100644
--- a/src/simulator-arm.cc
+++ b/src/simulator-arm.cc
@@ -90,12 +90,44 @@
}
+
+#ifdef GENERATED_CODE_COVERAGE
+static FILE* coverage_log = NULL;
+
+
+static void InitializeCoverage() {
+ char* file_name = getenv("V8_GENERATED_CODE_COVERAGE_LOG");
+ if (file_name != NULL) {
+ coverage_log = fopen(file_name, "aw+");
+ }
+}
+
+
+void Debugger::Stop(Instr* instr) {
+ char* str = reinterpret_cast<char*>(instr->InstructionBits() & 0x0fffffff);
+ if (strlen(str) > 0) {
+ if (coverage_log != NULL) {
+ fprintf(coverage_log, "%s\n", str);
+ fflush(coverage_log);
+ }
+ instr->SetInstructionBits(0xe1a00000); // Overwrite with nop.
+ }
+ sim_->set_pc(sim_->get_pc() + Instr::kInstrSize);
+}
+
+#else // ndef GENERATED_CODE_COVERAGE
+
+static void InitializeCoverage() {
+}
+
+
void Debugger::Stop(Instr* instr) {
const char* str = (const char*)(instr->InstructionBits() & 0x0fffffff);
PrintF("Simulator hit %s\n", str);
sim_->set_pc(sim_->get_pc() + Instr::kInstrSize);
Debug();
}
+#endif
static const char* reg_names[] = { "r0", "r1", "r2", "r3",
@@ -375,6 +407,7 @@
// access violation if the simulator ever tries to execute it.
registers_[pc] = bad_lr;
registers_[lr] = bad_lr;
+ InitializeCoverage();
}
@@ -427,6 +460,37 @@
}
+// For use in calls that take two double values, constructed from r0, r1, r2
+// and r3.
+void Simulator::GetFpArgs(double* x, double* y) {
+ // We use a char buffer to get around the strict-aliasing rules which
+ // otherwise allow the compiler to optimize away the copy.
+ char buffer[2 * sizeof(registers_[0])];
+ // Registers 0 and 1 -> x.
+ memcpy(buffer, registers_, sizeof(buffer));
+ memcpy(x, buffer, sizeof(buffer));
+ // Registers 2 and 3 -> y.
+ memcpy(buffer, registers_ + 2, sizeof(buffer));
+ memcpy(y, buffer, sizeof(buffer));
+}
+
+
+void Simulator::SetFpResult(const double& result) {
+ char buffer[2 * sizeof(registers_[0])];
+ memcpy(buffer, &result, sizeof(buffer));
+ // result -> registers 0 and 1.
+ memcpy(registers_, buffer, sizeof(buffer));
+}
+
+
+void Simulator::TrashCallerSaveRegisters() {
+ // We don't trash the registers with the return value.
+ registers_[2] = 0x50Bad4U;
+ registers_[3] = 0x50Bad4U;
+ registers_[12] = 0x50Bad4U;
+}
+
+
// The ARM cannot do unaligned reads and writes. On some ARM platforms an
// interrupt is caused. On others it does a funky rotation thing. For now we
// simply disallow unaligned reads, but at some point we may want to move to
@@ -862,7 +926,8 @@
// Software interrupt instructions are used by the simulator to call into the
// C-based V8 runtime.
void Simulator::SoftwareInterrupt(Instr* instr) {
- switch (instr->SwiField()) {
+ int swi = instr->SwiField();
+ switch (swi) {
case call_rt_r5: {
SimulatorRuntimeCall target =
reinterpret_cast<SimulatorRuntimeCall>(get_register(r5));
@@ -894,6 +959,30 @@
dbg.Debug();
break;
}
+ {
+ double x, y, z;
+ case simulator_fp_add:
+ GetFpArgs(&x, &y);
+ z = x + y;
+ SetFpResult(z);
+ TrashCallerSaveRegisters();
+ set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize);
+ break;
+ case simulator_fp_sub:
+ GetFpArgs(&x, &y);
+ z = x - y;
+ SetFpResult(z);
+ TrashCallerSaveRegisters();
+ set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize);
+ break;
+ case simulator_fp_mul:
+ GetFpArgs(&x, &y);
+ z = x * y;
+ SetFpResult(z);
+ TrashCallerSaveRegisters();
+ set_pc(reinterpret_cast<int32_t>(instr) + Instr::kInstrSize);
+ break;
+ }
default: {
UNREACHABLE();
break;
diff --git a/src/simulator-arm.h b/src/simulator-arm.h
index 4f3c53d..5553ab2 100644
--- a/src/simulator-arm.h
+++ b/src/simulator-arm.h
@@ -174,6 +174,12 @@
// Executes one instruction.
void InstructionDecode(Instr* instr);
+ // For use in calls that take two double values, constructed from r0, r1, r2
+ // and r3.
+ void GetFpArgs(double* x, double* y);
+ void SetFpResult(const double& result);
+ void TrashCallerSaveRegisters();
+
// architecture state
int32_t registers_[16];
bool n_flag_;
diff --git a/src/string-stream.cc b/src/string-stream.cc
index f311e20..2e0912f 100644
--- a/src/string-stream.cc
+++ b/src/string-stream.cc
@@ -57,19 +57,26 @@
bool StringStream::Put(char c) {
- if (space() == 0) return false;
- if (length_ >= capacity_ - 1) {
+ if (full()) return false;
+ ASSERT(length_ < capacity_);
+ // Since the trailing '\0' is not accounted for in length_ fullness is
+ // indicated by a difference of 1 between length_ and capacity_. Thus when
+ // reaching a difference of 2 we need to grow the buffer.
+ if (length_ == capacity_ - 2) {
unsigned new_capacity = capacity_;
char* new_buffer = allocator_->grow(&new_capacity);
if (new_capacity > capacity_) {
capacity_ = new_capacity;
buffer_ = new_buffer;
} else {
- // Indicate truncation with dots.
- memset(cursor(), '.', space());
- length_ = capacity_;
- buffer_[length_ - 2] = '\n';
- buffer_[length_ - 1] = '\0';
+ // Reached the end of the available buffer.
+ ASSERT(capacity_ >= 5);
+ length_ = capacity_ - 1; // Indicate fullness of the stream.
+ buffer_[length_ - 4] = '.';
+ buffer_[length_ - 3] = '.';
+ buffer_[length_ - 2] = '.';
+ buffer_[length_ - 1] = '\n';
+ buffer_[length_] = '\0';
return false;
}
}
@@ -95,8 +102,7 @@
void StringStream::Add(Vector<const char> format, Vector<FmtElm> elms) {
// If we already ran out of space then return immediately.
- if (space() == 0)
- return;
+ if (full()) return;
int offset = 0;
int elm = 0;
while (offset < format.length()) {
@@ -564,12 +570,10 @@
}
+// Only grow once to the maximum allowable size.
char* NoAllocationStringAllocator::grow(unsigned* bytes) {
- unsigned new_bytes = *bytes * 2;
- if (new_bytes > size_) {
- new_bytes = size_;
- }
- *bytes = new_bytes;
+ ASSERT(size_ >= *bytes);
+ *bytes = size_;
return space_;
}
diff --git a/src/string-stream.h b/src/string-stream.h
index 37ae93e..901f376 100644
--- a/src/string-stream.h
+++ b/src/string-stream.h
@@ -162,8 +162,8 @@
unsigned length_; // does not include terminating 0-character
char* buffer_;
+ bool full() const { return (capacity_ - length_) == 1; }
int space() const { return capacity_ - length_; }
- char* cursor() const { return buffer_ + length_; }
DISALLOW_IMPLICIT_CONSTRUCTORS(StringStream);
};
diff --git a/src/stub-cache-arm.cc b/src/stub-cache-arm.cc
index 211b643..56afa02 100644
--- a/src/stub-cache-arm.cc
+++ b/src/stub-cache-arm.cc
@@ -33,7 +33,7 @@
namespace v8 { namespace internal {
-#define __ masm->
+#define __ ACCESS_MASM(masm)
static void ProbeTable(MacroAssembler* masm,
@@ -183,7 +183,7 @@
// Check that the maps haven't changed.
Register reg =
- __ CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
+ masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
GenerateFastPropertyLoad(masm, r0, reg, holder, index);
__ Ret();
}
@@ -203,7 +203,7 @@
// Check that the maps haven't changed.
Register reg =
- __ CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
+ masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
// Return the constant value.
__ mov(r0, Operand(Handle<Object>(value)));
@@ -226,7 +226,7 @@
// Check that the maps haven't changed.
Register reg =
- __ CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
+ masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
// Push the arguments on the JS stack of the caller.
__ push(receiver); // receiver
@@ -256,7 +256,7 @@
// Check that the maps haven't changed.
Register reg =
- __ CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
+ masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
// Push the arguments on the JS stack of the caller.
__ push(receiver); // receiver
@@ -456,8 +456,7 @@
#undef __
-
-#define __ masm()->
+#define __ ACCESS_MASM(masm())
Object* StubCompiler::CompileLazyCompile(Code::Flags flags) {
@@ -511,7 +510,7 @@
// Do the right check and compute the holder register.
Register reg =
- __ CheckMaps(JSObject::cast(object), r0, holder, r3, r2, &miss);
+ masm()->CheckMaps(JSObject::cast(object), r0, holder, r3, r2, &miss);
GenerateFastPropertyLoad(masm(), r1, reg, holder, index);
// Check that the function really is a function.
diff --git a/src/stub-cache-ia32.cc b/src/stub-cache-ia32.cc
index 19777e6..bdfc3d6 100644
--- a/src/stub-cache-ia32.cc
+++ b/src/stub-cache-ia32.cc
@@ -33,7 +33,7 @@
namespace v8 { namespace internal {
-#define __ masm->
+#define __ ACCESS_MASM(masm)
static void ProbeTable(MacroAssembler* masm,
@@ -256,7 +256,7 @@
// Check that the maps haven't changed.
Register reg =
- __ CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
+ masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
// Get the value from the properties.
GenerateFastPropertyLoad(masm, eax, reg, holder, index);
@@ -279,7 +279,7 @@
// Check that the maps haven't changed.
Register reg =
- __ CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
+ masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
// Push the arguments on the JS stack of the caller.
__ pop(scratch2); // remove return address
@@ -310,7 +310,7 @@
// Check that the maps haven't changed.
Register reg =
- __ CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
+ masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
// Return the constant value.
__ mov(eax, Handle<Object>(value));
@@ -332,7 +332,7 @@
// Check that the maps haven't changed.
Register reg =
- __ CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
+ masm->CheckMaps(object, receiver, holder, scratch1, scratch2, miss_label);
// Push the arguments on the JS stack of the caller.
__ pop(scratch2); // remove return address
@@ -440,7 +440,7 @@
#undef __
-#define __ masm()->
+#define __ ACCESS_MASM(masm())
// TODO(1241006): Avoid having lazy compile stubs specialized by the
@@ -485,7 +485,7 @@
// Do the right check and compute the holder register.
Register reg =
- __ CheckMaps(JSObject::cast(object), edx, holder, ebx, ecx, &miss);
+ masm()->CheckMaps(JSObject::cast(object), edx, holder, ebx, ecx, &miss);
GenerateFastPropertyLoad(masm(), edi, reg, holder, index);
@@ -656,7 +656,7 @@
// Check that maps have not changed and compute the holder register.
Register reg =
- __ CheckMaps(JSObject::cast(object), edx, holder, ebx, ecx, &miss);
+ masm()->CheckMaps(JSObject::cast(object), edx, holder, ebx, ecx, &miss);
// Enter an internal frame.
__ EnterInternalFrame();
diff --git a/src/stub-cache.cc b/src/stub-cache.cc
index 35e3d42..6811fd2 100644
--- a/src/stub-cache.cc
+++ b/src/stub-cache.cc
@@ -594,6 +594,7 @@
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
Object* StubCache::ComputeCallDebugBreak(int argc) {
Code::Flags flags =
Code::ComputeFlags(Code::CALL_IC, DEBUG_BREAK, NORMAL, argc);
@@ -612,6 +613,7 @@
StubCompiler compiler;
return FillCache(compiler.CompileCallDebugPrepareStepIn(flags));
}
+#endif
Object* StubCache::ComputeLazyCompile(int argc) {
@@ -836,6 +838,7 @@
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
Object* StubCompiler::CompileCallDebugBreak(Code::Flags flags) {
HandleScope scope;
Debug::GenerateCallICDebugBreak(masm());
@@ -864,6 +867,7 @@
}
return result;
}
+#endif
Object* StubCompiler::GetCodeWithFlags(Code::Flags flags, const char* name) {
diff --git a/src/stub-cache.h b/src/stub-cache.h
index 05845e5..824f4ff 100644
--- a/src/stub-cache.h
+++ b/src/stub-cache.h
@@ -157,8 +157,10 @@
// Finds the Code object stored in the Heap::non_monomorphic_cache().
static Code* FindCallInitialize(int argc);
+#ifdef ENABLE_DEBUGGER_SUPPORT
static Object* ComputeCallDebugBreak(int argc);
static Object* ComputeCallDebugPrepareStepIn(int argc);
+#endif
static Object* ComputeLazyCompile(int argc);
@@ -288,8 +290,10 @@
Object* CompileCallNormal(Code::Flags flags);
Object* CompileCallMegamorphic(Code::Flags flags);
Object* CompileCallMiss(Code::Flags flags);
+#ifdef ENABLE_DEBUGGER_SUPPORT
Object* CompileCallDebugBreak(Code::Flags flags);
Object* CompileCallDebugPrepareStepIn(Code::Flags flags);
+#endif
Object* CompileLazyCompile(Code::Flags flags);
// Static functions for generating parts of stubs.
diff --git a/src/top.cc b/src/top.cc
index a99e9ff..96f98a5 100644
--- a/src/top.cc
+++ b/src/top.cc
@@ -116,15 +116,15 @@
// When the thread starts running it will allocate a fixed number of bytes
// on the stack and publish the location of this memory for others to use.
void Run() {
- EmbeddedVector<char, 32 * 1024> local_buffer;
+ EmbeddedVector<char, 15 * 1024> local_buffer;
// Initialize the buffer with a known good value.
OS::StrNCpy(local_buffer, "Trace data was not generated.\n",
local_buffer.length());
// Publish the local buffer and signal its availability.
- data_ = &local_buffer[0];
- length_ = sizeof(local_buffer);
+ data_ = local_buffer.start();
+ length_ = local_buffer.length();
data_ready_semaphore_->Signal();
while (keep_running_) {
@@ -728,8 +728,10 @@
bool should_return_exception = ShouldReportException(&is_caught_externally);
bool report_exception = !is_out_of_memory && should_return_exception;
+#ifdef ENABLE_DEBUGGER_SUPPORT
// Notify debugger of exception.
Debugger::OnException(exception_handle, report_exception);
+#endif
// Generate the message.
Handle<Object> message_obj;
diff --git a/src/utils.h b/src/utils.h
index f62b47a..e008c85 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -406,6 +406,16 @@
};
+template <typename T>
+class ScopedVector : public Vector<T> {
+ public:
+ explicit ScopedVector(int length) : Vector<T>(NewArray<T>(length), length) { }
+ ~ScopedVector() {
+ DeleteArray(this->start());
+ }
+};
+
+
inline Vector<const char> CStrVector(const char* data) {
return Vector<const char>(data, strlen(data));
}
diff --git a/src/v8.cc b/src/v8.cc
index 9158b52..fbe3d5d 100644
--- a/src/v8.cc
+++ b/src/v8.cc
@@ -72,7 +72,9 @@
v8::Locker::StartPreemption(100);
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
Debug::Setup(create_heap_objects);
+#endif
StubCache::Initialize(create_heap_objects);
// If we are deserializing, read the state into the now-empty heap.
@@ -111,7 +113,9 @@
Heap::TearDown();
Logger::TearDown();
+#ifdef ENABLE_DEBUGGER_SUPPORT
Debugger::TearDown();
+#endif
has_been_setup_ = false;
has_been_disposed_ = true;
diff --git a/src/v8.h b/src/v8.h
index 4cf0b70..4ced0d2 100644
--- a/src/v8.h
+++ b/src/v8.h
@@ -51,6 +51,11 @@
#error both DEBUG and NDEBUG are set
#endif
+// Enable debugger support by default, unless it is in ANDROID
+#if !defined(ENABLE_DEBUGGER_SUPPORT) && !defined(ANDROID)
+#define ENABLE_DEBUGGER_SUPPORT
+#endif
+
// Basic includes
#include "../include/v8.h"
#include "globals.h"
diff --git a/src/v8threads.cc b/src/v8threads.cc
index e8f6f9b..2439476 100644
--- a/src/v8threads.cc
+++ b/src/v8threads.cc
@@ -144,7 +144,9 @@
char* from = state->data();
from = HandleScopeImplementer::RestoreThread(from);
from = Top::RestoreThread(from);
+#ifdef ENABLE_DEBUGGER_SUPPORT
from = Debug::RestoreDebug(from);
+#endif
from = StackGuard::RestoreStackGuard(from);
from = RegExpStack::RestoreStack(from);
from = Bootstrapper::RestoreState(from);
@@ -172,7 +174,9 @@
static int ArchiveSpacePerThread() {
return HandleScopeImplementer::ArchiveSpacePerThread() +
Top::ArchiveSpacePerThread() +
+#ifdef ENABLE_DEBUGGER_SUPPORT
Debug::ArchiveSpacePerThread() +
+#endif
StackGuard::ArchiveSpacePerThread() +
RegExpStack::ArchiveSpacePerThread() +
Bootstrapper::ArchiveSpacePerThread();
@@ -259,7 +263,9 @@
char* to = state->data();
to = HandleScopeImplementer::ArchiveThread(to);
to = Top::ArchiveThread(to);
+#ifdef ENABLE_DEBUGGER_SUPPORT
to = Debug::ArchiveDebug(to);
+#endif
to = StackGuard::ArchiveStackGuard(to);
to = RegExpStack::ArchiveStack(to);
to = Bootstrapper::ArchiveState(to);
diff --git a/src/virtual-frame-arm.cc b/src/virtual-frame-arm.cc
index baf0814..b794d8b 100644
--- a/src/virtual-frame-arm.cc
+++ b/src/virtual-frame-arm.cc
@@ -36,7 +36,8 @@
// -------------------------------------------------------------------------
// VirtualFrame implementation.
-#define __ masm_->
+#define __ ACCESS_MASM(masm_)
+
// On entry to a function, the virtual frame already contains the
// receiver and the parameters. All initial frame elements are in
diff --git a/src/virtual-frame-ia32.cc b/src/virtual-frame-ia32.cc
index eb3befc..3647ca5 100644
--- a/src/virtual-frame-ia32.cc
+++ b/src/virtual-frame-ia32.cc
@@ -33,7 +33,7 @@
namespace v8 { namespace internal {
-#define __ masm_->
+#define __ ACCESS_MASM(masm_)
// -------------------------------------------------------------------------
// VirtualFrame implementation.
diff --git a/test/cctest/cctest.status b/test/cctest/cctest.status
index 9a3f819..e56452f 100644
--- a/test/cctest/cctest.status
+++ b/test/cctest/cctest.status
@@ -27,9 +27,6 @@
prefix cctest
-# BUG(96): Fix this flaky test.
-test-debug/ThreadedDebugging: PASS || FAIL
-
# BUG(281): This test fails on some Linuxes.
test-debug/DebuggerAgent: PASS, (PASS || FAIL) if $system == linux
@@ -37,14 +34,19 @@
test-debug: SKIP
+# Bug http://code.google.com/p/v8/issues/detail?id=323
+test-api/ApplyInterruption: SKIP
+
# BUG(113): Test seems flaky on ARM.
test-spaces/LargeObjectSpace: PASS || FAIL
# BUG(240): Test seems flaky on ARM.
test-api/RegExpInterruption: SKIP
+[ $simulator == arm ]
+
# BUG(271): During exception propagation, we compare pointers into the
# stack. These tests fail on the ARM simulator because the C++ and
# the JavaScript stacks are separate.
-test-api/ExceptionOrder: PASS || FAIL
-test-api/TryCatchInTryFinally: PASS || FAIL
+test-api/ExceptionOrder: FAIL
+test-api/TryCatchInTryFinally: FAIL
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index 23e57d5..b7a5cb6 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -1394,6 +1394,7 @@
v8::Handle<Value> data) {
CHECK_EQ(5.76, data->NumberValue());
CHECK_EQ(6.75, message->GetScriptResourceName()->NumberValue());
+ CHECK_EQ(7.56, message->GetScriptData()->NumberValue());
message_received = true;
}
@@ -1406,7 +1407,10 @@
LocalContext context;
v8::ScriptOrigin origin =
v8::ScriptOrigin(v8_str("6.75"));
- Script::Compile(v8_str("throw 'error'"), &origin)->Run();
+ v8::Handle<v8::Script> script = Script::Compile(v8_str("throw 'error'"),
+ &origin);
+ script->SetData(v8_str("7.56"));
+ script->Run();
CHECK(message_received);
// clear out the message listener
v8::V8::RemoveMessageListeners(check_message);
@@ -2925,7 +2929,19 @@
static v8::Handle<Value> GetK(Local<String> name, const AccessorInfo&) {
ApiTestFuzzer::Fuzz();
- return v8::Undefined();
+ if (name->Equals(v8_str("foo")) ||
+ name->Equals(v8_str("bar")) ||
+ name->Equals(v8_str("baz"))) {
+ return v8::Undefined();
+ }
+ return v8::Handle<Value>();
+}
+
+
+static v8::Handle<Value> IndexedGetK(uint32_t index, const AccessorInfo&) {
+ ApiTestFuzzer::Fuzz();
+ if (index == 0 || index == 1) return v8::Undefined();
+ return v8::Handle<Value>();
}
@@ -2942,8 +2958,8 @@
static v8::Handle<v8::Array> IndexedEnum(const AccessorInfo&) {
ApiTestFuzzer::Fuzz();
v8::Handle<v8::Array> result = v8::Array::New(2);
- result->Set(v8::Integer::New(0), v8_str("hat"));
- result->Set(v8::Integer::New(1), v8_str("gyt"));
+ result->Set(v8::Integer::New(0), v8_str("0"));
+ result->Set(v8::Integer::New(1), v8_str("1"));
return result;
}
@@ -2952,21 +2968,56 @@
v8::HandleScope scope;
v8::Handle<v8::ObjectTemplate> obj = ObjectTemplate::New();
obj->SetNamedPropertyHandler(GetK, NULL, NULL, NULL, NamedEnum);
- obj->SetIndexedPropertyHandler(NULL, NULL, NULL, NULL, IndexedEnum);
+ obj->SetIndexedPropertyHandler(IndexedGetK, NULL, NULL, NULL, IndexedEnum);
LocalContext context;
context->Global()->Set(v8_str("k"), obj->NewInstance());
v8::Handle<v8::Array> result = v8::Handle<v8::Array>::Cast(CompileRun(
+ "k[10] = 0;"
+ "k.a = 0;"
+ "k[5] = 0;"
+ "k.b = 0;"
+ "k[4294967295] = 0;"
+ "k.c = 0;"
+ "k[4294967296] = 0;"
+ "k.d = 0;"
+ "k[140000] = 0;"
+ "k.e = 0;"
+ "k[30000000000] = 0;"
+ "k.f = 0;"
"var result = [];"
"for (var prop in k) {"
" result.push(prop);"
"}"
"result"));
- CHECK_EQ(5, result->Length());
- CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(0)));
- CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(1)));
- CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(2)));
- CHECK_EQ(v8_str("hat"), result->Get(v8::Integer::New(3)));
- CHECK_EQ(v8_str("gyt"), result->Get(v8::Integer::New(4)));
+ // Check that we get all the property names returned including the
+ // ones from the enumerators in the right order: indexed properties
+ // in numerical order, indexed interceptor properties, named
+ // properties in insertion order, named interceptor properties.
+ // This order is not mandated by the spec, so this test is just
+ // documenting our behavior.
+ CHECK_EQ(17, result->Length());
+ // Indexed properties in numerical order.
+ CHECK_EQ(v8_str("5"), result->Get(v8::Integer::New(0)));
+ CHECK_EQ(v8_str("10"), result->Get(v8::Integer::New(1)));
+ CHECK_EQ(v8_str("140000"), result->Get(v8::Integer::New(2)));
+ CHECK_EQ(v8_str("4294967295"), result->Get(v8::Integer::New(3)));
+ // Indexed interceptor properties in the order they are returned
+ // from the enumerator interceptor.
+ CHECK_EQ(v8_str("0"), result->Get(v8::Integer::New(4)));
+ CHECK_EQ(v8_str("1"), result->Get(v8::Integer::New(5)));
+ // Named properties in insertion order.
+ CHECK_EQ(v8_str("a"), result->Get(v8::Integer::New(6)));
+ CHECK_EQ(v8_str("b"), result->Get(v8::Integer::New(7)));
+ CHECK_EQ(v8_str("c"), result->Get(v8::Integer::New(8)));
+ CHECK_EQ(v8_str("4294967296"), result->Get(v8::Integer::New(9)));
+ CHECK_EQ(v8_str("d"), result->Get(v8::Integer::New(10)));
+ CHECK_EQ(v8_str("e"), result->Get(v8::Integer::New(11)));
+ CHECK_EQ(v8_str("30000000000"), result->Get(v8::Integer::New(12)));
+ CHECK_EQ(v8_str("f"), result->Get(v8::Integer::New(13)));
+ // Named interceptor properties.
+ CHECK_EQ(v8_str("foo"), result->Get(v8::Integer::New(14)));
+ CHECK_EQ(v8_str("bar"), result->Get(v8::Integer::New(15)));
+ CHECK_EQ(v8_str("baz"), result->Get(v8::Integer::New(16)));
}
@@ -5963,6 +6014,112 @@
}
+class ApplyInterruptTest {
+ public:
+ ApplyInterruptTest() : block_(NULL) {}
+ ~ApplyInterruptTest() { delete block_; }
+ void RunTest() {
+ block_ = i::OS::CreateSemaphore(0);
+ gc_count_ = 0;
+ gc_during_apply_ = 0;
+ apply_success_ = false;
+ gc_success_ = false;
+ GCThread gc_thread(this);
+ gc_thread.Start();
+ v8::Locker::StartPreemption(1);
+
+ LongRunningApply();
+ {
+ v8::Unlocker unlock;
+ gc_thread.Join();
+ }
+ v8::Locker::StopPreemption();
+ CHECK(apply_success_);
+ CHECK(gc_success_);
+ }
+ private:
+ // Number of garbage collections required.
+ static const int kRequiredGCs = 2;
+
+ class GCThread : public i::Thread {
+ public:
+ explicit GCThread(ApplyInterruptTest* test)
+ : test_(test) {}
+ virtual void Run() {
+ test_->CollectGarbage();
+ }
+ private:
+ ApplyInterruptTest* test_;
+ };
+
+ void CollectGarbage() {
+ block_->Wait();
+ while (gc_during_apply_ < kRequiredGCs) {
+ {
+ v8::Locker lock;
+ i::Heap::CollectAllGarbage();
+ gc_count_++;
+ }
+ i::OS::Sleep(1);
+ }
+ gc_success_ = true;
+ }
+
+ void LongRunningApply() {
+ block_->Signal();
+ int rounds = 0;
+ while (gc_during_apply_ < kRequiredGCs) {
+ int gc_before = gc_count_;
+ {
+ const char* c_source =
+ "function do_very_little(bar) {"
+ " this.foo = bar;"
+ "}"
+ "for (var i = 0; i < 100000; i++) {"
+ " do_very_little.apply(this, ['bar']);"
+ "}";
+ Local<String> source = String::New(c_source);
+ Local<Script> script = Script::Compile(source);
+ Local<Value> result = script->Run();
+ }
+ int gc_after = gc_count_;
+ gc_during_apply_ += gc_after - gc_before;
+ rounds++;
+ }
+ apply_success_ = true;
+ }
+
+ i::Semaphore* block_;
+ int gc_count_;
+ int gc_during_apply_;
+ bool apply_success_;
+ bool gc_success_;
+};
+
+
+// Test that nothing bad happens if we get a preemption just when we were
+// about to do an apply().
+TEST(ApplyInterruption) {
+ v8::Locker lock;
+ v8::V8::Initialize();
+ v8::HandleScope scope;
+ Local<Context> local_env;
+ {
+ LocalContext env;
+ local_env = env.local();
+ }
+
+ // Local context should still be live.
+ CHECK(!local_env.IsEmpty());
+ local_env->Enter();
+
+ // Should complete without problems.
+ ApplyInterruptTest().RunTest();
+
+ local_env->Exit();
+}
+
+
// Verify that we can clone an object
TEST(ObjectClone) {
v8::HandleScope scope;
@@ -6187,3 +6344,118 @@
res = CompileRun("function f() { with (this) { y = 42 }; return y; }; f()");
CHECK_EQ(v8::Integer::New(42), res);
}
+
+static int force_set_set_count = 0;
+static int force_set_get_count = 0;
+bool pass_on_get = false;
+
+static v8::Handle<v8::Value> ForceSetGetter(v8::Local<v8::String> name,
+ const v8::AccessorInfo& info) {
+ force_set_get_count++;
+ if (pass_on_get) {
+ return v8::Handle<v8::Value>();
+ } else {
+ return v8::Int32::New(3);
+ }
+}
+
+static void ForceSetSetter(v8::Local<v8::String> name,
+ v8::Local<v8::Value> value,
+ const v8::AccessorInfo& info) {
+ force_set_set_count++;
+}
+
+static v8::Handle<v8::Value> ForceSetInterceptSetter(
+ v8::Local<v8::String> name,
+ v8::Local<v8::Value> value,
+ const v8::AccessorInfo& info) {
+ force_set_set_count++;
+ return v8::Undefined();
+}
+
+TEST(ForceSet) {
+ force_set_get_count = 0;
+ force_set_set_count = 0;
+ pass_on_get = false;
+
+ v8::HandleScope scope;
+ v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
+ v8::Handle<v8::String> access_property = v8::String::New("a");
+ templ->SetAccessor(access_property, ForceSetGetter, ForceSetSetter);
+ LocalContext context(NULL, templ);
+ v8::Handle<v8::Object> global = context->Global();
+
+ // Ordinary properties
+ v8::Handle<v8::String> simple_property = v8::String::New("p");
+ global->Set(simple_property, v8::Int32::New(4), v8::ReadOnly);
+ CHECK_EQ(4, global->Get(simple_property)->Int32Value());
+ // This should fail because the property is read-only
+ global->Set(simple_property, v8::Int32::New(5));
+ CHECK_EQ(4, global->Get(simple_property)->Int32Value());
+ // This should succeed even though the property is read-only
+ global->ForceSet(simple_property, v8::Int32::New(6));
+ CHECK_EQ(6, global->Get(simple_property)->Int32Value());
+
+ // Accessors
+ CHECK_EQ(0, force_set_set_count);
+ CHECK_EQ(0, force_set_get_count);
+ CHECK_EQ(3, global->Get(access_property)->Int32Value());
+ // CHECK_EQ the property shouldn't override it, just call the setter
+ // which in this case does nothing.
+ global->Set(access_property, v8::Int32::New(7));
+ CHECK_EQ(3, global->Get(access_property)->Int32Value());
+ CHECK_EQ(1, force_set_set_count);
+ CHECK_EQ(2, force_set_get_count);
+ // Forcing the property to be set should override the accessor without
+ // calling it
+ global->ForceSet(access_property, v8::Int32::New(8));
+ CHECK_EQ(8, global->Get(access_property)->Int32Value());
+ CHECK_EQ(1, force_set_set_count);
+ CHECK_EQ(2, force_set_get_count);
+}
+
+TEST(ForceSetWithInterceptor) {
+ force_set_get_count = 0;
+ force_set_set_count = 0;
+ pass_on_get = false;
+
+ v8::HandleScope scope;
+ v8::Handle<v8::ObjectTemplate> templ = v8::ObjectTemplate::New();
+ templ->SetNamedPropertyHandler(ForceSetGetter, ForceSetInterceptSetter);
+ LocalContext context(NULL, templ);
+ v8::Handle<v8::Object> global = context->Global();
+
+ v8::Handle<v8::String> some_property = v8::String::New("a");
+ CHECK_EQ(0, force_set_set_count);
+ CHECK_EQ(0, force_set_get_count);
+ CHECK_EQ(3, global->Get(some_property)->Int32Value());
+ // Setting the property shouldn't override it, just call the setter
+ // which in this case does nothing.
+ global->Set(some_property, v8::Int32::New(7));
+ CHECK_EQ(3, global->Get(some_property)->Int32Value());
+ CHECK_EQ(1, force_set_set_count);
+ CHECK_EQ(2, force_set_get_count);
+ // Getting the property when the interceptor returns an empty handle
+ // should yield undefined, since the property isn't present on the
+ // object itself yet.
+ pass_on_get = true;
+ CHECK(global->Get(some_property)->IsUndefined());
+ CHECK_EQ(1, force_set_set_count);
+ CHECK_EQ(3, force_set_get_count);
+ // Forcing the property to be set should cause the value to be
+ // set locally without calling the interceptor.
+ global->ForceSet(some_property, v8::Int32::New(8));
+ CHECK_EQ(8, global->Get(some_property)->Int32Value());
+ CHECK_EQ(1, force_set_set_count);
+ CHECK_EQ(4, force_set_get_count);
+ // Reenabling the interceptor should cause it to take precedence over
+ // the property
+ pass_on_get = false;
+ CHECK_EQ(3, global->Get(some_property)->Int32Value());
+ CHECK_EQ(1, force_set_set_count);
+ CHECK_EQ(5, force_set_get_count);
+ // The interceptor should also work for other properties
+ CHECK_EQ(3, global->Get(v8::String::New("b"))->Int32Value());
+ CHECK_EQ(1, force_set_set_count);
+ CHECK_EQ(6, force_set_get_count);
+}
diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc
index e482c7f..fea07c5 100644
--- a/test/cctest/test-debug.cc
+++ b/test/cctest/test-debug.cc
@@ -45,10 +45,13 @@
using ::v8::internal::Code;
using ::v8::internal::Debug;
using ::v8::internal::Debugger;
+using ::v8::internal::Message;
+using ::v8::internal::MessageQueue;
using ::v8::internal::StepAction;
using ::v8::internal::StepIn; // From StepAction enum
using ::v8::internal::StepNext; // From StepAction enum
using ::v8::internal::StepOut; // From StepAction enum
+using ::v8::internal::Vector;
// Size of temp buffer for formatting small strings.
@@ -525,6 +528,24 @@
v8::Local<v8::Function> frame_source_column;
+// Source for The JavaScript function which picks out the script name for the
+// top frame.
+const char* frame_script_name_source =
+ "function frame_script_name(exec_state) {"
+ " return exec_state.frame(0).func().script().name();"
+ "}";
+v8::Local<v8::Function> frame_script_name;
+
+
+// Source for The JavaScript function which picks out the script data for the
+// top frame.
+const char* frame_script_data_source =
+ "function frame_script_data(exec_state) {"
+ " return exec_state.frame(0).func().script().data();"
+ "}";
+v8::Local<v8::Function> frame_script_data;
+
+
// Source for The JavaScript function which returns the number of frames.
static const char* frame_count_source =
"function frame_count(exec_state) {"
@@ -536,6 +557,11 @@
// Global variable to store the last function hit - used by some tests.
char last_function_hit[80];
+// Global variable to store the name and data for last script hit - used by some
+// tests.
+char last_script_name_hit[80];
+char last_script_data_hit[80];
+
// Global variables to store the last source position - used by some tests.
int last_source_line = -1;
int last_source_column = -1;
@@ -586,6 +612,37 @@
CHECK(result->IsNumber());
last_source_column = result->Int32Value();
}
+
+ if (!frame_script_name.IsEmpty()) {
+ // Get the script name of the function script.
+ const int argc = 1;
+ v8::Handle<v8::Value> argv[argc] = { exec_state };
+ v8::Handle<v8::Value> result = frame_script_name->Call(exec_state,
+ argc, argv);
+ if (result->IsUndefined()) {
+ last_script_name_hit[0] = '\0';
+ } else {
+ CHECK(result->IsString());
+ v8::Handle<v8::String> script_name(result->ToString());
+ script_name->WriteAscii(last_script_name_hit);
+ }
+ }
+
+ if (!frame_script_data.IsEmpty()) {
+ // Get the script data of the function script.
+ const int argc = 1;
+ v8::Handle<v8::Value> argv[argc] = { exec_state };
+ v8::Handle<v8::Value> result = frame_script_data->Call(exec_state,
+ argc, argv);
+ if (result->IsUndefined()) {
+ last_script_data_hit[0] = '\0';
+ } else {
+ result = result->ToString();
+ CHECK(result->IsString());
+ v8::Handle<v8::String> script_data(result->ToString());
+ script_data->WriteAscii(last_script_data_hit);
+ }
+ }
}
}
@@ -2120,6 +2177,53 @@
}
+// Test of the stepping mechanism for keyed load in a loop.
+TEST(DebugStepKeyedLoadLoop) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+
+ // 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(
+ &env,
+ "function foo(a) {\n"
+ " var x;\n"
+ " var len = a.length;\n"
+ " for (var i = 0; i < len; i++) {\n"
+ " y = 1;\n"
+ " x = a[i];\n"
+ " }\n"
+ "}\n",
+ "foo");
+
+ // Create array [0,1,2,3,4,5,6,7,8,9]
+ v8::Local<v8::Array> a = v8::Array::New(10);
+ for (int i = 0; i < 10; i++) {
+ a->Set(v8::Number::New(i), v8::Number::New(i));
+ }
+
+ // Call function without any break points to ensure inlining is in place.
+ const int kArgc = 1;
+ 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;
+ break_point_hit_count = 0;
+ foo->Call(env->Global(), kArgc, args);
+
+ // With stepping all break locations are hit.
+ CHECK_EQ(22, break_point_hit_count);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
// Test the stepping mechanism with different ICs.
TEST(DebugStepLinearMixedICs) {
v8::HandleScope scope;
@@ -3321,7 +3425,8 @@
void Run();
};
-static void MessageHandler(const uint16_t* message, int length, void *data) {
+static void MessageHandler(const uint16_t* message, int length,
+ v8::Debug::ClientData* client_data) {
static char print_buffer[1000];
Utf16ToAscii(message, length, print_buffer);
if (IsBreakEventMessage(print_buffer)) {
@@ -3336,7 +3441,6 @@
fflush(stdout);
}
-
void MessageQueueDebuggerThread::Run() {
const int kBufferSize = 1000;
uint16_t buffer_1[kBufferSize];
@@ -3427,6 +3531,118 @@
fflush(stdout);
}
+
+class TestClientData : public v8::Debug::ClientData {
+ public:
+ TestClientData() {
+ constructor_call_counter++;
+ }
+ virtual ~TestClientData() {
+ destructor_call_counter++;
+ }
+
+ static void ResetCounters() {
+ constructor_call_counter = 0;
+ destructor_call_counter = 0;
+ }
+
+ static int constructor_call_counter;
+ static int destructor_call_counter;
+};
+
+int TestClientData::constructor_call_counter = 0;
+int TestClientData::destructor_call_counter = 0;
+
+
+// Tests that MessageQueue doesn't destroy client data when expands and
+// does destroy when it dies.
+TEST(MessageQueueExpandAndDestroy) {
+ TestClientData::ResetCounters();
+ { // Create a scope for the queue.
+ MessageQueue queue(1);
+ queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
+ new TestClientData()));
+ queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
+ new TestClientData()));
+ queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
+ new TestClientData()));
+ ASSERT_EQ(0, TestClientData::destructor_call_counter);
+ queue.Get().Dispose();
+ ASSERT_EQ(1, TestClientData::destructor_call_counter);
+ queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
+ new TestClientData()));
+ queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
+ new TestClientData()));
+ queue.Put(Message::NewCommand(Vector<uint16_t>::empty(),
+ new TestClientData()));
+ queue.Put(Message::NewOutput(v8::Handle<v8::String>(),
+ new TestClientData()));
+ queue.Put(Message::NewEmptyMessage());
+ ASSERT_EQ(1, TestClientData::destructor_call_counter);
+ queue.Get().Dispose();
+ ASSERT_EQ(2, TestClientData::destructor_call_counter);
+ }
+ // All the client data should be destroyed when the queue is destroyed.
+ ASSERT_EQ(TestClientData::destructor_call_counter,
+ TestClientData::destructor_call_counter);
+}
+
+
+static int handled_client_data_instances_count = 0;
+static void MessageHandlerCountingClientData(
+ const uint16_t* message,
+ int length,
+ v8::Debug::ClientData* client_data) {
+ if (client_data) {
+ handled_client_data_instances_count++;
+ }
+}
+
+
+// Tests that all client data passed to the debugger are sent to the handler.
+TEST(SendClientDataToHandler) {
+ // Create a V8 environment
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ TestClientData::ResetCounters();
+ handled_client_data_instances_count = 0;
+ v8::Debug::SetMessageHandler(MessageHandlerCountingClientData,
+ false /* message_handler_thread */);
+ const char* source_1 = "a = 3; b = 4; c = new Object(); c.d = 5; debugger;";
+ const int kBufferSize = 1000;
+ uint16_t buffer[kBufferSize];
+ const char* command_1 =
+ "{\"seq\":117,"
+ "\"type\":\"request\","
+ "\"command\":\"evaluate\","
+ "\"arguments\":{\"expression\":\"1+2\"}}";
+ const char* command_2 =
+ "{\"seq\":118,"
+ "\"type\":\"request\","
+ "\"command\":\"evaluate\","
+ "\"arguments\":{\"expression\":\"1+a\"}}";
+ const char* command_continue =
+ "{\"seq\":106,"
+ "\"type\":\"request\","
+ "\"command\":\"continue\"}";
+
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_1, buffer),
+ new TestClientData());
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_2, buffer), NULL);
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_2, buffer),
+ new TestClientData());
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_2, buffer),
+ new TestClientData());
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer));
+ CompileRun(source_1);
+ ASSERT_EQ(3, TestClientData::constructor_call_counter);
+ ASSERT_EQ(TestClientData::constructor_call_counter,
+ handled_client_data_instances_count);
+ ASSERT_EQ(TestClientData::constructor_call_counter,
+ TestClientData::destructor_call_counter);
+}
+
+
/* Test ThreadedDebugging */
/* This test interrupts a running infinite loop that is
* occupying the v8 thread by a break command from the
@@ -3454,7 +3670,7 @@
static void ThreadedMessageHandler(const uint16_t* message, int length,
- void *data) {
+ v8::Debug::ClientData* client_data) {
static char print_buffer[1000];
Utf16ToAscii(message, length, print_buffer);
if (IsBreakEventMessage(print_buffer)) {
@@ -3552,7 +3768,7 @@
static void BreakpointsMessageHandler(const uint16_t* message,
int length,
- void *data) {
+ v8::Debug::ClientData* client_data) {
static char print_buffer[1000];
Utf16ToAscii(message, length, print_buffer);
printf("%s\n", print_buffer);
@@ -3698,7 +3914,8 @@
static void DummyMessageHandler(const uint16_t* message,
- int length, void *data) {
+ int length,
+ v8::Debug::ClientData* client_data) {
}
@@ -3924,7 +4141,8 @@
// Debugger message handler which counts the number of times it is called.
static int message_handler_hit_count = 0;
static void MessageHandlerHitCount(const uint16_t* message,
- int length, void* data) {
+ int length,
+ v8::Debug::ClientData* client_data) {
message_handler_hit_count++;
const int kBufferSize = 1000;
@@ -3972,9 +4190,10 @@
// Debugger message handler which clears the message handler while active.
-static void MessageHandlerClearingMessageHandler(const uint16_t* message,
- int length,
- void* data) {
+static void MessageHandlerClearingMessageHandler(
+ const uint16_t* message,
+ int length,
+ v8::Debug::ClientData* client_data) {
message_handler_hit_count++;
// Clear debug message handler.
@@ -4004,54 +4223,107 @@
}
-int host_dispatch_hit_count = 0;
-static void HostDispatchHandlerHitCount(void* dispatch, void *data) {
- host_dispatch_hit_count++;
+/* Test DebuggerHostDispatch */
+/* In this test, the debugger waits for a command on a breakpoint
+ * and is dispatching host commands while in the infinite loop.
+ */
+
+class HostDispatchV8Thread : public v8::internal::Thread {
+ public:
+ void Run();
+};
+
+class HostDispatchDebuggerThread : public v8::internal::Thread {
+ public:
+ void Run();
+};
+
+Barriers* host_dispatch_barriers;
+
+static void HostDispatchMessageHandler(const uint16_t* message,
+ int length,
+ v8::Debug::ClientData* client_data) {
+ static char print_buffer[1000];
+ Utf16ToAscii(message, length, print_buffer);
+ printf("%s\n", print_buffer);
+ fflush(stdout);
}
-// Test that clearing the debug event listener actually clears all break points
-// and related information.
-TEST(DebuggerHostDispatch) {
- i::FLAG_debugger_auto_break = true;
+static void HostDispatchDispatchHandler() {
+ host_dispatch_barriers->semaphore_1->Signal();
+}
+
+
+void HostDispatchV8Thread::Run() {
+ const char* source_1 = "var y_global = 3;\n"
+ "function cat( new_value ) {\n"
+ " var x = new_value;\n"
+ " y_global = 4;\n"
+ " x = 3 * x + 1;\n"
+ " y_global = 5;\n"
+ " return x;\n"
+ "}\n"
+ "\n";
+ const char* source_2 = "cat(17);\n";
v8::HandleScope scope;
DebugLocalContext env;
- const int kBufferSize = 1000;
- uint16_t buffer[kBufferSize];
- const char* command_continue =
- "{\"seq\":0,"
- "\"type\":\"request\","
- "\"command\":\"continue\"}";
-
- // Create an empty function to call for processing debug commands
- v8::Local<v8::Function> empty =
- CompileFunction(&env, "function empty(){}", "empty");
-
// Setup message and host dispatch handlers.
- v8::Debug::SetMessageHandler(DummyMessageHandler);
- v8::Debug::SetHostDispatchHandler(HostDispatchHandlerHitCount,
- NULL);
+ v8::Debug::SetMessageHandler(HostDispatchMessageHandler);
+ v8::Debug::SetHostDispatchHandler(HostDispatchDispatchHandler, 10 /* ms */);
- // Send a host dispatch by itself.
- v8::Debug::SendHostDispatch(NULL);
- empty->Call(env->Global(), 0, NULL); // Run JavaScript to activate debugger.
- CHECK_EQ(1, host_dispatch_hit_count);
+ CompileRun(source_1);
+ host_dispatch_barriers->barrier_1.Wait();
+ host_dispatch_barriers->barrier_2.Wait();
+ CompileRun(source_2);
+}
- // Fill a host dispatch and a continue command on the command queue.
- v8::Debug::SendHostDispatch(NULL);
- v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer));
- empty->Call(env->Global(), 0, NULL); // Run JavaScript to activate debugger.
- // Fill a continue command and a host dispatch on the command queue.
- v8::Debug::SendCommand(buffer, AsciiToUtf16(command_continue, buffer));
- v8::Debug::SendHostDispatch(NULL);
- empty->Call(env->Global(), 0, NULL); // Run JavaScript to activate debugger.
- empty->Call(env->Global(), 0, NULL); // Run JavaScript to activate debugger.
+void HostDispatchDebuggerThread::Run() {
+ const int kBufSize = 1000;
+ uint16_t buffer[kBufSize];
- // All the host dispatch callback should be called.
- CHECK_EQ(3, host_dispatch_hit_count);
+ const char* command_1 = "{\"seq\":101,"
+ "\"type\":\"request\","
+ "\"command\":\"setbreakpoint\","
+ "\"arguments\":{\"type\":\"function\",\"target\":\"cat\",\"line\":3}}";
+ const char* command_2 = "{\"seq\":102,"
+ "\"type\":\"request\","
+ "\"command\":\"continue\"}";
+
+ // v8 thread initializes, runs source_1
+ host_dispatch_barriers->barrier_1.Wait();
+ // 1: Set breakpoint in cat().
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_1, buffer));
+
+ host_dispatch_barriers->barrier_2.Wait();
+ // v8 thread starts compiling source_2.
+ // Break happens, to run queued commands and host dispatches.
+ // Wait for host dispatch to be processed.
+ host_dispatch_barriers->semaphore_1->Wait();
+ // 2: Continue evaluation
+ v8::Debug::SendCommand(buffer, AsciiToUtf16(command_2, buffer));
+}
+
+HostDispatchDebuggerThread host_dispatch_debugger_thread;
+HostDispatchV8Thread host_dispatch_v8_thread;
+
+
+TEST(DebuggerHostDispatch) {
+ i::FLAG_debugger_auto_break = true;
+
+ // Create a V8 environment
+ Barriers stack_allocated_host_dispatch_barriers;
+ stack_allocated_host_dispatch_barriers.Initialize();
+ host_dispatch_barriers = &stack_allocated_host_dispatch_barriers;
+
+ host_dispatch_v8_thread.Start();
+ host_dispatch_debugger_thread.Start();
+
+ host_dispatch_v8_thread.Join();
+ host_dispatch_debugger_thread.Join();
}
@@ -4249,3 +4521,58 @@
// Must not crash while accessing line_ends.
i::FLAG_allow_natives_syntax = allow_natives_syntax;
}
+
+
+// Test script break points set on lines.
+TEST(ScriptNameAndData) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ env.ExposeDebug();
+
+ // Create functions for retrieving script name and data for the function on
+ // the top frame when hitting a break point.
+ frame_script_name = CompileFunction(&env,
+ frame_script_name_source,
+ "frame_script_name");
+ frame_script_data = CompileFunction(&env,
+ frame_script_data_source,
+ "frame_script_data");
+
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+
+ // Test function source.
+ v8::Local<v8::String> script = v8::String::New(
+ "function f() {\n"
+ " debugger;\n"
+ "}\n");
+
+ v8::ScriptOrigin origin1 = v8::ScriptOrigin(v8::String::New("name"));
+ v8::Handle<v8::Script> script1 = v8::Script::Compile(script, &origin1);
+ script1->SetData(v8::String::New("data"));
+ script1->Run();
+ v8::Script::Compile(script, &origin1)->Run();
+ v8::Local<v8::Function> f;
+ f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(1, break_point_hit_count);
+ CHECK_EQ("name", last_script_name_hit);
+ CHECK_EQ("data", last_script_data_hit);
+
+ v8::Local<v8::String> data_obj_source = v8::String::New(
+ "({ a: 'abc',\n"
+ " b: 123,\n"
+ " toString: function() { return this.a + ' ' + this.b; }\n"
+ "})\n");
+ v8::Local<v8::Value> data_obj = v8::Script::Compile(data_obj_source)->Run();
+ v8::ScriptOrigin origin2 = v8::ScriptOrigin(v8::String::New("new name"));
+ v8::Handle<v8::Script> script2 = v8::Script::Compile(script, &origin2);
+ script2->Run();
+ script2->SetData(data_obj);
+ f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(2, break_point_hit_count);
+ CHECK_EQ("new name", last_script_name_hit);
+ CHECK_EQ("abc 123", last_script_data_hit);
+}
diff --git a/test/cctest/test-func-name-inference.cc b/test/cctest/test-func-name-inference.cc
index 5058629..d91f75f 100644
--- a/test/cctest/test-func-name-inference.cc
+++ b/test/cctest/test-func-name-inference.cc
@@ -221,3 +221,30 @@
CheckFunctionName(script, "return 2", "");
CheckFunctionName(script, "return 3", "");
}
+
+
+TEST(MultipleFuncsConditional) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ v8::Handle<v8::Script> script = Compile(
+ "fun1 = 0 ?\n"
+ " function() { return 1; } :\n"
+ " function() { return 2; }");
+ CheckFunctionName(script, "return 1", "fun1");
+ CheckFunctionName(script, "return 2", "fun1");
+}
+
+
+TEST(MultipleFuncsInLiteral) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ v8::Handle<v8::Script> script = Compile(
+ "function MyClass() {}\n"
+ "MyClass.prototype = {\n"
+ " method1: 0 ? function() { return 1; } :\n"
+ " function() { return 2; } }");
+ CheckFunctionName(script, "return 1", "MyClass.method1");
+ CheckFunctionName(script, "return 2", "MyClass.method1");
+}
diff --git a/test/mjsunit/array-reduce.js b/test/mjsunit/array-reduce.js
new file mode 100644
index 0000000..e476e1c
--- /dev/null
+++ b/test/mjsunit/array-reduce.js
@@ -0,0 +1,514 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/**
+ * @fileoverview Test reduce and reduceRight
+ */
+
+function clone(v) {
+ // Shallow-copies arrays, returns everything else verbatim.
+ if (v instanceof Array) {
+ // Shallow-copy an array.
+ var newArray = new Array(v.length);
+ for (var i in v) {
+ newArray[i] = v[i];
+ }
+ return newArray;
+ }
+ return v;
+}
+
+
+// Creates a callback function for reduce/reduceRight that tests the number
+// of arguments and otherwise behaves as "func", but which also
+// records all calls in an array on the function (as arrays of arguments
+// followed by result).
+function makeRecorder(func, testName) {
+ var record = [];
+ var f = function recorder(a, b, i, s) {
+ assertEquals(4, arguments.length,
+ testName + "(number of arguments: " + arguments.length + ")");
+ assertEquals("number", typeof(i), testName + "(index must be number)");
+ assertEquals(s[i], b, testName + "(current argument is at index)");
+ if (record.length > 0) {
+ var prevRecord = record[record.length - 1];
+ var prevResult = prevRecord[prevRecord.length - 1];
+ assertEquals(prevResult, a,
+ testName + "(prev result -> current input)");
+ }
+ var args = [clone(a), clone(b), i, clone(s)];
+ var result = func.apply(this, arguments);
+ args.push(clone(result));
+ record.push(args);
+ return result;
+ };
+ f.record = record;
+ return f;
+}
+
+
+function testReduce(type,
+ testName,
+ expectedResult,
+ expectedCalls,
+ array,
+ combine,
+ init) {
+ var rec = makeRecorder(combine);
+ var result;
+ var performsCall;
+ if (arguments.length > 6) {
+ result = array[type](rec, init);
+ } else {
+ result = array[type](rec);
+ }
+ var calls = rec.record;
+ assertEquals(expectedCalls.length, calls.length,
+ testName + " (number of calls)");
+ for (var i = 0; i < expectedCalls.length; i++) {
+ assertEquals(expectedCalls[i], calls[i],
+ testName + " (call " + (i + 1) + ")");
+ }
+ assertEquals(expectedResult, result, testName + " (result)");
+}
+
+
+function sum(a, b) { return a + b; }
+function prod(a, b) { return a * b; }
+function dec(a, b, i, arr) { return a + b * Math.pow(10, arr.length - i - 1); }
+function accumulate(acc, elem, i) { acc[i] = elem; return acc; }
+
+// ---- Test Reduce[Left]
+
+var simpleArray = [2,4,6]
+
+testReduce("reduce", "SimpleReduceSum", 12,
+ [[0, 2, 0, simpleArray, 2],
+ [2, 4, 1, simpleArray, 6],
+ [6, 6, 2, simpleArray, 12]],
+ simpleArray, sum, 0);
+
+testReduce("reduce", "SimpleReduceProd", 48,
+ [[1, 2, 0, simpleArray, 2],
+ [2, 4, 1, simpleArray, 8],
+ [8, 6, 2, simpleArray, 48]],
+ simpleArray, prod, 1);
+
+testReduce("reduce", "SimpleReduceDec", 246,
+ [[0, 2, 0, simpleArray, 200],
+ [200, 4, 1, simpleArray, 240],
+ [240, 6, 2, simpleArray, 246]],
+ simpleArray, dec, 0);
+
+testReduce("reduce", "SimpleReduceAccumulate", simpleArray,
+ [[[], 2, 0, simpleArray, [2]],
+ [[2], 4, 1, simpleArray, [2, 4]],
+ [[2,4], 6, 2, simpleArray, simpleArray]],
+ simpleArray, accumulate, []);
+
+
+testReduce("reduce", "EmptyReduceSum", 0, [], [], sum, 0);
+testReduce("reduce", "EmptyReduceProd", 1, [], [], prod, 1);
+testReduce("reduce", "EmptyReduceDec", 0, [], [], dec, 0);
+testReduce("reduce", "EmptyReduceAccumulate", [], [], [], accumulate, []);
+
+testReduce("reduce", "EmptyReduceSumNoInit", 0, [], [0], sum);
+testReduce("reduce", "EmptyReduceProdNoInit", 1, [], [1], prod);
+testReduce("reduce", "EmptyReduceDecNoInit", 0, [], [0], dec);
+testReduce("reduce", "EmptyReduceAccumulateNoInit", [], [], [[]], accumulate);
+
+
+var simpleSparseArray = [,,,2,,4,,6,,];
+testReduce("reduce", "SimpleSparseReduceSum", 12,
+ [[0, 2, 3, simpleSparseArray, 2],
+ [2, 4, 5, simpleSparseArray, 6],
+ [6, 6, 7, simpleSparseArray, 12]],
+ simpleSparseArray, sum, 0);
+
+testReduce("reduce", "SimpleSparseReduceProd", 48,
+ [[1, 2, 3, simpleSparseArray, 2],
+ [2, 4, 5, simpleSparseArray, 8],
+ [8, 6, 7, simpleSparseArray, 48]],
+ simpleSparseArray, prod, 1);
+
+testReduce("reduce", "SimpleSparseReduceDec", 204060,
+ [[0, 2, 3, simpleSparseArray, 200000],
+ [200000, 4, 5, simpleSparseArray, 204000],
+ [204000, 6, 7, simpleSparseArray, 204060]],
+ simpleSparseArray, dec, 0);
+
+testReduce("reduce", "SimpleSparseReduceAccumulate", [,,,2,,4,,6],
+ [[[], 2, 3, simpleSparseArray, [,,,2]],
+ [[,,,2], 4, 5, simpleSparseArray, [,,,2,,4]],
+ [[,,,2,,4], 6, 7, simpleSparseArray, [,,,2,,4,,6]]],
+ simpleSparseArray, accumulate, []);
+
+
+testReduce("reduce", "EmptySparseReduceSumNoInit", 0, [], [,,0,,], sum);
+testReduce("reduce", "EmptySparseReduceProdNoInit", 1, [], [,,1,,], prod);
+testReduce("reduce", "EmptySparseReduceDecNoInit", 0, [], [,,0,,], dec);
+testReduce("reduce", "EmptySparseReduceAccumulateNoInit",
+ [], [], [,,[],,], accumulate);
+
+
+var verySparseArray = [];
+verySparseArray.length = 10000;
+verySparseArray[2000] = 2;
+verySparseArray[5000] = 4;
+verySparseArray[9000] = 6;
+var verySparseSlice2 = verySparseArray.slice(0, 2001);
+var verySparseSlice4 = verySparseArray.slice(0, 5001);
+var verySparseSlice6 = verySparseArray.slice(0, 9001);
+
+testReduce("reduce", "VerySparseReduceSum", 12,
+ [[0, 2, 2000, verySparseArray, 2],
+ [2, 4, 5000, verySparseArray, 6],
+ [6, 6, 9000, verySparseArray, 12]],
+ verySparseArray, sum, 0);
+
+testReduce("reduce", "VerySparseReduceProd", 48,
+ [[1, 2, 2000, verySparseArray, 2],
+ [2, 4, 5000, verySparseArray, 8],
+ [8, 6, 9000, verySparseArray, 48]],
+ verySparseArray, prod, 1);
+
+testReduce("reduce", "VerySparseReduceDec", Infinity,
+ [[0, 2, 2000, verySparseArray, Infinity],
+ [Infinity, 4, 5000, verySparseArray, Infinity],
+ [Infinity, 6, 9000, verySparseArray, Infinity]],
+ verySparseArray, dec, 0);
+
+testReduce("reduce", "VerySparseReduceAccumulate",
+ verySparseSlice6,
+ [[[], 2, 2000, verySparseArray, verySparseSlice2],
+ [verySparseSlice2, 4, 5000, verySparseArray, verySparseSlice4],
+ [verySparseSlice4, 6, 9000, verySparseArray, verySparseSlice6]],
+ verySparseArray, accumulate, []);
+
+
+testReduce("reduce", "VerySparseReduceSumNoInit", 12,
+ [[2, 4, 5000, verySparseArray, 6],
+ [6, 6, 9000, verySparseArray, 12]],
+ verySparseArray, sum);
+
+testReduce("reduce", "VerySparseReduceProdNoInit", 48,
+ [[2, 4, 5000, verySparseArray, 8],
+ [8, 6, 9000, verySparseArray, 48]],
+ verySparseArray, prod);
+
+testReduce("reduce", "VerySparseReduceDecNoInit", Infinity,
+ [[2, 4, 5000, verySparseArray, Infinity],
+ [Infinity, 6, 9000, verySparseArray, Infinity]],
+ verySparseArray, dec);
+
+testReduce("reduce", "SimpleSparseReduceAccumulateNoInit",
+ 2,
+ [[2, 4, 5000, verySparseArray, 2],
+ [2, 6, 9000, verySparseArray, 2]],
+ verySparseArray, accumulate);
+
+
+// ---- Test ReduceRight
+
+testReduce("reduceRight", "SimpleReduceRightSum", 12,
+ [[0, 6, 2, simpleArray, 6],
+ [6, 4, 1, simpleArray, 10],
+ [10, 2, 0, simpleArray, 12]],
+ simpleArray, sum, 0);
+
+testReduce("reduceRight", "SimpleReduceRightProd", 48,
+ [[1, 6, 2, simpleArray, 6],
+ [6, 4, 1, simpleArray, 24],
+ [24, 2, 0, simpleArray, 48]],
+ simpleArray, prod, 1);
+
+testReduce("reduceRight", "SimpleReduceRightDec", 246,
+ [[0, 6, 2, simpleArray, 6],
+ [6, 4, 1, simpleArray, 46],
+ [46, 2, 0, simpleArray, 246]],
+ simpleArray, dec, 0);
+
+testReduce("reduceRight", "SimpleReduceRightAccumulate", simpleArray,
+ [[[], 6, 2, simpleArray, [,,6]],
+ [[,,6], 4, 1, simpleArray, [,4,6]],
+ [[,4,6], 2, 0, simpleArray, simpleArray]],
+ simpleArray, accumulate, []);
+
+
+testReduce("reduceRight", "EmptyReduceRightSum", 0, [], [], sum, 0);
+testReduce("reduceRight", "EmptyReduceRightProd", 1, [], [], prod, 1);
+testReduce("reduceRight", "EmptyReduceRightDec", 0, [], [], dec, 0);
+testReduce("reduceRight", "EmptyReduceRightAccumulate", [],
+ [], [], accumulate, []);
+
+testReduce("reduceRight", "EmptyReduceRightSumNoInit", 0, [], [0], sum);
+testReduce("reduceRight", "EmptyReduceRightProdNoInit", 1, [], [1], prod);
+testReduce("reduceRight", "EmptyReduceRightDecNoInit", 0, [], [0], dec);
+testReduce("reduceRight", "EmptyReduceRightAccumulateNoInit",
+ [], [], [[]], accumulate);
+
+
+testReduce("reduceRight", "SimpleSparseReduceRightSum", 12,
+ [[0, 6, 7, simpleSparseArray, 6],
+ [6, 4, 5, simpleSparseArray, 10],
+ [10, 2, 3, simpleSparseArray, 12]],
+ simpleSparseArray, sum, 0);
+
+testReduce("reduceRight", "SimpleSparseReduceRightProd", 48,
+ [[1, 6, 7, simpleSparseArray, 6],
+ [6, 4, 5, simpleSparseArray, 24],
+ [24, 2, 3, simpleSparseArray, 48]],
+ simpleSparseArray, prod, 1);
+
+testReduce("reduceRight", "SimpleSparseReduceRightDec", 204060,
+ [[0, 6, 7, simpleSparseArray, 60],
+ [60, 4, 5, simpleSparseArray, 4060],
+ [4060, 2, 3, simpleSparseArray, 204060]],
+ simpleSparseArray, dec, 0);
+
+testReduce("reduceRight", "SimpleSparseReduceRightAccumulate", [,,,2,,4,,6],
+ [[[], 6, 7, simpleSparseArray, [,,,,,,,6]],
+ [[,,,,,,,6], 4, 5, simpleSparseArray, [,,,,,4,,6]],
+ [[,,,,,4,,6], 2, 3, simpleSparseArray, [,,,2,,4,,6]]],
+ simpleSparseArray, accumulate, []);
+
+
+testReduce("reduceRight", "EmptySparseReduceRightSumNoInit",
+ 0, [], [,,0,,], sum);
+testReduce("reduceRight", "EmptySparseReduceRightProdNoInit",
+ 1, [], [,,1,,], prod);
+testReduce("reduceRight", "EmptySparseReduceRightDecNoInit",
+ 0, [], [,,0,,], dec);
+testReduce("reduceRight", "EmptySparseReduceRightAccumulateNoInit",
+ [], [], [,,[],,], accumulate);
+
+
+var verySparseSuffix6 = [];
+verySparseSuffix6[9000] = 6;
+var verySparseSuffix4 = [];
+verySparseSuffix4[5000] = 4;
+verySparseSuffix4[9000] = 6;
+var verySparseSuffix2 = verySparseSlice6;
+
+
+testReduce("reduceRight", "VerySparseReduceRightSum", 12,
+ [[0, 6, 9000, verySparseArray, 6],
+ [6, 4, 5000, verySparseArray, 10],
+ [10, 2, 2000, verySparseArray, 12]],
+ verySparseArray, sum, 0);
+
+testReduce("reduceRight", "VerySparseReduceRightProd", 48,
+ [[1, 6, 9000, verySparseArray, 6],
+ [6, 4, 5000, verySparseArray, 24],
+ [24, 2, 2000, verySparseArray, 48]],
+ verySparseArray, prod, 1);
+
+testReduce("reduceRight", "VerySparseReduceRightDec", Infinity,
+ [[0, 6, 9000, verySparseArray, Infinity],
+ [Infinity, 4, 5000, verySparseArray, Infinity],
+ [Infinity, 2, 2000, verySparseArray, Infinity]],
+ verySparseArray, dec, 0);
+
+testReduce("reduceRight", "VerySparseReduceRightAccumulate",
+ verySparseSuffix2,
+ [[[], 6, 9000, verySparseArray, verySparseSuffix6],
+ [verySparseSuffix6, 4, 5000, verySparseArray, verySparseSuffix4],
+ [verySparseSuffix4, 2, 2000, verySparseArray, verySparseSuffix2]],
+ verySparseArray, accumulate, []);
+
+
+testReduce("reduceRight", "VerySparseReduceRightSumNoInit", 12,
+ [[6, 4, 5000, verySparseArray, 10],
+ [10, 2, 2000, verySparseArray, 12]],
+ verySparseArray, sum);
+
+testReduce("reduceRight", "VerySparseReduceRightProdNoInit", 48,
+ [[6, 4, 5000, verySparseArray, 24],
+ [24, 2, 2000, verySparseArray, 48]],
+ verySparseArray, prod);
+
+testReduce("reduceRight", "VerySparseReduceRightDecNoInit", Infinity,
+ [[6, 4, 5000, verySparseArray, Infinity],
+ [Infinity, 2, 2000, verySparseArray, Infinity]],
+ verySparseArray, dec);
+
+testReduce("reduceRight", "SimpleSparseReduceRightAccumulateNoInit",
+ 6,
+ [[6, 4, 5000, verySparseArray, 6],
+ [6, 2, 2000, verySparseArray, 6]],
+ verySparseArray, accumulate);
+
+
+// undefined is an element
+var undefArray = [,,undefined,,undefined,,];
+
+testReduce("reduce", "SparseUndefinedReduceAdd", NaN,
+ [[0, undefined, 2, undefArray, NaN],
+ [NaN, undefined, 4, undefArray, NaN],
+ ],
+ undefArray, sum, 0);
+
+testReduce("reduceRight", "SparseUndefinedReduceRightAdd", NaN,
+ [[0, undefined, 4, undefArray, NaN],
+ [NaN, undefined, 2, undefArray, NaN],
+ ], undefArray, sum, 0);
+
+testReduce("reduce", "SparseUndefinedReduceAddNoInit", NaN,
+ [[undefined, undefined, 4, undefArray, NaN],
+ ], undefArray, sum);
+
+testReduce("reduceRight", "SparseUndefinedReduceRightAddNoInit", NaN,
+ [[undefined, undefined, 2, undefArray, NaN],
+ ], undefArray, sum);
+
+
+// Ignore non-array properties:
+
+var arrayPlus = [1,2,,3];
+arrayPlus[-1] = NaN;
+arrayPlus[Math.pow(2,32)] = NaN;
+arrayPlus[NaN] = NaN;
+arrayPlus["00"] = NaN;
+arrayPlus["02"] = NaN;
+arrayPlus["-0"] = NaN;
+
+testReduce("reduce", "ArrayWithNonElementPropertiesReduce", 6,
+ [[0, 1, 0, arrayPlus, 1],
+ [1, 2, 1, arrayPlus, 3],
+ [3, 3, 3, arrayPlus, 6],
+ ], arrayPlus, sum, 0);
+
+testReduce("reduceRight", "ArrayWithNonElementPropertiesReduceRight", 6,
+ [[0, 3, 3, arrayPlus, 3],
+ [3, 2, 1, arrayPlus, 5],
+ [5, 1, 0, arrayPlus, 6],
+ ], arrayPlus, sum, 0);
+
+
+// Test error conditions:
+
+try {
+ [1].reduce("not a function");
+ fail("Reduce callback not a function not throwing");
+} catch (e) {
+ assertTrue(e instanceof TypeError,
+ "reduce callback not a function not throwing TypeError");
+ assertEquals("called_non_callable", e.type,
+ "reduce non function TypeError type");
+}
+
+try {
+ [1].reduceRight("not a function");
+ fail("ReduceRight callback not a function not throwing");
+} catch (e) {
+ assertTrue(e instanceof TypeError,
+ "reduceRight callback not a function not throwing TypeError");
+ assertEquals("called_non_callable", e.type,
+ "reduceRight non function TypeError type");
+}
+
+
+try {
+ [].reduce(sum);
+ fail("Reduce no initial value not throwing");
+} catch (e) {
+ assertTrue(e instanceof TypeError,
+ "reduce no initial value not throwing TypeError");
+ assertEquals("reduce_no_initial", e.type,
+ "reduce no initial TypeError type");
+}
+
+try {
+ [].reduceRight(sum);
+ fail("ReduceRight no initial value not throwing");
+} catch (e) {
+ assertTrue(e instanceof TypeError,
+ "reduceRight no initial value not throwing TypeError");
+ assertEquals("reduce_no_initial", e.type,
+ "reduceRight no initial TypeError type");
+}
+
+
+try {
+ [,,,].reduce(sum);
+ fail("Reduce sparse no initial value not throwing");
+} catch (e) {
+ assertTrue(e instanceof TypeError,
+ "reduce sparse no initial value not throwing TypeError");
+ assertEquals("reduce_no_initial", e.type,
+ "reduce no initial TypeError type");
+}
+
+try {
+ [,,,].reduceRight(sum);
+ fail("ReduceRight sparse no initial value not throwing");
+} catch (e) {
+ assertTrue(e instanceof TypeError,
+ "reduceRight sparse no initial value not throwing TypeError");
+ assertEquals("reduce_no_initial", e.type,
+ "reduceRight no initial TypeError type");
+}
+
+
+// Array changing length
+
+function manipulator(a, b, i, s) {
+ if (s.length % 2) {
+ s[s.length * 3] = i;
+ } else {
+ s.length = s.length >> 1;
+ }
+ return a + b;
+}
+
+var arr = [1, 2, 3, 4];
+testReduce("reduce", "ArrayManipulationShort", 3,
+ [[0, 1, 0, [1, 2, 3, 4], 1],
+ [1, 2, 1, [1, 2], 3],
+ ], arr, manipulator, 0);
+
+var arr = [1, 2, 3, 4, 5];
+testReduce("reduce", "ArrayManipulationLonger", 10,
+ [[0, 1, 0, [1, 2, 3, 4, 5], 1],
+ [1, 2, 1, [1, 2, 3, 4, 5,,,,,,,,,,, 0], 3],
+ [3, 3, 2, [1, 2, 3, 4, 5,,,,], 6],
+ [6, 4, 3, [1, 2, 3, 4], 10],
+ ], arr, manipulator, 0);
+
+function extender(a, b, i, s) {
+ s[s.length] = s.length;
+ return a + b;
+}
+
+var arr = [1, 2, 3, 4];
+testReduce("reduce", "ArrayManipulationExtender", 10,
+ [[0, 1, 0, [1, 2, 3, 4], 1],
+ [1, 2, 1, [1, 2, 3, 4, 4], 3],
+ [3, 3, 2, [1, 2, 3, 4, 4, 5], 6],
+ [6, 4, 3, [1, 2, 3, 4, 4, 5, 6], 10],
+ ], arr, extender, 0);
+
diff --git a/test/mjsunit/constant-folding.js b/test/mjsunit/constant-folding.js
index 41b632f..4deb43c 100644
--- a/test/mjsunit/constant-folding.js
+++ b/test/mjsunit/constant-folding.js
@@ -168,4 +168,65 @@
assertEquals(17, j, "switch with constant value");
}
+
+function TrueToString() {
+ return true.toString();
+}
+
+
+function FalseToString() {
+ return false.toString();
+}
+
+
+function BoolTest() {
+ assertEquals("true", TrueToString());
+ assertEquals("true", TrueToString());
+ assertEquals("true", TrueToString());
+ assertEquals("false", FalseToString());
+ assertEquals("false", FalseToString());
+ assertEquals("false", FalseToString());
+ Boolean.prototype.toString = function() { return "foo"; }
+ assertEquals("foo", TrueToString());
+ assertEquals("foo", FalseToString());
+}
+
+
+// Some tests of shifts that get into the corners in terms of coverage.
+// We generate different code for the case where the operand is a constant.
+function ShiftTest() {
+ var x = 123;
+ assertEquals(x, x >> 0);
+ assertEquals(x, x << 0);
+ assertEquals(x, x >>> 0);
+ assertEquals(61, x >> 1);
+ assertEquals(246, x << 1);
+ assertEquals(61, x >>> 1);
+ x = -123;
+ assertEquals(x, x >> 0);
+ assertEquals(x, x << 0);
+ assertEquals(0x10000 * 0x10000 + x, x >>> 0);
+ assertEquals(-62, x >> 1);
+ assertEquals(-246, x << 1);
+ assertEquals(0x10000 * 0x8000 - 62, x >>> 1);
+ // Answer is non-Smi so the subtraction is not folded in the code
+ // generator.
+ assertEquals(-0x40000001, -0x3fffffff - 2);
+
+ x = 123;
+ assertEquals(0, x & 0);
+
+ // Answer is non-smi and lhs of << is a temporary heap number that we can
+ // overwrite.
+ x = 123.0001;
+ assertEquals(1073741824, (x * x) << 30);
+ x = 123;
+ // Answer is non-smi and lhs of << is a temporary heap number that we think
+ // we can overwrite (but we can't because it's a Smi).
+ assertEquals(1073741824, (x * x) << 30);
+}
+
+
test();
+BoolTest();
+ShiftTest();
diff --git a/test/mjsunit/debug-handle.js b/test/mjsunit/debug-handle.js
index 64a68a8..c7ab76a 100644
--- a/test/mjsunit/debug-handle.js
+++ b/test/mjsunit/debug-handle.js
@@ -95,8 +95,8 @@
// Test some illegal lookup requests.
lookupRequest(dcp, void 0, false);
- lookupRequest(dcp, '{"handle":"a"}', false);
- lookupRequest(dcp, '{"handle":-1}', false);
+ lookupRequest(dcp, '{"handles":["a"]}', false);
+ lookupRequest(dcp, '{"handles":[-1]}', false);
// Evaluate and get some handles.
var handle_o = evaluateRequest(dcp, '{"expression":"o"}');
@@ -109,24 +109,28 @@
var response;
var count;
- response = lookupRequest(dcp, '{"handle":' + handle_o + '}', true);
- assertEquals(handle_o, response.body.handle);
+ response = lookupRequest(dcp, '{"handles":[' + handle_o + ']}', true);
+ var obj = response.body[handle_o];
+ assertTrue(!!obj, 'Object not found: ' + handle_o);
+ assertEquals(handle_o, obj.handle);
count = 0;
- for (i in response.body.properties) {
- switch (response.body.properties[i].name) {
+ for (i in obj.properties) {
+ switch (obj.properties[i].name) {
case 'o':
- response.body.properties[i].ref = handle_o;
+ obj.properties[i].ref = handle_o;
count++;
break;
case 'p':
- response.body.properties[i].ref = handle_p;
+ obj.properties[i].ref = handle_p;
count++;
break;
}
}
assertEquals(2, count, 'Either "o" or "p" not found');
- response = lookupRequest(dcp, '{"handle":' + handle_p + '}', true);
- assertEquals(handle_p, response.body.handle);
+ response = lookupRequest(dcp, '{"handles":[' + handle_p + ']}', true);
+ obj = response.body[handle_p];
+ assertTrue(!!obj, 'Object not found: ' + handle_p);
+ assertEquals(handle_p, obj.handle);
// Check handles for functions on the stack.
var handle_f = evaluateRequest(dcp, '{"expression":"f"}');
@@ -136,28 +140,31 @@
assertFalse(handle_f == handle_g, "f and g have he same handle");
assertEquals(handle_g, handle_caller, "caller for f should be g");
- response = lookupRequest(dcp, '{"handle":' + handle_f + '}', true);
- assertEquals(handle_f, response.body.handle);
+ response = lookupRequest(dcp, '{"handles":[' + handle_f + ']}', true);
+ obj = response.body[handle_f];
+ assertEquals(handle_f, obj.handle);
+
count = 0;
- for (i in response.body.properties) {
- var arguments = '{"handle":' + response.body.properties[i].ref + '}'
- switch (response.body.properties[i].name) {
+ for (i in obj.properties) {
+ var ref = obj.properties[i].ref;
+ var arguments = '{"handles":[' + ref + ']}';
+ switch (obj.properties[i].name) {
case 'name':
var response_name;
response_name = lookupRequest(dcp, arguments, true);
- assertEquals('string', response_name.body.type);
- assertEquals("f", response_name.body.value);
+ assertEquals('string', response_name.body[ref].type);
+ assertEquals("f", response_name.body[ref].value);
count++;
break;
case 'length':
var response_length;
response_length = lookupRequest(dcp, arguments, true);
- assertEquals('number', response_length.body.type);
- assertEquals(1, response_length.body.value);
+ assertEquals('number', response_length.body[ref].type);
+ assertEquals(1, response_length.body[ref].value);
count++;
break;
case 'caller':
- assertEquals(handle_g, response.body.properties[i].ref);
+ assertEquals(handle_g, obj.properties[i].ref);
count++;
break;
}
@@ -165,6 +172,49 @@
assertEquals(3, count, 'Either "name", "length" or "caller" not found');
+ // Resolve all at once.
+ var refs = [];
+ for (i in obj.properties) {
+ refs.push(obj.properties[i].ref);
+ }
+
+ var arguments = '{"handles":[' + refs.join(',') + ']}';
+ response = lookupRequest(dcp, arguments, true);
+ count = 0;
+ for (i in obj.properties) {
+ var ref = obj.properties[i].ref;
+ var val = response.body[ref];
+ assertTrue(!!val, 'Failed to lookup "' + obj.properties[i].name + '"');
+ switch (obj.properties[i].name) {
+ case 'name':
+ assertEquals('string', val.type);
+ assertEquals("f", val.value);
+ count++;
+ break;
+ case 'length':
+ assertEquals('number', val.type);
+ assertEquals(1, val.value);
+ count++;
+ break;
+ case 'caller':
+ assertEquals('function', val.type);
+ assertEquals(handle_g, ref);
+ count++;
+ break;
+ }
+ }
+ assertEquals(3, count, 'Either "name", "length" or "caller" not found');
+
+ count = 0;
+ for (var handle in response.body) {
+ assertTrue(refs.indexOf(parseInt(handle)) != -1,
+ 'Handle not in the request: ' + handle);
+ count++;
+ }
+ assertEquals(count, obj.properties.length,
+ 'Unexpected number of resolved objects');
+
+
// Indicate that all was processed.
listenerComplete = true;
}
@@ -195,5 +245,5 @@
g(o);
// Make sure that the debug event listener vas invoked.
-assertTrue(listenerComplete, "listener did not run to completion");
+assertTrue(listenerComplete, "listener did not run to completion: " + exception);
assertFalse(exception, "exception in listener")
diff --git a/test/mjsunit/debug-setbreakpoint.js b/test/mjsunit/debug-setbreakpoint.js
index 904ec18..f8d9b15 100644
--- a/test/mjsunit/debug-setbreakpoint.js
+++ b/test/mjsunit/debug-setbreakpoint.js
@@ -54,14 +54,14 @@
var json_response = dcp.processDebugJSONRequest(request);
var response = safeEval(json_response);
if (success) {
- assertTrue(response.success, json_response);
+ assertTrue(response.success, request + ' -> ' + json_response);
if (is_script) {
- assertEquals('scriptName', response.body.type, json_response);
+ assertEquals('scriptName', response.body.type, request + ' -> ' + json_response);
} else {
- assertEquals('scriptId', response.body.type, json_response);
+ assertEquals('scriptId', response.body.type, request + ' -> ' + json_response);
}
} else {
- assertFalse(response.success, json_response);
+ assertFalse(response.success, request + ' -> ' + json_response);
}
}
@@ -75,6 +75,8 @@
var request = '{' + base_request + '}'
var response = safeEval(dcp.processDebugJSONRequest(request));
assertFalse(response.success);
+
+ var mirror;
testArguments(dcp, '{}', false);
testArguments(dcp, '{"type":"xx"}', false);
@@ -86,6 +88,9 @@
testArguments(dcp, '{"type":"function","target":"f","line":-1}', false);
testArguments(dcp, '{"type":"function","target":"f","column":-1}', false);
testArguments(dcp, '{"type":"function","target":"f","ignoreCount":-1}', false);
+ testArguments(dcp, '{"type":"handle","target":"-1"}', false);
+ mirror = debug.MakeMirror(o);
+ testArguments(dcp, '{"type":"handle","target":' + mirror.handle() + '}', false);
// Test some legal setbreakpoint requests.
testArguments(dcp, '{"type":"function","target":"f"}', true, false);
@@ -106,6 +111,11 @@
testArguments(dcp, '{"type":"scriptId","target":' + g_script_id + ',"line":' + g_line + '}', true, false);
testArguments(dcp, '{"type":"scriptId","target":' + h_script_id + ',"line":' + h_line + '}', true, false);
+ mirror = debug.MakeMirror(f);
+ testArguments(dcp, '{"type":"handle","target":' + mirror.handle() + '}', true, false);
+ mirror = debug.MakeMirror(o.a);
+ testArguments(dcp, '{"type":"handle","target":' + mirror.handle() + '}', true, false);
+
// Indicate that all was processed.
listenerComplete = true;
}
@@ -127,6 +137,8 @@
eval('function h(){}');
+o = {a:function(){},b:function(){}}
+
// Check the script ids for the test functions.
f_script_id = Debug.findScript(f).id;
g_script_id = Debug.findScript(g).id;
diff --git a/test/mjsunit/enumeration-order.js b/test/mjsunit/enumeration-order.js
index 699a636..a328121 100644
--- a/test/mjsunit/enumeration-order.js
+++ b/test/mjsunit/enumeration-order.js
@@ -26,17 +26,17 @@
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
function check_enumeration_order(obj) {
- var value = 0;
+ var value = 0;
for (var name in obj) assertTrue(value < obj[name]);
value = obj[name];
}
function make_object(size) {
var a = new Object();
-
+
for (var i = 0; i < size; i++) a["a_" + i] = i + 1;
check_enumeration_order(a);
-
+
for (var i = 0; i < size; i +=3) delete a["a_" + i];
check_enumeration_order(a);
}
@@ -51,9 +51,59 @@
code += "a_" + (size - 1) + " : " + size;
code += " }";
eval("var a = " + code);
- check_enumeration_order(a);
+ check_enumeration_order(a);
}
-// Validate the enumeration order for object literals up to 100 named properties.
+// Validate the enumeration order for object literals up to 100 named
+// properties.
for (var j = 1; j< 100; j++) make_literal_object(j);
+// We enumerate indexed properties in numerical order followed by
+// named properties in insertion order, followed by indexed properties
+// of the prototype object in numerical order, followed by named
+// properties of the prototype object in insertion order, and so on.
+//
+// This enumeration order is not required by the specification, so
+// this just documents our choice.
+var proto2 = {};
+proto2[140000] = 0;
+proto2.a = 0;
+proto2[2] = 0;
+proto2[3] = 0; // also on the 'proto1' object
+proto2.b = 0;
+proto2[4294967295] = 0;
+proto2.c = 0;
+proto2[4294967296] = 0;
+
+var proto1 = {};
+proto1[5] = 0;
+proto1.d = 0;
+proto1[3] = 0;
+proto1.e = 0;
+proto1.f = 0; // also on the 'o' object
+
+var o = {};
+o[-23] = 0;
+o[300000000000] = 0;
+o[23] = 0;
+o.f = 0;
+o.g = 0;
+o[-4] = 0;
+o[42] = 0;
+
+o.__proto__ = proto1;
+proto1.__proto__ = proto2;
+
+var expected = ['23', '42', // indexed from 'o'
+ '-23', '300000000000', 'f', 'g', '-4', // named from 'o'
+ '3', '5', // indexed from 'proto1'
+ 'd', 'e', // named from 'proto1'
+ '2', '140000', '4294967295', // indexed from 'proto2'
+ 'a', 'b', 'c', '4294967296']; // named from 'proto2'
+var actual = [];
+for (var p in o) actual.push(p);
+assertArrayEquals(expected, actual);
+
+
+
+
diff --git a/test/mjsunit/eval-enclosing-function-name.js b/test/mjsunit/eval-enclosing-function-name.js
new file mode 100644
index 0000000..422f03f
--- /dev/null
+++ b/test/mjsunit/eval-enclosing-function-name.js
@@ -0,0 +1,76 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// From within 'eval', the name of the enclosing function should be
+// visible.
+
+var f = function y() { return typeof y; };
+assertEquals("function", f());
+
+
+f = function y() { return eval('typeof y'); };
+assertEquals("function", f());
+
+
+f = function y() { y = 3; return typeof y; };
+assertEquals("function", f());
+
+
+f = function y() { y += 3; return typeof y; };
+assertEquals("function", f());
+
+
+f = function y() { y &= y; return typeof y; };
+assertEquals("function", f());
+
+
+f = function y() { y = 3; return eval('typeof y'); }
+assertEquals("function", f());
+
+
+f = function y() { var y = 3; return typeof y; }
+assertEquals("number", f());
+
+
+f = function y() { var y = 3; return eval('typeof y'); }
+assertEquals("number", f());
+
+
+f = function y() { eval('y = 3'); return typeof y; }
+assertEquals("function", f());
+
+
+f = function y() { eval('y = 3'); return eval('typeof y'); }
+assertEquals("function", f());
+
+
+f = function y() { eval('var y = 3'); return typeof y; }
+assertEquals("number", f());
+
+
+f = function y() { eval('var y = 3'); return eval('typeof y'); }
+assertEquals("number", f());
diff --git a/test/mjsunit/keyed-ic.js b/test/mjsunit/keyed-ic.js
index d37bd03..a6726ed 100644
--- a/test/mjsunit/keyed-ic.js
+++ b/test/mjsunit/keyed-ic.js
@@ -145,6 +145,35 @@
// ----------------------------------------------------------------------
+// Indexed access.
+// ----------------------------------------------------------------------
+runTest = function() {
+ var o = [ 42, 43 ];
+
+ var initial_X = 0;
+ var X = initial_X;
+ var Y = 1;
+
+ function fieldTest(change_index) {
+ for (var i = 0; i < 10; i++) {
+ var property = o[X];
+ if (i <= change_index) {
+ assertEquals(42, property);
+ } else {
+ assertEquals(43, property);
+ }
+ if (i == change_index) X = Y;
+ }
+ X = initial_X;
+ };
+
+ for (var i = 0; i < 10; i++) fieldTest(i);
+}
+
+runTest();
+
+
+// ----------------------------------------------------------------------
// Constant function access.
// ----------------------------------------------------------------------
runTest = function() {
diff --git a/test/mjsunit/mjsunit.js b/test/mjsunit/mjsunit.js
index 3570d68..320e8d1 100644
--- a/test/mjsunit/mjsunit.js
+++ b/test/mjsunit/mjsunit.js
@@ -53,6 +53,9 @@
function deepEquals(a, b) {
if (a == b) return true;
+ if (typeof a == "number" && typeof b == "number" && isNaN(a) && isNaN(b)) {
+ return true;
+ }
if ((typeof a) !== 'object' || (typeof b) !== 'object' ||
(a === null) || (b === null))
return false;
@@ -113,6 +116,20 @@
}
+function assertNull(value, name_opt) {
+ if (value !== null) {
+ fail("null", value, name_opt);
+ }
+}
+
+
+function assertNotNull(value, name_opt) {
+ if (value === null) {
+ fail("not null", value, name_opt);
+ }
+}
+
+
function assertThrows(code) {
var threwException = true;
try {
diff --git a/test/mjsunit/mjsunit.status b/test/mjsunit/mjsunit.status
index 5a76078..4bb7c16 100644
--- a/test/mjsunit/mjsunit.status
+++ b/test/mjsunit/mjsunit.status
@@ -48,19 +48,19 @@
# Bug number 1020483: Debug tests fail on ARM.
debug-constructor: CRASH, FAIL
debug-continue: SKIP
-debug-evaluate-recursive: CRASH, FAIL if $mode == debug
-debug-changebreakpoint: CRASH, FAIL if $mode == debug
-debug-clearbreakpoint: CRASH, FAIL if $mode == debug
-debug-conditional-breakpoints: CRASH, FAIL if $mode == debug
-debug-evaluate: CRASH, FAIL if $mode == debug
-debug-ignore-breakpoints: CRASH, FAIL if $mode == debug
-debug-multiple-breakpoints: CRASH, FAIL if $mode == debug
-debug-setbreakpoint: CRASH, FAIL if $mode == debug
+debug-evaluate-recursive: CRASH || FAIL
+debug-changebreakpoint: CRASH || FAIL
+debug-clearbreakpoint: CRASH || FAIL
+debug-conditional-breakpoints: FAIL
+debug-evaluate: CRASH || FAIL
+debug-ignore-breakpoints: CRASH || FAIL
+debug-multiple-breakpoints: CRASH || FAIL
+debug-setbreakpoint: CRASH || FAIL || PASS
debug-step-stub-callfunction: SKIP
debug-stepin-constructor: CRASH, FAIL
debug-step: SKIP
debug-breakpoints: PASS || FAIL
-debug-handle: CRASH, FAIL if $mode == debug
+debug-handle: CRASH || FAIL || PASS
regress/regress-269: SKIP
# Bug number 130 http://code.google.com/p/v8/issues/detail?id=130
diff --git a/test/mjsunit/negate.js b/test/mjsunit/negate.js
index 3bf4111..70daf24 100644
--- a/test/mjsunit/negate.js
+++ b/test/mjsunit/negate.js
@@ -30,9 +30,9 @@
function testmulneg(a, b) {
var base = a * b;
- assertEquals(-base, a * -b);
- assertEquals(-base, -a * b);
- assertEquals(base, -a * -b);
+ assertEquals(-base, a * -b, "a * -b where a = " + a + ", b = " + b);
+ assertEquals(-base, -a * b, "-a * b where a = " + a + ", b = " + b);
+ assertEquals(base, -a * -b, "*-a * -b where a = " + a + ", b = " + b);
}
testmulneg(2, 3);
diff --git a/test/mjsunit/number-limits.js b/test/mjsunit/number-limits.js
index 1d9a1e5..99ed4e1 100644
--- a/test/mjsunit/number-limits.js
+++ b/test/mjsunit/number-limits.js
@@ -33,10 +33,14 @@
var addAboveMax = Number.MAX_VALUE + 1/eps;
var mulBelowMin = Number.MIN_VALUE * (1 - eps);
var addBelowMin = Number.MIN_VALUE - eps;
- assertTrue(mulAboveMax == Number.MAX_VALUE || mulAboveMax == Infinity);
- assertTrue(addAboveMax == Number.MAX_VALUE || addAboveMax == Infinity);
- assertTrue(mulBelowMin == Number.MIN_VALUE || mulBelowMin <= 0);
- assertTrue(addBelowMin == Number.MIN_VALUE || addBelowMin <= 0);
+ assertTrue(mulAboveMax == Number.MAX_VALUE ||
+ mulAboveMax == Infinity, "mul" + i);
+ assertTrue(addAboveMax == Number.MAX_VALUE ||
+ addAboveMax == Infinity, "add" + i);
+ assertTrue(mulBelowMin == Number.MIN_VALUE ||
+ mulBelowMin <= 0, "mul2" + i);
+ assertTrue(addBelowMin == Number.MIN_VALUE ||
+ addBelowMin <= 0, "add2" + i);
}
}
diff --git a/test/mjsunit/receiver-in-with-calls.js b/test/mjsunit/receiver-in-with-calls.js
new file mode 100644
index 0000000..5f2bdac
--- /dev/null
+++ b/test/mjsunit/receiver-in-with-calls.js
@@ -0,0 +1,47 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// When invoking functions from within a 'with' statement, we must set
+// the receiver to the object where we found the function.
+
+(function () {
+ var x = { get_this: function() { return this; } };
+ assertTrue(x === x.get_this());
+ with (x) assertTrue(x === get_this());
+})();
+
+
+assertTrue({ f: function() {
+ function g() { return this; };
+ return eval("g")();
+} }.f() == this);
+
+
+assertTrue({ f: function() {
+ function g() { return this; };
+ return eval("g()");
+} }.f() == this);
diff --git a/test/mjsunit/regexp-multiline-stack-trace.js b/test/mjsunit/regexp-multiline-stack-trace.js
index aa2de88..fc248ef 100644
--- a/test/mjsunit/regexp-multiline-stack-trace.js
+++ b/test/mjsunit/regexp-multiline-stack-trace.js
@@ -25,6 +25,8 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+// The flags below are to test the trace-calls functionality and the
+// preallocated meessage memory.
// Flags: --trace-calls --preallocate-message-memory
/**
diff --git a/test/mjsunit/regress/regress-318.js b/test/mjsunit/regress/regress-318.js
index 9e00b5f..e94f1cb 100644
--- a/test/mjsunit/regress/regress-318.js
+++ b/test/mjsunit/regress/regress-318.js
@@ -25,11 +25,11 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// Should not raise an exception.
+// Should not crash or raise an exception.
function test(value) {
if (typeof(value) == 'boolean') value = value + '';
if (typeof(value) == 'number') value = value + '';
}
-assertDoesNotThrow(test(0));
+assertDoesNotThrow('test(0)');
diff --git a/test/mjsunit/smi-ops.js b/test/mjsunit/smi-ops.js
index bdd7509..80d2012 100644
--- a/test/mjsunit/smi-ops.js
+++ b/test/mjsunit/smi-ops.js
@@ -100,3 +100,98 @@
assertEquals(ONE_HUNDRED - SMI_MIN, Sub100Reversed(SMI_MIN)); // overflow
assertEquals(42 - ONE_HUNDRED, Sub100(OBJ_42)); // non-smi
assertEquals(ONE_HUNDRED - 42, Sub100Reversed(OBJ_42)); // non-smi
+
+
+function Shr1(x) {
+ return x >>> 1;
+}
+
+function Shr100(x) {
+ return x >>> 100;
+}
+
+function Shr1Reversed(x) {
+ return 1 >>> x;
+}
+
+function Shr100Reversed(x) {
+ return 100 >>> x;
+}
+
+function Sar1(x) {
+ return x >> 1;
+}
+
+function Sar100(x) {
+ return x >> 100;
+}
+
+function Sar1Reversed(x) {
+ return 1 >> x;
+}
+
+function Sar100Reversed(x) {
+ return 100 >> x;
+}
+
+
+assertEquals(0, Shr1(1));
+assertEquals(0, Sar1(1));
+assertEquals(0, Shr1Reversed(2));
+assertEquals(0, Sar1Reversed(2));
+assertEquals(1610612736, Shr1(SMI_MIN));
+assertEquals(-536870912, Sar1(SMI_MIN));
+assertEquals(1, Shr1Reversed(SMI_MIN));
+assertEquals(1, Sar1Reversed(SMI_MIN));
+assertEquals(21, Shr1(OBJ_42));
+assertEquals(21, Sar1(OBJ_42));
+assertEquals(0, Shr1Reversed(OBJ_42));
+assertEquals(0, Sar1Reversed(OBJ_42));
+
+assertEquals(6, Shr100(100));
+assertEquals(6, Sar100(100));
+assertEquals(12, Shr100Reversed(99));
+assertEquals(12, Sar100Reversed(99));
+assertEquals(201326592, Shr100(SMI_MIN));
+assertEquals(-67108864, Sar100(SMI_MIN));
+assertEquals(100, Shr100Reversed(SMI_MIN));
+assertEquals(100, Sar100Reversed(SMI_MIN));
+assertEquals(2, Shr100(OBJ_42));
+assertEquals(2, Sar100(OBJ_42));
+assertEquals(0, Shr100Reversed(OBJ_42));
+assertEquals(0, Sar100Reversed(OBJ_42));
+
+
+function Xor1(x) {
+ return x ^ 1;
+}
+
+function Xor100(x) {
+ return x ^ 100;
+}
+
+function Xor1Reversed(x) {
+ return 1 ^ x;
+}
+
+function Xor100Reversed(x) {
+ return 100 ^ x;
+}
+
+
+assertEquals(0, Xor1(1));
+assertEquals(3, Xor1Reversed(2));
+assertEquals(SMI_MIN + 1, Xor1(SMI_MIN));
+assertEquals(SMI_MIN + 1, Xor1Reversed(SMI_MIN));
+assertEquals(43, Xor1(OBJ_42));
+assertEquals(43, Xor1Reversed(OBJ_42));
+
+assertEquals(0, Xor100(100));
+assertEquals(7, Xor100Reversed(99));
+assertEquals(-1073741724, Xor100(SMI_MIN));
+assertEquals(-1073741724, Xor100Reversed(SMI_MIN));
+assertEquals(78, Xor100(OBJ_42));
+assertEquals(78, Xor100Reversed(OBJ_42));
+
+var x = 0x23; var y = 0x35;
+assertEquals(0x16, x ^ y);
diff --git a/test/mjsunit/tools/codemap.js b/test/mjsunit/tools/codemap.js
new file mode 100644
index 0000000..7f344a6
--- /dev/null
+++ b/test/mjsunit/tools/codemap.js
@@ -0,0 +1,116 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Load Splay tree and CodeMap implementations from <project root>/tools.
+// Files: tools/splaytree.js tools/codemap.js
+
+
+function newCodeEntry(size, name) {
+ return new devtools.profiler.CodeMap.CodeEntry(size, name);
+};
+
+
+function assertEntry(codeMap, expected_name, addr) {
+ var entry = codeMap.findEntry(addr);
+ assertNotNull(entry, 'no entry at ' + addr.toString(16));
+ assertEquals(expected_name, entry.name, 'at ' + addr.toString(16));
+};
+
+
+function assertNoEntry(codeMap, addr) {
+ assertNull(codeMap.findEntry(addr), 'at ' + addr.toString(16));
+};
+
+
+(function testStaticCode() {
+ var codeMap = new devtools.profiler.CodeMap();
+ codeMap.addStaticCode(0x1500, newCodeEntry(0x3000, 'lib1'));
+ codeMap.addStaticCode(0x15500, newCodeEntry(0x5000, 'lib2'));
+ codeMap.addStaticCode(0x155500, newCodeEntry(0x10000, 'lib3'));
+ assertNoEntry(codeMap, 0);
+ assertNoEntry(codeMap, 0x1500 - 1);
+ assertEntry(codeMap, 'lib1', 0x1500);
+ assertEntry(codeMap, 'lib1', 0x1500 + 0x100);
+ assertEntry(codeMap, 'lib1', 0x1500 + 0x1000);
+ assertEntry(codeMap, 'lib1', 0x1500 + 0x3000 - 1);
+ assertNoEntry(codeMap, 0x1500 + 0x3000);
+ assertNoEntry(codeMap, 0x15500 - 1);
+ assertEntry(codeMap, 'lib2', 0x15500);
+ assertEntry(codeMap, 'lib2', 0x15500 + 0x100);
+ assertEntry(codeMap, 'lib2', 0x15500 + 0x1000);
+ assertEntry(codeMap, 'lib2', 0x15500 + 0x5000 - 1);
+ assertNoEntry(codeMap, 0x15500 + 0x5000);
+ assertNoEntry(codeMap, 0x155500 - 1);
+ assertEntry(codeMap, 'lib3', 0x155500);
+ assertEntry(codeMap, 'lib3', 0x155500 + 0x100);
+ assertEntry(codeMap, 'lib3', 0x155500 + 0x1000);
+ assertEntry(codeMap, 'lib3', 0x155500 + 0x10000 - 1);
+ assertNoEntry(codeMap, 0x155500 + 0x10000);
+ assertNoEntry(codeMap, 0xFFFFFFFF);
+})();
+
+
+(function testDynamicCode() {
+ var codeMap = new devtools.profiler.CodeMap();
+ codeMap.addCode(0x1500, newCodeEntry(0x200, 'code1'));
+ codeMap.addCode(0x1700, newCodeEntry(0x100, 'code2'));
+ codeMap.addCode(0x1900, newCodeEntry(0x50, 'code3'));
+ codeMap.addCode(0x1950, newCodeEntry(0x10, 'code4'));
+ assertNoEntry(codeMap, 0);
+ assertNoEntry(codeMap, 0x1500 - 1);
+ assertEntry(codeMap, 'code1', 0x1500);
+ assertEntry(codeMap, 'code1', 0x1500 + 0x100);
+ assertEntry(codeMap, 'code1', 0x1500 + 0x200 - 1);
+ assertEntry(codeMap, 'code2', 0x1700);
+ assertEntry(codeMap, 'code2', 0x1700 + 0x50);
+ assertEntry(codeMap, 'code2', 0x1700 + 0x100 - 1);
+ assertNoEntry(codeMap, 0x1700 + 0x100);
+ assertNoEntry(codeMap, 0x1900 - 1);
+ assertEntry(codeMap, 'code3', 0x1900);
+ assertEntry(codeMap, 'code3', 0x1900 + 0x28);
+ assertEntry(codeMap, 'code4', 0x1950);
+ assertEntry(codeMap, 'code4', 0x1950 + 0x7);
+ assertEntry(codeMap, 'code4', 0x1950 + 0x10 - 1);
+ assertNoEntry(codeMap, 0x1950 + 0x10);
+ assertNoEntry(codeMap, 0xFFFFFFFF);
+})();
+
+
+(function testCodeMovesAndDeletions() {
+ var codeMap = new devtools.profiler.CodeMap();
+ codeMap.addCode(0x1500, newCodeEntry(0x200, 'code1'));
+ codeMap.addCode(0x1700, newCodeEntry(0x100, 'code2'));
+ assertEntry(codeMap, 'code1', 0x1500);
+ assertEntry(codeMap, 'code2', 0x1700);
+ codeMap.moveCode(0x1500, 0x1800);
+ assertNoEntry(codeMap, 0x1500);
+ assertEntry(codeMap, 'code2', 0x1700);
+ assertEntry(codeMap, 'code1', 0x1800);
+ codeMap.deleteCode(0x1700);
+ assertNoEntry(codeMap, 0x1700);
+ assertEntry(codeMap, 'code1', 0x1800);
+})();
diff --git a/test/mjsunit/tools/csvparser.js b/test/mjsunit/tools/csvparser.js
new file mode 100644
index 0000000..db3a2eb
--- /dev/null
+++ b/test/mjsunit/tools/csvparser.js
@@ -0,0 +1,79 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Load CSV parser implementation from <project root>/tools.
+// Files: tools/csvparser.js
+
+var parser = new devtools.profiler.CsvParser();
+
+assertEquals(
+ [],
+ parser.parseLine(''));
+
+assertEquals(
+ ['', ''],
+ parser.parseLine(','));
+
+assertEquals(
+ ['1997','Ford','E350'],
+ parser.parseLine('1997,Ford,E350'));
+
+assertEquals(
+ ['1997','Ford','E350'],
+ parser.parseLine('"1997","Ford","E350"'));
+
+assertEquals(
+ ['1997','Ford','E350','Super, luxurious truck'],
+ parser.parseLine('1997,Ford,E350,"Super, luxurious truck"'));
+
+assertEquals(
+ ['1997','Ford','E350','Super "luxurious" truck'],
+ parser.parseLine('1997,Ford,E350,"Super ""luxurious"" truck"'));
+
+assertEquals(
+ ['1997','Ford','E350','Super "luxurious" "truck"'],
+ parser.parseLine('1997,Ford,E350,"Super ""luxurious"" ""truck"""'));
+
+assertEquals(
+ ['1997','Ford','E350','Super "luxurious""truck"'],
+ parser.parseLine('1997,Ford,E350,"Super ""luxurious""""truck"""'));
+
+assertEquals(
+ ['shared-library','/lib/ld-2.3.6.so','0x489a2000','0x489b7000'],
+ parser.parseLine('shared-library,"/lib/ld-2.3.6.so",0x489a2000,0x489b7000'));
+
+assertEquals(
+ ['code-creation','LazyCompile','0xf6fe2d20','1201','APPLY_PREPARE native runtime.js:165'],
+ parser.parseLine('code-creation,LazyCompile,0xf6fe2d20,1201,"APPLY_PREPARE native runtime.js:165"'));
+
+assertEquals(
+ ['code-creation','LazyCompile','0xf6fe4bc0','282',' native v8natives.js:69'],
+ parser.parseLine('code-creation,LazyCompile,0xf6fe4bc0,282," native v8natives.js:69"'));
+
+assertEquals(
+ ['code-creation','RegExp','0xf6c21c00','826','NccyrJroXvg\\/([^,]*)'],
+ parser.parseLine('code-creation,RegExp,0xf6c21c00,826,"NccyrJroXvg\\/([^,]*)"'));
diff --git a/test/mjsunit/tools/profile.js b/test/mjsunit/tools/profile.js
new file mode 100644
index 0000000..87ec8fa
--- /dev/null
+++ b/test/mjsunit/tools/profile.js
@@ -0,0 +1,283 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Load source code files from <project root>/tools.
+// Files: tools/splaytree.js tools/codemap.js tools/profile.js
+
+
+function stackToString(stack) {
+ return stack.join(' -> ');
+};
+
+
+function assertPathExists(root, path, opt_message) {
+ var message = opt_message ? ' (' + opt_message + ')' : '';
+ assertNotNull(root.descendToChild(path, function(node, pos) {
+ assertNotNull(node,
+ stackToString(path.slice(0, pos)) + ' has no child ' +
+ path[pos] + message);
+ }), opt_message);
+};
+
+
+function assertNoPathExists(root, path, opt_message) {
+ var message = opt_message ? ' (' + opt_message + ')' : '';
+ assertNull(root.descendToChild(path), opt_message);
+};
+
+
+function countNodes(profile, traverseFunc) {
+ var count = 0;
+ traverseFunc.call(profile, function () { count++; });
+ return count;
+};
+
+
+function ProfileTestDriver() {
+ this.profile = new devtools.profiler.Profile();
+ this.stack_ = [];
+ this.addFunctions_();
+};
+
+
+// Addresses inside functions.
+ProfileTestDriver.prototype.funcAddrs_ = {
+ 'lib1-f1': 0x11110, 'lib1-f2': 0x11210,
+ 'lib2-f1': 0x21110, 'lib2-f2': 0x21210,
+ 'T: F1': 0x50110, 'T: F2': 0x50210, 'T: F3': 0x50410 };
+
+
+ProfileTestDriver.prototype.addFunctions_ = function() {
+ this.profile.addStaticCode('lib1', 0x11000, 0x12000);
+ this.profile.addStaticCode('lib1-f1', 0x11100, 0x11900);
+ this.profile.addStaticCode('lib1-f2', 0x11200, 0x11500);
+ this.profile.addStaticCode('lib2', 0x21000, 0x22000);
+ this.profile.addStaticCode('lib2-f1', 0x21100, 0x21900);
+ this.profile.addStaticCode('lib2-f2', 0x21200, 0x21500);
+ this.profile.addCode('T', 'F1', 0x50100, 0x100);
+ this.profile.addCode('T', 'F2', 0x50200, 0x100);
+ this.profile.addCode('T', 'F3', 0x50400, 0x100);
+};
+
+
+ProfileTestDriver.prototype.enter = function(funcName) {
+ // Stack looks like this: [pc, caller, ..., main].
+ // Therefore, we are adding entries at the beginning.
+ this.stack_.unshift(this.funcAddrs_[funcName]);
+ this.profile.recordTick(this.stack_);
+};
+
+
+ProfileTestDriver.prototype.stay = function() {
+ this.profile.recordTick(this.stack_);
+};
+
+
+ProfileTestDriver.prototype.leave = function() {
+ this.stack_.shift();
+};
+
+
+ProfileTestDriver.prototype.execute = function() {
+ this.enter('lib1-f1');
+ this.enter('lib1-f2');
+ this.enter('T: F1');
+ this.enter('T: F2');
+ this.leave();
+ this.stay();
+ this.enter('lib2-f1');
+ this.enter('lib2-f1');
+ this.leave();
+ this.stay();
+ this.leave();
+ this.enter('T: F3');
+ this.enter('T: F3');
+ this.enter('T: F3');
+ this.leave();
+ this.enter('T: F2');
+ this.stay();
+ this.leave();
+ this.leave();
+ this.leave();
+ this.leave();
+ this.stay();
+ this.leave();
+};
+
+
+function Inherits(childCtor, parentCtor) {
+ function tempCtor() {};
+ tempCtor.prototype = parentCtor.prototype;
+ childCtor.superClass_ = parentCtor.prototype;
+ childCtor.prototype = new tempCtor();
+ childCtor.prototype.constructor = childCtor;
+};
+
+
+(function testCallTreeBuilding() {
+ function Driver() {
+ ProfileTestDriver.call(this);
+ this.namesTopDown = [];
+ this.namesBottomUp = [];
+ };
+ Inherits(Driver, ProfileTestDriver);
+
+ Driver.prototype.enter = function(func) {
+ this.namesTopDown.push(func);
+ this.namesBottomUp.unshift(func);
+ assertNoPathExists(this.profile.getTopDownTreeRoot(), this.namesTopDown,
+ 'pre enter/topDown');
+ assertNoPathExists(this.profile.getBottomUpTreeRoot(), this.namesBottomUp,
+ 'pre enter/bottomUp');
+ Driver.superClass_.enter.call(this, func);
+ assertPathExists(this.profile.getTopDownTreeRoot(), this.namesTopDown,
+ 'post enter/topDown');
+ assertPathExists(this.profile.getBottomUpTreeRoot(), this.namesBottomUp,
+ 'post enter/bottomUp');
+ };
+
+ Driver.prototype.stay = function() {
+ var preTopDownNodes = countNodes(this.profile, this.profile.traverseTopDownTree);
+ var preBottomUpNodes = countNodes(this.profile, this.profile.traverseBottomUpTree);
+ Driver.superClass_.stay.call(this);
+ var postTopDownNodes = countNodes(this.profile, this.profile.traverseTopDownTree);
+ var postBottomUpNodes = countNodes(this.profile, this.profile.traverseBottomUpTree);
+ // Must be no changes in tree layout.
+ assertEquals(preTopDownNodes, postTopDownNodes, 'stay/topDown');
+ assertEquals(preBottomUpNodes, postBottomUpNodes, 'stay/bottomUp');
+ };
+
+ Driver.prototype.leave = function() {
+ Driver.superClass_.leave.call(this);
+ this.namesTopDown.pop();
+ this.namesBottomUp.shift();
+ };
+
+ var testDriver = new Driver();
+ testDriver.execute();
+})();
+
+
+function assertNodeWeights(root, path, selfTicks, totalTicks) {
+ var node = root.descendToChild(path);
+ var stack = stackToString(path);
+ assertNotNull(node, 'node not found: ' + stack);
+ assertEquals(selfTicks, node.selfWeight, 'self of ' + stack);
+ assertEquals(totalTicks, node.totalWeight, 'total of ' + stack);
+};
+
+
+(function testTopDownRootProfileTicks() {
+ var testDriver = new ProfileTestDriver();
+ testDriver.execute();
+
+ var pathWeights = [
+ [['lib1-f1'], 1, 14],
+ [['lib1-f1', 'lib1-f2'], 2, 13],
+ [['lib1-f1', 'lib1-f2', 'T: F1'], 2, 11],
+ [['lib1-f1', 'lib1-f2', 'T: F1', 'T: F2'], 1, 1],
+ [['lib1-f1', 'lib1-f2', 'T: F1', 'lib2-f1'], 2, 3],
+ [['lib1-f1', 'lib1-f2', 'T: F1', 'lib2-f1', 'lib2-f1'], 1, 1],
+ [['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3'], 1, 5],
+ [['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3', 'T: F3'], 1, 4],
+ [['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3', 'T: F3', 'T: F3'], 1, 1],
+ [['lib1-f1', 'lib1-f2', 'T: F1', 'T: F3', 'T: F3', 'T: F2'], 2, 2]
+ ];
+
+ var root = testDriver.profile.getTopDownTreeRoot();
+ for (var i = 0; i < pathWeights.length; ++i) {
+ var data = pathWeights[i];
+ assertNodeWeights(root, data[0], data[1], data[2]);
+ }
+})();
+
+
+(function testRootFlatProfileTicks() {
+ function Driver() {
+ ProfileTestDriver.call(this);
+ this.namesTopDown = [''];
+ this.counters = {};
+ };
+ Inherits(Driver, ProfileTestDriver);
+
+ Driver.prototype.increment = function(func, self, total) {
+ if (!(func in this.counters)) {
+ this.counters[func] = { self: 0, total: 0 };
+ }
+ this.counters[func].self += self;
+ this.counters[func].total += total;
+ };
+
+ Driver.prototype.incrementTotals = function() {
+ // Only count each function in the stack once.
+ var met = {};
+ for (var i = 0; i < this.namesTopDown.length; ++i) {
+ var name = this.namesTopDown[i];
+ if (!(name in met)) {
+ this.increment(name, 0, 1);
+ }
+ met[name] = true;
+ }
+ };
+
+ Driver.prototype.enter = function(func) {
+ Driver.superClass_.enter.call(this, func);
+ this.namesTopDown.push(func);
+ this.increment(func, 1, 0);
+ this.incrementTotals();
+ };
+
+ Driver.prototype.stay = function() {
+ Driver.superClass_.stay.call(this);
+ this.increment(this.namesTopDown[this.namesTopDown.length - 1], 1, 0);
+ this.incrementTotals();
+ };
+
+ Driver.prototype.leave = function() {
+ Driver.superClass_.leave.call(this);
+ this.namesTopDown.pop();
+ };
+
+ var testDriver = new Driver();
+ testDriver.execute();
+
+ var counted = 0;
+ for (var c in testDriver.counters) {
+ counted++;
+ }
+
+ var flatProfile = testDriver.profile.getFlatProfile();
+ assertEquals(counted, flatProfile.length, 'counted vs. flatProfile');
+ for (var i = 0; i < flatProfile.length; ++i) {
+ var rec = flatProfile[i];
+ assertTrue(rec.label in testDriver.counters, 'uncounted: ' + rec.label);
+ var reference = testDriver.counters[rec.label];
+ assertEquals(reference.self, rec.selfWeight, 'self of ' + rec.label);
+ assertEquals(reference.total, rec.totalWeight, 'total of ' + rec.label);
+ }
+
+})();
diff --git a/tools/codemap.js b/tools/codemap.js
new file mode 100644
index 0000000..5149cfc
--- /dev/null
+++ b/tools/codemap.js
@@ -0,0 +1,210 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Initlialize namespaces
+var devtools = devtools || {};
+devtools.profiler = devtools.profiler || {};
+
+
+/**
+ * Constructs a mapper that maps addresses into code entries.
+ *
+ * @constructor
+ */
+devtools.profiler.CodeMap = function() {
+ /**
+ * Dynamic code entries. Used for JIT compiled code.
+ */
+ this.dynamics_ = new goog.structs.SplayTree();
+
+ /**
+ * Deleted code entries. Used for code collected by the GC.
+ */
+ this.deleted_ = [];
+
+ /**
+ * Static code entries. Used for libraries code.
+ */
+ this.statics_ = new goog.structs.SplayTree();
+
+ /**
+ * Map of memory pages occupied with static code.
+ */
+ this.pages_ = [];
+};
+
+
+/**
+ * The number of alignment bits in a page address.
+ */
+devtools.profiler.CodeMap.PAGE_ALIGNMENT = 12;
+
+
+/**
+ * Page size in bytes.
+ */
+devtools.profiler.CodeMap.PAGE_SIZE =
+ 1 << devtools.profiler.CodeMap.PAGE_ALIGNMENT;
+
+
+/**
+ * Adds a dynamic (i.e. moveable and discardable) code entry.
+ *
+ * @param {number} start The starting address.
+ * @param {devtools.profiler.CodeMap.CodeEntry} codeEntry Code entry object.
+ */
+devtools.profiler.CodeMap.prototype.addCode = function(start, codeEntry) {
+ this.dynamics_.insert(start, codeEntry);
+};
+
+
+/**
+ * Moves a dynamic code entry. Throws an exception if there is no dynamic
+ * code entry with the specified starting address.
+ *
+ * @param {number} from The starting address of the entry being moved.
+ * @param {number} to The destination address.
+ */
+devtools.profiler.CodeMap.prototype.moveCode = function(from, to) {
+ var removedNode = this.dynamics_.remove(from);
+ this.dynamics_.insert(to, removedNode.value);
+};
+
+
+/**
+ * Discards a dynamic code entry. Throws an exception if there is no dynamic
+ * code entry with the specified starting address. The entry will still be
+ * returned from the 'getAllDynamicEntries' method.
+ *
+ * @param {number} start The starting address of the entry being deleted.
+ */
+devtools.profiler.CodeMap.prototype.deleteCode = function(start) {
+ var removedNode = this.dynamics_.remove(start);
+ this.deleted_.push(removedNode.value);
+};
+
+
+/**
+ * Adds a static code entry.
+ *
+ * @param {number} start The starting address.
+ * @param {devtools.profiler.CodeMap.CodeEntry} codeEntry Code entry object.
+ */
+devtools.profiler.CodeMap.prototype.addStaticCode = function(
+ start, codeEntry) {
+ this.markPages_(start, start + codeEntry.size);
+ this.statics_.insert(start, codeEntry);
+};
+
+
+/**
+ * @private
+ */
+devtools.profiler.CodeMap.prototype.markPages_ = function(start, end) {
+ for (var addr = start; addr <= end;
+ addr += devtools.profiler.CodeMap.PAGE_SIZE) {
+ this.pages_[addr >> devtools.profiler.CodeMap.PAGE_ALIGNMENT] = 1;
+ }
+};
+
+
+/**
+ * @private
+ */
+devtools.profiler.CodeMap.prototype.isAddressBelongsTo_ = function(addr, node) {
+ return addr >= node.key && addr < (node.key + node.value.size);
+};
+
+
+/**
+ * @private
+ */
+devtools.profiler.CodeMap.prototype.findInTree_ = function(tree, addr) {
+ var node = tree.findGreatestLessThan(addr);
+ return node && this.isAddressBelongsTo_(addr, node) ? node.value : null;
+};
+
+
+/**
+ * Finds a code entry that contains the specified address. Both static and
+ * dynamic code entries are considered.
+ *
+ * @param {number} addr Address.
+ */
+devtools.profiler.CodeMap.prototype.findEntry = function(addr) {
+ var pageAddr = addr >> devtools.profiler.CodeMap.PAGE_ALIGNMENT;
+ if (pageAddr in this.pages_) {
+ return this.findInTree_(this.statics_, addr);
+ }
+ var min = this.dynamics_.findMin();
+ var max = this.dynamics_.findMax();
+ if (max != null && addr < (max.key + max.value.size) && addr >= min.key) {
+ return this.findInTree_(this.dynamics_, addr);
+ }
+ return null;
+};
+
+
+/**
+ * Returns an array of all dynamic code entries, including deleted ones.
+ */
+devtools.profiler.CodeMap.prototype.getAllDynamicEntries = function() {
+ var dynamicEntries = this.dynamics_.exportValues();
+ return dynamicEntries.concat(this.deleted_);
+};
+
+
+/**
+ * Returns an array of all static code entries.
+ */
+devtools.profiler.CodeMap.prototype.getAllStaticEntries = function() {
+ return this.statics_.exportValues();
+};
+
+
+/**
+ * Creates a code entry object.
+ *
+ * @param {number} size Code entry size in bytes.
+ * @param {string} opt_name Code entry name.
+ * @constructor
+ */
+devtools.profiler.CodeMap.CodeEntry = function(size, opt_name) {
+ this.size = size;
+ this.name = opt_name || '';
+};
+
+
+devtools.profiler.CodeMap.CodeEntry.prototype.getName = function() {
+ return this.name;
+};
+
+
+devtools.profiler.CodeMap.CodeEntry.prototype.toString = function() {
+ return this.name + ': ' + this.size.toString(16);
+};
diff --git a/tools/csvparser.js b/tools/csvparser.js
new file mode 100644
index 0000000..9e58dea
--- /dev/null
+++ b/tools/csvparser.js
@@ -0,0 +1,98 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Initlialize namespaces.
+var devtools = devtools || {};
+devtools.profiler = devtools.profiler || {};
+
+
+/**
+ * Creates a CSV lines parser.
+ */
+devtools.profiler.CsvParser = function() {
+};
+
+
+/**
+ * A regex for matching a trailing quote.
+ * @private
+ */
+devtools.profiler.CsvParser.TRAILING_QUOTE_RE_ = /\"$/;
+
+
+/**
+ * A regex for matching a double quote.
+ * @private
+ */
+devtools.profiler.CsvParser.DOUBLE_QUOTE_RE_ = /\"\"/g;
+
+
+/**
+ * Parses a line of CSV-encoded values. Returns an array of fields.
+ *
+ * @param {string} line Input line.
+ */
+devtools.profiler.CsvParser.prototype.parseLine = function(line) {
+ var insideQuotes = false;
+ var fields = [];
+ var prevPos = 0;
+ for (var i = 0, n = line.length; i < n; ++i) {
+ switch (line.charAt(i)) {
+ case ',':
+ if (!insideQuotes) {
+ fields.push(line.substring(prevPos, i));
+ prevPos = i + 1;
+ }
+ break;
+ case '"':
+ if (!insideQuotes) {
+ insideQuotes = true;
+ // Skip the leading quote.
+ prevPos++;
+ } else {
+ if (i + 1 < n && line.charAt(i + 1) != '"') {
+ insideQuotes = false;
+ } else {
+ i++;
+ }
+ }
+ break;
+ }
+ }
+ if (n > 0) {
+ fields.push(line.substring(prevPos));
+ }
+
+ for (i = 0; i < fields.length; ++i) {
+ // Eliminate trailing quotes.
+ fields[i] = fields[i].replace(devtools.profiler.CsvParser.TRAILING_QUOTE_RE_, '');
+ // Convert quoted quotes into single ones.
+ fields[i] = fields[i].replace(devtools.profiler.CsvParser.DOUBLE_QUOTE_RE_, '"');
+ }
+ return fields;
+};
diff --git a/tools/profile.js b/tools/profile.js
new file mode 100644
index 0000000..e70d244
--- /dev/null
+++ b/tools/profile.js
@@ -0,0 +1,468 @@
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+// Initlialize namespaces
+var devtools = devtools || {};
+devtools.profiler = devtools.profiler || {};
+
+
+/**
+ * Creates a profile object for processing profiling-related events
+ * and calculating function execution times.
+ *
+ * @constructor
+ */
+devtools.profiler.Profile = function() {
+ this.codeMap_ = new devtools.profiler.CodeMap();
+ this.topDownTree_ = new devtools.profiler.CallTree();
+ this.bottomUpTree_ = new devtools.profiler.CallTree();
+};
+
+
+/**
+ * Returns whether a function with the specified name must be skipped.
+ * Should be overriden by subclasses.
+ *
+ * @param {string} name Function name.
+ */
+devtools.profiler.Profile.prototype.skipThisFunction = function(name) {
+ return false;
+};
+
+
+/**
+ * Called whenever the specified operation has failed finding a function
+ * containing the specified address. Should be overriden by subclasses.
+ * Operation is one of the following: 'move', 'delete', 'tick'.
+ *
+ * @param {string} operation Operation name.
+ * @param {number} addr Address of the unknown code.
+ */
+devtools.profiler.Profile.prototype.handleUnknownCode = function(
+ operation, addr) {
+};
+
+
+/**
+ * Registers static (library) code entry.
+ *
+ * @param {string} name Code entry name.
+ * @param {number} startAddr Starting address.
+ * @param {number} endAddr Ending address.
+ */
+devtools.profiler.Profile.prototype.addStaticCode = function(
+ name, startAddr, endAddr) {
+ this.codeMap_.addStaticCode(startAddr,
+ new devtools.profiler.CodeMap.CodeEntry(endAddr - startAddr, name));
+};
+
+
+/**
+ * Registers dynamic (JIT-compiled) code entry.
+ *
+ * @param {string} type Code entry type.
+ * @param {string} name Code entry name.
+ * @param {number} start Starting address.
+ * @param {number} size Code entry size.
+ */
+devtools.profiler.Profile.prototype.addCode = function(
+ type, name, start, size) {
+ this.codeMap_.addCode(start,
+ new devtools.profiler.Profile.DynamicCodeEntry(size, type, name));
+};
+
+
+/**
+ * Reports about moving of a dynamic code entry.
+ *
+ * @param {number} from Current code entry address.
+ * @param {number} to New code entry address.
+ */
+devtools.profiler.Profile.prototype.moveCode = function(from, to) {
+ try {
+ this.codeMap_.moveCode(from, to);
+ } catch (e) {
+ this.handleUnknownCode('move', from);
+ }
+};
+
+
+/**
+ * Reports about deletion of a dynamic code entry.
+ *
+ * @param {number} start Starting address.
+ */
+devtools.profiler.Profile.prototype.deleteCode = function(start) {
+ try {
+ this.codeMap_.deleteCode(start);
+ } catch (e) {
+ this.handleUnknownCode('delete', start);
+ }
+};
+
+
+/**
+ * Records a tick event. Stack must contain a sequence of
+ * addresses starting with the program counter value.
+ *
+ * @param {Array<number>} stack Stack sample.
+ */
+devtools.profiler.Profile.prototype.recordTick = function(stack) {
+ var processedStack = this.resolveAndFilterFuncs_(stack);
+ this.bottomUpTree_.addPath(processedStack);
+ processedStack.reverse();
+ this.topDownTree_.addPath(processedStack);
+};
+
+
+/**
+ * Translates addresses into function names and filters unneeded
+ * functions.
+ *
+ * @param {Array<number>} stack Stack sample.
+ */
+devtools.profiler.Profile.prototype.resolveAndFilterFuncs_ = function(stack) {
+ var result = [];
+ for (var i = 0; i < stack.length; ++i) {
+ var entry = this.codeMap_.findEntry(stack[i]);
+ if (entry) {
+ var name = entry.getName();
+ if (!this.skipThisFunction(name)) {
+ result.push(name);
+ }
+ } else {
+ this.handleUnknownCode('tick', stack[i]);
+ }
+ }
+ return result;
+};
+
+
+/**
+ * Returns the root of the top down call graph.
+ */
+devtools.profiler.Profile.prototype.getTopDownTreeRoot = function() {
+ this.topDownTree_.computeTotalWeights();
+ return this.topDownTree_.root_;
+};
+
+
+/**
+ * Returns the root of the bottom up call graph.
+ */
+devtools.profiler.Profile.prototype.getBottomUpTreeRoot = function() {
+ this.bottomUpTree_.computeTotalWeights();
+ return this.bottomUpTree_.root_;
+};
+
+
+/**
+ * Traverses the top down call graph in preorder.
+ *
+ * @param {function(devtools.profiler.CallTree.Node)} f Visitor function.
+ */
+devtools.profiler.Profile.prototype.traverseTopDownTree = function(f) {
+ this.topDownTree_.traverse(f);
+};
+
+
+/**
+ * Traverses the bottom up call graph in preorder.
+ *
+ * @param {function(devtools.profiler.CallTree.Node)} f Visitor function.
+ */
+devtools.profiler.Profile.prototype.traverseBottomUpTree = function(f) {
+ this.bottomUpTree_.traverse(f);
+};
+
+
+/**
+ * Calculates a flat profile of callees starting from the specified node.
+ *
+ * @param {devtools.profiler.CallTree.Node} opt_root Starting node.
+ */
+devtools.profiler.Profile.prototype.getFlatProfile = function(opt_root) {
+ var counters = new devtools.profiler.CallTree.Node('');
+ var precs = {};
+ this.topDownTree_.computeTotalWeights();
+ this.topDownTree_.traverseInDepth(
+ function onEnter(node) {
+ if (!(node.label in precs)) {
+ precs[node.label] = 0;
+ }
+ var rec = counters.findOrAddChild(node.label);
+ rec.selfWeight += node.selfWeight;
+ if (precs[node.label] == 0) {
+ rec.totalWeight += node.totalWeight;
+ }
+ precs[node.label]++;
+ },
+ function onExit(node) {
+ precs[node.label]--;
+ },
+ opt_root);
+ return counters.exportChildren();
+};
+
+
+/**
+ * Creates a dynamic code entry.
+ *
+ * @param {number} size Code size.
+ * @param {string} type Code type.
+ * @param {string} name Function name.
+ * @constructor
+ */
+devtools.profiler.Profile.DynamicCodeEntry = function(size, type, name) {
+ devtools.profiler.CodeMap.CodeEntry.call(this, size, name);
+ this.type = type;
+};
+
+
+/**
+ * Returns node name.
+ */
+devtools.profiler.Profile.DynamicCodeEntry.prototype.getName = function() {
+ var name = this.name;
+ if (name.length == 0) {
+ name = '<anonymous>';
+ } else if (name.charAt(0) == ' ') {
+ // An anonymous function with location: " aaa.js:10".
+ name = '<anonymous>' + name;
+ }
+ return this.type + ': ' + name;
+};
+
+
+/**
+ * Constructs a call graph.
+ *
+ * @constructor
+ */
+devtools.profiler.CallTree = function() {
+ this.root_ = new devtools.profiler.CallTree.Node('');
+};
+
+
+/**
+ * @private
+ */
+devtools.profiler.CallTree.prototype.totalsComputed_ = false;
+
+
+/**
+ * Adds the specified call path, constructing nodes as necessary.
+ *
+ * @param {Array<string>} path Call path.
+ */
+devtools.profiler.CallTree.prototype.addPath = function(path) {
+ if (path.length == 0) {
+ return;
+ }
+ var curr = this.root_;
+ for (var i = 0; i < path.length; ++i) {
+ curr = curr.findOrAddChild(path[i]);
+ }
+ curr.selfWeight++;
+ this.totalsComputed_ = false;
+};
+
+
+/**
+ * Computes total weights in the call graph.
+ */
+devtools.profiler.CallTree.prototype.computeTotalWeights = function() {
+ if (this.totalsComputed_) {
+ return;
+ }
+ this.root_.computeTotalWeight();
+ this.totalsComputed_ = true;
+};
+
+
+/**
+ * Traverses the call graph in preorder.
+ *
+ * @param {function(devtools.profiler.CallTree.Node)} f Visitor function.
+ * @param {devtools.profiler.CallTree.Node} opt_start Starting node.
+ */
+devtools.profiler.CallTree.prototype.traverse = function(f, opt_start) {
+ var nodesToVisit = [opt_start || this.root_];
+ while (nodesToVisit.length > 0) {
+ var node = nodesToVisit.shift();
+ f(node);
+ nodesToVisit = nodesToVisit.concat(node.exportChildren());
+ }
+};
+
+
+/**
+ * Performs an indepth call graph traversal.
+ *
+ * @param {function(devtools.profiler.CallTree.Node)} enter A function called
+ * prior to visiting node's children.
+ * @param {function(devtools.profiler.CallTree.Node)} exit A function called
+ * after visiting node's children.
+ * @param {devtools.profiler.CallTree.Node} opt_start Starting node.
+ */
+devtools.profiler.CallTree.prototype.traverseInDepth = function(
+ enter, exit, opt_start) {
+ function traverse(node) {
+ enter(node);
+ node.forEachChild(traverse);
+ exit(node);
+ }
+ traverse(opt_start || this.root_);
+};
+
+
+/**
+ * Constructs a call graph node.
+ *
+ * @param {string} label Node label.
+ * @param {devtools.profiler.CallTree.Node} opt_parent Node parent.
+ */
+devtools.profiler.CallTree.Node = function(label, opt_parent) {
+ this.label = label;
+ this.parent = opt_parent;
+ this.children = {};
+};
+
+
+/**
+ * Node self weight (how many times this node was the last node in
+ * a call path).
+ * @type {number}
+ */
+devtools.profiler.CallTree.Node.prototype.selfWeight = 0;
+
+
+/**
+ * Node total weight (includes weights of all children).
+ * @type {number}
+ */
+devtools.profiler.CallTree.Node.prototype.totalWeight = 0;
+
+
+/**
+ * Adds a child node.
+ *
+ * @param {string} label Child node label.
+ */
+devtools.profiler.CallTree.Node.prototype.addChild = function(label) {
+ var child = new devtools.profiler.CallTree.Node(label, this);
+ this.children[label] = child;
+ return child;
+};
+
+
+/**
+ * Computes node's total weight.
+ */
+devtools.profiler.CallTree.Node.prototype.computeTotalWeight =
+ function() {
+ var totalWeight = this.selfWeight;
+ this.forEachChild(function(child) {
+ totalWeight += child.computeTotalWeight(); });
+ return this.totalWeight = totalWeight;
+};
+
+
+/**
+ * Returns all node's children as an array.
+ */
+devtools.profiler.CallTree.Node.prototype.exportChildren = function() {
+ var result = [];
+ this.forEachChild(function (node) { result.push(node); });
+ return result;
+};
+
+
+/**
+ * Finds an immediate child with the specified label.
+ *
+ * @param {string} label Child node label.
+ */
+devtools.profiler.CallTree.Node.prototype.findChild = function(label) {
+ return this.children[label] || null;
+};
+
+
+/**
+ * Finds an immediate child with the specified label, creates a child
+ * node if necessary.
+ *
+ * @param {string} label Child node label.
+ */
+devtools.profiler.CallTree.Node.prototype.findOrAddChild = function(
+ label) {
+ return this.findChild(label) || this.addChild(label);
+};
+
+
+/**
+ * Calls the specified function for every child.
+ *
+ * @param {function(devtools.profiler.CallTree.Node)} f Visitor function.
+ */
+devtools.profiler.CallTree.Node.prototype.forEachChild = function(f) {
+ for (var c in this.children) {
+ f(this.children[c]);
+ }
+};
+
+
+/**
+ * Walks up from the current node up to the call tree root.
+ *
+ * @param {function(devtools.profiler.CallTree.Node)} f Visitor function.
+ */
+devtools.profiler.CallTree.Node.prototype.walkUpToRoot = function(f) {
+ for (var curr = this; curr != null; curr = curr.parent) {
+ f(curr);
+ }
+};
+
+
+/**
+ * Tries to find a node with the specified path.
+ *
+ * @param {Array<string>} labels The path.
+ * @param {function(devtools.profiler.CallTree.Node)} opt_f Visitor function.
+ */
+devtools.profiler.CallTree.Node.prototype.descendToChild = function(
+ labels, opt_f) {
+ for (var pos = 0, curr = this; pos < labels.length && curr != null; pos++) {
+ var child = curr.findChild(labels[pos]);
+ if (opt_f) {
+ opt_f(child, pos);
+ }
+ curr = child;
+ }
+ return curr;
+};
diff --git a/tools/splaytree.js b/tools/splaytree.js
index 3045456..f3beb16 100644
--- a/tools/splaytree.js
+++ b/tools/splaytree.js
@@ -94,7 +94,6 @@
};
-
/**
* Removes a node with the specified key from the tree if the tree
* contains a node with this key. The removed node is returned. If the
diff --git a/tools/visual_studio/v8_base_arm.vcproj b/tools/visual_studio/v8_base_arm.vcproj
index a91d63a..04ed230 100644
--- a/tools/visual_studio/v8_base_arm.vcproj
+++ b/tools/visual_studio/v8_base_arm.vcproj
@@ -349,6 +349,14 @@
>
</File>
<File
+ RelativePath="..\..\src\debug-agent.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\debug-agent.h"
+ >
+ </File>
+ <File
RelativePath="..\..\src\debug-arm.cc"
>
</File>
@@ -593,6 +601,14 @@
>
</File>
<File
+ RelativePath="..\..\src\oprofile-agent.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\oprofile-agent.h"
+ >
+ </File>
+ <File
RelativePath="..\..\src\parser.cc"
>
</File>