lookup.h
00001 // -*- c-basic-offset: 2 -*- 00002 /* 00003 * This file is part of the KDE libraries 00004 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) 00005 * Copyright (C) 2003 Apple Computer, Inc. 00006 * 00007 * This library is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2 of the License, or (at your option) any later version. 00011 * 00012 * This library is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with this library; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 * 00021 */ 00022 00023 #ifndef _KJSLOOKUP_H_ 00024 #define _KJSLOOKUP_H_ 00025 00026 #include "identifier.h" 00027 #include "value.h" 00028 #include "object.h" 00029 #include "interpreter.h" 00030 #include <stdio.h> 00031 00032 namespace KJS { 00033 00037 struct HashEntry { 00041 unsigned short soffset; 00045 short int value; 00049 unsigned char attr; 00054 unsigned char params; 00058 short next; 00059 }; 00060 00072 struct HashTable { 00076 int type; 00082 int size; 00087 const HashEntry *const entries; 00091 int hashSize; 00092 00096 const char* const sbase; 00097 }; 00098 00102 class KJS_EXPORT Lookup { 00103 public: 00107 static int find(const struct HashTable *table, const Identifier &s); 00108 static int find(const struct HashTable *table, 00109 const UChar *c, unsigned int len); 00110 00116 static const HashEntry* findEntry(const struct HashTable *table, 00117 const Identifier &s); 00118 static const HashEntry* findEntry(const struct HashTable *table, 00119 const UChar *c, unsigned int len); 00120 00124 static unsigned int hash(const Identifier &key); 00125 static unsigned int hash(const UChar *c, unsigned int len); 00126 static unsigned int hash(const char *s); 00127 }; 00128 00129 class ExecState; 00130 class UString; 00135 template <class FuncImp> 00136 inline Value lookupOrCreateFunction(ExecState *exec, const Identifier &propertyName, 00137 const ObjectImp *thisObj, int token, int params, int attr) 00138 { 00139 // Look for cached value in dynamic map of properties (in ObjectImp) 00140 ValueImp * cachedVal = thisObj->ObjectImp::getDirect(propertyName); 00141 /*if (cachedVal) 00142 fprintf(stderr, "lookupOrCreateFunction: Function -> looked up in ObjectImp, found type=%d\n", cachedVal->type());*/ 00143 if (cachedVal) 00144 return Value(cachedVal); 00145 00146 ObjectImp* func = new FuncImp( exec, token, params ); 00147 Value val( func ); 00148 func->setFunctionName( propertyName ); 00149 ObjectImp *thatObj = const_cast<ObjectImp *>(thisObj); 00150 thatObj->ObjectImp::put(exec, propertyName, val, attr); 00151 return val; 00152 } 00153 00174 template <class FuncImp, class ThisImp, class ParentImp> 00175 inline Value lookupGet(ExecState *exec, const Identifier &propertyName, 00176 const HashTable* table, const ThisImp* thisObj) 00177 { 00178 const HashEntry* entry = Lookup::findEntry(table, propertyName); 00179 00180 if (!entry) // not found, forward to parent 00181 return thisObj->ParentImp::get(exec, propertyName); 00182 00183 //fprintf(stderr, "lookupGet: found value=%d attr=%d\n", entry->value, entry->attr); 00184 if (entry->attr & Function) 00185 return lookupOrCreateFunction<FuncImp>(exec, propertyName, thisObj, entry->value, entry->params, entry->attr); 00186 return thisObj->getValueProperty(exec, entry->value); 00187 } 00188 00193 template <class FuncImp, class ParentImp> 00194 inline Value lookupGetFunction(ExecState *exec, const Identifier &propertyName, 00195 const HashTable* table, const ObjectImp* thisObj) 00196 { 00197 const HashEntry* entry = Lookup::findEntry(table, propertyName); 00198 00199 if (!entry) // not found, forward to parent 00200 return static_cast<const ParentImp *>(thisObj)->ParentImp::get(exec, propertyName); 00201 00202 if (entry->attr & Function) 00203 return lookupOrCreateFunction<FuncImp>(exec, propertyName, thisObj, entry->value, entry->params, entry->attr); 00204 00205 fprintf(stderr, "Function bit not set! Shouldn't happen in lookupGetFunction!\n" ); 00206 return Undefined(); 00207 } 00208 00213 template <class ThisImp, class ParentImp> 00214 inline Value lookupGetValue(ExecState *exec, const Identifier &propertyName, 00215 const HashTable* table, const ThisImp* thisObj) 00216 { 00217 const HashEntry* entry = Lookup::findEntry(table, propertyName); 00218 00219 if (!entry) // not found, forward to parent 00220 return thisObj->ParentImp::get(exec, propertyName); 00221 00222 if (entry->attr & Function) 00223 fprintf(stderr, "Function bit set! Shouldn't happen in lookupGetValue! propertyName was %s\n", propertyName.ascii() ); 00224 return thisObj->getValueProperty(exec, entry->value); 00225 } 00226 00231 template <class ThisImp, class ParentImp> 00232 inline void lookupPut(ExecState *exec, const Identifier &propertyName, 00233 const Value& value, int attr, 00234 const HashTable* table, ThisImp* thisObj) 00235 { 00236 const HashEntry* entry = Lookup::findEntry(table, propertyName); 00237 00238 if (!entry) // not found: forward to parent 00239 thisObj->ParentImp::put(exec, propertyName, value, attr); 00240 else if (entry->attr & Function) // function: put as override property 00241 thisObj->ObjectImp::put(exec, propertyName, value, attr); 00242 else if (entry->attr & ReadOnly) // readonly! Can't put! 00243 #ifdef KJS_VERBOSE 00244 fprintf(stderr,"WARNING: Attempt to change value of readonly property '%s'\n",propertyName.ascii()); 00245 #else 00246 ; // do nothing 00247 #endif 00248 else 00249 thisObj->putValueProperty(exec, entry->value, value, attr); 00250 } 00251 00252 00260 template <class ClassCtor> 00261 inline KJS::Object cacheGlobalObject(ExecState *exec, const Identifier &propertyName) 00262 { 00263 ValueImp *obj = static_cast<KJS::ObjectImp*>(exec->interpreter()->globalObject().imp())->getDirect(propertyName); 00264 if (obj) 00265 return KJS::Object::dynamicCast(Value(obj)); 00266 else 00267 { 00268 KJS::Object newObject(new ClassCtor(exec)); 00269 exec->interpreter()->globalObject().put(exec, propertyName, newObject, Internal); 00270 return newObject; 00271 } 00272 } 00273 00274 00293 #define PUBLIC_DEFINE_PROTOTYPE(ClassName,ClassProto) \ 00294 namespace KJS { \ 00295 class ClassProto : public KJS::ObjectImp { \ 00296 friend KJS::Object cacheGlobalObject<ClassProto>(KJS::ExecState *exec, const KJS::Identifier &propertyName); \ 00297 public: \ 00298 static KJS::Object self(KJS::ExecState *exec) \ 00299 { \ 00300 return cacheGlobalObject<ClassProto>( exec, "[[" ClassName ".prototype]]" ); \ 00301 } \ 00302 protected: \ 00303 ClassProto( KJS::ExecState *exec ) \ 00304 : KJS::ObjectImp( exec->interpreter()->builtinObjectPrototype() ) {} \ 00305 \ 00306 public: \ 00307 virtual const KJS::ClassInfo *classInfo() const { return &info; } \ 00308 static const KJS::ClassInfo info; \ 00309 KJS::Value get(KJS::ExecState *exec, const KJS::Identifier &propertyName) const; \ 00310 bool hasProperty(KJS::ExecState *exec, const KJS::Identifier &propertyName) const; \ 00311 }; \ 00312 } 00313 00314 #define IMPLEMENT_CLASSINFO(ClassName,ClassProto) \ 00315 namespace KJS {\ 00316 const KJS::ClassInfo ClassProto::info = { ClassName, 0, &ClassProto##Table, 0 }; \ 00317 } 00318 00319 #define DEFINE_PROTOTYPE(ClassName,ClassProto) \ 00320 PUBLIC_DEFINE_PROTOTYPE(ClassName,ClassProto) \ 00321 IMPLEMENT_CLASSINFO(ClassName,ClassProto) 00322 00323 #define IMPLEMENT_PROTOTYPE(ClassProto,ClassFunc) \ 00324 KJS::Value KJS::ClassProto::get(KJS::ExecState *exec, const KJS::Identifier &propertyName) const \ 00325 { \ 00326 /*fprintf( stderr, "%sProto::get(%s) [in macro, no parent]\n", info.className, propertyName.ascii());*/ \ 00327 return lookupGetFunction<ClassFunc,KJS::ObjectImp>(exec, propertyName, &ClassProto##Table, this ); \ 00328 } \ 00329 bool KJS::ClassProto::hasProperty(KJS::ExecState *exec, const KJS::Identifier &propertyName) const \ 00330 { /*stupid but we need this to have a common macro for the declaration*/ \ 00331 return KJS::ObjectImp::hasProperty(exec, propertyName); \ 00332 } 00333 00334 #define PUBLIC_IMPLEMENT_PROTOTYPE(ClassProto,ClassName,ClassFunc) \ 00335 IMPLEMENT_PROTOTYPE(ClassProto,ClassFunc)\ 00336 IMPLEMENT_CLASSINFO(ClassName,ClassProto) 00337 00338 #define IMPLEMENT_PROTOTYPE_WITH_PARENT(ClassProto,ClassFunc,ParentProto) \ 00339 KJS::Value KJS::ClassProto::get(KJS::ExecState *exec, const KJS::Identifier &propertyName) const \ 00340 { \ 00341 /*fprintf( stderr, "%sProto::get(%s) [in macro]\n", info.className, propertyName.ascii());*/ \ 00342 KJS::Value val = lookupGetFunction<ClassFunc,KJS::ObjectImp>(exec, propertyName, &ClassProto##Table, this ); \ 00343 if ( val.type() != UndefinedType ) return val; \ 00344 /* Not found -> forward request to "parent" prototype */ \ 00345 return ParentProto::self(exec).get( exec, propertyName ); \ 00346 } \ 00347 bool KJS::ClassProto::hasProperty(KJS::ExecState *exec, const KJS::Identifier &propertyName) const \ 00348 { \ 00349 if (KJS::ObjectImp::hasProperty(exec, propertyName)) \ 00350 return true; \ 00351 return ParentProto::self(exec).hasProperty(exec, propertyName); \ 00352 } 00353 00354 #define PUBLIC_IMPLEMENT_PROTOTYPE_WITH_PARENT(ClassProto,ClassName,ClassFunc,ParentProto) \ 00355 IMPLEMENT_PROTOTYPE_WITH_PARENT(ClassProto,ClassFunc,ParentProto) \ 00356 IMPLEMENT_CLASSINFO(ClassName,ClassProto) 00357 00358 #define IMPLEMENT_PROTOFUNC(ClassFunc) \ 00359 namespace KJS { \ 00360 class ClassFunc : public ObjectImp { \ 00361 public: \ 00362 ClassFunc(KJS::ExecState *exec, int i, int len) \ 00363 : ObjectImp( /*proto? */ ), id(i) { \ 00364 KJS::Value protect(this); \ 00365 put(exec,lengthPropertyName,Number(len),DontDelete|ReadOnly|DontEnum); \ 00366 } \ 00367 virtual bool implementsCall() const { return true; } \ 00368 \ 00369 virtual KJS::Value call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args); \ 00370 private: \ 00371 int id; \ 00372 }; \ 00373 } 00374 00375 // To be used in all call() implementations, before casting the type of thisObj 00376 #define KJS_CHECK_THIS( ClassName, theObj ) \ 00377 if (!theObj.isValid() || !theObj.inherits(&ClassName::info)) { \ 00378 KJS::UString errMsg = "Attempt at calling a function that expects a "; \ 00379 errMsg += ClassName::info.className; \ 00380 errMsg += " on a "; \ 00381 errMsg += thisObj.className(); \ 00382 KJS::Object err = KJS::Error::create(exec, KJS::TypeError, errMsg.ascii()); \ 00383 exec->setException(err); \ 00384 return err; \ 00385 } 00386 00387 /* 00388 * List of things to do when porting an objectimp to the 'static hashtable' mechanism: 00389 * - write the hashtable source, between @begin and @end 00390 * - add a rule to build the .lut.h 00391 * - include the .lut.h 00392 * - mention the table in the classinfo (add a classinfo if necessary) 00393 * - write/update the class enum (for the tokens) 00394 * - turn get() into getValueProperty(), put() into putValueProperty(), using a switch and removing funcs 00395 * - write get() and/or put() using a template method 00396 * - cleanup old stuff (e.g. hasProperty) 00397 * - compile, test, commit ;) 00398 */ 00399 } // namespace 00400 00401 #endif