2 * Copyright (C) 2012 Apple Inc. All Rights Reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
28 #include "CodeCache.h"
30 #include "BytecodeGenerator.h"
31 #include "CodeSpecializationKind.h"
32 #include "JSCInlines.h"
34 #include "StrongInlines.h"
35 #include "UnlinkedCodeBlock.h"
39 const double CodeCacheMap::workingSetTime
= 10.0;
41 void CodeCacheMap::pruneSlowCase()
43 m_minCapacity
= std::max(m_size
- m_sizeAtLastPrune
, static_cast<int64_t>(0));
44 m_sizeAtLastPrune
= m_size
;
45 m_timeAtLastPrune
= monotonicallyIncreasingTime();
47 if (m_capacity
< m_minCapacity
)
48 m_capacity
= m_minCapacity
;
50 while (m_size
> m_capacity
|| !canPruneQuickly()) {
51 MapType::iterator it
= m_map
.begin();
52 m_size
-= it
->key
.length();
57 CodeCache::CodeCache()
61 CodeCache::~CodeCache()
65 template <typename T
> struct CacheTypes
{ };
67 template <> struct CacheTypes
<UnlinkedProgramCodeBlock
> {
68 typedef JSC::ProgramNode RootNode
;
69 static const SourceCodeKey::CodeType codeType
= SourceCodeKey::ProgramType
;
72 template <> struct CacheTypes
<UnlinkedEvalCodeBlock
> {
73 typedef JSC::EvalNode RootNode
;
74 static const SourceCodeKey::CodeType codeType
= SourceCodeKey::EvalType
;
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
)
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
;
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
);
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
);
109 UnlinkedCodeBlockType
* unlinkedCodeBlock
= UnlinkedCodeBlockType::create(&vm
, executable
->executableInfo());
110 unlinkedCodeBlock
->recordParse(rootNode
->features(), rootNode
->hasCapturedVariables(), rootNode
->firstLine() - source
.firstLine(), lineCount
, unlinkedEndColumn
);
112 auto generator
= std::make_unique
<BytecodeGenerator
>(vm
, rootNode
.get(), unlinkedCodeBlock
, debuggerMode
, profilerMode
);
113 error
= generator
->generate();
118 return unlinkedCodeBlock
;
120 m_sourceCode
.addCache(key
, SourceCodeValue(vm
, unlinkedCodeBlock
, m_sourceCode
.age()));
121 return unlinkedCodeBlock
;
124 UnlinkedProgramCodeBlock
* CodeCache::getProgramCodeBlock(VM
& vm
, ProgramExecutable
* executable
, const SourceCode
& source
, JSParserBuiltinMode builtinMode
, JSParserStrictMode strictMode
, DebuggerMode debuggerMode
, ProfilerMode profilerMode
, ParserError
& error
)
126 return getGlobalCodeBlock
<UnlinkedProgramCodeBlock
>(vm
, executable
, source
, builtinMode
, strictMode
, ThisTDZMode::CheckIfNeeded
, debuggerMode
, profilerMode
, error
);
129 UnlinkedEvalCodeBlock
* CodeCache::getEvalCodeBlock(VM
& vm
, EvalExecutable
* executable
, const SourceCode
& source
, JSParserBuiltinMode builtinMode
, JSParserStrictMode strictMode
, ThisTDZMode thisTDZMode
, DebuggerMode debuggerMode
, ProfilerMode profilerMode
, ParserError
& error
)
131 return getGlobalCodeBlock
<UnlinkedEvalCodeBlock
>(vm
, executable
, source
, builtinMode
, strictMode
, thisTDZMode
, debuggerMode
, profilerMode
, error
);
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
)
137 SourceCodeKey key
= SourceCodeKey(
138 source
, name
.string(), SourceCodeKey::FunctionType
,
139 JSParserBuiltinMode::NotBuiltin
,
140 JSParserStrictMode::NotStrict
);
141 SourceCodeValue
* cache
= m_sourceCode
.findCacheAndUpdateAge(key
);
143 return jsCast
<UnlinkedFunctionExecutable
*>(cache
->cell
.get());
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
);
151 RELEASE_ASSERT(error
.isValid());
155 // This function assumes an input string that would result in a single function declaration.
156 StatementNode
* statement
= program
->singleStatement();
158 ASSERT(statement
->isBlock());
159 if (!statement
|| !statement
->isBlock())
162 StatementNode
* funcDecl
= static_cast<BlockNode
*>(statement
)->singleStatement();
164 ASSERT(funcDecl
->isFuncDeclNode());
165 if (!funcDecl
|| !funcDecl
->isFuncDeclNode())
168 FunctionBodyNode
* body
= static_cast<FuncDeclNode
*>(funcDecl
)->body();
173 body
->setEndPosition(positionBeforeLastNewline
);
174 UnlinkedFunctionExecutable
* functionExecutable
= UnlinkedFunctionExecutable::create(&vm
, source
, body
, UnlinkedNormalFunction
);
175 functionExecutable
->m_nameValue
.set(vm
, functionExecutable
, jsString(&vm
, name
.string()));
177 m_sourceCode
.addCache(key
, SourceCodeValue(vm
, functionExecutable
, m_sourceCode
.age()));
178 return functionExecutable
;