2011-02-28 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 "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             ASSERT(this->structure()->propertyStorageCapacity() == JSNonFinalObject_inlineStorageCapacity);
327         }
328
329     private:
330         WriteBarrierBase<Unknown> m_inlineStorage[JSNonFinalObject_inlineStorageCapacity];
331     };
332
333     // JSFinalObject is a type of JSObject that contains sufficent internal
334     // storage to fully make use of the colloctor cell containing it.
335     class JSFinalObject : public JSObject {
336         friend class JSObject;
337
338     public:
339         static JSFinalObject* create(ExecState* exec, NonNullPassRefPtr<Structure> structure)
340         {
341             return new (exec) JSFinalObject(structure);
342         }
343
344         static PassRefPtr<Structure> createStructure(JSValue prototype)
345         {
346             return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info);
347         }
348
349     private:
350         explicit JSFinalObject(NonNullPassRefPtr<Structure> structure)
351             : JSObject(structure, m_inlineStorage)
352         {
353             ASSERT(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) % sizeof(double) == 0);
354             ASSERT(this->structure()->propertyStorageCapacity() == JSFinalObject_inlineStorageCapacity);
355         }
356
357         static const unsigned StructureFlags = JSObject::StructureFlags | IsJSFinalObject;
358
359         WriteBarrierBase<Unknown> m_inlineStorage[JSFinalObject_inlineStorageCapacity];
360     };
361
362 inline size_t JSObject::offsetOfInlineStorage()
363 {
364     ASSERT(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) == OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage));
365     return OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage);
366 }
367
368 inline JSObject* constructEmptyObject(ExecState* exec, NonNullPassRefPtr<Structure> structure)
369 {
370     return JSFinalObject::create(exec, structure);
371 }
372
373 inline PassRefPtr<Structure> createEmptyObjectStructure(JSValue prototype)
374 {
375     return JSFinalObject::createStructure(prototype);
376 }
377
378 inline JSObject* asObject(JSCell* cell)
379 {
380     ASSERT(cell->isObject());
381     return static_cast<JSObject*>(cell);
382 }
383
384 inline JSObject* asObject(JSValue value)
385 {
386     return asObject(value.asCell());
387 }
388
389 inline JSObject::JSObject(NonNullPassRefPtr<Structure> structure, PropertyStorage inlineStorage)
390     : JSCell(structure.releaseRef()) // ~JSObject balances this ref()
391     , m_propertyStorage(inlineStorage)
392 {
393     ASSERT(inherits(&s_info));
394     ASSERT(m_structure->propertyStorageCapacity() < baseExternalStorageCapacity);
395     ASSERT(m_structure->isEmpty());
396     ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype()));
397 }
398
399 inline JSObject::~JSObject()
400 {
401     ASSERT(m_structure);
402     if (!isUsingInlineStorage())
403         delete [] m_propertyStorage;
404     m_structure->deref();
405 }
406
407 inline JSValue JSObject::prototype() const
408 {
409     return m_structure->storedPrototype();
410 }
411
412 inline bool JSObject::setPrototypeWithCycleCheck(JSValue prototype)
413 {
414     JSValue nextPrototypeValue = prototype;
415     while (nextPrototypeValue && nextPrototypeValue.isObject()) {
416         JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject();
417         if (nextPrototype == this)
418             return false;
419         nextPrototypeValue = nextPrototype->prototype();
420     }
421     setPrototype(prototype);
422     return true;
423 }
424
425 inline void JSObject::setPrototype(JSValue prototype)
426 {
427     ASSERT(prototype);
428     RefPtr<Structure> newStructure = Structure::changePrototypeTransition(m_structure, prototype);
429     setStructure(newStructure.release());
430 }
431
432 inline void JSObject::setStructure(NonNullPassRefPtr<Structure> structure)
433 {
434     m_structure->deref();
435     m_structure = structure.leakRef(); // ~JSObject balances this ref()
436 }
437
438 inline Structure* JSObject::inheritorID()
439 {
440     if (m_inheritorID)
441         return m_inheritorID.get();
442     return createInheritorID();
443 }
444
445 inline bool Structure::isUsingInlineStorage() const
446 {
447     return propertyStorageCapacity() < JSObject::baseExternalStorageCapacity;
448 }
449
450 inline bool JSCell::inherits(const ClassInfo* info) const
451 {
452     for (const ClassInfo* ci = classInfo(); ci; ci = ci->parentClass) {
453         if (ci == info)
454             return true;
455     }
456     return false;
457 }
458
459 // this method is here to be after the inline declaration of JSCell::inherits
460 inline bool JSValue::inherits(const ClassInfo* classInfo) const
461 {
462     return isCell() && asCell()->inherits(classInfo);
463 }
464
465 ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
466 {
467     if (WriteBarrierBase<Unknown>* location = getDirectLocation(propertyName)) {
468         if (m_structure->hasGetterSetterProperties() && location->isGetterSetter())
469             fillGetterPropertySlot(slot, location);
470         else
471             slot.setValue(this, location->get(), offsetForLocation(location));
472         return true;
473     }
474
475     // non-standard Netscape extension
476     if (propertyName == exec->propertyNames().underscoreProto) {
477         slot.setValue(prototype());
478         return true;
479     }
480
481     return false;
482 }
483
484 // It may seem crazy to inline a function this large, especially a virtual function,
485 // but it makes a big difference to property lookup that derived classes can inline their
486 // base class call to this.
487 ALWAYS_INLINE bool JSObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
488 {
489     return inlineGetOwnPropertySlot(exec, propertyName, slot);
490 }
491
492 ALWAYS_INLINE bool JSCell::fastGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
493 {
494     if (!structure()->typeInfo().overridesGetOwnPropertySlot())
495         return asObject(this)->inlineGetOwnPropertySlot(exec, propertyName, slot);
496     return getOwnPropertySlot(exec, propertyName, slot);
497 }
498
499 // It may seem crazy to inline a function this large but it makes a big difference
500 // since this is function very hot in variable lookup
501 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
502 {
503     JSObject* object = this;
504     while (true) {
505         if (object->fastGetOwnPropertySlot(exec, propertyName, slot))
506             return true;
507         JSValue prototype = object->prototype();
508         if (!prototype.isObject())
509             return false;
510         object = asObject(prototype);
511     }
512 }
513
514 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
515 {
516     JSObject* object = this;
517     while (true) {
518         if (object->getOwnPropertySlot(exec, propertyName, slot))
519             return true;
520         JSValue prototype = object->prototype();
521         if (!prototype.isObject())
522             return false;
523         object = asObject(prototype);
524     }
525 }
526
527 inline JSValue JSObject::get(ExecState* exec, const Identifier& propertyName) const
528 {
529     PropertySlot slot(this);
530     if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
531         return slot.getValue(exec, propertyName);
532     
533     return jsUndefined();
534 }
535
536 inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const
537 {
538     PropertySlot slot(this);
539     if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
540         return slot.getValue(exec, propertyName);
541
542     return jsUndefined();
543 }
544
545 inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot, JSCell* specificFunction)
546 {
547     ASSERT(value);
548     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
549
550     if (m_structure->isDictionary()) {
551         unsigned currentAttributes;
552         JSCell* currentSpecificFunction;
553         size_t offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction);
554         if (offset != WTF::notFound) {
555             // If there is currently a specific function, and there now either isn't,
556             // or the new value is different, then despecify.
557             if (currentSpecificFunction && (specificFunction != currentSpecificFunction))
558                 m_structure->despecifyDictionaryFunction(propertyName);
559             if (checkReadOnly && currentAttributes & ReadOnly)
560                 return false;
561
562             putDirectOffset(globalData, offset, value);
563             // At this point, the objects structure only has a specific value set if previously there
564             // had been one set, and if the new value being specified is the same (otherwise we would
565             // have despecified, above).  So, if currentSpecificFunction is not set, or if the new
566             // value is different (or there is no new value), then the slot now has no value - and
567             // as such it is cachable.
568             // If there was previously a value, and the new value is the same, then we cannot cache.
569             if (!currentSpecificFunction || (specificFunction != currentSpecificFunction))
570                 slot.setExistingProperty(this, offset);
571             return true;
572         }
573
574         size_t currentCapacity = m_structure->propertyStorageCapacity();
575         offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, specificFunction);
576         if (currentCapacity != m_structure->propertyStorageCapacity())
577             allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
578
579         ASSERT(offset < m_structure->propertyStorageCapacity());
580         putDirectOffset(globalData, offset, value);
581         // See comment on setNewProperty call below.
582         if (!specificFunction)
583             slot.setNewProperty(this, offset);
584         return true;
585     }
586
587     size_t offset;
588     size_t currentCapacity = m_structure->propertyStorageCapacity();
589     if (RefPtr<Structure> structure = Structure::addPropertyTransitionToExistingStructure(m_structure, propertyName, attributes, specificFunction, offset)) {    
590         if (currentCapacity != structure->propertyStorageCapacity())
591             allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity());
592
593         ASSERT(offset < structure->propertyStorageCapacity());
594         setStructure(structure.release());
595         putDirectOffset(globalData, offset, value);
596         // This is a new property; transitions with specific values are not currently cachable,
597         // so leave the slot in an uncachable state.
598         if (!specificFunction)
599             slot.setNewProperty(this, offset);
600         return true;
601     }
602
603     unsigned currentAttributes;
604     JSCell* currentSpecificFunction;
605     offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction);
606     if (offset != WTF::notFound) {
607         if (checkReadOnly && currentAttributes & ReadOnly)
608             return false;
609
610         // There are three possibilities here:
611         //  (1) There is an existing specific value set, and we're overwriting with *the same value*.
612         //       * Do nothing - no need to despecify, but that means we can't cache (a cached
613         //         put could write a different value). Leave the slot in an uncachable state.
614         //  (2) There is a specific value currently set, but we're writing a different value.
615         //       * First, we have to despecify.  Having done so, this is now a regular slot
616         //         with no specific value, so go ahead & cache like normal.
617         //  (3) Normal case, there is no specific value set.
618         //       * Go ahead & cache like normal.
619         if (currentSpecificFunction) {
620             // case (1) Do the put, then return leaving the slot uncachable.
621             if (specificFunction == currentSpecificFunction) {
622                 putDirectOffset(globalData, offset, value);
623                 return true;
624             }
625             // case (2) Despecify, fall through to (3).
626             setStructure(Structure::despecifyFunctionTransition(m_structure, propertyName));
627         }
628
629         // case (3) set the slot, do the put, return.
630         slot.setExistingProperty(this, offset);
631         putDirectOffset(globalData, offset, value);
632         return true;
633     }
634
635     RefPtr<Structure> structure = Structure::addPropertyTransition(m_structure, propertyName, attributes, specificFunction, offset);
636
637     if (currentCapacity != structure->propertyStorageCapacity())
638         allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity());
639
640     ASSERT(offset < structure->propertyStorageCapacity());
641     setStructure(structure.release());
642     putDirectOffset(globalData, offset, value);
643     // This is a new property; transitions with specific values are not currently cachable,
644     // so leave the slot in an uncachable state.
645     if (!specificFunction)
646         slot.setNewProperty(this, offset);
647     return true;
648 }
649
650 inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
651 {
652     ASSERT(value);
653     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
654
655     return putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, getJSFunction(globalData, value));
656 }
657
658 inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
659 {
660     PutPropertySlot slot;
661     putDirectInternal(globalData, propertyName, value, attributes, false, slot, getJSFunction(globalData, value));
662 }
663
664 inline bool JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
665 {
666     ASSERT(value);
667     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
668
669     return putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, 0);
670 }
671
672 inline void JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
673 {
674     PutPropertySlot slot;
675     putDirectInternal(globalData, propertyName, value, attributes, false, slot, 0);
676 }
677
678 inline bool JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
679 {
680     return putDirectInternal(globalData, propertyName, value, 0, false, slot, 0);
681 }
682
683 inline void JSObject::putDirectFunction(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
684 {
685     putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, value);
686 }
687
688 inline void JSObject::putDirectFunction(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attr)
689 {
690     PutPropertySlot slot;
691     putDirectInternal(globalData, propertyName, value, attr, false, slot, value);
692 }
693
694 inline void JSObject::putDirectWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
695 {
696     size_t currentCapacity = m_structure->propertyStorageCapacity();
697     size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, 0);
698     if (currentCapacity != m_structure->propertyStorageCapacity())
699         allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
700     putDirectOffset(globalData, offset, value);
701 }
702
703 inline void JSObject::putDirectFunctionWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attributes)
704 {
705     size_t currentCapacity = m_structure->propertyStorageCapacity();
706     size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, value);
707     if (currentCapacity != m_structure->propertyStorageCapacity())
708         allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
709     putDirectOffset(globalData, offset, value);
710 }
711
712 inline void JSObject::transitionTo(Structure* newStructure)
713 {
714     if (m_structure->propertyStorageCapacity() != newStructure->propertyStorageCapacity())
715         allocatePropertyStorage(m_structure->propertyStorageCapacity(), newStructure->propertyStorageCapacity());
716     setStructure(newStructure);
717 }
718
719 inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
720 {
721     return defaultValue(exec, preferredType);
722 }
723
724 inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName) const
725 {
726     PropertySlot slot(asValue());
727     return get(exec, propertyName, slot);
728 }
729
730 inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) const
731 {
732     if (UNLIKELY(!isCell())) {
733         JSObject* prototype = synthesizePrototype(exec);
734         if (propertyName == exec->propertyNames().underscoreProto)
735             return prototype;
736         if (!prototype->getPropertySlot(exec, propertyName, slot))
737             return jsUndefined();
738         return slot.getValue(exec, propertyName);
739     }
740     JSCell* cell = asCell();
741     while (true) {
742         if (cell->fastGetOwnPropertySlot(exec, propertyName, slot))
743             return slot.getValue(exec, propertyName);
744         JSValue prototype = asObject(cell)->prototype();
745         if (!prototype.isObject())
746             return jsUndefined();
747         cell = asObject(prototype);
748     }
749 }
750
751 inline JSValue JSValue::get(ExecState* exec, unsigned propertyName) const
752 {
753     PropertySlot slot(asValue());
754     return get(exec, propertyName, slot);
755 }
756
757 inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const
758 {
759     if (UNLIKELY(!isCell())) {
760         JSObject* prototype = synthesizePrototype(exec);
761         if (!prototype->getPropertySlot(exec, propertyName, slot))
762             return jsUndefined();
763         return slot.getValue(exec, propertyName);
764     }
765     JSCell* cell = const_cast<JSCell*>(asCell());
766     while (true) {
767         if (cell->getOwnPropertySlot(exec, propertyName, slot))
768             return slot.getValue(exec, propertyName);
769         JSValue prototype = asObject(cell)->prototype();
770         if (!prototype.isObject())
771             return jsUndefined();
772         cell = prototype.asCell();
773     }
774 }
775
776 inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
777 {
778     if (UNLIKELY(!isCell())) {
779         synthesizeObject(exec)->put(exec, propertyName, value, slot);
780         return;
781     }
782     asCell()->put(exec, propertyName, value, slot);
783 }
784
785 inline void JSValue::putDirect(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
786 {
787     ASSERT(isCell() && isObject());
788     if (!asObject(asCell())->putDirect(exec->globalData(), propertyName, value, slot) && slot.isStrictMode())
789         throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
790 }
791
792 inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value)
793 {
794     if (UNLIKELY(!isCell())) {
795         synthesizeObject(exec)->put(exec, propertyName, value);
796         return;
797     }
798     asCell()->put(exec, propertyName, value);
799 }
800
801 ALWAYS_INLINE void JSObject::markChildrenDirect(MarkStack& markStack)
802 {
803     JSCell::markChildren(markStack);
804
805     markStack.append(m_structure->storedPrototypeSlot());
806     PropertyStorage storage = propertyStorage();
807     size_t storageSize = m_structure->propertyStorageSize();
808     markStack.appendValues(storage, storageSize);
809 }
810
811 // --- JSValue inlines ----------------------------
812
813 ALWAYS_INLINE UString JSValue::toThisString(ExecState* exec) const
814 {
815     return isString() ? static_cast<JSString*>(asCell())->value(exec) : toThisObject(exec)->toString(exec);
816 }
817
818 inline JSString* JSValue::toThisJSString(ExecState* exec) const
819 {
820     return isString() ? static_cast<JSString*>(asCell()) : jsString(exec, toThisObject(exec)->toString(exec));
821 }
822
823 inline JSValue JSValue::toStrictThisObject(ExecState* exec) const
824 {
825     if (!isObject())
826         return *this;
827     return asObject(asCell())->toStrictThisObject(exec);
828 }
829
830 } // namespace JSC
831
832 #endif // JSObject_h