1 // -*- c-basic-offset: 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
26 #include "interpreter.h"
27 #include "identifier.h"
32 class FunctionPrototype
;
35 * An entry in a hash table.
37 struct KJS_EXPORT HashEntry
{
39 * s is the key (e.g. a property name)
44 * value is the result value (usually an enum value)
48 * attr is a set for flags (e.g. the property flags, see object.h)
52 * params is another number. For property hashtables, it is used to
53 * denote the number of argument of the function
57 * next is the pointer to the next entry for the same hash value
59 const HashEntry
*next
;
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.
75 * type is a version number. Currently always 2
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
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
;
90 * the maximum value for the hash. Always smaller than size.
96 * @short Fast keyword lookup.
98 class KJS_EXPORT Lookup
{
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
);
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
);
132 const HashEntry
*entry
= slot
.staticEntry();
133 JSValue
*val
= new FuncImp(exec
, entry
->value
, entry
->params
, propertyName
);
134 thisObj
->putDirect(propertyName
, val
, entry
->attr
);
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
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.
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
>);
182 slot
.setStaticEntry(thisObj
, entry
, staticValueGetter
<ThisImp
>);
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
>);
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
>);
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
);
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!
244 fprintf(stderr
,"WARNING: Attempt to change value of readonly property '%s'\n",propertyName
.ascii());
249 thisObj
->putValueProperty(exec
, entry
->value
, value
, attr
);
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
271 // Work around a bug in GCC 4.1. The original code was
272 // #if !COMPILER(GCC)
273 // #define KJS_GCC_ROOT_NS_HACK ::
275 // #define KJS_GCC_ROOT_NS_HACK
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 ::
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::
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
292 #define KJS_OBJECTCACHE_IN_KJS (0)
293 #define KJS_CACHEGLOBALOBJECT_NS ::
294 #define KJS_CACHEGLOBALOBJECT_NS_USE ::
298 #if KJS_OBJECTCACHE_IN_KJS
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
);
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
);
334 #if KJS_OBJECTCACHE_IN_KJS
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); \
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&); \
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); \
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&); \
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]]"); \
407 #define KJS_IMPLEMENT_PROTOFUNC(ClassFunc) \
408 class ClassFunc : public KJS::InternalFunctionImp { \
410 ClassFunc(KJS::ExecState* exec, int i, int len, const KJS::Identifier& name) \
411 : InternalFunctionImp(static_cast<KJS::FunctionPrototype*>(exec->lexicalInterpreter()->builtinFunctionPrototype()), name) \
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); \
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 ;)