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 "CopiedSpaceInlineMethods.h"
28 #include "DatePrototype.h"
29 #include "ErrorConstructor.h"
30 #include "GetterSetter.h"
31 #include "JSFunction.h"
32 #include "JSGlobalObject.h"
33 #include "JSGlobalThis.h"
35 #include "NativeErrorConstructor.h"
37 #include "ObjectPrototype.h"
38 #include "Operations.h"
39 #include "PropertyDescriptor.h"
40 #include "PropertyNameArray.h"
42 #include <wtf/Assertions.h>
46 ASSERT_CLASS_FITS_IN_CELL(JSObject
);
47 ASSERT_CLASS_FITS_IN_CELL(JSNonFinalObject
);
48 ASSERT_CLASS_FITS_IN_CELL(JSFinalObject
);
50 ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSObject
);
51 ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSFinalObject
);
53 const char* StrictModeReadonlyPropertyWriteError
= "Attempted to assign to readonly property.";
55 const ClassInfo
JSObject::s_info
= { "Object", 0, 0, 0, CREATE_METHOD_TABLE(JSObject
) };
57 const ClassInfo
JSFinalObject::s_info
= { "Object", &Base::s_info
, 0, 0, CREATE_METHOD_TABLE(JSFinalObject
) };
59 static inline void getClassPropertyNames(ExecState
* exec
, const ClassInfo
* classInfo
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
61 // Add properties from the static hashtables of properties
62 for (; classInfo
; classInfo
= classInfo
->parentClass
) {
63 const HashTable
* table
= classInfo
->propHashTable(exec
);
66 table
->initializeIfNeeded(exec
);
69 int hashSizeMask
= table
->compactSize
- 1;
70 const HashEntry
* entry
= table
->table
;
71 for (int i
= 0; i
<= hashSizeMask
; ++i
, ++entry
) {
72 if (entry
->key() && (!(entry
->attributes() & DontEnum
) || (mode
== IncludeDontEnumProperties
)))
73 propertyNames
.add(entry
->key());
78 void JSObject::visitChildren(JSCell
* cell
, SlotVisitor
& visitor
)
80 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
81 ASSERT_GC_OBJECT_INHERITS(thisObject
, &s_info
);
83 bool wasCheckingForDefaultMarkViolation
= visitor
.m_isCheckingForDefaultMarkViolation
;
84 visitor
.m_isCheckingForDefaultMarkViolation
= false;
87 JSCell::visitChildren(thisObject
, visitor
);
89 PropertyStorage storage
= thisObject
->propertyStorage();
90 size_t storageSize
= thisObject
->structure()->propertyStorageSize();
91 if (thisObject
->isUsingInlineStorage())
92 visitor
.appendValues(storage
, storageSize
);
94 // We have this extra temp here to slake GCC's thirst for the blood of those who dereference type-punned pointers.
96 visitor
.copyAndAppend(&temp
, thisObject
->structure()->propertyStorageCapacity() * sizeof(WriteBarrierBase
<Unknown
>), storage
->slot(), storageSize
);
97 storage
= static_cast<PropertyStorage
>(temp
);
98 thisObject
->m_propertyStorage
.set(storage
, StorageBarrier::Unchecked
);
101 if (thisObject
->m_inheritorID
)
102 visitor
.append(&thisObject
->m_inheritorID
);
105 visitor
.m_isCheckingForDefaultMarkViolation
= wasCheckingForDefaultMarkViolation
;
109 UString
JSObject::className(const JSObject
* object
)
111 const ClassInfo
* info
= object
->classInfo();
113 return info
->className
;
116 bool JSObject::getOwnPropertySlotByIndex(JSCell
* cell
, ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
118 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
119 return thisObject
->methodTable()->getOwnPropertySlot(thisObject
, exec
, Identifier::from(exec
, propertyName
), slot
);
123 void JSObject::put(JSCell
* cell
, ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, PutPropertySlot
& slot
)
125 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
127 ASSERT(!Heap::heap(value
) || Heap::heap(value
) == Heap::heap(thisObject
));
128 JSGlobalData
& globalData
= exec
->globalData();
130 // Check if there are any setters or getters in the prototype chain
132 if (propertyName
!= exec
->propertyNames().underscoreProto
) {
133 for (JSObject
* obj
= thisObject
; !obj
->structure()->hasReadOnlyOrGetterSetterPropertiesExcludingProto(); obj
= asObject(prototype
)) {
134 prototype
= obj
->prototype();
135 if (prototype
.isNull()) {
136 if (!thisObject
->putDirectInternal
<PutModePut
>(globalData
, propertyName
, value
, 0, slot
, getJSFunction(value
)) && slot
.isStrictMode())
137 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
143 for (JSObject
* obj
= thisObject
; ; obj
= asObject(prototype
)) {
145 JSCell
* specificValue
;
146 size_t offset
= obj
->structure()->get(globalData
, propertyName
, attributes
, specificValue
);
147 if (offset
!= WTF::notFound
) {
148 if (attributes
& ReadOnly
) {
149 if (slot
.isStrictMode())
150 throwError(exec
, createTypeError(exec
, StrictModeReadonlyPropertyWriteError
));
154 JSValue gs
= obj
->getDirectOffset(offset
);
155 if (gs
.isGetterSetter()) {
156 JSObject
* setterFunc
= asGetterSetter(gs
)->setter();
158 if (slot
.isStrictMode())
159 throwError(exec
, createTypeError(exec
, "setting a property that has only a getter"));
164 CallType callType
= setterFunc
->methodTable()->getCallData(setterFunc
, callData
);
165 MarkedArgumentBuffer args
;
168 // If this is WebCore's global object then we need to substitute the shell.
169 call(exec
, setterFunc
, callType
, callData
, thisObject
->methodTable()->toThisObject(thisObject
, exec
), args
);
173 // If there's an existing property on the object or one of its
174 // prototypes it should be replaced, so break here.
178 prototype
= obj
->prototype();
179 if (prototype
.isNull())
183 if (!thisObject
->putDirectInternal
<PutModePut
>(globalData
, propertyName
, value
, 0, slot
, getJSFunction(value
)) && slot
.isStrictMode())
184 throwTypeError(exec
, StrictModeReadonlyPropertyWriteError
);
188 void JSObject::putByIndex(JSCell
* cell
, ExecState
* exec
, unsigned propertyName
, JSValue value
, bool shouldThrow
)
190 PutPropertySlot
slot(shouldThrow
);
191 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
192 thisObject
->methodTable()->put(thisObject
, exec
, Identifier::from(exec
, propertyName
), value
, slot
);
195 void JSObject::putDirectVirtual(JSObject
* object
, ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, unsigned attributes
)
197 ASSERT(!value
.isGetterSetter() && !(attributes
& Accessor
));
198 PutPropertySlot slot
;
199 object
->putDirectInternal
<PutModeDefineOwnProperty
>(exec
->globalData(), propertyName
, value
, attributes
, slot
, getJSFunction(value
));
202 bool JSObject::setPrototypeWithCycleCheck(JSGlobalData
& globalData
, JSValue prototype
)
204 JSValue checkFor
= this;
205 if (this->isGlobalObject())
206 checkFor
= jsCast
<JSGlobalObject
*>(this)->globalExec()->thisValue();
208 JSValue nextPrototype
= prototype
;
209 while (nextPrototype
&& nextPrototype
.isObject()) {
210 if (nextPrototype
== checkFor
)
212 nextPrototype
= asObject(nextPrototype
)->prototype();
214 setPrototype(globalData
, prototype
);
218 bool JSObject::allowsAccessFrom(ExecState
* exec
)
220 JSGlobalObject
* globalObject
= isGlobalThis() ? jsCast
<JSGlobalThis
*>(this)->unwrappedObject() : this->globalObject();
221 return globalObject
->globalObjectMethodTable()->allowsAccessFrom(globalObject
, exec
);
224 void JSObject::putDirectAccessor(JSGlobalData
& globalData
, const Identifier
& propertyName
, JSValue value
, unsigned attributes
)
226 ASSERT(value
.isGetterSetter() && (attributes
& Accessor
));
228 PutPropertySlot slot
;
229 putDirectInternal
<PutModeDefineOwnProperty
>(globalData
, propertyName
, value
, attributes
, slot
, getJSFunction(value
));
231 // putDirect will change our Structure if we add a new property. For
232 // getters and setters, though, we also need to change our Structure
233 // if we override an existing non-getter or non-setter.
234 if (slot
.type() != PutPropertySlot::NewProperty
)
235 setStructure(globalData
, Structure::attributeChangeTransition(globalData
, structure(), propertyName
, attributes
));
237 if (attributes
& ReadOnly
)
238 structure()->setContainsReadOnlyProperties();
240 structure()->setHasGetterSetterProperties(propertyName
== globalData
.propertyNames
->underscoreProto
);
243 bool JSObject::hasProperty(ExecState
* exec
, const Identifier
& propertyName
) const
246 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
249 bool JSObject::hasProperty(ExecState
* exec
, unsigned propertyName
) const
252 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
256 bool JSObject::deleteProperty(JSCell
* cell
, ExecState
* exec
, const Identifier
& propertyName
)
258 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
260 if (!thisObject
->staticFunctionsReified())
261 thisObject
->reifyStaticFunctionsForDelete(exec
);
264 JSCell
* specificValue
;
265 if (thisObject
->structure()->get(exec
->globalData(), propertyName
, attributes
, specificValue
) != WTF::notFound
) {
266 if (attributes
& DontDelete
&& !exec
->globalData().isInDefineOwnProperty())
268 thisObject
->removeDirect(exec
->globalData(), propertyName
);
272 // Look in the static hashtable of properties
273 const HashEntry
* entry
= thisObject
->findPropertyHashEntry(exec
, propertyName
);
274 if (entry
&& entry
->attributes() & DontDelete
&& !exec
->globalData().isInDefineOwnProperty())
275 return false; // this builtin property can't be deleted
277 // FIXME: Should the code here actually do some deletion?
281 bool JSObject::hasOwnProperty(ExecState
* exec
, const Identifier
& propertyName
) const
284 return const_cast<JSObject
*>(this)->methodTable()->getOwnPropertySlot(const_cast<JSObject
*>(this), exec
, propertyName
, slot
);
287 bool JSObject::deletePropertyByIndex(JSCell
* cell
, ExecState
* exec
, unsigned propertyName
)
289 JSObject
* thisObject
= jsCast
<JSObject
*>(cell
);
290 return thisObject
->methodTable()->deleteProperty(thisObject
, exec
, Identifier::from(exec
, propertyName
));
293 static ALWAYS_INLINE JSValue
callDefaultValueFunction(ExecState
* exec
, const JSObject
* object
, const Identifier
& propertyName
)
295 JSValue function
= object
->get(exec
, propertyName
);
297 CallType callType
= getCallData(function
, callData
);
298 if (callType
== CallTypeNone
)
299 return exec
->exception();
301 // Prevent "toString" and "valueOf" from observing execution if an exception
303 if (exec
->hadException())
304 return exec
->exception();
306 JSValue result
= call(exec
, function
, callType
, callData
, const_cast<JSObject
*>(object
), exec
->emptyList());
307 ASSERT(!result
.isGetterSetter());
308 if (exec
->hadException())
309 return exec
->exception();
310 if (result
.isObject())
315 bool JSObject::getPrimitiveNumber(ExecState
* exec
, double& number
, JSValue
& result
) const
317 result
= methodTable()->defaultValue(this, exec
, PreferNumber
);
318 number
= result
.toNumber(exec
);
319 return !result
.isString();
323 JSValue
JSObject::defaultValue(const JSObject
* object
, ExecState
* exec
, PreferredPrimitiveType hint
)
325 // Must call toString first for Date objects.
326 if ((hint
== PreferString
) || (hint
!= PreferNumber
&& object
->prototype() == exec
->lexicalGlobalObject()->datePrototype())) {
327 JSValue value
= callDefaultValueFunction(exec
, object
, exec
->propertyNames().toString
);
330 value
= callDefaultValueFunction(exec
, object
, exec
->propertyNames().valueOf
);
334 JSValue value
= callDefaultValueFunction(exec
, object
, exec
->propertyNames().valueOf
);
337 value
= callDefaultValueFunction(exec
, object
, exec
->propertyNames().toString
);
342 ASSERT(!exec
->hadException());
344 return throwError(exec
, createTypeError(exec
, "No default value"));
347 const HashEntry
* JSObject::findPropertyHashEntry(ExecState
* exec
, const Identifier
& propertyName
) const
349 for (const ClassInfo
* info
= classInfo(); info
; info
= info
->parentClass
) {
350 if (const HashTable
* propHashTable
= info
->propHashTable(exec
)) {
351 if (const HashEntry
* entry
= propHashTable
->entry(exec
, propertyName
))
358 bool JSObject::hasInstance(JSObject
*, ExecState
* exec
, JSValue value
, JSValue proto
)
360 if (!value
.isObject())
363 if (!proto
.isObject()) {
364 throwError(exec
, createTypeError(exec
, "instanceof called on an object with an invalid prototype property."));
368 JSObject
* object
= asObject(value
);
369 while ((object
= object
->prototype().getObject())) {
376 bool JSObject::propertyIsEnumerable(ExecState
* exec
, const Identifier
& propertyName
) const
378 PropertyDescriptor descriptor
;
379 if (!const_cast<JSObject
*>(this)->methodTable()->getOwnPropertyDescriptor(const_cast<JSObject
*>(this), exec
, propertyName
, descriptor
))
381 return descriptor
.enumerable();
384 bool JSObject::getPropertySpecificValue(ExecState
* exec
, const Identifier
& propertyName
, JSCell
*& specificValue
) const
387 if (structure()->get(exec
->globalData(), propertyName
, attributes
, specificValue
) != WTF::notFound
)
390 // This could be a function within the static table? - should probably
391 // also look in the hash? This currently should not be a problem, since
392 // we've currently always call 'get' first, which should have populated
393 // the normal storage.
397 void JSObject::getPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
399 object
->methodTable()->getOwnPropertyNames(object
, exec
, propertyNames
, mode
);
401 if (object
->prototype().isNull())
404 JSObject
* prototype
= asObject(object
->prototype());
406 if (prototype
->structure()->typeInfo().overridesGetPropertyNames()) {
407 prototype
->methodTable()->getPropertyNames(prototype
, exec
, propertyNames
, mode
);
410 prototype
->methodTable()->getOwnPropertyNames(prototype
, exec
, propertyNames
, mode
);
411 JSValue nextProto
= prototype
->prototype();
412 if (nextProto
.isNull())
414 prototype
= asObject(nextProto
);
418 void JSObject::getOwnPropertyNames(JSObject
* object
, ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
420 object
->structure()->getPropertyNamesFromStructure(exec
->globalData(), propertyNames
, mode
);
421 if (!object
->staticFunctionsReified())
422 getClassPropertyNames(exec
, object
->classInfo(), propertyNames
, mode
);
425 bool JSObject::toBoolean(ExecState
*) const
430 double JSObject::toNumber(ExecState
* exec
) const
432 JSValue primitive
= toPrimitive(exec
, PreferNumber
);
433 if (exec
->hadException()) // should be picked up soon in Nodes.cpp
435 return primitive
.toNumber(exec
);
438 JSString
* JSObject::toString(ExecState
* exec
) const
440 JSValue primitive
= toPrimitive(exec
, PreferString
);
441 if (exec
->hadException())
442 return jsEmptyString(exec
);
443 return primitive
.toString(exec
);
446 JSObject
* JSObject::toThisObject(JSCell
* cell
, ExecState
*)
448 return jsCast
<JSObject
*>(cell
);
451 JSObject
* JSObject::unwrappedObject()
454 return jsCast
<JSGlobalThis
*>(this)->unwrappedObject();
458 void JSObject::seal(JSGlobalData
& globalData
)
460 if (isSealed(globalData
))
462 preventExtensions(globalData
);
463 setStructure(globalData
, Structure::sealTransition(globalData
, structure()));
466 void JSObject::freeze(JSGlobalData
& globalData
)
468 if (isFrozen(globalData
))
470 preventExtensions(globalData
);
471 setStructure(globalData
, Structure::freezeTransition(globalData
, structure()));
474 void JSObject::preventExtensions(JSGlobalData
& globalData
)
477 asArray(this)->enterDictionaryMode(globalData
);
479 setStructure(globalData
, Structure::preventExtensionsTransition(globalData
, structure()));
482 // This presently will flatten to an uncachable dictionary; this is suitable
483 // for use in delete, we may want to do something different elsewhere.
484 void JSObject::reifyStaticFunctionsForDelete(ExecState
* exec
)
486 ASSERT(!staticFunctionsReified());
487 JSGlobalData
& globalData
= exec
->globalData();
489 // If this object's ClassInfo has no static properties, then nothing to reify!
490 // We can safely set the flag to avoid the expensive check again in the future.
491 if (!classInfo()->hasStaticProperties()) {
492 structure()->setStaticFunctionsReified();
496 if (!structure()->isUncacheableDictionary())
497 setStructure(globalData
, Structure::toUncacheableDictionaryTransition(globalData
, structure()));
499 for (const ClassInfo
* info
= classInfo(); info
; info
= info
->parentClass
) {
500 const HashTable
* hashTable
= info
->propHashTable(globalObject()->globalExec());
504 for (HashTable::ConstIterator iter
= hashTable
->begin(globalData
); iter
!= hashTable
->end(globalData
); ++iter
) {
505 if (iter
->attributes() & Function
)
506 setUpStaticFunctionSlot(globalObject()->globalExec(), *iter
, this, Identifier(&globalData
, iter
->key()), slot
);
510 structure()->setStaticFunctionsReified();
513 void JSObject::removeDirect(JSGlobalData
& globalData
, const Identifier
& propertyName
)
515 if (structure()->get(globalData
, propertyName
) == WTF::notFound
)
519 if (structure()->isUncacheableDictionary()) {
520 offset
= structure()->removePropertyWithoutTransition(globalData
, propertyName
);
521 if (offset
!= WTF::notFound
)
522 putUndefinedAtDirectOffset(offset
);
526 setStructure(globalData
, Structure::removePropertyTransition(globalData
, structure(), propertyName
, offset
));
527 if (offset
!= WTF::notFound
)
528 putUndefinedAtDirectOffset(offset
);
531 NEVER_INLINE
void JSObject::fillGetterPropertySlot(PropertySlot
& slot
, WriteBarrierBase
<Unknown
>* location
)
533 if (JSObject
* getterFunction
= asGetterSetter(location
->get())->getter()) {
534 if (!structure()->isDictionary())
535 slot
.setCacheableGetterSlot(this, getterFunction
, offsetForLocation(location
));
537 slot
.setGetterSlot(getterFunction
);
542 Structure
* JSObject::createInheritorID(JSGlobalData
& globalData
)
544 JSGlobalObject
* globalObject
;
546 globalObject
= static_cast<JSGlobalThis
*>(this)->unwrappedObject();
548 globalObject
= structure()->globalObject();
549 ASSERT(globalObject
);
550 m_inheritorID
.set(globalData
, this, createEmptyObjectStructure(globalData
, globalObject
, this));
551 ASSERT(m_inheritorID
->isEmpty());
552 return m_inheritorID
.get();
555 PropertyStorage
JSObject::growPropertyStorage(JSGlobalData
& globalData
, size_t oldSize
, size_t newSize
)
557 ASSERT(newSize
> oldSize
);
559 // It's important that this function not rely on structure(), since
560 // we might be in the middle of a transition.
562 PropertyStorage oldPropertyStorage
= m_propertyStorage
.get();
563 PropertyStorage newPropertyStorage
= 0;
565 if (isUsingInlineStorage()) {
566 // We have this extra temp here to slake GCC's thirst for the blood of those who dereference type-punned pointers.
567 void* temp
= newPropertyStorage
;
568 if (!globalData
.heap
.tryAllocateStorage(sizeof(WriteBarrierBase
<Unknown
>) * newSize
, &temp
))
570 newPropertyStorage
= static_cast<PropertyStorage
>(temp
);
572 for (unsigned i
= 0; i
< oldSize
; ++i
)
573 newPropertyStorage
[i
] = oldPropertyStorage
[i
];
575 // We have this extra temp here to slake GCC's thirst for the blood of those who dereference type-punned pointers.
576 void* temp
= oldPropertyStorage
;
577 if (!globalData
.heap
.tryReallocateStorage(&temp
, sizeof(WriteBarrierBase
<Unknown
>) * oldSize
, sizeof(WriteBarrierBase
<Unknown
>) * newSize
))
579 newPropertyStorage
= static_cast<PropertyStorage
>(temp
);
582 ASSERT(newPropertyStorage
);
583 return newPropertyStorage
;
586 bool JSObject::getOwnPropertyDescriptor(JSObject
* object
, ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
)
588 unsigned attributes
= 0;
590 size_t offset
= object
->structure()->get(exec
->globalData(), propertyName
, attributes
, cell
);
591 if (offset
== WTF::notFound
)
593 descriptor
.setDescriptor(object
->getDirectOffset(offset
), attributes
);
597 bool JSObject::getPropertyDescriptor(ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
)
599 JSObject
* object
= this;
601 if (object
->methodTable()->getOwnPropertyDescriptor(object
, exec
, propertyName
, descriptor
))
603 JSValue prototype
= object
->prototype();
604 if (!prototype
.isObject())
606 object
= asObject(prototype
);
610 static bool putDescriptor(ExecState
* exec
, JSObject
* target
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
, unsigned attributes
, const PropertyDescriptor
& oldDescriptor
)
612 if (descriptor
.isGenericDescriptor() || descriptor
.isDataDescriptor()) {
613 if (descriptor
.isGenericDescriptor() && oldDescriptor
.isAccessorDescriptor()) {
614 GetterSetter
* accessor
= GetterSetter::create(exec
);
615 if (oldDescriptor
.getterPresent())
616 accessor
->setGetter(exec
->globalData(), oldDescriptor
.getterObject());
617 if (oldDescriptor
.setterPresent())
618 accessor
->setSetter(exec
->globalData(), oldDescriptor
.setterObject());
619 target
->putDirectAccessor(exec
->globalData(), propertyName
, accessor
, attributes
| Accessor
);
622 JSValue newValue
= jsUndefined();
623 if (descriptor
.value())
624 newValue
= descriptor
.value();
625 else if (oldDescriptor
.value())
626 newValue
= oldDescriptor
.value();
627 target
->putDirect(exec
->globalData(), propertyName
, newValue
, attributes
& ~Accessor
);
628 if (attributes
& ReadOnly
)
629 target
->structure()->setContainsReadOnlyProperties();
632 attributes
&= ~ReadOnly
;
633 GetterSetter
* accessor
= GetterSetter::create(exec
);
635 if (descriptor
.getterPresent())
636 accessor
->setGetter(exec
->globalData(), descriptor
.getterObject());
637 else if (oldDescriptor
.getterPresent())
638 accessor
->setGetter(exec
->globalData(), oldDescriptor
.getterObject());
639 if (descriptor
.setterPresent())
640 accessor
->setSetter(exec
->globalData(), descriptor
.setterObject());
641 else if (oldDescriptor
.setterPresent())
642 accessor
->setSetter(exec
->globalData(), oldDescriptor
.setterObject());
644 target
->putDirectAccessor(exec
->globalData(), propertyName
, accessor
, attributes
| Accessor
);
648 class DefineOwnPropertyScope
{
650 DefineOwnPropertyScope(ExecState
* exec
)
651 : m_globalData(exec
->globalData())
653 m_globalData
.setInDefineOwnProperty(true);
656 ~DefineOwnPropertyScope()
658 m_globalData
.setInDefineOwnProperty(false);
662 JSGlobalData
& m_globalData
;
665 bool JSObject::defineOwnProperty(JSObject
* object
, ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
, bool throwException
)
667 // Track on the globaldata that we're in define property.
668 // Currently DefineOwnProperty uses delete to remove properties when they are being replaced
669 // (particularly when changing attributes), however delete won't allow non-configurable (i.e.
670 // DontDelete) properties to be deleted. For now, we can use this flag to make this work.
671 DefineOwnPropertyScope
scope(exec
);
673 // If we have a new property we can just put it on normally
674 PropertyDescriptor current
;
675 if (!object
->methodTable()->getOwnPropertyDescriptor(object
, exec
, propertyName
, current
)) {
676 // unless extensions are prevented!
677 if (!object
->isExtensible()) {
679 throwError(exec
, createTypeError(exec
, "Attempting to define property on object that is not extensible."));
682 PropertyDescriptor oldDescriptor
;
683 oldDescriptor
.setValue(jsUndefined());
684 return putDescriptor(exec
, object
, propertyName
, descriptor
, descriptor
.attributes(), oldDescriptor
);
687 if (descriptor
.isEmpty())
690 if (current
.equalTo(exec
, descriptor
))
693 // Filter out invalid changes
694 if (!current
.configurable()) {
695 if (descriptor
.configurable()) {
697 throwError(exec
, createTypeError(exec
, "Attempting to configurable attribute of unconfigurable property."));
700 if (descriptor
.enumerablePresent() && descriptor
.enumerable() != current
.enumerable()) {
702 throwError(exec
, createTypeError(exec
, "Attempting to change enumerable attribute of unconfigurable property."));
707 // A generic descriptor is simply changing the attributes of an existing property
708 if (descriptor
.isGenericDescriptor()) {
709 if (!current
.attributesEqual(descriptor
)) {
710 object
->methodTable()->deleteProperty(object
, exec
, propertyName
);
711 return putDescriptor(exec
, object
, propertyName
, descriptor
, descriptor
.attributesOverridingCurrent(current
), current
);
716 // Changing between a normal property or an accessor property
717 if (descriptor
.isDataDescriptor() != current
.isDataDescriptor()) {
718 if (!current
.configurable()) {
720 throwError(exec
, createTypeError(exec
, "Attempting to change access mechanism for an unconfigurable property."));
723 object
->methodTable()->deleteProperty(object
, exec
, propertyName
);
724 return putDescriptor(exec
, object
, propertyName
, descriptor
, descriptor
.attributesOverridingCurrent(current
), current
);
727 // Changing the value and attributes of an existing property
728 if (descriptor
.isDataDescriptor()) {
729 if (!current
.configurable()) {
730 if (!current
.writable() && descriptor
.writable()) {
732 throwError(exec
, createTypeError(exec
, "Attempting to change writable attribute of unconfigurable property."));
735 if (!current
.writable()) {
736 if (descriptor
.value() && !sameValue(exec
, current
.value(), descriptor
.value())) {
738 throwError(exec
, createTypeError(exec
, "Attempting to change value of a readonly property."));
743 if (current
.attributesEqual(descriptor
) && !descriptor
.value())
745 object
->methodTable()->deleteProperty(object
, exec
, propertyName
);
746 return putDescriptor(exec
, object
, propertyName
, descriptor
, descriptor
.attributesOverridingCurrent(current
), current
);
749 // Changing the accessor functions of an existing accessor property
750 ASSERT(descriptor
.isAccessorDescriptor());
751 if (!current
.configurable()) {
752 if (descriptor
.setterPresent() && !(current
.setterPresent() && JSValue::strictEqual(exec
, current
.setter(), descriptor
.setter()))) {
754 throwError(exec
, createTypeError(exec
, "Attempting to change the setter of an unconfigurable property."));
757 if (descriptor
.getterPresent() && !(current
.getterPresent() && JSValue::strictEqual(exec
, current
.getter(), descriptor
.getter()))) {
759 throwError(exec
, createTypeError(exec
, "Attempting to change the getter of an unconfigurable property."));
763 JSValue accessor
= object
->getDirect(exec
->globalData(), propertyName
);
766 GetterSetter
* getterSetter
= asGetterSetter(accessor
);
767 if (descriptor
.setterPresent())
768 getterSetter
->setSetter(exec
->globalData(), descriptor
.setterObject());
769 if (descriptor
.getterPresent())
770 getterSetter
->setGetter(exec
->globalData(), descriptor
.getterObject());
771 if (current
.attributesEqual(descriptor
))
773 object
->methodTable()->deleteProperty(object
, exec
, propertyName
);
774 unsigned attrs
= descriptor
.attributesOverridingCurrent(current
);
775 object
->putDirectAccessor(exec
->globalData(), propertyName
, getterSetter
, attrs
| Accessor
);
779 JSObject
* throwTypeError(ExecState
* exec
, const UString
& message
)
781 return throwError(exec
, createTypeError(exec
, message
));