/* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) * Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #ifndef Lookup_h #define Lookup_h #include "BatchedTransitionOptimizer.h" #include "CallFrame.h" #include "CustomGetterSetter.h" #include "Identifier.h" #include "IdentifierInlines.h" #include "Intrinsic.h" #include "JSGlobalObject.h" #include "PropertySlot.h" #include "PutPropertySlot.h" #include namespace JSC { struct CompactHashIndex { const int16_t value; const int16_t next; }; // FIXME: There is no reason this get function can't be simpler. // ie. typedef JSValue (*GetFunction)(ExecState*, JSObject* baseObject) typedef PropertySlot::GetValueFunc GetFunction; typedef PutPropertySlot::PutValueFunc PutFunction; typedef FunctionExecutable* (*BuiltinGenerator)(VM&); // Hash table generated by the create_hash_table script. struct HashTableValue { const char* m_key; // property name unsigned m_attributes; // JSObject attributes Intrinsic m_intrinsic; union ValueStorage { constexpr ValueStorage(intptr_t value1, intptr_t value2) : value1(value1) , value2(value2) { } constexpr ValueStorage(long long constant) : constant(constant) { } struct { intptr_t value1; intptr_t value2; }; long long constant; } m_values; unsigned attributes() const { return m_attributes; } Intrinsic intrinsic() const { ASSERT(m_attributes & Function); return m_intrinsic; } BuiltinGenerator builtinGenerator() const { ASSERT(m_attributes & Builtin); return reinterpret_cast(m_values.value1); } NativeFunction function() const { ASSERT(m_attributes & Function); return reinterpret_cast(m_values.value1); } unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast(m_values.value2); } GetFunction propertyGetter() const { ASSERT(!(m_attributes & BuiltinOrFunctionOrAccessorOrConstant)); return reinterpret_cast(m_values.value1); } PutFunction propertyPutter() const { ASSERT(!(m_attributes & BuiltinOrFunctionOrAccessorOrConstant)); return reinterpret_cast(m_values.value2); } NativeFunction accessorGetter() const { ASSERT(m_attributes & Accessor); return reinterpret_cast(m_values.value1); } NativeFunction accessorSetter() const { ASSERT(m_attributes & Accessor); return reinterpret_cast(m_values.value2); } BuiltinGenerator builtinAccessorGetterGenerator() const; BuiltinGenerator builtinAccessorSetterGenerator() const; long long constantInteger() const { ASSERT(m_attributes & ConstantInteger); return m_values.constant; } intptr_t lexerValue() const { ASSERT(!m_attributes); return m_values.value1; } }; struct HashTable { int numberOfValues; int indexMask; bool hasSetterOrReadonlyProperties; const HashTableValue* values; // Fixed values generated by script. const CompactHashIndex* index; // Find an entry in the table, and return the entry. ALWAYS_INLINE const HashTableValue* entry(PropertyName propertyName) const { if (propertyName.isSymbol()) return nullptr; auto uid = propertyName.uid(); if (!uid) return nullptr; int indexEntry = IdentifierRepHash::hash(uid) & indexMask; int valueIndex = index[indexEntry].value; if (valueIndex == -1) return nullptr; while (true) { if (WTF::equal(uid, values[valueIndex].m_key)) return &values[valueIndex]; indexEntry = index[indexEntry].next; if (indexEntry == -1) return nullptr; valueIndex = index[indexEntry].value; ASSERT(valueIndex != -1); }; } class ConstIterator { public: ConstIterator(const HashTable* table, int position) : m_table(table) , m_position(position) { skipInvalidKeys(); } const HashTableValue* value() const { return &m_table->values[m_position]; } const HashTableValue& operator*() const { return *value(); } const char* key() const { return m_table->values[m_position].m_key; } const HashTableValue* operator->() const { return value(); } bool operator!=(const ConstIterator& other) const { ASSERT(m_table == other.m_table); return m_position != other.m_position; } ConstIterator& operator++() { ASSERT(m_position < m_table->numberOfValues); ++m_position; skipInvalidKeys(); return *this; } private: void skipInvalidKeys() { ASSERT(m_position <= m_table->numberOfValues); while (m_position < m_table->numberOfValues && !m_table->values[m_position].m_key) ++m_position; ASSERT(m_position <= m_table->numberOfValues); } const HashTable* m_table; int m_position; }; ConstIterator begin() const { return ConstIterator(this, 0); } ConstIterator end() const { return ConstIterator(this, numberOfValues); } }; JS_EXPORT_PRIVATE bool setUpStaticFunctionSlot(ExecState*, const HashTableValue*, JSObject* thisObject, PropertyName, PropertySlot&); JS_EXPORT_PRIVATE void reifyStaticAccessor(VM&, const HashTableValue&, JSObject& thisObject, PropertyName); inline BuiltinGenerator HashTableValue::builtinAccessorGetterGenerator() const { ASSERT(m_attributes & Accessor); ASSERT(m_attributes & Builtin); return reinterpret_cast(m_values.value1); } inline BuiltinGenerator HashTableValue::builtinAccessorSetterGenerator() const { ASSERT(m_attributes & Accessor); ASSERT(m_attributes & Builtin); return reinterpret_cast(m_values.value2); } /** * This method does it all (looking in the hashtable, checking for function * overrides, creating the function or retrieving from cache, calling * getValueProperty in case of a non-function property, forwarding to parent if * unknown property). */ template inline bool getStaticPropertySlot(ExecState* exec, const HashTable& table, ThisImp* thisObj, PropertyName propertyName, PropertySlot& slot) { if (ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot)) return true; if (thisObj->staticFunctionsReified()) return false; auto* entry = table.entry(propertyName); if (!entry) return false; if (entry->attributes() & BuiltinOrFunctionOrAccessor) return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); if (entry->attributes() & ConstantInteger) { slot.setValue(thisObj, attributesForStructure(entry->attributes()), jsNumber(entry->constantInteger())); return true; } slot.setCacheableCustom(thisObj, attributesForStructure(entry->attributes()), entry->propertyGetter()); return true; } /** * Simplified version of getStaticPropertySlot in case there are only functions. * Using this instead of getStaticPropertySlot allows 'this' to avoid implementing * a dummy getValueProperty. */ template inline bool getStaticFunctionSlot(ExecState* exec, const HashTable& table, JSObject* thisObj, PropertyName propertyName, PropertySlot& slot) { if (ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot)) return true; if (thisObj->staticFunctionsReified()) return false; auto* entry = table.entry(propertyName); if (!entry) return false; return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); } /** * Simplified version of getStaticPropertySlot in case there are no functions, only "values". * Using this instead of getStaticPropertySlot removes the need for a FuncImp class. */ template inline bool getStaticValueSlot(ExecState* exec, const HashTable& table, ThisImp* thisObj, PropertyName propertyName, PropertySlot& slot) { if (ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot)) return true; if (thisObj->staticFunctionsReified()) return false; auto* entry = table.entry(propertyName); if (!entry) return false; ASSERT(!(entry->attributes() & BuiltinOrFunctionOrAccessor)); if (entry->attributes() & ConstantInteger) { slot.setValue(thisObj, attributesForStructure(entry->attributes()), jsNumber(entry->constantInteger())); return true; } slot.setCacheableCustom(thisObj, attributesForStructure(entry->attributes()), entry->propertyGetter()); return true; } // 'base' means the object holding the property (possibly in the prototype chain of the object put was called on). // 'thisValue' is the object that put is being applied to (in the case of a proxy, the proxy target). // 'slot.thisValue()' is the object the put was originally performed on (in the case of a proxy, the proxy itself). inline void putEntry(ExecState* exec, const HashTableValue* entry, JSObject* base, JSObject* thisValue, PropertyName propertyName, JSValue value, PutPropertySlot& slot) { if (entry->attributes() & BuiltinOrFunction) { if (!(entry->attributes() & ReadOnly)) { // If this is a function put it as an override property. if (JSObject* thisObject = jsDynamicCast(thisValue)) thisObject->putDirect(exec->vm(), propertyName, value); } else if (slot.isStrictMode()) throwTypeError(exec, StrictModeReadonlyPropertyWriteError); } else if (entry->attributes() & Accessor) { if (slot.isStrictMode()) throwTypeError(exec, StrictModeReadonlyPropertyWriteError); } else if (!(entry->attributes() & ReadOnly)) { JSValue updateThisValue = entry->attributes() & CustomAccessor ? slot.thisValue() : JSValue(base); entry->propertyPutter()(exec, JSValue::encode(updateThisValue), JSValue::encode(value)); if (entry->attributes() & CustomAccessor) slot.setCustomAccessor(base, entry->propertyPutter()); else slot.setCustomValue(base, entry->propertyPutter()); } else if (slot.isStrictMode()) throwTypeError(exec, StrictModeReadonlyPropertyWriteError); } /** * This one is for "put". * It looks up a hash entry for the property to be set. If an entry * is found it sets the value and returns true, else it returns false. */ inline bool lookupPut(ExecState* exec, PropertyName propertyName, JSObject* base, JSValue value, const HashTable& table, PutPropertySlot& slot) { const HashTableValue* entry = table.entry(propertyName); if (!entry) return false; putEntry(exec, entry, base, base, propertyName, value, slot); return true; } inline void reifyStaticProperty(VM& vm, const HashTableValue& value, JSObject& thisObj) { if (!value.m_key) return; Identifier propertyName = Identifier::fromString(&vm, reinterpret_cast(value.m_key), strlen(value.m_key)); if (value.attributes() & Builtin) { if (value.attributes() & Accessor) reifyStaticAccessor(vm, value, thisObj, propertyName); else thisObj.putDirectBuiltinFunction(vm, thisObj.globalObject(), propertyName, value.builtinGenerator()(vm), attributesForStructure(value.attributes())); return; } if (value.attributes() & Function) { thisObj.putDirectNativeFunction( vm, thisObj.globalObject(), propertyName, value.functionLength(), value.function(), value.intrinsic(), attributesForStructure(value.attributes())); return; } if (value.attributes() & ConstantInteger) { thisObj.putDirect(vm, propertyName, jsNumber(value.constantInteger()), attributesForStructure(value.attributes())); return; } if (value.attributes() & Accessor) { reifyStaticAccessor(vm, value, thisObj, propertyName); return; } CustomGetterSetter* customGetterSetter = CustomGetterSetter::create(vm, value.propertyGetter(), value.propertyPutter()); thisObj.putDirectCustomAccessor(vm, propertyName, customGetterSetter, attributesForStructure(value.attributes())); } template inline void reifyStaticProperties(VM& vm, const HashTableValue (&values)[numberOfValues], JSObject& thisObj) { BatchedTransitionOptimizer transitionOptimizer(vm, &thisObj); for (auto& value : values) reifyStaticProperty(vm, value, thisObj); } } // namespace JSC #endif // Lookup_h