]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - runtime/JSObject.cpp
JavaScriptCore-1097.13.tar.gz
[apple/javascriptcore.git] / runtime / JSObject.cpp
index d9500aa5a98d376122f026a00217c831ff602c2f..500f3891ae30a1bce59a6f28facfd2134d4b7506 100644 (file)
 #include "config.h"
 #include "JSObject.h"
 
+#include "CopiedSpaceInlineMethods.h"
 #include "DatePrototype.h"
 #include "ErrorConstructor.h"
 #include "GetterSetter.h"
+#include "JSFunction.h"
 #include "JSGlobalObject.h"
+#include "JSGlobalThis.h"
+#include "Lookup.h"
 #include "NativeErrorConstructor.h"
+#include "Nodes.h"
 #include "ObjectPrototype.h"
+#include "Operations.h"
 #include "PropertyDescriptor.h"
 #include "PropertyNameArray.h"
-#include "Lookup.h"
-#include "Nodes.h"
-#include "Operations.h"
 #include <math.h>
 #include <wtf/Assertions.h>
 
 namespace JSC {
 
 ASSERT_CLASS_FITS_IN_CELL(JSObject);
+ASSERT_CLASS_FITS_IN_CELL(JSNonFinalObject);
+ASSERT_CLASS_FITS_IN_CELL(JSFinalObject);
+
+ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSObject);
+ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSFinalObject);
+
+const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property.";
+
+const ClassInfo JSObject::s_info = { "Object", 0, 0, 0, CREATE_METHOD_TABLE(JSObject) };
+
+const ClassInfo JSFinalObject::s_info = { "Object", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSFinalObject) };
 
 static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode)
 {
@@ -61,92 +75,98 @@ static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* class
     }
 }
 
-void JSObject::markChildren(MarkStack& markStack)
+void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
 {
-#ifndef NDEBUG
-    bool wasCheckingForDefaultMarkViolation = markStack.m_isCheckingForDefaultMarkViolation;
-    markStack.m_isCheckingForDefaultMarkViolation = false;
+    JSObject* thisObject = jsCast<JSObject*>(cell);
+    ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
+#if !ASSERT_DISABLED
+    bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
+    visitor.m_isCheckingForDefaultMarkViolation = false;
 #endif
 
-    markChildrenDirect(markStack);
+    JSCell::visitChildren(thisObject, visitor);
+
+    PropertyStorage storage = thisObject->propertyStorage();
+    size_t storageSize = thisObject->structure()->propertyStorageSize();
+    if (thisObject->isUsingInlineStorage())
+        visitor.appendValues(storage, storageSize);
+    else {
+        // We have this extra temp here to slake GCC's thirst for the blood of those who dereference type-punned pointers.
+        void* temp = storage;
+        visitor.copyAndAppend(&temp, thisObject->structure()->propertyStorageCapacity() * sizeof(WriteBarrierBase<Unknown>), storage->slot(), storageSize);
+        storage = static_cast<PropertyStorage>(temp);
+        thisObject->m_propertyStorage.set(storage, StorageBarrier::Unchecked);
+    }
 
-#ifndef NDEBUG
-    markStack.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
-#endif
-}
+    if (thisObject->m_inheritorID)
+        visitor.append(&thisObject->m_inheritorID);
 
-UString JSObject::className() const
-{
-    const ClassInfo* info = classInfo();
-    if (info)
-        return info->className;
-    return "Object";
+#if !ASSERT_DISABLED
+    visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
+#endif
 }
 
-bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
+UString JSObject::className(const JSObject* object)
 {
-    return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
+    const ClassInfo* info = object->classInfo();
+    ASSERT(info);
+    return info->className;
 }
 
-static void throwSetterError(ExecState* exec)
+bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot)
 {
-    throwError(exec, TypeError, "setting a property that has only a getter");
+    JSObject* thisObject = jsCast<JSObject*>(cell);
+    return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, propertyName), slot);
 }
 
 // ECMA 8.6.2.2
-void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
+void JSObject::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
 {
+    JSObject* thisObject = jsCast<JSObject*>(cell);
     ASSERT(value);
-    ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
-
-    if (propertyName == exec->propertyNames().underscoreProto) {
-        // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
-        if (!value.isObject() && !value.isNull())
-            return;
-
-        JSValue nextPrototypeValue = value;
-        while (nextPrototypeValue && nextPrototypeValue.isObject()) {
-            JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject();
-            if (nextPrototype == this) {
-                throwError(exec, GeneralError, "cyclic __proto__ value");
-                return;
-            }
-            nextPrototypeValue = nextPrototype->prototype();
-        }
-
-        setPrototype(value);
-        return;
-    }
+    ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject));
+    JSGlobalData& globalData = exec->globalData();
 
     // Check if there are any setters or getters in the prototype chain
     JSValue prototype;
