]> git.saurik.com Git - apple/javascriptcore.git/blob - runtime/CodeCache.cpp
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / runtime / CodeCache.cpp
1 /*
2 * Copyright (C) 2012 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
28 #include "CodeCache.h"
29
30 #include "BytecodeGenerator.h"
31 #include "CodeSpecializationKind.h"
32 #include "JSCInlines.h"
33 #include "Parser.h"
34 #include "StrongInlines.h"
35 #include "UnlinkedCodeBlock.h"
36
37 namespace JSC {
38
39 const double CodeCacheMap::workingSetTime = 10.0;
40
41 void CodeCacheMap::pruneSlowCase()
42 {
43 m_minCapacity = std::max(m_size - m_sizeAtLastPrune, static_cast<int64_t>(0));
44 m_sizeAtLastPrune = m_size;
45 m_timeAtLastPrune = monotonicallyIncreasingTime();
46
47 if (m_capacity < m_minCapacity)
48 m_capacity = m_minCapacity;
49
50 while (m_size > m_capacity || !canPruneQuickly()) {
51 MapType::iterator it = m_map.begin();
52 m_size -= it->key.length();
53 m_map.remove(it);
54 }
55 }
56
57 CodeCache::CodeCache()
58 {
59 }
60
61 CodeCache::~CodeCache()
62 {
63 }
64
65 template <typename T> struct CacheTypes { };
66
67 template <> struct CacheTypes<UnlinkedProgramCodeBlock> {
68 typedef JSC::ProgramNode RootNode;
69 static const SourceCodeKey::CodeType codeType = SourceCodeKey::ProgramType;
70 };
71
72 template <> struct CacheTypes<UnlinkedEvalCodeBlock> {
73 typedef JSC::EvalNode RootNode;
74 static const SourceCodeKey::CodeType codeType = SourceCodeKey::EvalType;
75 };
76
77 template <class UnlinkedCodeBlockType, class ExecutableType>
78 UnlinkedCodeBlockType* CodeCache::getGlobalCodeBlock(VM& vm, ExecutableType* executable, const SourceCode& source, JSParserBuiltinMode builtinMode,
79 JSParserStrictMode strictMode, ThisTDZMode thisTDZMode, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
80 {
81 SourceCodeKey key = SourceCodeKey(source, String(), CacheTypes<UnlinkedCodeBlockType>::codeType, builtinMode, strictMode, thisTDZMode);
82 SourceCodeValue* cache = m_sourceCode.findCacheAndUpdateAge(key);
83 bool canCache = debuggerMode == DebuggerOff && profilerMode == ProfilerOff && !vm.typeProfiler() && !vm.controlFlowProfiler();
84 if (cache && canCache) {
85 UnlinkedCodeBlockType* unlinkedCodeBlock = jsCast<UnlinkedCodeBlockType*>(cache->cell.get());
86 unsigned firstLine = source.firstLine() + unlinkedCodeBlock->firstLine();
87 unsigned lineCount = unlinkedCodeBlock->lineCount();
88 unsigned startColumn = unlinkedCodeBlock->startColumn() + source.startColumn();
89 bool endColumnIsOnStartLine = !lineCount;
90 unsigned endColumn = unlinkedCodeBlock->endColumn() + (endColumnIsOnStartLine ? startColumn : 1);
91 executable->recordParse(unlinkedCodeBlock->codeFeatures(), unlinkedCodeBlock->hasCapturedVariables(), firstLine, firstLine + lineCount, startColumn, endColumn);
92 return unlinkedCodeBlock;
93 }
94
95 typedef typename CacheTypes<UnlinkedCodeBlockType>::RootNode RootNode;
96 std::unique_ptr<RootNode> rootNode = parse<RootNode>(
97 &vm, source, 0, Identifier(), builtinMode, strictMode,
98 JSParserCodeType::Program, error, 0, ConstructorKind::None, thisTDZMode);
99 if (!rootNode)
100 return nullptr;
101
102 unsigned lineCount = rootNode->lastLine() - rootNode->firstLine();
103 unsigned startColumn = rootNode->startColumn() + 1;
104 bool endColumnIsOnStartLine = !lineCount;
105 unsigned unlinkedEndColumn = rootNode->endColumn();
106 unsigned endColumn = unlinkedEndColumn + (endColumnIsOnStartLine ? startColumn : 1);
107 executable->recordParse(rootNode->features(), rootNode->hasCapturedVariables(), rootNode->firstLine(), rootNode->lastLine(), startColumn, endColumn);
108
109 UnlinkedCodeBlockType* unlinkedCodeBlock = UnlinkedCodeBlockType::create(&vm, executable->executableInfo());
110 unlinkedCodeBlock->recordParse(rootNode->features(), rootNode->hasCapturedVariables(), rootNode->firstLine() - source.firstLine(), lineCount, unlinkedEndColumn);
111
112 auto generator = std::make_unique<BytecodeGenerator>(vm, rootNode.get(), unlinkedCodeBlock, debuggerMode, profilerMode);
113 error = generator->generate();
114 if (error.isValid())
115 return nullptr;
116
117 if (!canCache)
118 return unlinkedCodeBlock;
119
120 m_sourceCode.addCache(key, SourceCodeValue(vm, unlinkedCodeBlock, m_sourceCode.age()));
121 return unlinkedCodeBlock;
122 }
123
124 UnlinkedProgramCodeBlock* CodeCache::getProgramCodeBlock(VM& vm, ProgramExecutable* executable, const SourceCode& source, JSParserBuiltinMode builtinMode, JSParserStrictMode strictMode, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
125 {
126 return getGlobalCodeBlock<UnlinkedProgramCodeBlock>(vm, executable, source, builtinMode, strictMode, ThisTDZMode::CheckIfNeeded, debuggerMode, profilerMode, error);
127 }
128
129 UnlinkedEvalCodeBlock* CodeCache::getEvalCodeBlock(VM& vm, EvalExecutable* executable, const SourceCode& source, JSParserBuiltinMode builtinMode, JSParserStrictMode strictMode, ThisTDZMode thisTDZMode, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
130 {
131 return getGlobalCodeBlock<UnlinkedEvalCodeBlock>(vm, executable, source, builtinMode, strictMode, thisTDZMode, debuggerMode, profilerMode, error);
132 }
133
134 // FIXME: There's no need to add the function's name to the key here. It's already in the source code.
135 UnlinkedFunctionExecutable* CodeCache::getFunctionExecutableFromGlobalCode(VM& vm, const Identifier& name, const SourceCode& source, ParserError& error)
136 {
137 SourceCodeKey key = SourceCodeKey(
138 source, name.string(), SourceCodeKey::FunctionType,
139 JSParserBuiltinMode::NotBuiltin,
140 JSParserStrictMode::NotStrict);
141 SourceCodeValue* cache = m_sourceCode.findCacheAndUpdateAge(key);
142 if (cache)
143 return jsCast<UnlinkedFunctionExecutable*>(cache->cell.get());
144
145 JSTextPosition positionBeforeLastNewline;
146 std::unique_ptr<ProgramNode> program = parse<ProgramNode>(
147 &vm, source, 0, Identifier(), JSParserBuiltinMode::NotBuiltin,
148 JSParserStrictMode::NotStrict, JSParserCodeType::Program,
149 error, &positionBeforeLastNewline);
150 if (!program) {
151 RELEASE_ASSERT(error.isValid());
152 return nullptr;
153 }
154
155 // This function assumes an input string that would result in a single function declaration.
156 StatementNode* statement = program->singleStatement();
157 ASSERT(statement);
158 ASSERT(statement->isBlock());
159 if (!statement || !statement->isBlock())
160 return nullptr;
161
162 StatementNode* funcDecl = static_cast<BlockNode*>(statement)->singleStatement();
163 ASSERT(funcDecl);
164 ASSERT(funcDecl->isFuncDeclNode());
165 if (!funcDecl || !funcDecl->isFuncDeclNode())
166 return nullptr;
167
168 FunctionBodyNode* body = static_cast<FuncDeclNode*>(funcDecl)->body();
169 ASSERT(body);
170 if (!body)
171 return nullptr;
172
173 body->setEndPosition(positionBeforeLastNewline);
174 UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, body, UnlinkedNormalFunction);
175 functionExecutable->m_nameValue.set(vm, functionExecutable, jsString(&vm, name.string()));
176
177 m_sourceCode.addCache(key, SourceCodeValue(vm, functionExecutable, m_sourceCode.age()));
178 return functionExecutable;
179 }
180
181 }