| /* | 
 |  * Copyright (C) 2012-2018 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 "CallLinkInfo.h" | 
 |  | 
 | #include "CallFrameShuffleData.h" | 
 | #include "DFGOperations.h" | 
 | #include "DFGThunks.h" | 
 | #include "FunctionCodeBlock.h" | 
 | #include "JSCInlines.h" | 
 | #include "MacroAssembler.h" | 
 | #include "Opcode.h" | 
 | #include "Repatch.h" | 
 | #include <wtf/ListDump.h> | 
 |  | 
 | #if ENABLE(JIT) | 
 | namespace JSC { | 
 |  | 
 | CallLinkInfo::CallType CallLinkInfo::callTypeFor(OpcodeID opcodeID) | 
 | { | 
 |     if (opcodeID == op_call || opcodeID == op_call_eval) | 
 |         return Call; | 
 |     if (opcodeID == op_call_varargs) | 
 |         return CallVarargs; | 
 |     if (opcodeID == op_construct) | 
 |         return Construct; | 
 |     if (opcodeID == op_construct_varargs) | 
 |         return ConstructVarargs; | 
 |     if (opcodeID == op_tail_call) | 
 |         return TailCall; | 
 |     ASSERT(opcodeID == op_tail_call_varargs || opcodeID == op_tail_call_forward_arguments); | 
 |     return TailCallVarargs; | 
 | } | 
 |  | 
 | CallLinkInfo::CallLinkInfo() | 
 |     : m_hasSeenShouldRepatch(false) | 
 |     , m_hasSeenClosure(false) | 
 |     , m_clearedByGC(false) | 
 |     , m_allowStubs(true) | 
 |     , m_isLinked(false) | 
 |     , m_callType(None) | 
 |     , m_calleeGPR(255) | 
 |     , m_maxNumArguments(0) | 
 |     , m_slowPathCount(0) | 
 | { | 
 | } | 
 |  | 
 | CallLinkInfo::~CallLinkInfo() | 
 | { | 
 |     clearStub(); | 
 |      | 
 |     if (isOnList()) | 
 |         remove(); | 
 | } | 
 |  | 
 | void CallLinkInfo::clearStub() | 
 | { | 
 |     if (!stub()) | 
 |         return; | 
 |  | 
 |     m_stub->clearCallNodesFor(this); | 
 |     m_stub = nullptr; | 
 | } | 
 |  | 
 | void CallLinkInfo::unlink(VM& vm) | 
 | { | 
 |     // We could be called even if we're not linked anymore because of how polymorphic calls | 
 |     // work. Each callsite within the polymorphic call stub may separately ask us to unlink(). | 
 |     if (isLinked()) | 
 |         unlinkFor(vm, *this); | 
 |  | 
 |     // Either we were unlinked, in which case we should not have been on any list, or we unlinked | 
 |     // ourselves so that we're not on any list anymore. | 
 |     RELEASE_ASSERT(!isOnList()); | 
 | } | 
 |  | 
 | CodeLocationNearCall<JSInternalPtrTag> CallLinkInfo::callReturnLocation() | 
 | { | 
 |     RELEASE_ASSERT(!isDirect()); | 
 |     return CodeLocationNearCall<JSInternalPtrTag>(m_callReturnLocationOrPatchableJump, Regular); | 
 | } | 
 |  | 
 | CodeLocationJump<JSInternalPtrTag> CallLinkInfo::patchableJump() | 
 | { | 
 |     RELEASE_ASSERT(callType() == DirectTailCall); | 
 |     return CodeLocationJump<JSInternalPtrTag>(m_callReturnLocationOrPatchableJump); | 
 | } | 
 |  | 
 | CodeLocationDataLabelPtr<JSInternalPtrTag> CallLinkInfo::hotPathBegin() | 
 | { | 
 |     RELEASE_ASSERT(!isDirect()); | 
 |     return CodeLocationDataLabelPtr<JSInternalPtrTag>(m_hotPathBeginOrSlowPathStart); | 
 | } | 
 |  | 
 | CodeLocationLabel<JSInternalPtrTag> CallLinkInfo::slowPathStart() | 
 | { | 
 |     RELEASE_ASSERT(isDirect()); | 
 |     return m_hotPathBeginOrSlowPathStart; | 
 | } | 
 |  | 
 | void CallLinkInfo::setCallee(VM& vm, JSCell* owner, JSObject* callee) | 
 | { | 
 |     RELEASE_ASSERT(!isDirect()); | 
 |     MacroAssembler::repatchPointer(hotPathBegin(), callee); | 
 |     m_calleeOrCodeBlock.set(vm, owner, callee); | 
 |     m_isLinked = true; | 
 | } | 
 |  | 
 | void CallLinkInfo::clearCallee() | 
 | { | 
 |     RELEASE_ASSERT(!isDirect()); | 
 |     MacroAssembler::repatchPointer(hotPathBegin(), nullptr); | 
 |     m_calleeOrCodeBlock.clear(); | 
 |     m_isLinked = false; | 
 | } | 
 |  | 
 | JSObject* CallLinkInfo::callee() | 
 | { | 
 |     RELEASE_ASSERT(!isDirect()); | 
 |     return jsCast<JSObject*>(m_calleeOrCodeBlock.get()); | 
 | } | 
 |  | 
 | void CallLinkInfo::setCodeBlock(VM& vm, JSCell* owner, FunctionCodeBlock* codeBlock) | 
 | { | 
 |     RELEASE_ASSERT(isDirect()); | 
 |     m_calleeOrCodeBlock.setMayBeNull(vm, owner, codeBlock); | 
 |     m_isLinked = true; | 
 | } | 
 |  | 
 | void CallLinkInfo::clearCodeBlock() | 
 | { | 
 |     RELEASE_ASSERT(isDirect()); | 
 |     m_calleeOrCodeBlock.clear(); | 
 |     m_isLinked = false; | 
 | } | 
 |  | 
 | FunctionCodeBlock* CallLinkInfo::codeBlock() | 
 | { | 
 |     RELEASE_ASSERT(isDirect()); | 
 |     return jsCast<FunctionCodeBlock*>(m_calleeOrCodeBlock.get()); | 
 | } | 
 |  | 
 | void CallLinkInfo::setLastSeenCallee(VM& vm, const JSCell* owner, JSObject* callee) | 
 | { | 
 |     RELEASE_ASSERT(!isDirect()); | 
 |     m_lastSeenCalleeOrExecutable.set(vm, owner, callee); | 
 | } | 
 |  | 
 | void CallLinkInfo::clearLastSeenCallee() | 
 | { | 
 |     RELEASE_ASSERT(!isDirect()); | 
 |     m_lastSeenCalleeOrExecutable.clear(); | 
 | } | 
 |  | 
 | JSObject* CallLinkInfo::lastSeenCallee() | 
 | { | 
 |     RELEASE_ASSERT(!isDirect()); | 
 |     return jsCast<JSObject*>(m_lastSeenCalleeOrExecutable.get()); | 
 | } | 
 |  | 
 | bool CallLinkInfo::haveLastSeenCallee() | 
 | { | 
 |     RELEASE_ASSERT(!isDirect()); | 
 |     return !!m_lastSeenCalleeOrExecutable; | 
 | } | 
 |  | 
 | void CallLinkInfo::setExecutableDuringCompilation(ExecutableBase* executable) | 
 | { | 
 |     RELEASE_ASSERT(isDirect()); | 
 |     m_lastSeenCalleeOrExecutable.setWithoutWriteBarrier(executable); | 
 | } | 
 |  | 
 | ExecutableBase* CallLinkInfo::executable() | 
 | { | 
 |     RELEASE_ASSERT(isDirect()); | 
 |     return jsCast<ExecutableBase*>(m_lastSeenCalleeOrExecutable.get()); | 
 | } | 
 |  | 
 | void CallLinkInfo::setMaxNumArguments(unsigned value) | 
 | { | 
 |     RELEASE_ASSERT(isDirect()); | 
 |     RELEASE_ASSERT(value); | 
 |     m_maxNumArguments = value; | 
 | } | 
 |  | 
 | void CallLinkInfo::visitWeak(VM& vm) | 
 | { | 
 |     auto handleSpecificCallee = [&] (JSFunction* callee) { | 
 |         if (Heap::isMarked(callee->executable())) | 
 |             m_hasSeenClosure = true; | 
 |         else | 
 |             m_clearedByGC = true; | 
 |     }; | 
 |      | 
 |     if (isLinked()) { | 
 |         if (stub()) { | 
 |             if (!stub()->visitWeak(vm)) { | 
 |                 if (Options::verboseOSR()) { | 
 |                     dataLog( | 
 |                         "Clearing closure call to ", | 
 |                         listDump(stub()->variants()), ", stub routine ", RawPointer(stub()), | 
 |                         ".\n"); | 
 |                 } | 
 |                 unlink(vm); | 
 |                 m_clearedByGC = true; | 
 |             } | 
 |         } else if (!Heap::isMarked(m_calleeOrCodeBlock.get())) { | 
 |             if (isDirect()) { | 
 |                 if (Options::verboseOSR()) { | 
 |                     dataLog( | 
 |                         "Clearing call to ", RawPointer(codeBlock()), " (", | 
 |                         pointerDump(codeBlock()), ").\n"); | 
 |                 } | 
 |             } else { | 
 |                 if (callee()->type() == JSFunctionType) { | 
 |                     if (Options::verboseOSR()) { | 
 |                         dataLog( | 
 |                             "Clearing call to ", | 
 |                             RawPointer(callee()), " (", | 
 |                             static_cast<JSFunction*>(callee())->executable()->hashFor(specializationKind()), | 
 |                             ").\n"); | 
 |                     } | 
 |                     handleSpecificCallee(static_cast<JSFunction*>(callee())); | 
 |                 } else { | 
 |                     if (Options::verboseOSR()) | 
 |                         dataLog("Clearing call to ", RawPointer(callee()), ".\n"); | 
 |                     m_clearedByGC = true; | 
 |                 } | 
 |             } | 
 |             unlink(vm); | 
 |         } else if (isDirect() && !Heap::isMarked(m_lastSeenCalleeOrExecutable.get())) { | 
 |             if (Options::verboseOSR()) { | 
 |                 dataLog( | 
 |                     "Clearing call to ", RawPointer(executable()), | 
 |                     " because the executable is dead.\n"); | 
 |             } | 
 |             unlink(vm); | 
 |             // We should only get here once the owning CodeBlock is dying, since the executable must | 
 |             // already be in the owner's weak references. | 
 |             m_lastSeenCalleeOrExecutable.clear(); | 
 |         } | 
 |     } | 
 |     if (!isDirect() && haveLastSeenCallee() && !Heap::isMarked(lastSeenCallee())) { | 
 |         if (lastSeenCallee()->type() == JSFunctionType) | 
 |             handleSpecificCallee(jsCast<JSFunction*>(lastSeenCallee())); | 
 |         else | 
 |             m_clearedByGC = true; | 
 |         clearLastSeenCallee(); | 
 |     } | 
 | } | 
 |  | 
 | void CallLinkInfo::setFrameShuffleData(const CallFrameShuffleData& shuffleData) | 
 | { | 
 |     m_frameShuffleData = std::make_unique<CallFrameShuffleData>(shuffleData); | 
 | } | 
 |  | 
 | } // namespace JSC | 
 | #endif // ENABLE(JIT) | 
 |  |