2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2016 Apple Inc. All rights reserved.
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.
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.
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
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"
35 #include <wtf/Assertions.h>
39 struct CompactHashIndex {
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*);
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;
57 constexpr ValueStorage(intptr_t value1, intptr_t value2)
61 constexpr ValueStorage(long long constant)
72 unsigned attributes() const { return m_attributes; }
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); }
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); }
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;
87 long long constantInteger() const { ASSERT(m_attributes & ConstantInteger); return m_values.constant; }
89 intptr_t lexerValue() const { ASSERT(!m_attributes); return m_values.value1; }
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); }
99 bool hasSetterOrReadonlyProperties;
101 const HashTableValue* values; // Fixed values generated by script.
102 const CompactHashIndex* index;
104 // Find an entry in the table, and return the entry.
105 ALWAYS_INLINE const HashTableValue* entry(PropertyName propertyName) const
107 if (propertyName.isSymbol())
110 auto uid = propertyName.uid();
114 int indexEntry = IdentifierRepHash::hash(uid) & indexMask;
115 int valueIndex = index[indexEntry].value;
116 if (valueIndex == -1)
120 if (WTF::equal(uid, values[valueIndex].m_key))
121 return &values[valueIndex];
123 indexEntry = index[indexEntry].next;
124 if (indexEntry == -1)
126 valueIndex = index[indexEntry].value;
127 ASSERT(valueIndex != -1);
131 class ConstIterator {
133 ConstIterator(const HashTable* table, int position)
135 , m_position(position)
140 const HashTableValue* value() const
142 return &m_table->values[m_position];
145 const HashTableValue& operator*() const { return *value(); }
147 const char* key() const
149 return m_table->values[m_position].m_key;
152 const HashTableValue* operator->() const
157 bool operator!=(const ConstIterator& other) const
159 ASSERT(m_table == other.m_table);
160 return m_position != other.m_position;
163 ConstIterator& operator++()
165 ASSERT(m_position < m_table->numberOfValues);
172 void skipInvalidKeys()
174 ASSERT(m_position <= m_table->numberOfValues);
175 while (m_position < m_table->numberOfValues && !m_table->values[m_position].m_key)
177 ASSERT(m_position <= m_table->numberOfValues);
180 const HashTable* m_table;
184 ConstIterator begin() const
186 return ConstIterator(this, 0);
188 ConstIterator end() const
190 return ConstIterator(this, numberOfValues);
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);
197 inline BuiltinGenerator HashTableValue::builtinAccessorGetterGenerator() const
199 ASSERT(m_attributes & Accessor);
200 ASSERT(m_attributes & Builtin);
201 return reinterpret_cast<BuiltinGenerator>(m_values.value1);
204 inline BuiltinGenerator HashTableValue::builtinAccessorSetterGenerator() const
206 ASSERT(m_attributes & Accessor);
207 ASSERT(m_attributes & Builtin);
208 return reinterpret_cast<BuiltinGenerator>(m_values.value2);
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
217 template <class ThisImp, class ParentImp>
218 inline bool getStaticPropertySlot(ExecState* exec, const HashTable& table, ThisImp* thisObj, PropertyName propertyName, PropertySlot& slot)
220 if (ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot))
223 if (thisObj->staticFunctionsReified())
226 auto* entry = table.entry(propertyName);
230 if (entry->attributes() & BuiltinOrFunctionOrAccessorOrLazyProperty)
231 return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
233 if (entry->attributes() & ConstantInteger) {
234 slot.setValue(thisObj, attributesForStructure(entry->attributes()), jsNumber(entry->constantInteger()));
238 slot.setCacheableCustom(thisObj, attributesForStructure(entry->attributes()), entry->propertyGetter());
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.
247 template <class ParentImp>
248 inline bool getStaticFunctionSlot(ExecState* exec, const HashTable& table, JSObject* thisObj, PropertyName propertyName, PropertySlot& slot)
250 if (ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot))
253 if (thisObj->staticFunctionsReified())
256 auto* entry = table.entry(propertyName);
260 return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot);
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.
267 template <class ThisImp, class ParentImp>
268 inline bool getStaticValueSlot(ExecState* exec, const HashTable& table, ThisImp* thisObj, PropertyName propertyName, PropertySlot& slot)
270 if (ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot))
273 if (thisObj->staticFunctionsReified())
276 auto* entry = table.entry(propertyName);
280 ASSERT(!(entry->attributes() & BuiltinOrFunctionOrAccessorOrLazyProperty));
282 if (entry->attributes() & ConstantInteger) {
283 slot.setValue(thisObj, attributesForStructure(entry->attributes()), jsNumber(entry->constantInteger()));
287 slot.setCacheableCustom(thisObj, attributesForStructure(entry->attributes()), entry->propertyGetter());
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)
296 if (entry->attributes() & BuiltinOrFunction) {
297 if (!(entry->attributes() & ReadOnly)) {
298 // If this is a function put it as an override property.
299 if (JSObject* thisObject = jsDynamicCast<JSObject*>(thisValue))
300 thisObject->putDirect(exec->vm(), propertyName, value);
303 return reject(exec, slot.isStrictMode(), StrictModeReadonlyPropertyWriteError);
306 if (entry->attributes() & Accessor)
307 return reject(exec, slot.isStrictMode(), StrictModeReadonlyPropertyWriteError);
309 if (!(entry->attributes() & ReadOnly)) {
310 bool isAccessor = entry->attributes() & CustomAccessor;
311 JSValue updateThisValue = entry->attributes() & CustomAccessor ? slot.thisValue() : JSValue(base);
312 bool result = callCustomSetter(exec, entry->propertyPutter(), isAccessor, updateThisValue, value);
314 slot.setCustomAccessor(base, entry->propertyPutter());
316 slot.setCustomValue(base, entry->propertyPutter());
320 return reject(exec, slot.isStrictMode(), StrictModeReadonlyPropertyWriteError);
324 * This one is for "put".
325 * It looks up a hash entry for the property to be set. If an entry
326 * is found it sets the value and returns true, else it returns false.
328 inline bool lookupPut(ExecState* exec, PropertyName propertyName, JSObject* base, JSValue value, const HashTable& table, PutPropertySlot& slot, bool& putResult)
330 const HashTableValue* entry = table.entry(propertyName);
335 putResult = putEntry(exec, entry, base, base, propertyName, value, slot);
339 inline void reifyStaticProperty(VM& vm, const HashTableValue& value, JSObject& thisObj)
344 Identifier propertyName = Identifier::fromString(&vm, reinterpret_cast<const LChar*>(value.m_key), strlen(value.m_key));
345 if (value.attributes() & Builtin) {
346 if (value.attributes() & Accessor)
347 reifyStaticAccessor(vm, value, thisObj, propertyName);
349 thisObj.putDirectBuiltinFunction(vm, thisObj.globalObject(), propertyName, value.builtinGenerator()(vm), attributesForStructure(value.attributes()));
353 if (value.attributes() & Function) {
354 thisObj.putDirectNativeFunction(
355 vm, thisObj.globalObject(), propertyName, value.functionLength(),
356 value.function(), value.intrinsic(), attributesForStructure(value.attributes()));
360 if (value.attributes() & ConstantInteger) {
361 thisObj.putDirect(vm, propertyName, jsNumber(value.constantInteger()), attributesForStructure(value.attributes()));
365 if (value.attributes() & Accessor) {
366 reifyStaticAccessor(vm, value, thisObj, propertyName);
370 if (value.attributes() & CellProperty) {
371 LazyCellProperty* property = bitwise_cast<LazyCellProperty*>(
372 bitwise_cast<char*>(&thisObj) + value.lazyCellPropertyOffset());
373 JSCell* result = property->get(&thisObj);
374 thisObj.putDirect(vm, propertyName, result, attributesForStructure(value.attributes()));
378 if (value.attributes() & ClassStructure) {
379 LazyClassStructure* structure = bitwise_cast<LazyClassStructure*>(
380 bitwise_cast<char*>(&thisObj) + value.lazyClassStructureOffset());
381 structure->get(jsCast<JSGlobalObject*>(&thisObj));
385 if (value.attributes() & PropertyCallback) {
386 JSValue result = value.lazyPropertyCallback()(vm, &thisObj);
387 thisObj.putDirect(vm, propertyName, result, attributesForStructure(value.attributes()));
391 CustomGetterSetter* customGetterSetter = CustomGetterSetter::create(vm, value.propertyGetter(), value.propertyPutter());
392 thisObj.putDirectCustomAccessor(vm, propertyName, customGetterSetter, attributesForStructure(value.attributes()));
395 template<unsigned numberOfValues>
396 inline void reifyStaticProperties(VM& vm, const HashTableValue (&values)[numberOfValues], JSObject& thisObj)
398 BatchedTransitionOptimizer transitionOptimizer(vm, &thisObj);
399 for (auto& value : values)
400 reifyStaticProperty(vm, value, thisObj);