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