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