• 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 "JSFunction.h"
27 #include "JSGlobalObject.h"
28 #include "JSObject.h"
29 #include "PropertySlot.h"
30 #include <stdio.h>
31 #include <wtf/Assertions.h>
32 
33 // Set ENABLE_PERFECT_HASH_SIZE to 0 to save memory at the
34 // cost of speed.  Test your platform as results may vary.
35 #define ENABLE_PERFECT_HASH_SIZE 1
36 
37 namespace JSC {
38 
39     // Hash table generated by the create_hash_table script.
40     struct HashTableValue {
41         const char* key; // property name
42         unsigned char attributes; // JSObject attributes
43         intptr_t value1;
44         intptr_t value2;
45     };
46 
47     // FIXME: There is no reason this get function can't be simpler.
48     // ie. typedef JSValuePtr (*GetFunction)(ExecState*, JSObject* baseObject)
49     typedef PropertySlot::GetValueFunc GetFunction;
50     typedef void (*PutFunction)(ExecState*, JSObject* baseObject, JSValuePtr value);
51 
52     class HashEntry {
53     public:
initialize(UString::Rep * key,unsigned char attributes,intptr_t v1,intptr_t v2)54         void initialize(UString::Rep* key, unsigned char attributes, intptr_t v1, intptr_t v2)
55         {
56             m_key = key;
57             m_attributes = attributes;
58             m_u.store.value1 = v1;
59             m_u.store.value2 = v2;
60 #if !ENABLE(PERFECT_HASH_SIZE)
61             m_next = 0;
62 #endif
63         }
64 
setKey(UString::Rep * key)65         void setKey(UString::Rep* key) { m_key = key; }
key()66         UString::Rep* key() const { return m_key; }
67 
attributes()68         unsigned char attributes() const { return m_attributes; }
69 
function()70         NativeFunction function() const { ASSERT(m_attributes & Function); return m_u.function.functionValue; }
functionLength()71         unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast<unsigned char>(m_u.function.length); }
72 
propertyGetter()73         GetFunction propertyGetter() const { ASSERT(!(m_attributes & Function)); return m_u.property.get; }
propertyPutter()74         PutFunction propertyPutter() const { ASSERT(!(m_attributes & Function)); return m_u.property.put; }
75 
lexerValue()76         intptr_t lexerValue() const { ASSERT(!m_attributes); return m_u.lexer.value; }
77 
78 #if !ENABLE(PERFECT_HASH_SIZE)
setNext(HashEntry * next)79         void setNext(HashEntry *next) { m_next = next; }
next()80         HashEntry* next() const { return m_next; }
81 #endif
82 
83     private:
84         UString::Rep* m_key;
85         unsigned char m_attributes; // JSObject attributes
86 
87         union {
88             struct {
89                 intptr_t value1;
90                 intptr_t value2;
91             } store;
92             struct {
93                 NativeFunction functionValue;
94                 intptr_t length; // number of arguments for function
95             } function;
96             struct {
97                 GetFunction get;
98                 PutFunction put;
99             } property;
100             struct {
101                 intptr_t value;
102                 intptr_t unused;
103             } lexer;
104         } m_u;
105 
106 #if !ENABLE(PERFECT_HASH_SIZE)
107         HashEntry* m_next;
108 #endif
109     };
110 
111     struct HashTable {
112 #if ENABLE(PERFECT_HASH_SIZE)
113         int hashSizeMask; // Precomputed size for the hash table (minus 1).
114 #else
115         int compactSize;
116         int compactHashSizeMask;
117 #endif
118         const HashTableValue* values; // Fixed values generated by script.
119         mutable const HashEntry* table; // Table allocated at runtime.
120 
initializeIfNeededHashTable121         ALWAYS_INLINE void initializeIfNeeded(JSGlobalData* globalData) const
122         {
123             if (!table)
124                 createTable(globalData);
125         }
126 
initializeIfNeededHashTable127         ALWAYS_INLINE void initializeIfNeeded(ExecState* exec) const
128         {
129             if (!table)
130                 createTable(&exec->globalData());
131         }
132 
133         void deleteTable() const;
134 
135         // Find an entry in the table, and return the entry.
entryHashTable136         ALWAYS_INLINE const HashEntry* entry(JSGlobalData* globalData, const Identifier& identifier) const
137         {
138             initializeIfNeeded(globalData);
139             return entry(identifier);
140         }
141 
entryHashTable142         ALWAYS_INLINE const HashEntry* entry(ExecState* exec, const Identifier& identifier) const
143         {
144             initializeIfNeeded(exec);
145             return entry(identifier);
146         }
147 
148     private:
entryHashTable149         ALWAYS_INLINE const HashEntry* entry(const Identifier& identifier) const
150         {
151 #if ENABLE(PERFECT_HASH_SIZE)
152             ASSERT(table);
153             const HashEntry* entry = &table[identifier.ustring().rep()->computedHash() & hashSizeMask];
154             if (entry->key() != identifier.ustring().rep())
155                 return 0;
156             return entry;
157 #else
158             ASSERT(table);
159 
160             const HashEntry* entry = &table[identifier.ustring().rep()->computedHash() & compactHashSizeMask];
161 
162             if (!entry->key())
163                 return 0;
164 
165             do {
166                 if (entry->key() == identifier.ustring().rep())
167                     return entry;
168                 entry = entry->next();
169             } while (entry);
170 
171             return 0;
172 #endif
173         }
174 
175         // Convert the hash table keys to identifiers.
176         void createTable(JSGlobalData*) const;
177     };
178 
179     void setUpStaticFunctionSlot(ExecState*, const HashEntry*, JSObject* thisObject, const Identifier& propertyName, PropertySlot&);
180 
181     /**
182      * This method does it all (looking in the hashtable, checking for function
183      * overrides, creating the function or retrieving from cache, calling
184      * getValueProperty in case of a non-function property, forwarding to parent if
185      * unknown property).
186      */
187     template <class ThisImp, class ParentImp>
getStaticPropertySlot(ExecState * exec,const HashTable * table,ThisImp * thisObj,const Identifier & propertyName,PropertySlot & slot)188     inline bool getStaticPropertySlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot)
189     {
190         const HashEntry* entry = table->entry(exec, propertyName);
191 
192         if (!entry) // not found, forward to parent
193             return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot);
194 
195         if (entry->attributes() & Function)
196             setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
197         else
198             slot.setCustom(thisObj, entry->propertyGetter());
199 
200         return true;
201     }
202 
203     /**
204      * Simplified version of getStaticPropertySlot in case there are only functions.
205      * Using this instead of getStaticPropertySlot allows 'this' to avoid implementing
206      * a dummy getValueProperty.
207      */
208     template <class ParentImp>
getStaticFunctionSlot(ExecState * exec,const HashTable * table,JSObject * thisObj,const Identifier & propertyName,PropertySlot & slot)209     inline bool getStaticFunctionSlot(ExecState* exec, const HashTable* table, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot)
210     {
211         if (static_cast<ParentImp*>(thisObj)->ParentImp::getOwnPropertySlot(exec, propertyName, slot))
212             return true;
213 
214         const HashEntry* entry = table->entry(exec, propertyName);
215         if (!entry)
216             return false;
217 
218         setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
219         return true;
220     }
221 
222     /**
223      * Simplified version of getStaticPropertySlot in case there are no functions, only "values".
224      * Using this instead of getStaticPropertySlot removes the need for a FuncImp class.
225      */
226     template <class ThisImp, class ParentImp>
getStaticValueSlot(ExecState * exec,const HashTable * table,ThisImp * thisObj,const Identifier & propertyName,PropertySlot & slot)227     inline bool getStaticValueSlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot)
228     {
229         const HashEntry* entry = table->entry(exec, propertyName);
230 
231         if (!entry) // not found, forward to parent
232             return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot);
233 
234         ASSERT(!(entry->attributes() & Function));
235 
236         slot.setCustom(thisObj, entry->propertyGetter());
237         return true;
238     }
239 
240     /**
241      * This one is for "put".
242      * It looks up a hash entry for the property to be set.  If an entry
243      * is found it sets the value and returns true, else it returns false.
244      */
245     template <class ThisImp>
lookupPut(ExecState * exec,const Identifier & propertyName,JSValuePtr value,const HashTable * table,ThisImp * thisObj)246     inline bool lookupPut(ExecState* exec, const Identifier& propertyName, JSValuePtr value, const HashTable* table, ThisImp* thisObj)
247     {
248         const HashEntry* entry = table->entry(exec, propertyName);
249 
250         if (!entry)
251             return false;
252 
253         if (entry->attributes() & Function) // function: put as override property
254             thisObj->putDirect(propertyName, value);
255         else if (!(entry->attributes() & ReadOnly))
256             entry->propertyPutter()(exec, thisObj, value);
257 
258         return true;
259     }
260 
261     /**
262      * This one is for "put".
263      * It calls lookupPut<ThisImp>() to set the value.  If that call
264      * returns false (meaning no entry in the hash table was found),
265      * then it calls put() on the ParentImp class.
266      */
267     template <class ThisImp, class ParentImp>
lookupPut(ExecState * exec,const Identifier & propertyName,JSValuePtr value,const HashTable * table,ThisImp * thisObj,PutPropertySlot & slot)268     inline void lookupPut(ExecState* exec, const Identifier& propertyName, JSValuePtr value, const HashTable* table, ThisImp* thisObj, PutPropertySlot& slot)
269     {
270         if (!lookupPut<ThisImp>(exec, propertyName, value, table, thisObj))
271             thisObj->ParentImp::put(exec, propertyName, value, slot); // not found: forward to parent
272     }
273 
274 } // namespace JSC
275 
276 #endif // Lookup_h
277