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