Unreviewed, rolling out r94445 and r94448.
[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 "PropertySlot.h"
33 #include "PutPropertySlot.h"
34 #include "ScopeChain.h"
35 #include "Structure.h"
36 #include "JSGlobalData.h"
37 #include "JSString.h"
38 #include <wtf/StdLibExtras.h>
39
40 namespace JSC {
41
42     inline JSCell* getJSFunction(JSGlobalData& globalData, JSValue value)
43     {
44         if (value.isCell() && (value.asCell()->vptr() == globalData.jsFunctionVPtr))
45             return value.asCell();
46         return 0;
47     }
48     
49     class HashEntry;
50     class InternalFunction;
51     class MarkedBlock;
52     class PropertyDescriptor;
53     class PropertyNameArray;
54     class Structure;
55     struct HashTable;
56
57     JSObject* throwTypeError(ExecState*, const UString&);
58     extern const char* StrictModeReadonlyPropertyWriteError;
59
60     // ECMA 262-3 8.6.1
61     // Property attributes
62     enum Attribute {
63         None         = 0,
64         ReadOnly     = 1 << 1,  // property can be only read, not written
65         DontEnum     = 1 << 2,  // property doesn't appear in (for .. in ..)
66         DontDelete   = 1 << 3,  // property can't be deleted
67         Function     = 1 << 4,  // property is a function - only used by static hashtables
68         Getter       = 1 << 5,  // property is a getter
69         Setter       = 1 << 6   // property is a setter
70     };
71
72     typedef WriteBarrierBase<Unknown>* PropertyStorage;
73     typedef const WriteBarrierBase<Unknown>* ConstPropertyStorage;
74
75     class JSObject : public JSCell {
76         friend class BatchedTransitionOptimizer;
77         friend class JIT;
78         friend class JSCell;
79         friend class MarkedBlock;
80         friend void setUpStaticFunctionSlot(ExecState* exec, const HashEntry* entry, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot);
81
82     public:
83         typedef JSCell Base;
84
85         virtual void visitChildren(SlotVisitor&);
86         ALWAYS_INLINE void visitChildrenDirect(SlotVisitor&);
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(JSGlobalData&, JSValue prototype);
94         bool setPrototypeWithCycleCheck(JSGlobalData&, JSValue prototype);
95         
96         void setStructure(JSGlobalData&, Structure*);
97         Structure* inheritorID(JSGlobalData&);
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*, JSGlobalObject*) 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(JSGlobalData& globalData, const Identifier& propertyName) const
153         {
154             size_t offset = m_structure->get(globalData, propertyName);
155             return offset != WTF::notFound ? getDirectOffset(offset) : JSValue();
156         }
157
158         WriteBarrierBase<Unknown>* getDirectLocation(JSGlobalData& globalData, const Identifier& propertyName)
159         {
160             size_t offset = m_structure->get(globalData, propertyName);
161             return offset != WTF::notFound ? locationForOffset(offset) : 0;
162         }
163
164         WriteBarrierBase<Unknown>* getDirectLocation(JSGlobalData& globalData, const Identifier& propertyName, unsigned& attributes)
165         {
166             JSCell* specificFunction;
167             size_t offset = m_structure->get(globalData, propertyName, attributes, specificFunction);
168             return offset != WTF::notFound ? locationForOffset(offset) : 0;
169         }
170
171         size_t offsetForLocation(WriteBarrierBase<Unknown>* location) const
172         {
173             return location - propertyStorage();
174         }
175
176         void transitionTo(JSGlobalData&, Structure*);
177
178         void removeDirect(JSGlobalData&, const Identifier& propertyName);
179         bool hasCustomProperties() { return m_structure->didTransition(); }
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&, WriteBarrierBase<Unknown>* 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         void seal(JSGlobalData&);
216         void freeze(JSGlobalData&);
217         virtual void preventExtensions(JSGlobalData&);
218         bool isSealed(JSGlobalData& globalData) { return m_structure->isSealed(globalData); }
219         bool isFrozen(JSGlobalData& globalData) { return m_structure->isFrozen(globalData); }
220         bool isExtensible() { return m_structure->isExtensible(); }
221
222         virtual ComplType exceptionType() const { return Throw; }
223
224         void allocatePropertyStorage(size_t oldSize, size_t newSize);
225         bool isUsingInlineStorage() const { return static_cast<const void*>(m_propertyStorage) == static_cast<const void*>(this + 1); }
226
227         void* addressOfPropertyAtOffset(size_t offset)
228         {
229             return static_cast<void*>(&m_propertyStorage[offset]);
230         }
231
232         static const unsigned baseExternalStorageCapacity = 16;
233
234         void flattenDictionaryObject(JSGlobalData& globalData)
235         {
236             m_structure->flattenDictionaryStructure(globalData, this);
237         }
238
239         void putAnonymousValue(JSGlobalData& globalData, unsigned index, JSValue value)
240         {
241             ASSERT(index < m_structure->anonymousSlotCount());
242             locationForOffset(index)->set(globalData, this, value);
243         }
244         void clearAnonymousValue(unsigned index)
245         {
246             ASSERT(index < m_structure->anonymousSlotCount());
247             locationForOffset(index)->clear();
248         }
249         JSValue getAnonymousValue(unsigned index) const
250         {
251             ASSERT(index < m_structure->anonymousSlotCount());
252             return locationForOffset(index)->get();
253         }
254
255         static size_t offsetOfInlineStorage();
256         static size_t offsetOfPropertyStorage();
257         static size_t offsetOfInheritorID();
258
259         static JS_EXPORTDATA const ClassInfo s_info;
260
261     protected:
262         void finishCreation(JSGlobalData& globalData, PropertyStorage inlineStorage)
263         {
264             Base::finishCreation(globalData);
265             ASSERT(inherits(&s_info));
266             ASSERT(m_structure->propertyStorageCapacity() < baseExternalStorageCapacity);
267             ASSERT(m_structure->isEmpty());
268             ASSERT(prototype().isNull() || Heap::heap(this) == Heap::heap(prototype()));
269             ASSERT_UNUSED(inlineStorage, static_cast<void*>(inlineStorage) == static_cast<void*>(this + 1));
270             ASSERT(m_structure->typeInfo().type() == ObjectType);
271         }
272
273         static Structure* createStructure(JSGlobalData& globalData, JSValue prototype)
274         {
275             return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info);
276         }
277
278         static const unsigned StructureFlags = 0;
279
280         void putThisToAnonymousValue(unsigned index)
281         {
282             locationForOffset(index)->setWithoutWriteBarrier(this);
283         }
284
285         // To instantiate objects you likely want JSFinalObject, below.
286         // To create derived types you likely want JSNonFinalObject, below.
287         JSObject(JSGlobalData&, Structure*, PropertyStorage inlineStorage);
288         JSObject(VPtrStealingHackType, PropertyStorage inlineStorage)
289             : JSCell(VPtrStealingHack)
290             , m_propertyStorage(inlineStorage)
291         {
292         }
293
294     private:
295         // Nobody should ever ask any of these questions on something already known to be a JSObject.
296         using JSCell::isAPIValueWrapper;
297         using JSCell::isGetterSetter;
298         using JSCell::toObject;
299         void getObject();
300         void getString(ExecState* exec);
301         void isObject();
302         void isString();
303         
304         ConstPropertyStorage propertyStorage() const { return m_propertyStorage; }
305         PropertyStorage propertyStorage() { return m_propertyStorage; }
306
307         const WriteBarrierBase<Unknown>* locationForOffset(size_t offset) const
308         {
309             return &propertyStorage()[offset];
310         }
311
312         WriteBarrierBase<Unknown>* locationForOffset(size_t offset)
313         {
314             return &propertyStorage()[offset];
315         }
316
317         bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&, JSCell*);
318         bool putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue, unsigned attr, bool checkReadOnly, PutPropertySlot&);
319         void putDirectInternal(JSGlobalData&, const Identifier& propertyName, JSValue value, unsigned attr = 0);
320
321         bool inlineGetOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
322
323         const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const;
324         Structure* createInheritorID(JSGlobalData&);
325
326         PropertyStorage m_propertyStorage;
327         WriteBarrier<Structure> m_inheritorID;
328     };
329
330
331 #if USE(JSVALUE32_64)
332 #define JSNonFinalObject_inlineStorageCapacity 4
333 #define JSFinalObject_inlineStorageCapacity 6
334 #else
335 #define JSNonFinalObject_inlineStorageCapacity 2
336 #define JSFinalObject_inlineStorageCapacity 4
337 #endif
338
339 COMPILE_ASSERT((JSFinalObject_inlineStorageCapacity >= JSNonFinalObject_inlineStorageCapacity), final_storage_is_at_least_as_large_as_non_final);
340
341     // JSNonFinalObject is a type of JSObject that has some internal storage,
342     // but also preserves some space in the collector cell for additional
343     // data members in derived types.
344     class JSNonFinalObject : public JSObject {
345         friend class JSObject;
346
347     public:
348         typedef JSObject Base;
349
350         static Structure* createStructure(JSGlobalData& globalData, JSValue prototype)
351         {
352             return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info);
353         }
354
355     protected:
356         explicit JSNonFinalObject(VPtrStealingHackType)
357             : JSObject(VPtrStealingHack, m_inlineStorage)
358         {
359         }
360     
361         explicit JSNonFinalObject(JSGlobalData& globalData, Structure* structure)
362             : JSObject(globalData, structure, m_inlineStorage)
363         {
364         }
365
366         void finishCreation(JSGlobalData& globalData)
367         {
368             Base::finishCreation(globalData, m_inlineStorage);
369             ASSERT(!(OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage) % sizeof(double)));
370             ASSERT(this->structure()->propertyStorageCapacity() == JSNonFinalObject_inlineStorageCapacity);
371         }
372
373     private:
374         WriteBarrier<Unknown> m_inlineStorage[JSNonFinalObject_inlineStorageCapacity];
375     };
376
377     // JSFinalObject is a type of JSObject that contains sufficent internal
378     // storage to fully make use of the colloctor cell containing it.
379     class JSFinalObject : public JSObject {
380         friend class JSObject;
381
382     public:
383         typedef JSObject Base;
384
385         explicit JSFinalObject(VPtrStealingHackType)
386             : JSObject(VPtrStealingHack, m_inlineStorage)
387         {
388         }
389         
390         static JSFinalObject* create(ExecState* exec, Structure* structure)
391         {
392             JSFinalObject* finalObject = new (allocateCell<JSFinalObject>(*exec->heap())) JSFinalObject(exec->globalData(), structure);
393             finalObject->finishCreation(exec->globalData());
394             return finalObject;
395         }
396
397         static Structure* createStructure(JSGlobalData& globalData, JSValue prototype)
398         {
399             return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info);
400         }
401
402     protected:
403         void finishCreation(JSGlobalData& globalData)
404         {
405             Base::finishCreation(globalData, m_inlineStorage);
406             ASSERT(!(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) % sizeof(double)));
407             ASSERT(this->structure()->propertyStorageCapacity() == JSFinalObject_inlineStorageCapacity);
408         }
409
410     private:
411         explicit JSFinalObject(JSGlobalData& globalData, Structure* structure)
412             : JSObject(globalData, structure, m_inlineStorage)
413         {
414         }
415
416         static const unsigned StructureFlags = JSObject::StructureFlags | IsJSFinalObject;
417
418         WriteBarrierBase<Unknown> m_inlineStorage[JSFinalObject_inlineStorageCapacity];
419     };
420
421 inline size_t JSObject::offsetOfInlineStorage()
422 {
423     ASSERT(OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage) == OBJECT_OFFSETOF(JSNonFinalObject, m_inlineStorage));
424     return OBJECT_OFFSETOF(JSFinalObject, m_inlineStorage);
425 }
426
427 inline size_t JSObject::offsetOfPropertyStorage()
428 {
429     return OBJECT_OFFSETOF(JSObject, m_propertyStorage);
430 }
431
432 inline size_t JSObject::offsetOfInheritorID()
433 {
434     return OBJECT_OFFSETOF(JSObject, m_inheritorID);
435 }
436
437 inline JSObject* constructEmptyObject(ExecState* exec, Structure* structure)
438 {
439     return JSFinalObject::create(exec, structure);
440 }
441
442 inline Structure* createEmptyObjectStructure(JSGlobalData& globalData, JSValue prototype)
443 {
444     return JSFinalObject::createStructure(globalData, prototype);
445 }
446
447 inline JSObject* asObject(JSCell* cell)
448 {
449     ASSERT(cell->isObject());
450     return static_cast<JSObject*>(cell);
451 }
452
453 inline JSObject* asObject(JSValue value)
454 {
455     return asObject(value.asCell());
456 }
457
458 inline JSObject::JSObject(JSGlobalData& globalData, Structure* structure, PropertyStorage inlineStorage)
459     : JSCell(globalData, structure)
460     , m_propertyStorage(inlineStorage)
461 {
462 }
463
464 inline JSObject::~JSObject()
465 {
466     if (!isUsingInlineStorage())
467         delete [] m_propertyStorage;
468 }
469
470 inline JSValue JSObject::prototype() const
471 {
472     return m_structure->storedPrototype();
473 }
474
475 inline bool JSObject::setPrototypeWithCycleCheck(JSGlobalData& globalData, JSValue prototype)
476 {
477     JSValue nextPrototypeValue = prototype;
478     while (nextPrototypeValue && nextPrototypeValue.isObject()) {
479         JSObject* nextPrototype = asObject(nextPrototypeValue)->unwrappedObject();
480         if (nextPrototype == this)
481             return false;
482         nextPrototypeValue = nextPrototype->prototype();
483     }
484     setPrototype(globalData, prototype);
485     return true;
486 }
487
488 inline void JSObject::setPrototype(JSGlobalData& globalData, JSValue prototype)
489 {
490     ASSERT(prototype);
491     setStructure(globalData, Structure::changePrototypeTransition(globalData, m_structure.get(), prototype));
492 }
493
494 inline void JSObject::setStructure(JSGlobalData& globalData, Structure* structure)
495 {
496     ASSERT(structure->typeInfo().overridesVisitChildren() == m_structure->typeInfo().overridesVisitChildren());
497     m_structure.set(globalData, this, structure);
498 }
499
500 inline Structure* JSObject::inheritorID(JSGlobalData& globalData)
501 {
502     if (m_inheritorID) {
503         ASSERT(m_inheritorID->isEmpty());
504         return m_inheritorID.get();
505     }
506     return createInheritorID(globalData);
507 }
508
509 inline bool Structure::isUsingInlineStorage() const
510 {
511     return propertyStorageCapacity() < JSObject::baseExternalStorageCapacity;
512 }
513
514 inline bool JSCell::inherits(const ClassInfo* info) const
515 {
516     for (const ClassInfo* ci = classInfo(); ci; ci = ci->parentClass) {
517         if (ci == info)
518             return true;
519     }
520     return false;
521 }
522
523 // this method is here to be after the inline declaration of JSCell::inherits
524 inline bool JSValue::inherits(const ClassInfo* classInfo) const
525 {
526     return isCell() && asCell()->inherits(classInfo);
527 }
528
529 ALWAYS_INLINE bool JSObject::inlineGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
530 {
531     if (WriteBarrierBase<Unknown>* location = getDirectLocation(exec->globalData(), propertyName)) {
532         if (m_structure->hasGetterSetterProperties() && location->isGetterSetter())
533             fillGetterPropertySlot(slot, location);
534         else
535             slot.setValue(this, location->get(), offsetForLocation(location));
536         return true;
537     }
538
539     // non-standard Netscape extension
540     if (propertyName == exec->propertyNames().underscoreProto) {
541         slot.setValue(prototype());
542         return true;
543     }
544
545     return false;
546 }
547
548 // It may seem crazy to inline a function this large, especially a virtual function,
549 // but it makes a big difference to property lookup that derived classes can inline their
550 // base class call to this.
551 ALWAYS_INLINE bool JSObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
552 {
553     return inlineGetOwnPropertySlot(exec, propertyName, slot);
554 }
555
556 ALWAYS_INLINE bool JSCell::fastGetOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
557 {
558     if (!structure()->typeInfo().overridesGetOwnPropertySlot())
559         return asObject(this)->inlineGetOwnPropertySlot(exec, propertyName, slot);
560     return getOwnPropertySlot(exec, propertyName, slot);
561 }
562
563 // Fast call to get a property where we may not yet have converted the string to an
564 // identifier. The first time we perform a property access with a given string, try
565 // performing the property map lookup without forming an identifier. We detect this
566 // case by checking whether the hash has yet been set for this string.
567 ALWAYS_INLINE JSValue JSCell::fastGetOwnProperty(ExecState* exec, const UString& name)
568 {
569     if (!m_structure->typeInfo().overridesGetOwnPropertySlot() && !m_structure->hasGetterSetterProperties()) {
570         size_t offset = name.impl()->hasHash()
571             ? m_structure->get(exec->globalData(), Identifier(exec, name))
572             : m_structure->get(exec->globalData(), name);
573         if (offset != WTF::notFound)
574             return asObject(this)->locationForOffset(offset)->get();
575     }
576     return JSValue();
577 }
578
579 // It may seem crazy to inline a function this large but it makes a big difference
580 // since this is function very hot in variable lookup
581 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
582 {
583     JSObject* object = this;
584     while (true) {
585         if (object->fastGetOwnPropertySlot(exec, propertyName, slot))
586             return true;
587         JSValue prototype = object->prototype();
588         if (!prototype.isObject())
589             return false;
590         object = asObject(prototype);
591     }
592 }
593
594 ALWAYS_INLINE bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
595 {
596     JSObject* object = this;
597     while (true) {
598         if (object->getOwnPropertySlot(exec, propertyName, slot))
599             return true;
600         JSValue prototype = object->prototype();
601         if (!prototype.isObject())
602             return false;
603         object = asObject(prototype);
604     }
605 }
606
607 inline JSValue JSObject::get(ExecState* exec, const Identifier& propertyName) const
608 {
609     PropertySlot slot(this);
610     if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
611         return slot.getValue(exec, propertyName);
612     
613     return jsUndefined();
614 }
615
616 inline JSValue JSObject::get(ExecState* exec, unsigned propertyName) const
617 {
618     PropertySlot slot(this);
619     if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
620         return slot.getValue(exec, propertyName);
621
622     return jsUndefined();
623 }
624
625 inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot, JSCell* specificFunction)
626 {
627     ASSERT(value);
628     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
629
630     if (m_structure->isDictionary()) {
631         unsigned currentAttributes;
632         JSCell* currentSpecificFunction;
633         size_t offset = m_structure->get(globalData, propertyName, currentAttributes, currentSpecificFunction);
634         if (offset != WTF::notFound) {
635             // If there is currently a specific function, and there now either isn't,
636             // or the new value is different, then despecify.
637             if (currentSpecificFunction && (specificFunction != currentSpecificFunction))
638                 m_structure->despecifyDictionaryFunction(globalData, propertyName);
639             if (checkReadOnly && currentAttributes & ReadOnly)
640                 return false;
641
642             putDirectOffset(globalData, offset, value);
643             // At this point, the objects structure only has a specific value set if previously there
644             // had been one set, and if the new value being specified is the same (otherwise we would
645             // have despecified, above).  So, if currentSpecificFunction is not set, or if the new
646             // value is different (or there is no new value), then the slot now has no value - and
647             // as such it is cachable.
648             // If there was previously a value, and the new value is the same, then we cannot cache.
649             if (!currentSpecificFunction || (specificFunction != currentSpecificFunction))
650                 slot.setExistingProperty(this, offset);
651             return true;
652         }
653
654         if (checkReadOnly && !isExtensible())
655             return false;
656
657         size_t currentCapacity = m_structure->propertyStorageCapacity();
658         offset = m_structure->addPropertyWithoutTransition(globalData, propertyName, attributes, specificFunction);
659         if (currentCapacity != m_structure->propertyStorageCapacity())
660             allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
661
662         ASSERT(offset < m_structure->propertyStorageCapacity());
663         putDirectOffset(globalData, offset, value);
664         // See comment on setNewProperty call below.
665         if (!specificFunction)
666             slot.setNewProperty(this, offset);
667         return true;
668     }
669
670     size_t offset;
671     size_t currentCapacity = m_structure->propertyStorageCapacity();
672     if (Structure* structure = Structure::addPropertyTransitionToExistingStructure(m_structure.get(), propertyName, attributes, specificFunction, offset)) {    
673         if (currentCapacity != structure->propertyStorageCapacity())
674             allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity());
675
676         ASSERT(offset < structure->propertyStorageCapacity());
677         setStructure(globalData, structure);
678         putDirectOffset(globalData, offset, value);
679         // This is a new property; transitions with specific values are not currently cachable,
680         // so leave the slot in an uncachable state.
681         if (!specificFunction)
682             slot.setNewProperty(this, offset);
683         return true;
684     }
685
686     unsigned currentAttributes;
687     JSCell* currentSpecificFunction;
688     offset = m_structure->get(globalData, propertyName, currentAttributes, currentSpecificFunction);
689     if (offset != WTF::notFound) {
690         if (checkReadOnly && currentAttributes & ReadOnly)
691             return false;
692
693         // There are three possibilities here:
694         //  (1) There is an existing specific value set, and we're overwriting with *the same value*.
695         //       * Do nothing - no need to despecify, but that means we can't cache (a cached
696         //         put could write a different value). Leave the slot in an uncachable state.
697         //  (2) There is a specific value currently set, but we're writing a different value.
698         //       * First, we have to despecify.  Having done so, this is now a regular slot
699         //         with no specific value, so go ahead & cache like normal.
700         //  (3) Normal case, there is no specific value set.
701         //       * Go ahead & cache like normal.
702         if (currentSpecificFunction) {
703             // case (1) Do the put, then return leaving the slot uncachable.
704             if (specificFunction == currentSpecificFunction) {
705                 putDirectOffset(globalData, offset, value);
706                 return true;
707             }
708             // case (2) Despecify, fall through to (3).
709             setStructure(globalData, Structure::despecifyFunctionTransition(globalData, m_structure.get(), propertyName));
710         }
711
712         // case (3) set the slot, do the put, return.
713         slot.setExistingProperty(this, offset);
714         putDirectOffset(globalData, offset, value);
715         return true;
716     }
717
718     if (checkReadOnly && !isExtensible())
719         return false;
720
721     Structure* structure = Structure::addPropertyTransition(globalData, m_structure.get(), propertyName, attributes, specificFunction, offset);
722
723     if (currentCapacity != structure->propertyStorageCapacity())
724         allocatePropertyStorage(currentCapacity, structure->propertyStorageCapacity());
725
726     ASSERT(offset < structure->propertyStorageCapacity());
727     setStructure(globalData, structure);
728     putDirectOffset(globalData, offset, value);
729     // This is a new property; transitions with specific values are not currently cachable,
730     // so leave the slot in an uncachable state.
731     if (!specificFunction)
732         slot.setNewProperty(this, offset);
733     return true;
734 }
735
736 inline bool JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
737 {
738     ASSERT(value);
739     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
740
741     return putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, getJSFunction(globalData, value));
742 }
743
744 inline void JSObject::putDirectInternal(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
745 {
746     PutPropertySlot slot;
747     putDirectInternal(globalData, propertyName, value, attributes, false, slot, getJSFunction(globalData, value));
748 }
749
750 inline bool JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
751 {
752     ASSERT(value);
753     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
754
755     return putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, 0);
756 }
757
758 inline void JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
759 {
760     PutPropertySlot slot;
761     putDirectInternal(globalData, propertyName, value, attributes, false, slot, 0);
762 }
763
764 inline bool JSObject::putDirect(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
765 {
766     return putDirectInternal(globalData, propertyName, value, 0, false, slot, 0);
767 }
768
769 inline void JSObject::putDirectFunction(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
770 {
771     putDirectInternal(globalData, propertyName, value, attributes, checkReadOnly, slot, value);
772 }
773
774 inline void JSObject::putDirectFunction(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attr)
775 {
776     PutPropertySlot slot;
777     putDirectInternal(globalData, propertyName, value, attr, false, slot, value);
778 }
779
780 inline void JSObject::putDirectWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, JSValue value, unsigned attributes)
781 {
782     size_t currentCapacity = m_structure->propertyStorageCapacity();
783     size_t offset = m_structure->addPropertyWithoutTransition(globalData, propertyName, attributes, 0);
784     if (currentCapacity != m_structure->propertyStorageCapacity())
785         allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
786     putDirectOffset(globalData, offset, value);
787 }
788
789 inline void JSObject::putDirectFunctionWithoutTransition(JSGlobalData& globalData, const Identifier& propertyName, JSCell* value, unsigned attributes)
790 {
791     size_t currentCapacity = m_structure->propertyStorageCapacity();
792     size_t offset = m_structure->addPropertyWithoutTransition(globalData, propertyName, attributes, value);
793     if (currentCapacity != m_structure->propertyStorageCapacity())
794         allocatePropertyStorage(currentCapacity, m_structure->propertyStorageCapacity());
795     putDirectOffset(globalData, offset, value);
796 }
797
798 inline void JSObject::transitionTo(JSGlobalData& globalData, Structure* newStructure)
799 {
800     if (m_structure->propertyStorageCapacity() != newStructure->propertyStorageCapacity())
801         allocatePropertyStorage(m_structure->propertyStorageCapacity(), newStructure->propertyStorageCapacity());
802     setStructure(globalData, newStructure);
803 }
804
805 inline JSValue JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
806 {
807     return defaultValue(exec, preferredType);
808 }
809
810 inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName) const
811 {
812     PropertySlot slot(asValue());
813     return get(exec, propertyName, slot);
814 }
815
816 inline JSValue JSValue::get(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) const
817 {
818     if (UNLIKELY(!isCell())) {
819         JSObject* prototype = synthesizePrototype(exec);
820         if (propertyName == exec->propertyNames().underscoreProto)
821             return prototype;
822         if (!prototype->getPropertySlot(exec, propertyName, slot))
823             return jsUndefined();
824         return slot.getValue(exec, propertyName);
825     }
826     JSCell* cell = asCell();
827     while (true) {
828         if (cell->fastGetOwnPropertySlot(exec, propertyName, slot))
829             return slot.getValue(exec, propertyName);
830         JSValue prototype = asObject(cell)->prototype();
831         if (!prototype.isObject())
832             return jsUndefined();
833         cell = asObject(prototype);
834     }
835 }
836
837 inline JSValue JSValue::get(ExecState* exec, unsigned propertyName) const
838 {
839     PropertySlot slot(asValue());
840     return get(exec, propertyName, slot);
841 }
842
843 inline JSValue JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const
844 {
845     if (UNLIKELY(!isCell())) {
846         JSObject* prototype = synthesizePrototype(exec);
847         if (!prototype->getPropertySlot(exec, propertyName, slot))
848             return jsUndefined();
849         return slot.getValue(exec, propertyName);
850     }
851     JSCell* cell = const_cast<JSCell*>(asCell());
852     while (true) {
853         if (cell->getOwnPropertySlot(exec, propertyName, slot))
854             return slot.getValue(exec, propertyName);
855         JSValue prototype = asObject(cell)->prototype();
856         if (!prototype.isObject())
857             return jsUndefined();
858         cell = prototype.asCell();
859     }
860 }
861
862 inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
863 {
864     if (UNLIKELY(!isCell())) {
865         synthesizeObject(exec)->put(exec, propertyName, value, slot);
866         return;
867     }
868     asCell()->put(exec, propertyName, value, slot);
869 }
870
871 inline void JSValue::putDirect(ExecState* exec, const Identifier& propertyName, JSValue value, PutPropertySlot& slot)
872 {
873     ASSERT(isCell() && isObject());
874     if (!asObject(asCell())->putDirect(exec->globalData(), propertyName, value, slot) && slot.isStrictMode())
875         throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
876 }
877
878 inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue value)
879 {
880     if (UNLIKELY(!isCell())) {
881         synthesizeObject(exec)->put(exec, propertyName, value);
882         return;
883     }
884     asCell()->put(exec, propertyName, value);
885 }
886
887 ALWAYS_INLINE void JSObject::visitChildrenDirect(SlotVisitor& visitor)
888 {
889     JSCell::visitChildren(visitor);
890
891     PropertyStorage storage = propertyStorage();
892     size_t storageSize = m_structure->propertyStorageSize();
893     visitor.appendValues(storage, storageSize);
894     if (m_inheritorID)
895         visitor.append(&m_inheritorID);
896 }
897
898 // --- JSValue inlines ----------------------------
899
900 ALWAYS_INLINE UString JSValue::toThisString(ExecState* exec) const
901 {
902     return isString() ? static_cast<JSString*>(asCell())->value(exec) : toThisObject(exec)->toString(exec);
903 }
904
905 inline JSString* JSValue::toThisJSString(ExecState* exec) const
906 {
907     return isString() ? static_cast<JSString*>(asCell()) : jsString(exec, toThisObject(exec)->toString(exec));
908 }
909
910 inline JSValue JSValue::toStrictThisObject(ExecState* exec) const
911 {
912     if (!isObject())
913         return *this;
914     return asObject(asCell())->toStrictThisObject(exec);
915 }
916
917 ALWAYS_INLINE JSObject* Register::function() const
918 {
919     if (!jsValue())
920         return 0;
921     return asObject(jsValue());
922 }
923
924 ALWAYS_INLINE Register Register::withCallee(JSObject* callee)
925 {
926     Register r;
927     r = JSValue(callee);
928     return r;
929 }
930
931 } // namespace JSC
932
933 #endif // JSObject_h