X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/f9bf01c6616d5ddcf65b13b33cedf9e387ff7a63..1df5f87f1309a8daa30dabdee855f48ae40d14ab:/runtime/JSObject.h diff --git a/runtime/JSObject.h b/runtime/JSObject.h index 2b31a65..60def6e 100644 --- a/runtime/JSObject.h +++ b/runtime/JSObject.h @@ -26,15 +26,16 @@ #include "ArgList.h" #include "ClassInfo.h" #include "CommonIdentifiers.h" +#include "Completion.h" #include "CallFrame.h" #include "JSCell.h" -#include "JSNumberCell.h" #include "MarkStack.h" #include "PropertySlot.h" #include "PutPropertySlot.h" #include "ScopeChain.h" #include "Structure.h" #include "JSGlobalData.h" +#include "JSString.h" #include namespace JSC { @@ -53,6 +54,9 @@ namespace JSC { class Structure; struct HashTable; + JSObject* throwTypeError(ExecState*, const UString&); + extern const char* StrictModeReadonlyPropertyWriteError; + // ECMA 262-3 8.6.1 // Property attributes enum Attribute { @@ -65,29 +69,29 @@ namespace JSC { Setter = 1 << 6 // property is a setter }; - typedef EncodedJSValue* PropertyStorage; - typedef const EncodedJSValue* ConstPropertyStorage; + typedef WriteBarrierBase* PropertyStorage; + typedef const WriteBarrierBase* ConstPropertyStorage; class JSObject : public JSCell { friend class BatchedTransitionOptimizer; friend class JIT; friend class JSCell; + friend void setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot); public: - explicit JSObject(NonNullPassRefPtr); - - virtual void markChildren(MarkStack&); - ALWAYS_INLINE void markChildrenDirect(MarkStack& markStack); + virtual void visitChildren(SlotVisitor&); + ALWAYS_INLINE void visitChildrenDirect(SlotVisitor&); // The inline virtual destructor cannot be the first virtual function declared // in the class as it results in the vtable being generated as a weak symbol virtual ~JSObject(); JSValue prototype() const; - void setPrototype(JSValue prototype); + void setPrototype(JSGlobalData&, JSValue prototype); + bool setPrototypeWithCycleCheck(JSGlobalData&, JSValue prototype); - void setStructure(NonNullPassRefPtr); - Structure* inheritorID(); + void setStructure(JSGlobalData&, Structure*); + Structure* inheritorID(JSGlobalData&); virtual UString className() const; @@ -105,6 +109,9 @@ namespace JSC { virtual void put(ExecState*, const Identifier& propertyName, JSValue value, PutPropertySlot&); virtual void put(ExecState*, unsigned propertyName, JSValue value); + virtual void putWithAttributes(JSGlobalData*, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot); + virtual void putWithAttributes(JSGlobalData*, const Identifier& propertyName, JSValue value, unsigned attributes); + virtual void putWithAttributes(JSGlobalData*, unsigned propertyName, JSValue value, unsigned attributes); virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot); virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes); virtual void putWithAttributes(ExecState*, unsigned propertyName, JSValue value, unsigned attributes); @@ -130,60 +137,65 @@ namespace JSC { virtual bool toBoolean(ExecState*) const; virtual double toNumber(ExecState*) const; virtual UString toString(ExecState*) const; - virtual JSObject* toObject(ExecState*) const; + virtual JSObject* toObject(ExecState*, JSGlobalObject*) const; virtual JSObject* toThisObject(ExecState*) const; + virtual JSValue toStrictThisObject(ExecState*) const; virtual JSObject* unwrappedObject(); bool getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificFunction) const; // This get function only looks at the property map. - JSValue getDirect(const Identifier& propertyName) const + JSValue getDirect(JSGlobalData& globalData, const Identifier& propertyName) const { - size_t offset = m_structure->get(propertyName); + size_t offset = m_structure->get(globalData, propertyName); return offset != WTF::notFound ? getDirectOffset(offset) : JSValue(); } - JSValue* getDirectLocation(const Identifier& propertyName) + WriteBarrierBase* getDirectLocation(JSGlobalData& globalData, const Identifier& propertyName) { - size_t offset = m_structure->get(propertyName); + size_t offset = m_structure->get(globalData, propertyName); return offset != WTF::notFound ? locationForOffset(offset) : 0; } - JSValue* getDirectLocation(const Identifier& propertyName, unsigned& attributes) + WriteBarrierBase* getDirectLocation(JSGlobalData& globalData, const Identifier& propertyName, unsigned& attributes) { JSCell* specificFunction; - size_t offset = m_structure->get(propertyName, attributes, specificFunction); + size_t offset = m_structure->get(globalData, propertyName, attributes, specificFunction); return offset != WTF::notFound ? locationForOffset(offset) : 0; } - size_t offsetForLocation(JSValue* location) const + size_t offsetForLocation(WriteBarrierBase* location) const { - return location - reinterpret_cast(propertyStorage()); + return location - propertyStorage(); } - void transitionTo(Structure*); + void transitionTo(JSGlobalData&, Structure*); - void removeDirect(const Identifier& propertyName); - bool hasCustomProperties() { return !m_structure->isEmpty(); } + void removeDirect(JSGlobalData&, const Identifier& propertyName); + bool hasCustomProperties() { return m_structure->didTransition(); } bool hasGetterSetterProperties() { return m_structure->hasGetterSetterProperties(); } - void putDirect(const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot); - void putDirect(const Identifier& propertyName, JSValue value, unsigned attr = 0); + bool putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&); + void putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr = 0); + bool putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, PutPropertySlot&); - void putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attr = 0); - void putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot); + void putDirectFunction(JSGlobalData&, const Identifier& propertyName, JSCell*, unsigned attr = 0); + void putDirectFunction(JSGlobalData&, const Identifier& propertyName, JSCell*, unsigned attr, bool checkReadOnly, PutPropertySlot&); void putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr = 0); + void putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr = 0); - void putDirectWithoutTransition(const Identifier& propertyName, JSValue value, unsigned attr = 0); - void putDirectFunctionWithoutTransition(const Identifier& propertyName, JSCell* value, unsigned attr = 0); + void putDirectWithoutTransition(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr = 0); + void putDirectFunctionWithoutTransition(JSGlobalData&, const Identifier& propertyName, JSCell* value, unsigned attr = 0); void putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr = 0); + void putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr = 0); // Fast access to known property offsets. - JSValue getDirectOffset(size_t offset) const { return JSValue::decode(propertyStorage()[offset]); } - void putDirectOffset(size_t offset, JSValue value) { propertyStorage()[offset] = JSValue::encode(value); } + JSValue getDirectOffset(size_t offset) const { return propertyStorage()[offset].get(); } + void putDirectOffset(JSGlobalData& globalData, size_t offset, JSValue value) { propertyStorage()[offset].set(globalData, this, value); } + void putUndefinedAtDirectOffset(size_t offset) { propertyStorage()[offset].setUndefined(); } - void fillGetterPropertySlot(PropertySlot&, JSValue* location); + void fillGetterPropertySlot(PropertySlot&, WriteBarrierBase* location); virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes = 0); virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes = 0); @@ -194,38 +206,68 @@ namespace JSC { virtual bool isGlobalObject() const { return false; } virtual bool isVariableObject() const { return false; } virtual bool isActivationObject() const { return false; } - virtual bool isWatchdogException() const { return false; } - virtual bool isNotAnObjectErrorStub() const { return false; } + virtual bool isStrictModeFunction() const { return false; } + virtual bool isErrorInstance() const { return false; } + + void seal(JSGlobalData&); + void freeze(JSGlobalData&); + virtual void preventExtensions(JSGlobalData&); + bool isSealed(JSGlobalData& globalData) { return m_structure->isSealed(globalData); } + bool isFrozen(JSGlobalData& globalData) { return m_structure->isFrozen(globalData); } + bool isExtensible() { return m_structure->isExtensible(); } + + virtual ComplType exceptionType() const { return Throw; } void allocatePropertyStorage(size_t oldSize, size_t newSize); - void allocatePropertyStorageInline(size_t oldSize, size_t newSize); - bool isUsingInlineStorage() const { return m_structure->isUsingInlineStorage(); } + bool isUsingInlineStorage() const { return static_cast(m_propertyStorage) == static_cast(this + 1); } - static const unsigned inlineStorageCapacity = sizeof(EncodedJSValue) == 2 * sizeof(void*) ? 4 : 3; - static const unsigned nonInlineBaseStorageCapacity = 16; + static const unsigned baseExternalStorageCapacity = 16; - static PassRefPtr createStructure(JSValue prototype) + void flattenDictionaryObject(JSGlobalData& globalData) { - return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount); + m_structure->flattenDictionaryStructure(globalData, this); } - void flattenDictionaryObject() + void putAnonymousValue(JSGlobalData& globalData, unsigned index, JSValue value) + { + ASSERT(index < m_structure->anonymousSlotCount()); + locationForOffset(index)->set(globalData, this, value); + } + void clearAnonymousValue(unsigned index) { - m_structure->flattenDictionaryStructure(this); + ASSERT(index < m_structure->anonymousSlotCount()); + locationForOffset(index)->clear(); } + JSValue getAnonymousValue(unsigned index) const + { + ASSERT(index < m_structure->anonymousSlotCount()); + return locationForOffset(index)->get(); + } + + static size_t offsetOfInlineStorage(); + + static JS_EXPORTDATA const ClassInfo s_info; protected: + static Structure* createStructure(JSGlobalData& globalData, JSValue prototype) + { + return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info); + } + static const unsigned StructureFlags = 0; - void putAnonymousValue(unsigned index, JSValue value) + void putThisToAnonymousValue(unsigned index) { - ASSERT(index < m_structure->anonymousSlotCount()); - *locationForOffset(index) = value; + locationForOffset(index)->setWithoutWriteBarrier(this); } - JSValue getAnonymousValue(unsigned index) const + + // To instantiate objects you likely want JSFinalObject, below. + // To create derived types you likely want JSNonFinalObject, below. + JSObject(JSGlobalData&, Structure*, PropertyStorage inlineStorage); + JSObject(VPtrStealingHackType, PropertyStorage inlineStorage) + : JSCell(VPtrStealingHack) + , m_propertyStorage(inlineStorage) { - ASSERT(index < m_structure->anonymousSlotCount()); - return *locationForOffset(index); } private: @@ -237,40 +279,118 @@ namespace JSC { void getString(ExecState* exec); void isObject(); void isString(); -#if USE(JSVALUE32) - void isNumber(); -#endif - - ConstPropertyStorage propertyStorage() const { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); } - PropertyStorage propertyStorage() { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); } + + ConstPropertyStorage propertyStorage() const { return m_propertyStorage; } + PropertyStorage propertyStorage() { return m_propertyStorage; } - const JSValue* locationForOffset(size_t offset) const + const WriteBarrierBase* locationForOffset(size_t offset) const { - return reinterpret_cast(&propertyStorage()[offset]); + return &propertyStorage()[offset]; } - JSValue* locationForOffset(size_t offset) + WriteBarrierBase* locationForOffset(size_t offset) { - return reinterpret_cast(&propertyStorage()[offset]); + return &propertyStorage()[offset]; } - void putDirectInternal(const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot, JSCell*); - void putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot); + bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&, JSCell*); + bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&); void putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue value, unsigned attr = 0); bool inlineGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const; - Structure* createInheritorID(); - - union { - PropertyStorage m_externalStorage; - EncodedJSValue m_inlineStorage[inlineStorageCapacity]; - }; + Structure* createInheritorID(JSGlobalData&); - RefPtr m_inheritorID; + PropertyStorage m_propertyStorage; + WriteBarrier m_inheritorID; }; + + +#if USE(JSVALUE32_64) +#define JSNonFinalObject_inlineStorageCapacity 4 +#define JSFinalObject_inlineStorageCapacity 6 +#else +#define JSNonFinalObject_inlineStorageCapacity 2 +#define JSFinalObject_inlineStorageCapacity 4 +#endif + +COMPILE_ASSERT((JSFinalObject_inlineStorageCapacity >= JSNonFinalObject_inlineStorageCapacity), final_storage_is_at_least_as_large_as_non_final); + + // JSNonFinalObject is a type of JSObject that has some internal storage, + // but also preserves some space in the collector cell for additional + // data members in derived types. + class JSNonFinalObject : public JSObject { + friend class JSObject; + + public: + static Structure* createStructure(JSGlobalData& globalData, JSValue prototype) + { + return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info); + } + + protected: + explicit JSNonFinalObject(VPtrStealingHackType) + : JSObject(VPtrStealingHack, m_inlineStorage) + { + } + explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure) + : JSObject(globalData, structure, m_inlineStorage) + { + ASSERT(!(OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage) % sizeof(double))); + ASSERT(this->structure()->propertyStorageCapacity() == JSNonFinalObject_inlineStorageCapacity); + } + + private: + WriteBarrier m_inlineStorage[JSNonFinalObject_inlineStorageCapacity]; + }; + + // JSFinalObject is a type of JSObject that contains sufficent internal + // storage to fully make use of the colloctor cell containing it. + class JSFinalObject : public JSObject { + friend class JSObject; + + public: + static JSFinalObject* create(ExecState* exec, Structure* structure) + { + return new (exec) JSFinalObject(exec->globalData(), structure); + } + + static Structure* createStructure(JSGlobalData& globalData, JSValue prototype) + { + return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info); + } + + private: + explicit JSFinalObject(JSGlobalData& globalData, Structure* structure) + : JSObject(globalData, structure, m_inlineStorage) + { + ASSERT(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) % sizeof(double) == 0); + ASSERT(this->structure()->propertyStorageCapacity() == JSFinalObject_inlineStorageCapacity); + } + + static const unsigned StructureFlags = JSObject::StructureFlags | IsJSFinalObject; + + WriteBarrierBase m_inlineStorage[JSFinalObject_inlineStorageCapacity]; + }; + +inline size_t JSObject::offsetOfInlineStorage() +{ + ASSERT(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) == OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage)); + return OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage); +} + +inline JSObject* constructEmptyObject(ExecState* exec, Structure* structure) +{ + return JSFinalObject::create(exec, structure); +} + +inline Structure* createEmptyObjectStructure(JSGlobalData& globalData, JSValue prototype) +{ + return JSFinalObject::createStructure(globalData, prototype); +} + inline JSObject* asObject(JSCell* cell) { ASSERT(cell->isObject()); @@ -282,23 +402,22 @@ inline JSObject* asObject(JSValue value) return asObject(value.asCell()); } -inline JSObject::JSObject(NonNullPassRefPtr structure) - : JSCell(structure.releaseRef()) // ~JSObject balances this ref() +inline JSObject::JSObject(JSGlobalData& globalData, Structure* structure, PropertyStorage inlineStorage) + : JSCell(globalData, structure) + , m_propertyStorage(inlineStorage) { - ASSERT(m_structure->propertyStorageCapacity() == inlineStorageCapacity); + ASSERT(inherits(&s_info)); + ASSERT(m_structure->propertyStorageCapacity() < baseExternalStorageCapacity); ASSERT(m_structure->isEmpty()); ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype())); -#if USE(JSVALUE64) || USE(JSVALUE32_64) - ASSERT(OBJECT_OFFSETOF(JSObject, m_inlineStorage) % sizeof(double) == 0); -#endif + ASSERT(static_cast(inlineStorage) == static_cast(this + 1)); + ASSERT(m_structure->typeInfo().type() == ObjectType); } inline JSObject::~JSObject() { - ASSERT(m_structure); if (!isUsingInlineStorage()) - delete [] m_externalStorage; - m_structure->deref(); + delete [] m_propertyStorage; } inline JSValue JSObject::prototype() const @@ -306,29 +425,43 @@ inline JSValue JSObject::prototype() const return m_structure->storedPrototype(); } -inline void JSObject::setPrototype(JSValue prototype) +inline bool JSObject::setPrototypeWithCycleCheck(JSGlobalData& globalData, JSValue prototype) +{ + JSValue nextPrototypeValue = prototype; + while (nextPrototypeValue && nextPrototypeValue.isObject()) { + JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject(); + if (nextPrototype == this) + return false; + nextPrototypeValue = nextPrototype->prototype(); + } + setPrototype(globalData, prototype); + return true; +} + +inline void JSObject::setPrototype(JSGlobalData& globalData, JSValue prototype) { ASSERT(prototype); - RefPtr newStructure = Structure::changePrototypeTransition(m_structure, prototype); - setStructure(newStructure.release()); + setStructure(globalData, Structure::changePrototypeTransition(globalData, m_structure.get(), prototype)); } -inline void JSObject::setStructure(NonNullPassRefPtr structure) +inline void JSObject::setStructure(JSGlobalData& globalData, Structure* structure) { - m_structure->deref(); - m_structure = structure.releaseRef(); // ~JSObject balances this ref() + ASSERT(structure->typeInfo().overridesVisitChildren() == m_structure->typeInfo().overridesVisitChildren()); + m_structure.set(globalData, this, structure); } -inline Structure* JSObject::inheritorID() +inline Structure* JSObject::inheritorID(JSGlobalData& globalData) { - if (m_inheritorID) + if (m_inheritorID) { + ASSERT(m_inheritorID->isEmpty()); return m_inheritorID.get(); - return createInheritorID(); + } + return createInheritorID(globalData); } inline bool Structure::isUsingInlineStorage() const { - return (propertyStorageCapacity() == JSObject::inlineStorageCapacity); + return propertyStorageCapacity() < JSObject::baseExternalStorageCapacity; } inline bool JSCell::inherits(const ClassInfo* info) const @@ -348,11 +481,11 @@ inline bool JSValue::inherits(const ClassInfo* classInfo) const ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { - if (JSValue* location = getDirectLocation(propertyName)) { - if (m_structure->hasGetterSetterProperties() && location[0].isGetterSetter()) + if (WriteBarrierBase* location = getDirectLocation(exec->globalData(), propertyName)) { + if (m_structure->hasGetterSetterProperties() && location->isGetterSetter()) fillGetterPropertySlot(slot, location); else - slot.setValueSlot(this, location, offsetForLocation(location)); + slot.setValue(this, location->get(), offsetForLocation(location)); return true; } @@ -426,7 +559,7 @@ inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const return jsUndefined(); } -inline void JSObject::putDirectInternal(const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot, JSCell* specificFunction) +inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot, JSCell* specificFunction) { ASSERT(value); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); @@ -434,149 +567,176 @@ inline void JSObject::putDirectInternal(const Identifier& propertyName, JSValue if (m_structure->isDictionary()) { unsigned currentAttributes; JSCell* currentSpecificFunction; - size_t offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction); + size_t offset = m_structure->get(globalData, propertyName, currentAttributes, currentSpecificFunction); if (offset != WTF::notFound) { + // If there is currently a specific function, and there now either isn't, + // or the new value is different, then despecify. if (currentSpecificFunction && (specificFunction != currentSpecificFunction)) - m_structure->despecifyDictionaryFunction(propertyName); + m_structure->despecifyDictionaryFunction(globalData, propertyName); if (checkReadOnly && currentAttributes & ReadOnly) - return; - putDirectOffset(offset, value); - if (!specificFunction && !currentSpecificFunction) + return false; + + putDirectOffset(globalData, offset, value); + // At this point, the objects structure only has a specific value set if previously there + // had been one set, and if the new value being specified is the same (otherwise we would + // have despecified, above). So, if currentSpecificFunction is not set, or if the new + // value is different (or there is no new value), then the slot now has no value - and + // as such it is cachable. + // If there was previously a value, and the new value is the same, then we cannot cache. + if (!currentSpecificFunction || (specificFunction != currentSpecificFunction)) slot.setExistingProperty(this, offset); - return; + return true; } + if (checkReadOnly && !isExtensible()) + return false; + size_t currentCapacity = m_structure->propertyStorageCapacity(); - offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, specificFunction); + offset = m_structure->addPropertyWithoutTransition(globalData, propertyName, attributes, specificFunction); if (currentCapacity != m_structure->propertyStorageCapacity()) allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); ASSERT(offset < m_structure->propertyStorageCapacity()); - putDirectOffset(offset, value); + putDirectOffset(globalData, offset, value); // See comment on setNewProperty call below. if (!specificFunction) slot.setNewProperty(this, offset); - return; + return true; } size_t offset; size_t currentCapacity = m_structure->propertyStorageCapacity(); - if (RefPtr structure = Structure::addPropertyTransitionToExistingStructure(m_structure, propertyName, attributes, specificFunction, offset)) { + if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(m_structure.get(), propertyName, attributes, specificFunction, offset)) { if (currentCapacity != structure->propertyStorageCapacity()) allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity()); ASSERT(offset < structure->propertyStorageCapacity()); - setStructure(structure.release()); - putDirectOffset(offset, value); - // See comment on setNewProperty call below. + setStructure(globalData, structure); + putDirectOffset(globalData, offset, value); + // This is a new property; transitions with specific values are not currently cachable, + // so leave the slot in an uncachable state. if (!specificFunction) slot.setNewProperty(this, offset); - return; + return true; } unsigned currentAttributes; JSCell* currentSpecificFunction; - offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction); + offset = m_structure->get(globalData, propertyName, currentAttributes, currentSpecificFunction); if (offset != WTF::notFound) { if (checkReadOnly && currentAttributes & ReadOnly) - return; + return false; - if (currentSpecificFunction && (specificFunction != currentSpecificFunction)) { - setStructure(Structure::despecifyFunctionTransition(m_structure, propertyName)); - putDirectOffset(offset, value); - // Function transitions are not currently cachable, so leave the slot in an uncachable state. - return; + // There are three possibilities here: + // (1) There is an existing specific value set, and we're overwriting with *the same value*. + // * Do nothing - no need to despecify, but that means we can't cache (a cached + // put could write a different value). Leave the slot in an uncachable state. + // (2) There is a specific value currently set, but we're writing a different value. + // * First, we have to despecify. Having done so, this is now a regular slot + // with no specific value, so go ahead & cache like normal. + // (3) Normal case, there is no specific value set. + // * Go ahead & cache like normal. + if (currentSpecificFunction) { + // case (1) Do the put, then return leaving the slot uncachable. + if (specificFunction == currentSpecificFunction) { + putDirectOffset(globalData, offset, value); + return true; + } + // case (2) Despecify, fall through to (3). + setStructure(globalData, Structure::despecifyFunctionTransition(globalData, m_structure.get(), propertyName)); } - putDirectOffset(offset, value); + + // case (3) set the slot, do the put, return. slot.setExistingProperty(this, offset); - return; + putDirectOffset(globalData, offset, value); + return true; } - // If we have a specific function, we may have got to this point if there is - // already a transition with the correct property name and attributes, but - // specialized to a different function. In this case we just want to give up - // and despecialize the transition. - // In this case we clear the value of specificFunction which will result - // in us adding a non-specific transition, and any subsequent lookup in - // Structure::addPropertyTransitionToExistingStructure will just use that. - if (specificFunction && m_structure->hasTransition(propertyName, attributes)) - specificFunction = 0; + if (checkReadOnly && !isExtensible()) + return false; - RefPtr structure = Structure::addPropertyTransition(m_structure, propertyName, attributes, specificFunction, offset); + Structure* structure = Structure::addPropertyTransition(globalData, m_structure.get(), propertyName, attributes, specificFunction, offset); if (currentCapacity != structure->propertyStorageCapacity()) allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity()); ASSERT(offset < structure->propertyStorageCapacity()); - setStructure(structure.release()); - putDirectOffset(offset, value); - // Function transitions are not currently cachable, so leave the slot in an uncachable state. + setStructure(globalData, structure); + putDirectOffset(globalData, offset, value); + // This is a new property; transitions with specific values are not currently cachable, + // so leave the slot in an uncachable state. if (!specificFunction) slot.setNewProperty(this, offset); + return true; } -inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) { ASSERT(value); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); - putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, getJSFunction(globalData, value)); + return putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, getJSFunction(globalData, value)); } inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes) { PutPropertySlot slot; - putDirectInternal(propertyName, value, attributes, false, slot, getJSFunction(globalData, value)); + putDirectInternal(globalData, propertyName, value, attributes, false, slot, getJSFunction(globalData, value)); } -inline void JSObject::putDirect(const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +inline bool JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) { ASSERT(value); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); - putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, 0); + return putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, 0); } -inline void JSObject::putDirect(const Identifier& propertyName, JSValue value, unsigned attributes) +inline void JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes) { PutPropertySlot slot; - putDirectInternal(propertyName, value, attributes, false, slot, 0); + putDirectInternal(globalData, propertyName, value, attributes, false, slot, 0); +} + +inline bool JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + return putDirectInternal(globalData, propertyName, value, 0, false, slot, 0); } -inline void JSObject::putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +inline void JSObject::putDirectFunction(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) { - putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, value); + putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, value); } -inline void JSObject::putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attr) +inline void JSObject::putDirectFunction(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attr) { PutPropertySlot slot; - putDirectInternal(propertyName, value, attr, false, slot, value); + putDirectInternal(globalData, propertyName, value, attr, false, slot, value); } -inline void JSObject::putDirectWithoutTransition(const Identifier& propertyName, JSValue value, unsigned attributes) +inline void JSObject::putDirectWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes) { size_t currentCapacity = m_structure->propertyStorageCapacity(); - size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, 0); + size_t offset = m_structure->addPropertyWithoutTransition(globalData, propertyName, attributes, 0); if (currentCapacity != m_structure->propertyStorageCapacity()) allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); - putDirectOffset(offset, value); + putDirectOffset(globalData, offset, value); } -inline void JSObject::putDirectFunctionWithoutTransition(const Identifier& propertyName, JSCell* value, unsigned attributes) +inline void JSObject::putDirectFunctionWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attributes) { size_t currentCapacity = m_structure->propertyStorageCapacity(); - size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, value); + size_t offset = m_structure->addPropertyWithoutTransition(globalData, propertyName, attributes, value); if (currentCapacity != m_structure->propertyStorageCapacity()) allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); - putDirectOffset(offset, value); + putDirectOffset(globalData, offset, value); } -inline void JSObject::transitionTo(Structure* newStructure) +inline void JSObject::transitionTo(JSGlobalData& globalData, Structure* newStructure) { if (m_structure->propertyStorageCapacity() != newStructure->propertyStorageCapacity()) allocatePropertyStorage(m_structure->propertyStorageCapacity(), newStructure->propertyStorageCapacity()); - setStructure(newStructure); + setStructure(globalData, newStructure); } inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const @@ -645,6 +805,13 @@ inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValu asCell()->put(exec, propertyName, value, slot); } +inline void JSValue::putDirect(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) +{ + ASSERT(isCell() && isObject()); + if (!asObject(asCell())->putDirect(exec->globalData(), propertyName, value, slot) && slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); +} + inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value) { if (UNLIKELY(!isCell())) { @@ -654,35 +821,48 @@ inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value) asCell()->put(exec, propertyName, value); } -ALWAYS_INLINE void JSObject::allocatePropertyStorageInline(size_t oldSize, size_t newSize) +ALWAYS_INLINE void JSObject::visitChildrenDirect(SlotVisitor& visitor) { - ASSERT(newSize > oldSize); + JSCell::visitChildren(visitor); - // It's important that this function not rely on m_structure, since - // we might be in the middle of a transition. - bool wasInline = (oldSize == JSObject::inlineStorageCapacity); + PropertyStorage storage = propertyStorage(); + size_t storageSize = m_structure->propertyStorageSize(); + visitor.appendValues(storage, storageSize); + if (m_inheritorID) + visitor.append(&m_inheritorID); +} - PropertyStorage oldPropertyStorage = (wasInline ? m_inlineStorage : m_externalStorage); - PropertyStorage newPropertyStorage = new EncodedJSValue[newSize]; +// --- JSValue inlines ---------------------------- - for (unsigned i = 0; i < oldSize; ++i) - newPropertyStorage[i] = oldPropertyStorage[i]; +ALWAYS_INLINE UString JSValue::toThisString(ExecState* exec) const +{ + return isString() ? static_cast(asCell())->value(exec) : toThisObject(exec)->toString(exec); +} - if (!wasInline) - delete [] oldPropertyStorage; +inline JSString* JSValue::toThisJSString(ExecState* exec) const +{ + return isString() ? static_cast(asCell()) : jsString(exec, toThisObject(exec)->toString(exec)); +} - m_externalStorage = newPropertyStorage; +inline JSValue JSValue::toStrictThisObject(ExecState* exec) const +{ + if (!isObject()) + return *this; + return asObject(asCell())->toStrictThisObject(exec); } -ALWAYS_INLINE void JSObject::markChildrenDirect(MarkStack& markStack) +ALWAYS_INLINE JSObject* Register::function() const { - JSCell::markChildren(markStack); + if (!jsValue()) + return 0; + return asObject(jsValue()); +} - markStack.append(prototype()); - - PropertyStorage storage = propertyStorage(); - size_t storageSize = m_structure->propertyStorageSize(); - markStack.appendValues(reinterpret_cast(storage), storageSize); +ALWAYS_INLINE Register Register::withCallee(JSObject* callee) +{ + Register r; + r = JSValue(callee); + return r; } } // namespace JSC