-    for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) {
-        prototype = obj->prototype();
-        if (prototype.isNull()) {
-            putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot);
-            return;
+    if (propertyName != exec->propertyNames().underscoreProto) {
+        for (JSObject* obj = thisObject; !obj->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj = asObject(prototype)) {
+            prototype = obj->prototype();
+            if (prototype.isNull()) {
+                if (!thisObject->putDirectInternal<PutModePut>(globalData, propertyName, value, 0, slot, getJSFunction(value)) && slot.isStrictMode())
+                    throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
+                return;
+            }
         }
     }
-    
-    unsigned attributes;
-    JSCell* specificValue;
-    if ((m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly)
-        return;
 
-    for (JSObject* obj = this; ; obj = asObject(prototype)) {
-        if (JSValue gs = obj->getDirect(propertyName)) {
+    for (JSObject* obj = thisObject; ; obj = asObject(prototype)) {
+        unsigned attributes;
+        JSCell* specificValue;
+        size_t offset = obj->structure()->get(globalData, propertyName, attributes, specificValue);
+        if (offset != WTF::notFound) {
+            if (attributes & ReadOnly) {
+                if (slot.isStrictMode())
+                    throwError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError));
+                return;
+            }
+
+            JSValue gs = obj->getDirectOffset(offset);
             if (gs.isGetterSetter()) {
                 JSObject* setterFunc = asGetterSetter(gs)->setter();        
                 if (!setterFunc) {
-                    throwSetterError(exec);
+                    if (slot.isStrictMode())
+                        throwError(exec, createTypeError(exec, "setting a property that has only a getter"));
                     return;
                 }
                 
                 CallData callData;
-                CallType callType = setterFunc->getCallData(callData);
+                CallType callType = setterFunc->methodTable()->getCallData(setterFunc, callData);
                 MarkedArgumentBuffer args;
                 args.append(value);
-                call(exec, setterFunc, callType, callData, this, args);
+
+                // If this is WebCore's global object then we need to substitute the shell.
+                call(exec, setterFunc, callType, callData, thisObject->methodTable()->toThisObject(thisObject, exec), args);
                 return;
             }
 
@@ -159,30 +179,65 @@ void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue valu
         if (prototype.isNull())
             break;
     }
-
-    putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot);
+    
+    if (!thisObject->putDirectInternal<PutModePut>(globalData, propertyName, value, 0, slot, getJSFunction(value)) && slot.isStrictMode())
+        throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
     return;
 }
 
-void JSObject::put(ExecState* exec, unsigned propertyName, JSValue value)
+void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
 {
+    PutPropertySlot slot(shouldThrow);
+    JSObject* thisObject = jsCast<JSObject*>(cell);
+    thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
+}
+
+void JSObject::putDirectVirtual(JSObject* object, ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes)
+{
+    ASSERT(!value.isGetterSetter() && !(attributes & Accessor));
     PutPropertySlot slot;
-    put(exec, Identifier::from(exec, propertyName), value, slot);
+    object->putDirectInternal<PutModeDefineOwnProperty>(exec->globalData(), propertyName, value, attributes, slot, getJSFunction(value));
 }
 
-void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
+bool JSObject::setPrototypeWithCycleCheck(JSGlobalData& globalData, JSValue prototype)
 {
-    putDirectInternal(exec->globalData(), propertyName, value, attributes, checkReadOnly, slot);
+    JSValue checkFor = this;
+    if (this->isGlobalObject())
+        checkFor = jsCast<JSGlobalObject*>(this)->globalExec()->thisValue();
+
+    JSValue nextPrototype = prototype;
+    while (nextPrototype && nextPrototype.isObject()) {
+        if (nextPrototype == checkFor)
+            return false;
+        nextPrototype = asObject(nextPrototype)->prototype();
+    }
+    setPrototype(globalData, prototype);
+    return true;
 }
 
-void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes)
+bool JSObject::allowsAccessFrom(ExecState* exec)
 {
-    putDirectInternal(exec->globalData(), propertyName, value, attributes);
+    JSGlobalObject* globalObject = isGlobalThis() ? jsCast<JSGlobalThis*>(this)->unwrappedObject() : this->globalObject();
+    return globalObject->globalObjectMethodTable()->allowsAccessFrom(globalObject, exec);
 }
 
