#ifndef EvalCodeCache_h
#define EvalCodeCache_h
#include "Executable.h"
#include "JSGlobalObject.h"
#include "JSScope.h"
#include "Options.h"
#include "SourceCode.h"
#include "SourceCodeKey.h"
#include <wtf/HashMap.h>
#include <wtf/RefPtr.h>
#include <wtf/text/StringHash.h>
namespace JSC {
class SlotVisitor;
class EvalCodeCache {
public:
class CacheKey {
public:
CacheKey(const String& source, DerivedContextType derivedContextType, EvalContextType evalContextType, bool isArrowFunctionContext)
: m_source(source.impl())
, m_flags(SourceCodeType::EvalType, JSParserBuiltinMode::NotBuiltin, JSParserStrictMode::NotStrict, derivedContextType, evalContextType, isArrowFunctionContext)
{
}
CacheKey(WTF::HashTableDeletedValueType)
: m_source(WTF::HashTableDeletedValue)
{
}
CacheKey() = default;
unsigned hash() const { return m_source->hash(); }
bool isEmptyValue() const { return !m_source; }
bool operator==(const CacheKey& other) const
{
return m_source == other.m_source && m_flags == other.m_flags;
}
bool isHashTableDeletedValue() const { return m_source.isHashTableDeletedValue(); }
struct Hash {
static unsigned hash(const CacheKey& key)
{
return key.hash();
}
static bool equal(const CacheKey& lhs, const CacheKey& rhs)
{
return StringHash::equal(lhs.m_source, rhs.m_source) && lhs.m_flags == rhs.m_flags;
}
static const bool safeToCompareToEmptyOrDeleted = false;
};
typedef SimpleClassHashTraits<CacheKey> HashTraits;
private:
RefPtr<StringImpl> m_source;
SourceCodeFlags m_flags;
};
EvalExecutable* tryGet(bool inStrictContext, const String& evalSource, DerivedContextType derivedContextType, EvalContextType evalContextType, bool isArrowFunctionContext, JSScope* scope)
{
if (isCacheable(inStrictContext, evalSource, scope)) {
ASSERT(!inStrictContext);
return m_cacheMap.fastGet(CacheKey(evalSource, derivedContextType, evalContextType, isArrowFunctionContext)).get();
}
return nullptr;
}
EvalExecutable* getSlow(ExecState* exec, JSCell* owner, bool inStrictContext, DerivedContextType derivedContextType, EvalContextType evalContextType, bool isArrowFunctionContext, const String& evalSource, JSScope* scope)
{
VariableEnvironment variablesUnderTDZ;
JSScope::collectVariablesUnderTDZ(scope, variablesUnderTDZ);
EvalExecutable* evalExecutable = EvalExecutable::create(exec, makeSource(evalSource), inStrictContext, derivedContextType, isArrowFunctionContext, evalContextType, &variablesUnderTDZ);
if (!evalExecutable)
return nullptr;
if (isCacheable(inStrictContext, evalSource, scope) && m_cacheMap.size() < maxCacheEntries) {
ASSERT(!inStrictContext);
m_cacheMap.set(CacheKey(evalSource, derivedContextType, evalContextType, isArrowFunctionContext), WriteBarrier<EvalExecutable>(exec->vm(), owner, evalExecutable));
}
return evalExecutable;
}
bool isEmpty() const { return m_cacheMap.isEmpty(); }
void visitAggregate(SlotVisitor&);
void clear()
{
m_cacheMap.clear();
}
private:
ALWAYS_INLINE bool isCacheableScope(JSScope* scope)
{
return scope->isGlobalLexicalEnvironment() || scope->isFunctionNameScopeObject() || scope->isVarScope();
}
ALWAYS_INLINE bool isCacheable(bool inStrictContext, const String& evalSource, JSScope* scope)
{
return !inStrictContext
&& static_cast<size_t>(evalSource.length()) < Options::maximumEvalCacheableSourceLength()
&& isCacheableScope(scope);
}
static const int maxCacheEntries = 64;
typedef HashMap<CacheKey, WriteBarrier<EvalExecutable>, CacheKey::Hash, CacheKey::HashTraits> EvalCacheMap;
EvalCacheMap m_cacheMap;
};
}
#endif // EvalCodeCache_h