X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/ba379fdc102753d6be2c4d937058fe40257329fe..2656c66b5b30d5597e842a751c7f19ad6c2fe31a:/runtime/Lookup.h diff --git a/runtime/Lookup.h b/runtime/Lookup.h index 167f2bc..909734b 100644 --- a/runtime/Lookup.h +++ b/runtime/Lookup.h @@ -21,148 +21,188 @@ #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 "JSObject.h" #include "PropertySlot.h" -#include +#include "PutPropertySlot.h" #include -// Bug #26843: Work around Metrowerks compiler bug -#if COMPILER(WINSCW) -#define JSC_CONST_HASHTABLE -#else -#define JSC_CONST_HASHTABLE const -#endif - 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; + 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 void (*PutFunction)(ExecState*, JSObject* baseObject, JSValue value); - - class HashEntry { - public: - void initialize(UString::Rep* key, unsigned char attributes, intptr_t v1, intptr_t v2) - { - m_key = key; - m_attributes = attributes; - m_u.store.value1 = v1; - m_u.store.value2 = v2; - m_next = 0; - } - - void setKey(UString::Rep* key) { m_key = key; } - UString::Rep* key() const { return m_key; } + typedef PutPropertySlot::PutValueFunc PutFunction; + typedef FunctionExecutable* (*BuiltinGenerator)(VM&); - unsigned char attributes() const { return m_attributes; } + // 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; + intptr_t m_value1; + intptr_t m_value2; - NativeFunction function() const { ASSERT(m_attributes & Function); return m_u.function.functionValue; } - unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast(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(m_value1); } + NativeFunction function() const { ASSERT(m_attributes & Function); return reinterpret_cast(m_value1); } + unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast(m_value2); } - intptr_t lexerValue() const { ASSERT(!m_attributes); return m_u.lexer.value; } + GetFunction propertyGetter() const { ASSERT(!(m_attributes & BuiltinOrFunctionOrConstant)); return reinterpret_cast(m_value1); } + PutFunction propertyPutter() const { ASSERT(!(m_attributes & BuiltinOrFunctionOrConstant)); return reinterpret_cast(m_value2); } - void setNext(HashEntry *next) { m_next = next; } - HashEntry* next() const { return m_next; } + intptr_t constantInteger() const { ASSERT(m_attributes & ConstantInteger); return m_value1; } - private: - UString::Rep* m_key; - unsigned char m_attributes; // JSObject attributes - - 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; + intptr_t lexerValue() const { ASSERT(!m_attributes); return m_value1; } }; struct HashTable { - - int compactSize; - int compactHashSizeMask; + mutable int numberOfValues; + int indexMask; + bool hasSetterOrReadonlyProperties; const HashTableValue* values; // Fixed values generated by script. - mutable const HashEntry* table; // Table allocated at runtime. + mutable const char** keys; // Table allocated at runtime. + const CompactHashIndex* index; + + ALWAYS_INLINE HashTable copy() const + { + // Don't copy dynamic table since it's thread specific. + HashTable result = { numberOfValues, indexMask, hasSetterOrReadonlyProperties, values, 0, index }; + return result; + } - ALWAYS_INLINE void initializeIfNeeded(JSGlobalData* globalData) const + ALWAYS_INLINE void initializeIfNeeded(VM& vm) const { - if (!table) - createTable(globalData); + if (!keys) + createTable(vm); } ALWAYS_INLINE void initializeIfNeeded(ExecState* exec) const { - if (!table) - createTable(&exec->globalData()); + if (!keys) + createTable(exec->vm()); } - void deleteTable() const; + JS_EXPORT_PRIVATE void deleteTable() const; // Find an entry in the table, and return the entry. - ALWAYS_INLINE const HashEntry* entry(JSGlobalData* globalData, const Identifier& identifier) const + ALWAYS_INLINE const HashTableValue* entry(VM& vm, PropertyName identifier) const { - initializeIfNeeded(globalData); + initializeIfNeeded(vm); return entry(identifier); } - ALWAYS_INLINE const HashEntry* entry(ExecState* exec, const Identifier& identifier) const + ALWAYS_INLINE const HashTableValue* entry(ExecState* exec, PropertyName identifier) const { initializeIfNeeded(exec); return entry(identifier); } - private: - ALWAYS_INLINE const HashEntry* entry(const Identifier& identifier) const + class ConstIterator { + public: + ConstIterator(const HashTable* table, int position) + : m_table(table) + , m_position(position) + { + skipInvalidKeys(); + } + + const HashTableValue* value() + { + return &m_table->values[m_position]; + } + + const char* key() + { + return m_table->keys[m_position]; + } + + const HashTableValue* operator->() + { + return value(); + } + + bool operator!=(const ConstIterator& other) + { + 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->keys[m_position]) + ++m_position; + ASSERT(m_position <= m_table->numberOfValues); + } + + const HashTable* m_table; + int m_position; + }; + + ConstIterator begin(VM& vm) const + { + initializeIfNeeded(vm); + return ConstIterator(this, 0); + } + ConstIterator end(VM& vm) const { - ASSERT(table); + initializeIfNeeded(vm); + return ConstIterator(this, numberOfValues); + } - const HashEntry* entry = &table[identifier.ustring().rep()->computedHash() & compactHashSizeMask]; + private: + ALWAYS_INLINE const HashTableValue* entry(PropertyName propertyName) const + { + StringImpl* impl = propertyName.uid(); + if (!impl) + return 0; + + ASSERT(keys); - if (!entry->key()) + int indexEntry = impl->existingHash() & indexMask; + int valueIndex = index[indexEntry].value; + if (valueIndex == -1) return 0; - do { - if (entry->key() == identifier.ustring().rep()) - return entry; - entry = entry->next(); - } while (entry); + while (true) { + if (WTF::equal(impl, keys[valueIndex])) + return &values[valueIndex]; - return 0; + indexEntry = index[indexEntry].next; + if (indexEntry == -1) + return nullptr; + valueIndex = index[indexEntry].value; + ASSERT(valueIndex != -1); + }; } // Convert the hash table keys to identifiers. - void createTable(JSGlobalData*) const; + JS_EXPORT_PRIVATE void createTable(VM&) const; }; - void setUpStaticFunctionSlot(ExecState*, const HashEntry*, JSObject* thisObject, const Identifier& propertyName, PropertySlot&); + JS_EXPORT_PRIVATE bool setUpStaticFunctionSlot(ExecState*, const HashTableValue*, JSObject* thisObject, PropertyName, PropertySlot&); /** * This method does it all (looking in the hashtable, checking for function @@ -171,18 +211,22 @@ namespace JSC { * unknown property). */ template - inline bool getStaticPropertySlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot) + inline bool getStaticPropertySlot(ExecState* exec, const HashTable& table, ThisImp* thisObj, PropertyName propertyName, PropertySlot& slot) { - const HashEntry* entry = table->entry(exec, propertyName); + const HashTableValue* entry = table.entry(exec, propertyName); if (!entry) // not found, forward to parent - return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot); + return ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot); - if (entry->attributes() & Function) - setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); - else - slot.setCustom(thisObj, entry->propertyGetter()); + if (entry->attributes() & BuiltinOrFunction) + return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); + if (entry->attributes() & ConstantInteger) { + slot.setValue(thisObj, entry->attributes(), jsNumber(entry->constantInteger())); + return true; + } + + slot.setCacheableCustom(thisObj, entry->attributes(), entry->propertyGetter()); return true; } @@ -192,17 +236,16 @@ namespace JSC { * a dummy getValueProperty. */ template - inline bool getStaticFunctionSlot(ExecState* exec, const HashTable* table, JSObject* thisObj, const Identifier& propertyName, PropertySlot& slot) + inline bool getStaticFunctionSlot(ExecState* exec, const HashTable& table, JSObject* thisObj, PropertyName propertyName, PropertySlot& slot) { - if (static_cast(thisObj)->ParentImp::getOwnPropertySlot(exec, propertyName, slot)) + if (ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot)) return true; - const HashEntry* entry = table->entry(exec, propertyName); + const HashTableValue* entry = table.entry(exec, propertyName); if (!entry) return false; - setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); - return true; + return setUpStaticFunctionSlot(exec, entry, thisObj, propertyName, slot); } /** @@ -210,54 +253,86 @@ namespace JSC { * Using this instead of getStaticPropertySlot removes the need for a FuncImp class. */ template - inline bool getStaticValueSlot(ExecState* exec, const HashTable* table, ThisImp* thisObj, const Identifier& propertyName, PropertySlot& slot) + inline bool getStaticValueSlot(ExecState* exec, const HashTable& table, ThisImp* thisObj, PropertyName propertyName, PropertySlot& slot) { - const HashEntry* entry = table->entry(exec, propertyName); + const HashTableValue* entry = table.entry(exec, propertyName); if (!entry) // not found, forward to parent - return thisObj->ParentImp::getOwnPropertySlot(exec, propertyName, slot); + return ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot); + + ASSERT(!(entry->attributes() & BuiltinOrFunction)); - ASSERT(!(entry->attributes() & Function)); + if (entry->attributes() & ConstantInteger) { + slot.setValue(thisObj, entry->attributes(), jsNumber(entry->constantInteger())); + return true; + } - slot.setCustom(thisObj, entry->propertyGetter()); + slot.setCacheableCustom(thisObj, entry->attributes(), entry->propertyGetter()); return true; } + inline void putEntry(ExecState* exec, const HashTableValue* entry, JSObject* base, PropertyName propertyName, JSValue value, PutPropertySlot& slot) + { + // If this is a function put it as an override property. + if (entry->attributes() & BuiltinOrFunction) { + if (JSObject* thisObject = jsDynamicCast(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); + } + /** * 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 - inline bool lookupPut(ExecState* exec, const Identifier& propertyName, JSValue value, const HashTable* table, ThisImp* thisObj) + inline bool lookupPut(ExecState* exec, PropertyName propertyName, JSObject* base, JSValue value, const HashTable& table, PutPropertySlot& slot) { - const HashEntry* entry = table->entry(exec, propertyName); + const HashTableValue* entry = table.entry(exec, propertyName); if (!entry) return false; - if (entry->attributes() & Function) { // function: put as override property - if (LIKELY(value.isCell())) - thisObj->putDirectFunction(propertyName, value.asCell()); - else - thisObj->putDirect(propertyName, value); - } else if (!(entry->attributes() & ReadOnly)) - entry->propertyPutter()(exec, thisObj, value); - + putEntry(exec, entry, base, propertyName, value, slot); return true; } - /** - * This one is for "put". - * It calls lookupPut() 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 - inline void lookupPut(ExecState* exec, const Identifier& propertyName, JSValue value, const HashTable* table, ThisImp* thisObj, PutPropertySlot& slot) + template + inline void reifyStaticProperties(VM& vm, const HashTableValue (&values)[numberOfValues], JSObject& thisObj) { - if (!lookupPut(exec, propertyName, value, table, thisObj)) - thisObj->ParentImp::put(exec, propertyName, value, slot); // not found: forward to parent + BatchedTransitionOptimizer transitionOptimizer(vm, &thisObj); + for (auto& value : values) { + if (!value.m_key) + continue; + + Identifier propertyName(&vm, reinterpret_cast(value.m_key), strlen(value.m_key)); + if (value.attributes() & Builtin) { + thisObj.putDirectBuiltinFunction(vm, thisObj.globalObject(), propertyName, value.builtinGenerator()(vm), value.attributes()); + continue; + } + + if (value.attributes() & Function) { + thisObj.putDirectNativeFunction(vm, thisObj.globalObject(), propertyName, value.functionLength(), + value.function(), value.intrinsic(), value.attributes()); + continue; + } + + if (value.attributes() & ConstantInteger) { + thisObj.putDirect(vm, propertyName, jsNumber(value.constantInteger()), value.attributes()); + continue; + } + + if (value.attributes() & Accessor) { + RELEASE_ASSERT_NOT_REACHED(); + continue; + } + + CustomGetterSetter* customGetterSetter = CustomGetterSetter::create(vm, value.propertyGetter(), value.propertyPutter()); + thisObj.putDirectCustomAccessor(vm, propertyName, customGetterSetter, value.attributes()); + } } } // namespace JSC