X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/ba379fdc102753d6be2c4d937058fe40257329fe..a253471d7f8e4d91bf6ebabab00155c3b387d3d0:/runtime/JSObject.h diff --git a/runtime/JSObject.h b/runtime/JSObject.h index ac4706f..d95860d 100644 --- a/runtime/JSObject.h +++ b/runtime/JSObject.h @@ -1,7 +1,7 @@ /* * Copyright (C) 1999-2001 Harri Porten (porten@kde.org) * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -27,29 +27,38 @@ #include "ClassInfo.h" #include "CommonIdentifiers.h" #include "CallFrame.h" -#include "JSNumberCell.h" +#include "JSCell.h" #include "PropertySlot.h" #include "PutPropertySlot.h" #include "ScopeChain.h" +#include "StorageBarrier.h" #include "Structure.h" #include "JSGlobalData.h" +#include "JSString.h" #include namespace JSC { - inline JSCell* getJSFunction(JSGlobalData& globalData, JSValue value) + inline JSCell* getJSFunction(JSValue value) { - if (value.isCell() && (value.asCell()->vptr() == globalData.jsFunctionVPtr)) + if (value.isCell() && (value.asCell()->structure()->typeInfo().type() == JSFunctionType)) return value.asCell(); return 0; } + class GetterSetter; + class HashEntry; class InternalFunction; + class LLIntOffsetsExtractor; + class MarkedBlock; + class PropertyDescriptor; class PropertyNameArray; class Structure; - struct HashEntry; struct HashTable; + JS_EXPORT_PRIVATE JSObject* throwTypeError(ExecState*, const UString&); + extern JS_EXPORTDATA const char* StrictModeReadonlyPropertyWriteError; + // ECMA 262-3 8.6.1 // Property attributes enum Attribute { @@ -58,274 +67,494 @@ namespace JSC { DontEnum = 1 << 2, // property doesn't appear in (for .. in ..) DontDelete = 1 << 3, // property can't be deleted Function = 1 << 4, // property is a function - only used by static hashtables - Getter = 1 << 5, // property is a getter - Setter = 1 << 6 // property is a setter + Accessor = 1 << 5, // property is a getter/setter }; - typedef EncodedJSValue* PropertyStorage; - typedef const EncodedJSValue* ConstPropertyStorage; - class JSObject : public JSCell { friend class BatchedTransitionOptimizer; friend class JIT; friend class JSCell; + friend class MarkedBlock; + JS_EXPORT_PRIVATE friend bool setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot); - public: - explicit JSObject(PassRefPtr); + enum PutMode { + PutModePut, + PutModeDefineOwnProperty, + }; - virtual void mark(); + public: + typedef JSCell Base; - // 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(); + JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&); - bool inherits(const ClassInfo* classInfo) const { return JSCell::isObject(classInfo); } + JS_EXPORT_PRIVATE static UString className(const JSObject*); JSValue prototype() const; - void setPrototype(JSValue prototype); + void setPrototype(JSGlobalData&, JSValue prototype); + bool setPrototypeWithCycleCheck(JSGlobalData&, JSValue prototype); - void setStructure(PassRefPtr); - Structure* inheritorID(); - - virtual UString className() const; + Structure* inheritorID(JSGlobalData&); JSValue get(ExecState*, const Identifier& propertyName) const; JSValue get(ExecState*, unsigned propertyName) const; bool getPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + JS_EXPORT_PRIVATE bool getPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&); - virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); - virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&); + static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&); + JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&); + JS_EXPORT_PRIVATE static bool getOwnPropertyDescriptor(JSObject*, ExecState*, const Identifier&, PropertyDescriptor&); - virtual void put(ExecState*, const Identifier& propertyName, JSValue value, PutPropertySlot&); - virtual void put(ExecState*, unsigned propertyName, JSValue value); + bool allowsAccessFrom(ExecState*); - 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); + JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&); + JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow); + + // putDirect is effectively an unchecked vesion of 'defineOwnProperty': + // - the prototype chain is not consulted + // - accessors are not called. + // - attributes will be respected (after the call the property will exist with the given attributes) + JS_EXPORT_PRIVATE static void putDirectVirtual(JSObject*, ExecState*, const Identifier& propertyName, JSValue, unsigned attributes); + void putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attributes = 0); + void putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, PutPropertySlot&); + void putDirectWithoutTransition(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attributes = 0); + void putDirectAccessor(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attributes); bool propertyIsEnumerable(ExecState*, const Identifier& propertyName) const; - bool hasProperty(ExecState*, const Identifier& propertyName) const; - bool hasProperty(ExecState*, unsigned propertyName) const; + JS_EXPORT_PRIVATE bool hasProperty(ExecState*, const Identifier& propertyName) const; + JS_EXPORT_PRIVATE bool hasProperty(ExecState*, unsigned propertyName) const; bool hasOwnProperty(ExecState*, const Identifier& propertyName) const; - virtual bool deleteProperty(ExecState*, const Identifier& propertyName); - virtual bool deleteProperty(ExecState*, unsigned propertyName); + JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName); + JS_EXPORT_PRIVATE static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName); - virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const; + JS_EXPORT_PRIVATE static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType); - virtual bool hasInstance(ExecState*, JSValue, JSValue prototypeProperty); + JS_EXPORT_PRIVATE static bool hasInstance(JSObject*, ExecState*, JSValue, JSValue prototypeProperty); - virtual void getPropertyNames(ExecState*, PropertyNameArray&); + JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); + JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode); - virtual JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const; - virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue& value); - virtual bool toBoolean(ExecState*) const; - virtual double toNumber(ExecState*) const; - virtual UString toString(ExecState*) const; - virtual JSObject* toObject(ExecState*) const; + JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const; + JS_EXPORT_PRIVATE bool toBoolean(ExecState*) const; + bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const; + JS_EXPORT_PRIVATE double toNumber(ExecState*) const; + JS_EXPORT_PRIVATE JSString* toString(ExecState*) const; - virtual JSObject* toThisObject(ExecState*) const; - virtual JSObject* unwrappedObject(); + // NOTE: JSObject and its subclasses must be able to gracefully handle ExecState* = 0, + // because this call may come from inside the compiler. + JS_EXPORT_PRIVATE static JSObject* toThisObject(JSCell*, ExecState*); + JSObject* unwrappedObject(); - virtual bool getPropertyAttributes(ExecState*, const Identifier& propertyName, unsigned& attributes) const; 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 = 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 = 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 = 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 removeDirect(const Identifier& propertyName); - bool hasCustomProperties() { return !m_structure->isEmpty(); } - 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); + void transitionTo(JSGlobalData&, Structure*); - 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(ExecState* exec, InternalFunction* function, unsigned attr = 0); + void removeDirect(JSGlobalData&, const Identifier& propertyName); + bool hasCustomProperties() { return structure()->didTransition(); } + bool hasGetterSetterProperties() { return structure()->hasGetterSetterProperties(); } - void putDirectWithoutTransition(const Identifier& propertyName, JSValue value, unsigned attr = 0); - void putDirectFunctionWithoutTransition(const Identifier& propertyName, JSCell* value, unsigned attr = 0); - void putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr = 0); + // putOwnDataProperty has 'put' like semantics, however this method: + // - assumes the object contains no own getter/setter properties. + // - provides no special handling for __proto__ + // - does not walk the prototype chain (to check for accessors or non-writable properties). + // This is used by JSActivation. + bool putOwnDataProperty(JSGlobalData&, const Identifier& propertyName, JSValue, PutPropertySlot&); // 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(); } + + JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow); + + bool isGlobalObject() const; + bool isVariableObject() const; + bool isStaticScopeObject() const; + bool isActivationObject() const; + bool isErrorInstance() const; + bool isGlobalThis() const; + + void seal(JSGlobalData&); + void freeze(JSGlobalData&); + JS_EXPORT_PRIVATE void preventExtensions(JSGlobalData&); + bool isSealed(JSGlobalData& globalData) { return structure()->isSealed(globalData); } + bool isFrozen(JSGlobalData& globalData) { return structure()->isFrozen(globalData); } + bool isExtensible() { return structure()->isExtensible(); } + + bool staticFunctionsReified() { return structure()->staticFunctionsReified(); } + void reifyStaticFunctionsForDelete(ExecState* exec); + + JS_EXPORT_PRIVATE PropertyStorage growPropertyStorage(JSGlobalData&, size_t oldSize, size_t newSize); + bool isUsingInlineStorage() const { return static_cast(m_propertyStorage.get()) == static_cast(this + 1); } + void setPropertyStorage(JSGlobalData&, PropertyStorage, Structure*); + + void* addressOfPropertyStorage() + { + return &m_propertyStorage; + } - void fillGetterPropertySlot(PropertySlot&, JSValue* location); + static const unsigned baseExternalStorageCapacity = 16; - virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction); - virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction); - virtual JSValue lookupGetter(ExecState*, const Identifier& propertyName); - virtual JSValue lookupSetter(ExecState*, const Identifier& propertyName); + void flattenDictionaryObject(JSGlobalData& globalData) + { + structure()->flattenDictionaryStructure(globalData, this); + } - 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; } + JSGlobalObject* globalObject() const + { + ASSERT(structure()->globalObject()); + ASSERT(!isGlobalObject() || ((JSObject*)structure()->globalObject()) == this); + return structure()->globalObject(); + } + + static size_t offsetOfInlineStorage(); + static size_t offsetOfPropertyStorage(); + static size_t offsetOfInheritorID(); - void allocatePropertyStorage(size_t oldSize, size_t newSize); - void allocatePropertyStorageInline(size_t oldSize, size_t newSize); - bool isUsingInlineStorage() const { return m_structure->isUsingInlineStorage(); } + static JS_EXPORTDATA const ClassInfo s_info; - static const size_t inlineStorageCapacity = sizeof(EncodedJSValue) == 2 * sizeof(void*) ? 4 : 3; - static const size_t nonInlineBaseStorageCapacity = 16; + protected: + void finishCreation(JSGlobalData& globalData, PropertyStorage inlineStorage) + { + Base::finishCreation(globalData); + ASSERT(inherits(&s_info)); + ASSERT(structure()->propertyStorageCapacity() < baseExternalStorageCapacity); + ASSERT(structure()->isEmpty()); + ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype())); + ASSERT_UNUSED(inlineStorage, static_cast(inlineStorage) == static_cast(this + 1)); + ASSERT(structure()->isObject()); + ASSERT(classInfo()); + } - static PassRefPtr createStructure(JSValue prototype) + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) { - return Structure::create(prototype, TypeInfo(ObjectType, HasStandardGetOwnPropertySlot)); + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); } - void flattenDictionaryObject() + static const unsigned StructureFlags = 0; + + // To instantiate objects you likely want JSFinalObject, below. + // To create derived types you likely want JSNonFinalObject, below. + JSObject(JSGlobalData&, Structure*, PropertyStorage inlineStorage); + + void resetInheritorID() { - m_structure->flattenDictionaryStructure(this); + m_inheritorID.clear(); } private: - ConstPropertyStorage propertyStorage() const { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); } - PropertyStorage propertyStorage() { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); } + friend class LLIntOffsetsExtractor; + + // Nobody should ever ask any of these questions on something already known to be a JSObject. + using JSCell::isAPIValueWrapper; + using JSCell::isGetterSetter; + void getObject(); + void getString(ExecState* exec); + void isObject(); + void isString(); + + ConstPropertyStorage propertyStorage() const { return m_propertyStorage.get(); } + PropertyStorage propertyStorage() { return m_propertyStorage.get(); } - 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); - void putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue value, unsigned attr = 0); + template + bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, PutPropertySlot&, JSCell*); bool inlineGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&); + JS_EXPORT_PRIVATE void fillGetterPropertySlot(PropertySlot&, WriteBarrierBase* location); const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const; - Structure* createInheritorID(); + Structure* createInheritorID(JSGlobalData&); - union { - PropertyStorage m_externalStorage; - EncodedJSValue m_inlineStorage[inlineStorageCapacity]; - }; + StorageBarrier 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); - RefPtr m_inheritorID; + // 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: + typedef JSObject Base; + + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info); + } + + protected: + explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure) + : JSObject(globalData, structure, m_inlineStorage) + { + } + + void finishCreation(JSGlobalData& globalData) + { + Base::finishCreation(globalData, m_inlineStorage); + ASSERT(!(OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage) % sizeof(double))); + ASSERT(this->structure()->propertyStorageCapacity() == JSNonFinalObject_inlineStorageCapacity); + ASSERT(classInfo()); + } + + private: + WriteBarrier m_inlineStorage[JSNonFinalObject_inlineStorageCapacity]; }; - -JSObject* constructEmptyObject(ExecState*); -inline JSObject* asObject(JSValue value) + class JSFinalObject; + + // 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: + typedef JSObject Base; + + static JSFinalObject* create(ExecState*, Structure*); + static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) + { + return Structure::create(globalData, globalObject, prototype, TypeInfo(FinalObjectType, StructureFlags), &s_info); + } + + static JS_EXPORTDATA const ClassInfo s_info; + + protected: + void finishCreation(JSGlobalData& globalData) + { + Base::finishCreation(globalData, m_inlineStorage); + ASSERT(!(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) % sizeof(double))); + ASSERT(this->structure()->propertyStorageCapacity() == JSFinalObject_inlineStorageCapacity); + ASSERT(classInfo()); + } + + private: + friend class LLIntOffsetsExtractor; + + explicit JSFinalObject(JSGlobalData& globalData, Structure* structure) + : JSObject(globalData, structure, m_inlineStorage) + { + } + + static const unsigned StructureFlags = JSObject::StructureFlags; + + WriteBarrierBase m_inlineStorage[JSFinalObject_inlineStorageCapacity]; + }; + +inline JSFinalObject* JSFinalObject::create(ExecState* exec, Structure* structure) { - ASSERT(asCell(value)->isObject()); - return static_cast(asCell(value)); + JSFinalObject* finalObject = new (NotNull, allocateCell(*exec->heap())) JSFinalObject(exec->globalData(), structure); + finalObject->finishCreation(exec->globalData()); + return finalObject; } -inline JSObject::JSObject(PassRefPtr structure) - : JSCell(structure.releaseRef()) // ~JSObject balances this ref() +inline bool isJSFinalObject(JSCell* cell) { - ASSERT(m_structure); - ASSERT(m_structure->propertyStorageCapacity() == inlineStorageCapacity); - 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 + return cell->classInfo() == &JSFinalObject::s_info; } -inline JSObject::~JSObject() +inline bool isJSFinalObject(JSValue value) { - ASSERT(m_structure); - if (!isUsingInlineStorage()) - delete [] m_externalStorage; - m_structure->deref(); + return value.isCell() && isJSFinalObject(value.asCell()); } -inline JSValue JSObject::prototype() const +inline size_t JSObject::offsetOfInlineStorage() { - return m_structure->storedPrototype(); + ASSERT(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) == OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage)); + return OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage); } -inline void JSObject::setPrototype(JSValue prototype) +inline size_t JSObject::offsetOfPropertyStorage() { - ASSERT(prototype); - RefPtr newStructure = Structure::changePrototypeTransition(m_structure, prototype); - setStructure(newStructure.release()); + return OBJECT_OFFSETOF(JSObject, m_propertyStorage); +} + +inline size_t JSObject::offsetOfInheritorID() +{ + return OBJECT_OFFSETOF(JSObject, m_inheritorID); +} + +inline bool JSObject::isGlobalObject() const +{ + return structure()->typeInfo().type() == GlobalObjectType; +} + +inline bool JSObject::isVariableObject() const +{ + return structure()->typeInfo().type() >= VariableObjectType; +} + +inline bool JSObject::isStaticScopeObject() const +{ + return structure()->typeInfo().type() == StaticScopeObjectType; +} + +inline bool JSObject::isActivationObject() const +{ + return structure()->typeInfo().type() == ActivationObjectType; +} + +inline bool JSObject::isErrorInstance() const +{ + return structure()->typeInfo().type() == ErrorInstanceType; +} + +inline bool JSObject::isGlobalThis() const +{ + return structure()->typeInfo().type() == GlobalThisType; } -inline void JSObject::setStructure(PassRefPtr structure) +inline void JSObject::setPropertyStorage(JSGlobalData& globalData, PropertyStorage storage, Structure* structure) { - m_structure->deref(); - m_structure = structure.releaseRef(); // ~JSObject balances this ref() + ASSERT(storage); + ASSERT(structure); + setStructure(globalData, structure); + m_propertyStorage.set(globalData, this, storage); } -inline Structure* JSObject::inheritorID() +inline JSObject* constructEmptyObject(ExecState* exec, Structure* structure) { - if (m_inheritorID) + return JSFinalObject::create(exec, structure); +} + +inline CallType getCallData(JSValue value, CallData& callData) +{ + CallType result = value.isCell() ? value.asCell()->methodTable()->getCallData(value.asCell(), callData) : CallTypeNone; + ASSERT(result == CallTypeNone || value.isValidCallee()); + return result; +} + +inline ConstructType getConstructData(JSValue value, ConstructData& constructData) +{ + ConstructType result = value.isCell() ? value.asCell()->methodTable()->getConstructData(value.asCell(), constructData) : ConstructTypeNone; + ASSERT(result == ConstructTypeNone || value.isValidCallee()); + return result; +} + +inline Structure* createEmptyObjectStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype) +{ + return JSFinalObject::createStructure(globalData, globalObject, prototype); +} + +inline JSObject* asObject(JSCell* cell) +{ + ASSERT(cell->isObject()); + return jsCast(cell); +} + +inline JSObject* asObject(JSValue value) +{ + return asObject(value.asCell()); +} + +inline JSObject::JSObject(JSGlobalData& globalData, Structure* structure, PropertyStorage inlineStorage) + : JSCell(globalData, structure) + , m_propertyStorage(globalData, this, inlineStorage) +{ +} + +inline JSValue JSObject::prototype() const +{ + return structure()->storedPrototype(); +} + +inline void JSObject::setPrototype(JSGlobalData& globalData, JSValue prototype) +{ + ASSERT(prototype); + setStructure(globalData, Structure::changePrototypeTransition(globalData, structure(), prototype)); +} + +inline Structure* JSObject::inheritorID(JSGlobalData& globalData) +{ + 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::isObject(const ClassInfo* info) const +inline bool JSCell::inherits(const ClassInfo* info) const { - for (const ClassInfo* ci = classInfo(); ci; ci = ci->parentClass) { - if (ci == info) - return true; - } - return false; + return classInfo()->isSubClassOf(info); +} + +inline const MethodTable* JSCell::methodTable() const +{ + return &classInfo()->methodTable; +} + +// this method is here to be after the inline declaration of JSCell::inherits +inline bool JSValue::inherits(const ClassInfo* classInfo) const +{ + return isCell() && asCell()->inherits(classInfo); } -// this method is here to be after the inline declaration of JSCell::isObject -inline bool JSValue::isObject(const ClassInfo* classInfo) const +inline JSObject* JSValue::toThisObject(ExecState* exec) const { - return isCell() && asCell()->isObject(classInfo); + return isCell() ? asCell()->methodTable()->toThisObject(asCell(), exec) : toThisObjectSlowCase(exec); } 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 (structure()->hasGetterSetterProperties() && location->isGetterSetter()) fillGetterPropertySlot(slot, location); else - slot.setValueSlot(this, location, offsetForLocation(location)); - return true; - } - - // non-standard Netscape extension - if (propertyName == exec->propertyNames().underscoreProto) { - slot.setValue(prototype()); + slot.setValue(this, location->get(), offsetForLocation(location)); return true; } @@ -335,21 +564,37 @@ ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Ide // It may seem crazy to inline a function this large, especially a virtual function, // but it makes a big difference to property lookup that derived classes can inline their // base class call to this. -ALWAYS_INLINE bool JSObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +ALWAYS_INLINE bool JSObject::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { - return inlineGetOwnPropertySlot(exec, propertyName, slot); + return jsCast(cell)->inlineGetOwnPropertySlot(exec, propertyName, slot); } ALWAYS_INLINE bool JSCell::fastGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { - if (structure()->typeInfo().hasStandardGetOwnPropertySlot()) + if (!structure()->typeInfo().overridesGetOwnPropertySlot()) return asObject(this)->inlineGetOwnPropertySlot(exec, propertyName, slot); - return getOwnPropertySlot(exec, propertyName, slot); + return methodTable()->getOwnPropertySlot(this, exec, propertyName, slot); +} + +// Fast call to get a property where we may not yet have converted the string to an +// identifier. The first time we perform a property access with a given string, try +// performing the property map lookup without forming an identifier. We detect this +// case by checking whether the hash has yet been set for this string. +ALWAYS_INLINE JSValue JSCell::fastGetOwnProperty(ExecState* exec, const UString& name) +{ + if (!structure()->typeInfo().overridesGetOwnPropertySlot() && !structure()->hasGetterSetterProperties()) { + size_t offset = name.impl()->hasHash() + ? structure()->get(exec->globalData(), Identifier(exec, name)) + : structure()->get(exec->globalData(), name); + if (offset != WTF::notFound) + return asObject(this)->locationForOffset(offset)->get(); + } + return JSValue(); } // It may seem crazy to inline a function this large but it makes a big difference // since this is function very hot in variable lookup -inline bool JSObject::getPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) +ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) { JSObject* object = this; while (true) { @@ -362,11 +607,11 @@ inline bool JSObject::getPropertySlot(ExecState* exec, const Identifier& propert } } -inline bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) +ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) { JSObject* object = this; while (true) { - if (object->getOwnPropertySlot(exec, propertyName, slot)) + if (object->methodTable()->getOwnPropertySlotByIndex(object, exec, propertyName, slot)) return true; JSValue prototype = object->prototype(); if (!prototype.isObject()) @@ -393,162 +638,166 @@ 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) +template +inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, PutPropertySlot& slot, JSCell* specificFunction) { ASSERT(value); + ASSERT(value.isGetterSetter() == !!(attributes & Accessor)); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); - if (m_structure->isDictionary()) { + if (structure()->isDictionary()) { unsigned currentAttributes; JSCell* currentSpecificFunction; - size_t offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction); + size_t offset = 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); - if (checkReadOnly && currentAttributes & ReadOnly) - return; - putDirectOffset(offset, value); - if (!specificFunction && !currentSpecificFunction) + structure()->despecifyDictionaryFunction(globalData, propertyName); + if ((mode == PutModePut) && currentAttributes & ReadOnly) + 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; } - size_t currentCapacity = m_structure->propertyStorageCapacity(); - offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, specificFunction); - if (currentCapacity != m_structure->propertyStorageCapacity()) - allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); + if ((mode == PutModePut) && !isExtensible()) + return false; - ASSERT(offset < m_structure->propertyStorageCapacity()); - putDirectOffset(offset, value); + PropertyStorage newStorage = propertyStorage(); + if (structure()->shouldGrowPropertyStorage()) + newStorage = growPropertyStorage(globalData, structure()->propertyStorageCapacity(), structure()->suggestedNewPropertyStorageSize()); + offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, specificFunction); + setPropertyStorage(globalData, newStorage, structure()); + + ASSERT(offset < structure()->propertyStorageCapacity()); + 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)) { + size_t currentCapacity = structure()->propertyStorageCapacity(); + if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName, attributes, specificFunction, offset)) { + PropertyStorage newStorage = propertyStorage(); if (currentCapacity != structure->propertyStorageCapacity()) - allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity()); + newStorage = growPropertyStorage(globalData, currentCapacity, structure->propertyStorageCapacity()); ASSERT(offset < structure->propertyStorageCapacity()); - setStructure(structure.release()); - putDirectOffset(offset, value); - // See comment on setNewProperty call below. + setPropertyStorage(globalData, newStorage, 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 = structure()->get(globalData, propertyName, currentAttributes, currentSpecificFunction); if (offset != WTF::notFound) { - if (checkReadOnly && currentAttributes & ReadOnly) - return; - - 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; + if ((mode == PutModePut) && currentAttributes & ReadOnly) + return false; + + // 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, structure(), 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 ((mode == PutModePut) && !isExtensible()) + return false; - RefPtr structure = Structure::addPropertyTransition(m_structure, propertyName, attributes, specificFunction, offset); + PropertyStorage newStorage = propertyStorage(); + if (structure()->shouldGrowPropertyStorage()) + newStorage = growPropertyStorage(globalData, structure()->propertyStorageCapacity(), structure()->suggestedNewPropertyStorageSize()); - if (currentCapacity != structure->propertyStorageCapacity()) - allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity()); + Structure* structure = Structure::addPropertyTransition(globalData, this->structure(), propertyName, attributes, specificFunction, offset); ASSERT(offset < structure->propertyStorageCapacity()); - setStructure(structure.release()); - putDirectOffset(offset, value); - // Function transitions are not currently cachable, so leave the slot in an uncachable state. + setPropertyStorage(globalData, newStorage, 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) -{ - ASSERT(value); - ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); - - putDirectInternal(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)); -} - -inline void JSObject::putDirect(const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +inline bool JSObject::putOwnDataProperty(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { ASSERT(value); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); + ASSERT(!structure()->hasGetterSetterProperties()); - putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, 0); + return putDirectInternal(globalData, propertyName, value, 0, slot, getJSFunction(value)); } -inline void JSObject::putDirect(const Identifier& propertyName, JSValue value, unsigned attributes) +inline void JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes) { + ASSERT(!value.isGetterSetter() && !(attributes & Accessor)); PutPropertySlot slot; - putDirectInternal(propertyName, value, attributes, false, slot, 0); + putDirectInternal(globalData, propertyName, value, attributes, slot, getJSFunction(value)); } -inline void JSObject::putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +inline void JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { - putDirectInternal(propertyName, value, attributes, checkReadOnly, slot, value); + ASSERT(!value.isGetterSetter()); + putDirectInternal(globalData, propertyName, value, 0, slot, getJSFunction(value)); } -inline void JSObject::putDirectFunction(const Identifier& propertyName, JSCell* value, unsigned attr) +inline void JSObject::putDirectWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes) { - PutPropertySlot slot; - putDirectInternal(propertyName, value, attr, false, slot, value); + ASSERT(!value.isGetterSetter() && !(attributes & Accessor)); + PropertyStorage newStorage = propertyStorage(); + if (structure()->shouldGrowPropertyStorage()) + newStorage = growPropertyStorage(globalData, structure()->propertyStorageCapacity(), structure()->suggestedNewPropertyStorageSize()); + size_t offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, getJSFunction(value)); + setPropertyStorage(globalData, newStorage, structure()); + putDirectOffset(globalData, offset, value); } -inline void JSObject::putDirectWithoutTransition(const Identifier& propertyName, JSValue value, unsigned attributes) +inline void JSObject::transitionTo(JSGlobalData& globalData, Structure* newStructure) { - size_t currentCapacity = m_structure->propertyStorageCapacity(); - size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, 0); - if (currentCapacity != m_structure->propertyStorageCapacity()) - allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); - putDirectOffset(offset, value); -} - -inline void JSObject::putDirectFunctionWithoutTransition(const Identifier& propertyName, JSCell* value, unsigned attributes) -{ - size_t currentCapacity = m_structure->propertyStorageCapacity(); - size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, value); - if (currentCapacity != m_structure->propertyStorageCapacity()) - allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity()); - putDirectOffset(offset, value); -} - -inline void JSObject::transitionTo(Structure* newStructure) -{ - if (m_structure->propertyStorageCapacity() != newStructure->propertyStorageCapacity()) - allocatePropertyStorage(m_structure->propertyStorageCapacity(), newStructure->propertyStorageCapacity()); - setStructure(newStructure); + PropertyStorage newStorage = propertyStorage(); + if (structure()->propertyStorageCapacity() != newStructure->propertyStorageCapacity()) + newStorage = growPropertyStorage(globalData, structure()->propertyStorageCapacity(), newStructure->propertyStorageCapacity()); + setPropertyStorage(globalData, newStorage, newStructure); } inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const { - return defaultValue(exec, preferredType); + return methodTable()->defaultValue(this, exec, preferredType); } inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName) const @@ -561,8 +810,6 @@ inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName, Pro { if (UNLIKELY(!isCell())) { JSObject* prototype = synthesizePrototype(exec); - if (propertyName == exec->propertyNames().underscoreProto) - return prototype; if (!prototype->getPropertySlot(exec, propertyName, slot)) return jsUndefined(); return slot.getValue(exec, propertyName); @@ -571,8 +818,7 @@ inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName, Pro while (true) { if (cell->fastGetOwnPropertySlot(exec, propertyName, slot)) return slot.getValue(exec, propertyName); - ASSERT(cell->isObject()); - JSValue prototype = static_cast(cell)->prototype(); + JSValue prototype = asObject(cell)->prototype(); if (!prototype.isObject()) return jsUndefined(); cell = asObject(prototype); @@ -595,10 +841,9 @@ inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot } JSCell* cell = const_cast(asCell()); while (true) { - if (cell->getOwnPropertySlot(exec, propertyName, slot)) + if (cell->methodTable()->getOwnPropertySlotByIndex(cell, exec, propertyName, slot)) return slot.getValue(exec, propertyName); - ASSERT(cell->isObject()); - JSValue prototype = static_cast(cell)->prototype(); + JSValue prototype = asObject(cell)->prototype(); if (!prototype.isObject()) return jsUndefined(); cell = prototype.asCell(); @@ -608,39 +853,36 @@ inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { if (UNLIKELY(!isCell())) { - synthesizeObject(exec)->put(exec, propertyName, value, slot); + putToPrimitive(exec, propertyName, value, slot); return; } - asCell()->put(exec, propertyName, value, slot); + asCell()->methodTable()->put(asCell(), exec, propertyName, value, slot); } -inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value) +inline void JSValue::putByIndex(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow) { if (UNLIKELY(!isCell())) { - synthesizeObject(exec)->put(exec, propertyName, value); + PutPropertySlot slot(shouldThrow); + putToPrimitive(exec, Identifier::from(exec, propertyName), value, slot); return; } - asCell()->put(exec, propertyName, value); + asCell()->methodTable()->putByIndex(asCell(), exec, propertyName, value, shouldThrow); } -ALWAYS_INLINE void JSObject::allocatePropertyStorageInline(size_t oldSize, size_t newSize) -{ - ASSERT(newSize > oldSize); - - // 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 oldPropertyStorage = (wasInline ? m_inlineStorage : m_externalStorage); - PropertyStorage newPropertyStorage = new EncodedJSValue[newSize]; +// --- JSValue inlines ---------------------------- - for (unsigned i = 0; i < oldSize; ++i) - newPropertyStorage[i] = oldPropertyStorage[i]; - - if (!wasInline) - delete [] oldPropertyStorage; +ALWAYS_INLINE JSObject* Register::function() const +{ + if (!jsValue()) + return 0; + return asObject(jsValue()); +} - m_externalStorage = newPropertyStorage; +ALWAYS_INLINE Register Register::withCallee(JSObject* callee) +{ + Register r; + r = JSValue(callee); + return r; } } // namespace JSC