blob: 106ffcd1af61feec0c906ab1249640630843510a [file] [log] [blame]
/*
* Copyright (C) 2012, 2016 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 "IndirectEvalExecutable.h"
namespace JSC {
const Seconds CodeCacheMap::workingSetTime = 10_s;
void CodeCacheMap::pruneSlowCase()
{
m_minCapacity = std::max(m_size - m_sizeAtLastPrune, static_cast<int64_t>(0));
m_sizeAtLastPrune = m_size;
m_timeAtLastPrune = MonotonicTime::now();
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);
}
}
template <class UnlinkedCodeBlockType, class ExecutableType>
UnlinkedCodeBlockType* CodeCache::getUnlinkedGlobalCodeBlock(VM& vm, ExecutableType* executable, const SourceCode& source, JSParserStrictMode strictMode, JSParserScriptMode scriptMode, DebuggerMode debuggerMode, ParserError& error, EvalContextType evalContextType)
{
DerivedContextType derivedContextType = executable->derivedContextType();
bool isArrowFunctionContext = executable->isArrowFunctionContext();
SourceCodeKey key(
source, String(), CacheTypes<UnlinkedCodeBlockType>::codeType, strictMode, scriptMode,
derivedContextType, evalContextType, isArrowFunctionContext, debuggerMode,
vm.typeProfiler() ? TypeProfilerEnabled::Yes : TypeProfilerEnabled::No,
vm.controlFlowProfiler() ? ControlFlowProfilerEnabled::Yes : ControlFlowProfilerEnabled::No);
SourceCodeValue* cache = m_sourceCode.findCacheAndUpdateAge(key);
if (cache && Options::useCodeCache()) {
UnlinkedCodeBlockType* unlinkedCodeBlock = jsCast<UnlinkedCodeBlockType*>(cache->cell.get());
unsigned lineCount = unlinkedCodeBlock->lineCount();
unsigned startColumn = unlinkedCodeBlock->startColumn() + source.startColumn().oneBasedInt();
bool endColumnIsOnStartLine = !lineCount;
unsigned endColumn = unlinkedCodeBlock->endColumn() + (endColumnIsOnStartLine ? startColumn : 1);
executable->recordParse(unlinkedCodeBlock->codeFeatures(), unlinkedCodeBlock->hasCapturedVariables(), source.firstLine().oneBasedInt() + lineCount, endColumn);
source.provider()->setSourceURLDirective(unlinkedCodeBlock->sourceURLDirective());
source.provider()->setSourceMappingURLDirective(unlinkedCodeBlock->sourceMappingURLDirective());
return unlinkedCodeBlock;
}
VariableEnvironment variablesUnderTDZ;
UnlinkedCodeBlockType* unlinkedCodeBlock = generateUnlinkedCodeBlock<UnlinkedCodeBlockType, ExecutableType>(vm, executable, source, strictMode, scriptMode, debuggerMode, error, evalContextType, &variablesUnderTDZ);
if (unlinkedCodeBlock && Options::useCodeCache())
m_sourceCode.addCache(key, SourceCodeValue(vm, unlinkedCodeBlock, m_sourceCode.age()));
return unlinkedCodeBlock;
}
UnlinkedProgramCodeBlock* CodeCache::getUnlinkedProgramCodeBlock(VM& vm, ProgramExecutable* executable, const SourceCode& source, JSParserStrictMode strictMode, DebuggerMode debuggerMode, ParserError& error)
{
return getUnlinkedGlobalCodeBlock<UnlinkedProgramCodeBlock>(vm, executable, source, strictMode, JSParserScriptMode::Classic, debuggerMode, error, EvalContextType::None);
}
UnlinkedEvalCodeBlock* CodeCache::getUnlinkedEvalCodeBlock(VM& vm, IndirectEvalExecutable* executable, const SourceCode& source, JSParserStrictMode strictMode, DebuggerMode debuggerMode, ParserError& error, EvalContextType evalContextType)
{
return getUnlinkedGlobalCodeBlock<UnlinkedEvalCodeBlock>(vm, executable, source, strictMode, JSParserScriptMode::Classic, debuggerMode, error, evalContextType);
}
UnlinkedModuleProgramCodeBlock* CodeCache::getUnlinkedModuleProgramCodeBlock(VM& vm, ModuleProgramExecutable* executable, const SourceCode& source, DebuggerMode debuggerMode, ParserError& error)
{
return getUnlinkedGlobalCodeBlock<UnlinkedModuleProgramCodeBlock>(vm, executable, source, JSParserStrictMode::Strict, JSParserScriptMode::Module, debuggerMode, error, EvalContextType::None);
}
UnlinkedFunctionExecutable* CodeCache::getUnlinkedGlobalFunctionExecutable(VM& vm, const Identifier& name, const SourceCode& source, DebuggerMode debuggerMode, ParserError& error)
{
bool isArrowFunctionContext = false;
SourceCodeKey key(
source, name.string(), SourceCodeType::FunctionType,
JSParserStrictMode::NotStrict,
JSParserScriptMode::Classic,
DerivedContextType::None,
EvalContextType::None,
isArrowFunctionContext,
debuggerMode,
vm.typeProfiler() ? TypeProfilerEnabled::Yes : TypeProfilerEnabled::No,
vm.controlFlowProfiler() ? ControlFlowProfilerEnabled::Yes : ControlFlowProfilerEnabled::No);
SourceCodeValue* cache = m_sourceCode.findCacheAndUpdateAge(key);
if (cache && Options::useCodeCache()) {
UnlinkedFunctionExecutable* executable = jsCast<UnlinkedFunctionExecutable*>(cache->cell.get());
source.provider()->setSourceURLDirective(executable->sourceURLDirective());
source.provider()->setSourceMappingURLDirective(executable->sourceMappingURLDirective());
return executable;
}
JSTextPosition positionBeforeLastNewline;
std::unique_ptr<ProgramNode> program = parse<ProgramNode>(
&vm, source, Identifier(), JSParserBuiltinMode::NotBuiltin,
JSParserStrictMode::NotStrict, JSParserScriptMode::Classic, SourceParseMode::ProgramMode, SuperBinding::NotNeeded,
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();
if (UNLIKELY(!statement)) {
JSToken token;
error = ParserError(ParserError::SyntaxError, ParserError::SyntaxErrorIrrecoverable, token, "Parser error", -1);
return nullptr;
}
ASSERT(statement->isBlock());
StatementNode* funcDecl = static_cast<BlockNode*>(statement)->singleStatement();
if (UNLIKELY(!funcDecl)) {
JSToken token;
error = ParserError(ParserError::SyntaxError, ParserError::SyntaxErrorIrrecoverable, token, "Parser error", -1);
return nullptr;
}
ASSERT(funcDecl->isFuncDeclNode());
FunctionMetadataNode* metadata = static_cast<FuncDeclNode*>(funcDecl)->metadata();
ASSERT(metadata);
if (!metadata)
return nullptr;
metadata->overrideName(name);
metadata->setEndPosition(positionBeforeLastNewline);
// The Function constructor only has access to global variables, so no variables will be under TDZ.
VariableEnvironment emptyTDZVariables;
ConstructAbility constructAbility = constructAbilityForParseMode(metadata->parseMode());
UnlinkedFunctionExecutable* functionExecutable = UnlinkedFunctionExecutable::create(&vm, source, metadata, UnlinkedNormalFunction, constructAbility, JSParserScriptMode::Classic, emptyTDZVariables, DerivedContextType::None);
functionExecutable->setSourceURLDirective(source.provider()->sourceURL());
functionExecutable->setSourceMappingURLDirective(source.provider()->sourceMappingURL());
if (Options::useCodeCache())
m_sourceCode.addCache(key, SourceCodeValue(vm, functionExecutable, m_sourceCode.age()));
return functionExecutable;
}
}