-void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes)
+void JSObject::putDirectAccessor(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
 {
-    putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
+    ASSERT(value.isGetterSetter() && (attributes & Accessor));
+
+    PutPropertySlot slot;
+    putDirectInternal<PutModeDefineOwnProperty>(globalData, propertyName, value, attributes, slot, getJSFunction(value));
+
+    // putDirect will change our Structure if we add a new property. For
+    // getters and setters, though, we also need to change our Structure
+    // if we override an existing non-getter or non-setter.
+    if (slot.type() != PutPropertySlot::NewProperty)
+        setStructure(globalData, Structure::attributeChangeTransition(globalData, structure(), propertyName, attributes));
+
+    if (attributes & ReadOnly)
+        structure()->setContainsReadOnlyProperties();
+
+    structure()->setHasGetterSetterProperties(propertyName == globalData.propertyNames->underscoreProto);
 }
 
 bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const
@@ -198,20 +253,25 @@ bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
 }
 
 // ECMA 8.6.2.5
-bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
+bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName)
 {
+    JSObject* thisObject = jsCast<JSObject*>(cell);
+
+    if (!thisObject->staticFunctionsReified())
+        thisObject->reifyStaticFunctionsForDelete(exec);
+
     unsigned attributes;
     JSCell* specificValue;
-    if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) {
-        if ((attributes & DontDelete))
+    if (thisObject->structure()->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound) {
+        if (attributes & DontDelete && !exec->globalData().isInDefineOwnProperty())
             return false;
-        removeDirect(propertyName);
+        thisObject->removeDirect(exec->globalData(), propertyName);
         return true;
     }
 
     // Look in the static hashtable of properties
-    const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
-    if (entry && entry->attributes() & DontDelete)
+    const HashEntry* entry = thisObject->findPropertyHashEntry(exec, propertyName);
+    if (entry && entry->attributes() & DontDelete && !exec->globalData().isInDefineOwnProperty())
         return false; // this builtin property can't be deleted
 
     // FIXME: Should the code here actually do some deletion?
@@ -221,19 +281,20 @@ bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
 bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
 {
     PropertySlot slot;
-    return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
+    return const_cast<JSObject*>(this)->methodTable()->getOwnPropertySlot(const_cast<JSObject*>(this), exec, propertyName, slot);
 }
 
-bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName)
+bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName)
 {
-    return deleteProperty(exec, Identifier::from(exec, propertyName));
+    JSObject* thisObject = jsCast<JSObject*>(cell);
+    return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, propertyName));
 }
 
 static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
 {
     JSValue function = object->get(exec, propertyName);
     CallData callData;
-    CallType callType = function.getCallData(callData);
+    CallType callType = getCallData(function, callData);
     if (callType == CallTypeNone)
         return exec->exception();
 
@@ -251,36 +312,36 @@ static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSO
     return result;
 }
 
-bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result)
+bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
 {
-    result = defaultValue(exec, PreferNumber);
+    result = methodTable()->defaultValue(this, exec, PreferNumber);
     number = result.toNumber(exec);
     return !result.isString();
 }
 
 // ECMA 8.6.2.6
-JSValue JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
+JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint)
 {
     // Must call toString first for Date objects.
-    if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) {
-        JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
+    if ((hint == PreferString) || (hint != PreferNumber && object->prototype() == exec->lexicalGlobalObject()->datePrototype())) {
+        JSValue value = callDefaultValueFunction(exec, object, exec->propertyNames().toString);
         if (value)
             return value;
-        value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
+        value = callDefaultValueFunction(exec, object, exec->propertyNames().valueOf);
         if (value)
             return value;
     } else {
-        JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
+        JSValue value = callDefaultValueFunction(exec, object, exec->propertyNames().valueOf);
         if (value)
             return value;
-        value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
+        value = callDefaultValueFunction(exec, object, exec->propertyNames().toString);
         if (value)
             return value;
     }
 
     ASSERT(!exec->hadException());
 
-    return throwError(exec, TypeError, "No default value");
+    return throwError(exec, createTypeError(exec, "No default value"));
 }
 
 const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
@@ -294,105 +355,13 @@ const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifi
     return 0;
 }
 
