fix logic
[personal-kdelibs.git] / kjs / lookup.h
blob134324df8e34536d8bc3e27c6c88635008f04694
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.
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.
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.
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
23 #ifndef _KJSLOOKUP_H_
24 #define _KJSLOOKUP_H_
26 #include "interpreter.h"
27 #include "identifier.h"
28 #include "object.h"
29 #include <stdio.h>
31 namespace KJS {
32 class FunctionPrototype;
34 /**
35 * An entry in a hash table.
37 struct KJS_EXPORT HashEntry {
38 /**
39 * s is the key (e.g. a property name)
41 const char *s;
43 /**
44 * value is the result value (usually an enum value)
46 int value;
47 /**
48 * attr is a set for flags (e.g. the property flags, see object.h)
50 short int attr;
51 /**
52 * params is another number. For property hashtables, it is used to
53 * denote the number of argument of the function
55 short int params;
56 /**
57 * next is the pointer to the next entry for the same hash value
59 const HashEntry *next;
62 /**
63 * A hash table
64 * Usually the hashtable is generated by the create_hash_table script, from a .table file.
66 * The implementation uses an array of entries, "size" is the total size of that array.
67 * The entries between 0 and hashSize-1 are the entry points
68 * for each hash value, and the entries between hashSize and size-1
69 * are the overflow entries for the hash values that need one.
70 * The "next" pointer of the entry links entry points to overflow entries,
71 * and links overflow entries between them.
73 struct HashTable {
74 /**
75 * type is a version number. Currently always 2
77 int type;
78 /**
79 * size is the total number of entries in the hashtable, including the null entries,
80 * i.e. the size of the "entries" array.
81 * Used to iterate over all entries in the table
83 int size;
84 /**
85 * pointer to the array of entries
86 * Mind that some entries in the array are null (0,0,0,0).
88 const HashEntry *entries;
89 /**
90 * the maximum value for the hash. Always smaller than size.
92 int hashSize;
95 /**
96 * @short Fast keyword lookup.
98 class KJS_EXPORT Lookup {
99 public:
101 * Find an entry in the table, and return its value (i.e. the value field of HashEntry)
103 static int find(const struct HashTable *table, const Identifier &s);
104 static int find(const struct HashTable *table,
105 const UChar *c, unsigned int len);
109 * Find an entry in the table, and return the entry
110 * This variant gives access to the other attributes of the entry,
111 * especially the attr field.
113 static const HashEntry* findEntry(const struct HashTable *table,
114 const Identifier &s);
118 class ExecState;
120 * @internal
121 * Helper for getStaticFunctionSlot and getStaticPropertySlot
123 template <class FuncImp>
124 inline JSValue *staticFunctionGetter(ExecState *exec, JSObject * /*originalObject*/, const Identifier& propertyName, const PropertySlot& slot)
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;
132 const HashEntry *entry = slot.staticEntry();
133 JSValue *val = new FuncImp(exec, entry->value, entry->params, propertyName);
134 thisObj->putDirect(propertyName, val, entry->attr);
135 return val;
139 * @internal
140 * Helper for getStaticValueSlot and getStaticPropertySlot
142 template <class ThisImp>
143 inline JSValue *staticValueGetter(ExecState *exec, JSObject*, const Identifier&, const PropertySlot& slot)
145 ThisImp* thisObj = static_cast<ThisImp*>(slot.slotBase());
146 const HashEntry* entry = slot.staticEntry();
147 return thisObj->getValueProperty(exec, entry->value);
151 * Helper method for property lookups
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).
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.
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"
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)
174 const HashEntry* entry = Lookup::findEntry(table, propertyName);
176 if (!entry) // not found, forward to parent
177 return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot);
179 if (entry->attr & Function)
180 slot.setStaticEntry(thisObj, entry, staticFunctionGetter<FuncImp>);
181 else
182 slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>);
184 return true;
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.
192 template <class FuncImp, class ParentImp>
193 inline bool getStaticFunctionSlot(ExecState *exec, const HashTable *table,
194 JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot)
196 const HashEntry* entry = Lookup::findEntry(table, propertyName);
198 if (!entry) // not found, forward to parent
199 return static_cast<ParentImp *>(thisObj)->ParentImp::getOwnPropertySlot(exec, propertyName, slot);
201 assert(entry->attr & Function);
203 slot.setStaticEntry(thisObj, entry, staticFunctionGetter<FuncImp>);
204 return true;
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.
211 template <class ThisImp, class ParentImp>
212 inline bool getStaticValueSlot(ExecState *exec, const HashTable* table,
213 ThisImp* thisObj, const Identifier &propertyName, PropertySlot& slot)
215 const HashEntry* entry = Lookup::findEntry(table, propertyName);
217 if (!entry) // not found, forward to parent
218 return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot);
220 assert(!(entry->attr & Function));
222 slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>);
223 return true;
227 * This one is for "put".
228 * It looks up a hash entry for the property to be set. If an entry
229 * is found it sets the value and returns true, else it returns false.
231 template <class ThisImp>
232 inline bool lookupPut(ExecState* exec, const Identifier &propertyName,
233 JSValue* value, int attr,
234 const HashTable* table, ThisImp* thisObj)
236 const HashEntry* entry = Lookup::findEntry(table, propertyName);
237 if (!entry)
238 return false;
240 if (entry->attr & Function) // function: put as override property
241 thisObj->JSObject::put(exec, propertyName, value, attr);
242 else if (entry->attr & ReadOnly) // readonly! Can't put!
243 #ifdef KJS_VERBOSE
244 fprintf(stderr,"WARNING: Attempt to change value of readonly property '%s'\n",propertyName.ascii());
245 #else
246 ; // do nothing
247 #endif
248 else
249 thisObj->putValueProperty(exec, entry->value, value, attr);
251 return true;
255 * This one is for "put".
256 * It calls lookupPut<ThisImp>() to set the value. If that call
257 * returns false (meaning no entry in the hash table was found),
258 * then it calls put() on the ParentImp class.
260 template <class ThisImp, class ParentImp>
261 inline void lookupPut(ExecState* exec, const Identifier &propertyName,
262 JSValue* value, int attr,
263 const HashTable* table, ThisImp* thisObj)
265 if (!lookupPut<ThisImp>(exec, propertyName, value, attr, table, thisObj))
266 thisObj->ParentImp::put(exec, propertyName, value, attr); // not found: forward to parent
268 } // namespace
270 #if COMPILER(GCC)
271 // Work around a bug in GCC 4.1. The original code was
272 // #if !COMPILER(GCC)
273 // #define KJS_GCC_ROOT_NS_HACK ::
274 // #else
275 // #define KJS_GCC_ROOT_NS_HACK
276 // #endif
277 // We separate use and declaration here; the define KJS_OBJECTCAHE_IN_KJS
278 // distinguishes if the cache is in KJS (value 1) or not (value 0).
279 #define KJS_OBJECTCACHE_IN_KJS (0)
280 #define KJS_CACHEGLOBALOBJECT_NS
281 #define KJS_CACHEGLOBALOBJECT_NS_USE ::
282 #else
283 #if COMPILER(SUNPRO)
284 // SunPro puts the whole thing in namespace KJS::, no linking problems.
285 #define KJS_OBJECTCACHE_IN_KJS (1)
286 #define KJS_CACHEGLOBALOBJECT_NS KJS::
287 #define KJS_CACHEGLOBALOBJECT_NS_USE KJS::
288 #else
289 // All other non-Studio, non-GCC compilers are forced to put the
290 // object cache outside the KJS namespace, and don't use the GCC
291 // hack to do so.
292 #define KJS_OBJECTCACHE_IN_KJS (0)
293 #define KJS_CACHEGLOBALOBJECT_NS ::
294 #define KJS_CACHEGLOBALOBJECT_NS_USE ::
295 #endif
296 #endif
298 #if KJS_OBJECTCACHE_IN_KJS
299 namespace KJS {
300 #endif
302 * The template method below can't be in the KJS namespace because it's used in
303 * KJS_DEFINE_PROPERTY which can be used outside of the KJS namespace. It can
304 * be moved back when a gcc with a fix for
305 * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=8355
306 * is mainstream enough.
308 * This note applies only to GCC and other non-Studio12 compilers. Studio12
309 * does support having this template in namespace KJS. The macro
310 * KJS_OBJECTCACHE_IN_KJS expands to 1 when it is safe to put the template
311 * in the KJS namespace.
315 * This template method retrieves or create an object that is unique
316 * (for a given interpreter) The first time this is called (for a given
317 * property name), the Object will be constructed, and set as a property
318 * of the interpreter's global object. Later calls will simply retrieve
319 * that cached object. Note that the object constructor must take 1 argument, exec.
321 template <class ClassCtor>
322 inline KJS::JSObject *cacheGlobalObject(KJS::ExecState *exec, const KJS::Identifier &propertyName)
324 KJS::JSObject *globalObject = static_cast<KJS::JSObject *>(exec->lexicalInterpreter()->globalObject());
325 KJS::JSValue *obj = globalObject->getDirect(propertyName);
326 if (obj) {
327 assert(obj->isObject());
328 return static_cast<KJS::JSObject *>(obj);
330 KJS::JSObject *newObject = new ClassCtor(exec);
331 globalObject->put(exec, propertyName, newObject, KJS::Internal | KJS::DontEnum);
332 return newObject;
334 #if KJS_OBJECTCACHE_IN_KJS
336 #endif
339 * Helpers to define prototype objects (each of which simply implements
340 * the functions for a type of objects).
341 * Sorry for this not being very readable, but it actually saves much copy-n-paste.
342 * ParentProto is not our base class, it's the object we use as fallback.
343 * The reason for this is that there should only be ONE DOMNode.hasAttributes (e.g.),
344 * not one in each derived class. So we link the (unique) prototypes between them.
346 * Using those macros is very simple: define the hashtable (e.g. "DOMNodeProtoTable"), then
347 * KJS_DEFINE_PROTOTYPE(DOMNodeProto)
348 * KJS_IMPLEMENT_PROTOFUNC(DOMNodeProtoFunc)
349 * KJS_IMPLEMENT_PROTOTYPE("DOMNode", DOMNodeProto,DOMNodeProtoFunc)
350 * and use DOMNodeProto::self(exec) as prototype in the DOMNode constructor.
351 * If the prototype has a "parent prototype", e.g. DOMElementProto falls back on DOMNodeProto,
352 * then the first line will use KJS_DEFINE_PROTOTYPE_WITH_PROTOTYPE, with DOMNodeProto as the second argument.
355 // These macros assume that a prototype's only properties are functions
356 #define KJS_DEFINE_PROTOTYPE(ClassProto) \
357 class ClassProto : public KJS::JSObject { \
358 friend KJS::JSObject* KJS_CACHEGLOBALOBJECT_NS cacheGlobalObject<ClassProto>(KJS::ExecState *exec, const KJS::Identifier &propertyName); \
359 public: \
360 static KJS::JSObject *self(KJS::ExecState *exec); \
361 virtual const KJS::ClassInfo *classInfo() const { return &info; } \
362 static const KJS::ClassInfo info; \
363 bool getOwnPropertySlot(KJS::ExecState *, const KJS::Identifier&, KJS::PropertySlot&); \
364 protected: \
365 ClassProto(KJS::ExecState *exec) \
366 : KJS::JSObject(exec->lexicalInterpreter()->builtinObjectPrototype()) { } \
368 static KJS::Identifier* s_name; \
369 static KJS::Identifier* name(); \
372 #define KJS_DEFINE_PROTOTYPE_WITH_PROTOTYPE(ClassProto, ClassProtoProto) \
373 class ClassProto : public KJS::JSObject { \
374 friend KJS::JSObject* KJS_CACHEGLOBALOBJECT_NS cacheGlobalObject<ClassProto>(KJS::ExecState* exec, const KJS::Identifier& propertyName); \
375 public: \
376 static KJS::JSObject* self(KJS::ExecState* exec); \
377 virtual const KJS::ClassInfo* classInfo() const { return &info; } \
378 static const KJS::ClassInfo info; \
379 bool getOwnPropertySlot(KJS::ExecState*, const KJS::Identifier&, KJS::PropertySlot&); \
380 protected: \
381 ClassProto(KJS::ExecState* exec) \
382 : KJS::JSObject(ClassProtoProto::self(exec)) { } \
384 static KJS::Identifier* s_name; \
385 static KJS::Identifier* name(); \
389 #define KJS_IMPLEMENT_PROTOTYPE(ClassName, ClassProto,ClassFunc) \
390 const KJS::ClassInfo ClassProto::info = { ClassName, 0, &ClassProto##Table, 0 }; \
391 KJS::Identifier* ClassProto::s_name = 0; \
392 KJS::JSObject *ClassProto::self(KJS::ExecState *exec) \
394 return KJS_CACHEGLOBALOBJECT_NS cacheGlobalObject<ClassProto>(exec, *name()); \
396 bool ClassProto::getOwnPropertySlot(KJS::ExecState *exec, const KJS::Identifier& propertyName, KJS::PropertySlot& slot) \
398 return KJS::getStaticFunctionSlot<ClassFunc, KJS::JSObject>(exec, &ClassProto##Table, this, propertyName, slot); \
400 KJS::Identifier* ClassProto::name() \
402 if (!s_name) s_name = new KJS::Identifier("[[" ClassName ".prototype]]"); \
403 return s_name; \
407 #define KJS_IMPLEMENT_PROTOFUNC(ClassFunc) \
408 class ClassFunc : public KJS::InternalFunctionImp { \
409 public: \
410 ClassFunc(KJS::ExecState* exec, int i, int len, const KJS::Identifier& name) \
411 : InternalFunctionImp(static_cast<KJS::FunctionPrototype*>(exec->lexicalInterpreter()->builtinFunctionPrototype()), name) \
412 , id(i) \
414 put(exec, exec->propertyNames().length, KJS::jsNumber(len), KJS::DontDelete|KJS::ReadOnly|KJS::DontEnum); \
416 /* Macro user needs to implement the callAsFunction function. */ \
417 virtual KJS::JSValue *callAsFunction(KJS::ExecState *exec, KJS::JSObject *thisObj, const KJS::List &args); \
418 private: \
419 int id; \
423 * List of things to do when porting an objectimp to the 'static hashtable' mechanism:
424 * - write the hashtable source, between @begin and @end
425 * - add a rule to build the .lut.h
426 * - include the .lut.h
427 * - mention the table in the classinfo (add a classinfo if necessary)
428 * - write/update the class enum (for the tokens)
429 * - turn get() into getValueProperty(), put() into putValueProperty(), using a switch and removing funcs
430 * - write get() and/or put() using a template method
431 * - cleanup old stuff (e.g. hasProperty)
432 * - compile, test, commit ;)
436 #endif