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