9e313433901943855c0cf00be11d2a3c2eabbdb0
[WebKit.git] / JavaScriptCore / kjs / 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 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 "ExecState.h"
30 #include "JSNumberCell.h"
31 #include "PropertyMap.h"
32 #include "PropertySlot.h"
33 #include "PutPropertySlot.h"
34 #include "ScopeChain.h"
35 #include "StructureID.h"
36
37 namespace JSC {
38
39     class InternalFunction;
40     class PropertyNameArray;
41     class StructureID;
42     struct HashEntry;
43     struct HashTable;
44
45     // ECMA 262-3 8.6.1
46     // Property attributes
47     enum Attribute {
48         None         = 0,
49         ReadOnly     = 1 << 1,  // property can be only read, not written
50         DontEnum     = 1 << 2,  // property doesn't appear in (for .. in ..)
51         DontDelete   = 1 << 3,  // property can't be deleted
52         Function     = 1 << 4,  // property is a function - only used by static hashtables
53     };
54
55     class JSObject : public JSCell {
56         friend class BatchedTransitionOptimizer;
57         friend class CTI;
58
59     public:
60         explicit JSObject(PassRefPtr<StructureID>);
61
62         virtual void mark();
63
64         // The inline virtual destructor cannot be the first virtual function declared
65         // in the class as it results in the vtable being generated as a weak symbol
66         virtual ~JSObject();
67
68         bool inherits(const ClassInfo* classInfo) const { return JSCell::isObject(classInfo); }
69
70         JSValue* prototype() const;
71         void setPrototype(JSValue* prototype);
72         
73         void setStructureID(PassRefPtr<StructureID>);
74         StructureID* inheritorID();
75
76         PropertyStorage& propertyStorage() { return m_propertyStorage; }
77
78         virtual UString className() const;
79
80         JSValue* get(ExecState*, const Identifier& propertyName) const;
81         JSValue* get(ExecState*, unsigned propertyName) const;
82
83         bool getPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
84         bool getPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
85
86         virtual bool getOwnPropertySlot(ExecState*, const Identifier& propertyName, PropertySlot&);
87         virtual bool getOwnPropertySlot(ExecState*, unsigned propertyName, PropertySlot&);
88
89         virtual void put(ExecState*, const Identifier& propertyName, JSValue* value, PutPropertySlot&);
90         virtual void put(ExecState*, unsigned propertyName, JSValue* value);
91
92         virtual void putWithAttributes(ExecState*, const Identifier& propertyName, JSValue* value, unsigned attributes);
93         virtual void putWithAttributes(ExecState*, unsigned propertyName, JSValue* value, unsigned attributes);
94
95         bool propertyIsEnumerable(ExecState*, const Identifier& propertyName) const;
96
97         bool hasProperty(ExecState*, const Identifier& propertyName) const;
98         bool hasProperty(ExecState*, unsigned propertyName) const;
99         bool hasOwnProperty(ExecState*, const Identifier& propertyName) const;
100
101         virtual bool deleteProperty(ExecState*, const Identifier& propertyName);
102         virtual bool deleteProperty(ExecState*, unsigned propertyName);
103
104         virtual JSValue* defaultValue(ExecState*, PreferredPrimitiveType) const;
105
106         virtual bool hasInstance(ExecState*, JSValue*, JSValue* prototypeProperty);
107
108         virtual void getPropertyNames(ExecState*, PropertyNameArray&);
109
110         virtual JSValue* toPrimitive(ExecState*, PreferredPrimitiveType = NoPreference) const;
111         virtual bool getPrimitiveNumber(ExecState*, double& number, JSValue*& value);
112         virtual bool toBoolean(ExecState*) const;
113         virtual double toNumber(ExecState*) const;
114         virtual UString toString(ExecState*) const;
115         virtual JSObject* toObject(ExecState*) const;
116
117         virtual JSObject* toThisObject(ExecState*) const;
118         virtual JSGlobalObject* toGlobalObject(ExecState*) const;
119
120         virtual bool getPropertyAttributes(ExecState*, const Identifier& propertyName, unsigned& attributes) const;
121
122         // This get function only looks at the property map.
123         JSValue* getDirect(const Identifier& propertyName) const
124         {
125             size_t offset = m_structureID->propertyMap().get(propertyName);
126             return offset != WTF::notFound ? m_propertyStorage[offset] : 0;
127         }
128
129         JSValue** getDirectLocation(const Identifier& propertyName)
130         {
131             size_t offset = m_structureID->propertyMap().get(propertyName);
132             return offset != WTF::notFound ? locationForOffset(offset) : 0;
133         }
134
135         JSValue** getDirectLocation(const Identifier& propertyName, unsigned& attributes)
136         {
137             size_t offset = m_structureID->propertyMap().get(propertyName, attributes);
138             return offset != WTF::notFound ? locationForOffset(offset) : 0;
139         }
140
141         size_t offsetForLocation(JSValue** location)
142         {
143             return location - m_propertyStorage;
144         }
145
146         JSValue** locationForOffset(size_t offset)
147         {
148             return &m_propertyStorage[offset];
149         }
150
151         void transitionTo(StructureID*);
152
153         void removeDirect(const Identifier& propertyName);
154         bool hasCustomProperties() { return !m_structureID->propertyMap().isEmpty(); }
155         bool hasGetterSetterProperties() { return m_structureID->propertyMap().hasGetterSetterProperties(); }
156
157         void putDirect(const Identifier& propertyName, JSValue* value, unsigned attr = 0);
158         void putDirect(const Identifier& propertyName, JSValue* value, unsigned attr, bool checkReadOnly, PutPropertySlot& slot);
159         void putDirectFunction(ExecState* exec, InternalFunction* function, unsigned attr = 0);
160
161         // Fast access to known property offsets.
162         JSValue* getDirectOffset(size_t offset) { return m_propertyStorage[offset]; }
163         void putDirectOffset(size_t offset, JSValue* value) { m_propertyStorage[offset] = value; }
164
165         void fillGetterPropertySlot(PropertySlot&, JSValue** location);
166
167         virtual void defineGetter(ExecState*, const Identifier& propertyName, JSObject* getterFunction);
168         virtual void defineSetter(ExecState*, const Identifier& propertyName, JSObject* setterFunction);
169         virtual JSValue* lookupGetter(ExecState*, const Identifier& propertyName);
170         virtual JSValue* lookupSetter(ExecState*, const Identifier& propertyName);
171
172         virtual bool isGlobalObject() const { return false; }
173         virtual bool isVariableObject() const { return false; }
174         virtual bool isWatchdogException() const { return false; }
175         virtual bool isNotAnObjectErrorStub() const { return false; }
176
177         void allocatePropertyStorage(size_t oldSize, size_t newSize);
178         bool usingInlineStorage() const { return m_propertyStorage == m_inlineStorage; }
179
180         static const size_t inlineStorageCapacity = 2;
181         static const size_t nonInlineBaseStorageCapacity = 16;
182
183         static PassRefPtr<StructureID> createStructureID(JSValue* proto) { return StructureID::create(proto, TypeInfo(ObjectType)); }
184
185     protected:
186         bool getOwnPropertySlotForWrite(ExecState*, const Identifier&, PropertySlot&, bool& slotIsWriteable);
187
188     private:
189         const HashEntry* findPropertyHashEntry(ExecState*, const Identifier& propertyName) const;
190         StructureID* createInheritorID();
191
192         RefPtr<StructureID> m_inheritorID;
193
194         PropertyStorage m_propertyStorage;        
195         JSValue* m_inlineStorage[inlineStorageCapacity];
196     };
197
198   JSObject* constructEmptyObject(ExecState*);
199
200 inline JSObject::JSObject(PassRefPtr<StructureID> structureID)
201     : JSCell(structureID.releaseRef()) // ~JSObject balances this ref()
202     , m_propertyStorage(m_inlineStorage)
203 {
204     ASSERT(m_structureID);
205     ASSERT(prototype()->isNull() || Heap::heap(this) == Heap::heap(prototype()));
206 }
207
208 inline JSObject::~JSObject()
209 {
210     ASSERT(m_structureID);
211     if (m_propertyStorage != m_inlineStorage)
212         delete [] m_propertyStorage;
213     m_structureID->deref();
214 }
215
216 inline JSValue* JSObject::prototype() const
217 {
218     return m_structureID->storedPrototype();
219 }
220
221 inline void JSObject::setPrototype(JSValue* prototype)
222 {
223     ASSERT(prototype);
224     RefPtr<StructureID> newStructureID = StructureID::changePrototypeTransition(m_structureID, prototype);
225     setStructureID(newStructureID.release());
226 }
227
228 inline void JSObject::setStructureID(PassRefPtr<StructureID> structureID)
229 {
230     m_structureID->deref();
231     m_structureID = structureID.releaseRef(); // ~JSObject balances this ref()
232 }
233
234 inline StructureID* JSObject::inheritorID()
235 {
236     if (m_inheritorID)
237         return m_inheritorID.get();
238     return createInheritorID();
239 }
240
241 inline bool JSCell::isObject(const ClassInfo* info) const
242 {
243     for (const ClassInfo* ci = classInfo(); ci; ci = ci->parentClass) {
244         if (ci == info)
245             return true;
246     }
247     return false;
248 }
249
250 // this method is here to be after the inline declaration of JSCell::isObject
251 inline bool JSValue::isObject(const ClassInfo* classInfo) const
252 {
253     return !JSImmediate::isImmediate(this) && asCell()->isObject(classInfo);
254 }
255
256 inline JSValue* JSObject::get(ExecState* exec, const Identifier& propertyName) const
257 {
258     PropertySlot slot(const_cast<JSObject*>(this));
259     if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
260         return slot.getValue(exec, propertyName);
261     
262     return jsUndefined();
263 }
264
265 inline JSValue* JSObject::get(ExecState* exec, unsigned propertyName) const
266 {
267     PropertySlot slot(const_cast<JSObject*>(this));
268     if (const_cast<JSObject*>(this)->getPropertySlot(exec, propertyName, slot))
269         return slot.getValue(exec, propertyName);
270
271     return jsUndefined();
272 }
273
274 // It may seem crazy to inline a function this large but it makes a big difference
275 // since this is function very hot in variable lookup
276 inline bool JSObject::getPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
277 {
278     JSObject* object = this;
279     while (true) {
280         if (object->getOwnPropertySlot(exec, propertyName, slot))
281             return true;
282
283         JSValue* prototype = object->prototype();
284         if (!prototype->isObject())
285             return false;
286
287         object = static_cast<JSObject*>(prototype);
288     }
289 }
290
291 inline bool JSObject::getPropertySlot(ExecState* exec, unsigned propertyName, PropertySlot& slot)
292 {
293     JSObject* object = this;
294
295     while (true) {
296         if (object->getOwnPropertySlot(exec, propertyName, slot))
297             return true;
298
299         JSValue* prototype = object->prototype();
300         if (!prototype->isObject())
301             break;
302
303         object = static_cast<JSObject*>(prototype);
304     }
305
306     return false;
307 }
308
309 // It may seem crazy to inline a function this large, especially a virtual function,
310 // but it makes a big difference to property lookup that derived classes can inline their
311 // base class call to this.
312 ALWAYS_INLINE bool JSObject::getOwnPropertySlotForWrite(ExecState* exec, const Identifier& propertyName, PropertySlot& slot, bool& slotIsWriteable)
313 {
314     unsigned attributes;
315     if (JSValue** location = getDirectLocation(propertyName, attributes)) {
316         if (m_structureID->propertyMap().hasGetterSetterProperties() && location[0]->isGetterSetter()) {
317             slotIsWriteable = false;
318             fillGetterPropertySlot(slot, location);
319         } else {
320             slotIsWriteable = !(attributes & ReadOnly);
321             slot.setValueSlot(this, location, offsetForLocation(location));
322         }
323         return true;
324     }
325
326     // non-standard Netscape extension
327     if (propertyName == exec->propertyNames().underscoreProto) {
328         slot.setValue(prototype());
329         slotIsWriteable = false;
330         return true;
331     }
332
333     return false;
334 }
335
336 // It may seem crazy to inline a function this large, especially a virtual function,
337 // but it makes a big difference to property lookup that derived classes can inline their
338 // base class call to this.
339 ALWAYS_INLINE bool JSObject::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot)
340 {
341     if (JSValue** location = getDirectLocation(propertyName)) {
342         if (m_structureID->propertyMap().hasGetterSetterProperties() && location[0]->isGetterSetter())
343             fillGetterPropertySlot(slot, location);
344         else
345             slot.setValueSlot(this, location, offsetForLocation(location));
346         return true;
347     }
348
349     // non-standard Netscape extension
350     if (propertyName == exec->propertyNames().underscoreProto) {
351         slot.setValue(prototype());
352         return true;
353     }
354
355     return false;
356 }
357
358 inline void JSObject::putDirect(const Identifier& propertyName, JSValue* value, unsigned attr)
359 {
360     PutPropertySlot slot;
361     putDirect(propertyName, value, attr, false, slot);
362 }
363
364 inline void JSObject::putDirect(const Identifier& propertyName, JSValue* value, unsigned attributes, bool checkReadOnly, PutPropertySlot& slot)
365 {
366     ASSERT(!Heap::heap(value) || Heap::heap(value) == Heap::heap(this));
367
368     if (m_structureID->isDictionary()) {
369         unsigned currentAttributes;
370         size_t offset = m_structureID->propertyMap().get(propertyName, currentAttributes);
371         if (offset != WTF::notFound) {
372             if (checkReadOnly && currentAttributes & ReadOnly)
373                 return;
374             m_propertyStorage[offset] = value;
375             slot.setExistingProperty(this, offset);
376             return;
377         }
378
379         size_t currentCapacity = m_structureID->propertyStorageCapacity();
380         offset = m_structureID->propertyMap().put(propertyName, attributes);
381         if (m_structureID->propertyMap().storageSize() > m_structureID->propertyStorageCapacity()) {
382             m_structureID->growPropertyStorageCapacity();
383             allocatePropertyStorage(currentCapacity, m_structureID->propertyStorageCapacity());
384         }
385
386         m_propertyStorage[offset] = value;
387         slot.setNewProperty(this, offset);
388         m_structureID->clearEnumerationCache();
389         return;
390     }
391
392     unsigned currentAttributes;
393     size_t offset = m_structureID->propertyMap().get(propertyName, currentAttributes);
394     if (offset != WTF::notFound) {
395         if (checkReadOnly && currentAttributes & ReadOnly)
396             return;
397         m_propertyStorage[offset] = value;
398         slot.setExistingProperty(this, offset);
399         return;
400     }
401
402     size_t currentCapacity = m_structureID->propertyStorageCapacity();
403     RefPtr<StructureID> structureID = StructureID::addPropertyTransition(m_structureID, propertyName, attributes, offset);
404     if (currentCapacity != structureID->propertyStorageCapacity())
405         allocatePropertyStorage(currentCapacity, structureID->propertyStorageCapacity());
406
407     ASSERT(offset < structureID->propertyStorageCapacity());
408     m_propertyStorage[offset] = value;
409     slot.setNewProperty(this, offset);
410     slot.setWasTransition(true);
411     setStructureID(structureID.release());
412 }
413
414 inline void JSObject::transitionTo(StructureID* newStructureID)
415 {
416     if (m_structureID->propertyStorageCapacity() != newStructureID->propertyStorageCapacity())
417         allocatePropertyStorage(m_structureID->propertyStorageCapacity(), newStructureID->propertyStorageCapacity());
418     setStructureID(newStructureID);
419 }
420
421 inline JSValue* JSObject::toPrimitive(ExecState* exec, PreferredPrimitiveType preferredType) const
422 {
423     return defaultValue(exec, preferredType);
424 }
425
426 inline JSValue* JSValue::get(ExecState* exec, const Identifier& propertyName) const
427 {
428     PropertySlot slot(const_cast<JSValue*>(this));
429     return get(exec, propertyName, slot);
430 }
431
432 inline JSValue* JSValue::get(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) const
433 {
434     if (UNLIKELY(JSImmediate::isImmediate(this))) {
435         JSObject* prototype = JSImmediate::prototype(this, exec);
436         if (!prototype->getPropertySlot(exec, propertyName, slot))
437             return jsUndefined();
438         return slot.getValue(exec, propertyName);
439     }
440     JSCell* cell = static_cast<JSCell*>(const_cast<JSValue*>(this));
441     while (true) {
442         if (cell->getOwnPropertySlot(exec, propertyName, slot))
443             return slot.getValue(exec, propertyName);
444         ASSERT(cell->isObject());
445         JSValue* prototype = static_cast<JSObject*>(cell)->prototype();
446         if (!prototype->isObject())
447             return jsUndefined();
448         cell = static_cast<JSCell*>(prototype);
449     }
450 }
451
452 inline JSValue* JSValue::get(ExecState* exec, unsigned propertyName) const
453 {
454     PropertySlot slot(const_cast<JSValue*>(this));
455     return get(exec, propertyName, slot);
456 }
457
458 inline JSValue* JSValue::get(ExecState* exec, unsigned propertyName, PropertySlot& slot) const
459 {
460     if (UNLIKELY(JSImmediate::isImmediate(this))) {
461         JSObject* prototype = JSImmediate::prototype(this, exec);
462         if (!prototype->getPropertySlot(exec, propertyName, slot))
463             return jsUndefined();
464         return slot.getValue(exec, propertyName);
465     }
466     JSCell* cell = const_cast<JSCell*>(asCell());
467     while (true) {
468         if (cell->getOwnPropertySlot(exec, propertyName, slot))
469             return slot.getValue(exec, propertyName);
470         ASSERT(cell->isObject());
471         JSValue* prototype = static_cast<JSObject*>(cell)->prototype();
472         if (!prototype->isObject())
473             return jsUndefined();
474         cell = static_cast<JSCell*>(prototype);
475     }
476 }
477
478 inline void JSValue::put(ExecState* exec, const Identifier& propertyName, JSValue* value, PutPropertySlot& slot)
479 {
480     if (UNLIKELY(JSImmediate::isImmediate(this))) {
481         JSImmediate::toObject(this, exec)->put(exec, propertyName, value, slot);
482         return;
483     }
484     asCell()->put(exec, propertyName, value, slot);
485 }
486
487 inline void JSValue::put(ExecState* exec, unsigned propertyName, JSValue* value)
488 {
489     if (UNLIKELY(JSImmediate::isImmediate(this))) {
490         JSImmediate::toObject(this, exec)->put(exec, propertyName, value);
491         return;
492     }
493     asCell()->put(exec, propertyName, value);
494 }
495
496 } // namespace JSC
497
498 #endif // JSObject_h