]> git.saurik.com Git - apple/javascriptcore.git/blame - runtime/JSObject.cpp
JavaScriptCore-554.1.tar.gz
[apple/javascriptcore.git] / runtime / JSObject.cpp
CommitLineData
9dae56ea
A
1/*
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)
6 *
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.
11 *
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.
16 *
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.
21 *
22 */
23
24#include "config.h"
25#include "JSObject.h"
26
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"
34#include "Lookup.h"
35#include "Nodes.h"
36#include "Operations.h"
37#include <math.h>
38#include <wtf/Assertions.h>
39
40#define JSOBJECT_MARK_TRACING 0
41
42#if JSOBJECT_MARK_TRACING
43
44#define JSOBJECT_MARK_BEGIN() \
45 static int markStackDepth = 0; \
46 for (int i = 0; i < markStackDepth; i++) \
47 putchar('-'); \
48 printf("%s (%p)\n", className().UTF8String().c_str(), this); \
49 markStackDepth++; \
50
51#define JSOBJECT_MARK_END() \
52 markStackDepth--;
53
54#else // JSOBJECT_MARK_TRACING
55
56#define JSOBJECT_MARK_BEGIN()
57#define JSOBJECT_MARK_END()
58
59#endif // JSOBJECT_MARK_TRACING
60
61namespace JSC {
62
63ASSERT_CLASS_FITS_IN_CELL(JSObject);
64
65void JSObject::mark()
66{
67 JSOBJECT_MARK_BEGIN();
68
69 JSCell::mark();
70 m_structure->mark();
71
ba379fdc
A
72 PropertyStorage storage = propertyStorage();
73
9dae56ea
A
74 size_t storageSize = m_structure->propertyStorageSize();
75 for (size_t i = 0; i < storageSize; ++i) {
ba379fdc 76 JSValue v = JSValue::decode(storage[i]);
9dae56ea
A
77 if (!v.marked())
78 v.mark();
79 }
80
81 JSOBJECT_MARK_END();
82}
83
84UString JSObject::className() const
85{
86 const ClassInfo* info = classInfo();
87 if (info)
88 return info->className;
89 return "Object";
90}
91
92bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
93{
94 return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
95}
96
97static void throwSetterError(ExecState* exec)
98{
99 throwError(exec, TypeError, "setting a property that has only a getter");
100}
101
102// ECMA 8.6.2.2
ba379fdc 103void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
9dae56ea
A
104{
105 ASSERT(value);
106 ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
107
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())
111 return;
112
ba379fdc 113 JSValue nextPrototypeValue = value;
9dae56ea
A
114 while (nextPrototypeValue && nextPrototypeValue.isObject()) {
115 JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject();
116 if (nextPrototype == this) {
117 throwError(exec, GeneralError, "cyclic __proto__ value");
118 return;
119 }
120 nextPrototypeValue = nextPrototype->prototype();
121 }
122
123 setPrototype(value);
124 return;
125 }
126
127 // Check if there are any setters or getters in the prototype chain
ba379fdc 128 JSValue prototype;
9dae56ea
A
129 for (JSObject* obj = this; !obj->structure()->hasGetterSetterProperties(); obj = asObject(prototype)) {
130 prototype = obj->prototype();
131 if (prototype.isNull()) {
ba379fdc 132 putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot);
9dae56ea
A
133 return;
134 }
135 }
136
137 unsigned attributes;
ba379fdc
A
138 JSCell* specificValue;
139 if ((m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) && attributes & ReadOnly)
9dae56ea
A
140 return;
141
142 for (JSObject* obj = this; ; obj = asObject(prototype)) {
ba379fdc 143 if (JSValue gs = obj->getDirect(propertyName)) {
9dae56ea
A
144 if (gs.isGetterSetter()) {
145 JSObject* setterFunc = asGetterSetter(gs)->setter();
146 if (!setterFunc) {
147 throwSetterError(exec);
148 return;
149 }
150
151 CallData callData;
152 CallType callType = setterFunc->getCallData(callData);
ba379fdc 153 MarkedArgumentBuffer args;
9dae56ea
A
154 args.append(value);
155 call(exec, setterFunc, callType, callData, this, args);
156 return;
157 }
158
159 // If there's an existing property on the object or one of its
160 // prototypes it should be replaced, so break here.
161 break;
162 }
163
164 prototype = obj->prototype();
165 if (prototype.isNull())
166 break;
167 }
168
ba379fdc 169 putDirectInternal(exec->globalData(), propertyName, value, 0, true, slot);
9dae56ea
A
170 return;
171}
172
ba379fdc 173void JSObject::put(ExecState* exec, unsigned propertyName, JSValue value)
9dae56ea
A
174{
175 PutPropertySlot slot;
176 put(exec, Identifier::from(exec, propertyName), value, slot);
177}
178
ba379fdc 179void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
9dae56ea 180{
ba379fdc 181 putDirectInternal(exec->globalData(), propertyName, value, attributes, checkReadOnly, slot);
9dae56ea
A
182}
183
ba379fdc
A
184void JSObject::putWithAttributes(ExecState* exec, const Identifier& propertyName, JSValue value, unsigned attributes)
185{
186 putDirectInternal(exec->globalData(), propertyName, value, attributes);
187}
188
189void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes)
9dae56ea
A
190{
191 putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
192}
193
194bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const
195{
196 PropertySlot slot;
197 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
198}
199
200bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
201{
202 PropertySlot slot;
203 return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
204}
205
206// ECMA 8.6.2.5
207bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
208{
209 unsigned attributes;
ba379fdc
A
210 JSCell* specificValue;
211 if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound) {
9dae56ea
A
212 if ((attributes & DontDelete))
213 return false;
214 removeDirect(propertyName);
215 return true;
216 }
217
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
222
223 // FIXME: Should the code here actually do some deletion?
224 return true;
225}
226
227bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
228{
229 PropertySlot slot;
230 return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
231}
232
233bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName)
234{
235 return deleteProperty(exec, Identifier::from(exec, propertyName));
236}
237
ba379fdc 238static ALWAYS_INLINE JSValue callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
9dae56ea 239{
ba379fdc 240 JSValue function = object->get(exec, propertyName);
9dae56ea
A
241 CallData callData;
242 CallType callType = function.getCallData(callData);
243 if (callType == CallTypeNone)
244 return exec->exception();
245
246 // Prevent "toString" and "valueOf" from observing execution if an exception
247 // is pending.
248 if (exec->hadException())
249 return exec->exception();
250
ba379fdc 251 JSValue result = call(exec, function, callType, callData, const_cast<JSObject*>(object), exec->emptyList());
9dae56ea
A
252 ASSERT(!result.isGetterSetter());
253 if (exec->hadException())
254 return exec->exception();
255 if (result.isObject())
ba379fdc 256 return JSValue();
9dae56ea
A
257 return result;
258}
259
ba379fdc 260bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValue& result)
9dae56ea
A
261{
262 result = defaultValue(exec, PreferNumber);
263 number = result.toNumber(exec);
264 return !result.isString();
265}
266
267// ECMA 8.6.2.6
ba379fdc 268JSValue JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
9dae56ea
A
269{
270 // Must call toString first for Date objects.
271 if ((hint == PreferString) || (hint != PreferNumber && prototype() == exec->lexicalGlobalObject()->datePrototype())) {
ba379fdc 272 JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
9dae56ea
A
273 if (value)
274 return value;
275 value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
276 if (value)
277 return value;
278 } else {
ba379fdc 279 JSValue value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
9dae56ea
A
280 if (value)
281 return value;
282 value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
283 if (value)
284 return value;
285 }
286
287 ASSERT(!exec->hadException());
288
289 return throwError(exec, TypeError, "No default value");
290}
291
292const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
293{
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))
297 return entry;
298 }
299 }
300 return 0;
301}
302
303void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction)
304{
ba379fdc 305 JSValue object = getDirect(propertyName);
9dae56ea
A
306 if (object && object.isGetterSetter()) {
307 ASSERT(m_structure->hasGetterSetterProperties());
308 asGetterSetter(object)->setGetter(getterFunction);
309 return;
310 }
311
312 PutPropertySlot slot;
313 GetterSetter* getterSetter = new (exec) GetterSetter;
ba379fdc 314 putDirectInternal(exec->globalData(), propertyName, getterSetter, Getter, true, slot);
9dae56ea
A
315
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());
323 }
324 }
325
326 m_structure->setHasGetterSetterProperties(true);
327 getterSetter->setGetter(getterFunction);
328}
329
330void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction)
331{
ba379fdc 332 JSValue object = getDirect(propertyName);
9dae56ea
A
333 if (object && object.isGetterSetter()) {
334 ASSERT(m_structure->hasGetterSetterProperties());
335 asGetterSetter(object)->setSetter(setterFunction);
336 return;
337 }
338
339 PutPropertySlot slot;
340 GetterSetter* getterSetter = new (exec) GetterSetter;
ba379fdc 341 putDirectInternal(exec->globalData(), propertyName, getterSetter, Setter, true, slot);
9dae56ea
A
342
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());
350 }
351 }
352
353 m_structure->setHasGetterSetterProperties(true);
354 getterSetter->setSetter(setterFunction);
355}
356
ba379fdc 357JSValue JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
9dae56ea
A
358{
359 JSObject* object = this;
360 while (true) {
ba379fdc 361 if (JSValue value = object->getDirect(propertyName)) {
9dae56ea
A
362 if (!value.isGetterSetter())
363 return jsUndefined();
364 JSObject* functionObject = asGetterSetter(value)->getter();
365 if (!functionObject)
366 return jsUndefined();
367 return functionObject;
368 }
369
370 if (!object->prototype() || !object->prototype().isObject())
371 return jsUndefined();
372 object = asObject(object->prototype());
373 }
374}
375
ba379fdc 376JSValue JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
9dae56ea
A
377{
378 JSObject* object = this;
379 while (true) {
ba379fdc 380 if (JSValue value = object->getDirect(propertyName)) {
9dae56ea
A
381 if (!value.isGetterSetter())
382 return jsUndefined();
383 JSObject* functionObject = asGetterSetter(value)->setter();
384 if (!functionObject)
385 return jsUndefined();
386 return functionObject;
387 }
388
389 if (!object->prototype() || !object->prototype().isObject())
390 return jsUndefined();
391 object = asObject(object->prototype());
392 }
393}
394
ba379fdc 395bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue proto)
9dae56ea 396{
ba379fdc
A
397 if (!value.isObject())
398 return false;
399
9dae56ea
A
400 if (!proto.isObject()) {
401 throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
402 return false;
403 }
404
9dae56ea
A
405 JSObject* object = asObject(value);
406 while ((object = object->prototype().getObject())) {
407 if (proto == object)
408 return true;
409 }
410 return false;
411}
412
413bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
414{
415 unsigned attributes;
416 if (!getPropertyAttributes(exec, propertyName, attributes))
417 return false;
418 return !(attributes & DontEnum);
419}
420
421bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
422{
ba379fdc
A
423 JSCell* specificValue;
424 if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound)
9dae56ea
A
425 return true;
426
427 // Look in the static hashtable of properties
428 const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
429 if (entry) {
430 attributes = entry->attributes();
431 return true;
432 }
433
434 return false;
435}
436
ba379fdc
A
437bool JSObject::getPropertySpecificValue(ExecState*, const Identifier& propertyName, JSCell*& specificValue) const
438{
439 unsigned attributes;
440 if (m_structure->get(propertyName, attributes, specificValue) != WTF::notFound)
441 return true;
442
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.
447 return false;
448}
449
9dae56ea
A
450void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
451{
452 m_structure->getEnumerablePropertyNames(exec, propertyNames, this);
453}
454
455bool JSObject::toBoolean(ExecState*) const
456{
457 return true;
458}
459
460double JSObject::toNumber(ExecState* exec) const
461{
ba379fdc 462 JSValue primitive = toPrimitive(exec, PreferNumber);
9dae56ea
A
463 if (exec->hadException()) // should be picked up soon in Nodes.cpp
464 return 0.0;
465 return primitive.toNumber(exec);
466}
467
468UString JSObject::toString(ExecState* exec) const
469{
ba379fdc 470 JSValue primitive = toPrimitive(exec, PreferString);
9dae56ea
A
471 if (exec->hadException())
472 return "";
473 return primitive.toString(exec);
474}
475
476JSObject* JSObject::toObject(ExecState*) const
477{
478 return const_cast<JSObject*>(this);
479}
480
481JSObject* JSObject::toThisObject(ExecState*) const
482{
483 return const_cast<JSObject*>(this);
484}
485
486JSObject* JSObject::unwrappedObject()
487{
488 return this;
489}
490
491void JSObject::removeDirect(const Identifier& propertyName)
492{
493 size_t offset;
ba379fdc 494 if (m_structure->isUncacheableDictionary()) {
9dae56ea
A
495 offset = m_structure->removePropertyWithoutTransition(propertyName);
496 if (offset != WTF::notFound)
ba379fdc 497 putDirectOffset(offset, jsUndefined());
9dae56ea
A
498 return;
499 }
500
501 RefPtr<Structure> structure = Structure::removePropertyTransition(m_structure, propertyName, offset);
9dae56ea 502 setStructure(structure.release());
ba379fdc
A
503 if (offset != WTF::notFound)
504 putDirectOffset(offset, jsUndefined());
9dae56ea
A
505}
506
507void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
508{
ba379fdc 509 putDirectFunction(Identifier(exec, function->name(&exec->globalData())), function, attr);
9dae56ea
A
510}
511
512void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
513{
ba379fdc 514 putDirectFunctionWithoutTransition(Identifier(exec, function->name(&exec->globalData())), function, attr);
9dae56ea
A
515}
516
ba379fdc 517NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValue* location)
9dae56ea
A
518{
519 if (JSObject* getterFunction = asGetterSetter(*location)->getter())
520 slot.setGetterSlot(getterFunction);
521 else
522 slot.setUndefined();
523}
524
525Structure* JSObject::createInheritorID()
526{
527 m_inheritorID = JSObject::createStructure(this);
528 return m_inheritorID.get();
529}
530
531void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
532{
533 allocatePropertyStorageInline(oldSize, newSize);
534}
535
536JSObject* constructEmptyObject(ExecState* exec)
537{
538 return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure());
539}
540
541} // namespace JSC