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 "JSCInlines.h" 33#include "Parser.h" 34#include "StrongInlines.h" 35#include "UnlinkedCodeBlock.h" 36 37namespace JSC { 38 39const double CodeCacheMap::workingSetTime = 10.0; 40 41void CodeCacheMap::pruneSlowCase() 42{ 43 m_minCapacity = std::max(m_size - m_sizeAtLastPrune, static_cast<int64_t>(0)); 44 m_sizeAtLastPrune = m_size; 45 m_timeAtLastPrune = monotonicallyIncreasingTime(); 46 47 if (m_capacity < m_minCapacity) 48 m_capacity = m_minCapacity; 49 50 while (m_size > m_capacity || !canPruneQuickly()) { 51 MapType::iterator it = m_map.begin(); 52 m_size -= it->key.length(); 53 m_map.remove(it); 54 } 55} 56 57CodeCache::CodeCache() 58{ 59} 60 61CodeCache::~CodeCache() 62{ 63} 64 65template <typename T> struct CacheTypes { }; 66 67template <> struct CacheTypes<UnlinkedProgramCodeBlock> { 68 typedef JSC::ProgramNode RootNode; 69 static const SourceCodeKey::CodeType codeType = SourceCodeKey::ProgramType; 70}; 71 72template <> struct CacheTypes<UnlinkedEvalCodeBlock> { 73 typedef JSC::EvalNode RootNode; 74 static const SourceCodeKey::CodeType codeType = SourceCodeKey::EvalType; 75}; 76 77template <class UnlinkedCodeBlockType, class ExecutableType> 78UnlinkedCodeBlockType* CodeCache::getGlobalCodeBlock(VM& vm, ExecutableType* executable, const SourceCode& source, JSParserStrictness strictness, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error) 79{ 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; 92 } 93 94 typedef typename CacheTypes<UnlinkedCodeBlockType>::RootNode RootNode; 95 RefPtr<RootNode> rootNode = parse<RootNode>(&vm, source, 0, Identifier(), strictness, JSParseProgramCode, error); 96 if (!rootNode) { 97 m_sourceCode.remove(addResult.iterator); 98 return 0; 99 } 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); 106 107 UnlinkedCodeBlockType* unlinkedCodeBlock = UnlinkedCodeBlockType::create(&vm, executable->executableInfo()); 108 unlinkedCodeBlock->recordParse(rootNode->features(), rootNode->hasCapturedVariables(), rootNode->lineNo() - source.firstLine(), lineCount, unlinkedEndColumn); 109 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); 115 return 0; 116 } 117 118 if (!canCache) { 119 m_sourceCode.remove(addResult.iterator); 120 return unlinkedCodeBlock; 121 } 122 123 addResult.iterator->value = SourceCodeValue(vm, unlinkedCodeBlock, m_sourceCode.age()); 124 return unlinkedCodeBlock; 125} 126 127UnlinkedProgramCodeBlock* CodeCache::getProgramCodeBlock(VM& vm, ProgramExecutable* executable, const SourceCode& source, JSParserStrictness strictness, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error) 128{ 129 return getGlobalCodeBlock<UnlinkedProgramCodeBlock>(vm, executable, source, strictness, debuggerMode, profilerMode, error); 130} 131 132UnlinkedEvalCodeBlock* CodeCache::getEvalCodeBlock(VM& vm, EvalExecutable* executable, const SourceCode& source, JSParserStrictness strictness, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error) 133{ 134 return getGlobalCodeBlock<UnlinkedEvalCodeBlock>(vm, executable, source, strictness, debuggerMode, profilerMode, error); 135} 136 137UnlinkedFunctionExecutable* CodeCache::getFunctionExecutableFromGlobalCode(VM& vm, const Identifier& name, const SourceCode& source, ParserError& error) 138{ 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()); 143 144 JSTextPosition positionBeforeLastNewline; 145 RefPtr<ProgramNode> program = parse<ProgramNode>(&vm, source, 0, Identifier(), JSParseNormal, JSParseProgramCode, error, &positionBeforeLastNewline); 146 if (!program) { 147 RELEASE_ASSERT(error.m_type != ParserError::ErrorNone); 148 m_sourceCode.remove(addResult.iterator); 149 return 0; 150 } 151 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()); 161 162 body->setEndPosition(positionBeforeLastNewline); 163 RELEASE_ASSERT(body); 164 RELEASE_ASSERT(body->ident().isNull()); 165 166 UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, body, UnlinkedNormalFunction); 167 functionExecutable->m_nameValue.set(vm, functionExecutable, jsString(&vm, name.string())); 168 169 addResult.iterator->value = SourceCodeValue(vm, functionExecutable, m_sourceCode.age()); 170 return functionExecutable; 171} 172 173} 174