-void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes)
-{
-    JSValue object = getDirect(propertyName);
-    if (object && object.isGetterSetter()) {
-        ASSERT(m_structure->hasGetterSetterProperties());
-        asGetterSetter(object)->setGetter(getterFunction);
-        return;
-    }
-
-    PutPropertySlot slot;
-    GetterSetter* getterSetter = new (exec) GetterSetter(exec);
-    putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Getter, true, slot);
-
-    // putDirect will change our Structure if we add a new property. For
-    // getters and setters, though, we also need to change our Structure
-    // if we override an existing non-getter or non-setter.
-    if (slot.type() != PutPropertySlot::NewProperty) {
-        if (!m_structure->isDictionary()) {
-            RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure);
-            setStructure(structure.release());
-        }
-    }
-
-    m_structure->setHasGetterSetterProperties(true);
-    getterSetter->setGetter(getterFunction);
-}
-
-void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes)
-{
-    JSValue object = getDirect(propertyName);
-    if (object && object.isGetterSetter()) {
-        ASSERT(m_structure->hasGetterSetterProperties());
-        asGetterSetter(object)->setSetter(setterFunction);
-        return;
-    }
-
-    PutPropertySlot slot;
-    GetterSetter* getterSetter = new (exec) GetterSetter(exec);
-    putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot);
-
-    // putDirect will change our Structure if we add a new property. For
-    // getters and setters, though, we also need to change our Structure
-    // if we override an existing non-getter or non-setter.
-    if (slot.type() != PutPropertySlot::NewProperty) {
-        if (!m_structure->isDictionary()) {
-            RefPtr<Structure> structure = Structure::getterSetterTransition(m_structure);
-            setStructure(structure.release());
-        }
-    }
-
-    m_structure->setHasGetterSetterProperties(true);
-    getterSetter->setSetter(setterFunction);
-}
-
-JSValue JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
-{
-    JSObject* object = this;
-    while (true) {
-        if (JSValue value = object->getDirect(propertyName)) {
-            if (!value.isGetterSetter())
-                return jsUndefined();
-            JSObject* functionObject = asGetterSetter(value)->getter();
-            if (!functionObject)
-                return jsUndefined();
-            return functionObject;
-        }
-
-        if (!object->prototype() || !object->prototype().isObject())
-            return jsUndefined();
-        object = asObject(object->prototype());
-    }
-}
-
-JSValue JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
-{
-    JSObject* object = this;
-    while (true) {
-        if (JSValue value = object->getDirect(propertyName)) {
-            if (!value.isGetterSetter())
-                return jsUndefined();
-            JSObject* functionObject = asGetterSetter(value)->setter();
-            if (!functionObject)
-                return jsUndefined();
-            return functionObject;
-        }
-
-        if (!object->prototype() || !object->prototype().isObject())
-            return jsUndefined();
-        object = asObject(object->prototype());
-    }
-}
-
-bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto)
+bool JSObject::hasInstance(JSObject*, ExecState* exec, JSValue value, JSValue proto)
 {
     if (!value.isObject())
         return false;
 
     if (!proto.isObject()) {
-        throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
+        throwError(exec, createTypeError(exec, "instanceof called on an object with an invalid prototype property."));
         return false;
     }
 
@@ -407,15 +376,15 @@ bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto)
 bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
 {
     PropertyDescriptor descriptor;
-    if (!const_cast<JSObject*>(this)->getOwnPropertyDescriptor(exec, propertyName, descriptor))
+    if (!const_cast<JSObject*>(this)->methodTable()->getOwnPropertyDescriptor(const_cast<JSObject*>(this), exec, propertyName, descriptor))
         return false;
     return descriptor.enumerable();
 }
 
-bool JSObject::getPropertySpecificValue(ExecState*, const Identifier& propertyName, JSCell*& specificValue) const
+bool JSObject::getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificValue) const
 {
     unsigned attributes;
-    if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound)
+    if (structure()->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound)
         return true;
 
     // This could be a function within the static table? - should probably
@@ -425,20 +394,20 @@ bool JSObject::getPropertySpecificValue(ExecState*, const Identifier& propertyNa
     return false;
 }
 
-void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+void JSObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
 {
-    getOwnPropertyNames(exec, propertyNames, mode);
+    object->methodTable()->getOwnPropertyNames(object, exec, propertyNames, mode);
 
-    if (prototype().isNull())
+    if (object->prototype().isNull())
         return;
 
-    JSObject* prototype = asObject(this->prototype());
+    JSObject* prototype = asObject(object->prototype());
     while(1) {
         if (prototype->structure()->typeInfo().overridesGetPropertyNames()) {
-            prototype->getPropertyNames(exec, propertyNames, mode);
+            prototype->methodTable()->getPropertyNames(prototype, exec, propertyNames, mode);
             break;
         }
-        prototype->getOwnPropertyNames(exec, propertyNames, mode);
+        prototype->methodTable()->getOwnPropertyNames(prototype, exec, propertyNames, mode);
         JSValue nextProto = prototype->prototype();
         if (nextProto.isNull())
             break;
@@ -446,10 +415,11 @@ void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyName
     }
 }
 
