summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2010-07-07 19:47:38 +0200
committerRyan Dahl <ry@tinyclouds.org>2010-07-07 19:47:38 +0200
commitfacb904c5db23345a693bf3aa02b1497af12fddd (patch)
tree57de363b1a8e39b25d3e020a88f87f832f1f2aa5
parent70a8fb3763c92337bbc6a6138dd0c320134b847c (diff)
downloadnode-new-facb904c5db23345a693bf3aa02b1497af12fddd.tar.gz
Upgrade V8 to 2.2.23
-rw-r--r--deps/v8/ChangeLog36
-rw-r--r--deps/v8/include/v8.h388
-rw-r--r--deps/v8/src/api.h10
-rw-r--r--deps/v8/src/arm/assembler-arm.cc10
-rw-r--r--deps/v8/src/arm/assembler-arm.h4
-rw-r--r--deps/v8/src/arm/codegen-arm.cc679
-rw-r--r--deps/v8/src/arm/codegen-arm.h5
-rw-r--r--deps/v8/src/arm/disasm-arm.cc9
-rw-r--r--deps/v8/src/arm/full-codegen-arm.cc8
-rw-r--r--deps/v8/src/arm/ic-arm.cc179
-rw-r--r--deps/v8/src/arm/macro-assembler-arm.cc61
-rw-r--r--deps/v8/src/arm/macro-assembler-arm.h28
-rw-r--r--deps/v8/src/arm/simulator-arm.cc9
-rw-r--r--deps/v8/src/arm/stub-cache-arm.cc3
-rw-r--r--deps/v8/src/builtins.cc5
-rw-r--r--deps/v8/src/builtins.h1
-rw-r--r--deps/v8/src/codegen.cc14
-rw-r--r--deps/v8/src/codegen.h25
-rw-r--r--deps/v8/src/date.js194
-rw-r--r--deps/v8/src/debug-debugger.js16
-rw-r--r--deps/v8/src/debug.cc32
-rw-r--r--deps/v8/src/debug.h22
-rw-r--r--deps/v8/src/factory.cc6
-rw-r--r--deps/v8/src/factory.h12
-rw-r--r--deps/v8/src/frames.cc2
-rw-r--r--deps/v8/src/globals.h6
-rw-r--r--deps/v8/src/heap.cc31
-rw-r--r--deps/v8/src/ia32/codegen-ia32.cc248
-rw-r--r--deps/v8/src/ia32/full-codegen-ia32.cc8
-rw-r--r--deps/v8/src/ia32/ic-ia32.cc201
-rw-r--r--deps/v8/src/ia32/macro-assembler-ia32.cc129
-rw-r--r--deps/v8/src/ia32/macro-assembler-ia32.h39
-rw-r--r--deps/v8/src/ia32/stub-cache-ia32.cc294
-rw-r--r--deps/v8/src/ic-inl.h33
-rw-r--r--deps/v8/src/ic.cc157
-rw-r--r--deps/v8/src/ic.h12
-rw-r--r--deps/v8/src/liveedit-debugger.js169
-rw-r--r--deps/v8/src/liveedit.cc20
-rw-r--r--deps/v8/src/macros.py5
-rw-r--r--deps/v8/src/messages.js4
-rw-r--r--deps/v8/src/objects-inl.h68
-rw-r--r--deps/v8/src/objects.cc59
-rw-r--r--deps/v8/src/objects.h57
-rw-r--r--deps/v8/src/parser.cc17
-rw-r--r--deps/v8/src/rewriter.cc93
-rw-r--r--deps/v8/src/runtime.cc157
-rw-r--r--deps/v8/src/runtime.h1
-rw-r--r--deps/v8/src/spaces.cc4
-rw-r--r--deps/v8/src/stub-cache.cc124
-rw-r--r--deps/v8/src/stub-cache.h36
-rw-r--r--deps/v8/src/v8-counters.h4
-rw-r--r--deps/v8/src/v8dll-main.cc39
-rw-r--r--deps/v8/src/v8natives.js25
-rw-r--r--deps/v8/src/version.cc2
-rw-r--r--deps/v8/src/x64/builtins-x64.cc4
-rw-r--r--deps/v8/src/x64/codegen-x64.cc322
-rw-r--r--deps/v8/src/x64/codegen-x64.h3
-rw-r--r--deps/v8/src/x64/frames-x64.h4
-rw-r--r--deps/v8/src/x64/full-codegen-x64.cc8
-rw-r--r--deps/v8/src/x64/ic-x64.cc194
-rw-r--r--deps/v8/src/x64/macro-assembler-x64.cc232
-rw-r--r--deps/v8/src/x64/macro-assembler-x64.h47
-rw-r--r--deps/v8/src/x64/register-allocator-x64-inl.h11
-rw-r--r--deps/v8/src/x64/register-allocator-x64.h2
-rw-r--r--deps/v8/src/x64/stub-cache-x64.cc3
-rw-r--r--deps/v8/src/x64/virtual-frame-x64.h7
-rw-r--r--deps/v8/test/cctest/test-api.cc84
-rw-r--r--deps/v8/test/cctest/test-debug.cc35
-rw-r--r--deps/v8/test/cctest/test-disasm-arm.cc5
-rwxr-xr-xdeps/v8/test/cctest/test-macro-assembler-x64.cc153
-rw-r--r--deps/v8/test/es5conform/es5conform.status11
-rw-r--r--deps/v8/test/mjsunit/call-stub.js51
-rw-r--r--deps/v8/test/mjsunit/date.js9
-rw-r--r--deps/v8/test/mjsunit/debug-liveedit-3.js3
-rw-r--r--deps/v8/test/mjsunit/debug-liveedit-breakpoints.js3
-rw-r--r--deps/v8/test/mjsunit/debug-liveedit-newsource.js3
-rw-r--r--deps/v8/test/mjsunit/fuzz-natives.js2
-rw-r--r--deps/v8/test/mjsunit/math-min-max.js11
-rw-r--r--deps/v8/test/mjsunit/math-pow.js129
-rw-r--r--deps/v8/test/mjsunit/object-prevent-extensions.js157
-rw-r--r--deps/v8/test/mjsunit/store-dictionary.js65
-rw-r--r--deps/v8/test/mjsunit/string-replace-with-empty.js57
-rw-r--r--deps/v8/test/mjsunit/value-of.js33
-rw-r--r--deps/v8/tools/gyp/v8.gyp57
-rwxr-xr-xdeps/v8/tools/js2c.py30
85 files changed, 4064 insertions, 1471 deletions
diff --git a/deps/v8/ChangeLog b/deps/v8/ChangeLog
index 8b58faa183..602ad80488 100644
--- a/deps/v8/ChangeLog
+++ b/deps/v8/ChangeLog
@@ -1,3 +1,28 @@
+2010-07-07: Version 2.2.23
+
+ API change: Convert Unicode code points outside the basic multilingual
+ plane to the replacement character. Previous behavior was to silently
+ truncate the value to 16 bits.
+
+ Fixed crash: handle all flat string types in regexp replace.
+
+ Prevent invalid pre-parsing data passed in through the API from
+ crashing V8.
+
+ Performance improvements on all platforms.
+
+2010-07-05: Version 2.2.22
+
+ Added ES5 Object.isExtensible and Object.preventExtensions.
+
+ Enabled building V8 as a DLL.
+
+ Fixed a bug in date code where -0 was not interpreted as 0
+ (issue 736).
+
+ Performance improvements on all platforms.
+
+
2010-06-30: Version 2.2.21
Fix bug in externalizing some ASCII strings (Chromium issue 47824).
@@ -13,18 +38,21 @@
Provide actual breakpoints locations in response to setBreakpoint
and listBreakpoints requests.
+
2010-06-28: Version 2.2.20
+
Fix bug with for-in on x64 platform (issue 748).
Fix crash bug on x64 platform (issue 756).
-
+
Fix bug in Object.getOwnPropertyNames. (chromium issue 41243).
- Fix a bug on ARM that caused the result of 1 << x to be
+ Fix a bug on ARM that caused the result of 1 << x to be
miscalculated for some inputs.
Performance improvements on all platforms.
+
2010-06-23: Version 2.2.19
Fix bug that causes the build to break when profillingsupport=off
@@ -64,11 +92,11 @@
2010-06-09: Version 2.2.16
- Removed the SetExternalStringDiposeCallback API. Changed the
+ Removed the SetExternalStringDiposeCallback API. Changed the
disposal of external string resources to call a virtual Dispose
method on the resource.
- Added support for more precise break points when debugging and
+ Added support for more precise break points when debugging and
stepping.
Memory usage improvements on all platforms.
diff --git a/deps/v8/include/v8.h b/deps/v8/include/v8.h
index b62561876c..ca4a247fe8 100644
--- a/deps/v8/include/v8.h
+++ b/deps/v8/include/v8.h
@@ -61,10 +61,6 @@ typedef unsigned __int64 uint64_t;
// the V8 DLL USING_V8_SHARED needs to be defined. When either building the V8
// static library or building a program which uses the V8 static library neither
// BUILDING_V8_SHARED nor USING_V8_SHARED should be defined.
-// The reason for having both V8EXPORT and V8EXPORT_INLINE is that classes which
-// have their code inside this header file need to have __declspec(dllexport)
-// when building the DLL but cannot have __declspec(dllimport) when building
-// a program which uses the DLL.
#if defined(BUILDING_V8_SHARED) && defined(USING_V8_SHARED)
#error both BUILDING_V8_SHARED and USING_V8_SHARED are set - please check the\
build configuration to ensure that at most one of these is set
@@ -72,13 +68,10 @@ typedef unsigned __int64 uint64_t;
#ifdef BUILDING_V8_SHARED
#define V8EXPORT __declspec(dllexport)
-#define V8EXPORT_INLINE __declspec(dllexport)
#elif USING_V8_SHARED
#define V8EXPORT __declspec(dllimport)
-#define V8EXPORT_INLINE
#else
#define V8EXPORT
-#define V8EXPORT_INLINE
#endif // BUILDING_V8_SHARED
#else // _WIN32
@@ -90,10 +83,8 @@ typedef unsigned __int64 uint64_t;
// export symbols when we are building a static library.
#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(V8_SHARED)
#define V8EXPORT __attribute__ ((visibility("default")))
-#define V8EXPORT_INLINE __attribute__ ((visibility("default")))
#else // defined(__GNUC__) && (__GNUC__ >= 4)
#define V8EXPORT
-#define V8EXPORT_INLINE
#endif // defined(__GNUC__) && (__GNUC__ >= 4)
#endif // _WIN32
@@ -185,7 +176,7 @@ typedef void (*WeakReferenceCallback)(Persistent<Value> object,
* behind the scenes and the same rules apply to these values as to
* their handles.
*/
-template <class T> class V8EXPORT_INLINE Handle {
+template <class T> class Handle {
public:
/**
@@ -196,7 +187,7 @@ template <class T> class V8EXPORT_INLINE Handle {
/**
* Creates a new handle for the specified value.
*/
- explicit Handle(T* val) : val_(val) { }
+ inline explicit Handle(T* val) : val_(val) { }
/**
* Creates a handle for the contents of the specified handle. This
@@ -221,16 +212,16 @@ template <class T> class V8EXPORT_INLINE Handle {
/**
* Returns true if the handle is empty.
*/
- bool IsEmpty() const { return val_ == 0; }
+ inline bool IsEmpty() const { return val_ == 0; }
- T* operator->() const { return val_; }
+ inline T* operator->() const { return val_; }
- T* operator*() const { return val_; }
+ inline T* operator*() const { return val_; }
/**
* Sets the handle to be empty. IsEmpty() will then return true.
*/
- void Clear() { this->val_ = 0; }
+ inline void Clear() { this->val_ = 0; }
/**
* Checks whether two handles are the same.
@@ -238,7 +229,7 @@ template <class T> class V8EXPORT_INLINE Handle {
* to which they refer are identical.
* The handles' references are not checked.
*/
- template <class S> bool operator==(Handle<S> that) const {
+ template <class S> inline bool operator==(Handle<S> that) const {
internal::Object** a = reinterpret_cast<internal::Object**>(**this);
internal::Object** b = reinterpret_cast<internal::Object**>(*that);
if (a == 0) return b == 0;
@@ -252,7 +243,7 @@ template <class T> class V8EXPORT_INLINE Handle {
* the objects to which they refer are different.
* The handles' references are not checked.
*/
- template <class S> bool operator!=(Handle<S> that) const {
+ template <class S> inline bool operator!=(Handle<S> that) const {
return !operator==(that);
}
@@ -281,7 +272,7 @@ template <class T> class V8EXPORT_INLINE Handle {
* handle scope are destroyed when the handle scope is destroyed. Hence it
* is not necessary to explicitly deallocate local handles.
*/
-template <class T> class V8EXPORT_INLINE Local : public Handle<T> {
+template <class T> class Local : public Handle<T> {
public:
inline Local();
template <class S> inline Local(Local<S> that)
@@ -332,7 +323,7 @@ template <class T> class V8EXPORT_INLINE Local : public Handle<T> {
* different storage cells but rather two references to the same
* storage cell.
*/
-template <class T> class V8EXPORT_INLINE Persistent : public Handle<T> {
+template <class T> class Persistent : public Handle<T> {
public:
/**
@@ -563,11 +554,12 @@ class V8EXPORT ScriptData { // NOLINT
/**
* The origin, within a file, of a script.
*/
-class V8EXPORT ScriptOrigin {
+class ScriptOrigin {
public:
- ScriptOrigin(Handle<Value> resource_name,
- Handle<Integer> resource_line_offset = Handle<Integer>(),
- Handle<Integer> resource_column_offset = Handle<Integer>())
+ inline ScriptOrigin(
+ Handle<Value> resource_name,
+ Handle<Integer> resource_line_offset = Handle<Integer>(),
+ Handle<Integer> resource_column_offset = Handle<Integer>())
: resource_name_(resource_name),
resource_line_offset_(resource_line_offset),
resource_column_offset_(resource_column_offset) { }
@@ -841,30 +833,30 @@ class V8EXPORT StackFrame {
/**
* The superclass of all JavaScript values and objects.
*/
-class V8EXPORT Value : public Data {
+class Value : public Data {
public:
/**
* Returns true if this value is the undefined value. See ECMA-262
* 4.3.10.
*/
- bool IsUndefined() const;
+ V8EXPORT bool IsUndefined() const;
/**
* Returns true if this value is the null value. See ECMA-262
* 4.3.11.
*/
- bool IsNull() const;
+ V8EXPORT bool IsNull() const;
/**
* Returns true if this value is true.
*/
- bool IsTrue() const;
+ V8EXPORT bool IsTrue() const;
/**
* Returns true if this value is false.
*/
- bool IsFalse() const;
+ V8EXPORT bool IsFalse() const;
/**
* Returns true if this value is an instance of the String type.
@@ -875,92 +867,92 @@ class V8EXPORT Value : public Data {
/**
* Returns true if this value is a function.
*/
- bool IsFunction() const;
+ V8EXPORT bool IsFunction() const;
/**
* Returns true if this value is an array.
*/
- bool IsArray() const;
+ V8EXPORT bool IsArray() const;
/**
* Returns true if this value is an object.
*/
- bool IsObject() const;
+ V8EXPORT bool IsObject() const;
/**
* Returns true if this value is boolean.
*/
- bool IsBoolean() const;
+ V8EXPORT bool IsBoolean() const;
/**
* Returns true if this value is a number.
*/
- bool IsNumber() const;
+ V8EXPORT bool IsNumber() const;
/**
* Returns true if this value is external.
*/
- bool IsExternal() const;
+ V8EXPORT bool IsExternal() const;
/**
* Returns true if this value is a 32-bit signed integer.
*/
- bool IsInt32() const;
+ V8EXPORT bool IsInt32() const;
/**
* Returns true if this value is a 32-bit unsigned integer.
*/
- bool IsUint32() const;
+ V8EXPORT bool IsUint32() const;
/**
* Returns true if this value is a Date.
*/
- bool IsDate() const;
+ V8EXPORT bool IsDate() const;
- Local<Boolean> ToBoolean() const;
- Local<Number> ToNumber() const;
- Local<String> ToString() const;
- Local<String> ToDetailString() const;
- Local<Object> ToObject() const;
- Local<Integer> ToInteger() const;
- Local<Uint32> ToUint32() const;
- Local<Int32> ToInt32() const;
+ V8EXPORT Local<Boolean> ToBoolean() const;
+ V8EXPORT Local<Number> ToNumber() const;
+ V8EXPORT Local<String> ToString() const;
+ V8EXPORT Local<String> ToDetailString() const;
+ V8EXPORT Local<Object> ToObject() const;
+ V8EXPORT Local<Integer> ToInteger() const;
+ V8EXPORT Local<Uint32> ToUint32() const;
+ V8EXPORT Local<Int32> ToInt32() const;
/**
* Attempts to convert a string to an array index.
* Returns an empty handle if the conversion fails.
*/
- Local<Uint32> ToArrayIndex() const;
+ V8EXPORT Local<Uint32> ToArrayIndex() const;
- bool BooleanValue() const;
- double NumberValue() const;
- int64_t IntegerValue() const;
- uint32_t Uint32Value() const;
- int32_t Int32Value() const;
+ V8EXPORT bool BooleanValue() const;
+ V8EXPORT double NumberValue() const;
+ V8EXPORT int64_t IntegerValue() const;
+ V8EXPORT uint32_t Uint32Value() const;
+ V8EXPORT int32_t Int32Value() const;
/** JS == */
- bool Equals(Handle<Value> that) const;
- bool StrictEquals(Handle<Value> that) const;
+ V8EXPORT bool Equals(Handle<Value> that) const;
+ V8EXPORT bool StrictEquals(Handle<Value> that) const;
private:
inline bool QuickIsString() const;
- bool FullIsString() const;
+ V8EXPORT bool FullIsString() const;
};
/**
* The superclass of primitive values. See ECMA-262 4.3.2.
*/
-class V8EXPORT Primitive : public Value { };
+class Primitive : public Value { };
/**
* A primitive boolean value (ECMA-262, 4.3.14). Either the true
* or false value.
*/
-class V8EXPORT Boolean : public Primitive {
+class Boolean : public Primitive {
public:
- bool Value() const;
+ V8EXPORT bool Value() const;
static inline Handle<Boolean> New(bool value);
};
@@ -968,19 +960,19 @@ class V8EXPORT Boolean : public Primitive {
/**
* A JavaScript string value (ECMA-262, 4.3.17).
*/
-class V8EXPORT String : public Primitive {
+class String : public Primitive {
public:
/**
* Returns the number of characters in this string.
*/
- int Length() const;
+ V8EXPORT int Length() const;
/**
* Returns the number of bytes in the UTF-8 encoded
* representation of this string.
*/
- int Utf8Length() const;
+ V8EXPORT int Utf8Length() const;
/**
* Write the contents of the string to an external buffer.
@@ -1007,33 +999,33 @@ class V8EXPORT String : public Primitive {
HINT_MANY_WRITES_EXPECTED = 1
};
- int Write(uint16_t* buffer,
- int start = 0,
- int length = -1,
- WriteHints hints = NO_HINTS) const; // UTF-16
- int WriteAscii(char* buffer,
- int start = 0,
- int length = -1,
- WriteHints hints = NO_HINTS) const; // ASCII
- int WriteUtf8(char* buffer,
- int length = -1,
- int* nchars_ref = NULL,
- WriteHints hints = NO_HINTS) const; // UTF-8
+ V8EXPORT int Write(uint16_t* buffer,
+ int start = 0,
+ int length = -1,
+ WriteHints hints = NO_HINTS) const; // UTF-16
+ V8EXPORT int WriteAscii(char* buffer,
+ int start = 0,
+ int length = -1,
+ WriteHints hints = NO_HINTS) const; // ASCII
+ V8EXPORT int WriteUtf8(char* buffer,
+ int length = -1,
+ int* nchars_ref = NULL,
+ WriteHints hints = NO_HINTS) const; // UTF-8
/**
* A zero length string.
*/
- static v8::Local<v8::String> Empty();
+ V8EXPORT static v8::Local<v8::String> Empty();
/**
* Returns true if the string is external
*/
- bool IsExternal() const;
+ V8EXPORT bool IsExternal() const;
/**
* Returns true if the string is both external and ascii
*/
- bool IsExternalAscii() const;
+ V8EXPORT bool IsExternalAscii() const;
class V8EXPORT ExternalStringResourceBase {
public:
@@ -1124,7 +1116,7 @@ class V8EXPORT String : public Primitive {
* Get the ExternalAsciiStringResource for an external ascii string.
* Returns NULL if IsExternalAscii() doesn't return true.
*/
- ExternalAsciiStringResource* GetExternalAsciiStringResource() const;
+ V8EXPORT ExternalAsciiStringResource* GetExternalAsciiStringResource() const;
static inline String* Cast(v8::Value* obj);
@@ -1137,19 +1129,20 @@ class V8EXPORT String : public Primitive {
* 'strlen' to determine the buffer length, it might be
* wrong if 'data' contains a null character.
*/
- static Local<String> New(const char* data, int length = -1);
+ V8EXPORT static Local<String> New(const char* data, int length = -1);
/** Allocates a new string from utf16 data.*/
- static Local<String> New(const uint16_t* data, int length = -1);
+ V8EXPORT static Local<String> New(const uint16_t* data, int length = -1);
/** Creates a symbol. Returns one if it exists already.*/
- static Local<String> NewSymbol(const char* data, int length = -1);
+ V8EXPORT static Local<String> NewSymbol(const char* data, int length = -1);
/**
* Creates a new string by concatenating the left and the right strings
* passed in as parameters.
*/
- static Local<String> Concat(Handle<String> left, Handle<String>right);
+ V8EXPORT static Local<String> Concat(Handle<String> left,
+ Handle<String>right);
/**
* Creates a new external string using the data defined in the given
@@ -1159,7 +1152,7 @@ class V8EXPORT String : public Primitive {
* should the underlying buffer be deallocated or modified except through the
* destructor of the external string resource.
*/
- static Local<String> NewExternal(ExternalStringResource* resource);
+ V8EXPORT static Local<String> NewExternal(ExternalStringResource* resource);
/**
* Associate an external string resource with this string by transforming it
@@ -1170,7 +1163,7 @@ class V8EXPORT String : public Primitive {
* The string is not modified if the operation fails. See NewExternal for
* information on the lifetime of the resource.
*/
- bool MakeExternal(ExternalStringResource* resource);
+ V8EXPORT bool MakeExternal(ExternalStringResource* resource);
/**
* Creates a new external string using the ascii data defined in the given
@@ -1180,7 +1173,8 @@ class V8EXPORT String : public Primitive {
* should the underlying buffer be deallocated or modified except through the
* destructor of the external string resource.
*/
- static Local<String> NewExternal(ExternalAsciiStringResource* resource);
+ V8EXPORT static Local<String> NewExternal(
+ ExternalAsciiStringResource* resource);
/**
* Associate an external string resource with this string by transforming it
@@ -1191,18 +1185,20 @@ class V8EXPORT String : public Primitive {
* The string is not modified if the operation fails. See NewExternal for
* information on the lifetime of the resource.
*/
- bool MakeExternal(ExternalAsciiStringResource* resource);
+ V8EXPORT bool MakeExternal(ExternalAsciiStringResource* resource);
/**
* Returns true if this string can be made external.
*/
- bool CanMakeExternal();
+ V8EXPORT bool CanMakeExternal();
/** Creates an undetectable string from the supplied ascii or utf-8 data.*/
- static Local<String> NewUndetectable(const char* data, int length = -1);
+ V8EXPORT static Local<String> NewUndetectable(const char* data,
+ int length = -1);
/** Creates an undetectable string from the supplied utf-16 data.*/
- static Local<String> NewUndetectable(const uint16_t* data, int length = -1);
+ V8EXPORT static Local<String> NewUndetectable(const uint16_t* data,
+ int length = -1);
/**
* Converts an object to a utf8-encoded character array. Useful if
@@ -1273,21 +1269,21 @@ class V8EXPORT String : public Primitive {
};
private:
- void VerifyExternalStringResource(ExternalStringResource* val) const;
- static void CheckCast(v8::Value* obj);
+ V8EXPORT void VerifyExternalStringResource(ExternalStringResource* val) const;
+ V8EXPORT static void CheckCast(v8::Value* obj);
};
/**
* A JavaScript number value (ECMA-262, 4.3.20)
*/
-class V8EXPORT Number : public Primitive {
+class Number : public Primitive {
public:
- double Value() const;
- static Local<Number> New(double value);
+ V8EXPORT double Value() const;
+ V8EXPORT static Local<Number> New(double value);
static inline Number* Cast(v8::Value* obj);
private:
- Number();
+ V8EXPORT Number();
static void CheckCast(v8::Value* obj);
};
@@ -1295,56 +1291,56 @@ class V8EXPORT Number : public Primitive {
/**
* A JavaScript value representing a signed integer.
*/
-class V8EXPORT Integer : public Number {
+class Integer : public Number {
public:
- static Local<Integer> New(int32_t value);
- static Local<Integer> NewFromUnsigned(uint32_t value);
- int64_t Value() const;
+ V8EXPORT static Local<Integer> New(int32_t value);
+ V8EXPORT static Local<Integer> NewFromUnsigned(uint32_t value);
+ V8EXPORT int64_t Value() const;
static inline Integer* Cast(v8::Value* obj);
private:
- Integer();
- static void CheckCast(v8::Value* obj);
+ V8EXPORT Integer();
+ V8EXPORT static void CheckCast(v8::Value* obj);
};
/**
* A JavaScript value representing a 32-bit signed integer.
*/
-class V8EXPORT Int32 : public Integer {
+class Int32 : public Integer {
public:
- int32_t Value() const;
+ V8EXPORT int32_t Value() const;
private:
- Int32();
+ V8EXPORT Int32();
};
/**
* A JavaScript value representing a 32-bit unsigned integer.
*/
-class V8EXPORT Uint32 : public Integer {
+class Uint32 : public Integer {
public:
- uint32_t Value() const;
+ V8EXPORT uint32_t Value() const;
private:
- Uint32();
+ V8EXPORT Uint32();
};
/**
* An instance of the built-in Date constructor (ECMA-262, 15.9).
*/
-class V8EXPORT Date : public Value {
+class Date : public Value {
public:
- static Local<Value> New(double time);
+ V8EXPORT static Local<Value> New(double time);
/**
* A specialization of Value::NumberValue that is more efficient
* because we know the structure of this object.
*/
- double NumberValue() const;
+ V8EXPORT double NumberValue() const;
static inline Date* Cast(v8::Value* obj);
private:
- static void CheckCast(v8::Value* obj);
+ V8EXPORT static void CheckCast(v8::Value* obj);
};
@@ -1403,14 +1399,14 @@ enum AccessControl {
/**
* A JavaScript object (ECMA-262, 4.3.3)
*/
-class V8EXPORT Object : public Value {
+class Object : public Value {
public:
- bool Set(Handle<Value> key,
- Handle<Value> value,
- PropertyAttribute attribs = None);
+ V8EXPORT bool Set(Handle<Value> key,
+ Handle<Value> value,
+ PropertyAttribute attribs = None);
- bool Set(uint32_t index,
- Handle<Value> value);
+ V8EXPORT bool Set(uint32_t index,
+ Handle<Value> value);
// Sets a local property on this object bypassing interceptors and
// overriding accessors or read-only properties.
@@ -1420,34 +1416,34 @@ class V8EXPORT Object : public Value {
// will only be returned if the interceptor doesn't return a value.
//
// Note also that this only works for named properties.
- bool ForceSet(Handle<Value> key,
- Handle<Value> value,
- PropertyAttribute attribs = None);
+ V8EXPORT bool ForceSet(Handle<Value> key,
+ Handle<Value> value,
+ PropertyAttribute attribs = None);
- Local<Value> Get(Handle<Value> key);
+ V8EXPORT Local<Value> Get(Handle<Value> key);
- Local<Value> Get(uint32_t index);
+ V8EXPORT Local<Value> Get(uint32_t index);
// TODO(1245389): Replace the type-specific versions of these
// functions with generic ones that accept a Handle<Value> key.
- bool Has(Handle<String> key);
+ V8EXPORT bool Has(Handle<String> key);
- bool Delete(Handle<String> key);
+ V8EXPORT bool Delete(Handle<String> key);
// Delete a property on this object bypassing interceptors and
// ignoring dont-delete attributes.
- bool ForceDelete(Handle<Value> key);
+ V8EXPORT bool ForceDelete(Handle<Value> key);
- bool Has(uint32_t index);
+ V8EXPORT bool Has(uint32_t index);
- bool Delete(uint32_t index);
+ V8EXPORT bool Delete(uint32_t index);
- bool SetAccessor(Handle<String> name,
- AccessorGetter getter,
- AccessorSetter setter = 0,
- Handle<Value> data = Handle<Value>(),
- AccessControl settings = DEFAULT,
- PropertyAttribute attribute = None);
+ V8EXPORT bool SetAccessor(Handle<String> name,
+ AccessorGetter getter,
+ AccessorSetter setter = 0,
+ Handle<Value> data = Handle<Value>(),
+ AccessControl settings = DEFAULT,
+ PropertyAttribute attribute = None);
/**
* Returns an array containing the names of the enumerable properties
@@ -1455,78 +1451,80 @@ class V8EXPORT Object : public Value {
* array returned by this method contains the same values as would
* be enumerated by a for-in statement over this object.
*/
- Local<Array> GetPropertyNames();
+ V8EXPORT Local<Array> GetPropertyNames();
/**
* Get the prototype object. This does not skip objects marked to
* be skipped by __proto__ and it does not consult the security
* handler.
*/
- Local<Value> GetPrototype();
+ V8EXPORT 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);
+ V8EXPORT bool SetPrototype(Handle<Value> prototype);
/**
* Finds an instance of the given function template in the prototype
* chain.
*/
- Local<Object> FindInstanceInPrototypeChain(Handle<FunctionTemplate> tmpl);
+ V8EXPORT Local<Object> FindInstanceInPrototypeChain(
+ Handle<FunctionTemplate> tmpl);
/**
* Call builtin Object.prototype.toString on this object.
* This is different from Value::ToString() that may call
* user-defined toString function. This one does not.
*/
- Local<String> ObjectProtoToString();
+ V8EXPORT Local<String> ObjectProtoToString();
/** Gets the number of internal fields for this Object. */
- int InternalFieldCount();
+ V8EXPORT int InternalFieldCount();
/** Gets the value in an internal field. */
inline Local<Value> GetInternalField(int index);
/** Sets the value in an internal field. */
- void SetInternalField(int index, Handle<Value> value);
+ V8EXPORT void SetInternalField(int index, Handle<Value> value);
/** Gets a native pointer from an internal field. */
inline void* GetPointerFromInternalField(int index);
/** Sets a native pointer in an internal field. */
- void SetPointerInInternalField(int index, void* value);
+ V8EXPORT void SetPointerInInternalField(int index, void* value);
// Testers for local properties.
- bool HasRealNamedProperty(Handle<String> key);
- bool HasRealIndexedProperty(uint32_t index);
- bool HasRealNamedCallbackProperty(Handle<String> key);
+ V8EXPORT bool HasRealNamedProperty(Handle<String> key);
+ V8EXPORT bool HasRealIndexedProperty(uint32_t index);
+ V8EXPORT bool HasRealNamedCallbackProperty(Handle<String> key);
/**
* If result.IsEmpty() no real property was located in the prototype chain.
* This means interceptors in the prototype chain are not called.
*/
- Local<Value> GetRealNamedPropertyInPrototypeChain(Handle<String> key);
+ V8EXPORT Local<Value> GetRealNamedPropertyInPrototypeChain(
+ Handle<String> key);
/**
* If result.IsEmpty() no real property was located on the object or
* in the prototype chain.
* This means interceptors in the prototype chain are not called.
*/
- Local<Value> GetRealNamedProperty(Handle<String> key);
+ V8EXPORT Local<Value> GetRealNamedProperty(Handle<String> key);
/** Tests for a named lookup interceptor.*/
- bool HasNamedLookupInterceptor();
+ V8EXPORT bool HasNamedLookupInterceptor();
/** Tests for an index lookup interceptor.*/
- bool HasIndexedLookupInterceptor();
+ V8EXPORT bool HasIndexedLookupInterceptor();
/**
* Turns on access check on the object if the object is an instance of
* a template that has access check callbacks. If an object has no
* access check info, the object cannot be accessed by anyone.
*/
- void TurnOnAccessCheck();
+ V8EXPORT void TurnOnAccessCheck();
/**
* Returns the identity hash for this object. The current implemenation uses
@@ -1535,7 +1533,7 @@ class V8EXPORT Object : public Value {
* The return value will never be 0. Also, it is not guaranteed to be
* unique.
*/
- int GetIdentityHash();
+ V8EXPORT int GetIdentityHash();
/**
* Access hidden properties on JavaScript objects. These properties are
@@ -1543,9 +1541,9 @@ class V8EXPORT Object : public Value {
* C++ API. Hidden properties introduced by V8 internally (for example the
* identity hash) are prefixed with "v8::".
*/
- bool SetHiddenValue(Handle<String> key, Handle<Value> value);
- Local<Value> GetHiddenValue(Handle<String> key);
- bool DeleteHiddenValue(Handle<String> key);
+ V8EXPORT bool SetHiddenValue(Handle<String> key, Handle<Value> value);
+ V8EXPORT Local<Value> GetHiddenValue(Handle<String> key);
+ V8EXPORT bool DeleteHiddenValue(Handle<String> key);
/**
* Returns true if this is an instance of an api function (one
@@ -1554,13 +1552,13 @@ class V8EXPORT Object : public Value {
* conservative and may return true for objects that haven't actually
* been modified.
*/
- bool IsDirty();
+ V8EXPORT bool IsDirty();
/**
* Clone this object with a fast but shallow copy. Values will point
* to the same values as the original object.
*/
- Local<Object> Clone();
+ V8EXPORT Local<Object> Clone();
/**
* Set the backing store of the indexed properties to be managed by the
@@ -1569,7 +1567,7 @@ class V8EXPORT Object : public Value {
* Note: The embedding program still owns the data and needs to ensure that
* the backing store is preserved while V8 has a reference.
*/
- void SetIndexedPropertiesToPixelData(uint8_t* data, int length);
+ V8EXPORT void SetIndexedPropertiesToPixelData(uint8_t* data, int length);
bool HasIndexedPropertiesInPixelData();
uint8_t* GetIndexedPropertiesPixelData();
int GetIndexedPropertiesPixelDataLength();
@@ -1581,21 +1579,22 @@ class V8EXPORT Object : public Value {
* Note: The embedding program still owns the data and needs to ensure that
* the backing store is preserved while V8 has a reference.
*/
- void SetIndexedPropertiesToExternalArrayData(void* data,
- ExternalArrayType array_type,
- int number_of_elements);
+ V8EXPORT void SetIndexedPropertiesToExternalArrayData(
+ void* data,
+ ExternalArrayType array_type,
+ int number_of_elements);
bool HasIndexedPropertiesInExternalArrayData();
void* GetIndexedPropertiesExternalArrayData();
ExternalArrayType GetIndexedPropertiesExternalArrayDataType();
int GetIndexedPropertiesExternalArrayDataLength();
- static Local<Object> New();
+ V8EXPORT static Local<Object> New();
static inline Object* Cast(Value* obj);
private:
- Object();
- static void CheckCast(Value* obj);
- Local<Value> CheckedGetInternalField(int index);
- void* SlowGetPointerFromInternalField(int index);
+ V8EXPORT Object();
+ V8EXPORT static void CheckCast(Value* obj);
+ V8EXPORT Local<Value> CheckedGetInternalField(int index);
+ V8EXPORT void* SlowGetPointerFromInternalField(int index);
/**
* If quick access to the internal field is possible this method
@@ -1608,20 +1607,20 @@ class V8EXPORT Object : public Value {
/**
* An instance of the built-in array constructor (ECMA-262, 15.4.2).
*/
-class V8EXPORT Array : public Object {
+class Array : public Object {
public:
- uint32_t Length() const;
+ V8EXPORT uint32_t Length() const;
/**
* Clones an element at index |index|. Returns an empty
* handle if cloning fails (for any reason).
*/
- Local<Object> CloneElementAt(uint32_t index);
+ V8EXPORT Local<Object> CloneElementAt(uint32_t index);
- static Local<Array> New(int length = 0);
+ V8EXPORT static Local<Array> New(int length = 0);
static inline Array* Cast(Value* obj);
private:
- Array();
+ V8EXPORT Array();
static void CheckCast(Value* obj);
};
@@ -1629,25 +1628,27 @@ class V8EXPORT Array : public Object {
/**
* A JavaScript function object (ECMA-262, 15.3).
*/
-class V8EXPORT Function : public Object {
+class Function : public Object {
public:
- Local<Object> NewInstance() const;
- Local<Object> NewInstance(int argc, Handle<Value> argv[]) const;
- Local<Value> Call(Handle<Object> recv, int argc, Handle<Value> argv[]);
- void SetName(Handle<String> name);
- Handle<Value> GetName() const;
+ V8EXPORT Local<Object> NewInstance() const;
+ V8EXPORT Local<Object> NewInstance(int argc, Handle<Value> argv[]) const;
+ V8EXPORT Local<Value> Call(Handle<Object> recv,
+ int argc,
+ Handle<Value> argv[]);
+ V8EXPORT void SetName(Handle<String> name);
+ V8EXPORT Handle<Value> GetName() const;
/**
* Returns zero based line number of function body and
* kLineOffsetNotFound if no information available.
*/
- int GetScriptLineNumber() const;
- ScriptOrigin GetScriptOrigin() const;
+ V8EXPORT int GetScriptLineNumber() const;
+ V8EXPORT ScriptOrigin GetScriptOrigin() const;
static inline Function* Cast(Value* obj);
- static const int kLineOffsetNotFound;
+ V8EXPORT static const int kLineOffsetNotFound;
private:
- Function();
- static void CheckCast(Value* obj);
+ V8EXPORT Function();
+ V8EXPORT static void CheckCast(Value* obj);
};
@@ -1662,19 +1663,19 @@ class V8EXPORT Function : public Object {
* value Unwrap should be used, all other operations on that object will lead
* to unpredictable results.
*/
-class V8EXPORT External : public Value {
+class External : public Value {
public:
- static Local<Value> Wrap(void* data);
+ V8EXPORT static Local<Value> Wrap(void* data);
static inline void* Unwrap(Handle<Value> obj);
- static Local<External> New(void* value);
+ V8EXPORT static Local<External> New(void* value);
static inline External* Cast(Value* obj);
- void* Value() const;
+ V8EXPORT void* Value() const;
private:
- External();
- static void CheckCast(v8::Value* obj);
+ V8EXPORT External();
+ V8EXPORT static void CheckCast(v8::Value* obj);
static inline void* QuickUnwrap(Handle<v8::Value> obj);
- static void* FullUnwrap(Handle<v8::Value> obj);
+ V8EXPORT static void* FullUnwrap(Handle<v8::Value> obj);
};
@@ -1704,7 +1705,7 @@ class V8EXPORT Template : public Data {
* including the receiver, the number and values of arguments, and
* the holder of the function.
*/
-class V8EXPORT Arguments {
+class Arguments {
public:
inline int Length() const;
inline Local<Value> operator[](int i) const;
@@ -1714,7 +1715,6 @@ class V8EXPORT Arguments {
inline bool IsConstructCall() const;
inline Local<Value> Data() const;
private:
- Arguments();
friend class ImplementationUtilities;
inline Arguments(Local<Value> data,
Local<Object> holder,
@@ -3001,7 +3001,7 @@ class V8EXPORT Context {
* Stack-allocated class which sets the execution context for all
* operations executed within a local scope.
*/
- class V8EXPORT Scope {
+ class Scope {
public:
inline Scope(Handle<Context> context) : context_(context) {
context_->Enter();
@@ -3320,6 +3320,17 @@ void Persistent<T>::ClearWeak() {
V8::ClearWeak(reinterpret_cast<internal::Object**>(**this));
}
+
+Arguments::Arguments(v8::Local<v8::Value> data,
+ v8::Local<v8::Object> holder,
+ v8::Local<v8::Function> callee,
+ bool is_construct_call,
+ void** values, int length)
+ : data_(data), holder_(holder), callee_(callee),
+ is_construct_call_(is_construct_call),
+ values_(values), length_(length) { }
+
+
Local<Value> Arguments::operator[](int i) const {
if (i < 0 || length_ <= i) return Local<Value>(*Undefined());
return Local<Value>(reinterpret_cast<Value*>(values_ - i));
@@ -3580,7 +3591,6 @@ Local<Object> AccessorInfo::Holder() const {
#undef V8EXPORT
-#undef V8EXPORT_INLINE
#undef TYPE_CHECK
diff --git a/deps/v8/src/api.h b/deps/v8/src/api.h
index e7b13949a9..5c671369de 100644
--- a/deps/v8/src/api.h
+++ b/deps/v8/src/api.h
@@ -134,16 +134,6 @@ class ApiFunction {
};
-v8::Arguments::Arguments(v8::Local<v8::Value> data,
- v8::Local<v8::Object> holder,
- v8::Local<v8::Function> callee,
- bool is_construct_call,
- void** values, int length)
- : data_(data), holder_(holder), callee_(callee),
- is_construct_call_(is_construct_call),
- values_(values), length_(length) { }
-
-
enum ExtensionTraversalState {
UNVISITED, VISITED, INSTALLED
};
diff --git a/deps/v8/src/arm/assembler-arm.cc b/deps/v8/src/arm/assembler-arm.cc
index c8170b3d55..f5ff43a656 100644
--- a/deps/v8/src/arm/assembler-arm.cc
+++ b/deps/v8/src/arm/assembler-arm.cc
@@ -1802,6 +1802,16 @@ void Assembler::vstr(const DwVfpRegister src,
void Assembler::vmov(const DwVfpRegister dst,
+ const DwVfpRegister src,
+ const Condition cond) {
+ // Dd = Dm
+ // Instruction details available in ARM DDI 0406B, A8-642.
+ emit(cond | 0xE*B24 | 0xB*B20 |
+ dst.code()*B12 | 0x5*B9 | B8 | B6 | src.code());
+}
+
+
+void Assembler::vmov(const DwVfpRegister dst,
const Register src1,
const Register src2,
const Condition cond) {
diff --git a/deps/v8/src/arm/assembler-arm.h b/deps/v8/src/arm/assembler-arm.h
index 8a4173d837..6a4fb23e85 100644
--- a/deps/v8/src/arm/assembler-arm.h
+++ b/deps/v8/src/arm/assembler-arm.h
@@ -930,6 +930,10 @@ class Assembler : public Malloced {
const Register base,
int offset, // Offset must be a multiple of 4.
const Condition cond = al);
+
+ void vmov(const DwVfpRegister dst,
+ const DwVfpRegister src,
+ const Condition cond = al);
void vmov(const DwVfpRegister dst,
const Register src1,
const Register src2,
diff --git a/deps/v8/src/arm/codegen-arm.cc b/deps/v8/src/arm/codegen-arm.cc
index f923c092d1..fa6efcd3c1 100644
--- a/deps/v8/src/arm/codegen-arm.cc
+++ b/deps/v8/src/arm/codegen-arm.cc
@@ -748,37 +748,43 @@ void CodeGenerator::ToBoolean(JumpTarget* true_target,
JumpTarget* false_target) {
// Note: The generated code snippet does not change stack variables.
// Only the condition code should be set.
+ bool known_smi = frame_->KnownSmiAt(0);
Register tos = frame_->PopToRegister();
// Fast case checks
// Check if the value is 'false'.
- __ LoadRoot(ip, Heap::kFalseValueRootIndex);
- __ cmp(tos, ip);
- false_target->Branch(eq);
+ if (!known_smi) {
+ __ LoadRoot(ip, Heap::kFalseValueRootIndex);
+ __ cmp(tos, ip);
+ false_target->Branch(eq);
- // Check if the value is 'true'.
- __ LoadRoot(ip, Heap::kTrueValueRootIndex);
- __ cmp(tos, ip);
- true_target->Branch(eq);
+ // Check if the value is 'true'.
+ __ LoadRoot(ip, Heap::kTrueValueRootIndex);
+ __ cmp(tos, ip);
+ true_target->Branch(eq);
- // Check if the value is 'undefined'.
- __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
- __ cmp(tos, ip);
- false_target->Branch(eq);
+ // Check if the value is 'undefined'.
+ __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+ __ cmp(tos, ip);
+ false_target->Branch(eq);
+ }
// Check if the value is a smi.
__ cmp(tos, Operand(Smi::FromInt(0)));
- false_target->Branch(eq);
- __ tst(tos, Operand(kSmiTagMask));
- true_target->Branch(eq);
- // Slow case: call the runtime.
- frame_->EmitPush(tos);
- frame_->CallRuntime(Runtime::kToBool, 1);
- // Convert the result (r0) to a condition code.
- __ LoadRoot(ip, Heap::kFalseValueRootIndex);
- __ cmp(r0, ip);
+ if (!known_smi) {
+ false_target->Branch(eq);
+ __ tst(tos, Operand(kSmiTagMask));
+ true_target->Branch(eq);
+
+ // Slow case: call the runtime.
+ frame_->EmitPush(tos);
+ frame_->CallRuntime(Runtime::kToBool, 1);
+ // Convert the result (r0) to a condition code.
+ __ LoadRoot(ip, Heap::kFalseValueRootIndex);
+ __ cmp(r0, ip);
+ }
cc_reg_ = ne;
}
@@ -1745,11 +1751,15 @@ void CodeGenerator::VisitDeclaration(Declaration* node) {
val = node->fun(); // NULL if we don't have a function
}
+
if (val != NULL) {
+ WriteBarrierCharacter wb_info =
+ val->type()->IsLikelySmi() ? LIKELY_SMI : UNLIKELY_SMI;
+ if (val->AsLiteral() != NULL) wb_info = NEVER_NEWSPACE;
// Set initial value.
Reference target(this, node->proxy());
Load(val);
- target.SetValue(NOT_CONST_INIT);
+ target.SetValue(NOT_CONST_INIT, wb_info);
// Get rid of the assigned value (declarations are statements).
frame_->Drop();
@@ -2485,13 +2495,13 @@ void CodeGenerator::VisitForInStatement(ForInStatement* node) {
if (each.size() > 0) {
__ ldr(r0, frame_->ElementAt(each.size()));
frame_->EmitPush(r0);
- each.SetValue(NOT_CONST_INIT);
+ each.SetValue(NOT_CONST_INIT, UNLIKELY_SMI);
frame_->Drop(2);
} else {
// If the reference was to a slot we rely on the convenient property
// that it doesn't matter whether a value (eg, r3 pushed above) is
// right on top of or right underneath a zero-sized reference.
- each.SetValue(NOT_CONST_INIT);
+ each.SetValue(NOT_CONST_INIT, UNLIKELY_SMI);
frame_->Drop();
}
}
@@ -3646,6 +3656,8 @@ void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
// Evaluate the receiver subexpression.
Load(prop->obj());
+ WriteBarrierCharacter wb_info;
+
// Change to slow case in the beginning of an initialization block to
// avoid the quadratic behavior of repeatedly adding fast properties.
if (node->starts_initialization_block()) {
@@ -3667,7 +3679,7 @@ void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
// [tos] : key
// [tos+1] : receiver
// [tos+2] : receiver if at the end of an initialization block
-
+ //
// Evaluate the right-hand side.
if (node->is_compound()) {
// For a compound assignment the right-hand side is a binary operation
@@ -3699,9 +3711,13 @@ void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
overwrite_value ? OVERWRITE_RIGHT : NO_OVERWRITE,
inline_smi);
}
+ wb_info = node->type()->IsLikelySmi() ? LIKELY_SMI : UNLIKELY_SMI;
} else {
// For non-compound assignment just load the right-hand side.
Load(node->value());
+ wb_info = node->value()->AsLiteral() != NULL ?
+ NEVER_NEWSPACE :
+ (node->value()->type()->IsLikelySmi() ? LIKELY_SMI : UNLIKELY_SMI);
}
// Stack layout:
@@ -3713,7 +3729,7 @@ void CodeGenerator::EmitKeyedPropertyAssignment(Assignment* node) {
// Perform the assignment. It is safe to ignore constants here.
ASSERT(node->op() != Token::INIT_CONST);
CodeForSourcePosition(node->position());
- EmitKeyedStore(prop->key()->type());
+ EmitKeyedStore(prop->key()->type(), wb_info);
frame_->EmitPush(r0);
// Stack layout:
@@ -4291,7 +4307,7 @@ void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
} else {
CpuFeatures::Scope scope(VFP3);
JumpTarget runtime, done;
- Label not_minus_half, allocate_return;
+ Label exponent_nonsmi, base_nonsmi, powi, not_minus_half, allocate_return;
Register scratch1 = VirtualFrame::scratch0();
Register scratch2 = VirtualFrame::scratch1();
@@ -4299,18 +4315,74 @@ void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
// Get base and exponent to registers.
Register exponent = frame_->PopToRegister();
Register base = frame_->PopToRegister(exponent);
+ Register heap_number_map = no_reg;
// Set the frame for the runtime jump target. The code below jumps to the
// jump target label so the frame needs to be established before that.
ASSERT(runtime.entry_frame() == NULL);
runtime.set_entry_frame(frame_);
- __ BranchOnSmi(exponent, runtime.entry_label());
+ __ BranchOnNotSmi(exponent, &exponent_nonsmi);
+ __ BranchOnNotSmi(base, &base_nonsmi);
+
+ heap_number_map = r6;
+ __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+
+ // Exponent is a smi and base is a smi. Get the smi value into vfp register
+ // d1.
+ __ SmiToDoubleVFPRegister(base, d1, scratch1, s0);
+ __ b(&powi);
+ __ bind(&base_nonsmi);
+ // Exponent is smi and base is non smi. Get the double value from the base
+ // into vfp register d1.
+ __ ObjectToDoubleVFPRegister(base, d1,
+ scratch1, scratch2, heap_number_map, s0,
+ runtime.entry_label());
+
+ __ bind(&powi);
+
+ // Load 1.0 into d0.
+ __ mov(scratch2, Operand(0x3ff00000));
+ __ mov(scratch1, Operand(0));
+ __ vmov(d0, scratch1, scratch2);
+
+ // Get the absolute untagged value of the exponent and use that for the
+ // calculation.
+ __ mov(scratch1, Operand(exponent, ASR, kSmiTagSize), SetCC);
+ __ rsb(scratch1, scratch1, Operand(0), LeaveCC, mi); // Negate if negative.
+ __ vmov(d2, d0, mi); // 1.0 needed in d2 later if exponent is negative.
+
+ // Run through all the bits in the exponent. The result is calculated in d0
+ // and d1 holds base^(bit^2).
+ Label more_bits;
+ __ bind(&more_bits);
+ __ mov(scratch1, Operand(scratch1, LSR, 1), SetCC);
+ __ vmul(d0, d0, d1, cs); // Multiply with base^(bit^2) if bit is set.
+ __ vmul(d1, d1, d1, ne); // Don't bother calculating next d1 if done.
+ __ b(ne, &more_bits);
+
+ // If exponent is positive we are done.
+ __ cmp(exponent, Operand(0));
+ __ b(ge, &allocate_return);
+
+ // If exponent is negative result is 1/result (d2 already holds 1.0 in that
+ // case). However if d0 has reached infinity this will not provide the
+ // correct result, so call runtime if that is the case.
+ __ mov(scratch2, Operand(0x7FF00000));
+ __ mov(scratch1, Operand(0));
+ __ vmov(d1, scratch1, scratch2); // Load infinity into d1.
+ __ vcmp(d0, d1);
+ __ vmrs(pc);
+ runtime.Branch(eq); // d0 reached infinity.
+ __ vdiv(d0, d2, d0);
+ __ b(&allocate_return);
+
+ __ bind(&exponent_nonsmi);
// Special handling of raising to the power of -0.5 and 0.5. First check
// that the value is a heap number and that the lower bits (which for both
// values are zero).
- Register heap_number_map = r6;
+ heap_number_map = r6;
__ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
__ ldr(scratch1, FieldMemOperand(exponent, HeapObject::kMapOffset));
__ ldr(scratch2, FieldMemOperand(exponent, HeapNumber::kMantissaOffset));
@@ -4319,7 +4391,7 @@ void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
__ tst(scratch2, scratch2);
runtime.Branch(ne);
- // Load the e
+ // Load the higher bits (which contains the floating point exponent).
__ ldr(scratch1, FieldMemOperand(exponent, HeapNumber::kExponentOffset));
// Compare exponent with -0.5.
@@ -4356,8 +4428,10 @@ void CodeGenerator::GenerateMathPow(ZoneList<Expression*>* args) {
__ vsqrt(d0, d0);
__ bind(&allocate_return);
- __ AllocateHeapNumberWithValue(
- base, d0, scratch1, scratch2, heap_number_map, runtime.entry_label());
+ Register scratch3 = r5;
+ __ AllocateHeapNumberWithValue(scratch3, d0, scratch1, scratch2,
+ heap_number_map, runtime.entry_label());
+ __ mov(base, scratch3);
done.Jump();
runtime.Bind();
@@ -5349,9 +5423,13 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
frame_->EmitPush(r0); // r0 has result
} else {
- bool overwrite =
+ bool can_overwrite =
(node->expression()->AsBinaryOperation() != NULL &&
node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
+ UnaryOverwriteMode overwrite =
+ can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
+
+ bool no_negative_zero = node->expression()->no_negative_zero();
Load(node->expression());
switch (op) {
case Token::NOT:
@@ -5362,7 +5440,10 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
case Token::SUB: {
frame_->PopToR0();
- GenericUnaryOpStub stub(Token::SUB, overwrite);
+ GenericUnaryOpStub stub(
+ Token::SUB,
+ overwrite,
+ no_negative_zero ? kIgnoreNegativeZero : kStrictNegativeZero);
frame_->CallStub(&stub, 0);
frame_->EmitPush(r0); // r0 has result
break;
@@ -5451,7 +5532,7 @@ void CodeGenerator::VisitCountOperation(CountOperation* node) {
__ sub(value, value, Operand(Smi::FromInt(1)));
}
frame_->EmitPush(value);
- target.SetValue(NOT_CONST_INIT);
+ target.SetValue(NOT_CONST_INIT, LIKELY_SMI);
if (is_postfix) frame_->Pop();
ASSERT_EQ(original_height + 1, frame_->height());
return;
@@ -5550,7 +5631,7 @@ void CodeGenerator::VisitCountOperation(CountOperation* node) {
// Set the target with the result, leaving the result on
// top of the stack. Removes the target from the stack if
// it has a non-zero size.
- if (!is_const) target.SetValue(NOT_CONST_INIT);
+ if (!is_const) target.SetValue(NOT_CONST_INIT, LIKELY_SMI);
}
// Postfix: Discard the new value and use the old.
@@ -6283,7 +6364,8 @@ void CodeGenerator::EmitKeyedLoad() {
}
-void CodeGenerator::EmitKeyedStore(StaticType* key_type) {
+void CodeGenerator::EmitKeyedStore(StaticType* key_type,
+ WriteBarrierCharacter wb_info) {
// 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()) {
@@ -6299,25 +6381,45 @@ void CodeGenerator::EmitKeyedStore(StaticType* key_type) {
__ IncrementCounter(&Counters::keyed_store_inline, 1,
scratch1, scratch2);
+
+
// Load the value, key and receiver from the stack.
+ bool value_is_harmless = frame_->KnownSmiAt(0);
+ if (wb_info == NEVER_NEWSPACE) value_is_harmless = true;
+ bool key_is_smi = frame_->KnownSmiAt(1);
Register value = frame_->PopToRegister();
Register key = frame_->PopToRegister(value);
VirtualFrame::SpilledScope spilled(frame_);
Register receiver = r2;
frame_->EmitPop(receiver);
+#ifdef DEBUG
+ bool we_remembered_the_write_barrier = value_is_harmless;
+#endif
+
// The deferred code expects value, key and receiver in registers.
DeferredReferenceSetKeyedValue* deferred =
new DeferredReferenceSetKeyedValue(value, key, receiver);
// Check that the value is a smi. As this inlined code does not set the
// write barrier it is only possible to store smi values.
- __ tst(value, Operand(kSmiTagMask));
- deferred->Branch(ne);
+ if (!value_is_harmless) {
+ // If the value is not likely to be a Smi then let's test the fixed array
+ // for new space instead. See below.
+ if (wb_info == LIKELY_SMI) {
+ __ tst(value, Operand(kSmiTagMask));
+ deferred->Branch(ne);
+#ifdef DEBUG
+ we_remembered_the_write_barrier = true;
+#endif
+ }
+ }
- // Check that the key is a smi.
- __ tst(key, Operand(kSmiTagMask));
- deferred->Branch(ne);
+ if (!key_is_smi) {
+ // Check that the key is a smi.
+ __ tst(key, Operand(kSmiTagMask));
+ deferred->Branch(ne);
+ }
// Check that the receiver is a heap object.
__ tst(receiver, Operand(kSmiTagMask));
@@ -6333,24 +6435,35 @@ void CodeGenerator::EmitKeyedStore(StaticType* key_type) {
__ cmp(scratch1, key);
deferred->Branch(ls); // Unsigned less equal.
+ // Get the elements array from the receiver.
+ __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
+ if (!value_is_harmless && wb_info != LIKELY_SMI) {
+ Label ok;
+ __ and_(scratch2, scratch1, Operand(ExternalReference::new_space_mask()));
+ __ cmp(scratch2, Operand(ExternalReference::new_space_start()));
+ __ tst(value, Operand(kSmiTagMask), ne);
+ deferred->Branch(ne);
+#ifdef DEBUG
+ we_remembered_the_write_barrier = true;
+#endif
+ }
+ // Check that the elements array is not a dictionary.
+ __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset));
// The following instructions are the part of the inlined store keyed
// property code which can be patched. Therefore the exact number of
// instructions generated need to be fixed, so the constant pool is blocked
// while generating this code.
{ Assembler::BlockConstPoolScope block_const_pool(masm_);
- // Get the elements array from the receiver and check that it
- // is not a dictionary.
- __ ldr(scratch1, FieldMemOperand(receiver, JSObject::kElementsOffset));
- __ ldr(scratch2, FieldMemOperand(scratch1, JSObject::kMapOffset));
+#ifdef DEBUG
+ Label check_inlined_codesize;
+ masm_->bind(&check_inlined_codesize);
+#endif
+
// Read the fixed array map from the constant pool (not from the root
// array) so that the value can be patched. 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.
-#ifdef DEBUG
- Label check_inlined_codesize;
- masm_->bind(&check_inlined_codesize);
-#endif
__ mov(scratch3, Operand(Factory::fixed_array_map()));
__ cmp(scratch2, scratch3);
deferred->Branch(ne);
@@ -6367,6 +6480,8 @@ void CodeGenerator::EmitKeyedStore(StaticType* key_type) {
masm_->InstructionsGeneratedSince(&check_inlined_codesize));
}
+ ASSERT(we_remembered_the_write_barrier);
+
deferred->BindExit();
} else {
frame()->CallKeyedStoreIC();
@@ -6464,7 +6579,7 @@ void Reference::GetValue() {
}
-void Reference::SetValue(InitState init_state) {
+void Reference::SetValue(InitState init_state, WriteBarrierCharacter wb_info) {
ASSERT(!is_illegal());
ASSERT(!cgen_->has_cc());
MacroAssembler* masm = cgen_->masm();
@@ -6496,7 +6611,7 @@ void Reference::SetValue(InitState init_state) {
Property* property = expression_->AsProperty();
ASSERT(property != NULL);
cgen_->CodeForSourcePosition(property->position());
- cgen_->EmitKeyedStore(property->key()->type());
+ cgen_->EmitKeyedStore(property->key()->type(), wb_info);
frame->EmitPush(r0);
set_unloaded();
break;
@@ -7170,22 +7285,42 @@ static void EmitCheckForTwoHeapNumbers(MacroAssembler* masm,
// Fast negative check for symbol-to-symbol equality.
-static void EmitCheckForSymbols(MacroAssembler* masm, Label* slow) {
+static void EmitCheckForSymbolsOrObjects(MacroAssembler* masm,
+ Label* possible_strings,
+ Label* not_both_strings) {
// r2 is object type of r0.
// Ensure that no non-strings have the symbol bit set.
- ASSERT(kNotStringTag + kIsSymbolMask > LAST_TYPE);
+ Label object_test;
ASSERT(kSymbolTag != 0);
+ __ tst(r2, Operand(kIsNotStringMask));
+ __ b(ne, &object_test);
__ tst(r2, Operand(kIsSymbolMask));
- __ b(eq, slow);
- __ ldr(r3, FieldMemOperand(r1, HeapObject::kMapOffset));
- __ ldrb(r3, FieldMemOperand(r3, Map::kInstanceTypeOffset));
+ __ b(eq, possible_strings);
+ __ CompareObjectType(r1, r3, r3, FIRST_NONSTRING_TYPE);
+ __ b(ge, not_both_strings);
__ tst(r3, Operand(kIsSymbolMask));
- __ b(eq, slow);
+ __ b(eq, possible_strings);
// Both are symbols. We already checked they weren't the same pointer
// so they are not equal.
__ mov(r0, Operand(1)); // Non-zero indicates not equal.
__ mov(pc, Operand(lr)); // Return.
+
+ __ bind(&object_test);
+ __ cmp(r2, Operand(FIRST_JS_OBJECT_TYPE));
+ __ b(lt, not_both_strings);
+ __ CompareObjectType(r1, r2, r3, FIRST_JS_OBJECT_TYPE);
+ __ b(lt, not_both_strings);
+ // If both objects are undetectable, they are equal. Otherwise, they
+ // are not equal, since they are different objects and an object is not
+ // equal to undefined.
+ __ ldr(r3, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ ldrb(r2, FieldMemOperand(r2, Map::kBitFieldOffset));
+ __ ldrb(r3, FieldMemOperand(r3, Map::kBitFieldOffset));
+ __ and_(r0, r2, Operand(r3));
+ __ and_(r0, r0, Operand(1 << Map::kIsUndetectable));
+ __ eor(r0, r0, Operand(1 << Map::kIsUndetectable));
+ __ mov(pc, Operand(lr)); // Return.
}
@@ -7301,7 +7436,8 @@ void NumberToStringStub::Generate(MacroAssembler* masm) {
void RecordWriteStub::Generate(MacroAssembler* masm) {
- __ RecordWriteHelper(object_, Operand(offset_), offset_, scratch_);
+ __ add(offset_, object_, Operand(offset_));
+ __ RecordWriteHelper(object_, offset_, scratch_);
__ Ret();
}
@@ -7398,9 +7534,10 @@ void CompareStub::Generate(MacroAssembler* masm) {
// In the strict case the EmitStrictTwoHeapObjectCompare already took care of
// symbols.
if (cc_ == eq && !strict_) {
- // Either jumps to slow or returns the answer. Assumes that r2 is the type
- // of r0 on entry.
- EmitCheckForSymbols(masm, &flat_string_check);
+ // Returns an answer for two symbols or two detectable objects.
+ // Otherwise jumps to string case or not both strings case.
+ // Assumes that r2 is the type of r0 on entry.
+ EmitCheckForSymbolsOrObjects(masm, &flat_string_check, &slow);
}
// Check for both being sequential ASCII strings, and inline if that is the
@@ -7511,189 +7648,197 @@ void GenericBinaryOpStub::HandleBinaryOpSlowCases(
__ Swap(r0, r1, ip);
}
- if (ShouldGenerateFPCode()) {
- Label r0_is_smi, r1_is_smi, finished_loading_r0, finished_loading_r1;
+ // The type transition also calculates the answer.
+ bool generate_code_to_calculate_answer = true;
+ if (ShouldGenerateFPCode()) {
if (runtime_operands_type_ == BinaryOpIC::DEFAULT) {
switch (op_) {
case Token::ADD:
case Token::SUB:
case Token::MUL:
case Token::DIV:
- GenerateTypeTransition(masm);
+ GenerateTypeTransition(masm); // Tail call.
+ generate_code_to_calculate_answer = false;
break;
default:
break;
}
- // Restore heap number map register.
- __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
- }
-
- if (mode_ == NO_OVERWRITE) {
- // In the case where there is no chance of an overwritable float we may as
- // well do the allocation immediately while r0 and r1 are untouched.
- __ AllocateHeapNumber(r5, r3, r7, heap_number_map, &slow);
}
- // Move r0 to a double in r2-r3.
- __ tst(r0, Operand(kSmiTagMask));
- __ b(eq, &r0_is_smi); // It's a Smi so don't check it's a heap number.
- __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
- __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
- __ cmp(r4, heap_number_map);
- __ b(ne, &slow);
- if (mode_ == OVERWRITE_RIGHT) {
- __ mov(r5, Operand(r0)); // Overwrite this heap number.
- }
- if (use_fp_registers) {
- CpuFeatures::Scope scope(VFP3);
- // Load the double from tagged HeapNumber r0 to d7.
- __ sub(r7, r0, Operand(kHeapObjectTag));
- __ vldr(d7, r7, HeapNumber::kValueOffset);
- } else {
- // Calling convention says that second double is in r2 and r3.
- __ Ldrd(r2, r3, FieldMemOperand(r0, HeapNumber::kValueOffset));
- }
- __ jmp(&finished_loading_r0);
- __ bind(&r0_is_smi);
- if (mode_ == OVERWRITE_RIGHT) {
- // We can't overwrite a Smi so get address of new heap number into r5.
- __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
- }
+ if (generate_code_to_calculate_answer) {
+ Label r0_is_smi, r1_is_smi, finished_loading_r0, finished_loading_r1;
+ if (mode_ == NO_OVERWRITE) {
+ // In the case where there is no chance of an overwritable float we may
+ // as well do the allocation immediately while r0 and r1 are untouched.
+ __ AllocateHeapNumber(r5, r3, r7, heap_number_map, &slow);
+ }
- if (CpuFeatures::IsSupported(VFP3)) {
- CpuFeatures::Scope scope(VFP3);
- // Convert smi in r0 to double in d7.
- __ mov(r7, Operand(r0, ASR, kSmiTagSize));
- __ vmov(s15, r7);
- __ vcvt_f64_s32(d7, s15);
- if (!use_fp_registers) {
- __ vmov(r2, r3, d7);
+ // Move r0 to a double in r2-r3.
+ __ tst(r0, Operand(kSmiTagMask));
+ __ b(eq, &r0_is_smi); // It's a Smi so don't check it's a heap number.
+ __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+ __ cmp(r4, heap_number_map);
+ __ b(ne, &slow);
+ if (mode_ == OVERWRITE_RIGHT) {
+ __ mov(r5, Operand(r0)); // Overwrite this heap number.
+ }
+ if (use_fp_registers) {
+ CpuFeatures::Scope scope(VFP3);
+ // Load the double from tagged HeapNumber r0 to d7.
+ __ sub(r7, r0, Operand(kHeapObjectTag));
+ __ vldr(d7, r7, HeapNumber::kValueOffset);
+ } else {
+ // Calling convention says that second double is in r2 and r3.
+ __ Ldrd(r2, r3, FieldMemOperand(r0, HeapNumber::kValueOffset));
+ }
+ __ jmp(&finished_loading_r0);
+ __ bind(&r0_is_smi);
+ if (mode_ == OVERWRITE_RIGHT) {
+ // We can't overwrite a Smi so get address of new heap number into r5.
+ __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
}
- } else {
- // Write Smi from r0 to r3 and r2 in double format.
- __ mov(r7, Operand(r0));
- ConvertToDoubleStub stub3(r3, r2, r7, r4);
- __ push(lr);
- __ Call(stub3.GetCode(), RelocInfo::CODE_TARGET);
- __ pop(lr);
- }
- // HEAP_NUMBERS stub is slower than GENERIC on a pair of smis.
- // r0 is known to be a smi. If r1 is also a smi then switch to GENERIC.
- Label r1_is_not_smi;
- if (runtime_operands_type_ == BinaryOpIC::HEAP_NUMBERS) {
- __ tst(r1, Operand(kSmiTagMask));
- __ b(ne, &r1_is_not_smi);
- GenerateTypeTransition(masm);
- // Restore heap number map register.
- __ LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
- __ jmp(&r1_is_smi);
- }
+ if (CpuFeatures::IsSupported(VFP3)) {
+ CpuFeatures::Scope scope(VFP3);
+ // Convert smi in r0 to double in d7.
+ __ mov(r7, Operand(r0, ASR, kSmiTagSize));
+ __ vmov(s15, r7);
+ __ vcvt_f64_s32(d7, s15);
+ if (!use_fp_registers) {
+ __ vmov(r2, r3, d7);
+ }
+ } else {
+ // Write Smi from r0 to r3 and r2 in double format.
+ __ mov(r7, Operand(r0));
+ ConvertToDoubleStub stub3(r3, r2, r7, r4);
+ __ push(lr);
+ __ Call(stub3.GetCode(), RelocInfo::CODE_TARGET);
+ __ pop(lr);
+ }
- __ bind(&finished_loading_r0);
+ // HEAP_NUMBERS stub is slower than GENERIC on a pair of smis.
+ // r0 is known to be a smi. If r1 is also a smi then switch to GENERIC.
+ Label r1_is_not_smi;
+ if (runtime_operands_type_ == BinaryOpIC::HEAP_NUMBERS) {
+ __ tst(r1, Operand(kSmiTagMask));
+ __ b(ne, &r1_is_not_smi);
+ GenerateTypeTransition(masm); // Tail call.
+ }
- // Move r1 to a double in r0-r1.
- __ tst(r1, Operand(kSmiTagMask));
- __ b(eq, &r1_is_smi); // It's a Smi so don't check it's a heap number.
- __ bind(&r1_is_not_smi);
- __ ldr(r4, FieldMemOperand(r1, HeapNumber::kMapOffset));
- __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
- __ cmp(r4, heap_number_map);
- __ b(ne, &slow);
- if (mode_ == OVERWRITE_LEFT) {
- __ mov(r5, Operand(r1)); // Overwrite this heap number.
- }
- if (use_fp_registers) {
- CpuFeatures::Scope scope(VFP3);
- // Load the double from tagged HeapNumber r1 to d6.
- __ sub(r7, r1, Operand(kHeapObjectTag));
- __ vldr(d6, r7, HeapNumber::kValueOffset);
- } else {
- // Calling convention says that first double is in r0 and r1.
- __ Ldrd(r0, r1, FieldMemOperand(r1, HeapNumber::kValueOffset));
- }
- __ jmp(&finished_loading_r1);
- __ bind(&r1_is_smi);
- if (mode_ == OVERWRITE_LEFT) {
- // We can't overwrite a Smi so get address of new heap number into r5.
- __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
- }
+ __ bind(&finished_loading_r0);
- if (CpuFeatures::IsSupported(VFP3)) {
- CpuFeatures::Scope scope(VFP3);
- // Convert smi in r1 to double in d6.
- __ mov(r7, Operand(r1, ASR, kSmiTagSize));
- __ vmov(s13, r7);
- __ vcvt_f64_s32(d6, s13);
- if (!use_fp_registers) {
- __ vmov(r0, r1, d6);
+ // Move r1 to a double in r0-r1.
+ __ tst(r1, Operand(kSmiTagMask));
+ __ b(eq, &r1_is_smi); // It's a Smi so don't check it's a heap number.
+ __ bind(&r1_is_not_smi);
+ __ ldr(r4, FieldMemOperand(r1, HeapNumber::kMapOffset));
+ __ AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex);
+ __ cmp(r4, heap_number_map);
+ __ b(ne, &slow);
+ if (mode_ == OVERWRITE_LEFT) {
+ __ mov(r5, Operand(r1)); // Overwrite this heap number.
+ }
+ if (use_fp_registers) {
+ CpuFeatures::Scope scope(VFP3);
+ // Load the double from tagged HeapNumber r1 to d6.
+ __ sub(r7, r1, Operand(kHeapObjectTag));
+ __ vldr(d6, r7, HeapNumber::kValueOffset);
+ } else {
+ // Calling convention says that first double is in r0 and r1.
+ __ Ldrd(r0, r1, FieldMemOperand(r1, HeapNumber::kValueOffset));
+ }
+ __ jmp(&finished_loading_r1);
+ __ bind(&r1_is_smi);
+ if (mode_ == OVERWRITE_LEFT) {
+ // We can't overwrite a Smi so get address of new heap number into r5.
+ __ AllocateHeapNumber(r5, r4, r7, heap_number_map, &slow);
}
- } else {
- // Write Smi from r1 to r1 and r0 in double format.
- __ mov(r7, Operand(r1));
- ConvertToDoubleStub stub4(r1, r0, r7, r9);
- __ push(lr);
- __ Call(stub4.GetCode(), RelocInfo::CODE_TARGET);
- __ pop(lr);
- }
- __ bind(&finished_loading_r1);
+ if (CpuFeatures::IsSupported(VFP3)) {
+ CpuFeatures::Scope scope(VFP3);
+ // Convert smi in r1 to double in d6.
+ __ mov(r7, Operand(r1, ASR, kSmiTagSize));
+ __ vmov(s13, r7);
+ __ vcvt_f64_s32(d6, s13);
+ if (!use_fp_registers) {
+ __ vmov(r0, r1, d6);
+ }
+ } else {
+ // Write Smi from r1 to r1 and r0 in double format.
+ __ mov(r7, Operand(r1));
+ ConvertToDoubleStub stub4(r1, r0, r7, r9);
+ __ push(lr);
+ __ Call(stub4.GetCode(), RelocInfo::CODE_TARGET);
+ __ pop(lr);
+ }
- __ bind(&do_the_call);
- // If we are inlining the operation using VFP3 instructions for
- // add, subtract, multiply, or divide, the arguments are in d6 and d7.
- if (use_fp_registers) {
- CpuFeatures::Scope scope(VFP3);
- // ARMv7 VFP3 instructions to implement
- // double precision, add, subtract, multiply, divide.
-
- if (Token::MUL == op_) {
- __ vmul(d5, d6, d7);
- } else if (Token::DIV == op_) {
- __ vdiv(d5, d6, d7);
- } else if (Token::ADD == op_) {
- __ vadd(d5, d6, d7);
- } else if (Token::SUB == op_) {
- __ vsub(d5, d6, d7);
+ __ bind(&finished_loading_r1);
+ }
+
+ if (generate_code_to_calculate_answer || do_the_call.is_linked()) {
+ __ bind(&do_the_call);
+ // If we are inlining the operation using VFP3 instructions for
+ // add, subtract, multiply, or divide, the arguments are in d6 and d7.
+ if (use_fp_registers) {
+ CpuFeatures::Scope scope(VFP3);
+ // ARMv7 VFP3 instructions to implement
+ // double precision, add, subtract, multiply, divide.
+
+ if (Token::MUL == op_) {
+ __ vmul(d5, d6, d7);
+ } else if (Token::DIV == op_) {
+ __ vdiv(d5, d6, d7);
+ } else if (Token::ADD == op_) {
+ __ vadd(d5, d6, d7);
+ } else if (Token::SUB == op_) {
+ __ vsub(d5, d6, d7);
+ } else {
+ UNREACHABLE();
+ }
+ __ sub(r0, r5, Operand(kHeapObjectTag));
+ __ vstr(d5, r0, HeapNumber::kValueOffset);
+ __ add(r0, r0, Operand(kHeapObjectTag));
+ __ mov(pc, lr);
} else {
- UNREACHABLE();
+ // If we did not inline the operation, then the arguments are in:
+ // r0: Left value (least significant part of mantissa).
+ // r1: Left value (sign, exponent, top of mantissa).
+ // r2: Right value (least significant part of mantissa).
+ // r3: Right value (sign, exponent, top of mantissa).
+ // r5: Address of heap number for result.
+
+ __ push(lr); // For later.
+ __ PrepareCallCFunction(4, r4); // Two doubles count as 4 arguments.
+ // Call C routine that may not cause GC or other trouble. r5 is callee
+ // save.
+ __ CallCFunction(ExternalReference::double_fp_operation(op_), 4);
+ // Store answer in the overwritable heap number.
+ #if !defined(USE_ARM_EABI)
+ // Double returned in fp coprocessor register 0 and 1, encoded as
+ // register cr8. Offsets must be divisible by 4 for coprocessor so we
+ // need to substract the tag from r5.
+ __ sub(r4, r5, Operand(kHeapObjectTag));
+ __ stc(p1, cr8, MemOperand(r4, HeapNumber::kValueOffset));
+ #else
+ // Double returned in registers 0 and 1.
+ __ Strd(r0, r1, FieldMemOperand(r5, HeapNumber::kValueOffset));
+ #endif
+ __ mov(r0, Operand(r5));
+ // And we are done.
+ __ pop(pc);
}
- __ sub(r0, r5, Operand(kHeapObjectTag));
- __ vstr(d5, r0, HeapNumber::kValueOffset);
- __ add(r0, r0, Operand(kHeapObjectTag));
- __ mov(pc, lr);
- } else {
- // If we did not inline the operation, then the arguments are in:
- // r0: Left value (least significant part of mantissa).
- // r1: Left value (sign, exponent, top of mantissa).
- // r2: Right value (least significant part of mantissa).
- // r3: Right value (sign, exponent, top of mantissa).
- // r5: Address of heap number for result.
-
- __ push(lr); // For later.
- __ PrepareCallCFunction(4, r4); // Two doubles count as 4 arguments.
- // Call C routine that may not cause GC or other trouble. r5 is callee
- // save.
- __ CallCFunction(ExternalReference::double_fp_operation(op_), 4);
- // Store answer in the overwritable heap number.
- #if !defined(USE_ARM_EABI)
- // Double returned in fp coprocessor register 0 and 1, encoded as register
- // cr8. Offsets must be divisible by 4 for coprocessor so we need to
- // substract the tag from r5.
- __ sub(r4, r5, Operand(kHeapObjectTag));
- __ stc(p1, cr8, MemOperand(r4, HeapNumber::kValueOffset));
- #else
- // Double returned in registers 0 and 1.
- __ Strd(r0, r1, FieldMemOperand(r5, HeapNumber::kValueOffset));
- #endif
- __ mov(r0, Operand(r5));
- // And we are done.
- __ pop(pc);
}
}
+ if (!generate_code_to_calculate_answer &&
+ !slow_reverse.is_linked() &&
+ !slow.is_linked()) {
+ return;
+ }
+
if (lhs.is(r0)) {
__ b(&slow);
__ bind(&slow_reverse);
@@ -7913,7 +8058,11 @@ void GenericBinaryOpStub::HandleNonSmiBitwiseOp(MacroAssembler* masm,
// The code below for writing into heap numbers isn't capable of writing
// the register as an unsigned int so we go to slow case if we hit this
// case.
- __ b(mi, &slow);
+ if (CpuFeatures::IsSupported(VFP3)) {
+ __ b(mi, &result_not_a_smi);
+ } else {
+ __ b(mi, &slow);
+ }
break;
case Token::SHL:
// Use only the 5 least significant bits of the shift count.
@@ -7957,10 +8106,24 @@ void GenericBinaryOpStub::HandleNonSmiBitwiseOp(MacroAssembler* masm,
// result.
__ mov(r0, Operand(r5));
- // Tail call that writes the int32 in r2 to the heap number in r0, using
- // r3 as scratch. r0 is preserved and returned.
- WriteInt32ToHeapNumberStub stub(r2, r0, r3);
- __ Jump(stub.GetCode(), RelocInfo::CODE_TARGET);
+ if (CpuFeatures::IsSupported(VFP3)) {
+ // Convert the int32 in r2 to the heap number in r0. r3 is corrupted.
+ CpuFeatures::Scope scope(VFP3);
+ __ vmov(s0, r2);
+ if (op_ == Token::SHR) {
+ __ vcvt_f64_u32(d0, s0);
+ } else {
+ __ vcvt_f64_s32(d0, s0);
+ }
+ __ sub(r3, r0, Operand(kHeapObjectTag));
+ __ vstr(d0, r3, HeapNumber::kValueOffset);
+ __ Ret();
+ } else {
+ // Tail call that writes the int32 in r2 to the heap number in r0, using
+ // r3 as scratch. r0 is preserved and returned.
+ WriteInt32ToHeapNumberStub stub(r2, r0, r3);
+ __ TailCallStub(&stub);
+ }
if (mode_ != NO_OVERWRITE) {
__ bind(&have_to_allocate);
@@ -8597,29 +8760,15 @@ void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
__ Push(r1, r0);
- // Internal frame is necessary to handle exceptions properly.
- __ EnterInternalFrame();
- // Call the stub proper to get the result in r0.
- __ Call(&get_result);
- __ LeaveInternalFrame();
-
- __ push(r0);
-
- __ mov(r0, Operand(Smi::FromInt(MinorKey())));
- __ push(r0);
- __ mov(r0, Operand(Smi::FromInt(op_)));
- __ push(r0);
+ __ mov(r2, Operand(Smi::FromInt(MinorKey())));
+ __ mov(r1, Operand(Smi::FromInt(op_)));
__ mov(r0, Operand(Smi::FromInt(runtime_operands_type_)));
- __ push(r0);
+ __ Push(r2, r1, r0);
__ TailCallExternalReference(
ExternalReference(IC_Utility(IC::kBinaryOp_Patch)),
- 6,
+ 5,
1);
-
- // The entry point for the result calculation is assumed to be immediately
- // after this sequence.
- __ bind(&get_result);
}
@@ -8751,16 +8900,23 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
// Go slow case if the value of the expression is zero
// to make sure that we switch between 0 and -0.
- __ cmp(r0, Operand(0));
- __ b(eq, &slow);
-
- // The value of the expression is a smi that is not zero. Try
- // optimistic subtraction '0 - value'.
- __ rsb(r1, r0, Operand(0), SetCC);
- __ b(vs, &slow);
-
- __ mov(r0, Operand(r1)); // Set r0 to result.
- __ b(&done);
+ if (negative_zero_ == kStrictNegativeZero) {
+ // If we have to check for zero, then we can check for the max negative
+ // smi while we are at it.
+ __ bic(ip, r0, Operand(0x80000000), SetCC);
+ __ b(eq, &slow);
+ __ rsb(r0, r0, Operand(0));
+ __ StubReturn(1);
+ } else {
+ // The value of the expression is a smi and 0 is OK for -0. Try
+ // optimistic subtraction '0 - value'.
+ __ rsb(r0, r0, Operand(0), SetCC);
+ __ StubReturn(1, vc);
+ // We don't have to reverse the optimistic neg since the only case
+ // where we fall through is the minimum negative Smi, which is the case
+ // where the neg leaves the register unchanged.
+ __ jmp(&slow); // Go slow on max negative Smi.
+ }
__ bind(&try_float);
__ ldr(r1, FieldMemOperand(r0, HeapObject::kMapOffset));
@@ -8768,7 +8924,7 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
__ cmp(r1, heap_number_map);
__ b(ne, &slow);
// r0 is a heap number. Get a new heap number in r1.
- if (overwrite_) {
+ if (overwrite_ == UNARY_OVERWRITE) {
__ ldr(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
__ eor(r2, r2, Operand(HeapNumber::kSignMask)); // Flip sign.
__ str(r2, FieldMemOperand(r0, HeapNumber::kExponentOffset));
@@ -8801,7 +8957,7 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
__ b(&done);
__ bind(&try_float);
- if (!overwrite_) {
+ if (!overwrite_ == UNARY_OVERWRITE) {
// Allocate a fresh heap number, but don't overwrite r0 until
// we're sure we can do it without going through the slow case
// that needs the value in r0.
@@ -8809,12 +8965,21 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
__ mov(r0, Operand(r2));
}
- // WriteInt32ToHeapNumberStub does not trigger GC, so we do not
- // have to set up a frame.
- WriteInt32ToHeapNumberStub stub(r1, r0, r2);
- __ push(lr);
- __ Call(stub.GetCode(), RelocInfo::CODE_TARGET);
- __ pop(lr);
+ if (CpuFeatures::IsSupported(VFP3)) {
+ // Convert the int32 in r1 to the heap number in r0. r2 is corrupted.
+ CpuFeatures::Scope scope(VFP3);
+ __ vmov(s0, r1);
+ __ vcvt_f64_s32(d0, s0);
+ __ sub(r2, r0, Operand(kHeapObjectTag));
+ __ vstr(d0, r2, HeapNumber::kValueOffset);
+ } else {
+ // WriteInt32ToHeapNumberStub does not trigger GC, so we do not
+ // have to set up a frame.
+ WriteInt32ToHeapNumberStub stub(r1, r0, r2);
+ __ push(lr);
+ __ Call(stub.GetCode(), RelocInfo::CODE_TARGET);
+ __ pop(lr);
+ }
} else {
UNIMPLEMENTED();
}
diff --git a/deps/v8/src/arm/codegen-arm.h b/deps/v8/src/arm/codegen-arm.h
index 2d8a935203..855723d9c0 100644
--- a/deps/v8/src/arm/codegen-arm.h
+++ b/deps/v8/src/arm/codegen-arm.h
@@ -44,6 +44,7 @@ class RegisterFile;
enum InitState { CONST_INIT, NOT_CONST_INIT };
enum TypeofState { INSIDE_TYPEOF, NOT_INSIDE_TYPEOF };
enum GenerateInlineSmi { DONT_GENERATE_INLINE_SMI, GENERATE_INLINE_SMI };
+enum WriteBarrierCharacter { UNLIKELY_SMI, LIKELY_SMI, NEVER_NEWSPACE };
// -------------------------------------------------------------------------
@@ -100,7 +101,7 @@ class Reference BASE_EMBEDDED {
// on the expression stack. The value is stored in the location specified
// by the reference, and is left on top of the stack, after the reference
// is popped from beneath it (unloaded).
- void SetValue(InitState init_state);
+ void SetValue(InitState init_state, WriteBarrierCharacter wb);
// This is in preparation for something that uses the reference on the stack.
// If we need this reference afterwards get then dup it now. Otherwise mark
@@ -384,7 +385,7 @@ class CodeGenerator: public AstVisitor {
// Store a keyed property. Key and receiver are on the stack and the value is
// in r0. Result is returned in r0.
- void EmitKeyedStore(StaticType* key_type);
+ void EmitKeyedStore(StaticType* key_type, WriteBarrierCharacter wb_info);
void LoadFromGlobalSlotCheckExtensions(Slot* slot,
TypeofState typeof_state,
diff --git a/deps/v8/src/arm/disasm-arm.cc b/deps/v8/src/arm/disasm-arm.cc
index fb17d45d5a..a52417beef 100644
--- a/deps/v8/src/arm/disasm-arm.cc
+++ b/deps/v8/src/arm/disasm-arm.cc
@@ -1047,7 +1047,14 @@ void Decoder::DecodeTypeVFP(Instr* instr) {
if (instr->Bit(4) == 0) {
if (instr->Opc1Field() == 0x7) {
// Other data processing instructions
- if ((instr->Opc2Field() == 0x7) && (instr->Opc3Field() == 0x3)) {
+ if ((instr->Opc2Field() == 0x0) && (instr->Opc3Field() == 0x1)) {
+ // vmov register to register.
+ if (instr->SzField() == 0x1) {
+ Format(instr, "vmov.f64'cond 'Dd, 'Dm");
+ } else {
+ Unknown(instr); // Not used by V8.
+ }
+ } else if ((instr->Opc2Field() == 0x7) && (instr->Opc3Field() == 0x3)) {
DecodeVCVTBetweenDoubleAndSingle(instr);
} else if ((instr->Opc2Field() == 0x8) && (instr->Opc3Field() & 0x1)) {
DecodeVCVTBetweenFloatingPointAndInteger(instr);
diff --git a/deps/v8/src/arm/full-codegen-arm.cc b/deps/v8/src/arm/full-codegen-arm.cc
index 673287388a..080cb83392 100644
--- a/deps/v8/src/arm/full-codegen-arm.cc
+++ b/deps/v8/src/arm/full-codegen-arm.cc
@@ -2736,9 +2736,11 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
case Token::SUB: {
Comment cmt(masm_, "[ UnaryOperation (SUB)");
- bool overwrite =
+ bool can_overwrite =
(expr->expression()->AsBinaryOperation() != NULL &&
expr->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
+ UnaryOverwriteMode overwrite =
+ can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
GenericUnaryOpStub stub(Token::SUB, overwrite);
// GenericUnaryOpStub expects the argument to be in the
// accumulator register r0.
@@ -2750,9 +2752,11 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
case Token::BIT_NOT: {
Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)");
- bool overwrite =
+ bool can_overwrite =
(expr->expression()->AsBinaryOperation() != NULL &&
expr->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
+ UnaryOverwriteMode overwrite =
+ can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
// GenericUnaryOpStub expects the argument to be in the
// accumulator register r0.
diff --git a/deps/v8/src/arm/ic-arm.cc b/deps/v8/src/arm/ic-arm.cc
index 0af10362f0..97e61488d3 100644
--- a/deps/v8/src/arm/ic-arm.cc
+++ b/deps/v8/src/arm/ic-arm.cc
@@ -64,12 +64,12 @@ static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
// Generated code falls through if the receiver is a regular non-global
// JS object with slow properties and no interceptors.
-static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm,
- Register receiver,
- Register elements,
- Register t0,
- Register t1,
- Label* miss) {
+static void GenerateStringDictionaryReceiverCheck(MacroAssembler* masm,
+ Register receiver,
+ Register elements,
+ Register t0,
+ Register t1,
+ Label* miss) {
// Register usage:
// receiver: holds the receiver on entry and is unchanged.
// elements: holds the property dictionary on fall through.
@@ -105,33 +105,16 @@ static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm,
}
-// Helper function used from LoadIC/CallIC GenerateNormal.
-//
-// elements: Property dictionary. It is not clobbered if a jump to the miss
-// label is done.
-// name: Property name. It is not clobbered if a jump to the miss label is
-// done
-// result: Register for the result. It is only updated if a jump to the miss
-// label is not done. Can be the same as elements or name clobbering
-// one of these in the case of not jumping to the miss label.
-// The two scratch registers need to be different from elements, name and
-// result.
-// The generated code assumes that the receiver has slow properties,
-// is not a global object and does not have interceptors.
-static void GenerateDictionaryLoad(MacroAssembler* masm,
- Label* miss,
- Register elements,
- Register name,
- Register result,
- Register scratch1,
- Register scratch2) {
- // Main use of the scratch registers.
- // scratch1: Used as temporary and to hold the capacity of the property
- // dictionary.
- // scratch2: Used as temporary.
-
- Label done;
-
+// Probe the string dictionary in the |elements| register. Jump to the
+// |done| label if a property with the given name is found. Jump to
+// the |miss| label otherwise.
+static void GenerateStringDictionaryProbes(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register scratch1,
+ Register scratch2) {
// Compute the capacity mask.
const int kCapacityOffset = StringDictionary::kHeaderSize +
StringDictionary::kCapacityIndex * kPointerSize;
@@ -170,16 +153,56 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
__ ldr(ip, FieldMemOperand(scratch2, kElementsStartOffset));
__ cmp(name, Operand(ip));
if (i != kProbes - 1) {
- __ b(eq, &done);
+ __ b(eq, done);
} else {
__ b(ne, miss);
}
}
+}
- // Check that the value is a normal property.
+
+// Helper function used from LoadIC/CallIC GenerateNormal.
+//
+// elements: Property dictionary. It is not clobbered if a jump to the miss
+// label is done.
+// name: Property name. It is not clobbered if a jump to the miss label is
+// done
+// result: Register for the result. It is only updated if a jump to the miss
+// label is not done. Can be the same as elements or name clobbering
+// one of these in the case of not jumping to the miss label.
+// The two scratch registers need to be different from elements, name and
+// result.
+// The generated code assumes that the receiver has slow properties,
+// is not a global object and does not have interceptors.
+static void GenerateDictionaryLoad(MacroAssembler* masm,
+ Label* miss,
+ Register elements,
+ Register name,
+ Register result,
+ Register scratch1,
+ Register scratch2) {
+ // Main use of the scratch registers.
+ // scratch1: Used as temporary and to hold the capacity of the property
+ // dictionary.
+ // scratch2: Used as temporary.
+ Label done;
+
+ // Probe the dictionary.
+ GenerateStringDictionaryProbes(masm,
+ miss,
+ &done,
+ elements,
+ name,
+ scratch1,
+ scratch2);
+
+ // If probing finds an entry check that the value is a normal
+ // property.
__ bind(&done); // scratch2 == elements + 4 * index
- __ ldr(scratch1,
- FieldMemOperand(scratch2, kElementsStartOffset + 2 * kPointerSize));
+ const int kElementsStartOffset = StringDictionary::kHeaderSize +
+ StringDictionary::kElementsStartIndex * kPointerSize;
+ const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
+ __ ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
__ tst(scratch1, Operand(PropertyDetails::TypeField::mask() << kSmiTagSize));
__ b(ne, miss);
@@ -189,6 +212,63 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
}
+// Helper function used from StoreIC::GenerateNormal.
+//
+// elements: Property dictionary. It is not clobbered if a jump to the miss
+// label is done.
+// name: Property name. It is not clobbered if a jump to the miss label is
+// done
+// value: The value to store.
+// The two scratch registers need to be different from elements, name and
+// result.
+// The generated code assumes that the receiver has slow properties,
+// is not a global object and does not have interceptors.
+static void GenerateDictionaryStore(MacroAssembler* masm,
+ Label* miss,
+ Register elements,
+ Register name,
+ Register value,
+ Register scratch1,
+ Register scratch2) {
+ // Main use of the scratch registers.
+ // scratch1: Used as temporary and to hold the capacity of the property
+ // dictionary.
+ // scratch2: Used as temporary.
+ Label done;
+
+ // Probe the dictionary.
+ GenerateStringDictionaryProbes(masm,
+ miss,
+ &done,
+ elements,
+ name,
+ scratch1,
+ scratch2);
+
+ // If probing finds an entry in the dictionary check that the value
+ // is a normal property that is not read only.
+ __ bind(&done); // scratch2 == elements + 4 * index
+ const int kElementsStartOffset = StringDictionary::kHeaderSize +
+ StringDictionary::kElementsStartIndex * kPointerSize;
+ const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
+ const int kTypeAndReadOnlyMask
+ = (PropertyDetails::TypeField::mask() |
+ PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize;
+ __ ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset));
+ __ tst(scratch1, Operand(kTypeAndReadOnlyMask));
+ __ b(ne, miss);
+
+ // Store the value at the masked, scaled index and return.
+ const int kValueOffset = kElementsStartOffset + kPointerSize;
+ __ add(scratch2, scratch2, Operand(kValueOffset - kHeapObjectTag));
+ __ str(value, MemOperand(scratch2));
+
+ // Update the write barrier. Make sure not to clobber the value.
+ __ mov(scratch1, value);
+ __ RecordWrite(elements, scratch2, scratch1);
+}
+
+
static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
Label* miss,
Register elements,
@@ -560,7 +640,7 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) {
// Get the receiver of the function from the stack into r1.
__ ldr(r1, MemOperand(sp, argc * kPointerSize));
- GenerateDictionaryLoadReceiverCheck(masm, r1, r0, r3, r4, &miss);
+ GenerateStringDictionaryReceiverCheck(masm, r1, r0, r3, r4, &miss);
// r0: elements
// Search the dictionary - put result in register r1.
@@ -815,7 +895,7 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
// -----------------------------------
Label miss;
- GenerateDictionaryLoadReceiverCheck(masm, r0, r1, r3, r4, &miss);
+ GenerateStringDictionaryReceiverCheck(masm, r0, r1, r3, r4, &miss);
// r1: elements
GenerateDictionaryLoad(masm, &miss, r1, r2, r0, r3, r4);
@@ -2138,6 +2218,27 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
}
+void StoreIC::GenerateNormal(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- r0 : value
+ // -- r1 : receiver
+ // -- r2 : name
+ // -- lr : return address
+ // -----------------------------------
+ Label miss;
+
+ GenerateStringDictionaryReceiverCheck(masm, r1, r3, r4, r5, &miss);
+
+ GenerateDictionaryStore(masm, &miss, r3, r2, r0, r4, r5);
+ __ IncrementCounter(&Counters::store_normal_hit, 1, r4, r5);
+ __ Ret();
+
+ __ bind(&miss);
+ __ IncrementCounter(&Counters::store_normal_miss, 1, r4, r5);
+ GenerateMiss(masm);
+}
+
+
#undef __
diff --git a/deps/v8/src/arm/macro-assembler-arm.cc b/deps/v8/src/arm/macro-assembler-arm.cc
index 630e0b80ac..2896cc96e7 100644
--- a/deps/v8/src/arm/macro-assembler-arm.cc
+++ b/deps/v8/src/arm/macro-assembler-arm.cc
@@ -310,32 +310,28 @@ void MacroAssembler::StoreRoot(Register source,
void MacroAssembler::RecordWriteHelper(Register object,
- Operand offset,
- Register scratch0,
- Register scratch1) {
+ Register address,
+ Register scratch) {
if (FLAG_debug_code) {
// Check that the object is not in new space.
Label not_in_new_space;
- InNewSpace(object, scratch1, ne, &not_in_new_space);
+ InNewSpace(object, scratch, ne, &not_in_new_space);
Abort("new-space object passed to RecordWriteHelper");
bind(&not_in_new_space);
}
- // Add offset into the object.
- add(scratch0, object, offset);
-
// Calculate page address.
Bfc(object, 0, kPageSizeBits);
// Calculate region number.
- Ubfx(scratch0, scratch0, Page::kRegionSizeLog2,
+ Ubfx(address, address, Page::kRegionSizeLog2,
kPageSizeBits - Page::kRegionSizeLog2);
// Mark region dirty.
- ldr(scratch1, MemOperand(object, Page::kDirtyFlagOffset));
+ ldr(scratch, MemOperand(object, Page::kDirtyFlagOffset));
mov(ip, Operand(1));
- orr(scratch1, scratch1, Operand(ip, LSL, scratch0));
- str(scratch1, MemOperand(object, Page::kDirtyFlagOffset));
+ orr(scratch, scratch, Operand(ip, LSL, address));
+ str(scratch, MemOperand(object, Page::kDirtyFlagOffset));
}
@@ -368,8 +364,11 @@ void MacroAssembler::RecordWrite(Register object,
// region marks for new space pages.
InNewSpace(object, scratch0, eq, &done);
+ // Add offset into the object.
+ add(scratch0, object, offset);
+
// Record the actual write.
- RecordWriteHelper(object, offset, scratch0, scratch1);
+ RecordWriteHelper(object, scratch0, scratch1);
bind(&done);
@@ -383,6 +382,38 @@ void MacroAssembler::RecordWrite(Register object,
}
+// Will clobber 4 registers: object, address, scratch, ip. The
+// register 'object' contains a heap object pointer. The heap object
+// tag is shifted away.
+void MacroAssembler::RecordWrite(Register object,
+ Register address,
+ Register scratch) {
+ // The compiled code assumes that record write doesn't change the
+ // context register, so we check that none of the clobbered
+ // registers are cp.
+ ASSERT(!object.is(cp) && !address.is(cp) && !scratch.is(cp));
+
+ Label done;
+
+ // First, test that the object is not in the new space. We cannot set
+ // region marks for new space pages.
+ InNewSpace(object, scratch, eq, &done);
+
+ // Record the actual write.
+ RecordWriteHelper(object, address, scratch);
+
+ bind(&done);
+
+ // Clobber all input registers when running with the debug-code flag
+ // turned on to provoke errors.
+ if (FLAG_debug_code) {
+ mov(object, Operand(BitCast<int32_t>(kZapValue)));
+ mov(address, Operand(BitCast<int32_t>(kZapValue)));
+ mov(scratch, Operand(BitCast<int32_t>(kZapValue)));
+ }
+}
+
+
void MacroAssembler::Ldrd(Register dst1, Register dst2,
const MemOperand& src, Condition cond) {
ASSERT(src.rm().is(no_reg));
@@ -1341,12 +1372,12 @@ void MacroAssembler::TailCallStub(CodeStub* stub, Condition cond) {
}
-void MacroAssembler::StubReturn(int argc) {
+void MacroAssembler::StubReturn(int argc, Condition cond) {
ASSERT(argc >= 1 && generating_stub());
if (argc > 1) {
- add(sp, sp, Operand((argc - 1) * kPointerSize));
+ add(sp, sp, Operand((argc - 1) * kPointerSize), LeaveCC, cond);
}
- Ret();
+ Ret(cond);
}
diff --git a/deps/v8/src/arm/macro-assembler-arm.h b/deps/v8/src/arm/macro-assembler-arm.h
index c3f45a632e..f1f7de7fe4 100644
--- a/deps/v8/src/arm/macro-assembler-arm.h
+++ b/deps/v8/src/arm/macro-assembler-arm.h
@@ -137,22 +137,32 @@ class MacroAssembler: public Assembler {
Label* branch);
- // For the page containing |object| mark the region covering [object+offset]
+ // For the page containing |object| mark the region covering [address]
// dirty. The object address must be in the first 8K of an allocated page.
void RecordWriteHelper(Register object,
- Operand offset,
- Register scratch0,
- Register scratch1);
+ Register address,
+ Register scratch);
- // For the page containing |object| mark the region covering [object+offset]
- // dirty. The object address must be in the first 8K of an allocated page.
- // The 'scratch' registers are used in the implementation and all 3 registers
- // are clobbered by the operation, as well as the ip register.
+ // For the page containing |object| mark the region covering
+ // [object+offset] dirty. The object address must be in the first 8K
+ // of an allocated page. The 'scratch' registers are used in the
+ // implementation and all 3 registers are clobbered by the
+ // operation, as well as the ip register. RecordWrite updates the
+ // write barrier even when storing smis.
void RecordWrite(Register object,
Operand offset,
Register scratch0,
Register scratch1);
+ // For the page containing |object| mark the region covering
+ // [address] dirty. The object address must be in the first 8K of an
+ // allocated page. All 3 registers are clobbered by the operation,
+ // as well as the ip register. RecordWrite updates the write barrier
+ // even when storing smis.
+ void RecordWrite(Register object,
+ Register address,
+ Register scratch);
+
// Push two registers. Pushes leftmost register first (to highest address).
void Push(Register src1, Register src2, Condition cond = al) {
ASSERT(!src1.is(src2));
@@ -527,7 +537,7 @@ class MacroAssembler: public Assembler {
void TailCallStub(CodeStub* stub, Condition cond = al);
// Return from a code stub after popping its arguments.
- void StubReturn(int argc);
+ void StubReturn(int argc, Condition cond = al);
// Call a runtime routine.
void CallRuntime(Runtime::Function* f, int num_arguments);
diff --git a/deps/v8/src/arm/simulator-arm.cc b/deps/v8/src/arm/simulator-arm.cc
index 6240cd4787..f09ce0035f 100644
--- a/deps/v8/src/arm/simulator-arm.cc
+++ b/deps/v8/src/arm/simulator-arm.cc
@@ -2276,7 +2276,14 @@ void Simulator::DecodeTypeVFP(Instr* instr) {
if (instr->Bit(4) == 0) {
if (instr->Opc1Field() == 0x7) {
// Other data processing instructions
- if ((instr->Opc2Field() == 0x7) && (instr->Opc3Field() == 0x3)) {
+ if ((instr->Opc2Field() == 0x0) && (instr->Opc3Field() == 0x1)) {
+ // vmov register to register.
+ if (instr->SzField() == 0x1) {
+ set_d_register_from_double(vd, get_double_from_d_register(vm));
+ } else {
+ UNREACHABLE(); // Not used by V8.
+ }
+ } else if ((instr->Opc2Field() == 0x7) && (instr->Opc3Field() == 0x3)) {
DecodeVCVTBetweenDoubleAndSingle(instr);
} else if ((instr->Opc2Field() == 0x8) && (instr->Opc3Field() & 0x1)) {
DecodeVCVTBetweenFloatingPointAndInteger(instr);
diff --git a/deps/v8/src/arm/stub-cache-arm.cc b/deps/v8/src/arm/stub-cache-arm.cc
index 3e5ba1126f..0e649ccd13 100644
--- a/deps/v8/src/arm/stub-cache-arm.cc
+++ b/deps/v8/src/arm/stub-cache-arm.cc
@@ -741,7 +741,8 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
Register scratch,
String* name,
int save_at_depth,
- Label* miss) {
+ Label* miss,
+ Register extra) {
// Check that the maps haven't changed.
Register result =
masm()->CheckMaps(object, object_reg, holder, holder_reg, scratch,
diff --git a/deps/v8/src/builtins.cc b/deps/v8/src/builtins.cc
index c8d4e097a5..ad52ea18b8 100644
--- a/deps/v8/src/builtins.cc
+++ b/deps/v8/src/builtins.cc
@@ -1263,6 +1263,11 @@ static void Generate_StoreIC_Miss(MacroAssembler* masm) {
}
+static void Generate_StoreIC_Normal(MacroAssembler* masm) {
+ StoreIC::GenerateNormal(masm);
+}
+
+
static void Generate_StoreIC_Megamorphic(MacroAssembler* masm) {
StoreIC::GenerateMegamorphic(masm);
}
diff --git a/deps/v8/src/builtins.h b/deps/v8/src/builtins.h
index 1fab375476..3dcab627b2 100644
--- a/deps/v8/src/builtins.h
+++ b/deps/v8/src/builtins.h
@@ -98,6 +98,7 @@ enum BuiltinExtraArguments {
\
V(StoreIC_Initialize, STORE_IC, UNINITIALIZED) \
V(StoreIC_ArrayLength, STORE_IC, MONOMORPHIC) \
+ V(StoreIC_Normal, STORE_IC, MONOMORPHIC) \
V(StoreIC_Megamorphic, STORE_IC, MEGAMORPHIC) \
\
V(KeyedStoreIC_Initialize, KEYED_STORE_IC, UNINITIALIZED) \
diff --git a/deps/v8/src/codegen.cc b/deps/v8/src/codegen.cc
index 686e173e55..8864c95a6f 100644
--- a/deps/v8/src/codegen.cc
+++ b/deps/v8/src/codegen.cc
@@ -460,11 +460,17 @@ void CodeGenerator::CodeForSourcePosition(int pos) {
const char* GenericUnaryOpStub::GetName() {
switch (op_) {
case Token::SUB:
- return overwrite_
- ? "GenericUnaryOpStub_SUB_Overwrite"
- : "GenericUnaryOpStub_SUB_Alloc";
+ if (negative_zero_ == kStrictNegativeZero) {
+ return overwrite_ == UNARY_OVERWRITE
+ ? "GenericUnaryOpStub_SUB_Overwrite_Strict0"
+ : "GenericUnaryOpStub_SUB_Alloc_Strict0";
+ } else {
+ return overwrite_ == UNARY_OVERWRITE
+ ? "GenericUnaryOpStub_SUB_Overwrite_Ignore0"
+ : "GenericUnaryOpStub_SUB_Alloc_Ignore0";
+ }
case Token::BIT_NOT:
- return overwrite_
+ return overwrite_ == UNARY_OVERWRITE
? "GenericUnaryOpStub_BIT_NOT_Overwrite"
: "GenericUnaryOpStub_BIT_NOT_Alloc";
default:
diff --git a/deps/v8/src/codegen.h b/deps/v8/src/codegen.h
index 0576fbb903..783bef00d1 100644
--- a/deps/v8/src/codegen.h
+++ b/deps/v8/src/codegen.h
@@ -75,6 +75,7 @@
// Mode to overwrite BinaryExpression values.
enum OverwriteMode { NO_OVERWRITE, OVERWRITE_LEFT, OVERWRITE_RIGHT };
+enum UnaryOverwriteMode { UNARY_OVERWRITE, UNARY_NO_OVERWRITE };
// Types of uncatchable exceptions.
enum UncatchableExceptionType { OUT_OF_MEMORY, TERMINATION };
@@ -414,21 +415,33 @@ class InstanceofStub: public CodeStub {
};
+enum NegativeZeroHandling {
+ kStrictNegativeZero,
+ kIgnoreNegativeZero
+};
+
+
class GenericUnaryOpStub : public CodeStub {
public:
- GenericUnaryOpStub(Token::Value op, bool overwrite)
- : op_(op), overwrite_(overwrite) { }
+ GenericUnaryOpStub(Token::Value op,
+ UnaryOverwriteMode overwrite,
+ NegativeZeroHandling negative_zero = kStrictNegativeZero)
+ : op_(op), overwrite_(overwrite), negative_zero_(negative_zero) { }
private:
Token::Value op_;
- bool overwrite_;
+ UnaryOverwriteMode overwrite_;
+ NegativeZeroHandling negative_zero_;
- class OverwriteField: public BitField<int, 0, 1> {};
- class OpField: public BitField<Token::Value, 1, kMinorBits - 1> {};
+ class OverwriteField: public BitField<UnaryOverwriteMode, 0, 1> {};
+ class NegativeZeroField: public BitField<NegativeZeroHandling, 1, 1> {};
+ class OpField: public BitField<Token::Value, 2, kMinorBits - 2> {};
Major MajorKey() { return GenericUnaryOp; }
int MinorKey() {
- return OpField::encode(op_) | OverwriteField::encode(overwrite_);
+ return OpField::encode(op_) |
+ OverwriteField::encode(overwrite_) |
+ NegativeZeroField::encode(negative_zero_);
}
void Generate(MacroAssembler* masm);
diff --git a/deps/v8/src/date.js b/deps/v8/src/date.js
index e780cb86a5..9c42a04f67 100644
--- a/deps/v8/src/date.js
+++ b/deps/v8/src/date.js
@@ -347,9 +347,10 @@ function DateFromTime(t) {
function MakeDay(year, month, date) {
if (!$isFinite(year) || !$isFinite(month) || !$isFinite(date)) return $NaN;
- year = TO_INTEGER(year);
- month = TO_INTEGER(month);
- date = TO_INTEGER(date);
+ // Convert to integer and map -0 to 0.
+ year = TO_INTEGER_MAP_MINUS_ZERO(year);
+ month = TO_INTEGER_MAP_MINUS_ZERO(month);
+ date = TO_INTEGER_MAP_MINUS_ZERO(date);
if (year < kMinYear || year > kMaxYear ||
month < kMinMonth || month > kMaxMonth ||
@@ -452,111 +453,6 @@ var Date_cache = {
});
-// 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));
@@ -736,37 +632,50 @@ function DateGetTime() {
// ECMA 262 - 15.9.5.10
function DateGetFullYear() {
- return GetFullYearFrom(this)
+ var t = DATE_VALUE(this);
+ if (NUMBER_IS_NAN(t)) return t;
+ var cache = Date_cache;
+ if (cache.time === t) return cache.year;
+ return YEAR_FROM_TIME(LocalTimeNoCheck(t));
}
// ECMA 262 - 15.9.5.11
function DateGetUTCFullYear() {
- return GetUTCFullYearFrom(this)
+ var t = DATE_VALUE(this);
+ if (NUMBER_IS_NAN(t)) return t;
+ return YEAR_FROM_TIME(t);
}
// ECMA 262 - 15.9.5.12
function DateGetMonth() {
- return GetMonthFrom(this);
+ var t = DATE_VALUE(this);
+ if (NUMBER_IS_NAN(t)) return t;
+ return MONTH_FROM_TIME(LocalTimeNoCheck(t));
}
// ECMA 262 - 15.9.5.13
function DateGetUTCMonth() {
- return GetUTCMonthFrom(this);
+ var t = DATE_VALUE(this);
+ if (NUMBER_IS_NAN(t)) return t;
+ return MONTH_FROM_TIME(t);
}
// ECMA 262 - 15.9.5.14
function DateGetDate() {
- return GetDateFrom(this);
+ var t = DATE_VALUE(this);
+ if (NUMBER_IS_NAN(t)) return t;
+ return DATE_FROM_TIME(LocalTimeNoCheck(t));
}
// ECMA 262 - 15.9.5.15
function DateGetUTCDate() {
- return GetUTCDateFrom(this);
+ var t = DATE_VALUE(this);
+ return NAN_OR_DATE_FROM_TIME(t);
}
@@ -788,49 +697,62 @@ function DateGetUTCDay() {
// ECMA 262 - 15.9.5.18
function DateGetHours() {
- return GetHoursFrom(this);
+ var t = DATE_VALUE(this);
+ if (NUMBER_IS_NAN(t)) return t;
+ return HOUR_FROM_TIME(LocalTimeNoCheck(t));
}
// ECMA 262 - 15.9.5.19
function DateGetUTCHours() {
- return GetUTCHoursFrom(this);
+ var t = DATE_VALUE(this);
+ if (NUMBER_IS_NAN(t)) return t;
+ return HOUR_FROM_TIME(t);
}
// ECMA 262 - 15.9.5.20
function DateGetMinutes() {
- return GetMinutesFrom(this);
+ var t = DATE_VALUE(this);
+ if (NUMBER_IS_NAN(t)) return t;
+ return MIN_FROM_TIME(LocalTimeNoCheck(t));
}
// ECMA 262 - 15.9.5.21
function DateGetUTCMinutes() {
- return GetUTCMinutesFrom(this);
+ var t = DATE_VALUE(this);
+ return NAN_OR_MIN_FROM_TIME(t);
}
// ECMA 262 - 15.9.5.22
function DateGetSeconds() {
- return GetSecondsFrom(this);
+ var t = DATE_VALUE(this);
+ if (NUMBER_IS_NAN(t)) return t;
+ return SEC_FROM_TIME(LocalTimeNoCheck(t));
}
// ECMA 262 - 15.9.5.23
function DateGetUTCSeconds() {
- return GetUTCSecondsFrom(this);
+ var t = DATE_VALUE(this);
+ return NAN_OR_SEC_FROM_TIME(t);
}
// ECMA 262 - 15.9.5.24
function DateGetMilliseconds() {
- return GetMillisecondsFrom(this);
+ var t = DATE_VALUE(this);
+ if (NUMBER_IS_NAN(t)) return t;
+ return MS_FROM_TIME(LocalTimeNoCheck(t));
}
// ECMA 262 - 15.9.5.25
function DateGetUTCMilliseconds() {
- return GetUTCMillisecondsFrom(this);
+ var t = DATE_VALUE(this);
+ return NAN_OR_MS_FROM_TIME(t);
}
@@ -871,7 +793,7 @@ function DateSetUTCMilliseconds(ms) {
function DateSetSeconds(sec, ms) {
var t = LocalTime(DATE_VALUE(this));
sec = ToNumber(sec);
- ms = %_ArgumentsLength() < 2 ? GetMillisecondsFrom(this) : ToNumber(ms);
+ ms = %_ArgumentsLength() < 2 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
}
@@ -881,7 +803,7 @@ function DateSetSeconds(sec, ms) {
function DateSetUTCSeconds(sec, ms) {
var t = DATE_VALUE(this);
sec = ToNumber(sec);
- ms = %_ArgumentsLength() < 2 ? GetUTCMillisecondsFrom(this) : ToNumber(ms);
+ ms = %_ArgumentsLength() < 2 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
var time = MakeTime(HOUR_FROM_TIME(t), MIN_FROM_TIME(t), sec, ms);
return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
}
@@ -892,8 +814,8 @@ 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);
+ sec = argc < 2 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
+ ms = argc < 3 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
}
@@ -904,8 +826,8 @@ 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);
+ sec = argc < 2 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
+ ms = argc < 3 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
var time = MakeTime(HOUR_FROM_TIME(t), min, sec, ms);
return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
}
@@ -916,9 +838,9 @@ 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);
+ min = argc < 2 ? NAN_OR_MIN_FROM_TIME(t) : ToNumber(min);
+ sec = argc < 3 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
+ ms = argc < 4 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
var time = MakeTime(hour, min, sec, ms);
return %_SetValueOf(this, TimeClip(UTC(MakeDate(DAY(t), time))));
}
@@ -929,9 +851,9 @@ 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);
+ min = argc < 2 ? NAN_OR_MIN_FROM_TIME(t) : ToNumber(min);
+ sec = argc < 3 ? NAN_OR_SEC_FROM_TIME(t) : ToNumber(sec);
+ ms = argc < 4 ? NAN_OR_MS_FROM_TIME(t) : ToNumber(ms);
var time = MakeTime(hour, min, sec, ms);
return %_SetValueOf(this, TimeClip(MakeDate(DAY(t), time)));
}
@@ -959,7 +881,7 @@ function DateSetUTCDate(date) {
function DateSetMonth(month, date) {
var t = LocalTime(DATE_VALUE(this));
month = ToNumber(month);
- date = %_ArgumentsLength() < 2 ? GetDateFrom(this) : ToNumber(date);
+ date = %_ArgumentsLength() < 2 ? NAN_OR_DATE_FROM_TIME(t) : ToNumber(date);
var day = MakeDay(YEAR_FROM_TIME(t), month, date);
return %_SetValueOf(this, TimeClip(UTC(MakeDate(day, TimeWithinDay(t)))));
}
@@ -969,7 +891,7 @@ function DateSetMonth(month, date) {
function DateSetUTCMonth(month, date) {
var t = DATE_VALUE(this);
month = ToNumber(month);
- date = %_ArgumentsLength() < 2 ? GetUTCDateFrom(this) : ToNumber(date);
+ date = %_ArgumentsLength() < 2 ? NAN_OR_DATE_FROM_TIME(t) : ToNumber(date);
var day = MakeDay(YEAR_FROM_TIME(t), month, date);
return %_SetValueOf(this, TimeClip(MakeDate(day, TimeWithinDay(t))));
}
diff --git a/deps/v8/src/debug-debugger.js b/deps/v8/src/debug-debugger.js
index c808c87b8e..47a3c8e3ef 100644
--- a/deps/v8/src/debug-debugger.js
+++ b/deps/v8/src/debug-debugger.js
@@ -2070,6 +2070,7 @@ DebugCommandProcessor.prototype.changeLiveRequest_ = function(request, response)
return response.failed('Missing arguments');
}
var script_id = request.arguments.script_id;
+ var preview_only = !!request.arguments.preview_only;
var scripts = %DebugGetLoadedScripts();
@@ -2092,18 +2093,9 @@ DebugCommandProcessor.prototype.changeLiveRequest_ = function(request, response)
var new_source = request.arguments.new_source;
- try {
- Debug.LiveEdit.SetScriptSource(the_script, new_source, change_log);
- } catch (e) {
- if (e instanceof Debug.LiveEdit.Failure) {
- // Let's treat it as a "success" so that body with change_log will be
- // sent back. "change_log" will have "failure" field set.
- change_log.push( { failure: true, message: e.toString() } );
- } else {
- throw e;
- }
- }
- response.body = {change_log: change_log};
+ var result_description = Debug.LiveEdit.SetScriptSource(the_script,
+ new_source, preview_only, change_log);
+ response.body = {change_log: change_log, result: result_description};
};
diff --git a/deps/v8/src/debug.cc b/deps/v8/src/debug.cc
index d513b3121c..b8e0252aae 100644
--- a/deps/v8/src/debug.cc
+++ b/deps/v8/src/debug.cc
@@ -472,8 +472,9 @@ void BreakLocationIterator::ClearDebugBreakAtIC() {
RelocInfo::Mode mode = rmode();
if (RelocInfo::IsCodeTarget(mode)) {
+ AssertNoAllocation nogc;
Address target = original_rinfo()->target_address();
- Handle<Code> code(Code::GetCodeFromTargetAddress(target));
+ Code* code = Code::GetCodeFromTargetAddress(target);
// Restore the inlined version of keyed stores to get back to the
// fast case. We need to patch back the keyed store because no
@@ -684,6 +685,12 @@ void Debug::Setup(bool create_heap_objects) {
void Debug::HandleWeakDebugInfo(v8::Persistent<v8::Value> obj, void* data) {
DebugInfoListNode* node = reinterpret_cast<DebugInfoListNode*>(data);
+ // We need to clear all breakpoints associated with the function to restore
+ // original code and avoid patching the code twice later because
+ // the function will live in the heap until next gc, and can be found by
+ // Runtime::FindSharedFunctionInfoInScript.
+ BreakLocationIterator it(node->debug_info(), ALL_BREAK_LOCATIONS);
+ it.ClearAllDebugBreak();
RemoveDebugInfo(node->debug_info());
#ifdef DEBUG
node = Debug::debug_info_list_;
@@ -854,7 +861,7 @@ Object* Debug::Break(Arguments args) {
HandleScope scope;
ASSERT(args.length() == 0);
- thread_local_.frames_are_dropped_ = false;
+ thread_local_.frame_drop_mode_ = FRAMES_UNTOUCHED;
// Get the top-most JavaScript frame.
JavaScriptFrameIterator it;
@@ -932,12 +939,22 @@ Object* Debug::Break(Arguments args) {
PrepareStep(step_action, step_count);
}
- if (thread_local_.frames_are_dropped_) {
- // We must have been calling IC stub. Do not return there anymore.
+ if (thread_local_.frame_drop_mode_ == FRAMES_UNTOUCHED) {
+ SetAfterBreakTarget(frame);
+ } else if (thread_local_.frame_drop_mode_ == FRAME_DROPPED_IN_IC_CALL) {
+ // We must have been calling IC stub. Do not go there anymore.
Code* plain_return = Builtins::builtin(Builtins::PlainReturn_LiveEdit);
thread_local_.after_break_target_ = plain_return->entry();
+ } else if (thread_local_.frame_drop_mode_ ==
+ FRAME_DROPPED_IN_DEBUG_SLOT_CALL) {
+ // Debug break slot stub does not return normally, instead it manually
+ // cleans the stack and jumps. We should patch the jump address.
+ Code* plain_return = Builtins::builtin(Builtins::FrameDropper_LiveEdit);
+ thread_local_.after_break_target_ = plain_return->entry();
+ } else if (thread_local_.frame_drop_mode_ == FRAME_DROPPED_IN_DIRECT_CALL) {
+ // Nothing to do, after_break_target is not used here.
} else {
- SetAfterBreakTarget(frame);
+ UNREACHABLE();
}
return Heap::undefined_value();
@@ -1749,8 +1766,9 @@ bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) {
}
-void Debug::FramesHaveBeenDropped(StackFrame::Id new_break_frame_id) {
- thread_local_.frames_are_dropped_ = true;
+void Debug::FramesHaveBeenDropped(StackFrame::Id new_break_frame_id,
+ FrameDropMode mode) {
+ thread_local_.frame_drop_mode_ = mode;
thread_local_.break_frame_id_ = new_break_frame_id;
}
diff --git a/deps/v8/src/debug.h b/deps/v8/src/debug.h
index 6019294f22..fb9269272f 100644
--- a/deps/v8/src/debug.h
+++ b/deps/v8/src/debug.h
@@ -400,7 +400,22 @@ class Debug {
// Called from stub-cache.cc.
static void GenerateCallICDebugBreak(MacroAssembler* masm);
- static void FramesHaveBeenDropped(StackFrame::Id new_break_frame_id);
+ // Describes how exactly a frame has been dropped from stack.
+ enum FrameDropMode {
+ // No frame has been dropped.
+ FRAMES_UNTOUCHED,
+ // The top JS frame had been calling IC stub. IC stub mustn't be called now.
+ FRAME_DROPPED_IN_IC_CALL,
+ // The top JS frame had been calling debug break slot stub. Patch the
+ // address this stub jumps to in the end.
+ FRAME_DROPPED_IN_DEBUG_SLOT_CALL,
+ // The top JS frame had been calling some C++ function. The return address
+ // gets patched automatically.
+ FRAME_DROPPED_IN_DIRECT_CALL
+ };
+
+ static void FramesHaveBeenDropped(StackFrame::Id new_break_frame_id,
+ FrameDropMode mode);
static void SetUpFrameDropperFrame(StackFrame* bottom_js_frame,
Handle<Code> code);
@@ -471,8 +486,9 @@ class Debug {
// Storage location for jump when exiting debug break calls.
Address after_break_target_;
- // Indicates that LiveEdit has patched the stack.
- bool frames_are_dropped_;
+ // Stores the way how LiveEdit has patched the stack. It is used when
+ // debugger returns control back to user script.
+ FrameDropMode frame_drop_mode_;
// Top debugger entry.
EnterDebugger* debugger_entry_;
diff --git a/deps/v8/src/factory.cc b/deps/v8/src/factory.cc
index f6b93b0773..39e881ac3d 100644
--- a/deps/v8/src/factory.cc
+++ b/deps/v8/src/factory.cc
@@ -96,6 +96,12 @@ Handle<String> Factory::NewStringFromTwoByte(Vector<const uc16> string,
}
+Handle<String> Factory::NewRawAsciiString(int length,
+ PretenureFlag pretenure) {
+ CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(length, pretenure), String);
+}
+
+
Handle<String> Factory::NewRawTwoByteString(int length,
PretenureFlag pretenure) {
CALL_HEAP_FUNCTION(Heap::AllocateRawTwoByteString(length, pretenure), String);
diff --git a/deps/v8/src/factory.h b/deps/v8/src/factory.h
index b0a0571af0..56deda5ab5 100644
--- a/deps/v8/src/factory.h
+++ b/deps/v8/src/factory.h
@@ -95,12 +95,16 @@ class Factory : public AllStatic {
Vector<const char> str,
PretenureFlag pretenure = NOT_TENURED);
- static Handle<String> NewStringFromTwoByte(Vector<const uc16> str,
+ static Handle<String> NewStringFromTwoByte(
+ Vector<const uc16> str,
PretenureFlag pretenure = NOT_TENURED);
- // Allocates and partially initializes a TwoByte String. The characters of
- // the string are uninitialized. Currently used in regexp code only, where
- // they are pretenured.
+ // Allocates and partially initializes an ASCII or TwoByte String. The
+ // characters of the string are uninitialized. Currently used in regexp code
+ // only, where they are pretenured.
+ static Handle<String> NewRawAsciiString(
+ int length,
+ PretenureFlag pretenure = NOT_TENURED);
static Handle<String> NewRawTwoByteString(
int length,
PretenureFlag pretenure = NOT_TENURED);
diff --git a/deps/v8/src/frames.cc b/deps/v8/src/frames.cc
index 9cf83c91cf..67a20d3cb8 100644
--- a/deps/v8/src/frames.cc
+++ b/deps/v8/src/frames.cc
@@ -542,7 +542,7 @@ void JavaScriptFrame::Print(StringStream* accumulator,
Address pc = this->pc();
if (code != NULL && code->kind() == Code::FUNCTION &&
- pc >= code->instruction_start() && pc < code->relocation_start()) {
+ pc >= code->instruction_start() && pc < code->instruction_end()) {
int source_pos = code->SourcePosition(pc);
int line = GetScriptLineNumberSafe(script, source_pos) + 1;
accumulator->Add(":%d", line);
diff --git a/deps/v8/src/globals.h b/deps/v8/src/globals.h
index 6cf2626171..aea88586fb 100644
--- a/deps/v8/src/globals.h
+++ b/deps/v8/src/globals.h
@@ -463,6 +463,12 @@ enum CallFunctionFlags {
};
+enum InlineCacheHolderFlag {
+ OWN_MAP, // For fast properties objects.
+ PROTOTYPE_MAP // For slow properties objects (except GlobalObjects).
+};
+
+
// Type of properties.
// Order of properties is significant.
// Must fit in the BitField PropertyDetails::TypeField.
diff --git a/deps/v8/src/heap.cc b/deps/v8/src/heap.cc
index 6ae46f2a6e..1b625897d1 100644
--- a/deps/v8/src/heap.cc
+++ b/deps/v8/src/heap.cc
@@ -2351,8 +2351,13 @@ Object* Heap::CreateCode(const CodeDesc& desc,
ZoneScopeInfo* sinfo,
Code::Flags flags,
Handle<Object> self_reference) {
+ // Allocate ByteArray before the Code object, so that we do not risk
+ // leaving uninitialized Code object (and breaking the heap).
+ Object* reloc_info = AllocateByteArray(desc.reloc_size, TENURED);
+ if (reloc_info->IsFailure()) return reloc_info;
+
// Compute size
- int body_size = RoundUp(desc.instr_size + desc.reloc_size, kObjectAlignment);
+ int body_size = RoundUp(desc.instr_size, kObjectAlignment);
int sinfo_size = 0;
if (sinfo != NULL) sinfo_size = sinfo->Serialize(NULL);
int obj_size = Code::SizeFor(body_size, sinfo_size);
@@ -2371,7 +2376,7 @@ Object* Heap::CreateCode(const CodeDesc& desc,
Code* code = Code::cast(result);
ASSERT(!CodeRange::exists() || CodeRange::contains(code->address()));
code->set_instruction_size(desc.instr_size);
- code->set_relocation_size(desc.reloc_size);
+ code->set_relocation_info(ByteArray::cast(reloc_info));
code->set_sinfo_size(sinfo_size);
code->set_flags(flags);
// Allow self references to created code object by patching the handle to
@@ -2419,8 +2424,12 @@ Object* Heap::CopyCode(Code* code) {
Object* Heap::CopyCode(Code* code, Vector<byte> reloc_info) {
- int new_body_size = RoundUp(code->instruction_size() + reloc_info.length(),
- kObjectAlignment);
+ // Allocate ByteArray before the Code object, so that we do not risk
+ // leaving uninitialized Code object (and breaking the heap).
+ Object* reloc_info_array = AllocateByteArray(reloc_info.length(), TENURED);
+ if (reloc_info_array->IsFailure()) return reloc_info_array;
+
+ int new_body_size = RoundUp(code->instruction_size(), kObjectAlignment);
int sinfo_size = code->sinfo_size();
@@ -2429,7 +2438,7 @@ Object* Heap::CopyCode(Code* code, Vector<byte> reloc_info) {
Address old_addr = code->address();
size_t relocation_offset =
- static_cast<size_t>(code->relocation_start() - old_addr);
+ static_cast<size_t>(code->instruction_end() - old_addr);
Object* result;
if (new_obj_size > MaxObjectSizeInPagedSpace()) {
@@ -2446,14 +2455,11 @@ Object* Heap::CopyCode(Code* code, Vector<byte> reloc_info) {
// Copy header and instructions.
memcpy(new_addr, old_addr, relocation_offset);
- // Copy patched rinfo.
- memcpy(new_addr + relocation_offset,
- reloc_info.start(),
- reloc_info.length());
-
Code* new_code = Code::cast(result);
- new_code->set_relocation_size(reloc_info.length());
+ new_code->set_relocation_info(ByteArray::cast(reloc_info_array));
+ // Copy patched rinfo.
+ memcpy(new_code->relocation_start(), reloc_info.start(), reloc_info.length());
// Copy sinfo.
memcpy(new_code->sinfo_start(), code->sinfo_start(), code->sinfo_size());
@@ -2866,6 +2872,8 @@ Object* Heap::AllocateStringFromAscii(Vector<const char> string,
Object* Heap::AllocateStringFromUtf8(Vector<const char> string,
PretenureFlag pretenure) {
+ // V8 only supports characters in the Basic Multilingual Plane.
+ const uc32 kMaxSupportedChar = 0xFFFF;
// Count the number of characters in the UTF-8 string and check if
// it is an ASCII string.
Access<Scanner::Utf8Decoder> decoder(Scanner::utf8_decoder());
@@ -2890,6 +2898,7 @@ Object* Heap::AllocateStringFromUtf8(Vector<const char> string,
decoder->Reset(string.start(), string.length());
for (int i = 0; i < chars; i++) {
uc32 r = decoder->GetNext();
+ if (r > kMaxSupportedChar) { r = unibrow::Utf8::kBadChar; }
string_result->Set(i, r);
}
return result;
diff --git a/deps/v8/src/ia32/codegen-ia32.cc b/deps/v8/src/ia32/codegen-ia32.cc
index 1a847c1681..0f72074711 100644
--- a/deps/v8/src/ia32/codegen-ia32.cc
+++ b/deps/v8/src/ia32/codegen-ia32.cc
@@ -7583,9 +7583,12 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
frame_->Push(&value);
} else {
Load(node->expression());
- bool overwrite =
+ bool can_overwrite =
(node->expression()->AsBinaryOperation() != NULL &&
node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
+ UnaryOverwriteMode overwrite =
+ can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
+ bool no_negative_zero = node->expression()->no_negative_zero();
switch (op) {
case Token::NOT:
case Token::DELETE:
@@ -7594,7 +7597,10 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
break;
case Token::SUB: {
- GenericUnaryOpStub stub(Token::SUB, overwrite);
+ GenericUnaryOpStub stub(
+ Token::SUB,
+ overwrite,
+ no_negative_zero ? kIgnoreNegativeZero : kStrictNegativeZero);
Result operand = frame_->Pop();
Result answer = frame_->CallStub(&stub, &operand);
answer.set_type_info(TypeInfo::Number());
@@ -9860,6 +9866,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
// the four basic operations. The stub stays in the DEFAULT state
// forever for all other operations (also if smi code is skipped).
GenerateTypeTransition(masm);
+ break;
}
Label not_floats;
@@ -10207,51 +10214,28 @@ void GenericBinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
- Label get_result;
-
- // Keep a copy of operands on the stack and make sure they are also in
- // edx, eax.
+ // Ensure the operands are on the stack.
if (HasArgsInRegisters()) {
GenerateRegisterArgsPush(masm);
- } else {
- GenerateLoadArguments(masm);
}
- // Internal frame is necessary to handle exceptions properly.
- __ EnterInternalFrame();
-
- // Push arguments on stack if the stub expects them there.
- if (!HasArgsInRegisters()) {
- __ push(edx);
- __ push(eax);
- }
- // Call the stub proper to get the result in eax.
- __ call(&get_result);
- __ LeaveInternalFrame();
+ __ pop(ecx); // Save return address.
- __ pop(ecx); // Return address.
// Left and right arguments are now on top.
- // Push the operation result. The tail call to BinaryOp_Patch will
- // return it to the original caller.
- __ push(eax);
// Push this stub's key. Although the operation and the type info are
// encoded into the key, the encoding is opaque, so push them too.
__ push(Immediate(Smi::FromInt(MinorKey())));
__ push(Immediate(Smi::FromInt(op_)));
__ push(Immediate(Smi::FromInt(runtime_operands_type_)));
- __ push(ecx); // Return address.
+ __ push(ecx); // Push return address.
- // Patch the caller to an appropriate specialized stub
- // and return the operation result.
+ // Patch the caller to an appropriate specialized stub and return the
+ // operation result to the caller of the stub.
__ TailCallExternalReference(
ExternalReference(IC_Utility(IC::kBinaryOp_Patch)),
- 6,
+ 5,
1);
-
- // The entry point for the result calculation is assumed to be immediately
- // after this sequence.
- __ bind(&get_result);
}
@@ -10934,10 +10918,12 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
__ test(eax, Immediate(kSmiTagMask));
__ j(not_zero, &try_float, not_taken);
- // Go slow case if the value of the expression is zero
- // to make sure that we switch between 0 and -0.
- __ test(eax, Operand(eax));
- __ j(zero, &slow, not_taken);
+ if (negative_zero_ == kStrictNegativeZero) {
+ // Go slow case if the value of the expression is zero
+ // to make sure that we switch between 0 and -0.
+ __ test(eax, Operand(eax));
+ __ j(zero, &slow, not_taken);
+ }
// The value of the expression is a smi that is not zero. Try
// optimistic subtraction '0 - value'.
@@ -10945,11 +10931,7 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
__ mov(edx, Operand(eax));
__ Set(eax, Immediate(0));
__ sub(eax, Operand(edx));
- __ j(overflow, &undo, not_taken);
-
- // If result is a smi we are done.
- __ test(eax, Immediate(kSmiTagMask));
- __ j(zero, &done, taken);
+ __ j(no_overflow, &done, taken);
// Restore eax and go slow case.
__ bind(&undo);
@@ -10961,7 +10943,7 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
__ mov(edx, FieldOperand(eax, HeapObject::kMapOffset));
__ cmp(edx, Factory::heap_number_map());
__ j(not_equal, &slow);
- if (overwrite_) {
+ if (overwrite_ == UNARY_OVERWRITE) {
__ mov(edx, FieldOperand(eax, HeapNumber::kExponentOffset));
__ xor_(edx, HeapNumber::kSignMask); // Flip sign.
__ mov(FieldOperand(eax, HeapNumber::kExponentOffset), edx);
@@ -11002,7 +10984,7 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
// Try to store the result in a heap number.
__ bind(&try_float);
- if (!overwrite_) {
+ if (overwrite_ == UNARY_NO_OVERWRITE) {
// Allocate a fresh heap number, but don't overwrite eax until
// we're sure we can do it without going through the slow case
// that needs the value in eax.
@@ -11656,7 +11638,7 @@ static int NegativeComparisonResult(Condition cc) {
void CompareStub::Generate(MacroAssembler* masm) {
- Label call_builtin, done;
+ Label check_unequal_objects, done;
// NOTICE! This code is only reached after a smi-fast-case check, so
// it is certain that at least one operand isn't a smi.
@@ -11689,15 +11671,15 @@ void CompareStub::Generate(MacroAssembler* masm) {
Label heap_number;
__ cmp(FieldOperand(edx, HeapObject::kMapOffset),
Immediate(Factory::heap_number_map()));
- if (cc_ == equal) {
- __ j(equal, &heap_number);
- // Identical objects are equal for operators ==, !=, and ===.
- __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
- __ ret(0);
- } else {
- // Identical objects must call ToPrimitive for <, <=, >, and >=.
- __ j(not_equal, &not_identical);
+ __ j(equal, &heap_number);
+ if (cc_ != equal) {
+ // Call runtime on identical JSObjects. Otherwise return equal.
+ __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx);
+ __ j(above_equal, &not_identical);
}
+ __ Set(eax, Immediate(Smi::FromInt(EQUAL)));
+ __ ret(0);
+
__ bind(&heap_number);
// It is a heap number, so return non-equal if it's NaN and equal if
// it's not NaN.
@@ -11734,79 +11716,75 @@ void CompareStub::Generate(MacroAssembler* masm) {
__ bind(&not_identical);
}
- if (cc_ == equal) { // Both strict and non-strict.
+ // Strict equality can quickly decide whether objects are equal.
+ // Non-strict object equality is slower, so it is handled later in the stub.
+ if (cc_ == equal && strict_) {
Label slow; // Fallthrough label.
-
+ Label not_smis;
// If we're doing a strict equality comparison, we don't have to do
// type conversion, so we generate code to do fast comparison for objects
// and oddballs. Non-smi numbers and strings still go through the usual
// slow-case code.
- if (strict_) {
- // If either is a Smi (we know that not both are), then they can only
- // be equal if the other is a HeapNumber. If so, use the slow case.
- {
- Label not_smis;
- ASSERT_EQ(0, kSmiTag);
- ASSERT_EQ(0, Smi::FromInt(0));
- __ mov(ecx, Immediate(kSmiTagMask));
- __ and_(ecx, Operand(eax));
- __ test(ecx, Operand(edx));
- __ j(not_zero, &not_smis);
- // One operand is a smi.
-
- // Check whether the non-smi is a heap number.
- ASSERT_EQ(1, kSmiTagMask);
- // ecx still holds eax & kSmiTag, which is either zero or one.
- __ sub(Operand(ecx), Immediate(0x01));
- __ mov(ebx, edx);
- __ xor_(ebx, Operand(eax));
- __ and_(ebx, Operand(ecx)); // ebx holds either 0 or eax ^ edx.
- __ xor_(ebx, Operand(eax));
- // if eax was smi, ebx is now edx, else eax.
-
- // Check if the non-smi operand is a heap number.
- __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
- Immediate(Factory::heap_number_map()));
- // If heap number, handle it in the slow case.
- __ j(equal, &slow);
- // Return non-equal (ebx is not zero)
- __ mov(eax, ebx);
- __ ret(0);
-
- __ bind(&not_smis);
- }
-
- // If either operand is a JSObject or an oddball value, then they are not
- // equal since their pointers are different
- // There is no test for undetectability in strict equality.
-
- // Get the type of the first operand.
- // If the first object is a JS object, we have done pointer comparison.
- Label first_non_object;
- ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
- __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx);
- __ j(below, &first_non_object);
+ // If either is a Smi (we know that not both are), then they can only
+ // be equal if the other is a HeapNumber. If so, use the slow case.
+ ASSERT_EQ(0, kSmiTag);
+ ASSERT_EQ(0, Smi::FromInt(0));
+ __ mov(ecx, Immediate(kSmiTagMask));
+ __ and_(ecx, Operand(eax));
+ __ test(ecx, Operand(edx));
+ __ j(not_zero, &not_smis);
+ // One operand is a smi.
+
+ // Check whether the non-smi is a heap number.
+ ASSERT_EQ(1, kSmiTagMask);
+ // ecx still holds eax & kSmiTag, which is either zero or one.
+ __ sub(Operand(ecx), Immediate(0x01));
+ __ mov(ebx, edx);
+ __ xor_(ebx, Operand(eax));
+ __ and_(ebx, Operand(ecx)); // ebx holds either 0 or eax ^ edx.
+ __ xor_(ebx, Operand(eax));
+ // if eax was smi, ebx is now edx, else eax.
+
+ // Check if the non-smi operand is a heap number.
+ __ cmp(FieldOperand(ebx, HeapObject::kMapOffset),
+ Immediate(Factory::heap_number_map()));
+ // If heap number, handle it in the slow case.
+ __ j(equal, &slow);
+ // Return non-equal (ebx is not zero)
+ __ mov(eax, ebx);
+ __ ret(0);
- // Return non-zero (eax is not zero)
- Label return_not_equal;
- ASSERT(kHeapObjectTag != 0);
- __ bind(&return_not_equal);
- __ ret(0);
+ __ bind(&not_smis);
+ // If either operand is a JSObject or an oddball value, then they are not
+ // equal since their pointers are different
+ // There is no test for undetectability in strict equality.
+
+ // Get the type of the first operand.
+ // If the first object is a JS object, we have done pointer comparison.
+ Label first_non_object;
+ ASSERT(LAST_TYPE == JS_FUNCTION_TYPE);
+ __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx);
+ __ j(below, &first_non_object);
+
+ // Return non-zero (eax is not zero)
+ Label return_not_equal;
+ ASSERT(kHeapObjectTag != 0);
+ __ bind(&return_not_equal);
+ __ ret(0);
- __ bind(&first_non_object);
- // Check for oddballs: true, false, null, undefined.
- __ CmpInstanceType(ecx, ODDBALL_TYPE);
- __ j(equal, &return_not_equal);
+ __ bind(&first_non_object);
+ // Check for oddballs: true, false, null, undefined.
+ __ CmpInstanceType(ecx, ODDBALL_TYPE);
+ __ j(equal, &return_not_equal);
- __ CmpObjectType(edx, FIRST_JS_OBJECT_TYPE, ecx);
- __ j(above_equal, &return_not_equal);
+ __ CmpObjectType(edx, FIRST_JS_OBJECT_TYPE, ecx);
+ __ j(above_equal, &return_not_equal);
- // Check for oddballs: true, false, null, undefined.
- __ CmpInstanceType(ecx, ODDBALL_TYPE);
- __ j(equal, &return_not_equal);
+ // Check for oddballs: true, false, null, undefined.
+ __ CmpInstanceType(ecx, ODDBALL_TYPE);
+ __ j(equal, &return_not_equal);
- // Fall through to the general case.
- }
+ // Fall through to the general case.
__ bind(&slow);
}
@@ -11893,7 +11871,8 @@ void CompareStub::Generate(MacroAssembler* masm) {
__ bind(&check_for_strings);
- __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx, &call_builtin);
+ __ JumpIfNotBothSequentialAsciiStrings(edx, eax, ecx, ebx,
+ &check_unequal_objects);
// Inline comparison of ascii strings.
StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
@@ -11906,7 +11885,44 @@ void CompareStub::Generate(MacroAssembler* masm) {
__ Abort("Unexpected fall-through from string comparison");
#endif
- __ bind(&call_builtin);
+ __ bind(&check_unequal_objects);
+ if (cc_ == equal && !strict_) {
+ // Non-strict equality. Objects are unequal if
+ // they are both JSObjects and not undetectable,
+ // and their pointers are different.
+ Label not_both_objects;
+ Label return_unequal;
+ // At most one is a smi, so we can test for smi by adding the two.
+ // A smi plus a heap object has the low bit set, a heap object plus
+ // a heap object has the low bit clear.
+ ASSERT_EQ(0, kSmiTag);
+ ASSERT_EQ(1, kSmiTagMask);
+ __ lea(ecx, Operand(eax, edx, times_1, 0));
+ __ test(ecx, Immediate(kSmiTagMask));
+ __ j(not_zero, &not_both_objects);
+ __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx);
+ __ j(below, &not_both_objects);
+ __ CmpObjectType(edx, FIRST_JS_OBJECT_TYPE, ebx);
+ __ j(below, &not_both_objects);
+ // We do not bail out after this point. Both are JSObjects, and
+ // they are equal if and only if both are undetectable.
+ // The and of the undetectable flags is 1 if and only if they are equal.
+ __ test_b(FieldOperand(ecx, Map::kBitFieldOffset),
+ 1 << Map::kIsUndetectable);
+ __ j(zero, &return_unequal);
+ __ test_b(FieldOperand(ebx, Map::kBitFieldOffset),
+ 1 << Map::kIsUndetectable);
+ __ j(zero, &return_unequal);
+ // The objects are both undetectable, so they both compare as the value
+ // undefined, and are equal.
+ __ Set(eax, Immediate(EQUAL));
+ __ bind(&return_unequal);
+ // Return non-equal by returning the non-zero object pointer in eax,
+ // or return equal if we fell through to here.
+ __ ret(2 * kPointerSize); // rax, rdx were pushed
+ __ bind(&not_both_objects);
+ }
+
// must swap argument order
__ pop(ecx);
__ pop(edx);
diff --git a/deps/v8/src/ia32/full-codegen-ia32.cc b/deps/v8/src/ia32/full-codegen-ia32.cc
index 13173e2b92..2ca1105718 100644
--- a/deps/v8/src/ia32/full-codegen-ia32.cc
+++ b/deps/v8/src/ia32/full-codegen-ia32.cc
@@ -2813,9 +2813,11 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
case Token::SUB: {
Comment cmt(masm_, "[ UnaryOperation (SUB)");
- bool overwrite =
+ bool can_overwrite =
(expr->expression()->AsBinaryOperation() != NULL &&
expr->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
+ UnaryOverwriteMode overwrite =
+ can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
GenericUnaryOpStub stub(Token::SUB, overwrite);
// GenericUnaryOpStub expects the argument to be in the
// accumulator register eax.
@@ -2827,9 +2829,11 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
case Token::BIT_NOT: {
Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)");
- bool overwrite =
+ bool can_overwrite =
(expr->expression()->AsBinaryOperation() != NULL &&
expr->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
+ UnaryOverwriteMode overwrite =
+ can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
// GenericUnaryOpStub expects the argument to be in the
// accumulator register eax.
diff --git a/deps/v8/src/ia32/ic-ia32.cc b/deps/v8/src/ia32/ic-ia32.cc
index 62f878c048..062f0f2c4b 100644
--- a/deps/v8/src/ia32/ic-ia32.cc
+++ b/deps/v8/src/ia32/ic-ia32.cc
@@ -61,11 +61,11 @@ static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
// Generated code falls through if the receiver is a regular non-global
// JS object with slow properties and no interceptors.
-static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm,
- Register receiver,
- Register r0,
- Register r1,
- Label* miss) {
+static void GenerateStringDictionaryReceiverCheck(MacroAssembler* masm,
+ Register receiver,
+ Register r0,
+ Register r1,
+ Label* miss) {
// Register usage:
// receiver: holds the receiver on entry and is unchanged.
// r0: used to hold receiver instance type.
@@ -98,36 +98,17 @@ static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm,
}
-// Helper function used to load a property from a dictionary backing storage.
-// This function may return false negatives, so miss_label
-// must always call a backup property load that is complete.
-// This function is safe to call if name is not a symbol, and will jump to
-// the miss_label in that case.
-// The generated code assumes that the receiver has slow properties,
-// is not a global object and does not have interceptors.
-static void GenerateDictionaryLoad(MacroAssembler* masm,
- Label* miss_label,
- Register elements,
- Register name,
- Register r0,
- Register r1,
- Register result) {
- // Register use:
- //
- // elements - holds the property dictionary on entry and is unchanged.
- //
- // name - holds the name of the property on entry and is unchanged.
- //
- // Scratch registers:
- //
- // r0 - used for the index into the property dictionary
- //
- // r1 - used to hold the capacity of the property dictionary.
- //
- // result - holds the result on exit.
-
- Label done;
-
+// Probe the string dictionary in the |elements| register. Jump to the
+// |done| label if a property with the given name is found leaving the
+// index into the dictionary in |r0|. Jump to the |miss| label
+// otherwise.
+static void GenerateStringDictionaryProbes(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1) {
// Compute the capacity mask.
const int kCapacityOffset =
StringDictionary::kHeaderSize +
@@ -160,14 +141,61 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
__ cmp(name, Operand(elements, r0, times_4,
kElementsStartOffset - kHeapObjectTag));
if (i != kProbes - 1) {
- __ j(equal, &done, taken);
+ __ j(equal, done, taken);
} else {
- __ j(not_equal, miss_label, not_taken);
+ __ j(not_equal, miss, not_taken);
}
}
+}
+
+
+
+// Helper function used to load a property from a dictionary backing
+// storage. This function may fail to load a property even though it is
+// in the dictionary, so code at miss_label must always call a backup
+// property load that is complete. This function is safe to call if
+// name is not a symbol, and will jump to the miss_label in that
+// case. The generated code assumes that the receiver has slow
+// properties, is not a global object and does not have interceptors.
+static void GenerateDictionaryLoad(MacroAssembler* masm,
+ Label* miss_label,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1,
+ Register result) {
+ // Register use:
+ //
+ // elements - holds the property dictionary on entry and is unchanged.
+ //
+ // name - holds the name of the property on entry and is unchanged.
+ //
+ // Scratch registers:
+ //
+ // r0 - used for the index into the property dictionary
+ //
+ // r1 - used to hold the capacity of the property dictionary.
+ //
+ // result - holds the result on exit.
- // Check that the value is a normal property.
+ Label done;
+
+ // Probe the dictionary.
+ GenerateStringDictionaryProbes(masm,
+ miss_label,
+ &done,
+ elements,
+ name,
+ r0,
+ r1);
+
+ // If probing finds an entry in the dictionary, r0 contains the
+ // index into the dictionary. Check that the value is a normal
+ // property.
__ bind(&done);
+ const int kElementsStartOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kElementsStartIndex * kPointerSize;
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
__ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag),
Immediate(PropertyDetails::TypeField::mask() << kSmiTagSize));
@@ -179,6 +207,69 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
}
+// Helper function used to store a property to a dictionary backing
+// storage. This function may fail to store a property eventhough it
+// is in the dictionary, so code at miss_label must always call a
+// backup property store that is complete. This function is safe to
+// call if name is not a symbol, and will jump to the miss_label in
+// that case. The generated code assumes that the receiver has slow
+// properties, is not a global object and does not have interceptors.
+static void GenerateDictionaryStore(MacroAssembler* masm,
+ Label* miss_label,
+ Register elements,
+ Register name,
+ Register value,
+ Register r0,
+ Register r1) {
+ // Register use:
+ //
+ // elements - holds the property dictionary on entry and is clobbered.
+ //
+ // name - holds the name of the property on entry and is unchanged.
+ //
+ // value - holds the value to store and is unchanged.
+ //
+ // r0 - used for index into the property dictionary and is clobbered.
+ //
+ // r1 - used to hold the capacity of the property dictionary and is clobbered.
+ Label done;
+
+
+ // Probe the dictionary.
+ GenerateStringDictionaryProbes(masm,
+ miss_label,
+ &done,
+ elements,
+ name,
+ r0,
+ r1);
+
+ // If probing finds an entry in the dictionary, r0 contains the
+ // index into the dictionary. Check that the value is a normal
+ // property that is not read only.
+ __ bind(&done);
+ const int kElementsStartOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kElementsStartIndex * kPointerSize;
+ const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
+ const int kTypeAndReadOnlyMask
+ = (PropertyDetails::TypeField::mask() |
+ PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize;
+ __ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag),
+ Immediate(kTypeAndReadOnlyMask));
+ __ j(not_zero, miss_label, not_taken);
+
+ // Store the value at the masked, scaled index.
+ const int kValueOffset = kElementsStartOffset + kPointerSize;
+ __ lea(r0, Operand(elements, r0, times_4, kValueOffset - kHeapObjectTag));
+ __ mov(Operand(r0, 0), value);
+
+ // Update write barrier. Make sure not to clobber the value.
+ __ mov(r1, value);
+ __ RecordWrite(elements, r0, r1);
+}
+
+
static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
Label* miss,
Register elements,
@@ -1238,7 +1329,7 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) {
// Get the receiver of the function from the stack; 1 ~ return address.
__ mov(edx, Operand(esp, (argc + 1) * kPointerSize));
- GenerateDictionaryLoadReceiverCheck(masm, edx, eax, ebx, &miss);
+ GenerateStringDictionaryReceiverCheck(masm, edx, eax, ebx, &miss);
// eax: elements
// Search the dictionary placing the result in edi.
@@ -1517,7 +1608,7 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
// -----------------------------------
Label miss;
- GenerateDictionaryLoadReceiverCheck(masm, eax, edx, ebx, &miss);
+ GenerateStringDictionaryReceiverCheck(masm, eax, edx, ebx, &miss);
// edx: elements
// Search the dictionary placing the result in eax.
@@ -1775,6 +1866,36 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
}
+void StoreIC::GenerateNormal(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- eax : value
+ // -- ecx : name
+ // -- edx : receiver
+ // -- esp[0] : return address
+ // -----------------------------------
+
+ Label miss, restore_miss;
+
+ GenerateStringDictionaryReceiverCheck(masm, edx, ebx, edi, &miss);
+
+ // A lot of registers are needed for storing to slow case
+ // objects. Push and restore receiver but rely on
+ // GenerateDictionaryStore preserving the value and name.
+ __ push(edx);
+ GenerateDictionaryStore(masm, &restore_miss, ebx, ecx, eax, edx, edi);
+ __ Drop(1);
+ __ IncrementCounter(&Counters::store_normal_hit, 1);
+ __ ret(0);
+
+ __ bind(&restore_miss);
+ __ pop(edx);
+
+ __ bind(&miss);
+ __ IncrementCounter(&Counters::store_normal_miss, 1);
+ GenerateMiss(masm);
+}
+
+
// Defined in ic.cc.
Object* KeyedStoreIC_Miss(Arguments args);
diff --git a/deps/v8/src/ia32/macro-assembler-ia32.cc b/deps/v8/src/ia32/macro-assembler-ia32.cc
index b83f9bc75b..b3f7c21273 100644
--- a/deps/v8/src/ia32/macro-assembler-ia32.cc
+++ b/deps/v8/src/ia32/macro-assembler-ia32.cc
@@ -98,11 +98,6 @@ void MacroAssembler::InNewSpace(Register object,
}
-// For page containing |object| mark region covering [object+offset] dirty.
-// object is the object being stored into, value is the object being stored.
-// If offset is zero, then the scratch register contains the array index into
-// the elements array represented as a Smi.
-// All registers are clobbered by the operation.
void MacroAssembler::RecordWrite(Register object, int offset,
Register value, Register scratch) {
// The compiled code assumes that record write doesn't change the
@@ -153,6 +148,39 @@ void MacroAssembler::RecordWrite(Register object, int offset,
}
+void MacroAssembler::RecordWrite(Register object,
+ Register address,
+ Register value) {
+ // The compiled code assumes that record write doesn't change the
+ // context register, so we check that none of the clobbered
+ // registers are esi.
+ ASSERT(!object.is(esi) && !value.is(esi) && !address.is(esi));
+
+ // First, check if a write barrier is even needed. The tests below
+ // catch stores of Smis and stores into young gen.
+ Label done;
+
+ // Skip barrier if writing a smi.
+ ASSERT_EQ(0, kSmiTag);
+ test(value, Immediate(kSmiTagMask));
+ j(zero, &done);
+
+ InNewSpace(object, value, equal, &done);
+
+ RecordWriteHelper(object, address, value);
+
+ bind(&done);
+
+ // Clobber all input registers when running with the debug-code flag
+ // turned on to provoke errors.
+ if (FLAG_debug_code) {
+ mov(object, Immediate(BitCast<int32_t>(kZapValue)));
+ mov(address, Immediate(BitCast<int32_t>(kZapValue)));
+ mov(value, Immediate(BitCast<int32_t>(kZapValue)));
+ }
+}
+
+
void MacroAssembler::StackLimitCheck(Label* on_stack_overflow) {
cmp(esp,
Operand::StaticVariable(ExternalReference::address_of_stack_limit()));
@@ -514,97 +542,6 @@ 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.
- ASSERT(!scratch.is(object_reg) && !scratch.is(holder_reg));
-
- // Keep track of the current object in register reg.
- Register reg = object_reg;
- 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.
- while (object != holder) {
- depth++;
-
- // Only global objects and objects that do not require access
- // checks are allowed in stubs.
- ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
-
- JSObject* prototype = JSObject::cast(object->GetPrototype());
- if (Heap::InNewSpace(prototype)) {
- // Get the map of the current object.
- mov(scratch, FieldOperand(reg, HeapObject::kMapOffset));
- cmp(Operand(scratch), Immediate(Handle<Map>(object->map())));
- // Branch on the result of the map check.
- j(not_equal, miss, not_taken);
- // Check access rights to the global object. This has to happen
- // after the map check so that we know that the object is
- // actually a global object.
- if (object->IsJSGlobalProxy()) {
- CheckAccessGlobalProxy(reg, scratch, miss);
-
- // Restore scratch register to be the map of the object.
- // We load the prototype from the map in the scratch register.
- mov(scratch, FieldOperand(reg, HeapObject::kMapOffset));
- }
- // The prototype is in new space; we cannot store a reference
- // 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),
- Immediate(Handle<Map>(object->map())));
- // Branch on the result of the map check.
- j(not_equal, miss, not_taken);
- // Check access rights to the global object. This has to happen
- // after the map check so that we know that the object is
- // actually a global object.
- if (object->IsJSGlobalProxy()) {
- CheckAccessGlobalProxy(reg, scratch, miss);
- }
- // The prototype is in old space; load it directly.
- reg = holder_reg; // from now the object is in holder_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;
- }
-
- // Check the holder map.
- cmp(FieldOperand(reg, HeapObject::kMapOffset),
- Immediate(Handle<Map>(holder->map())));
- j(not_equal, miss, not_taken);
-
- // Log the check depth.
- LOG(IntEvent("check-maps-depth", depth + 1));
-
- // Perform security check for access to the global object and return
- // the holder register.
- ASSERT(object == holder);
- ASSERT(object->IsJSGlobalProxy() || !object->IsAccessCheckNeeded());
- if (object->IsJSGlobalProxy()) {
- CheckAccessGlobalProxy(reg, scratch, miss);
- }
- return reg;
-}
-
-
void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg,
Register scratch,
Label* miss) {
diff --git a/deps/v8/src/ia32/macro-assembler-ia32.h b/deps/v8/src/ia32/macro-assembler-ia32.h
index 2018721d6b..02cfd4d9f3 100644
--- a/deps/v8/src/ia32/macro-assembler-ia32.h
+++ b/deps/v8/src/ia32/macro-assembler-ia32.h
@@ -73,16 +73,27 @@ class MacroAssembler: public Assembler {
Condition cc, // equal for new space, not_equal otherwise.
Label* branch);
- // For page containing |object| mark region covering [object+offset] dirty.
- // object is the object being stored into, value is the object being stored.
- // If offset is zero, then the scratch register contains the array index into
- // the elements array represented as a Smi.
- // All registers are clobbered by the operation.
+ // For page containing |object| mark region covering [object+offset]
+ // dirty. |object| is the object being stored into, |value| is the
+ // object being stored. If offset is zero, then the scratch register
+ // contains the array index into the elements array represented as a
+ // Smi. All registers are clobbered by the operation. RecordWrite
+ // filters out smis so it does not update the write barrier if the
+ // value is a smi.
void RecordWrite(Register object,
int offset,
Register value,
Register scratch);
+ // For page containing |object| mark region covering |address|
+ // dirty. |object| is the object being stored into, |value| is the
+ // object being stored. All registers are clobbered by the
+ // operation. RecordWrite filters out smis so it does not update the
+ // write barrier if the value is a smi.
+ void RecordWrite(Register object,
+ Register address,
+ Register value);
+
#ifdef ENABLE_DEBUGGER_SUPPORT
// ---------------------------------------------------------------------------
// Debugger Support
@@ -233,24 +244,6 @@ class MacroAssembler: public Assembler {
// ---------------------------------------------------------------------------
// Inline caching support
- // Generates code that verifies that the maps of objects in the
- // prototype chain of object hasn't changed since the code was
- // generated and branches to the miss label if any map has. If
- // necessary the function also generates code for security check
- // in case of global object holders. The scratch and holder
- // registers are always clobbered, but the object register is only
- // 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,
- 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
// is left untouched, but the scratch register is clobbered.
diff --git a/deps/v8/src/ia32/stub-cache-ia32.cc b/deps/v8/src/ia32/stub-cache-ia32.cc
index bab0435f38..26361d10e4 100644
--- a/deps/v8/src/ia32/stub-cache-ia32.cc
+++ b/deps/v8/src/ia32/stub-cache-ia32.cc
@@ -101,6 +101,110 @@ static void ProbeTable(MacroAssembler* masm,
}
+// Helper function used to check that the dictionary doesn't contain
+// the property. This function may return false negatives, so miss_label
+// must always call a backup property check that is complete.
+// This function is safe to call if the receiver has fast properties.
+// Name must be a symbol and receiver must be a heap object.
+static void GenerateDictionaryNegativeLookup(MacroAssembler* masm,
+ Label* miss_label,
+ Register receiver,
+ String* name,
+ Register r0,
+ Register extra) {
+ ASSERT(name->IsSymbol());
+ __ IncrementCounter(&Counters::negative_lookups, 1);
+ __ IncrementCounter(&Counters::negative_lookups_miss, 1);
+
+ Label done;
+ __ mov(r0, FieldOperand(receiver, HeapObject::kMapOffset));
+
+ const int kInterceptorOrAccessCheckNeededMask =
+ (1 << Map::kHasNamedInterceptor) | (1 << Map::kIsAccessCheckNeeded);
+ // Bail out if the receiver has a named interceptor or requires access checks.
+ __ test(FieldOperand(r0, Map::kBitFieldOffset),
+ Immediate(kInterceptorOrAccessCheckNeededMask));
+ __ j(not_zero, miss_label, not_taken);
+
+ __ CmpInstanceType(r0, FIRST_JS_OBJECT_TYPE);
+ __ j(below, miss_label, not_taken);
+
+ // Load properties array.
+ Register properties = r0;
+ __ mov(properties, FieldOperand(receiver, JSObject::kPropertiesOffset));
+
+ // Check that the properties array is a dictionary.
+ __ cmp(FieldOperand(properties, HeapObject::kMapOffset),
+ Immediate(Factory::hash_table_map()));
+ __ j(not_equal, miss_label);
+
+ // Compute the capacity mask.
+ const int kCapacityOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kCapacityIndex * kPointerSize;
+
+ // Generate an unrolled loop that performs a few probes before
+ // giving up.
+ static const int kProbes = 4;
+ const int kElementsStartOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kElementsStartIndex * kPointerSize;
+
+ // If names of slots in range from 1 to kProbes - 1 for the hash value are
+ // not equal to the name and kProbes-th slot is not used (its name is the
+ // undefined value), it guarantees the hash table doesn't contain the
+ // property. It's true even if some slots represent deleted properties
+ // (their names are the null value).
+ for (int i = 0; i < kProbes; i++) {
+ // r0 points to properties hash.
+ // Compute the masked index: (hash + i + i * i) & mask.
+ if (extra.is(no_reg)) {
+ __ push(receiver);
+ }
+ Register index = extra.is(no_reg) ? receiver : extra;
+ // Capacity is smi 2^n.
+ __ mov(index, FieldOperand(properties, kCapacityOffset));
+ __ dec(index);
+ __ and_(Operand(index),
+ Immediate(Smi::FromInt(name->Hash() +
+ StringDictionary::GetProbeOffset(i))));
+
+ // Scale the index by multiplying by the entry size.
+ ASSERT(StringDictionary::kEntrySize == 3);
+ __ lea(index, Operand(index, index, times_2, 0)); // index *= 3.
+
+ Register entity_name = extra.is(no_reg) ? properties : extra;
+ // Having undefined at this place means the name is not contained.
+ ASSERT_EQ(kSmiTagSize, 1);
+ __ mov(entity_name, Operand(properties, index, times_half_pointer_size,
+ kElementsStartOffset - kHeapObjectTag));
+ __ cmp(entity_name, Factory::undefined_value());
+ if (extra.is(no_reg)) {
+ // 'receiver' shares a register with 'entity_name'.
+ __ pop(receiver);
+ }
+ if (i != kProbes - 1) {
+ __ j(equal, &done, taken);
+
+ // Stop if found the property.
+ __ cmp(entity_name, Handle<String>(name));
+ __ j(equal, miss_label, not_taken);
+
+ if (extra.is(no_reg)) {
+ // Restore the properties if their register was occupied by the name.
+ __ mov(properties, FieldOperand(receiver, JSObject::kPropertiesOffset));
+ }
+ } else {
+ // Give up probing if still not found the undefined value.
+ __ j(not_equal, miss_label, not_taken);
+ }
+ }
+
+ __ bind(&done);
+ __ DecrementCounter(&Counters::negative_lookups_miss, 1);
+}
+
+
void StubCache::GenerateProbe(MacroAssembler* masm,
Code::Flags flags,
Register receiver,
@@ -723,6 +827,33 @@ static Object* GenerateCheckPropertyCell(MacroAssembler* masm,
}
+// Calls GenerateCheckPropertyCell for each global object in the prototype chain
+// from object to (but not including) holder.
+static Object* GenerateCheckPropertyCells(MacroAssembler* masm,
+ JSObject* object,
+ JSObject* holder,
+ String* name,
+ Register scratch,
+ Label* miss) {
+ JSObject* current = object;
+ while (current != holder) {
+ if (current->IsGlobalObject()) {
+ Object* cell = GenerateCheckPropertyCell(masm,
+ GlobalObject::cast(current),
+ name,
+ scratch,
+ miss);
+ if (cell->IsFailure()) {
+ return cell;
+ }
+ }
+ ASSERT(current->IsJSObject());
+ current = JSObject::cast(current->GetPrototype());
+ }
+ return NULL;
+}
+
+
#undef __
#define __ ACCESS_MASM(masm())
@@ -733,33 +864,129 @@ 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,
- push_at_depth, miss);
+ int save_at_depth,
+ Label* miss,
+ Register extra) {
+ // Make sure there's no overlap between holder and object registers.
+ ASSERT(!scratch.is(object_reg) && !scratch.is(holder_reg));
+ ASSERT(!extra.is(object_reg) && !extra.is(holder_reg) && !extra.is(scratch));
+ // Keep track of the current object in register reg.
+ Register reg = object_reg;
+ JSObject* current = object;
+ int depth = 0;
+
+ if (save_at_depth == depth) {
+ __ mov(Operand(esp, kPointerSize), reg);
+ }
- // If we've skipped any global objects, it's not enough to verify
- // that their maps haven't changed. We also need to check that the
- // property cell for the property is still empty.
- while (object != holder) {
- if (object->IsGlobalObject()) {
- Object* cell = GenerateCheckPropertyCell(masm(),
- GlobalObject::cast(object),
- name,
- scratch,
- miss);
- if (cell->IsFailure()) {
- set_failure(Failure::cast(cell));
- return result;
+ // Traverse the prototype chain and check the maps in the prototype chain for
+ // fast and global objects or do negative lookup for normal objects.
+ while (current != holder) {
+ depth++;
+
+ // Only global objects and objects that do not require access
+ // checks are allowed in stubs.
+ ASSERT(current->IsJSGlobalProxy() || !current->IsAccessCheckNeeded());
+
+ ASSERT(current->GetPrototype()->IsJSObject());
+ JSObject* prototype = JSObject::cast(current->GetPrototype());
+ if (!current->HasFastProperties() &&
+ !current->IsJSGlobalObject() &&
+ !current->IsJSGlobalProxy()) {
+ if (!name->IsSymbol()) {
+ Object* lookup_result = Heap::LookupSymbol(name);
+ if (lookup_result->IsFailure()) {
+ set_failure(Failure::cast(lookup_result));
+ return reg;
+ } else {
+ name = String::cast(lookup_result);
+ }
+ }
+ ASSERT(current->property_dictionary()->FindEntry(name) ==
+ StringDictionary::kNotFound);
+
+ GenerateDictionaryNegativeLookup(masm(),
+ miss,
+ reg,
+ name,
+ scratch,
+ extra);
+ __ mov(scratch, FieldOperand(reg, HeapObject::kMapOffset));
+ reg = holder_reg; // from now the object is in holder_reg
+ __ mov(reg, FieldOperand(scratch, Map::kPrototypeOffset));
+ } else if (Heap::InNewSpace(prototype)) {
+ // Get the map of the current object.
+ __ mov(scratch, FieldOperand(reg, HeapObject::kMapOffset));
+ __ cmp(Operand(scratch), Immediate(Handle<Map>(current->map())));
+ // Branch on the result of the map check.
+ __ j(not_equal, miss, not_taken);
+ // Check access rights to the global object. This has to happen
+ // after the map check so that we know that the object is
+ // actually a global object.
+ if (current->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(reg, scratch, miss);
+
+ // Restore scratch register to be the map of the object.
+ // We load the prototype from the map in the scratch register.
+ __ mov(scratch, FieldOperand(reg, HeapObject::kMapOffset));
+ }
+ // The prototype is in new space; we cannot store a reference
+ // 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),
+ Immediate(Handle<Map>(current->map())));
+ // Branch on the result of the map check.
+ __ j(not_equal, miss, not_taken);
+ // Check access rights to the global object. This has to happen
+ // after the map check so that we know that the object is
+ // actually a global object.
+ if (current->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(reg, scratch, miss);
}
+ // The prototype is in old space; load it directly.
+ reg = holder_reg; // from now the object is in holder_reg
+ __ mov(reg, Handle<JSObject>(prototype));
}
- object = JSObject::cast(object->GetPrototype());
+
+ if (save_at_depth == depth) {
+ __ mov(Operand(esp, kPointerSize), reg);
+ }
+
+ // Go to the next object in the prototype chain.
+ current = prototype;
}
+ ASSERT(current == holder);
+
+ // Log the check depth.
+ LOG(IntEvent("check-maps-depth", depth + 1));
+
+ // Check the holder map.
+ __ cmp(FieldOperand(reg, HeapObject::kMapOffset),
+ Immediate(Handle<Map>(holder->map())));
+ __ j(not_equal, miss, not_taken);
+
+ // Perform security check for access to the global object.
+ ASSERT(holder->IsJSGlobalProxy() || !holder->IsAccessCheckNeeded());
+ if (holder->IsJSGlobalProxy()) {
+ __ CheckAccessGlobalProxy(reg, scratch, miss);
+ };
+
+ // If we've skipped any global objects, it's not enough to verify
+ // that their maps haven't changed. We also need to check that the
+ // property cell for the property is still empty.
+ Object* result = GenerateCheckPropertyCells(masm(),
+ object,
+ holder,
+ name,
+ scratch,
+ miss);
+ if (result->IsFailure()) set_failure(Failure::cast(result));
// Return the register containing the holder.
- return result;
+ return reg;
}
@@ -1083,7 +1310,8 @@ Object* CallStubCompiler::CompileCallField(JSObject* object,
__ j(zero, &miss, not_taken);
// Do the right check and compute the holder register.
- Register reg = CheckPrototypes(object, edx, holder, ebx, eax, name, &miss);
+ Register reg = CheckPrototypes(object, edx, holder, ebx, eax,
+ name, &miss, edi);
GenerateFastPropertyLoad(masm(), edi, reg, holder, index);
@@ -1145,7 +1373,7 @@ Object* CallStubCompiler::CompileArrayPushCall(Object* object,
CheckPrototypes(JSObject::cast(object), edx,
holder, ebx,
- eax, name, &miss);
+ eax, name, &miss, edi);
if (argc == 0) {
// Noop, return the length.
@@ -1291,7 +1519,7 @@ Object* CallStubCompiler::CompileArrayPopCall(Object* object,
__ j(zero, &miss);
CheckPrototypes(JSObject::cast(object), edx,
holder, ebx,
- eax, name, &miss);
+ eax, name, &miss, edi);
// Get the elements array of the object.
__ mov(ebx, FieldOperand(edx, JSArray::kElementsOffset));
@@ -1366,7 +1594,7 @@ Object* CallStubCompiler::CompileStringCharCodeAtCall(Object* object,
Context::STRING_FUNCTION_INDEX,
eax);
CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder,
- ebx, edx, name, &miss);
+ ebx, edx, name, &miss, edi);
Register receiver = ebx;
Register index = edi;
@@ -1431,7 +1659,7 @@ Object* CallStubCompiler::CompileStringCharAtCall(Object* object,
Context::STRING_FUNCTION_INDEX,
eax);
CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder,
- ebx, edx, name, &miss);
+ ebx, edx, name, &miss, edi);
Register receiver = eax;
Register index = edi;
@@ -1536,7 +1764,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
// Check that the maps haven't changed.
CheckPrototypes(JSObject::cast(object), edx, holder,
- ebx, eax, name, depth, &miss);
+ ebx, eax, name, depth, &miss, edi);
// Patch the receiver on the stack with the global proxy if
// necessary.
@@ -1559,7 +1787,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
GenerateDirectLoadGlobalFunctionPrototype(
masm(), Context::STRING_FUNCTION_INDEX, eax);
CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder,
- ebx, edx, name, &miss);
+ ebx, edx, name, &miss, edi);
}
break;
@@ -1579,7 +1807,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
GenerateDirectLoadGlobalFunctionPrototype(
masm(), Context::NUMBER_FUNCTION_INDEX, eax);
CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder,
- ebx, edx, name, &miss);
+ ebx, edx, name, &miss, edi);
}
break;
}
@@ -1600,7 +1828,7 @@ Object* CallStubCompiler::CompileCallConstant(Object* object,
GenerateDirectLoadGlobalFunctionPrototype(
masm(), Context::BOOLEAN_FUNCTION_INDEX, eax);
CheckPrototypes(JSObject::cast(object->GetPrototype()), eax, holder,
- ebx, edx, name, &miss);
+ ebx, edx, name, &miss, edi);
}
break;
}
@@ -1722,7 +1950,7 @@ Object* CallStubCompiler::CompileCallGlobal(JSObject* object,
}
// Check that the maps haven't changed.
- CheckPrototypes(object, edx, holder, ebx, eax, name, &miss);
+ CheckPrototypes(object, edx, holder, ebx, eax, name, &miss, edi);
// Get the value from the cell.
__ mov(edi, Immediate(Handle<JSGlobalPropertyCell>(cell)));
@@ -1993,6 +2221,8 @@ Object* LoadStubCompiler::CompileLoadNonexistent(String* name,
__ test(eax, Immediate(kSmiTagMask));
__ j(zero, &miss, not_taken);
+ ASSERT(last->IsGlobalObject() || last->HasFastProperties());
+
// Check the maps of the full prototype chain. Also check that
// global property cells up to (but not including) the last object
// in the prototype chain are empty.
@@ -2140,7 +2370,7 @@ Object* LoadStubCompiler::CompileLoadGlobal(JSObject* object,
}
// Check that the maps haven't changed.
- CheckPrototypes(object, eax, holder, ebx, edx, name, &miss);
+ CheckPrototypes(object, eax, holder, ebx, edx, name, &miss, edi);
// Get the value from the cell.
__ mov(ebx, Immediate(Handle<JSGlobalPropertyCell>(cell)));
diff --git a/deps/v8/src/ic-inl.h b/deps/v8/src/ic-inl.h
index 131f77bc19..70bbaf8c96 100644
--- a/deps/v8/src/ic-inl.h
+++ b/deps/v8/src/ic-inl.h
@@ -80,11 +80,38 @@ void IC::SetTargetAtAddress(Address address, Code* target) {
}
-Map* IC::GetCodeCacheMapForObject(Object* object) {
- if (object->IsJSObject()) return JSObject::cast(object)->map();
+InlineCacheHolderFlag IC::GetCodeCacheForObject(Object* object,
+ JSObject* holder) {
+ if (object->IsJSObject()) {
+ return GetCodeCacheForObject(JSObject::cast(object), holder);
+ }
// If the object is a value, we use the prototype map for the cache.
ASSERT(object->IsString() || object->IsNumber() || object->IsBoolean());
- return JSObject::cast(object->GetPrototype())->map();
+ return PROTOTYPE_MAP;
+}
+
+
+InlineCacheHolderFlag IC::GetCodeCacheForObject(JSObject* object,
+ JSObject* holder) {
+ // Fast-properties and global objects store stubs in their own maps.
+ // Slow properties objects use prototype's map (unless the property is its own
+ // when holder == object). It works because slow properties objects having
+ // the same prototype (or a prototype with the same map) and not having
+ // the property are interchangeable for such a stub.
+ if (holder != object &&
+ !object->HasFastProperties() &&
+ !object->IsJSGlobalProxy() &&
+ !object->IsJSGlobalObject()) {
+ return PROTOTYPE_MAP;
+ }
+ return OWN_MAP;
+}
+
+
+Map* IC::GetCodeCacheMap(Object* object, InlineCacheHolderFlag holder) {
+ Object* map_owner = (holder == OWN_MAP ? object : object->GetPrototype());
+ ASSERT(map_owner->IsJSObject());
+ return JSObject::cast(map_owner)->map();
}
diff --git a/deps/v8/src/ic.cc b/deps/v8/src/ic.cc
index 4b77d92507..12332f9fd3 100644
--- a/deps/v8/src/ic.cc
+++ b/deps/v8/src/ic.cc
@@ -134,13 +134,45 @@ Address IC::OriginalCodeAddress() {
}
#endif
+
+static bool HasNormalObjectsInPrototypeChain(LookupResult* lookup,
+ Object* receiver) {
+ Object* end = lookup->IsProperty() ? lookup->holder() : Heap::null_value();
+ for (Object* current = receiver;
+ current != end;
+ current = current->GetPrototype()) {
+ if (current->IsJSObject() &&
+ !JSObject::cast(current)->HasFastProperties() &&
+ !current->IsJSGlobalProxy() &&
+ !current->IsJSGlobalObject()) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) {
IC::State state = target->ic_state();
if (state != MONOMORPHIC) return state;
if (receiver->IsUndefined() || receiver->IsNull()) return state;
- Map* map = GetCodeCacheMapForObject(receiver);
+ InlineCacheHolderFlag cache_holder =
+ Code::ExtractCacheHolderFromFlags(target->flags());
+
+
+ if (cache_holder == OWN_MAP && !receiver->IsJSObject()) {
+ // The stub was generated for JSObject but called for non-JSObject.
+ // IC::GetCodeCacheMap is not applicable.
+ return MONOMORPHIC;
+ } else if (cache_holder == PROTOTYPE_MAP &&
+ receiver->GetPrototype()->IsNull()) {
+ // IC::GetCodeCacheMap is not applicable.
+ return MONOMORPHIC;
+ }
+ Map* map = IC::GetCodeCacheMap(receiver, cache_holder);
// Decide whether the inline cache failed because of changes to the
// receiver itself or changes to one of its prototypes.
@@ -487,12 +519,24 @@ Object* CallICBase::LoadFunction(State state,
void CallICBase::UpdateCaches(LookupResult* lookup,
- State state,
- Handle<Object> object,
- Handle<String> name) {
+ State state,
+ Handle<Object> object,
+ Handle<String> name) {
// Bail out if we didn't find a result.
if (!lookup->IsProperty() || !lookup->IsCacheable()) return;
+#ifndef V8_TARGET_ARCH_IA32
+ // Normal objects only implemented for IA32 by now.
+ if (HasNormalObjectsInPrototypeChain(lookup, *object)) return;
+#else
+ if (lookup->holder() != *object &&
+ HasNormalObjectsInPrototypeChain(lookup, object->GetPrototype())) {
+ // Suppress optimization for prototype chains with slow properties objects
+ // in the middle.
+ return;
+ }
+#endif
+
// Compute the number of arguments.
int argc = target()->arguments_count();
InLoopFlag in_loop = target()->ic_in_loop();
@@ -590,8 +634,13 @@ void CallICBase::UpdateCaches(LookupResult* lookup,
state == MONOMORPHIC_PROTOTYPE_FAILURE) {
set_target(Code::cast(code));
} else if (state == MEGAMORPHIC) {
+ // Cache code holding map should be consistent with
+ // GenerateMonomorphicCacheProbe. It is not the map which holds the stub.
+ Map* map = JSObject::cast(object->IsJSObject() ? *object :
+ object->GetPrototype())->map();
+
// Update the stub cache.
- StubCache::Set(*name, GetCodeCacheMapForObject(*object), Code::cast(code));
+ StubCache::Set(*name, map, Code::cast(code));
}
#ifdef DEBUG
@@ -795,6 +844,8 @@ void LoadIC::UpdateCaches(LookupResult* lookup,
if (!object->IsJSObject()) return;
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
+ if (HasNormalObjectsInPrototypeChain(lookup, *object)) return;
+
// Compute the code stub for this load.
Object* code = NULL;
if (state == UNINITIALIZED) {
@@ -836,7 +887,7 @@ void LoadIC::UpdateCaches(LookupResult* lookup,
// property must be found in the receiver for the stub to be
// applicable.
if (lookup->holder() != *receiver) return;
- code = StubCache::ComputeLoadNormal(*name, *receiver);
+ code = StubCache::ComputeLoadNormal();
}
break;
}
@@ -871,8 +922,12 @@ void LoadIC::UpdateCaches(LookupResult* lookup,
} else if (state == MONOMORPHIC) {
set_target(megamorphic_stub());
} else if (state == MEGAMORPHIC) {
- // Update the stub cache.
- StubCache::Set(*name, GetCodeCacheMapForObject(*object), Code::cast(code));
+ // Cache code holding map should be consistent with
+ // GenerateMonomorphicCacheProbe.
+ Map* map = JSObject::cast(object->IsJSObject() ? *object :
+ object->GetPrototype())->map();
+
+ StubCache::Set(*name, map, Code::cast(code));
}
#ifdef DEBUG
@@ -1018,6 +1073,8 @@ void KeyedLoadIC::UpdateCaches(LookupResult* lookup, State state,
if (!object->IsJSObject()) return;
Handle<JSObject> receiver = Handle<JSObject>::cast(object);
+ if (HasNormalObjectsInPrototypeChain(lookup, *object)) return;
+
// Compute the code stub for this load.
Object* code = NULL;
@@ -1198,16 +1255,18 @@ void StoreIC::UpdateCaches(LookupResult* lookup,
break;
}
case NORMAL: {
- if (!receiver->IsGlobalObject()) {
- return;
+ if (receiver->IsGlobalObject()) {
+ // The stub generated for the global object picks the value directly
+ // from the property cell. So the property must be directly on the
+ // global object.
+ Handle<GlobalObject> global = Handle<GlobalObject>::cast(receiver);
+ JSGlobalPropertyCell* cell =
+ JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup));
+ code = StubCache::ComputeStoreGlobal(*name, *global, cell);
+ } else {
+ if (lookup->holder() != *receiver) return;
+ code = StubCache::ComputeStoreNormal();
}
- // The stub generated for the global object picks the value directly
- // from the property cell. So the property must be directly on the
- // global object.
- Handle<GlobalObject> global = Handle<GlobalObject>::cast(receiver);
- JSGlobalPropertyCell* cell =
- JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup));
- code = StubCache::ComputeStoreGlobal(*name, *global, cell);
break;
}
case CALLBACKS: {
@@ -1580,16 +1639,15 @@ Handle<Code> GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info);
Object* BinaryOp_Patch(Arguments args) {
- ASSERT(args.length() == 6);
+ ASSERT(args.length() == 5);
Handle<Object> left = args.at<Object>(0);
Handle<Object> right = args.at<Object>(1);
- Handle<Object> result = args.at<Object>(2);
- int key = Smi::cast(args[3])->value();
+ int key = Smi::cast(args[2])->value();
+ Token::Value op = static_cast<Token::Value>(Smi::cast(args[3])->value());
#ifdef DEBUG
- Token::Value op = static_cast<Token::Value>(Smi::cast(args[4])->value());
BinaryOpIC::TypeInfo prev_type_info =
- static_cast<BinaryOpIC::TypeInfo>(Smi::cast(args[5])->value());
+ static_cast<BinaryOpIC::TypeInfo>(Smi::cast(args[4])->value());
#endif // DEBUG
{ HandleScope scope;
BinaryOpIC::TypeInfo type_info = BinaryOpIC::GetTypeInfo(*left, *right);
@@ -1608,6 +1666,61 @@ Object* BinaryOp_Patch(Arguments args) {
}
}
+ HandleScope scope;
+ Handle<JSBuiltinsObject> builtins = Top::builtins();
+
+ Object* builtin = NULL; // Initialization calms down the compiler.
+
+ switch (op) {
+ case Token::ADD:
+ builtin = builtins->javascript_builtin(Builtins::ADD);
+ break;
+ case Token::SUB:
+ builtin = builtins->javascript_builtin(Builtins::SUB);
+ break;
+ case Token::MUL:
+ builtin = builtins->javascript_builtin(Builtins::MUL);
+ break;
+ case Token::DIV:
+ builtin = builtins->javascript_builtin(Builtins::DIV);
+ break;
+ case Token::MOD:
+ builtin = builtins->javascript_builtin(Builtins::MOD);
+ break;
+ case Token::BIT_AND:
+ builtin = builtins->javascript_builtin(Builtins::BIT_AND);
+ break;
+ case Token::BIT_OR:
+ builtin = builtins->javascript_builtin(Builtins::BIT_OR);
+ break;
+ case Token::BIT_XOR:
+ builtin = builtins->javascript_builtin(Builtins::BIT_XOR);
+ break;
+ case Token::SHR:
+ builtin = builtins->javascript_builtin(Builtins::SHR);
+ break;
+ case Token::SAR:
+ builtin = builtins->javascript_builtin(Builtins::SAR);
+ break;
+ case Token::SHL:
+ builtin = builtins->javascript_builtin(Builtins::SHL);
+ break;
+ default:
+ UNREACHABLE();
+ }
+
+ Handle<JSFunction> builtin_function(JSFunction::cast(builtin));
+
+ bool caught_exception;
+ Object** builtin_args[] = { right.location() };
+ Handle<Object> result = Execution::Call(builtin_function,
+ left,
+ ARRAY_SIZE(builtin_args),
+ builtin_args,
+ &caught_exception);
+ if (caught_exception) {
+ return Failure::Exception();
+ }
return *result;
}
diff --git a/deps/v8/src/ic.h b/deps/v8/src/ic.h
index 738b6f4e68..0d5df963f7 100644
--- a/deps/v8/src/ic.h
+++ b/deps/v8/src/ic.h
@@ -117,9 +117,14 @@ class IC {
return ComputeMode() == RelocInfo::CODE_TARGET_CONTEXT;
}
- // Returns the map to use for caching stubs for a given object.
- // This method should not be called with undefined or null.
- static inline Map* GetCodeCacheMapForObject(Object* object);
+ // Determines which map must be used for keeping the code stub.
+ // These methods should not be called with undefined or null.
+ static inline InlineCacheHolderFlag GetCodeCacheForObject(Object* object,
+ JSObject* holder);
+ static inline InlineCacheHolderFlag GetCodeCacheForObject(JSObject* object,
+ JSObject* holder);
+ static inline Map* GetCodeCacheMap(Object* object,
+ InlineCacheHolderFlag holder);
protected:
Address fp() const { return fp_; }
@@ -384,6 +389,7 @@ class StoreIC: public IC {
static void GenerateMiss(MacroAssembler* masm);
static void GenerateMegamorphic(MacroAssembler* masm);
static void GenerateArrayLength(MacroAssembler* masm);
+ static void GenerateNormal(MacroAssembler* masm);
private:
// Update the inline cache and the global stub cache based on the
diff --git a/deps/v8/src/liveedit-debugger.js b/deps/v8/src/liveedit-debugger.js
index 34d5c0da89..c8c6f082c0 100644
--- a/deps/v8/src/liveedit-debugger.js
+++ b/deps/v8/src/liveedit-debugger.js
@@ -51,7 +51,8 @@ Debug.LiveEdit = new function() {
// Applies the change to the script.
// The change is in form of list of chunks encoded in a single array as
// a series of triplets (pos1_start, pos1_end, pos2_end)
- function ApplyPatchMultiChunk(script, diff_array, new_source, change_log) {
+ function ApplyPatchMultiChunk(script, diff_array, new_source, preview_only,
+ change_log) {
var old_source = script.source;
@@ -96,7 +97,7 @@ Debug.LiveEdit = new function() {
}
// Recursively collects all newly compiled functions that are going into
- // business and should be have link to the actual script updated.
+ // business and should have link to the actual script updated.
function CollectNew(node_list) {
for (var i = 0; i < node_list.length; i++) {
link_to_original_script_list.push(node_list[i]);
@@ -121,6 +122,20 @@ Debug.LiveEdit = new function() {
}
}
+ var preview_description = {
+ change_tree: DescribeChangeTree(root_old_node),
+ textual_diff: {
+ old_len: old_source.length,
+ new_len: new_source.length,
+ chunks: diff_array
+ },
+ updated: false
+ };
+
+ if (preview_only) {
+ return preview_description;
+ }
+
HarvestTodo(root_old_node);
// Collect shared infos for functions whose code need to be patched.
@@ -132,13 +147,15 @@ Debug.LiveEdit = new function() {
}
}
- // Check that function being patched is not currently on stack.
- CheckStackActivations(replaced_function_infos, change_log);
-
-
// We haven't changed anything before this line yet.
// Committing all changes.
+ // Check that function being patched is not currently on stack or drop them.
+ var dropped_functions_number =
+ CheckStackActivations(replaced_function_infos, change_log);
+
+ preview_description.stack_modified = dropped_functions_number != 0;
+
// Start with breakpoints. Convert their line/column positions and
// temporary remove.
var break_points_restorer = TemporaryRemoveBreakPoints(script, change_log);
@@ -166,6 +183,8 @@ Debug.LiveEdit = new function() {
LinkToOldScript(link_to_old_script_list[i], old_script,
link_to_old_script_report);
}
+
+ preview_description.created_script_name = old_script_name;
}
// Link to an actual script all the functions that we are going to use.
@@ -189,6 +208,9 @@ Debug.LiveEdit = new function() {
}
break_points_restorer(pos_translator, old_script);
+
+ preview_description.updated = true;
+ return preview_description;
}
// Function is public.
this.ApplyPatchMultiChunk = ApplyPatchMultiChunk;
@@ -494,6 +516,16 @@ Debug.LiveEdit = new function() {
this.new_end_pos = void 0;
this.corresponding_node = void 0;
this.unmatched_new_nodes = void 0;
+
+ // 'Textual' correspondence/matching is weaker than 'pure'
+ // correspondence/matching. We need 'textual' level for visual presentation
+ // in UI, we use 'pure' level for actual code manipulation.
+ // Sometimes only function body is changed (functions in old and new script
+ // textually correspond), but we cannot patch the code, so we see them
+ // as an old function deleted and new function created.
+ this.textual_corresponding_node = void 0;
+ this.textually_unmatched_new_nodes = void 0;
+
this.live_shared_info_wrapper = void 0;
}
@@ -640,6 +672,7 @@ Debug.LiveEdit = new function() {
var new_children = new_node.children;
var unmatched_new_nodes_list = [];
+ var textually_unmatched_new_nodes_list = [];
var old_index = 0;
var new_index = 0;
@@ -650,6 +683,7 @@ Debug.LiveEdit = new function() {
if (new_children[new_index].info.start_position <
old_children[old_index].new_start_pos) {
unmatched_new_nodes_list.push(new_children[new_index]);
+ textually_unmatched_new_nodes_list.push(new_children[new_index]);
new_index++;
} else if (new_children[new_index].info.start_position ==
old_children[old_index].new_start_pos) {
@@ -657,6 +691,8 @@ Debug.LiveEdit = new function() {
old_children[old_index].new_end_pos) {
old_children[old_index].corresponding_node =
new_children[new_index];
+ old_children[old_index].textual_corresponding_node =
+ new_children[new_index];
if (old_children[old_index].status != FunctionStatus.UNCHANGED) {
ProcessChildren(old_children[old_index],
new_children[new_index]);
@@ -673,6 +709,7 @@ Debug.LiveEdit = new function() {
"No corresponding function in new script found";
old_node.status = FunctionStatus.CHANGED;
unmatched_new_nodes_list.push(new_children[new_index]);
+ textually_unmatched_new_nodes_list.push(new_children[new_index]);
}
new_index++;
old_index++;
@@ -694,21 +731,28 @@ Debug.LiveEdit = new function() {
while (new_index < new_children.length) {
unmatched_new_nodes_list.push(new_children[new_index]);
+ textually_unmatched_new_nodes_list.push(new_children[new_index]);
new_index++;
}
if (old_node.status == FunctionStatus.CHANGED) {
- if (!CompareFunctionExpectations(old_node.info, new_node.info)) {
+ var why_wrong_expectations =
+ WhyFunctionExpectationsDiffer(old_node.info, new_node.info);
+ if (why_wrong_expectations) {
old_node.status = FunctionStatus.DAMAGED;
- old_node.status_explanation = "Changed code expectations";
+ old_node.status_explanation = why_wrong_expectations;
}
}
old_node.unmatched_new_nodes = unmatched_new_nodes_list;
+ old_node.textually_unmatched_new_nodes =
+ textually_unmatched_new_nodes_list;
}
ProcessChildren(old_code_tree, new_code_tree);
old_code_tree.corresponding_node = new_code_tree;
+ old_code_tree.textual_corresponding_node = new_code_tree;
+
Assert(old_code_tree.status != FunctionStatus.DAMAGED,
"Script became damaged");
}
@@ -792,27 +836,37 @@ Debug.LiveEdit = new function() {
}
// Compares a function interface old and new version, whether it
- // changed or not.
- function CompareFunctionExpectations(function_info1, function_info2) {
+ // changed or not. Returns explanation if they differ.
+ function WhyFunctionExpectationsDiffer(function_info1, function_info2) {
// Check that function has the same number of parameters (there may exist
// an adapter, that won't survive function parameter number change).
if (function_info1.param_num != function_info2.param_num) {
- return false;
+ return "Changed parameter number: " + function_info1.param_num +
+ " and " + function_info2.param_num;
}
var scope_info1 = function_info1.scope_info;
var scope_info2 = function_info2.scope_info;
-
- if (!scope_info1) {
- return !scope_info2;
+
+ var scope_info1_text;
+ var scope_info2_text;
+
+ if (scope_info1) {
+ scope_info1_text = scope_info1.toString();
+ } else {
+ scope_info1_text = "";
}
-
- if (scope_info1.length != scope_info2.length) {
- return false;
+ if (scope_info2) {
+ scope_info2_text = scope_info2.toString();
+ } else {
+ scope_info2_text = "";
}
-
- // Check that outer scope structure is not changed. Otherwise the function
- // will not properly work with existing scopes.
- return scope_info1.toString() == scope_info2.toString();
+
+ if (scope_info1_text != scope_info2_text) {
+ return "Incompatible variable maps: [" + scope_info1_text +
+ "] and [" + scope_info2_text + "]";
+ }
+ // No differences. Return undefined.
+ return;
}
// Minifier forward declaration.
@@ -856,6 +910,8 @@ Debug.LiveEdit = new function() {
change_log.push( { functions_on_stack: problems } );
throw new Failure("Blocked by functions on stack");
}
+
+ return dropped.length;
}
// A copy of the FunctionPatchabilityStatus enum from liveedit.h
@@ -897,14 +953,11 @@ Debug.LiveEdit = new function() {
this.GetPcFromSourcePos = GetPcFromSourcePos;
// LiveEdit main entry point: changes a script text to a new string.
- function SetScriptSource(script, new_source, change_log) {
+ function SetScriptSource(script, new_source, preview_only, change_log) {
var old_source = script.source;
var diff = CompareStringsLinewise(old_source, new_source);
- if (diff.length == 0) {
- change_log.push( { empty_diff: true } );
- return;
- }
- ApplyPatchMultiChunk(script, diff, new_source, change_log);
+ return ApplyPatchMultiChunk(script, diff, new_source, preview_only,
+ change_log);
}
// Function is public.
this.SetScriptSource = SetScriptSource;
@@ -931,7 +984,67 @@ Debug.LiveEdit = new function() {
return ApplyPatchMultiChunk(script,
[ change_pos, change_pos + change_len, change_pos + new_str.length],
- new_source, change_log);
+ new_source, false, change_log);
+ }
+
+ // Creates JSON description for a change tree.
+ function DescribeChangeTree(old_code_tree) {
+
+ function ProcessOldNode(node) {
+ var child_infos = [];
+ for (var i = 0; i < node.children.length; i++) {
+ var child = node.children[i];
+ if (child.status != FunctionStatus.UNCHANGED) {
+ child_infos.push(ProcessOldNode(child));
+ }
+ }
+ var new_child_infos = [];
+ if (node.textually_unmatched_new_nodes) {
+ for (var i = 0; i < node.textually_unmatched_new_nodes.length; i++) {
+ var child = node.textually_unmatched_new_nodes[i];
+ new_child_infos.push(ProcessNewNode(child));
+ }
+ }
+ var res = {
+ name: node.info.function_name,
+ positions: DescribePositions(node),
+ status: node.status,
+ children: child_infos,
+ new_children: new_child_infos
+ };
+ if (node.status_explanation) {
+ res.status_explanation = node.status_explanation;
+ }
+ if (node.textual_corresponding_node) {
+ res.new_positions = DescribePositions(node.textual_corresponding_node);
+ }
+ return res;
+ }
+
+ function ProcessNewNode(node) {
+ var child_infos = [];
+ // Do not list ancestors.
+ if (false) {
+ for (var i = 0; i < node.children.length; i++) {
+ child_infos.push(ProcessNewNode(node.children[i]));
+ }
+ }
+ var res = {
+ name: node.info.function_name,
+ positions: DescribePositions(node),
+ children: child_infos,
+ };
+ return res;
+ }
+
+ function DescribePositions(node) {
+ return {
+ start_position: node.info.start_position,
+ end_position: node.info.end_position
+ };
+ }
+
+ return ProcessOldNode(old_code_tree);
}
diff --git a/deps/v8/src/liveedit.cc b/deps/v8/src/liveedit.cc
index 950f8e0de2..04631a3f7c 100644
--- a/deps/v8/src/liveedit.cc
+++ b/deps/v8/src/liveedit.cc
@@ -1187,7 +1187,12 @@ static bool FixTryCatchHandler(StackFrame* top_frame,
// Returns error message or NULL.
static const char* DropFrames(Vector<StackFrame*> frames,
int top_frame_index,
- int bottom_js_frame_index) {
+ int bottom_js_frame_index,
+ Debug::FrameDropMode* mode) {
+ if (Debug::kFrameDropperFrameSize < 0) {
+ return "Stack manipulations are not supported in this architecture.";
+ }
+
StackFrame* pre_top_frame = frames[top_frame_index - 1];
StackFrame* top_frame = frames[top_frame_index];
StackFrame* bottom_js_frame = frames[bottom_js_frame_index];
@@ -1198,12 +1203,18 @@ static const char* DropFrames(Vector<StackFrame*> frames,
if (pre_top_frame->code()->is_inline_cache_stub() &&
pre_top_frame->code()->ic_state() == DEBUG_BREAK) {
// OK, we can drop inline cache calls.
+ *mode = Debug::FRAME_DROPPED_IN_IC_CALL;
+ } else if (pre_top_frame->code() == Debug::debug_break_slot()) {
+ // OK, we can drop debug break slot.
+ *mode = Debug::FRAME_DROPPED_IN_DEBUG_SLOT_CALL;
} else if (pre_top_frame->code() ==
Builtins::builtin(Builtins::FrameDropper_LiveEdit)) {
// OK, we can drop our own code.
+ *mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL;
} else if (pre_top_frame->code()->kind() == Code::STUB &&
pre_top_frame->code()->major_key()) {
- // Unit Test entry, it's fine, we support this case.
+ // Entry from our unit tests, it's fine, we support this case.
+ *mode = Debug::FRAME_DROPPED_IN_DIRECT_CALL;
} else {
return "Unknown structure of stack above changing function";
}
@@ -1316,8 +1327,9 @@ static const char* DropActivationsInActiveThread(
return NULL;
}
+ Debug::FrameDropMode drop_mode = Debug::FRAMES_UNTOUCHED;
const char* error_message = DropFrames(frames, top_frame_index,
- bottom_js_frame_index);
+ bottom_js_frame_index, &drop_mode);
if (error_message != NULL) {
return error_message;
@@ -1331,7 +1343,7 @@ static const char* DropActivationsInActiveThread(
break;
}
}
- Debug::FramesHaveBeenDropped(new_id);
+ Debug::FramesHaveBeenDropped(new_id, drop_mode);
// Replace "blocked on active" with "replaced on active" status.
for (int i = 0; i < array_len; i++) {
diff --git a/deps/v8/src/macros.py b/deps/v8/src/macros.py
index 7d97918245..b4be15bf4c 100644
--- a/deps/v8/src/macros.py
+++ b/deps/v8/src/macros.py
@@ -120,6 +120,7 @@ macro IS_SPEC_OBJECT_OR_NULL(arg) = (%_IsObject(arg) || %_IsFunction(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_INTEGER_MAP_MINUS_ZERO(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : %NumberToIntegerMapMinusZero(ToNumber(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));
@@ -145,11 +146,15 @@ macro DATE_VALUE(arg) = (%_ClassOf(arg) === 'Date' ? %_ValueOf(arg) : ThrowDateT
macro DAY(time) = ($floor(time / 86400000));
macro MONTH_FROM_TIME(time) = (MonthFromTime(time));
macro DATE_FROM_TIME(time) = (DateFromTime(time));
+macro NAN_OR_DATE_FROM_TIME(time) = (NUMBER_IS_NAN(time) ? time : DATE_FROM_TIME(time));
macro YEAR_FROM_TIME(time) = (YearFromTime(time));
macro HOUR_FROM_TIME(time) = (Modulo($floor(time / 3600000), 24));
macro MIN_FROM_TIME(time) = (Modulo($floor(time / 60000), 60));
+macro NAN_OR_MIN_FROM_TIME(time) = (NUMBER_IS_NAN(time) ? time : MIN_FROM_TIME(time));
macro SEC_FROM_TIME(time) = (Modulo($floor(time / 1000), 60));
+macro NAN_OR_SEC_FROM_TIME(time) = (NUMBER_IS_NAN(time) ? time : SEC_FROM_TIME(time));
macro MS_FROM_TIME(time) = (Modulo(time, 1000));
+macro NAN_OR_MS_FROM_TIME(time) = (NUMBER_IS_NAN(time) ? time : MS_FROM_TIME(time));
# Last input and last subject of regexp matches.
macro LAST_SUBJECT(array) = ((array)[1]);
diff --git a/deps/v8/src/messages.js b/deps/v8/src/messages.js
index a46af4ac2d..99ba45464f 100644
--- a/deps/v8/src/messages.js
+++ b/deps/v8/src/messages.js
@@ -196,7 +196,9 @@ function FormatMessage(message) {
circular_structure: "Converting circular structure to JSON",
obj_ctor_property_non_object: "Object.%0 called on non-object",
array_indexof_not_defined: "Array.getIndexOf: Argument undefined",
- illegal_access: "illegal access"
+ object_not_extensible: "Can't add property %0, object is not extensible",
+ illegal_access: "Illegal access",
+ invalid_preparser_data: "Invalid preparser data for function %0"
};
}
var format = kMessages[message.type];
diff --git a/deps/v8/src/objects-inl.h b/deps/v8/src/objects-inl.h
index f9def82d23..0e45550845 100644
--- a/deps/v8/src/objects-inl.h
+++ b/deps/v8/src/objects-inl.h
@@ -1335,6 +1335,21 @@ void JSObject::InitializeBody(int object_size) {
}
+bool JSObject::HasFastProperties() {
+ return !properties()->IsDictionary();
+}
+
+
+int JSObject::MaxFastProperties() {
+ // Allow extra fast properties if the object has more than
+ // kMaxFastProperties in-object properties. When this is the case,
+ // it is very unlikely that the object is being used as a dictionary
+ // and there is a good chance that allowing more map transitions
+ // will be worth it.
+ return Max(map()->inobject_properties(), kMaxFastProperties);
+}
+
+
void Struct::InitializeBody(int object_size) {
Object* value = Heap::undefined_value();
for (int offset = kHeaderSize; offset < object_size; offset += kPointerSize) {
@@ -1343,11 +1358,6 @@ void Struct::InitializeBody(int object_size) {
}
-bool JSObject::HasFastProperties() {
- return !properties()->IsDictionary();
-}
-
-
bool Object::ToArrayIndex(uint32_t* index) {
if (IsSmi()) {
int value = Smi::cast(this)->value();
@@ -2189,6 +2199,20 @@ bool Map::is_access_check_needed() {
}
+void Map::set_is_extensible(bool value) {
+ if (value) {
+ set_bit_field2(bit_field2() | (1 << kIsExtensible));
+ } else {
+ set_bit_field2(bit_field2() & ~(1 << kIsExtensible));
+ }
+}
+
+bool Map::is_extensible() {
+ return ((1 << kIsExtensible) & bit_field2()) != 0;
+}
+
+
+
Code::Flags Code::flags() {
return static_cast<Flags>(READ_INT_FIELD(this, kFlagsOffset));
}
@@ -2263,13 +2287,15 @@ Code::Flags Code::ComputeFlags(Kind kind,
InLoopFlag in_loop,
InlineCacheState ic_state,
PropertyType type,
- int argc) {
+ int argc,
+ InlineCacheHolderFlag holder) {
// Compute the bit mask.
int bits = kind << kFlagsKindShift;
if (in_loop) bits |= kFlagsICInLoopMask;
bits |= ic_state << kFlagsICStateShift;
bits |= type << kFlagsTypeShift;
bits |= argc << kFlagsArgumentsCountShift;
+ if (holder == PROTOTYPE_MAP) bits |= kFlagsCacheInPrototypeMapMask;
// Cast to flags and validate result before returning it.
Flags result = static_cast<Flags>(bits);
ASSERT(ExtractKindFromFlags(result) == kind);
@@ -2283,9 +2309,10 @@ Code::Flags Code::ComputeFlags(Kind kind,
Code::Flags Code::ComputeMonomorphicFlags(Kind kind,
PropertyType type,
+ InlineCacheHolderFlag holder,
InLoopFlag in_loop,
int argc) {
- return ComputeFlags(kind, in_loop, MONOMORPHIC, type, argc);
+ return ComputeFlags(kind, in_loop, MONOMORPHIC, type, argc, holder);
}
@@ -2318,6 +2345,12 @@ int Code::ExtractArgumentsCountFromFlags(Flags flags) {
}
+InlineCacheHolderFlag Code::ExtractCacheHolderFromFlags(Flags flags) {
+ int bits = (flags & kFlagsCacheInPrototypeMapMask);
+ return bits != 0 ? PROTOTYPE_MAP : OWN_MAP;
+}
+
+
Code::Flags Code::RemoveTypeFromFlags(Flags flags) {
int bits = flags & ~kFlagsTypeMask;
return static_cast<Flags>(bits);
@@ -2774,7 +2807,7 @@ JSValue* JSValue::cast(Object* obj) {
INT_ACCESSORS(Code, instruction_size, kInstructionSizeOffset)
-INT_ACCESSORS(Code, relocation_size, kRelocationSizeOffset)
+ACCESSORS(Code, relocation_info, ByteArray, kRelocationInfoOffset)
INT_ACCESSORS(Code, sinfo_size, kSInfoSizeOffset)
@@ -2783,13 +2816,28 @@ byte* Code::instruction_start() {
}
+byte* Code::instruction_end() {
+ return instruction_start() + instruction_size();
+}
+
+
int Code::body_size() {
- return RoundUp(instruction_size() + relocation_size(), kObjectAlignment);
+ return RoundUp(instruction_size(), kObjectAlignment);
+}
+
+
+ByteArray* Code::unchecked_relocation_info() {
+ return reinterpret_cast<ByteArray*>(READ_FIELD(this, kRelocationInfoOffset));
}
byte* Code::relocation_start() {
- return FIELD_ADDR(this, kHeaderSize + instruction_size());
+ return unchecked_relocation_info()->GetDataStartAddress();
+}
+
+
+int Code::relocation_size() {
+ return unchecked_relocation_info()->length();
}
diff --git a/deps/v8/src/objects.cc b/deps/v8/src/objects.cc
index 883b28ef42..e79a5505c9 100644
--- a/deps/v8/src/objects.cc
+++ b/deps/v8/src/objects.cc
@@ -1276,7 +1276,7 @@ Object* JSObject::AddFastProperty(String* name,
}
if (map()->unused_property_fields() == 0) {
- if (properties()->length() > kMaxFastProperties) {
+ if (properties()->length() > MaxFastProperties()) {
Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
if (obj->IsFailure()) return obj;
return AddSlowProperty(name, value, attributes);
@@ -1386,6 +1386,11 @@ Object* JSObject::AddProperty(String* name,
Object* value,
PropertyAttributes attributes) {
ASSERT(!IsJSGlobalProxy());
+ if (!map()->is_extensible()) {
+ Handle<Object> args[1] = {Handle<String>(name)};
+ return Top::Throw(*Factory::NewTypeError("object_not_extensible",
+ HandleVector(args, 1)));
+ }
if (HasFastProperties()) {
// Ensure the descriptor array does not get too big.
if (map()->instance_descriptors()->number_of_descriptors() <
@@ -1474,7 +1479,7 @@ Object* JSObject::ConvertDescriptorToField(String* name,
Object* new_value,
PropertyAttributes attributes) {
if (map()->unused_property_fields() == 0 &&
- properties()->length() > kMaxFastProperties) {
+ properties()->length() > MaxFastProperties()) {
Object* obj = NormalizeProperties(CLEAR_INOBJECT_PROPERTIES, 0);
if (obj->IsFailure()) return obj;
return ReplaceSlowProperty(name, new_value, attributes);
@@ -1746,8 +1751,6 @@ void JSObject::LocalLookupRealNamedProperty(String* name,
result->DictionaryResult(this, entry);
return;
}
- // Slow case object skipped during lookup. Do not use inline caching.
- if (!IsGlobalObject()) result->DisallowCaching();
}
result->NotFound();
}
@@ -2576,6 +2579,25 @@ bool JSObject::ReferencesObject(Object* obj) {
}
+Object* JSObject::PreventExtensions() {
+ // If there are fast elements we normalize.
+ if (HasFastElements()) {
+ NormalizeElements();
+ }
+ // Make sure that we never go back to fast case.
+ element_dictionary()->set_requires_slow_elements();
+
+ // Do a map transition, other objects with this map may still
+ // be extensible.
+ Object* new_map = map()->CopyDropTransitions();
+ if (new_map->IsFailure()) return new_map;
+ Map::cast(new_map)->set_is_extensible(false);
+ set_map(Map::cast(new_map));
+ ASSERT(!map()->is_extensible());
+ return new_map;
+}
+
+
// Tests for the fast common case for property enumeration:
// - This object and all prototypes has an enum cache (which means that it has
// no interceptors and needs no access checks).
@@ -3076,7 +3098,7 @@ Object* Map::CopyDropTransitions() {
Object* descriptors = instance_descriptors()->RemoveTransitions();
if (descriptors->IsFailure()) return descriptors;
cast(new_map)->set_instance_descriptors(DescriptorArray::cast(descriptors));
- return cast(new_map);
+ return new_map;
}
@@ -5292,7 +5314,15 @@ void Code::CodeIterateBody(ObjectVisitor* v) {
RelocInfo::ModeMask(RelocInfo::DEBUG_BREAK_SLOT) |
RelocInfo::ModeMask(RelocInfo::RUNTIME_ENTRY);
- for (RelocIterator it(this, mode_mask); !it.done(); it.next()) {
+ // Use the relocation info pointer before it is visited by
+ // the heap compaction in the next statement.
+ RelocIterator it(this, mode_mask);
+
+ IteratePointers(v,
+ kRelocationInfoOffset,
+ kRelocationInfoOffset + kPointerSize);
+
+ for (; !it.done(); it.next()) {
it.rinfo()->Visit(v);
}
@@ -5312,14 +5342,6 @@ void Code::CopyFrom(const CodeDesc& desc) {
// copy code
memmove(instruction_start(), desc.buffer, desc.instr_size);
- // fill gap with zero bytes
- { byte* p = instruction_start() + desc.instr_size;
- byte* q = relocation_start();
- while (p < q) {
- *p++ = 0;
- }
- }
-
// copy reloc info
memmove(relocation_start(),
desc.buffer + desc.buffer_size - desc.reloc_size,
@@ -6209,6 +6231,15 @@ Object* JSObject::SetElementWithoutInterceptor(uint32_t index, Object* value) {
return value;
}
}
+ // When we set the is_extensible flag to false we always force
+ // the element into dictionary mode (and force them to stay there).
+ if (!map()->is_extensible()) {
+ Handle<Object> number(Heap::NumberFromUint32(index));
+ Handle<String> index_string(Factory::NumberToString(number));
+ Handle<Object> args[1] = { index_string };
+ return Top::Throw(*Factory::NewTypeError("object_not_extensible",
+ HandleVector(args, 1)));
+ }
Object* result = dictionary->AtNumberPut(index, value);
if (result->IsFailure()) return result;
if (elms != FixedArray::cast(result)) {
diff --git a/deps/v8/src/objects.h b/deps/v8/src/objects.h
index 0ad6f14cde..4a7dee6a83 100644
--- a/deps/v8/src/objects.h
+++ b/deps/v8/src/objects.h
@@ -1367,6 +1367,7 @@ class JSObject: public HeapObject {
// Returns the index'th element.
// The undefined object if index is out of bounds.
Object* GetElementWithReceiver(JSObject* receiver, uint32_t index);
+ Object* GetElementWithInterceptor(JSObject* receiver, uint32_t index);
Object* SetFastElementsCapacityAndLength(int capacity, int length);
Object* SetSlowElements(Object* length);
@@ -1516,6 +1517,10 @@ class JSObject: public HeapObject {
// Casting.
static inline JSObject* cast(Object* obj);
+ // Disalow further properties to be added to the object.
+ Object* PreventExtensions();
+
+
// Dispatched behavior.
void JSObjectIterateBody(int object_size, ObjectVisitor* v);
void JSObjectShortPrint(StringStream* accumulator);
@@ -1547,6 +1552,11 @@ class JSObject: public HeapObject {
#endif
Object* SlowReverseLookup(Object* value);
+ // Maximal number of fast properties for the JSObject. Used to
+ // restrict the number of map transitions to avoid an explosion in
+ // the number of maps for objects used as dictionaries.
+ inline int MaxFastProperties();
+
// Maximal number of elements (numbered 0 .. kMaxElementCount - 1).
// Also maximal value of JSArray's length property.
static const uint32_t kMaxElementCount = 0xffffffffu;
@@ -1568,8 +1578,6 @@ class JSObject: public HeapObject {
STATIC_CHECK(kHeaderSize == Internals::kJSObjectHeaderSize);
- Object* GetElementWithInterceptor(JSObject* receiver, uint32_t index);
-
private:
Object* GetElementWithCallback(Object* receiver,
Object* structure,
@@ -2728,9 +2736,13 @@ class Code: public HeapObject {
inline int instruction_size();
inline void set_instruction_size(int value);
- // [relocation_size]: Size of relocation information.
+ // [relocation_info]: Code relocation information
+ DECL_ACCESSORS(relocation_info, ByteArray)
+
+ // Unchecked accessor to be used during GC.
+ inline ByteArray* unchecked_relocation_info();
+
inline int relocation_size();
- inline void set_relocation_size(int value);
// [sinfo_size]: Size of scope information.
inline int sinfo_size();
@@ -2765,11 +2777,13 @@ class Code: public HeapObject {
InLoopFlag in_loop = NOT_IN_LOOP,
InlineCacheState ic_state = UNINITIALIZED,
PropertyType type = NORMAL,
- int argc = -1);
+ int argc = -1,
+ InlineCacheHolderFlag holder = OWN_MAP);
static inline Flags ComputeMonomorphicFlags(
Kind kind,
PropertyType type,
+ InlineCacheHolderFlag holder = OWN_MAP,
InLoopFlag in_loop = NOT_IN_LOOP,
int argc = -1);
@@ -2778,6 +2792,7 @@ class Code: public HeapObject {
static inline InLoopFlag ExtractICInLoopFromFlags(Flags flags);
static inline PropertyType ExtractTypeFromFlags(Flags flags);
static inline int ExtractArgumentsCountFromFlags(Flags flags);
+ static inline InlineCacheHolderFlag ExtractCacheHolderFromFlags(Flags flags);
static inline Flags RemoveTypeFromFlags(Flags flags);
// Convert a target address into a code object.
@@ -2786,6 +2801,9 @@ class Code: public HeapObject {
// Returns the address of the first instruction.
inline byte* instruction_start();
+ // Returns the address right after the last instruction.
+ inline byte* instruction_end();
+
// Returns the size of the instructions, padding, and relocation information.
inline int body_size();
@@ -2846,8 +2864,8 @@ class Code: public HeapObject {
// Layout description.
static const int kInstructionSizeOffset = HeapObject::kHeaderSize;
- static const int kRelocationSizeOffset = kInstructionSizeOffset + kIntSize;
- static const int kSInfoSizeOffset = kRelocationSizeOffset + kIntSize;
+ static const int kRelocationInfoOffset = kInstructionSizeOffset + kIntSize;
+ static const int kSInfoSizeOffset = kRelocationInfoOffset + kPointerSize;
static const int kFlagsOffset = kSInfoSizeOffset + kIntSize;
static const int kKindSpecificFlagsOffset = kFlagsOffset + kIntSize;
// Add padding to align the instruction start following right after
@@ -2864,16 +2882,18 @@ class Code: public HeapObject {
static const int kFlagsICInLoopShift = 3;
static const int kFlagsTypeShift = 4;
static const int kFlagsKindShift = 7;
- static const int kFlagsArgumentsCountShift = 11;
+ static const int kFlagsICHolderShift = 11;
+ static const int kFlagsArgumentsCountShift = 12;
static const int kFlagsICStateMask = 0x00000007; // 00000000111
static const int kFlagsICInLoopMask = 0x00000008; // 00000001000
static const int kFlagsTypeMask = 0x00000070; // 00001110000
static const int kFlagsKindMask = 0x00000780; // 11110000000
- static const int kFlagsArgumentsCountMask = 0xFFFFF800;
+ static const int kFlagsCacheInPrototypeMapMask = 0x00000800;
+ static const int kFlagsArgumentsCountMask = 0xFFFFF000;
static const int kFlagsNotUsedInLookup =
- (kFlagsICInLoopMask | kFlagsTypeMask);
+ (kFlagsICInLoopMask | kFlagsTypeMask | kFlagsCacheInPrototypeMapMask);
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Code);
@@ -2980,13 +3000,8 @@ class Map: public HeapObject {
return ((1 << kHasInstanceCallHandler) & bit_field()) != 0;
}
- inline void set_is_extensible() {
- set_bit_field2(bit_field2() | (1 << kIsExtensible));
- }
-
- inline bool is_extensible() {
- return ((1 << kIsExtensible) & bit_field2()) != 0;
- }
+ inline void set_is_extensible(bool value);
+ inline bool is_extensible();
// Tells whether the instance has fast elements.
void set_has_fast_elements(bool value) {
@@ -4398,6 +4413,8 @@ class SeqString: public String {
// Each character in the AsciiString is an ascii character.
class SeqAsciiString: public SeqString {
public:
+ static const bool kHasAsciiEncoding = true;
+
// Dispatched behavior.
inline uint16_t SeqAsciiStringGet(int index);
inline void SeqAsciiStringSet(int index, uint16_t value);
@@ -4447,6 +4464,8 @@ class SeqAsciiString: public SeqString {
// Each character in the TwoByteString is a two-byte uint16_t.
class SeqTwoByteString: public SeqString {
public:
+ static const bool kHasAsciiEncoding = false;
+
// Dispatched behavior.
inline uint16_t SeqTwoByteStringGet(int index);
inline void SeqTwoByteStringSet(int index, uint16_t value);
@@ -4579,6 +4598,8 @@ class ExternalString: public String {
// ASCII string.
class ExternalAsciiString: public ExternalString {
public:
+ static const bool kHasAsciiEncoding = true;
+
typedef v8::String::ExternalAsciiStringResource Resource;
// The underlying resource.
@@ -4611,6 +4632,8 @@ class ExternalAsciiString: public ExternalString {
// encoded string.
class ExternalTwoByteString: public ExternalString {
public:
+ static const bool kHasAsciiEncoding = false;
+
typedef v8::String::ExternalStringResource Resource;
// The underlying string resource.
diff --git a/deps/v8/src/parser.cc b/deps/v8/src/parser.cc
index 31bac9184f..fb58cfa3f7 100644
--- a/deps/v8/src/parser.cc
+++ b/deps/v8/src/parser.cc
@@ -134,6 +134,7 @@ class Parser {
// Report syntax error
void ReportUnexpectedToken(Token::Value token);
+ void ReportInvalidPreparseData(Handle<String> name, bool* ok);
Handle<Script> script_;
Scanner scanner_;
@@ -3263,6 +3264,15 @@ void Parser::ReportUnexpectedToken(Token::Value token) {
}
+void Parser::ReportInvalidPreparseData(Handle<String> name, bool* ok) {
+ SmartPointer<char> name_string = name->ToCString(DISALLOW_NULLS);
+ const char* element[1] = { *name_string };
+ ReportMessage("invalid_preparser_data",
+ Vector<const char*>(element, 1));
+ *ok = false;
+}
+
+
Expression* Parser::ParsePrimaryExpression(bool* ok) {
// PrimaryExpression ::
// 'this'
@@ -3810,7 +3820,14 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> var_name,
Handle<FixedArray> this_property_assignments;
if (is_lazily_compiled && pre_data() != NULL) {
FunctionEntry entry = pre_data()->GetFunctionEnd(start_pos);
+ if (!entry.is_valid()) {
+ ReportInvalidPreparseData(name, CHECK_OK);
+ }
int end_pos = entry.end_pos();
+ if (end_pos <= start_pos) {
+ // End position greater than end of stream is safe, and hard to check.
+ ReportInvalidPreparseData(name, CHECK_OK);
+ }
Counters::total_preparse_skipped.Increment(end_pos - start_pos);
scanner_.SeekForward(end_pos);
materialized_literal_count = entry.literal_count();
diff --git a/deps/v8/src/rewriter.cc b/deps/v8/src/rewriter.cc
index c97408e0d4..73301b9186 100644
--- a/deps/v8/src/rewriter.cc
+++ b/deps/v8/src/rewriter.cc
@@ -87,11 +87,13 @@ void AstOptimizer::VisitBlock(Block* node) {
void AstOptimizer::VisitExpressionStatement(ExpressionStatement* node) {
+ node->expression()->set_no_negative_zero(true);
Visit(node->expression());
}
void AstOptimizer::VisitIfStatement(IfStatement* node) {
+ node->condition()->set_no_negative_zero(true);
Visit(node->condition());
Visit(node->then_statement());
if (node->HasElseStatement()) {
@@ -101,6 +103,7 @@ void AstOptimizer::VisitIfStatement(IfStatement* node) {
void AstOptimizer::VisitDoWhileStatement(DoWhileStatement* node) {
+ node->cond()->set_no_negative_zero(true);
Visit(node->cond());
Visit(node->body());
}
@@ -108,6 +111,7 @@ void AstOptimizer::VisitDoWhileStatement(DoWhileStatement* node) {
void AstOptimizer::VisitWhileStatement(WhileStatement* node) {
has_function_literal_ = false;
+ node->cond()->set_no_negative_zero(true);
Visit(node->cond());
node->may_have_function_literal_ = has_function_literal_;
Visit(node->body());
@@ -120,6 +124,7 @@ void AstOptimizer::VisitForStatement(ForStatement* node) {
}
if (node->cond() != NULL) {
has_function_literal_ = false;
+ node->cond()->set_no_negative_zero(true);
Visit(node->cond());
node->may_have_function_literal_ = has_function_literal_;
}
@@ -151,6 +156,7 @@ void AstOptimizer::VisitTryFinallyStatement(TryFinallyStatement* node) {
void AstOptimizer::VisitSwitchStatement(SwitchStatement* node) {
+ node->tag()->set_no_negative_zero(true);
Visit(node->tag());
for (int i = 0; i < node->cases()->length(); i++) {
CaseClause* clause = node->cases()->at(i);
@@ -444,6 +450,7 @@ void AstOptimizer::VisitUnaryOperation(UnaryOperation* node) {
if (FLAG_safe_int32_compiler) {
switch (node->op()) {
case Token::BIT_NOT:
+ node->expression()->set_no_negative_zero(true);
node->expression()->set_to_int32(true);
// Fall through.
case Token::ADD:
@@ -476,10 +483,49 @@ void AstOptimizer::VisitCountOperation(CountOperation* node) {
}
+static bool CouldBeNegativeZero(AstNode* node) {
+ Literal* literal = node->AsLiteral();
+ if (literal != NULL) {
+ Handle<Object> handle = literal->handle();
+ if (handle->IsString() || handle->IsSmi()) {
+ return false;
+ } else if (handle->IsHeapNumber()) {
+ double double_value = HeapNumber::cast(*handle)->value();
+ if (double_value != 0) {
+ return false;
+ }
+ }
+ }
+ BinaryOperation* binary = node->AsBinaryOperation();
+ if (binary != NULL && Token::IsBitOp(binary->op())) {
+ return false;
+ }
+ return true;
+}
+
+
+static bool CouldBePositiveZero(AstNode* node) {
+ Literal* literal = node->AsLiteral();
+ if (literal != NULL) {
+ Handle<Object> handle = literal->handle();
+ if (handle->IsSmi()) {
+ if (Smi::cast(*handle) != Smi::FromInt(0)) {
+ return false;
+ }
+ } else if (handle->IsHeapNumber()) {
+ // Heap number literal can't be +0, because that's a Smi.
+ return false;
+ }
+ }
+ return true;
+}
+
+
void AstOptimizer::VisitBinaryOperation(BinaryOperation* node) {
// Depending on the operation we can propagate this node's type down the
// AST nodes.
- switch (node->op()) {
+ Token::Value op = node->op();
+ switch (op) {
case Token::COMMA:
case Token::OR:
node->left()->set_no_negative_zero(true);
@@ -503,23 +549,54 @@ void AstOptimizer::VisitBinaryOperation(BinaryOperation* node) {
node->left()->set_no_negative_zero(true);
node->right()->set_no_negative_zero(true);
break;
+ case Token::MUL: {
+ VariableProxy* lvar_proxy = node->left()->AsVariableProxy();
+ VariableProxy* rvar_proxy = node->right()->AsVariableProxy();
+ if (lvar_proxy != NULL && rvar_proxy != NULL) {
+ Variable* lvar = lvar_proxy->AsVariable();
+ Variable* rvar = rvar_proxy->AsVariable();
+ if (lvar != NULL && rvar != NULL) {
+ if (lvar->mode() == Variable::VAR && rvar->mode() == Variable::VAR) {
+ Slot* lslot = lvar->slot();
+ Slot* rslot = rvar->slot();
+ if (lslot->type() == rslot->type() &&
+ (lslot->type() == Slot::PARAMETER ||
+ lslot->type() == Slot::LOCAL) &&
+ lslot->index() == rslot->index()) {
+ // A number squared doesn't give negative zero.
+ node->set_no_negative_zero(true);
+ }
+ }
+ }
+ }
+ }
case Token::ADD:
case Token::SUB:
- case Token::MUL:
case Token::DIV:
- case Token::MOD:
+ case Token::MOD: {
if (node->type()->IsLikelySmi()) {
node->left()->type()->SetAsLikelySmiIfUnknown();
node->right()->type()->SetAsLikelySmiIfUnknown();
}
- node->left()->set_no_negative_zero(node->no_negative_zero());
- node->right()->set_no_negative_zero(node->no_negative_zero());
+ if (op == Token::ADD && (!CouldBeNegativeZero(node->left()) ||
+ !CouldBeNegativeZero(node->right()))) {
+ node->left()->set_no_negative_zero(true);
+ node->right()->set_no_negative_zero(true);
+ } else if (op == Token::SUB && (!CouldBeNegativeZero(node->left()) ||
+ !CouldBePositiveZero(node->right()))) {
+ node->left()->set_no_negative_zero(true);
+ node->right()->set_no_negative_zero(true);
+ } else {
+ node->left()->set_no_negative_zero(node->no_negative_zero());
+ node->right()->set_no_negative_zero(node->no_negative_zero());
+ }
if (node->op() == Token::DIV) {
node->right()->set_no_negative_zero(false);
} else if (node->op() == Token::MOD) {
node->right()->set_no_negative_zero(true);
}
break;
+ }
default:
UNREACHABLE();
break;
@@ -530,7 +607,7 @@ void AstOptimizer::VisitBinaryOperation(BinaryOperation* node) {
// After visiting the operand nodes we have to check if this node's type
// can be updated. If it does, then we can push that information down
- // towards the leafs again if the new information is an upgrade over the
+ // towards the leaves again if the new information is an upgrade over the
// previous type of the operand nodes.
if (node->type()->IsUnknown()) {
if (node->left()->type()->IsLikelySmi() ||
@@ -590,7 +667,7 @@ void AstOptimizer::VisitBinaryOperation(BinaryOperation* node) {
void AstOptimizer::VisitCompareOperation(CompareOperation* node) {
if (node->type()->IsKnown()) {
- // Propagate useful information down towards the leafs.
+ // Propagate useful information down towards the leaves.
node->left()->type()->SetAsLikelySmiIfUnknown();
node->right()->type()->SetAsLikelySmiIfUnknown();
}
@@ -604,7 +681,7 @@ void AstOptimizer::VisitCompareOperation(CompareOperation* node) {
// After visiting the operand nodes we have to check if this node's type
// can be updated. If it does, then we can push that information down
- // towards the leafs again if the new information is an upgrade over the
+ // towards the leaves again if the new information is an upgrade over the
// previous type of the operand nodes.
if (node->type()->IsUnknown()) {
if (node->left()->type()->IsLikelySmi() ||
diff --git a/deps/v8/src/runtime.cc b/deps/v8/src/runtime.cc
index 22e80b33e0..4a0fe7ae22 100644
--- a/deps/v8/src/runtime.cc
+++ b/deps/v8/src/runtime.cc
@@ -678,6 +678,12 @@ static Object* Runtime_GetOwnProperty(Arguments args) {
}
+static Object* Runtime_PreventExtensions(Arguments args) {
+ ASSERT(args.length() == 1);
+ CONVERT_CHECKED(JSObject, obj, args[0]);
+ return obj->PreventExtensions();
+}
+
static Object* Runtime_IsExtensible(Arguments args) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(JSObject, obj, args[0]);
@@ -2279,6 +2285,134 @@ static Object* StringReplaceRegExpWithString(String* subject,
}
+template <typename ResultSeqString>
+static Object* StringReplaceRegExpWithEmptyString(String* subject,
+ JSRegExp* regexp,
+ JSArray* last_match_info) {
+ ASSERT(subject->IsFlat());
+
+ HandleScope handles;
+
+ Handle<String> subject_handle(subject);
+ Handle<JSRegExp> regexp_handle(regexp);
+ Handle<JSArray> last_match_info_handle(last_match_info);
+ Handle<Object> match = RegExpImpl::Exec(regexp_handle,
+ subject_handle,
+ 0,
+ last_match_info_handle);
+ if (match.is_null()) return Failure::Exception();
+ if (match->IsNull()) return *subject_handle;
+
+ ASSERT(last_match_info_handle->HasFastElements());
+
+ HandleScope loop_scope;
+ int start, end;
+ {
+ AssertNoAllocation match_info_array_is_not_in_a_handle;
+ FixedArray* match_info_array =
+ FixedArray::cast(last_match_info_handle->elements());
+
+ start = RegExpImpl::GetCapture(match_info_array, 0);
+ end = RegExpImpl::GetCapture(match_info_array, 1);
+ }
+
+ int length = subject->length();
+ int new_length = length - (end - start);
+ if (new_length == 0) {
+ return Heap::empty_string();
+ }
+ Handle<ResultSeqString> answer;
+ if (ResultSeqString::kHasAsciiEncoding) {
+ answer =
+ Handle<ResultSeqString>::cast(Factory::NewRawAsciiString(new_length));
+ } else {
+ answer =
+ Handle<ResultSeqString>::cast(Factory::NewRawTwoByteString(new_length));
+ }
+
+ // If the regexp isn't global, only match once.
+ if (!regexp_handle->GetFlags().is_global()) {
+ if (start > 0) {
+ String::WriteToFlat(*subject_handle,
+ answer->GetChars(),
+ 0,
+ start);
+ }
+ if (end < length) {
+ String::WriteToFlat(*subject_handle,
+ answer->GetChars() + start,
+ end,
+ length);
+ }
+ return *answer;
+ }
+
+ int prev = 0; // Index of end of last match.
+ int next = 0; // Start of next search (prev unless last match was empty).
+ int position = 0;
+
+ do {
+ if (prev < start) {
+ // Add substring subject[prev;start] to answer string.
+ String::WriteToFlat(*subject_handle,
+ answer->GetChars() + position,
+ prev,
+ start);
+ position += start - prev;
+ }
+ prev = end;
+ next = end;
+ // Continue from where the match ended, unless it was an empty match.
+ if (start == end) {
+ next++;
+ if (next > length) break;
+ }
+ match = RegExpImpl::Exec(regexp_handle,
+ subject_handle,
+ next,
+ last_match_info_handle);
+ if (match.is_null()) return Failure::Exception();
+ if (match->IsNull()) break;
+
+ ASSERT(last_match_info_handle->HasFastElements());
+ HandleScope loop_scope;
+ {
+ AssertNoAllocation match_info_array_is_not_in_a_handle;
+ FixedArray* match_info_array =
+ FixedArray::cast(last_match_info_handle->elements());
+ start = RegExpImpl::GetCapture(match_info_array, 0);
+ end = RegExpImpl::GetCapture(match_info_array, 1);
+ }
+ } while (true);
+
+ if (prev < length) {
+ // Add substring subject[prev;length] to answer string.
+ String::WriteToFlat(*subject_handle,
+ answer->GetChars() + position,
+ prev,
+ length);
+ position += length - prev;
+ }
+
+ if (position == 0) {
+ return Heap::empty_string();
+ }
+
+ // Shorten string and fill
+ int string_size = ResultSeqString::SizeFor(position);
+ int allocated_string_size = ResultSeqString::SizeFor(new_length);
+ int delta = allocated_string_size - string_size;
+
+ answer->set_length(position);
+ if (delta == 0) return *answer;
+
+ Address end_of_string = answer->address() + string_size;
+ Heap::CreateFillerObjectAt(end_of_string, delta);
+
+ return *answer;
+}
+
+
static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
ASSERT(args.length() == 4);
@@ -2305,6 +2439,16 @@ static Object* Runtime_StringReplaceRegExpWithString(Arguments args) {
ASSERT(last_match_info->HasFastElements());
+ if (replacement->length() == 0) {
+ if (subject->HasOnlyAsciiChars()) {
+ return StringReplaceRegExpWithEmptyString<SeqAsciiString>(
+ subject, regexp, last_match_info);
+ } else {
+ return StringReplaceRegExpWithEmptyString<SeqTwoByteString>(
+ subject, regexp, last_match_info);
+ }
+ }
+
return StringReplaceRegExpWithString(subject,
regexp,
replacement,
@@ -5362,9 +5506,6 @@ static Object* Runtime_NumberToInteger(Arguments args) {
}
-
-
-
static Object* Runtime_NumberToIntegerMapMinusZero(Arguments args) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
@@ -5434,7 +5575,7 @@ static Object* Runtime_NumberAdd(Arguments args) {
CONVERT_DOUBLE_CHECKED(x, args[0]);
CONVERT_DOUBLE_CHECKED(y, args[1]);
- return Heap::AllocateHeapNumber(x + y);
+ return Heap::NumberFromDouble(x + y);
}
@@ -5444,7 +5585,7 @@ static Object* Runtime_NumberSub(Arguments args) {
CONVERT_DOUBLE_CHECKED(x, args[0]);
CONVERT_DOUBLE_CHECKED(y, args[1]);
- return Heap::AllocateHeapNumber(x - y);
+ return Heap::NumberFromDouble(x - y);
}
@@ -5454,7 +5595,7 @@ static Object* Runtime_NumberMul(Arguments args) {
CONVERT_DOUBLE_CHECKED(x, args[0]);
CONVERT_DOUBLE_CHECKED(y, args[1]);
- return Heap::AllocateHeapNumber(x * y);
+ return Heap::NumberFromDouble(x * y);
}
@@ -5463,7 +5604,7 @@ static Object* Runtime_NumberUnaryMinus(Arguments args) {
ASSERT(args.length() == 1);
CONVERT_DOUBLE_CHECKED(x, args[0]);
- return Heap::AllocateHeapNumber(-x);
+ return Heap::NumberFromDouble(-x);
}
@@ -6065,7 +6206,7 @@ static Object* Runtime_Math_pow(Arguments args) {
// custom powi() function than the generic pow().
if (args[1]->IsSmi()) {
int y = Smi::cast(args[1])->value();
- return Heap::AllocateHeapNumber(powi(x, y));
+ return Heap::NumberFromDouble(powi(x, y));
}
CONVERT_DOUBLE_CHECKED(y, args[1]);
diff --git a/deps/v8/src/runtime.h b/deps/v8/src/runtime.h
index 3d4df1bd36..5719fc8955 100644
--- a/deps/v8/src/runtime.h
+++ b/deps/v8/src/runtime.h
@@ -72,6 +72,7 @@ namespace internal {
F(GetOwnProperty, 2, 1) \
\
F(IsExtensible, 1, 1) \
+ F(PreventExtensions, 1, 1)\
\
/* Utilities */ \
F(GetFunctionDelegate, 1, 1) \
diff --git a/deps/v8/src/spaces.cc b/deps/v8/src/spaces.cc
index 3c495ba54e..ca4b66ac72 100644
--- a/deps/v8/src/spaces.cc
+++ b/deps/v8/src/spaces.cc
@@ -2305,8 +2305,8 @@ void PagedSpace::CollectCodeStatistics() {
}
ASSERT(code->instruction_start() <= prev_pc &&
- prev_pc <= code->relocation_start());
- delta += static_cast<int>(code->relocation_start() - prev_pc);
+ prev_pc <= code->instruction_end());
+ delta += static_cast<int>(code->instruction_end() - prev_pc);
EnterComment("NoComment", delta);
}
}
diff --git a/deps/v8/src/stub-cache.cc b/deps/v8/src/stub-cache.cc
index ffa92dd366..a654a08624 100644
--- a/deps/v8/src/stub-cache.cc
+++ b/deps/v8/src/stub-cache.cc
@@ -94,6 +94,7 @@ Code* StubCache::Set(String* name, Map* map, Code* code) {
Object* StubCache::ComputeLoadNonexistent(String* name, JSObject* receiver) {
+ ASSERT(receiver->IsGlobalObject() || receiver->HasFastProperties());
// If no global objects are present in the prototype chain, the load
// nonexistent IC stub can be shared for all names for a given map
// and we use the empty string for the map cache in that case. If
@@ -129,14 +130,16 @@ Object* StubCache::ComputeLoadField(String* name,
JSObject* receiver,
JSObject* holder,
int field_index) {
+ ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP);
+ Map* map = receiver->map();
Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, FIELD);
- Object* code = receiver->map()->FindInCodeCache(name, flags);
+ Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
LoadStubCompiler compiler;
code = compiler.CompileLoadField(receiver, holder, field_index, name);
if (code->IsFailure()) return code;
PROFILE(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
- Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
+ Object* result = map->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
return code;
@@ -148,14 +151,16 @@ Object* StubCache::ComputeLoadCallback(String* name,
JSObject* holder,
AccessorInfo* callback) {
ASSERT(v8::ToCData<Address>(callback->getter()) != 0);
+ ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP);
+ Map* map = receiver->map();
Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, CALLBACKS);
- Object* code = receiver->map()->FindInCodeCache(name, flags);
+ Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
LoadStubCompiler compiler;
code = compiler.CompileLoadCallback(name, receiver, holder, callback);
if (code->IsFailure()) return code;
PROFILE(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
- Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
+ Object* result = map->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
return code;
@@ -166,15 +171,17 @@ Object* StubCache::ComputeLoadConstant(String* name,
JSObject* receiver,
JSObject* holder,
Object* value) {
+ ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP);
+ Map* map = receiver->map();
Code::Flags flags =
Code::ComputeMonomorphicFlags(Code::LOAD_IC, CONSTANT_FUNCTION);
- Object* code = receiver->map()->FindInCodeCache(name, flags);
+ Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
LoadStubCompiler compiler;
code = compiler.CompileLoadConstant(receiver, holder, value, name);
if (code->IsFailure()) return code;
PROFILE(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
- Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
+ Object* result = map->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
return code;
@@ -184,21 +191,23 @@ Object* StubCache::ComputeLoadConstant(String* name,
Object* StubCache::ComputeLoadInterceptor(String* name,
JSObject* receiver,
JSObject* holder) {
+ ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP);
+ Map* map = receiver->map();
Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, INTERCEPTOR);
- Object* code = receiver->map()->FindInCodeCache(name, flags);
+ Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
LoadStubCompiler compiler;
code = compiler.CompileLoadInterceptor(receiver, holder, name);
if (code->IsFailure()) return code;
PROFILE(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
- Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
+ Object* result = map->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
return code;
}
-Object* StubCache::ComputeLoadNormal(String* name, JSObject* receiver) {
+Object* StubCache::ComputeLoadNormal() {
return Builtins::builtin(Builtins::LoadIC_Normal);
}
@@ -208,8 +217,10 @@ Object* StubCache::ComputeLoadGlobal(String* name,
GlobalObject* holder,
JSGlobalPropertyCell* cell,
bool is_dont_delete) {
+ ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP);
+ Map* map = receiver->map();
Code::Flags flags = Code::ComputeMonomorphicFlags(Code::LOAD_IC, NORMAL);
- Object* code = receiver->map()->FindInCodeCache(name, flags);
+ Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
LoadStubCompiler compiler;
code = compiler.CompileLoadGlobal(receiver,
@@ -219,7 +230,7 @@ Object* StubCache::ComputeLoadGlobal(String* name,
is_dont_delete);
if (code->IsFailure()) return code;
PROFILE(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
- Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
+ Object* result = map->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
return code;
@@ -230,14 +241,16 @@ Object* StubCache::ComputeKeyedLoadField(String* name,
JSObject* receiver,
JSObject* holder,
int field_index) {
+ ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP);
+ Map* map = receiver->map();
Code::Flags flags = Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, FIELD);
- Object* code = receiver->map()->FindInCodeCache(name, flags);
+ Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
KeyedLoadStubCompiler compiler;
code = compiler.CompileLoadField(name, receiver, holder, field_index);
if (code->IsFailure()) return code;
PROFILE(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name));
- Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
+ Object* result = map->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
return code;
@@ -248,15 +261,17 @@ Object* StubCache::ComputeKeyedLoadConstant(String* name,
JSObject* receiver,
JSObject* holder,
Object* value) {
+ ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP);
+ Map* map = receiver->map();
Code::Flags flags =
Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CONSTANT_FUNCTION);
- Object* code = receiver->map()->FindInCodeCache(name, flags);
+ Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
KeyedLoadStubCompiler compiler;
code = compiler.CompileLoadConstant(name, receiver, holder, value);
if (code->IsFailure()) return code;
PROFILE(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name));
- Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
+ Object* result = map->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
return code;
@@ -266,15 +281,17 @@ Object* StubCache::ComputeKeyedLoadConstant(String* name,
Object* StubCache::ComputeKeyedLoadInterceptor(String* name,
JSObject* receiver,
JSObject* holder) {
+ ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP);
+ Map* map = receiver->map();
Code::Flags flags =
Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, INTERCEPTOR);
- Object* code = receiver->map()->FindInCodeCache(name, flags);
+ Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
KeyedLoadStubCompiler compiler;
code = compiler.CompileLoadInterceptor(receiver, holder, name);
if (code->IsFailure()) return code;
PROFILE(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name));
- Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
+ Object* result = map->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
return code;
@@ -285,15 +302,17 @@ Object* StubCache::ComputeKeyedLoadCallback(String* name,
JSObject* receiver,
JSObject* holder,
AccessorInfo* callback) {
+ ASSERT(IC::GetCodeCacheForObject(receiver, holder) == OWN_MAP);
+ Map* map = receiver->map();
Code::Flags flags =
Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS);
- Object* code = receiver->map()->FindInCodeCache(name, flags);
+ Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
KeyedLoadStubCompiler compiler;
code = compiler.CompileLoadCallback(name, receiver, holder, callback);
if (code->IsFailure()) return code;
PROFILE(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name));
- Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
+ Object* result = map->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
return code;
@@ -305,13 +324,15 @@ Object* StubCache::ComputeKeyedLoadArrayLength(String* name,
JSArray* receiver) {
Code::Flags flags =
Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS);
- Object* code = receiver->map()->FindInCodeCache(name, flags);
+ ASSERT(receiver->IsJSObject());
+ Map* map = receiver->map();
+ Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
KeyedLoadStubCompiler compiler;
code = compiler.CompileLoadArrayLength(name);
if (code->IsFailure()) return code;
PROFILE(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name));
- Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
+ Object* result = map->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
return code;
@@ -322,13 +343,14 @@ Object* StubCache::ComputeKeyedLoadStringLength(String* name,
String* receiver) {
Code::Flags flags =
Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS);
- Object* code = receiver->map()->FindInCodeCache(name, flags);
+ Map* map = receiver->map();
+ Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
KeyedLoadStubCompiler compiler;
code = compiler.CompileLoadStringLength(name);
if (code->IsFailure()) return code;
PROFILE(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name));
- Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
+ Object* result = map->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
return code;
@@ -339,13 +361,14 @@ Object* StubCache::ComputeKeyedLoadFunctionPrototype(String* name,
JSFunction* receiver) {
Code::Flags flags =
Code::ComputeMonomorphicFlags(Code::KEYED_LOAD_IC, CALLBACKS);
- Object* code = receiver->map()->FindInCodeCache(name, flags);
+ Map* map = receiver->map();
+ Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
KeyedLoadStubCompiler compiler;
code = compiler.CompileLoadFunctionPrototype(name);
if (code->IsFailure()) return code;
PROFILE(CodeCreateEvent(Logger::KEYED_LOAD_IC_TAG, Code::cast(code), name));
- Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
+ Object* result = map->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
return code;
@@ -371,6 +394,11 @@ Object* StubCache::ComputeStoreField(String* name,
}
+Object* StubCache::ComputeStoreNormal() {
+ return Builtins::builtin(Builtins::StoreIC_Normal);
+}
+
+
Object* StubCache::ComputeStoreGlobal(String* name,
GlobalObject* receiver,
JSGlobalPropertyCell* cell) {
@@ -380,7 +408,7 @@ Object* StubCache::ComputeStoreGlobal(String* name,
StoreStubCompiler compiler;
code = compiler.CompileStoreGlobal(receiver, cell, name);
if (code->IsFailure()) return code;
- PROFILE(CodeCreateEvent(Logger::LOAD_IC_TAG, Code::cast(code), name));
+ PROFILE(CodeCreateEvent(Logger::STORE_IC_TAG, Code::cast(code), name));
Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
@@ -451,7 +479,9 @@ Object* StubCache::ComputeCallConstant(int argc,
JSObject* holder,
JSFunction* function) {
// Compute the check type and the map.
- Map* map = IC::GetCodeCacheMapForObject(object);
+ InlineCacheHolderFlag cache_holder =
+ IC::GetCodeCacheForObject(object, holder);
+ Map* map = IC::GetCodeCacheMap(object, cache_holder);
// Compute check type based on receiver/holder.
StubCompiler::CheckType check = StubCompiler::RECEIVER_MAP_CHECK;
@@ -466,6 +496,7 @@ Object* StubCache::ComputeCallConstant(int argc,
Code::Flags flags =
Code::ComputeMonomorphicFlags(kind,
CONSTANT_FUNCTION,
+ cache_holder,
in_loop,
argc);
Object* code = map->FindInCodeCache(name, flags);
@@ -476,7 +507,7 @@ Object* StubCache::ComputeCallConstant(int argc,
// caches.
if (!function->is_compiled()) return Failure::InternalError();
// Compile the stub - only create stubs for fully compiled functions.
- CallStubCompiler compiler(argc, in_loop, kind);
+ CallStubCompiler compiler(argc, in_loop, kind, cache_holder);
code = compiler.CompileCallConstant(object, holder, function, name, check);
if (code->IsFailure()) return code;
ASSERT_EQ(flags, Code::cast(code)->flags());
@@ -497,7 +528,9 @@ Object* StubCache::ComputeCallField(int argc,
JSObject* holder,
int index) {
// Compute the check type and the map.
- Map* map = IC::GetCodeCacheMapForObject(object);
+ InlineCacheHolderFlag cache_holder =
+ IC::GetCodeCacheForObject(object, holder);
+ Map* map = IC::GetCodeCacheMap(object, cache_holder);
// TODO(1233596): We cannot do receiver map check for non-JS objects
// because they may be represented as immediates without a
@@ -508,11 +541,12 @@ Object* StubCache::ComputeCallField(int argc,
Code::Flags flags = Code::ComputeMonomorphicFlags(kind,
FIELD,
+ cache_holder,
in_loop,
argc);
Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
- CallStubCompiler compiler(argc, in_loop, kind);
+ CallStubCompiler compiler(argc, in_loop, kind, cache_holder);
code = compiler.CompileCallField(JSObject::cast(object),
holder,
index,
@@ -534,8 +568,9 @@ Object* StubCache::ComputeCallInterceptor(int argc,
Object* object,
JSObject* holder) {
// Compute the check type and the map.
- // If the object is a value, we use the prototype map for the cache.
- Map* map = IC::GetCodeCacheMapForObject(object);
+ InlineCacheHolderFlag cache_holder =
+ IC::GetCodeCacheForObject(object, holder);
+ Map* map = IC::GetCodeCacheMap(object, cache_holder);
// TODO(1233596): We cannot do receiver map check for non-JS objects
// because they may be represented as immediates without a
@@ -547,11 +582,12 @@ Object* StubCache::ComputeCallInterceptor(int argc,
Code::Flags flags =
Code::ComputeMonomorphicFlags(kind,
INTERCEPTOR,
+ cache_holder,
NOT_IN_LOOP,
argc);
Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
- CallStubCompiler compiler(argc, NOT_IN_LOOP, kind);
+ CallStubCompiler compiler(argc, NOT_IN_LOOP, kind, cache_holder);
code = compiler.CompileCallInterceptor(JSObject::cast(object),
holder,
name);
@@ -585,25 +621,29 @@ Object* StubCache::ComputeCallGlobal(int argc,
GlobalObject* holder,
JSGlobalPropertyCell* cell,
JSFunction* function) {
+ InlineCacheHolderFlag cache_holder =
+ IC::GetCodeCacheForObject(receiver, holder);
+ Map* map = IC::GetCodeCacheMap(receiver, cache_holder);
Code::Flags flags =
Code::ComputeMonomorphicFlags(kind,
NORMAL,
+ cache_holder,
in_loop,
argc);
- Object* code = receiver->map()->FindInCodeCache(name, flags);
+ Object* code = map->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
// If the function hasn't been compiled yet, we cannot do it now
// because it may cause GC. To avoid this issue, we return an
// internal error which will make sure we do not update any
// caches.
if (!function->is_compiled()) return Failure::InternalError();
- CallStubCompiler compiler(argc, in_loop, kind);
+ CallStubCompiler compiler(argc, in_loop, kind, cache_holder);
code = compiler.CompileCallGlobal(receiver, holder, cell, function, name);
if (code->IsFailure()) return code;
ASSERT_EQ(flags, Code::cast(code)->flags());
PROFILE(CodeCreateEvent(CALL_LOGGER_TAG(kind, CALL_IC_TAG),
Code::cast(code), name));
- Object* result = receiver->map()->UpdateCodeCache(name, Code::cast(code));
+ Object* result = map->UpdateCodeCache(name, Code::cast(code));
if (result->IsFailure()) return result;
}
return code;
@@ -1203,6 +1243,17 @@ Object* KeyedStoreStubCompiler::GetCode(PropertyType type, String* name) {
}
+CallStubCompiler::CallStubCompiler(int argc,
+ InLoopFlag in_loop,
+ Code::Kind kind,
+ InlineCacheHolderFlag cache_holder)
+ : arguments_(argc)
+ , in_loop_(in_loop)
+ , kind_(kind)
+ , cache_holder_(cache_holder) {
+}
+
+
Object* CallStubCompiler::CompileCustomCall(int generator_id,
Object* object,
JSObject* holder,
@@ -1230,6 +1281,7 @@ Object* CallStubCompiler::GetCode(PropertyType type, String* name) {
int argc = arguments_.immediate();
Code::Flags flags = Code::ComputeMonomorphicFlags(kind_,
type,
+ cache_holder_,
in_loop_,
argc);
return GetCodeWithFlags(flags, name);
diff --git a/deps/v8/src/stub-cache.h b/deps/v8/src/stub-cache.h
index fcfffcfc5d..856904a4a3 100644
--- a/deps/v8/src/stub-cache.h
+++ b/deps/v8/src/stub-cache.h
@@ -77,7 +77,7 @@ class StubCache : public AllStatic {
JSObject* receiver,
JSObject* holder);
- static Object* ComputeLoadNormal(String* name, JSObject* receiver);
+ static Object* ComputeLoadNormal();
static Object* ComputeLoadGlobal(String* name,
@@ -121,6 +121,8 @@ class StubCache : public AllStatic {
int field_index,
Map* transition = NULL);
+ static Object* ComputeStoreNormal();
+
static Object* ComputeStoreGlobal(String* name,
GlobalObject* receiver,
JSGlobalPropertyCell* cell);
@@ -407,8 +409,21 @@ class StubCompiler BASE_EMBEDDED {
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.
+ // Generates code that verifies that the property holder has not changed
+ // (checking maps of objects in the prototype chain for fast and global
+ // objects or doing negative lookup for slow objects, ensures that the
+ // property cells for global objects are still empty) and checks that the map
+ // of the holder has not changed. If necessary the function also generates
+ // code for security check in case of global object holders. Helps to make
+ // sure that the current IC is still valid.
+ //
+ // The scratch and holder registers are always clobbered, but the object
+ // register is only 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 CheckPrototypes(JSObject* object,
Register object_reg,
@@ -416,9 +431,10 @@ class StubCompiler BASE_EMBEDDED {
Register holder_reg,
Register scratch,
String* name,
- Label* miss) {
+ Label* miss,
+ Register extra = no_reg) {
return CheckPrototypes(object, object_reg, holder, holder_reg, scratch,
- name, kInvalidProtoDepth, miss);
+ name, kInvalidProtoDepth, miss, extra);
}
Register CheckPrototypes(JSObject* object,
@@ -428,7 +444,8 @@ class StubCompiler BASE_EMBEDDED {
Register scratch,
String* name,
int save_at_depth,
- Label* miss);
+ Label* miss,
+ Register extra = no_reg);
protected:
Object* GetCodeWithFlags(Code::Flags flags, const char* name);
@@ -611,8 +628,10 @@ class CallStubCompiler: public StubCompiler {
kNumCallGenerators
};
- CallStubCompiler(int argc, InLoopFlag in_loop, Code::Kind kind)
- : arguments_(argc), in_loop_(in_loop), kind_(kind) { }
+ CallStubCompiler(int argc,
+ InLoopFlag in_loop,
+ Code::Kind kind,
+ InlineCacheHolderFlag cache_holder);
Object* CompileCallField(JSObject* object,
JSObject* holder,
@@ -653,6 +672,7 @@ class CallStubCompiler: public StubCompiler {
const ParameterCount arguments_;
const InLoopFlag in_loop_;
const Code::Kind kind_;
+ const InlineCacheHolderFlag cache_holder_;
const ParameterCount& arguments() { return arguments_; }
diff --git a/deps/v8/src/v8-counters.h b/deps/v8/src/v8-counters.h
index 93fecd1fe8..509de3d004 100644
--- a/deps/v8/src/v8-counters.h
+++ b/deps/v8/src/v8-counters.h
@@ -153,6 +153,8 @@ namespace internal {
SC(keyed_store_inline_miss, V8.KeyedStoreInlineMiss) \
SC(named_store_global_inline, V8.NamedStoreGlobalInline) \
SC(named_store_global_inline_miss, V8.NamedStoreGlobalInlineMiss) \
+ SC(store_normal_miss, V8.StoreNormalMiss) \
+ SC(store_normal_hit, V8.StoreNormalHit) \
SC(call_miss, V8.CallMiss) \
SC(keyed_call_miss, V8.KeyedCallMiss) \
SC(load_miss, V8.LoadMiss) \
@@ -166,6 +168,8 @@ namespace internal {
SC(constructed_objects, V8.ConstructedObjects) \
SC(constructed_objects_runtime, V8.ConstructedObjectsRuntime) \
SC(constructed_objects_stub, V8.ConstructedObjectsStub) \
+ SC(negative_lookups, V8.NegativeLookups) \
+ SC(negative_lookups_miss, V8.NegativeLookupsMiss) \
SC(array_function_runtime, V8.ArrayFunctionRuntime) \
SC(array_function_native, V8.ArrayFunctionNative) \
SC(for_in, V8.ForIn) \
diff --git a/deps/v8/src/v8dll-main.cc b/deps/v8/src/v8dll-main.cc
new file mode 100644
index 0000000000..3d4b3a379e
--- /dev/null
+++ b/deps/v8/src/v8dll-main.cc
@@ -0,0 +1,39 @@
+// 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 <windows.h>
+
+#include "../include/v8.h"
+
+extern "C" {
+BOOL WINAPI DllMain(HANDLE hinstDLL,
+ DWORD dwReason,
+ LPVOID lpvReserved) {
+ // Do nothing.
+ return TRUE;
+}
+}
diff --git a/deps/v8/src/v8natives.js b/deps/v8/src/v8natives.js
index 24d5e7cd36..487faabcd3 100644
--- a/deps/v8/src/v8natives.js
+++ b/deps/v8/src/v8natives.js
@@ -745,6 +745,27 @@ function ObjectDefineProperties(obj, properties) {
}
+// ES5 section 15.2.3.10
+function ObjectPreventExtension(obj) {
+ if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) &&
+ !IS_UNDETECTABLE(obj)) {
+ throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]);
+ }
+ %PreventExtensions(obj);
+ return obj;
+}
+
+
+// ES5 section 15.2.3.13
+function ObjectIsExtensible(obj) {
+ if ((!IS_SPEC_OBJECT_OR_NULL(obj) || IS_NULL_OR_UNDEFINED(obj)) &&
+ !IS_UNDETECTABLE(obj)) {
+ throw MakeTypeError("obj_ctor_property_non_object", ["preventExtension"]);
+ }
+ return %IsExtensible(obj);
+}
+
+
%SetCode($Object, function(x) {
if (%_IsConstructCall()) {
if (x == null) return this;
@@ -780,7 +801,9 @@ function SetupObject() {
"defineProperties", ObjectDefineProperties,
"getPrototypeOf", ObjectGetPrototypeOf,
"getOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptor,
- "getOwnPropertyNames", ObjectGetOwnPropertyNames
+ "getOwnPropertyNames", ObjectGetOwnPropertyNames,
+ "isExtensible", ObjectIsExtensible,
+ "preventExtensions", ObjectPreventExtension
));
}
diff --git a/deps/v8/src/version.cc b/deps/v8/src/version.cc
index 68c2e7320b..d930c8dec2 100644
--- a/deps/v8/src/version.cc
+++ b/deps/v8/src/version.cc
@@ -34,7 +34,7 @@
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 2
#define MINOR_VERSION 2
-#define BUILD_NUMBER 21
+#define BUILD_NUMBER 23
#define PATCH_LEVEL 0
#define CANDIDATE_VERSION false
diff --git a/deps/v8/src/x64/builtins-x64.cc b/deps/v8/src/x64/builtins-x64.cc
index ff655c7665..a38ebafc02 100644
--- a/deps/v8/src/x64/builtins-x64.cc
+++ b/deps/v8/src/x64/builtins-x64.cc
@@ -1238,10 +1238,6 @@ static void Generate_JSEntryTrampolineHelper(MacroAssembler* masm,
__ movq(rbx, r8);
#endif // _WIN64
- // Set up the roots register.
- ExternalReference roots_address = ExternalReference::roots_address();
- __ movq(kRootRegister, roots_address);
-
// Current stack contents:
// [rsp + 2 * kPointerSize ... ]: Internal frame
// [rsp + kPointerSize] : function
diff --git a/deps/v8/src/x64/codegen-x64.cc b/deps/v8/src/x64/codegen-x64.cc
index a6d31be79f..7e04c20e2d 100644
--- a/deps/v8/src/x64/codegen-x64.cc
+++ b/deps/v8/src/x64/codegen-x64.cc
@@ -592,7 +592,6 @@ bool CodeGenerator::HasValidEntryRegisters() {
&& (allocator()->count(r9) == (frame()->is_used(r9) ? 1 : 0))
&& (allocator()->count(r11) == (frame()->is_used(r11) ? 1 : 0))
&& (allocator()->count(r14) == (frame()->is_used(r14) ? 1 : 0))
- && (allocator()->count(r15) == (frame()->is_used(r15) ? 1 : 0))
&& (allocator()->count(r12) == (frame()->is_used(r12) ? 1 : 0));
}
#endif
@@ -1600,11 +1599,133 @@ void CodeGenerator::SetTypeForStackSlot(Slot* slot, TypeInfo info) {
}
+void CodeGenerator::GenerateFastSmiLoop(ForStatement* node) {
+ // A fast smi loop is a for loop with an initializer
+ // that is a simple assignment of a smi to a stack variable,
+ // a test that is a simple test of that variable against a smi constant,
+ // and a step that is a increment/decrement of the variable, and
+ // where the variable isn't modified in the loop body.
+ // This guarantees that the variable is always a smi.
+
+ Variable* loop_var = node->loop_variable();
+ Smi* initial_value = *Handle<Smi>::cast(node->init()
+ ->StatementAsSimpleAssignment()->value()->AsLiteral()->handle());
+ Smi* limit_value = *Handle<Smi>::cast(
+ node->cond()->AsCompareOperation()->right()->AsLiteral()->handle());
+ Token::Value compare_op =
+ node->cond()->AsCompareOperation()->op();
+ bool increments =
+ node->next()->StatementAsCountOperation()->op() == Token::INC;
+
+ // Check that the condition isn't initially false.
+ bool initially_false = false;
+ int initial_int_value = initial_value->value();
+ int limit_int_value = limit_value->value();
+ switch (compare_op) {
+ case Token::LT:
+ initially_false = initial_int_value >= limit_int_value;
+ break;
+ case Token::LTE:
+ initially_false = initial_int_value > limit_int_value;
+ break;
+ case Token::GT:
+ initially_false = initial_int_value <= limit_int_value;
+ break;
+ case Token::GTE:
+ initially_false = initial_int_value < limit_int_value;
+ break;
+ default:
+ UNREACHABLE();
+ }
+ if (initially_false) return;
+
+ // Only check loop condition at the end.
+
+ Visit(node->init());
+
+ JumpTarget loop(JumpTarget::BIDIRECTIONAL);
+ // Set type and stack height of BreakTargets.
+ node->continue_target()->set_direction(JumpTarget::FORWARD_ONLY);
+ node->break_target()->set_direction(JumpTarget::FORWARD_ONLY);
+
+ IncrementLoopNesting();
+ loop.Bind();
+
+ // Set number type of the loop variable to smi.
+ CheckStack(); // TODO(1222600): ignore if body contains calls.
+
+ SetTypeForStackSlot(loop_var->slot(), TypeInfo::Smi());
+ Visit(node->body());
+
+ if (node->continue_target()->is_linked()) {
+ node->continue_target()->Bind();
+ }
+
+ if (has_valid_frame()) {
+ CodeForStatementPosition(node);
+ Slot* loop_var_slot = loop_var->slot();
+ if (loop_var_slot->type() == Slot::LOCAL) {
+ frame_->PushLocalAt(loop_var_slot->index());
+ } else {
+ ASSERT(loop_var_slot->type() == Slot::PARAMETER);
+ frame_->PushParameterAt(loop_var_slot->index());
+ }
+ Result loop_var_result = frame_->Pop();
+ if (!loop_var_result.is_register()) {
+ loop_var_result.ToRegister();
+ }
+
+ if (increments) {
+ __ SmiAddConstant(loop_var_result.reg(),
+ loop_var_result.reg(),
+ Smi::FromInt(1));
+ } else {
+ __ SmiSubConstant(loop_var_result.reg(),
+ loop_var_result.reg(),
+ Smi::FromInt(1));
+ }
+
+ {
+ __ SmiCompare(loop_var_result.reg(), limit_value);
+ Condition condition;
+ switch (compare_op) {
+ case Token::LT:
+ condition = less;
+ break;
+ case Token::LTE:
+ condition = less_equal;
+ break;
+ case Token::GT:
+ condition = greater;
+ break;
+ case Token::GTE:
+ condition = greater_equal;
+ break;
+ default:
+ condition = never;
+ UNREACHABLE();
+ }
+ loop.Branch(condition);
+ }
+ loop_var_result.Unuse();
+ }
+ if (node->break_target()->is_linked()) {
+ node->break_target()->Bind();
+ }
+ DecrementLoopNesting();
+}
+
+
void CodeGenerator::VisitForStatement(ForStatement* node) {
ASSERT(!in_spilled_code());
Comment cmnt(masm_, "[ ForStatement");
CodeForStatementPosition(node);
+ if (node->is_fast_smi_loop()) {
+ GenerateFastSmiLoop(node);
+ return;
+ }
+
// Compile the init expression if present.
if (node->init() != NULL) {
Visit(node->init());
@@ -1694,16 +1815,6 @@ void CodeGenerator::VisitForStatement(ForStatement* node) {
CheckStack(); // TODO(1222600): ignore if body contains calls.
- // We know that the loop index is a smi if it is not modified in the
- // loop body and it is checked against a constant limit in the loop
- // condition. In this case, we reset the static type information of the
- // loop index to smi before compiling the body, the update expression, and
- // the bottom check of the loop condition.
- if (node->is_fast_smi_loop()) {
- // Set number type of the loop variable to smi.
- SetTypeForStackSlot(node->loop_variable()->slot(), TypeInfo::Smi());
- }
-
Visit(node->body());
// If there is an update expression, compile it if necessary.
@@ -1723,13 +1834,6 @@ void CodeGenerator::VisitForStatement(ForStatement* node) {
}
}
- // Set the type of the loop variable to smi before compiling the test
- // expression if we are in a fast smi loop condition.
- if (node->is_fast_smi_loop() && has_valid_frame()) {
- // Set number type of the loop variable to smi.
- SetTypeForStackSlot(node->loop_variable()->slot(), TypeInfo::Smi());
- }
-
// Based on the condition analysis, compile the backward jump as
// necessary.
switch (info) {
@@ -3269,9 +3373,12 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
}
} else {
- bool overwrite =
+ bool can_overwrite =
(node->expression()->AsBinaryOperation() != NULL &&
node->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
+ UnaryOverwriteMode overwrite =
+ can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
+ bool no_negative_zero = node->expression()->no_negative_zero();
Load(node->expression());
switch (op) {
case Token::NOT:
@@ -3281,7 +3388,10 @@ void CodeGenerator::VisitUnaryOperation(UnaryOperation* node) {
break;
case Token::SUB: {
- GenericUnaryOpStub stub(Token::SUB, overwrite);
+ GenericUnaryOpStub stub(
+ Token::SUB,
+ overwrite,
+ no_negative_zero ? kIgnoreNegativeZero : kStrictNegativeZero);
Result operand = frame_->Pop();
Result answer = frame_->CallStub(&stub, &operand);
answer.set_type_info(TypeInfo::Number());
@@ -3501,17 +3611,16 @@ void CodeGenerator::VisitCountOperation(CountOperation* node) {
__ JumpIfNotSmi(new_value.reg(), deferred->entry_label());
}
if (is_increment) {
- __ SmiAddConstant(kScratchRegister,
+ __ SmiAddConstant(new_value.reg(),
new_value.reg(),
Smi::FromInt(1),
deferred->entry_label());
} else {
- __ SmiSubConstant(kScratchRegister,
+ __ SmiSubConstant(new_value.reg(),
new_value.reg(),
Smi::FromInt(1),
deferred->entry_label());
}
- __ movq(new_value.reg(), kScratchRegister);
deferred->BindExit();
// Postfix count operations return their input converted to
@@ -8403,6 +8512,11 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
Label try_float;
__ JumpIfNotSmi(rax, &try_float);
+ if (negative_zero_ == kIgnoreNegativeZero) {
+ __ SmiCompare(rax, Smi::FromInt(0));
+ __ j(equal, &done);
+ }
+
// Enter runtime system if the value of the smi is zero
// to make sure that we switch between 0 and -0.
// Also enter it if the value of the smi is Smi::kMinValue.
@@ -8410,10 +8524,14 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
// Either zero or Smi::kMinValue, neither of which become a smi when
// negated.
- __ SmiCompare(rax, Smi::FromInt(0));
- __ j(not_equal, &slow);
- __ Move(rax, Factory::minus_zero_value());
- __ jmp(&done);
+ if (negative_zero_ == kStrictNegativeZero) {
+ __ SmiCompare(rax, Smi::FromInt(0));
+ __ j(not_equal, &slow);
+ __ Move(rax, Factory::minus_zero_value());
+ __ jmp(&done);
+ } else {
+ __ jmp(&slow);
+ }
// Try floating point case.
__ bind(&try_float);
@@ -8426,7 +8544,7 @@ void GenericUnaryOpStub::Generate(MacroAssembler* masm) {
__ shl(kScratchRegister, Immediate(63));
__ xor_(rdx, kScratchRegister); // Flip sign.
// rdx is value to store.
- if (overwrite_) {
+ if (overwrite_ == UNARY_OVERWRITE) {
__ movq(FieldOperand(rax, HeapNumber::kValueOffset), rdx);
} else {
__ AllocateHeapNumber(rcx, rbx, &slow);
@@ -8622,26 +8740,26 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ bind(&seq_ascii_string);
// rax: subject string (sequential ascii)
// rcx: RegExp data (FixedArray)
- __ movq(r12, FieldOperand(rcx, JSRegExp::kDataAsciiCodeOffset));
+ __ movq(r11, FieldOperand(rcx, JSRegExp::kDataAsciiCodeOffset));
__ Set(rdi, 1); // Type is ascii.
__ jmp(&check_code);
__ bind(&seq_two_byte_string);
// rax: subject string (flat two-byte)
// rcx: RegExp data (FixedArray)
- __ movq(r12, FieldOperand(rcx, JSRegExp::kDataUC16CodeOffset));
+ __ movq(r11, FieldOperand(rcx, JSRegExp::kDataUC16CodeOffset));
__ Set(rdi, 0); // Type is two byte.
__ bind(&check_code);
// Check that the irregexp code has been generated for the actual string
// encoding. If it has, the field contains a code object otherwise it contains
// the hole.
- __ CmpObjectType(r12, CODE_TYPE, kScratchRegister);
+ __ CmpObjectType(r11, CODE_TYPE, kScratchRegister);
__ j(not_equal, &runtime);
// rax: subject string
// rdi: encoding of subject string (1 if ascii, 0 if two_byte);
- // r12: code
+ // r11: code
// Load used arguments before starting to push arguments for call to native
// RegExp code to avoid handling changing stack height.
__ SmiToInteger64(rbx, Operand(rsp, kPreviousIndexOffset));
@@ -8649,7 +8767,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// rax: subject string
// rbx: previous index
// rdi: encoding of subject string (1 if ascii 0 if two_byte);
- // r12: code
+ // r11: code
// All checks done. Now push arguments for native regexp code.
__ IncrementCounter(&Counters::regexp_entry_native, 1);
@@ -8699,7 +8817,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
// rax: subject string
// rbx: previous index
// rdi: encoding of subject string (1 if ascii 0 if two_byte);
- // r12: code
+ // r11: code
// Argument 4: End of string data
// Argument 3: Start of string data
@@ -8723,8 +8841,8 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
__ movq(arg1, rax);
// Locate the code entry and call it.
- __ addq(r12, Immediate(Code::kHeaderSize - kHeapObjectTag));
- __ CallCFunction(r12, kRegExpExecuteArguments);
+ __ addq(r11, Immediate(Code::kHeaderSize - kHeapObjectTag));
+ __ CallCFunction(r11, kRegExpExecuteArguments);
// rsi is caller save, as it is used to pass parameter.
__ pop(rsi);
@@ -8938,7 +9056,7 @@ static int NegativeComparisonResult(Condition cc) {
void CompareStub::Generate(MacroAssembler* masm) {
- Label call_builtin, done;
+ Label check_unequal_objects, done;
// The compare stub returns a positive, negative, or zero 64-bit integer
// value in rax, corresponding to result of comparing the two inputs.
// NOTICE! This code is only reached after a smi-fast-case check, so
@@ -8975,14 +9093,14 @@ void CompareStub::Generate(MacroAssembler* masm) {
// If it's not a heap number, then return equal for (in)equality operator.
__ Cmp(FieldOperand(rdx, HeapObject::kMapOffset),
Factory::heap_number_map());
- if (cc_ == equal) {
- __ j(equal, &heap_number);
- __ Set(rax, EQUAL);
- __ ret(0);
- } else {
- // Identical objects must still be converted to primitive for < and >.
- __ j(not_equal, &not_identical);
+ __ j(equal, &heap_number);
+ if (cc_ != equal) {
+ // Call runtime on identical JSObjects. Otherwise return equal.
+ __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rcx);
+ __ j(above_equal, &not_identical);
}
+ __ Set(rax, EQUAL);
+ __ ret(0);
__ bind(&heap_number);
// It is a heap number, so return equal if it's not NaN.
@@ -9113,7 +9231,8 @@ void CompareStub::Generate(MacroAssembler* masm) {
__ bind(&check_for_strings);
- __ JumpIfNotBothSequentialAsciiStrings(rdx, rax, rcx, rbx, &call_builtin);
+ __ JumpIfNotBothSequentialAsciiStrings(
+ rdx, rax, rcx, rbx, &check_unequal_objects);
// Inline comparison of ascii strings.
StringCompareStub::GenerateCompareFlatAsciiStrings(masm,
@@ -9128,7 +9247,40 @@ void CompareStub::Generate(MacroAssembler* masm) {
__ Abort("Unexpected fall-through from string comparison");
#endif
- __ bind(&call_builtin);
+ __ bind(&check_unequal_objects);
+ if (cc_ == equal && !strict_) {
+ // Not strict equality. Objects are unequal if
+ // they are both JSObjects and not undetectable,
+ // and their pointers are different.
+ Label not_both_objects, return_unequal;
+ // At most one is a smi, so we can test for smi by adding the two.
+ // A smi plus a heap object has the low bit set, a heap object plus
+ // a heap object has the low bit clear.
+ ASSERT_EQ(0, kSmiTag);
+ ASSERT_EQ(V8_UINT64_C(1), kSmiTagMask);
+ __ lea(rcx, Operand(rax, rdx, times_1, 0));
+ __ testb(rcx, Immediate(kSmiTagMask));
+ __ j(not_zero, &not_both_objects);
+ __ CmpObjectType(rax, FIRST_JS_OBJECT_TYPE, rbx);
+ __ j(below, &not_both_objects);
+ __ CmpObjectType(rdx, FIRST_JS_OBJECT_TYPE, rcx);
+ __ j(below, &not_both_objects);
+ __ testb(FieldOperand(rbx, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsUndetectable));
+ __ j(zero, &return_unequal);
+ __ testb(FieldOperand(rcx, Map::kBitFieldOffset),
+ Immediate(1 << Map::kIsUndetectable));
+ __ j(zero, &return_unequal);
+ // The objects are both undetectable, so they both compare as the value
+ // undefined, and are equal.
+ __ Set(rax, EQUAL);
+ __ bind(&return_unequal);
+ // Return non-equal by returning the non-zero object pointer in eax,
+ // or return equal if we fell through to here.
+ __ ret(2 * kPointerSize); // rax, rdx were pushed
+ __ bind(&not_both_objects);
+ }
+
// must swap argument order
__ pop(rcx);
__ pop(rdx);
@@ -9488,7 +9640,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
// rbp: frame pointer (restored after C call).
// rsp: stack pointer (restored after C call).
// r14: number of arguments including receiver (C callee-saved).
- // r15: pointer to the first argument (C callee-saved).
+ // r12: pointer to the first argument (C callee-saved).
// This pointer is reused in LeaveExitFrame(), so it is stored in a
// callee-saved register.
@@ -9529,7 +9681,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
// Windows 64-bit ABI passes arguments in rcx, rdx, r8, r9
// Store Arguments object on stack, below the 4 WIN64 ABI parameter slots.
__ movq(Operand(rsp, 4 * kPointerSize), r14); // argc.
- __ movq(Operand(rsp, 5 * kPointerSize), r15); // argv.
+ __ movq(Operand(rsp, 5 * kPointerSize), r12); // argv.
if (result_size_ < 2) {
// Pass a pointer to the Arguments object as the first argument.
// Return result in single register (rax).
@@ -9545,7 +9697,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm,
#else // _WIN64
// GCC passes arguments in rdi, rsi, rdx, rcx, r8, r9.
__ movq(rdi, r14); // argc.
- __ movq(rsi, r15); // argv.
+ __ movq(rsi, r12); // argv.
#endif
__ call(rbx);
// Result is in rax - do not destroy this register!
@@ -9747,7 +9899,7 @@ void CEntryStub::Generate(MacroAssembler* masm) {
// rbp: frame pointer of exit frame (restored after C call).
// rsp: stack pointer (restored after C call).
// r14: number of arguments including receiver (C callee-saved).
- // r15: argv pointer (C callee-saved).
+ // r12: argv pointer (C callee-saved).
Label throw_normal_exception;
Label throw_termination_exception;
@@ -9807,24 +9959,38 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
// Push the stack frame type marker twice.
int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY;
- __ Push(Smi::FromInt(marker)); // context slot
- __ Push(Smi::FromInt(marker)); // function slot
- // Save callee-saved registers (X64 calling conventions).
+ // Scratch register is neither callee-save, nor an argument register on any
+ // platform. It's free to use at this point.
+ // Cannot use smi-register for loading yet.
+ __ movq(kScratchRegister,
+ reinterpret_cast<uint64_t>(Smi::FromInt(marker)),
+ RelocInfo::NONE);
+ __ push(kScratchRegister); // context slot
+ __ push(kScratchRegister); // function slot
+ // Save callee-saved registers (X64/Win64 calling conventions).
__ push(r12);
__ push(r13);
__ push(r14);
__ push(r15);
- __ push(rdi);
- __ push(rsi);
+#ifdef _WIN64
+ __ push(rdi); // Only callee save in Win64 ABI, argument in AMD64 ABI.
+ __ push(rsi); // Only callee save in Win64 ABI, argument in AMD64 ABI.
+#endif
__ push(rbx);
- // TODO(X64): Push XMM6-XMM15 (low 64 bits) as well, or make them
- // callee-save in JS code as well.
+ // TODO(X64): On Win64, if we ever use XMM6-XMM15, the low low 64 bits are
+ // callee save as well.
// Save copies of the top frame descriptor on the stack.
ExternalReference c_entry_fp(Top::k_c_entry_fp_address);
__ load_rax(c_entry_fp);
__ push(rax);
+ // Set up the roots and smi constant registers.
+ // Needs to be done before any further smi loads.
+ ExternalReference roots_address = ExternalReference::roots_address();
+ __ movq(kRootRegister, roots_address);
+ __ InitializeSmiConstantRegister();
+
#ifdef ENABLE_LOGGING_AND_PROFILING
// If this is the outermost JS call, set js_entry_sp value.
ExternalReference js_entry_sp(Top::k_js_entry_sp_address);
@@ -9895,8 +10061,11 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) {
// Restore callee-saved registers (X64 conventions).
__ pop(rbx);
+#ifdef _WIN64
+ // Callee save on in Win64 ABI, arguments/volatile in AMD64 ABI.
__ pop(rsi);
__ pop(rdi);
+#endif
__ pop(r15);
__ pop(r14);
__ pop(r13);
@@ -10442,6 +10611,7 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
// the four basic operations. The stub stays in the DEFAULT state
// forever for all other operations (also if smi code is skipped).
GenerateTypeTransition(masm);
+ break;
}
Label not_floats;
@@ -10759,31 +10929,13 @@ void GenericBinaryOpStub::GenerateRegisterArgsPush(MacroAssembler* masm) {
void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
Label get_result;
- // Keep a copy of operands on the stack and make sure they are also in
- // rdx, rax.
+ // Ensure the operands are on the stack.
if (HasArgsInRegisters()) {
GenerateRegisterArgsPush(masm);
- } else {
- GenerateLoadArguments(masm);
}
- // Internal frame is necessary to handle exceptions properly.
- __ EnterInternalFrame();
-
- // Push arguments on stack if the stub expects them there.
- if (!HasArgsInRegisters()) {
- __ push(rdx);
- __ push(rax);
- }
- // Call the stub proper to get the result in rax.
- __ call(&get_result);
- __ LeaveInternalFrame();
-
// Left and right arguments are already on stack.
- __ pop(rcx);
- // Push the operation result. The tail call to BinaryOp_Patch will
- // return it to the original caller..
- __ push(rax);
+ __ pop(rcx); // Save the return address.
// Push this stub's key.
__ Push(Smi::FromInt(MinorKey()));
@@ -10794,17 +10946,13 @@ void GenericBinaryOpStub::GenerateTypeTransition(MacroAssembler* masm) {
__ Push(Smi::FromInt(runtime_operands_type_));
- __ push(rcx);
+ __ push(rcx); // The return address.
// Perform patching to an appropriate fast case and return the result.
__ TailCallExternalReference(
ExternalReference(IC_Utility(IC::kBinaryOp_Patch)),
- 6,
+ 5,
1);
-
- // The entry point for the result calculation is assumed to be immediately
- // after this sequence.
- __ bind(&get_result);
}
@@ -11130,7 +11278,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
// Check that both strings are non-external ascii strings.
__ JumpIfBothInstanceTypesAreNotSequentialAscii(r8, r9, rbx, rcx,
- &string_add_runtime);
+ &string_add_runtime);
// Get the two characters forming the sub string.
__ movzxbq(rbx, FieldOperand(rax, SeqAsciiString::kHeaderSize));
@@ -11140,7 +11288,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
// just allocate a new one.
Label make_two_character_string, make_flat_ascii_string;
StringHelper::GenerateTwoCharacterSymbolTableProbe(
- masm, rbx, rcx, r14, r12, rdi, r15, &make_two_character_string);
+ masm, rbx, rcx, r14, r11, rdi, r12, &make_two_character_string);
__ IncrementCounter(&Counters::string_add_native, 1);
__ ret(2 * kPointerSize);
@@ -11232,7 +11380,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
__ bind(&make_flat_ascii_string);
// Both strings are ascii strings. As they are short they are both flat.
- __ AllocateAsciiString(rcx, rbx, rdi, r14, r15, &string_add_runtime);
+ __ AllocateAsciiString(rcx, rbx, rdi, r14, r11, &string_add_runtime);
// rcx: result string
__ movq(rbx, rcx);
// Locate first character of result.
@@ -11269,7 +11417,7 @@ void StringAddStub::Generate(MacroAssembler* masm) {
__ j(not_zero, &string_add_runtime);
// Both strings are two byte strings. As they are short they are both
// flat.
- __ AllocateTwoByteString(rcx, rbx, rdi, r14, r15, &string_add_runtime);
+ __ AllocateTwoByteString(rcx, rbx, rdi, r14, r11, &string_add_runtime);
// rcx: result string
__ movq(rbx, rcx);
// Locate first character of result.
diff --git a/deps/v8/src/x64/codegen-x64.h b/deps/v8/src/x64/codegen-x64.h
index cd03d2acc1..b9a3b70631 100644
--- a/deps/v8/src/x64/codegen-x64.h
+++ b/deps/v8/src/x64/codegen-x64.h
@@ -393,6 +393,9 @@ class CodeGenerator: public AstVisitor {
// target (which can not be done more than once).
void GenerateReturnSequence(Result* return_value);
+ // Generate code for a fast smi loop.
+ void GenerateFastSmiLoop(ForStatement* node);
+
// Returns the arguments allocation mode.
ArgumentsAllocationMode ArgumentsMode();
diff --git a/deps/v8/src/x64/frames-x64.h b/deps/v8/src/x64/frames-x64.h
index a92b248d88..9991981a79 100644
--- a/deps/v8/src/x64/frames-x64.h
+++ b/deps/v8/src/x64/frames-x64.h
@@ -56,7 +56,11 @@ class StackHandlerConstants : public AllStatic {
class EntryFrameConstants : public AllStatic {
public:
+#ifdef _WIN64
static const int kCallerFPOffset = -10 * kPointerSize;
+#else
+ static const int kCallerFPOffset = -8 * kPointerSize;
+#endif
static const int kArgvOffset = 6 * kPointerSize;
};
diff --git a/deps/v8/src/x64/full-codegen-x64.cc b/deps/v8/src/x64/full-codegen-x64.cc
index e3f74f6c05..c6be503366 100644
--- a/deps/v8/src/x64/full-codegen-x64.cc
+++ b/deps/v8/src/x64/full-codegen-x64.cc
@@ -2807,9 +2807,11 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
case Token::SUB: {
Comment cmt(masm_, "[ UnaryOperation (SUB)");
- bool overwrite =
+ bool can_overwrite =
(expr->expression()->AsBinaryOperation() != NULL &&
expr->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
+ UnaryOverwriteMode overwrite =
+ can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
GenericUnaryOpStub stub(Token::SUB, overwrite);
// GenericUnaryOpStub expects the argument to be in the
// accumulator register rax.
@@ -2821,9 +2823,11 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) {
case Token::BIT_NOT: {
Comment cmt(masm_, "[ UnaryOperation (BIT_NOT)");
- bool overwrite =
+ bool can_overwrite =
(expr->expression()->AsBinaryOperation() != NULL &&
expr->expression()->AsBinaryOperation()->ResultOverwriteAllowed());
+ UnaryOverwriteMode overwrite =
+ can_overwrite ? UNARY_OVERWRITE : UNARY_NO_OVERWRITE;
GenericUnaryOpStub stub(Token::BIT_NOT, overwrite);
// GenericUnaryOpStub expects the argument to be in the
// accumulator register rax.
diff --git a/deps/v8/src/x64/ic-x64.cc b/deps/v8/src/x64/ic-x64.cc
index 31a806a36b..d04a7dcd59 100644
--- a/deps/v8/src/x64/ic-x64.cc
+++ b/deps/v8/src/x64/ic-x64.cc
@@ -61,11 +61,11 @@ static void GenerateGlobalInstanceTypeCheck(MacroAssembler* masm,
// Generated code falls through if the receiver is a regular non-global
// JS object with slow properties and no interceptors.
-static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm,
- Register receiver,
- Register r0,
- Register r1,
- Label* miss) {
+static void GenerateStringDictionaryReceiverCheck(MacroAssembler* masm,
+ Register receiver,
+ Register r0,
+ Register r1,
+ Label* miss) {
// Register usage:
// receiver: holds the receiver on entry and is unchanged.
// r0: used to hold receiver instance type.
@@ -98,34 +98,17 @@ static void GenerateDictionaryLoadReceiverCheck(MacroAssembler* masm,
}
-// Helper function used to load a property from a dictionary backing storage.
-// This function may return false negatives, so miss_label
-// must always call a backup property load that is complete.
-// This function is safe to call if name is not a symbol, and will jump to
-// the miss_label in that case.
-// The generated code assumes that the receiver has slow properties,
-// is not a global object and does not have interceptors.
-static void GenerateDictionaryLoad(MacroAssembler* masm,
- Label* miss_label,
- Register elements,
- Register name,
- Register r0,
- Register r1,
- Register result) {
- // Register use:
- //
- // elements - holds the property dictionary on entry and is unchanged.
- //
- // name - holds the name of the property on entry and is unchanged.
- //
- // r0 - used to hold the capacity of the property dictionary.
- //
- // r1 - used to hold the index into the property dictionary.
- //
- // result - holds the result on exit if the load succeeded.
-
- Label done;
-
+// Probe the string dictionary in the |elements| register. Jump to the
+// |done| label if a property with the given name is found leaving the
+// index into the dictionary in |r1|. Jump to the |miss| label
+// otherwise.
+static void GenerateStringDictionaryProbes(MacroAssembler* masm,
+ Label* miss,
+ Label* done,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1) {
// Compute the capacity mask.
const int kCapacityOffset =
StringDictionary::kHeaderSize +
@@ -157,14 +140,58 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
__ cmpq(name, Operand(elements, r1, times_pointer_size,
kElementsStartOffset - kHeapObjectTag));
if (i != kProbes - 1) {
- __ j(equal, &done);
+ __ j(equal, done);
} else {
- __ j(not_equal, miss_label);
+ __ j(not_equal, miss);
}
}
+}
+
+
+// Helper function used to load a property from a dictionary backing storage.
+// This function may return false negatives, so miss_label
+// must always call a backup property load that is complete.
+// This function is safe to call if name is not a symbol, and will jump to
+// the miss_label in that case.
+// The generated code assumes that the receiver has slow properties,
+// is not a global object and does not have interceptors.
+static void GenerateDictionaryLoad(MacroAssembler* masm,
+ Label* miss_label,
+ Register elements,
+ Register name,
+ Register r0,
+ Register r1,
+ Register result) {
+ // Register use:
+ //
+ // elements - holds the property dictionary on entry and is unchanged.
+ //
+ // name - holds the name of the property on entry and is unchanged.
+ //
+ // r0 - used to hold the capacity of the property dictionary.
+ //
+ // r1 - used to hold the index into the property dictionary.
+ //
+ // result - holds the result on exit if the load succeeded.
+
+ Label done;
- // Check that the value is a normal property.
+ // Probe the dictionary.
+ GenerateStringDictionaryProbes(masm,
+ miss_label,
+ &done,
+ elements,
+ name,
+ r0,
+ r1);
+
+ // If probing finds an entry in the dictionary, r0 contains the
+ // index into the dictionary. Check that the value is a normal
+ // property.
__ bind(&done);
+ const int kElementsStartOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kElementsStartIndex * kPointerSize;
const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
__ Test(Operand(elements, r1, times_pointer_size,
kDetailsOffset - kHeapObjectTag),
@@ -179,6 +206,75 @@ static void GenerateDictionaryLoad(MacroAssembler* masm,
}
+// Helper function used to store a property to a dictionary backing
+// storage. This function may fail to store a property even though it
+// is in the dictionary, so code at miss_label must always call a
+// backup property store that is complete. This function is safe to
+// call if name is not a symbol, and will jump to the miss_label in
+// that case. The generated code assumes that the receiver has slow
+// properties, is not a global object and does not have interceptors.
+static void GenerateDictionaryStore(MacroAssembler* masm,
+ Label* miss_label,
+ Register elements,
+ Register name,
+ Register value,
+ Register scratch0,
+ Register scratch1) {
+ // Register use:
+ //
+ // elements - holds the property dictionary on entry and is clobbered.
+ //
+ // name - holds the name of the property on entry and is unchanged.
+ //
+ // value - holds the value to store and is unchanged.
+ //
+ // scratch0 - used for index into the property dictionary and is clobbered.
+ //
+ // scratch1 - used to hold the capacity of the property dictionary and is
+ // clobbered.
+ Label done;
+
+ // Probe the dictionary.
+ GenerateStringDictionaryProbes(masm,
+ miss_label,
+ &done,
+ elements,
+ name,
+ scratch0,
+ scratch1);
+
+ // If probing finds an entry in the dictionary, scratch0 contains the
+ // index into the dictionary. Check that the value is a normal
+ // property that is not read only.
+ __ bind(&done);
+ const int kElementsStartOffset =
+ StringDictionary::kHeaderSize +
+ StringDictionary::kElementsStartIndex * kPointerSize;
+ const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize;
+ const int kTypeAndReadOnlyMask
+ = (PropertyDetails::TypeField::mask() |
+ PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize;
+ __ Test(Operand(elements,
+ scratch1,
+ times_pointer_size,
+ kDetailsOffset - kHeapObjectTag),
+ Smi::FromInt(kTypeAndReadOnlyMask));
+ __ j(not_zero, miss_label);
+
+ // Store the value at the masked, scaled index.
+ const int kValueOffset = kElementsStartOffset + kPointerSize;
+ __ lea(scratch1, Operand(elements,
+ scratch1,
+ times_pointer_size,
+ kValueOffset - kHeapObjectTag));
+ __ movq(Operand(scratch1, 0), value);
+
+ // Update write barrier. Make sure not to clobber the value.
+ __ movq(scratch0, value);
+ __ RecordWrite(elements, scratch1, scratch0);
+}
+
+
static void GenerateNumberDictionaryLoad(MacroAssembler* masm,
Label* miss,
Register elements,
@@ -1332,7 +1428,7 @@ static void GenerateCallNormal(MacroAssembler* masm, int argc) {
// Get the receiver of the function from the stack.
__ movq(rdx, Operand(rsp, (argc + 1) * kPointerSize));
- GenerateDictionaryLoadReceiverCheck(masm, rdx, rax, rbx, &miss);
+ GenerateStringDictionaryReceiverCheck(masm, rdx, rax, rbx, &miss);
// rax: elements
// Search the dictionary placing the result in rdi.
@@ -1616,7 +1712,7 @@ void LoadIC::GenerateNormal(MacroAssembler* masm) {
// -----------------------------------
Label miss;
- GenerateDictionaryLoadReceiverCheck(masm, rax, rdx, rbx, &miss);
+ GenerateStringDictionaryReceiverCheck(masm, rax, rdx, rbx, &miss);
// rdx: elements
// Search the dictionary placing the result in rax.
@@ -1760,6 +1856,28 @@ void StoreIC::GenerateArrayLength(MacroAssembler* masm) {
}
+void StoreIC::GenerateNormal(MacroAssembler* masm) {
+ // ----------- S t a t e -------------
+ // -- rax : value
+ // -- rcx : name
+ // -- rdx : receiver
+ // -- rsp[0] : return address
+ // -----------------------------------
+
+ Label miss, restore_miss;
+
+ GenerateStringDictionaryReceiverCheck(masm, rdx, rbx, rdi, &miss);
+
+ GenerateDictionaryStore(masm, &miss, rbx, rcx, rax, r8, r9);
+ __ IncrementCounter(&Counters::store_normal_hit, 1);
+ __ ret(0);
+
+ __ bind(&miss);
+ __ IncrementCounter(&Counters::store_normal_miss, 1);
+ GenerateMiss(masm);
+}
+
+
#undef __
diff --git a/deps/v8/src/x64/macro-assembler-x64.cc b/deps/v8/src/x64/macro-assembler-x64.cc
index 3b2c789d1b..76200d7e54 100644
--- a/deps/v8/src/x64/macro-assembler-x64.cc
+++ b/deps/v8/src/x64/macro-assembler-x64.cc
@@ -105,12 +105,6 @@ void MacroAssembler::RecordWriteHelper(Register object,
}
-// For page containing |object| mark region covering [object+offset] dirty.
-// object is the object being stored into, value is the object being stored.
-// If offset is zero, then the index register contains the array index into
-// the elements array represented a zero extended int32. Otherwise it can be
-// used as a scratch register.
-// All registers are clobbered by the operation.
void MacroAssembler::RecordWrite(Register object,
int offset,
Register value,
@@ -141,6 +135,35 @@ void MacroAssembler::RecordWrite(Register object,
}
+void MacroAssembler::RecordWrite(Register object,
+ Register address,
+ Register value) {
+ // The compiled code assumes that record write doesn't change the
+ // context register, so we check that none of the clobbered
+ // registers are esi.
+ ASSERT(!object.is(rsi) && !value.is(rsi) && !address.is(rsi));
+
+ // First, check if a write barrier is even needed. The tests below
+ // catch stores of Smis and stores into young gen.
+ Label done;
+ JumpIfSmi(value, &done);
+
+ InNewSpace(object, value, equal, &done);
+
+ RecordWriteHelper(object, address, value);
+
+ bind(&done);
+
+ // Clobber all input registers when running with the debug-code flag
+ // turned on to provoke errors.
+ if (FLAG_debug_code) {
+ movq(object, BitCast<int64_t>(kZapValue), RelocInfo::NONE);
+ movq(address, BitCast<int64_t>(kZapValue), RelocInfo::NONE);
+ movq(value, BitCast<int64_t>(kZapValue), RelocInfo::NONE);
+ }
+}
+
+
void MacroAssembler::RecordWriteNonSmi(Register object,
int offset,
Register scratch,
@@ -444,7 +467,7 @@ void MacroAssembler::GetBuiltinEntry(Register target, Builtins::JavaScript id) {
void MacroAssembler::Set(Register dst, int64_t x) {
if (x == 0) {
- xor_(dst, dst);
+ xorl(dst, dst);
} else if (is_int32(x)) {
movq(dst, Immediate(static_cast<int32_t>(x)));
} else if (is_uint32(x)) {
@@ -454,7 +477,6 @@ void MacroAssembler::Set(Register dst, int64_t x) {
}
}
-
void MacroAssembler::Set(const Operand& dst, int64_t x) {
if (is_int32(x)) {
movq(dst, Immediate(static_cast<int32_t>(x)));
@@ -469,6 +491,78 @@ void MacroAssembler::Set(const Operand& dst, int64_t x) {
static int kSmiShift = kSmiTagSize + kSmiShiftSize;
+Register MacroAssembler::GetSmiConstant(Smi* source) {
+ int value = source->value();
+ if (value == 0) {
+ xorl(kScratchRegister, kScratchRegister);
+ return kScratchRegister;
+ }
+ if (value == 1) {
+ return kSmiConstantRegister;
+ }
+ LoadSmiConstant(kScratchRegister, source);
+ return kScratchRegister;
+}
+
+void MacroAssembler::LoadSmiConstant(Register dst, Smi* source) {
+ if (FLAG_debug_code) {
+ movq(dst,
+ reinterpret_cast<uint64_t>(Smi::FromInt(kSmiConstantRegisterValue)),
+ RelocInfo::NONE);
+ cmpq(dst, kSmiConstantRegister);
+ if (allow_stub_calls()) {
+ Assert(equal, "Uninitialized kSmiConstantRegister");
+ } else {
+ Label ok;
+ j(equal, &ok);
+ int3();
+ bind(&ok);
+ }
+ }
+ if (source->value() == 0) {
+ xorl(dst, dst);
+ return;
+ }
+ int value = source->value();
+ bool negative = value < 0;
+ unsigned int uvalue = negative ? -value : value;
+
+ switch (uvalue) {
+ case 9:
+ lea(dst, Operand(kSmiConstantRegister, kSmiConstantRegister, times_8, 0));
+ break;
+ case 8:
+ xorl(dst, dst);
+ lea(dst, Operand(dst, kSmiConstantRegister, times_8, 0));
+ break;
+ case 4:
+ xorl(dst, dst);
+ lea(dst, Operand(dst, kSmiConstantRegister, times_4, 0));
+ break;
+ case 5:
+ lea(dst, Operand(kSmiConstantRegister, kSmiConstantRegister, times_4, 0));
+ break;
+ case 3:
+ lea(dst, Operand(kSmiConstantRegister, kSmiConstantRegister, times_2, 0));
+ break;
+ case 2:
+ lea(dst, Operand(kSmiConstantRegister, kSmiConstantRegister, times_1, 0));
+ break;
+ case 1:
+ movq(dst, kSmiConstantRegister);
+ break;
+ case 0:
+ UNREACHABLE();
+ return;
+ default:
+ movq(dst, reinterpret_cast<uint64_t>(source), RelocInfo::NONE);
+ return;
+ }
+ if (negative) {
+ neg(dst);
+ }
+}
+
void MacroAssembler::Integer32ToSmi(Register dst, Register src) {
ASSERT_EQ(0, kSmiTag);
if (!dst.is(src)) {
@@ -629,9 +723,10 @@ Condition MacroAssembler::CheckSmi(Register src) {
Condition MacroAssembler::CheckPositiveSmi(Register src) {
ASSERT_EQ(0, kSmiTag);
+ // Make mask 0x8000000000000001 and test that both bits are zero.
movq(kScratchRegister, src);
rol(kScratchRegister, Immediate(1));
- testl(kScratchRegister, Immediate(0x03));
+ testb(kScratchRegister, Immediate(3));
return zero;
}
@@ -660,7 +755,6 @@ Condition MacroAssembler::CheckBothPositiveSmi(Register first,
}
-
Condition MacroAssembler::CheckEitherSmi(Register first, Register second) {
if (first.is(second)) {
return CheckSmi(first);
@@ -673,11 +767,10 @@ Condition MacroAssembler::CheckEitherSmi(Register first, Register second) {
Condition MacroAssembler::CheckIsMinSmi(Register src) {
- ASSERT(kSmiTag == 0 && kSmiTagSize == 1);
- movq(kScratchRegister, src);
- rol(kScratchRegister, Immediate(1));
- cmpq(kScratchRegister, Immediate(1));
- return equal;
+ ASSERT(!src.is(kScratchRegister));
+ // If we overflow by subtracting one, it's the minimal smi value.
+ cmpq(src, kSmiConstantRegister);
+ return overflow;
}
@@ -690,8 +783,8 @@ Condition MacroAssembler::CheckInteger32ValidSmiValue(Register src) {
Condition MacroAssembler::CheckUInteger32ValidSmiValue(Register src) {
// An unsigned 32-bit integer value is valid as long as the high bit
// is not set.
- testq(src, Immediate(0x80000000));
- return zero;
+ testl(src, src);
+ return positive;
}
@@ -784,10 +877,10 @@ void MacroAssembler::SmiSub(Register dst,
}
Assert(no_overflow, "Smi subtraction overflow");
} else if (dst.is(src1)) {
- movq(kScratchRegister, src1);
- subq(kScratchRegister, src2);
+ movq(kScratchRegister, src2);
+ cmpq(src1, kScratchRegister);
j(overflow, on_not_smi_result);
- movq(src1, kScratchRegister);
+ subq(src1, kScratchRegister);
} else {
movq(dst, src1);
subq(dst, src2);
@@ -860,7 +953,7 @@ void MacroAssembler::SmiTryAddConstant(Register dst,
JumpIfNotSmi(src, on_not_smi_result);
Register tmp = (dst.is(src) ? kScratchRegister : dst);
- Move(tmp, constant);
+ LoadSmiConstant(tmp, constant);
addq(tmp, src);
j(overflow, on_not_smi_result);
if (dst.is(src)) {
@@ -874,14 +967,46 @@ void MacroAssembler::SmiAddConstant(Register dst, Register src, Smi* constant) {
if (!dst.is(src)) {
movq(dst, src);
}
+ return;
} else if (dst.is(src)) {
ASSERT(!dst.is(kScratchRegister));
-
- Move(kScratchRegister, constant);
- addq(dst, kScratchRegister);
+ switch (constant->value()) {
+ case 1:
+ addq(dst, kSmiConstantRegister);
+ return;
+ case 2:
+ lea(dst, Operand(src, kSmiConstantRegister, times_2, 0));
+ return;
+ case 4:
+ lea(dst, Operand(src, kSmiConstantRegister, times_4, 0));
+ return;
+ case 8:
+ lea(dst, Operand(src, kSmiConstantRegister, times_8, 0));
+ return;
+ default:
+ Register constant_reg = GetSmiConstant(constant);
+ addq(dst, constant_reg);
+ return;
+ }
} else {
- Move(dst, constant);
- addq(dst, src);
+ switch (constant->value()) {
+ case 1:
+ lea(dst, Operand(src, kSmiConstantRegister, times_1, 0));
+ return;
+ case 2:
+ lea(dst, Operand(src, kSmiConstantRegister, times_2, 0));
+ return;
+ case 4:
+ lea(dst, Operand(src, kSmiConstantRegister, times_4, 0));
+ return;
+ case 8:
+ lea(dst, Operand(src, kSmiConstantRegister, times_8, 0));
+ return;
+ default:
+ LoadSmiConstant(dst, constant);
+ addq(dst, src);
+ return;
+ }
}
}
@@ -904,12 +1029,12 @@ void MacroAssembler::SmiAddConstant(Register dst,
} else if (dst.is(src)) {
ASSERT(!dst.is(kScratchRegister));
- Move(kScratchRegister, constant);
- addq(kScratchRegister, dst);
+ LoadSmiConstant(kScratchRegister, constant);
+ addq(kScratchRegister, src);
j(overflow, on_not_smi_result);
movq(dst, kScratchRegister);
} else {
- Move(dst, constant);
+ LoadSmiConstant(dst, constant);
addq(dst, src);
j(overflow, on_not_smi_result);
}
@@ -923,19 +1048,17 @@ void MacroAssembler::SmiSubConstant(Register dst, Register src, Smi* constant) {
}
} else if (dst.is(src)) {
ASSERT(!dst.is(kScratchRegister));
-
- Move(kScratchRegister, constant);
- subq(dst, kScratchRegister);
+ Register constant_reg = GetSmiConstant(constant);
+ subq(dst, constant_reg);
} else {
- // Subtract by adding the negative, to do it in two operations.
if (constant->value() == Smi::kMinValue) {
- Move(dst, constant);
+ LoadSmiConstant(dst, constant);
// Adding and subtracting the min-value gives the same result, it only
// differs on the overflow bit, which we don't check here.
addq(dst, src);
} else {
// Subtract by adding the negation.
- Move(dst, Smi::FromInt(-constant->value()));
+ LoadSmiConstant(dst, Smi::FromInt(-constant->value()));
addq(dst, src);
}
}
@@ -957,11 +1080,11 @@ void MacroAssembler::SmiSubConstant(Register dst,
// We test the non-negativeness before doing the subtraction.
testq(src, src);
j(not_sign, on_not_smi_result);
- Move(kScratchRegister, constant);
+ LoadSmiConstant(kScratchRegister, constant);
subq(dst, kScratchRegister);
} else {
// Subtract by adding the negation.
- Move(kScratchRegister, Smi::FromInt(-constant->value()));
+ LoadSmiConstant(kScratchRegister, Smi::FromInt(-constant->value()));
addq(kScratchRegister, dst);
j(overflow, on_not_smi_result);
movq(dst, kScratchRegister);
@@ -972,13 +1095,13 @@ void MacroAssembler::SmiSubConstant(Register dst,
// We test the non-negativeness before doing the subtraction.
testq(src, src);
j(not_sign, on_not_smi_result);
- Move(dst, constant);
+ LoadSmiConstant(dst, constant);
// Adding and subtracting the min-value gives the same result, it only
// differs on the overflow bit, which we don't check here.
addq(dst, src);
} else {
// Subtract by adding the negation.
- Move(dst, Smi::FromInt(-(constant->value())));
+ LoadSmiConstant(dst, Smi::FromInt(-(constant->value())));
addq(dst, src);
j(overflow, on_not_smi_result);
}
@@ -1132,10 +1255,10 @@ void MacroAssembler::SmiAndConstant(Register dst, Register src, Smi* constant) {
xor_(dst, dst);
} else if (dst.is(src)) {
ASSERT(!dst.is(kScratchRegister));
- Move(kScratchRegister, constant);
- and_(dst, kScratchRegister);
+ Register constant_reg = GetSmiConstant(constant);
+ and_(dst, constant_reg);
} else {
- Move(dst, constant);
+ LoadSmiConstant(dst, constant);
and_(dst, src);
}
}
@@ -1152,10 +1275,10 @@ void MacroAssembler::SmiOr(Register dst, Register src1, Register src2) {
void MacroAssembler::SmiOrConstant(Register dst, Register src, Smi* constant) {
if (dst.is(src)) {
ASSERT(!dst.is(kScratchRegister));
- Move(kScratchRegister, constant);
- or_(dst, kScratchRegister);
+ Register constant_reg = GetSmiConstant(constant);
+ or_(dst, constant_reg);
} else {
- Move(dst, constant);
+ LoadSmiConstant(dst, constant);
or_(dst, src);
}
}
@@ -1172,10 +1295,10 @@ void MacroAssembler::SmiXor(Register dst, Register src1, Register src2) {
void MacroAssembler::SmiXorConstant(Register dst, Register src, Smi* constant) {
if (dst.is(src)) {
ASSERT(!dst.is(kScratchRegister));
- Move(kScratchRegister, constant);
- xor_(dst, kScratchRegister);
+ Register constant_reg = GetSmiConstant(constant);
+ xor_(dst, constant_reg);
} else {
- Move(dst, constant);
+ LoadSmiConstant(dst, constant);
xor_(dst, src);
}
}
@@ -1343,6 +1466,7 @@ void MacroAssembler::SelectNonSmi(Register dst,
// If src1 is a smi, dst is src2, else it is src1, i.e., the non-smi.
}
+
SmiIndex MacroAssembler::SmiToIndex(Register dst,
Register src,
int shift) {
@@ -1568,8 +1692,8 @@ void MacroAssembler::Push(Smi* source) {
if (is_int32(smi)) {
push(Immediate(static_cast<int32_t>(smi)));
} else {
- Set(kScratchRegister, smi);
- push(kScratchRegister);
+ Register constant = GetSmiConstant(source);
+ push(constant);
}
}
@@ -2109,10 +2233,10 @@ void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode, int result_size) {
movq(rax, rsi);
store_rax(context_address);
- // Setup argv in callee-saved register r15. It is reused in LeaveExitFrame,
+ // Setup argv in callee-saved register r12. It is reused in LeaveExitFrame,
// so it must be retained across the C-call.
int offset = StandardFrameConstants::kCallerSPOffset - kPointerSize;
- lea(r15, Operand(rbp, r14, times_pointer_size, offset));
+ lea(r12, Operand(rbp, r14, times_pointer_size, offset));
#ifdef ENABLE_DEBUGGER_SUPPORT
// Save the state of all registers to the stack from the memory
@@ -2158,7 +2282,7 @@ void MacroAssembler::EnterExitFrame(ExitFrame::Mode mode, int result_size) {
void MacroAssembler::LeaveExitFrame(ExitFrame::Mode mode, int result_size) {
// Registers:
- // r15 : argv
+ // r12 : argv
#ifdef ENABLE_DEBUGGER_SUPPORT
// Restore the memory copy of the registers by digging them out from
// the stack. This is needed to allow nested break points.
@@ -2178,7 +2302,7 @@ void MacroAssembler::LeaveExitFrame(ExitFrame::Mode mode, int result_size) {
// Pop everything up to and including the arguments and the receiver
// from the caller stack.
- lea(rsp, Operand(r15, 1 * kPointerSize));
+ lea(rsp, Operand(r12, 1 * kPointerSize));
// Restore current context from top and clear it in debug mode.
ExternalReference context_address(Top::k_context_address);
diff --git a/deps/v8/src/x64/macro-assembler-x64.h b/deps/v8/src/x64/macro-assembler-x64.h
index 44573f34fa..a256ab82bd 100644
--- a/deps/v8/src/x64/macro-assembler-x64.h
+++ b/deps/v8/src/x64/macro-assembler-x64.h
@@ -47,8 +47,11 @@ enum AllocationFlags {
// 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 = { 10 }; // r10.
-static const Register kRootRegister = { 13 }; // r13
+static const Register kScratchRegister = { 10 }; // r10.
+static const Register kSmiConstantRegister = { 15 }; // r15 (callee save).
+static const Register kRootRegister = { 13 }; // r13 (callee save).
+// Value of smi in kSmiConstantRegister.
+static const int kSmiConstantRegisterValue = 1;
// Convenience for platform-independent signatures.
typedef Operand MemOperand;
@@ -93,16 +96,27 @@ class MacroAssembler: public Assembler {
Condition cc,
Label* branch);
- // For page containing |object| mark region covering [object+offset] dirty.
- // object is the object being stored into, value is the object being stored.
- // If offset is zero, then the scratch register contains the array index into
- // the elements array represented as a Smi.
- // All registers are clobbered by the operation.
+ // For page containing |object| mark region covering [object+offset]
+ // dirty. |object| is the object being stored into, |value| is the
+ // object being stored. If |offset| is zero, then the |scratch|
+ // register contains the array index into the elements array
+ // represented as a Smi. All registers are clobbered by the
+ // operation. RecordWrite filters out smis so it does not update the
+ // write barrier if the value is a smi.
void RecordWrite(Register object,
int offset,
Register value,
Register scratch);
+ // For page containing |object| mark region covering [address]
+ // dirty. |object| is the object being stored into, |value| is the
+ // object being stored. All registers are clobbered by the
+ // operation. RecordWrite filters out smis so it does not update
+ // the write barrier if the value is a smi.
+ void RecordWrite(Register object,
+ Register address,
+ Register value);
+
// For page containing |object| mark region covering [object+offset] dirty.
// The value is known to not be a smi.
// object is the object being stored into, value is the object being stored.
@@ -191,6 +205,12 @@ class MacroAssembler: public Assembler {
// ---------------------------------------------------------------------------
// Smi tagging, untagging and operations on tagged smis.
+ void InitializeSmiConstantRegister() {
+ movq(kSmiConstantRegister,
+ reinterpret_cast<uint64_t>(Smi::FromInt(kSmiConstantRegisterValue)),
+ RelocInfo::NONE);
+ }
+
// Conversions between tagged smi values and non-tagged integer values.
// Tag an integer value. The result must be known to be a valid smi value.
@@ -458,11 +478,12 @@ class MacroAssembler: public Assembler {
// Basic Smi operations.
void Move(Register dst, Smi* source) {
- Set(dst, reinterpret_cast<int64_t>(source));
+ LoadSmiConstant(dst, source);
}
void Move(const Operand& dst, Smi* source) {
- Set(dst, reinterpret_cast<int64_t>(source));
+ Register constant = GetSmiConstant(source);
+ movq(dst, constant);
}
void Push(Smi* smi);
@@ -809,6 +830,14 @@ class MacroAssembler: public Assembler {
private:
bool generating_stub_;
bool allow_stub_calls_;
+
+ // Returns a register holding the smi value. The register MUST NOT be
+ // modified. It may be the "smi 1 constant" register.
+ Register GetSmiConstant(Smi* value);
+
+ // Moves the smi value to the destination register.
+ void LoadSmiConstant(Register dst, Smi* value);
+
// This handle will be patched with the code object on installation.
Handle<Object> code_object_;
diff --git a/deps/v8/src/x64/register-allocator-x64-inl.h b/deps/v8/src/x64/register-allocator-x64-inl.h
index c7c18b397c..c6bea3ab09 100644
--- a/deps/v8/src/x64/register-allocator-x64-inl.h
+++ b/deps/v8/src/x64/register-allocator-x64-inl.h
@@ -38,7 +38,8 @@ namespace internal {
bool RegisterAllocator::IsReserved(Register reg) {
return reg.is(rsp) || reg.is(rbp) || reg.is(rsi) ||
- reg.is(kScratchRegister) || reg.is(kRootRegister);
+ reg.is(kScratchRegister) || reg.is(kRootRegister) ||
+ reg.is(kSmiConstantRegister);
}
@@ -58,11 +59,11 @@ int RegisterAllocator::ToNumber(Register reg) {
5, // r8
6, // r9
-1, // r10 Scratch register.
- 9, // r11
- 10, // r12
+ 8, // r11
+ 9, // r12
-1, // r13 Roots array. This is callee saved.
7, // r14
- 8 // r15
+ -1 // r15 Smi constant register.
};
return kNumbers[reg.code()];
}
@@ -71,7 +72,7 @@ int RegisterAllocator::ToNumber(Register reg) {
Register RegisterAllocator::ToRegister(int num) {
ASSERT(num >= 0 && num < kNumRegisters);
const Register kRegisters[] =
- { rax, rbx, rcx, rdx, rdi, r8, r9, r14, r15, r11, r12 };
+ { rax, rbx, rcx, rdx, rdi, r8, r9, r14, r11, r12 };
return kRegisters[num];
}
diff --git a/deps/v8/src/x64/register-allocator-x64.h b/deps/v8/src/x64/register-allocator-x64.h
index 8d666d2236..a2884d9128 100644
--- a/deps/v8/src/x64/register-allocator-x64.h
+++ b/deps/v8/src/x64/register-allocator-x64.h
@@ -33,7 +33,7 @@ namespace internal {
class RegisterAllocatorConstants : public AllStatic {
public:
- static const int kNumRegisters = 11;
+ static const int kNumRegisters = 10;
static const int kInvalidRegister = -1;
};
diff --git a/deps/v8/src/x64/stub-cache-x64.cc b/deps/v8/src/x64/stub-cache-x64.cc
index 1e103ac263..ab75b96857 100644
--- a/deps/v8/src/x64/stub-cache-x64.cc
+++ b/deps/v8/src/x64/stub-cache-x64.cc
@@ -2125,7 +2125,8 @@ Register StubCompiler::CheckPrototypes(JSObject* object,
Register scratch,
String* name,
int save_at_depth,
- Label* miss) {
+ Label* miss,
+ Register extra) {
// Check that the maps haven't changed.
Register result =
masm()->CheckMaps(object,
diff --git a/deps/v8/src/x64/virtual-frame-x64.h b/deps/v8/src/x64/virtual-frame-x64.h
index 0549e3cc2a..adf47e2167 100644
--- a/deps/v8/src/x64/virtual-frame-x64.h
+++ b/deps/v8/src/x64/virtual-frame-x64.h
@@ -388,6 +388,13 @@ class VirtualFrame : public ZoneObject {
// Duplicate the top element of the frame.
void Dup() { PushFrameSlotAt(element_count() - 1); }
+ // Duplicate the n'th element from the top of the frame.
+ // Dup(1) is equivalent to Dup().
+ void Dup(int n) {
+ ASSERT(n > 0);
+ PushFrameSlotAt(element_count() - n);
+ }
+
// Pop an element from the top of the expression stack. Returns a
// Result, which may be a constant or a register.
Result Pop();
diff --git a/deps/v8/test/cctest/test-api.cc b/deps/v8/test/cctest/test-api.cc
index be5ecba688..330ca5bcd1 100644
--- a/deps/v8/test/cctest/test-api.cc
+++ b/deps/v8/test/cctest/test-api.cc
@@ -3335,6 +3335,42 @@ THREADED_TEST(UndetectableObject) {
}
+
+THREADED_TEST(ExtensibleOnUndetectable) {
+ v8::HandleScope scope;
+ LocalContext env;
+
+ Local<v8::FunctionTemplate> desc =
+ v8::FunctionTemplate::New(0, v8::Handle<Value>());
+ desc->InstanceTemplate()->MarkAsUndetectable(); // undetectable
+
+ Local<v8::Object> obj = desc->GetFunction()->NewInstance();
+ env->Global()->Set(v8_str("undetectable"), obj);
+
+ Local<String> source = v8_str("undetectable.x = 42;"
+ "undetectable.x");
+
+ Local<Script> script = Script::Compile(source);
+
+ CHECK_EQ(v8::Integer::New(42), script->Run());
+
+ ExpectBoolean("Object.isExtensible(undetectable)", true);
+
+ source = v8_str("Object.preventExtensions(undetectable);");
+ script = Script::Compile(source);
+ script->Run();
+ ExpectBoolean("Object.isExtensible(undetectable)", false);
+
+ source = v8_str("undetectable.y = 2000;");
+ script = Script::Compile(source);
+ v8::TryCatch try_catch;
+ Local<Value> result = script->Run();
+ CHECK(result.IsEmpty());
+ CHECK(try_catch.HasCaught());
+}
+
+
+
THREADED_TEST(UndetectableString) {
v8::HandleScope scope;
LocalContext env;
@@ -8521,6 +8557,54 @@ TEST(PreCompileDeserializationError) {
}
+// Attempts to deserialize bad data.
+TEST(PreCompileInvalidPreparseDataError) {
+ v8::V8::Initialize();
+ v8::HandleScope scope;
+ LocalContext context;
+
+ const char* script = "function foo(){ return 5;}\n"
+ "function bar(){ return 6 + 7;} foo();";
+ v8::ScriptData* sd =
+ v8::ScriptData::PreCompile(script, i::StrLength(script));
+ CHECK(!sd->HasError());
+ // ScriptDataImpl private implementation details
+ const int kUnsignedSize = sizeof(unsigned);
+ const int kHeaderSize = 4;
+ const int kFunctionEntrySize = 4;
+ const int kFunctionEntryStartOffset = 0;
+ const int kFunctionEntryEndOffset = 1;
+ unsigned* sd_data =
+ reinterpret_cast<unsigned*>(const_cast<char*>(sd->Data()));
+ CHECK_EQ(sd->Length(),
+ (kHeaderSize + 2 * kFunctionEntrySize) * kUnsignedSize);
+
+ // Overwrite function bar's end position with 0.
+ sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryEndOffset] = 0;
+ v8::TryCatch try_catch;
+
+ Local<String> source = String::New(script);
+ Local<Script> compiled_script = Script::New(source, NULL, sd);
+ CHECK(try_catch.HasCaught());
+ String::AsciiValue exception_value(try_catch.Message()->Get());
+ CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
+ *exception_value);
+
+ try_catch.Reset();
+ // Overwrite function bar's start position with 200. The function entry
+ // will not be found when searching for it by position.
+ sd_data[kHeaderSize + 1 * kFunctionEntrySize + kFunctionEntryStartOffset] =
+ 200;
+ compiled_script = Script::New(source, NULL, sd);
+ CHECK(try_catch.HasCaught());
+ String::AsciiValue second_exception_value(try_catch.Message()->Get());
+ CHECK_EQ("Uncaught SyntaxError: Invalid preparser data for function bar",
+ *second_exception_value);
+
+ delete sd;
+}
+
+
// Verifies that the Handle<String> and const char* versions of the API produce
// the same results (at least for one trivial case).
TEST(PreCompileAPIVariationsAreSame) {
diff --git a/deps/v8/test/cctest/test-debug.cc b/deps/v8/test/cctest/test-debug.cc
index e689637865..8ebf7522a6 100644
--- a/deps/v8/test/cctest/test-debug.cc
+++ b/deps/v8/test/cctest/test-debug.cc
@@ -2075,6 +2075,39 @@ TEST(ScriptBreakPointLineTopLevel) {
}
+// Test that it is possible to add and remove break points in a top level
+// function which has no references but has not been collected yet.
+TEST(ScriptBreakPointTopLevelCrash) {
+ v8::HandleScope scope;
+ DebugLocalContext env;
+ env.ExposeDebug();
+
+ v8::Debug::SetDebugEventListener(DebugEventBreakPointHitCount,
+ v8::Undefined());
+
+ v8::Local<v8::String> script_source = v8::String::New(
+ "function f() {\n"
+ " return 0;\n"
+ "}\n"
+ "f()");
+
+ int sbp1 = SetScriptBreakPointByNameFromJS("test.html", 3, -1);
+ {
+ v8::HandleScope scope;
+ break_point_hit_count = 0;
+ v8::Script::Compile(script_source, v8::String::New("test.html"))->Run();
+ CHECK_EQ(1, break_point_hit_count);
+ }
+
+ int sbp2 = SetScriptBreakPointByNameFromJS("test.html", 3, -1);
+ ClearBreakPointFromJS(sbp1);
+ ClearBreakPointFromJS(sbp2);
+
+ v8::Debug::SetDebugEventListener(NULL);
+ CheckDebuggerUnloaded();
+}
+
+
// Test that it is possible to remove the last break point for a function
// inside the break handling of that break point.
TEST(RemoveBreakPointInBreak) {
@@ -2129,7 +2162,7 @@ TEST(DebuggerStatement) {
}
-// Test setting a breakpoint on the debugger statement.
+// Test setting a breakpoint on the debugger statement.
TEST(DebuggerStatementBreakpoint) {
break_point_hit_count = 0;
v8::HandleScope scope;
diff --git a/deps/v8/test/cctest/test-disasm-arm.cc b/deps/v8/test/cctest/test-disasm-arm.cc
index 18e641d6a3..f890fc10a0 100644
--- a/deps/v8/test/cctest/test-disasm-arm.cc
+++ b/deps/v8/test/cctest/test-disasm-arm.cc
@@ -408,6 +408,11 @@ TEST(Vfp) {
if (CpuFeatures::IsSupported(VFP3)) {
CpuFeatures::Scope scope(VFP3);
+ COMPARE(vmov(d0, d1),
+ "eeb00b41 vmov.f64 d0, d1");
+ COMPARE(vmov(d3, d3, eq),
+ "0eb03b43 vmov.f64eq d3, d3");
+
COMPARE(vadd(d0, d1, d2),
"ee310b02 vadd.f64 d0, d1, d2");
COMPARE(vadd(d3, d4, d5, mi),
diff --git a/deps/v8/test/cctest/test-macro-assembler-x64.cc b/deps/v8/test/cctest/test-macro-assembler-x64.cc
index dd97498611..3d2b91b544 100755
--- a/deps/v8/test/cctest/test-macro-assembler-x64.cc
+++ b/deps/v8/test/cctest/test-macro-assembler-x64.cc
@@ -57,10 +57,9 @@ using v8::internal::rsp;
using v8::internal::r8;
using v8::internal::r9;
using v8::internal::r11;
-using v8::internal::r12; // Remember: r12..r15 are callee save!
+using v8::internal::r12;
using v8::internal::r13;
using v8::internal::r14;
-using v8::internal::r15;
using v8::internal::times_pointer_size;
using v8::internal::FUNCTION_CAST;
using v8::internal::CodeDesc;
@@ -92,6 +91,24 @@ typedef int (*F0)();
#define __ masm->
+
+static void EntryCode(MacroAssembler* masm) {
+ // Smi constant register is callee save.
+ __ push(v8::internal::kSmiConstantRegister);
+ __ InitializeSmiConstantRegister();
+}
+
+
+static void ExitCode(MacroAssembler* masm) {
+ // Return -1 if kSmiConstantRegister was clobbered during the test.
+ __ Move(rdx, Smi::FromInt(1));
+ __ cmpq(rdx, v8::internal::kSmiConstantRegister);
+ __ movq(rdx, Immediate(-1));
+ __ cmovq(not_equal, rax, rdx);
+ __ pop(v8::internal::kSmiConstantRegister);
+}
+
+
TEST(Smi) {
// Check that C++ Smi operations work as expected.
int64_t test_numbers[] = {
@@ -139,6 +156,7 @@ TEST(SmiMove) {
MacroAssembler assembler(buffer, static_cast<int>(actual_size));
MacroAssembler* masm = &assembler; // Create a pointer for the __ macro.
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
TestMoveSmi(masm, &exit, 1, Smi::FromInt(0));
@@ -156,6 +174,7 @@ TEST(SmiMove) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -225,6 +244,7 @@ TEST(SmiCompare) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
TestSmiCompare(masm, &exit, 0x10, 0, 0);
@@ -249,6 +269,7 @@ TEST(SmiCompare) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -272,6 +293,7 @@ TEST(Integer32ToSmi) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
__ movq(rax, Immediate(1)); // Test number.
@@ -349,6 +371,7 @@ TEST(Integer32ToSmi) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -397,6 +420,7 @@ TEST(Integer64PlusConstantToSmi) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
int64_t twice_max = static_cast<int64_t>(Smi::kMaxValue) * 2;
@@ -416,6 +440,7 @@ TEST(Integer64PlusConstantToSmi) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -438,6 +463,7 @@ TEST(SmiCheck) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
Condition cond;
@@ -613,6 +639,7 @@ TEST(SmiCheck) {
__ xor_(rax, rax);
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -683,6 +710,7 @@ TEST(SmiNeg) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
TestSmiNeg(masm, &exit, 0x10, 0);
@@ -696,6 +724,7 @@ TEST(SmiNeg) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -768,6 +797,7 @@ TEST(SmiAdd) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
// No-overflow tests.
@@ -782,6 +812,7 @@ TEST(SmiAdd) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -955,6 +986,7 @@ TEST(SmiSub) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
SmiSubTest(masm, &exit, 0x10, 1, 2);
@@ -977,6 +1009,7 @@ TEST(SmiSub) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -1042,6 +1075,7 @@ TEST(SmiMul) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
TestSmiMul(masm, &exit, 0x10, 0, 0);
@@ -1061,6 +1095,7 @@ TEST(SmiMul) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -1081,51 +1116,51 @@ void TestSmiDiv(MacroAssembler* masm, Label* exit, int id, int x, int y) {
#endif
bool fraction = !division_by_zero && !overflow && (x % y != 0);
__ Move(r11, Smi::FromInt(x));
- __ Move(r12, Smi::FromInt(y));
+ __ Move(r14, Smi::FromInt(y));
if (!fraction && !overflow && !negative_zero && !division_by_zero) {
// Division succeeds
__ movq(rcx, r11);
- __ movq(r15, Immediate(id));
+ __ movq(r12, Immediate(id));
int result = x / y;
__ Move(r8, Smi::FromInt(result));
- __ SmiDiv(r9, rcx, r12, exit);
- // Might have destroyed rcx and r12.
- __ incq(r15);
+ __ SmiDiv(r9, rcx, r14, exit);
+ // Might have destroyed rcx and r14.
+ __ incq(r12);
__ SmiCompare(r9, r8);
__ j(not_equal, exit);
- __ incq(r15);
+ __ incq(r12);
__ movq(rcx, r11);
- __ Move(r12, Smi::FromInt(y));
+ __ Move(r14, Smi::FromInt(y));
__ SmiCompare(rcx, r11);
__ j(not_equal, exit);
- __ incq(r15);
- __ SmiDiv(rcx, rcx, r12, exit);
+ __ incq(r12);
+ __ SmiDiv(rcx, rcx, r14, exit);
- __ incq(r15);
+ __ incq(r12);
__ SmiCompare(rcx, r8);
__ j(not_equal, exit);
} else {
// Division fails.
- __ movq(r15, Immediate(id + 8));
+ __ movq(r12, Immediate(id + 8));
Label fail_ok, fail_ok2;
__ movq(rcx, r11);
- __ SmiDiv(r9, rcx, r12, &fail_ok);
+ __ SmiDiv(r9, rcx, r14, &fail_ok);
__ jmp(exit);
__ bind(&fail_ok);
- __ incq(r15);
+ __ incq(r12);
__ SmiCompare(rcx, r11);
__ j(not_equal, exit);
- __ incq(r15);
- __ SmiDiv(rcx, rcx, r12, &fail_ok2);
+ __ incq(r12);
+ __ SmiDiv(rcx, rcx, r14, &fail_ok2);
__ jmp(exit);
__ bind(&fail_ok2);
- __ incq(r15);
+ __ incq(r12);
__ SmiCompare(rcx, r11);
__ j(not_equal, exit);
}
@@ -1145,10 +1180,11 @@ TEST(SmiDiv) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
+ __ push(r14);
__ push(r12);
- __ push(r15);
TestSmiDiv(masm, &exit, 0x10, 1, 1);
TestSmiDiv(masm, &exit, 0x20, 1, 0);
TestSmiDiv(masm, &exit, 0x30, -1, 0);
@@ -1170,11 +1206,12 @@ TEST(SmiDiv) {
TestSmiDiv(masm, &exit, 0x130, Smi::kMinValue, Smi::kMinValue);
TestSmiDiv(masm, &exit, 0x140, Smi::kMinValue, -1);
- __ xor_(r15, r15); // Success.
+ __ xor_(r12, r12); // Success.
__ bind(&exit);
- __ movq(rax, r15);
- __ pop(r15);
+ __ movq(rax, r12);
__ pop(r12);
+ __ pop(r14);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -1192,47 +1229,47 @@ void TestSmiMod(MacroAssembler* masm, Label* exit, int id, int x, int y) {
bool negative_zero = (!fraction && x < 0);
__ Move(rcx, Smi::FromInt(x));
__ movq(r11, rcx);
- __ Move(r12, Smi::FromInt(y));
+ __ Move(r14, Smi::FromInt(y));
if (!division_overflow && !negative_zero && !division_by_zero) {
// Modulo succeeds
- __ movq(r15, Immediate(id));
+ __ movq(r12, Immediate(id));
int result = x % y;
__ Move(r8, Smi::FromInt(result));
- __ SmiMod(r9, rcx, r12, exit);
+ __ SmiMod(r9, rcx, r14, exit);
- __ incq(r15);
+ __ incq(r12);
__ SmiCompare(r9, r8);
__ j(not_equal, exit);
- __ incq(r15);
+ __ incq(r12);
__ SmiCompare(rcx, r11);
__ j(not_equal, exit);
- __ incq(r15);
- __ SmiMod(rcx, rcx, r12, exit);
+ __ incq(r12);
+ __ SmiMod(rcx, rcx, r14, exit);
- __ incq(r15);
+ __ incq(r12);
__ SmiCompare(rcx, r8);
__ j(not_equal, exit);
} else {
// Modulo fails.
- __ movq(r15, Immediate(id + 8));
+ __ movq(r12, Immediate(id + 8));
Label fail_ok, fail_ok2;
- __ SmiMod(r9, rcx, r12, &fail_ok);
+ __ SmiMod(r9, rcx, r14, &fail_ok);
__ jmp(exit);
__ bind(&fail_ok);
- __ incq(r15);
+ __ incq(r12);
__ SmiCompare(rcx, r11);
__ j(not_equal, exit);
- __ incq(r15);
- __ SmiMod(rcx, rcx, r12, &fail_ok2);
+ __ incq(r12);
+ __ SmiMod(rcx, rcx, r14, &fail_ok2);
__ jmp(exit);
__ bind(&fail_ok2);
- __ incq(r15);
+ __ incq(r12);
__ SmiCompare(rcx, r11);
__ j(not_equal, exit);
}
@@ -1252,10 +1289,11 @@ TEST(SmiMod) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
+ __ push(r14);
__ push(r12);
- __ push(r15);
TestSmiMod(masm, &exit, 0x10, 1, 1);
TestSmiMod(masm, &exit, 0x20, 1, 0);
TestSmiMod(masm, &exit, 0x30, -1, 0);
@@ -1277,11 +1315,12 @@ TEST(SmiMod) {
TestSmiMod(masm, &exit, 0x130, Smi::kMinValue, Smi::kMinValue);
TestSmiMod(masm, &exit, 0x140, Smi::kMinValue, -1);
- __ xor_(r15, r15); // Success.
+ __ xor_(r12, r12); // Success.
__ bind(&exit);
- __ movq(rax, r15);
- __ pop(r15);
+ __ movq(rax, r12);
__ pop(r12);
+ __ pop(r14);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -1336,7 +1375,7 @@ TEST(SmiIndex) {
// Allocate an executable page of memory.
size_t actual_size;
byte* buffer =
- static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 2,
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 3,
&actual_size,
true));
CHECK(buffer);
@@ -1345,6 +1384,7 @@ TEST(SmiIndex) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
TestSmiIndex(masm, &exit, 0x10, 0);
@@ -1355,6 +1395,7 @@ TEST(SmiIndex) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -1411,6 +1452,7 @@ TEST(SmiSelectNonSmi) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false); // Avoid inline checks.
+ EntryCode(masm);
Label exit;
TestSelectNonSmi(masm, &exit, 0x10, 0, 0);
@@ -1425,6 +1467,7 @@ TEST(SmiSelectNonSmi) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -1487,6 +1530,7 @@ TEST(SmiAnd) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
TestSmiAnd(masm, &exit, 0x10, 0, 0);
@@ -1503,6 +1547,7 @@ TEST(SmiAnd) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -1565,6 +1610,7 @@ TEST(SmiOr) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
TestSmiOr(masm, &exit, 0x10, 0, 0);
@@ -1583,6 +1629,7 @@ TEST(SmiOr) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -1645,6 +1692,7 @@ TEST(SmiXor) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
TestSmiXor(masm, &exit, 0x10, 0, 0);
@@ -1663,6 +1711,7 @@ TEST(SmiXor) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -1709,6 +1758,7 @@ TEST(SmiNot) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
TestSmiNot(masm, &exit, 0x10, 0);
@@ -1722,6 +1772,7 @@ TEST(SmiNot) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -1793,7 +1844,7 @@ TEST(SmiShiftLeft) {
// Allocate an executable page of memory.
size_t actual_size;
byte* buffer =
- static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 3,
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 4,
&actual_size,
true));
CHECK(buffer);
@@ -1802,6 +1853,7 @@ TEST(SmiShiftLeft) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
TestSmiShiftLeft(masm, &exit, 0x10, 0);
@@ -1814,6 +1866,7 @@ TEST(SmiShiftLeft) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -1896,7 +1949,7 @@ TEST(SmiShiftLogicalRight) {
// Allocate an executable page of memory.
size_t actual_size;
byte* buffer =
- static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 2,
+ static_cast<byte*>(OS::Allocate(Assembler::kMinimalBufferSize * 3,
&actual_size,
true));
CHECK(buffer);
@@ -1905,6 +1958,7 @@ TEST(SmiShiftLogicalRight) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
TestSmiShiftLogicalRight(masm, &exit, 0x10, 0);
@@ -1917,6 +1971,7 @@ TEST(SmiShiftLogicalRight) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -1971,6 +2026,7 @@ TEST(SmiShiftArithmeticRight) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
TestSmiShiftArithmeticRight(masm, &exit, 0x10, 0);
@@ -1983,6 +2039,7 @@ TEST(SmiShiftArithmeticRight) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -2032,6 +2089,7 @@ TEST(PositiveSmiTimesPowerOfTwoToInteger64) {
MacroAssembler* masm = &assembler;
masm->set_allow_stub_calls(false);
+ EntryCode(masm);
Label exit;
TestPositiveSmiPowerUp(masm, &exit, 0x20, 0);
@@ -2046,6 +2104,7 @@ TEST(PositiveSmiTimesPowerOfTwoToInteger64) {
__ xor_(rax, rax); // Success.
__ bind(&exit);
+ ExitCode(masm);
__ ret(0);
CodeDesc desc;
@@ -2074,8 +2133,9 @@ TEST(OperandOffset) {
masm->set_allow_stub_calls(false);
Label exit;
- __ push(r12);
+ EntryCode(masm);
__ push(r13);
+ __ push(r14);
__ push(rbx);
__ push(rbp);
__ push(Immediate(0x100)); // <-- rbp
@@ -2093,7 +2153,7 @@ TEST(OperandOffset) {
// r12 = rsp[3]
// rbx = rsp[5]
// r13 = rsp[7]
- __ lea(r12, Operand(rsp, 3 * kPointerSize));
+ __ lea(r14, Operand(rsp, 3 * kPointerSize));
__ lea(r13, Operand(rbp, -3 * kPointerSize));
__ lea(rbx, Operand(rbp, -5 * kPointerSize));
__ movl(rcx, Immediate(2));
@@ -2396,8 +2456,9 @@ TEST(OperandOffset) {
__ lea(rsp, Operand(rbp, kPointerSize));
__ pop(rbp);
__ pop(rbx);
+ __ pop(r14);
__ pop(r13);
- __ pop(r12);
+ ExitCode(masm);
__ ret(0);
diff --git a/deps/v8/test/es5conform/es5conform.status b/deps/v8/test/es5conform/es5conform.status
index 8e1e941e02..e461349f26 100644
--- a/deps/v8/test/es5conform/es5conform.status
+++ b/deps/v8/test/es5conform/es5conform.status
@@ -44,7 +44,6 @@ chapter11/11.1/11.1.5: UNIMPLEMENTED
chapter11/11.4/11.4.1//11.4.1-4.a-5: FAIL
chapter11/11.4/11.4.1//11.4.1-4.a-7: FAIL
-
# We do not have a global object called 'global' as required by tests.
chapter15/15.1: FAIL_OK
@@ -52,14 +51,10 @@ chapter15/15.1: FAIL_OK
chapter15/15.2/15.2.3/15.2.3.8: UNIMPLEMENTED
# NOT IMPLEMENTED: freeze
chapter15/15.2/15.2.3/15.2.3.9: UNIMPLEMENTED
-# NOT IMPLEMENTED: preventExtensions
-chapter15/15.2/15.2.3/15.2.3.10: UNIMPLEMENTED
# NOT IMPLEMENTED: isSealed
chapter15/15.2/15.2.3/15.2.3.11: UNIMPLEMENTED
# NOT IMPLEMENTED: isFrozen
chapter15/15.2/15.2.3/15.2.3.12: UNIMPLEMENTED
-# NOT IMPLEMENTED: isExtensible
-chapter15/15.2/15.2.3/15.2.3.13: UNIMPLEMENTED
# NOT IMPLEMENTED: seal
chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-20: UNIMPLEMENTED
@@ -67,18 +62,12 @@ chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-20: UNIMPLEMENTED
# NOT IMPLEMENTED: freeze
chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-21: UNIMPLEMENTED
-# NOT IMPLEMENTED: preventExtensions
-chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-22: UNIMPLEMENTED
-
# NOT IMPLEMENTED: isSealed
chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-23: UNIMPLEMENTED
# NOT IMPLEMENTED: isFrozen
chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-24: UNIMPLEMENTED
-# NOT IMPLEMENTED: isExtensible
-chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-25: UNIMPLEMENTED
-
# NOT IMPLEMENTED: bind
chapter15/15.2/15.2.3/15.2.3.3/15.2.3.3-4-38: UNIMPLEMENTED
diff --git a/deps/v8/test/mjsunit/call-stub.js b/deps/v8/test/mjsunit/call-stub.js
new file mode 100644
index 0000000000..a9132a6bd0
--- /dev/null
+++ b/deps/v8/test/mjsunit/call-stub.js
@@ -0,0 +1,51 @@
+// 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.
+
+function Hash() {
+ for (var i = 0; i < 100; i++) {
+ this['a' + i] = i;
+ }
+
+ delete this.a50; // Ensure it's a normal object.
+}
+
+Hash.prototype.m = function() {
+ return 1;
+};
+
+var h = new Hash();
+
+for (var i = 1; i < 100; i++) {
+ if (i == 50) {
+ h.m = function() {
+ return 2;
+ };
+ } else if (i == 70) {
+ delete h.m;
+ }
+ assertEquals(i < 50 || i >= 70 ? 1 : 2, h.m());
+}
diff --git a/deps/v8/test/mjsunit/date.js b/deps/v8/test/mjsunit/date.js
index b264a19c3d..57fc5a0e8e 100644
--- a/deps/v8/test/mjsunit/date.js
+++ b/deps/v8/test/mjsunit/date.js
@@ -154,6 +154,15 @@ function testToLocaleTimeString() {
testToLocaleTimeString();
+// Test that -0 is treated correctly in MakeDay.
+var d = new Date();
+assertDoesNotThrow("d.setDate(-0)");
+assertDoesNotThrow("new Date(-0, -0, -0, -0, -0, -0. -0)");
+assertDoesNotThrow("new Date(0x40000000, 0x40000000, 0x40000000," +
+ "0x40000000, 0x40000000, 0x40000000, 0x40000000)")
+assertDoesNotThrow("new Date(-0x40000001, -0x40000001, -0x40000001," +
+ "-0x40000001, -0x40000001, -0x40000001, -0x40000001)")
+
// Modified test from WebKit
// LayoutTests/fast/js/script-tests/date-utc-timeclip.js:
diff --git a/deps/v8/test/mjsunit/debug-liveedit-3.js b/deps/v8/test/mjsunit/debug-liveedit-3.js
index b68e38dd49..b2106579d8 100644
--- a/deps/v8/test/mjsunit/debug-liveedit-3.js
+++ b/deps/v8/test/mjsunit/debug-liveedit-3.js
@@ -57,7 +57,8 @@ var new_source = script.source.replace(function_z_text, "function Intermediate()
print("new source: " + new_source);
var change_log = new Array();
-Debug.LiveEdit.SetScriptSource(script, new_source, change_log);
+var result = Debug.LiveEdit.SetScriptSource(script, new_source, false, change_log);
+print("Result: " + JSON.stringify(result) + "\n");
print("Change log: " + JSON.stringify(change_log) + "\n");
assertEquals(8, z6());
diff --git a/deps/v8/test/mjsunit/debug-liveedit-breakpoints.js b/deps/v8/test/mjsunit/debug-liveedit-breakpoints.js
index 5c61cf49d2..f01a8c481d 100644
--- a/deps/v8/test/mjsunit/debug-liveedit-breakpoints.js
+++ b/deps/v8/test/mjsunit/debug-liveedit-breakpoints.js
@@ -72,7 +72,8 @@ var new_source = script.source.replace(function_z_text, "");
print("new source: " + new_source);
var change_log = new Array();
-Debug.LiveEdit.SetScriptSource(script, new_source, change_log);
+var result = Debug.LiveEdit.SetScriptSource(script, new_source, false, change_log);
+print("Result: " + JSON.stringify(result) + "\n");
print("Change log: " + JSON.stringify(change_log) + "\n");
var breaks = Debug.scriptBreakPoints();
diff --git a/deps/v8/test/mjsunit/debug-liveedit-newsource.js b/deps/v8/test/mjsunit/debug-liveedit-newsource.js
index db256a48fe..7b8945a705 100644
--- a/deps/v8/test/mjsunit/debug-liveedit-newsource.js
+++ b/deps/v8/test/mjsunit/debug-liveedit-newsource.js
@@ -57,7 +57,8 @@ var new_source = new_source.replace("17", "18");
print("new source: " + new_source);
var change_log = new Array();
-Debug.LiveEdit.SetScriptSource(script, new_source, change_log);
+var result = Debug.LiveEdit.SetScriptSource(script, new_source, false, change_log);
+print("Result: " + JSON.stringify(result) + "\n");
print("Change log: " + JSON.stringify(change_log) + "\n");
assertEquals("Capybara", ChooseAnimal());
diff --git a/deps/v8/test/mjsunit/fuzz-natives.js b/deps/v8/test/mjsunit/fuzz-natives.js
index 78245c39df..66841bbd60 100644
--- a/deps/v8/test/mjsunit/fuzz-natives.js
+++ b/deps/v8/test/mjsunit/fuzz-natives.js
@@ -63,7 +63,7 @@ function testArgumentCount(name, argc) {
try {
func = makeFunction(name, i);
} catch (e) {
- if (e != "SyntaxError: illegal access") throw e;
+ if (e != "SyntaxError: Illegal access") throw e;
}
if (func === null && i == argc) {
throw "unexpected exception";
diff --git a/deps/v8/test/mjsunit/math-min-max.js b/deps/v8/test/mjsunit/math-min-max.js
index f9475d6fa5..72d8ba30d9 100644
--- a/deps/v8/test/mjsunit/math-min-max.js
+++ b/deps/v8/test/mjsunit/math-min-max.js
@@ -42,7 +42,16 @@ assertEquals(1.1, Math.min(2.2, 3.3, 1.1));
// Prepare a non-Smi zero value.
function returnsNonSmi(){ return 0.25; }
-var ZERO = returnsNonSmi() - returnsNonSmi();
+var ZERO = (function() {
+ var z;
+ // We have to have a loop here because the first time we get a Smi from the
+ // runtime system. After a while the binary op IC settles down and we get
+ // a non-Smi from the generated code.
+ for (var i = 0; i < 10; i++) {
+ z = returnsNonSmi() - returnsNonSmi();
+ }
+ return z;
+})();
assertEquals(0, ZERO);
assertEquals(Infinity, 1/ZERO);
assertEquals(-Infinity, 1/-ZERO);
diff --git a/deps/v8/test/mjsunit/math-pow.js b/deps/v8/test/mjsunit/math-pow.js
new file mode 100644
index 0000000000..e732955c98
--- /dev/null
+++ b/deps/v8/test/mjsunit/math-pow.js
@@ -0,0 +1,129 @@
+// 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 special cases specified by ES 15.8.2.13
+
+// Simple sanity check
+assertEquals(4, Math.pow(2, 2));
+assertEquals(2147483648, Math.pow(2, 31));
+assertEquals(0.25, Math.pow(2, -2));
+assertEquals(0.0625, Math.pow(2, -4));
+assertEquals(1, Math.pow(1, 100));
+assertEquals(0, Math.pow(0, 1000));
+
+// Spec tests
+assertEquals(NaN, Math.pow(2, NaN));
+assertEquals(NaN, Math.pow(+0, NaN));
+assertEquals(NaN, Math.pow(-0, NaN));
+assertEquals(NaN, Math.pow(Infinity, NaN));
+assertEquals(NaN, Math.pow(-Infinity, NaN));
+
+assertEquals(1, Math.pow(NaN, +0));
+assertEquals(1, Math.pow(NaN, -0));
+
+assertEquals(NaN, Math.pow(NaN, NaN));
+assertEquals(NaN, Math.pow(NaN, 2.2));
+assertEquals(NaN, Math.pow(NaN, 1));
+assertEquals(NaN, Math.pow(NaN, -1));
+assertEquals(NaN, Math.pow(NaN, -2.2));
+assertEquals(NaN, Math.pow(NaN, Infinity));
+assertEquals(NaN, Math.pow(NaN, -Infinity));
+
+assertEquals(Infinity, Math.pow(1.1, Infinity));
+assertEquals(Infinity, Math.pow(-1.1, Infinity));
+assertEquals(Infinity, Math.pow(2, Infinity));
+assertEquals(Infinity, Math.pow(-2, Infinity));
+
+assertEquals(+0, Math.pow(1.1, -Infinity));
+assertEquals(+0, Math.pow(-1.1, -Infinity));
+assertEquals(+0, Math.pow(2, -Infinity));
+assertEquals(+0, Math.pow(-2, -Infinity));
+
+assertEquals(NaN, Math.pow(1, Infinity));
+assertEquals(NaN, Math.pow(1, -Infinity));
+assertEquals(NaN, Math.pow(-1, Infinity));
+assertEquals(NaN, Math.pow(-1, -Infinity));
+
+assertEquals(+0, Math.pow(0.1, Infinity));
+assertEquals(+0, Math.pow(-0.1, Infinity));
+assertEquals(+0, Math.pow(0.999, Infinity));
+assertEquals(+0, Math.pow(-0.999, Infinity));
+
+assertEquals(Infinity, Math.pow(0.1, -Infinity));
+assertEquals(Infinity, Math.pow(-0.1, -Infinity));
+assertEquals(Infinity, Math.pow(0.999, -Infinity));
+assertEquals(Infinity, Math.pow(-0.999, -Infinity));
+
+assertEquals(Infinity, Math.pow(Infinity, 0.1));
+assertEquals(Infinity, Math.pow(Infinity, 2));
+
+assertEquals(+0, Math.pow(Infinity, -0.1));
+assertEquals(+0, Math.pow(Infinity, -2));
+
+assertEquals(-Infinity, Math.pow(-Infinity, 3));
+assertEquals(-Infinity, Math.pow(-Infinity, 13));
+
+assertEquals(Infinity, Math.pow(-Infinity, 3.1));
+assertEquals(Infinity, Math.pow(-Infinity, 2));
+
+assertEquals(-0, Math.pow(-Infinity, -3));
+assertEquals(-0, Math.pow(-Infinity, -13));
+
+assertEquals(+0, Math.pow(-Infinity, -3.1));
+assertEquals(+0, Math.pow(-Infinity, -2));
+
+assertEquals(+0, Math.pow(+0, 1.1));
+assertEquals(+0, Math.pow(+0, 2));
+
+assertEquals(Infinity, Math.pow(+0, -1.1));
+assertEquals(Infinity, Math.pow(+0, -2));
+
+assertEquals(-0, Math.pow(-0, 3));
+assertEquals(-0, Math.pow(-0, 13));
+
+assertEquals(+0, Math.pow(-0, 3.1));
+assertEquals(+0, Math.pow(-0, 2));
+
+assertEquals(-Infinity, Math.pow(-0, -3));
+assertEquals(-Infinity, Math.pow(-0, -13));
+
+assertEquals(Infinity, Math.pow(-0, -3.1));
+assertEquals(Infinity, Math.pow(-0, -2));
+
+assertEquals(NaN, Math.pow(-0.00001, 1.1));
+assertEquals(NaN, Math.pow(-0.00001, -1.1));
+assertEquals(NaN, Math.pow(-1.1, 1.1));
+assertEquals(NaN, Math.pow(-1.1, -1.1));
+assertEquals(NaN, Math.pow(-2, 1.1));
+assertEquals(NaN, Math.pow(-2, -1.1));
+assertEquals(NaN, Math.pow(-1000, 1.1));
+assertEquals(NaN, Math.pow(-1000, -1.1));
+
+// Tests from Sputnik S8.5_A13_T1.
+assertTrue((1*((Math.pow(2,53))-1)*(Math.pow(2,-1074))) === 4.4501477170144023e-308);
+assertTrue((1*(Math.pow(2,52))*(Math.pow(2,-1074))) === 2.2250738585072014e-308);
+assertTrue((-1*(Math.pow(2,52))*(Math.pow(2,-1074))) === -2.2250738585072014e-308);
diff --git a/deps/v8/test/mjsunit/object-prevent-extensions.js b/deps/v8/test/mjsunit/object-prevent-extensions.js
new file mode 100644
index 0000000000..ebc2cfa69d
--- /dev/null
+++ b/deps/v8/test/mjsunit/object-prevent-extensions.js
@@ -0,0 +1,157 @@
+// 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.preventExtensions method - ES 15.2.3.10
+
+
+var obj1 = {};
+// Extensible defaults to true.
+assertTrue(Object.isExtensible(obj1));
+Object.preventExtensions(obj1);
+
+// Make sure the is_extensible flag is set.
+assertFalse(Object.isExtensible(obj1));
+// Try adding a new property.
+try {
+ obj1.x = 42;
+ assertUnreachable();
+} catch (e) {
+ assertTrue(/object is not extensible/.test(e));
+}
+assertEquals(undefined, obj1.x);
+
+// Try adding a new element.
+try {
+ obj1[1] = 42;
+ assertUnreachable();
+} catch (e) {
+ assertTrue(/object is not extensible/.test(e));
+}
+assertEquals(undefined, obj1[1]);
+
+
+// Try when the object has an existing property.
+var obj2 = {};
+assertTrue(Object.isExtensible(obj2));
+obj2.x = 42;
+assertEquals(42, obj2.x);
+assertTrue(Object.isExtensible(obj2));
+
+Object.preventExtensions(obj2);
+assertEquals(42, obj2.x);
+
+try {
+ obj2.y = 42;
+ assertUnreachable();
+} catch (e) {
+ assertTrue(/object is not extensible/.test(e));
+}
+
+// obj2.y should still be undefined.
+assertEquals(undefined, obj2.y);
+// Make sure we can still write values to obj.x.
+obj2.x = 43;
+assertEquals(43, obj2.x)
+
+try {
+ obj2.y = new function() { return 42; };
+ assertUnreachable();
+} catch (e) {
+ assertTrue(/object is not extensible/.test(e));
+}
+// obj2.y should still be undefined.
+assertEquals(undefined, obj2.y);
+assertEquals(43, obj2.x)
+
+try {
+ Object.defineProperty(obj2, "y", {value: 42});
+} catch (e) {
+ assertTrue(/object is not extensible/.test(e));
+}
+
+// obj2.y should still be undefined.
+assertEquals(undefined, obj2.y);
+assertEquals(43, obj2.x);
+
+try {
+ obj2[1] = 42;
+} catch (e) {
+ assertTrue(/object is not extensible/.test(e));
+}
+
+assertEquals(undefined, obj2[1]);
+
+var arr = new Array();
+arr[1] = 10;
+
+Object.preventExtensions(arr);
+
+try {
+ arr[2] = 42;
+ assertUnreachable();
+} catch (e) {
+ assertTrue(/object is not extensible/.test(e));
+}
+assertEquals(10, arr[1]);
+
+// We should still be able to change exiting elements.
+arr[1]= 42;
+assertEquals(42, arr[1]);
+
+
+// Test the the extensible flag is not inherited.
+var parent = {};
+parent.x = 42;
+Object.preventExtensions(parent);
+
+var child = Object.create(parent);
+
+// We should be able to add new properties to the child object.
+child.y = 42;
+
+// This should have no influence on the parent class.
+try {
+ parent.y = 29;
+ assertUnreachable();
+} catch (e) {
+ assertTrue(/object is not extensible/.test(e));
+}
+
+
+// Test that attributes on functions are also handled correctly.
+function foo() {
+ return 42;
+}
+
+Object.preventExtensions(foo);
+
+try {
+ foo.x = 29;
+ assertUnreachable();
+} catch (e) {
+ assertTrue(/object is not extensible/.test(e));
+}
diff --git a/deps/v8/test/mjsunit/store-dictionary.js b/deps/v8/test/mjsunit/store-dictionary.js
new file mode 100644
index 0000000000..45e254bd2b
--- /dev/null
+++ b/deps/v8/test/mjsunit/store-dictionary.js
@@ -0,0 +1,65 @@
+// 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.
+
+// Test dictionary store ICs.
+
+// Function that stores property 'x' on an object.
+function store(obj) { obj.x = 42; }
+
+// Create object and force it to dictionary mode by deleting property.
+var o = { x: 32, y: 33 };
+delete o.y;
+
+// Make the store ic in the 'store' function go into dictionary store
+// case.
+for (var i = 0; i < 3; i++) {
+ store(o);
+}
+assertEquals(42, o.x);
+
+// Test that READ_ONLY property attribute is respected. Make 'x'
+// READ_ONLY.
+Object.defineProperty(o, 'x', { value: 32, writable: false });
+
+// Attempt to store using the store ic in the 'store' function.
+store(o);
+
+// Check that the store did not change the value.
+assertEquals(32, o.x);
+
+// Check that bail-out code works.
+// Smi.
+store(1);
+// Fast case object.
+o = new Object();
+store(o);
+assertEquals(42, o.x);
+// Slow case object without x property.
+delete o.x;
+store(o);
+assertEquals(42, o.x);
+
diff --git a/deps/v8/test/mjsunit/string-replace-with-empty.js b/deps/v8/test/mjsunit/string-replace-with-empty.js
new file mode 100644
index 0000000000..0e1e70a1f6
--- /dev/null
+++ b/deps/v8/test/mjsunit/string-replace-with-empty.js
@@ -0,0 +1,57 @@
+// 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: --expose-externalize-string
+
+assertEquals("0123", "aa0bb1cc2dd3".replace(/[a-z]/g, ""));
+assertEquals("0123", "\u1234a0bb1cc2dd3".replace(/[\u1234a-z]/g, ""));
+
+var expected = "0123";
+var cons = "a0b1c2d3";
+for (var i = 0; i < 5; i++) {
+ expected += expected;
+ cons += cons;
+}
+assertEquals(expected, cons.replace(/[a-z]/g, ""));
+cons = "\u12340b1c2d3";
+for (var i = 0; i < 5; i++) {
+ cons += cons;
+}
+assertEquals(expected, cons.replace(/[\u1234a-z]/g, ""));
+
+cons = "a0b1c2d3";
+for (var i = 0; i < 5; i++) {
+ cons += cons;
+}
+externalizeString(cons, true/* force two-byte */);
+assertEquals(expected, cons.replace(/[a-z]/g, ""));
+cons = "\u12340b1c2d3";
+for (var i = 0; i < 5; i++) {
+ cons += cons;
+}
+externalizeString(cons);
+assertEquals(expected, cons.replace(/[\u1234a-z]/g, ""));
diff --git a/deps/v8/test/mjsunit/value-of.js b/deps/v8/test/mjsunit/value-of.js
new file mode 100644
index 0000000000..1a242c0ec4
--- /dev/null
+++ b/deps/v8/test/mjsunit/value-of.js
@@ -0,0 +1,33 @@
+// 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.
+
+function MyException() { }
+
+var o = new Object();
+o.valueOf = function() { throw new MyException(); }
+
+assertThrows(function() { o + 1 }, MyException);
diff --git a/deps/v8/tools/gyp/v8.gyp b/deps/v8/tools/gyp/v8.gyp
index a92576e8d3..8c7ae373e2 100644
--- a/deps/v8/tools/gyp/v8.gyp
+++ b/deps/v8/tools/gyp/v8.gyp
@@ -75,7 +75,14 @@
'msvs_settings': {
'VCCLCompilerTool': {
'Optimizations': '0',
- 'RuntimeLibrary': '1',
+
+ 'conditions': [
+ ['OS=="win" and component=="shared_library"', {
+ 'RuntimeLibrary': '3', # /MDd
+ }, {
+ 'RuntimeLibrary': '1', # /MTd
+ }],
+ ],
},
'VCLinkerTool': {
'LinkIncremental': '2',
@@ -129,13 +136,20 @@
},
'msvs_settings': {
'VCCLCompilerTool': {
- 'RuntimeLibrary': '0',
'Optimizations': '2',
'InlineFunctionExpansion': '2',
'EnableIntrinsicFunctions': 'true',
'FavorSizeOrSpeed': '0',
'OmitFramePointers': 'true',
'StringPooling': 'true',
+
+ 'conditions': [
+ ['OS=="win" and component=="shared_library"', {
+ 'RuntimeLibrary': '2', #/MD
+ }, {
+ 'RuntimeLibrary': '0', #/MT
+ }],
+ ],
},
'VCLinkerTool': {
'LinkIncremental': '1',
@@ -152,7 +166,6 @@
'targets': [
{
'target_name': 'v8',
- 'type': 'none',
'conditions': [
['v8_use_snapshot=="true"', {
'dependencies': ['v8_snapshot'],
@@ -160,6 +173,18 @@
{
'dependencies': ['v8_nosnapshot'],
}],
+ ['OS=="win" and component=="shared_library"', {
+ 'type': '<(component)',
+ 'sources': [
+ '../../src/v8dll-main.cc',
+ ],
+ 'defines': [
+ 'BUILDING_V8_SHARED'
+ ],
+ },
+ {
+ 'type': 'none',
+ }],
],
'direct_dependent_settings': {
'include_dirs': [
@@ -170,6 +195,13 @@
{
'target_name': 'v8_snapshot',
'type': '<(library)',
+ 'conditions': [
+ ['OS=="win" and component=="shared_library"', {
+ 'defines': [
+ 'BUILDING_V8_SHARED',
+ ],
+ }],
+ ],
'dependencies': [
'mksnapshot#host',
'js2c#host',
@@ -216,7 +248,12 @@
['v8_target_arch=="arm" and host_arch=="x64" and _toolset=="host"', {
'cflags': ['-m32'],
'ldflags': ['-m32'],
- }]
+ }],
+ ['OS=="win" and component=="shared_library"', {
+ 'defines': [
+ 'BUILDING_V8_SHARED',
+ ],
+ }],
]
},
{
@@ -614,6 +651,11 @@
'libraries': [ '-lwinmm.lib' ],
},
}],
+ ['OS=="win" and component=="shared_library"', {
+ 'defines': [
+ 'BUILDING_V8_SHARED'
+ ],
+ }],
],
},
{
@@ -692,10 +734,15 @@
'../../samples/shell.cc',
],
'conditions': [
- [ 'OS=="win"', {
+ ['OS=="win"', {
# This could be gotten by not setting chromium_code, if that's OK.
'defines': ['_CRT_SECURE_NO_WARNINGS'],
}],
+ ['OS=="win" and component=="shared_library"', {
+ 'defines': [
+ 'USING_V8_SHARED',
+ ],
+ }],
],
},
],
diff --git a/deps/v8/tools/js2c.py b/deps/v8/tools/js2c.py
index 64de7d3156..35bf43bc44 100755
--- a/deps/v8/tools/js2c.py
+++ b/deps/v8/tools/js2c.py
@@ -104,19 +104,22 @@ def Validate(lines, file):
def ExpandConstants(lines, constants):
- for key, value in constants.items():
- lines = lines.replace(key, str(value))
+ for key, value in constants:
+ lines = key.sub(str(value), lines)
return lines
def ExpandMacros(lines, macros):
- for name, macro in macros.items():
- start = lines.find(name + '(', 0)
- while start != -1:
+ # We allow macros to depend on the previously declared macros, but
+ # we don't allow self-dependecies or recursion.
+ for name_pattern, macro in reversed(macros):
+ pattern_match = name_pattern.search(lines, 0)
+ while pattern_match is not None:
# Scan over the arguments
- assert lines[start + len(name)] == '('
height = 1
- end = start + len(name) + 1
+ start = pattern_match.start()
+ end = pattern_match.end()
+ assert lines[end - 1] == '('
last_match = end
arg_index = 0
mapping = { }
@@ -139,7 +142,7 @@ def ExpandMacros(lines, macros):
result = macro.expand(mapping)
# Replace the occurrence of the macro with the expansion
lines = lines[:start] + result + lines[end:]
- start = lines.find(name + '(', end)
+ pattern_match = name_pattern.search(lines, start + len(result))
return lines
class TextMacro:
@@ -166,9 +169,10 @@ CONST_PATTERN = re.compile(r'^const\s+([a-zA-Z0-9_]+)\s*=\s*([^;]*);$')
MACRO_PATTERN = re.compile(r'^macro\s+([a-zA-Z0-9_]+)\s*\(([^)]*)\)\s*=\s*([^;]*);$')
PYTHON_MACRO_PATTERN = re.compile(r'^python\s+macro\s+([a-zA-Z0-9_]+)\s*\(([^)]*)\)\s*=\s*([^;]*);$')
+
def ReadMacros(lines):
- constants = { }
- macros = { }
+ constants = []
+ macros = []
for line in lines:
hash = line.find('#')
if hash != -1: line = line[:hash]
@@ -178,14 +182,14 @@ def ReadMacros(lines):
if const_match:
name = const_match.group(1)
value = const_match.group(2).strip()
- constants[name] = value
+ constants.append((re.compile("\\b%s\\b" % name), value))
else:
macro_match = MACRO_PATTERN.match(line)
if macro_match:
name = macro_match.group(1)
args = map(string.strip, macro_match.group(2).split(','))
body = macro_match.group(3).strip()
- macros[name] = TextMacro(args, body)
+ macros.append((re.compile("\\b%s\\(" % name), TextMacro(args, body)))
else:
python_match = PYTHON_MACRO_PATTERN.match(line)
if python_match:
@@ -193,7 +197,7 @@ def ReadMacros(lines):
args = map(string.strip, python_match.group(2).split(','))
body = python_match.group(3).strip()
fun = eval("lambda " + ",".join(args) + ': ' + body)
- macros[name] = PythonMacro(args, fun)
+ macros.append((re.compile("\\b%s\\(" % name), PythonMacro(args, fun)))
else:
raise ("Illegal line: " + line)
return (constants, macros)