2 * Copyright (C) 2012, 2013 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 #include "CodeSpecializationKind.h"
30 #include "ParserModes.h"
31 #include "SourceCode.h"
33 #include "WeakRandom.h"
34 #include <wtf/CurrentTime.h>
35 #include <wtf/Forward.h>
36 #include <wtf/PassOwnPtr.h>
37 #include <wtf/RandomNumber.h>
38 #include <wtf/text/WTFString.h>
43 class FunctionBodyNode
;
46 class ProgramExecutable
;
47 class UnlinkedCodeBlock
;
48 class UnlinkedEvalCodeBlock
;
49 class UnlinkedFunctionCodeBlock
;
50 class UnlinkedFunctionExecutable
;
51 class UnlinkedProgramCodeBlock
;
59 enum CodeType
{ EvalType
, ProgramType
, FunctionType
};
65 SourceCodeKey(const SourceCode
& sourceCode
, const String
& name
, CodeType codeType
, JSParserStrictness jsParserStrictness
)
66 : m_sourceCode(sourceCode
)
68 , m_flags((codeType
<< 2) | jsParserStrictness
)
69 , m_hash(string().impl()->hash())
73 SourceCodeKey(WTF::HashTableDeletedValueType
)
74 : m_sourceCode(WTF::HashTableDeletedValue
)
78 bool isHashTableDeletedValue() const { return m_sourceCode
.isHashTableDeletedValue(); }
80 unsigned hash() const { return m_hash
; }
82 size_t length() const { return m_sourceCode
.length(); }
84 bool isNull() const { return m_sourceCode
.isNull(); }
86 // To save memory, we compute our string on demand. It's expected that source
87 // providers cache their strings to make this efficient.
88 String
string() const { return m_sourceCode
.toString(); }
90 bool operator==(const SourceCodeKey
& other
) const
92 return m_hash
== other
.m_hash
93 && length() == other
.length()
94 && m_flags
== other
.m_flags
95 && m_name
== other
.m_name
96 && string() == other
.string();
100 SourceCode m_sourceCode
;
106 struct SourceCodeKeyHash
{
107 static unsigned hash(const SourceCodeKey
& key
) { return key
.hash(); }
108 static bool equal(const SourceCodeKey
& a
, const SourceCodeKey
& b
) { return a
== b
; }
109 static const bool safeToCompareToEmptyOrDeleted
= false;
112 struct SourceCodeKeyHashTraits
: SimpleClassHashTraits
<SourceCodeKey
> {
113 static const bool hasIsEmptyValueFunction
= true;
114 static bool isEmptyValue(const SourceCodeKey
& sourceCodeKey
) { return sourceCodeKey
.isNull(); }
117 struct SourceCodeValue
{
122 SourceCodeValue(VM
& vm
, JSCell
* cell
, int64_t age
)
134 typedef HashMap
<SourceCodeKey
, SourceCodeValue
, SourceCodeKeyHash
, SourceCodeKeyHashTraits
> MapType
;
135 typedef MapType::iterator iterator
;
136 typedef MapType::AddResult AddResult
;
140 , m_sizeAtLastPrune(0)
141 , m_timeAtLastPrune(monotonicallyIncreasingTime())
148 AddResult
add(const SourceCodeKey
& key
, const SourceCodeValue
& value
)
152 AddResult addResult
= m_map
.add(key
, value
);
153 if (addResult
.isNewEntry
) {
154 m_size
+= key
.length();
155 m_age
+= key
.length();
159 int64_t age
= m_age
- addResult
.iterator
->value
.age
;
160 if (age
> m_capacity
) {
161 // A requested object is older than the cache's capacity. We can
162 // infer that requested objects are subject to high eviction probability,
163 // so we grow the cache to improve our hit rate.
164 m_capacity
+= recencyBias
* oldObjectSamplingMultiplier
* key
.length();
165 } else if (age
< m_capacity
/ 2) {
166 // A requested object is much younger than the cache's capacity. We can
167 // infer that requested objects are subject to low eviction probability,
168 // so we shrink the cache to save memory.
169 m_capacity
-= recencyBias
* key
.length();
170 if (m_capacity
< m_minCapacity
)
171 m_capacity
= m_minCapacity
;
174 addResult
.iterator
->value
.age
= m_age
;
175 m_age
+= key
.length();
179 void remove(iterator it
)
181 m_size
-= it
->key
.length();
192 int64_t age() { return m_age
; }
195 // This constant factor biases cache capacity toward allowing a minimum
196 // working set to enter the cache before it starts evicting.
197 static const double workingSetTime
;
198 static const int64_t workingSetMaxBytes
= 16000000;
199 static const size_t workingSetMaxEntries
= 2000;
201 // This constant factor biases cache capacity toward recent activity. We
202 // want to adapt to changing workloads.
203 static const int64_t recencyBias
= 4;
205 // This constant factor treats a sampled event for one old object as if it
206 // happened for many old objects. Most old objects are evicted before we can
207 // sample them, so we need to extrapolate from the ones we do sample.
208 static const int64_t oldObjectSamplingMultiplier
= 32;
210 size_t numberOfEntries() const { return static_cast<size_t>(m_map
.size()); }
211 bool canPruneQuickly() const { return numberOfEntries() < workingSetMaxEntries
; }
213 void pruneSlowCase();
216 if (m_size
<= m_capacity
&& canPruneQuickly())
219 if (monotonicallyIncreasingTime() - m_timeAtLastPrune
< workingSetTime
220 && m_size
- m_sizeAtLastPrune
< workingSetMaxBytes
221 && canPruneQuickly())
229 int64_t m_sizeAtLastPrune
;
230 double m_timeAtLastPrune
;
231 int64_t m_minCapacity
;
236 // Caches top-level code such as <script>, eval(), new Function, and JSEvaluateScript().
238 WTF_MAKE_FAST_ALLOCATED
;
240 static PassOwnPtr
<CodeCache
> create() { return adoptPtr(new CodeCache
); }
242 UnlinkedProgramCodeBlock
* getProgramCodeBlock(VM
&, ProgramExecutable
*, const SourceCode
&, JSParserStrictness
, DebuggerMode
, ProfilerMode
, ParserError
&);
243 UnlinkedEvalCodeBlock
* getEvalCodeBlock(VM
&, EvalExecutable
*, const SourceCode
&, JSParserStrictness
, DebuggerMode
, ProfilerMode
, ParserError
&);
244 UnlinkedFunctionExecutable
* getFunctionExecutableFromGlobalCode(VM
&, const Identifier
&, const SourceCode
&, ParserError
&);
249 m_sourceCode
.clear();
255 template <class UnlinkedCodeBlockType
, class ExecutableType
>
256 UnlinkedCodeBlockType
* getGlobalCodeBlock(VM
&, ExecutableType
*, const SourceCode
&, JSParserStrictness
, DebuggerMode
, ProfilerMode
, ParserError
&);
258 CodeCacheMap m_sourceCode
;
263 #endif // CodeCache_h