]>
Commit | Line | Data |
---|---|---|
b37bf2e1 A |
1 | // -*- c-basic-offset: 2 -*- |
2 | /* | |
3 | * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) | |
4 | * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved. | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | * | |
20 | */ | |
21 | ||
22 | #ifndef KJS_lookup_h | |
23 | #define KJS_lookup_h | |
24 | ||
25 | #include "ExecState.h" | |
26 | #include "function.h" | |
27 | #include "identifier.h" | |
28 | #include "JSGlobalObject.h" | |
29 | #include "object.h" | |
30 | #include <stdio.h> | |
31 | #include <wtf/Assertions.h> | |
32 | ||
33 | namespace KJS { | |
34 | ||
35 | /** | |
36 | * An entry in a hash table. | |
37 | */ | |
38 | struct HashEntry { | |
39 | /** | |
40 | * s is the key (e.g. a property name) | |
41 | */ | |
42 | const char* s; | |
43 | ||
44 | /** | |
45 | * value is the result value (enum value for properties and a function pointer to a constructor factory for functions) | |
46 | */ | |
47 | union { | |
48 | intptr_t intValue; | |
49 | PrototypeFunction::JSMemberFunction functionValue; | |
50 | } value; | |
51 | ||
52 | /** | |
53 | * attr is a set for flags (e.g. the property flags, see object.h) | |
54 | */ | |
55 | unsigned char attr; | |
56 | /** | |
57 | * params is another number. For property hashtables, it is used to | |
58 | * denote the number of argument of the function | |
59 | */ | |
60 | short int params; | |
61 | /** | |
62 | * next is the pointer to the next entry for the same hash value | |
63 | */ | |
64 | const HashEntry* next; | |
65 | }; | |
66 | ||
67 | /** | |
68 | * A hash table | |
69 | * Usually the hashtable is generated by the create_hash_table script, from a .table file. | |
70 | * | |
71 | * The implementation uses an array of entries, "size" is the total size of that array. | |
72 | * The entries between 0 and hashSize-1 are the entry points | |
73 | * for each hash value, and the entries between hashSize and size-1 | |
74 | * are the overflow entries for the hash values that need one. | |
75 | * The "next" pointer of the entry links entry points to overflow entries, | |
76 | * and links overflow entries between them. | |
77 | */ | |
78 | struct HashTable { | |
79 | /** | |
80 | * type is a version number. Currently always 2 | |
81 | */ | |
82 | int type; | |
83 | /** | |
84 | * size is the total number of entries in the hashtable, including the null entries, | |
85 | * i.e. the size of the "entries" array. | |
86 | * Used to iterate over all entries in the table | |
87 | */ | |
88 | int size; | |
89 | /** | |
90 | * pointer to the array of entries | |
91 | * Mind that some entries in the array are null (0,0,0,0). | |
92 | */ | |
93 | const HashEntry* entries; | |
94 | /** | |
95 | * the maximum value for the hash minus 1. Always smaller than size. | |
96 | */ | |
97 | int hashSizeMask; | |
98 | }; | |
99 | ||
100 | /** | |
101 | * @short Fast keyword lookup. | |
102 | */ | |
103 | class Lookup { | |
104 | public: | |
105 | /** | |
106 | * Find an entry in the table, and return its value (i.e. the value field of HashEntry) | |
107 | */ | |
108 | static int find(const struct HashTable*, const Identifier&); | |
109 | static int find(const struct HashTable*, const UChar*, unsigned int len); | |
110 | ||
111 | /** | |
112 | * Find an entry in the table, and return the entry | |
113 | * This variant gives access to the other attributes of the entry, | |
114 | * especially the attr field. | |
115 | */ | |
116 | static const HashEntry* findEntry(const struct HashTable*, const Identifier&); | |
117 | ||
118 | }; | |
119 | ||
120 | class ExecState; | |
121 | class UString; | |
122 | /** | |
123 | * @internal | |
124 | * Helper for getStaticFunctionSlot and getStaticPropertySlot | |
125 | */ | |
126 | inline JSValue* staticFunctionGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot) | |
127 | { | |
128 | // Look for cached value in dynamic map of properties (in JSObject) | |
129 | JSObject* thisObj = slot.slotBase(); | |
130 | JSValue* cachedVal = thisObj->getDirect(propertyName); | |
131 | if (cachedVal) | |
132 | return cachedVal; | |
133 | ||
134 | const HashEntry* entry = slot.staticEntry(); | |
135 | JSValue* val = new PrototypeFunction(exec, entry->params, propertyName, entry->value.functionValue); | |
136 | thisObj->putDirect(propertyName, val, entry->attr); | |
137 | return val; | |
138 | } | |
139 | ||
140 | /** | |
141 | * @internal | |
142 | * Helper for getStaticValueSlot and getStaticPropertySlot | |
143 | */ | |
144 | template <class ThisImp> | |
145 | inline JSValue* staticValueGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot) | |
146 | { | |
147 | ThisImp* thisObj = static_cast<ThisImp*>(slot.slotBase()); | |
148 | const HashEntry* entry = slot.staticEntry(); | |
149 | return thisObj->getValueProperty(exec, entry->value.intValue); | |
150 | } | |
151 | ||
152 | /** | |
153 | * Helper method for property lookups | |
154 | * | |
155 | * This method does it all (looking in the hashtable, checking for function | |
156 | * overrides, creating the function or retrieving from cache, calling | |
157 | * getValueProperty in case of a non-function property, forwarding to parent if | |
158 | * unknown property). | |
159 | * | |
160 | * Template arguments: | |
161 | * @param FuncImp the class which implements this object's functions | |
162 | * @param ThisImp the class of "this". It must implement the getValueProperty(exec,token) method, | |
163 | * for non-function properties. | |
164 | * @param ParentImp the class of the parent, to propagate the lookup. | |
165 | * | |
166 | * Method arguments: | |
167 | * @param exec execution state, as usual | |
168 | * @param propertyName the property we're looking for | |
169 | * @param table the static hashtable for this class | |
170 | * @param thisObj "this" | |
171 | */ | |
172 | template <class ThisImp, class ParentImp> | |
173 | inline bool getStaticPropertySlot(ExecState* exec, const HashTable* table, | |
174 | ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot) | |
175 | { | |
176 | const HashEntry* entry = Lookup::findEntry(table, propertyName); | |
177 | ||
178 | if (!entry) // not found, forward to parent | |
179 | return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot); | |
180 | ||
181 | if (entry->attr & Function) | |
182 | slot.setStaticEntry(thisObj, entry, staticFunctionGetter); | |
183 | else | |
184 | slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>); | |
185 | ||
186 | return true; | |
187 | } | |
188 | ||
189 | /** | |
190 | * Simplified version of getStaticPropertySlot in case there are only functions. | |
191 | * Using this instead of getStaticPropertySlot allows 'this' to avoid implementing | |
192 | * a dummy getValueProperty. | |
193 | */ | |
194 | template <class ParentImp> | |
195 | inline bool getStaticFunctionSlot(ExecState* exec, const HashTable* table, | |
196 | JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot) | |
197 | { | |
198 | const HashEntry* entry = Lookup::findEntry(table, propertyName); | |
199 | ||
200 | if (!entry) // not found, forward to parent | |
201 | return static_cast<ParentImp*>(thisObj)->ParentImp::getOwnPropertySlot(exec, propertyName, slot); | |
202 | ||
203 | ASSERT(entry->attr & Function); | |
204 | ||
205 | slot.setStaticEntry(thisObj, entry, staticFunctionGetter); | |
206 | return true; | |
207 | } | |
208 | ||
209 | /** | |
210 | * Simplified version of getStaticPropertySlot in case there are no functions, only "values". | |
211 | * Using this instead of getStaticPropertySlot removes the need for a FuncImp class. | |
212 | */ | |
213 | template <class ThisImp, class ParentImp> | |
214 | inline bool getStaticValueSlot(ExecState* exec, const HashTable* table, | |
215 | ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot) | |
216 | { | |
217 | const HashEntry* entry = Lookup::findEntry(table, propertyName); | |
218 | ||
219 | if (!entry) // not found, forward to parent | |
220 | return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot); | |
221 | ||
222 | ASSERT(!(entry->attr & Function)); | |
223 | ||
224 | slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>); | |
225 | return true; | |
226 | } | |
227 | ||
228 | /** | |
229 | * This one is for "put". | |
230 | * It looks up a hash entry for the property to be set. If an entry | |
231 | * is found it sets the value and returns true, else it returns false. | |
232 | */ | |
233 | template <class ThisImp> | |
234 | inline bool lookupPut(ExecState* exec, const Identifier& propertyName, | |
235 | JSValue* value, int attr, | |
236 | const HashTable* table, ThisImp* thisObj) | |
237 | { | |
238 | const HashEntry* entry = Lookup::findEntry(table, propertyName); | |
239 | ||
240 | if (!entry) | |
241 | return false; | |
242 | ||
243 | if (entry->attr & Function) // function: put as override property | |
244 | thisObj->JSObject::put(exec, propertyName, value, attr); | |
245 | else if (!(entry->attr & ReadOnly)) | |
246 | thisObj->putValueProperty(exec, entry->value.intValue, value, attr); | |
247 | ||
248 | return true; | |
249 | } | |
250 | ||
251 | /** | |
252 | * This one is for "put". | |
253 | * It calls lookupPut<ThisImp>() to set the value. If that call | |
254 | * returns false (meaning no entry in the hash table was found), | |
255 | * then it calls put() on the ParentImp class. | |
256 | */ | |
257 | template <class ThisImp, class ParentImp> | |
258 | inline void lookupPut(ExecState* exec, const Identifier& propertyName, | |
259 | JSValue* value, int attr, | |
260 | const HashTable* table, ThisImp* thisObj) | |
261 | { | |
262 | if (!lookupPut<ThisImp>(exec, propertyName, value, attr, table, thisObj)) | |
263 | thisObj->ParentImp::put(exec, propertyName, value, attr); // not found: forward to parent | |
264 | } | |
265 | ||
266 | /** | |
267 | * This template method retrieves or create an object that is unique | |
268 | * (for a given global object) The first time this is called (for a given | |
269 | * property name), the Object will be constructed, and set as a property | |
270 | * of the global object. Later calls will simply retrieve that cached object. | |
271 | * Note that the object constructor must take 1 argument, exec. | |
272 | */ | |
273 | template <class ClassCtor> | |
274 | inline JSObject* cacheGlobalObject(ExecState* exec, const Identifier& propertyName) | |
275 | { | |
276 | JSGlobalObject* globalObject = exec->lexicalGlobalObject(); | |
277 | JSValue* obj = globalObject->getDirect(propertyName); | |
278 | if (obj) { | |
279 | ASSERT(obj->isObject()); | |
280 | return static_cast<JSObject* >(obj); | |
281 | } | |
282 | JSObject* newObject = new ClassCtor(exec); | |
283 | globalObject->putDirect(propertyName, newObject, Internal | DontEnum); | |
284 | return newObject; | |
285 | } | |
286 | ||
287 | } // namespace | |
288 | ||
289 | /** | |
290 | * Helpers to define prototype objects (each of which simply implements | |
291 | * the functions for a type of objects). | |
292 | * Sorry for this not being very readable, but it actually saves much copy-n-paste. | |
293 | * ParentPrototype is not our base class, it's the object we use as fallback. | |
294 | * The reason for this is that there should only be ONE DOMNode.hasAttributes (e.g.), | |
295 | * not one in each derived class. So we link the (unique) prototypes between them. | |
296 | * | |
297 | * Using those macros is very simple: define the hashtable (e.g. "DOMNodePrototypeTable"), then | |
298 | * KJS_DEFINE_PROTOTYPE(DOMNodePrototype) | |
299 | * KJS_IMPLEMENT_PROTOTYPE("DOMNode", DOMNodePrototype, DOMNodePrototypeFunction) | |
300 | * and use DOMNodePrototype::self(exec) as prototype in the DOMNode constructor. | |
301 | * If the prototype has a "parent prototype", e.g. DOMElementPrototype falls back on DOMNodePrototype, | |
302 | * then the first line will use KJS_DEFINE_PROTOTYPE_WITH_PROTOTYPE, with DOMNodePrototype as the second argument. | |
303 | */ | |
304 | ||
305 | // These macros assume that a prototype's only properties are functions | |
306 | #define KJS_DEFINE_PROTOTYPE(ClassPrototype) \ | |
307 | class ClassPrototype : public KJS::JSObject { \ | |
308 | public: \ | |
309 | static KJS::JSObject* self(KJS::ExecState* exec); \ | |
310 | virtual const KJS::ClassInfo* classInfo() const { return &info; } \ | |
311 | static const KJS::ClassInfo info; \ | |
312 | bool getOwnPropertySlot(KJS::ExecState* , const KJS::Identifier&, KJS::PropertySlot&); \ | |
313 | ClassPrototype(KJS::ExecState* exec) \ | |
314 | : KJS::JSObject(exec->lexicalGlobalObject()->objectPrototype()) { } \ | |
315 | \ | |
316 | }; | |
317 | ||
318 | #define KJS_DEFINE_PROTOTYPE_WITH_PROTOTYPE(ClassPrototype, ClassPrototypePrototype) \ | |
319 | class ClassPrototype : public KJS::JSObject { \ | |
320 | public: \ | |
321 | static KJS::JSObject* self(KJS::ExecState* exec); \ | |
322 | virtual const KJS::ClassInfo* classInfo() const { return &info; } \ | |
323 | static const KJS::ClassInfo info; \ | |
324 | bool getOwnPropertySlot(KJS::ExecState*, const KJS::Identifier&, KJS::PropertySlot&); \ | |
325 | ClassPrototype(KJS::ExecState* exec) \ | |
326 | : KJS::JSObject(ClassPrototypePrototype::self(exec)) { } \ | |
327 | \ | |
328 | }; | |
329 | ||
330 | #define KJS_IMPLEMENT_PROTOTYPE(ClassName, ClassPrototype) \ | |
331 | const ClassInfo ClassPrototype::info = { ClassName"Prototype", 0, &ClassPrototype##Table }; \ | |
332 | JSObject* ClassPrototype::self(ExecState* exec) \ | |
333 | { \ | |
334 | static Identifier* prototypeIdentifier = new Identifier("[[" ClassName ".prototype]]"); \ | |
335 | return KJS::cacheGlobalObject<ClassPrototype>(exec, *prototypeIdentifier); \ | |
336 | } \ | |
337 | bool ClassPrototype::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) \ | |
338 | { \ | |
339 | return getStaticFunctionSlot<JSObject>(exec, &ClassPrototype##Table, this, propertyName, slot); \ | |
340 | } | |
341 | ||
342 | #endif // KJS_lookup_h |