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