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 PropertyStorage storage
= propertyStorage();
74 size_t storageSize
= m_structure
->propertyStorageSize();
75 for (size_t i
= 0; i
< storageSize
; ++i
) {
76 JSValue v
= JSValue::decode(storage
[i
]);
84 UString
JSObject::className() const
86 const ClassInfo
* info
= classInfo();
88 return info
->className
;
92 bool JSObject::getOwnPropertySlot(ExecState
* exec
, unsigned propertyName
, PropertySlot
& slot
)
94 return getOwnPropertySlot(exec
, Identifier::from(exec
, propertyName
), slot
);
97 static void throwSetterError(ExecState
* exec
)
99 throwError(exec
, TypeError
, "setting a property that has only a getter");
103 void JSObject::put(ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, PutPropertySlot
& slot
)
106 ASSERT(!Heap::heap(value
) || Heap::heap(value
) == Heap::heap(this));
108 if (propertyName
== exec
->propertyNames().underscoreProto
) {
109 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
110 if (!value
.isObject() && !value
.isNull())
113 JSValue nextPrototypeValue
= value
;
114 while (nextPrototypeValue
&& nextPrototypeValue
.isObject()) {
115 JSObject
* nextPrototype
= asObject(nextPrototypeValue
)->unwrappedObject();
116 if (nextPrototype
== this) {
117 throwError(exec
, GeneralError
, "cyclic __proto__ value");
120 nextPrototypeValue
= nextPrototype
->prototype();
127 // Check if there are any setters or getters in the prototype chain
129 for (JSObject
* obj
= this; !obj
->structure()->hasGetterSetterProperties(); obj
= asObject(prototype
)) {
130 prototype
= obj
->prototype();
131 if (prototype
.isNull()) {
132 putDirectInternal(exec
->globalData(), propertyName
, value
, 0, true, slot
);
138 JSCell
* specificValue
;
139 if ((m_structure
->get(propertyName
, attributes
, specificValue
) != WTF::notFound
) && attributes
& ReadOnly
)
142 for (JSObject
* obj
= this; ; obj
= asObject(prototype
)) {
143 if (JSValue gs
= obj
->getDirect(propertyName
)) {
144 if (gs
.isGetterSetter()) {
145 JSObject
* setterFunc
= asGetterSetter(gs
)->setter();
147 throwSetterError(exec
);
152 CallType callType
= setterFunc
->getCallData(callData
);
153 MarkedArgumentBuffer args
;
155 call(exec
, setterFunc
, callType
, callData
, this, args
);
159 // If there's an existing property on the object or one of its
160 // prototypes it should be replaced, so break here.
164 prototype
= obj
->prototype();
165 if (prototype
.isNull())
169 putDirectInternal(exec
->globalData(), propertyName
, value
, 0, true, slot
);
173 void JSObject::put(ExecState
* exec
, unsigned propertyName
, JSValue value
)
175 PutPropertySlot slot
;
176 put(exec
, Identifier::from(exec
, propertyName
), value
, slot
);
179 void JSObject::putWithAttributes(ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, unsigned attributes
, bool checkReadOnly
, PutPropertySlot
& slot
)
181 putDirectInternal(exec
->globalData(), propertyName
, value
, attributes
, checkReadOnly
, slot
);
184 void JSObject::putWithAttributes(ExecState
* exec
, const Identifier
& propertyName
, JSValue value
, unsigned attributes
)
186 putDirectInternal(exec
->globalData(), propertyName
, value
, attributes
);
189 void JSObject::putWithAttributes(ExecState
* exec
, unsigned propertyName
, JSValue value
, unsigned attributes
)
191 putWithAttributes(exec
, Identifier::from(exec
, propertyName
), value
, attributes
);
194 bool JSObject::hasProperty(ExecState
* exec
, const Identifier
& propertyName
) const
197 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
200 bool JSObject::hasProperty(ExecState
* exec
, unsigned propertyName
) const
203 return const_cast<JSObject
*>(this)->getPropertySlot(exec
, propertyName
, slot
);
207 bool JSObject::deleteProperty(ExecState
* exec
, const Identifier
& propertyName
)
210 JSCell
* specificValue
;
211 if (m_structure
->get(propertyName
, attributes
, specificValue
) != WTF::notFound
) {
212 if ((attributes
& DontDelete
))
214 removeDirect(propertyName
);
218 // Look in the static hashtable of properties
219 const HashEntry
* entry
= findPropertyHashEntry(exec
, propertyName
);
220 if (entry
&& entry
->attributes() & DontDelete
)
221 return false; // this builtin property can't be deleted
223 // FIXME: Should the code here actually do some deletion?
227 bool JSObject::hasOwnProperty(ExecState
* exec
, const Identifier
& propertyName
) const
230 return const_cast<JSObject
*>(this)->getOwnPropertySlot(exec
, propertyName
, slot
);
233 bool JSObject::deleteProperty(ExecState
* exec
, unsigned propertyName
)
235 return deleteProperty(exec
, Identifier::from(exec
, propertyName
));
238 static ALWAYS_INLINE JSValue
callDefaultValueFunction(ExecState
* exec
, const JSObject
* object
, const Identifier
& propertyName
)
240 JSValue function
= object
->get(exec
, propertyName
);
242 CallType callType
= function
.getCallData(callData
);
243 if (callType
== CallTypeNone
)
244 return exec
->exception();
246 // Prevent "toString" and "valueOf" from observing execution if an exception
248 if (exec
->hadException())
249 return exec
->exception();
251 JSValue result
= call(exec
, function
, callType
, callData
, const_cast<JSObject
*>(object
), exec
->emptyList());
252 ASSERT(!result
.isGetterSetter());
253 if (exec
->hadException())
254 return exec
->exception();
255 if (result
.isObject())
260 bool JSObject::getPrimitiveNumber(ExecState
* exec
, double& number
, JSValue
& result
)
262 result
= defaultValue(exec
, PreferNumber
);
263 number
= result
.toNumber(exec
);
264 return !result
.isString();
268 JSValue
JSObject::defaultValue(ExecState
* exec
, PreferredPrimitiveType hint
) const
270 // Must call toString first for Date objects.
271 if ((hint
== PreferString
) || (hint
!= PreferNumber
&& prototype() == exec
->lexicalGlobalObject()->datePrototype())) {
272 JSValue value
= callDefaultValueFunction(exec
, this, exec
->propertyNames().toString
);
275 value
= callDefaultValueFunction(exec
, this, exec
->propertyNames().valueOf
);
279 JSValue value
= callDefaultValueFunction(exec
, this, exec
->propertyNames().valueOf
);
282 value
= callDefaultValueFunction(exec
, this, exec
->propertyNames().toString
);
287 ASSERT(!exec
->hadException());
289 return throwError(exec
, TypeError
, "No default value");
292 const HashEntry
* JSObject::findPropertyHashEntry(ExecState
* exec
, const Identifier
& propertyName
) const
294 for (const ClassInfo
* info
= classInfo(); info
; info
= info
->parentClass
) {
295 if (const HashTable
* propHashTable
= info
->propHashTable(exec
)) {
296 if (const HashEntry
* entry
= propHashTable
->entry(exec
, propertyName
))
303 void JSObject::defineGetter(ExecState
* exec
, const Identifier
& propertyName
, JSObject
* getterFunction
)
305 JSValue object
= getDirect(propertyName
);
306 if (object
&& object
.isGetterSetter()) {
307 ASSERT(m_structure
->hasGetterSetterProperties());
308 asGetterSetter(object
)->setGetter(getterFunction
);
312 PutPropertySlot slot
;
313 GetterSetter
* getterSetter
= new (exec
) GetterSetter
;
314 putDirectInternal(exec
->globalData(), propertyName
, getterSetter
, Getter
, true, slot
);
316 // putDirect will change our Structure if we add a new property. For
317 // getters and setters, though, we also need to change our Structure
318 // if we override an existing non-getter or non-setter.
319 if (slot
.type() != PutPropertySlot::NewProperty
) {
320 if (!m_structure
->isDictionary()) {
321 RefPtr
<Structure
> structure
= Structure::getterSetterTransition(m_structure
);
322 setStructure(structure
.release());
326 m_structure
->setHasGetterSetterProperties(true);
327 getterSetter
->setGetter(getterFunction
);
330 void JSObject::defineSetter(ExecState
* exec
, const Identifier
& propertyName
, JSObject
* setterFunction
)
332 JSValue object
= getDirect(propertyName
);
333 if (object
&& object
.isGetterSetter()) {
334 ASSERT(m_structure
->hasGetterSetterProperties());
335 asGetterSetter(object
)->setSetter(setterFunction
);
339 PutPropertySlot slot
;
340 GetterSetter
* getterSetter
= new (exec
) GetterSetter
;
341 putDirectInternal(exec
->globalData(), propertyName
, getterSetter
, Setter
, true, slot
);
343 // putDirect will change our Structure if we add a new property. For
344 // getters and setters, though, we also need to change our Structure
345 // if we override an existing non-getter or non-setter.
346 if (slot
.type() != PutPropertySlot::NewProperty
) {
347 if (!m_structure
->isDictionary()) {
348 RefPtr
<Structure
> structure
= Structure::getterSetterTransition(m_structure
);
349 setStructure(structure
.release());
353 m_structure
->setHasGetterSetterProperties(true);
354 getterSetter
->setSetter(setterFunction
);
357 JSValue
JSObject::lookupGetter(ExecState
*, const Identifier
& propertyName
)
359 JSObject
* object
= this;
361 if (JSValue value
= object
->getDirect(propertyName
)) {
362 if (!value
.isGetterSetter())
363 return jsUndefined();
364 JSObject
* functionObject
= asGetterSetter(value
)->getter();
366 return jsUndefined();
367 return functionObject
;
370 if (!object
->prototype() || !object
->prototype().isObject())
371 return jsUndefined();
372 object
= asObject(object
->prototype());
376 JSValue
JSObject::lookupSetter(ExecState
*, const Identifier
& propertyName
)
378 JSObject
* object
= this;
380 if (JSValue value
= object
->getDirect(propertyName
)) {
381 if (!value
.isGetterSetter())
382 return jsUndefined();
383 JSObject
* functionObject
= asGetterSetter(value
)->setter();
385 return jsUndefined();
386 return functionObject
;
389 if (!object
->prototype() || !object
->prototype().isObject())
390 return jsUndefined();
391 object
= asObject(object
->prototype());
395 bool JSObject::hasInstance(ExecState
* exec
, JSValue value
, JSValue proto
)
397 if (!value
.isObject())
400 if (!proto
.isObject()) {
401 throwError(exec
, TypeError
, "instanceof called on an object with an invalid prototype property.");
405 JSObject
* object
= asObject(value
);
406 while ((object
= object
->prototype().getObject())) {
413 bool JSObject::propertyIsEnumerable(ExecState
* exec
, const Identifier
& propertyName
) const
416 if (!getPropertyAttributes(exec
, propertyName
, attributes
))
418 return !(attributes
& DontEnum
);
421 bool JSObject::getPropertyAttributes(ExecState
* exec
, const Identifier
& propertyName
, unsigned& attributes
) const
423 JSCell
* specificValue
;
424 if (m_structure
->get(propertyName
, attributes
, specificValue
) != WTF::notFound
)
427 // Look in the static hashtable of properties
428 const HashEntry
* entry
= findPropertyHashEntry(exec
, propertyName
);
430 attributes
= entry
->attributes();
437 bool JSObject::getPropertySpecificValue(ExecState
*, const Identifier
& propertyName
, JSCell
*& specificValue
) const
440 if (m_structure
->get(propertyName
, attributes
, specificValue
) != WTF::notFound
)
443 // This could be a function within the static table? - should probably
444 // also look in the hash? This currently should not be a problem, since
445 // we've currently always call 'get' first, which should have populated
446 // the normal storage.
450 void JSObject::getPropertyNames(ExecState
* exec
, PropertyNameArray
& propertyNames
)
452 m_structure
->getEnumerablePropertyNames(exec
, propertyNames
, this);
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
->globalData())), function
, attr
);
512 void JSObject::putDirectFunctionWithoutTransition(ExecState
* exec
, InternalFunction
* function
, unsigned attr
)
514 putDirectFunctionWithoutTransition(Identifier(exec
, function
->name(&exec
->globalData())), function
, attr
);
517 NEVER_INLINE
void JSObject::fillGetterPropertySlot(PropertySlot
& slot
, JSValue
* location
)
519 if (JSObject
* getterFunction
= asGetterSetter(*location
)->getter())
520 slot
.setGetterSlot(getterFunction
);
525 Structure
* JSObject::createInheritorID()
527 m_inheritorID
= JSObject::createStructure(this);
528 return m_inheritorID
.get();
531 void JSObject::allocatePropertyStorage(size_t oldSize
, size_t newSize
)
533 allocatePropertyStorageInline(oldSize
, newSize
);
536 JSObject
* constructEmptyObject(ExecState
* exec
)
538 return new (exec
) JSObject(exec
->lexicalGlobalObject()->emptyObjectStructure());