blob: f13b25e4c9b262c525e0f56ec0f69cdc7cbedf0e [file] [log] [blame]
/*
* 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 "JSCInlines.h"
#include "Parser.h"
#include "StrongInlines.h"
#include "UnlinkedCodeBlock.h"
namespace JSC {
const double CodeCacheMap::workingSetTime = 10.0;
void CodeCacheMap::pruneSlowCase()
{
m_minCapacity = std::max(m_size - m_sizeAtLastPrune, static_cast<int64_t>(0));
m_sizeAtLastPrune = m_size;
m_timeAtLastPrune = monotonicallyIncreasingTime();
if (m_capacity < m_minCapacity)
m_capacity = m_minCapacity;
while (m_size > m_capacity || !canPruneQuickly()) {
MapType::iterator it = m_map.begin();
m_size -= it->key.length();
m_map.remove(it);
}
}
CodeCache::CodeCache()
{
}
CodeCache::~CodeCache()
{
}
template <typename T> struct CacheTypes { };
template <> struct CacheTypes<UnlinkedProgramCodeBlock> {
typedef JSC::ProgramNode RootNode;
static const SourceCodeKey::CodeType codeType = SourceCodeKey::ProgramType;
static const SourceParseMode parseMode = SourceParseMode::ProgramMode;
};
template <> struct CacheTypes<UnlinkedEvalCodeBlock> {
typedef JSC::EvalNode RootNode;
static const SourceCodeKey::CodeType codeType = SourceCodeKey::EvalType;
static const SourceParseMode parseMode = SourceParseMode::ProgramMode;
};
template <> struct CacheTypes<UnlinkedModuleProgramCodeBlock> {
typedef JSC::ModuleProgramNode RootNode;
static const SourceCodeKey::CodeType codeType = SourceCodeKey::ModuleType;
static const SourceParseMode parseMode = SourceParseMode::ModuleEvaluateMode;
};
template <class UnlinkedCodeBlockType, class ExecutableType>
UnlinkedCodeBlockType* CodeCache::getGlobalCodeBlock(VM& vm, ExecutableType* executable, const SourceCode& source, JSParserBuiltinMode builtinMode,
JSParserStrictMode strictMode, ThisTDZMode thisTDZMode, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error, const VariableEnvironment* variablesUnderTDZ)
{
SourceCodeKey key = SourceCodeKey(source, String(), CacheTypes<UnlinkedCodeBlockType>::codeType, builtinMode, strictMode, thisTDZMode);
SourceCodeValue* cache = m_sourceCode.findCacheAndUpdateAge(key);
bool canCache = debuggerMode == DebuggerOff && profilerMode == ProfilerOff && !vm.typeProfiler() && !vm.controlFlowProfiler();
if (cache && canCache) {
UnlinkedCodeBlockType* unlinkedCodeBlock = jsCast<UnlinkedCodeBlockType*>(cache->cell.get());
unsigned firstLine = source.firstLine() + unlinkedCodeBlock->firstLine();
unsigned lineCount = unlinkedCodeBlock->lineCount();
unsigned startColumn = unlinkedCodeBlock->startColumn() + source.startColumn();
bool endColumnIsOnStartLine = !lineCount;
unsigned endColumn = unlinkedCodeBlock->endColumn() + (endColumnIsOnStartLine ? startColumn : 1);
executable->recordParse(unlinkedCodeBlock->codeFeatures(), unlinkedCodeBlock->hasCapturedVariables(), firstLine, firstLine + lineCount, startColumn, endColumn);
return unlinkedCodeBlock;
}
typedef typename CacheTypes<UnlinkedCodeBlockType>::RootNode RootNode;
std::unique_ptr<RootNode> rootNode = parse<RootNode>(
&vm, source, Identifier(), builtinMode, strictMode,
CacheTypes<UnlinkedCodeBlockType>::parseMode, error, nullptr, ConstructorKind::None, thisTDZMode);
if (!rootNode)
return nullptr;
unsigned lineCount = rootNode->lastLine() - rootNode->firstLine();
unsigned startColumn = rootNode->startColumn() + 1;
bool endColumnIsOnStartLine = !lineCount;
unsigned unlinkedEndColumn = rootNode->endColumn();
unsigned endColumn = unlinkedEndColumn + (endColumnIsOnStartLine ? startColumn : 1);
executable->recordParse(rootNode->features(), rootNode->hasCapturedVariables(), rootNode->firstLine(), rootNode->lastLine(), startColumn, endColumn);
UnlinkedCodeBlockType* unlinkedCodeBlock = UnlinkedCodeBlockType::create(&vm, executable->executableInfo());
unlinkedCodeBlock->recordParse(rootNode->features(), rootNode->hasCapturedVariables(), rootNode->firstLine() - source.firstLine(), lineCount, unlinkedEndColumn);
auto generator = std::make_unique<BytecodeGenerator>(vm, rootNode.get(), unlinkedCodeBlock, debuggerMode, profilerMode, variablesUnderTDZ);
error = generator->generate();
if (error.isValid())
return nullptr;
if (!canCache)
return unlinkedCodeBlock;
m_sourceCode.addCache(key, SourceCodeValue(vm, unlinkedCodeBlock, m_sourceCode.age()));
return unlinkedCodeBlock;
}
UnlinkedProgramCodeBlock* CodeCache::getProgramCodeBlock(VM& vm, ProgramExecutable* executable, const SourceCode& source, JSParserBuiltinMode builtinMode, JSParserStrictMode strictMode, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
{
VariableEnvironment emptyParentTDZVariables;
return getGlobalCodeBlock<UnlinkedProgramCodeBlock>(vm, executable, source, builtinMode, strictMode, ThisTDZMode::CheckIfNeeded, debuggerMode, profilerMode, error, &emptyParentTDZVariables);
}
UnlinkedEvalCodeBlock* CodeCache::getEvalCodeBlock(VM& vm, EvalExecutable* executable, const SourceCode& source, JSParserBuiltinMode builtinMode, JSParserStrictMode strictMode, ThisTDZMode thisTDZMode, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error, const VariableEnvironment* variablesUnderTDZ)
{
return getGlobalCodeBlock<UnlinkedEvalCodeBlock>(vm, executable, source, builtinMode, strictMode, thisTDZMode, debuggerMode, profilerMode, error, variablesUnderTDZ);
}
UnlinkedModuleProgramCodeBlock* CodeCache::getModuleProgramCodeBlock(VM& vm, ModuleProgramExecutable* executable, const SourceCode& source, JSParserBuiltinMode builtinMode, DebuggerMode debuggerMode, ProfilerMode profilerMode, ParserError& error)
{
VariableEnvironment emptyParentTDZVariables;
return getGlobalCodeBlock<UnlinkedModuleProgramCodeBlock>(vm, executable, source, builtinMode, JSParserStrictMode::Strict, ThisTDZMode::CheckIfNeeded, debuggerMode, profilerMode, error, &emptyParentTDZVariables);
}
// FIXME: There's no need to add the function's name to the key here. It's already in the source code.
UnlinkedFunctionExecutable* CodeCache::getFunctionExecutableFromGlobalCode(VM& vm, const Identifier& name, const SourceCode& source, ParserError& error)
{
SourceCodeKey key = SourceCodeKey(
source, name.string(), SourceCodeKey::FunctionType,
JSParserBuiltinMode::NotBuiltin,
JSParserStrictMode::NotStrict);
SourceCodeValue* cache = m_sourceCode.findCacheAndUpdateAge(key);
if (cache)
return jsCast<UnlinkedFunctionExecutable*>(cache->cell.get());
JSTextPosition positionBeforeLastNewline;
std::unique_ptr<ProgramNode> program = parse<ProgramNode>(
&vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin,
JSParserStrictMode::NotStrict, SourceParseMode::ProgramMode,
error, &positionBeforeLastNewline);
if (!program) {
RELEASE_ASSERT(error.isValid());
return nullptr;
}
// This function assumes an input string that would result in a single function declaration.
StatementNode* statement = program->singleStatement();
ASSERT(statement);
ASSERT(statement->isBlock());
if (!statement || !statement->isBlock())
return nullptr;
StatementNode* funcDecl = static_cast<BlockNode*>(statement)->singleStatement();
ASSERT(funcDecl);
ASSERT(funcDecl->isFuncDeclNode());
if (!funcDecl || !funcDecl->isFuncDeclNode())
return nullptr;
FunctionMetadataNode* metadata = static_cast<FuncDeclNode*>(funcDecl)->metadata();
ASSERT(metadata);
if (!metadata)
return nullptr;
metadata->setEndPosition(positionBeforeLastNewline);
// The Function constructor only has access to global variables, so no variables will be under TDZ.
VariableEnvironment emptyTDZVariables;
UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, metadata, UnlinkedNormalFunction, ConstructAbility::CanConstruct, emptyTDZVariables);
functionExecutable->m_nameValue.set(vm, functionExecutable, jsString(&vm, name.string()));
m_sourceCode.addCache(key, SourceCodeValue(vm, functionExecutable, m_sourceCode.age()));
return functionExecutable;
}
}