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
39namespace 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()->existingHash() & 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
324