diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/runtime/Lookup.h | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/JavaScriptCore/runtime/Lookup.h')
-rw-r--r-- | Source/JavaScriptCore/runtime/Lookup.h | 555 |
1 files changed, 313 insertions, 242 deletions
diff --git a/Source/JavaScriptCore/runtime/Lookup.h b/Source/JavaScriptCore/runtime/Lookup.h index bde83d4a6..b627f7947 100644 --- a/Source/JavaScriptCore/runtime/Lookup.h +++ b/Source/JavaScriptCore/runtime/Lookup.h @@ -1,6 +1,6 @@ /* * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) - * Copyright (C) 2003, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2003, 2006, 2007, 2008, 2009, 2016 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 @@ -18,308 +18,379 @@ * */ -#ifndef Lookup_h -#define Lookup_h +#pragma once +#include "BatchedTransitionOptimizer.h" #include "CallFrame.h" -#include "Intrinsic.h" +#include "CustomGetterSetter.h" +#include "DOMJITGetterSetter.h" +#include "DOMJITSignature.h" #include "Identifier.h" +#include "IdentifierInlines.h" +#include "Intrinsic.h" +#include "JSFunction.h" #include "JSGlobalObject.h" +#include "LazyProperty.h" #include "PropertySlot.h" #include "PutPropertySlot.h" +#include "TypeError.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 - Intrinsic intrinsic; - intptr_t value1; - intptr_t value2; - }; - - // 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; - - 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; } +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&); +typedef JSValue (*LazyPropertyCallback)(VM&, JSObject*); +typedef DOMJIT::GetterSetter* (*DOMJITGetterSetterGenerator)(void); + +// 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; - Intrinsic intrinsic() const - { - ASSERT(m_attributes & Function); - return m_intrinsic; - } + unsigned attributes() const { return m_attributes; } - 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); } + 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); + if (m_attributes & DOMJITFunction) + return signature()->argumentCount; + return static_cast<unsigned char>(m_values.value2); + } - GetFunction propertyGetter() const { ASSERT(!(m_attributes & Function)); return m_u.property.get; } - PutFunction propertyPutter() const { ASSERT(!(m_attributes & Function)); return m_u.property.put; } + GetFunction propertyGetter() const { ASSERT(!(m_attributes & BuiltinOrFunctionOrAccessorOrLazyPropertyOrConstant)); return reinterpret_cast<GetFunction>(m_values.value1); } + PutFunction propertyPutter() const { ASSERT(!(m_attributes & BuiltinOrFunctionOrAccessorOrLazyPropertyOrConstant)); return reinterpret_cast<PutFunction>(m_values.value2); } - intptr_t lexerValue() const { ASSERT(!m_attributes); return m_u.lexer.value; } + DOMJIT::GetterSetter* domJIT() const { ASSERT(m_attributes & DOMJITAttribute); return reinterpret_cast<DOMJITGetterSetterGenerator>(m_values.value1)(); } + const DOMJIT::Signature* signature() const { ASSERT(m_attributes & DOMJITFunction); return reinterpret_cast<const DOMJIT::Signature*>(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; } + + ptrdiff_t lazyCellPropertyOffset() const { ASSERT(m_attributes & CellProperty); return m_values.value1; } + ptrdiff_t lazyClassStructureOffset() const { ASSERT(m_attributes & ClassStructure); return m_values.value1; } + LazyPropertyCallback lazyPropertyCallback() const { ASSERT(m_attributes & PropertyCallback); return reinterpret_cast<LazyPropertyCallback>(m_values.value1); } +}; - int compactSize; - int compactHashSizeMask; - bool hasSetterOrReadonlyProperties; +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, hasSetterOrReadonlyProperties, 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); - - if (!entry) // not found, forward to parent - return ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot); - - if (entry->attributes() & Function) - return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); + return ConstIterator(this, 0); + } + ConstIterator end() const + { + return ConstIterator(this, numberOfValues); + } +}; + +JS_EXPORT_PRIVATE bool setUpStaticFunctionSlot(VM&, 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); +} + +inline bool getStaticPropertySlotFromTable(VM& vm, const HashTable& table, JSObject* thisObject, PropertyName propertyName, PropertySlot& slot) +{ + if (thisObject->staticPropertiesReified()) + return false; + + auto* entry = table.entry(propertyName); + if (!entry) + return false; + + if (entry->attributes() & BuiltinOrFunctionOrAccessorOrLazyProperty) + return setUpStaticFunctionSlot(vm, entry, thisObject, propertyName, slot); + + if (entry->attributes() & ConstantInteger) { + slot.setValue(thisObject, attributesForStructure(entry->attributes()), jsNumber(entry->constantInteger())); + return true; + } - slot.setCacheableCustom(thisObj, entry->attributes(), entry->propertyGetter()); + if (entry->attributes() & DOMJITAttribute) { + DOMJIT::GetterSetter* domJIT = entry->domJIT(); + slot.setCacheableCustom(thisObject, attributesForStructure(entry->attributes()), domJIT->getter(), domJIT); 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)) + slot.setCacheableCustom(thisObject, attributesForStructure(entry->attributes()), entry->propertyGetter()); + return true; +} + +inline bool replaceStaticPropertySlot(VM& vm, JSObject* thisObject, PropertyName propertyName, JSValue value) +{ + if (!thisObject->putDirect(vm, propertyName, value)) + return false; + + if (!thisObject->staticPropertiesReified()) + thisObject->JSObject::setStructure(vm, Structure::attributeChangeTransition(vm, thisObject->structure(), propertyName, 0)); + + 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 bool putEntry(ExecState* exec, const HashTableValue* entry, JSObject* base, JSObject* thisValue, PropertyName propertyName, JSValue value, PutPropertySlot& slot) +{ + VM& vm = exec->vm(); + auto scope = DECLARE_THROW_SCOPE(vm); + + if (entry->attributes() & BuiltinOrFunctionOrLazyProperty) { + if (!(entry->attributes() & ReadOnly)) { + // If this is a function or lazy property put then we just do the put because + // logically the object already had the property, so this is just a replace. + if (JSObject* thisObject = jsDynamicCast<JSObject*>(vm, thisValue)) + thisObject->putDirect(vm, propertyName, value); return true; - - const HashEntry* entry = table.entry(exec, propertyName); - if (!entry) - return false; - - return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); + } + return typeError(exec, scope, slot.isStrictMode(), ASCIILiteral(ReadonlyPropertyWriteError)); } - /** - * 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); + if (entry->attributes() & Accessor) + return typeError(exec, scope, slot.isStrictMode(), ASCIILiteral(ReadonlyPropertyWriteError)); + + if (!(entry->attributes() & ReadOnly)) { + ASSERT_WITH_MESSAGE(!(entry->attributes() & DOMJITAttribute), "DOMJITAttribute supports readonly attributes currently."); + bool isAccessor = entry->attributes() & CustomAccessor; + JSValue updateThisValue = entry->attributes() & CustomAccessor ? slot.thisValue() : JSValue(base); + bool result = callCustomSetter(exec, entry->propertyPutter(), isAccessor, updateThisValue, value); + if (isAccessor) + slot.setCustomAccessor(base, entry->propertyPutter()); + else + slot.setCustomValue(base, entry->propertyPutter()); + return result; + } - if (!entry) // not found, forward to parent - return ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot); + return typeError(exec, scope, slot.isStrictMode(), ASCIILiteral(ReadonlyPropertyWriteError)); +} - ASSERT(!(entry->attributes() & Function)); +/** + * 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, bool& putResult) +{ + const HashTableValue* entry = table.entry(propertyName); + + if (!entry) + return false; + + putResult = putEntry(exec, entry, base, base, propertyName, value, slot); + return true; +} + +inline void reifyStaticProperty(VM& vm, const PropertyName& propertyName, const HashTableValue& value, JSObject& thisObj) +{ + 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; + } - slot.setCacheableCustom(thisObj, entry->attributes(), entry->propertyGetter()); - return true; + if (value.attributes() & Function) { + if (value.attributes() & DOMJITFunction) { + thisObj.putDirectNativeFunction( + vm, thisObj.globalObject(), propertyName, value.functionLength(), + value.function(), value.intrinsic(), value.signature(), attributesForStructure(value.attributes())); + return; + } + thisObj.putDirectNativeFunction( + vm, thisObj.globalObject(), propertyName, value.functionLength(), + value.function(), value.intrinsic(), attributesForStructure(value.attributes())); + return; } - inline void putEntry(ExecState* exec, const HashEntry* entry, JSObject* base, PropertyName propertyName, JSValue value, PutPropertySlot& slot) - { - // If this is a function put it as an override property. - if (entry->attributes() & Function) { - if (JSObject* thisObject = jsDynamicCast<JSObject*>(slot.thisValue())) - thisObject->putDirect(exec->vm(), propertyName, value); - } else if (!(entry->attributes() & ReadOnly)) { - entry->propertyPutter()(exec, base, JSValue::encode(slot.thisValue()), JSValue::encode(value)); - slot.setCustomProperty(base, entry->propertyPutter()); - } else if (slot.isStrictMode()) - throwTypeError(exec, StrictModeReadonlyPropertyWriteError); + if (value.attributes() & ConstantInteger) { + thisObj.putDirect(vm, propertyName, jsNumber(value.constantInteger()), 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. - */ - inline bool lookupPut(ExecState* exec, PropertyName propertyName, JSObject* base, JSValue value, const HashTable& table, PutPropertySlot& slot) - { - const HashEntry* entry = table.entry(exec, propertyName); + if (value.attributes() & Accessor) { + reifyStaticAccessor(vm, value, thisObj, propertyName); + return; + } + + if (value.attributes() & CellProperty) { + LazyCellProperty* property = bitwise_cast<LazyCellProperty*>( + bitwise_cast<char*>(&thisObj) + value.lazyCellPropertyOffset()); + JSCell* result = property->get(&thisObj); + thisObj.putDirect(vm, propertyName, result, attributesForStructure(value.attributes())); + return; + } + + if (value.attributes() & ClassStructure) { + LazyClassStructure* structure = bitwise_cast<LazyClassStructure*>( + bitwise_cast<char*>(&thisObj) + value.lazyClassStructureOffset()); + structure->get(jsCast<JSGlobalObject*>(&thisObj)); + return; + } + + if (value.attributes() & PropertyCallback) { + JSValue result = value.lazyPropertyCallback()(vm, &thisObj); + thisObj.putDirect(vm, propertyName, result, attributesForStructure(value.attributes())); + return; + } - if (!entry) - return false; + if (value.attributes() & DOMJITAttribute) { + DOMJIT::GetterSetter* domJIT = value.domJIT(); + CustomGetterSetter* customGetterSetter = CustomGetterSetter::create(vm, domJIT->getter(), domJIT->setter(), domJIT); + thisObj.putDirectCustomAccessor(vm, propertyName, customGetterSetter, attributesForStructure(value.attributes())); + return; + } - putEntry(exec, entry, base, propertyName, value, slot); - return true; + 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) { + if (!value.m_key) + continue; + auto key = Identifier::fromString(&vm, reinterpret_cast<const LChar*>(value.m_key), strlen(value.m_key)); + reifyStaticProperty(vm, key, value, thisObj); } -} // namespace JSC +} + +template<NativeFunction nativeFunction, int length> EncodedJSValue nonCachingStaticFunctionGetter(ExecState* state, EncodedJSValue, PropertyName propertyName) +{ + return JSValue::encode(JSFunction::create(state->vm(), state->lexicalGlobalObject(), length, propertyName.publicName(), nativeFunction)); +} -#endif // Lookup_h +} // namespace JSC |