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