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
, JSParserStrictness strictness
, DebuggerMode debuggerMode
, ProfilerMode profilerMode
, ParserError
& error
)
80 SourceCodeKey key
= SourceCodeKey(source
, String(), CacheTypes
<UnlinkedCodeBlockType
>::codeType
, strictness
);
81 CodeCacheMap::AddResult addResult
= m_sourceCode
.add(key
, SourceCodeValue());
82 bool canCache
= debuggerMode
== DebuggerOff
&& profilerMode
== ProfilerOff
;
83 if (!addResult
.isNewEntry
&& canCache
) {
84 UnlinkedCodeBlockType
* unlinkedCodeBlock
= jsCast
<UnlinkedCodeBlockType
*>(addResult
.iterator
->value
.cell
.get());
85 unsigned firstLine
= source
.firstLine() + unlinkedCodeBlock
->firstLine();
86 unsigned lineCount
= unlinkedCodeBlock
->lineCount();
87 unsigned startColumn
= unlinkedCodeBlock
->startColumn() + source
.startColumn();
88 bool endColumnIsOnStartLine
= !lineCount
;
89 unsigned endColumn
= unlinkedCodeBlock
->endColumn() + (endColumnIsOnStartLine
? startColumn
: 1);
90 executable
->recordParse(unlinkedCodeBlock
->codeFeatures(), unlinkedCodeBlock
->hasCapturedVariables(), firstLine
, firstLine
+ lineCount
, startColumn
, endColumn
);
91 return unlinkedCodeBlock
;
94 typedef typename CacheTypes
<UnlinkedCodeBlockType
>::RootNode RootNode
;
95 RefPtr
<RootNode
> rootNode
= parse
<RootNode
>(&vm
, source
, 0, Identifier(), strictness
, JSParseProgramCode
, error
);
97 m_sourceCode
.remove(addResult
.iterator
);
100 unsigned lineCount
= rootNode
->lastLine() - rootNode
->lineNo();
101 unsigned startColumn
= rootNode
->startColumn() + 1;
102 bool endColumnIsOnStartLine
= !lineCount
;
103 unsigned unlinkedEndColumn
= rootNode
->endColumn();
104 unsigned endColumn
= unlinkedEndColumn
+ (endColumnIsOnStartLine
? startColumn
: 1);
105 executable
->recordParse(rootNode
->features(), rootNode
->hasCapturedVariables(), rootNode
->lineNo(), rootNode
->lastLine(), startColumn
, endColumn
);
107 UnlinkedCodeBlockType
* unlinkedCodeBlock
= UnlinkedCodeBlockType::create(&vm
, executable
->executableInfo());
108 unlinkedCodeBlock
->recordParse(rootNode
->features(), rootNode
->hasCapturedVariables(), rootNode
->lineNo() - source
.firstLine(), lineCount
, unlinkedEndColumn
);
110 OwnPtr
<BytecodeGenerator
> generator(adoptPtr(new BytecodeGenerator(vm
, rootNode
.get(), unlinkedCodeBlock
, debuggerMode
, profilerMode
)));
111 error
= generator
->generate();
112 rootNode
->destroyData();
113 if (error
.m_type
!= ParserError::ErrorNone
) {
114 m_sourceCode
.remove(addResult
.iterator
);
119 m_sourceCode
.remove(addResult
.iterator
);
120 return unlinkedCodeBlock
;
123 addResult
.iterator
->value
= SourceCodeValue(vm
, unlinkedCodeBlock
, m_sourceCode
.age());
124 return unlinkedCodeBlock
;
127 UnlinkedProgramCodeBlock
* CodeCache::getProgramCodeBlock(VM
& vm
, ProgramExecutable
* executable
, const SourceCode
& source
, JSParserStrictness strictness
, DebuggerMode debuggerMode
, ProfilerMode profilerMode
, ParserError
& error
)
129 return getGlobalCodeBlock
<UnlinkedProgramCodeBlock
>(vm
, executable
, source
, strictness
, debuggerMode
, profilerMode
, error
);
132 UnlinkedEvalCodeBlock
* CodeCache::getEvalCodeBlock(VM
& vm
, EvalExecutable
* executable
, const SourceCode
& source
, JSParserStrictness strictness
, DebuggerMode debuggerMode
, ProfilerMode profilerMode
, ParserError
& error
)
134 return getGlobalCodeBlock
<UnlinkedEvalCodeBlock
>(vm
, executable
, source
, strictness
, debuggerMode
, profilerMode
, error
);
137 UnlinkedFunctionExecutable
* CodeCache::getFunctionExecutableFromGlobalCode(VM
& vm
, const Identifier
& name
, const SourceCode
& source
, ParserError
& error
)
139 SourceCodeKey key
= SourceCodeKey(source
, name
.string(), SourceCodeKey::FunctionType
, JSParseNormal
);
140 CodeCacheMap::AddResult addResult
= m_sourceCode
.add(key
, SourceCodeValue());
141 if (!addResult
.isNewEntry
)
142 return jsCast
<UnlinkedFunctionExecutable
*>(addResult
.iterator
->value
.cell
.get());
144 JSTextPosition positionBeforeLastNewline
;
145 RefPtr
<ProgramNode
> program
= parse
<ProgramNode
>(&vm
, source
, 0, Identifier(), JSParseNormal
, JSParseProgramCode
, error
, &positionBeforeLastNewline
);
147 RELEASE_ASSERT(error
.m_type
!= ParserError::ErrorNone
);
148 m_sourceCode
.remove(addResult
.iterator
);
152 // This function assumes an input string that would result in a single anonymous function expression.
153 StatementNode
* exprStatement
= program
->singleStatement();
154 RELEASE_ASSERT(exprStatement
);
155 RELEASE_ASSERT(exprStatement
->isExprStatement());
156 ExpressionNode
* funcExpr
= static_cast<ExprStatementNode
*>(exprStatement
)->expr();
157 RELEASE_ASSERT(funcExpr
);
158 RELEASE_ASSERT(funcExpr
->isFuncExprNode());
159 FunctionBodyNode
* body
= static_cast<FuncExprNode
*>(funcExpr
)->body();
160 RELEASE_ASSERT(!program
->hasCapturedVariables());
162 body
->setEndPosition(positionBeforeLastNewline
);
163 RELEASE_ASSERT(body
);
164 RELEASE_ASSERT(body
->ident().isNull());
166 UnlinkedFunctionExecutable
* functionExecutable
= UnlinkedFunctionExecutable::create(&vm
, source
, body
, UnlinkedNormalFunction
);
167 functionExecutable
->m_nameValue
.set(vm
, functionExecutable
, jsString(&vm
, name
.string()));
169 addResult
.iterator
->value
= SourceCodeValue(vm
, functionExecutable
, m_sourceCode
.age());
170 return functionExecutable
;