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