eb09755849c92bb165e7a81b650d6c7100397abe
[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, 2012 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 "ArrayConventions.h"
28 #include "ArrayStorage.h"
29 #include "Butterfly.h"
30 #include "ClassInfo.h"
31 #include "CommonIdentifiers.h"
32 #include "CallFrame.h"
33 #include "JSCell.h"
34 #include "PropertySlot.h"
35 #include "PropertyStorage.h"
36 #include "PutDirectIndexMode.h"
37 #include "PutPropertySlot.h"
38
39 #include "Structure.h"
40 #include "JSGlobalData.h"
41 #include "JSString.h"
42 #include "SparseArrayValueMap.h"
43 #include <wtf/StdLibExtras.h>
44
45 namespace JSC {
46
47     inline JSCell* getJSFunction(JSValue value)
48     {
49         if (value.isCell() && (value.asCell()->structure()->typeInfo().type() == JSFunctionType))
50             return value.asCell();
51         return 0;
52     }
53
54     JS_EXPORT_PRIVATE JSCell* getCallableObjectSlow(JSCell*);
55
56     inline JSCell* getCallableObject(JSValue value)
57     {
58         if (!value.isCell())
59             return 0;
60         return getCallableObjectSlow(value.asCell());
61     }
62
63     class GetterSetter;
64     class HashEntry;
65     class InternalFunction;
66     class LLIntOffsetsExtractor;
67     class MarkedBlock;
68     class PropertyDescriptor;
69     class PropertyNameArray;
70     class Structure;
71     struct HashTable;
72
73     JS_EXPORT_PRIVATE JSObject* throwTypeError(ExecState*, const String&);
74     extern JS_EXPORTDATA const char* StrictModeReadonlyPropertyWriteError;
75
76     // ECMA 262-3 8.6.1
77     // Property attributes
78     enum Attribute {
79         None         = 0,
80         ReadOnly     = 1 << 1,  // property can be only read, not written
81         DontEnum     = 1 << 2,  // property doesn't appear in (for .. in ..)
82         DontDelete   = 1 << 3,  // property can't be deleted
83         Function     = 1 << 4,  // property is a function - only used by static hashtables
84         Accessor     = 1 << 5,  // property is a getter/setter
85     };
86
87     COMPILE_ASSERT(None < FirstInternalAttribute, None_is_below_FirstInternalAttribute);
88     COMPILE_ASSERT(ReadOnly < FirstInternalAttribute, ReadOnly_is_below_FirstInternalAttribute);
89     COMPILE_ASSERT(DontEnum < FirstInternalAttribute, DontEnum_is_below_FirstInternalAttribute);
90     COMPILE_ASSERT(DontDelete < FirstInternalAttribute, DontDelete_is_below_FirstInternalAttribute);
91     COMPILE_ASSERT(Function < FirstInternalAttribute, Function_is_below_FirstInternalAttribute);
92     COMPILE_ASSERT(Accessor < FirstInternalAttribute, Accessor_is_below_FirstInternalAttribute);
93
94     class JSFinalObject;
95
96     class JSObject : public JSCell {
97         friend class BatchedTransitionOptimizer;
98         friend class JIT;
99         friend class JSCell;
100         friend class JSFinalObject;
101         friend class MarkedBlock;
102         JS_EXPORT_PRIVATE friend bool setUpStaticFunctionSlot(ExecState*, const HashEntry*, JSObject*, PropertyName, PropertySlot&);
103
104         enum PutMode {
105             PutModePut,
106             PutModeDefineOwnProperty,
107         };
108
109     public:
110         typedef JSCell Base;
111         
112         static size_t allocationSize(size_t inlineCapacity)
113         {
114             return sizeof(JSObject) + inlineCapacity * sizeof(WriteBarrierBase<Unknown>);
115         }
116         
117         JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&);
118
119         JS_EXPORT_PRIVATE static String className(const JSObject*);
120
121         JSValue prototype() const;
122         void setPrototype(JSGlobalData&, JSValue prototype);
123         bool setPrototypeWithCycleCheck(JSGlobalData&, JSValue prototype);
124         
125         Structure* inheritorID(JSGlobalData&);
126         void notifyUsedAsPrototype(JSGlobalData&);
127         
128         bool mayBeUsedAsPrototype(JSGlobalData& globalData)
129         {
130             return isValidOffset(structure()->get(globalData, globalData.m_inheritorIDKey));
131         }
132         
133         bool mayInterceptIndexedAccesses()
134         {
135             return structure()->mayInterceptIndexedAccesses();
136         }
137         
138         JSValue get(ExecState*, PropertyName) const;
139         JSValue get(ExecState*, unsigned propertyName) const;
140
141         bool getPropertySlot(ExecState*, PropertyName, PropertySlot&);
142         bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
143         JS_EXPORT_PRIVATE bool getPropertyDescriptor(ExecState*, PropertyName, PropertyDescriptor&);
144
145         static bool getOwnPropertySlot(JSCell*, ExecState*, PropertyName, PropertySlot&);
146         JS_EXPORT_PRIVATE static bool getOwnPropertySlotByIndex(JSCell*, ExecState*, unsigned propertyName, PropertySlot&);
147         JS_EXPORT_PRIVATE static bool getOwnPropertyDescriptor(JSObject*, ExecState*, PropertyName, PropertyDescriptor&);
148
149         bool allowsAccessFrom(ExecState*);
150
151         unsigned getArrayLength() const
152         {
153             switch (structure()->indexingType()) {
154             case ALL_BLANK_INDEXING_TYPES:
155                 return 0;
156             case ALL_CONTIGUOUS_INDEXING_TYPES:
157             case ALL_ARRAY_STORAGE_INDEXING_TYPES:
158                 return m_butterfly->publicLength();
159             default:
160                 ASSERT_NOT_REACHED();
161                 return 0;
162             }
163         }
164         
165         unsigned getVectorLength()
166         {
167             switch (structure()->indexingType()) {
168             case ALL_BLANK_INDEXING_TYPES:
169                 return 0;
170             case ALL_CONTIGUOUS_INDEXING_TYPES:
171             case ALL_ARRAY_STORAGE_INDEXING_TYPES:
172                 return m_butterfly->vectorLength();
173             default:
174                 ASSERT_NOT_REACHED();
175                 return 0;
176             }
177         }
178         
179         JS_EXPORT_PRIVATE static void put(JSCell*, ExecState*, PropertyName, JSValue, PutPropertySlot&);
180         JS_EXPORT_PRIVATE static void putByIndex(JSCell*, ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
181         
182         void putByIndexInline(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
183         {
184             if (canSetIndexQuickly(propertyName)) {
185                 setIndexQuickly(exec->globalData(), propertyName, value);
186                 return;
187             }
188             methodTable()->putByIndex(this, exec, propertyName, value, shouldThrow);
189         }
190         
191         // This is similar to the putDirect* methods:
192         //  - the prototype chain is not consulted
193         //  - accessors are not called.
194         //  - it will ignore extensibility and read-only properties if PutDirectIndexLikePutDirect is passed as the mode (the default).
195         // This method creates a property with attributes writable, enumerable and configurable all set to true.
196         bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value, unsigned attributes, PutDirectIndexMode mode)
197         {
198             if (!attributes && canSetIndexQuicklyForPutDirect(propertyName)) {
199                 setIndexQuickly(exec->globalData(), propertyName, value);
200                 return true;
201             }
202             return putDirectIndexBeyondVectorLength(exec, propertyName, value, attributes, mode);
203         }
204         bool putDirectIndex(ExecState* exec, unsigned propertyName, JSValue value)
205         {
206             return putDirectIndex(exec, propertyName, value, 0, PutDirectIndexLikePutDirect);
207         }
208
209         // A non-throwing version of putDirect and putDirectIndex.
210         JS_EXPORT_PRIVATE void putDirectMayBeIndex(ExecState*, PropertyName, JSValue);
211         
212         bool canGetIndexQuickly(unsigned i)
213         {
214             switch (structure()->indexingType()) {
215             case ALL_BLANK_INDEXING_TYPES:
216                 return false;
217             case ALL_CONTIGUOUS_INDEXING_TYPES:
218                 return i < m_butterfly->vectorLength() && m_butterfly->contiguous()[i];
219             case ALL_ARRAY_STORAGE_INDEXING_TYPES:
220                 return i < m_butterfly->arrayStorage()->vectorLength() && m_butterfly->arrayStorage()->m_vector[i];
221             default:
222                 ASSERT_NOT_REACHED();
223                 return false;
224             }
225         }
226         
227         JSValue getIndexQuickly(unsigned i)
228         {
229             switch (structure()->indexingType()) {
230             case ALL_CONTIGUOUS_INDEXING_TYPES:
231                 return m_butterfly->contiguous()[i].get();
232             case ALL_ARRAY_STORAGE_INDEXING_TYPES:
233                 return m_butterfly->arrayStorage()->m_vector[i].get();
234             default:
235                 ASSERT_NOT_REACHED();
236                 return JSValue();
237             }
238         }
239         
240         JSValue tryGetIndexQuickly(unsigned i)
241         {
242             switch (structure()->indexingType()) {
243             case ALL_BLANK_INDEXING_TYPES:
244                 break;
245             case ALL_CONTIGUOUS_INDEXING_TYPES:
246                 if (i < m_butterfly->publicLength())
247                     return m_butterfly->contiguous()[i].get();
248                 break;
249             case ALL_ARRAY_STORAGE_INDEXING_TYPES:
250                 if (i < m_butterfly->arrayStorage()->vectorLength())
251                     return m_butterfly->arrayStorage()->m_vector[i].get();
252                 break;
253             default:
254                 ASSERT_NOT_REACHED();
255                 break;
256             }
257             return JSValue();
258         }
259         
260         JSValue getDirectIndex(ExecState* exec, unsigned i)
261         {
262             if (JSValue result = tryGetIndexQuickly(i))
263                 return result;
264             PropertySlot slot(this);
265             if (methodTable()->getOwnPropertySlotByIndex(this, exec, i, slot))
266                 return slot.getValue(exec, i);
267             return JSValue();
268         }
269         
270         JSValue getIndex(ExecState* exec, unsigned i)
271         {
272             if (JSValue result = tryGetIndexQuickly(i))
273                 return result;
274             return get(exec, i);
275         }
276         
277         bool canSetIndexQuickly(unsigned i)
278         {
279             switch (structure()->indexingType()) {
280             case ALL_BLANK_INDEXING_TYPES:
281                 return false;
282             case ALL_CONTIGUOUS_INDEXING_TYPES:
283             case NonArrayWithArrayStorage:
284             case ArrayWithArrayStorage:
285                 return i < m_butterfly->vectorLength();
286             case NonArrayWithSlowPutArrayStorage:
287             case ArrayWithSlowPutArrayStorage:
288                 return i < m_butterfly->arrayStorage()->vectorLength()
289                     && !!m_butterfly->arrayStorage()->m_vector[i];
290             default:
291                 ASSERT_NOT_REACHED();
292                 return false;
293             }
294         }
295         
296         bool canSetIndexQuicklyForPutDirect(unsigned i)
297         {
298             switch (structure()->indexingType()) {
299             case ALL_BLANK_INDEXING_TYPES:
300                 return false;
301             case ALL_CONTIGUOUS_INDEXING_TYPES:
302             case ALL_ARRAY_STORAGE_INDEXING_TYPES:
303                 return i < m_butterfly->vectorLength();
304             default:
305                 ASSERT_NOT_REACHED();
306                 return false;
307             }
308         }
309         
310         void setIndexQuickly(JSGlobalData& globalData, unsigned i, JSValue v)
311         {
312             switch (structure()->indexingType()) {
313             case ALL_CONTIGUOUS_INDEXING_TYPES: {
314                 ASSERT(i < m_butterfly->vectorLength());
315                 m_butterfly->contiguous()[i].set(globalData, this, v);
316                 if (i >= m_butterfly->publicLength())
317                     m_butterfly->setPublicLength(i + 1);
318                 break;
319             }
320             case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
321                 ArrayStorage* storage = m_butterfly->arrayStorage();
322                 WriteBarrier<Unknown>& x = storage->m_vector[i];
323                 JSValue old = x.get();
324                 x.set(globalData, this, v);
325                 if (!old) {
326                     ++storage->m_numValuesInVector;
327                     if (i >= storage->length())
328                         storage->setLength(i + 1);
329                 }
330                 break;
331             }
332             default:
333                 ASSERT_NOT_REACHED();
334             }
335         }
336         
337         void initializeIndex(JSGlobalData& globalData, unsigned i, JSValue v)
338         {
339             switch (structure()->indexingType()) {
340             case ALL_CONTIGUOUS_INDEXING_TYPES: {
341                 ASSERT(i < m_butterfly->publicLength());
342                 ASSERT(i < m_butterfly->vectorLength());
343                 m_butterfly->contiguous()[i].set(globalData, this, v);
344                 break;
345             }
346             case ALL_ARRAY_STORAGE_INDEXING_TYPES: {
347                 ArrayStorage* storage = m_butterfly->arrayStorage();
348                 ASSERT(i < storage->length());
349                 ASSERT(i < storage->m_numValuesInVector);
350                 storage->m_vector[i].set(globalData, this, v);
351                 break;
352             }
353             default:
354                 ASSERT_NOT_REACHED();
355             }
356         }
357         
358         bool hasSparseMap()
359         {
360             switch (structure()->indexingType()) {
361             case ALL_BLANK_INDEXING_TYPES:
362             case ALL_CONTIGUOUS_INDEXING_TYPES:
363                 return false;
364             case ALL_ARRAY_STORAGE_INDEXING_TYPES:
365                 return m_butterfly->arrayStorage()->m_sparseMap;
366             default:
367                 ASSERT_NOT_REACHED();
368                 return false;
369             }
370         }
371         
372         bool inSparseIndexingMode()
373         {
374             switch (structure()->indexingType()) {
375             case ALL_BLANK_INDEXING_TYPES:
376             case ALL_CONTIGUOUS_INDEXING_TYPES:
377                 return false;
378             case ALL_ARRAY_STORAGE_INDEXING_TYPES:
379                 return m_butterfly->arrayStorage()->inSparseMode();
380             default:
381                 ASSERT_NOT_REACHED();
382                 return false;
383             }
384         }
385         
386         void enterDictionaryIndexingMode(JSGlobalData&);
387
388         // putDirect is effectively an unchecked vesion of 'defineOwnProperty':
389         //  - the prototype chain is not consulted
390         //  - accessors are not called.
391         //  - attributes will be respected (after the call the property will exist with the given attributes)
392         //  - the property name is assumed to not be an index.
393         JS_EXPORT_PRIVATE static void putDirectVirtual(JSObject*, ExecState*, PropertyName, JSValue, unsigned attributes);
394         void putDirect(JSGlobalData&, PropertyName, JSValue, unsigned attributes = 0);
395         void putDirect(JSGlobalData&, PropertyName, JSValue, PutPropertySlot&);
396         void putDirectWithoutTransition(JSGlobalData&, PropertyName, JSValue, unsigned attributes = 0);
397         void putDirectAccessor(ExecState*, PropertyName, JSValue, unsigned attributes);
398
399         bool propertyIsEnumerable(ExecState*, const Identifier& propertyName) const;
400
401         JS_EXPORT_PRIVATE bool hasProperty(ExecState*, PropertyName) const;
402         JS_EXPORT_PRIVATE bool hasProperty(ExecState*, unsigned propertyName) const;
403         bool hasOwnProperty(ExecState*, PropertyName) const;
404
405         JS_EXPORT_PRIVATE static bool deleteProperty(JSCell*, ExecState*, PropertyName);
406         JS_EXPORT_PRIVATE static bool deletePropertyByIndex(JSCell*, ExecState*, unsigned propertyName);
407
408         JS_EXPORT_PRIVATE static JSValue defaultValue(const JSObject*, ExecState*, PreferredPrimitiveType);
409
410         bool hasInstance(ExecState*, JSValue);
411         static bool defaultHasInstance(ExecState*, JSValue, JSValue prototypeProperty);
412
413         JS_EXPORT_PRIVATE static void getOwnPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
414         JS_EXPORT_PRIVATE static void getOwnNonIndexPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
415         JS_EXPORT_PRIVATE static void getPropertyNames(JSObject*, ExecState*, PropertyNameArray&, EnumerationMode);
416
417         JSValue toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const;
418         bool getPrimitiveNumber(ExecState*, double& number, JSValue&) const;
419         JS_EXPORT_PRIVATE double toNumber(ExecState*) const;
420         JS_EXPORT_PRIVATE JSString* toString(ExecState*) const;
421
422         // NOTE: JSObject and its subclasses must be able to gracefully handle ExecState* = 0,
423         // because this call may come from inside the compiler.
424         JS_EXPORT_PRIVATE static JSObject* toThisObject(JSCell*, ExecState*);
425
426         bool getPropertySpecificValue(ExecState*, PropertyName, JSCell*& specificFunction) const;
427
428         // This get function only looks at the property map.
429         JSValue getDirect(JSGlobalData& globalData, PropertyName propertyName) const
430         {
431             PropertyOffset offset = structure()->get(globalData, propertyName);
432             checkOffset(offset, structure()->typeInfo().type());
433             return offset != invalidOffset ? getDirectOffset(offset) : JSValue();
434         }
435
436         WriteBarrierBase<Unknown>* getDirectLocation(JSGlobalData& globalData, PropertyName propertyName)
437         {
438             PropertyOffset offset = structure()->get(globalData, propertyName);
439             checkOffset(offset, structure()->typeInfo().type());
440             return isValidOffset(offset) ? locationForOffset(offset) : 0;
441         }
442
443         WriteBarrierBase<Unknown>* getDirectLocation(JSGlobalData& globalData, PropertyName propertyName, unsigned& attributes)
444         {
445             JSCell* specificFunction;
446             PropertyOffset offset = structure()->get(globalData, propertyName, attributes, specificFunction);
447             return isValidOffset(offset) ? locationForOffset(offset) : 0;
448         }
449
450         bool hasInlineStorage() const { return structure()->hasInlineStorage(); }
451         ConstPropertyStorage inlineStorageUnsafe() const
452         {
453             return bitwise_cast<ConstPropertyStorage>(this + 1);
454         }
455         PropertyStorage inlineStorageUnsafe()
456         {
457             return bitwise_cast<PropertyStorage>(this + 1);
458         }
459         ConstPropertyStorage inlineStorage() const
460         {
461             ASSERT(hasInlineStorage());
462             return inlineStorageUnsafe();
463         }
464         PropertyStorage inlineStorage()
465         {
466             ASSERT(hasInlineStorage());
467             return inlineStorageUnsafe();
468         }
469         
470         const Butterfly* butterfly() const { return m_butterfly; }
471         Butterfly* butterfly() { return m_butterfly; }
472         
473         ConstPropertyStorage outOfLineStorage() const { return m_butterfly->propertyStorage(); }
474         PropertyStorage outOfLineStorage() { return m_butterfly->propertyStorage(); }
475
476         const WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset) const
477         {
478             if (isInlineOffset(offset))
479                 return &inlineStorage()[offsetInInlineStorage(offset)];
480             return &outOfLineStorage()[offsetInOutOfLineStorage(offset)];
481         }
482
483         WriteBarrierBase<Unknown>* locationForOffset(PropertyOffset offset)
484         {
485             if (isInlineOffset(offset))
486                 return &inlineStorage()[offsetInInlineStorage(offset)];
487             return &outOfLineStorage()[offsetInOutOfLineStorage(offset)];
488         }
489
490         PropertyOffset offsetForLocation(WriteBarrierBase<Unknown>* location) const
491         {
492             PropertyOffset result;
493             size_t offsetInInlineStorage = location - inlineStorageUnsafe();
494             if (offsetInInlineStorage < static_cast<size_t>(firstOutOfLineOffset))
495                 result = offsetInInlineStorage;
496             else
497                 result = outOfLineStorage() - location + (firstOutOfLineOffset - 1);
498             validateOffset(result, structure()->typeInfo().type());
499             return result;
500         }
501
502         void transitionTo(JSGlobalData&, Structure*);
503
504         bool removeDirect(JSGlobalData&, PropertyName); // Return true if anything is removed.
505         bool hasCustomProperties() { return structure()->didTransition(); }
506         bool hasGetterSetterProperties() { return structure()->hasGetterSetterProperties(); }
507
508         // putOwnDataProperty has 'put' like semantics, however this method:
509         //  - assumes the object contains no own getter/setter properties.
510         //  - provides no special handling for __proto__
511         //  - does not walk the prototype chain (to check for accessors or non-writable properties).
512         // This is used by JSActivation.
513         bool putOwnDataProperty(JSGlobalData&, PropertyName, JSValue, PutPropertySlot&);
514
515         // Fast access to known property offsets.
516         JSValue getDirectOffset(PropertyOffset offset) const { return locationForOffset(offset)->get(); }
517         void putDirectOffset(JSGlobalData& globalData, PropertyOffset offset, JSValue value) { locationForOffset(offset)->set(globalData, this, value); }
518         void putUndefinedAtDirectOffset(PropertyOffset offset) { locationForOffset(offset)->setUndefined(); }
519
520         JS_EXPORT_PRIVATE static bool defineOwnProperty(JSObject*, ExecState*, PropertyName, PropertyDescriptor&, bool shouldThrow);
521
522         bool isGlobalObject() const;
523         bool isVariableObject() const;
524         bool isNameScopeObject() const;
525         bool isActivationObject() const;
526         bool isErrorInstance() const;
527         bool isProxy() const;
528
529         void seal(JSGlobalData&);
530         void freeze(JSGlobalData&);
531         JS_EXPORT_PRIVATE void preventExtensions(JSGlobalData&);
532         bool isSealed(JSGlobalData& globalData) { return structure()->isSealed(globalData); }
533         bool isFrozen(JSGlobalData& globalData) { return structure()->isFrozen(globalData); }
534         bool isExtensible() { return structure()->isExtensible(); }
535         bool indexingShouldBeSparse()
536         {
537             return !isExtensible()
538                 || structure()->typeInfo().interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero();
539         }
540
541         bool staticFunctionsReified() { return structure()->staticFunctionsReified(); }
542         void reifyStaticFunctionsForDelete(ExecState* exec);
543
544         JS_EXPORT_PRIVATE Butterfly* growOutOfLineStorage(JSGlobalData&, size_t oldSize, size_t newSize);
545         void setButterfly(JSGlobalData&, Butterfly*, Structure*);
546         void setButterflyWithoutChangingStructure(Butterfly*); // You probably don't want to call this.
547         
548         void setStructureAndReallocateStorageIfNecessary(JSGlobalData&, unsigned oldCapacity, Structure*);
549         void setStructureAndReallocateStorageIfNecessary(JSGlobalData&, Structure*);
550
551         void flattenDictionaryObject(JSGlobalData& globalData)
552         {
553             structure()->flattenDictionaryStructure(globalData, this);
554         }
555
556         JSGlobalObject* globalObject() const
557         {
558             ASSERT(structure()->globalObject());
559             ASSERT(!isGlobalObject() || ((JSObject*)structure()->globalObject()) == this);
560             return structure()->globalObject();
561         }
562         
563         void switchToSlowPutArrayStorage(JSGlobalData&);
564         
565         // The receiver is the prototype in this case. The following:
566         //
567         // asObject(foo->structure()->storedPrototype())->attemptToInterceptPutByIndexOnHoleForPrototype(...)
568         //
569         // is equivalent to:
570         //
571         // foo->attemptToInterceptPutByIndexOnHole(...);
572         bool attemptToInterceptPutByIndexOnHoleForPrototype(ExecState*, JSValue thisValue, unsigned propertyName, JSValue, bool shouldThrow);
573         
574         // Returns 0 if contiguous storage cannot be created - either because
575         // indexing should be sparse or because we're having a bad time.
576         WriteBarrier<Unknown>* ensureContiguous(JSGlobalData& globalData)
577         {
578             if (LIKELY(hasContiguous(structure()->indexingType())))
579                 return m_butterfly->contiguous();
580             
581             return ensureContiguousSlow(globalData);
582         }
583         
584         // Ensure that the object is in a mode where it has array storage. Use
585         // this if you're about to perform actions that would have required the
586         // object to be converted to have array storage, if it didn't have it
587         // already.
588         ArrayStorage* ensureArrayStorage(JSGlobalData& globalData)
589         {
590             if (LIKELY(hasArrayStorage(structure()->indexingType())))
591                 return m_butterfly->arrayStorage();
592             
593             return ensureArrayStorageSlow(globalData);
594         }
595         
596         Butterfly* ensureIndexedStorage(JSGlobalData& globalData)
597         {
598             if (LIKELY(hasIndexedProperties(structure()->indexingType())))
599                 return m_butterfly;
600             
601             return ensureIndexedStorageSlow(globalData);
602         }
603         
604         static size_t offsetOfInlineStorage();
605         
606         static ptrdiff_t butterflyOffset()
607         {
608             return OBJECT_OFFSETOF(JSObject, m_butterfly);
609         }
610         
611         void* butterflyAddress()
612         {
613             return &m_butterfly;
614         }
615
616         static JS_EXPORTDATA const ClassInfo s_info;
617
618     protected:
619         void finishCreation(JSGlobalData& globalData)
620         {
621             Base::finishCreation(globalData);
622             ASSERT(inherits(&s_info));
623             ASSERT(!structure()->outOfLineCapacity());
624             ASSERT(structure()->isEmpty());
625             ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype()));
626             ASSERT(structure()->isObject());
627             ASSERT(classInfo());
628         }
629
630         static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
631         {
632             return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
633         }
634
635         // To instantiate objects you likely want JSFinalObject, below.
636         // To create derived types you likely want JSNonFinalObject, below.
637         JSObject(JSGlobalData&, Structure*, Butterfly* = 0);
638         
639         void resetInheritorID(JSGlobalData&);
640         
641         void visitButterfly(SlotVisitor&, Butterfly*, size_t storageSize);
642
643         // Call this if you know that the object is in a mode where it has array
644         // storage. This will assert otherwise.
645         ArrayStorage* arrayStorage()
646         {
647             ASSERT(hasArrayStorage(structure()->indexingType()));
648             return m_butterfly->arrayStorage();
649         }
650         
651         // Call this if you want to predicate some actions on whether or not the
652         // object is in a mode where it has array storage.
653         ArrayStorage* arrayStorageOrNull()
654         {
655             switch (structure()->indexingType()) {
656             case ALL_ARRAY_STORAGE_INDEXING_TYPES:
657                 return m_butterfly->arrayStorage();
658                 
659             default:
660                 return 0;
661             }
662         }
663
664         ArrayStorage* createArrayStorage(JSGlobalData&, unsigned length, unsigned vectorLength);
665         ArrayStorage* createInitialArrayStorage(JSGlobalData&);
666         WriteBarrier<Unknown>* createInitialContiguous(JSGlobalData&, unsigned length);
667         ArrayStorage* convertContiguousToArrayStorage(JSGlobalData&, NonPropertyTransition, unsigned neededLength);
668         ArrayStorage* convertContiguousToArrayStorage(JSGlobalData&, NonPropertyTransition);
669         ArrayStorage* convertContiguousToArrayStorage(JSGlobalData&);
670         
671         ArrayStorage* ensureArrayStorageExistsAndEnterDictionaryIndexingMode(JSGlobalData&);
672         
673         bool defineOwnNonIndexProperty(ExecState*, PropertyName, PropertyDescriptor&, bool throwException);
674
675         void putByIndexBeyondVectorLengthContiguousWithoutAttributes(ExecState*, unsigned propertyName, JSValue);
676         void putByIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, bool shouldThrow, ArrayStorage*);
677
678         bool increaseVectorLength(JSGlobalData&, unsigned newLength);
679         void deallocateSparseIndexMap();
680         bool defineOwnIndexedProperty(ExecState*, unsigned, PropertyDescriptor&, bool throwException);
681         SparseArrayValueMap* allocateSparseIndexMap(JSGlobalData&);
682         
683         void notifyPresenceOfIndexedAccessors(JSGlobalData&);
684         
685         bool attemptToInterceptPutByIndexOnHole(ExecState*, unsigned index, JSValue, bool shouldThrow);
686         
687         // Call this if you want setIndexQuickly to succeed and you're sure that
688         // the array is contiguous.
689         void ensureContiguousLength(JSGlobalData& globalData, unsigned length)
690         {
691             ASSERT(length < MAX_ARRAY_INDEX);
692             ASSERT(hasContiguous(structure()->indexingType()));
693             
694             if (m_butterfly->vectorLength() < length)
695                 ensureContiguousLengthSlow(globalData, length);
696             
697             if (m_butterfly->publicLength() < length)
698                 m_butterfly->setPublicLength(length);
699         }
700         
701         unsigned countElementsInContiguous(Butterfly*);
702         
703         template<IndexingType indexingType>
704         WriteBarrier<Unknown>* indexingData()
705         {
706             switch (indexingType) {
707             case ALL_CONTIGUOUS_INDEXING_TYPES:
708                 return m_butterfly->contiguous();
709                 
710             case ALL_ARRAY_STORAGE_INDEXING_TYPES:
711                 return m_butterfly->arrayStorage()->m_vector;
712                 
713             default:
714                 CRASH();
715                 return 0;
716             }
717         }
718         
719         template<IndexingType indexingType>
720         unsigned relevantLength()
721         {
722             switch (indexingType) {
723             case ALL_CONTIGUOUS_INDEXING_TYPES:
724                 return m_butterfly->publicLength();
725                 
726             case ALL_ARRAY_STORAGE_INDEXING_TYPES:
727                 return std::min(
728                     m_butterfly->arrayStorage()->length(),
729                     m_butterfly->arrayStorage()->vectorLength());
730                 
731             default:
732                 CRASH();
733                 return 0;
734             }
735         }
736
737     private:
738         friend class LLIntOffsetsExtractor;
739         
740         // Nobody should ever ask any of these questions on something already known to be a JSObject.
741         using JSCell::isAPIValueWrapper;
742         using JSCell::isGetterSetter;
743         void getObject();
744         void getString(ExecState* exec);
745         void isObject();
746         void isString();
747         
748         ArrayStorage* enterDictionaryIndexingModeWhenArrayStorageAlreadyExists(JSGlobalData&, ArrayStorage*);
749         
750         template<PutMode>
751         bool putDirectInternal(JSGlobalData&, PropertyName, JSValue, unsigned attr, PutPropertySlot&, JSCell*);
752
753         bool inlineGetOwnPropertySlot(ExecState*, PropertyName, PropertySlot&);
754         JS_EXPORT_PRIVATE void fillGetterPropertySlot(PropertySlot&, PropertyOffset);
755
756         const HashEntry* findPropertyHashEntry(ExecState*, PropertyName) const;
757         Structure* createInheritorID(JSGlobalData&);
758         
759         void putIndexedDescriptor(ExecState*, SparseArrayEntry*, PropertyDescriptor&, PropertyDescriptor& old);
760         
761         void putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
762         bool putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode, ArrayStorage*);
763         JS_EXPORT_PRIVATE bool putDirectIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode);
764         
765         unsigned getNewVectorLength(unsigned currentVectorLength, unsigned currentLength, unsigned desiredLength);
766         unsigned getNewVectorLength(unsigned desiredLength);
767
768         JS_EXPORT_PRIVATE bool getOwnPropertySlotSlow(ExecState*, PropertyName, PropertySlot&);
769         
770         void ensureContiguousLengthSlow(JSGlobalData&, unsigned length);
771         
772         WriteBarrier<Unknown>* ensureContiguousSlow(JSGlobalData&);
773         ArrayStorage* ensureArrayStorageSlow(JSGlobalData&);
774         Butterfly* ensureIndexedStorageSlow(JSGlobalData&);
775         
776     protected:
777         Butterfly* m_butterfly;
778     };
779
780
781     // JSNonFinalObject is a type of JSObject that has some internal storage,
782     // but also preserves some space in the collector cell for additional
783     // data members in derived types.
784     class JSNonFinalObject : public JSObject {
785         friend class JSObject;
786
787     public:
788         typedef JSObject Base;
789
790         static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
791         {
792             return Structure::create(globalData, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), &s_info);
793         }
794
795     protected:
796         explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure, Butterfly* butterfly = 0)
797             : JSObject(globalData, structure, butterfly)
798         {
799         }
800
801         void finishCreation(JSGlobalData& globalData)
802         {
803             Base::finishCreation(globalData);
804             ASSERT(!this->structure()->totalStorageCapacity());
805             ASSERT(classInfo());
806         }
807     };
808
809     class JSFinalObject;
810
811     // JSFinalObject is a type of JSObject that contains sufficent internal
812     // storage to fully make use of the colloctor cell containing it.
813     class JSFinalObject : public JSObject {
814         friend class JSObject;
815
816     public:
817         typedef JSObject Base;
818
819         static JSFinalObject* create(ExecState*, Structure*);
820         static Structure* createStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
821         {
822             return Structure::create(globalData, globalObject, prototype, TypeInfo(FinalObjectType, StructureFlags), &s_info, NonArray, INLINE_STORAGE_CAPACITY);
823         }
824
825         JS_EXPORT_PRIVATE static void visitChildren(JSCell*, SlotVisitor&);
826
827         static JS_EXPORTDATA const ClassInfo s_info;
828
829     protected:
830         void visitChildrenCommon(SlotVisitor&);
831         
832         void finishCreation(JSGlobalData& globalData)
833         {
834             Base::finishCreation(globalData);
835             ASSERT(structure()->totalStorageCapacity() == structure()->inlineCapacity());
836             ASSERT(classInfo());
837         }
838
839     private:
840         friend class LLIntOffsetsExtractor;
841
842         explicit JSFinalObject(JSGlobalData& globalData, Structure* structure)
843             : JSObject(globalData, structure)
844         {
845         }
846
847         static const unsigned StructureFlags = JSObject::StructureFlags;
848     };
849
850 inline JSFinalObject* JSFinalObject::create(ExecState* exec, Structure* structure)
851 {
852     JSFinalObject* finalObject = new (
853         NotNull, 
854         allocateCell<JSFinalObject>(
855             *exec->heap(),
856             allocationSize(structure->inlineCapacity())
857         )
858     ) JSFinalObject(exec->globalData(), structure);
859     finalObject->finishCreation(exec->globalData());
860     return finalObject;
861 }
862
863 inline bool isJSFinalObject(JSCell* cell)
864 {
865     return cell->classInfo() == &JSFinalObject::s_info;
866 }
867
868 inline bool isJSFinalObject(JSValue value)
869 {
870     return value.isCell() && isJSFinalObject(value.asCell());
871 }
872
873 inline size_t JSObject::offsetOfInlineStorage()
874 {
875     return sizeof(JSObject);
876 }
877
878 inline bool JSObject::isGlobalObject() const
879 {
880     return structure()->typeInfo().type() == GlobalObjectType;
881 }
882
883 inline bool JSObject::isVariableObject() const
884 {
885     return structure()->typeInfo().type() >= VariableObjectType;
886 }
887
888 inline bool JSObject::isNameScopeObject() const
889 {
890     return structure()->typeInfo().type() == NameScopeObjectType;
891 }
892
893 inline bool JSObject::isActivationObject() const
894 {
895     return structure()->typeInfo().type() == ActivationObjectType;
896 }
897
898 inline bool JSObject::isErrorInstance() const
899 {
900     return structure()->typeInfo().type() == ErrorInstanceType;
901 }
902
903 inline bool JSObject::isProxy() const
904 {
905     return structure()->typeInfo().type() == ProxyType;
906 }
907
908 inline void JSObject::setButterfly(JSGlobalData& globalData, Butterfly* butterfly, Structure* structure)
909 {
910     ASSERT(structure);
911     ASSERT(!butterfly == (!structure->outOfLineCapacity() && !hasIndexingHeader(structure->indexingType())));
912     setStructure(globalData, structure);
913     m_butterfly = butterfly;
914 }
915
916 inline void JSObject::setButterflyWithoutChangingStructure(Butterfly* butterfly)
917 {
918     m_butterfly = butterfly;
919 }
920
921 inline JSObject* constructEmptyObject(ExecState* exec, Structure* structure)
922 {
923     return JSFinalObject::create(exec, structure);
924 }
925
926 inline CallType getCallData(JSValue value, CallData& callData)
927 {
928     CallType result = value.isCell() ? value.asCell()->methodTable()->getCallData(value.asCell(), callData) : CallTypeNone;
929     ASSERT(result == CallTypeNone || value.isValidCallee());
930     return result;
931 }
932
933 inline ConstructType getConstructData(JSValue value, ConstructData& constructData)
934 {
935     ConstructType result = value.isCell() ? value.asCell()->methodTable()->getConstructData(value.asCell(), constructData) : ConstructTypeNone;
936     ASSERT(result == ConstructTypeNone || value.isValidCallee());
937     return result;
938 }
939
940 inline Structure* createEmptyObjectStructure(JSGlobalData& globalData, JSGlobalObject* globalObject, JSValue prototype)
941 {
942     return JSFinalObject::createStructure(globalData, globalObject, prototype);
943 }
944
945 inline JSObject* asObject(JSCell* cell)
946 {
947     ASSERT(cell->isObject());
948     return jsCast<JSObject*>(cell);
949 }
950
951 inline JSObject* asObject(JSValue value)
952 {
953     return asObject(value.asCell());
954 }
955
956 inline JSObject::JSObject(JSGlobalData& globalData, Structure* structure, Butterfly* butterfly)
957     : JSCell(globalData, structure)
958     , m_butterfly(butterfly)
959 {
960 }
961
962 inline JSValue JSObject::prototype() const
963 {
964     return structure()->storedPrototype();
965 }
966
967 inline bool JSCell::inherits(const ClassInfo* info) const
968 {
969     return classInfo()->isSubClassOf(info);
970 }
971
972 inline const MethodTable* JSCell::methodTable() const
973 {
974     return &classInfo()->methodTable;
975 }
976
977 // this method is here to be after the inline declaration of JSCell::inherits
978 inline bool JSValue::inherits(const ClassInfo* classInfo) const
979 {
980     return isCell() && asCell()->inherits(classInfo);
981 }
982
983 inline JSObject* JSValue::toThisObject(ExecState* exec) const
984 {
985     return isCell() ? asCell()->methodTable()->toThisObject(asCell(), exec) : toThisObjectSlowCase(exec);
986 }
987
988 ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
989 {
990     PropertyOffset offset = structure()->get(exec->globalData(), propertyName);
991     if (LIKELY(isValidOffset(offset))) {
992         JSValue value = getDirectOffset(offset);
993         if (structure()->hasGetterSetterProperties() && value.isGetterSetter())
994             fillGetterPropertySlot(slot, offset);
995         else
996             slot.setValue(this, value, offset);
997         return true;
998     }
999
1000     return getOwnPropertySlotSlow(exec, propertyName, slot);
1001 }
1002
1003 // It may seem crazy to inline a function this large, especially a virtual function,
1004 // but it makes a big difference to property lookup that derived classes can inline their
1005 // base class call to this.
1006 ALWAYS_INLINE bool JSObject::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
1007 {
1008     return jsCast<JSObject*>(cell)->inlineGetOwnPropertySlot(exec, propertyName, slot);
1009 }
1010
1011 ALWAYS_INLINE bool JSCell::fastGetOwnPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
1012 {
1013     if (!structure()->typeInfo().overridesGetOwnPropertySlot())
1014         return asObject(this)->inlineGetOwnPropertySlot(exec, propertyName, slot);
1015     return methodTable()->getOwnPropertySlot(this, exec, propertyName, slot);
1016 }
1017
1018 // Fast call to get a property where we may not yet have converted the string to an
1019 // identifier. The first time we perform a property access with a given string, try
1020 // performing the property map lookup without forming an identifier. We detect this
1021 // case by checking whether the hash has yet been set for this string.
1022 ALWAYS_INLINE JSValue JSCell::fastGetOwnProperty(ExecState* exec, const String& name)
1023 {
1024     if (!structure()->typeInfo().overridesGetOwnPropertySlot() && !structure()->hasGetterSetterProperties()) {
1025         PropertyOffset offset = name.impl()->hasHash()
1026             ? structure()->get(exec->globalData(), Identifier(exec, name))
1027             : structure()->get(exec->globalData(), name);
1028         if (offset != invalidOffset)
1029             return asObject(this)->locationForOffset(offset)->get();
1030     }
1031     return JSValue();
1032 }
1033
1034 // It may seem crazy to inline a function this large but it makes a big difference
1035 // since this is function very hot in variable lookup
1036 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, PropertyName propertyName, PropertySlot& slot)
1037 {
1038     JSObject* object = this;
1039     while (true) {
1040         if (object->fastGetOwnPropertySlot(exec, propertyName, slot))
1041             return true;
1042         JSValue prototype = object->prototype();
1043         if (!prototype.isObject())
1044             return false;
1045         object = asObject(prototype);
1046     }
1047 }
1048
1049 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
1050 {
1051     JSObject* object = this;
1052     while (true) {
1053         if (object->methodTable()->getOwnPropertySlotByIndex(object, exec, propertyName, slot))
1054             return true;
1055         JSValue prototype = object->prototype();
1056         if (!prototype.isObject())
1057             return false;
1058         object = asObject(prototype);
1059     }
1060 }
1061
1062 inline JSValue JSObject::get(ExecState* exec, PropertyName propertyName) const
1063 {
1064     PropertySlot slot(this);
1065     if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
1066         return slot.getValue(exec, propertyName);
1067     
1068     return jsUndefined();
1069 }
1070
1071 inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const
1072 {
1073     PropertySlot slot(this);
1074     if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
1075         return slot.getValue(exec, propertyName);
1076
1077     return jsUndefined();
1078 }
1079
1080 template<JSObject::PutMode mode>
1081 inline bool JSObject::putDirectInternal(JSGlobalData& globalData, PropertyName propertyName, JSValue value, unsigned attributes, PutPropertySlot& slot, JSCell* specificFunction)
1082 {
1083     ASSERT(value);
1084     ASSERT(value.isGetterSetter() == !!(attributes & Accessor));
1085     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
1086     ASSERT(propertyName.asIndex() == PropertyName::NotAnIndex);
1087
1088     if (structure()->isDictionary()) {
1089         unsigned currentAttributes;
1090         JSCell* currentSpecificFunction;
1091         PropertyOffset offset = structure()->get(globalData, propertyName, currentAttributes, currentSpecificFunction);
1092         if (offset != invalidOffset) {
1093             // If there is currently a specific function, and there now either isn't,
1094             // or the new value is different, then despecify.
1095             if (currentSpecificFunction && (specificFunction != currentSpecificFunction))
1096                 structure()->despecifyDictionaryFunction(globalData, propertyName);
1097             if ((mode == PutModePut) && currentAttributes & ReadOnly)
1098                 return false;
1099
1100             putDirectOffset(globalData, offset, value);
1101             // At this point, the objects structure only has a specific value set if previously there
1102             // had been one set, and if the new value being specified is the same (otherwise we would
1103             // have despecified, above).  So, if currentSpecificFunction is not set, or if the new
1104             // value is different (or there is no new value), then the slot now has no value - and
1105             // as such it is cachable.
1106             // If there was previously a value, and the new value is the same, then we cannot cache.
1107             if (!currentSpecificFunction || (specificFunction != currentSpecificFunction))
1108                 slot.setExistingProperty(this, offset);
1109             return true;
1110         }
1111
1112         if ((mode == PutModePut) && !isExtensible())
1113             return false;
1114
1115         Butterfly* newButterfly = m_butterfly;
1116         if (structure()->putWillGrowOutOfLineStorage())
1117             newButterfly = growOutOfLineStorage(globalData, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity());
1118         offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, specificFunction);
1119         setButterfly(globalData, newButterfly, structure());
1120
1121         validateOffset(offset);
1122         ASSERT(structure()->isValidOffset(offset));
1123         putDirectOffset(globalData, offset, value);
1124         // See comment on setNewProperty call below.
1125         if (!specificFunction)
1126             slot.setNewProperty(this, offset);
1127         return true;
1128     }
1129
1130     PropertyOffset offset;
1131     size_t currentCapacity = structure()->outOfLineCapacity();
1132     if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(this->structure(), propertyName, attributes, specificFunction, offset)) {
1133         Butterfly* newButterfly = m_butterfly;
1134         if (currentCapacity != structure->outOfLineCapacity())
1135             newButterfly = growOutOfLineStorage(globalData, currentCapacity, structure->outOfLineCapacity());
1136
1137         validateOffset(offset);
1138         ASSERT(structure->isValidOffset(offset));
1139         setButterfly(globalData, newButterfly, structure);
1140         putDirectOffset(globalData, offset, value);
1141         // This is a new property; transitions with specific values are not currently cachable,
1142         // so leave the slot in an uncachable state.
1143         if (!specificFunction)
1144             slot.setNewProperty(this, offset);
1145         return true;
1146     }
1147
1148     unsigned currentAttributes;
1149     JSCell* currentSpecificFunction;
1150     offset = structure()->get(globalData, propertyName, currentAttributes, currentSpecificFunction);
1151     if (offset != invalidOffset) {
1152         if ((mode == PutModePut) && currentAttributes & ReadOnly)
1153             return false;
1154
1155         // There are three possibilities here:
1156         //  (1) There is an existing specific value set, and we're overwriting with *the same value*.
1157         //       * Do nothing - no need to despecify, but that means we can't cache (a cached
1158         //         put could write a different value). Leave the slot in an uncachable state.
1159         //  (2) There is a specific value currently set, but we're writing a different value.
1160         //       * First, we have to despecify.  Having done so, this is now a regular slot
1161         //         with no specific value, so go ahead & cache like normal.
1162         //  (3) Normal case, there is no specific value set.
1163         //       * Go ahead & cache like normal.
1164         if (currentSpecificFunction) {
1165             // case (1) Do the put, then return leaving the slot uncachable.
1166             if (specificFunction == currentSpecificFunction) {
1167                 putDirectOffset(globalData, offset, value);
1168                 return true;
1169             }
1170             // case (2) Despecify, fall through to (3).
1171             setStructure(globalData, Structure::despecifyFunctionTransition(globalData, structure(), propertyName));
1172         }
1173
1174         // case (3) set the slot, do the put, return.
1175         slot.setExistingProperty(this, offset);
1176         putDirectOffset(globalData, offset, value);
1177         return true;
1178     }
1179
1180     if ((mode == PutModePut) && !isExtensible())
1181         return false;
1182
1183     Structure* structure = Structure::addPropertyTransition(globalData, this->structure(), propertyName, attributes, specificFunction, offset);
1184     
1185     validateOffset(offset);
1186     ASSERT(structure->isValidOffset(offset));
1187     setStructureAndReallocateStorageIfNecessary(globalData, structure);
1188
1189     putDirectOffset(globalData, offset, value);
1190     // This is a new property; transitions with specific values are not currently cachable,
1191     // so leave the slot in an uncachable state.
1192     if (!specificFunction)
1193         slot.setNewProperty(this, offset);
1194     return true;
1195 }
1196
1197 inline void JSObject::setStructureAndReallocateStorageIfNecessary(JSGlobalData& globalData, unsigned oldCapacity, Structure* newStructure)
1198 {
1199     ASSERT(oldCapacity <= newStructure->outOfLineCapacity());
1200     
1201     if (oldCapacity == newStructure->outOfLineCapacity()) {
1202         setStructure(globalData, newStructure);
1203         return;
1204     }
1205     
1206     Butterfly* newButterfly = growOutOfLineStorage(
1207         globalData, oldCapacity, newStructure->outOfLineCapacity());
1208     setButterfly(globalData, newButterfly, newStructure);
1209 }
1210
1211 inline void JSObject::setStructureAndReallocateStorageIfNecessary(JSGlobalData& globalData, Structure* newStructure)
1212 {
1213     setStructureAndReallocateStorageIfNecessary(
1214         globalData, structure()->outOfLineCapacity(), newStructure);
1215 }
1216
1217 inline bool JSObject::putOwnDataProperty(JSGlobalData& globalData, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
1218 {
1219     ASSERT(value);
1220     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
1221     ASSERT(!structure()->hasGetterSetterProperties());
1222
1223     return putDirectInternal<PutModePut>(globalData, propertyName, value, 0, slot, getCallableObject(value));
1224 }
1225
1226 inline void JSObject::putDirect(JSGlobalData& globalData, PropertyName propertyName, JSValue value, unsigned attributes)
1227 {
1228     ASSERT(!value.isGetterSetter() && !(attributes & Accessor));
1229     PutPropertySlot slot;
1230     putDirectInternal<PutModeDefineOwnProperty>(globalData, propertyName, value, attributes, slot, getCallableObject(value));
1231 }
1232
1233 inline void JSObject::putDirect(JSGlobalData& globalData, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
1234 {
1235     ASSERT(!value.isGetterSetter());
1236     putDirectInternal<PutModeDefineOwnProperty>(globalData, propertyName, value, 0, slot, getCallableObject(value));
1237 }
1238
1239 inline void JSObject::putDirectWithoutTransition(JSGlobalData& globalData, PropertyName propertyName, JSValue value, unsigned attributes)
1240 {
1241     ASSERT(!value.isGetterSetter() && !(attributes & Accessor));
1242     Butterfly* newButterfly = m_butterfly;
1243     if (structure()->putWillGrowOutOfLineStorage())
1244         newButterfly = growOutOfLineStorage(globalData, structure()->outOfLineCapacity(), structure()->suggestedNewOutOfLineStorageCapacity());
1245     PropertyOffset offset = structure()->addPropertyWithoutTransition(globalData, propertyName, attributes, getCallableObject(value));
1246     setButterfly(globalData, newButterfly, structure());
1247     putDirectOffset(globalData, offset, value);
1248 }
1249
1250 inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
1251 {
1252     return methodTable()->defaultValue(this, exec, preferredType);
1253 }
1254
1255 inline JSValue JSValue::get(ExecState* exec, PropertyName propertyName) const
1256 {
1257     PropertySlot slot(asValue());
1258     return get(exec, propertyName, slot);
1259 }
1260
1261 inline JSValue JSValue::get(ExecState* exec, PropertyName propertyName, PropertySlot& slot) const
1262 {
1263     if (UNLIKELY(!isCell())) {
1264         JSObject* prototype = synthesizePrototype(exec);
1265         if (!prototype->getPropertySlot(exec, propertyName, slot))
1266             return jsUndefined();
1267         return slot.getValue(exec, propertyName);
1268     }
1269     JSCell* cell = asCell();
1270     while (true) {
1271         if (cell->fastGetOwnPropertySlot(exec, propertyName, slot))
1272             return slot.getValue(exec, propertyName);
1273         JSValue prototype = asObject(cell)->prototype();
1274         if (!prototype.isObject())
1275             return jsUndefined();
1276         cell = asObject(prototype);
1277     }
1278 }
1279
1280 inline JSValue JSValue::get(ExecState* exec, unsigned propertyName) const
1281 {
1282     PropertySlot slot(asValue());
1283     return get(exec, propertyName, slot);
1284 }
1285
1286 inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const
1287 {
1288     if (UNLIKELY(!isCell())) {
1289         JSObject* prototype = synthesizePrototype(exec);
1290         if (!prototype->getPropertySlot(exec, propertyName, slot))
1291             return jsUndefined();
1292         return slot.getValue(exec, propertyName);
1293     }
1294     JSCell* cell = const_cast<JSCell*>(asCell());
1295     while (true) {
1296         if (cell->methodTable()->getOwnPropertySlotByIndex(cell, exec, propertyName, slot))
1297             return slot.getValue(exec, propertyName);
1298         JSValue prototype = asObject(cell)->prototype();
1299         if (!prototype.isObject())
1300             return jsUndefined();
1301         cell = prototype.asCell();
1302     }
1303 }
1304
1305 inline void JSValue::put(ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
1306 {
1307     if (UNLIKELY(!isCell())) {
1308         putToPrimitive(exec, propertyName, value, slot);
1309         return;
1310     }
1311     asCell()->methodTable()->put(asCell(), exec, propertyName, value, slot);
1312 }
1313
1314 inline void JSValue::putByIndex(ExecState* exec, unsigned propertyName, JSValue value, bool shouldThrow)
1315 {
1316     if (UNLIKELY(!isCell())) {
1317         putToPrimitiveByIndex(exec, propertyName, value, shouldThrow);
1318         return;
1319     }
1320     asCell()->methodTable()->putByIndex(asCell(), exec, propertyName, value, shouldThrow);
1321 }
1322
1323 ALWAYS_INLINE JSObject* Register::function() const
1324 {
1325     if (!jsValue())
1326         return 0;
1327     return asObject(jsValue());
1328 }
1329
1330 ALWAYS_INLINE Register Register::withCallee(JSObject* callee)
1331 {
1332     Register r;
1333     r = JSValue(callee);
1334     return r;
1335 }
1336
1337 inline size_t offsetInButterfly(PropertyOffset offset)
1338 {
1339     return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage();
1340 }
1341
1342 // This is a helper for patching code where you want to emit a load or store and
1343 // the base is:
1344 // For inline offsets: a pointer to the out-of-line storage pointer.
1345 // For out-of-line offsets: the base of the out-of-line storage.
1346 inline size_t offsetRelativeToPatchedStorage(PropertyOffset offset)
1347 {
1348     if (isOutOfLineOffset(offset))
1349         return sizeof(EncodedJSValue) * offsetInButterfly(offset);
1350     return JSObject::offsetOfInlineStorage() - JSObject::butterflyOffset() + sizeof(EncodedJSValue) * offsetInInlineStorage(offset);
1351 }
1352
1353 inline int indexRelativeToBase(PropertyOffset offset)
1354 {
1355     if (isOutOfLineOffset(offset))
1356         return offsetInOutOfLineStorage(offset) + Butterfly::indexOfPropertyStorage();
1357     ASSERT(!(JSObject::offsetOfInlineStorage() % sizeof(EncodedJSValue)));
1358     return JSObject::offsetOfInlineStorage() / sizeof(EncodedJSValue) + offsetInInlineStorage(offset);
1359 }
1360
1361 inline int offsetRelativeToBase(PropertyOffset offset)
1362 {
1363     if (isOutOfLineOffset(offset))
1364         return offsetInOutOfLineStorage(offset) * sizeof(EncodedJSValue) + Butterfly::offsetOfPropertyStorage();
1365     return JSObject::offsetOfInlineStorage() + offsetInInlineStorage(offset) * sizeof(EncodedJSValue);
1366 }
1367
1368 COMPILE_ASSERT(!(sizeof(JSObject) % sizeof(WriteBarrierBase<Unknown>)), JSObject_inline_storage_has_correct_alignment);
1369
1370 } // namespace JSC
1371
1372 #endif // JSObject_h