Home | History | Annotate | Download | only in runtime
      1 /*
      2  *  Copyright (C) 1999-2000 Harri Porten (porten (at) 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 
     75         void setKey(StringImpl* key) { m_key = key; }
     76         StringImpl* key() const { return m_key; }
     77 
     78         unsigned char attributes() const { return m_attributes; }
     79 
     80 #if ENABLE(JIT) && ENABLE(JIT_OPTIMIZE_NATIVE_CALL)
     81         ThunkGenerator generator() const { ASSERT(m_attributes & Function); return m_u.function.generator; }
     82 #endif
     83         NativeFunction function() const { ASSERT(m_attributes & Function); return m_u.function.functionValue; }
     84         unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast<unsigned char>(m_u.function.length); }
     85 
     86         GetFunction propertyGetter() const { ASSERT(!(m_attributes & Function)); return m_u.property.get; }
     87         PutFunction propertyPutter() const { ASSERT(!(m_attributes & Function)); return m_u.property.put; }
     88 
     89         intptr_t lexerValue() const { ASSERT(!m_attributes); return m_u.lexer.value; }
     90 
     91         void setNext(HashEntry *next) { m_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 
    131         ALWAYS_INLINE void initializeIfNeeded(JSGlobalData* globalData) const
    132         {
    133             if (!table)
    134                 createTable(globalData);
    135         }
    136 
    137         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.
    146         ALWAYS_INLINE const HashEntry* entry(JSGlobalData* globalData, const Identifier& identifier) const
    147         {
    148             initializeIfNeeded(globalData);
    149             return entry(identifier);
    150         }
    151 
    152         ALWAYS_INLINE const HashEntry* entry(ExecState* exec, const Identifier& identifier) const
    153         {
    154             initializeIfNeeded(exec);
    155             return entry(identifier);
    156         }
    157 
    158     private:
    159         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>
    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>
    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>
    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>
    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>
    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>
    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>
    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>
    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