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