blob: b7ecbb190a04b509d90410ffc4cc32dd039480fa [file] [log] [blame]
/*
* 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 <wtf/Assertions.h>
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;
intptr_t m_value1;
intptr_t m_value2;
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_value1); }
NativeFunction function() const { ASSERT(m_attributes & Function); return reinterpret_cast<NativeFunction>(m_value1); }
unsigned char functionLength() const { ASSERT(m_attributes & Function); return static_cast<unsigned char>(m_value2); }
GetFunction propertyGetter() const { ASSERT(!(m_attributes & BuiltinOrFunctionOrConstant)); return reinterpret_cast<GetFunction>(m_value1); }
PutFunction propertyPutter() const { ASSERT(!(m_attributes & BuiltinOrFunctionOrConstant)); return reinterpret_cast<PutFunction>(m_value2); }
intptr_t constantInteger() const { ASSERT(m_attributes & ConstantInteger); return m_value1; }
intptr_t lexerValue() const { ASSERT(!m_attributes); return m_value1; }
};
struct HashTable {
mutable int numberOfValues;
int indexMask;
bool hasSetterOrReadonlyProperties;
const HashTableValue* values; // Fixed values generated by script.
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() const
{
if (!keys)
createTable();
}
JS_EXPORT_PRIVATE void deleteTable() const;
// Find an entry in the table, and return the entry.
ALWAYS_INLINE const HashTableValue* entry(PropertyName propertyName) const
{
initializeIfNeeded();
StringImpl* impl = propertyName.uid();
if (!impl)
return 0;
ASSERT(keys);
int indexEntry = impl->existingHash() & indexMask;
int valueIndex = index[indexEntry].value;
if (valueIndex == -1)
return 0;
while (true) {
if (WTF::equal(impl, keys[valueIndex]))
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()
{
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() const
{
initializeIfNeeded();
return ConstIterator(this, 0);
}
ConstIterator end() const
{
initializeIfNeeded();
return ConstIterator(this, numberOfValues);
}
private:
// Convert the hash table keys to identifiers.
JS_EXPORT_PRIVATE void createTable() const;
};
JS_EXPORT_PRIVATE bool setUpStaticFunctionSlot(ExecState*, const HashTableValue*, 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)
{
const HashTableValue* entry = table.entry(propertyName);
if (!entry) // not found, forward to parent
return ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot);
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;
}
/**
* 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;
const HashTableValue* 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 <class ThisImp, class ParentImp>
inline bool getStaticValueSlot(ExecState* exec, const HashTable& table, ThisImp* thisObj, PropertyName propertyName, PropertySlot& slot)
{
const HashTableValue* entry = table.entry(propertyName);
if (!entry) // not found, forward to parent
return ParentImp::getOwnPropertySlot(thisObj, exec, propertyName, slot);
ASSERT(!(entry->attributes() & BuiltinOrFunction));
if (entry->attributes() & ConstantInteger) {
slot.setValue(thisObj, entry->attributes(), jsNumber(entry->constantInteger()));
return true;
}
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<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);
}
/**
* 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, propertyName, value, slot);
return true;
}
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;
Identifier propertyName(&vm, reinterpret_cast<const LChar*>(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
#endif // Lookup_h