|  | /* | 
|  | * 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 |