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 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 "PropertyNameArray.h"
36 #include "Operations.h"
38 #include <wtf/Assertions.h>
40 #define JSOBJECT_MARK_TRACING 0
42 #if JSOBJECT_MARK_TRACING
44 #define JSOBJECT_MARK_BEGIN() \
45 static int markStackDepth = 0; \
46 for (int i = 0; i < markStackDepth; i++) \
48 printf("%s (%p)\n", className().UTF8String().c_str(), this); \
51 #define JSOBJECT_MARK_END() \
54 #else // JSOBJECT_MARK_TRACING
56 #define JSOBJECT_MARK_BEGIN()
57 #define JSOBJECT_MARK_END()
59 #endif // JSOBJECT_MARK_TRACING
63 ASSERT_CLASS_FITS_IN_CELL(JSObject
);
67 JSOBJECT_MARK_BEGIN();
72 size_t storageSize
= m_structure
->propertyStorageSize();
73 for (size_t i
= 0; i
< storageSize
; ++i
) {
74 JSValuePtr v
= m_propertyStorage
[i
];
82 UString
JSObject::className() const
84 const ClassInfo
* info
= classInfo();
86 return info
->className
;
90 bool JSObject::getOwnPropertySlot(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
92 return getOwnPropertySlot(exec
, Identifier::from(exec
, propertyName
), slot
);
95 static void throwSetterError(ExecState
* exec
)
97 throwError(exec
, TypeError
, "setting a property that has only a getter");
101 void JSObject::put(ExecState
* exec
, const Identifier
& propertyName
, JSValuePtr value
, PutPropertySlot
& slot
)
104 ASSERT(!Heap::heap(value
) || Heap::heap(value
) == Heap::heap(this));
106 if (propertyName
== exec
->propertyNames().underscoreProto
) {
107 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
108 if (!value
.isObject() && !value
.isNull())
111 JSValuePtr nextPrototypeValue
= value
;
112 while (nextPrototypeValue
&& nextPrototypeValue
.isObject()) {
113 JSObject
* nextPrototype
= asObject(nextPrototypeValue
)->unwrappedObject();
114 if (nextPrototype
== this) {
115 throwError(exec
, GeneralError
, "cyclic __proto__ value");
118 nextPrototypeValue
= nextPrototype
->prototype();
125 // Check if there are any setters or getters in the prototype chain
126 JSValuePtr prototype
;
127 for (JSObject
* obj
= this; !obj
->structure()->hasGetterSetterProperties(); obj
= asObject(prototype
)) {
128 prototype
= obj
->prototype();
129 if (prototype
.isNull()) {
130 putDirect(propertyName
, value
, 0, true, slot
);
136 if ((m_structure
->get(propertyName
, attributes
) != WTF::notFound
) && attributes
& ReadOnly
)
139 for (JSObject
* obj
= this; ; obj
= asObject(prototype
)) {
140 if (JSValuePtr gs
= obj
->getDirect(propertyName
)) {
141 if (gs
.isGetterSetter()) {
142 JSObject
* setterFunc
= asGetterSetter(gs
)->setter();
144 throwSetterError(exec
);
149 CallType callType
= setterFunc
->getCallData(callData
);
152 call(exec
, setterFunc
, callType
, callData
, this, args
);
156 // If there's an existing property on the object or one of its
157 // prototypes it should be replaced, so break here.
161 prototype
= obj
->prototype();
162 if (prototype
.isNull())
166 putDirect(propertyName
, value
, 0, true, slot
);
170 void JSObject::put(ExecState
* exec
, unsigned propertyName
, JSValuePtr value
)
172 PutPropertySlot slot
;
173 put(exec
, Identifier::from(exec
, propertyName
), value
, slot
);
176 void JSObject::putWithAttributes(ExecState
*, const Identifier
& propertyName
, JSValuePtr value
, unsigned attributes
)
178 putDirect(propertyName
, value
, attributes
);
181 void JSObject::putWithAttributes(ExecState
* exec
, unsigned propertyName
, JSValuePtr value
, unsigned attributes
)
183 putWithAttributes(exec
, Identifier::from(exec
, propertyName
), value
, attributes
);
186 bool JSObject::hasProperty(ExecState
* exec
, const Identifier
& propertyName
) const
189 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
192 bool JSObject::hasProperty(ExecState
* exec
, unsigned propertyName
) const
195 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
199 bool JSObject::deleteProperty(ExecState
* exec
, const Identifier
& propertyName
)
202 if (m_structure
->get(propertyName
, attributes
) != WTF::notFound
) {
203 if ((attributes
& DontDelete
))
205 removeDirect(propertyName
);
209 // Look in the static hashtable of properties
210 const HashEntry
* entry
= findPropertyHashEntry(exec
, propertyName
);
211 if (entry
&& entry
->attributes() & DontDelete
)
212 return false; // this builtin property can't be deleted
214 // FIXME: Should the code here actually do some deletion?
218 bool JSObject::hasOwnProperty(ExecState
* exec
, const Identifier
& propertyName
) const
221 return const_cast<JSObject
*>(this)->getOwnPropertySlot(exec
, propertyName
, slot
);
224 bool JSObject::deleteProperty(ExecState
* exec
, unsigned propertyName
)
226 return deleteProperty(exec
, Identifier::from(exec
, propertyName
));
229 static ALWAYS_INLINE JSValuePtr
callDefaultValueFunction(ExecState
* exec
, const JSObject
* object
, const Identifier
& propertyName
)
231 JSValuePtr function
= object
->get(exec
, propertyName
);
233 CallType callType
= function
.getCallData(callData
);
234 if (callType
== CallTypeNone
)
235 return exec
->exception();
237 // Prevent "toString" and "valueOf" from observing execution if an exception
239 if (exec
->hadException())
240 return exec
->exception();
242 JSValuePtr result
= call(exec
, function
, callType
, callData
, const_cast<JSObject
*>(object
), exec
->emptyList());
243 ASSERT(!result
.isGetterSetter());
244 if (exec
->hadException())
245 return exec
->exception();
246 if (result
.isObject())
251 bool JSObject::getPrimitiveNumber(ExecState
* exec
, double& number
, JSValuePtr
& result
)
253 result
= defaultValue(exec
, PreferNumber
);
254 number
= result
.toNumber(exec
);
255 return !result
.isString();
259 JSValuePtr
JSObject::defaultValue(ExecState
* exec
, PreferredPrimitiveType hint
) const
261 // Must call toString first for Date objects.
262 if ((hint
== PreferString
) || (hint
!= PreferNumber
&& prototype() == exec
->lexicalGlobalObject()->datePrototype())) {
263 JSValuePtr value
= callDefaultValueFunction(exec
, this, exec
->propertyNames().toString
);
266 value
= callDefaultValueFunction(exec
, this, exec
->propertyNames().valueOf
);
270 JSValuePtr value
= callDefaultValueFunction(exec
, this, exec
->propertyNames().valueOf
);
273 value
= callDefaultValueFunction(exec
, this, exec
->propertyNames().toString
);
278 ASSERT(!exec
->hadException());
280 return throwError(exec
, TypeError
, "No default value");
283 const HashEntry
* JSObject::findPropertyHashEntry(ExecState
* exec
, const Identifier
& propertyName
) const
285 for (const ClassInfo
* info
= classInfo(); info
; info
= info
->parentClass
) {
286 if (const HashTable
* propHashTable
= info
->propHashTable(exec
)) {
287 if (const HashEntry
* entry
= propHashTable
->entry(exec
, propertyName
))
294 void JSObject::defineGetter(ExecState
* exec
, const Identifier
& propertyName
, JSObject
* getterFunction
)
296 JSValuePtr object
= getDirect(propertyName
);
297 if (object
&& object
.isGetterSetter()) {
298 ASSERT(m_structure
->hasGetterSetterProperties());
299 asGetterSetter(object
)->setGetter(getterFunction
);
303 PutPropertySlot slot
;
304 GetterSetter
* getterSetter
= new (exec
) GetterSetter
;
305 putDirect(propertyName
, getterSetter
, None
, true, slot
);
307 // putDirect will change our Structure if we add a new property. For
308 // getters and setters, though, we also need to change our Structure
309 // if we override an existing non-getter or non-setter.
310 if (slot
.type() != PutPropertySlot::NewProperty
) {
311 if (!m_structure
->isDictionary()) {
312 RefPtr
<Structure
> structure
= Structure::getterSetterTransition(m_structure
);
313 setStructure(structure
.release());
317 m_structure
->setHasGetterSetterProperties(true);
318 getterSetter
->setGetter(getterFunction
);
321 void JSObject::defineSetter(ExecState
* exec
, const Identifier
& propertyName
, JSObject
* setterFunction
)
323 JSValuePtr object
= getDirect(propertyName
);
324 if (object
&& object
.isGetterSetter()) {
325 ASSERT(m_structure
->hasGetterSetterProperties());
326 asGetterSetter(object
)->setSetter(setterFunction
);
330 PutPropertySlot slot
;
331 GetterSetter
* getterSetter
= new (exec
) GetterSetter
;
332 putDirect(propertyName
, getterSetter
, None
, true, slot
);
334 // putDirect will change our Structure if we add a new property. For
335 // getters and setters, though, we also need to change our Structure
336 // if we override an existing non-getter or non-setter.
337 if (slot
.type() != PutPropertySlot::NewProperty
) {
338 if (!m_structure
->isDictionary()) {
339 RefPtr
<Structure
> structure
= Structure::getterSetterTransition(m_structure
);
340 setStructure(structure
.release());
344 m_structure
->setHasGetterSetterProperties(true);
345 getterSetter
->setSetter(setterFunction
);
348 JSValuePtr
JSObject::lookupGetter(ExecState
*, const Identifier
& propertyName
)
350 JSObject
* object
= this;
352 if (JSValuePtr value
= object
->getDirect(propertyName
)) {
353 if (!value
.isGetterSetter())
354 return jsUndefined();
355 JSObject
* functionObject
= asGetterSetter(value
)->getter();
357 return jsUndefined();
358 return functionObject
;
361 if (!object
->prototype() || !object
->prototype().isObject())
362 return jsUndefined();
363 object
= asObject(object
->prototype());
367 JSValuePtr
JSObject::lookupSetter(ExecState
*, const Identifier
& propertyName
)
369 JSObject
* object
= this;
371 if (JSValuePtr value
= object
->getDirect(propertyName
)) {
372 if (!value
.isGetterSetter())
373 return jsUndefined();
374 JSObject
* functionObject
= asGetterSetter(value
)->setter();
376 return jsUndefined();
377 return functionObject
;
380 if (!object
->prototype() || !object
->prototype().isObject())
381 return jsUndefined();
382 object
= asObject(object
->prototype());
386 bool JSObject::hasInstance(ExecState
* exec
, JSValuePtr value
, JSValuePtr proto
)
388 if (!proto
.isObject()) {
389 throwError(exec
, TypeError
, "instanceof called on an object with an invalid prototype property.");
393 if (!value
.isObject())
396 JSObject
* object
= asObject(value
);
397 while ((object
= object
->prototype().getObject())) {
404 bool JSObject::propertyIsEnumerable(ExecState
* exec
, const Identifier
& propertyName
) const
407 if (!getPropertyAttributes(exec
, propertyName
, attributes
))
409 return !(attributes
& DontEnum
);
412 bool JSObject::getPropertyAttributes(ExecState
* exec
, const Identifier
& propertyName
, unsigned& attributes
) const
414 if (m_structure
->get(propertyName
, attributes
) != WTF::notFound
)
417 // Look in the static hashtable of properties
418 const HashEntry
* entry
= findPropertyHashEntry(exec
, propertyName
);
420 attributes
= entry
->attributes();
427 void JSObject::getPropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
)
429 m_structure
->getEnumerablePropertyNames(exec
, propertyNames
, this);
432 bool JSObject::toBoolean(ExecState
*) const
437 double JSObject::toNumber(ExecState
* exec
) const
439 JSValuePtr primitive
= toPrimitive(exec
, PreferNumber
);
440 if (exec
->hadException()) // should be picked up soon in Nodes.cpp
442 return primitive
.toNumber(exec
);
445 UString
JSObject::toString(ExecState
* exec
) const
447 JSValuePtr primitive
= toPrimitive(exec
, PreferString
);
448 if (exec
->hadException())
450 return primitive
.toString(exec
);
453 JSObject
* JSObject::toObject(ExecState
*) const
455 return const_cast<JSObject
*>(this);
458 JSObject
* JSObject::toThisObject(ExecState
*) const
460 return const_cast<JSObject
*>(this);
463 JSObject
* JSObject::unwrappedObject()
468 void JSObject::removeDirect(const Identifier
& propertyName
)
471 if (m_structure
->isDictionary()) {
472 offset
= m_structure
->removePropertyWithoutTransition(propertyName
);
473 if (offset
!= WTF::notFound
)
474 m_propertyStorage
[offset
] = jsUndefined();
478 RefPtr
<Structure
> structure
= Structure::removePropertyTransition(m_structure
, propertyName
, offset
);
479 if (offset
!= WTF::notFound
)
480 m_propertyStorage
[offset
] = jsUndefined();
481 setStructure(structure
.release());
484 void JSObject::putDirectFunction(ExecState
* exec
, InternalFunction
* function
, unsigned attr
)
486 putDirect(Identifier(exec
, function
->name(&exec
->globalData())), function
, attr
);
489 void JSObject::putDirectFunctionWithoutTransition(ExecState
* exec
, InternalFunction
* function
, unsigned attr
)
491 putDirectWithoutTransition(Identifier(exec
, function
->name(&exec
->globalData())), function
, attr
);
494 NEVER_INLINE
void JSObject::fillGetterPropertySlot(PropertySlot
& slot
, JSValuePtr
* location
)
496 if (JSObject
* getterFunction
= asGetterSetter(*location
)->getter())
497 slot
.setGetterSlot(getterFunction
);
502 Structure
* JSObject::createInheritorID()
504 m_inheritorID
= JSObject::createStructure(this);
505 return m_inheritorID
.get();
508 void JSObject::allocatePropertyStorage(size_t oldSize
, size_t newSize
)
510 allocatePropertyStorageInline(oldSize
, newSize
);
513 JSObject
* constructEmptyObject(ExecState
* exec
)
515 return new (exec
) JSObject(exec
->lexicalGlobalObject()->emptyObjectStructure());