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 "BatchedTransitionOptimizer.h"
25#include "CallFrame.h"
26#include "CustomGetterSetter.h"
27#include "Identifier.h"
28#include "IdentifierInlines.h"
29#include "Intrinsic.h"
30#include "JSGlobalObject.h"
31#include "PropertySlot.h"
32#include "PutPropertySlot.h"
33#include <wtf/Assertions.h>
34
35namespace JSC {
36    struct CompactHashIndex {
37        const int16_t value;
38        const int16_t next;
39    };
40
41    // FIXME: There is no reason this get function can't be simpler.
42    // ie. typedef JSValue (*GetFunction)(ExecState*, JSObject* baseObject)
43    typedef PropertySlot::GetValueFunc GetFunction;
44    typedef PutPropertySlot::PutValueFunc PutFunction;
45    typedef FunctionExecutable* (*BuiltinGenerator)(VM&);
46
47    // Hash table generated by the create_hash_table script.
48    struct HashTableValue {
49        const char* m_key; // property name
50        unsigned m_attributes; // JSObject attributes
51        Intrinsic m_intrinsic;
52        intptr_t m_value1;
53        intptr_t m_value2;
54
55        unsigned attributes() const { return m_attributes; }
56
57        Intrinsic intrinsic() const { ASSERT(m_attributes & Function); return m_intrinsic; }
58        BuiltinGenerator builtinGenerator() const { ASSERT(m_attributes & Builtin); return reinterpret_cast<BuiltinGenerator>(m_value1); }
59        NativeFunction function() const { ASSERT(m_attributes & Function); return reinterpret_cast<NativeFunction>(m_value1); }
60        unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast<unsigned char>(m_value2); }
61
62        GetFunction propertyGetter() const { ASSERT(!(m_attributes & BuiltinOrFunctionOrConstant)); return reinterpret_cast<GetFunction>(m_value1); }
63        PutFunction propertyPutter() const { ASSERT(!(m_attributes & BuiltinOrFunctionOrConstant)); return reinterpret_cast<PutFunction>(m_value2); }
64
65        intptr_t constantInteger() const { ASSERT(m_attributes & ConstantInteger); return m_value1; }
66
67        intptr_t lexerValue() const { ASSERT(!m_attributes); return m_value1; }
68    };
69
70    struct HashTable {
71        mutable int numberOfValues;
72        int indexMask;
73        bool hasSetterOrReadonlyProperties;
74
75        const HashTableValue* values; // Fixed values generated by script.
76        mutable const char** keys; // Table allocated at runtime.
77        const CompactHashIndex* index;
78
79        ALWAYS_INLINE HashTable copy() const
80        {
81            // Don't copy dynamic table since it's thread specific.
82            HashTable result = { numberOfValues, indexMask, hasSetterOrReadonlyProperties, values, 0, index };
83            return result;
84        }
85
86        ALWAYS_INLINE void initializeIfNeeded(VM& vm) const
87        {
88            if (!keys)
89                createTable(vm);
90        }
91
92        ALWAYS_INLINE void initializeIfNeeded(ExecState* exec) const
93        {
94            if (!keys)
95                createTable(exec->vm());
96        }
97
98        JS_EXPORT_PRIVATE void deleteTable() const;
99
100        // Find an entry in the table, and return the entry.
101        ALWAYS_INLINE const HashTableValue* entry(VM& vm, PropertyName identifier) const
102        {
103            initializeIfNeeded(vm);
104            return entry(identifier);
105        }
106
107        ALWAYS_INLINE const HashTableValue* entry(ExecState* exec, PropertyName identifier) const
108        {
109            initializeIfNeeded(exec);
110            return entry(identifier);
111        }
112
113        class ConstIterator {
114        public:
115            ConstIterator(const HashTable* table, int position)
116                : m_table(table)
117                , m_position(position)
118            {
119                skipInvalidKeys();
120            }
121
122            const HashTableValue* value()
123            {
124                return &m_table->values[m_position];
125            }
126
127            const char* key()
128            {
129                return m_table->keys[m_position];
130            }
131
132            const HashTableValue* operator->()
133            {
134                return value();
135            }
136
137            bool operator!=(const ConstIterator& other)
138            {
139                ASSERT(m_table == other.m_table);
140                return m_position != other.m_position;
141            }
142
143            ConstIterator& operator++()
144            {
145                ASSERT(m_position < m_table->numberOfValues);
146                ++m_position;
147                skipInvalidKeys();
148                return *this;
149            }
150
151        private:
152            void skipInvalidKeys()
153            {
154                ASSERT(m_position <= m_table->numberOfValues);
155                while (m_position < m_table->numberOfValues && !m_table->keys[m_position])
156                    ++m_position;
157                ASSERT(m_position <= m_table->numberOfValues);
158            }
159
160            const HashTable* m_table;
161            int m_position;
162        };
163
164        ConstIterator begin(VM& vm) const
165        {
166            initializeIfNeeded(vm);
167            return ConstIterator(this, 0);
168        }
169        ConstIterator end(VM& vm) const
170        {
171            initializeIfNeeded(vm);
172            return ConstIterator(this, numberOfValues);
173        }
174
175    private:
176        ALWAYS_INLINE const HashTableValue* entry(PropertyName propertyName) const
177        {
178            StringImpl* impl = propertyName.uid();
179            if (!impl)
180                return 0;
181
182            ASSERT(keys);
183
184            int indexEntry = impl->existingHash() & indexMask;
185            int valueIndex = index[indexEntry].value;
186            if (valueIndex == -1)
187                return 0;
188
189            while (true) {
190                if (WTF::equal(impl, keys[valueIndex]))
191                    return &values[valueIndex];
192
193                indexEntry = index[indexEntry].next;
194                if (indexEntry == -1)
195                    return nullptr;
196                valueIndex = index[indexEntry].value;
197                ASSERT(valueIndex != -1);
198            };
199        }
200
201        // Convert the hash table keys to identifiers.
202        JS_EXPORT_PRIVATE void createTable(VM&) const;
203    };
204
205    JS_EXPORT_PRIVATE bool setUpStaticFunctionSlot(ExecState*, const HashTableValue*, JSObject* thisObject, PropertyName, PropertySlot&);
206
207    /**
208     * This method does it all (looking in the hashtable, checking for function
209     * overrides, creating the function or retrieving from cache, calling
210     * getValueProperty in case of a non-function property, forwarding to parent if
211     * unknown property).
212     */
213    template <class ThisImp, class ParentImp>
214    inline bool getStaticPropertySlot(ExecState* exec, const HashTable& table, ThisImp* thisObj, PropertyName propertyName, PropertySlot& slot)
215    {
216        const HashTableValue* entry = table.entry(exec, propertyName);
217
218        if (!entry) // not found, forward to parent
219            return ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot);
220
221        if (entry->attributes() & BuiltinOrFunction)
222            return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
223
224        if (entry->attributes() & ConstantInteger) {
225            slot.setValue(thisObj, entry->attributes(), jsNumber(entry->constantInteger()));
226            return true;
227        }
228
229        slot.setCacheableCustom(thisObj, entry->attributes(), entry->propertyGetter());
230        return true;
231    }
232
233    /**
234     * Simplified version of getStaticPropertySlot in case there are only functions.
235     * Using this instead of getStaticPropertySlot allows 'this' to avoid implementing
236     * a dummy getValueProperty.
237     */
238    template <class ParentImp>
239    inline bool getStaticFunctionSlot(ExecState* exec, const HashTable& table, JSObject* thisObj, PropertyName propertyName, PropertySlot& slot)
240    {
241        if (ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot))
242            return true;
243
244        const HashTableValue* entry = table.entry(exec, propertyName);
245        if (!entry)
246            return false;
247
248        return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
249    }
250
251    /**
252     * Simplified version of getStaticPropertySlot in case there are no functions, only "values".
253     * Using this instead of getStaticPropertySlot removes the need for a FuncImp class.
254     */
255    template <class ThisImp, class ParentImp>
256    inline bool getStaticValueSlot(ExecState* exec, const HashTable& table, ThisImp* thisObj, PropertyName propertyName, PropertySlot& slot)
257    {
258        const HashTableValue* entry = table.entry(exec, propertyName);
259
260        if (!entry) // not found, forward to parent
261            return ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot);
262
263        ASSERT(!(entry->attributes() & BuiltinOrFunction));
264
265        if (entry->attributes() & ConstantInteger) {
266            slot.setValue(thisObj, entry->attributes(), jsNumber(entry->constantInteger()));
267            return true;
268        }
269
270        slot.setCacheableCustom(thisObj, entry->attributes(), entry->propertyGetter());
271        return true;
272    }
273
274    inline void putEntry(ExecState* exec, const HashTableValue* entry, JSObject* base, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
275    {
276        // If this is a function put it as an override property.
277        if (entry->attributes() & BuiltinOrFunction) {
278            if (JSObject* thisObject = jsDynamicCast<JSObject*>(slot.thisValue()))
279                thisObject->putDirect(exec->vm(), propertyName, value);
280        } else if (!(entry->attributes() & ReadOnly)) {
281            entry->propertyPutter()(exec, base, JSValue::encode(slot.thisValue()), JSValue::encode(value));
282            slot.setCustomProperty(base, entry->propertyPutter());
283        } else if (slot.isStrictMode())
284            throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
285    }
286
287    /**
288     * This one is for "put".
289     * It looks up a hash entry for the property to be set.  If an entry
290     * is found it sets the value and returns true, else it returns false.
291     */
292    inline bool lookupPut(ExecState* exec, PropertyName propertyName, JSObject* base, JSValue value, const HashTable& table, PutPropertySlot& slot)
293    {
294        const HashTableValue* entry = table.entry(exec, propertyName);
295
296        if (!entry)
297            return false;
298
299        putEntry(exec, entry, base, propertyName, value, slot);
300        return true;
301    }
302
303    template<unsigned numberOfValues>
304    inline void reifyStaticProperties(VM& vm, const HashTableValue (&values)[numberOfValues], JSObject& thisObj)
305    {
306        BatchedTransitionOptimizer transitionOptimizer(vm, &thisObj);
307        for (auto& value : values) {
308            if (!value.m_key)
309                continue;
310
311            Identifier propertyName(&vm, reinterpret_cast<const LChar*>(value.m_key), strlen(value.m_key));
312            if (value.attributes() & Builtin) {
313                thisObj.putDirectBuiltinFunction(vm, thisObj.globalObject(), propertyName, value.builtinGenerator()(vm), value.attributes());
314                continue;
315            }
316
317            if (value.attributes() & Function) {
318                thisObj.putDirectNativeFunction(vm, thisObj.globalObject(), propertyName, value.functionLength(),
319                    value.function(), value.intrinsic(), value.attributes());
320                continue;
321            }
322
323            if (value.attributes() & ConstantInteger) {
324                thisObj.putDirect(vm, propertyName, jsNumber(value.constantInteger()), value.attributes());
325                continue;
326            }
327
328            if (value.attributes() & Accessor) {
329                RELEASE_ASSERT_NOT_REACHED();
330                continue;
331            }
332
333            CustomGetterSetter* customGetterSetter = CustomGetterSetter::create(vm, value.propertyGetter(), value.propertyPutter());
334            thisObj.putDirectCustomAccessor(vm, propertyName, customGetterSetter, value.attributes());
335        }
336    }
337
338} // namespace JSC
339
340#endif // Lookup_h
341