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
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 "TypeError.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*);
50 typedef DOMJIT::GetterSetter* (*DOMJITGetterSetterGenerator)(void);
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;
58 constexpr ValueStorage(intptr_t value1, intptr_t value2)
62 constexpr ValueStorage(long long constant)
73 unsigned attributes() const { return m_attributes; }
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); }
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); }
83 DOMJIT::GetterSetter* domJIT() const { ASSERT(m_attributes & DOMJITAttribute); return reinterpret_cast<DOMJITGetterSetterGenerator>(m_values.value1)(); }
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;
90 long long constantInteger() const { ASSERT(m_attributes & ConstantInteger); return m_values.constant; }
92 intptr_t lexerValue() const { ASSERT(!m_attributes); return m_values.value1; }
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); }
102 bool hasSetterOrReadonlyProperties;
104 const HashTableValue* values; // Fixed values generated by script.
105 const CompactHashIndex* index;
107 // Find an entry in the table, and return the entry.
108 ALWAYS_INLINE const HashTableValue* entry(PropertyName propertyName) const
110 if (propertyName.isSymbol())
113 auto uid = propertyName.uid();
117 int indexEntry = IdentifierRepHash::hash(uid) & indexMask;
118 int valueIndex = index[indexEntry].value;
119 if (valueIndex == -1)
123 if (WTF::equal(uid, values[valueIndex].m_key))
124 return &values[valueIndex];
126 indexEntry = index[indexEntry].next;
127 if (indexEntry == -1)
129 valueIndex = index[indexEntry].value;
130 ASSERT(valueIndex != -1);
134 class ConstIterator {
136 ConstIterator(const HashTable* table, int position)
138 , m_position(position)
143 const HashTableValue* value() const
145 return &m_table->values[m_position];
148 const HashTableValue& operator*() const { return *value(); }
150 const char* key() const
152 return m_table->values[m_position].m_key;
155 const HashTableValue* operator->() const
160 bool operator!=(const ConstIterator& other) const
162 ASSERT(m_table == other.m_table);
163 return m_position != other.m_position;
166 ConstIterator& operator++()
168 ASSERT(m_position < m_table->numberOfValues);
175 void skipInvalidKeys()
177 ASSERT(m_position <= m_table->numberOfValues);
178 while (m_position < m_table->numberOfValues && !m_table->values[m_position].m_key)
180 ASSERT(m_position <= m_table->numberOfValues);
183 const HashTable* m_table;
187 ConstIterator begin() const
189 return ConstIterator(this, 0);
191 ConstIterator end() const
193 return ConstIterator(this, numberOfValues);
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);
200 inline BuiltinGenerator HashTableValue::builtinAccessorGetterGenerator() const
202 ASSERT(m_attributes & Accessor);
203 ASSERT(m_attributes & Builtin);
204 return reinterpret_cast<BuiltinGenerator>(m_values.value1);
207 inline BuiltinGenerator HashTableValue::builtinAccessorSetterGenerator() const
209 ASSERT(m_attributes & Accessor);
210 ASSERT(m_attributes & Builtin);
211 return reinterpret_cast<BuiltinGenerator>(m_values.value2);
214 inline bool getStaticPropertySlotFromTable(VM& vm, const HashTable& table, JSObject* thisObject, PropertyName propertyName, PropertySlot& slot)
216 if (thisObject->staticPropertiesReified())
219 auto* entry = table.entry(propertyName);
223 if (entry->attributes() & BuiltinOrFunctionOrAccessorOrLazyProperty)
224 return setUpStaticFunctionSlot(vm, entry, thisObject, propertyName, slot);
226 if (entry->attributes() & ConstantInteger) {
227 slot.setValue(thisObject, attributesForStructure(entry->attributes()), jsNumber(entry->constantInteger()));
231 if (entry->attributes() & DOMJITAttribute) {
232 DOMJIT::GetterSetter* domJIT = entry->domJIT();
233 slot.setCacheableCustom(thisObject, attributesForStructure(entry->attributes()), domJIT->getter(), domJIT);
237 slot.setCacheableCustom(thisObject, attributesForStructure(entry->attributes()), entry->propertyGetter());
241 inline bool replaceStaticPropertySlot(VM& vm, JSObject* thisObject, PropertyName propertyName, JSValue value)
243 if (!thisObject->putDirect(vm, propertyName, value))
246 if (!thisObject->staticPropertiesReified())
247 thisObject->JSObject::setStructure(vm, Structure::attributeChangeTransition(vm, thisObject->structure(), propertyName, 0));
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)
258 auto scope = DECLARE_THROW_SCOPE(vm);
260 if (entry->attributes() & BuiltinOrFunctionOrLazyProperty) {
261 if (!(entry->attributes() & ReadOnly)) {
262 // If this is a function or lazy property put then we just do the put because
263 // logically the object already had the property, so this is just a replace.
264 if (JSObject* thisObject = jsDynamicCast<JSObject*>(thisValue))
265 thisObject->putDirect(vm, propertyName, value);
268 return typeError(exec, scope, slot.isStrictMode(), ASCIILiteral(ReadonlyPropertyWriteError));
271 if (entry->attributes() & Accessor)
272 return typeError(exec, scope, slot.isStrictMode(), ASCIILiteral(ReadonlyPropertyWriteError));
274 if (!(entry->attributes() & ReadOnly)) {
275 ASSERT_WITH_MESSAGE(!(entry->attributes() & DOMJITAttribute), "DOMJITAttribute supports readonly attributes currently.");
276 bool isAccessor = entry->attributes() & CustomAccessor;
277 JSValue updateThisValue = entry->attributes() & CustomAccessor ? slot.thisValue() : JSValue(base);
278 bool result = callCustomSetter(exec, entry->propertyPutter(), isAccessor, updateThisValue, value);
280 slot.setCustomAccessor(base, entry->propertyPutter());
282 slot.setCustomValue(base, entry->propertyPutter());
286 return typeError(exec, scope, slot.isStrictMode(), ASCIILiteral(ReadonlyPropertyWriteError));
290 * This one is for "put".
291 * It looks up a hash entry for the property to be set. If an entry
292 * is found it sets the value and returns true, else it returns false.
294 inline bool lookupPut(ExecState* exec, PropertyName propertyName, JSObject* base, JSValue value, const HashTable& table, PutPropertySlot& slot, bool& putResult)
296 const HashTableValue* entry = table.entry(propertyName);
301 putResult = putEntry(exec, entry, base, base, propertyName, value, slot);
305 inline void reifyStaticProperty(VM& vm, const PropertyName& propertyName, const HashTableValue& value, JSObject& thisObj)
307 if (value.attributes() & Builtin) {
308 if (value.attributes() & Accessor)
309 reifyStaticAccessor(vm, value, thisObj, propertyName);
311 thisObj.putDirectBuiltinFunction(vm, thisObj.globalObject(), propertyName, value.builtinGenerator()(vm), attributesForStructure(value.attributes()));
315 if (value.attributes() & Function) {
316 thisObj.putDirectNativeFunction(
317 vm, thisObj.globalObject(), propertyName, value.functionLength(),
318 value.function(), value.intrinsic(), attributesForStructure(value.attributes()));
322 if (value.attributes() & ConstantInteger) {
323 thisObj.putDirect(vm, propertyName, jsNumber(value.constantInteger()), attributesForStructure(value.attributes()));
327 if (value.attributes() & Accessor) {
328 reifyStaticAccessor(vm, value, thisObj, propertyName);
332 if (value.attributes() & CellProperty) {
333 LazyCellProperty* property = bitwise_cast<LazyCellProperty*>(
334 bitwise_cast<char*>(&thisObj) + value.lazyCellPropertyOffset());
335 JSCell* result = property->get(&thisObj);
336 thisObj.putDirect(vm, propertyName, result, attributesForStructure(value.attributes()));
340 if (value.attributes() & ClassStructure) {
341 LazyClassStructure* structure = bitwise_cast<LazyClassStructure*>(
342 bitwise_cast<char*>(&thisObj) + value.lazyClassStructureOffset());
343 structure->get(jsCast<JSGlobalObject*>(&thisObj));
347 if (value.attributes() & PropertyCallback) {
348 JSValue result = value.lazyPropertyCallback()(vm, &thisObj);
349 thisObj.putDirect(vm, propertyName, result, attributesForStructure(value.attributes()));
353 if (value.attributes() & DOMJITAttribute) {
354 DOMJIT::GetterSetter* domJIT = value.domJIT();
355 CustomGetterSetter* customGetterSetter = CustomGetterSetter::create(vm, domJIT->getter(), domJIT->setter(), domJIT);
356 thisObj.putDirectCustomAccessor(vm, propertyName, customGetterSetter, attributesForStructure(value.attributes()));
360 CustomGetterSetter* customGetterSetter = CustomGetterSetter::create(vm, value.propertyGetter(), value.propertyPutter());
361 thisObj.putDirectCustomAccessor(vm, propertyName, customGetterSetter, attributesForStructure(value.attributes()));
364 template<unsigned numberOfValues>
365 inline void reifyStaticProperties(VM& vm, const HashTableValue (&values)[numberOfValues], JSObject& thisObj)
367 BatchedTransitionOptimizer transitionOptimizer(vm, &thisObj);
368 for (auto& value : values) {
371 auto key = Identifier::fromString(&vm, reinterpret_cast<const LChar*>(value.m_key), strlen(value.m_key));
372 reifyStaticProperty(vm, key, value, thisObj);