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