]>
Commit | Line | Data |
---|---|---|
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 | ||
37 | namespace JSC { | |
38 | ||
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; | |
45 | ||
46 | void 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 | ||
62 | CodeCache::CodeCache(CodeCacheKind kind) | |
63 | : m_sourceCode(kind == GlobalCodeCache ? CodeCacheMap::globalWorkingSetMaxBytes : CodeCacheMap::nonGlobalWorkingSetMaxBytes, | |
64 | kind == GlobalCodeCache ? CodeCacheMap::globalWorkingSetMaxEntries : CodeCacheMap::nonGlobalWorkingSetMaxEntries) | |
65 | { | |
66 | } | |
67 | ||
68 | CodeCache::~CodeCache() | |
69 | { | |
70 | } | |
71 | ||
72 | template <typename T> struct CacheTypes { }; | |
73 | ||
74 | template <> struct CacheTypes<UnlinkedProgramCodeBlock> { | |
75 | typedef JSC::ProgramNode RootNode; | |
76 | static const SourceCodeKey::CodeType codeType = SourceCodeKey::ProgramType; | |
77 | }; | |
78 | ||
79 | template <> struct CacheTypes<UnlinkedEvalCodeBlock> { | |
80 | typedef JSC::EvalNode RootNode; | |
81 | static const SourceCodeKey::CodeType codeType = SourceCodeKey::EvalType; | |
82 | }; | |
83 | ||
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) | |
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 | ||
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) | |
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 | ||
134 | UnlinkedProgramCodeBlock* 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 | ||
139 | UnlinkedEvalCodeBlock* 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 | ||
144 | UnlinkedFunctionExecutable* 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 | } |