KJS

lookup.h
1 /*
2  * This file is part of the KDE libraries
3  * Copyright (C) 1999-2000 Harri Porten ([email protected])
4  * Copyright (C) 2003 Apple Computer, Inc.
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 _KJSLOOKUP_H_
23 #define _KJSLOOKUP_H_
24 
25 #include "JSVariableObject.h"
26 #include "interpreter.h"
27 #include "identifier.h"
28 #include "object.h"
29 #include <stdio.h>
30 
31 namespace KJS
32 {
33 class FunctionPrototype;
34 
35 /**
36  * An entry in a hash table.
37  */
38 struct KJS_EXPORT 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 (usually an enum value)
46  */
47  int value;
48  /**
49  * attr is a set for flags (e.g. the property flags, see object.h)
50  */
51  short int attr;
52  /**
53  * params is another number. For property hashtables, it is used to
54  * denote the number of argument of the function
55  */
56  short int params;
57  /**
58  * next is the pointer to the next entry for the same hash value
59  */
60  const HashEntry *next;
61 };
62 
63 /**
64  * A hash table
65  * Usually the hashtable is generated by the create_hash_table script, from a .table file.
66  *
67  * The implementation uses an array of entries, "size" is the total size of that array.
68  * The entries between 0 and hashSize-1 are the entry points
69  * for each hash value, and the entries between hashSize and size-1
70  * are the overflow entries for the hash values that need one.
71  * The "next" pointer of the entry links entry points to overflow entries,
72  * and links overflow entries between them.
73  */
74 struct HashTable {
75  /**
76  * type is a version number. Currently always 2
77  */
78  int type;
79  /**
80  * size is the total number of entries in the hashtable, including the null entries,
81  * i.e. the size of the "entries" array.
82  * Used to iterate over all entries in the table
83  */
84  int size;
85  /**
86  * pointer to the array of entries
87  * Mind that some entries in the array are null (0,0,0,0).
88  */
90  /**
91  * the maximum value for the hash. Always smaller than size.
92  */
93  int hashSize;
94 };
95 
96 /**
97  * @short Fast keyword lookup.
98  */
99 class KJS_EXPORT Lookup
100 {
101 public:
102  /**
103  * Find an entry in the table, and return its value (i.e. the value field of HashEntry)
104  */
105  static int find(const struct HashTable *table, const Identifier &s);
106  static int find(const struct HashTable *table,
107  const UChar *c, unsigned int len);
108 
109  /**
110  * Find an entry in the table, and return the entry
111  * This variant gives access to the other attributes of the entry,
112  * especially the attr field.
113  */
114  static const HashEntry *findEntry(const struct HashTable *table,
115  const Identifier &s);
116 
117 };
118 
119 class ExecState;
120 /**
121 * @internal
122 * Helper for getStaticFunctionSlot and getStaticPropertySlot
123 */
124 template <class FuncImp>
125 inline JSValue *staticFunctionGetter(ExecState *exec, JSObject * /*originalObject*/, const Identifier &propertyName, const PropertySlot &slot)
126 {
127  // Look for cached value in dynamic map of properties (in JSObject)
128  JSObject *thisObj = slot.slotBase();
129  JSValue *cachedVal = thisObj->getDirect(propertyName);
130  if (cachedVal) {
131  return cachedVal;
132  }
133 
134  const HashEntry *entry = slot.staticEntry();
135  JSValue *val = new FuncImp(exec, entry->value, entry->params, propertyName);
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);
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 FuncImp, 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 
182  if (entry->attr & Function) {
183  slot.setStaticEntry(thisObj, entry, staticFunctionGetter<FuncImp>);
184  } else {
185  slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>);
186  }
187 
188  return true;
189 }
190 
191 /**
192  * Simplified version of getStaticPropertySlot in case there are only functions.
193  * Using this instead of getStaticPropertySlot allows 'this' to avoid implementing
194  * a dummy getValueProperty.
195  */
196 template <class FuncImp, class ParentImp>
197 inline bool getStaticFunctionSlot(ExecState *exec, const HashTable *table,
198  JSObject *thisObj, const Identifier &propertyName, PropertySlot &slot)
199 {
200  const HashEntry *entry = Lookup::findEntry(table, propertyName);
201 
202  if (!entry) { // not found, forward to parent
203  return static_cast<ParentImp *>(thisObj)->ParentImp::getOwnPropertySlot(exec, propertyName, slot);
204  }
205 
206  assert(entry->attr & Function);
207 
208  slot.setStaticEntry(thisObj, entry, staticFunctionGetter<FuncImp>);
209  return true;
210 }
211 
212 /**
213  * Simplified version of getStaticPropertySlot in case there are no functions, only "values".
214  * Using this instead of getStaticPropertySlot removes the need for a FuncImp class.
215  */
216 template <class ThisImp, class ParentImp>
217 inline bool getStaticValueSlot(ExecState *exec, const HashTable *table,
218  ThisImp *thisObj, const Identifier &propertyName, PropertySlot &slot)
219 {
220  const HashEntry *entry = Lookup::findEntry(table, propertyName);
221 
222  if (!entry) { // not found, forward to parent
223  return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot);
224  }
225 
226  assert(!(entry->attr & Function));
227 
228  slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>);
229  return true;
230 }
231 
232 /**
233  * This one is for "put".
234  * It looks up a hash entry for the property to be set. If an entry
235  * is found it sets the value and returns true, else it returns false.
236  */
237 template <class ThisImp>
238 inline bool lookupPut(ExecState *exec, const Identifier &propertyName,
239  JSValue *value, int attr,
240  const HashTable *table, ThisImp *thisObj)
241 {
242  const HashEntry *entry = Lookup::findEntry(table, propertyName);
243  if (!entry) {
244  return false;
245  }
246 
247  if (entry->attr & Function) { // function: put as override property
248  thisObj->JSObject::put(exec, propertyName, value, attr);
249  } else if (entry->attr & ReadOnly) // readonly! Can't put!
250 #ifdef KJS_VERBOSE
251  fprintf(stderr, "WARNING: Attempt to change value of readonly property '%s'\n", propertyName.ascii());
252 #else
253  ; // do nothing
254 #endif
255  else {
256  thisObj->putValueProperty(exec, entry->value, value, attr);
257  }
258 
259  return true;
260 }
261 
262 /**
263  * This one is for "put".
264  * It calls lookupPut<ThisImp>() to set the value. If that call
265  * returns false (meaning no entry in the hash table was found),
266  * then it calls put() on the ParentImp class.
267  */
268 template <class ThisImp, class ParentImp>
269 inline void lookupPut(ExecState *exec, const Identifier &propertyName,
270  JSValue *value, int attr,
271  const HashTable *table, ThisImp *thisObj)
272 {
273  if (!lookupPut<ThisImp>(exec, propertyName, value, attr, table, thisObj)) {
274  thisObj->ParentImp::put(exec, propertyName, value, attr); // not found: forward to parent
275  }
276 }
277 } // namespace
278 
279 #if defined(WTF_COMPILER_GCC)
280 // Work around a bug in GCC 4.1. The original code was
281 // #if !defined(WTF_COMPILER_GCC)
282 // #define KJS_GCC_ROOT_NS_HACK ::
283 // #else
284 // #define KJS_GCC_ROOT_NS_HACK
285 // #endif
286 // We separate use and declaration here; the define KJS_OBJECTCAHE_IN_KJS
287 // distinguishes if the cache is in KJS (value 1) or not (value 0).
288 #define KJS_OBJECTCACHE_IN_KJS (0)
289 #define KJS_CACHEGLOBALOBJECT_NS
290 #define KJS_CACHEGLOBALOBJECT_NS_USE ::
291 #else
292 #if defined(WTF_COMPILER_SUNPRO)
293 // SunPro puts the whole thing in namespace KJS::, no linking problems.
294 #define KJS_OBJECTCACHE_IN_KJS (1)
295 #define KJS_CACHEGLOBALOBJECT_NS KJS::
296 #define KJS_CACHEGLOBALOBJECT_NS_USE KJS::
297 #else
298 // All other non-Studio, non-GCC compilers are forced to put the
299 // object cache outside the KJS namespace, and don't use the GCC
300 // hack to do so.
301 #define KJS_OBJECTCACHE_IN_KJS (0)
302 #define KJS_CACHEGLOBALOBJECT_NS ::
303 #define KJS_CACHEGLOBALOBJECT_NS_USE ::
304 #endif
305 #endif
306 
307 #if KJS_OBJECTCACHE_IN_KJS
308 namespace KJS
309 {
310 #endif
311 /*
312  * The template method below can't be in the KJS namespace because it's used in
313  * KJS_DEFINE_PROPERTY which can be used outside of the KJS namespace. It can
314  * be moved back when a gcc with a fix for
315  * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=8355
316  * is mainstream enough.
317  *
318  * This note applies only to GCC and other non-Studio12 compilers. Studio12
319  * does support having this template in namespace KJS. The macro
320  * KJS_OBJECTCACHE_IN_KJS expands to 1 when it is safe to put the template
321  * in the KJS namespace.
322  */
323 
324 /**
325  * This template method retrieves or create an object that is unique
326  * (for a given interpreter) The first time this is called (for a given
327  * property name), the Object will be constructed, and set as a property
328  * of the interpreter's global object. Later calls will simply retrieve
329  * that cached object. Note that the object constructor must take 1 argument, exec.
330  */
331 template <class ClassCtor>
332 inline KJS::JSObject *cacheGlobalObject(KJS::ExecState *exec, const KJS::Identifier &propertyName)
333 {
334  KJS::JSObject *globalObject = static_cast<KJS::JSObject *>(exec->lexicalInterpreter()->globalObject());
335  KJS::JSValue *obj = globalObject->getDirect(propertyName);
336  if (obj) {
337  assert(KJS::JSValue::isObject(obj));
338  return static_cast<KJS::JSObject *>(obj);
339  }
340  KJS::JSObject *newObject = new ClassCtor(exec);
341  globalObject->put(exec, propertyName, newObject, KJS::Internal | KJS::DontEnum);
342  return newObject;
343 }
344 #if KJS_OBJECTCACHE_IN_KJS
345 }
346 #endif
347 
348 /**
349  * Helpers to define prototype objects (each of which simply implements
350  * the functions for a type of objects).
351  * Sorry for this not being very readable, but it actually saves much copy-n-paste.
352  * ParentProto is not our base class, it's the object we use as fallback.
353  * The reason for this is that there should only be ONE DOMNode.hasAttributes (e.g.),
354  * not one in each derived class. So we link the (unique) prototypes between them.
355  *
356  * Using those macros is very simple: define the hashtable (e.g. "DOMNodeProtoTable"), then
357  * KJS_DEFINE_PROTOTYPE(DOMNodeProto)
358  * KJS_IMPLEMENT_PROTOFUNC(DOMNodeProtoFunc)
359  * KJS_IMPLEMENT_PROTOTYPE("DOMNode", DOMNodeProto,DOMNodeProtoFunc)
360  * and use DOMNodeProto::self(exec) as prototype in the DOMNode constructor.
361  * If the prototype has a "parent prototype", e.g. DOMElementProto falls back on DOMNodeProto,
362  * then the first line will use KJS_DEFINE_PROTOTYPE_WITH_PROTOTYPE, with DOMNodeProto as the second argument.
363  */
364 
365 // These macros assume that a prototype's only properties are functions
366 #define KJS_DEFINE_PROTOTYPE(ClassProto) \
367  class ClassProto : public KJS::JSObject { \
368  friend KJS::JSObject* KJS_CACHEGLOBALOBJECT_NS cacheGlobalObject<ClassProto>(KJS::ExecState *exec, const KJS::Identifier &propertyName); \
369  public: \
370  static KJS::JSObject *self(KJS::ExecState *exec); \
371  virtual const KJS::ClassInfo *classInfo() const { return &info; } \
372  static const KJS::ClassInfo info; \
373  bool getOwnPropertySlot(KJS::ExecState *, const KJS::Identifier&, KJS::PropertySlot&); \
374  using JSObject::getOwnPropertySlot; \
375  protected: \
376  ClassProto(KJS::ExecState *exec);\
377  static KJS::Identifier* s_name; \
378  static KJS::Identifier* name(); \
379  };
380 
381 #define KJS_IMPLEMENT_PROTOTYPE_IMP(ClassName,ClassProto,ClassFunc,ClassProtoProto) \
382  const KJS::ClassInfo ClassProto::info = { ClassName, nullptr, &ClassProto##Table, nullptr }; \
383  KJS::Identifier* ClassProto::s_name = nullptr; \
384  KJS::JSObject *ClassProto::self(KJS::ExecState *exec) \
385  { \
386  return KJS_CACHEGLOBALOBJECT_NS cacheGlobalObject<ClassProto>(exec, *name()); \
387  } \
388  bool ClassProto::getOwnPropertySlot(KJS::ExecState *exec, const KJS::Identifier& propertyName, KJS::PropertySlot& slot) \
389  { \
390  return KJS::getStaticFunctionSlot<ClassFunc, KJS::JSObject>(exec, &ClassProto##Table, this, propertyName, slot); \
391  } \
392  KJS::Identifier* ClassProto::name() \
393  { \
394  if (!s_name) s_name = new KJS::Identifier("[[" ClassName ".prototype]]"); \
395  return s_name; \
396  }\
397  ClassProto::ClassProto(KJS::ExecState *exec): KJS::JSObject(ClassProtoProto::self(exec))
398 
399 #define KJS_IMPLEMENT_PROTOTYPE(ClassName,ClassProto,ClassFunc,ClassProtoProto) \
400  KJS_IMPLEMENT_PROTOTYPE_IMP(ClassName,ClassProto,ClassFunc,ClassProtoProto) {}
401 
402 #define KJS_IMPLEMENT_PROTOFUNC(ClassFunc) \
403  class ClassFunc : public KJS::InternalFunctionImp { \
404  public: \
405  ClassFunc(KJS::ExecState* exec, int i, int len, const KJS::Identifier& name) \
406  : InternalFunctionImp(static_cast<KJS::FunctionPrototype*>(exec->lexicalInterpreter()->builtinFunctionPrototype()), name) \
407  , id(i) \
408  { \
409  put(exec, exec->propertyNames().length, KJS::jsNumber(len), KJS::DontDelete|KJS::ReadOnly|KJS::DontEnum); \
410  } \
411  /* Macro user needs to implement the callAsFunction function. */ \
412  virtual KJS::JSValue *callAsFunction(KJS::ExecState *exec, KJS::JSObject *thisObj, const KJS::List &args); \
413  private: \
414  int id; \
415  };
416 
417 /*
418  * List of things to do when porting an objectimp to the 'static hashtable' mechanism:
419  * - write the hashtable source, between @begin and @end
420  * - add a rule to build the .lut.h
421  * - include the .lut.h
422  * - mention the table in the classinfo (add a classinfo if necessary)
423  * - write/update the class enum (for the tokens)
424  * - turn get() into getValueProperty(), put() into putValueProperty(), using a switch and removing funcs
425  * - write get() and/or put() using a template method
426  * - cleanup old stuff (e.g. hasProperty)
427  * - compile, test, commit ;)
428  */
429 
430 #endif
JSGlobalObject * globalObject() const
Returns the object that is used as the global object during all script execution performed by this in...
JSValue is the base type for all primitives (Undefined, Null, Boolean, String, Number) and objects in...
Definition: value.h:58
int size
size is the total number of entries in the hashtable, including the null entries, i....
Definition: lookup.h:84
short int attr
attr is a set for flags (e.g.
Definition: lookup.h:51
int type
type is a version number.
Definition: lookup.h:78
int hashSize
the maximum value for the hash.
Definition: lookup.h:93
const HashEntry * entries
pointer to the array of entries Mind that some entries in the array are null (0,0,...
Definition: lookup.h:89
Represents the current state of script execution.
Definition: ExecState.h:53
Represents an Identifier for a Javascript object.
Definition: identifier.h:36
Unicode character.
Definition: ustring.h:70
int value
value is the result value (usually an enum value)
Definition: lookup.h:47
static const HashEntry * findEntry(const struct HashTable *table, const Identifier &s)
Find an entry in the table, and return the entry This variant gives access to the other attributes of...
Definition: lookup.cpp:72
Interpreter * lexicalInterpreter() const
Returns the interpreter associated with the current scope's global object.
Definition: ExecState.cpp:35
Fast keyword lookup.
Definition: lookup.h:99
const char * s
s is the key (e.g.
Definition: lookup.h:42
short int params
params is another number.
Definition: lookup.h:56
A hash table Usually the hashtable is generated by the create_hash_table script, from a ....
Definition: lookup.h:74
An entry in a hash table.
Definition: lookup.h:38
const HashEntry * next
next is the pointer to the next entry for the same hash value
Definition: lookup.h:60
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Tue Aug 16 2022 03:54:56 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.