// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. // // File: StubGen.cpp // // #include "common.h" #include "stubgen.h" #include "jitinterface.h" #include "ilstubcache.h" #include "sigbuilder.h" #include "formattype.h" #include "typestring.h" #include "field.h" // // ....[.....\xxxxx]..0... -> ....[xxxxx]..0... // ^ ^ ^ void DumpIL_RemoveFullPath(SString &strTokenFormatting) { STANDARD_VM_CONTRACT; if (strTokenFormatting.IsEmpty()) return; SString::Iterator begin = strTokenFormatting.Begin(); SString::Iterator end = strTokenFormatting.End(); SString::Iterator leftBracket = strTokenFormatting.Begin(); // Find the first '[' in the string. while ((leftBracket != end) && (*leftBracket != W('['))) { ++leftBracket; } if (leftBracket != end) { SString::Iterator lastSlash = strTokenFormatting.End() - 1; // Find the last '\\' in the string. while ((lastSlash != leftBracket) && (*lastSlash != W('\\'))) { --lastSlash; } if (leftBracket != lastSlash) { strTokenFormatting.Delete(leftBracket + 1, lastSlash - leftBracket); } } } void DumpIL_FormatToken(TokenLookupMap* pTokenMap, mdToken token, SString &strTokenFormatting, const SString &strStubTargetSig) { void* pvLookupRetVal = (void*)POISONC; _ASSERTE(strTokenFormatting.IsEmpty()); EX_TRY { if (TypeFromToken(token) == mdtMethodDef) { MethodDesc* pMD = pTokenMap->LookupMethodDef(token); pvLookupRetVal = pMD; CONSISTENCY_CHECK(CheckPointer(pMD)); pMD->GetFullMethodInfo(strTokenFormatting); } else if (TypeFromToken(token) == mdtTypeDef) { TypeHandle typeHnd = pTokenMap->LookupTypeDef(token); pvLookupRetVal = typeHnd.AsPtr(); CONSISTENCY_CHECK(!typeHnd.IsNull()); SString typeName; MethodTable *pMT = NULL; if (typeHnd.IsTypeDesc()) { TypeDesc *pTypeDesc = typeHnd.AsTypeDesc(); pMT = pTypeDesc->GetMethodTable(); } else { pMT = typeHnd.AsMethodTable(); } // AppendType handles NULL correctly TypeString::AppendType(typeName, TypeHandle(pMT)); if (pMT && typeHnd.IsNativeValueType()) typeName.Append(W("_NativeValueType")); strTokenFormatting.Set(typeName); } else if (TypeFromToken(token) == mdtFieldDef) { FieldDesc* pFD = pTokenMap->LookupFieldDef(token); pvLookupRetVal = pFD; CONSISTENCY_CHECK(CheckPointer(pFD)); SString typeName; TypeString::AppendType(typeName, TypeHandle(pFD->GetApproxEnclosingMethodTable())); SString strFieldName(SString::Utf8, pFD->GetName()); strTokenFormatting.Printf(W("%s::%s"), typeName.GetUnicode(), strFieldName.GetUnicode()); } else if (TypeFromToken(token) == mdtModule) { // Do nothing, because strTokenFormatting is already empty. } else if (TypeFromToken(token) == mdtSignature) { CONSISTENCY_CHECK(token == TOKEN_ILSTUB_TARGET_SIG); strTokenFormatting.Set(strStubTargetSig); } else { strTokenFormatting.Printf(W("%d"), token); } DumpIL_RemoveFullPath(strTokenFormatting); } EX_CATCH { strTokenFormatting.Printf(W("%d"), token); } EX_END_CATCH(SwallowAllExceptions) } void ILCodeStream::Emit(ILInstrEnum instr, INT16 iStackDelta, UINT_PTR uArg) { STANDARD_VM_CONTRACT; UINT idxCurInstr = 0; ILStubLinker::ILInstruction* pInstrBuffer = NULL; if (NULL == m_pqbILInstructions) { m_pqbILInstructions = new ILCodeStreamBuffer(); } idxCurInstr = m_uCurInstrIdx; m_uCurInstrIdx++; m_pqbILInstructions->ReSizeThrows(m_uCurInstrIdx * sizeof(ILStubLinker::ILInstruction)); pInstrBuffer = (ILStubLinker::ILInstruction*)m_pqbILInstructions->Ptr(); pInstrBuffer[idxCurInstr].uInstruction = static_cast(instr); pInstrBuffer[idxCurInstr].iStackDelta = iStackDelta; pInstrBuffer[idxCurInstr].uArg = uArg; } ILCodeLabel* ILStubLinker::NewCodeLabel() { STANDARD_VM_CONTRACT; ILCodeLabel* pCodeLabel = new ILCodeLabel(); pCodeLabel->m_pNext = m_pLabelList; pCodeLabel->m_pOwningStubLinker = this; pCodeLabel->m_pCodeStreamOfLabel = NULL; pCodeLabel->m_idxLabeledInstruction = -1; m_pLabelList = pCodeLabel; return pCodeLabel; } void ILCodeStream::EmitLabel(ILCodeLabel* pCodeLabel) { CONTRACTL { STANDARD_VM_CHECK; PRECONDITION_MSG(m_pOwner == pCodeLabel->m_pOwningStubLinker, "you can only use a code label in the ILStubLinker that created it!"); } CONTRACTL_END; pCodeLabel->m_pCodeStreamOfLabel = this; pCodeLabel->m_idxLabeledInstruction = m_uCurInstrIdx; Emit(CEE_CODE_LABEL, 0, (UINT_PTR)pCodeLabel); } static const BYTE s_rgbOpcodeSizes[] = { #define OPDEF(name,string,pop,push,oprType,opcType,l,s1,s2,ctrl) \ ((l) + (oprType)), #define InlineNone 0 #define ShortInlineVar 1 #define ShortInlineI 1 #define InlineI 4 #define InlineI8 8 #define ShortInlineR 4 #define InlineR 8 #define InlineMethod 4 #define InlineSig 4 #define ShortInlineBrTarget 1 #define InlineBrTarget 4 #define InlineSwitch -1 #define InlineType 4 #define InlineString 4 #define InlineField 4 #define InlineTok 4 #define InlineVar 2 #include "opcode.def" #undef OPDEF #undef InlineNone #undef ShortInlineVar #undef ShortInlineI #undef InlineI #undef InlineI8 #undef ShortInlineR #undef InlineR #undef InlineMethod #undef InlineSig #undef ShortInlineBrTarget #undef InlineBrTarget #undef InlineSwitch #undef InlineType #undef InlineString #undef InlineField #undef InlineTok #undef InlineVar }; struct ILOpcode { BYTE byte1; BYTE byte2; }; static const ILOpcode s_rgOpcodes[] = { #define OPDEF(name,string,pop,push,oprType,opcType,l,s1,s2,ctrl) \ { (s1), (s2) }, #include "opcode.def" #undef OPDEF }; ILCodeStream::ILInstrEnum ILCodeStream::LowerOpcode(ILInstrEnum instr, ILStubLinker::ILInstruction* pInstr) { CONTRACTL { STANDARD_VM_CHECK; PRECONDITION(instr == (ILInstrEnum)pInstr->uInstruction); } CONTRACTL_END; // // NOTE: we do not lower branches to their smallest form because that // would introduce extra passes at link time, which isn't really // worth the savings in IL code size. // UINT_PTR uConst = pInstr->uArg; switch (instr) { case CEE_LDC_I8: { if (uConst == (UINT_PTR)-1) { instr = CEE_LDC_I4_M1; } else if (uConst < 9) { instr = (ILInstrEnum)((UINT_PTR)CEE_LDC_I4_0 + uConst); } else if (FitsInI1(uConst)) { instr = CEE_LDC_I4_S; } else if (FitsInI4(uConst)) { instr = CEE_LDC_I4; } break; } case CEE_LDARG: { if (uConst <= 3) { instr = (ILInstrEnum)((UINT_PTR)CEE_LDARG_0 + uConst); break; } goto lShortForm; } case CEE_LDLOC: { if (uConst <= 3) { instr = (ILInstrEnum)((UINT_PTR)CEE_LDLOC_0 + uConst); break; } goto lShortForm; } case CEE_STLOC: { if (uConst <= 3) { instr = (ILInstrEnum)((UINT_PTR)CEE_STLOC_0 + uConst); break; } lShortForm: if (FitsInI1(uConst)) { static const UINT_PTR c_uMakeShortDelta = ((UINT_PTR)CEE_LDARG - (UINT_PTR)CEE_LDARG_S); static_assert_no_msg(((UINT_PTR)CEE_LDARG - c_uMakeShortDelta) == (UINT_PTR)CEE_LDARG_S); static_assert_no_msg(((UINT_PTR)CEE_LDLOC - c_uMakeShortDelta) == (UINT_PTR)CEE_LDLOC_S); static_assert_no_msg(((UINT_PTR)CEE_STLOC - c_uMakeShortDelta) == (UINT_PTR)CEE_STLOC_S); instr = (ILInstrEnum)((UINT_PTR)instr - c_uMakeShortDelta); } break; } case CEE_LDARGA: case CEE_STARG: case CEE_LDLOCA: { if (FitsInI1(uConst)) { static const UINT_PTR c_uMakeShortDelta = ((UINT_PTR)CEE_LDARGA - (UINT_PTR)CEE_LDARGA_S); static_assert_no_msg(((UINT_PTR)CEE_LDARGA - c_uMakeShortDelta) == (UINT_PTR)CEE_LDARGA_S); static_assert_no_msg(((UINT_PTR)CEE_STARG - c_uMakeShortDelta) == (UINT_PTR)CEE_STARG_S); static_assert_no_msg(((UINT_PTR)CEE_LDLOCA - c_uMakeShortDelta) == (UINT_PTR)CEE_LDLOCA_S); instr = (ILInstrEnum)((UINT_PTR)instr - c_uMakeShortDelta); } break; } default: break; } pInstr->uInstruction = static_cast(instr); return instr; } void ILStubLinker::PatchInstructionArgument(ILCodeLabel* pLabel, UINT_PTR uNewArg DEBUG_ARG(UINT16 uExpectedInstruction)) { LIMITED_METHOD_CONTRACT; UINT idx = pLabel->m_idxLabeledInstruction; ILCodeStream* pLabelCodeStream = pLabel->m_pCodeStreamOfLabel; ILInstruction* pLabelInstrBuffer = (ILInstruction*)pLabelCodeStream->m_pqbILInstructions->Ptr(); CONSISTENCY_CHECK(pLabelInstrBuffer[idx].uInstruction == ILCodeStream::CEE_CODE_LABEL); CONSISTENCY_CHECK(pLabelInstrBuffer[idx].iStackDelta == 0); idx++; CONSISTENCY_CHECK(idx < pLabelCodeStream->m_uCurInstrIdx); CONSISTENCY_CHECK(pLabelInstrBuffer[idx].uInstruction == uExpectedInstruction); pLabelInstrBuffer[idx].uArg = uNewArg; } ILCodeLabel::ILCodeLabel() { m_pNext = NULL; m_pOwningStubLinker = NULL; m_pCodeStreamOfLabel = NULL; m_codeOffset = -1; m_idxLabeledInstruction = -1; } ILCodeLabel::~ILCodeLabel() { } size_t ILCodeLabel::GetCodeOffset() { LIMITED_METHOD_CONTRACT; CONSISTENCY_CHECK(m_codeOffset != (size_t)-1); return m_codeOffset; } void ILCodeLabel::SetCodeOffset(size_t codeOffset) { LIMITED_METHOD_CONTRACT; CONSISTENCY_CHECK((m_codeOffset == (size_t)-1) && (codeOffset != (size_t)-1)); m_codeOffset = codeOffset; } static const LPCSTR s_rgOpcodeNames[] = { #define OPDEF(name,string,pop,push,oprType,opcType,l,s1,s2,ctrl) \ string, #include "opcode.def" #undef OPDEF }; #include "openum.h" static const BYTE s_rgbOpcodeArgType[] = { #define OPDEF(name,string,pop,push,oprType,opcType,l,s1,s2,ctrl) \ oprType, #include "opcode.def" #undef OPDEF }; //--------------------------------------------------------------------------------------- // void ILStubLinker::LogILInstruction( size_t curOffset, bool isLabeled, INT iCurStack, ILInstruction * pInstruction, SString * pDumpILStubCode) { STANDARD_VM_CONTRACT; // // format label // SString strLabel; if (isLabeled) { strLabel.Printf(W("IL_%04x:"), curOffset); } else { strLabel.Set(W(" ")); } // // format opcode // SString strOpcode; ILCodeStream::ILInstrEnum instr = (ILCodeStream::ILInstrEnum)pInstruction->uInstruction; size_t cbOpcodeName = strlen(s_rgOpcodeNames[instr]); SString strOpcodeName; strOpcodeName.SetUTF8(s_rgOpcodeNames[instr]); // Set the width of the opcode to 15. strOpcode.Set(W(" ")); strOpcode.Replace(strOpcode.Begin(), (COUNT_T)cbOpcodeName, strOpcodeName); // // format argument // static const size_t c_cchPreallocateArgument = 512; SString strArgument; strArgument.Preallocate(c_cchPreallocateArgument); static const size_t c_cchPreallocateTokenName = 1024; SString strTokenName; strTokenName.Preallocate(c_cchPreallocateTokenName); if (ILCodeStream::IsBranchInstruction(instr)) { size_t branchDistance = (size_t)pInstruction->uArg; size_t targetOffset = curOffset + s_rgbOpcodeSizes[instr] + branchDistance; strArgument.Printf(W("IL_%04x"), targetOffset); } else if ((ILCodeStream::ILInstrEnum)CEE_NOP == instr) { SString strInstruction; strInstruction.Printf("%s", (char *)pInstruction->uArg); strInstruction.ConvertToUnicode(strArgument); } else { switch (s_rgbOpcodeArgType[instr]) { case InlineNone: break; case ShortInlineVar: case ShortInlineI: case InlineI: strArgument.Printf(W("0x%x"), pInstruction->uArg); break; case InlineI8: strArgument.Printf(W("0x%p"), (void *)pInstruction->uArg); break; case InlineMethod: case InlineField: case InlineType: case InlineString: case InlineSig: case InlineRVA: case InlineTok: // No token value when we dump IL for ETW if (pDumpILStubCode == NULL) strArgument.Printf(W("0x%08x"), pInstruction->uArg); LPUTF8 pszFormattedStubTargetSig = NULL; CQuickBytes qbTargetSig; if (TOKEN_ILSTUB_TARGET_SIG == pInstruction->uArg) { PCCOR_SIGNATURE pTargetSig; ULONG cTargetSig; CQuickBytes qbTempTargetSig; IMDInternalImport * pIMDI = MscorlibBinder::GetModule()->GetMDImport(); cTargetSig = GetStubTargetMethodSigSize(); pTargetSig = (PCCOR_SIGNATURE)qbTempTargetSig.AllocThrows(cTargetSig); GetStubTargetMethodSig((BYTE*)pTargetSig, cTargetSig); PrettyPrintSig(pTargetSig, cTargetSig, "", &qbTargetSig, pIMDI, NULL); pszFormattedStubTargetSig = (LPUTF8)qbTargetSig.Ptr(); } // Dump to szTokenNameBuffer if logging, otherwise dump to szArgumentBuffer to avoid an extra space because we are omitting the token _ASSERTE(FitsIn(pInstruction->uArg)); SString strFormattedStubTargetSig; strFormattedStubTargetSig.SetUTF8(pszFormattedStubTargetSig); if (pDumpILStubCode == NULL) DumpIL_FormatToken(&m_tokenMap, static_cast(pInstruction->uArg), strTokenName, strFormattedStubTargetSig); else DumpIL_FormatToken(&m_tokenMap, static_cast(pInstruction->uArg), strArgument, strFormattedStubTargetSig); break; } } // // log it! // if (pDumpILStubCode) { pDumpILStubCode->AppendPrintf(W("%s /*(%2d)*/ %s %s %s\n"), strLabel.GetUnicode(), iCurStack, strOpcode.GetUnicode(), strArgument.GetUnicode(), strTokenName.GetUnicode()); } else { StackScratchBuffer strLabelBuffer; StackScratchBuffer strOpcodeBuffer; StackScratchBuffer strArgumentBuffer; StackScratchBuffer strTokenNameBuffer; LOG((LF_STUBS, LL_INFO1000, "%s (%2d) %s %s %s\n", strLabel.GetUTF8(strLabelBuffer), iCurStack, \ strOpcode.GetUTF8(strOpcodeBuffer), strArgument.GetUTF8(strArgumentBuffer), strTokenName.GetUTF8(strTokenNameBuffer))); } } // ILStubLinker::LogILInstruction //--------------------------------------------------------------------------------------- // void ILStubLinker::LogILStubWorker( ILInstruction * pInstrBuffer, UINT numInstr, size_t * pcbCode, INT * piCurStack, SString * pDumpILStubCode) { CONTRACTL { STANDARD_VM_CHECK; PRECONDITION(CheckPointer(pcbCode)); PRECONDITION(CheckPointer(piCurStack)); PRECONDITION(CheckPointer(pDumpILStubCode, NULL_OK)); } CONTRACTL_END; bool isLabeled = false; for (UINT i = 0; i < numInstr; i++) { ILCodeStream::ILInstrEnum instr = (ILCodeStream::ILInstrEnum)pInstrBuffer[i].uInstruction; CONSISTENCY_CHECK(ILCodeStream::IsSupportedInstruction(instr)); if (instr == ILCodeStream::CEE_CODE_LABEL) { isLabeled = true; continue; } LogILInstruction(*pcbCode, isLabeled, *piCurStack, &pInstrBuffer[i], pDumpILStubCode); isLabeled = false; // // calculate the code size // PREFIX_ASSUME((size_t)instr < sizeof(s_rgbOpcodeSizes)); *pcbCode += s_rgbOpcodeSizes[instr]; // // calculate curstack // *piCurStack += pInstrBuffer[i].iStackDelta; } // Be sure to log any trailing label that has no associated instruction. if (isLabeled) { if (pDumpILStubCode) { pDumpILStubCode->AppendPrintf(W("IL_%04x:\n"), *pcbCode); } else { LOG((LF_STUBS, LL_INFO1000, "IL_%04x:\n", *pcbCode)); } } } static void LogJitFlags(DWORD facility, DWORD level, CORJIT_FLAGS jitFlags) { CONTRACTL { STANDARD_VM_CHECK; } CONTRACTL_END; LOG((facility, level, "jitFlags:\n")); #define LOG_FLAG(name) \ if (jitFlags.IsSet(name)) \ { \ LOG((facility, level, " " #name "\n")); \ jitFlags.Clear(name); \ } // these are all we care about at the moment LOG_FLAG(CORJIT_FLAGS::CORJIT_FLAG_IL_STUB); LOG_FLAG(CORJIT_FLAGS::CORJIT_FLAG_PUBLISH_SECRET_PARAM); #undef LOG_FLAGS if (!jitFlags.IsEmpty()) { LOG((facility, level, "UNKNOWN FLAGS also set\n")); } } void ILStubLinker::LogILStub(CORJIT_FLAGS jitFlags, SString *pDumpILStubCode) { CONTRACTL { STANDARD_VM_CHECK; PRECONDITION(CheckPointer(pDumpILStubCode, NULL_OK)); } CONTRACTL_END; ILCodeStream* pCurrentStream = m_pCodeStreamList; size_t cbCode = 0; INT iCurStack = 0; if (pDumpILStubCode == NULL) LogJitFlags(LF_STUBS, LL_INFO1000, jitFlags); while (pCurrentStream) { if (pCurrentStream->m_pqbILInstructions) { if (pDumpILStubCode) pDumpILStubCode->AppendPrintf("// %s {\n", pCurrentStream->GetStreamDescription(pCurrentStream->GetStreamType())); else LOG((LF_STUBS, LL_INFO1000, "%s {\n", pCurrentStream->GetStreamDescription(pCurrentStream->GetStreamType()))); ILInstruction* pInstrBuffer = (ILInstruction*)pCurrentStream->m_pqbILInstructions->Ptr(); LogILStubWorker(pInstrBuffer, pCurrentStream->m_uCurInstrIdx, &cbCode, &iCurStack, pDumpILStubCode); if (pDumpILStubCode) pDumpILStubCode->AppendPrintf("// } %s \n", pCurrentStream->GetStreamDescription(pCurrentStream->GetStreamType())); else LOG((LF_STUBS, LL_INFO1000, "}\n")); } pCurrentStream = pCurrentStream->m_pNextStream; } } bool ILStubLinker::FirstPassLink(ILInstruction* pInstrBuffer, UINT numInstr, size_t* pcbCode, INT* piCurStack, UINT* puMaxStack) { CONTRACTL { STANDARD_VM_CHECK; PRECONDITION(CheckPointer(puMaxStack)); } CONTRACTL_END; bool fStackUnderflow = false; for (UINT i = 0; i < numInstr; i++) { ILCodeStream::ILInstrEnum instr = (ILCodeStream::ILInstrEnum)pInstrBuffer[i].uInstruction; CONSISTENCY_CHECK(ILCodeStream::IsSupportedInstruction(instr)); // // down-size instructions // instr = ILCodeStream::LowerOpcode(instr, &pInstrBuffer[i]); // // fill in code label offsets // if (instr == ILCodeStream::CEE_CODE_LABEL) { ILCodeLabel* pLabel = (ILCodeLabel*)(pInstrBuffer[i].uArg); pLabel->SetCodeOffset(*pcbCode); } // // calculate the code size // PREFIX_ASSUME((size_t)instr < sizeof(s_rgbOpcodeSizes)); *pcbCode += s_rgbOpcodeSizes[instr]; // // calculate maxstack // *piCurStack += pInstrBuffer[i].iStackDelta; if (*piCurStack > (INT)*puMaxStack) { *puMaxStack = *piCurStack; } #ifdef _DEBUG if (*piCurStack < 0) { fStackUnderflow = true; } #endif // _DEBUG } return fStackUnderflow; } void ILStubLinker::SecondPassLink(ILInstruction* pInstrBuffer, UINT numInstr, size_t* pCurCodeOffset) { CONTRACTL { STANDARD_VM_CHECK; PRECONDITION(CheckPointer(pCurCodeOffset)); } CONTRACTL_END; for (UINT i = 0; i < numInstr; i++) { ILCodeStream::ILInstrEnum instr = (ILCodeStream::ILInstrEnum)pInstrBuffer[i].uInstruction; CONSISTENCY_CHECK(ILCodeStream::IsSupportedInstruction(instr)); *pCurCodeOffset += s_rgbOpcodeSizes[instr]; if (ILCodeStream::IsBranchInstruction(instr)) { ILCodeLabel* pLabel = (ILCodeLabel*) pInstrBuffer[i].uArg; CONSISTENCY_CHECK(this == pLabel->m_pOwningStubLinker); CONSISTENCY_CHECK(IsInCodeStreamList(pLabel->m_pCodeStreamOfLabel)); pInstrBuffer[i].uArg = pLabel->GetCodeOffset() - *pCurCodeOffset; } } } size_t ILStubLinker::Link(UINT* puMaxStack) { CONTRACTL { STANDARD_VM_CHECK; PRECONDITION(CheckPointer(puMaxStack)); } CONTRACTL_END; // // Pass1: calculate code size, lower instructions to smallest form, // fill in branch target offsets, and calculate maxstack // ILCodeStream* pCurrentStream = m_pCodeStreamList; size_t cbCode = 0; INT iCurStack = 0; UINT uMaxStack = 0; DEBUG_STMT(bool fStackUnderflow = false); while (pCurrentStream) { if (pCurrentStream->m_pqbILInstructions) { ILInstruction* pInstrBuffer = (ILInstruction*)pCurrentStream->m_pqbILInstructions->Ptr(); INDEBUG( fStackUnderflow = ) FirstPassLink(pInstrBuffer, pCurrentStream->m_uCurInstrIdx, &cbCode, &iCurStack, &uMaxStack); } pCurrentStream = pCurrentStream->m_pNextStream; } // // Pass2: go back and patch the branch instructions // pCurrentStream = m_pCodeStreamList; size_t curCodeOffset = 0; while (pCurrentStream) { if (pCurrentStream->m_pqbILInstructions) { ILInstruction* pInstrBuffer = (ILInstruction*)pCurrentStream->m_pqbILInstructions->Ptr(); SecondPassLink(pInstrBuffer, pCurrentStream->m_uCurInstrIdx, &curCodeOffset); } pCurrentStream = pCurrentStream->m_pNextStream; } #ifdef _DEBUG if (fStackUnderflow) { LogILStub(CORJIT_FLAGS()); CONSISTENCY_CHECK_MSG(false, "IL stack underflow! -- see logging output"); } #endif // _DEBUG *puMaxStack = uMaxStack; return cbCode; } #ifdef _DEBUG static const PCSTR s_rgOpNames[] = { #define OPDEF(name,string,pop,push,oprType,opcType,l,s1,s2,ctrl) \ #name, #include "opcode.def" #undef OPDEF }; #endif // _DEBUG BYTE* ILStubLinker::GenerateCodeWorker(BYTE* pbBuffer, ILInstruction* pInstrBuffer, UINT numInstr, size_t* pcbCode) { CONTRACTL { STANDARD_VM_CHECK; PRECONDITION(CheckPointer(pcbCode)); } CONTRACTL_END; for (UINT i = 0; i < numInstr; i++) { ILCodeStream::ILInstrEnum instr = (ILCodeStream::ILInstrEnum)pInstrBuffer[i].uInstruction; CONSISTENCY_CHECK(ILCodeStream::IsSupportedInstruction(instr)); // // copy the IL instructions from the various linkers into the given buffer // if (instr != ILCodeStream::CEE_CODE_LABEL) { const ILOpcode* pOpcode = &s_rgOpcodes[instr]; PREFIX_ASSUME((size_t)instr < sizeof(s_rgbOpcodeSizes)); int opSize = s_rgbOpcodeSizes[instr]; bool twoByteOp = (pOpcode->byte1 != 0xFF); int argSize = opSize - (twoByteOp ? 2 : 1); if (twoByteOp) { *pbBuffer++ = pOpcode->byte1; } *pbBuffer++ = pOpcode->byte2; switch (argSize) { case 0: break; case 1: *pbBuffer = (BYTE)pInstrBuffer[i].uArg; break; case 2: SET_UNALIGNED_VAL16(pbBuffer, pInstrBuffer[i].uArg); break; case 4: SET_UNALIGNED_VAL32(pbBuffer, pInstrBuffer[i].uArg); break; case 8: { UINT64 uVal = pInstrBuffer[i].uArg; #ifndef _WIN64 // We don't have room on 32-bit platforms to store the CLR_NAN_64 value, so // we use a special value to represent CLR_NAN_64 and then recreate it here. if ((instr == ILCodeStream::CEE_LDC_R8) && (((UINT32)uVal) == ILCodeStream::SPECIAL_VALUE_NAN_64_ON_32)) uVal = CLR_NAN_64; #endif // _WIN64 SET_UNALIGNED_VAL64(pbBuffer, uVal); } break; default: UNREACHABLE_MSG("unexpected il opcode argument size"); } pbBuffer += argSize; *pcbCode += opSize; } } return pbBuffer; } void ILStubLinker::GenerateCode(BYTE* pbBuffer, size_t cbBufferSize) { STANDARD_VM_CONTRACT; ILCodeStream* pCurrentStream = m_pCodeStreamList; size_t cbCode = 0; while (pCurrentStream) { if (pCurrentStream->m_pqbILInstructions) { ILInstruction* pInstrBuffer = (ILInstruction*)pCurrentStream->m_pqbILInstructions->Ptr(); pbBuffer = GenerateCodeWorker(pbBuffer, pInstrBuffer, pCurrentStream->m_uCurInstrIdx, &cbCode); } pCurrentStream = pCurrentStream->m_pNextStream; } CONSISTENCY_CHECK(cbCode <= cbBufferSize); } #ifdef _DEBUG bool ILStubLinker::IsInCodeStreamList(ILCodeStream* pcs) { LIMITED_METHOD_CONTRACT; ILCodeStream* pCurrentStream = m_pCodeStreamList; while (pCurrentStream) { if (pcs == pCurrentStream) { return true; } pCurrentStream = pCurrentStream->m_pNextStream; } return false; } // static bool ILCodeStream::IsSupportedInstruction(ILInstrEnum instr) { LIMITED_METHOD_CONTRACT; CONSISTENCY_CHECK_MSG(instr != CEE_SWITCH, "CEE_SWITCH is not supported currently due to InlineSwitch in s_rgbOpcodeSizes"); CONSISTENCY_CHECK_MSG(((instr >= CEE_BR_S) && (instr <= CEE_BLT_UN_S)) || (CEE_LEAVE), "we only use long-form branch opcodes"); return true; } #endif // _DEBUG LPCSTR ILCodeStream::GetStreamDescription(ILStubLinker::CodeStreamType streamType) { LIMITED_METHOD_CONTRACT; static LPCSTR lpszDescriptions[] = { "Initialize", "Marshal", "CallMethod", "UnmarshalReturn", "Unmarshal", "ExceptionCleanup", "Cleanup", "ExceptionHandler", }; #ifdef _DEBUG size_t len = sizeof(lpszDescriptions)/sizeof(LPCSTR); _ASSERT(streamType >= 0 && (size_t)streamType < len); #endif // _DEBUG return lpszDescriptions[streamType]; } void ILCodeStream::EmitADD() { WRAPPER_NO_CONTRACT; Emit(CEE_ADD, -1, 0); } void ILCodeStream::EmitADD_OVF() { WRAPPER_NO_CONTRACT; Emit(CEE_ADD_OVF, -1, 0); } void ILCodeStream::EmitAND() { WRAPPER_NO_CONTRACT; Emit(CEE_AND, -1, 0); } void ILCodeStream::EmitARGLIST() { WRAPPER_NO_CONTRACT; Emit(CEE_ARGLIST, 1, 0); } void ILCodeStream::EmitBEQ(ILCodeLabel* pCodeLabel) { WRAPPER_NO_CONTRACT; Emit(CEE_BEQ, -2, (UINT_PTR)pCodeLabel); } void ILCodeStream::EmitBGE(ILCodeLabel* pCodeLabel) { WRAPPER_NO_CONTRACT; Emit(CEE_BGE, -2, (UINT_PTR)pCodeLabel); } void ILCodeStream::EmitBGE_UN(ILCodeLabel* pCodeLabel) { WRAPPER_NO_CONTRACT; Emit(CEE_BGE_UN, -2, (UINT_PTR)pCodeLabel); } void ILCodeStream::EmitBGT(ILCodeLabel* pCodeLabel) { WRAPPER_NO_CONTRACT; Emit(CEE_BGT, -2, (UINT_PTR)pCodeLabel); } void ILCodeStream::EmitBLE(ILCodeLabel* pCodeLabel) { WRAPPER_NO_CONTRACT; Emit(CEE_BLE, -2, (UINT_PTR)pCodeLabel); } void ILCodeStream::EmitBLE_UN(ILCodeLabel* pCodeLabel) { WRAPPER_NO_CONTRACT; Emit(CEE_BLE_UN, -2, (UINT_PTR)pCodeLabel); } void ILCodeStream::EmitBLT(ILCodeLabel* pCodeLabel) { WRAPPER_NO_CONTRACT; Emit(CEE_BLT, -2, (UINT_PTR)pCodeLabel); } void ILCodeStream::EmitBR(ILCodeLabel* pCodeLabel) { WRAPPER_NO_CONTRACT; Emit(CEE_BR, 0, (UINT_PTR)pCodeLabel); } void ILCodeStream::EmitBREAK() { WRAPPER_NO_CONTRACT; Emit(CEE_BREAK, 0, 0); } void ILCodeStream::EmitBRFALSE(ILCodeLabel* pCodeLabel) { WRAPPER_NO_CONTRACT; Emit(CEE_BRFALSE, -1, (UINT_PTR)pCodeLabel); } void ILCodeStream::EmitBRTRUE(ILCodeLabel* pCodeLabel) { WRAPPER_NO_CONTRACT; Emit(CEE_BRTRUE, -1, (UINT_PTR)pCodeLabel); } void ILCodeStream::EmitCALL(int token, int numInArgs, int numRetArgs) { WRAPPER_NO_CONTRACT; Emit(CEE_CALL, (INT16)(numRetArgs - numInArgs), token); } void ILCodeStream::EmitCALLI(int token, int numInArgs, int numRetArgs) { WRAPPER_NO_CONTRACT; Emit(CEE_CALLI, (INT16)(numRetArgs - numInArgs - 1), token); } void ILCodeStream::EmitCEQ () { WRAPPER_NO_CONTRACT; Emit(CEE_CEQ, -1, 0); } void ILCodeStream::EmitCGT() { WRAPPER_NO_CONTRACT; Emit(CEE_CGT, -1, 0); } void ILCodeStream::EmitCGT_UN() { WRAPPER_NO_CONTRACT; Emit(CEE_CGT_UN, -1, 0); } void ILCodeStream::EmitCLT() { WRAPPER_NO_CONTRACT; Emit(CEE_CLT, -1, 0); } void ILCodeStream::EmitCLT_UN() { WRAPPER_NO_CONTRACT; Emit(CEE_CLT_UN, -1, 0); } void ILCodeStream::EmitCONV_I() { WRAPPER_NO_CONTRACT; Emit(CEE_CONV_I, 0, 0); } void ILCodeStream::EmitCONV_I1() { WRAPPER_NO_CONTRACT; Emit(CEE_CONV_I1, 0, 0); } void ILCodeStream::EmitCONV_I2() { WRAPPER_NO_CONTRACT; Emit(CEE_CONV_I2, 0, 0); } void ILCodeStream::EmitCONV_I4() { WRAPPER_NO_CONTRACT; Emit(CEE_CONV_I4, 0, 0); } void ILCodeStream::EmitCONV_I8() { WRAPPER_NO_CONTRACT; Emit(CEE_CONV_I8, 0, 0); } void ILCodeStream::EmitCONV_U() { WRAPPER_NO_CONTRACT; Emit(CEE_CONV_U, 0, 0); } void ILCodeStream::EmitCONV_U1() { WRAPPER_NO_CONTRACT; Emit(CEE_CONV_U1, 0, 0); } void ILCodeStream::EmitCONV_U2() { WRAPPER_NO_CONTRACT; Emit(CEE_CONV_U2, 0, 0); } void ILCodeStream::EmitCONV_U4() { WRAPPER_NO_CONTRACT; Emit(CEE_CONV_U4, 0, 0); } void ILCodeStream::EmitCONV_U8() { WRAPPER_NO_CONTRACT; Emit(CEE_CONV_U8, 0, 0); } void ILCodeStream::EmitCONV_R4() { WRAPPER_NO_CONTRACT; Emit(CEE_CONV_R4, 0, 0); } void ILCodeStream::EmitCONV_R8() { WRAPPER_NO_CONTRACT; Emit(CEE_CONV_R8, 0, 0); } void ILCodeStream::EmitCONV_OVF_I4() { WRAPPER_NO_CONTRACT; Emit(CEE_CONV_OVF_I4, 0, 0); } void ILCodeStream::EmitCONV_T(CorElementType t) { STANDARD_VM_CONTRACT; switch (t) { case ELEMENT_TYPE_U1: EmitCONV_U1(); break; case ELEMENT_TYPE_I1: EmitCONV_I1(); break; case ELEMENT_TYPE_U2: EmitCONV_U2(); break; case ELEMENT_TYPE_I2: EmitCONV_I2(); break; case ELEMENT_TYPE_U4: EmitCONV_U4(); break; case ELEMENT_TYPE_I4: EmitCONV_I4(); break; case ELEMENT_TYPE_U8: EmitCONV_U8(); break; case ELEMENT_TYPE_I8: EmitCONV_I8(); break; case ELEMENT_TYPE_R4: EmitCONV_R4(); break; case ELEMENT_TYPE_R8: EmitCONV_R8(); break; case ELEMENT_TYPE_I: EmitCONV_I(); break; case ELEMENT_TYPE_U: EmitCONV_U(); break; default: _ASSERTE(!"Invalid type for conversion"); break; } } void ILCodeStream::EmitCPBLK() { WRAPPER_NO_CONTRACT; Emit(CEE_CPBLK, -3, 0); } void ILCodeStream::EmitCPOBJ(int token) { WRAPPER_NO_CONTRACT; Emit(CEE_CPOBJ, -2, token); } void ILCodeStream::EmitDUP () { WRAPPER_NO_CONTRACT; Emit(CEE_DUP, 1, 0); } void ILCodeStream::EmitENDFINALLY() { WRAPPER_NO_CONTRACT; Emit(CEE_ENDFINALLY, 0, 0); } void ILCodeStream::EmitINITBLK() { WRAPPER_NO_CONTRACT; Emit(CEE_INITBLK, -3, 0); } void ILCodeStream::EmitINITOBJ(int token) { WRAPPER_NO_CONTRACT; Emit(CEE_INITOBJ, -1, token); } void ILCodeStream::EmitJMP(int token) { WRAPPER_NO_CONTRACT; Emit(CEE_JMP, 0, token); } void ILCodeStream::EmitLDARG (unsigned uArgIdx) { WRAPPER_NO_CONTRACT; if (m_pOwner->m_fHasThis) { uArgIdx++; } Emit(CEE_LDARG, 1, uArgIdx); } void ILCodeStream::EmitLDARGA (unsigned uArgIdx) { WRAPPER_NO_CONTRACT; if (m_pOwner->m_fHasThis) { uArgIdx++; } Emit(CEE_LDARGA, 1, uArgIdx); } void ILCodeStream::EmitLDC(DWORD_PTR uConst) { WRAPPER_NO_CONTRACT; Emit( #ifdef _WIN64 CEE_LDC_I8 #else CEE_LDC_I4 #endif , 1, uConst); } void ILCodeStream::EmitLDC_R4(UINT32 uConst) { WRAPPER_NO_CONTRACT; Emit(CEE_LDC_R4, 1, uConst); } void ILCodeStream::EmitLDC_R8(UINT64 uConst) { STANDARD_VM_CONTRACT; #ifndef _WIN64 // We don't have room on 32-bit platforms to stor the CLR_NAN_64 value, so // we use a special value to represent CLR_NAN_64 and then recreate it later. CONSISTENCY_CHECK(((UINT32)uConst) != SPECIAL_VALUE_NAN_64_ON_32); if (uConst == CLR_NAN_64) uConst = SPECIAL_VALUE_NAN_64_ON_32; else CONSISTENCY_CHECK(FitsInU4(uConst)); #endif // _WIN64 Emit(CEE_LDC_R8, 1, (UINT_PTR)uConst); } void ILCodeStream::EmitLDELEM_REF() { WRAPPER_NO_CONTRACT; Emit(CEE_LDELEM_REF, -1, 0); } void ILCodeStream::EmitLDFLD(int token) { WRAPPER_NO_CONTRACT; Emit(CEE_LDFLD, 0, token); } void ILCodeStream::EmitLDFLDA(int token) { WRAPPER_NO_CONTRACT; Emit(CEE_LDFLDA, 0, token); } void ILCodeStream::EmitLDFTN(int token) { WRAPPER_NO_CONTRACT; Emit(CEE_LDFTN, 1, token); } void ILCodeStream::EmitLDIND_I() { WRAPPER_NO_CONTRACT; Emit(CEE_LDIND_I, 0, 0); } void ILCodeStream::EmitLDIND_I1() { WRAPPER_NO_CONTRACT; Emit(CEE_LDIND_I1, 0, 0); } void ILCodeStream::EmitLDIND_I2() { WRAPPER_NO_CONTRACT; Emit(CEE_LDIND_I2, 0, 0); } void ILCodeStream::EmitLDIND_I4() { WRAPPER_NO_CONTRACT; Emit(CEE_LDIND_I4, 0, 0); } void ILCodeStream::EmitLDIND_I8() { WRAPPER_NO_CONTRACT; Emit(CEE_LDIND_I8, 0, 0); } void ILCodeStream::EmitLDIND_R4() { WRAPPER_NO_CONTRACT; Emit(CEE_LDIND_R4, 0, 0); } void ILCodeStream::EmitLDIND_R8() { WRAPPER_NO_CONTRACT; Emit(CEE_LDIND_R8, 0, 0); } void ILCodeStream::EmitLDIND_REF() { WRAPPER_NO_CONTRACT; Emit(CEE_LDIND_REF, 0, 0); } void ILCodeStream::EmitLDIND_T(LocalDesc* pType) { CONTRACTL { PRECONDITION(pType->cbType == 1); } CONTRACTL_END; switch (pType->ElementType[0]) { case ELEMENT_TYPE_I1: EmitLDIND_I1(); break; case ELEMENT_TYPE_BOOLEAN: // fall through case ELEMENT_TYPE_U1: EmitLDIND_U1(); break; case ELEMENT_TYPE_I2: EmitLDIND_I2(); break; case ELEMENT_TYPE_CHAR: // fall through case ELEMENT_TYPE_U2: EmitLDIND_U2(); break; case ELEMENT_TYPE_I4: EmitLDIND_I4(); break; case ELEMENT_TYPE_U4: EmitLDIND_U4(); break; case ELEMENT_TYPE_I8: EmitLDIND_I8(); break; case ELEMENT_TYPE_U8: EmitLDIND_I8(); break; case ELEMENT_TYPE_R4: EmitLDIND_R4(); break; case ELEMENT_TYPE_R8: EmitLDIND_R8(); break; case ELEMENT_TYPE_PTR: // same as ELEMENT_TYPE_I case ELEMENT_TYPE_FNPTR: // same as ELEMENT_TYPE_I case ELEMENT_TYPE_I: EmitLDIND_I(); break; case ELEMENT_TYPE_U: EmitLDIND_I(); break; case ELEMENT_TYPE_STRING: // fall through case ELEMENT_TYPE_CLASS: // fall through case ELEMENT_TYPE_ARRAY: case ELEMENT_TYPE_SZARRAY: case ELEMENT_TYPE_OBJECT: EmitLDIND_REF(); break; case ELEMENT_TYPE_INTERNAL: { CONSISTENCY_CHECK_MSG(!(pType->InternalToken.GetMethodTable()->IsValueType()), "don't know how to handle value types here"); EmitLDIND_REF(); break; } default: UNREACHABLE_MSG("unexpected type passed to EmitLDIND_T"); break; } } void ILCodeStream::EmitLDIND_U1() { WRAPPER_NO_CONTRACT; Emit(CEE_LDIND_U1, 0, 0); } void ILCodeStream::EmitLDIND_U2() { WRAPPER_NO_CONTRACT; Emit(CEE_LDIND_U2, 0, 0); } void ILCodeStream::EmitLDIND_U4() { WRAPPER_NO_CONTRACT; Emit(CEE_LDIND_U4, 0, 0); } void ILCodeStream::EmitLDLEN() { WRAPPER_NO_CONTRACT; Emit(CEE_LDLEN, 0, 0); } void ILCodeStream::EmitLDLOC (DWORD dwLocalNum) { STANDARD_VM_CONTRACT; CONSISTENCY_CHECK(dwLocalNum != (DWORD)-1); CONSISTENCY_CHECK(dwLocalNum != (WORD)-1); Emit(CEE_LDLOC, 1, dwLocalNum); } void ILCodeStream::EmitLDLOCA (DWORD dwLocalNum) { WRAPPER_NO_CONTRACT; Emit(CEE_LDLOCA, 1, dwLocalNum); } void ILCodeStream::EmitLDNULL() { WRAPPER_NO_CONTRACT; Emit(CEE_LDNULL, 1, 0); } void ILCodeStream::EmitLDOBJ (int token) { WRAPPER_NO_CONTRACT; Emit(CEE_LDOBJ, 0, token); } void ILCodeStream::EmitLDSFLD(int token) { WRAPPER_NO_CONTRACT; Emit(CEE_LDSFLD, 1, token); } void ILCodeStream::EmitLDSFLDA(int token) { WRAPPER_NO_CONTRACT; Emit(CEE_LDSFLDA, 1, token); } void ILCodeStream::EmitLDTOKEN(int token) { WRAPPER_NO_CONTRACT; Emit(CEE_LDTOKEN, 1, token); } void ILCodeStream::EmitLEAVE(ILCodeLabel* pCodeLabel) { WRAPPER_NO_CONTRACT; Emit(CEE_LEAVE, 0, (UINT_PTR)pCodeLabel); } void ILCodeStream::EmitLOCALLOC() { WRAPPER_NO_CONTRACT; Emit(CEE_LOCALLOC, 0, 0); } void ILCodeStream::EmitMUL() { WRAPPER_NO_CONTRACT; Emit(CEE_MUL, -1, 0); } void ILCodeStream::EmitMUL_OVF() { WRAPPER_NO_CONTRACT; Emit(CEE_MUL_OVF, -1, 0); } void ILCodeStream::EmitNEWOBJ(int token, int numInArgs) { WRAPPER_NO_CONTRACT; Emit(CEE_NEWOBJ, (INT16)(1 - numInArgs), token); } void ILCodeStream::EmitNOP(LPCSTR pszNopComment) { WRAPPER_NO_CONTRACT; Emit(CEE_NOP, 0, (UINT_PTR)pszNopComment); } void ILCodeStream::EmitPOP() { WRAPPER_NO_CONTRACT; Emit(CEE_POP, -1, 0); } void ILCodeStream::EmitRET() { WRAPPER_NO_CONTRACT; INT16 iStackDelta = m_pOwner->m_StubHasVoidReturnType ? 0 : -1; Emit(CEE_RET, iStackDelta, 0); } void ILCodeStream::EmitSHR_UN() { WRAPPER_NO_CONTRACT; Emit(CEE_SHR_UN, -1, 0); } void ILCodeStream::EmitSTARG(unsigned uArgIdx) { WRAPPER_NO_CONTRACT; Emit(CEE_STARG, -1, uArgIdx); } void ILCodeStream::EmitSTELEM_REF() { WRAPPER_NO_CONTRACT; Emit(CEE_STELEM_REF, -3, 0); } void ILCodeStream::EmitSTIND_I() { WRAPPER_NO_CONTRACT; Emit(CEE_STIND_I, -2, 0); } void ILCodeStream::EmitSTIND_I1() { WRAPPER_NO_CONTRACT; Emit(CEE_STIND_I1, -2, 0); } void ILCodeStream::EmitSTIND_I2() { WRAPPER_NO_CONTRACT; Emit(CEE_STIND_I2, -2, 0); } void ILCodeStream::EmitSTIND_I4() { WRAPPER_NO_CONTRACT; Emit(CEE_STIND_I4, -2, 0); } void ILCodeStream::EmitSTIND_I8() { WRAPPER_NO_CONTRACT; Emit(CEE_STIND_I8, -2, 0); } void ILCodeStream::EmitSTIND_R4() { WRAPPER_NO_CONTRACT; Emit(CEE_STIND_R4, -2, 0); } void ILCodeStream::EmitSTIND_R8() { WRAPPER_NO_CONTRACT; Emit(CEE_STIND_R8, -2, 0); } void ILCodeStream::EmitSTIND_REF() { WRAPPER_NO_CONTRACT; Emit(CEE_STIND_REF, -2, 0); } void ILCodeStream::EmitSTIND_T(LocalDesc* pType) { CONTRACTL { PRECONDITION(pType->cbType == 1); } CONTRACTL_END; switch (pType->ElementType[0]) { case ELEMENT_TYPE_I1: EmitSTIND_I1(); break; case ELEMENT_TYPE_BOOLEAN: // fall through case ELEMENT_TYPE_U1: EmitSTIND_I1(); break; case ELEMENT_TYPE_I2: EmitSTIND_I2(); break; case ELEMENT_TYPE_CHAR: // fall through case ELEMENT_TYPE_U2: EmitSTIND_I2(); break; case ELEMENT_TYPE_I4: EmitSTIND_I4(); break; case ELEMENT_TYPE_U4: EmitSTIND_I4(); break; case ELEMENT_TYPE_I8: EmitSTIND_I8(); break; case ELEMENT_TYPE_U8: EmitSTIND_I8(); break; case ELEMENT_TYPE_R4: EmitSTIND_R4(); break; case ELEMENT_TYPE_R8: EmitSTIND_R8(); break; case ELEMENT_TYPE_PTR: // same as ELEMENT_TYPE_I case ELEMENT_TYPE_FNPTR: // same as ELEMENT_TYPE_I case ELEMENT_TYPE_I: EmitSTIND_I(); break; case ELEMENT_TYPE_U: EmitSTIND_I(); break; case ELEMENT_TYPE_STRING: // fall through case ELEMENT_TYPE_CLASS: // fall through case ELEMENT_TYPE_ARRAY: case ELEMENT_TYPE_SZARRAY: case ELEMENT_TYPE_OBJECT: EmitSTIND_REF(); break; case ELEMENT_TYPE_INTERNAL: { CONSISTENCY_CHECK_MSG(!(pType->InternalToken.GetMethodTable()->IsValueType()), "don't know how to handle value types here"); EmitSTIND_REF(); break; } default: UNREACHABLE_MSG("unexpected type passed to EmitSTIND_T"); break; } } void ILCodeStream::EmitSTFLD(int token) { WRAPPER_NO_CONTRACT; Emit(CEE_STFLD, -2, token); } void ILCodeStream::EmitSTLOC(DWORD dwLocalNum) { WRAPPER_NO_CONTRACT; Emit(CEE_STLOC, -1, dwLocalNum); } void ILCodeStream::EmitSTOBJ(int token) { WRAPPER_NO_CONTRACT; Emit(CEE_STOBJ, -2, token); } void ILCodeStream::EmitSTSFLD(int token) { WRAPPER_NO_CONTRACT; Emit(CEE_STSFLD, -1, token); } void ILCodeStream::EmitSUB() { WRAPPER_NO_CONTRACT; Emit(CEE_SUB, -1, 0); } void ILCodeStream::EmitTHROW() { WRAPPER_NO_CONTRACT; Emit(CEE_THROW, -1, 0); } void ILCodeStream::EmitNEWOBJ(BinderMethodID id, int numInArgs) { STANDARD_VM_CONTRACT; EmitNEWOBJ(GetToken(MscorlibBinder::GetMethod(id)), numInArgs); } void ILCodeStream::EmitCALL(BinderMethodID id, int numInArgs, int numRetArgs) { STANDARD_VM_CONTRACT; EmitCALL(GetToken(MscorlibBinder::GetMethod(id)), numInArgs, numRetArgs); } void ILStubLinker::SetHasThis (bool fHasThis) { LIMITED_METHOD_CONTRACT; m_fHasThis = fHasThis; } void ILCodeStream::EmitLoadThis () { WRAPPER_NO_CONTRACT; _ASSERTE(m_pOwner->m_fHasThis); // OK, this is ugly, but we add 1 to all LDARGs when // m_fHasThis is true, so we compensate for that here // so that we don't have to have a special method to // load arguments. EmitLDARG((unsigned)-1); } void ILCodeStream::EmitLoadNullPtr() { WRAPPER_NO_CONTRACT; // This is the correct way to load unmanaged zero pointer. EmitLDC(0) alone works // fine in most cases but may lead to wrong code being generated on 64-bit if the // flow graph is complex. EmitLDC(0); EmitCONV_I(); } void ILCodeStream::EmitArgIteratorCreateAndLoad() { STANDARD_VM_CONTRACT; // // we insert the ArgIterator in the same spot that the VASigCookie will go for sanity // LocalDesc aiLoc(MscorlibBinder::GetClass(CLASS__ARG_ITERATOR)); int aiLocNum; aiLocNum = NewLocal(aiLoc); EmitLDLOCA(aiLocNum); EmitDUP(); EmitARGLIST(); EmitLoadNullPtr(); EmitCALL(METHOD__ARG_ITERATOR__CTOR2, 2, 0); aiLoc.ElementType[0] = ELEMENT_TYPE_BYREF; aiLoc.ElementType[1] = ELEMENT_TYPE_INTERNAL; aiLoc.cbType = 2; aiLoc.InternalToken = MscorlibBinder::GetClass(CLASS__ARG_ITERATOR); SetStubTargetArgType(&aiLoc, false); } DWORD ILStubLinker::NewLocal(CorElementType typ) { CONTRACTL { STANDARD_VM_CHECK; INJECT_FAULT(COMPlusThrowOM()); } CONTRACTL_END; LocalDesc locDesc(typ); return NewLocal(locDesc); } StubSigBuilder::StubSigBuilder() : m_nItems(0), m_cbSig(0) { STANDARD_VM_CONTRACT; m_pbSigCursor = (BYTE*) m_qbSigBuffer.AllocThrows(INITIAL_BUFFER_SIZE); } void StubSigBuilder::EnsureEnoughQuickBytes(size_t cbToAppend) { STANDARD_VM_CONTRACT; SIZE_T cbBuffer = m_qbSigBuffer.Size(); if ((m_cbSig + cbToAppend) >= cbBuffer) { m_qbSigBuffer.ReSizeThrows(2 * cbBuffer); m_pbSigCursor = ((BYTE*)m_qbSigBuffer.Ptr()) + m_cbSig; } } DWORD StubSigBuilder::Append(LocalDesc* pLoc) { CONTRACTL { STANDARD_VM_CHECK; INJECT_FAULT(COMPlusThrowOM()); PRECONDITION(CheckPointer(pLoc)); } CONTRACTL_END; EnsureEnoughQuickBytes(pLoc->cbType + sizeof(TypeHandle)); memcpyNoGCRefs(m_pbSigCursor, pLoc->ElementType, pLoc->cbType); m_pbSigCursor += pLoc->cbType; m_cbSig += pLoc->cbType; size_t i = 0; while (i < pLoc->cbType) { CONSISTENCY_CHECK( ELEMENT_TYPE_CLASS != pLoc->ElementType[i] && ELEMENT_TYPE_VALUETYPE != pLoc->ElementType[i]); switch (pLoc->ElementType[i]) { case ELEMENT_TYPE_INTERNAL: SET_UNALIGNED_PTR(m_pbSigCursor, (UINT_PTR)pLoc->InternalToken.AsPtr()); m_pbSigCursor += sizeof(TypeHandle); m_cbSig += sizeof(TypeHandle); break; case ELEMENT_TYPE_FNPTR: { SigPointer ptr(pLoc->pSig); SigBuilder sigBuilder; ptr.ConvertToInternalSignature(pLoc->pSigModule, NULL, &sigBuilder); DWORD cbFnPtrSig; PVOID pFnPtrSig = sigBuilder.GetSignature(&cbFnPtrSig); EnsureEnoughQuickBytes(cbFnPtrSig); memcpyNoGCRefs(m_pbSigCursor, pFnPtrSig, cbFnPtrSig); m_pbSigCursor += cbFnPtrSig; m_cbSig += cbFnPtrSig; } break; default: break; } i++; } if (pLoc->ElementType[0] == ELEMENT_TYPE_ARRAY) { EnsureEnoughQuickBytes(pLoc->cbArrayBoundsInfo); memcpyNoGCRefs(m_pbSigCursor, pLoc->pSig, pLoc->cbArrayBoundsInfo); m_pbSigCursor += pLoc->cbArrayBoundsInfo; m_cbSig += pLoc->cbArrayBoundsInfo; } _ASSERTE(m_cbSig <= m_qbSigBuffer.Size()); // we corrupted our buffer resizing if this assert fires return m_nItems++; } //--------------------------------------------------------------------------------------- // DWORD LocalSigBuilder::GetSigSize() { STANDARD_VM_CONTRACT; BYTE temp[4]; UINT32 cbEncoded = CorSigCompressData(m_nItems, temp); S_UINT32 cbSigSize = S_UINT32(1) + // IMAGE_CEE_CS_CALLCONV_LOCAL_SIG S_UINT32(cbEncoded) + // encoded number of locals S_UINT32(m_cbSig) + // types S_UINT32(1); // ELEMENT_TYPE_END if (cbSigSize.IsOverflow()) { IfFailThrow(COR_E_OVERFLOW); } return cbSigSize.Value(); } //--------------------------------------------------------------------------------------- // DWORD LocalSigBuilder::GetSig( BYTE * pbLocalSig, DWORD cbBuffer) { STANDARD_VM_CONTRACT; BYTE temp[4]; size_t cb = CorSigCompressData(m_nItems, temp); _ASSERTE((1 + cb + m_cbSig + 1) == GetSigSize()); if ((1 + cb + m_cbSig + 1) <= cbBuffer) { pbLocalSig[0] = IMAGE_CEE_CS_CALLCONV_LOCAL_SIG; memcpyNoGCRefs(&pbLocalSig[1], temp, cb); memcpyNoGCRefs(&pbLocalSig[1 + cb], m_qbSigBuffer.Ptr(), m_cbSig); pbLocalSig[1 + cb + m_cbSig] = ELEMENT_TYPE_END; return (DWORD)(1 + cb + m_cbSig + 1); } else { return NULL; } } FunctionSigBuilder::FunctionSigBuilder() : m_callingConv(IMAGE_CEE_CS_CALLCONV_DEFAULT) { STANDARD_VM_CONTRACT; m_qbReturnSig.ReSizeThrows(1); *(CorElementType *)m_qbReturnSig.Ptr() = ELEMENT_TYPE_VOID; } void FunctionSigBuilder::SetReturnType(LocalDesc* pLoc) { CONTRACTL { STANDARD_VM_CHECK; PRECONDITION(pLoc->cbType > 0); } CONTRACTL_END; m_qbReturnSig.ReSizeThrows(pLoc->cbType); memcpyNoGCRefs(m_qbReturnSig.Ptr(), pLoc->ElementType, pLoc->cbType); size_t i = 0; while (i < pLoc->cbType) { CONSISTENCY_CHECK( ELEMENT_TYPE_CLASS != pLoc->ElementType[i] && ELEMENT_TYPE_VALUETYPE != pLoc->ElementType[i]); switch (pLoc->ElementType[i]) { case ELEMENT_TYPE_INTERNAL: m_qbReturnSig.ReSizeThrows(m_qbReturnSig.Size() + sizeof(TypeHandle)); SET_UNALIGNED_PTR((BYTE *)m_qbReturnSig.Ptr() + m_qbReturnSig.Size() - + sizeof(TypeHandle), (UINT_PTR)pLoc->InternalToken.AsPtr()); break; case ELEMENT_TYPE_FNPTR: { SigPointer ptr(pLoc->pSig); SigBuilder sigBuilder; ptr.ConvertToInternalSignature(pLoc->pSigModule, NULL, &sigBuilder); DWORD cbFnPtrSig; PVOID pFnPtrSig = sigBuilder.GetSignature(&cbFnPtrSig); m_qbReturnSig.ReSizeThrows(m_qbReturnSig.Size() + cbFnPtrSig); memcpyNoGCRefs((BYTE *)m_qbReturnSig.Ptr() + m_qbReturnSig.Size() - cbFnPtrSig, pFnPtrSig, cbFnPtrSig); } break; default: break; } i++; } if (pLoc->ElementType[0] == ELEMENT_TYPE_ARRAY) { SIZE_T size = m_qbReturnSig.Size(); m_qbReturnSig.ReSizeThrows(size + pLoc->cbArrayBoundsInfo); memcpyNoGCRefs((BYTE *)m_qbReturnSig.Ptr() + size, pLoc->pSig, pLoc->cbArrayBoundsInfo); } } void FunctionSigBuilder::SetSig(PCCOR_SIGNATURE pSig, DWORD cSig) { STANDARD_VM_CONTRACT; // parse the incoming signature SigPointer sigPtr(pSig, cSig); // 1) calling convention ULONG callConv; IfFailThrow(sigPtr.GetCallingConvInfo(&callConv)); SetCallingConv((CorCallingConvention)callConv); // 2) number of parameters IfFailThrow(sigPtr.GetData(&m_nItems)); // 3) return type PCCOR_SIGNATURE ptr = sigPtr.GetPtr(); IfFailThrow(sigPtr.SkipExactlyOne()); size_t retSigLength = sigPtr.GetPtr() - ptr; m_qbReturnSig.ReSizeThrows(retSigLength); memcpyNoGCRefs(m_qbReturnSig.Ptr(), ptr, retSigLength); // 4) parameters m_cbSig = 0; size_t cbSigLen = (cSig - (sigPtr.GetPtr() - pSig)); m_pbSigCursor = (BYTE *)m_qbSigBuffer.Ptr(); EnsureEnoughQuickBytes(cbSigLen); memcpyNoGCRefs(m_pbSigCursor, sigPtr.GetPtr(), cbSigLen); m_cbSig = cbSigLen; m_pbSigCursor += cbSigLen; } //--------------------------------------------------------------------------------------- // DWORD FunctionSigBuilder::GetSigSize() { STANDARD_VM_CONTRACT; BYTE temp[4]; DWORD cbEncodedLen = CorSigCompressData(m_nItems, temp); SIZE_T cbEncodedRetType = m_qbReturnSig.Size(); CONSISTENCY_CHECK(cbEncodedRetType > 0); S_UINT32 cbSigSize = S_UINT32(1) + // calling convention S_UINT32(cbEncodedLen) + // encoded number of args S_UINT32(cbEncodedRetType) + // encoded return type S_UINT32(m_cbSig) + // types S_UINT32(1); // ELEMENT_TYPE_END if (cbSigSize.IsOverflow()) { IfFailThrow(COR_E_OVERFLOW); } return cbSigSize.Value(); } //--------------------------------------------------------------------------------------- // DWORD FunctionSigBuilder::GetSig( BYTE * pbLocalSig, DWORD cbBuffer) { STANDARD_VM_CONTRACT; BYTE tempLen[4]; size_t cbEncodedLen = CorSigCompressData(m_nItems, tempLen); size_t cbEncodedRetType = m_qbReturnSig.Size(); CONSISTENCY_CHECK(cbEncodedRetType > 0); _ASSERTE((1 + cbEncodedLen + cbEncodedRetType + m_cbSig + 1) == GetSigSize()); if ((1 + cbEncodedLen + cbEncodedRetType + m_cbSig + 1) <= cbBuffer) { BYTE* pbCursor = pbLocalSig; *pbCursor = static_cast(m_callingConv); pbCursor++; memcpyNoGCRefs(pbCursor, tempLen, cbEncodedLen); pbCursor += cbEncodedLen; memcpyNoGCRefs(pbCursor, m_qbReturnSig.Ptr(), m_qbReturnSig.Size()); pbCursor += m_qbReturnSig.Size(); memcpyNoGCRefs(pbCursor, m_qbSigBuffer.Ptr(), m_cbSig); pbCursor += m_cbSig; pbCursor[0] = ELEMENT_TYPE_END; return (DWORD)(1 + cbEncodedLen + cbEncodedRetType + m_cbSig + 1); } else { return NULL; } } DWORD ILStubLinker::NewLocal(LocalDesc loc) { WRAPPER_NO_CONTRACT; return m_localSigBuilder.NewLocal(&loc); } //--------------------------------------------------------------------------------------- // DWORD ILStubLinker::GetLocalSigSize() { LIMITED_METHOD_CONTRACT; return m_localSigBuilder.GetSigSize(); } //--------------------------------------------------------------------------------------- // DWORD ILStubLinker::GetLocalSig( BYTE * pbLocalSig, DWORD cbBuffer) { STANDARD_VM_CONTRACT; DWORD dwRet = m_localSigBuilder.GetSig(pbLocalSig, cbBuffer); return dwRet; } //--------------------------------------------------------------------------------------- // DWORD ILStubLinker::GetStubTargetMethodSigSize() { STANDARD_VM_CONTRACT; return m_nativeFnSigBuilder.GetSigSize(); } //--------------------------------------------------------------------------------------- // DWORD ILStubLinker::GetStubTargetMethodSig( BYTE * pbSig, DWORD cbSig) { LIMITED_METHOD_CONTRACT; DWORD dwRet = m_nativeFnSigBuilder.GetSig(pbSig, cbSig); return dwRet; } void ILStubLinker::SetStubTargetMethodSig(PCCOR_SIGNATURE pSig, DWORD cSig) { STANDARD_VM_CONTRACT; m_nativeFnSigBuilder.SetSig(pSig, cSig); } static BOOL SigHasVoidReturnType(const Signature &signature) { CONTRACTL { THROWS; GC_NOTRIGGER; } CONTRACTL_END SigPointer ptr = signature.CreateSigPointer(); ULONG data; IfFailThrow(ptr.GetCallingConvInfo(&data)); // Skip number of type arguments if (data & IMAGE_CEE_CS_CALLCONV_GENERIC) { IfFailThrow(ptr.GetData(NULL)); } // skip number of args IfFailThrow(ptr.GetData(NULL)); CorElementType retType; IfFailThrow(ptr.PeekElemType(&retType)); return (ELEMENT_TYPE_VOID == retType); } ILStubLinker::ILStubLinker(Module* pStubSigModule, const Signature &signature, SigTypeContext *pTypeContext, MethodDesc *pMD, BOOL fTargetHasThis, BOOL fStubHasThis, BOOL fIsNDirectStub) : m_pCodeStreamList(NULL), m_stubSig(signature), m_pTypeContext(pTypeContext), m_pCode(NULL), m_pStubSigModule(pStubSigModule), m_pLabelList(NULL), m_StubHasVoidReturnType(0), m_iTargetStackDelta(0), m_cbCurrentCompressedSigLen(1), m_nLocals(0), m_fHasThis(false), m_pMD(pMD) { CONTRACTL { THROWS; GC_TRIGGERS; INJECT_FAULT(COMPlusThrowOM()); } CONTRACTL_END m_managedSigPtr = signature.CreateSigPointer(); if (!signature.IsEmpty()) { m_StubHasVoidReturnType = SigHasVoidReturnType(signature); // // Get the stub's calling convention. Set m_fHasThis to match // IMAGE_CEE_CS_CALLCONV_HASTHIS. // ULONG uStubCallingConvInfo; IfFailThrow(m_managedSigPtr.GetCallingConvInfo(&uStubCallingConvInfo)); if (fStubHasThis) { m_fHasThis = true; } // // If target calling convention was specified, use it instead. // Otherwise, derive one based on the stub's signature. // ULONG uCallingConvInfo = uStubCallingConvInfo; ULONG uCallingConv = (uCallingConvInfo & IMAGE_CEE_CS_CALLCONV_MASK); ULONG uNativeCallingConv; if (IMAGE_CEE_CS_CALLCONV_VARARG == uCallingConv) { // // If we have a PInvoke stub that has a VARARG calling convention // we will transition to a NATIVEVARARG calling convention for the // target call. The JIT64 knows about this calling convention, // basically it is the same as the managed vararg calling convention // except without a VASigCookie. // // If our stub is not a PInvoke stub and has a vararg calling convention, // we are most likely going to have to forward those variable arguments // on to our call target. Unfortunately, callsites to varargs methods // in IL always have full signatures (that's where the VASigCookie comes // from). But we don't have that in this case, so we play some tricks and // pass an ArgIterator down to an assembly routine that pulls out the // variable arguments and puts them in the right spot before forwarding // to the stub target. // // The net result is that we don't want to set the native calling // convention to be vararg for non-PInvoke stubs, so we just use // the default callconv. // if (!fIsNDirectStub) uNativeCallingConv = IMAGE_CEE_CS_CALLCONV_DEFAULT; else uNativeCallingConv = IMAGE_CEE_CS_CALLCONV_NATIVEVARARG; } else { uNativeCallingConv = IMAGE_CEE_CS_CALLCONV_DEFAULT; } if (fTargetHasThis && !fIsNDirectStub) { // ndirect native sig never has a 'this' pointer uNativeCallingConv |= IMAGE_CEE_CS_CALLCONV_HASTHIS; } if (fTargetHasThis) { m_iTargetStackDelta--; } m_nativeFnSigBuilder.SetCallingConv((CorCallingConvention)uNativeCallingConv); if (uStubCallingConvInfo & IMAGE_CEE_CS_CALLCONV_GENERIC) IfFailThrow(m_managedSigPtr.GetData(NULL)); // skip number of type parameters IfFailThrow(m_managedSigPtr.GetData(NULL)); // skip number of parameters IfFailThrow(m_managedSigPtr.SkipExactlyOne()); // skip return type } } ILStubLinker::~ILStubLinker() { CONTRACTL { NOTHROW; GC_TRIGGERS; MODE_ANY; } CONTRACTL_END; DeleteCodeLabels(); DeleteCodeStreams(); } void ILStubLinker::DeleteCodeLabels() { CONTRACTL { NOTHROW; MODE_ANY; GC_TRIGGERS; } CONTRACTL_END; // // walk the list of labels and free each one // ILCodeLabel* pCurrent = m_pLabelList; while (pCurrent) { ILCodeLabel* pDeleteMe = pCurrent; pCurrent = pCurrent->m_pNext; delete pDeleteMe; } m_pLabelList = NULL; } void ILStubLinker::DeleteCodeStreams() { CONTRACTL { NOTHROW; MODE_ANY; GC_TRIGGERS; } CONTRACTL_END; ILCodeStream* pCurrent = m_pCodeStreamList; while (pCurrent) { ILCodeStream* pDeleteMe = pCurrent; pCurrent = pCurrent->m_pNextStream; delete pDeleteMe; } m_pCodeStreamList = NULL; } void ILStubLinker::ClearCodeStreams() { CONTRACTL { NOTHROW; MODE_ANY; GC_TRIGGERS; } CONTRACTL_END; ILCodeStream* pCurrent = m_pCodeStreamList; while (pCurrent) { pCurrent->ClearCode(); pCurrent = pCurrent->m_pNextStream; } } void ILStubLinker::GetStubReturnType(LocalDesc* pLoc) { WRAPPER_NO_CONTRACT; GetStubReturnType(pLoc, m_pStubSigModule); } void ILStubLinker::GetStubReturnType(LocalDesc* pLoc, Module* pModule) { STANDARD_VM_CONTRACT; SigPointer ptr = m_stubSig.CreateSigPointer(); ULONG uCallingConv; int nTypeArgs = 0; int nArgs; IfFailThrow(ptr.GetCallingConvInfo(&uCallingConv)); if (uCallingConv & IMAGE_CEE_CS_CALLCONV_GENERIC) IfFailThrow(ptr.GetData((ULONG*)&nTypeArgs)); IfFailThrow(ptr.GetData((ULONG*)&nArgs)); GetManagedTypeHelper(pLoc, pModule, ptr.GetPtr(), m_pTypeContext, m_pMD); } CorCallingConvention ILStubLinker::GetStubTargetCallingConv() { LIMITED_METHOD_CONTRACT; return m_nativeFnSigBuilder.GetCallingConv(); } void ILStubLinker::TransformArgForJIT(LocalDesc *pLoc) { STANDARD_VM_CONTRACT; // Turn everything into blittable primitives. The reason this method is needed are // byrefs which are OK only when they ref stack data or are pinned. This condition // cannot be verified by code:NDirect.MarshalingRequired so we explicitly get rid // of them here. switch (pLoc->ElementType[0]) { // primitives case ELEMENT_TYPE_VOID: case ELEMENT_TYPE_BOOLEAN: case ELEMENT_TYPE_CHAR: case ELEMENT_TYPE_I1: case ELEMENT_TYPE_U1: case ELEMENT_TYPE_I2: case ELEMENT_TYPE_U2: case ELEMENT_TYPE_I4: case ELEMENT_TYPE_U4: case ELEMENT_TYPE_I8: case ELEMENT_TYPE_U8: case ELEMENT_TYPE_R4: case ELEMENT_TYPE_R8: case ELEMENT_TYPE_I: case ELEMENT_TYPE_U: { // no transformation needed break; } case ELEMENT_TYPE_VALUETYPE: { _ASSERTE(!"Should have been replaced by a native value type!"); break; } case ELEMENT_TYPE_PTR: { #ifdef _TARGET_X86_ if (pLoc->bIsCopyConstructed) { // The only pointers that we don't transform to ELEMENT_TYPE_I are those that are // ET_TYPE_CMOD_REQD/ET_TYPE_CMOD_REQD // in the original signature. This convention is understood by the UM thunk compiler // (code:UMThunkMarshInfo.CompileNExportThunk) which will generate different thunk code. // Such parameters come from unmanaged by value but must enter the IL stub by reference // because we are not supposed to make a copy. } else #endif // _TARGET_X86_ { pLoc->ElementType[0] = ELEMENT_TYPE_I; pLoc->cbType = 1; } break; } case ELEMENT_TYPE_INTERNAL: { // JIT will handle structures if (pLoc->InternalToken.IsValueType()) { _ASSERTE(pLoc->InternalToken.IsBlittable()); break; } // intentional fall-thru } // pointers, byrefs, strings, arrays, other ref types -> ELEMENT_TYPE_I default: { pLoc->ElementType[0] = ELEMENT_TYPE_I; pLoc->cbType = 1; break; } } } Module *ILStubLinker::GetStubSigModule() { LIMITED_METHOD_CONTRACT; return m_pStubSigModule; } SigTypeContext *ILStubLinker::GetStubSigTypeContext() { LIMITED_METHOD_CONTRACT; return m_pTypeContext; } void ILStubLinker::SetStubTargetReturnType(CorElementType typ) { WRAPPER_NO_CONTRACT; LocalDesc locDesc(typ); SetStubTargetReturnType(&locDesc); } void ILStubLinker::SetStubTargetReturnType(LocalDesc* pLoc) { CONTRACTL { WRAPPER(NOTHROW); WRAPPER(GC_NOTRIGGER); WRAPPER(MODE_ANY); PRECONDITION(CheckPointer(pLoc, NULL_NOT_OK)); } CONTRACTL_END; TransformArgForJIT(pLoc); m_nativeFnSigBuilder.SetReturnType(pLoc); if ((1 != pLoc->cbType) || (ELEMENT_TYPE_VOID != pLoc->ElementType[0])) { m_iTargetStackDelta++; } } DWORD ILStubLinker::SetStubTargetArgType(CorElementType typ, bool fConsumeStubArg /*= true*/) { STANDARD_VM_CONTRACT; LocalDesc locDesc(typ); return SetStubTargetArgType(&locDesc, fConsumeStubArg); } void ILStubLinker::SetStubTargetCallingConv(CorCallingConvention uNativeCallingConv) { LIMITED_METHOD_CONTRACT; m_nativeFnSigBuilder.SetCallingConv(uNativeCallingConv); } static size_t GetManagedTypeForMDArray(LocalDesc* pLoc, Module* pModule, PCCOR_SIGNATURE psigManagedArg, SigTypeContext *pTypeContext) { CONTRACTL { STANDARD_VM_CHECK; PRECONDITION(CheckPointer(pLoc)); PRECONDITION(CheckPointer(pModule)); PRECONDITION(CheckPointer(psigManagedArg)); PRECONDITION(*psigManagedArg == ELEMENT_TYPE_ARRAY); } CONTRACTL_END; SigPointer ptr; size_t cbDest = 0; // // copy ELEMENT_TYPE_ARRAY // pLoc->ElementType[cbDest] = *psigManagedArg; psigManagedArg++; cbDest++; ptr.SetSig(psigManagedArg); IfFailThrow(ptr.SkipCustomModifiers()); psigManagedArg = ptr.GetPtr(); // // get array type // pLoc->InternalToken = ptr.GetTypeHandleThrowing(pModule, pTypeContext); pLoc->ElementType[cbDest] = ELEMENT_TYPE_INTERNAL; cbDest++; // // get array bounds // size_t cbType; PCCOR_SIGNATURE psigNextManagedArg; // find the start of the next argument ptr.SetSig(psigManagedArg - 1); // -1 to back up to E_T_ARRAY; IfFailThrow(ptr.SkipExactlyOne()); psigNextManagedArg = ptr.GetPtr(); // find the start of the array bounds information ptr.SetSig(psigManagedArg); IfFailThrow(ptr.SkipExactlyOne()); psigManagedArg = ptr.GetPtr(); // point to the array bounds info cbType = psigNextManagedArg - psigManagedArg; pLoc->pSig = psigManagedArg; // point to the array bounds info pLoc->cbArrayBoundsInfo = cbType; // size of array bounds info pLoc->cbType = cbDest; return cbDest; } // static void ILStubLinker::GetManagedTypeHelper(LocalDesc* pLoc, Module* pModule, PCCOR_SIGNATURE psigManagedArg, SigTypeContext *pTypeContext, MethodDesc *pMD) { CONTRACTL { STANDARD_VM_CHECK; PRECONDITION(CheckPointer(pLoc)); PRECONDITION(CheckPointer(pModule)); PRECONDITION(CheckPointer(psigManagedArg)); } CONTRACTL_END; SigPointer ptr(psigManagedArg); CorElementType eType; LOG((LF_STUBS, LL_INFO10000, "GetManagedTypeHelper on type at %p\n", psigManagedArg)); IfFailThrow(ptr.PeekElemType(&eType)); size_t cbDest = 0; while (eType == ELEMENT_TYPE_PTR || eType == ELEMENT_TYPE_BYREF || eType == ELEMENT_TYPE_SZARRAY) { pLoc->ElementType[cbDest] = static_cast(eType); cbDest++; if (cbDest >= LocalDesc::MAX_LOCALDESC_ELEMENTS) { COMPlusThrow(kMarshalDirectiveException, IDS_EE_SIGTOOCOMPLEX); } IfFailThrow(ptr.GetElemType(NULL)); IfFailThrow(ptr.PeekElemType(&eType)); } SigPointer ptr2(ptr); IfFailThrow(ptr2.SkipCustomModifiers()); psigManagedArg = ptr2.GetPtr(); switch (eType) { case ELEMENT_TYPE_VAR: case ELEMENT_TYPE_MVAR: { IfFailThrow(ptr.GetElemType(NULL)); // skip ET ULONG varNum; IfFailThrowBF(ptr.GetData(&varNum), BFA_BAD_COMPLUS_SIG, pModule); DWORD varCount = (eType == ELEMENT_TYPE_VAR ? pTypeContext->m_classInst.GetNumArgs() : pTypeContext->m_methodInst.GetNumArgs()); THROW_BAD_FORMAT_MAYBE(varNum < varCount, BFA_BAD_COMPLUS_SIG, pModule); pLoc->InternalToken = (eType == ELEMENT_TYPE_VAR ? pTypeContext->m_classInst[varNum] : pTypeContext->m_methodInst[varNum]); pLoc->ElementType[cbDest] = ELEMENT_TYPE_INTERNAL; cbDest++; break; } case ELEMENT_TYPE_CLASS: case ELEMENT_TYPE_VALUETYPE: case ELEMENT_TYPE_INTERNAL: { pLoc->InternalToken = ptr.GetTypeHandleThrowing(pModule, pTypeContext); pLoc->ElementType[cbDest] = ELEMENT_TYPE_INTERNAL; cbDest++; break; } case ELEMENT_TYPE_GENERICINST: { pLoc->InternalToken = ptr.GetTypeHandleThrowing(pModule, pTypeContext); pLoc->ElementType[cbDest] = ELEMENT_TYPE_INTERNAL; cbDest++; break; } case ELEMENT_TYPE_FNPTR: // save off a pointer to the managed sig // we'll convert it in bulk when we store it // in the generated sig pLoc->pSigModule = pModule; pLoc->pSig = psigManagedArg+1; pLoc->ElementType[cbDest] = ELEMENT_TYPE_FNPTR; cbDest++; break; case ELEMENT_TYPE_ARRAY: cbDest = GetManagedTypeForMDArray(pLoc, pModule, psigManagedArg, pTypeContext); break; default: { size_t cbType; PCCOR_SIGNATURE psigNextManagedArg; IfFailThrow(ptr.SkipExactlyOne()); psigNextManagedArg = ptr.GetPtr(); cbType = psigNextManagedArg - psigManagedArg; size_t cbNewDest; if (!ClrSafeInt::addition(cbDest, cbType, cbNewDest) || cbNewDest > LocalDesc::MAX_LOCALDESC_ELEMENTS) { COMPlusThrow(kMarshalDirectiveException, IDS_EE_SIGTOOCOMPLEX); } memcpyNoGCRefs(&pLoc->ElementType[cbDest], psigManagedArg, cbType); cbDest = cbNewDest; break; } } if (cbDest > LocalDesc::MAX_LOCALDESC_ELEMENTS) { COMPlusThrow(kMarshalDirectiveException, IDS_EE_SIGTOOCOMPLEX); } pLoc->cbType = cbDest; } void ILStubLinker::GetStubTargetReturnType(LocalDesc* pLoc) { CONTRACTL { STANDARD_VM_CHECK; PRECONDITION(CheckPointer(pLoc)); } CONTRACTL_END; GetStubTargetReturnType(pLoc, m_pStubSigModule); } void ILStubLinker::GetStubTargetReturnType(LocalDesc* pLoc, Module* pModule) { CONTRACTL { STANDARD_VM_CHECK; PRECONDITION(CheckPointer(pLoc)); PRECONDITION(CheckPointer(pModule)); } CONTRACTL_END; GetManagedTypeHelper(pLoc, pModule, m_nativeFnSigBuilder.GetReturnSig(), m_pTypeContext, NULL); } void ILStubLinker::GetStubArgType(LocalDesc* pLoc) { CONTRACTL { STANDARD_VM_CHECK; PRECONDITION(CheckPointer(pLoc)); } CONTRACTL_END; GetStubArgType(pLoc, m_pStubSigModule); } void ILStubLinker::GetStubArgType(LocalDesc* pLoc, Module* pModule) { CONTRACTL { STANDARD_VM_CHECK; PRECONDITION(CheckPointer(pLoc)); PRECONDITION(CheckPointer(pModule)); } CONTRACTL_END; GetManagedTypeHelper(pLoc, pModule, m_managedSigPtr.GetPtr(), m_pTypeContext, m_pMD); } //--------------------------------------------------------------------------------------- // DWORD ILStubLinker::SetStubTargetArgType( LocalDesc * pLoc, // = NULL bool fConsumeStubArg) // = true { STANDARD_VM_CONTRACT; LocalDesc locDesc; if (fConsumeStubArg) { _ASSERTE(m_pStubSigModule); if (pLoc == NULL) { pLoc = &locDesc; GetStubArgType(pLoc, m_pStubSigModule); } IfFailThrow(m_managedSigPtr.SkipExactlyOne()); } TransformArgForJIT(pLoc); DWORD dwArgNum = m_nativeFnSigBuilder.NewArg(pLoc); m_iTargetStackDelta--; return dwArgNum; } // ILStubLinker::SetStubTargetArgType //--------------------------------------------------------------------------------------- // int ILStubLinker::GetToken(MethodDesc* pMD) { STANDARD_VM_CONTRACT; return m_tokenMap.GetToken(pMD); } int ILStubLinker::GetToken(MethodTable* pMT) { STANDARD_VM_CONTRACT; return m_tokenMap.GetToken(TypeHandle(pMT)); } int ILStubLinker::GetToken(TypeHandle th) { STANDARD_VM_CONTRACT; return m_tokenMap.GetToken(th); } int ILStubLinker::GetToken(FieldDesc* pFD) { STANDARD_VM_CONTRACT; return m_tokenMap.GetToken(pFD); } BOOL ILStubLinker::StubHasVoidReturnType() { LIMITED_METHOD_CONTRACT; return m_StubHasVoidReturnType; } void ILStubLinker::ClearCode() { CONTRACTL { NOTHROW; MODE_ANY; GC_TRIGGERS; } CONTRACTL_END; DeleteCodeLabels(); ClearCodeStreams(); } // static ILCodeStream* ILStubLinker::FindLastCodeStream(ILCodeStream* pList) { LIMITED_METHOD_CONTRACT; if (NULL == pList) { return NULL; } while (NULL != pList->m_pNextStream) { pList = pList->m_pNextStream; } return pList; } ILCodeStream* ILStubLinker::NewCodeStream(CodeStreamType codeStreamType) { STANDARD_VM_CONTRACT; NewHolder pNewCodeStream = new ILCodeStream(this, codeStreamType); if (NULL == m_pCodeStreamList) { m_pCodeStreamList = pNewCodeStream; } else { ILCodeStream* pTail = FindLastCodeStream(m_pCodeStreamList); CONSISTENCY_CHECK(NULL == pTail->m_pNextStream); pTail->m_pNextStream = pNewCodeStream; } pNewCodeStream.SuppressRelease(); return pNewCodeStream; } int ILCodeStream::GetToken(MethodDesc* pMD) { STANDARD_VM_CONTRACT; return m_pOwner->GetToken(pMD); } int ILCodeStream::GetToken(MethodTable* pMT) { STANDARD_VM_CONTRACT; return m_pOwner->GetToken(pMT); } int ILCodeStream::GetToken(TypeHandle th) { STANDARD_VM_CONTRACT; return m_pOwner->GetToken(th); } int ILCodeStream::GetToken(FieldDesc* pFD) { STANDARD_VM_CONTRACT; return m_pOwner->GetToken(pFD); } DWORD ILCodeStream::NewLocal(CorElementType typ) { STANDARD_VM_CONTRACT; return m_pOwner->NewLocal(typ); } DWORD ILCodeStream::NewLocal(LocalDesc loc) { WRAPPER_NO_CONTRACT; return m_pOwner->NewLocal(loc); } DWORD ILCodeStream::SetStubTargetArgType(CorElementType typ, bool fConsumeStubArg) { STANDARD_VM_CONTRACT; return m_pOwner->SetStubTargetArgType(typ, fConsumeStubArg); } DWORD ILCodeStream::SetStubTargetArgType(LocalDesc* pLoc, bool fConsumeStubArg) { STANDARD_VM_CONTRACT; return m_pOwner->SetStubTargetArgType(pLoc, fConsumeStubArg); } void ILCodeStream::SetStubTargetReturnType(CorElementType typ) { STANDARD_VM_CONTRACT; m_pOwner->SetStubTargetReturnType(typ); } void ILCodeStream::SetStubTargetReturnType(LocalDesc* pLoc) { STANDARD_VM_CONTRACT; m_pOwner->SetStubTargetReturnType(pLoc); } ILCodeLabel* ILCodeStream::NewCodeLabel() { STANDARD_VM_CONTRACT; return m_pOwner->NewCodeLabel(); } void ILCodeStream::ClearCode() { LIMITED_METHOD_CONTRACT; m_uCurInstrIdx = 0; }