-void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
+void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
 {
-    m_structure->getPropertyNames(propertyNames, mode);
-    getClassPropertyNames(exec, classInfo(), propertyNames, mode);
+    object->structure()->getPropertyNamesFromStructure(exec->globalData(), propertyNames, mode);
+    if (!object->staticFunctionsReified())
+        getClassPropertyNames(exec, object->classInfo(), propertyNames, mode);
 }
 
 bool JSObject::toBoolean(ExecState*) const
@@ -465,82 +435,162 @@ double JSObject::toNumber(ExecState* exec) const
     return primitive.toNumber(exec);
 }
 
-UString JSObject::toString(ExecState* exec) const
+JSString* JSObject::toString(ExecState* exec) const
 {
     JSValue primitive = toPrimitive(exec, PreferString);
     if (exec->hadException())
-        return "";
+        return jsEmptyString(exec);
     return primitive.toString(exec);
 }
 
-JSObject* JSObject::toObject(ExecState*) const
+JSObject* JSObject::toThisObject(JSCell* cell, ExecState*)
 {
-    return const_cast<JSObject*>(this);
+    return jsCast<JSObject*>(cell);
 }
 
-JSObject* JSObject::toThisObject(ExecState*) const
+JSObject* JSObject::unwrappedObject()
 {
-    return const_cast<JSObject*>(this);
+    if (isGlobalThis())
+        return jsCast<JSGlobalThis*>(this)->unwrappedObject();
+    return this;
 }
 
-JSObject* JSObject::unwrappedObject()
+void JSObject::seal(JSGlobalData& globalData)
 {
-    return this;
+    if (isSealed(globalData))
+        return;
+    preventExtensions(globalData);
+    setStructure(globalData, Structure::sealTransition(globalData, structure()));
 }
 
-void JSObject::removeDirect(const Identifier& propertyName)
+void JSObject::freeze(JSGlobalData& globalData)
 {
-    size_t offset;
-    if (m_structure->isUncacheableDictionary()) {
-        offset = m_structure->removePropertyWithoutTransition(propertyName);
-        if (offset != WTF::notFound)
-            putDirectOffset(offset, jsUndefined());
+    if (isFrozen(globalData))
         return;
-    }
+    preventExtensions(globalData);
+    setStructure(globalData, Structure::freezeTransition(globalData, structure()));
+}
 
-    RefPtr<Structure> structure = Structure::removePropertyTransition(m_structure, propertyName, offset);
-    setStructure(structure.release());
-    if (offset != WTF::notFound)
-        putDirectOffset(offset, jsUndefined());
+void JSObject::preventExtensions(JSGlobalData& globalData)
+{
+    if (isJSArray(this))
+        asArray(this)->enterDictionaryMode(globalData);
+    if (isExtensible())
+        setStructure(globalData, Structure::preventExtensionsTransition(globalData, structure()));
 }
 
-void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
+// This presently will flatten to an uncachable dictionary; this is suitable
+// for use in delete, we may want to do something different elsewhere.
+void JSObject::reifyStaticFunctionsForDelete(ExecState* exec)
 {
-    putDirectFunction(Identifier(exec, function->name(exec)), function, attr);
+    ASSERT(!staticFunctionsReified());
+    JSGlobalData& globalData = exec->globalData();
+
+    // If this object's ClassInfo has no static properties, then nothing to reify!
+    // We can safely set the flag to avoid the expensive check again in the future.
+    if (!classInfo()->hasStaticProperties()) {
+        structure()->setStaticFunctionsReified();
+        return;
+    }
+
+    if (!structure()->isUncacheableDictionary())
+        setStructure(globalData, Structure::toUncacheableDictionaryTransition(globalData, structure()));
+
+    for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
+        const HashTable* hashTable = info->propHashTable(globalObject()->globalExec());
+        if (!hashTable)
+            continue;
+        PropertySlot slot;
+        for (HashTable::ConstIterator iter = hashTable->begin(globalData); iter != hashTable->end(globalData); ++iter) {
+            if (iter->attributes() & Function)
+                setUpStaticFunctionSlot(globalObject()->globalExec(), *iter, this, Identifier(&globalData, iter->key()), slot);
+        }
+    }
+
+    structure()->setStaticFunctionsReified();
 }
 
