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