Cache bytecode to disk
[WebKit-https.git] / Source / JavaScriptCore / runtime / CodeCache.cpp
1 /*
2  * Copyright (C) 2012, 2016 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 #include "config.h"
27 #include "CodeCache.h"
28
29 #include "IndirectEvalExecutable.h"
30
31 namespace JSC {
32
33 const Seconds CodeCacheMap::workingSetTime = 10_s;
34
35 void CodeCacheMap::pruneSlowCase()
36 {
37     m_minCapacity = std::max(m_size - m_sizeAtLastPrune, static_cast<int64_t>(0));
38     m_sizeAtLastPrune = m_size;
39     m_timeAtLastPrune = MonotonicTime::now();
40
41     if (m_capacity < m_minCapacity)
42         m_capacity = m_minCapacity;
43
44     while (m_size > m_capacity || !canPruneQuickly()) {
45         MapType::iterator it = m_map.begin();
46
47         writeCodeBlock(*it->value.cell->vm(), it->key, it->value);
48
49         m_size -= it->key.length();
50         m_map.remove(it);
51     }
52 }
53
54 template <class UnlinkedCodeBlockType, class ExecutableType>
55 UnlinkedCodeBlockType* CodeCache::getUnlinkedGlobalCodeBlock(VM& vm, ExecutableType* executable, const SourceCode& source, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, DebuggerMode debuggerMode, ParserError& error, EvalContextType evalContextType)
56 {
57     DerivedContextType derivedContextType = executable->derivedContextType();
58     bool isArrowFunctionContext = executable->isArrowFunctionContext();
59     SourceCodeKey key(
60         source, String(), CacheTypes<UnlinkedCodeBlockType>::codeType, strictMode, scriptMode, 
61         derivedContextType, evalContextType, isArrowFunctionContext, debuggerMode, 
62         vm.typeProfiler() ? TypeProfilerEnabled::Yes : TypeProfilerEnabled::No, 
63         vm.controlFlowProfiler() ? ControlFlowProfilerEnabled::Yes : ControlFlowProfilerEnabled::No,
64         WTF::nullopt);
65     UnlinkedCodeBlockType* unlinkedCodeBlock = m_sourceCode.findCacheAndUpdateAge<UnlinkedCodeBlockType>(vm, key);
66     if (unlinkedCodeBlock && Options::useCodeCache()) {
67         unsigned lineCount = unlinkedCodeBlock->lineCount();
68         unsigned startColumn = unlinkedCodeBlock->startColumn() + source.startColumn().oneBasedInt();
69         bool endColumnIsOnStartLine = !lineCount;
70         unsigned endColumn = unlinkedCodeBlock->endColumn() + (endColumnIsOnStartLine ? startColumn : 1);
71         executable->recordParse(unlinkedCodeBlock->codeFeatures(), unlinkedCodeBlock->hasCapturedVariables(), source.firstLine().oneBasedInt() + lineCount, endColumn);
72         source.provider()->setSourceURLDirective(unlinkedCodeBlock->sourceURLDirective());
73         source.provider()->setSourceMappingURLDirective(unlinkedCodeBlock->sourceMappingURLDirective());
74         return unlinkedCodeBlock;
75     }
76
77     VariableEnvironment variablesUnderTDZ;
78     unlinkedCodeBlock = generateUnlinkedCodeBlock<UnlinkedCodeBlockType, ExecutableType>(vm, executable, source, strictMode, scriptMode, debuggerMode, error, evalContextType, &variablesUnderTDZ);
79
80     if (unlinkedCodeBlock && Options::useCodeCache())
81         m_sourceCode.addCache(key, SourceCodeValue(vm, unlinkedCodeBlock, m_sourceCode.age()));
82
83     return unlinkedCodeBlock;
84 }
85
86 UnlinkedProgramCodeBlock* CodeCache::getUnlinkedProgramCodeBlock(VM& vm, ProgramExecutable* executable, const SourceCode& source, JSParserStrictMode strictMode, DebuggerMode debuggerMode, ParserError& error)
87 {
88     return getUnlinkedGlobalCodeBlock<UnlinkedProgramCodeBlock>(vm, executable, source, strictMode, JSParserScriptMode::Classic, debuggerMode, error, EvalContextType::None);
89 }
90
91 UnlinkedEvalCodeBlock* CodeCache::getUnlinkedEvalCodeBlock(VM& vm, IndirectEvalExecutable* executable, const SourceCode& source, JSParserStrictMode strictMode, DebuggerMode debuggerMode, ParserError& error, EvalContextType evalContextType)
92 {
93     return getUnlinkedGlobalCodeBlock<UnlinkedEvalCodeBlock>(vm, executable, source, strictMode, JSParserScriptMode::Classic, debuggerMode, error, evalContextType);
94 }
95
96 UnlinkedModuleProgramCodeBlock* CodeCache::getUnlinkedModuleProgramCodeBlock(VM& vm, ModuleProgramExecutable* executable, const SourceCode& source, DebuggerMode debuggerMode, ParserError& error)
97 {
98     return getUnlinkedGlobalCodeBlock<UnlinkedModuleProgramCodeBlock>(vm, executable, source, JSParserStrictMode::Strict, JSParserScriptMode::Module, debuggerMode, error, EvalContextType::None);
99 }
100
101 UnlinkedFunctionExecutable* CodeCache::getUnlinkedGlobalFunctionExecutable(VM& vm, const Identifier& name, const SourceCode& source, DebuggerMode debuggerMode, Optional<int> functionConstructorParametersEndPosition, ParserError& error)
102 {
103     bool isArrowFunctionContext = false;
104     SourceCodeKey key(
105         source, name.string(), SourceCodeType::FunctionType,
106         JSParserStrictMode::NotStrict,
107         JSParserScriptMode::Classic,
108         DerivedContextType::None,
109         EvalContextType::None,
110         isArrowFunctionContext,
111         debuggerMode, 
112         vm.typeProfiler() ? TypeProfilerEnabled::Yes : TypeProfilerEnabled::No, 
113         vm.controlFlowProfiler() ? ControlFlowProfilerEnabled::Yes : ControlFlowProfilerEnabled::No,
114         functionConstructorParametersEndPosition);
115     UnlinkedFunctionExecutable* executable = m_sourceCode.findCacheAndUpdateAge<UnlinkedFunctionExecutable>(vm, key);
116     if (executable && Options::useCodeCache()) {
117         source.provider()->setSourceURLDirective(executable->sourceURLDirective());
118         source.provider()->setSourceMappingURLDirective(executable->sourceMappingURLDirective());
119         return executable;
120     }
121
122     JSTextPosition positionBeforeLastNewline;
123     std::unique_ptr<ProgramNode> program = parseFunctionForFunctionConstructor(vm, source, error, &positionBeforeLastNewline, functionConstructorParametersEndPosition);
124     if (!program) {
125         RELEASE_ASSERT(error.isValid());
126         return nullptr;
127     }
128
129     // This function assumes an input string that would result in a single function declaration.
130     StatementNode* funcDecl = program->singleStatement();
131     if (UNLIKELY(!funcDecl)) {
132         JSToken token;
133         error = ParserError(ParserError::SyntaxError, ParserError::SyntaxErrorIrrecoverable, token, "Parser error", -1);
134         return nullptr;
135     }
136     ASSERT(funcDecl->isFuncDeclNode());
137
138     FunctionMetadataNode* metadata = static_cast<FuncDeclNode*>(funcDecl)->metadata();
139     ASSERT(metadata);
140     if (!metadata)
141         return nullptr;
142     
143     metadata->overrideName(name);
144     metadata->setEndPosition(positionBeforeLastNewline);
145     // The Function constructor only has access to global variables, so no variables will be under TDZ unless they're
146     // in the global lexical environment, which we always TDZ check accesses from.
147     VariableEnvironment emptyTDZVariables;
148     ConstructAbility constructAbility = constructAbilityForParseMode(metadata->parseMode());
149     UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, metadata, UnlinkedNormalFunction, constructAbility, JSParserScriptMode::Classic, emptyTDZVariables, DerivedContextType::None);
150
151     functionExecutable->setSourceURLDirective(source.provider()->sourceURL());
152     functionExecutable->setSourceMappingURLDirective(source.provider()->sourceMappingURL());
153
154     if (Options::useCodeCache())
155         m_sourceCode.addCache(key, SourceCodeValue(vm, functionExecutable, m_sourceCode.age()));
156     return functionExecutable;
157 }
158
159 void CodeCache::write(VM& vm)
160 {
161     for (const auto& it : m_sourceCode)
162         writeCodeBlock(vm, it.key, it.value);
163 }
164
165 }