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