-void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
+void JSObject::removeDirect(JSGlobalData& globalData, const Identifier& propertyName)
 {
-    putDirectFunctionWithoutTransition(Identifier(exec, function->name(exec)), function, attr);
+    if (structure()->get(globalData, propertyName) == WTF::notFound)
+        return;
+
+    size_t offset;
+    if (structure()->isUncacheableDictionary()) {
+        offset = structure()->removePropertyWithoutTransition(globalData, propertyName);
+        if (offset != WTF::notFound)
+            putUndefinedAtDirectOffset(offset);
+        return;
+    }
+
+    setStructure(globalData, Structure::removePropertyTransition(globalData, structure(), propertyName, offset));
+    if (offset != WTF::notFound)
+        putUndefinedAtDirectOffset(offset);
 }
 
-NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location)
+NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarrierBase<Unknown>* location)
 {
-    if (JSObject* getterFunction = asGetterSetter(*location)->getter())
-        slot.setGetterSlot(getterFunction);
-    else
+    if (JSObject* getterFunction = asGetterSetter(location->get())->getter()) {
+        if (!structure()->isDictionary())
+            slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location));
+        else
+            slot.setGetterSlot(getterFunction);
+    } else
         slot.setUndefined();
 }
 
-Structure* JSObject::createInheritorID()
+Structure* JSObject::createInheritorID(JSGlobalData& globalData)
 {
-    m_inheritorID = JSObject::createStructure(this);
+    JSGlobalObject* globalObject;
+    if (isGlobalThis())
+        globalObject = static_cast<JSGlobalThis*>(this)->unwrappedObject();
+    else
+        globalObject = structure()->globalObject();
+    ASSERT(globalObject);
+    m_inheritorID.set(globalData, this, createEmptyObjectStructure(globalData, globalObject, this));
+    ASSERT(m_inheritorID->isEmpty());
     return m_inheritorID.get();
 }
 
-void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
+PropertyStorage JSObject::growPropertyStorage(JSGlobalData& globalData, size_t oldSize, size_t newSize)
 {
-    allocatePropertyStorageInline(oldSize, newSize);
+    ASSERT(newSize > oldSize);
+
+    // It's important that this function not rely on structure(), since
+    // we might be in the middle of a transition.
+
+    PropertyStorage oldPropertyStorage = m_propertyStorage.get();
+    PropertyStorage newPropertyStorage = 0;
+
+    if (isUsingInlineStorage()) {
+        // We have this extra temp here to slake GCC's thirst for the blood of those who dereference type-punned pointers.
+        void* temp = newPropertyStorage;
+        if (!globalData.heap.tryAllocateStorage(sizeof(WriteBarrierBase<Unknown>) * newSize, &temp))
+            CRASH();
+        newPropertyStorage = static_cast<PropertyStorage>(temp);
+
+        for (unsigned i = 0; i < oldSize; ++i)
+            newPropertyStorage[i] = oldPropertyStorage[i];
+    } else {
+        // We have this extra temp here to slake GCC's thirst for the blood of those who dereference type-punned pointers.
+        void* temp = oldPropertyStorage;
+        if (!globalData.heap.tryReallocateStorage(&temp, sizeof(WriteBarrierBase<Unknown>) * oldSize, sizeof(WriteBarrierBase<Unknown>) * newSize))
+            CRASH();
+        newPropertyStorage = static_cast<PropertyStorage>(temp);
+    }
+
+    ASSERT(newPropertyStorage);
+    return newPropertyStorage;
 }
 
-bool JSObject::getOwnPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor& descriptor)
+bool JSObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
 {
     unsigned attributes = 0;
     JSCell* cell = 0;
-    size_t offset = m_structure->get(propertyName, attributes, cell);
+    size_t offset = object->structure()->get(exec->globalData(), propertyName, attributes, cell);
     if (offset == WTF::notFound)
         return false;
-    descriptor.setDescriptor(getDirectOffset(offset), attributes);
+    descriptor.setDescriptor(object->getDirectOffset(offset), attributes);
     return true;
 }
 
@@ -548,7 +598,7 @@ bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& property
 {
     JSObject* object = this;
     while (true) {
-        if (object->getOwnPropertyDescriptor(exec, propertyName, descriptor))
+        if (object->methodTable()->getOwnPropertyDescriptor(object, exec, propertyName, descriptor))
             return true;
         JSValue prototype = object->prototype();
         if (!prototype.isObject())
@@ -557,28 +607,82 @@ bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& property
     }
 }
 
