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