diff options
Diffstat (limited to 'Source/JavaScriptCore/runtime/Lookup.h')
-rw-r--r-- | Source/JavaScriptCore/runtime/Lookup.h | 579 |
1 files changed, 275 insertions, 304 deletions
diff --git a/Source/JavaScriptCore/runtime/Lookup.h b/Source/JavaScriptCore/runtime/Lookup.h index c90ceae7c..0bf9d2e9b 100644 --- a/Source/JavaScriptCore/runtime/Lookup.h +++ b/Source/JavaScriptCore/runtime/Lookup.h @@ -21,380 +21,351 @@ #ifndef Lookup_h #define Lookup_h +#include "BatchedTransitionOptimizer.h" #include "CallFrame.h" -#include "Intrinsic.h" +#include "CustomGetterSetter.h" #include "Identifier.h" +#include "IdentifierInlines.h" +#include "Intrinsic.h" #include "JSGlobalObject.h" #include "PropertySlot.h" -#include <stdio.h> +#include "PutPropertySlot.h" #include <wtf/Assertions.h> namespace JSC { - // Hash table generated by the create_hash_table script. - struct HashTableValue { - const char* key; // property name - unsigned char attributes; // JSObject attributes - intptr_t value1; - intptr_t value2; - Intrinsic intrinsic; - }; - - // FIXME: There is no reason this get function can't be simpler. - // ie. typedef JSValue (*GetFunction)(ExecState*, JSObject* baseObject) - typedef PropertySlot::GetValueFunc GetFunction; - typedef void (*PutFunction)(ExecState*, JSObject* baseObject, JSValue value); - - class HashEntry { - WTF_MAKE_FAST_ALLOCATED; - public: - void initialize(StringImpl* key, unsigned char attributes, intptr_t v1, intptr_t v2, Intrinsic intrinsic) - { - m_key = key; - m_attributes = attributes; - m_u.store.value1 = v1; - m_u.store.value2 = v2; - m_intrinsic = intrinsic; - m_next = 0; - } - void setKey(StringImpl* key) { m_key = key; } - StringImpl* key() const { return m_key; } - - unsigned char attributes() const { return m_attributes; } - - Intrinsic intrinsic() const - { - ASSERT(m_attributes & Function); - return m_intrinsic; - } +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; - NativeFunction function() const { ASSERT(m_attributes & Function); return m_u.function.functionValue; } - unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast<unsigned char>(m_u.function.length); } + unsigned attributes() const { return m_attributes; } - GetFunction propertyGetter() const { ASSERT(!(m_attributes & Function)); return m_u.property.get; } - PutFunction propertyPutter() const { ASSERT(!(m_attributes & Function)); return m_u.property.put; } + Intrinsic intrinsic() const { ASSERT(m_attributes & Function); return m_intrinsic; } + BuiltinGenerator builtinGenerator() const { ASSERT(m_attributes & Builtin); return reinterpret_cast<BuiltinGenerator>(m_values.value1); } + NativeFunction function() const { ASSERT(m_attributes & Function); return reinterpret_cast<NativeFunction>(m_values.value1); } + unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast<unsigned char>(m_values.value2); } - intptr_t lexerValue() const { ASSERT(!m_attributes); return m_u.lexer.value; } + GetFunction propertyGetter() const { ASSERT(!(m_attributes & BuiltinOrFunctionOrAccessorOrConstant)); return reinterpret_cast<GetFunction>(m_values.value1); } + PutFunction propertyPutter() const { ASSERT(!(m_attributes & BuiltinOrFunctionOrAccessorOrConstant)); return reinterpret_cast<PutFunction>(m_values.value2); } - void setNext(HashEntry *next) { m_next = next; } - HashEntry* next() const { return m_next; } + NativeFunction accessorGetter() const { ASSERT(m_attributes & Accessor); return reinterpret_cast<NativeFunction>(m_values.value1); } + NativeFunction accessorSetter() const { ASSERT(m_attributes & Accessor); return reinterpret_cast<NativeFunction>(m_values.value2); } + BuiltinGenerator builtinAccessorGetterGenerator() const; + BuiltinGenerator builtinAccessorSetterGenerator() const; - private: - StringImpl* m_key; - unsigned char m_attributes; // JSObject attributes - Intrinsic m_intrinsic; - - union { - struct { - intptr_t value1; - intptr_t value2; - } store; - struct { - NativeFunction functionValue; - intptr_t length; // number of arguments for function - } function; - struct { - GetFunction get; - PutFunction put; - } property; - struct { - intptr_t value; - intptr_t unused; - } lexer; - } m_u; - - HashEntry* m_next; - }; + long long constantInteger() const { ASSERT(m_attributes & ConstantInteger); return m_values.constant; } - struct HashTable { + intptr_t lexerValue() const { ASSERT(!m_attributes); return m_values.value1; } +}; - int compactSize; - int compactHashSizeMask; +struct HashTable { + int numberOfValues; + int indexMask; + bool hasSetterOrReadonlyProperties; - const HashTableValue* values; // Fixed values generated by script. - mutable const HashEntry* table; // Table allocated at runtime. + const HashTableValue* values; // Fixed values generated by script. + const CompactHashIndex* index; - ALWAYS_INLINE HashTable copy() const - { - // Don't copy dynamic table since it's thread specific. - HashTable result = { compactSize, compactHashSizeMask, values, 0 }; - return result; - } + // 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); + }; + } - ALWAYS_INLINE void initializeIfNeeded(VM* vm) const + class ConstIterator { + public: + ConstIterator(const HashTable* table, int position) + : m_table(table) + , m_position(position) { - if (!table) - createTable(vm); + skipInvalidKeys(); } - ALWAYS_INLINE void initializeIfNeeded(ExecState* exec) const + const HashTableValue* value() const { - if (!table) - createTable(&exec->vm()); + return &m_table->values[m_position]; } - JS_EXPORT_PRIVATE void deleteTable() const; + const HashTableValue& operator*() const { return *value(); } - // Find an entry in the table, and return the entry. - ALWAYS_INLINE const HashEntry* entry(VM* vm, PropertyName identifier) const + const char* key() const { - initializeIfNeeded(vm); - return entry(identifier); + return m_table->values[m_position].m_key; } - ALWAYS_INLINE const HashEntry* entry(ExecState* exec, PropertyName identifier) const + const HashTableValue* operator->() const { - initializeIfNeeded(exec); - return entry(identifier); + return value(); } - class ConstIterator { - public: - ConstIterator(const HashTable* table, int position) - : m_table(table) - , m_position(position) - { - skipInvalidKeys(); - } - - const HashEntry* operator->() - { - return &m_table->table[m_position]; - } - - const HashEntry* operator*() - { - return &m_table->table[m_position]; - } - - bool operator!=(const ConstIterator& other) - { - ASSERT(m_table == other.m_table); - return m_position != other.m_position; - } - - ConstIterator& operator++() - { - ASSERT(m_position < m_table->compactSize); - ++m_position; - skipInvalidKeys(); - return *this; - } - - private: - void skipInvalidKeys() - { - ASSERT(m_position <= m_table->compactSize); - while (m_position < m_table->compactSize && !m_table->table[m_position].key()) - ++m_position; - ASSERT(m_position <= m_table->compactSize); - } - - const HashTable* m_table; - int m_position; - }; - - ConstIterator begin(VM& vm) const + bool operator!=(const ConstIterator& other) const { - initializeIfNeeded(&vm); - return ConstIterator(this, 0); + ASSERT(m_table == other.m_table); + return m_position != other.m_position; } - ConstIterator end(VM& vm) const + + ConstIterator& operator++() { - initializeIfNeeded(&vm); - return ConstIterator(this, compactSize); + ASSERT(m_position < m_table->numberOfValues); + ++m_position; + skipInvalidKeys(); + return *this; } private: - ALWAYS_INLINE const HashEntry* entry(PropertyName propertyName) const + void skipInvalidKeys() { - StringImpl* impl = propertyName.publicName(); - if (!impl) - return 0; - - ASSERT(table); - - const HashEntry* entry = &table[impl->existingHash() & compactHashSizeMask]; - - if (!entry->key()) - return 0; - - do { - if (entry->key() == impl) - return entry; - entry = entry->next(); - } while (entry); - - return 0; + 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); } - // Convert the hash table keys to identifiers. - JS_EXPORT_PRIVATE void createTable(VM*) const; + const HashTable* m_table; + int m_position; }; - JS_EXPORT_PRIVATE bool setUpStaticFunctionSlot(ExecState*, const HashEntry*, JSObject* thisObject, PropertyName, PropertySlot&); - - /** - * 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 <class ThisImp, class ParentImp> - inline bool getStaticPropertySlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, PropertyName propertyName, PropertySlot& slot) + ConstIterator begin() const { - const HashEntry* entry = table->entry(exec, propertyName); + 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<BuiltinGenerator>(m_values.value1); +} + +inline BuiltinGenerator HashTableValue::builtinAccessorSetterGenerator() const +{ + ASSERT(m_attributes & Accessor); + ASSERT(m_attributes & Builtin); + return reinterpret_cast<BuiltinGenerator>(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 <class ThisImp, class ParentImp> +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; - if (!entry) // not found, forward to parent - return ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot); + auto* entry = table.entry(propertyName); + if (!entry) + return false; - if (entry->attributes() & Function) - return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); + if (entry->attributes() & BuiltinOrFunctionOrAccessor) + return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); - slot.setCacheableCustom(thisObj, entry->propertyGetter()); + if (entry->attributes() & ConstantInteger) { + slot.setValue(thisObj, attributesForStructure(entry->attributes()), jsNumber(entry->constantInteger())); return true; } - template <class ThisImp, class ParentImp> - inline bool getStaticPropertyDescriptor(ExecState* exec, const HashTable* table, ThisImp* thisObj, PropertyName propertyName, PropertyDescriptor& descriptor) - { - const HashEntry* entry = table->entry(exec, propertyName); - - if (!entry) // not found, forward to parent - return ParentImp::getOwnPropertyDescriptor(thisObj, exec, propertyName, descriptor); - - PropertySlot slot; - if (entry->attributes() & Function) { - bool present = setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); - if (present) - descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes()); - return present; - } + slot.setCacheableCustom(thisObj, attributesForStructure(entry->attributes()), entry->propertyGetter()); + return true; +} - slot.setCustom(thisObj, entry->propertyGetter()); - descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes()); +/** + * Simplified version of getStaticPropertySlot in case there are only functions. + * Using this instead of getStaticPropertySlot allows 'this' to avoid implementing + * a dummy getValueProperty. + */ +template <class ParentImp> +inline bool getStaticFunctionSlot(ExecState* exec, const HashTable& table, JSObject* thisObj, PropertyName propertyName, PropertySlot& slot) +{ + if (ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot)) 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 <class ParentImp> - 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; - const HashEntry* entry = table->entry(exec, propertyName); - if (!entry) - return false; + auto* entry = table.entry(propertyName); + if (!entry) + return false; - return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); - } - - /** - * Simplified version of getStaticPropertyDescriptor in case there are only functions. - * Using this instead of getStaticPropertyDescriptor allows 'this' to avoid implementing - * a dummy getValueProperty. - */ - template <class ParentImp> - inline bool getStaticFunctionDescriptor(ExecState* exec, const HashTable* table, JSObject* thisObj, PropertyName propertyName, PropertyDescriptor& descriptor) - { - if (ParentImp::getOwnPropertyDescriptor(static_cast<ParentImp*>(thisObj), exec, propertyName, descriptor)) - return true; - - const HashEntry* entry = table->entry(exec, propertyName); - if (!entry) - return false; - - PropertySlot slot; - bool present = setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); - if (present) - descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes()); - return present; - } + 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 <class ThisImp, class ParentImp> - inline bool getStaticValueSlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, PropertyName propertyName, PropertySlot& slot) - { - const HashEntry* entry = table->entry(exec, propertyName); +/** + * 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 <class ThisImp, class ParentImp> +inline bool getStaticValueSlot(ExecState* exec, const HashTable& table, ThisImp* thisObj, PropertyName propertyName, PropertySlot& slot) +{ + if (ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot)) + return true; - if (!entry) // not found, forward to parent - return ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot); + if (thisObj->staticFunctionsReified()) + return false; - ASSERT(!(entry->attributes() & Function)); + auto* entry = table.entry(propertyName); + if (!entry) + return false; - slot.setCacheableCustom(thisObj, entry->propertyGetter()); - return true; - } + ASSERT(!(entry->attributes() & BuiltinOrFunctionOrAccessor)); - /** - * Simplified version of getStaticPropertyDescriptor in case there are no functions, only "values". - * Using this instead of getStaticPropertyDescriptor removes the need for a FuncImp class. - */ - template <class ThisImp, class ParentImp> - inline bool getStaticValueDescriptor(ExecState* exec, const HashTable* table, ThisImp* thisObj, PropertyName propertyName, PropertyDescriptor& descriptor) - { - const HashEntry* entry = table->entry(exec, propertyName); - - if (!entry) // not found, forward to parent - return ParentImp::getOwnPropertyDescriptor(thisObj, exec, propertyName, descriptor); - - ASSERT(!(entry->attributes() & Function)); - PropertySlot slot; - slot.setCustom(thisObj, entry->propertyGetter()); - descriptor.setDescriptor(slot.getValue(exec, propertyName), entry->attributes()); + if (entry->attributes() & ConstantInteger) { + slot.setValue(thisObj, attributesForStructure(entry->attributes()), jsNumber(entry->constantInteger())); return true; } - template <class ThisImp> - inline void putEntry(ExecState* exec, const HashEntry* entry, PropertyName propertyName, JSValue value, ThisImp* thisObj, bool shouldThrow = false) - { - // If this is a function put it as an override property. - if (entry->attributes() & Function) - thisObj->putDirect(exec->vm(), propertyName, value); - else if (!(entry->attributes() & ReadOnly)) - entry->propertyPutter()(exec, thisObj, value); - else if (shouldThrow) + 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<JSObject*>(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<const LChar*>(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; } - /** - * 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. - */ - template <class ThisImp> - inline bool lookupPut(ExecState* exec, PropertyName propertyName, JSValue value, const HashTable* table, ThisImp* thisObj, bool shouldThrow = false) - { - const HashEntry* entry = table->entry(exec, propertyName); - - if (!entry) - return false; + if (value.attributes() & Function) { + thisObj.putDirectNativeFunction( + vm, thisObj.globalObject(), propertyName, value.functionLength(), + value.function(), value.intrinsic(), attributesForStructure(value.attributes())); + return; + } - putEntry<ThisImp>(exec, entry, propertyName, value, thisObj, shouldThrow); - return true; + if (value.attributes() & ConstantInteger) { + thisObj.putDirect(vm, propertyName, jsNumber(value.constantInteger()), attributesForStructure(value.attributes())); + return; } - /** - * This one is for "put". - * It calls lookupPut<ThisImp>() to set the value. If that call - * returns false (meaning no entry in the hash table was found), - * then it calls put() on the ParentImp class. - */ - template <class ThisImp, class ParentImp> - inline void lookupPut(ExecState* exec, PropertyName propertyName, JSValue value, const HashTable* table, ThisImp* thisObj, PutPropertySlot& slot) - { - if (!lookupPut<ThisImp>(exec, propertyName, value, table, thisObj, slot.isStrictMode())) - ParentImp::put(thisObj, exec, propertyName, value, slot); // not found: forward to parent + 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<unsigned numberOfValues> +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 |