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
39namespace 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