d38c325c87fce7ed01a1635b212959a6212ab67b
[WebKit-https.git] / JavaScriptCore / runtime / JSObject.cpp
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
61 namespace JSC {
62
63 ASSERT_CLASS_FITS_IN_CELL(JSObject);
64
65 void JSObject::mark()
66 {
67     JSOBJECT_MARK_BEGIN();
68
69     JSCell::mark();
70     m_structure->mark();
71
72     size_t storageSize = m_structure->propertyStorageSize();
73     for (size_t i = 0; i < storageSize; ++i) {
74         JSValuePtr v = m_propertyStorage[i];
75         if (!v->marked())
76             v->mark();
77     }
78
79     JSOBJECT_MARK_END();
80 }
81
82 UString JSObject::className() const
83 {
84     const ClassInfo* info = classInfo();
85     if (info)
86         return info->className;
87     return "Object";
88 }
89
90 bool JSObject::getOwnPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
91 {
92     return getOwnPropertySlot(exec, Identifier::from(exec, propertyName), slot);
93 }
94
95 static void throwSetterError(ExecState* exec)
96 {
97     throwError(exec, TypeError, "setting a property that has only a getter");
98 }
99
100 // ECMA 8.6.2.2
101 void JSObject::put(ExecState* exec, const Identifier& propertyName, JSValuePtr value, PutPropertySlot& slot)
102 {
103     ASSERT(value);
104     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
105
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())
109             return;
110
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");
116                 return;
117             }
118             nextPrototypeValue = nextPrototype->prototype();
119         }
120
121         setPrototype(value);
122         return;
123     }
124
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);
131             return;
132         }
133     }
134     
135     unsigned attributes;
136     if ((m_structure->get(propertyName, attributes) != WTF::notFound) && attributes & ReadOnly)
137         return;
138
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();        
143                 if (!setterFunc) {
144                     throwSetterError(exec);
145                     return;
146                 }
147                 
148                 CallData callData;
149                 CallType callType = setterFunc->getCallData(callData);
150                 ArgList args;
151                 args.append(value);
152                 call(exec, setterFunc, callType, callData, this, args);
153                 return;
154             }
155
156             // If there's an existing property on the object or one of its 
157             // prototypes it should be replaced, so break here.
158             break;
159         }
160
161         prototype = obj->prototype();
162         if (prototype->isNull())
163             break;
164     }
165
166     putDirect(propertyName, value, 0, true, slot);
167     return;
168 }
169
170 void JSObject::put(ExecState* exec, unsigned propertyName, JSValuePtr value)
171 {
172     PutPropertySlot slot;
173     put(exec, Identifier::from(exec, propertyName), value, slot);
174 }
175
176 void JSObject::putWithAttributes(ExecState*, const Identifier& propertyName, JSValuePtr value, unsigned attributes)
177 {
178     putDirect(propertyName, value, attributes);
179 }
180
181 void JSObject::putWithAttributes(ExecState* exec, unsigned propertyName, JSValuePtr value, unsigned attributes)
182 {
183     putWithAttributes(exec, Identifier::from(exec, propertyName), value, attributes);
184 }
185
186 bool JSObject::hasProperty(ExecState* exec, const Identifier& propertyName) const
187 {
188     PropertySlot slot;
189     return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
190 }
191
192 bool JSObject::hasProperty(ExecState* exec, unsigned propertyName) const
193 {
194     PropertySlot slot;
195     return const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot);
196 }
197
198 // ECMA 8.6.2.5
199 bool JSObject::deleteProperty(ExecState* exec, const Identifier& propertyName)
200 {
201     unsigned attributes;
202     if (m_structure->get(propertyName, attributes) != WTF::notFound) {
203         if ((attributes & DontDelete))
204             return false;
205         removeDirect(propertyName);
206         return true;
207     }
208
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
213
214     // FIXME: Should the code here actually do some deletion?
215     return true;
216 }
217
218 bool JSObject::hasOwnProperty(ExecState* exec, const Identifier& propertyName) const
219 {
220     PropertySlot slot;
221     return const_cast<JSObject*>(this)->getOwnPropertySlot(exec, propertyName, slot);
222 }
223
224 bool JSObject::deleteProperty(ExecState* exec, unsigned propertyName)
225 {
226     return deleteProperty(exec, Identifier::from(exec, propertyName));
227 }
228
229 static ALWAYS_INLINE JSValuePtr callDefaultValueFunction(ExecState* exec, const JSObject* object, const Identifier& propertyName)
230 {
231     JSValuePtr function = object->get(exec, propertyName);
232     CallData callData;
233     CallType callType = function->getCallData(callData);
234     if (callType == CallTypeNone)
235         return exec->exception();
236
237     // Prevent "toString" and "valueOf" from observing execution if an exception
238     // is pending.
239     if (exec->hadException())
240         return exec->exception();
241
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())
247         return noValue();
248     return result;
249 }
250
251 bool JSObject::getPrimitiveNumber(ExecState* exec, double& number, JSValuePtr& result)
252 {
253     result = defaultValue(exec, PreferNumber);
254     number = result->toNumber(exec);
255     return !result->isString();
256 }
257
258 // ECMA 8.6.2.6
259 JSValuePtr JSObject::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
260 {
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);
264         if (value)
265             return value;
266         value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
267         if (value)
268             return value;
269     } else {
270         JSValuePtr value = callDefaultValueFunction(exec, this, exec->propertyNames().valueOf);
271         if (value)
272             return value;
273         value = callDefaultValueFunction(exec, this, exec->propertyNames().toString);
274         if (value)
275             return value;
276     }
277
278     ASSERT(!exec->hadException());
279
280     return throwError(exec, TypeError, "No default value");
281 }
282
283 const HashEntry* JSObject::findPropertyHashEntry(ExecState* exec, const Identifier& propertyName) const
284 {
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))
288                 return entry;
289         }
290     }
291     return 0;
292 }
293
294 void JSObject::defineGetter(ExecState* exec, const Identifier& propertyName, JSObject* getterFunction)
295 {
296     JSValuePtr object = getDirect(propertyName);
297     if (object && object->isGetterSetter()) {
298         ASSERT(m_structure->hasGetterSetterProperties());
299         asGetterSetter(object)->setGetter(getterFunction);
300         return;
301     }
302
303     PutPropertySlot slot;
304     GetterSetter* getterSetter = new (exec) GetterSetter;
305     putDirect(propertyName, getterSetter, None, true, slot);
306
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());
314         }
315     }
316
317     m_structure->setHasGetterSetterProperties(true);
318     getterSetter->setGetter(getterFunction);
319 }
320
321 void JSObject::defineSetter(ExecState* exec, const Identifier& propertyName, JSObject* setterFunction)
322 {
323     JSValuePtr object = getDirect(propertyName);
324     if (object && object->isGetterSetter()) {
325         ASSERT(m_structure->hasGetterSetterProperties());
326         asGetterSetter(object)->setSetter(setterFunction);
327         return;
328     }
329
330     PutPropertySlot slot;
331     GetterSetter* getterSetter = new (exec) GetterSetter;
332     putDirect(propertyName, getterSetter, None, true, slot);
333
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());
341         }
342     }
343
344     m_structure->setHasGetterSetterProperties(true);
345     getterSetter->setSetter(setterFunction);
346 }
347
348 JSValuePtr JSObject::lookupGetter(ExecState*, const Identifier& propertyName)
349 {
350     JSObject* object = this;
351     while (true) {
352         if (JSValuePtr value = object->getDirect(propertyName)) {
353             if (!value->isGetterSetter())
354                 return jsUndefined();
355             JSObject* functionObject = asGetterSetter(value)->getter();
356             if (!functionObject)
357                 return jsUndefined();
358             return functionObject;
359         }
360
361         if (!object->prototype() || !object->prototype()->isObject())
362             return jsUndefined();
363         object = asObject(object->prototype());
364     }
365 }
366
367 JSValuePtr JSObject::lookupSetter(ExecState*, const Identifier& propertyName)
368 {
369     JSObject* object = this;
370     while (true) {
371         if (JSValuePtr value = object->getDirect(propertyName)) {
372             if (!value->isGetterSetter())
373                 return jsUndefined();
374             JSObject* functionObject = asGetterSetter(value)->setter();
375             if (!functionObject)
376                 return jsUndefined();
377             return functionObject;
378         }
379
380         if (!object->prototype() || !object->prototype()->isObject())
381             return jsUndefined();
382         object = asObject(object->prototype());
383     }
384 }
385
386 bool JSObject::hasInstance(ExecState* exec, JSValuePtr value, JSValuePtr proto)
387 {
388     if (!proto->isObject()) {
389         throwError(exec, TypeError, "instanceof called on an object with an invalid prototype property.");
390         return false;
391     }
392
393     if (!value->isObject())
394         return false;
395
396     JSObject* object = asObject(value);
397     while ((object = object->prototype()->getObject())) {
398         if (proto == object)
399             return true;
400     }
401     return false;
402 }
403
404 bool JSObject::propertyIsEnumerable(ExecState* exec, const Identifier& propertyName) const
405 {
406     unsigned attributes;
407     if (!getPropertyAttributes(exec, propertyName, attributes))
408         return false;
409     return !(attributes & DontEnum);
410 }
411
412 bool JSObject::getPropertyAttributes(ExecState* exec, const Identifier& propertyName, unsigned& attributes) const
413 {
414     if (m_structure->get(propertyName, attributes) != WTF::notFound)
415         return true;
416     
417     // Look in the static hashtable of properties
418     const HashEntry* entry = findPropertyHashEntry(exec, propertyName);
419     if (entry) {
420         attributes = entry->attributes();
421         return true;
422     }
423     
424     return false;
425 }
426
427 void JSObject::getPropertyNames(ExecState* exec, PropertyNameArray& propertyNames)
428 {
429     m_structure->getEnumerablePropertyNames(exec, propertyNames, this);
430 }
431
432 bool JSObject::toBoolean(ExecState*) const
433 {
434     return true;
435 }
436
437 double JSObject::toNumber(ExecState* exec) const
438 {
439     JSValuePtr primitive = toPrimitive(exec, PreferNumber);
440     if (exec->hadException()) // should be picked up soon in Nodes.cpp
441         return 0.0;
442     return primitive->toNumber(exec);
443 }
444
445 UString JSObject::toString(ExecState* exec) const
446 {
447     JSValuePtr primitive = toPrimitive(exec, PreferString);
448     if (exec->hadException())
449         return "";
450     return primitive->toString(exec);
451 }
452
453 JSObject* JSObject::toObject(ExecState*) const
454 {
455     return const_cast<JSObject*>(this);
456 }
457
458 JSObject* JSObject::toThisObject(ExecState*) const
459 {
460     return const_cast<JSObject*>(this);
461 }
462
463 JSObject* JSObject::unwrappedObject()
464 {
465     return this;
466 }
467
468 void JSObject::removeDirect(const Identifier& propertyName)
469 {
470     size_t offset;
471     if (m_structure->isDictionary()) {
472         offset = m_structure->removePropertyWithoutTransition(propertyName);
473         if (offset != WTF::notFound)
474             m_propertyStorage[offset] = jsUndefined();
475         return;
476     }
477
478     RefPtr<Structure> structure = Structure::removePropertyTransition(m_structure, propertyName, offset);
479     if (offset != WTF::notFound)
480         m_propertyStorage[offset] = jsUndefined();
481     setStructure(structure.release());
482 }
483
484 void JSObject::putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr)
485 {
486     putDirect(Identifier(exec, function->name(&exec->globalData())), function, attr);
487 }
488
489 void JSObject::putDirectFunctionWithoutTransition(ExecState* exec, InternalFunction* function, unsigned attr)
490 {
491     putDirectWithoutTransition(Identifier(exec, function->name(&exec->globalData())), function, attr);
492 }
493
494 NEVER_INLINE void JSObject::fillGetterPropertySlot(PropertySlot& slot, JSValuePtr* location)
495 {
496     if (JSObject* getterFunction = asGetterSetter(*location)->getter())
497         slot.setGetterSlot(getterFunction);
498     else
499         slot.setUndefined();
500 }
501
502 Structure* JSObject::createInheritorID()
503 {
504     m_inheritorID = JSObject::createStructure(this);
505     return m_inheritorID.get();
506 }
507
508 void JSObject::allocatePropertyStorage(size_t oldSize, size_t newSize)
509 {
510     allocatePropertyStorageInline(oldSize, newSize);
511 }
512
513 JSObject* constructEmptyObject(ExecState* exec)
514 {
515     return new (exec) JSObject(exec->lexicalGlobalObject()->emptyObjectStructure());
516 }
517
518 } // namespace JSC