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