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 "JSGlobalObject.h" 27 #include "JSObject.h" 28 #include "PropertySlot.h" 29 #include <stdio.h> 30 #include <wtf/Assertions.h> 31 32 // Bug #26843: Work around Metrowerks compiler bug 33 #if COMPILER(WINSCW) 34 #define JSC_CONST_HASHTABLE 35 #else 36 #define JSC_CONST_HASHTABLE const 37 #endif 38 39 namespace JSC { 40 41 // Hash table generated by the create_hash_table script. 42 struct HashTableValue { 43 const char* key; // property name 44 unsigned char attributes; // JSObject attributes 45 intptr_t value1; 46 intptr_t value2; 47 }; 48 49 // FIXME: There is no reason this get function can't be simpler. 50 // ie. typedef JSValue (*GetFunction)(ExecState*, JSObject* baseObject) 51 typedef PropertySlot::GetValueFunc GetFunction; 52 typedef void (*PutFunction)(ExecState*, JSObject* baseObject, JSValue value); 53 54 class HashEntry : public FastAllocBase { 55 public: initialize(UString::Rep * key,unsigned char attributes,intptr_t v1,intptr_t v2)56 void initialize(UString::Rep* key, unsigned char attributes, intptr_t v1, intptr_t v2) 57 { 58 m_key = key; 59 m_attributes = attributes; 60 m_u.store.value1 = v1; 61 m_u.store.value2 = v2; 62 m_next = 0; 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 setNext(HashEntry * next)78 void setNext(HashEntry *next) { m_next = next; } next()79 HashEntry* next() const { return m_next; } 80 81 private: 82 UString::Rep* m_key; 83 unsigned char m_attributes; // JSObject attributes 84 85 union { 86 struct { 87 intptr_t value1; 88 intptr_t value2; 89 } store; 90 struct { 91 NativeFunction functionValue; 92 intptr_t length; // number of arguments for function 93 } function; 94 struct { 95 GetFunction get; 96 PutFunction put; 97 } property; 98 struct { 99 intptr_t value; 100 intptr_t unused; 101 } lexer; 102 } m_u; 103 104 HashEntry* m_next; 105 }; 106 107 struct HashTable { 108 109 int compactSize; 110 int compactHashSizeMask; 111 112 const HashTableValue* values; // Fixed values generated by script. 113 mutable const HashEntry* table; // Table allocated at runtime. 114 initializeIfNeededHashTable115 ALWAYS_INLINE void initializeIfNeeded(JSGlobalData* globalData) const 116 { 117 if (!table) 118 createTable(globalData); 119 } 120 initializeIfNeededHashTable121 ALWAYS_INLINE void initializeIfNeeded(ExecState* exec) const 122 { 123 if (!table) 124 createTable(&exec->globalData()); 125 } 126 127 void deleteTable() const; 128 129 // Find an entry in the table, and return the entry. entryHashTable130 ALWAYS_INLINE const HashEntry* entry(JSGlobalData* globalData, const Identifier& identifier) const 131 { 132 initializeIfNeeded(globalData); 133 return entry(identifier); 134 } 135 entryHashTable136 ALWAYS_INLINE const HashEntry* entry(ExecState* exec, const Identifier& identifier) const 137 { 138 initializeIfNeeded(exec); 139 return entry(identifier); 140 } 141 142 private: entryHashTable143 ALWAYS_INLINE const HashEntry* entry(const Identifier& identifier) const 144 { 145 ASSERT(table); 146 147 const HashEntry* entry = &table[identifier.ustring().rep()->existingHash() & compactHashSizeMask]; 148 149 if (!entry->key()) 150 return 0; 151 152 do { 153 if (entry->key() == identifier.ustring().rep()) 154 return entry; 155 entry = entry->next(); 156 } while (entry); 157 158 return 0; 159 } 160 161 // Convert the hash table keys to identifiers. 162 void createTable(JSGlobalData*) const; 163 }; 164 165 void setUpStaticFunctionSlot(ExecState*, const HashEntry*, JSObject* thisObject, const Identifier& propertyName, PropertySlot&); 166 167 /** 168 * This method does it all (looking in the hashtable, checking for function 169 * overrides, creating the function or retrieving from cache, calling 170 * getValueProperty in case of a non-function property, forwarding to parent if 171 * unknown property). 172 */ 173 template <class ThisImp, class ParentImp> getStaticPropertySlot(ExecState * exec,const HashTable * table,ThisImp * thisObj,const Identifier & propertyName,PropertySlot & slot)174 inline bool getStaticPropertySlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot) 175 { 176 const HashEntry* entry = table->entry(exec, propertyName); 177 178 if (!entry) // not found, forward to parent 179 return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot); 180 181 if (entry->attributes() & Function) 182 setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); 183 else 184 slot.setCustom(thisObj, entry->propertyGetter()); 185 186 return true; 187 } 188 189 template <class ThisImp, class ParentImp> getStaticPropertyDescriptor(ExecState * exec,const HashTable * table,ThisImp * thisObj,const Identifier & propertyName,PropertyDescriptor & descriptor)190 inline bool getStaticPropertyDescriptor(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertyDescriptor& descriptor) 191 { 192 const HashEntry* entry = table->entry(exec, propertyName); 193 194 if (!entry) // not found, forward to parent 195 return thisObj->ParentImp::getOwnPropertyDescriptor(exec, propertyName, descriptor); 196 197 PropertySlot slot; 198 if (entry->attributes() & Function) 199 setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); 200 else 201 slot.setCustom(thisObj, entry->propertyGetter()); 202 203 descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes()); 204 return true; 205 } 206 207 /** 208 * Simplified version of getStaticPropertySlot in case there are only functions. 209 * Using this instead of getStaticPropertySlot allows 'this' to avoid implementing 210 * a dummy getValueProperty. 211 */ 212 template <class ParentImp> getStaticFunctionSlot(ExecState * exec,const HashTable * table,JSObject * thisObj,const Identifier & propertyName,PropertySlot & slot)213 inline bool getStaticFunctionSlot(ExecState* exec, const HashTable* table, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot) 214 { 215 if (static_cast<ParentImp*>(thisObj)->ParentImp::getOwnPropertySlot(exec, propertyName, slot)) 216 return true; 217 218 const HashEntry* entry = table->entry(exec, propertyName); 219 if (!entry) 220 return false; 221 222 setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); 223 return true; 224 } 225 226 /** 227 * Simplified version of getStaticPropertyDescriptor in case there are only functions. 228 * Using this instead of getStaticPropertyDescriptor allows 'this' to avoid implementing 229 * a dummy getValueProperty. 230 */ 231 template <class ParentImp> getStaticFunctionDescriptor(ExecState * exec,const HashTable * table,JSObject * thisObj,const Identifier & propertyName,PropertyDescriptor & descriptor)232 inline bool getStaticFunctionDescriptor(ExecState* exec, const HashTable* table, JSObject* thisObj, const Identifier& propertyName, PropertyDescriptor& descriptor) 233 { 234 if (static_cast<ParentImp*>(thisObj)->ParentImp::getOwnPropertyDescriptor(exec, propertyName, descriptor)) 235 return true; 236 237 const HashEntry* entry = table->entry(exec, propertyName); 238 if (!entry) 239 return false; 240 241 PropertySlot slot; 242 setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); 243 descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes()); 244 return true; 245 } 246 247 /** 248 * Simplified version of getStaticPropertySlot in case there are no functions, only "values". 249 * Using this instead of getStaticPropertySlot removes the need for a FuncImp class. 250 */ 251 template <class ThisImp, class ParentImp> getStaticValueSlot(ExecState * exec,const HashTable * table,ThisImp * thisObj,const Identifier & propertyName,PropertySlot & slot)252 inline bool getStaticValueSlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot) 253 { 254 const HashEntry* entry = table->entry(exec, propertyName); 255 256 if (!entry) // not found, forward to parent 257 return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot); 258 259 ASSERT(!(entry->attributes() & Function)); 260 261 slot.setCustom(thisObj, entry->propertyGetter()); 262 return true; 263 } 264 265 /** 266 * Simplified version of getStaticPropertyDescriptor in case there are no functions, only "values". 267 * Using this instead of getStaticPropertyDescriptor removes the need for a FuncImp class. 268 */ 269 template <class ThisImp, class ParentImp> getStaticValueDescriptor(ExecState * exec,const HashTable * table,ThisImp * thisObj,const Identifier & propertyName,PropertyDescriptor & descriptor)270 inline bool getStaticValueDescriptor(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertyDescriptor& descriptor) 271 { 272 const HashEntry* entry = table->entry(exec, propertyName); 273 274 if (!entry) // not found, forward to parent 275 return thisObj->ParentImp::getOwnPropertyDescriptor(exec, propertyName, descriptor); 276 277 ASSERT(!(entry->attributes() & Function)); 278 PropertySlot slot; 279 slot.setCustom(thisObj, entry->propertyGetter()); 280 descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes()); 281 return true; 282 } 283 284 /** 285 * This one is for "put". 286 * It looks up a hash entry for the property to be set. If an entry 287 * is found it sets the value and returns true, else it returns false. 288 */ 289 template <class ThisImp> lookupPut(ExecState * exec,const Identifier & propertyName,JSValue value,const HashTable * table,ThisImp * thisObj)290 inline bool lookupPut(ExecState* exec, const Identifier& propertyName, JSValue value, const HashTable* table, ThisImp* thisObj) 291 { 292 const HashEntry* entry = table->entry(exec, propertyName); 293 294 if (!entry) 295 return false; 296 297 if (entry->attributes() & Function) { // function: put as override property 298 if (LIKELY(value.isCell())) 299 thisObj->putDirectFunction(propertyName, value.asCell()); 300 else 301 thisObj->putDirect(propertyName, value); 302 } else if (!(entry->attributes() & ReadOnly)) 303 entry->propertyPutter()(exec, thisObj, value); 304 305 return true; 306 } 307 308 /** 309 * This one is for "put". 310 * It calls lookupPut<ThisImp>() to set the value. If that call 311 * returns false (meaning no entry in the hash table was found), 312 * then it calls put() on the ParentImp class. 313 */ 314 template <class ThisImp, class ParentImp> lookupPut(ExecState * exec,const Identifier & propertyName,JSValue value,const HashTable * table,ThisImp * thisObj,PutPropertySlot & slot)315 inline void lookupPut(ExecState* exec, const Identifier& propertyName, JSValue value, const HashTable* table, ThisImp* thisObj, PutPropertySlot& slot) 316 { 317 if (!lookupPut<ThisImp>(exec, propertyName, value, table, thisObj)) 318 thisObj->ParentImp::put(exec, propertyName, value, slot); // not found: forward to parent 319 } 320 321 } // namespace JSC 322 323 #endif // Lookup_h 324