]> git.saurik.com Git - apple/javascriptcore.git/blame - runtime/CodeCache.cpp
JavaScriptCore-1218.33.tar.gz
[apple/javascriptcore.git] / runtime / CodeCache.cpp
CommitLineData
93a37866
A
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 "Operations.h"
33#include "Parser.h"
34#include "StrongInlines.h"
35#include "UnlinkedCodeBlock.h"
36
37namespace JSC {
38
39const double CodeCacheMap::workingSetTime = 10.0;
40const int64_t CodeCacheMap::globalWorkingSetMaxBytes = 16000000;
41const size_t CodeCacheMap::globalWorkingSetMaxEntries = 2000;
42const unsigned CodeCacheMap::nonGlobalWorkingSetScale = 20;
43const int64_t CodeCacheMap::nonGlobalWorkingSetMaxBytes = CodeCacheMap::globalWorkingSetMaxBytes / CodeCacheMap::nonGlobalWorkingSetScale;
44const size_t CodeCacheMap::nonGlobalWorkingSetMaxEntries = CodeCacheMap::globalWorkingSetMaxEntries / CodeCacheMap::nonGlobalWorkingSetScale;
45
46void CodeCacheMap::pruneSlowCase()
47{
48 m_minCapacity = std::max(m_size - m_sizeAtLastPrune, static_cast<int64_t>(0));
49 m_sizeAtLastPrune = m_size;
50 m_timeAtLastPrune = monotonicallyIncreasingTime();
51
52 if (m_capacity < m_minCapacity)
53 m_capacity = m_minCapacity;
54
55 while (m_size > m_capacity || !canPruneQuickly()) {
56 MapType::iterator it = m_map.begin();
57 m_size -= it->key.length();
58 m_map.remove(it);
59 }
60}
61
62CodeCache::CodeCache(CodeCacheKind kind)
63: m_sourceCode(kind == GlobalCodeCache ? CodeCacheMap::globalWorkingSetMaxBytes : CodeCacheMap::nonGlobalWorkingSetMaxBytes,
64 kind == GlobalCodeCache ? CodeCacheMap::globalWorkingSetMaxEntries : CodeCacheMap::nonGlobalWorkingSetMaxEntries)
65{
66}
67
68CodeCache::~CodeCache()
69{
70}
71
72template <typename T> struct CacheTypes { };
73
74template <> struct CacheTypes<UnlinkedProgramCodeBlock> {
75 typedef JSC::ProgramNode RootNode;
76 static const SourceCodeKey::CodeType codeType = SourceCodeKey::ProgramType;
77};
78
79template <> struct CacheTypes<UnlinkedEvalCodeBlock> {
80 typedef JSC::EvalNode RootNode;
81 static const SourceCodeKey::CodeType codeType = SourceCodeKey::EvalType;
82};
83
84template <class UnlinkedCodeBlockType, class ExecutableType>
85UnlinkedCodeBlockType* CodeCache::generateBytecode(VM& vm, JSScope* scope, ExecutableType* executable, const SourceCode& source, JSParserStrictness strictness, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
86{
87 typedef typename CacheTypes<UnlinkedCodeBlockType>::RootNode RootNode;
88 RefPtr<RootNode> rootNode = parse<RootNode>(&vm, source, 0, Identifier(), strictness, JSParseProgramCode, error);
89 if (!rootNode)
90 return 0;
91 executable->recordParse(rootNode->features(), rootNode->hasCapturedVariables(), rootNode->lineNo(), rootNode->lastLine(), rootNode->startColumn());
92
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)
99 return 0;
100 return unlinkedCode;
101}
102
103template <class UnlinkedCodeBlockType, class ExecutableType>
104UnlinkedCodeBlockType* CodeCache::getCodeBlock(VM& vm, JSScope* scope, ExecutableType* executable, const SourceCode& source, JSParserStrictness strictness, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
105{
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);
110 }
111
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;
115
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);
121 return unlinkedCode;
122 }
123 UnlinkedCodeBlockType* unlinkedCode = generateBytecode<UnlinkedCodeBlockType, ExecutableType>(vm, scope, executable, source, strictness, debuggerMode, profilerMode, error);
124
125 if (!canCache || !unlinkedCode) {
126 m_sourceCode.remove(addResult.iterator);
127 return unlinkedCode;
128 }
129
130 addResult.iterator->value = SourceCodeValue(vm, unlinkedCode, m_sourceCode.age());
131 return unlinkedCode;
132}
133
134UnlinkedProgramCodeBlock* CodeCache::getProgramCodeBlock(VM& vm, ProgramExecutable* executable, const SourceCode& source, JSParserStrictness strictness, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
135{
136 return getCodeBlock<UnlinkedProgramCodeBlock>(vm, 0, executable, source, strictness, debuggerMode, profilerMode, error);
137}
138
139UnlinkedEvalCodeBlock* CodeCache::getEvalCodeBlock(VM& vm, JSScope* scope, EvalExecutable* executable, const SourceCode& source, JSParserStrictness strictness, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
140{
141 return getCodeBlock<UnlinkedEvalCodeBlock>(vm, scope, executable, source, strictness, debuggerMode, profilerMode, error);
142}
143
144UnlinkedFunctionExecutable* CodeCache::getFunctionExecutableFromGlobalCode(VM& vm, const Identifier& name, const SourceCode& source, ParserError& error)
145{
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());
150
151 RefPtr<ProgramNode> program = parse<ProgramNode>(&vm, source, 0, Identifier(), JSParseNormal, JSParseProgramCode, error);
152 if (!program) {
153 ASSERT(error.m_type != ParserError::ErrorNone);
154 m_sourceCode.remove(addResult.iterator);
155 return 0;
156 }
157
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();
163 ASSERT(funcExpr);
164 RELEASE_ASSERT(funcExpr->isFuncExprNode());
165 FunctionBodyNode* body = static_cast<FuncExprNode*>(funcExpr)->body();
166 ASSERT(body);
167 ASSERT(body->ident().isNull());
168
169 UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, body);
170 functionExecutable->m_nameValue.set(vm, functionExecutable, jsString(&vm, name.string()));
171
172 addResult.iterator->value = SourceCodeValue(vm, functionExecutable, m_sourceCode.age());
173 return functionExecutable;
174}
175
176}