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/RandomNumber.h>
37 #include <wtf/text/WTFString.h>
42 class FunctionBodyNode
;
46 class ProgramExecutable
;
47 class UnlinkedCodeBlock
;
48 class UnlinkedEvalCodeBlock
;
49 class UnlinkedFunctionCodeBlock
;
50 class UnlinkedFunctionExecutable
;
51 class UnlinkedProgramCodeBlock
;
58 enum CodeType
{ EvalType
, ProgramType
, FunctionType
};
64 SourceCodeKey(const SourceCode
& sourceCode
, const String
& name
, CodeType codeType
, JSParserBuiltinMode builtinMode
,
65 JSParserStrictMode strictMode
, ThisTDZMode thisTDZMode
= ThisTDZMode::CheckIfNeeded
)
66 : m_sourceCode(sourceCode
)
69 (static_cast<unsigned>(codeType
) << 3)
70 | (static_cast<unsigned>(builtinMode
) << 2)
71 | (static_cast<unsigned>(strictMode
) << 1)
72 | static_cast<unsigned>(thisTDZMode
))
73 , m_hash(string().impl()->hash())
77 SourceCodeKey(WTF::HashTableDeletedValueType
)
78 : m_sourceCode(WTF::HashTableDeletedValue
)
82 bool isHashTableDeletedValue() const { return m_sourceCode
.isHashTableDeletedValue(); }
84 unsigned hash() const { return m_hash
; }
86 size_t length() const { return m_sourceCode
.length(); }
88 bool isNull() const { return m_sourceCode
.isNull(); }
90 // To save memory, we compute our string on demand. It's expected that source
91 // providers cache their strings to make this efficient.
92 String
string() const { return m_sourceCode
.toString(); }
94 bool operator==(const SourceCodeKey
& other
) const
96 return m_hash
== other
.m_hash
97 && length() == other
.length()
98 && m_flags
== other
.m_flags
99 && m_name
== other
.m_name
100 && string() == other
.string();
104 SourceCode m_sourceCode
;
110 struct SourceCodeKeyHash
{
111 static unsigned hash(const SourceCodeKey
& key
) { return key
.hash(); }
112 static bool equal(const SourceCodeKey
& a
, const SourceCodeKey
& b
) { return a
== b
; }
113 static const bool safeToCompareToEmptyOrDeleted
= false;
116 struct SourceCodeKeyHashTraits
: SimpleClassHashTraits
<SourceCodeKey
> {
117 static const bool hasIsEmptyValueFunction
= true;
118 static bool isEmptyValue(const SourceCodeKey
& sourceCodeKey
) { return sourceCodeKey
.isNull(); }
121 struct SourceCodeValue
{
126 SourceCodeValue(VM
& vm
, JSCell
* cell
, int64_t age
)
138 typedef HashMap
<SourceCodeKey
, SourceCodeValue
, SourceCodeKeyHash
, SourceCodeKeyHashTraits
> MapType
;
139 typedef MapType::iterator iterator
;
140 typedef MapType::AddResult AddResult
;
144 , m_sizeAtLastPrune(0)
145 , m_timeAtLastPrune(monotonicallyIncreasingTime())
152 SourceCodeValue
* findCacheAndUpdateAge(const SourceCodeKey
& key
)
156 iterator findResult
= m_map
.find(key
);
157 if (findResult
== m_map
.end())
160 int64_t age
= m_age
- findResult
->value
.age
;
161 if (age
> m_capacity
) {
162 // A requested object is older than the cache's capacity. We can
163 // infer that requested objects are subject to high eviction probability,
164 // so we grow the cache to improve our hit rate.
165 m_capacity
+= recencyBias
* oldObjectSamplingMultiplier
* key
.length();
166 } else if (age
< m_capacity
/ 2) {
167 // A requested object is much younger than the cache's capacity. We can
168 // infer that requested objects are subject to low eviction probability,
169 // so we shrink the cache to save memory.
170 m_capacity
-= recencyBias
* key
.length();
171 if (m_capacity
< m_minCapacity
)
172 m_capacity
= m_minCapacity
;
175 findResult
->value
.age
= m_age
;
176 m_age
+= key
.length();
178 return &findResult
->value
;
181 AddResult
addCache(const SourceCodeKey
& key
, const SourceCodeValue
& value
)
185 AddResult addResult
= m_map
.add(key
, value
);
186 ASSERT(addResult
.isNewEntry
);
188 m_size
+= key
.length();
189 m_age
+= key
.length();
193 void remove(iterator it
)
195 m_size
-= it
->key
.length();
206 int64_t age() { return m_age
; }
209 // This constant factor biases cache capacity toward allowing a minimum
210 // working set to enter the cache before it starts evicting.
211 static const double workingSetTime
;
212 static const int64_t workingSetMaxBytes
= 16000000;
213 static const size_t workingSetMaxEntries
= 2000;
215 // This constant factor biases cache capacity toward recent activity. We
216 // want to adapt to changing workloads.
217 static const int64_t recencyBias
= 4;
219 // This constant factor treats a sampled event for one old object as if it
220 // happened for many old objects. Most old objects are evicted before we can
221 // sample them, so we need to extrapolate from the ones we do sample.
222 static const int64_t oldObjectSamplingMultiplier
= 32;
224 size_t numberOfEntries() const { return static_cast<size_t>(m_map
.size()); }
225 bool canPruneQuickly() const { return numberOfEntries() < workingSetMaxEntries
; }
227 void pruneSlowCase();
230 if (m_size
<= m_capacity
&& canPruneQuickly())
233 if (monotonicallyIncreasingTime() - m_timeAtLastPrune
< workingSetTime
234 && m_size
- m_sizeAtLastPrune
< workingSetMaxBytes
235 && canPruneQuickly())
243 int64_t m_sizeAtLastPrune
;
244 double m_timeAtLastPrune
;
245 int64_t m_minCapacity
;
250 // Caches top-level code such as <script>, eval(), new Function, and JSEvaluateScript().
252 WTF_MAKE_FAST_ALLOCATED
;
257 UnlinkedProgramCodeBlock
* getProgramCodeBlock(VM
&, ProgramExecutable
*, const SourceCode
&, JSParserBuiltinMode
, JSParserStrictMode
, DebuggerMode
, ProfilerMode
, ParserError
&);
258 UnlinkedEvalCodeBlock
* getEvalCodeBlock(VM
&, EvalExecutable
*, const SourceCode
&, JSParserBuiltinMode
, JSParserStrictMode
, ThisTDZMode
, DebuggerMode
, ProfilerMode
, ParserError
&);
259 UnlinkedFunctionExecutable
* getFunctionExecutableFromGlobalCode(VM
&, const Identifier
&, const SourceCode
&, ParserError
&);
263 m_sourceCode
.clear();
267 template <class UnlinkedCodeBlockType
, class ExecutableType
>
268 UnlinkedCodeBlockType
* getGlobalCodeBlock(VM
&, ExecutableType
*, const SourceCode
&, JSParserBuiltinMode
, JSParserStrictMode
, ThisTDZMode
, DebuggerMode
, ProfilerMode
, ParserError
&);
270 CodeCacheMap m_sourceCode
;
275 #endif // CodeCache_h