/* * 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 "CodeBlock.h" #include "Debugger.h" #include "EvalCodeBlock.h" #include "FunctionCodeBlock.h" #include "GlobalExecutable.h" #include "IsoCellSetInlines.h" #include "JIT.h" #include "JSCellInlines.h" #include "JSGlobalObjectInlines.h" #include "JSObjectInlines.h" #include "JSTemplateObjectDescriptor.h" #include "LLIntEntrypoint.h" #include "ModuleProgramCodeBlock.h" #include "ParserError.h" #include "ProgramCodeBlock.h" #include "VMInlines.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, bool isInsideOrdinaryFunction, 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_isInsideOrdinaryFunction(isInsideOrdinaryFunction) , m_derivedContextType(static_cast(derivedContextType)) , m_evalContextType(static_cast(evalContextType)) { } void ScriptExecutable::destroy(JSCell* cell) { static_cast(cell)->ScriptExecutable::~ScriptExecutable(); } void ScriptExecutable::clearCode(IsoCellSet& clearableCodeSet) { m_jitCodeForCall = nullptr; m_jitCodeForConstruct = nullptr; m_jitCodeForCallWithArityCheck = MacroAssemblerCodePtr(); m_jitCodeForConstructWithArityCheck = MacroAssemblerCodePtr(); switch (type()) { case FunctionExecutableType: { FunctionExecutable* executable = static_cast(this); executable->m_codeBlockForCall.clear(); executable->m_codeBlockForConstruct.clear(); break; } case EvalExecutableType: { EvalExecutable* executable = static_cast(this); executable->m_evalCodeBlock.clear(); executable->m_unlinkedEvalCodeBlock.clear(); break; } case ProgramExecutableType: { ProgramExecutable* executable = static_cast(this); executable->m_programCodeBlock.clear(); executable->m_unlinkedProgramCodeBlock.clear(); break; } case ModuleProgramExecutableType: { ModuleProgramExecutable* executable = static_cast(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(this); ProgramCodeBlock* codeBlock = static_cast(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(this); ModuleProgramCodeBlock* codeBlock = static_cast(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(this); EvalCodeBlock* codeBlock = static_cast(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(this); FunctionCodeBlock* codeBlock = static_cast(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())); dataLogLnIf(Options::verboseOSR(), "Installing ", *genericCodeBlock); 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(this); if (executable->m_codeBlockForCall || executable->m_codeBlockForConstruct) return true; } else if (structure(vm)->classInfo() == EvalExecutable::info()) { auto* executable = static_cast(this); if (executable->m_evalCodeBlock || executable->m_unlinkedEvalCodeBlock) return true; } else if (structure(vm)->classInfo() == ProgramExecutable::info()) { auto* executable = static_cast(this); if (executable->m_programCodeBlock || executable->m_unlinkedProgramCodeBlock) return true; } else if (structure(vm)->classInfo() == ModuleProgramExecutable::info()) { auto* executable = static_cast(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(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(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(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(this); RELEASE_ASSERT(!executable->codeBlockFor(kind)); ParserError error; OptionSet 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; } auto* codeBlock = FunctionCodeBlock::create(vm, executable, unlinkedCodeBlock, scope); if (throwScope.exception()) exception = throwScope.exception(); return codeBlock; } CodeBlock* ScriptExecutable::newReplacementCodeBlockFor( CodeSpecializationKind kind) { VM& vm = this->vm(); if (classInfo(vm) == EvalExecutable::info()) { RELEASE_ASSERT(kind == CodeForCall); EvalExecutable* executable = jsCast(this); EvalCodeBlock* baseline = static_cast( 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(this); ProgramCodeBlock* baseline = static_cast( 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(this); ModuleProgramCodeBlock* baseline = static_cast( 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(this); FunctionCodeBlock* baseline = static_cast( 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(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()); } 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& dest) -> TemplateObjectMap& { if (dest) return *dest; auto result = makeUnique(); WTF::storeStoreFence(); dest = WTFMove(result); return *dest; } auto ScriptExecutable::ensureTemplateObjectMap(VM& vm) -> TemplateObjectMap& { switch (type()) { case FunctionExecutableType: return static_cast(this)->ensureTemplateObjectMap(vm); case EvalExecutableType: return static_cast(this)->ensureTemplateObjectMap(vm); case ProgramExecutableType: return static_cast(this)->ensureTemplateObjectMap(vm); case ModuleProgramExecutableType: default: ASSERT(type() == ModuleProgramExecutableType); return static_cast(this)->ensureTemplateObjectMap(vm); } } CodeBlockHash ScriptExecutable::hashFor(CodeSpecializationKind kind) const { return CodeBlockHash(source(), kind); } Optional ScriptExecutable::overrideLineNumber(VM& vm) const { if (inherits(vm)) return jsCast(this)->overrideLineNumber(); return WTF::nullopt; } unsigned ScriptExecutable::typeProfilingStartOffset(VM& vm) const { if (inherits(vm)) return jsCast(this)->typeProfilingStartOffset(vm); if (inherits(vm)) return UINT_MAX; return 0; } unsigned ScriptExecutable::typeProfilingEndOffset(VM& vm) const { if (inherits(vm)) return jsCast(this)->typeProfilingEndOffset(vm); if (inherits(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(this)->recordParse(features, hasCapturedVariables); return; default: jsCast(this)->recordParse(features, hasCapturedVariables, lastLine, endColumn); return; } } int ScriptExecutable::lastLine() const { switch (type()) { case FunctionExecutableType: return jsCast(this)->lastLine(); default: return jsCast(this)->lastLine(); } return 0; } unsigned ScriptExecutable::endColumn() const { switch (type()) { case FunctionExecutableType: return jsCast(this)->endColumn(); default: return jsCast(this)->endColumn(); } return 0; } } // namespace JSC