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