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