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 "NativeErrorConstructor.h"
33 #include "ObjectPrototype.h"
34 #include "PropertyDescriptor.h"
35 #include "PropertyNameArray.h"
38 #include "Operations.h"
40 #include <wtf/Assertions.h>
44 ASSERT_CLASS_FITS_IN_CELL(JSObject
);
45 ASSERT_CLASS_FITS_IN_CELL(JSNonFinalObject
);
46 ASSERT_CLASS_FITS_IN_CELL(JSFinalObject
);
48 const char* StrictModeReadonlyPropertyWriteError
= "Attempted to assign to readonly property.";
50 const ClassInfo
JSObject::s_info
= { "Object", 0, 0, 0 };
52 static inline void getClassPropertyNames(ExecState
* exec
, const ClassInfo
* classInfo
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
54 // Add properties from the static hashtables of properties
55 for (; classInfo
; classInfo
= classInfo
->parentClass
) {
56 const HashTable
* table
= classInfo
->propHashTable(exec
);
59 table
->initializeIfNeeded(exec
);
62 int hashSizeMask
= table
->compactSize
- 1;
63 const HashEntry
* entry
= table
->table
;
64 for (int i
= 0; i
<= hashSizeMask
; ++i
, ++entry
) {
65 if (entry
->key() && (!(entry
->attributes() & DontEnum
) || (mode
== IncludeDontEnumProperties
)))
66 propertyNames
.add(entry
->key());
71 void JSObject::visitChildren(SlotVisitor
& visitor
)
73 ASSERT_GC_OBJECT_INHERITS(this, &s_info
);
75 bool wasCheckingForDefaultMarkViolation
= visitor
.m_isCheckingForDefaultMarkViolation
;
76 visitor
.m_isCheckingForDefaultMarkViolation
= false;
79 visitChildrenDirect(visitor
);
82 visitor
.m_isCheckingForDefaultMarkViolation
= wasCheckingForDefaultMarkViolation
;
86 UString
JSObject::className() const
88 const ClassInfo
* info
= classInfo();
90 return info
->className
;
93 bool JSObject::getOwnPropertySlot(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
95 return getOwnPropertySlot(exec
, Identifier::from(exec
, propertyName
), slot
);
98 static void throwSetterError(ExecState
* exec
)
100 throwError(exec
, createTypeError(exec
, "setting a property that has only a getter"));
104 void JSObject::put(ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, PutPropertySlot
& slot
)
107 ASSERT(!Heap::heap(value
) || Heap::heap(value
) == Heap::heap(this));
109 if (propertyName
== exec
->propertyNames().underscoreProto
) {
110 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
111 if (!value
.isObject() && !value
.isNull())
113 if (!setPrototypeWithCycleCheck(exec
->globalData(), value
))
114 throwError(exec
, createError(exec
, "cyclic __proto__ value"));
118 // Check if there are any setters or getters in the prototype chain
120 for (JSObject
* obj
= this; !obj
->structure()->hasGetterSetterProperties(); obj
= asObject(prototype
)) {
121 prototype
= obj
->prototype();
122 if (prototype
.isNull()) {
123 if (!putDirectInternal(exec
->globalData(), propertyName
, value
, 0, true, slot
) && slot
.isStrictMode())
124 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
130 JSCell
* specificValue
;
131 if ((m_structure
->get(exec
->globalData(), propertyName
, attributes
, specificValue
) != WTF::notFound
) && attributes
& ReadOnly
) {
132 if (slot
.isStrictMode())
133 throwError(exec
, createTypeError(exec
, StrictModeReadonlyPropertyWriteError
));
137 for (JSObject
* obj
= this; ; obj
= asObject(prototype
)) {
138 if (JSValue gs
= obj
->getDirect(exec
->globalData(), propertyName
)) {
139 if (gs
.isGetterSetter()) {
140 JSObject
* setterFunc
= asGetterSetter(gs
)->setter();
142 throwSetterError(exec
);
147 CallType callType
= setterFunc
->getCallData(callData
);
148 MarkedArgumentBuffer args
;
150 call(exec
, setterFunc
, callType
, callData
, this, args
);
154 // If there's an existing property on the object or one of its
155 // prototypes it should be replaced, so break here.
159 prototype
= obj
->prototype();
160 if (prototype
.isNull())
164 if (!putDirectInternal(exec
->globalData(), propertyName
, value
, 0, true, slot
) && slot
.isStrictMode())
165 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
169 void JSObject::put(ExecState
* exec
, unsigned propertyName
, JSValue value
)
171 PutPropertySlot slot
;
172 put(exec
, Identifier::from(exec
, propertyName
), value
, slot
);
175 void JSObject::putWithAttributes(JSGlobalData
* globalData
, const Identifier
& propertyName
, JSValue value
, unsigned attributes
, bool checkReadOnly
, PutPropertySlot
& slot
)
177 putDirectInternal(*globalData
, propertyName
, value
, attributes
, checkReadOnly
, slot
);
180 void JSObject::putWithAttributes(JSGlobalData
* globalData
, const Identifier
& propertyName
, JSValue value
, unsigned attributes
)
182 putDirectInternal(*globalData
, propertyName
, value
, attributes
);
185 void JSObject::putWithAttributes(JSGlobalData
* globalData
, unsigned propertyName
, JSValue value
, unsigned attributes
)
187 putWithAttributes(globalData
, Identifier::from(globalData
, propertyName
), value
, attributes
);
190 void JSObject::putWithAttributes(ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, unsigned attributes
, bool checkReadOnly
, PutPropertySlot
& slot
)
192 putDirectInternal(exec
->globalData(), propertyName
, value
, attributes
, checkReadOnly
, slot
);
195 void JSObject::putWithAttributes(ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, unsigned attributes
)
197 putDirectInternal(exec
->globalData(), propertyName
, value
, attributes
);
200 void JSObject::putWithAttributes(ExecState
* exec
, unsigned propertyName
, JSValue value
, unsigned attributes
)
202 putWithAttributes(exec
, Identifier::from(exec
, propertyName
), value
, attributes
);
205 bool JSObject::hasProperty(ExecState
* exec
, const Identifier
& propertyName
) const
208 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
211 bool JSObject::hasProperty(ExecState
* exec
, unsigned propertyName
) const
214 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
218 bool JSObject::deleteProperty(ExecState
* exec
, const Identifier
& propertyName
)
221 JSCell
* specificValue
;
222 if (m_structure
->get(exec
->globalData(), propertyName
, attributes
, specificValue
) != WTF::notFound
) {
223 if ((attributes
& DontDelete
))
225 removeDirect(exec
->globalData(), propertyName
);
229 // Look in the static hashtable of properties
230 const HashEntry
* entry
= findPropertyHashEntry(exec
, propertyName
);
231 if (entry
&& entry
->attributes() & DontDelete
)
232 return false; // this builtin property can't be deleted
234 // FIXME: Should the code here actually do some deletion?
238 bool JSObject::hasOwnProperty(ExecState
* exec
, const Identifier
& propertyName
) const
241 return const_cast<JSObject
*>(this)->getOwnPropertySlot(exec
, propertyName
, slot
);
244 bool JSObject::deleteProperty(ExecState
* exec
, unsigned propertyName
)
246 return deleteProperty(exec
, Identifier::from(exec
, propertyName
));
249 static ALWAYS_INLINE JSValue
callDefaultValueFunction(ExecState
* exec
, const JSObject
* object
, const Identifier
& propertyName
)
251 JSValue function
= object
->get(exec
, propertyName
);
253 CallType callType
= getCallData(function
, callData
);
254 if (callType
== CallTypeNone
)
255 return exec
->exception();
257 // Prevent "toString" and "valueOf" from observing execution if an exception
259 if (exec
->hadException())
260 return exec
->exception();
262 JSValue result
= call(exec
, function
, callType
, callData
, const_cast<JSObject
*>(object
), exec
->emptyList());
263 ASSERT(!result
.isGetterSetter());
264 if (exec
->hadException())
265 return exec
->exception();
266 if (result
.isObject())
271 bool JSObject::getPrimitiveNumber(ExecState
* exec
, double& number
, JSValue
& result
)
273 result
= defaultValue(exec
, PreferNumber
);
274 number
= result
.toNumber(exec
);
275 return !result
.isString();
279 JSValue
JSObject::defaultValue(ExecState
* exec
, PreferredPrimitiveType hint
) const
281 // Must call toString first for Date objects.
282 if ((hint
== PreferString
) || (hint
!= PreferNumber
&& prototype() == exec
->lexicalGlobalObject()->datePrototype())) {
283 JSValue value
= callDefaultValueFunction(exec
, this, exec
->propertyNames().toString
);
286 value
= callDefaultValueFunction(exec
, this, exec
->propertyNames().valueOf
);
290 JSValue value
= callDefaultValueFunction(exec
, this, exec
->propertyNames().valueOf
);
293 value
= callDefaultValueFunction(exec
, this, exec
->propertyNames().toString
);
298 ASSERT(!exec
->hadException());
300 return throwError(exec
, createTypeError(exec
, "No default value"));
303 const HashEntry
* JSObject::findPropertyHashEntry(ExecState
* exec
, const Identifier
& propertyName
) const
305 for (const ClassInfo
* info
= classInfo(); info
; info
= info
->parentClass
) {
306 if (const HashTable
* propHashTable
= info
->propHashTable(exec
)) {
307 if (const HashEntry
* entry
= propHashTable
->entry(exec
, propertyName
))
314 void JSObject::defineGetter(ExecState
* exec
, const Identifier
& propertyName
, JSObject
* getterFunction
, unsigned attributes
)
316 if (propertyName
== exec
->propertyNames().underscoreProto
) {
317 // Defining a getter for __proto__ is silently ignored.
321 JSValue object
= getDirect(exec
->globalData(), propertyName
);
322 if (object
&& object
.isGetterSetter()) {
323 ASSERT(m_structure
->hasGetterSetterProperties());
324 asGetterSetter(object
)->setGetter(exec
->globalData(), getterFunction
);
328 JSGlobalData
& globalData
= exec
->globalData();
329 PutPropertySlot slot
;
330 GetterSetter
* getterSetter
= new (exec
) GetterSetter(exec
);
331 putDirectInternal(globalData
, propertyName
, getterSetter
, attributes
| Getter
, true, slot
);
333 // putDirect will change our Structure if we add a new property. For
334 // getters and setters, though, we also need to change our Structure
335 // if we override an existing non-getter or non-setter.
336 if (slot
.type() != PutPropertySlot::NewProperty
) {
337 if (!m_structure
->isDictionary())
338 setStructure(exec
->globalData(), Structure::getterSetterTransition(globalData
, m_structure
.get()));
341 m_structure
->setHasGetterSetterProperties(true);
342 getterSetter
->setGetter(globalData
, getterFunction
);
345 void JSObject::defineSetter(ExecState
* exec
, const Identifier
& propertyName
, JSObject
* setterFunction
, unsigned attributes
)
347 if (propertyName
== exec
->propertyNames().underscoreProto
) {
348 // Defining a setter for __proto__ is silently ignored.
352 JSValue object
= getDirect(exec
->globalData(), propertyName
);
353 if (object
&& object
.isGetterSetter()) {
354 ASSERT(m_structure
->hasGetterSetterProperties());
355 asGetterSetter(object
)->setSetter(exec
->globalData(), setterFunction
);
359 PutPropertySlot slot
;
360 GetterSetter
* getterSetter
= new (exec
) GetterSetter(exec
);
361 putDirectInternal(exec
->globalData(), propertyName
, getterSetter
, attributes
| Setter
, true, slot
);
363 // putDirect will change our Structure if we add a new property. For
364 // getters and setters, though, we also need to change our Structure
365 // if we override an existing non-getter or non-setter.
366 if (slot
.type() != PutPropertySlot::NewProperty
) {
367 if (!m_structure
->isDictionary())
368 setStructure(exec
->globalData(), Structure::getterSetterTransition(exec
->globalData(), m_structure
.get()));
371 m_structure
->setHasGetterSetterProperties(true);
372 getterSetter
->setSetter(exec
->globalData(), setterFunction
);
375 JSValue
JSObject::lookupGetter(ExecState
* exec
, const Identifier
& propertyName
)
377 JSObject
* object
= this;
379 if (JSValue value
= object
->getDirect(exec
->globalData(), propertyName
)) {
380 if (!value
.isGetterSetter())
381 return jsUndefined();
382 JSObject
* functionObject
= asGetterSetter(value
)->getter();
384 return jsUndefined();
385 return functionObject
;
388 if (!object
->prototype() || !object
->prototype().isObject())
389 return jsUndefined();
390 object
= asObject(object
->prototype());
394 JSValue
JSObject::lookupSetter(ExecState
* exec
, const Identifier
& propertyName
)
396 JSObject
* object
= this;
398 if (JSValue value
= object
->getDirect(exec
->globalData(), propertyName
)) {
399 if (!value
.isGetterSetter())
400 return jsUndefined();
401 JSObject
* functionObject
= asGetterSetter(value
)->setter();
403 return jsUndefined();
404 return functionObject
;
407 if (!object
->prototype() || !object
->prototype().isObject())
408 return jsUndefined();
409 object
= asObject(object
->prototype());
413 bool JSObject::hasInstance(ExecState
* exec
, JSValue value
, JSValue proto
)
415 if (!value
.isObject())
418 if (!proto
.isObject()) {
419 throwError(exec
, createTypeError(exec
, "instanceof called on an object with an invalid prototype property."));
423 JSObject
* object
= asObject(value
);
424 while ((object
= object
->prototype().getObject())) {
431 bool JSObject::propertyIsEnumerable(ExecState
* exec
, const Identifier
& propertyName
) const
433 PropertyDescriptor descriptor
;
434 if (!const_cast<JSObject
*>(this)->getOwnPropertyDescriptor(exec
, propertyName
, descriptor
))
436 return descriptor
.enumerable();
439 bool JSObject::getPropertySpecificValue(ExecState
* exec
, const Identifier
& propertyName
, JSCell
*& specificValue
) const
442 if (m_structure
->get(exec
->globalData(), propertyName
, attributes
, specificValue
) != WTF::notFound
)
445 // This could be a function within the static table? - should probably
446 // also look in the hash? This currently should not be a problem, since
447 // we've currently always call 'get' first, which should have populated
448 // the normal storage.
452 void JSObject::getPropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
454 getOwnPropertyNames(exec
, propertyNames
, mode
);
456 if (prototype().isNull())
459 JSObject
* prototype
= asObject(this->prototype());
461 if (prototype
->structure()->typeInfo().overridesGetPropertyNames()) {
462 prototype
->getPropertyNames(exec
, propertyNames
, mode
);
465 prototype
->getOwnPropertyNames(exec
, propertyNames
, mode
);
466 JSValue nextProto
= prototype
->prototype();
467 if (nextProto
.isNull())
469 prototype
= asObject(nextProto
);
473 void JSObject::getOwnPropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
475 m_structure
->getPropertyNames(exec
->globalData(), propertyNames
, mode
);
476 getClassPropertyNames(exec
, classInfo(), propertyNames
, mode
);
479 bool JSObject::toBoolean(ExecState
*) const
484 double JSObject::toNumber(ExecState
* exec
) const
486 JSValue primitive
= toPrimitive(exec
, PreferNumber
);
487 if (exec
->hadException()) // should be picked up soon in Nodes.cpp
489 return primitive
.toNumber(exec
);
492 UString
JSObject::toString(ExecState
* exec
) const
494 JSValue primitive
= toPrimitive(exec
, PreferString
);
495 if (exec
->hadException())
497 return primitive
.toString(exec
);
500 JSObject
* JSObject::toObject(ExecState
*, JSGlobalObject
*) const
502 return const_cast<JSObject
*>(this);
505 JSObject
* JSObject::toThisObject(ExecState
*) const
507 return const_cast<JSObject
*>(this);
510 JSValue
JSObject::toStrictThisObject(ExecState
*) const
512 return const_cast<JSObject
*>(this);
515 JSObject
* JSObject::unwrappedObject()
520 void JSObject::seal(JSGlobalData
& globalData
)
522 if (isSealed(globalData
))
524 preventExtensions(globalData
);
525 setStructure(globalData
, Structure::sealTransition(globalData
, m_structure
.get()));
528 void JSObject::freeze(JSGlobalData
& globalData
)
530 if (isFrozen(globalData
))
532 preventExtensions(globalData
);
533 setStructure(globalData
, Structure::freezeTransition(globalData
, m_structure
.get()));
536 void JSObject::preventExtensions(JSGlobalData
& globalData
)
539 setStructure(globalData
, Structure::preventExtensionsTransition(globalData
, m_structure
.get()));
542 void JSObject::removeDirect(JSGlobalData
& globalData
, const Identifier
& propertyName
)
544 if (m_structure
->get(globalData
, propertyName
) == WTF::notFound
)
548 if (m_structure
->isUncacheableDictionary()) {
549 offset
= m_structure
->removePropertyWithoutTransition(globalData
, propertyName
);
550 if (offset
!= WTF::notFound
)
551 putUndefinedAtDirectOffset(offset
);
555 setStructure(globalData
, Structure::removePropertyTransition(globalData
, m_structure
.get(), propertyName
, offset
));
556 if (offset
!= WTF::notFound
)
557 putUndefinedAtDirectOffset(offset
);
560 void JSObject::putDirectFunction(ExecState
* exec
, InternalFunction
* function
, unsigned attr
)
562 putDirectFunction(exec
->globalData(), Identifier(exec
, function
->name(exec
)), function
, attr
);
565 void JSObject::putDirectFunction(ExecState
* exec
, JSFunction
* function
, unsigned attr
)
567 putDirectFunction(exec
->globalData(), Identifier(exec
, function
->name(exec
)), function
, attr
);
570 void JSObject::putDirectFunctionWithoutTransition(ExecState
* exec
, InternalFunction
* function
, unsigned attr
)
572 putDirectFunctionWithoutTransition(exec
->globalData(), Identifier(exec
, function
->name(exec
)), function
, attr
);
575 void JSObject::putDirectFunctionWithoutTransition(ExecState
* exec
, JSFunction
* function
, unsigned attr
)
577 putDirectFunctionWithoutTransition(exec
->globalData(), Identifier(exec
, function
->name(exec
)), function
, attr
);
580 NEVER_INLINE
void JSObject::fillGetterPropertySlot(PropertySlot
& slot
, WriteBarrierBase
<Unknown
>* location
)
582 if (JSObject
* getterFunction
= asGetterSetter(location
->get())->getter()) {
583 if (!structure()->isDictionary())
584 slot
.setCacheableGetterSlot(this, getterFunction
, offsetForLocation(location
));
586 slot
.setGetterSlot(getterFunction
);
591 Structure
* JSObject::createInheritorID(JSGlobalData
& globalData
)
593 m_inheritorID
.set(globalData
, this, createEmptyObjectStructure(globalData
, this));
594 ASSERT(m_inheritorID
->isEmpty());
595 return m_inheritorID
.get();
598 void JSObject::allocatePropertyStorage(size_t oldSize
, size_t newSize
)
600 ASSERT(newSize
> oldSize
);
602 // It's important that this function not rely on m_structure, since
603 // we might be in the middle of a transition.
604 bool wasInline
= (oldSize
< JSObject::baseExternalStorageCapacity
);
606 PropertyStorage oldPropertyStorage
= m_propertyStorage
;
607 PropertyStorage newPropertyStorage
= new WriteBarrierBase
<Unknown
>[newSize
];
609 for (unsigned i
= 0; i
< oldSize
; ++i
)
610 newPropertyStorage
[i
] = oldPropertyStorage
[i
];
613 delete [] oldPropertyStorage
;
615 m_propertyStorage
= newPropertyStorage
;
618 bool JSObject::getOwnPropertyDescriptor(ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
)
620 unsigned attributes
= 0;
622 size_t offset
= m_structure
->get(exec
->globalData(), propertyName
, attributes
, cell
);
623 if (offset
== WTF::notFound
)
625 descriptor
.setDescriptor(getDirectOffset(offset
), attributes
);
629 bool JSObject::getPropertyDescriptor(ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
)
631 JSObject
* object
= this;
633 if (object
->getOwnPropertyDescriptor(exec
, propertyName
, descriptor
))
635 JSValue prototype
= object
->prototype();
636 if (!prototype
.isObject())
638 object
= asObject(prototype
);
642 static bool putDescriptor(ExecState
* exec
, JSObject
* target
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
, unsigned attributes
, const PropertyDescriptor
& oldDescriptor
)
644 if (descriptor
.isGenericDescriptor() || descriptor
.isDataDescriptor()) {
645 if (descriptor
.isGenericDescriptor() && oldDescriptor
.isAccessorDescriptor()) {
646 GetterSetter
* accessor
= new (exec
) GetterSetter(exec
);
647 if (oldDescriptor
.getter()) {
648 attributes
|= Getter
;
649 accessor
->setGetter(exec
->globalData(), asObject(oldDescriptor
.getter()));
651 if (oldDescriptor
.setter()) {
652 attributes
|= Setter
;
653 accessor
->setSetter(exec
->globalData(), asObject(oldDescriptor
.setter()));
655 target
->putWithAttributes(exec
, propertyName
, accessor
, attributes
);
658 JSValue newValue
= jsUndefined();
659 if (descriptor
.value())
660 newValue
= descriptor
.value();
661 else if (oldDescriptor
.value())
662 newValue
= oldDescriptor
.value();
663 target
->putWithAttributes(exec
, propertyName
, newValue
, attributes
& ~(Getter
| Setter
));
666 attributes
&= ~ReadOnly
;
667 if (descriptor
.getter() && descriptor
.getter().isObject())
668 target
->defineGetter(exec
, propertyName
, asObject(descriptor
.getter()), attributes
);
669 if (exec
->hadException())
671 if (descriptor
.setter() && descriptor
.setter().isObject())
672 target
->defineSetter(exec
, propertyName
, asObject(descriptor
.setter()), attributes
);
673 return !exec
->hadException();
676 bool JSObject::defineOwnProperty(ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
, bool throwException
)
678 // If we have a new property we can just put it on normally
679 PropertyDescriptor current
;
680 if (!getOwnPropertyDescriptor(exec
, propertyName
, current
)) {
681 // unless extensions are prevented!
682 if (!isExtensible()) {
684 throwError(exec
, createTypeError(exec
, "Attempting to define property on object that is not extensible."));
687 PropertyDescriptor oldDescriptor
;
688 oldDescriptor
.setValue(jsUndefined());
689 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributes(), oldDescriptor
);
692 if (descriptor
.isEmpty())
695 if (current
.equalTo(exec
, descriptor
))
698 // Filter out invalid changes
699 if (!current
.configurable()) {
700 if (descriptor
.configurable()) {
702 throwError(exec
, createTypeError(exec
, "Attempting to configurable attribute of unconfigurable property."));
705 if (descriptor
.enumerablePresent() && descriptor
.enumerable() != current
.enumerable()) {
707 throwError(exec
, createTypeError(exec
, "Attempting to change enumerable attribute of unconfigurable property."));
712 // A generic descriptor is simply changing the attributes of an existing property
713 if (descriptor
.isGenericDescriptor()) {
714 if (!current
.attributesEqual(descriptor
)) {
715 deleteProperty(exec
, propertyName
);
716 putDescriptor(exec
, this, propertyName
, descriptor
, current
.attributesWithOverride(descriptor
), current
);
721 // Changing between a normal property or an accessor property
722 if (descriptor
.isDataDescriptor() != current
.isDataDescriptor()) {
723 if (!current
.configurable()) {
725 throwError(exec
, createTypeError(exec
, "Attempting to change access mechanism for an unconfigurable property."));
728 deleteProperty(exec
, propertyName
);
729 return putDescriptor(exec
, this, propertyName
, descriptor
, current
.attributesWithOverride(descriptor
), current
);
732 // Changing the value and attributes of an existing property
733 if (descriptor
.isDataDescriptor()) {
734 if (!current
.configurable()) {
735 if (!current
.writable() && descriptor
.writable()) {
737 throwError(exec
, createTypeError(exec
, "Attempting to change writable attribute of unconfigurable property."));
740 if (!current
.writable()) {
741 if (descriptor
.value() || !JSValue::strictEqual(exec
, current
.value(), descriptor
.value())) {
743 throwError(exec
, createTypeError(exec
, "Attempting to change value of a readonly property."));
747 } else if (current
.attributesEqual(descriptor
)) {
748 if (!descriptor
.value())
750 PutPropertySlot slot
;
751 put(exec
, propertyName
, descriptor
.value(), slot
);
752 if (exec
->hadException())
756 deleteProperty(exec
, propertyName
);
757 return putDescriptor(exec
, this, propertyName
, descriptor
, current
.attributesWithOverride(descriptor
), current
);
760 // Changing the accessor functions of an existing accessor property
761 ASSERT(descriptor
.isAccessorDescriptor());
762 if (!current
.configurable()) {
763 if (descriptor
.setterPresent() && !(current
.setterPresent() && JSValue::strictEqual(exec
, current
.setter(), descriptor
.setter()))) {
765 throwError(exec
, createTypeError(exec
, "Attempting to change the setter of an unconfigurable property."));
768 if (descriptor
.getterPresent() && !(current
.getterPresent() && JSValue::strictEqual(exec
, current
.getter(), descriptor
.getter()))) {
770 throwError(exec
, createTypeError(exec
, "Attempting to change the getter of an unconfigurable property."));
774 JSValue accessor
= getDirect(exec
->globalData(), propertyName
);
777 GetterSetter
* getterSetter
= asGetterSetter(accessor
);
778 if (current
.attributesEqual(descriptor
)) {
779 if (descriptor
.setter())
780 getterSetter
->setSetter(exec
->globalData(), asObject(descriptor
.setter()));
781 if (descriptor
.getter())
782 getterSetter
->setGetter(exec
->globalData(), asObject(descriptor
.getter()));
785 deleteProperty(exec
, propertyName
);
786 unsigned attrs
= current
.attributesWithOverride(descriptor
);
787 if (descriptor
.setter())
789 if (descriptor
.getter())
791 putDirect(exec
->globalData(), propertyName
, getterSetter
, attrs
);
795 JSObject
* throwTypeError(ExecState
* exec
, const UString
& message
)
797 return throwError(exec
, createTypeError(exec
, message
));