|  | /* | 
|  | * Copyright (C) 2009, 2010, 2013, 2015-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 "BatchedTransitionOptimizer.h" | 
|  | #include "CodeBlock.h" | 
|  | #include "Debugger.h" | 
|  | #include "EvalCodeBlock.h" | 
|  | #include "FunctionCodeBlock.h" | 
|  | #include "JIT.h" | 
|  | #include "JSCInlines.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, NUM_PARAMETERS_NOT_COMPILED, intrinsic) | 
|  | , m_features(isInStrictContext ? StrictModeFeature : 0) | 
|  | , m_didTryToEnterInLoop(false) | 
|  | , m_hasCapturedVariables(false) | 
|  | , m_neverInline(false) | 
|  | , m_neverOptimize(false) | 
|  | , m_neverFTLOptimize(false) | 
|  | , m_isArrowFunctionContext(isInArrowFunctionContext) | 
|  | , m_canUseOSRExitFuzzing(true) | 
|  | , m_derivedContextType(static_cast<unsigned>(derivedContextType)) | 
|  | , m_evalContextType(static_cast<unsigned>(evalContextType)) | 
|  | , m_overrideLineNumber(-1) | 
|  | , m_lastLine(-1) | 
|  | , m_endColumn(UINT_MAX) | 
|  | , m_typeProfilingStartOffset(UINT_MAX) | 
|  | , m_typeProfilingEndOffset(UINT_MAX) | 
|  | , m_source(source) | 
|  | { | 
|  | } | 
|  |  | 
|  | void ScriptExecutable::destroy(JSCell* cell) | 
|  | { | 
|  | static_cast<ScriptExecutable*>(cell)->ScriptExecutable::~ScriptExecutable(); | 
|  | } | 
|  |  | 
|  | 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 = MacroAssemblerCodePtr(); | 
|  | m_numParametersForCall = genericCodeBlock ? genericCodeBlock->numParameters() : NUM_PARAMETERS_NOT_COMPILED; | 
|  | break; | 
|  | case CodeForConstruct: | 
|  | m_jitCodeForConstruct = genericCodeBlock ? genericCodeBlock->jitCode() : nullptr; | 
|  | m_jitCodeForConstructWithArityCheck = MacroAssemblerCodePtr(); | 
|  | m_numParametersForConstruct = genericCodeBlock ? genericCodeBlock->numParameters() : NUM_PARAMETERS_NOT_COMPILED; | 
|  | break; | 
|  | } | 
|  |  | 
|  | 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); | 
|  | } | 
|  |  | 
|  | CodeBlock* ScriptExecutable::newCodeBlockFor( | 
|  | CodeSpecializationKind kind, JSFunction* function, JSScope* scope, JSObject*& exception) | 
|  | { | 
|  | VM* vm = scope->vm(); | 
|  | auto throwScope = DECLARE_THROW_SCOPE(*vm); | 
|  |  | 
|  | ASSERT(vm->heap.isDeferred()); | 
|  | ASSERT(endColumn() != UINT_MAX); | 
|  |  | 
|  | JSGlobalObject* globalObject = scope->globalObject(); | 
|  | ExecState* exec = globalObject->globalExec(); | 
|  |  | 
|  | 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, | 
|  | executable->source().provider()); | 
|  | EXCEPTION_ASSERT(throwScope.exception() || codeBlock); | 
|  | if (!codeBlock) { | 
|  | exception = throwException( | 
|  | exec, throwScope, | 
|  | createOutOfMemoryError(exec)); | 
|  | 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, | 
|  | executable->source().provider(), startColumn()); | 
|  | EXCEPTION_ASSERT(throwScope.exception() || codeBlock); | 
|  | if (!codeBlock) { | 
|  | exception = throwException( | 
|  | exec, throwScope, | 
|  | createOutOfMemoryError(exec)); | 
|  | 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, | 
|  | executable->source().provider(), startColumn()); | 
|  | EXCEPTION_ASSERT(throwScope.exception() || codeBlock); | 
|  | if (!codeBlock) { | 
|  | exception = throwException( | 
|  | exec, throwScope, | 
|  | createOutOfMemoryError(exec)); | 
|  | 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; | 
|  | DebuggerMode debuggerMode = globalObject->hasInteractiveDebugger() ? DebuggerOn : DebuggerOff; | 
|  | UnlinkedFunctionCodeBlock* unlinkedCodeBlock = | 
|  | executable->m_unlinkedExecutable->unlinkedCodeBlockFor( | 
|  | *vm, executable->m_source, kind, debuggerMode, error, | 
|  | executable->parseMode()); | 
|  | recordParse( | 
|  | executable->m_unlinkedExecutable->features(), | 
|  | executable->m_unlinkedExecutable->hasCapturedVariables(), | 
|  | lastLine(), endColumn()); | 
|  | if (!unlinkedCodeBlock) { | 
|  | exception = throwException( | 
|  | globalObject->globalExec(), throwScope, | 
|  | error.toErrorObject(globalObject, executable->m_source)); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | throwScope.release(); | 
|  | return FunctionCodeBlock::create(vm, executable, unlinkedCodeBlock, scope, | 
|  | source().provider(), source().startOffset(), startColumn()); | 
|  | } | 
|  |  | 
|  | 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(VM& vm, CodeBlock* codeBlock) | 
|  | { | 
|  | LLInt::setEntrypoint(vm, 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 | 
|  | } | 
|  |  | 
|  | JSObject* ScriptExecutable::prepareForExecutionImpl( | 
|  | VM& vm, JSFunction* function, JSScope* scope, CodeSpecializationKind kind, CodeBlock*& resultCodeBlock) | 
|  | { | 
|  | auto throwScope = DECLARE_THROW_SCOPE(vm); | 
|  | DeferGCForAWhile deferGC(vm.heap); | 
|  |  | 
|  | if (vm.getAndClearFailNextNewCodeBlock()) { | 
|  | auto& state = *scope->globalObject()->globalExec(); | 
|  | return throwException(&state, throwScope, createError(&state, ASCIILiteral("Forced Failure"))); | 
|  | } | 
|  |  | 
|  | JSObject* 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(vm, codeBlock); | 
|  | else | 
|  | setupJIT(vm, codeBlock); | 
|  |  | 
|  | installCode(vm, codeBlock, codeBlock->codeType(), codeBlock->specializationKind()); | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | CodeBlockHash ScriptExecutable::hashFor(CodeSpecializationKind kind) const | 
|  | { | 
|  | return CodeBlockHash(source(), kind); | 
|  | } | 
|  |  | 
|  | } // namespace JSC |