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