blob: ff842025d57ec062bfca50ea01edb53e1d3a28bb [file] [log] [blame]
/*
* Copyright (C) 2009-2019 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 "BatchedTransitionOptimizer.h"
#include "CodeBlock.h"
#include "Debugger.h"
#include "EvalCodeBlock.h"
#include "FunctionCodeBlock.h"
#include "GlobalExecutable.h"
#include "IsoCellSetInlines.h"
#include "JIT.h"
#include "JSCInlines.h"
#include "JSTemplateObjectDescriptor.h"
#include "LLIntEntrypoint.h"
#include "ModuleProgramCodeBlock.h"
#include "Parser.h"
#include "ProgramCodeBlock.h"
#include "TypeProfiler.h"
#include "VMInlines.h"
#include <wtf/CommaPrinter.h>
namespace JSC {
const ClassInfo ScriptExecutable::s_info = { "ScriptExecutable", &ExecutableBase::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(ScriptExecutable) };
ScriptExecutable::ScriptExecutable(Structure* structure, VM& vm, const SourceCode& source, bool isInStrictContext, DerivedContextType derivedContextType, bool isInArrowFunctionContext, EvalContextType evalContextType, Intrinsic intrinsic)
: ExecutableBase(vm, structure)
, m_source(source)
, m_intrinsic(intrinsic)
, m_features(isInStrictContext ? StrictModeFeature : 0)
, m_hasCapturedVariables(false)
, m_neverInline(false)
, m_neverOptimize(false)
, m_neverFTLOptimize(false)
, m_isArrowFunctionContext(isInArrowFunctionContext)
, m_canUseOSRExitFuzzing(true)
, m_codeForGeneratorBodyWasGenerated(false)
, m_derivedContextType(static_cast<unsigned>(derivedContextType))
, m_evalContextType(static_cast<unsigned>(evalContextType))
{
}
void ScriptExecutable::destroy(JSCell* cell)
{
static_cast<ScriptExecutable*>(cell)->ScriptExecutable::~ScriptExecutable();
}
void ScriptExecutable::clearCode(IsoCellSet& clearableCodeSet)
{
m_jitCodeForCall = nullptr;
m_jitCodeForConstruct = nullptr;
m_jitCodeForCallWithArityCheck = MacroAssemblerCodePtr<JSEntryPtrTag>();
m_jitCodeForConstructWithArityCheck = MacroAssemblerCodePtr<JSEntryPtrTag>();
switch (type()) {
case FunctionExecutableType: {
FunctionExecutable* executable = static_cast<FunctionExecutable*>(this);
executable->m_codeBlockForCall.clear();
executable->m_codeBlockForConstruct.clear();
break;
}
case EvalExecutableType: {
EvalExecutable* executable = static_cast<EvalExecutable*>(this);
executable->m_evalCodeBlock.clear();
executable->m_unlinkedEvalCodeBlock.clear();
break;
}
case ProgramExecutableType: {
ProgramExecutable* executable = static_cast<ProgramExecutable*>(this);
executable->m_programCodeBlock.clear();
executable->m_unlinkedProgramCodeBlock.clear();
break;
}
case ModuleProgramExecutableType: {
ModuleProgramExecutable* executable = static_cast<ModuleProgramExecutable*>(this);
executable->m_moduleProgramCodeBlock.clear();
executable->m_unlinkedModuleProgramCodeBlock.clear();
executable->m_moduleEnvironmentSymbolTable.clear();
break;
}
default:
RELEASE_ASSERT_NOT_REACHED();
break;
}
ASSERT(&VM::SpaceAndSet::setFor(*subspace()) == &clearableCodeSet);
clearableCodeSet.remove(this);
}
void ScriptExecutable::installCode(CodeBlock* codeBlock)
{
installCode(codeBlock->vm(), codeBlock, codeBlock->codeType(), codeBlock->specializationKind());
}
void ScriptExecutable::installCode(VM& vm, CodeBlock* genericCodeBlock, CodeType codeType, CodeSpecializationKind kind)
{
if (genericCodeBlock)
CODEBLOCK_LOG_EVENT(genericCodeBlock, "installCode", ());
CodeBlock* oldCodeBlock = nullptr;
switch (codeType) {
case GlobalCode: {
ProgramExecutable* executable = jsCast<ProgramExecutable*>(this);
ProgramCodeBlock* codeBlock = static_cast<ProgramCodeBlock*>(genericCodeBlock);
ASSERT(kind == CodeForCall);
oldCodeBlock = ExecutableToCodeBlockEdge::deactivateAndUnwrap(executable->m_programCodeBlock.get());
executable->m_programCodeBlock.setMayBeNull(vm, this, ExecutableToCodeBlockEdge::wrapAndActivate(codeBlock));
break;
}
case ModuleCode: {
ModuleProgramExecutable* executable = jsCast<ModuleProgramExecutable*>(this);
ModuleProgramCodeBlock* codeBlock = static_cast<ModuleProgramCodeBlock*>(genericCodeBlock);
ASSERT(kind == CodeForCall);
oldCodeBlock = ExecutableToCodeBlockEdge::deactivateAndUnwrap(executable->m_moduleProgramCodeBlock.get());
executable->m_moduleProgramCodeBlock.setMayBeNull(vm, this, ExecutableToCodeBlockEdge::wrapAndActivate(codeBlock));
break;
}
case EvalCode: {
EvalExecutable* executable = jsCast<EvalExecutable*>(this);
EvalCodeBlock* codeBlock = static_cast<EvalCodeBlock*>(genericCodeBlock);
ASSERT(kind == CodeForCall);
oldCodeBlock = ExecutableToCodeBlockEdge::deactivateAndUnwrap(executable->m_evalCodeBlock.get());
executable->m_evalCodeBlock.setMayBeNull(vm, this, ExecutableToCodeBlockEdge::wrapAndActivate(codeBlock));
break;
}
case FunctionCode: {
FunctionExecutable* executable = jsCast<FunctionExecutable*>(this);
FunctionCodeBlock* codeBlock = static_cast<FunctionCodeBlock*>(genericCodeBlock);
switch (kind) {
case CodeForCall:
oldCodeBlock = ExecutableToCodeBlockEdge::deactivateAndUnwrap(executable->m_codeBlockForCall.get());
executable->m_codeBlockForCall.setMayBeNull(vm, this, ExecutableToCodeBlockEdge::wrapAndActivate(codeBlock));
break;
case CodeForConstruct:
oldCodeBlock = ExecutableToCodeBlockEdge::deactivateAndUnwrap(executable->m_codeBlockForConstruct.get());
executable->m_codeBlockForConstruct.setMayBeNull(vm, this, ExecutableToCodeBlockEdge::wrapAndActivate(codeBlock));
break;
}
break;
}
}
switch (kind) {
case CodeForCall:
m_jitCodeForCall = genericCodeBlock ? genericCodeBlock->jitCode() : nullptr;
m_jitCodeForCallWithArityCheck = nullptr;
break;
case CodeForConstruct:
m_jitCodeForConstruct = genericCodeBlock ? genericCodeBlock->jitCode() : nullptr;
m_jitCodeForConstructWithArityCheck = nullptr;
break;
}
auto& clearableCodeSet = VM::SpaceAndSet::setFor(*subspace());
if (hasClearableCode(vm))
clearableCodeSet.add(this);
else
clearableCodeSet.remove(this);
if (genericCodeBlock) {
RELEASE_ASSERT(genericCodeBlock->ownerExecutable() == this);
RELEASE_ASSERT(JITCode::isExecutableScript(genericCodeBlock->jitType()));
if (UNLIKELY(Options::verboseOSR()))
dataLog("Installing ", *genericCodeBlock, "\n");
if (UNLIKELY(vm.m_perBytecodeProfiler))
vm.m_perBytecodeProfiler->ensureBytecodesFor(genericCodeBlock);
Debugger* debugger = genericCodeBlock->globalObject()->debugger();
if (UNLIKELY(debugger))
debugger->registerCodeBlock(genericCodeBlock);
}
if (oldCodeBlock)
oldCodeBlock->unlinkIncomingCalls();
vm.heap.writeBarrier(this);
}
bool ScriptExecutable::hasClearableCode(VM& vm) const
{
if (m_jitCodeForCall
|| m_jitCodeForConstruct
|| m_jitCodeForCallWithArityCheck
|| m_jitCodeForConstructWithArityCheck)
return true;
if (structure(vm)->classInfo() == FunctionExecutable::info()) {
auto* executable = static_cast<const FunctionExecutable*>(this);
if (executable->m_codeBlockForCall || executable->m_codeBlockForConstruct)
return true;
} else if (structure(vm)->classInfo() == EvalExecutable::info()) {
auto* executable = static_cast<const EvalExecutable*>(this);
if (executable->m_evalCodeBlock || executable->m_unlinkedEvalCodeBlock)
return true;
} else if (structure(vm)->classInfo() == ProgramExecutable::info()) {
auto* executable = static_cast<const ProgramExecutable*>(this);
if (executable->m_programCodeBlock || executable->m_unlinkedProgramCodeBlock)
return true;
} else if (structure(vm)->classInfo() == ModuleProgramExecutable::info()) {
auto* executable = static_cast<const ModuleProgramExecutable*>(this);
if (executable->m_moduleProgramCodeBlock
|| executable->m_unlinkedModuleProgramCodeBlock
|| executable->m_moduleEnvironmentSymbolTable)
return true;
}
return false;
}
CodeBlock* ScriptExecutable::newCodeBlockFor(
CodeSpecializationKind kind, JSFunction* function, JSScope* scope, Exception*& exception)
{
VM& vm = scope->vm();
auto throwScope = DECLARE_THROW_SCOPE(vm);
ASSERT(vm.heap.isDeferred());
ASSERT(endColumn() != UINT_MAX);
JSGlobalObject* globalObject = scope->globalObject(vm);
if (classInfo(vm) == EvalExecutable::info()) {
EvalExecutable* executable = jsCast<EvalExecutable*>(this);
RELEASE_ASSERT(kind == CodeForCall);
RELEASE_ASSERT(!executable->m_evalCodeBlock);
RELEASE_ASSERT(!function);
auto codeBlock = EvalCodeBlock::create(vm,
executable, executable->m_unlinkedEvalCodeBlock.get(), scope);
EXCEPTION_ASSERT(throwScope.exception() || codeBlock);
if (!codeBlock) {
exception = throwException(
globalObject, throwScope,
createOutOfMemoryError(globalObject));
return nullptr;
}
return codeBlock;
}
if (classInfo(vm) == ProgramExecutable::info()) {
ProgramExecutable* executable = jsCast<ProgramExecutable*>(this);
RELEASE_ASSERT(kind == CodeForCall);
RELEASE_ASSERT(!executable->m_programCodeBlock);
RELEASE_ASSERT(!function);
auto codeBlock = ProgramCodeBlock::create(vm,
executable, executable->m_unlinkedProgramCodeBlock.get(), scope);
EXCEPTION_ASSERT(throwScope.exception() || codeBlock);
if (!codeBlock) {
exception = throwException(
globalObject, throwScope,
createOutOfMemoryError(globalObject));
return nullptr;
}
return codeBlock;
}
if (classInfo(vm) == ModuleProgramExecutable::info()) {
ModuleProgramExecutable* executable = jsCast<ModuleProgramExecutable*>(this);
RELEASE_ASSERT(kind == CodeForCall);
RELEASE_ASSERT(!executable->m_moduleProgramCodeBlock);
RELEASE_ASSERT(!function);
auto codeBlock = ModuleProgramCodeBlock::create(vm,
executable, executable->m_unlinkedModuleProgramCodeBlock.get(), scope);
EXCEPTION_ASSERT(throwScope.exception() || codeBlock);
if (!codeBlock) {
exception = throwException(
globalObject, throwScope,
createOutOfMemoryError(globalObject));
return nullptr;
}
return codeBlock;
}
RELEASE_ASSERT(classInfo(vm) == FunctionExecutable::info());
RELEASE_ASSERT(function);
FunctionExecutable* executable = jsCast<FunctionExecutable*>(this);
RELEASE_ASSERT(!executable->codeBlockFor(kind));
ParserError error;
OptionSet<CodeGenerationMode> codeGenerationMode = globalObject->defaultCodeGenerationMode();
// We continue using the same CodeGenerationMode for Generators because live generator objects can
// keep the state which is only valid with the CodeBlock compiled with the same CodeGenerationMode.
if (isGeneratorOrAsyncFunctionBodyParseMode(executable->parseMode())) {
if (!m_codeForGeneratorBodyWasGenerated) {
m_codeGenerationModeForGeneratorBody = codeGenerationMode;
m_codeForGeneratorBodyWasGenerated = true;
} else
codeGenerationMode = m_codeGenerationModeForGeneratorBody;
}
UnlinkedFunctionCodeBlock* unlinkedCodeBlock =
executable->m_unlinkedExecutable->unlinkedCodeBlockFor(
vm, executable->source(), kind, codeGenerationMode, error,
executable->parseMode());
recordParse(
executable->m_unlinkedExecutable->features(),
executable->m_unlinkedExecutable->hasCapturedVariables(),
lastLine(), endColumn());
if (!unlinkedCodeBlock) {
exception = throwException(
globalObject, throwScope,
error.toErrorObject(globalObject, executable->source()));
return nullptr;
}
RELEASE_AND_RETURN(throwScope, FunctionCodeBlock::create(vm, executable, unlinkedCodeBlock, scope));
}
CodeBlock* ScriptExecutable::newReplacementCodeBlockFor(
CodeSpecializationKind kind)
{
VM& vm = this->vm();
if (classInfo(vm) == EvalExecutable::info()) {
RELEASE_ASSERT(kind == CodeForCall);
EvalExecutable* executable = jsCast<EvalExecutable*>(this);
EvalCodeBlock* baseline = static_cast<EvalCodeBlock*>(
executable->codeBlock()->baselineVersion());
EvalCodeBlock* result = EvalCodeBlock::create(vm,
CodeBlock::CopyParsedBlock, *baseline);
result->setAlternative(vm, baseline);
return result;
}
if (classInfo(vm) == ProgramExecutable::info()) {
RELEASE_ASSERT(kind == CodeForCall);
ProgramExecutable* executable = jsCast<ProgramExecutable*>(this);
ProgramCodeBlock* baseline = static_cast<ProgramCodeBlock*>(
executable->codeBlock()->baselineVersion());
ProgramCodeBlock* result = ProgramCodeBlock::create(vm,
CodeBlock::CopyParsedBlock, *baseline);
result->setAlternative(vm, baseline);
return result;
}
if (classInfo(vm) == ModuleProgramExecutable::info()) {
RELEASE_ASSERT(kind == CodeForCall);
ModuleProgramExecutable* executable = jsCast<ModuleProgramExecutable*>(this);
ModuleProgramCodeBlock* baseline = static_cast<ModuleProgramCodeBlock*>(
executable->codeBlock()->baselineVersion());
ModuleProgramCodeBlock* result = ModuleProgramCodeBlock::create(vm,
CodeBlock::CopyParsedBlock, *baseline);
result->setAlternative(vm, baseline);
return result;
}
RELEASE_ASSERT(classInfo(vm) == FunctionExecutable::info());
FunctionExecutable* executable = jsCast<FunctionExecutable*>(this);
FunctionCodeBlock* baseline = static_cast<FunctionCodeBlock*>(
executable->codeBlockFor(kind)->baselineVersion());
FunctionCodeBlock* result = FunctionCodeBlock::create(vm,
CodeBlock::CopyParsedBlock, *baseline);
result->setAlternative(vm, baseline);
return result;
}
static void setupLLInt(CodeBlock* codeBlock)
{
LLInt::setEntrypoint(codeBlock);
}
static void setupJIT(VM& vm, CodeBlock* codeBlock)
{
#if ENABLE(JIT)
CompilationResult result = JIT::compile(vm, codeBlock, JITCompilationMustSucceed);
RELEASE_ASSERT(result == CompilationSuccessful);
#else
UNUSED_PARAM(vm);
UNUSED_PARAM(codeBlock);
UNREACHABLE_FOR_PLATFORM();
#endif
}
Exception* ScriptExecutable::prepareForExecutionImpl(
VM& vm, JSFunction* function, JSScope* scope, CodeSpecializationKind kind, CodeBlock*& resultCodeBlock)
{
auto throwScope = DECLARE_THROW_SCOPE(vm);
DeferGCForAWhile deferGC(vm.heap);
if (UNLIKELY(vm.getAndClearFailNextNewCodeBlock())) {
JSGlobalObject* globalObject = scope->globalObject(vm);
return throwException(globalObject, throwScope, createError(globalObject, "Forced Failure"_s));
}
Exception* exception = nullptr;
CodeBlock* codeBlock = newCodeBlockFor(kind, function, scope, exception);
resultCodeBlock = codeBlock;
EXCEPTION_ASSERT(!!throwScope.exception() == !codeBlock);
if (UNLIKELY(!codeBlock))
return exception;
if (Options::validateBytecode())
codeBlock->validate();
if (Options::useLLInt())
setupLLInt(codeBlock);
else
setupJIT(vm, codeBlock);
installCode(vm, codeBlock, codeBlock->codeType(), codeBlock->specializationKind());
return nullptr;
}
ScriptExecutable* ScriptExecutable::topLevelExecutable()
{
switch (type()) {
case FunctionExecutableType:
return jsCast<FunctionExecutable*>(this)->topLevelExecutable();
default:
return this;
}
}
JSArray* ScriptExecutable::createTemplateObject(JSGlobalObject* globalObject, JSTemplateObjectDescriptor* descriptor)
{
VM& vm = globalObject->vm();
auto scope = DECLARE_THROW_SCOPE(vm);
TemplateObjectMap& templateObjectMap = ensureTemplateObjectMap(vm);
TemplateObjectMap::AddResult result;
{
auto locker = holdLock(cellLock());
result = templateObjectMap.add(descriptor->endOffset(), WriteBarrier<JSArray>());
}
if (JSArray* array = result.iterator->value.get())
return array;
JSArray* templateObject = descriptor->createTemplateObject(globalObject);
RETURN_IF_EXCEPTION(scope, nullptr);
result.iterator->value.set(vm, this, templateObject);
return templateObject;
}
auto ScriptExecutable::ensureTemplateObjectMapImpl(std::unique_ptr<TemplateObjectMap>& dest) -> TemplateObjectMap&
{
if (dest)
return *dest;
auto result = makeUnique<TemplateObjectMap>();
WTF::storeStoreFence();
dest = WTFMove(result);
return *dest;
}
auto ScriptExecutable::ensureTemplateObjectMap(VM& vm) -> TemplateObjectMap&
{
switch (type()) {
case FunctionExecutableType:
return static_cast<FunctionExecutable*>(this)->ensureTemplateObjectMap(vm);
case EvalExecutableType:
return static_cast<EvalExecutable*>(this)->ensureTemplateObjectMap(vm);
case ProgramExecutableType:
return static_cast<ProgramExecutable*>(this)->ensureTemplateObjectMap(vm);
case ModuleProgramExecutableType:
default:
ASSERT(type() == ModuleProgramExecutableType);
return static_cast<ModuleProgramExecutable*>(this)->ensureTemplateObjectMap(vm);
}
}
CodeBlockHash ScriptExecutable::hashFor(CodeSpecializationKind kind) const
{
return CodeBlockHash(source(), kind);
}
Optional<int> ScriptExecutable::overrideLineNumber(VM& vm) const
{
if (inherits<FunctionExecutable>(vm))
return jsCast<const FunctionExecutable*>(this)->overrideLineNumber();
return WTF::nullopt;
}
unsigned ScriptExecutable::typeProfilingStartOffset(VM& vm) const
{
if (inherits<FunctionExecutable>(vm))
return jsCast<const FunctionExecutable*>(this)->typeProfilingStartOffset(vm);
if (inherits<EvalExecutable>(vm))
return UINT_MAX;
return 0;
}
unsigned ScriptExecutable::typeProfilingEndOffset(VM& vm) const
{
if (inherits<FunctionExecutable>(vm))
return jsCast<const FunctionExecutable*>(this)->typeProfilingEndOffset(vm);
if (inherits<EvalExecutable>(vm))
return UINT_MAX;
return source().length() - 1;
}
void ScriptExecutable::recordParse(CodeFeatures features, bool hasCapturedVariables, int lastLine, unsigned endColumn)
{
switch (type()) {
case FunctionExecutableType:
// Since UnlinkedFunctionExecutable holds the information to calculate lastLine and endColumn, we do not need to remember them in ScriptExecutable's fields.
jsCast<FunctionExecutable*>(this)->recordParse(features, hasCapturedVariables);
return;
default:
jsCast<GlobalExecutable*>(this)->recordParse(features, hasCapturedVariables, lastLine, endColumn);
return;
}
}
int ScriptExecutable::lastLine() const
{
switch (type()) {
case FunctionExecutableType:
return jsCast<const FunctionExecutable*>(this)->lastLine();
default:
return jsCast<const GlobalExecutable*>(this)->lastLine();
}
return 0;
}
unsigned ScriptExecutable::endColumn() const
{
switch (type()) {
case FunctionExecutableType:
return jsCast<const FunctionExecutable*>(this)->endColumn();
default:
return jsCast<const GlobalExecutable*>(this)->endColumn();
}
return 0;
}
} // namespace JSC