Static property hashtable should only lookup with non-symbol key
[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         if (propertyName.isSymbol())
101             return nullptr;
102
103         StringImpl* uid = propertyName.uid();
104         if (!uid)
105             return nullptr;
106
107         ASSERT(keys);
108
109         int indexEntry = IdentifierRepHash::hash(uid) & indexMask;
110         int valueIndex = index[indexEntry].value;
111         if (valueIndex == -1)
112             return nullptr;
113
114         while (true) {
115             if (WTF::equal(uid, keys[valueIndex]))
116                 return &values[valueIndex];
117
118             indexEntry = index[indexEntry].next;
119             if (indexEntry == -1)
120                 return nullptr;
121             valueIndex = index[indexEntry].value;
122             ASSERT(valueIndex != -1);
123         };
124     }
125
126     class ConstIterator {
127     public:
128         ConstIterator(const HashTable* table, int position)
129             : m_table(table)
130             , m_position(position)
131         {
132             skipInvalidKeys();
133         }
134
135         const HashTableValue* value()
136         {
137             return &m_table->values[m_position];
138         }
139
140         const char* key()
141         {
142             return m_table->keys[m_position];
143         }
144
145         const HashTableValue* operator->()
146         {
147             return value();
148         }
149
150         bool operator!=(const ConstIterator& other)
151         {
152             ASSERT(m_table == other.m_table);
153             return m_position != other.m_position;
154         }
155
156         ConstIterator& operator++()
157         {
158             ASSERT(m_position < m_table->numberOfValues);
159             ++m_position;
160             skipInvalidKeys();
161             return *this;
162         }
163
164     private:
165         void skipInvalidKeys()
166         {
167             ASSERT(m_position <= m_table->numberOfValues);
168             while (m_position < m_table->numberOfValues && !m_table->keys[m_position])
169                 ++m_position;
170             ASSERT(m_position <= m_table->numberOfValues);
171         }
172
173         const HashTable* m_table;
174         int m_position;
175     };
176
177     ConstIterator begin() const
178     {
179         initializeIfNeeded();
180         return ConstIterator(this, 0);
181     }
182     ConstIterator end() const
183     {
184         initializeIfNeeded();
185         return ConstIterator(this, numberOfValues);
186     }
187
188 private:
189     // Convert the hash table keys to identifiers.
190     JS_EXPORT_PRIVATE void createTable() const;
191 };
192
193 JS_EXPORT_PRIVATE bool setUpStaticFunctionSlot(ExecState*, const HashTableValue*, JSObject* thisObject, PropertyName, PropertySlot&);
194
195 /**
196  * This method does it all (looking in the hashtable, checking for function
197  * overrides, creating the function or retrieving from cache, calling
198  * getValueProperty in case of a non-function property, forwarding to parent if
199  * unknown property).
200  */
201 template <class ThisImp, class ParentImp>
202 inline bool getStaticPropertySlot(ExecState* exec, const HashTable& table, ThisImp* thisObj, PropertyName propertyName, PropertySlot& slot)
203 {
204     const HashTableValue* entry = table.entry(propertyName);
205
206     if (!entry) // not found, forward to parent
207         return ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot);
208
209     if (entry->attributes() & BuiltinOrFunction)
210         return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
211
212     if (entry->attributes() & ConstantInteger) {
213         slot.setValue(thisObj, entry->attributes(), jsNumber(entry->constantInteger()));
214         return true;
215     }
216
217     slot.setCacheableCustom(thisObj, entry->attributes(), entry->propertyGetter());
218     return true;
219 }
220
221 /**
222  * Simplified version of getStaticPropertySlot in case there are only functions.
223  * Using this instead of getStaticPropertySlot allows 'this' to avoid implementing
224  * a dummy getValueProperty.
225  */
226 template <class ParentImp>
227 inline bool getStaticFunctionSlot(ExecState* exec, const HashTable& table, JSObject* thisObj, PropertyName propertyName, PropertySlot& slot)
228 {
229     if (ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot))
230         return true;
231
232     const HashTableValue* entry = table.entry(propertyName);
233     if (!entry)
234         return false;
235
236     return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
237 }
238
239 /**
240  * Simplified version of getStaticPropertySlot in case there are no functions, only "values".
241  * Using this instead of getStaticPropertySlot removes the need for a FuncImp class.
242  */
243 template <class ThisImp, class ParentImp>
244 inline bool getStaticValueSlot(ExecState* exec, const HashTable& table, ThisImp* thisObj, PropertyName propertyName, PropertySlot& slot)
245 {
246     const HashTableValue* entry = table.entry(propertyName);
247
248     if (!entry) // not found, forward to parent
249         return ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot);
250
251     ASSERT(!(entry->attributes() & BuiltinOrFunction));
252
253     if (entry->attributes() & ConstantInteger) {
254         slot.setValue(thisObj, entry->attributes(), jsNumber(entry->constantInteger()));
255         return true;
256     }
257
258     slot.setCacheableCustom(thisObj, entry->attributes(), entry->propertyGetter());
259     return true;
260 }
261
262 inline void putEntry(ExecState* exec, const HashTableValue* entry, JSObject* base, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
263 {
264     // If this is a function put it as an override property.
265     if (entry->attributes() & BuiltinOrFunction) {
266         if (JSObject* thisObject = jsDynamicCast<JSObject*>(slot.thisValue()))
267             thisObject->putDirect(exec->vm(), propertyName, value);
268     } else if (!(entry->attributes() & ReadOnly)) {
269         entry->propertyPutter()(exec, base, JSValue::encode(slot.thisValue()), JSValue::encode(value));
270         slot.setCustomProperty(base, entry->propertyPutter());
271     } else if (slot.isStrictMode())
272         throwTypeError(exec, StrictModeReadonlyPropertyWriteError);
273 }
274
275 /**
276  * This one is for "put".
277  * It looks up a hash entry for the property to be set.  If an entry
278  * is found it sets the value and returns true, else it returns false.
279  */
280 inline bool lookupPut(ExecState* exec, PropertyName propertyName, JSObject* base, JSValue value, const HashTable& table, PutPropertySlot& slot)
281 {
282     const HashTableValue* entry = table.entry(propertyName);
283
284     if (!entry)
285         return false;
286
287     putEntry(exec, entry, base, propertyName, value, slot);
288     return true;
289 }
290
291 template<unsigned numberOfValues>
292 inline void reifyStaticProperties(VM& vm, const HashTableValue (&values)[numberOfValues], JSObject& thisObj)
293 {
294     BatchedTransitionOptimizer transitionOptimizer(vm, &thisObj);
295     for (auto& value : values) {
296         if (!value.m_key)
297             continue;                
298
299         Identifier propertyName = Identifier::fromString(&vm, reinterpret_cast<const LChar*>(value.m_key), strlen(value.m_key));
300         if (value.attributes() & Builtin) {
301             thisObj.putDirectBuiltinFunction(vm, thisObj.globalObject(), propertyName, value.builtinGenerator()(vm), value.attributes());
302             continue;
303         }
304
305         if (value.attributes() & Function) {
306             thisObj.putDirectNativeFunction(vm, thisObj.globalObject(), propertyName, value.functionLength(),
307                 value.function(), value.intrinsic(), value.attributes());
308             continue;
309         }
310
311         if (value.attributes() & ConstantInteger) {
312             thisObj.putDirect(vm, propertyName, jsNumber(value.constantInteger()), value.attributes());
313             continue;
314         }
315
316         if (value.attributes() & Accessor) {
317             RELEASE_ASSERT_NOT_REACHED();
318             continue;
319         }
320
321         CustomGetterSetter* customGetterSetter = CustomGetterSetter::create(vm, value.propertyGetter(), value.propertyPutter());
322         thisObj.putDirectCustomAccessor(vm, propertyName, customGetterSetter, value.attributes());
323     }
324 }
325
326 } // namespace JSC
327
328 #endif // Lookup_h