diff options
Diffstat (limited to 'Source/JavaScriptCore/runtime/CodeCache.cpp')
-rw-r--r-- | Source/JavaScriptCore/runtime/CodeCache.cpp | 186 |
1 files changed, 186 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/runtime/CodeCache.cpp b/Source/JavaScriptCore/runtime/CodeCache.cpp new file mode 100644 index 000000000..4de760e49 --- /dev/null +++ b/Source/JavaScriptCore/runtime/CodeCache.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2012 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#include "CodeCache.h" + +#include "BytecodeGenerator.h" +#include "CodeSpecializationKind.h" +#include "Parser.h" +#include "StrongInlines.h" +#include "UnlinkedCodeBlock.h" + +namespace JSC { + +CodeCache::CodeCache() + : m_randomGenerator(static_cast<uint32_t>(randomNumber() * UINT32_MAX)) +{ +} + +CodeCache::~CodeCache() +{ +} + +CodeCache::CodeBlockKey CodeCache::makeCodeBlockKey(const SourceCode& source, CodeCache::CodeType type, JSParserStrictness strictness) +{ + return std::make_pair(source.toString(), (type << 1) | strictness); +} + +template <typename T> struct CacheTypes { }; + +template <> struct CacheTypes<UnlinkedProgramCodeBlock> { + typedef JSC::ProgramNode RootNode; + static const CodeCache::CodeType codeType = CodeCache::ProgramType; +}; + +template <> struct CacheTypes<UnlinkedEvalCodeBlock> { + typedef JSC::EvalNode RootNode; + static const CodeCache::CodeType codeType = CodeCache::EvalType; +}; + +template <class UnlinkedCodeBlockType, class ExecutableType> +UnlinkedCodeBlockType* CodeCache::getCodeBlock(JSGlobalData& globalData, ExecutableType* executable, const SourceCode& source, JSParserStrictness strictness, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error) +{ + CodeBlockKey key = makeCodeBlockKey(source, CacheTypes<UnlinkedCodeBlockType>::codeType, strictness); + bool storeInCache = false; + if (debuggerMode == DebuggerOff && profilerMode == ProfilerOff) { + CodeBlockIndicesMap::iterator result = m_cachedCodeBlockIndices.find(key); + if (result != m_cachedCodeBlockIndices.end()) { + UnlinkedCodeBlockType* unlinkedCode = jsCast<UnlinkedCodeBlockType*>(m_cachedCodeBlocks[result->value].second.get()); + unsigned firstLine = source.firstLine() + unlinkedCode->firstLine(); + executable->recordParse(unlinkedCode->codeFeatures(), unlinkedCode->hasCapturedVariables(), firstLine, firstLine + unlinkedCode->lineCount()); + return unlinkedCode; + } + storeInCache = true; + } + + typedef typename CacheTypes<UnlinkedCodeBlockType>::RootNode RootNode; + RefPtr<RootNode> rootNode = parse<RootNode>(&globalData, source, 0, Identifier(), strictness, JSParseProgramCode, error); + if (!rootNode) + return 0; + executable->recordParse(rootNode->features(), rootNode->hasCapturedVariables(), rootNode->lineNo(), rootNode->lastLine()); + + UnlinkedCodeBlockType* unlinkedCode = UnlinkedCodeBlockType::create(&globalData, executable->executableInfo()); + unlinkedCode->recordParse(rootNode->features(), rootNode->hasCapturedVariables(), rootNode->lineNo() - source.firstLine(), rootNode->lastLine() - rootNode->lineNo()); + OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(globalData, rootNode.get(), unlinkedCode, debuggerMode, profilerMode))); + error = generator->generate(); + rootNode->destroyData(); + if (error.m_type != ParserError::ErrorNone) + return 0; + + if (storeInCache) { + size_t index = m_randomGenerator.getUint32() % kMaxCodeBlockEntries; + if (m_cachedCodeBlocks[index].second) + m_cachedCodeBlockIndices.remove(m_cachedCodeBlocks[index].first); + m_cachedCodeBlockIndices.set(key, index); + m_cachedCodeBlocks[index].second.set(globalData, unlinkedCode); + m_cachedCodeBlocks[index].first = key; + } + + return unlinkedCode; +} + +UnlinkedProgramCodeBlock* CodeCache::getProgramCodeBlock(JSGlobalData& globalData, ProgramExecutable* executable, const SourceCode& source, JSParserStrictness strictness, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error) +{ + return getCodeBlock<UnlinkedProgramCodeBlock>(globalData, executable, source, strictness, debuggerMode, profilerMode, error); +} + +UnlinkedEvalCodeBlock* CodeCache::getEvalCodeBlock(JSGlobalData& globalData, EvalExecutable* executable, const SourceCode& source, JSParserStrictness strictness, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error) +{ + return getCodeBlock<UnlinkedEvalCodeBlock>(globalData, executable, source, strictness, debuggerMode, profilerMode, error); +} + +UnlinkedFunctionCodeBlock* CodeCache::generateFunctionCodeBlock(JSGlobalData& globalData, UnlinkedFunctionExecutable* executable, const SourceCode& source, CodeSpecializationKind kind, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error) +{ + RefPtr<FunctionBodyNode> body = parse<FunctionBodyNode>(&globalData, source, executable->parameters(), executable->name(), executable->isInStrictContext() ? JSParseStrict : JSParseNormal, JSParseFunctionCode, error); + + if (!body) { + ASSERT(error.m_type != ParserError::ErrorNone); + return 0; + } + + if (executable->forceUsesArguments()) + body->setUsesArguments(); + body->finishParsing(executable->parameters(), executable->name(), executable->functionNameIsInScopeToggle()); + executable->recordParse(body->features(), body->hasCapturedVariables(), body->lineNo(), body->lastLine()); + + UnlinkedFunctionCodeBlock* result = UnlinkedFunctionCodeBlock::create(&globalData, FunctionCode, ExecutableInfo(body->needsActivation(), body->usesEval(), body->isStrictMode(), kind == CodeForConstruct)); + OwnPtr<BytecodeGenerator> generator(adoptPtr(new BytecodeGenerator(globalData, body.get(), result, debuggerMode, profilerMode))); + error = generator->generate(); + body->destroyData(); + if (error.m_type != ParserError::ErrorNone) + return 0; + return result; +} + +UnlinkedFunctionCodeBlock* CodeCache::getFunctionCodeBlock(JSGlobalData& globalData, UnlinkedFunctionExecutable* executable, const SourceCode& source, CodeSpecializationKind kind, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error) +{ + return generateFunctionCodeBlock(globalData, executable, source, kind, debuggerMode, profilerMode, error); +} + +CodeCache::GlobalFunctionKey CodeCache::makeGlobalFunctionKey(const SourceCode& source, const String& name) +{ + return GlobalFunctionKey(source.toString(), name); +} + +UnlinkedFunctionExecutable* CodeCache::getFunctionExecutableFromGlobalCode(JSGlobalData& globalData, const Identifier& name, const SourceCode& source, ParserError& error) +{ + GlobalFunctionKey key = makeGlobalFunctionKey(source, name.string()); + GlobalFunctionIndicesMap::iterator result = m_cachedGlobalFunctionIndices.find(key); + if (result != m_cachedGlobalFunctionIndices.end()) + return m_cachedGlobalFunctions[result->value].second.get(); + + RefPtr<ProgramNode> program = parse<ProgramNode>(&globalData, source, 0, Identifier(), JSParseNormal, JSParseProgramCode, error); + if (!program) { + ASSERT(error.m_type != ParserError::ErrorNone); + return 0; + } + + // This function assumes an input string that would result in a single anonymous function expression. + StatementNode* exprStatement = program->singleStatement(); + ASSERT(exprStatement); + ASSERT(exprStatement->isExprStatement()); + ExpressionNode* funcExpr = static_cast<ExprStatementNode*>(exprStatement)->expr(); + ASSERT(funcExpr); + ASSERT(funcExpr->isFuncExprNode()); + FunctionBodyNode* body = static_cast<FuncExprNode*>(funcExpr)->body(); + ASSERT(body); + ASSERT(body->ident().isNull()); + + UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&globalData, source, body); + functionExecutable->m_nameValue.set(globalData, functionExecutable, jsString(&globalData, name.string())); + + size_t index = m_randomGenerator.getUint32() % kMaxGlobalFunctionEntries; + if (m_cachedGlobalFunctions[index].second) + m_cachedGlobalFunctionIndices.remove(m_cachedGlobalFunctions[index].first); + m_cachedGlobalFunctionIndices.set(key, index); + m_cachedGlobalFunctions[index].second.set(globalData, functionExecutable); + m_cachedGlobalFunctions[index].first = key; + + return functionExecutable; +} + +} |