-static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, JSValue oldValue)
+static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor)
 {
     if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
-        target->putWithAttributes(exec, propertyName, descriptor.value() ? descriptor.value() : oldValue, attributes & ~(Getter | Setter));
+        if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) {
+            GetterSetter* accessor = GetterSetter::create(exec);
+            if (oldDescriptor.getterPresent())
+                accessor->setGetter(exec->globalData(), oldDescriptor.getterObject());
+            if (oldDescriptor.setterPresent())
+                accessor->setSetter(exec->globalData(), oldDescriptor.setterObject());
+            target->putDirectAccessor(exec->globalData(), propertyName, accessor, attributes | Accessor);
+            return true;
+        }
+        JSValue newValue = jsUndefined();
+        if (descriptor.value())
+            newValue = descriptor.value();
+        else if (oldDescriptor.value())
+            newValue = oldDescriptor.value();
+        target->putDirect(exec->globalData(), propertyName, newValue, attributes & ~Accessor);
+        if (attributes & ReadOnly)
+            target->structure()->setContainsReadOnlyProperties();
         return true;
     }
     attributes &= ~ReadOnly;
-    if (descriptor.getter() && descriptor.getter().isObject())
-        target->defineGetter(exec, propertyName, asObject(descriptor.getter()), attributes);
-    if (exec->hadException())
-        return false;
-    if (descriptor.setter() && descriptor.setter().isObject())
-        target->defineSetter(exec, propertyName, asObject(descriptor.setter()), attributes);
-    return !exec->hadException();
+    GetterSetter* accessor = GetterSetter::create(exec);
+
+    if (descriptor.getterPresent())
+        accessor->setGetter(exec->globalData(), descriptor.getterObject());
+    else if (oldDescriptor.getterPresent())
+        accessor->setGetter(exec->globalData(), oldDescriptor.getterObject());
+    if (descriptor.setterPresent())
+        accessor->setSetter(exec->globalData(), descriptor.setterObject());
+    else if (oldDescriptor.setterPresent())
+        accessor->setSetter(exec->globalData(), oldDescriptor.setterObject());
+
+    target->putDirectAccessor(exec->globalData(), propertyName, accessor, attributes | Accessor);
+    return true;
 }
 
-bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException)
+class DefineOwnPropertyScope {
+public:
+    DefineOwnPropertyScope(ExecState* exec)
+        : m_globalData(exec->globalData())
+    {
+        m_globalData.setInDefineOwnProperty(true);
+    }
+
+    ~DefineOwnPropertyScope()
+    {
+        m_globalData.setInDefineOwnProperty(false);
+    }
+
+private:
+    JSGlobalData& m_globalData;
+};
+
+bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException)
 {
+    // Track on the globaldata that we're in define property.
+    // Currently DefineOwnProperty uses delete to remove properties when they are being replaced
+    // (particularly when changing attributes), however delete won't allow non-configurable (i.e.
+    // DontDelete) properties to be deleted. For now, we can use this flag to make this work.
+    DefineOwnPropertyScope scope(exec);
+
     // If we have a new property we can just put it on normally
     PropertyDescriptor current;
-    if (!getOwnPropertyDescriptor(exec, propertyName, current))
-        return putDescriptor(exec, this, propertyName, descriptor, descriptor.attributes(), jsUndefined());
+    if (!object->methodTable()->getOwnPropertyDescriptor(object, exec, propertyName, current)) {
+        // unless extensions are prevented!
+        if (!object->isExtensible()) {
+            if (throwException)
+                throwError(exec, createTypeError(exec, "Attempting to define property on object that is not extensible."));
+            return false;
+        }
+        PropertyDescriptor oldDescriptor;
+        oldDescriptor.setValue(jsUndefined());
+        return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
+    }
 
     if (descriptor.isEmpty())
         return true;
@@ -590,12 +694,12 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName
     if (!current.configurable()) {
         if (descriptor.configurable()) {
             if (throwException)
-                throwError(exec, TypeError, "Attempting to configurable attribute of unconfigurable property.");
+                throwError(exec, createTypeError(exec, "Attempting to configurable attribute of unconfigurable property."));
             return false;
         }
         if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
             if (throwException)
-                throwError(exec, TypeError, "Attempting to change enumerable attribute of unconfigurable property.");
+                throwError(exec, createTypeError(exec, "Attempting to change enumerable attribute of unconfigurable property."));
             return false;
         }
     }
