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"
28 #include "identifier.h"
29 #include "function_object.h"
35 class FunctionPrototype;
38 * An entry in a hash table.
42 * s is the key (e.g. a property name)
47 * value is the result value (usually an enum value)
51 * attr is a set for flags (e.g. the property flags, see object.h)
55 * params is another number. For property hashtables, it is used to
56 * denote the number of argument of the function
60 * next is the pointer to the next entry for the same hash value
62 const HashEntry* next;
67 * Usually the hashtable is generated by the create_hash_table script, from a .table file.
69 * The implementation uses an array of entries, "size" is the total size of that array.
70 * The entries between 0 and hashSize-1 are the entry points
71 * for each hash value, and the entries between hashSize and size-1
72 * are the overflow entries for the hash values that need one.
73 * The "next" pointer of the entry links entry points to overflow entries,
74 * and links overflow entries between them.
78 * type is a version number. Currently always 2
82 * size is the total number of entries in the hashtable, including the null entries,
83 * i.e. the size of the "entries" array.
84 * Used to iterate over all entries in the table
88 * pointer to the array of entries
89 * Mind that some entries in the array are null (0,0,0,0).
91 const HashEntry* entries;
93 * the maximum value for the hash. Always smaller than size.
99 * @short Fast keyword lookup.
104 * Find an entry in the table, and return its value (i.e. the value field of HashEntry)
106 static int find(const struct HashTable*, const Identifier&);
107 static int find(const struct HashTable*, const UChar*, unsigned int len);
111 * Find an entry in the table, and return the entry
112 * This variant gives access to the other attributes of the entry,
113 * especially the attr field.
115 static const HashEntry* findEntry(const struct HashTable*, const Identifier&);
123 * Helper for getStaticFunctionSlot and getStaticPropertySlot
125 template <class FuncImp>
126 inline JSValue* staticFunctionGetter(ExecState* exec, JSObject*, const Identifier& propertyName, const PropertySlot& slot)
128 // Look for cached value in dynamic map of properties (in JSObject)
129 JSObject* thisObj = slot.slotBase();
130 JSValue* cachedVal = thisObj->getDirect(propertyName);
134 const HashEntry* entry = slot.staticEntry();
135 JSValue* val = new FuncImp(exec, entry->value, entry->params, propertyName);
136 thisObj->putDirect(propertyName, val, entry->attr);
142 * Helper for getStaticValueSlot and getStaticPropertySlot
144 template <class ThisImp>
145 inline JSValue* staticValueGetter(ExecState* exec, JSObject*, const Identifier&, const PropertySlot& slot)
147 ThisImp* thisObj = static_cast<ThisImp*>(slot.slotBase());
148 const HashEntry* entry = slot.staticEntry();
149 return thisObj->getValueProperty(exec, entry->value);
153 * Helper method for property lookups
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
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.
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"
172 template <class FuncImp, class ThisImp, class ParentImp>
173 inline bool getStaticPropertySlot(ExecState* exec, const HashTable* table,
174 ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot)
176 const HashEntry* entry = Lookup::findEntry(table, propertyName);
178 if (!entry) // not found, forward to parent
179 return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot);
181 if (entry->attr & Function)
182 slot.setStaticEntry(thisObj, entry, staticFunctionGetter<FuncImp>);
184 slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>);
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.
194 template <class FuncImp, class ParentImp>
195 inline bool getStaticFunctionSlot(ExecState* exec, const HashTable* table,
196 JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot)
198 const HashEntry* entry = Lookup::findEntry(table, propertyName);
200 if (!entry) // not found, forward to parent
201 return static_cast<ParentImp*>(thisObj)->ParentImp::getOwnPropertySlot(exec, propertyName, slot);
203 assert(entry->attr & Function);
205 slot.setStaticEntry(thisObj, entry, staticFunctionGetter<FuncImp>);
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.
213 template <class ThisImp, class ParentImp>
214 inline bool getStaticValueSlot(ExecState* exec, const HashTable* table,
215 ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot)
217 const HashEntry* entry = Lookup::findEntry(table, propertyName);
219 if (!entry) // not found, forward to parent
220 return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot);
222 assert(!(entry->attr & Function));
224 slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>);
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.
233 template <class ThisImp>
234 inline bool lookupPut(ExecState* exec, const Identifier& propertyName,
235 JSValue* value, int attr,
236 const HashTable* table, ThisImp* thisObj)
238 const HashEntry* entry = Lookup::findEntry(table, propertyName);
243 if (entry->attr & Function) // function: put as override property
244 thisObj->JSObject::put(exec, propertyName, value, attr);
245 else if (entry->attr & ReadOnly) // readonly! Can't put!
247 fprintf(stderr,"WARNING: Attempt to change value of readonly property '%s'\n",propertyName.ascii());
252 thisObj->putValueProperty(exec, entry->value, value, attr);
258 * This one is for "put".
259 * It calls lookupPut<ThisImp>() to set the value. If that call
260 * returns false (meaning no entry in the hash table was found),
261 * then it calls put() on the ParentImp class.
263 template <class ThisImp, class ParentImp>
264 inline void lookupPut(ExecState* exec, const Identifier& propertyName,
265 JSValue* value, int attr,
266 const HashTable* table, ThisImp* thisObj)
268 if (!lookupPut<ThisImp>(exec, propertyName, value, attr, table, thisObj))
269 thisObj->ParentImp::put(exec, propertyName, value, attr); // not found: forward to parent
273 * This template method retrieves or create an object that is unique
274 * (for a given interpreter) The first time this is called (for a given
275 * property name), the Object will be constructed, and set as a property
276 * of the interpreter's global object. Later calls will simply retrieve
277 * that cached object. Note that the object constructor must take 1 argument, exec.
279 template <class ClassCtor>
280 inline JSObject* cacheGlobalObject(ExecState* exec, const Identifier& propertyName)
282 JSObject* globalObject = static_cast<JSObject*>(exec->lexicalInterpreter()->globalObject());
283 JSValue* obj = globalObject->getDirect(propertyName);
285 assert(obj->isObject());
286 return static_cast<JSObject* >(obj);
288 JSObject* newObject = new ClassCtor(exec);
289 globalObject->put(exec, propertyName, newObject, Internal | DontEnum);
296 * Helpers to define prototype objects (each of which simply implements
297 * the functions for a type of objects).
298 * Sorry for this not being very readable, but it actually saves much copy-n-paste.
299 * ParentProto is not our base class, it's the object we use as fallback.
300 * The reason for this is that there should only be ONE DOMNode.hasAttributes (e.g.),
301 * not one in each derived class. So we link the (unique) prototypes between them.
303 * Using those macros is very simple: define the hashtable (e.g. "DOMNodeProtoTable"), then
304 * KJS_DEFINE_PROTOTYPE(DOMNodeProto)
305 * KJS_IMPLEMENT_PROTOFUNC(DOMNodeProtoFunc)
306 * KJS_IMPLEMENT_PROTOTYPE("DOMNode", DOMNodeProto,DOMNodeProtoFunc)
307 * and use DOMNodeProto::self(exec) as prototype in the DOMNode constructor.
308 * If the prototype has a "parent prototype", e.g. DOMElementProto falls back on DOMNodeProto,
309 * then the first line will use KJS_DEFINE_PROTOTYPE_WITH_PROTOTYPE, with DOMNodeProto as the second argument.
312 // These macros assume that a prototype's only properties are functions
313 #define KJS_DEFINE_PROTOTYPE(ClassProto) \
314 class ClassProto : public KJS::JSObject { \
316 static KJS::JSObject* self(KJS::ExecState* exec); \
317 virtual const KJS::ClassInfo* classInfo() const { return &info; } \
318 static const KJS::ClassInfo info; \
319 bool getOwnPropertySlot(KJS::ExecState* , const KJS::Identifier&, KJS::PropertySlot&); \
320 ClassProto(KJS::ExecState* exec) \
321 : KJS::JSObject(exec->lexicalInterpreter()->builtinObjectPrototype()) { } \
325 #define KJS_DEFINE_PROTOTYPE_WITH_PROTOTYPE(ClassProto, ClassProtoProto) \
326 class ClassProto : public KJS::JSObject { \
328 static KJS::JSObject* self(KJS::ExecState* exec); \
329 virtual const KJS::ClassInfo* classInfo() const { return &info; } \
330 static const KJS::ClassInfo info; \
331 bool getOwnPropertySlot(KJS::ExecState*, const KJS::Identifier&, KJS::PropertySlot&); \
332 ClassProto(KJS::ExecState* exec) \
333 : KJS::JSObject(ClassProtoProto::self(exec)) { } \
337 #define KJS_IMPLEMENT_PROTOTYPE(ClassName, ClassProto, ClassFunc) \
338 const ClassInfo ClassProto::info = { ClassName, 0, &ClassProto##Table, 0 }; \
339 JSObject* ClassProto::self(ExecState* exec) \
341 return KJS::cacheGlobalObject<ClassProto>(exec, "[[" ClassName ".prototype]]"); \
343 bool ClassProto::getOwnPropertySlot(ExecState* exec, const Identifier& propertyName, PropertySlot& slot) \
345 return getStaticFunctionSlot<ClassFunc, JSObject>(exec, &ClassProto##Table, this, propertyName, slot); \
348 #define KJS_IMPLEMENT_PROTOFUNC(ClassFunc) \
349 class ClassFunc : public InternalFunctionImp { \
351 ClassFunc(ExecState* exec, int i, int len, const Identifier& name) \
352 : InternalFunctionImp(static_cast<FunctionPrototype*>(exec->lexicalInterpreter()->builtinFunctionPrototype()), name) \
355 put(exec, lengthPropertyName, jsNumber(len), DontDelete|ReadOnly|DontEnum); \
357 /* Macro user needs to implement the callAsFunction function. */ \
358 virtual JSValue* callAsFunction(ExecState* exec, JSObject* thisObj, const List& args); \