De-virtualize JSObject::getOwnPropertyDescriptor
[WebKit-https.git] / Source / JavaScriptCore / runtime / Lookup.h
1 /*
2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
4  *
5  *  This library is free software; you can redistribute it and/or
6  *  modify it under the terms of the GNU Lesser General Public
7  *  License as published by the Free Software Foundation; either
8  *  version 2 of the License, or (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  *  Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public
16  *  License along with this library; if not, write to the Free Software
17  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  */
20
21 #ifndef Lookup_h
22 #define Lookup_h
23
24 #include "CallFrame.h"
25 #include "DFGIntrinsic.h"
26 #include "Identifier.h"
27 #include "JSGlobalObject.h"
28 #include "PropertySlot.h"
29 #include <stdio.h>
30 #include <wtf/Assertions.h>
31
32 // Bug #26843: Work around Metrowerks compiler bug
33 #if COMPILER(WINSCW)
34 #define JSC_CONST_HASHTABLE
35 #else
36 #define JSC_CONST_HASHTABLE const
37 #endif
38
39 namespace JSC {
40     // Hash table generated by the create_hash_table script.
41     struct HashTableValue {
42         const char* key; // property name
43         unsigned char attributes; // JSObject attributes
44         intptr_t value1;
45         intptr_t value2;
46 #if ENABLE(JIT)
47         ThunkGenerator generator;
48 #if ENABLE(DFG_JIT)
49         DFG::Intrinsic intrinsic;
50 #endif
51 #endif
52     };
53
54     // FIXME: There is no reason this get function can't be simpler.
55     // ie. typedef JSValue (*GetFunction)(ExecState*, JSObject* baseObject)
56     typedef PropertySlot::GetValueFunc GetFunction;
57     typedef void (*PutFunction)(ExecState*, JSObject* baseObject, JSValue value);
58
59     class HashEntry {
60         WTF_MAKE_FAST_ALLOCATED;
61     public:
62         void initialize(StringImpl* key, unsigned char attributes, intptr_t v1, intptr_t v2
63 #if ENABLE(JIT)
64                         , ThunkGenerator generator = 0
65 #if ENABLE(DFG_JIT)
66                         , DFG::Intrinsic intrinsic = DFG::NoIntrinsic
67 #endif
68 #endif
69                         )
70         {
71             m_key = key;
72             m_attributes = attributes;
73             m_u.store.value1 = v1;
74             m_u.store.value2 = v2;
75 #if ENABLE(JIT)
76             m_u.function.generator = generator;
77 #if ENABLE(DFG_JIT)
78             m_u.function.intrinsic = intrinsic;
79 #endif
80 #endif
81             m_next = 0;
82         }
83
84         void setKey(StringImpl* key) { m_key = key; }
85         StringImpl* key() const { return m_key; }
86
87         unsigned char attributes() const { return m_attributes; }
88
89 #if ENABLE(JIT)
90         ThunkGenerator generator() const { ASSERT(m_attributes & Function); return m_u.function.generator; }
91         DFG::Intrinsic intrinsic() const
92         {
93             ASSERT(m_attributes & Function);
94 #if ENABLE(DFG_JIT)
95             return m_u.function.intrinsic;
96 #else
97             return DFG::NoIntrinsic;
98 #endif
99         }
100 #endif
101         NativeFunction function() const { ASSERT(m_attributes & Function); return m_u.function.functionValue; }
102         unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast<unsigned char>(m_u.function.length); }
103
104         GetFunction propertyGetter() const { ASSERT(!(m_attributes & Function)); return m_u.property.get; }
105         PutFunction propertyPutter() const { ASSERT(!(m_attributes & Function)); return m_u.property.put; }
106
107         intptr_t lexerValue() const { ASSERT(!m_attributes); return m_u.lexer.value; }
108
109         void setNext(HashEntry *next) { m_next = next; }
110         HashEntry* next() const { return m_next; }
111
112     private:
113         StringImpl* m_key;
114         unsigned char m_attributes; // JSObject attributes
115
116         union {
117             struct {
118                 intptr_t value1;
119                 intptr_t value2;
120             } store;
121             struct {
122                 NativeFunction functionValue;
123                 intptr_t length; // number of arguments for function
124 #if ENABLE(JIT)
125                 ThunkGenerator generator;
126 #if ENABLE(DFG_JIT)
127                 DFG::Intrinsic intrinsic;
128 #endif
129 #endif
130             } function;
131             struct {
132                 GetFunction get;
133                 PutFunction put;
134             } property;
135             struct {
136                 intptr_t value;
137                 intptr_t unused;
138             } lexer;
139         } m_u;
140
141         HashEntry* m_next;
142     };
143
144     struct HashTable {
145
146         int compactSize;
147         int compactHashSizeMask;
148
149         const HashTableValue* values; // Fixed values generated by script.
150         mutable const HashEntry* table; // Table allocated at runtime.
151
152         ALWAYS_INLINE void initializeIfNeeded(JSGlobalData* globalData) const
153         {
154             if (!table)
155                 createTable(globalData);
156         }
157
158         ALWAYS_INLINE void initializeIfNeeded(ExecState* exec) const
159         {
160             if (!table)
161                 createTable(&exec->globalData());
162         }
163
164         void deleteTable() const;
165
166         // Find an entry in the table, and return the entry.
167         ALWAYS_INLINE const HashEntry* entry(JSGlobalData* globalData, const Identifier& identifier) const
168         {
169             initializeIfNeeded(globalData);
170             return entry(identifier);
171         }
172
173         ALWAYS_INLINE const HashEntry* entry(ExecState* exec, const Identifier& identifier) const
174         {
175             initializeIfNeeded(exec);
176             return entry(identifier);
177         }
178
179         class ConstIterator {
180         public:
181             ConstIterator(const HashTable* table, int position)
182                 : m_table(table)
183                 , m_position(position)
184             {
185                 skipInvalidKeys();
186             }
187
188             const HashEntry* operator->()
189             {
190                 return &m_table->table[m_position];
191             }
192
193             const HashEntry* operator*()
194             {
195                 return &m_table->table[m_position];
196             }
197
198             bool operator!=(const ConstIterator& other)
199             {
200                 ASSERT(m_table == other.m_table);
201                 return m_position != other.m_position;
202             }
203             
204             ConstIterator& operator++()
205             {
206                 ASSERT(m_position < m_table->compactSize);
207                 ++m_position;
208                 skipInvalidKeys();
209                 return *this;
210             }
211
212         private:
213             void skipInvalidKeys()
214             {
215                 ASSERT(m_position <= m_table->compactSize);
216                 while (m_position < m_table->compactSize && !m_table->table[m_position].key())
217                     ++m_position;
218                 ASSERT(m_position <= m_table->compactSize);
219             }
220             
221             const HashTable* m_table;
222             int m_position;
223         };
224
225         ConstIterator begin(JSGlobalData& globalData) const
226         {
227             initializeIfNeeded(&globalData);
228             return ConstIterator(this, 0);
229         }
230         ConstIterator end(JSGlobalData& globalData) const
231         {
232             initializeIfNeeded(&globalData);
233             return ConstIterator(this, compactSize);
234         }
235
236     private:
237         ALWAYS_INLINE const HashEntry* entry(const Identifier& identifier) const
238         {
239             ASSERT(table);
240
241             const HashEntry* entry = &table[identifier.impl()->existingHash() & compactHashSizeMask];
242
243             if (!entry->key())
244                 return 0;
245
246             do {
247                 if (entry->key() == identifier.impl())
248                     return entry;
249                 entry = entry->next();
250             } while (entry);
251
252             return 0;
253         }
254
255         // Convert the hash table keys to identifiers.
256         void createTable(JSGlobalData*) const;
257     };
258
259     bool setUpStaticFunctionSlot(ExecState*, const HashEntry*, JSObject* thisObject, const Identifier& propertyName, PropertySlot&);
260
261     /**
262      * This method does it all (looking in the hashtable, checking for function
263      * overrides, creating the function or retrieving from cache, calling
264      * getValueProperty in case of a non-function property, forwarding to parent if
265      * unknown property).
266      */
267     template <class ThisImp, class ParentImp>
268     inline bool getStaticPropertySlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot)
269     {
270         const HashEntry* entry = table->entry(exec, propertyName);
271
272         if (!entry) // not found, forward to parent
273             return ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot);
274
275         if (entry->attributes() & Function)
276             return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
277
278         slot.setCacheableCustom(thisObj, entry->propertyGetter());
279         return true;
280     }
281
282     template <class ThisImp, class ParentImp>
283     inline bool getStaticPropertyDescriptor(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertyDescriptor& descriptor)
284     {
285         const HashEntry* entry = table->entry(exec, propertyName);
286         
287         if (!entry) // not found, forward to parent
288             return ParentImp::getOwnPropertyDescriptor(thisObj, exec, propertyName, descriptor);
289  
290         PropertySlot slot;
291         if (entry->attributes() & Function) {
292             bool present = setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
293             if (present)
294                 descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes());
295             return present;
296         }
297
298         slot.setCustom(thisObj, entry->propertyGetter());
299         descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes());
300         return true;
301     }
302
303     /**
304      * Simplified version of getStaticPropertySlot in case there are only functions.
305      * Using this instead of getStaticPropertySlot allows 'this' to avoid implementing
306      * a dummy getValueProperty.
307      */
308     template <class ParentImp>
309     inline bool getStaticFunctionSlot(ExecState* exec, const HashTable* table, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot)
310     {
311         if (ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot))
312             return true;
313
314         const HashEntry* entry = table->entry(exec, propertyName);
315         if (!entry)
316             return false;
317
318         return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
319     }
320     
321     /**
322      * Simplified version of getStaticPropertyDescriptor in case there are only functions.
323      * Using this instead of getStaticPropertyDescriptor allows 'this' to avoid implementing
324      * a dummy getValueProperty.
325      */
326     template <class ParentImp>
327     inline bool getStaticFunctionDescriptor(ExecState* exec, const HashTable* table, JSObject* thisObj, const Identifier& propertyName, PropertyDescriptor& descriptor)
328     {
329         if (ParentImp::getOwnPropertyDescriptor(static_cast<ParentImp*>(thisObj), exec, propertyName, descriptor))
330             return true;
331         
332         const HashEntry* entry = table->entry(exec, propertyName);
333         if (!entry)
334             return false;
335         
336         PropertySlot slot;
337         bool present = setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
338         if (present)
339             descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes());
340         return present;
341     }
342
343     /**
344      * Simplified version of getStaticPropertySlot in case there are no functions, only "values".
345      * Using this instead of getStaticPropertySlot removes the need for a FuncImp class.
346      */
347     template <class ThisImp, class ParentImp>
348     inline bool getStaticValueSlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot)
349     {
350         const HashEntry* entry = table->entry(exec, propertyName);
351
352         if (!entry) // not found, forward to parent
353             return ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot);
354
355         ASSERT(!(entry->attributes() & Function));
356
357         slot.setCacheableCustom(thisObj, entry->propertyGetter());
358         return true;
359     }
360
361     /**
362      * Simplified version of getStaticPropertyDescriptor in case there are no functions, only "values".
363      * Using this instead of getStaticPropertyDescriptor removes the need for a FuncImp class.
364      */
365     template <class ThisImp, class ParentImp>
366     inline bool getStaticValueDescriptor(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertyDescriptor& descriptor)
367     {
368         const HashEntry* entry = table->entry(exec, propertyName);
369         
370         if (!entry) // not found, forward to parent
371             return ParentImp::getOwnPropertyDescriptor(thisObj, exec, propertyName, descriptor);
372         
373         ASSERT(!(entry->attributes() & Function));
374         PropertySlot slot;
375         slot.setCustom(thisObj, entry->propertyGetter());
376         descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes());
377         return true;
378     }
379
380     /**
381      * This one is for "put".
382      * It looks up a hash entry for the property to be set.  If an entry
383      * is found it sets the value and returns true, else it returns false.
384      */
385     template <class ThisImp>
386     inline bool lookupPut(ExecState* exec, const Identifier& propertyName, JSValue value, const HashTable* table, ThisImp* thisObj)
387     {
388         const HashEntry* entry = table->entry(exec, propertyName);
389
390         if (!entry)
391             return false;
392
393         // If this is a function put it as an override property.
394         if (entry->attributes() & Function)
395             thisObj->putDirect(exec->globalData(), propertyName, value);
396         else if (!(entry->attributes() & ReadOnly))
397             entry->propertyPutter()(exec, thisObj, value);
398
399         return true;
400     }
401
402     /**
403      * This one is for "put".
404      * It calls lookupPut<ThisImp>() to set the value.  If that call
405      * returns false (meaning no entry in the hash table was found),
406      * then it calls put() on the ParentImp class.
407      */
408     template <class ThisImp, class ParentImp>
409     inline void lookupPut(ExecState* exec, const Identifier& propertyName, JSValue value, const HashTable* table, ThisImp* thisObj, PutPropertySlot& slot)
410     {
411         if (!lookupPut<ThisImp>(exec, propertyName, value, table, thisObj))
412             ParentImp::put(thisObj, exec, propertyName, value, slot); // not found: forward to parent
413     }
414
415 } // namespace JSC
416
417 #endif // Lookup_h