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