X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/9dae56ea45a0f5f8136a5c93d6f3a7f99399ca73..1df5f87f1309a8daa30dabdee855f48ae40d14ab:/runtime/JSObject.cpp?ds=sidebyside diff --git a/runtime/JSObject.cpp b/runtime/JSObject.cpp index e9e95f6..dbf9ccf 100644 --- a/runtime/JSObject.cpp +++ b/runtime/JSObject.cpp @@ -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, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Apple Inc. All rights reserved. * Copyright (C) 2007 Eric Seidel (eric@webkit.org) * * This library is free software; you can redistribute it and/or @@ -27,9 +27,11 @@ #include "DatePrototype.h" #include "ErrorConstructor.h" #include "GetterSetter.h" +#include "JSFunction.h" #include "JSGlobalObject.h" #include "NativeErrorConstructor.h" #include "ObjectPrototype.h" +#include "PropertyDescriptor.h" #include "PropertyNameArray.h" #include "Lookup.h" #include "Nodes.h" @@ -37,54 +39,55 @@ #include #include -#define JSOBJECT_MARK_TRACING 0 - -#if JSOBJECT_MARK_TRACING - -#define JSOBJECT_MARK_BEGIN() \ - static int markStackDepth = 0; \ - for (int i = 0; i < markStackDepth; i++) \ - putchar('-'); \ - printf("%s (%p)\n", className().UTF8String().c_str(), this); \ - markStackDepth++; \ - -#define JSOBJECT_MARK_END() \ - markStackDepth--; +namespace JSC { -#else // JSOBJECT_MARK_TRACING +ASSERT_CLASS_FITS_IN_CELL(JSObject); +ASSERT_CLASS_FITS_IN_CELL(JSNonFinalObject); +ASSERT_CLASS_FITS_IN_CELL(JSFinalObject); -#define JSOBJECT_MARK_BEGIN() -#define JSOBJECT_MARK_END() +const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property."; -#endif // JSOBJECT_MARK_TRACING +const ClassInfo JSObject::s_info = { "Object", 0, 0, 0 }; -namespace JSC { +static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + // Add properties from the static hashtables of properties + for (; classInfo; classInfo = classInfo->parentClass) { + const HashTable* table = classInfo->propHashTable(exec); + if (!table) + continue; + table->initializeIfNeeded(exec); + ASSERT(table->table); -ASSERT_CLASS_FITS_IN_CELL(JSObject); + int hashSizeMask = table->compactSize - 1; + const HashEntry* entry = table->table; + for (int i = 0; i <= hashSizeMask; ++i, ++entry) { + if (entry->key() && (!(entry->attributes() & DontEnum) || (mode == IncludeDontEnumProperties))) + propertyNames.add(entry->key()); + } + } +} -void JSObject::mark() +void JSObject::visitChildren(SlotVisitor& visitor) { - JSOBJECT_MARK_BEGIN(); - - JSCell::mark(); - m_structure->mark(); + ASSERT_GC_OBJECT_INHERITS(this, &s_info); +#ifndef NDEBUG + bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation; + visitor.m_isCheckingForDefaultMarkViolation = false; +#endif - size_t storageSize = m_structure->propertyStorageSize(); - for (size_t i = 0; i < storageSize; ++i) { - JSValuePtr v = m_propertyStorage[i]; - if (!v.marked()) - v.mark(); - } + visitChildrenDirect(visitor); - JSOBJECT_MARK_END(); +#ifndef NDEBUG + visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation; +#endif } UString JSObject::className() const { const ClassInfo* info = classInfo(); - if (info) - return info->className; - return "Object"; + ASSERT(info); + return info->className; } bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot) @@ -94,11 +97,11 @@ bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, Proper static void throwSetterError(ExecState* exec) { - throwError(exec, TypeError, "setting a property that has only a getter"); + throwError(exec, createTypeError(exec, "setting a property that has only a getter")); } // ECMA 8.6.2.2 -void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValuePtr value, PutPropertySlot& slot) +void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot) { ASSERT(value); ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this)); @@ -107,37 +110,32 @@ void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValuePtr v // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla. if (!value.isObject() && !value.isNull()) return; - - JSValuePtr 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); + if (!setPrototypeWithCycleCheck(exec->globalData(), value)) + throwError(exec, createError(exec, "cyclic __proto__ value")); return; } // Check if there are any setters or getters in the prototype chain - JSValuePtr prototype; + JSValue prototype; for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) { prototype = obj->prototype(); if (prototype.isNull()) { - putDirect(propertyName, value, 0, true, slot); + if (!putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot) && slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return; } } unsigned attributes; - if ((m_structure->get(propertyName, attributes) != WTF::notFound) && attributes & ReadOnly) + 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 (JSValuePtr gs = obj->getDirect(propertyName)) { + if (JSValue gs = obj->getDirect(exec->globalData(), propertyName)) { if (gs.isGetterSetter()) { JSObject* setterFunc = asGetterSetter(gs)->setter(); if (!setterFunc) { @@ -147,7 +145,7 @@ void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValuePtr v CallData callData; CallType callType = setterFunc->getCallData(callData); - ArgList args; + MarkedArgumentBuffer args; args.append(value); call(exec, setterFunc, callType, callData, this, args); return; @@ -163,22 +161,43 @@ void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValuePtr v break; } - putDirect(propertyName, value, 0, true, slot); + if (!putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot) && slot.isStrictMode()) + throwTypeError(exec, StrictModeReadonlyPropertyWriteError); return; } -void JSObject::put(ExecState* exec, unsigned propertyName, JSValuePtr value) +void JSObject::put(ExecState* exec, unsigned propertyName, JSValue value) { PutPropertySlot slot; put(exec, Identifier::from(exec, propertyName), value, slot); } -void JSObject::putWithAttributes(ExecState*, const Identifier& propertyName, JSValuePtr value, unsigned attributes) +void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +{ + putDirectInternal(*globalData, propertyName, value, attributes, checkReadOnly, slot); +} + +void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes) +{ + putDirectInternal(*globalData, propertyName, value, attributes); +} + +void JSObject::putWithAttributes(JSGlobalData* globalData, unsigned propertyName, JSValue value, unsigned attributes) +{ + putWithAttributes(globalData, Identifier::from(globalData, propertyName), value, attributes); +} + +void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot) +{ + putDirectInternal(exec->globalData(), propertyName, value, attributes, checkReadOnly, slot); +} + +void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes) { - putDirect(propertyName, value, attributes); + putDirectInternal(exec->globalData(), propertyName, value, attributes); } -void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValuePtr value, unsigned attributes) +void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes) { putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes); } @@ -199,10 +218,11 @@ bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName) { unsigned attributes; - if (m_structure->get(propertyName, attributes) != WTF::notFound) { + JSCell* specificValue; + if (m_structure->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound) { if ((attributes & DontDelete)) return false; - removeDirect(propertyName); + removeDirect(exec->globalData(), propertyName); return true; } @@ -226,11 +246,11 @@ bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName) return deleteProperty(exec, Identifier::from(exec, propertyName)); } -static ALWAYS_INLINE JSValuePtr callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName) +static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName) { - JSValuePtr function = object->get(exec, 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(); @@ -239,16 +259,16 @@ static ALWAYS_INLINE JSValuePtr callDefaultValueFunction(ExecState* exec, const if (exec->hadException()) return exec->exception(); - JSValuePtr result = call(exec, function, callType, callData, const_cast(object), exec->emptyList()); + JSValue result = call(exec, function, callType, callData, const_cast(object), exec->emptyList()); ASSERT(!result.isGetterSetter()); if (exec->hadException()) return exec->exception(); if (result.isObject()) - return noValue(); + return JSValue(); return result; } -bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValuePtr& result) +bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) { result = defaultValue(exec, PreferNumber); number = result.toNumber(exec); @@ -256,18 +276,18 @@ bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValuePtr& r } // ECMA 8.6.2.6 -JSValuePtr JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const +JSValue JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const { // Must call toString first for Date objects. if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) { - JSValuePtr value = callDefaultValueFunction(exec, this, exec->propertyNames().toString); + JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().toString); if (value) return value; value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf); if (value) return value; } else { - JSValuePtr value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf); + JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf); if (value) return value; value = callDefaultValueFunction(exec, this, exec->propertyNames().toString); @@ -277,7 +297,7 @@ JSValuePtr JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) 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 @@ -291,65 +311,72 @@ const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifi return 0; } -void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction) +void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes) { - JSValuePtr object = getDirect(propertyName); + if (propertyName == exec->propertyNames().underscoreProto) { + // Defining a getter for __proto__ is silently ignored. + return; + } + + JSValue object = getDirect(exec->globalData(), propertyName); if (object && object.isGetterSetter()) { ASSERT(m_structure->hasGetterSetterProperties()); - asGetterSetter(object)->setGetter(getterFunction); + asGetterSetter(object)->setGetter(exec->globalData(), getterFunction); return; } + JSGlobalData& globalData = exec->globalData(); PutPropertySlot slot; - GetterSetter* getterSetter = new (exec) GetterSetter; - putDirect(propertyName, getterSetter, None, true, 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()) { - RefPtr structure = Structure::getterSetterTransition(m_structure); - setStructure(structure.release()); - } + if (!m_structure->isDictionary()) + setStructure(exec->globalData(), Structure::getterSetterTransition(globalData, m_structure.get())); } m_structure->setHasGetterSetterProperties(true); - getterSetter->setGetter(getterFunction); + getterSetter->setGetter(globalData, getterFunction); } -void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction) +void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes) { - JSValuePtr object = getDirect(propertyName); + if (propertyName == exec->propertyNames().underscoreProto) { + // Defining a setter for __proto__ is silently ignored. + return; + } + + JSValue object = getDirect(exec->globalData(), propertyName); if (object && object.isGetterSetter()) { ASSERT(m_structure->hasGetterSetterProperties()); - asGetterSetter(object)->setSetter(setterFunction); + asGetterSetter(object)->setSetter(exec->globalData(), setterFunction); return; } PutPropertySlot slot; - GetterSetter* getterSetter = new (exec) GetterSetter; - putDirect(propertyName, getterSetter, None, true, 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::getterSetterTransition(m_structure); - setStructure(structure.release()); - } + if (!m_structure->isDictionary()) + setStructure(exec->globalData(), Structure::getterSetterTransition(exec->globalData(), m_structure.get())); } m_structure->setHasGetterSetterProperties(true); - getterSetter->setSetter(setterFunction); + getterSetter->setSetter(exec->globalData(), setterFunction); } -JSValuePtr JSObject::lookupGetter(ExecState*, const Identifier& propertyName) +JSValue JSObject::lookupGetter(ExecState* exec, const Identifier& propertyName) { JSObject* object = this; while (true) { - if (JSValuePtr value = object->getDirect(propertyName)) { + if (JSValue value = object->getDirect(exec->globalData(), propertyName)) { if (!value.isGetterSetter()) return jsUndefined(); JSObject* functionObject = asGetterSetter(value)->getter(); @@ -364,11 +391,11 @@ JSValuePtr JSObject::lookupGetter(ExecState*, const Identifier& propertyName) } } -JSValuePtr JSObject::lookupSetter(ExecState*, const Identifier& propertyName) +JSValue JSObject::lookupSetter(ExecState* exec, const Identifier& propertyName) { JSObject* object = this; while (true) { - if (JSValuePtr value = object->getDirect(propertyName)) { + if (JSValue value = object->getDirect(exec->globalData(), propertyName)) { if (!value.isGetterSetter()) return jsUndefined(); JSObject* functionObject = asGetterSetter(value)->setter(); @@ -383,15 +410,15 @@ JSValuePtr JSObject::lookupSetter(ExecState*, const Identifier& propertyName) } } -bool JSObject::hasInstance(ExecState* exec, JSValuePtr value, JSValuePtr proto) +bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto) { - if (!proto.isObject()) { - throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property."); + if (!value.isObject()) return false; - } - if (!value.isObject()) + if (!proto.isObject()) { + throwError(exec, createTypeError(exec, "instanceof called on an object with an invalid prototype property.")); return false; + } JSObject* object = asObject(value); while ((object = object->prototype().getObject())) { @@ -403,30 +430,50 @@ bool JSObject::hasInstance(ExecState* exec, JSValuePtr value, JSValuePtr proto) bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const { - unsigned attributes; - if (!getPropertyAttributes(exec, propertyName, attributes)) + PropertyDescriptor descriptor; + if (!const_cast(this)->getOwnPropertyDescriptor(exec, propertyName, descriptor)) return false; - return !(attributes & DontEnum); + return descriptor.enumerable(); } -bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const +bool JSObject::getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificValue) const { - if (m_structure->get(propertyName, attributes) != WTF::notFound) - return true; - - // Look in the static hashtable of properties - const HashEntry* entry = findPropertyHashEntry(exec, propertyName); - if (entry) { - attributes = entry->attributes(); + unsigned attributes; + if (m_structure->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound) return true; - } - + + // This could be a function within the static table? - should probably + // also look in the hash? This currently should not be a problem, since + // we've currently always call 'get' first, which should have populated + // the normal storage. return false; } -void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames) +void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) +{ + getOwnPropertyNames(exec, propertyNames, mode); + + if (prototype().isNull()) + return; + + JSObject* prototype = asObject(this->prototype()); + while(1) { + if (prototype->structure()->typeInfo().overridesGetPropertyNames()) { + prototype->getPropertyNames(exec, propertyNames, mode); + break; + } + prototype->getOwnPropertyNames(exec, propertyNames, mode); + JSValue nextProto = prototype->prototype(); + if (nextProto.isNull()) + break; + prototype = asObject(nextProto); + } +} + +void JSObject::getOwnPropertyNames(ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode) { - m_structure->getEnumerablePropertyNames(exec, propertyNames, this); + m_structure->getPropertyNames(exec->globalData(), propertyNames, mode); + getClassPropertyNames(exec, classInfo(), propertyNames, mode); } bool JSObject::toBoolean(ExecState*) const @@ -436,7 +483,7 @@ bool JSObject::toBoolean(ExecState*) const double JSObject::toNumber(ExecState* exec) const { - JSValuePtr primitive = toPrimitive(exec, PreferNumber); + JSValue primitive = toPrimitive(exec, PreferNumber); if (exec->hadException()) // should be picked up soon in Nodes.cpp return 0.0; return primitive.toNumber(exec); @@ -444,13 +491,13 @@ double JSObject::toNumber(ExecState* exec) const UString JSObject::toString(ExecState* exec) const { - JSValuePtr primitive = toPrimitive(exec, PreferString); + JSValue primitive = toPrimitive(exec, PreferString); if (exec->hadException()) return ""; return primitive.toString(exec); } -JSObject* JSObject::toObject(ExecState*) const +JSObject* JSObject::toObject(ExecState*, JSGlobalObject*) const { return const_cast(this); } @@ -460,59 +507,294 @@ JSObject* JSObject::toThisObject(ExecState*) const return const_cast(this); } +JSValue JSObject::toStrictThisObject(ExecState*) const +{ + return const_cast(this); +} + JSObject* JSObject::unwrappedObject() { return this; } -void JSObject::removeDirect(const Identifier& propertyName) +void JSObject::seal(JSGlobalData& globalData) { + if (isSealed(globalData)) + return; + preventExtensions(globalData); + setStructure(globalData, Structure::sealTransition(globalData, m_structure.get())); +} + +void JSObject::freeze(JSGlobalData& globalData) +{ + if (isFrozen(globalData)) + return; + preventExtensions(globalData); + setStructure(globalData, Structure::freezeTransition(globalData, m_structure.get())); +} + +void JSObject::preventExtensions(JSGlobalData& globalData) +{ + if (isExtensible()) + setStructure(globalData, Structure::preventExtensionsTransition(globalData, m_structure.get())); +} + +void JSObject::removeDirect(JSGlobalData& globalData, const Identifier& propertyName) +{ + if (m_structure->get(globalData, propertyName) == WTF::notFound) + return; + size_t offset; - if (m_structure->isDictionary()) { - offset = m_structure->removePropertyWithoutTransition(propertyName); + if (m_structure->isUncacheableDictionary()) { + offset = m_structure->removePropertyWithoutTransition(globalData, propertyName); if (offset != WTF::notFound) - m_propertyStorage[offset] = jsUndefined(); + putUndefinedAtDirectOffset(offset); return; } - RefPtr structure = Structure::removePropertyTransition(m_structure, propertyName, offset); + setStructure(globalData, Structure::removePropertyTransition(globalData, m_structure.get(), propertyName, offset)); if (offset != WTF::notFound) - m_propertyStorage[offset] = jsUndefined(); - setStructure(structure.release()); + putUndefinedAtDirectOffset(offset); } void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr) { - putDirect(Identifier(exec, function->name(&exec->globalData())), function, 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) { - putDirectWithoutTransition(Identifier(exec, function->name(&exec->globalData())), function, attr); + putDirectFunctionWithoutTransition(exec->globalData(), Identifier(exec, function->name(exec)), function, attr); } -NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValuePtr* location) +void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, JSFunction* function, unsigned attr) { - if (JSObject* getterFunction = asGetterSetter(*location)->getter()) - slot.setGetterSlot(getterFunction); - else + 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()) { + 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); + m_inheritorID.set(globalData, this, createEmptyObjectStructure(globalData, this)); + ASSERT(m_inheritorID->isEmpty()); return m_inheritorID.get(); } void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize) { - allocatePropertyStorageInline(oldSize, 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::baseExternalStorageCapacity); + + PropertyStorage oldPropertyStorage = m_propertyStorage; + PropertyStorage newPropertyStorage = new WriteBarrierBase[newSize]; + + for (unsigned i = 0; i < oldSize; ++i) + newPropertyStorage[i] = oldPropertyStorage[i]; + + if (!wasInline) + delete [] oldPropertyStorage; + + m_propertyStorage = newPropertyStorage; +} + +bool JSObject::getOwnPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + unsigned attributes = 0; + JSCell* cell = 0; + size_t offset = m_structure->get(exec->globalData(), propertyName, attributes, cell); + if (offset == WTF::notFound) + return false; + descriptor.setDescriptor(getDirectOffset(offset), attributes); + return true; +} + +bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor) +{ + JSObject* object = this; + while (true) { + if (object->getOwnPropertyDescriptor(exec, propertyName, descriptor)) + return true; + JSValue prototype = object->prototype(); + if (!prototype.isObject()) + return false; + object = asObject(prototype); + } +} + +static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor) +{ + 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); + return true; + } + JSValue newValue = jsUndefined(); + if (descriptor.value()) + newValue = descriptor.value(); + else if (oldDescriptor.value()) + newValue = oldDescriptor.value(); + target->putWithAttributes(exec, propertyName, newValue, attributes & ~(Getter | Setter)); + 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(); +} + +bool JSObject::defineOwnProperty(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException) +{ + // If we have a new property we can just put it on normally + PropertyDescriptor current; + if (!getOwnPropertyDescriptor(exec, propertyName, current)) { + // unless extensions are prevented! + if (!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); + } + + if (descriptor.isEmpty()) + return true; + + if (current.equalTo(exec, descriptor)) + return true; + + // Filter out invalid changes + if (!current.configurable()) { + if (descriptor.configurable()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to configurable attribute of unconfigurable property.")); + return false; + } + if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to change enumerable attribute of unconfigurable property.")); + return false; + } + } + + // 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); + } + return true; + } + + // Changing between a normal property or an accessor property + if (descriptor.isDataDescriptor() != current.isDataDescriptor()) { + if (!current.configurable()) { + if (throwException) + 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); + } + + // Changing the value and attributes of an existing property + if (descriptor.isDataDescriptor()) { + if (!current.configurable()) { + if (!current.writable() && descriptor.writable()) { + if (throwException) + 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 (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); + } + + // Changing the accessor functions of an existing accessor property + ASSERT(descriptor.isAccessorDescriptor()); + if (!current.configurable()) { + if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to change the setter of an unconfigurable property.")); + return false; + } + if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) { + if (throwException) + throwError(exec, createTypeError(exec, "Attempting to change the getter of an unconfigurable property.")); + return false; + } + } + JSValue accessor = 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())); + 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); + return true; } -JSObject* constructEmptyObject(ExecState* exec) +JSObject* throwTypeError(ExecState* exec, const UString& message) { - return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure()); + return throwError(exec, createTypeError(exec, message)); } } // namespace JSC