#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 "LazyProperty.h"
#include "PropertySlot.h"
#include "PutPropertySlot.h"
#include "Reject.h"
#include <wtf/Assertions.h>
namespace JSC {
struct CompactHashIndex {
const int16_t value;
const int16_t next;
};
typedef PropertySlot::GetValueFunc GetFunction;
typedef PutPropertySlot::PutValueFunc PutFunction;
typedef FunctionExecutable* (*BuiltinGenerator)(VM&);
typedef JSValue (*LazyPropertyCallback)(VM&, JSObject*);
struct HashTableValue {
const char* m_key; unsigned m_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<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); }
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); }
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;
long long constantInteger() const { ASSERT(m_attributes & ConstantInteger); return m_values.constant; }
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); }
};
struct HashTable {
int numberOfValues;
int indexMask;
bool hasSetterOrReadonlyProperties;
const HashTableValue* values; const CompactHashIndex* index;
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(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(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;
}
inline bool putEntry(ExecState* exec, const HashTableValue* entry, JSObject* base, JSObject* thisValue, PropertyName propertyName, JSValue value, PutPropertySlot& slot)
{
if (entry->attributes() & BuiltinOrFunctionOrLazyProperty) {
if (!(entry->attributes() & ReadOnly)) {
if (JSObject* thisObject = jsDynamicCast<JSObject*>(thisValue))
thisObject->putDirect(exec->vm(), propertyName, value);
return true;
}
return reject(exec, slot.isStrictMode(), StrictModeReadonlyPropertyWriteError);
}
if (entry->attributes() & Accessor)
return reject(exec, slot.isStrictMode(), StrictModeReadonlyPropertyWriteError);
if (!(entry->attributes() & ReadOnly)) {
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;
}
return reject(exec, slot.isStrictMode(), StrictModeReadonlyPropertyWriteError);
}
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;
}
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;
}
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;
}
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);
}
}
}
#endif // Lookup_h