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 "Operations.h"
34 #include "StrongInlines.h"
35 #include "UnlinkedCodeBlock.h"
39 const double CodeCacheMap::workingSetTime
= 10.0;
40 const int64_t CodeCacheMap::globalWorkingSetMaxBytes
= 16000000;
41 const size_t CodeCacheMap::globalWorkingSetMaxEntries
= 2000;
42 const unsigned CodeCacheMap::nonGlobalWorkingSetScale
= 20;
43 const int64_t CodeCacheMap::nonGlobalWorkingSetMaxBytes
= CodeCacheMap::globalWorkingSetMaxBytes
/ CodeCacheMap::nonGlobalWorkingSetScale
;
44 const size_t CodeCacheMap::nonGlobalWorkingSetMaxEntries
= CodeCacheMap::globalWorkingSetMaxEntries
/ CodeCacheMap::nonGlobalWorkingSetScale
;
46 void CodeCacheMap::pruneSlowCase()
48 m_minCapacity
= std::max(m_size
- m_sizeAtLastPrune
, static_cast<int64_t>(0));
49 m_sizeAtLastPrune
= m_size
;
50 m_timeAtLastPrune
= monotonicallyIncreasingTime();
52 if (m_capacity
< m_minCapacity
)
53 m_capacity
= m_minCapacity
;
55 while (m_size
> m_capacity
|| !canPruneQuickly()) {
56 MapType::iterator it
= m_map
.begin();
57 m_size
-= it
->key
.length();
62 CodeCache::CodeCache(CodeCacheKind kind
)
63 : m_sourceCode(kind
== GlobalCodeCache
? CodeCacheMap::globalWorkingSetMaxBytes
: CodeCacheMap::nonGlobalWorkingSetMaxBytes
,
64 kind
== GlobalCodeCache
? CodeCacheMap::globalWorkingSetMaxEntries
: CodeCacheMap::nonGlobalWorkingSetMaxEntries
)
68 CodeCache::~CodeCache()
72 template <typename T
> struct CacheTypes
{ };
74 template <> struct CacheTypes
<UnlinkedProgramCodeBlock
> {
75 typedef JSC::ProgramNode RootNode
;
76 static const SourceCodeKey::CodeType codeType
= SourceCodeKey::ProgramType
;
79 template <> struct CacheTypes
<UnlinkedEvalCodeBlock
> {
80 typedef JSC::EvalNode RootNode
;
81 static const SourceCodeKey::CodeType codeType
= SourceCodeKey::EvalType
;
84 template <class UnlinkedCodeBlockType
, class ExecutableType
>
85 UnlinkedCodeBlockType
* CodeCache::generateBytecode(VM
& vm
, JSScope
* scope
, ExecutableType
* executable
, const SourceCode
& source
, JSParserStrictness strictness
, DebuggerMode debuggerMode
, ProfilerMode profilerMode
, ParserError
& error
)
87 typedef typename CacheTypes
<UnlinkedCodeBlockType
>::RootNode RootNode
;
88 RefPtr
<RootNode
> rootNode
= parse
<RootNode
>(&vm
, source
, 0, Identifier(), strictness
, JSParseProgramCode
, error
);
91 executable
->recordParse(rootNode
->features(), rootNode
->hasCapturedVariables(), rootNode
->lineNo(), rootNode
->lastLine(), rootNode
->startColumn());
93 UnlinkedCodeBlockType
* unlinkedCode
= UnlinkedCodeBlockType::create(&vm
, executable
->executableInfo());
94 unlinkedCode
->recordParse(rootNode
->features(), rootNode
->hasCapturedVariables(), rootNode
->lineNo() - source
.firstLine(), rootNode
->lastLine() - rootNode
->lineNo());
95 OwnPtr
<BytecodeGenerator
> generator(adoptPtr(new BytecodeGenerator(vm
, scope
, rootNode
.get(), unlinkedCode
, debuggerMode
, profilerMode
)));
96 error
= generator
->generate();
97 rootNode
->destroyData();
98 if (error
.m_type
!= ParserError::ErrorNone
)
103 template <class UnlinkedCodeBlockType
, class ExecutableType
>
104 UnlinkedCodeBlockType
* CodeCache::getCodeBlock(VM
& vm
, JSScope
* scope
, ExecutableType
* executable
, const SourceCode
& source
, JSParserStrictness strictness
, DebuggerMode debuggerMode
, ProfilerMode profilerMode
, ParserError
& error
)
106 // We completely skip the cache if we're an eval that isn't at the top of the scope chain.
107 if (CacheTypes
<UnlinkedCodeBlockType
>::codeType
== SourceCodeKey::EvalType
) {
108 if (scope
->next() && !scope
->isActivationObject())
109 return generateBytecode
<UnlinkedCodeBlockType
, ExecutableType
>(vm
, scope
, executable
, source
, strictness
, debuggerMode
, profilerMode
, error
);
112 SourceCodeKey key
= SourceCodeKey(source
, String(), CacheTypes
<UnlinkedCodeBlockType
>::codeType
, strictness
);
113 CodeCacheMap::AddResult addResult
= m_sourceCode
.add(key
, SourceCodeValue());
114 bool canCache
= debuggerMode
== DebuggerOff
&& profilerMode
== ProfilerOff
;
116 if (!addResult
.isNewEntry
&& canCache
) {
117 UnlinkedCodeBlockType
* unlinkedCode
= jsCast
<UnlinkedCodeBlockType
*>(addResult
.iterator
->value
.cell
.get());
118 unsigned firstLine
= source
.firstLine() + unlinkedCode
->firstLine();
119 unsigned startColumn
= source
.firstLine() ? source
.startColumn() : 0;
120 executable
->recordParse(unlinkedCode
->codeFeatures(), unlinkedCode
->hasCapturedVariables(), firstLine
, firstLine
+ unlinkedCode
->lineCount(), startColumn
);
123 UnlinkedCodeBlockType
* unlinkedCode
= generateBytecode
<UnlinkedCodeBlockType
, ExecutableType
>(vm
, scope
, executable
, source
, strictness
, debuggerMode
, profilerMode
, error
);
125 if (!canCache
|| !unlinkedCode
) {
126 m_sourceCode
.remove(addResult
.iterator
);
130 addResult
.iterator
->value
= SourceCodeValue(vm
, unlinkedCode
, m_sourceCode
.age());
134 UnlinkedProgramCodeBlock
* CodeCache::getProgramCodeBlock(VM
& vm
, ProgramExecutable
* executable
, const SourceCode
& source
, JSParserStrictness strictness
, DebuggerMode debuggerMode
, ProfilerMode profilerMode
, ParserError
& error
)
136 return getCodeBlock
<UnlinkedProgramCodeBlock
>(vm
, 0, executable
, source
, strictness
, debuggerMode
, profilerMode
, error
);
139 UnlinkedEvalCodeBlock
* CodeCache::getEvalCodeBlock(VM
& vm
, JSScope
* scope
, EvalExecutable
* executable
, const SourceCode
& source
, JSParserStrictness strictness
, DebuggerMode debuggerMode
, ProfilerMode profilerMode
, ParserError
& error
)
141 return getCodeBlock
<UnlinkedEvalCodeBlock
>(vm
, scope
, executable
, source
, strictness
, debuggerMode
, profilerMode
, error
);
144 UnlinkedFunctionExecutable
* CodeCache::getFunctionExecutableFromGlobalCode(VM
& vm
, const Identifier
& name
, const SourceCode
& source
, ParserError
& error
)
146 SourceCodeKey key
= SourceCodeKey(source
, name
.string(), SourceCodeKey::FunctionType
, JSParseNormal
);
147 CodeCacheMap::AddResult addResult
= m_sourceCode
.add(key
, SourceCodeValue());
148 if (!addResult
.isNewEntry
)
149 return jsCast
<UnlinkedFunctionExecutable
*>(addResult
.iterator
->value
.cell
.get());
151 RefPtr
<ProgramNode
> program
= parse
<ProgramNode
>(&vm
, source
, 0, Identifier(), JSParseNormal
, JSParseProgramCode
, error
);
153 ASSERT(error
.m_type
!= ParserError::ErrorNone
);
154 m_sourceCode
.remove(addResult
.iterator
);
158 // This function assumes an input string that would result in a single anonymous function expression.
159 StatementNode
* exprStatement
= program
->singleStatement();
160 ASSERT(exprStatement
);
161 ASSERT(exprStatement
->isExprStatement());
162 ExpressionNode
* funcExpr
= static_cast<ExprStatementNode
*>(exprStatement
)->expr();
164 RELEASE_ASSERT(funcExpr
->isFuncExprNode());
165 FunctionBodyNode
* body
= static_cast<FuncExprNode
*>(funcExpr
)->body();
167 ASSERT(body
->ident().isNull());
169 UnlinkedFunctionExecutable
* functionExecutable
= UnlinkedFunctionExecutable::create(&vm
, source
, body
);
170 functionExecutable
->m_nameValue
.set(vm
, functionExecutable
, jsString(&vm
, name
.string()));
172 addResult
.iterator
->value
= SourceCodeValue(vm
, functionExecutable
, m_sourceCode
.age());
173 return functionExecutable
;