X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/14957cd040308e3eeec43d26bae5d76da13fcd85..a253471d7f8e4d91bf6ebabab00155c3b387d3d0:/runtime/JSObject.cpp?ds=sidebyside diff --git a/runtime/JSObject.cpp b/runtime/JSObject.cpp index 05ec555..500f389 100644 --- a/runtime/JSObject.cpp +++ b/runtime/JSObject.cpp @@ -24,18 +24,20 @@ #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 #include @@ -45,9 +47,14 @@ 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 }; +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) { @@ -68,86 +75,98 @@ static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* class } } -void JSObject::visitChildren(SlotVisitor& visitor) +void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor) { - ASSERT_GC_OBJECT_INHERITS(this, &s_info); -#ifndef NDEBUG + JSObject* thisObject = jsCast(cell); + ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); +#if !ASSERT_DISABLED bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation; visitor.m_isCheckingForDefaultMarkViolation = false; #endif - visitChildrenDirect(visitor); + 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), storage->slot(), storageSize); + storage = static_cast(temp); + thisObject->m_propertyStorage.set(storage, StorageBarrier::Unchecked); + } + + if (thisObject->m_inheritorID) + visitor.append(&thisObject->m_inheritorID); -#ifndef NDEBUG +#if !ASSERT_DISABLED visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation; #endif } -UString JSObject::className() const +UString JSObject::className(const JSObject* object) { - const ClassInfo* info = classInfo(); + const ClassInfo* info = object->classInfo(); ASSERT(info); return info->className; } -bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) +bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot) { - return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot); -} - -static void throwSetterError(ExecState* exec) -{ - throwError(exec, createTypeError(exec, "setting a property that has only a getter")); + JSObject* thisObject = jsCast(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(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; - if (!setPrototypeWithCycleCheck(exec->globalData(), value)) - throwError(exec, createError(exec, "cyclic __proto__ 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()) { - if (!putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot) && slot.isStrictMode()) - throwTypeError(exec, StrictModeReadonlyPropertyWriteError); - 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(globalData, propertyName, value, 0, slot, getJSFunction(value)) && slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + return; + } } } - - unsigned attributes; - JSCell* specificValue; - if ((m_structure->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly) { - if (slot.isStrictMode()) - throwError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError)); - return; - } - for (JSObject* obj = this; ; obj = asObject(prototype)) { - if (JSValue gs = obj->getDirect(exec->globalData(), 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; } @@ -160,46 +179,65 @@ void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue valu if (prototype.isNull()) break; } - - if (!putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot) && slot.isStrictMode()) + + if (!thisObject->putDirectInternal(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; - put(exec, Identifier::from(exec, propertyName), value, slot); + PutPropertySlot slot(shouldThrow); + JSObject* thisObject = jsCast(cell); + thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot); } -void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +void JSObject::putDirectVirtual(JSObject* object, ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes) { - putDirectInternal(*globalData, propertyName, value, attributes, checkReadOnly, slot); + ASSERT(!value.isGetterSetter() && !(attributes & Accessor)); + PutPropertySlot slot; + object->putDirectInternal(exec->globalData(), propertyName, value, attributes, slot, getJSFunction(value)); } -void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes) +bool JSObject::setPrototypeWithCycleCheck(JSGlobalData& globalData, JSValue prototype) { - putDirectInternal(*globalData, propertyName, value, attributes); -} + JSValue checkFor = this; + if (this->isGlobalObject()) + checkFor = jsCast(this)->globalExec()->thisValue(); -void JSObject::putWithAttributes(JSGlobalData* globalData, unsigned propertyName, JSValue value, unsigned attributes) -{ - putWithAttributes(globalData, Identifier::from(globalData, propertyName), value, attributes); + 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 checkReadOnly, PutPropertySlot& slot) +bool JSObject::allowsAccessFrom(ExecState* exec) { - putDirectInternal(exec->globalData(), propertyName, value, attributes, checkReadOnly, slot); + JSGlobalObject* globalObject = isGlobalThis() ? jsCast(this)->unwrappedObject() : this->globalObject(); + return globalObject->globalObjectMethodTable()->allowsAccessFrom(globalObject, exec); } -void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes) +void JSObject::putDirectAccessor(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes) { - putDirectInternal(exec->globalData(), propertyName, value, attributes); -} + ASSERT(value.isGetterSetter() && (attributes & Accessor)); -void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes) -{ - putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes); + PutPropertySlot slot; + putDirectInternal(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 @@ -215,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(cell); + + if (!thisObject->staticFunctionsReified()) + thisObject->reifyStaticFunctionsForDelete(exec); + unsigned attributes; JSCell* specificValue; - if (m_structure->get(exec->globalData(), 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(exec->globalData(), 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? @@ -238,12 +281,13 @@ bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName) bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const { PropertySlot slot; - return const_cast(this)->getOwnPropertySlot(exec, propertyName, slot); + return const_cast(this)->methodTable()->getOwnPropertySlot(const_cast(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(cell); + return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, propertyName)); } static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName) @@ -268,29 +312,29 @@ 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; } @@ -311,96 +355,7 @@ 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(exec->globalData(), propertyName); - if (object && object.isGetterSetter()) { - ASSERT(m_structure->hasGetterSetterProperties()); - asGetterSetter(object)->setGetter(exec->globalData(), getterFunction); - return; - } - - JSGlobalData& globalData = exec->globalData(); - PutPropertySlot slot; - GetterSetter* getterSetter = new (exec) GetterSetter(exec); - putDirectInternal(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()) - setStructure(exec->globalData(), Structure::getterSetterTransition(globalData, m_structure.get())); - } - - m_structure->setHasGetterSetterProperties(true); - getterSetter->setGetter(globalData, getterFunction); -} - -void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes) -{ - JSValue object = getDirect(exec->globalData(), propertyName); - if (object && object.isGetterSetter()) { - ASSERT(m_structure->hasGetterSetterProperties()); - asGetterSetter(object)->setSetter(exec->globalData(), 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()) - setStructure(exec->globalData(), Structure::getterSetterTransition(exec->globalData(), m_structure.get())); - } - - m_structure->setHasGetterSetterProperties(true); - getterSetter->setSetter(exec->globalData(), setterFunction); -} - -JSValue JSObject::lookupGetter(ExecState* exec, const Identifier& propertyName) -{ - JSObject* object = this; - while (true) { - if (JSValue value = object->getDirect(exec->globalData(), 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* exec, const Identifier& propertyName) -{ - JSObject* object = this; - while (true) { - if (JSValue value = object->getDirect(exec->globalData(), 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; @@ -421,7 +376,7 @@ bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto) bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const { PropertyDescriptor descriptor; - if (!const_cast(this)->getOwnPropertyDescriptor(exec, propertyName, descriptor)) + if (!const_cast(this)->methodTable()->getOwnPropertyDescriptor(const_cast(this), exec, propertyName, descriptor)) return false; return descriptor.enumerable(); } @@ -429,7 +384,7 @@ bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyN bool JSObject::getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificValue) const { unsigned attributes; - if (m_structure->get(exec->globalData(), 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 @@ -439,20 +394,20 @@ bool JSObject::getPropertySpecificValue(ExecState* exec, const Identifier& prope 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; @@ -460,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(exec->globalData(), 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 @@ -479,31 +435,23 @@ 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*, JSGlobalObject*) const -{ - return const_cast(this); -} - -JSObject* JSObject::toThisObject(ExecState*) const -{ - return const_cast(this); -} - -JSValue JSObject::toStrictThisObject(ExecState*) const +JSObject* JSObject::toThisObject(JSCell* cell, ExecState*) { - return const_cast(this); + return jsCast(cell); } JSObject* JSObject::unwrappedObject() { + if (isGlobalThis()) + return jsCast(this)->unwrappedObject(); return this; } @@ -512,7 +460,7 @@ void JSObject::seal(JSGlobalData& globalData) if (isSealed(globalData)) return; preventExtensions(globalData); - setStructure(globalData, Structure::sealTransition(globalData, m_structure.get())); + setStructure(globalData, Structure::sealTransition(globalData, structure())); } void JSObject::freeze(JSGlobalData& globalData) @@ -520,53 +468,66 @@ void JSObject::freeze(JSGlobalData& globalData) if (isFrozen(globalData)) return; preventExtensions(globalData); - setStructure(globalData, Structure::freezeTransition(globalData, m_structure.get())); + setStructure(globalData, Structure::freezeTransition(globalData, structure())); } void JSObject::preventExtensions(JSGlobalData& globalData) { + if (isJSArray(this)) + asArray(this)->enterDictionaryMode(globalData); if (isExtensible()) - setStructure(globalData, Structure::preventExtensionsTransition(globalData, m_structure.get())); + setStructure(globalData, Structure::preventExtensionsTransition(globalData, structure())); +} + +// 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) +{ + 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::removeDirect(JSGlobalData& globalData, const Identifier& propertyName) { - if (m_structure->get(globalData, propertyName) == WTF::notFound) + if (structure()->get(globalData, propertyName) == WTF::notFound) return; size_t offset; - if (m_structure->isUncacheableDictionary()) { - offset = m_structure->removePropertyWithoutTransition(globalData, propertyName); + if (structure()->isUncacheableDictionary()) { + offset = structure()->removePropertyWithoutTransition(globalData, propertyName); if (offset != WTF::notFound) putUndefinedAtDirectOffset(offset); return; } - setStructure(globalData, Structure::removePropertyTransition(globalData, m_structure.get(), propertyName, offset)); + setStructure(globalData, Structure::removePropertyTransition(globalData, structure(), propertyName, offset)); if (offset != WTF::notFound) putUndefinedAtDirectOffset(offset); } -void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr) -{ - putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr); -} - -void JSObject::putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr) -{ - putDirectFunction(exec->globalData(), Identifier(exec, function->name(exec)), function, attr); -} - -void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr) -{ - putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr); -} - -void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr) -{ - putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr); -} - NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarrierBase* location) { if (JSObject* getterFunction = asGetterSetter(location->get())->getter()) { @@ -580,39 +541,56 @@ NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarr Structure* JSObject::createInheritorID(JSGlobalData& globalData) { - m_inheritorID.set(globalData, this, createEmptyObjectStructure(globalData, this)); + JSGlobalObject* globalObject; + if (isGlobalThis()) + globalObject = static_cast(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) { ASSERT(newSize > oldSize); - // It's important that this function not rely on m_structure, since + // It's important that this function not rely on structure(), since // we might be in the middle of a transition. - bool wasInline = (oldSize < JSObject::baseExternalStorageCapacity); - PropertyStorage oldPropertyStorage = m_propertyStorage; - PropertyStorage newPropertyStorage = new WriteBarrierBase[newSize]; + PropertyStorage oldPropertyStorage = m_propertyStorage.get(); + PropertyStorage newPropertyStorage = 0; - for (unsigned i = 0; i < oldSize; ++i) - newPropertyStorage[i] = oldPropertyStorage[i]; + 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) * newSize, &temp)) + CRASH(); + newPropertyStorage = static_cast(temp); - if (!wasInline) - delete [] oldPropertyStorage; + 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) * oldSize, sizeof(WriteBarrierBase) * newSize)) + CRASH(); + newPropertyStorage = static_cast(temp); + } - m_propertyStorage = newPropertyStorage; + ASSERT(newPropertyStorage); + return newPropertyStorage; } -bool JSObject::getOwnPropertyDescriptor(ExecState* exec, 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(exec->globalData(), 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; } @@ -620,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()) @@ -633,16 +611,12 @@ static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& p { if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) { if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) { - GetterSetter* accessor = new (exec) GetterSetter(exec); - if (oldDescriptor.getter()) { - attributes |= Getter; - accessor->setGetter(exec->globalData(), asObject(oldDescriptor.getter())); - } - if (oldDescriptor.setter()) { - attributes |= Setter; - accessor->setSetter(exec->globalData(), asObject(oldDescriptor.setter())); - } - target->putWithAttributes(exec, propertyName, accessor, attributes); + 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(); @@ -650,33 +624,64 @@ static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& p newValue = descriptor.value(); else if (oldDescriptor.value()) newValue = oldDescriptor.value(); - target->putWithAttributes(exec, propertyName, newValue, attributes & ~(Getter | Setter)); + 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)) { + if (!object->methodTable()->getOwnPropertyDescriptor(object, exec, propertyName, current)) { // unless extensions are prevented! - if (!isExtensible()) { + 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, this, propertyName, descriptor, descriptor.attributes(), oldDescriptor); + return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributes(), oldDescriptor); } if (descriptor.isEmpty()) @@ -702,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); + object->methodTable()->deleteProperty(object, exec, propertyName); + return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributesOverridingCurrent(current), current); } return true; } @@ -715,8 +720,8 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName 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); + 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 @@ -728,23 +733,17 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName 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, 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); + 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 @@ -761,24 +760,19 @@ bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName return false; } } - JSValue accessor = getDirect(exec->globalData(), 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(exec->globalData(), asObject(descriptor.setter())); - if (descriptor.getter()) - getterSetter->setGetter(exec->globalData(), 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(exec->globalData(), propertyName, getterSetter, attrs); + object->methodTable()->deleteProperty(object, exec, propertyName); + unsigned attrs = descriptor.attributesOverridingCurrent(current); + object->putDirectAccessor(exec->globalData(), propertyName, getterSetter, attrs | Accessor); return true; }