[JSC] Shrink UnlinkedFunctionExecutable
[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 #include <wtf/text/StringConcatenateNumbers.h>
31
32 namespace JSC {
33
34 const Seconds CodeCacheMap::workingSetTime = 10_s;
35
36 void CodeCacheMap::pruneSlowCase()
37 {
38     m_minCapacity = std::max(m_size - m_sizeAtLastPrune, static_cast<int64_t>(0));
39     m_sizeAtLastPrune = m_size;
40     m_timeAtLastPrune = MonotonicTime::now();
41
42     if (m_capacity < m_minCapacity)
43         m_capacity = m_minCapacity;
44
45     while (m_size > m_capacity || !canPruneQuickly()) {
46         MapType::iterator it = m_map.begin();
47
48         writeCodeBlock(*it->value.cell->vm(), it->key, it->value);
49
50         m_size -= it->key.length();
51         m_map.remove(it);
52     }
53 }
54
55 template <class UnlinkedCodeBlockType, class ExecutableType>
56 UnlinkedCodeBlockType* CodeCache::getUnlinkedGlobalCodeBlock(VM& vm, ExecutableType* executable, const SourceCode& source, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, DebuggerMode debuggerMode, ParserError& error, EvalContextType evalContextType)
57 {
58     DerivedContextType derivedContextType = executable->derivedContextType();
59     bool isArrowFunctionContext = executable->isArrowFunctionContext();
60     SourceCodeKey key(
61         source, String(), CacheTypes<UnlinkedCodeBlockType>::codeType, strictMode, scriptMode, 
62         derivedContextType, evalContextType, isArrowFunctionContext, debuggerMode, 
63         vm.typeProfiler() ? TypeProfilerEnabled::Yes : TypeProfilerEnabled::No, 
64         vm.controlFlowProfiler() ? ControlFlowProfilerEnabled::Yes : ControlFlowProfilerEnabled::No,
65         WTF::nullopt);
66     UnlinkedCodeBlockType* unlinkedCodeBlock = m_sourceCode.findCacheAndUpdateAge<UnlinkedCodeBlockType>(vm, key);
67     if (unlinkedCodeBlock && Options::useCodeCache()) {
68         unsigned lineCount = unlinkedCodeBlock->lineCount();
69         unsigned startColumn = unlinkedCodeBlock->startColumn() + source.startColumn().oneBasedInt();
70         bool endColumnIsOnStartLine = !lineCount;
71         unsigned endColumn = unlinkedCodeBlock->endColumn() + (endColumnIsOnStartLine ? startColumn : 1);
72         executable->recordParse(unlinkedCodeBlock->codeFeatures(), unlinkedCodeBlock->hasCapturedVariables(), source.firstLine().oneBasedInt() + lineCount, endColumn);
73         if (!unlinkedCodeBlock->sourceURLDirective().isNull())
74             source.provider()->setSourceURLDirective(unlinkedCodeBlock->sourceURLDirective());
75         if (!unlinkedCodeBlock->sourceMappingURLDirective().isNull())
76             source.provider()->setSourceMappingURLDirective(unlinkedCodeBlock->sourceMappingURLDirective());
77         return unlinkedCodeBlock;
78     }
79
80     VariableEnvironment variablesUnderTDZ;
81     unlinkedCodeBlock = generateUnlinkedCodeBlock<UnlinkedCodeBlockType, ExecutableType>(vm, executable, source, strictMode, scriptMode, debuggerMode, error, evalContextType, &variablesUnderTDZ);
82
83     if (unlinkedCodeBlock && Options::useCodeCache())
84         m_sourceCode.addCache(key, SourceCodeValue(vm, unlinkedCodeBlock, m_sourceCode.age()));
85
86     return unlinkedCodeBlock;
87 }
88
89 UnlinkedProgramCodeBlock* CodeCache::getUnlinkedProgramCodeBlock(VM& vm, ProgramExecutable* executable, const SourceCode& source, JSParserStrictMode strictMode, DebuggerMode debuggerMode, ParserError& error)
90 {
91     return getUnlinkedGlobalCodeBlock<UnlinkedProgramCodeBlock>(vm, executable, source, strictMode, JSParserScriptMode::Classic, debuggerMode, error, EvalContextType::None);
92 }
93
94 UnlinkedEvalCodeBlock* CodeCache::getUnlinkedEvalCodeBlock(VM& vm, IndirectEvalExecutable* executable, const SourceCode& source, JSParserStrictMode strictMode, DebuggerMode debuggerMode, ParserError& error, EvalContextType evalContextType)
95 {
96     return getUnlinkedGlobalCodeBlock<UnlinkedEvalCodeBlock>(vm, executable, source, strictMode, JSParserScriptMode::Classic, debuggerMode, error, evalContextType);
97 }
98
99 UnlinkedModuleProgramCodeBlock* CodeCache::getUnlinkedModuleProgramCodeBlock(VM& vm, ModuleProgramExecutable* executable, const SourceCode& source, DebuggerMode debuggerMode, ParserError& error)
100 {
101     return getUnlinkedGlobalCodeBlock<UnlinkedModuleProgramCodeBlock>(vm, executable, source, JSParserStrictMode::Strict, JSParserScriptMode::Module, debuggerMode, error, EvalContextType::None);
102 }
103
104 UnlinkedFunctionExecutable* CodeCache::getUnlinkedGlobalFunctionExecutable(VM& vm, const Identifier& name, const SourceCode& source, DebuggerMode debuggerMode, Optional<int> functionConstructorParametersEndPosition, ParserError& error)
105 {
106     bool isArrowFunctionContext = false;
107     SourceCodeKey key(
108         source, name.string(), SourceCodeType::FunctionType,
109         JSParserStrictMode::NotStrict,
110         JSParserScriptMode::Classic,
111         DerivedContextType::None,
112         EvalContextType::None,
113         isArrowFunctionContext,
114         debuggerMode, 
115         vm.typeProfiler() ? TypeProfilerEnabled::Yes : TypeProfilerEnabled::No, 
116         vm.controlFlowProfiler() ? ControlFlowProfilerEnabled::Yes : ControlFlowProfilerEnabled::No,
117         functionConstructorParametersEndPosition);
118     UnlinkedFunctionExecutable* executable = m_sourceCode.findCacheAndUpdateAge<UnlinkedFunctionExecutable>(vm, key);
119     if (executable && Options::useCodeCache()) {
120         if (!executable->sourceURLDirective().isNull())
121             source.provider()->setSourceURLDirective(executable->sourceURLDirective());
122         if (!executable->sourceMappingURLDirective().isNull())
123             source.provider()->setSourceMappingURLDirective(executable->sourceMappingURLDirective());
124         return executable;
125     }
126
127     JSTextPosition positionBeforeLastNewline;
128     std::unique_ptr<ProgramNode> program = parseFunctionForFunctionConstructor(vm, source, error, &positionBeforeLastNewline, functionConstructorParametersEndPosition);
129     if (!program) {
130         RELEASE_ASSERT(error.isValid());
131         return nullptr;
132     }
133
134     // This function assumes an input string that would result in a single function declaration.
135     StatementNode* funcDecl = program->singleStatement();
136     if (UNLIKELY(!funcDecl)) {
137         JSToken token;
138         error = ParserError(ParserError::SyntaxError, ParserError::SyntaxErrorIrrecoverable, token, "Parser error", -1);
139         return nullptr;
140     }
141     ASSERT(funcDecl->isFuncDeclNode());
142
143     FunctionMetadataNode* metadata = static_cast<FuncDeclNode*>(funcDecl)->metadata();
144     ASSERT(metadata);
145     if (!metadata)
146         return nullptr;
147     
148     metadata->overrideName(name);
149     metadata->setEndPosition(positionBeforeLastNewline);
150     // The Function constructor only has access to global variables, so no variables will be under TDZ unless they're
151     // in the global lexical environment, which we always TDZ check accesses from.
152     VariableEnvironment emptyTDZVariables;
153     ConstructAbility constructAbility = constructAbilityForParseMode(metadata->parseMode());
154     UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, metadata, UnlinkedNormalFunction, constructAbility, JSParserScriptMode::Classic, vm.m_compactVariableMap->get(emptyTDZVariables), DerivedContextType::None);
155
156     if (!source.provider()->sourceURLDirective().isNull())
157         functionExecutable->setSourceURLDirective(source.provider()->sourceURLDirective());
158     if (!source.provider()->sourceMappingURLDirective().isNull())
159         functionExecutable->setSourceMappingURLDirective(source.provider()->sourceMappingURLDirective());
160
161     if (Options::useCodeCache())
162         m_sourceCode.addCache(key, SourceCodeValue(vm, functionExecutable, m_sourceCode.age()));
163     return functionExecutable;
164 }
165
166 void CodeCache::write(VM& vm)
167 {
168     for (auto& it : m_sourceCode) {
169         if (it.value.written)
170             continue;
171         it.value.written = true;
172         writeCodeBlock(vm, it.key, it.value);
173     }
174 }
175
176 void generateUnlinkedCodeBlockForFunctions(VM& vm, UnlinkedCodeBlock* unlinkedCodeBlock, const SourceCode& parentSource, DebuggerMode debuggerMode, ParserError& error)
177 {
178     auto generate = [&](UnlinkedFunctionExecutable* unlinkedExecutable, CodeSpecializationKind constructorKind) {
179         if (constructorKind == CodeForConstruct && SourceParseModeSet(SourceParseMode::AsyncArrowFunctionMode, SourceParseMode::AsyncMethodMode, SourceParseMode::AsyncFunctionMode).contains(unlinkedExecutable->parseMode()))
180             return;
181
182         SourceCode source = unlinkedExecutable->linkedSourceCode(parentSource);
183         UnlinkedFunctionCodeBlock* unlinkedFunctionCodeBlock = unlinkedExecutable->unlinkedCodeBlockFor(vm, source, constructorKind, debuggerMode, error, unlinkedExecutable->parseMode());
184         if (unlinkedFunctionCodeBlock)
185             generateUnlinkedCodeBlockForFunctions(vm, unlinkedFunctionCodeBlock, source, debuggerMode, error);
186     };
187
188     // FIXME: We should also generate CodeBlocks for CodeForConstruct
189     // https://bugs.webkit.org/show_bug.cgi?id=193823
190     for (unsigned i = 0; i < unlinkedCodeBlock->numberOfFunctionDecls(); i++)
191         generate(unlinkedCodeBlock->functionDecl(i), CodeForCall);
192     for (unsigned i = 0; i < unlinkedCodeBlock->numberOfFunctionExprs(); i++)
193         generate(unlinkedCodeBlock->functionExpr(i), CodeForCall);
194 }
195
196 void writeCodeBlock(VM& vm, const SourceCodeKey& key, const SourceCodeValue& value)
197 {
198     UnlinkedCodeBlock* codeBlock = jsDynamicCast<UnlinkedCodeBlock*>(vm, value.cell.get());
199     if (!codeBlock)
200         return;
201
202     std::pair<MallocPtr<uint8_t>, size_t> result = encodeCodeBlock(vm, key, codeBlock);
203     CachedBytecode cachedBytecode { WTFMove(result.first), result.second };
204     key.source().provider().cacheBytecode(cachedBytecode);
205 }
206
207 CachedBytecode serializeBytecode(VM& vm, UnlinkedCodeBlock* codeBlock, const SourceCode& source, SourceCodeType codeType, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, DebuggerMode debuggerMode)
208 {
209     SourceCodeKey key(
210         source, String(), codeType, strictMode, scriptMode,
211         DerivedContextType::None, EvalContextType::None, false, debuggerMode,
212         vm.typeProfiler() ? TypeProfilerEnabled::Yes : TypeProfilerEnabled::No,
213         vm.controlFlowProfiler() ? ControlFlowProfilerEnabled::Yes : ControlFlowProfilerEnabled::No,
214         WTF::nullopt);
215     std::pair<MallocPtr<uint8_t>, size_t> result = encodeCodeBlock(vm, key, codeBlock);
216     return CachedBytecode { WTFMove(result.first), result.second };
217 }
218
219 }