Add finalizer to JSObject
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSObject.h
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, 2007, 2008, 2009 Apple Inc. All rights reserved.
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Library General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Library General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Library General Public License
17  *  along with this library; see the file COPYING.LIB.  If not, write to
18  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  *  Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #ifndef JSObject_h
24 #define JSObject_h
25
26 #include "ArgList.h"
27 #include "ClassInfo.h"
28 #include "CommonIdentifiers.h"
29 #include "CallFrame.h"
30 #include "JSCell.h"
31 #include "PropertySlot.h"
32 #include "PutPropertySlot.h"
33 #include "ScopeChain.h"
34 #include "StorageBarrier.h"
35 #include "Structure.h"
36 #include "JSGlobalData.h"
37 #include "JSString.h"
38 #include <wtf/StdLibExtras.h>
39
40 namespace JSC {
41
42     inline JSCell* getJSFunction(JSValue value)
43     {
44         if (value.isCell() && (value.asCell()->structure()->typeInfo().type() == JSFunctionType))
45             return value.asCell();
46         return 0;
47     }
48
49     class GetterSetter;
50     class HashEntry;
51     class InternalFunction;
52     class MarkedBlock;
53     class PropertyDescriptor;
54     class PropertyNameArray;
55     class Structure;
56     struct HashTable;
57
58     JSObject* throwTypeError(ExecState*, const UString&);
59     extern const char* StrictModeReadonlyPropertyWriteError;
60
61     // ECMA 262-3 8.6.1
62     // Property attributes
63     enum Attribute {
64         None         = 0,
65         ReadOnly     = 1 << 1,  // property can be only read, not written
66         DontEnum     = 1 << 2,  // property doesn't appear in (for .. in ..)
67         DontDelete   = 1 << 3,  // property can't be deleted
68         Function     = 1 << 4,  // property is a function - only used by static hashtables
69         Getter       = 1 << 5,  // property is a getter
70         Setter       = 1 << 6   // property is a setter
71     };
72
73     class JSObject : public JSCell {
74         friend class BatchedTransitionOptimizer;
75         friend class JIT;
76         friend class JSCell;
77         friend class MarkedBlock;
78         friend bool setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot);
79
80     public:
81         typedef JSCell Base;
82
83         static void visitChildren(JSCell*, SlotVisitor&);
84
85         virtual UString className() const;
86
87         static void finalize(JSCell*);
88
89         JSValue prototype() const;
90         void setPrototype(JSGlobalData&, JSValue prototype);
91         bool setPrototypeWithCycleCheck(JSGlobalData&, JSValue prototype);
92         
93         Structure* inheritorID(JSGlobalData&);
94
95         JSValue get(ExecState*, const Identifier& propertyName) const;
96         JSValue get(ExecState*, unsigned propertyName) const;
97
98         bool getPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
99         bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
100         bool getPropertyDescriptor(ExecState*, const Identifier& propertyName, PropertyDescriptor&);
101
102         virtual bool getOwnPropertySlotVirtual(ExecState*, const Identifier& propertyName, PropertySlot&);
103         static bool getOwnPropertySlot(JSCell*, ExecState*, const Identifier& propertyName, PropertySlot&);
104         virtual bool getOwnPropertySlotVirtual(ExecState*, unsigned propertyName, PropertySlot&);
105         static bool getOwnPropertySlot(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
106         virtual bool getOwnPropertyDescriptor(ExecState*, const Identifier&, PropertyDescriptor&);
107
108         virtual void putVirtual(ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&);
109         static void put(JSCell*, ExecState*, const Identifier& propertyName, JSValue, PutPropertySlot&);
110         virtual void putVirtual(ExecState*, unsigned propertyName, JSValue);
111         static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue);
112
113         virtual void putWithAttributes(JSGlobalData*, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot);
114         virtual void putWithAttributes(JSGlobalData*, const Identifier& propertyName, JSValue value, unsigned attributes);
115         virtual void putWithAttributes(JSGlobalData*, unsigned propertyName, JSValue value, unsigned attributes);
116         virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot);
117         virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue value, unsigned attributes);
118         virtual void putWithAttributes(ExecState*, unsigned propertyName, JSValue value, unsigned attributes);
119
120         bool propertyIsEnumerable(ExecState*, const Identifier& propertyName) const;
121
122         bool hasProperty(ExecState*, const Identifier& propertyName) const;
123         bool hasProperty(ExecState*, unsigned propertyName) const;
124         bool hasOwnProperty(ExecState*, const Identifier& propertyName) const;
125
126         virtual bool deletePropertyVirtual(ExecState*, const Identifier& propertyName);
127         static bool deleteProperty(JSCell*, ExecState*, const Identifier& propertyName);
128         virtual bool deletePropertyVirtual(ExecState*, unsigned propertyName);
129         static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName);
130
131         virtual JSValue defaultValue(ExecState*, PreferredPrimitiveType) const;
132
133         virtual bool hasInstance(ExecState*, JSValue, JSValue prototypeProperty);
134
135         virtual void getPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties);
136         virtual void getOwnPropertyNames(ExecState*, PropertyNameArray&, EnumerationMode mode = ExcludeDontEnumProperties);
137
138         JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const;
139         bool toBoolean(ExecState*) const;
140         bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
141         double toNumber(ExecState*) const;
142         UString toString(ExecState*) const;
143
144         virtual JSObject* toThisObject(ExecState*) const;
145         virtual JSObject* unwrappedObject();
146
147         bool getPropertySpecificValue(ExecState* exec, const Identifier& propertyName, JSCell*& specificFunction) const;
148
149         // This get function only looks at the property map.
150         JSValue getDirect(JSGlobalData& globalData, const Identifier& propertyName) const
151         {
152             size_t offset = structure()->get(globalData, propertyName);
153             return offset != WTF::notFound ? getDirectOffset(offset) : JSValue();
154         }
155
156         WriteBarrierBase<Unknown>* getDirectLocation(JSGlobalData& globalData, const Identifier& propertyName)
157         {
158             size_t offset = structure()->get(globalData, propertyName);
159             return offset != WTF::notFound ? locationForOffset(offset) : 0;
160         }
161
162         WriteBarrierBase<Unknown>* getDirectLocation(JSGlobalData& globalData, const Identifier& propertyName, unsigned& attributes)
163         {
164             JSCell* specificFunction;
165             size_t offset = structure()->get(globalData, propertyName, attributes, specificFunction);
166             return offset != WTF::notFound ? locationForOffset(offset) : 0;
167         }
168
169         size_t offsetForLocation(WriteBarrierBase<Unknown>* location) const
170         {
171             return location - propertyStorage();
172         }
173
174         void transitionTo(JSGlobalData&, Structure*);
175
176         void removeDirect(JSGlobalData&, const Identifier& propertyName);
177         bool hasCustomProperties() { return structure()->didTransition(); }
178         bool hasGetterSetterProperties() { return structure()->hasGetterSetterProperties(); }
179
180         bool putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&);
181         void putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr = 0);
182         bool putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, PutPropertySlot&);
183
184         void putDirectWithoutTransition(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr = 0);
185
186         // Fast access to known property offsets.
187         JSValue getDirectOffset(size_t offset) const { return propertyStorage()[offset].get(); }
188         void putDirectOffset(JSGlobalData& globalData, size_t offset, JSValue value) { propertyStorage()[offset].set(globalData, this, value); }
189         void putUndefinedAtDirectOffset(size_t offset) { propertyStorage()[offset].setUndefined(); }
190
191         void fillGetterPropertySlot(PropertySlot&, WriteBarrierBase<Unknown>* location);
192         void initializeGetterSetterProperty(ExecState*, const Identifier&, GetterSetter*, unsigned attributes);
193
194         virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes = 0);
195         virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes = 0);
196         virtual JSValue lookupGetter(ExecState*, const Identifier& propertyName);
197         virtual JSValue lookupSetter(ExecState*, const Identifier& propertyName);
198         virtual bool defineOwnProperty(ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow);
199
200         virtual bool isGlobalObject() const { return false; }
201         virtual bool isVariableObject() const { return false; }
202         virtual bool isActivationObject() const { return false; }
203         virtual bool isErrorInstance() const { return false; }
204
205         void seal(JSGlobalData&);
206         void freeze(JSGlobalData&);
207         void preventExtensions(JSGlobalData&);
208         bool isSealed(JSGlobalData& globalData) { return structure()->isSealed(globalData); }
209         bool isFrozen(JSGlobalData& globalData) { return structure()->isFrozen(globalData); }
210         bool isExtensible() { return structure()->isExtensible(); }
211
212         bool staticFunctionsReified() { return structure()->staticFunctionsReified(); }
213         void reifyStaticFunctionsForDelete(ExecState* exec);
214
215         void allocatePropertyStorage(JSGlobalData&, size_t oldSize, size_t newSize);
216         bool isUsingInlineStorage() const { return static_cast<const void*>(m_propertyStorage.get()) == static_cast<const void*>(this + 1); }
217
218         void* addressOfPropertyStorage()
219         {
220             return &m_propertyStorage;
221         }
222
223         static const unsigned baseExternalStorageCapacity = 16;
224
225         void flattenDictionaryObject(JSGlobalData& globalData)
226         {
227             structure()->flattenDictionaryStructure(globalData, this);
228         }
229
230         JSGlobalObject* globalObject() const
231         {
232             ASSERT(structure()->globalObject());
233             ASSERT(!isGlobalObject() || ((JSObject*)structure()->globalObject()) == this);
234             return structure()->globalObject();
235         }
236         
237         static size_t offsetOfInlineStorage();
238         static size_t offsetOfPropertyStorage();
239         static size_t offsetOfInheritorID();
240
241         static JS_EXPORTDATA const ClassInfo s_info;
242
243     protected:
244         void finishCreation(JSGlobalData& globalData, PropertyStorage inlineStorage)
245         {
246             Base::finishCreation(globalData);
247             ASSERT(inherits(&s_info));
248             ASSERT(structure()->propertyStorageCapacity() < baseExternalStorageCapacity);
249             ASSERT(structure()->isEmpty());
250             ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype()));
251             ASSERT_UNUSED(inlineStorage, static_cast<void*>(inlineStorage) == static_cast<void*>(this + 1));
252             ASSERT(structure()->isObject());
253         }
254
255         static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
256         {
257             return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
258         }
259
260         static const unsigned StructureFlags = 0;
261
262         // To instantiate objects you likely want JSFinalObject, below.
263         // To create derived types you likely want JSNonFinalObject, below.
264         JSObject(JSGlobalData&, Structure*, PropertyStorage inlineStorage);
265         JSObject(VPtrStealingHackType, PropertyStorage inlineStorage)
266             : JSCell(VPtrStealingHack)
267             , m_propertyStorage(inlineStorage, StorageBarrier::Unchecked)
268         {
269         }
270
271     private:
272         // Nobody should ever ask any of these questions on something already known to be a JSObject.
273         using JSCell::isAPIValueWrapper;
274         using JSCell::isGetterSetter;
275         void getObject();
276         void getString(ExecState* exec);
277         void isObject();
278         void isString();
279         
280         ConstPropertyStorage propertyStorage() const { return m_propertyStorage.get(); }
281         PropertyStorage propertyStorage() { return m_propertyStorage.get(); }
282
283         const WriteBarrierBase<Unknown>* locationForOffset(size_t offset) const
284         {
285             return &propertyStorage()[offset];
286         }
287
288         WriteBarrierBase<Unknown>* locationForOffset(size_t offset)
289         {
290             return &propertyStorage()[offset];
291         }
292
293         bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&, JSCell*);
294
295         bool inlineGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
296
297         const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const;
298         Structure* createInheritorID(JSGlobalData&);
299
300         StorageBarrier m_propertyStorage;
301         WriteBarrier<Structure> m_inheritorID;
302     };
303
304
305 #if USE(JSVALUE32_64)
306 #define JSNonFinalObject_inlineStorageCapacity 4
307 #define JSFinalObject_inlineStorageCapacity 6
308 #else
309 #define JSNonFinalObject_inlineStorageCapacity 2
310 #define JSFinalObject_inlineStorageCapacity 4
311 #endif
312
313 COMPILE_ASSERT((JSFinalObject_inlineStorageCapacity >= JSNonFinalObject_inlineStorageCapacity), final_storage_is_at_least_as_large_as_non_final);
314
315     // JSNonFinalObject is a type of JSObject that has some internal storage,
316     // but also preserves some space in the collector cell for additional
317     // data members in derived types.
318     class JSNonFinalObject : public JSObject {
319         friend class JSObject;
320
321     public:
322         typedef JSObject Base;
323
324         static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
325         {
326             return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
327         }
328
329     protected:
330         explicit JSNonFinalObject(VPtrStealingHackType)
331             : JSObject(VPtrStealingHack, m_inlineStorage)
332         {
333         }
334     
335         explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure)
336             : JSObject(globalData, structure, m_inlineStorage)
337         {
338         }
339
340         void finishCreation(JSGlobalData& globalData)
341         {
342             Base::finishCreation(globalData, m_inlineStorage);
343             ASSERT(!(OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage) % sizeof(double)));
344             ASSERT(this->structure()->propertyStorageCapacity() == JSNonFinalObject_inlineStorageCapacity);
345         }
346
347     private:
348         WriteBarrier<Unknown> m_inlineStorage[JSNonFinalObject_inlineStorageCapacity];
349     };
350
351     // JSFinalObject is a type of JSObject that contains sufficent internal
352     // storage to fully make use of the colloctor cell containing it.
353     class JSFinalObject : public JSObject {
354         friend class JSObject;
355
356     public:
357         typedef JSObject Base;
358
359         explicit JSFinalObject(VPtrStealingHackType)
360             : JSObject(VPtrStealingHack, m_inlineStorage)
361         {
362         }
363         
364         static JSFinalObject* create(ExecState* exec, Structure* structure)
365         {
366             JSFinalObject* finalObject = new (allocateCell<JSFinalObject>(*exec->heap())) JSFinalObject(exec->globalData(), structure);
367             finalObject->finishCreation(exec->globalData());
368             return finalObject;
369         }
370
371         static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
372         {
373             return Structure::create(globalData, globalObject, prototype, TypeInfo(FinalObjectType, StructureFlags), &s_info);
374         }
375
376         static JS_EXPORTDATA const ClassInfo s_info;
377
378     protected:
379         void finishCreation(JSGlobalData& globalData)
380         {
381             Base::finishCreation(globalData, m_inlineStorage);
382             ASSERT(!(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) % sizeof(double)));
383             ASSERT(this->structure()->propertyStorageCapacity() == JSFinalObject_inlineStorageCapacity);
384         }
385
386     private:
387         explicit JSFinalObject(JSGlobalData& globalData, Structure* structure)
388             : JSObject(globalData, structure, m_inlineStorage)
389         {
390         }
391
392         static const unsigned StructureFlags = JSObject::StructureFlags;
393
394         WriteBarrierBase<Unknown> m_inlineStorage[JSFinalObject_inlineStorageCapacity];
395     };
396
397 inline size_t JSObject::offsetOfInlineStorage()
398 {
399     ASSERT(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) == OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage));
400     return OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage);
401 }
402
403 inline size_t JSObject::offsetOfPropertyStorage()
404 {
405     return OBJECT_OFFSETOF(JSObject, m_propertyStorage);
406 }
407
408 inline size_t JSObject::offsetOfInheritorID()
409 {
410     return OBJECT_OFFSETOF(JSObject, m_inheritorID);
411 }
412
413 inline JSObject* constructEmptyObject(ExecState* exec, Structure* structure)
414 {
415     return JSFinalObject::create(exec, structure);
416 }
417
418 inline CallType getCallData(JSValue value, CallData& callData)
419 {
420     CallType result = value.isCell() ? value.asCell()->methodTable()->getCallData(value.asCell(), callData) : CallTypeNone;
421     ASSERT(result == CallTypeNone || value.isValidCallee());
422     return result;
423 }
424
425 inline Structure* createEmptyObjectStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
426 {
427     return JSFinalObject::createStructure(globalData, globalObject, prototype);
428 }
429
430 inline JSObject* asObject(JSCell* cell)
431 {
432     ASSERT(cell->isObject());
433     return static_cast<JSObject*>(cell);
434 }
435
436 inline JSObject* asObject(JSValue value)
437 {
438     return asObject(value.asCell());
439 }
440
441 inline JSObject::JSObject(JSGlobalData& globalData, Structure* structure, PropertyStorage inlineStorage)
442     : JSCell(globalData, structure)
443     , m_propertyStorage(globalData, this, inlineStorage)
444 {
445 }
446
447 inline JSValue JSObject::prototype() const
448 {
449     return structure()->storedPrototype();
450 }
451
452 inline bool JSObject::setPrototypeWithCycleCheck(JSGlobalData& globalData, JSValue prototype)
453 {
454     JSValue nextPrototypeValue = prototype;
455     while (nextPrototypeValue && nextPrototypeValue.isObject()) {
456         JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject();
457         if (nextPrototype == this)
458             return false;
459         nextPrototypeValue = nextPrototype->prototype();
460     }
461     setPrototype(globalData, prototype);
462     return true;
463 }
464
465 inline void JSObject::setPrototype(JSGlobalData& globalData, JSValue prototype)
466 {
467     ASSERT(prototype);
468     setStructure(globalData, Structure::changePrototypeTransition(globalData, structure(), prototype));
469 }
470
471 inline Structure* JSObject::inheritorID(JSGlobalData& globalData)
472 {
473     if (m_inheritorID) {
474         ASSERT(m_inheritorID->isEmpty());
475         return m_inheritorID.get();
476     }
477     return createInheritorID(globalData);
478 }
479
480 inline bool Structure::isUsingInlineStorage() const
481 {
482     return propertyStorageCapacity() < JSObject::baseExternalStorageCapacity;
483 }
484
485 inline bool JSCell::inherits(const ClassInfo* info) const
486 {
487     return classInfo()->isSubClassOf(info);
488 }
489
490 inline const MethodTable* JSCell::methodTable() const
491 {
492     return &classInfo()->methodTable;
493 }
494
495 // this method is here to be after the inline declaration of JSCell::inherits
496 inline bool JSValue::inherits(const ClassInfo* classInfo) const
497 {
498     return isCell() && asCell()->inherits(classInfo);
499 }
500
501 ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
502 {
503     if (WriteBarrierBase<Unknown>* location = getDirectLocation(exec->globalData(), propertyName)) {
504         if (structure()->hasGetterSetterProperties() && location->isGetterSetter())
505             fillGetterPropertySlot(slot, location);
506         else
507             slot.setValue(this, location->get(), offsetForLocation(location));
508         return true;
509     }
510
511     // non-standard Netscape extension
512     if (propertyName == exec->propertyNames().underscoreProto) {
513         slot.setValue(prototype());
514         return true;
515     }
516
517     return false;
518 }
519
520 ALWAYS_INLINE bool JSObject::getOwnPropertySlotVirtual(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
521 {
522     return getOwnPropertySlot(this, exec, propertyName, slot);
523 }
524
525 // It may seem crazy to inline a function this large, especially a virtual function,
526 // but it makes a big difference to property lookup that derived classes can inline their
527 // base class call to this.
528 ALWAYS_INLINE bool JSObject::getOwnPropertySlot(JSCell* cell, ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
529 {
530     return static_cast<JSObject*>(cell)->inlineGetOwnPropertySlot(exec, propertyName, slot);
531 }
532
533 ALWAYS_INLINE bool JSCell::fastGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
534 {
535     if (!structure()->typeInfo().overridesGetOwnPropertySlot())
536         return asObject(this)->inlineGetOwnPropertySlot(exec, propertyName, slot);
537     return getOwnPropertySlotVirtual(exec, propertyName, slot);
538 }
539
540 // Fast call to get a property where we may not yet have converted the string to an
541 // identifier. The first time we perform a property access with a given string, try
542 // performing the property map lookup without forming an identifier. We detect this
543 // case by checking whether the hash has yet been set for this string.
544 ALWAYS_INLINE JSValue JSCell::fastGetOwnProperty(ExecState* exec, const UString& name)
545 {
546     if (!structure()->typeInfo().overridesGetOwnPropertySlot() && !structure()->hasGetterSetterProperties()) {
547         size_t offset = name.impl()->hasHash()
548             ? structure()->get(exec->globalData(), Identifier(exec, name))
549             : structure()->get(exec->globalData(), name);
550         if (offset != WTF::notFound)
551             return asObject(this)->locationForOffset(offset)->get();
552     }
553     return JSValue();
554 }
555
556 // It may seem crazy to inline a function this large but it makes a big difference
557 // since this is function very hot in variable lookup
558 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
559 {
560     JSObject* object = this;
561     while (true) {
562         if (object->fastGetOwnPropertySlot(exec, propertyName, slot))
563             return true;
564         JSValue prototype = object->prototype();
565         if (!prototype.isObject())
566             return false;
567         object = asObject(prototype);
568     }
569 }
570
571 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
572 {
573     JSObject* object = this;
574     while (true) {
575         if (object->getOwnPropertySlotVirtual(exec, propertyName, slot))
576             return true;
577         JSValue prototype = object->prototype();
578         if (!prototype.isObject())
579             return false;
580         object = asObject(prototype);
581     }
582 }
583
584 inline JSValue JSObject::get(ExecState* exec, const Identifier& propertyName) const
585 {
586     PropertySlot slot(this);
587     if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
588         return slot.getValue(exec, propertyName);
589     
590     return jsUndefined();
591 }
592
593 inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const
594 {
595     PropertySlot slot(this);
596     if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
597         return slot.getValue(exec, propertyName);
598
599     return jsUndefined();
600 }
601
602 inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot, JSCell* specificFunction)
603 {
604     ASSERT(value);
605     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
606
607     if (structure()->isDictionary()) {
608         unsigned currentAttributes;
609         JSCell* currentSpecificFunction;
610         size_t offset = structure()->get(globalData, propertyName, currentAttributes, currentSpecificFunction);
611         if (offset != WTF::notFound) {
612             // If there is currently a specific function, and there now either isn't,
613             // or the new value is different, then despecify.
614             if (currentSpecificFunction && (specificFunction != currentSpecificFunction))
615                 structure()->despecifyDictionaryFunction(globalData, propertyName);
616             if (checkReadOnly && currentAttributes & ReadOnly)
617                 return false;
618
619             putDirectOffset(globalData, offset, value);
620             // At this point, the objects structure only has a specific value set if previously there
621             // had been one set, and if the new value being specified is the same (otherwise we would
622             // have despecified, above).  So, if currentSpecificFunction is not set, or if the new
623             // value is different (or there is no new value), then the slot now has no value - and
624             // as such it is cachable.
625             // If there was previously a value, and the new value is the same, then we cannot cache.
626             if (!currentSpecificFunction || (specificFunction != currentSpecificFunction))
627                 slot.setExistingProperty(this, offset);
628             return true;
629         }
630
631         if (checkReadOnly && !isExtensible())
632             return false;
633
634         size_t currentCapacity = structure()->propertyStorageCapacity();
635         offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, specificFunction);
636         if (currentCapacity != structure()->propertyStorageCapacity())
637             allocatePropertyStorage(globalData, currentCapacity, structure()->propertyStorageCapacity());
638
639         ASSERT(offset < structure()->propertyStorageCapacity());
640         putDirectOffset(globalData, offset, value);
641         // See comment on setNewProperty call below.
642         if (!specificFunction)
643             slot.setNewProperty(this, offset);
644         return true;
645     }
646
647     size_t offset;
648     size_t currentCapacity = structure()->propertyStorageCapacity();
649     if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName, attributes, specificFunction, offset)) {    
650         if (currentCapacity != structure->propertyStorageCapacity())
651             allocatePropertyStorage(globalData, currentCapacity, structure->propertyStorageCapacity());
652
653         ASSERT(offset < structure->propertyStorageCapacity());
654         setStructure(globalData, structure);
655         putDirectOffset(globalData, offset, value);
656         // This is a new property; transitions with specific values are not currently cachable,
657         // so leave the slot in an uncachable state.
658         if (!specificFunction)
659             slot.setNewProperty(this, offset);
660         return true;
661     }
662
663     unsigned currentAttributes;
664     JSCell* currentSpecificFunction;
665     offset = structure()->get(globalData, propertyName, currentAttributes, currentSpecificFunction);
666     if (offset != WTF::notFound) {
667         if (checkReadOnly && currentAttributes & ReadOnly)
668             return false;
669
670         // There are three possibilities here:
671         //  (1) There is an existing specific value set, and we're overwriting with *the same value*.
672         //       * Do nothing - no need to despecify, but that means we can't cache (a cached
673         //         put could write a different value). Leave the slot in an uncachable state.
674         //  (2) There is a specific value currently set, but we're writing a different value.
675         //       * First, we have to despecify.  Having done so, this is now a regular slot
676         //         with no specific value, so go ahead & cache like normal.
677         //  (3) Normal case, there is no specific value set.
678         //       * Go ahead & cache like normal.
679         if (currentSpecificFunction) {
680             // case (1) Do the put, then return leaving the slot uncachable.
681             if (specificFunction == currentSpecificFunction) {
682                 putDirectOffset(globalData, offset, value);
683                 return true;
684             }
685             // case (2) Despecify, fall through to (3).
686             setStructure(globalData, Structure::despecifyFunctionTransition(globalData, structure(), propertyName));
687         }
688
689         // case (3) set the slot, do the put, return.
690         slot.setExistingProperty(this, offset);
691         putDirectOffset(globalData, offset, value);
692         return true;
693     }
694
695     if (checkReadOnly && !isExtensible())
696         return false;
697
698     Structure* structure = Structure::addPropertyTransition(globalData, this->structure(), propertyName, attributes, specificFunction, offset);
699
700     if (currentCapacity != structure->propertyStorageCapacity())
701         allocatePropertyStorage(globalData, currentCapacity, structure->propertyStorageCapacity());
702
703     ASSERT(offset < structure->propertyStorageCapacity());
704     setStructure(globalData, structure);
705     putDirectOffset(globalData, offset, value);
706     // This is a new property; transitions with specific values are not currently cachable,
707     // so leave the slot in an uncachable state.
708     if (!specificFunction)
709         slot.setNewProperty(this, offset);
710     return true;
711 }
712
713 inline bool JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
714 {
715     ASSERT(value);
716     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
717
718     return putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, getJSFunction(value));
719 }
720
721 inline void JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
722 {
723     PutPropertySlot slot;
724     putDirectInternal(globalData, propertyName, value, attributes, false, slot, getJSFunction(value));
725 }
726
727 inline bool JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
728 {
729     return putDirectInternal(globalData, propertyName, value, 0, false, slot, getJSFunction(value));
730 }
731
732 inline void JSObject::putDirectWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
733 {
734     size_t currentCapacity = structure()->propertyStorageCapacity();
735     size_t offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, getJSFunction(value));
736     if (currentCapacity != structure()->propertyStorageCapacity())
737         allocatePropertyStorage(globalData, currentCapacity, structure()->propertyStorageCapacity());
738     putDirectOffset(globalData, offset, value);
739 }
740
741 inline void JSObject::transitionTo(JSGlobalData& globalData, Structure* newStructure)
742 {
743     if (structure()->propertyStorageCapacity() != newStructure->propertyStorageCapacity())
744         allocatePropertyStorage(globalData, structure()->propertyStorageCapacity(), newStructure->propertyStorageCapacity());
745     setStructure(globalData, newStructure);
746 }
747
748 inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
749 {
750     return defaultValue(exec, preferredType);
751 }
752
753 inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName) const
754 {
755     PropertySlot slot(asValue());
756     return get(exec, propertyName, slot);
757 }
758
759 inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) const
760 {
761     if (UNLIKELY(!isCell())) {
762         JSObject* prototype = synthesizePrototype(exec);
763         if (propertyName == exec->propertyNames().underscoreProto)
764             return prototype;
765         if (!prototype->getPropertySlot(exec, propertyName, slot))
766             return jsUndefined();
767         return slot.getValue(exec, propertyName);
768     }
769     JSCell* cell = asCell();
770     while (true) {
771         if (cell->fastGetOwnPropertySlot(exec, propertyName, slot))
772             return slot.getValue(exec, propertyName);
773         JSValue prototype = asObject(cell)->prototype();
774         if (!prototype.isObject())
775             return jsUndefined();
776         cell = asObject(prototype);
777     }
778 }
779
780 inline JSValue JSValue::get(ExecState* exec, unsigned propertyName) const
781 {
782     PropertySlot slot(asValue());
783     return get(exec, propertyName, slot);
784 }
785
786 inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const
787 {
788     if (UNLIKELY(!isCell())) {
789         JSObject* prototype = synthesizePrototype(exec);
790         if (!prototype->getPropertySlot(exec, propertyName, slot))
791             return jsUndefined();
792         return slot.getValue(exec, propertyName);
793     }
794     JSCell* cell = const_cast<JSCell*>(asCell());
795     while (true) {
796         if (cell->getOwnPropertySlotVirtual(exec, propertyName, slot))
797             return slot.getValue(exec, propertyName);
798         JSValue prototype = asObject(cell)->prototype();
799         if (!prototype.isObject())
800             return jsUndefined();
801         cell = prototype.asCell();
802     }
803 }
804
805 inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
806 {
807     if (UNLIKELY(!isCell())) {
808         synthesizeObject(exec)->putVirtual(exec, propertyName, value, slot);
809         return;
810     }
811     asCell()->putVirtual(exec, propertyName, value, slot);
812 }
813
814 inline void JSValue::putDirect(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
815 {
816     ASSERT(isCell() && isObject());
817     if (!asObject(asCell())->putDirect(exec->globalData(), propertyName, value, slot) && slot.isStrictMode())
818         throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
819 }
820
821 inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value)
822 {
823     if (UNLIKELY(!isCell())) {
824         synthesizeObject(exec)->putVirtual(exec, propertyName, value);
825         return;
826     }
827     asCell()->putVirtual(exec, propertyName, value);
828 }
829
830 // --- JSValue inlines ----------------------------
831
832 ALWAYS_INLINE JSObject* Register::function() const
833 {
834     if (!jsValue())
835         return 0;
836     return asObject(jsValue());
837 }
838
839 ALWAYS_INLINE Register Register::withCallee(JSObject* callee)
840 {
841     Register r;
842     r = JSValue(callee);
843     return r;
844 }
845
846 } // namespace JSC
847
848 #endif // JSObject_h