1 /* 2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) 3 * Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Lesser General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Lesser General Public License for more details. 14 * 15 * You should have received a copy of the GNU Lesser General Public 16 * License along with this library; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 * 19 */ 20 21 #ifndef Lookup_h 22 #define Lookup_h 23 24 #include "CallFrame.h" 25 #include "Identifier.h" 26 #include "JSFunction.h" 27 #include "JSGlobalObject.h" 28 #include "JSObject.h" 29 #include "PropertySlot.h" 30 #include <stdio.h> 31 #include <wtf/Assertions.h> 32 33 // Set ENABLE_PERFECT_HASH_SIZE to 0 to save memory at the 34 // cost of speed. Test your platform as results may vary. 35 #define ENABLE_PERFECT_HASH_SIZE 1 36 37 namespace JSC { 38 39 // Hash table generated by the create_hash_table script. 40 struct HashTableValue { 41 const char* key; // property name 42 unsigned char attributes; // JSObject attributes 43 intptr_t value1; 44 intptr_t value2; 45 }; 46 47 // FIXME: There is no reason this get function can't be simpler. 48 // ie. typedef JSValuePtr (*GetFunction)(ExecState*, JSObject* baseObject) 49 typedef PropertySlot::GetValueFunc GetFunction; 50 typedef void (*PutFunction)(ExecState*, JSObject* baseObject, JSValuePtr value); 51 52 class HashEntry { 53 public: initialize(UString::Rep * key,unsigned char attributes,intptr_t v1,intptr_t v2)54 void initialize(UString::Rep* key, unsigned char attributes, intptr_t v1, intptr_t v2) 55 { 56 m_key = key; 57 m_attributes = attributes; 58 m_u.store.value1 = v1; 59 m_u.store.value2 = v2; 60 #if !ENABLE(PERFECT_HASH_SIZE) 61 m_next = 0; 62 #endif 63 } 64 setKey(UString::Rep * key)65 void setKey(UString::Rep* key) { m_key = key; } key()66 UString::Rep* key() const { return m_key; } 67 attributes()68 unsigned char attributes() const { return m_attributes; } 69 function()70 NativeFunction function() const { ASSERT(m_attributes & Function); return m_u.function.functionValue; } functionLength()71 unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast<unsigned char>(m_u.function.length); } 72 propertyGetter()73 GetFunction propertyGetter() const { ASSERT(!(m_attributes & Function)); return m_u.property.get; } propertyPutter()74 PutFunction propertyPutter() const { ASSERT(!(m_attributes & Function)); return m_u.property.put; } 75 lexerValue()76 intptr_t lexerValue() const { ASSERT(!m_attributes); return m_u.lexer.value; } 77 78 #if !ENABLE(PERFECT_HASH_SIZE) setNext(HashEntry * next)79 void setNext(HashEntry *next) { m_next = next; } next()80 HashEntry* next() const { return m_next; } 81 #endif 82 83 private: 84 UString::Rep* m_key; 85 unsigned char m_attributes; // JSObject attributes 86 87 union { 88 struct { 89 intptr_t value1; 90 intptr_t value2; 91 } store; 92 struct { 93 NativeFunction functionValue; 94 intptr_t length; // number of arguments for function 95 } function; 96 struct { 97 GetFunction get; 98 PutFunction put; 99 } property; 100 struct { 101 intptr_t value; 102 intptr_t unused; 103 } lexer; 104 } m_u; 105 106 #if !ENABLE(PERFECT_HASH_SIZE) 107 HashEntry* m_next; 108 #endif 109 }; 110 111 struct HashTable { 112 #if ENABLE(PERFECT_HASH_SIZE) 113 int hashSizeMask; // Precomputed size for the hash table (minus 1). 114 #else 115 int compactSize; 116 int compactHashSizeMask; 117 #endif 118 const HashTableValue* values; // Fixed values generated by script. 119 mutable const HashEntry* table; // Table allocated at runtime. 120 initializeIfNeededHashTable121 ALWAYS_INLINE void initializeIfNeeded(JSGlobalData* globalData) const 122 { 123 if (!table) 124 createTable(globalData); 125 } 126 initializeIfNeededHashTable127 ALWAYS_INLINE void initializeIfNeeded(ExecState* exec) const 128 { 129 if (!table) 130 createTable(&exec->globalData()); 131 } 132 133 void deleteTable() const; 134 135 // Find an entry in the table, and return the entry. entryHashTable136 ALWAYS_INLINE const HashEntry* entry(JSGlobalData* globalData, const Identifier& identifier) const 137 { 138 initializeIfNeeded(globalData); 139 return entry(identifier); 140 } 141 entryHashTable142 ALWAYS_INLINE const HashEntry* entry(ExecState* exec, const Identifier& identifier) const 143 { 144 initializeIfNeeded(exec); 145 return entry(identifier); 146 } 147 148 private: entryHashTable149 ALWAYS_INLINE const HashEntry* entry(const Identifier& identifier) const 150 { 151 #if ENABLE(PERFECT_HASH_SIZE) 152 ASSERT(table); 153 const HashEntry* entry = &table[identifier.ustring().rep()->computedHash() & hashSizeMask]; 154 if (entry->key() != identifier.ustring().rep()) 155 return 0; 156 return entry; 157 #else 158 ASSERT(table); 159 160 const HashEntry* entry = &table[identifier.ustring().rep()->computedHash() & compactHashSizeMask]; 161 162 if (!entry->key()) 163 return 0; 164 165 do { 166 if (entry->key() == identifier.ustring().rep()) 167 return entry; 168 entry = entry->next(); 169 } while (entry); 170 171 return 0; 172 #endif 173 } 174 175 // Convert the hash table keys to identifiers. 176 void createTable(JSGlobalData*) const; 177 }; 178 179 void setUpStaticFunctionSlot(ExecState*, const HashEntry*, JSObject* thisObject, const Identifier& propertyName, PropertySlot&); 180 181 /** 182 * This method does it all (looking in the hashtable, checking for function 183 * overrides, creating the function or retrieving from cache, calling 184 * getValueProperty in case of a non-function property, forwarding to parent if 185 * unknown property). 186 */ 187 template <class ThisImp, class ParentImp> getStaticPropertySlot(ExecState * exec,const HashTable * table,ThisImp * thisObj,const Identifier & propertyName,PropertySlot & slot)188 inline bool getStaticPropertySlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot) 189 { 190 const HashEntry* entry = table->entry(exec, propertyName); 191 192 if (!entry) // not found, forward to parent 193 return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot); 194 195 if (entry->attributes() & Function) 196 setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); 197 else 198 slot.setCustom(thisObj, entry->propertyGetter()); 199 200 return true; 201 } 202 203 /** 204 * Simplified version of getStaticPropertySlot in case there are only functions. 205 * Using this instead of getStaticPropertySlot allows 'this' to avoid implementing 206 * a dummy getValueProperty. 207 */ 208 template <class ParentImp> getStaticFunctionSlot(ExecState * exec,const HashTable * table,JSObject * thisObj,const Identifier & propertyName,PropertySlot & slot)209 inline bool getStaticFunctionSlot(ExecState* exec, const HashTable* table, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot) 210 { 211 if (static_cast<ParentImp*>(thisObj)->ParentImp::getOwnPropertySlot(exec, propertyName, slot)) 212 return true; 213 214 const HashEntry* entry = table->entry(exec, propertyName); 215 if (!entry) 216 return false; 217 218 setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); 219 return true; 220 } 221 222 /** 223 * Simplified version of getStaticPropertySlot in case there are no functions, only "values". 224 * Using this instead of getStaticPropertySlot removes the need for a FuncImp class. 225 */ 226 template <class ThisImp, class ParentImp> getStaticValueSlot(ExecState * exec,const HashTable * table,ThisImp * thisObj,const Identifier & propertyName,PropertySlot & slot)227 inline bool getStaticValueSlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot) 228 { 229 const HashEntry* entry = table->entry(exec, propertyName); 230 231 if (!entry) // not found, forward to parent 232 return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot); 233 234 ASSERT(!(entry->attributes() & Function)); 235 236 slot.setCustom(thisObj, entry->propertyGetter()); 237 return true; 238 } 239 240 /** 241 * This one is for "put". 242 * It looks up a hash entry for the property to be set. If an entry 243 * is found it sets the value and returns true, else it returns false. 244 */ 245 template <class ThisImp> lookupPut(ExecState * exec,const Identifier & propertyName,JSValuePtr value,const HashTable * table,ThisImp * thisObj)246 inline bool lookupPut(ExecState* exec, const Identifier& propertyName, JSValuePtr value, const HashTable* table, ThisImp* thisObj) 247 { 248 const HashEntry* entry = table->entry(exec, propertyName); 249 250 if (!entry) 251 return false; 252 253 if (entry->attributes() & Function) // function: put as override property 254 thisObj->putDirect(propertyName, value); 255 else if (!(entry->attributes() & ReadOnly)) 256 entry->propertyPutter()(exec, thisObj, value); 257 258 return true; 259 } 260 261 /** 262 * This one is for "put". 263 * It calls lookupPut<ThisImp>() to set the value. If that call 264 * returns false (meaning no entry in the hash table was found), 265 * then it calls put() on the ParentImp class. 266 */ 267 template <class ThisImp, class ParentImp> lookupPut(ExecState * exec,const Identifier & propertyName,JSValuePtr value,const HashTable * table,ThisImp * thisObj,PutPropertySlot & slot)268 inline void lookupPut(ExecState* exec, const Identifier& propertyName, JSValuePtr value, const HashTable* table, ThisImp* thisObj, PutPropertySlot& slot) 269 { 270 if (!lookupPut<ThisImp>(exec, propertyName, value, table, thisObj)) 271 thisObj->ParentImp::put(exec, propertyName, value, slot); // not found: forward to parent 272 } 273 274 } // namespace JSC 275 276 #endif // Lookup_h 277