8a4419f96ca8fa4a629be0e9a7827cbaf68cff86
[WebKit-https.git] / Source / JavaScriptCore / runtime / CodeCache.h
1 /*
2  * Copyright (C) 2012, 2013 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
24  */
25
26 #pragma once
27
28 #include "BytecodeGenerator.h"
29 #include "ExecutableInfo.h"
30 #include "JSCInlines.h"
31 #include "Parser.h"
32 #include "ParserModes.h"
33 #include "SourceCodeKey.h"
34 #include "Strong.h"
35 #include "StrongInlines.h"
36 #include "UnlinkedCodeBlock.h"
37 #include "UnlinkedEvalCodeBlock.h"
38 #include "UnlinkedModuleProgramCodeBlock.h"
39 #include "UnlinkedProgramCodeBlock.h"
40 #include <wtf/Forward.h>
41 #include <wtf/text/WTFString.h>
42
43 namespace JSC {
44
45 class EvalExecutable;
46 class IndirectEvalExecutable;
47 class Identifier;
48 class DirectEvalExecutable;
49 class ModuleProgramExecutable;
50 class ParserError;
51 class ProgramExecutable;
52 class SourceCode;
53 class UnlinkedCodeBlock;
54 class UnlinkedEvalCodeBlock;
55 class UnlinkedFunctionExecutable;
56 class UnlinkedModuleProgramCodeBlock;
57 class UnlinkedProgramCodeBlock;
58 class VM;
59 class VariableEnvironment;
60
61 struct SourceCodeValue {
62     SourceCodeValue()
63     {
64     }
65
66     SourceCodeValue(VM& vm, JSCell* cell, int64_t age)
67         : cell(vm, cell)
68         , age(age)
69     {
70     }
71
72     Strong<JSCell> cell;
73     int64_t age;
74 };
75
76 class CodeCacheMap {
77 public:
78     typedef HashMap<SourceCodeKey, SourceCodeValue, SourceCodeKey::Hash, SourceCodeKey::HashTraits> MapType;
79     typedef MapType::iterator iterator;
80     typedef MapType::AddResult AddResult;
81
82     CodeCacheMap()
83         : m_size(0)
84         , m_sizeAtLastPrune(0)
85         , m_timeAtLastPrune(MonotonicTime::now())
86         , m_minCapacity(0)
87         , m_capacity(0)
88         , m_age(0)
89     {
90     }
91
92     SourceCodeValue* findCacheAndUpdateAge(const SourceCodeKey& key)
93     {
94         prune();
95
96         iterator findResult = m_map.find(key);
97         if (findResult == m_map.end())
98             return nullptr;
99
100         int64_t age = m_age - findResult->value.age;
101         if (age > m_capacity) {
102             // A requested object is older than the cache's capacity. We can
103             // infer that requested objects are subject to high eviction probability,
104             // so we grow the cache to improve our hit rate.
105             m_capacity += recencyBias * oldObjectSamplingMultiplier * key.length();
106         } else if (age < m_capacity / 2) {
107             // A requested object is much younger than the cache's capacity. We can
108             // infer that requested objects are subject to low eviction probability,
109             // so we shrink the cache to save memory.
110             m_capacity -= recencyBias * key.length();
111             if (m_capacity < m_minCapacity)
112                 m_capacity = m_minCapacity;
113         }
114
115         findResult->value.age = m_age;
116         m_age += key.length();
117
118         return &findResult->value;
119     }
120
121     AddResult addCache(const SourceCodeKey& key, const SourceCodeValue& value)
122     {
123         prune();
124
125         AddResult addResult = m_map.add(key, value);
126         ASSERT(addResult.isNewEntry);
127
128         m_size += key.length();
129         m_age += key.length();
130         return addResult;
131     }
132
133     void remove(iterator it)
134     {
135         m_size -= it->key.length();
136         m_map.remove(it);
137     }
138
139     void clear()
140     {
141         m_size = 0;
142         m_age = 0;
143         m_map.clear();
144     }
145
146     int64_t age() { return m_age; }
147
148 private:
149     // This constant factor biases cache capacity toward allowing a minimum
150     // working set to enter the cache before it starts evicting.
151     static const Seconds workingSetTime;
152     static const int64_t workingSetMaxBytes = 16000000;
153     static const size_t workingSetMaxEntries = 2000;
154
155     // This constant factor biases cache capacity toward recent activity. We
156     // want to adapt to changing workloads.
157     static const int64_t recencyBias = 4;
158
159     // This constant factor treats a sampled event for one old object as if it
160     // happened for many old objects. Most old objects are evicted before we can
161     // sample them, so we need to extrapolate from the ones we do sample.
162     static const int64_t oldObjectSamplingMultiplier = 32;
163
164     size_t numberOfEntries() const { return static_cast<size_t>(m_map.size()); }
165     bool canPruneQuickly() const { return numberOfEntries() < workingSetMaxEntries; }
166
167     void pruneSlowCase();
168     void prune()
169     {
170         if (m_size <= m_capacity && canPruneQuickly())
171             return;
172
173         if (MonotonicTime::now() - m_timeAtLastPrune < workingSetTime
174             && m_size - m_sizeAtLastPrune < workingSetMaxBytes
175             && canPruneQuickly())
176                 return;
177
178         pruneSlowCase();
179     }
180
181     MapType m_map;
182     int64_t m_size;
183     int64_t m_sizeAtLastPrune;
184     MonotonicTime m_timeAtLastPrune;
185     int64_t m_minCapacity;
186     int64_t m_capacity;
187     int64_t m_age;
188 };
189
190 // Caches top-level code such as <script>, window.eval(), new Function, and JSEvaluateScript().
191 class CodeCache {
192     WTF_MAKE_FAST_ALLOCATED;
193 public:
194     UnlinkedProgramCodeBlock* getUnlinkedProgramCodeBlock(VM&, ProgramExecutable*, const SourceCode&, JSParserStrictMode, DebuggerMode, ParserError&);
195     UnlinkedEvalCodeBlock* getUnlinkedEvalCodeBlock(VM&, IndirectEvalExecutable*, const SourceCode&, JSParserStrictMode, DebuggerMode, ParserError&, EvalContextType);
196     UnlinkedModuleProgramCodeBlock* getUnlinkedModuleProgramCodeBlock(VM&, ModuleProgramExecutable*, const SourceCode&, DebuggerMode, ParserError&);
197     UnlinkedFunctionExecutable* getUnlinkedGlobalFunctionExecutable(VM&, const Identifier&, const SourceCode&, DebuggerMode, Optional<int> functionConstructorParametersEndPosition, ParserError&);
198
199     void clear() { m_sourceCode.clear(); }
200
201 private:
202     template <class UnlinkedCodeBlockType, class ExecutableType> 
203     UnlinkedCodeBlockType* getUnlinkedGlobalCodeBlock(VM&, ExecutableType*, const SourceCode&, JSParserStrictMode, JSParserScriptMode, DebuggerMode, ParserError&, EvalContextType);
204
205     CodeCacheMap m_sourceCode;
206 };
207
208 template <typename T> struct CacheTypes { };
209
210 template <> struct CacheTypes<UnlinkedProgramCodeBlock> {
211     typedef JSC::ProgramNode RootNode;
212     static const SourceCodeType codeType = SourceCodeType::ProgramType;
213     static const SourceParseMode parseMode = SourceParseMode::ProgramMode;
214 };
215
216 template <> struct CacheTypes<UnlinkedEvalCodeBlock> {
217     typedef JSC::EvalNode RootNode;
218     static const SourceCodeType codeType = SourceCodeType::EvalType;
219     static const SourceParseMode parseMode = SourceParseMode::ProgramMode;
220 };
221
222 template <> struct CacheTypes<UnlinkedModuleProgramCodeBlock> {
223     typedef JSC::ModuleProgramNode RootNode;
224     static const SourceCodeType codeType = SourceCodeType::ModuleType;
225     static const SourceParseMode parseMode = SourceParseMode::ModuleEvaluateMode;
226 };
227
228 template <class UnlinkedCodeBlockType, class ExecutableType>
229 UnlinkedCodeBlockType* generateUnlinkedCodeBlock(VM& vm, ExecutableType* executable, const SourceCode& source, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, DebuggerMode debuggerMode, ParserError& error, EvalContextType evalContextType, const VariableEnvironment* variablesUnderTDZ)
230 {
231     typedef typename CacheTypes<UnlinkedCodeBlockType>::RootNode RootNode;
232     DerivedContextType derivedContextType = executable->derivedContextType();
233     std::unique_ptr<RootNode> rootNode = parse<RootNode>(
234         &vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin, strictMode, scriptMode, CacheTypes<UnlinkedCodeBlockType>::parseMode, SuperBinding::NotNeeded, error, nullptr, ConstructorKind::None, derivedContextType, evalContextType);
235     if (!rootNode)
236         return nullptr;
237
238     unsigned lineCount = rootNode->lastLine() - rootNode->firstLine();
239     unsigned startColumn = rootNode->startColumn() + 1;
240     bool endColumnIsOnStartLine = !lineCount;
241     unsigned unlinkedEndColumn = rootNode->endColumn();
242     unsigned endColumn = unlinkedEndColumn + (endColumnIsOnStartLine ? startColumn : 1);
243     unsigned arrowContextFeature = executable->isArrowFunctionContext() ? ArrowFunctionContextFeature : 0;
244     executable->recordParse(rootNode->features() | arrowContextFeature, rootNode->hasCapturedVariables(), rootNode->lastLine(), endColumn);
245
246     UnlinkedCodeBlockType* unlinkedCodeBlock = UnlinkedCodeBlockType::create(&vm, executable->executableInfo(), debuggerMode);
247     unlinkedCodeBlock->recordParse(rootNode->features(), rootNode->hasCapturedVariables(), lineCount, unlinkedEndColumn);
248     unlinkedCodeBlock->setSourceURLDirective(source.provider()->sourceURL());
249     unlinkedCodeBlock->setSourceMappingURLDirective(source.provider()->sourceMappingURL());
250
251     error = BytecodeGenerator::generate(vm, rootNode.get(), source, unlinkedCodeBlock, debuggerMode, variablesUnderTDZ);
252
253     if (error.isValid())
254         return nullptr;
255
256     return unlinkedCodeBlock;
257 }
258
259 } // namespace JSC