2 * Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3 * Copyright (C) 2001 Peter Kelly (pmk@post.com)
4 * Copyright (C) 2003, 2004, 2005, 2006, 2008, 2009 Apple Inc. All rights reserved.
5 * Copyright (C) 2007 Eric Seidel (eric@webkit.org)
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
27 #include "DatePrototype.h"
28 #include "ErrorConstructor.h"
29 #include "GetterSetter.h"
30 #include "JSFunction.h"
31 #include "JSGlobalObject.h"
32 #include "JSGlobalThis.h"
34 #include "NativeErrorConstructor.h"
36 #include "ObjectPrototype.h"
37 #include "Operations.h"
38 #include "PropertyDescriptor.h"
39 #include "PropertyNameArray.h"
41 #include <wtf/Assertions.h>
45 ASSERT_CLASS_FITS_IN_CELL(JSObject);
46 ASSERT_CLASS_FITS_IN_CELL(JSNonFinalObject);
47 ASSERT_CLASS_FITS_IN_CELL(JSFinalObject);
49 const char* StrictModeReadonlyPropertyWriteError = "Attempted to assign to readonly property.";
51 const ClassInfo JSObject::s_info = { "Object", 0, 0, 0, CREATE_METHOD_TABLE(JSObject) };
53 const ClassInfo JSFinalObject::s_info = { "Object", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSFinalObject) };
55 static inline void getClassPropertyNames(ExecState* exec, const ClassInfo* classInfo, PropertyNameArray& propertyNames, EnumerationMode mode)
57 // Add properties from the static hashtables of properties
58 for (; classInfo; classInfo = classInfo->parentClass) {
59 const HashTable* table = classInfo->propHashTable(exec);
62 table->initializeIfNeeded(exec);
65 int hashSizeMask = table->compactSize - 1;
66 const HashEntry* entry = table->table;
67 for (int i = 0; i <= hashSizeMask; ++i, ++entry) {
68 if (entry->key() && (!(entry->attributes() & DontEnum) || (mode == IncludeDontEnumProperties)))
69 propertyNames.add(entry->key());
74 void JSObject::finalize(JSCell* cell)
76 delete [] jsCast<JSObject*>(cell)->m_propertyStorage.get();
79 void JSObject::vtableAnchor()
83 void JSObject::visitChildren(JSCell* cell, SlotVisitor& visitor)
85 JSObject* thisObject = jsCast<JSObject*>(cell);
86 ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
88 bool wasCheckingForDefaultMarkViolation = visitor.m_isCheckingForDefaultMarkViolation;
89 visitor.m_isCheckingForDefaultMarkViolation = false;
92 JSCell::visitChildren(thisObject, visitor);
94 PropertyStorage storage = thisObject->propertyStorage();
95 size_t storageSize = thisObject->structure()->propertyStorageSize();
96 visitor.appendValues(storage, storageSize);
97 if (thisObject->m_inheritorID)
98 visitor.append(&thisObject->m_inheritorID);
101 visitor.m_isCheckingForDefaultMarkViolation = wasCheckingForDefaultMarkViolation;
105 UString JSObject::className(const JSObject* object)
107 const ClassInfo* info = object->classInfo();
109 return info->className;
112 bool JSObject::getOwnPropertySlotByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, PropertySlot& slot)
114 JSObject* thisObject = jsCast<JSObject*>(cell);
115 return thisObject->methodTable()->getOwnPropertySlot(thisObject, exec, Identifier::from(exec, propertyName), slot);
118 static void throwSetterError(ExecState* exec)
120 throwError(exec, createTypeError(exec, "setting a property that has only a getter"));
124 void JSObject::put(JSCell* cell, ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
126 JSObject* thisObject = jsCast<JSObject*>(cell);
128 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(thisObject));
129 JSGlobalData& globalData = exec->globalData();
131 if (propertyName == exec->propertyNames().underscoreProto) {
132 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
133 if (!value.isObject() && !value.isNull())
136 if (!thisObject->isExtensible()) {
137 if (slot.isStrictMode())
138 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
142 if (!thisObject->setPrototypeWithCycleCheck(globalData, value))
143 throwError(exec, createError(exec, "cyclic __proto__ value"));
147 // Check if there are any setters or getters in the prototype chain
149 for (JSObject* obj = thisObject; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) {
150 prototype = obj->prototype();
151 if (prototype.isNull()) {
152 if (!thisObject->putDirectInternal(globalData, propertyName, value, 0, true, slot, getJSFunction(value)) && slot.isStrictMode())
153 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
159 JSCell* specificValue;
160 if ((thisObject->structure()->get(globalData, propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly) {
161 if (slot.isStrictMode())
162 throwError(exec, createTypeError(exec, StrictModeReadonlyPropertyWriteError));
166 for (JSObject* obj = thisObject; ; obj = asObject(prototype)) {
167 if (JSValue gs = obj->getDirect(globalData, propertyName)) {
168 if (gs.isGetterSetter()) {
169 JSObject* setterFunc = asGetterSetter(gs)->setter();
171 throwSetterError(exec);
176 CallType callType = setterFunc->methodTable()->getCallData(setterFunc, callData);
177 MarkedArgumentBuffer args;
180 // If this is WebCore's global object then we need to substitute the shell.
181 call(exec, setterFunc, callType, callData, thisObject->methodTable()->toThisObject(thisObject, exec), args);
185 // If there's an existing property on the object or one of its
186 // prototypes it should be replaced, so break here.
190 prototype = obj->prototype();
191 if (prototype.isNull())
195 if (!thisObject->putDirectInternal(globalData, propertyName, value, 0, true, slot, getJSFunction(value)) && slot.isStrictMode())
196 throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
200 void JSObject::putByIndex(JSCell* cell, ExecState* exec, unsigned propertyName, JSValue value)
202 PutPropertySlot slot;
203 JSObject* thisObject = jsCast<JSObject*>(cell);
204 thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);
207 void JSObject::putWithAttributes(JSObject* object, ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes)
209 PutPropertySlot slot;
210 object->putDirectInternal(exec->globalData(), propertyName, value, attributes, true, slot, getJSFunction(value));
213 void JSObject::putWithAttributes(JSGlobalData* globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
215 PutPropertySlot slot;
216 putDirectInternal(*globalData, propertyName, value, attributes, true, slot, getJSFunction(value));
219 bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const
222 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
225 bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
228 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
232 bool JSObject::deleteProperty(JSCell* cell, ExecState* exec, const Identifier& propertyName)
234 JSObject* thisObject = jsCast<JSObject*>(cell);
236 if (!thisObject->staticFunctionsReified())
237 thisObject->reifyStaticFunctionsForDelete(exec);
240 JSCell* specificValue;
241 if (thisObject->structure()->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound) {
242 if ((attributes & DontDelete))
244 thisObject->removeDirect(exec->globalData(), propertyName);
248 // Look in the static hashtable of properties
249 const HashEntry* entry = thisObject->findPropertyHashEntry(exec, propertyName);
250 if (entry && entry->attributes() & DontDelete)
251 return false; // this builtin property can't be deleted
253 // FIXME: Should the code here actually do some deletion?
257 bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
260 return const_cast<JSObject*>(this)->methodTable()->getOwnPropertySlot(const_cast<JSObject*>(this), exec, propertyName, slot);
263 bool JSObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName)
265 JSObject* thisObject = jsCast<JSObject*>(cell);
266 return thisObject->methodTable()->deleteProperty(thisObject, exec, Identifier::from(exec, propertyName));
269 static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
271 JSValue function = object->get(exec, propertyName);
273 CallType callType = getCallData(function, callData);
274 if (callType == CallTypeNone)
275 return exec->exception();
277 // Prevent "toString" and "valueOf" from observing execution if an exception
279 if (exec->hadException())
280 return exec->exception();
282 JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
283 ASSERT(!result.isGetterSetter());
284 if (exec->hadException())
285 return exec->exception();
286 if (result.isObject())
291 bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result) const
293 result = methodTable()->defaultValue(this, exec, PreferNumber);
294 number = result.toNumber(exec);
295 return !result.isString();
299 JSValue JSObject::defaultValue(const JSObject* object, ExecState* exec, PreferredPrimitiveType hint)
301 // Must call toString first for Date objects.
302 if ((hint == PreferString) || (hint != PreferNumber && object->prototype() == exec->lexicalGlobalObject()->datePrototype())) {
303 JSValue value = callDefaultValueFunction(exec, object, exec->propertyNames().toString);
306 value = callDefaultValueFunction(exec, object, exec->propertyNames().valueOf);
310 JSValue value = callDefaultValueFunction(exec, object, exec->propertyNames().valueOf);
313 value = callDefaultValueFunction(exec, object, exec->propertyNames().toString);
318 ASSERT(!exec->hadException());
320 return throwError(exec, createTypeError(exec, "No default value"));
323 const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
325 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
326 if (const HashTable* propHashTable = info->propHashTable(exec)) {
327 if (const HashEntry* entry = propHashTable->entry(exec, propertyName))
334 void JSObject::defineGetter(JSObject* thisObject, ExecState* exec, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes)
336 if (propertyName == exec->propertyNames().underscoreProto) {
337 // Defining a getter for __proto__ is silently ignored.
341 JSValue object = thisObject->getDirect(exec->globalData(), propertyName);
342 if (object && object.isGetterSetter()) {
343 ASSERT(thisObject->structure()->hasGetterSetterProperties());
344 asGetterSetter(object)->setGetter(exec->globalData(), getterFunction);
348 JSGlobalData& globalData = exec->globalData();
349 PutPropertySlot slot;
350 GetterSetter* getterSetter = GetterSetter::create(exec);
351 thisObject->putDirectInternal(globalData, propertyName, getterSetter, attributes | Getter, true, slot, 0);
353 // putDirect will change our Structure if we add a new property. For
354 // getters and setters, though, we also need to change our Structure
355 // if we override an existing non-getter or non-setter.
356 if (slot.type() != PutPropertySlot::NewProperty) {
357 if (!thisObject->structure()->isDictionary())
358 thisObject->setStructure(exec->globalData(), Structure::getterSetterTransition(globalData, thisObject->structure()));
361 thisObject->structure()->setHasGetterSetterProperties(true);
362 getterSetter->setGetter(globalData, getterFunction);
365 void JSObject::initializeGetterSetterProperty(ExecState* exec, const Identifier& propertyName, GetterSetter* getterSetter, unsigned attributes)
367 // Set an inital property on an object; the property must not already exist & the attribute flags must be set correctly.
368 ASSERT(structure()->get(exec->globalData(), propertyName) == WTF::notFound);
369 ASSERT(static_cast<bool>(getterSetter->getter()) == static_cast<bool>(attributes & Getter));
370 ASSERT(static_cast<bool>(getterSetter->setter()) == static_cast<bool>(attributes & Setter));
372 JSGlobalData& globalData = exec->globalData();
373 PutPropertySlot slot;
374 putDirectInternal(globalData, propertyName, getterSetter, attributes | Getter, true, slot, 0);
376 // putDirect will change our Structure if we add a new property. For
377 // getters and setters, though, we also need to change our Structure
378 // if we override an existing non-getter or non-setter.
379 if (slot.type() != PutPropertySlot::NewProperty) {
380 if (!structure()->isDictionary())
381 setStructure(exec->globalData(), Structure::getterSetterTransition(globalData, structure()));
384 structure()->setHasGetterSetterProperties(true);
387 void JSObject::defineSetter(JSObject* thisObject, ExecState* exec, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes)
389 if (propertyName == exec->propertyNames().underscoreProto) {
390 // Defining a setter for __proto__ is silently ignored.
394 JSValue object = thisObject->getDirect(exec->globalData(), propertyName);
395 if (object && object.isGetterSetter()) {
396 ASSERT(thisObject->structure()->hasGetterSetterProperties());
397 asGetterSetter(object)->setSetter(exec->globalData(), setterFunction);
401 PutPropertySlot slot;
402 GetterSetter* getterSetter = GetterSetter::create(exec);
403 thisObject->putDirectInternal(exec->globalData(), propertyName, getterSetter, attributes | Setter, true, slot, 0);
405 // putDirect will change our Structure if we add a new property. For
406 // getters and setters, though, we also need to change our Structure
407 // if we override an existing non-getter or non-setter.
408 if (slot.type() != PutPropertySlot::NewProperty) {
409 if (!thisObject->structure()->isDictionary())
410 thisObject->setStructure(exec->globalData(), Structure::getterSetterTransition(exec->globalData(), thisObject->structure()));
413 thisObject->structure()->setHasGetterSetterProperties(true);
414 getterSetter->setSetter(exec->globalData(), setterFunction);
417 JSValue JSObject::lookupGetter(ExecState* exec, const Identifier& propertyName)
419 PropertyDescriptor descriptor;
420 if (!getPropertyDescriptor(exec, propertyName, descriptor))
421 return jsUndefined();
423 if (!descriptor.getterPresent())
424 return jsUndefined();
426 return descriptor.getter();
429 JSValue JSObject::lookupSetter(ExecState* exec, const Identifier& propertyName)
431 PropertyDescriptor descriptor;
432 if (!getPropertyDescriptor(exec, propertyName, descriptor))
433 return jsUndefined();
435 if (!descriptor.setterPresent())
436 return jsUndefined();
438 return descriptor.setter();
441 bool JSObject::hasInstance(JSObject*, ExecState* exec, JSValue value, JSValue proto)
443 if (!value.isObject())
446 if (!proto.isObject()) {
447 throwError(exec, createTypeError(exec, "instanceof called on an object with an invalid prototype property."));
451 JSObject* object = asObject(value);
452 while ((object = object->prototype().getObject())) {
459 bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
461 PropertyDescriptor descriptor;
462 if (!const_cast<JSObject*>(this)->methodTable()->getOwnPropertyDescriptor(const_cast<JSObject*>(this), exec, propertyName, descriptor))
464 return descriptor.enumerable();
467 bool JSObject::getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificValue) const
470 if (structure()->get(exec->globalData(), propertyName, attributes, specificValue) != WTF::notFound)
473 // This could be a function within the static table? - should probably
474 // also look in the hash? This currently should not be a problem, since
475 // we've currently always call 'get' first, which should have populated
476 // the normal storage.
480 void JSObject::getPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
482 object->methodTable()->getOwnPropertyNames(object, exec, propertyNames, mode);
484 if (object->prototype().isNull())
487 JSObject* prototype = asObject(object->prototype());
489 if (prototype->structure()->typeInfo().overridesGetPropertyNames()) {
490 prototype->methodTable()->getPropertyNames(prototype, exec, propertyNames, mode);
493 prototype->methodTable()->getOwnPropertyNames(prototype, exec, propertyNames, mode);
494 JSValue nextProto = prototype->prototype();
495 if (nextProto.isNull())
497 prototype = asObject(nextProto);
501 void JSObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNames, EnumerationMode mode)
503 object->structure()->getPropertyNamesFromStructure(exec->globalData(), propertyNames, mode);
504 if (!object->staticFunctionsReified())
505 getClassPropertyNames(exec, object->classInfo(), propertyNames, mode);
508 bool JSObject::toBoolean(ExecState*) const
513 double JSObject::toNumber(ExecState* exec) const
515 JSValue primitive = toPrimitive(exec, PreferNumber);
516 if (exec->hadException()) // should be picked up soon in Nodes.cpp
518 return primitive.toNumber(exec);
521 UString JSObject::toString(ExecState* exec) const
523 JSValue primitive = toPrimitive(exec, PreferString);
524 if (exec->hadException())
526 return primitive.toString(exec);
529 JSObject* JSObject::toThisObject(JSCell* cell, ExecState*)
531 return static_cast<JSObject*>(cell);
534 JSObject* JSObject::unwrappedObject()
537 return static_cast<JSGlobalThis*>(this)->unwrappedObject();
541 void JSObject::seal(JSGlobalData& globalData)
543 if (isSealed(globalData))
545 preventExtensions(globalData);
546 setStructure(globalData, Structure::sealTransition(globalData, structure()));
549 void JSObject::freeze(JSGlobalData& globalData)
551 if (isFrozen(globalData))
553 preventExtensions(globalData);
554 setStructure(globalData, Structure::freezeTransition(globalData, structure()));
557 void JSObject::preventExtensions(JSGlobalData& globalData)
560 setStructure(globalData, Structure::preventExtensionsTransition(globalData, structure()));
563 // This presently will flatten to an uncachable dictionary; this is suitable
564 // for use in delete, we may want to do something different elsewhere.
565 void JSObject::reifyStaticFunctionsForDelete(ExecState* exec)
567 ASSERT(!staticFunctionsReified());
568 JSGlobalData& globalData = exec->globalData();
570 // If this object's ClassInfo has no static properties, then nothing to reify!
571 // We can safely set the flag to avoid the expensive check again in the future.
572 if (!classInfo()->hasStaticProperties()) {
573 structure()->setStaticFunctionsReified();
577 if (!structure()->isUncacheableDictionary())
578 setStructure(globalData, Structure::toUncacheableDictionaryTransition(globalData, structure()));
580 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
581 const HashTable* hashTable = info->propHashTable(globalObject()->globalExec());
585 for (HashTable::ConstIterator iter = hashTable->begin(globalData); iter != hashTable->end(globalData); ++iter) {
586 if (iter->attributes() & Function)
587 setUpStaticFunctionSlot(globalObject()->globalExec(), *iter, this, Identifier(&globalData, iter->key()), slot);
591 structure()->setStaticFunctionsReified();
594 void JSObject::removeDirect(JSGlobalData& globalData, const Identifier& propertyName)
596 if (structure()->get(globalData, propertyName) == WTF::notFound)
600 if (structure()->isUncacheableDictionary()) {
601 offset = structure()->removePropertyWithoutTransition(globalData, propertyName);
602 if (offset != WTF::notFound)
603 putUndefinedAtDirectOffset(offset);
607 setStructure(globalData, Structure::removePropertyTransition(globalData, structure(), propertyName, offset));
608 if (offset != WTF::notFound)
609 putUndefinedAtDirectOffset(offset);
612 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, WriteBarrierBase<Unknown>* location)
614 if (JSObject* getterFunction = asGetterSetter(location->get())->getter()) {
615 if (!structure()->isDictionary())
616 slot.setCacheableGetterSlot(this, getterFunction, offsetForLocation(location));
618 slot.setGetterSlot(getterFunction);
623 Structure* JSObject::createInheritorID(JSGlobalData& globalData)
625 m_inheritorID.set(globalData, this, createEmptyObjectStructure(globalData, structure()->globalObject(), this));
626 ASSERT(m_inheritorID->isEmpty());
627 return m_inheritorID.get();
630 void JSObject::allocatePropertyStorage(JSGlobalData& globalData, size_t oldSize, size_t newSize)
632 ASSERT(newSize > oldSize);
634 // It's important that this function not rely on structure(), since
635 // we might be in the middle of a transition.
636 PropertyStorage newPropertyStorage = 0;
637 newPropertyStorage = new WriteBarrierBase<Unknown>[newSize];
639 PropertyStorage oldPropertyStorage = m_propertyStorage.get();
640 ASSERT(newPropertyStorage);
642 for (unsigned i = 0; i < oldSize; ++i)
643 newPropertyStorage[i] = oldPropertyStorage[i];
645 if (isUsingInlineStorage())
646 Heap::heap(this)->addFinalizer(this, &finalize);
648 delete [] oldPropertyStorage;
650 m_propertyStorage.set(globalData, this, newPropertyStorage);
653 bool JSObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
655 unsigned attributes = 0;
657 size_t offset = object->structure()->get(exec->globalData(), propertyName, attributes, cell);
658 if (offset == WTF::notFound)
660 descriptor.setDescriptor(object->getDirectOffset(offset), attributes);
664 bool JSObject::getPropertyDescriptor(ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor)
666 JSObject* object = this;
668 if (object->methodTable()->getOwnPropertyDescriptor(object, exec, propertyName, descriptor))
670 JSValue prototype = object->prototype();
671 if (!prototype.isObject())
673 object = asObject(prototype);
677 static bool putDescriptor(ExecState* exec, JSObject* target, const Identifier& propertyName, PropertyDescriptor& descriptor, unsigned attributes, const PropertyDescriptor& oldDescriptor)
679 if (descriptor.isGenericDescriptor() || descriptor.isDataDescriptor()) {
680 if (descriptor.isGenericDescriptor() && oldDescriptor.isAccessorDescriptor()) {
681 GetterSetter* accessor = GetterSetter::create(exec);
682 if (oldDescriptor.getter()) {
683 attributes |= Getter;
684 accessor->setGetter(exec->globalData(), asObject(oldDescriptor.getter()));
686 if (oldDescriptor.setter()) {
687 attributes |= Setter;
688 accessor->setSetter(exec->globalData(), asObject(oldDescriptor.setter()));
690 target->methodTable()->putWithAttributes(target, exec, propertyName, accessor, attributes);
693 JSValue newValue = jsUndefined();
694 if (descriptor.value())
695 newValue = descriptor.value();
696 else if (oldDescriptor.value())
697 newValue = oldDescriptor.value();
698 target->methodTable()->putWithAttributes(target, exec, propertyName, newValue, attributes & ~(Getter | Setter));
701 attributes &= ~ReadOnly;
702 if (descriptor.getter() && descriptor.getter().isObject())
703 target->methodTable()->defineGetter(target, exec, propertyName, asObject(descriptor.getter()), attributes);
704 if (exec->hadException())
706 if (descriptor.setter() && descriptor.setter().isObject())
707 target->methodTable()->defineSetter(target, exec, propertyName, asObject(descriptor.setter()), attributes);
708 return !exec->hadException();
711 bool JSObject::defineOwnProperty(JSObject* object, ExecState* exec, const Identifier& propertyName, PropertyDescriptor& descriptor, bool throwException)
713 // If we have a new property we can just put it on normally
714 PropertyDescriptor current;
715 if (!object->methodTable()->getOwnPropertyDescriptor(object, exec, propertyName, current)) {
716 // unless extensions are prevented!
717 if (!object->isExtensible()) {
719 throwError(exec, createTypeError(exec, "Attempting to define property on object that is not extensible."));
722 PropertyDescriptor oldDescriptor;
723 oldDescriptor.setValue(jsUndefined());
724 return putDescriptor(exec, object, propertyName, descriptor, descriptor.attributes(), oldDescriptor);
727 if (descriptor.isEmpty())
730 if (current.equalTo(exec, descriptor))
733 // Filter out invalid changes
734 if (!current.configurable()) {
735 if (descriptor.configurable()) {
737 throwError(exec, createTypeError(exec, "Attempting to configurable attribute of unconfigurable property."));
740 if (descriptor.enumerablePresent() && descriptor.enumerable() != current.enumerable()) {
742 throwError(exec, createTypeError(exec, "Attempting to change enumerable attribute of unconfigurable property."));
747 // A generic descriptor is simply changing the attributes of an existing property
748 if (descriptor.isGenericDescriptor()) {
749 if (!current.attributesEqual(descriptor)) {
750 object->methodTable()->deleteProperty(object, exec, propertyName);
751 putDescriptor(exec, object, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
756 // Changing between a normal property or an accessor property
757 if (descriptor.isDataDescriptor() != current.isDataDescriptor()) {
758 if (!current.configurable()) {
760 throwError(exec, createTypeError(exec, "Attempting to change access mechanism for an unconfigurable property."));
763 object->methodTable()->deleteProperty(object, exec, propertyName);
764 return putDescriptor(exec, object, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
767 // Changing the value and attributes of an existing property
768 if (descriptor.isDataDescriptor()) {
769 if (!current.configurable()) {
770 if (!current.writable() && descriptor.writable()) {
772 throwError(exec, createTypeError(exec, "Attempting to change writable attribute of unconfigurable property."));
775 if (!current.writable()) {
776 if (descriptor.value() || !JSValue::strictEqual(exec, current.value(), descriptor.value())) {
778 throwError(exec, createTypeError(exec, "Attempting to change value of a readonly property."));
782 } else if (current.attributesEqual(descriptor)) {
783 if (!descriptor.value())
785 PutPropertySlot slot;
786 object->methodTable()->put(object, exec, propertyName, descriptor.value(), slot);
787 if (exec->hadException())
791 object->methodTable()->deleteProperty(object, exec, propertyName);
792 return putDescriptor(exec, object, propertyName, descriptor, current.attributesWithOverride(descriptor), current);
795 // Changing the accessor functions of an existing accessor property
796 ASSERT(descriptor.isAccessorDescriptor());
797 if (!current.configurable()) {
798 if (descriptor.setterPresent() && !(current.setterPresent() && JSValue::strictEqual(exec, current.setter(), descriptor.setter()))) {
800 throwError(exec, createTypeError(exec, "Attempting to change the setter of an unconfigurable property."));
803 if (descriptor.getterPresent() && !(current.getterPresent() && JSValue::strictEqual(exec, current.getter(), descriptor.getter()))) {
805 throwError(exec, createTypeError(exec, "Attempting to change the getter of an unconfigurable property."));
809 JSValue accessor = object->getDirect(exec->globalData(), propertyName);
812 GetterSetter* getterSetter = asGetterSetter(accessor);
813 if (current.attributesEqual(descriptor)) {
814 if (descriptor.setter())
815 getterSetter->setSetter(exec->globalData(), asObject(descriptor.setter()));
816 if (descriptor.getter())
817 getterSetter->setGetter(exec->globalData(), asObject(descriptor.getter()));
820 object->methodTable()->deleteProperty(object, exec, propertyName);
821 unsigned attrs = current.attributesWithOverride(descriptor);
822 if (descriptor.setter())
824 if (descriptor.getter())
826 object->putDirect(exec->globalData(), propertyName, getterSetter, attrs);
830 JSObject* throwTypeError(ExecState* exec, const UString& message)
832 return throwError(exec, createTypeError(exec, message));