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/FixedArray.h>
36 #include <wtf/Forward.h>
37 #include <wtf/PassOwnPtr.h>
38 #include <wtf/RandomNumber.h>
39 #include <wtf/text/WTFString.h>
44 class FunctionBodyNode
;
47 class ProgramExecutable
;
48 class UnlinkedCodeBlock
;
49 class UnlinkedEvalCodeBlock
;
50 class UnlinkedFunctionCodeBlock
;
51 class UnlinkedFunctionExecutable
;
52 class UnlinkedProgramCodeBlock
;
60 enum CodeType
{ EvalType
, ProgramType
, FunctionType
};
66 SourceCodeKey(const SourceCode
& sourceCode
, const String
& name
, CodeType codeType
, JSParserStrictness jsParserStrictness
)
67 : m_sourceCode(sourceCode
)
69 , m_flags((codeType
<< 1) | jsParserStrictness
)
70 , m_hash(string().impl()->hash())
74 SourceCodeKey(WTF::HashTableDeletedValueType
)
75 : m_sourceCode(WTF::HashTableDeletedValue
)
79 bool isHashTableDeletedValue() const { return m_sourceCode
.isHashTableDeletedValue(); }
81 unsigned hash() const { return m_hash
; }
83 size_t length() const { return m_sourceCode
.length(); }
85 bool isNull() const { return m_sourceCode
.isNull(); }
87 // To save memory, we compute our string on demand. It's expected that source
88 // providers cache their strings to make this efficient.
89 String
string() const { return m_sourceCode
.toString(); }
91 bool operator==(const SourceCodeKey
& other
) const
93 return m_hash
== other
.m_hash
94 && length() == other
.length()
95 && m_flags
== other
.m_flags
96 && m_name
== other
.m_name
97 && string() == other
.string();
101 SourceCode m_sourceCode
;
107 struct SourceCodeKeyHash
{
108 static unsigned hash(const SourceCodeKey
& key
) { return key
.hash(); }
109 static bool equal(const SourceCodeKey
& a
, const SourceCodeKey
& b
) { return a
== b
; }
110 static const bool safeToCompareToEmptyOrDeleted
= false;
113 struct SourceCodeKeyHashTraits
: SimpleClassHashTraits
<SourceCodeKey
> {
114 static const bool hasIsEmptyValueFunction
= true;
115 static bool isEmptyValue(const SourceCodeKey
& sourceCodeKey
) { return sourceCodeKey
.isNull(); }
118 struct SourceCodeValue
{
123 SourceCodeValue(VM
& vm
, JSCell
* cell
, int64_t age
)
135 typedef HashMap
<SourceCodeKey
, SourceCodeValue
, SourceCodeKeyHash
, SourceCodeKeyHashTraits
> MapType
;
136 typedef MapType::iterator iterator
;
137 typedef MapType::AddResult AddResult
;
139 CodeCacheMap(int64_t workingSetMaxBytes
, size_t workingSetMaxEntries
)
141 , m_sizeAtLastPrune(0)
142 , m_timeAtLastPrune(monotonicallyIncreasingTime())
146 , m_workingSetMaxBytes(workingSetMaxBytes
)
147 , m_workingSetMaxEntries(workingSetMaxEntries
)
151 AddResult
add(const SourceCodeKey
& key
, const SourceCodeValue
& value
)
155 AddResult addResult
= m_map
.add(key
, value
);
156 if (addResult
.isNewEntry
) {
157 m_size
+= key
.length();
158 m_age
+= key
.length();
162 int64_t age
= m_age
- addResult
.iterator
->value
.age
;
163 if (age
> m_capacity
) {
164 // A requested object is older than the cache's capacity. We can
165 // infer that requested objects are subject to high eviction probability,
166 // so we grow the cache to improve our hit rate.
167 m_capacity
+= recencyBias
* oldObjectSamplingMultiplier
* key
.length();
168 } else if (age
< m_capacity
/ 2) {
169 // A requested object is much younger than the cache's capacity. We can
170 // infer that requested objects are subject to low eviction probability,
171 // so we shrink the cache to save memory.
172 m_capacity
-= recencyBias
* key
.length();
173 if (m_capacity
< m_minCapacity
)
174 m_capacity
= m_minCapacity
;
177 addResult
.iterator
->value
.age
= m_age
;
178 m_age
+= key
.length();
182 void remove(iterator it
)
184 m_size
-= it
->key
.length();
195 int64_t age() { return m_age
; }
197 static const int64_t globalWorkingSetMaxBytes
;
198 static const size_t globalWorkingSetMaxEntries
;
200 // We have a smaller cap for the per-codeblock CodeCache that approximates the
201 // linked EvalCodeCache limits, but still allows us to keep large string based
202 // evals at least partially cached.
203 static const unsigned nonGlobalWorkingSetScale
;
204 static const int64_t nonGlobalWorkingSetMaxBytes
;
205 static const size_t nonGlobalWorkingSetMaxEntries
;
208 // This constant factor biases cache capacity toward allowing a minimum
209 // working set to enter the cache before it starts evicting.
210 static const double workingSetTime
;
212 // This constant factor biases cache capacity toward recent activity. We
213 // want to adapt to changing workloads.
214 static const int64_t recencyBias
= 4;
216 // This constant factor treats a sampled event for one old object as if it
217 // happened for many old objects. Most old objects are evicted before we can
218 // sample them, so we need to extrapolate from the ones we do sample.
219 static const int64_t oldObjectSamplingMultiplier
= 32;
221 size_t numberOfEntries() const { return static_cast<size_t>(m_map
.size()); }
222 bool canPruneQuickly() const { return numberOfEntries() < m_workingSetMaxEntries
; }
224 void pruneSlowCase();
227 if (m_size
<= m_capacity
&& canPruneQuickly())
230 if (monotonicallyIncreasingTime() - m_timeAtLastPrune
< workingSetTime
231 && m_size
- m_sizeAtLastPrune
< m_workingSetMaxBytes
232 && canPruneQuickly())
240 int64_t m_sizeAtLastPrune
;
241 double m_timeAtLastPrune
;
242 int64_t m_minCapacity
;
245 const int64_t m_workingSetMaxBytes
;
246 const size_t m_workingSetMaxEntries
;
249 // Caches top-level code such as <script>, eval(), new Function, and JSEvaluateScript().
250 class CodeCache
: public RefCounted
<CodeCache
> {
252 enum CodeCacheKind
{ GlobalCodeCache
, NonGlobalCodeCache
};
253 static PassRefPtr
<CodeCache
> create(CodeCacheKind kind
) { return adoptRef(new CodeCache(kind
)); }
255 UnlinkedProgramCodeBlock
* getProgramCodeBlock(VM
&, ProgramExecutable
*, const SourceCode
&, JSParserStrictness
, DebuggerMode
, ProfilerMode
, ParserError
&);
256 UnlinkedEvalCodeBlock
* getEvalCodeBlock(VM
&, JSScope
*, EvalExecutable
*, const SourceCode
&, JSParserStrictness
, DebuggerMode
, ProfilerMode
, ParserError
&);
257 UnlinkedFunctionExecutable
* getFunctionExecutableFromGlobalCode(VM
&, const Identifier
&, const SourceCode
&, ParserError
&);
262 m_sourceCode
.clear();
266 CodeCache(CodeCacheKind
);
268 template <class UnlinkedCodeBlockType
, class ExecutableType
>
269 UnlinkedCodeBlockType
* getCodeBlock(VM
&, JSScope
*, ExecutableType
*, const SourceCode
&, JSParserStrictness
, DebuggerMode
, ProfilerMode
, ParserError
&);
271 template <class UnlinkedCodeBlockType
, class ExecutableType
>
272 UnlinkedCodeBlockType
* generateBytecode(VM
&, JSScope
*, ExecutableType
*, const SourceCode
&, JSParserStrictness
, DebuggerMode
, ProfilerMode
, ParserError
&);
274 CodeCacheMap m_sourceCode
;
279 #endif // CodeCache_h