• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "Identifier.h"
26 #include "JSGlobalObject.h"
27 #include "JSObject.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 #endif
49     };
50 
51     // FIXME: There is no reason this get function can't be simpler.
52     // ie. typedef JSValue (*GetFunction)(ExecState*, JSObject* baseObject)
53     typedef PropertySlot::GetValueFunc GetFunction;
54     typedef void (*PutFunction)(ExecState*, JSObject* baseObject, JSValue value);
55 
56     class HashEntry {
57         WTF_MAKE_FAST_ALLOCATED;
58     public:
59         void initialize(StringImpl* key, unsigned char attributes, intptr_t v1, intptr_t v2
60 #if ENABLE(JIT)
61                         , ThunkGenerator generator = 0
62 #endif
63                         )
64         {
65             m_key = key;
66             m_attributes = attributes;
67             m_u.store.value1 = v1;
68             m_u.store.value2 = v2;
69 #if ENABLE(JIT)
70             m_u.function.generator = generator;
71 #endif
72             m_next = 0;
73         }
74 
setKey(StringImpl * key)75         void setKey(StringImpl* key) { m_key = key; }
key()76         StringImpl* key() const { return m_key; }
77 
attributes()78         unsigned char attributes() const { return m_attributes; }
79 
80 #if ENABLE(JIT) && ENABLE(JIT_OPTIMIZE_NATIVE_CALL)
generator()81         ThunkGenerator generator() const { ASSERT(m_attributes & Function); return m_u.function.generator; }
82 #endif
function()83         NativeFunction function() const { ASSERT(m_attributes & Function); return m_u.function.functionValue; }
functionLength()84         unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast<unsigned char>(m_u.function.length); }
85 
propertyGetter()86         GetFunction propertyGetter() const { ASSERT(!(m_attributes & Function)); return m_u.property.get; }
propertyPutter()87         PutFunction propertyPutter() const { ASSERT(!(m_attributes & Function)); return m_u.property.put; }
88 
lexerValue()89         intptr_t lexerValue() const { ASSERT(!m_attributes); return m_u.lexer.value; }
90 
setNext(HashEntry * next)91         void setNext(HashEntry *next) { m_next = next; }
next()92         HashEntry* next() const { return m_next; }
93 
94     private:
95         StringImpl* m_key;
96         unsigned char m_attributes; // JSObject attributes
97 
98         union {
99             struct {
100                 intptr_t value1;
101                 intptr_t value2;
102             } store;
103             struct {
104                 NativeFunction functionValue;
105                 intptr_t length; // number of arguments for function
106 #if ENABLE(JIT)
107                 ThunkGenerator generator;
108 #endif
109             } function;
110             struct {
111                 GetFunction get;
112                 PutFunction put;
113             } property;
114             struct {
115                 intptr_t value;
116                 intptr_t unused;
117             } lexer;
118         } m_u;
119 
120         HashEntry* m_next;
121     };
122 
123     struct HashTable {
124 
125         int compactSize;
126         int compactHashSizeMask;
127 
128         const HashTableValue* values; // Fixed values generated by script.
129         mutable const HashEntry* table; // Table allocated at runtime.
130 
initializeIfNeededHashTable131         ALWAYS_INLINE void initializeIfNeeded(JSGlobalData* globalData) const
132         {
133             if (!table)
134                 createTable(globalData);
135         }
136 
initializeIfNeededHashTable137         ALWAYS_INLINE void initializeIfNeeded(ExecState* exec) const
138         {
139             if (!table)
140                 createTable(&exec->globalData());
141         }
142 
143         void deleteTable() const;
144 
145         // Find an entry in the table, and return the entry.
entryHashTable146         ALWAYS_INLINE const HashEntry* entry(JSGlobalData* globalData, const Identifier& identifier) const
147         {
148             initializeIfNeeded(globalData);
149             return entry(identifier);
150         }
151 
entryHashTable152         ALWAYS_INLINE const HashEntry* entry(ExecState* exec, const Identifier& identifier) const
153         {
154             initializeIfNeeded(exec);
155             return entry(identifier);
156         }
157 
158     private:
entryHashTable159         ALWAYS_INLINE const HashEntry* entry(const Identifier& identifier) const
160         {
161             ASSERT(table);
162 
163             const HashEntry* entry = &table[identifier.impl()->existingHash() & compactHashSizeMask];
164 
165             if (!entry->key())
166                 return 0;
167 
168             do {
169                 if (entry->key() == identifier.impl())
170                     return entry;
171                 entry = entry->next();
172             } while (entry);
173 
174             return 0;
175         }
176 
177         // Convert the hash table keys to identifiers.
178         void createTable(JSGlobalData*) const;
179     };
180 
181     void setUpStaticFunctionSlot(ExecState*, const HashEntry*, JSObject* thisObject, const Identifier& propertyName, PropertySlot&);
182 
183     /**
184      * This method does it all (looking in the hashtable, checking for function
185      * overrides, creating the function or retrieving from cache, calling
186      * getValueProperty in case of a non-function property, forwarding to parent if
187      * unknown property).
188      */
189     template <class ThisImp, class ParentImp>
getStaticPropertySlot(ExecState * exec,const HashTable * table,ThisImp * thisObj,const Identifier & propertyName,PropertySlot & slot)190     inline bool getStaticPropertySlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot)
191     {
192         const HashEntry* entry = table->entry(exec, propertyName);
193 
194         if (!entry) // not found, forward to parent
195             return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot);
196 
197         if (entry->attributes() & Function)
198             setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
199         else
200             slot.setCacheableCustom(thisObj, entry->propertyGetter());
201 
202         return true;
203     }
204 
205     template <class ThisImp, class ParentImp>
getStaticPropertyDescriptor(ExecState * exec,const HashTable * table,ThisImp * thisObj,const Identifier & propertyName,PropertyDescriptor & descriptor)206     inline bool getStaticPropertyDescriptor(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertyDescriptor& descriptor)
207     {
208         const HashEntry* entry = table->entry(exec, propertyName);
209 
210         if (!entry) // not found, forward to parent
211             return thisObj->ParentImp::getOwnPropertyDescriptor(exec, propertyName, descriptor);
212 
213         PropertySlot slot;
214         if (entry->attributes() & Function)
215             setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
216         else
217             slot.setCustom(thisObj, entry->propertyGetter());
218 
219         descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes());
220         return true;
221     }
222 
223     /**
224      * Simplified version of getStaticPropertySlot in case there are only functions.
225      * Using this instead of getStaticPropertySlot allows 'this' to avoid implementing
226      * a dummy getValueProperty.
227      */
228     template <class ParentImp>
getStaticFunctionSlot(ExecState * exec,const HashTable * table,JSObject * thisObj,const Identifier & propertyName,PropertySlot & slot)229     inline bool getStaticFunctionSlot(ExecState* exec, const HashTable* table, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot)
230     {
231         if (static_cast<ParentImp*>(thisObj)->ParentImp::getOwnPropertySlot(exec, propertyName, slot))
232             return true;
233 
234         const HashEntry* entry = table->entry(exec, propertyName);
235         if (!entry)
236             return false;
237 
238         setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
239         return true;
240     }
241 
242     /**
243      * Simplified version of getStaticPropertyDescriptor in case there are only functions.
244      * Using this instead of getStaticPropertyDescriptor allows 'this' to avoid implementing
245      * a dummy getValueProperty.
246      */
247     template <class ParentImp>
getStaticFunctionDescriptor(ExecState * exec,const HashTable * table,JSObject * thisObj,const Identifier & propertyName,PropertyDescriptor & descriptor)248     inline bool getStaticFunctionDescriptor(ExecState* exec, const HashTable* table, JSObject* thisObj, const Identifier& propertyName, PropertyDescriptor& descriptor)
249     {
250         if (static_cast<ParentImp*>(thisObj)->ParentImp::getOwnPropertyDescriptor(exec, propertyName, descriptor))
251             return true;
252 
253         const HashEntry* entry = table->entry(exec, propertyName);
254         if (!entry)
255             return false;
256 
257         PropertySlot slot;
258         setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
259         descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes());
260         return true;
261     }
262 
263     /**
264      * Simplified version of getStaticPropertySlot in case there are no functions, only "values".
265      * Using this instead of getStaticPropertySlot removes the need for a FuncImp class.
266      */
267     template <class ThisImp, class ParentImp>
getStaticValueSlot(ExecState * exec,const HashTable * table,ThisImp * thisObj,const Identifier & propertyName,PropertySlot & slot)268     inline bool getStaticValueSlot(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 thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot);
274 
275         ASSERT(!(entry->attributes() & Function));
276 
277         slot.setCacheableCustom(thisObj, entry->propertyGetter());
278         return true;
279     }
280 
281     /**
282      * Simplified version of getStaticPropertyDescriptor in case there are no functions, only "values".
283      * Using this instead of getStaticPropertyDescriptor removes the need for a FuncImp class.
284      */
285     template <class ThisImp, class ParentImp>
getStaticValueDescriptor(ExecState * exec,const HashTable * table,ThisImp * thisObj,const Identifier & propertyName,PropertyDescriptor & descriptor)286     inline bool getStaticValueDescriptor(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertyDescriptor& descriptor)
287     {
288         const HashEntry* entry = table->entry(exec, propertyName);
289 
290         if (!entry) // not found, forward to parent
291             return thisObj->ParentImp::getOwnPropertyDescriptor(exec, propertyName, descriptor);
292 
293         ASSERT(!(entry->attributes() & Function));
294         PropertySlot slot;
295         slot.setCustom(thisObj, entry->propertyGetter());
296         descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes());
297         return true;
298     }
299 
300     /**
301      * This one is for "put".
302      * It looks up a hash entry for the property to be set.  If an entry
303      * is found it sets the value and returns true, else it returns false.
304      */
305     template <class ThisImp>
lookupPut(ExecState * exec,const Identifier & propertyName,JSValue value,const HashTable * table,ThisImp * thisObj)306     inline bool lookupPut(ExecState* exec, const Identifier& propertyName, JSValue value, const HashTable* table, ThisImp* thisObj)
307     {
308         const HashEntry* entry = table->entry(exec, propertyName);
309 
310         if (!entry)
311             return false;
312 
313         if (entry->attributes() & Function) { // function: put as override property
314             if (LIKELY(value.isCell()))
315                 thisObj->putDirectFunction(exec->globalData(), propertyName, value.asCell());
316             else
317                 thisObj->putDirect(exec->globalData(), propertyName, value);
318         } else if (!(entry->attributes() & ReadOnly))
319             entry->propertyPutter()(exec, thisObj, value);
320 
321         return true;
322     }
323 
324     /**
325      * This one is for "put".
326      * It calls lookupPut<ThisImp>() to set the value.  If that call
327      * returns false (meaning no entry in the hash table was found),
328      * then it calls put() on the ParentImp class.
329      */
330     template <class ThisImp, class ParentImp>
lookupPut(ExecState * exec,const Identifier & propertyName,JSValue value,const HashTable * table,ThisImp * thisObj,PutPropertySlot & slot)331     inline void lookupPut(ExecState* exec, const Identifier& propertyName, JSValue value, const HashTable* table, ThisImp* thisObj, PutPropertySlot& slot)
332     {
333         if (!lookupPut<ThisImp>(exec, propertyName, value, table, thisObj))
334             thisObj->ParentImp::put(exec, propertyName, value, slot); // not found: forward to parent
335     }
336 
337 } // namespace JSC
338 
339 #endif // Lookup_h
340