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