2011-01-27 Oliver Hunt <oliver@apple.com>
[WebKit-https.git] / Source / JavaScriptCore / runtime / JSObject.h
1 /*
2  *  Copyright (C) 1999-2001 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2001 Peter Kelly (pmk@post.com)
4  *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Library General Public
8  *  License as published by the Free Software Foundation; either
9  *  version 2 of the License, or (at your option) any later version.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Library General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Library General Public License
17  *  along with this library; see the file COPYING.LIB.  If not, write to
18  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  *  Boston, MA 02110-1301, USA.
20  *
21  */
22
23 #ifndef JSObject_h
24 #define JSObject_h
25
26 #include "ArgList.h"
27 #include "ClassInfo.h"
28 #include "CommonIdentifiers.h"
29 #include "Completion.h"
30 #include "CallFrame.h"
31 #include "JSCell.h"
32 #include "JSNumberCell.h"
33 #include "MarkStack.h"
34 #include "PropertySlot.h"
35 #include "PutPropertySlot.h"
36 #include "ScopeChain.h"
37 #include "Structure.h"
38 #include "JSGlobalData.h"
39 #include "JSString.h"
40 #include <wtf/StdLibExtras.h>
41
42 namespace JSC {
43
44     inline JSCell* getJSFunction(JSGlobalData& globalData, JSValue value)
45     {
46         if (value.isCell() && (value.asCell()->vptr() == globalData.jsFunctionVPtr))
47             return value.asCell();
48         return 0;
49     }
50     
51     class HashEntry;
52     class InternalFunction;
53     class PropertyDescriptor;
54     class PropertyNameArray;
55     class Structure;
56     struct HashTable;
57
58     JSObject* throwTypeError(ExecState*, const UString&);
59     extern const char* StrictModeReadonlyPropertyWriteError;
60
61     // ECMA 262-3 8.6.1
62     // Property attributes
63     enum Attribute {
64         None         = 0,
65         ReadOnly     = 1 << 1,  // property can be only read, not written
66         DontEnum     = 1 << 2,  // property doesn't appear in (for .. in ..)
67         DontDelete   = 1 << 3,  // property can't be deleted
68         Function     = 1 << 4,  // property is a function - only used by static hashtables
69         Getter       = 1 << 5,  // property is a getter
70         Setter       = 1 << 6   // property is a setter
71     };
72
73     typedef WriteBarrierBase<Unknown>* PropertyStorage;
74     typedef const WriteBarrierBase<Unknown>* ConstPropertyStorage;
75
76     class JSObject : public JSCell {
77         friend class BatchedTransitionOptimizer;
78         friend class JIT;
79         friend class JSCell;
80         friend void setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot);
81
82     public:
83         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(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&);
183         void putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr = 0);
184         bool putDirect(JSGlobalData&, const Identifier& propertyName, JSValue, PutPropertySlot&);
185
186         void putDirectFunction(JSGlobalData&, const Identifier& propertyName, JSCell*, unsigned attr = 0);
187         void putDirectFunction(JSGlobalData&, const Identifier& propertyName, JSCell*, unsigned attr, bool checkReadOnly, PutPropertySlot&);
188         void putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr = 0);
189         void putDirectFunction(ExecState* exec, JSFunction* function, unsigned attr = 0);
190
191         void putDirectWithoutTransition(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr = 0);
192         void putDirectFunctionWithoutTransition(JSGlobalData&, 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 propertyStorage()[offset].get(); }
198         void putDirectOffset(JSGlobalData& globalData, size_t offset, JSValue value) { propertyStorage()[offset].set(globalData, this, value); }
199         void putUndefinedAtDirectOffset(size_t offset) { propertyStorage()[offset].setUndefined(); }
200
201         void fillGetterPropertySlot(PropertySlot&, JSValue* location);
202
203         virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction, unsigned attributes = 0);
204         virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction, unsigned attributes = 0);
205         virtual JSValue lookupGetter(ExecState*, const Identifier& propertyName);
206         virtual JSValue lookupSetter(ExecState*, const Identifier& propertyName);
207         virtual bool defineOwnProperty(ExecState*, const Identifier& propertyName, PropertyDescriptor&, bool shouldThrow);
208
209         virtual bool isGlobalObject() const { return false; }
210         virtual bool isVariableObject() const { return false; }
211         virtual bool isActivationObject() const { return false; }
212         virtual bool isStrictModeFunction() const { return false; }
213         virtual bool isErrorInstance() const { return false; }
214
215         virtual ComplType exceptionType() const { return Throw; }
216
217         void allocatePropertyStorage(size_t oldSize, size_t newSize);
218         void allocatePropertyStorageInline(size_t oldSize, size_t newSize);
219         bool isUsingInlineStorage() const { return m_structure->isUsingInlineStorage(); }
220
221         static const unsigned inlineStorageCapacity = sizeof(EncodedJSValue) == 2 * sizeof(void*) ? 4 : 3;
222         static const unsigned nonInlineBaseStorageCapacity = 16;
223
224         static PassRefPtr<Structure> createStructure(JSValue prototype)
225         {
226             return Structure::create(prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount);
227         }
228
229         void flattenDictionaryObject(JSGlobalData& globalData)
230         {
231             m_structure->flattenDictionaryStructure(globalData, this);
232         }
233
234         void putAnonymousValue(unsigned index, JSValue value)
235         {
236             ASSERT(index < m_structure->anonymousSlotCount());
237             *locationForOffset(index) = value;
238         }
239         JSValue getAnonymousValue(unsigned index) const
240         {
241             ASSERT(index < m_structure->anonymousSlotCount());
242             return *locationForOffset(index);
243         }
244         
245     protected:
246         static const unsigned StructureFlags = 0;
247         
248     private:
249         // Nobody should ever ask any of these questions on something already known to be a JSObject.
250         using JSCell::isAPIValueWrapper;
251         using JSCell::isGetterSetter;
252         using JSCell::toObject;
253         void getObject();
254         void getString(ExecState* exec);
255         void isObject();
256         void isString();
257
258         ConstPropertyStorage propertyStorage() const { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); }
259         PropertyStorage propertyStorage() { return (isUsingInlineStorage() ? m_inlineStorage : m_externalStorage); }
260
261         const JSValue* locationForOffset(size_t offset) const
262         {
263             return reinterpret_cast<const JSValue*>(&propertyStorage()[offset]);
264         }
265
266         JSValue* locationForOffset(size_t offset)
267         {
268             return reinterpret_cast<JSValue*>(&propertyStorage()[offset]);
269         }
270
271         bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&, JSCell*);
272         bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&);
273         void putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue value, unsigned attr = 0);
274
275         bool inlineGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
276
277         const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const;
278         Structure* createInheritorID();
279
280         union {
281             PropertyStorage m_externalStorage;
282             WriteBarrierBase<Unknown> m_inlineStorage[inlineStorageCapacity];
283         };
284
285         RefPtr<Structure> m_inheritorID;
286     };
287     
288 inline JSObject* asObject(JSCell* cell)
289 {
290     ASSERT(cell->isObject());
291     return static_cast<JSObject*>(cell);
292 }
293
294 inline JSObject* asObject(JSValue value)
295 {
296     return asObject(value.asCell());
297 }
298
299 inline JSObject::JSObject(NonNullPassRefPtr<Structure> structure)
300     : JSCell(structure.releaseRef()) // ~JSObject balances this ref()
301 {
302     ASSERT(m_structure->propertyStorageCapacity() == inlineStorageCapacity);
303     ASSERT(m_structure->isEmpty());
304     ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype()));
305     ASSERT(OBJECT_OFFSETOF(JSObject, m_inlineStorage) % sizeof(double) == 0);
306 }
307
308 inline JSObject::~JSObject()
309 {
310     ASSERT(m_structure);
311     if (!isUsingInlineStorage())
312         delete [] m_externalStorage;
313     m_structure->deref();
314 }
315
316 inline JSValue JSObject::prototype() const
317 {
318     return m_structure->storedPrototype();
319 }
320
321 inline bool JSObject::setPrototypeWithCycleCheck(JSValue prototype)
322 {
323     JSValue nextPrototypeValue = prototype;
324     while (nextPrototypeValue && nextPrototypeValue.isObject()) {
325         JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject();
326         if (nextPrototype == this)
327             return false;
328         nextPrototypeValue = nextPrototype->prototype();
329     }
330     setPrototype(prototype);
331     return true;
332 }
333
334 inline void JSObject::setPrototype(JSValue prototype)
335 {
336     ASSERT(prototype);
337     RefPtr<Structure> newStructure = Structure::changePrototypeTransition(m_structure, prototype);
338     setStructure(newStructure.release());
339 }
340
341 inline void JSObject::setStructure(NonNullPassRefPtr<Structure> structure)
342 {
343     m_structure->deref();
344     m_structure = structure.leakRef(); // ~JSObject balances this ref()
345 }
346
347 inline Structure* JSObject::inheritorID()
348 {
349     if (m_inheritorID)
350         return m_inheritorID.get();
351     return createInheritorID();
352 }
353
354 inline bool Structure::isUsingInlineStorage() const
355 {
356     return (propertyStorageCapacity() == JSObject::inlineStorageCapacity);
357 }
358
359 inline bool JSCell::inherits(const ClassInfo* info) const
360 {
361     for (const ClassInfo* ci = classInfo(); ci; ci = ci->parentClass) {
362         if (ci == info)
363             return true;
364     }
365     return false;
366 }
367
368 // this method is here to be after the inline declaration of JSCell::inherits
369 inline bool JSValue::inherits(const ClassInfo* classInfo) const
370 {
371     return isCell() && asCell()->inherits(classInfo);
372 }
373
374 ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
375 {
376     if (JSValue* location = getDirectLocation(propertyName)) {
377         if (m_structure->hasGetterSetterProperties() && location[0].isGetterSetter())
378             fillGetterPropertySlot(slot, location);
379         else
380             slot.setValueSlot(this, location, offsetForLocation(location));
381         return true;
382     }
383
384     // non-standard Netscape extension
385     if (propertyName == exec->propertyNames().underscoreProto) {
386         slot.setValue(prototype());
387         return true;
388     }
389
390     return false;
391 }
392
393 // It may seem crazy to inline a function this large, especially a virtual function,
394 // but it makes a big difference to property lookup that derived classes can inline their
395 // base class call to this.
396 ALWAYS_INLINE bool JSObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
397 {
398     return inlineGetOwnPropertySlot(exec, propertyName, slot);
399 }
400
401 ALWAYS_INLINE bool JSCell::fastGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
402 {
403     if (!structure()->typeInfo().overridesGetOwnPropertySlot())
404         return asObject(this)->inlineGetOwnPropertySlot(exec, propertyName, slot);
405     return getOwnPropertySlot(exec, propertyName, slot);
406 }
407
408 // It may seem crazy to inline a function this large but it makes a big difference
409 // since this is function very hot in variable lookup
410 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
411 {
412     JSObject* object = this;
413     while (true) {
414         if (object->fastGetOwnPropertySlot(exec, propertyName, slot))
415             return true;
416         JSValue prototype = object->prototype();
417         if (!prototype.isObject())
418             return false;
419         object = asObject(prototype);
420     }
421 }
422
423 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
424 {
425     JSObject* object = this;
426     while (true) {
427         if (object->getOwnPropertySlot(exec, propertyName, slot))
428             return true;
429         JSValue prototype = object->prototype();
430         if (!prototype.isObject())
431             return false;
432         object = asObject(prototype);
433     }
434 }
435
436 inline JSValue JSObject::get(ExecState* exec, const Identifier& propertyName) const
437 {
438     PropertySlot slot(this);
439     if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
440         return slot.getValue(exec, propertyName);
441     
442     return jsUndefined();
443 }
444
445 inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const
446 {
447     PropertySlot slot(this);
448     if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
449         return slot.getValue(exec, propertyName);
450
451     return jsUndefined();
452 }
453
454 inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot, JSCell* specificFunction)
455 {
456     ASSERT(value);
457     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
458
459     if (m_structure->isDictionary()) {
460         unsigned currentAttributes;
461         JSCell* currentSpecificFunction;
462         size_t offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction);
463         if (offset != WTF::notFound) {
464             // If there is currently a specific function, and there now either isn't,
465             // or the new value is different, then despecify.
466             if (currentSpecificFunction && (specificFunction != currentSpecificFunction))
467                 m_structure->despecifyDictionaryFunction(propertyName);
468             if (checkReadOnly && currentAttributes & ReadOnly)
469                 return false;
470
471             putDirectOffset(globalData, offset, value);
472             // At this point, the objects structure only has a specific value set if previously there
473             // had been one set, and if the new value being specified is the same (otherwise we would
474             // have despecified, above).  So, if currentSpecificFunction is not set, or if the new
475             // value is different (or there is no new value), then the slot now has no value - and
476             // as such it is cachable.
477             // If there was previously a value, and the new value is the same, then we cannot cache.
478             if (!currentSpecificFunction || (specificFunction != currentSpecificFunction))
479                 slot.setExistingProperty(this, offset);
480             return true;
481         }
482
483         size_t currentCapacity = m_structure->propertyStorageCapacity();
484         offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, specificFunction);
485         if (currentCapacity != m_structure->propertyStorageCapacity())
486             allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
487
488         ASSERT(offset < m_structure->propertyStorageCapacity());
489         putDirectOffset(globalData, offset, value);
490         // See comment on setNewProperty call below.
491         if (!specificFunction)
492             slot.setNewProperty(this, offset);
493         return true;
494     }
495
496     size_t offset;
497     size_t currentCapacity = m_structure->propertyStorageCapacity();
498     if (RefPtr<Structure> structure = Structure::addPropertyTransitionToExistingStructure(m_structure, propertyName, attributes, specificFunction, offset)) {    
499         if (currentCapacity != structure->propertyStorageCapacity())
500             allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity());
501
502         ASSERT(offset < structure->propertyStorageCapacity());
503         setStructure(structure.release());
504         putDirectOffset(globalData, offset, value);
505         // This is a new property; transitions with specific values are not currently cachable,
506         // so leave the slot in an uncachable state.
507         if (!specificFunction)
508             slot.setNewProperty(this, offset);
509         return true;
510     }
511
512     unsigned currentAttributes;
513     JSCell* currentSpecificFunction;
514     offset = m_structure->get(propertyName, currentAttributes, currentSpecificFunction);
515     if (offset != WTF::notFound) {
516         if (checkReadOnly && currentAttributes & ReadOnly)
517             return false;
518
519         // There are three possibilities here:
520         //  (1) There is an existing specific value set, and we're overwriting with *the same value*.
521         //       * Do nothing - no need to despecify, but that means we can't cache (a cached
522         //         put could write a different value). Leave the slot in an uncachable state.
523         //  (2) There is a specific value currently set, but we're writing a different value.
524         //       * First, we have to despecify.  Having done so, this is now a regular slot
525         //         with no specific value, so go ahead & cache like normal.
526         //  (3) Normal case, there is no specific value set.
527         //       * Go ahead & cache like normal.
528         if (currentSpecificFunction) {
529             // case (1) Do the put, then return leaving the slot uncachable.
530             if (specificFunction == currentSpecificFunction) {
531                 putDirectOffset(globalData, offset, value);
532                 return true;
533             }
534             // case (2) Despecify, fall through to (3).
535             setStructure(Structure::despecifyFunctionTransition(m_structure, propertyName));
536         }
537
538         // case (3) set the slot, do the put, return.
539         slot.setExistingProperty(this, offset);
540         putDirectOffset(globalData, offset, value);
541         return true;
542     }
543
544     // If we have a specific function, we may have got to this point if there is
545     // already a transition with the correct property name and attributes, but
546     // specialized to a different function.  In this case we just want to give up
547     // and despecialize the transition.
548     // In this case we clear the value of specificFunction which will result
549     // in us adding a non-specific transition, and any subsequent lookup in
550     // Structure::addPropertyTransitionToExistingStructure will just use that.
551     if (specificFunction && m_structure->hasTransition(propertyName, attributes))
552         specificFunction = 0;
553
554     RefPtr<Structure> structure = Structure::addPropertyTransition(m_structure, propertyName, attributes, specificFunction, offset);
555
556     if (currentCapacity != structure->propertyStorageCapacity())
557         allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity());
558
559     ASSERT(offset < structure->propertyStorageCapacity());
560     setStructure(structure.release());
561     putDirectOffset(globalData, offset, value);
562     // This is a new property; transitions with specific values are not currently cachable,
563     // so leave the slot in an uncachable state.
564     if (!specificFunction)
565         slot.setNewProperty(this, offset);
566     return true;
567 }
568
569 inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
570 {
571     ASSERT(value);
572     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
573
574     return putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, getJSFunction(globalData, value));
575 }
576
577 inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
578 {
579     PutPropertySlot slot;
580     putDirectInternal(globalData, propertyName, value, attributes, false, slot, getJSFunction(globalData, value));
581 }
582
583 inline bool JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
584 {
585     ASSERT(value);
586     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
587
588     return putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, 0);
589 }
590
591 inline void JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
592 {
593     PutPropertySlot slot;
594     putDirectInternal(globalData, propertyName, value, attributes, false, slot, 0);
595 }
596
597 inline bool JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
598 {
599     return putDirectInternal(globalData, propertyName, value, 0, false, slot, 0);
600 }
601
602 inline void JSObject::putDirectFunction(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
603 {
604     putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, value);
605 }
606
607 inline void JSObject::putDirectFunction(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attr)
608 {
609     PutPropertySlot slot;
610     putDirectInternal(globalData, propertyName, value, attr, false, slot, value);
611 }
612
613 inline void JSObject::putDirectWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
614 {
615     size_t currentCapacity = m_structure->propertyStorageCapacity();
616     size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, 0);
617     if (currentCapacity != m_structure->propertyStorageCapacity())
618         allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
619     putDirectOffset(globalData, offset, value);
620 }
621
622 inline void JSObject::putDirectFunctionWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attributes)
623 {
624     size_t currentCapacity = m_structure->propertyStorageCapacity();
625     size_t offset = m_structure->addPropertyWithoutTransition(propertyName, attributes, value);
626     if (currentCapacity != m_structure->propertyStorageCapacity())
627         allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
628     putDirectOffset(globalData, offset, value);
629 }
630
631 inline void JSObject::transitionTo(Structure* newStructure)
632 {
633     if (m_structure->propertyStorageCapacity() != newStructure->propertyStorageCapacity())
634         allocatePropertyStorage(m_structure->propertyStorageCapacity(), newStructure->propertyStorageCapacity());
635     setStructure(newStructure);
636 }
637
638 inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
639 {
640     return defaultValue(exec, preferredType);
641 }
642
643 inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName) const
644 {
645     PropertySlot slot(asValue());
646     return get(exec, propertyName, slot);
647 }
648
649 inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) const
650 {
651     if (UNLIKELY(!isCell())) {
652         JSObject* prototype = synthesizePrototype(exec);
653         if (propertyName == exec->propertyNames().underscoreProto)
654             return prototype;
655         if (!prototype->getPropertySlot(exec, propertyName, slot))
656             return jsUndefined();
657         return slot.getValue(exec, propertyName);
658     }
659     JSCell* cell = asCell();
660     while (true) {
661         if (cell->fastGetOwnPropertySlot(exec, propertyName, slot))
662             return slot.getValue(exec, propertyName);
663         JSValue prototype = asObject(cell)->prototype();
664         if (!prototype.isObject())
665             return jsUndefined();
666         cell = asObject(prototype);
667     }
668 }
669
670 inline JSValue JSValue::get(ExecState* exec, unsigned propertyName) const
671 {
672     PropertySlot slot(asValue());
673     return get(exec, propertyName, slot);
674 }
675
676 inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const
677 {
678     if (UNLIKELY(!isCell())) {
679         JSObject* prototype = synthesizePrototype(exec);
680         if (!prototype->getPropertySlot(exec, propertyName, slot))
681             return jsUndefined();
682         return slot.getValue(exec, propertyName);
683     }
684     JSCell* cell = const_cast<JSCell*>(asCell());
685     while (true) {
686         if (cell->getOwnPropertySlot(exec, propertyName, slot))
687             return slot.getValue(exec, propertyName);
688         JSValue prototype = asObject(cell)->prototype();
689         if (!prototype.isObject())
690             return jsUndefined();
691         cell = prototype.asCell();
692     }
693 }
694
695 inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
696 {
697     if (UNLIKELY(!isCell())) {
698         synthesizeObject(exec)->put(exec, propertyName, value, slot);
699         return;
700     }
701     asCell()->put(exec, propertyName, value, slot);
702 }
703
704 inline void JSValue::putDirect(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
705 {
706     ASSERT(isCell() && isObject());
707     if (!asObject(asCell())->putDirect(exec->globalData(), propertyName, value, slot) && slot.isStrictMode())
708         throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
709 }
710
711 inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value)
712 {
713     if (UNLIKELY(!isCell())) {
714         synthesizeObject(exec)->put(exec, propertyName, value);
715         return;
716     }
717     asCell()->put(exec, propertyName, value);
718 }
719
720 ALWAYS_INLINE void JSObject::allocatePropertyStorageInline(size_t oldSize, size_t newSize)
721 {
722     ASSERT(newSize > oldSize);
723
724     // It's important that this function not rely on m_structure, since
725     // we might be in the middle of a transition.
726     bool wasInline = (oldSize == JSObject::inlineStorageCapacity);
727
728     PropertyStorage oldPropertyStorage = (wasInline ? m_inlineStorage : m_externalStorage);
729     PropertyStorage newPropertyStorage = new WriteBarrierBase<Unknown>[newSize];
730
731     for (unsigned i = 0; i < oldSize; ++i)
732        newPropertyStorage[i] = oldPropertyStorage[i];
733
734     if (!wasInline)
735         delete [] oldPropertyStorage;
736
737     m_externalStorage = newPropertyStorage;
738 }
739
740 ALWAYS_INLINE void JSObject::markChildrenDirect(MarkStack& markStack)
741 {
742     JSCell::markChildren(markStack);
743
744     markStack.append(m_structure->storedPrototypeSlot());
745     PropertyStorage storage = propertyStorage();
746     size_t storageSize = m_structure->propertyStorageSize();
747     markStack.appendValues(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