@@ -603,8 +707,8 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName
     // A generic descriptor is simply changing the attributes of an existing property
     if (descriptor.isGenericDescriptor()) {
         if (!current.attributesEqual(descriptor)) {
-            deleteProperty(exec, propertyName);
-            putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value());
+            object->methodTable()->deleteProperty(object, exec, propertyName);
+            return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
         }
         return true;
     }
@@ -613,11 +717,11 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName
     if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
         if (!current.configurable()) {
             if (throwException)
-                throwError(exec, TypeError, "Attempting to change access mechanism for an unconfigurable property.");
+                throwError(exec, createTypeError(exec, "Attempting to change access mechanism for an unconfigurable property."));
             return false;
         }
-        deleteProperty(exec, propertyName);
-        return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value() ? current.value() : jsUndefined());
+        object->methodTable()->deleteProperty(object, exec, propertyName);
+        return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
     }
 
     // Changing the value and attributes of an existing property
@@ -625,62 +729,56 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName
         if (!current.configurable()) {
             if (!current.writable() && descriptor.writable()) {
                 if (throwException)
-                    throwError(exec, TypeError, "Attempting to change writable attribute of unconfigurable property.");
+                    throwError(exec, createTypeError(exec, "Attempting to change writable attribute of unconfigurable property."));
                 return false;
             }
             if (!current.writable()) {
-                if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) {
+                if (descriptor.value() && !sameValue(exec, current.value(), descriptor.value())) {
                     if (throwException)
-                        throwError(exec, TypeError, "Attempting to change value of a readonly property.");
+                        throwError(exec, createTypeError(exec, "Attempting to change value of a readonly property."));
                     return false;
                 }
             }
-        } else if (current.attributesEqual(descriptor)) {
-            if (!descriptor.value())
-                return true;
-            PutPropertySlot slot;
-            put(exec, propertyName, descriptor.value(), slot);
-            if (exec->hadException())
-                return false;
-            return true;
         }
-        deleteProperty(exec, propertyName);
-        return putDescriptor(exec, this, propertyName, descriptor, current.attributesWithOverride(descriptor), current.value());
+        if (current.attributesEqual(descriptor) && !descriptor.value())
+            return true;
+        object->methodTable()->deleteProperty(object, exec, propertyName);
+        return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current);
     }
 
     // Changing the accessor functions of an existing accessor property
     ASSERT(descriptor.isAccessorDescriptor());
     if (!current.configurable()) {
-        if (descriptor.setterPresent() && !(current.setter() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) {
+        if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) {
             if (throwException)
-                throwError(exec, TypeError, "Attempting to change the setter of an unconfigurable property.");
+                throwError(exec, createTypeError(exec, "Attempting to change the setter of an unconfigurable property."));
             return false;
         }
-        if (descriptor.getterPresent() && !(current.getter() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) {
+        if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) {
             if (throwException)
-                throwError(exec, TypeError, "Attempting to change the getter of an unconfigurable property.");
+                throwError(exec, createTypeError(exec, "Attempting to change the getter of an unconfigurable property."));
             return false;
         }
     }
-    JSValue accessor = getDirect(propertyName);
+    JSValue accessor = object->getDirect(exec->globalData(), propertyName);
     if (!accessor)
         return false;
     GetterSetter* getterSetter = asGetterSetter(accessor);
-    if (current.attributesEqual(descriptor)) {
-        if (descriptor.setter())
-            getterSetter->setSetter(asObject(descriptor.setter()));
-        if (descriptor.getter())
-            getterSetter->setGetter(asObject(descriptor.getter()));
+    if (descriptor.setterPresent())
+        getterSetter->setSetter(exec->globalData(), descriptor.setterObject());
+    if (descriptor.getterPresent())
+        getterSetter->setGetter(exec->globalData(), descriptor.getterObject());
+    if (current.attributesEqual(descriptor))
         return true;
-    }
-    deleteProperty(exec, propertyName);
-    unsigned attrs = current.attributesWithOverride(descriptor);
-    if (descriptor.setter())
-        attrs |= Setter;
-    if (descriptor.getter())
-        attrs |= Getter;
-    putDirect(propertyName, getterSetter, attrs);
+    object->methodTable()->deleteProperty(object, exec, propertyName);
+    unsigned attrs = descriptor.attributesOverridingCurrent(current);
+    object->putDirectAccessor(exec->globalData(), propertyName, getterSetter, attrs | Accessor);
     return true;
 }
 
+JSObject* throwTypeError(ExecState* exec, const UString& message)
+{
+    return throwError(exec, createTypeError(exec, message));
+}
+
 } // namespace JSC