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 <profiler/Profiler.h>
39 #include <wtf/Assertions.h>
41 #define JSOBJECT_MARK_TRACING 0
43 #if JSOBJECT_MARK_TRACING
45 #define JSOBJECT_MARK_BEGIN() \
46 static int markStackDepth = 0; \
47 for (int i = 0; i < markStackDepth; i++) \
49 printf("%s (%p)\n", className().UTF8String().c_str(), this); \
52 #define JSOBJECT_MARK_END() \
55 #else // JSOBJECT_MARK_TRACING
57 #define JSOBJECT_MARK_BEGIN()
58 #define JSOBJECT_MARK_END()
60 #endif // JSOBJECT_MARK_TRACING
64 ASSERT_CLASS_FITS_IN_CELL(JSObject);
68 JSOBJECT_MARK_BEGIN();
71 m_structureID->mark();
73 unsigned storageSize = m_structureID->propertyMap().storageSize();
74 for (unsigned i = 0; i < storageSize; ++i) {
75 JSValue* v = m_propertyStorage[i];
83 UString JSObject::className() const
85 const ClassInfo* info = classInfo();
87 return info->className;
91 bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
93 return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
96 static void throwSetterError(ExecState* exec)
98 throwError(exec, TypeError, "setting a property that has only a getter");
102 void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue* value, PutPropertySlot& slot)
105 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
107 if (propertyName == exec->propertyNames().underscoreProto) {
108 JSObject* proto = value->getObject();
110 // Setting __proto__ to a non-object, non-null value is silently ignored to match Mozilla.
111 if (!proto && !value->isNull())
116 throwError(exec, GeneralError, "cyclic __proto__ value");
119 proto = proto->prototype() ? proto->prototype()->getObject() : 0;
126 // Check if there are any setters or getters in the prototype chain
128 for (JSObject* obj = this; !obj->structureID()->hasGetterSetterProperties(); obj = static_cast<JSObject*>(prototype)) {
129 prototype = obj->prototype();
130 if (prototype->isNull()) {
131 putDirect(propertyName, value, 0, true, slot);
137 if ((m_structureID->propertyMap().get(propertyName, attributes) != WTF::notFound) && attributes & ReadOnly)
140 for (JSObject* obj = this; ; obj = static_cast<JSObject*>(prototype)) {
141 if (JSValue* gs = obj->getDirect(propertyName)) {
142 if (gs->isGetterSetter()) {
143 JSObject* setterFunc = static_cast<GetterSetter*>(gs)->setter();
145 throwSetterError(exec);
150 CallType callType = setterFunc->getCallData(callData);
153 call(exec, setterFunc, callType, callData, this, args);
157 // If there's an existing property on the object or one of its
158 // prototypes it should be replaced, so break here.
162 prototype = obj->prototype();
163 if (prototype->isNull())
167 putDirect(propertyName, value, 0, true, slot);
171 void JSObject::put(ExecState* exec, unsigned propertyName, JSValue* value)
173 PutPropertySlot slot;
174 put(exec, Identifier::from(exec, propertyName), value, slot);
177 void JSObject::putWithAttributes(ExecState*, const Identifier& propertyName, JSValue* value, unsigned attributes)
179 putDirect(propertyName, value, attributes);
182 void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue* value, unsigned attributes)
184 putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
187 bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const
190 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
193 bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
196 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
200 bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
203 if (m_structureID->propertyMap().get(propertyName, attributes) != WTF::notFound) {
204 if ((attributes & DontDelete))
206 removeDirect(propertyName);
210 // Look in the static hashtable of properties
211 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
212 if (entry && entry->attributes() & DontDelete)
213 return false; // this builtin property can't be deleted
215 // FIXME: Should the code here actually do some deletion?
219 bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
222 return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
225 bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName)
227 return deleteProperty(exec, Identifier::from(exec, propertyName));
230 static ALWAYS_INLINE JSValue* callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
232 JSValue* function = object->get(exec, propertyName);
234 CallType callType = function->getCallData(callData);
235 if (callType == CallTypeNone)
236 return exec->exception();
238 // Prevent "toString" and "valueOf" from observing execution if an exception
240 if (exec->hadException())
241 return exec->exception();
243 JSValue* result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
244 ASSERT(!result->isGetterSetter());
245 if (exec->hadException())
246 return exec->exception();
247 if (result->isObject())
252 bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue*& result)
254 result = defaultValue(exec, PreferNumber);
255 number = result->toNumber(exec);
256 return !result->isString();
260 JSValue* JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
262 // Must call toString first for Date objects.
263 if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) {
264 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().toString))
266 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf))
269 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf))
271 if (JSValue* value = callDefaultValueFunction(exec, this, exec->propertyNames().toString))
275 ASSERT(!exec->hadException());
277 return throwError(exec, TypeError, "No default value");
280 const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
282 for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
283 if (const HashTable* propHashTable = info->propHashTable(exec)) {
284 if (const HashEntry* entry = propHashTable->entry(exec, propertyName))
291 void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction)
293 JSValue* object = getDirect(propertyName);
294 if (object && object->isGetterSetter()) {
295 ASSERT(m_structureID->hasGetterSetterProperties());
296 GetterSetter* getterSetter = static_cast<GetterSetter*>(object);
297 getterSetter->setGetter(getterFunction);
301 PutPropertySlot slot;
302 GetterSetter* getterSetter = new (exec) GetterSetter;
303 putDirect(propertyName, getterSetter, None, true, slot);
305 // putDirect will change our StructureID if we add a new property. For
306 // getters and setters, though, we also need to change our StructureID
307 // if we override an existing non-getter or non-setter.
308 if (slot.type() != PutPropertySlot::NewProperty) {
309 if (!m_structureID->isDictionary()) {
310 RefPtr<StructureID> structureID = StructureID::getterSetterTransition(m_structureID);
311 setStructureID(structureID.release());
315 m_structureID->setHasGetterSetterProperties(true);
316 getterSetter->setGetter(getterFunction);
319 void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction)
321 JSValue* object = getDirect(propertyName);
322 if (object && object->isGetterSetter()) {
323 ASSERT(m_structureID->hasGetterSetterProperties());
324 GetterSetter* getterSetter = static_cast<GetterSetter*>(object);
325 getterSetter->setSetter(setterFunction);
329 PutPropertySlot slot;
330 GetterSetter* getterSetter = new (exec) GetterSetter;
331 putDirect(propertyName, getterSetter, None, true, slot);
333 // putDirect will change our StructureID if we add a new property. For
334 // getters and setters, though, we also need to change our StructureID
335 // if we override an existing non-getter or non-setter.
336 if (slot.type() != PutPropertySlot::NewProperty) {
337 if (!m_structureID->isDictionary()) {
338 RefPtr<StructureID> structureID = StructureID::getterSetterTransition(m_structureID);
339 setStructureID(structureID.release());
343 m_structureID->setHasGetterSetterProperties(true);
344 getterSetter->setSetter(setterFunction);
347 JSValue* JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
349 JSObject* object = this;
351 JSValue* value = object->getDirect(propertyName);
353 if (!value->isGetterSetter())
354 return jsUndefined();
355 JSObject* functionObject = static_cast<GetterSetter*>(value)->getter();
357 return jsUndefined();
358 return functionObject;
361 if (!object->prototype() || !object->prototype()->isObject())
362 return jsUndefined();
363 object = static_cast<JSObject*>(object->prototype());
367 JSValue* JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
369 JSObject* object = this;
371 JSValue* value = object->getDirect(propertyName);
373 if (!value->isGetterSetter())
374 return jsUndefined();
375 JSObject* functionObject = static_cast<GetterSetter*>(value)->setter();
377 return jsUndefined();
378 return functionObject;
381 if (!object->prototype() || !object->prototype()->isObject())
382 return jsUndefined();
383 object = static_cast<JSObject*>(object->prototype());
387 bool JSObject::hasInstance(ExecState* exec, JSValue* value, JSValue* proto)
389 if (!proto->isObject()) {
390 throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
394 if (!value->isObject())
397 JSObject* object = static_cast<JSObject*>(value);
398 while ((object = object->prototype()->getObject())) {
405 bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
408 if (!getPropertyAttributes(exec, propertyName, attributes))
410 return !(attributes & DontEnum);
413 bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
415 if (m_structureID->propertyMap().get(propertyName, attributes) != WTF::notFound)
418 // Look in the static hashtable of properties
419 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
421 attributes = entry->attributes();
428 void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
430 m_structureID->getEnumerablePropertyNames(exec, propertyNames, this);
433 bool JSObject::toBoolean(ExecState*) const
438 double JSObject::toNumber(ExecState* exec) const
440 JSValue* primitive = toPrimitive(exec, PreferNumber);
441 if (exec->hadException()) // should be picked up soon in nodes.cpp
443 return primitive->toNumber(exec);
446 UString JSObject::toString(ExecState* exec) const
448 JSValue* primitive = toPrimitive(exec, PreferString);
449 if (exec->hadException())
451 return primitive->toString(exec);
454 JSObject* JSObject::toObject(ExecState*) const
456 return const_cast<JSObject*>(this);
459 JSObject* JSObject::toThisObject(ExecState*) const
461 return const_cast<JSObject*>(this);
464 JSGlobalObject* JSObject::toGlobalObject(ExecState*) const
469 void JSObject::removeDirect(const Identifier& propertyName)
472 if (m_structureID->isDictionary()) {
473 offset = m_structureID->propertyMap().remove(propertyName);
474 if (offset != WTF::notFound) {
475 m_propertyStorage[offset] = jsUndefined();
476 m_structureID->clearEnumerationCache();
481 RefPtr<StructureID> structureID = StructureID::toDictionaryTransition(m_structureID);
482 offset = structureID->propertyMap().remove(propertyName);
483 if (offset != WTF::notFound)
484 m_propertyStorage[offset] = jsUndefined();
485 setStructureID(structureID.release());
488 void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
490 putDirect(Identifier(exec, function->name(&exec->globalData())), function, attr);
493 void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
495 putDirectWithoutTransition(Identifier(exec, function->name(&exec->globalData())), function, attr);
498 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue** location)
500 if (JSObject* getterFunction = static_cast<GetterSetter*>(*location)->getter())
501 slot.setGetterSlot(getterFunction);
506 StructureID* JSObject::createInheritorID()
508 m_inheritorID = JSObject::createStructureID(this);
509 return m_inheritorID.get();
512 void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
514 ASSERT(newSize > oldSize);
516 JSValue** oldPropertStorage = m_propertyStorage;
517 m_propertyStorage = new JSValue*[newSize];
519 for (unsigned i = 0; i < oldSize; ++i)
520 m_propertyStorage[i] = oldPropertStorage[i];
522 if (oldPropertStorage != m_inlineStorage)
523 delete [] oldPropertStorage;
526 JSObject* constructEmptyObject(ExecState* exec)
528 return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure());