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 object, * behind the scenes and the same rules apply to these values as to * their handles. */ -template class V8EXPORT_INLINE Handle { +template class Handle { public: /** @@ -196,7 +187,7 @@ template 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 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 V8EXPORT_INLINE Handle { * to which they refer are identical. * The handles' references are not checked. */ - template bool operator==(Handle that) const { + template inline bool operator==(Handle that) const { internal::Object** a = reinterpret_cast(**this); internal::Object** b = reinterpret_cast(*that); if (a == 0) return b == 0; @@ -252,7 +243,7 @@ template class V8EXPORT_INLINE Handle { * the objects to which they refer are different. * The handles' references are not checked. */ - template bool operator!=(Handle that) const { + template inline bool operator!=(Handle that) const { return !operator==(that); } @@ -281,7 +272,7 @@ template 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 V8EXPORT_INLINE Local : public Handle { +template class Local : public Handle { public: inline Local(); template inline Local(Local that) @@ -332,7 +323,7 @@ template class V8EXPORT_INLINE Local : public Handle { * different storage cells but rather two references to the same * storage cell. */ -template class V8EXPORT_INLINE Persistent : public Handle { +template class Persistent : public Handle { 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 resource_name, - Handle resource_line_offset = Handle(), - Handle resource_column_offset = Handle()) + inline ScriptOrigin( + Handle resource_name, + Handle resource_line_offset = Handle(), + Handle resource_column_offset = Handle()) : 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 ToBoolean() const; - Local ToNumber() const; - Local ToString() const; - Local ToDetailString() const; - Local ToObject() const; - Local ToInteger() const; - Local ToUint32() const; - Local ToInt32() const; + V8EXPORT Local ToBoolean() const; + V8EXPORT Local ToNumber() const; + V8EXPORT Local ToString() const; + V8EXPORT Local ToDetailString() const; + V8EXPORT Local ToObject() const; + V8EXPORT Local ToInteger() const; + V8EXPORT Local ToUint32() const; + V8EXPORT Local ToInt32() const; /** * Attempts to convert a string to an array index. * Returns an empty handle if the conversion fails. */ - Local ToArrayIndex() const; + V8EXPORT Local 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 that) const; - bool StrictEquals(Handle that) const; + V8EXPORT bool Equals(Handle that) const; + V8EXPORT bool StrictEquals(Handle 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 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 Empty(); + V8EXPORT static v8::Local 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 New(const char* data, int length = -1); + V8EXPORT static Local New(const char* data, int length = -1); /** Allocates a new string from utf16 data.*/ - static Local New(const uint16_t* data, int length = -1); + V8EXPORT static Local New(const uint16_t* data, int length = -1); /** Creates a symbol. Returns one if it exists already.*/ - static Local NewSymbol(const char* data, int length = -1); + V8EXPORT static Local 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 Concat(Handle left, Handleright); + V8EXPORT static Local Concat(Handle left, + Handleright); /** * 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 NewExternal(ExternalStringResource* resource); + V8EXPORT static Local 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 NewExternal(ExternalAsciiStringResource* resource); + V8EXPORT static Local 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 NewUndetectable(const char* data, int length = -1); + V8EXPORT static Local NewUndetectable(const char* data, + int length = -1); /** Creates an undetectable string from the supplied utf-16 data.*/ - static Local NewUndetectable(const uint16_t* data, int length = -1); + V8EXPORT static Local 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 New(double value); + V8EXPORT double Value() const; + V8EXPORT static Local 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 New(int32_t value); - static Local NewFromUnsigned(uint32_t value); - int64_t Value() const; + V8EXPORT static Local New(int32_t value); + V8EXPORT static Local 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 New(double time); + V8EXPORT static Local 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 key, - Handle value, - PropertyAttribute attribs = None); + V8EXPORT bool Set(Handle key, + Handle value, + PropertyAttribute attribs = None); - bool Set(uint32_t index, - Handle value); + V8EXPORT bool Set(uint32_t index, + Handle 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 key, - Handle value, - PropertyAttribute attribs = None); + V8EXPORT bool ForceSet(Handle key, + Handle value, + PropertyAttribute attribs = None); - Local Get(Handle key); + V8EXPORT Local Get(Handle key); - Local Get(uint32_t index); + V8EXPORT Local Get(uint32_t index); // TODO(1245389): Replace the type-specific versions of these // functions with generic ones that accept a Handle key. - bool Has(Handle key); + V8EXPORT bool Has(Handle key); - bool Delete(Handle key); + V8EXPORT bool Delete(Handle key); // Delete a property on this object bypassing interceptors and // ignoring dont-delete attributes. - bool ForceDelete(Handle key); + V8EXPORT bool ForceDelete(Handle 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 name, - AccessorGetter getter, - AccessorSetter setter = 0, - Handle data = Handle(), - AccessControl settings = DEFAULT, - PropertyAttribute attribute = None); + V8EXPORT bool SetAccessor(Handle name, + AccessorGetter getter, + AccessorSetter setter = 0, + Handle data = Handle(), + 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 GetPropertyNames(); + V8EXPORT Local 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 GetPrototype(); + V8EXPORT Local 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 prototype); + V8EXPORT bool SetPrototype(Handle prototype); /** * Finds an instance of the given function template in the prototype * chain. */ - Local FindInstanceInPrototypeChain(Handle tmpl); + V8EXPORT Local FindInstanceInPrototypeChain( + Handle 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 ObjectProtoToString(); + V8EXPORT Local ObjectProtoToString(); /** Gets the number of internal fields for this Object. */ - int InternalFieldCount(); + V8EXPORT int InternalFieldCount(); /** Gets the value in an internal field. */ inline Local GetInternalField(int index); /** Sets the value in an internal field. */ - void SetInternalField(int index, Handle value); + V8EXPORT void SetInternalField(int index, Handle 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 key); - bool HasRealIndexedProperty(uint32_t index); - bool HasRealNamedCallbackProperty(Handle key); + V8EXPORT bool HasRealNamedProperty(Handle key); + V8EXPORT bool HasRealIndexedProperty(uint32_t index); + V8EXPORT bool HasRealNamedCallbackProperty(Handle key); /** * If result.IsEmpty() no real property was located in the prototype chain. * This means interceptors in the prototype chain are not called. */ - Local GetRealNamedPropertyInPrototypeChain(Handle key); + V8EXPORT Local GetRealNamedPropertyInPrototypeChain( + Handle 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 GetRealNamedProperty(Handle key); + V8EXPORT Local GetRealNamedProperty(Handle 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 key, Handle value); - Local GetHiddenValue(Handle key); - bool DeleteHiddenValue(Handle key); + V8EXPORT bool SetHiddenValue(Handle key, Handle value); + V8EXPORT Local GetHiddenValue(Handle key); + V8EXPORT bool DeleteHiddenValue(Handle 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 Clone(); + V8EXPORT Local 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 New(); + V8EXPORT static Local New(); static inline Object* Cast(Value* obj); private: - Object(); - static void CheckCast(Value* obj); - Local CheckedGetInternalField(int index); - void* SlowGetPointerFromInternalField(int index); + V8EXPORT Object(); + V8EXPORT static void CheckCast(Value* obj); + V8EXPORT Local 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 CloneElementAt(uint32_t index); + V8EXPORT Local CloneElementAt(uint32_t index); - static Local New(int length = 0); + V8EXPORT static Local 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 NewInstance() const; - Local NewInstance(int argc, Handle argv[]) const; - Local Call(Handle recv, int argc, Handle argv[]); - void SetName(Handle name); - Handle GetName() const; + V8EXPORT Local NewInstance() const; + V8EXPORT Local NewInstance(int argc, Handle argv[]) const; + V8EXPORT Local Call(Handle recv, + int argc, + Handle argv[]); + V8EXPORT void SetName(Handle name); + V8EXPORT Handle 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 Wrap(void* data); + V8EXPORT static Local Wrap(void* data); static inline void* Unwrap(Handle obj); - static Local New(void* value); + V8EXPORT static Local 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 obj); - static void* FullUnwrap(Handle obj); + V8EXPORT static void* FullUnwrap(Handle 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 operator[](int i) const; @@ -1714,7 +1715,6 @@ class V8EXPORT Arguments { inline bool IsConstructCall() const; inline Local Data() const; private: - Arguments(); friend class ImplementationUtilities; inline Arguments(Local data, Local 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_->Enter(); @@ -3320,6 +3320,17 @@ void Persistent::ClearWeak() { V8::ClearWeak(reinterpret_cast(**this)); } + +Arguments::Arguments(v8::Local data, + v8::Local holder, + v8::Local 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 Arguments::operator[](int i) const { if (i < 0 || length_ <= i) return Local(*Undefined()); return Local(reinterpret_cast(values_ - i)); @@ -3580,7 +3591,6 @@ Local 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 data, - v8::Local holder, - v8::Local 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 @@ -1801,6 +1801,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, 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* 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* 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* 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* 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, ¬_in_new_space); + InNewSpace(object, scratch, ne, ¬_in_new_space); Abort("new-space object passed to RecordWriteHelper"); bind(¬_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(kZapValue))); + mov(address, Operand(BitCast(kZapValue))); + mov(scratch, Operand(BitCast(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 {}; - class OpField: public BitField {}; + class OverwriteField: public BitField {}; + class NegativeZeroField: public BitField {}; + class OpField: public BitField {}; 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::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 obj, void* data) { DebugInfoListNode* node = reinterpret_cast(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); @@ -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 Factory::NewStringFromTwoByte(Vector string, } +Handle Factory::NewRawAsciiString(int length, + PretenureFlag pretenure) { + CALL_HEAP_FUNCTION(Heap::AllocateRawAsciiString(length, pretenure), String); +} + + Handle 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 str, PretenureFlag pretenure = NOT_TENURED); - static Handle NewStringFromTwoByte(Vector str, + static Handle NewStringFromTwoByte( + Vector 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 NewRawAsciiString( + int length, + PretenureFlag pretenure = NOT_TENURED); static Handle 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 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 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 reloc_info) { Address old_addr = code->address(); size_t relocation_offset = - static_cast(code->relocation_start() - old_addr); + static_cast(code->instruction_end() - old_addr); Object* result; if (new_obj_size > MaxObjectSizeInPagedSpace()) { @@ -2446,14 +2455,11 @@ Object* Heap::CopyCode(Code* code, Vector 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 string, Object* Heap::AllocateStringFromUtf8(Vector 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 decoder(Scanner::utf8_decoder()); @@ -2890,6 +2898,7 @@ Object* Heap::AllocateStringFromUtf8(Vector 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, ¬_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, ¬_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(¬_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, ¬_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(¬_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, ¬_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(¬_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, ¬_both_objects); + __ CmpObjectType(eax, FIRST_JS_OBJECT_TYPE, ecx); + __ j(below, ¬_both_objects); + __ CmpObjectType(edx, FIRST_JS_OBJECT_TYPE, ebx); + __ j(below, ¬_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(¬_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(kZapValue))); + mov(address, Immediate(BitCast(kZapValue))); + mov(value, Immediate(BitCast(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(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(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(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(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(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(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(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(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(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(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(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, - Handle name) { + State state, + Handle object, + Handle 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 receiver = Handle::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 receiver = Handle::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 global = Handle::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 global = Handle::cast(receiver); - JSGlobalPropertyCell* cell = - JSGlobalPropertyCell::cast(global->GetPropertyCell(lookup)); - code = StubCache::ComputeStoreGlobal(*name, *global, cell); break; } case CALLBACKS: { @@ -1580,16 +1639,15 @@ Handle GetBinaryOpStub(int key, BinaryOpIC::TypeInfo type_info); Object* BinaryOp_Patch(Arguments args) { - ASSERT(args.length() == 6); + ASSERT(args.length() == 5); Handle left = args.at(0); Handle right = args.at(1); - Handle result = args.at(2); - int key = Smi::cast(args[3])->value(); + int key = Smi::cast(args[2])->value(); + Token::Value op = static_cast(Smi::cast(args[3])->value()); #ifdef DEBUG - Token::Value op = static_cast(Smi::cast(args[4])->value()); BinaryOpIC::TypeInfo prev_type_info = - static_cast(Smi::cast(args[5])->value()); + static_cast(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 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 builtin_function(JSFunction::cast(builtin)); + + bool caught_exception; + Object** builtin_args[] = { right.location() }; + Handle 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 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 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(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(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(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(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 args[1] = {Handle(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 number(Heap::NumberFromUint32(index)); + Handle index_string(Factory::NumberToString(number)); + Handle 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 name, bool* ok); Handle