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