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