File indexing completed on 2024-05-12 15:43:26
0001 /* 0002 * This file is part of the KDE libraries 0003 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) 0004 * Copyright (C) 2003 Apple Computer, Inc. 0005 * 0006 * This library is free software; you can redistribute it and/or 0007 * modify it under the terms of the GNU Lesser General Public 0008 * License as published by the Free Software Foundation; either 0009 * version 2 of the License, or (at your option) any later version. 0010 * 0011 * This library is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 * Lesser General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU Lesser General Public 0017 * License along with this library; if not, write to the Free Software 0018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 0019 * 0020 */ 0021 0022 #ifndef _KJSLOOKUP_H_ 0023 #define _KJSLOOKUP_H_ 0024 0025 #include "JSVariableObject.h" 0026 #include "interpreter.h" 0027 #include "identifier.h" 0028 #include "object.h" 0029 #include <stdio.h> 0030 0031 namespace KJS 0032 { 0033 class FunctionPrototype; 0034 0035 /** 0036 * An entry in a hash table. 0037 */ 0038 struct KJS_EXPORT HashEntry { 0039 /** 0040 * s is the key (e.g. a property name) 0041 */ 0042 const char *s; 0043 0044 /** 0045 * value is the result value (usually an enum value) 0046 */ 0047 int value; 0048 /** 0049 * attr is a set for flags (e.g. the property flags, see object.h) 0050 */ 0051 short int attr; 0052 /** 0053 * params is another number. For property hashtables, it is used to 0054 * denote the number of argument of the function 0055 */ 0056 short int params; 0057 /** 0058 * next is the pointer to the next entry for the same hash value 0059 */ 0060 const HashEntry *next; 0061 }; 0062 0063 /** 0064 * A hash table 0065 * Usually the hashtable is generated by the create_hash_table script, from a .table file. 0066 * 0067 * The implementation uses an array of entries, "size" is the total size of that array. 0068 * The entries between 0 and hashSize-1 are the entry points 0069 * for each hash value, and the entries between hashSize and size-1 0070 * are the overflow entries for the hash values that need one. 0071 * The "next" pointer of the entry links entry points to overflow entries, 0072 * and links overflow entries between them. 0073 */ 0074 struct HashTable { 0075 /** 0076 * type is a version number. Currently always 2 0077 */ 0078 int type; 0079 /** 0080 * size is the total number of entries in the hashtable, including the null entries, 0081 * i.e. the size of the "entries" array. 0082 * Used to iterate over all entries in the table 0083 */ 0084 int size; 0085 /** 0086 * pointer to the array of entries 0087 * Mind that some entries in the array are null (0,0,0,0). 0088 */ 0089 const HashEntry *entries; 0090 /** 0091 * the maximum value for the hash. Always smaller than size. 0092 */ 0093 int hashSize; 0094 }; 0095 0096 /** 0097 * @short Fast keyword lookup. 0098 */ 0099 class KJS_EXPORT Lookup 0100 { 0101 public: 0102 /** 0103 * Find an entry in the table, and return its value (i.e. the value field of HashEntry) 0104 */ 0105 static int find(const struct HashTable *table, const Identifier &s); 0106 static int find(const struct HashTable *table, 0107 const UChar *c, unsigned int len); 0108 0109 /** 0110 * Find an entry in the table, and return the entry 0111 * This variant gives access to the other attributes of the entry, 0112 * especially the attr field. 0113 */ 0114 static const HashEntry *findEntry(const struct HashTable *table, 0115 const Identifier &s); 0116 0117 }; 0118 0119 class ExecState; 0120 /** 0121 * @internal 0122 * Helper for getStaticFunctionSlot and getStaticPropertySlot 0123 */ 0124 template <class FuncImp> 0125 inline JSValue *staticFunctionGetter(ExecState *exec, JSObject * /*originalObject*/, const Identifier &propertyName, const PropertySlot &slot) 0126 { 0127 // Look for cached value in dynamic map of properties (in JSObject) 0128 JSObject *thisObj = slot.slotBase(); 0129 JSValue *cachedVal = thisObj->getDirect(propertyName); 0130 if (cachedVal) { 0131 return cachedVal; 0132 } 0133 0134 const HashEntry *entry = slot.staticEntry(); 0135 JSValue *val = new FuncImp(exec, entry->value, entry->params, propertyName); 0136 thisObj->putDirect(propertyName, val, entry->attr); 0137 return val; 0138 } 0139 0140 /** 0141 * @internal 0142 * Helper for getStaticValueSlot and getStaticPropertySlot 0143 */ 0144 template <class ThisImp> 0145 inline JSValue *staticValueGetter(ExecState *exec, JSObject *, const Identifier &, const PropertySlot &slot) 0146 { 0147 ThisImp *thisObj = static_cast<ThisImp *>(slot.slotBase()); 0148 const HashEntry *entry = slot.staticEntry(); 0149 return thisObj->getValueProperty(exec, entry->value); 0150 } 0151 0152 /** 0153 * Helper method for property lookups 0154 * 0155 * This method does it all (looking in the hashtable, checking for function 0156 * overrides, creating the function or retrieving from cache, calling 0157 * getValueProperty in case of a non-function property, forwarding to parent if 0158 * unknown property). 0159 * 0160 * Template arguments: 0161 * @param FuncImp the class which implements this object's functions 0162 * @param ThisImp the class of "this". It must implement the getValueProperty(exec,token) method, 0163 * for non-function properties. 0164 * @param ParentImp the class of the parent, to propagate the lookup. 0165 * 0166 * Method arguments: 0167 * @param exec execution state, as usual 0168 * @param propertyName the property we're looking for 0169 * @param table the static hashtable for this class 0170 * @param thisObj "this" 0171 */ 0172 template <class FuncImp, class ThisImp, class ParentImp> 0173 inline bool getStaticPropertySlot(ExecState *exec, const HashTable *table, 0174 ThisImp *thisObj, const Identifier &propertyName, PropertySlot &slot) 0175 { 0176 const HashEntry *entry = Lookup::findEntry(table, propertyName); 0177 0178 if (!entry) { // not found, forward to parent 0179 return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot); 0180 } 0181 0182 if (entry->attr & Function) { 0183 slot.setStaticEntry(thisObj, entry, staticFunctionGetter<FuncImp>); 0184 } else { 0185 slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>); 0186 } 0187 0188 return true; 0189 } 0190 0191 /** 0192 * Simplified version of getStaticPropertySlot in case there are only functions. 0193 * Using this instead of getStaticPropertySlot allows 'this' to avoid implementing 0194 * a dummy getValueProperty. 0195 */ 0196 template <class FuncImp, class ParentImp> 0197 inline bool getStaticFunctionSlot(ExecState *exec, const HashTable *table, 0198 JSObject *thisObj, const Identifier &propertyName, PropertySlot &slot) 0199 { 0200 const HashEntry *entry = Lookup::findEntry(table, propertyName); 0201 0202 if (!entry) { // not found, forward to parent 0203 return static_cast<ParentImp *>(thisObj)->ParentImp::getOwnPropertySlot(exec, propertyName, slot); 0204 } 0205 0206 assert(entry->attr & Function); 0207 0208 slot.setStaticEntry(thisObj, entry, staticFunctionGetter<FuncImp>); 0209 return true; 0210 } 0211 0212 /** 0213 * Simplified version of getStaticPropertySlot in case there are no functions, only "values". 0214 * Using this instead of getStaticPropertySlot removes the need for a FuncImp class. 0215 */ 0216 template <class ThisImp, class ParentImp> 0217 inline bool getStaticValueSlot(ExecState *exec, const HashTable *table, 0218 ThisImp *thisObj, const Identifier &propertyName, PropertySlot &slot) 0219 { 0220 const HashEntry *entry = Lookup::findEntry(table, propertyName); 0221 0222 if (!entry) { // not found, forward to parent 0223 return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot); 0224 } 0225 0226 assert(!(entry->attr & Function)); 0227 0228 slot.setStaticEntry(thisObj, entry, staticValueGetter<ThisImp>); 0229 return true; 0230 } 0231 0232 /** 0233 * This one is for "put". 0234 * It looks up a hash entry for the property to be set. If an entry 0235 * is found it sets the value and returns true, else it returns false. 0236 */ 0237 template <class ThisImp> 0238 inline bool lookupPut(ExecState *exec, const Identifier &propertyName, 0239 JSValue *value, int attr, 0240 const HashTable *table, ThisImp *thisObj) 0241 { 0242 const HashEntry *entry = Lookup::findEntry(table, propertyName); 0243 if (!entry) { 0244 return false; 0245 } 0246 0247 if (entry->attr & Function) { // function: put as override property 0248 thisObj->JSObject::put(exec, propertyName, value, attr); 0249 } else if (entry->attr & ReadOnly) // readonly! Can't put! 0250 #ifdef KJS_VERBOSE 0251 fprintf(stderr, "WARNING: Attempt to change value of readonly property '%s'\n", propertyName.ascii()); 0252 #else 0253 ; // do nothing 0254 #endif 0255 else { 0256 thisObj->putValueProperty(exec, entry->value, value, attr); 0257 } 0258 0259 return true; 0260 } 0261 0262 /** 0263 * This one is for "put". 0264 * It calls lookupPut<ThisImp>() to set the value. If that call 0265 * returns false (meaning no entry in the hash table was found), 0266 * then it calls put() on the ParentImp class. 0267 */ 0268 template <class ThisImp, class ParentImp> 0269 inline void lookupPut(ExecState *exec, const Identifier &propertyName, 0270 JSValue *value, int attr, 0271 const HashTable *table, ThisImp *thisObj) 0272 { 0273 if (!lookupPut<ThisImp>(exec, propertyName, value, attr, table, thisObj)) { 0274 thisObj->ParentImp::put(exec, propertyName, value, attr); // not found: forward to parent 0275 } 0276 } 0277 } // namespace 0278 0279 #if defined(WTF_COMPILER_GCC) 0280 // Work around a bug in GCC 4.1. The original code was 0281 // #if !defined(WTF_COMPILER_GCC) 0282 // #define KJS_GCC_ROOT_NS_HACK :: 0283 // #else 0284 // #define KJS_GCC_ROOT_NS_HACK 0285 // #endif 0286 // We separate use and declaration here; the define KJS_OBJECTCAHE_IN_KJS 0287 // distinguishes if the cache is in KJS (value 1) or not (value 0). 0288 #define KJS_OBJECTCACHE_IN_KJS (0) 0289 #define KJS_CACHEGLOBALOBJECT_NS 0290 #define KJS_CACHEGLOBALOBJECT_NS_USE :: 0291 #else 0292 #if defined(WTF_COMPILER_SUNPRO) 0293 // SunPro puts the whole thing in namespace KJS::, no linking problems. 0294 #define KJS_OBJECTCACHE_IN_KJS (1) 0295 #define KJS_CACHEGLOBALOBJECT_NS KJS:: 0296 #define KJS_CACHEGLOBALOBJECT_NS_USE KJS:: 0297 #else 0298 // All other non-Studio, non-GCC compilers are forced to put the 0299 // object cache outside the KJS namespace, and don't use the GCC 0300 // hack to do so. 0301 #define KJS_OBJECTCACHE_IN_KJS (0) 0302 #define KJS_CACHEGLOBALOBJECT_NS :: 0303 #define KJS_CACHEGLOBALOBJECT_NS_USE :: 0304 #endif 0305 #endif 0306 0307 #if KJS_OBJECTCACHE_IN_KJS 0308 namespace KJS 0309 { 0310 #endif 0311 /* 0312 * The template method below can't be in the KJS namespace because it's used in 0313 * KJS_DEFINE_PROPERTY which can be used outside of the KJS namespace. It can 0314 * be moved back when a gcc with a fix for 0315 * http://gcc.gnu.org/bugzilla/show_bug.cgi?id=8355 0316 * is mainstream enough. 0317 * 0318 * This note applies only to GCC and other non-Studio12 compilers. Studio12 0319 * does support having this template in namespace KJS. The macro 0320 * KJS_OBJECTCACHE_IN_KJS expands to 1 when it is safe to put the template 0321 * in the KJS namespace. 0322 */ 0323 0324 /** 0325 * This template method retrieves or create an object that is unique 0326 * (for a given interpreter) The first time this is called (for a given 0327 * property name), the Object will be constructed, and set as a property 0328 * of the interpreter's global object. Later calls will simply retrieve 0329 * that cached object. Note that the object constructor must take 1 argument, exec. 0330 */ 0331 template <class ClassCtor> 0332 inline KJS::JSObject *cacheGlobalObject(KJS::ExecState *exec, const KJS::Identifier &propertyName) 0333 { 0334 KJS::JSObject *globalObject = static_cast<KJS::JSObject *>(exec->lexicalInterpreter()->globalObject()); 0335 KJS::JSValue *obj = globalObject->getDirect(propertyName); 0336 if (obj) { 0337 assert(KJS::JSValue::isObject(obj)); 0338 return static_cast<KJS::JSObject *>(obj); 0339 } 0340 KJS::JSObject *newObject = new ClassCtor(exec); 0341 globalObject->put(exec, propertyName, newObject, KJS::Internal | KJS::DontEnum); 0342 return newObject; 0343 } 0344 #if KJS_OBJECTCACHE_IN_KJS 0345 } 0346 #endif 0347 0348 /** 0349 * Helpers to define prototype objects (each of which simply implements 0350 * the functions for a type of objects). 0351 * Sorry for this not being very readable, but it actually saves much copy-n-paste. 0352 * ParentProto is not our base class, it's the object we use as fallback. 0353 * The reason for this is that there should only be ONE DOMNode.hasAttributes (e.g.), 0354 * not one in each derived class. So we link the (unique) prototypes between them. 0355 * 0356 * Using those macros is very simple: define the hashtable (e.g. "DOMNodeProtoTable"), then 0357 * KJS_DEFINE_PROTOTYPE(DOMNodeProto) 0358 * KJS_IMPLEMENT_PROTOFUNC(DOMNodeProtoFunc) 0359 * KJS_IMPLEMENT_PROTOTYPE("DOMNode", DOMNodeProto,DOMNodeProtoFunc) 0360 * and use DOMNodeProto::self(exec) as prototype in the DOMNode constructor. 0361 * If the prototype has a "parent prototype", e.g. DOMElementProto falls back on DOMNodeProto, 0362 * then the first line will use KJS_DEFINE_PROTOTYPE_WITH_PROTOTYPE, with DOMNodeProto as the second argument. 0363 */ 0364 0365 // These macros assume that a prototype's only properties are functions 0366 #define KJS_DEFINE_PROTOTYPE(ClassProto) \ 0367 class ClassProto : public KJS::JSObject { \ 0368 friend KJS::JSObject* KJS_CACHEGLOBALOBJECT_NS cacheGlobalObject<ClassProto>(KJS::ExecState *exec, const KJS::Identifier &propertyName); \ 0369 public: \ 0370 static KJS::JSObject *self(KJS::ExecState *exec); \ 0371 virtual const KJS::ClassInfo *classInfo() const { return &info; } \ 0372 static const KJS::ClassInfo info; \ 0373 bool getOwnPropertySlot(KJS::ExecState *, const KJS::Identifier&, KJS::PropertySlot&); \ 0374 using JSObject::getOwnPropertySlot; \ 0375 protected: \ 0376 ClassProto(KJS::ExecState *exec);\ 0377 static KJS::Identifier* s_name; \ 0378 static KJS::Identifier* name(); \ 0379 }; 0380 0381 #define KJS_IMPLEMENT_PROTOTYPE_IMP(ClassName,ClassProto,ClassFunc,ClassProtoProto) \ 0382 const KJS::ClassInfo ClassProto::info = { ClassName, nullptr, &ClassProto##Table, nullptr }; \ 0383 KJS::Identifier* ClassProto::s_name = nullptr; \ 0384 KJS::JSObject *ClassProto::self(KJS::ExecState *exec) \ 0385 { \ 0386 return KJS_CACHEGLOBALOBJECT_NS cacheGlobalObject<ClassProto>(exec, *name()); \ 0387 } \ 0388 bool ClassProto::getOwnPropertySlot(KJS::ExecState *exec, const KJS::Identifier& propertyName, KJS::PropertySlot& slot) \ 0389 { \ 0390 return KJS::getStaticFunctionSlot<ClassFunc, KJS::JSObject>(exec, &ClassProto##Table, this, propertyName, slot); \ 0391 } \ 0392 KJS::Identifier* ClassProto::name() \ 0393 { \ 0394 if (!s_name) s_name = new KJS::Identifier("[[" ClassName ".prototype]]"); \ 0395 return s_name; \ 0396 }\ 0397 ClassProto::ClassProto(KJS::ExecState *exec): KJS::JSObject(ClassProtoProto::self(exec)) 0398 0399 #define KJS_IMPLEMENT_PROTOTYPE(ClassName,ClassProto,ClassFunc,ClassProtoProto) \ 0400 KJS_IMPLEMENT_PROTOTYPE_IMP(ClassName,ClassProto,ClassFunc,ClassProtoProto) {} 0401 0402 #define KJS_IMPLEMENT_PROTOFUNC(ClassFunc) \ 0403 class ClassFunc : public KJS::InternalFunctionImp { \ 0404 public: \ 0405 ClassFunc(KJS::ExecState* exec, int i, int len, const KJS::Identifier& name) \ 0406 : InternalFunctionImp(static_cast<KJS::FunctionPrototype*>(exec->lexicalInterpreter()->builtinFunctionPrototype()), name) \ 0407 , id(i) \ 0408 { \ 0409 put(exec, exec->propertyNames().length, KJS::jsNumber(len), KJS::DontDelete|KJS::ReadOnly|KJS::DontEnum); \ 0410 } \ 0411 /* Macro user needs to implement the callAsFunction function. */ \ 0412 virtual KJS::JSValue *callAsFunction(KJS::ExecState *exec, KJS::JSObject *thisObj, const KJS::List &args); \ 0413 private: \ 0414 int id; \ 0415 }; 0416 0417 /* 0418 * List of things to do when porting an objectimp to the 'static hashtable' mechanism: 0419 * - write the hashtable source, between @begin and @end 0420 * - add a rule to build the .lut.h 0421 * - include the .lut.h 0422 * - mention the table in the classinfo (add a classinfo if necessary) 0423 * - write/update the class enum (for the tokens) 0424 * - turn get() into getValueProperty(), put() into putValueProperty(), using a switch and removing funcs 0425 * - write get() and/or put() using a template method 0426 * - cleanup old stuff (e.g. hasProperty) 0427 * - compile, test, commit ;) 0428 */ 0429 0430 #endif