CodeCache.cpp
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 "BytecodeGenerator.h" 30 #include "IndirectEvalExecutable.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 static void generateUnlinkedCodeBlockForFunctions(VM& vm, UnlinkedCodeBlock* unlinkedCodeBlock, const SourceCode& parentSource, OptionSet<CodeGenerationMode> codeGenerationMode, ParserError& error) 54 { 55 auto generate = [&](UnlinkedFunctionExecutable* unlinkedExecutable, CodeSpecializationKind constructorKind) { 56 if (constructorKind == CodeForConstruct && SourceParseModeSet(SourceParseMode::AsyncArrowFunctionMode, SourceParseMode::AsyncMethodMode, SourceParseMode::AsyncFunctionMode).contains(unlinkedExecutable->parseMode())) 57 return; 58 59 SourceCode source = unlinkedExecutable->linkedSourceCode(parentSource); 60 UnlinkedFunctionCodeBlock* unlinkedFunctionCodeBlock = unlinkedExecutable->unlinkedCodeBlockFor(vm, source, constructorKind, codeGenerationMode, error, unlinkedExecutable->parseMode()); 61 if (unlinkedFunctionCodeBlock) 62 generateUnlinkedCodeBlockForFunctions(vm, unlinkedFunctionCodeBlock, source, codeGenerationMode, error); 63 }; 64 65 // FIXME: We should also generate CodeBlocks for CodeForConstruct 66 // https://bugs.webkit.org/show_bug.cgi?id=193823 67 for (unsigned i = 0; i < unlinkedCodeBlock->numberOfFunctionDecls(); i++) 68 generate(unlinkedCodeBlock->functionDecl(i), CodeForCall); 69 for (unsigned i = 0; i < unlinkedCodeBlock->numberOfFunctionExprs(); i++) 70 generate(unlinkedCodeBlock->functionExpr(i), CodeForCall); 71 } 72 73 template <class UnlinkedCodeBlockType, class ExecutableType = ScriptExecutable> 74 UnlinkedCodeBlockType* generateUnlinkedCodeBlockImpl(VM& vm, const SourceCode& source, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, OptionSet<CodeGenerationMode> codeGenerationMode, ParserError& error, EvalContextType evalContextType, DerivedContextType derivedContextType, bool isArrowFunctionContext, const TDZEnvironment* variablesUnderTDZ = nullptr, const VariableEnvironment* parentScopePrivateNames = nullptr, ExecutableType* executable = nullptr) 75 { 76 typedef typename CacheTypes<UnlinkedCodeBlockType>::RootNode RootNode; 77 bool isInsideOrdinaryFunction = executable && executable->isInsideOrdinaryFunction(); 78 79 std::unique_ptr<RootNode> rootNode = parse<RootNode>( 80 vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin, strictMode, scriptMode, CacheTypes<UnlinkedCodeBlockType>::parseMode, SuperBinding::NotNeeded, error, nullptr, ConstructorKind::None, derivedContextType, evalContextType, nullptr, parentScopePrivateNames, nullptr, isInsideOrdinaryFunction); 81 82 if (!rootNode) 83 return nullptr; 84 85 unsigned lineCount = rootNode->lastLine() - rootNode->firstLine(); 86 unsigned startColumn = rootNode->startColumn() + 1; 87 bool endColumnIsOnStartLine = !lineCount; 88 unsigned unlinkedEndColumn = rootNode->endColumn(); 89 unsigned endColumn = unlinkedEndColumn + (endColumnIsOnStartLine ? startColumn : 1); 90 unsigned arrowContextFeature = isArrowFunctionContext ? ArrowFunctionContextFeature : 0; 91 if (executable) 92 executable->recordParse(rootNode->features() | arrowContextFeature, rootNode->hasCapturedVariables(), rootNode->lastLine(), endColumn); 93 94 bool usesEval = rootNode->features() & EvalFeature; 95 ECMAMode ecmaMode = rootNode->features() & StrictModeFeature ? ECMAMode::strict() : ECMAMode::sloppy(); 96 NeedsClassFieldInitializer needsClassFieldInitializer = NeedsClassFieldInitializer::No; 97 if constexpr (std::is_same_v<ExecutableType, DirectEvalExecutable>) 98 needsClassFieldInitializer = executable->needsClassFieldInitializer(); 99 ExecutableInfo executableInfo(usesEval, false, false, ConstructorKind::None, scriptMode, SuperBinding::NotNeeded, CacheTypes<UnlinkedCodeBlockType>::parseMode, derivedContextType, needsClassFieldInitializer, isArrowFunctionContext, false, evalContextType); 100 101 UnlinkedCodeBlockType* unlinkedCodeBlock = UnlinkedCodeBlockType::create(vm, executableInfo, codeGenerationMode); 102 unlinkedCodeBlock->recordParse(rootNode->features(), rootNode->hasCapturedVariables(), lineCount, unlinkedEndColumn); 103 if (!source.provider()->sourceURLDirective().isNull()) 104 unlinkedCodeBlock->setSourceURLDirective(source.provider()->sourceURLDirective()); 105 if (!source.provider()->sourceMappingURLDirective().isNull()) 106 unlinkedCodeBlock->setSourceMappingURLDirective(source.provider()->sourceMappingURLDirective()); 107 108 RefPtr<TDZEnvironmentLink> parentVariablesUnderTDZ; 109 if (variablesUnderTDZ) 110 parentVariablesUnderTDZ = TDZEnvironmentLink::create(vm.m_compactVariableMap->get(*variablesUnderTDZ), nullptr); 111 error = BytecodeGenerator::generate(vm, rootNode.get(), source, unlinkedCodeBlock, codeGenerationMode, parentVariablesUnderTDZ, ecmaMode); 112 113 if (error.isValid()) 114 return nullptr; 115 116 return unlinkedCodeBlock; 117 } 118 119 template <class UnlinkedCodeBlockType, class ExecutableType> 120 UnlinkedCodeBlockType* generateUnlinkedCodeBlock(VM& vm, ExecutableType* executable, const SourceCode& source, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, OptionSet<CodeGenerationMode> codeGenerationMode, ParserError& error, EvalContextType evalContextType, const TDZEnvironment* variablesUnderTDZ = nullptr, const VariableEnvironment* parentScopePrivateNames = nullptr) 121 { 122 return generateUnlinkedCodeBlockImpl<UnlinkedCodeBlockType, ExecutableType>(vm, source, strictMode, scriptMode, codeGenerationMode, error, evalContextType, executable->derivedContextType(), executable->isArrowFunctionContext(), variablesUnderTDZ, parentScopePrivateNames, executable); 123 } 124 125 UnlinkedEvalCodeBlock* generateUnlinkedCodeBlockForDirectEval(VM& vm, DirectEvalExecutable* executable, const SourceCode& source, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, OptionSet<CodeGenerationMode> codeGenerationMode, ParserError& error, EvalContextType evalContextType, const TDZEnvironment* variablesUnderTDZ, const VariableEnvironment* parentScopePrivateNames) 126 { 127 return generateUnlinkedCodeBlock<UnlinkedEvalCodeBlock>(vm, executable, source, strictMode, scriptMode, codeGenerationMode, error, evalContextType, variablesUnderTDZ, parentScopePrivateNames); 128 } 129 130 template <class UnlinkedCodeBlockType> 131 std::enable_if_t<!std::is_same<UnlinkedCodeBlockType, UnlinkedEvalCodeBlock>::value, UnlinkedCodeBlockType*> 132 recursivelyGenerateUnlinkedCodeBlock(VM& vm, const SourceCode& source, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, OptionSet<CodeGenerationMode> codeGenerationMode, ParserError& error, EvalContextType evalContextType) 133 { 134 bool isArrowFunctionContext = false; 135 UnlinkedCodeBlockType* unlinkedCodeBlock = generateUnlinkedCodeBlockImpl<UnlinkedCodeBlockType>(vm, source, strictMode, scriptMode, codeGenerationMode, error, evalContextType, DerivedContextType::None, isArrowFunctionContext); 136 if (!unlinkedCodeBlock) 137 return nullptr; 138 139 generateUnlinkedCodeBlockForFunctions(vm, unlinkedCodeBlock, source, codeGenerationMode, error); 140 return unlinkedCodeBlock; 141 } 142 143 UnlinkedProgramCodeBlock* recursivelyGenerateUnlinkedCodeBlockForProgram(VM& vm, const SourceCode& source, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, OptionSet<CodeGenerationMode> codeGenerationMode, ParserError& error, EvalContextType evalContextType) 144 { 145 return recursivelyGenerateUnlinkedCodeBlock<UnlinkedProgramCodeBlock>(vm, source, strictMode, scriptMode, codeGenerationMode, error, evalContextType); 146 } 147 148 UnlinkedModuleProgramCodeBlock* recursivelyGenerateUnlinkedCodeBlockForModuleProgram(VM& vm, const SourceCode& source, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, OptionSet<CodeGenerationMode> codeGenerationMode, ParserError& error, EvalContextType evalContextType) 149 { 150 return recursivelyGenerateUnlinkedCodeBlock<UnlinkedModuleProgramCodeBlock>(vm, source, strictMode, scriptMode, codeGenerationMode, error, evalContextType); 151 } 152 153 template <class UnlinkedCodeBlockType, class ExecutableType> 154 UnlinkedCodeBlockType* CodeCache::getUnlinkedGlobalCodeBlock(VM& vm, ExecutableType* executable, const SourceCode& source, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, OptionSet<CodeGenerationMode> codeGenerationMode, ParserError& error, EvalContextType evalContextType) 155 { 156 DerivedContextType derivedContextType = executable->derivedContextType(); 157 bool isArrowFunctionContext = executable->isArrowFunctionContext(); 158 SourceCodeKey key( 159 source, String(), CacheTypes<UnlinkedCodeBlockType>::codeType, strictMode, scriptMode, 160 derivedContextType, evalContextType, isArrowFunctionContext, codeGenerationMode, 161 WTF::nullopt); 162 UnlinkedCodeBlockType* unlinkedCodeBlock = m_sourceCode.findCacheAndUpdateAge<UnlinkedCodeBlockType>(vm, key); 163 if (unlinkedCodeBlock && Options::useCodeCache()) { 164 unsigned lineCount = unlinkedCodeBlock->lineCount(); 165 unsigned startColumn = unlinkedCodeBlock->startColumn() + source.startColumn().oneBasedInt(); 166 bool endColumnIsOnStartLine = !lineCount; 167 unsigned endColumn = unlinkedCodeBlock->endColumn() + (endColumnIsOnStartLine ? startColumn : 1); 168 executable->recordParse(unlinkedCodeBlock->codeFeatures(), unlinkedCodeBlock->hasCapturedVariables(), source.firstLine().oneBasedInt() + lineCount, endColumn); 169 if (unlinkedCodeBlock->sourceURLDirective()) 170 source.provider()->setSourceURLDirective(unlinkedCodeBlock->sourceURLDirective()); 171 if (unlinkedCodeBlock->sourceMappingURLDirective()) 172 source.provider()->setSourceMappingURLDirective(unlinkedCodeBlock->sourceMappingURLDirective()); 173 return unlinkedCodeBlock; 174 } 175 176 unlinkedCodeBlock = generateUnlinkedCodeBlock<UnlinkedCodeBlockType, ExecutableType>(vm, executable, source, strictMode, scriptMode, codeGenerationMode, error, evalContextType); 177 178 if (unlinkedCodeBlock && Options::useCodeCache()) { 179 m_sourceCode.addCache(key, SourceCodeValue(vm, unlinkedCodeBlock, m_sourceCode.age())); 180 181 key.source().provider().cacheBytecode([&] { 182 return encodeCodeBlock(vm, key, unlinkedCodeBlock); 183 }); 184 } 185 186 return unlinkedCodeBlock; 187 } 188 189 UnlinkedProgramCodeBlock* CodeCache::getUnlinkedProgramCodeBlock(VM& vm, ProgramExecutable* executable, const SourceCode& source, JSParserStrictMode strictMode, OptionSet<CodeGenerationMode> codeGenerationMode, ParserError& error) 190 { 191 return getUnlinkedGlobalCodeBlock<UnlinkedProgramCodeBlock>(vm, executable, source, strictMode, JSParserScriptMode::Classic, codeGenerationMode, error, EvalContextType::None); 192 } 193 194 UnlinkedEvalCodeBlock* CodeCache::getUnlinkedEvalCodeBlock(VM& vm, IndirectEvalExecutable* executable, const SourceCode& source, JSParserStrictMode strictMode, OptionSet<CodeGenerationMode> codeGenerationMode, ParserError& error, EvalContextType evalContextType) 195 { 196 return getUnlinkedGlobalCodeBlock<UnlinkedEvalCodeBlock>(vm, executable, source, strictMode, JSParserScriptMode::Classic, codeGenerationMode, error, evalContextType); 197 } 198 199 UnlinkedModuleProgramCodeBlock* CodeCache::getUnlinkedModuleProgramCodeBlock(VM& vm, ModuleProgramExecutable* executable, const SourceCode& source, OptionSet<CodeGenerationMode> codeGenerationMode, ParserError& error) 200 { 201 return getUnlinkedGlobalCodeBlock<UnlinkedModuleProgramCodeBlock>(vm, executable, source, JSParserStrictMode::Strict, JSParserScriptMode::Module, codeGenerationMode, error, EvalContextType::None); 202 } 203 204 UnlinkedFunctionExecutable* CodeCache::getUnlinkedGlobalFunctionExecutable(VM& vm, const Identifier& name, const SourceCode& source, OptionSet<CodeGenerationMode> codeGenerationMode, Optional<int> functionConstructorParametersEndPosition, ParserError& error) 205 { 206 bool isArrowFunctionContext = false; 207 SourceCodeKey key( 208 source, name.string(), SourceCodeType::FunctionType, 209 JSParserStrictMode::NotStrict, 210 JSParserScriptMode::Classic, 211 DerivedContextType::None, 212 EvalContextType::None, 213 isArrowFunctionContext, 214 codeGenerationMode, 215 functionConstructorParametersEndPosition); 216 UnlinkedFunctionExecutable* executable = m_sourceCode.findCacheAndUpdateAge<UnlinkedFunctionExecutable>(vm, key); 217 if (executable && Options::useCodeCache()) { 218 if (!executable->sourceURLDirective().isNull()) 219 source.provider()->setSourceURLDirective(executable->sourceURLDirective()); 220 if (!executable->sourceMappingURLDirective().isNull()) 221 source.provider()->setSourceMappingURLDirective(executable->sourceMappingURLDirective()); 222 return executable; 223 } 224 225 JSTextPosition positionBeforeLastNewline; 226 std::unique_ptr<ProgramNode> program = parseFunctionForFunctionConstructor(vm, source, error, &positionBeforeLastNewline, functionConstructorParametersEndPosition); 227 if (!program) { 228 RELEASE_ASSERT(error.isValid()); 229 return nullptr; 230 } 231 232 // This function assumes an input string that would result in a single function declaration. 233 StatementNode* funcDecl = program->singleStatement(); 234 if (UNLIKELY(!funcDecl)) { 235 JSToken token; 236 error = ParserError(ParserError::SyntaxError, ParserError::SyntaxErrorIrrecoverable, token, "Parser error", -1); 237 return nullptr; 238 } 239 ASSERT(funcDecl->isFuncDeclNode()); 240 241 FunctionMetadataNode* metadata = static_cast<FuncDeclNode*>(funcDecl)->metadata(); 242 ASSERT(metadata); 243 if (!metadata) 244 return nullptr; 245 246 metadata->overrideName(name); 247 metadata->setEndPosition(positionBeforeLastNewline); 248 // The Function constructor only has access to global variables, so no variables will be under TDZ unless they're 249 // in the global lexical environment, which we always TDZ check accesses from. 250 ConstructAbility constructAbility = constructAbilityForParseMode(metadata->parseMode()); 251 UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(vm, source, metadata, UnlinkedNormalFunction, constructAbility, JSParserScriptMode::Classic, nullptr, DerivedContextType::None, NeedsClassFieldInitializer::No); 252 253 if (!source.provider()->sourceURLDirective().isNull()) 254 functionExecutable->setSourceURLDirective(source.provider()->sourceURLDirective()); 255 if (!source.provider()->sourceMappingURLDirective().isNull()) 256 functionExecutable->setSourceMappingURLDirective(source.provider()->sourceMappingURLDirective()); 257 258 if (Options::useCodeCache()) 259 m_sourceCode.addCache(key, SourceCodeValue(vm, functionExecutable, m_sourceCode.age())); 260 return functionExecutable; 261 } 262 263 void CodeCache::updateCache(const UnlinkedFunctionExecutable* executable, const SourceCode& parentSource, CodeSpecializationKind kind, const UnlinkedFunctionCodeBlock* codeBlock) 264 { 265 parentSource.provider()->updateCache(executable, parentSource, kind, codeBlock); 266 } 267 268 void CodeCache::write(VM& vm) 269 { 270 for (auto& it : m_sourceCode) 271 writeCodeBlock(vm, it.key, it.value); 272 } 273 274 void writeCodeBlock(VM& vm, const SourceCodeKey& key, const SourceCodeValue& value) 275 { 276 UnlinkedCodeBlock* codeBlock = jsDynamicCast<UnlinkedCodeBlock*>(vm, value.cell.get()); 277 if (!codeBlock) 278 return; 279 280 key.source().provider().commitCachedBytecode(); 281 } 282 283 static SourceCodeKey sourceCodeKeyForSerializedBytecode(VM&, const SourceCode& sourceCode, SourceCodeType codeType, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, OptionSet<CodeGenerationMode> codeGenerationMode) 284 { 285 return SourceCodeKey( 286 sourceCode, String(), codeType, strictMode, scriptMode, 287 DerivedContextType::None, EvalContextType::None, false, codeGenerationMode, 288 WTF::nullopt); 289 } 290 291 SourceCodeKey sourceCodeKeyForSerializedProgram(VM& vm, const SourceCode& sourceCode) 292 { 293 JSParserStrictMode strictMode = JSParserStrictMode::NotStrict; 294 JSParserScriptMode scriptMode = JSParserScriptMode::Classic; 295 return sourceCodeKeyForSerializedBytecode(vm, sourceCode, SourceCodeType::ProgramType, strictMode, scriptMode, { }); 296 } 297 298 SourceCodeKey sourceCodeKeyForSerializedModule(VM& vm, const SourceCode& sourceCode) 299 { 300 JSParserStrictMode strictMode = JSParserStrictMode::Strict; 301 JSParserScriptMode scriptMode = JSParserScriptMode::Module; 302 return sourceCodeKeyForSerializedBytecode(vm, sourceCode, SourceCodeType::ModuleType, strictMode, scriptMode, { }); 303 } 304 305 RefPtr<CachedBytecode> serializeBytecode(VM& vm, UnlinkedCodeBlock* codeBlock, const SourceCode& source, SourceCodeType codeType, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, FileSystem::PlatformFileHandle fd, BytecodeCacheError& error, OptionSet<CodeGenerationMode> codeGenerationMode) 306 { 307 return encodeCodeBlock(vm, 308 sourceCodeKeyForSerializedBytecode(vm, source, codeType, strictMode, scriptMode, codeGenerationMode), codeBlock, fd, error); 309 } 310 311 }