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 
     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:
     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 
     65         void setKey(UString::Rep* key) { m_key = key; }
     66         UString::Rep* key() const { return m_key; }
     67 
     68         unsigned char attributes() const { return m_attributes; }
     69 
     70         NativeFunction function() const { ASSERT(m_attributes & Function); return m_u.function.functionValue; }
     71         unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast<unsigned char>(m_u.function.length); }
     72 
     73         GetFunction propertyGetter() const { ASSERT(!(m_attributes & Function)); return m_u.property.get; }
     74         PutFunction propertyPutter() const { ASSERT(!(m_attributes & Function)); return m_u.property.put; }
     75 
     76         intptr_t lexerValue() const { ASSERT(!m_attributes); return m_u.lexer.value; }
     77 
     78         void setNext(HashEntry *next) { m_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 
    115         ALWAYS_INLINE void initializeIfNeeded(JSGlobalData* globalData) const
    116         {
    117             if (!table)
    118                 createTable(globalData);
    119         }
    120 
    121         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.
    130         ALWAYS_INLINE const HashEntry* entry(JSGlobalData* globalData, const Identifier& identifier) const
    131         {
    132             initializeIfNeeded(globalData);
    133             return entry(identifier);
    134         }
    135 
    136         ALWAYS_INLINE const HashEntry* entry(ExecState* exec, const Identifier& identifier) const
    137         {
    138             initializeIfNeeded(exec);
    139             return entry(identifier);
    140         }
    141 
    142     private:
    143         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>
    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>
    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>
    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>
    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>
    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>
    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>
    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>
    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