summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndrei Popescu <andreip@google.com>2010-02-26 13:31:12 +0000
committerAndrei Popescu <andreip@google.com>2010-02-26 13:31:12 +0000
commit402d937239b0e2fd11bf2f4fe972ad78aa9fd481 (patch)
treeb9d769439a27fa48d7171e1a669e98f519591b94
parent2007755a32dfa1ac843f501dec4fb872f8bbcc52 (diff)
downloadandroid_external_v8-402d937239b0e2fd11bf2f4fe972ad78aa9fd481.tar.gz
android_external_v8-402d937239b0e2fd11bf2f4fe972ad78aa9fd481.tar.bz2
android_external_v8-402d937239b0e2fd11bf2f4fe972ad78aa9fd481.zip
update V8 to TOT snapshot branch
-rw-r--r--AUTHORS1
-rw-r--r--Android.v8common.mk1
-rw-r--r--ChangeLog22
-rw-r--r--include/v8.h122
-rw-r--r--samples/lineprocessor.cc2
-rwxr-xr-xsrc/SConscript5
-rw-r--r--src/accessors.cc37
-rw-r--r--src/api.cc156
-rw-r--r--src/arm/assembler-arm.cc47
-rw-r--r--src/arm/assembler-thumb2-inl.h30
-rw-r--r--src/arm/assembler-thumb2.cc227
-rw-r--r--src/arm/assembler-thumb2.h21
-rw-r--r--src/arm/builtins-arm.cc143
-rw-r--r--src/arm/codegen-arm.cc290
-rw-r--r--src/arm/codegen-arm.h19
-rw-r--r--src/arm/cpu-arm.cc2
-rw-r--r--src/arm/debug-arm.cc10
-rw-r--r--src/arm/fast-codegen-arm.cc181
-rw-r--r--src/arm/full-codegen-arm.cc140
-rw-r--r--src/arm/ic-arm.cc183
-rw-r--r--src/arm/macro-assembler-arm.cc101
-rw-r--r--src/arm/macro-assembler-arm.h29
-rw-r--r--src/arm/stub-cache-arm.cc467
-rw-r--r--src/arm/stub-cache-arm.cc.rej153
-rw-r--r--src/arm/virtual-frame-arm.cc12
-rw-r--r--src/arm/virtual-frame-arm.h9
-rw-r--r--src/array.js5
-rw-r--r--src/assembler.cc20
-rw-r--r--src/assembler.h3
-rw-r--r--src/ast.h50
-rw-r--r--src/bootstrapper.cc234
-rw-r--r--src/bootstrapper.h3
-rw-r--r--src/builtins.cc494
-rw-r--r--src/builtins.h9
-rw-r--r--src/checks.h4
-rw-r--r--src/code-stubs.cc9
-rw-r--r--src/code-stubs.h2
-rw-r--r--src/codegen.cc23
-rw-r--r--src/codegen.h22
-rwxr-xr-xsrc/compiler.cc8
-rw-r--r--src/compiler.h58
-rw-r--r--src/contexts.h1
-rw-r--r--src/d8-readline.cc4
-rw-r--r--src/data-flow.cc309
-rw-r--r--src/data-flow.h50
-rw-r--r--src/date-delay.js1138
-rw-r--r--src/date.js3
-rw-r--r--src/debug-debugger.js23
-rw-r--r--src/debug-delay.js2073
-rw-r--r--src/debug.cc12
-rw-r--r--src/execution.cc2
-rw-r--r--src/fast-codegen.cc189
-rw-r--r--src/fast-codegen.h80
-rw-r--r--src/flag-definitions.h2
-rw-r--r--src/frame-element.cc4
-rw-r--r--src/frame-element.h46
-rw-r--r--src/frames.cc7
-rw-r--r--src/full-codegen.cc90
-rw-r--r--src/handles.cc36
-rw-r--r--src/handles.h2
-rw-r--r--src/heap.cc25
-rw-r--r--src/heap.h19
-rw-r--r--src/ia32/assembler-ia32.cc23
-rw-r--r--src/ia32/assembler-ia32.h7
-rw-r--r--src/ia32/builtins-ia32.cc135
-rw-r--r--src/ia32/codegen-ia32.cc1858
-rw-r--r--src/ia32/codegen-ia32.h151
-rw-r--r--src/ia32/debug-ia32.cc3
-rw-r--r--src/ia32/disasm-ia32.cc3
-rw-r--r--src/ia32/fast-codegen-ia32.cc888
-rw-r--r--src/ia32/fast-codegen-ia32.h155
-rw-r--r--src/ia32/full-codegen-ia32.cc152
-rw-r--r--src/ia32/ic-ia32.cc413
-rw-r--r--src/ia32/macro-assembler-ia32.cc141
-rw-r--r--src/ia32/macro-assembler-ia32.h45
-rw-r--r--src/ia32/stub-cache-ia32.cc634
-rw-r--r--src/ia32/virtual-frame-ia32.cc183
-rw-r--r--src/ia32/virtual-frame-ia32.h35
-rw-r--r--src/ic.cc69
-rw-r--r--src/ic.h30
-rw-r--r--src/json-delay.js254
-rw-r--r--src/jump-target-inl.h3
-rw-r--r--src/jump-target.cc48
-rw-r--r--src/liveedit.cc87
-rw-r--r--src/liveedit.h78
-rw-r--r--src/log-utils.cc9
-rw-r--r--src/log-utils.h3
-rw-r--r--src/log.cc99
-rw-r--r--src/log.h15
-rw-r--r--src/macro-assembler.h2
-rw-r--r--src/macros.py14
-rw-r--r--src/math.js6
-rw-r--r--src/mips/codegen-mips.cc11
-rw-r--r--src/mips/codegen-mips.h4
-rw-r--r--src/mirror-debugger.js59
-rw-r--r--src/mirror-delay.js2334
-rw-r--r--src/mksnapshot.cc1
-rw-r--r--src/number-info.h72
-rw-r--r--src/objects-debug.cc2
-rw-r--r--src/objects.cc221
-rw-r--r--src/objects.h9
-rw-r--r--src/parser.cc3
-rw-r--r--src/platform-freebsd.cc3
-rw-r--r--src/platform-linux.cc13
-rw-r--r--src/platform-openbsd.cc3
-rw-r--r--src/property.cc2
-rw-r--r--src/property.h27
-rw-r--r--src/regexp-delay.js406
-rw-r--r--src/register-allocator-inl.h33
-rw-r--r--src/register-allocator.cc23
-rw-r--r--src/register-allocator.h35
-rw-r--r--src/runtime.cc279
-rw-r--r--src/runtime.h13
-rw-r--r--src/runtime.js29
-rw-r--r--src/scopeinfo.cc2
-rw-r--r--src/serialize.cc46
-rw-r--r--src/spaces-inl.h2
-rw-r--r--src/string.js134
-rw-r--r--src/stub-cache.cc34
-rw-r--r--src/stub-cache.h32
-rw-r--r--src/top.cc7
-rw-r--r--src/top.h1
-rw-r--r--src/utils.cc37
-rw-r--r--src/utils.h48
-rw-r--r--src/v8-counters.h151
-rw-r--r--src/v8natives.js11
-rw-r--r--src/version.cc2
-rw-r--r--src/virtual-frame.cc46
-rw-r--r--src/x64/assembler-x64.cc87
-rw-r--r--src/x64/assembler-x64.h107
-rw-r--r--src/x64/builtins-x64.cc145
-rw-r--r--src/x64/codegen-x64.cc465
-rw-r--r--src/x64/codegen-x64.h55
-rw-r--r--src/x64/disasm-x64.cc95
-rw-r--r--src/x64/fast-codegen-x64.cc190
-rw-r--r--src/x64/full-codegen-x64.cc106
-rw-r--r--src/x64/ic-x64.cc186
-rw-r--r--src/x64/macro-assembler-x64.cc132
-rw-r--r--src/x64/macro-assembler-x64.h34
-rw-r--r--src/x64/stub-cache-x64.cc218
-rw-r--r--src/x64/virtual-frame-x64.cc87
-rw-r--r--src/x64/virtual-frame-x64.h30
-rw-r--r--test/cctest/test-api.cc799
-rw-r--r--test/cctest/test-assembler-mips.cc257
-rw-r--r--test/cctest/test-compiler.cc25
-rw-r--r--test/cctest/test-debug.cc50
-rw-r--r--test/cctest/test-log.cc324
-rw-r--r--test/cctest/test-serialize.cc4
-rw-r--r--test/cctest/test-utils.cc105
-rw-r--r--test/mjsunit/array-functions-prototype-misc.js314
-rw-r--r--test/mjsunit/array-shift.js71
-rw-r--r--test/mjsunit/array-slice.js162
-rw-r--r--test/mjsunit/array-splice.js535
-rw-r--r--test/mjsunit/array-unshift.js132
-rw-r--r--test/mjsunit/bugs/618.js86
-rw-r--r--test/mjsunit/bugs/bug-619.js62
-rw-r--r--test/mjsunit/codegen-coverage.js92
-rw-r--r--test/mjsunit/compiler/assignment.js264
-rw-r--r--test/mjsunit/compiler/simple-bailouts.js127
-rw-r--r--test/mjsunit/compiler/simple-binary-op.js40
-rw-r--r--test/mjsunit/compiler/simple-global-access.js53
-rw-r--r--test/mjsunit/compiler/this-property-refs.js64
-rw-r--r--test/mjsunit/debug-compile-event.js6
-rw-r--r--test/mjsunit/debug-evaluate.js37
-rw-r--r--test/mjsunit/div-mod.js16
-rw-r--r--test/mjsunit/fuzz-natives.js19
-rw-r--r--test/mjsunit/object-define-properties.js56
-rw-r--r--test/mjsunit/object-define-property.js499
-rw-r--r--test/mjsunit/object-get-own-property-names.js2
-rw-r--r--test/mjsunit/regress/regress-603.js49
-rw-r--r--test/mjsunit/regress/regress-612.js44
-rw-r--r--test/mjsunit/setter-on-constructor-prototype.js111
-rwxr-xr-x[-rw-r--r--]test/mjsunit/substr.js0
-rw-r--r--test/mjsunit/tools/tickprocessor-test-func-info.log13
-rw-r--r--tools/gyp/v8.gyp17
-rw-r--r--tools/tickprocessor.js16
-rw-r--r--tools/visual_studio/v8_base.vcproj14
-rw-r--r--tools/visual_studio/v8_base_arm.vcproj12
-rw-r--r--tools/visual_studio/v8_base_x64.vcproj12
179 files changed, 12760 insertions, 10813 deletions
diff --git a/AUTHORS b/AUTHORS
index 9128ba3e..8b0db5c3 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -23,3 +23,4 @@ Rene Rebe <rene@exactcode.de>
Ryan Dahl <coldredlemur@gmail.com>
Patrick Gansterer <paroga@paroga.com>
Subrato K De <subratokde@codeaurora.org>
+Dineel D Sule <dsule@codeaurora.org>
diff --git a/Android.v8common.mk b/Android.v8common.mk
index 6d8034be..ded5f61f 100644
--- a/Android.v8common.mk
+++ b/Android.v8common.mk
@@ -38,6 +38,7 @@ V8_LOCAL_SRC_FILES := \
src/interpreter-irregexp.cc \
src/jsregexp.cc \
src/jump-target.cc \
+ src/liveedit.cc \
src/log.cc \
src/log-utils.cc \
src/mark-compact.cc \
diff --git a/ChangeLog b/ChangeLog
index 29ecccd7..339fd18f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2010-02-23: Version 2.1.2
+
+ Fix a crash bug caused by wrong assert.
+
+ Fix a bug with register names on 64-bit V8 (issue 615).
+
+ Performance improvements on all platforms.
+
+2010-02-19: Version 2.1.1
+
+ [ES5] Implemented Object.defineProperty.
+
+ Improved profiler support.
+
+ Added SetPrototype method in the public V8 API.
+
+ Added GetScriptOrigin and GetScriptLineNumber methods to Function
+ objects in the API.
+
+ Performance improvements on all platforms.
+
+
2010-02-03: Version 2.1.0
Values are now always wrapped in objects when used as a receiver.
diff --git a/include/v8.h b/include/v8.h
index 19a41f49..c911cc7a 100644
--- a/include/v8.h
+++ b/include/v8.h
@@ -534,51 +534,76 @@ class V8EXPORT ScriptOrigin {
class V8EXPORT Script {
public:
- /**
- * Compiles the specified script. The ScriptOrigin* and ScriptData*
- * parameters are owned by the caller of Script::Compile. No
- * references to these objects are kept after compilation finishes.
- *
- * The script object returned is context independent; when run it
- * will use the currently entered context.
- */
- static Local<Script> New(Handle<String> source,
- ScriptOrigin* origin = NULL,
- ScriptData* pre_data = NULL);
+ /**
+ * Compiles the specified script (context-independent).
+ *
+ * \param source Script source code.
+ * \param origin Script origin, owned by caller, no references are kept
+ * when New() returns
+ * \param pre_data Pre-parsing data, as obtained by ScriptData::PreCompile()
+ * using pre_data speeds compilation if it's done multiple times.
+ * Owned by caller, no references are kept when New() returns.
+ * \param script_data Arbitrary data associated with script. Using
+ * this has same effect as calling SetData(), but allows data to be
+ * available to compile event handlers.
+ * \return Compiled script object (context independent; when run it
+ * will use the currently entered context).
+ */
+ static Local<Script> New(Handle<String> source,
+ ScriptOrigin* origin = NULL,
+ ScriptData* pre_data = NULL,
+ Handle<String> script_data = Handle<String>());
- /**
- * Compiles the specified script using the specified file name
- * object (typically a string) as the script's origin.
- *
- * The script object returned is context independent; when run it
- * will use the currently entered context.
- */
- static Local<Script> New(Handle<String> source,
- Handle<Value> file_name);
-
- /**
- * Compiles the specified script. The ScriptOrigin* and ScriptData*
- * parameters are owned by the caller of Script::Compile. No
- * references to these objects are kept after compilation finishes.
+ /**
+ * Compiles the specified script using the specified file name
+ * object (typically a string) as the script's origin.
+ *
+ * \param source Script source code.
+ * \patam file_name file name object (typically a string) to be used
+ * as the script's origin.
+ * \return Compiled script object (context independent; when run it
+ * will use the currently entered context).
+ */
+ static Local<Script> New(Handle<String> source,
+ Handle<Value> file_name);
+
+ /**
+ * Compiles the specified script (bound to current context).
*
- * The script object returned is bound to the context that was active
- * when this function was called. When run it will always use this
- * context.
+ * \param source Script source code.
+ * \param origin Script origin, owned by caller, no references are kept
+ * when Compile() returns
+ * \param pre_data Pre-parsing data, as obtained by ScriptData::PreCompile()
+ * using pre_data speeds compilation if it's done multiple times.
+ * Owned by caller, no references are kept when Compile() returns.
+ * \param script_data Arbitrary data associated with script. Using
+ * this has same effect as calling SetData(), but makes data available
+ * earlier (i.e. to compile event handlers).
+ * \return Compiled script object, bound to the context that was active
+ * when this function was called. When run it will always use this
+ * context.
*/
static Local<Script> Compile(Handle<String> source,
ScriptOrigin* origin = NULL,
- ScriptData* pre_data = NULL);
+ ScriptData* pre_data = NULL,
+ Handle<String> script_data = Handle<String>());
/**
* Compiles the specified script using the specified file name
* object (typically a string) as the script's origin.
*
- * The script object returned is bound to the context that was active
- * when this function was called. When run it will always use this
- * context.
+ * \param source Script source code.
+ * \param file_name File name to use as script's origin
+ * \param script_data Arbitrary data associated with script. Using
+ * this has same effect as calling SetData(), but makes data available
+ * earlier (i.e. to compile event handlers).
+ * \return Compiled script object, bound to the context that was active
+ * when this function was called. When run it will always use this
+ * context.
*/
static Local<Script> Compile(Handle<String> source,
- Handle<Value> file_name);
+ Handle<Value> file_name,
+ Handle<String> script_data = Handle<String>());
/**
* Runs the script returning the resulting value. If the script is
@@ -1197,6 +1222,13 @@ class V8EXPORT Object : public Value {
Local<Value> GetPrototype();
/**
+ * Set the prototype object. This does not skip objects marked to
+ * be skipped by __proto__ and it does not consult the security
+ * handler.
+ */
+ bool SetPrototype(Handle<Value> prototype);
+
+ /**
* Finds an instance of the given function template in the prototype
* chain.
*/
@@ -1354,7 +1386,15 @@ class V8EXPORT Function : public Object {
Local<Value> Call(Handle<Object> recv, int argc, Handle<Value> argv[]);
void SetName(Handle<String> name);
Handle<Value> GetName() const;
+
+ /**
+ * Returns zero based line number of function body and
+ * kLineOffsetNotFound if no information available.
+ */
+ int GetScriptLineNumber() const;
+ ScriptOrigin GetScriptOrigin() const;
static inline Function* Cast(Value* obj);
+ static const int kLineOffsetNotFound;
private:
Function();
static void CheckCast(Value* obj);
@@ -2309,22 +2349,30 @@ class V8EXPORT V8 {
static bool IsProfilerPaused();
/**
- * Resumes specified profiler modules.
+ * Resumes specified profiler modules. Can be called several times to
+ * mark the opening of a profiler events block with the given tag.
+ *
* "ResumeProfiler" is equivalent to "ResumeProfilerEx(PROFILER_MODULE_CPU)".
* See ProfilerModules enum.
*
* \param flags Flags specifying profiler modules.
+ * \param tag Profile tag.
*/
- static void ResumeProfilerEx(int flags);
+ static void ResumeProfilerEx(int flags, int tag = 0);
/**
- * Pauses specified profiler modules.
+ * Pauses specified profiler modules. Each call to "PauseProfilerEx" closes
+ * a block of profiler events opened by a call to "ResumeProfilerEx" with the
+ * same tag value. There is no need for blocks to be properly nested.
+ * The profiler is paused when the last opened block is closed.
+ *
* "PauseProfiler" is equivalent to "PauseProfilerEx(PROFILER_MODULE_CPU)".
* See ProfilerModules enum.
*
* \param flags Flags specifying profiler modules.
+ * \param tag Profile tag.
*/
- static void PauseProfilerEx(int flags);
+ static void PauseProfilerEx(int flags, int tag = 0);
/**
* Returns active (resumed) profiler modules.
diff --git a/samples/lineprocessor.cc b/samples/lineprocessor.cc
index 505dabf9..61517d36 100644
--- a/samples/lineprocessor.cc
+++ b/samples/lineprocessor.cc
@@ -152,7 +152,7 @@ int RunMain(int argc, char* argv[]) {
} else if (strcmp(str, "--main-cycle-in-js") == 0) {
cycle_type = CycleInJs;
} else if (strcmp(str, "-p") == 0 && i + 1 < argc) {
- port_number = atoi(argv[i + 1]);
+ port_number = atoi(argv[i + 1]); // NOLINT
i++;
} else if (strncmp(str, "--", 2) == 0) {
printf("Warning: unknown flag %s.\nTry --help for options\n", str);
diff --git a/src/SConscript b/src/SConscript
index 864b4e78..3b227c8b 100755
--- a/src/SConscript
+++ b/src/SConscript
@@ -57,7 +57,6 @@ SOURCES = {
disassembler.cc
execution.cc
factory.cc
- fast-codegen.cc
flags.cc
frame-element.cc
frames.cc
@@ -72,6 +71,7 @@ SOURCES = {
interpreter-irregexp.cc
jsregexp.cc
jump-target.cc
+ liveedit.cc
log-utils.cc
log.cc
mark-compact.cc
@@ -108,6 +108,7 @@ SOURCES = {
zone.cc
"""),
'arch:arm': Split("""
+ fast-codegen.cc
arm/builtins-arm.cc
arm/codegen-arm.cc
arm/constants-arm.cc
@@ -132,6 +133,7 @@ SOURCES = {
arm/assembler-thumb2.cc
"""),
'arch:mips': Split("""
+ fast-codegen.cc
mips/assembler-mips.cc
mips/builtins-mips.cc
mips/codegen-mips.cc
@@ -168,6 +170,7 @@ SOURCES = {
ia32/virtual-frame-ia32.cc
"""),
'arch:x64': Split("""
+ fast-codegen.cc
x64/assembler-x64.cc
x64/builtins-x64.cc
x64/codegen-x64.cc
diff --git a/src/accessors.cc b/src/accessors.cc
index 5a029285..b05719ed 100644
--- a/src/accessors.cc
+++ b/src/accessors.cc
@@ -647,42 +647,9 @@ Object* Accessors::ObjectGetPrototype(Object* receiver, void*) {
Object* Accessors::ObjectSetPrototype(JSObject* receiver,
Object* value,
void*) {
- // Before we can set the prototype we need to be sure
- // prototype cycles are prevented.
- // It is sufficient to validate that the receiver is not in the new prototype
- // chain.
-
- // Silently ignore the change if value is not a JSObject or null.
- // SpiderMonkey behaves this way.
- if (!value->IsJSObject() && !value->IsNull()) return value;
-
- for (Object* pt = value; pt != Heap::null_value(); pt = pt->GetPrototype()) {
- if (JSObject::cast(pt) == receiver) {
- // Cycle detected.
- HandleScope scope;
- return Top::Throw(*Factory::NewError("cyclic_proto",
- HandleVector<Object>(NULL, 0)));
- }
- }
-
- // Find the first object in the chain whose prototype object is not
- // hidden and set the new prototype on that object.
- JSObject* current = receiver;
- Object* current_proto = receiver->GetPrototype();
- while (current_proto->IsJSObject() &&
- JSObject::cast(current_proto)->map()->is_hidden_prototype()) {
- current = JSObject::cast(current_proto);
- current_proto = current_proto->GetPrototype();
- }
-
- // Set the new prototype of the object.
- Object* new_map = current->map()->CopyDropTransitions();
- if (new_map->IsFailure()) return new_map;
- Map::cast(new_map)->set_prototype(value);
- current->set_map(Map::cast(new_map));
-
+ const bool skip_hidden_prototypes = true;
// To be consistent with other Set functions, return the value.
- return value;
+ return receiver->SetPrototype(value, skip_hidden_prototypes);
}
diff --git a/src/api.cc b/src/api.cc
index a949e6f4..262bf528 100644
--- a/src/api.cc
+++ b/src/api.cc
@@ -1107,7 +1107,8 @@ ScriptData* ScriptData::New(unsigned* data, int length) {
Local<Script> Script::New(v8::Handle<String> source,
v8::ScriptOrigin* origin,
- v8::ScriptData* script_data) {
+ v8::ScriptData* pre_data,
+ v8::Handle<String> script_data) {
ON_BAILOUT("v8::Script::New()", return Local<Script>());
LOG_API("Script::New");
ENTER_V8;
@@ -1127,13 +1128,13 @@ Local<Script> Script::New(v8::Handle<String> source,
}
}
EXCEPTION_PREAMBLE();
- i::ScriptDataImpl* pre_data = static_cast<i::ScriptDataImpl*>(script_data);
+ i::ScriptDataImpl* pre_data_impl = static_cast<i::ScriptDataImpl*>(pre_data);
// We assert that the pre-data is sane, even though we can actually
// handle it if it turns out not to be in release mode.
- ASSERT(pre_data == NULL || pre_data->SanityCheck());
+ ASSERT(pre_data_impl == NULL || pre_data_impl->SanityCheck());
// If the pre-data isn't sane we simply ignore it
- if (pre_data != NULL && !pre_data->SanityCheck()) {
- pre_data = NULL;
+ if (pre_data_impl != NULL && !pre_data_impl->SanityCheck()) {
+ pre_data_impl = NULL;
}
i::Handle<i::JSFunction> boilerplate =
i::Compiler::Compile(str,
@@ -1141,7 +1142,8 @@ Local<Script> Script::New(v8::Handle<String> source,
line_offset,
column_offset,
NULL,
- pre_data,
+ pre_data_impl,
+ Utils::OpenHandle(*script_data),
i::NOT_NATIVES_CODE);
has_pending_exception = boilerplate.is_null();
EXCEPTION_BAILOUT_CHECK(Local<Script>());
@@ -1158,11 +1160,12 @@ Local<Script> Script::New(v8::Handle<String> source,
Local<Script> Script::Compile(v8::Handle<String> source,
v8::ScriptOrigin* origin,
- v8::ScriptData* script_data) {
+ v8::ScriptData* pre_data,
+ v8::Handle<String> script_data) {
ON_BAILOUT("v8::Script::Compile()", return Local<Script>());
LOG_API("Script::Compile");
ENTER_V8;
- Local<Script> generic = New(source, origin, script_data);
+ Local<Script> generic = New(source, origin, pre_data, script_data);
if (generic.IsEmpty())
return generic;
i::Handle<i::JSFunction> boilerplate = Utils::OpenHandle(*generic);
@@ -1174,9 +1177,10 @@ Local<Script> Script::Compile(v8::Handle<String> source,
Local<Script> Script::Compile(v8::Handle<String> source,
- v8::Handle<Value> file_name) {
+ v8::Handle<Value> file_name,
+ v8::Handle<String> script_data) {
ScriptOrigin origin(file_name);
- return Compile(source, &origin);
+ return Compile(source, &origin, 0, script_data);
}
@@ -2035,6 +2039,19 @@ Local<Value> v8::Object::GetPrototype() {
}
+bool v8::Object::SetPrototype(Handle<Value> value) {
+ ON_BAILOUT("v8::Object::SetPrototype()", return false);
+ ENTER_V8;
+ i::Handle<i::JSObject> self = Utils::OpenHandle(this);
+ i::Handle<i::Object> value_obj = Utils::OpenHandle(*value);
+ EXCEPTION_PREAMBLE();
+ i::Handle<i::Object> result = i::SetPrototype(self, value_obj);
+ has_pending_exception = result.is_null();
+ EXCEPTION_BAILOUT_CHECK(false);
+ return true;
+}
+
+
Local<Object> v8::Object::FindInstanceInPrototypeChain(
v8::Handle<FunctionTemplate> tmpl) {
ON_BAILOUT("v8::Object::FindInstanceInPrototypeChain()",
@@ -2197,7 +2214,7 @@ Local<Value> v8::Object::GetRealNamedPropertyInPrototypeChain(
i::Handle<i::String> key_obj = Utils::OpenHandle(*key);
i::LookupResult lookup;
self_obj->LookupRealNamedPropertyInPrototypes(*key_obj, &lookup);
- if (lookup.IsValid()) {
+ if (lookup.IsProperty()) {
PropertyAttributes attributes;
i::Handle<i::Object> result(self_obj->GetProperty(*self_obj,
&lookup,
@@ -2216,7 +2233,7 @@ Local<Value> v8::Object::GetRealNamedProperty(Handle<String> key) {
i::Handle<i::String> key_obj = Utils::OpenHandle(*key);
i::LookupResult lookup;
self_obj->LookupRealNamedProperty(*key_obj, &lookup);
- if (lookup.IsValid()) {
+ if (lookup.IsProperty()) {
PropertyAttributes attributes;
i::Handle<i::Object> result(self_obj->GetProperty(*self_obj,
&lookup,
@@ -2448,6 +2465,99 @@ Handle<Value> Function::GetName() const {
}
+ScriptOrigin Function::GetScriptOrigin() const {
+ i::Handle<i::JSFunction> func = Utils::OpenHandle(this);
+ if (func->shared()->script()->IsScript()) {
+ i::Handle<i::Script> script(i::Script::cast(func->shared()->script()));
+ v8::ScriptOrigin origin(
+ Utils::ToLocal(i::Handle<i::Object>(script->name())),
+ v8::Integer::New(script->line_offset()->value()),
+ v8::Integer::New(script->column_offset()->value()));
+ return origin;
+ }
+ return v8::ScriptOrigin(Handle<Value>());
+}
+
+
+const int Function::kLineOffsetNotFound = -1;
+
+
+int Function::GetScriptLineNumber() const {
+ i::Handle<i::JSFunction> func = Utils::OpenHandle(this);
+ if (func->shared()->script()->IsScript()) {
+ i::Handle<i::Script> script(i::Script::cast(func->shared()->script()));
+ return i::GetScriptLineNumber(script, func->shared()->start_position());
+ }
+ return kLineOffsetNotFound;
+}
+
+
+namespace {
+
+// Tracks string usage to help make better decisions when
+// externalizing strings.
+//
+// Implementation note: internally this class only tracks fresh
+// strings and keeps a single use counter for them.
+class StringTracker {
+ public:
+ // Records that the given string's characters were copied to some
+ // external buffer. If this happens often we should honor
+ // externalization requests for the string.
+ static void RecordWrite(i::Handle<i::String> string) {
+ i::Address address = reinterpret_cast<i::Address>(*string);
+ i::Address top = i::Heap::NewSpaceTop();
+ if (IsFreshString(address, top)) {
+ IncrementUseCount(top);
+ }
+ }
+
+ // Estimates freshness and use frequency of the given string based
+ // on how close it is to the new space top and the recorded usage
+ // history.
+ static inline bool IsFreshUnusedString(i::Handle<i::String> string) {
+ i::Address address = reinterpret_cast<i::Address>(*string);
+ i::Address top = i::Heap::NewSpaceTop();
+ return IsFreshString(address, top) && IsUseCountLow(top);
+ }
+
+ private:
+ static inline bool IsFreshString(i::Address string, i::Address top) {
+ return top - kFreshnessLimit <= string && string <= top;
+ }
+
+ static inline bool IsUseCountLow(i::Address top) {
+ if (last_top_ != top) return true;
+ return use_count_ < kUseLimit;
+ }
+
+ static inline void IncrementUseCount(i::Address top) {
+ if (last_top_ != top) {
+ use_count_ = 0;
+ last_top_ = top;
+ }
+ ++use_count_;
+ }
+
+ // How close to the new space top a fresh string has to be.
+ static const int kFreshnessLimit = 1024;
+
+ // The number of uses required to consider a string useful.
+ static const int kUseLimit = 32;
+
+ // Single use counter shared by all fresh strings.
+ static int use_count_;
+
+ // Last new space top when the use count above was valid.
+ static i::Address last_top_;
+};
+
+int StringTracker::use_count_ = 0;
+i::Address StringTracker::last_top_ = NULL;
+
+} // namespace
+
+
int String::Length() const {
if (IsDeadCheck("v8::String::Length()")) return 0;
return Utils::OpenHandle(this)->length();
@@ -2465,6 +2575,7 @@ int String::WriteUtf8(char* buffer, int capacity) const {
LOG_API("String::WriteUtf8");
ENTER_V8;
i::Handle<i::String> str = Utils::OpenHandle(this);
+ StringTracker::RecordWrite(str);
write_input_buffer.Reset(0, *str);
int len = str->length();
// Encode the first K - 3 bytes directly into the buffer since we
@@ -2508,6 +2619,7 @@ int String::WriteAscii(char* buffer, int start, int length) const {
ENTER_V8;
ASSERT(start >= 0 && length >= -1);
i::Handle<i::String> str = Utils::OpenHandle(this);
+ StringTracker::RecordWrite(str);
// Flatten the string for efficiency. This applies whether we are
// using StringInputBuffer or Get(i) to access the characters.
str->TryFlattenIfNotFlat();
@@ -2534,6 +2646,7 @@ int String::Write(uint16_t* buffer, int start, int length) const {
ENTER_V8;
ASSERT(start >= 0 && length >= -1);
i::Handle<i::String> str = Utils::OpenHandle(this);
+ StringTracker::RecordWrite(str);
int end = length;
if ( (length == -1) || (length > str->length() - start) )
end = str->length() - start;
@@ -3102,6 +3215,7 @@ bool v8::String::MakeExternal(v8::String::ExternalStringResource* resource) {
if (this->IsExternal()) return false; // Already an external string.
ENTER_V8;
i::Handle<i::String> obj = Utils::OpenHandle(this);
+ if (StringTracker::IsFreshUnusedString(obj)) return false;
bool result = obj->MakeExternal(resource);
if (result && !obj->IsSymbol()) {
i::ExternalStringTable::AddString(*obj);
@@ -3127,6 +3241,7 @@ bool v8::String::MakeExternal(
if (this->IsExternal()) return false; // Already an external string.
ENTER_V8;
i::Handle<i::String> obj = Utils::OpenHandle(this);
+ if (StringTracker::IsFreshUnusedString(obj)) return false;
bool result = obj->MakeExternal(resource);
if (result && !obj->IsSymbol()) {
i::ExternalStringTable::AddString(*obj);
@@ -3138,6 +3253,7 @@ bool v8::String::MakeExternal(
bool v8::String::CanMakeExternal() {
if (IsDeadCheck("v8::String::CanMakeExternal()")) return false;
i::Handle<i::String> obj = Utils::OpenHandle(this);
+ if (StringTracker::IsFreshUnusedString(obj)) return false;
int size = obj->Size(); // Byte size of the original string.
if (size < i::ExternalString::kSize)
return false;
@@ -3361,14 +3477,14 @@ void V8::SetGlobalGCEpilogueCallback(GCCallback callback) {
void V8::PauseProfiler() {
#ifdef ENABLE_LOGGING_AND_PROFILING
- i::Logger::PauseProfiler(PROFILER_MODULE_CPU);
+ PauseProfilerEx(PROFILER_MODULE_CPU);
#endif
}
void V8::ResumeProfiler() {
#ifdef ENABLE_LOGGING_AND_PROFILING
- i::Logger::ResumeProfiler(PROFILER_MODULE_CPU);
+ ResumeProfilerEx(PROFILER_MODULE_CPU);
#endif
}
@@ -3382,7 +3498,7 @@ bool V8::IsProfilerPaused() {
}
-void V8::ResumeProfilerEx(int flags) {
+void V8::ResumeProfilerEx(int flags, int tag) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (flags & PROFILER_MODULE_HEAP_SNAPSHOT) {
// Snapshot mode: resume modules, perform GC, then pause only
@@ -3392,19 +3508,19 @@ void V8::ResumeProfilerEx(int flags) {
// Reset snapshot flag and CPU module flags.
flags &= ~(PROFILER_MODULE_HEAP_SNAPSHOT | PROFILER_MODULE_CPU);
const int current_flags = i::Logger::GetActiveProfilerModules();
- i::Logger::ResumeProfiler(flags);
+ i::Logger::ResumeProfiler(flags, tag);
i::Heap::CollectAllGarbage(false);
- i::Logger::PauseProfiler(~current_flags & flags);
+ i::Logger::PauseProfiler(~current_flags & flags, tag);
} else {
- i::Logger::ResumeProfiler(flags);
+ i::Logger::ResumeProfiler(flags, tag);
}
#endif
}
-void V8::PauseProfilerEx(int flags) {
+void V8::PauseProfilerEx(int flags, int tag) {
#ifdef ENABLE_LOGGING_AND_PROFILING
- i::Logger::PauseProfiler(flags);
+ i::Logger::PauseProfiler(flags, tag);
#endif
}
diff --git a/src/arm/assembler-arm.cc b/src/arm/assembler-arm.cc
index cf167f0f..1b3bcb0b 100644
--- a/src/arm/assembler-arm.cc
+++ b/src/arm/assembler-arm.cc
@@ -47,10 +47,29 @@ unsigned CpuFeatures::supported_ = 0;
unsigned CpuFeatures::enabled_ = 0;
unsigned CpuFeatures::found_by_runtime_probing_ = 0;
+
+#ifdef __arm__
+static uint64_t CpuFeaturesImpliedByCompiler() {
+ uint64_t answer = 0;
+#ifdef CAN_USE_ARMV7_INSTRUCTIONS
+ answer |= 1u << ARMv7;
+#endif // def CAN_USE_ARMV7_INSTRUCTIONS
+ // If the compiler is allowed to use VFP then we can use VFP too in our code
+ // generation even when generating snapshots. This won't work for cross
+ // compilation.
+#if defined(__VFP_FP__) && !defined(__SOFTFP__)
+ answer |= 1u << VFP3;
+#endif // defined(__VFP_FP__) && !defined(__SOFTFP__)
+#ifdef CAN_USE_VFP_INSTRUCTIONS
+ answer |= 1u << VFP3;
+#endif // def CAN_USE_VFP_INSTRUCTIONS
+ return answer;
+}
+#endif // def __arm__
+
+
void CpuFeatures::Probe() {
- // If the compiler is allowed to use vfp then we can use vfp too in our
- // code generation.
-#if !defined(__arm__)
+#ifndef __arm__
// For the simulator=arm build, use VFP when FLAG_enable_vfp3 is enabled.
if (FLAG_enable_vfp3) {
supported_ |= 1u << VFP3;
@@ -59,10 +78,10 @@ void CpuFeatures::Probe() {
if (FLAG_enable_armv7) {
supported_ |= 1u << ARMv7;
}
-#else
+#else // def __arm__
if (Serializer::enabled()) {
- supported_ |= 1u << VFP3;
- //supported_ |= OS::CpuFeaturesImpliedByPlatform();
+ supported_ |= OS::CpuFeaturesImpliedByPlatform();
+ supported_ |= CpuFeaturesImpliedByCompiler();
return; // No features if we might serialize.
}
@@ -77,7 +96,7 @@ void CpuFeatures::Probe() {
supported_ |= 1u << ARMv7;
found_by_runtime_probing_ |= 1u << ARMv7;
}
-#endif
+#endif // def __arm__
}
@@ -621,7 +640,7 @@ static bool MustUseIp(RelocInfo::Mode rmode) {
if (!Serializer::enabled()) {
Serializer::TooLateToEnableNow();
}
-#endif
+#endif // def DEBUG
return Serializer::enabled();
} else if (rmode == RelocInfo::NONE) {
return false;
@@ -1226,14 +1245,16 @@ void Assembler::swpb(Register dst,
// Exception-generating instructions and debugging support.
void Assembler::stop(const char* msg) {
-#if !defined(__arm__)
+#ifndef __arm__
// The simulator handles these special instructions and stops execution.
emit(15 << 28 | ((intptr_t) msg));
-#else
- // Just issue a simple break instruction for now. Alternatively we could use
- // the swi(0x9f0001) instruction on Linux.
+#else // def __arm__
+#ifdef CAN_USE_ARMV5_INSTRUCTIONS
bkpt(0);
-#endif
+#else // ndef CAN_USE_ARMV5_INSTRUCTIONS
+ swi(0x9f0001);
+#endif // ndef CAN_USE_ARMV5_INSTRUCTIONS
+#endif // def __arm__
}
diff --git a/src/arm/assembler-thumb2-inl.h b/src/arm/assembler-thumb2-inl.h
index 3808ef00..9e0fc2f7 100644
--- a/src/arm/assembler-thumb2-inl.h
+++ b/src/arm/assembler-thumb2-inl.h
@@ -174,20 +174,6 @@ Operand::Operand(const ExternalReference& f) {
}
-Operand::Operand(Object** opp) {
- rm_ = no_reg;
- imm32_ = reinterpret_cast<int32_t>(opp);
- rmode_ = RelocInfo::NONE;
-}
-
-
-Operand::Operand(Context** cpp) {
- rm_ = no_reg;
- imm32_ = reinterpret_cast<int32_t>(cpp);
- rmode_ = RelocInfo::NONE;
-}
-
-
Operand::Operand(Smi* value) {
rm_ = no_reg;
imm32_ = reinterpret_cast<intptr_t>(value);
@@ -229,14 +215,24 @@ void Assembler::emit(Instr x) {
Address Assembler::target_address_address_at(Address pc) {
- Instr instr = Memory::int32_at(pc);
- // Verify that the instruction at pc is a ldr<cond> <Rd>, [pc +/- offset_12].
+ Address target_pc = pc;
+ Instr instr = Memory::int32_at(target_pc);
+ // If we have a bx instruction, the instruction before the bx is
+ // what we need to patch.
+ static const int32_t kBxInstMask = 0x0ffffff0;
+ static const int32_t kBxInstPattern = 0x012fff10;
+ if ((instr & kBxInstMask) == kBxInstPattern) {
+ target_pc -= kInstrSize;
+ instr = Memory::int32_at(target_pc);
+ }
+ // Verify that the instruction to patch is a
+ // ldr<cond> <Rd>, [pc +/- offset_12].
ASSERT((instr & 0x0f7f0000) == 0x051f0000);
int offset = instr & 0xfff; // offset_12 is unsigned
if ((instr & (1 << 23)) == 0) offset = -offset; // U bit defines offset sign
// Verify that the constant pool comes after the instruction referencing it.
ASSERT(offset >= -4);
- return pc + offset + 8;
+ return target_pc + offset + 8;
}
diff --git a/src/arm/assembler-thumb2.cc b/src/arm/assembler-thumb2.cc
index 6c2b9032..e31c4291 100644
--- a/src/arm/assembler-thumb2.cc
+++ b/src/arm/assembler-thumb2.cc
@@ -30,9 +30,9 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
-// The original source code covered by the above license above has been modified
-// significantly by Google Inc.
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// The original source code covered by the above license above has been
+// modified significantly by Google Inc.
+// Copyright 2010 the V8 project authors. All rights reserved.
#include "v8.h"
@@ -51,9 +51,14 @@ void CpuFeatures::Probe() {
// If the compiler is allowed to use vfp then we can use vfp too in our
// code generation.
#if !defined(__arm__)
- // For the simulator=arm build, always use VFP since the arm simulator has
- // VFP support.
- supported_ |= 1u << VFP3;
+ // For the simulator=arm build, use VFP when FLAG_enable_vfp3 is enabled.
+ if (FLAG_enable_vfp3) {
+ supported_ |= 1u << VFP3;
+ }
+ // For the simulator=arm build, use ARMv7 when FLAG_enable_armv7 is enabled
+ if (FLAG_enable_armv7) {
+ supported_ |= 1u << ARMv7;
+ }
#else
if (Serializer::enabled()) {
supported_ |= OS::CpuFeaturesImpliedByPlatform();
@@ -66,6 +71,11 @@ void CpuFeatures::Probe() {
supported_ |= 1u << VFP3;
found_by_runtime_probing_ |= 1u << VFP3;
}
+
+ if (OS::ArmCpuHasFeature(ARMv7)) {
+ supported_ |= 1u << ARMv7;
+ found_by_runtime_probing_ |= 1u << ARMv7;
+ }
#endif
}
@@ -83,9 +93,9 @@ Register r4 = { 4 };
Register r5 = { 5 };
Register r6 = { 6 };
Register r7 = { 7 };
-Register r8 = { 8 };
+Register r8 = { 8 }; // Used as context register.
Register r9 = { 9 };
-Register r10 = { 10 };
+Register r10 = { 10 }; // Used as roots register.
Register fp = { 11 };
Register ip = { 12 };
Register sp = { 13 };
@@ -264,9 +274,9 @@ MemOperand::MemOperand(Register rn, Register rm,
// -----------------------------------------------------------------------------
-// Implementation of Assembler
+// Implementation of Assembler.
-// Instruction encoding bits
+// Instruction encoding bits.
enum {
H = 1 << 5, // halfword (or byte)
S6 = 1 << 6, // signed (or unsigned)
@@ -299,14 +309,14 @@ enum {
B26 = 1 << 26,
B27 = 1 << 27,
- // Instruction bit masks
+ // Instruction bit masks.
RdMask = 15 << 12, // in str instruction
CondMask = 15 << 28,
CoprocessorMask = 15 << 8,
OpCodeMask = 15 << 21, // in data-processing instructions
Imm24Mask = (1 << 24) - 1,
Off12Mask = (1 << 12) - 1,
- // Reserved condition
+ // Reserved condition.
nv = 15 << 28
};
@@ -327,13 +337,13 @@ const Instr kMovLrPc = al | 13*B21 | pc.code() | lr.code() * B12;
// ldr pc, [pc, #XXX]
const Instr kLdrPCPattern = al | B26 | L | pc.code() * B16;
-// spare_buffer_
+// Spare buffer.
static const int kMinimalBufferSize = 4*KB;
static byte* spare_buffer_ = NULL;
Assembler::Assembler(void* buffer, int buffer_size) {
if (buffer == NULL) {
- // do our own buffer management
+ // Do our own buffer management.
if (buffer_size <= kMinimalBufferSize) {
buffer_size = kMinimalBufferSize;
@@ -351,14 +361,14 @@ Assembler::Assembler(void* buffer, int buffer_size) {
own_buffer_ = true;
} else {
- // use externally provided buffer instead
+ // Use externally provided buffer instead.
ASSERT(buffer_size > 0);
buffer_ = static_cast<byte*>(buffer);
buffer_size_ = buffer_size;
own_buffer_ = false;
}
- // setup buffer pointers
+ // Setup buffer pointers.
ASSERT(buffer_ != NULL);
pc_ = buffer_;
reloc_info_writer.Reposition(buffer_ + buffer_size, pc_);
@@ -386,11 +396,11 @@ Assembler::~Assembler() {
void Assembler::GetCode(CodeDesc* desc) {
- // emit constant pool if necessary
+ // Emit constant pool if necessary.
CheckConstPool(true, false);
ASSERT(num_prinfo_ == 0);
- // setup desc
+ // Setup code descriptor.
desc->buffer = buffer_;
desc->buffer_size = buffer_size_;
desc->instr_size = pc_offset();
@@ -539,7 +549,7 @@ void Assembler::bind_to(Label* L, int pos) {
void Assembler::link_to(Label* L, Label* appendix) {
if (appendix->is_linked()) {
if (L->is_linked()) {
- // append appendix to L's list
+ // Append appendix to L's list.
int fixup_pos;
int link = L->pos();
do {
@@ -549,7 +559,7 @@ void Assembler::link_to(Label* L, Label* appendix) {
ASSERT(link == kEndOfChain);
target_at_put(fixup_pos, appendix->pos());
} else {
- // L is empty, simply use appendix
+ // L is empty, simply use appendix.
*L = *appendix;
}
}
@@ -575,12 +585,12 @@ void Assembler::next(Label* L) {
}
-// Low-level code emission routines depending on the addressing mode
+// Low-level code emission routines depending on the addressing mode.
static bool fits_shifter(uint32_t imm32,
uint32_t* rotate_imm,
uint32_t* immed_8,
Instr* instr) {
- // imm32 must be unsigned
+ // imm32 must be unsigned.
for (int rot = 0; rot < 16; rot++) {
uint32_t imm8 = (imm32 << 2*rot) | (imm32 >> (32 - 2*rot));
if ((imm8 <= 0xff)) {
@@ -589,7 +599,7 @@ static bool fits_shifter(uint32_t imm32,
return true;
}
}
- // if the opcode is mov or mvn and if ~imm32 fits, change the opcode
+ // If the opcode is mov or mvn and if ~imm32 fits, change the opcode.
if (instr != NULL && (*instr & 0xd*B21) == 0xd*B21) {
if (fits_shifter(~imm32, rotate_imm, immed_8, NULL)) {
*instr ^= 0x2*B21;
@@ -626,7 +636,7 @@ void Assembler::addrmod1(Instr instr,
CheckBuffer();
ASSERT((instr & ~(CondMask | OpCodeMask | S)) == 0);
if (!x.rm_.is_valid()) {
- // immediate
+ // Immediate.
uint32_t rotate_imm;
uint32_t immed_8;
if (MustUseIp(x.rmode_) ||
@@ -634,7 +644,7 @@ void Assembler::addrmod1(Instr instr,
// The immediate operand cannot be encoded as a shifter operand, so load
// it first to register ip and change the original instruction to use ip.
// However, if the original instruction is a 'mov rd, x' (not setting the
- // condition code), then replace it with a 'ldr rd, [pc]'
+ // condition code), then replace it with a 'ldr rd, [pc]'.
RecordRelocInfo(x.rmode_, x.imm32_);
CHECK(!rn.is(ip)); // rn should never be ip, or will be trashed
Condition cond = static_cast<Condition>(instr & CondMask);
@@ -648,16 +658,16 @@ void Assembler::addrmod1(Instr instr,
}
instr |= I | rotate_imm*B8 | immed_8;
} else if (!x.rs_.is_valid()) {
- // immediate shift
+ // Immediate shift.
instr |= x.shift_imm_*B7 | x.shift_op_ | x.rm_.code();
} else {
- // register shift
+ // Register shift.
ASSERT(!rn.is(pc) && !rd.is(pc) && !x.rm_.is(pc) && !x.rs_.is(pc));
instr |= x.rs_.code()*B8 | x.shift_op_ | B4 | x.rm_.code();
}
emit(instr | rn.code()*B16 | rd.code()*B12);
if (rn.is(pc) || x.rm_.is(pc))
- // block constant pool emission for one instruction after reading pc
+ // Block constant pool emission for one instruction after reading pc.
BlockConstPoolBefore(pc_offset() + kInstrSize);
}
@@ -666,15 +676,15 @@ void Assembler::addrmod2(Instr instr, Register rd, const MemOperand& x) {
ASSERT((instr & ~(CondMask | B | L)) == B26);
int am = x.am_;
if (!x.rm_.is_valid()) {
- // immediate offset
+ // Immediate offset.
int offset_12 = x.offset_;
if (offset_12 < 0) {
offset_12 = -offset_12;
am ^= U;
}
if (!is_uint12(offset_12)) {
- // immediate offset cannot be encoded, load it first to register ip
- // rn (and rd in a load) should never be ip, or will be trashed
+ // Immediate offset cannot be encoded, load it first to register ip
+ // rn (and rd in a load) should never be ip, or will be trashed.
ASSERT(!x.rn_.is(ip) && ((instr & L) == L || !rd.is(ip)));
mov(ip, Operand(x.offset_), LeaveCC,
static_cast<Condition>(instr & CondMask));
@@ -684,9 +694,9 @@ void Assembler::addrmod2(Instr instr, Register rd, const MemOperand& x) {
ASSERT(offset_12 >= 0); // no masking needed
instr |= offset_12;
} else {
- // register offset (shift_imm_ and shift_op_ are 0) or scaled
+ // Register offset (shift_imm_ and shift_op_ are 0) or scaled
// register offset the constructors make sure than both shift_imm_
- // and shift_op_ are initialized
+ // and shift_op_ are initialized.
ASSERT(!x.rm_.is(pc));
instr |= B25 | x.shift_imm_*B7 | x.shift_op_ | x.rm_.code();
}
@@ -700,15 +710,15 @@ void Assembler::addrmod3(Instr instr, Register rd, const MemOperand& x) {
ASSERT(x.rn_.is_valid());
int am = x.am_;
if (!x.rm_.is_valid()) {
- // immediate offset
+ // Immediate offset.
int offset_8 = x.offset_;
if (offset_8 < 0) {
offset_8 = -offset_8;
am ^= U;
}
if (!is_uint8(offset_8)) {
- // immediate offset cannot be encoded, load it first to register ip
- // rn (and rd in a load) should never be ip, or will be trashed
+ // Immediate offset cannot be encoded, load it first to register ip
+ // rn (and rd in a load) should never be ip, or will be trashed.
ASSERT(!x.rn_.is(ip) && ((instr & L) == L || !rd.is(ip)));
mov(ip, Operand(x.offset_), LeaveCC,
static_cast<Condition>(instr & CondMask));
@@ -718,15 +728,15 @@ void Assembler::addrmod3(Instr instr, Register rd, const MemOperand& x) {
ASSERT(offset_8 >= 0); // no masking needed
instr |= B | (offset_8 >> 4)*B8 | (offset_8 & 0xf);
} else if (x.shift_imm_ != 0) {
- // scaled register offset not supported, load index first
- // rn (and rd in a load) should never be ip, or will be trashed
+ // Scaled register offset not supported, load index first
+ // rn (and rd in a load) should never be ip, or will be trashed.
ASSERT(!x.rn_.is(ip) && ((instr & L) == L || !rd.is(ip)));
mov(ip, Operand(x.rm_, x.shift_op_, x.shift_imm_), LeaveCC,
static_cast<Condition>(instr & CondMask));
addrmod3(instr, rd, MemOperand(x.rn_, ip, x.am_));
return;
} else {
- // register offset
+ // Register offset.
ASSERT((am & (P|W)) == P || !x.rm_.is(pc)); // no pc index with writeback
instr |= x.rm_.code();
}
@@ -744,7 +754,7 @@ void Assembler::addrmod4(Instr instr, Register rn, RegList rl) {
void Assembler::addrmod5(Instr instr, CRegister crd, const MemOperand& x) {
- // unindexed addressing is not encoded by this function
+ // Unindexed addressing is not encoded by this function.
ASSERT_EQ((B27 | B26),
(instr & ~(CondMask | CoprocessorMask | P | U | N | W | L)));
ASSERT(x.rn_.is_valid() && !x.rm_.is_valid());
@@ -759,7 +769,7 @@ void Assembler::addrmod5(Instr instr, CRegister crd, const MemOperand& x) {
ASSERT(is_uint8(offset_8)); // unsigned word offset must fit in a byte
ASSERT((am & (P|W)) == P || !x.rn_.is(pc)); // no pc base with writeback
- // post-indexed addressing requires W == 1; different than in addrmod2/3
+ // Post-indexed addressing requires W == 1; different than in addrmod2/3.
if ((am & P) == 0)
am |= W;
@@ -782,7 +792,7 @@ int Assembler::branch_offset(Label* L, bool jump_elimination_allowed) {
}
// Block the emission of the constant pool, since the branch instruction must
- // be emitted at the pc offset recorded by the label
+ // be emitted at the pc offset recorded by the label.
BlockConstPoolBefore(pc_offset() + kInstrSize);
return target_pos - (pc_offset() + kPcLoadDelta);
}
@@ -804,7 +814,7 @@ void Assembler::label_at_put(Label* L, int at_offset) {
}
-// Branch instructions
+// Branch instructions.
void Assembler::b(int branch_offset, Condition cond) {
ASSERT((branch_offset & 3) == 0);
int imm24 = branch_offset >> 2;
@@ -812,7 +822,7 @@ void Assembler::b(int branch_offset, Condition cond) {
emit(cond | B27 | B25 | (imm24 & Imm24Mask));
if (cond == al)
- // dead code is a good location to emit the constant pool
+ // Dead code is a good location to emit the constant pool.
CheckConstPool(false, false);
}
@@ -849,7 +859,22 @@ void Assembler::bx(Register target, Condition cond) { // v5 and above, plus v4t
}
-// Data-processing instructions
+// Data-processing instructions.
+
+// UBFX <Rd>,<Rn>,#<lsb>,#<width - 1>
+// Instruction details available in ARM DDI 0406A, A8-464.
+// cond(31-28) | 01111(27-23)| 1(22) | 1(21) | widthm1(20-16) |
+// Rd(15-12) | lsb(11-7) | 101(6-4) | Rn(3-0)
+void Assembler::ubfx(Register dst, Register src1, const Operand& src2,
+ const Operand& src3, Condition cond) {
+ ASSERT(!src2.rm_.is_valid() && !src3.rm_.is_valid());
+ ASSERT(static_cast<uint32_t>(src2.imm32_) <= 0x1f);
+ ASSERT(static_cast<uint32_t>(src3.imm32_) <= 0x1f);
+ emit(cond | 0x3F*B21 | src3.imm32_*B16 |
+ dst.code()*B12 | src2.imm32_*B7 | 0x5*B4 | src1.code());
+}
+
+
void Assembler::and_(Register dst, Register src1, const Operand& src2,
SBit s, Condition cond) {
addrmod1(cond | 0*B21 | s, src1, dst, src2);
@@ -886,7 +911,7 @@ void Assembler::add(Register dst, Register src1, const Operand& src2,
if (FLAG_push_pop_elimination &&
last_bound_pos_ <= (pc_offset() - pattern_size) &&
reloc_info_writer.last_pc() <= (pc_ - pattern_size) &&
- // pattern
+ // Pattern.
instr_at(pc_ - 1 * kInstrSize) == kPopInstruction &&
(instr_at(pc_ - 2 * kInstrSize) & ~RdMask) == kPushRegPattern) {
pc_ -= 2 * kInstrSize;
@@ -960,7 +985,7 @@ void Assembler::mvn(Register dst, const Operand& src, SBit s, Condition cond) {
}
-// Multiply instructions
+// Multiply instructions.
void Assembler::mla(Register dst, Register src1, Register src2, Register srcA,
SBit s, Condition cond) {
ASSERT(!dst.is(pc) && !src1.is(pc) && !src2.is(pc) && !srcA.is(pc));
@@ -1029,7 +1054,7 @@ void Assembler::umull(Register dstL,
}
-// Miscellaneous arithmetic instructions
+// Miscellaneous arithmetic instructions.
void Assembler::clz(Register dst, Register src, Condition cond) {
// v5 and above.
ASSERT(!dst.is(pc) && !src.is(pc));
@@ -1038,7 +1063,7 @@ void Assembler::clz(Register dst, Register src, Condition cond) {
}
-// Status register access instructions
+// Status register access instructions.
void Assembler::mrs(Register dst, SRegister s, Condition cond) {
ASSERT(!dst.is(pc));
emit(cond | B24 | s | 15*B16 | dst.code()*B12);
@@ -1050,12 +1075,12 @@ void Assembler::msr(SRegisterFieldMask fields, const Operand& src,
ASSERT(fields >= B16 && fields < B20); // at least one field set
Instr instr;
if (!src.rm_.is_valid()) {
- // immediate
+ // Immediate.
uint32_t rotate_imm;
uint32_t immed_8;
if (MustUseIp(src.rmode_) ||
!fits_shifter(src.imm32_, &rotate_imm, &immed_8, NULL)) {
- // immediate operand cannot be encoded, load it first to register ip
+ // Immediate operand cannot be encoded, load it first to register ip.
RecordRelocInfo(src.rmode_, src.imm32_);
ldr(ip, MemOperand(pc, 0), cond);
msr(fields, Operand(ip), cond);
@@ -1070,7 +1095,7 @@ void Assembler::msr(SRegisterFieldMask fields, const Operand& src,
}
-// Load/Store instructions
+// Load/Store instructions.
void Assembler::ldr(Register dst, const MemOperand& src, Condition cond) {
if (dst.is(pc)) {
WriteRecordedPositions();
@@ -1085,7 +1110,7 @@ void Assembler::ldr(Register dst, const MemOperand& src, Condition cond) {
if (FLAG_push_pop_elimination &&
last_bound_pos_ <= (pc_offset() - pattern_size) &&
reloc_info_writer.last_pc() <= (pc_ - pattern_size) &&
- // pattern
+ // Pattern.
instr_at(pc_ - 1 * kInstrSize) == (kPopRegPattern | dst.code() * B12) &&
instr_at(pc_ - 2 * kInstrSize) == (kPushRegPattern | dst.code() * B12)) {
pc_ -= 2 * kInstrSize;
@@ -1106,6 +1131,7 @@ void Assembler::str(Register src, const MemOperand& dst, Condition cond) {
if (FLAG_push_pop_elimination &&
last_bound_pos_ <= (pc_offset() - pattern_size) &&
reloc_info_writer.last_pc() <= (pc_ - pattern_size) &&
+ // Pattern.
instr_at(pc_ - 1 * kInstrSize) == (kPushRegPattern | src.code() * B12) &&
instr_at(pc_ - 2 * kInstrSize) == kPopInstruction) {
pc_ -= 2 * kInstrSize;
@@ -1147,17 +1173,17 @@ void Assembler::ldrsh(Register dst, const MemOperand& src, Condition cond) {
}
-// Load/Store multiple instructions
+// Load/Store multiple instructions.
void Assembler::ldm(BlockAddrMode am,
Register base,
RegList dst,
Condition cond) {
- // ABI stack constraint: ldmxx base, {..sp..} base != sp is not restartable
+ // ABI stack constraint: ldmxx base, {..sp..} base != sp is not restartable.
ASSERT(base.is(sp) || (dst & sp.bit()) == 0);
addrmod4(cond | B27 | am | L, base, dst);
- // emit the constant pool after a function return implemented by ldm ..{..pc}
+ // Emit the constant pool after a function return implemented by ldm ..{..pc}.
if (cond == al && (dst & pc.bit()) != 0) {
// There is a slight chance that the ldm instruction was actually a call,
// in which case it would be wrong to return into the constant pool; we
@@ -1177,7 +1203,7 @@ void Assembler::stm(BlockAddrMode am,
}
-// Semaphore instructions
+// Semaphore instructions.
void Assembler::swp(Register dst, Register src, Register base, Condition cond) {
ASSERT(!dst.is(pc) && !src.is(pc) && !base.is(pc));
ASSERT(!dst.is(base) && !src.is(base));
@@ -1197,7 +1223,7 @@ void Assembler::swpb(Register dst,
}
-// Exception-generating instructions and debugging support
+// Exception-generating instructions and debugging support.
void Assembler::stop(const char* msg) {
#if !defined(__arm__)
// The simulator handles these special instructions and stops execution.
@@ -1222,7 +1248,7 @@ void Assembler::swi(uint32_t imm24, Condition cond) {
}
-// Coprocessor instructions
+// Coprocessor instructions.
void Assembler::cdp(Coprocessor coproc,
int opcode_1,
CRegister crd,
@@ -1307,7 +1333,7 @@ void Assembler::ldc(Coprocessor coproc,
int option,
LFlag l,
Condition cond) {
- // unindexed addressing
+ // Unindexed addressing.
ASSERT(is_uint8(option));
emit(cond | B27 | B26 | U | l | L | rn.code()*B16 | crd.code()*B12 |
coproc*B8 | (option & 255));
@@ -1346,7 +1372,7 @@ void Assembler::stc(Coprocessor coproc,
int option,
LFlag l,
Condition cond) {
- // unindexed addressing
+ // Unindexed addressing.
ASSERT(is_uint8(option));
emit(cond | B27 | B26 | U | l | rn.code()*B16 | crd.code()*B12 |
coproc*B8 | (option & 255));
@@ -1371,6 +1397,36 @@ void Assembler::stc2(Coprocessor coproc,
// Support for VFP.
+void Assembler::vldr(const DwVfpRegister dst,
+ const Register base,
+ int offset,
+ const Condition cond) {
+ // Ddst = MEM(Rbase + offset).
+ // Instruction details available in ARM DDI 0406A, A8-628.
+ // cond(31-28) | 1101(27-24)| 1001(23-20) | Rbase(19-16) |
+ // Vdst(15-12) | 1011(11-8) | offset
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
+ ASSERT(offset % 4 == 0);
+ emit(cond | 0xD9*B20 | base.code()*B16 | dst.code()*B12 |
+ 0xB*B8 | ((offset / 4) & 255));
+}
+
+
+void Assembler::vstr(const DwVfpRegister src,
+ const Register base,
+ int offset,
+ const Condition cond) {
+ // MEM(Rbase + offset) = Dsrc.
+ // Instruction details available in ARM DDI 0406A, A8-786.
+ // cond(31-28) | 1101(27-24)| 1000(23-20) | | Rbase(19-16) |
+ // Vsrc(15-12) | 1011(11-8) | (offset/4)
+ ASSERT(CpuFeatures::IsEnabled(VFP3));
+ ASSERT(offset % 4 == 0);
+ emit(cond | 0xD8*B20 | base.code()*B16 | src.code()*B12 |
+ 0xB*B8 | ((offset / 4) & 255));
+}
+
+
void Assembler::vmov(const DwVfpRegister dst,
const Register src1,
const Register src2,
@@ -1434,7 +1490,7 @@ void Assembler::vcvt(const DwVfpRegister dst,
const Condition cond) {
// Dd = Sm (integer in Sm converted to IEEE 64-bit doubles in Dd).
// Instruction details available in ARM DDI 0406A, A8-576.
- // cond(31-28) | 11101(27-23)| D=?(22) | 11(21-20) | 1(19) |opc2=000(18-16) |
+ // cond(31-28) | 11101(27-23)| D=?(22) | 11(21-20) | 1(19) | opc2=000(18-16) |
// Vd(15-12) | 101(11-9) | sz(8)=1 | op(7)=1 | 1(6) | M=?(5) | 0(4) | Vm(3-0)
ASSERT(CpuFeatures::IsEnabled(VFP3));
emit(cond | 0xE*B24 | B23 | 0x3*B20 | B19 |
@@ -1541,14 +1597,14 @@ void Assembler::vmrs(Register dst, Condition cond) {
}
-// Pseudo instructions
+// Pseudo instructions.
void Assembler::lea(Register dst,
const MemOperand& x,
SBit s,
Condition cond) {
int am = x.am_;
if (!x.rm_.is_valid()) {
- // immediate offset
+ // Immediate offset.
if ((am & P) == 0) // post indexing
mov(dst, Operand(x.rn_), s, cond);
else if ((am & U) == 0) // negative indexing
@@ -1582,7 +1638,7 @@ void Assembler::BlockConstPoolFor(int instructions) {
}
-// Debugging
+// Debugging.
void Assembler::RecordJSReturn() {
WriteRecordedPositions();
CheckBuffer();
@@ -1635,7 +1691,7 @@ void Assembler::WriteRecordedPositions() {
void Assembler::GrowBuffer() {
if (!own_buffer_) FATAL("external code buffer is too small");
- // compute new buffer size
+ // Compute new buffer size.
CodeDesc desc; // the new buffer
if (buffer_size_ < 4*KB) {
desc.buffer_size = 4*KB;
@@ -1646,20 +1702,20 @@ void Assembler::GrowBuffer() {
}
CHECK_GT(desc.buffer_size, 0); // no overflow
- // setup new buffer
+ // Setup new buffer.
desc.buffer = NewArray<byte>(desc.buffer_size);
desc.instr_size = pc_offset();
desc.reloc_size = (buffer_ + buffer_size_) - reloc_info_writer.pos();
- // copy the data
+ // Copy the data.
int pc_delta = desc.buffer - buffer_;
int rc_delta = (desc.buffer + desc.buffer_size) - (buffer_ + buffer_size_);
memmove(desc.buffer, buffer_, desc.instr_size);
memmove(reloc_info_writer.pos() + rc_delta,
reloc_info_writer.pos(), desc.reloc_size);
- // switch buffers
+ // Switch buffers.
DeleteArray(buffer_);
buffer_ = desc.buffer;
buffer_size_ = desc.buffer_size;
@@ -1667,11 +1723,11 @@ void Assembler::GrowBuffer() {
reloc_info_writer.Reposition(reloc_info_writer.pos() + rc_delta,
reloc_info_writer.last_pc() + pc_delta);
- // none of our relocation types are pc relative pointing outside the code
+ // None of our relocation types are pc relative pointing outside the code
// buffer nor pc absolute pointing inside the code buffer, so there is no need
- // to relocate any emitted relocation entries
+ // to relocate any emitted relocation entries.
- // relocate pending relocation entries
+ // Relocate pending relocation entries.
for (int i = 0; i < num_prinfo_; i++) {
RelocInfo& rinfo = prinfo_[i];
ASSERT(rinfo.rmode() != RelocInfo::COMMENT &&
@@ -1686,16 +1742,16 @@ void Assembler::GrowBuffer() {
void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) {
RelocInfo rinfo(pc_, rmode, data); // we do not try to reuse pool constants
if (rmode >= RelocInfo::JS_RETURN && rmode <= RelocInfo::STATEMENT_POSITION) {
- // Adjust code for new modes
+ // Adjust code for new modes.
ASSERT(RelocInfo::IsJSReturn(rmode)
|| RelocInfo::IsComment(rmode)
|| RelocInfo::IsPosition(rmode));
- // these modes do not need an entry in the constant pool
+ // These modes do not need an entry in the constant pool.
} else {
ASSERT(num_prinfo_ < kMaxNumPRInfo);
prinfo_[num_prinfo_++] = rinfo;
// Make sure the constant pool is not emitted in place of the next
- // instruction for which we just recorded relocation info
+ // instruction for which we just recorded relocation info.
BlockConstPoolBefore(pc_offset() + kInstrSize);
}
if (rinfo.rmode() != RelocInfo::NONE) {
@@ -1722,7 +1778,7 @@ void Assembler::CheckConstPool(bool force_emit, bool require_jump) {
// blocked for a specific range.
next_buffer_check_ = pc_offset() + kCheckConstInterval;
- // There is nothing to do if there are no pending relocation info entries
+ // There is nothing to do if there are no pending relocation info entries.
if (num_prinfo_ == 0) return;
// We emit a constant pool at regular intervals of about kDistBetweenPools
@@ -1748,10 +1804,11 @@ void Assembler::CheckConstPool(bool force_emit, bool require_jump) {
// no_const_pool_before_, which is checked here. Also, recursive calls to
// CheckConstPool are blocked by no_const_pool_before_.
if (pc_offset() < no_const_pool_before_) {
- // Emission is currently blocked; make sure we try again as soon as possible
+ // Emission is currently blocked; make sure we try again as soon as
+ // possible.
next_buffer_check_ = no_const_pool_before_;
- // Something is wrong if emission is forced and blocked at the same time
+ // Something is wrong if emission is forced and blocked at the same time.
ASSERT(!force_emit);
return;
}
@@ -1765,23 +1822,23 @@ void Assembler::CheckConstPool(bool force_emit, bool require_jump) {
jump_instr + kInstrSize + num_prinfo_*(kInstrSize + kMaxRelocSize);
while (buffer_space() <= (max_needed_space + kGap)) GrowBuffer();
- // Block recursive calls to CheckConstPool
+ // Block recursive calls to CheckConstPool.
BlockConstPoolBefore(pc_offset() + jump_instr + kInstrSize +
num_prinfo_*kInstrSize);
// Don't bother to check for the emit calls below.
next_buffer_check_ = no_const_pool_before_;
- // Emit jump over constant pool if necessary
+ // Emit jump over constant pool if necessary.
Label after_pool;
if (require_jump) b(&after_pool);
RecordComment("[ Constant Pool");
- // Put down constant pool marker
- // "Undefined instruction" as specified by A3.1 Instruction set encoding
+ // Put down constant pool marker "Undefined instruction" as specified by
+ // A3.1 Instruction set encoding.
emit(0x03000000 | num_prinfo_);
- // Emit constant pool entries
+ // Emit constant pool entries.
for (int i = 0; i < num_prinfo_; i++) {
RelocInfo& rinfo = prinfo_[i];
ASSERT(rinfo.rmode() != RelocInfo::COMMENT &&
@@ -1789,8 +1846,8 @@ void Assembler::CheckConstPool(bool force_emit, bool require_jump) {
rinfo.rmode() != RelocInfo::STATEMENT_POSITION);
Instr instr = instr_at(rinfo.pc());
- // Instruction to patch must be a ldr/str [pc, #offset]
- // P and U set, B and W clear, Rn == pc, offset12 still 0
+ // Instruction to patch must be a ldr/str [pc, #offset].
+ // P and U set, B and W clear, Rn == pc, offset12 still 0.
ASSERT((instr & (7*B25 | P | U | B | W | 15*B16 | Off12Mask)) ==
(2*B25 | P | U | pc.code()*B16));
int delta = pc_ - rinfo.pc() - 8;
diff --git a/src/arm/assembler-thumb2.h b/src/arm/assembler-thumb2.h
index 9998e638..869ac461 100644
--- a/src/arm/assembler-thumb2.h
+++ b/src/arm/assembler-thumb2.h
@@ -30,9 +30,9 @@
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
-// The original source code covered by the above license above has been modified
-// significantly by Google Inc.
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// The original source code covered by the above license above has been
+// modified significantly by Google Inc.
+// Copyright 2010 the V8 project authors. All rights reserved.
// A light-weight ARM Assembler
// Generates user mode instructions for the ARM architecture up to version 5
@@ -250,7 +250,7 @@ enum Coprocessor {
};
-// Condition field in instructions
+// Condition field in instructions.
enum Condition {
eq = 0 << 28, // Z set equal.
ne = 1 << 28, // Z clear not equal.
@@ -398,8 +398,6 @@ class Operand BASE_EMBEDDED {
RelocInfo::Mode rmode = RelocInfo::NONE));
INLINE(explicit Operand(const ExternalReference& f));
INLINE(explicit Operand(const char* s));
- INLINE(explicit Operand(Object** opp));
- INLINE(explicit Operand(Context** cpp));
explicit Operand(Handle<Object> handle);
INLINE(explicit Operand(Smi* value));
@@ -630,6 +628,9 @@ class Assembler : public Malloced {
void blx(Label* L) { blx(branch_offset(L, false)); } // v5 and above
// Data-processing instructions
+ void ubfx(Register dst, Register src1, const Operand& src2,
+ const Operand& src3, Condition cond = al);
+
void and_(Register dst, Register src1, const Operand& src2,
SBit s = LeaveCC, Condition cond = al);
@@ -796,6 +797,14 @@ class Assembler : public Malloced {
// However, some simple modifications can allow
// these APIs to support D16 to D31.
+ void vldr(const DwVfpRegister dst,
+ const Register base,
+ int offset, // Offset must be a multiple of 4.
+ const Condition cond = al);
+ void vstr(const DwVfpRegister src,
+ const Register base,
+ int offset, // Offset must be a multiple of 4.
+ const Condition cond = al);
void vmov(const DwVfpRegister dst,
const Register src1,
const Register src2,
diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc
index ae7dae3b..edb1b0ae 100644
--- a/src/arm/builtins-arm.cc
+++ b/src/arm/builtins-arm.cc
@@ -499,7 +499,10 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
// r0: number of arguments
// r1: called object
__ bind(&non_function_call);
-
+ // CALL_NON_FUNCTION expects the non-function constructor as receiver
+ // (instead of the original receiver from the call site). The receiver is
+ // stack element argc.
+ __ str(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
// Set expected number of arguments to zero (not changing r0).
__ mov(r2, Operand(0));
__ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
@@ -904,7 +907,7 @@ void Builtins::Generate_JSConstructEntryTrampoline(MacroAssembler* masm) {
void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
// 1. Make sure we have at least one argument.
- // r0: actual number of argument
+ // r0: actual number of arguments
{ Label done;
__ tst(r0, Operand(r0));
__ b(ne, &done);
@@ -914,40 +917,31 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ bind(&done);
}
- // 2. Get the function to call from the stack.
- // r0: actual number of argument
- { Label done, non_function, function;
- __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
- __ tst(r1, Operand(kSmiTagMask));
- __ b(eq, &non_function);
- __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
- __ b(eq, &function);
-
- // Non-function called: Clear the function to force exception.
- __ bind(&non_function);
- __ mov(r1, Operand(0));
- __ b(&done);
-
- // Change the context eagerly because it will be used below to get the
- // right global object.
- __ bind(&function);
- __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
-
- __ bind(&done);
- }
+ // 2. Get the function to call (passed as receiver) from the stack, check
+ // if it is a function.
+ // r0: actual number of arguments
+ Label non_function;
+ __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
+ __ tst(r1, Operand(kSmiTagMask));
+ __ b(eq, &non_function);
+ __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
+ __ b(ne, &non_function);
- // 3. Make sure first argument is an object; convert if necessary.
+ // 3a. Patch the first argument if necessary when calling a function.
// r0: actual number of arguments
// r1: function
- { Label call_to_object, use_global_receiver, patch_receiver, done;
+ Label shift_arguments;
+ { Label convert_to_object, use_global_receiver, patch_receiver;
+ // Change context eagerly in case we need the global receiver.
+ __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
+
__ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
__ ldr(r2, MemOperand(r2, -kPointerSize));
-
// r0: actual number of arguments
// r1: function
// r2: first argument
__ tst(r2, Operand(kSmiTagMask));
- __ b(eq, &call_to_object);
+ __ b(eq, &convert_to_object);
__ LoadRoot(r3, Heap::kNullValueRootIndex);
__ cmp(r2, r3);
@@ -957,31 +951,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ b(eq, &use_global_receiver);
__ CompareObjectType(r2, r3, r3, FIRST_JS_OBJECT_TYPE);
- __ b(lt, &call_to_object);
+ __ b(lt, &convert_to_object);
__ cmp(r3, Operand(LAST_JS_OBJECT_TYPE));
- __ b(le, &done);
-
- __ bind(&call_to_object);
- __ EnterInternalFrame();
+ __ b(le, &shift_arguments);
- // Store number of arguments and function across the call into the runtime.
- __ mov(r0, Operand(r0, LSL, kSmiTagSize));
+ __ bind(&convert_to_object);
+ __ EnterInternalFrame(); // In order to preserve argument count.
+ __ mov(r0, Operand(r0, LSL, kSmiTagSize)); // Smi-tagged.
__ push(r0);
- __ push(r1);
__ push(r2);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_JS);
__ mov(r2, r0);
- // Restore number of arguments and function.
- __ pop(r1);
__ pop(r0);
__ mov(r0, Operand(r0, ASR, kSmiTagSize));
-
__ LeaveInternalFrame();
- __ b(&patch_receiver);
+ // Restore the function to r1.
+ __ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
+ __ jmp(&patch_receiver);
- // Use the global receiver object from the called function as the receiver.
+ // Use the global receiver object from the called function as the
+ // receiver.
__ bind(&use_global_receiver);
const int kGlobalIndex =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
@@ -994,16 +985,30 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ add(r3, sp, Operand(r0, LSL, kPointerSizeLog2));
__ str(r2, MemOperand(r3, -kPointerSize));
- __ bind(&done);
+ __ jmp(&shift_arguments);
}
- // 4. Shift stuff one slot down the stack
- // r0: actual number of arguments (including call() receiver)
+ // 3b. Patch the first argument when calling a non-function. The
+ // CALL_NON_FUNCTION builtin expects the non-function callee as
+ // receiver, so overwrite the first argument which will ultimately
+ // become the receiver.
+ // r0: actual number of arguments
+ // r1: function
+ __ bind(&non_function);
+ __ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
+ __ str(r1, MemOperand(r2, -kPointerSize));
+ // Clear r1 to indicate a non-function being called.
+ __ mov(r1, Operand(0));
+
+ // 4. Shift arguments and return address one slot down on the stack
+ // (overwriting the original receiver). Adjust argument count to make
+ // the original first argument the new receiver.
+ // r0: actual number of arguments
// r1: function
+ __ bind(&shift_arguments);
{ Label loop;
// Calculate the copy start address (destination). Copy end address is sp.
__ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
- __ add(r2, r2, Operand(kPointerSize)); // copy receiver too
__ bind(&loop);
__ ldr(ip, MemOperand(r2, -kPointerSize));
@@ -1011,43 +1016,41 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ sub(r2, r2, Operand(kPointerSize));
__ cmp(r2, sp);
__ b(ne, &loop);
+ // Adjust the actual number of arguments and remove the top element
+ // (which is a copy of the last argument).
+ __ sub(r0, r0, Operand(1));
+ __ pop();
}
- // 5. Adjust the actual number of arguments and remove the top element.
- // r0: actual number of arguments (including call() receiver)
- // r1: function
- __ sub(r0, r0, Operand(1));
- __ add(sp, sp, Operand(kPointerSize));
-
- // 6. Get the code for the function or the non-function builtin.
- // If number of expected arguments matches, then call. Otherwise restart
- // the arguments adaptor stub.
+ // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
// r0: actual number of arguments
// r1: function
- { Label invoke;
+ { Label function;
__ tst(r1, r1);
- __ b(ne, &invoke);
+ __ b(ne, &function);
__ mov(r2, Operand(0)); // expected arguments is 0 for CALL_NON_FUNCTION
__ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
RelocInfo::CODE_TARGET);
+ __ bind(&function);
+ }
- __ bind(&invoke);
- __ ldr(r3, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
- __ ldr(r2,
- FieldMemOperand(r3,
- SharedFunctionInfo::kFormalParameterCountOffset));
- __ ldr(r3,
- MemOperand(r3, SharedFunctionInfo::kCodeOffset - kHeapObjectTag));
- __ add(r3, r3, Operand(Code::kHeaderSize - kHeapObjectTag));
- __ cmp(r2, r0); // Check formal and actual parameter counts.
- __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
- RelocInfo::CODE_TARGET, ne);
+ // 5b. Get the code to call from the function and check that the number of
+ // expected arguments matches what we're providing. If so, jump
+ // (tail-call) to the code in register edx without checking arguments.
+ // r0: actual number of arguments
+ // r1: function
+ __ ldr(r3, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
+ __ ldr(r2,
+ FieldMemOperand(r3, SharedFunctionInfo::kFormalParameterCountOffset));
+ __ ldr(r3, FieldMemOperand(r3, SharedFunctionInfo::kCodeOffset));
+ __ add(r3, r3, Operand(Code::kHeaderSize - kHeapObjectTag));
+ __ cmp(r2, r0); // Check formal and actual parameter counts.
+ __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
+ RelocInfo::CODE_TARGET, ne);
- // 7. Jump to the code in r3 without checking arguments.
- ParameterCount expected(0);
- __ InvokeCode(r3, expected, expected, JUMP_FUNCTION);
- }
+ ParameterCount expected(0);
+ __ InvokeCode(r3, expected, expected, JUMP_FUNCTION);
}
diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc
index 9afefac0..e47d3921 100644
--- a/src/arm/codegen-arm.cc
+++ b/src/arm/codegen-arm.cc
@@ -142,7 +142,7 @@ Scope* CodeGenerator::scope() { return info_->function()->scope(); }
// r1: called JS function
// cp: callee's context
-void CodeGenerator::Generate(CompilationInfo* info, Mode mode) {
+void CodeGenerator::Generate(CompilationInfo* info) {
// Record the position for debugging purposes.
CodeForFunctionPosition(info->function());
@@ -174,7 +174,7 @@ void CodeGenerator::Generate(CompilationInfo* info, Mode mode) {
}
#endif
- if (mode == PRIMARY) {
+ if (info->mode() == CompilationInfo::PRIMARY) {
frame_->Enter();
// tos: code slot
@@ -277,6 +277,12 @@ void CodeGenerator::Generate(CompilationInfo* info, Mode mode) {
frame_->Adjust(4);
allocator_->Unuse(r1);
allocator_->Unuse(lr);
+
+ // Bind all the bailout labels to the beginning of the function.
+ List<CompilationInfo::Bailout*>* bailouts = info->bailouts();
+ for (int i = 0; i < bailouts->length(); i++) {
+ __ bind(bailouts->at(i)->label());
+ }
}
// Initialize the function return target after the locals are set
@@ -2293,8 +2299,7 @@ void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
Comment cmnt(masm_, "[ DebuggerStatament");
CodeForStatementPosition(node);
#ifdef ENABLE_DEBUGGER_SUPPORT
- DebuggerStatementStub ces;
- frame_->CallStub(&ces, 0);
+ frame_->DebugBreak();
#endif
// Ignore the return value.
ASSERT(frame_->height() == original_height);
@@ -2719,9 +2724,9 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
frame_->CallRuntime(Runtime::kCreateObjectLiteralShallow, 3);
}
frame_->EmitPush(r0); // save the result
- // r0: created object literal
-
for (int i = 0; i < node->properties()->length(); i++) {
+ // At the start of each iteration, the top of stack contains
+ // the newly created object literal.
ObjectLiteral::Property* property = node->properties()->at(i);
Literal* key = property->key();
Expression* value = property->value();
@@ -2731,34 +2736,43 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
case ObjectLiteral::Property::MATERIALIZED_LITERAL:
if (CompileTimeValue::IsCompileTimeValue(property->value())) break;
// else fall through
- case ObjectLiteral::Property::COMPUTED: // fall through
+ case ObjectLiteral::Property::COMPUTED:
+ if (key->handle()->IsSymbol()) {
+ Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
+ LoadAndSpill(value);
+ frame_->EmitPop(r0);
+ __ mov(r2, Operand(key->handle()));
+ __ ldr(r1, frame_->Top()); // Load the receiver.
+ frame_->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
+ break;
+ }
+ // else fall through
case ObjectLiteral::Property::PROTOTYPE: {
+ __ ldr(r0, frame_->Top());
frame_->EmitPush(r0); // dup the result
LoadAndSpill(key);
LoadAndSpill(value);
frame_->CallRuntime(Runtime::kSetProperty, 3);
- // restore r0
- __ ldr(r0, frame_->Top());
break;
}
case ObjectLiteral::Property::SETTER: {
+ __ ldr(r0, frame_->Top());
frame_->EmitPush(r0);
LoadAndSpill(key);
__ mov(r0, Operand(Smi::FromInt(1)));
frame_->EmitPush(r0);
LoadAndSpill(value);
frame_->CallRuntime(Runtime::kDefineAccessor, 4);
- __ ldr(r0, frame_->Top());
break;
}
case ObjectLiteral::Property::GETTER: {
+ __ ldr(r0, frame_->Top());
frame_->EmitPush(r0);
LoadAndSpill(key);
__ mov(r0, Operand(Smi::FromInt(0)));
frame_->EmitPush(r0);
LoadAndSpill(value);
frame_->CallRuntime(Runtime::kDefineAccessor, 4);
- __ ldr(r0, frame_->Top());
break;
}
}
@@ -2776,17 +2790,19 @@ void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
// Load the function of this activation.
__ ldr(r2, frame_->Function());
- // Literals array.
+ // Load the literals array of the function.
__ ldr(r2, FieldMemOperand(r2, JSFunction::kLiteralsOffset));
- // Literal index.
__ mov(r1, Operand(Smi::FromInt(node->literal_index())));
- // Constant elements.
__ mov(r0, Operand(node->constant_elements()));
frame_->EmitPushMultiple(3, r2.bit() | r1.bit() | r0.bit());
+ int length = node->values()->length();
if (node->depth() > 1) {
frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3);
- } else {
+ } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
+ } else {
+ FastCloneShallowArrayStub stub(length);
+ frame_->CallStub(&stub, 3);
}
frame_->EmitPush(r0); // save the result
// r0: created object literal
@@ -3013,11 +3029,6 @@ void CodeGenerator::VisitCall(Call* node) {
// ----------------------------------
// JavaScript example: 'foo(1, 2, 3)' // foo is global
// ----------------------------------
-
- // Push the name of the function and the receiver onto the stack.
- __ mov(r0, Operand(var->name()));
- frame_->EmitPush(r0);
-
// Pass the global object as the receiver and let the IC stub
// patch the stack to use the global proxy as 'this' in the
// invoked function.
@@ -3029,15 +3040,14 @@ void CodeGenerator::VisitCall(Call* node) {
LoadAndSpill(args->at(i));
}
- // Setup the receiver register and call the IC initialization code.
+ // Setup the name register and call the IC initialization code.
+ __ mov(r2, Operand(var->name()));
InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
CodeForSourcePosition(node->position());
frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET_CONTEXT,
arg_count + 1);
__ ldr(cp, frame_->Context());
- // Remove the function from the stack.
- frame_->Drop();
frame_->EmitPush(r0);
} else if (var != NULL && var->slot() != NULL &&
@@ -3070,28 +3080,21 @@ void CodeGenerator::VisitCall(Call* node) {
// JavaScript example: 'object.foo(1, 2, 3)' or 'map["key"](1, 2, 3)'
// ------------------------------------------------------------------
- // Push the name of the function and the receiver onto the stack.
- __ mov(r0, Operand(literal->handle()));
- frame_->EmitPush(r0);
- LoadAndSpill(property->obj());
-
+ LoadAndSpill(property->obj()); // Receiver.
// Load the arguments.
int arg_count = args->length();
for (int i = 0; i < arg_count; i++) {
LoadAndSpill(args->at(i));
}
- // Set the receiver register and call the IC initialization code.
+ // Set the name register and call the IC initialization code.
+ __ mov(r2, Operand(literal->handle()));
InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
CodeForSourcePosition(node->position());
frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
__ ldr(cp, frame_->Context());
-
- // Remove the function from the stack.
- frame_->Drop();
-
- frame_->EmitPush(r0); // push after get rid of function from the stack
+ frame_->EmitPush(r0);
} else {
// -------------------------------------------
@@ -3423,6 +3426,25 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
}
+void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
+ VirtualFrame::SpilledScope spilled_scope;
+ ASSERT(args->length() == 1);
+ LoadAndSpill(args->at(0));
+ JumpTarget answer;
+ // We need the CC bits to come out as not_equal in the case where the
+ // object is a smi. This can't be done with the usual test opcode so
+ // we use XOR to get the right CC bits.
+ frame_->EmitPop(r0);
+ __ and_(r1, r0, Operand(kSmiTagMask));
+ __ eor(r1, r1, Operand(kSmiTagMask), SetCC);
+ answer.Branch(ne);
+ // It is a heap object - get the map. Check if the object is a regexp.
+ __ CompareObjectType(r0, r1, r1, JS_REGEXP_TYPE);
+ answer.Bind();
+ cc_reg_ = eq;
+}
+
+
void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
// This generates a fast version of:
// (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
@@ -3595,6 +3617,35 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
}
+void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 1);
+
+ // Load the argument on the stack and jump to the runtime.
+ Load(args->at(0));
+
+ frame_->CallRuntime(Runtime::kNumberToString, 1);
+ frame_->EmitPush(r0);
+}
+
+
+void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 1);
+ // Load the argument on the stack and jump to the runtime.
+ Load(args->at(0));
+ frame_->CallRuntime(Runtime::kMath_sin, 1);
+ frame_->EmitPush(r0);
+}
+
+
+void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 1);
+ // Load the argument on the stack and jump to the runtime.
+ Load(args->at(0));
+ frame_->CallRuntime(Runtime::kMath_cos, 1);
+ frame_->EmitPush(r0);
+}
+
+
void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
VirtualFrame::SpilledScope spilled_scope;
ASSERT(args->length() == 2);
@@ -3626,8 +3677,6 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
if (function == NULL) {
// Prepare stack for calling JS runtime function.
- __ mov(r0, Operand(node->name()));
- frame_->EmitPush(r0);
// Push the builtins object found in the current global object.
__ ldr(r1, GlobalObject());
__ ldr(r0, FieldMemOperand(r1, GlobalObject::kBuiltinsOffset));
@@ -3642,11 +3691,11 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
if (function == NULL) {
// Call the JS runtime function.
+ __ mov(r2, Operand(node->name()));
InLoopFlag in_loop = loop_nesting() > 0 ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> stub = ComputeCallInitialize(arg_count, in_loop);
frame_->CallCodeObject(stub, RelocInfo::CODE_TARGET, arg_count + 1);
__ ldr(cp, frame_->Context());
- frame_->Drop();
frame_->EmitPush(r0);
} else {
// Call the C runtime function.
@@ -4389,11 +4438,11 @@ void Reference::SetValue(InitState init_state) {
Handle<String> name(GetName());
frame->EmitPop(r0);
- // Setup the name register.
+ frame->EmitPop(r1);
__ mov(r2, Operand(name));
frame->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
frame->EmitPush(r0);
- cgen_->UnloadReference(this);
+ set_unloaded();
break;
}
@@ -4405,7 +4454,6 @@ void Reference::SetValue(InitState init_state) {
// Call IC code.
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
- // TODO(1222589): Make the IC grab the values from the stack.
frame->EmitPop(r0); // value
frame->CallCodeObject(ic, RelocInfo::CODE_TARGET, 0);
frame->EmitPush(r0);
@@ -4480,7 +4528,7 @@ void FastNewContextStub::Generate(MacroAssembler* masm) {
TAG_OBJECT);
// Load the function from the stack.
- __ ldr(r3, MemOperand(sp, 0 * kPointerSize));
+ __ ldr(r3, MemOperand(sp, 0));
// Setup the object header.
__ LoadRoot(r2, Heap::kContextMapRootIndex);
@@ -4516,6 +4564,69 @@ void FastNewContextStub::Generate(MacroAssembler* masm) {
}
+void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
+ // Stack layout on entry:
+ //
+ // [sp]: constant elements.
+ // [sp + kPointerSize]: literal index.
+ // [sp + (2 * kPointerSize)]: literals array.
+
+ // All sizes here are multiples of kPointerSize.
+ int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0;
+ int size = JSArray::kSize + elements_size;
+
+ // Load boilerplate object into r3 and check if we need to create a
+ // boilerplate.
+ Label slow_case;
+ __ ldr(r3, MemOperand(sp, 2 * kPointerSize));
+ __ ldr(r0, MemOperand(sp, 1 * kPointerSize));
+ __ add(r3, r3, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ ldr(r3, MemOperand(r3, r0, LSL, kPointerSizeLog2 - kSmiTagSize));
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ cmp(r3, ip);
+ __ b(eq, &slow_case);
+
+ // Allocate both the JS array and the elements array in one big
+ // allocation. This avoids multiple limit checks.
+ __ AllocateInNewSpace(size / kPointerSize,
+ r0,
+ r1,
+ r2,
+ &slow_case,
+ TAG_OBJECT);
+
+ // Copy the JS array part.
+ for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
+ if ((i != JSArray::kElementsOffset) || (length_ == 0)) {
+ __ ldr(r1, FieldMemOperand(r3, i));
+ __ str(r1, FieldMemOperand(r0, i));
+ }
+ }
+
+ if (length_ > 0) {
+ // Get hold of the elements array of the boilerplate and setup the
+ // elements pointer in the resulting object.
+ __ ldr(r3, FieldMemOperand(r3, JSArray::kElementsOffset));
+ __ add(r2, r0, Operand(JSArray::kSize));
+ __ str(r2, FieldMemOperand(r0, JSArray::kElementsOffset));
+
+ // Copy the elements array.
+ for (int i = 0; i < elements_size; i += kPointerSize) {
+ __ ldr(r1, FieldMemOperand(r3, i));
+ __ str(r1, FieldMemOperand(r2, i));
+ }
+ }
+
+ // Return and remove the on-stack parameters.
+ __ add(sp, sp, Operand(3 * kPointerSize));
+ __ Ret();
+
+ __ bind(&slow_case);
+ ExternalReference runtime(Runtime::kCreateArrayLiteralShallow);
+ __ TailCallRuntime(runtime, 3, 1);
+}
+
+
// Count leading zeros in a 32 bit word. On ARM5 and later it uses the clz
// instruction. On pre-ARM5 hardware this routine gives the wrong answer for 0
// (31 instead of 32).
@@ -6584,7 +6695,7 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
__ b(gt, &slow);
// Get the prototype of the function (r4 is result, r2 is scratch).
- __ ldr(r1, MemOperand(sp, 0 * kPointerSize));
+ __ ldr(r1, MemOperand(sp, 0));
__ TryGetFunctionPrototype(r1, r4, r2, &slow);
// Check that the function prototype is a JS object.
@@ -6699,20 +6810,102 @@ void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
+ // sp[0] : number of parameters
+ // sp[4] : receiver displacement
+ // sp[8] : function
+
// Check if the calling frame is an arguments adaptor frame.
- Label runtime;
+ Label adaptor_frame, try_allocate, runtime;
__ ldr(r2, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
__ ldr(r3, MemOperand(r2, StandardFrameConstants::kContextOffset));
__ cmp(r3, Operand(Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR)));
- __ b(ne, &runtime);
+ __ b(eq, &adaptor_frame);
+
+ // Get the length from the frame.
+ __ ldr(r1, MemOperand(sp, 0));
+ __ b(&try_allocate);
// Patch the arguments.length and the parameters pointer.
- __ ldr(r0, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
- __ str(r0, MemOperand(sp, 0 * kPointerSize));
- __ add(r3, r2, Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize));
+ __ bind(&adaptor_frame);
+ __ ldr(r1, MemOperand(r2, ArgumentsAdaptorFrameConstants::kLengthOffset));
+ __ str(r1, MemOperand(sp, 0));
+ __ add(r3, r2, Operand(r1, LSL, kPointerSizeLog2 - kSmiTagSize));
__ add(r3, r3, Operand(StandardFrameConstants::kCallerSPOffset));
__ str(r3, MemOperand(sp, 1 * kPointerSize));
+ // Try the new space allocation. Start out with computing the size
+ // of the arguments object and the elements array (in words, not
+ // bytes because AllocateInNewSpace expects words).
+ Label add_arguments_object;
+ __ bind(&try_allocate);
+ __ cmp(r1, Operand(0));
+ __ b(eq, &add_arguments_object);
+ __ mov(r1, Operand(r1, LSR, kSmiTagSize));
+ __ add(r1, r1, Operand(FixedArray::kHeaderSize / kPointerSize));
+ __ bind(&add_arguments_object);
+ __ add(r1, r1, Operand(Heap::kArgumentsObjectSize / kPointerSize));
+
+ // Do the allocation of both objects in one go.
+ __ AllocateInNewSpace(r1, r0, r2, r3, &runtime, TAG_OBJECT);
+
+ // Get the arguments boilerplate from the current (global) context.
+ int offset = Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX);
+ __ ldr(r4, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ __ ldr(r4, FieldMemOperand(r4, GlobalObject::kGlobalContextOffset));
+ __ ldr(r4, MemOperand(r4, offset));
+
+ // Copy the JS object part.
+ for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
+ __ ldr(r3, FieldMemOperand(r4, i));
+ __ str(r3, FieldMemOperand(r0, i));
+ }
+
+ // Setup the callee in-object property.
+ ASSERT(Heap::arguments_callee_index == 0);
+ __ ldr(r3, MemOperand(sp, 2 * kPointerSize));
+ __ str(r3, FieldMemOperand(r0, JSObject::kHeaderSize));
+
+ // Get the length (smi tagged) and set that as an in-object property too.
+ ASSERT(Heap::arguments_length_index == 1);
+ __ ldr(r1, MemOperand(sp, 0 * kPointerSize));
+ __ str(r1, FieldMemOperand(r0, JSObject::kHeaderSize + kPointerSize));
+
+ // If there are no actual arguments, we're done.
+ Label done;
+ __ cmp(r1, Operand(0));
+ __ b(eq, &done);
+
+ // Get the parameters pointer from the stack and untag the length.
+ __ ldr(r2, MemOperand(sp, 1 * kPointerSize));
+ __ mov(r1, Operand(r1, LSR, kSmiTagSize));
+
+ // Setup the elements pointer in the allocated arguments object and
+ // initialize the header in the elements fixed array.
+ __ add(r4, r0, Operand(Heap::kArgumentsObjectSize));
+ __ str(r4, FieldMemOperand(r0, JSObject::kElementsOffset));
+ __ LoadRoot(r3, Heap::kFixedArrayMapRootIndex);
+ __ str(r3, FieldMemOperand(r4, FixedArray::kMapOffset));
+ __ str(r1, FieldMemOperand(r4, FixedArray::kLengthOffset));
+
+ // Copy the fixed array slots.
+ Label loop;
+ // Setup r4 to point to the first array slot.
+ __ add(r4, r4, Operand(FixedArray::kHeaderSize - kHeapObjectTag));
+ __ bind(&loop);
+ // Pre-decrement r2 with kPointerSize on each iteration.
+ // Pre-decrement in order to skip receiver.
+ __ ldr(r3, MemOperand(r2, kPointerSize, NegPreIndex));
+ // Post-increment r4 with kPointerSize on each iteration.
+ __ str(r3, MemOperand(r4, kPointerSize, PostIndex));
+ __ sub(r1, r1, Operand(1));
+ __ cmp(r1, Operand(0));
+ __ b(ne, &loop);
+
+ // Return and remove the on-stack parameters.
+ __ bind(&done);
+ __ add(sp, sp, Operand(3 * kPointerSize));
+ __ Ret();
+
// Do the runtime call to allocate the arguments object.
__ bind(&runtime);
__ TailCallRuntime(ExternalReference(Runtime::kNewArgumentsFast), 3, 1);
@@ -6766,6 +6959,9 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
// Slow-case: Non-function called.
__ bind(&slow);
+ // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
+ // of the original receiver from the call site).
+ __ str(r1, MemOperand(sp, argc_ * kPointerSize));
__ mov(r0, Operand(argc_)); // Setup the number of arguments.
__ mov(r2, Operand(0));
__ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h
index 2578a398..22dd854a 100644
--- a/src/arm/codegen-arm.h
+++ b/src/arm/codegen-arm.h
@@ -150,15 +150,6 @@ class CodeGenState BASE_EMBEDDED {
class CodeGenerator: public AstVisitor {
public:
- // Compilation mode. Either the compiler is used as the primary
- // compiler and needs to setup everything or the compiler is used as
- // the secondary compiler for split compilation and has to handle
- // bailouts.
- enum Mode {
- PRIMARY,
- SECONDARY
- };
-
// Takes a function literal, generates code for it. This function should only
// be called by compiler.cc.
static Handle<Code> MakeCode(CompilationInfo* info);
@@ -244,7 +235,7 @@ class CodeGenerator: public AstVisitor {
inline void VisitStatementsAndSpill(ZoneList<Statement*>* statements);
// Main code generation function
- void Generate(CompilationInfo* info, Mode mode);
+ void Generate(CompilationInfo* info);
// The following are used by class Reference.
void LoadReference(Reference* ref);
@@ -359,6 +350,7 @@ class CodeGenerator: public AstVisitor {
void GenerateIsSmi(ZoneList<Expression*>* args);
void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
void GenerateIsArray(ZoneList<Expression*>* args);
+ void GenerateIsRegExp(ZoneList<Expression*>* args);
void GenerateIsObject(ZoneList<Expression*>* args);
void GenerateIsFunction(ZoneList<Expression*>* args);
void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
@@ -398,6 +390,13 @@ class CodeGenerator: public AstVisitor {
// Support for direct calls from JavaScript to native RegExp code.
void GenerateRegExpExec(ZoneList<Expression*>* args);
+ // Fast support for number to string.
+ void GenerateNumberToString(ZoneList<Expression*>* args);
+
+ // Fast call to sine function.
+ void GenerateMathSin(ZoneList<Expression*>* args);
+ void GenerateMathCos(ZoneList<Expression*>* args);
+
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
diff --git a/src/arm/cpu-arm.cc b/src/arm/cpu-arm.cc
index 4e39cdaf..55f31d46 100644
--- a/src/arm/cpu-arm.cc
+++ b/src/arm/cpu-arm.cc
@@ -122,7 +122,7 @@ void CPU::FlushICache(void* start, size_t size) {
void CPU::DebugBreak() {
-#if !defined (__arm__)
+#if !defined (__arm__) || !defined(CAN_USE_ARMV5_INSTRUCTIONS)
UNIMPLEMENTED(); // when building ARM emulator target
#else
asm volatile("bkpt 0");
diff --git a/src/arm/debug-arm.cc b/src/arm/debug-arm.cc
index 6eb5239b..e6b61b4d 100644
--- a/src/arm/debug-arm.cc
+++ b/src/arm/debug-arm.cc
@@ -128,7 +128,7 @@ void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) {
// -- lr : return address
// -- [sp] : receiver
// -----------------------------------
- // Registers r0 and r2 contain objects that needs to be pushed on the
+ // Registers r0 and r2 contain objects that need to be pushed on the
// expression stack of the fake JS frame.
Generate_DebugBreakCallHelper(masm, r0.bit() | r2.bit());
}
@@ -137,14 +137,14 @@ void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) {
void Debug::GenerateStoreICDebugBreak(MacroAssembler* masm) {
// Calling convention for IC store (from ic-arm.cc).
// ----------- S t a t e -------------
- // -- r0 : receiver
+ // -- r0 : value
+ // -- r1 : receiver
// -- r2 : name
// -- lr : return address
- // -- [sp] : receiver
// -----------------------------------
- // Registers r0 and r2 contain objects that needs to be pushed on the
+ // Registers r0, r1, and r2 contain objects that need to be pushed on the
// expression stack of the fake JS frame.
- Generate_DebugBreakCallHelper(masm, r0.bit() | r2.bit());
+ Generate_DebugBreakCallHelper(masm, r0.bit() | r1.bit() | r2.bit());
}
diff --git a/src/arm/fast-codegen-arm.cc b/src/arm/fast-codegen-arm.cc
index 80da5336..aa7128fc 100644
--- a/src/arm/fast-codegen-arm.cc
+++ b/src/arm/fast-codegen-arm.cc
@@ -35,55 +35,86 @@ namespace internal {
#define __ ACCESS_MASM(masm())
-void FastCodeGenerator::EmitLoadReceiver(Register reg) {
+Register FastCodeGenerator::accumulator0() { return r0; }
+Register FastCodeGenerator::accumulator1() { return r1; }
+Register FastCodeGenerator::scratch0() { return r3; }
+Register FastCodeGenerator::scratch1() { return r4; }
+Register FastCodeGenerator::receiver_reg() { return r2; }
+Register FastCodeGenerator::context_reg() { return cp; }
+
+
+void FastCodeGenerator::EmitLoadReceiver() {
// Offset 2 is due to return address and saved frame pointer.
int index = 2 + scope()->num_parameters();
- __ ldr(reg, MemOperand(sp, index * kPointerSize));
+ __ ldr(receiver_reg(), MemOperand(sp, index * kPointerSize));
}
-void FastCodeGenerator::EmitReceiverMapCheck() {
- Comment cmnt(masm(), ";; MapCheck(this)");
- if (FLAG_print_ir) {
- PrintF("MapCheck(this)\n");
- }
+void FastCodeGenerator::EmitGlobalVariableLoad(Handle<Object> cell) {
+ ASSERT(!destination().is(no_reg));
+ ASSERT(cell->IsJSGlobalPropertyCell());
- ASSERT(info()->has_receiver() && info()->receiver()->IsHeapObject());
- Handle<HeapObject> object = Handle<HeapObject>::cast(info()->receiver());
- Handle<Map> map(object->map());
+ __ mov(destination(), Operand(cell));
+ __ ldr(destination(),
+ FieldMemOperand(destination(), JSGlobalPropertyCell::kValueOffset));
+ if (FLAG_debug_code) {
+ __ mov(ip, Operand(Factory::the_hole_value()));
+ __ cmp(destination(), ip);
+ __ Check(ne, "DontDelete cells can't contain the hole");
+ }
- EmitLoadReceiver(r1);
- __ CheckMap(r1, r3, map, bailout(), false);
+ // The loaded value is not known to be a smi.
+ clear_as_smi(destination());
}
-void FastCodeGenerator::EmitGlobalMapCheck() {
- Comment cmnt(masm(), ";; GlobalMapCheck");
- if (FLAG_print_ir) {
- PrintF(";; GlobalMapCheck()");
- }
+void FastCodeGenerator::EmitThisPropertyStore(Handle<String> name) {
+ LookupResult lookup;
+ info()->receiver()->Lookup(*name, &lookup);
- ASSERT(info()->has_global_object());
- Handle<Map> map(info()->global_object()->map());
+ ASSERT(lookup.holder() == *info()->receiver());
+ ASSERT(lookup.type() == FIELD);
+ Handle<Map> map(Handle<HeapObject>::cast(info()->receiver())->map());
+ int index = lookup.GetFieldIndex() - map->inobject_properties();
+ int offset = index * kPointerSize;
- __ ldr(r3, CodeGenerator::GlobalObject());
- __ CheckMap(r3, r3, map, bailout(), true);
-}
+ // We will emit the write barrier unless the stored value is statically
+ // known to be a smi.
+ bool needs_write_barrier = !is_smi(accumulator0());
+ // Negative offsets are inobject properties.
+ if (offset < 0) {
+ offset += map->instance_size();
+ __ str(accumulator0(), FieldMemOperand(receiver_reg(), offset));
+ if (needs_write_barrier) {
+ // Preserve receiver from write barrier.
+ __ mov(scratch0(), receiver_reg());
+ }
+ } else {
+ offset += FixedArray::kHeaderSize;
+ __ ldr(scratch0(),
+ FieldMemOperand(receiver_reg(), JSObject::kPropertiesOffset));
+ __ str(accumulator0(), FieldMemOperand(scratch0(), offset));
+ }
-void FastCodeGenerator::EmitGlobalVariableLoad(Handle<Object> cell) {
- ASSERT(cell->IsJSGlobalPropertyCell());
- __ mov(r0, Operand(cell));
- __ ldr(r0, FieldMemOperand(r0, JSGlobalPropertyCell::kValueOffset));
- if (FLAG_debug_code) {
- __ mov(ip, Operand(Factory::the_hole_value()));
- __ cmp(r0, ip);
- __ Check(ne, "DontDelete cells can't contain the hole");
+ if (needs_write_barrier) {
+ __ mov(scratch1(), Operand(offset));
+ __ RecordWrite(scratch0(), scratch1(), ip);
+ }
+
+ if (destination().is(accumulator1())) {
+ __ mov(accumulator1(), accumulator0());
+ if (is_smi(accumulator0())) {
+ set_as_smi(accumulator1());
+ } else {
+ clear_as_smi(accumulator1());
+ }
}
}
-void FastCodeGenerator::EmitThisPropertyStore(Handle<String> name) {
+void FastCodeGenerator::EmitThisPropertyLoad(Handle<String> name) {
+ ASSERT(!destination().is(no_reg));
LookupResult lookup;
info()->receiver()->Lookup(*name, &lookup);
@@ -93,18 +124,55 @@ void FastCodeGenerator::EmitThisPropertyStore(Handle<String> name) {
int index = lookup.GetFieldIndex() - map->inobject_properties();
int offset = index * kPointerSize;
- // Negative offsets are inobject properties.
+ // Perform the load. Negative offsets are inobject properties.
if (offset < 0) {
offset += map->instance_size();
- __ mov(r2, r1); // Copy receiver for write barrier.
+ __ ldr(destination(), FieldMemOperand(receiver_reg(), offset));
} else {
offset += FixedArray::kHeaderSize;
- __ ldr(r2, FieldMemOperand(r1, JSObject::kPropertiesOffset));
+ __ ldr(scratch0(),
+ FieldMemOperand(receiver_reg(), JSObject::kPropertiesOffset));
+ __ ldr(destination(), FieldMemOperand(scratch0(), offset));
+ }
+
+ // The loaded value is not known to be a smi.
+ clear_as_smi(destination());
+}
+
+
+void FastCodeGenerator::EmitBitOr() {
+ if (is_smi(accumulator0()) && is_smi(accumulator1())) {
+ // If both operands are known to be a smi then there is no need to check
+ // the operands or result. There is no need to perform the operation in
+ // an effect context.
+ if (!destination().is(no_reg)) {
+ __ orr(destination(), accumulator1(), Operand(accumulator0()));
+ }
+ } else {
+ // Left is in accumulator1, right in accumulator0.
+ if (destination().is(accumulator0())) {
+ __ mov(scratch0(), accumulator0());
+ __ orr(destination(), accumulator1(), Operand(accumulator1()));
+ Label* bailout =
+ info()->AddBailout(accumulator1(), scratch0()); // Left, right.
+ __ BranchOnNotSmi(destination(), bailout);
+ } else if (destination().is(accumulator1())) {
+ __ mov(scratch0(), accumulator1());
+ __ orr(destination(), accumulator1(), Operand(accumulator0()));
+ Label* bailout = info()->AddBailout(scratch0(), accumulator0());
+ __ BranchOnNotSmi(destination(), bailout);
+ } else {
+ ASSERT(destination().is(no_reg));
+ __ orr(scratch0(), accumulator1(), Operand(accumulator0()));
+ Label* bailout = info()->AddBailout(accumulator1(), accumulator0());
+ __ BranchOnNotSmi(scratch0(), bailout);
+ }
}
- // Perform the store.
- __ str(r0, FieldMemOperand(r2, offset));
- __ mov(r3, Operand(offset));
- __ RecordWrite(r2, r3, ip);
+
+ // If we didn't bailout, the result (in fact, both inputs too) is known to
+ // be a smi.
+ set_as_smi(accumulator0());
+ set_as_smi(accumulator1());
}
@@ -119,26 +187,45 @@ void FastCodeGenerator::Generate(CompilationInfo* compilation_info) {
// Note that we keep a live register reference to cp (context) at
// this point.
- // Receiver (this) is allocated to r1 if there are this properties.
- if (info()->has_this_properties()) EmitReceiverMapCheck();
+ Label* bailout_to_beginning = info()->AddBailout();
+ // Receiver (this) is allocated to a fixed register.
+ if (info()->has_this_properties()) {
+ Comment cmnt(masm(), ";; MapCheck(this)");
+ if (FLAG_print_ir) {
+ PrintF("MapCheck(this)\n");
+ }
+ ASSERT(info()->has_receiver() && info()->receiver()->IsHeapObject());
+ Handle<HeapObject> object = Handle<HeapObject>::cast(info()->receiver());
+ Handle<Map> map(object->map());
+ EmitLoadReceiver();
+ __ CheckMap(receiver_reg(), scratch0(), map, bailout_to_beginning, false);
+ }
- // If there is a global variable access check if the global object
- // is the same as at lazy-compilation time.
- if (info()->has_globals()) EmitGlobalMapCheck();
+ // If there is a global variable access check if the global object is the
+ // same as at lazy-compilation time.
+ if (info()->has_globals()) {
+ Comment cmnt(masm(), ";; MapCheck(GLOBAL)");
+ if (FLAG_print_ir) {
+ PrintF("MapCheck(GLOBAL)\n");
+ }
+ ASSERT(info()->has_global_object());
+ Handle<Map> map(info()->global_object()->map());
+ __ ldr(scratch0(), CodeGenerator::GlobalObject());
+ __ CheckMap(scratch0(), scratch1(), map, bailout_to_beginning, true);
+ }
VisitStatements(function()->body());
Comment return_cmnt(masm(), ";; Return(<undefined>)");
+ if (FLAG_print_ir) {
+ PrintF("Return(<undefined>)\n");
+ }
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex);
-
- Comment epilogue_cmnt(masm(), ";; Epilogue");
__ mov(sp, fp);
__ ldm(ia_w, sp, fp.bit() | lr.bit());
int32_t sp_delta = (scope()->num_parameters() + 1) * kPointerSize;
__ add(sp, sp, Operand(sp_delta));
__ Jump(lr);
-
- __ bind(&bailout_);
}
diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc
index 7e048fff..48963738 100644
--- a/src/arm/full-codegen-arm.cc
+++ b/src/arm/full-codegen-arm.cc
@@ -816,9 +816,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
if (key->handle()->IsSymbol()) {
VisitForValue(value, kAccumulator);
__ mov(r2, Operand(key->handle()));
+ __ ldr(r1, MemOperand(sp));
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET);
- // StoreIC leaves the receiver on the stack.
break;
}
// Fall through.
@@ -907,6 +907,92 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
}
+void FullCodeGenerator::VisitAssignment(Assignment* expr) {
+ Comment cmnt(masm_, "[ Assignment");
+ ASSERT(expr->op() != Token::INIT_CONST);
+ // Left-hand side can only be a property, a global or a (parameter or local)
+ // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* prop = expr->target()->AsProperty();
+ if (prop != NULL) {
+ assign_type =
+ (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
+ }
+
+ // Evaluate LHS expression.
+ switch (assign_type) {
+ case VARIABLE:
+ // Nothing to do here.
+ break;
+ case NAMED_PROPERTY:
+ if (expr->is_compound()) {
+ // We need the receiver both on the stack and in the accumulator.
+ VisitForValue(prop->obj(), kAccumulator);
+ __ push(result_register());
+ } else {
+ VisitForValue(prop->obj(), kStack);
+ }
+ break;
+ case KEYED_PROPERTY:
+ VisitForValue(prop->obj(), kStack);
+ VisitForValue(prop->key(), kStack);
+ break;
+ }
+
+ // If we have a compound assignment: Get value of LHS expression and
+ // store in on top of the stack.
+ if (expr->is_compound()) {
+ Location saved_location = location_;
+ location_ = kStack;
+ switch (assign_type) {
+ case VARIABLE:
+ EmitVariableLoad(expr->target()->AsVariableProxy()->var(),
+ Expression::kValue);
+ break;
+ case NAMED_PROPERTY:
+ EmitNamedPropertyLoad(prop);
+ __ push(result_register());
+ break;
+ case KEYED_PROPERTY:
+ EmitKeyedPropertyLoad(prop);
+ __ push(result_register());
+ break;
+ }
+ location_ = saved_location;
+ }
+
+ // Evaluate RHS expression.
+ Expression* rhs = expr->value();
+ VisitForValue(rhs, kAccumulator);
+
+ // If we have a compound assignment: Apply operator.
+ if (expr->is_compound()) {
+ Location saved_location = location_;
+ location_ = kAccumulator;
+ EmitBinaryOp(expr->binary_op(), Expression::kValue);
+ location_ = saved_location;
+ }
+
+ // Record source position before possible IC call.
+ SetSourcePosition(expr->position());
+
+ // Store the value.
+ switch (assign_type) {
+ case VARIABLE:
+ EmitVariableAssignment(expr->target()->AsVariableProxy()->var(),
+ context_);
+ break;
+ case NAMED_PROPERTY:
+ EmitNamedPropertyAssignment(expr);
+ break;
+ case KEYED_PROPERTY:
+ EmitKeyedPropertyAssignment(expr);
+ break;
+ }
+}
+
+
void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
@@ -945,21 +1031,17 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
ASSERT(!var->is_this());
// Assignment to a global variable. Use inline caching for the
// assignment. Right-hand-side value is passed in r0, variable name in
- // r2, and the global object on the stack.
+ // r2, and the global object in r1.
__ mov(r2, Operand(var->name()));
- __ ldr(ip, CodeGenerator::GlobalObject());
- __ push(ip);
+ __ ldr(r1, CodeGenerator::GlobalObject());
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET);
- // Overwrite the global object on the stack with the result if needed.
- DropAndApply(1, context, r0);
} else if (slot != NULL && slot->type() == Slot::LOOKUP) {
__ push(result_register()); // Value.
__ mov(r1, Operand(var->name()));
__ stm(db_w, sp, cp.bit() | r1.bit()); // Context and name.
__ CallRuntime(Runtime::kStoreContextSlot, 3);
- Apply(context, r0);
} else if (var->slot() != NULL) {
Slot* slot = var->slot();
@@ -986,13 +1068,13 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var,
UNREACHABLE();
break;
}
- Apply(context, result_register());
} else {
// Variables rewritten as properties are not treated as variables in
// assignments.
UNREACHABLE();
}
+ Apply(context, result_register());
}
@@ -1016,6 +1098,12 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) {
// Record source code position before IC call.
SetSourcePosition(expr->position());
__ mov(r2, Operand(prop->key()->AsLiteral()->handle()));
+ if (expr->ends_initialization_block()) {
+ __ ldr(r1, MemOperand(sp));
+ } else {
+ __ pop(r1);
+ }
+
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET);
@@ -1026,9 +1114,10 @@ void FullCodeGenerator::EmitNamedPropertyAssignment(Assignment* expr) {
__ push(ip);
__ CallRuntime(Runtime::kToFastProperties, 1);
__ pop(r0);
+ DropAndApply(1, context_, r0);
+ } else {
+ Apply(context_, r0);
}
-
- DropAndApply(1, context_, r0);
}
@@ -1087,7 +1176,7 @@ void FullCodeGenerator::VisitProperty(Property* expr) {
}
void FullCodeGenerator::EmitCallWithIC(Call* expr,
- Handle<Object> ignored,
+ Handle<Object> name,
RelocInfo::Mode mode) {
// Code common for calls using the IC.
ZoneList<Expression*>* args = expr->arguments();
@@ -1095,16 +1184,16 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
for (int i = 0; i < arg_count; i++) {
VisitForValue(args->at(i), kStack);
}
+ __ mov(r2, Operand(name));
// Record source position for debugger.
SetSourcePosition(expr->position());
// Call the IC initialization code.
- Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count,
- NOT_IN_LOOP);
+ InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
+ Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop);
__ Call(ic, mode);
// Restore context register.
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
- // Discard the function left on TOS.
- DropAndApply(1, context_, r0);
+ Apply(context_, r0);
}
@@ -1121,7 +1210,6 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr) {
__ CallStub(&stub);
// Restore context register.
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
- // Discard the function left on TOS.
DropAndApply(1, context_, r0);
}
@@ -1135,11 +1223,9 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// Call to the identifier 'eval'.
UNREACHABLE();
} else if (var != NULL && !var->is_this() && var->is_global()) {
- // Call to a global variable.
- __ mov(r1, Operand(var->name()));
- // Push global object as receiver for the call IC lookup.
+ // Push global object as receiver for the call IC.
__ ldr(r0, CodeGenerator::GlobalObject());
- __ stm(db_w, sp, r1.bit() | r0.bit());
+ __ push(r0);
EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT);
} else if (var != NULL && var->slot() != NULL &&
var->slot()->type() == Slot::LOOKUP) {
@@ -1151,8 +1237,6 @@ void FullCodeGenerator::VisitCall(Call* expr) {
Literal* key = prop->key()->AsLiteral();
if (key != NULL && key->handle()->IsSymbol()) {
// Call to a named property, use call IC.
- __ mov(r0, Operand(key->handle()));
- __ push(r0);
VisitForValue(prop->obj(), kStack);
EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET);
} else {
@@ -1238,10 +1322,9 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
if (expr->is_jsruntime()) {
// Prepare for calling JS runtime function.
- __ mov(r1, Operand(expr->name()));
__ ldr(r0, CodeGenerator::GlobalObject());
__ ldr(r0, FieldMemOperand(r0, GlobalObject::kBuiltinsOffset));
- __ stm(db_w, sp, r1.bit() | r0.bit());
+ __ push(r0);
}
// Push the arguments ("left-to-right").
@@ -1252,18 +1335,17 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
if (expr->is_jsruntime()) {
// Call the JS runtime function.
+ __ mov(r2, Operand(expr->name()));
Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count,
NOT_IN_LOOP);
__ Call(ic, RelocInfo::CODE_TARGET);
// Restore context register.
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
- // Discard the function left on TOS.
- DropAndApply(1, context_, r0);
} else {
// Call the C runtime function.
__ CallRuntime(expr->function(), arg_count);
- Apply(context_, r0);
}
+ Apply(context_, r0);
}
@@ -1548,15 +1630,15 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
break;
case NAMED_PROPERTY: {
__ mov(r2, Operand(prop->key()->AsLiteral()->handle()));
+ __ pop(r1);
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
__ Call(ic, RelocInfo::CODE_TARGET);
if (expr->is_postfix()) {
- __ Drop(1); // Result is on the stack under the receiver.
if (context_ != Expression::kEffect) {
ApplyTOS(context_);
}
} else {
- DropAndApply(1, context_, r0);
+ Apply(context_, r0);
}
break;
}
diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc
index 19583a98..2a1fef9b 100644
--- a/src/arm/ic-arm.cc
+++ b/src/arm/ic-arm.cc
@@ -59,7 +59,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
// r3 - used as temporary and to hold the capacity of the property
// dictionary.
//
- // r2 - holds the name of the property and is unchanges.
+ // r2 - holds the name of the property and is unchanged.
Label done;
@@ -171,7 +171,7 @@ void LoadIC::GenerateStringLength(MacroAssembler* masm) {
__ ldr(r0, MemOperand(sp, 0));
- StubCompiler::GenerateLoadStringLength2(masm, r0, r1, r3, &miss);
+ StubCompiler::GenerateLoadStringLength(masm, r0, r1, r3, &miss);
// Cache miss: Jump to runtime.
__ bind(&miss);
StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
@@ -200,14 +200,13 @@ Object* CallIC_Miss(Arguments args);
void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
// ----------- S t a t e -------------
- // -- lr: return address
+ // -- r2 : name
+ // -- lr : return address
// -----------------------------------
Label number, non_number, non_string, boolean, probe, miss;
// Get the receiver of the function from the stack into r1.
__ ldr(r1, MemOperand(sp, argc * kPointerSize));
- // Get the name of the function from the stack; 1 ~ receiver.
- __ ldr(r2, MemOperand(sp, (argc + 1) * kPointerSize));
// Probe the stub cache.
Code::Flags flags =
@@ -276,9 +275,9 @@ static void GenerateNormalHelper(MacroAssembler* masm,
// Patch the receiver with the global proxy if necessary.
if (is_global_object) {
- __ ldr(r2, MemOperand(sp, argc * kPointerSize));
- __ ldr(r2, FieldMemOperand(r2, GlobalObject::kGlobalReceiverOffset));
- __ str(r2, MemOperand(sp, argc * kPointerSize));
+ __ ldr(r0, MemOperand(sp, argc * kPointerSize));
+ __ ldr(r0, FieldMemOperand(r0, GlobalObject::kGlobalReceiverOffset));
+ __ str(r0, MemOperand(sp, argc * kPointerSize));
}
// Invoke the function.
@@ -289,14 +288,13 @@ static void GenerateNormalHelper(MacroAssembler* masm,
void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
// ----------- S t a t e -------------
- // -- lr: return address
+ // -- r2 : name
+ // -- lr : return address
// -----------------------------------
Label miss, global_object, non_global_object;
// Get the receiver of the function from the stack into r1.
__ ldr(r1, MemOperand(sp, argc * kPointerSize));
- // Get the name of the function from the stack; 1 ~ receiver.
- __ ldr(r2, MemOperand(sp, (argc + 1) * kPointerSize));
// Check that the receiver isn't a smi.
__ tst(r1, Operand(kSmiTagMask));
@@ -349,18 +347,17 @@ void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
// ----------- S t a t e -------------
- // -- lr: return address
+ // -- r2 : name
+ // -- lr : return address
// -----------------------------------
// Get the receiver of the function from the stack.
- __ ldr(r2, MemOperand(sp, argc * kPointerSize));
- // Get the name of the function to call from the stack.
- __ ldr(r1, MemOperand(sp, (argc + 1) * kPointerSize));
+ __ ldr(r3, MemOperand(sp, argc * kPointerSize));
__ EnterInternalFrame();
// Push the receiver and the name of the function.
- __ stm(db_w, sp, r1.bit() | r2.bit());
+ __ stm(db_w, sp, r2.bit() | r3.bit());
// Call the entry.
__ mov(r0, Operand(2));
@@ -413,7 +410,7 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
StubCache::GenerateProbe(masm, flags, r0, r2, r3, no_reg);
// Cache miss: Jump to runtime.
- Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss)));
+ GenerateMiss(masm);
}
@@ -456,16 +453,11 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
// Cache miss: Restore receiver from stack and jump to runtime.
__ bind(&miss);
- Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss)));
+ GenerateMiss(masm);
}
void LoadIC::GenerateMiss(MacroAssembler* masm) {
- Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss)));
-}
-
-
-void LoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) {
// ----------- S t a t e -------------
// -- r2 : name
// -- lr : return address
@@ -476,7 +468,7 @@ void LoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) {
__ stm(db_w, sp, r2.bit() | r3.bit());
// Perform tail call to the entry.
- __ TailCallRuntime(f, 2, 1);
+ __ TailCallRuntime(ExternalReference(IC_Utility(kLoadIC_Miss)), 2, 1);
}
@@ -504,11 +496,20 @@ Object* KeyedLoadIC_Miss(Arguments args);
void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
- Generate(masm, ExternalReference(IC_Utility(kKeyedLoadIC_Miss)));
+ // ---------- S t a t e --------------
+ // -- lr : return address
+ // -- sp[0] : key
+ // -- sp[4] : receiver
+ // -----------------------------------
+
+ __ ldm(ia, sp, r2.bit() | r3.bit());
+ __ stm(db_w, sp, r2.bit() | r3.bit());
+
+ __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedLoadIC_Miss)), 2, 1);
}
-void KeyedLoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) {
+void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
// ---------- S t a t e --------------
// -- lr : return address
// -- sp[0] : key
@@ -518,7 +519,7 @@ void KeyedLoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) {
__ ldm(ia, sp, r2.bit() | r3.bit());
__ stm(db_w, sp, r2.bit() | r3.bit());
- __ TailCallRuntime(f, 2, 1);
+ __ TailCallRuntime(ExternalReference(Runtime::kGetProperty), 2, 1);
}
@@ -532,17 +533,11 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
// 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 isn't a smi.
+ __ BranchOnSmi(r1, &slow);
// Get the map of the receiver.
__ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
-
// Check bit field.
__ ldrb(r3, FieldMemOperand(r2, Map::kBitFieldOffset));
__ tst(r3, Operand(kSlowCaseBitFieldMask));
@@ -556,6 +551,10 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
__ cmp(r2, Operand(JS_OBJECT_TYPE));
__ b(lt, &slow);
+ // Check that the key is a smi.
+ __ BranchOnNotSmi(r0, &slow);
+ __ mov(r0, Operand(r0, ASR, kSmiTagSize));
+
// Get the elements array of the object.
__ ldr(r1, FieldMemOperand(r1, JSObject::kElementsOffset));
// Check that the object is in fast mode (not dictionary).
@@ -571,10 +570,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
// Slow case: Push extra copies of the arguments (2).
__ bind(&slow);
__ IncrementCounter(&Counters::keyed_load_generic_slow, 1, r0, r1);
- __ 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, 1);
+ GenerateRuntimeGetProperty(masm);
// Fast case: Do the load.
__ bind(&fast);
@@ -608,8 +604,47 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm,
}
-void KeyedStoreIC::Generate(MacroAssembler* masm,
- const ExternalReference& f) {
+void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- lr : return address
+ // -- sp[0] : key
+ // -- sp[4] : receiver
+ // -----------------------------------
+ Label slow;
+
+ // Get the key and receiver object from the stack.
+ __ ldm(ia, sp, r0.bit() | r1.bit());
+
+ // Check that the receiver isn't a smi.
+ __ BranchOnSmi(r1, &slow);
+
+ // Check that the key is a smi.
+ __ BranchOnNotSmi(r0, &slow);
+
+ // Get the map of the receiver.
+ __ ldr(r2, FieldMemOperand(r1, HeapObject::kMapOffset));
+
+ // Check that it has indexed interceptor and access checks
+ // are not enabled for this object.
+ __ ldrb(r3, FieldMemOperand(r2, Map::kBitFieldOffset));
+ __ and_(r3, r3, Operand(kSlowCaseBitFieldMask));
+ __ cmp(r3, Operand(1 << Map::kHasIndexedInterceptor));
+ __ b(ne, &slow);
+
+ // Everything is fine, call runtime.
+ __ push(r1); // receiver
+ __ push(r0); // key
+
+ // Perform tail call to the entry.
+ __ TailCallRuntime(ExternalReference(
+ IC_Utility(kKeyedLoadPropertyWithInterceptor)), 2, 1);
+
+ __ bind(&slow);
+ GenerateMiss(masm);
+}
+
+
+void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
// ---------- S t a t e --------------
// -- r0 : value
// -- lr : return address
@@ -620,7 +655,21 @@ void KeyedStoreIC::Generate(MacroAssembler* masm,
__ ldm(ia, sp, r2.bit() | r3.bit());
__ stm(db_w, sp, r0.bit() | r2.bit() | r3.bit());
- __ TailCallRuntime(f, 3, 1);
+ __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedStoreIC_Miss)), 3, 1);
+}
+
+
+void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) {
+ // ---------- S t a t e --------------
+ // -- r0 : value
+ // -- lr : return address
+ // -- sp[0] : key
+ // -- sp[1] : receiver
+ // -----------------------------------
+ __ ldm(ia, sp, r1.bit() | r3.bit()); // r0 == value, r1 == key, r3 == object
+ __ stm(db_w, sp, r0.bit() | r1.bit() | r3.bit());
+
+ __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3, 1);
}
@@ -675,12 +724,9 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
__ b(lo, &fast);
- // Slow case: Push extra copies of the arguments (3).
+ // Slow case:
__ 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, 1);
+ GenerateRuntimeSetProperty(masm);
// Extra capacity case: Check if there is extra capacity to
// perform the store and update the length. Used for adding one
@@ -751,33 +797,15 @@ void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm,
}
-void KeyedStoreIC::GenerateExtendStorage(MacroAssembler* masm) {
- // ---------- S t a t e --------------
- // -- r0 : value
- // -- lr : return address
- // -- sp[0] : key
- // -- sp[1] : receiver
- // ----------- S t a t e -------------
-
- __ ldm(ia, sp, r2.bit() | r3.bit());
- __ stm(db_w, sp, r0.bit() | r2.bit() | r3.bit());
-
- // Perform tail call to the entry.
- __ TailCallRuntime(
- ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3, 1);
-}
-
-
void StoreIC::GenerateMegamorphic(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- r0 : value
+ // -- r1 : receiver
// -- r2 : name
// -- lr : return address
- // -- [sp] : receiver
// -----------------------------------
// Get the receiver from the stack and probe the stub cache.
- __ ldr(r1, MemOperand(sp));
Code::Flags flags = Code::ComputeFlags(Code::STORE_IC,
NOT_IN_LOOP,
MONOMORPHIC);
@@ -788,33 +816,16 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm) {
}
-void StoreIC::GenerateExtendStorage(MacroAssembler* masm) {
- // ----------- S t a t e -------------
- // -- r0 : value
- // -- r2 : name
- // -- lr : return address
- // -- [sp] : receiver
- // -----------------------------------
-
- __ ldr(r3, MemOperand(sp)); // copy receiver
- __ stm(db_w, sp, r0.bit() | r2.bit() | r3.bit());
-
- // Perform tail call to the entry.
- __ TailCallRuntime(
- ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3, 1);
-}
-
-
void StoreIC::GenerateMiss(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- r0 : value
+ // -- r1 : receiver
// -- r2 : name
// -- lr : return address
- // -- [sp] : receiver
// -----------------------------------
- __ ldr(r3, MemOperand(sp)); // copy receiver
- __ stm(db_w, sp, r0.bit() | r2.bit() | r3.bit());
+ __ push(r1);
+ __ stm(db_w, sp, r2.bit() | r0.bit());
// Perform tail call to the entry.
__ TailCallRuntime(ExternalReference(IC_Utility(kStoreIC_Miss)), 3, 1);
diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc
index 1f08c7cf..b9335f8e 100644
--- a/src/arm/macro-assembler-arm.cc
+++ b/src/arm/macro-assembler-arm.cc
@@ -37,7 +37,6 @@ namespace internal {
MacroAssembler::MacroAssembler(void* buffer, int size)
: Assembler(buffer, size),
- unresolved_(0),
generating_stub_(false),
allow_stub_calls_(true),
code_object_(Heap::undefined_value()) {
@@ -221,7 +220,7 @@ void MacroAssembler::RecordWrite(Register object, Register offset,
// remembered set bits in the new space.
// object: heap object pointer (with tag)
// offset: offset to store location from the object
- and_(scratch, object, Operand(Heap::NewSpaceMask()));
+ and_(scratch, object, Operand(ExternalReference::new_space_mask()));
cmp(scratch, Operand(ExternalReference::new_space_start()));
b(eq, &done);
@@ -331,14 +330,10 @@ void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode) {
// Push in reverse order: caller_fp, sp_on_exit, and caller_pc.
stm(db_w, sp, fp.bit() | ip.bit() | lr.bit());
- mov(fp, Operand(sp)); // setup new frame pointer
+ mov(fp, Operand(sp)); // Setup new frame pointer.
- if (mode == ExitFrame::MODE_DEBUG) {
- mov(ip, Operand(Smi::FromInt(0)));
- } else {
- mov(ip, Operand(CodeObject()));
- }
- push(ip);
+ mov(ip, Operand(CodeObject()));
+ push(ip); // Accessed from ExitFrame::code_slot.
// Save the frame pointer and the context in top.
mov(ip, Operand(ExternalReference(Top::k_c_entry_fp_address)));
@@ -550,6 +545,21 @@ void MacroAssembler::InvokeFunction(Register fun,
}
+void MacroAssembler::InvokeFunction(JSFunction* function,
+ const ParameterCount& actual,
+ InvokeFlag flag) {
+ ASSERT(function->is_compiled());
+
+ // Get the function and setup the context.
+ mov(r1, Operand(Handle<JSFunction>(function)));
+ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
+
+ // Invoke the cached code.
+ Handle<Code> code(function->code());
+ ParameterCount expected(function->shared()->formal_parameter_count());
+ InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET, flag);
+}
+
#ifdef ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::SaveRegistersToMemory(RegList regs) {
ASSERT((regs & ~kJSCallerSaved) == 0);
@@ -608,6 +618,15 @@ void MacroAssembler::CopyRegistersFromStackToMemory(Register base,
}
}
}
+
+
+void MacroAssembler::DebugBreak() {
+ ASSERT(allow_stub_calls());
+ mov(r0, Operand(0));
+ mov(r1, Operand(ExternalReference(Runtime::kDebugBreak)));
+ CEntryStub ces(1);
+ Call(ces.GetCode(), RelocInfo::DEBUG_BREAK);
+}
#endif
@@ -1205,6 +1224,16 @@ void MacroAssembler::CallRuntime(Runtime::FunctionId fid, int num_arguments) {
}
+void MacroAssembler::CallExternalReference(const ExternalReference& ext,
+ int num_arguments) {
+ mov(r0, Operand(num_arguments));
+ mov(r1, Operand(ext));
+
+ CEntryStub stub(1);
+ CallStub(&stub);
+}
+
+
void MacroAssembler::TailCallRuntime(const ExternalReference& ext,
int num_arguments,
int result_size) {
@@ -1228,58 +1257,28 @@ void MacroAssembler::JumpToRuntime(const ExternalReference& builtin) {
}
-Handle<Code> MacroAssembler::ResolveBuiltin(Builtins::JavaScript id,
- bool* resolved) {
- // Contract with compiled functions is that the function is passed in r1.
- int builtins_offset =
- JSBuiltinsObject::kJSBuiltinsOffset + (id * kPointerSize);
- ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
- ldr(r1, FieldMemOperand(r1, GlobalObject::kBuiltinsOffset));
- ldr(r1, FieldMemOperand(r1, builtins_offset));
-
- return Builtins::GetCode(id, resolved);
-}
-
-
void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id,
InvokeJSFlags flags) {
- bool resolved;
- Handle<Code> code = ResolveBuiltin(id, &resolved);
-
+ GetBuiltinEntry(r2, id);
if (flags == CALL_JS) {
- Call(code, RelocInfo::CODE_TARGET);
+ Call(r2);
} else {
ASSERT(flags == JUMP_JS);
- Jump(code, RelocInfo::CODE_TARGET);
- }
-
- if (!resolved) {
- const char* name = Builtins::GetName(id);
- int argc = Builtins::GetArgumentsCount(id);
- uint32_t flags =
- Bootstrapper::FixupFlagsArgumentsCount::encode(argc) |
- Bootstrapper::FixupFlagsUseCodeObject::encode(false);
- Unresolved entry = { pc_offset() - kInstrSize, flags, name };
- unresolved_.Add(entry);
+ Jump(r2);
}
}
void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) {
- bool resolved;
- Handle<Code> code = ResolveBuiltin(id, &resolved);
-
- mov(target, Operand(code));
- if (!resolved) {
- const char* name = Builtins::GetName(id);
- int argc = Builtins::GetArgumentsCount(id);
- uint32_t flags =
- Bootstrapper::FixupFlagsArgumentsCount::encode(argc) |
- Bootstrapper::FixupFlagsUseCodeObject::encode(true);
- Unresolved entry = { pc_offset() - kInstrSize, flags, name };
- unresolved_.Add(entry);
- }
-
+ // Load the JavaScript builtin function from the builtins object.
+ ldr(r1, MemOperand(cp, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ ldr(r1, FieldMemOperand(r1, GlobalObject::kBuiltinsOffset));
+ int builtins_offset =
+ JSBuiltinsObject::kJSBuiltinsOffset + (id * kPointerSize);
+ ldr(r1, FieldMemOperand(r1, builtins_offset));
+ // Load the code entry point from the function into the target register.
+ ldr(target, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
+ ldr(target, FieldMemOperand(target, SharedFunctionInfo::kCodeOffset));
add(target, target, Operand(Code::kHeaderSize - kHeapObjectTag));
}
diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h
index 66ef4f9a..98cea163 100644
--- a/src/arm/macro-assembler-arm.h
+++ b/src/arm/macro-assembler-arm.h
@@ -135,6 +135,10 @@ class MacroAssembler: public Assembler {
const ParameterCount& actual,
InvokeFlag flag);
+ void InvokeFunction(JSFunction* function,
+ const ParameterCount& actual,
+ InvokeFlag flag);
+
#ifdef ENABLE_DEBUGGER_SUPPORT
// ---------------------------------------------------------------------------
@@ -146,6 +150,7 @@ class MacroAssembler: public Assembler {
void CopyRegistersFromStackToMemory(Register base,
Register scratch,
RegList regs);
+ void DebugBreak();
#endif
// ---------------------------------------------------------------------------
@@ -334,6 +339,10 @@ class MacroAssembler: public Assembler {
// Convenience function: Same as above, but takes the fid instead.
void CallRuntime(Runtime::FunctionId fid, int num_arguments);
+ // Convenience function: call an external reference.
+ void CallExternalReference(const ExternalReference& ext,
+ int num_arguments);
+
// Tail call of a runtime routine (jump).
// Like JumpToRuntime, but also takes care of passing the number
// of parameters.
@@ -352,13 +361,6 @@ class MacroAssembler: public Assembler {
// setup the function in r1.
void GetBuiltinEntry(Register target, Builtins::JavaScript id);
- struct Unresolved {
- int pc;
- uint32_t flags; // see Bootstrapper::FixupFlags decoders/encoders.
- const char* name;
- };
- List<Unresolved>* unresolved() { return &unresolved_; }
-
Handle<Object> CodeObject() { return code_object_; }
@@ -431,23 +433,10 @@ class MacroAssembler: public Assembler {
Label* done,
InvokeFlag flag);
- // Prepares for a call or jump to a builtin by doing two things:
- // 1. Emits code that fetches the builtin's function object from the context
- // at runtime, and puts it in the register rdi.
- // 2. Fetches the builtin's code object, and returns it in a handle, at
- // compile time, so that later code can emit instructions to jump or call
- // the builtin directly. If the code object has not yet been created, it
- // returns the builtin code object for IllegalFunction, and sets the
- // output parameter "resolved" to false. Code that uses the return value
- // should then add the address and the builtin name to the list of fixups
- // called unresolved_, which is fixed up by the bootstrapper.
- Handle<Code> ResolveBuiltin(Builtins::JavaScript id, bool* resolved);
-
// Activation support.
void EnterFrame(StackFrame::Type type);
void LeaveFrame(StackFrame::Type type);
- List<Unresolved> unresolved_;
bool generating_stub_;
bool allow_stub_calls_;
// This handle will be patched with the code object on installation.
diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc
index d19a683d..da739421 100644
--- a/src/arm/stub-cache-arm.cc
+++ b/src/arm/stub-cache-arm.cc
@@ -189,8 +189,9 @@ void StubCompiler::GenerateLoadArrayLength(MacroAssembler* masm,
}
-// Generate code to check if an object is a string. If the object is
-// a string, the map's instance type is left in the scratch1 register.
+// Generate code to check if an object is a string. If the object is a
+// heap object, its map's instance type is left in the scratch1 register.
+// If this is not needed, scratch1 and scratch2 may be the same register.
static void GenerateStringCheck(MacroAssembler* masm,
Register receiver,
Register scratch1,
@@ -215,18 +216,16 @@ static void GenerateStringCheck(MacroAssembler* masm,
// If the receiver object is not a string or a wrapped string object the
// execution continues at the miss label. The register containing the
// receiver is potentially clobbered.
-void StubCompiler::GenerateLoadStringLength2(MacroAssembler* masm,
- Register receiver,
- Register scratch1,
- Register scratch2,
- Label* miss) {
- Label check_string, check_wrapper;
-
- __ bind(&check_string);
+void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Label* miss) {
+ Label check_wrapper;
+
// Check if the object is a string leaving the instance type in the
// scratch1 register.
- GenerateStringCheck(masm, receiver, scratch1, scratch2,
- miss, &check_wrapper);
+ GenerateStringCheck(masm, receiver, scratch1, scratch2, miss, &check_wrapper);
// Load length directly from the string.
__ ldr(r0, FieldMemOperand(receiver, String::kLengthOffset));
@@ -238,9 +237,12 @@ void StubCompiler::GenerateLoadStringLength2(MacroAssembler* masm,
__ cmp(scratch1, Operand(JS_VALUE_TYPE));
__ b(ne, miss);
- // Unwrap the value in place and check if the wrapped value is a string.
- __ ldr(receiver, FieldMemOperand(receiver, JSValue::kValueOffset));
- __ b(&check_string);
+ // Unwrap the value and check if the wrapped value is a string.
+ __ ldr(scratch1, FieldMemOperand(receiver, JSValue::kValueOffset));
+ GenerateStringCheck(masm, scratch1, scratch2, scratch2, miss, miss);
+ __ ldr(r0, FieldMemOperand(scratch1, String::kLengthOffset));
+ __ mov(r0, Operand(r0, LSL, kSmiTagSize));
+ __ Ret();
}
@@ -256,10 +258,10 @@ void StubCompiler::GenerateLoadFunctionPrototype(MacroAssembler* masm,
// Generate StoreField code, value is passed in r0 register.
-// After executing generated code, the receiver_reg and name_reg
-// may be clobbered.
+// When leaving generated code after success, the receiver_reg and name_reg
+// may be clobbered. Upon branch to miss_label, the receiver and name
+// registers have their original values.
void StubCompiler::GenerateStoreField(MacroAssembler* masm,
- Builtins::Name storage_extend,
JSObject* object,
int index,
Map* transition,
@@ -292,11 +294,12 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm,
if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) {
// The properties must be extended before we can store the value.
// We jump to a runtime call that extends the properties array.
+ __ push(receiver_reg);
__ mov(r2, Operand(Handle<Map>(transition)));
- // Please note, if we implement keyed store for arm we need
- // to call the Builtins::KeyedStoreIC_ExtendStorage.
- Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_ExtendStorage));
- __ Jump(ic, RelocInfo::CODE_TARGET);
+ __ stm(db_w, sp, r2.bit() | r0.bit());
+ __ TailCallRuntime(
+ ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage)),
+ 3, 1);
return;
}
@@ -373,7 +376,7 @@ static void GenerateCallFunction(MacroAssembler* masm,
// Check that the function really is a function.
__ BranchOnSmi(r1, miss);
- __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
+ __ CompareObjectType(r1, r3, r3, JS_FUNCTION_TYPE);
__ b(ne, miss);
// Patch the receiver on the stack with the global proxy if
@@ -388,68 +391,6 @@ static void GenerateCallFunction(MacroAssembler* masm,
}
-static void GenerateCallConstFunction(MacroAssembler* masm,
- JSFunction* function,
- const ParameterCount& arguments) {
- ASSERT(function->is_compiled());
-
- // Get the function and setup the context.
- __ mov(r1, Operand(Handle<JSFunction>(function)));
- __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
-
- // Jump to the cached code (tail call).
- Handle<Code> code(function->code());
- ParameterCount expected(function->shared()->formal_parameter_count());
- __ InvokeCode(code, expected, arguments,
- RelocInfo::CODE_TARGET, JUMP_FUNCTION);
-}
-
-
-template <class Compiler>
-static void CompileLoadInterceptor(Compiler* compiler,
- StubCompiler* stub_compiler,
- MacroAssembler* masm,
- JSObject* object,
- JSObject* holder,
- String* name,
- LookupResult* lookup,
- Register receiver,
- Register scratch1,
- Register scratch2,
- Label* miss) {
- ASSERT(holder->HasNamedInterceptor());
- ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined());
-
- // Check that the receiver isn't a smi.
- __ BranchOnSmi(receiver, miss);
-
- // Check that the maps haven't changed.
- Register reg =
- stub_compiler->CheckPrototypes(object, receiver, holder,
- scratch1, scratch2, name, miss);
-
- if (lookup->IsValid() && lookup->IsCacheable()) {
- compiler->CompileCacheable(masm,
- stub_compiler,
- receiver,
- reg,
- scratch1,
- scratch2,
- holder,
- lookup,
- name,
- miss);
- } else {
- compiler->CompileRegular(masm,
- receiver,
- reg,
- scratch2,
- holder,
- miss);
- }
-}
-
-
static void PushInterceptorArguments(MacroAssembler* masm,
Register receiver,
Register holder,
@@ -500,7 +441,7 @@ class LoadInterceptorCompiler BASE_EMBEDDED {
LookupResult* lookup,
String* name,
Label* miss_label) {
- AccessorInfo* callback = 0;
+ AccessorInfo* callback = NULL;
bool optimize = false;
// So far the most popular follow ups for interceptor loads are FIELD
// and CALLBACKS, so inline only them, other cases may be added
@@ -523,9 +464,7 @@ class LoadInterceptorCompiler BASE_EMBEDDED {
// Note: starting a frame here makes GC aware of pointers pushed below.
__ EnterInternalFrame();
- if (lookup->type() == CALLBACKS) {
- __ push(receiver);
- }
+ __ push(receiver);
__ push(holder);
__ push(name_);
@@ -546,10 +485,7 @@ class LoadInterceptorCompiler BASE_EMBEDDED {
__ bind(&interceptor_failed);
__ pop(name_);
__ pop(holder);
-
- if (lookup->type() == CALLBACKS) {
- __ pop(receiver);
- }
+ __ pop(receiver);
__ LeaveInternalFrame();
@@ -621,108 +557,48 @@ class LoadInterceptorCompiler BASE_EMBEDDED {
};
-class CallInterceptorCompiler BASE_EMBEDDED {
- public:
- CallInterceptorCompiler(const ParameterCount& arguments, Register name)
- : arguments_(arguments), argc_(arguments.immediate()), name_(name) {}
-
- void CompileCacheable(MacroAssembler* masm,
- StubCompiler* stub_compiler,
- Register receiver,
- Register holder,
- Register scratch1,
- Register scratch2,
- JSObject* holder_obj,
- LookupResult* lookup,
- String* name,
- Label* miss_label) {
- JSFunction* function = 0;
- bool optimize = false;
- // So far the most popular case for failed interceptor is
- // CONSTANT_FUNCTION sitting below.
- if (lookup->type() == CONSTANT_FUNCTION) {
- function = lookup->GetConstantFunction();
- // JSArray holder is a special case for call constant function
- // (see the corresponding code).
- if (function->is_compiled() && !holder_obj->IsJSArray()) {
- optimize = true;
- }
- }
-
- if (!optimize) {
- CompileRegular(masm, receiver, holder, scratch2, holder_obj, miss_label);
- return;
- }
-
- // Constant functions cannot sit on global object.
- ASSERT(!lookup->holder()->IsGlobalObject());
-
- __ EnterInternalFrame();
- __ push(holder); // Save the holder.
- __ push(name_); // Save the name.
-
- CompileCallLoadPropertyWithInterceptor(masm,
- receiver,
- holder,
- name_,
- holder_obj);
-
- ASSERT(!r0.is(name_));
- ASSERT(!r0.is(scratch1));
- __ pop(name_); // Restore the name.
- __ pop(scratch1); // Restore the holder.
- __ LeaveInternalFrame();
-
- // Compare with no_interceptor_result_sentinel.
- __ LoadRoot(scratch2, Heap::kNoInterceptorResultSentinelRootIndex);
- __ cmp(r0, scratch2);
- Label invoke;
- __ b(ne, &invoke);
-
- stub_compiler->CheckPrototypes(holder_obj, scratch1,
- lookup->holder(), scratch1,
- scratch2,
- name,
- miss_label);
- GenerateCallConstFunction(masm, function, arguments_);
+static void CompileLoadInterceptor(LoadInterceptorCompiler* compiler,
+ StubCompiler* stub_compiler,
+ MacroAssembler* masm,
+ JSObject* object,
+ JSObject* holder,
+ String* name,
+ LookupResult* lookup,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Label* miss) {
+ ASSERT(holder->HasNamedInterceptor());
+ ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined());
- __ bind(&invoke);
- }
+ // Check that the receiver isn't a smi.
+ __ BranchOnSmi(receiver, miss);
- void CompileRegular(MacroAssembler* masm,
- Register receiver,
- Register holder,
- Register scratch,
- JSObject* holder_obj,
- Label* miss_label) {
- __ EnterInternalFrame();
- // Save the name_ register across the call.
- __ push(name_);
+ // Check that the maps haven't changed.
+ Register reg =
+ stub_compiler->CheckPrototypes(object, receiver, holder,
+ scratch1, scratch2, name, miss);
- PushInterceptorArguments(masm,
+ if (lookup->IsProperty() && lookup->IsCacheable()) {
+ compiler->CompileCacheable(masm,
+ stub_compiler,
+ receiver,
+ reg,
+ scratch1,
+ scratch2,
+ holder,
+ lookup,
+ name,
+ miss);
+ } else {
+ compiler->CompileRegular(masm,
receiver,
+ reg,
+ scratch2,
holder,
- name_,
- holder_obj);
-
- ExternalReference ref = ExternalReference(
- IC_Utility(IC::kLoadPropertyWithInterceptorForCall));
- __ mov(r0, Operand(5));
- __ mov(r1, Operand(ref));
-
- CEntryStub stub(1);
- __ CallStub(&stub);
-
- // Restore the name_ register.
- __ pop(name_);
- __ LeaveInternalFrame();
+ miss);
}
-
- private:
- const ParameterCount& arguments_;
- int argc_;
- Register name_;
-};
+}
#undef __
@@ -735,7 +611,11 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
Register holder_reg,
Register scratch,
String* name,
+ int save_at_depth,
Label* miss) {
+ // TODO(602): support object saving.
+ ASSERT(save_at_depth == kInvalidProtoDepth);
+
// Check that the maps haven't changed.
Register result =
masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch, miss);
@@ -762,7 +642,7 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
object = JSObject::cast(object->GetPrototype());
}
- // Return the register containin the holder.
+ // Return the register containing the holder.
return result;
}
@@ -901,12 +781,13 @@ Object* StubCompiler::CompileLazyCompile(Code::Flags flags) {
}
-Object* CallStubCompiler::CompileCallField(Object* object,
+Object* CallStubCompiler::CompileCallField(JSObject* object,
JSObject* holder,
int index,
String* name) {
// ----------- S t a t e -------------
- // -- lr: return address
+ // -- r2 : name
+ // -- lr : return address
// -----------------------------------
Label miss;
@@ -919,8 +800,7 @@ Object* CallStubCompiler::CompileCallField(Object* object,
__ b(eq, &miss);
// Do the right check and compute the holder register.
- Register reg =
- CheckPrototypes(JSObject::cast(object), r0, holder, r3, r2, name, &miss);
+ Register reg = CheckPrototypes(object, r0, holder, r1, r3, name, &miss);
GenerateFastPropertyLoad(masm(), r1, reg, holder, index);
GenerateCallFunction(masm(), object, arguments(), &miss);
@@ -941,7 +821,8 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
String* name,
CheckType check) {
// ----------- S t a t e -------------
- // -- lr: return address
+ // -- r2 : name
+ // -- lr : return address
// -----------------------------------
Label miss;
@@ -962,7 +843,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
switch (check) {
case RECEIVER_MAP_CHECK:
// Check that the maps haven't changed.
- CheckPrototypes(JSObject::cast(object), r1, holder, r3, r2, name, &miss);
+ CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, name, &miss);
// Patch the receiver on the stack with the global proxy if
// necessary.
@@ -978,13 +859,13 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
__ jmp(&miss);
} else {
// Check that the object is a two-byte string or a symbol.
- __ CompareObjectType(r1, r2, r2, FIRST_NONSTRING_TYPE);
+ __ CompareObjectType(r1, r3, r3, FIRST_NONSTRING_TYPE);
__ b(hs, &miss);
// Check that the maps starting from the prototype haven't changed.
GenerateLoadGlobalFunctionPrototype(masm(),
Context::STRING_FUNCTION_INDEX,
- r2);
- CheckPrototypes(JSObject::cast(object->GetPrototype()), r2, holder, r3,
+ r0);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), r0, holder, r3,
r1, name, &miss);
}
break;
@@ -998,14 +879,14 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
// Check that the object is a smi or a heap number.
__ tst(r1, Operand(kSmiTagMask));
__ b(eq, &fast);
- __ CompareObjectType(r1, r2, r2, HEAP_NUMBER_TYPE);
+ __ CompareObjectType(r1, r0, r0, HEAP_NUMBER_TYPE);
__ b(ne, &miss);
__ bind(&fast);
// Check that the maps starting from the prototype haven't changed.
GenerateLoadGlobalFunctionPrototype(masm(),
Context::NUMBER_FUNCTION_INDEX,
- r2);
- CheckPrototypes(JSObject::cast(object->GetPrototype()), r2, holder, r3,
+ r0);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), r0, holder, r3,
r1, name, &miss);
}
break;
@@ -1028,22 +909,22 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
// Check that the maps starting from the prototype haven't changed.
GenerateLoadGlobalFunctionPrototype(masm(),
Context::BOOLEAN_FUNCTION_INDEX,
- r2);
- CheckPrototypes(JSObject::cast(object->GetPrototype()), r2, holder, r3,
+ r0);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), r0, holder, r3,
r1, name, &miss);
}
break;
}
case JSARRAY_HAS_FAST_ELEMENTS_CHECK:
- CheckPrototypes(JSObject::cast(object), r1, holder, r3, r2, name, &miss);
+ CheckPrototypes(JSObject::cast(object), r1, holder, r3, r0, name, &miss);
// Make sure object->HasFastElements().
// Get the elements array of the object.
__ ldr(r3, FieldMemOperand(r1, JSObject::kElementsOffset));
// Check that the object is in fast mode (not dictionary).
- __ ldr(r2, FieldMemOperand(r3, HeapObject::kMapOffset));
+ __ ldr(r0, FieldMemOperand(r3, HeapObject::kMapOffset));
__ LoadRoot(ip, Heap::kFixedArrayMapRootIndex);
- __ cmp(r2, ip);
+ __ cmp(r0, ip);
__ b(ne, &miss);
break;
@@ -1051,7 +932,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
UNREACHABLE();
}
- GenerateCallConstFunction(masm(), function, arguments());
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
// Handle call cache miss.
__ bind(&miss);
@@ -1067,14 +948,22 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
}
-Object* CallStubCompiler::CompileCallInterceptor(Object* object,
+Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
JSObject* holder,
String* name) {
// ----------- S t a t e -------------
- // -- lr: return address
+ // -- r2 : name
+ // -- lr : return address
// -----------------------------------
+ ASSERT(holder->HasNamedInterceptor());
+ ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined());
Label miss;
+ const Register receiver = r0;
+ const Register holder_reg = r1;
+ const Register name_reg = r2;
+ const Register scratch = r3;
+
// Get the number of arguments.
const int argc = arguments().immediate();
@@ -1083,24 +972,79 @@ Object* CallStubCompiler::CompileCallInterceptor(Object* object,
// Get the receiver from the stack into r0.
__ ldr(r0, MemOperand(sp, argc * kPointerSize));
- // Load the name from the stack into r1.
- __ ldr(r1, MemOperand(sp, (argc + 1) * kPointerSize));
- CallInterceptorCompiler compiler(arguments(), r1);
- CompileLoadInterceptor(&compiler,
- this,
- masm(),
- JSObject::cast(object),
- holder,
- name,
- &lookup,
- r0,
- r2,
- r3,
- &miss);
+ // Check that the receiver isn't a smi.
+ __ BranchOnSmi(receiver, &miss);
+
+ // Check that the maps haven't changed.
+ Register reg = CheckPrototypes(object, receiver, holder, holder_reg,
+ scratch, name, &miss);
+ if (!reg.is(holder_reg)) {
+ __ mov(holder_reg, reg);
+ }
+
+ // If we call a constant function when the interceptor returns
+ // the no-result sentinel, generate code that optimizes this case.
+ if (lookup.IsProperty() &&
+ lookup.IsCacheable() &&
+ lookup.type() == CONSTANT_FUNCTION &&
+ lookup.GetConstantFunction()->is_compiled() &&
+ !holder->IsJSArray()) {
+ // Constant functions cannot sit on global object.
+ ASSERT(!lookup.holder()->IsGlobalObject());
+
+ // Call the interceptor.
+ __ EnterInternalFrame();
+ __ push(holder_reg);
+ __ push(name_reg);
+ CompileCallLoadPropertyWithInterceptor(masm(),
+ receiver,
+ holder_reg,
+ name_reg,
+ holder);
+ __ pop(name_reg);
+ __ pop(holder_reg);
+ __ LeaveInternalFrame();
+ // r0 no longer contains the receiver.
+
+ // If interceptor returns no-result sentinal, call the constant function.
+ __ LoadRoot(scratch, Heap::kNoInterceptorResultSentinelRootIndex);
+ __ cmp(r0, scratch);
+ Label invoke;
+ __ b(ne, &invoke);
+ // Check the prototypes between the interceptor's holder and the
+ // constant function's holder.
+ CheckPrototypes(holder, holder_reg,
+ lookup.holder(), r0,
+ scratch,
+ name,
+ &miss);
+
+ __ InvokeFunction(lookup.GetConstantFunction(),
+ arguments(),
+ JUMP_FUNCTION);
+
+ __ bind(&invoke);
+
+ } else {
+ // Call a runtime function to load the interceptor property.
+ __ EnterInternalFrame();
+ __ push(name_reg);
+
+ PushInterceptorArguments(masm(), receiver, holder_reg, name_reg, holder);
+ __ CallExternalReference(
+ ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForCall)),
+ 5);
+
+ __ pop(name_reg);
+ __ LeaveInternalFrame();
+ }
+
+ // Move returned value, the function to call, to r1.
+ __ mov(r1, r0);
// Restore receiver.
- __ ldr(r0, MemOperand(sp, argc * kPointerSize));
+ __ ldr(receiver, MemOperand(sp, argc * kPointerSize));
GenerateCallFunction(masm(), object, arguments(), &miss);
@@ -1120,7 +1064,8 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
JSFunction* function,
String* name) {
// ----------- S t a t e -------------
- // -- lr: return address
+ // -- r2 : name
+ // -- lr : return address
// -----------------------------------
Label miss;
@@ -1139,7 +1084,7 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
}
// Check that the maps haven't changed.
- CheckPrototypes(object, r0, holder, r3, r2, name, &miss);
+ CheckPrototypes(object, r0, holder, r3, r1, name, &miss);
// Get the value from the cell.
__ mov(r3, Operand(Handle<JSGlobalPropertyCell>(cell)));
@@ -1159,8 +1104,8 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
// Check the shared function info. Make sure it hasn't changed.
__ mov(r3, Operand(Handle<SharedFunctionInfo>(function->shared())));
- __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
- __ cmp(r2, r3);
+ __ ldr(r4, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
+ __ cmp(r4, r3);
__ b(ne, &miss);
} else {
__ cmp(r1, Operand(Handle<JSFunction>(function)));
@@ -1178,7 +1123,7 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
__ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
// Jump to the cached code (tail call).
- __ IncrementCounter(&Counters::call_global_inline, 1, r2, r3);
+ __ IncrementCounter(&Counters::call_global_inline, 1, r1, r3);
ASSERT(function->is_compiled());
Handle<Code> code(function->code());
ParameterCount expected(function->shared()->formal_parameter_count());
@@ -1202,25 +1147,19 @@ Object* StoreStubCompiler::CompileStoreField(JSObject* object,
String* name) {
// ----------- S t a t e -------------
// -- r0 : value
+ // -- r1 : receiver
// -- r2 : name
// -- lr : return address
- // -- [sp] : receiver
// -----------------------------------
Label miss;
- // Get the receiver from the stack.
- __ ldr(r3, MemOperand(sp, 0 * kPointerSize));
-
- // name register might be clobbered.
GenerateStoreField(masm(),
- Builtins::StoreIC_ExtendStorage,
object,
index,
transition,
- r3, r2, r1,
+ r1, r2, r3,
&miss);
__ bind(&miss);
- __ mov(r2, Operand(Handle<String>(name))); // restore name
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss));
__ Jump(ic, RelocInfo::CODE_TARGET);
@@ -1234,39 +1173,33 @@ Object* StoreStubCompiler::CompileStoreCallback(JSObject* object,
String* name) {
// ----------- S t a t e -------------
// -- r0 : value
+ // -- r1 : receiver
// -- r2 : name
// -- lr : return address
- // -- [sp] : receiver
// -----------------------------------
Label miss;
- // Get the object from the stack.
- __ ldr(r3, MemOperand(sp, 0 * kPointerSize));
-
// Check that the object isn't a smi.
- __ tst(r3, Operand(kSmiTagMask));
+ __ tst(r1, Operand(kSmiTagMask));
__ b(eq, &miss);
// Check that the map of the object hasn't changed.
- __ ldr(r1, FieldMemOperand(r3, HeapObject::kMapOffset));
- __ cmp(r1, Operand(Handle<Map>(object->map())));
+ __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
+ __ cmp(r3, Operand(Handle<Map>(object->map())));
__ b(ne, &miss);
// Perform global security token check if needed.
if (object->IsJSGlobalProxy()) {
- __ CheckAccessGlobalProxy(r3, r1, &miss);
+ __ CheckAccessGlobalProxy(r1, r3, &miss);
}
// Stub never generated for non-global objects that require access
// checks.
ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
- __ ldr(ip, MemOperand(sp)); // receiver
- __ push(ip);
+ __ push(r1); // receiver
__ mov(ip, Operand(Handle<AccessorInfo>(callback))); // callback info
- __ push(ip);
- __ push(r2); // name
- __ push(r0); // value
+ __ stm(db_w, sp, ip.bit() | r2.bit() | r0.bit());
// Do tail-call to the runtime system.
ExternalReference store_callback_property =
@@ -1287,37 +1220,33 @@ Object* StoreStubCompiler::CompileStoreInterceptor(JSObject* receiver,
String* name) {
// ----------- S t a t e -------------
// -- r0 : value
+ // -- r1 : receiver
// -- r2 : name
// -- lr : return address
- // -- [sp] : receiver
// -----------------------------------
Label miss;
- // Get the object from the stack.
- __ ldr(r3, MemOperand(sp, 0 * kPointerSize));
-
// Check that the object isn't a smi.
- __ tst(r3, Operand(kSmiTagMask));
+ __ tst(r1, Operand(kSmiTagMask));
__ b(eq, &miss);
// Check that the map of the object hasn't changed.
- __ ldr(r1, FieldMemOperand(r3, HeapObject::kMapOffset));
- __ cmp(r1, Operand(Handle<Map>(receiver->map())));
+ __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
+ __ cmp(r3, Operand(Handle<Map>(receiver->map())));
__ b(ne, &miss);
// Perform global security token check if needed.
if (receiver->IsJSGlobalProxy()) {
- __ CheckAccessGlobalProxy(r3, r1, &miss);
+ __ CheckAccessGlobalProxy(r1, r3, &miss);
}
- // Stub never generated for non-global objects that require access
+ // Stub is never generated for non-global objects that require access
// checks.
ASSERT(receiver->IsJSGlobalProxy() || !receiver->IsAccessCheckNeeded());
- __ ldr(ip, MemOperand(sp)); // receiver
- __ push(ip);
- __ push(r2); // name
- __ push(r0); // value
+ __ push(r1); // receiver.
+ __ push(r2); // name.
+ __ push(r0); // value.
// Do tail-call to the runtime system.
ExternalReference store_ic_property =
@@ -1339,14 +1268,13 @@ Object* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object,
String* name) {
// ----------- S t a t e -------------
// -- r0 : value
+ // -- r1 : receiver
// -- r2 : name
// -- lr : return address
- // -- [sp] : receiver
// -----------------------------------
Label miss;
// Check that the map of the global has not changed.
- __ ldr(r1, MemOperand(sp, 0 * kPointerSize));
__ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
__ cmp(r3, Operand(Handle<Map>(object->map())));
__ b(ne, &miss);
@@ -1355,12 +1283,12 @@ Object* StoreStubCompiler::CompileStoreGlobal(GlobalObject* object,
__ mov(r2, Operand(Handle<JSGlobalPropertyCell>(cell)));
__ str(r0, FieldMemOperand(r2, JSGlobalPropertyCell::kValueOffset));
- __ IncrementCounter(&Counters::named_store_global_inline, 1, r1, r3);
+ __ IncrementCounter(&Counters::named_store_global_inline, 1, r4, r3);
__ Ret();
// Handle store cache miss.
__ bind(&miss);
- __ IncrementCounter(&Counters::named_store_global_inline_miss, 1, r1, r3);
+ __ IncrementCounter(&Counters::named_store_global_inline_miss, 1, r4, r3);
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss));
__ Jump(ic, RelocInfo::CODE_TARGET);
@@ -1672,7 +1600,7 @@ Object* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) {
__ cmp(r2, Operand(Handle<String>(name)));
__ b(ne, &miss);
- GenerateLoadStringLength2(masm(), r0, r1, r3, &miss);
+ GenerateLoadStringLength(masm(), r0, r1, r3, &miss);
__ bind(&miss);
__ DecrementCounter(&Counters::keyed_load_string_length, 1, r1, r3);
@@ -1717,7 +1645,6 @@ Object* KeyedStoreStubCompiler::CompileStoreField(JSObject* object,
__ ldr(r3, MemOperand(sp));
// r1 is used as scratch register, r3 and r2 might be clobbered.
GenerateStoreField(masm(),
- Builtins::StoreIC_ExtendStorage,
object,
index,
transition,
diff --git a/src/arm/stub-cache-arm.cc.rej b/src/arm/stub-cache-arm.cc.rej
deleted file mode 100644
index f8baa413..00000000
--- a/src/arm/stub-cache-arm.cc.rej
+++ /dev/null
@@ -1,153 +0,0 @@
-*************** void StubCompiler::GenerateLoadInterceptor(JSObject* object,
-*** 491,520 ****
- Register scratch2,
- String* name,
- Label* miss) {
-- // Check that the receiver isn't a smi.
-- __ tst(receiver, Operand(kSmiTagMask));
-- __ b(eq, miss);
--
-- // Check that the maps haven't changed.
-- Register reg =
-- CheckPrototypes(object, receiver, holder, scratch1, scratch2, name, miss);
--
-- // Push the arguments on the JS stack of the caller.
-- __ push(receiver); // receiver
-- __ push(reg); // holder
-- __ push(name_reg); // name
--
-- InterceptorInfo* interceptor = holder->GetNamedInterceptor();
-- ASSERT(!Heap::InNewSpace(interceptor));
-- __ mov(scratch1, Operand(Handle<Object>(interceptor)));
-- __ push(scratch1);
-- __ ldr(scratch2, FieldMemOperand(scratch1, InterceptorInfo::kDataOffset));
-- __ push(scratch2);
--
-- // Do tail-call to the runtime system.
-- ExternalReference load_ic_property =
-- ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForLoad));
-- __ TailCallRuntime(load_ic_property, 5, 1);
- }
-
-
---- 854,871 ----
- Register scratch2,
- String* name,
- Label* miss) {
-+ LoadInterceptorCompiler compiler(name_reg);
-+ CompileLoadInterceptor(&compiler,
-+ this,
-+ masm(),
-+ object,
-+ holder,
-+ name,
-+ lookup,
-+ receiver,
-+ scratch1,
-+ scratch2,
-+ miss);
- }
-
-
-*************** Object* CallStubCompiler::CompileCallField(Object* object,
-*** 572,593 ****
- CheckPrototypes(JSObject::cast(object), r0, holder, r3, r2, name, &miss);
- GenerateFastPropertyLoad(masm(), r1, reg, holder, index);
-
-- // Check that the function really is a function.
-- __ tst(r1, Operand(kSmiTagMask));
-- __ b(eq, &miss);
-- // Get the map.
-- __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
-- __ b(ne, &miss);
--
-- // Patch the receiver on the stack with the global proxy if
-- // necessary.
-- if (object->IsGlobalObject()) {
-- __ ldr(r3, FieldMemOperand(r0, GlobalObject::kGlobalReceiverOffset));
-- __ str(r3, MemOperand(sp, argc * kPointerSize));
-- }
--
-- // Invoke the function.
-- __ InvokeFunction(r1, arguments(), JUMP_FUNCTION);
-
- // Handle call cache miss.
- __ bind(&miss);
---- 923,929 ----
- CheckPrototypes(JSObject::cast(object), r0, holder, r3, r2, name, &miss);
- GenerateFastPropertyLoad(masm(), r1, reg, holder, index);
-
-+ GenerateCallFunction(masm(), object, arguments(), &miss);
-
- // Handle call cache miss.
- __ bind(&miss);
-*************** Object* CallStubCompiler::CompileCallConstant(Object* object,
-*** 715,730 ****
- UNREACHABLE();
- }
-
-- // Get the function and setup the context.
-- __ mov(r1, Operand(Handle<JSFunction>(function)));
-- __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
--
-- // Jump to the cached code (tail call).
-- ASSERT(function->is_compiled());
-- Handle<Code> code(function->code());
-- ParameterCount expected(function->shared()->formal_parameter_count());
-- __ InvokeCode(code, expected, arguments(),
-- RelocInfo::CODE_TARGET, JUMP_FUNCTION);
-
- // Handle call cache miss.
- __ bind(&miss);
---- 1051,1057 ----
- UNREACHABLE();
- }
-
-+ GenerateCallConstFunction(masm(), function, arguments());
-
- // Handle call cache miss.
- __ bind(&miss);
-*************** Object* CallStubCompiler::CompileCallInterceptor(Object* object,
-*** 748,754 ****
- // -----------------------------------
- Label miss;
-
-- // TODO(1224669): Implement.
-
- // Handle call cache miss.
- __ bind(&miss);
---- 1075,1108 ----
- // -----------------------------------
- Label miss;
-
-+ // Get the number of arguments.
-+ const int argc = arguments().immediate();
-+
-+ LookupResult lookup;
-+ LookupPostInterceptor(holder, name, &lookup);
-+
-+ // Get the receiver from the stack into r0.
-+ __ ldr(r0, MemOperand(sp, argc * kPointerSize));
-+ // Load the name from the stack into r1.
-+ __ ldr(r1, MemOperand(sp, (argc + 1) * kPointerSize));
-+
-+ CallInterceptorCompiler compiler(arguments(), r1);
-+ CompileLoadInterceptor(&compiler,
-+ this,
-+ masm(),
-+ JSObject::cast(object),
-+ holder,
-+ name,
-+ &lookup,
-+ r0,
-+ r2,
-+ r3,
-+ &miss);
-+
-+ // Restore receiver.
-+ __ ldr(r0, MemOperand(sp, argc * kPointerSize));
-+
-+ GenerateCallFunction(masm(), object, arguments(), &miss);
-
- // Handle call cache miss.
- __ bind(&miss);
diff --git a/src/arm/virtual-frame-arm.cc b/src/arm/virtual-frame-arm.cc
index 7a8ac726..0f7c5971 100644
--- a/src/arm/virtual-frame-arm.cc
+++ b/src/arm/virtual-frame-arm.cc
@@ -47,7 +47,7 @@ VirtualFrame::VirtualFrame()
: elements_(parameter_count() + local_count() + kPreallocatedElements),
stack_pointer_(parameter_count()) { // 0-based index of TOS.
for (int i = 0; i <= stack_pointer_; i++) {
- elements_.Add(FrameElement::MemoryElement());
+ elements_.Add(FrameElement::MemoryElement(NumberInfo::kUnknown));
}
for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
register_locations_[i] = kIllegalIndex;
@@ -233,6 +233,14 @@ void VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) {
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
+void VirtualFrame::DebugBreak() {
+ ASSERT(cgen()->HasValidEntryRegisters());
+ __ DebugBreak();
+}
+#endif
+
+
void VirtualFrame::InvokeBuiltin(Builtins::JavaScript id,
InvokeJSFlags flags,
int arg_count) {
@@ -305,7 +313,7 @@ void VirtualFrame::EmitPop(Register reg) {
void VirtualFrame::EmitPush(Register reg) {
ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement());
+ elements_.Add(FrameElement::MemoryElement(NumberInfo::kUnknown));
stack_pointer_++;
__ push(reg);
}
diff --git a/src/arm/virtual-frame-arm.h b/src/arm/virtual-frame-arm.h
index 9a2f7d36..a45cfc6e 100644
--- a/src/arm/virtual-frame-arm.h
+++ b/src/arm/virtual-frame-arm.h
@@ -68,7 +68,8 @@ class VirtualFrame : public ZoneObject {
MacroAssembler* masm() { return cgen()->masm(); }
// Create a duplicate of an existing valid frame element.
- FrameElement CopyElementAt(int index);
+ FrameElement CopyElementAt(int index,
+ NumberInfo::Type info = NumberInfo::kUnknown);
// The number of elements on the virtual frame.
int element_count() { return elements_.length(); }
@@ -297,6 +298,10 @@ class VirtualFrame : public ZoneObject {
void CallRuntime(Runtime::Function* f, int arg_count);
void CallRuntime(Runtime::FunctionId id, int arg_count);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ void DebugBreak();
+#endif
+
// Invoke builtin given the number of arguments it expects on (and
// removes from) the stack.
void InvokeBuiltin(Builtins::JavaScript id,
@@ -339,7 +344,7 @@ class VirtualFrame : public ZoneObject {
void EmitPushMultiple(int count, int src_regs);
// Push an element on the virtual frame.
- void Push(Register reg);
+ void Push(Register reg, NumberInfo::Type info = NumberInfo::kUnknown);
void Push(Handle<Object> value);
void Push(Smi* value) { Push(Handle<Object>(value)); }
diff --git a/src/array.js b/src/array.js
index c3ab179d..c28a6629 100644
--- a/src/array.js
+++ b/src/array.js
@@ -566,10 +566,11 @@ function ArraySlice(start, end) {
function ArraySplice(start, delete_count) {
var num_arguments = %_ArgumentsLength();
- // SpiderMonkey and KJS return undefined in the case where no
+ // SpiderMonkey and JSC return undefined in the case where no
// arguments are given instead of using the implicit undefined
// arguments. This does not follow ECMA-262, but we do the same for
// compatibility.
+ // TraceMonkey follows ECMA-262 though.
if (num_arguments == 0) return;
var len = TO_UINT32(this.length);
@@ -582,7 +583,7 @@ function ArraySplice(start, delete_count) {
if (start_i > len) start_i = len;
}
- // SpiderMonkey and KJS treat the case where no delete count is
+ // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
// given differently from when an undefined delete count is given.
// This does not follow ECMA-262, but we do the same for
// compatibility.
diff --git a/src/assembler.cc b/src/assembler.cc
index dbf2742b..aaf10efe 100644
--- a/src/assembler.cc
+++ b/src/assembler.cc
@@ -430,6 +430,11 @@ const char* RelocInfo::RelocModeName(RelocInfo::Mode rmode) {
return "code target (js construct call)";
case RelocInfo::CODE_TARGET_CONTEXT:
return "code target (context)";
+ case RelocInfo::DEBUG_BREAK:
+#ifndef ENABLE_DEBUGGER_SUPPORT
+ UNREACHABLE();
+#endif
+ return "debug break";
case RelocInfo::CODE_TARGET:
return "code target";
case RelocInfo::RUNTIME_ENTRY:
@@ -485,6 +490,11 @@ void RelocInfo::Verify() {
case EMBEDDED_OBJECT:
Object::VerifyPointer(target_object());
break;
+ case DEBUG_BREAK:
+#ifndef ENABLE_DEBUGGER_SUPPORT
+ UNREACHABLE();
+ break;
+#endif
case CONSTRUCT_CALL:
case CODE_TARGET_CONTEXT:
case CODE_TARGET: {
@@ -569,6 +579,11 @@ ExternalReference ExternalReference::random_positive_smi_function() {
}
+ExternalReference ExternalReference::transcendental_cache_array_address() {
+ return ExternalReference(TranscendentalCache::cache_array_address());
+}
+
+
ExternalReference ExternalReference::keyed_lookup_cache_keys() {
return ExternalReference(KeyedLookupCache::keys_address());
}
@@ -609,6 +624,11 @@ ExternalReference ExternalReference::new_space_start() {
}
+ExternalReference ExternalReference::new_space_mask() {
+ return ExternalReference(reinterpret_cast<Address>(Heap::NewSpaceMask()));
+}
+
+
ExternalReference ExternalReference::new_space_allocation_top_address() {
return ExternalReference(Heap::NewSpaceAllocationTopAddress());
}
diff --git a/src/assembler.h b/src/assembler.h
index 942ce476..004ede35 100644
--- a/src/assembler.h
+++ b/src/assembler.h
@@ -119,6 +119,7 @@ class RelocInfo BASE_EMBEDDED {
// Please note the order is important (see IsCodeTarget, IsGCRelocMode).
CONSTRUCT_CALL, // code target that is a call to a JavaScript constructor.
CODE_TARGET_CONTEXT, // code target used for contextual loads.
+ DEBUG_BREAK,
CODE_TARGET, // code target which is not any of the above.
EMBEDDED_OBJECT,
EMBEDDED_STRING,
@@ -399,6 +400,7 @@ class ExternalReference BASE_EMBEDDED {
static ExternalReference perform_gc_function();
static ExternalReference random_positive_smi_function();
+ static ExternalReference transcendental_cache_array_address();
// Static data in the keyed lookup cache.
static ExternalReference keyed_lookup_cache_keys();
@@ -426,6 +428,7 @@ class ExternalReference BASE_EMBEDDED {
// Static variable Heap::NewSpaceStart()
static ExternalReference new_space_start();
+ static ExternalReference new_space_mask();
static ExternalReference heap_always_allocate_scope_depth();
// Used for fast allocation in generated code.
diff --git a/src/ast.h b/src/ast.h
index 48d0bfac..927a9f50 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -1,4 +1,4 @@
-// Copyright 2006-2008 the V8 project authors. All rights reserved.
+// Copyright 2010 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:
@@ -102,6 +102,7 @@ namespace internal {
// Forward declarations
class TargetCollector;
class MaterializedLiteral;
+class DefinitionInfo;
#define DEF_FORWARD_DECLARATION(type) class type;
AST_NODE_LIST(DEF_FORWARD_DECLARATION)
@@ -182,7 +183,7 @@ class Expression: public AstNode {
static const int kNoLabel = -1;
- Expression() : num_(kNoLabel) {}
+ Expression() : num_(kNoLabel), def_(NULL), defined_vars_(NULL) {}
virtual Expression* AsExpression() { return this; }
@@ -193,6 +194,15 @@ class Expression: public AstNode {
// names because [] for string objects is handled only by keyed ICs.
virtual bool IsPropertyName() { return false; }
+ // True if the expression does not have (evaluated) subexpressions.
+ // Function literals are leaves because their subexpressions are not
+ // evaluated.
+ virtual bool IsLeaf() { return false; }
+
+ // True if the expression has no side effects and is safe to
+ // evaluate out of order.
+ virtual bool IsTrivial() { return false; }
+
// Mark the expression as being compiled as an expression
// statement. This is used to transform postfix increments to
// (faster) prefix increments.
@@ -206,9 +216,20 @@ class Expression: public AstNode {
// AST node numbering ordered by evaluation order.
void set_num(int n) { num_ = n; }
+ // Data flow information.
+ DefinitionInfo* var_def() { return def_; }
+ void set_var_def(DefinitionInfo* def) { def_ = def; }
+
+ ZoneList<DefinitionInfo*>* defined_vars() { return defined_vars_; }
+ void set_defined_vars(ZoneList<DefinitionInfo*>* defined_vars) {
+ defined_vars_ = defined_vars;
+ }
+
private:
StaticType type_;
int num_;
+ DefinitionInfo* def_;
+ ZoneList<DefinitionInfo*>* defined_vars_;
};
@@ -720,6 +741,9 @@ class Literal: public Expression {
return false;
}
+ virtual bool IsLeaf() { return true; }
+ virtual bool IsTrivial() { return true; }
+
// Identity testers.
bool IsNull() const { return handle_.is_identical_to(Factory::null_value()); }
bool IsTrue() const { return handle_.is_identical_to(Factory::true_value()); }
@@ -802,6 +826,8 @@ class ObjectLiteral: public MaterializedLiteral {
virtual ObjectLiteral* AsObjectLiteral() { return this; }
virtual void Accept(AstVisitor* v);
+ virtual bool IsLeaf() { return properties()->is_empty(); }
+
Handle<FixedArray> constant_properties() const {
return constant_properties_;
}
@@ -825,6 +851,8 @@ class RegExpLiteral: public MaterializedLiteral {
virtual void Accept(AstVisitor* v);
+ virtual bool IsLeaf() { return true; }
+
Handle<String> pattern() const { return pattern_; }
Handle<String> flags() const { return flags_; }
@@ -849,6 +877,8 @@ class ArrayLiteral: public MaterializedLiteral {
virtual void Accept(AstVisitor* v);
virtual ArrayLiteral* AsArrayLiteral() { return this; }
+ virtual bool IsLeaf() { return values()->is_empty(); }
+
Handle<FixedArray> constant_elements() const { return constant_elements_; }
ZoneList<Expression*>* values() const { return values_; }
@@ -896,6 +926,15 @@ class VariableProxy: public Expression {
return var_ == NULL ? true : var_->IsValidLeftHandSide();
}
+ virtual bool IsLeaf() {
+ ASSERT(var_ != NULL); // Variable must be resolved.
+ return var()->is_global() || var()->rewrite()->IsLeaf();
+ }
+
+ // Reading from a mutable variable is a side effect, but 'this' is
+ // immutable.
+ virtual bool IsTrivial() { return is_this(); }
+
bool IsVariable(Handle<String> n) {
return !is_this() && name().is_identical_to(n);
}
@@ -981,6 +1020,8 @@ class Slot: public Expression {
// Type testing & conversion
virtual Slot* AsSlot() { return this; }
+ virtual bool IsLeaf() { return true; }
+
// Accessors
Variable* var() const { return var_; }
Type type() const { return type_; }
@@ -1337,6 +1378,8 @@ class FunctionLiteral: public Expression {
// Type testing & conversion
virtual FunctionLiteral* AsFunctionLiteral() { return this; }
+ virtual bool IsLeaf() { return true; }
+
Handle<String> name() const { return name_; }
Scope* scope() const { return scope_; }
ZoneList<Statement*>* body() const { return body_; }
@@ -1403,6 +1446,8 @@ class FunctionBoilerplateLiteral: public Expression {
Handle<JSFunction> boilerplate() const { return boilerplate_; }
+ virtual bool IsLeaf() { return true; }
+
virtual void Accept(AstVisitor* v);
private:
@@ -1413,6 +1458,7 @@ class FunctionBoilerplateLiteral: public Expression {
class ThisFunction: public Expression {
public:
virtual void Accept(AstVisitor* v);
+ virtual bool IsLeaf() { return true; }
};
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index 62edae50..6e565e08 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -177,116 +177,6 @@ void Bootstrapper::TearDown() {
}
-// Pending fixups are code positions that refer to builtin code
-// objects that were not available at the time the code was generated.
-// The pending list is processed whenever an environment has been
-// created.
-class PendingFixups : public AllStatic {
- public:
- static void Add(Code* code, MacroAssembler* masm);
- static bool Process(Handle<JSBuiltinsObject> builtins);
-
- static void Iterate(ObjectVisitor* v);
-
- private:
- static List<Object*> code_;
- static List<const char*> name_;
- static List<int> pc_;
- static List<uint32_t> flags_;
-
- static void Clear();
-};
-
-
-List<Object*> PendingFixups::code_(0);
-List<const char*> PendingFixups::name_(0);
-List<int> PendingFixups::pc_(0);
-List<uint32_t> PendingFixups::flags_(0);
-
-
-void PendingFixups::Add(Code* code, MacroAssembler* masm) {
- // Note this code is not only called during bootstrapping.
- List<MacroAssembler::Unresolved>* unresolved = masm->unresolved();
- int n = unresolved->length();
- for (int i = 0; i < n; i++) {
- const char* name = unresolved->at(i).name;
- code_.Add(code);
- name_.Add(name);
- pc_.Add(unresolved->at(i).pc);
- flags_.Add(unresolved->at(i).flags);
- LOG(StringEvent("unresolved", name));
- }
-}
-
-
-bool PendingFixups::Process(Handle<JSBuiltinsObject> builtins) {
- HandleScope scope;
- // NOTE: Extra fixups may be added to the list during the iteration
- // due to lazy compilation of functions during the processing. Do not
- // cache the result of getting the length of the code list.
- for (int i = 0; i < code_.length(); i++) {
- const char* name = name_[i];
- uint32_t flags = flags_[i];
- Handle<String> symbol = Factory::LookupAsciiSymbol(name);
- Object* o = builtins->GetProperty(*symbol);
-#ifdef DEBUG
- if (!o->IsJSFunction()) {
- V8_Fatal(__FILE__, __LINE__, "Cannot resolve call to builtin %s", name);
- }
-#endif
- Handle<SharedFunctionInfo> shared(JSFunction::cast(o)->shared());
- // Make sure the number of parameters match the formal parameter count.
- int argc = Bootstrapper::FixupFlagsArgumentsCount::decode(flags);
- USE(argc);
- ASSERT(shared->formal_parameter_count() == argc);
- // Do lazy compilation if necessary and check for stack overflows.
- if (!EnsureCompiled(shared, CLEAR_EXCEPTION)) {
- Clear();
- return false;
- }
- Code* code = Code::cast(code_[i]);
- Address pc = code->instruction_start() + pc_[i];
- RelocInfo target(pc, RelocInfo::CODE_TARGET, 0);
- bool use_code_object = Bootstrapper::FixupFlagsUseCodeObject::decode(flags);
- if (use_code_object) {
- target.set_target_object(shared->code());
- } else {
- target.set_target_address(shared->code()->instruction_start());
- }
- LOG(StringEvent("resolved", name));
- }
- Clear();
-
- // TODO(1240818): We should probably try to avoid doing this for all
- // the V8 builtin JS files. It should only happen after running
- // runtime.js - just like there shouldn't be any fixups left after
- // that.
- for (int i = 0; i < Builtins::NumberOfJavaScriptBuiltins(); i++) {
- Builtins::JavaScript id = static_cast<Builtins::JavaScript>(i);
- Handle<String> name = Factory::LookupAsciiSymbol(Builtins::GetName(id));
- JSFunction* function = JSFunction::cast(builtins->GetProperty(*name));
- builtins->set_javascript_builtin(id, function);
- }
-
- return true;
-}
-
-
-void PendingFixups::Clear() {
- code_.Clear();
- name_.Clear();
- pc_.Clear();
- flags_.Clear();
-}
-
-
-void PendingFixups::Iterate(ObjectVisitor* v) {
- if (!code_.is_empty()) {
- v->VisitPointers(&code_[0], &code_[0] + code_.length());
- }
-}
-
-
class Genesis BASE_EMBEDDED {
public:
Genesis(Handle<Object> global_object,
@@ -327,6 +217,10 @@ class Genesis BASE_EMBEDDED {
// deserialized, leaving the GC to pick it up.
void HookUpGlobalProxy(Handle<GlobalObject> inner_global,
Handle<JSGlobalProxy> global_proxy);
+ // Similarly, we want to use the inner global that has been created by the
+ // templates passed through the API. The inner global from the snapshot is
+ // detached from the other objects in the snapshot.
+ void HookUpInnerGlobal(Handle<GlobalObject> inner_global);
// New context initialization. Used for creating a context from scratch.
void InitializeGlobal(Handle<GlobalObject> inner_global,
Handle<JSFunction> empty_function);
@@ -341,12 +235,10 @@ class Genesis BASE_EMBEDDED {
static bool InstallExtension(const char* name);
static bool InstallExtension(v8::RegisteredExtension* current);
static void InstallSpecialObjects(Handle<Context> global_context);
+ bool InstallJSBuiltins(Handle<JSBuiltinsObject> builtins);
bool ConfigureApiObject(Handle<JSObject> object,
Handle<ObjectTemplateInfo> object_template);
bool ConfigureGlobalObjects(v8::Handle<v8::ObjectTemplate> global_template);
- void TransferMapsToDeserializedGlobals(
- Handle<GlobalObject> inner_global_outside_snapshot,
- Handle<GlobalObject> inner_global_from_snapshot);
// Migrates all properties from the 'from' object to the 'to'
// object and overrides the prototype in 'to' with the one from
@@ -385,15 +277,6 @@ class Genesis BASE_EMBEDDED {
void Bootstrapper::Iterate(ObjectVisitor* v) {
extensions_cache.Iterate(v);
v->Synchronize("Extensions");
- PendingFixups::Iterate(v);
- v->Synchronize("PendingFixups");
-}
-
-
-// While setting up the environment, we collect code positions that
-// need to be patched before we can run any code in the environment.
-void Bootstrapper::AddFixup(Code* code, MacroAssembler* masm) {
- PendingFixups::Add(code, masm);
}
@@ -634,7 +517,7 @@ Handle<JSGlobalProxy> Genesis::CreateNewGlobals(
Handle<Code> code = Handle<Code>(Builtins::builtin(Builtins::Illegal));
js_global_function =
Factory::NewFunction(name, JS_GLOBAL_OBJECT_TYPE,
- JSGlobalObject::kSize + 17 * kPointerSize, code, true);
+ JSGlobalObject::kSize, code, true);
// Change the constructor property of the prototype of the
// hidden global function to refer to the Object function.
Handle<JSObject> prototype =
@@ -704,6 +587,28 @@ void Genesis::HookUpGlobalProxy(Handle<GlobalObject> inner_global,
}
+void Genesis::HookUpInnerGlobal(Handle<GlobalObject> inner_global) {
+ Handle<GlobalObject> inner_global_from_snapshot(
+ GlobalObject::cast(global_context_->extension()));
+ Handle<JSBuiltinsObject> builtins_global(global_context_->builtins());
+ global_context_->set_extension(*inner_global);
+ global_context_->set_global(*inner_global);
+ global_context_->set_security_token(*inner_global);
+ static const PropertyAttributes attributes =
+ static_cast<PropertyAttributes>(READ_ONLY | DONT_DELETE);
+ ForceSetProperty(builtins_global,
+ Factory::LookupAsciiSymbol("global"),
+ inner_global,
+ attributes);
+ // Setup the reference from the global object to the builtins object.
+ JSGlobalObject::cast(*inner_global)->set_builtins(*builtins_global);
+ TransferNamedProperties(inner_global_from_snapshot, inner_global);
+ TransferIndexedProperties(inner_global_from_snapshot, inner_global);
+}
+
+
+// This is only called if we are not using snapshots. The equivalent
+// work in the snapshot case is done in HookUpInnerGlobal.
void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global,
Handle<JSFunction> empty_function) {
// --- G l o b a l C o n t e x t ---
@@ -865,11 +770,11 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global,
#ifdef DEBUG
LookupResult lookup;
result->LocalLookup(Heap::callee_symbol(), &lookup);
- ASSERT(lookup.IsValid() && (lookup.type() == FIELD));
+ ASSERT(lookup.IsProperty() && (lookup.type() == FIELD));
ASSERT(lookup.GetFieldIndex() == Heap::arguments_callee_index);
result->LocalLookup(Heap::length_symbol(), &lookup);
- ASSERT(lookup.IsValid() && (lookup.type() == FIELD));
+ ASSERT(lookup.IsProperty() && (lookup.type() == FIELD));
ASSERT(lookup.GetFieldIndex() == Heap::arguments_length_index);
ASSERT(result->map()->inobject_properties() > Heap::arguments_callee_index);
@@ -977,6 +882,7 @@ bool Genesis::CompileScriptCached(Vector<const char> name,
0,
extension,
NULL,
+ Handle<String>::null(),
use_runtime_context ? NATIVES_CODE : NOT_NATIVES_CODE);
if (boilerplate.is_null()) return false;
if (cache != NULL) cache->Add(name, boilerplate);
@@ -1003,8 +909,7 @@ bool Genesis::CompileScriptCached(Vector<const char> name,
Handle<Object> result =
Execution::Call(fun, receiver, 0, NULL, &has_pending_exception);
if (has_pending_exception) return false;
- return PendingFixups::Process(
- Handle<JSBuiltinsObject>(top_context->builtins()));
+ return true;
}
@@ -1024,7 +929,6 @@ void Genesis::InstallNativeFunctions() {
INSTALL_NATIVE(JSFunction, "ToInteger", to_integer_fun);
INSTALL_NATIVE(JSFunction, "ToUint32", to_uint32_fun);
INSTALL_NATIVE(JSFunction, "ToInt32", to_int32_fun);
- INSTALL_NATIVE(JSFunction, "ToBoolean", to_boolean_fun);
INSTALL_NATIVE(JSFunction, "GlobalEval", global_eval_fun);
INSTALL_NATIVE(JSFunction, "Instantiate", instantiate_fun);
INSTALL_NATIVE(JSFunction, "ConfigureTemplateInstance",
@@ -1211,6 +1115,10 @@ bool Genesis::InstallNatives() {
i++) {
Vector<const char> name = Natives::GetScriptName(i);
if (!CompileBuiltin(i)) return false;
+ // TODO(ager): We really only need to install the JS builtin
+ // functions on the builtins object after compiling and running
+ // runtime.js.
+ if (!InstallJSBuiltins(builtins)) return false;
}
InstallNativeFunctions();
@@ -1397,17 +1305,19 @@ bool Genesis::InstallExtension(v8::RegisteredExtension* current) {
}
-void Genesis::TransferMapsToDeserializedGlobals(
- Handle<GlobalObject> inner_global_outside_snapshot,
- Handle<GlobalObject> inner_global_from_snapshot) {
- Handle<Map> from_map(inner_global_outside_snapshot->map());
-#ifdef DEBUG
- Handle<Map> to_map(inner_global_from_snapshot->map());
- ASSERT_EQ(to_map->instance_size(), from_map->instance_size());
- ASSERT_EQ(0, to_map->inobject_properties());
- ASSERT_EQ(0, from_map->inobject_properties());
-#endif
- inner_global_from_snapshot->set_map(*from_map);
+bool Genesis::InstallJSBuiltins(Handle<JSBuiltinsObject> builtins) {
+ HandleScope scope;
+ for (int i = 0; i < Builtins::NumberOfJavaScriptBuiltins(); i++) {
+ Builtins::JavaScript id = static_cast<Builtins::JavaScript>(i);
+ Handle<String> name = Factory::LookupAsciiSymbol(Builtins::GetName(id));
+ Handle<JSFunction> function
+ = Handle<JSFunction>(JSFunction::cast(builtins->GetProperty(*name)));
+ builtins->set_javascript_builtin(id, *function);
+ Handle<SharedFunctionInfo> shared
+ = Handle<SharedFunctionInfo>(function->shared());
+ if (!EnsureCompiled(shared, CLEAR_EXCEPTION)) return false;
+ }
+ return true;
}
@@ -1415,7 +1325,7 @@ bool Genesis::ConfigureGlobalObjects(
v8::Handle<v8::ObjectTemplate> global_proxy_template) {
Handle<JSObject> global_proxy(
JSObject::cast(global_context()->global_proxy()));
- Handle<JSObject> js_global(JSObject::cast(global_context()->global()));
+ Handle<JSObject> inner_global(JSObject::cast(global_context()->global()));
if (!global_proxy_template.IsEmpty()) {
// Configure the global proxy object.
@@ -1429,11 +1339,11 @@ bool Genesis::ConfigureGlobalObjects(
if (!proxy_constructor->prototype_template()->IsUndefined()) {
Handle<ObjectTemplateInfo> inner_data(
ObjectTemplateInfo::cast(proxy_constructor->prototype_template()));
- if (!ConfigureApiObject(js_global, inner_data)) return false;
+ if (!ConfigureApiObject(inner_global, inner_data)) return false;
}
}
- SetObjectPrototype(global_proxy, js_global);
+ SetObjectPrototype(global_proxy, inner_global);
return true;
}
@@ -1485,7 +1395,7 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from,
LookupResult result;
to->LocalLookup(descs->GetKey(i), &result);
// If the property is already there we skip it
- if (result.IsValid()) continue;
+ if (result.IsProperty()) continue;
HandleScope inner;
ASSERT(!to->HasFastProperties());
// Add to dictionary.
@@ -1520,7 +1430,7 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from,
// If the property is already there we skip it.
LookupResult result;
to->LocalLookup(String::cast(raw_key), &result);
- if (result.IsValid()) continue;
+ if (result.IsProperty()) continue;
// Set the property.
Handle<String> key = Handle<String>(String::cast(raw_key));
Handle<Object> value = Handle<Object>(properties->ValueAt(i));
@@ -1604,25 +1514,33 @@ void Genesis::AddSpecialFunction(Handle<JSObject> prototype,
void Genesis::BuildSpecialFunctionTable() {
HandleScope scope;
Handle<JSObject> global = Handle<JSObject>(global_context()->global());
- // Add special versions for Array.prototype.pop and push.
+ // Add special versions for some Array.prototype functions.
Handle<JSFunction> function =
Handle<JSFunction>(
JSFunction::cast(global->GetProperty(Heap::Array_symbol())));
Handle<JSObject> visible_prototype =
Handle<JSObject>(JSObject::cast(function->prototype()));
- // Remember to put push and pop on the hidden prototype if it's there.
- Handle<JSObject> push_and_pop_prototype;
+ // Remember to put those specializations on the hidden prototype if present.
+ Handle<JSObject> special_prototype;
Handle<Object> superproto(visible_prototype->GetPrototype());
if (superproto->IsJSObject() &&
JSObject::cast(*superproto)->map()->is_hidden_prototype()) {
- push_and_pop_prototype = Handle<JSObject>::cast(superproto);
+ special_prototype = Handle<JSObject>::cast(superproto);
} else {
- push_and_pop_prototype = visible_prototype;
+ special_prototype = visible_prototype;
}
- AddSpecialFunction(push_and_pop_prototype, "pop",
+ AddSpecialFunction(special_prototype, "pop",
Handle<Code>(Builtins::builtin(Builtins::ArrayPop)));
- AddSpecialFunction(push_and_pop_prototype, "push",
+ AddSpecialFunction(special_prototype, "push",
Handle<Code>(Builtins::builtin(Builtins::ArrayPush)));
+ AddSpecialFunction(special_prototype, "shift",
+ Handle<Code>(Builtins::builtin(Builtins::ArrayShift)));
+ AddSpecialFunction(special_prototype, "unshift",
+ Handle<Code>(Builtins::builtin(Builtins::ArrayUnshift)));
+ AddSpecialFunction(special_prototype, "slice",
+ Handle<Code>(Builtins::builtin(Builtins::ArraySlice)));
+ AddSpecialFunction(special_prototype, "splice",
+ Handle<Code>(Builtins::builtin(Builtins::ArraySplice)));
}
@@ -1648,19 +1566,15 @@ Genesis::Genesis(Handle<Object> global_object,
JSFunction* empty_function =
JSFunction::cast(result_->function_map()->prototype());
empty_function_ = Handle<JSFunction>(empty_function);
- Handle<GlobalObject> inner_global_outside_snapshot;
+ Handle<GlobalObject> inner_global;
Handle<JSGlobalProxy> global_proxy =
CreateNewGlobals(global_template,
global_object,
- &inner_global_outside_snapshot);
- // CreateNewGlobals returns an inner global that it just made, but
- // we won't give that to HookUpGlobalProxy because we want to hook
- // up the global proxy to the one from the snapshot.
- Handle<GlobalObject> inner_global(
- GlobalObject::cast(global_context_->extension()));
+ &inner_global);
+
HookUpGlobalProxy(inner_global, global_proxy);
- TransferMapsToDeserializedGlobals(inner_global_outside_snapshot,
- inner_global);
+ HookUpInnerGlobal(inner_global);
+
if (!ConfigureGlobalObjects(global_template)) return;
} else {
// We get here if there was no context snapshot.
diff --git a/src/bootstrapper.h b/src/bootstrapper.h
index 19fc39aa..f905a28b 100644
--- a/src/bootstrapper.h
+++ b/src/bootstrapper.h
@@ -74,9 +74,6 @@ class Bootstrapper : public AllStatic {
// Accessors for the native scripts cache. Used in lazy loading.
static Handle<String> NativesSourceLookup(int index);
- // Append code that needs fixup at the end of boot strapping.
- static void AddFixup(Code* code, MacroAssembler* masm);
-
// Tells whether bootstrapping is active.
static bool IsActive() { return BootstrapperActive::IsActive(); }
diff --git a/src/builtins.cc b/src/builtins.cc
index db0770f3..ee98769b 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -168,28 +168,6 @@ static inline bool CalledAsConstructor() {
// ----------------------------------------------------------------------------
-Handle<Code> Builtins::GetCode(JavaScript id, bool* resolved) {
- Code* code = Builtins::builtin(Builtins::Illegal);
- *resolved = false;
-
- if (Top::context() != NULL) {
- Object* object = Top::builtins()->javascript_builtin(id);
- if (object->IsJSFunction()) {
- Handle<SharedFunctionInfo> shared(JSFunction::cast(object)->shared());
- // Make sure the number of parameters match the formal parameter count.
- ASSERT(shared->formal_parameter_count() ==
- Builtins::GetArgumentsCount(id));
- if (EnsureCompiled(shared, CLEAR_EXCEPTION)) {
- code = shared->code();
- *resolved = true;
- }
- }
- }
-
- return Handle<Code>(code);
-}
-
-
BUILTIN(Illegal) {
UNREACHABLE();
return Heap::undefined_value(); // Make compiler happy.
@@ -268,19 +246,19 @@ BUILTIN(ArrayPush) {
JSArray* array = JSArray::cast(*args.receiver());
ASSERT(array->HasFastElements());
- // Make sure we have space for the elements.
int len = Smi::cast(array->length())->value();
+ int to_add = args.length() - 1;
+ if (to_add == 0) {
+ return Smi::FromInt(len);
+ }
+ // Currently fixed arrays cannot grow too big, so
+ // we should never hit this case.
+ ASSERT(to_add <= (Smi::kMaxValue - len));
- // Set new length.
- int new_length = len + args.length() - 1;
+ int new_length = len + to_add;
FixedArray* elms = FixedArray::cast(array->elements());
- if (new_length <= elms->length()) {
- // Backing storage has extra space for the provided values.
- for (int index = 0; index < args.length() - 1; index++) {
- elms->set(index + len, args[index+1]);
- }
- } else {
+ if (new_length > elms->length()) {
// New backing storage is needed.
int capacity = new_length + (new_length >> 1) + 16;
Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
@@ -291,16 +269,21 @@ BUILTIN(ArrayPush) {
WriteBarrierMode mode = new_elms->GetWriteBarrierMode(no_gc);
// Fill out the new array with old elements.
for (int i = 0; i < len; i++) new_elms->set(i, elms->get(i), mode);
- // Add the provided values.
- for (int index = 0; index < args.length() - 1; index++) {
- new_elms->set(index + len, args[index+1], mode);
- }
- // Set the new backing storage.
- array->set_elements(new_elms);
+ elms = new_elms;
+ array->set_elements(elms);
+ }
+
+ AssertNoAllocation no_gc;
+ WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
+
+ // Add the provided values.
+ for (int index = 0; index < to_add; index++) {
+ elms->set(index + len, args[index + 1], mode);
}
+
// Set the length.
array->set_length(Smi::FromInt(new_length));
- return array->length();
+ return Smi::FromInt(new_length);
}
@@ -335,6 +318,355 @@ BUILTIN(ArrayPop) {
}
+static Object* GetElementToMove(uint32_t index,
+ FixedArray* elms,
+ JSObject* prototype) {
+ Object* e = elms->get(index);
+ if (e->IsTheHole() && prototype->HasElement(index)) {
+ e = prototype->GetElement(index);
+ }
+ return e;
+}
+
+
+BUILTIN(ArrayShift) {
+ JSArray* array = JSArray::cast(*args.receiver());
+ ASSERT(array->HasFastElements());
+
+ int len = Smi::cast(array->length())->value();
+ if (len == 0) return Heap::undefined_value();
+
+ // Fetch the prototype.
+ JSFunction* array_function =
+ Top::context()->global_context()->array_function();
+ JSObject* prototype = JSObject::cast(array_function->prototype());
+
+ FixedArray* elms = FixedArray::cast(array->elements());
+
+ // Get first element
+ Object* first = elms->get(0);
+ if (first->IsTheHole()) {
+ first = prototype->GetElement(0);
+ }
+
+ // Shift the elements.
+ for (int i = 0; i < len - 1; i++) {
+ elms->set(i, GetElementToMove(i + 1, elms, prototype));
+ }
+ elms->set(len - 1, Heap::the_hole_value());
+
+ // Set the length.
+ array->set_length(Smi::FromInt(len - 1));
+
+ return first;
+}
+
+
+BUILTIN(ArrayUnshift) {
+ JSArray* array = JSArray::cast(*args.receiver());
+ ASSERT(array->HasFastElements());
+
+ int len = Smi::cast(array->length())->value();
+ int to_add = args.length() - 1;
+ // Note that we cannot quit early if to_add == 0 as
+ // values should be lifted from prototype into
+ // the array.
+
+ int new_length = len + to_add;
+ // Currently fixed arrays cannot grow too big, so
+ // we should never hit this case.
+ ASSERT(to_add <= (Smi::kMaxValue - len));
+
+ FixedArray* elms = FixedArray::cast(array->elements());
+
+ // Fetch the prototype.
+ JSFunction* array_function =
+ Top::context()->global_context()->array_function();
+ JSObject* prototype = JSObject::cast(array_function->prototype());
+
+ if (new_length > elms->length()) {
+ // New backing storage is needed.
+ int capacity = new_length + (new_length >> 1) + 16;
+ Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
+ if (obj->IsFailure()) return obj;
+
+ AssertNoAllocation no_gc;
+ FixedArray* new_elms = FixedArray::cast(obj);
+ WriteBarrierMode mode = new_elms->GetWriteBarrierMode(no_gc);
+ // Fill out the new array with old elements.
+ for (int i = 0; i < len; i++)
+ new_elms->set(to_add + i,
+ GetElementToMove(i, elms, prototype),
+ mode);
+
+ elms = new_elms;
+ array->set_elements(elms);
+ } else {
+ AssertNoAllocation no_gc;
+ WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
+
+ // Move elements to the right
+ for (int i = 0; i < len; i++) {
+ elms->set(new_length - i - 1,
+ GetElementToMove(len - i - 1, elms, prototype),
+ mode);
+ }
+ }
+
+ // Add the provided values.
+ AssertNoAllocation no_gc;
+ WriteBarrierMode mode = elms->GetWriteBarrierMode(no_gc);
+ for (int i = 0; i < to_add; i++) {
+ elms->set(i, args[i + 1], mode);
+ }
+
+ // Set the length.
+ array->set_length(Smi::FromInt(new_length));
+ return Smi::FromInt(new_length);
+}
+
+
+static Object* CallJsBuiltin(const char* name,
+ BuiltinArguments<NO_EXTRA_ARGUMENTS> args) {
+ HandleScope handleScope;
+
+ Handle<Object> js_builtin =
+ GetProperty(Handle<JSObject>(Top::global_context()->builtins()),
+ name);
+ ASSERT(js_builtin->IsJSFunction());
+ Handle<JSFunction> function(Handle<JSFunction>::cast(js_builtin));
+ Vector<Object**> argv(Vector<Object**>::New(args.length() - 1));
+ int n_args = args.length() - 1;
+ for (int i = 0; i < n_args; i++) {
+ argv[i] = &args[i + 1];
+ }
+ bool pending_exception = false;
+ Handle<Object> result = Execution::Call(function,
+ args.receiver(),
+ n_args,
+ argv.start(),
+ &pending_exception);
+ if (pending_exception) return Failure::Exception();
+ return *result;
+}
+
+
+BUILTIN(ArraySlice) {
+ JSArray* array = JSArray::cast(*args.receiver());
+ ASSERT(array->HasFastElements());
+
+ int len = Smi::cast(array->length())->value();
+
+ int n_arguments = args.length() - 1;
+
+ // Note carefully choosen defaults---if argument is missing,
+ // it's undefined which gets converted to 0 for relativeStart
+ // and to len for relativeEnd.
+ int relativeStart = 0;
+ int relativeEnd = len;
+ if (n_arguments > 0) {
+ Object* arg1 = args[1];
+ if (arg1->IsSmi()) {
+ relativeStart = Smi::cast(arg1)->value();
+ } else if (!arg1->IsUndefined()) {
+ return CallJsBuiltin("ArraySlice", args);
+ }
+ if (n_arguments > 1) {
+ Object* arg2 = args[2];
+ if (arg2->IsSmi()) {
+ relativeEnd = Smi::cast(arg2)->value();
+ } else if (!arg2->IsUndefined()) {
+ return CallJsBuiltin("ArraySlice", args);
+ }
+ }
+ }
+
+ // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 6.
+ int k = (relativeStart < 0) ? Max(len + relativeStart, 0)
+ : Min(relativeStart, len);
+
+ // ECMAScript 232, 3rd Edition, Section 15.4.4.10, step 8.
+ int final = (relativeEnd < 0) ? Max(len + relativeEnd, 0)
+ : Min(relativeEnd, len);
+
+ // Calculate the length of result array.
+ int result_len = final - k;
+ if (result_len < 0) {
+ result_len = 0;
+ }
+
+ JSFunction* array_function =
+ Top::context()->global_context()->array_function();
+ Object* result = Heap::AllocateJSObject(array_function);
+ if (result->IsFailure()) return result;
+ JSArray* result_array = JSArray::cast(result);
+
+ result = Heap::AllocateFixedArrayWithHoles(result_len);
+ if (result->IsFailure()) return result;
+ FixedArray* result_elms = FixedArray::cast(result);
+
+ FixedArray* elms = FixedArray::cast(array->elements());
+
+ // Fetch the prototype.
+ JSObject* prototype = JSObject::cast(array_function->prototype());
+
+ AssertNoAllocation no_gc;
+ WriteBarrierMode mode = result_elms->GetWriteBarrierMode(no_gc);
+
+ // Fill newly created array.
+ for (int i = 0; i < result_len; i++) {
+ result_elms->set(i,
+ GetElementToMove(k + i, elms, prototype),
+ mode);
+ }
+
+ // Set elements.
+ result_array->set_elements(result_elms);
+
+ // Set the length.
+ result_array->set_length(Smi::FromInt(result_len));
+ return result_array;
+}
+
+
+BUILTIN(ArraySplice) {
+ JSArray* array = JSArray::cast(*args.receiver());
+ ASSERT(array->HasFastElements());
+
+ int len = Smi::cast(array->length())->value();
+
+ int n_arguments = args.length() - 1;
+
+ // SpiderMonkey and JSC return undefined in the case where no
+ // arguments are given instead of using the implicit undefined
+ // arguments. This does not follow ECMA-262, but we do the same for
+ // compatibility.
+ // TraceMonkey follows ECMA-262 though.
+ if (n_arguments == 0) {
+ return Heap::undefined_value();
+ }
+
+ int relativeStart = 0;
+ Object* arg1 = args[1];
+ if (arg1->IsSmi()) {
+ relativeStart = Smi::cast(arg1)->value();
+ } else if (!arg1->IsUndefined()) {
+ return CallJsBuiltin("ArraySplice", args);
+ }
+ int actualStart = (relativeStart < 0) ? Max(len + relativeStart, 0)
+ : Min(relativeStart, len);
+
+ // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
+ // given differently from when an undefined delete count is given.
+ // This does not follow ECMA-262, but we do the same for
+ // compatibility.
+ int deleteCount = len;
+ if (n_arguments > 1) {
+ Object* arg2 = args[2];
+ if (arg2->IsSmi()) {
+ deleteCount = Smi::cast(arg2)->value();
+ } else {
+ return CallJsBuiltin("ArraySplice", args);
+ }
+ }
+ int actualDeleteCount = Min(Max(deleteCount, 0), len - actualStart);
+
+ JSFunction* array_function =
+ Top::context()->global_context()->array_function();
+
+ // Allocate result array.
+ Object* result = Heap::AllocateJSObject(array_function);
+ if (result->IsFailure()) return result;
+ JSArray* result_array = JSArray::cast(result);
+
+ result = Heap::AllocateFixedArrayWithHoles(actualDeleteCount);
+ if (result->IsFailure()) return result;
+ FixedArray* result_elms = FixedArray::cast(result);
+
+ FixedArray* elms = FixedArray::cast(array->elements());
+
+ // Fetch the prototype.
+ JSObject* prototype = JSObject::cast(array_function->prototype());
+
+ AssertNoAllocation no_gc;
+ WriteBarrierMode mode = result_elms->GetWriteBarrierMode(no_gc);
+
+ // Fill newly created array.
+ for (int k = 0; k < actualDeleteCount; k++) {
+ result_elms->set(k,
+ GetElementToMove(actualStart + k, elms, prototype),
+ mode);
+ }
+
+ // Set elements.
+ result_array->set_elements(result_elms);
+
+ // Set the length.
+ result_array->set_length(Smi::FromInt(actualDeleteCount));
+
+ int itemCount = (n_arguments > 1) ? (n_arguments - 2) : 0;
+
+ int new_length = len - actualDeleteCount + itemCount;
+
+ mode = elms->GetWriteBarrierMode(no_gc);
+ if (itemCount < actualDeleteCount) {
+ // Shrink the array.
+ for (int k = actualStart; k < (len - actualDeleteCount); k++) {
+ elms->set(k + itemCount,
+ GetElementToMove(k + actualDeleteCount, elms, prototype),
+ mode);
+ }
+
+ for (int k = len; k > new_length; k--) {
+ elms->set(k - 1, Heap::the_hole_value());
+ }
+ } else if (itemCount > actualDeleteCount) {
+ // Currently fixed arrays cannot grow too big, so
+ // we should never hit this case.
+ ASSERT((itemCount - actualDeleteCount) <= (Smi::kMaxValue - len));
+
+ FixedArray* source_elms = elms;
+
+ // Check if array need to grow.
+ if (new_length > elms->length()) {
+ // New backing storage is needed.
+ int capacity = new_length + (new_length >> 1) + 16;
+ Object* obj = Heap::AllocateFixedArrayWithHoles(capacity);
+ if (obj->IsFailure()) return obj;
+
+ FixedArray* new_elms = FixedArray::cast(obj);
+ mode = new_elms->GetWriteBarrierMode(no_gc);
+
+ // Copy the part before actualStart as is.
+ for (int k = 0; k < actualStart; k++) {
+ new_elms->set(k, elms->get(k), mode);
+ }
+
+ source_elms = elms;
+ elms = new_elms;
+ array->set_elements(elms);
+ }
+
+ for (int k = len - actualDeleteCount; k > actualStart; k--) {
+ elms->set(k + itemCount - 1,
+ GetElementToMove(k + actualDeleteCount - 1,
+ source_elms,
+ prototype),
+ mode);
+ }
+ }
+
+ for (int k = actualStart; k < actualStart + itemCount; k++) {
+ elms->set(k, args[3 + k - actualStart], mode);
+ }
+
+ // Set the length.
+ array->set_length(Smi::FromInt(new_length));
+
+ return result_array;
+}
+
+
// -----------------------------------------------------------------------------
//
@@ -474,6 +806,76 @@ BUILTIN(HandleApiCallConstruct) {
}
+#ifdef DEBUG
+
+static void VerifyTypeCheck(Handle<JSObject> object,
+ Handle<JSFunction> function) {
+ FunctionTemplateInfo* info =
+ FunctionTemplateInfo::cast(function->shared()->function_data());
+ if (info->signature()->IsUndefined()) return;
+ SignatureInfo* signature = SignatureInfo::cast(info->signature());
+ Object* receiver_type = signature->receiver();
+ if (receiver_type->IsUndefined()) return;
+ FunctionTemplateInfo* type = FunctionTemplateInfo::cast(receiver_type);
+ ASSERT(object->IsInstanceOf(type));
+}
+
+#endif
+
+
+BUILTIN(FastHandleApiCall) {
+ ASSERT(!CalledAsConstructor());
+ const bool is_construct = false;
+
+ // We expect four more arguments: function, callback, call data, and holder.
+ const int args_length = args.length() - 4;
+ ASSERT(args_length >= 0);
+
+ Handle<JSFunction> function = args.at<JSFunction>(args_length);
+ Object* callback_obj = args[args_length + 1];
+ Handle<Object> data_handle = args.at<Object>(args_length + 2);
+ Handle<JSObject> checked_holder = args.at<JSObject>(args_length + 3);
+
+#ifdef DEBUG
+ VerifyTypeCheck(checked_holder, function);
+#endif
+
+ v8::Local<v8::Object> holder = v8::Utils::ToLocal(checked_holder);
+ v8::Local<v8::Function> callee = v8::Utils::ToLocal(function);
+ v8::InvocationCallback callback =
+ v8::ToCData<v8::InvocationCallback>(callback_obj);
+ v8::Local<v8::Value> data = v8::Utils::ToLocal(data_handle);
+
+ v8::Arguments new_args = v8::ImplementationUtilities::NewArguments(
+ data,
+ holder,
+ callee,
+ is_construct,
+ reinterpret_cast<void**>(&args[0] - 1),
+ args_length - 1);
+
+ HandleScope scope;
+ Object* result;
+ v8::Handle<v8::Value> value;
+ {
+ // Leaving JavaScript.
+ VMState state(EXTERNAL);
+#ifdef ENABLE_LOGGING_AND_PROFILING
+ state.set_external_callback(v8::ToCData<Address>(callback_obj));
+#endif
+ value = callback(new_args);
+ }
+ if (value.IsEmpty()) {
+ result = Heap::undefined_value();
+ } else {
+ result = *reinterpret_cast<Object**>(*value);
+ }
+
+ RETURN_IF_SCHEDULED_EXCEPTION();
+ return result;
+}
+
+
// Helper function to handle calls to non-function objects created through the
// API. The object can be called as either a constructor (using new) or just as
// a function (without new).
@@ -657,6 +1059,10 @@ static void Generate_KeyedLoadIC_PreMonomorphic(MacroAssembler* masm) {
KeyedLoadIC::GeneratePreMonomorphic(masm);
}
+static void Generate_KeyedLoadIC_IndexedInterceptor(MacroAssembler* masm) {
+ KeyedLoadIC::GenerateIndexedInterceptor(masm);
+}
+
static void Generate_StoreIC_Initialize(MacroAssembler* masm) {
StoreIC::GenerateInitialize(masm);
@@ -668,10 +1074,6 @@ static void Generate_StoreIC_Miss(MacroAssembler* masm) {
}
-static void Generate_StoreIC_ExtendStorage(MacroAssembler* masm) {
- StoreIC::GenerateExtendStorage(masm);
-}
-
static void Generate_StoreIC_Megamorphic(MacroAssembler* masm) {
StoreIC::GenerateMegamorphic(masm);
}
@@ -720,11 +1122,6 @@ static void Generate_KeyedStoreIC_ExternalFloatArray(MacroAssembler* masm) {
}
-static void Generate_KeyedStoreIC_ExtendStorage(MacroAssembler* masm) {
- KeyedStoreIC::GenerateExtendStorage(masm);
-}
-
-
static void Generate_KeyedStoreIC_Miss(MacroAssembler* masm) {
KeyedStoreIC::GenerateMiss(masm);
}
@@ -869,9 +1266,6 @@ void Builtins::Setup(bool create_heap_objects) {
v8::internal::V8::FatalProcessOutOfMemory("CreateCode");
}
}
- // Add any unresolved jumps or calls to the fixup list in the
- // bootstrapper.
- Bootstrapper::AddFixup(Code::cast(code), &masm);
// Log the event and add the code to the builtins array.
LOG(CodeCreateEvent(Logger::BUILTIN_TAG,
Code::cast(code), functions[i].s_name));
diff --git a/src/builtins.h b/src/builtins.h
index 418948f7..13322c23 100644
--- a/src/builtins.h
+++ b/src/builtins.h
@@ -48,8 +48,13 @@ enum BuiltinExtraArguments {
\
V(ArrayPush, NO_EXTRA_ARGUMENTS) \
V(ArrayPop, NO_EXTRA_ARGUMENTS) \
+ V(ArrayShift, NO_EXTRA_ARGUMENTS) \
+ V(ArrayUnshift, NO_EXTRA_ARGUMENTS) \
+ V(ArraySlice, NO_EXTRA_ARGUMENTS) \
+ V(ArraySplice, NO_EXTRA_ARGUMENTS) \
\
V(HandleApiCall, NEEDS_CALLED_FUNCTION) \
+ V(FastHandleApiCall, NO_EXTRA_ARGUMENTS) \
V(HandleApiCallConstruct, NEEDS_CALLED_FUNCTION) \
V(HandleApiCallAsFunction, NO_EXTRA_ARGUMENTS) \
V(HandleApiCallAsConstructor, NO_EXTRA_ARGUMENTS)
@@ -69,9 +74,6 @@ enum BuiltinExtraArguments {
V(StoreIC_Miss, BUILTIN, UNINITIALIZED) \
V(KeyedStoreIC_Miss, BUILTIN, UNINITIALIZED) \
\
- V(StoreIC_ExtendStorage, BUILTIN, UNINITIALIZED) \
- V(KeyedStoreIC_ExtendStorage, BUILTIN, UNINITIALIZED) \
- \
V(LoadIC_Initialize, LOAD_IC, UNINITIALIZED) \
V(LoadIC_PreMonomorphic, LOAD_IC, PREMONOMORPHIC) \
V(LoadIC_Normal, LOAD_IC, MONOMORPHIC) \
@@ -91,6 +93,7 @@ enum BuiltinExtraArguments {
V(KeyedLoadIC_ExternalIntArray, KEYED_LOAD_IC, MEGAMORPHIC) \
V(KeyedLoadIC_ExternalUnsignedIntArray, KEYED_LOAD_IC, MEGAMORPHIC) \
V(KeyedLoadIC_ExternalFloatArray, KEYED_LOAD_IC, MEGAMORPHIC) \
+ V(KeyedLoadIC_IndexedInterceptor, KEYED_LOAD_IC, MEGAMORPHIC) \
\
V(StoreIC_Initialize, STORE_IC, UNINITIALIZED) \
V(StoreIC_Megamorphic, STORE_IC, MEGAMORPHIC) \
diff --git a/src/checks.h b/src/checks.h
index 3b0c8513..eeb748b4 100644
--- a/src/checks.h
+++ b/src/checks.h
@@ -125,7 +125,9 @@ static inline void CheckEqualsHelper(const char* file,
const char* expected,
const char* value_source,
const char* value) {
- if (strcmp(expected, value) != 0) {
+ if ((expected == NULL && value != NULL) ||
+ (expected != NULL && value == NULL) ||
+ (expected != NULL && value != NULL && strcmp(expected, value) != 0)) {
V8_Fatal(file, line,
"CHECK_EQ(%s, %s) failed\n# Expected: %s\n# Found: %s",
expected_source, value_source, expected, value);
diff --git a/src/code-stubs.cc b/src/code-stubs.cc
index 95f0760a..4d0fd299 100644
--- a/src/code-stubs.cc
+++ b/src/code-stubs.cc
@@ -31,6 +31,7 @@
#include "code-stubs.h"
#include "factory.h"
#include "macro-assembler.h"
+#include "oprofile-agent.h"
namespace v8 {
namespace internal {
@@ -60,8 +61,12 @@ void CodeStub::GenerateCode(MacroAssembler* masm) {
void CodeStub::RecordCodeGeneration(Code* code, MacroAssembler* masm) {
code->set_major_key(MajorKey());
- // Add unresolved entries in the code to the fixup list.
- Bootstrapper::AddFixup(code, masm);
+#ifdef ENABLE_OPROFILE_AGENT
+ // Register the generated stub with the OPROFILE agent.
+ OProfileAgent::CreateNativeCodeRegion(GetName(),
+ code->instruction_start(),
+ code->instruction_size());
+#endif
LOG(CodeCreateEvent(Logger::STUB_TAG, code, GetName()));
Counters::total_stubs_code_size.Increment(code->instruction_size());
diff --git a/src/code-stubs.h b/src/code-stubs.h
index d502f14c..d5189c27 100644
--- a/src/code-stubs.h
+++ b/src/code-stubs.h
@@ -48,6 +48,7 @@ namespace internal {
V(FastNewClosure) \
V(FastNewContext) \
V(FastCloneShallowArray) \
+ V(TranscendentalCache) \
V(GenericUnaryOp) \
V(RevertToNumber) \
V(ToBoolean) \
@@ -55,6 +56,7 @@ namespace internal {
V(CounterOp) \
V(ArgumentsAccess) \
V(RegExpExec) \
+ V(NumberToString) \
V(CEntry) \
V(JSEntry) \
V(DebuggerStatement)
diff --git a/src/codegen.cc b/src/codegen.cc
index 24eb4762..bc722bbe 100644
--- a/src/codegen.cc
+++ b/src/codegen.cc
@@ -31,6 +31,7 @@
#include "codegen-inl.h"
#include "compiler.h"
#include "debug.h"
+#include "liveedit.h"
#include "oprofile-agent.h"
#include "prettyprinter.h"
#include "register-allocator-inl.h"
@@ -197,9 +198,6 @@ Handle<Code> CodeGenerator::MakeCodeEpilogue(MacroAssembler* masm,
Handle<Code> code =
Factory::NewCode(desc, &sinfo, flags, masm->CodeObject());
- // Add unresolved entries in the code to the fixup list.
- Bootstrapper::AddFixup(*code, masm);
-
#ifdef ENABLE_DISASSEMBLER
bool print_code = Bootstrapper::IsActive()
? FLAG_print_builtin_code
@@ -237,6 +235,7 @@ Handle<Code> CodeGenerator::MakeCodeEpilogue(MacroAssembler* masm,
// all the pieces into a Code object. This function is only to be called by
// the compiler.cc code.
Handle<Code> CodeGenerator::MakeCode(CompilationInfo* info) {
+ LiveEditFunctionTracker live_edit_tracker(info->function());
Handle<Script> script = info->script();
if (!script->IsUndefined() && !script->source()->IsUndefined()) {
int len = String::cast(script->source())->length();
@@ -248,7 +247,8 @@ Handle<Code> CodeGenerator::MakeCode(CompilationInfo* info) {
MacroAssembler masm(NULL, kInitialBufferSize);
CodeGenerator cgen(&masm);
CodeGeneratorScope scope(&cgen);
- cgen.Generate(info, PRIMARY);
+ live_edit_tracker.RecordFunctionScope(info->function()->scope());
+ cgen.Generate(info);
if (cgen.HasStackOverflow()) {
ASSERT(!Top::has_pending_exception());
return Handle<Code>::null();
@@ -256,7 +256,9 @@ Handle<Code> CodeGenerator::MakeCode(CompilationInfo* info) {
InLoopFlag in_loop = (cgen.loop_nesting() != 0) ? IN_LOOP : NOT_IN_LOOP;
Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, in_loop);
- return MakeCodeEpilogue(cgen.masm(), flags, info);
+ Handle<Code> result = MakeCodeEpilogue(cgen.masm(), flags, info);
+ live_edit_tracker.RecordFunctionCode(result);
+ return result;
}
@@ -358,6 +360,7 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
{&CodeGenerator::GenerateIsSmi, "_IsSmi"},
{&CodeGenerator::GenerateIsNonNegativeSmi, "_IsNonNegativeSmi"},
{&CodeGenerator::GenerateIsArray, "_IsArray"},
+ {&CodeGenerator::GenerateIsRegExp, "_IsRegExp"},
{&CodeGenerator::GenerateIsConstructCall, "_IsConstructCall"},
{&CodeGenerator::GenerateArgumentsLength, "_ArgumentsLength"},
{&CodeGenerator::GenerateArgumentsAccess, "_Arguments"},
@@ -375,6 +378,9 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
{&CodeGenerator::GenerateSubString, "_SubString"},
{&CodeGenerator::GenerateStringCompare, "_StringCompare"},
{&CodeGenerator::GenerateRegExpExec, "_RegExpExec"},
+ {&CodeGenerator::GenerateNumberToString, "_NumberToString"},
+ {&CodeGenerator::GenerateMathSin, "_Math_sin"},
+ {&CodeGenerator::GenerateMathCos, "_Math_cos"},
};
@@ -525,12 +531,5 @@ void ApiGetterEntryStub::SetCustomCache(Code* value) {
info()->set_load_stub_cache(value);
}
-#ifdef ENABLE_DEBUGGER_SUPPORT
-void DebuggerStatementStub::Generate(MacroAssembler* masm) {
- Runtime::Function* f = Runtime::FunctionForId(Runtime::kDebugBreak);
- masm->TailCallRuntime(ExternalReference(f), 0, f->result_size);
-}
-#endif
-
} } // namespace v8::internal
diff --git a/src/codegen.h b/src/codegen.h
index 3afa0414..8dcde84b 100644
--- a/src/codegen.h
+++ b/src/codegen.h
@@ -31,6 +31,7 @@
#include "ast.h"
#include "code-stubs.h"
#include "runtime.h"
+#include "number-info.h"
// Include the declaration of the architecture defined class CodeGenerator.
// The contract to the shared code is that the the CodeGenerator is a subclass
@@ -415,21 +416,6 @@ class ApiGetterEntryStub : public CodeStub {
};
-// Mark the debugger statement to be recognized by debugger (by the MajorKey)
-class DebuggerStatementStub : public CodeStub {
- public:
- DebuggerStatementStub() { }
-
- void Generate(MacroAssembler* masm);
-
- private:
- Major MajorKey() { return DebuggerStatement; }
- int MinorKey() { return 0; }
-
- const char* GetName() { return "DebuggerStatementStub"; }
-};
-
-
class JSEntryStub : public CodeStub {
public:
JSEntryStub() { }
@@ -532,14 +518,14 @@ class CallFunctionStub: public CodeStub {
}
#endif
- // Minor key encoding in 31 bits AAAAAAAAAAAAAAAAAAAAAFI A(rgs)F(lag)I(nloop).
+ // Minor key encoding in 32 bits with Bitfield <Type, shift, size>.
class InLoopBits: public BitField<InLoopFlag, 0, 1> {};
class FlagBits: public BitField<CallFunctionFlags, 1, 1> {};
- class ArgcBits: public BitField<int, 2, 29> {};
+ class ArgcBits: public BitField<int, 2, 32 - 2> {};
Major MajorKey() { return CallFunction; }
int MinorKey() {
- // Encode the parameters in a unique 31 bit value.
+ // Encode the parameters in a unique 32 bit value.
return InLoopBits::encode(in_loop_)
| FlagBits::encode(flags_)
| ArgcBits::encode(argc_);
diff --git a/src/compiler.cc b/src/compiler.cc
index 6556d374..7b6734a3 100755
--- a/src/compiler.cc
+++ b/src/compiler.cc
@@ -38,6 +38,7 @@
#include "rewriter.h"
#include "scopes.h"
#include "usage-analyzer.h"
+#include "liveedit.h"
namespace v8 {
namespace internal {
@@ -238,6 +239,7 @@ Handle<JSFunction> Compiler::Compile(Handle<String> source,
int line_offset, int column_offset,
v8::Extension* extension,
ScriptDataImpl* input_pre_data,
+ Handle<Object> script_data,
NativesFlag natives) {
int source_length = source->length();
Counters::total_load_size.Increment(source_length);
@@ -275,6 +277,9 @@ Handle<JSFunction> Compiler::Compile(Handle<String> source,
script->set_column_offset(Smi::FromInt(column_offset));
}
+ script->set_data(script_data.is_null() ? Heap::undefined_value()
+ : *script_data);
+
// Compile the function and add it to the cache.
result = MakeFunction(true,
false,
@@ -429,7 +434,8 @@ Handle<JSFunction> Compiler::BuildBoilerplate(FunctionLiteral* literal,
// compiled. These builtins cannot be handled lazily by the parser,
// since we have to know if a function uses the special natives
// syntax, which is something the parser records.
- bool allow_lazy = literal->AllowsLazyCompilation();
+ bool allow_lazy = literal->AllowsLazyCompilation() &&
+ !LiveEditFunctionTracker::IsActive();
// Generate code
Handle<Code> code;
diff --git a/src/compiler.h b/src/compiler.h
index 88f4479a..f972ac9b 100644
--- a/src/compiler.h
+++ b/src/compiler.h
@@ -28,8 +28,10 @@
#ifndef V8_COMPILER_H_
#define V8_COMPILER_H_
+#include "ast.h"
#include "frame-element.h"
#include "parser.h"
+#include "register-allocator.h"
#include "zone.h"
namespace v8 {
@@ -39,6 +41,37 @@ namespace internal {
// is constructed based on the resources available at compile-time.
class CompilationInfo BASE_EMBEDDED {
public:
+ // Compilation mode. Either the compiler is used as the primary
+ // compiler and needs to setup everything or the compiler is used as
+ // the secondary compiler for split compilation and has to handle
+ // bailouts.
+ enum Mode {
+ PRIMARY,
+ SECONDARY
+ };
+
+ // A description of the compilation state at a bailout to the secondary
+ // code generator.
+ //
+ // The state is currently simple: there are no parameters or local
+ // variables to worry about ('this' can be found in the stack frame).
+ // There are at most two live values.
+ //
+ // There is a label that should be bound to the beginning of the bailout
+ // stub code.
+ class Bailout : public ZoneObject {
+ public:
+ Bailout(Register left, Register right) : left_(left), right_(right) {}
+
+ Label* label() { return &label_; }
+
+ private:
+ Register left_;
+ Register right_;
+ Label label_;
+ };
+
+
// Lazy compilation of a JSFunction.
CompilationInfo(Handle<JSFunction> closure,
int loop_nesting,
@@ -115,9 +148,13 @@ class CompilationInfo BASE_EMBEDDED {
int loop_nesting() { return loop_nesting_; }
bool has_receiver() { return !receiver_.is_null(); }
Handle<Object> receiver() { return receiver_; }
+ List<Bailout*>* bailouts() { return &bailouts_; }
- // Accessors for mutable fields, possibly set by analysis passes with
+ // Accessors for mutable fields (possibly set by analysis passes) with
// default values given by Initialize.
+ Mode mode() { return mode_; }
+ void set_mode(Mode mode) { mode_ = mode; }
+
bool has_this_properties() { return has_this_properties_; }
void set_has_this_properties(bool flag) { has_this_properties_ = flag; }
@@ -135,8 +172,19 @@ class CompilationInfo BASE_EMBEDDED {
// Derived accessors.
Scope* scope() { return function()->scope(); }
+ // Add a bailout with two live values.
+ Label* AddBailout(Register left, Register right) {
+ Bailout* bailout = new Bailout(left, right);
+ bailouts_.Add(bailout);
+ return bailout->label();
+ }
+
+ // Add a bailout with no live values.
+ Label* AddBailout() { return AddBailout(no_reg, no_reg); }
+
private:
void Initialize() {
+ mode_ = PRIMARY;
has_this_properties_ = false;
has_globals_ = false;
}
@@ -146,6 +194,7 @@ class CompilationInfo BASE_EMBEDDED {
Handle<Script> script_;
FunctionLiteral* function_;
+ Mode mode_;
bool is_eval_;
int loop_nesting_;
@@ -155,6 +204,10 @@ class CompilationInfo BASE_EMBEDDED {
bool has_this_properties_;
bool has_globals_;
+ // An ordered list of bailout points encountered during fast-path
+ // compilation.
+ List<Bailout*> bailouts_;
+
DISALLOW_COPY_AND_ASSIGN(CompilationInfo);
};
@@ -183,7 +236,8 @@ class Compiler : public AllStatic {
Handle<Object> script_name,
int line_offset, int column_offset,
v8::Extension* extension,
- ScriptDataImpl* script_Data,
+ ScriptDataImpl* pre_data,
+ Handle<Object> script_data,
NativesFlag is_natives_code);
// Compile a String source within a context for Eval.
diff --git a/src/contexts.h b/src/contexts.h
index 3feb6ce4..2453db7f 100644
--- a/src/contexts.h
+++ b/src/contexts.h
@@ -76,7 +76,6 @@ enum ContextLookupFlags {
V(TO_INTEGER_FUN_INDEX, JSFunction, to_integer_fun) \
V(TO_UINT32_FUN_INDEX, JSFunction, to_uint32_fun) \
V(TO_INT32_FUN_INDEX, JSFunction, to_int32_fun) \
- V(TO_BOOLEAN_FUN_INDEX, JSFunction, to_boolean_fun) \
V(GLOBAL_EVAL_FUN_INDEX, JSFunction, global_eval_fun) \
V(INSTANTIATE_FUN_INDEX, JSFunction, instantiate_fun) \
V(CONFIGURE_INSTANCE_FUN_INDEX, JSFunction, configure_instance_fun) \
diff --git a/src/d8-readline.cc b/src/d8-readline.cc
index 34b7b60d..67fc9eff 100644
--- a/src/d8-readline.cc
+++ b/src/d8-readline.cc
@@ -27,8 +27,8 @@
#include <cstdio> // NOLINT
-#include <readline/readline.h>
-#include <readline/history.h>
+#include <readline/readline.h> // NOLINT
+#include <readline/history.h> // NOLINT
#include "d8.h"
diff --git a/src/data-flow.cc b/src/data-flow.cc
index 22ec66fd..5e9d217d 100644
--- a/src/data-flow.cc
+++ b/src/data-flow.cc
@@ -199,15 +199,11 @@ void AstLabeler::VisitCatchExtensionObject(
void AstLabeler::VisitAssignment(Assignment* expr) {
Property* prop = expr->target()->AsProperty();
ASSERT(prop != NULL);
- if (prop != NULL) {
- ASSERT(prop->key()->IsPropertyName());
- VariableProxy* proxy = prop->obj()->AsVariableProxy();
- if (proxy != NULL && proxy->var()->is_this()) {
- info()->set_has_this_properties(true);
- } else {
- Visit(prop->obj());
- }
- }
+ ASSERT(prop->key()->IsPropertyName());
+ VariableProxy* proxy = prop->obj()->AsVariableProxy();
+ USE(proxy);
+ ASSERT(proxy != NULL && proxy->var()->is_this());
+ info()->set_has_this_properties(true);
Visit(expr->value());
expr->set_num(next_number_++);
}
@@ -219,7 +215,12 @@ void AstLabeler::VisitThrow(Throw* expr) {
void AstLabeler::VisitProperty(Property* expr) {
- UNREACHABLE();
+ ASSERT(expr->key()->IsPropertyName());
+ VariableProxy* proxy = expr->obj()->AsVariableProxy();
+ USE(proxy);
+ ASSERT(proxy != NULL && proxy->var()->is_this());
+ info()->set_has_this_properties(true);
+ expr->set_num(next_number_++);
}
@@ -269,4 +270,292 @@ void AstLabeler::VisitDeclaration(Declaration* decl) {
UNREACHABLE();
}
+
+ZoneList<Expression*>* VarUseMap::Lookup(Variable* var) {
+ HashMap::Entry* entry = HashMap::Lookup(var, var->name()->Hash(), true);
+ if (entry->value == NULL) {
+ entry->value = new ZoneList<Expression*>(1);
+ }
+ return reinterpret_cast<ZoneList<Expression*>*>(entry->value);
+}
+
+
+void LivenessAnalyzer::Analyze(FunctionLiteral* fun) {
+ // Process the function body.
+ VisitStatements(fun->body());
+
+ // All variables are implicitly defined at the function start.
+ // Record a definition of all variables live at function entry.
+ for (HashMap::Entry* p = live_vars_.Start();
+ p != NULL;
+ p = live_vars_.Next(p)) {
+ Variable* var = reinterpret_cast<Variable*>(p->key);
+ RecordDef(var, fun);
+ }
+}
+
+
+void LivenessAnalyzer::VisitStatements(ZoneList<Statement*>* stmts) {
+ // Visit statements right-to-left.
+ for (int i = stmts->length() - 1; i >= 0; i--) {
+ Visit(stmts->at(i));
+ }
+}
+
+
+void LivenessAnalyzer::RecordUse(Variable* var, Expression* expr) {
+ ASSERT(var->is_global() || var->is_this());
+ ZoneList<Expression*>* uses = live_vars_.Lookup(var);
+ uses->Add(expr);
+}
+
+
+void LivenessAnalyzer::RecordDef(Variable* var, Expression* expr) {
+ ASSERT(var->is_global() || var->is_this());
+
+ // We do not support other expressions that can define variables.
+ ASSERT(expr->AsFunctionLiteral() != NULL);
+
+ // Add the variable to the list of defined variables.
+ if (expr->defined_vars() == NULL) {
+ expr->set_defined_vars(new ZoneList<DefinitionInfo*>(1));
+ }
+ DefinitionInfo* def = new DefinitionInfo();
+ expr->AsFunctionLiteral()->defined_vars()->Add(def);
+
+ // Compute the last use of the definition. The variable uses are
+ // inserted in reversed evaluation order. The first element
+ // in the list of live uses is the last use.
+ ZoneList<Expression*>* uses = live_vars_.Lookup(var);
+ while (uses->length() > 0) {
+ Expression* use_site = uses->RemoveLast();
+ use_site->set_var_def(def);
+ if (uses->length() == 0) {
+ def->set_last_use(use_site);
+ }
+ }
+}
+
+
+// Visitor functions for live variable analysis.
+void LivenessAnalyzer::VisitDeclaration(Declaration* decl) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitBlock(Block* stmt) {
+ VisitStatements(stmt->statements());
+}
+
+
+void LivenessAnalyzer::VisitExpressionStatement(
+ ExpressionStatement* stmt) {
+ Visit(stmt->expression());
+}
+
+
+void LivenessAnalyzer::VisitEmptyStatement(EmptyStatement* stmt) {
+ // Do nothing.
+}
+
+
+void LivenessAnalyzer::VisitIfStatement(IfStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitContinueStatement(ContinueStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitBreakStatement(BreakStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitReturnStatement(ReturnStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitWithEnterStatement(
+ WithEnterStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitWithExitStatement(WithExitStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitSwitchStatement(SwitchStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitDoWhileStatement(DoWhileStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitWhileStatement(WhileStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitForStatement(ForStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitForInStatement(ForInStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitTryCatchStatement(TryCatchStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitTryFinallyStatement(
+ TryFinallyStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitDebuggerStatement(
+ DebuggerStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitFunctionLiteral(FunctionLiteral* expr) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitFunctionBoilerplateLiteral(
+ FunctionBoilerplateLiteral* expr) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitConditional(Conditional* expr) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitSlot(Slot* expr) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitVariableProxy(VariableProxy* expr) {
+ Variable* var = expr->var();
+ ASSERT(var->is_global());
+ ASSERT(!var->is_this());
+ RecordUse(var, expr);
+}
+
+
+void LivenessAnalyzer::VisitLiteral(Literal* expr) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitRegExpLiteral(RegExpLiteral* expr) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitObjectLiteral(ObjectLiteral* expr) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitArrayLiteral(ArrayLiteral* expr) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitCatchExtensionObject(
+ CatchExtensionObject* expr) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitAssignment(Assignment* expr) {
+ Property* prop = expr->target()->AsProperty();
+ ASSERT(prop != NULL);
+ ASSERT(prop->key()->IsPropertyName());
+ VariableProxy* proxy = prop->obj()->AsVariableProxy();
+ ASSERT(proxy != NULL && proxy->var()->is_this());
+
+ // Record use of this at the assignment node. Assignments to
+ // this-properties are treated like unary operations.
+ RecordUse(proxy->var(), expr);
+
+ // Visit right-hand side.
+ Visit(expr->value());
+}
+
+
+void LivenessAnalyzer::VisitThrow(Throw* expr) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitProperty(Property* expr) {
+ ASSERT(expr->key()->IsPropertyName());
+ VariableProxy* proxy = expr->obj()->AsVariableProxy();
+ ASSERT(proxy != NULL && proxy->var()->is_this());
+ RecordUse(proxy->var(), expr);
+}
+
+
+void LivenessAnalyzer::VisitCall(Call* expr) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitCallNew(CallNew* expr) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitCallRuntime(CallRuntime* expr) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitUnaryOperation(UnaryOperation* expr) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitCountOperation(CountOperation* expr) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitBinaryOperation(BinaryOperation* expr) {
+ // Visit child nodes in reverse evaluation order.
+ Visit(expr->right());
+ Visit(expr->left());
+}
+
+
+void LivenessAnalyzer::VisitCompareOperation(CompareOperation* expr) {
+ UNREACHABLE();
+}
+
+
+void LivenessAnalyzer::VisitThisFunction(ThisFunction* expr) {
+ UNREACHABLE();
+}
+
+
} } // namespace v8::internal
diff --git a/src/data-flow.h b/src/data-flow.h
index 7c16d5dc..23319444 100644
--- a/src/data-flow.h
+++ b/src/data-flow.h
@@ -62,6 +62,56 @@ class AstLabeler: public AstVisitor {
};
+class VarUseMap : public HashMap {
+ public:
+ VarUseMap() : HashMap(VarMatch) {}
+
+ ZoneList<Expression*>* Lookup(Variable* var);
+
+ private:
+ static bool VarMatch(void* key1, void* key2) { return key1 == key2; }
+};
+
+
+class DefinitionInfo : public ZoneObject {
+ public:
+ explicit DefinitionInfo() : last_use_(NULL) {}
+
+ Expression* last_use() { return last_use_; }
+ void set_last_use(Expression* expr) { last_use_ = expr; }
+
+ private:
+ Expression* last_use_;
+ Register location_;
+};
+
+
+class LivenessAnalyzer : public AstVisitor {
+ public:
+ LivenessAnalyzer() {}
+
+ void Analyze(FunctionLiteral* fun);
+
+ private:
+ void VisitStatements(ZoneList<Statement*>* stmts);
+
+ void RecordUse(Variable* var, Expression* expr);
+ void RecordDef(Variable* var, Expression* expr);
+
+
+ // AST node visit functions.
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ // Map for tracking the live variables.
+ VarUseMap live_vars_;
+
+ DISALLOW_COPY_AND_ASSIGN(LivenessAnalyzer);
+};
+
+
} } // namespace v8::internal
+
#endif // V8_DATAFLOW_H_
diff --git a/src/date-delay.js b/src/date-delay.js
deleted file mode 100644
index 7d8f4588..00000000
--- a/src/date-delay.js
+++ /dev/null
@@ -1,1138 +0,0 @@
-// Copyright 2006-2008 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.
-
-
-// This file relies on the fact that the following declarations have been made
-// in v8natives.js:
-// const $isFinite = GlobalIsFinite;
-
-// -------------------------------------------------------------------
-
-// This file contains date support implemented in JavaScript.
-
-
-// Keep reference to original values of some global properties. This
-// has the added benefit that the code in this file is isolated from
-// changes to these properties.
-const $Date = global.Date;
-
-// Helper function to throw error.
-function ThrowDateTypeError() {
- throw new $TypeError('this is not a Date object.');
-}
-
-// ECMA 262 - 5.2
-function Modulo(value, remainder) {
- var mod = value % remainder;
- // Guard against returning -0.
- if (mod == 0) return 0;
- return mod >= 0 ? mod : mod + remainder;
-}
-
-
-function TimeWithinDay(time) {
- return Modulo(time, msPerDay);
-}
-
-
-// ECMA 262 - 15.9.1.3
-function DaysInYear(year) {
- if (year % 4 != 0) return 365;
- if ((year % 100 == 0) && (year % 400 != 0)) return 365;
- return 366;
-}
-
-
-function DayFromYear(year) {
- return 365 * (year-1970)
- + FLOOR((year-1969)/4)
- - FLOOR((year-1901)/100)
- + FLOOR((year-1601)/400);
-}
-
-
-function TimeFromYear(year) {
- return msPerDay * DayFromYear(year);
-}
-
-
-function InLeapYear(time) {
- return DaysInYear(YEAR_FROM_TIME(time)) == 366 ? 1 : 0;
-}
-
-
-function DayWithinYear(time) {
- return DAY(time) - DayFromYear(YEAR_FROM_TIME(time));
-}
-
-
-// ECMA 262 - 15.9.1.9
-function EquivalentYear(year) {
- // Returns an equivalent year in the range [2008-2035] matching
- // - leap year.
- // - week day of first day.
- var time = TimeFromYear(year);
- var recent_year = (InLeapYear(time) == 0 ? 1967 : 1956) +
- (WeekDay(time) * 12) % 28;
- // Find the year in the range 2008..2037 that is equivalent mod 28.
- // Add 3*28 to give a positive argument to the modulus operator.
- return 2008 + (recent_year + 3*28 - 2008) % 28;
-}
-
-
-function EquivalentTime(t) {
- // The issue here is that some library calls don't work right for dates
- // that cannot be represented using a non-negative signed 32 bit integer
- // (measured in whole seconds based on the 1970 epoch).
- // We solve this by mapping the time to a year with same leap-year-ness
- // and same starting day for the year. The ECMAscript specification says
- // we must do this, but for compatibility with other browsers, we use
- // the actual year if it is in the range 1970..2037
- if (t >= 0 && t <= 2.1e12) return t;
- var day = MakeDay(EquivalentYear(YEAR_FROM_TIME(t)), MONTH_FROM_TIME(t), DATE_FROM_TIME(t));
- return TimeClip(MakeDate(day, TimeWithinDay(t)));
-}
-
-
-// Because computing the DST offset is a pretty expensive operation
-// we keep a cache of last computed offset along with a time interval
-// where we know the cache is valid.
-var DST_offset_cache = {
- // Cached DST offset.
- offset: 0,
- // Time interval where the cached offset is valid.
- start: 0, end: -1,
- // Size of next interval expansion.
- increment: 0
-};
-
-
-// NOTE: The implementation relies on the fact that no time zones have
-// more than one daylight savings offset change per month.
-// If this function is called with NaN it returns NaN.
-function DaylightSavingsOffset(t) {
- // Load the cache object from the builtins object.
- var cache = DST_offset_cache;
-
- // Cache the start and the end in local variables for fast access.
- var start = cache.start;
- var end = cache.end;
-
- if (start <= t) {
- // If the time fits in the cached interval, return the cached offset.
- if (t <= end) return cache.offset;
-
- // Compute a possible new interval end.
- var new_end = end + cache.increment;
-
- if (t <= new_end) {
- var end_offset = %DateDaylightSavingsOffset(EquivalentTime(new_end));
- if (cache.offset == end_offset) {
- // If the offset at the end of the new interval still matches
- // the offset in the cache, we grow the cached time interval
- // and return the offset.
- cache.end = new_end;
- cache.increment = msPerMonth;
- return end_offset;
- } else {
- var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
- if (offset == end_offset) {
- // The offset at the given time is equal to the offset at the
- // new end of the interval, so that means that we've just skipped
- // the point in time where the DST offset change occurred. Updated
- // the interval to reflect this and reset the increment.
- cache.start = t;
- cache.end = new_end;
- cache.increment = msPerMonth;
- } else {
- // The interval contains a DST offset change and the given time is
- // before it. Adjust the increment to avoid a linear search for
- // the offset change point and change the end of the interval.
- cache.increment /= 3;
- cache.end = t;
- }
- // Update the offset in the cache and return it.
- cache.offset = offset;
- return offset;
- }
- }
- }
-
- // Compute the DST offset for the time and shrink the cache interval
- // to only contain the time. This allows fast repeated DST offset
- // computations for the same time.
- var offset = %DateDaylightSavingsOffset(EquivalentTime(t));
- cache.offset = offset;
- cache.start = cache.end = t;
- cache.increment = msPerMonth;
- return offset;
-}
-
-
-var timezone_cache_time = $NaN;
-var timezone_cache_timezone;
-
-function LocalTimezone(t) {
- if (NUMBER_IS_NAN(t)) return "";
- if (t == timezone_cache_time) {
- return timezone_cache_timezone;
- }
- var timezone = %DateLocalTimezone(EquivalentTime(t));
- timezone_cache_time = t;
- timezone_cache_timezone = timezone;
- return timezone;
-}
-
-
-function WeekDay(time) {
- return Modulo(DAY(time) + 4, 7);
-}
-
-var local_time_offset = %DateLocalTimeOffset();
-
-function LocalTime(time) {
- if (NUMBER_IS_NAN(time)) return time;
- return time + local_time_offset + DaylightSavingsOffset(time);
-}
-
-function LocalTimeNoCheck(time) {
- // Inline the DST offset cache checks for speed.
- var cache = DST_offset_cache;
- if (cache.start <= time && time <= cache.end) {
- var dst_offset = cache.offset;
- } else {
- var dst_offset = DaylightSavingsOffset(time);
- }
- return time + local_time_offset + dst_offset;
-}
-
-
-function UTC(time) {
- if (NUMBER_IS_NAN(time)) return time;
- var tmp = time - local_time_offset;
- return tmp - DaylightSavingsOffset(tmp);
-}
-
-
-// ECMA 262 - 15.9.1.11
-function MakeTime(hour, min, sec, ms) {
- if (!$isFinite(hour)) return $NaN;
- if (!$isFinite(min)) return $NaN;
- if (!$isFinite(sec)) return $NaN;
- if (!$isFinite(ms)) return $NaN;
- return TO_INTEGER(hour) * msPerHour
- + TO_INTEGER(min) * msPerMinute
- + TO_INTEGER(sec) * msPerSecond
- + TO_INTEGER(ms);
-}
-
-
-// ECMA 262 - 15.9.1.12
-function TimeInYear(year) {
- return DaysInYear(year) * msPerDay;
-}
-
-
-// Compute modified Julian day from year, month, date.
-function ToJulianDay(year, month, date) {
- var jy = (month > 1) ? year : year - 1;
- var jm = (month > 1) ? month + 2 : month + 14;
- var ja = FLOOR(jy / 100);
- return FLOOR(FLOOR(365.25*jy) + FLOOR(30.6001*jm) + date + 1720995) + 2 - ja + FLOOR(0.25*ja);
-}
-
-var four_year_cycle_table = CalculateDateTable();
-
-
-function CalculateDateTable() {
- var month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
- var four_year_cycle_table = new $Array(1461);
-
- var cumulative = 0;
- var position = 0;
- var leap_position = 0;
- for (var month = 0; month < 12; month++) {
- var month_bits = month << kMonthShift;
- var length = month_lengths[month];
- for (var day = 1; day <= length; day++) {
- four_year_cycle_table[leap_position] =
- month_bits + day;
- four_year_cycle_table[366 + position] =
- (1 << kYearShift) + month_bits + day;
- four_year_cycle_table[731 + position] =
- (2 << kYearShift) + month_bits + day;
- four_year_cycle_table[1096 + position] =
- (3 << kYearShift) + month_bits + day;
- leap_position++;
- position++;
- }
- if (month == 1) {
- four_year_cycle_table[leap_position++] = month_bits + 29;
- }
- }
- return four_year_cycle_table;
-}
-
-
-// Constructor for creating objects holding year, month, and date.
-// Introduced to ensure the two return points in FromJulianDay match same map.
-function DayTriplet(year, month, date) {
- this.year = year;
- this.month = month;
- this.date = date;
-}
-
-var julian_day_cache_triplet;
-var julian_day_cache_day = $NaN;
-
-// Compute year, month, and day from modified Julian day.
-// The missing days in 1582 are ignored for JavaScript compatibility.
-function FromJulianDay(julian) {
- if (julian_day_cache_day == julian) {
- return julian_day_cache_triplet;
- }
- var result;
- // Avoid floating point and non-Smi maths in common case. This is also a period of
- // time where leap years are very regular. The range is not too large to avoid overflow
- // when doing the multiply-to-divide trick.
- if (julian > kDayZeroInJulianDay &&
- (julian - kDayZeroInJulianDay) < 40177) { // 1970 - 2080
- var jsimple = (julian - kDayZeroInJulianDay) + 731; // Day 0 is 1st January 1968
- var y = 1968;
- // Divide by 1461 by multiplying with 22967 and shifting down by 25!
- var after_1968 = (jsimple * 22967) >> 25;
- y += after_1968 << 2;
- jsimple -= 1461 * after_1968;
- var four_year_cycle = four_year_cycle_table[jsimple];
- result = new DayTriplet(y + (four_year_cycle >> kYearShift),
- (four_year_cycle & kMonthMask) >> kMonthShift,
- four_year_cycle & kDayMask);
- } else {
- var jalpha = FLOOR((julian - 1867216.25) / 36524.25);
- var jb = julian + 1 + jalpha - FLOOR(0.25 * jalpha) + 1524;
- var jc = FLOOR(6680.0 + ((jb-2439870) - 122.1)/365.25);
- var jd = FLOOR(365 * jc + (0.25 * jc));
- var je = FLOOR((jb - jd)/30.6001);
- var m = je - 1;
- if (m > 12) m -= 13;
- var y = jc - 4715;
- if (m > 2) { --y; --m; }
- var d = jb - jd - FLOOR(30.6001 * je);
- result = new DayTriplet(y, m, d);
- }
- julian_day_cache_day = julian;
- julian_day_cache_triplet = result;
- return result;
-}
-
-
-// Compute number of days given a year, month, date.
-// Note that month and date can lie outside the normal range.
-// For example:
-// MakeDay(2007, -4, 20) --> MakeDay(2006, 8, 20)
-// MakeDay(2007, -33, 1) --> MakeDay(2004, 3, 1)
-// MakeDay(2007, 14, -50) --> MakeDay(2007, 8, 11)
-function MakeDay(year, month, date) {
- if (!$isFinite(year) || !$isFinite(month) || !$isFinite(date)) return $NaN;
-
- // Conversion to integers.
- year = TO_INTEGER(year);
- month = TO_INTEGER(month);
- date = TO_INTEGER(date);
-
- // Overflow months into year.
- year = year + FLOOR(month/12);
- month = month % 12;
- if (month < 0) {
- month += 12;
- }
-
- // Return days relative to Jan 1 1970.
- return ToJulianDay(year, month, date) - kDayZeroInJulianDay;
-}
-
-
-// ECMA 262 - 15.9.1.13
-function MakeDate(day, time) {
- if (!$isFinite(day)) return $NaN;
- if (!$isFinite(time)) return $NaN;
- return day * msPerDay + time;
-}
-
-
-// ECMA 262 - 15.9.1.14
-function TimeClip(time) {
- if (!$isFinite(time)) return $NaN;
- if ($abs(time) > 8.64E15) return $NaN;
- return TO_INTEGER(time);
-}
-
-
-// The Date cache is used to limit the cost of parsing the same Date
-// strings over and over again.
-var Date_cache = {
- // Cached time value.
- time: $NaN,
- // Cached year when interpreting the time as a local time. Only
- // valid when the time matches cached time.
- year: $NaN,
- // String input for which the cached time is valid.
- string: null
-};
-
-
-%SetCode($Date, function(year, month, date, hours, minutes, seconds, ms) {
- if (!%_IsConstructCall()) {
- // ECMA 262 - 15.9.2
- return (new $Date()).toString();
- }
-
- // ECMA 262 - 15.9.3
- var argc = %_ArgumentsLength();
- var value;
- if (argc == 0) {
- value = %DateCurrentTime();
-
- } else if (argc == 1) {
- if (IS_NUMBER(year)) {
- value = TimeClip(year);
-
- } else if (IS_STRING(year)) {
- // Probe the Date cache. If we already have a time value for the
- // given time, we re-use that instead of parsing the string again.
- var cache = Date_cache;
- if (cache.string === year) {
- value = cache.time;
- } else {
- value = DateParse(year);
- if (!NUMBER_IS_NAN(value)) {
- cache.time = value;
- cache.year = YEAR_FROM_TIME(LocalTimeNoCheck(value));
- cache.string = year;
- }
- }
-
- } else {
- // According to ECMA 262, no hint should be given for this
- // conversion. However, ToPrimitive defaults to STRING_HINT for
- // Date objects which will lose precision when the Date
- // constructor is called with another Date object as its
- // argument. We therefore use NUMBER_HINT for the conversion,
- // which is the default for everything else than Date objects.
- // This makes us behave like KJS and SpiderMonkey.
- var time = ToPrimitive(year, NUMBER_HINT);
- value = IS_STRING(time) ? DateParse(time) : TimeClip(ToNumber(time));
- }
-
- } else {
- year = ToNumber(year);
- month = ToNumber(month);
- date = argc > 2 ? ToNumber(date) : 1;
- hours = argc > 3 ? ToNumber(hours) : 0;
- minutes = argc > 4 ? ToNumber(minutes) : 0;
- seconds = argc > 5 ? ToNumber(seconds) : 0;
- ms = argc > 6 ? ToNumber(ms) : 0;
- year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
- ? 1900 + TO_INTEGER(year) : year;
- var day = MakeDay(year, month, date);
- var time = MakeTime(hours, minutes, seconds, ms);
- value = TimeClip(UTC(MakeDate(day, time)));
- }
- %_SetValueOf(this, value);
-});
-
-
-// Helper functions.
-function GetTimeFrom(aDate) {
- return DATE_VALUE(aDate);
-}
-
-function GetMillisecondsFrom(aDate) {
- var t = DATE_VALUE(aDate);
- if (NUMBER_IS_NAN(t)) return t;
- return MS_FROM_TIME(LocalTimeNoCheck(t));
-}
-
-
-function GetUTCMillisecondsFrom(aDate) {
- var t = DATE_VALUE(aDate);
- if (NUMBER_IS_NAN(t)) return t;
- return MS_FROM_TIME(t);
-}
-
-
-function GetSecondsFrom(aDate) {
- var t = DATE_VALUE(aDate);
- if (NUMBER_IS_NAN(t)) return t;
- return SEC_FROM_TIME(LocalTimeNoCheck(t));
-}
-
-
-function GetUTCSecondsFrom(aDate) {
- var t = DATE_VALUE(aDate);
- if (NUMBER_IS_NAN(t)) return t;
- return SEC_FROM_TIME(t);
-}
-
-
-function GetMinutesFrom(aDate) {
- var t = DATE_VALUE(aDate);
- if (NUMBER_IS_NAN(t)) return t;
- return MIN_FROM_TIME(LocalTimeNoCheck(t));
-}
-
-
-function GetUTCMinutesFrom(aDate) {
- var t = DATE_VALUE(aDate);
- if (NUMBER_IS_NAN(t)) return t;
- return MIN_FROM_TIME(t);
-}
-
-
-function GetHoursFrom(aDate) {
- var t = DATE_VALUE(aDate);
- if (NUMBER_IS_NAN(t)) return t;
- return HOUR_FROM_TIME(LocalTimeNoCheck(t));
-}
-
-
-function GetUTCHoursFrom(aDate) {
- var t = DATE_VALUE(aDate);
- if (NUMBER_IS_NAN(t)) return t;
- return HOUR_FROM_TIME(t);
-}
-
-
-function GetFullYearFrom(aDate) {
- var t = DATE_VALUE(aDate);
- if (NUMBER_IS_NAN(t)) return t;
- var cache = Date_cache;
- if (cache.time === t) return cache.year;
- return YEAR_FROM_TIME(LocalTimeNoCheck(t));
-}
-
-
-function GetUTCFullYearFrom(aDate) {
- var t = DATE_VALUE(aDate);
- if (NUMBER_IS_NAN(t)) return t;
- return YEAR_FROM_TIME(t);
-}
-
-
-function GetMonthFrom(aDate) {
- var t = DATE_VALUE(aDate);
- if (NUMBER_IS_NAN(t)) return t;
- return MONTH_FROM_TIME(LocalTimeNoCheck(t));
-}
-
-
-function GetUTCMonthFrom(aDate) {
- var t = DATE_VALUE(aDate);
- if (NUMBER_IS_NAN(t)) return t;
- return MONTH_FROM_TIME(t);
-}
-
-
-function GetDateFrom(aDate) {
- var t = DATE_VALUE(aDate);
- if (NUMBER_IS_NAN(t)) return t;
- return DATE_FROM_TIME(LocalTimeNoCheck(t));
-}
-
-
-function GetUTCDateFrom(aDate) {
- var t = DATE_VALUE(aDate);
- if (NUMBER_IS_NAN(t)) return t;
- return DATE_FROM_TIME(t);
-}
-
-
-%FunctionSetPrototype($Date, new $Date($NaN));
-
-
-var WeekDays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
-var Months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
-
-
-function TwoDigitString(value) {
- return value < 10 ? "0" + value : "" + value;
-}
-
-
-function DateString(time) {
- var YMD = FromJulianDay(DAY(time) + kDayZeroInJulianDay);
- return WeekDays[WeekDay(time)] + ' '
- + Months[YMD.month] + ' '
- + TwoDigitString(YMD.date) + ' '
- + YMD.year;
-}
-
-
-var LongWeekDays = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
-var LongMonths = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
-
-
-function LongDateString(time) {
- var YMD = FromJulianDay(DAY(time) + kDayZeroInJulianDay);
- return LongWeekDays[WeekDay(time)] + ', '
- + LongMonths[YMD.month] + ' '
- + TwoDigitString(YMD.date) + ', '
- + YMD.year;
-}
-
-
-function TimeString(time) {
- return TwoDigitString(HOUR_FROM_TIME(time)) + ':'
- + TwoDigitString(MIN_FROM_TIME(time)) + ':'
- + TwoDigitString(SEC_FROM_TIME(time));
-}
-
-
-function LocalTimezoneString(time) {
- var timezoneOffset =
- (local_time_offset + DaylightSavingsOffset(time)) / msPerMinute;
- var sign = (timezoneOffset >= 0) ? 1 : -1;
- var hours = FLOOR((sign * timezoneOffset)/60);
- var min = FLOOR((sign * timezoneOffset)%60);
- var gmt = ' GMT' + ((sign == 1) ? '+' : '-') +
- TwoDigitString(hours) + TwoDigitString(min);
- return gmt + ' (' + LocalTimezone(time) + ')';
-}
-
-
-function DatePrintString(time) {
- return DateString(time) + ' ' + TimeString(time);
-}
-
-// -------------------------------------------------------------------
-
-// Reused output buffer. Used when parsing date strings.
-var parse_buffer = $Array(7);
-
-// ECMA 262 - 15.9.4.2
-function DateParse(string) {
- var arr = %DateParseString(ToString(string), parse_buffer);
- if (IS_NULL(arr)) return $NaN;
-
- var day = MakeDay(arr[0], arr[1], arr[2]);
- var time = MakeTime(arr[3], arr[4], arr[5], 0);
- var date = MakeDate(day, time);
-
- if (IS_NULL(arr[6])) {
- return TimeClip(UTC(date));
- } else {
- return TimeClip(date - arr[6] * 1000);
- }
-}
-
-
-// ECMA 262 - 15.9.4.3
-function DateUTC(year, month, date, hours, minutes, seconds, ms) {
- year = ToNumber(year);
- month = ToNumber(month);
- var argc = %_ArgumentsLength();
- date = argc > 2 ? ToNumber(date) : 1;
- hours = argc > 3 ? ToNumber(hours) : 0;
- minutes = argc > 4 ? ToNumber(minutes) : 0;
- seconds = argc > 5 ? ToNumber(seconds) : 0;
- ms = argc > 6 ? ToNumber(ms) : 0;
- year = (!NUMBER_IS_NAN(year) && 0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
- ? 1900 + TO_INTEGER(year) : year;
- var day = MakeDay(year, month, date);
- var time = MakeTime(hours, minutes, seconds, ms);
- return %_SetValueOf(this, TimeClip(MakeDate(day, time)));
-}
-
-
-// Mozilla-specific extension. Returns the number of milliseconds
-// elapsed since 1 January 1970 00:00:00 UTC.
-function DateNow() {
- return %DateCurrentTime();
-}
-
-
-// ECMA 262 - 15.9.5.2
-function DateToString() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return kInvalidDate;
- return DatePrintString(LocalTimeNoCheck(t)) + LocalTimezoneString(t);
-}
-
-
-// ECMA 262 - 15.9.5.3
-function DateToDateString() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return kInvalidDate;
- return DateString(LocalTimeNoCheck(t));
-}
-
-
-// ECMA 262 - 15.9.5.4
-function DateToTimeString() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return kInvalidDate;
- var lt = LocalTimeNoCheck(t);
- return TimeString(lt) + LocalTimezoneString(lt);
-}
-
-
-// ECMA 262 - 15.9.5.5
-function DateToLocaleString() {
- return DateToString.call(this);
-}
-
-
-// ECMA 262 - 15.9.5.6
-function DateToLocaleDateString() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return kInvalidDate;
- return LongDateString(LocalTimeNoCheck(t));
-}
-
-
-// ECMA 262 - 15.9.5.7
-function DateToLocaleTimeString() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return kInvalidDate;
- var lt = LocalTimeNoCheck(t);
- return TimeString(lt);
-}
-
-
-// ECMA 262 - 15.9.5.8
-function DateValueOf() {
- return DATE_VALUE(this);
-}
-
-
-// ECMA 262 - 15.9.5.9
-function DateGetTime() {
- return DATE_VALUE(this);
-}
-
-
-// ECMA 262 - 15.9.5.10
-function DateGetFullYear() {
- return GetFullYearFrom(this)
-}
-
-
-// ECMA 262 - 15.9.5.11
-function DateGetUTCFullYear() {
- return GetUTCFullYearFrom(this)
-}
-
-
-// ECMA 262 - 15.9.5.12
-function DateGetMonth() {
- return GetMonthFrom(this);
-}
-
-
-// ECMA 262 - 15.9.5.13
-function DateGetUTCMonth() {
- return GetUTCMonthFrom(this);
-}
-
-
-// ECMA 262 - 15.9.5.14
-function DateGetDate() {
- return GetDateFrom(this);
-}
-
-
-// ECMA 262 - 15.9.5.15
-function DateGetUTCDate() {
- return GetUTCDateFrom(this);
-}
-
-
-// ECMA 262 - 15.9.5.16
-function DateGetDay() {
- var t = %_ValueOf(this);
- if (NUMBER_IS_NAN(t)) return t;
- return WeekDay(LocalTimeNoCheck(t));
-}
-
-
-// ECMA 262 - 15.9.5.17
-function DateGetUTCDay() {
- var t = %_ValueOf(this);
- if (NUMBER_IS_NAN(t)) return t;
- return WeekDay(t);
-}
-
-
-// ECMA 262 - 15.9.5.18
-function DateGetHours() {
- return GetHoursFrom(this);
-}
-
-
-// ECMA 262 - 15.9.5.19
-function DateGetUTCHours() {
- return GetUTCHoursFrom(this);
-}
-
-
-// ECMA 262 - 15.9.5.20
-function DateGetMinutes() {
- return GetMinutesFrom(this);
-}
-
-
-// ECMA 262 - 15.9.5.21
-function DateGetUTCMinutes() {
- return GetUTCMinutesFrom(this);
-}
-
-
-// ECMA 262 - 15.9.5.22
-function DateGetSeconds() {
- return GetSecondsFrom(this);
-}
-
-
-// ECMA 262 - 15.9.5.23
-function DateGetUTCSeconds() {
- return GetUTCSecondsFrom(this);
-}
-
-
-// ECMA 262 - 15.9.5.24
-function DateGetMilliseconds() {
- return GetMillisecondsFrom(this);
-}
-
-
-// ECMA 262 - 15.9.5.25
-function DateGetUTCMilliseconds() {
- return GetUTCMillisecondsFrom(this);
-}
-
-
-// ECMA 262 - 15.9.5.26
-function DateGetTimezoneOffset() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return t;
- return (t - LocalTimeNoCheck(t)) / msPerMinute;
-}
-
-
-// ECMA 262 - 15.9.5.27
-function DateSetTime(ms) {
- if (!IS_DATE(this)) ThrowDateTypeError();
- return %_SetValueOf(this, TimeClip(ToNumber(ms)));
-}
-
-
-// ECMA 262 - 15.9.5.28
-function DateSetMilliseconds(ms) {
- var t = LocalTime(DATE_VALUE(this));
- ms = ToNumber(ms);
- var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
- return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
-}
-
-
-// ECMA 262 - 15.9.5.29
-function DateSetUTCMilliseconds(ms) {
- var t = DATE_VALUE(this);
- ms = ToNumber(ms);
- var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), SEC_FROM_TIME(t), ms);
- return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
-}
-
-
-// ECMA 262 - 15.9.5.30
-function DateSetSeconds(sec, ms) {
- var t = LocalTime(DATE_VALUE(this));
- sec = ToNumber(sec);
- ms = %_ArgumentsLength() < 2 ? GetMillisecondsFrom(this) : ToNumber(ms);
- var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
- return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
-}
-
-
-// ECMA 262 - 15.9.5.31
-function DateSetUTCSeconds(sec, ms) {
- var t = DATE_VALUE(this);
- sec = ToNumber(sec);
- ms = %_ArgumentsLength() < 2 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
- var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
- return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
-}
-
-
-// ECMA 262 - 15.9.5.33
-function DateSetMinutes(min, sec, ms) {
- var t = LocalTime(DATE_VALUE(this));
- min = ToNumber(min);
- var argc = %_ArgumentsLength();
- sec = argc < 2 ? GetSecondsFrom(this) : ToNumber(sec);
- ms = argc < 3 ? GetMillisecondsFrom(this) : ToNumber(ms);
- var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
- return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
-}
-
-
-// ECMA 262 - 15.9.5.34
-function DateSetUTCMinutes(min, sec, ms) {
- var t = DATE_VALUE(this);
- min = ToNumber(min);
- var argc = %_ArgumentsLength();
- sec = argc < 2 ? GetUTCSecondsFrom(this) : ToNumber(sec);
- ms = argc < 3 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
- var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
- return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
-}
-
-
-// ECMA 262 - 15.9.5.35
-function DateSetHours(hour, min, sec, ms) {
- var t = LocalTime(DATE_VALUE(this));
- hour = ToNumber(hour);
- var argc = %_ArgumentsLength();
- min = argc < 2 ? GetMinutesFrom(this) : ToNumber(min);
- sec = argc < 3 ? GetSecondsFrom(this) : ToNumber(sec);
- ms = argc < 4 ? GetMillisecondsFrom(this) : ToNumber(ms);
- var time = MakeTime(hour, min, sec, ms);
- return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
-}
-
-
-// ECMA 262 - 15.9.5.34
-function DateSetUTCHours(hour, min, sec, ms) {
- var t = DATE_VALUE(this);
- hour = ToNumber(hour);
- var argc = %_ArgumentsLength();
- min = argc < 2 ? GetUTCMinutesFrom(this) : ToNumber(min);
- sec = argc < 3 ? GetUTCSecondsFrom(this) : ToNumber(sec);
- ms = argc < 4 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
- var time = MakeTime(hour, min, sec, ms);
- return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
-}
-
-
-// ECMA 262 - 15.9.5.36
-function DateSetDate(date) {
- var t = LocalTime(DATE_VALUE(this));
- date = ToNumber(date);
- var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date);
- return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
-}
-
-
-// ECMA 262 - 15.9.5.37
-function DateSetUTCDate(date) {
- var t = DATE_VALUE(this);
- date = ToNumber(date);
- var day = MakeDay(YEAR_FROM_TIME(t), MONTH_FROM_TIME(t), date);
- return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
-}
-
-
-// ECMA 262 - 15.9.5.38
-function DateSetMonth(month, date) {
- var t = LocalTime(DATE_VALUE(this));
- month = ToNumber(month);
- date = %_ArgumentsLength() < 2 ? GetDateFrom(this) : ToNumber(date);
- var day = MakeDay(YEAR_FROM_TIME(t), month, date);
- return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
-}
-
-
-// ECMA 262 - 15.9.5.39
-function DateSetUTCMonth(month, date) {
- var t = DATE_VALUE(this);
- month = ToNumber(month);
- date = %_ArgumentsLength() < 2 ? GetUTCDateFrom(this) : ToNumber(date);
- var day = MakeDay(YEAR_FROM_TIME(t), month, date);
- return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
-}
-
-
-// ECMA 262 - 15.9.5.40
-function DateSetFullYear(year, month, date) {
- var t = DATE_VALUE(this);
- t = NUMBER_IS_NAN(t) ? 0 : LocalTimeNoCheck(t);
- year = ToNumber(year);
- var argc = %_ArgumentsLength();
- month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month);
- date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date);
- var day = MakeDay(year, month, date);
- return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
-}
-
-
-// ECMA 262 - 15.9.5.41
-function DateSetUTCFullYear(year, month, date) {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) t = 0;
- var argc = %_ArgumentsLength();
- year = ToNumber(year);
- month = argc < 2 ? MONTH_FROM_TIME(t) : ToNumber(month);
- date = argc < 3 ? DATE_FROM_TIME(t) : ToNumber(date);
- var day = MakeDay(year, month, date);
- return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
-}
-
-
-// ECMA 262 - 15.9.5.42
-function DateToUTCString() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return kInvalidDate;
- // Return UTC string of the form: Sat, 31 Jan 1970 23:00:00 GMT
- return WeekDays[WeekDay(t)] + ', '
- + TwoDigitString(DATE_FROM_TIME(t)) + ' '
- + Months[MONTH_FROM_TIME(t)] + ' '
- + YEAR_FROM_TIME(t) + ' '
- + TimeString(t) + ' GMT';
-}
-
-
-// ECMA 262 - B.2.4
-function DateGetYear() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return $NaN;
- return YEAR_FROM_TIME(LocalTimeNoCheck(t)) - 1900;
-}
-
-
-// ECMA 262 - B.2.5
-function DateSetYear(year) {
- var t = LocalTime(DATE_VALUE(this));
- if (NUMBER_IS_NAN(t)) t = 0;
- year = ToNumber(year);
- if (NUMBER_IS_NAN(year)) return %_SetValueOf(this, $NaN);
- year = (0 <= TO_INTEGER(year) && TO_INTEGER(year) <= 99)
- ? 1900 + TO_INTEGER(year) : year;
- var day = MakeDay(year, MONTH_FROM_TIME(t), DATE_FROM_TIME(t));
- return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
-}
-
-
-// ECMA 262 - B.2.6
-//
-// Notice that this does not follow ECMA 262 completely. ECMA 262
-// says that toGMTString should be the same Function object as
-// toUTCString. JSC does not do this, so for compatibility we do not
-// do that either. Instead, we create a new function whose name
-// property will return toGMTString.
-function DateToGMTString() {
- return DateToUTCString.call(this);
-}
-
-
-function PadInt(n, digits) {
- if (digits == 1) return n;
- return n < MathPow(10, digits - 1) ? '0' + PadInt(n, digits - 1) : n;
-}
-
-
-function DateToISOString() {
- var t = DATE_VALUE(this);
- if (NUMBER_IS_NAN(t)) return kInvalidDate;
- return this.getUTCFullYear() + '-' + PadInt(this.getUTCMonth() + 1, 2) +
- '-' + PadInt(this.getUTCDate(), 2) + 'T' + PadInt(this.getUTCHours(), 2) +
- ':' + PadInt(this.getUTCMinutes(), 2) + ':' + PadInt(this.getUTCSeconds(), 2) +
- '.' + PadInt(this.getUTCMilliseconds(), 3) +
- 'Z';
-}
-
-
-function DateToJSON(key) {
- return CheckJSONPrimitive(this.toISOString());
-}
-
-
-// -------------------------------------------------------------------
-
-function SetupDate() {
- // Setup non-enumerable properties of the Date object itself.
- InstallFunctions($Date, DONT_ENUM, $Array(
- "UTC", DateUTC,
- "parse", DateParse,
- "now", DateNow
- ));
-
- // Setup non-enumerable constructor property of the Date prototype object.
- %SetProperty($Date.prototype, "constructor", $Date, DONT_ENUM);
-
- // Setup non-enumerable functions of the Date prototype object and
- // set their names.
- InstallFunctionsOnHiddenPrototype($Date.prototype, DONT_ENUM, $Array(
- "toString", DateToString,
- "toDateString", DateToDateString,
- "toTimeString", DateToTimeString,
- "toLocaleString", DateToLocaleString,
- "toLocaleDateString", DateToLocaleDateString,
- "toLocaleTimeString", DateToLocaleTimeString,
- "valueOf", DateValueOf,
- "getTime", DateGetTime,
- "getFullYear", DateGetFullYear,
- "getUTCFullYear", DateGetUTCFullYear,
- "getMonth", DateGetMonth,
- "getUTCMonth", DateGetUTCMonth,
- "getDate", DateGetDate,
- "getUTCDate", DateGetUTCDate,
- "getDay", DateGetDay,
- "getUTCDay", DateGetUTCDay,
- "getHours", DateGetHours,
- "getUTCHours", DateGetUTCHours,
- "getMinutes", DateGetMinutes,
- "getUTCMinutes", DateGetUTCMinutes,
- "getSeconds", DateGetSeconds,
- "getUTCSeconds", DateGetUTCSeconds,
- "getMilliseconds", DateGetMilliseconds,
- "getUTCMilliseconds", DateGetUTCMilliseconds,
- "getTimezoneOffset", DateGetTimezoneOffset,
- "setTime", DateSetTime,
- "setMilliseconds", DateSetMilliseconds,
- "setUTCMilliseconds", DateSetUTCMilliseconds,
- "setSeconds", DateSetSeconds,
- "setUTCSeconds", DateSetUTCSeconds,
- "setMinutes", DateSetMinutes,
- "setUTCMinutes", DateSetUTCMinutes,
- "setHours", DateSetHours,
- "setUTCHours", DateSetUTCHours,
- "setDate", DateSetDate,
- "setUTCDate", DateSetUTCDate,
- "setMonth", DateSetMonth,
- "setUTCMonth", DateSetUTCMonth,
- "setFullYear", DateSetFullYear,
- "setUTCFullYear", DateSetUTCFullYear,
- "toGMTString", DateToGMTString,
- "toUTCString", DateToUTCString,
- "getYear", DateGetYear,
- "setYear", DateSetYear,
- "toISOString", DateToISOString,
- "toJSON", DateToJSON
- ));
-}
-
-SetupDate();
diff --git a/src/date.js b/src/date.js
index 2ffd0060..7d8f4588 100644
--- a/src/date.js
+++ b/src/date.js
@@ -730,8 +730,7 @@ function DateValueOf() {
// ECMA 262 - 15.9.5.9
-function DateGetTime(logMarker) {
- if (logMarker) %ProfileLogMarker(logMarker);
+function DateGetTime() {
return DATE_VALUE(this);
}
diff --git a/src/debug-debugger.js b/src/debug-debugger.js
index 14d8c883..55c25a92 100644
--- a/src/debug-debugger.js
+++ b/src/debug-debugger.js
@@ -1202,11 +1202,16 @@ DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request)
throw new Error('Command not specified');
}
- // TODO(yurys): remove request.arguments.compactFormat check once
- // ChromeDevTools are switched to 'inlineRefs'
- if (request.arguments && (request.arguments.inlineRefs ||
- request.arguments.compactFormat)) {
- response.setOption('inlineRefs', true);
+ if (request.arguments) {
+ var args = request.arguments;
+ // TODO(yurys): remove request.arguments.compactFormat check once
+ // ChromeDevTools are switched to 'inlineRefs'
+ if (args.inlineRefs || args.compactFormat) {
+ response.setOption('inlineRefs', true);
+ }
+ if (!IS_UNDEFINED(args.maxStringLength)) {
+ response.setOption('maxStringLength', args.maxStringLength);
+ }
}
if (request.command == 'continue') {
@@ -1934,10 +1939,14 @@ DebugCommandProcessor.prototype.profileRequest_ = function(request, response) {
if (isNaN(modules)) {
return response.failed('Modules is not an integer');
}
+ var tag = parseInt(request.arguments.tag);
+ if (isNaN(tag)) {
+ tag = 0;
+ }
if (request.arguments.command == 'resume') {
- %ProfilerResume(modules);
+ %ProfilerResume(modules, tag);
} else if (request.arguments.command == 'pause') {
- %ProfilerPause(modules);
+ %ProfilerPause(modules, tag);
} else {
return response.failed('Unknown command');
}
diff --git a/src/debug-delay.js b/src/debug-delay.js
deleted file mode 100644
index 14d8c883..00000000
--- a/src/debug-delay.js
+++ /dev/null
@@ -1,2073 +0,0 @@
-// Copyright 2006-2008 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.
-
-// Default number of frames to include in the response to backtrace request.
-const kDefaultBacktraceLength = 10;
-
-const Debug = {};
-
-// Regular expression to skip "crud" at the beginning of a source line which is
-// not really code. Currently the regular expression matches whitespace and
-// comments.
-const sourceLineBeginningSkip = /^(?:\s*(?:\/\*.*?\*\/)*)*/;
-
-// Debug events which can occour in the V8 JavaScript engine. These originate
-// from the API include file debug.h.
-Debug.DebugEvent = { Break: 1,
- Exception: 2,
- NewFunction: 3,
- BeforeCompile: 4,
- AfterCompile: 5,
- ScriptCollected: 6 };
-
-// Types of exceptions that can be broken upon.
-Debug.ExceptionBreak = { All : 0,
- Uncaught: 1 };
-
-// The different types of steps.
-Debug.StepAction = { StepOut: 0,
- StepNext: 1,
- StepIn: 2,
- StepMin: 3,
- StepInMin: 4 };
-
-// The different types of scripts matching enum ScriptType in objects.h.
-Debug.ScriptType = { Native: 0,
- Extension: 1,
- Normal: 2 };
-
-// The different types of script compilations matching enum
-// Script::CompilationType in objects.h.
-Debug.ScriptCompilationType = { Host: 0,
- Eval: 1,
- JSON: 2 };
-
-// The different script break point types.
-Debug.ScriptBreakPointType = { ScriptId: 0,
- ScriptName: 1 };
-
-function ScriptTypeFlag(type) {
- return (1 << type);
-}
-
-// Globals.
-var next_response_seq = 0;
-var next_break_point_number = 1;
-var break_points = [];
-var script_break_points = [];
-
-
-// Create a new break point object and add it to the list of break points.
-function MakeBreakPoint(source_position, opt_line, opt_column, opt_script_break_point) {
- var break_point = new BreakPoint(source_position, opt_line, opt_column, opt_script_break_point);
- break_points.push(break_point);
- return break_point;
-}
-
-
-// Object representing a break point.
-// NOTE: This object does not have a reference to the function having break
-// point as this would cause function not to be garbage collected when it is
-// not used any more. We do not want break points to keep functions alive.
-function BreakPoint(source_position, opt_line, opt_column, opt_script_break_point) {
- this.source_position_ = source_position;
- this.source_line_ = opt_line;
- this.source_column_ = opt_column;
- if (opt_script_break_point) {
- this.script_break_point_ = opt_script_break_point;
- } else {
- this.number_ = next_break_point_number++;
- }
- this.hit_count_ = 0;
- this.active_ = true;
- this.condition_ = null;
- this.ignoreCount_ = 0;
-}
-
-
-BreakPoint.prototype.number = function() {
- return this.number_;
-};
-
-
-BreakPoint.prototype.func = function() {
- return this.func_;
-};
-
-
-BreakPoint.prototype.source_position = function() {
- return this.source_position_;
-};
-
-
-BreakPoint.prototype.hit_count = function() {
- return this.hit_count_;
-};
-
-
-BreakPoint.prototype.active = function() {
- if (this.script_break_point()) {
- return this.script_break_point().active();
- }
- return this.active_;
-};
-
-
-BreakPoint.prototype.condition = function() {
- if (this.script_break_point() && this.script_break_point().condition()) {
- return this.script_break_point().condition();
- }
- return this.condition_;
-};
-
-
-BreakPoint.prototype.ignoreCount = function() {
- return this.ignoreCount_;
-};
-
-
-BreakPoint.prototype.script_break_point = function() {
- return this.script_break_point_;
-};
-
-
-BreakPoint.prototype.enable = function() {
- this.active_ = true;
-};
-
-
-BreakPoint.prototype.disable = function() {
- this.active_ = false;
-};
-
-
-BreakPoint.prototype.setCondition = function(condition) {
- this.condition_ = condition;
-};
-
-
-BreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
- this.ignoreCount_ = ignoreCount;
-};
-
-
-BreakPoint.prototype.isTriggered = function(exec_state) {
- // Break point not active - not triggered.
- if (!this.active()) return false;
-
- // Check for conditional break point.
- if (this.condition()) {
- // If break point has condition try to evaluate it in the top frame.
- try {
- var mirror = exec_state.frame(0).evaluate(this.condition());
- // If no sensible mirror or non true value break point not triggered.
- if (!(mirror instanceof ValueMirror) || !%ToBoolean(mirror.value_)) {
- return false;
- }
- } catch (e) {
- // Exception evaluating condition counts as not triggered.
- return false;
- }
- }
-
- // Update the hit count.
- this.hit_count_++;
- if (this.script_break_point_) {
- this.script_break_point_.hit_count_++;
- }
-
- // If the break point has an ignore count it is not triggered.
- if (this.ignoreCount_ > 0) {
- this.ignoreCount_--;
- return false;
- }
-
- // Break point triggered.
- return true;
-};
-
-
-// Function called from the runtime when a break point is hit. Returns true if
-// the break point is triggered and supposed to break execution.
-function IsBreakPointTriggered(break_id, break_point) {
- return break_point.isTriggered(MakeExecutionState(break_id));
-}
-
-
-// Object representing a script break point. The script is referenced by its
-// script name or script id and the break point is represented as line and
-// column.
-function ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
- opt_groupId) {
- this.type_ = type;
- if (type == Debug.ScriptBreakPointType.ScriptId) {
- this.script_id_ = script_id_or_name;
- } else { // type == Debug.ScriptBreakPointType.ScriptName
- this.script_name_ = script_id_or_name;
- }
- this.line_ = opt_line || 0;
- this.column_ = opt_column;
- this.groupId_ = opt_groupId;
- this.hit_count_ = 0;
- this.active_ = true;
- this.condition_ = null;
- this.ignoreCount_ = 0;
-}
-
-
-ScriptBreakPoint.prototype.number = function() {
- return this.number_;
-};
-
-
-ScriptBreakPoint.prototype.groupId = function() {
- return this.groupId_;
-};
-
-
-ScriptBreakPoint.prototype.type = function() {
- return this.type_;
-};
-
-
-ScriptBreakPoint.prototype.script_id = function() {
- return this.script_id_;
-};
-
-
-ScriptBreakPoint.prototype.script_name = function() {
- return this.script_name_;
-};
-
-
-ScriptBreakPoint.prototype.line = function() {
- return this.line_;
-};
-
-
-ScriptBreakPoint.prototype.column = function() {
- return this.column_;
-};
-
-
-ScriptBreakPoint.prototype.hit_count = function() {
- return this.hit_count_;
-};
-
-
-ScriptBreakPoint.prototype.active = function() {
- return this.active_;
-};
-
-
-ScriptBreakPoint.prototype.condition = function() {
- return this.condition_;
-};
-
-
-ScriptBreakPoint.prototype.ignoreCount = function() {
- return this.ignoreCount_;
-};
-
-
-ScriptBreakPoint.prototype.enable = function() {
- this.active_ = true;
-};
-
-
-ScriptBreakPoint.prototype.disable = function() {
- this.active_ = false;
-};
-
-
-ScriptBreakPoint.prototype.setCondition = function(condition) {
- this.condition_ = condition;
-};
-
-
-ScriptBreakPoint.prototype.setIgnoreCount = function(ignoreCount) {
- this.ignoreCount_ = ignoreCount;
-
- // Set ignore count on all break points created from this script break point.
- for (var i = 0; i < break_points.length; i++) {
- if (break_points[i].script_break_point() === this) {
- break_points[i].setIgnoreCount(ignoreCount);
- }
- }
-};
-
-
-// Check whether a script matches this script break point. Currently this is
-// only based on script name.
-ScriptBreakPoint.prototype.matchesScript = function(script) {
- if (this.type_ == Debug.ScriptBreakPointType.ScriptId) {
- return this.script_id_ == script.id;
- } else { // this.type_ == Debug.ScriptBreakPointType.ScriptName
- return this.script_name_ == script.name &&
- script.line_offset <= this.line_ &&
- this.line_ < script.line_offset + script.lineCount();
- }
-};
-
-
-// Set the script break point in a script.
-ScriptBreakPoint.prototype.set = function (script) {
- var column = this.column();
- var line = this.line();
- // If the column is undefined the break is on the line. To help locate the
- // first piece of breakable code on the line try to find the column on the
- // line which contains some source.
- if (IS_UNDEFINED(column)) {
- var source_line = script.sourceLine(this.line());
-
- // Allocate array for caching the columns where the actual source starts.
- if (!script.sourceColumnStart_) {
- script.sourceColumnStart_ = new Array(script.lineCount());
- }
-
- // Fill cache if needed and get column where the actual source starts.
- if (IS_UNDEFINED(script.sourceColumnStart_[line])) {
- script.sourceColumnStart_[line] =
- source_line.match(sourceLineBeginningSkip)[0].length;
- }
- column = script.sourceColumnStart_[line];
- }
-
- // Convert the line and column into an absolute position within the script.
- var pos = Debug.findScriptSourcePosition(script, this.line(), column);
-
- // If the position is not found in the script (the script might be shorter
- // than it used to be) just ignore it.
- if (pos === null) return;
-
- // Create a break point object and set the break point.
- break_point = MakeBreakPoint(pos, this.line(), this.column(), this);
- break_point.setIgnoreCount(this.ignoreCount());
- %SetScriptBreakPoint(script, pos, break_point);
-
- return break_point;
-};
-
-
-// Clear all the break points created from this script break point
-ScriptBreakPoint.prototype.clear = function () {
- var remaining_break_points = [];
- for (var i = 0; i < break_points.length; i++) {
- if (break_points[i].script_break_point() &&
- break_points[i].script_break_point() === this) {
- %ClearBreakPoint(break_points[i]);
- } else {
- remaining_break_points.push(break_points[i]);
- }
- }
- break_points = remaining_break_points;
-};
-
-
-// Function called from runtime when a new script is compiled to set any script
-// break points set in this script.
-function UpdateScriptBreakPoints(script) {
- for (var i = 0; i < script_break_points.length; i++) {
- if (script_break_points[i].type() == Debug.ScriptBreakPointType.ScriptName &&
- script_break_points[i].matchesScript(script)) {
- script_break_points[i].set(script);
- }
- }
-}
-
-
-Debug.setListener = function(listener, opt_data) {
- if (!IS_FUNCTION(listener) && !IS_UNDEFINED(listener) && !IS_NULL(listener)) {
- throw new Error('Parameters have wrong types.');
- }
- %SetDebugEventListener(listener, opt_data);
-};
-
-
-Debug.breakExecution = function(f) {
- %Break();
-};
-
-Debug.breakLocations = function(f) {
- if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
- return %GetBreakLocations(f);
-};
-
-// Returns a Script object. If the parameter is a function the return value
-// is the script in which the function is defined. If the parameter is a string
-// the return value is the script for which the script name has that string
-// value. If it is a regexp and there is a unique script whose name matches
-// we return that, otherwise undefined.
-Debug.findScript = function(func_or_script_name) {
- if (IS_FUNCTION(func_or_script_name)) {
- return %FunctionGetScript(func_or_script_name);
- } else if (IS_REGEXP(func_or_script_name)) {
- var scripts = Debug.scripts();
- var last_result = null;
- var result_count = 0;
- for (var i in scripts) {
- var script = scripts[i];
- if (func_or_script_name.test(script.name)) {
- last_result = script;
- result_count++;
- }
- }
- // Return the unique script matching the regexp. If there are more
- // than one we don't return a value since there is no good way to
- // decide which one to return. Returning a "random" one, say the
- // first, would introduce nondeterminism (or something close to it)
- // because the order is the heap iteration order.
- if (result_count == 1) {
- return last_result;
- } else {
- return undefined;
- }
- } else {
- return %GetScript(func_or_script_name);
- }
-};
-
-// Returns the script source. If the parameter is a function the return value
-// is the script source for the script in which the function is defined. If the
-// parameter is a string the return value is the script for which the script
-// name has that string value.
-Debug.scriptSource = function(func_or_script_name) {
- return this.findScript(func_or_script_name).source;
-};
-
-Debug.source = function(f) {
- if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
- return %FunctionGetSourceCode(f);
-};
-
-Debug.disassemble = function(f) {
- if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
- return %DebugDisassembleFunction(f);
-};
-
-Debug.disassembleConstructor = function(f) {
- if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
- return %DebugDisassembleConstructor(f);
-};
-
-Debug.sourcePosition = function(f) {
- if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
- return %FunctionGetScriptSourcePosition(f);
-};
-
-
-Debug.findFunctionSourceLocation = function(func, opt_line, opt_column) {
- var script = %FunctionGetScript(func);
- var script_offset = %FunctionGetScriptSourcePosition(func);
- return script.locationFromLine(opt_line, opt_column, script_offset);
-}
-
-
-// Returns the character position in a script based on a line number and an
-// optional position within that line.
-Debug.findScriptSourcePosition = function(script, opt_line, opt_column) {
- var location = script.locationFromLine(opt_line, opt_column);
- return location ? location.position : null;
-}
-
-
-Debug.findBreakPoint = function(break_point_number, remove) {
- var break_point;
- for (var i = 0; i < break_points.length; i++) {
- if (break_points[i].number() == break_point_number) {
- break_point = break_points[i];
- // Remove the break point from the list if requested.
- if (remove) {
- break_points.splice(i, 1);
- }
- break;
- }
- }
- if (break_point) {
- return break_point;
- } else {
- return this.findScriptBreakPoint(break_point_number, remove);
- }
-};
-
-
-Debug.setBreakPoint = function(func, opt_line, opt_column, opt_condition) {
- if (!IS_FUNCTION(func)) throw new Error('Parameters have wrong types.');
- // Break points in API functions are not supported.
- if (%FunctionIsAPIFunction(func)) {
- throw new Error('Cannot set break point in native code.');
- }
- // Find source position relative to start of the function
- var break_position =
- this.findFunctionSourceLocation(func, opt_line, opt_column).position;
- var source_position = break_position - this.sourcePosition(func);
- // Find the script for the function.
- var script = %FunctionGetScript(func);
- // Break in builtin JavaScript code is not supported.
- if (script.type == Debug.ScriptType.Native) {
- throw new Error('Cannot set break point in native code.');
- }
- // If the script for the function has a name convert this to a script break
- // point.
- if (script && script.id) {
- // Adjust the source position to be script relative.
- source_position += %FunctionGetScriptSourcePosition(func);
- // Find line and column for the position in the script and set a script
- // break point from that.
- var location = script.locationFromPosition(source_position, false);
- return this.setScriptBreakPointById(script.id,
- location.line, location.column,
- opt_condition);
- } else {
- // Set a break point directly on the function.
- var break_point = MakeBreakPoint(source_position, opt_line, opt_column);
- %SetFunctionBreakPoint(func, source_position, break_point);
- break_point.setCondition(opt_condition);
- return break_point.number();
- }
-};
-
-
-Debug.enableBreakPoint = function(break_point_number) {
- var break_point = this.findBreakPoint(break_point_number, false);
- break_point.enable();
-};
-
-
-Debug.disableBreakPoint = function(break_point_number) {
- var break_point = this.findBreakPoint(break_point_number, false);
- break_point.disable();
-};
-
-
-Debug.changeBreakPointCondition = function(break_point_number, condition) {
- var break_point = this.findBreakPoint(break_point_number, false);
- break_point.setCondition(condition);
-};
-
-
-Debug.changeBreakPointIgnoreCount = function(break_point_number, ignoreCount) {
- if (ignoreCount < 0) {
- throw new Error('Invalid argument');
- }
- var break_point = this.findBreakPoint(break_point_number, false);
- break_point.setIgnoreCount(ignoreCount);
-};
-
-
-Debug.clearBreakPoint = function(break_point_number) {
- var break_point = this.findBreakPoint(break_point_number, true);
- if (break_point) {
- return %ClearBreakPoint(break_point);
- } else {
- break_point = this.findScriptBreakPoint(break_point_number, true);
- if (!break_point) {
- throw new Error('Invalid breakpoint');
- }
- }
-};
-
-
-Debug.clearAllBreakPoints = function() {
- for (var i = 0; i < break_points.length; i++) {
- break_point = break_points[i];
- %ClearBreakPoint(break_point);
- }
- break_points = [];
-};
-
-
-Debug.findScriptBreakPoint = function(break_point_number, remove) {
- var script_break_point;
- for (var i = 0; i < script_break_points.length; i++) {
- if (script_break_points[i].number() == break_point_number) {
- script_break_point = script_break_points[i];
- // Remove the break point from the list if requested.
- if (remove) {
- script_break_point.clear();
- script_break_points.splice(i,1);
- }
- break;
- }
- }
- return script_break_point;
-}
-
-
-// Sets a breakpoint in a script identified through id or name at the
-// specified source line and column within that line.
-Debug.setScriptBreakPoint = function(type, script_id_or_name,
- opt_line, opt_column, opt_condition,
- opt_groupId) {
- // Create script break point object.
- var script_break_point =
- new ScriptBreakPoint(type, script_id_or_name, opt_line, opt_column,
- opt_groupId);
-
- // Assign number to the new script break point and add it.
- script_break_point.number_ = next_break_point_number++;
- script_break_point.setCondition(opt_condition);
- script_break_points.push(script_break_point);
-
- // Run through all scripts to see if this script break point matches any
- // loaded scripts.
- var scripts = this.scripts();
- for (var i = 0; i < scripts.length; i++) {
- if (script_break_point.matchesScript(scripts[i])) {
- script_break_point.set(scripts[i]);
- }
- }
-
- return script_break_point.number();
-}
-
-
-Debug.setScriptBreakPointById = function(script_id,
- opt_line, opt_column,
- opt_condition, opt_groupId) {
- return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptId,
- script_id, opt_line, opt_column,
- opt_condition, opt_groupId);
-}
-
-
-Debug.setScriptBreakPointByName = function(script_name,
- opt_line, opt_column,
- opt_condition, opt_groupId) {
- return this.setScriptBreakPoint(Debug.ScriptBreakPointType.ScriptName,
- script_name, opt_line, opt_column,
- opt_condition, opt_groupId);
-}
-
-
-Debug.enableScriptBreakPoint = function(break_point_number) {
- var script_break_point = this.findScriptBreakPoint(break_point_number, false);
- script_break_point.enable();
-};
-
-
-Debug.disableScriptBreakPoint = function(break_point_number) {
- var script_break_point = this.findScriptBreakPoint(break_point_number, false);
- script_break_point.disable();
-};
-
-
-Debug.changeScriptBreakPointCondition = function(break_point_number, condition) {
- var script_break_point = this.findScriptBreakPoint(break_point_number, false);
- script_break_point.setCondition(condition);
-};
-
-
-Debug.changeScriptBreakPointIgnoreCount = function(break_point_number, ignoreCount) {
- if (ignoreCount < 0) {
- throw new Error('Invalid argument');
- }
- var script_break_point = this.findScriptBreakPoint(break_point_number, false);
- script_break_point.setIgnoreCount(ignoreCount);
-};
-
-
-Debug.scriptBreakPoints = function() {
- return script_break_points;
-}
-
-
-Debug.clearStepping = function() {
- %ClearStepping();
-}
-
-Debug.setBreakOnException = function() {
- return %ChangeBreakOnException(Debug.ExceptionBreak.All, true);
-};
-
-Debug.clearBreakOnException = function() {
- return %ChangeBreakOnException(Debug.ExceptionBreak.All, false);
-};
-
-Debug.setBreakOnUncaughtException = function() {
- return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, true);
-};
-
-Debug.clearBreakOnUncaughtException = function() {
- return %ChangeBreakOnException(Debug.ExceptionBreak.Uncaught, false);
-};
-
-Debug.showBreakPoints = function(f, full) {
- if (!IS_FUNCTION(f)) throw new Error('Parameters have wrong types.');
- var source = full ? this.scriptSource(f) : this.source(f);
- var offset = full ? this.sourcePosition(f) : 0;
- var locations = this.breakLocations(f);
- if (!locations) return source;
- locations.sort(function(x, y) { return x - y; });
- var result = "";
- var prev_pos = 0;
- var pos;
- for (var i = 0; i < locations.length; i++) {
- pos = locations[i] - offset;
- result += source.slice(prev_pos, pos);
- result += "[B" + i + "]";
- prev_pos = pos;
- }
- pos = source.length;
- result += source.substring(prev_pos, pos);
- return result;
-};
-
-
-// Get all the scripts currently loaded. Locating all the scripts is based on
-// scanning the heap.
-Debug.scripts = function() {
- // Collect all scripts in the heap.
- return %DebugGetLoadedScripts();
-}
-
-function MakeExecutionState(break_id) {
- return new ExecutionState(break_id);
-}
-
-function ExecutionState(break_id) {
- this.break_id = break_id;
- this.selected_frame = 0;
-}
-
-ExecutionState.prototype.prepareStep = function(opt_action, opt_count) {
- var action = Debug.StepAction.StepIn;
- if (!IS_UNDEFINED(opt_action)) action = %ToNumber(opt_action);
- var count = opt_count ? %ToNumber(opt_count) : 1;
-
- return %PrepareStep(this.break_id, action, count);
-}
-
-ExecutionState.prototype.evaluateGlobal = function(source, disable_break) {
- return MakeMirror(
- %DebugEvaluateGlobal(this.break_id, source, Boolean(disable_break)));
-};
-
-ExecutionState.prototype.frameCount = function() {
- return %GetFrameCount(this.break_id);
-};
-
-ExecutionState.prototype.threadCount = function() {
- return %GetThreadCount(this.break_id);
-};
-
-ExecutionState.prototype.frame = function(opt_index) {
- // If no index supplied return the selected frame.
- if (opt_index == null) opt_index = this.selected_frame;
- return new FrameMirror(this.break_id, opt_index);
-};
-
-ExecutionState.prototype.cframesValue = function(opt_from_index, opt_to_index) {
- return %GetCFrames(this.break_id);
-};
-
-ExecutionState.prototype.setSelectedFrame = function(index) {
- var i = %ToNumber(index);
- if (i < 0 || i >= this.frameCount()) throw new Error('Illegal frame index.');
- this.selected_frame = i;
-};
-
-ExecutionState.prototype.selectedFrame = function() {
- return this.selected_frame;
-};
-
-ExecutionState.prototype.debugCommandProcessor = function(opt_is_running) {
- return new DebugCommandProcessor(this, opt_is_running);
-};
-
-
-function MakeBreakEvent(exec_state, break_points_hit) {
- return new BreakEvent(exec_state, break_points_hit);
-}
-
-
-function BreakEvent(exec_state, break_points_hit) {
- this.exec_state_ = exec_state;
- this.break_points_hit_ = break_points_hit;
-}
-
-
-BreakEvent.prototype.executionState = function() {
- return this.exec_state_;
-};
-
-
-BreakEvent.prototype.eventType = function() {
- return Debug.DebugEvent.Break;
-};
-
-
-BreakEvent.prototype.func = function() {
- return this.exec_state_.frame(0).func();
-};
-
-
-BreakEvent.prototype.sourceLine = function() {
- return this.exec_state_.frame(0).sourceLine();
-};
-
-
-BreakEvent.prototype.sourceColumn = function() {
- return this.exec_state_.frame(0).sourceColumn();
-};
-
-
-BreakEvent.prototype.sourceLineText = function() {
- return this.exec_state_.frame(0).sourceLineText();
-};
-
-
-BreakEvent.prototype.breakPointsHit = function() {
- return this.break_points_hit_;
-};
-
-
-BreakEvent.prototype.toJSONProtocol = function() {
- var o = { seq: next_response_seq++,
- type: "event",
- event: "break",
- body: { invocationText: this.exec_state_.frame(0).invocationText(),
- }
- };
-
- // Add script related information to the event if available.
- var script = this.func().script();
- if (script) {
- o.body.sourceLine = this.sourceLine(),
- o.body.sourceColumn = this.sourceColumn(),
- o.body.sourceLineText = this.sourceLineText(),
- o.body.script = MakeScriptObject_(script, false);
- }
-
- // Add an Array of break points hit if any.
- if (this.breakPointsHit()) {
- o.body.breakpoints = [];
- for (var i = 0; i < this.breakPointsHit().length; i++) {
- // Find the break point number. For break points originating from a
- // script break point supply the script break point number.
- var breakpoint = this.breakPointsHit()[i];
- var script_break_point = breakpoint.script_break_point();
- var number;
- if (script_break_point) {
- number = script_break_point.number();
- } else {
- number = breakpoint.number();
- }
- o.body.breakpoints.push(number);
- }
- }
- return JSON.stringify(ObjectToProtocolObject_(o));
-};
-
-
-function MakeExceptionEvent(exec_state, exception, uncaught) {
- return new ExceptionEvent(exec_state, exception, uncaught);
-}
-
-
-function ExceptionEvent(exec_state, exception, uncaught) {
- this.exec_state_ = exec_state;
- this.exception_ = exception;
- this.uncaught_ = uncaught;
-}
-
-
-ExceptionEvent.prototype.executionState = function() {
- return this.exec_state_;
-};
-
-
-ExceptionEvent.prototype.eventType = function() {
- return Debug.DebugEvent.Exception;
-};
-
-
-ExceptionEvent.prototype.exception = function() {
- return this.exception_;
-}
-
-
-ExceptionEvent.prototype.uncaught = function() {
- return this.uncaught_;
-}
-
-
-ExceptionEvent.prototype.func = function() {
- return this.exec_state_.frame(0).func();
-};
-
-
-ExceptionEvent.prototype.sourceLine = function() {
- return this.exec_state_.frame(0).sourceLine();
-};
-
-
-ExceptionEvent.prototype.sourceColumn = function() {
- return this.exec_state_.frame(0).sourceColumn();
-};
-
-
-ExceptionEvent.prototype.sourceLineText = function() {
- return this.exec_state_.frame(0).sourceLineText();
-};
-
-
-ExceptionEvent.prototype.toJSONProtocol = function() {
- var o = new ProtocolMessage();
- o.event = "exception";
- o.body = { uncaught: this.uncaught_,
- exception: MakeMirror(this.exception_)
- };
-
- // Exceptions might happen whithout any JavaScript frames.
- if (this.exec_state_.frameCount() > 0) {
- o.body.sourceLine = this.sourceLine();
- o.body.sourceColumn = this.sourceColumn();
- o.body.sourceLineText = this.sourceLineText();
-
- // Add script information to the event if available.
- var script = this.func().script();
- if (script) {
- o.body.script = MakeScriptObject_(script, false);
- }
- } else {
- o.body.sourceLine = -1;
- }
-
- return o.toJSONProtocol();
-};
-
-
-function MakeCompileEvent(exec_state, script, before) {
- return new CompileEvent(exec_state, script, before);
-}
-
-
-function CompileEvent(exec_state, script, before) {
- this.exec_state_ = exec_state;
- this.script_ = MakeMirror(script);
- this.before_ = before;
-}
-
-
-CompileEvent.prototype.executionState = function() {
- return this.exec_state_;
-};
-
-
-CompileEvent.prototype.eventType = function() {
- if (this.before_) {
- return Debug.DebugEvent.BeforeCompile;
- } else {
- return Debug.DebugEvent.AfterCompile;
- }
-};
-
-
-CompileEvent.prototype.script = function() {
- return this.script_;
-};
-
-
-CompileEvent.prototype.toJSONProtocol = function() {
- var o = new ProtocolMessage();
- o.running = true;
- if (this.before_) {
- o.event = "beforeCompile";
- } else {
- o.event = "afterCompile";
- }
- o.body = {};
- o.body.script = this.script_;
-
- return o.toJSONProtocol();
-}
-
-
-function MakeNewFunctionEvent(func) {
- return new NewFunctionEvent(func);
-}
-
-
-function NewFunctionEvent(func) {
- this.func = func;
-}
-
-
-NewFunctionEvent.prototype.eventType = function() {
- return Debug.DebugEvent.NewFunction;
-};
-
-
-NewFunctionEvent.prototype.name = function() {
- return this.func.name;
-};
-
-
-NewFunctionEvent.prototype.setBreakPoint = function(p) {
- Debug.setBreakPoint(this.func, p || 0);
-};
-
-
-function MakeScriptCollectedEvent(exec_state, id) {
- return new ScriptCollectedEvent(exec_state, id);
-}
-
-
-function ScriptCollectedEvent(exec_state, id) {
- this.exec_state_ = exec_state;
- this.id_ = id;
-}
-
-
-ScriptCollectedEvent.prototype.id = function() {
- return this.id_;
-};
-
-
-ScriptCollectedEvent.prototype.executionState = function() {
- return this.exec_state_;
-};
-
-
-ScriptCollectedEvent.prototype.toJSONProtocol = function() {
- var o = new ProtocolMessage();
- o.running = true;
- o.event = "scriptCollected";
- o.body = {};
- o.body.script = { id: this.id() };
- return o.toJSONProtocol();
-}
-
-
-function MakeScriptObject_(script, include_source) {
- var o = { id: script.id(),
- name: script.name(),
- lineOffset: script.lineOffset(),
- columnOffset: script.columnOffset(),
- lineCount: script.lineCount(),
- };
- if (!IS_UNDEFINED(script.data())) {
- o.data = script.data();
- }
- if (include_source) {
- o.source = script.source();
- }
- return o;
-};
-
-
-function DebugCommandProcessor(exec_state, opt_is_running) {
- this.exec_state_ = exec_state;
- this.running_ = opt_is_running || false;
-};
-
-
-DebugCommandProcessor.prototype.processDebugRequest = function (request) {
- return this.processDebugJSONRequest(request);
-}
-
-
-function ProtocolMessage(request) {
- // Update sequence number.
- this.seq = next_response_seq++;
-
- if (request) {
- // If message is based on a request this is a response. Fill the initial
- // response from the request.
- this.type = 'response';
- this.request_seq = request.seq;
- this.command = request.command;
- } else {
- // If message is not based on a request it is a dabugger generated event.
- this.type = 'event';
- }
- this.success = true;
- // Handler may set this field to control debugger state.
- this.running = undefined;
-}
-
-
-ProtocolMessage.prototype.setOption = function(name, value) {
- if (!this.options_) {
- this.options_ = {};
- }
- this.options_[name] = value;
-}
-
-
-ProtocolMessage.prototype.failed = function(message) {
- this.success = false;
- this.message = message;
-}
-
-
-ProtocolMessage.prototype.toJSONProtocol = function() {
- // Encode the protocol header.
- var json = {};
- json.seq= this.seq;
- if (this.request_seq) {
- json.request_seq = this.request_seq;
- }
- json.type = this.type;
- if (this.event) {
- json.event = this.event;
- }
- if (this.command) {
- json.command = this.command;
- }
- if (this.success) {
- json.success = this.success;
- } else {
- json.success = false;
- }
- if (this.body) {
- // Encode the body part.
- var bodyJson;
- var serializer = MakeMirrorSerializer(true, this.options_);
- if (this.body instanceof Mirror) {
- bodyJson = serializer.serializeValue(this.body);
- } else if (this.body instanceof Array) {
- bodyJson = [];
- for (var i = 0; i < this.body.length; i++) {
- if (this.body[i] instanceof Mirror) {
- bodyJson.push(serializer.serializeValue(this.body[i]));
- } else {
- bodyJson.push(ObjectToProtocolObject_(this.body[i], serializer));
- }
- }
- } else {
- bodyJson = ObjectToProtocolObject_(this.body, serializer);
- }
- json.body = bodyJson;
- json.refs = serializer.serializeReferencedObjects();
- }
- if (this.message) {
- json.message = this.message;
- }
- json.running = this.running;
- return JSON.stringify(json);
-}
-
-
-DebugCommandProcessor.prototype.createResponse = function(request) {
- return new ProtocolMessage(request);
-};
-
-
-DebugCommandProcessor.prototype.processDebugJSONRequest = function(json_request) {
- var request; // Current request.
- var response; // Generated response.
- try {
- try {
- // Convert the JSON string to an object.
- request = %CompileString('(' + json_request + ')', false)();
-
- // Create an initial response.
- response = this.createResponse(request);
-
- if (!request.type) {
- throw new Error('Type not specified');
- }
-
- if (request.type != 'request') {
- throw new Error("Illegal type '" + request.type + "' in request");
- }
-
- if (!request.command) {
- throw new Error('Command not specified');
- }
-
- // TODO(yurys): remove request.arguments.compactFormat check once
- // ChromeDevTools are switched to 'inlineRefs'
- if (request.arguments && (request.arguments.inlineRefs ||
- request.arguments.compactFormat)) {
- response.setOption('inlineRefs', true);
- }
-
- if (request.command == 'continue') {
- this.continueRequest_(request, response);
- } else if (request.command == 'break') {
- this.breakRequest_(request, response);
- } else if (request.command == 'setbreakpoint') {
- this.setBreakPointRequest_(request, response);
- } else if (request.command == 'changebreakpoint') {
- this.changeBreakPointRequest_(request, response);
- } else if (request.command == 'clearbreakpoint') {
- this.clearBreakPointRequest_(request, response);
- } else if (request.command == 'clearbreakpointgroup') {
- this.clearBreakPointGroupRequest_(request, response);
- } else if (request.command == 'backtrace') {
- this.backtraceRequest_(request, response);
- } else if (request.command == 'frame') {
- this.frameRequest_(request, response);
- } else if (request.command == 'scopes') {
- this.scopesRequest_(request, response);
- } else if (request.command == 'scope') {
- this.scopeRequest_(request, response);
- } else if (request.command == 'evaluate') {
- this.evaluateRequest_(request, response);
- } else if (request.command == 'lookup') {
- this.lookupRequest_(request, response);
- } else if (request.command == 'references') {
- this.referencesRequest_(request, response);
- } else if (request.command == 'source') {
- this.sourceRequest_(request, response);
- } else if (request.command == 'scripts') {
- this.scriptsRequest_(request, response);
- } else if (request.command == 'threads') {
- this.threadsRequest_(request, response);
- } else if (request.command == 'suspend') {
- this.suspendRequest_(request, response);
- } else if (request.command == 'version') {
- this.versionRequest_(request, response);
- } else if (request.command == 'profile') {
- this.profileRequest_(request, response);
- } else {
- throw new Error('Unknown command "' + request.command + '" in request');
- }
- } catch (e) {
- // If there is no response object created one (without command).
- if (!response) {
- response = this.createResponse();
- }
- response.success = false;
- response.message = %ToString(e);
- }
-
- // Return the response as a JSON encoded string.
- try {
- if (!IS_UNDEFINED(response.running)) {
- // Response controls running state.
- this.running_ = response.running;
- }
- response.running = this.running_;
- return response.toJSONProtocol();
- } catch (e) {
- // Failed to generate response - return generic error.
- return '{"seq":' + response.seq + ',' +
- '"request_seq":' + request.seq + ',' +
- '"type":"response",' +
- '"success":false,' +
- '"message":"Internal error: ' + %ToString(e) + '"}';
- }
- } catch (e) {
- // Failed in one of the catch blocks above - most generic error.
- return '{"seq":0,"type":"response","success":false,"message":"Internal error"}';
- }
-};
-
-
-DebugCommandProcessor.prototype.continueRequest_ = function(request, response) {
- // Check for arguments for continue.
- if (request.arguments) {
- var count = 1;
- var action = Debug.StepAction.StepIn;
-
- // Pull out arguments.
- var stepaction = request.arguments.stepaction;
- var stepcount = request.arguments.stepcount;
-
- // Get the stepcount argument if any.
- if (stepcount) {
- count = %ToNumber(stepcount);
- if (count < 0) {
- throw new Error('Invalid stepcount argument "' + stepcount + '".');
- }
- }
-
- // Get the stepaction argument.
- if (stepaction) {
- if (stepaction == 'in') {
- action = Debug.StepAction.StepIn;
- } else if (stepaction == 'min') {
- action = Debug.StepAction.StepMin;
- } else if (stepaction == 'next') {
- action = Debug.StepAction.StepNext;
- } else if (stepaction == 'out') {
- action = Debug.StepAction.StepOut;
- } else {
- throw new Error('Invalid stepaction argument "' + stepaction + '".');
- }
- }
-
- // Setup the VM for stepping.
- this.exec_state_.prepareStep(action, count);
- }
-
- // VM should be running after executing this request.
- response.running = true;
-};
-
-
-DebugCommandProcessor.prototype.breakRequest_ = function(request, response) {
- // Ignore as break command does not do anything when broken.
-};
-
-
-DebugCommandProcessor.prototype.setBreakPointRequest_ =
- function(request, response) {
- // Check for legal request.
- if (!request.arguments) {
- response.failed('Missing arguments');
- return;
- }
-
- // Pull out arguments.
- var type = request.arguments.type;
- var target = request.arguments.target;
- var line = request.arguments.line;
- var column = request.arguments.column;
- var enabled = IS_UNDEFINED(request.arguments.enabled) ?
- true : request.arguments.enabled;
- var condition = request.arguments.condition;
- var ignoreCount = request.arguments.ignoreCount;
- var groupId = request.arguments.groupId;
-
- // Check for legal arguments.
- if (!type || IS_UNDEFINED(target)) {
- response.failed('Missing argument "type" or "target"');
- return;
- }
- if (type != 'function' && type != 'handle' &&
- type != 'script' && type != 'scriptId') {
- response.failed('Illegal type "' + type + '"');
- return;
- }
-
- // Either function or script break point.
- var break_point_number;
- if (type == 'function') {
- // Handle function break point.
- if (!IS_STRING(target)) {
- response.failed('Argument "target" is not a string value');
- return;
- }
- var f;
- try {
- // Find the function through a global evaluate.
- f = this.exec_state_.evaluateGlobal(target).value();
- } catch (e) {
- response.failed('Error: "' + %ToString(e) +
- '" evaluating "' + target + '"');
- return;
- }
- if (!IS_FUNCTION(f)) {
- response.failed('"' + target + '" does not evaluate to a function');
- return;
- }
-
- // 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 =
- Debug.setScriptBreakPointByName(target, line, column, condition,
- groupId);
- } else { // type == 'scriptId.
- break_point_number =
- Debug.setScriptBreakPointById(target, line, column, condition, groupId);
- }
-
- // Set additional break point properties.
- var break_point = Debug.findBreakPoint(break_point_number);
- if (ignoreCount) {
- Debug.changeBreakPointIgnoreCount(break_point_number, ignoreCount);
- }
- if (!enabled) {
- Debug.disableBreakPoint(break_point_number);
- }
-
- // Add the break point number to the response.
- response.body = { type: type,
- breakpoint: break_point_number }
-
- // Add break point information to the response.
- if (break_point instanceof ScriptBreakPoint) {
- if (break_point.type() == Debug.ScriptBreakPointType.ScriptId) {
- response.body.type = 'scriptId';
- response.body.script_id = break_point.script_id();
- } else {
- response.body.type = 'scriptName';
- response.body.script_name = break_point.script_name();
- }
- response.body.line = break_point.line();
- response.body.column = break_point.column();
- } else {
- response.body.type = 'function';
- }
-};
-
-
-DebugCommandProcessor.prototype.changeBreakPointRequest_ = function(request, response) {
- // Check for legal request.
- if (!request.arguments) {
- response.failed('Missing arguments');
- return;
- }
-
- // Pull out arguments.
- var break_point = %ToNumber(request.arguments.breakpoint);
- var enabled = request.arguments.enabled;
- var condition = request.arguments.condition;
- var ignoreCount = request.arguments.ignoreCount;
-
- // Check for legal arguments.
- if (!break_point) {
- response.failed('Missing argument "breakpoint"');
- return;
- }
-
- // Change enabled state if supplied.
- if (!IS_UNDEFINED(enabled)) {
- if (enabled) {
- Debug.enableBreakPoint(break_point);
- } else {
- Debug.disableBreakPoint(break_point);
- }
- }
-
- // Change condition if supplied
- if (!IS_UNDEFINED(condition)) {
- Debug.changeBreakPointCondition(break_point, condition);
- }
-
- // Change ignore count if supplied
- if (!IS_UNDEFINED(ignoreCount)) {
- Debug.changeBreakPointIgnoreCount(break_point, ignoreCount);
- }
-}
-
-
-DebugCommandProcessor.prototype.clearBreakPointGroupRequest_ = function(request, response) {
- // Check for legal request.
- if (!request.arguments) {
- response.failed('Missing arguments');
- return;
- }
-
- // Pull out arguments.
- var group_id = request.arguments.groupId;
-
- // Check for legal arguments.
- if (!group_id) {
- response.failed('Missing argument "groupId"');
- return;
- }
-
- var cleared_break_points = [];
- var new_script_break_points = [];
- for (var i = 0; i < script_break_points.length; i++) {
- var next_break_point = script_break_points[i];
- if (next_break_point.groupId() == group_id) {
- cleared_break_points.push(next_break_point.number());
- next_break_point.clear();
- } else {
- new_script_break_points.push(next_break_point);
- }
- }
- script_break_points = new_script_break_points;
-
- // Add the cleared break point numbers to the response.
- response.body = { breakpoints: cleared_break_points };
-}
-
-
-DebugCommandProcessor.prototype.clearBreakPointRequest_ = function(request, response) {
- // Check for legal request.
- if (!request.arguments) {
- response.failed('Missing arguments');
- return;
- }
-
- // Pull out arguments.
- var break_point = %ToNumber(request.arguments.breakpoint);
-
- // Check for legal arguments.
- if (!break_point) {
- response.failed('Missing argument "breakpoint"');
- return;
- }
-
- // Clear break point.
- Debug.clearBreakPoint(break_point);
-
- // Add the cleared break point number to the response.
- response.body = { breakpoint: break_point }
-}
-
-
-DebugCommandProcessor.prototype.backtraceRequest_ = function(request, response) {
- // Get the number of frames.
- var total_frames = this.exec_state_.frameCount();
-
- // Create simple response if there are no frames.
- if (total_frames == 0) {
- response.body = {
- totalFrames: total_frames
- }
- return;
- }
-
- // Default frame range to include in backtrace.
- var from_index = 0
- var to_index = kDefaultBacktraceLength;
-
- // Get the range from the arguments.
- if (request.arguments) {
- if (request.arguments.fromFrame) {
- from_index = request.arguments.fromFrame;
- }
- if (request.arguments.toFrame) {
- to_index = request.arguments.toFrame;
- }
- if (request.arguments.bottom) {
- var tmp_index = total_frames - from_index;
- from_index = total_frames - to_index
- to_index = tmp_index;
- }
- if (from_index < 0 || to_index < 0) {
- return response.failed('Invalid frame number');
- }
- }
-
- // Adjust the index.
- to_index = Math.min(total_frames, to_index);
-
- if (to_index <= from_index) {
- var error = 'Invalid frame range';
- return response.failed(error);
- }
-
- // Create the response body.
- var frames = [];
- for (var i = from_index; i < to_index; i++) {
- frames.push(this.exec_state_.frame(i));
- }
- response.body = {
- fromFrame: from_index,
- toFrame: to_index,
- totalFrames: total_frames,
- frames: frames
- }
-};
-
-
-DebugCommandProcessor.prototype.backtracec = function(cmd, args) {
- return this.exec_state_.cframesValue();
-};
-
-
-DebugCommandProcessor.prototype.frameRequest_ = function(request, response) {
- // No frames no source.
- if (this.exec_state_.frameCount() == 0) {
- return response.failed('No frames');
- }
-
- // With no arguments just keep the selected frame.
- if (request.arguments) {
- var index = request.arguments.number;
- if (index < 0 || this.exec_state_.frameCount() <= index) {
- return response.failed('Invalid frame number');
- }
-
- this.exec_state_.setSelectedFrame(request.arguments.number);
- }
- response.body = this.exec_state_.frame();
-};
-
-
-DebugCommandProcessor.prototype.frameForScopeRequest_ = function(request) {
- // Get the frame for which the scope or scopes are requested. With no frameNumber
- // argument use the currently selected frame.
- if (request.arguments && !IS_UNDEFINED(request.arguments.frameNumber)) {
- frame_index = request.arguments.frameNumber;
- if (frame_index < 0 || this.exec_state_.frameCount() <= frame_index) {
- return response.failed('Invalid frame number');
- }
- return this.exec_state_.frame(frame_index);
- } else {
- return this.exec_state_.frame();
- }
-}
-
-
-DebugCommandProcessor.prototype.scopesRequest_ = function(request, response) {
- // No frames no scopes.
- if (this.exec_state_.frameCount() == 0) {
- return response.failed('No scopes');
- }
-
- // Get the frame for which the scopes are requested.
- var frame = this.frameForScopeRequest_(request);
-
- // Fill all scopes for this frame.
- var total_scopes = frame.scopeCount();
- var scopes = [];
- for (var i = 0; i < total_scopes; i++) {
- scopes.push(frame.scope(i));
- }
- response.body = {
- fromScope: 0,
- toScope: total_scopes,
- totalScopes: total_scopes,
- scopes: scopes
- }
-};
-
-
-DebugCommandProcessor.prototype.scopeRequest_ = function(request, response) {
- // No frames no scopes.
- if (this.exec_state_.frameCount() == 0) {
- return response.failed('No scopes');
- }
-
- // Get the frame for which the scope is requested.
- var frame = this.frameForScopeRequest_(request);
-
- // With no scope argument just return top scope.
- var scope_index = 0;
- if (request.arguments && !IS_UNDEFINED(request.arguments.number)) {
- scope_index = %ToNumber(request.arguments.number);
- if (scope_index < 0 || frame.scopeCount() <= scope_index) {
- return response.failed('Invalid scope number');
- }
- }
-
- response.body = frame.scope(scope_index);
-};
-
-
-DebugCommandProcessor.prototype.evaluateRequest_ = function(request, response) {
- if (!request.arguments) {
- return response.failed('Missing arguments');
- }
-
- // Pull out arguments.
- var expression = request.arguments.expression;
- var frame = request.arguments.frame;
- var global = request.arguments.global;
- var disable_break = request.arguments.disable_break;
-
- // The expression argument could be an integer so we convert it to a
- // string.
- try {
- expression = String(expression);
- } catch(e) {
- return response.failed('Failed to convert expression argument to string');
- }
-
- // Check for legal arguments.
- if (!IS_UNDEFINED(frame) && global) {
- return response.failed('Arguments "frame" and "global" are exclusive');
- }
-
- // Global evaluate.
- if (global) {
- // Evaluate in the global context.
- response.body =
- this.exec_state_.evaluateGlobal(expression, Boolean(disable_break));
- return;
- }
-
- // Default value for disable_break is true.
- if (IS_UNDEFINED(disable_break)) {
- disable_break = true;
- }
-
- // No frames no evaluate in frame.
- if (this.exec_state_.frameCount() == 0) {
- return response.failed('No frames');
- }
-
- // Check whether a frame was specified.
- if (!IS_UNDEFINED(frame)) {
- var frame_number = %ToNumber(frame);
- if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
- return response.failed('Invalid frame "' + frame + '"');
- }
- // Evaluate in the specified frame.
- response.body = this.exec_state_.frame(frame_number).evaluate(
- expression, Boolean(disable_break));
- return;
- } else {
- // Evaluate in the selected frame.
- response.body = this.exec_state_.frame().evaluate(
- expression, Boolean(disable_break));
- return;
- }
-};
-
-
-DebugCommandProcessor.prototype.lookupRequest_ = function(request, response) {
- if (!request.arguments) {
- return response.failed('Missing arguments');
- }
-
- // Pull out arguments.
- var handles = request.arguments.handles;
-
- // Check for legal arguments.
- if (IS_UNDEFINED(handles)) {
- return response.failed('Argument "handles" missing');
- }
-
- // Set 'includeSource' option for script lookup.
- if (!IS_UNDEFINED(request.arguments.includeSource)) {
- includeSource = %ToBoolean(request.arguments.includeSource);
- response.setOption('includeSource', includeSource);
- }
-
- // 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;
-};
-
-
-DebugCommandProcessor.prototype.referencesRequest_ =
- function(request, response) {
- if (!request.arguments) {
- return response.failed('Missing arguments');
- }
-
- // Pull out arguments.
- var type = request.arguments.type;
- var handle = request.arguments.handle;
-
- // Check for legal arguments.
- if (IS_UNDEFINED(type)) {
- return response.failed('Argument "type" missing');
- }
- if (IS_UNDEFINED(handle)) {
- return response.failed('Argument "handle" missing');
- }
- if (type != 'referencedBy' && type != 'constructedBy') {
- return response.failed('Invalid type "' + type + '"');
- }
-
- // Lookup handle and return objects with references the object.
- var mirror = LookupMirror(handle);
- if (mirror) {
- if (type == 'referencedBy') {
- response.body = mirror.referencedBy();
- } else {
- response.body = mirror.constructedBy();
- }
- } else {
- return response.failed('Object #' + handle + '# not found');
- }
-};
-
-
-DebugCommandProcessor.prototype.sourceRequest_ = function(request, response) {
- // No frames no source.
- if (this.exec_state_.frameCount() == 0) {
- return response.failed('No source');
- }
-
- var from_line;
- var to_line;
- var frame = this.exec_state_.frame();
- if (request.arguments) {
- // Pull out arguments.
- from_line = request.arguments.fromLine;
- to_line = request.arguments.toLine;
-
- if (!IS_UNDEFINED(request.arguments.frame)) {
- var frame_number = %ToNumber(request.arguments.frame);
- if (frame_number < 0 || frame_number >= this.exec_state_.frameCount()) {
- return response.failed('Invalid frame "' + frame + '"');
- }
- frame = this.exec_state_.frame(frame_number);
- }
- }
-
- // Get the script selected.
- var script = frame.func().script();
- if (!script) {
- return response.failed('No source');
- }
-
- // Get the source slice and fill it into the response.
- var slice = script.sourceSlice(from_line, to_line);
- if (!slice) {
- return response.failed('Invalid line interval');
- }
- response.body = {};
- response.body.source = slice.sourceText();
- response.body.fromLine = slice.from_line;
- response.body.toLine = slice.to_line;
- response.body.fromPosition = slice.from_position;
- response.body.toPosition = slice.to_position;
- response.body.totalLines = script.lineCount();
-};
-
-
-DebugCommandProcessor.prototype.scriptsRequest_ = function(request, response) {
- var types = ScriptTypeFlag(Debug.ScriptType.Normal);
- var includeSource = false;
- var idsToInclude = null;
- if (request.arguments) {
- // Pull out arguments.
- if (!IS_UNDEFINED(request.arguments.types)) {
- types = %ToNumber(request.arguments.types);
- if (isNaN(types) || types < 0) {
- return response.failed('Invalid types "' + request.arguments.types + '"');
- }
- }
-
- if (!IS_UNDEFINED(request.arguments.includeSource)) {
- includeSource = %ToBoolean(request.arguments.includeSource);
- response.setOption('includeSource', includeSource);
- }
-
- if (IS_ARRAY(request.arguments.ids)) {
- idsToInclude = {};
- var ids = request.arguments.ids;
- for (var i = 0; i < ids.length; i++) {
- idsToInclude[ids[i]] = true;
- }
- }
- }
-
- // Collect all scripts in the heap.
- var scripts = %DebugGetLoadedScripts();
-
- response.body = [];
-
- for (var i = 0; i < scripts.length; i++) {
- if (idsToInclude && !idsToInclude[scripts[i].id]) {
- continue;
- }
- if (types & ScriptTypeFlag(scripts[i].type)) {
- response.body.push(MakeMirror(scripts[i]));
- }
- }
-};
-
-
-DebugCommandProcessor.prototype.threadsRequest_ = function(request, response) {
- // Get the number of threads.
- var total_threads = this.exec_state_.threadCount();
-
- // Get information for all threads.
- var threads = [];
- for (var i = 0; i < total_threads; i++) {
- var details = %GetThreadDetails(this.exec_state_.break_id, i);
- var thread_info = { current: details[0],
- id: details[1]
- }
- threads.push(thread_info);
- }
-
- // Create the response body.
- response.body = {
- totalThreads: total_threads,
- threads: threads
- }
-};
-
-
-DebugCommandProcessor.prototype.suspendRequest_ = function(request, response) {
- response.running = false;
-};
-
-
-DebugCommandProcessor.prototype.versionRequest_ = function(request, response) {
- response.body = {
- V8Version: %GetV8Version()
- }
-};
-
-
-DebugCommandProcessor.prototype.profileRequest_ = function(request, response) {
- if (!request.arguments) {
- return response.failed('Missing arguments');
- }
- var modules = parseInt(request.arguments.modules);
- if (isNaN(modules)) {
- return response.failed('Modules is not an integer');
- }
- if (request.arguments.command == 'resume') {
- %ProfilerResume(modules);
- } else if (request.arguments.command == 'pause') {
- %ProfilerPause(modules);
- } else {
- return response.failed('Unknown command');
- }
- response.body = {};
-};
-
-
-// Check whether the previously processed command caused the VM to become
-// running.
-DebugCommandProcessor.prototype.isRunning = function() {
- return this.running_;
-}
-
-
-DebugCommandProcessor.prototype.systemBreak = function(cmd, args) {
- return %SystemBreak();
-};
-
-
-function NumberToHex8Str(n) {
- var r = "";
- for (var i = 0; i < 8; ++i) {
- var c = hexCharArray[n & 0x0F]; // hexCharArray is defined in uri.js
- r = c + r;
- n = n >>> 4;
- }
- return r;
-};
-
-DebugCommandProcessor.prototype.formatCFrames = function(cframes_value) {
- var result = "";
- if (cframes_value == null || cframes_value.length == 0) {
- result += "(stack empty)";
- } else {
- for (var i = 0; i < cframes_value.length; ++i) {
- if (i != 0) result += "\n";
- result += this.formatCFrame(cframes_value[i]);
- }
- }
- return result;
-};
-
-
-DebugCommandProcessor.prototype.formatCFrame = function(cframe_value) {
- var result = "";
- result += "0x" + NumberToHex8Str(cframe_value.address);
- if (!IS_UNDEFINED(cframe_value.text)) {
- result += " " + cframe_value.text;
- }
- return result;
-}
-
-
-/**
- * Convert an Object to its debugger protocol representation. The representation
- * may be serilized to a JSON object using JSON.stringify().
- * This implementation simply runs through all string property names, converts
- * each property value to a protocol value and adds the property to the result
- * object. For type "object" the function will be called recursively. Note that
- * circular structures will cause infinite recursion.
- * @param {Object} object The object to format as protocol object.
- * @param {MirrorSerializer} mirror_serializer The serializer to use if any
- * mirror objects are encountered.
- * @return {Object} Protocol object value.
- */
-function ObjectToProtocolObject_(object, mirror_serializer) {
- var content = {};
- for (var key in object) {
- // Only consider string keys.
- if (typeof key == 'string') {
- // Format the value based on its type.
- var property_value_json = ValueToProtocolValue_(object[key],
- mirror_serializer);
- // Add the property if relevant.
- if (!IS_UNDEFINED(property_value_json)) {
- content[key] = property_value_json;
- }
- }
- }
-
- return content;
-}
-
-
-/**
- * Convert an array to its debugger protocol representation. It will convert
- * each array element to a protocol value.
- * @param {Array} array The array to format as protocol array.
- * @param {MirrorSerializer} mirror_serializer The serializer to use if any
- * mirror objects are encountered.
- * @return {Array} Protocol array value.
- */
-function ArrayToProtocolArray_(array, mirror_serializer) {
- var json = [];
- for (var i = 0; i < array.length; i++) {
- json.push(ValueToProtocolValue_(array[i], mirror_serializer));
- }
- return json;
-}
-
-
-/**
- * Convert a value to its debugger protocol representation.
- * @param {*} value The value to format as protocol value.
- * @param {MirrorSerializer} mirror_serializer The serializer to use if any
- * mirror objects are encountered.
- * @return {*} Protocol value.
- */
-function ValueToProtocolValue_(value, mirror_serializer) {
- // Format the value based on its type.
- var json;
- switch (typeof value) {
- case 'object':
- if (value instanceof Mirror) {
- json = mirror_serializer.serializeValue(value);
- } else if (IS_ARRAY(value)){
- json = ArrayToProtocolArray_(value, mirror_serializer);
- } else {
- json = ObjectToProtocolObject_(value, mirror_serializer);
- }
- break;
-
- case 'boolean':
- case 'string':
- case 'number':
- json = value;
- break
-
- default:
- json = null;
- }
- return json;
-}
diff --git a/src/debug.cc b/src/debug.cc
index 68f8d1e2..c71a98fc 100644
--- a/src/debug.cc
+++ b/src/debug.cc
@@ -31,6 +31,7 @@
#include "arguments.h"
#include "bootstrapper.h"
#include "code-stubs.h"
+#include "codegen.h"
#include "compilation-cache.h"
#include "compiler.h"
#include "debug.h"
@@ -453,15 +454,7 @@ void BreakLocationIterator::ClearDebugBreakAtIC() {
bool BreakLocationIterator::IsDebuggerStatement() {
- if (RelocInfo::IsCodeTarget(rmode())) {
- Address target = original_rinfo()->target_address();
- Code* code = Code::GetCodeFromTargetAddress(target);
- if (code->kind() == Code::STUB) {
- CodeStub::Major major_key = code->major_key();
- return (major_key == CodeStub::DebuggerStatement);
- }
- }
- return false;
+ return RelocInfo::DEBUG_BREAK == rmode();
}
@@ -696,6 +689,7 @@ bool Debug::CompileDebuggerScript(int index) {
0,
NULL,
NULL,
+ Handle<String>::null(),
NATIVES_CODE);
FLAG_allow_natives_syntax = allow_natives_syntax;
diff --git a/src/execution.cc b/src/execution.cc
index a79af237..20684136 100644
--- a/src/execution.cc
+++ b/src/execution.cc
@@ -91,7 +91,7 @@ static Handle<Object> Invoke(bool construct,
JSEntryFunction entry = FUNCTION_CAST<JSEntryFunction>(code->entry());
// Call the function through the right JS entry stub.
- byte* entry_address= func->code()->entry();
+ byte* entry_address = func->code()->entry();
JSFunction* function = *func;
Object* receiver_pointer = *receiver;
value = CALL_GENERATED_CODE(entry, entry_address, function,
diff --git a/src/fast-codegen.cc b/src/fast-codegen.cc
index 36d7297f..602d6b88 100644
--- a/src/fast-codegen.cc
+++ b/src/fast-codegen.cc
@@ -89,10 +89,10 @@ void FastCodeGenSyntaxChecker::VisitDeclarations(
void FastCodeGenSyntaxChecker::VisitStatements(ZoneList<Statement*>* stmts) {
- for (int i = 0, len = stmts->length(); i < len; i++) {
- Visit(stmts->at(i));
- CHECK_BAILOUT;
+ if (stmts->length() != 1) {
+ BAILOUT("Function body is not a singleton statement.");
}
+ Visit(stmts->at(0));
}
@@ -220,8 +220,16 @@ void FastCodeGenSyntaxChecker::VisitVariableProxy(VariableProxy* expr) {
if (info()->has_global_object()) {
LookupResult lookup;
info()->global_object()->Lookup(*expr->name(), &lookup);
- if (!lookup.IsValid() || !lookup.IsDontDelete()) {
- BAILOUT("Non-existing or deletable global variable");
+ if (!lookup.IsProperty()) {
+ BAILOUT("Non-existing global variable");
+ }
+ // We do not handle global variables with accessors or interceptors.
+ if (lookup.type() != NORMAL) {
+ BAILOUT("Global variable with accessors or interceptors.");
+ }
+ // We do not handle deletable global variables.
+ if (!lookup.IsDontDelete()) {
+ BAILOUT("Deletable global variable");
}
}
}
@@ -276,6 +284,9 @@ void FastCodeGenSyntaxChecker::VisitAssignment(Assignment* expr) {
Handle<String> name = Handle<String>::cast(key->handle());
LookupResult lookup;
receiver->Lookup(*name, &lookup);
+ if (!lookup.IsProperty()) {
+ BAILOUT("Assigned property not found at compile time");
+ }
if (lookup.holder() != *receiver) BAILOUT("Non-own property assignment");
if (!lookup.type() == FIELD) BAILOUT("Non-field property assignment");
} else {
@@ -293,7 +304,33 @@ void FastCodeGenSyntaxChecker::VisitThrow(Throw* expr) {
void FastCodeGenSyntaxChecker::VisitProperty(Property* expr) {
- BAILOUT("Property");
+ // We support named this property references.
+ VariableProxy* proxy = expr->obj()->AsVariableProxy();
+ if (proxy == NULL || !proxy->var()->is_this()) {
+ BAILOUT("Non-this-property reference");
+ }
+ if (!expr->key()->IsPropertyName()) {
+ BAILOUT("Non-named-property reference");
+ }
+
+ // We will only specialize for fields on the object itself.
+ // Expression::IsPropertyName implies that the name is a literal
+ // symbol but we do not assume that.
+ Literal* key = expr->key()->AsLiteral();
+ if (key != NULL && key->handle()->IsString()) {
+ Handle<Object> receiver = info()->receiver();
+ Handle<String> name = Handle<String>::cast(key->handle());
+ LookupResult lookup;
+ receiver->Lookup(*name, &lookup);
+ if (!lookup.IsProperty()) {
+ BAILOUT("Referenced property not found at compile time");
+ }
+ if (lookup.holder() != *receiver) BAILOUT("Non-own property reference");
+ if (!lookup.type() == FIELD) BAILOUT("Non-field property reference");
+ } else {
+ UNREACHABLE();
+ BAILOUT("Unexpected non-string-literal property key");
+ }
}
@@ -323,7 +360,58 @@ void FastCodeGenSyntaxChecker::VisitCountOperation(CountOperation* expr) {
void FastCodeGenSyntaxChecker::VisitBinaryOperation(BinaryOperation* expr) {
- BAILOUT("BinaryOperation");
+ // We support bitwise OR.
+ switch (expr->op()) {
+ case Token::COMMA:
+ BAILOUT("BinaryOperation COMMA");
+ case Token::OR:
+ BAILOUT("BinaryOperation OR");
+ case Token::AND:
+ BAILOUT("BinaryOperation AND");
+
+ case Token::BIT_OR:
+ // We support expressions nested on the left because they only require
+ // a pair of registers to keep all intermediate values in registers
+ // (i.e., the expression stack has height no more than two).
+ if (!expr->right()->IsLeaf()) BAILOUT("expression nested on right");
+
+ // We do not allow subexpressions with side effects because we
+ // (currently) bail out to the beginning of the full function. The
+ // only expressions with side effects that we would otherwise handle
+ // are assignments.
+ if (expr->left()->AsAssignment() != NULL ||
+ expr->right()->AsAssignment() != NULL) {
+ BAILOUT("subexpression of binary operation has side effects");
+ }
+
+ Visit(expr->left());
+ CHECK_BAILOUT;
+ Visit(expr->right());
+ break;
+
+ case Token::BIT_XOR:
+ BAILOUT("BinaryOperation BIT_XOR");
+ case Token::BIT_AND:
+ BAILOUT("BinaryOperation BIT_AND");
+ case Token::SHL:
+ BAILOUT("BinaryOperation SHL");
+ case Token::SAR:
+ BAILOUT("BinaryOperation SAR");
+ case Token::SHR:
+ BAILOUT("BinaryOperation SHR");
+ case Token::ADD:
+ BAILOUT("BinaryOperation ADD");
+ case Token::SUB:
+ BAILOUT("BinaryOperation SUB");
+ case Token::MUL:
+ BAILOUT("BinaryOperation MUL");
+ case Token::DIV:
+ BAILOUT("BinaryOperation DIV");
+ case Token::MOD:
+ BAILOUT("BinaryOperation MOD");
+ default:
+ UNREACHABLE();
+ }
}
@@ -348,6 +436,9 @@ Handle<Code> FastCodeGenerator::MakeCode(CompilationInfo* info) {
AstLabeler labeler;
labeler.Label(info);
+ LivenessAnalyzer analyzer;
+ analyzer.Analyze(info->function());
+
CodeGenerator::MakeCodePrologue(info);
const int kInitialBufferSize = 4 * KB;
@@ -365,7 +456,8 @@ Handle<Code> FastCodeGenerator::MakeCode(CompilationInfo* info) {
// macro assembler.
CodeGenerator cgen(&masm);
CodeGeneratorScope scope(&cgen);
- cgen.Generate(info, CodeGenerator::SECONDARY);
+ info->set_mode(CompilationInfo::SECONDARY);
+ cgen.Generate(info);
if (cgen.HasStackOverflow()) {
ASSERT(!Top::has_pending_exception());
return Handle<Code>::null();
@@ -489,21 +581,28 @@ void FastCodeGenerator::VisitSlot(Slot* expr) {
void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
ASSERT(expr->var()->is_global() && !expr->var()->is_this());
- Comment cmnt(masm(), ";; Global");
- if (FLAG_print_ir) {
- SmartPointer<char> name = expr->name()->ToCString();
- PrintF("%d: t%d = Global(%s)\n", expr->num(), expr->num(), *name);
- }
-
// Check if we can compile a global variable load directly from the cell.
ASSERT(info()->has_global_object());
LookupResult lookup;
info()->global_object()->Lookup(*expr->name(), &lookup);
- // We only support DontDelete properties for now.
- ASSERT(lookup.IsValid());
+ // We only support normal (non-accessor/interceptor) DontDelete properties
+ // for now.
+ ASSERT(lookup.IsProperty());
+ ASSERT_EQ(NORMAL, lookup.type());
ASSERT(lookup.IsDontDelete());
Handle<Object> cell(info()->global_object()->GetPropertyCell(&lookup));
- EmitGlobalVariableLoad(cell);
+
+ // Global variable lookups do not have side effects, so we do not need to
+ // emit code if we are in an effect context.
+ if (!destination().is(no_reg)) {
+ Comment cmnt(masm(), ";; Global");
+ if (FLAG_print_ir) {
+ SmartPointer<char> name = expr->name()->ToCString();
+ PrintF("%d: t%d = Global(%s) // last_use = %d\n", expr->num(),
+ expr->num(), *name, expr->var_def()->last_use()->num());
+ }
+ EmitGlobalVariableLoad(cell);
+ }
}
@@ -533,8 +632,13 @@ void FastCodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* expr) {
void FastCodeGenerator::VisitAssignment(Assignment* expr) {
- // Known to be a simple this property assignment.
- Visit(expr->value());
+ // Known to be a simple this property assignment. Effectively a unary
+ // operation.
+ { Register my_destination = destination();
+ set_destination(accumulator0());
+ Visit(expr->value());
+ set_destination(my_destination);
+ }
Property* prop = expr->target()->AsProperty();
ASSERT_NOT_NULL(prop);
@@ -544,11 +648,14 @@ void FastCodeGenerator::VisitAssignment(Assignment* expr) {
Handle<String> name =
Handle<String>::cast(prop->key()->AsLiteral()->handle());
- Comment cmnt(masm(), ";; Store(this)");
+ Comment cmnt(masm(), ";; Store to this");
if (FLAG_print_ir) {
SmartPointer<char> name_string = name->ToCString();
- PrintF("%d: t%d = Store(this, \"%s\", t%d)\n",
- expr->num(), expr->num(), *name_string, expr->value()->num());
+ PrintF("%d: ", expr->num());
+ if (!destination().is(no_reg)) PrintF("t%d = ", expr->num());
+ PrintF("Store(this, \"%s\", t%d) // last_use(this) = %d\n", *name_string,
+ expr->value()->num(),
+ expr->var_def()->last_use()->num());
}
EmitThisPropertyStore(name);
@@ -561,7 +668,22 @@ void FastCodeGenerator::VisitThrow(Throw* expr) {
void FastCodeGenerator::VisitProperty(Property* expr) {
- UNREACHABLE();
+ ASSERT_NOT_NULL(expr->obj()->AsVariableProxy());
+ ASSERT(expr->obj()->AsVariableProxy()->var()->is_this());
+ ASSERT(expr->key()->IsPropertyName());
+ if (!destination().is(no_reg)) {
+ Handle<String> name =
+ Handle<String>::cast(expr->key()->AsLiteral()->handle());
+
+ Comment cmnt(masm(), ";; Load from this");
+ if (FLAG_print_ir) {
+ SmartPointer<char> name_string = name->ToCString();
+ PrintF("%d: t%d = Load(this, \"%s\") // last_use(this) = %d\n",
+ expr->num(), expr->num(), *name_string,
+ expr->var_def()->last_use()->num());
+ }
+ EmitThisPropertyLoad(name);
+ }
}
@@ -591,7 +713,26 @@ void FastCodeGenerator::VisitCountOperation(CountOperation* expr) {
void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
- UNREACHABLE();
+ // We support limited binary operations: bitwise OR only allowed to be
+ // nested on the left.
+ ASSERT(expr->op() == Token::BIT_OR);
+ ASSERT(expr->right()->IsLeaf());
+
+ { Register my_destination = destination();
+ set_destination(accumulator1());
+ Visit(expr->left());
+ set_destination(accumulator0());
+ Visit(expr->right());
+ set_destination(my_destination);
+ }
+
+ Comment cmnt(masm(), ";; BIT_OR");
+ if (FLAG_print_ir) {
+ PrintF("%d: ", expr->num());
+ if (!destination().is(no_reg)) PrintF("t%d = ", expr->num());
+ PrintF("BIT_OR(t%d, t%d)\n", expr->left()->num(), expr->right()->num());
+ }
+ EmitBitOr();
}
diff --git a/src/fast-codegen.h b/src/fast-codegen.h
index cbcb5bfa..e96daf65 100644
--- a/src/fast-codegen.h
+++ b/src/fast-codegen.h
@@ -28,10 +28,15 @@
#ifndef V8_FAST_CODEGEN_H_
#define V8_FAST_CODEGEN_H_
+#if V8_TARGET_ARCH_IA32
+#include "ia32/fast-codegen-ia32.h"
+#else
+
#include "v8.h"
#include "ast.h"
#include "compiler.h"
+#include "list.h"
namespace v8 {
namespace internal {
@@ -65,7 +70,9 @@ class FastCodeGenSyntaxChecker: public AstVisitor {
class FastCodeGenerator: public AstVisitor {
public:
- explicit FastCodeGenerator(MacroAssembler* masm) : masm_(masm), info_(NULL) {}
+ explicit FastCodeGenerator(MacroAssembler* masm)
+ : masm_(masm), info_(NULL), destination_(no_reg), smi_bits_(0) {
+ }
static Handle<Code> MakeCode(CompilationInfo* info);
@@ -74,42 +81,73 @@ class FastCodeGenerator: public AstVisitor {
private:
MacroAssembler* masm() { return masm_; }
CompilationInfo* info() { return info_; }
- Label* bailout() { return &bailout_; }
+
+ Register destination() { return destination_; }
+ void set_destination(Register reg) { destination_ = reg; }
FunctionLiteral* function() { return info_->function(); }
Scope* scope() { return info_->scope(); }
+ // Platform-specific fixed registers, all guaranteed distinct.
+ Register accumulator0();
+ Register accumulator1();
+ Register scratch0();
+ Register scratch1();
+ Register receiver_reg();
+ Register context_reg();
+
+ Register other_accumulator(Register reg) {
+ ASSERT(reg.is(accumulator0()) || reg.is(accumulator1()));
+ return (reg.is(accumulator0())) ? accumulator1() : accumulator0();
+ }
+
+ // Flags are true if the respective register is statically known to hold a
+ // smi. We do not track every register, only the accumulator registers.
+ bool is_smi(Register reg) {
+ ASSERT(!reg.is(no_reg));
+ return (smi_bits_ & reg.bit()) != 0;
+ }
+ void set_as_smi(Register reg) {
+ ASSERT(!reg.is(no_reg));
+ smi_bits_ = smi_bits_ | reg.bit();
+ }
+ void clear_as_smi(Register reg) {
+ ASSERT(!reg.is(no_reg));
+ smi_bits_ = smi_bits_ & ~reg.bit();
+ }
+
// AST node visit functions.
#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
AST_NODE_LIST(DECLARE_VISIT)
#undef DECLARE_VISIT
- // Emit code to load the receiver from the stack into a given register.
- void EmitLoadReceiver(Register reg);
-
- // Emit code to check that the receiver has the same map as the
- // compile-time receiver. Receiver is expected in {ia32-edx, x64-rdx,
- // arm-r1}. Emit a branch to the (single) bailout label if check fails.
- void EmitReceiverMapCheck();
+ // Emit code to load the receiver from the stack into receiver_reg.
+ void EmitLoadReceiver();
- // Emit code to check that the global object has the same map as the
- // global object seen at compile time.
- void EmitGlobalMapCheck();
-
- // Emit code to load a global variable directly from a global
- // property cell into {ia32-eax, x64-rax, arm-r0}.
+ // Emit code to load a global variable directly from a global property
+ // cell into the destination register.
void EmitGlobalVariableLoad(Handle<Object> cell);
// Emit a store to an own property of this. The stored value is expected
- // in {ia32-eax, x64-rax, arm-r0} and the receiver in {is32-edx, x64-rdx,
- // arm-r1}. Both are preserve.
+ // in accumulator0 and the receiver in receiver_reg. The receiver
+ // register is preserved and the result (the stored value) is left in the
+ // destination register.
void EmitThisPropertyStore(Handle<String> name);
- MacroAssembler* masm_;
+ // Emit a load from an own property of this. The receiver is expected in
+ // receiver_reg. The receiver register is preserved and the result is
+ // left in the destination register.
+ void EmitThisPropertyLoad(Handle<String> name);
- CompilationInfo* info_;
+ // Emit a bitwise or operation. The left operand is in accumulator1 and
+ // the right is in accumulator0. The result should be left in the
+ // destination register.
+ void EmitBitOr();
- Label bailout_;
+ MacroAssembler* masm_;
+ CompilationInfo* info_;
+ Register destination_;
+ uint32_t smi_bits_;
DISALLOW_COPY_AND_ASSIGN(FastCodeGenerator);
};
@@ -117,4 +155,6 @@ class FastCodeGenerator: public AstVisitor {
} } // namespace v8::internal
+#endif // V8_TARGET_ARCH_IA32
+
#endif // V8_FAST_CODEGEN_H_
diff --git a/src/flag-definitions.h b/src/flag-definitions.h
index 9afdea4c..dbb9ce72 100644
--- a/src/flag-definitions.h
+++ b/src/flag-definitions.h
@@ -384,7 +384,7 @@ DEFINE_bool(prof_lazy, false,
DEFINE_bool(log_regexp, false, "Log regular expression execution.")
DEFINE_bool(sliding_state_window, false,
"Update sliding state window counters.")
-DEFINE_string(logfile, "/sdcard/v8.log", "Specify the name of the log file.")
+DEFINE_string(logfile, "v8.log", "Specify the name of the log file.")
DEFINE_bool(oprofile, false, "Enable JIT agent for OProfile.")
//
diff --git a/src/frame-element.cc b/src/frame-element.cc
index e6bc2eaf..14555596 100644
--- a/src/frame-element.cc
+++ b/src/frame-element.cc
@@ -32,10 +32,6 @@
namespace v8 {
namespace internal {
-// -------------------------------------------------------------------------
-// FrameElement implementation.
-
-
FrameElement::ZoneObjectList* FrameElement::ConstantList() {
static ZoneObjectList list(10);
return &list;
diff --git a/src/frame-element.h b/src/frame-element.h
index ccdecf1d..5762814f 100644
--- a/src/frame-element.h
+++ b/src/frame-element.h
@@ -28,7 +28,8 @@
#ifndef V8_FRAME_ELEMENT_H_
#define V8_FRAME_ELEMENT_H_
-#include "register-allocator-inl.h"
+#include "number-info.h"
+#include "macro-assembler.h"
namespace v8 {
namespace internal {
@@ -52,11 +53,31 @@ class FrameElement BASE_EMBEDDED {
SYNCED
};
+ inline NumberInfo::Type number_info() {
+ // Copied elements do not have number info. Instead
+ // we have to inspect their backing element in the frame.
+ ASSERT(!is_copy());
+ if (!is_constant()) return NumberInfoField::decode(value_);
+ Handle<Object> value = handle();
+ if (value->IsSmi()) return NumberInfo::kSmi;
+ if (value->IsHeapNumber()) return NumberInfo::kHeapNumber;
+ return NumberInfo::kUnknown;
+ }
+
+ inline void set_number_info(NumberInfo::Type info) {
+ // Copied elements do not have number info. Instead
+ // we have to inspect their backing element in the frame.
+ ASSERT(!is_copy());
+ value_ = value_ & ~NumberInfoField::mask();
+ value_ = value_ | NumberInfoField::encode(info);
+ }
+
// The default constructor creates an invalid frame element.
FrameElement() {
value_ = TypeField::encode(INVALID)
| CopiedField::encode(false)
| SyncedField::encode(false)
+ | NumberInfoField::encode(NumberInfo::kUninitialized)
| DataField::encode(0);
}
@@ -67,15 +88,16 @@ class FrameElement BASE_EMBEDDED {
}
// Factory function to construct an in-memory frame element.
- static FrameElement MemoryElement() {
- FrameElement result(MEMORY, no_reg, SYNCED);
+ static FrameElement MemoryElement(NumberInfo::Type info) {
+ FrameElement result(MEMORY, no_reg, SYNCED, info);
return result;
}
// Factory function to construct an in-register frame element.
static FrameElement RegisterElement(Register reg,
- SyncFlag is_synced) {
- return FrameElement(REGISTER, reg, is_synced);
+ SyncFlag is_synced,
+ NumberInfo::Type info) {
+ return FrameElement(REGISTER, reg, is_synced, info);
}
// Factory function to construct a frame element whose value is known at
@@ -185,10 +207,14 @@ class FrameElement BASE_EMBEDDED {
};
// Used to construct memory and register elements.
- FrameElement(Type type, Register reg, SyncFlag is_synced) {
+ FrameElement(Type type,
+ Register reg,
+ SyncFlag is_synced,
+ NumberInfo::Type info) {
value_ = TypeField::encode(type)
| CopiedField::encode(false)
| SyncedField::encode(is_synced != NOT_SYNCED)
+ | NumberInfoField::encode(info)
| DataField::encode(reg.code_ > 0 ? reg.code_ : 0);
}
@@ -197,6 +223,7 @@ class FrameElement BASE_EMBEDDED {
value_ = TypeField::encode(CONSTANT)
| CopiedField::encode(false)
| SyncedField::encode(is_synced != NOT_SYNCED)
+ | NumberInfoField::encode(NumberInfo::kUninitialized)
| DataField::encode(ConstantList()->length());
ConstantList()->Add(value);
}
@@ -223,9 +250,10 @@ class FrameElement BASE_EMBEDDED {
uint32_t value_;
class TypeField: public BitField<Type, 0, 3> {};
- class CopiedField: public BitField<uint32_t, 3, 1> {};
- class SyncedField: public BitField<uint32_t, 4, 1> {};
- class DataField: public BitField<uint32_t, 5, 32 - 6> {};
+ class CopiedField: public BitField<bool, 3, 1> {};
+ class SyncedField: public BitField<bool, 4, 1> {};
+ class NumberInfoField: public BitField<NumberInfo::Type, 5, 3> {};
+ class DataField: public BitField<uint32_t, 8, 32 - 8> {};
friend class VirtualFrame;
};
diff --git a/src/frames.cc b/src/frames.cc
index 05507406..06896ea7 100644
--- a/src/frames.cc
+++ b/src/frames.cc
@@ -410,12 +410,7 @@ Object*& ExitFrame::code_slot() const {
Code* ExitFrame::code() const {
- Object* code = code_slot();
- if (code->IsSmi()) {
- return Heap::debugger_statement_code();
- } else {
- return Code::cast(code);
- }
+ return Code::cast(code_slot());
}
diff --git a/src/full-codegen.cc b/src/full-codegen.cc
index 22510e9d..63714392 100644
--- a/src/full-codegen.cc
+++ b/src/full-codegen.cc
@@ -32,6 +32,7 @@
#include "full-codegen.h"
#include "stub-cache.h"
#include "debug.h"
+#include "liveedit.h"
namespace v8 {
namespace internal {
@@ -448,6 +449,8 @@ Handle<Code> FullCodeGenerator::MakeCode(CompilationInfo* info) {
CodeGenerator::MakeCodePrologue(info);
const int kInitialBufferSize = 4 * KB;
MacroAssembler masm(NULL, kInitialBufferSize);
+ LiveEditFunctionTracker live_edit_tracker(info->function());
+
FullCodeGenerator cgen(&masm);
cgen.Generate(info, PRIMARY);
if (cgen.HasStackOverflow()) {
@@ -455,7 +458,9 @@ Handle<Code> FullCodeGenerator::MakeCode(CompilationInfo* info) {
return Handle<Code>::null();
}
Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, NOT_IN_LOOP);
- return CodeGenerator::MakeCodeEpilogue(&masm, flags, info);
+ Handle<Code> result = CodeGenerator::MakeCodeEpilogue(&masm, flags, info);
+ live_edit_tracker.RecordFunctionCode(result);
+ return result;
}
@@ -986,8 +991,7 @@ void FullCodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) {
Comment cmnt(masm_, "[ DebuggerStatement");
SetStatementPosition(stmt);
- DebuggerStatementStub ces;
- __ CallStub(&ces);
+ __ DebugBreak();
// Ignore the return value.
#endif
}
@@ -1032,86 +1036,6 @@ void FullCodeGenerator::VisitLiteral(Literal* expr) {
}
-void FullCodeGenerator::VisitAssignment(Assignment* expr) {
- Comment cmnt(masm_, "[ Assignment");
- ASSERT(expr->op() != Token::INIT_CONST);
- // Left-hand side can only be a property, a global or a (parameter or local)
- // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
- enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
- LhsKind assign_type = VARIABLE;
- Property* prop = expr->target()->AsProperty();
- if (prop != NULL) {
- assign_type =
- (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
- }
-
- // Evaluate LHS expression.
- switch (assign_type) {
- case VARIABLE:
- // Nothing to do here.
- break;
- case NAMED_PROPERTY:
- VisitForValue(prop->obj(), kStack);
- break;
- case KEYED_PROPERTY:
- VisitForValue(prop->obj(), kStack);
- VisitForValue(prop->key(), kStack);
- break;
- }
-
- // If we have a compound assignment: Get value of LHS expression and
- // store in on top of the stack.
- if (expr->is_compound()) {
- Location saved_location = location_;
- location_ = kStack;
- switch (assign_type) {
- case VARIABLE:
- EmitVariableLoad(expr->target()->AsVariableProxy()->var(),
- Expression::kValue);
- break;
- case NAMED_PROPERTY:
- EmitNamedPropertyLoad(prop);
- __ push(result_register());
- break;
- case KEYED_PROPERTY:
- EmitKeyedPropertyLoad(prop);
- __ push(result_register());
- break;
- }
- location_ = saved_location;
- }
-
- // Evaluate RHS expression.
- Expression* rhs = expr->value();
- VisitForValue(rhs, kAccumulator);
-
- // If we have a compound assignment: Apply operator.
- if (expr->is_compound()) {
- Location saved_location = location_;
- location_ = kAccumulator;
- EmitBinaryOp(expr->binary_op(), Expression::kValue);
- location_ = saved_location;
- }
-
- // Record source position before possible IC call.
- SetSourcePosition(expr->position());
-
- // Store the value.
- switch (assign_type) {
- case VARIABLE:
- EmitVariableAssignment(expr->target()->AsVariableProxy()->var(),
- context_);
- break;
- case NAMED_PROPERTY:
- EmitNamedPropertyAssignment(expr);
- break;
- case KEYED_PROPERTY:
- EmitKeyedPropertyAssignment(expr);
- break;
- }
-}
-
-
void FullCodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* expr) {
// Call runtime routine to allocate the catch extension object and
// assign the exception value to the catch variable.
diff --git a/src/handles.cc b/src/handles.cc
index 05f561ef..c9a28774 100644
--- a/src/handles.cc
+++ b/src/handles.cc
@@ -309,6 +309,12 @@ Handle<Object> GetPrototype(Handle<Object> obj) {
}
+Handle<Object> SetPrototype(Handle<JSObject> obj, Handle<Object> value) {
+ const bool skip_hidden_prototypes = false;
+ CALL_HEAP_FUNCTION(obj->SetPrototype(*value, skip_hidden_prototypes), Object);
+}
+
+
Handle<Object> GetHiddenProperties(Handle<JSObject> obj,
bool create_if_needed) {
Object* holder = obj->BypassGlobalProxy();
@@ -486,25 +492,25 @@ void InitScriptLineEnds(Handle<Script> script) {
int GetScriptLineNumber(Handle<Script> script, int code_pos) {
InitScriptLineEnds(script);
AssertNoAllocation no_allocation;
- FixedArray* line_ends_array =
- FixedArray::cast(script->line_ends());
+ FixedArray* line_ends_array = FixedArray::cast(script->line_ends());
const int line_ends_len = line_ends_array->length();
- int line = -1;
- if (line_ends_len > 0 &&
- code_pos <= (Smi::cast(line_ends_array->get(0)))->value()) {
- line = 0;
- } else {
- for (int i = 1; i < line_ends_len; ++i) {
- if ((Smi::cast(line_ends_array->get(i - 1)))->value() < code_pos &&
- code_pos <= (Smi::cast(line_ends_array->get(i)))->value()) {
- line = i;
- break;
- }
+ if (!line_ends_len)
+ return -1;
+
+ if ((Smi::cast(line_ends_array->get(0)))->value() >= code_pos)
+ return script->line_offset()->value();
+
+ int left = 0;
+ int right = line_ends_len;
+ while (int half = (right - left) / 2) {
+ if ((Smi::cast(line_ends_array->get(left + half)))->value() > code_pos) {
+ right -= half;
+ } else {
+ left += half;
}
}
-
- return line != -1 ? line + script->line_offset()->value() : line;
+ return right + script->line_offset()->value();
}
diff --git a/src/handles.h b/src/handles.h
index caa99664..79029095 100644
--- a/src/handles.h
+++ b/src/handles.h
@@ -245,6 +245,8 @@ Handle<Object> GetPropertyWithInterceptor(Handle<JSObject> receiver,
Handle<Object> GetPrototype(Handle<Object> obj);
+Handle<Object> SetPrototype(Handle<JSObject> obj, Handle<Object> value);
+
// Return the object's hidden properties object. If the object has no hidden
// properties and create_if_needed is true, then a new hidden property object
// will be allocated. Otherwise the Heap::undefined_value is returned.
diff --git a/src/heap.cc b/src/heap.cc
index 7263e230..cfb786ac 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -1498,14 +1498,6 @@ void Heap::CreateRegExpCEntryStub() {
#endif
-#ifdef ENABLE_DEBUGGER_SUPPORT
-void Heap::CreateCEntryDebugBreakStub() {
- DebuggerStatementStub stub;
- set_debugger_statement_code(*stub.GetCode());
-}
-#endif
-
-
void Heap::CreateJSEntryStub() {
JSEntryStub stub;
set_js_entry_code(*stub.GetCode());
@@ -1533,9 +1525,6 @@ void Heap::CreateFixedStubs() {
// }
// To workaround the problem, make separate functions without inlining.
Heap::CreateCEntryStub();
-#ifdef ENABLE_DEBUGGER_SUPPORT
- Heap::CreateCEntryDebugBreakStub();
-#endif
Heap::CreateJSEntryStub();
Heap::CreateJSConstructEntryStub();
#if V8_TARGET_ARCH_ARM && V8_NATIVE_REGEXP
@@ -1778,6 +1767,7 @@ Object* Heap::SmiOrNumberFromDouble(double value,
Object* Heap::NumberToString(Object* number) {
+ Counters::number_to_string_runtime.Increment();
Object* cached = GetNumberStringCache(number);
if (cached != undefined_value()) {
return cached;
@@ -2393,12 +2383,13 @@ Object* Heap::AllocateInitialMap(JSFunction* fun) {
map->set_unused_property_fields(in_object_properties);
map->set_prototype(prototype);
- // If the function has only simple this property assignments add field
- // descriptors for these to the initial map as the object cannot be
- // constructed without having these properties.
+ // If the function has only simple this property assignments add
+ // field descriptors for these to the initial map as the object
+ // cannot be constructed without having these properties. Guard by
+ // the inline_new flag so we only change the map if we generate a
+ // specialized construct stub.
ASSERT(in_object_properties <= Map::kMaxPreAllocatedPropertyFields);
- if (fun->shared()->has_only_simple_this_property_assignments() &&
- fun->shared()->this_property_assignments_count() > 0) {
+ if (fun->shared()->CanGenerateInlineConstructor(prototype)) {
int count = fun->shared()->this_property_assignments_count();
if (count > in_object_properties) {
count = in_object_properties;
@@ -4120,7 +4111,7 @@ int KeyedLookupCache::Hash(Map* map, String* name) {
// Uses only lower 32 bits if pointers are larger.
uintptr_t addr_hash =
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(map)) >> kMapHashShift;
- return (addr_hash ^ name->Hash()) & kCapacityMask;
+ return static_cast<uint32_t>((addr_hash ^ name->Hash()) & kCapacityMask);
}
diff --git a/src/heap.h b/src/heap.h
index 7e1a743b..b1073183 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -101,7 +101,6 @@ namespace internal {
V(Code, js_entry_code, JsEntryCode) \
V(Code, js_construct_entry_code, JsConstructEntryCode) \
V(Code, c_entry_code, CEntryCode) \
- V(Code, debugger_statement_code, DebuggerStatementCode) \
V(FixedArray, number_string_cache, NumberStringCache) \
V(FixedArray, single_character_string_cache, SingleCharacterStringCache) \
V(FixedArray, natives_source_cache, NativesSourceCache) \
@@ -1051,7 +1050,6 @@ class Heap : public AllStatic {
// These four Create*EntryStub functions are here because of a gcc-4.4 bug
// that assigns wrong vtable entries.
static void CreateCEntryStub();
- static void CreateCEntryDebugBreakStub();
static void CreateJSEntryStub();
static void CreateJSConstructEntryStub();
static void CreateRegExpCEntryStub();
@@ -1390,9 +1388,9 @@ class DescriptorLookupCache {
private:
static int Hash(DescriptorArray* array, String* name) {
// Uses only lower 32 bits if pointers are larger.
- uintptr_t array_hash =
+ uint32_t array_hash =
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(array)) >> 2;
- uintptr_t name_hash =
+ uint32_t name_hash =
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name)) >> 2;
return (array_hash ^ name_hash) % kLength;
}
@@ -1617,6 +1615,7 @@ class TranscendentalCache {
if (e.in[0] == c.integers[0] &&
e.in[1] == c.integers[1]) {
ASSERT(e.output != NULL);
+ Counters::transcendental_cache_hit.Increment();
return e.output;
}
double answer = Calculate(input);
@@ -1626,6 +1625,7 @@ class TranscendentalCache {
elements_[hash].in[1] = c.integers[1];
elements_[hash].output = heap_number;
}
+ Counters::transcendental_cache_miss.Increment();
return heap_number;
}
@@ -1666,6 +1666,17 @@ class TranscendentalCache {
hash ^= hash >> 8;
return (hash & (kCacheSize - 1));
}
+
+ static Address cache_array_address() {
+ // Used to create an external reference.
+ return reinterpret_cast<Address>(caches_);
+ }
+
+ // Allow access to the caches_ array as an ExternalReference.
+ friend class ExternalReference;
+ // Inline implementation of the caching.
+ friend class TranscendentalCacheStub;
+
static TranscendentalCache* caches_[kNumberOfCaches];
Element elements_[kCacheSize];
Type type_;
diff --git a/src/ia32/assembler-ia32.cc b/src/ia32/assembler-ia32.cc
index ffcefe0b..89708aae 100644
--- a/src/ia32/assembler-ia32.cc
+++ b/src/ia32/assembler-ia32.cc
@@ -1637,6 +1637,13 @@ void Assembler::fld(int i) {
}
+void Assembler::fstp(int i) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ emit_farith(0xDD, 0xD8, i);
+}
+
+
void Assembler::fld1() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@@ -1645,6 +1652,14 @@ void Assembler::fld1() {
}
+void Assembler::fldpi() {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0xD9);
+ EMIT(0xEB);
+}
+
+
void Assembler::fldz() {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
@@ -1685,6 +1700,14 @@ void Assembler::fstp_d(const Operand& adr) {
}
+void Assembler::fst_d(const Operand& adr) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ EMIT(0xDD);
+ emit_operand(edx, adr);
+}
+
+
void Assembler::fild_s(const Operand& adr) {
EnsureSpace ensure_space(this);
last_pc_ = pc_;
diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h
index f0cf4f11..36aad5e5 100644
--- a/src/ia32/assembler-ia32.h
+++ b/src/ia32/assembler-ia32.h
@@ -231,7 +231,8 @@ enum ScaleFactor {
times_8 = 3,
times_int_size = times_4,
times_half_pointer_size = times_2,
- times_pointer_size = times_4
+ times_pointer_size = times_4,
+ times_twice_pointer_size = times_8
};
@@ -667,6 +668,7 @@ class Assembler : public Malloced {
void call(Label* L);
void call(byte* entry, RelocInfo::Mode rmode);
void call(const Operand& adr);
+ void call(const ExternalReference& target);
void call(Handle<Code> code, RelocInfo::Mode rmode);
// Jumps
@@ -682,15 +684,18 @@ class Assembler : public Malloced {
// Floating-point operations
void fld(int i);
+ void fstp(int i);
void fld1();
void fldz();
+ void fldpi();
void fld_s(const Operand& adr);
void fld_d(const Operand& adr);
void fstp_s(const Operand& adr);
void fstp_d(const Operand& adr);
+ void fst_d(const Operand& adr);
void fild_s(const Operand& adr);
void fild_d(const Operand& adr);
diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc
index 2c5b1d1f..54ef382a 100644
--- a/src/ia32/builtins-ia32.cc
+++ b/src/ia32/builtins-ia32.cc
@@ -93,7 +93,10 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
// edi: called object
// eax: number of arguments
__ bind(&non_function_call);
-
+ // CALL_NON_FUNCTION expects the non-function constructor as receiver
+ // (instead of the original receiver from the call site). The receiver is
+ // stack element argc+1.
+ __ mov(Operand(esp, eax, times_4, kPointerSize), edi);
// Set expected number of arguments to zero (not changing eax).
__ Set(ebx, Immediate(0));
__ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
@@ -437,33 +440,26 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ bind(&done);
}
- // 2. Get the function to call from the stack.
- { Label done, non_function, function;
- // +1 ~ return address.
- __ mov(edi, Operand(esp, eax, times_4, +1 * kPointerSize));
- __ test(edi, Immediate(kSmiTagMask));
- __ j(zero, &non_function, not_taken);
- __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
- __ j(equal, &function, taken);
-
- // Non-function called: Clear the function to force exception.
- __ bind(&non_function);
- __ xor_(edi, Operand(edi));
- __ jmp(&done);
-
- // Function called: Change context eagerly to get the right global object.
- __ bind(&function);
- __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+ // 2. Get the function to call (passed as receiver) from the stack, check
+ // if it is a function.
+ Label non_function;
+ // 1 ~ return address.
+ __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
+ __ test(edi, Immediate(kSmiTagMask));
+ __ j(zero, &non_function, not_taken);
+ __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
+ __ j(not_equal, &non_function, not_taken);
- __ bind(&done);
- }
- // 3. Make sure first argument is an object; convert if necessary.
- { Label call_to_object, use_global_receiver, patch_receiver, done;
- __ mov(ebx, Operand(esp, eax, times_4, 0));
+ // 3a. Patch the first argument if necessary when calling a function.
+ Label shift_arguments;
+ { Label convert_to_object, use_global_receiver, patch_receiver;
+ // Change context eagerly in case we need the global receiver.
+ __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+ __ mov(ebx, Operand(esp, eax, times_4, 0)); // First argument.
__ test(ebx, Immediate(kSmiTagMask));
- __ j(zero, &call_to_object);
+ __ j(zero, &convert_to_object);
__ cmp(ebx, Factory::null_value());
__ j(equal, &use_global_receiver);
@@ -473,31 +469,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset));
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
__ cmp(ecx, FIRST_JS_OBJECT_TYPE);
- __ j(less, &call_to_object);
+ __ j(below, &convert_to_object);
__ cmp(ecx, LAST_JS_OBJECT_TYPE);
- __ j(less_equal, &done);
+ __ j(below_equal, &shift_arguments);
- __ bind(&call_to_object);
- __ EnterInternalFrame(); // preserves eax, ebx, edi
-
- // Store the arguments count on the stack (smi tagged).
+ __ bind(&convert_to_object);
+ __ EnterInternalFrame(); // In order to preserve argument count.
__ SmiTag(eax);
__ push(eax);
- __ push(edi); // save edi across the call
__ push(ebx);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ mov(ebx, eax);
- __ pop(edi); // restore edi after the call
- // Get the arguments count and untag it.
__ pop(eax);
__ SmiUntag(eax);
-
__ LeaveInternalFrame();
+ // Restore the function to edi.
+ __ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
__ jmp(&patch_receiver);
- // Use the global receiver object from the called function as the receiver.
+ // Use the global receiver object from the called function as the
+ // receiver.
__ bind(&use_global_receiver);
const int kGlobalIndex =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
@@ -509,50 +502,55 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ bind(&patch_receiver);
__ mov(Operand(esp, eax, times_4, 0), ebx);
- __ bind(&done);
+ __ jmp(&shift_arguments);
}
- // 4. Check that the function really is a function.
- { Label done;
- __ test(edi, Operand(edi));
- __ j(not_zero, &done, taken);
- __ xor_(ebx, Operand(ebx));
- // CALL_NON_FUNCTION will expect to find the non-function callee on the
- // expression stack of the caller. Transfer it from receiver to the
- // caller's expression stack (and make the first argument the receiver
- // for CALL_NON_FUNCTION) by decrementing the argument count.
- __ dec(eax);
- __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
- __ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
- RelocInfo::CODE_TARGET);
- __ bind(&done);
- }
-
- // 5. Shift arguments and return address one slot down on the stack
- // (overwriting the receiver).
+ // 3b. Patch the first argument when calling a non-function. The
+ // CALL_NON_FUNCTION builtin expects the non-function callee as
+ // receiver, so overwrite the first argument which will ultimately
+ // become the receiver.
+ __ bind(&non_function);
+ __ mov(Operand(esp, eax, times_4, 0), edi);
+ // Clear edi to indicate a non-function being called.
+ __ xor_(edi, Operand(edi));
+
+ // 4. Shift arguments and return address one slot down on the stack
+ // (overwriting the original receiver). Adjust argument count to make
+ // the original first argument the new receiver.
+ __ bind(&shift_arguments);
{ Label loop;
__ mov(ecx, eax);
__ bind(&loop);
__ mov(ebx, Operand(esp, ecx, times_4, 0));
__ mov(Operand(esp, ecx, times_4, kPointerSize), ebx);
__ dec(ecx);
- __ j(not_sign, &loop);
+ __ j(not_sign, &loop); // While non-negative (to copy return address).
__ pop(ebx); // Discard copy of return address.
__ dec(eax); // One fewer argument (first argument is new receiver).
}
- // 6. Get the code to call from the function and check that the number of
- // expected arguments matches what we're providing.
- { __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
- __ mov(ebx,
- FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
- __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
- __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
- __ cmp(eax, Operand(ebx));
- __ j(not_equal, Handle<Code>(builtin(ArgumentsAdaptorTrampoline)));
+ // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
+ { Label function;
+ __ test(edi, Operand(edi));
+ __ j(not_zero, &function, taken);
+ __ xor_(ebx, Operand(ebx));
+ __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
+ __ jmp(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
+ RelocInfo::CODE_TARGET);
+ __ bind(&function);
}
- // 7. Jump (tail-call) to the code in register edx without checking arguments.
+ // 5b. Get the code to call from the function and check that the number of
+ // expected arguments matches what we're providing. If so, jump
+ // (tail-call) to the code in register edx without checking arguments.
+ __ mov(edx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+ __ mov(ebx,
+ FieldOperand(edx, SharedFunctionInfo::kFormalParameterCountOffset));
+ __ mov(edx, FieldOperand(edx, SharedFunctionInfo::kCodeOffset));
+ __ lea(edx, FieldOperand(edx, Code::kHeaderSize));
+ __ cmp(eax, Operand(ebx));
+ __ j(not_equal, Handle<Code>(builtin(ArgumentsAdaptorTrampoline)));
+
ParameterCount expected(0);
__ InvokeCode(Operand(edx), expected, expected, JUMP_FUNCTION);
}
@@ -647,9 +645,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
__ mov(eax, Operand(ebp, kIndexOffset));
__ jmp(&entry);
__ bind(&loop);
- __ mov(ecx, Operand(ebp, 2 * kPointerSize)); // load arguments
- __ push(ecx);
- __ push(eax);
+ __ mov(edx, Operand(ebp, 2 * kPointerSize)); // load arguments
// Use inline caching to speed up access to arguments.
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
@@ -659,8 +655,7 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
// we have generated an inline version of the keyed load. In this
// case, we know that we are not generating a test instruction next.
- // Remove IC arguments from the stack and push the nth argument.
- __ add(Operand(esp), Immediate(2 * kPointerSize));
+ // Push the nth argument.
__ push(eax);
// Update the index on the stack and in register eax.
diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc
index d0fbabba..ecb4c497 100644
--- a/src/ia32/codegen-ia32.cc
+++ b/src/ia32/codegen-ia32.cc
@@ -125,7 +125,7 @@ Scope* CodeGenerator::scope() { return info_->function()->scope(); }
// edi: called JS function
// esi: callee's context
-void CodeGenerator::Generate(CompilationInfo* info, Mode mode) {
+void CodeGenerator::Generate(CompilationInfo* info) {
// Record the position for debugging purposes.
CodeForFunctionPosition(info->function());
@@ -164,7 +164,7 @@ void CodeGenerator::Generate(CompilationInfo* info, Mode mode) {
// esi: callee's context
allocator_->Initialize();
- if (mode == PRIMARY) {
+ if (info->mode() == CompilationInfo::PRIMARY) {
frame_->Enter();
// Allocate space for locals and initialize them.
@@ -255,6 +255,12 @@ void CodeGenerator::Generate(CompilationInfo* info, Mode mode) {
// frame to match this state.
frame_->Adjust(3);
allocator_->Unuse(edi);
+
+ // Bind all the bailout labels to the beginning of the function.
+ List<CompilationInfo::Bailout*>* bailouts = info->bailouts();
+ for (int i = 0; i < bailouts->length(); i++) {
+ __ bind(bailouts->at(i)->label());
+ }
}
// Initialize the function return target after the locals are set
@@ -574,7 +580,9 @@ void CodeGenerator::LoadTypeofExpression(Expression* expr) {
} else if (variable != NULL && variable->slot() != NULL) {
// For a variable that rewrites to a slot, we signal it is the immediate
// subexpression of a typeof.
- LoadFromSlotCheckForArguments(variable->slot(), INSIDE_TYPEOF);
+ Result result =
+ LoadFromSlotCheckForArguments(variable->slot(), INSIDE_TYPEOF);
+ frame()->Push(&result);
} else {
// Anything else can be handled normally.
Load(expr);
@@ -623,8 +631,7 @@ Result CodeGenerator::StoreArgumentsObject(bool initial) {
// We have to skip storing into the arguments slot if it has already
// been written to. This can happen if the a function has a local
// variable named 'arguments'.
- LoadFromSlot(arguments->slot(), NOT_INSIDE_TYPEOF);
- Result probe = frame_->Pop();
+ Result probe = LoadFromSlot(arguments->slot(), NOT_INSIDE_TYPEOF);
if (probe.is_constant()) {
// We have to skip updating the arguments object if it has
// been assigned a proper value.
@@ -688,6 +695,11 @@ void CodeGenerator::LoadReference(Reference* ref) {
// The expression is a variable proxy that does not rewrite to a
// property. Global variables are treated as named property references.
if (var->is_global()) {
+ // If eax is free, the register allocator prefers it. Thus the code
+ // generator will load the global object into eax, which is where
+ // LoadIC wants it. Most uses of Reference call LoadIC directly
+ // after the reference is created.
+ frame_->Spill(eax);
LoadGlobal();
ref->set_type(Reference::NAMED);
} else {
@@ -721,35 +733,54 @@ void CodeGenerator::ToBoolean(ControlDestination* dest) {
// The value to convert should be popped from the frame.
Result value = frame_->Pop();
value.ToRegister();
- // Fast case checks.
- // 'false' => false.
- __ cmp(value.reg(), Factory::false_value());
- dest->false_target()->Branch(equal);
+ if (value.is_number()) {
+ Comment cmnt(masm_, "ONLY_NUMBER");
+ // Fast case if NumberInfo indicates only numbers.
+ if (FLAG_debug_code) {
+ __ AbortIfNotNumber(value.reg(), "ToBoolean operand is not a number.");
+ }
+ // Smi => false iff zero.
+ ASSERT(kSmiTag == 0);
+ __ test(value.reg(), Operand(value.reg()));
+ dest->false_target()->Branch(zero);
+ __ test(value.reg(), Immediate(kSmiTagMask));
+ dest->true_target()->Branch(zero);
+ __ fldz();
+ __ fld_d(FieldOperand(value.reg(), HeapNumber::kValueOffset));
+ __ FCmp();
+ value.Unuse();
+ dest->Split(not_zero);
+ } else {
+ // Fast case checks.
+ // 'false' => false.
+ __ cmp(value.reg(), Factory::false_value());
+ dest->false_target()->Branch(equal);
- // 'true' => true.
- __ cmp(value.reg(), Factory::true_value());
- dest->true_target()->Branch(equal);
+ // 'true' => true.
+ __ cmp(value.reg(), Factory::true_value());
+ dest->true_target()->Branch(equal);
- // 'undefined' => false.
- __ cmp(value.reg(), Factory::undefined_value());
- dest->false_target()->Branch(equal);
+ // 'undefined' => false.
+ __ cmp(value.reg(), Factory::undefined_value());
+ dest->false_target()->Branch(equal);
- // Smi => false iff zero.
- ASSERT(kSmiTag == 0);
- __ test(value.reg(), Operand(value.reg()));
- dest->false_target()->Branch(zero);
- __ test(value.reg(), Immediate(kSmiTagMask));
- dest->true_target()->Branch(zero);
-
- // Call the stub for all other cases.
- frame_->Push(&value); // Undo the Pop() from above.
- ToBooleanStub stub;
- Result temp = frame_->CallStub(&stub, 1);
- // Convert the result to a condition code.
- __ test(temp.reg(), Operand(temp.reg()));
- temp.Unuse();
- dest->Split(not_equal);
+ // Smi => false iff zero.
+ ASSERT(kSmiTag == 0);
+ __ test(value.reg(), Operand(value.reg()));
+ dest->false_target()->Branch(zero);
+ __ test(value.reg(), Immediate(kSmiTagMask));
+ dest->true_target()->Branch(zero);
+
+ // Call the stub for all other cases.
+ frame_->Push(&value); // Undo the Pop() from above.
+ ToBooleanStub stub;
+ Result temp = frame_->CallStub(&stub, 1);
+ // Convert the result to a condition code.
+ __ test(temp.reg(), Operand(temp.reg()));
+ temp.Unuse();
+ dest->Split(not_equal);
+ }
}
@@ -789,6 +820,10 @@ class FloatingPointHelper : public AllStatic {
static void LoadAsIntegers(MacroAssembler* masm,
bool use_sse3,
Label* operand_conversion_failure);
+ // Test if operands are smis or heap numbers and load them
+ // into xmm0 and xmm1 if they are. Operands are in edx and eax.
+ // Leaves operands unchanged.
+ static void LoadSSE2Operands(MacroAssembler* masm);
// Test if operands are numbers (smi or HeapNumber objects), and load
// them into xmm0 and xmm1 if they are. Jump to label not_numbers if
// either operand is not a number. Operands are in edx and eax.
@@ -816,12 +851,13 @@ const char* GenericBinaryOpStub::GetName() {
}
OS::SNPrintF(Vector<char>(name_, kMaxNameLength),
- "GenericBinaryOpStub_%s_%s%s_%s%s",
+ "GenericBinaryOpStub_%s_%s%s_%s%s_%s",
op_name,
overwrite_name,
(flags_ & NO_SMI_CODE_IN_STUB) ? "_NoSmiInStub" : "",
args_in_registers_ ? "RegArgs" : "StackArgs",
- args_reversed_ ? "_R" : "");
+ args_reversed_ ? "_R" : "",
+ NumberInfo::ToString(operands_type_));
return name_;
}
@@ -971,27 +1007,35 @@ void CodeGenerator::GenericBinaryOperation(Token::Value op,
// Neither operand is known to be a string.
}
- bool left_is_smi = left.is_constant() && left.handle()->IsSmi();
- bool left_is_non_smi = left.is_constant() && !left.handle()->IsSmi();
- bool right_is_smi = right.is_constant() && right.handle()->IsSmi();
- bool right_is_non_smi = right.is_constant() && !right.handle()->IsSmi();
+ bool left_is_smi_constant = left.is_constant() && left.handle()->IsSmi();
+ bool left_is_non_smi_constant = left.is_constant() && !left.handle()->IsSmi();
+ bool right_is_smi_constant = right.is_constant() && right.handle()->IsSmi();
+ bool right_is_non_smi_constant =
+ right.is_constant() && !right.handle()->IsSmi();
- if (left_is_smi && right_is_smi) {
+ if (left_is_smi_constant && right_is_smi_constant) {
// Compute the constant result at compile time, and leave it on the frame.
int left_int = Smi::cast(*left.handle())->value();
int right_int = Smi::cast(*right.handle())->value();
if (FoldConstantSmis(op, left_int, right_int)) return;
}
+ // Get number type of left and right sub-expressions.
+ NumberInfo::Type operands_type =
+ NumberInfo::Combine(left.number_info(), right.number_info());
+
Result answer;
- if (left_is_non_smi || right_is_non_smi) {
+ if (left_is_non_smi_constant || right_is_non_smi_constant) {
// Go straight to the slow case, with no smi code.
- GenericBinaryOpStub stub(op, overwrite_mode, NO_SMI_CODE_IN_STUB);
+ GenericBinaryOpStub stub(op,
+ overwrite_mode,
+ NO_SMI_CODE_IN_STUB,
+ operands_type);
answer = stub.GenerateCall(masm_, frame_, &left, &right);
- } else if (right_is_smi) {
+ } else if (right_is_smi_constant) {
answer = ConstantSmiBinaryOperation(op, &left, right.handle(),
type, false, overwrite_mode);
- } else if (left_is_smi) {
+ } else if (left_is_smi_constant) {
answer = ConstantSmiBinaryOperation(op, &right, left.handle(),
type, true, overwrite_mode);
} else {
@@ -1003,10 +1047,67 @@ void CodeGenerator::GenericBinaryOperation(Token::Value op,
if (loop_nesting() > 0 && (Token::IsBitOp(op) || type->IsLikelySmi())) {
answer = LikelySmiBinaryOperation(op, &left, &right, overwrite_mode);
} else {
- GenericBinaryOpStub stub(op, overwrite_mode, NO_GENERIC_BINARY_FLAGS);
+ GenericBinaryOpStub stub(op,
+ overwrite_mode,
+ NO_GENERIC_BINARY_FLAGS,
+ operands_type);
answer = stub.GenerateCall(masm_, frame_, &left, &right);
}
}
+
+ // Set NumberInfo of result according to the operation performed.
+ // Rely on the fact that smis have a 31 bit payload on ia32.
+ ASSERT(kSmiValueSize == 31);
+ NumberInfo::Type result_type = NumberInfo::kUnknown;
+ switch (op) {
+ case Token::COMMA:
+ result_type = right.number_info();
+ break;
+ case Token::OR:
+ case Token::AND:
+ // Result type can be either of the two input types.
+ result_type = operands_type;
+ break;
+ case Token::BIT_OR:
+ case Token::BIT_XOR:
+ case Token::BIT_AND:
+ // Result is always a number. Smi property of inputs is preserved.
+ result_type = (operands_type == NumberInfo::kSmi)
+ ? NumberInfo::kSmi
+ : NumberInfo::kNumber;
+ break;
+ case Token::SAR:
+ // Result is a smi if we shift by a constant >= 1, otherwise a number.
+ result_type = (right.is_constant() && right.handle()->IsSmi()
+ && Smi::cast(*right.handle())->value() >= 1)
+ ? NumberInfo::kSmi
+ : NumberInfo::kNumber;
+ break;
+ case Token::SHR:
+ // Result is a smi if we shift by a constant >= 2, otherwise a number.
+ result_type = (right.is_constant() && right.handle()->IsSmi()
+ && Smi::cast(*right.handle())->value() >= 2)
+ ? NumberInfo::kSmi
+ : NumberInfo::kNumber;
+ break;
+ case Token::ADD:
+ // Result could be a string or a number. Check types of inputs.
+ result_type = NumberInfo::IsNumber(operands_type)
+ ? NumberInfo::kNumber
+ : NumberInfo::kUnknown;
+ break;
+ case Token::SHL:
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD:
+ // Result is always a number.
+ result_type = NumberInfo::kNumber;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ answer.set_number_info(result_type);
frame_->Push(&answer);
}
@@ -1848,6 +1949,39 @@ Result CodeGenerator::ConstantSmiBinaryOperation(Token::Value op,
break;
}
+ case Token::DIV:
+ if (!reversed && int_value == 2) {
+ operand->ToRegister();
+ frame_->Spill(operand->reg());
+
+ DeferredInlineSmiOperation* deferred =
+ new DeferredInlineSmiOperation(op,
+ operand->reg(),
+ operand->reg(),
+ smi_value,
+ overwrite_mode);
+ // Check that lowest log2(value) bits of operand are zero, and test
+ // smi tag at the same time.
+ ASSERT_EQ(0, kSmiTag);
+ ASSERT_EQ(1, kSmiTagSize);
+ __ test(operand->reg(), Immediate(3));
+ deferred->Branch(not_zero); // Branch if non-smi or odd smi.
+ __ sar(operand->reg(), 1);
+ deferred->BindExit();
+ answer = *operand;
+ } else {
+ // Cannot fall through MOD to default case, so we duplicate the
+ // default case here.
+ Result constant_operand(value);
+ if (reversed) {
+ answer = LikelySmiBinaryOperation(op, &constant_operand, operand,
+ overwrite_mode);
+ } else {
+ answer = LikelySmiBinaryOperation(op, operand, &constant_operand,
+ overwrite_mode);
+ }
+ }
+ break;
// Generate inline code for mod of powers of 2 and negative powers of 2.
case Token::MOD:
if (!reversed &&
@@ -2327,6 +2461,7 @@ void CodeGenerator::CallApplyLazy(Expression* applicand,
// Load applicand.apply onto the stack. This will usually
// give us a megamorphic load site. Not super, but it works.
Load(applicand);
+ frame()->Dup();
Handle<String> name = Factory::LookupAsciiSymbol("apply");
frame()->Push(name);
Result answer = frame()->CallLoadIC(RelocInfo::CODE_TARGET);
@@ -2336,7 +2471,9 @@ void CodeGenerator::CallApplyLazy(Expression* applicand,
// Load the receiver and the existing arguments object onto the
// expression stack. Avoid allocating the arguments object here.
Load(receiver);
- LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
+ Result existing_args =
+ LoadFromSlot(scope()->arguments()->var()->slot(), NOT_INSIDE_TYPEOF);
+ frame()->Push(&existing_args);
// Emit the source position information after having loaded the
// receiver and the arguments.
@@ -3906,35 +4043,32 @@ void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
// Spill everything, even constants, to the frame.
frame_->SpillAll();
- DebuggerStatementStub ces;
- frame_->CallStub(&ces, 0);
+ frame_->DebugBreak();
// Ignore the return value.
#endif
}
-void CodeGenerator::InstantiateBoilerplate(Handle<JSFunction> boilerplate) {
+Result CodeGenerator::InstantiateBoilerplate(Handle<JSFunction> boilerplate) {
ASSERT(boilerplate->IsBoilerplate());
// The inevitable call will sync frame elements to memory anyway, so
// we do it eagerly to allow us to push the arguments directly into
// place.
- frame_->SyncRange(0, frame_->element_count() - 1);
+ frame()->SyncRange(0, frame()->element_count() - 1);
// Use the fast case closure allocation code that allocates in new
// space for nested functions that don't need literals cloning.
if (scope()->is_function_scope() && boilerplate->NumberOfLiterals() == 0) {
FastNewClosureStub stub;
- frame_->EmitPush(Immediate(boilerplate));
- Result answer = frame_->CallStub(&stub, 1);
- frame_->Push(&answer);
+ frame()->EmitPush(Immediate(boilerplate));
+ return frame()->CallStub(&stub, 1);
} else {
// Call the runtime to instantiate the function boilerplate
// object.
- frame_->EmitPush(esi);
- frame_->EmitPush(Immediate(boilerplate));
- Result result = frame_->CallRuntime(Runtime::kNewClosure, 2);
- frame_->Push(&result);
+ frame()->EmitPush(esi);
+ frame()->EmitPush(Immediate(boilerplate));
+ return frame()->CallRuntime(Runtime::kNewClosure, 2);
}
}
@@ -3947,14 +4081,16 @@ void CodeGenerator::VisitFunctionLiteral(FunctionLiteral* node) {
Compiler::BuildBoilerplate(node, script(), this);
// Check for stack-overflow exception.
if (HasStackOverflow()) return;
- InstantiateBoilerplate(boilerplate);
+ Result result = InstantiateBoilerplate(boilerplate);
+ frame()->Push(&result);
}
void CodeGenerator::VisitFunctionBoilerplateLiteral(
FunctionBoilerplateLiteral* node) {
Comment cmnt(masm_, "[ FunctionBoilerplateLiteral");
- InstantiateBoilerplate(node->boilerplate());
+ Result result = InstantiateBoilerplate(node->boilerplate());
+ frame()->Push(&result);
}
@@ -3990,13 +4126,12 @@ void CodeGenerator::VisitConditional(Conditional* node) {
}
-void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
+Result CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
+ Result result;
if (slot->type() == Slot::LOOKUP) {
ASSERT(slot->var()->is_dynamic());
-
JumpTarget slow;
JumpTarget done;
- Result value;
// Generate fast-case code for variables that might be shadowed by
// eval-introduced variables. Eval is used a lot without
@@ -4004,14 +4139,10 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
// perform a runtime call for all variables in the scope
// containing the eval.
if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
- value = LoadFromGlobalSlotCheckExtensions(slot, typeof_state, &slow);
+ result = LoadFromGlobalSlotCheckExtensions(slot, typeof_state, &slow);
// If there was no control flow to slow, we can exit early.
- if (!slow.is_linked()) {
- frame_->Push(&value);
- return;
- }
-
- done.Jump(&value);
+ if (!slow.is_linked()) return result;
+ done.Jump(&result);
} else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
@@ -4021,21 +4152,21 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
// Allocate a fresh register to use as a temp in
// ContextSlotOperandCheckExtensions and to hold the result
// value.
- value = allocator_->Allocate();
- ASSERT(value.is_valid());
- __ mov(value.reg(),
+ result = allocator()->Allocate();
+ ASSERT(result.is_valid());
+ __ mov(result.reg(),
ContextSlotOperandCheckExtensions(potential_slot,
- value,
+ result,
&slow));
if (potential_slot->var()->mode() == Variable::CONST) {
- __ cmp(value.reg(), Factory::the_hole_value());
- done.Branch(not_equal, &value);
- __ mov(value.reg(), Factory::undefined_value());
+ __ cmp(result.reg(), Factory::the_hole_value());
+ done.Branch(not_equal, &result);
+ __ mov(result.reg(), Factory::undefined_value());
}
// There is always control flow to slow from
// ContextSlotOperandCheckExtensions so we have to jump around
// it.
- done.Jump(&value);
+ done.Jump(&result);
}
}
@@ -4043,18 +4174,18 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
// A runtime call is inevitable. We eagerly sync frame elements
// to memory so that we can push the arguments directly into place
// on top of the frame.
- frame_->SyncRange(0, frame_->element_count() - 1);
- frame_->EmitPush(esi);
- frame_->EmitPush(Immediate(slot->var()->name()));
+ frame()->SyncRange(0, frame()->element_count() - 1);
+ frame()->EmitPush(esi);
+ frame()->EmitPush(Immediate(slot->var()->name()));
if (typeof_state == INSIDE_TYPEOF) {
- value =
- frame_->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
+ result =
+ frame()->CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
} else {
- value = frame_->CallRuntime(Runtime::kLoadContextSlot, 2);
+ result = frame()->CallRuntime(Runtime::kLoadContextSlot, 2);
}
- done.Bind(&value);
- frame_->Push(&value);
+ done.Bind(&result);
+ return result;
} else if (slot->var()->mode() == Variable::CONST) {
// Const slots may contain 'the hole' value (the constant hasn't been
@@ -4065,19 +4196,21 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
// potentially unsafe direct-frame access of SlotOperand.
VirtualFrame::SpilledScope spilled_scope;
Comment cmnt(masm_, "[ Load const");
- JumpTarget exit;
+ Label exit;
__ mov(ecx, SlotOperand(slot, ecx));
__ cmp(ecx, Factory::the_hole_value());
- exit.Branch(not_equal);
+ __ j(not_equal, &exit);
__ mov(ecx, Factory::undefined_value());
- exit.Bind();
- frame_->EmitPush(ecx);
+ __ bind(&exit);
+ return Result(ecx);
} else if (slot->type() == Slot::PARAMETER) {
- frame_->PushParameterAt(slot->index());
+ frame()->PushParameterAt(slot->index());
+ return frame()->Pop();
} else if (slot->type() == Slot::LOCAL) {
- frame_->PushLocalAt(slot->index());
+ frame()->PushLocalAt(slot->index());
+ return frame()->Pop();
} else {
// The other remaining slot types (LOOKUP and GLOBAL) cannot reach
@@ -4086,49 +4219,46 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
// The use of SlotOperand below is safe for an unspilled frame
// because it will always be a context slot.
ASSERT(slot->type() == Slot::CONTEXT);
- Result temp = allocator_->Allocate();
- ASSERT(temp.is_valid());
- __ mov(temp.reg(), SlotOperand(slot, temp.reg()));
- frame_->Push(&temp);
+ result = allocator()->Allocate();
+ ASSERT(result.is_valid());
+ __ mov(result.reg(), SlotOperand(slot, result.reg()));
+ return result;
}
}
-void CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
- TypeofState state) {
- LoadFromSlot(slot, state);
+Result CodeGenerator::LoadFromSlotCheckForArguments(Slot* slot,
+ TypeofState state) {
+ Result result = LoadFromSlot(slot, state);
// Bail out quickly if we're not using lazy arguments allocation.
- if (ArgumentsMode() != LAZY_ARGUMENTS_ALLOCATION) return;
+ if (ArgumentsMode() != LAZY_ARGUMENTS_ALLOCATION) return result;
// ... or if the slot isn't a non-parameter arguments slot.
- if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return;
-
- // Pop the loaded value from the stack.
- Result value = frame_->Pop();
+ if (slot->type() == Slot::PARAMETER || !slot->is_arguments()) return result;
// If the loaded value is a constant, we know if the arguments
// object has been lazily loaded yet.
- if (value.is_constant()) {
- if (value.handle()->IsTheHole()) {
- Result arguments = StoreArgumentsObject(false);
- frame_->Push(&arguments);
+ if (result.is_constant()) {
+ if (result.handle()->IsTheHole()) {
+ result.Unuse();
+ return StoreArgumentsObject(false);
} else {
- frame_->Push(&value);
+ return result;
}
- return;
}
// The loaded value is in a register. If it is the sentinel that
// indicates that we haven't loaded the arguments object yet, we
// need to do it now.
JumpTarget exit;
- __ cmp(Operand(value.reg()), Immediate(Factory::the_hole_value()));
- frame_->Push(&value);
- exit.Branch(not_equal);
- Result arguments = StoreArgumentsObject(false);
- frame_->SetElementAt(0, &arguments);
- exit.Bind();
+ __ cmp(Operand(result.reg()), Immediate(Factory::the_hole_value()));
+ exit.Branch(not_equal, &result);
+
+ result.Unuse();
+ result = StoreArgumentsObject(false);
+ exit.Bind(&result);
+ return result;
}
@@ -4188,6 +4318,10 @@ Result CodeGenerator::LoadFromGlobalSlotCheckExtensions(
// All extension objects were empty and it is safe to use a global
// load IC call.
+ // The register allocator prefers eax if it is free, so the code generator
+ // will load the global object directly into eax, which is where the LoadIC
+ // expects it.
+ frame_->Spill(eax);
LoadGlobal();
frame_->Push(slot->var()->name());
RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF)
@@ -4198,8 +4332,6 @@ Result CodeGenerator::LoadFromGlobalSlotCheckExtensions(
// property case was inlined. Ensure that there is not a test eax
// instruction here.
__ nop();
- // Discard the global object. The result is in answer.
- frame_->Drop();
return answer;
}
@@ -4304,7 +4436,8 @@ void CodeGenerator::StoreToSlot(Slot* slot, InitState init_state) {
void CodeGenerator::VisitSlot(Slot* node) {
Comment cmnt(masm_, "[ Slot");
- LoadFromSlotCheckForArguments(node, NOT_INSIDE_TYPEOF);
+ Result result = LoadFromSlotCheckForArguments(node, NOT_INSIDE_TYPEOF);
+ frame()->Push(&result);
}
@@ -4474,8 +4607,8 @@ void CodeGenerator::VisitObjectLiteral(ObjectLiteral* node) {
// Duplicate the object as the IC receiver.
frame_->Dup();
Load(property->value());
- frame_->Push(key);
- Result ignored = frame_->CallStoreIC();
+ Result dummy = frame_->CallStoreIC(Handle<String>::cast(key), false);
+ dummy.Unuse();
break;
}
// Fall through
@@ -4599,106 +4732,241 @@ void CodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* node) {
}
-void CodeGenerator::VisitAssignment(Assignment* node) {
+void CodeGenerator::EmitSlotAssignment(Assignment* node) {
#ifdef DEBUG
- int original_height = frame_->height();
+ int original_height = frame()->height();
#endif
- Comment cmnt(masm_, "[ Assignment");
+ Comment cmnt(masm(), "[ Variable Assignment");
+ Variable* var = node->target()->AsVariableProxy()->AsVariable();
+ ASSERT(var != NULL);
+ Slot* slot = var->slot();
+ ASSERT(slot != NULL);
- { Reference target(this, node->target(), node->is_compound());
- if (target.is_illegal()) {
- // Fool the virtual frame into thinking that we left the assignment's
- // value on the frame.
- frame_->Push(Smi::FromInt(0));
- return;
- }
- Variable* var = node->target()->AsVariableProxy()->AsVariable();
+ // Evaluate the right-hand side.
+ if (node->is_compound()) {
+ Result result = LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
+ frame()->Push(&result);
+ Load(node->value());
+
+ bool overwrite_value =
+ (node->value()->AsBinaryOperation() != NULL &&
+ node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
+ GenericBinaryOperation(node->binary_op(),
+ node->type(),
+ overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
+ } else {
+ Load(node->value());
+ }
- if (node->starts_initialization_block()) {
- ASSERT(target.type() == Reference::NAMED ||
- target.type() == Reference::KEYED);
- // Change to slow case in the beginning of an initialization
- // block to avoid the quadratic behavior of repeatedly adding
- // fast properties.
+ // Perform the assignment.
+ if (var->mode() != Variable::CONST || node->op() == Token::INIT_CONST) {
+ CodeForSourcePosition(node->position());
+ StoreToSlot(slot,
+ node->op() == Token::INIT_CONST ? CONST_INIT : NOT_CONST_INIT);
+ }
+ ASSERT(frame()->height() == original_height + 1);
+}
- // The receiver is the argument to the runtime call. It is the
- // first value pushed when the reference was loaded to the
- // frame.
- frame_->PushElementAt(target.size() - 1);
- Result ignored = frame_->CallRuntime(Runtime::kToSlowProperties, 1);
- }
- if (node->ends_initialization_block()) {
- // Add an extra copy of the receiver to the frame, so that it can be
- // converted back to fast case after the assignment.
- ASSERT(target.type() == Reference::NAMED ||
- target.type() == Reference::KEYED);
- if (target.type() == Reference::NAMED) {
- frame_->Dup();
- // Dup target receiver on stack.
- } else {
- ASSERT(target.type() == Reference::KEYED);
- Result temp = frame_->Pop();
- frame_->Dup();
- frame_->Push(&temp);
- }
- }
- if (node->op() == Token::ASSIGN ||
- node->op() == Token::INIT_VAR ||
- node->op() == Token::INIT_CONST) {
- Load(node->value());
-
- } else { // Assignment is a compound assignment.
- 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,
- // or the right hand side is a different variable. TakeValue invalidates
- // the target, with an implicit promise that it will be written to again
- // before it is read.
- if (literal != NULL || (right_var != NULL && right_var != var)) {
- target.TakeValue();
- } else {
- target.GetValue();
- }
- Load(node->value());
- GenericBinaryOperation(node->binary_op(),
- node->type(),
- overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
+
+void CodeGenerator::EmitNamedPropertyAssignment(Assignment* node) {
+#ifdef DEBUG
+ int original_height = frame()->height();
+#endif
+ Comment cmnt(masm(), "[ Named Property Assignment");
+ Variable* var = node->target()->AsVariableProxy()->AsVariable();
+ Property* prop = node->target()->AsProperty();
+ ASSERT(var == NULL || (prop == NULL && var->is_global()));
+
+ // Initialize name and evaluate the receiver subexpression if necessary.
+ Handle<String> name;
+ bool is_trivial_receiver = false;
+ if (var != NULL) {
+ name = var->name();
+ } else {
+ Literal* lit = prop->key()->AsLiteral();
+ ASSERT_NOT_NULL(lit);
+ name = Handle<String>::cast(lit->handle());
+ // Do not materialize the receiver on the frame if it is trivial.
+ is_trivial_receiver = prop->obj()->IsTrivial();
+ if (!is_trivial_receiver) Load(prop->obj());
+ }
+
+ if (node->starts_initialization_block()) {
+ ASSERT_EQ(NULL, var);
+ // Change to slow case in the beginning of an initialization block to
+ // avoid the quadratic behavior of repeatedly adding fast properties.
+ if (is_trivial_receiver) {
+ frame()->Push(prop->obj());
+ } else {
+ frame()->Dup();
}
+ Result ignored = frame()->CallRuntime(Runtime::kToSlowProperties, 1);
+ }
+
+ if (node->ends_initialization_block() && !is_trivial_receiver) {
+ // Add an extra copy of the receiver to the frame, so that it can be
+ // converted back to fast case after the assignment.
+ frame()->Dup();
+ }
- if (var != NULL &&
- var->mode() == Variable::CONST &&
- node->op() != Token::INIT_VAR && node->op() != Token::INIT_CONST) {
- // Assignment ignored - leave the value on the stack.
- UnloadReference(&target);
+ // Evaluate the right-hand side.
+ if (node->is_compound()) {
+ if (is_trivial_receiver) {
+ frame()->Push(prop->obj());
+ } else if (var != NULL) {
+ // The LoadIC stub expects the object in eax.
+ // Freeing eax causes the code generator to load the global into it.
+ frame_->Spill(eax);
+ LoadGlobal();
} else {
- CodeForSourcePosition(node->position());
- if (node->op() == Token::INIT_CONST) {
- // Dynamic constant initializations must use the function context
- // and initialize the actual constant declared. Dynamic variable
- // initializations are simply assignments and use SetValue.
- target.SetValue(CONST_INIT);
- } else {
- target.SetValue(NOT_CONST_INIT);
- }
- if (node->ends_initialization_block()) {
- ASSERT(target.type() == Reference::UNLOADED);
- // End of initialization block. Revert to fast case. The
- // argument to the runtime call is the extra copy of the receiver,
- // which is below the value of the assignment.
- // Swap the receiver and the value of the assignment expression.
- Result lhs = frame_->Pop();
- Result receiver = frame_->Pop();
- frame_->Push(&lhs);
- frame_->Push(&receiver);
- Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1);
- }
+ frame()->Dup();
+ }
+ Result value = EmitNamedLoad(name, var != NULL);
+ frame()->Push(&value);
+ Load(node->value());
+
+ bool overwrite_value =
+ (node->value()->AsBinaryOperation() != NULL &&
+ node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
+ GenericBinaryOperation(node->binary_op(),
+ node->type(),
+ overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
+ } else {
+ Load(node->value());
+ }
+
+ // Perform the assignment. It is safe to ignore constants here.
+ ASSERT(var == NULL || var->mode() != Variable::CONST);
+ ASSERT_NE(Token::INIT_CONST, node->op());
+ if (is_trivial_receiver) {
+ Result value = frame()->Pop();
+ frame()->Push(prop->obj());
+ frame()->Push(&value);
+ }
+ CodeForSourcePosition(node->position());
+ bool is_contextual = (var != NULL);
+ Result answer = EmitNamedStore(name, is_contextual);
+ frame()->Push(&answer);
+
+ if (node->ends_initialization_block()) {
+ ASSERT_EQ(NULL, var);
+ // The argument to the runtime call is the receiver.
+ if (is_trivial_receiver) {
+ frame()->Push(prop->obj());
+ } else {
+ // A copy of the receiver is below the value of the assignment. Swap
+ // the receiver and the value of the assignment expression.
+ Result result = frame()->Pop();
+ Result receiver = frame()->Pop();
+ frame()->Push(&result);
+ frame()->Push(&receiver);
}
+ Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1);
}
- ASSERT(frame_->height() == original_height + 1);
+
+ ASSERT_EQ(frame()->height(), original_height + 1);
+}
+
+
+void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
+#ifdef DEBUG
+ int original_height = frame()->height();
+#endif
+ Comment cmnt(masm_, "[ Named Property Assignment");
+ Property* prop = node->target()->AsProperty();
+ ASSERT_NOT_NULL(prop);
+
+ // Evaluate the receiver subexpression.
+ Load(prop->obj());
+
+ if (node->starts_initialization_block()) {
+ // Change to slow case in the beginning of an initialization block to
+ // avoid the quadratic behavior of repeatedly adding fast properties.
+ frame_->Dup();
+ Result ignored = frame_->CallRuntime(Runtime::kToSlowProperties, 1);
+ }
+
+ if (node->ends_initialization_block()) {
+ // Add an extra copy of the receiver to the frame, so that it can be
+ // converted back to fast case after the assignment.
+ frame_->Dup();
+ }
+
+ // Evaluate the key subexpression.
+ Load(prop->key());
+
+ // Evaluate the right-hand side.
+ if (node->is_compound()) {
+ // Duplicate receiver and key.
+ frame()->PushElementAt(1);
+ frame()->PushElementAt(1);
+ Result value = EmitKeyedLoad();
+ frame()->Push(&value);
+ Load(node->value());
+
+ bool overwrite_value =
+ (node->value()->AsBinaryOperation() != NULL &&
+ node->value()->AsBinaryOperation()->ResultOverwriteAllowed());
+ GenericBinaryOperation(node->binary_op(),
+ node->type(),
+ overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE);
+ } else {
+ Load(node->value());
+ }
+
+ // Perform the assignment. It is safe to ignore constants here.
+ ASSERT(node->op() != Token::INIT_CONST);
+ CodeForSourcePosition(node->position());
+ Result answer = EmitKeyedStore(prop->key()->type());
+ frame()->Push(&answer);
+
+ if (node->ends_initialization_block()) {
+ // The argument to the runtime call is the extra copy of the receiver,
+ // which is below the value of the assignment. Swap the receiver and
+ // the value of the assignment expression.
+ Result result = frame()->Pop();
+ Result receiver = frame()->Pop();
+ frame()->Push(&result);
+ frame()->Push(&receiver);
+ Result ignored = frame_->CallRuntime(Runtime::kToFastProperties, 1);
+ }
+
+ ASSERT(frame()->height() == original_height + 1);
+}
+
+
+void CodeGenerator::VisitAssignment(Assignment* node) {
+#ifdef DEBUG
+ int original_height = frame()->height();
+#endif
+ Variable* var = node->target()->AsVariableProxy()->AsVariable();
+ Property* prop = node->target()->AsProperty();
+
+ if (var != NULL && !var->is_global()) {
+ EmitSlotAssignment(node);
+
+ } else if ((prop != NULL && prop->key()->IsPropertyName()) ||
+ (var != NULL && var->is_global())) {
+ // Properties whose keys are property names and global variables are
+ // treated as named property references. We do not need to consider
+ // global 'this' because it is not a valid left-hand side.
+ EmitNamedPropertyAssignment(node);
+
+ } else if (prop != NULL) {
+ // Other properties (including rewritten parameters for a function that
+ // uses arguments) are keyed property assignments.
+ EmitKeyedPropertyAssignment(node);
+
+ } else {
+ // Invalid left-hand side.
+ Load(node->target());
+ Result result = frame()->CallRuntime(Runtime::kThrowReferenceError, 1);
+ // The runtime call doesn't actually return but the code generator will
+ // still generate code and expects a certain frame height.
+ frame()->Push(&result);
+ }
+
+ ASSERT(frame()->height() == original_height + 1);
}
@@ -4903,9 +5171,9 @@ void CodeGenerator::VisitCall(Call* node) {
LoadGlobalReceiver();
} else {
Load(property->obj());
+ frame()->Dup();
Load(property->key());
- Result function = EmitKeyedLoad(false);
- frame_->Drop(); // Key.
+ Result function = EmitKeyedLoad();
Result receiver = frame_->Pop();
frame_->Push(&function);
frame_->Push(&receiver);
@@ -5173,6 +5441,25 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
}
+void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+ Load(args->at(0));
+ Result value = frame_->Pop();
+ value.ToRegister();
+ ASSERT(value.is_valid());
+ __ test(value.reg(), Immediate(kSmiTagMask));
+ destination()->false_target()->Branch(equal);
+ // It is a heap object - get map.
+ Result temp = allocator()->Allocate();
+ ASSERT(temp.is_valid());
+ // Check if the object is a regexp.
+ __ CmpObjectType(value.reg(), JS_REGEXP_TYPE, temp.reg());
+ value.Unuse();
+ temp.Unuse();
+ destination()->Split(equal);
+}
+
+
void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
// This generates a fast version of:
// (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
@@ -5527,6 +5814,35 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
}
+void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 1);
+
+ // Load the argument on the stack and call the stub.
+ Load(args->at(0));
+ NumberToStringStub stub;
+ Result result = frame_->CallStub(&stub, 1);
+ frame_->Push(&result);
+}
+
+
+void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 1);
+ Load(args->at(0));
+ TranscendentalCacheStub stub(TranscendentalCache::SIN);
+ Result result = frame_->CallStub(&stub, 1);
+ frame_->Push(&result);
+}
+
+
+void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 1);
+ Load(args->at(0));
+ TranscendentalCacheStub stub(TranscendentalCache::COS);
+ Result result = frame_->CallStub(&stub, 1);
+ frame_->Push(&result);
+}
+
+
void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
if (CheckForInlineRuntimeCall(node)) {
return;
@@ -5661,7 +5977,6 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
switch (op) {
case Token::SUB: {
GenericUnaryOpStub stub(Token::SUB, overwrite);
- // TODO(1222589): remove dependency of TOS being cached inside stub
Result operand = frame_->Pop();
Result answer = frame_->CallStub(&stub, &operand);
frame_->Push(&answer);
@@ -6111,13 +6426,10 @@ void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
__ movzx_b(temp.reg(), FieldOperand(temp.reg(), Map::kBitFieldOffset));
__ test(temp.reg(), Immediate(1 << Map::kIsUndetectable));
destination()->false_target()->Branch(not_zero);
- __ mov(temp.reg(), FieldOperand(answer.reg(), HeapObject::kMapOffset));
- __ movzx_b(temp.reg(),
- FieldOperand(temp.reg(), Map::kInstanceTypeOffset));
- __ cmp(temp.reg(), FIRST_NONSTRING_TYPE);
+ __ CmpObjectType(answer.reg(), FIRST_NONSTRING_TYPE, temp.reg());
temp.Unuse();
answer.Unuse();
- destination()->Split(less);
+ destination()->Split(below);
} else if (check->Equals(Heap::boolean_symbol())) {
__ cmp(answer.reg(), Factory::true_value());
@@ -6277,7 +6589,7 @@ bool CodeGenerator::HasValidEntryRegisters() {
// Emit a LoadIC call to get the value from receiver and leave it in
-// dst. The receiver register is restored after the call.
+// dst.
class DeferredReferenceGetNamedValue: public DeferredCode {
public:
DeferredReferenceGetNamedValue(Register dst,
@@ -6300,7 +6612,9 @@ class DeferredReferenceGetNamedValue: public DeferredCode {
void DeferredReferenceGetNamedValue::Generate() {
- __ push(receiver_);
+ if (!receiver_.is(eax)) {
+ __ mov(eax, receiver_);
+ }
__ Set(ecx, Immediate(name_));
Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
@@ -6317,7 +6631,6 @@ void DeferredReferenceGetNamedValue::Generate() {
__ IncrementCounter(&Counters::named_load_inline_miss, 1);
if (!dst_.is(eax)) __ mov(dst_, eax);
- __ pop(receiver_);
}
@@ -6325,9 +6638,8 @@ class DeferredReferenceGetKeyedValue: public DeferredCode {
public:
explicit DeferredReferenceGetKeyedValue(Register dst,
Register receiver,
- Register key,
- bool is_global)
- : dst_(dst), receiver_(receiver), key_(key), is_global_(is_global) {
+ Register key)
+ : dst_(dst), receiver_(receiver), key_(key) {
set_comment("[ DeferredReferenceGetKeyedValue");
}
@@ -6340,14 +6652,29 @@ class DeferredReferenceGetKeyedValue: public DeferredCode {
Register dst_;
Register receiver_;
Register key_;
- bool is_global_;
};
void DeferredReferenceGetKeyedValue::Generate() {
- __ push(receiver_); // First IC argument.
- __ push(key_); // Second IC argument.
-
+ if (!receiver_.is(eax)) {
+ // Register eax is available for key.
+ if (!key_.is(eax)) {
+ __ mov(eax, key_);
+ }
+ if (!receiver_.is(edx)) {
+ __ mov(edx, receiver_);
+ }
+ } else if (!key_.is(edx)) {
+ // Register edx is available for receiver.
+ if (!receiver_.is(edx)) {
+ __ mov(edx, receiver_);
+ }
+ if (!key_.is(eax)) {
+ __ mov(eax, key_);
+ }
+ } else {
+ __ xchg(edx, eax);
+ }
// Calculate the delta from the IC call instruction to the map check
// cmp instruction in the inlined version. This delta is stored in
// a test(eax, delta) instruction after the call so that we can find
@@ -6355,10 +6682,7 @@ void DeferredReferenceGetKeyedValue::Generate() {
// This means that we cannot allow test instructions after calls to
// KeyedLoadIC stubs in other places.
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
- RelocInfo::Mode mode = is_global_
- ? RelocInfo::CODE_TARGET_CONTEXT
- : RelocInfo::CODE_TARGET;
- __ call(ic, mode);
+ __ call(ic, RelocInfo::CODE_TARGET);
// The delta from the start of the map-compare instruction to the
// test instruction. We use masm_-> directly here instead of the __
// macro because the macro sometimes uses macro expansion to turn
@@ -6371,8 +6695,6 @@ void DeferredReferenceGetKeyedValue::Generate() {
__ IncrementCounter(&Counters::keyed_load_inline_miss, 1);
if (!dst_.is(eax)) __ mov(dst_, eax);
- __ pop(key_);
- __ pop(receiver_);
}
@@ -6424,12 +6746,90 @@ void DeferredReferenceSetKeyedValue::Generate() {
}
-Result CodeGenerator::EmitKeyedLoad(bool is_global) {
- Comment cmnt(masm_, "[ Load from keyed Property");
- // Inline array load code if inside of a loop. We do not know
- // the receiver map yet, so we initially generate the code with
- // a check against an invalid map. In the inline cache code, we
- // patch the map check if appropriate.
+Result CodeGenerator::EmitNamedLoad(Handle<String> name, bool is_contextual) {
+#ifdef DEBUG
+ int original_height = frame()->height();
+#endif
+ Result result;
+ // Do not inline the inobject property case for loads from the global
+ // object. Also do not inline for unoptimized code. This saves time in
+ // the code generator. Unoptimized code is toplevel code or code that is
+ // not in a loop.
+ if (is_contextual || scope()->is_global_scope() || loop_nesting() == 0) {
+ Comment cmnt(masm(), "[ Load from named Property");
+ frame()->Push(name);
+
+ RelocInfo::Mode mode = is_contextual
+ ? RelocInfo::CODE_TARGET_CONTEXT
+ : RelocInfo::CODE_TARGET;
+ result = frame()->CallLoadIC(mode);
+ // A test eax instruction following the call signals that the inobject
+ // property case was inlined. Ensure that there is not a test eax
+ // instruction here.
+ __ nop();
+ } else {
+ // Inline the inobject property case.
+ Comment cmnt(masm(), "[ Inlined named property load");
+ Result receiver = frame()->Pop();
+ receiver.ToRegister();
+
+ result = allocator()->Allocate();
+ ASSERT(result.is_valid());
+ DeferredReferenceGetNamedValue* deferred =
+ new DeferredReferenceGetNamedValue(result.reg(), receiver.reg(), name);
+
+ // Check that the receiver is a heap object.
+ __ test(receiver.reg(), Immediate(kSmiTagMask));
+ deferred->Branch(zero);
+
+ __ bind(deferred->patch_site());
+ // This is the map check instruction that will be patched (so we can't
+ // use the double underscore macro that may insert instructions).
+ // Initially use an invalid map to force a failure.
+ masm()->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
+ Immediate(Factory::null_value()));
+ // This branch is always a forwards branch so it's always a fixed size
+ // which allows the assert below to succeed and patching to work.
+ deferred->Branch(not_equal);
+
+ // The delta from the patch label to the load offset must be statically
+ // known.
+ ASSERT(masm()->SizeOfCodeGeneratedSince(deferred->patch_site()) ==
+ LoadIC::kOffsetToLoadInstruction);
+ // The initial (invalid) offset has to be large enough to force a 32-bit
+ // instruction encoding to allow patching with an arbitrary offset. Use
+ // kMaxInt (minus kHeapObjectTag).
+ int offset = kMaxInt;
+ masm()->mov(result.reg(), FieldOperand(receiver.reg(), offset));
+
+ __ IncrementCounter(&Counters::named_load_inline, 1);
+ deferred->BindExit();
+ }
+ ASSERT(frame()->height() == original_height - 1);
+ return result;
+}
+
+
+Result CodeGenerator::EmitNamedStore(Handle<String> name, bool is_contextual) {
+#ifdef DEBUG
+ int expected_height = frame()->height() - (is_contextual ? 1 : 2);
+#endif
+ Result result = frame()->CallStoreIC(name, is_contextual);
+
+ ASSERT_EQ(expected_height, frame()->height());
+ return result;
+}
+
+
+Result CodeGenerator::EmitKeyedLoad() {
+#ifdef DEBUG
+ int original_height = frame()->height();
+#endif
+ Result result;
+ // Inline array load code if inside of a loop. We do not know the
+ // receiver map yet, so we initially generate the code with a check
+ // against an invalid map. In the inline cache code, we patch the map
+ // check if appropriate.
if (loop_nesting() > 0) {
Comment cmnt(masm_, "[ Inlined load from keyed Property");
@@ -6445,22 +6845,16 @@ Result CodeGenerator::EmitKeyedLoad(bool is_global) {
// Use a fresh temporary for the index and later the loaded
// value.
- Result index = allocator()->Allocate();
- ASSERT(index.is_valid());
+ result = allocator()->Allocate();
+ ASSERT(result.is_valid());
DeferredReferenceGetKeyedValue* deferred =
- new DeferredReferenceGetKeyedValue(index.reg(),
+ new DeferredReferenceGetKeyedValue(result.reg(),
receiver.reg(),
- key.reg(),
- is_global);
+ key.reg());
- // Check that the receiver is not a smi (only needed if this
- // is not a load from the global context) and that it has the
- // expected map.
- if (!is_global) {
- __ test(receiver.reg(), Immediate(kSmiTagMask));
- deferred->Branch(zero);
- }
+ __ test(receiver.reg(), Immediate(kSmiTagMask));
+ deferred->Branch(zero);
// Initially, use an invalid map. The map is patched in the IC
// initialization code.
@@ -6485,50 +6879,132 @@ Result CodeGenerator::EmitKeyedLoad(bool is_global) {
// Shift the key to get the actual index value and check that
// it is within bounds.
- __ mov(index.reg(), key.reg());
- __ SmiUntag(index.reg());
- __ cmp(index.reg(),
+ __ mov(result.reg(), key.reg());
+ __ SmiUntag(result.reg());
+ __ cmp(result.reg(),
FieldOperand(elements.reg(), FixedArray::kLengthOffset));
deferred->Branch(above_equal);
- // Load and check that the result is not the hole. We could
- // reuse the index or elements register for the value.
- //
- // TODO(206): Consider whether it makes sense to try some
- // heuristic about which register to reuse. For example, if
- // one is eax, the we can reuse that one because the value
- // coming from the deferred code will be in eax.
- Result value = index;
- __ mov(value.reg(), Operand(elements.reg(),
- index.reg(),
- times_4,
- FixedArray::kHeaderSize - kHeapObjectTag));
+ // Load and check that the result is not the hole.
+ __ mov(result.reg(), Operand(elements.reg(),
+ result.reg(),
+ times_4,
+ FixedArray::kHeaderSize - kHeapObjectTag));
elements.Unuse();
- index.Unuse();
- __ cmp(Operand(value.reg()), Immediate(Factory::the_hole_value()));
+ __ cmp(Operand(result.reg()), Immediate(Factory::the_hole_value()));
deferred->Branch(equal);
__ IncrementCounter(&Counters::keyed_load_inline, 1);
deferred->BindExit();
- // Restore the receiver and key to the frame and push the
- // result on top of it.
- frame_->Push(&receiver);
- frame_->Push(&key);
- return value;
} else {
Comment cmnt(masm_, "[ Load from keyed Property");
- RelocInfo::Mode mode = is_global
- ? RelocInfo::CODE_TARGET_CONTEXT
- : RelocInfo::CODE_TARGET;
- Result answer = frame_->CallKeyedLoadIC(mode);
+ result = frame_->CallKeyedLoadIC(RelocInfo::CODE_TARGET);
// Make sure that we do not have a test instruction after the
// call. A test instruction after the call is used to
// indicate that we have generated an inline version of the
// keyed load. The explicit nop instruction is here because
// the push that follows might be peep-hole optimized away.
__ nop();
- return answer;
}
+ ASSERT(frame()->height() == original_height - 2);
+ return result;
+}
+
+
+Result CodeGenerator::EmitKeyedStore(StaticType* key_type) {
+#ifdef DEBUG
+ int original_height = frame()->height();
+#endif
+ Result result;
+ // Generate inlined version of the keyed store if the code is in a loop
+ // and the key is likely to be a smi.
+ if (loop_nesting() > 0 && key_type->IsLikelySmi()) {
+ Comment cmnt(masm(), "[ Inlined store to keyed Property");
+
+ // Get the receiver, key and value into registers.
+ result = frame()->Pop();
+ Result key = frame()->Pop();
+ Result receiver = frame()->Pop();
+
+ Result tmp = allocator_->Allocate();
+ ASSERT(tmp.is_valid());
+
+ // Determine whether the value is a constant before putting it in a
+ // register.
+ bool value_is_constant = result.is_constant();
+
+ // Make sure that value, key and receiver are in registers.
+ result.ToRegister();
+ key.ToRegister();
+ receiver.ToRegister();
+
+ DeferredReferenceSetKeyedValue* deferred =
+ new DeferredReferenceSetKeyedValue(result.reg(),
+ key.reg(),
+ receiver.reg());
+
+ // Check that the value is a smi if it is not a constant. We can skip
+ // the write barrier for smis and constants.
+ if (!value_is_constant) {
+ __ test(result.reg(), Immediate(kSmiTagMask));
+ deferred->Branch(not_zero);
+ }
+
+ // Check that the key is a non-negative smi.
+ __ test(key.reg(), Immediate(kSmiTagMask | 0x80000000));
+ deferred->Branch(not_zero);
+
+ // Check that the receiver is not a smi.
+ __ test(receiver.reg(), Immediate(kSmiTagMask));
+ deferred->Branch(zero);
+
+ // Check that the receiver is a JSArray.
+ __ mov(tmp.reg(),
+ FieldOperand(receiver.reg(), HeapObject::kMapOffset));
+ __ movzx_b(tmp.reg(),
+ FieldOperand(tmp.reg(), Map::kInstanceTypeOffset));
+ __ cmp(tmp.reg(), JS_ARRAY_TYPE);
+ deferred->Branch(not_equal);
+
+ // Check that the key is within bounds. Both the key and the length of
+ // the JSArray are smis.
+ __ cmp(key.reg(),
+ FieldOperand(receiver.reg(), JSArray::kLengthOffset));
+ deferred->Branch(greater_equal);
+
+ // Get the elements array from the receiver and check that it is not a
+ // dictionary.
+ __ mov(tmp.reg(),
+ FieldOperand(receiver.reg(), JSObject::kElementsOffset));
+ // Bind the deferred code patch site to be able to locate the fixed
+ // array map comparison. When debugging, we patch this comparison to
+ // always fail so that we will hit the IC call in the deferred code
+ // which will allow the debugger to break for fast case stores.
+ __ bind(deferred->patch_site());
+ __ cmp(FieldOperand(tmp.reg(), HeapObject::kMapOffset),
+ Immediate(Factory::fixed_array_map()));
+ deferred->Branch(not_equal);
+
+ // Store the value.
+ __ mov(Operand(tmp.reg(),
+ key.reg(),
+ times_2,
+ FixedArray::kHeaderSize - kHeapObjectTag),
+ result.reg());
+ __ IncrementCounter(&Counters::keyed_store_inline, 1);
+
+ deferred->BindExit();
+ } else {
+ result = frame()->CallKeyedStoreIC();
+ // Make sure that we do not have a test instruction after the
+ // call. A test instruction after the call is used to
+ // indicate that we have generated an inline version of the
+ // keyed store.
+ __ nop();
+ frame()->Drop(2);
+ }
+ ASSERT(frame()->height() == original_height - 3);
+ return result;
}
@@ -6548,7 +7024,7 @@ Handle<String> Reference::GetName() {
} else {
Literal* raw_name = property->key()->AsLiteral();
ASSERT(raw_name != NULL);
- return Handle<String>(String::cast(*raw_name->handle()));
+ return Handle<String>::cast(raw_name->handle());
}
}
@@ -6570,7 +7046,10 @@ void Reference::GetValue() {
Comment cmnt(masm, "[ Load from Slot");
Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
ASSERT(slot != NULL);
- cgen_->LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
+ Result result =
+ cgen_->LoadFromSlotCheckForArguments(slot, NOT_INSIDE_TYPEOF);
+ if (!persist_after_get_) set_unloaded();
+ cgen_->frame()->Push(&result);
break;
}
@@ -6578,87 +7057,27 @@ void Reference::GetValue() {
Variable* var = expression_->AsVariableProxy()->AsVariable();
bool is_global = var != NULL;
ASSERT(!is_global || var->is_global());
-
- // Do not inline the inobject property case for loads from the global
- // object. Also do not inline for unoptimized code. This saves time
- // in the code generator. Unoptimized code is toplevel code or code
- // that is not in a loop.
- if (is_global ||
- cgen_->scope()->is_global_scope() ||
- cgen_->loop_nesting() == 0) {
- Comment cmnt(masm, "[ Load from named Property");
- cgen_->frame()->Push(GetName());
-
- RelocInfo::Mode mode = is_global
- ? RelocInfo::CODE_TARGET_CONTEXT
- : RelocInfo::CODE_TARGET;
- Result answer = cgen_->frame()->CallLoadIC(mode);
- // A test eax instruction following the call signals that the
- // inobject property case was inlined. Ensure that there is not
- // a test eax instruction here.
- __ nop();
- cgen_->frame()->Push(&answer);
- } else {
- // Inline the inobject property case.
- Comment cmnt(masm, "[ Inlined named property load");
- Result receiver = cgen_->frame()->Pop();
- receiver.ToRegister();
-
- Result value = cgen_->allocator()->Allocate();
- ASSERT(value.is_valid());
- DeferredReferenceGetNamedValue* deferred =
- new DeferredReferenceGetNamedValue(value.reg(),
- receiver.reg(),
- GetName());
-
- // Check that the receiver is a heap object.
- __ test(receiver.reg(), Immediate(kSmiTagMask));
- deferred->Branch(zero);
-
- __ bind(deferred->patch_site());
- // This is the map check instruction that will be patched (so we can't
- // use the double underscore macro that may insert instructions).
- // Initially use an invalid map to force a failure.
- masm->cmp(FieldOperand(receiver.reg(), HeapObject::kMapOffset),
- Immediate(Factory::null_value()));
- // This branch is always a forwards branch so it's always a fixed
- // size which allows the assert below to succeed and patching to work.
- deferred->Branch(not_equal);
-
- // The delta from the patch label to the load offset must be
- // statically known.
- ASSERT(masm->SizeOfCodeGeneratedSince(deferred->patch_site()) ==
- LoadIC::kOffsetToLoadInstruction);
- // The initial (invalid) offset has to be large enough to force
- // a 32-bit instruction encoding to allow patching with an
- // arbitrary offset. Use kMaxInt (minus kHeapObjectTag).
- int offset = kMaxInt;
- masm->mov(value.reg(), FieldOperand(receiver.reg(), offset));
-
- __ IncrementCounter(&Counters::named_load_inline, 1);
- deferred->BindExit();
- cgen_->frame()->Push(&receiver);
- cgen_->frame()->Push(&value);
- }
+ if (persist_after_get_) cgen_->frame()->Dup();
+ Result result = cgen_->EmitNamedLoad(GetName(), is_global);
+ if (!persist_after_get_) set_unloaded();
+ cgen_->frame()->Push(&result);
break;
}
case KEYED: {
- Variable* var = expression_->AsVariableProxy()->AsVariable();
- bool is_global = var != NULL;
- ASSERT(!is_global || var->is_global());
- Result value = cgen_->EmitKeyedLoad(is_global);
+ if (persist_after_get_) {
+ cgen_->frame()->PushElementAt(1);
+ cgen_->frame()->PushElementAt(1);
+ }
+ Result value = cgen_->EmitKeyedLoad();
cgen_->frame()->Push(&value);
+ if (!persist_after_get_) set_unloaded();
break;
}
default:
UNREACHABLE();
}
-
- if (!persist_after_get_) {
- cgen_->UnloadReference(this);
- }
}
@@ -6708,14 +7127,13 @@ void Reference::SetValue(InitState init_state) {
Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
ASSERT(slot != NULL);
cgen_->StoreToSlot(slot, init_state);
- cgen_->UnloadReference(this);
+ set_unloaded();
break;
}
case NAMED: {
Comment cmnt(masm, "[ Store to named Property");
- cgen_->frame()->Push(GetName());
- Result answer = cgen_->frame()->CallStoreIC();
+ Result answer = cgen_->EmitNamedStore(GetName(), false);
cgen_->frame()->Push(&answer);
set_unloaded();
break;
@@ -6723,108 +7141,16 @@ void Reference::SetValue(InitState init_state) {
case KEYED: {
Comment cmnt(masm, "[ Store to keyed Property");
-
- // Generate inlined version of the keyed store if the code is in
- // a loop and the key is likely to be a smi.
Property* property = expression()->AsProperty();
ASSERT(property != NULL);
- StaticType* key_smi_analysis = property->key()->type();
-
- if (cgen_->loop_nesting() > 0 && key_smi_analysis->IsLikelySmi()) {
- Comment cmnt(masm, "[ Inlined store to keyed Property");
-
- // Get the receiver, key and value into registers.
- Result value = cgen_->frame()->Pop();
- Result key = cgen_->frame()->Pop();
- Result receiver = cgen_->frame()->Pop();
-
- Result tmp = cgen_->allocator_->Allocate();
- ASSERT(tmp.is_valid());
-
- // Determine whether the value is a constant before putting it
- // in a register.
- bool value_is_constant = value.is_constant();
-
- // Make sure that value, key and receiver are in registers.
- value.ToRegister();
- key.ToRegister();
- receiver.ToRegister();
-
- DeferredReferenceSetKeyedValue* deferred =
- new DeferredReferenceSetKeyedValue(value.reg(),
- key.reg(),
- receiver.reg());
-
- // Check that the value is a smi if it is not a constant. We
- // can skip the write barrier for smis and constants.
- if (!value_is_constant) {
- __ test(value.reg(), Immediate(kSmiTagMask));
- deferred->Branch(not_zero);
- }
-
- // Check that the key is a non-negative smi.
- __ test(key.reg(), Immediate(kSmiTagMask | 0x80000000));
- deferred->Branch(not_zero);
-
- // Check that the receiver is not a smi.
- __ test(receiver.reg(), Immediate(kSmiTagMask));
- deferred->Branch(zero);
-
- // Check that the receiver is a JSArray.
- __ mov(tmp.reg(),
- FieldOperand(receiver.reg(), HeapObject::kMapOffset));
- __ movzx_b(tmp.reg(),
- FieldOperand(tmp.reg(), Map::kInstanceTypeOffset));
- __ cmp(tmp.reg(), JS_ARRAY_TYPE);
- deferred->Branch(not_equal);
-
- // Check that the key is within bounds. Both the key and the
- // length of the JSArray are smis.
- __ cmp(key.reg(),
- FieldOperand(receiver.reg(), JSArray::kLengthOffset));
- deferred->Branch(greater_equal);
-
- // Get the elements array from the receiver and check that it
- // is not a dictionary.
- __ mov(tmp.reg(),
- FieldOperand(receiver.reg(), JSObject::kElementsOffset));
- // Bind the deferred code patch site to be able to locate the
- // fixed array map comparison. When debugging, we patch this
- // comparison to always fail so that we will hit the IC call
- // in the deferred code which will allow the debugger to
- // break for fast case stores.
- __ bind(deferred->patch_site());
- __ cmp(FieldOperand(tmp.reg(), HeapObject::kMapOffset),
- Immediate(Factory::fixed_array_map()));
- deferred->Branch(not_equal);
-
- // Store the value.
- __ mov(Operand(tmp.reg(),
- key.reg(),
- times_2,
- FixedArray::kHeaderSize - kHeapObjectTag),
- value.reg());
- __ IncrementCounter(&Counters::keyed_store_inline, 1);
-
- deferred->BindExit();
-
- cgen_->frame()->Push(&receiver);
- cgen_->frame()->Push(&key);
- cgen_->frame()->Push(&value);
- } else {
- Result answer = cgen_->frame()->CallKeyedStoreIC();
- // Make sure that we do not have a test instruction after the
- // call. A test instruction after the call is used to
- // indicate that we have generated an inline version of the
- // keyed store.
- __ nop();
- cgen_->frame()->Push(&answer);
- }
- cgen_->UnloadReference(this);
+ Result answer = cgen_->EmitKeyedStore(property->key()->type());
+ cgen_->frame()->Push(&answer);
+ set_unloaded();
break;
}
- default:
+ case UNLOADED:
+ case ILLEGAL:
UNREACHABLE();
}
}
@@ -6918,6 +7244,13 @@ void FastNewContextStub::Generate(MacroAssembler* masm) {
void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
+ // Stack layout on entry:
+ //
+ // [esp + kPointerSize]: constant elements.
+ // [esp + (2 * kPointerSize)]: literal index.
+ // [esp + (3 * kPointerSize)]: literals array.
+
+ // All sizes here are multiples of kPointerSize.
int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0;
int size = JSArray::kSize + elements_size;
@@ -7037,6 +7370,8 @@ void GenericBinaryOpStub::GenerateCall(
}
} else if (left.is(left_arg)) {
__ mov(right_arg, right);
+ } else if (right.is(right_arg)) {
+ __ mov(left_arg, left);
} else if (left.is(right_arg)) {
if (IsOperationCommutative()) {
__ mov(left_arg, right);
@@ -7055,8 +7390,6 @@ void GenericBinaryOpStub::GenerateCall(
__ mov(right_arg, right);
__ mov(left_arg, left);
}
- } else if (right.is(right_arg)) {
- __ mov(left_arg, left);
} else {
// Order of moves is not important.
__ mov(left_arg, left);
@@ -7092,6 +7425,10 @@ void GenericBinaryOpStub::GenerateCall(
__ mov(left_arg, Immediate(right));
SetArgsReversed();
} else {
+ // For non-commutative operations, left and right_arg might be
+ // the same register. Therefore, the order of the moves is
+ // important here in order to not overwrite left before moving
+ // it to left_arg.
__ mov(left_arg, left);
__ mov(right_arg, Immediate(right));
}
@@ -7124,8 +7461,12 @@ void GenericBinaryOpStub::GenerateCall(
__ mov(right_arg, Immediate(left));
SetArgsReversed();
} else {
- __ mov(left_arg, Immediate(left));
+ // For non-commutative operations, right and left_arg might be
+ // the same register. Therefore, the order of the moves is
+ // important here in order to not overwrite right before moving
+ // it to right_arg.
__ mov(right_arg, right);
+ __ mov(left_arg, Immediate(left));
}
// Update flags to indicate that arguments are in registers.
SetArgsInRegisters();
@@ -7493,7 +7834,18 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
case Token::DIV: {
if (CpuFeatures::IsSupported(SSE2)) {
CpuFeatures::Scope use_sse2(SSE2);
- FloatingPointHelper::LoadSSE2Operands(masm, &call_runtime);
+ if (NumberInfo::IsNumber(operands_type_)) {
+ if (FLAG_debug_code) {
+ // Assert at runtime that inputs are only numbers.
+ __ AbortIfNotNumber(edx,
+ "GenericBinaryOpStub operand not a number.");
+ __ AbortIfNotNumber(eax,
+ "GenericBinaryOpStub operand not a number.");
+ }
+ FloatingPointHelper::LoadSSE2Operands(masm);
+ } else {
+ FloatingPointHelper::LoadSSE2Operands(masm, &call_runtime);
+ }
switch (op_) {
case Token::ADD: __ addsd(xmm0, xmm1); break;
@@ -7506,7 +7858,17 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
__ movdbl(FieldOperand(eax, HeapNumber::kValueOffset), xmm0);
GenerateReturn(masm);
} else { // SSE2 not available, use FPU.
- FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx);
+ if (NumberInfo::IsNumber(operands_type_)) {
+ if (FLAG_debug_code) {
+ // Assert at runtime that inputs are only numbers.
+ __ AbortIfNotNumber(edx,
+ "GenericBinaryOpStub operand not a number.");
+ __ AbortIfNotNumber(eax,
+ "GenericBinaryOpStub operand not a number.");
+ }
+ } else {
+ FloatingPointHelper::CheckFloatOperands(masm, &call_runtime, ebx);
+ }
FloatingPointHelper::LoadFloatOperands(
masm,
ecx,
@@ -7618,7 +7980,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
switch (op_) {
case Token::ADD: {
// Test for string arguments before calling runtime.
- Label not_strings, not_string1, string1;
+ Label not_strings, not_string1, string1, string1_smi2;
Result answer;
__ test(edx, Immediate(kSmiTagMask));
__ j(zero, &not_string1);
@@ -7627,15 +7989,28 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
// First argument is a string, test second.
__ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &string1);
+ __ j(zero, &string1_smi2);
__ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ecx);
__ j(above_equal, &string1);
// First and second argument are strings. Jump to the string add stub.
- StringAddStub stub(NO_STRING_CHECK_IN_STUB);
- __ TailCallStub(&stub);
+ StringAddStub string_add_stub(NO_STRING_CHECK_IN_STUB);
+ __ TailCallStub(&string_add_stub);
+
+ __ bind(&string1_smi2);
+ // First argument is a string, second is a smi. Try to lookup the number
+ // string for the smi in the number string cache.
+ NumberToStringStub::GenerateLookupNumberStringCache(
+ masm, eax, edi, ebx, ecx, true, &string1);
+
+ // Call the string add stub to make the result.
+ __ EnterInternalFrame();
+ __ push(edx); // Original first argument.
+ __ push(edi); // Number to string result for second argument.
+ __ CallStub(&string_add_stub);
+ __ LeaveInternalFrame();
+ __ ret(2 * kPointerSize);
- // Only first argument is a string.
__ bind(&string1);
__ InvokeBuiltin(
HasArgsReversed() ?
@@ -7766,6 +8141,212 @@ void GenericBinaryOpStub::GenerateReturn(MacroAssembler* masm) {
}
+void TranscendentalCacheStub::Generate(MacroAssembler* masm) {
+ // Input on stack:
+ // esp[4]: argument (should be number).
+ // esp[0]: return address.
+ // Test that eax is a number.
+ Label runtime_call;
+ Label runtime_call_clear_stack;
+ Label input_not_smi;
+ Label loaded;
+ __ mov(eax, Operand(esp, kPointerSize));
+ __ test(eax, Immediate(kSmiTagMask));
+ __ j(not_zero, &input_not_smi);
+ // Input is a smi. Untag and load it onto the FPU stack.
+ // Then load the low and high words of the double into ebx, edx.
+ ASSERT_EQ(1, kSmiTagSize);
+ __ sar(eax, 1);
+ __ sub(Operand(esp), Immediate(2 * kPointerSize));
+ __ mov(Operand(esp, 0), eax);
+ __ fild_s(Operand(esp, 0));
+ __ fst_d(Operand(esp, 0));
+ __ pop(edx);
+ __ pop(ebx);
+ __ jmp(&loaded);
+ __ bind(&input_not_smi);
+ // Check if input is a HeapNumber.
+ __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ cmp(Operand(ebx), Immediate(Factory::heap_number_map()));
+ __ j(not_equal, &runtime_call);
+ // Input is a HeapNumber. Push it on the FPU stack and load its
+ // low and high words into ebx, edx.
+ __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ __ mov(edx, FieldOperand(eax, HeapNumber::kExponentOffset));
+ __ mov(ebx, FieldOperand(eax, HeapNumber::kMantissaOffset));
+
+ __ bind(&loaded);
+ // ST[0] == double value
+ // ebx = low 32 bits of double value
+ // edx = high 32 bits of double value
+ // Compute hash:
+ // h = (low ^ high); h ^= h >> 16; h ^= h >> 8; h = h & (cacheSize - 1);
+ __ mov(ecx, ebx);
+ __ xor_(ecx, Operand(edx));
+ __ mov(eax, ecx);
+ __ sar(eax, 16);
+ __ xor_(ecx, Operand(eax));
+ __ mov(eax, ecx);
+ __ sar(eax, 8);
+ __ xor_(ecx, Operand(eax));
+ ASSERT(IsPowerOf2(TranscendentalCache::kCacheSize));
+ __ and_(Operand(ecx), Immediate(TranscendentalCache::kCacheSize - 1));
+ // ST[0] == double value.
+ // ebx = low 32 bits of double value.
+ // edx = high 32 bits of double value.
+ // ecx = TranscendentalCache::hash(double value).
+ __ mov(eax,
+ Immediate(ExternalReference::transcendental_cache_array_address()));
+ // Eax points to cache array.
+ __ mov(eax, Operand(eax, type_ * sizeof(TranscendentalCache::caches_[0])));
+ // Eax points to the cache for the type type_.
+ // If NULL, the cache hasn't been initialized yet, so go through runtime.
+ __ test(eax, Operand(eax));
+ __ j(zero, &runtime_call_clear_stack);
+#ifdef DEBUG
+ // Check that the layout of cache elements match expectations.
+ { // NOLINT - doesn't like a single brace on a line.
+ TranscendentalCache::Element test_elem[2];
+ char* elem_start = reinterpret_cast<char*>(&test_elem[0]);
+ char* elem2_start = reinterpret_cast<char*>(&test_elem[1]);
+ char* elem_in0 = reinterpret_cast<char*>(&(test_elem[0].in[0]));
+ char* elem_in1 = reinterpret_cast<char*>(&(test_elem[0].in[1]));
+ char* elem_out = reinterpret_cast<char*>(&(test_elem[0].output));
+ CHECK_EQ(12, elem2_start - elem_start); // Two uint_32's and a pointer.
+ CHECK_EQ(0, elem_in0 - elem_start);
+ CHECK_EQ(kIntSize, elem_in1 - elem_start);
+ CHECK_EQ(2 * kIntSize, elem_out - elem_start);
+ }
+#endif
+ // Find the address of the ecx'th entry in the cache, i.e., &eax[ecx*12].
+ __ lea(ecx, Operand(ecx, ecx, times_2, 0));
+ __ lea(ecx, Operand(eax, ecx, times_4, 0));
+ // Check if cache matches: Double value is stored in uint32_t[2] array.
+ Label cache_miss;
+ __ cmp(ebx, Operand(ecx, 0));
+ __ j(not_equal, &cache_miss);
+ __ cmp(edx, Operand(ecx, kIntSize));
+ __ j(not_equal, &cache_miss);
+ // Cache hit!
+ __ mov(eax, Operand(ecx, 2 * kIntSize));
+ __ fstp(0);
+ __ ret(kPointerSize);
+
+ __ bind(&cache_miss);
+ // Update cache with new value.
+ // We are short on registers, so use no_reg as scratch.
+ // This gives slightly larger code.
+ __ AllocateHeapNumber(eax, edi, no_reg, &runtime_call_clear_stack);
+ GenerateOperation(masm);
+ __ mov(Operand(ecx, 0), ebx);
+ __ mov(Operand(ecx, kIntSize), edx);
+ __ mov(Operand(ecx, 2 * kIntSize), eax);
+ __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
+ __ ret(kPointerSize);
+
+ __ bind(&runtime_call_clear_stack);
+ __ fstp(0);
+ __ bind(&runtime_call);
+ __ TailCallRuntime(ExternalReference(RuntimeFunction()), 1, 1);
+}
+
+
+Runtime::FunctionId TranscendentalCacheStub::RuntimeFunction() {
+ switch (type_) {
+ // Add more cases when necessary.
+ case TranscendentalCache::SIN: return Runtime::kMath_sin;
+ case TranscendentalCache::COS: return Runtime::kMath_cos;
+ default:
+ UNIMPLEMENTED();
+ return Runtime::kAbort;
+ }
+}
+
+
+void TranscendentalCacheStub::GenerateOperation(MacroAssembler* masm) {
+ // Only free register is edi.
+ Label done;
+ ASSERT(type_ == TranscendentalCache::SIN ||
+ type_ == TranscendentalCache::COS);
+ // More transcendental types can be added later.
+
+ // Both fsin and fcos require arguments in the range +/-2^63 and
+ // return NaN for infinities and NaN. They can share all code except
+ // the actual fsin/fcos operation.
+ Label in_range;
+ // If argument is outside the range -2^63..2^63, fsin/cos doesn't
+ // work. We must reduce it to the appropriate range.
+ __ mov(edi, edx);
+ __ and_(Operand(edi), Immediate(0x7ff00000)); // Exponent only.
+ int supported_exponent_limit =
+ (63 + HeapNumber::kExponentBias) << HeapNumber::kExponentShift;
+ __ cmp(Operand(edi), Immediate(supported_exponent_limit));
+ __ j(below, &in_range, taken);
+ // Check for infinity and NaN. Both return NaN for sin.
+ __ cmp(Operand(edi), Immediate(0x7ff00000));
+ Label non_nan_result;
+ __ j(not_equal, &non_nan_result, taken);
+ // Input is +/-Infinity or NaN. Result is NaN.
+ __ fstp(0);
+ // NaN is represented by 0x7ff8000000000000.
+ __ push(Immediate(0x7ff80000));
+ __ push(Immediate(0));
+ __ fld_d(Operand(esp, 0));
+ __ add(Operand(esp), Immediate(2 * kPointerSize));
+ __ jmp(&done);
+
+ __ bind(&non_nan_result);
+
+ // Use fpmod to restrict argument to the range +/-2*PI.
+ __ mov(edi, eax); // Save eax before using fnstsw_ax.
+ __ fldpi();
+ __ fadd(0);
+ __ fld(1);
+ // FPU Stack: input, 2*pi, input.
+ {
+ Label no_exceptions;
+ __ fwait();
+ __ fnstsw_ax();
+ // Clear if Illegal Operand or Zero Division exceptions are set.
+ __ test(Operand(eax), Immediate(5));
+ __ j(zero, &no_exceptions);
+ __ fnclex();
+ __ bind(&no_exceptions);
+ }
+
+ // Compute st(0) % st(1)
+ {
+ Label partial_remainder_loop;
+ __ bind(&partial_remainder_loop);
+ __ fprem1();
+ __ fwait();
+ __ fnstsw_ax();
+ __ test(Operand(eax), Immediate(0x400 /* C2 */));
+ // If C2 is set, computation only has partial result. Loop to
+ // continue computation.
+ __ j(not_zero, &partial_remainder_loop);
+ }
+ // FPU Stack: input, 2*pi, input % 2*pi
+ __ fstp(2);
+ __ fstp(0);
+ __ mov(eax, edi); // Restore eax (allocated HeapNumber pointer).
+
+ // FPU Stack: input % 2*pi
+ __ bind(&in_range);
+ switch (type_) {
+ case TranscendentalCache::SIN:
+ __ fsin();
+ break;
+ case TranscendentalCache::COS:
+ __ fcos();
+ break;
+ default:
+ UNREACHABLE();
+ }
+ __ bind(&done);
+}
+
+
// Get the integer part of a heap number. Surprisingly, all this bit twiddling
// is faster than using the built-in instructions on floating point registers.
// Trashes edi and ebx. Dest is ecx. Source cannot be ecx or one of the
@@ -7977,6 +8558,35 @@ void FloatingPointHelper::LoadFloatOperand(MacroAssembler* masm,
}
+void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm) {
+ Label load_smi_edx, load_eax, load_smi_eax, done;
+ // Load operand in edx into xmm0.
+ __ test(edx, Immediate(kSmiTagMask));
+ __ j(zero, &load_smi_edx, not_taken); // Argument in edx is a smi.
+ __ movdbl(xmm0, FieldOperand(edx, HeapNumber::kValueOffset));
+
+ __ bind(&load_eax);
+ // Load operand in eax into xmm1.
+ __ test(eax, Immediate(kSmiTagMask));
+ __ j(zero, &load_smi_eax, not_taken); // Argument in eax is a smi.
+ __ movdbl(xmm1, FieldOperand(eax, HeapNumber::kValueOffset));
+ __ jmp(&done);
+
+ __ bind(&load_smi_edx);
+ __ SmiUntag(edx); // Untag smi before converting to float.
+ __ cvtsi2sd(xmm0, Operand(edx));
+ __ SmiTag(edx); // Retag smi for heap number overwriting test.
+ __ jmp(&load_eax);
+
+ __ bind(&load_smi_eax);
+ __ SmiUntag(eax); // Untag smi before converting to float.
+ __ cvtsi2sd(xmm1, Operand(eax));
+ __ SmiTag(eax); // Retag smi for heap number overwriting test.
+
+ __ bind(&done);
+}
+
+
void FloatingPointHelper::LoadSSE2Operands(MacroAssembler* masm,
Label* not_numbers) {
Label load_smi_edx, load_eax, load_smi_eax, load_float_eax, done;
@@ -8306,6 +8916,11 @@ void ArgumentsAccessStub::GenerateReadElement(MacroAssembler* masm) {
void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
+ // esp[0] : return address
+ // esp[4] : number of parameters
+ // esp[8] : receiver displacement
+ // esp[16] : function
+
// The displacement is used for skipping the return address and the
// frame pointer on the stack. It is the offset of the last
// parameter (if any) relative to the frame pointer.
@@ -8389,7 +9004,6 @@ void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
__ add(Operand(edi), Immediate(kPointerSize));
__ sub(Operand(edx), Immediate(kPointerSize));
__ dec(ecx);
- __ test(ecx, Operand(ecx));
__ j(not_zero, &loop);
// Return and remove the on-stack parameters.
@@ -8737,6 +9351,74 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
}
+void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
+ Register object,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ bool object_is_smi,
+ Label* not_found) {
+ // Currently only lookup for smis. Check for smi if object is not known to be
+ // a smi.
+ if (!object_is_smi) {
+ ASSERT(kSmiTag == 0);
+ __ test(object, Immediate(kSmiTagMask));
+ __ j(not_zero, not_found);
+ }
+
+ // Use of registers. Register result is used as a temporary.
+ Register number_string_cache = result;
+ Register mask = scratch1;
+ Register scratch = scratch2;
+
+ // Load the number string cache.
+ ExternalReference roots_address = ExternalReference::roots_address();
+ __ mov(scratch, Immediate(Heap::kNumberStringCacheRootIndex));
+ __ mov(number_string_cache,
+ Operand::StaticArray(scratch, times_pointer_size, roots_address));
+ // Make the hash mask from the length of the number string cache. It
+ // contains two elements (number and string) for each cache entry.
+ __ mov(mask, FieldOperand(number_string_cache, FixedArray::kLengthOffset));
+ __ shr(mask, 1); // Divide length by two (length is not a smi).
+ __ sub(Operand(mask), Immediate(1)); // Make mask.
+ // Calculate the entry in the number string cache. The hash value in the
+ // number string cache for smis is just the smi value.
+ __ mov(scratch, object);
+ __ SmiUntag(scratch);
+ __ and_(scratch, Operand(mask));
+ // Check if the entry is the smi we are looking for.
+ __ cmp(object,
+ FieldOperand(number_string_cache,
+ scratch,
+ times_twice_pointer_size,
+ FixedArray::kHeaderSize));
+ __ j(not_equal, not_found);
+
+ // Get the result from the cache.
+ __ mov(result,
+ FieldOperand(number_string_cache,
+ scratch,
+ times_twice_pointer_size,
+ FixedArray::kHeaderSize + kPointerSize));
+ __ IncrementCounter(&Counters::number_to_string_native, 1);
+}
+
+
+void NumberToStringStub::Generate(MacroAssembler* masm) {
+ Label runtime;
+
+ __ mov(ebx, Operand(esp, kPointerSize));
+
+ // Generate code to lookup number in the number string cache.
+ GenerateLookupNumberStringCache(masm, ebx, eax, ecx, edx, false, &runtime);
+ __ ret(1 * kPointerSize);
+
+ __ bind(&runtime);
+ // Handle number to string in the runtime system if not found in the cache.
+ __ TailCallRuntime(ExternalReference(Runtime::kNumberToString), 1, 1);
+}
+
+
void CompareStub::Generate(MacroAssembler* masm) {
Label call_builtin, done;
@@ -9069,6 +9751,9 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
// Slow-case: Non-function called.
__ bind(&slow);
+ // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
+ // of the original receiver from the call site).
+ __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edi);
__ Set(eax, Immediate(argc_));
__ Set(ebx, Immediate(0));
__ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
@@ -9642,13 +10327,34 @@ void StringAddStub::Generate(MacroAssembler* masm) {
// ecx: length of second string
// edx: second string
// Look at the length of the result of adding the two strings.
- Label string_add_flat_result;
+ Label string_add_flat_result, longer_than_two;
__ bind(&both_not_zero_length);
__ add(ebx, Operand(ecx));
// Use the runtime system when adding two one character strings, as it
// contains optimizations for this specific case using the symbol table.
__ cmp(ebx, 2);
- __ j(equal, &string_add_runtime);
+ __ j(not_equal, &longer_than_two);
+
+ // Check that both strings are non-external ascii strings.
+ __ JumpIfNotBothSequentialAsciiStrings(eax, edx, ebx, ecx,
+ &string_add_runtime);
+
+ // Get the two characters forming the sub string.
+ __ movzx_b(ebx, FieldOperand(eax, SeqAsciiString::kHeaderSize));
+ __ movzx_b(ecx, FieldOperand(edx, SeqAsciiString::kHeaderSize));
+
+ // Try to lookup two character string in symbol table. If it is not found
+ // just allocate a new one.
+ Label make_two_character_string, make_flat_ascii_string;
+ GenerateTwoCharacterSymbolTableProbe(masm, ebx, ecx, eax, edx, edi,
+ &make_two_character_string);
+ __ ret(2 * kPointerSize);
+
+ __ bind(&make_two_character_string);
+ __ Set(ebx, Immediate(2));
+ __ jmp(&make_flat_ascii_string);
+
+ __ bind(&longer_than_two);
// Check if resulting string will be flat.
__ cmp(ebx, String::kMinNonFlatLength);
__ j(below, &string_add_flat_result);
@@ -9715,7 +10421,10 @@ void StringAddStub::Generate(MacroAssembler* masm) {
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
__ test(ecx, Immediate(kAsciiStringTag));
__ j(zero, &string_add_runtime);
+
+ __ bind(&make_flat_ascii_string);
// Both strings are ascii strings. As they are short they are both flat.
+ // ebx: length of resulting flat string
__ AllocateAsciiString(eax, ebx, ecx, edx, edi, &string_add_runtime);
// eax: result string
__ mov(ecx, eax);
@@ -9872,6 +10581,190 @@ void StringStubBase::GenerateCopyCharactersREP(MacroAssembler* masm,
}
+void StringStubBase::GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
+ Register c1,
+ Register c2,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* not_found) {
+ // Register scratch3 is the general scratch register in this function.
+ Register scratch = scratch3;
+
+ // Make sure that both characters are not digits as such strings has a
+ // different hash algorithm. Don't try to look for these in the symbol table.
+ Label not_array_index;
+ __ mov(scratch, c1);
+ __ sub(Operand(scratch), Immediate(static_cast<int>('0')));
+ __ cmp(Operand(scratch), Immediate(static_cast<int>('9' - '0')));
+ __ j(above, &not_array_index);
+ __ mov(scratch, c2);
+ __ sub(Operand(scratch), Immediate(static_cast<int>('0')));
+ __ cmp(Operand(scratch), Immediate(static_cast<int>('9' - '0')));
+ __ j(below_equal, not_found);
+
+ __ bind(&not_array_index);
+ // Calculate the two character string hash.
+ Register hash = scratch1;
+ GenerateHashInit(masm, hash, c1, scratch);
+ GenerateHashAddCharacter(masm, hash, c2, scratch);
+ GenerateHashGetHash(masm, hash, scratch);
+
+ // Collect the two characters in a register.
+ Register chars = c1;
+ __ shl(c2, kBitsPerByte);
+ __ or_(chars, Operand(c2));
+
+ // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
+ // hash: hash of two character string.
+
+ // Load the symbol table.
+ Register symbol_table = c2;
+ ExternalReference roots_address = ExternalReference::roots_address();
+ __ mov(scratch, Immediate(Heap::kSymbolTableRootIndex));
+ __ mov(symbol_table,
+ Operand::StaticArray(scratch, times_pointer_size, roots_address));
+
+ // Calculate capacity mask from the symbol table capacity.
+ Register mask = scratch2;
+ static const int kCapacityOffset =
+ FixedArray::kHeaderSize +
+ SymbolTable::kCapacityIndex * kPointerSize;
+ __ mov(mask, FieldOperand(symbol_table, kCapacityOffset));
+ __ SmiUntag(mask);
+ __ sub(Operand(mask), Immediate(1));
+
+ // Registers
+ // chars: two character string, char 1 in byte 0 and char 2 in byte 1.
+ // hash: hash of two character string
+ // symbol_table: symbol table
+ // mask: capacity mask
+ // scratch: -
+
+ // Perform a number of probes in the symbol table.
+ static const int kProbes = 4;
+ Label found_in_symbol_table;
+ Label next_probe[kProbes], next_probe_pop_mask[kProbes];
+ for (int i = 0; i < kProbes; i++) {
+ // Calculate entry in symbol table.
+ __ mov(scratch, hash);
+ if (i > 0) {
+ __ add(Operand(scratch), Immediate(SymbolTable::GetProbeOffset(i)));
+ }
+ __ and_(scratch, Operand(mask));
+
+ // Load the entry from the symble table.
+ Register candidate = scratch; // Scratch register contains candidate.
+ ASSERT_EQ(1, SymbolTableShape::kEntrySize);
+ static const int kFirstElementOffset =
+ FixedArray::kHeaderSize +
+ SymbolTable::kPrefixStartIndex * kPointerSize +
+ SymbolTableShape::kPrefixSize * kPointerSize;
+ __ mov(candidate,
+ FieldOperand(symbol_table,
+ scratch,
+ times_pointer_size,
+ kFirstElementOffset));
+
+ // If entry is undefined no string with this hash can be found.
+ __ cmp(candidate, Factory::undefined_value());
+ __ j(equal, not_found);
+
+ // If length is not 2 the string is not a candidate.
+ __ cmp(FieldOperand(candidate, String::kLengthOffset), Immediate(2));
+ __ j(not_equal, &next_probe[i]);
+
+ // As we are out of registers save the mask on the stack and use that
+ // register as a temporary.
+ __ push(mask);
+ Register temp = mask;
+
+ // Check that the candidate is a non-external ascii string.
+ __ mov(temp, FieldOperand(candidate, HeapObject::kMapOffset));
+ __ movzx_b(temp, FieldOperand(temp, Map::kInstanceTypeOffset));
+ __ JumpIfInstanceTypeIsNotSequentialAscii(
+ temp, temp, &next_probe_pop_mask[i]);
+
+ // Check if the two characters match.
+ __ mov(temp, FieldOperand(candidate, SeqAsciiString::kHeaderSize));
+ __ and_(temp, 0x0000ffff);
+ __ cmp(chars, Operand(temp));
+ __ j(equal, &found_in_symbol_table);
+ __ bind(&next_probe_pop_mask[i]);
+ __ pop(mask);
+ __ bind(&next_probe[i]);
+ }
+
+ // No matching 2 character string found by probing.
+ __ jmp(not_found);
+
+ // Scratch register contains result when we fall through to here.
+ Register result = scratch;
+ __ bind(&found_in_symbol_table);
+ __ pop(mask); // Pop temporally saved mask from the stack.
+ if (!result.is(eax)) {
+ __ mov(eax, result);
+ }
+}
+
+
+void StringStubBase::GenerateHashInit(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch) {
+ // hash = character + (character << 10);
+ __ mov(hash, character);
+ __ shl(hash, 10);
+ __ add(hash, Operand(character));
+ // hash ^= hash >> 6;
+ __ mov(scratch, hash);
+ __ sar(scratch, 6);
+ __ xor_(hash, Operand(scratch));
+}
+
+
+void StringStubBase::GenerateHashAddCharacter(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch) {
+ // hash += character;
+ __ add(hash, Operand(character));
+ // hash += hash << 10;
+ __ mov(scratch, hash);
+ __ shl(scratch, 10);
+ __ add(hash, Operand(scratch));
+ // hash ^= hash >> 6;
+ __ mov(scratch, hash);
+ __ sar(scratch, 6);
+ __ xor_(hash, Operand(scratch));
+}
+
+
+void StringStubBase::GenerateHashGetHash(MacroAssembler* masm,
+ Register hash,
+ Register scratch) {
+ // hash += hash << 3;
+ __ mov(scratch, hash);
+ __ shl(scratch, 3);
+ __ add(hash, Operand(scratch));
+ // hash ^= hash >> 11;
+ __ mov(scratch, hash);
+ __ sar(scratch, 11);
+ __ xor_(hash, Operand(scratch));
+ // hash += hash << 15;
+ __ mov(scratch, hash);
+ __ shl(scratch, 15);
+ __ add(hash, Operand(scratch));
+
+ // if (hash == 0) hash = 27;
+ Label hash_not_zero;
+ __ test(hash, Operand(hash));
+ __ j(not_zero, &hash_not_zero);
+ __ mov(hash, Immediate(27));
+ __ bind(&hash_not_zero);
+}
+
+
void SubStringStub::Generate(MacroAssembler* masm) {
Label runtime;
@@ -9892,26 +10785,55 @@ void SubStringStub::Generate(MacroAssembler* masm) {
// eax: string
// ebx: instance type
// Calculate length of sub string using the smi values.
- __ mov(ecx, Operand(esp, 1 * kPointerSize)); // to
+ Label result_longer_than_two;
+ __ mov(ecx, Operand(esp, 1 * kPointerSize)); // To index.
__ test(ecx, Immediate(kSmiTagMask));
__ j(not_zero, &runtime);
- __ mov(edx, Operand(esp, 2 * kPointerSize)); // from
+ __ mov(edx, Operand(esp, 2 * kPointerSize)); // From index.
__ test(edx, Immediate(kSmiTagMask));
__ j(not_zero, &runtime);
__ sub(ecx, Operand(edx));
- // Handle sub-strings of length 2 and less in the runtime system.
+ // Special handling of sub-strings of length 1 and 2. One character strings
+ // are handled in the runtime system (looked up in the single character
+ // cache). Two character strings are looked for in the symbol cache.
__ SmiUntag(ecx); // Result length is no longer smi.
__ cmp(ecx, 2);
- __ j(below_equal, &runtime);
+ __ j(greater, &result_longer_than_two);
+ __ j(less, &runtime);
+
+ // Sub string of length 2 requested.
+ // eax: string
+ // ebx: instance type
+ // ecx: sub string length (value is 2)
+ // edx: from index (smi)
+ __ JumpIfInstanceTypeIsNotSequentialAscii(ebx, ebx, &runtime);
+
+ // Get the two characters forming the sub string.
+ __ SmiUntag(edx); // From index is no longer smi.
+ __ movzx_b(ebx, FieldOperand(eax, edx, times_1, SeqAsciiString::kHeaderSize));
+ __ movzx_b(ecx,
+ FieldOperand(eax, edx, times_1, SeqAsciiString::kHeaderSize + 1));
+
+ // Try to lookup two character string in symbol table.
+ Label make_two_character_string;
+ GenerateTwoCharacterSymbolTableProbe(masm, ebx, ecx, eax, edx, edi,
+ &make_two_character_string);
+ __ ret(2 * kPointerSize);
+
+ __ bind(&make_two_character_string);
+ // Setup registers for allocating the two character string.
+ __ mov(eax, Operand(esp, 3 * kPointerSize));
+ __ mov(ebx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ movzx_b(ebx, FieldOperand(ebx, Map::kInstanceTypeOffset));
+ __ Set(ecx, Immediate(2));
+ __ bind(&result_longer_than_two);
// eax: string
// ebx: instance type
// ecx: result string length
// Check for flat ascii string
Label non_ascii_flat;
- __ and_(ebx, kStringRepresentationMask | kStringEncodingMask);
- __ cmp(ebx, kSeqStringTag | kAsciiStringTag);
- __ j(not_equal, &non_ascii_flat);
+ __ JumpIfInstanceTypeIsNotSequentialAscii(ebx, ebx, &non_ascii_flat);
// Allocate the result.
__ AllocateAsciiString(eax, ecx, ebx, edx, edi, &runtime);
diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h
index 843bbf76..b84a6bba 100644
--- a/src/ia32/codegen-ia32.h
+++ b/src/ia32/codegen-ia32.h
@@ -294,15 +294,6 @@ enum ArgumentsAllocationMode {
class CodeGenerator: public AstVisitor {
public:
- // Compilation mode. Either the compiler is used as the primary
- // compiler and needs to setup everything or the compiler is used as
- // the secondary compiler for split compilation and has to handle
- // bailouts.
- enum Mode {
- PRIMARY,
- SECONDARY
- };
-
// Takes a function literal, generates code for it. This function should only
// be called by compiler.cc.
static Handle<Code> MakeCode(CompilationInfo* info);
@@ -384,7 +375,7 @@ class CodeGenerator: public AstVisitor {
void VisitStatementsAndSpill(ZoneList<Statement*>* statements);
// Main code generation function
- void Generate(CompilationInfo* info, Mode mode);
+ void Generate(CompilationInfo* info);
// Generate the return sequence code. Should be called no more than
// once per compiled function, immediately after binding the return
@@ -429,8 +420,8 @@ class CodeGenerator: public AstVisitor {
void LoadAndSpill(Expression* expression);
// Read a value from a slot and leave it on top of the expression stack.
- void LoadFromSlot(Slot* slot, TypeofState typeof_state);
- void LoadFromSlotCheckForArguments(Slot* slot, TypeofState typeof_state);
+ Result LoadFromSlot(Slot* slot, TypeofState typeof_state);
+ Result LoadFromSlotCheckForArguments(Slot* slot, TypeofState typeof_state);
Result LoadFromGlobalSlotCheckExtensions(Slot* slot,
TypeofState typeof_state,
JumpTarget* slow);
@@ -439,10 +430,23 @@ class CodeGenerator: public AstVisitor {
// value in place.
void StoreToSlot(Slot* slot, InitState init_state);
- // Load a property of an object, returning it in a Result.
- // The object and the property name are passed on the stack, and
- // not changed.
- Result EmitKeyedLoad(bool is_global);
+ // Support for compiling assignment expressions.
+ void EmitSlotAssignment(Assignment* node);
+ void EmitNamedPropertyAssignment(Assignment* node);
+ void EmitKeyedPropertyAssignment(Assignment* node);
+
+ // Receiver is passed on the frame and consumed.
+ Result EmitNamedLoad(Handle<String> name, bool is_contextual);
+
+ // If the store is contextual, value is passed on the frame and consumed.
+ // Otherwise, receiver and value are passed on the frame and consumed.
+ Result EmitNamedStore(Handle<String> name, bool is_contextual);
+
+ // Receiver and key are passed on the frame and consumed.
+ Result EmitKeyedLoad();
+
+ // Receiver, key, and value are passed on the frame and consumed.
+ Result EmitKeyedStore(StaticType* key_type);
// Special code for typeof expressions: Unfortunately, we must
// be careful when loading the expression in 'typeof'
@@ -533,12 +537,13 @@ class CodeGenerator: public AstVisitor {
void DeclareGlobals(Handle<FixedArray> pairs);
// Instantiate the function boilerplate.
- void InstantiateBoilerplate(Handle<JSFunction> boilerplate);
+ Result InstantiateBoilerplate(Handle<JSFunction> boilerplate);
// Support for type checks.
void GenerateIsSmi(ZoneList<Expression*>* args);
void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
void GenerateIsArray(ZoneList<Expression*>* args);
+ void GenerateIsRegExp(ZoneList<Expression*>* args);
void GenerateIsObject(ZoneList<Expression*>* args);
void GenerateIsFunction(ZoneList<Expression*>* args);
void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
@@ -580,6 +585,13 @@ class CodeGenerator: public AstVisitor {
// Support for direct calls from JavaScript to native RegExp code.
void GenerateRegExpExec(ZoneList<Expression*>* args);
+ // Fast support for number to string.
+ void GenerateNumberToString(ZoneList<Expression*>* args);
+
+ // Fast call to transcendental functions.
+ void GenerateMathSin(ZoneList<Expression*>* args);
+ void GenerateMathCos(ZoneList<Expression*>* args);
+
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
@@ -647,6 +659,22 @@ class CodeGenerator: public AstVisitor {
};
+// Compute a transcendental math function natively, or call the
+// TranscendentalCache runtime function.
+class TranscendentalCacheStub: public CodeStub {
+ public:
+ explicit TranscendentalCacheStub(TranscendentalCache::Type type)
+ : type_(type) {}
+ void Generate(MacroAssembler* masm);
+ private:
+ TranscendentalCache::Type type_;
+ Major MajorKey() { return TranscendentalCache; }
+ int MinorKey() { return type_; }
+ Runtime::FunctionId RuntimeFunction();
+ void GenerateOperation(MacroAssembler* masm);
+};
+
+
// Flag that indicates how to generate code for the stub GenericBinaryOpStub.
enum GenericBinaryFlags {
NO_GENERIC_BINARY_FLAGS = 0,
@@ -658,13 +686,15 @@ class GenericBinaryOpStub: public CodeStub {
public:
GenericBinaryOpStub(Token::Value op,
OverwriteMode mode,
- GenericBinaryFlags flags)
+ GenericBinaryFlags flags,
+ NumberInfo::Type operands_type = NumberInfo::kUnknown)
: op_(op),
mode_(mode),
flags_(flags),
args_in_registers_(false),
args_reversed_(false),
- name_(NULL) {
+ name_(NULL),
+ operands_type_(operands_type) {
use_sse3_ = CpuFeatures::IsSupported(SSE3);
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
@@ -689,28 +719,32 @@ class GenericBinaryOpStub: public CodeStub {
bool args_reversed_; // Left and right argument are swapped.
bool use_sse3_;
char* name_;
+ NumberInfo::Type operands_type_; // Number type information of operands.
const char* GetName();
#ifdef DEBUG
void Print() {
- PrintF("GenericBinaryOpStub (op %s), "
- "(mode %d, flags %d, registers %d, reversed %d)\n",
+ PrintF("GenericBinaryOpStub %d (op %s), "
+ "(mode %d, flags %d, registers %d, reversed %d, number_info %s)\n",
+ MinorKey(),
Token::String(op_),
static_cast<int>(mode_),
static_cast<int>(flags_),
static_cast<int>(args_in_registers_),
- static_cast<int>(args_reversed_));
+ static_cast<int>(args_reversed_),
+ NumberInfo::ToString(operands_type_));
}
#endif
- // Minor key encoding in 16 bits FRASOOOOOOOOOOMM.
+ // Minor key encoding in 16 bits NNNFRASOOOOOOOMM.
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
- class OpBits: public BitField<Token::Value, 2, 10> {};
- class SSE3Bits: public BitField<bool, 12, 1> {};
- class ArgsInRegistersBits: public BitField<bool, 13, 1> {};
- class ArgsReversedBits: public BitField<bool, 14, 1> {};
- class FlagBits: public BitField<GenericBinaryFlags, 15, 1> {};
+ class OpBits: public BitField<Token::Value, 2, 7> {};
+ class SSE3Bits: public BitField<bool, 9, 1> {};
+ class ArgsInRegistersBits: public BitField<bool, 10, 1> {};
+ class ArgsReversedBits: public BitField<bool, 11, 1> {};
+ class FlagBits: public BitField<GenericBinaryFlags, 12, 1> {};
+ class NumberInfoBits: public BitField<NumberInfo::Type, 13, 3> {};
Major MajorKey() { return GenericBinaryOp; }
int MinorKey() {
@@ -720,7 +754,8 @@ class GenericBinaryOpStub: public CodeStub {
| FlagBits::encode(flags_)
| SSE3Bits::encode(use_sse3_)
| ArgsInRegistersBits::encode(args_in_registers_)
- | ArgsReversedBits::encode(args_reversed_);
+ | ArgsReversedBits::encode(args_reversed_)
+ | NumberInfoBits::encode(operands_type_);
}
void Generate(MacroAssembler* masm);
@@ -767,6 +802,31 @@ class StringStubBase: public CodeStub {
Register count, // Must be ecx.
Register scratch, // Neither of the above.
bool ascii);
+
+ // Probe the symbol table for a two character string. If the string is
+ // not found by probing a jump to the label not_found is performed. This jump
+ // does not guarantee that the string is not in the symbol table. If the
+ // string is found the code falls through with the string in register eax.
+ void GenerateTwoCharacterSymbolTableProbe(MacroAssembler* masm,
+ Register c1,
+ Register c2,
+ Register scratch1,
+ Register scratch2,
+ Register scratch3,
+ Label* not_found);
+
+ // Generate string hash.
+ void GenerateHashInit(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch);
+ void GenerateHashAddCharacter(MacroAssembler* masm,
+ Register hash,
+ Register character,
+ Register scratch);
+ void GenerateHashGetHash(MacroAssembler* masm,
+ Register hash,
+ Register scratch);
};
@@ -828,6 +888,39 @@ class StringCompareStub: public StringStubBase {
};
+class NumberToStringStub: public CodeStub {
+ public:
+ NumberToStringStub() { }
+
+ // Generate code to do a lookup in the number string cache. If the number in
+ // the register object is found in the cache the generated code falls through
+ // with the result in the result register. The object and the result register
+ // can be the same. If the number is not found in the cache the code jumps to
+ // the label not_found with only the content of register object unchanged.
+ static void GenerateLookupNumberStringCache(MacroAssembler* masm,
+ Register object,
+ Register result,
+ Register scratch1,
+ Register scratch2,
+ bool object_is_smi,
+ Label* not_found);
+
+ private:
+ Major MajorKey() { return NumberToString; }
+ int MinorKey() { return 0; }
+
+ void Generate(MacroAssembler* masm);
+
+ const char* GetName() { return "NumberToStringStub"; }
+
+#ifdef DEBUG
+ void Print() {
+ PrintF("NumberToStringStub\n");
+ }
+#endif
+};
+
+
} } // namespace v8::internal
#endif // V8_IA32_CODEGEN_IA32_H_
diff --git a/src/ia32/debug-ia32.cc b/src/ia32/debug-ia32.cc
index 1f34b302..a9e26263 100644
--- a/src/ia32/debug-ia32.cc
+++ b/src/ia32/debug-ia32.cc
@@ -125,9 +125,10 @@ static void Generate_DebugBreakCallHelper(MacroAssembler* masm,
void Debug::GenerateLoadICDebugBreak(MacroAssembler* masm) {
// Register state for IC load call (from ic-ia32.cc).
// ----------- S t a t e -------------
+ // -- eax : receiver
// -- ecx : name
// -----------------------------------
- Generate_DebugBreakCallHelper(masm, ecx.bit(), false);
+ Generate_DebugBreakCallHelper(masm, eax.bit() | ecx.bit(), false);
}
diff --git a/src/ia32/disasm-ia32.cc b/src/ia32/disasm-ia32.cc
index cb500d56..a085900a 100644
--- a/src/ia32/disasm-ia32.cc
+++ b/src/ia32/disasm-ia32.cc
@@ -679,6 +679,7 @@ int DisassemblerIA32::MemoryFPUInstruction(int escape_opcode,
case 0xDD: switch (regop) {
case 0: mnem = "fld_d"; break;
+ case 2: mnem = "fstp"; break;
case 3: mnem = "fstp_d"; break;
default: UnimplementedInstruction();
}
@@ -720,6 +721,7 @@ int DisassemblerIA32::RegisterFPUInstruction(int escape_opcode,
case 0xE1: mnem = "fabs"; break;
case 0xE4: mnem = "ftst"; break;
case 0xE8: mnem = "fld1"; break;
+ case 0xEB: mnem = "fldpi"; break;
case 0xEE: mnem = "fldz"; break;
case 0xF5: mnem = "fprem1"; break;
case 0xF7: mnem = "fincstp"; break;
@@ -1014,7 +1016,6 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
int mod, regop, rm;
get_modrm(*data, &mod, &regop, &rm);
const char* mnem = NULL;
- printf("%d\n", regop);
switch (regop) {
case 5: mnem = "subb"; break;
case 7: mnem = "cmpb"; break;
diff --git a/src/ia32/fast-codegen-ia32.cc b/src/ia32/fast-codegen-ia32.cc
index 126f96b3..f1c25077 100644
--- a/src/ia32/fast-codegen-ia32.cc
+++ b/src/ia32/fast-codegen-ia32.cc
@@ -29,56 +29,474 @@
#include "codegen-inl.h"
#include "fast-codegen.h"
+#include "data-flow.h"
+#include "scopes.h"
namespace v8 {
namespace internal {
-#define __ ACCESS_MASM(masm())
+#define BAILOUT(reason) \
+ do { \
+ if (FLAG_trace_bailout) { \
+ PrintF("%s\n", reason); \
+ } \
+ has_supported_syntax_ = false; \
+ return; \
+ } while (false)
+
+
+#define CHECK_BAILOUT \
+ do { \
+ if (!has_supported_syntax_) return; \
+ } while (false)
+
+
+void FastCodeGenSyntaxChecker::Check(CompilationInfo* info) {
+ info_ = info;
+
+ // We do not specialize if we do not have a receiver or if it is not a
+ // JS object with fast mode properties.
+ if (!info->has_receiver()) BAILOUT("No receiver");
+ if (!info->receiver()->IsJSObject()) BAILOUT("Receiver is not an object");
+ Handle<JSObject> object = Handle<JSObject>::cast(info->receiver());
+ if (!object->HasFastProperties()) BAILOUT("Receiver is in dictionary mode");
+
+ // We do not support stack or heap slots (both of which require
+ // allocation).
+ Scope* scope = info->scope();
+ if (scope->num_stack_slots() > 0) {
+ BAILOUT("Function has stack-allocated locals");
+ }
+ if (scope->num_heap_slots() > 0) {
+ BAILOUT("Function has context-allocated locals");
+ }
-void FastCodeGenerator::EmitLoadReceiver(Register reg) {
- // Offset 2 is due to return address and saved frame pointer.
- int index = 2 + function()->scope()->num_parameters();
- __ mov(reg, Operand(ebp, index * kPointerSize));
+ VisitDeclarations(scope->declarations());
+ CHECK_BAILOUT;
+
+ // We do not support empty function bodies.
+ if (info->function()->body()->is_empty()) {
+ BAILOUT("Function has an empty body");
+ }
+ VisitStatements(info->function()->body());
}
-void FastCodeGenerator::EmitReceiverMapCheck() {
- Comment cmnt(masm(), ";; MapCheck(this)");
- if (FLAG_print_ir) {
- PrintF("MapCheck(this)\n");
+void FastCodeGenSyntaxChecker::VisitDeclarations(
+ ZoneList<Declaration*>* decls) {
+ if (!decls->is_empty()) BAILOUT("Function has declarations");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitStatements(ZoneList<Statement*>* stmts) {
+ if (stmts->length() != 1) {
+ BAILOUT("Function body is not a singleton statement.");
}
+ Visit(stmts->at(0));
+}
- ASSERT(info()->has_receiver() && info()->receiver()->IsHeapObject());
- Handle<HeapObject> object = Handle<HeapObject>::cast(info()->receiver());
- Handle<Map> map(object->map());
- EmitLoadReceiver(edx);
- __ CheckMap(edx, map, bailout(), false);
+void FastCodeGenSyntaxChecker::VisitDeclaration(Declaration* decl) {
+ UNREACHABLE();
}
-void FastCodeGenerator::EmitGlobalMapCheck() {
- Comment cmnt(masm(), ";; GlobalMapCheck");
- if (FLAG_print_ir) {
- PrintF(";; GlobalMapCheck()");
+void FastCodeGenSyntaxChecker::VisitBlock(Block* stmt) {
+ VisitStatements(stmt->statements());
+}
+
+
+void FastCodeGenSyntaxChecker::VisitExpressionStatement(
+ ExpressionStatement* stmt) {
+ Visit(stmt->expression());
+}
+
+
+void FastCodeGenSyntaxChecker::VisitEmptyStatement(EmptyStatement* stmt) {
+ // Supported.
+}
+
+
+void FastCodeGenSyntaxChecker::VisitIfStatement(IfStatement* stmt) {
+ BAILOUT("IfStatement");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitContinueStatement(ContinueStatement* stmt) {
+ BAILOUT("Continuestatement");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitBreakStatement(BreakStatement* stmt) {
+ BAILOUT("BreakStatement");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitReturnStatement(ReturnStatement* stmt) {
+ BAILOUT("ReturnStatement");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitWithEnterStatement(
+ WithEnterStatement* stmt) {
+ BAILOUT("WithEnterStatement");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitWithExitStatement(WithExitStatement* stmt) {
+ BAILOUT("WithExitStatement");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitSwitchStatement(SwitchStatement* stmt) {
+ BAILOUT("SwitchStatement");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitDoWhileStatement(DoWhileStatement* stmt) {
+ BAILOUT("DoWhileStatement");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitWhileStatement(WhileStatement* stmt) {
+ BAILOUT("WhileStatement");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitForStatement(ForStatement* stmt) {
+ BAILOUT("ForStatement");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitForInStatement(ForInStatement* stmt) {
+ BAILOUT("ForInStatement");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitTryCatchStatement(TryCatchStatement* stmt) {
+ BAILOUT("TryCatchStatement");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitTryFinallyStatement(
+ TryFinallyStatement* stmt) {
+ BAILOUT("TryFinallyStatement");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitDebuggerStatement(
+ DebuggerStatement* stmt) {
+ BAILOUT("DebuggerStatement");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitFunctionLiteral(FunctionLiteral* expr) {
+ BAILOUT("FunctionLiteral");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitFunctionBoilerplateLiteral(
+ FunctionBoilerplateLiteral* expr) {
+ BAILOUT("FunctionBoilerplateLiteral");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitConditional(Conditional* expr) {
+ BAILOUT("Conditional");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitSlot(Slot* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenSyntaxChecker::VisitVariableProxy(VariableProxy* expr) {
+ // Only global variable references are supported.
+ Variable* var = expr->var();
+ if (!var->is_global() || var->is_this()) BAILOUT("Non-global variable");
+
+ // Check if the global variable is existing and non-deletable.
+ if (info()->has_global_object()) {
+ LookupResult lookup;
+ info()->global_object()->Lookup(*expr->name(), &lookup);
+ if (!lookup.IsProperty()) {
+ BAILOUT("Non-existing global variable");
+ }
+ // We do not handle global variables with accessors or interceptors.
+ if (lookup.type() != NORMAL) {
+ BAILOUT("Global variable with accessors or interceptors.");
+ }
+ // We do not handle deletable global variables.
+ if (!lookup.IsDontDelete()) {
+ BAILOUT("Deletable global variable");
+ }
}
+}
+
+
+void FastCodeGenSyntaxChecker::VisitLiteral(Literal* expr) {
+ BAILOUT("Literal");
+}
- ASSERT(info()->has_global_object());
- Handle<Map> map(info()->global_object()->map());
- __ mov(ebx, CodeGenerator::GlobalObject());
- __ CheckMap(ebx, map, bailout(), true);
+void FastCodeGenSyntaxChecker::VisitRegExpLiteral(RegExpLiteral* expr) {
+ BAILOUT("RegExpLiteral");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitObjectLiteral(ObjectLiteral* expr) {
+ BAILOUT("ObjectLiteral");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitArrayLiteral(ArrayLiteral* expr) {
+ BAILOUT("ArrayLiteral");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitCatchExtensionObject(
+ CatchExtensionObject* expr) {
+ BAILOUT("CatchExtensionObject");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitAssignment(Assignment* expr) {
+ // Simple assignments to (named) this properties are supported.
+ if (expr->op() != Token::ASSIGN) BAILOUT("Non-simple assignment");
+
+ Property* prop = expr->target()->AsProperty();
+ if (prop == NULL) BAILOUT("Non-property assignment");
+ VariableProxy* proxy = prop->obj()->AsVariableProxy();
+ if (proxy == NULL || !proxy->var()->is_this()) {
+ BAILOUT("Non-this-property assignment");
+ }
+ if (!prop->key()->IsPropertyName()) {
+ BAILOUT("Non-named-property assignment");
+ }
+
+ // We will only specialize for fields on the object itself.
+ // Expression::IsPropertyName implies that the name is a literal
+ // symbol but we do not assume that.
+ Literal* key = prop->key()->AsLiteral();
+ if (key != NULL && key->handle()->IsString()) {
+ Handle<Object> receiver = info()->receiver();
+ Handle<String> name = Handle<String>::cast(key->handle());
+ LookupResult lookup;
+ receiver->Lookup(*name, &lookup);
+ if (!lookup.IsProperty()) {
+ BAILOUT("Assigned property not found at compile time");
+ }
+ if (lookup.holder() != *receiver) BAILOUT("Non-own property assignment");
+ if (!lookup.type() == FIELD) BAILOUT("Non-field property assignment");
+ } else {
+ UNREACHABLE();
+ BAILOUT("Unexpected non-string-literal property key");
+ }
+
+ Visit(expr->value());
+}
+
+
+void FastCodeGenSyntaxChecker::VisitThrow(Throw* expr) {
+ BAILOUT("Throw");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitProperty(Property* expr) {
+ // We support named this property references.
+ VariableProxy* proxy = expr->obj()->AsVariableProxy();
+ if (proxy == NULL || !proxy->var()->is_this()) {
+ BAILOUT("Non-this-property reference");
+ }
+ if (!expr->key()->IsPropertyName()) {
+ BAILOUT("Non-named-property reference");
+ }
+
+ // We will only specialize for fields on the object itself.
+ // Expression::IsPropertyName implies that the name is a literal
+ // symbol but we do not assume that.
+ Literal* key = expr->key()->AsLiteral();
+ if (key != NULL && key->handle()->IsString()) {
+ Handle<Object> receiver = info()->receiver();
+ Handle<String> name = Handle<String>::cast(key->handle());
+ LookupResult lookup;
+ receiver->Lookup(*name, &lookup);
+ if (!lookup.IsProperty()) {
+ BAILOUT("Referenced property not found at compile time");
+ }
+ if (lookup.holder() != *receiver) BAILOUT("Non-own property reference");
+ if (!lookup.type() == FIELD) BAILOUT("Non-field property reference");
+ } else {
+ UNREACHABLE();
+ BAILOUT("Unexpected non-string-literal property key");
+ }
+}
+
+
+void FastCodeGenSyntaxChecker::VisitCall(Call* expr) {
+ BAILOUT("Call");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitCallNew(CallNew* expr) {
+ BAILOUT("CallNew");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitCallRuntime(CallRuntime* expr) {
+ BAILOUT("CallRuntime");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitUnaryOperation(UnaryOperation* expr) {
+ BAILOUT("UnaryOperation");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitCountOperation(CountOperation* expr) {
+ BAILOUT("CountOperation");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitBinaryOperation(BinaryOperation* expr) {
+ // We support bitwise OR.
+ switch (expr->op()) {
+ case Token::COMMA:
+ BAILOUT("BinaryOperation COMMA");
+ case Token::OR:
+ BAILOUT("BinaryOperation OR");
+ case Token::AND:
+ BAILOUT("BinaryOperation AND");
+
+ case Token::BIT_OR:
+ // We support expressions nested on the left because they only require
+ // a pair of registers to keep all intermediate values in registers
+ // (i.e., the expression stack has height no more than two).
+ if (!expr->right()->IsLeaf()) BAILOUT("expression nested on right");
+
+ // We do not allow subexpressions with side effects because we
+ // (currently) bail out to the beginning of the full function. The
+ // only expressions with side effects that we would otherwise handle
+ // are assignments.
+ if (expr->left()->AsAssignment() != NULL ||
+ expr->right()->AsAssignment() != NULL) {
+ BAILOUT("subexpression of binary operation has side effects");
+ }
+
+ Visit(expr->left());
+ CHECK_BAILOUT;
+ Visit(expr->right());
+ break;
+
+ case Token::BIT_XOR:
+ BAILOUT("BinaryOperation BIT_XOR");
+ case Token::BIT_AND:
+ BAILOUT("BinaryOperation BIT_AND");
+ case Token::SHL:
+ BAILOUT("BinaryOperation SHL");
+ case Token::SAR:
+ BAILOUT("BinaryOperation SAR");
+ case Token::SHR:
+ BAILOUT("BinaryOperation SHR");
+ case Token::ADD:
+ BAILOUT("BinaryOperation ADD");
+ case Token::SUB:
+ BAILOUT("BinaryOperation SUB");
+ case Token::MUL:
+ BAILOUT("BinaryOperation MUL");
+ case Token::DIV:
+ BAILOUT("BinaryOperation DIV");
+ case Token::MOD:
+ BAILOUT("BinaryOperation MOD");
+ default:
+ UNREACHABLE();
+ }
+}
+
+
+void FastCodeGenSyntaxChecker::VisitCompareOperation(CompareOperation* expr) {
+ BAILOUT("CompareOperation");
+}
+
+
+void FastCodeGenSyntaxChecker::VisitThisFunction(ThisFunction* expr) {
+ BAILOUT("ThisFunction");
+}
+
+#undef BAILOUT
+#undef CHECK_BAILOUT
+
+
+#define __ ACCESS_MASM(masm())
+
+Handle<Code> FastCodeGenerator::MakeCode(CompilationInfo* info) {
+ // Label the AST before calling MakeCodePrologue, so AST node numbers are
+ // printed with the AST.
+ AstLabeler labeler;
+ labeler.Label(info);
+
+ LivenessAnalyzer analyzer;
+ analyzer.Analyze(info->function());
+
+ CodeGenerator::MakeCodePrologue(info);
+
+ const int kInitialBufferSize = 4 * KB;
+ MacroAssembler masm(NULL, kInitialBufferSize);
+
+ // Generate the fast-path code.
+ FastCodeGenerator fast_cgen(&masm);
+ fast_cgen.Generate(info);
+ if (fast_cgen.HasStackOverflow()) {
+ ASSERT(!Top::has_pending_exception());
+ return Handle<Code>::null();
+ }
+
+ // Generate the full code for the function in bailout mode, using the same
+ // macro assembler.
+ CodeGenerator cgen(&masm);
+ CodeGeneratorScope scope(&cgen);
+ info->set_mode(CompilationInfo::SECONDARY);
+ cgen.Generate(info);
+ if (cgen.HasStackOverflow()) {
+ ASSERT(!Top::has_pending_exception());
+ return Handle<Code>::null();
+ }
+
+ Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, NOT_IN_LOOP);
+ return CodeGenerator::MakeCodeEpilogue(&masm, flags, info);
+}
+
+
+Register FastCodeGenerator::accumulator0() { return eax; }
+Register FastCodeGenerator::accumulator1() { return edx; }
+Register FastCodeGenerator::scratch0() { return ecx; }
+Register FastCodeGenerator::scratch1() { return edi; }
+Register FastCodeGenerator::receiver_reg() { return ebx; }
+Register FastCodeGenerator::context_reg() { return esi; }
+
+
+void FastCodeGenerator::EmitLoadReceiver() {
+ // Offset 2 is due to return address and saved frame pointer.
+ int index = 2 + function()->scope()->num_parameters();
+ __ mov(receiver_reg(), Operand(ebp, index * kPointerSize));
}
void FastCodeGenerator::EmitGlobalVariableLoad(Handle<Object> cell) {
+ ASSERT(!destination().is(no_reg));
ASSERT(cell->IsJSGlobalPropertyCell());
- __ mov(eax, Immediate(cell));
- __ mov(eax, FieldOperand(eax, JSGlobalPropertyCell::kValueOffset));
+
+ __ mov(destination(), Immediate(cell));
+ __ mov(destination(),
+ FieldOperand(destination(), JSGlobalPropertyCell::kValueOffset));
if (FLAG_debug_code) {
- __ cmp(eax, Factory::the_hole_value());
+ __ cmp(destination(), Factory::the_hole_value());
__ Check(not_equal, "DontDelete cells can't contain the hole");
}
+
+ // The loaded value is not known to be a smi.
+ clear_as_smi(destination());
}
@@ -92,19 +510,111 @@ void FastCodeGenerator::EmitThisPropertyStore(Handle<String> name) {
int index = lookup.GetFieldIndex() - map->inobject_properties();
int offset = index * kPointerSize;
- // Negative offsets are inobject properties.
+ // We will emit the write barrier unless the stored value is statically
+ // known to be a smi.
+ bool needs_write_barrier = !is_smi(accumulator0());
+
+ // Perform the store. Negative offsets are inobject properties.
if (offset < 0) {
offset += map->instance_size();
- __ mov(ecx, edx); // Copy receiver for write barrier.
+ __ mov(FieldOperand(receiver_reg(), offset), accumulator0());
+ if (needs_write_barrier) {
+ // Preserve receiver from write barrier.
+ __ mov(scratch0(), receiver_reg());
+ }
} else {
offset += FixedArray::kHeaderSize;
- __ mov(ecx, FieldOperand(edx, JSObject::kPropertiesOffset));
+ __ mov(scratch0(),
+ FieldOperand(receiver_reg(), JSObject::kPropertiesOffset));
+ __ mov(FieldOperand(scratch0(), offset), accumulator0());
}
- // Perform the store.
- __ mov(FieldOperand(ecx, offset), eax);
- // Preserve value from write barrier in case it's needed.
- __ mov(ebx, eax);
- __ RecordWrite(ecx, offset, ebx, edi);
+
+ if (needs_write_barrier) {
+ if (destination().is(no_reg)) {
+ // After RecordWrite accumulator0 is only accidently a smi, but it is
+ // already marked as not known to be one.
+ __ RecordWrite(scratch0(), offset, accumulator0(), scratch1());
+ } else {
+ // Copy the value to the other accumulator to preserve a copy from the
+ // write barrier. One of the accumulators is available as a scratch
+ // register. Neither is a smi.
+ __ mov(accumulator1(), accumulator0());
+ clear_as_smi(accumulator1());
+ Register value_scratch = other_accumulator(destination());
+ __ RecordWrite(scratch0(), offset, value_scratch, scratch1());
+ }
+ } else if (destination().is(accumulator1())) {
+ __ mov(accumulator1(), accumulator0());
+ // Is a smi because we do not need the write barrier.
+ set_as_smi(accumulator1());
+ }
+}
+
+
+void FastCodeGenerator::EmitThisPropertyLoad(Handle<String> name) {
+ ASSERT(!destination().is(no_reg));
+ LookupResult lookup;
+ info()->receiver()->Lookup(*name, &lookup);
+
+ ASSERT(lookup.holder() == *info()->receiver());
+ ASSERT(lookup.type() == FIELD);
+ Handle<Map> map(Handle<HeapObject>::cast(info()->receiver())->map());
+ int index = lookup.GetFieldIndex() - map->inobject_properties();
+ int offset = index * kPointerSize;
+
+ // Perform the load. Negative offsets are inobject properties.
+ if (offset < 0) {
+ offset += map->instance_size();
+ __ mov(destination(), FieldOperand(receiver_reg(), offset));
+ } else {
+ offset += FixedArray::kHeaderSize;
+ __ mov(scratch0(),
+ FieldOperand(receiver_reg(), JSObject::kPropertiesOffset));
+ __ mov(destination(), FieldOperand(scratch0(), offset));
+ }
+
+ // The loaded value is not known to be a smi.
+ clear_as_smi(destination());
+}
+
+
+void FastCodeGenerator::EmitBitOr() {
+ if (is_smi(accumulator0()) && is_smi(accumulator1())) {
+ // If both operands are known to be a smi then there is no need to check
+ // the operands or result. There is no need to perform the operation in
+ // an effect context.
+ if (!destination().is(no_reg)) {
+ // Leave the result in the destination register. Bitwise or is
+ // commutative.
+ __ or_(destination(), Operand(other_accumulator(destination())));
+ }
+ } else {
+ // Left is in accumulator1, right in accumulator0.
+ Label* bailout = NULL;
+ if (destination().is(accumulator0())) {
+ __ mov(scratch0(), accumulator0());
+ __ or_(destination(), Operand(accumulator1())); // Or is commutative.
+ __ test(destination(), Immediate(kSmiTagMask));
+ bailout = info()->AddBailout(accumulator1(), scratch0()); // Left, right.
+ } else if (destination().is(accumulator1())) {
+ __ mov(scratch0(), accumulator1());
+ __ or_(destination(), Operand(accumulator0()));
+ __ test(destination(), Immediate(kSmiTagMask));
+ bailout = info()->AddBailout(scratch0(), accumulator0());
+ } else {
+ ASSERT(destination().is(no_reg));
+ __ mov(scratch0(), accumulator1());
+ __ or_(scratch0(), Operand(accumulator0()));
+ __ test(scratch0(), Immediate(kSmiTagMask));
+ bailout = info()->AddBailout(accumulator1(), accumulator0());
+ }
+ __ j(not_zero, bailout, not_taken);
+ }
+
+ // If we didn't bailout, the result (in fact, both inputs too) is known to
+ // be a smi.
+ set_as_smi(accumulator0());
+ set_as_smi(accumulator1());
}
@@ -121,27 +631,323 @@ void FastCodeGenerator::Generate(CompilationInfo* compilation_info) {
// Note that we keep a live register reference to esi (context) at this
// point.
- // Receiver (this) is allocated to edx if there are this properties.
- if (info()->has_this_properties()) EmitReceiverMapCheck();
+ Label* bailout_to_beginning = info()->AddBailout();
+ // Receiver (this) is allocated to a fixed register.
+ if (info()->has_this_properties()) {
+ Comment cmnt(masm(), ";; MapCheck(this)");
+ if (FLAG_print_ir) {
+ PrintF("#: MapCheck(this)\n");
+ }
+ ASSERT(info()->has_receiver() && info()->receiver()->IsHeapObject());
+ Handle<HeapObject> object = Handle<HeapObject>::cast(info()->receiver());
+ Handle<Map> map(object->map());
+ EmitLoadReceiver();
+ __ CheckMap(receiver_reg(), map, bailout_to_beginning, false);
+ }
- // If there is a global variable access check if the global object
- // is the same as at lazy-compilation time.
- if (info()->has_globals()) EmitGlobalMapCheck();
+ // If there is a global variable access check if the global object is the
+ // same as at lazy-compilation time.
+ if (info()->has_globals()) {
+ Comment cmnt(masm(), ";; MapCheck(GLOBAL)");
+ if (FLAG_print_ir) {
+ PrintF("#: MapCheck(GLOBAL)\n");
+ }
+ ASSERT(info()->has_global_object());
+ Handle<Map> map(info()->global_object()->map());
+ __ mov(scratch0(), CodeGenerator::GlobalObject());
+ __ CheckMap(scratch0(), map, bailout_to_beginning, true);
+ }
VisitStatements(function()->body());
Comment return_cmnt(masm(), ";; Return(<undefined>)");
+ if (FLAG_print_ir) {
+ PrintF("#: Return(<undefined>)\n");
+ }
__ mov(eax, Factory::undefined_value());
-
- Comment epilogue_cmnt(masm(), ";; Epilogue");
__ mov(esp, ebp);
__ pop(ebp);
__ ret((scope()->num_parameters() + 1) * kPointerSize);
+}
+
+
+void FastCodeGenerator::VisitDeclaration(Declaration* decl) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitBlock(Block* stmt) {
+ VisitStatements(stmt->statements());
+}
+
+
+void FastCodeGenerator::VisitExpressionStatement(ExpressionStatement* stmt) {
+ Visit(stmt->expression());
+}
+
+
+void FastCodeGenerator::VisitEmptyStatement(EmptyStatement* stmt) {
+ // Nothing to do.
+}
+
+
+void FastCodeGenerator::VisitIfStatement(IfStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitContinueStatement(ContinueStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitBreakStatement(BreakStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitWithEnterStatement(WithEnterStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitWithExitStatement(WithExitStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitSwitchStatement(SwitchStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitWhileStatement(WhileStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitForStatement(ForStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitForInStatement(ForInStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
+ UNREACHABLE();
+}
+
- __ bind(&bailout_);
+void FastCodeGenerator::VisitTryFinallyStatement(TryFinallyStatement* stmt) {
+ UNREACHABLE();
}
+void FastCodeGenerator::VisitDebuggerStatement(DebuggerStatement* stmt) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitFunctionLiteral(FunctionLiteral* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitFunctionBoilerplateLiteral(
+ FunctionBoilerplateLiteral* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitConditional(Conditional* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitSlot(Slot* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitVariableProxy(VariableProxy* expr) {
+ ASSERT(expr->var()->is_global() && !expr->var()->is_this());
+ // Check if we can compile a global variable load directly from the cell.
+ ASSERT(info()->has_global_object());
+ LookupResult lookup;
+ info()->global_object()->Lookup(*expr->name(), &lookup);
+ // We only support normal (non-accessor/interceptor) DontDelete properties
+ // for now.
+ ASSERT(lookup.IsProperty());
+ ASSERT_EQ(NORMAL, lookup.type());
+ ASSERT(lookup.IsDontDelete());
+ Handle<Object> cell(info()->global_object()->GetPropertyCell(&lookup));
+
+ // Global variable lookups do not have side effects, so we do not need to
+ // emit code if we are in an effect context.
+ if (!destination().is(no_reg)) {
+ Comment cmnt(masm(), ";; Global");
+ if (FLAG_print_ir) {
+ SmartPointer<char> name = expr->name()->ToCString();
+ PrintF("%d: t%d = Global(%s) // last_use = %d\n", expr->num(),
+ expr->num(), *name, expr->var_def()->last_use()->num());
+ }
+ EmitGlobalVariableLoad(cell);
+ }
+}
+
+
+void FastCodeGenerator::VisitLiteral(Literal* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitCatchExtensionObject(CatchExtensionObject* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitAssignment(Assignment* expr) {
+ // Known to be a simple this property assignment. Effectively a unary
+ // operation.
+ { Register my_destination = destination();
+ set_destination(accumulator0());
+ Visit(expr->value());
+ set_destination(my_destination);
+ }
+
+ Property* prop = expr->target()->AsProperty();
+ ASSERT_NOT_NULL(prop);
+ ASSERT_NOT_NULL(prop->obj()->AsVariableProxy());
+ ASSERT(prop->obj()->AsVariableProxy()->var()->is_this());
+ ASSERT(prop->key()->IsPropertyName());
+ Handle<String> name =
+ Handle<String>::cast(prop->key()->AsLiteral()->handle());
+
+ Comment cmnt(masm(), ";; Store to this");
+ if (FLAG_print_ir) {
+ SmartPointer<char> name_string = name->ToCString();
+ PrintF("%d: ", expr->num());
+ if (!destination().is(no_reg)) PrintF("t%d = ", expr->num());
+ PrintF("Store(this, \"%s\", t%d) // last_use(this) = %d\n", *name_string,
+ expr->value()->num(),
+ expr->var_def()->last_use()->num());
+ }
+
+ EmitThisPropertyStore(name);
+}
+
+
+void FastCodeGenerator::VisitThrow(Throw* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitProperty(Property* expr) {
+ ASSERT_NOT_NULL(expr->obj()->AsVariableProxy());
+ ASSERT(expr->obj()->AsVariableProxy()->var()->is_this());
+ ASSERT(expr->key()->IsPropertyName());
+ if (!destination().is(no_reg)) {
+ Handle<String> name =
+ Handle<String>::cast(expr->key()->AsLiteral()->handle());
+
+ Comment cmnt(masm(), ";; Load from this");
+ if (FLAG_print_ir) {
+ SmartPointer<char> name_string = name->ToCString();
+ PrintF("%d: t%d = Load(this, \"%s\") // last_use(this) = %d\n",
+ expr->num(), expr->num(), *name_string,
+ expr->var_def()->last_use()->num());
+ }
+ EmitThisPropertyLoad(name);
+ }
+}
+
+
+void FastCodeGenerator::VisitCall(Call* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitCallNew(CallNew* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitCountOperation(CountOperation* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitBinaryOperation(BinaryOperation* expr) {
+ // We support limited binary operations: bitwise OR only allowed to be
+ // nested on the left.
+ ASSERT(expr->op() == Token::BIT_OR);
+ ASSERT(expr->right()->IsLeaf());
+
+ { Register my_destination = destination();
+ set_destination(accumulator1());
+ Visit(expr->left());
+ set_destination(accumulator0());
+ Visit(expr->right());
+ set_destination(my_destination);
+ }
+
+ Comment cmnt(masm(), ";; BIT_OR");
+ if (FLAG_print_ir) {
+ PrintF("%d: ", expr->num());
+ if (!destination().is(no_reg)) PrintF("t%d = ", expr->num());
+ PrintF("BIT_OR(t%d, t%d)\n", expr->left()->num(), expr->right()->num());
+ }
+ EmitBitOr();
+}
+
+
+void FastCodeGenerator::VisitCompareOperation(CompareOperation* expr) {
+ UNREACHABLE();
+}
+
+
+void FastCodeGenerator::VisitThisFunction(ThisFunction* expr) {
+ UNREACHABLE();
+}
+
#undef __
diff --git a/src/ia32/fast-codegen-ia32.h b/src/ia32/fast-codegen-ia32.h
new file mode 100644
index 00000000..e0851afe
--- /dev/null
+++ b/src/ia32/fast-codegen-ia32.h
@@ -0,0 +1,155 @@
+// Copyright 2010 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.
+
+#ifndef V8_FAST_CODEGEN_IA32_H_
+#define V8_FAST_CODEGEN_IA32_H_
+
+#include "v8.h"
+
+#include "ast.h"
+#include "compiler.h"
+#include "list.h"
+
+namespace v8 {
+namespace internal {
+
+class FastCodeGenSyntaxChecker: public AstVisitor {
+ public:
+ explicit FastCodeGenSyntaxChecker()
+ : info_(NULL), has_supported_syntax_(true) {
+ }
+
+ void Check(CompilationInfo* info);
+
+ CompilationInfo* info() { return info_; }
+ bool has_supported_syntax() { return has_supported_syntax_; }
+
+ private:
+ void VisitDeclarations(ZoneList<Declaration*>* decls);
+ void VisitStatements(ZoneList<Statement*>* stmts);
+
+ // AST node visit functions.
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ CompilationInfo* info_;
+ bool has_supported_syntax_;
+
+ DISALLOW_COPY_AND_ASSIGN(FastCodeGenSyntaxChecker);
+};
+
+
+class FastCodeGenerator: public AstVisitor {
+ public:
+ explicit FastCodeGenerator(MacroAssembler* masm)
+ : masm_(masm), info_(NULL), destination_(no_reg), smi_bits_(0) {
+ }
+
+ static Handle<Code> MakeCode(CompilationInfo* info);
+
+ void Generate(CompilationInfo* compilation_info);
+
+ private:
+ MacroAssembler* masm() { return masm_; }
+ CompilationInfo* info() { return info_; }
+
+ Register destination() { return destination_; }
+ void set_destination(Register reg) { destination_ = reg; }
+
+ FunctionLiteral* function() { return info_->function(); }
+ Scope* scope() { return info_->scope(); }
+
+ // Platform-specific fixed registers, all guaranteed distinct.
+ Register accumulator0();
+ Register accumulator1();
+ Register scratch0();
+ Register scratch1();
+ Register receiver_reg();
+ Register context_reg();
+
+ Register other_accumulator(Register reg) {
+ ASSERT(reg.is(accumulator0()) || reg.is(accumulator1()));
+ return (reg.is(accumulator0())) ? accumulator1() : accumulator0();
+ }
+
+ // Flags are true if the respective register is statically known to hold a
+ // smi. We do not track every register, only the accumulator registers.
+ bool is_smi(Register reg) {
+ ASSERT(!reg.is(no_reg));
+ return (smi_bits_ & reg.bit()) != 0;
+ }
+ void set_as_smi(Register reg) {
+ ASSERT(!reg.is(no_reg));
+ smi_bits_ = smi_bits_ | reg.bit();
+ }
+ void clear_as_smi(Register reg) {
+ ASSERT(!reg.is(no_reg));
+ smi_bits_ = smi_bits_ & ~reg.bit();
+ }
+
+ // AST node visit functions.
+#define DECLARE_VISIT(type) virtual void Visit##type(type* node);
+ AST_NODE_LIST(DECLARE_VISIT)
+#undef DECLARE_VISIT
+
+ // Emit code to load the receiver from the stack into receiver_reg.
+ void EmitLoadReceiver();
+
+ // Emit code to load a global variable directly from a global property
+ // cell into the destination register.
+ void EmitGlobalVariableLoad(Handle<Object> cell);
+
+ // Emit a store to an own property of this. The stored value is expected
+ // in accumulator0 and the receiver in receiver_reg. The receiver
+ // register is preserved and the result (the stored value) is left in the
+ // destination register.
+ void EmitThisPropertyStore(Handle<String> name);
+
+ // Emit a load from an own property of this. The receiver is expected in
+ // receiver_reg. The receiver register is preserved and the result is
+ // left in the destination register.
+ void EmitThisPropertyLoad(Handle<String> name);
+
+ // Emit a bitwise or operation. The left operand is in accumulator1 and
+ // the right is in accumulator0. The result should be left in the
+ // destination register.
+ void EmitBitOr();
+
+ MacroAssembler* masm_;
+ CompilationInfo* info_;
+
+ Register destination_;
+ uint32_t smi_bits_;
+
+ DISALLOW_COPY_AND_ASSIGN(FastCodeGenerator);
+};
+
+
+} } // namespace v8::internal
+
+#endif // V8_FAST_CODEGEN_IA32_H_
diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc
index 3163b190..2394bed6 100644
--- a/src/ia32/full-codegen-ia32.cc
+++ b/src/ia32/full-codegen-ia32.cc
@@ -808,7 +808,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var,
Comment cmnt(masm_, "Global variable");
// Use inline caching. Variable name is passed in ecx and the global
// object on the stack.
- __ push(CodeGenerator::GlobalObject());
+ __ mov(eax, CodeGenerator::GlobalObject());
__ mov(ecx, var->name());
Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET_CONTEXT);
@@ -817,7 +817,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var,
// Remember that the assembler may choose to do peephole optimization
// (eg, push/pop elimination).
__ nop();
- DropAndApply(1, context, eax);
+ Apply(context, eax);
} else if (slot != NULL && slot->type() == Slot::LOOKUP) {
Comment cmnt(masm_, "Lookup slot");
@@ -845,7 +845,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var,
// Load the object.
MemOperand object_loc = EmitSlotSearch(object_slot, eax);
- __ push(object_loc);
+ __ mov(edx, object_loc);
// Assert that the key is a smi.
Literal* key_literal = property->key()->AsLiteral();
@@ -853,7 +853,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var,
ASSERT(key_literal->handle()->IsSmi());
// Load the key.
- __ push(Immediate(key_literal->handle()));
+ __ mov(eax, Immediate(key_literal->handle()));
// Do a keyed property load.
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
@@ -862,7 +862,7 @@ void FullCodeGenerator::EmitVariableLoad(Variable* var,
// call. It is treated specially by the LoadIC code.
__ nop();
// Drop key and object left on the stack by IC.
- DropAndApply(2, context, eax);
+ Apply(context, eax);
}
}
@@ -1013,6 +1013,99 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
}
+void FullCodeGenerator::VisitAssignment(Assignment* expr) {
+ Comment cmnt(masm_, "[ Assignment");
+ ASSERT(expr->op() != Token::INIT_CONST);
+ // Left-hand side can only be a property, a global or a (parameter or local)
+ // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* prop = expr->target()->AsProperty();
+ if (prop != NULL) {
+ assign_type =
+ (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
+ }
+
+ // Evaluate LHS expression.
+ switch (assign_type) {
+ case VARIABLE:
+ // Nothing to do here.
+ break;
+ case NAMED_PROPERTY:
+ if (expr->is_compound()) {
+ // We need the receiver both on the stack and in the accumulator.
+ VisitForValue(prop->obj(), kAccumulator);
+ __ push(result_register());
+ } else {
+ VisitForValue(prop->obj(), kStack);
+ }
+ break;
+ case KEYED_PROPERTY:
+ if (expr->is_compound()) {
+ VisitForValue(prop->obj(), kStack);
+ VisitForValue(prop->key(), kAccumulator);
+ __ mov(edx, Operand(esp, 0));
+ __ push(eax);
+ } else {
+ VisitForValue(prop->obj(), kStack);
+ VisitForValue(prop->key(), kStack);
+ }
+ break;
+ }
+
+ // If we have a compound assignment: Get value of LHS expression and
+ // store in on top of the stack.
+ if (expr->is_compound()) {
+ Location saved_location = location_;
+ location_ = kStack;
+ switch (assign_type) {
+ case VARIABLE:
+ EmitVariableLoad(expr->target()->AsVariableProxy()->var(),
+ Expression::kValue);
+ break;
+ case NAMED_PROPERTY:
+ EmitNamedPropertyLoad(prop);
+ __ push(result_register());
+ break;
+ case KEYED_PROPERTY:
+ EmitKeyedPropertyLoad(prop);
+ __ push(result_register());
+ break;
+ }
+ location_ = saved_location;
+ }
+
+ // Evaluate RHS expression.
+ Expression* rhs = expr->value();
+ VisitForValue(rhs, kAccumulator);
+
+ // If we have a compound assignment: Apply operator.
+ if (expr->is_compound()) {
+ Location saved_location = location_;
+ location_ = kAccumulator;
+ EmitBinaryOp(expr->binary_op(), Expression::kValue);
+ location_ = saved_location;
+ }
+
+ // Record source position before possible IC call.
+ SetSourcePosition(expr->position());
+
+ // Store the value.
+ switch (assign_type) {
+ case VARIABLE:
+ EmitVariableAssignment(expr->target()->AsVariableProxy()->var(),
+ context_);
+ break;
+ case NAMED_PROPERTY:
+ EmitNamedPropertyAssignment(expr);
+ break;
+ case KEYED_PROPERTY:
+ EmitKeyedPropertyAssignment(expr);
+ break;
+ }
+}
+
+
void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
@@ -1183,18 +1276,16 @@ void FullCodeGenerator::VisitProperty(Property* expr) {
Comment cmnt(masm_, "[ Property");
Expression* key = expr->key();
- // Evaluate the receiver.
- VisitForValue(expr->obj(), kStack);
-
if (key->IsPropertyName()) {
+ VisitForValue(expr->obj(), kAccumulator);
EmitNamedPropertyLoad(expr);
- // Drop receiver left on the stack by IC.
- DropAndApply(1, context_, eax);
+ Apply(context_, eax);
} else {
- VisitForValue(expr->key(), kStack);
+ VisitForValue(expr->obj(), kStack);
+ VisitForValue(expr->key(), kAccumulator);
+ __ pop(edx);
EmitKeyedPropertyLoad(expr);
- // Drop key and receiver left on the stack by IC.
- DropAndApply(2, context_, eax);
+ Apply(context_, eax);
}
}
@@ -1265,25 +1356,31 @@ void FullCodeGenerator::VisitCall(Call* expr) {
// Call to a keyed property, use keyed load IC followed by function
// call.
VisitForValue(prop->obj(), kStack);
- VisitForValue(prop->key(), kStack);
+ VisitForValue(prop->key(), kAccumulator);
// Record source code position for IC call.
SetSourcePosition(prop->position());
+ if (prop->is_synthetic()) {
+ __ pop(edx); // We do not need to keep the receiver.
+ } else {
+ __ mov(edx, Operand(esp, 0)); // Keep receiver, to call function on.
+ }
+
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
__ call(ic, RelocInfo::CODE_TARGET);
// By emitting a nop we make sure that we do not have a "test eax,..."
// instruction after the call it is treated specially by the LoadIC code.
__ nop();
- // Drop key left on the stack by IC.
- __ Drop(1);
- // Pop receiver.
- __ pop(ebx);
- // Push result (function).
- __ push(eax);
- // Push receiver object on stack.
if (prop->is_synthetic()) {
+ // Push result (function).
+ __ push(eax);
+ // Push Global receiver.
__ mov(ecx, CodeGenerator::GlobalObject());
__ push(FieldOperand(ecx, GlobalObject::kGlobalReceiverOffset));
} else {
+ // Pop receiver.
+ __ pop(ebx);
+ // Push result (function).
+ __ push(eax);
__ push(ebx);
}
EmitCallWithStub(expr);
@@ -1455,13 +1552,13 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
!proxy->var()->is_this() &&
proxy->var()->is_global()) {
Comment cmnt(masm_, "Global variable");
- __ push(CodeGenerator::GlobalObject());
+ __ mov(eax, CodeGenerator::GlobalObject());
__ mov(ecx, Immediate(proxy->name()));
Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
// Use a regular load, not a contextual load, to avoid a reference
// error.
__ call(ic, RelocInfo::CODE_TARGET);
- __ mov(Operand(esp, 0), eax);
+ __ push(eax);
} else if (proxy != NULL &&
proxy->var()->slot() != NULL &&
proxy->var()->slot()->type() == Slot::LOOKUP) {
@@ -1565,11 +1662,16 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) {
if (expr->is_postfix() && context_ != Expression::kEffect) {
__ push(Immediate(Smi::FromInt(0)));
}
- VisitForValue(prop->obj(), kStack);
if (assign_type == NAMED_PROPERTY) {
+ // Put the object both on the stack and in the accumulator.
+ VisitForValue(prop->obj(), kAccumulator);
+ __ push(eax);
EmitNamedPropertyLoad(prop);
} else {
- VisitForValue(prop->key(), kStack);
+ VisitForValue(prop->obj(), kStack);
+ VisitForValue(prop->key(), kAccumulator);
+ __ mov(edx, Operand(esp, 0));
+ __ push(eax);
EmitKeyedPropertyLoad(prop);
}
}
diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc
index d1ae28b1..0d79c54d 100644
--- a/src/ia32/ic-ia32.cc
+++ b/src/ia32/ic-ia32.cc
@@ -50,28 +50,29 @@ namespace internal {
// or if name is not a symbol, and will jump to the miss_label in that case.
static void GenerateDictionaryLoad(MacroAssembler* masm,
Label* miss_label,
+ Register receiver,
+ Register name,
Register r0,
Register r1,
Register r2,
- Register name,
DictionaryCheck check_dictionary) {
// Register use:
//
+ // name - holds the name of the property and is unchanged.
+ // receiver - holds the receiver and is unchanged.
+ // Scratch registers:
// r0 - used to hold the property dictionary.
//
- // r1 - initially the receiver
- // - used for the index into the property dictionary
+ // r1 - used for the index into the property dictionary
// - holds the result on exit.
//
// r2 - used to hold the capacity of the property dictionary.
- //
- // name - holds the name of the property and is unchanged.
Label done;
// Check for the absence of an interceptor.
// Load the map into r0.
- __ mov(r0, FieldOperand(r1, JSObject::kMapOffset));
+ __ mov(r0, FieldOperand(receiver, JSObject::kMapOffset));
// Test the has_named_interceptor bit in the map.
__ test(FieldOperand(r0, Map::kInstanceAttributesOffset),
Immediate(1 << (Map::kHasNamedInterceptor + (3 * 8))));
@@ -91,7 +92,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
__ j(equal, miss_label, not_taken);
// Load properties array.
- __ mov(r0, FieldOperand(r1, JSObject::kPropertiesOffset));
+ __ mov(r0, FieldOperand(receiver, JSObject::kPropertiesOffset));
// Check that the properties array is a dictionary.
if (check_dictionary == CHECK_DICTIONARY) {
@@ -159,14 +160,12 @@ const int LoadIC::kOffsetToLoadInstruction = 13;
void LoadIC::GenerateArrayLength(MacroAssembler* masm) {
// ----------- S t a t e -------------
+ // -- eax : receiver
// -- ecx : name
// -- esp[0] : return address
- // -- esp[4] : receiver
// -----------------------------------
Label miss;
- __ mov(eax, Operand(esp, kPointerSize));
-
StubCompiler::GenerateLoadArrayLength(masm, eax, edx, &miss);
__ bind(&miss);
StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
@@ -175,15 +174,13 @@ void LoadIC::GenerateArrayLength(MacroAssembler* masm) {
void LoadIC::GenerateStringLength(MacroAssembler* masm) {
// ----------- S t a t e -------------
+ // -- eax : receiver
// -- ecx : name
// -- esp[0] : return address
- // -- esp[4] : receiver
// -----------------------------------
Label miss;
- __ mov(eax, Operand(esp, kPointerSize));
-
- StubCompiler::GenerateLoadStringLength(masm, eax, edx, &miss);
+ StubCompiler::GenerateLoadStringLength(masm, eax, edx, ebx, &miss);
__ bind(&miss);
StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
}
@@ -191,14 +188,12 @@ void LoadIC::GenerateStringLength(MacroAssembler* masm) {
void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) {
// ----------- S t a t e -------------
+ // -- eax : receiver
// -- ecx : name
// -- esp[0] : return address
- // -- esp[4] : receiver
// -----------------------------------
Label miss;
- __ mov(eax, Operand(esp, kPointerSize));
-
StubCompiler::GenerateLoadFunctionPrototype(masm, eax, edx, ebx, &miss);
__ bind(&miss);
StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
@@ -207,26 +202,22 @@ void LoadIC::GenerateFunctionPrototype(MacroAssembler* masm) {
void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
// ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : name
- // -- esp[8] : receiver
// -----------------------------------
Label slow, check_string, index_int, index_string;
Label check_pixel_array, probe_dictionary;
- // Load name and receiver.
- __ mov(eax, Operand(esp, kPointerSize));
- __ mov(ecx, Operand(esp, 2 * kPointerSize));
-
// Check that the object isn't a smi.
- __ test(ecx, Immediate(kSmiTagMask));
+ __ test(edx, Immediate(kSmiTagMask));
__ j(zero, &slow, not_taken);
// Get the map of the receiver.
- __ mov(edx, FieldOperand(ecx, HeapObject::kMapOffset));
+ __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
// Check bit field.
- __ movzx_b(ebx, FieldOperand(edx, Map::kBitFieldOffset));
+ __ movzx_b(ebx, FieldOperand(ecx, Map::kBitFieldOffset));
__ test(ebx, Immediate(kSlowCaseBitFieldMask));
__ j(not_zero, &slow, not_taken);
// Check that the object is some kind of JS object EXCEPT JS Value type.
@@ -234,56 +225,58 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
// we enter the runtime system to make sure that indexing
// into string objects work as intended.
ASSERT(JS_OBJECT_TYPE > JS_VALUE_TYPE);
- __ movzx_b(edx, FieldOperand(edx, Map::kInstanceTypeOffset));
- __ cmp(edx, JS_OBJECT_TYPE);
- __ j(less, &slow, not_taken);
+ __ CmpInstanceType(ecx, JS_OBJECT_TYPE);
+ __ j(below, &slow, not_taken);
// Check that the key is a smi.
__ test(eax, Immediate(kSmiTagMask));
__ j(not_zero, &check_string, not_taken);
- __ sar(eax, kSmiTagSize);
+ __ mov(ebx, eax);
+ __ SmiUntag(ebx);
// Get the elements array of the object.
__ bind(&index_int);
- __ mov(ecx, FieldOperand(ecx, JSObject::kElementsOffset));
+ __ mov(ecx, FieldOperand(edx, JSObject::kElementsOffset));
// Check that the object is in fast mode (not dictionary).
- __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
- Immediate(Factory::fixed_array_map()));
- __ j(not_equal, &check_pixel_array);
+ __ CheckMap(ecx, Factory::fixed_array_map(), &check_pixel_array, true);
// Check that the key (index) is within bounds.
- __ cmp(eax, FieldOperand(ecx, FixedArray::kLengthOffset));
+ __ cmp(ebx, FieldOperand(ecx, FixedArray::kLengthOffset));
__ j(above_equal, &slow);
// Fast case: Do the load.
- __ mov(eax,
- Operand(ecx, eax, times_4, FixedArray::kHeaderSize - kHeapObjectTag));
- __ cmp(Operand(eax), Immediate(Factory::the_hole_value()));
+ __ mov(ecx, FieldOperand(ecx, ebx, times_4, FixedArray::kHeaderSize));
+ __ cmp(Operand(ecx), Immediate(Factory::the_hole_value()));
// In case the loaded value is the_hole we have to consult GetProperty
// to ensure the prototype chain is searched.
__ j(equal, &slow);
+ __ mov(eax, ecx);
__ IncrementCounter(&Counters::keyed_load_generic_smi, 1);
__ ret(0);
- // Check whether the elements is a pixel array.
- // eax: untagged index
- // ecx: elements array
__ bind(&check_pixel_array);
- __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
- Immediate(Factory::pixel_array_map()));
- __ j(not_equal, &slow);
- __ cmp(eax, FieldOperand(ecx, PixelArray::kLengthOffset));
+ // Check whether the elements is a pixel array.
+ // edx: receiver
+ // ebx: untagged index
+ // eax: key
+ // ecx: elements
+ __ CheckMap(ecx, Factory::pixel_array_map(), &slow, true);
+ __ cmp(ebx, FieldOperand(ecx, PixelArray::kLengthOffset));
__ j(above_equal, &slow);
- __ mov(ecx, FieldOperand(ecx, PixelArray::kExternalPointerOffset));
- __ movzx_b(eax, Operand(ecx, eax, times_1, 0));
- __ shl(eax, kSmiTagSize);
+ __ mov(eax, FieldOperand(ecx, PixelArray::kExternalPointerOffset));
+ __ movzx_b(eax, Operand(eax, ebx, times_1, 0));
+ __ SmiTag(eax);
__ ret(0);
- // Slow case: Load name and receiver from stack and jump to runtime.
__ bind(&slow);
+ // Slow case: jump to runtime.
+ // edx: receiver
+ // eax: key
__ IncrementCounter(&Counters::keyed_load_generic_slow, 1);
- Generate(masm, ExternalReference(Runtime::kKeyedGetProperty));
+ GenerateRuntimeGetProperty(masm);
__ bind(&check_string);
// The key is not a smi.
// Is it a string?
- __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, edx);
+ // edx: receiver
+ // eax: key
+ __ CmpObjectType(eax, FIRST_NONSTRING_TYPE, ecx);
__ j(above_equal, &slow);
// Is the string an array index, with cached numeric value?
__ mov(ebx, FieldOperand(eax, String::kHashFieldOffset));
@@ -291,55 +284,58 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
__ j(not_zero, &index_string, not_taken);
// Is the string a symbol?
- __ movzx_b(ebx, FieldOperand(edx, Map::kInstanceTypeOffset));
+ __ movzx_b(ebx, FieldOperand(ecx, Map::kInstanceTypeOffset));
ASSERT(kSymbolTag != 0);
__ test(ebx, Immediate(kIsSymbolMask));
__ j(zero, &slow, not_taken);
// If the receiver is a fast-case object, check the keyed lookup
- // cache. Otherwise probe the dictionary leaving result in ecx.
- __ mov(ebx, FieldOperand(ecx, JSObject::kPropertiesOffset));
+ // cache. Otherwise probe the dictionary.
+ __ mov(ebx, FieldOperand(edx, JSObject::kPropertiesOffset));
__ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
Immediate(Factory::hash_table_map()));
__ j(equal, &probe_dictionary);
// Load the map of the receiver, compute the keyed lookup cache hash
// based on 32 bits of the map pointer and the string hash.
- __ mov(ebx, FieldOperand(ecx, HeapObject::kMapOffset));
- __ mov(edx, ebx);
- __ shr(edx, KeyedLookupCache::kMapHashShift);
- __ mov(eax, FieldOperand(eax, String::kHashFieldOffset));
- __ shr(eax, String::kHashShift);
- __ xor_(edx, Operand(eax));
- __ and_(edx, KeyedLookupCache::kCapacityMask);
+ __ mov(ebx, FieldOperand(edx, HeapObject::kMapOffset));
+ __ mov(ecx, ebx);
+ __ shr(ecx, KeyedLookupCache::kMapHashShift);
+ __ mov(edi, FieldOperand(eax, String::kHashFieldOffset));
+ __ shr(edi, String::kHashShift);
+ __ xor_(ecx, Operand(edi));
+ __ and_(ecx, KeyedLookupCache::kCapacityMask);
// Load the key (consisting of map and symbol) from the cache and
// check for match.
ExternalReference cache_keys
= ExternalReference::keyed_lookup_cache_keys();
- __ mov(edi, edx);
+ __ mov(edi, ecx);
__ shl(edi, kPointerSizeLog2 + 1);
__ cmp(ebx, Operand::StaticArray(edi, times_1, cache_keys));
__ j(not_equal, &slow);
__ add(Operand(edi), Immediate(kPointerSize));
- __ mov(edi, Operand::StaticArray(edi, times_1, cache_keys));
- __ cmp(edi, Operand(esp, kPointerSize));
+ __ cmp(eax, Operand::StaticArray(edi, times_1, cache_keys));
__ j(not_equal, &slow);
// Get field offset and check that it is an in-object property.
+ // edx : receiver
+ // ebx : receiver's map
+ // eax : key
+ // ecx : lookup cache index
ExternalReference cache_field_offsets
= ExternalReference::keyed_lookup_cache_field_offsets();
- __ mov(eax,
- Operand::StaticArray(edx, times_pointer_size, cache_field_offsets));
- __ movzx_b(edx, FieldOperand(ebx, Map::kInObjectPropertiesOffset));
- __ cmp(eax, Operand(edx));
+ __ mov(edi,
+ Operand::StaticArray(ecx, times_pointer_size, cache_field_offsets));
+ __ movzx_b(ecx, FieldOperand(ebx, Map::kInObjectPropertiesOffset));
+ __ cmp(edi, Operand(ecx));
__ j(above_equal, &slow);
// Load in-object property.
- __ sub(eax, Operand(edx));
- __ movzx_b(edx, FieldOperand(ebx, Map::kInstanceSizeOffset));
- __ add(eax, Operand(edx));
- __ mov(eax, FieldOperand(ecx, eax, times_pointer_size, 0));
+ __ sub(edi, Operand(ecx));
+ __ movzx_b(ecx, FieldOperand(ebx, Map::kInstanceSizeOffset));
+ __ add(ecx, Operand(edi));
+ __ mov(eax, FieldOperand(edx, ecx, times_pointer_size, 0));
__ ret(0);
// Do a quick inline probe of the receiver's dictionary, if it
@@ -347,10 +343,11 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
__ bind(&probe_dictionary);
GenerateDictionaryLoad(masm,
&slow,
- ebx,
- ecx,
edx,
eax,
+ ebx,
+ ecx,
+ edi,
DICTIONARY_CHECK_DONE);
__ mov(eax, Operand(ecx));
__ IncrementCounter(&Counters::keyed_load_generic_symbol, 1);
@@ -363,51 +360,47 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) <
(1 << String::kArrayIndexValueBits));
__ bind(&index_string);
- __ mov(eax, Operand(ebx));
- __ and_(eax, String::kArrayIndexHashMask);
- __ shr(eax, String::kHashShift);
+ __ and_(ebx, String::kArrayIndexHashMask);
+ __ shr(ebx, String::kHashShift);
__ jmp(&index_int);
}
void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
// ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : key
- // -- esp[8] : receiver
// -----------------------------------
Label miss, index_ok;
// Pop return address.
// Performing the load early is better in the common case.
- __ pop(eax);
+ __ pop(ebx);
- __ mov(ebx, Operand(esp, 1 * kPointerSize));
- __ test(ebx, Immediate(kSmiTagMask));
+ __ test(edx, Immediate(kSmiTagMask));
__ j(zero, &miss);
- __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset));
+ __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
__ movzx_b(ecx, FieldOperand(ecx, Map::kInstanceTypeOffset));
__ test(ecx, Immediate(kIsNotStringMask));
__ j(not_zero, &miss);
// Check if key is a smi or a heap number.
- __ mov(edx, Operand(esp, 0));
- __ test(edx, Immediate(kSmiTagMask));
+ __ test(eax, Immediate(kSmiTagMask));
__ j(zero, &index_ok);
- __ mov(ecx, FieldOperand(ebx, HeapObject::kMapOffset));
+ __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset));
__ cmp(ecx, Factory::heap_number_map());
__ j(not_equal, &miss);
__ bind(&index_ok);
- // Duplicate receiver and key since they are expected on the stack after
- // the KeyedLoadIC call.
- __ push(ebx); // receiver
- __ push(edx); // key
- __ push(eax); // return address
+ // Push receiver and key on the stack, and make a tail call.
+ __ push(edx); // receiver
+ __ push(eax); // key
+ __ push(ebx); // return address
__ InvokeBuiltin(Builtins::STRING_CHAR_AT, JUMP_FUNCTION);
__ bind(&miss);
- __ push(eax);
+ __ push(ebx);
GenerateMiss(masm);
}
@@ -415,18 +408,14 @@ void KeyedLoadIC::GenerateString(MacroAssembler* masm) {
void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm,
ExternalArrayType array_type) {
// ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : key
- // -- esp[8] : receiver
// -----------------------------------
Label slow, failed_allocation;
- // Load name and receiver.
- __ mov(eax, Operand(esp, kPointerSize));
- __ mov(ecx, Operand(esp, 2 * kPointerSize));
-
// Check that the object isn't a smi.
- __ test(ecx, Immediate(kSmiTagMask));
+ __ test(edx, Immediate(kSmiTagMask));
__ j(zero, &slow, not_taken);
// Check that the key is a smi.
@@ -434,59 +423,56 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm,
__ j(not_zero, &slow, not_taken);
// Get the map of the receiver.
- __ mov(edx, FieldOperand(ecx, HeapObject::kMapOffset));
+ __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
// Check that the receiver does not require access checks. We need
// to check this explicitly since this generic stub does not perform
// map checks.
- __ movzx_b(ebx, FieldOperand(edx, Map::kBitFieldOffset));
+ __ movzx_b(ebx, FieldOperand(ecx, Map::kBitFieldOffset));
__ test(ebx, Immediate(1 << Map::kIsAccessCheckNeeded));
__ j(not_zero, &slow, not_taken);
- // Get the instance type from the map of the receiver.
- __ movzx_b(edx, FieldOperand(edx, Map::kInstanceTypeOffset));
- // Check that the object is a JS object.
- __ cmp(edx, JS_OBJECT_TYPE);
+ __ CmpInstanceType(ecx, JS_OBJECT_TYPE);
__ j(not_equal, &slow, not_taken);
// Check that the elements array is the appropriate type of
// ExternalArray.
- // eax: index (as a smi)
- // ecx: JSObject
- __ mov(ecx, FieldOperand(ecx, JSObject::kElementsOffset));
+ __ mov(ebx, FieldOperand(edx, JSObject::kElementsOffset));
Handle<Map> map(Heap::MapForExternalArrayType(array_type));
- __ cmp(FieldOperand(ecx, HeapObject::kMapOffset),
+ __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
Immediate(map));
__ j(not_equal, &slow, not_taken);
+ // eax: key, known to be a smi.
+ // edx: receiver, known to be a JSObject.
+ // ebx: elements object, known to be an external array.
// Check that the index is in range.
- __ sar(eax, kSmiTagSize); // Untag the index.
- __ cmp(eax, FieldOperand(ecx, ExternalArray::kLengthOffset));
+ __ mov(ecx, eax);
+ __ SmiUntag(ecx); // Untag the index.
+ __ cmp(ecx, FieldOperand(ebx, ExternalArray::kLengthOffset));
// Unsigned comparison catches both negative and too-large values.
__ j(above_equal, &slow);
- // eax: untagged index
- // ecx: elements array
- __ mov(ecx, FieldOperand(ecx, ExternalArray::kExternalPointerOffset));
- // ecx: base pointer of external storage
+ __ mov(ebx, FieldOperand(ebx, ExternalArray::kExternalPointerOffset));
+ // ebx: base pointer of external storage
switch (array_type) {
case kExternalByteArray:
- __ movsx_b(eax, Operand(ecx, eax, times_1, 0));
+ __ movsx_b(ecx, Operand(ebx, ecx, times_1, 0));
break;
case kExternalUnsignedByteArray:
- __ movzx_b(eax, Operand(ecx, eax, times_1, 0));
+ __ movzx_b(ecx, Operand(ebx, ecx, times_1, 0));
break;
case kExternalShortArray:
- __ movsx_w(eax, Operand(ecx, eax, times_2, 0));
+ __ movsx_w(ecx, Operand(ebx, ecx, times_2, 0));
break;
case kExternalUnsignedShortArray:
- __ movzx_w(eax, Operand(ecx, eax, times_2, 0));
+ __ movzx_w(ecx, Operand(ebx, ecx, times_2, 0));
break;
case kExternalIntArray:
case kExternalUnsignedIntArray:
- __ mov(eax, Operand(ecx, eax, times_4, 0));
+ __ mov(ecx, Operand(ebx, ecx, times_4, 0));
break;
case kExternalFloatArray:
- __ fld_s(Operand(ecx, eax, times_4, 0));
+ __ fld_s(Operand(ebx, ecx, times_4, 0));
break;
default:
UNREACHABLE();
@@ -494,7 +480,7 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm,
}
// For integer array types:
- // eax: value
+ // ecx: value
// For floating-point array type:
// FP(0): value
@@ -505,21 +491,19 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm,
// it to a HeapNumber.
Label box_int;
if (array_type == kExternalIntArray) {
- // See Smi::IsValid for why this works.
- __ mov(ebx, eax);
- __ add(Operand(ebx), Immediate(0x40000000));
- __ cmp(ebx, 0x80000000);
- __ j(above_equal, &box_int);
+ __ cmp(ecx, 0xC0000000);
+ __ j(sign, &box_int);
} else {
ASSERT_EQ(array_type, kExternalUnsignedIntArray);
// The test is different for unsigned int values. Since we need
- // the Smi-encoded result to be treated as unsigned, we can't
+ // the value to be in the range of a positive smi, we can't
// handle either of the top two bits being set in the value.
- __ test(eax, Immediate(0xC0000000));
+ __ test(ecx, Immediate(0xC0000000));
__ j(not_zero, &box_int);
}
- __ shl(eax, kSmiTagSize);
+ __ mov(eax, ecx);
+ __ SmiTag(eax);
__ ret(0);
__ bind(&box_int);
@@ -527,34 +511,37 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm,
// Allocate a HeapNumber for the int and perform int-to-double
// conversion.
if (array_type == kExternalIntArray) {
- __ push(eax);
+ __ push(ecx);
__ fild_s(Operand(esp, 0));
- __ pop(eax);
+ __ pop(ecx);
} else {
ASSERT(array_type == kExternalUnsignedIntArray);
// Need to zero-extend the value.
// There's no fild variant for unsigned values, so zero-extend
// to a 64-bit int manually.
__ push(Immediate(0));
- __ push(eax);
+ __ push(ecx);
__ fild_d(Operand(esp, 0));
- __ pop(eax);
- __ pop(eax);
+ __ pop(ecx);
+ __ pop(ecx);
}
// FP(0): value
- __ AllocateHeapNumber(eax, ebx, ecx, &failed_allocation);
+ __ AllocateHeapNumber(ecx, ebx, edi, &failed_allocation);
// Set the value.
+ __ mov(eax, ecx);
__ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
__ ret(0);
} else if (array_type == kExternalFloatArray) {
// For the floating-point array type, we need to always allocate a
// HeapNumber.
- __ AllocateHeapNumber(eax, ebx, ecx, &failed_allocation);
+ __ AllocateHeapNumber(ecx, ebx, edi, &failed_allocation);
// Set the value.
+ __ mov(eax, ecx);
__ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset));
__ ret(0);
} else {
- __ shl(eax, kSmiTagSize);
+ __ mov(eax, ecx);
+ __ SmiTag(eax);
__ ret(0);
}
@@ -565,10 +552,51 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm,
__ fincstp();
// Fall through to slow case.
- // Slow case: Load name and receiver from stack and jump to runtime.
+ // Slow case: Load key and receiver from stack and jump to runtime.
__ bind(&slow);
__ IncrementCounter(&Counters::keyed_load_external_array_slow, 1);
- Generate(masm, ExternalReference(Runtime::kKeyedGetProperty));
+ GenerateRuntimeGetProperty(masm);
+}
+
+
+void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+ Label slow;
+
+ // Check that the receiver isn't a smi.
+ __ test(edx, Immediate(kSmiTagMask));
+ __ j(zero, &slow, not_taken);
+
+ // Check that the key is a smi.
+ __ test(eax, Immediate(kSmiTagMask));
+ __ j(not_zero, &slow, not_taken);
+
+ // Get the map of the receiver.
+ __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset));
+
+ // Check that it has indexed interceptor and access checks
+ // are not enabled for this object.
+ __ movzx_b(ecx, FieldOperand(ecx, Map::kBitFieldOffset));
+ __ and_(Operand(ecx), Immediate(kSlowCaseBitFieldMask));
+ __ cmp(Operand(ecx), Immediate(1 << Map::kHasIndexedInterceptor));
+ __ j(not_zero, &slow, not_taken);
+
+ // Everything is fine, call runtime.
+ __ pop(ecx);
+ __ push(edx); // receiver
+ __ push(eax); // key
+ __ push(ecx); // return address
+
+ // Perform tail call to the entry.
+ __ TailCallRuntime(ExternalReference(
+ IC_Utility(kKeyedLoadPropertyWithInterceptor)), 2, 1);
+
+ __ bind(&slow);
+ GenerateMiss(masm);
}
@@ -627,7 +655,7 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
// Slow case: call runtime.
__ bind(&slow);
- Generate(masm, ExternalReference(Runtime::kSetProperty));
+ GenerateRuntimeSetProperty(masm);
// Check whether the elements is a pixel array.
// eax: value
@@ -900,7 +928,7 @@ void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm,
// Slow case: call runtime.
__ bind(&slow);
- Generate(masm, ExternalReference(Runtime::kSetProperty));
+ GenerateRuntimeSetProperty(masm);
}
@@ -983,7 +1011,7 @@ static void GenerateNormalHelper(MacroAssembler* masm,
// Search dictionary - put result in register edi.
__ mov(edi, edx);
- GenerateDictionaryLoad(masm, miss, eax, edi, ebx, ecx, CHECK_DICTIONARY);
+ GenerateDictionaryLoad(masm, miss, edx, ecx, eax, edi, ebx, CHECK_DICTIONARY);
// Check that the result is not a smi.
__ test(edi, Immediate(kSmiTagMask));
@@ -1127,13 +1155,11 @@ Object* LoadIC_Miss(Arguments args);
void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
// ----------- S t a t e -------------
+ // -- eax : receiver
// -- ecx : name
// -- esp[0] : return address
- // -- esp[4] : receiver
// -----------------------------------
- __ mov(eax, Operand(esp, kPointerSize));
-
// Probe the stub cache.
Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC,
NOT_IN_LOOP,
@@ -1141,20 +1167,18 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
StubCache::GenerateProbe(masm, flags, eax, ecx, ebx, edx);
// Cache miss: Jump to runtime.
- Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss)));
+ GenerateMiss(masm);
}
void LoadIC::GenerateNormal(MacroAssembler* masm) {
// ----------- S t a t e -------------
+ // -- eax : receiver
// -- ecx : name
// -- esp[0] : return address
- // -- esp[4] : receiver
// -----------------------------------
Label miss, probe, global;
- __ mov(eax, Operand(esp, kPointerSize));
-
// Check that the receiver isn't a smi.
__ test(eax, Immediate(kSmiTagMask));
__ j(zero, &miss, not_taken);
@@ -1179,7 +1203,15 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
// Search the dictionary placing the result in eax.
__ bind(&probe);
- GenerateDictionaryLoad(masm, &miss, edx, eax, ebx, ecx, CHECK_DICTIONARY);
+ GenerateDictionaryLoad(masm,
+ &miss,
+ eax,
+ ecx,
+ edx,
+ edi,
+ ebx,
+ CHECK_DICTIONARY);
+ __ mov(eax, edi);
__ ret(0);
// Global object access: Check access rights.
@@ -1189,37 +1221,24 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
// Cache miss: Restore receiver from stack and jump to runtime.
__ bind(&miss);
- __ mov(eax, Operand(esp, 1 * kPointerSize));
- Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss)));
+ GenerateMiss(masm);
}
void LoadIC::GenerateMiss(MacroAssembler* masm) {
// ----------- S t a t e -------------
+ // -- eax : receiver
// -- ecx : name
// -- esp[0] : return address
- // -- esp[4] : receiver
// -----------------------------------
- Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss)));
-}
-
-
-void LoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) {
- // ----------- S t a t e -------------
- // -- ecx : name
- // -- esp[0] : return address
- // -- esp[4] : receiver
- // -----------------------------------
-
- __ mov(eax, Operand(esp, kPointerSize));
__ pop(ebx);
__ push(eax); // receiver
__ push(ecx); // name
__ push(ebx); // return address
// Perform tail call to the entry.
- __ TailCallRuntime(f, 2, 1);
+ __ TailCallRuntime(ExternalReference(IC_Utility(kLoadIC_Miss)), 2, 1);
}
@@ -1323,31 +1342,35 @@ Object* KeyedLoadIC_Miss(Arguments args);
void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
// ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : name
- // -- esp[8] : receiver
// -----------------------------------
- Generate(masm, ExternalReference(IC_Utility(kKeyedLoadIC_Miss)));
+ __ pop(ebx);
+ __ push(edx); // receiver
+ __ push(eax); // name
+ __ push(ebx); // return address
+
+ // Perform tail call to the entry.
+ __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedLoadIC_Miss)), 2, 1);
}
-void KeyedLoadIC::Generate(MacroAssembler* masm, const ExternalReference& f) {
+void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
// ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : name
- // -- esp[8] : receiver
// -----------------------------------
- __ mov(eax, Operand(esp, kPointerSize));
- __ mov(ecx, Operand(esp, 2 * kPointerSize));
__ pop(ebx);
- __ push(ecx); // receiver
+ __ push(edx); // receiver
__ push(eax); // name
__ push(ebx); // return address
// Perform tail call to the entry.
- __ TailCallRuntime(f, 2, 1);
+ __ TailCallRuntime(ExternalReference(Runtime::kKeyedGetProperty), 2, 1);
}
@@ -1369,26 +1392,6 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm) {
}
-void StoreIC::GenerateExtendStorage(MacroAssembler* masm) {
- // ----------- S t a t e -------------
- // -- eax : value
- // -- ecx : transition map
- // -- edx : receiver
- // -- esp[0] : return address
- // -----------------------------------
-
- __ pop(ebx);
- __ push(edx); // receiver
- __ push(ecx); // transition map
- __ push(eax); // value
- __ push(ebx); // return address
-
- // Perform tail call to the entry.
- __ TailCallRuntime(
- ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3, 1);
-}
-
-
void StoreIC::GenerateMiss(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : value
@@ -1411,7 +1414,7 @@ void StoreIC::GenerateMiss(MacroAssembler* masm) {
// Defined in ic.cc.
Object* KeyedStoreIC_Miss(Arguments args);
-void KeyedStoreIC::Generate(MacroAssembler* masm, const ExternalReference& f) {
+void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : value
// -- esp[0] : return address
@@ -1426,28 +1429,26 @@ void KeyedStoreIC::Generate(MacroAssembler* masm, const ExternalReference& f) {
__ push(ecx);
// Do tail-call to runtime routine.
- __ TailCallRuntime(f, 3, 1);
+ __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3, 1);
}
-void KeyedStoreIC::GenerateExtendStorage(MacroAssembler* masm) {
+void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- eax : value
- // -- ecx : transition map
// -- esp[0] : return address
// -- esp[4] : key
// -- esp[8] : receiver
// -----------------------------------
- __ pop(ebx);
+ __ pop(ecx);
+ __ push(Operand(esp, 1 * kPointerSize));
__ push(Operand(esp, 1 * kPointerSize));
- __ push(ecx);
__ push(eax);
- __ push(ebx);
+ __ push(ecx);
// Do tail-call to runtime routine.
- __ TailCallRuntime(
- ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3, 1);
+ __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedStoreIC_Miss)), 3, 1);
}
#undef __
diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc
index 4dd6a9bc..1f08e872 100644
--- a/src/ia32/macro-assembler-ia32.cc
+++ b/src/ia32/macro-assembler-ia32.cc
@@ -41,7 +41,6 @@ namespace internal {
MacroAssembler::MacroAssembler(void* buffer, int size)
: Assembler(buffer, size),
- unresolved_(0),
generating_stub_(false),
allow_stub_calls_(true),
code_object_(Heap::undefined_value()) {
@@ -165,7 +164,10 @@ void MacroAssembler::RecordWrite(Register object, int offset,
if (Serializer::enabled()) {
// Can't do arithmetic on external references if it might get serialized.
mov(value, Operand(object));
- and_(value, Heap::NewSpaceMask());
+ // The mask isn't really an address. We load it as an external reference in
+ // case the size of the new space is different between the snapshot maker
+ // and the running system.
+ and_(Operand(value), Immediate(ExternalReference::new_space_mask()));
cmp(Operand(value), Immediate(ExternalReference::new_space_start()));
j(equal, &done);
} else {
@@ -308,6 +310,13 @@ void MacroAssembler::CopyRegistersFromStackToMemory(Register base,
}
}
}
+
+void MacroAssembler::DebugBreak() {
+ Set(eax, Immediate(0));
+ mov(ebx, Immediate(ExternalReference(Runtime::kDebugBreak)));
+ CEntryStub ces(1);
+ call(ces.GetCode(), RelocInfo::DEBUG_BREAK);
+}
#endif
void MacroAssembler::Set(Register dst, const Immediate& x) {
@@ -377,6 +386,17 @@ void MacroAssembler::FCmp() {
}
+void MacroAssembler::AbortIfNotNumber(Register object, const char* msg) {
+ Label ok;
+ test(object, Immediate(kSmiTagMask));
+ j(zero, &ok);
+ cmp(FieldOperand(object, HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ Assert(equal, msg);
+ bind(&ok);
+}
+
+
void MacroAssembler::EnterFrame(StackFrame::Type type) {
push(ebp);
mov(ebp, Operand(esp));
@@ -409,12 +429,8 @@ void MacroAssembler::EnterExitFramePrologue(ExitFrame::Mode mode) {
// Reserve room for entry stack pointer and push the debug marker.
ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize);
- push(Immediate(0)); // saved entry sp, patched before call
- if (mode == ExitFrame::MODE_DEBUG) {
- push(Immediate(0));
- } else {
- push(Immediate(CodeObject()));
- }
+ push(Immediate(0)); // Saved entry sp, patched before call.
+ push(Immediate(CodeObject())); // Accessed from ExitFrame::code_slot.
// Save the frame pointer and the context in top.
ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address);
@@ -551,6 +567,7 @@ void MacroAssembler::PopTryHandler() {
Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
JSObject* holder, Register holder_reg,
Register scratch,
+ int save_at_depth,
Label* miss) {
// Make sure there's no overlap between scratch and the other
// registers.
@@ -558,7 +575,11 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
// Keep track of the current object in register reg.
Register reg = object_reg;
- int depth = 1;
+ int depth = 0;
+
+ if (save_at_depth == depth) {
+ mov(Operand(esp, kPointerSize), object_reg);
+ }
// Check the maps in the prototype chain.
// Traverse the prototype chain from the object and do map checks.
@@ -590,7 +611,6 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
// to it in the code. Load it from the map.
reg = holder_reg; // from now the object is in holder_reg
mov(reg, FieldOperand(scratch, Map::kPrototypeOffset));
-
} else {
// Check the map of the current object.
cmp(FieldOperand(reg, HeapObject::kMapOffset),
@@ -608,6 +628,10 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
mov(reg, Handle<JSObject>(prototype));
}
+ if (save_at_depth == depth) {
+ mov(Operand(esp, kPointerSize), reg);
+ }
+
// Go to the next object in the prototype chain.
object = prototype;
}
@@ -618,7 +642,7 @@ Register MacroAssembler::CheckMaps(JSObject* object, Register object_reg,
j(not_equal, miss, not_taken);
// Log the check depth.
- LOG(IntEvent("check-maps-depth", depth));
+ LOG(IntEvent("check-maps-depth", depth + 1));
// Perform security check for access to the global object and return
// the holder register.
@@ -1135,6 +1159,16 @@ void MacroAssembler::CallRuntime(Runtime::Function* f, int num_arguments) {
}
+void MacroAssembler::CallExternalReference(ExternalReference ref,
+ int num_arguments) {
+ mov(eax, Immediate(num_arguments));
+ mov(ebx, Immediate(ref));
+
+ CEntryStub stub(1);
+ CallStub(&stub);
+}
+
+
Object* MacroAssembler::TryCallRuntime(Runtime::Function* f,
int num_arguments) {
if (f->nargs >= 0 && f->nargs != num_arguments) {
@@ -1355,10 +1389,22 @@ void MacroAssembler::InvokeFunction(Register fun,
}
-void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) {
- bool resolved;
- Handle<Code> code = ResolveBuiltin(id, &resolved);
+void MacroAssembler::InvokeFunction(JSFunction* function,
+ const ParameterCount& actual,
+ InvokeFlag flag) {
+ ASSERT(function->is_compiled());
+ // Get the function and setup the context.
+ mov(edi, Immediate(Handle<JSFunction>(function)));
+ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+
+ // Invoke the cached code.
+ Handle<Code> code(function->code());
+ ParameterCount expected(function->shared()->formal_parameter_count());
+ InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET, flag);
+}
+
+void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) {
// Calls are not allowed in some stubs.
ASSERT(flag == JUMP_FUNCTION || allow_stub_calls());
@@ -1366,55 +1412,22 @@ void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) {
// arguments match the expected number of arguments. Fake a
// parameter count to avoid emitting code to do the check.
ParameterCount expected(0);
- InvokeCode(Handle<Code>(code), expected, expected,
- RelocInfo::CODE_TARGET, flag);
-
- const char* name = Builtins::GetName(id);
- int argc = Builtins::GetArgumentsCount(id);
-
- if (!resolved) {
- uint32_t flags =
- Bootstrapper::FixupFlagsArgumentsCount::encode(argc) |
- Bootstrapper::FixupFlagsUseCodeObject::encode(false);
- Unresolved entry = { pc_offset() - sizeof(int32_t), flags, name };
- unresolved_.Add(entry);
- }
+ GetBuiltinEntry(edx, id);
+ InvokeCode(Operand(edx), expected, expected, flag);
}
void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) {
- bool resolved;
- Handle<Code> code = ResolveBuiltin(id, &resolved);
-
- const char* name = Builtins::GetName(id);
- int argc = Builtins::GetArgumentsCount(id);
-
- mov(Operand(target), Immediate(code));
- if (!resolved) {
- uint32_t flags =
- Bootstrapper::FixupFlagsArgumentsCount::encode(argc) |
- Bootstrapper::FixupFlagsUseCodeObject::encode(true);
- Unresolved entry = { pc_offset() - sizeof(int32_t), flags, name };
- unresolved_.Add(entry);
- }
- add(Operand(target), Immediate(Code::kHeaderSize - kHeapObjectTag));
-}
-
-
-Handle<Code> MacroAssembler::ResolveBuiltin(Builtins::JavaScript id,
- bool* resolved) {
- // Move the builtin function into the temporary function slot by
- // reading it from the builtins object. NOTE: We should be able to
- // reduce this to two instructions by putting the function table in
- // the global object instead of the "builtins" object and by using a
- // real register for the function.
- mov(edx, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
- mov(edx, FieldOperand(edx, GlobalObject::kBuiltinsOffset));
+ // Load the JavaScript builtin function from the builtins object.
+ mov(edi, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ mov(edi, FieldOperand(edi, GlobalObject::kBuiltinsOffset));
int builtins_offset =
JSBuiltinsObject::kJSBuiltinsOffset + (id * kPointerSize);
- mov(edi, FieldOperand(edx, builtins_offset));
-
- return Builtins::GetCode(id, resolved);
+ mov(edi, FieldOperand(edi, builtins_offset));
+ // Load the code entry point from the function into the target register.
+ mov(target, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
+ mov(target, FieldOperand(target, SharedFunctionInfo::kCodeOffset));
+ add(Operand(target), Immediate(Code::kHeaderSize - kHeapObjectTag));
}
@@ -1559,6 +1572,20 @@ void MacroAssembler::Abort(const char* msg) {
}
+void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii(
+ Register instance_type,
+ Register scratch,
+ Label *failure) {
+ if (!scratch.is(instance_type)) {
+ mov(scratch, instance_type);
+ }
+ and_(scratch,
+ kIsNotStringMask | kStringRepresentationMask | kStringEncodingMask);
+ cmp(scratch, kStringTag | kSeqStringTag | kAsciiStringTag);
+ j(not_equal, failure);
+}
+
+
void MacroAssembler::JumpIfNotBothSequentialAsciiStrings(Register object1,
Register object2,
Register scratch1,
diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h
index 0ddbd5d3..69dc54ca 100644
--- a/src/ia32/macro-assembler-ia32.h
+++ b/src/ia32/macro-assembler-ia32.h
@@ -69,6 +69,7 @@ class MacroAssembler: public Assembler {
void CopyRegistersFromStackToMemory(Register base,
Register scratch,
RegList regs);
+ void DebugBreak();
#endif
// ---------------------------------------------------------------------------
@@ -123,6 +124,10 @@ class MacroAssembler: public Assembler {
const ParameterCount& actual,
InvokeFlag flag);
+ void InvokeFunction(JSFunction* function,
+ const ParameterCount& actual,
+ InvokeFlag flag);
+
// Invoke specified builtin JavaScript function. Adds an entry to
// the unresolved list if the name does not resolve.
void InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag);
@@ -171,6 +176,9 @@ class MacroAssembler: public Assembler {
sar(reg, kSmiTagSize);
}
+ // Abort execution if argument is not a number. Used in debug code.
+ void AbortIfNotNumber(Register object, const char* msg);
+
// ---------------------------------------------------------------------------
// Exception handling
@@ -193,9 +201,14 @@ class MacroAssembler: public Assembler {
// clobbered if it the same as the holder register. The function
// returns a register containing the holder - either object_reg or
// holder_reg.
+ // The function can optionally (when save_at_depth !=
+ // kInvalidProtoDepth) save the object at the given depth by moving
+ // it to [esp + kPointerSize].
Register CheckMaps(JSObject* object, Register object_reg,
JSObject* holder, Register holder_reg,
- Register scratch, Label* miss);
+ Register scratch,
+ int save_at_depth,
+ Label* miss);
// Generate code for checking access rights - used for security checks
// on access to global objects across environments. The holder register
@@ -347,6 +360,9 @@ class MacroAssembler: public Assembler {
// Convenience function: Same as above, but takes the fid instead.
void CallRuntime(Runtime::FunctionId id, int num_arguments);
+ // Convenience function: call an external reference.
+ void CallExternalReference(ExternalReference ref, int num_arguments);
+
// Convenience function: Same as above, but takes the fid instead.
Object* TryCallRuntime(Runtime::FunctionId id, int num_arguments);
@@ -384,13 +400,6 @@ class MacroAssembler: public Assembler {
void Move(Register target, Handle<Object> value);
- struct Unresolved {
- int pc;
- uint32_t flags; // see Bootstrapper::FixupFlags decoders/encoders.
- const char* name;
- };
- List<Unresolved>* unresolved() { return &unresolved_; }
-
Handle<Object> CodeObject() { return code_object_; }
@@ -426,6 +435,13 @@ class MacroAssembler: public Assembler {
// ---------------------------------------------------------------------------
// String utilities.
+ // Check whether the instance type represents a flat ascii string. Jump to the
+ // label if not. If the instance type can be scratched specify same register
+ // for both instance type and scratch.
+ void JumpIfInstanceTypeIsNotSequentialAscii(Register instance_type,
+ Register scratch,
+ Label *on_not_flat_ascii_string);
+
// Checks if both objects are sequential ASCII strings, and jumps to label
// if either is not.
void JumpIfNotBothSequentialAsciiStrings(Register object1,
@@ -435,7 +451,6 @@ class MacroAssembler: public Assembler {
Label *on_not_flat_ascii_strings);
private:
- List<Unresolved> unresolved_;
bool generating_stub_;
bool allow_stub_calls_;
// This handle will be patched with the code object on installation.
@@ -449,18 +464,6 @@ class MacroAssembler: public Assembler {
Label* done,
InvokeFlag flag);
- // Prepares for a call or jump to a builtin by doing two things:
- // 1. Emits code that fetches the builtin's function object from the context
- // at runtime, and puts it in the register rdi.
- // 2. Fetches the builtin's code object, and returns it in a handle, at
- // compile time, so that later code can emit instructions to jump or call
- // the builtin directly. If the code object has not yet been created, it
- // returns the builtin code object for IllegalFunction, and sets the
- // output parameter "resolved" to false. Code that uses the return value
- // should then add the address and the builtin name to the list of fixups
- // called unresolved_, which is fixed up by the bootstrapper.
- Handle<Code> ResolveBuiltin(Builtins::JavaScript id, bool* resolved);
-
// Activation support.
void EnterFrame(StackFrame::Type type);
void LeaveFrame(StackFrame::Type type);
diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc
index 7acf81c9..5729d9d7 100644
--- a/src/ia32/stub-cache-ia32.cc
+++ b/src/ia32/stub-cache-ia32.cc
@@ -152,22 +152,6 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
}
-static void PushInterceptorArguments(MacroAssembler* masm,
- Register receiver,
- Register holder,
- Register name,
- JSObject* holder_obj) {
- __ push(receiver);
- __ push(holder);
- __ push(name);
- InterceptorInfo* interceptor = holder_obj->GetNamedInterceptor();
- ASSERT(!Heap::InNewSpace(interceptor));
- __ mov(receiver, Immediate(Handle<Object>(interceptor)));
- __ push(receiver);
- __ push(FieldOperand(receiver, InterceptorInfo::kDataOffset));
-}
-
-
void StubCompiler::GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm,
int index,
Register prototype) {
@@ -226,30 +210,32 @@ static void GenerateStringCheck(MacroAssembler* masm,
void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm,
Register receiver,
- Register scratch,
+ Register scratch1,
+ Register scratch2,
Label* miss) {
- Label load_length, check_wrapper;
+ Label check_wrapper;
// Check if the object is a string leaving the instance type in the
// scratch register.
- GenerateStringCheck(masm, receiver, scratch, miss, &check_wrapper);
+ GenerateStringCheck(masm, receiver, scratch1, miss, &check_wrapper);
// Load length from the string and convert to a smi.
- __ bind(&load_length);
__ mov(eax, FieldOperand(receiver, String::kLengthOffset));
__ SmiTag(eax);
__ ret(0);
// Check if the object is a JSValue wrapper.
__ bind(&check_wrapper);
- __ cmp(scratch, JS_VALUE_TYPE);
+ __ cmp(scratch1, JS_VALUE_TYPE);
__ j(not_equal, miss, not_taken);
// Check if the wrapped value is a string and load the length
// directly if it is.
- __ mov(receiver, FieldOperand(receiver, JSValue::kValueOffset));
- GenerateStringCheck(masm, receiver, scratch, miss, miss);
- __ jmp(&load_length);
+ __ mov(scratch2, FieldOperand(receiver, JSValue::kValueOffset));
+ GenerateStringCheck(masm, scratch2, scratch1, miss, miss);
+ __ mov(eax, FieldOperand(scratch2, String::kLengthOffset));
+ __ SmiTag(eax);
+ __ ret(0);
}
@@ -285,20 +271,31 @@ void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm,
}
+static void PushInterceptorArguments(MacroAssembler* masm,
+ Register receiver,
+ Register holder,
+ Register name,
+ JSObject* holder_obj) {
+ __ push(receiver);
+ __ push(holder);
+ __ push(name);
+ InterceptorInfo* interceptor = holder_obj->GetNamedInterceptor();
+ ASSERT(!Heap::InNewSpace(interceptor));
+ __ mov(receiver, Immediate(Handle<Object>(interceptor)));
+ __ push(receiver);
+ __ push(FieldOperand(receiver, InterceptorInfo::kDataOffset));
+}
+
+
static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm,
Register receiver,
Register holder,
Register name,
JSObject* holder_obj) {
PushInterceptorArguments(masm, receiver, holder, name, holder_obj);
-
- ExternalReference ref =
- ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly));
- __ mov(eax, Immediate(5));
- __ mov(ebx, Immediate(ref));
-
- CEntryStub stub(1);
- __ CallStub(&stub);
+ __ CallExternalReference(
+ ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorOnly)),
+ 5);
}
@@ -326,7 +323,7 @@ static void CompileLoadInterceptor(Compiler* compiler,
stub_compiler->CheckPrototypes(object, receiver, holder,
scratch1, scratch2, name, miss);
- if (lookup->IsValid() && lookup->IsCacheable()) {
+ if (lookup->IsProperty() && lookup->IsCacheable()) {
compiler->CompileCacheable(masm,
stub_compiler,
receiver,
@@ -362,7 +359,7 @@ class LoadInterceptorCompiler BASE_EMBEDDED {
LookupResult* lookup,
String* name,
Label* miss_label) {
- AccessorInfo* callback = 0;
+ AccessorInfo* callback = NULL;
bool optimize = false;
// So far the most popular follow ups for interceptor loads are FIELD
// and CALLBACKS, so inline only them, other cases may be added
@@ -479,88 +476,337 @@ class LoadInterceptorCompiler BASE_EMBEDDED {
};
+// Holds information about possible function call optimizations.
+class CallOptimization BASE_EMBEDDED {
+ public:
+ explicit CallOptimization(LookupResult* lookup)
+ : constant_function_(NULL),
+ is_simple_api_call_(false),
+ expected_receiver_type_(NULL),
+ api_call_info_(NULL) {
+ if (!lookup->IsProperty() || !lookup->IsCacheable()) return;
+
+ // We only optimize constant function calls.
+ if (lookup->type() != CONSTANT_FUNCTION) return;
+
+ Initialize(lookup->GetConstantFunction());
+ }
+
+ explicit CallOptimization(JSFunction* function) {
+ Initialize(function);
+ }
+
+ bool is_constant_call() const {
+ return constant_function_ != NULL;
+ }
+
+ JSFunction* constant_function() const {
+ ASSERT(constant_function_ != NULL);
+ return constant_function_;
+ }
+
+ bool is_simple_api_call() const {
+ return is_simple_api_call_;
+ }
+
+ FunctionTemplateInfo* expected_receiver_type() const {
+ ASSERT(is_simple_api_call_);
+ return expected_receiver_type_;
+ }
+
+ CallHandlerInfo* api_call_info() const {
+ ASSERT(is_simple_api_call_);
+ return api_call_info_;
+ }
+
+ // Returns the depth of the object having the expected type in the
+ // prototype chain between the two arguments.
+ int GetPrototypeDepthOfExpectedType(JSObject* object,
+ JSObject* holder) const {
+ ASSERT(is_simple_api_call_);
+ if (expected_receiver_type_ == NULL) return 0;
+ int depth = 0;
+ while (object != holder) {
+ if (object->IsInstanceOf(expected_receiver_type_)) return depth;
+ object = JSObject::cast(object->GetPrototype());
+ ++depth;
+ }
+ if (holder->IsInstanceOf(expected_receiver_type_)) return depth;
+ return kInvalidProtoDepth;
+ }
+
+ private:
+ void Initialize(JSFunction* function) {
+ if (!function->is_compiled()) return;
+
+ constant_function_ = function;
+ is_simple_api_call_ = false;
+
+ AnalyzePossibleApiFunction(function);
+ }
+
+ // Determines whether the given function can be called using the
+ // fast api call builtin.
+ void AnalyzePossibleApiFunction(JSFunction* function) {
+ SharedFunctionInfo* sfi = function->shared();
+ if (sfi->function_data()->IsUndefined()) return;
+ FunctionTemplateInfo* info =
+ FunctionTemplateInfo::cast(sfi->function_data());
+
+ // Require a C++ callback.
+ if (info->call_code()->IsUndefined()) return;
+ api_call_info_ = CallHandlerInfo::cast(info->call_code());
+
+ // Accept signatures that either have no restrictions at all or
+ // only have restrictions on the receiver.
+ if (!info->signature()->IsUndefined()) {
+ SignatureInfo* signature = SignatureInfo::cast(info->signature());
+ if (!signature->args()->IsUndefined()) return;
+ if (!signature->receiver()->IsUndefined()) {
+ expected_receiver_type_ =
+ FunctionTemplateInfo::cast(signature->receiver());
+ }
+ }
+
+ is_simple_api_call_ = true;
+ }
+
+ JSFunction* constant_function_;
+ bool is_simple_api_call_;
+ FunctionTemplateInfo* expected_receiver_type_;
+ CallHandlerInfo* api_call_info_;
+};
+
+
+// Reserves space for the extra arguments to FastHandleApiCall in the
+// caller's frame.
+//
+// These arguments are set by CheckPrototypes and GenerateFastApiCall.
+static void ReserveSpaceForFastApiCall(MacroAssembler* masm, Register scratch) {
+ // ----------- S t a t e -------------
+ // -- esp[0] : return address
+ // -- esp[4] : last argument in the internal frame of the caller
+ // -----------------------------------
+ __ pop(scratch);
+ __ push(Immediate(Smi::FromInt(0)));
+ __ push(Immediate(Smi::FromInt(0)));
+ __ push(Immediate(Smi::FromInt(0)));
+ __ push(Immediate(Smi::FromInt(0)));
+ __ push(scratch);
+}
+
+
+// Undoes the effects of ReserveSpaceForFastApiCall.
+static void FreeSpaceForFastApiCall(MacroAssembler* masm, Register scratch) {
+ // ----------- S t a t e -------------
+ // -- esp[0] : return address
+ // -- esp[4] : last fast api call extra argument
+ // -- ...
+ // -- esp[16] : first fast api call extra argument
+ // -- esp[20] : last argument in the internal frame
+ // -----------------------------------
+ __ pop(scratch);
+ __ add(Operand(esp), Immediate(kPointerSize * 4));
+ __ push(scratch);
+}
+
+
+// Generates call to FastHandleApiCall builtin.
+static void GenerateFastApiCall(MacroAssembler* masm,
+ const CallOptimization& optimization,
+ int argc) {
+ // ----------- S t a t e -------------
+ // -- esp[0] : return address
+ // -- esp[4] : object passing the type check
+ // (last fast api call extra argument,
+ // set by CheckPrototypes)
+ // -- esp[8] : api call data
+ // -- esp[12] : api callback
+ // -- esp[16] : api function
+ // (first fast api call extra argument)
+ // -- esp[20] : last argument
+ // -- ...
+ // -- esp[(argc + 5) * 4] : first argument
+ // -- esp[(argc + 6) * 4] : receiver
+ // -----------------------------------
+
+ // Get the function and setup the context.
+ JSFunction* function = optimization.constant_function();
+ __ mov(edi, Immediate(Handle<JSFunction>(function)));
+ __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+
+ // Pass the additional arguments FastHandleApiCall expects.
+ __ mov(Operand(esp, 4 * kPointerSize), edi);
+ bool info_loaded = false;
+ Object* callback = optimization.api_call_info()->callback();
+ if (Heap::InNewSpace(callback)) {
+ info_loaded = true;
+ __ mov(ecx, Handle<CallHandlerInfo>(optimization.api_call_info()));
+ __ mov(ebx, FieldOperand(ecx, CallHandlerInfo::kCallbackOffset));
+ __ mov(Operand(esp, 3 * kPointerSize), ebx);
+ } else {
+ __ mov(Operand(esp, 3 * kPointerSize), Immediate(Handle<Object>(callback)));
+ }
+ Object* call_data = optimization.api_call_info()->data();
+ if (Heap::InNewSpace(call_data)) {
+ if (!info_loaded) {
+ __ mov(ecx, Handle<CallHandlerInfo>(optimization.api_call_info()));
+ }
+ __ mov(ebx, FieldOperand(ecx, CallHandlerInfo::kDataOffset));
+ __ mov(Operand(esp, 2 * kPointerSize), ebx);
+ } else {
+ __ mov(Operand(esp, 2 * kPointerSize),
+ Immediate(Handle<Object>(call_data)));
+ }
+
+ // Set the number of arguments.
+ __ mov(eax, Immediate(argc + 4));
+
+ // Jump to the fast api call builtin (tail call).
+ Handle<Code> code = Handle<Code>(
+ Builtins::builtin(Builtins::FastHandleApiCall));
+ ParameterCount expected(0);
+ __ InvokeCode(code, expected, expected,
+ RelocInfo::CODE_TARGET, JUMP_FUNCTION);
+}
+
+
class CallInterceptorCompiler BASE_EMBEDDED {
public:
- CallInterceptorCompiler(const ParameterCount& arguments, Register name)
- : arguments_(arguments), argc_(arguments.immediate()), name_(name) {}
+ CallInterceptorCompiler(StubCompiler* stub_compiler,
+ const ParameterCount& arguments,
+ Register name)
+ : stub_compiler_(stub_compiler),
+ arguments_(arguments),
+ name_(name) {}
+
+ void Compile(MacroAssembler* masm,
+ JSObject* object,
+ JSObject* holder,
+ String* name,
+ LookupResult* lookup,
+ Register receiver,
+ Register scratch1,
+ Register scratch2,
+ Label* miss) {
+ ASSERT(holder->HasNamedInterceptor());
+ ASSERT(!holder->GetNamedInterceptor()->getter()->IsUndefined());
+
+ // Check that the receiver isn't a smi.
+ __ test(receiver, Immediate(kSmiTagMask));
+ __ j(zero, miss, not_taken);
+
+ CallOptimization optimization(lookup);
+
+ if (optimization.is_constant_call() &&
+ !Top::CanHaveSpecialFunctions(holder)) {
+ CompileCacheable(masm,
+ object,
+ receiver,
+ scratch1,
+ scratch2,
+ holder,
+ lookup,
+ name,
+ optimization,
+ miss);
+ } else {
+ CompileRegular(masm,
+ object,
+ receiver,
+ scratch1,
+ scratch2,
+ name,
+ holder,
+ miss);
+ }
+ }
+ private:
void CompileCacheable(MacroAssembler* masm,
- StubCompiler* stub_compiler,
+ JSObject* object,
Register receiver,
- Register holder,
Register scratch1,
Register scratch2,
JSObject* holder_obj,
LookupResult* lookup,
String* name,
+ const CallOptimization& optimization,
Label* miss_label) {
- JSFunction* function = 0;
- bool optimize = false;
- // So far the most popular case for failed interceptor is
- // CONSTANT_FUNCTION sitting below.
- if (lookup->type() == CONSTANT_FUNCTION) {
- function = lookup->GetConstantFunction();
- // JSArray holder is a special case for call constant function
- // (see the corresponding code).
- if (function->is_compiled() && !holder_obj->IsJSArray()) {
- optimize = true;
+ ASSERT(optimization.is_constant_call());
+ ASSERT(!lookup->holder()->IsGlobalObject());
+
+ int depth1 = kInvalidProtoDepth;
+ int depth2 = kInvalidProtoDepth;
+ bool can_do_fast_api_call = false;
+ if (optimization.is_simple_api_call() &&
+ !lookup->holder()->IsGlobalObject()) {
+ depth1 = optimization.GetPrototypeDepthOfExpectedType(object, holder_obj);
+ if (depth1 == kInvalidProtoDepth) {
+ depth2 = optimization.GetPrototypeDepthOfExpectedType(holder_obj,
+ lookup->holder());
}
+ can_do_fast_api_call = (depth1 != kInvalidProtoDepth) ||
+ (depth2 != kInvalidProtoDepth);
}
- if (!optimize) {
- CompileRegular(masm, receiver, holder, scratch2, holder_obj, miss_label);
- return;
+ __ IncrementCounter(&Counters::call_const_interceptor, 1);
+
+ if (can_do_fast_api_call) {
+ __ IncrementCounter(&Counters::call_const_interceptor_fast_api, 1);
+ ReserveSpaceForFastApiCall(masm, scratch1);
}
- __ EnterInternalFrame();
- __ push(holder); // Save the holder.
- __ push(name_); // Save the name.
+ Label miss_cleanup;
+ Label* miss = can_do_fast_api_call ? &miss_cleanup : miss_label;
+ Register holder =
+ stub_compiler_->CheckPrototypes(object, receiver, holder_obj,
+ scratch1, scratch2, name,
+ depth1, miss);
- CompileCallLoadPropertyWithInterceptor(masm,
- receiver,
- holder,
- name_,
- holder_obj);
+ Label regular_invoke;
+ LoadWithInterceptor(masm, receiver, holder, holder_obj, &regular_invoke);
- __ pop(name_); // Restore the name.
- __ pop(receiver); // Restore the holder.
- __ LeaveInternalFrame();
+ // Generate code for the failed interceptor case.
- __ cmp(eax, Factory::no_interceptor_result_sentinel());
- Label invoke;
- __ j(not_equal, &invoke);
-
- stub_compiler->CheckPrototypes(holder_obj, receiver,
- lookup->holder(), scratch1,
- scratch2,
- name,
- miss_label);
- if (lookup->holder()->IsGlobalObject()) {
- __ mov(edx, Operand(esp, (argc_ + 1) * kPointerSize));
- __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
- __ mov(Operand(esp, (argc_ + 1) * kPointerSize), edx);
- }
+ // Check the lookup is still valid.
+ stub_compiler_->CheckPrototypes(holder_obj, receiver,
+ lookup->holder(),
+ scratch1, scratch2, name,
+ depth2, miss);
- ASSERT(function->is_compiled());
- // Get the function and setup the context.
- __ mov(edi, Immediate(Handle<JSFunction>(function)));
- __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
+ if (can_do_fast_api_call) {
+ GenerateFastApiCall(masm, optimization, arguments_.immediate());
+ } else {
+ __ InvokeFunction(optimization.constant_function(), arguments_,
+ JUMP_FUNCTION);
+ }
- // Jump to the cached code (tail call).
- Handle<Code> code(function->code());
- ParameterCount expected(function->shared()->formal_parameter_count());
- __ InvokeCode(code, expected, arguments_,
- RelocInfo::CODE_TARGET, JUMP_FUNCTION);
+ if (can_do_fast_api_call) {
+ __ bind(&miss_cleanup);
+ FreeSpaceForFastApiCall(masm, scratch1);
+ __ jmp(miss_label);
+ }
- __ bind(&invoke);
+ __ bind(&regular_invoke);
+ if (can_do_fast_api_call) {
+ FreeSpaceForFastApiCall(masm, scratch1);
+ }
}
void CompileRegular(MacroAssembler* masm,
+ JSObject* object,
Register receiver,
- Register holder,
- Register scratch,
+ Register scratch1,
+ Register scratch2,
+ String* name,
JSObject* holder_obj,
Label* miss_label) {
+ Register holder =
+ stub_compiler_->CheckPrototypes(object, receiver, holder_obj,
+ scratch1, scratch2, name,
+ miss_label);
+
__ EnterInternalFrame();
// Save the name_ register across the call.
__ push(name_);
@@ -571,22 +817,41 @@ class CallInterceptorCompiler BASE_EMBEDDED {
name_,
holder_obj);
- ExternalReference ref = ExternalReference(
- IC_Utility(IC::kLoadPropertyWithInterceptorForCall));
- __ mov(eax, Immediate(5));
- __ mov(ebx, Immediate(ref));
-
- CEntryStub stub(1);
- __ CallStub(&stub);
+ __ CallExternalReference(
+ ExternalReference(
+ IC_Utility(IC::kLoadPropertyWithInterceptorForCall)),
+ 5);
// Restore the name_ register.
__ pop(name_);
__ LeaveInternalFrame();
}
- private:
+ void LoadWithInterceptor(MacroAssembler* masm,
+ Register receiver,
+ Register holder,
+ JSObject* holder_obj,
+ Label* interceptor_succeeded) {
+ __ EnterInternalFrame();
+ __ push(holder); // Save the holder.
+ __ push(name_); // Save the name.
+
+ CompileCallLoadPropertyWithInterceptor(masm,
+ receiver,
+ holder,
+ name_,
+ holder_obj);
+
+ __ pop(name_); // Restore the name.
+ __ pop(receiver); // Restore the holder.
+ __ LeaveInternalFrame();
+
+ __ cmp(eax, Factory::no_interceptor_result_sentinel());
+ __ j(not_equal, interceptor_succeeded);
+ }
+
+ StubCompiler* stub_compiler_;
const ParameterCount& arguments_;
- int argc_;
Register name_;
};
@@ -605,8 +870,9 @@ void StubCompiler::GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind) {
}
+// Both name_reg and receiver_reg are preserved on jumps to miss_label,
+// but may be destroyed if store is successful.
void StubCompiler::GenerateStoreField(MacroAssembler* masm,
- Builtins::Name storage_extend,
JSObject* object,
int index,
Map* transition,
@@ -636,9 +902,13 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm,
if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) {
// The properties must be extended before we can store the value.
// We jump to a runtime call that extends the properties array.
- __ mov(ecx, Immediate(Handle<Map>(transition)));
- Handle<Code> ic(Builtins::builtin(storage_extend));
- __ jmp(ic, RelocInfo::CODE_TARGET);
+ __ pop(scratch); // Return address.
+ __ push(receiver_reg);
+ __ push(Immediate(Handle<Map>(transition)));
+ __ push(eax);
+ __ push(scratch);
+ __ TailCallRuntime(
+ ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage)), 3, 1);
return;
}
@@ -691,10 +961,12 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
Register holder_reg,
Register scratch,
String* name,
+ int push_at_depth,
Label* miss) {
// Check that the maps haven't changed.
Register result =
- masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch, miss);
+ masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch,
+ push_at_depth, miss);
// If we've skipped any global objects, it's not enough to verify
// that their maps haven't changed.
@@ -716,7 +988,7 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
object = JSObject::cast(object->GetPrototype());
}
- // Return the register containin the holder.
+ // Return the register containing the holder.
return result;
}
@@ -887,7 +1159,7 @@ Object* StubCompiler::CompileLazyCompile(Code::Flags flags) {
}
-Object* CallStubCompiler::CompileCallField(Object* object,
+Object* CallStubCompiler::CompileCallField(JSObject* object,
JSObject* holder,
int index,
String* name) {
@@ -909,9 +1181,7 @@ Object* CallStubCompiler::CompileCallField(Object* object,
__ j(zero, &miss, not_taken);
// Do the right check and compute the holder register.
- Register reg =
- CheckPrototypes(JSObject::cast(object), edx, holder,
- ebx, eax, name, &miss);
+ Register reg = CheckPrototypes(object, edx, holder, ebx, eax, name, &miss);
GenerateFastPropertyLoad(masm(), edi, reg, holder, index);
@@ -969,15 +1239,31 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
// unless we're doing a receiver map check.
ASSERT(!object->IsGlobalObject() || check == RECEIVER_MAP_CHECK);
+ CallOptimization optimization(function);
+ int depth = kInvalidProtoDepth;
+
switch (check) {
case RECEIVER_MAP_CHECK:
+ __ IncrementCounter(&Counters::call_const, 1);
+
+ if (optimization.is_simple_api_call() && !object->IsGlobalObject()) {
+ depth = optimization.GetPrototypeDepthOfExpectedType(
+ JSObject::cast(object), holder);
+ }
+
+ if (depth != kInvalidProtoDepth) {
+ __ IncrementCounter(&Counters::call_const_fast_api, 1);
+ ReserveSpaceForFastApiCall(masm(), eax);
+ }
+
// Check that the maps haven't changed.
CheckPrototypes(JSObject::cast(object), edx, holder,
- ebx, eax, name, &miss);
+ ebx, eax, name, depth, &miss);
// Patch the receiver on the stack with the global proxy if
// necessary.
if (object->IsGlobalObject()) {
+ ASSERT(depth == kInvalidProtoDepth);
__ mov(edx, FieldOperand(edx, GlobalObject::kGlobalReceiverOffset));
__ mov(Operand(esp, (argc + 1) * kPointerSize), edx);
}
@@ -1062,19 +1348,17 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
UNREACHABLE();
}
- // Get the function and setup the context.
- __ mov(edi, Immediate(Handle<JSFunction>(function)));
- __ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
-
- // Jump to the cached code (tail call).
- ASSERT(function->is_compiled());
- Handle<Code> code(function->code());
- ParameterCount expected(function->shared()->formal_parameter_count());
- __ InvokeCode(code, expected, arguments(),
- RelocInfo::CODE_TARGET, JUMP_FUNCTION);
+ if (depth != kInvalidProtoDepth) {
+ GenerateFastApiCall(masm(), optimization, argc);
+ } else {
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
+ }
// Handle call cache miss.
__ bind(&miss);
+ if (depth != kInvalidProtoDepth) {
+ FreeSpaceForFastApiCall(masm(), eax);
+ }
Handle<Code> ic = ComputeCallMiss(arguments().immediate());
__ jmp(ic, RelocInfo::CODE_TARGET);
@@ -1087,7 +1371,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
}
-Object* CallStubCompiler::CompileCallInterceptor(Object* object,
+Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
JSObject* holder,
String* name) {
// ----------- S t a t e -------------
@@ -1108,18 +1392,16 @@ Object* CallStubCompiler::CompileCallInterceptor(Object* object,
// Get the receiver from the stack.
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
- CallInterceptorCompiler compiler(arguments(), ecx);
- CompileLoadInterceptor(&compiler,
- this,
- masm(),
- JSObject::cast(object),
- holder,
- name,
- &lookup,
- edx,
- ebx,
- edi,
- &miss);
+ CallInterceptorCompiler compiler(this, arguments(), ecx);
+ compiler.Compile(masm(),
+ object,
+ holder,
+ name,
+ &lookup,
+ edx,
+ ebx,
+ edi,
+ &miss);
// Restore receiver.
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
@@ -1249,7 +1531,6 @@ Object* StoreStubCompiler::CompileStoreField(JSObject* object,
// Generate store field code. Trashes the name register.
GenerateStoreField(masm(),
- Builtins::StoreIC_ExtendStorage,
object,
index,
transition,
@@ -1423,15 +1704,14 @@ Object* KeyedStoreStubCompiler::CompileStoreField(JSObject* object,
__ j(not_equal, &miss, not_taken);
// Get the object from the stack.
- __ mov(ebx, Operand(esp, 2 * kPointerSize));
+ __ mov(edx, Operand(esp, 2 * kPointerSize));
// Generate store field code. Trashes the name register.
GenerateStoreField(masm(),
- Builtins::KeyedStoreIC_ExtendStorage,
object,
index,
transition,
- ebx, ecx, edx,
+ edx, ecx, ebx,
&miss);
// Handle store cache miss.
@@ -1451,13 +1731,12 @@ Object* LoadStubCompiler::CompileLoadField(JSObject* object,
int index,
String* name) {
// ----------- S t a t e -------------
+ // -- eax : receiver
// -- ecx : name
// -- esp[0] : return address
- // -- esp[4] : receiver
// -----------------------------------
Label miss;
- __ mov(eax, Operand(esp, kPointerSize));
GenerateLoadField(object, holder, eax, ebx, edx, index, name, &miss);
__ bind(&miss);
GenerateLoadMiss(masm(), Code::LOAD_IC);
@@ -1472,13 +1751,12 @@ Object* LoadStubCompiler::CompileLoadCallback(String* name,
JSObject* holder,
AccessorInfo* callback) {
// ----------- S t a t e -------------
+ // -- eax : receiver
// -- ecx : name
// -- esp[0] : return address
- // -- esp[4] : receiver
// -----------------------------------
Label miss;
- __ mov(eax, Operand(esp, kPointerSize));
Failure* failure = Failure::InternalError();
bool success = GenerateLoadCallback(object, holder, eax, ecx, ebx, edx,
callback, name, &miss, &failure);
@@ -1497,13 +1775,12 @@ Object* LoadStubCompiler::CompileLoadConstant(JSObject* object,
Object* value,
String* name) {
// ----------- S t a t e -------------
+ // -- eax : receiver
// -- ecx : name
// -- esp[0] : return address
- // -- esp[4] : receiver
// -----------------------------------
Label miss;
- __ mov(eax, Operand(esp, kPointerSize));
GenerateLoadConstant(object, holder, eax, ebx, edx, value, name, &miss);
__ bind(&miss);
GenerateLoadMiss(masm(), Code::LOAD_IC);
@@ -1517,16 +1794,15 @@ Object* LoadStubCompiler::CompileLoadInterceptor(JSObject* receiver,
JSObject* holder,
String* name) {
// ----------- S t a t e -------------
+ // -- eax : receiver
// -- ecx : name
// -- esp[0] : return address
- // -- esp[4] : receiver
// -----------------------------------
Label miss;
LookupResult lookup;
LookupPostInterceptor(holder, name, &lookup);
- __ mov(eax, Operand(esp, kPointerSize));
// TODO(368): Compile in the whole chain: all the interceptors in
// prototypes and ultimate answer.
GenerateLoadInterceptor(receiver,
@@ -1553,15 +1829,12 @@ Object* LoadStubCompiler::CompileLoadGlobal(JSObject* object,
String* name,
bool is_dont_delete) {
// ----------- S t a t e -------------
+ // -- eax : receiver
// -- ecx : name
// -- esp[0] : return address
- // -- esp[4] : receiver
// -----------------------------------
Label miss;
- // Get the receiver from the stack.
- __ mov(eax, Operand(esp, kPointerSize));
-
// If the object is the holder then we know that it's a global
// object which can only happen for contextual loads. In this case,
// the receiver cannot be a smi.
@@ -1574,19 +1847,20 @@ Object* LoadStubCompiler::CompileLoadGlobal(JSObject* object,
CheckPrototypes(object, eax, holder, ebx, edx, name, &miss);
// Get the value from the cell.
- __ mov(eax, Immediate(Handle<JSGlobalPropertyCell>(cell)));
- __ mov(eax, FieldOperand(eax, JSGlobalPropertyCell::kValueOffset));
+ __ mov(ebx, Immediate(Handle<JSGlobalPropertyCell>(cell)));
+ __ mov(ebx, FieldOperand(ebx, JSGlobalPropertyCell::kValueOffset));
// Check for deleted property if property can actually be deleted.
if (!is_dont_delete) {
- __ cmp(eax, Factory::the_hole_value());
+ __ cmp(ebx, Factory::the_hole_value());
__ j(equal, &miss, not_taken);
} else if (FLAG_debug_code) {
- __ cmp(eax, Factory::the_hole_value());
+ __ cmp(ebx, Factory::the_hole_value());
__ Check(not_equal, "DontDelete cells can't contain the hole");
}
__ IncrementCounter(&Counters::named_load_global_inline, 1);
+ __ mov(eax, ebx);
__ ret(0);
__ bind(&miss);
@@ -1603,21 +1877,19 @@ Object* KeyedLoadStubCompiler::CompileLoadField(String* name,
JSObject* holder,
int index) {
// ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : name
- // -- esp[8] : receiver
// -----------------------------------
Label miss;
- __ mov(eax, Operand(esp, kPointerSize));
- __ mov(ecx, Operand(esp, 2 * kPointerSize));
__ IncrementCounter(&Counters::keyed_load_field, 1);
// Check that the name has not changed.
__ cmp(Operand(eax), Immediate(Handle<String>(name)));
__ j(not_equal, &miss, not_taken);
- GenerateLoadField(receiver, holder, ecx, ebx, edx, index, name, &miss);
+ GenerateLoadField(receiver, holder, edx, ebx, ecx, index, name, &miss);
__ bind(&miss);
__ DecrementCounter(&Counters::keyed_load_field, 1);
@@ -1633,14 +1905,12 @@ Object* KeyedLoadStubCompiler::CompileLoadCallback(String* name,
JSObject* holder,
AccessorInfo* callback) {
// ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : name
- // -- esp[8] : receiver
// -----------------------------------
Label miss;
- __ mov(eax, Operand(esp, kPointerSize));
- __ mov(ecx, Operand(esp, 2 * kPointerSize));
__ IncrementCounter(&Counters::keyed_load_callback, 1);
// Check that the name has not changed.
@@ -1648,7 +1918,7 @@ Object* KeyedLoadStubCompiler::CompileLoadCallback(String* name,
__ j(not_equal, &miss, not_taken);
Failure* failure = Failure::InternalError();
- bool success = GenerateLoadCallback(receiver, holder, ecx, eax, ebx, edx,
+ bool success = GenerateLoadCallback(receiver, holder, edx, eax, ebx, ecx,
callback, name, &miss, &failure);
if (!success) return failure;
@@ -1666,21 +1936,19 @@ Object* KeyedLoadStubCompiler::CompileLoadConstant(String* name,
JSObject* holder,
Object* value) {
// ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : name
- // -- esp[8] : receiver
// -----------------------------------
Label miss;
- __ mov(eax, Operand(esp, kPointerSize));
- __ mov(ecx, Operand(esp, 2 * kPointerSize));
__ IncrementCounter(&Counters::keyed_load_constant_function, 1);
// Check that the name has not changed.
__ cmp(Operand(eax), Immediate(Handle<String>(name)));
__ j(not_equal, &miss, not_taken);
- GenerateLoadConstant(receiver, holder, ecx, ebx, edx,
+ GenerateLoadConstant(receiver, holder, edx, ebx, ecx,
value, name, &miss);
__ bind(&miss);
__ DecrementCounter(&Counters::keyed_load_constant_function, 1);
@@ -1695,14 +1963,12 @@ Object* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver,
JSObject* holder,
String* name) {
// ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : name
- // -- esp[8] : receiver
// -----------------------------------
Label miss;
- __ mov(eax, Operand(esp, kPointerSize));
- __ mov(ecx, Operand(esp, 2 * kPointerSize));
__ IncrementCounter(&Counters::keyed_load_interceptor, 1);
// Check that the name has not changed.
@@ -1714,9 +1980,9 @@ Object* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver,
GenerateLoadInterceptor(receiver,
holder,
&lookup,
- ecx,
- eax,
edx,
+ eax,
+ ecx,
ebx,
name,
&miss);
@@ -1733,21 +1999,19 @@ Object* KeyedLoadStubCompiler::CompileLoadInterceptor(JSObject* receiver,
Object* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) {
// ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : name
- // -- esp[8] : receiver
// -----------------------------------
Label miss;
- __ mov(eax, Operand(esp, kPointerSize));
- __ mov(ecx, Operand(esp, 2 * kPointerSize));
__ IncrementCounter(&Counters::keyed_load_array_length, 1);
// Check that the name has not changed.
__ cmp(Operand(eax), Immediate(Handle<String>(name)));
__ j(not_equal, &miss, not_taken);
- GenerateLoadArrayLength(masm(), ecx, edx, &miss);
+ GenerateLoadArrayLength(masm(), edx, ecx, &miss);
__ bind(&miss);
__ DecrementCounter(&Counters::keyed_load_array_length, 1);
GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
@@ -1759,21 +2023,19 @@ Object* KeyedLoadStubCompiler::CompileLoadArrayLength(String* name) {
Object* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) {
// ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : name
- // -- esp[8] : receiver
// -----------------------------------
Label miss;
- __ mov(eax, Operand(esp, kPointerSize));
- __ mov(ecx, Operand(esp, 2 * kPointerSize));
__ IncrementCounter(&Counters::keyed_load_string_length, 1);
// Check that the name has not changed.
__ cmp(Operand(eax), Immediate(Handle<String>(name)));
__ j(not_equal, &miss, not_taken);
- GenerateLoadStringLength(masm(), ecx, edx, &miss);
+ GenerateLoadStringLength(masm(), edx, ecx, ebx, &miss);
__ bind(&miss);
__ DecrementCounter(&Counters::keyed_load_string_length, 1);
GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
@@ -1785,21 +2047,19 @@ Object* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) {
Object* KeyedLoadStubCompiler::CompileLoadFunctionPrototype(String* name) {
// ----------- S t a t e -------------
+ // -- eax : key
+ // -- edx : receiver
// -- esp[0] : return address
- // -- esp[4] : name
- // -- esp[8] : receiver
// -----------------------------------
Label miss;
- __ mov(eax, Operand(esp, kPointerSize));
- __ mov(ecx, Operand(esp, 2 * kPointerSize));
__ IncrementCounter(&Counters::keyed_load_function_prototype, 1);
// Check that the name has not changed.
__ cmp(Operand(eax), Immediate(Handle<String>(name)));
__ j(not_equal, &miss, not_taken);
- GenerateLoadFunctionPrototype(masm(), ecx, edx, ebx, &miss);
+ GenerateLoadFunctionPrototype(masm(), edx, ecx, ebx, &miss);
__ bind(&miss);
__ DecrementCounter(&Counters::keyed_load_function_prototype, 1);
GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
diff --git a/src/ia32/virtual-frame-ia32.cc b/src/ia32/virtual-frame-ia32.cc
index 9267507c..7df028e9 100644
--- a/src/ia32/virtual-frame-ia32.cc
+++ b/src/ia32/virtual-frame-ia32.cc
@@ -45,7 +45,7 @@ VirtualFrame::VirtualFrame()
: elements_(parameter_count() + local_count() + kPreallocatedElements),
stack_pointer_(parameter_count() + 1) { // 0-based index of TOS.
for (int i = 0; i <= stack_pointer_; i++) {
- elements_.Add(FrameElement::MemoryElement());
+ elements_.Add(FrameElement::MemoryElement(NumberInfo::kUnknown));
}
for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
register_locations_[i] = kIllegalIndex;
@@ -173,10 +173,12 @@ void VirtualFrame::MakeMergable() {
for (int i = 0; i < element_count(); i++) {
FrameElement element = elements_[i];
+ // All number type information is reset to unknown for a mergable frame
+ // because of incoming back edges.
if (element.is_constant() || element.is_copy()) {
if (element.is_synced()) {
// Just spill.
- elements_[i] = FrameElement::MemoryElement();
+ elements_[i] = FrameElement::MemoryElement(NumberInfo::kUnknown);
} else {
// Allocate to a register.
FrameElement backing_element; // Invalid if not a copy.
@@ -187,7 +189,8 @@ void VirtualFrame::MakeMergable() {
ASSERT(fresh.is_valid()); // A register was spilled if all were in use.
elements_[i] =
FrameElement::RegisterElement(fresh.reg(),
- FrameElement::NOT_SYNCED);
+ FrameElement::NOT_SYNCED,
+ NumberInfo::kUnknown);
Use(fresh.reg(), i);
// Emit a move.
@@ -220,6 +223,7 @@ void VirtualFrame::MakeMergable() {
// The copy flag is not relied on before the end of this loop,
// including when registers are spilled.
elements_[i].clear_copied();
+ elements_[i].set_number_info(NumberInfo::kUnknown);
}
}
}
@@ -607,10 +611,14 @@ int VirtualFrame::InvalidateFrameSlotAt(int index) {
// Set the new backing element.
if (elements_[new_backing_index].is_synced()) {
elements_[new_backing_index] =
- FrameElement::RegisterElement(backing_reg, FrameElement::SYNCED);
+ FrameElement::RegisterElement(backing_reg,
+ FrameElement::SYNCED,
+ original.number_info());
} else {
elements_[new_backing_index] =
- FrameElement::RegisterElement(backing_reg, FrameElement::NOT_SYNCED);
+ FrameElement::RegisterElement(backing_reg,
+ FrameElement::NOT_SYNCED,
+ original.number_info());
}
// Update the other copies.
for (int i = new_backing_index + 1; i < element_count(); i++) {
@@ -641,7 +649,8 @@ void VirtualFrame::TakeFrameSlotAt(int index) {
ASSERT(fresh.is_valid());
FrameElement new_element =
FrameElement::RegisterElement(fresh.reg(),
- FrameElement::NOT_SYNCED);
+ FrameElement::NOT_SYNCED,
+ original.number_info());
Use(fresh.reg(), element_count());
elements_.Add(new_element);
__ mov(fresh.reg(), Operand(ebp, fp_relative(index)));
@@ -853,6 +862,17 @@ Result VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) {
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
+void VirtualFrame::DebugBreak() {
+ PrepareForCall(0, 0);
+ ASSERT(cgen()->HasValidEntryRegisters());
+ __ DebugBreak();
+ Result result = cgen()->allocator()->Allocate(eax);
+ ASSERT(result.is_valid());
+}
+#endif
+
+
Result VirtualFrame::InvokeBuiltin(Builtins::JavaScript id,
InvokeFlag flag,
int arg_count) {
@@ -877,67 +897,89 @@ Result VirtualFrame::RawCallCodeObject(Handle<Code> code,
Result VirtualFrame::CallLoadIC(RelocInfo::Mode mode) {
// Name and receiver are on the top of the frame. The IC expects
- // name in ecx and receiver on the stack. It does not drop the
- // receiver.
+ // name in ecx and receiver in eax.
Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
Result name = Pop();
- PrepareForCall(1, 0); // One stack arg, not callee-dropped.
- name.ToRegister(ecx);
+ Result receiver = Pop();
+ PrepareForCall(0, 0); // No stack arguments.
+ // Move results to the right registers:
+ if (name.is_register() && name.reg().is(eax)) {
+ if (receiver.is_register() && receiver.reg().is(ecx)) {
+ // Wrong registers.
+ __ xchg(eax, ecx);
+ } else {
+ // Register ecx is free for name, which frees eax for receiver.
+ name.ToRegister(ecx);
+ receiver.ToRegister(eax);
+ }
+ } else {
+ // Register eax is free for receiver, which frees ecx for name.
+ receiver.ToRegister(eax);
+ name.ToRegister(ecx);
+ }
name.Unuse();
+ receiver.Unuse();
return RawCallCodeObject(ic, mode);
}
Result VirtualFrame::CallKeyedLoadIC(RelocInfo::Mode mode) {
- // Key and receiver are on top of the frame. The IC expects them on
- // the stack. It does not drop them.
+ // Key and receiver are on top of the frame. Put them in eax and edx.
+ Result key = Pop();
+ Result receiver = Pop();
+ PrepareForCall(0, 0);
+
+ if (!key.is_register() || !key.reg().is(edx)) {
+ // Register edx is available for receiver.
+ receiver.ToRegister(edx);
+ key.ToRegister(eax);
+ } else if (!receiver.is_register() || !receiver.reg().is(eax)) {
+ // Register eax is available for key.
+ key.ToRegister(eax);
+ receiver.ToRegister(edx);
+ } else {
+ __ xchg(edx, eax);
+ }
+ key.Unuse();
+ receiver.Unuse();
+
Handle<Code> ic(Builtins::builtin(Builtins::KeyedLoadIC_Initialize));
- PrepareForCall(2, 0); // Two stack args, neither callee-dropped.
return RawCallCodeObject(ic, mode);
}
-Result VirtualFrame::CallStoreIC() {
- // Name, value, and receiver are on top of the frame. The IC
- // expects name in ecx, value in eax, and receiver in edx.
+Result VirtualFrame::CallStoreIC(Handle<String> name, bool is_contextual) {
+ // Value and (if not contextual) receiver are on top of the frame.
+ // The IC expects name in ecx, value in eax, and receiver in edx.
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Initialize));
- Result name = Pop();
Result value = Pop();
- Result receiver = Pop();
- PrepareForCall(0, 0);
+ if (is_contextual) {
+ PrepareForCall(0, 0);
+ value.ToRegister(eax);
+ __ mov(edx, Operand(esi, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ __ mov(ecx, name);
+ } else {
+ Result receiver = Pop();
+ PrepareForCall(0, 0);
- // Optimized for case in which name is a constant value.
- if (name.is_register() && (name.reg().is(edx) || name.reg().is(eax))) {
- if (!is_used(ecx)) {
- name.ToRegister(ecx);
- } else if (!is_used(ebx)) {
- name.ToRegister(ebx);
- } else {
- ASSERT(!is_used(edi)); // Only three results are live, so edi is free.
- name.ToRegister(edi);
- }
- }
- // Now name is not in edx or eax, so we can fix them, then move name to ecx.
- if (value.is_register() && value.reg().is(edx)) {
- if (receiver.is_register() && receiver.reg().is(eax)) {
- // Wrong registers.
- __ xchg(eax, edx);
+ if (value.is_register() && value.reg().is(edx)) {
+ if (receiver.is_register() && receiver.reg().is(eax)) {
+ // Wrong registers.
+ __ xchg(eax, edx);
+ } else {
+ // Register eax is free for value, which frees edx for receiver.
+ value.ToRegister(eax);
+ receiver.ToRegister(edx);
+ }
} else {
- // Register eax is free for value, which frees edx for receiver.
- value.ToRegister(eax);
+ // Register edx is free for receiver, which guarantees eax is free for
+ // value.
receiver.ToRegister(edx);
+ value.ToRegister(eax);
}
- } else {
- // Register edx is free for receiver, which guarantees eax is free for
- // value.
- receiver.ToRegister(edx);
- value.ToRegister(eax);
}
- // Receiver and value are in the right place, so ecx is free for name.
- name.ToRegister(ecx);
- name.Unuse();
+ __ mov(ecx, name);
value.Unuse();
- receiver.Unuse();
return RawCallCodeObject(ic, RelocInfo::CODE_TARGET);
}
@@ -947,7 +989,6 @@ Result VirtualFrame::CallKeyedStoreIC() {
// expects value in eax and key and receiver on the stack. It does
// not drop the key and receiver.
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
- // TODO(1222589): Make the IC grab the values from the stack.
Result value = Pop();
PrepareForCall(2, 0); // Two stack args, neither callee-dropped.
value.ToRegister(eax);
@@ -1025,6 +1066,14 @@ Result VirtualFrame::Pop() {
int index = element_count();
ASSERT(element.is_valid());
+ // Get number type information of the result.
+ NumberInfo::Type info;
+ if (!element.is_copy()) {
+ info = element.number_info();
+ } else {
+ info = elements_[element.index()].number_info();
+ }
+
bool pop_needed = (stack_pointer_ == index);
if (pop_needed) {
stack_pointer_--;
@@ -1032,6 +1081,7 @@ Result VirtualFrame::Pop() {
Result temp = cgen()->allocator()->Allocate();
ASSERT(temp.is_valid());
__ pop(temp.reg());
+ temp.set_number_info(info);
return temp;
}
@@ -1059,14 +1109,16 @@ Result VirtualFrame::Pop() {
ASSERT(temp.is_valid());
Use(temp.reg(), index);
FrameElement new_element =
- FrameElement::RegisterElement(temp.reg(), FrameElement::SYNCED);
+ FrameElement::RegisterElement(temp.reg(),
+ FrameElement::SYNCED,
+ element.number_info());
// Preserve the copy flag on the element.
if (element.is_copied()) new_element.set_copied();
elements_[index] = new_element;
__ mov(temp.reg(), Operand(ebp, fp_relative(index)));
- return Result(temp.reg());
+ return Result(temp.reg(), info);
} else if (element.is_register()) {
- return Result(element.reg());
+ return Result(element.reg(), info);
} else {
ASSERT(element.is_constant());
return Result(element.handle());
@@ -1090,30 +1142,49 @@ void VirtualFrame::EmitPop(Operand operand) {
}
-void VirtualFrame::EmitPush(Register reg) {
+void VirtualFrame::EmitPush(Register reg, NumberInfo::Type info) {
ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement());
+ elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
__ push(reg);
}
-void VirtualFrame::EmitPush(Operand operand) {
+void VirtualFrame::EmitPush(Operand operand, NumberInfo::Type info) {
ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement());
+ elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
__ push(operand);
}
-void VirtualFrame::EmitPush(Immediate immediate) {
+void VirtualFrame::EmitPush(Immediate immediate, NumberInfo::Type info) {
ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement());
+ elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
__ push(immediate);
}
+void VirtualFrame::Push(Expression* expr) {
+ ASSERT(expr->IsTrivial());
+
+ Literal* lit = expr->AsLiteral();
+ if (lit != NULL) {
+ Push(lit->handle());
+ return;
+ }
+
+ VariableProxy* proxy = expr->AsVariableProxy();
+ if (proxy != NULL && proxy->is_this()) {
+ PushParameterAt(-1);
+ return;
+ }
+
+ UNREACHABLE();
+}
+
+
#undef __
} } // namespace v8::internal
diff --git a/src/ia32/virtual-frame-ia32.h b/src/ia32/virtual-frame-ia32.h
index d6d55d12..7be593cc 100644
--- a/src/ia32/virtual-frame-ia32.h
+++ b/src/ia32/virtual-frame-ia32.h
@@ -28,6 +28,7 @@
#ifndef V8_IA32_VIRTUAL_FRAME_IA32_H_
#define V8_IA32_VIRTUAL_FRAME_IA32_H_
+#include "number-info.h"
#include "register-allocator.h"
#include "scopes.h"
@@ -82,7 +83,8 @@ class VirtualFrame: public ZoneObject {
MacroAssembler* masm() { return cgen()->masm(); }
// Create a duplicate of an existing valid frame element.
- FrameElement CopyElementAt(int index);
+ FrameElement CopyElementAt(int index,
+ NumberInfo::Type info = NumberInfo::kUninitialized);
// The number of elements on the virtual frame.
int element_count() { return elements_.length(); }
@@ -324,21 +326,25 @@ class VirtualFrame: public ZoneObject {
Result CallRuntime(Runtime::Function* f, int arg_count);
Result CallRuntime(Runtime::FunctionId id, int arg_count);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ void DebugBreak();
+#endif
+
// Invoke builtin given the number of arguments it expects on (and
// removes from) the stack.
Result InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag, int arg_count);
// Call load IC. Name and receiver are found on top of the frame.
- // Receiver is not dropped.
+ // Both are dropped.
Result CallLoadIC(RelocInfo::Mode mode);
// Call keyed load IC. Key and receiver are found on top of the
- // frame. They are not dropped.
+ // frame. Both are dropped.
Result CallKeyedLoadIC(RelocInfo::Mode mode);
- // Call store IC. Name, value, and receiver are found on top of the
- // frame. Receiver is not dropped.
- Result CallStoreIC();
+ // Call store IC. If the load is contextual, value is found on top of the
+ // frame. If not, value and receiver are on the frame. Both are dropped.
+ Result CallStoreIC(Handle<String> name, bool is_contextual);
// Call keyed store IC. Value, key, and receiver are found on top
// of the frame. Key and receiver are not dropped.
@@ -381,12 +387,15 @@ class VirtualFrame: public ZoneObject {
// Push an element on top of the expression stack and emit a
// corresponding push instruction.
- void EmitPush(Register reg);
- void EmitPush(Operand operand);
- void EmitPush(Immediate immediate);
+ void EmitPush(Register reg,
+ NumberInfo::Type info = NumberInfo::kUnknown);
+ void EmitPush(Operand operand,
+ NumberInfo::Type info = NumberInfo::kUnknown);
+ void EmitPush(Immediate immediate,
+ NumberInfo::Type info = NumberInfo::kUnknown);
// Push an element on the virtual frame.
- void Push(Register reg);
+ void Push(Register reg, NumberInfo::Type info = NumberInfo::kUnknown);
void Push(Handle<Object> value);
void Push(Smi* value) {
Push(Handle<Object> (value));
@@ -398,7 +407,7 @@ class VirtualFrame: public ZoneObject {
// This assert will trigger if you try to push the same value twice.
ASSERT(result->is_valid());
if (result->is_register()) {
- Push(result->reg());
+ Push(result->reg(), result->number_info());
} else {
ASSERT(result->is_constant());
Push(result->handle());
@@ -406,6 +415,10 @@ class VirtualFrame: public ZoneObject {
result->Unuse();
}
+ // Pushing an expression expects that the expression is trivial (according
+ // to Expression::IsTrivial).
+ void Push(Expression* expr);
+
// Nip removes zero or more elements from immediately below the top
// of the frame, leaving the previous top-of-frame value on top of
// the frame. Nip(k) is equivalent to x = Pop(), Drop(k), Push(x).
diff --git a/src/ic.cc b/src/ic.cc
index 27a18419..b6b57dce 100644
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -330,10 +330,11 @@ static void LookupForRead(Object* object,
while (true) {
object->Lookup(name, lookup);
// Besides normal conditions (property not found or it's not
- // an interceptor), bail out of lookup is not cacheable: we won't
+ // an interceptor), bail out if lookup is not cacheable: we won't
// be able to IC it anyway and regular lookup should work fine.
- if (lookup->IsNotFound() || lookup->type() != INTERCEPTOR ||
- !lookup->IsCacheable()) {
+ if (!lookup->IsFound()
+ || (lookup->type() != INTERCEPTOR)
+ || !lookup->IsCacheable()) {
return;
}
@@ -343,7 +344,7 @@ static void LookupForRead(Object* object,
}
holder->LocalLookupRealNamedProperty(name, lookup);
- if (lookup->IsValid()) {
+ if (lookup->IsProperty()) {
ASSERT(lookup->type() != INTERCEPTOR);
return;
}
@@ -422,7 +423,7 @@ Object* CallIC::LoadFunction(State state,
LookupResult lookup;
LookupForRead(*object, *name, &lookup);
- if (!lookup.IsValid()) {
+ if (!lookup.IsProperty()) {
// If the object does not have the requested property, check which
// exception we need to throw.
if (IsContextual(object)) {
@@ -455,7 +456,7 @@ Object* CallIC::LoadFunction(State state,
if (result->IsJSFunction()) {
// Check if there is an optimized (builtin) version of the function.
- // Ignored this will degrade performance for Array.prototype.{push,pop}.
+ // Ignored this will degrade performance for some Array functions.
// Please note we only return the optimized function iff
// the JSObject has FastElements.
if (object->IsJSObject() && JSObject::cast(*object)->HasFastElements()) {
@@ -492,7 +493,7 @@ void CallIC::UpdateCaches(LookupResult* lookup,
Handle<Object> object,
Handle<String> name) {
// Bail out if we didn't find a result.
- if (!lookup->IsValid() || !lookup->IsCacheable()) return;
+ if (!lookup->IsProperty() || !lookup->IsCacheable()) return;
// Compute the number of arguments.
int argc = target()->arguments_count();
@@ -641,8 +642,8 @@ Object* LoadIC::Load(State state, Handle<Object> object, Handle<String> name) {
LookupResult lookup;
LookupForRead(*object, *name, &lookup);
- // If lookup is invalid, check if we need to throw an exception.
- if (!lookup.IsValid()) {
+ // If we did not find a property, check if we need to throw an exception.
+ if (!lookup.IsProperty()) {
if (FLAG_strict || IsContextual(object)) {
return ReferenceError("not_defined", name);
}
@@ -652,7 +653,7 @@ Object* LoadIC::Load(State state, Handle<Object> object, Handle<String> name) {
bool can_be_inlined =
FLAG_use_ic &&
state == PREMONOMORPHIC &&
- lookup.IsValid() &&
+ lookup.IsProperty() &&
lookup.IsCacheable() &&
lookup.holder() == *object &&
lookup.type() == FIELD &&
@@ -679,7 +680,7 @@ Object* LoadIC::Load(State state, Handle<Object> object, Handle<String> name) {
}
PropertyAttributes attr;
- if (lookup.IsValid() && lookup.type() == INTERCEPTOR) {
+ if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) {
// Get the property.
Object* result = object->GetProperty(*object, &lookup, *name, &attr);
if (result->IsFailure()) return result;
@@ -701,7 +702,7 @@ void LoadIC::UpdateCaches(LookupResult* lookup,
Handle<Object> object,
Handle<String> name) {
// Bail out if we didn't find a result.
- if (!lookup->IsValid() || !lookup->IsCacheable()) return;
+ if (!lookup->IsProperty() || !lookup->IsCacheable()) return;
// Loading properties from values is not common, so don't try to
// deal with non-JS objects here.
@@ -854,8 +855,8 @@ Object* KeyedLoadIC::Load(State state,
LookupResult lookup;
LookupForRead(*object, *name, &lookup);
- // If lookup is invalid, check if we need to throw an exception.
- if (!lookup.IsValid()) {
+ // If we did not find a property, check if we need to throw an exception.
+ if (!lookup.IsProperty()) {
if (FLAG_strict || IsContextual(object)) {
return ReferenceError("not_defined", name);
}
@@ -866,7 +867,7 @@ Object* KeyedLoadIC::Load(State state,
}
PropertyAttributes attr;
- if (lookup.IsValid() && lookup.type() == INTERCEPTOR) {
+ if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) {
// Get the property.
Object* result = object->GetProperty(*object, &lookup, *name, &attr);
if (result->IsFailure()) return result;
@@ -893,6 +894,8 @@ Object* KeyedLoadIC::Load(State state,
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
if (receiver->HasExternalArrayElements()) {
stub = external_array_stub(receiver->GetElementsKind());
+ } else if (receiver->HasIndexedInterceptor()) {
+ stub = indexed_interceptor_stub();
}
}
set_target(stub);
@@ -915,7 +918,7 @@ Object* KeyedLoadIC::Load(State state,
void KeyedLoadIC::UpdateCaches(LookupResult* lookup, State state,
Handle<Object> object, Handle<String> name) {
// Bail out if we didn't find a result.
- if (!lookup->IsValid() || !lookup->IsCacheable()) return;
+ if (!lookup->IsProperty() || !lookup->IsCacheable()) return;
if (!object->IsJSObject()) return;
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
@@ -988,7 +991,7 @@ void KeyedLoadIC::UpdateCaches(LookupResult* lookup, State state,
static bool StoreICableLookup(LookupResult* lookup) {
// Bail out if we didn't find a result.
- if (!lookup->IsValid() || !lookup->IsCacheable()) return false;
+ if (!lookup->IsPropertyOrTransition() || !lookup->IsCacheable()) return false;
// If the property is read-only, we leave the IC in its current
// state.
@@ -1203,7 +1206,7 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup,
if (receiver->IsJSGlobalProxy()) return;
// Bail out if we didn't find a result.
- if (!lookup->IsValid() || !lookup->IsCacheable()) return;
+ if (!lookup->IsPropertyOrTransition() || !lookup->IsCacheable()) return;
// If the property is read-only, we leave the IC in its current
// state.
@@ -1311,16 +1314,6 @@ Object* LoadIC_Miss(Arguments args) {
}
-void LoadIC::GenerateInitialize(MacroAssembler* masm) {
- Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss)));
-}
-
-
-void LoadIC::GeneratePreMonomorphic(MacroAssembler* masm) {
- Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss)));
-}
-
-
// Used from ic_<arch>.cc
Object* KeyedLoadIC_Miss(Arguments args) {
NoHandleAllocation na;
@@ -1331,16 +1324,6 @@ Object* KeyedLoadIC_Miss(Arguments args) {
}
-void KeyedLoadIC::GenerateInitialize(MacroAssembler* masm) {
- Generate(masm, ExternalReference(IC_Utility(kKeyedLoadIC_Miss)));
-}
-
-
-void KeyedLoadIC::GeneratePreMonomorphic(MacroAssembler* masm) {
- Generate(masm, ExternalReference(IC_Utility(kKeyedLoadIC_Miss)));
-}
-
-
// Used from ic_<arch>.cc.
Object* StoreIC_Miss(Arguments args) {
NoHandleAllocation na;
@@ -1397,16 +1380,6 @@ Object* KeyedStoreIC_Miss(Arguments args) {
}
-void KeyedStoreIC::GenerateInitialize(MacroAssembler* masm) {
- Generate(masm, ExternalReference(IC_Utility(kKeyedStoreIC_Miss)));
-}
-
-
-void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
- Generate(masm, ExternalReference(IC_Utility(kKeyedStoreIC_Miss)));
-}
-
-
static Address IC_utilities[] = {
#define ADDR(name) FUNCTION_ADDR(name),
IC_UTIL_LIST(ADDR)
diff --git a/src/ic.h b/src/ic.h
index a991e30a..feff8c5b 100644
--- a/src/ic.h
+++ b/src/ic.h
@@ -53,6 +53,7 @@ enum DictionaryCheck { CHECK_DICTIONARY, DICTIONARY_CHECK_DONE };
ICU(LoadPropertyWithInterceptorOnly) \
ICU(LoadPropertyWithInterceptorForLoad) \
ICU(LoadPropertyWithInterceptorForCall) \
+ ICU(KeyedLoadPropertyWithInterceptor) \
ICU(StoreInterceptorProperty)
//
@@ -223,8 +224,10 @@ class LoadIC: public IC {
Object* Load(State state, Handle<Object> object, Handle<String> name);
// Code generator routines.
- static void GenerateInitialize(MacroAssembler* masm);
- static void GeneratePreMonomorphic(MacroAssembler* masm);
+ static void GenerateInitialize(MacroAssembler* masm) { GenerateMiss(masm); }
+ static void GeneratePreMonomorphic(MacroAssembler* masm) {
+ GenerateMiss(masm);
+ }
static void GenerateMiss(MacroAssembler* masm);
static void GenerateMegamorphic(MacroAssembler* masm);
static void GenerateNormal(MacroAssembler* masm);
@@ -240,8 +243,6 @@ class LoadIC: public IC {
static const int kOffsetToLoadInstruction;
private:
- static void Generate(MacroAssembler* masm, const ExternalReference& f);
-
// Update the inline cache and the global stub cache based on the
// lookup result.
void UpdateCaches(LookupResult* lookup,
@@ -279,8 +280,11 @@ class KeyedLoadIC: public IC {
// Code generator routines.
static void GenerateMiss(MacroAssembler* masm);
- static void GenerateInitialize(MacroAssembler* masm);
- static void GeneratePreMonomorphic(MacroAssembler* masm);
+ static void GenerateRuntimeGetProperty(MacroAssembler* masm);
+ static void GenerateInitialize(MacroAssembler* masm) { GenerateMiss(masm); }
+ static void GeneratePreMonomorphic(MacroAssembler* masm) {
+ GenerateMiss(masm);
+ }
static void GenerateGeneric(MacroAssembler* masm);
static void GenerateString(MacroAssembler* masm);
@@ -290,6 +294,7 @@ class KeyedLoadIC: public IC {
// for all other types.
static void GenerateExternalArray(MacroAssembler* masm,
ExternalArrayType array_type);
+ static void GenerateIndexedInterceptor(MacroAssembler* masm);
// Clear the use of the inlined version.
static void ClearInlinedVersion(Address address);
@@ -302,8 +307,6 @@ class KeyedLoadIC: public IC {
static const int kSlowCaseBitFieldMask =
(1 << Map::kIsAccessCheckNeeded) | (1 << Map::kHasIndexedInterceptor);
- static void Generate(MacroAssembler* masm, const ExternalReference& f);
-
// Update the inline cache.
void UpdateCaches(LookupResult* lookup,
State state,
@@ -328,6 +331,10 @@ class KeyedLoadIC: public IC {
}
static Code* external_array_stub(JSObject::ElementsKind elements_kind);
+ static Code* indexed_interceptor_stub() {
+ return Builtins::builtin(Builtins::KeyedLoadIC_IndexedInterceptor);
+ }
+
static void Clear(Address address, Code* target);
// Support for patching the map that is checked in an inlined
@@ -351,7 +358,6 @@ class StoreIC: public IC {
static void GenerateInitialize(MacroAssembler* masm) { GenerateMiss(masm); }
static void GenerateMiss(MacroAssembler* masm);
static void GenerateMegamorphic(MacroAssembler* masm);
- static void GenerateExtendStorage(MacroAssembler* masm);
private:
// Update the inline cache and the global stub cache based on the
@@ -384,10 +390,10 @@ class KeyedStoreIC: public IC {
Handle<Object> value);
// Code generators for stub routines. Only called once at startup.
- static void GenerateInitialize(MacroAssembler* masm);
+ static void GenerateInitialize(MacroAssembler* masm) { GenerateMiss(masm); }
static void GenerateMiss(MacroAssembler* masm);
+ static void GenerateRuntimeSetProperty(MacroAssembler* masm);
static void GenerateGeneric(MacroAssembler* masm);
- static void GenerateExtendStorage(MacroAssembler* masm);
// Generators for external array types. See objects.h.
// These are similar to the generic IC; they optimize the case of
@@ -403,8 +409,6 @@ class KeyedStoreIC: public IC {
static void RestoreInlinedVersion(Address address);
private:
- static void Generate(MacroAssembler* masm, const ExternalReference& f);
-
// Update the inline cache.
void UpdateCaches(LookupResult* lookup,
State state,
diff --git a/src/json-delay.js b/src/json-delay.js
deleted file mode 100644
index 7788f516..00000000
--- a/src/json-delay.js
+++ /dev/null
@@ -1,254 +0,0 @@
-// 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.
-
-var $JSON = global.JSON;
-
-function ParseJSONUnfiltered(text) {
- var s = $String(text);
- var f = %CompileString(text, true);
- return f();
-}
-
-function Revive(holder, name, reviver) {
- var val = holder[name];
- if (IS_OBJECT(val)) {
- if (IS_ARRAY(val)) {
- var length = val.length;
- for (var i = 0; i < length; i++) {
- var newElement = Revive(val, $String(i), reviver);
- val[i] = newElement;
- }
- } else {
- for (var p in val) {
- if (ObjectHasOwnProperty.call(val, p)) {
- var newElement = Revive(val, p, reviver);
- if (IS_UNDEFINED(newElement)) {
- delete val[p];
- } else {
- val[p] = newElement;
- }
- }
- }
- }
- }
- return reviver.call(holder, name, val);
-}
-
-function JSONParse(text, reviver) {
- var unfiltered = ParseJSONUnfiltered(text);
- if (IS_FUNCTION(reviver)) {
- return Revive({'': unfiltered}, '', reviver);
- } else {
- return unfiltered;
- }
-}
-
-var characterQuoteCache = {
- '\"': '\\"',
- '\\': '\\\\',
- '/': '\\/',
- '\b': '\\b',
- '\f': '\\f',
- '\n': '\\n',
- '\r': '\\r',
- '\t': '\\t',
- '\x0B': '\\u000b'
-};
-
-function QuoteSingleJSONCharacter(c) {
- if (c in characterQuoteCache)
- return characterQuoteCache[c];
- var charCode = c.charCodeAt(0);
- var result;
- if (charCode < 16) result = '\\u000';
- else if (charCode < 256) result = '\\u00';
- else if (charCode < 4096) result = '\\u0';
- else result = '\\u';
- result += charCode.toString(16);
- characterQuoteCache[c] = result;
- return result;
-}
-
-function QuoteJSONString(str) {
- var quotable = /[\\\"\x00-\x1f\x80-\uffff]/g;
- return '"' + str.replace(quotable, QuoteSingleJSONCharacter) + '"';
-}
-
-function StackContains(stack, val) {
- var length = stack.length;
- for (var i = 0; i < length; i++) {
- if (stack[i] === val)
- return true;
- }
- return false;
-}
-
-function SerializeArray(value, replacer, stack, indent, gap) {
- if (StackContains(stack, value))
- throw MakeTypeError('circular_structure', []);
- stack.push(value);
- var stepback = indent;
- indent += gap;
- var partial = [];
- var len = value.length;
- for (var i = 0; i < len; i++) {
- var strP = JSONSerialize($String(i), value, replacer, stack,
- indent, gap);
- if (IS_UNDEFINED(strP))
- strP = "null";
- partial.push(strP);
- }
- var final;
- if (gap == "") {
- final = "[" + partial.join(",") + "]";
- } else if (partial.length > 0) {
- var separator = ",\n" + indent;
- final = "[\n" + indent + partial.join(separator) + "\n" +
- stepback + "]";
- } else {
- final = "[]";
- }
- stack.pop();
- return final;
-}
-
-function SerializeObject(value, replacer, stack, indent, gap) {
- if (StackContains(stack, value))
- throw MakeTypeError('circular_structure', []);
- stack.push(value);
- var stepback = indent;
- indent += gap;
- var partial = [];
- if (IS_ARRAY(replacer)) {
- var length = replacer.length;
- for (var i = 0; i < length; i++) {
- if (ObjectHasOwnProperty.call(replacer, i)) {
- var p = replacer[i];
- var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
- if (!IS_UNDEFINED(strP)) {
- var member = QuoteJSONString(p) + ":";
- if (gap != "") member += " ";
- member += strP;
- partial.push(member);
- }
- }
- }
- } else {
- for (var p in value) {
- if (ObjectHasOwnProperty.call(value, p)) {
- var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
- if (!IS_UNDEFINED(strP)) {
- var member = QuoteJSONString(p) + ":";
- if (gap != "") member += " ";
- member += strP;
- partial.push(member);
- }
- }
- }
- }
- var final;
- if (gap == "") {
- final = "{" + partial.join(",") + "}";
- } else if (partial.length > 0) {
- var separator = ",\n" + indent;
- final = "{\n" + indent + partial.join(separator) + "\n" +
- stepback + "}";
- } else {
- final = "{}";
- }
- stack.pop();
- return final;
-}
-
-function JSONSerialize(key, holder, replacer, stack, indent, gap) {
- var value = holder[key];
- if (IS_OBJECT(value) && value) {
- var toJSON = value.toJSON;
- if (IS_FUNCTION(toJSON))
- value = toJSON.call(value, key);
- }
- if (IS_FUNCTION(replacer))
- value = replacer.call(holder, key, value);
- // Unwrap value if necessary
- if (IS_OBJECT(value)) {
- if (IS_NUMBER_WRAPPER(value)) {
- value = $Number(value);
- } else if (IS_STRING_WRAPPER(value)) {
- value = $String(value);
- }
- }
- switch (typeof value) {
- case "string":
- return QuoteJSONString(value);
- case "object":
- if (!value) {
- return "null";
- } else if (IS_ARRAY(value)) {
- return SerializeArray(value, replacer, stack, indent, gap);
- } else {
- return SerializeObject(value, replacer, stack, indent, gap);
- }
- case "number":
- return $isFinite(value) ? $String(value) : "null";
- case "boolean":
- return value ? "true" : "false";
- }
-}
-
-function JSONStringify(value, replacer, space) {
- var stack = [];
- var indent = "";
- if (IS_OBJECT(space)) {
- // Unwrap 'space' if it is wrapped
- if (IS_NUMBER_WRAPPER(space)) {
- space = $Number(space);
- } else if (IS_STRING_WRAPPER(space)) {
- space = $String(space);
- }
- }
- var gap;
- if (IS_NUMBER(space)) {
- space = $Math.min(space, 100);
- gap = "";
- for (var i = 0; i < space; i++)
- gap += " ";
- } else if (IS_STRING(space)) {
- gap = space;
- } else {
- gap = "";
- }
- return JSONSerialize('', {'': value}, replacer, stack, indent, gap);
-}
-
-function SetupJSON() {
- InstallFunctions($JSON, DONT_ENUM, $Array(
- "parse", JSONParse,
- "stringify", JSONStringify
- ));
-}
-
-SetupJSON();
diff --git a/src/jump-target-inl.h b/src/jump-target-inl.h
index 1f0676df..3cd9a8bd 100644
--- a/src/jump-target-inl.h
+++ b/src/jump-target-inl.h
@@ -42,6 +42,9 @@ void JumpTarget::InitializeEntryElement(int index, FrameElement* target) {
} else if (target->is_copy()) {
entry_frame_->elements_[target->index()].set_copied();
}
+ if (direction_ == BIDIRECTIONAL && !target->is_copy()) {
+ entry_frame_->elements_[index].set_number_info(NumberInfo::kUnknown);
+ }
}
} } // namespace v8::internal
diff --git a/src/jump-target.cc b/src/jump-target.cc
index 3782f92a..bce379a9 100644
--- a/src/jump-target.cc
+++ b/src/jump-target.cc
@@ -101,6 +101,16 @@ void JumpTarget::ComputeEntryFrame() {
if (element == NULL || !element->is_valid()) break;
element = element->Combine(&reaching_frames_[j]->elements_[i]);
+
+ FrameElement* other = &reaching_frames_[j]->elements_[i];
+ if (element != NULL && !element->is_copy()) {
+ ASSERT(other != NULL);
+ // We overwrite the number information of one of the incoming frames.
+ // This is safe because we only use the frame for emitting merge code.
+ // The number information of incoming frames is not used anymore.
+ element->set_number_info(NumberInfo::Combine(element->number_info(),
+ other->number_info()));
+ }
}
elements[i] = element;
}
@@ -125,7 +135,8 @@ void JumpTarget::ComputeEntryFrame() {
for (; index < length; index++) {
FrameElement* target = elements[index];
if (target == NULL) {
- entry_frame_->elements_.Add(FrameElement::MemoryElement());
+ entry_frame_->elements_.Add(
+ FrameElement::MemoryElement(NumberInfo::kUninitialized));
} else {
entry_frame_->elements_.Add(*target);
InitializeEntryElement(index, target);
@@ -142,9 +153,20 @@ void JumpTarget::ComputeEntryFrame() {
RegisterFile candidate_registers;
int best_count = kMinInt;
int best_reg_num = RegisterAllocator::kInvalidRegister;
+ NumberInfo::Type info = NumberInfo::kUninitialized;
for (int j = 0; j < reaching_frames_.length(); j++) {
FrameElement element = reaching_frames_[j]->elements_[i];
+ if (direction_ == BIDIRECTIONAL) {
+ info = NumberInfo::kUnknown;
+ } else if (!element.is_copy()) {
+ info = NumberInfo::Combine(info, element.number_info());
+ } else {
+ // New elements will not be copies, so get number information from
+ // backing element in the reaching frame.
+ info = NumberInfo::Combine(info,
+ reaching_frames_[j]->elements_[element.index()].number_info());
+ }
is_synced = is_synced && element.is_synced();
if (element.is_register() && !entry_frame_->is_used(element.reg())) {
// Count the register occurrence and remember it if better
@@ -158,11 +180,17 @@ void JumpTarget::ComputeEntryFrame() {
}
}
+ // We must have a number type information now (not for copied elements).
+ ASSERT(entry_frame_->elements_[i].is_copy()
+ || info != NumberInfo::kUninitialized);
+
// If the value is synced on all frames, put it in memory. This
// costs nothing at the merge code but will incur a
// memory-to-register move when the value is needed later.
if (is_synced) {
// Already recorded as a memory element.
+ // Set combined number info.
+ entry_frame_->elements_[i].set_number_info(info);
continue;
}
@@ -183,13 +211,27 @@ void JumpTarget::ComputeEntryFrame() {
bool is_copied = entry_frame_->elements_[i].is_copied();
Register reg = RegisterAllocator::ToRegister(best_reg_num);
entry_frame_->elements_[i] =
- FrameElement::RegisterElement(reg,
- FrameElement::NOT_SYNCED);
+ FrameElement::RegisterElement(reg, FrameElement::NOT_SYNCED,
+ NumberInfo::kUninitialized);
if (is_copied) entry_frame_->elements_[i].set_copied();
entry_frame_->set_register_location(reg, i);
}
+ // Set combined number info.
+ entry_frame_->elements_[i].set_number_info(info);
+ }
+ }
+
+ // If we have incoming backward edges assert we forget all number information.
+#ifdef DEBUG
+ if (direction_ == BIDIRECTIONAL) {
+ for (int i = 0; i < length; ++i) {
+ if (!entry_frame_->elements_[i].is_copy()) {
+ ASSERT(entry_frame_->elements_[i].number_info() ==
+ NumberInfo::kUnknown);
+ }
}
}
+#endif
// The stack pointer is at the highest synced element or the base of
// the expression stack.
diff --git a/src/liveedit.cc b/src/liveedit.cc
new file mode 100644
index 00000000..c50e007f
--- /dev/null
+++ b/src/liveedit.cc
@@ -0,0 +1,87 @@
+// Copyright 2010 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.
+
+
+#include "v8.h"
+
+#include "liveedit.h"
+#include "compiler.h"
+#include "oprofile-agent.h"
+#include "scopes.h"
+#include "global-handles.h"
+#include "debug.h"
+
+namespace v8 {
+namespace internal {
+
+
+class FunctionInfoListener {
+ public:
+ void FunctionStarted(FunctionLiteral* fun) {
+ // Implementation follows.
+ }
+
+ void FunctionDone() {
+ // Implementation follows.
+ }
+
+ void FunctionScope(Scope* scope) {
+ // Implementation follows.
+ }
+
+ void FunctionCode(Handle<Code> function_code) {
+ // Implementation follows.
+ }
+};
+
+static FunctionInfoListener* active_function_info_listener = NULL;
+
+LiveEditFunctionTracker::LiveEditFunctionTracker(FunctionLiteral* fun) {
+ if (active_function_info_listener != NULL) {
+ active_function_info_listener->FunctionStarted(fun);
+ }
+}
+LiveEditFunctionTracker::~LiveEditFunctionTracker() {
+ if (active_function_info_listener != NULL) {
+ active_function_info_listener->FunctionDone();
+ }
+}
+void LiveEditFunctionTracker::RecordFunctionCode(Handle<Code> code) {
+ if (active_function_info_listener != NULL) {
+ active_function_info_listener->FunctionCode(code);
+ }
+}
+void LiveEditFunctionTracker::RecordFunctionScope(Scope* scope) {
+ if (active_function_info_listener != NULL) {
+ active_function_info_listener->FunctionScope(scope);
+ }
+}
+bool LiveEditFunctionTracker::IsActive() {
+ return active_function_info_listener != NULL;
+}
+
+} } // namespace v8::internal
diff --git a/src/liveedit.h b/src/liveedit.h
new file mode 100644
index 00000000..73aa7d3d
--- /dev/null
+++ b/src/liveedit.h
@@ -0,0 +1,78 @@
+// Copyright 2010 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.
+
+#ifndef V8_LIVEEDIT_H_
+#define V8_LIVEEDIT_H_
+
+
+
+// Live Edit feature implementation.
+// User should be able to change script on already running VM. This feature
+// matches hot swap features in other frameworks.
+//
+// The basic use-case is when user spots some mistake in function body
+// from debugger and wishes to change the algorithm without restart.
+//
+// A single change always has a form of a simple replacement (in pseudo-code):
+// script.source[positions, positions+length] = new_string;
+// Implementation first determines, which function's body includes this
+// change area. Then both old and new versions of script are fully compiled
+// in order to analyze, whether the function changed its outer scope
+// expectations (or number of parameters). If it didn't, function's code is
+// patched with a newly compiled code. If it did change, enclosing function
+// gets patched. All inner functions are left untouched, whatever happened
+// to them in a new script version. However, new version of code will
+// instantiate newly compiled functions.
+
+
+#include "compiler.h"
+
+namespace v8 {
+namespace internal {
+
+// This class collects some specific information on structure of functions
+// in a particular script. It gets called from compiler all the time, but
+// actually records any data only when liveedit operation is in process;
+// in any other time this class is very cheap.
+//
+// The primary interest of the Tracker is to record function scope structures
+// in order to analyze whether function code maybe safely patched (with new
+// code successfully reading existing data from function scopes). The Tracker
+// also collects compiled function codes.
+class LiveEditFunctionTracker {
+ public:
+ explicit LiveEditFunctionTracker(FunctionLiteral* fun);
+ ~LiveEditFunctionTracker();
+ void RecordFunctionCode(Handle<Code> code);
+ void RecordFunctionScope(Scope* scope);
+
+ static bool IsActive();
+};
+
+} } // namespace v8::internal
+
+#endif /* V*_LIVEEDIT_H_ */
diff --git a/src/log-utils.cc b/src/log-utils.cc
index fd956041..722e0fc0 100644
--- a/src/log-utils.cc
+++ b/src/log-utils.cc
@@ -351,15 +351,6 @@ void LogMessageBuilder::WriteToLogFile() {
}
-void LogMessageBuilder::WriteCStringToLogFile(const char* str) {
- const int len = StrLength(str);
- const int written = Log::Write(str, len);
- if (written != len && write_failure_handler != NULL) {
- write_failure_handler();
- }
-}
-
-
// Formatting string for back references to the whole line. E.g. "#2" means
// "the second line above".
const char* LogRecordCompressor::kLineBackwardReferenceFormat = "#%d";
diff --git a/src/log-utils.h b/src/log-utils.h
index 3e25b0e7..b769e904 100644
--- a/src/log-utils.h
+++ b/src/log-utils.h
@@ -268,9 +268,6 @@ class LogMessageBuilder BASE_EMBEDDED {
// Write the log message to the log file currently opened.
void WriteToLogFile();
- // Write a null-terminated string to to the log file currently opened.
- void WriteCStringToLogFile(const char* str);
-
// A handler that is called when Log::Write fails.
typedef void (*WriteFailureHandler)();
diff --git a/src/log.cc b/src/log.cc
index cf2fc97c..a3fef731 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -330,6 +330,8 @@ SlidingStateWindow* Logger::sliding_state_window_ = NULL;
const char** Logger::log_events_ = NULL;
CompressionHelper* Logger::compression_helper_ = NULL;
bool Logger::is_logging_ = false;
+int Logger::cpu_profiler_nesting_ = 0;
+int Logger::heap_profiler_nesting_ = 0;
#define DECLARE_LONG_EVENT(ignore1, long_name, ignore2) long_name,
const char* kLongLogEventsNames[Logger::NUMBER_OF_LOG_EVENTS] = {
@@ -368,15 +370,6 @@ void Logger::LogAliases() {
#endif // ENABLE_LOGGING_AND_PROFILING
-void Logger::Preamble(const char* content) {
-#ifdef ENABLE_LOGGING_AND_PROFILING
- if (!Log::IsEnabled() || !FLAG_log_code) return;
- LogMessageBuilder msg;
- msg.WriteCStringToLogFile(content);
-#endif
-}
-
-
void Logger::StringEvent(const char* name, const char* value) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (FLAG_log) UncheckedStringEvent(name, value);
@@ -573,20 +566,6 @@ void Logger::LogRuntime(Vector<const char> format, JSArray* args) {
}
-void Logger::LogProfileMarker(Vector<const char> marker) {
-#ifdef ENABLE_LOGGING_AND_PROFILING
- if (!Log::IsEnabled() || !FLAG_prof) return;
- LogMessageBuilder msg;
- for (int i = 0; i < marker.length(); i++) {
- char c = marker[i];
- msg.Append(c);
- }
- msg.Append('\n');
- msg.WriteToLogFile();
-#endif
-}
-
-
void Logger::ApiIndexedSecurityCheck(uint32_t index) {
#ifdef ENABLE_LOGGING_AND_PROFILING
if (!Log::IsEnabled() || !FLAG_log_api) return;
@@ -1178,53 +1157,61 @@ int Logger::GetActiveProfilerModules() {
}
-void Logger::PauseProfiler(int flags) {
+void Logger::PauseProfiler(int flags, int tag) {
if (!Log::IsEnabled()) return;
- const int active_modules = GetActiveProfilerModules();
- const int modules_to_disable = active_modules & flags;
- if (modules_to_disable == PROFILER_MODULE_NONE) return;
-
- if (modules_to_disable & PROFILER_MODULE_CPU) {
- profiler_->pause();
- if (FLAG_prof_lazy) {
- if (!FLAG_sliding_state_window) ticker_->Stop();
- FLAG_log_code = false;
- // Must be the same message as Log::kDynamicBufferSeal.
- LOG(UncheckedStringEvent("profiler", "pause"));
+ if (flags & PROFILER_MODULE_CPU) {
+ // It is OK to have negative nesting.
+ if (--cpu_profiler_nesting_ == 0) {
+ profiler_->pause();
+ if (FLAG_prof_lazy) {
+ if (!FLAG_sliding_state_window) ticker_->Stop();
+ FLAG_log_code = false;
+ // Must be the same message as Log::kDynamicBufferSeal.
+ LOG(UncheckedStringEvent("profiler", "pause"));
+ }
}
}
- if (modules_to_disable &
+ if (flags &
(PROFILER_MODULE_HEAP_STATS | PROFILER_MODULE_JS_CONSTRUCTORS)) {
- FLAG_log_gc = false;
+ if (--heap_profiler_nesting_ == 0) {
+ FLAG_log_gc = false;
+ }
+ }
+ if (tag != 0) {
+ IntEvent("close-tag", tag);
}
- // Turn off logging if no active modules remain.
- if ((active_modules & ~flags) == PROFILER_MODULE_NONE) {
+ if (GetActiveProfilerModules() == PROFILER_MODULE_NONE) {
is_logging_ = false;
}
}
-void Logger::ResumeProfiler(int flags) {
+void Logger::ResumeProfiler(int flags, int tag) {
if (!Log::IsEnabled()) return;
- const int modules_to_enable = ~GetActiveProfilerModules() & flags;
- if (modules_to_enable != PROFILER_MODULE_NONE) {
- is_logging_ = true;
+ if (tag != 0) {
+ IntEvent("open-tag", tag);
}
- if (modules_to_enable & PROFILER_MODULE_CPU) {
- if (FLAG_prof_lazy) {
- profiler_->Engage();
- LOG(UncheckedStringEvent("profiler", "resume"));
- FLAG_log_code = true;
- LogCompiledFunctions();
- LogFunctionObjects();
- LogAccessorCallbacks();
- if (!FLAG_sliding_state_window) ticker_->Start();
+ if (flags & PROFILER_MODULE_CPU) {
+ if (cpu_profiler_nesting_++ == 0) {
+ is_logging_ = true;
+ if (FLAG_prof_lazy) {
+ profiler_->Engage();
+ LOG(UncheckedStringEvent("profiler", "resume"));
+ FLAG_log_code = true;
+ LogCompiledFunctions();
+ LogFunctionObjects();
+ LogAccessorCallbacks();
+ if (!FLAG_sliding_state_window) ticker_->Start();
+ }
+ profiler_->resume();
}
- profiler_->resume();
}
- if (modules_to_enable &
+ if (flags &
(PROFILER_MODULE_HEAP_STATS | PROFILER_MODULE_JS_CONSTRUCTORS)) {
- FLAG_log_gc = true;
+ if (heap_profiler_nesting_++ == 0) {
+ is_logging_ = true;
+ FLAG_log_gc = true;
+ }
}
}
@@ -1233,7 +1220,7 @@ void Logger::ResumeProfiler(int flags) {
// either from main or Profiler's thread.
void Logger::StopLoggingAndProfiling() {
Log::stop();
- PauseProfiler(PROFILER_MODULE_CPU);
+ PauseProfiler(PROFILER_MODULE_CPU, 0);
}
diff --git a/src/log.h b/src/log.h
index ae1e4be5..eb8369cf 100644
--- a/src/log.h
+++ b/src/log.h
@@ -161,12 +161,6 @@ class Logger {
// Enable the computation of a sliding window of states.
static void EnableSlidingStateWindow();
- // Write a raw string to the log to be used as a preamble.
- // No check is made that the 'preamble' is actually at the beginning
- // of the log. The preample is used to write code events saved in the
- // snapshot.
- static void Preamble(const char* content);
-
// Emits an event with a string value -> (name, value).
static void StringEvent(const char* name, const char* value);
@@ -265,9 +259,6 @@ class Logger {
// Log an event reported from generated code
static void LogRuntime(Vector<const char> format, JSArray* args);
- // Log a profiling marker.
- static void LogProfileMarker(Vector<const char> marker);
-
#ifdef ENABLE_LOGGING_AND_PROFILING
static StateTag state() {
return current_state_ ? current_state_->state() : OTHER;
@@ -280,8 +271,8 @@ class Logger {
// Pause/Resume collection of profiling data.
// When data collection is paused, CPU Tick events are discarded until
// data collection is Resumed.
- static void PauseProfiler(int flags);
- static void ResumeProfiler(int flags);
+ static void PauseProfiler(int flags, int tag);
+ static void ResumeProfiler(int flags, int tag);
static int GetActiveProfilerModules();
// If logging is performed into a memory buffer, allows to
@@ -382,6 +373,8 @@ class Logger {
friend class LoggerTestHelper;
static bool is_logging_;
+ static int cpu_profiler_nesting_;
+ static int heap_profiler_nesting_;
#else
static bool is_logging() { return false; }
#endif
diff --git a/src/macro-assembler.h b/src/macro-assembler.h
index e33148ce..81e5bf7a 100644
--- a/src/macro-assembler.h
+++ b/src/macro-assembler.h
@@ -61,6 +61,8 @@ enum AllocationFlags {
RESULT_CONTAINS_TOP = 1 << 1
};
+// Invalid depth in prototype chain.
+const int kInvalidProtoDepth = -1;
#if V8_TARGET_ARCH_IA32
#include "assembler.h"
diff --git a/src/macros.py b/src/macros.py
index 537113ce..47519338 100644
--- a/src/macros.py
+++ b/src/macros.py
@@ -74,6 +74,10 @@ const kYearShift = 9;
const kMonthShift = 5;
# Type query macros.
+#
+# Note: We have special support for typeof(foo) === 'bar' in the compiler.
+# It will *not* generate a runtime typeof call for the most important
+# values of 'bar'.
macro IS_NULL(arg) = (arg === null);
macro IS_NULL_OR_UNDEFINED(arg) = (arg == null);
macro IS_UNDEFINED(arg) = (typeof(arg) === 'undefined');
@@ -83,7 +87,7 @@ macro IS_BOOLEAN(arg) = (typeof(arg) === 'boolean');
macro IS_OBJECT(arg) = (%_IsObject(arg));
macro IS_ARRAY(arg) = (%_IsArray(arg));
macro IS_FUNCTION(arg) = (%_IsFunction(arg));
-macro IS_REGEXP(arg) = (%_ClassOf(arg) === 'RegExp');
+macro IS_REGEXP(arg) = (%_IsRegExp(arg));
macro IS_DATE(arg) = (%_ClassOf(arg) === 'Date');
macro IS_NUMBER_WRAPPER(arg) = (%_ClassOf(arg) === 'Number');
macro IS_STRING_WRAPPER(arg) = (%_ClassOf(arg) === 'String');
@@ -97,9 +101,11 @@ macro FLOOR(arg) = $floor(arg);
# Inline macros. Use %IS_VAR to make sure arg is evaluated only once.
macro NUMBER_IS_NAN(arg) = (!%_IsSmi(%IS_VAR(arg)) && !(arg == arg));
-macro TO_INTEGER(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : ToInteger(arg));
-macro TO_INT32(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : (arg >> 0));
-macro TO_UINT32(arg) = (arg >>> 0);
+macro TO_INTEGER(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : ToInteger(arg));
+macro TO_INT32(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : (arg >> 0));
+macro TO_UINT32(arg) = (arg >>> 0);
+macro TO_STRING_INLINE(arg) = (IS_STRING(%IS_VAR(arg)) ? arg : NonStringToString(arg));
+
# Macros implemented in Python.
python macro CHAR_CODE(str) = ord(str[1]);
diff --git a/src/math.js b/src/math.js
index d804648f..4c9de674 100644
--- a/src/math.js
+++ b/src/math.js
@@ -84,7 +84,7 @@ function MathCeil(x) {
// ECMA 262 - 15.8.2.7
function MathCos(x) {
if (!IS_NUMBER(x)) x = ToNumber(x);
- return %Math_cos(x);
+ return %_Math_cos(x);
}
// ECMA 262 - 15.8.2.8
@@ -176,7 +176,7 @@ function MathRound(x) {
// ECMA 262 - 15.8.2.16
function MathSin(x) {
if (!IS_NUMBER(x)) x = ToNumber(x);
- return %Math_sin(x);
+ return %_Math_sin(x);
}
// ECMA 262 - 15.8.2.17
@@ -233,7 +233,7 @@ function SetupMath() {
"SQRT2",
1.4142135623730951,
DONT_ENUM | DONT_DELETE | READ_ONLY);
- %TransformToFastProperties($Math);
+ %ToFastProperties($Math);
// Setup non-enumerable functions of the Math object and
// set their names.
diff --git a/src/mips/codegen-mips.cc b/src/mips/codegen-mips.cc
index 5a27c286..2de45f67 100644
--- a/src/mips/codegen-mips.cc
+++ b/src/mips/codegen-mips.cc
@@ -305,6 +305,11 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
}
+void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
+ UNIMPLEMENTED_MIPS();
+}
+
+
void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
UNIMPLEMENTED_MIPS();
}
@@ -365,6 +370,11 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
}
+void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
+ UNIMPLEMENTED_MIPS();
+}
+
+
void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
UNIMPLEMENTED_MIPS();
}
@@ -498,4 +508,3 @@ int CompareStub::MinorKey() {
#undef __
} } // namespace v8::internal
-
diff --git a/src/mips/codegen-mips.h b/src/mips/codegen-mips.h
index 05138bc6..147b8724 100644
--- a/src/mips/codegen-mips.h
+++ b/src/mips/codegen-mips.h
@@ -210,6 +210,7 @@ class CodeGenerator: public AstVisitor {
void GenerateIsSmi(ZoneList<Expression*>* args);
void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
void GenerateIsArray(ZoneList<Expression*>* args);
+ void GenerateIsRegExp(ZoneList<Expression*>* args);
// Support for construct call checks.
void GenerateIsConstructCall(ZoneList<Expression*>* args);
@@ -241,6 +242,8 @@ class CodeGenerator: public AstVisitor {
void GenerateSubString(ZoneList<Expression*>* args);
void GenerateStringCompare(ZoneList<Expression*>* args);
void GenerateRegExpExec(ZoneList<Expression*>* args);
+ void GenerateNumberToString(ZoneList<Expression*>* args);
+
// Fast support for Math.sin and Math.cos.
inline void GenerateMathSin(ZoneList<Expression*>* args);
@@ -308,4 +311,3 @@ class CodeGenerator: public AstVisitor {
} } // namespace v8::internal
#endif // V8_MIPS_CODEGEN_MIPS_H_
-
diff --git a/src/mirror-debugger.js b/src/mirror-debugger.js
index 3b34797e..dfe297b1 100644
--- a/src/mirror-debugger.js
+++ b/src/mirror-debugger.js
@@ -546,14 +546,16 @@ StringMirror.prototype.length = function() {
return this.value_.length;
};
-
-StringMirror.prototype.toText = function() {
- if (this.length() > kMaxProtocolStringLength) {
- return this.value_.substring(0, kMaxProtocolStringLength) +
+StringMirror.prototype.getTruncatedValue = function(maxLength) {
+ if (maxLength != -1 && this.length() > maxLength) {
+ return this.value_.substring(0, maxLength) +
'... (length: ' + this.length() + ')';
- } else {
- return this.value_;
}
+ return this.value_;
+}
+
+StringMirror.prototype.toText = function() {
+ return this.getTruncatedValue(kMaxProtocolStringLength);
}
@@ -1726,7 +1728,8 @@ ScriptMirror.prototype.value = function() {
ScriptMirror.prototype.name = function() {
- return this.script_.name;
+ // If we have name, we trust it more than sourceURL from comments
+ return this.script_.name || this.sourceUrlFromComment_();
};
@@ -1822,6 +1825,29 @@ ScriptMirror.prototype.toText = function() {
/**
+ * Returns a suggested script URL from comments in script code (if found),
+ * undefined otherwise. Used primarily by debuggers for identifying eval()'ed
+ * scripts. See
+ * http://fbug.googlecode.com/svn/branches/firebug1.1/docs/ReleaseNotes_1.1.txt
+ * for details.
+ *
+ * @return {?string} value for //@ sourceURL comment
+ */
+ScriptMirror.prototype.sourceUrlFromComment_ = function() {
+ if (!('sourceUrl_' in this) && this.source()) {
+ // TODO(608): the spaces in a regexp below had to be escaped as \040
+ // because this file is being processed by js2c whose handling of spaces
+ // in regexps is broken.
+ // We're not using \s here to prevent \n from matching.
+ var sourceUrlPattern = /\/\/@[\040\t]sourceURL=[\040\t]*(\S+)[\040\t]*$/m;
+ var match = sourceUrlPattern.exec(this.source());
+ this.sourceUrl_ = match ? match[1] : undefined;
+ }
+ return this.sourceUrl_;
+};
+
+
+/**
* Mirror object for context.
* @param {Object} data The context data
* @constructor
@@ -1924,6 +1950,15 @@ JSONProtocolSerializer.prototype.inlineRefs_ = function() {
}
+JSONProtocolSerializer.prototype.maxStringLength_ = function() {
+ if (IS_UNDEFINED(this.options_) ||
+ IS_UNDEFINED(this.options_.maxStringLength)) {
+ return kMaxProtocolStringLength;
+ }
+ return this.options_.maxStringLength;
+}
+
+
JSONProtocolSerializer.prototype.add_ = function(mirror) {
// If this mirror is already in the list just return.
for (var i = 0; i < this.mirrors_.length; i++) {
@@ -1956,8 +1991,7 @@ JSONProtocolSerializer.prototype.serializeReferenceWithDisplayData_ =
o.value = mirror.value();
break;
case STRING_TYPE:
- // Limit string length.
- o.value = mirror.toText();
+ o.value = mirror.getTruncatedValue(this.maxStringLength_());
break;
case FUNCTION_TYPE:
o.name = mirror.name();
@@ -2021,11 +2055,12 @@ JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference,
case STRING_TYPE:
// String values might have their value cropped to keep down size.
- if (mirror.length() > kMaxProtocolStringLength) {
- var str = mirror.value().substring(0, kMaxProtocolStringLength);
+ if (this.maxStringLength_() != -1 &&
+ mirror.length() > this.maxStringLength_()) {
+ var str = mirror.getTruncatedValue(this.maxStringLength_());
content.value = str;
content.fromIndex = 0;
- content.toIndex = kMaxProtocolStringLength;
+ content.toIndex = this.maxStringLength_();
} else {
content.value = mirror.value();
}
diff --git a/src/mirror-delay.js b/src/mirror-delay.js
deleted file mode 100644
index 1487ce57..00000000
--- a/src/mirror-delay.js
+++ /dev/null
@@ -1,2334 +0,0 @@
-// Copyright 2006-2008 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.
-
-// Touch the RegExp and Date functions to make sure that date-delay.js and
-// regexp-delay.js has been loaded. This is required as the mirrors use
-// functions within these files through the builtins object.
-RegExp;
-Date;
-
-
-// Handle id counters.
-var next_handle_ = 0;
-var next_transient_handle_ = -1;
-
-// Mirror cache.
-var mirror_cache_ = [];
-
-
-/**
- * Clear the mirror handle cache.
- */
-function ClearMirrorCache() {
- next_handle_ = 0;
- mirror_cache_ = [];
-}
-
-
-/**
- * Returns the mirror for a specified value or object.
- *
- * @param {value or Object} value the value or object to retreive the mirror for
- * @param {boolean} transient indicate whether this object is transient and
- * should not be added to the mirror cache. The default is not transient.
- * @returns {Mirror} the mirror reflects the passed value or object
- */
-function MakeMirror(value, opt_transient) {
- var mirror;
-
- // Look for non transient mirrors in the mirror cache.
- if (!opt_transient) {
- for (id in mirror_cache_) {
- mirror = mirror_cache_[id];
- if (mirror.value() === value) {
- return mirror;
- }
- // Special check for NaN as NaN == NaN is false.
- if (mirror.isNumber() && isNaN(mirror.value()) &&
- typeof value == 'number' && isNaN(value)) {
- return mirror;
- }
- }
- }
-
- if (IS_UNDEFINED(value)) {
- mirror = new UndefinedMirror();
- } else if (IS_NULL(value)) {
- mirror = new NullMirror();
- } else if (IS_BOOLEAN(value)) {
- mirror = new BooleanMirror(value);
- } else if (IS_NUMBER(value)) {
- mirror = new NumberMirror(value);
- } else if (IS_STRING(value)) {
- mirror = new StringMirror(value);
- } else if (IS_ARRAY(value)) {
- mirror = new ArrayMirror(value);
- } else if (IS_DATE(value)) {
- mirror = new DateMirror(value);
- } else if (IS_FUNCTION(value)) {
- mirror = new FunctionMirror(value);
- } else if (IS_REGEXP(value)) {
- mirror = new RegExpMirror(value);
- } else if (IS_ERROR(value)) {
- mirror = new ErrorMirror(value);
- } else if (IS_SCRIPT(value)) {
- mirror = new ScriptMirror(value);
- } else {
- mirror = new ObjectMirror(value, OBJECT_TYPE, opt_transient);
- }
-
- mirror_cache_[mirror.handle()] = mirror;
- return mirror;
-}
-
-
-/**
- * Returns the mirror for a specified mirror handle.
- *
- * @param {number} handle the handle to find the mirror for
- * @returns {Mirror or undefiend} the mirror with the requested handle or
- * undefined if no mirror with the requested handle was found
- */
-function LookupMirror(handle) {
- return mirror_cache_[handle];
-}
-
-
-/**
- * Returns the mirror for the undefined value.
- *
- * @returns {Mirror} the mirror reflects the undefined value
- */
-function GetUndefinedMirror() {
- return MakeMirror(void 0);
-}
-
-
-/**
- * Inherit the prototype methods from one constructor into another.
- *
- * The Function.prototype.inherits from lang.js rewritten as a standalone
- * function (not on Function.prototype). NOTE: If this file is to be loaded
- * during bootstrapping this function needs to be revritten using some native
- * functions as prototype setup using normal JavaScript does not work as
- * expected during bootstrapping (see mirror.js in r114903).
- *
- * @param {function} ctor Constructor function which needs to inherit the
- * prototype
- * @param {function} superCtor Constructor function to inherit prototype from
- */
-function inherits(ctor, superCtor) {
- var tempCtor = function(){};
- tempCtor.prototype = superCtor.prototype;
- ctor.super_ = superCtor.prototype;
- ctor.prototype = new tempCtor();
- ctor.prototype.constructor = ctor;
-}
-
-
-// Type names of the different mirrors.
-const UNDEFINED_TYPE = 'undefined';
-const NULL_TYPE = 'null';
-const BOOLEAN_TYPE = 'boolean';
-const NUMBER_TYPE = 'number';
-const STRING_TYPE = 'string';
-const OBJECT_TYPE = 'object';
-const FUNCTION_TYPE = 'function';
-const REGEXP_TYPE = 'regexp';
-const ERROR_TYPE = 'error';
-const PROPERTY_TYPE = 'property';
-const FRAME_TYPE = 'frame';
-const SCRIPT_TYPE = 'script';
-const CONTEXT_TYPE = 'context';
-const SCOPE_TYPE = 'scope';
-
-// Maximum length when sending strings through the JSON protocol.
-const kMaxProtocolStringLength = 80;
-
-// Different kind of properties.
-PropertyKind = {};
-PropertyKind.Named = 1;
-PropertyKind.Indexed = 2;
-
-
-// A copy of the PropertyType enum from global.h
-PropertyType = {};
-PropertyType.Normal = 0;
-PropertyType.Field = 1;
-PropertyType.ConstantFunction = 2;
-PropertyType.Callbacks = 3;
-PropertyType.Interceptor = 4;
-PropertyType.MapTransition = 5;
-PropertyType.ConstantTransition = 6;
-PropertyType.NullDescriptor = 7;
-
-
-// Different attributes for a property.
-PropertyAttribute = {};
-PropertyAttribute.None = NONE;
-PropertyAttribute.ReadOnly = READ_ONLY;
-PropertyAttribute.DontEnum = DONT_ENUM;
-PropertyAttribute.DontDelete = DONT_DELETE;
-
-
-// A copy of the scope types from runtime.cc.
-ScopeType = { Global: 0,
- Local: 1,
- With: 2,
- Closure: 3,
- Catch: 4 };
-
-
-// Mirror hierarchy:
-// - Mirror
-// - ValueMirror
-// - UndefinedMirror
-// - NullMirror
-// - NumberMirror
-// - StringMirror
-// - ObjectMirror
-// - FunctionMirror
-// - UnresolvedFunctionMirror
-// - ArrayMirror
-// - DateMirror
-// - RegExpMirror
-// - ErrorMirror
-// - PropertyMirror
-// - FrameMirror
-// - ScriptMirror
-
-
-/**
- * Base class for all mirror objects.
- * @param {string} type The type of the mirror
- * @constructor
- */
-function Mirror(type) {
- this.type_ = type;
-};
-
-
-Mirror.prototype.type = function() {
- return this.type_;
-};
-
-
-/**
- * Check whether the mirror reflects a value.
- * @returns {boolean} True if the mirror reflects a value.
- */
-Mirror.prototype.isValue = function() {
- return this instanceof ValueMirror;
-}
-
-
-/**
- * Check whether the mirror reflects the undefined value.
- * @returns {boolean} True if the mirror reflects the undefined value.
- */
-Mirror.prototype.isUndefined = function() {
- return this instanceof UndefinedMirror;
-}
-
-
-/**
- * Check whether the mirror reflects the null value.
- * @returns {boolean} True if the mirror reflects the null value
- */
-Mirror.prototype.isNull = function() {
- return this instanceof NullMirror;
-}
-
-
-/**
- * Check whether the mirror reflects a boolean value.
- * @returns {boolean} True if the mirror reflects a boolean value
- */
-Mirror.prototype.isBoolean = function() {
- return this instanceof BooleanMirror;
-}
-
-
-/**
- * Check whether the mirror reflects a number value.
- * @returns {boolean} True if the mirror reflects a number value
- */
-Mirror.prototype.isNumber = function() {
- return this instanceof NumberMirror;
-}
-
-
-/**
- * Check whether the mirror reflects a string value.
- * @returns {boolean} True if the mirror reflects a string value
- */
-Mirror.prototype.isString = function() {
- return this instanceof StringMirror;
-}
-
-
-/**
- * Check whether the mirror reflects an object.
- * @returns {boolean} True if the mirror reflects an object
- */
-Mirror.prototype.isObject = function() {
- return this instanceof ObjectMirror;
-}
-
-
-/**
- * Check whether the mirror reflects a function.
- * @returns {boolean} True if the mirror reflects a function
- */
-Mirror.prototype.isFunction = function() {
- return this instanceof FunctionMirror;
-}
-
-
-/**
- * Check whether the mirror reflects an unresolved function.
- * @returns {boolean} True if the mirror reflects an unresolved function
- */
-Mirror.prototype.isUnresolvedFunction = function() {
- return this instanceof UnresolvedFunctionMirror;
-}
-
-
-/**
- * Check whether the mirror reflects an array.
- * @returns {boolean} True if the mirror reflects an array
- */
-Mirror.prototype.isArray = function() {
- return this instanceof ArrayMirror;
-}
-
-
-/**
- * Check whether the mirror reflects a date.
- * @returns {boolean} True if the mirror reflects a date
- */
-Mirror.prototype.isDate = function() {
- return this instanceof DateMirror;
-}
-
-
-/**
- * Check whether the mirror reflects a regular expression.
- * @returns {boolean} True if the mirror reflects a regular expression
- */
-Mirror.prototype.isRegExp = function() {
- return this instanceof RegExpMirror;
-}
-
-
-/**
- * Check whether the mirror reflects an error.
- * @returns {boolean} True if the mirror reflects an error
- */
-Mirror.prototype.isError = function() {
- return this instanceof ErrorMirror;
-}
-
-
-/**
- * Check whether the mirror reflects a property.
- * @returns {boolean} True if the mirror reflects a property
- */
-Mirror.prototype.isProperty = function() {
- return this instanceof PropertyMirror;
-}
-
-
-/**
- * Check whether the mirror reflects a stack frame.
- * @returns {boolean} True if the mirror reflects a stack frame
- */
-Mirror.prototype.isFrame = function() {
- return this instanceof FrameMirror;
-}
-
-
-/**
- * Check whether the mirror reflects a script.
- * @returns {boolean} True if the mirror reflects a script
- */
-Mirror.prototype.isScript = function() {
- return this instanceof ScriptMirror;
-}
-
-
-/**
- * Check whether the mirror reflects a context.
- * @returns {boolean} True if the mirror reflects a context
- */
-Mirror.prototype.isContext = function() {
- return this instanceof ContextMirror;
-}
-
-
-/**
- * Check whether the mirror reflects a scope.
- * @returns {boolean} True if the mirror reflects a scope
- */
-Mirror.prototype.isScope = function() {
- return this instanceof ScopeMirror;
-}
-
-
-/**
- * Allocate a handle id for this object.
- */
-Mirror.prototype.allocateHandle_ = function() {
- this.handle_ = next_handle_++;
-}
-
-
-/**
- * Allocate a transient handle id for this object. Transient handles are
- * negative.
- */
-Mirror.prototype.allocateTransientHandle_ = function() {
- this.handle_ = next_transient_handle_--;
-}
-
-
-Mirror.prototype.toText = function() {
- // Simpel to text which is used when on specialization in subclass.
- return "#<" + builtins.GetInstanceName(this.constructor.name) + ">";
-}
-
-
-/**
- * Base class for all value mirror objects.
- * @param {string} type The type of the mirror
- * @param {value} value The value reflected by this mirror
- * @param {boolean} transient indicate whether this object is transient with a
- * transient handle
- * @constructor
- * @extends Mirror
- */
-function ValueMirror(type, value, transient) {
- Mirror.call(this, type);
- this.value_ = value;
- if (!transient) {
- this.allocateHandle_();
- } else {
- this.allocateTransientHandle_();
- }
-}
-inherits(ValueMirror, Mirror);
-
-
-Mirror.prototype.handle = function() {
- return this.handle_;
-};
-
-
-/**
- * Check whether this is a primitive value.
- * @return {boolean} True if the mirror reflects a primitive value
- */
-ValueMirror.prototype.isPrimitive = function() {
- var type = this.type();
- return type === 'undefined' ||
- type === 'null' ||
- type === 'boolean' ||
- type === 'number' ||
- type === 'string';
-};
-
-
-/**
- * Get the actual value reflected by this mirror.
- * @return {value} The value reflected by this mirror
- */
-ValueMirror.prototype.value = function() {
- return this.value_;
-};
-
-
-/**
- * Mirror object for Undefined.
- * @constructor
- * @extends ValueMirror
- */
-function UndefinedMirror() {
- ValueMirror.call(this, UNDEFINED_TYPE, void 0);
-}
-inherits(UndefinedMirror, ValueMirror);
-
-
-UndefinedMirror.prototype.toText = function() {
- return 'undefined';
-}
-
-
-/**
- * Mirror object for null.
- * @constructor
- * @extends ValueMirror
- */
-function NullMirror() {
- ValueMirror.call(this, NULL_TYPE, null);
-}
-inherits(NullMirror, ValueMirror);
-
-
-NullMirror.prototype.toText = function() {
- return 'null';
-}
-
-
-/**
- * Mirror object for boolean values.
- * @param {boolean} value The boolean value reflected by this mirror
- * @constructor
- * @extends ValueMirror
- */
-function BooleanMirror(value) {
- ValueMirror.call(this, BOOLEAN_TYPE, value);
-}
-inherits(BooleanMirror, ValueMirror);
-
-
-BooleanMirror.prototype.toText = function() {
- return this.value_ ? 'true' : 'false';
-}
-
-
-/**
- * Mirror object for number values.
- * @param {number} value The number value reflected by this mirror
- * @constructor
- * @extends ValueMirror
- */
-function NumberMirror(value) {
- ValueMirror.call(this, NUMBER_TYPE, value);
-}
-inherits(NumberMirror, ValueMirror);
-
-
-NumberMirror.prototype.toText = function() {
- return %NumberToString(this.value_);
-}
-
-
-/**
- * Mirror object for string values.
- * @param {string} value The string value reflected by this mirror
- * @constructor
- * @extends ValueMirror
- */
-function StringMirror(value) {
- ValueMirror.call(this, STRING_TYPE, value);
-}
-inherits(StringMirror, ValueMirror);
-
-
-StringMirror.prototype.length = function() {
- return this.value_.length;
-};
-
-
-StringMirror.prototype.toText = function() {
- if (this.length() > kMaxProtocolStringLength) {
- return this.value_.substring(0, kMaxProtocolStringLength) +
- '... (length: ' + this.length() + ')';
- } else {
- return this.value_;
- }
-}
-
-
-/**
- * Mirror object for objects.
- * @param {object} value The object reflected by this mirror
- * @param {boolean} transient indicate whether this object is transient with a
- * transient handle
- * @constructor
- * @extends ValueMirror
- */
-function ObjectMirror(value, type, transient) {
- ValueMirror.call(this, type || OBJECT_TYPE, value, transient);
-}
-inherits(ObjectMirror, ValueMirror);
-
-
-ObjectMirror.prototype.className = function() {
- return %_ClassOf(this.value_);
-};
-
-
-ObjectMirror.prototype.constructorFunction = function() {
- return MakeMirror(%DebugGetProperty(this.value_, 'constructor'));
-};
-
-
-ObjectMirror.prototype.prototypeObject = function() {
- return MakeMirror(%DebugGetProperty(this.value_, 'prototype'));
-};
-
-
-ObjectMirror.prototype.protoObject = function() {
- return MakeMirror(%DebugGetPrototype(this.value_));
-};
-
-
-ObjectMirror.prototype.hasNamedInterceptor = function() {
- // Get information on interceptors for this object.
- var x = %GetInterceptorInfo(this.value_);
- return (x & 2) != 0;
-};
-
-
-ObjectMirror.prototype.hasIndexedInterceptor = function() {
- // Get information on interceptors for this object.
- var x = %GetInterceptorInfo(this.value_);
- return (x & 1) != 0;
-};
-
-
-/**
- * Return the property names for this object.
- * @param {number} kind Indicate whether named, indexed or both kinds of
- * properties are requested
- * @param {number} limit Limit the number of names returend to the specified
- value
- * @return {Array} Property names for this object
- */
-ObjectMirror.prototype.propertyNames = function(kind, limit) {
- // Find kind and limit and allocate array for the result
- kind = kind || PropertyKind.Named | PropertyKind.Indexed;
-
- var propertyNames;
- var elementNames;
- var total = 0;
-
- // Find all the named properties.
- if (kind & PropertyKind.Named) {
- // Get the local property names.
- propertyNames = %GetLocalPropertyNames(this.value_);
- total += propertyNames.length;
-
- // Get names for named interceptor properties if any.
- if (this.hasNamedInterceptor() && (kind & PropertyKind.Named)) {
- var namedInterceptorNames =
- %GetNamedInterceptorPropertyNames(this.value_);
- if (namedInterceptorNames) {
- propertyNames = propertyNames.concat(namedInterceptorNames);
- total += namedInterceptorNames.length;
- }
- }
- }
-
- // Find all the indexed properties.
- if (kind & PropertyKind.Indexed) {
- // Get the local element names.
- elementNames = %GetLocalElementNames(this.value_);
- total += elementNames.length;
-
- // Get names for indexed interceptor properties.
- if (this.hasIndexedInterceptor() && (kind & PropertyKind.Indexed)) {
- var indexedInterceptorNames =
- %GetIndexedInterceptorElementNames(this.value_);
- if (indexedInterceptorNames) {
- elementNames = elementNames.concat(indexedInterceptorNames);
- total += indexedInterceptorNames.length;
- }
- }
- }
- limit = Math.min(limit || total, total);
-
- var names = new Array(limit);
- var index = 0;
-
- // Copy names for named properties.
- if (kind & PropertyKind.Named) {
- for (var i = 0; index < limit && i < propertyNames.length; i++) {
- names[index++] = propertyNames[i];
- }
- }
-
- // Copy names for indexed properties.
- if (kind & PropertyKind.Indexed) {
- for (var i = 0; index < limit && i < elementNames.length; i++) {
- names[index++] = elementNames[i];
- }
- }
-
- return names;
-};
-
-
-/**
- * Return the properties for this object as an array of PropertyMirror objects.
- * @param {number} kind Indicate whether named, indexed or both kinds of
- * properties are requested
- * @param {number} limit Limit the number of properties returend to the
- specified value
- * @return {Array} Property mirrors for this object
- */
-ObjectMirror.prototype.properties = function(kind, limit) {
- var names = this.propertyNames(kind, limit);
- var properties = new Array(names.length);
- for (var i = 0; i < names.length; i++) {
- properties[i] = this.property(names[i]);
- }
-
- return properties;
-};
-
-
-ObjectMirror.prototype.property = function(name) {
- var details = %DebugGetPropertyDetails(this.value_, %ToString(name));
- if (details) {
- return new PropertyMirror(this, name, details);
- }
-
- // Nothing found.
- return GetUndefinedMirror();
-};
-
-
-
-/**
- * Try to find a property from its value.
- * @param {Mirror} value The property value to look for
- * @return {PropertyMirror} The property with the specified value. If no
- * property was found with the specified value UndefinedMirror is returned
- */
-ObjectMirror.prototype.lookupProperty = function(value) {
- var properties = this.properties();
-
- // Look for property value in properties.
- for (var i = 0; i < properties.length; i++) {
-
- // Skip properties which are defined through assessors.
- var property = properties[i];
- if (property.propertyType() != PropertyType.Callbacks) {
- if (%_ObjectEquals(property.value_, value.value_)) {
- return property;
- }
- }
- }
-
- // Nothing found.
- return GetUndefinedMirror();
-};
-
-
-/**
- * Returns objects which has direct references to this object
- * @param {number} opt_max_objects Optional parameter specifying the maximum
- * number of referencing objects to return.
- * @return {Array} The objects which has direct references to this object.
- */
-ObjectMirror.prototype.referencedBy = function(opt_max_objects) {
- // Find all objects with direct references to this object.
- var result = %DebugReferencedBy(this.value_,
- Mirror.prototype, opt_max_objects || 0);
-
- // Make mirrors for all the references found.
- for (var i = 0; i < result.length; i++) {
- result[i] = MakeMirror(result[i]);
- }
-
- return result;
-};
-
-
-ObjectMirror.prototype.toText = function() {
- var name;
- var ctor = this.constructorFunction();
- if (!ctor.isFunction()) {
- name = this.className();
- } else {
- name = ctor.name();
- if (!name) {
- name = this.className();
- }
- }
- return '#<' + builtins.GetInstanceName(name) + '>';
-};
-
-
-/**
- * Mirror object for functions.
- * @param {function} value The function object reflected by this mirror.
- * @constructor
- * @extends ObjectMirror
- */
-function FunctionMirror(value) {
- ObjectMirror.call(this, value, FUNCTION_TYPE);
- this.resolved_ = true;
-}
-inherits(FunctionMirror, ObjectMirror);
-
-
-/**
- * Returns whether the function is resolved.
- * @return {boolean} True if the function is resolved. Unresolved functions can
- * only originate as functions from stack frames
- */
-FunctionMirror.prototype.resolved = function() {
- return this.resolved_;
-};
-
-
-/**
- * Returns the name of the function.
- * @return {string} Name of the function
- */
-FunctionMirror.prototype.name = function() {
- return %FunctionGetName(this.value_);
-};
-
-
-/**
- * Returns the inferred name of the function.
- * @return {string} Name of the function
- */
-FunctionMirror.prototype.inferredName = function() {
- return %FunctionGetInferredName(this.value_);
-};
-
-
-/**
- * Returns the source code for the function.
- * @return {string or undefined} The source code for the function. If the
- * function is not resolved undefined will be returned.
- */
-FunctionMirror.prototype.source = function() {
- // Return source if function is resolved. Otherwise just fall through to
- // return undefined.
- if (this.resolved()) {
- return builtins.FunctionSourceString(this.value_);
- }
-};
-
-
-/**
- * Returns the script object for the function.
- * @return {ScriptMirror or undefined} Script object for the function or
- * undefined if the function has no script
- */
-FunctionMirror.prototype.script = function() {
- // Return script if function is resolved. Otherwise just fall through
- // to return undefined.
- if (this.resolved()) {
- var script = %FunctionGetScript(this.value_);
- if (script) {
- return MakeMirror(script);
- }
- }
-};
-
-
-/**
- * Returns the script source position for the function. Only makes sense
- * for functions which has a script defined.
- * @return {Number or undefined} in-script position for the function
- */
-FunctionMirror.prototype.sourcePosition_ = function() {
- // Return script if function is resolved. Otherwise just fall through
- // to return undefined.
- if (this.resolved()) {
- return %FunctionGetScriptSourcePosition(this.value_);
- }
-};
-
-
-/**
- * Returns the script source location object for the function. Only makes sense
- * for functions which has a script defined.
- * @return {Location or undefined} in-script location for the function begin
- */
-FunctionMirror.prototype.sourceLocation = function() {
- if (this.resolved() && this.script()) {
- return this.script().locationFromPosition(this.sourcePosition_(),
- true);
- }
-};
-
-
-/**
- * Returns objects constructed by this function.
- * @param {number} opt_max_instances Optional parameter specifying the maximum
- * number of instances to return.
- * @return {Array or undefined} The objects constructed by this function.
- */
-FunctionMirror.prototype.constructedBy = function(opt_max_instances) {
- if (this.resolved()) {
- // Find all objects constructed from this function.
- var result = %DebugConstructedBy(this.value_, opt_max_instances || 0);
-
- // Make mirrors for all the instances found.
- for (var i = 0; i < result.length; i++) {
- result[i] = MakeMirror(result[i]);
- }
-
- return result;
- } else {
- return [];
- }
-};
-
-
-FunctionMirror.prototype.toText = function() {
- return this.source();
-}
-
-
-/**
- * Mirror object for unresolved functions.
- * @param {string} value The name for the unresolved function reflected by this
- * mirror.
- * @constructor
- * @extends ObjectMirror
- */
-function UnresolvedFunctionMirror(value) {
- // Construct this using the ValueMirror as an unresolved function is not a
- // real object but just a string.
- ValueMirror.call(this, FUNCTION_TYPE, value);
- this.propertyCount_ = 0;
- this.elementCount_ = 0;
- this.resolved_ = false;
-}
-inherits(UnresolvedFunctionMirror, FunctionMirror);
-
-
-UnresolvedFunctionMirror.prototype.className = function() {
- return 'Function';
-};
-
-
-UnresolvedFunctionMirror.prototype.constructorFunction = function() {
- return GetUndefinedMirror();
-};
-
-
-UnresolvedFunctionMirror.prototype.prototypeObject = function() {
- return GetUndefinedMirror();
-};
-
-
-UnresolvedFunctionMirror.prototype.protoObject = function() {
- return GetUndefinedMirror();
-};
-
-
-UnresolvedFunctionMirror.prototype.name = function() {
- return this.value_;
-};
-
-
-UnresolvedFunctionMirror.prototype.inferredName = function() {
- return undefined;
-};
-
-
-UnresolvedFunctionMirror.prototype.propertyNames = function(kind, limit) {
- return [];
-}
-
-
-/**
- * Mirror object for arrays.
- * @param {Array} value The Array object reflected by this mirror
- * @constructor
- * @extends ObjectMirror
- */
-function ArrayMirror(value) {
- ObjectMirror.call(this, value);
-}
-inherits(ArrayMirror, ObjectMirror);
-
-
-ArrayMirror.prototype.length = function() {
- return this.value_.length;
-};
-
-
-ArrayMirror.prototype.indexedPropertiesFromRange = function(opt_from_index, opt_to_index) {
- var from_index = opt_from_index || 0;
- var to_index = opt_to_index || this.length() - 1;
- if (from_index > to_index) return new Array();
- var values = new Array(to_index - from_index + 1);
- for (var i = from_index; i <= to_index; i++) {
- var details = %DebugGetPropertyDetails(this.value_, %ToString(i));
- var value;
- if (details) {
- value = new PropertyMirror(this, i, details);
- } else {
- value = GetUndefinedMirror();
- }
- values[i - from_index] = value;
- }
- return values;
-}
-
-
-/**
- * Mirror object for dates.
- * @param {Date} value The Date object reflected by this mirror
- * @constructor
- * @extends ObjectMirror
- */
-function DateMirror(value) {
- ObjectMirror.call(this, value);
-}
-inherits(DateMirror, ObjectMirror);
-
-
-DateMirror.prototype.toText = function() {
- var s = JSON.stringify(this.value_);
- return s.substring(1, s.length - 1); // cut quotes
-}
-
-
-/**
- * Mirror object for regular expressions.
- * @param {RegExp} value The RegExp object reflected by this mirror
- * @constructor
- * @extends ObjectMirror
- */
-function RegExpMirror(value) {
- ObjectMirror.call(this, value, REGEXP_TYPE);
-}
-inherits(RegExpMirror, ObjectMirror);
-
-
-/**
- * Returns the source to the regular expression.
- * @return {string or undefined} The source to the regular expression
- */
-RegExpMirror.prototype.source = function() {
- return this.value_.source;
-};
-
-
-/**
- * Returns whether this regular expression has the global (g) flag set.
- * @return {boolean} Value of the global flag
- */
-RegExpMirror.prototype.global = function() {
- return this.value_.global;
-};
-
-
-/**
- * Returns whether this regular expression has the ignore case (i) flag set.
- * @return {boolean} Value of the ignore case flag
- */
-RegExpMirror.prototype.ignoreCase = function() {
- return this.value_.ignoreCase;
-};
-
-
-/**
- * Returns whether this regular expression has the multiline (m) flag set.
- * @return {boolean} Value of the multiline flag
- */
-RegExpMirror.prototype.multiline = function() {
- return this.value_.multiline;
-};
-
-
-RegExpMirror.prototype.toText = function() {
- // Simpel to text which is used when on specialization in subclass.
- return "/" + this.source() + "/";
-}
-
-
-/**
- * Mirror object for error objects.
- * @param {Error} value The error object reflected by this mirror
- * @constructor
- * @extends ObjectMirror
- */
-function ErrorMirror(value) {
- ObjectMirror.call(this, value, ERROR_TYPE);
-}
-inherits(ErrorMirror, ObjectMirror);
-
-
-/**
- * Returns the message for this eror object.
- * @return {string or undefined} The message for this eror object
- */
-ErrorMirror.prototype.message = function() {
- return this.value_.message;
-};
-
-
-ErrorMirror.prototype.toText = function() {
- // Use the same text representation as in messages.js.
- var text;
- try {
- str = builtins.ToDetailString(this.value_);
- } catch (e) {
- str = '#<an Error>';
- }
- return str;
-}
-
-
-/**
- * Base mirror object for properties.
- * @param {ObjectMirror} mirror The mirror object having this property
- * @param {string} name The name of the property
- * @param {Array} details Details about the property
- * @constructor
- * @extends Mirror
- */
-function PropertyMirror(mirror, name, details) {
- Mirror.call(this, PROPERTY_TYPE);
- this.mirror_ = mirror;
- this.name_ = name;
- this.value_ = details[0];
- this.details_ = details[1];
- if (details.length > 2) {
- this.exception_ = details[2]
- this.getter_ = details[3];
- this.setter_ = details[4];
- }
-}
-inherits(PropertyMirror, Mirror);
-
-
-PropertyMirror.prototype.isReadOnly = function() {
- return (this.attributes() & PropertyAttribute.ReadOnly) != 0;
-}
-
-
-PropertyMirror.prototype.isEnum = function() {
- return (this.attributes() & PropertyAttribute.DontEnum) == 0;
-}
-
-
-PropertyMirror.prototype.canDelete = function() {
- return (this.attributes() & PropertyAttribute.DontDelete) == 0;
-}
-
-
-PropertyMirror.prototype.name = function() {
- return this.name_;
-}
-
-
-PropertyMirror.prototype.isIndexed = function() {
- for (var i = 0; i < this.name_.length; i++) {
- if (this.name_[i] < '0' || '9' < this.name_[i]) {
- return false;
- }
- }
- return true;
-}
-
-
-PropertyMirror.prototype.value = function() {
- return MakeMirror(this.value_, false);
-}
-
-
-/**
- * Returns whether this property value is an exception.
- * @return {booolean} True if this property value is an exception
- */
-PropertyMirror.prototype.isException = function() {
- return this.exception_ ? true : false;
-}
-
-
-PropertyMirror.prototype.attributes = function() {
- return %DebugPropertyAttributesFromDetails(this.details_);
-}
-
-
-PropertyMirror.prototype.propertyType = function() {
- return %DebugPropertyTypeFromDetails(this.details_);
-}
-
-
-PropertyMirror.prototype.insertionIndex = function() {
- return %DebugPropertyIndexFromDetails(this.details_);
-}
-
-
-/**
- * Returns whether this property has a getter defined through __defineGetter__.
- * @return {booolean} True if this property has a getter
- */
-PropertyMirror.prototype.hasGetter = function() {
- return this.getter_ ? true : false;
-}
-
-
-/**
- * Returns whether this property has a setter defined through __defineSetter__.
- * @return {booolean} True if this property has a setter
- */
-PropertyMirror.prototype.hasSetter = function() {
- return this.setter_ ? true : false;
-}
-
-
-/**
- * Returns the getter for this property defined through __defineGetter__.
- * @return {Mirror} FunctionMirror reflecting the getter function or
- * UndefinedMirror if there is no getter for this property
- */
-PropertyMirror.prototype.getter = function() {
- if (this.hasGetter()) {
- return MakeMirror(this.getter_);
- } else {
- return GetUndefinedMirror();
- }
-}
-
-
-/**
- * Returns the setter for this property defined through __defineSetter__.
- * @return {Mirror} FunctionMirror reflecting the setter function or
- * UndefinedMirror if there is no setter for this property
- */
-PropertyMirror.prototype.setter = function() {
- if (this.hasSetter()) {
- return MakeMirror(this.setter_);
- } else {
- return GetUndefinedMirror();
- }
-}
-
-
-/**
- * Returns whether this property is natively implemented by the host or a set
- * through JavaScript code.
- * @return {boolean} True if the property is
- * UndefinedMirror if there is no setter for this property
- */
-PropertyMirror.prototype.isNative = function() {
- return (this.propertyType() == PropertyType.Interceptor) ||
- ((this.propertyType() == PropertyType.Callbacks) &&
- !this.hasGetter() && !this.hasSetter());
-}
-
-
-const kFrameDetailsFrameIdIndex = 0;
-const kFrameDetailsReceiverIndex = 1;
-const kFrameDetailsFunctionIndex = 2;
-const kFrameDetailsArgumentCountIndex = 3;
-const kFrameDetailsLocalCountIndex = 4;
-const kFrameDetailsSourcePositionIndex = 5;
-const kFrameDetailsConstructCallIndex = 6;
-const kFrameDetailsDebuggerFrameIndex = 7;
-const kFrameDetailsFirstDynamicIndex = 8;
-
-const kFrameDetailsNameIndex = 0;
-const kFrameDetailsValueIndex = 1;
-const kFrameDetailsNameValueSize = 2;
-
-/**
- * Wrapper for the frame details information retreived from the VM. The frame
- * details from the VM is an array with the following content. See runtime.cc
- * Runtime_GetFrameDetails.
- * 0: Id
- * 1: Receiver
- * 2: Function
- * 3: Argument count
- * 4: Local count
- * 5: Source position
- * 6: Construct call
- * Arguments name, value
- * Locals name, value
- * @param {number} break_id Current break id
- * @param {number} index Frame number
- * @constructor
- */
-function FrameDetails(break_id, index) {
- this.break_id_ = break_id;
- this.details_ = %GetFrameDetails(break_id, index);
-}
-
-
-FrameDetails.prototype.frameId = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kFrameDetailsFrameIdIndex];
-}
-
-
-FrameDetails.prototype.receiver = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kFrameDetailsReceiverIndex];
-}
-
-
-FrameDetails.prototype.func = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kFrameDetailsFunctionIndex];
-}
-
-
-FrameDetails.prototype.isConstructCall = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kFrameDetailsConstructCallIndex];
-}
-
-
-FrameDetails.prototype.isDebuggerFrame = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kFrameDetailsDebuggerFrameIndex];
-}
-
-
-FrameDetails.prototype.argumentCount = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kFrameDetailsArgumentCountIndex];
-}
-
-
-FrameDetails.prototype.argumentName = function(index) {
- %CheckExecutionState(this.break_id_);
- if (index >= 0 && index < this.argumentCount()) {
- return this.details_[kFrameDetailsFirstDynamicIndex +
- index * kFrameDetailsNameValueSize +
- kFrameDetailsNameIndex]
- }
-}
-
-
-FrameDetails.prototype.argumentValue = function(index) {
- %CheckExecutionState(this.break_id_);
- if (index >= 0 && index < this.argumentCount()) {
- return this.details_[kFrameDetailsFirstDynamicIndex +
- index * kFrameDetailsNameValueSize +
- kFrameDetailsValueIndex]
- }
-}
-
-
-FrameDetails.prototype.localCount = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kFrameDetailsLocalCountIndex];
-}
-
-
-FrameDetails.prototype.sourcePosition = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kFrameDetailsSourcePositionIndex];
-}
-
-
-FrameDetails.prototype.localName = function(index) {
- %CheckExecutionState(this.break_id_);
- if (index >= 0 && index < this.localCount()) {
- var locals_offset = kFrameDetailsFirstDynamicIndex + this.argumentCount() * kFrameDetailsNameValueSize
- return this.details_[locals_offset +
- index * kFrameDetailsNameValueSize +
- kFrameDetailsNameIndex]
- }
-}
-
-
-FrameDetails.prototype.localValue = function(index) {
- %CheckExecutionState(this.break_id_);
- if (index >= 0 && index < this.localCount()) {
- var locals_offset = kFrameDetailsFirstDynamicIndex + this.argumentCount() * kFrameDetailsNameValueSize
- return this.details_[locals_offset +
- index * kFrameDetailsNameValueSize +
- kFrameDetailsValueIndex]
- }
-}
-
-
-FrameDetails.prototype.scopeCount = function() {
- return %GetScopeCount(this.break_id_, this.frameId());
-}
-
-
-/**
- * Mirror object for stack frames.
- * @param {number} break_id The break id in the VM for which this frame is
- valid
- * @param {number} index The frame index (top frame is index 0)
- * @constructor
- * @extends Mirror
- */
-function FrameMirror(break_id, index) {
- Mirror.call(this, FRAME_TYPE);
- this.break_id_ = break_id;
- this.index_ = index;
- this.details_ = new FrameDetails(break_id, index);
-}
-inherits(FrameMirror, Mirror);
-
-
-FrameMirror.prototype.index = function() {
- return this.index_;
-};
-
-
-FrameMirror.prototype.func = function() {
- // Get the function for this frame from the VM.
- var f = this.details_.func();
-
- // Create a function mirror. NOTE: MakeMirror cannot be used here as the
- // value returned from the VM might be a string if the function for the
- // frame is unresolved.
- if (IS_FUNCTION(f)) {
- return MakeMirror(f);
- } else {
- return new UnresolvedFunctionMirror(f);
- }
-};
-
-
-FrameMirror.prototype.receiver = function() {
- return MakeMirror(this.details_.receiver());
-};
-
-
-FrameMirror.prototype.isConstructCall = function() {
- return this.details_.isConstructCall();
-};
-
-
-FrameMirror.prototype.isDebuggerFrame = function() {
- return this.details_.isDebuggerFrame();
-};
-
-
-FrameMirror.prototype.argumentCount = function() {
- return this.details_.argumentCount();
-};
-
-
-FrameMirror.prototype.argumentName = function(index) {
- return this.details_.argumentName(index);
-};
-
-
-FrameMirror.prototype.argumentValue = function(index) {
- return MakeMirror(this.details_.argumentValue(index));
-};
-
-
-FrameMirror.prototype.localCount = function() {
- return this.details_.localCount();
-};
-
-
-FrameMirror.prototype.localName = function(index) {
- return this.details_.localName(index);
-};
-
-
-FrameMirror.prototype.localValue = function(index) {
- return MakeMirror(this.details_.localValue(index));
-};
-
-
-FrameMirror.prototype.sourcePosition = function() {
- return this.details_.sourcePosition();
-};
-
-
-FrameMirror.prototype.sourceLocation = function() {
- if (this.func().resolved() && this.func().script()) {
- return this.func().script().locationFromPosition(this.sourcePosition(),
- true);
- }
-};
-
-
-FrameMirror.prototype.sourceLine = function() {
- if (this.func().resolved()) {
- var location = this.sourceLocation();
- if (location) {
- return location.line;
- }
- }
-};
-
-
-FrameMirror.prototype.sourceColumn = function() {
- if (this.func().resolved()) {
- var location = this.sourceLocation();
- if (location) {
- return location.column;
- }
- }
-};
-
-
-FrameMirror.prototype.sourceLineText = function() {
- if (this.func().resolved()) {
- var location = this.sourceLocation();
- if (location) {
- return location.sourceText();
- }
- }
-};
-
-
-FrameMirror.prototype.scopeCount = function() {
- return this.details_.scopeCount();
-};
-
-
-FrameMirror.prototype.scope = function(index) {
- return new ScopeMirror(this, index);
-};
-
-
-FrameMirror.prototype.evaluate = function(source, disable_break) {
- var result = %DebugEvaluate(this.break_id_, this.details_.frameId(),
- source, Boolean(disable_break));
- return MakeMirror(result);
-};
-
-
-FrameMirror.prototype.invocationText = function() {
- // Format frame invoaction (receiver, function and arguments).
- var result = '';
- var func = this.func();
- var receiver = this.receiver();
- if (this.isConstructCall()) {
- // For constructor frames display new followed by the function name.
- result += 'new ';
- result += func.name() ? func.name() : '[anonymous]';
- } else if (this.isDebuggerFrame()) {
- result += '[debugger]';
- } else {
- // If the receiver has a className which is 'global' don't display it.
- var display_receiver = !receiver.className || receiver.className() != 'global';
- if (display_receiver) {
- result += receiver.toText();
- }
- // Try to find the function as a property in the receiver. Include the
- // prototype chain in the lookup.
- var property = GetUndefinedMirror();
- if (!receiver.isUndefined()) {
- for (var r = receiver; !r.isNull() && property.isUndefined(); r = r.protoObject()) {
- property = r.lookupProperty(func);
- }
- }
- if (!property.isUndefined()) {
- // The function invoked was found on the receiver. Use the property name
- // for the backtrace.
- if (!property.isIndexed()) {
- if (display_receiver) {
- result += '.';
- }
- result += property.name();
- } else {
- result += '[';
- result += property.name();
- result += ']';
- }
- // Also known as - if the name in the function doesn't match the name
- // under which it was looked up.
- if (func.name() && func.name() != property.name()) {
- result += '(aka ' + func.name() + ')';
- }
- } else {
- // The function invoked was not found on the receiver. Use the function
- // name if available for the backtrace.
- if (display_receiver) {
- result += '.';
- }
- result += func.name() ? func.name() : '[anonymous]';
- }
- }
-
- // Render arguments for normal frames.
- if (!this.isDebuggerFrame()) {
- result += '(';
- for (var i = 0; i < this.argumentCount(); i++) {
- if (i != 0) result += ', ';
- if (this.argumentName(i)) {
- result += this.argumentName(i);
- result += '=';
- }
- result += this.argumentValue(i).toText();
- }
- result += ')';
- }
-
- return result;
-}
-
-
-FrameMirror.prototype.sourceAndPositionText = function() {
- // Format source and position.
- var result = '';
- var func = this.func();
- if (func.resolved()) {
- if (func.script()) {
- if (func.script().name()) {
- result += func.script().name();
- } else {
- result += '[unnamed]';
- }
- if (!this.isDebuggerFrame()) {
- var location = this.sourceLocation();
- result += ' line ';
- result += !IS_UNDEFINED(location) ? (location.line + 1) : '?';
- result += ' column ';
- result += !IS_UNDEFINED(location) ? (location.column + 1) : '?';
- if (!IS_UNDEFINED(this.sourcePosition())) {
- result += ' (position ' + (this.sourcePosition() + 1) + ')';
- }
- }
- } else {
- result += '[no source]';
- }
- } else {
- result += '[unresolved]';
- }
-
- return result;
-}
-
-
-FrameMirror.prototype.localsText = function() {
- // Format local variables.
- var result = '';
- var locals_count = this.localCount()
- if (locals_count > 0) {
- for (var i = 0; i < locals_count; ++i) {
- result += ' var ';
- result += this.localName(i);
- result += ' = ';
- result += this.localValue(i).toText();
- if (i < locals_count - 1) result += '\n';
- }
- }
-
- return result;
-}
-
-
-FrameMirror.prototype.toText = function(opt_locals) {
- var result = '';
- result += '#' + (this.index() <= 9 ? '0' : '') + this.index();
- result += ' ';
- result += this.invocationText();
- result += ' ';
- result += this.sourceAndPositionText();
- if (opt_locals) {
- result += '\n';
- result += this.localsText();
- }
- return result;
-}
-
-
-const kScopeDetailsTypeIndex = 0;
-const kScopeDetailsObjectIndex = 1;
-
-function ScopeDetails(frame, index) {
- this.break_id_ = frame.break_id_;
- this.details_ = %GetScopeDetails(frame.break_id_,
- frame.details_.frameId(),
- index);
-}
-
-
-ScopeDetails.prototype.type = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kScopeDetailsTypeIndex];
-}
-
-
-ScopeDetails.prototype.object = function() {
- %CheckExecutionState(this.break_id_);
- return this.details_[kScopeDetailsObjectIndex];
-}
-
-
-/**
- * Mirror object for scope.
- * @param {FrameMirror} frame The frame this scope is a part of
- * @param {number} index The scope index in the frame
- * @constructor
- * @extends Mirror
- */
-function ScopeMirror(frame, index) {
- Mirror.call(this, SCOPE_TYPE);
- this.frame_index_ = frame.index_;
- this.scope_index_ = index;
- this.details_ = new ScopeDetails(frame, index);
-}
-inherits(ScopeMirror, Mirror);
-
-
-ScopeMirror.prototype.frameIndex = function() {
- return this.frame_index_;
-};
-
-
-ScopeMirror.prototype.scopeIndex = function() {
- return this.scope_index_;
-};
-
-
-ScopeMirror.prototype.scopeType = function() {
- return this.details_.type();
-};
-
-
-ScopeMirror.prototype.scopeObject = function() {
- // For local and closure scopes create a transient mirror as these objects are
- // created on the fly materializing the local or closure scopes and
- // therefore will not preserve identity.
- var transient = this.scopeType() == ScopeType.Local ||
- this.scopeType() == ScopeType.Closure;
- return MakeMirror(this.details_.object(), transient);
-};
-
-
-/**
- * Mirror object for script source.
- * @param {Script} script The script object
- * @constructor
- * @extends Mirror
- */
-function ScriptMirror(script) {
- Mirror.call(this, SCRIPT_TYPE);
- this.script_ = script;
- this.context_ = new ContextMirror(script.context_data);
- this.allocateHandle_();
-}
-inherits(ScriptMirror, Mirror);
-
-
-ScriptMirror.prototype.value = function() {
- return this.script_;
-};
-
-
-ScriptMirror.prototype.name = function() {
- return this.script_.name;
-};
-
-
-ScriptMirror.prototype.id = function() {
- return this.script_.id;
-};
-
-
-ScriptMirror.prototype.source = function() {
- return this.script_.source;
-};
-
-
-ScriptMirror.prototype.lineOffset = function() {
- return this.script_.line_offset;
-};
-
-
-ScriptMirror.prototype.columnOffset = function() {
- return this.script_.column_offset;
-};
-
-
-ScriptMirror.prototype.data = function() {
- return this.script_.data;
-};
-
-
-ScriptMirror.prototype.scriptType = function() {
- return this.script_.type;
-};
-
-
-ScriptMirror.prototype.compilationType = function() {
- return this.script_.compilation_type;
-};
-
-
-ScriptMirror.prototype.lineCount = function() {
- return this.script_.lineCount();
-};
-
-
-ScriptMirror.prototype.locationFromPosition = function(
- position, include_resource_offset) {
- return this.script_.locationFromPosition(position, include_resource_offset);
-}
-
-
-ScriptMirror.prototype.sourceSlice = function (opt_from_line, opt_to_line) {
- return this.script_.sourceSlice(opt_from_line, opt_to_line);
-}
-
-
-ScriptMirror.prototype.context = function() {
- return this.context_;
-};
-
-
-ScriptMirror.prototype.evalFromScript = function() {
- return MakeMirror(this.script_.eval_from_script);
-};
-
-
-ScriptMirror.prototype.evalFromFunctionName = function() {
- return MakeMirror(this.script_.eval_from_function_name);
-};
-
-
-ScriptMirror.prototype.evalFromLocation = function() {
- var eval_from_script = this.evalFromScript();
- if (!eval_from_script.isUndefined()) {
- var position = this.script_.eval_from_script_position;
- return eval_from_script.locationFromPosition(position, true);
- }
-};
-
-
-ScriptMirror.prototype.toText = function() {
- var result = '';
- result += this.name();
- result += ' (lines: ';
- if (this.lineOffset() > 0) {
- result += this.lineOffset();
- result += '-';
- result += this.lineOffset() + this.lineCount() - 1;
- } else {
- result += this.lineCount();
- }
- result += ')';
- return result;
-}
-
-
-/**
- * Mirror object for context.
- * @param {Object} data The context data
- * @constructor
- * @extends Mirror
- */
-function ContextMirror(data) {
- Mirror.call(this, CONTEXT_TYPE);
- this.data_ = data;
- this.allocateHandle_();
-}
-inherits(ContextMirror, Mirror);
-
-
-ContextMirror.prototype.data = function() {
- return this.data_;
-};
-
-
-/**
- * Returns a mirror serializer
- *
- * @param {boolean} details Set to true to include details
- * @param {Object} options Options comtrolling the serialization
- * The following options can be set:
- * includeSource: include ths full source of scripts
- * @returns {MirrorSerializer} mirror serializer
- */
-function MakeMirrorSerializer(details, options) {
- return new JSONProtocolSerializer(details, options);
-}
-
-
-/**
- * Object for serializing a mirror objects and its direct references.
- * @param {boolean} details Indicates whether to include details for the mirror
- * serialized
- * @constructor
- */
-function JSONProtocolSerializer(details, options) {
- this.details_ = details;
- this.options_ = options;
- this.mirrors_ = [ ];
-}
-
-
-/**
- * Returns a serialization of an object reference. The referenced object are
- * added to the serialization state.
- *
- * @param {Mirror} mirror The mirror to serialize
- * @returns {String} JSON serialization
- */
-JSONProtocolSerializer.prototype.serializeReference = function(mirror) {
- return this.serialize_(mirror, true, true);
-}
-
-
-/**
- * Returns a serialization of an object value. The referenced objects are
- * added to the serialization state.
- *
- * @param {Mirror} mirror The mirror to serialize
- * @returns {String} JSON serialization
- */
-JSONProtocolSerializer.prototype.serializeValue = function(mirror) {
- var json = this.serialize_(mirror, false, true);
- return json;
-}
-
-
-/**
- * Returns a serialization of all the objects referenced.
- *
- * @param {Mirror} mirror The mirror to serialize.
- * @returns {Array.<Object>} Array of the referenced objects converted to
- * protcol objects.
- */
-JSONProtocolSerializer.prototype.serializeReferencedObjects = function() {
- // Collect the protocol representation of the referenced objects in an array.
- var content = [];
-
- // Get the number of referenced objects.
- var count = this.mirrors_.length;
-
- for (var i = 0; i < count; i++) {
- content.push(this.serialize_(this.mirrors_[i], false, false));
- }
-
- return content;
-}
-
-
-JSONProtocolSerializer.prototype.includeSource_ = function() {
- return this.options_ && this.options_.includeSource;
-}
-
-
-JSONProtocolSerializer.prototype.inlineRefs_ = function() {
- return this.options_ && this.options_.inlineRefs;
-}
-
-
-JSONProtocolSerializer.prototype.add_ = function(mirror) {
- // If this mirror is already in the list just return.
- for (var i = 0; i < this.mirrors_.length; i++) {
- if (this.mirrors_[i] === mirror) {
- return;
- }
- }
-
- // Add the mirror to the list of mirrors to be serialized.
- this.mirrors_.push(mirror);
-}
-
-
-/**
- * Formats mirror object to protocol reference object with some data that can
- * be used to display the value in debugger.
- * @param {Mirror} mirror Mirror to serialize.
- * @return {Object} Protocol reference object.
- */
-JSONProtocolSerializer.prototype.serializeReferenceWithDisplayData_ =
- function(mirror) {
- var o = {};
- o.ref = mirror.handle();
- o.type = mirror.type();
- switch (mirror.type()) {
- case UNDEFINED_TYPE:
- case NULL_TYPE:
- case BOOLEAN_TYPE:
- case NUMBER_TYPE:
- o.value = mirror.value();
- break;
- case STRING_TYPE:
- // Limit string length.
- o.value = mirror.toText();
- break;
- case FUNCTION_TYPE:
- o.name = mirror.name();
- o.inferredName = mirror.inferredName();
- if (mirror.script()) {
- o.scriptId = mirror.script().id();
- }
- break;
- case ERROR_TYPE:
- case REGEXP_TYPE:
- o.value = mirror.toText();
- break;
- case OBJECT_TYPE:
- o.className = mirror.className();
- break;
- }
- return o;
-};
-
-
-JSONProtocolSerializer.prototype.serialize_ = function(mirror, reference,
- details) {
- // If serializing a reference to a mirror just return the reference and add
- // the mirror to the referenced mirrors.
- if (reference &&
- (mirror.isValue() || mirror.isScript() || mirror.isContext())) {
- if (this.inlineRefs_() && mirror.isValue()) {
- return this.serializeReferenceWithDisplayData_(mirror);
- } else {
- this.add_(mirror);
- return {'ref' : mirror.handle()};
- }
- }
-
- // Collect the JSON property/value pairs.
- var content = {};
-
- // Add the mirror handle.
- if (mirror.isValue() || mirror.isScript() || mirror.isContext()) {
- content.handle = mirror.handle();
- }
-
- // Always add the type.
- content.type = mirror.type();
-
- switch (mirror.type()) {
- case UNDEFINED_TYPE:
- case NULL_TYPE:
- // Undefined and null are represented just by their type.
- break;
-
- case BOOLEAN_TYPE:
- // Boolean values are simply represented by their value.
- content.value = mirror.value();
- break;
-
- case NUMBER_TYPE:
- // Number values are simply represented by their value.
- content.value = NumberToJSON_(mirror.value());
- break;
-
- case STRING_TYPE:
- // String values might have their value cropped to keep down size.
- if (mirror.length() > kMaxProtocolStringLength) {
- var str = mirror.value().substring(0, kMaxProtocolStringLength);
- content.value = str;
- content.fromIndex = 0;
- content.toIndex = kMaxProtocolStringLength;
- } else {
- content.value = mirror.value();
- }
- content.length = mirror.length();
- break;
-
- case OBJECT_TYPE:
- case FUNCTION_TYPE:
- case ERROR_TYPE:
- case REGEXP_TYPE:
- // Add object representation.
- this.serializeObject_(mirror, content, details);
- break;
-
- case PROPERTY_TYPE:
- throw new Error('PropertyMirror cannot be serialized independeltly')
- break;
-
- case FRAME_TYPE:
- // Add object representation.
- this.serializeFrame_(mirror, content);
- break;
-
- case SCOPE_TYPE:
- // Add object representation.
- this.serializeScope_(mirror, content);
- break;
-
- case SCRIPT_TYPE:
- // Script is represented by id, name and source attributes.
- if (mirror.name()) {
- content.name = mirror.name();
- }
- content.id = mirror.id();
- content.lineOffset = mirror.lineOffset();
- content.columnOffset = mirror.columnOffset();
- content.lineCount = mirror.lineCount();
- if (mirror.data()) {
- content.data = mirror.data();
- }
- if (this.includeSource_()) {
- content.source = mirror.source();
- } else {
- var sourceStart = mirror.source().substring(0, 80);
- content.sourceStart = sourceStart;
- }
- content.sourceLength = mirror.source().length;
- content.scriptType = mirror.scriptType();
- content.compilationType = mirror.compilationType();
- // For compilation type eval emit information on the script from which
- // eval was called if a script is present.
- if (mirror.compilationType() == 1 &&
- mirror.evalFromScript()) {
- content.evalFromScript =
- this.serializeReference(mirror.evalFromScript());
- var evalFromLocation = mirror.evalFromLocation()
- if (evalFromLocation) {
- content.evalFromLocation = { line: evalFromLocation.line,
- column: evalFromLocation.column };
- }
- if (mirror.evalFromFunctionName()) {
- content.evalFromFunctionName = mirror.evalFromFunctionName();
- }
- }
- if (mirror.context()) {
- content.context = this.serializeReference(mirror.context());
- }
- break;
-
- case CONTEXT_TYPE:
- content.data = mirror.data();
- break;
- }
-
- // Always add the text representation.
- content.text = mirror.toText();
-
- // Create and return the JSON string.
- return content;
-}
-
-
-/**
- * Serialize object information to the following JSON format.
- *
- * {"className":"<class name>",
- * "constructorFunction":{"ref":<number>},
- * "protoObject":{"ref":<number>},
- * "prototypeObject":{"ref":<number>},
- * "namedInterceptor":<boolean>,
- * "indexedInterceptor":<boolean>,
- * "properties":[<properties>]}
- */
-JSONProtocolSerializer.prototype.serializeObject_ = function(mirror, content,
- details) {
- // Add general object properties.
- content.className = mirror.className();
- content.constructorFunction =
- this.serializeReference(mirror.constructorFunction());
- content.protoObject = this.serializeReference(mirror.protoObject());
- content.prototypeObject = this.serializeReference(mirror.prototypeObject());
-
- // Add flags to indicate whether there are interceptors.
- if (mirror.hasNamedInterceptor()) {
- content.namedInterceptor = true;
- }
- if (mirror.hasIndexedInterceptor()) {
- content.indexedInterceptor = true;
- }
-
- // Add function specific properties.
- if (mirror.isFunction()) {
- // Add function specific properties.
- content.name = mirror.name();
- if (!IS_UNDEFINED(mirror.inferredName())) {
- content.inferredName = mirror.inferredName();
- }
- content.resolved = mirror.resolved();
- if (mirror.resolved()) {
- content.source = mirror.source();
- }
- if (mirror.script()) {
- content.script = this.serializeReference(mirror.script());
- content.scriptId = mirror.script().id();
-
- serializeLocationFields(mirror.sourceLocation(), content);
- }
- }
-
- // Add date specific properties.
- if (mirror.isDate()) {
- // Add date specific properties.
- content.value = mirror.value();
- }
-
- // Add actual properties - named properties followed by indexed properties.
- var propertyNames = mirror.propertyNames(PropertyKind.Named);
- var propertyIndexes = mirror.propertyNames(PropertyKind.Indexed);
- var p = new Array(propertyNames.length + propertyIndexes.length);
- for (var i = 0; i < propertyNames.length; i++) {
- var propertyMirror = mirror.property(propertyNames[i]);
- p[i] = this.serializeProperty_(propertyMirror);
- if (details) {
- this.add_(propertyMirror.value());
- }
- }
- for (var i = 0; i < propertyIndexes.length; i++) {
- var propertyMirror = mirror.property(propertyIndexes[i]);
- p[propertyNames.length + i] = this.serializeProperty_(propertyMirror);
- if (details) {
- this.add_(propertyMirror.value());
- }
- }
- content.properties = p;
-}
-
-
-/**
- * Serialize location information to the following JSON format:
- *
- * "position":"<position>",
- * "line":"<line>",
- * "column":"<column>",
- *
- * @param {SourceLocation} location The location to serialize, may be undefined.
- */
-function serializeLocationFields (location, content) {
- if (!location) {
- return;
- }
- content.position = location.position;
- var line = location.line;
- if (!IS_UNDEFINED(line)) {
- content.line = line;
- }
- var column = location.column;
- if (!IS_UNDEFINED(column)) {
- content.column = column;
- }
-}
-
-
-/**
- * Serialize property information to the following JSON format for building the
- * array of properties.
- *
- * {"name":"<property name>",
- * "attributes":<number>,
- * "propertyType":<number>,
- * "ref":<number>}
- *
- * If the attribute for the property is PropertyAttribute.None it is not added.
- * If the propertyType for the property is PropertyType.Normal it is not added.
- * Here are a couple of examples.
- *
- * {"name":"hello","ref":1}
- * {"name":"length","attributes":7,"propertyType":3,"ref":2}
- *
- * @param {PropertyMirror} propertyMirror The property to serialize.
- * @returns {Object} Protocol object representing the property.
- */
-JSONProtocolSerializer.prototype.serializeProperty_ = function(propertyMirror) {
- var result = {};
-
- result.name = propertyMirror.name();
- var propertyValue = propertyMirror.value();
- if (this.inlineRefs_() && propertyValue.isValue()) {
- result.value = this.serializeReferenceWithDisplayData_(propertyValue);
- } else {
- if (propertyMirror.attributes() != PropertyAttribute.None) {
- result.attributes = propertyMirror.attributes();
- }
- if (propertyMirror.propertyType() != PropertyType.Normal) {
- result.propertyType = propertyMirror.propertyType();
- }
- result.ref = propertyValue.handle();
- }
- return result;
-}
-
-
-JSONProtocolSerializer.prototype.serializeFrame_ = function(mirror, content) {
- content.index = mirror.index();
- content.receiver = this.serializeReference(mirror.receiver());
- var func = mirror.func();
- content.func = this.serializeReference(func);
- if (func.script()) {
- content.script = this.serializeReference(func.script());
- }
- content.constructCall = mirror.isConstructCall();
- content.debuggerFrame = mirror.isDebuggerFrame();
- var x = new Array(mirror.argumentCount());
- for (var i = 0; i < mirror.argumentCount(); i++) {
- var arg = {};
- var argument_name = mirror.argumentName(i)
- if (argument_name) {
- arg.name = argument_name;
- }
- arg.value = this.serializeReference(mirror.argumentValue(i));
- x[i] = arg;
- }
- content.arguments = x;
- var x = new Array(mirror.localCount());
- for (var i = 0; i < mirror.localCount(); i++) {
- var local = {};
- local.name = mirror.localName(i);
- local.value = this.serializeReference(mirror.localValue(i));
- x[i] = local;
- }
- content.locals = x;
- serializeLocationFields(mirror.sourceLocation(), content);
- var source_line_text = mirror.sourceLineText();
- if (!IS_UNDEFINED(source_line_text)) {
- content.sourceLineText = source_line_text;
- }
-
- content.scopes = [];
- for (var i = 0; i < mirror.scopeCount(); i++) {
- var scope = mirror.scope(i);
- content.scopes.push({
- type: scope.scopeType(),
- index: i
- });
- }
-}
-
-
-JSONProtocolSerializer.prototype.serializeScope_ = function(mirror, content) {
- content.index = mirror.scopeIndex();
- content.frameIndex = mirror.frameIndex();
- content.type = mirror.scopeType();
- content.object = this.inlineRefs_() ?
- this.serializeValue(mirror.scopeObject()) :
- this.serializeReference(mirror.scopeObject());
-}
-
-
-/**
- * Convert a number to a protocol value. For all finite numbers the number
- * itself is returned. For non finite numbers NaN, Infinite and
- * -Infinite the string representation "NaN", "Infinite" or "-Infinite"
- * (not including the quotes) is returned.
- *
- * @param {number} value The number value to convert to a protocol value.
- * @returns {number|string} Protocol value.
- */
-function NumberToJSON_(value) {
- if (isNaN(value)) {
- return 'NaN';
- }
- if (!isFinite(value)) {
- if (value > 0) {
- return 'Infinity';
- } else {
- return '-Infinity';
- }
- }
- return value;
-}
diff --git a/src/mksnapshot.cc b/src/mksnapshot.cc
index 37cf263d..a30b4507 100644
--- a/src/mksnapshot.cc
+++ b/src/mksnapshot.cc
@@ -205,7 +205,6 @@ class CppByteSink : public i::SnapshotByteSink {
int main(int argc, char** argv) {
-
#ifdef ENABLE_LOGGING_AND_PROFILING
// By default, log code create information in the snapshot.
i::FLAG_log_code = true;
diff --git a/src/number-info.h b/src/number-info.h
new file mode 100644
index 00000000..c6f32e47
--- /dev/null
+++ b/src/number-info.h
@@ -0,0 +1,72 @@
+// Copyright 2010 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.
+
+#ifndef V8_NUMBER_INFO_H_
+#define V8_NUMBER_INFO_H_
+
+namespace v8 {
+namespace internal {
+
+class NumberInfo : public AllStatic {
+ public:
+ enum Type {
+ kUnknown = 0,
+ kNumber = 1,
+ kSmi = 3,
+ kHeapNumber = 5,
+ kUninitialized = 7
+ };
+
+ // Return the weakest (least precise) common type.
+ static Type Combine(Type a, Type b) {
+ // Make use of the order of enum values.
+ return static_cast<Type>(a & b);
+ }
+
+ static bool IsNumber(Type a) {
+ ASSERT(a != kUninitialized);
+ return ((a & kNumber) != 0);
+ }
+
+ static const char* ToString(Type a) {
+ switch (a) {
+ case kUnknown: return "UnknownType";
+ case kNumber: return "NumberType";
+ case kSmi: return "SmiType";
+ case kHeapNumber: return "HeapNumberType";
+ case kUninitialized:
+ UNREACHABLE();
+ return "UninitializedType";
+ }
+ UNREACHABLE();
+ return "Unreachable code";
+ }
+};
+
+} } // namespace v8::internal
+
+#endif // V8_NUMBER_INFO_H_
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index ded213b2..8f26f742 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -1033,6 +1033,8 @@ void FunctionTemplateInfo::FunctionTemplateInfoVerify() {
void FunctionTemplateInfo::FunctionTemplateInfoPrint() {
HeapObject::PrintHeader("FunctionTemplateInfo");
+ PrintF("\n - class name: ");
+ class_name()->ShortPrint();
PrintF("\n - tag: ");
tag()->ShortPrint();
PrintF("\n - property_list: ");
diff --git a/src/objects.cc b/src/objects.cc
index a8328ac0..99532aca 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -219,7 +219,7 @@ Object* JSObject::GetPropertyWithFailedAccessCheck(
LookupResult* result,
String* name,
PropertyAttributes* attributes) {
- if (result->IsValid()) {
+ if (result->IsProperty()) {
switch (result->type()) {
case CALLBACKS: {
// Only allow API accessors.
@@ -242,7 +242,7 @@ Object* JSObject::GetPropertyWithFailedAccessCheck(
// Search ALL_CAN_READ accessors in prototype chain.
LookupResult r;
result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
- if (r.IsValid()) {
+ if (r.IsProperty()) {
return GetPropertyWithFailedAccessCheck(receiver,
&r,
name,
@@ -255,16 +255,16 @@ Object* JSObject::GetPropertyWithFailedAccessCheck(
// No access check in GetPropertyAttributeWithInterceptor.
LookupResult r;
result->holder()->LookupRealNamedProperty(name, &r);
- if (r.IsValid()) {
+ if (r.IsProperty()) {
return GetPropertyWithFailedAccessCheck(receiver,
&r,
name,
attributes);
}
- }
- default: {
break;
}
+ default:
+ UNREACHABLE();
}
}
@@ -280,7 +280,7 @@ PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck(
LookupResult* result,
String* name,
bool continue_search) {
- if (result->IsValid()) {
+ if (result->IsProperty()) {
switch (result->type()) {
case CALLBACKS: {
// Only allow API accessors.
@@ -301,7 +301,7 @@ PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck(
// Search ALL_CAN_READ accessors in prototype chain.
LookupResult r;
result->holder()->LookupRealNamedPropertyInPrototypes(name, &r);
- if (r.IsValid()) {
+ if (r.IsProperty()) {
return GetPropertyAttributeWithFailedAccessCheck(receiver,
&r,
name,
@@ -319,7 +319,7 @@ PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck(
} else {
result->holder()->LocalLookupRealNamedProperty(name, &r);
}
- if (r.IsValid()) {
+ if (r.IsProperty()) {
return GetPropertyAttributeWithFailedAccessCheck(receiver,
&r,
name,
@@ -328,9 +328,8 @@ PropertyAttributes JSObject::GetPropertyAttributeWithFailedAccessCheck(
break;
}
- default: {
- break;
- }
+ default:
+ UNREACHABLE();
}
}
@@ -456,7 +455,7 @@ Object* Object::GetProperty(Object* receiver,
// holder will always be the interceptor holder and the search may
// only continue with a current object just after the interceptor
// holder in the prototype chain.
- Object* last = result->IsValid() ? result->holder() : Heap::null_value();
+ Object* last = result->IsProperty() ? result->holder() : Heap::null_value();
for (Object* current = this; true; current = current->GetPrototype()) {
if (current->IsAccessCheckNeeded()) {
// Check if we're allowed to read from the current object. Note
@@ -1408,8 +1407,12 @@ Object* JSObject::SetPropertyPostInterceptor(String* name,
// Check local property, ignore interceptor.
LookupResult result;
LocalLookupRealNamedProperty(name, &result);
- if (result.IsValid()) return SetProperty(&result, name, value, attributes);
- // Add real property.
+ if (result.IsFound()) {
+ // An existing property, a map transition or a null descriptor was
+ // found. Use set property to handle all these cases.
+ return SetProperty(&result, name, value, attributes);
+ }
+ // Add a new real property.
return AddProperty(name, value, attributes);
}
@@ -1641,8 +1644,8 @@ void JSObject::LookupCallbackSetterInPrototypes(String* name,
pt != Heap::null_value();
pt = pt->GetPrototype()) {
JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
- if (result->IsValid()) {
- if (!result->IsTransitionType() && result->IsReadOnly()) {
+ if (result->IsProperty()) {
+ if (result->IsReadOnly()) {
result->NotFound();
return;
}
@@ -1703,7 +1706,11 @@ void JSObject::LocalLookupRealNamedProperty(String* name,
if (HasFastProperties()) {
LookupInDescriptor(name, result);
- if (result->IsValid()) {
+ if (result->IsFound()) {
+ // A property, a map transition or a null descriptor was found.
+ // We return all of these result types because
+ // LocalLookupRealNamedProperty is used when setting properties
+ // where map transitions and null descriptors are handled.
ASSERT(result->holder() == this && result->type() != NORMAL);
// Disallow caching for uninitialized constants. These can only
// occur as fields.
@@ -1752,16 +1759,7 @@ void JSObject::LookupRealNamedPropertyInPrototypes(String* name,
pt != Heap::null_value();
pt = JSObject::cast(pt)->GetPrototype()) {
JSObject::cast(pt)->LocalLookupRealNamedProperty(name, result);
- if (result->IsValid()) {
- switch (result->type()) {
- case NORMAL:
- case FIELD:
- case CONSTANT_FUNCTION:
- case CALLBACKS:
- return;
- default: break;
- }
- }
+ if (result->IsProperty() && (result->type() != INTERCEPTOR)) return;
}
result->NotFound();
}
@@ -1847,14 +1845,15 @@ Object* JSObject::SetProperty(LookupResult* result,
// accessor that wants to handle the property.
LookupResult accessor_result;
LookupCallbackSetterInPrototypes(name, &accessor_result);
- if (accessor_result.IsValid()) {
+ if (accessor_result.IsProperty()) {
return SetPropertyWithCallback(accessor_result.GetCallbackObject(),
name,
value,
accessor_result.holder());
}
}
- if (result->IsNotFound()) {
+ if (!result->IsFound()) {
+ // Neither properties nor transitions found.
return AddProperty(name, value, attributes);
}
if (result->IsReadOnly() && result->IsProperty()) return value;
@@ -1913,15 +1912,12 @@ Object* JSObject::IgnoreAttributesAndSetLocalProperty(
// Make sure that the top context does not change when doing callbacks or
// interceptor calls.
AssertNoContextChange ncc;
- // ADDED TO CLONE
- LookupResult result_struct;
- LocalLookup(name, &result_struct);
- LookupResult* result = &result_struct;
- // END ADDED TO CLONE
+ LookupResult result;
+ LocalLookup(name, &result);
// Check access rights if needed.
if (IsAccessCheckNeeded()
- && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
- return SetPropertyWithFailedAccessCheck(result, name, value);
+ && !Top::MayNamedAccess(this, name, v8::ACCESS_SET)) {
+ return SetPropertyWithFailedAccessCheck(&result, name, value);
}
if (IsJSGlobalProxy()) {
@@ -1935,28 +1931,31 @@ Object* JSObject::IgnoreAttributesAndSetLocalProperty(
}
// Check for accessor in prototype chain removed here in clone.
- if (result->IsNotFound()) {
+ if (!result.IsFound()) {
+ // Neither properties nor transitions found.
return AddProperty(name, value, attributes);
}
+ PropertyDetails details = PropertyDetails(attributes, NORMAL);
+
// Check of IsReadOnly removed from here in clone.
- switch (result->type()) {
+ switch (result.type()) {
case NORMAL:
- return SetNormalizedProperty(result, value);
+ return SetNormalizedProperty(name, value, details);
case FIELD:
- return FastPropertyAtPut(result->GetFieldIndex(), value);
+ return FastPropertyAtPut(result.GetFieldIndex(), value);
case MAP_TRANSITION:
- if (attributes == result->GetAttributes()) {
+ if (attributes == result.GetAttributes()) {
// Only use map transition if the attributes match.
- return AddFastPropertyUsingMap(result->GetTransitionMap(),
+ return AddFastPropertyUsingMap(result.GetTransitionMap(),
name,
value);
}
return ConvertDescriptorToField(name, value, attributes);
case CONSTANT_FUNCTION:
// Only replace the function if necessary.
- if (value == result->GetConstantFunction()) return value;
+ if (value == result.GetConstantFunction()) return value;
// Preserve the attributes of this existing property.
- attributes = result->GetAttributes();
+ attributes = result.GetAttributes();
return ConvertDescriptorToField(name, value, attributes);
case CALLBACKS:
case INTERCEPTOR:
@@ -2072,7 +2071,7 @@ PropertyAttributes JSObject::GetPropertyAttribute(JSObject* receiver,
name,
continue_search);
}
- if (result->IsValid()) {
+ if (result->IsProperty()) {
switch (result->type()) {
case NORMAL: // fall through
case FIELD:
@@ -2082,13 +2081,8 @@ PropertyAttributes JSObject::GetPropertyAttribute(JSObject* receiver,
case INTERCEPTOR:
return result->holder()->
GetPropertyAttributeWithInterceptor(receiver, name, continue_search);
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- case NULL_DESCRIPTOR:
- return ABSENT;
default:
UNREACHABLE();
- break;
}
}
return ABSENT;
@@ -2261,7 +2255,7 @@ Object* JSObject::DeletePropertyPostInterceptor(String* name, DeleteMode mode) {
// Check local property, ignore interceptor.
LookupResult result;
LocalLookupRealNamedProperty(name, &result);
- if (!result.IsValid()) return Heap::true_value();
+ if (!result.IsProperty()) return Heap::true_value();
// Normalize object if needed.
Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
@@ -2445,7 +2439,7 @@ Object* JSObject::DeleteProperty(String* name, DeleteMode mode) {
} else {
LookupResult result;
LocalLookup(name, &result);
- if (!result.IsValid()) return Heap::true_value();
+ if (!result.IsProperty()) return Heap::true_value();
// Ignore attributes if forcing a deletion.
if (result.IsDontDelete() && mode != FORCE_DELETION) {
return Heap::false_value();
@@ -2675,7 +2669,7 @@ void JSObject::Lookup(String* name, LookupResult* result) {
current != Heap::null_value();
current = JSObject::cast(current)->GetPrototype()) {
JSObject::cast(current)->LocalLookup(name, result);
- if (result->IsValid() && !result->IsTransitionType()) return;
+ if (result->IsProperty()) return;
}
result->NotFound();
}
@@ -2687,7 +2681,7 @@ void JSObject::LookupCallback(String* name, LookupResult* result) {
current != Heap::null_value();
current = JSObject::cast(current)->GetPrototype()) {
JSObject::cast(current)->LocalLookupRealNamedProperty(name, result);
- if (result->IsValid() && result->type() == CALLBACKS) return;
+ if (result->IsProperty() && result->type() == CALLBACKS) return;
}
result->NotFound();
}
@@ -2717,7 +2711,7 @@ Object* JSObject::DefineGetterSetter(String* name,
// cause security problems.
LookupResult callback_result;
LookupCallback(name, &callback_result);
- if (callback_result.IsValid()) {
+ if (callback_result.IsFound()) {
Object* obj = callback_result.GetCallbackObject();
if (obj->IsAccessorInfo() &&
AccessorInfo::cast(obj)->prohibits_overwriting()) {
@@ -2768,11 +2762,16 @@ Object* JSObject::DefineGetterSetter(String* name,
// Lookup the name.
LookupResult result;
LocalLookup(name, &result);
- if (result.IsValid()) {
+ if (result.IsProperty()) {
if (result.IsReadOnly()) return Heap::undefined_value();
if (result.type() == CALLBACKS) {
Object* obj = result.GetCallbackObject();
if (obj->IsFixedArray()) {
+ // The object might be in fast mode even though it has
+ // a getter/setter.
+ Object* ok = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
+ if (ok->IsFailure()) return ok;
+
PropertyDetails details = PropertyDetails(attributes, CALLBACKS);
SetNormalizedProperty(name, obj, details);
return obj;
@@ -2885,7 +2884,7 @@ Object* JSObject::LookupAccessor(String* name, bool is_getter) {
obj = JSObject::cast(obj)->GetPrototype()) {
LookupResult result;
JSObject::cast(obj)->LocalLookup(name, &result);
- if (result.IsValid()) {
+ if (result.IsProperty()) {
if (result.IsReadOnly()) return Heap::undefined_value();
if (result.type() == CALLBACKS) {
Object* obj = result.GetCallbackObject();
@@ -4753,6 +4752,40 @@ int SharedFunctionInfo::CalculateInObjectProperties() {
}
+bool SharedFunctionInfo::CanGenerateInlineConstructor(Object* prototype) {
+ // Check the basic conditions for generating inline constructor code.
+ if (!FLAG_inline_new
+ || !has_only_simple_this_property_assignments()
+ || this_property_assignments_count() == 0) {
+ return false;
+ }
+
+ // If the prototype is null inline constructors cause no problems.
+ if (!prototype->IsJSObject()) {
+ ASSERT(prototype->IsNull());
+ return true;
+ }
+
+ // Traverse the proposed prototype chain looking for setters for properties of
+ // the same names as are set by the inline constructor.
+ for (Object* obj = prototype;
+ obj != Heap::null_value();
+ obj = obj->GetPrototype()) {
+ JSObject* js_object = JSObject::cast(obj);
+ for (int i = 0; i < this_property_assignments_count(); i++) {
+ LookupResult result;
+ String* name = GetThisPropertyAssignmentName(i);
+ js_object->LocalLookupRealNamedProperty(name, &result);
+ if (result.IsProperty() && result.type() == CALLBACKS) {
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
void SharedFunctionInfo::SetThisPropertyAssignmentsInfo(
bool only_simple_this_property_assignments,
FixedArray* assignments) {
@@ -4808,7 +4841,6 @@ Object* SharedFunctionInfo::GetThisPropertyAssignmentConstant(int index) {
}
-
// Support function for printing the source code to a StringStream
// without any allocation in the heap.
void SharedFunctionInfo::SourceCodePrint(StringStream* accumulator,
@@ -5286,6 +5318,48 @@ Object* JSObject::SetElementsLength(Object* len) {
}
+Object* JSObject::SetPrototype(Object* value,
+ bool skip_hidden_prototypes) {
+ // Silently ignore the change if value is not a JSObject or null.
+ // SpiderMonkey behaves this way.
+ if (!value->IsJSObject() && !value->IsNull()) return value;
+
+ // Before we can set the prototype we need to be sure
+ // prototype cycles are prevented.
+ // It is sufficient to validate that the receiver is not in the new prototype
+ // chain.
+ for (Object* pt = value; pt != Heap::null_value(); pt = pt->GetPrototype()) {
+ if (JSObject::cast(pt) == this) {
+ // Cycle detected.
+ HandleScope scope;
+ return Top::Throw(*Factory::NewError("cyclic_proto",
+ HandleVector<Object>(NULL, 0)));
+ }
+ }
+
+ JSObject* real_receiver = this;
+
+ if (skip_hidden_prototypes) {
+ // Find the first object in the chain whose prototype object is not
+ // hidden and set the new prototype on that object.
+ Object* current_proto = real_receiver->GetPrototype();
+ while (current_proto->IsJSObject() &&
+ JSObject::cast(current_proto)->map()->is_hidden_prototype()) {
+ real_receiver = JSObject::cast(current_proto);
+ current_proto = current_proto->GetPrototype();
+ }
+ }
+
+ // Set the new prototype of the object.
+ Object* new_map = real_receiver->map()->CopyDropTransitions();
+ if (new_map->IsFailure()) return new_map;
+ Map::cast(new_map)->set_prototype(value);
+ real_receiver->set_map(Map::cast(new_map));
+
+ return value;
+}
+
+
bool JSObject::HasElementPostInterceptor(JSObject* receiver, uint32_t index) {
switch (GetElementsKind()) {
case FAST_ELEMENTS: {
@@ -6103,7 +6177,9 @@ Object* JSObject::GetPropertyPostInterceptor(JSObject* receiver,
// Check local property in holder, ignore interceptor.
LookupResult result;
LocalLookupRealNamedProperty(name, &result);
- if (result.IsValid()) return GetProperty(receiver, &result, name, attributes);
+ if (result.IsProperty()) {
+ return GetProperty(receiver, &result, name, attributes);
+ }
// Continue searching via the prototype chain.
Object* pt = GetPrototype();
*attributes = ABSENT;
@@ -6119,8 +6195,10 @@ Object* JSObject::GetLocalPropertyPostInterceptor(
// Check local property in holder, ignore interceptor.
LookupResult result;
LocalLookupRealNamedProperty(name, &result);
- if (!result.IsValid()) return Heap::undefined_value();
- return GetProperty(receiver, &result, name, attributes);
+ if (result.IsProperty()) {
+ return GetProperty(receiver, &result, name, attributes);
+ }
+ return Heap::undefined_value();
}
@@ -6172,24 +6250,7 @@ bool JSObject::HasRealNamedProperty(String* key) {
LookupResult result;
LocalLookupRealNamedProperty(key, &result);
- if (result.IsValid()) {
- switch (result.type()) {
- case NORMAL: // fall through.
- case FIELD: // fall through.
- case CALLBACKS: // fall through.
- case CONSTANT_FUNCTION:
- return true;
- case INTERCEPTOR:
- case MAP_TRANSITION:
- case CONSTANT_TRANSITION:
- case NULL_DESCRIPTOR:
- return false;
- default:
- UNREACHABLE();
- }
- }
-
- return false;
+ return result.IsProperty() && (result.type() != INTERCEPTOR);
}
@@ -6251,7 +6312,7 @@ bool JSObject::HasRealNamedCallbackProperty(String* key) {
LookupResult result;
LocalLookupRealNamedProperty(key, &result);
- return result.IsValid() && (result.type() == CALLBACKS);
+ return result.IsProperty() && (result.type() == CALLBACKS);
}
diff --git a/src/objects.h b/src/objects.h
index 48936661..00304968 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -179,7 +179,7 @@ class PropertyDetails BASE_EMBEDDED {
class TypeField: public BitField<PropertyType, 0, 3> {};
class AttributesField: public BitField<PropertyAttributes, 3, 3> {};
class DeletedField: public BitField<uint32_t, 6, 1> {};
- class IndexField: public BitField<uint32_t, 7, 31-7> {};
+ class IndexField: public BitField<uint32_t, 7, 32-7> {};
static const int kInitialIndex = 1;
private:
@@ -1301,6 +1301,9 @@ class JSObject: public HeapObject {
// Return the object's prototype (might be Heap::null_value()).
inline Object* GetPrototype();
+ // Set the object's prototype (only JSObject and null are allowed).
+ Object* SetPrototype(Object* value, bool skip_hidden_prototypes);
+
// Tells whether the index'th element is present.
inline bool HasElement(uint32_t index);
bool HasElementWithReceiver(JSObject* receiver, uint32_t index);
@@ -3200,6 +3203,10 @@ class SharedFunctionInfo: public HeapObject {
inline bool try_full_codegen();
inline void set_try_full_codegen(bool flag);
+ // Check whether a inlined constructor can be generated with the given
+ // prototype.
+ bool CanGenerateInlineConstructor(Object* prototype);
+
// For functions which only contains this property assignments this provides
// access to the names for the properties assigned.
DECL_ACCESSORS(this_property_assignments, Object)
diff --git a/src/parser.cc b/src/parser.cc
index b06d86f5..5058296d 100644
--- a/src/parser.cc
+++ b/src/parser.cc
@@ -1690,7 +1690,8 @@ void* Parser::ParseSourceElements(ZoneListWrapper<Statement>* processor,
// Propagate the collected information on this property assignments.
if (top_scope_->is_function_scope()) {
bool only_simple_this_property_assignments =
- this_property_assignment_finder.only_simple_this_property_assignments();
+ this_property_assignment_finder.only_simple_this_property_assignments()
+ && top_scope_->declarations()->length() == 0;
if (only_simple_this_property_assignments) {
temp_scope_->SetThisPropertyAssignmentInfo(
only_simple_this_property_assignments,
diff --git a/src/platform-freebsd.cc b/src/platform-freebsd.cc
index ff757768..a9787776 100644
--- a/src/platform-freebsd.cc
+++ b/src/platform-freebsd.cc
@@ -192,7 +192,8 @@ void OS::Abort() {
void OS::DebugBreak() {
-#if defined(__arm__) || defined(__thumb__)
+#if (defined(__arm__) || defined(__thumb__)) && \
+ defined(CAN_USE_ARMV5_INSTRUCTIONS)
asm("bkpt 0");
#else
asm("int $3");
diff --git a/src/platform-linux.cc b/src/platform-linux.cc
index ef4ae17b..8cc513d8 100644
--- a/src/platform-linux.cc
+++ b/src/platform-linux.cc
@@ -266,7 +266,8 @@ void OS::Abort() {
void OS::DebugBreak() {
// TODO(lrn): Introduce processor define for runtime system (!= V8_ARCH_x,
// which is the architecture of generated code).
-#if defined(__arm__) || defined(__thumb__)
+#if (defined(__arm__) || defined(__thumb__)) && \
+ defined(CAN_USE_ARMV5_INSTRUCTIONS)
asm("bkpt 0");
#elif defined(__mips__)
asm("break");
@@ -721,11 +722,9 @@ static inline bool IsVmThread() {
static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
- return;
-/*#ifndef V8_HOST_ARCH_MIPS
+#ifndef V8_HOST_ARCH_MIPS
USE(info);
if (signal != SIGPROF) return;
- if (!IsVmThread()) return;
if (active_sampler_ == NULL) return;
TickSample sample;
@@ -766,7 +765,7 @@ static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) {
sample.state = Logger::state();
active_sampler_->Tick(&sample);
-#endif*/
+#endif
}
@@ -805,7 +804,7 @@ void Sampler::Start() {
sa.sa_sigaction = ProfilerSignalHandler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_SIGINFO;
- if (sigaction(SIGALRM, &sa, &data_->old_signal_handler_) != 0) return;
+ if (sigaction(SIGPROF, &sa, &data_->old_signal_handler_) != 0) return;
data_->signal_handler_installed_ = true;
// Set the itimer to generate a tick for each interval.
@@ -814,7 +813,7 @@ void Sampler::Start() {
itimer.it_interval.tv_usec = (interval_ % 1000) * 1000;
itimer.it_value.tv_sec = itimer.it_interval.tv_sec;
itimer.it_value.tv_usec = itimer.it_interval.tv_usec;
- setitimer(ITIMER_REAL, &itimer, &data_->old_timer_value_);
+ setitimer(ITIMER_PROF, &itimer, &data_->old_timer_value_);
// Set this sampler as the active sampler.
active_sampler_ = this;
diff --git a/src/platform-openbsd.cc b/src/platform-openbsd.cc
index 62e60044..f96e769c 100644
--- a/src/platform-openbsd.cc
+++ b/src/platform-openbsd.cc
@@ -190,7 +190,8 @@ void OS::Abort() {
void OS::DebugBreak() {
-#if defined(__arm__) || defined(__thumb__)
+#if (defined(__arm__) || defined(__thumb__)) && \
+ defined(CAN_USE_ARMV5_INSTRUCTIONS)
asm("bkpt 0");
#else
asm("int $3");
diff --git a/src/property.cc b/src/property.cc
index caa73975..b579b687 100644
--- a/src/property.cc
+++ b/src/property.cc
@@ -33,7 +33,7 @@ namespace internal {
#ifdef DEBUG
void LookupResult::Print() {
- if (!IsValid()) {
+ if (!IsFound()) {
PrintF("Not Found\n");
return;
}
diff --git a/src/property.h b/src/property.h
index b993af11..15a56528 100644
--- a/src/property.h
+++ b/src/property.h
@@ -201,23 +201,17 @@ class LookupResult BASE_EMBEDDED {
}
JSObject* holder() {
- ASSERT(IsValid());
+ ASSERT(IsFound());
return holder_;
}
PropertyType type() {
- ASSERT(IsValid());
+ ASSERT(IsFound());
return details_.type();
}
- bool IsTransitionType() {
- PropertyType t = type();
- if (t == MAP_TRANSITION || t == CONSTANT_TRANSITION) return true;
- return false;
- }
-
PropertyAttributes GetAttributes() {
- ASSERT(IsValid());
+ ASSERT(IsFound());
return details_.attributes();
}
@@ -229,14 +223,17 @@ class LookupResult BASE_EMBEDDED {
bool IsDontDelete() { return details_.IsDontDelete(); }
bool IsDontEnum() { return details_.IsDontEnum(); }
bool IsDeleted() { return details_.IsDeleted(); }
+ bool IsFound() { return lookup_type_ != NOT_FOUND; }
- bool IsValid() { return lookup_type_ != NOT_FOUND; }
- bool IsNotFound() { return lookup_type_ == NOT_FOUND; }
-
- // Tells whether the result is a property.
- // Excluding transitions and the null descriptor.
+ // Is the result is a property excluding transitions and the null
+ // descriptor?
bool IsProperty() {
- return IsValid() && type() < FIRST_PHANTOM_PROPERTY_TYPE;
+ return IsFound() && (type() < FIRST_PHANTOM_PROPERTY_TYPE);
+ }
+
+ // Is the result a property or a transition?
+ bool IsPropertyOrTransition() {
+ return IsFound() && (type() != NULL_DESCRIPTOR);
}
bool IsCacheable() { return cacheable_; }
diff --git a/src/regexp-delay.js b/src/regexp-delay.js
deleted file mode 100644
index 7bec455d..00000000
--- a/src/regexp-delay.js
+++ /dev/null
@@ -1,406 +0,0 @@
-// Copyright 2006-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.
-
-// Expect $Object = global.Object;
-// Expect $Array = global.Array;
-
-const $RegExp = global.RegExp;
-
-// A recursive descent parser for Patterns according to the grammar of
-// ECMA-262 15.10.1, with deviations noted below.
-function DoConstructRegExp(object, pattern, flags, isConstructorCall) {
- // RegExp : Called as constructor; see ECMA-262, section 15.10.4.
- if (IS_REGEXP(pattern)) {
- if (!IS_UNDEFINED(flags)) {
- throw MakeTypeError('regexp_flags', []);
- }
- flags = (pattern.global ? 'g' : '')
- + (pattern.ignoreCase ? 'i' : '')
- + (pattern.multiline ? 'm' : '');
- pattern = pattern.source;
- }
-
- pattern = IS_UNDEFINED(pattern) ? '' : ToString(pattern);
- flags = IS_UNDEFINED(flags) ? '' : ToString(flags);
-
- var global = false;
- var ignoreCase = false;
- var multiline = false;
-
- for (var i = 0; i < flags.length; i++) {
- var c = StringCharAt.call(flags, i);
- switch (c) {
- case 'g':
- // Allow duplicate flags to be consistent with JSC and others.
- global = true;
- break;
- case 'i':
- ignoreCase = true;
- break;
- case 'm':
- multiline = true;
- break;
- default:
- // Ignore flags that have no meaning to be consistent with
- // JSC.
- break;
- }
- }
-
- if (isConstructorCall) {
- // ECMA-262, section 15.10.7.1.
- %SetProperty(object, 'source', pattern,
- DONT_DELETE | READ_ONLY | DONT_ENUM);
-
- // ECMA-262, section 15.10.7.2.
- %SetProperty(object, 'global', global, DONT_DELETE | READ_ONLY | DONT_ENUM);
-
- // ECMA-262, section 15.10.7.3.
- %SetProperty(object, 'ignoreCase', ignoreCase,
- DONT_DELETE | READ_ONLY | DONT_ENUM);
-
- // ECMA-262, section 15.10.7.4.
- %SetProperty(object, 'multiline', multiline,
- DONT_DELETE | READ_ONLY | DONT_ENUM);
-
- // ECMA-262, section 15.10.7.5.
- %SetProperty(object, 'lastIndex', 0, DONT_DELETE | DONT_ENUM);
- } else { // RegExp is being recompiled via RegExp.prototype.compile.
- %IgnoreAttributesAndSetProperty(object, 'source', pattern);
- %IgnoreAttributesAndSetProperty(object, 'global', global);
- %IgnoreAttributesAndSetProperty(object, 'ignoreCase', ignoreCase);
- %IgnoreAttributesAndSetProperty(object, 'multiline', multiline);
- %IgnoreAttributesAndSetProperty(object, 'lastIndex', 0);
- }
-
- // Call internal function to compile the pattern.
- %RegExpCompile(object, pattern, flags);
-}
-
-
-function RegExpConstructor(pattern, flags) {
- if (%_IsConstructCall()) {
- DoConstructRegExp(this, pattern, flags, true);
- } else {
- // RegExp : Called as function; see ECMA-262, section 15.10.3.1.
- if (IS_REGEXP(pattern) && IS_UNDEFINED(flags)) {
- return pattern;
- }
- return new $RegExp(pattern, flags);
- }
-}
-
-
-// Deprecated RegExp.prototype.compile method. We behave like the constructor
-// were called again. In SpiderMonkey, this method returns the regexp object.
-// In JSC, it returns undefined. For compatibility with JSC, we match their
-// behavior.
-function CompileRegExp(pattern, flags) {
- // Both JSC and SpiderMonkey treat a missing pattern argument as the
- // empty subject string, and an actual undefined value passed as the
- // pattern as the string 'undefined'. Note that JSC is inconsistent
- // here, treating undefined values differently in
- // RegExp.prototype.compile and in the constructor, where they are
- // the empty string. For compatibility with JSC, we match their
- // behavior.
- if (IS_UNDEFINED(pattern) && %_ArgumentsLength() != 0) {
- DoConstructRegExp(this, 'undefined', flags, false);
- } else {
- DoConstructRegExp(this, pattern, flags, false);
- }
-}
-
-
-function DoRegExpExec(regexp, string, index) {
- return %_RegExpExec(regexp, string, index, lastMatchInfo);
-}
-
-
-function RegExpExec(string) {
- if (!IS_REGEXP(this)) {
- throw MakeTypeError('method_called_on_incompatible',
- ['RegExp.prototype.exec', this]);
- }
- if (%_ArgumentsLength() == 0) {
- var regExpInput = LAST_INPUT(lastMatchInfo);
- if (IS_UNDEFINED(regExpInput)) {
- throw MakeError('no_input_to_regexp', [this]);
- }
- string = regExpInput;
- }
- var s = ToString(string);
- var length = s.length;
- var lastIndex = this.lastIndex;
- var i = this.global ? TO_INTEGER(lastIndex) : 0;
-
- if (i < 0 || i > s.length) {
- this.lastIndex = 0;
- return null;
- }
-
- %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]);
- // matchIndices is either null or the lastMatchInfo array.
- var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
-
- if (matchIndices == null) {
- if (this.global) this.lastIndex = 0;
- return matchIndices; // no match
- }
-
- var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
- var result = new $Array(numResults);
- for (var i = 0; i < numResults; i++) {
- var matchStart = lastMatchInfo[CAPTURE(i << 1)];
- var matchEnd = lastMatchInfo[CAPTURE((i << 1) + 1)];
- if (matchStart != -1 && matchEnd != -1) {
- result[i] = SubString(s, matchStart, matchEnd);
- } else {
- // Make sure the element is present. Avoid reading the undefined
- // property from the global object since this may change.
- result[i] = void 0;
- }
- }
-
- if (this.global)
- this.lastIndex = lastMatchInfo[CAPTURE1];
- result.index = lastMatchInfo[CAPTURE0];
- result.input = s;
- return result;
-}
-
-
-// Section 15.10.6.3 doesn't actually make sense, but the intention seems to be
-// that test is defined in terms of String.prototype.exec. However, it probably
-// means the original value of String.prototype.exec, which is what everybody
-// else implements.
-function RegExpTest(string) {
- if (!IS_REGEXP(this)) {
- throw MakeTypeError('method_called_on_incompatible',
- ['RegExp.prototype.test', this]);
- }
- if (%_ArgumentsLength() == 0) {
- var regExpInput = LAST_INPUT(lastMatchInfo);
- if (IS_UNDEFINED(regExpInput)) {
- throw MakeError('no_input_to_regexp', [this]);
- }
- string = regExpInput;
- }
- var s = ToString(string);
- var length = s.length;
- var lastIndex = this.lastIndex;
- var i = this.global ? TO_INTEGER(lastIndex) : 0;
-
- if (i < 0 || i > s.length) {
- this.lastIndex = 0;
- return false;
- }
-
- %_Log('regexp', 'regexp-exec,%0r,%1S,%2i', [this, s, lastIndex]);
- // matchIndices is either null or the lastMatchInfo array.
- var matchIndices = %_RegExpExec(this, s, i, lastMatchInfo);
-
- if (matchIndices == null) {
- if (this.global) this.lastIndex = 0;
- return false;
- }
-
- if (this.global) this.lastIndex = lastMatchInfo[CAPTURE1];
- return true;
-}
-
-
-function RegExpToString() {
- // If this.source is an empty string, output /(?:)/.
- // http://bugzilla.mozilla.org/show_bug.cgi?id=225550
- // ecma_2/RegExp/properties-001.js.
- var src = this.source ? this.source : '(?:)';
- var result = '/' + src + '/';
- if (this.global)
- result += 'g';
- if (this.ignoreCase)
- result += 'i';
- if (this.multiline)
- result += 'm';
- return result;
-}
-
-
-// Getters for the static properties lastMatch, lastParen, leftContext, and
-// rightContext of the RegExp constructor. The properties are computed based
-// on the captures array of the last successful match and the subject string
-// of the last successful match.
-function RegExpGetLastMatch() {
- var regExpSubject = LAST_SUBJECT(lastMatchInfo);
- return SubString(regExpSubject,
- lastMatchInfo[CAPTURE0],
- lastMatchInfo[CAPTURE1]);
-}
-
-
-function RegExpGetLastParen() {
- var length = NUMBER_OF_CAPTURES(lastMatchInfo);
- if (length <= 2) return ''; // There were no captures.
- // We match the SpiderMonkey behavior: return the substring defined by the
- // last pair (after the first pair) of elements of the capture array even if
- // it is empty.
- var regExpSubject = LAST_SUBJECT(lastMatchInfo);
- var start = lastMatchInfo[CAPTURE(length - 2)];
- var end = lastMatchInfo[CAPTURE(length - 1)];
- if (start != -1 && end != -1) {
- return SubString(regExpSubject, start, end);
- }
- return "";
-}
-
-
-function RegExpGetLeftContext() {
- return SubString(LAST_SUBJECT(lastMatchInfo),
- 0,
- lastMatchInfo[CAPTURE0]);
-}
-
-
-function RegExpGetRightContext() {
- var subject = LAST_SUBJECT(lastMatchInfo);
- return SubString(subject,
- lastMatchInfo[CAPTURE1],
- subject.length);
-}
-
-
-// The properties $1..$9 are the first nine capturing substrings of the last
-// successful match, or ''. The function RegExpMakeCaptureGetter will be
-// called with indices from 1 to 9.
-function RegExpMakeCaptureGetter(n) {
- return function() {
- var index = n * 2;
- if (index >= NUMBER_OF_CAPTURES(lastMatchInfo)) return '';
- var matchStart = lastMatchInfo[CAPTURE(index)];
- var matchEnd = lastMatchInfo[CAPTURE(index + 1)];
- if (matchStart == -1 || matchEnd == -1) return '';
- return SubString(LAST_SUBJECT(lastMatchInfo), matchStart, matchEnd);
- };
-}
-
-
-// Property of the builtins object for recording the result of the last
-// regexp match. The property lastMatchInfo includes the matchIndices
-// array of the last successful regexp match (an array of start/end index
-// pairs for the match and all the captured substrings), the invariant is
-// that there are at least two capture indeces. The array also contains
-// the subject string for the last successful match.
-var lastMatchInfo = [
- 2, // REGEXP_NUMBER_OF_CAPTURES
- "", // Last subject.
- void 0, // Last input - settable with RegExpSetInput.
- 0, // REGEXP_FIRST_CAPTURE + 0
- 0, // REGEXP_FIRST_CAPTURE + 1
-];
-
-// -------------------------------------------------------------------
-
-function SetupRegExp() {
- %FunctionSetInstanceClassName($RegExp, 'RegExp');
- %FunctionSetPrototype($RegExp, new $Object());
- %SetProperty($RegExp.prototype, 'constructor', $RegExp, DONT_ENUM);
- %SetCode($RegExp, RegExpConstructor);
-
- InstallFunctions($RegExp.prototype, DONT_ENUM, $Array(
- "exec", RegExpExec,
- "test", RegExpTest,
- "toString", RegExpToString,
- "compile", CompileRegExp
- ));
-
- // The length of compile is 1 in SpiderMonkey.
- %FunctionSetLength($RegExp.prototype.compile, 1);
-
- // The properties input, $input, and $_ are aliases for each other. When this
- // value is set the value it is set to is coerced to a string.
- // Getter and setter for the input.
- function RegExpGetInput() {
- var regExpInput = LAST_INPUT(lastMatchInfo);
- return IS_UNDEFINED(regExpInput) ? "" : regExpInput;
- }
- function RegExpSetInput(string) {
- LAST_INPUT(lastMatchInfo) = ToString(string);
- };
-
- %DefineAccessor($RegExp, 'input', GETTER, RegExpGetInput, DONT_DELETE);
- %DefineAccessor($RegExp, 'input', SETTER, RegExpSetInput, DONT_DELETE);
- %DefineAccessor($RegExp, '$_', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE);
- %DefineAccessor($RegExp, '$_', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE);
- %DefineAccessor($RegExp, '$input', GETTER, RegExpGetInput, DONT_ENUM | DONT_DELETE);
- %DefineAccessor($RegExp, '$input', SETTER, RegExpSetInput, DONT_ENUM | DONT_DELETE);
-
- // The properties multiline and $* are aliases for each other. When this
- // value is set in SpiderMonkey, the value it is set to is coerced to a
- // boolean. We mimic that behavior with a slight difference: in SpiderMonkey
- // the value of the expression 'RegExp.multiline = null' (for instance) is the
- // boolean false (ie, the value after coercion), while in V8 it is the value
- // null (ie, the value before coercion).
-
- // Getter and setter for multiline.
- var multiline = false;
- function RegExpGetMultiline() { return multiline; };
- function RegExpSetMultiline(flag) { multiline = flag ? true : false; };
-
- %DefineAccessor($RegExp, 'multiline', GETTER, RegExpGetMultiline, DONT_DELETE);
- %DefineAccessor($RegExp, 'multiline', SETTER, RegExpSetMultiline, DONT_DELETE);
- %DefineAccessor($RegExp, '$*', GETTER, RegExpGetMultiline, DONT_ENUM | DONT_DELETE);
- %DefineAccessor($RegExp, '$*', SETTER, RegExpSetMultiline, DONT_ENUM | DONT_DELETE);
-
-
- function NoOpSetter(ignored) {}
-
-
- // Static properties set by a successful match.
- %DefineAccessor($RegExp, 'lastMatch', GETTER, RegExpGetLastMatch, DONT_DELETE);
- %DefineAccessor($RegExp, 'lastMatch', SETTER, NoOpSetter, DONT_DELETE);
- %DefineAccessor($RegExp, '$&', GETTER, RegExpGetLastMatch, DONT_ENUM | DONT_DELETE);
- %DefineAccessor($RegExp, '$&', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
- %DefineAccessor($RegExp, 'lastParen', GETTER, RegExpGetLastParen, DONT_DELETE);
- %DefineAccessor($RegExp, 'lastParen', SETTER, NoOpSetter, DONT_DELETE);
- %DefineAccessor($RegExp, '$+', GETTER, RegExpGetLastParen, DONT_ENUM | DONT_DELETE);
- %DefineAccessor($RegExp, '$+', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
- %DefineAccessor($RegExp, 'leftContext', GETTER, RegExpGetLeftContext, DONT_DELETE);
- %DefineAccessor($RegExp, 'leftContext', SETTER, NoOpSetter, DONT_DELETE);
- %DefineAccessor($RegExp, '$`', GETTER, RegExpGetLeftContext, DONT_ENUM | DONT_DELETE);
- %DefineAccessor($RegExp, '$`', SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
- %DefineAccessor($RegExp, 'rightContext', GETTER, RegExpGetRightContext, DONT_DELETE);
- %DefineAccessor($RegExp, 'rightContext', SETTER, NoOpSetter, DONT_DELETE);
- %DefineAccessor($RegExp, "$'", GETTER, RegExpGetRightContext, DONT_ENUM | DONT_DELETE);
- %DefineAccessor($RegExp, "$'", SETTER, NoOpSetter, DONT_ENUM | DONT_DELETE);
-
- for (var i = 1; i < 10; ++i) {
- %DefineAccessor($RegExp, '$' + i, GETTER, RegExpMakeCaptureGetter(i), DONT_DELETE);
- %DefineAccessor($RegExp, '$' + i, SETTER, NoOpSetter, DONT_DELETE);
- }
-}
-
-
-SetupRegExp();
diff --git a/src/register-allocator-inl.h b/src/register-allocator-inl.h
index b3fa4747..a99f4550 100644
--- a/src/register-allocator-inl.h
+++ b/src/register-allocator-inl.h
@@ -48,6 +48,20 @@
namespace v8 {
namespace internal {
+Result::Result(const Result& other) {
+ other.CopyTo(this);
+}
+
+
+Result& Result::operator=(const Result& other) {
+ if (this != &other) {
+ Unuse();
+ other.CopyTo(this);
+ }
+ return *this;
+}
+
+
Result::~Result() {
if (is_register()) {
CodeGeneratorScope::Current()->allocator()->Unuse(reg());
@@ -71,6 +85,25 @@ void Result::CopyTo(Result* destination) const {
}
+bool RegisterAllocator::is_used(Register reg) {
+ return registers_.is_used(ToNumber(reg));
+}
+
+
+int RegisterAllocator::count(Register reg) {
+ return registers_.count(ToNumber(reg));
+}
+
+
+void RegisterAllocator::Use(Register reg) {
+ registers_.Use(ToNumber(reg));
+}
+
+
+void RegisterAllocator::Unuse(Register reg) {
+ registers_.Unuse(ToNumber(reg));
+}
+
} } // namespace v8::internal
#endif // V8_REGISTER_ALLOCATOR_INL_H_
diff --git a/src/register-allocator.cc b/src/register-allocator.cc
index d55f949d..349cc246 100644
--- a/src/register-allocator.cc
+++ b/src/register-allocator.cc
@@ -37,10 +37,12 @@ namespace internal {
// Result implementation.
-Result::Result(Register reg) {
+Result::Result(Register reg, NumberInfo::Type info) {
ASSERT(reg.is_valid() && !RegisterAllocator::IsReserved(reg));
CodeGeneratorScope::Current()->allocator()->Use(reg);
- value_ = TypeField::encode(REGISTER) | DataField::encode(reg.code_);
+ value_ = TypeField::encode(REGISTER)
+ | NumberInfoField::encode(info)
+ | DataField::encode(reg.code_);
}
@@ -50,6 +52,23 @@ Result::ZoneObjectList* Result::ConstantList() {
}
+NumberInfo::Type Result::number_info() {
+ ASSERT(is_valid());
+ if (!is_constant()) return NumberInfoField::decode(value_);
+ Handle<Object> value = handle();
+ if (value->IsSmi()) return NumberInfo::kSmi;
+ if (value->IsHeapNumber()) return NumberInfo::kHeapNumber;
+ return NumberInfo::kUnknown;
+}
+
+
+void Result::set_number_info(NumberInfo::Type info) {
+ ASSERT(is_valid());
+ value_ = value_ & ~NumberInfoField::mask();
+ value_ = value_ | NumberInfoField::encode(info);
+}
+
+
// -------------------------------------------------------------------------
// RegisterAllocator implementation.
diff --git a/src/register-allocator.h b/src/register-allocator.h
index 0f46996c..747200a0 100644
--- a/src/register-allocator.h
+++ b/src/register-allocator.h
@@ -29,6 +29,7 @@
#define V8_REGISTER_ALLOCATOR_H_
#include "macro-assembler.h"
+#include "number-info.h"
#if V8_TARGET_ARCH_IA32
#include "ia32/register-allocator-ia32.h"
@@ -64,28 +65,21 @@ class Result BASE_EMBEDDED {
Result() { invalidate(); }
// Construct a register Result.
- explicit Result(Register reg);
+ explicit Result(Register reg, NumberInfo::Type info = NumberInfo::kUnknown);
// Construct a Result whose value is a compile-time constant.
explicit Result(Handle<Object> value) {
value_ = TypeField::encode(CONSTANT)
+ | NumberInfoField::encode(NumberInfo::kUninitialized)
| DataField::encode(ConstantList()->length());
ConstantList()->Add(value);
}
// The copy constructor and assignment operators could each create a new
// register reference.
- Result(const Result& other) {
- other.CopyTo(this);
- }
+ inline Result(const Result& other);
- Result& operator=(const Result& other) {
- if (this != &other) {
- Unuse();
- other.CopyTo(this);
- }
- return *this;
- }
+ inline Result& operator=(const Result& other);
inline ~Result();
@@ -107,6 +101,14 @@ class Result BASE_EMBEDDED {
void invalidate() { value_ = TypeField::encode(INVALID); }
+ NumberInfo::Type number_info();
+ void set_number_info(NumberInfo::Type info);
+ bool is_number() {
+ return (number_info() & NumberInfo::kNumber) != 0;
+ }
+ bool is_smi() { return number_info() == NumberInfo::kSmi; }
+ bool is_heap_number() { return number_info() == NumberInfo::kHeapNumber; }
+
bool is_valid() const { return type() != INVALID; }
bool is_register() const { return type() == REGISTER; }
bool is_constant() const { return type() == CONSTANT; }
@@ -138,7 +140,8 @@ class Result BASE_EMBEDDED {
uint32_t value_;
class TypeField: public BitField<Type, 0, 2> {};
- class DataField: public BitField<uint32_t, 2, 32 - 3> {};
+ class NumberInfoField : public BitField<NumberInfo::Type, 2, 3> {};
+ class DataField: public BitField<uint32_t, 5, 32 - 5> {};
inline void CopyTo(Result* destination) const;
@@ -237,18 +240,18 @@ class RegisterAllocator BASE_EMBEDDED {
// Predicates and accessors for the registers' reference counts.
bool is_used(int num) { return registers_.is_used(num); }
- bool is_used(Register reg) { return registers_.is_used(ToNumber(reg)); }
+ inline bool is_used(Register reg);
int count(int num) { return registers_.count(num); }
- int count(Register reg) { return registers_.count(ToNumber(reg)); }
+ inline int count(Register reg);
// Explicitly record a reference to a register.
void Use(int num) { registers_.Use(num); }
- void Use(Register reg) { registers_.Use(ToNumber(reg)); }
+ inline void Use(Register reg);
// Explicitly record that a register will no longer be used.
void Unuse(int num) { registers_.Unuse(num); }
- void Unuse(Register reg) { registers_.Unuse(ToNumber(reg)); }
+ inline void Unuse(Register reg);
// Reset the register reference counts to free all non-reserved registers.
void Reset() { registers_.Reset(); }
diff --git a/src/runtime.cc b/src/runtime.cc
index aa94ce5c..70755423 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -1208,17 +1208,6 @@ static Object* Runtime_OptimizeObjectForAddingMultipleProperties(
}
-static Object* Runtime_TransformToFastProperties(Arguments args) {
- HandleScope scope;
- ASSERT(args.length() == 1);
- CONVERT_ARG_CHECKED(JSObject, object, 0);
- if (!object->HasFastProperties() && !object->IsGlobalObject()) {
- TransformToFastProperties(object, 0);
- }
- return *object;
-}
-
-
static Object* Runtime_RegExpExec(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 4);
@@ -2287,6 +2276,20 @@ static int SingleCharIndexOf(Vector<const schar> string,
return -1;
}
+
+template <typename schar>
+static int SingleCharLastIndexOf(Vector<const schar> string,
+ schar pattern_char,
+ int start_index) {
+ for (int i = start_index; i >= 0; i--) {
+ if (pattern_char == string[i]) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
// Trivial string search for shorter strings.
// On return, if "complete" is set to true, the return value is the
// final result of searching for the patter in the subject.
@@ -2363,7 +2366,7 @@ static int StringMatchStrategy(Vector<const schar> sub,
// We have an ASCII haystack and a non-ASCII needle. Check if there
// really is a non-ASCII character in the needle and bail out if there
// is.
- if (sizeof(pchar) > 1 && sizeof(schar) == 1) {
+ if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
for (int i = 0; i < pat.length(); i++) {
uc16 c = pat[i];
if (c > String::kMaxAsciiCharCode) {
@@ -2466,39 +2469,115 @@ static Object* Runtime_StringIndexOf(Arguments args) {
}
+template <typename schar, typename pchar>
+static int StringMatchBackwards(Vector<const schar> sub,
+ Vector<const pchar> pat,
+ int idx) {
+ ASSERT(pat.length() >= 1);
+ ASSERT(idx + pat.length() <= sub.length());
+
+ if (sizeof(schar) == 1 && sizeof(pchar) > 1) {
+ for (int i = 0; i < pat.length(); i++) {
+ uc16 c = pat[i];
+ if (c > String::kMaxAsciiCharCode) {
+ return -1;
+ }
+ }
+ }
+
+ pchar pattern_first_char = pat[0];
+ for (int i = idx; i >= 0; i--) {
+ if (sub[i] != pattern_first_char) continue;
+ int j = 1;
+ while (j < pat.length()) {
+ if (pat[j] != sub[i+j]) {
+ break;
+ }
+ j++;
+ }
+ if (j == pat.length()) {
+ return i;
+ }
+ }
+ return -1;
+}
+
static Object* Runtime_StringLastIndexOf(Arguments args) {
- NoHandleAllocation ha;
+ HandleScope scope; // create a new handle scope
ASSERT(args.length() == 3);
- CONVERT_CHECKED(String, sub, args[0]);
- CONVERT_CHECKED(String, pat, args[1]);
- Object* index = args[2];
-
- sub->TryFlattenIfNotFlat();
- pat->TryFlattenIfNotFlat();
+ CONVERT_ARG_CHECKED(String, sub, 0);
+ CONVERT_ARG_CHECKED(String, pat, 1);
+ Object* index = args[2];
uint32_t start_index;
if (!Array::IndexFromObject(index, &start_index)) return Smi::FromInt(-1);
- uint32_t pattern_length = pat->length();
+ uint32_t pat_length = pat->length();
uint32_t sub_length = sub->length();
- if (start_index + pattern_length > sub_length) {
- start_index = sub_length - pattern_length;
+ if (start_index + pat_length > sub_length) {
+ start_index = sub_length - pat_length;
}
- for (int i = start_index; i >= 0; i--) {
- bool found = true;
- for (uint32_t j = 0; j < pattern_length; j++) {
- if (sub->Get(i + j) != pat->Get(j)) {
- found = false;
- break;
+ if (pat_length == 0) {
+ return Smi::FromInt(start_index);
+ }
+
+ if (!sub->IsFlat()) {
+ FlattenString(sub);
+ }
+
+ if (pat_length == 1) {
+ AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
+ if (sub->IsAsciiRepresentation()) {
+ uc16 pchar = pat->Get(0);
+ if (pchar > String::kMaxAsciiCharCode) {
+ return Smi::FromInt(-1);
}
+ return Smi::FromInt(SingleCharLastIndexOf(sub->ToAsciiVector(),
+ static_cast<char>(pat->Get(0)),
+ start_index));
+ } else {
+ return Smi::FromInt(SingleCharLastIndexOf(sub->ToUC16Vector(),
+ pat->Get(0),
+ start_index));
+ }
+ }
+
+ if (!pat->IsFlat()) {
+ FlattenString(pat);
+ }
+
+ AssertNoAllocation no_heap_allocation; // ensure vectors stay valid
+
+ int position = -1;
+
+ if (pat->IsAsciiRepresentation()) {
+ Vector<const char> pat_vector = pat->ToAsciiVector();
+ if (sub->IsAsciiRepresentation()) {
+ position = StringMatchBackwards(sub->ToAsciiVector(),
+ pat_vector,
+ start_index);
+ } else {
+ position = StringMatchBackwards(sub->ToUC16Vector(),
+ pat_vector,
+ start_index);
+ }
+ } else {
+ Vector<const uc16> pat_vector = pat->ToUC16Vector();
+ if (sub->IsAsciiRepresentation()) {
+ position = StringMatchBackwards(sub->ToAsciiVector(),
+ pat_vector,
+ start_index);
+ } else {
+ position = StringMatchBackwards(sub->ToUC16Vector(),
+ pat_vector,
+ start_index);
}
- if (found) return Smi::FromInt(i);
}
- return Smi::FromInt(-1);
+ return Smi::FromInt(position);
}
@@ -2906,7 +2985,7 @@ static Object* Runtime_DefineOrRedefineAccessorProperty(Arguments args) {
// If an existing property is either FIELD, NORMAL or CONSTANT_FUNCTION
// delete it to avoid running into trouble in DefineAccessor, which
// handles this incorrectly if the property is readonly (does nothing)
- if (result.IsValid() &&
+ if (result.IsProperty() &&
(result.type() == FIELD || result.type() == NORMAL
|| result.type() == CONSTANT_FUNCTION)) {
obj->DeleteProperty(name, JSObject::NORMAL_DELETION);
@@ -2937,12 +3016,14 @@ static Object* Runtime_DefineOrRedefineDataProperty(Arguments args) {
// correctly in the case where a property is a field and is reset with
// new attributes.
if (result.IsProperty() && attr != result.GetAttributes()) {
- PropertyDetails details = PropertyDetails(attr, NORMAL);
// New attributes - normalize to avoid writing to instance descriptor
- js_object->NormalizeProperties(KEEP_INOBJECT_PROPERTIES, 0);
- return js_object->SetNormalizedProperty(*name, *obj_value, details);
+ js_object->NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
+ // Use IgnoreAttributes version since a readonly property may be
+ // overridden and SetProperty does not allow this.
+ return js_object->IgnoreAttributesAndSetLocalProperty(*name,
+ *obj_value,
+ attr);
}
-
return Runtime::SetObjectProperty(js_object, name, obj_value, attr);
}
@@ -2969,8 +3050,6 @@ Object* Runtime::SetObjectProperty(Handle<Object> object,
// 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.
@@ -2990,7 +3069,6 @@ Object* Runtime::SetObjectProperty(Handle<Object> object,
if (key->IsString()) {
Handle<Object> result;
if (Handle<String>::cast(key)->AsArrayIndex(&index)) {
- ASSERT(attr == NONE);
result = SetElement(js_object, index, value);
} else {
Handle<String> key_string = Handle<String>::cast(key);
@@ -3008,7 +3086,6 @@ Object* Runtime::SetObjectProperty(Handle<Object> object,
Handle<String> name = Handle<String>::cast(converted);
if (name->AsArrayIndex(&index)) {
- ASSERT(attr == NONE);
return js_object->SetElement(index, *value);
} else {
return js_object->SetProperty(*name, *value, attr);
@@ -3025,8 +3102,6 @@ Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
// 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.
@@ -3043,7 +3118,6 @@ Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
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);
@@ -3061,7 +3135,6 @@ Object* Runtime::ForceSetObjectProperty(Handle<JSObject> js_object,
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);
@@ -3520,17 +3593,23 @@ static Object* Runtime_GetArgumentsProperty(Arguments args) {
static Object* Runtime_ToFastProperties(Arguments args) {
+ HandleScope scope;
+
ASSERT(args.length() == 1);
Handle<Object> object = args.at<Object>(0);
if (object->IsJSObject()) {
Handle<JSObject> js_object = Handle<JSObject>::cast(object);
- js_object->TransformToFastProperties(0);
+ if (!js_object->HasFastProperties() && !js_object->IsGlobalObject()) {
+ js_object->TransformToFastProperties(0);
+ }
}
return *object;
}
static Object* Runtime_ToSlowProperties(Arguments args) {
+ HandleScope scope;
+
ASSERT(args.length() == 1);
Handle<Object> object = args.at<Object>(0);
if (object->IsJSObject()) {
@@ -4559,6 +4638,7 @@ static Object* Runtime_StringCompare(Arguments args) {
static Object* Runtime_Math_abs(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_abs.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return Heap::AllocateHeapNumber(fabs(x));
@@ -4568,6 +4648,7 @@ static Object* Runtime_Math_abs(Arguments args) {
static Object* Runtime_Math_acos(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_acos.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::ACOS, x);
@@ -4577,6 +4658,7 @@ static Object* Runtime_Math_acos(Arguments args) {
static Object* Runtime_Math_asin(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_asin.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::ASIN, x);
@@ -4586,6 +4668,7 @@ static Object* Runtime_Math_asin(Arguments args) {
static Object* Runtime_Math_atan(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_atan.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::ATAN, x);
@@ -4595,6 +4678,7 @@ static Object* Runtime_Math_atan(Arguments args) {
static Object* Runtime_Math_atan2(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
+ Counters::math_atan2.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
CONVERT_DOUBLE_CHECKED(y, args[1]);
@@ -4618,6 +4702,7 @@ static Object* Runtime_Math_atan2(Arguments args) {
static Object* Runtime_Math_ceil(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_ceil.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return Heap::NumberFromDouble(ceiling(x));
@@ -4627,6 +4712,7 @@ static Object* Runtime_Math_ceil(Arguments args) {
static Object* Runtime_Math_cos(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_cos.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::COS, x);
@@ -4636,6 +4722,7 @@ static Object* Runtime_Math_cos(Arguments args) {
static Object* Runtime_Math_exp(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_exp.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::EXP, x);
@@ -4645,6 +4732,7 @@ static Object* Runtime_Math_exp(Arguments args) {
static Object* Runtime_Math_floor(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_floor.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return Heap::NumberFromDouble(floor(x));
@@ -4654,6 +4742,7 @@ static Object* Runtime_Math_floor(Arguments args) {
static Object* Runtime_Math_log(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_log.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::LOG, x);
@@ -4694,6 +4783,7 @@ static double powi(double x, int y) {
static Object* Runtime_Math_pow(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
+ Counters::math_pow.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
@@ -4732,6 +4822,7 @@ static Object* Runtime_Math_pow(Arguments args) {
static Object* Runtime_Math_round(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_round.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
if (signbit(x) && x >= -0.5) return Heap::minus_zero_value();
@@ -4744,6 +4835,7 @@ static Object* Runtime_Math_round(Arguments args) {
static Object* Runtime_Math_sin(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_sin.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::SIN, x);
@@ -4753,6 +4845,7 @@ static Object* Runtime_Math_sin(Arguments args) {
static Object* Runtime_Math_sqrt(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_sqrt.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return Heap::AllocateHeapNumber(sqrt(x));
@@ -4762,47 +4855,13 @@ static Object* Runtime_Math_sqrt(Arguments args) {
static Object* Runtime_Math_tan(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
+ Counters::math_tan.Increment();
CONVERT_DOUBLE_CHECKED(x, args[0]);
return TranscendentalCache::Get(TranscendentalCache::TAN, x);
}
-// The NewArguments function is only used when constructing the
-// arguments array when calling non-functions from JavaScript in
-// runtime.js:CALL_NON_FUNCTION.
-static Object* Runtime_NewArguments(Arguments args) {
- NoHandleAllocation ha;
- ASSERT(args.length() == 1);
-
- // ECMA-262, 3rd., 10.1.8, p.39
- CONVERT_CHECKED(JSFunction, callee, args[0]);
-
- // Compute the frame holding the arguments.
- JavaScriptFrameIterator it;
- it.AdvanceToArgumentsFrame();
- JavaScriptFrame* frame = it.frame();
-
- const int length = frame->GetProvidedParametersCount();
- Object* result = Heap::AllocateArgumentsObject(callee, length);
- if (result->IsFailure()) return result;
- if (length > 0) {
- Object* obj = Heap::AllocateFixedArray(length);
- if (obj->IsFailure()) return obj;
- FixedArray* array = FixedArray::cast(obj);
- ASSERT(array->length() == length);
-
- AssertNoAllocation no_gc;
- WriteBarrierMode mode = array->GetWriteBarrierMode(no_gc);
- for (int i = 0; i < length; i++) {
- array->set(i, frame->GetParameter(i), mode);
- }
- JSObject::cast(result)->set_elements(array);
- }
- return result;
-}
-
-
static Object* Runtime_NewArgumentsFast(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 3);
@@ -4849,21 +4908,21 @@ static Object* Runtime_NewClosure(Arguments args) {
}
-static Code* ComputeConstructStub(Handle<SharedFunctionInfo> shared) {
- // TODO(385): Change this to create a construct stub specialized for
- // the given map to make allocation of simple objects - and maybe
- // arrays - much faster.
- if (FLAG_inline_new
- && shared->has_only_simple_this_property_assignments()) {
+static Code* ComputeConstructStub(Handle<JSFunction> function) {
+ Handle<Object> prototype = Factory::null_value();
+ if (function->has_instance_prototype()) {
+ prototype = Handle<Object>(function->instance_prototype());
+ }
+ if (function->shared()->CanGenerateInlineConstructor(*prototype)) {
ConstructStubCompiler compiler;
- Object* code = compiler.CompileConstructStub(*shared);
+ Object* code = compiler.CompileConstructStub(function->shared());
if (code->IsFailure()) {
return Builtins::builtin(Builtins::JSConstructStubGeneric);
}
return Code::cast(code);
}
- return shared->construct_stub();
+ return function->shared()->construct_stub();
}
@@ -4913,10 +4972,9 @@ static Object* Runtime_NewObject(Arguments args) {
bool first_allocation = !function->has_initial_map();
Handle<JSObject> result = Factory::NewJSObject(function);
if (first_allocation) {
- Handle<Map> map = Handle<Map>(function->initial_map());
Handle<Code> stub = Handle<Code>(
- ComputeConstructStub(Handle<SharedFunctionInfo>(function->shared())));
- function->shared()->set_construct_stub(*stub);
+ ComputeConstructStub(Handle<JSFunction>(function)));
+ shared->set_construct_stub(*stub);
}
Counters::constructed_objects.Increment();
@@ -4955,28 +5013,6 @@ static Object* Runtime_LazyCompile(Arguments args) {
}
-static Object* Runtime_GetCalledFunction(Arguments args) {
- HandleScope scope;
- ASSERT(args.length() == 0);
- StackFrameIterator it;
- // Get past the JS-to-C exit frame.
- ASSERT(it.frame()->is_exit());
- it.Advance();
- // Get past the CALL_NON_FUNCTION activation frame.
- ASSERT(it.frame()->is_java_script());
- it.Advance();
- // Argument adaptor frames do not copy the function; we have to skip
- // past them to get to the real calling frame.
- if (it.frame()->is_arguments_adaptor()) it.Advance();
- // Get the function from the top of the expression stack of the
- // calling frame.
- StandardFrame* frame = StandardFrame::cast(it.frame());
- int index = frame->ComputeExpressionsCount() - 1;
- Object* result = frame->GetExpression(index);
- return result;
-}
-
-
static Object* Runtime_GetFunctionDelegate(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 1);
@@ -7981,20 +8017,22 @@ static Object* Runtime_FunctionGetInferredName(Arguments args) {
static Object* Runtime_ProfilerResume(Arguments args) {
NoHandleAllocation ha;
- ASSERT(args.length() == 1);
+ ASSERT(args.length() == 2);
CONVERT_CHECKED(Smi, smi_modules, args[0]);
- v8::V8::ResumeProfilerEx(smi_modules->value());
+ CONVERT_CHECKED(Smi, smi_tag, args[1]);
+ v8::V8::ResumeProfilerEx(smi_modules->value(), smi_tag->value());
return Heap::undefined_value();
}
static Object* Runtime_ProfilerPause(Arguments args) {
NoHandleAllocation ha;
- ASSERT(args.length() == 1);
+ ASSERT(args.length() == 2);
CONVERT_CHECKED(Smi, smi_modules, args[0]);
- v8::V8::PauseProfilerEx(smi_modules->value());
+ CONVERT_CHECKED(Smi, smi_tag, args[1]);
+ v8::V8::PauseProfilerEx(smi_modules->value(), smi_tag->value());
return Heap::undefined_value();
}
@@ -8159,15 +8197,6 @@ static Object* Runtime_DeleteHandleScopeExtensions(Arguments args) {
}
-static Object* Runtime_ProfileLogMarker(Arguments args) {
- ASSERT(args.length() == 1);
- CONVERT_CHECKED(String, format, args[0]);
- Vector<const char> marker = format->ToAsciiVector();
- Logger::LogProfileMarker(marker);
- return Heap::undefined_value();
-}
-
-
#ifdef DEBUG
// ListNatives is ONLY used by the fuzz-natives.js in debug mode
// Exclude the code in release mode.
diff --git a/src/runtime.h b/src/runtime.h
index 12f969d4..e2e5c221 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -71,10 +71,8 @@ namespace internal {
F(IsExtensible, 1, 1) \
\
/* Utilities */ \
- F(GetCalledFunction, 0, 1) \
F(GetFunctionDelegate, 1, 1) \
F(GetConstructorDelegate, 1, 1) \
- F(NewArguments, 1, 1) \
F(NewArgumentsFast, 3, 1) \
F(LazyCompile, 1, 1) \
F(SetNewFunctionAttributes, 1, 1) \
@@ -268,7 +266,6 @@ namespace internal {
F(InitializeConstGlobal, 2, 1) \
F(InitializeConstContextSlot, 3, 1) \
F(OptimizeObjectForAddingMultipleProperties, 2, 1) \
- F(TransformToFastProperties, 1, 1) \
\
/* Debugging */ \
F(DebugPrint, 1, 1) \
@@ -284,11 +281,7 @@ namespace internal {
F(DeleteHandleScopeExtensions, 0, 1) \
\
/* Pseudo functions - handled as macros by parser */ \
- F(IS_VAR, 1, 1) \
- \
- /* Profile marker support for more fine grained profiling of page cyclers. */ \
- /* Also known as a stupid hack. :) */ \
- F(ProfileLogMarker, 1, 1)
+ F(IS_VAR, 1, 1)
#ifdef ENABLE_DEBUGGER_SUPPORT
#define RUNTIME_FUNCTION_LIST_DEBUGGER_SUPPORT(F) \
@@ -335,8 +328,8 @@ namespace internal {
#ifdef ENABLE_LOGGING_AND_PROFILING
#define RUNTIME_FUNCTION_LIST_PROFILER_SUPPORT(F) \
- F(ProfilerResume, 1, 1) \
- F(ProfilerPause, 1, 1)
+ F(ProfilerResume, 2, 1) \
+ F(ProfilerPause, 2, 1)
#else
#define RUNTIME_FUNCTION_LIST_PROFILER_SUPPORT(F)
#endif
diff --git a/src/runtime.js b/src/runtime.js
index 10ef98eb..e9d98487 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -178,7 +178,7 @@ function STRING_ADD_LEFT(y) {
y = %_ValueOf(y);
} else {
y = IS_NUMBER(y)
- ? %NumberToString(y)
+ ? %_NumberToString(y)
: %ToString(%ToPrimitive(y, NO_HINT));
}
}
@@ -194,7 +194,7 @@ function STRING_ADD_RIGHT(y) {
x = %_ValueOf(x);
} else {
x = IS_NUMBER(x)
- ? %NumberToString(x)
+ ? %_NumberToString(x)
: %ToString(%ToPrimitive(x, NO_HINT));
}
}
@@ -395,26 +395,20 @@ function FILTER_KEY(key) {
function CALL_NON_FUNCTION() {
- var callee = %GetCalledFunction();
- var delegate = %GetFunctionDelegate(callee);
+ var delegate = %GetFunctionDelegate(this);
if (!IS_FUNCTION(delegate)) {
- throw %MakeTypeError('called_non_callable', [typeof callee]);
+ throw %MakeTypeError('called_non_callable', [typeof this]);
}
-
- var parameters = %NewArguments(delegate);
- return delegate.apply(callee, parameters);
+ return delegate.apply(this, arguments);
}
function CALL_NON_FUNCTION_AS_CONSTRUCTOR() {
- var callee = %GetCalledFunction();
- var delegate = %GetConstructorDelegate(callee);
+ var delegate = %GetConstructorDelegate(this);
if (!IS_FUNCTION(delegate)) {
- throw %MakeTypeError('called_non_callable', [typeof callee]);
+ throw %MakeTypeError('called_non_callable', [typeof this]);
}
-
- var parameters = %NewArguments(delegate);
- return delegate.apply(callee, parameters);
+ return delegate.apply(this, arguments);
}
@@ -529,6 +523,13 @@ function ToNumber(x) {
// ECMA-262, section 9.8, page 35.
function ToString(x) {
if (IS_STRING(x)) return x;
+ if (IS_NUMBER(x)) return %_NumberToString(x);
+ if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
+ if (IS_UNDEFINED(x)) return 'undefined';
+ return (IS_NULL(x)) ? 'null' : %ToString(%DefaultString(x));
+}
+
+function NonStringToString(x) {
if (IS_NUMBER(x)) return %NumberToString(x);
if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
if (IS_UNDEFINED(x)) return 'undefined';
diff --git a/src/scopeinfo.cc b/src/scopeinfo.cc
index 8b989d7a..de1841b0 100644
--- a/src/scopeinfo.cc
+++ b/src/scopeinfo.cc
@@ -536,7 +536,7 @@ int ContextSlotCache::Hash(Code* code, String* name) {
// Uses only lower 32 bits if pointers are larger.
uintptr_t addr_hash =
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(code)) >> 2;
- return (addr_hash ^ name->Hash()) % kLength;
+ return static_cast<int>((addr_hash ^ name->Hash()) % kLength);
}
diff --git a/src/serialize.cc b/src/serialize.cc
index ad8e20f6..110e4619 100644
--- a/src/serialize.cc
+++ b/src/serialize.cc
@@ -359,79 +359,87 @@ void ExternalReferenceTable::PopulateTable() {
UNCLASSIFIED,
7,
"Heap::NewSpaceStart()");
- Add(ExternalReference::heap_always_allocate_scope_depth().address(),
+ Add(ExternalReference::new_space_mask().address(),
UNCLASSIFIED,
8,
+ "Heap::NewSpaceMask()");
+ Add(ExternalReference::heap_always_allocate_scope_depth().address(),
+ UNCLASSIFIED,
+ 9,
"Heap::always_allocate_scope_depth()");
Add(ExternalReference::new_space_allocation_limit_address().address(),
UNCLASSIFIED,
- 9,
+ 10,
"Heap::NewSpaceAllocationLimitAddress()");
Add(ExternalReference::new_space_allocation_top_address().address(),
UNCLASSIFIED,
- 10,
+ 11,
"Heap::NewSpaceAllocationTopAddress()");
#ifdef ENABLE_DEBUGGER_SUPPORT
Add(ExternalReference::debug_break().address(),
UNCLASSIFIED,
- 11,
+ 12,
"Debug::Break()");
Add(ExternalReference::debug_step_in_fp_address().address(),
UNCLASSIFIED,
- 12,
+ 13,
"Debug::step_in_fp_addr()");
#endif
Add(ExternalReference::double_fp_operation(Token::ADD).address(),
UNCLASSIFIED,
- 13,
+ 14,
"add_two_doubles");
Add(ExternalReference::double_fp_operation(Token::SUB).address(),
UNCLASSIFIED,
- 14,
+ 15,
"sub_two_doubles");
Add(ExternalReference::double_fp_operation(Token::MUL).address(),
UNCLASSIFIED,
- 15,
+ 16,
"mul_two_doubles");
Add(ExternalReference::double_fp_operation(Token::DIV).address(),
UNCLASSIFIED,
- 16,
+ 17,
"div_two_doubles");
Add(ExternalReference::double_fp_operation(Token::MOD).address(),
UNCLASSIFIED,
- 17,
+ 18,
"mod_two_doubles");
Add(ExternalReference::compare_doubles().address(),
UNCLASSIFIED,
- 18,
+ 19,
"compare_doubles");
#ifdef V8_NATIVE_REGEXP
Add(ExternalReference::re_case_insensitive_compare_uc16().address(),
UNCLASSIFIED,
- 19,
+ 20,
"NativeRegExpMacroAssembler::CaseInsensitiveCompareUC16()");
Add(ExternalReference::re_check_stack_guard_state().address(),
UNCLASSIFIED,
- 20,
+ 21,
"RegExpMacroAssembler*::CheckStackGuardState()");
Add(ExternalReference::re_grow_stack().address(),
UNCLASSIFIED,
- 21,
+ 22,
"NativeRegExpMacroAssembler::GrowStack()");
Add(ExternalReference::re_word_character_map().address(),
UNCLASSIFIED,
- 22,
+ 23,
"NativeRegExpMacroAssembler::word_character_map");
#endif
// Keyed lookup cache.
Add(ExternalReference::keyed_lookup_cache_keys().address(),
UNCLASSIFIED,
- 23,
+ 24,
"KeyedLookupCache::keys()");
Add(ExternalReference::keyed_lookup_cache_field_offsets().address(),
UNCLASSIFIED,
- 24,
+ 25,
"KeyedLookupCache::field_offsets()");
+ Add(ExternalReference::transcendental_cache_array_address().address(),
+ UNCLASSIFIED,
+ 26,
+ "TranscendentalCache::caches()");
}
@@ -852,10 +860,10 @@ void SnapshotByteSink::PutInt(uintptr_t integer, const char* description) {
const int max_shift = ((kPointerSize * kBitsPerByte) / 7) * 7;
for (int shift = max_shift; shift > 0; shift -= 7) {
if (integer >= static_cast<uintptr_t>(1u) << shift) {
- Put(((integer >> shift) & 0x7f) | 0x80, "IntPart");
+ Put((static_cast<int>((integer >> shift)) & 0x7f) | 0x80, "IntPart");
}
}
- PutSection(integer & 0x7f, "IntLastPart");
+ PutSection(static_cast<int>(integer & 0x7f), "IntLastPart");
}
#ifdef DEBUG
diff --git a/src/spaces-inl.h b/src/spaces-inl.h
index 4fd8a6c8..72f83050 100644
--- a/src/spaces-inl.h
+++ b/src/spaces-inl.h
@@ -183,7 +183,7 @@ Page* MemoryAllocator::GetNextPage(Page* p) {
int MemoryAllocator::GetChunkId(Page* p) {
ASSERT(p->is_valid());
- return p->opaque_header & Page::kPageAlignmentMask;
+ return static_cast<int>(p->opaque_header & Page::kPageAlignmentMask);
}
diff --git a/src/string.js b/src/string.js
index b2af0504..a8fc8d47 100644
--- a/src/string.js
+++ b/src/string.js
@@ -34,7 +34,7 @@
// Set the String function and constructor.
%SetCode($String, function(x) {
- var value = %_ArgumentsLength() == 0 ? '' : ToString(x);
+ var value = %_ArgumentsLength() == 0 ? '' : TO_STRING_INLINE(x);
if (%_IsConstructCall()) {
%_SetValueOf(this, value);
} else {
@@ -64,7 +64,7 @@ function StringValueOf() {
function StringCharAt(pos) {
var char_code = %_FastCharCodeAt(this, pos);
if (!%_IsSmi(char_code)) {
- var subject = ToString(this);
+ var subject = TO_STRING_INLINE(this);
var index = TO_INTEGER(pos);
if (index >= subject.length || index < 0) return "";
char_code = %StringCharCodeAt(subject, index);
@@ -79,7 +79,7 @@ function StringCharCodeAt(pos) {
if (%_IsSmi(fast_answer)) {
return fast_answer;
}
- var subject = ToString(this);
+ var subject = TO_STRING_INLINE(this);
var index = TO_INTEGER(pos);
return %StringCharCodeAt(subject, index);
}
@@ -87,14 +87,18 @@ function StringCharCodeAt(pos) {
// ECMA-262, section 15.5.4.6
function StringConcat() {
- var len = %_ArgumentsLength() + 1;
- var parts = new $Array(len);
- parts[0] = IS_STRING(this) ? this : ToString(this);
- for (var i = 1; i < len; i++) {
- var part = %_Arguments(i - 1);
- parts[i] = IS_STRING(part) ? part : ToString(part);
+ var len = %_ArgumentsLength();
+ var this_as_string = TO_STRING_INLINE(this);
+ if (len === 1) {
+ return this_as_string + %_Arguments(0);
}
- return %StringBuilderConcat(parts, len, "");
+ var parts = new $Array(len + 1);
+ parts[0] = this_as_string;
+ for (var i = 0; i < len; i++) {
+ var part = %_Arguments(i);
+ parts[i + 1] = TO_STRING_INLINE(part);
+ }
+ return %StringBuilderConcat(parts, len + 1, "");
}
// Match ES3 and Safari
@@ -103,8 +107,8 @@ function StringConcat() {
// ECMA-262 section 15.5.4.7
function StringIndexOf(searchString /* position */) { // length == 1
- var subject_str = ToString(this);
- var pattern_str = ToString(searchString);
+ var subject_str = TO_STRING_INLINE(this);
+ var pattern_str = TO_STRING_INLINE(searchString);
var subject_str_len = subject_str.length;
var pattern_str_len = pattern_str.length;
var index = 0;
@@ -121,9 +125,9 @@ function StringIndexOf(searchString /* position */) { // length == 1
// ECMA-262 section 15.5.4.8
function StringLastIndexOf(searchString /* position */) { // length == 1
- var sub = ToString(this);
+ var sub = TO_STRING_INLINE(this);
var subLength = sub.length;
- var pat = ToString(searchString);
+ var pat = TO_STRING_INLINE(searchString);
var patLength = pat.length;
var index = subLength - patLength;
if (%_ArgumentsLength() > 1) {
@@ -152,8 +156,8 @@ function StringLastIndexOf(searchString /* position */) { // length == 1
function StringLocaleCompare(other) {
if (%_ArgumentsLength() === 0) return 0;
- var this_str = ToString(this);
- var other_str = ToString(other);
+ var this_str = TO_STRING_INLINE(this);
+ var other_str = TO_STRING_INLINE(other);
return %StringLocaleCompare(this_str, other_str);
}
@@ -161,7 +165,7 @@ function StringLocaleCompare(other) {
// ECMA-262 section 15.5.4.10
function StringMatch(regexp) {
if (!IS_REGEXP(regexp)) regexp = new $RegExp(regexp);
- var subject = ToString(this);
+ var subject = TO_STRING_INLINE(this);
if (!regexp.global) return regexp.exec(subject);
%_Log('regexp', 'regexp-match,%0S,%1r', [subject, regexp]);
@@ -196,7 +200,7 @@ var reusableMatchInfo = [2, "", "", -1, -1];
// ECMA-262, section 15.5.4.11
function StringReplace(search, replace) {
- var subject = IS_STRING(this) ? this : ToString(this);
+ var subject = TO_STRING_INLINE(this);
// Delegate to one of the regular expression variants if necessary.
if (IS_REGEXP(search)) {
@@ -209,7 +213,7 @@ function StringReplace(search, replace) {
}
// Convert the search argument to a string and search for it.
- search = IS_STRING(search) ? search : ToString(search);
+ search = TO_STRING_INLINE(search);
var start = %StringIndexOf(subject, search, 0);
if (start < 0) return subject;
var end = start + search.length;
@@ -224,7 +228,7 @@ function StringReplace(search, replace) {
} else {
reusableMatchInfo[CAPTURE0] = start;
reusableMatchInfo[CAPTURE1] = end;
- if (!IS_STRING(replace)) replace = ToString(replace);
+ replace = TO_STRING_INLINE(replace);
ExpandReplacement(replace, subject, reusableMatchInfo, builder);
}
@@ -237,7 +241,7 @@ function StringReplace(search, replace) {
// Helper function for regular expressions in String.prototype.replace.
function StringReplaceRegExp(subject, regexp, replace) {
- replace = ToString(replace);
+ replace = TO_STRING_INLINE(replace);
return %StringReplaceRegExpWithString(subject,
regexp,
replace,
@@ -458,7 +462,7 @@ function ApplyReplacementFunction(replace, matchInfo, subject) {
// ECMA-262 section 15.5.4.12
function StringSearch(re) {
var regexp = new $RegExp(re);
- var s = ToString(this);
+ var s = TO_STRING_INLINE(this);
var last_idx = regexp.lastIndex; // keep old lastIndex
regexp.lastIndex = 0; // ignore re.global property
var result = regexp.exec(s);
@@ -472,7 +476,7 @@ function StringSearch(re) {
// ECMA-262 section 15.5.4.13
function StringSlice(start, end) {
- var s = ToString(this);
+ var s = TO_STRING_INLINE(this);
var s_len = s.length;
var start_i = TO_INTEGER(start);
var end_i = s_len;
@@ -507,7 +511,7 @@ function StringSlice(start, end) {
// ECMA-262 section 15.5.4.14
function StringSplit(separator, limit) {
- var subject = ToString(this);
+ var subject = TO_STRING_INLINE(this);
limit = (IS_UNDEFINED(limit)) ? 0xffffffff : TO_UINT32(limit);
if (limit === 0) return [];
@@ -521,18 +525,35 @@ function StringSplit(separator, limit) {
}
var length = subject.length;
- if (IS_REGEXP(separator)) {
- %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
- } else {
- separator = ToString(separator);
+ if (!IS_REGEXP(separator)) {
+ separator = TO_STRING_INLINE(separator);
+ var separator_length = separator.length;
+
// If the separator string is empty then return the elements in the subject.
- if (separator.length == 0) {
+ if (separator_length === 0) {
var result = $Array(length);
for (var i = 0; i < length; i++) result[i] = subject[i];
return result;
}
+
+ var result = [];
+ var start_index = 0;
+ var index;
+ while (true) {
+ if (start_index + separator_length > length ||
+ (index = %StringIndexOf(subject, separator, start_index)) === -1) {
+ result.push(SubString(subject, start_index, length));
+ break;
+ }
+ if (result.push(SubString(subject, start_index, index)) === limit) break;
+ start_index = index + separator_length;
+ }
+
+ return result;
}
+ %_Log('regexp', 'regexp-split,%0S,%1r', [subject, separator]);
+
if (length === 0) {
if (splitMatch(separator, subject, 0, 0) != null) return [];
return [subject];
@@ -567,7 +588,8 @@ function StringSplit(separator, limit) {
result[result.length] = SubString(subject, currentIndex, matchInfo[CAPTURE0]);
if (result.length === limit) return result;
- for (var i = 2; i < NUMBER_OF_CAPTURES(matchInfo); i += 2) {
+ var num_captures = NUMBER_OF_CAPTURES(matchInfo);
+ for (var i = 2; i < num_captures; i += 2) {
var start = matchInfo[CAPTURE(i)];
var end = matchInfo[CAPTURE(i + 1)];
if (start != -1 && end != -1) {
@@ -587,28 +609,18 @@ function StringSplit(separator, limit) {
// Helper function used by split. This version returns the matchInfo
// instead of allocating a new array with basically the same information.
function splitMatch(separator, subject, current_index, start_index) {
- if (IS_REGEXP(separator)) {
- var matchInfo = DoRegExpExec(separator, subject, start_index);
- if (matchInfo == null) return null;
- // Section 15.5.4.14 paragraph two says that we do not allow zero length
- // matches at the end of the string.
- if (matchInfo[CAPTURE0] === subject.length) return null;
- return matchInfo;
- }
-
- var separatorIndex = subject.indexOf(separator, start_index);
- if (separatorIndex === -1) return null;
-
- reusableMatchInfo[CAPTURE0] = separatorIndex;
- reusableMatchInfo[CAPTURE1] = separatorIndex + separator.length;
- return reusableMatchInfo;
-};
+ var matchInfo = DoRegExpExec(separator, subject, start_index);
+ if (matchInfo == null) return null;
+ // Section 15.5.4.14 paragraph two says that we do not allow zero length
+ // matches at the end of the string.
+ if (matchInfo[CAPTURE0] === subject.length) return null;
+ return matchInfo;
+}
// ECMA-262 section 15.5.4.15
function StringSubstring(start, end) {
- var s = this;
- if (!IS_STRING(s)) s = ToString(s);
+ var s = TO_STRING_INLINE(this);
var s_len = s.length;
var start_i = TO_INTEGER(start);
@@ -639,7 +651,7 @@ function StringSubstring(start, end) {
// This is not a part of ECMA-262.
function StringSubstr(start, n) {
- var s = ToString(this);
+ var s = TO_STRING_INLINE(this);
var len;
// Correct n: If not given, set to string length; if explicitly
@@ -677,38 +689,38 @@ function StringSubstr(start, n) {
// ECMA-262, 15.5.4.16
function StringToLowerCase() {
- return %StringToLowerCase(ToString(this));
+ return %StringToLowerCase(TO_STRING_INLINE(this));
}
// ECMA-262, 15.5.4.17
function StringToLocaleLowerCase() {
- return %StringToLowerCase(ToString(this));
+ return %StringToLowerCase(TO_STRING_INLINE(this));
}
// ECMA-262, 15.5.4.18
function StringToUpperCase() {
- return %StringToUpperCase(ToString(this));
+ return %StringToUpperCase(TO_STRING_INLINE(this));
}
// ECMA-262, 15.5.4.19
function StringToLocaleUpperCase() {
- return %StringToUpperCase(ToString(this));
+ return %StringToUpperCase(TO_STRING_INLINE(this));
}
// ES5, 15.5.4.20
function StringTrim() {
- return %StringTrim(ToString(this), true, true);
+ return %StringTrim(TO_STRING_INLINE(this), true, true);
}
function StringTrimLeft() {
- return %StringTrim(ToString(this), true, false);
+ return %StringTrim(TO_STRING_INLINE(this), true, false);
}
function StringTrimRight() {
- return %StringTrim(ToString(this), false, true);
+ return %StringTrim(TO_STRING_INLINE(this), false, true);
}
// ECMA-262, section 15.5.3.2
@@ -727,10 +739,10 @@ function StringFromCharCode(code) {
// Helper function for very basic XSS protection.
function HtmlEscape(str) {
- return ToString(str).replace(/</g, "&lt;")
- .replace(/>/g, "&gt;")
- .replace(/"/g, "&quot;")
- .replace(/'/g, "&#039;");
+ return TO_STRING_INLINE(str).replace(/</g, "&lt;")
+ .replace(/>/g, "&gt;")
+ .replace(/"/g, "&quot;")
+ .replace(/'/g, "&#039;");
};
@@ -809,7 +821,7 @@ function ReplaceResultBuilder(str) {
ReplaceResultBuilder.prototype.add = function(str) {
- if (!IS_STRING(str)) str = ToString(str);
+ str = TO_STRING_INLINE(str);
if (str.length > 0) {
var elements = this.elements;
elements[elements.length] = str;
diff --git a/src/stub-cache.cc b/src/stub-cache.cc
index 81f89fd4..577c2d77 100644
--- a/src/stub-cache.cc
+++ b/src/stub-cache.cc
@@ -105,7 +105,7 @@ Object* StubCache::ComputeLoadField(String* name,
if (code->IsFailure()) return code;
LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
- if (result->IsFailure()) return code;
+ if (result->IsFailure()) return result;
}
return Set(name, receiver->map(), Code::cast(code));
}
@@ -124,7 +124,7 @@ Object* StubCache::ComputeLoadCallback(String* name,
if (code->IsFailure()) return code;
LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
- if (result->IsFailure()) return code;
+ if (result->IsFailure()) return result;
}
return Set(name, receiver->map(), Code::cast(code));
}
@@ -143,7 +143,7 @@ Object* StubCache::ComputeLoadConstant(String* name,
if (code->IsFailure()) return code;
LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
- if (result->IsFailure()) return code;
+ if (result->IsFailure()) return result;
}
return Set(name, receiver->map(), Code::cast(code));
}
@@ -160,7 +160,7 @@ Object* StubCache::ComputeLoadInterceptor(String* name,
if (code->IsFailure()) return code;
LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
- if (result->IsFailure()) return code;
+ if (result->IsFailure()) return result;
}
return Set(name, receiver->map(), Code::cast(code));
}
@@ -189,7 +189,7 @@ Object* StubCache::ComputeLoadGlobal(String* name,
if (code->IsFailure()) return code;
LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
- if (result->IsFailure()) return code;
+ if (result->IsFailure()) return result;
}
return Set(name, receiver->map(), Code::cast(code));
}
@@ -351,7 +351,7 @@ Object* StubCache::ComputeStoreGlobal(String* name,
if (code->IsFailure()) return code;
LOG(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
- if (result->IsFailure()) return code;
+ if (result->IsFailure()) return result;
}
return Set(name, receiver->map(), Code::cast(code));
}
@@ -484,7 +484,10 @@ Object* StubCache::ComputeCallField(int argc,
Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
CallStubCompiler compiler(argc, in_loop);
- code = compiler.CompileCallField(object, holder, index, name);
+ code = compiler.CompileCallField(JSObject::cast(object),
+ holder,
+ index,
+ name);
if (code->IsFailure()) return code;
ASSERT_EQ(flags, Code::cast(code)->flags());
LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name));
@@ -518,7 +521,9 @@ Object* StubCache::ComputeCallInterceptor(int argc,
Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
CallStubCompiler compiler(argc, NOT_IN_LOOP);
- code = compiler.CompileCallInterceptor(object, holder, name);
+ code = compiler.CompileCallInterceptor(JSObject::cast(object),
+ holder,
+ name);
if (code->IsFailure()) return code;
ASSERT_EQ(flags, Code::cast(code)->flags());
LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name));
@@ -561,7 +566,7 @@ Object* StubCache::ComputeCallGlobal(int argc,
ASSERT_EQ(flags, Code::cast(code)->flags());
LOG(CodeCreateEvent(Logger::CALL_IC_TAG, Code::cast(code), name));
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
- if (result->IsFailure()) return code;
+ if (result->IsFailure()) return result;
}
return Set(name, receiver->map(), Code::cast(code));
}
@@ -920,6 +925,13 @@ Object* StoreInterceptorProperty(Arguments args) {
}
+Object* KeyedLoadPropertyWithInterceptor(Arguments args) {
+ JSObject* receiver = JSObject::cast(args[0]);
+ uint32_t index = Smi::cast(args[1])->value();
+ return receiver->GetElementWithInterceptor(receiver, index);
+}
+
+
Object* StubCompiler::CompileCallInitialize(Code::Flags flags) {
HandleScope scope;
int argc = Code::ExtractArgumentsCountFromFlags(flags);
@@ -1058,11 +1070,13 @@ Object* StubCompiler::GetCodeWithFlags(Code::Flags flags, String* name) {
return GetCodeWithFlags(flags, reinterpret_cast<char*>(NULL));
}
+
void StubCompiler::LookupPostInterceptor(JSObject* holder,
String* name,
LookupResult* lookup) {
holder->LocalLookupRealNamedProperty(name, lookup);
- if (lookup->IsNotFound()) {
+ if (!lookup->IsProperty()) {
+ lookup->NotFound();
Object* proto = holder->GetPrototype();
if (proto != Heap::null_value()) {
proto->Lookup(name, lookup);
diff --git a/src/stub-cache.h b/src/stub-cache.h
index d97fe773..43354db1 100644
--- a/src/stub-cache.h
+++ b/src/stub-cache.h
@@ -312,6 +312,7 @@ Object* LoadPropertyWithInterceptorForLoad(Arguments args);
Object* LoadPropertyWithInterceptorForCall(Arguments args);
Object* StoreInterceptorProperty(Arguments args);
Object* CallInterceptorProperty(Arguments args);
+Object* KeyedLoadPropertyWithInterceptor(Arguments args);
// Support function for computing call IC miss stubs.
@@ -346,6 +347,7 @@ class StubCompiler BASE_EMBEDDED {
static void GenerateLoadGlobalFunctionPrototype(MacroAssembler* masm,
int index,
Register prototype);
+
static void GenerateFastPropertyLoad(MacroAssembler* masm,
Register dst, Register src,
JSObject* holder, int index);
@@ -354,22 +356,20 @@ class StubCompiler BASE_EMBEDDED {
Register receiver,
Register scratch,
Label* miss_label);
+
static void GenerateLoadStringLength(MacroAssembler* masm,
Register receiver,
- Register scratch,
+ Register scratch1,
+ Register scratch2,
Label* miss_label);
- static void GenerateLoadStringLength2(MacroAssembler* masm,
- Register receiver,
- Register scratch1,
- Register scratch2,
- Label* miss_label);
+
static void GenerateLoadFunctionPrototype(MacroAssembler* masm,
Register receiver,
Register scratch1,
Register scratch2,
Label* miss_label);
+
static void GenerateStoreField(MacroAssembler* masm,
- Builtins::Name storage_extend,
JSObject* object,
int index,
Map* transition,
@@ -377,16 +377,30 @@ class StubCompiler BASE_EMBEDDED {
Register name_reg,
Register scratch,
Label* miss_label);
+
static void GenerateLoadMiss(MacroAssembler* masm, Code::Kind kind);
// Check the integrity of the prototype chain to make sure that the
// current IC is still valid.
+
+ Register CheckPrototypes(JSObject* object,
+ Register object_reg,
+ JSObject* holder,
+ Register holder_reg,
+ Register scratch,
+ String* name,
+ Label* miss) {
+ return CheckPrototypes(object, object_reg, holder, holder_reg, scratch,
+ name, kInvalidProtoDepth, miss);
+ }
+
Register CheckPrototypes(JSObject* object,
Register object_reg,
JSObject* holder,
Register holder_reg,
Register scratch,
String* name,
+ int save_at_depth,
Label* miss);
protected:
@@ -538,7 +552,7 @@ class CallStubCompiler: public StubCompiler {
explicit CallStubCompiler(int argc, InLoopFlag in_loop)
: arguments_(argc), in_loop_(in_loop) { }
- Object* CompileCallField(Object* object,
+ Object* CompileCallField(JSObject* object,
JSObject* holder,
int index,
String* name);
@@ -547,7 +561,7 @@ class CallStubCompiler: public StubCompiler {
JSFunction* function,
String* name,
CheckType check);
- Object* CompileCallInterceptor(Object* object,
+ Object* CompileCallInterceptor(JSObject* object,
JSObject* holder,
String* name);
Object* CompileCallGlobal(JSObject* object,
diff --git a/src/top.cc b/src/top.cc
index 056d3840..d1741757 100644
--- a/src/top.cc
+++ b/src/top.cc
@@ -949,10 +949,15 @@ Handle<Context> Top::GetCallingGlobalContext() {
}
+bool Top::CanHaveSpecialFunctions(JSObject* object) {
+ return object->IsJSArray();
+}
+
+
Object* Top::LookupSpecialFunction(JSObject* receiver,
JSObject* prototype,
JSFunction* function) {
- if (receiver->IsJSArray()) {
+ if (CanHaveSpecialFunctions(receiver)) {
FixedArray* table = context()->global_context()->special_function_table();
for (int index = 0; index < table->length(); index +=3) {
if ((prototype == table->get(index)) &&
diff --git a/src/top.h b/src/top.h
index 8780844b..ddc73ba5 100644
--- a/src/top.h
+++ b/src/top.h
@@ -342,6 +342,7 @@ class Top {
return Handle<JSBuiltinsObject>(thread_local_.context_->builtins());
}
+ static bool CanHaveSpecialFunctions(JSObject* object);
static Object* LookupSpecialFunction(JSObject* receiver,
JSObject* prototype,
JSFunction* value);
diff --git a/src/utils.cc b/src/utils.cc
index 374385b6..45a4cd60 100644
--- a/src/utils.cc
+++ b/src/utils.cc
@@ -51,43 +51,6 @@ uint32_t RoundUpToPowerOf2(uint32_t x) {
}
-byte* EncodeInt(byte* p, int x) {
- while (x < -64 || x >= 64) {
- *p++ = static_cast<byte>(x & 127);
- x = ArithmeticShiftRight(x, 7);
- }
- // -64 <= x && x < 64
- *p++ = static_cast<byte>(x + 192);
- return p;
-}
-
-
-byte* DecodeInt(byte* p, int* x) {
- int r = 0;
- unsigned int s = 0;
- byte b = *p++;
- while (b < 128) {
- r |= static_cast<int>(b) << s;
- s += 7;
- b = *p++;
- }
- // b >= 128
- *x = r | ((static_cast<int>(b) - 192) << s);
- return p;
-}
-
-
-byte* EncodeUnsignedIntBackward(byte* p, unsigned int x) {
- while (x >= 128) {
- *--p = static_cast<byte>(x & 127);
- x = x >> 7;
- }
- // x < 128
- *--p = static_cast<byte>(x + 128);
- return p;
-}
-
-
// Thomas Wang, Integer Hash Functions.
// http://www.concentric.net/~Ttwang/tech/inthash.htm
uint32_t ComputeIntegerHash(uint32_t key) {
diff --git a/src/utils.h b/src/utils.h
index 0fd24ec9..2fcd241f 100644
--- a/src/utils.h
+++ b/src/utils.h
@@ -157,7 +157,9 @@ class BitField {
// Returns a uint32_t mask of bit field.
static uint32_t mask() {
- return (1U << (size + shift)) - (1U << shift);
+ // To use all bits of a uint32 in a bitfield without compiler warnings we
+ // have to compute 2^32 without using a shift count of 32.
+ return ((1U << shift) << size) - (1U << shift);
}
// Returns a uint32_t with the bit field value encoded.
@@ -168,54 +170,12 @@ class BitField {
// Extracts the bit field from the value.
static T decode(uint32_t value) {
- return static_cast<T>((value >> shift) & ((1U << (size)) - 1));
+ return static_cast<T>((value & mask()) >> shift);
}
};
// ----------------------------------------------------------------------------
-// Support for compressed, machine-independent encoding
-// and decoding of integer values of arbitrary size.
-
-// Encoding and decoding from/to a buffer at position p;
-// the result is the position after the encoded integer.
-// Small signed integers in the range -64 <= x && x < 64
-// are encoded in 1 byte; larger values are encoded in 2
-// or more bytes. At most sizeof(int) + 1 bytes are used
-// in the worst case.
-byte* EncodeInt(byte* p, int x);
-byte* DecodeInt(byte* p, int* x);
-
-
-// Encoding and decoding from/to a buffer at position p - 1
-// moving backward; the result is the position of the last
-// byte written. These routines are useful to read/write
-// into a buffer starting at the end of the buffer.
-byte* EncodeUnsignedIntBackward(byte* p, unsigned int x);
-
-// The decoding function is inlined since its performance is
-// important to mark-sweep garbage collection.
-inline byte* DecodeUnsignedIntBackward(byte* p, unsigned int* x) {
- byte b = *--p;
- if (b >= 128) {
- *x = static_cast<unsigned int>(b) - 128;
- return p;
- }
- unsigned int r = static_cast<unsigned int>(b);
- unsigned int s = 7;
- b = *--p;
- while (b < 128) {
- r |= static_cast<unsigned int>(b) << s;
- s += 7;
- b = *--p;
- }
- // b >= 128
- *x = r | ((static_cast<unsigned int>(b) - 128) << s);
- return p;
-}
-
-
-// ----------------------------------------------------------------------------
// Hash function.
uint32_t ComputeIntegerHash(uint32_t key);
diff --git a/src/v8-counters.h b/src/v8-counters.h
index 2d24765a..1ba20038 100644
--- a/src/v8-counters.h
+++ b/src/v8-counters.h
@@ -104,71 +104,94 @@ namespace internal {
SC(contexts_created_by_snapshot, V8.ContextsCreatedBySnapshot)
+#define STATS_COUNTER_LIST_2(SC) \
+ /* Number of code stubs. */ \
+ SC(code_stubs, V8.CodeStubs) \
+ /* Amount of stub code. */ \
+ SC(total_stubs_code_size, V8.TotalStubsCodeSize) \
+ /* Amount of (JS) compiled code. */ \
+ SC(total_compiled_code_size, V8.TotalCompiledCodeSize) \
+ SC(gc_compactor_caused_by_request, V8.GCCompactorCausedByRequest) \
+ SC(gc_compactor_caused_by_promoted_data, \
+ V8.GCCompactorCausedByPromotedData) \
+ SC(gc_compactor_caused_by_oldspace_exhaustion, \
+ V8.GCCompactorCausedByOldspaceExhaustion) \
+ SC(gc_compactor_caused_by_weak_handles, \
+ V8.GCCompactorCausedByWeakHandles) \
+ SC(gc_last_resort_from_js, V8.GCLastResortFromJS) \
+ SC(gc_last_resort_from_handles, V8.GCLastResortFromHandles) \
+ /* How is the generic keyed-load stub used? */ \
+ SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi) \
+ SC(keyed_load_generic_symbol, V8.KeyedLoadGenericSymbol) \
+ SC(keyed_load_generic_slow, V8.KeyedLoadGenericSlow) \
+ SC(keyed_load_external_array_slow, V8.KeyedLoadExternalArraySlow) \
+ /* Count how much the monomorphic keyed-load stubs are hit. */ \
+ SC(keyed_load_function_prototype, V8.KeyedLoadFunctionPrototype) \
+ SC(keyed_load_string_length, V8.KeyedLoadStringLength) \
+ SC(keyed_load_array_length, V8.KeyedLoadArrayLength) \
+ SC(keyed_load_constant_function, V8.KeyedLoadConstantFunction) \
+ SC(keyed_load_field, V8.KeyedLoadField) \
+ SC(keyed_load_callback, V8.KeyedLoadCallback) \
+ SC(keyed_load_interceptor, V8.KeyedLoadInterceptor) \
+ SC(keyed_load_inline, V8.KeyedLoadInline) \
+ SC(keyed_load_inline_miss, V8.KeyedLoadInlineMiss) \
+ SC(named_load_inline, V8.NamedLoadInline) \
+ SC(named_load_inline_miss, V8.NamedLoadInlineMiss) \
+ SC(named_load_global_inline, V8.NamedLoadGlobalInline) \
+ SC(named_load_global_inline_miss, V8.NamedLoadGlobalInlineMiss) \
+ SC(keyed_store_field, V8.KeyedStoreField) \
+ SC(keyed_store_inline, V8.KeyedStoreInline) \
+ SC(keyed_store_inline_miss, V8.KeyedStoreInlineMiss) \
+ SC(named_store_global_inline, V8.NamedStoreGlobalInline) \
+ SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \
+ SC(call_const, V8.CallConst) \
+ SC(call_const_fast_api, V8.CallConstFastApi) \
+ SC(call_const_interceptor, V8.CallConstInterceptor) \
+ SC(call_const_interceptor_fast_api, V8.CallConstInterceptorFastApi) \
+ SC(call_global_inline, V8.CallGlobalInline) \
+ SC(call_global_inline_miss, V8.CallGlobalInlineMiss) \
+ SC(constructed_objects, V8.ConstructedObjects) \
+ SC(constructed_objects_runtime, V8.ConstructedObjectsRuntime) \
+ SC(constructed_objects_stub, V8.ConstructedObjectsStub) \
+ SC(array_function_runtime, V8.ArrayFunctionRuntime) \
+ SC(array_function_native, V8.ArrayFunctionNative) \
+ SC(for_in, V8.ForIn) \
+ SC(enum_cache_hits, V8.EnumCacheHits) \
+ SC(enum_cache_misses, V8.EnumCacheMisses) \
+ SC(reloc_info_count, V8.RelocInfoCount) \
+ SC(reloc_info_size, V8.RelocInfoSize) \
+ SC(zone_segment_bytes, V8.ZoneSegmentBytes) \
+ SC(compute_entry_frame, V8.ComputeEntryFrame) \
+ SC(generic_binary_stub_calls, V8.GenericBinaryStubCalls) \
+ SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs) \
+ SC(string_add_runtime, V8.StringAddRuntime) \
+ SC(string_add_native, V8.StringAddNative) \
+ SC(sub_string_runtime, V8.SubStringRuntime) \
+ SC(sub_string_native, V8.SubStringNative) \
+ SC(string_compare_native, V8.StringCompareNative) \
+ SC(string_compare_runtime, V8.StringCompareRuntime) \
+ SC(regexp_entry_runtime, V8.RegExpEntryRuntime) \
+ SC(regexp_entry_native, V8.RegExpEntryNative) \
+ SC(number_to_string_native, V8.NumberToStringNative) \
+ SC(number_to_string_runtime, V8.NumberToStringRuntime) \
+ SC(math_abs, V8.MathAbs) \
+ SC(math_acos, V8.MathAcos) \
+ SC(math_asin, V8.MathAsin) \
+ SC(math_atan, V8.MathAtan) \
+ SC(math_atan2, V8.MathAtan2) \
+ SC(math_ceil, V8.MathCeil) \
+ SC(math_cos, V8.MathCos) \
+ SC(math_exp, V8.MathExp) \
+ SC(math_floor, V8.MathFloor) \
+ SC(math_log, V8.MathLog) \
+ SC(math_pow, V8.MathPow) \
+ SC(math_round, V8.MathRound) \
+ SC(math_sin, V8.MathSin) \
+ SC(math_sqrt, V8.MathSqrt) \
+ SC(math_tan, V8.MathTan) \
+ SC(transcendental_cache_hit, V8.TranscendentalCacheHit) \
+ SC(transcendental_cache_miss, V8.TranscendentalCacheMiss)
-#define STATS_COUNTER_LIST_2(SC) \
- /* Number of code stubs. */ \
- SC(code_stubs, V8.CodeStubs) \
- /* Amount of stub code. */ \
- SC(total_stubs_code_size, V8.TotalStubsCodeSize) \
- /* Amount of (JS) compiled code. */ \
- SC(total_compiled_code_size, V8.TotalCompiledCodeSize) \
- SC(gc_compactor_caused_by_request, V8.GCCompactorCausedByRequest) \
- SC(gc_compactor_caused_by_promoted_data, \
- V8.GCCompactorCausedByPromotedData) \
- SC(gc_compactor_caused_by_oldspace_exhaustion, \
- V8.GCCompactorCausedByOldspaceExhaustion) \
- SC(gc_compactor_caused_by_weak_handles, \
- V8.GCCompactorCausedByWeakHandles) \
- SC(gc_last_resort_from_js, V8.GCLastResortFromJS) \
- SC(gc_last_resort_from_handles, V8.GCLastResortFromHandles) \
- /* How is the generic keyed-load stub used? */ \
- SC(keyed_load_generic_smi, V8.KeyedLoadGenericSmi) \
- SC(keyed_load_generic_symbol, V8.KeyedLoadGenericSymbol) \
- SC(keyed_load_generic_slow, V8.KeyedLoadGenericSlow) \
- SC(keyed_load_external_array_slow, V8.KeyedLoadExternalArraySlow) \
- /* Count how much the monomorphic keyed-load stubs are hit. */ \
- SC(keyed_load_function_prototype, V8.KeyedLoadFunctionPrototype) \
- SC(keyed_load_string_length, V8.KeyedLoadStringLength) \
- SC(keyed_load_array_length, V8.KeyedLoadArrayLength) \
- SC(keyed_load_constant_function, V8.KeyedLoadConstantFunction) \
- SC(keyed_load_field, V8.KeyedLoadField) \
- SC(keyed_load_callback, V8.KeyedLoadCallback) \
- SC(keyed_load_interceptor, V8.KeyedLoadInterceptor) \
- SC(keyed_load_inline, V8.KeyedLoadInline) \
- SC(keyed_load_inline_miss, V8.KeyedLoadInlineMiss) \
- SC(named_load_inline, V8.NamedLoadInline) \
- SC(named_load_inline_miss, V8.NamedLoadInlineMiss) \
- SC(named_load_global_inline, V8.NamedLoadGlobalInline) \
- SC(named_load_global_inline_miss, V8.NamedLoadGlobalInlineMiss) \
- SC(keyed_store_field, V8.KeyedStoreField) \
- SC(keyed_store_inline, V8.KeyedStoreInline) \
- SC(keyed_store_inline_miss, V8.KeyedStoreInlineMiss) \
- SC(named_store_global_inline, V8.NamedStoreGlobalInline) \
- SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \
- SC(call_global_inline, V8.CallGlobalInline) \
- SC(call_global_inline_miss, V8.CallGlobalInlineMiss) \
- SC(constructed_objects, V8.ConstructedObjects) \
- SC(constructed_objects_runtime, V8.ConstructedObjectsRuntime) \
- SC(constructed_objects_stub, V8.ConstructedObjectsStub) \
- SC(array_function_runtime, V8.ArrayFunctionRuntime) \
- SC(array_function_native, V8.ArrayFunctionNative) \
- SC(for_in, V8.ForIn) \
- SC(enum_cache_hits, V8.EnumCacheHits) \
- SC(enum_cache_misses, V8.EnumCacheMisses) \
- SC(reloc_info_count, V8.RelocInfoCount) \
- SC(reloc_info_size, V8.RelocInfoSize) \
- SC(zone_segment_bytes, V8.ZoneSegmentBytes) \
- SC(compute_entry_frame, V8.ComputeEntryFrame) \
- SC(generic_binary_stub_calls, V8.GenericBinaryStubCalls) \
- SC(generic_binary_stub_calls_regs, V8.GenericBinaryStubCallsRegs) \
- SC(string_add_runtime, V8.StringAddRuntime) \
- SC(string_add_native, V8.StringAddNative) \
- SC(sub_string_runtime, V8.SubStringRuntime) \
- SC(sub_string_native, V8.SubStringNative) \
- SC(string_compare_native, V8.StringCompareNative) \
- SC(string_compare_runtime, V8.StringCompareRuntime) \
- SC(regexp_entry_runtime, V8.RegExpEntryRuntime) \
- SC(regexp_entry_native, V8.RegExpEntryNative)
// This file contains all the v8 counters that are in use.
class Counters : AllStatic {
diff --git a/src/v8natives.js b/src/v8natives.js
index 5a47211a..6a32d7bd 100644
--- a/src/v8natives.js
+++ b/src/v8natives.js
@@ -56,7 +56,7 @@ function InstallFunctions(object, attributes, functions) {
%FunctionSetName(f, key);
%SetProperty(object, key, f, attributes);
}
- %TransformToFastProperties(object);
+ %ToFastProperties(object);
}
// Emulates JSC by installing functions on a hidden prototype that
@@ -623,9 +623,8 @@ function ObjectGetOwnPropertyNames(obj) {
if (%GetInterceptorInfo(obj) & 1) {
var indexedInterceptorNames =
%GetIndexedInterceptorElementNames(obj);
- if (indexedInterceptorNames) {
+ if (indexedInterceptorNames)
propertyNames = propertyNames.concat(indexedInterceptorNames);
- }
}
// Find all the named properties.
@@ -643,6 +642,10 @@ function ObjectGetOwnPropertyNames(obj) {
}
}
+ // Property names are expected to be strings.
+ for (var i = 0; i < propertyNames.length; ++i)
+ propertyNames[i] = ToString(propertyNames[i]);
+
return propertyNames;
}
@@ -911,7 +914,7 @@ function SetupNumber() {
"POSITIVE_INFINITY",
1/0,
DONT_ENUM | DONT_DELETE | READ_ONLY);
- %TransformToFastProperties($Number);
+ %ToFastProperties($Number);
// Setup non-enumerable functions on the Number prototype object.
InstallFunctions($Number.prototype, DONT_ENUM, $Array(
diff --git a/src/version.cc b/src/version.cc
index a2008616..f6d84f31 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,7 +34,7 @@
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 2
#define MINOR_VERSION 1
-#define BUILD_NUMBER 1
+#define BUILD_NUMBER 3
#define PATCH_LEVEL 0
#define CANDIDATE_VERSION true
diff --git a/src/virtual-frame.cc b/src/virtual-frame.cc
index 44e5fae4..3624e254 100644
--- a/src/virtual-frame.cc
+++ b/src/virtual-frame.cc
@@ -48,7 +48,13 @@ VirtualFrame::VirtualFrame(VirtualFrame* original)
}
-FrameElement VirtualFrame::CopyElementAt(int index) {
+// Create a duplicate of an existing valid frame element.
+// We can pass an optional number type information that will override the
+// existing information about the backing element. The new information must
+// not conflict with the existing type information and must be equally or
+// more precise. The default parameter value kUninitialized means that there
+// is no additional information.
+FrameElement VirtualFrame::CopyElementAt(int index, NumberInfo::Type info) {
ASSERT(index >= 0);
ASSERT(index < element_count());
@@ -71,15 +77,26 @@ FrameElement VirtualFrame::CopyElementAt(int index) {
// Fall through.
case FrameElement::MEMORY: // Fall through.
- case FrameElement::REGISTER:
+ case FrameElement::REGISTER: {
// All copies are backed by memory or register locations.
result.set_type(FrameElement::COPY);
result.clear_copied();
result.clear_sync();
result.set_index(index);
elements_[index].set_copied();
+ // Update backing element's number information.
+ NumberInfo::Type existing = elements_[index].number_info();
+ ASSERT(existing != NumberInfo::kUninitialized);
+ // Assert that the new type information (a) does not conflict with the
+ // existing one and (b) is equally or more precise.
+ ASSERT((info == NumberInfo::kUninitialized) ||
+ (existing | info) != NumberInfo::kUninitialized);
+ ASSERT(existing <= info);
+ elements_[index].set_number_info(info != NumberInfo::kUninitialized
+ ? info
+ : existing);
break;
-
+ }
case FrameElement::INVALID:
// We should not try to copy invalid elements.
UNREACHABLE();
@@ -98,7 +115,7 @@ void VirtualFrame::Adjust(int count) {
ASSERT(stack_pointer_ == element_count() - 1);
for (int i = 0; i < count; i++) {
- elements_.Add(FrameElement::MemoryElement());
+ elements_.Add(FrameElement::MemoryElement(NumberInfo::kUnknown));
}
stack_pointer_ += count;
}
@@ -144,8 +161,16 @@ void VirtualFrame::SpillElementAt(int index) {
if (!elements_[index].is_valid()) return;
SyncElementAt(index);
+ // Number type information is preserved.
+ // Copies get their number information from their backing element.
+ NumberInfo::Type info;
+ if (!elements_[index].is_copy()) {
+ info = elements_[index].number_info();
+ } else {
+ info = elements_[elements_[index].index()].number_info();
+ }
// The element is now in memory. Its copied flag is preserved.
- FrameElement new_element = FrameElement::MemoryElement();
+ FrameElement new_element = FrameElement::MemoryElement(info);
if (elements_[index].is_copied()) {
new_element.set_copied();
}
@@ -268,7 +293,6 @@ void VirtualFrame::SetElementAt(int index, Result* value) {
InvalidateFrameSlotAt(frame_index);
- FrameElement new_element;
if (value->is_register()) {
if (is_used(value->reg())) {
// The register already appears on the frame. Either the existing
@@ -301,7 +325,8 @@ void VirtualFrame::SetElementAt(int index, Result* value) {
Use(value->reg(), frame_index);
elements_[frame_index] =
FrameElement::RegisterElement(value->reg(),
- FrameElement::NOT_SYNCED);
+ FrameElement::NOT_SYNCED,
+ value->number_info());
}
} else {
ASSERT(value->is_constant());
@@ -318,16 +343,15 @@ void VirtualFrame::PushFrameSlotAt(int index) {
}
-void VirtualFrame::Push(Register reg) {
+void VirtualFrame::Push(Register reg, NumberInfo::Type info) {
if (is_used(reg)) {
int index = register_location(reg);
- FrameElement element = CopyElementAt(index);
+ FrameElement element = CopyElementAt(index, info);
elements_.Add(element);
} else {
Use(reg, element_count());
FrameElement element =
- FrameElement::RegisterElement(reg,
- FrameElement::NOT_SYNCED);
+ FrameElement::RegisterElement(reg, FrameElement::NOT_SYNCED, info);
elements_.Add(element);
}
}
diff --git a/src/x64/assembler-x64.cc b/src/x64/assembler-x64.cc
index 1b6874e9..a994f45b 100644
--- a/src/x64/assembler-x64.cc
+++ b/src/x64/assembler-x64.cc
@@ -34,46 +34,6 @@ namespace v8 {
namespace internal {
// -----------------------------------------------------------------------------
-// Implementation of Register
-
-Register rax = { 0 };
-Register rcx = { 1 };
-Register rdx = { 2 };
-Register rbx = { 3 };
-Register rsp = { 4 };
-Register rbp = { 5 };
-Register rsi = { 6 };
-Register rdi = { 7 };
-Register r8 = { 8 };
-Register r9 = { 9 };
-Register r10 = { 10 };
-Register r11 = { 11 };
-Register r12 = { 12 };
-Register r13 = { 13 };
-Register r14 = { 14 };
-Register r15 = { 15 };
-
-Register no_reg = { -1 };
-
-XMMRegister xmm0 = { 0 };
-XMMRegister xmm1 = { 1 };
-XMMRegister xmm2 = { 2 };
-XMMRegister xmm3 = { 3 };
-XMMRegister xmm4 = { 4 };
-XMMRegister xmm5 = { 5 };
-XMMRegister xmm6 = { 6 };
-XMMRegister xmm7 = { 7 };
-XMMRegister xmm8 = { 8 };
-XMMRegister xmm9 = { 9 };
-XMMRegister xmm10 = { 10 };
-XMMRegister xmm11 = { 11 };
-XMMRegister xmm12 = { 12 };
-XMMRegister xmm13 = { 13 };
-XMMRegister xmm14 = { 14 };
-XMMRegister xmm15 = { 15 };
-
-
-// -----------------------------------------------------------------------------
// Implementation of CpuFeatures
// The required user mode extensions in X64 are (from AMD64 ABI Table A.1):
@@ -224,7 +184,7 @@ void RelocInfo::PatchCode(byte* instructions, int instruction_count) {
// -----------------------------------------------------------------------------
// Implementation of Operand
-Operand::Operand(Register base, int32_t disp): rex_(0) {
+Operand::Operand(Register base, int32_t disp) : rex_(0) {
len_ = 1;
if (base.is(rsp) || base.is(r12)) {
// SIB byte is needed to encode (rsp + offset) or (r12 + offset).
@@ -246,7 +206,7 @@ Operand::Operand(Register base, int32_t disp): rex_(0) {
Operand::Operand(Register base,
Register index,
ScaleFactor scale,
- int32_t disp): rex_(0) {
+ int32_t disp) : rex_(0) {
ASSERT(!index.is(rsp));
len_ = 1;
set_sib(scale, index, base);
@@ -264,6 +224,17 @@ Operand::Operand(Register base,
}
+Operand::Operand(Register index,
+ ScaleFactor scale,
+ int32_t disp) : rex_(0) {
+ ASSERT(!index.is(rsp));
+ len_ = 1;
+ set_modrm(0, rsp);
+ set_sib(scale, index, rbp);
+ set_disp32(disp);
+}
+
+
// -----------------------------------------------------------------------------
// Implementation of Assembler.
@@ -2508,6 +2479,38 @@ void Assembler::divsd(XMMRegister dst, XMMRegister src) {
}
+void Assembler::xorpd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ emit(0x66);
+ emit_optional_rex_32(dst, src);
+ emit(0x0f);
+ emit(0x57);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::comisd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ emit(0x66);
+ emit_optional_rex_32(dst, src);
+ emit(0x0f);
+ emit(0x2f);
+ emit_sse_operand(dst, src);
+}
+
+
+void Assembler::ucomisd(XMMRegister dst, XMMRegister src) {
+ EnsureSpace ensure_space(this);
+ last_pc_ = pc_;
+ emit(0x66);
+ emit_optional_rex_32(dst, src);
+ emit(0x0f);
+ emit(0x2e);
+ emit_sse_operand(dst, src);
+}
+
void Assembler::emit_sse_operand(XMMRegister reg, const Operand& adr) {
Register ireg = { reg.code() };
diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h
index 64fbd888..50195255 100644
--- a/src/x64/assembler-x64.h
+++ b/src/x64/assembler-x64.h
@@ -118,51 +118,23 @@ struct Register {
int code_;
};
-extern Register rax;
-extern Register rcx;
-extern Register rdx;
-extern Register rbx;
-extern Register rsp;
-extern Register rbp;
-extern Register rsi;
-extern Register rdi;
-extern Register r8;
-extern Register r9;
-extern Register r10;
-extern Register r11;
-extern Register r12;
-extern Register r13;
-extern Register r14;
-extern Register r15;
-extern Register no_reg;
-
-
-struct MMXRegister {
- bool is_valid() const { return 0 <= code_ && code_ < 2; }
- int code() const {
- ASSERT(is_valid());
- return code_;
- }
-
- int code_;
-};
-
-extern MMXRegister mm0;
-extern MMXRegister mm1;
-extern MMXRegister mm2;
-extern MMXRegister mm3;
-extern MMXRegister mm4;
-extern MMXRegister mm5;
-extern MMXRegister mm6;
-extern MMXRegister mm7;
-extern MMXRegister mm8;
-extern MMXRegister mm9;
-extern MMXRegister mm10;
-extern MMXRegister mm11;
-extern MMXRegister mm12;
-extern MMXRegister mm13;
-extern MMXRegister mm14;
-extern MMXRegister mm15;
+const Register rax = { 0 };
+const Register rcx = { 1 };
+const Register rdx = { 2 };
+const Register rbx = { 3 };
+const Register rsp = { 4 };
+const Register rbp = { 5 };
+const Register rsi = { 6 };
+const Register rdi = { 7 };
+const Register r8 = { 8 };
+const Register r9 = { 9 };
+const Register r10 = { 10 };
+const Register r11 = { 11 };
+const Register r12 = { 12 };
+const Register r13 = { 13 };
+const Register r14 = { 14 };
+const Register r15 = { 15 };
+const Register no_reg = { -1 };
struct XMMRegister {
@@ -186,22 +158,22 @@ struct XMMRegister {
int code_;
};
-extern XMMRegister xmm0;
-extern XMMRegister xmm1;
-extern XMMRegister xmm2;
-extern XMMRegister xmm3;
-extern XMMRegister xmm4;
-extern XMMRegister xmm5;
-extern XMMRegister xmm6;
-extern XMMRegister xmm7;
-extern XMMRegister xmm8;
-extern XMMRegister xmm9;
-extern XMMRegister xmm10;
-extern XMMRegister xmm11;
-extern XMMRegister xmm12;
-extern XMMRegister xmm13;
-extern XMMRegister xmm14;
-extern XMMRegister xmm15;
+const XMMRegister xmm0 = { 0 };
+const XMMRegister xmm1 = { 1 };
+const XMMRegister xmm2 = { 2 };
+const XMMRegister xmm3 = { 3 };
+const XMMRegister xmm4 = { 4 };
+const XMMRegister xmm5 = { 5 };
+const XMMRegister xmm6 = { 6 };
+const XMMRegister xmm7 = { 7 };
+const XMMRegister xmm8 = { 8 };
+const XMMRegister xmm9 = { 9 };
+const XMMRegister xmm10 = { 10 };
+const XMMRegister xmm11 = { 11 };
+const XMMRegister xmm12 = { 12 };
+const XMMRegister xmm13 = { 13 };
+const XMMRegister xmm14 = { 14 };
+const XMMRegister xmm15 = { 15 };
enum Condition {
// any value < 0 is considered no_condition
@@ -308,7 +280,6 @@ enum ScaleFactor {
times_4 = 2,
times_8 = 3,
times_int_size = times_4,
- times_half_pointer_size = times_4,
times_pointer_size = times_8
};
@@ -1122,6 +1093,10 @@ class Assembler : public Malloced {
void mulsd(XMMRegister dst, XMMRegister src);
void divsd(XMMRegister dst, XMMRegister src);
+ void xorpd(XMMRegister dst, XMMRegister src);
+
+ void comisd(XMMRegister dst, XMMRegister src);
+ void ucomisd(XMMRegister dst, XMMRegister src);
void emit_sse_operand(XMMRegister dst, XMMRegister src);
void emit_sse_operand(XMMRegister reg, const Operand& adr);
@@ -1168,14 +1143,6 @@ class Assembler : public Malloced {
static const int kMaximalBufferSize = 512*MB;
static const int kMinimalBufferSize = 4*KB;
- protected:
- // void movsd(XMMRegister dst, const Operand& src);
- // void movsd(const Operand& dst, XMMRegister src);
-
- // void emit_sse_operand(XMMRegister reg, const Operand& adr);
- // void emit_sse_operand(XMMRegister dst, XMMRegister src);
-
-
private:
byte* addr_at(int pos) { return buffer_ + pos; }
byte byte_at(int pos) { return buffer_[pos]; }
diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc
index 0b95bba6..b3c5e33f 100644
--- a/src/x64/builtins-x64.cc
+++ b/src/x64/builtins-x64.cc
@@ -185,14 +185,14 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
// Stack Layout:
- // rsp: return address
- // +1: Argument n
- // +2: Argument n-1
+ // rsp[0]: Return address
+ // rsp[1]: Argument n
+ // rsp[2]: Argument n-1
// ...
- // +n: Argument 1 = receiver
- // +n+1: Argument 0 = function to call
+ // rsp[n]: Argument 1
+ // rsp[n+1]: Receiver (function to call)
//
- // rax contains the number of arguments, n, not counting the function.
+ // rax contains the number of arguments, n, not counting the receiver.
//
// 1. Make sure we have at least one argument.
{ Label done;
@@ -205,31 +205,23 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ bind(&done);
}
- // 2. Get the function to call from the stack.
- { Label done, non_function, function;
- // The function to call is at position n+1 on the stack.
- __ movq(rdi, Operand(rsp, rax, times_pointer_size, +1 * kPointerSize));
- __ JumpIfSmi(rdi, &non_function);
- __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
- __ j(equal, &function);
-
- // Non-function called: Clear the function to force exception.
- __ bind(&non_function);
- __ xor_(rdi, rdi);
- __ jmp(&done);
+ // 2. Get the function to call (passed as receiver) from the stack, check
+ // if it is a function.
+ Label non_function;
+ // The function to call is at position n+1 on the stack.
+ __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize));
+ __ JumpIfSmi(rdi, &non_function);
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
+ __ j(not_equal, &non_function);
- // Function called: Change context eagerly to get the right global object.
- __ bind(&function);
+ // 3a. Patch the first argument if necessary when calling a function.
+ Label shift_arguments;
+ { Label convert_to_object, use_global_receiver, patch_receiver;
+ // Change context eagerly in case we need the global receiver.
__ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
- __ bind(&done);
- }
-
- // 3. Make sure first argument is an object; convert if necessary.
- { Label call_to_object, use_global_receiver, patch_receiver, done;
__ movq(rbx, Operand(rsp, rax, times_pointer_size, 0));
-
- __ JumpIfSmi(rbx, &call_to_object);
+ __ JumpIfSmi(rbx, &convert_to_object);
__ CompareRoot(rbx, Heap::kNullValueRootIndex);
__ j(equal, &use_global_receiver);
@@ -237,31 +229,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ j(equal, &use_global_receiver);
__ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx);
- __ j(below, &call_to_object);
+ __ j(below, &convert_to_object);
__ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE);
- __ j(below_equal, &done);
-
- __ bind(&call_to_object);
- __ EnterInternalFrame(); // preserves rax, rbx, rdi
+ __ j(below_equal, &shift_arguments);
- // Store the arguments count on the stack (smi tagged).
+ __ bind(&convert_to_object);
+ __ EnterInternalFrame(); // In order to preserve argument count.
__ Integer32ToSmi(rax, rax);
__ push(rax);
- __ push(rdi); // save edi across the call
__ push(rbx);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ movq(rbx, rax);
- __ pop(rdi); // restore edi after the call
- // Get the arguments count and untag it.
__ pop(rax);
__ SmiToInteger32(rax, rax);
-
__ LeaveInternalFrame();
+ // Restore the function to rdi.
+ __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize));
__ jmp(&patch_receiver);
- // Use the global receiver object from the called function as the receiver.
+ // Use the global receiver object from the called function as the
+ // receiver.
__ bind(&use_global_receiver);
const int kGlobalIndex =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
@@ -273,48 +262,57 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ bind(&patch_receiver);
__ movq(Operand(rsp, rax, times_pointer_size, 0), rbx);
- __ bind(&done);
+ __ jmp(&shift_arguments);
}
- // 4. Shift stuff one slot down the stack.
+
+ // 3b. Patch the first argument when calling a non-function. The
+ // CALL_NON_FUNCTION builtin expects the non-function callee as
+ // receiver, so overwrite the first argument which will ultimately
+ // become the receiver.
+ __ bind(&non_function);
+ __ movq(Operand(rsp, rax, times_pointer_size, 0), rdi);
+ __ xor_(rdi, rdi);
+
+ // 4. Shift arguments and return address one slot down on the stack
+ // (overwriting the original receiver). Adjust argument count to make
+ // the original first argument the new receiver.
+ __ bind(&shift_arguments);
{ Label loop;
- __ lea(rcx, Operand(rax, +1)); // +1 ~ copy receiver too
+ __ movq(rcx, rax);
__ bind(&loop);
__ movq(rbx, Operand(rsp, rcx, times_pointer_size, 0));
__ movq(Operand(rsp, rcx, times_pointer_size, 1 * kPointerSize), rbx);
__ decq(rcx);
- __ j(not_zero, &loop);
+ __ j(not_sign, &loop); // While non-negative (to copy return address).
+ __ pop(rbx); // Discard copy of return address.
+ __ decq(rax); // One fewer argument (first argument is new receiver).
}
- // 5. Remove TOS (copy of last arguments), but keep return address.
- __ pop(rbx);
- __ pop(rcx);
- __ push(rbx);
- __ decq(rax);
-
- // 6. Check that function really was a function and get the code to
- // call from the function and check that the number of expected
- // arguments matches what we're providing.
- { Label invoke, trampoline;
+ // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
+ { Label function;
__ testq(rdi, rdi);
- __ j(not_zero, &invoke);
+ __ j(not_zero, &function);
__ xor_(rbx, rbx);
__ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
- __ bind(&trampoline);
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
RelocInfo::CODE_TARGET);
-
- __ bind(&invoke);
- __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
- __ movsxlq(rbx,
- FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset));
- __ movq(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset));
- __ lea(rdx, FieldOperand(rdx, Code::kHeaderSize));
- __ cmpq(rax, rbx);
- __ j(not_equal, &trampoline);
+ __ bind(&function);
}
- // 7. Jump (tail-call) to the code in register edx without checking arguments.
+ // 5b. Get the code to call from the function and check that the number of
+ // expected arguments matches what we're providing. If so, jump
+ // (tail-call) to the code in register edx without checking arguments.
+ __ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
+ __ movsxlq(rbx,
+ FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset));
+ __ movq(rdx, FieldOperand(rdx, SharedFunctionInfo::kCodeOffset));
+ __ lea(rdx, FieldOperand(rdx, Code::kHeaderSize));
+ __ cmpq(rax, rbx);
+ __ j(not_equal,
+ Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
+ RelocInfo::CODE_TARGET);
+
ParameterCount expected(0);
__ InvokeCode(rdx, expected, expected, JUMP_FUNCTION);
}
@@ -586,6 +584,7 @@ static void AllocateJSArray(MacroAssembler* masm,
JSFunction::kPrototypeOrInitialMapOffset));
// Check whether an empty sized array is requested.
+ __ SmiToInteger64(array_size, array_size);
__ testq(array_size, array_size);
__ j(not_zero, &not_empty);
@@ -605,7 +604,7 @@ static void AllocateJSArray(MacroAssembler* masm,
__ bind(&not_empty);
ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
__ AllocateInNewSpace(JSArray::kSize + FixedArray::kHeaderSize,
- times_half_pointer_size, // array_size is a smi.
+ times_pointer_size,
array_size,
result,
elements_array_end,
@@ -618,19 +617,20 @@ static void AllocateJSArray(MacroAssembler* masm,
// result: JSObject
// elements_array: initial map
// elements_array_end: start of next object
- // array_size: size of array (smi)
+ // array_size: size of array
__ bind(&allocated);
__ movq(FieldOperand(result, JSObject::kMapOffset), elements_array);
__ Move(elements_array, Factory::empty_fixed_array());
__ movq(FieldOperand(result, JSArray::kPropertiesOffset), elements_array);
// Field JSArray::kElementsOffset is initialized later.
- __ movq(FieldOperand(result, JSArray::kLengthOffset), array_size);
+ __ Integer32ToSmi(scratch, array_size);
+ __ movq(FieldOperand(result, JSArray::kLengthOffset), scratch);
// Calculate the location of the elements array and set elements array member
// of the JSArray.
// result: JSObject
// elements_array_end: start of next object
- // array_size: size of array (smi)
+ // array_size: size of array
__ lea(elements_array, Operand(result, JSArray::kSize));
__ movq(FieldOperand(result, JSArray::kElementsOffset), elements_array);
@@ -638,9 +638,8 @@ static void AllocateJSArray(MacroAssembler* masm,
// result: JSObject
// elements_array: elements array
// elements_array_end: start of next object
- // array_size: size of array (smi)
+ // array_size: size of array
ASSERT(kSmiTag == 0);
- __ SmiToInteger64(array_size, array_size);
__ Move(FieldOperand(elements_array, JSObject::kMapOffset),
Factory::fixed_array_map());
Label not_empty_2, fill_array;
@@ -900,7 +899,11 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
// edi: called object
// eax: number of arguments
__ bind(&non_function_call);
- // Set expected number of arguments to zero (not changing eax).
+ // CALL_NON_FUNCTION expects the non-function constructor as receiver
+ // (instead of the original receiver from the call site). The receiver is
+ // stack element argc+1.
+ __ movq(Operand(rsp, rax, times_pointer_size, kPointerSize), rdi);
+ // Set expected number of arguments to zero (not changing rax).
__ movq(rbx, Immediate(0));
__ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc
index ee8f251b..e4188836 100644
--- a/src/x64/codegen-x64.cc
+++ b/src/x64/codegen-x64.cc
@@ -277,7 +277,7 @@ void CodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
}
-void CodeGenerator::Generate(CompilationInfo* info, Mode mode) {
+void CodeGenerator::Generate(CompilationInfo* info) {
// Record the position for debugging purposes.
CodeForFunctionPosition(info->function());
@@ -316,7 +316,7 @@ void CodeGenerator::Generate(CompilationInfo* info, Mode mode) {
// rsi: callee's context
allocator_->Initialize();
- if (mode == PRIMARY) {
+ if (info->mode() == CompilationInfo::PRIMARY) {
frame_->Enter();
// Allocate space for locals and initialize them.
@@ -407,6 +407,12 @@ void CodeGenerator::Generate(CompilationInfo* info, Mode mode) {
// frame to match this state.
frame_->Adjust(3);
allocator_->Unuse(rdi);
+
+ // Bind all the bailout labels to the beginning of the function.
+ List<CompilationInfo::Bailout*>* bailouts = info->bailouts();
+ for (int i = 0; i < bailouts->length(); i++) {
+ __ bind(bailouts->at(i)->label());
+ }
}
// Initialize the function return target after the locals are set
@@ -1221,7 +1227,7 @@ void CodeGenerator::VisitSwitchStatement(SwitchStatement* node) {
// Compare and branch to the body if true or the next test if
// false. Prefer the next test as a fall through.
ControlDestination dest(clause->body_target(), &next_test, false);
- Comparison(equal, true, &dest);
+ Comparison(node, equal, true, &dest);
// If the comparison fell through to the true target, jump to the
// actual body.
@@ -2218,8 +2224,7 @@ void CodeGenerator::VisitDebuggerStatement(DebuggerStatement* node) {
// Spill everything, even constants, to the frame.
frame_->SpillAll();
- DebuggerStatementStub ces;
- frame_->CallStub(&ces, 0);
+ frame_->DebugBreak();
// Ignore the return value.
#endif
}
@@ -2496,17 +2501,19 @@ void CodeGenerator::VisitArrayLiteral(ArrayLiteral* node) {
// Load the literals array of the function.
__ movq(literals.reg(),
FieldOperand(literals.reg(), JSFunction::kLiteralsOffset));
- // Literal array.
+
frame_->Push(&literals);
- // Literal index.
frame_->Push(Smi::FromInt(node->literal_index()));
- // Constant elements.
frame_->Push(node->constant_elements());
+ int length = node->values()->length();
Result clone;
if (node->depth() > 1) {
clone = frame_->CallRuntime(Runtime::kCreateArrayLiteral, 3);
- } else {
+ } else if (length > FastCloneShallowArrayStub::kMaximumLength) {
clone = frame_->CallRuntime(Runtime::kCreateArrayLiteralShallow, 3);
+ } else {
+ FastCloneShallowArrayStub stub(length);
+ clone = frame_->CallStub(&stub, 3);
}
frame_->Push(&clone);
@@ -2756,9 +2763,6 @@ void CodeGenerator::VisitCall(Call* node) {
// JavaScript example: 'foo(1, 2, 3)' // foo is global
// ----------------------------------
- // Push the name of the function and the receiver onto the stack.
- frame_->Push(var->name());
-
// Pass the global object as the receiver and let the IC stub
// patch the stack to use the global proxy as 'this' in the
// invoked function.
@@ -2770,6 +2774,9 @@ void CodeGenerator::VisitCall(Call* node) {
Load(args->at(i));
}
+ // Push the name of the function on the frame.
+ frame_->Push(var->name());
+
// Call the IC initialization code.
CodeForSourcePosition(node->position());
Result result = frame_->CallCallIC(RelocInfo::CODE_TARGET_CONTEXT,
@@ -2777,7 +2784,7 @@ void CodeGenerator::VisitCall(Call* node) {
loop_nesting());
frame_->RestoreContextRegister();
// Replace the function on the stack with the result.
- frame_->SetElementAt(0, &result);
+ frame_->Push(&result);
} else if (var != NULL && var->slot() != NULL &&
var->slot()->type() == Slot::LOOKUP) {
@@ -2830,8 +2837,7 @@ void CodeGenerator::VisitCall(Call* node) {
node->position());
} else {
- // Push the name of the function and the receiver onto the stack.
- frame_->Push(name);
+ // Push the receiver onto the frame.
Load(property->obj());
// Load the arguments.
@@ -2840,14 +2846,16 @@ void CodeGenerator::VisitCall(Call* node) {
Load(args->at(i));
}
+ // Push the name of the function onto the frame.
+ frame_->Push(name);
+
// Call the IC initialization code.
CodeForSourcePosition(node->position());
Result result = frame_->CallCallIC(RelocInfo::CODE_TARGET,
arg_count,
loop_nesting());
frame_->RestoreContextRegister();
- // Replace the function on the stack with the result.
- frame_->SetElementAt(0, &result);
+ frame_->Push(&result);
}
} else {
@@ -2938,8 +2946,6 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
Runtime::Function* function = node->function();
if (function == NULL) {
- // Prepare stack for calling JS runtime function.
- frame_->Push(node->name());
// Push the builtins object found in the current global object.
Result temp = allocator()->Allocate();
ASSERT(temp.is_valid());
@@ -2957,11 +2963,12 @@ void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
if (function == NULL) {
// Call the JS runtime function.
+ frame_->Push(node->name());
Result answer = frame_->CallCallIC(RelocInfo::CODE_TARGET,
arg_count,
loop_nesting_);
frame_->RestoreContextRegister();
- frame_->SetElementAt(0, &answer);
+ frame_->Push(&answer);
} else {
// Call the C runtime function.
Result answer = frame_->CallRuntime(function, arg_count);
@@ -3070,7 +3077,6 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
case Token::SUB: {
GenericUnaryOpStub stub(Token::SUB, overwrite);
- // TODO(1222589): remove dependency of TOS being cached inside stub
Result operand = frame_->Pop();
Result answer = frame_->CallStub(&stub, &operand);
frame_->Push(&answer);
@@ -3586,7 +3592,7 @@ void CodeGenerator::VisitCompareOperation(CompareOperation* node) {
}
Load(left);
Load(right);
- Comparison(cc, strict, destination());
+ Comparison(node, cc, strict, destination());
}
@@ -3627,6 +3633,22 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
}
+void CodeGenerator::GenerateIsRegExp(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+ Load(args->at(0));
+ Result value = frame_->Pop();
+ value.ToRegister();
+ ASSERT(value.is_valid());
+ Condition is_smi = masm_->CheckSmi(value.reg());
+ destination()->false_target()->Branch(is_smi);
+ // It is a heap object - get map.
+ // Check if the object is a regexp.
+ __ CmpObjectType(value.reg(), JS_REGEXP_TYPE, kScratchRegister);
+ value.Unuse();
+ destination()->Split(equal);
+}
+
+
void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
// This generates a fast version of:
// (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
@@ -3971,6 +3993,35 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
}
+void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 1);
+
+ // Load the argument on the stack and jump to the runtime.
+ Load(args->at(0));
+
+ Result answer = frame_->CallRuntime(Runtime::kNumberToString, 1);
+ frame_->Push(&answer);
+}
+
+
+void CodeGenerator::GenerateMathSin(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 1);
+ // Load the argument on the stack and jump to the runtime.
+ Load(args->at(0));
+ Result answer = frame_->CallRuntime(Runtime::kMath_sin, 1);
+ frame_->Push(&answer);
+}
+
+
+void CodeGenerator::GenerateMathCos(ZoneList<Expression*>* args) {
+ ASSERT_EQ(args->length(), 1);
+ // Load the argument on the stack and jump to the runtime.
+ Load(args->at(0));
+ Result answer = frame_->CallRuntime(Runtime::kMath_cos, 1);
+ frame_->Push(&answer);
+}
+
+
void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
ASSERT_EQ(2, args->length());
@@ -4260,34 +4311,52 @@ void CodeGenerator::ToBoolean(ControlDestination* dest) {
// The value to convert should be popped from the frame.
Result value = frame_->Pop();
value.ToRegister();
- // Fast case checks.
- // 'false' => false.
- __ CompareRoot(value.reg(), Heap::kFalseValueRootIndex);
- dest->false_target()->Branch(equal);
+ if (value.is_number()) {
+ Comment cmnt(masm_, "ONLY_NUMBER");
+ // Fast case if NumberInfo indicates only numbers.
+ if (FLAG_debug_code) {
+ __ AbortIfNotNumber(value.reg(), "ToBoolean operand is not a number.");
+ }
+ // Smi => false iff zero.
+ __ SmiCompare(value.reg(), Smi::FromInt(0));
+ dest->false_target()->Branch(equal);
+ Condition is_smi = masm_->CheckSmi(value.reg());
+ dest->true_target()->Branch(is_smi);
+ __ fldz();
+ __ fld_d(FieldOperand(value.reg(), HeapNumber::kValueOffset));
+ __ FCmp();
+ value.Unuse();
+ dest->Split(not_zero);
+ } else {
+ // Fast case checks.
+ // 'false' => false.
+ __ CompareRoot(value.reg(), Heap::kFalseValueRootIndex);
+ dest->false_target()->Branch(equal);
- // 'true' => true.
- __ CompareRoot(value.reg(), Heap::kTrueValueRootIndex);
- dest->true_target()->Branch(equal);
+ // 'true' => true.
+ __ CompareRoot(value.reg(), Heap::kTrueValueRootIndex);
+ dest->true_target()->Branch(equal);
- // 'undefined' => false.
- __ CompareRoot(value.reg(), Heap::kUndefinedValueRootIndex);
- dest->false_target()->Branch(equal);
+ // 'undefined' => false.
+ __ CompareRoot(value.reg(), Heap::kUndefinedValueRootIndex);
+ dest->false_target()->Branch(equal);
- // Smi => false iff zero.
- __ SmiCompare(value.reg(), Smi::FromInt(0));
- dest->false_target()->Branch(equal);
- Condition is_smi = masm_->CheckSmi(value.reg());
- dest->true_target()->Branch(is_smi);
+ // Smi => false iff zero.
+ __ SmiCompare(value.reg(), Smi::FromInt(0));
+ dest->false_target()->Branch(equal);
+ Condition is_smi = masm_->CheckSmi(value.reg());
+ dest->true_target()->Branch(is_smi);
- // Call the stub for all other cases.
- frame_->Push(&value); // Undo the Pop() from above.
- ToBooleanStub stub;
- Result temp = frame_->CallStub(&stub, 1);
- // Convert the result to a condition code.
- __ testq(temp.reg(), temp.reg());
- temp.Unuse();
- dest->Split(not_equal);
+ // Call the stub for all other cases.
+ frame_->Push(&value); // Undo the Pop() from above.
+ ToBooleanStub stub;
+ Result temp = frame_->CallStub(&stub, 1);
+ // Convert the result to a condition code.
+ __ testq(temp.reg(), temp.reg());
+ temp.Unuse();
+ dest->Split(not_equal);
+ }
}
@@ -4868,7 +4937,8 @@ void CodeGenerator::LoadTypeofExpression(Expression* expr) {
}
-void CodeGenerator::Comparison(Condition cc,
+void CodeGenerator::Comparison(AstNode* node,
+ Condition cc,
bool strict,
ControlDestination* dest) {
// Strict only makes sense for equality comparisons.
@@ -4915,7 +4985,8 @@ void CodeGenerator::Comparison(Condition cc,
default:
UNREACHABLE();
}
- } else { // Only one side is a constant Smi.
+ } else {
+ // Only one side is a constant Smi.
// If left side is a constant Smi, reverse the operands.
// Since one side is a constant Smi, conversion order does not matter.
if (left_side_constant_smi) {
@@ -4929,6 +5000,8 @@ void CodeGenerator::Comparison(Condition cc,
// Implement comparison against a constant Smi, inlining the case
// where both sides are Smis.
left_side.ToRegister();
+ Register left_reg = left_side.reg();
+ Handle<Object> right_val = right_side.handle();
// Here we split control flow to the stub call and inlined cases
// before finally splitting it to the control destination. We use
@@ -4936,12 +5009,48 @@ void CodeGenerator::Comparison(Condition cc,
// the first split. We manually handle the off-frame references
// by reconstituting them on the non-fall-through path.
JumpTarget is_smi;
- Register left_reg = left_side.reg();
- Handle<Object> right_val = right_side.handle();
Condition left_is_smi = masm_->CheckSmi(left_side.reg());
is_smi.Branch(left_is_smi);
+ bool is_for_loop_compare = (node->AsCompareOperation() != NULL)
+ && node->AsCompareOperation()->is_for_loop_condition();
+ if (!is_for_loop_compare && right_val->IsSmi()) {
+ // Right side is a constant smi and left side has been checked
+ // not to be a smi.
+ JumpTarget not_number;
+ __ Cmp(FieldOperand(left_reg, HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ not_number.Branch(not_equal, &left_side);
+ __ movsd(xmm1,
+ FieldOperand(left_reg, HeapNumber::kValueOffset));
+ int value = Smi::cast(*right_val)->value();
+ if (value == 0) {
+ __ xorpd(xmm0, xmm0);
+ } else {
+ Result temp = allocator()->Allocate();
+ __ movl(temp.reg(), Immediate(value));
+ __ cvtlsi2sd(xmm0, temp.reg());
+ temp.Unuse();
+ }
+ __ ucomisd(xmm1, xmm0);
+ // Jump to builtin for NaN.
+ not_number.Branch(parity_even, &left_side);
+ left_side.Unuse();
+ Condition double_cc = cc;
+ switch (cc) {
+ case less: double_cc = below; break;
+ case equal: double_cc = equal; break;
+ case less_equal: double_cc = below_equal; break;
+ case greater: double_cc = above; break;
+ case greater_equal: double_cc = above_equal; break;
+ default: UNREACHABLE();
+ }
+ dest->true_target()->Branch(double_cc);
+ dest->false_target()->Jump();
+ not_number.Bind(&left_side);
+ }
+
// Setup and call the compare stub.
CompareStub stub(cc, strict);
Result result = frame_->CallStub(&stub, &left_side, &right_side);
@@ -5114,26 +5223,34 @@ void CodeGenerator::GenericBinaryOperation(Token::Value op,
// Neither operand is known to be a string.
}
- bool left_is_smi = left.is_constant() && left.handle()->IsSmi();
- bool left_is_non_smi = left.is_constant() && !left.handle()->IsSmi();
- bool right_is_smi = right.is_constant() && right.handle()->IsSmi();
- bool right_is_non_smi = right.is_constant() && !right.handle()->IsSmi();
+ bool left_is_smi_constant = left.is_constant() && left.handle()->IsSmi();
+ bool left_is_non_smi_constant = left.is_constant() && !left.handle()->IsSmi();
+ bool right_is_smi_constant = right.is_constant() && right.handle()->IsSmi();
+ bool right_is_non_smi_constant =
+ right.is_constant() && !right.handle()->IsSmi();
- if (left_is_smi && right_is_smi) {
+ if (left_is_smi_constant && right_is_smi_constant) {
// Compute the constant result at compile time, and leave it on the frame.
int left_int = Smi::cast(*left.handle())->value();
int right_int = Smi::cast(*right.handle())->value();
if (FoldConstantSmis(op, left_int, right_int)) return;
}
+ // Get number type of left and right sub-expressions.
+ NumberInfo::Type operands_type =
+ NumberInfo::Combine(left.number_info(), right.number_info());
+
Result answer;
- if (left_is_non_smi || right_is_non_smi) {
- GenericBinaryOpStub stub(op, overwrite_mode, NO_SMI_CODE_IN_STUB);
+ if (left_is_non_smi_constant || right_is_non_smi_constant) {
+ GenericBinaryOpStub stub(op,
+ overwrite_mode,
+ NO_SMI_CODE_IN_STUB,
+ operands_type);
answer = stub.GenerateCall(masm_, frame_, &left, &right);
- } else if (right_is_smi) {
+ } else if (right_is_smi_constant) {
answer = ConstantSmiBinaryOperation(op, &left, right.handle(),
type, false, overwrite_mode);
- } else if (left_is_smi) {
+ } else if (left_is_smi_constant) {
answer = ConstantSmiBinaryOperation(op, &right, left.handle(),
type, true, overwrite_mode);
} else {
@@ -5145,10 +5262,62 @@ void CodeGenerator::GenericBinaryOperation(Token::Value op,
if (loop_nesting() > 0 && (Token::IsBitOp(op) || type->IsLikelySmi())) {
answer = LikelySmiBinaryOperation(op, &left, &right, overwrite_mode);
} else {
- GenericBinaryOpStub stub(op, overwrite_mode, NO_GENERIC_BINARY_FLAGS);
+ GenericBinaryOpStub stub(op,
+ overwrite_mode,
+ NO_GENERIC_BINARY_FLAGS,
+ operands_type);
answer = stub.GenerateCall(masm_, frame_, &left, &right);
}
}
+
+ // Set NumberInfo of result according to the operation performed.
+ // We rely on the fact that smis have a 32 bit payload on x64.
+ ASSERT(kSmiValueSize == 32);
+ NumberInfo::Type result_type = NumberInfo::kUnknown;
+ switch (op) {
+ case Token::COMMA:
+ result_type = right.number_info();
+ break;
+ case Token::OR:
+ case Token::AND:
+ // Result type can be either of the two input types.
+ result_type = operands_type;
+ break;
+ case Token::BIT_OR:
+ case Token::BIT_XOR:
+ case Token::BIT_AND:
+ // Result is always a smi.
+ result_type = NumberInfo::kSmi;
+ break;
+ case Token::SAR:
+ case Token::SHL:
+ // Result is always a smi.
+ result_type = NumberInfo::kSmi;
+ break;
+ case Token::SHR:
+ // Result of x >>> y is always a smi if y >= 1, otherwise a number.
+ result_type = (right.is_constant() && right.handle()->IsSmi()
+ && Smi::cast(*right.handle())->value() >= 1)
+ ? NumberInfo::kSmi
+ : NumberInfo::kNumber;
+ break;
+ case Token::ADD:
+ // Result could be a string or a number. Check types of inputs.
+ result_type = NumberInfo::IsNumber(operands_type)
+ ? NumberInfo::kNumber
+ : NumberInfo::kUnknown;
+ break;
+ case Token::SUB:
+ case Token::MUL:
+ case Token::DIV:
+ case Token::MOD:
+ // Result is always a number.
+ result_type = NumberInfo::kNumber;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ answer.set_number_info(result_type);
frame_->Push(&answer);
}
@@ -6221,6 +6390,63 @@ void FastNewContextStub::Generate(MacroAssembler* masm) {
}
+void FastCloneShallowArrayStub::Generate(MacroAssembler* masm) {
+ // Stack layout on entry:
+ //
+ // [rsp + kPointerSize]: constant elements.
+ // [rsp + (2 * kPointerSize)]: literal index.
+ // [rsp + (3 * kPointerSize)]: literals array.
+
+ // All sizes here are multiples of kPointerSize.
+ int elements_size = (length_ > 0) ? FixedArray::SizeFor(length_) : 0;
+ int size = JSArray::kSize + elements_size;
+
+ // Load boilerplate object into rcx and check if we need to create a
+ // boilerplate.
+ Label slow_case;
+ __ movq(rcx, Operand(rsp, 3 * kPointerSize));
+ __ movq(rax, Operand(rsp, 2 * kPointerSize));
+ SmiIndex index = masm->SmiToIndex(rax, rax, kPointerSizeLog2);
+ __ movq(rcx,
+ FieldOperand(rcx, index.reg, index.scale, FixedArray::kHeaderSize));
+ __ CompareRoot(rcx, Heap::kUndefinedValueRootIndex);
+ __ j(equal, &slow_case);
+
+ // Allocate both the JS array and the elements array in one big
+ // allocation. This avoids multiple limit checks.
+ __ AllocateInNewSpace(size, rax, rbx, rdx, &slow_case, TAG_OBJECT);
+
+ // Copy the JS array part.
+ for (int i = 0; i < JSArray::kSize; i += kPointerSize) {
+ if ((i != JSArray::kElementsOffset) || (length_ == 0)) {
+ __ movq(rbx, FieldOperand(rcx, i));
+ __ movq(FieldOperand(rax, i), rbx);
+ }
+ }
+
+ if (length_ > 0) {
+ // Get hold of the elements array of the boilerplate and setup the
+ // elements pointer in the resulting object.
+ __ movq(rcx, FieldOperand(rcx, JSArray::kElementsOffset));
+ __ lea(rdx, Operand(rax, JSArray::kSize));
+ __ movq(FieldOperand(rax, JSArray::kElementsOffset), rdx);
+
+ // Copy the elements array.
+ for (int i = 0; i < elements_size; i += kPointerSize) {
+ __ movq(rbx, FieldOperand(rcx, i));
+ __ movq(FieldOperand(rdx, i), rbx);
+ }
+ }
+
+ // Return and remove the on-stack parameters.
+ __ ret(3 * kPointerSize);
+
+ __ bind(&slow_case);
+ ExternalReference runtime(Runtime::kCreateArrayLiteralShallow);
+ __ TailCallRuntime(runtime, 3, 1);
+}
+
+
void ToBooleanStub::Generate(MacroAssembler* masm) {
Label false_result, true_result, not_string;
__ movq(rax, Operand(rsp, 1 * kPointerSize));
@@ -7234,30 +7460,107 @@ void InstanceofStub::Generate(MacroAssembler* masm) {
void ArgumentsAccessStub::GenerateNewObject(MacroAssembler* masm) {
+ // rsp[0] : return address
+ // rsp[8] : number of parameters
+ // rsp[16] : receiver displacement
+ // rsp[24] : function
+
// The displacement is used for skipping the return address and the
// frame pointer on the stack. It is the offset of the last
// parameter (if any) relative to the frame pointer.
static const int kDisplacement = 2 * kPointerSize;
// Check if the calling frame is an arguments adaptor frame.
- Label runtime;
+ Label adaptor_frame, try_allocate, runtime;
__ movq(rdx, Operand(rbp, StandardFrameConstants::kCallerFPOffset));
__ SmiCompare(Operand(rdx, StandardFrameConstants::kContextOffset),
Smi::FromInt(StackFrame::ARGUMENTS_ADAPTOR));
- __ j(not_equal, &runtime);
- // Value in rcx is Smi encoded.
+ __ j(equal, &adaptor_frame);
+
+ // Get the length from the frame.
+ __ movq(rcx, Operand(rsp, 1 * kPointerSize));
+ __ jmp(&try_allocate);
// Patch the arguments.length and the parameters pointer.
+ __ bind(&adaptor_frame);
__ movq(rcx, Operand(rdx, ArgumentsAdaptorFrameConstants::kLengthOffset));
__ movq(Operand(rsp, 1 * kPointerSize), rcx);
- SmiIndex index = masm->SmiToIndex(rcx, rcx, kPointerSizeLog2);
+ // Do not clobber the length index for the indexing operation since
+ // it is used compute the size for allocation later.
+ SmiIndex index = masm->SmiToIndex(rbx, rcx, kPointerSizeLog2);
__ lea(rdx, Operand(rdx, index.reg, index.scale, kDisplacement));
__ movq(Operand(rsp, 2 * kPointerSize), rdx);
+ // Try the new space allocation. Start out with computing the size of
+ // the arguments object and the elements array.
+ Label add_arguments_object;
+ __ bind(&try_allocate);
+ __ testq(rcx, rcx);
+ __ j(zero, &add_arguments_object);
+ index = masm->SmiToIndex(rcx, rcx, kPointerSizeLog2);
+ __ lea(rcx, Operand(index.reg, index.scale, FixedArray::kHeaderSize));
+ __ bind(&add_arguments_object);
+ __ addq(rcx, Immediate(Heap::kArgumentsObjectSize));
+
+ // Do the allocation of both objects in one go.
+ __ AllocateInNewSpace(rcx, rax, rdx, rbx, &runtime, TAG_OBJECT);
+
+ // Get the arguments boilerplate from the current (global) context.
+ int offset = Context::SlotOffset(Context::ARGUMENTS_BOILERPLATE_INDEX);
+ __ movq(rdi, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ __ movq(rdi, FieldOperand(rdi, GlobalObject::kGlobalContextOffset));
+ __ movq(rdi, Operand(rdi, offset));
+
+ // Copy the JS object part.
+ for (int i = 0; i < JSObject::kHeaderSize; i += kPointerSize) {
+ __ movq(kScratchRegister, FieldOperand(rdi, i));
+ __ movq(FieldOperand(rax, i), kScratchRegister);
+ }
+
+ // Setup the callee in-object property.
+ ASSERT(Heap::arguments_callee_index == 0);
+ __ movq(kScratchRegister, Operand(rsp, 3 * kPointerSize));
+ __ movq(FieldOperand(rax, JSObject::kHeaderSize), kScratchRegister);
+
+ // Get the length (smi tagged) and set that as an in-object property too.
+ ASSERT(Heap::arguments_length_index == 1);
+ __ movq(rcx, Operand(rsp, 1 * kPointerSize));
+ __ movq(FieldOperand(rax, JSObject::kHeaderSize + kPointerSize), rcx);
+
+ // If there are no actual arguments, we're done.
+ Label done;
+ __ testq(rcx, rcx);
+ __ j(zero, &done);
+
+ // Get the parameters pointer from the stack and untag the length.
+ __ movq(rdx, Operand(rsp, 2 * kPointerSize));
+ __ SmiToInteger32(rcx, rcx);
+
+ // Setup the elements pointer in the allocated arguments object and
+ // initialize the header in the elements fixed array.
+ __ lea(rdi, Operand(rax, Heap::kArgumentsObjectSize));
+ __ movq(FieldOperand(rax, JSObject::kElementsOffset), rdi);
+ __ LoadRoot(kScratchRegister, Heap::kFixedArrayMapRootIndex);
+ __ movq(FieldOperand(rdi, FixedArray::kMapOffset), kScratchRegister);
+ __ movq(FieldOperand(rdi, FixedArray::kLengthOffset), rcx);
+
+ // Copy the fixed array slots.
+ Label loop;
+ __ bind(&loop);
+ __ movq(kScratchRegister, Operand(rdx, -1 * kPointerSize)); // Skip receiver.
+ __ movq(FieldOperand(rdi, FixedArray::kHeaderSize), kScratchRegister);
+ __ addq(rdi, Immediate(kPointerSize));
+ __ subq(rdx, Immediate(kPointerSize));
+ __ decq(rcx);
+ __ j(not_zero, &loop);
+
+ // Return and remove the on-stack parameters.
+ __ bind(&done);
+ __ ret(3 * kPointerSize);
+
// Do the runtime call to allocate the arguments object.
__ bind(&runtime);
- Runtime::Function* f = Runtime::FunctionForId(Runtime::kNewArgumentsFast);
- __ TailCallRuntime(ExternalReference(f), 3, f->result_size);
+ __ TailCallRuntime(ExternalReference(Runtime::kNewArgumentsFast), 3, 1);
}
@@ -7592,6 +7895,9 @@ void CallFunctionStub::Generate(MacroAssembler* masm) {
// Slow-case: Non-function called.
__ bind(&slow);
+ // CALL_NON_FUNCTION expects the non-function callee as receiver (instead
+ // of the original receiver from the call site).
+ __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rdi);
__ Set(rax, argc_);
__ Set(rbx, 0);
__ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
@@ -7980,13 +8286,14 @@ const char* GenericBinaryOpStub::GetName() {
}
OS::SNPrintF(Vector<char>(name_, len),
- "GenericBinaryOpStub_%s_%s%s_%s%s_%s",
+ "GenericBinaryOpStub_%s_%s%s_%s%s_%s%s",
op_name,
overwrite_name,
(flags_ & NO_SMI_CODE_IN_STUB) ? "_NoSmiInStub" : "",
args_in_registers_ ? "RegArgs" : "StackArgs",
args_reversed_ ? "_R" : "",
- use_sse3_ ? "SSE3" : "SSE2");
+ use_sse3_ ? "SSE3" : "SSE2",
+ NumberInfo::ToString(operands_type_));
return name_;
}
@@ -8012,6 +8319,8 @@ void GenericBinaryOpStub::GenerateCall(
}
} else if (left.is(left_arg)) {
__ movq(right_arg, right);
+ } else if (right.is(right_arg)) {
+ __ movq(left_arg, left);
} else if (left.is(right_arg)) {
if (IsOperationCommutative()) {
__ movq(left_arg, right);
@@ -8030,8 +8339,6 @@ void GenericBinaryOpStub::GenerateCall(
__ movq(right_arg, right);
__ movq(left_arg, left);
}
- } else if (right.is(right_arg)) {
- __ movq(left_arg, left);
} else {
// Order of moves is not important.
__ movq(left_arg, left);
@@ -8067,6 +8374,10 @@ void GenericBinaryOpStub::GenerateCall(
__ Move(left_arg, right);
SetArgsReversed();
} else {
+ // For non-commutative operations, left and right_arg might be
+ // the same register. Therefore, the order of the moves is
+ // important here in order to not overwrite left before moving
+ // it to left_arg.
__ movq(left_arg, left);
__ Move(right_arg, right);
}
@@ -8099,8 +8410,12 @@ void GenericBinaryOpStub::GenerateCall(
__ Move(right_arg, left);
SetArgsReversed();
} else {
- __ Move(left_arg, left);
+ // For non-commutative operations, right and left_arg might be
+ // the same register. Therefore, the order of the moves is
+ // important here in order to not overwrite right before moving
+ // it to right_arg.
__ movq(right_arg, right);
+ __ Move(left_arg, left);
}
// Update flags to indicate that arguments are in registers.
SetArgsInRegisters();
@@ -8302,7 +8617,15 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
case Token::DIV: {
// rax: y
// rdx: x
- FloatingPointHelper::CheckNumberOperands(masm, &call_runtime);
+ if (NumberInfo::IsNumber(operands_type_)) {
+ if (FLAG_debug_code) {
+ // Assert at runtime that inputs are only numbers.
+ __ AbortIfNotNumber(rdx, "GenericBinaryOpStub operand not a number.");
+ __ AbortIfNotNumber(rax, "GenericBinaryOpStub operand not a number.");
+ }
+ } else {
+ FloatingPointHelper::CheckNumberOperands(masm, &call_runtime);
+ }
// Fast-case: Both operands are numbers.
// xmm4 and xmm5 are volatile XMM registers.
FloatingPointHelper::LoadFloatOperands(masm, xmm4, xmm5);
diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h
index 8fbbe5a6..4b0c77d2 100644
--- a/src/x64/codegen-x64.h
+++ b/src/x64/codegen-x64.h
@@ -294,15 +294,6 @@ enum ArgumentsAllocationMode {
class CodeGenerator: public AstVisitor {
public:
- // Compilation mode. Either the compiler is used as the primary
- // compiler and needs to setup everything or the compiler is used as
- // the secondary compiler for split compilation and has to handle
- // bailouts.
- enum Mode {
- PRIMARY,
- SECONDARY
- };
-
// Takes a function literal, generates code for it. This function should only
// be called by compiler.cc.
static Handle<Code> MakeCode(CompilationInfo* info);
@@ -385,7 +376,7 @@ class CodeGenerator: public AstVisitor {
void VisitStatementsAndSpill(ZoneList<Statement*>* statements);
// Main code generation function
- void Generate(CompilationInfo* info, Mode mode);
+ void Generate(CompilationInfo* info);
// Generate the return sequence code. Should be called no more than
// once per compiled function, immediately after binding the return
@@ -484,7 +475,8 @@ class CodeGenerator: public AstVisitor {
Result* right,
OverwriteMode overwrite_mode);
- void Comparison(Condition cc,
+ void Comparison(AstNode* node,
+ Condition cc,
bool strict,
ControlDestination* destination);
@@ -535,6 +527,7 @@ class CodeGenerator: public AstVisitor {
void GenerateIsSmi(ZoneList<Expression*>* args);
void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
void GenerateIsArray(ZoneList<Expression*>* args);
+ void GenerateIsRegExp(ZoneList<Expression*>* args);
void GenerateIsObject(ZoneList<Expression*>* args);
void GenerateIsFunction(ZoneList<Expression*>* args);
void GenerateIsUndetectableObject(ZoneList<Expression*>* args);
@@ -576,7 +569,14 @@ class CodeGenerator: public AstVisitor {
// Support for direct calls from JavaScript to native RegExp code.
void GenerateRegExpExec(ZoneList<Expression*>* args);
- // Simple condition analysis.
+ // Fast support for number to string.
+ void GenerateNumberToString(ZoneList<Expression*>* args);
+
+ // Fast call to math functions.
+ void GenerateMathSin(ZoneList<Expression*>* args);
+ void GenerateMathCos(ZoneList<Expression*>* args);
+
+// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
ALWAYS_FALSE,
@@ -654,13 +654,15 @@ class GenericBinaryOpStub: public CodeStub {
public:
GenericBinaryOpStub(Token::Value op,
OverwriteMode mode,
- GenericBinaryFlags flags)
+ GenericBinaryFlags flags,
+ NumberInfo::Type operands_type = NumberInfo::kUnknown)
: op_(op),
mode_(mode),
flags_(flags),
args_in_registers_(false),
args_reversed_(false),
- name_(NULL) {
+ name_(NULL),
+ operands_type_(operands_type) {
use_sse3_ = CpuFeatures::IsSupported(SSE3);
ASSERT(OpBits::is_valid(Token::NUM_TOKENS));
}
@@ -685,28 +687,32 @@ class GenericBinaryOpStub: public CodeStub {
bool args_reversed_; // Left and right argument are swapped.
bool use_sse3_;
char* name_;
+ NumberInfo::Type operands_type_;
const char* GetName();
#ifdef DEBUG
void Print() {
- PrintF("GenericBinaryOpStub (op %s), "
- "(mode %d, flags %d, registers %d, reversed %d)\n",
+ PrintF("GenericBinaryOpStub %d (op %s), "
+ "(mode %d, flags %d, registers %d, reversed %d, only_numbers %s)\n",
+ MinorKey(),
Token::String(op_),
static_cast<int>(mode_),
static_cast<int>(flags_),
static_cast<int>(args_in_registers_),
- static_cast<int>(args_reversed_));
+ static_cast<int>(args_reversed_),
+ NumberInfo::ToString(operands_type_));
}
#endif
- // Minor key encoding in 16 bits FRASOOOOOOOOOOMM.
+ // Minor key encoding in 16 bits NNNFRASOOOOOOOMM.
class ModeBits: public BitField<OverwriteMode, 0, 2> {};
- class OpBits: public BitField<Token::Value, 2, 10> {};
- class SSE3Bits: public BitField<bool, 12, 1> {};
- class ArgsInRegistersBits: public BitField<bool, 13, 1> {};
- class ArgsReversedBits: public BitField<bool, 14, 1> {};
- class FlagBits: public BitField<GenericBinaryFlags, 15, 1> {};
+ class OpBits: public BitField<Token::Value, 2, 7> {};
+ class SSE3Bits: public BitField<bool, 9, 1> {};
+ class ArgsInRegistersBits: public BitField<bool, 10, 1> {};
+ class ArgsReversedBits: public BitField<bool, 11, 1> {};
+ class FlagBits: public BitField<GenericBinaryFlags, 12, 1> {};
+ class NumberInfoBits: public BitField<NumberInfo::Type, 13, 3> {};
Major MajorKey() { return GenericBinaryOp; }
int MinorKey() {
@@ -716,7 +722,8 @@ class GenericBinaryOpStub: public CodeStub {
| FlagBits::encode(flags_)
| SSE3Bits::encode(use_sse3_)
| ArgsInRegistersBits::encode(args_in_registers_)
- | ArgsReversedBits::encode(args_reversed_);
+ | ArgsReversedBits::encode(args_reversed_)
+ | NumberInfoBits::encode(operands_type_);
}
void Generate(MacroAssembler* masm);
diff --git a/src/x64/disasm-x64.cc b/src/x64/disasm-x64.cc
index ce3aae8a..547daeeb 100644
--- a/src/x64/disasm-x64.cc
+++ b/src/x64/disasm-x64.cc
@@ -993,7 +993,60 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
byte* current = data + 2;
// At return, "current" points to the start of the next instruction.
const char* mnemonic = TwoByteMnemonic(opcode);
- if (opcode == 0x1F) {
+ if (operand_size_ == 0x66) {
+ // 0x66 0x0F prefix.
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ const char* mnemonic = "?";
+ if (opcode == 0x57) {
+ mnemonic = "xorpd";
+ } else if (opcode == 0x2E) {
+ mnemonic = "comisd";
+ } else if (opcode == 0x2F) {
+ mnemonic = "ucomisd";
+ } else {
+ UnimplementedInstruction();
+ }
+ AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+ } else if (group_1_prefix_ == 0xF2) {
+ // Beginning of instructions with prefix 0xF2.
+
+ if (opcode == 0x11 || opcode == 0x10) {
+ // MOVSD: Move scalar double-precision fp to/from/between XMM registers.
+ AppendToBuffer("movsd ");
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ if (opcode == 0x11) {
+ current += PrintRightOperand(current);
+ AppendToBuffer(",%s", NameOfXMMRegister(regop));
+ } else {
+ AppendToBuffer("%s,", NameOfXMMRegister(regop));
+ current += PrintRightOperand(current);
+ }
+ } else if (opcode == 0x2A) {
+ // CVTSI2SD: integer to XMM double conversion.
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
+ current += PrintRightOperand(current);
+ } else if ((opcode & 0xF8) == 0x58) {
+ // XMM arithmetic. Mnemonic was retrieved at the start of this function.
+ int mod, regop, rm;
+ get_modrm(*current, &mod, &regop, &rm);
+ AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
+ current += PrintRightXMMOperand(current);
+ } else {
+ UnimplementedInstruction();
+ }
+ } else if (opcode == 0x2C && group_1_prefix_ == 0xF3) {
+ // Instruction with prefix 0xF3.
+
+ // CVTTSS2SI: Convert scalar single-precision FP to dword integer.
+ // Assert that mod is not 3, so source is memory, not an XMM register.
+ ASSERT_NE(0xC0, *current & 0xC0);
+ current += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, current);
+ } else if (opcode == 0x1F) {
// NOP
int mod, regop, rm;
get_modrm(*current, &mod, &regop, &rm);
@@ -1007,8 +1060,7 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
current += 4;
} // else no immediate displacement.
AppendToBuffer("nop");
-
- } else if (opcode == 0xA2 || opcode == 0x31) {
+ } else if (opcode == 0xA2 || opcode == 0x31) {
// RDTSC or CPUID
AppendToBuffer("%s", mnemonic);
@@ -1043,43 +1095,6 @@ int DisassemblerX64::TwoByteOpcodeInstruction(byte* data) {
} else {
AppendToBuffer(",%s,cl", NameOfCPURegister(regop));
}
- } else if (group_1_prefix_ == 0xF2) {
- // Beginning of instructions with prefix 0xF2.
-
- if (opcode == 0x11 || opcode == 0x10) {
- // MOVSD: Move scalar double-precision fp to/from/between XMM registers.
- AppendToBuffer("movsd ");
- int mod, regop, rm;
- get_modrm(*current, &mod, &regop, &rm);
- if (opcode == 0x11) {
- current += PrintRightOperand(current);
- AppendToBuffer(",%s", NameOfXMMRegister(regop));
- } else {
- AppendToBuffer("%s,", NameOfXMMRegister(regop));
- current += PrintRightOperand(current);
- }
- } else if (opcode == 0x2A) {
- // CVTSI2SD: integer to XMM double conversion.
- int mod, regop, rm;
- get_modrm(*current, &mod, &regop, &rm);
- AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
- current += PrintRightOperand(current);
- } else if ((opcode & 0xF8) == 0x58) {
- // XMM arithmetic. Mnemonic was retrieved at the start of this function.
- int mod, regop, rm;
- get_modrm(*current, &mod, &regop, &rm);
- AppendToBuffer("%s %s,", mnemonic, NameOfXMMRegister(regop));
- current += PrintRightXMMOperand(current);
- } else {
- UnimplementedInstruction();
- }
- } else if (opcode == 0x2C && group_1_prefix_ == 0xF3) {
- // Instruction with prefix 0xF3.
-
- // CVTTSS2SI: Convert scalar single-precision FP to dword integer.
- // Assert that mod is not 3, so source is memory, not an XMM register.
- ASSERT_NE(0xC0, *current & 0xC0);
- current += PrintOperands("cvttss2si", REG_OPER_OP_ORDER, current);
} else {
UnimplementedInstruction();
}
diff --git a/src/x64/fast-codegen-x64.cc b/src/x64/fast-codegen-x64.cc
index c6e7be20..4dbf26a4 100644
--- a/src/x64/fast-codegen-x64.cc
+++ b/src/x64/fast-codegen-x64.cc
@@ -35,54 +35,91 @@ namespace internal {
#define __ ACCESS_MASM(masm())
-void FastCodeGenerator::EmitLoadReceiver(Register reg) {
+Register FastCodeGenerator::accumulator0() { return rax; }
+Register FastCodeGenerator::accumulator1() { return rdx; }
+Register FastCodeGenerator::scratch0() { return rcx; }
+Register FastCodeGenerator::scratch1() { return rdi; }
+Register FastCodeGenerator::receiver_reg() { return rbx; }
+Register FastCodeGenerator::context_reg() { return rsi; }
+
+
+void FastCodeGenerator::EmitLoadReceiver() {
// Offset 2 is due to return address and saved frame pointer.
int index = 2 + scope()->num_parameters();
- __ movq(reg, Operand(rbp, index * kPointerSize));
+ __ movq(receiver_reg(), Operand(rbp, index * kPointerSize));
}
-void FastCodeGenerator::EmitReceiverMapCheck() {
- Comment cmnt(masm(), ";; MapCheck(this)");
- if (FLAG_print_ir) {
- PrintF("MapCheck(this)\n");
- }
+void FastCodeGenerator::EmitGlobalVariableLoad(Handle<Object> cell) {
+ ASSERT(!destination().is(no_reg));
+ ASSERT(cell->IsJSGlobalPropertyCell());
- ASSERT(info()->has_receiver() && info()->receiver()->IsHeapObject());
- Handle<HeapObject> object = Handle<HeapObject>::cast(info()->receiver());
- Handle<Map> map(object->map());
+ __ Move(destination(), cell);
+ __ movq(destination(),
+ FieldOperand(destination(), JSGlobalPropertyCell::kValueOffset));
+ if (FLAG_debug_code) {
+ __ Cmp(destination(), Factory::the_hole_value());
+ __ Check(not_equal, "DontDelete cells can't contain the hole");
+ }
- EmitLoadReceiver(rdx);
- __ CheckMap(rdx, map, bailout(), false);
+ // The loaded value is not known to be a smi.
+ clear_as_smi(destination());
}
-void FastCodeGenerator::EmitGlobalMapCheck() {
- Comment cmnt(masm(), ";; GlobalMapCheck");
- if (FLAG_print_ir) {
- PrintF(";; GlobalMapCheck()");
- }
+void FastCodeGenerator::EmitThisPropertyStore(Handle<String> name) {
+ LookupResult lookup;
+ info()->receiver()->Lookup(*name, &lookup);
- ASSERT(info()->has_global_object());
- Handle<Map> map(info()->global_object()->map());
+ ASSERT(lookup.holder() == *info()->receiver());
+ ASSERT(lookup.type() == FIELD);
+ Handle<Map> map(Handle<HeapObject>::cast(info()->receiver())->map());
+ int index = lookup.GetFieldIndex() - map->inobject_properties();
+ int offset = index * kPointerSize;
- __ movq(rbx, CodeGenerator::GlobalObject());
- __ CheckMap(rbx, map, bailout(), true);
-}
+ // We will emit the write barrier unless the stored value is statically
+ // known to be a smi.
+ bool needs_write_barrier = !is_smi(accumulator0());
+ // Perform the store. Negative offsets are inobject properties.
+ if (offset < 0) {
+ offset += map->instance_size();
+ __ movq(FieldOperand(receiver_reg(), offset), accumulator0());
+ if (needs_write_barrier) {
+ // Preserve receiver from write barrier.
+ __ movq(scratch0(), receiver_reg());
+ }
+ } else {
+ offset += FixedArray::kHeaderSize;
+ __ movq(scratch0(),
+ FieldOperand(receiver_reg(), JSObject::kPropertiesOffset));
+ __ movq(FieldOperand(scratch0(), offset), accumulator0());
+ }
-void FastCodeGenerator::EmitGlobalVariableLoad(Handle<Object> cell) {
- ASSERT(cell->IsJSGlobalPropertyCell());
- __ Move(rax, cell);
- __ movq(rax, FieldOperand(rax, JSGlobalPropertyCell::kValueOffset));
- if (FLAG_debug_code) {
- __ Cmp(rax, Factory::the_hole_value());
- __ Check(not_equal, "DontDelete cells can't contain the hole");
+ if (needs_write_barrier) {
+ if (destination().is(no_reg)) {
+ // After RecordWrite accumulator0 is only accidently a smi, but it is
+ // already marked as not known to be one.
+ __ RecordWrite(scratch0(), offset, accumulator0(), scratch1());
+ } else {
+ // Copy the value to the other accumulator to preserve a copy from the
+ // write barrier. One of the accumulators is available as a scratch
+ // register. Neither is a smi.
+ __ movq(accumulator1(), accumulator0());
+ clear_as_smi(accumulator1());
+ Register value_scratch = other_accumulator(destination());
+ __ RecordWrite(scratch0(), offset, value_scratch, scratch1());
+ }
+ } else if (destination().is(accumulator1())) {
+ __ movq(accumulator1(), accumulator0());
+ // Is a smi because we do not need the write barrier.
+ set_as_smi(accumulator1());
}
}
-void FastCodeGenerator::EmitThisPropertyStore(Handle<String> name) {
+void FastCodeGenerator::EmitThisPropertyLoad(Handle<String> name) {
+ ASSERT(!destination().is(no_reg));
LookupResult lookup;
info()->receiver()->Lookup(*name, &lookup);
@@ -92,19 +129,59 @@ void FastCodeGenerator::EmitThisPropertyStore(Handle<String> name) {
int index = lookup.GetFieldIndex() - map->inobject_properties();
int offset = index * kPointerSize;
- // Negative offsets are inobject properties.
+ // Perform the load. Negative offsets are inobject properties.
if (offset < 0) {
offset += map->instance_size();
- __ movq(rcx, rdx); // Copy receiver for write barrier.
+ __ movq(destination(), FieldOperand(receiver_reg(), offset));
} else {
offset += FixedArray::kHeaderSize;
- __ movq(rcx, FieldOperand(rdx, JSObject::kPropertiesOffset));
+ __ movq(scratch0(),
+ FieldOperand(receiver_reg(), JSObject::kPropertiesOffset));
+ __ movq(destination(), FieldOperand(scratch0(), offset));
}
- // Perform the store.
- __ movq(FieldOperand(rcx, offset), rax);
- // Preserve value from write barrier in case it's needed.
- __ movq(rbx, rax);
- __ RecordWrite(rcx, offset, rbx, rdi);
+
+ // The loaded value is not known to be a smi.
+ clear_as_smi(destination());
+}
+
+
+void FastCodeGenerator::EmitBitOr() {
+ if (is_smi(accumulator0()) && is_smi(accumulator1())) {
+ // If both operands are known to be a smi then there is no need to check
+ // the operands or result.
+ if (destination().is(no_reg)) {
+ __ or_(accumulator1(), accumulator0());
+ } else {
+ // Leave the result in the destination register. Bitwise or is
+ // commutative.
+ __ or_(destination(), other_accumulator(destination()));
+ }
+ } else {
+ // Left is in accumulator1, right in accumulator0.
+ if (destination().is(accumulator0())) {
+ __ movq(scratch0(), accumulator0());
+ __ or_(destination(), accumulator1()); // Or is commutative.
+ Label* bailout =
+ info()->AddBailout(accumulator1(), scratch0()); // Left, right.
+ __ JumpIfNotSmi(destination(), bailout);
+ } else if (destination().is(accumulator1())) {
+ __ movq(scratch0(), accumulator1());
+ __ or_(destination(), accumulator0());
+ Label* bailout = info()->AddBailout(scratch0(), accumulator0());
+ __ JumpIfNotSmi(destination(), bailout);
+ } else {
+ ASSERT(destination().is(no_reg));
+ __ movq(scratch0(), accumulator1());
+ __ or_(scratch0(), accumulator0());
+ Label* bailout = info()->AddBailout(accumulator1(), accumulator0());
+ __ JumpIfNotSmi(scratch0(), bailout);
+ }
+ }
+
+ // If we didn't bailout, the result (in fact, both inputs too) is known to
+ // be a smi.
+ set_as_smi(accumulator0());
+ set_as_smi(accumulator1());
}
@@ -121,24 +198,43 @@ void FastCodeGenerator::Generate(CompilationInfo* compilation_info) {
// Note that we keep a live register reference to esi (context) at this
// point.
- // Receiver (this) is allocated to rdx if there are this properties.
- if (info()->has_this_properties()) EmitReceiverMapCheck();
+ Label* bailout_to_beginning = info()->AddBailout();
+ // Receiver (this) is allocated to a fixed register.
+ if (info()->has_this_properties()) {
+ Comment cmnt(masm(), ";; MapCheck(this)");
+ if (FLAG_print_ir) {
+ PrintF("MapCheck(this)\n");
+ }
+ ASSERT(info()->has_receiver() && info()->receiver()->IsHeapObject());
+ Handle<HeapObject> object = Handle<HeapObject>::cast(info()->receiver());
+ Handle<Map> map(object->map());
+ EmitLoadReceiver();
+ __ CheckMap(receiver_reg(), map, bailout_to_beginning, false);
+ }
- // If there is a global variable access check if the global object
- // is the same as at lazy-compilation time.
- if (info()->has_globals()) EmitGlobalMapCheck();
+ // If there is a global variable access check if the global object is the
+ // same as at lazy-compilation time.
+ if (info()->has_globals()) {
+ Comment cmnt(masm(), ";; MapCheck(GLOBAL)");
+ if (FLAG_print_ir) {
+ PrintF("MapCheck(GLOBAL)\n");
+ }
+ ASSERT(info()->has_global_object());
+ Handle<Map> map(info()->global_object()->map());
+ __ movq(scratch0(), CodeGenerator::GlobalObject());
+ __ CheckMap(scratch0(), map, bailout_to_beginning, true);
+ }
VisitStatements(info()->function()->body());
Comment return_cmnt(masm(), ";; Return(<undefined>)");
+ if (FLAG_print_ir) {
+ PrintF("Return(<undefined>)\n");
+ }
__ LoadRoot(rax, Heap::kUndefinedValueRootIndex);
-
- Comment epilogue_cmnt(masm(), ";; Epilogue");
__ movq(rsp, rbp);
__ pop(rbp);
__ ret((scope()->num_parameters() + 1) * kPointerSize);
-
- __ bind(&bailout_);
}
diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc
index 2e95c688..30db660d 100644
--- a/src/x64/full-codegen-x64.cc
+++ b/src/x64/full-codegen-x64.cc
@@ -1015,6 +1015,92 @@ void FullCodeGenerator::VisitArrayLiteral(ArrayLiteral* expr) {
}
+void FullCodeGenerator::VisitAssignment(Assignment* expr) {
+ Comment cmnt(masm_, "[ Assignment");
+ ASSERT(expr->op() != Token::INIT_CONST);
+ // Left-hand side can only be a property, a global or a (parameter or local)
+ // slot. Variables with rewrite to .arguments are treated as KEYED_PROPERTY.
+ enum LhsKind { VARIABLE, NAMED_PROPERTY, KEYED_PROPERTY };
+ LhsKind assign_type = VARIABLE;
+ Property* prop = expr->target()->AsProperty();
+ if (prop != NULL) {
+ assign_type =
+ (prop->key()->IsPropertyName()) ? NAMED_PROPERTY : KEYED_PROPERTY;
+ }
+
+ // Evaluate LHS expression.
+ switch (assign_type) {
+ case VARIABLE:
+ // Nothing to do here.
+ break;
+ case NAMED_PROPERTY:
+ if (expr->is_compound()) {
+ // We need the receiver both on the stack and in the accumulator.
+ VisitForValue(prop->obj(), kAccumulator);
+ __ push(result_register());
+ } else {
+ VisitForValue(prop->obj(), kStack);
+ }
+ break;
+ case KEYED_PROPERTY:
+ VisitForValue(prop->obj(), kStack);
+ VisitForValue(prop->key(), kStack);
+ break;
+ }
+
+ // If we have a compound assignment: Get value of LHS expression and
+ // store in on top of the stack.
+ if (expr->is_compound()) {
+ Location saved_location = location_;
+ location_ = kStack;
+ switch (assign_type) {
+ case VARIABLE:
+ EmitVariableLoad(expr->target()->AsVariableProxy()->var(),
+ Expression::kValue);
+ break;
+ case NAMED_PROPERTY:
+ EmitNamedPropertyLoad(prop);
+ __ push(result_register());
+ break;
+ case KEYED_PROPERTY:
+ EmitKeyedPropertyLoad(prop);
+ __ push(result_register());
+ break;
+ }
+ location_ = saved_location;
+ }
+
+ // Evaluate RHS expression.
+ Expression* rhs = expr->value();
+ VisitForValue(rhs, kAccumulator);
+
+ // If we have a compound assignment: Apply operator.
+ if (expr->is_compound()) {
+ Location saved_location = location_;
+ location_ = kAccumulator;
+ EmitBinaryOp(expr->binary_op(), Expression::kValue);
+ location_ = saved_location;
+ }
+
+ // Record source position before possible IC call.
+ SetSourcePosition(expr->position());
+
+ // Store the value.
+ switch (assign_type) {
+ case VARIABLE:
+ EmitVariableAssignment(expr->target()->AsVariableProxy()->var(),
+ context_);
+ break;
+ case NAMED_PROPERTY:
+ EmitNamedPropertyAssignment(expr);
+ break;
+ case KEYED_PROPERTY:
+ EmitKeyedPropertyAssignment(expr);
+ break;
+ }
+}
+
+
void FullCodeGenerator::EmitNamedPropertyLoad(Property* prop) {
SetSourcePosition(prop->position());
Literal* key = prop->key()->AsLiteral();
@@ -1200,7 +1286,7 @@ void FullCodeGenerator::VisitProperty(Property* expr) {
void FullCodeGenerator::EmitCallWithIC(Call* expr,
- Handle<Object> ignored,
+ Handle<Object> name,
RelocInfo::Mode mode) {
// Code common for calls using the IC.
ZoneList<Expression*>* args = expr->arguments();
@@ -1208,6 +1294,7 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
for (int i = 0; i < arg_count; i++) {
VisitForValue(args->at(i), kStack);
}
+ __ Move(rcx, name);
// Record source position for debugger.
SetSourcePosition(expr->position());
// Call the IC initialization code.
@@ -1217,8 +1304,7 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr,
__ Call(ic, mode);
// Restore context register.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
- // Discard the function left on TOS.
- DropAndApply(1, context_, rax);
+ Apply(context_, rax);
}
@@ -1250,7 +1336,6 @@ void FullCodeGenerator::VisitCall(Call* expr) {
UNREACHABLE();
} else if (var != NULL && !var->is_this() && var->is_global()) {
// Call to a global variable.
- __ Push(var->name());
// Push global object as receiver for the call IC lookup.
__ push(CodeGenerator::GlobalObject());
EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT);
@@ -1264,7 +1349,6 @@ void FullCodeGenerator::VisitCall(Call* expr) {
Literal* key = prop->key()->AsLiteral();
if (key != NULL && key->handle()->IsSymbol()) {
// Call to a named property, use call IC.
- __ Push(key->handle());
VisitForValue(prop->obj(), kStack);
EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET);
} else {
@@ -1355,7 +1439,6 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
if (expr->is_jsruntime()) {
// Prepare for calling JS runtime function.
- __ Push(expr->name());
__ movq(rax, CodeGenerator::GlobalObject());
__ push(FieldOperand(rax, GlobalObject::kBuiltinsOffset));
}
@@ -1367,18 +1450,17 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) {
}
if (expr->is_jsruntime()) {
- // Call the JS runtime function.
- Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count,
- NOT_IN_LOOP);
+ // Call the JS runtime function using a call IC.
+ __ Move(rcx, expr->name());
+ InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
+ Handle<Code> ic = CodeGenerator::ComputeCallInitialize(arg_count, in_loop);
__ call(ic, RelocInfo::CODE_TARGET);
// Restore context register.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
- // Discard the function left on TOS.
- DropAndApply(1, context_, rax);
} else {
__ CallRuntime(expr->function(), arg_count);
- Apply(context_, rax);
}
+ Apply(context_, rax);
}
diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc
index 99a8c7d4..8d43332e 100644
--- a/src/x64/ic-x64.cc
+++ b/src/x64/ic-x64.cc
@@ -212,23 +212,37 @@ void KeyedStoreIC::RestoreInlinedVersion(Address address) {
}
-void KeyedLoadIC::Generate(MacroAssembler* masm,
- ExternalReference const& f) {
+void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rsp[0] : return address
// -- rsp[8] : name
// -- rsp[16] : receiver
// -----------------------------------
- __ movq(rax, Operand(rsp, kPointerSize));
- __ movq(rcx, Operand(rsp, 2 * kPointerSize));
__ pop(rbx);
- __ push(rcx); // receiver
- __ push(rax); // name
+ __ push(Operand(rsp, 1 * kPointerSize)); // receiver
+ __ push(Operand(rsp, 1 * kPointerSize)); // name
__ push(rbx); // return address
// Perform tail call to the entry.
- __ TailCallRuntime(f, 2, 1);
+ __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedLoadIC_Miss)), 2, 1);
+}
+
+
+void KeyedLoadIC::GenerateRuntimeGetProperty(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rsp[0] : return address
+ // -- rsp[8] : name
+ // -- rsp[16] : receiver
+ // -----------------------------------
+
+ __ pop(rbx);
+ __ push(Operand(rsp, 1 * kPointerSize)); // receiver
+ __ push(Operand(rsp, 1 * kPointerSize)); // name
+ __ push(rbx); // return address
+
+ // Perform tail call to the entry.
+ __ TailCallRuntime(ExternalReference(Runtime::kKeyedGetProperty), 2, 1);
}
@@ -301,7 +315,7 @@ void KeyedLoadIC::GenerateGeneric(MacroAssembler* masm) {
// Slow case: Load name and receiver from stack and jump to runtime.
__ bind(&slow);
__ IncrementCounter(&Counters::keyed_load_generic_slow, 1);
- Generate(masm, ExternalReference(Runtime::kKeyedGetProperty));
+ GenerateRuntimeGetProperty(masm);
__ bind(&check_string);
// The key is not a smi.
// Is it a string?
@@ -538,21 +552,54 @@ void KeyedLoadIC::GenerateExternalArray(MacroAssembler* masm,
// Slow case: Load name and receiver from stack and jump to runtime.
__ bind(&slow);
__ IncrementCounter(&Counters::keyed_load_external_array_slow, 1);
- Generate(masm, ExternalReference(Runtime::kKeyedGetProperty));
+ GenerateRuntimeGetProperty(masm);
}
-void KeyedLoadIC::GenerateMiss(MacroAssembler* masm) {
+void KeyedLoadIC::GenerateIndexedInterceptor(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rsp[0] : return address
- // -- rsp[8] : name
+ // -- rsp[8] : key
// -- rsp[16] : receiver
// -----------------------------------
- Generate(masm, ExternalReference(IC_Utility(kKeyedLoadIC_Miss)));
+ Label slow;
+
+ // Load key and receiver.
+ __ movq(rax, Operand(rsp, kPointerSize));
+ __ movq(rcx, Operand(rsp, 2 * kPointerSize));
+
+ // Check that the receiver isn't a smi.
+ __ JumpIfSmi(rcx, &slow);
+
+ // Check that the key is a smi.
+ __ JumpIfNotSmi(rax, &slow);
+
+ // Get the map of the receiver.
+ __ movq(rdx, FieldOperand(rcx, HeapObject::kMapOffset));
+
+ // Check that it has indexed interceptor and access checks
+ // are not enabled for this object.
+ __ movb(rdx, FieldOperand(rdx, Map::kBitFieldOffset));
+ __ andb(rdx, Immediate(kSlowCaseBitFieldMask));
+ __ cmpb(rdx, Immediate(1 << Map::kHasIndexedInterceptor));
+ __ j(not_zero, &slow);
+
+ // Everything is fine, call runtime.
+ __ pop(rdx);
+ __ push(rcx); // receiver
+ __ push(rax); // key
+ __ push(rdx); // return address
+
+ // Perform tail call to the entry.
+ __ TailCallRuntime(ExternalReference(
+ IC_Utility(kKeyedLoadPropertyWithInterceptor)), 2, 1);
+
+ __ bind(&slow);
+ GenerateMiss(masm);
}
-void KeyedStoreIC::Generate(MacroAssembler* masm, ExternalReference const& f) {
+void KeyedStoreIC::GenerateMiss(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : value
// -- rsp[0] : return address
@@ -567,28 +614,26 @@ void KeyedStoreIC::Generate(MacroAssembler* masm, ExternalReference const& f) {
__ push(rcx); // return address
// Do tail-call to runtime routine.
- __ TailCallRuntime(f, 3, 1);
+ __ TailCallRuntime(ExternalReference(IC_Utility(kKeyedStoreIC_Miss)), 3, 1);
}
-void KeyedStoreIC::GenerateExtendStorage(MacroAssembler* masm) {
+void KeyedStoreIC::GenerateRuntimeSetProperty(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : value
- // -- rcx : transition map
// -- rsp[0] : return address
// -- rsp[8] : key
// -- rsp[16] : receiver
// -----------------------------------
- __ pop(rbx);
+ __ pop(rcx);
__ push(Operand(rsp, 1 * kPointerSize)); // receiver
- __ push(rcx); // transition map
+ __ push(Operand(rsp, 1 * kPointerSize)); // key
__ push(rax); // value
- __ push(rbx); // return address
+ __ push(rcx); // return address
// Do tail-call to runtime routine.
- __ TailCallRuntime(
- ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3, 1);
+ __ TailCallRuntime(ExternalReference(Runtime::kSetProperty), 3, 1);
}
@@ -642,7 +687,7 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm) {
// Slow case: call runtime.
__ bind(&slow);
- Generate(masm, ExternalReference(Runtime::kSetProperty));
+ GenerateRuntimeSetProperty(masm);
// Check whether the elements is a pixel array.
// rax: value
@@ -906,23 +951,29 @@ void KeyedStoreIC::GenerateExternalArray(MacroAssembler* masm,
// Slow case: call runtime.
__ bind(&slow);
- Generate(masm, ExternalReference(Runtime::kSetProperty));
+ GenerateRuntimeSetProperty(masm);
}
void CallIC::GenerateMiss(MacroAssembler* masm, int argc) {
+ // ----------- S t a t e -------------
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
+ // ...
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // -----------------------------------
// Get the receiver of the function from the stack; 1 ~ return address.
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
- // Get the name of the function to call from the stack.
- // 2 ~ receiver, return address.
- __ movq(rbx, Operand(rsp, (argc + 2) * kPointerSize));
// Enter an internal frame.
__ EnterInternalFrame();
// Push the receiver and the name of the function.
__ push(rdx);
- __ push(rbx);
+ __ push(rcx);
// Call the entry.
CEntryStub stub(1);
@@ -960,20 +1011,18 @@ Object* CallIC_Miss(Arguments args);
void CallIC::GenerateMegamorphic(MacroAssembler* masm, int argc) {
// ----------- S t a t e -------------
- // rsp[0] return address
- // rsp[8] argument argc
- // rsp[16] argument argc - 1
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
// ...
- // rsp[argc * 8] argument 1
- // rsp[(argc + 1) * 8] argument 0 = receiver
- // rsp[(argc + 2) * 8] function name
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
Label number, non_number, non_string, boolean, probe, miss;
// Get the receiver of the function from the stack; 1 ~ return address.
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
- // Get the name of the function from the stack; 2 ~ return address, receiver
- __ movq(rcx, Operand(rsp, (argc + 2) * kPointerSize));
// Probe the stub cache.
Code::Flags flags =
@@ -1026,6 +1075,16 @@ static void GenerateNormalHelper(MacroAssembler* masm,
int argc,
bool is_global_object,
Label* miss) {
+ // ----------- S t a t e -------------
+ // rcx : function name
+ // rdx : receiver
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
+ // ...
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // -----------------------------------
// Search dictionary - put result in register rdx.
GenerateDictionaryLoad(masm, miss, rax, rdx, rbx, rcx, CHECK_DICTIONARY);
@@ -1052,20 +1111,18 @@ static void GenerateNormalHelper(MacroAssembler* masm,
void CallIC::GenerateNormal(MacroAssembler* masm, int argc) {
// ----------- S t a t e -------------
- // rsp[0] return address
- // rsp[8] argument argc
- // rsp[16] argument argc - 1
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
// ...
- // rsp[argc * 8] argument 1
- // rsp[(argc + 1) * 8] argument 0 = receiver
- // rsp[(argc + 2) * 8] function name
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
Label miss, global_object, non_global_object;
// Get the receiver of the function from the stack.
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
- // Get the name of the function from the stack.
- __ movq(rcx, Operand(rsp, (argc + 2) * kPointerSize));
// Check that the receiver isn't a smi.
__ JumpIfSmi(rdx, &miss);
@@ -1132,22 +1189,20 @@ void LoadIC::ClearInlinedVersion(Address address) {
}
-void LoadIC::Generate(MacroAssembler* masm, ExternalReference const& f) {
+void LoadIC::GenerateMiss(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rcx : name
// -- rsp[0] : return address
// -- rsp[8] : receiver
// -----------------------------------
- __ movq(rax, Operand(rsp, kPointerSize));
-
__ pop(rbx);
- __ push(rax); // receiver
+ __ push(Operand(rsp, 0)); // receiver
__ push(rcx); // name
__ push(rbx); // return address
// Perform tail call to the entry.
- __ TailCallRuntime(f, 2, 1);
+ __ TailCallRuntime(ExternalReference(IC_Utility(kLoadIC_Miss)), 2, 1);
}
@@ -1203,17 +1258,6 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) {
}
-void LoadIC::GenerateMiss(MacroAssembler* masm) {
- // ----------- S t a t e -------------
- // -- rcx : name
- // -- rsp[0] : return address
- // -- rsp[8] : receiver
- // -----------------------------------
-
- Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss)));
-}
-
-
void LoadIC::GenerateNormal(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rcx : name
@@ -1256,7 +1300,7 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
// Cache miss: Restore receiver from stack and jump to runtime.
__ bind(&miss);
__ movq(rax, Operand(rsp, 1 * kPointerSize));
- Generate(masm, ExternalReference(IC_Utility(kLoadIC_Miss)));
+ GenerateMiss(masm);
}
@@ -1270,13 +1314,12 @@ void LoadIC::GenerateStringLength(MacroAssembler* masm) {
__ movq(rax, Operand(rsp, kPointerSize));
- StubCompiler::GenerateLoadStringLength(masm, rax, rdx, &miss);
+ StubCompiler::GenerateLoadStringLength(masm, rax, rdx, rbx, &miss);
__ bind(&miss);
StubCompiler::GenerateLoadMiss(masm, Code::LOAD_IC);
}
-
bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
// The address of the instruction following the call.
Address test_instruction_address =
@@ -1304,6 +1347,7 @@ bool LoadIC::PatchInlinedLoad(Address address, Object* map, int offset) {
return true;
}
+
void StoreIC::GenerateMiss(MacroAssembler* masm) {
// ----------- S t a t e -------------
// -- rax : value
@@ -1322,24 +1366,6 @@ void StoreIC::GenerateMiss(MacroAssembler* masm) {
__ TailCallRuntime(ExternalReference(IC_Utility(kStoreIC_Miss)), 3, 1);
}
-void StoreIC::GenerateExtendStorage(MacroAssembler* masm) {
- // ----------- S t a t e -------------
- // -- rax : value
- // -- rcx : Map (target of map transition)
- // -- rdx : receiver
- // -- rsp[0] : return address
- // -----------------------------------
-
- __ pop(rbx);
- __ push(rdx); // receiver
- __ push(rcx); // transition map
- __ push(rax); // value
- __ push(rbx); // return address
-
- // Perform tail call to the entry.
- __ TailCallRuntime(
- ExternalReference(IC_Utility(kSharedStoreIC_ExtendStorage)), 3, 1);
-}
void StoreIC::GenerateMegamorphic(MacroAssembler* masm) {
// ----------- S t a t e -------------
diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc
index 56bbc202..90a9c75d 100644
--- a/src/x64/macro-assembler-x64.cc
+++ b/src/x64/macro-assembler-x64.cc
@@ -39,7 +39,6 @@ namespace internal {
MacroAssembler::MacroAssembler(void* buffer, int size)
: Assembler(buffer, size),
- unresolved_(0),
generating_stub_(false),
allow_stub_calls_(true),
code_object_(Heap::undefined_value()) {
@@ -387,6 +386,16 @@ void MacroAssembler::CallRuntime(Runtime::Function* f, int num_arguments) {
}
+void MacroAssembler::CallExternalReference(const ExternalReference& ext,
+ int num_arguments) {
+ movq(rax, Immediate(num_arguments));
+ movq(rbx, ext);
+
+ CEntryStub stub(1);
+ CallStub(&stub);
+}
+
+
void MacroAssembler::TailCallRuntime(ExternalReference const& ext,
int num_arguments,
int result_size) {
@@ -415,38 +424,30 @@ void MacroAssembler::JumpToRuntime(const ExternalReference& ext,
}
-void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) {
- bool resolved;
- Handle<Code> code = ResolveBuiltin(id, &resolved);
-
- const char* name = Builtins::GetName(id);
- int argc = Builtins::GetArgumentsCount(id);
+void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) {
+ // Calls are not allowed in some stubs.
+ ASSERT(flag == JUMP_FUNCTION || allow_stub_calls());
- movq(target, code, RelocInfo::EMBEDDED_OBJECT);
- if (!resolved) {
- uint32_t flags =
- Bootstrapper::FixupFlagsArgumentsCount::encode(argc) |
- Bootstrapper::FixupFlagsUseCodeObject::encode(true);
- Unresolved entry = { pc_offset() - sizeof(intptr_t), flags, name };
- unresolved_.Add(entry);
- }
- addq(target, Immediate(Code::kHeaderSize - kHeapObjectTag));
+ // Rely on the assertion to check that the number of provided
+ // arguments match the expected number of arguments. Fake a
+ // parameter count to avoid emitting code to do the check.
+ ParameterCount expected(0);
+ GetBuiltinEntry(rdx, id);
+ InvokeCode(rdx, expected, expected, flag);
}
-Handle<Code> MacroAssembler::ResolveBuiltin(Builtins::JavaScript id,
- bool* resolved) {
- // Move the builtin function into the temporary function slot by
- // reading it from the builtins object. NOTE: We should be able to
- // reduce this to two instructions by putting the function table in
- // the global object instead of the "builtins" object and by using a
- // real register for the function.
- movq(rdx, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
- movq(rdx, FieldOperand(rdx, GlobalObject::kBuiltinsOffset));
+
+void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) {
+ // Load the JavaScript builtin function from the builtins object.
+ movq(rdi, Operand(rsi, Context::SlotOffset(Context::GLOBAL_INDEX)));
+ movq(rdi, FieldOperand(rdi, GlobalObject::kBuiltinsOffset));
int builtins_offset =
JSBuiltinsObject::kJSBuiltinsOffset + (id * kPointerSize);
- movq(rdi, FieldOperand(rdx, builtins_offset));
-
- return Builtins::GetCode(id, resolved);
+ movq(rdi, FieldOperand(rdi, builtins_offset));
+ // Load the code entry point from the function into the target register.
+ movq(target, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
+ movq(target, FieldOperand(target, SharedFunctionInfo::kCodeOffset));
+ addq(target, Immediate(Code::kHeaderSize - kHeapObjectTag));
}
@@ -1597,6 +1598,17 @@ void MacroAssembler::CheckMap(Register obj,
}
+void MacroAssembler::AbortIfNotNumber(Register object, const char* msg) {
+ Label ok;
+ Condition is_smi = CheckSmi(object);
+ j(is_smi, &ok);
+ Cmp(FieldOperand(object, HeapObject::kMapOffset),
+ Factory::heap_number_map());
+ Assert(equal, msg);
+ bind(&ok);
+}
+
+
Condition MacroAssembler::IsObjectStringType(Register heap_object,
Register map,
Register instance_type) {
@@ -1774,39 +1786,14 @@ void MacroAssembler::CopyRegistersFromStackToMemory(Register base,
}
}
-#endif // ENABLE_DEBUGGER_SUPPORT
-
-
-void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag) {
- bool resolved;
- Handle<Code> code = ResolveBuiltin(id, &resolved);
-
- // Calls are not allowed in some stubs.
- ASSERT(flag == JUMP_FUNCTION || allow_stub_calls());
-
- // Rely on the assertion to check that the number of provided
- // arguments match the expected number of arguments. Fake a
- // parameter count to avoid emitting code to do the check.
- ParameterCount expected(0);
- InvokeCode(Handle<Code>(code),
- expected,
- expected,
- RelocInfo::CODE_TARGET,
- flag);
-
- const char* name = Builtins::GetName(id);
- int argc = Builtins::GetArgumentsCount(id);
- // The target address for the jump is stored as an immediate at offset
- // kInvokeCodeAddressOffset.
- if (!resolved) {
- uint32_t flags =
- Bootstrapper::FixupFlagsArgumentsCount::encode(argc) |
- Bootstrapper::FixupFlagsUseCodeObject::encode(false);
- Unresolved entry =
- { pc_offset() - kCallTargetAddressOffset, flags, name };
- unresolved_.Add(entry);
- }
+void MacroAssembler::DebugBreak() {
+ ASSERT(allow_stub_calls());
+ xor_(rax, rax); // no arguments
+ movq(rbx, ExternalReference(Runtime::kDebugBreak));
+ CEntryStub ces(1);
+ Call(ces.GetCode(), RelocInfo::DEBUG_BREAK);
}
+#endif // ENABLE_DEBUGGER_SUPPORT
void MacroAssembler::InvokePrologue(const ParameterCount& expected,
@@ -1926,6 +1913,21 @@ void MacroAssembler::InvokeFunction(Register function,
}
+void MacroAssembler::InvokeFunction(JSFunction* function,
+ const ParameterCount& actual,
+ InvokeFlag flag) {
+ ASSERT(function->is_compiled());
+ // Get the function and setup the context.
+ Move(rdi, Handle<JSFunction>(function));
+ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
+
+ // Invoke the cached code.
+ Handle<Code> code(function->code());
+ ParameterCount expected(function->shared()->formal_parameter_count());
+ InvokeCode(code, expected, actual, RelocInfo::CODE_TARGET, flag);
+}
+
+
void MacroAssembler::EnterFrame(StackFrame::Type type) {
push(rbp);
movq(rbp, rsp);
@@ -1965,13 +1967,9 @@ void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode, int result_size) {
// Reserve room for entry stack pointer and push the debug marker.
ASSERT(ExitFrameConstants::kSPOffset == -1 * kPointerSize);
- push(Immediate(0)); // saved entry sp, patched before call
- if (mode == ExitFrame::MODE_DEBUG) {
- push(Immediate(0));
- } else {
- movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT);
- push(kScratchRegister);
- }
+ push(Immediate(0)); // Saved entry sp, patched before call.
+ movq(kScratchRegister, CodeObject(), RelocInfo::EMBEDDED_OBJECT);
+ push(kScratchRegister); // Accessed from EditFrame::code_slot.
// Save the frame pointer and the context in top.
ExternalReference c_entry_fp_address(Top::k_c_entry_fp_address);
diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h
index a975dcaf..2673086d 100644
--- a/src/x64/macro-assembler-x64.h
+++ b/src/x64/macro-assembler-x64.h
@@ -36,7 +36,7 @@ namespace internal {
// Default scratch register used by MacroAssembler (and other code that needs
// a spare register). The register isn't callee save, and not used by the
// function calling convention.
-static const Register kScratchRegister = r10;
+static const Register kScratchRegister = { 10 }; // r10.
// Convenience for platform-independent signatures.
typedef Operand MemOperand;
@@ -98,6 +98,7 @@ class MacroAssembler: public Assembler {
void CopyRegistersFromStackToMemory(Register base,
Register scratch,
RegList regs);
+ void DebugBreak();
#endif
// ---------------------------------------------------------------------------
@@ -148,6 +149,10 @@ class MacroAssembler: public Assembler {
const ParameterCount& actual,
InvokeFlag flag);
+ void InvokeFunction(JSFunction* function,
+ const ParameterCount& actual,
+ InvokeFlag flag);
+
// Invoke specified builtin JavaScript function. Adds an entry to
// the unresolved list if the name does not resolve.
void InvokeBuiltin(Builtins::JavaScript id, InvokeFlag flag);
@@ -481,6 +486,9 @@ class MacroAssembler: public Assembler {
// jcc instructions (je, ja, jae, jb, jbe, je, and jz).
void FCmp();
+ // Abort execution if argument is not a number. Used in debug code.
+ void AbortIfNotNumber(Register object, const char* msg);
+
// ---------------------------------------------------------------------------
// Exception handling
@@ -643,6 +651,10 @@ class MacroAssembler: public Assembler {
// Convenience function: Same as above, but takes the fid instead.
void CallRuntime(Runtime::FunctionId id, int num_arguments);
+ // Convenience function: call an external reference.
+ void CallExternalReference(const ExternalReference& ext,
+ int num_arguments);
+
// Tail call of a runtime routine (jump).
// Like JumpToRuntime, but also takes care of passing the number
// of arguments.
@@ -679,13 +691,6 @@ class MacroAssembler: public Assembler {
void Ret();
- struct Unresolved {
- int pc;
- uint32_t flags; // see Bootstrapper::FixupFlags decoders/encoders.
- const char* name;
- };
- List<Unresolved>* unresolved() { return &unresolved_; }
-
Handle<Object> CodeObject() { return code_object_; }
@@ -717,7 +722,6 @@ class MacroAssembler: public Assembler {
bool allow_stub_calls() { return allow_stub_calls_; }
private:
- List<Unresolved> unresolved_;
bool generating_stub_;
bool allow_stub_calls_;
// This handle will be patched with the code object on installation.
@@ -731,18 +735,6 @@ class MacroAssembler: public Assembler {
Label* done,
InvokeFlag flag);
- // Prepares for a call or jump to a builtin by doing two things:
- // 1. Emits code that fetches the builtin's function object from the context
- // at runtime, and puts it in the register rdi.
- // 2. Fetches the builtin's code object, and returns it in a handle, at
- // compile time, so that later code can emit instructions to jump or call
- // the builtin directly. If the code object has not yet been created, it
- // returns the builtin code object for IllegalFunction, and sets the
- // output parameter "resolved" to false. Code that uses the return value
- // should then add the address and the builtin name to the list of fixups
- // called unresolved_, which is fixed up by the bootstrapper.
- Handle<Code> ResolveBuiltin(Builtins::JavaScript id, bool* resolved);
-
// Activation support.
void EnterFrame(StackFrame::Type type);
void LeaveFrame(StackFrame::Type type);
diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc
index 693447b5..9c8b4f75 100644
--- a/src/x64/stub-cache-x64.cc
+++ b/src/x64/stub-cache-x64.cc
@@ -133,11 +133,10 @@ void StubCompiler::GenerateFastPropertyLoad(MacroAssembler* masm,
}
-template <typename Pushable>
static void PushInterceptorArguments(MacroAssembler* masm,
Register receiver,
Register holder,
- Pushable name,
+ Register name,
JSObject* holder_obj) {
__ push(receiver);
__ push(holder);
@@ -201,8 +200,9 @@ void StubCache::GenerateProbe(MacroAssembler* masm,
}
+// Both name_reg and receiver_reg are preserved on jumps to miss_label,
+// but may be destroyed if store is successful.
void StubCompiler::GenerateStoreField(MacroAssembler* masm,
- Builtins::Name storage_extend,
JSObject* object,
int index,
Map* transition,
@@ -231,9 +231,13 @@ void StubCompiler::GenerateStoreField(MacroAssembler* masm,
if ((transition != NULL) && (object->map()->unused_property_fields() == 0)) {
// The properties must be extended before we can store the value.
// We jump to a runtime call that extends the properties array.
- __ Move(rcx, Handle<Map>(transition));
- Handle<Code> ic(Builtins::builtin(storage_extend));
- __ Jump(ic, RelocInfo::CODE_TARGET);
+ __ pop(scratch); // Return address.
+ __ push(receiver_reg);
+ __ Push(Handle<Map>(transition));
+ __ push(rax);
+ __ push(scratch);
+ __ TailCallRuntime(
+ ExternalReference(IC_Utility(IC::kSharedStoreIC_ExtendStorage)), 3, 1);
return;
}
@@ -314,38 +318,39 @@ static void GenerateStringCheck(MacroAssembler* masm,
void StubCompiler::GenerateLoadStringLength(MacroAssembler* masm,
Register receiver,
- Register scratch,
+ Register scratch1,
+ Register scratch2,
Label* miss) {
- Label load_length, check_wrapper;
+ Label check_wrapper;
// Check if the object is a string leaving the instance type in the
// scratch register.
- GenerateStringCheck(masm, receiver, scratch, miss, &check_wrapper);
+ GenerateStringCheck(masm, receiver, scratch1, miss, &check_wrapper);
// Load length directly from the string.
- __ bind(&load_length);
__ movl(rax, FieldOperand(receiver, String::kLengthOffset));
__ Integer32ToSmi(rax, rax);
__ ret(0);
// Check if the object is a JSValue wrapper.
__ bind(&check_wrapper);
- __ cmpl(scratch, Immediate(JS_VALUE_TYPE));
+ __ cmpl(scratch1, Immediate(JS_VALUE_TYPE));
__ j(not_equal, miss);
// Check if the wrapped value is a string and load the length
// directly if it is.
- __ movq(receiver, FieldOperand(receiver, JSValue::kValueOffset));
- GenerateStringCheck(masm, receiver, scratch, miss, miss);
- __ jmp(&load_length);
+ __ movq(scratch2, FieldOperand(receiver, JSValue::kValueOffset));
+ GenerateStringCheck(masm, scratch2, scratch1, miss, miss);
+ __ movl(rax, FieldOperand(scratch2, String::kLengthOffset));
+ __ Integer32ToSmi(rax, rax);
+ __ ret(0);
}
-template <class Pushable>
static void CompileCallLoadPropertyWithInterceptor(MacroAssembler* masm,
Register receiver,
Register holder,
- Pushable name,
+ Register name,
JSObject* holder_obj) {
PushInterceptorArguments(masm, receiver, holder, name, holder_obj);
@@ -394,7 +399,7 @@ static void CompileLoadInterceptor(Compiler* compiler,
stub_compiler->CheckPrototypes(object, receiver, holder,
scratch1, scratch2, name, miss);
- if (lookup->IsValid() && lookup->IsCacheable()) {
+ if (lookup->IsProperty() && lookup->IsCacheable()) {
compiler->CompileCacheable(masm,
stub_compiler,
receiver,
@@ -430,7 +435,7 @@ class LoadInterceptorCompiler BASE_EMBEDDED {
LookupResult* lookup,
String* name,
Label* miss_label) {
- AccessorInfo* callback = 0;
+ AccessorInfo* callback = NULL;
bool optimize = false;
// So far the most popular follow ups for interceptor loads are FIELD
// and CALLBACKS, so inline only them, other cases may be added
@@ -553,8 +558,8 @@ class LoadInterceptorCompiler BASE_EMBEDDED {
class CallInterceptorCompiler BASE_EMBEDDED {
public:
- explicit CallInterceptorCompiler(const ParameterCount& arguments)
- : arguments_(arguments), argc_(arguments.immediate()) {}
+ CallInterceptorCompiler(const ParameterCount& arguments, Register name)
+ : arguments_(arguments), name_(name) {}
void CompileCacheable(MacroAssembler* masm,
StubCompiler* stub_compiler,
@@ -584,18 +589,20 @@ class CallInterceptorCompiler BASE_EMBEDDED {
return;
}
+ ASSERT(!lookup->holder()->IsGlobalObject());
+
__ EnterInternalFrame();
- __ push(holder); // save the holder
+ __ push(holder); // Save the holder.
+ __ push(name_); // Save the name.
- CompileCallLoadPropertyWithInterceptor(
- masm,
- receiver,
- holder,
- // Under EnterInternalFrame this refers to name.
- Operand(rbp, (argc_ + 3) * kPointerSize),
- holder_obj);
+ CompileCallLoadPropertyWithInterceptor(masm,
+ receiver,
+ holder,
+ name_,
+ holder_obj);
- __ pop(receiver); // restore holder
+ __ pop(name_); // Restore the name.
+ __ pop(receiver); // Restore the holder.
__ LeaveInternalFrame();
__ CompareRoot(rax, Heap::kNoInterceptorResultSentinelRootIndex);
@@ -607,22 +614,8 @@ class CallInterceptorCompiler BASE_EMBEDDED {
scratch2,
name,
miss_label);
- if (lookup->holder()->IsGlobalObject()) {
- __ movq(rdx, Operand(rsp, (argc_ + 1) * kPointerSize));
- __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalReceiverOffset));
- __ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rdx);
- }
- ASSERT(function->is_compiled());
- // Get the function and setup the context.
- __ Move(rdi, Handle<JSFunction>(function));
- __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
-
- // Jump to the cached code (tail call).
- Handle<Code> code(function->code());
- ParameterCount expected(function->shared()->formal_parameter_count());
- __ InvokeCode(code, expected, arguments_,
- RelocInfo::CODE_TARGET, JUMP_FUNCTION);
+ __ InvokeFunction(function, arguments_, JUMP_FUNCTION);
__ bind(&invoke);
}
@@ -634,27 +627,26 @@ class CallInterceptorCompiler BASE_EMBEDDED {
JSObject* holder_obj,
Label* miss_label) {
__ EnterInternalFrame();
+ // Save the name_ register across the call.
+ __ push(name_);
PushInterceptorArguments(masm,
receiver,
holder,
- Operand(rbp, (argc_ + 3) * kPointerSize),
+ name_,
holder_obj);
- ExternalReference ref = ExternalReference(
- IC_Utility(IC::kLoadPropertyWithInterceptorForCall));
- __ movq(rax, Immediate(5));
- __ movq(rbx, ref);
-
- CEntryStub stub(1);
- __ CallStub(&stub);
+ __ CallExternalReference(
+ ExternalReference(IC_Utility(IC::kLoadPropertyWithInterceptorForCall)),
+ 5);
+ __ pop(name_);
__ LeaveInternalFrame();
}
private:
const ParameterCount& arguments_;
- int argc_;
+ Register name_;
};
@@ -669,14 +661,14 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
String* name,
StubCompiler::CheckType check) {
// ----------- S t a t e -------------
- // -----------------------------------
- // rsp[0] return address
- // rsp[8] argument argc
- // rsp[16] argument argc - 1
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
// ...
- // rsp[argc * 8] argument 1
- // rsp[(argc + 1) * 8] argument 0 = receiver
- // rsp[(argc + 2) * 8] function name
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // -----------------------------------
Label miss;
@@ -697,7 +689,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
case RECEIVER_MAP_CHECK:
// Check that the maps haven't changed.
CheckPrototypes(JSObject::cast(object), rdx, holder,
- rbx, rcx, name, &miss);
+ rbx, rax, name, &miss);
// Patch the receiver on the stack with the global proxy if
// necessary.
@@ -713,13 +705,13 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
__ jmp(&miss);
} else {
// Check that the object is a two-byte string or a symbol.
- __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rcx);
+ __ CmpObjectType(rdx, FIRST_NONSTRING_TYPE, rax);
__ j(above_equal, &miss);
// Check that the maps starting from the prototype haven't changed.
GenerateLoadGlobalFunctionPrototype(masm(),
Context::STRING_FUNCTION_INDEX,
- rcx);
- CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder,
+ rax);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder,
rbx, rdx, name, &miss);
}
break;
@@ -732,14 +724,14 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
Label fast;
// Check that the object is a smi or a heap number.
__ JumpIfSmi(rdx, &fast);
- __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rcx);
+ __ CmpObjectType(rdx, HEAP_NUMBER_TYPE, rax);
__ j(not_equal, &miss);
__ bind(&fast);
// Check that the maps starting from the prototype haven't changed.
GenerateLoadGlobalFunctionPrototype(masm(),
Context::NUMBER_FUNCTION_INDEX,
- rcx);
- CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder,
+ rax);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder,
rbx, rdx, name, &miss);
}
break;
@@ -760,8 +752,8 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
// Check that the maps starting from the prototype haven't changed.
GenerateLoadGlobalFunctionPrototype(masm(),
Context::BOOLEAN_FUNCTION_INDEX,
- rcx);
- CheckPrototypes(JSObject::cast(object->GetPrototype()), rcx, holder,
+ rax);
+ CheckPrototypes(JSObject::cast(object->GetPrototype()), rax, holder,
rbx, rdx, name, &miss);
}
break;
@@ -769,7 +761,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
case JSARRAY_HAS_FAST_ELEMENTS_CHECK:
CheckPrototypes(JSObject::cast(object), rdx, holder,
- rbx, rcx, name, &miss);
+ rbx, rax, name, &miss);
// Make sure object->HasFastElements().
// Get the elements array of the object.
__ movq(rbx, FieldOperand(rdx, JSObject::kElementsOffset));
@@ -783,16 +775,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
UNREACHABLE();
}
- // Get the function and setup the context.
- __ Move(rdi, Handle<JSFunction>(function));
- __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
-
- // Jump to the cached code (tail call).
- ASSERT(function->is_compiled());
- Handle<Code> code(function->code());
- ParameterCount expected(function->shared()->formal_parameter_count());
- __ InvokeCode(code, expected, arguments(),
- RelocInfo::CODE_TARGET, JUMP_FUNCTION);
+ __ InvokeFunction(function, arguments(), JUMP_FUNCTION);
// Handle call cache miss.
__ bind(&miss);
@@ -808,19 +791,19 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
}
-Object* CallStubCompiler::CompileCallField(Object* object,
+Object* CallStubCompiler::CompileCallField(JSObject* object,
JSObject* holder,
int index,
String* name) {
// ----------- S t a t e -------------
- // -----------------------------------
- // rsp[0] return address
- // rsp[8] argument argc
- // rsp[16] argument argc - 1
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
// ...
- // rsp[argc * 8] argument 1
- // rsp[(argc + 1) * 8] argument 0 = receiver
- // rsp[(argc + 2) * 8] function name
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
+ // -----------------------------------
Label miss;
// Get the receiver from the stack.
@@ -831,9 +814,7 @@ Object* CallStubCompiler::CompileCallField(Object* object,
__ JumpIfSmi(rdx, &miss);
// Do the right check and compute the holder register.
- Register reg =
- CheckPrototypes(JSObject::cast(object), rdx, holder,
- rbx, rcx, name, &miss);
+ Register reg = CheckPrototypes(object, rdx, holder, rbx, rax, name, &miss);
GenerateFastPropertyLoad(masm(), rdi, reg, holder, index);
@@ -862,10 +843,17 @@ Object* CallStubCompiler::CompileCallField(Object* object,
}
-Object* CallStubCompiler::CompileCallInterceptor(Object* object,
+Object* CallStubCompiler::CompileCallInterceptor(JSObject* object,
JSObject* holder,
String* name) {
// ----------- S t a t e -------------
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
+ // ...
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
// -----------------------------------
Label miss;
@@ -878,17 +866,17 @@ Object* CallStubCompiler::CompileCallInterceptor(Object* object,
// Get the receiver from the stack.
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
- CallInterceptorCompiler compiler(arguments());
+ CallInterceptorCompiler compiler(arguments(), rcx);
CompileLoadInterceptor(&compiler,
this,
masm(),
- JSObject::cast(object),
+ object,
holder,
name,
&lookup,
rdx,
rbx,
- rcx,
+ rdi,
&miss);
// Restore receiver.
@@ -920,7 +908,6 @@ Object* CallStubCompiler::CompileCallInterceptor(Object* object,
}
-
Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
GlobalObject* holder,
JSGlobalPropertyCell* cell,
@@ -928,13 +915,13 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
String* name) {
// ----------- S t a t e -------------
// -----------------------------------
- // rsp[0] return address
- // rsp[8] argument argc
- // rsp[16] argument argc - 1
+ // rcx : function name
+ // rsp[0] : return address
+ // rsp[8] : argument argc
+ // rsp[16] : argument argc - 1
// ...
- // rsp[argc * 8] argument 1
- // rsp[(argc + 1) * 8] argument 0 = receiver
- // rsp[(argc + 2) * 8] function name
+ // rsp[argc * 8] : argument 1
+ // rsp[(argc + 1) * 8] : argument 0 = receiver
Label miss;
// Get the number of arguments.
@@ -951,7 +938,7 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
}
// Check that the maps haven't changed.
- CheckPrototypes(object, rdx, holder, rbx, rcx, name, &miss);
+ CheckPrototypes(object, rdx, holder, rbx, rax, name, &miss);
// Get the value from the cell.
__ Move(rdi, Handle<JSGlobalPropertyCell>(cell));
@@ -965,12 +952,12 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
// function can all use this call IC. Before we load through the
// function, we have to verify that it still is a function.
__ JumpIfSmi(rdi, &miss);
- __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rax);
__ j(not_equal, &miss);
// Check the shared function info. Make sure it hasn't changed.
- __ Move(rcx, Handle<SharedFunctionInfo>(function->shared()));
- __ cmpq(FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset), rcx);
+ __ Move(rax, Handle<SharedFunctionInfo>(function->shared()));
+ __ cmpq(FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset), rax);
__ j(not_equal, &miss);
} else {
__ Cmp(rdi, Handle<JSFunction>(function));
@@ -1325,7 +1312,7 @@ Object* KeyedLoadStubCompiler::CompileLoadStringLength(String* name) {
__ Cmp(rax, Handle<String>(name));
__ j(not_equal, &miss);
- GenerateLoadStringLength(masm(), rcx, rdx, &miss);
+ GenerateLoadStringLength(masm(), rcx, rdx, rbx, &miss);
__ bind(&miss);
__ DecrementCounter(&Counters::keyed_load_string_length, 1);
GenerateLoadMiss(masm(), Code::KEYED_LOAD_IC);
@@ -1397,9 +1384,8 @@ Object* StoreStubCompiler::CompileStoreField(JSObject* object,
// -----------------------------------
Label miss;
- // Generate store field code. Trashes the name register.
+ // Generate store field code. Preserves receiver and name on jump to miss.
GenerateStoreField(masm(),
- Builtins::StoreIC_ExtendStorage,
object,
index,
transition,
@@ -1408,7 +1394,6 @@ Object* StoreStubCompiler::CompileStoreField(JSObject* object,
// Handle store cache miss.
__ bind(&miss);
- __ Move(rcx, Handle<String>(name)); // restore name
Handle<Code> ic(Builtins::builtin(Builtins::StoreIC_Miss));
__ Jump(ic, RelocInfo::CODE_TARGET);
@@ -1550,16 +1535,15 @@ Object* KeyedStoreStubCompiler::CompileStoreField(JSObject* object,
__ Cmp(rcx, Handle<String>(name));
__ j(not_equal, &miss);
- // Get the object from the stack.
- __ movq(rbx, Operand(rsp, 2 * kPointerSize));
+ // Get the receiver from the stack.
+ __ movq(rdx, Operand(rsp, 2 * kPointerSize));
- // Generate store field code. Trashes the name register.
+ // Generate store field code. Preserves receiver and name on jump to miss.
GenerateStoreField(masm(),
- Builtins::KeyedStoreIC_ExtendStorage,
object,
index,
transition,
- rbx, rcx, rdx,
+ rdx, rcx, rbx,
&miss);
// Handle store cache miss.
@@ -1665,7 +1649,11 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
Register holder_reg,
Register scratch,
String* name,
+ int save_at_depth,
Label* miss) {
+ // TODO(602): support object saving.
+ ASSERT(save_at_depth == kInvalidProtoDepth);
+
// Check that the maps haven't changed.
Register result =
__ CheckMaps(object, object_reg, holder, holder_reg, scratch, miss);
diff --git a/src/x64/virtual-frame-x64.cc b/src/x64/virtual-frame-x64.cc
index cb93d5d4..a0e883c8 100644
--- a/src/x64/virtual-frame-x64.cc
+++ b/src/x64/virtual-frame-x64.cc
@@ -45,7 +45,7 @@ VirtualFrame::VirtualFrame()
: elements_(parameter_count() + local_count() + kPreallocatedElements),
stack_pointer_(parameter_count() + 1) { // 0-based index of TOS.
for (int i = 0; i <= stack_pointer_; i++) {
- elements_.Add(FrameElement::MemoryElement());
+ elements_.Add(FrameElement::MemoryElement(NumberInfo::kUnknown));
}
for (int i = 0; i < RegisterAllocator::kNumRegisters; i++) {
register_locations_[i] = kIllegalIndex;
@@ -193,25 +193,25 @@ void VirtualFrame::EmitPop(const Operand& operand) {
}
-void VirtualFrame::EmitPush(Register reg) {
+void VirtualFrame::EmitPush(Register reg, NumberInfo::Type info) {
ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement());
+ elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
__ push(reg);
}
-void VirtualFrame::EmitPush(const Operand& operand) {
+void VirtualFrame::EmitPush(const Operand& operand, NumberInfo::Type info) {
ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement());
+ elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
__ push(operand);
}
-void VirtualFrame::EmitPush(Immediate immediate) {
+void VirtualFrame::EmitPush(Immediate immediate, NumberInfo::Type info) {
ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement());
+ elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
__ push(immediate);
}
@@ -219,7 +219,7 @@ void VirtualFrame::EmitPush(Immediate immediate) {
void VirtualFrame::EmitPush(Smi* smi_value) {
ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement());
+ elements_.Add(FrameElement::MemoryElement(NumberInfo::kSmi));
stack_pointer_++;
__ Push(smi_value);
}
@@ -227,15 +227,21 @@ void VirtualFrame::EmitPush(Smi* smi_value) {
void VirtualFrame::EmitPush(Handle<Object> value) {
ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement());
+ NumberInfo::Type info = NumberInfo::kUnknown;
+ if (value->IsSmi()) {
+ info = NumberInfo::kSmi;
+ } else if (value->IsHeapNumber()) {
+ info = NumberInfo::kHeapNumber;
+ }
+ elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
__ Push(value);
}
-void VirtualFrame::EmitPush(Heap::RootListIndex index) {
+void VirtualFrame::EmitPush(Heap::RootListIndex index, NumberInfo::Type info) {
ASSERT(stack_pointer_ == element_count() - 1);
- elements_.Add(FrameElement::MemoryElement());
+ elements_.Add(FrameElement::MemoryElement(info));
stack_pointer_++;
__ PushRoot(index);
}
@@ -305,10 +311,14 @@ int VirtualFrame::InvalidateFrameSlotAt(int index) {
// Set the new backing element.
if (elements_[new_backing_index].is_synced()) {
elements_[new_backing_index] =
- FrameElement::RegisterElement(backing_reg, FrameElement::SYNCED);
+ FrameElement::RegisterElement(backing_reg,
+ FrameElement::SYNCED,
+ original.number_info());
} else {
elements_[new_backing_index] =
- FrameElement::RegisterElement(backing_reg, FrameElement::NOT_SYNCED);
+ FrameElement::RegisterElement(backing_reg,
+ FrameElement::NOT_SYNCED,
+ original.number_info());
}
// Update the other copies.
for (int i = new_backing_index + 1; i < element_count(); i++) {
@@ -339,7 +349,8 @@ void VirtualFrame::TakeFrameSlotAt(int index) {
ASSERT(fresh.is_valid());
FrameElement new_element =
FrameElement::RegisterElement(fresh.reg(),
- FrameElement::NOT_SYNCED);
+ FrameElement::NOT_SYNCED,
+ original.number_info());
Use(fresh.reg(), element_count());
elements_.Add(new_element);
__ movq(fresh.reg(), Operand(rbp, fp_relative(index)));
@@ -480,10 +491,12 @@ void VirtualFrame::MakeMergable() {
for (int i = 0; i < element_count(); i++) {
FrameElement element = elements_[i];
+ // In all cases we have to reset the number type information
+ // to unknown for a mergable frame because of incoming back edges.
if (element.is_constant() || element.is_copy()) {
if (element.is_synced()) {
// Just spill.
- elements_[i] = FrameElement::MemoryElement();
+ elements_[i] = FrameElement::MemoryElement(NumberInfo::kUnknown);
} else {
// Allocate to a register.
FrameElement backing_element; // Invalid if not a copy.
@@ -494,7 +507,8 @@ void VirtualFrame::MakeMergable() {
ASSERT(fresh.is_valid()); // A register was spilled if all were in use.
elements_[i] =
FrameElement::RegisterElement(fresh.reg(),
- FrameElement::NOT_SYNCED);
+ FrameElement::NOT_SYNCED,
+ NumberInfo::kUnknown);
Use(fresh.reg(), i);
// Emit a move.
@@ -523,6 +537,7 @@ void VirtualFrame::MakeMergable() {
// The copy flag is not relied on before the end of this loop,
// including when registers are spilled.
elements_[i].clear_copied();
+ elements_[i].set_number_info(NumberInfo::kUnknown);
}
}
}
@@ -728,6 +743,14 @@ Result VirtualFrame::Pop() {
int index = element_count();
ASSERT(element.is_valid());
+ // Get number type information of the result.
+ NumberInfo::Type info;
+ if (!element.is_copy()) {
+ info = element.number_info();
+ } else {
+ info = elements_[element.index()].number_info();
+ }
+
bool pop_needed = (stack_pointer_ == index);
if (pop_needed) {
stack_pointer_--;
@@ -735,6 +758,7 @@ Result VirtualFrame::Pop() {
Result temp = cgen()->allocator()->Allocate();
ASSERT(temp.is_valid());
__ pop(temp.reg());
+ temp.set_number_info(info);
return temp;
}
@@ -762,14 +786,16 @@ Result VirtualFrame::Pop() {
ASSERT(temp.is_valid());
Use(temp.reg(), index);
FrameElement new_element =
- FrameElement::RegisterElement(temp.reg(), FrameElement::SYNCED);
+ FrameElement::RegisterElement(temp.reg(),
+ FrameElement::SYNCED,
+ element.number_info());
// Preserve the copy flag on the element.
if (element.is_copied()) new_element.set_copied();
elements_[index] = new_element;
__ movq(temp.reg(), Operand(rbp, fp_relative(index)));
- return Result(temp.reg());
+ return Result(temp.reg(), info);
} else if (element.is_register()) {
- return Result(element.reg());
+ return Result(element.reg(), info);
} else {
ASSERT(element.is_constant());
return Result(element.handle());
@@ -969,6 +995,17 @@ Result VirtualFrame::CallRuntime(Runtime::FunctionId id, int arg_count) {
}
+#ifdef ENABLE_DEBUGGER_SUPPORT
+void VirtualFrame::DebugBreak() {
+ PrepareForCall(0, 0);
+ ASSERT(cgen()->HasValidEntryRegisters());
+ __ DebugBreak();
+ Result result = cgen()->allocator()->Allocate(rax);
+ ASSERT(result.is_valid());
+}
+#endif
+
+
Result VirtualFrame::CallLoadIC(RelocInfo::Mode mode) {
// Name and receiver are on the top of the frame. The IC expects
// name in rcx and receiver on the stack. It does not drop the
@@ -996,7 +1033,6 @@ Result VirtualFrame::CallKeyedStoreIC() {
// expects value in rax and key and receiver on the stack. It does
// not drop the key and receiver.
Handle<Code> ic(Builtins::builtin(Builtins::KeyedStoreIC_Initialize));
- // TODO(1222589): Make the IC grab the values from the stack.
Result value = Pop();
PrepareForCall(2, 0); // Two stack args, neither callee-dropped.
value.ToRegister(rax);
@@ -1008,14 +1044,17 @@ Result VirtualFrame::CallKeyedStoreIC() {
Result VirtualFrame::CallCallIC(RelocInfo::Mode mode,
int arg_count,
int loop_nesting) {
- // Arguments, receiver, and function name are on top of the frame.
- // The IC expects them on the stack. It does not drop the function
- // name slot (but it does drop the rest).
+ // Function name, arguments, and receiver are found on top of the frame
+ // and dropped by the call. The IC expects the name in rcx and the rest
+ // on the stack, and drops them all.
InLoopFlag in_loop = loop_nesting > 0 ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic = cgen()->ComputeCallInitialize(arg_count, in_loop);
+ Result name = Pop();
// Spill args, receiver, and function. The call will drop args and
// receiver.
- PrepareForCall(arg_count + 2, arg_count + 1);
+ PrepareForCall(arg_count + 1, arg_count + 1);
+ name.ToRegister(rcx);
+ name.Unuse();
return RawCallCodeObject(ic, mode);
}
diff --git a/src/x64/virtual-frame-x64.h b/src/x64/virtual-frame-x64.h
index 8e3e40f0..c9aa7991 100644
--- a/src/x64/virtual-frame-x64.h
+++ b/src/x64/virtual-frame-x64.h
@@ -28,6 +28,7 @@
#ifndef V8_X64_VIRTUAL_FRAME_X64_H_
#define V8_X64_VIRTUAL_FRAME_X64_H_
+#include "number-info.h"
#include "register-allocator.h"
#include "scopes.h"
@@ -81,7 +82,8 @@ class VirtualFrame : public ZoneObject {
MacroAssembler* masm() { return cgen()->masm(); }
// Create a duplicate of an existing valid frame element.
- FrameElement CopyElementAt(int index);
+ FrameElement CopyElementAt(int index,
+ NumberInfo::Type info = NumberInfo::kUninitialized);
// The number of elements on the virtual frame.
int element_count() { return elements_.length(); }
@@ -321,6 +323,10 @@ class VirtualFrame : public ZoneObject {
Result CallRuntime(Runtime::Function* f, int arg_count);
Result CallRuntime(Runtime::FunctionId id, int arg_count);
+#ifdef ENABLE_DEBUGGER_SUPPORT
+ void DebugBreak();
+#endif
+
// Invoke builtin given the number of arguments it expects on (and
// removes from) the stack.
Result InvokeBuiltin(Builtins::JavaScript id,
@@ -343,9 +349,9 @@ class VirtualFrame : public ZoneObject {
// of the frame. Key and receiver are not dropped.
Result CallKeyedStoreIC();
- // Call call IC. Arguments, receiver, and function name are found
- // on top of the frame. Function name slot is not dropped. The
- // argument count does not include the receiver.
+ // Call call IC. Function name, arguments, and receiver are found on top
+ // of the frame and dropped by the call.
+ // The argument count does not include the receiver.
Result CallCallIC(RelocInfo::Mode mode, int arg_count, int loop_nesting);
// Allocate and call JS function as constructor. Arguments,
@@ -376,16 +382,20 @@ class VirtualFrame : public ZoneObject {
// Push an element on top of the expression stack and emit a
// corresponding push instruction.
- void EmitPush(Register reg);
- void EmitPush(const Operand& operand);
- void EmitPush(Heap::RootListIndex index);
- void EmitPush(Immediate immediate);
+ void EmitPush(Register reg,
+ NumberInfo::Type info = NumberInfo::kUnknown);
+ void EmitPush(const Operand& operand,
+ NumberInfo::Type info = NumberInfo::kUnknown);
+ void EmitPush(Heap::RootListIndex index,
+ NumberInfo::Type info = NumberInfo::kUnknown);
+ void EmitPush(Immediate immediate,
+ NumberInfo::Type info = NumberInfo::kUnknown);
void EmitPush(Smi* value);
// Uses kScratchRegister, emits appropriate relocation info.
void EmitPush(Handle<Object> value);
// Push an element on the virtual frame.
- void Push(Register reg);
+ void Push(Register reg, NumberInfo::Type info = NumberInfo::kUnknown);
void Push(Handle<Object> value);
void Push(Smi* value) { Push(Handle<Object>(value)); }
@@ -393,7 +403,7 @@ class VirtualFrame : public ZoneObject {
// frame).
void Push(Result* result) {
if (result->is_register()) {
- Push(result->reg());
+ Push(result->reg(), result->number_info());
} else {
ASSERT(result->is_constant());
Push(result->handle());
diff --git a/test/cctest/test-api.cc b/test/cctest/test-api.cc
index 0a392eb6..db16bfc4 100644
--- a/test/cctest/test-api.cc
+++ b/test/cctest/test-api.cc
@@ -394,6 +394,9 @@ THREADED_TEST(ScriptMakingExternalString) {
v8::HandleScope scope;
LocalContext env;
Local<String> source = String::New(two_byte_source);
+ // Trigger GCs so that the newly allocated string moves to old gen.
+ i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
+ i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
bool success = source->MakeExternal(new TestResource(two_byte_source));
CHECK(success);
Local<Script> script = Script::Compile(source);
@@ -416,6 +419,9 @@ THREADED_TEST(ScriptMakingExternalAsciiString) {
v8::HandleScope scope;
LocalContext env;
Local<String> source = v8_str(c_source);
+ // Trigger GCs so that the newly allocated string moves to old gen.
+ i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
+ i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
bool success = source->MakeExternal(
new TestAsciiResource(i::StrDup(c_source)));
CHECK(success);
@@ -432,6 +438,80 @@ THREADED_TEST(ScriptMakingExternalAsciiString) {
}
+TEST(MakingExternalStringConditions) {
+ v8::HandleScope scope;
+ LocalContext env;
+
+ // Free some space in the new space so that we can check freshness.
+ i::Heap::CollectGarbage(0, i::NEW_SPACE);
+ i::Heap::CollectGarbage(0, i::NEW_SPACE);
+
+ Local<String> small_string = String::New(AsciiToTwoByteString("small"));
+ // We should refuse to externalize newly created small string.
+ CHECK(!small_string->CanMakeExternal());
+ // Trigger GCs so that the newly allocated string moves to old gen.
+ i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
+ i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
+ // Old space strings should be accepted.
+ CHECK(small_string->CanMakeExternal());
+
+ small_string = String::New(AsciiToTwoByteString("small 2"));
+ // We should refuse externalizing newly created small string.
+ CHECK(!small_string->CanMakeExternal());
+ for (int i = 0; i < 100; i++) {
+ String::Value value(small_string);
+ }
+ // Frequently used strings should be accepted.
+ CHECK(small_string->CanMakeExternal());
+
+ const int buf_size = 10 * 1024;
+ char* buf = i::NewArray<char>(buf_size);
+ memset(buf, 'a', buf_size);
+ buf[buf_size - 1] = '\0';
+ Local<String> large_string = String::New(AsciiToTwoByteString(buf));
+ i::DeleteArray(buf);
+ // Large strings should be immediately accepted.
+ CHECK(large_string->CanMakeExternal());
+}
+
+
+TEST(MakingExternalAsciiStringConditions) {
+ v8::HandleScope scope;
+ LocalContext env;
+
+ // Free some space in the new space so that we can check freshness.
+ i::Heap::CollectGarbage(0, i::NEW_SPACE);
+ i::Heap::CollectGarbage(0, i::NEW_SPACE);
+
+ Local<String> small_string = String::New("small");
+ // We should refuse to externalize newly created small string.
+ CHECK(!small_string->CanMakeExternal());
+ // Trigger GCs so that the newly allocated string moves to old gen.
+ i::Heap::CollectGarbage(0, i::NEW_SPACE); // in survivor space now
+ i::Heap::CollectGarbage(0, i::NEW_SPACE); // in old gen now
+ // Old space strings should be accepted.
+ CHECK(small_string->CanMakeExternal());
+
+ small_string = String::New("small 2");
+ // We should refuse externalizing newly created small string.
+ CHECK(!small_string->CanMakeExternal());
+ for (int i = 0; i < 100; i++) {
+ String::Value value(small_string);
+ }
+ // Frequently used strings should be accepted.
+ CHECK(small_string->CanMakeExternal());
+
+ const int buf_size = 10 * 1024;
+ char* buf = i::NewArray<char>(buf_size);
+ memset(buf, 'a', buf_size);
+ buf[buf_size - 1] = '\0';
+ Local<String> large_string = String::New(buf);
+ i::DeleteArray(buf);
+ // Large strings should be immediately accepted.
+ CHECK(large_string->CanMakeExternal());
+}
+
+
THREADED_TEST(UsingExternalString) {
{
v8::HandleScope scope;
@@ -2448,6 +2528,33 @@ THREADED_TEST(NamedInterceptorPropertyRead) {
}
+static v8::Handle<Value> SetXOnPrototypeGetter(Local<String> property,
+ const AccessorInfo& info) {
+ // Set x on the prototype object and do not handle the get request.
+ v8::Handle<v8::Value> proto = info.Holder()->GetPrototype();
+ v8::Handle<v8::Object>::Cast(proto)->Set(v8_str("x"), v8::Integer::New(23));
+ return v8::Handle<Value>();
+}
+
+
+// This is a regression test for http://crbug.com/20104. Map
+// transitions should not interfere with post interceptor lookup.
+THREADED_TEST(NamedInterceptorMapTransitionRead) {
+ v8::HandleScope scope;
+ Local<v8::FunctionTemplate> function_template = v8::FunctionTemplate::New();
+ Local<v8::ObjectTemplate> instance_template
+ = function_template->InstanceTemplate();
+ instance_template->SetNamedPropertyHandler(SetXOnPrototypeGetter);
+ LocalContext context;
+ context->Global()->Set(v8_str("F"), function_template->GetFunction());
+ // Create an instance of F and introduce a map transition for x.
+ CompileRun("var o = new F(); o.x = 23;");
+ // Create an instance of F and invoke the getter. The result should be 23.
+ Local<Value> result = CompileRun("o = new F(); o.x");
+ CHECK_EQ(result->Int32Value(), 23);
+}
+
+
static v8::Handle<Value> IndexedPropertyGetter(uint32_t index,
const AccessorInfo& info) {
ApiTestFuzzer::Fuzz();
@@ -2529,6 +2636,195 @@ THREADED_TEST(IndexedInterceptorWithNoSetter) {
}
+THREADED_TEST(IndexedInterceptorWithAccessorCheck) {
+ v8::HandleScope scope;
+ Local<ObjectTemplate> templ = ObjectTemplate::New();
+ templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
+
+ LocalContext context;
+ Local<v8::Object> obj = templ->NewInstance();
+ obj->TurnOnAccessCheck();
+ context->Global()->Set(v8_str("obj"), obj);
+
+ const char* code =
+ "try {"
+ " for (var i = 0; i < 100; i++) {"
+ " var v = obj[0];"
+ " if (v != undefined) throw 'Wrong value ' + v + ' at iteration ' + i;"
+ " }"
+ " 'PASSED'"
+ "} catch(e) {"
+ " e"
+ "}";
+ ExpectString(code, "PASSED");
+}
+
+
+THREADED_TEST(IndexedInterceptorWithAccessorCheckSwitchedOn) {
+ i::FLAG_allow_natives_syntax = true;
+ v8::HandleScope scope;
+ Local<ObjectTemplate> templ = ObjectTemplate::New();
+ templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
+
+ LocalContext context;
+ Local<v8::Object> obj = templ->NewInstance();
+ context->Global()->Set(v8_str("obj"), obj);
+
+ const char* code =
+ "try {"
+ " for (var i = 0; i < 100; i++) {"
+ " var expected = i;"
+ " if (i == 5) {"
+ " %EnableAccessChecks(obj);"
+ " expected = undefined;"
+ " }"
+ " var v = obj[i];"
+ " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
+ " if (i == 5) %DisableAccessChecks(obj);"
+ " }"
+ " 'PASSED'"
+ "} catch(e) {"
+ " e"
+ "}";
+ ExpectString(code, "PASSED");
+}
+
+
+THREADED_TEST(IndexedInterceptorWithDifferentIndices) {
+ v8::HandleScope scope;
+ Local<ObjectTemplate> templ = ObjectTemplate::New();
+ templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
+
+ LocalContext context;
+ Local<v8::Object> obj = templ->NewInstance();
+ context->Global()->Set(v8_str("obj"), obj);
+
+ const char* code =
+ "try {"
+ " for (var i = 0; i < 100; i++) {"
+ " var v = obj[i];"
+ " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
+ " }"
+ " 'PASSED'"
+ "} catch(e) {"
+ " e"
+ "}";
+ ExpectString(code, "PASSED");
+}
+
+
+THREADED_TEST(IndexedInterceptorWithNotSmiLookup) {
+ v8::HandleScope scope;
+ Local<ObjectTemplate> templ = ObjectTemplate::New();
+ templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
+
+ LocalContext context;
+ Local<v8::Object> obj = templ->NewInstance();
+ context->Global()->Set(v8_str("obj"), obj);
+
+ const char* code =
+ "try {"
+ " for (var i = 0; i < 100; i++) {"
+ " var expected = i;"
+ " if (i == 50) {"
+ " i = 'foobar';"
+ " expected = undefined;"
+ " }"
+ " var v = obj[i];"
+ " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
+ " }"
+ " 'PASSED'"
+ "} catch(e) {"
+ " e"
+ "}";
+ ExpectString(code, "PASSED");
+}
+
+
+THREADED_TEST(IndexedInterceptorGoingMegamorphic) {
+ v8::HandleScope scope;
+ Local<ObjectTemplate> templ = ObjectTemplate::New();
+ templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
+
+ LocalContext context;
+ Local<v8::Object> obj = templ->NewInstance();
+ context->Global()->Set(v8_str("obj"), obj);
+
+ const char* code =
+ "var original = obj;"
+ "try {"
+ " for (var i = 0; i < 100; i++) {"
+ " var expected = i;"
+ " if (i == 50) {"
+ " obj = {50: 'foobar'};"
+ " expected = 'foobar';"
+ " }"
+ " var v = obj[i];"
+ " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
+ " if (i == 50) obj = original;"
+ " }"
+ " 'PASSED'"
+ "} catch(e) {"
+ " e"
+ "}";
+ ExpectString(code, "PASSED");
+}
+
+
+THREADED_TEST(IndexedInterceptorReceiverTurningSmi) {
+ v8::HandleScope scope;
+ Local<ObjectTemplate> templ = ObjectTemplate::New();
+ templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
+
+ LocalContext context;
+ Local<v8::Object> obj = templ->NewInstance();
+ context->Global()->Set(v8_str("obj"), obj);
+
+ const char* code =
+ "var original = obj;"
+ "try {"
+ " for (var i = 0; i < 100; i++) {"
+ " var expected = i;"
+ " if (i == 5) {"
+ " obj = 239;"
+ " expected = undefined;"
+ " }"
+ " var v = obj[i];"
+ " if (v != expected) throw 'Wrong value ' + v + ' at iteration ' + i;"
+ " if (i == 5) obj = original;"
+ " }"
+ " 'PASSED'"
+ "} catch(e) {"
+ " e"
+ "}";
+ ExpectString(code, "PASSED");
+}
+
+
+THREADED_TEST(IndexedInterceptorOnProto) {
+ v8::HandleScope scope;
+ Local<ObjectTemplate> templ = ObjectTemplate::New();
+ templ->SetIndexedPropertyHandler(IdentityIndexedPropertyGetter);
+
+ LocalContext context;
+ Local<v8::Object> obj = templ->NewInstance();
+ context->Global()->Set(v8_str("obj"), obj);
+
+ const char* code =
+ "var o = {__proto__: obj};"
+ "try {"
+ " for (var i = 0; i < 100; i++) {"
+ " var v = o[i];"
+ " if (v != i) throw 'Wrong value ' + v + ' at iteration ' + i;"
+ " }"
+ " 'PASSED'"
+ "} catch(e) {"
+ " e"
+ "}";
+ ExpectString(code, "PASSED");
+}
+
+
THREADED_TEST(MultiContexts) {
v8::HandleScope scope;
v8::Handle<ObjectTemplate> templ = ObjectTemplate::New();
@@ -4844,6 +5140,84 @@ THREADED_TEST(HiddenPrototype) {
}
+THREADED_TEST(SetPrototype) {
+ v8::HandleScope handle_scope;
+ LocalContext context;
+
+ Local<v8::FunctionTemplate> t0 = v8::FunctionTemplate::New();
+ t0->InstanceTemplate()->Set(v8_str("x"), v8_num(0));
+ Local<v8::FunctionTemplate> t1 = v8::FunctionTemplate::New();
+ t1->SetHiddenPrototype(true);
+ t1->InstanceTemplate()->Set(v8_str("y"), v8_num(1));
+ Local<v8::FunctionTemplate> t2 = v8::FunctionTemplate::New();
+ t2->SetHiddenPrototype(true);
+ t2->InstanceTemplate()->Set(v8_str("z"), v8_num(2));
+ Local<v8::FunctionTemplate> t3 = v8::FunctionTemplate::New();
+ t3->InstanceTemplate()->Set(v8_str("u"), v8_num(3));
+
+ Local<v8::Object> o0 = t0->GetFunction()->NewInstance();
+ Local<v8::Object> o1 = t1->GetFunction()->NewInstance();
+ Local<v8::Object> o2 = t2->GetFunction()->NewInstance();
+ Local<v8::Object> o3 = t3->GetFunction()->NewInstance();
+
+ // Setting the prototype on an object does not skip hidden prototypes.
+ CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
+ CHECK(o0->SetPrototype(o1));
+ CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
+ CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
+ CHECK(o1->SetPrototype(o2));
+ CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
+ CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
+ CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
+ CHECK(o2->SetPrototype(o3));
+ CHECK_EQ(0, o0->Get(v8_str("x"))->Int32Value());
+ CHECK_EQ(1, o0->Get(v8_str("y"))->Int32Value());
+ CHECK_EQ(2, o0->Get(v8_str("z"))->Int32Value());
+ CHECK_EQ(3, o0->Get(v8_str("u"))->Int32Value());
+
+ // Getting the prototype of o0 should get the first visible one
+ // which is o3. Therefore, z should not be defined on the prototype
+ // object.
+ Local<Value> proto = o0->Get(v8_str("__proto__"));
+ CHECK(proto->IsObject());
+ CHECK_EQ(v8::Handle<v8::Object>::Cast(proto), o3);
+
+ // However, Object::GetPrototype ignores hidden prototype.
+ Local<Value> proto0 = o0->GetPrototype();
+ CHECK(proto0->IsObject());
+ CHECK_EQ(v8::Handle<v8::Object>::Cast(proto0), o1);
+
+ Local<Value> proto1 = o1->GetPrototype();
+ CHECK(proto1->IsObject());
+ CHECK_EQ(v8::Handle<v8::Object>::Cast(proto1), o2);
+
+ Local<Value> proto2 = o2->GetPrototype();
+ CHECK(proto2->IsObject());
+ CHECK_EQ(v8::Handle<v8::Object>::Cast(proto2), o3);
+}
+
+
+THREADED_TEST(SetPrototypeThrows) {
+ v8::HandleScope handle_scope;
+ LocalContext context;
+
+ Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New();
+
+ Local<v8::Object> o0 = t->GetFunction()->NewInstance();
+ Local<v8::Object> o1 = t->GetFunction()->NewInstance();
+
+ CHECK(o0->SetPrototype(o1));
+ // If setting the prototype leads to the cycle, SetPrototype should
+ // return false and keep VM in sane state.
+ v8::TryCatch try_catch;
+ CHECK(!o1->SetPrototype(o0));
+ CHECK(!try_catch.HasCaught());
+ ASSERT(!i::Top::has_pending_exception());
+
+ CHECK_EQ(42, CompileRun("function f() { return 42; }; f()")->Int32Value());
+}
+
+
THREADED_TEST(GetterSetterExceptions) {
v8::HandleScope handle_scope;
LocalContext context;
@@ -5078,7 +5452,7 @@ THREADED_TEST(CrossLazyLoad) {
static v8::Handle<Value> call_as_function(const v8::Arguments& args) {
- //ApiTestFuzzer::Fuzz();
+ ApiTestFuzzer::Fuzz();
if (args.IsConstructCall()) {
if (args[0]->IsInt32()) {
return v8_num(-args[0]->Int32Value());
@@ -5891,6 +6265,294 @@ THREADED_TEST(InterceptorCallICCachedFromGlobal) {
CHECK_EQ(239 * 10, value->Int32Value());
}
+static v8::Handle<Value> InterceptorCallICFastApi(Local<String> name,
+ const AccessorInfo& info) {
+ ApiTestFuzzer::Fuzz();
+ int* call_count = reinterpret_cast<int*>(v8::External::Unwrap(info.Data()));
+ ++(*call_count);
+ if ((*call_count) % 20 == 0) {
+ v8::internal::Heap::CollectAllGarbage(true);
+ }
+ return v8::Handle<Value>();
+}
+
+static v8::Handle<Value> FastApiCallback_TrivialSignature(
+ const v8::Arguments& args) {
+ ApiTestFuzzer::Fuzz();
+ CHECK_EQ(args.This(), args.Holder());
+ CHECK(args.Data()->Equals(v8_str("method_data")));
+ return v8::Integer::New(args[0]->Int32Value() + 1);
+}
+
+static v8::Handle<Value> FastApiCallback_SimpleSignature(
+ const v8::Arguments& args) {
+ ApiTestFuzzer::Fuzz();
+ CHECK_EQ(args.This()->GetPrototype(), args.Holder());
+ CHECK(args.Data()->Equals(v8_str("method_data")));
+ // Note, we're using HasRealNamedProperty instead of Has to avoid
+ // invoking the interceptor again.
+ CHECK(args.Holder()->HasRealNamedProperty(v8_str("foo")));
+ return v8::Integer::New(args[0]->Int32Value() + 1);
+}
+
+// Helper to maximize the odds of object moving.
+static void GenerateSomeGarbage() {
+ CompileRun(
+ "var garbage;"
+ "for (var i = 0; i < 1000; i++) {"
+ " garbage = [1/i, \"garbage\" + i, garbage, {foo: garbage}];"
+ "}"
+ "garbage = undefined;");
+}
+
+THREADED_TEST(InterceptorCallICFastApi_TrivialSignature) {
+ int interceptor_call_count = 0;
+ v8::HandleScope scope;
+ v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+ v8::Handle<v8::FunctionTemplate> method_templ =
+ v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
+ v8_str("method_data"),
+ v8::Handle<v8::Signature>());
+ v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
+ proto_templ->Set(v8_str("method"), method_templ);
+ v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
+ templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
+ NULL, NULL, NULL, NULL,
+ v8::External::Wrap(&interceptor_call_count));
+ LocalContext context;
+ v8::Handle<v8::Function> fun = fun_templ->GetFunction();
+ GenerateSomeGarbage();
+ context->Global()->Set(v8_str("o"), fun->NewInstance());
+ v8::Handle<Value> value = CompileRun(
+ "var result = 0;"
+ "for (var i = 0; i < 100; i++) {"
+ " result = o.method(41);"
+ "}");
+ CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
+ CHECK_EQ(100, interceptor_call_count);
+}
+
+THREADED_TEST(InterceptorCallICFastApi_SimpleSignature) {
+ int interceptor_call_count = 0;
+ v8::HandleScope scope;
+ v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+ v8::Handle<v8::FunctionTemplate> method_templ =
+ v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
+ v8_str("method_data"),
+ v8::Signature::New(fun_templ));
+ v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
+ proto_templ->Set(v8_str("method"), method_templ);
+ v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
+ templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
+ NULL, NULL, NULL, NULL,
+ v8::External::Wrap(&interceptor_call_count));
+ LocalContext context;
+ v8::Handle<v8::Function> fun = fun_templ->GetFunction();
+ GenerateSomeGarbage();
+ context->Global()->Set(v8_str("o"), fun->NewInstance());
+ v8::Handle<Value> value = CompileRun(
+ "o.foo = 17;"
+ "var receiver = {};"
+ "receiver.__proto__ = o;"
+ "var result = 0;"
+ "for (var i = 0; i < 100; i++) {"
+ " result = receiver.method(41);"
+ "}");
+ CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
+ CHECK_EQ(100, interceptor_call_count);
+}
+
+THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss1) {
+ int interceptor_call_count = 0;
+ v8::HandleScope scope;
+ v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+ v8::Handle<v8::FunctionTemplate> method_templ =
+ v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
+ v8_str("method_data"),
+ v8::Signature::New(fun_templ));
+ v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
+ proto_templ->Set(v8_str("method"), method_templ);
+ v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
+ templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
+ NULL, NULL, NULL, NULL,
+ v8::External::Wrap(&interceptor_call_count));
+ LocalContext context;
+ v8::Handle<v8::Function> fun = fun_templ->GetFunction();
+ GenerateSomeGarbage();
+ context->Global()->Set(v8_str("o"), fun->NewInstance());
+ v8::Handle<Value> value = CompileRun(
+ "o.foo = 17;"
+ "var receiver = {};"
+ "receiver.__proto__ = o;"
+ "var result = 0;"
+ "var saved_result = 0;"
+ "for (var i = 0; i < 100; i++) {"
+ " result = receiver.method(41);"
+ " if (i == 50) {"
+ " saved_result = result;"
+ " receiver = {method: function(x) { return x - 1 }};"
+ " }"
+ "}");
+ CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
+ CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
+ CHECK_GE(interceptor_call_count, 50);
+}
+
+THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_Miss2) {
+ int interceptor_call_count = 0;
+ v8::HandleScope scope;
+ v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+ v8::Handle<v8::FunctionTemplate> method_templ =
+ v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
+ v8_str("method_data"),
+ v8::Signature::New(fun_templ));
+ v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
+ proto_templ->Set(v8_str("method"), method_templ);
+ v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
+ templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
+ NULL, NULL, NULL, NULL,
+ v8::External::Wrap(&interceptor_call_count));
+ LocalContext context;
+ v8::Handle<v8::Function> fun = fun_templ->GetFunction();
+ GenerateSomeGarbage();
+ context->Global()->Set(v8_str("o"), fun->NewInstance());
+ v8::Handle<Value> value = CompileRun(
+ "o.foo = 17;"
+ "var receiver = {};"
+ "receiver.__proto__ = o;"
+ "var result = 0;"
+ "var saved_result = 0;"
+ "for (var i = 0; i < 100; i++) {"
+ " result = receiver.method(41);"
+ " if (i == 50) {"
+ " saved_result = result;"
+ " o.method = function(x) { return x - 1 };"
+ " }"
+ "}");
+ CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
+ CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
+ CHECK_GE(interceptor_call_count, 50);
+}
+
+THREADED_TEST(InterceptorCallICFastApi_SimpleSignature_TypeError) {
+ int interceptor_call_count = 0;
+ v8::HandleScope scope;
+ v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+ v8::Handle<v8::FunctionTemplate> method_templ =
+ v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
+ v8_str("method_data"),
+ v8::Signature::New(fun_templ));
+ v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
+ proto_templ->Set(v8_str("method"), method_templ);
+ v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
+ templ->SetNamedPropertyHandler(InterceptorCallICFastApi,
+ NULL, NULL, NULL, NULL,
+ v8::External::Wrap(&interceptor_call_count));
+ LocalContext context;
+ v8::Handle<v8::Function> fun = fun_templ->GetFunction();
+ GenerateSomeGarbage();
+ context->Global()->Set(v8_str("o"), fun->NewInstance());
+ v8::TryCatch try_catch;
+ v8::Handle<Value> value = CompileRun(
+ "o.foo = 17;"
+ "var receiver = {};"
+ "receiver.__proto__ = o;"
+ "var result = 0;"
+ "var saved_result = 0;"
+ "for (var i = 0; i < 100; i++) {"
+ " result = receiver.method(41);"
+ " if (i == 50) {"
+ " saved_result = result;"
+ " receiver = {method: receiver.method};"
+ " }"
+ "}");
+ CHECK(try_catch.HasCaught());
+ CHECK_EQ(v8_str("TypeError: Illegal invocation"),
+ try_catch.Exception()->ToString());
+ CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
+ CHECK_GE(interceptor_call_count, 50);
+}
+
+THREADED_TEST(CallICFastApi_TrivialSignature) {
+ v8::HandleScope scope;
+ v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+ v8::Handle<v8::FunctionTemplate> method_templ =
+ v8::FunctionTemplate::New(FastApiCallback_TrivialSignature,
+ v8_str("method_data"),
+ v8::Handle<v8::Signature>());
+ v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
+ proto_templ->Set(v8_str("method"), method_templ);
+ v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
+ LocalContext context;
+ v8::Handle<v8::Function> fun = fun_templ->GetFunction();
+ GenerateSomeGarbage();
+ context->Global()->Set(v8_str("o"), fun->NewInstance());
+ v8::Handle<Value> value = CompileRun(
+ "var result = 0;"
+ "for (var i = 0; i < 100; i++) {"
+ " result = o.method(41);"
+ "}");
+
+ CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
+}
+
+THREADED_TEST(CallICFastApi_SimpleSignature) {
+ v8::HandleScope scope;
+ v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+ v8::Handle<v8::FunctionTemplate> method_templ =
+ v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
+ v8_str("method_data"),
+ v8::Signature::New(fun_templ));
+ v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
+ proto_templ->Set(v8_str("method"), method_templ);
+ v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
+ LocalContext context;
+ v8::Handle<v8::Function> fun = fun_templ->GetFunction();
+ GenerateSomeGarbage();
+ context->Global()->Set(v8_str("o"), fun->NewInstance());
+ v8::Handle<Value> value = CompileRun(
+ "o.foo = 17;"
+ "var receiver = {};"
+ "receiver.__proto__ = o;"
+ "var result = 0;"
+ "for (var i = 0; i < 100; i++) {"
+ " result = receiver.method(41);"
+ "}");
+
+ CHECK_EQ(42, context->Global()->Get(v8_str("result"))->Int32Value());
+}
+
+THREADED_TEST(CallICFastApi_SimpleSignature_Miss) {
+ v8::HandleScope scope;
+ v8::Handle<v8::FunctionTemplate> fun_templ = v8::FunctionTemplate::New();
+ v8::Handle<v8::FunctionTemplate> method_templ =
+ v8::FunctionTemplate::New(FastApiCallback_SimpleSignature,
+ v8_str("method_data"),
+ v8::Signature::New(fun_templ));
+ v8::Handle<v8::ObjectTemplate> proto_templ = fun_templ->PrototypeTemplate();
+ proto_templ->Set(v8_str("method"), method_templ);
+ v8::Handle<v8::ObjectTemplate> templ = fun_templ->InstanceTemplate();
+ LocalContext context;
+ v8::Handle<v8::Function> fun = fun_templ->GetFunction();
+ GenerateSomeGarbage();
+ context->Global()->Set(v8_str("o"), fun->NewInstance());
+ v8::Handle<Value> value = CompileRun(
+ "o.foo = 17;"
+ "var receiver = {};"
+ "receiver.__proto__ = o;"
+ "var result = 0;"
+ "var saved_result = 0;"
+ "for (var i = 0; i < 100; i++) {"
+ " result = receiver.method(41);"
+ " if (i == 50) {"
+ " saved_result = result;"
+ " receiver = {method: function(x) { return x - 1 }};"
+ " }"
+ "}");
+ CHECK_EQ(40, context->Global()->Get(v8_str("result"))->Int32Value());
+ CHECK_EQ(42, context->Global()->Get(v8_str("saved_result"))->Int32Value());
+}
+
static int interceptor_call_count = 0;
@@ -8927,3 +9589,138 @@ TEST(Regress528) {
other_context.Dispose();
}
+
+
+THREADED_TEST(ScriptOrigin) {
+ v8::HandleScope scope;
+ LocalContext env;
+ v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
+ v8::Handle<v8::String> script = v8::String::New(
+ "function f() {}\n\nfunction g() {}");
+ v8::Script::Compile(script, &origin)->Run();
+ v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
+ env->Global()->Get(v8::String::New("f")));
+ v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
+ env->Global()->Get(v8::String::New("g")));
+
+ v8::ScriptOrigin script_origin_f = f->GetScriptOrigin();
+ CHECK_EQ("test", *v8::String::AsciiValue(script_origin_f.ResourceName()));
+ CHECK_EQ(0, script_origin_f.ResourceLineOffset()->Int32Value());
+
+ v8::ScriptOrigin script_origin_g = g->GetScriptOrigin();
+ CHECK_EQ("test", *v8::String::AsciiValue(script_origin_g.ResourceName()));
+ CHECK_EQ(0, script_origin_g.ResourceLineOffset()->Int32Value());
+}
+
+
+THREADED_TEST(ScriptLineNumber) {
+ v8::HandleScope scope;
+ LocalContext env;
+ v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
+ v8::Handle<v8::String> script = v8::String::New(
+ "function f() {}\n\nfunction g() {}");
+ v8::Script::Compile(script, &origin)->Run();
+ v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
+ env->Global()->Get(v8::String::New("f")));
+ v8::Local<v8::Function> g = v8::Local<v8::Function>::Cast(
+ env->Global()->Get(v8::String::New("g")));
+ CHECK_EQ(0, f->GetScriptLineNumber());
+ CHECK_EQ(2, g->GetScriptLineNumber());
+}
+
+
+static v8::Handle<Value> GetterWhichReturns42(Local<String> name,
+ const AccessorInfo& info) {
+ return v8_num(42);
+}
+
+
+static void SetterWhichSetsYOnThisTo23(Local<String> name,
+ Local<Value> value,
+ const AccessorInfo& info) {
+ info.This()->Set(v8_str("y"), v8_num(23));
+}
+
+
+THREADED_TEST(SetterOnConstructorPrototype) {
+ v8::HandleScope scope;
+ Local<ObjectTemplate> templ = ObjectTemplate::New();
+ templ->SetAccessor(v8_str("x"),
+ GetterWhichReturns42,
+ SetterWhichSetsYOnThisTo23);
+ LocalContext context;
+ context->Global()->Set(v8_str("P"), templ->NewInstance());
+ CompileRun("function C1() {"
+ " this.x = 23;"
+ "};"
+ "C1.prototype = P;"
+ "function C2() {"
+ " this.x = 23"
+ "};"
+ "C2.prototype = { };"
+ "C2.prototype.__proto__ = P;");
+
+ v8::Local<v8::Script> script;
+ script = v8::Script::Compile(v8_str("new C1();"));
+ for (int i = 0; i < 10; i++) {
+ v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
+ CHECK_EQ(42, c1->Get(v8_str("x"))->Int32Value());
+ CHECK_EQ(23, c1->Get(v8_str("y"))->Int32Value());
+ }
+
+ script = v8::Script::Compile(v8_str("new C2();"));
+ for (int i = 0; i < 10; i++) {
+ v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
+ CHECK_EQ(42, c2->Get(v8_str("x"))->Int32Value());
+ CHECK_EQ(23, c2->Get(v8_str("y"))->Int32Value());
+ }
+}
+
+
+static v8::Handle<Value> NamedPropertyGetterWhichReturns42(
+ Local<String> name, const AccessorInfo& info) {
+ return v8_num(42);
+}
+
+
+static v8::Handle<Value> NamedPropertySetterWhichSetsYOnThisTo23(
+ Local<String> name, Local<Value> value, const AccessorInfo& info) {
+ if (name->Equals(v8_str("x"))) {
+ info.This()->Set(v8_str("y"), v8_num(23));
+ }
+ return v8::Handle<Value>();
+}
+
+
+THREADED_TEST(InterceptorOnConstructorPrototype) {
+ v8::HandleScope scope;
+ Local<ObjectTemplate> templ = ObjectTemplate::New();
+ templ->SetNamedPropertyHandler(NamedPropertyGetterWhichReturns42,
+ NamedPropertySetterWhichSetsYOnThisTo23);
+ LocalContext context;
+ context->Global()->Set(v8_str("P"), templ->NewInstance());
+ CompileRun("function C1() {"
+ " this.x = 23;"
+ "};"
+ "C1.prototype = P;"
+ "function C2() {"
+ " this.x = 23"
+ "};"
+ "C2.prototype = { };"
+ "C2.prototype.__proto__ = P;");
+
+ v8::Local<v8::Script> script;
+ script = v8::Script::Compile(v8_str("new C1();"));
+ for (int i = 0; i < 10; i++) {
+ v8::Handle<v8::Object> c1 = v8::Handle<v8::Object>::Cast(script->Run());
+ CHECK_EQ(23, c1->Get(v8_str("x"))->Int32Value());
+ CHECK_EQ(42, c1->Get(v8_str("y"))->Int32Value());
+ }
+
+ script = v8::Script::Compile(v8_str("new C2();"));
+ for (int i = 0; i < 10; i++) {
+ v8::Handle<v8::Object> c2 = v8::Handle<v8::Object>::Cast(script->Run());
+ CHECK_EQ(23, c2->Get(v8_str("x"))->Int32Value());
+ CHECK_EQ(42, c2->Get(v8_str("y"))->Int32Value());
+ }
+}
diff --git a/test/cctest/test-assembler-mips.cc b/test/cctest/test-assembler-mips.cc
new file mode 100644
index 00000000..ab011a73
--- /dev/null
+++ b/test/cctest/test-assembler-mips.cc
@@ -0,0 +1,257 @@
+// Copyright 2010 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.
+
+#include "v8.h"
+
+#include "disassembler.h"
+#include "factory.h"
+#include "macro-assembler.h"
+#include "mips/macro-assembler-mips.h"
+#include "mips/simulator-mips.h"
+
+#include "cctest.h"
+
+using namespace v8::internal;
+
+
+// Define these function prototypes to match JSEntryFunction in execution.cc.
+typedef Object* (*F1)(int x, int p1, int p2, int p3, int p4);
+typedef Object* (*F2)(int x, int y, int p2, int p3, int p4);
+typedef Object* (*F3)(void* p, int p1, int p2, int p3, int p4);
+
+
+static v8::Persistent<v8::Context> env;
+
+
+// The test framework does not accept flags on the command line, so we set them.
+static void InitializeVM() {
+ // Disable compilation of natives by specifying an empty natives file.
+ FLAG_natives_file = "";
+
+ // Enable generation of comments.
+ FLAG_debug_code = true;
+
+ if (env.IsEmpty()) {
+ env = v8::Context::New();
+ }
+}
+
+
+#define __ assm.
+
+TEST(MIPS0) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ MacroAssembler assm(NULL, 0);
+
+ // Addition.
+ __ addu(v0, a0, a1);
+ __ jr(ra);
+ __ nop();
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
+ CHECK(code->IsCode());
+#ifdef DEBUG
+ Code::cast(code)->Print();
+#endif
+ F2 f = FUNCTION_CAST<F2>(Code::cast(code)->entry());
+ int res = reinterpret_cast<int>(CALL_GENERATED_CODE(f, 0xab0, 0xc, 0, 0, 0));
+ ::printf("f() = %d\n", res);
+ CHECK_EQ(0xabc, res);
+}
+
+
+TEST(MIPS1) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ MacroAssembler assm(NULL, 0);
+ Label L, C;
+
+ __ mov(a1, a0);
+ __ li(v0, 0);
+ __ b(&C);
+ __ nop();
+
+ __ bind(&L);
+ __ add(v0, v0, a1);
+ __ addiu(a1, a1, -1);
+
+ __ bind(&C);
+ __ xori(v1, a1, 0);
+ __ Branch(ne, &L, v1, Operand(0));
+ __ nop();
+
+ __ jr(ra);
+ __ nop();
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
+ CHECK(code->IsCode());
+#ifdef DEBUG
+ Code::cast(code)->Print();
+#endif
+ F1 f = FUNCTION_CAST<F1>(Code::cast(code)->entry());
+ int res = reinterpret_cast<int>(CALL_GENERATED_CODE(f, 50, 0, 0, 0, 0));
+ ::printf("f() = %d\n", res);
+ CHECK_EQ(1275, res);
+}
+
+
+TEST(MIPS2) {
+ InitializeVM();
+ v8::HandleScope scope;
+
+ MacroAssembler assm(NULL, 0);
+
+ Label exit, error;
+
+ // ----- Test all instructions.
+
+ // Test lui, ori, and addiu, used in the li pseudo-instruction.
+ // This way we can then safely load registers with chosen values.
+
+ __ ori(t0, zero_reg, 0);
+ __ lui(t0, 0x1234);
+ __ ori(t0, t0, 0);
+ __ ori(t0, t0, 0x0f0f);
+ __ ori(t0, t0, 0xf0f0);
+ __ addiu(t1, t0, 1);
+ __ addiu(t2, t1, -0x10);
+
+ // Load values in temporary registers.
+ __ li(t0, 0x00000004);
+ __ li(t1, 0x00001234);
+ __ li(t2, 0x12345678);
+ __ li(t3, 0x7fffffff);
+ __ li(t4, 0xfffffffc);
+ __ li(t5, 0xffffedcc);
+ __ li(t6, 0xedcba988);
+ __ li(t7, 0x80000000);
+
+ // SPECIAL class.
+ __ srl(v0, t2, 8); // 0x00123456
+ __ sll(v0, v0, 11); // 0x91a2b000
+ __ sra(v0, v0, 3); // 0xf2345600
+ __ srav(v0, v0, t0); // 0xff234560
+ __ sllv(v0, v0, t0); // 0xf2345600
+ __ srlv(v0, v0, t0); // 0x0f234560
+ __ Branch(ne, &error, v0, Operand(0x0f234560));
+ __ nop();
+
+ __ add(v0, t0, t1); // 0x00001238
+ __ sub(v0, v0, t0); // 0x00001234
+ __ Branch(ne, &error, v0, Operand(0x00001234));
+ __ nop();
+ __ addu(v1, t3, t0);
+ __ Branch(ne, &error, v1, Operand(0x80000003));
+ __ nop();
+ __ subu(v1, t7, t0); // 0x7ffffffc
+ __ Branch(ne, &error, v1, Operand(0x7ffffffc));
+ __ nop();
+
+ __ and_(v0, t1, t2); // 0x00001230
+ __ or_(v0, v0, t1); // 0x00001234
+ __ xor_(v0, v0, t2); // 0x1234444c
+ __ nor(v0, v0, t2); // 0xedcba987
+ __ Branch(ne, &error, v0, Operand(0xedcba983));
+ __ nop();
+
+ __ slt(v0, t7, t3);
+ __ Branch(ne, &error, v0, Operand(0x1));
+ __ nop();
+ __ sltu(v0, t7, t3);
+ __ Branch(ne, &error, v0, Operand(0x0));
+ __ nop();
+ // End of SPECIAL class.
+
+ __ addi(v0, zero_reg, 0x7421); // 0x00007421
+ __ addi(v0, v0, -0x1); // 0x00007420
+ __ addiu(v0, v0, -0x20); // 0x00007400
+ __ Branch(ne, &error, v0, Operand(0x00007400));
+ __ nop();
+ __ addiu(v1, t3, 0x1); // 0x80000000
+ __ Branch(ne, &error, v1, Operand(0x80000000));
+ __ nop();
+
+ __ slti(v0, t1, 0x00002000); // 0x1
+ __ slti(v0, v0, 0xffff8000); // 0x0
+ __ Branch(ne, &error, v0, Operand(0x0));
+ __ nop();
+ __ sltiu(v0, t1, 0x00002000); // 0x1
+ __ sltiu(v0, v0, 0x00008000); // 0x1
+ __ Branch(ne, &error, v0, Operand(0x1));
+ __ nop();
+
+ __ andi(v0, t1, 0xf0f0); // 0x00001030
+ __ ori(v0, v0, 0x8a00); // 0x00009a30
+ __ xori(v0, v0, 0x83cc); // 0x000019fc
+ __ Branch(ne, &error, v0, Operand(0x000019fc));
+ __ nop();
+ __ lui(v1, 0x8123); // 0x81230000
+ __ Branch(ne, &error, v1, Operand(0x81230000));
+ __ nop();
+
+ // Everything was correctly executed. Load the expected result.
+ __ li(v0, 0x31415926);
+ __ b(&exit);
+ __ nop();
+
+ __ bind(&error);
+ // Got an error. Return a wrong result.
+
+ __ bind(&exit);
+ __ jr(ra);
+ __ nop();
+
+ CodeDesc desc;
+ assm.GetCode(&desc);
+ Object* code = Heap::CreateCode(desc,
+ NULL,
+ Code::ComputeFlags(Code::STUB),
+ Handle<Object>(Heap::undefined_value()));
+ CHECK(code->IsCode());
+#ifdef DEBUG
+ Code::cast(code)->Print();
+#endif
+ F2 f = FUNCTION_CAST<F2>(Code::cast(code)->entry());
+ int res = reinterpret_cast<int>(CALL_GENERATED_CODE(f, 0xab0, 0xc, 0, 0, 0));
+ ::printf("f() = %d\n", res);
+ CHECK_EQ(0x31415926, res);
+}
+
+#undef __
diff --git a/test/cctest/test-compiler.cc b/test/cctest/test-compiler.cc
index b5f12b9c..471753c0 100644
--- a/test/cctest/test-compiler.cc
+++ b/test/cctest/test-compiler.cc
@@ -120,6 +120,7 @@ static Handle<JSFunction> Compile(const char* source) {
0,
NULL,
NULL,
+ Handle<String>::null(),
NOT_NATIVES_CODE);
return Factory::NewFunctionFromBoilerplate(boilerplate,
Top::global_context());
@@ -322,3 +323,27 @@ TEST(Regression236) {
CHECK_EQ(-1, GetScriptLineNumber(script, 100));
CHECK_EQ(-1, GetScriptLineNumber(script, -1));
}
+
+
+TEST(GetScriptLineNumber) {
+ LocalContext env;
+ v8::HandleScope scope;
+ v8::ScriptOrigin origin = v8::ScriptOrigin(v8::String::New("test"));
+ const char function_f[] = "function f() {}";
+ const int max_rows = 1000;
+ const int buffer_size = max_rows + sizeof(function_f);
+ ScopedVector<char> buffer(buffer_size);
+ memset(buffer.start(), '\n', buffer_size - 1);
+ buffer[buffer_size - 1] = '\0';
+
+ for (int i = 0; i < max_rows; ++i) {
+ if (i > 0)
+ buffer[i - 1] = '\n';
+ memcpy(&buffer[i], function_f, sizeof(function_f) - 1);
+ v8::Handle<v8::String> script_body = v8::String::New(buffer.start());
+ v8::Script::Compile(script_body, &origin)->Run();
+ v8::Local<v8::Function> f = v8::Local<v8::Function>::Cast(
+ env->Global()->Get(v8::String::New("f")));
+ CHECK_EQ(i, f->GetScriptLineNumber());
+ }
+}
diff --git a/test/cctest/test-debug.cc b/test/cctest/test-debug.cc
index 92e18e06..b7c39b22 100644
--- a/test/cctest/test-debug.cc
+++ b/test/cctest/test-debug.cc
@@ -550,6 +550,15 @@ const char* frame_script_data_source =
v8::Local<v8::Function> frame_script_data;
+// Source for The JavaScript function which picks out the script data from
+// AfterCompile event
+const char* compiled_script_data_source =
+ "function compiled_script_data(event_data) {"
+ " return event_data.script().data();"
+ "}";
+v8::Local<v8::Function> compiled_script_data;
+
+
// Source for The JavaScript function which returns the number of frames.
static const char* frame_count_source =
"function frame_count(exec_state) {"
@@ -647,6 +656,19 @@ static void DebugEventBreakPointHitCount(v8::DebugEvent event,
script_data->WriteAscii(last_script_data_hit);
}
}
+ } else if (event == v8::AfterCompile && !compiled_script_data.IsEmpty()) {
+ const int argc = 1;
+ v8::Handle<v8::Value> argv[argc] = { event_data };
+ v8::Handle<v8::Value> result = compiled_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);
+ }
}
}
@@ -3884,6 +3906,11 @@ bool IsEvaluateResponseMessage(char* message) {
}
+static int StringToInt(const char* s) {
+ return atoi(s); // NOLINT
+}
+
+
// We match parts of the message to get evaluate result int value.
int GetEvaluateIntResult(char *message) {
const char* value = "\"value\":";
@@ -3892,7 +3919,7 @@ int GetEvaluateIntResult(char *message) {
return -1;
}
int res = -1;
- res = atoi(pos + strlen(value));
+ res = StringToInt(pos + strlen(value));
return res;
}
@@ -3905,7 +3932,7 @@ int GetBreakpointIdFromBreakEventMessage(char *message) {
return -1;
}
int res = -1;
- res = atoi(pos + strlen(breakpoints));
+ res = StringToInt(pos + strlen(breakpoints));
return res;
}
@@ -3918,11 +3945,7 @@ int GetTotalFramesInt(char *message) {
return -1;
}
pos += strlen(prefix);
- char* pos_end = pos;
- int res = static_cast<int>(strtol(pos, &pos_end, 10));
- if (pos_end == pos) {
- return -1;
- }
+ int res = StringToInt(pos);
return res;
}
@@ -5231,6 +5254,9 @@ TEST(ScriptNameAndData) {
frame_script_data = CompileFunction(&env,
frame_script_data_source,
"frame_script_data");
+ compiled_script_data = CompileFunction(&env,
+ compiled_script_data_source,
+ "compiled_script_data");
v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
v8::Undefined());
@@ -5277,6 +5303,16 @@ TEST(ScriptNameAndData) {
CHECK_EQ(3, break_point_hit_count);
CHECK_EQ("new name", last_script_name_hit);
CHECK_EQ("abc 123", last_script_data_hit);
+
+ v8::Handle<v8::Script> script3 =
+ v8::Script::Compile(script, &origin2, NULL,
+ v8::String::New("in compile"));
+ CHECK_EQ("in compile", last_script_data_hit);
+ script3->Run();
+ f = v8::Local<v8::Function>::Cast(env->Global()->Get(v8::String::New("f")));
+ f->Call(env->Global(), 0, NULL);
+ CHECK_EQ(4, break_point_hit_count);
+ CHECK_EQ("in compile", last_script_data_hit);
}
diff --git a/test/cctest/test-log.cc b/test/cctest/test-log.cc
index eca2c2b6..9853af32 100644
--- a/test/cctest/test-log.cc
+++ b/test/cctest/test-log.cc
@@ -170,21 +170,110 @@ static void SigProfSignalHandler(int signal, siginfo_t* info, void* context) {
#endif // __linux__
-static int CheckThatProfilerWorks(int log_pos) {
- Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU);
+namespace {
+
+class ScopedLoggerInitializer {
+ public:
+ explicit ScopedLoggerInitializer(bool log, bool prof_lazy)
+ : saved_log_(i::FLAG_log),
+ saved_prof_lazy_(i::FLAG_prof_lazy),
+ saved_prof_(i::FLAG_prof),
+ saved_prof_auto_(i::FLAG_prof_auto),
+ trick_to_run_init_flags_(init_flags_(log, prof_lazy)),
+ need_to_set_up_logger_(i::V8::IsRunning()),
+ scope_(),
+ env_(v8::Context::New()) {
+ if (need_to_set_up_logger_) Logger::Setup();
+ env_->Enter();
+ }
+
+ ~ScopedLoggerInitializer() {
+ env_->Exit();
+ Logger::TearDown();
+ i::FLAG_prof_lazy = saved_prof_lazy_;
+ i::FLAG_prof = saved_prof_;
+ i::FLAG_prof_auto = saved_prof_auto_;
+ i::FLAG_log = saved_log_;
+ }
+
+ v8::Handle<v8::Context>& env() { return env_; }
+
+ private:
+ static bool init_flags_(bool log, bool prof_lazy) {
+ i::FLAG_log = log;
+ i::FLAG_prof = true;
+ i::FLAG_prof_lazy = prof_lazy;
+ i::FLAG_prof_auto = false;
+ i::FLAG_logfile = "*";
+ return prof_lazy;
+ }
+
+ const bool saved_log_;
+ const bool saved_prof_lazy_;
+ const bool saved_prof_;
+ const bool saved_prof_auto_;
+ const bool trick_to_run_init_flags_;
+ const bool need_to_set_up_logger_;
+ v8::HandleScope scope_;
+ v8::Handle<v8::Context> env_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedLoggerInitializer);
+};
+
+
+class LogBufferMatcher {
+ public:
+ LogBufferMatcher() {
+ // Skip all initially logged stuff.
+ log_pos_ = GetLogLines(0, &buffer_);
+ }
+
+ int log_pos() { return log_pos_; }
+
+ int GetNextChunk() {
+ int chunk_size = GetLogLines(log_pos_, &buffer_);
+ CHECK_GT(buffer_.length(), chunk_size);
+ buffer_[chunk_size] = '\0';
+ log_pos_ += chunk_size;
+ return chunk_size;
+ }
+
+ const char* Find(const char* substr) {
+ return strstr(buffer_.start(), substr);
+ }
+
+ const char* Find(const i::Vector<char>& substr) {
+ return Find(substr.start());
+ }
+
+ bool IsInSequence(const char* s1, const char* s2) {
+ const char* s1_pos = Find(s1);
+ const char* s2_pos = Find(s2);
+ CHECK_NE(NULL, s1_pos);
+ CHECK_NE(NULL, s2_pos);
+ return s1_pos < s2_pos;
+ }
+
+ void PrintBuffer() {
+ puts(buffer_.start());
+ }
+
+ private:
+ EmbeddedVector<char, 102400> buffer_;
+ int log_pos_;
+};
+
+} // namespace
+
+
+static void CheckThatProfilerWorks(LogBufferMatcher* matcher) {
+ Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 0);
CHECK(LoggerTestHelper::IsSamplerActive());
// Verify that the current map of compiled functions has been logged.
- EmbeddedVector<char, 102400> buffer;
- int map_log_size = GetLogLines(log_pos, &buffer);
- printf("map_log_size: %d\n", map_log_size);
- CHECK_GT(map_log_size, 0);
- CHECK_GT(buffer.length(), map_log_size);
- log_pos += map_log_size;
- // Check buffer contents.
- buffer[map_log_size] = '\0';
+ CHECK_GT(matcher->GetNextChunk(), 0);
const char* code_creation = "\ncode-creation,"; // eq. to /^code-creation,/
- CHECK_NE(NULL, strstr(buffer.start(), code_creation));
+ CHECK_NE(NULL, matcher->Find(code_creation));
#ifdef __linux__
// Intercept SIGPROF handler to make sure that the test process
@@ -204,7 +293,7 @@ static int CheckThatProfilerWorks(int log_pos) {
i::OS::SNPrintF(script_src,
"function f%d(x) { return %d * x; }"
"for (var i = 0; i < 10000; ++i) { f%d(i); }",
- log_pos, log_pos, log_pos);
+ matcher->log_pos(), matcher->log_pos(), matcher->log_pos());
// Run code for 200 msecs to get some ticks.
const double end_time = i::OS::TimeCurrentMillis() + 200;
while (i::OS::TimeCurrentMillis() < end_time) {
@@ -213,7 +302,7 @@ static int CheckThatProfilerWorks(int log_pos) {
i::OS::Sleep(1);
}
- Logger::PauseProfiler(v8::PROFILER_MODULE_CPU);
+ Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 0);
CHECK(!LoggerTestHelper::IsSamplerActive());
// Wait 50 msecs to allow Profiler thread to process the last
@@ -221,68 +310,39 @@ static int CheckThatProfilerWorks(int log_pos) {
i::OS::Sleep(50);
// Now we must have compiler and tick records.
- int log_size = GetLogLines(log_pos, &buffer);
- printf("log_size: %d\n", log_size);
- CHECK_GT(log_size, 0);
- CHECK_GT(buffer.length(), log_size);
- log_pos += log_size;
- // Check buffer contents.
- buffer[log_size] = '\0';
- printf("%s", buffer.start());
+ CHECK_GT(matcher->GetNextChunk(), 0);
+ matcher->PrintBuffer();
+ CHECK_NE(NULL, matcher->Find(code_creation));
const char* tick = "\ntick,";
- CHECK_NE(NULL, strstr(buffer.start(), code_creation));
- const bool ticks_found = strstr(buffer.start(), tick) != NULL;
+ const bool ticks_found = matcher->Find(tick) != NULL;
CHECK_EQ(was_sigprof_received, ticks_found);
-
- return log_pos;
}
TEST(ProfLazyMode) {
- const bool saved_prof_lazy = i::FLAG_prof_lazy;
- const bool saved_prof = i::FLAG_prof;
- const bool saved_prof_auto = i::FLAG_prof_auto;
- i::FLAG_prof = true;
- i::FLAG_prof_lazy = true;
- i::FLAG_prof_auto = false;
- i::FLAG_logfile = "*";
-
- // If tests are being run manually, V8 will be already initialized
- // by the bottom test.
- const bool need_to_set_up_logger = i::V8::IsRunning();
- v8::HandleScope scope;
- v8::Handle<v8::Context> env = v8::Context::New();
- if (need_to_set_up_logger) Logger::Setup();
- env->Enter();
+ ScopedLoggerInitializer initialize_logger(false, true);
// No sampling should happen prior to resuming profiler.
CHECK(!LoggerTestHelper::IsSamplerActive());
- EmbeddedVector<char, 102400> buffer;
+ LogBufferMatcher matcher;
// Nothing must be logged until profiling is resumed.
- int log_pos = GetLogLines(0, &buffer);
- CHECK_EQ(0, log_pos);
+ CHECK_EQ(0, matcher.log_pos());
CompileAndRunScript("var a = (function(x) { return x + 1; })(10);");
// Nothing must be logged while profiling is suspended.
- CHECK_EQ(0, GetLogLines(log_pos, &buffer));
+ CHECK_EQ(0, matcher.GetNextChunk());
- log_pos = CheckThatProfilerWorks(log_pos);
+ CheckThatProfilerWorks(&matcher);
CompileAndRunScript("var a = (function(x) { return x + 1; })(10);");
// No new data beyond last retrieved position.
- CHECK_EQ(0, GetLogLines(log_pos, &buffer));
+ CHECK_EQ(0, matcher.GetNextChunk());
// Check that profiling can be resumed again.
- CheckThatProfilerWorks(log_pos);
-
- env->Exit();
- Logger::TearDown();
- i::FLAG_prof_lazy = saved_prof_lazy;
- i::FLAG_prof = saved_prof;
- i::FLAG_prof_auto = saved_prof_auto;
+ CheckThatProfilerWorks(&matcher);
}
@@ -480,25 +540,8 @@ static v8::Handle<v8::Value> ObjMethod1(const v8::Arguments& args) {
}
TEST(LogCallbacks) {
- const bool saved_prof_lazy = i::FLAG_prof_lazy;
- const bool saved_prof = i::FLAG_prof;
- const bool saved_prof_auto = i::FLAG_prof_auto;
- i::FLAG_prof = true;
- i::FLAG_prof_lazy = false;
- i::FLAG_prof_auto = false;
- i::FLAG_logfile = "*";
-
- // If tests are being run manually, V8 will be already initialized
- // by the bottom test.
- const bool need_to_set_up_logger = i::V8::IsRunning();
- v8::HandleScope scope;
- v8::Handle<v8::Context> env = v8::Context::New();
- if (need_to_set_up_logger) Logger::Setup();
- env->Enter();
-
- // Skip all initially logged stuff.
- EmbeddedVector<char, 102400> buffer;
- int log_pos = GetLogLines(0, &buffer);
+ ScopedLoggerInitializer initialize_logger(false, false);
+ LogBufferMatcher matcher;
v8::Persistent<v8::FunctionTemplate> obj =
v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New());
@@ -511,16 +554,14 @@ TEST(LogCallbacks) {
signature),
static_cast<v8::PropertyAttribute>(v8::DontDelete));
- env->Global()->Set(v8_str("Obj"), obj->GetFunction());
+ initialize_logger.env()->Global()->Set(v8_str("Obj"), obj->GetFunction());
CompileAndRunScript("Obj.prototype.method1.toString();");
i::Logger::LogCompiledFunctions();
- log_pos = GetLogLines(log_pos, &buffer);
- CHECK_GT(log_pos, 0);
- buffer[log_pos] = 0;
+ CHECK_GT(matcher.GetNextChunk(), 0);
const char* callback_rec = "code-creation,Callback,";
- char* pos = strstr(buffer.start(), callback_rec);
+ char* pos = const_cast<char*>(matcher.Find(callback_rec));
CHECK_NE(NULL, pos);
pos += strlen(callback_rec);
EmbeddedVector<char, 100> ref_data;
@@ -530,12 +571,6 @@ TEST(LogCallbacks) {
CHECK_EQ(ref_data.start(), pos);
obj.Dispose();
-
- env->Exit();
- Logger::TearDown();
- i::FLAG_prof_lazy = saved_prof_lazy;
- i::FLAG_prof = saved_prof;
- i::FLAG_prof_auto = saved_prof_auto;
}
@@ -555,25 +590,8 @@ static v8::Handle<v8::Value> Prop2Getter(v8::Local<v8::String> property,
}
TEST(LogAccessorCallbacks) {
- const bool saved_prof_lazy = i::FLAG_prof_lazy;
- const bool saved_prof = i::FLAG_prof;
- const bool saved_prof_auto = i::FLAG_prof_auto;
- i::FLAG_prof = true;
- i::FLAG_prof_lazy = false;
- i::FLAG_prof_auto = false;
- i::FLAG_logfile = "*";
-
- // If tests are being run manually, V8 will be already initialized
- // by the bottom test.
- const bool need_to_set_up_logger = i::V8::IsRunning();
- v8::HandleScope scope;
- v8::Handle<v8::Context> env = v8::Context::New();
- if (need_to_set_up_logger) Logger::Setup();
- env->Enter();
-
- // Skip all initially logged stuff.
- EmbeddedVector<char, 102400> buffer;
- int log_pos = GetLogLines(0, &buffer);
+ ScopedLoggerInitializer initialize_logger(false, false);
+ LogBufferMatcher matcher;
v8::Persistent<v8::FunctionTemplate> obj =
v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New());
@@ -583,34 +601,112 @@ TEST(LogAccessorCallbacks) {
inst->SetAccessor(v8::String::New("prop2"), Prop2Getter);
i::Logger::LogAccessorCallbacks();
- log_pos = GetLogLines(log_pos, &buffer);
- CHECK_GT(log_pos, 0);
- buffer[log_pos] = 0;
- printf("%s", buffer.start());
+ CHECK_GT(matcher.GetNextChunk(), 0);
+ matcher.PrintBuffer();
EmbeddedVector<char, 100> prop1_getter_record;
i::OS::SNPrintF(prop1_getter_record,
"code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop1\"",
Prop1Getter);
- CHECK_NE(NULL, strstr(buffer.start(), prop1_getter_record.start()));
+ CHECK_NE(NULL, matcher.Find(prop1_getter_record));
EmbeddedVector<char, 100> prop1_setter_record;
i::OS::SNPrintF(prop1_setter_record,
"code-creation,Callback,0x%" V8PRIxPTR ",1,\"set prop1\"",
Prop1Setter);
- CHECK_NE(NULL, strstr(buffer.start(), prop1_setter_record.start()));
+ CHECK_NE(NULL, matcher.Find(prop1_setter_record));
EmbeddedVector<char, 100> prop2_getter_record;
i::OS::SNPrintF(prop2_getter_record,
"code-creation,Callback,0x%" V8PRIxPTR ",1,\"get prop2\"",
Prop2Getter);
- CHECK_NE(NULL, strstr(buffer.start(), prop2_getter_record.start()));
+ CHECK_NE(NULL, matcher.Find(prop2_getter_record));
obj.Dispose();
+}
- env->Exit();
- Logger::TearDown();
- i::FLAG_prof_lazy = saved_prof_lazy;
- i::FLAG_prof = saved_prof;
- i::FLAG_prof_auto = saved_prof_auto;
+
+TEST(LogTags) {
+ ScopedLoggerInitializer initialize_logger(true, false);
+ LogBufferMatcher matcher;
+
+ const char* open_tag = "open-tag,";
+ const char* close_tag = "close-tag,";
+
+ // Check compatibility with the old style behavior.
+ CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+ Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 0);
+ CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+ Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 0);
+ CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+ CHECK_EQ(NULL, matcher.Find(open_tag));
+ CHECK_EQ(NULL, matcher.Find(close_tag));
+
+ const char* open_tag1 = "open-tag,1\n";
+ const char* close_tag1 = "close-tag,1\n";
+
+ // Check non-nested tag case.
+ CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+ Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1);
+ CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+ Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1);
+ CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+ CHECK_GT(matcher.GetNextChunk(), 0);
+ CHECK(matcher.IsInSequence(open_tag1, close_tag1));
+
+ const char* open_tag2 = "open-tag,2\n";
+ const char* close_tag2 = "close-tag,2\n";
+
+ // Check nested tags case.
+ CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+ Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1);
+ CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+ Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 2);
+ CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+ Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 2);
+ CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+ Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1);
+ CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+ CHECK_GT(matcher.GetNextChunk(), 0);
+ // open_tag1 < open_tag2 < close_tag2 < close_tag1
+ CHECK(matcher.IsInSequence(open_tag1, open_tag2));
+ CHECK(matcher.IsInSequence(open_tag2, close_tag2));
+ CHECK(matcher.IsInSequence(close_tag2, close_tag1));
+
+ // Check overlapped tags case.
+ CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+ Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1);
+ CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+ Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 2);
+ CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+ Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1);
+ CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+ Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 2);
+ CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+ CHECK_GT(matcher.GetNextChunk(), 0);
+ // open_tag1 < open_tag2 < close_tag1 < close_tag2
+ CHECK(matcher.IsInSequence(open_tag1, open_tag2));
+ CHECK(matcher.IsInSequence(open_tag2, close_tag1));
+ CHECK(matcher.IsInSequence(close_tag1, close_tag2));
+
+ const char* open_tag3 = "open-tag,3\n";
+ const char* close_tag3 = "close-tag,3\n";
+
+ // Check pausing overflow case.
+ CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+ Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 1);
+ CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+ Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 2);
+ CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+ Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 2);
+ CHECK_EQ(v8::PROFILER_MODULE_CPU, Logger::GetActiveProfilerModules());
+ Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 1);
+ CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+ Logger::PauseProfiler(v8::PROFILER_MODULE_CPU, 3);
+ CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+ Logger::ResumeProfiler(v8::PROFILER_MODULE_CPU, 3);
+ CHECK_EQ(v8::PROFILER_MODULE_NONE, Logger::GetActiveProfilerModules());
+ // Must be no tags, because logging must be disabled.
+ CHECK_EQ(NULL, matcher.Find(open_tag3));
+ CHECK_EQ(NULL, matcher.Find(close_tag3));
}
diff --git a/test/cctest/test-serialize.cc b/test/cctest/test-serialize.cc
index 18f69880..5335e9b6 100644
--- a/test/cctest/test-serialize.cc
+++ b/test/cctest/test-serialize.cc
@@ -131,7 +131,7 @@ TEST(ExternalReferenceEncoder) {
ExternalReference::address_of_real_stack_limit();
CHECK_EQ(make_code(UNCLASSIFIED, 5),
encoder.Encode(real_stack_limit_address.address()));
- CHECK_EQ(make_code(UNCLASSIFIED, 11),
+ CHECK_EQ(make_code(UNCLASSIFIED, 12),
encoder.Encode(ExternalReference::debug_break().address()));
CHECK_EQ(make_code(UNCLASSIFIED, 7),
encoder.Encode(ExternalReference::new_space_start().address()));
@@ -165,7 +165,7 @@ TEST(ExternalReferenceDecoder) {
CHECK_EQ(ExternalReference::address_of_real_stack_limit().address(),
decoder.Decode(make_code(UNCLASSIFIED, 5)));
CHECK_EQ(ExternalReference::debug_break().address(),
- decoder.Decode(make_code(UNCLASSIFIED, 11)));
+ decoder.Decode(make_code(UNCLASSIFIED, 12)));
CHECK_EQ(ExternalReference::new_space_start().address(),
decoder.Decode(make_code(UNCLASSIFIED, 7)));
}
diff --git a/test/cctest/test-utils.cc b/test/cctest/test-utils.cc
index 1d65e686..24b3c908 100644
--- a/test/cctest/test-utils.cc
+++ b/test/cctest/test-utils.cc
@@ -35,111 +35,6 @@
using namespace v8::internal;
-enum Mode {
- forward,
- backward_unsigned
-};
-
-
-static v8::internal::byte* Write(v8::internal::byte* p, Mode m, int x) {
- v8::internal::byte* q = NULL;
- switch (m) {
- case forward:
- q = EncodeInt(p, x);
- CHECK(q <= p + sizeof(x) + 1);
- break;
- case backward_unsigned:
- q = EncodeUnsignedIntBackward(p, x);
- CHECK(q >= p - sizeof(x) - 1);
- break;
- }
- return q;
-}
-
-
-static v8::internal::byte* Read(v8::internal::byte* p, Mode m, int x) {
- v8::internal::byte* q = NULL;
- int y;
- switch (m) {
- case forward:
- q = DecodeInt(p, &y);
- CHECK(q <= p + sizeof(y) + 1);
- break;
- case backward_unsigned: {
- unsigned int uy;
- q = DecodeUnsignedIntBackward(p, &uy);
- y = uy;
- CHECK(q >= p - sizeof(uy) - 1);
- break;
- }
- }
- CHECK(y == x);
- return q;
-}
-
-
-static v8::internal::byte* WriteMany(v8::internal::byte* p, Mode m, int x) {
- p = Write(p, m, x - 7);
- p = Write(p, m, x - 1);
- p = Write(p, m, x);
- p = Write(p, m, x + 1);
- p = Write(p, m, x + 2);
- p = Write(p, m, -x - 5);
- p = Write(p, m, -x - 1);
- p = Write(p, m, -x);
- p = Write(p, m, -x + 1);
- p = Write(p, m, -x + 3);
-
- return p;
-}
-
-
-static v8::internal::byte* ReadMany(v8::internal::byte* p, Mode m, int x) {
- p = Read(p, m, x - 7);
- p = Read(p, m, x - 1);
- p = Read(p, m, x);
- p = Read(p, m, x + 1);
- p = Read(p, m, x + 2);
- p = Read(p, m, -x - 5);
- p = Read(p, m, -x - 1);
- p = Read(p, m, -x);
- p = Read(p, m, -x + 1);
- p = Read(p, m, -x + 3);
-
- return p;
-}
-
-
-void ProcessValues(int* values, int n, Mode m) {
- v8::internal::byte buf[4 * KB]; // make this big enough
- v8::internal::byte* p0 = (m == forward ? buf : buf + ARRAY_SIZE(buf));
-
- v8::internal::byte* p = p0;
- for (int i = 0; i < n; i++) {
- p = WriteMany(p, m, values[i]);
- }
-
- v8::internal::byte* q = p0;
- for (int i = 0; i < n; i++) {
- q = ReadMany(q, m, values[i]);
- }
-
- CHECK(p == q);
-}
-
-
-TEST(Utils0) {
- int values[] = {
- 0, 1, 10, 16, 32, 64, 128, 256, 512, 1024, 1234, 5731,
- 10000, 100000, 1000000, 10000000, 100000000, 1000000000
- };
- const int n = ARRAY_SIZE(values);
-
- ProcessValues(values, n, forward);
- ProcessValues(values, n, backward_unsigned);
-}
-
-
TEST(Utils1) {
CHECK_EQ(-1000000, FastD2I(-1000000.0));
CHECK_EQ(-1, FastD2I(-1.0));
diff --git a/test/mjsunit/array-functions-prototype-misc.js b/test/mjsunit/array-functions-prototype-misc.js
new file mode 100644
index 00000000..0543c323
--- /dev/null
+++ b/test/mjsunit/array-functions-prototype-misc.js
@@ -0,0 +1,314 @@
+// Copyright 2008 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 splice, shift, unshift, slice and join on small
+ * and large arrays. Some of these methods are specified such that they
+ * should work on other objects too, so we test that too.
+ */
+
+var LARGE = 40000000;
+var VERYLARGE = 4000000000;
+
+// Nicer for firefox 1.5. Unless you uncomment the following two lines,
+// smjs will appear to hang on this file.
+//var LARGE = 40000;
+//var VERYLARGE = 40000;
+
+var fourhundredth = LARGE/400;
+
+function PseudoArray() {
+};
+
+for (var use_real_arrays = 0; use_real_arrays <= 1; use_real_arrays++) {
+ var poses = [0, 140, 20000, VERYLARGE];
+ var the_prototype;
+ var new_function;
+ var push_function;
+ var concat_function;
+ var slice_function;
+ var splice_function;
+ var splice_function_2;
+ var unshift_function;
+ var unshift_function_2;
+ var shift_function;
+ if (use_real_arrays) {
+ new_function = function(length) {
+ return new Array(length);
+ };
+ the_prototype = Array.prototype;
+ push_function = function(array, elt) {
+ return array.push(elt);
+ };
+ concat_function = function(array, other) {
+ return array.concat(other);
+ };
+ slice_function = function(array, start, len) {
+ return array.slice(start, len);
+ };
+ splice_function = function(array, start, len) {
+ return array.splice(start, len);
+ };
+ splice_function_2 = function(array, start, len, elt) {
+ return array.splice(start, len, elt);
+ };
+ unshift_function = function(array, elt) {
+ return array.unshift(elt);
+ };
+ unshift_function_2 = function(array, elt1, elt2) {
+ return array.unshift(elt1, elt2);
+ };
+ shift_function = function(array) {
+ return array.shift();
+ };
+ } else {
+ // Don't run largest size on non-arrays or we'll be here for ever.
+ poses.pop();
+ new_function = function(length) {
+ var obj = new PseudoArray();
+ obj.length = length;
+ return obj;
+ };
+ the_prototype = PseudoArray.prototype;
+ push_function = function(array, elt) {
+ array[array.length] = elt;
+ array.length++;
+ };
+ concat_function = function(array, other) {
+ return Array.prototype.concat.call(array, other);
+ };
+ slice_function = function(array, start, len) {
+ return Array.prototype.slice.call(array, start, len);
+ };
+ splice_function = function(array, start, len) {
+ return Array.prototype.splice.call(array, start, len);
+ };
+ splice_function_2 = function(array, start, len, elt) {
+ return Array.prototype.splice.call(array, start, len, elt);
+ };
+ unshift_function = function(array, elt) {
+ return Array.prototype.unshift.call(array, elt);
+ };
+ unshift_function_2 = function(array, elt1, elt2) {
+ return Array.prototype.unshift.call(array, elt1, elt2);
+ };
+ shift_function = function(array) {
+ return Array.prototype.shift.call(array);
+ };
+ }
+
+ for (var pos_pos = 0; pos_pos < poses.length; pos_pos++) {
+ var pos = poses[pos_pos];
+ if (pos > 100) {
+ var a = new_function(pos);
+ assertEquals(pos, a.length);
+ push_function(a, 'foo');
+ assertEquals(pos + 1, a.length);
+ var b = ['bar'];
+ // Delete a huge number of holes.
+ var c = splice_function(a, 10, pos - 20);
+ assertEquals(pos - 20, c.length);
+ assertEquals(21, a.length);
+ }
+
+ // Add a numeric property to the prototype of the array class. This
+ // allows us to test some borderline stuff relative to the standard.
+ the_prototype["" + (pos + 1)] = 'baz';
+
+ if (use_real_arrays) {
+ // It seems quite clear from ECMAScript spec 15.4.4.5. Just call Get on
+ // every integer in the range.
+ // IE, Safari get this right.
+ // FF, Opera get this wrong.
+ var a = ['zero', ,'two'];
+ if (pos == 0) {
+ assertEquals("zero,baz,two", a.join(","));
+ }
+
+ // Concat only applies to real arrays, unlike most of the other methods.
+ var a = new_function(pos);
+ push_function(a, "con");
+ assertEquals("con", a[pos]);
+ assertEquals(pos + 1, a.length);
+ var b = new_function(0);
+ push_function(b, "cat");
+ assertEquals("cat", b[0]);
+ var ab = concat_function(a, b);
+ assertEquals("con", ab[pos]);
+ assertEquals(pos + 2, ab.length);
+ assertEquals("cat", ab[pos + 1]);
+ var ba = concat_function(b, a);
+ assertEquals("con", ba[pos + 1]);
+ assertEquals(pos + 2, ba.length);
+ assertEquals("cat", ba[0]);
+
+ // Join with '' as separator.
+ var join = a.join('');
+ assertEquals("con", join);
+ join = b.join('');
+ assertEquals("cat", join);
+ join = ab.join('');
+ assertEquals("concat", join);
+ join = ba.join('');
+ assertEquals("catcon", join);
+
+ var sparse = [];
+ sparse[pos + 1000] = 'is ';
+ sparse[pos + 271828] = 'time ';
+ sparse[pos + 31415] = 'the ';
+ sparse[pos + 012260199] = 'all ';
+ sparse[-1] = 'foo';
+ sparse[pos + 22591927] = 'good ';
+ sparse[pos + 1618033] = 'for ';
+ sparse[pos + 91] = ': Now ';
+ sparse[pos + 86720199] = 'men.';
+ sparse.hest = 'fisk';
+
+ assertEquals("baz: Now is the time for all good men.", sparse.join(''));
+ }
+
+ a = new_function(pos);
+ push_function(a, 'zero');
+ push_function(a, void 0);
+ push_function(a, 'two');
+
+ // Splice works differently from join.
+ // IE, Safari get this wrong.
+ // FF, Opera get this right.
+ // 15.4.4.12 line 24 says the object itself has to have the property...
+ var zero = splice_function(a, pos, 1);
+ assertEquals("undefined", typeof(a[pos]));
+ assertEquals("two", a[pos+1], "pos1:" + pos);
+ assertEquals(pos + 2, a.length, "a length");
+ assertEquals(1, zero.length, "zero length");
+ assertEquals("zero", zero[0]);
+
+ // 15.4.4.12 line 41 says the object itself has to have the property...
+ a = new_function(pos);
+ push_function(a, 'zero');
+ push_function(a, void 0);
+ push_function(a, 'two');
+ var nothing = splice_function_2(a, pos, 0, 'minus1');
+ assertEquals("minus1", a[pos]);
+ assertEquals("zero", a[pos+1]);
+ assertEquals("undefined", typeof(a[pos+2]), "toot!");
+ assertEquals("two", a[pos+3], "pos3");
+ assertEquals(pos + 4, a.length);
+ assertEquals(1, zero.length);
+ assertEquals("zero", zero[0]);
+
+ // 15.4.4.12 line 10 says the object itself has to have the property...
+ a = new_function(pos);
+ push_function(a, 'zero');
+ push_function(a, void 0);
+ push_function(a, 'two');
+ var one = splice_function(a, pos + 1, 1);
+ assertEquals("", one.join(","));
+ assertEquals(pos + 2, a.length);
+ assertEquals("zero", a[pos]);
+ assertEquals("two", a[pos+1]);
+
+ // Set things back to the way they were.
+ the_prototype[pos + 1] = undefined;
+
+ // Unshift.
+ var a = new_function(pos);
+ push_function(a, "foo");
+ assertEquals("foo", a[pos]);
+ assertEquals(pos + 1, a.length);
+ unshift_function(a, "bar");
+ assertEquals("foo", a[pos+1]);
+ assertEquals(pos + 2, a.length);
+ assertEquals("bar", a[0]);
+ unshift_function_2(a, "baz", "boo");
+ assertEquals("foo", a[pos+3]);
+ assertEquals(pos + 4, a.length);
+ assertEquals("baz", a[0]);
+ assertEquals("boo", a[1]);
+ assertEquals("bar", a[2]);
+
+ // Shift.
+ var baz = shift_function(a);
+ assertEquals("baz", baz);
+ assertEquals("boo", a[0]);
+ assertEquals(pos + 3, a.length);
+ assertEquals("foo", a[pos + 2]);
+
+ // Slice.
+ var bar = slice_function(a, 1, 0); // don't throw an exception please.
+ bar = slice_function(a, 1, 2);
+ assertEquals("bar", bar[0]);
+ assertEquals(1, bar.length);
+ assertEquals("bar", a[1]);
+
+ }
+}
+
+// Lets see if performance is reasonable.
+
+var a = new Array(LARGE + 10);
+for (var i = 0; i < a.length; i += 1000) {
+ a[i] = i;
+}
+
+// Take something near the end of the array.
+for (var i = 0; i < 100; i++) {
+ var top = a.splice(LARGE, 5);
+ assertEquals(5, top.length);
+ assertEquals(LARGE, top[0]);
+ assertEquals("undefined", typeof(top[1]));
+ assertEquals(LARGE + 5, a.length);
+ a.splice(LARGE, 0, LARGE);
+ a.length = LARGE + 10;
+}
+
+var a = new Array(LARGE + 10);
+for (var i = 0; i < a.length; i += fourhundredth) {
+ a[i] = i;
+}
+
+// Take something near the middle of the array.
+for (var i = 0; i < 10; i++) {
+ var top = a.splice(LARGE >> 1, 5);
+ assertEquals(5, top.length);
+ assertEquals(LARGE >> 1, top[0]);
+ assertEquals("undefined", typeof(top[1]));
+ assertEquals(LARGE + 5, a.length);
+ a.splice(LARGE >> 1, 0, LARGE >> 1, void 0, void 0, void 0, void 0);
+}
+
+
+// Test http://b/issue?id=1202711
+arr = [0];
+arr.length = 2;
+Array.prototype[1] = 1;
+assertEquals(1, arr.pop());
+assertEquals(0, arr.pop());
+Array.prototype[1] = undefined;
+
+// Test http://code.google.com/p/chromium/issues/detail?id=21860
+Array.prototype.push.apply([], [1].splice(0, -(-1 % 5)));
diff --git a/test/mjsunit/array-shift.js b/test/mjsunit/array-shift.js
new file mode 100644
index 00000000..d985b31e
--- /dev/null
+++ b/test/mjsunit/array-shift.js
@@ -0,0 +1,71 @@
+// Copyright 2010 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.
+
+// Check that shifting array of holes keeps it as array of holes
+(function() {
+ var array = new Array(10);
+ array.shift();
+ assertFalse(0 in array);
+})();
+
+// Now check the case with array of holes and some elements on prototype.
+(function() {
+ var len = 9;
+ var array = new Array(len);
+ Array.prototype[3] = "@3";
+ Array.prototype[7] = "@7";
+
+ assertEquals(len, array.length);
+ for (var i = 0; i < array.length; i++) {
+ assertEquals(array[i], Array.prototype[i]);
+ }
+
+ array.shift();
+
+ assertEquals(len - 1, array.length);
+ // Note that shift copies values from prototype into the array.
+ assertEquals(array[2], Array.prototype[3]);
+ assertTrue(array.hasOwnProperty(2));
+
+ assertEquals(array[6], Array.prototype[7]);
+ assertTrue(array.hasOwnProperty(6));
+
+ // ... but keeps the rest as holes:
+ Array.prototype[5] = "@5";
+ assertEquals(array[5], Array.prototype[5]);
+ assertFalse(array.hasOwnProperty(5));
+
+ assertEquals(array[3], Array.prototype[3]);
+ assertFalse(array.hasOwnProperty(3));
+
+ assertEquals(array[7], Array.prototype[7]);
+ assertFalse(array.hasOwnProperty(7));
+
+ assertTrue(delete Array.prototype[3]);
+ assertTrue(delete Array.prototype[5]);
+ assertTrue(delete Array.prototype[7]);
+})();
diff --git a/test/mjsunit/array-slice.js b/test/mjsunit/array-slice.js
new file mode 100644
index 00000000..c993a077
--- /dev/null
+++ b/test/mjsunit/array-slice.js
@@ -0,0 +1,162 @@
+// Copyright 2010 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.
+
+// Check that slicing array of holes keeps it as array of holes
+(function() {
+ var array = new Array(10);
+ for (var i = 0; i < 7; i++) {
+ var sliced = array.slice();
+ assertEquals(array.length, sliced.length);
+ assertFalse(0 in sliced);
+ }
+})();
+
+
+// Check various forms of arguments omission.
+(function() {
+ var array = new Array(7);
+
+ for (var i = 0; i < 7; i++) {
+ assertEquals(array, array.slice());
+ assertEquals(array, array.slice(0));
+ assertEquals(array, array.slice(undefined));
+ assertEquals(array, array.slice("foobar"));
+ assertEquals(array, array.slice(undefined, undefined));
+ }
+})();
+
+
+// Check variants of negatives and positive indices.
+(function() {
+ var array = new Array(7);
+
+ for (var i = 0; i < 7; i++) {
+ assertEquals(7, array.slice(-100).length);
+ assertEquals(3, array.slice(-3).length);
+ assertEquals(3, array.slice(4).length);
+ assertEquals(1, array.slice(6).length);
+ assertEquals(0, array.slice(7).length);
+ assertEquals(0, array.slice(8).length);
+ assertEquals(0, array.slice(100).length);
+
+ assertEquals(0, array.slice(0, -100).length);
+ assertEquals(4, array.slice(0, -3).length);
+ assertEquals(4, array.slice(0, 4).length);
+ assertEquals(6, array.slice(0, 6).length);
+ assertEquals(7, array.slice(0, 7).length);
+ assertEquals(7, array.slice(0, 8).length);
+ assertEquals(7, array.slice(0, 100).length);
+
+ // Some exotic cases.
+
+ obj = { toString: function() { throw 'Exception'; } };
+
+ // More than 2 arguments:
+ assertEquals(7, array.slice(0, 7, obj, null, undefined).length);
+
+ // Custom conversion:
+ assertEquals(1, array.slice({valueOf: function() { return 1; }},
+ {toString: function() { return 2; }}).length);
+
+ // Throwing an exception in conversion:
+ try {
+ assertEquals(7, array.slice(0, obj).length);
+ throw 'Should have thrown';
+ } catch (e) {
+ assertEquals('Exception', e);
+ }
+ }
+})();
+
+
+// Nasty: modify the array in ToInteger.
+(function() {
+ var array = [];
+ var expected = []
+ bad_guy = { valueOf: function() { array.push(array.length); return -1; } };
+
+ for (var i = 0; i < 13; i++) {
+ var sliced = array.slice(bad_guy);
+ expected.push(i);
+ assertEquals(expected, array);
+ // According to the spec (15.4.4.10), length is calculated before
+ // performing ToInteger on arguments.
+ if (i == 0) {
+ assertEquals([], sliced); // Length was 0, nothing to get.
+ } else {
+ // Actually out of array [0..i] we get [i - 1] as length is i.
+ assertEquals([i - 1], sliced);
+ }
+ }
+})();
+
+
+// Now check the case with array of holes and some elements on prototype.
+(function() {
+ var len = 9;
+ var array = new Array(len);
+
+ var at3 = "@3";
+ var at7 = "@7";
+
+ for (var i = 0; i < 7; i++) {
+ Array.prototype[3] = at3;
+ Array.prototype[7] = at7;
+
+ assertEquals(len, array.length);
+ for (var i = 0; i < array.length; i++) {
+ assertEquals(array[i], Array.prototype[i]);
+ }
+
+ var sliced = array.slice();
+
+ assertEquals(len, sliced.length);
+
+ assertTrue(delete Array.prototype[3]);
+ assertTrue(delete Array.prototype[7]);
+
+ // Note that slice copies values from prototype into the array.
+ assertEquals(array[3], undefined);
+ assertFalse(array.hasOwnProperty(3));
+ assertEquals(sliced[3], at3);
+ assertTrue(sliced.hasOwnProperty(3));
+
+ assertEquals(array[7], undefined);
+ assertFalse(array.hasOwnProperty(7));
+ assertEquals(sliced[7], at7);
+ assertTrue(sliced.hasOwnProperty(7));
+
+ // ... but keeps the rest as holes:
+ Array.prototype[5] = "@5";
+ assertEquals(array[5], Array.prototype[5]);
+ assertFalse(array.hasOwnProperty(5));
+ assertEquals(sliced[5], Array.prototype[5]);
+ assertFalse(sliced.hasOwnProperty(5));
+
+ assertTrue(delete Array.prototype[5]);
+ }
+})();
diff --git a/test/mjsunit/array-splice.js b/test/mjsunit/array-splice.js
index 0543c323..18f81fe8 100644
--- a/test/mjsunit/array-splice.js
+++ b/test/mjsunit/array-splice.js
@@ -1,4 +1,4 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright 2010 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:
@@ -25,290 +25,265 @@
// (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 splice, shift, unshift, slice and join on small
- * and large arrays. Some of these methods are specified such that they
- * should work on other objects too, so we test that too.
- */
-
-var LARGE = 40000000;
-var VERYLARGE = 4000000000;
-
-// Nicer for firefox 1.5. Unless you uncomment the following two lines,
-// smjs will appear to hang on this file.
-//var LARGE = 40000;
-//var VERYLARGE = 40000;
-
-var fourhundredth = LARGE/400;
-
-function PseudoArray() {
-};
-
-for (var use_real_arrays = 0; use_real_arrays <= 1; use_real_arrays++) {
- var poses = [0, 140, 20000, VERYLARGE];
- var the_prototype;
- var new_function;
- var push_function;
- var concat_function;
- var slice_function;
- var splice_function;
- var splice_function_2;
- var unshift_function;
- var unshift_function_2;
- var shift_function;
- if (use_real_arrays) {
- new_function = function(length) {
- return new Array(length);
- };
- the_prototype = Array.prototype;
- push_function = function(array, elt) {
- return array.push(elt);
- };
- concat_function = function(array, other) {
- return array.concat(other);
- };
- slice_function = function(array, start, len) {
- return array.slice(start, len);
- };
- splice_function = function(array, start, len) {
- return array.splice(start, len);
- };
- splice_function_2 = function(array, start, len, elt) {
- return array.splice(start, len, elt);
- };
- unshift_function = function(array, elt) {
- return array.unshift(elt);
- };
- unshift_function_2 = function(array, elt1, elt2) {
- return array.unshift(elt1, elt2);
- };
- shift_function = function(array) {
- return array.shift();
- };
- } else {
- // Don't run largest size on non-arrays or we'll be here for ever.
- poses.pop();
- new_function = function(length) {
- var obj = new PseudoArray();
- obj.length = length;
- return obj;
- };
- the_prototype = PseudoArray.prototype;
- push_function = function(array, elt) {
- array[array.length] = elt;
- array.length++;
- };
- concat_function = function(array, other) {
- return Array.prototype.concat.call(array, other);
- };
- slice_function = function(array, start, len) {
- return Array.prototype.slice.call(array, start, len);
- };
- splice_function = function(array, start, len) {
- return Array.prototype.splice.call(array, start, len);
- };
- splice_function_2 = function(array, start, len, elt) {
- return Array.prototype.splice.call(array, start, len, elt);
- };
- unshift_function = function(array, elt) {
- return Array.prototype.unshift.call(array, elt);
- };
- unshift_function_2 = function(array, elt1, elt2) {
- return Array.prototype.unshift.call(array, elt1, elt2);
- };
- shift_function = function(array) {
- return Array.prototype.shift.call(array);
- };
+// Check that splicing array of holes keeps it as array of holes
+(function() {
+ for (var i = 0; i < 7; i++) {
+ var array = new Array(10);
+ var spliced = array.splice(1, 1, 'one', 'two');
+ assertEquals(1, spliced.length);
+ assertFalse(0 in spliced);
+
+ assertEquals(11, array.length);
+ assertFalse(0 in array);
+ assertTrue(1 in array);
+ assertTrue(2 in array);
+ assertFalse(3 in array);
+ }
+})();
+
+
+// Check various forms of arguments omission.
+(function() {
+ var array;
+ for (var i = 0; i < 7; i++) {
+ // SpiderMonkey and JSC return undefined in the case where no
+ // arguments are given instead of using the implicit undefined
+ // arguments. This does not follow ECMA-262, but we do the same for
+ // compatibility.
+ // TraceMonkey follows ECMA-262 though.
+ array = [1, 2, 3]
+ assertEquals(undefined, array.splice());
+ assertEquals([1, 2, 3], array);
+
+ // SpiderMonkey, TraceMonkey and JSC treat the case where no delete count is
+ // given differently from when an undefined delete count is given.
+ // This does not follow ECMA-262, but we do the same for
+ // compatibility.
+ array = [1, 2, 3]
+ assertEquals([1, 2, 3], array.splice(0));
+ assertEquals([], array);
+
+ array = [1, 2, 3]
+ assertEquals([1, 2, 3], array.splice(undefined));
+ assertEquals([], array);
+
+ array = [1, 2, 3]
+ assertEquals([1, 2, 3], array.splice("foobar"));
+ assertEquals([], array);
+
+ array = [1, 2, 3]
+ assertEquals([], array.splice(undefined, undefined));
+ assertEquals([1, 2, 3], array);
+
+ array = [1, 2, 3]
+ assertEquals([], array.splice("foobar", undefined));
+ assertEquals([1, 2, 3], array);
+
+ array = [1, 2, 3]
+ assertEquals([], array.splice(undefined, "foobar"));
+ assertEquals([1, 2, 3], array);
+
+ array = [1, 2, 3]
+ assertEquals([], array.splice("foobar", "foobar"));
+ assertEquals([1, 2, 3], array);
}
+})();
+
+
+// Check variants of negatives and positive indices.
+(function() {
+ var array, spliced;
+ for (var i = 0; i < 7; i++) {
+ array = [1, 2, 3, 4, 5, 6, 7];
+ spliced = array.splice(-100);
+ assertEquals([], array);
+ assertEquals([1, 2, 3, 4, 5, 6, 7], spliced);
+
+ array = [1, 2, 3, 4, 5, 6, 7];
+ spliced = array.splice(-3);
+ assertEquals([1, 2, 3, 4], array);
+ assertEquals([5, 6, 7], spliced);
+
+ array = [1, 2, 3, 4, 5, 6, 7];
+ spliced = array.splice(4);
+ assertEquals([1, 2, 3, 4], array);
+ assertEquals([5, 6, 7], spliced);
+
+ array = [1, 2, 3, 4, 5, 6, 7];
+ spliced = array.splice(6);
+ assertEquals([1, 2, 3, 4, 5, 6], array);
+ assertEquals([7], spliced);
+
+ array = [1, 2, 3, 4, 5, 6, 7];
+ spliced = array.splice(7);
+ assertEquals([1, 2, 3, 4, 5, 6, 7], array);
+ assertEquals([], spliced);
+
+ array = [1, 2, 3, 4, 5, 6, 7];
+ spliced = array.splice(8);
+ assertEquals([1, 2, 3, 4, 5, 6, 7], array);
+ assertEquals([], spliced);
+
+ array = [1, 2, 3, 4, 5, 6, 7];
+ spliced = array.splice(100);
+ assertEquals([1, 2, 3, 4, 5, 6, 7], array);
+ assertEquals([], spliced);
+
+ array = [1, 2, 3, 4, 5, 6, 7];
+ spliced = array.splice(0, -100);
+ assertEquals([1, 2, 3, 4, 5, 6, 7], array);
+ assertEquals([], spliced);
+
+ array = [1, 2, 3, 4, 5, 6, 7];
+ spliced = array.splice(0, -3);
+ assertEquals([1, 2, 3, 4, 5, 6, 7], array);
+ assertEquals([], spliced);
+
+ array = [1, 2, 3, 4, 5, 6, 7];
+ spliced = array.splice(0, 4);
+ assertEquals([5, 6, 7], array);
+ assertEquals([1, 2, 3, 4], spliced);
+
+ array = [1, 2, 3, 4, 5, 6, 7];
+ spliced = array.splice(0, 6);
+ assertEquals([7], array);
+ assertEquals([1, 2, 3, 4, 5, 6], spliced);
+
+ array = [1, 2, 3, 4, 5, 6, 7];
+ spliced = array.splice(0, 7);
+ assertEquals([], array);
+ assertEquals([1, 2, 3, 4, 5, 6, 7], spliced);
- for (var pos_pos = 0; pos_pos < poses.length; pos_pos++) {
- var pos = poses[pos_pos];
- if (pos > 100) {
- var a = new_function(pos);
- assertEquals(pos, a.length);
- push_function(a, 'foo');
- assertEquals(pos + 1, a.length);
- var b = ['bar'];
- // Delete a huge number of holes.
- var c = splice_function(a, 10, pos - 20);
- assertEquals(pos - 20, c.length);
- assertEquals(21, a.length);
+ array = [1, 2, 3, 4, 5, 6, 7];
+ spliced = array.splice(0, 8);
+ assertEquals([], array);
+ assertEquals([1, 2, 3, 4, 5, 6, 7], spliced);
+
+ array = [1, 2, 3, 4, 5, 6, 7];
+ spliced = array.splice(0, 100);
+ assertEquals([], array);
+ assertEquals([1, 2, 3, 4, 5, 6, 7], spliced);
+
+ // Some exotic cases.
+ obj = { toString: function() { throw 'Exception'; } };
+
+ // Throwing an exception in conversion:
+ try {
+ [1, 2, 3].splice(obj, 3);
+ throw 'Should have thrown';
+ } catch (e) {
+ assertEquals('Exception', e);
}
- // Add a numeric property to the prototype of the array class. This
- // allows us to test some borderline stuff relative to the standard.
- the_prototype["" + (pos + 1)] = 'baz';
-
- if (use_real_arrays) {
- // It seems quite clear from ECMAScript spec 15.4.4.5. Just call Get on
- // every integer in the range.
- // IE, Safari get this right.
- // FF, Opera get this wrong.
- var a = ['zero', ,'two'];
- if (pos == 0) {
- assertEquals("zero,baz,two", a.join(","));
- }
-
- // Concat only applies to real arrays, unlike most of the other methods.
- var a = new_function(pos);
- push_function(a, "con");
- assertEquals("con", a[pos]);
- assertEquals(pos + 1, a.length);
- var b = new_function(0);
- push_function(b, "cat");
- assertEquals("cat", b[0]);
- var ab = concat_function(a, b);
- assertEquals("con", ab[pos]);
- assertEquals(pos + 2, ab.length);
- assertEquals("cat", ab[pos + 1]);
- var ba = concat_function(b, a);
- assertEquals("con", ba[pos + 1]);
- assertEquals(pos + 2, ba.length);
- assertEquals("cat", ba[0]);
-
- // Join with '' as separator.
- var join = a.join('');
- assertEquals("con", join);
- join = b.join('');
- assertEquals("cat", join);
- join = ab.join('');
- assertEquals("concat", join);
- join = ba.join('');
- assertEquals("catcon", join);
-
- var sparse = [];
- sparse[pos + 1000] = 'is ';
- sparse[pos + 271828] = 'time ';
- sparse[pos + 31415] = 'the ';
- sparse[pos + 012260199] = 'all ';
- sparse[-1] = 'foo';
- sparse[pos + 22591927] = 'good ';
- sparse[pos + 1618033] = 'for ';
- sparse[pos + 91] = ': Now ';
- sparse[pos + 86720199] = 'men.';
- sparse.hest = 'fisk';
-
- assertEquals("baz: Now is the time for all good men.", sparse.join(''));
+ try {
+ [1, 2, 3].splice(0, obj, 3);
+ throw 'Should have thrown';
+ } catch (e) {
+ assertEquals('Exception', e);
+ }
+
+ array = [1, 2, 3];
+ array.splice(0, 3, obj);
+ assertEquals(1, array.length);
+
+ // Custom conversion:
+ array = [1, 2, 3];
+ spliced = array.splice({valueOf: function() { return 1; }},
+ {toString: function() { return 2; }},
+ 'one', 'two');
+ assertEquals([2, 3], spliced);
+ assertEquals([1, 'one', 'two'], array);
+ }
+})();
+
+
+// Nasty: modify the array in ToInteger.
+(function() {
+ var array = [];
+ var spliced;
+
+ for (var i = 0; i < 13; i++) {
+ bad_start = { valueOf: function() { array.push(2*i); return -1; } };
+ bad_count = { valueOf: function() { array.push(2*i + 1); return 1; } };
+ spliced = array.splice(bad_start, bad_count);
+ // According to the spec (15.4.4.12), length is calculated before
+ // performing ToInteger on arguments. However, v8 ignores elements
+ // we add while converting, so we need corrective pushes.
+ array.push(2*i); array.push(2*i + 1);
+ if (i == 0) {
+ assertEquals([], spliced); // Length was 0, nothing to get.
+ assertEquals([0, 1], array);
+ } else {
+ // When we start splice, array is [0 .. 2*i - 1], so we get
+ // as a result [2*i], this element is removed from the array,
+ // but [2 * i, 2 * i + 1] are added.
+ assertEquals([2 * i - 1], spliced);
+ assertEquals(2 * i, array[i]);
+ assertEquals(2 * i + 1, array[i + 1]);
}
+ }
+})();
+
- a = new_function(pos);
- push_function(a, 'zero');
- push_function(a, void 0);
- push_function(a, 'two');
-
- // Splice works differently from join.
- // IE, Safari get this wrong.
- // FF, Opera get this right.
- // 15.4.4.12 line 24 says the object itself has to have the property...
- var zero = splice_function(a, pos, 1);
- assertEquals("undefined", typeof(a[pos]));
- assertEquals("two", a[pos+1], "pos1:" + pos);
- assertEquals(pos + 2, a.length, "a length");
- assertEquals(1, zero.length, "zero length");
- assertEquals("zero", zero[0]);
-
- // 15.4.4.12 line 41 says the object itself has to have the property...
- a = new_function(pos);
- push_function(a, 'zero');
- push_function(a, void 0);
- push_function(a, 'two');
- var nothing = splice_function_2(a, pos, 0, 'minus1');
- assertEquals("minus1", a[pos]);
- assertEquals("zero", a[pos+1]);
- assertEquals("undefined", typeof(a[pos+2]), "toot!");
- assertEquals("two", a[pos+3], "pos3");
- assertEquals(pos + 4, a.length);
- assertEquals(1, zero.length);
- assertEquals("zero", zero[0]);
-
- // 15.4.4.12 line 10 says the object itself has to have the property...
- a = new_function(pos);
- push_function(a, 'zero');
- push_function(a, void 0);
- push_function(a, 'two');
- var one = splice_function(a, pos + 1, 1);
- assertEquals("", one.join(","));
- assertEquals(pos + 2, a.length);
- assertEquals("zero", a[pos]);
- assertEquals("two", a[pos+1]);
-
- // Set things back to the way they were.
- the_prototype[pos + 1] = undefined;
-
- // Unshift.
- var a = new_function(pos);
- push_function(a, "foo");
- assertEquals("foo", a[pos]);
- assertEquals(pos + 1, a.length);
- unshift_function(a, "bar");
- assertEquals("foo", a[pos+1]);
- assertEquals(pos + 2, a.length);
- assertEquals("bar", a[0]);
- unshift_function_2(a, "baz", "boo");
- assertEquals("foo", a[pos+3]);
- assertEquals(pos + 4, a.length);
- assertEquals("baz", a[0]);
- assertEquals("boo", a[1]);
- assertEquals("bar", a[2]);
-
- // Shift.
- var baz = shift_function(a);
- assertEquals("baz", baz);
- assertEquals("boo", a[0]);
- assertEquals(pos + 3, a.length);
- assertEquals("foo", a[pos + 2]);
-
- // Slice.
- var bar = slice_function(a, 1, 0); // don't throw an exception please.
- bar = slice_function(a, 1, 2);
- assertEquals("bar", bar[0]);
- assertEquals(1, bar.length);
- assertEquals("bar", a[1]);
+// Now check the case with array of holes and some elements on prototype.
+(function() {
+ var len = 9;
+
+ var at3 = "@3";
+ var at7 = "@7";
+
+ for (var i = 0; i < 7; i++) {
+ var array = new Array(len);
+ Array.prototype[3] = at3;
+ Array.prototype[7] = at7;
+
+ var spliced = array.splice(2, 2, 'one', undefined, 'two');
+
+ // Second hole (at index 3) of array turns into
+ // value of Array.prototype[3] while copying.
+ assertEquals([, at3], spliced);
+ assertEquals([, , 'one', undefined, 'two', , , at7, at7, ,], array);
+
+ // ... but array[7] is actually a hole:
+ assertTrue(delete Array.prototype[7]);
+ assertEquals(undefined, array[7]);
+
+ // and now check hasOwnProperty
+ assertFalse(array.hasOwnProperty(0));
+ assertFalse(array.hasOwnProperty(1));
+ assertTrue(array.hasOwnProperty(2));
+ assertTrue(array.hasOwnProperty(3));
+ assertTrue(array.hasOwnProperty(4));
+ assertFalse(array.hasOwnProperty(5));
+ assertFalse(array.hasOwnProperty(6));
+ assertFalse(array.hasOwnProperty(7));
+ assertTrue(array.hasOwnProperty(8));
+ assertFalse(array.hasOwnProperty(9));
+
+ // and now check couple of indices above length.
+ assertFalse(array.hasOwnProperty(10));
+ assertFalse(array.hasOwnProperty(15));
+ assertFalse(array.hasOwnProperty(31));
+ assertFalse(array.hasOwnProperty(63));
+ assertFalse(array.hasOwnProperty(2 << 32 - 1));
+ }
+})();
+
+
+// Check the behaviour when approaching maximal values for length.
+(function() {
+ for (var i = 0; i < 7; i++) {
+ try {
+ new Array((1 << 32) - 3).splice(-1, 0, 1, 2, 3, 4, 5);
+ throw 'Should have thrown RangeError';
+ } catch (e) {
+ assertTrue(e instanceof RangeError);
+ }
+ // Check smi boundary
+ var bigNum = (1 << 30) - 3;
+ var array = new Array(bigNum);
+ array.splice(-1, 0, 1, 2, 3, 4, 5, 6, 7);
+ assertEquals(bigNum + 7, array.length);
}
-}
-
-// Lets see if performance is reasonable.
-
-var a = new Array(LARGE + 10);
-for (var i = 0; i < a.length; i += 1000) {
- a[i] = i;
-}
-
-// Take something near the end of the array.
-for (var i = 0; i < 100; i++) {
- var top = a.splice(LARGE, 5);
- assertEquals(5, top.length);
- assertEquals(LARGE, top[0]);
- assertEquals("undefined", typeof(top[1]));
- assertEquals(LARGE + 5, a.length);
- a.splice(LARGE, 0, LARGE);
- a.length = LARGE + 10;
-}
-
-var a = new Array(LARGE + 10);
-for (var i = 0; i < a.length; i += fourhundredth) {
- a[i] = i;
-}
-
-// Take something near the middle of the array.
-for (var i = 0; i < 10; i++) {
- var top = a.splice(LARGE >> 1, 5);
- assertEquals(5, top.length);
- assertEquals(LARGE >> 1, top[0]);
- assertEquals("undefined", typeof(top[1]));
- assertEquals(LARGE + 5, a.length);
- a.splice(LARGE >> 1, 0, LARGE >> 1, void 0, void 0, void 0, void 0);
-}
-
-
-// Test http://b/issue?id=1202711
-arr = [0];
-arr.length = 2;
-Array.prototype[1] = 1;
-assertEquals(1, arr.pop());
-assertEquals(0, arr.pop());
-Array.prototype[1] = undefined;
-
-// Test http://code.google.com/p/chromium/issues/detail?id=21860
-Array.prototype.push.apply([], [1].splice(0, -(-1 % 5)));
+})();
diff --git a/test/mjsunit/array-unshift.js b/test/mjsunit/array-unshift.js
new file mode 100644
index 00000000..06a78a7d
--- /dev/null
+++ b/test/mjsunit/array-unshift.js
@@ -0,0 +1,132 @@
+// Copyright 2010 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.
+
+// Check that unshifting array of holes keeps the original array
+// as array of holes
+(function() {
+ var array = new Array(10);
+ assertEquals(13, array.unshift('1st', '2ns', '3rd'));
+ assertTrue(0 in array);
+ assertTrue(1 in array);
+ assertTrue(2 in array);
+ assertFalse(3 in array);
+})();
+
+
+// Check that unshif with no args has a side-effect of
+// feeling the holes with elements from the prototype
+// (if present, of course)
+(function() {
+ var len = 3;
+ var array = new Array(len);
+
+ var at0 = '@0';
+ var at2 = '@2';
+
+ Array.prototype[0] = at0;
+ Array.prototype[2] = at2;
+
+ // array owns nothing...
+ assertFalse(array.hasOwnProperty(0));
+ assertFalse(array.hasOwnProperty(1));
+ assertFalse(array.hasOwnProperty(2));
+
+ // ... but sees values from Array.prototype
+ assertEquals(array[0], at0);
+ assertEquals(array[1], undefined);
+ assertEquals(array[2], at2);
+
+ assertEquals(len, array.unshift());
+
+ assertTrue(delete Array.prototype[0]);
+ assertTrue(delete Array.prototype[2]);
+
+ // unshift makes array own 0 and 2...
+ assertTrue(array.hasOwnProperty(0));
+ assertFalse(array.hasOwnProperty(1));
+ assertTrue(array.hasOwnProperty(2));
+
+ // ... so they are not affected be delete.
+ assertEquals(array[0], at0);
+ assertEquals(array[1], undefined);
+ assertEquals(array[2], at2);
+})();
+
+
+// Now check the case with array of holes and some elements on prototype.
+(function() {
+ var len = 9;
+ var array = new Array(len);
+ Array.prototype[3] = "@3";
+ Array.prototype[7] = "@7";
+
+ assertEquals(len, array.length);
+ for (var i = 0; i < array.length; i++) {
+ assertEquals(array[i], Array.prototype[i]);
+ }
+
+ assertEquals(len + 1, array.unshift('head'));
+
+ assertEquals(len + 1, array.length);
+ // Note that unshift copies values from prototype into the array.
+ assertEquals(array[4], Array.prototype[3]);
+ assertTrue(array.hasOwnProperty(4));
+
+ assertEquals(array[8], Array.prototype[7]);
+ assertTrue(array.hasOwnProperty(8));
+
+ // ... but keeps the rest as holes:
+ Array.prototype[5] = "@5";
+ assertEquals(array[5], Array.prototype[5]);
+ assertFalse(array.hasOwnProperty(5));
+
+ assertEquals(array[3], Array.prototype[3]);
+ assertFalse(array.hasOwnProperty(3));
+
+ assertEquals(array[7], Array.prototype[7]);
+ assertFalse(array.hasOwnProperty(7));
+
+ assertTrue(delete Array.prototype[3]);
+ assertTrue(delete Array.prototype[5]);
+ assertTrue(delete Array.prototype[7]);
+})();
+
+// Check the behaviour when approaching maximal values for length.
+(function() {
+ for (var i = 0; i < 7; i++) {
+ try {
+ new Array((1 << 32) - 3).unshift(1, 2, 3, 4, 5);
+ throw 'Should have thrown RangeError';
+ } catch (e) {
+ assertTrue(e instanceof RangeError);
+ }
+
+ // Check smi boundary
+ var bigNum = (1 << 30) - 3;
+ assertEquals(bigNum + 7, new Array(bigNum).unshift(1, 2, 3, 4, 5, 6, 7));
+ }
+})();
diff --git a/test/mjsunit/bugs/618.js b/test/mjsunit/bugs/618.js
new file mode 100644
index 00000000..afa9929a
--- /dev/null
+++ b/test/mjsunit/bugs/618.js
@@ -0,0 +1,86 @@
+// Copyright 2010 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.
+
+// Simple class using inline constructor.
+function C1() {
+ this.x = 23;
+};
+var c1 = new C1();
+assertEquals(23, c1.x);
+assertEquals("undefined", typeof c1.y);
+
+// Add setter somewhere on the prototype chain after having constructed the
+// first instance.
+C1.prototype = { set x(value) { this.y = 23; } };
+var c1 = new C1();
+assertEquals("undefined", typeof c1.x);
+assertEquals(23, c1.y);
+
+// Simple class using inline constructor.
+function C2() {
+ this.x = 23;
+};
+var c2 = new C2();
+assertEquals(23, c2.x);
+assertEquals("undefined", typeof c2.y);
+
+// Add setter somewhere on the prototype chain after having constructed the
+// first instance.
+C2.prototype.__proto__ = { set x(value) { this.y = 23; } };
+var c2 = new C2();
+assertEquals("undefined", typeof c2.x);
+assertEquals(23, c2.y);
+
+// Simple class using inline constructor.
+function C3() {
+ this.x = 23;
+};
+var c3 = new C3();
+assertEquals(23, c3.x);
+assertEquals("undefined", typeof c3.y);
+
+// Add setter somewhere on the prototype chain after having constructed the
+// first instance.
+C3.prototype.__defineSetter__('x', function(value) { this.y = 23; });
+var c3 = new C3();
+assertEquals("undefined", typeof c3.x);
+assertEquals(23, c3.y);
+
+// Simple class using inline constructor.
+function C4() {
+ this.x = 23;
+};
+var c4 = new C4();
+assertEquals(23, c4.x);
+assertEquals("undefined", typeof c4.y);
+
+// Add setter somewhere on the prototype chain after having constructed the
+// first instance.
+C4.prototype.__proto__.__defineSetter__('x', function(value) { this.y = 23; });
+var c4 = new C4();
+assertEquals("undefined", typeof c4.x);
+assertEquals(23, c4.y);
diff --git a/test/mjsunit/bugs/bug-619.js b/test/mjsunit/bugs/bug-619.js
new file mode 100644
index 00000000..ef8ba80e
--- /dev/null
+++ b/test/mjsunit/bugs/bug-619.js
@@ -0,0 +1,62 @@
+// Copyright 2010 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 this bug is corrected move to object-define-property and add
+// additional tests for configurable in the same manner as existing tests
+// there.
+
+var obj = {};
+obj[1] = 42;
+assertEquals(42, obj[1]);
+Object.defineProperty(obj, '1', {value:10, writable:false});
+assertEquals(10, obj[1]);
+
+// We should not be able to override obj[1].
+obj[1] = 5;
+assertEquals(10, obj[1]);
+
+// Try on a range of numbers.
+for(var i = 0; i < 1024; i++) {
+ obj[i] = 42;
+}
+
+for(var i = 0; i < 1024; i++) {
+ Object.defineProperty(obj, i, {value: i, writable:false});
+}
+
+for(var i = 0; i < 1024; i++) {
+ assertEquals(i, obj[i]);
+}
+
+for(var i = 0; i < 1024; i++) {
+ obj[1] = 5;
+}
+
+for(var i = 0; i < 1024; i++) {
+ assertEquals(i, obj[i]);
+}
+
diff --git a/test/mjsunit/codegen-coverage.js b/test/mjsunit/codegen-coverage.js
index d5e7769d..42c371ba 100644
--- a/test/mjsunit/codegen-coverage.js
+++ b/test/mjsunit/codegen-coverage.js
@@ -25,64 +25,110 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-// Test the paths in the code generator where values in specific
-// registers get moved around so that the shift operation can use
-// register ECX on ia32 for the shift amount. Other codegen coverage
-// tests should go here too.
-
-
+// Flags: --nofull-compiler --nofast-compiler
+// Test paths in the code generator where values in specific registers
+// get moved around.
function identity(x) {
return x;
}
function cover_codegen_paths() {
var x = 1;
- var a; // Register EAX
- var b; // Register EBX
- var c; // Register ECX
- var d; // Register EDX
- // Register ESI is already used.
- var di; // Register EDI
+
+ // This test depends on the fixed order of register allocation. We try to
+ // get values in specific registers (ia32, x64):
+ var a; // Register eax, rax.
+ var b; // Register ebx, rbx.
+ var c; // Register ecx, rcx.
+ var d; // Register edx, rdx.
+ var di; // Register edi, rdi.
while (x == 1) {
+ // The call will spill registers and leave x in {eax,rax}.
x = identity(1);
+ // The add will spill x and reuse {eax,rax} for the result.
a = x + 1;
+ // A fresh register {ebx,rbx} will be allocated for x, then reused for
+ // the result.
+ b = x + 1;
+ // Et cetera.
c = x + 1;
d = x + 1;
- b = x + 1;
di = x + 1;
// Locals are in the corresponding registers here.
- assertEquals(c << a, 8);
+ assertEquals(8, c << a);
x = identity(1);
a = x + 1;
+ b = x + 1;
c = x + 1;
d = x + 1;
- b = x + 1;
di = x + 1;
- // Locals are in the corresponding registers here.
- assertEquals(a << c, 8);
+ assertEquals(8, a << c);
x = identity(1);
a = x + 1;
+ b = x + 1;
c = x + 1;
d = x + 1;
- b = x + 1;
di = x + 1;
- // Locals are in the corresponding registers here.
c = 0; // Free register ecx.
- assertEquals(a << d, 8);
+ assertEquals(8, a << d);
x = identity(1);
a = x + 1;
+ b = x + 1;
c = x + 1;
d = x + 1;
- b = x + 1;
di = x + 1;
- // Locals are in the corresponding registers here.
b = 0; // Free register ebx.
- assertEquals(a << d, 8);
+ assertEquals(8, a << d);
+
+ // Test the non-commutative subtraction operation with a smi on the
+ // left, all available registers on the right, and a non-smi result.
+ x = identity(-1073741824); // Least (31-bit) smi.
+ a = x + 1; // Still a smi, the greatest smi negated.
+ b = x + 1;
+ c = x + 1;
+ d = x + 1;
+ di = x + 1;
+ // Subtraction should overflow the 31-bit smi range. The result
+ // (1073741824) is outside the 31-bit smi range so it doesn't hit the
+ // "unsafe smi" code that spills a register.
+ assertEquals(1073741824, 1 - a);
+
+ x = identity(-1073741824);
+ a = x + 1;
+ b = x + 1;
+ c = x + 1;
+ d = x + 1;
+ di = x + 1;
+ assertEquals(1073741824, 1 - b);
+
+ x = identity(-1073741824);
+ a = x + 1;
+ b = x + 1;
+ c = x + 1;
+ d = x + 1;
+ di = x + 1;
+ assertEquals(1073741824, 1 - c);
+
+ x = identity(-1073741824);
+ a = x + 1;
+ b = x + 1;
+ c = x + 1;
+ d = x + 1;
+ di = x + 1;
+ assertEquals(1073741824, 1 - d);
+
+ x = identity(-1073741824);
+ a = x + 1;
+ b = x + 1;
+ c = x + 1;
+ d = x + 1;
+ di = x + 1;
+ assertEquals(1073741824, 1 - di);
x = 3;
}
diff --git a/test/mjsunit/compiler/assignment.js b/test/mjsunit/compiler/assignment.js
new file mode 100644
index 00000000..ee2d3237
--- /dev/null
+++ b/test/mjsunit/compiler/assignment.js
@@ -0,0 +1,264 @@
+// Copyright 2010 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.
+
+// Tests for compound assignments at the top level
+
+z = 2;
+z += 4;
+
+assertEquals(z, 6);
+
+a = new Array(10);
+
+a[2] += 7;
+a[2] = 15;
+a[2] += 2;
+
+assertEquals(17, a[2]);
+
+b = new Object();
+b.foo = 5;
+b.foo += 12;
+
+assertEquals(17, b.foo);
+
+// Test compound assignments in an anonymous function with local variables.
+(function () {
+ var z = 2;
+ z += 4;
+
+ assertEquals(z, 6);
+
+ var a = new Array(10);
+
+ a[2] += 7;
+ a[2] = 15;
+ a[2] += 2;
+
+ assertEquals(17, a[2]);
+
+ var b = new Object();
+ b.foo = 5;
+ b.foo += 12;
+
+ assertEquals(17, b.foo);
+})();
+
+// Test compound assignments in an anonymous function with global variables.
+(function () {
+ z = 2;
+ z += 4;
+
+ assertEquals(z, 6);
+
+ a = new Array(10);
+
+ a[2] += 7;
+ a[2] = 15;
+ a[2] += 2;
+
+ assertEquals(17, a[2]);
+
+ b = new Object();
+ b.foo = 5;
+ b.foo += 12;
+
+ assertEquals(17, b.foo);
+})();
+
+// Test compound assignments in a named function with local variables.
+function foo() {
+ var z = 3;
+ z += 4;
+
+ assertEquals(z, 7);
+
+ var a = new Array(10);
+
+ a[2] += 7;
+ a[2] = 15;
+ a[2] += 2;
+
+ assertEquals(17, a[2]);
+
+ var b = new Object();
+ b.foo = 5;
+ b.foo += 12;
+
+ assertEquals(17, b.foo);
+}
+
+foo();
+
+// Test compound assignments in a named function with global variables.
+function bar() {
+ z = 2;
+ z += 5;
+
+ assertEquals(z, 7);
+
+ a = new Array(10);
+
+ a[2] += 7;
+ a[2] = 15;
+ a[2] += 2;
+
+ assertEquals(17, a[2]);
+
+ b = new Object();
+ b.foo = 5;
+ b.foo += 12;
+
+ assertEquals(17, b.foo);
+}
+
+bar();
+
+// Entire series of tests repeated, in loops.
+// -------------------------------------------
+// Tests for compound assignments in a loop at the top level
+
+for (i = 0; i < 5; ++i) {
+ z = 2;
+ z += 4;
+
+ assertEquals(z, 6);
+
+ a = new Array(10);
+
+ a[2] += 7;
+ a[2] = 15;
+ a[2] += 2;
+
+ assertEquals(17, a[2]);
+
+ b = new Object();
+ b.foo = 5;
+ b.foo += 12;
+
+ assertEquals(17, b.foo);
+}
+
+// Test compound assignments in an anonymous function with local variables.
+(function () {
+ for (var i = 0; i < 5; ++i) {
+ var z = 2;
+ z += 4;
+
+ assertEquals(z, 6);
+
+ var a = new Array(10);
+
+ a[2] += 7;
+ a[2] = 15;
+ a[2] += 2;
+
+ assertEquals(17, a[2]);
+
+ var b = new Object();
+ b.foo = 5;
+ b.foo += 12;
+
+ assertEquals(17, b.foo);
+ }
+})();
+
+// Test compound assignments in an anonymous function with global variables.
+(function () {
+ for (i = 0; i < 5; ++i) {
+ z = 2;
+ z += 4;
+
+ assertEquals(z, 6);
+
+ a = new Array(10);
+
+ a[2] += 7;
+ a[2] = 15;
+ a[2] += 2;
+
+ assertEquals(17, a[2]);
+
+ b = new Object();
+ b.foo = 5;
+ b.foo += 12;
+
+ assertEquals(17, b.foo);
+ }
+})();
+
+// Test compound assignments in a named function with local variables.
+function foo_loop() {
+ for (i = 0; i < 5; ++i) {
+ var z = 3;
+ z += 4;
+
+ assertEquals(z, 7);
+
+ var a = new Array(10);
+
+ a[2] += 7;
+ a[2] = 15;
+ a[2] += 2;
+
+ assertEquals(17, a[2]);
+
+ var b = new Object();
+ b.foo = 5;
+ b.foo += 12;
+
+ assertEquals(17, b.foo);
+ }
+}
+
+foo_loop();
+
+// Test compound assignments in a named function with global variables.
+function bar_loop() {
+ for (i = 0; i < 5; ++i) {
+ z = 2;
+ z += 5;
+
+ assertEquals(z, 7);
+
+ a = new Array(10);
+
+ a[2] += 7;
+ a[2] = 15;
+ a[2] += 2;
+
+ assertEquals(17, a[2]);
+
+ b = new Object();
+ b.foo = 5;
+ b.foo += 12;
+
+ assertEquals(17, b.foo);
+ }
+}
+
+bar_loop();
diff --git a/test/mjsunit/compiler/simple-bailouts.js b/test/mjsunit/compiler/simple-bailouts.js
new file mode 100644
index 00000000..af80b7f0
--- /dev/null
+++ b/test/mjsunit/compiler/simple-bailouts.js
@@ -0,0 +1,127 @@
+// Copyright 2010 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.
+
+// Flags: --fast-compiler
+
+function Test() {
+ this.result = 0;
+ this.x = 0;
+ this.y = 0;
+ this.z = 0;
+}
+var a = 1;
+var b = 2;
+var c = 4;
+var d = 8;
+
+// Test operations expected to stay on the fast path. Enumerate all binary
+// trees with <= 4 leaves.
+Test.prototype.test0 = function () {
+ this.result = a | b;
+};
+
+Test.prototype.test1 = function() {
+ this.result = (a | b) | c;
+};
+
+Test.prototype.test2 = function() {
+ this.result = a | (b | c);
+};
+
+Test.prototype.test3 = function() {
+ this.result = ((a | b) | c) | d;
+};
+
+Test.prototype.test4 = function() {
+ this.result = (a | (b | c)) | d;
+};
+
+Test.prototype.test5 = function() {
+ this.result = (a | b) | (c | d);
+};
+
+Test.prototype.test6 = function() {
+ this.result = a | ((b | c) | d);
+};
+
+Test.prototype.test7 = function() {
+ this.result = a | (b | (c | d));
+};
+
+// These tests should fail if we bailed out to the beginning of the full
+// code.
+Test.prototype.test8 = function () {
+ // If this.x = 1 and a = 1.1:
+ this.y = this.x | b; // Should be (1 | 2) == 3.
+ this.x = c; // Should be 4.
+ this.z = this.x | a; // Should be (4 | 1.1) == 5.
+};
+
+Test.prototype.test9 = function() {
+ // If this.x = 2 and a = 1.1:
+ this.z = // (14 | 1.1) == 15
+ (this.x = // (6 | 8) == 14
+ (this.y = // (2 | 4) == 6
+ this.x // 2
+ | c) // 4
+ | d) // 8
+ | a; // 1.1
+}
+
+var t = new Test();
+
+t.test0();
+assertEquals(3, t.result);
+
+t.test1();
+assertEquals(7, t.result);
+t.test2();
+assertEquals(7, t.result);
+
+t.test3();
+assertEquals(15, t.result);
+t.test4();
+assertEquals(15, t.result);
+t.test5();
+assertEquals(15, t.result);
+t.test6();
+assertEquals(15, t.result);
+t.test7();
+assertEquals(15, t.result);
+
+a = 1.1;
+t.x = 1;
+t.test8();
+assertEquals(4, t.x);
+assertEquals(3, t.y);
+assertEquals(5, t.z);
+
+t.x = 2;
+t.test9();
+assertEquals(14, t.x);
+assertEquals(6, t.y);
+assertEquals(15, t.z);
diff --git a/test/mjsunit/compiler/simple-binary-op.js b/test/mjsunit/compiler/simple-binary-op.js
new file mode 100644
index 00000000..15e1a559
--- /dev/null
+++ b/test/mjsunit/compiler/simple-binary-op.js
@@ -0,0 +1,40 @@
+// Copyright 2010 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.
+
+// Flags: --fast-compiler
+
+var a = 1;
+var b = 2;
+var c = 4;
+
+function f() { this.x = this.x | a | b | c | a | c; }
+
+var o = {x:0, g:f}
+
+o.g();
+
+assertEquals(7, o.x);
diff --git a/test/mjsunit/compiler/simple-global-access.js b/test/mjsunit/compiler/simple-global-access.js
new file mode 100644
index 00000000..35746ba8
--- /dev/null
+++ b/test/mjsunit/compiler/simple-global-access.js
@@ -0,0 +1,53 @@
+// Copyright 2010 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.
+
+// Flags: --fast-compiler
+
+// Test global variable loads with the fast compiler.
+var g1 = 42;
+var g2 = 43;
+var g3 = 44;
+this.__defineGetter__("g4", function () { return 45; });
+
+function f1() { this.x = this.y = this.z = g1; }
+function f2() { this.x = g1; this.y = g2; this.z = g3; }
+function f3() { this.x = g4; }
+
+var o = { x:0, y:0, z:0, test1:f1, test2:f2, test3:f3 }
+
+o.test1();
+assertEquals(42, o.x);
+assertEquals(42, o.y);
+assertEquals(42, o.z);
+
+o.test2();
+assertEquals(42, o.x);
+assertEquals(43, o.y);
+assertEquals(44, o.z);
+
+o.test3();
+assertEquals(45, o.x);
diff --git a/test/mjsunit/compiler/this-property-refs.js b/test/mjsunit/compiler/this-property-refs.js
new file mode 100644
index 00000000..5e8ea596
--- /dev/null
+++ b/test/mjsunit/compiler/this-property-refs.js
@@ -0,0 +1,64 @@
+// Copyright 2010 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.
+
+// Flags: --fast-compiler
+
+// Test references to properties of this.
+function Test() {
+ this.a = 0;
+ this.b = 1;
+ this.c = 2;
+ this.d = 3;
+}
+
+Test.prototype.test0 = function () {
+ this.a = this.b;
+};
+
+Test.prototype.test1 = function() {
+ this.a = this.b = this.c;
+};
+
+Test.prototype.test2 = function() {
+ this.c = this.d;
+ this.b = this.c;
+ this.a = this.b;
+};
+
+var t = new Test();
+
+t.test0();
+assertEquals(1, t.a);
+
+t.test1();
+assertEquals(2, t.a);
+assertEquals(2, t.b);
+
+t.test2();
+assertEquals(3, t.a);
+assertEquals(3, t.b);
+assertEquals(3, t.c);
diff --git a/test/mjsunit/debug-compile-event.js b/test/mjsunit/debug-compile-event.js
index 071183bf..e7ecf47e 100644
--- a/test/mjsunit/debug-compile-event.js
+++ b/test/mjsunit/debug-compile-event.js
@@ -90,6 +90,11 @@ function listener(event, exec_state, event_data, data) {
var json = event_data.toJSONProtocol();
var msg = eval('(' + json + ')');
assertTrue('context' in msg.body.script);
+
+ // Check that we pick script name from //@ sourceURL, iff present
+ assertEquals(current_source.indexOf('sourceURL') >= 0 ?
+ 'myscript.js' : undefined,
+ event_data.script().name());
}
} catch (e) {
exception = e
@@ -109,6 +114,7 @@ compileSource('eval("eval(\'(function(){return a;})\')")');
source_count += 2; // Using eval causes additional compilation event.
compileSource('JSON.parse(\'{"a":1,"b":2}\')');
source_count++; // Using JSON.parse causes additional compilation event.
+compileSource('x=1; //@ sourceURL=myscript.js');
// Make sure that the debug event listener was invoked.
assertFalse(exception, "exception in listener")
diff --git a/test/mjsunit/debug-evaluate.js b/test/mjsunit/debug-evaluate.js
index c4779072..182e2ac9 100644
--- a/test/mjsunit/debug-evaluate.js
+++ b/test/mjsunit/debug-evaluate.js
@@ -87,6 +87,37 @@ function listener(event, exec_state, event_data, data) {
testRequest(dcp, '{"expression":"a","global":true}', true, 1);
testRequest(dcp, '{"expression":"this.a","global":true}', true, 1);
+ // Test that the whole string text is returned if maxStringLength
+ // parameter is passed.
+ testRequest(
+ dcp,
+ '{"expression":"this.longString","global":true,maxStringLength:-1}',
+ true,
+ longString);
+ testRequest(
+ dcp,
+ '{"expression":"this.longString","global":true,maxStringLength:' +
+ longString.length + '}',
+ true,
+ longString);
+ var truncatedStringSuffix = '... (length: ' + longString.length + ')';
+ testRequest(
+ dcp,
+ '{"expression":"this.longString","global":true,maxStringLength:0}',
+ true,
+ truncatedStringSuffix);
+ testRequest(
+ dcp,
+ '{"expression":"this.longString","global":true,maxStringLength:1}',
+ true,
+ longString.charAt(0) + truncatedStringSuffix);
+ // Test that by default string is truncated to first 80 chars.
+ testRequest(
+ dcp,
+ '{"expression":"this.longString","global":true}',
+ true,
+ longString.substring(0, 80) + truncatedStringSuffix);
+
// Indicate that all was processed.
listenerComplete = true;
}
@@ -109,6 +140,12 @@ function g() {
a = 1;
+// String which is longer than 80 chars.
+var longString = "1234567890_";
+for (var i = 0; i < 4; i++) {
+ longString += longString;
+}
+
// Set a break point at return in f and invoke g to hit the breakpoint.
Debug.setBreakPoint(f, 2, 0);
g();
diff --git a/test/mjsunit/div-mod.js b/test/mjsunit/div-mod.js
index b3c77e1d..1d352b55 100644
--- a/test/mjsunit/div-mod.js
+++ b/test/mjsunit/div-mod.js
@@ -154,4 +154,18 @@ function compute_mod(dividend, divisor) {
doTest(-a,-b);
}
}
-})()
+})();
+
+
+(function () {
+ // Edge cases
+ var zero = 0;
+ var minsmi32 = -0x40000000;
+ var minsmi64 = -0x80000000;
+ var somenum = 3532;
+ assertEquals(-0, zero / -1, "0 / -1");
+ assertEquals(1, minsmi32 / -0x40000000, "minsmi/minsmi-32");
+ assertEquals(1, minsmi64 / -0x80000000, "minsmi/minsmi-64");
+ assertEquals(somenum, somenum % -0x40000000, "%minsmi-32");
+ assertEquals(somenum, somenum % -0x80000000, "%minsmi-64");
+})();
diff --git a/test/mjsunit/fuzz-natives.js b/test/mjsunit/fuzz-natives.js
index d906eb8a..e2f601eb 100644
--- a/test/mjsunit/fuzz-natives.js
+++ b/test/mjsunit/fuzz-natives.js
@@ -27,6 +27,9 @@
// Flags: --allow-natives-syntax
+var RUN_WITH_ALL_ARGUMENT_ENTRIES = false;
+var kOnManyArgumentsRemove = 5;
+
function makeArguments() {
var result = [ ];
result.push(17);
@@ -74,13 +77,23 @@ function testArgumentTypes(name, argc) {
var func = makeFunction(name, argc);
while (hasMore) {
var argPool = makeArguments();
+ // When we have 5 or more arguments we lower the amount of tests cases
+ // by randomly removing kOnManyArgumentsRemove entries
+ var numArguments = RUN_WITH_ALL_ARGUMENT_ENTRIES ?
+ kArgObjects : kArgObjects-kOnManyArgumentsRemove;
+ if (argc >= 5 && !RUN_WITH_ALL_ARGUMENT_ENTRIES) {
+ for (var i = 0; i < kOnManyArgumentsRemove; i++) {
+ var rand = Math.floor(Math.random() * (kArgObjects - i));
+ argPool.splice(rand,1);
+ }
+ }
var current = type;
var hasMore = false;
var argList = [ ];
for (var i = 0; i < argc; i++) {
- var index = current % kArgObjects;
- current = (current / kArgObjects) << 0;
- if (index != (kArgObjects - 1))
+ var index = current % numArguments;
+ current = (current / numArguments) << 0;
+ if (index != (numArguments - 1))
hasMore = true;
argList.push(argPool[index]);
}
diff --git a/test/mjsunit/object-define-properties.js b/test/mjsunit/object-define-properties.js
new file mode 100644
index 00000000..6b3725b3
--- /dev/null
+++ b/test/mjsunit/object-define-properties.js
@@ -0,0 +1,56 @@
+// Copyright 2010 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.
+
+// Tests the Object.defineProperties method - ES 15.2.3.7
+// Note that the internal DefineOwnProperty method is tested through
+// object-define-property.js, this file only contains tests specific for
+// Object.defineProperties. Also note that object-create.js contains
+// a range of indirect tests on this method since Object.create uses
+// Object.defineProperties as a step in setting up the object.
+
+// Try defining with null as descriptor:
+try {
+ Object.defineProperties({}, null);
+} catch(e) {
+ assertTrue(/null to object/.test(e));
+}
+
+// Try defining with null as object
+try {
+ Object.defineProperties(null, {});
+} catch(e) {
+ assertTrue(/called on non-object/.test(e));
+}
+
+
+var desc = {foo: {value: 10}, bar: {get: function() {return 42; }}};
+var obj = {};
+// Check that we actually get the object back as returnvalue
+var x = Object.defineProperties(obj, desc);
+
+assertEquals(x.foo, 10);
+assertEquals(x.bar, 42);
diff --git a/test/mjsunit/object-define-property.js b/test/mjsunit/object-define-property.js
new file mode 100644
index 00000000..43b1c7f0
--- /dev/null
+++ b/test/mjsunit/object-define-property.js
@@ -0,0 +1,499 @@
+// Copyright 2010 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.
+
+// Tests the object.defineProperty method - ES 15.2.3.6
+
+// Flags: --allow-natives-syntax
+
+// Check that an exception is thrown when null is passed as object.
+try {
+ Object.defineProperty(null, null, null);
+ assertTrue(false);
+} catch (e) {
+ assertTrue(/called on non-object/.test(e));
+}
+
+// Check that an exception is thrown when undefined is passed as object.
+try {
+ Object.defineProperty(undefined, undefined, undefined);
+ assertTrue(false);
+} catch (e) {
+ assertTrue(/called on non-object/.test(e));
+}
+
+// Check that an exception is thrown when non-object is passed as object.
+try {
+ Object.defineProperty(0, "foo", undefined);
+ assertTrue(false);
+} catch (e) {
+ assertTrue(/called on non-object/.test(e));
+}
+
+// Object
+var obj1 = {};
+
+// Values
+var val1 = 0;
+var val2 = 0;
+var val3 = 0;
+
+// Descriptors
+var emptyDesc = {};
+
+var accessorConfigurable = {
+ set: function() { val1++; },
+ get: function() { return val1; },
+ configurable: true
+};
+
+var accessorNoConfigurable = {
+ set: function() { val2++; },
+ get: function() { return val2; },
+ configurable: false
+};
+
+var accessorOnlySet = {
+ set: function() { val3++; },
+ configurable: true
+};
+
+var accessorOnlyGet = {
+ get: function() { return val3; },
+ configurable: true
+};
+
+var accessorDefault = {set: function(){} };
+
+var dataConfigurable = { value: 1000, configurable: true };
+
+var dataNoConfigurable = { value: 2000, configurable: false };
+
+var dataWritable = { value: 3000, writable: true};
+
+
+// Check that we can't add property with undefined attributes.
+try {
+ Object.defineProperty(obj1, "foo", undefined);
+ assertTrue(false);
+} catch (e) {
+ assertTrue(/must be an object/.test(e));
+}
+
+// Make sure that we can add a property with an empty descriptor and
+// that it has the default descriptor values.
+Object.defineProperty(obj1, "foo", emptyDesc);
+
+// foo should be undefined as it has no get, set or value
+assertEquals(undefined, obj1.foo);
+
+// We should, however, be able to retrieve the propertydescriptor which should
+// have all default values (according to 8.6.1).
+var desc = Object.getOwnPropertyDescriptor(obj1, "foo");
+assertFalse(desc.configurable);
+assertFalse(desc.enumerable);
+assertFalse(desc.writable);
+assertEquals(desc.get, undefined);
+assertEquals(desc.set, undefined);
+assertEquals(desc.value, undefined);
+
+// Make sure that getOwnPropertyDescriptor does not return a descriptor
+// with default values if called with non existing property (otherwise
+// the test above is invalid).
+desc = Object.getOwnPropertyDescriptor(obj1, "bar");
+assertEquals(desc, undefined);
+
+// Make sure that foo can't be reset (as configurable is false).
+try {
+ Object.defineProperty(obj1, "foo", accessorConfigurable);
+} catch (e) {
+ assertTrue(/Cannot redefine property/.test(e));
+}
+
+
+// Accessor properties
+
+Object.defineProperty(obj1, "bar", accessorConfigurable);
+desc = Object.getOwnPropertyDescriptor(obj1, "bar");
+assertTrue(desc.configurable);
+assertFalse(desc.enumerable);
+assertEquals(desc.writable, undefined);
+assertEquals(desc.get, accessorConfigurable.get);
+assertEquals(desc.set, accessorConfigurable.set);
+assertEquals(desc.value, undefined);
+assertEquals(1, obj1.bar = 1);
+assertEquals(1, val1);
+assertEquals(1, obj1.bar = 1);
+assertEquals(2, val1);
+assertEquals(2, obj1.bar);
+
+// Redefine bar with non configurable test
+Object.defineProperty(obj1, "bar", accessorNoConfigurable);
+desc = Object.getOwnPropertyDescriptor(obj1, "bar");
+assertFalse(desc.configurable);
+assertFalse(desc.enumerable);
+assertEquals(desc.writable, undefined);
+assertEquals(desc.get, accessorNoConfigurable.get);
+assertEquals(desc.set, accessorNoConfigurable.set);
+assertEquals(desc.value, undefined);
+assertEquals(1, obj1.bar = 1);
+assertEquals(2, val1);
+assertEquals(1, val2);
+assertEquals(1, obj1.bar = 1)
+assertEquals(2, val1);
+assertEquals(2, val2);
+assertEquals(2, obj1.bar);
+
+// Try to redefine bar again - should fail as configurable is false.
+try {
+ Object.defineProperty(obj1, "bar", accessorConfigurable);
+ assertTrue(false);
+} catch(e) {
+ assertTrue(/Cannot redefine property/.test(e));
+}
+
+// Try to redefine bar again using the data descriptor - should fail.
+try {
+ Object.defineProperty(obj1, "bar", dataConfigurable);
+ assertTrue(false);
+} catch(e) {
+ assertTrue(/Cannot redefine property/.test(e));
+}
+
+// Redefine using same descriptor - should succeed.
+Object.defineProperty(obj1, "bar", accessorNoConfigurable);
+desc = Object.getOwnPropertyDescriptor(obj1, "bar");
+assertFalse(desc.configurable);
+assertFalse(desc.enumerable);
+assertEquals(desc.writable, undefined);
+assertEquals(desc.get, accessorNoConfigurable.get);
+assertEquals(desc.set, accessorNoConfigurable.set);
+assertEquals(desc.value, undefined);
+assertEquals(1, obj1.bar = 1);
+assertEquals(2, val1);
+assertEquals(3, val2);
+assertEquals(1, obj1.bar = 1)
+assertEquals(2, val1);
+assertEquals(4, val2);
+assertEquals(4, obj1.bar);
+
+// Define an accessor that has only a setter
+Object.defineProperty(obj1, "setOnly", accessorOnlySet);
+desc = Object.getOwnPropertyDescriptor(obj1, "setOnly");
+assertTrue(desc.configurable);
+assertFalse(desc.enumerable);
+assertEquals(desc.set, accessorOnlySet.set);
+assertEquals(desc.writable, undefined);
+assertEquals(desc.value, undefined);
+assertEquals(desc.get, undefined);
+assertEquals(1, obj1.setOnly = 1);
+assertEquals(1, val3);
+
+// Add a getter - should not touch the setter
+Object.defineProperty(obj1, "setOnly", accessorOnlyGet);
+desc = Object.getOwnPropertyDescriptor(obj1, "setOnly");
+assertTrue(desc.configurable);
+assertFalse(desc.enumerable);
+assertEquals(desc.get, accessorOnlyGet.get);
+assertEquals(desc.set, accessorOnlySet.set);
+assertEquals(desc.writable, undefined);
+assertEquals(desc.value, undefined);
+assertEquals(1, obj1.setOnly = 1);
+assertEquals(2, val3);
+
+// The above should also work if redefining just a getter or setter on
+// an existing property with both a getter and a setter.
+Object.defineProperty(obj1, "both", accessorConfigurable);
+
+Object.defineProperty(obj1, "both", accessorOnlySet);
+desc = Object.getOwnPropertyDescriptor(obj1, "both");
+assertTrue(desc.configurable);
+assertFalse(desc.enumerable);
+assertEquals(desc.set, accessorOnlySet.set);
+assertEquals(desc.get, accessorConfigurable.get);
+assertEquals(desc.writable, undefined);
+assertEquals(desc.value, undefined);
+assertEquals(1, obj1.both = 1);
+assertEquals(3, val3);
+
+
+// Data properties
+
+Object.defineProperty(obj1, "foobar", dataConfigurable);
+desc = Object.getOwnPropertyDescriptor(obj1, "foobar");
+assertEquals(obj1.foobar, 1000);
+assertEquals(desc.value, 1000);
+assertTrue(desc.configurable);
+assertFalse(desc.writable);
+assertFalse(desc.enumerable);
+assertEquals(desc.get, undefined);
+assertEquals(desc.set, undefined);
+//Try writing to non writable attribute - should remain 1000
+obj1.foobar = 1001;
+assertEquals(obj1.foobar, 1000);
+
+
+// Redefine to writable descriptor - now writing to foobar should be allowed
+Object.defineProperty(obj1, "foobar", dataWritable);
+desc = Object.getOwnPropertyDescriptor(obj1, "foobar");
+assertEquals(obj1.foobar, 3000);
+assertEquals(desc.value, 3000);
+// Note that since dataWritable does not define configurable the configurable
+// setting from the redefined property (in this case true) is used.
+assertTrue(desc.configurable);
+assertTrue(desc.writable);
+assertFalse(desc.enumerable);
+assertEquals(desc.get, undefined);
+assertEquals(desc.set, undefined);
+// Writing to the property should now be allowed
+obj1.foobar = 1001;
+assertEquals(obj1.foobar, 1001);
+
+
+// Redefine with non configurable data property.
+Object.defineProperty(obj1, "foobar", dataNoConfigurable);
+desc = Object.getOwnPropertyDescriptor(obj1, "foobar");
+assertEquals(obj1.foobar, 2000);
+assertEquals(desc.value, 2000);
+assertFalse(desc.configurable);
+assertFalse(desc.writable);
+assertFalse(desc.enumerable);
+assertEquals(desc.get, undefined);
+assertEquals(desc.set, undefined);
+
+// Try redefine again - shold fail because configurable is now false.
+try {
+ Object.defineProperty(obj1, "foobar", dataConfigurable);
+ assertTrue(false);
+} catch (e) {
+ assertTrue(/Cannot redefine property/.test(e));
+}
+
+// Try redefine again with accessor property - shold also fail.
+try {
+ Object.defineProperty(obj1, "foobar", dataConfigurable);
+ assertTrue(false);
+} catch (e) {
+ assertTrue(/Cannot redefine property/.test(e));
+}
+
+
+// Redifine with the same descriptor - should succeed (step 6).
+Object.defineProperty(obj1, "foobar", dataNoConfigurable);
+desc = Object.getOwnPropertyDescriptor(obj1, "foobar");
+assertEquals(obj1.foobar, 2000);
+assertEquals(desc.value, 2000);
+assertFalse(desc.configurable);
+assertFalse(desc.writable);
+assertFalse(desc.enumerable);
+assertEquals(desc.get, undefined);
+assertEquals(desc.set, undefined);
+
+
+// New object
+var obj2 = {};
+
+// Make accessor - redefine to data
+Object.defineProperty(obj2, "foo", accessorConfigurable);
+
+// Redefine to data property
+Object.defineProperty(obj2, "foo", dataConfigurable);
+desc = Object.getOwnPropertyDescriptor(obj2, "foo");
+assertEquals(obj2.foo, 1000);
+assertEquals(desc.value, 1000);
+assertTrue(desc.configurable);
+assertFalse(desc.writable);
+assertFalse(desc.enumerable);
+assertEquals(desc.get, undefined);
+assertEquals(desc.set, undefined);
+
+
+// Redefine back to accessor
+Object.defineProperty(obj2, "foo", accessorConfigurable);
+desc = Object.getOwnPropertyDescriptor(obj2, "foo");
+assertTrue(desc.configurable);
+assertFalse(desc.enumerable);
+assertEquals(desc.writable, undefined);
+assertEquals(desc.get, accessorConfigurable.get);
+assertEquals(desc.set, accessorConfigurable.set);
+assertEquals(desc.value, undefined);
+assertEquals(1, obj2.foo = 1);
+assertEquals(3, val1);
+assertEquals(4, val2);
+assertEquals(3, obj2.foo);
+
+// Make data - redefine to accessor
+Object.defineProperty(obj2, "bar", dataConfigurable)
+
+// Redefine to accessor property
+Object.defineProperty(obj2, "bar", accessorConfigurable);
+desc = Object.getOwnPropertyDescriptor(obj2, "bar");
+assertTrue(desc.configurable);
+assertFalse(desc.enumerable);
+assertEquals(desc.writable, undefined);
+assertEquals(desc.get, accessorConfigurable.get);
+assertEquals(desc.set, accessorConfigurable.set);
+assertEquals(desc.value, undefined);
+assertEquals(1, obj2.bar = 1);
+assertEquals(4, val1);
+assertEquals(4, val2);
+assertEquals(4, obj2.foo);
+
+// Redefine back to data property
+Object.defineProperty(obj2, "bar", dataConfigurable);
+desc = Object.getOwnPropertyDescriptor(obj2, "bar");
+assertEquals(obj2.bar, 1000);
+assertEquals(desc.value, 1000);
+assertTrue(desc.configurable);
+assertFalse(desc.writable);
+assertFalse(desc.enumerable);
+assertEquals(desc.get, undefined);
+assertEquals(desc.set, undefined);
+
+
+// Redefinition of an accessor defined using __defineGetter__ and
+// __defineSetter__
+function get(){return this.x}
+function set(x){this.x=x};
+
+var obj3 = {x:1000};
+obj3.__defineGetter__("foo", get);
+obj3.__defineSetter__("foo", set);
+
+desc = Object.getOwnPropertyDescriptor(obj3, "foo");
+assertTrue(desc.configurable);
+assertTrue(desc.enumerable);
+assertEquals(desc.writable, undefined);
+assertEquals(desc.get, get);
+assertEquals(desc.set, set);
+assertEquals(desc.value, undefined);
+assertEquals(1, obj3.foo = 1);
+assertEquals(1, obj3.x);
+assertEquals(1, obj3.foo);
+
+// Redefine to accessor property (non configurable) - note that enumerable
+// which we do not redefine should remain the same (true).
+Object.defineProperty(obj3, "foo", accessorNoConfigurable);
+desc = Object.getOwnPropertyDescriptor(obj3, "foo");
+assertFalse(desc.configurable);
+assertTrue(desc.enumerable);
+assertEquals(desc.writable, undefined);
+assertEquals(desc.get, accessorNoConfigurable.get);
+assertEquals(desc.set, accessorNoConfigurable.set);
+assertEquals(desc.value, undefined);
+assertEquals(1, obj3.foo = 1);
+assertEquals(5, val2);
+assertEquals(5, obj3.foo);
+
+
+obj3.__defineGetter__("bar", get);
+obj3.__defineSetter__("bar", set);
+
+
+// Redefine back to data property
+Object.defineProperty(obj3, "bar", dataConfigurable);
+desc = Object.getOwnPropertyDescriptor(obj3, "bar");
+assertEquals(obj3.bar, 1000);
+assertEquals(desc.value, 1000);
+assertTrue(desc.configurable);
+assertFalse(desc.writable);
+assertTrue(desc.enumerable);
+assertEquals(desc.get, undefined);
+assertEquals(desc.set, undefined);
+
+
+var obj4 = {};
+var func = function (){return 42;};
+obj4.bar = func;
+assertEquals(42, obj4.bar());
+
+Object.defineProperty(obj4, "bar", accessorConfigurable);
+desc = Object.getOwnPropertyDescriptor(obj4, "bar");
+assertTrue(desc.configurable);
+assertTrue(desc.enumerable);
+assertEquals(desc.writable, undefined);
+assertEquals(desc.get, accessorConfigurable.get);
+assertEquals(desc.set, accessorConfigurable.set);
+assertEquals(desc.value, undefined);
+assertEquals(1, obj4.bar = 1);
+assertEquals(5, val1);
+assertEquals(5, obj4.bar);
+
+// Make sure an error is thrown when trying to access to redefined function
+try {
+ obj4.bar();
+ assertTrue(false);
+} catch (e) {
+ assertTrue(/is not a function/.test(e));
+}
+
+
+// Test runtime calls to DefineOrRedefineDataProperty and
+// DefineOrRedefineAccessorProperty - make sure we don't
+// crash
+try {
+ %DefineOrRedefineAccessorProperty(0, 0, 0, 0, 0);
+} catch (e) {
+ assertTrue(/illegal access/.test(e));
+}
+
+try {
+ %DefineOrRedefineDataProperty(0, 0, 0, 0);
+} catch (e) {
+ assertTrue(/illegal access/.test(e));
+}
+
+try {
+ %DefineOrRedefineDataProperty(null, null, null, null);
+} catch (e) {
+ assertTrue(/illegal access/.test(e));
+}
+
+try {
+ %DefineOrRedefineAccessorProperty(null, null, null, null, null);
+} catch (e) {
+ assertTrue(/illegal access/.test(e));
+}
+
+try {
+ %DefineOrRedefineDataProperty({}, null, null, null);
+} catch (e) {
+ assertTrue(/illegal access/.test(e));
+}
+
+// Defining properties null should fail even when we have
+// other allowed values
+try {
+ %DefineOrRedefineAccessorProperty(null, 'foo', 0, func, 0);
+} catch (e) {
+ assertTrue(/illegal access/.test(e));
+}
+
+try {
+ %DefineOrRedefineDataProperty(null, 'foo', 0, 0);
+} catch (e) {
+ assertTrue(/illegal access/.test(e));
+}
diff --git a/test/mjsunit/object-get-own-property-names.js b/test/mjsunit/object-get-own-property-names.js
index f52cee2f..33aa85ef 100644
--- a/test/mjsunit/object-get-own-property-names.js
+++ b/test/mjsunit/object-get-own-property-names.js
@@ -57,6 +57,8 @@ propertyNames.sort();
assertEquals(3, propertyNames.length);
assertEquals("0", propertyNames[0]);
assertEquals("1", propertyNames[1]);
+assertEquals("string", typeof propertyNames[0]);
+assertEquals("string", typeof propertyNames[1]);
assertEquals("length", propertyNames[2]);
// Check that no proto properties are returned.
diff --git a/test/mjsunit/regress/regress-603.js b/test/mjsunit/regress/regress-603.js
new file mode 100644
index 00000000..7d4c3229
--- /dev/null
+++ b/test/mjsunit/regress/regress-603.js
@@ -0,0 +1,49 @@
+// Copyright 2010 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.
+
+// Calling non-objects directly or via Function.prototype.call should
+// not mess up the stack.
+// http://code.google.com/p/v8/issues/detail?id=603
+
+function test0() {
+ var re = /b../;
+ return re('abcdefghijklm') + 'z';
+}
+assertEquals('bcdz', test0());
+
+var re1 = /c../;
+re1.call = Function.prototype.call;
+var test1 = re1.call(null, 'abcdefghijklm') + 'z';
+assertEquals('cdez', test1);
+
+var re2 = /d../;
+var test2 = Function.prototype.call.call(re2, null, 'abcdefghijklm') + 'z';
+assertEquals('defz', test2);
+
+var re3 = /e../;
+var test3 = Function.prototype.call.apply(re3, [null, 'abcdefghijklm']) + 'z';
+assertEquals('efgz', test3);
diff --git a/test/mjsunit/regress/regress-612.js b/test/mjsunit/regress/regress-612.js
new file mode 100644
index 00000000..aee6d530
--- /dev/null
+++ b/test/mjsunit/regress/regress-612.js
@@ -0,0 +1,44 @@
+// Copyright 2010 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.
+
+// Tests intercation between __defineGetter__/__defineSetter and fast and slow
+// mode of the objects due to series of assignments optimization.
+// (See http://code.google.com/p/v8/issues/detail?id=612)
+
+obj = {}
+
+// Define getter which currently moves object into slow mode.
+obj.__defineGetter__('foobar', function() { return 42; })
+
+// Starts initialization block mode. And turns object into slow mode.
+obj.a = 1
+obj.b = 2;
+obj.c = 3;
+// Now object is turned into fast mode, but it has getter defined above...
+
+// Now assert is triggered.
+obj.__defineGetter__('foobar', function() { return 42; })
diff --git a/test/mjsunit/setter-on-constructor-prototype.js b/test/mjsunit/setter-on-constructor-prototype.js
new file mode 100644
index 00000000..d5718f9c
--- /dev/null
+++ b/test/mjsunit/setter-on-constructor-prototype.js
@@ -0,0 +1,111 @@
+// Copyright 2008 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.
+
+// Flags: --allow-natives-syntax
+
+function RunTest(ensure_fast_case) {
+ function C1() {
+ this.x = 23;
+ };
+ C1.prototype = { set x(value) { this.y = 23; } };
+ if (ensure_fast_case) {
+ %ToFastProperties(C1.prototype);
+ }
+
+ for (var i = 0; i < 10; i++) {
+ var c1 = new C1();
+ assertEquals("undefined", typeof c1.x);
+ assertEquals(23, c1.y);
+ }
+
+
+ function C2() {
+ this.x = 23;
+ };
+ C2.prototype = { };
+ C2.prototype.__proto__ = { set x(value) { this.y = 23; } };
+ if (ensure_fast_case) {
+ %ToFastProperties(C2.prototype.__proto__)
+ }
+
+ for (var i = 0; i < 10; i++) {
+ var c2 = new C2();
+ assertEquals("undefined", typeof c2.x);
+ assertEquals(23, c2.y);
+ }
+
+
+ function C3() {
+ this.x = 23;
+ };
+ C3.prototype = { };
+ C3.prototype.__defineSetter__('x', function(value) { this.y = 23; });
+ if (ensure_fast_case) {
+ %ToFastProperties(C3.prototype);
+ }
+
+ for (var i = 0; i < 10; i++) {
+ var c3 = new C3();
+ assertEquals("undefined", typeof c3.x);
+ assertEquals(23, c3.y);
+ }
+
+
+ function C4() {
+ this.x = 23;
+ };
+ C4.prototype = { };
+ C4.prototype.__proto__ = { };
+ C4.prototype.__proto__.__defineSetter__('x', function(value) { this.y = 23; });
+ if (ensure_fast_case) {
+ %ToFastProperties(C4.prototype.__proto__);
+ }
+
+ for (var i = 0; i < 10; i++) {
+ var c4 = new C4();
+ assertEquals("undefined", typeof c4.x);
+ assertEquals(23, c4.y);
+ }
+
+
+ function D() {
+ this.x = 23;
+ };
+ D.prototype = 1;
+ if (ensure_fast_case) {
+ %ToFastProperties(D.prototype);
+ }
+
+ for (var i = 0; i < 10; i++) {
+ var d = new D();
+ assertEquals(23, d.x);
+ assertEquals("undefined", typeof d.y);
+ }
+}
+
+RunTest(false);
+RunTest(true);
diff --git a/test/mjsunit/substr.js b/test/mjsunit/substr.js
index f69a9c04..f69a9c04 100644..100755
--- a/test/mjsunit/substr.js
+++ b/test/mjsunit/substr.js
diff --git a/test/mjsunit/tools/tickprocessor-test-func-info.log b/test/mjsunit/tools/tickprocessor-test-func-info.log
new file mode 100644
index 00000000..29a12f6f
--- /dev/null
+++ b/test/mjsunit/tools/tickprocessor-test-func-info.log
@@ -0,0 +1,13 @@
+shared-library,"shell",0x08048000,0x081ee000
+shared-library,"/lib32/libm-2.7.so",0xf7db6000,0xf7dd9000
+shared-library,"ffffe000-fffff000",0xffffe000,0xfffff000
+profiler,"begin",1
+code-creation,Stub,0x424260,348,"CompareStub_GE"
+code-creation,LazyCompile,0x2a8100,18535,"DrawQube 3d-cube.js:188"
+function-creation,0x2d11b8,0x2a8100
+code-creation,LazyCompile,0x480100,3908,"DrawLine 3d-cube.js:17"
+function-creation,0x2d0f7c,0x480100
+tick,0x424284,0xbfffeea0,0x2d0f7c,0,0x2aaaa5
+tick,0x42429f,0xbfffed88,0x2d0f7c,0,0x2aacb4
+tick,0x48063d,0xbfffec7c,0x2d0f7c,0,0x2aaec6
+profiler,"end"
diff --git a/tools/gyp/v8.gyp b/tools/gyp/v8.gyp
index f2d1b98e..f190598c 100644
--- a/tools/gyp/v8.gyp
+++ b/tools/gyp/v8.gyp
@@ -269,7 +269,6 @@
'../../src/execution.h',
'../../src/factory.cc',
'../../src/factory.h',
- '../../src/fast-codegen.cc',
'../../src/fast-codegen.h',
'../../src/flag-definitions.h',
'../../src/flags.cc',
@@ -308,6 +307,8 @@
'../../src/jsregexp.h',
'../../src/list-inl.h',
'../../src/list.h',
+ '../../src/liveedit.cc',
+ '../../src/liveedit.h',
'../../src/log-inl.h',
'../../src/log-utils.cc',
'../../src/log-utils.h',
@@ -320,6 +321,7 @@
'../../src/messages.cc',
'../../src/messages.h',
'../../src/natives.h',
+ '../../src/number-info.h',
'../../src/objects-debug.cc',
'../../src/objects-inl.h',
'../../src/objects.cc',
@@ -401,6 +403,7 @@
'../../src/arm',
],
'sources': [
+ '../../src/fast-codegen.cc',
'../../src/arm/assembler-arm-inl.h',
'../../src/arm/assembler-arm.cc',
'../../src/arm/assembler-arm.h',
@@ -452,6 +455,7 @@
'../../src/ia32/debug-ia32.cc',
'../../src/ia32/disasm-ia32.cc',
'../../src/ia32/fast-codegen-ia32.cc',
+ '../../src/ia32/fast-codegen-ia32.h',
'../../src/ia32/frames-ia32.cc',
'../../src/ia32/frames-ia32.h',
'../../src/ia32/full-codegen-ia32.cc',
@@ -472,6 +476,7 @@
'../../src/x64',
],
'sources': [
+ '../../src/fast-codegen.cc',
'../../src/x64/assembler-x64-inl.h',
'../../src/x64/assembler-x64.cc',
'../../src/x64/assembler-x64.h',
@@ -556,11 +561,11 @@
'../../src/math.js',
'../../src/messages.js',
'../../src/apinatives.js',
- '../../src/debug-delay.js',
- '../../src/mirror-delay.js',
- '../../src/date-delay.js',
- '../../src/json-delay.js',
- '../../src/regexp-delay.js',
+ '../../src/debug-debugger.js',
+ '../../src/mirror-debugger.js',
+ '../../src/date.js',
+ '../../src/json.js',
+ '../../src/regexp.js',
'../../src/macros.py',
],
},
diff --git a/tools/tickprocessor.js b/tools/tickprocessor.js
index 40cee8a0..a3e14c3a 100644
--- a/tools/tickprocessor.js
+++ b/tools/tickprocessor.js
@@ -160,10 +160,6 @@ function TickProcessor(
processor: this.processHeapSampleEnd },
'heap-js-prod-item': { parsers: [null, 'var-args'],
processor: this.processJSProducer, backrefs: true },
- 'PAGE-LOAD-START': { parsers: [null, null],
- processor: this.processPageLoadStart },
- 'PAGE-LOAD-END': { parsers: [null, null],
- processor: this.processPageLoadEnd },
// Ignored events.
'profiler': null,
'heap-sample-stats': null,
@@ -180,7 +176,6 @@ function TickProcessor(
this.stateFilter_ = stateFilter;
this.snapshotLogProcessor_ = snapshotLogProcessor;
this.deserializedEntriesNames_ = [];
- this.handle_ticks_ = false;
var ticks = this.ticks_ =
{ total: 0, unaccounted: 0, excluded: 0, gc: 0 };
@@ -344,7 +339,6 @@ TickProcessor.prototype.includeTick = function(vmState) {
TickProcessor.prototype.processTick = function(pc, sp, func, vmState, stack) {
- if (!this.handle_ticks_) return;
this.ticks_.total++;
if (vmState == TickProcessor.VmStates.GC) this.ticks_.gc++;
if (!this.includeTick(vmState)) {
@@ -392,16 +386,6 @@ TickProcessor.prototype.processHeapSampleEnd = function(space, state) {
};
-TickProcessor.prototype.processPageLoadStart = function() {
- this.handle_ticks_ = true;
-};
-
-
-TickProcessor.prototype.processPageLoadEnd = function() {
- this.handle_ticks_ = false;
-};
-
-
TickProcessor.prototype.processJSProducer = function(constructor, stack) {
if (!this.currentProducerProfile_) return;
if (stack.length == 0) return;
diff --git a/tools/visual_studio/v8_base.vcproj b/tools/visual_studio/v8_base.vcproj
index e58e8ff3..85935288 100644
--- a/tools/visual_studio/v8_base.vcproj
+++ b/tools/visual_studio/v8_base.vcproj
@@ -401,7 +401,7 @@
>
</File>
<File
- RelativePath="..\..\src\fast-codegen.cc"
+ RelativePath="..\..\src\ia32\fast-codegen-ia32.h"
>
</File>
<File
@@ -577,6 +577,14 @@
>
</File>
<File
+ RelativePath="..\..\src\liveedit.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\liveedit.h"
+ >
+ </File>
+ <File
RelativePath="..\..\src\log.cc"
>
</File>
@@ -633,6 +641,10 @@
>
</File>
<File
+ RelativePath="..\..\src\number-info.h"
+ >
+ </File>
+ <File
RelativePath="..\..\src\objects-debug.cc"
>
<FileConfiguration
diff --git a/tools/visual_studio/v8_base_arm.vcproj b/tools/visual_studio/v8_base_arm.vcproj
index 4b37b538..2602be45 100644
--- a/tools/visual_studio/v8_base_arm.vcproj
+++ b/tools/visual_studio/v8_base_arm.vcproj
@@ -581,6 +581,14 @@
>
</File>
<File
+ RelativePath="..\..\src\liveedit.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\liveedit.h"
+ >
+ </File>
+ <File
RelativePath="..\..\src\log.cc"
>
</File>
@@ -637,6 +645,10 @@
>
</File>
<File
+ RelativePath="..\..\src\number-info.h"
+ >
+ </File>
+ <File
RelativePath="..\..\src\objects-debug.cc"
>
<FileConfiguration
diff --git a/tools/visual_studio/v8_base_x64.vcproj b/tools/visual_studio/v8_base_x64.vcproj
index b6d5c7d8..d3f55c6a 100644
--- a/tools/visual_studio/v8_base_x64.vcproj
+++ b/tools/visual_studio/v8_base_x64.vcproj
@@ -578,6 +578,14 @@
>
</File>
<File
+ RelativePath="..\..\src\liveedit.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\src\liveedit.h"
+ >
+ </File>
+ <File
RelativePath="..\..\src\log.cc"
>
</File>
@@ -634,6 +642,10 @@
>
</File>
<File
+ RelativePath="..\..\src\number-info.h"
+ >
+ </File>
+ <File
RelativePath="..\..\src\objects-debug.cc"
>
<FileConfiguration