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 "JSGlobalObject.h"
31 #include "NativeErrorConstructor.h"
32 #include "ObjectPrototype.h"
33 #include "PropertyDescriptor.h"
34 #include "PropertyNameArray.h"
37 #include "Operations.h"
39 #include <wtf/Assertions.h>
43 ASSERT_CLASS_FITS_IN_CELL(JSObject
);
45 static inline void getClassPropertyNames(ExecState
* exec
, const ClassInfo
* classInfo
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
47 // Add properties from the static hashtables of properties
48 for (; classInfo
; classInfo
= classInfo
->parentClass
) {
49 const HashTable
* table
= classInfo
->propHashTable(exec
);
52 table
->initializeIfNeeded(exec
);
55 int hashSizeMask
= table
->compactSize
- 1;
56 const HashEntry
* entry
= table
->table
;
57 for (int i
= 0; i
<= hashSizeMask
; ++i
, ++entry
) {
58 if (entry
->key() && (!(entry
->attributes() & DontEnum
) || (mode
== IncludeDontEnumProperties
)))
59 propertyNames
.add(entry
->key());
64 void JSObject::markChildren(MarkStack
& markStack
)
67 bool wasCheckingForDefaultMarkViolation
= markStack
.m_isCheckingForDefaultMarkViolation
;
68 markStack
.m_isCheckingForDefaultMarkViolation
= false;
71 markChildrenDirect(markStack
);
74 markStack
.m_isCheckingForDefaultMarkViolation
= wasCheckingForDefaultMarkViolation
;
78 UString
JSObject::className() const
80 const ClassInfo
* info
= classInfo();
82 return info
->className
;
86 bool JSObject::getOwnPropertySlot(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
88 return getOwnPropertySlot(exec
, Identifier::from(exec
, propertyName
), slot
);
91 static void throwSetterError(ExecState
* exec
)
93 throwError(exec
, TypeError
, "setting a property that has only a getter");
97 void JSObject::put(ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, PutPropertySlot
& slot
)
100 ASSERT(!Heap::heap(value
) || Heap::heap(value
) == Heap::heap(this));
102 if (propertyName
== exec
->propertyNames().underscoreProto
) {
103 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
104 if (!value
.isObject() && !value
.isNull())
107 JSValue nextPrototypeValue
= value
;
108 while (nextPrototypeValue
&& nextPrototypeValue
.isObject()) {
109 JSObject
* nextPrototype
= asObject(nextPrototypeValue
)->unwrappedObject();
110 if (nextPrototype
== this) {
111 throwError(exec
, GeneralError
, "cyclic __proto__ value");
114 nextPrototypeValue
= nextPrototype
->prototype();
121 // Check if there are any setters or getters in the prototype chain
123 for (JSObject
* obj
= this; !obj
->structure()->hasGetterSetterProperties(); obj
= asObject(prototype
)) {
124 prototype
= obj
->prototype();
125 if (prototype
.isNull()) {
126 putDirectInternal(exec
->globalData(), propertyName
, value
, 0, true, slot
);
132 JSCell
* specificValue
;
133 if ((m_structure
->get(propertyName
, attributes
, specificValue
) != WTF::notFound
) && attributes
& ReadOnly
)
136 for (JSObject
* obj
= this; ; obj
= asObject(prototype
)) {
137 if (JSValue gs
= obj
->getDirect(propertyName
)) {
138 if (gs
.isGetterSetter()) {
139 JSObject
* setterFunc
= asGetterSetter(gs
)->setter();
141 throwSetterError(exec
);
146 CallType callType
= setterFunc
->getCallData(callData
);
147 MarkedArgumentBuffer args
;
149 call(exec
, setterFunc
, callType
, callData
, this, args
);
153 // If there's an existing property on the object or one of its
154 // prototypes it should be replaced, so break here.
158 prototype
= obj
->prototype();
159 if (prototype
.isNull())
163 putDirectInternal(exec
->globalData(), propertyName
, value
, 0, true, slot
);
167 void JSObject::put(ExecState
* exec
, unsigned propertyName
, JSValue value
)
169 PutPropertySlot slot
;
170 put(exec
, Identifier::from(exec
, propertyName
), value
, slot
);
173 void JSObject::putWithAttributes(ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, unsigned attributes
, bool checkReadOnly
, PutPropertySlot
& slot
)
175 putDirectInternal(exec
->globalData(), propertyName
, value
, attributes
, checkReadOnly
, slot
);
178 void JSObject::putWithAttributes(ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, unsigned attributes
)
180 putDirectInternal(exec
->globalData(), propertyName
, value
, attributes
);
183 void JSObject::putWithAttributes(ExecState
* exec
, unsigned propertyName
, JSValue value
, unsigned attributes
)
185 putWithAttributes(exec
, Identifier::from(exec
, propertyName
), value
, attributes
);
188 bool JSObject::hasProperty(ExecState
* exec
, const Identifier
& propertyName
) const
191 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
194 bool JSObject::hasProperty(ExecState
* exec
, unsigned propertyName
) const
197 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
201 bool JSObject::deleteProperty(ExecState
* exec
, const Identifier
& propertyName
)
204 JSCell
* specificValue
;
205 if (m_structure
->get(propertyName
, attributes
, specificValue
) != WTF::notFound
) {
206 if ((attributes
& DontDelete
))
208 removeDirect(propertyName
);
212 // Look in the static hashtable of properties
213 const HashEntry
* entry
= findPropertyHashEntry(exec
, propertyName
);
214 if (entry
&& entry
->attributes() & DontDelete
)
215 return false; // this builtin property can't be deleted
217 // FIXME: Should the code here actually do some deletion?
221 bool JSObject::hasOwnProperty(ExecState
* exec
, const Identifier
& propertyName
) const
224 return const_cast<JSObject
*>(this)->getOwnPropertySlot(exec
, propertyName
, slot
);
227 bool JSObject::deleteProperty(ExecState
* exec
, unsigned propertyName
)
229 return deleteProperty(exec
, Identifier::from(exec
, propertyName
));
232 static ALWAYS_INLINE JSValue
callDefaultValueFunction(ExecState
* exec
, const JSObject
* object
, const Identifier
& propertyName
)
234 JSValue function
= object
->get(exec
, propertyName
);
236 CallType callType
= function
.getCallData(callData
);
237 if (callType
== CallTypeNone
)
238 return exec
->exception();
240 // Prevent "toString" and "valueOf" from observing execution if an exception
242 if (exec
->hadException())
243 return exec
->exception();
245 JSValue result
= call(exec
, function
, callType
, callData
, const_cast<JSObject
*>(object
), exec
->emptyList());
246 ASSERT(!result
.isGetterSetter());
247 if (exec
->hadException())
248 return exec
->exception();
249 if (result
.isObject())
254 bool JSObject::getPrimitiveNumber(ExecState
* exec
, double& number
, JSValue
& result
)
256 result
= defaultValue(exec
, PreferNumber
);
257 number
= result
.toNumber(exec
);
258 return !result
.isString();
262 JSValue
JSObject::defaultValue(ExecState
* exec
, PreferredPrimitiveType hint
) const
264 // Must call toString first for Date objects.
265 if ((hint
== PreferString
) || (hint
!= PreferNumber
&& prototype() == exec
->lexicalGlobalObject()->datePrototype())) {
266 JSValue value
= callDefaultValueFunction(exec
, this, exec
->propertyNames().toString
);
269 value
= callDefaultValueFunction(exec
, this, exec
->propertyNames().valueOf
);
273 JSValue value
= callDefaultValueFunction(exec
, this, exec
->propertyNames().valueOf
);
276 value
= callDefaultValueFunction(exec
, this, exec
->propertyNames().toString
);
281 ASSERT(!exec
->hadException());
283 return throwError(exec
, TypeError
, "No default value");
286 const HashEntry
* JSObject::findPropertyHashEntry(ExecState
* exec
, const Identifier
& propertyName
) const
288 for (const ClassInfo
* info
= classInfo(); info
; info
= info
->parentClass
) {
289 if (const HashTable
* propHashTable
= info
->propHashTable(exec
)) {
290 if (const HashEntry
* entry
= propHashTable
->entry(exec
, propertyName
))
297 void JSObject::defineGetter(ExecState
* exec
, const Identifier
& propertyName
, JSObject
* getterFunction
, unsigned attributes
)
299 JSValue object
= getDirect(propertyName
);
300 if (object
&& object
.isGetterSetter()) {
301 ASSERT(m_structure
->hasGetterSetterProperties());
302 asGetterSetter(object
)->setGetter(getterFunction
);
306 PutPropertySlot slot
;
307 GetterSetter
* getterSetter
= new (exec
) GetterSetter(exec
);
308 putDirectInternal(exec
->globalData(), propertyName
, getterSetter
, attributes
| Getter
, true, slot
);
310 // putDirect will change our Structure if we add a new property. For
311 // getters and setters, though, we also need to change our Structure
312 // if we override an existing non-getter or non-setter.
313 if (slot
.type() != PutPropertySlot::NewProperty
) {
314 if (!m_structure
->isDictionary()) {
315 RefPtr
<Structure
> structure
= Structure::getterSetterTransition(m_structure
);
316 setStructure(structure
.release());
320 m_structure
->setHasGetterSetterProperties(true);
321 getterSetter
->setGetter(getterFunction
);
324 void JSObject::defineSetter(ExecState
* exec
, const Identifier
& propertyName
, JSObject
* setterFunction
, unsigned attributes
)
326 JSValue object
= getDirect(propertyName
);
327 if (object
&& object
.isGetterSetter()) {
328 ASSERT(m_structure
->hasGetterSetterProperties());
329 asGetterSetter(object
)->setSetter(setterFunction
);
333 PutPropertySlot slot
;
334 GetterSetter
* getterSetter
= new (exec
) GetterSetter(exec
);
335 putDirectInternal(exec
->globalData(), propertyName
, getterSetter
, attributes
| Setter
, true, slot
);
337 // putDirect will change our Structure if we add a new property. For
338 // getters and setters, though, we also need to change our Structure
339 // if we override an existing non-getter or non-setter.
340 if (slot
.type() != PutPropertySlot::NewProperty
) {
341 if (!m_structure
->isDictionary()) {
342 RefPtr
<Structure
> structure
= Structure::getterSetterTransition(m_structure
);
343 setStructure(structure
.release());
347 m_structure
->setHasGetterSetterProperties(true);
348 getterSetter
->setSetter(setterFunction
);
351 JSValue
JSObject::lookupGetter(ExecState
*, const Identifier
& propertyName
)
353 JSObject
* object
= this;
355 if (JSValue value
= object
->getDirect(propertyName
)) {
356 if (!value
.isGetterSetter())
357 return jsUndefined();
358 JSObject
* functionObject
= asGetterSetter(value
)->getter();
360 return jsUndefined();
361 return functionObject
;
364 if (!object
->prototype() || !object
->prototype().isObject())
365 return jsUndefined();
366 object
= asObject(object
->prototype());
370 JSValue
JSObject::lookupSetter(ExecState
*, const Identifier
& propertyName
)
372 JSObject
* object
= this;
374 if (JSValue value
= object
->getDirect(propertyName
)) {
375 if (!value
.isGetterSetter())
376 return jsUndefined();
377 JSObject
* functionObject
= asGetterSetter(value
)->setter();
379 return jsUndefined();
380 return functionObject
;
383 if (!object
->prototype() || !object
->prototype().isObject())
384 return jsUndefined();
385 object
= asObject(object
->prototype());
389 bool JSObject::hasInstance(ExecState
* exec
, JSValue value
, JSValue proto
)
391 if (!value
.isObject())
394 if (!proto
.isObject()) {
395 throwError(exec
, TypeError
, "instanceof called on an object with an invalid prototype property.");
399 JSObject
* object
= asObject(value
);
400 while ((object
= object
->prototype().getObject())) {
407 bool JSObject::propertyIsEnumerable(ExecState
* exec
, const Identifier
& propertyName
) const
409 PropertyDescriptor descriptor
;
410 if (!const_cast<JSObject
*>(this)->getOwnPropertyDescriptor(exec
, propertyName
, descriptor
))
412 return descriptor
.enumerable();
415 bool JSObject::getPropertySpecificValue(ExecState
*, const Identifier
& propertyName
, JSCell
*& specificValue
) const
418 if (m_structure
->get(propertyName
, attributes
, specificValue
) != WTF::notFound
)
421 // This could be a function within the static table? - should probably
422 // also look in the hash? This currently should not be a problem, since
423 // we've currently always call 'get' first, which should have populated
424 // the normal storage.
428 void JSObject::getPropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
430 getOwnPropertyNames(exec
, propertyNames
, mode
);
432 if (prototype().isNull())
435 JSObject
* prototype
= asObject(this->prototype());
437 if (prototype
->structure()->typeInfo().overridesGetPropertyNames()) {
438 prototype
->getPropertyNames(exec
, propertyNames
, mode
);
441 prototype
->getOwnPropertyNames(exec
, propertyNames
, mode
);
442 JSValue nextProto
= prototype
->prototype();
443 if (nextProto
.isNull())
445 prototype
= asObject(nextProto
);
449 void JSObject::getOwnPropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
, EnumerationMode mode
)
451 m_structure
->getPropertyNames(propertyNames
, mode
);
452 getClassPropertyNames(exec
, classInfo(), propertyNames
, mode
);
455 bool JSObject::toBoolean(ExecState
*) const
460 double JSObject::toNumber(ExecState
* exec
) const
462 JSValue primitive
= toPrimitive(exec
, PreferNumber
);
463 if (exec
->hadException()) // should be picked up soon in Nodes.cpp
465 return primitive
.toNumber(exec
);
468 UString
JSObject::toString(ExecState
* exec
) const
470 JSValue primitive
= toPrimitive(exec
, PreferString
);
471 if (exec
->hadException())
473 return primitive
.toString(exec
);
476 JSObject
* JSObject::toObject(ExecState
*) const
478 return const_cast<JSObject
*>(this);
481 JSObject
* JSObject::toThisObject(ExecState
*) const
483 return const_cast<JSObject
*>(this);
486 JSObject
* JSObject::unwrappedObject()
491 void JSObject::removeDirect(const Identifier
& propertyName
)
494 if (m_structure
->isUncacheableDictionary()) {
495 offset
= m_structure
->removePropertyWithoutTransition(propertyName
);
496 if (offset
!= WTF::notFound
)
497 putDirectOffset(offset
, jsUndefined());
501 RefPtr
<Structure
> structure
= Structure::removePropertyTransition(m_structure
, propertyName
, offset
);
502 setStructure(structure
.release());
503 if (offset
!= WTF::notFound
)
504 putDirectOffset(offset
, jsUndefined());
507 void JSObject::putDirectFunction(ExecState
* exec
, InternalFunction
* function
, unsigned attr
)
509 putDirectFunction(Identifier(exec
, function
->name(exec
)), function
, attr
);
512 void JSObject::putDirectFunctionWithoutTransition(ExecState
* exec
, InternalFunction
* function
, unsigned attr
)
514 putDirectFunctionWithoutTransition(Identifier(exec
, function
->name(exec
)), function
, attr
);
517 NEVER_INLINE
void JSObject::fillGetterPropertySlot(PropertySlot
& slot
, JSValue
* location
)
519 if (JSObject
* getterFunction
= asGetterSetter(*location
)->getter()) {
520 if (!structure()->isDictionary())
521 slot
.setCacheableGetterSlot(this, getterFunction
, offsetForLocation(location
));
523 slot
.setGetterSlot(getterFunction
);
528 Structure
* JSObject::createInheritorID()
530 m_inheritorID
= JSObject::createStructure(this);
531 return m_inheritorID
.get();
534 void JSObject::allocatePropertyStorage(size_t oldSize
, size_t newSize
)
536 allocatePropertyStorageInline(oldSize
, newSize
);
539 bool JSObject::getOwnPropertyDescriptor(ExecState
*, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
)
541 unsigned attributes
= 0;
543 size_t offset
= m_structure
->get(propertyName
, attributes
, cell
);
544 if (offset
== WTF::notFound
)
546 descriptor
.setDescriptor(getDirectOffset(offset
), attributes
);
550 bool JSObject::getPropertyDescriptor(ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
)
552 JSObject
* object
= this;
554 if (object
->getOwnPropertyDescriptor(exec
, propertyName
, descriptor
))
556 JSValue prototype
= object
->prototype();
557 if (!prototype
.isObject())
559 object
= asObject(prototype
);
563 static bool putDescriptor(ExecState
* exec
, JSObject
* target
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
, unsigned attributes
, JSValue oldValue
)
565 if (descriptor
.isGenericDescriptor() || descriptor
.isDataDescriptor()) {
566 target
->putWithAttributes(exec
, propertyName
, descriptor
.value() ? descriptor
.value() : oldValue
, attributes
& ~(Getter
| Setter
));
569 attributes
&= ~ReadOnly
;
570 if (descriptor
.getter() && descriptor
.getter().isObject())
571 target
->defineGetter(exec
, propertyName
, asObject(descriptor
.getter()), attributes
);
572 if (exec
->hadException())
574 if (descriptor
.setter() && descriptor
.setter().isObject())
575 target
->defineSetter(exec
, propertyName
, asObject(descriptor
.setter()), attributes
);
576 return !exec
->hadException();
579 bool JSObject::defineOwnProperty(ExecState
* exec
, const Identifier
& propertyName
, PropertyDescriptor
& descriptor
, bool throwException
)
581 // If we have a new property we can just put it on normally
582 PropertyDescriptor current
;
583 if (!getOwnPropertyDescriptor(exec
, propertyName
, current
))
584 return putDescriptor(exec
, this, propertyName
, descriptor
, descriptor
.attributes(), jsUndefined());
586 if (descriptor
.isEmpty())
589 if (current
.equalTo(exec
, descriptor
))
592 // Filter out invalid changes
593 if (!current
.configurable()) {
594 if (descriptor
.configurable()) {
596 throwError(exec
, TypeError
, "Attempting to configurable attribute of unconfigurable property.");
599 if (descriptor
.enumerablePresent() && descriptor
.enumerable() != current
.enumerable()) {
601 throwError(exec
, TypeError
, "Attempting to change enumerable attribute of unconfigurable property.");
606 // A generic descriptor is simply changing the attributes of an existing property
607 if (descriptor
.isGenericDescriptor()) {
608 if (!current
.attributesEqual(descriptor
)) {
609 deleteProperty(exec
, propertyName
);
610 putDescriptor(exec
, this, propertyName
, descriptor
, current
.attributesWithOverride(descriptor
), current
.value());
615 // Changing between a normal property or an accessor property
616 if (descriptor
.isDataDescriptor() != current
.isDataDescriptor()) {
617 if (!current
.configurable()) {
619 throwError(exec
, TypeError
, "Attempting to change access mechanism for an unconfigurable property.");
622 deleteProperty(exec
, propertyName
);
623 return putDescriptor(exec
, this, propertyName
, descriptor
, current
.attributesWithOverride(descriptor
), current
.value() ? current
.value() : jsUndefined());
626 // Changing the value and attributes of an existing property
627 if (descriptor
.isDataDescriptor()) {
628 if (!current
.configurable()) {
629 if (!current
.writable() && descriptor
.writable()) {
631 throwError(exec
, TypeError
, "Attempting to change writable attribute of unconfigurable property.");
634 if (!current
.writable()) {
635 if (descriptor
.value() || !JSValue::strictEqual(exec
, current
.value(), descriptor
.value())) {
637 throwError(exec
, TypeError
, "Attempting to change value of a readonly property.");
641 } else if (current
.attributesEqual(descriptor
)) {
642 if (!descriptor
.value())
644 PutPropertySlot slot
;
645 put(exec
, propertyName
, descriptor
.value(), slot
);
646 if (exec
->hadException())
650 deleteProperty(exec
, propertyName
);
651 return putDescriptor(exec
, this, propertyName
, descriptor
, current
.attributesWithOverride(descriptor
), current
.value());
654 // Changing the accessor functions of an existing accessor property
655 ASSERT(descriptor
.isAccessorDescriptor());
656 if (!current
.configurable()) {
657 if (descriptor
.setterPresent() && !(current
.setter() && JSValue::strictEqual(exec
, current
.setter(), descriptor
.setter()))) {
659 throwError(exec
, TypeError
, "Attempting to change the setter of an unconfigurable property.");
662 if (descriptor
.getterPresent() && !(current
.getter() && JSValue::strictEqual(exec
, current
.getter(), descriptor
.getter()))) {
664 throwError(exec
, TypeError
, "Attempting to change the getter of an unconfigurable property.");
668 JSValue accessor
= getDirect(propertyName
);
671 GetterSetter
* getterSetter
= asGetterSetter(accessor
);
672 if (current
.attributesEqual(descriptor
)) {
673 if (descriptor
.setter())
674 getterSetter
->setSetter(asObject(descriptor
.setter()));
675 if (descriptor
.getter())
676 getterSetter
->setGetter(asObject(descriptor
.getter()));
679 deleteProperty(exec
, propertyName
);
680 unsigned attrs
= current
.attributesWithOverride(descriptor
);
681 if (descriptor
.setter())
683 if (descriptor
.getter())
685 putDirect(propertyName
, getterSetter
, attrs
);