/* * Copyright (C) 2012, 2013 Apple Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef CodeCache_h #define CodeCache_h #include "CodeSpecializationKind.h" #include "ParserModes.h" #include "SourceCode.h" #include "Strong.h" #include "WeakRandom.h" #include #include #include #include #include namespace JSC { class EvalExecutable; class FunctionBodyNode; class Identifier; class JSScope; class ProgramExecutable; class UnlinkedCodeBlock; class UnlinkedEvalCodeBlock; class UnlinkedFunctionCodeBlock; class UnlinkedFunctionExecutable; class UnlinkedProgramCodeBlock; class VM; struct ParserError; class SourceCode; class SourceProvider; class SourceCodeKey { public: enum CodeType { EvalType, ProgramType, FunctionType }; SourceCodeKey() { } SourceCodeKey(const SourceCode& sourceCode, const String& name, CodeType codeType, JSParserStrictness jsParserStrictness) : m_sourceCode(sourceCode) , m_name(name) , m_flags((codeType hash()) { } SourceCodeKey(WTF::HashTableDeletedValueType) : m_sourceCode(WTF::HashTableDeletedValue) { } bool isHashTableDeletedValue() const { return m_sourceCode.isHashTableDeletedValue(); } unsigned hash() const { return m_hash; } size_t length() const { return m_sourceCode.length(); } bool isNull() const { return m_sourceCode.isNull(); } // To save memory, we compute our string on demand. It's expected that source // providers cache their strings to make this efficient. String string() const { return m_sourceCode.toString(); } bool operator==(const SourceCodeKey& other) const { return m_hash == other.m_hash && length() == other.length() && m_flags == other.m_flags && m_name == other.m_name && string() == other.string(); } private: SourceCode m_sourceCode; String m_name; unsigned m_flags; unsigned m_hash; }; struct SourceCodeKeyHash { static unsigned hash(const SourceCodeKey& key) { return key.hash(); } static bool equal(const SourceCodeKey& a, const SourceCodeKey& b) { return a == b; } static const bool safeToCompareToEmptyOrDeleted = false; }; struct SourceCodeKeyHashTraits : SimpleClassHashTraits { static const bool hasIsEmptyValueFunction = true; static bool isEmptyValue(const SourceCodeKey& sourceCodeKey) { return sourceCodeKey.isNull(); } }; struct SourceCodeValue { SourceCodeValue() { } SourceCodeValue(VM& vm, JSCell* cell, int64_t age) : cell(vm, cell) , age(age) { } Strong cell; int64_t age; }; class CodeCacheMap { public: typedef HashMap MapType; typedef MapType::iterator iterator; typedef MapType::AddResult AddResult; CodeCacheMap() : m_size(0) , m_sizeAtLastPrune(0) , m_timeAtLastPrune(monotonicallyIncreasingTime()) , m_minCapacity(0) , m_capacity(0) , m_age(0) { } AddResult add(const SourceCodeKey& key, const SourceCodeValue& value) { prune(); AddResult addResult = m_map.add(key, value); if (addResult.isNewEntry) { m_size += key.length(); m_age += key.length(); return addResult; } int64_t age = m_age - addResult.iterator->value.age; if (age > m_capacity) { // A requested object is older than the cache's capacity. We can // infer that requested objects are subject to high eviction probability, // so we grow the cache to improve our hit rate. m_capacity += recencyBias * oldObjectSamplingMultiplier * key.length(); } else if (age value.age = m_age; m_age += key.length(); return addResult; } void remove(iterator it) { m_size -= it->key.length(); m_map.remove(it); } void clear() { m_size = 0; m_age = 0; m_map.clear(); } int64_t age() { return m_age; } private: // This constant factor biases cache capacity toward allowing a minimum // working set to enter the cache before it starts evicting. static const double workingSetTime; static const int64_t workingSetMaxBytes = 16000000; static const size_t workingSetMaxEntries = 2000; // This constant factor biases cache capacity toward recent activity. We // want to adapt to changing workloads. static const int64_t recencyBias = 4; // This constant factor treats a sampled event for one old object as if it // happened for many old objects. Most old objects are evicted before we can // sample them, so we need to extrapolate from the ones we do sample. static const int64_t oldObjectSamplingMultiplier = 32; size_t numberOfEntries() const { return static_cast(m_map.size()); } bool canPruneQuickly() const { return numberOfEntries() , eval(), new Function, and JSEvaluateScript(). class CodeCache { public: static PassOwnPtr create() { return adoptPtr(new CodeCache); } UnlinkedProgramCodeBlock* getProgramCodeBlock(VM&, ProgramExecutable*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&); UnlinkedEvalCodeBlock* getEvalCodeBlock(VM&, EvalExecutable*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&); UnlinkedFunctionExecutable* getFunctionExecutableFromGlobalCode(VM&, const Identifier&, const SourceCode&, ParserError&); ~CodeCache(); void clear() { m_sourceCode.clear(); } private: CodeCache(); template UnlinkedCodeBlockType* getGlobalCodeBlock(VM&, ExecutableType*, const SourceCode&, JSParserStrictness, DebuggerMode, ProfilerMode, ParserError&); CodeCacheMap m_sourceCode; }; } #endif // CodeCache_h