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