class __declspec()

in src/InstrumentationEngine/Instruction.h [13:221]


    class __declspec(uuid("BEA964F2-5527-4F7C-B606-D8A1BD8CFB39"))
    CInstruction : public IInstruction, public CDataContainer
    {
        friend CInstructionGraph::~CInstructionGraph();

        // Static table of information about each opcode
        struct ILOpcodeInfo
        {
        public:
            LPCTSTR m_name;               // Display name of the opcode
            DWORD m_opcodeLength;         // Length of the opcode (1 or 2)
            DWORD m_operandLength;        // Length of the parameter length
            ILOperandType m_type;         // The type of the operand
            ILOrdinalOpcode m_alternate;  // The index of the alternate short/long form of the instruction
            ILOpcodeFlags m_flags;
            DWORD m_popSlots;             // Number of slots this instruction pops off the instruction graph
            DWORD m_pushSlots;            // Number of slots this instruction pushes on the instruction graph
        };

    protected:
        static ILOpcodeInfo s_ilOpcodeInfo[];

        // CONSIDER: Store the original length as well? The graph immediately changes short branches etc...

        ILOrdinalOpcode m_opcode;

        DWORD m_offset;
        DWORD m_origOffset;
        InstructionGeneration m_instructionGeneration;
        BOOL m_bIsRemoved;

        // The graph that owns this instruction. Not a CComPtr
        // so that we don't have circular references.
        CInstructionGraph* m_pGraph;

        CComPtr<CInstruction> m_pNextInstruction;
        CComPtr<CInstruction> m_pPreviousInstruction;

        CComPtr<CInstruction> m_pOriginalNextInstruction;
        CComPtr<CInstruction> m_pOriginalPreviousInstruction;

        //Private helper to break up circular dependencies between instructions.
        virtual HRESULT Disconnect();
    public:
        CInstruction(
            _In_ ILOrdinalOpcode opcode,
            _In_ bool isNew
        );

        virtual HRESULT InitializeFromBytes(
            _In_reads_to_ptr_(pEndOfCode) LPCBYTE pCode,
            _In_ LPCBYTE pEndOfCode
            );

    public:
        DEFINE_DELEGATED_REFCOUNT_ADDREF(CInstruction);
        DEFINE_DELEGATED_REFCOUNT_RELEASE(CInstruction);
        STDMETHOD(QueryInterface)(_In_ REFIID riid, _Out_ void **ppvObject) override
        {
            if (FAILED(QueryBaseType<CInstruction>(riid, ppvObject)))
            {
                return ImplQueryInterface(
                    static_cast<IInstruction*>(this),
                    static_cast<IDataContainer*>(this),
                    riid,
                    ppvObject
                );
            }
            return S_OK;
        }

    public:
        HRESULT SetIsRemoved();

        // Return the instruction's impact on the execution stack.
        // This can be positive for a push and negative for a pop.
        // For variable stack impact (call, etc...) it calculates the stack impact using metadata.
        HRESULT GetStackImpact(_In_ IMethodInfo* pMethodInfo, _In_ DWORD currStackDepth, _Out_ int* pStackImpact);
        HRESULT SetGraph(_In_ CInstructionGraph* pGraph);
        constexpr CInstructionGraph* GetGraph() { return m_pGraph; }

    public:
        virtual HRESULT __stdcall GetOffset(_Out_ DWORD* pdwOffset) override;
        virtual HRESULT __stdcall GetOriginalOffset(_Out_ DWORD* pdwOffset) override;

        virtual HRESULT __stdcall GetOpCodeName(_Out_ BSTR* pbstrName) override;

        virtual HRESULT __stdcall GetOpCode(_Out_ ILOrdinalOpcode* pOpCode) override;

        // Only single byte opcodes have short and long forms
        virtual HRESULT __stdcall GetAlternateOrdinalOpcode(_Out_ ILOrdinalOpcode* pdwAlternative) override;

        virtual HRESULT __stdcall GetInstructionLength(_Out_ DWORD* pdwLength) override;

        virtual HRESULT __stdcall GetOpcodeFlags(_Out_ ILOpcodeFlags* pFlags) override;

        virtual HRESULT __stdcall GetOpcodeLength(_Out_ DWORD* pdwLength) override;
        virtual HRESULT __stdcall GetOperandType(_Out_ ILOperandType* pType) override;
        virtual HRESULT __stdcall GetOperandLength(_Out_ DWORD* pdwLength) override;

        // set to true if this instruction did not exist in the original instruction stream.
        virtual HRESULT __stdcall GetIsNew(_Out_ BOOL* pbValue) override;

        // set to true of this instruction has been removed or replaced from in the instruction stream.
        virtual HRESULT __stdcall GetIsRemoved(_Out_ BOOL* pbValue) override;

        virtual HRESULT __stdcall GetIsBranch(_Out_ BOOL* pbValue) override;
        virtual HRESULT __stdcall GetIsSwitch(_Out_ BOOL* pbValue) override;
        virtual HRESULT __stdcall GetIsCallInstruction(_Out_ BOOL* pbValue) override;

        virtual HRESULT __stdcall GetTerminationType(_Out_ InstructionTerminationType* pTerminationType) override;

        virtual HRESULT __stdcall GetIsFallThrough(_Out_ BOOL* pbIsFallThrough) override;

        // Get the current next and previous instructions reflecting changes that instrumentation methods have made
        virtual HRESULT __stdcall GetNextInstruction(_Out_ IInstruction** ppNextInstruction) override;
        virtual HRESULT __stdcall GetPreviousInstruction(_Out_ IInstruction** ppPrevInstruction) override;

        // Get the original instructions from the graph before any instrumentation method made chanages to it.
        virtual HRESULT __stdcall GetOriginalNextInstruction(_Out_ IInstruction** ppNextInstruction) override;
        virtual HRESULT __stdcall GetOriginalPreviousInstruction(_Out_ IInstruction** ppPrevInstruction) override;

        virtual HRESULT __stdcall GetInstructionGeneration(_Out_ InstructionGeneration* pInstructionGeneration) override;

    public:
        // NOTE: these do not fixup the rest of hte graph. Call the versions on instruction graph if you want
        // previous, and next to be udpated correctly

        virtual DWORD GetInstructionSize();
        HRESULT SetNextInstruction(_In_opt_ CInstruction* pInstruction, _In_ bool setOrig);
        HRESULT SetPreviousInstruction(_In_opt_ CInstruction* pInstruction, _In_ bool setOrig);


        HRESULT SetOriginalOffset(_In_ ULONG offset);
        HRESULT SetOffset(_In_ ULONG offset);
        HRESULT SetInstructionGeneration(_In_ InstructionGeneration instructionGeneration);

        static HRESULT InstructionFromBytes(_In_ LPCBYTE pCode, _In_ LPCBYTE pEndOfCode, _Out_ CInstruction** ppInstruction);
        static HRESULT OrdinalOpcodeFromBytes(_In_reads_to_ptr_(pEndOfCode) LPCBYTE pCode, _In_ LPCBYTE pEndOfCode, _Out_ ILOrdinalOpcode* pOpcode);

        HRESULT EmitIL(_In_reads_bytes_(dwcbILBuffer) BYTE* pILBuffer, _In_ DWORD dwcbILBuffer);

        HRESULT LogInstruction(bool ignoreTest);

        constexpr CInstruction* NextInstructionInternal() { return m_pNextInstruction.p; }
        constexpr CInstruction* PreviousInstructionInternal() { return m_pPreviousInstruction.p; }
        constexpr CInstruction* OriginalNextInstructionInternal() { return m_pOriginalNextInstruction.p; }
        constexpr CInstruction* OriginalPreviousInstructionInternal() { return m_pOriginalPreviousInstruction.p; }
        constexpr bool GetIsSwitchInternal() const { return m_opcode == Cee_Switch; }
        bool GetIsBranchInternal() const { return IsFlagSet(s_ilOpcodeInfo[m_opcode].m_flags, ILOpcodeFlag_Branch); }

        // Attempt to cast the given IInstruction to a more specific TCast type. pInstruction may
        // be null, in which case, the result will also be null.
        template<typename TCast>
        static HRESULT CastTo(_In_opt_ IInstruction* pInstruction, _Outptr_result_maybenull_ TCast** pResult)
        {
            IfNullRet(pResult);
            if (pInstruction == nullptr)
            {
                *pResult = nullptr;
            }

            return pInstruction->QueryInterface(pResult);
        }

    protected:

        template<typename TBase>
        HRESULT QueryBaseType(_In_ REFIID riid, _Out_ void** ppvObject)
        {
            // Workaround for the diamond inheritence problem.
            // Both CDataContainer and IInstruction inherit from IUnknown and
            //    ImplQueryInterface() doesn't know which one to pick when casting CInstruction* to IUnknown*.
            // A more comprehensive fix will be covered by https://github.com/microsoft/CLRInstrumentationEngine/issues/311

            IfNullRet(ppvObject);

            if (__uuidof(TBase) == riid)
            {
                *ppvObject = static_cast<TBase*>(this);
                AddRef();
                return S_OK;
            }

            return E_NOINTERFACE;
        }

    private:
        // If the callee token is a MethodSpec (generic method), the parameter count and signature returned
        // will represent the parent method declaration/reference, not the generic parameters or generic signature.
        HRESULT GetSignatureInfoFromCallToken(
            _In_ IMethodInfo* pMethodInfo,
            _Out_ PCCOR_SIGNATURE* ppSig,
            _Out_ DWORD* pSigLength,
            _Out_ CorCallingConvention* pCallConv,
            _Out_ DWORD* pParameterCount,
            _Out_ CorElementType* pRetTypeElementType
            );

        HRESULT EnsureGraphUpdated()
        {
            if (m_pGraph != nullptr)
            {
                return m_pGraph->RefreshInstructions();
            }

            return S_OK;
        }
    };