// 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. //***************************************************************************** // ImportHelper.cpp // // // contains utility code to MD directory // //***************************************************************************** #include "stdafx.h" #include "importhelper.h" #include "mdutil.h" #include "rwutil.h" #include "mdlog.h" #include "strongname.h" #include "sstring.h" #define COM_RUNTIME_LIBRARY "ComRuntimeLibrary" //******************************************************************************* // Find the MethodSpec by Method and Instantiation //******************************************************************************* //@GENERICS: todo: look in hashtable (cf. MetaModelRW.cpp) if necessary HRESULT ImportHelper::FindMethodSpecByMethodAndInstantiation( CMiniMdRW *pMiniMd, // [IN] the minimd to lookup /*mdMethodDefOrRef*/ mdToken tkMethod, // [IN] MethodSpec method field PCCOR_SIGNATURE pInstantiation, // [IN] MethodSpec instantiation (a signature) ULONG cbInstantiation, // [IN] Size of instantiation. mdMethodSpec *pMethodSpec, // [OUT] Put the MethodSpec token here. RID rid /* = 0*/) // [IN] Optional rid to be ignored. { HRESULT hr; MethodSpecRec *pRecord; /*mdMethodDefOrRef*/ mdToken tkMethodTmp; PCCOR_SIGNATURE pInstantiationTmp; ULONG cbInstantiationTmp; ULONG cMethodSpecs; ULONG i; _ASSERTE(pMethodSpec); cMethodSpecs = pMiniMd->getCountMethodSpecs(); // linear scan through the MethodSpec table for (i=1; i <= cMethodSpecs; ++i) { // For the call from Validator ignore the rid passed in. if (i == rid) continue; IfFailRet(pMiniMd->GetMethodSpecRecord(i, &pRecord)); tkMethodTmp = pMiniMd->getMethodOfMethodSpec(pRecord); if ((tkMethodTmp != tkMethod)) continue; //@GENERICS: not sure what is meant by duplicate here: identical sig pointers or sig pointer contents? IfFailRet(pMiniMd->getInstantiationOfMethodSpec(pRecord, &pInstantiationTmp, &cbInstantiationTmp)); if (cbInstantiationTmp != cbInstantiation || memcmp(pInstantiation, pInstantiationTmp, cbInstantiation)) continue; // Matching record found. *pMethodSpec = TokenFromRid(i, mdtMethodSpec); return S_OK; } return CLDB_E_RECORD_NOTFOUND; } // HRESULT ImportHelper::FindMethodSpecByMethodAndInstantiation() //******************************************************************************* // Find the GenericParam by owner and constraint //******************************************************************************* //@GENERICS: todo: look in hashtable (cf. MetaModelRW.cpp) if necessary HRESULT ImportHelper::FindGenericParamConstraintByOwnerAndConstraint( CMiniMdRW *pMiniMd, // [IN] the minimd to lookup mdGenericParam tkOwner, // [IN] GenericParamConstraint Owner mdToken tkConstraint, // [IN] GenericParamConstraint Constraint mdGenericParamConstraint *pGenericParamConstraint,// [OUT] Put the GenericParam token here. RID rid /* = 0*/) // [IN] Optional rid to be ignored. { HRESULT hr; GenericParamConstraintRec *pRecord; mdGenericParam tkOwnerTmp; mdToken tkConstraintTmp; ULONG cGenericParamConstraints; ULONG i; _ASSERTE(pGenericParamConstraint); cGenericParamConstraints = pMiniMd->getCountGenericParamConstraints(); // linear scan through the GenericParam table for (i=1; i <= cGenericParamConstraints; ++i) { // For the call from Validator ignore the rid passed in. if (i == rid) continue; IfFailRet(pMiniMd->GetGenericParamConstraintRecord(i, &pRecord)); tkOwnerTmp = pMiniMd->getOwnerOfGenericParamConstraint(pRecord); tkConstraintTmp = pMiniMd->getConstraintOfGenericParamConstraint(pRecord); if ((tkOwnerTmp != tkOwner) || (tkConstraintTmp != tkConstraint)) continue; // Matching record found. *pGenericParamConstraint = TokenFromRid(i, mdtGenericParamConstraint); return S_OK; } return CLDB_E_RECORD_NOTFOUND; } // HRESULT ImportHelper::FindGenericParamConstraintByOwnerAndConstraint() //******************************************************************************* // Find the GenericParam by owner and name or number //******************************************************************************* // @GENERICS: todo: look in hashtable (cf. MetaModelRW.cpp) if necessary HRESULT ImportHelper::FindGenericParamByOwner( CMiniMdRW *pMiniMd, // [IN] the minimd to lookup mdToken tkOwner, // [IN] GenericParam Owner LPCUTF8 szUTF8Name, // [IN] GeneriParam Name, may be NULL if not used for search ULONG *pNumber, // [IN] GeneriParam Number, may be NULL if not used for search mdGenericParam *pGenericParam, // [OUT] Put the GenericParam token here. RID rid /* = 0*/) // [IN] Optional rid to be ignored. { HRESULT hr; GenericParamRec *pRecord; mdToken tkOwnerTmp; ULONG cGenericParams; LPCUTF8 szCurName; ULONG curNumber; ULONG i; _ASSERTE(pGenericParam); cGenericParams = pMiniMd->getCountGenericParams(); // linear scan through the GenericParam table for (i=1; i <= cGenericParams; ++i) { // For the call from Validator ignore the rid passed in. if (i == rid) continue; IfFailRet(pMiniMd->GetGenericParamRecord(i, &pRecord)); tkOwnerTmp = pMiniMd->getOwnerOfGenericParam(pRecord); if ( tkOwnerTmp != tkOwner) continue; // if the name is significant, try to match it if (szUTF8Name) { IfFailRet(pMiniMd->getNameOfGenericParam(pRecord, &szCurName)); if (strcmp(szCurName, szUTF8Name)) continue; } // if the number is significant, try to match it if (pNumber) { curNumber = pMiniMd->getNumberOfGenericParam(pRecord); if (*pNumber != curNumber) continue; } // Matching record found. *pGenericParam = TokenFromRid(i, mdtGenericParam); return S_OK; } return CLDB_E_RECORD_NOTFOUND; } // HRESULT ImportHelper::FindGenericParamByOwner() //******************************************************************************* // Find a Method given a parent, name and signature. //******************************************************************************* HRESULT ImportHelper::FindMethod( CMiniMdRW * pMiniMd, // [IN] the minimd to lookup mdTypeDef td, // [IN] parent. LPCUTF8 szName, // [IN] MethodDef name. PCCOR_SIGNATURE pSig, // [IN] Signature. ULONG cbSig, // [IN] Size of signature. mdMethodDef * pmb, // [OUT] Put the MethodDef token here. RID rid, // = 0 // [IN] Optional rid to be ignored. PSIGCOMPARE pSignatureCompare, // = NULL // [IN] Optional Routine to compare signatures void * pCompareContext) // = NULL // [IN] Optional context for the compare function { HRESULT hr = S_OK; ULONG ridStart; // Start of td's methods. ULONG ridEnd; // End of td's methods. ULONG index; // Loop control. TypeDefRec *pRec; // A TypeDef Record. MethodRec *pMethod; // A MethodDef Record. LPCUTF8 szNameUtf8Tmp; // A found MethodDef's name. PCCOR_SIGNATURE pSigTmp; // A found MethodDef's signature. ULONG cbSigTmp; // Size of a found MethodDef's signature. PCCOR_SIGNATURE pvSigTemp = pSig; // For use in parsing a signature. CQuickBytes qbSig; // Struct to build a non-varargs signature. CMiniMdRW::HashSearchResult rtn; if (cbSig) { // check to see if this is a vararg signature if (isCallConv(CorSigUncompressCallingConv(pvSigTemp), IMAGE_CEE_CS_CALLCONV_VARARG)) { // Get the fix part of VARARG signature IfFailGo(_GetFixedSigOfVarArg(pSig, cbSig, &qbSig, &cbSig)); pSig = (PCCOR_SIGNATURE) qbSig.Ptr(); } } *pmb = TokenFromRid(rid, mdtMethodDef); // to know what to ignore rtn = pMiniMd->FindMemberDefFromHash(td, szName, pSig, cbSig, pmb); if (rtn == CMiniMdRW::Found) { goto ErrExit; } else if (rtn == CMiniMdRW::NotFound) { IfFailGo(CLDB_E_RECORD_NOTFOUND); } _ASSERTE(rtn == CMiniMdRW::NoTable); *pmb = mdMethodDefNil; // get the range of method rids given a typedef IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(td), &pRec)); ridStart = pMiniMd->getMethodListOfTypeDef(pRec); IfFailGo(pMiniMd->getEndMethodListOfTypeDef(RidFromToken(td), &ridEnd)); // Iterate over the methods. for (index = ridStart; index < ridEnd; index ++ ) { RID methodRID; IfFailGo(pMiniMd->GetMethodRid(index, &methodRID)); // For the call from Validator ignore the rid passed in. if (methodRID != rid) { // Get the method and its name. IfFailGo(pMiniMd->GetMethodRecord(methodRID, &pMethod)); IfFailGo(pMiniMd->getNameOfMethod(pMethod, &szNameUtf8Tmp)); // If name matches what was requested... if ( strcmp(szNameUtf8Tmp, szName) == 0 ) { if (cbSig && pSig) { IfFailGo(pMiniMd->getSignatureOfMethod(pMethod, &pSigTmp, &cbSigTmp)); // If the caller did not provide a custom compare routine // then we use memcmp to match the signatures // if (pSignatureCompare == NULL) { if (cbSigTmp != cbSig || memcmp(pSig, pSigTmp, cbSig)) continue; } else { // Call the custom compare routine // if (!pSignatureCompare(pSigTmp, cbSigTmp, pSig, cbSig, pCompareContext)) continue; } } // Ignore PrivateScope methods. if (IsMdPrivateScope(pMiniMd->getFlagsOfMethod(pMethod))) continue; // Found method. *pmb = TokenFromRid(methodRID, mdtMethodDef); goto ErrExit; } } } // record not found *pmb = mdMethodDefNil; hr = CLDB_E_RECORD_NOTFOUND; ErrExit: return hr; } // ImportHelper::FindMethod //******************************************************************************* // Find a Field given a parent, name and signature. //******************************************************************************* HRESULT ImportHelper::FindField( CMiniMdRW * pMiniMd, // [IN] the minimd to lookup mdTypeDef td, // [IN] parent. LPCUTF8 szName, // [IN] FieldDef name. PCCOR_SIGNATURE pSig, // [IN] Signature. ULONG cbSig, // [IN] Size of signature. mdFieldDef * pfd, // [OUT] Put the FieldDef token here. RID rid) // = 0 // [IN] Optional rid to be ignored. { HRESULT hr = S_OK; // A result. ULONG ridStart; // Start of td's methods. ULONG ridEnd; // End of td's methods. ULONG index; // Loop control. TypeDefRec *pRec; // A TypeDef Record. FieldRec *pField; // A FieldDef Record. LPCUTF8 szNameUtf8Tmp; // A found FieldDef's name. PCCOR_SIGNATURE pSigTmp; // A found FieldDef's signature. ULONG cbSigTmp; // Size of a found FieldDef's signature. CMiniMdRW::HashSearchResult rtn; *pfd = TokenFromRid(rid,mdtFieldDef); // to know what to ignore rtn = pMiniMd->FindMemberDefFromHash(td, szName, pSig, cbSig, pfd); if (rtn == CMiniMdRW::Found) { goto ErrExit; } else if (rtn == CMiniMdRW::NotFound) { IfFailGo(CLDB_E_RECORD_NOTFOUND); } _ASSERTE(rtn == CMiniMdRW::NoTable); *pfd = mdFieldDefNil; // get the range of method rids given a typedef IfFailGo(pMiniMd->GetTypeDefRecord(RidFromToken(td), &pRec)); ridStart = pMiniMd->getFieldListOfTypeDef(pRec); IfFailGo(pMiniMd->getEndFieldListOfTypeDef(RidFromToken(td), &ridEnd)); // Iterate over the methods. for (index = ridStart; index < ridEnd; index ++ ) { RID fieldRID; IfFailGo(pMiniMd->GetFieldRid(index, &fieldRID)); // For the call from Validator ignore the rid passed in. if (fieldRID != rid) { // Get the field and its name. IfFailGo(pMiniMd->GetFieldRecord(fieldRID, &pField)); IfFailGo(pMiniMd->getNameOfField(pField, &szNameUtf8Tmp)); // If name matches what was requested... if ( strcmp(szNameUtf8Tmp, szName) == 0 ) { // Check signature if specified. if (cbSig && pSig) { IfFailGo(pMiniMd->getSignatureOfField(pField, &pSigTmp, &cbSigTmp)); if (cbSigTmp != cbSig || memcmp(pSig, pSigTmp, cbSig)) continue; } // Ignore PrivateScope fields. if (IsFdPrivateScope(pMiniMd->getFlagsOfField(pField))) continue; // Field found. *pfd = TokenFromRid(fieldRID, mdtFieldDef); goto ErrExit; } } } // record not found *pfd = mdFieldDefNil; hr = CLDB_E_RECORD_NOTFOUND; ErrExit: return hr; } // ImportHelper::FindField //******************************************************************************* // Find a Member given a parent, name and signature. //******************************************************************************* HRESULT ImportHelper::FindMember( CMiniMdRW * pMiniMd, // [IN] the minimd to lookup mdTypeDef td, // [IN] parent. LPCUTF8 szName, // [IN] Member name. PCCOR_SIGNATURE pSig, // [IN] Signature. ULONG cbSig, // [IN] Size of signature. mdToken * ptk) // [OUT] Put the token here. { HRESULT hr; if (cbSig == 0) { Debug_ReportError("Invalid signature size 0."); return CLDB_E_INDEX_NOTFOUND; } // determine if it is ref to MethodDef or FieldDef if ((pSig[0] & IMAGE_CEE_CS_CALLCONV_MASK) != IMAGE_CEE_CS_CALLCONV_FIELD) { hr = FindMethod(pMiniMd, td, szName, pSig, cbSig, ptk); } else { hr = FindField(pMiniMd, td, szName, pSig, cbSig, ptk); } if (hr == CLDB_E_RECORD_NOTFOUND) *ptk = mdTokenNil; return hr; } // ImportHelper::FindMember //******************************************************************************* // Find the memberref given name, sig, and parent //******************************************************************************* HRESULT ImportHelper::FindMemberRef( CMiniMdRW * pMiniMd, // [IN] the minimd to lookup mdToken tkParent, // [IN] the parent token LPCUTF8 szName, // [IN] memberref name const COR_SIGNATURE * pbSig, // [IN] Signature. ULONG cbSig, // [IN] Size of signature. mdMemberRef * pmr, // [OUT] Put the MemberRef token found RID rid, // = 0 // [IN] Optional rid to be ignored. HashSearchOption fCreateHash) // = DoNotCreateHash // [IN] Should we create hash first? (Optimize for multiple calls vs. single isolated call) { ULONG cMemberRefRecs; MemberRefRec * pMemberRefRec; LPCUTF8 szNameTmp = 0; const COR_SIGNATURE * pbSigTmp; // Signature. ULONG cbSigTmp; // Size of signature. mdToken tkParentTmp; // the parent token HRESULT hr = NOERROR; CMiniMdRW::HashSearchResult rtn; if ((szName == NULL) || (pmr == NULL)) { IfFailGo(CLDB_E_RECORD_NOTFOUND); } if (fCreateHash == CreateHash) { // Caller asked for creating hash to optimize for multiple calls IfFailGo(pMiniMd->CreateMemberRefHash()); } *pmr = TokenFromRid(rid, mdtMemberRef); // to know what to ignore rtn = pMiniMd->FindMemberRefFromHash(tkParent, szName, pbSig, cbSig, pmr); if (rtn == CMiniMdRW::Found) { goto ErrExit; } else if (rtn == CMiniMdRW::NotFound) { IfFailGo(CLDB_E_RECORD_NOTFOUND); } _ASSERTE(rtn == CMiniMdRW::NoTable); *pmr = mdMemberRefNil; cMemberRefRecs = pMiniMd->getCountMemberRefs(); // Search for the MemberRef for (ULONG i = 1; i <= cMemberRefRecs; i++) { // For the call from Validator ignore the rid passed in. if (i == rid) continue; IfFailGo(pMiniMd->GetMemberRefRecord(i, &pMemberRefRec)); if (!IsNilToken(tkParent)) { // given a valid parent tkParentTmp = pMiniMd->getClassOfMemberRef(pMemberRefRec); if (tkParentTmp != tkParent) { // if parent is specified and not equal to the current row, // try the next row. continue; } } if ((szName != NULL) && (*szName != 0)) { // name is specified IfFailGo(pMiniMd->getNameOfMemberRef(pMemberRefRec, &szNameTmp)); if (strcmp(szName, szNameTmp) != 0) { // Name is not equal. Try next row. continue; } } if ((cbSig != 0) && (pbSig != NULL)) { // signature is specifed IfFailGo(pMiniMd->getSignatureOfMemberRef(pMemberRefRec, &pbSigTmp, &cbSigTmp)); if (cbSigTmp != cbSig) continue; if (memcmp( pbSig, pbSigTmp, cbSig ) != 0) continue; } // we found a match *pmr = TokenFromRid(i, mdtMemberRef); return S_OK; } hr = CLDB_E_RECORD_NOTFOUND; ErrExit: return hr; } // ImportHelper::FindMemberRef //******************************************************************************* // Find duplicate StandAloneSig //******************************************************************************* HRESULT ImportHelper::FindStandAloneSig( CMiniMdRW *pMiniMd, // [IN] the minimd to lookup const COR_SIGNATURE *pbSig, // [IN] Signature. ULONG cbSig, // [IN] Size of signature. mdSignature *psa) // [OUT] Put the StandAloneSig token found { HRESULT hr; ULONG cRecs; StandAloneSigRec *pRec; const COR_SIGNATURE *pbSigTmp; // Signature. ULONG cbSigTmp; // Size of signature. _ASSERTE(cbSig && psa); *psa = mdSignatureNil; cRecs = pMiniMd->getCountStandAloneSigs(); // Search for the StandAloneSignature for (ULONG i = 1; i <= cRecs; i++) { IfFailRet(pMiniMd->GetStandAloneSigRecord(i, &pRec)); IfFailRet(pMiniMd->getSignatureOfStandAloneSig(pRec, &pbSigTmp, &cbSigTmp)); if (cbSigTmp != cbSig) continue; if (memcmp( pbSig, pbSigTmp, cbSig ) != 0) continue; // we found a match *psa = TokenFromRid(i, mdtSignature); return S_OK; } return CLDB_E_RECORD_NOTFOUND; } // HRESULT ImportHelper::FindStandAloneSig() //******************************************************************************* // Find duplicate TypeSpec //******************************************************************************* HRESULT ImportHelper::FindTypeSpec( CMiniMdRW * pMiniMd, // [IN] the minimd to lookup const COR_SIGNATURE * pbSig, // [IN] Signature. ULONG cbSig, // [IN] Size of signature. mdTypeSpec * pTypeSpec) // [OUT] Put the TypeSpec token found { HRESULT hr; ULONG cRecs; TypeSpecRec * pRec; const COR_SIGNATURE * pbSigTmp; // Signature. ULONG cbSigTmp; // Size of signature. // cbSig can be 0 _ASSERTE(pTypeSpec != NULL); *pTypeSpec = mdSignatureNil; cRecs = pMiniMd->getCountTypeSpecs(); // Search for the TypeSpec for (ULONG i = 1; i <= cRecs; i++) { IfFailRet(pMiniMd->GetTypeSpecRecord(i, &pRec)); IfFailRet(pMiniMd->getSignatureOfTypeSpec(pRec, &pbSigTmp, &cbSigTmp)); if (cbSigTmp != cbSig) continue; if (memcmp(pbSig, pbSigTmp, cbSig) != 0) continue; // we found a match *pTypeSpec = TokenFromRid(i, mdtTypeSpec); return S_OK; } return CLDB_E_RECORD_NOTFOUND; } // ImportHelper::FindTypeSpec //******************************************************************************* // Find the MethodImpl //******************************************************************************* HRESULT ImportHelper::FindMethodImpl( CMiniMdRW *pMiniMd, // [IN] The MiniMd to lookup. mdTypeDef tkClass, // [IN] The parent TypeDef token. mdMethodDef tkBody, // [IN] Method body token. mdMethodDef tkDecl, // [IN] Method declaration token. RID *pRid) // [OUT] Put the MethodImpl rid here { HRESULT hr; MethodImplRec *pMethodImplRec; // MethodImpl record. ULONG cMethodImplRecs; // Count of MethodImpl records. mdTypeDef tkClassTmp; // Parent TypeDef token. mdToken tkBodyTmp; // Method body token. mdToken tkDeclTmp; // Method declaration token. _ASSERTE(TypeFromToken(tkClass) == mdtTypeDef); _ASSERTE(TypeFromToken(tkBody) == mdtMemberRef || TypeFromToken(tkBody) == mdtMethodDef); _ASSERTE(TypeFromToken(tkDecl) == mdtMemberRef || TypeFromToken(tkDecl) == mdtMethodDef); _ASSERTE(!IsNilToken(tkClass) && !IsNilToken(tkBody) && !IsNilToken(tkDecl)); if (pRid) *pRid = 0; cMethodImplRecs = pMiniMd->getCountMethodImpls(); // Search for the MethodImpl. for (ULONG i = 1; i <= cMethodImplRecs; i++) { IfFailRet(pMiniMd->GetMethodImplRecord(i, &pMethodImplRec)); // match the parent column tkClassTmp = pMiniMd->getClassOfMethodImpl(pMethodImplRec); if (tkClassTmp != tkClass) continue; // match the method body column tkBodyTmp = pMiniMd->getMethodBodyOfMethodImpl(pMethodImplRec); if (tkBodyTmp != tkBody) continue; // match the method declaration column tkDeclTmp = pMiniMd->getMethodDeclarationOfMethodImpl(pMethodImplRec); if (tkDeclTmp != tkDecl) continue; // we found a match if (pRid) *pRid = i; return S_OK; } return CLDB_E_RECORD_NOTFOUND; } // HRESULT ImportHelper::FindMethodImpl() //******************************************************************************* // Find the TypeRef given the fully qualified name and the assembly name //******************************************************************************* HRESULT ImportHelper::FindCustomAttributeCtorByName( CMiniMdRW *pMiniMd, // [IN] the minimd to lookup LPCUTF8 szAssemblyName, // [IN] Assembly Name. LPCUTF8 szNamespace, // [IN] TypeRef Namespace. LPCUTF8 szName, // [IN] TypeRef Name. mdTypeDef *ptk, // [OUT] Put the TypeRef token here. RID rid /* = 0*/) // [IN] Optional rid to be ignored. { HRESULT hr; ULONG cRecs; // Count of records. AssemblyRefRec *pRec; // Current record being looked at. LPCUTF8 szTmp; // Temp string. mdTypeRef tkCAType; cRecs = pMiniMd->getCountAssemblyRefs(); // Search for the AssemblyRef record. for (ULONG i = 1; i <= cRecs; i++) { IfFailRet(pMiniMd->GetAssemblyRefRecord(i, &pRec)); IfFailRet(pMiniMd->getNameOfAssemblyRef(pRec, &szTmp)); if (!strcmp(szTmp, szAssemblyName) && (SUCCEEDED(FindTypeRefByName(pMiniMd, TokenFromRid(i, mdtAssemblyRef), szNamespace, szName, &tkCAType, rid))) && (SUCCEEDED(FindMemberRef(pMiniMd, tkCAType, COR_CTOR_METHOD_NAME, NULL, 0 ,ptk)))) { return S_OK; } } return CLDB_E_RECORD_NOTFOUND; } //******************************************************************************* // Find the TypeRef given the fully qualified name. //******************************************************************************* HRESULT ImportHelper::FindTypeRefByName( CMiniMdRW *pMiniMd, // [IN] the minimd to lookup mdToken tkResolutionScope, // [IN] Resolution scope for the TypeRef. LPCUTF8 szNamespace, // [IN] TypeRef Namespace. LPCUTF8 szName, // [IN] TypeRef Name. mdTypeRef *ptk, // [OUT] Put the TypeRef token here. RID rid /* = 0*/) // [IN] Optional rid to be ignored. { HRESULT hr=S_OK; // A result. ULONG cTypeRefRecs; // Count of TypeRefs to scan. TypeRefRec *pTypeRefRec; // A TypeRef record. LPCUTF8 szNameTmp; // A TypeRef's Name. LPCUTF8 szNamespaceTmp; // A TypeRef's Namespace. mdToken tkResTmp; // TypeRef's resolution scope. ULONG i; // Loop control. _ASSERTE(szName && ptk); *ptk = mdTypeRefNil; // Treat no namespace as empty string. if (!szNamespace) szNamespace = ""; if (pMiniMd->m_pNamedItemHash) { // If hash is build, go through the hash table TOKENHASHENTRY *p; // Hash entry from chain. ULONG iHash; // Item's hash value. int pos; // Position in hash chain. // Hash the data. iHash = pMiniMd->HashNamedItem(0, szName); // Go through every entry in the hash chain looking for ours. for (p = pMiniMd->m_pNamedItemHash->FindFirst(iHash, pos); p; p = pMiniMd->m_pNamedItemHash->FindNext(pos)) { // name hash can hold more than one kind of token if (TypeFromToken(p->tok) != (ULONG)mdtTypeRef) { continue; } // skip this one if asked if (RidFromToken(p->tok) == rid) continue; IfFailGo(pMiniMd->GetTypeRefRecord(RidFromToken(p->tok), &pTypeRefRec)); IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szNamespaceTmp)); IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szNameTmp)); if (strcmp(szName, szNameTmp) || strcmp(szNamespace, szNamespaceTmp)) { // if the name space is not equal, then check the next one. continue; } tkResTmp = pMiniMd->getResolutionScopeOfTypeRef(pTypeRefRec); if (tkResTmp == tkResolutionScope || (IsNilToken(tkResTmp) && IsNilToken(tkResolutionScope))) { // we found a match *ptk = p->tok; return S_OK; } } hr = CLDB_E_RECORD_NOTFOUND; } else { cTypeRefRecs = pMiniMd->getCountTypeRefs(); // Search for the TypeRef. for (i = 1; i <= cTypeRefRecs; i++) { // For the call from Validator ignore the rid passed in. if (i == rid) continue; IfFailGo(pMiniMd->GetTypeRefRecord(i, &pTypeRefRec)); // See if the Resolution scopes match. tkResTmp = pMiniMd->getResolutionScopeOfTypeRef(pTypeRefRec); if (IsNilToken(tkResTmp)) { if (!IsNilToken(tkResolutionScope)) continue; } else if (tkResTmp != tkResolutionScope) continue; IfFailGo(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szNamespaceTmp)); if (strcmp(szNamespace, szNamespaceTmp)) continue; IfFailGo(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szNameTmp)); if (! strcmp(szName, szNameTmp)) { *ptk = TokenFromRid(i, mdtTypeRef); return S_OK; } } hr = CLDB_E_RECORD_NOTFOUND; } ErrExit: return hr; } // HRESULT ImportHelper::FindTypeRefByName() //******************************************************************************* // Find the ModuleRef given the name, guid and mvid. //******************************************************************************* HRESULT ImportHelper::FindModuleRef( CMiniMdRW *pMiniMd, // [IN] the minimd to lookup LPCUTF8 szUTF8Name, // [IN] ModuleRef name. mdModuleRef *pmur, // [OUT] Put the ModuleRef token here. RID rid /* = 0*/) // [IN] Optional rid to be ignored. { HRESULT hr; ModuleRefRec *pModuleRef; ULONG cModuleRefs; LPCUTF8 szCurName; ULONG i; _ASSERTE(pmur); _ASSERTE(szUTF8Name); cModuleRefs = pMiniMd->getCountModuleRefs(); // linear scan through the ModuleRef table for (i=1; i <= cModuleRefs; ++i) { // For the call from Validator ignore the rid passed in. if (i == rid) continue; IfFailRet(pMiniMd->GetModuleRefRecord(i, &pModuleRef)); if (szUTF8Name != NULL) { IfFailRet(pMiniMd->getNameOfModuleRef(pModuleRef, &szCurName)); if (strcmp(szCurName, szUTF8Name)) continue; } // Matching record found. *pmur = TokenFromRid(i, mdtModuleRef); return S_OK; } return CLDB_E_RECORD_NOTFOUND; } // HRESULT ImportHelper::FindModuleRef() //******************************************************************************* // Find the TypeDef given the type and namespace name //******************************************************************************* HRESULT ImportHelper::FindTypeDefByName( CMiniMdRW * pMiniMd, // [IN] the minimd to lookup LPCUTF8 szTypeDefNamespace, // [IN] Full qualified TypeRef name. LPCUTF8 szTypeDefName, // [IN] Full qualified TypeRef name. mdToken tkEnclosingClass, // [IN] TypeDef/TypeRef/Module for Enclosing class. mdTypeDef * ptkTypeDef, // [OUT] Put the TypeRef token here. RID ridIgnore) // =0 // [IN] Optional rid to be ignored. { ULONG cTypeDefRecs; TypeDefRec * pTypeDefRec; LPCUTF8 szName; LPCUTF8 szNamespace; DWORD dwFlags; HRESULT hr = S_OK; _ASSERTE((szTypeDefName != NULL) && (ptkTypeDef != NULL)); _ASSERTE((TypeFromToken(tkEnclosingClass) == mdtTypeDef) || (TypeFromToken(tkEnclosingClass) == mdtTypeRef) || (tkEnclosingClass == TokenFromRid(1, mdtModule)) || IsNilToken(tkEnclosingClass)); *ptkTypeDef = mdTypeDefNil; cTypeDefRecs = pMiniMd->getCountTypeDefs(); // Treat no namespace as empty string. if (szTypeDefNamespace == NULL) szTypeDefNamespace = ""; if (tkEnclosingClass == TokenFromRid(1, mdtModule)) { // Module scope is the same as no scope (used in .winmd files as TypeRef scope for self-references) tkEnclosingClass = mdTokenNil; } // Get TypeDef of the tkEnclosingClass passed in if (TypeFromToken(tkEnclosingClass) == mdtTypeRef) { // Resolve the TypeRef to a TypeDef TypeRefRec * pTypeRefRec; mdToken tkResolutionScope; LPCUTF8 szTypeRefName; LPCUTF8 szTypeRefNamespace; IfFailRet(pMiniMd->GetTypeRefRecord(RidFromToken(tkEnclosingClass), &pTypeRefRec)); tkResolutionScope = pMiniMd->getResolutionScopeOfTypeRef(pTypeRefRec); IfFailRet(pMiniMd->getNameOfTypeRef(pTypeRefRec, &szTypeRefName)); IfFailRet(pMiniMd->getNamespaceOfTypeRef(pTypeRefRec, &szTypeRefNamespace)); if (tkEnclosingClass == tkResolutionScope && !strcmp(szTypeDefName, szTypeRefName) && ((szTypeDefNamespace == nullptr && szTypeRefNamespace == nullptr) || (szTypeDefNamespace != nullptr && szTypeRefNamespace != nullptr && !strcmp(szTypeDefNamespace, szTypeRefNamespace)))) { // // This defensive workaround works around a feature of DotFuscator that adds a bad type-ref // which causes tools like ILDASM to crash. The type-ref's parent is set to itself // which causes this function to recurse infinitely. A side-effect is that during Ngen we // parse all the type-refs in an assembly and Ngen also hangs infinitely. // This workaround is necessary because several popular gaming libraries experience hangs // and we need binary compatibility in Apollo. // return CLDB_E_FILE_CORRUPT; } // Update tkEnclosingClass to TypeDef IfFailRet(FindTypeDefByName( pMiniMd, szTypeRefNamespace, szTypeRefName, (TypeFromToken(tkResolutionScope) == mdtTypeRef) ? tkResolutionScope : mdTokenNil, &tkEnclosingClass)); _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef); } // Search for the TypeDef for (ULONG i = 1; i <= cTypeDefRecs; i++) { // For the call from Validator ignore the rid passed in. if (i == ridIgnore) continue; IfFailRet(pMiniMd->GetTypeDefRecord(i, &pTypeDefRec)); dwFlags = pMiniMd->getFlagsOfTypeDef(pTypeDefRec); if (!IsTdNested(dwFlags) && !IsNilToken(tkEnclosingClass)) { // If the class is not Nested and EnclosingClass passed in is not nil continue; } else if (IsTdNested(dwFlags) && IsNilToken(tkEnclosingClass)) { // If the class is nested and EnclosingClass passed is nil continue; } else if (!IsNilToken(tkEnclosingClass)) { _ASSERTE(TypeFromToken(tkEnclosingClass) == mdtTypeDef); RID iNestedClassRec; NestedClassRec * pNestedClassRec; mdTypeDef tkEnclosingClassTmp; IfFailRet(pMiniMd->FindNestedClassHelper(TokenFromRid(i, mdtTypeDef), &iNestedClassRec)); if (InvalidRid(iNestedClassRec)) continue; IfFailRet(pMiniMd->GetNestedClassRecord(iNestedClassRec, &pNestedClassRec)); tkEnclosingClassTmp = pMiniMd->getEnclosingClassOfNestedClass(pNestedClassRec); if (tkEnclosingClass != tkEnclosingClassTmp) continue; } IfFailRet(pMiniMd->getNameOfTypeDef(pTypeDefRec, &szName)); if (strcmp(szTypeDefName, szName) == 0) { IfFailRet(pMiniMd->getNamespaceOfTypeDef(pTypeDefRec, &szNamespace)); if (strcmp(szTypeDefNamespace, szNamespace) == 0) { *ptkTypeDef = TokenFromRid(i, mdtTypeDef); return S_OK; } } } return CLDB_E_RECORD_NOTFOUND; } // ImportHelper::FindTypeDefByName //******************************************************************************* // Find the InterfaceImpl given the typedef and implemented interface //******************************************************************************* HRESULT ImportHelper::FindInterfaceImpl( CMiniMdRW *pMiniMd, // [IN] the minimd to lookup mdToken tkClass, // [IN] TypeDef of the type mdToken tkInterface, // [IN] could be typedef/typeref mdInterfaceImpl *ptk, // [OUT] Put the interface token here. RID rid /* = 0*/) // [IN] Optional rid to be ignored. { HRESULT hr; ULONG ridStart, ridEnd; ULONG i; InterfaceImplRec *pInterfaceImplRec; _ASSERTE(ptk); *ptk = mdInterfaceImplNil; if ( pMiniMd->IsSorted(TBL_InterfaceImpl) ) { IfFailRet(pMiniMd->getInterfaceImplsForTypeDef(RidFromToken(tkClass), &ridEnd, &ridStart)); } else { ridStart = 1; ridEnd = pMiniMd->getCountInterfaceImpls() + 1; } // Search for the interfaceimpl for (i = ridStart; i < ridEnd; i++) { // For the call from Validator ignore the rid passed in. if (i == rid) continue; IfFailRet(pMiniMd->GetInterfaceImplRecord(i, &pInterfaceImplRec)); if ( tkClass != pMiniMd->getClassOfInterfaceImpl(pInterfaceImplRec) ) continue; if ( tkInterface == pMiniMd->getInterfaceOfInterfaceImpl(pInterfaceImplRec) ) { *ptk = TokenFromRid(i, mdtInterfaceImpl); return S_OK; } } return CLDB_E_RECORD_NOTFOUND; } // HRESULT ImportHelper::FindInterfaceImpl() //******************************************************************************* // Find the Permission by parent and action //******************************************************************************* HRESULT ImportHelper::FindPermission( CMiniMdRW *pMiniMd, // [IN] the minimd to lookup mdToken tkParent, // [IN] Token with the Permission USHORT usAction, // [IN] The action of the permission mdPermission *ppm) // [OUT] Put permission token here { HRESULT hr; DeclSecurityRec *pRec; ULONG ridStart, ridEnd; ULONG i; mdToken tkParentTmp; _ASSERTE(ppm); if ( pMiniMd->IsSorted(TBL_DeclSecurity) ) { IfFailRet(pMiniMd->getDeclSecurityForToken(tkParent, &ridEnd, &ridStart)); } else { ridStart = 1; ridEnd = pMiniMd->getCountDeclSecuritys() + 1; } // loop through all permission for (i = ridStart; i < ridEnd; i++) { IfFailRet(pMiniMd->GetDeclSecurityRecord(i, &pRec)); tkParentTmp = pMiniMd->getParentOfDeclSecurity(pRec); if ( tkParentTmp != tkParent ) continue; if (pRec->GetAction() == usAction) { *ppm = TokenFromRid(i, mdtPermission); return S_OK; } } return CLDB_E_RECORD_NOTFOUND; } // HRESULT ImportHelper::FindPermission() //***************************************************************************** // find a property record //***************************************************************************** HRESULT ImportHelper::FindProperty( CMiniMdRW *pMiniMd, // [IN] the minimd to lookup mdToken tkTypeDef, // [IN] typedef token LPCUTF8 szName, // [IN] name of the property const COR_SIGNATURE *pbSig, // [IN] Signature. ULONG cbSig, // [IN] Size of signature. mdProperty *ppr) // [OUT] Property token { HRESULT hr; RID ridPropertyMap; PropertyMapRec *pPropertyMapRec; PropertyRec *pRec; ULONG ridStart; ULONG ridEnd; ULONG i; LPCUTF8 szNameTmp; PCCOR_SIGNATURE pbSigTmp; ULONG cbSigTmp; ULONG pr; IfFailRet(pMiniMd->FindPropertyMapFor(RidFromToken(tkTypeDef), &ridPropertyMap)); if ( !InvalidRid(ridPropertyMap) ) { IfFailRet(pMiniMd->GetPropertyMapRecord(ridPropertyMap, &pPropertyMapRec)); ridStart = pMiniMd->getPropertyListOfPropertyMap(pPropertyMapRec); IfFailRet(pMiniMd->getEndPropertyListOfPropertyMap(ridPropertyMap, &ridEnd)); for (i = ridStart; i < ridEnd; i++) { // get the property rid IfFailRet(pMiniMd->GetPropertyRid(i, &pr)); IfFailRet(pMiniMd->GetPropertyRecord(pr, &pRec)); IfFailRet(pMiniMd->getNameOfProperty(pRec, &szNameTmp)); IfFailRet(pMiniMd->getTypeOfProperty(pRec, &pbSigTmp, &cbSigTmp)); if ( strcmp (szName, szNameTmp) != 0 ) continue; if ( cbSig != 0 && (cbSigTmp != cbSig || memcmp(pbSig, pbSigTmp, cbSig) != 0 ) ) continue; *ppr = TokenFromRid( i, mdtProperty ); return S_OK; } return CLDB_E_RECORD_NOTFOUND; } else { return CLDB_E_RECORD_NOTFOUND; } } // HRESULT ImportHelper::FindProperty() //***************************************************************************** // find an Event record //***************************************************************************** HRESULT ImportHelper::FindEvent( CMiniMdRW *pMiniMd, // [IN] the minimd to lookup mdToken tkTypeDef, // [IN] typedef token LPCUTF8 szName, // [IN] name of the event mdProperty *pev) // [OUT] Event token { HRESULT hr; RID ridEventMap; EventMapRec *pEventMapRec; EventRec *pRec; ULONG ridStart; ULONG ridEnd; ULONG i; LPCUTF8 szNameTmp; ULONG ev; IfFailRet(pMiniMd->FindEventMapFor(RidFromToken(tkTypeDef), &ridEventMap)); if ( !InvalidRid(ridEventMap) ) { IfFailRet(pMiniMd->GetEventMapRecord(ridEventMap, &pEventMapRec)); ridStart = pMiniMd->getEventListOfEventMap(pEventMapRec); IfFailRet(pMiniMd->getEndEventListOfEventMap(ridEventMap, &ridEnd)); for (i = ridStart; i < ridEnd; i++) { // get the Event rid IfFailRet(pMiniMd->GetEventRid(i, &ev)); // get the event row IfFailRet(pMiniMd->GetEventRecord(ev, &pRec)); IfFailRet(pMiniMd->getNameOfEvent(pRec, &szNameTmp)); if ( strcmp (szName, szNameTmp) == 0) { *pev = TokenFromRid( ev, mdtEvent ); return S_OK; } } return CLDB_E_RECORD_NOTFOUND; } else { return CLDB_E_RECORD_NOTFOUND; } } // HRESULT ImportHelper::FindEvent() //***************************************************************************** // find an custom value record given by parent and type token. This will always return // the first one that is found regardless duplicated. //***************************************************************************** HRESULT ImportHelper::FindCustomAttributeByToken( CMiniMdRW *pMiniMd, // [IN] the minimd to lookup mdToken tkParent, // [IN] the parent that custom value is associated with mdToken tkType, // [IN] type of the CustomAttribute const void *pCustBlob, // [IN] custom attribute blob ULONG cbCustBlob, // [IN] size of the blob. mdCustomAttribute *pcv) // [OUT] CustomAttribute token { HRESULT hr; CustomAttributeRec *pRec; ULONG ridStart, ridEnd; ULONG i; mdToken tkParentTmp; mdToken tkTypeTmp; const void *pCustBlobTmp; ULONG cbCustBlobTmp; _ASSERTE(pcv); *pcv = mdCustomAttributeNil; if ( pMiniMd->IsSorted(TBL_CustomAttribute) ) { IfFailRet(pMiniMd->FindCustomAttributeFor( RidFromToken(tkParent), TypeFromToken(tkParent), tkType, (RID *)pcv)); if (InvalidRid(*pcv)) { return S_FALSE; } else if (pCustBlob) { IfFailRet(pMiniMd->GetCustomAttributeRecord(RidFromToken(*pcv), &pRec)); IfFailRet(pMiniMd->getValueOfCustomAttribute(pRec, (const BYTE **)&pCustBlobTmp, &cbCustBlobTmp)); if (cbCustBlob == cbCustBlobTmp && !memcmp(pCustBlob, pCustBlobTmp, cbCustBlob)) { return S_OK; } } else { return S_OK; } } else { CLookUpHash *pHashTable = pMiniMd->m_pLookUpHashs[TBL_CustomAttribute]; if (pHashTable) { // table is not sorted but hash is built // We want to create dynmaic array to hold the dynamic enumerator. TOKENHASHENTRY *p; ULONG iHash; int pos; // Hash the data. iHash = pMiniMd->HashCustomAttribute(tkParent); // Go through every entry in the hash chain looking for ours. for (p = pHashTable->FindFirst(iHash, pos); p; p = pHashTable->FindNext(pos)) { IfFailRet(pMiniMd->GetCustomAttributeRecord(RidFromToken(p->tok), &pRec)); tkParentTmp = pMiniMd->getParentOfCustomAttribute(pRec); if (tkParentTmp != tkParent) continue; tkTypeTmp = pMiniMd->getTypeOfCustomAttribute(pRec); if (tkType != tkTypeTmp) continue; if (pCustBlob != NULL) { IfFailRet(pMiniMd->getValueOfCustomAttribute(pRec, (const BYTE **)&pCustBlobTmp, &cbCustBlobTmp)); if (cbCustBlob == cbCustBlobTmp && !memcmp(pCustBlob, pCustBlobTmp, cbCustBlob)) { *pcv = TokenFromRid(p->tok, mdtCustomAttribute); return S_OK; } } else return S_OK; } } else { // linear scan ridStart = 1; ridEnd = pMiniMd->getCountCustomAttributes() + 1; // loop through all custom values for (i = ridStart; i < ridEnd; i++) { IfFailRet(pMiniMd->GetCustomAttributeRecord(i, &pRec)); tkParentTmp = pMiniMd->getParentOfCustomAttribute(pRec); if ( tkParentTmp != tkParent ) continue; tkTypeTmp = pMiniMd->getTypeOfCustomAttribute(pRec); if (tkType != tkTypeTmp) continue; if (pCustBlob != NULL) { IfFailRet(pMiniMd->getValueOfCustomAttribute(pRec, (const BYTE **)&pCustBlobTmp, &cbCustBlobTmp)); if (cbCustBlob == cbCustBlobTmp && !memcmp(pCustBlob, pCustBlobTmp, cbCustBlob)) { *pcv = TokenFromRid(i, mdtCustomAttribute); return S_OK; } } else return S_OK; } } // fall through } return S_FALSE; } // ImportHelper::FindCustomAttributeByToken //***************************************************************************** // Helper function to lookup and retrieve a CustomAttribute. //***************************************************************************** HRESULT ImportHelper::GetCustomAttributeByName( // S_OK or error. CMiniMdRW *pMiniMd, // [IN] the minimd to lookup mdToken tkObj, // [IN] Object with Custom Attribute. LPCUTF8 szName, // [IN] Name of desired Custom Attribute. const void **ppData, // [OUT] Put pointer to data here. ULONG *pcbData) // [OUT] Put size of data here. { return pMiniMd->CommonGetCustomAttributeByName(tkObj, szName, ppData, pcbData); } // ImportHelper::GetCustomAttributeByName #ifdef FEATURE_METADATA_EMIT //******************************************************************************* // Find an AssemblyRef record given the name. //******************************************************************************* HRESULT ImportHelper::FindAssemblyRef( CMiniMdRW *pMiniMd, // [IN] the minimd to lookup. LPCUTF8 szName, // [IN] Name. LPCUTF8 szLocale, // [IN] Locale. const void *pbPublicKeyOrToken, // [IN] Public key or token (based on flags). ULONG cbPublicKeyOrToken, // [IN] Byte count of public key or token. USHORT usMajorVersion, // [IN] Major version. USHORT usMinorVersion, // [IN] Minor version. USHORT usBuildNumber, // [IN] Build number. USHORT usRevisionNumber, // [IN] Revision number. DWORD dwFlags, // [IN] Flags. mdAssemblyRef *pmar) // [OUT] returned AssemblyRef token. { HRESULT hr; ULONG cRecs; // Count of records. AssemblyRefRec *pRec; // Current record being looked at. LPCUTF8 szTmp; // Temp string. const void *pbTmp; // Temp blob. ULONG cbTmp; // Temp byte count. DWORD dwTmp; // Temp flags. const void *pbToken = NULL; // Token version of public key. ULONG cbToken = 0; // Count of bytes in token. #if !defined(FEATURE_METADATA_EMIT_IN_DEBUGGER) || defined(DACCESS_COMPILE) const void *pbTmpToken; // Token version of public key. ULONG cbTmpToken; // Count of bytes in token. bool fMatch; // Did public key or tokens match? #endif // !FEATURE_METADATA_EMIT_IN_DEBUGGER || DACCESS_COMPILE // Handle special cases upfront. if (!szLocale) szLocale = ""; if (!pbPublicKeyOrToken) cbPublicKeyOrToken = 0; if (!IsAfPublicKey(dwFlags)) { pbToken = pbPublicKeyOrToken; cbToken = cbPublicKeyOrToken; } _ASSERTE(pMiniMd && szName && pmar); *pmar = 0; cRecs = pMiniMd->getCountAssemblyRefs(); // Search for the AssemblyRef record. for (ULONG i = 1; i <= cRecs; i++) { IfFailRet(pMiniMd->GetAssemblyRefRecord(i, &pRec)); IfFailRet(pMiniMd->getNameOfAssemblyRef(pRec, &szTmp)); if (strcmp(szTmp, szName)) continue; IfFailRet(pMiniMd->getLocaleOfAssemblyRef(pRec, &szTmp)); if (strcmp(szTmp, szLocale)) continue; if (pRec->GetMajorVersion() != usMajorVersion) continue; if (pRec->GetMinorVersion() != usMinorVersion) continue; // We'll "unify" all versions of mscorlib and Microsoft.VisualC... so if this // is one of those, we won't do the version check beyond the major/minor LPCUTF8 szAssemblyRefName; IfFailRet(pMiniMd->getNameOfAssemblyRef(pRec, &szAssemblyRefName)); if (SString::_stricmp(szAssemblyRefName, "mscorlib") && SString::_stricmp(szAssemblyRefName, "microsoft.visualc")) { if (pRec->GetBuildNumber() != usBuildNumber) continue; if (pRec->GetRevisionNumber() != usRevisionNumber) continue; } IfFailRet(pMiniMd->getPublicKeyOrTokenOfAssemblyRef(pRec, (const BYTE **)&pbTmp, &cbTmp)); if ((cbPublicKeyOrToken && !cbTmp) || (!cbPublicKeyOrToken && cbTmp)) continue; if (cbTmp) { // Either ref may be using either a full public key or a token // (determined by the ref flags). Must cope with all variations. dwTmp = pMiniMd->getFlagsOfAssemblyRef(pRec); if (IsAfPublicKey(dwTmp) == IsAfPublicKey(dwFlags)) { // Easy case, they're both in the same form. if (cbTmp != cbPublicKeyOrToken || memcmp(pbTmp, pbPublicKeyOrToken, cbTmp)) continue; } else if (IsAfPublicKey(dwTmp)) { #if defined(FEATURE_METADATA_EMIT_IN_DEBUGGER) && !defined(DACCESS_COMPILE) return E_FAIL; #else //!FEATURE_METADATA_EMIT_IN_DEBUGGER || DACCESS_COMPILE // Need to compress target public key to see if it matches. if (!StrongNameTokenFromPublicKey((BYTE*)pbTmp, cbTmp, (BYTE**)&pbTmpToken, &cbTmpToken)) { return StrongNameErrorInfo(); } fMatch = cbTmpToken == cbPublicKeyOrToken && !memcmp(pbTmpToken, pbPublicKeyOrToken, cbTmpToken); StrongNameFreeBuffer((BYTE*)pbTmpToken); if (!fMatch) continue; #endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER || DACCESS_COMPILE } else { // Need to compress out public key to see if it matches. We // cache the result of this for further iterations. if (!pbToken) { #if defined(FEATURE_METADATA_EMIT_IN_DEBUGGER) && !defined(DACCESS_COMPILE) return E_FAIL; #else //!FEATURE_METADATA_EMIT_IN_DEBUGGER || DACCESS_COMPILE if (!StrongNameTokenFromPublicKey((BYTE*)pbPublicKeyOrToken, cbPublicKeyOrToken, (BYTE**)&pbToken, &cbToken)) { return StrongNameErrorInfo(); } #endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER || DACCESS_COMPILE } if (cbTmp != cbToken || memcmp(pbTmp, pbToken, cbToken)) continue; } } if (pbToken && IsAfPublicKey(dwFlags)) { #if !defined(FEATURE_METADATA_EMIT_IN_DEBUGGER) || defined(DACCESS_COMPILE) StrongNameFreeBuffer((BYTE*)pbToken); #endif } *pmar = TokenFromRid(i, mdtAssemblyRef); return S_OK; } if (pbToken && IsAfPublicKey(dwFlags)) { #if !defined(FEATURE_METADATA_EMIT_IN_DEBUGGER) || defined(DACCESS_COMPILE) StrongNameFreeBuffer((BYTE*)pbToken); #endif } return CLDB_E_RECORD_NOTFOUND; } // ImportHelper::FindAssemblyRef //******************************************************************************* // Find a File record given the name. //******************************************************************************* HRESULT ImportHelper::FindFile( CMiniMdRW *pMiniMd, // [IN] the minimd to lookup. LPCUTF8 szName, // [IN] name for the File. mdFile *pmf, // [OUT] returned File token. RID rid /* = 0 */) // [IN] Optional rid to be ignored. { HRESULT hr; ULONG cRecs; // Count of records. FileRec *pRec; // Current record being looked at. LPCUTF8 szNameTmp; _ASSERTE(pMiniMd && szName && pmf); *pmf = 0; cRecs = pMiniMd->getCountFiles(); // Search for the File record. for (ULONG i = 1; i <= cRecs; i++) { // For the call from Validator ignore the rid passed in. if (i == rid) continue; IfFailRet(pMiniMd->GetFileRecord(i, &pRec)); IfFailRet(pMiniMd->getNameOfFile(pRec, &szNameTmp)); if (!strcmp(szNameTmp, szName)) { *pmf = TokenFromRid(i, mdtFile); return S_OK; } } return CLDB_E_RECORD_NOTFOUND; } // ImportHelper::FindFile #endif //FEATURE_METADATA_EMIT //******************************************************************************* // Find a ExportedType record given the name. //******************************************************************************* HRESULT ImportHelper::FindExportedType( CMiniMdRW *pMiniMd, // [IN] the minimd to lookup. LPCUTF8 szNamespace, // [IN] namespace for the ExportedType. LPCUTF8 szName, // [IN] name for the ExportedType. mdExportedType tkEnclosingType, // [IN] token for the enclosing type. mdExportedType *pmct, // [OUT] returned ExportedType token. RID rid /* = 0 */) // [IN] Optional rid to be ignored. { HRESULT hr; ULONG cRecs; // Count of records. ExportedTypeRec *pRec; // Current record being looked at. mdToken tkImpl; LPCUTF8 szNamespaceTmp; LPCUTF8 szNameTmp; _ASSERTE(pMiniMd && szName && pmct); *pmct = 0; // Treat no namespace as empty string. if (!szNamespace) szNamespace = ""; cRecs = pMiniMd->getCountExportedTypes(); // Search for the ExportedType record. for (ULONG i = 1; i <= cRecs; i++) { // For the call from Validator ignore the rid passed in. if (i == rid) continue; IfFailRet(pMiniMd->GetExportedTypeRecord(i, &pRec)); // Handle the case of nested vs. non-nested classes. tkImpl = pMiniMd->getImplementationOfExportedType(pRec); if (TypeFromToken(tkImpl) == mdtExportedType && !IsNilToken(tkImpl)) { // Current ExportedType being looked at is a nested type, so // comparing the implementation token. if (tkImpl != tkEnclosingType) continue; } else if (TypeFromToken(tkEnclosingType) == mdtExportedType && !IsNilToken(tkEnclosingType)) { // ExportedType passed in is nested but the current ExportedType is not. continue; } IfFailRet(pMiniMd->getTypeNamespaceOfExportedType(pRec, &szNamespaceTmp)); if (strcmp(szNamespaceTmp, szNamespace)) continue; IfFailRet(pMiniMd->getTypeNameOfExportedType(pRec, &szNameTmp)); if (!strcmp(szNameTmp, szName)) { *pmct = TokenFromRid(i, mdtExportedType); return S_OK; } } return CLDB_E_RECORD_NOTFOUND; } // HRESULT ImportHelper::FindExportedType() //******************************************************************************* // Find a ManifestResource record given the name. //******************************************************************************* HRESULT ImportHelper::FindManifestResource( CMiniMdRW *pMiniMd, // [IN] the minimd to lookup. LPCUTF8 szName, // [IN] name for the ManifestResource. mdManifestResource *pmmr, // [OUT] returned ManifestResource token. RID rid /* = 0 */) // [IN] Optional rid to be ignored. { HRESULT hr; ULONG cRecs; // Count of records. ManifestResourceRec *pRec; // Current record being looked at. LPCUTF8 szNameTmp; _ASSERTE(pMiniMd && szName && pmmr); *pmmr = 0; cRecs = pMiniMd->getCountManifestResources(); // Search for the ManifestResource record. for (ULONG i = 1; i <= cRecs; i++) { // For the call from Validator ignore the rid passed in. if (i == rid) continue; IfFailRet(pMiniMd->GetManifestResourceRecord(i, &pRec)); IfFailRet(pMiniMd->getNameOfManifestResource(pRec, &szNameTmp)); if (!strcmp(szNameTmp, szName)) { *pmmr = TokenFromRid(i, mdtManifestResource); return S_OK; } } return CLDB_E_RECORD_NOTFOUND; } // HRESULT ImportHelper::FindManifestResource() #ifdef FEATURE_METADATA_EMIT //**************************************************************************** // Convert tokens contained in an element type //**************************************************************************** HRESULT ImportHelper::MergeUpdateTokenInFieldSig( CMiniMdRW *pMiniMdAssemEmit, // [IN] The assembly emit scope. CMiniMdRW *pMiniMdEmit, // [IN] The emit scope. IMetaModelCommon *pCommonAssemImport,// [IN] Assembly scope where the signature is from. const void *pbHashValue, // [IN] Hash value for the import assembly. ULONG cbHashValue, // [IN] Size in bytes for the hash value. IMetaModelCommon *pCommonImport, // [IN] The scope to merge into the emit scope. PCCOR_SIGNATURE pbSigImp, // signature from the imported scope MDTOKENMAP *ptkMap, // Internal OID mapping structure. CQuickBytes *pqkSigEmit, // [OUT] buffer for translated signature ULONG cbStartEmit, // [IN] start point of buffer to write to ULONG *pcbImp, // [OUT] total number of bytes consumed from pbSigImp ULONG *pcbEmit) // [OUT] total number of bytes write to pqkSigEmit { HRESULT hr; // A result. ULONG cb; // count of bytes ULONG cb1; // count of bytes ULONG cb2; // count of bytes ULONG cbSubTotal; ULONG cbImp; ULONG cbEmit; ULONG cbSrcTotal = 0; // count of bytes consumed in the imported signature ULONG cbDestTotal = 0; // count of bytes for the new signature ULONG ulElementType = 0; // place holder for expanded data ULONG ulData; ULONG ulTemp; mdToken tkRidFrom; // Original rid mdToken tkRidTo; // new rid int iData; CQuickArray cqaNesters; // Array of Nester tokens. CQuickArray cqaNesterNamespaces; // Array of Nester Namespaces. CQuickArray cqaNesterNames; // Array of Nester names. _ASSERTE(pcbEmit); cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &ulElementType); cbSrcTotal += cb; // count numbers of modifiers while (CorIsModifierElementType((CorElementType) ulElementType)) { cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &ulElementType); cbSrcTotal += cb; } // copy ELEMENT_TYPE_* over cbDestTotal = cbSrcTotal; IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbDestTotal)); memcpy(((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit, pbSigImp, cbDestTotal); switch (ulElementType) { case ELEMENT_TYPE_SZARRAY: // syntax : SZARRAY // conver the base type for the SZARRAY or GENERICARRAY IfFailGo(MergeUpdateTokenInFieldSig( pMiniMdAssemEmit, // The assembly emit scope. pMiniMdEmit, // The emit scope. pCommonAssemImport, // The assembly scope where the signature is from. pbHashValue, // Hash value for the import assembly. cbHashValue, // Size in bytes for the hash value. pCommonImport, // scope to merge into the emit scope. &pbSigImp[cbSrcTotal], // from the imported scope ptkMap, // OID mapping structure. pqkSigEmit, // [OUT] buffer for translated signature cbStartEmit + cbDestTotal, // [IN] start point of buffer to write to &cbImp, // [OUT] total number of bytes consumed from pbSigImp &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit cbSrcTotal += cbImp; cbDestTotal += cbEmit; break; case ELEMENT_TYPE_GENERICINST: { // syntax : WITH (ELEMENT_TYPE_CLASS | ELEMENT_TYPE_VALUECLASS) IfFailGo(MergeUpdateTokenInFieldSig( pMiniMdAssemEmit, // The assembly emit scope. pMiniMdEmit, // The emit scope. pCommonAssemImport, // The assembly scope where the signature is from. pbHashValue, // Hash value for the import assembly. cbHashValue, // Size in bytes for the hash value. pCommonImport, // scope to merge into the emit scope. &pbSigImp[cbSrcTotal], // from the imported scope ptkMap, // OID mapping structure. pqkSigEmit, // [OUT] buffer for translated signature cbStartEmit + cbDestTotal, // [IN] start point of buffer to write to &cbImp, // [OUT] total number of bytes consumed from pbSigImp &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit cbSrcTotal += cbImp; cbDestTotal += cbEmit; // copy over the number of arguments ULONG nargs; cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &nargs); IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbDestTotal + cb)); cb1 = CorSigCompressData(nargs, ((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit + cbDestTotal); _ASSERTE(cb == cb1); cbSrcTotal += cb; cbDestTotal += cb1; for (ULONG narg = 0; narg < nargs; narg++) { IfFailGo(MergeUpdateTokenInFieldSig( pMiniMdAssemEmit, // The assembly emit scope. pMiniMdEmit, // The emit scope. pCommonAssemImport, // The assembly scope where the signature is from. pbHashValue, // Hash value for the import assembly. cbHashValue, // Size in bytes for the hash value. pCommonImport, // The scope to merge into the emit scope. &pbSigImp[cbSrcTotal], // signature from the imported scope ptkMap, // Internal OID mapping structure. pqkSigEmit, // [OUT] buffer for translated signature cbStartEmit + cbDestTotal, // [IN] start point of buffer to write to &cbImp, // [OUT] total number of bytes consumed from pbSigImp &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit cbSrcTotal += cbImp; cbDestTotal += cbEmit; } } break; case ELEMENT_TYPE_MVAR: case ELEMENT_TYPE_VAR: // syntax : VAR // syntax : MVAR // after the VAR or MVAR there is an integer indicating which type variable // cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &ulData); IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbDestTotal + cb)); cb1 = CorSigCompressData(ulData, ((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit + cbDestTotal); _ASSERTE(cb == cb1); cbSrcTotal += cb; cbDestTotal += cb1; break; case ELEMENT_TYPE_ARRAY: // syntax : ARRAY BaseType [i size_1... size_i] [j lowerbound_1 ... lowerbound_j] // conver the base type for the MDARRAY IfFailGo(MergeUpdateTokenInFieldSig( pMiniMdAssemEmit, // The assembly emit scope. pMiniMdEmit, // The emit scope. pCommonAssemImport, // The assembly scope where the signature is from. pbHashValue, // Hash value for the import assembly. cbHashValue, // Size in bytes for the hash value. pCommonImport, // The scope to merge into the emit scope. &pbSigImp[cbSrcTotal], // signature from the imported scope ptkMap, // Internal OID mapping structure. pqkSigEmit, // [OUT] buffer for translated signature cbStartEmit + cbSrcTotal, // [IN] start point of buffer to write to &cbImp, // [OUT] total number of bytes consumed from pbSigImp &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit cbSrcTotal += cbImp; cbDestTotal += cbEmit; // Parse for the rank cbSubTotal = CorSigUncompressData(&pbSigImp[cbSrcTotal], &ulData); // if rank == 0, we are done if (ulData != 0) { // any size of dimension specified? cb = CorSigUncompressData(&pbSigImp[cbSrcTotal + cbSubTotal], &ulData); cbSubTotal += cb; while (ulData--) { cb = CorSigUncompressData(&pbSigImp[cbSrcTotal + cbSubTotal], &ulTemp); cbSubTotal += cb; } // any lower bound specified? cb = CorSigUncompressData(&pbSigImp[cbSrcTotal + cbSubTotal], &ulData); cbSubTotal += cb; while (ulData--) { cb = CorSigUncompressSignedInt(&pbSigImp[cbSrcTotal + cbSubTotal], &iData); cbSubTotal += cb; } } // cbSubTotal is now the number of bytes still left to move over // cbSrcTotal is where bytes start on the pbSigImp to be copied over // cbStartEmit + cbDestTotal is where the destination of copy IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbDestTotal + cbSubTotal)); memcpy(((BYTE *)pqkSigEmit->Ptr())+cbStartEmit + cbDestTotal, &pbSigImp[cbSrcTotal], cbSubTotal); cbSrcTotal = cbSrcTotal + cbSubTotal; cbDestTotal = cbDestTotal + cbSubTotal; break; case ELEMENT_TYPE_FNPTR: // function pointer is followed by another complete signature IfFailGo(MergeUpdateTokenInSig( pMiniMdAssemEmit, // The assembly emit scope. pMiniMdEmit, // The emit scope. pCommonAssemImport, // The assembly scope where the signature is from. pbHashValue, // Hash value for the import assembly. cbHashValue, // Size in bytes for the hash value. pCommonImport, // The scope to merge into the emit scope. &pbSigImp[cbSrcTotal], // signature from the imported scope ptkMap, // Internal OID mapping structure. pqkSigEmit, // [OUT] buffer for translated signature cbStartEmit + cbDestTotal, // [IN] start point of buffer to write to &cbImp, // [OUT] total number of bytes consumed from pbSigImp &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit cbSrcTotal += cbImp; cbDestTotal += cbEmit; break; case ELEMENT_TYPE_VALUETYPE: case ELEMENT_TYPE_CLASS: case ELEMENT_TYPE_CMOD_REQD: case ELEMENT_TYPE_CMOD_OPT: // syntax for CLASS = ELEMENT_TYPE_CLASS // syntax for VALUE_CLASS = ELEMENT_TYPE_VALUECLASS // now get the embedded typeref token cb = CorSigUncompressToken(&pbSigImp[cbSrcTotal], &tkRidFrom); // Map the ulRidFrom to ulRidTo if (ptkMap) { // mdtBaseType does not record in the map. It is unique across modules if ( TypeFromToken(tkRidFrom) == mdtBaseType ) { tkRidTo = tkRidFrom; } else { IfFailGo( ptkMap->Remap(tkRidFrom, &tkRidTo) ); } } else { // If the token is a TypeDef or a TypeRef, get/create the // ResolutionScope for the outermost TypeRef. if (TypeFromToken(tkRidFrom) == mdtTypeDef) { IfFailGo(ImportTypeDef(pMiniMdAssemEmit, pMiniMdEmit, pCommonAssemImport, pbHashValue, cbHashValue, pCommonImport, tkRidFrom, true, // Optimize to TypeDef if emit and import scopes are identical. &tkRidTo)); } else if (TypeFromToken(tkRidFrom) == mdtTypeRef) { IfFailGo(ImportTypeRef(pMiniMdAssemEmit, pMiniMdEmit, pCommonAssemImport, pbHashValue, cbHashValue, pCommonImport, tkRidFrom, &tkRidTo)); } else if ( TypeFromToken(tkRidFrom) == mdtTypeSpec ) { // copy over the TypeSpec PCCOR_SIGNATURE pvTypeSpecSig; ULONG cbTypeSpecSig; CQuickBytes qkTypeSpecSigEmit; ULONG cbTypeSpecEmit; IfFailGo(pCommonImport->CommonGetTypeSpecProps( tkRidFrom, &pvTypeSpecSig, &cbTypeSpecSig)); // Translate the typespec signature before look up IfFailGo(MergeUpdateTokenInFieldSig( pMiniMdAssemEmit, // The assembly emit scope. pMiniMdEmit, // The emit scope. pCommonAssemImport, // The assembly scope where the signature is from. pbHashValue, // Hash value for the import assembly. cbHashValue, // Size in bytes for the hash value. pCommonImport, // The scope to merge into the emit scope. pvTypeSpecSig, // signature from the imported scope ptkMap, // Internal OID mapping structure. &qkTypeSpecSigEmit, // [OUT] buffer for translated signature 0, // start from first byte of TypeSpec signature 0, // don't care how many bytes are consumed &cbTypeSpecEmit) ); // [OUT] total number of bytes write to pqkSigEmit hr = FindTypeSpec(pMiniMdEmit, (PCCOR_SIGNATURE) (qkTypeSpecSigEmit.Ptr()), cbTypeSpecEmit, &tkRidTo); if ( hr == CLDB_E_RECORD_NOTFOUND ) { // Create TypeSpec record. TypeSpecRec *pRecEmit; IfFailGo(pMiniMdEmit->AddTypeSpecRecord(&pRecEmit, (RID *)&tkRidTo)); IfFailGo(pMiniMdEmit->PutBlob( TBL_TypeSpec, TypeSpecRec::COL_Signature, pRecEmit, (PCCOR_SIGNATURE) (qkTypeSpecSigEmit.Ptr()), cbTypeSpecEmit)); tkRidTo = TokenFromRid( tkRidTo, mdtTypeSpec ); IfFailGo(pMiniMdEmit->UpdateENCLog(tkRidTo)); } IfFailGo( hr ); } else { _ASSERTE( TypeFromToken(tkRidFrom) == mdtBaseType ); // base type is unique across module tkRidTo = tkRidFrom; } } // How many bytes the new rid will consume? cb1 = CorSigCompressToken(tkRidTo, &ulData); // ensure buffer is big enough IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbDestTotal + cb1)); // store the new token cb2 = CorSigCompressToken( tkRidTo, (ULONG *)( ((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit + cbDestTotal) ); // inconsistency on CorSigCompressToken and CorSigUncompressToken _ASSERTE(cb1 == cb2); cbSrcTotal = cbSrcTotal + cb; cbDestTotal = cbDestTotal + cb1; if ( ulElementType == ELEMENT_TYPE_CMOD_REQD || ulElementType == ELEMENT_TYPE_CMOD_OPT) { // need to skip over the base type IfFailGo(MergeUpdateTokenInFieldSig( pMiniMdAssemEmit, // The assembly emit scope. pMiniMdEmit, // The emit scope. pCommonAssemImport, // The assembly scope where the signature is from. pbHashValue, // Hash value for the import assembly. cbHashValue, // Size in bytes for the hash value. pCommonImport, // The scope to merge into the emit scope. &pbSigImp[cbSrcTotal], // signature from the imported scope ptkMap, // Internal OID mapping structure. pqkSigEmit, // [OUT] buffer for translated signature cbStartEmit + cbDestTotal, // [IN] start point of buffer to write to &cbImp, // [OUT] total number of bytes consumed from pbSigImp &cbEmit)); // [OUT] total number of bytes write to pqkSigEmit cbSrcTotal += cbImp; cbDestTotal += cbEmit; } break; default: _ASSERTE(cbSrcTotal == cbDestTotal); if ((ulElementType >= ELEMENT_TYPE_MAX) || (ulElementType == ELEMENT_TYPE_PTR) || (ulElementType == ELEMENT_TYPE_BYREF) || (ulElementType == ELEMENT_TYPE_VALUEARRAY_UNSUPPORTED)) { IfFailGo(META_E_BAD_SIGNATURE); } break; } if (pcbImp) *pcbImp = cbSrcTotal; *pcbEmit = cbDestTotal; ErrExit: return hr; } // ImportHelper::MergeUpdateTokenInFieldSig #endif //FEATURE_METADATA_EMIT //**************************************************************************** // convert tokens contained in a COM+ signature //**************************************************************************** HRESULT ImportHelper::MergeUpdateTokenInSig(// S_OK or error. CMiniMdRW *pMiniMdAssemEmit, // [IN] The assembly emit scope. CMiniMdRW *pMiniMdEmit, // [IN] The emit scope. IMetaModelCommon *pCommonAssemImport,// [IN] Assembly scope where the signature is from. const void *pbHashValue, // [IN] Hash value for the import assembly. ULONG cbHashValue, // [IN] Size in bytes for the hash value. IMetaModelCommon *pCommonImport, // [IN] The scope to merge into the emit scope. PCCOR_SIGNATURE pbSigImp, // signature from the imported scope MDTOKENMAP *ptkMap, // Internal OID mapping structure. CQuickBytes *pqkSigEmit, // [OUT] translated signature ULONG cbStartEmit, // [IN] start point of buffer to write to ULONG *pcbImp, // [OUT] total number of bytes consumed from pbSigImp ULONG *pcbEmit) // [OUT] total number of bytes write to pqkSigEmit { #ifdef FEATURE_METADATA_EMIT HRESULT hr = NOERROR; // A result. ULONG cb; // count of bytes ULONG cb1; ULONG cbSrcTotal = 0; // count of bytes consumed in the imported signature ULONG cbDestTotal = 0; // count of bytes for the new signature ULONG cbEmit; // count of bytes consumed in the imported signature ULONG cbImp; // count of bytes for the new signature ULONG cArg = 0; // count of arguments in the signature ULONG cTyArg = 0; ULONG callingconv = 0; // calling convention from signature _ASSERTE(pcbEmit && pqkSigEmit && pbSigImp); // calling convention cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &callingconv); _ASSERTE((callingconv & IMAGE_CEE_CS_CALLCONV_MASK) < IMAGE_CEE_CS_CALLCONV_MAX); // skip over calling convention cbSrcTotal += cb; if (isCallConv(callingconv, IMAGE_CEE_CS_CALLCONV_FIELD)) { // It is a FieldRef cb1 = CorSigCompressData(callingconv, ((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit); // compression and uncompression better match _ASSERTE(cb == cb1); cbDestTotal = cbSrcTotal = cb; IfFailGo(MergeUpdateTokenInFieldSig( pMiniMdAssemEmit, pMiniMdEmit, pCommonAssemImport, pbHashValue, cbHashValue, pCommonImport, &pbSigImp[cbSrcTotal], ptkMap, pqkSigEmit, // output buffer to hold the new sig for the field cbStartEmit + cbDestTotal, // number of bytes already in pqkSigDest &cbImp, // number of bytes consumed from imported signature &cbEmit)); // number of bytes write to the new signature *pcbEmit = cbDestTotal + cbEmit; } else { // It is a MethodRef // count of type arguments if (callingconv & IMAGE_CEE_CS_CALLCONV_GENERIC) { cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &cTyArg); cbSrcTotal += cb; } // count of argument cb = CorSigUncompressData(&pbSigImp[cbSrcTotal], &cArg); cbSrcTotal += cb; // move over the calling convention and the count of arguments IfFailGo(pqkSigEmit->ReSizeNoThrow(cbStartEmit + cbSrcTotal)); memcpy(((BYTE *)pqkSigEmit->Ptr()) + cbStartEmit, pbSigImp, cbSrcTotal); cbDestTotal = cbSrcTotal; if ( !( isCallConv(callingconv, IMAGE_CEE_CS_CALLCONV_LOCAL_SIG) || isCallConv(callingconv, IMAGE_CEE_CS_CALLCONV_GENERICINST)) ) { // LocalVar sig does not have return type // process the return type IfFailGo(MergeUpdateTokenInFieldSig( pMiniMdAssemEmit, pMiniMdEmit, pCommonAssemImport, pbHashValue, cbHashValue, pCommonImport, &pbSigImp[cbSrcTotal], ptkMap, pqkSigEmit, // output buffer to hold the new sig for the field cbStartEmit + cbDestTotal, // number of bytes already in pqkSigDest &cbImp, // number of bytes consumed from imported signature &cbEmit)); // number of bytes write to the new signature // advance the count cbSrcTotal += cbImp; cbDestTotal += cbEmit; } while (cArg) { // process every argument IfFailGo(MergeUpdateTokenInFieldSig( pMiniMdAssemEmit, pMiniMdEmit, pCommonAssemImport, pbHashValue, cbHashValue, pCommonImport, &pbSigImp[cbSrcTotal], ptkMap, pqkSigEmit, // output buffer to hold the new sig for the field cbStartEmit + cbDestTotal, &cbImp, // number of bytes consumed from imported signature &cbEmit)); // number of bytes write to the new signature cbSrcTotal += cbImp; cbDestTotal += cbEmit; cArg--; } // total of number of bytes consumed from imported signature if (pcbImp) *pcbImp = cbSrcTotal; // total number of bytes emitted by this function call to the emitting signature *pcbEmit = cbDestTotal; } ErrExit: return hr; #else //!FEATURE_METADATA_EMIT // This code should be called only with public emit APIs _ASSERTE_MSG(FALSE, "This method should not be reachable"); return E_NOTIMPL; #endif //!FEATURE_METADATA_EMIT } // ImportHelper::MergeUpdateTokenInSig //**************************************************************************** // Given a TypeDef or a TypeRef, return the Nesting hierarchy. The first // element in the returned array always refers to the class token passed and // the nesting hierarchy expands outwards from there. //**************************************************************************** HRESULT ImportHelper::GetNesterHierarchy( IMetaModelCommon *pCommon, // Scope in which to find the hierarchy. mdToken tk, // TypeDef/TypeRef whose hierarchy is needed. CQuickArray &cqaNesters, // Array of Nesters. CQuickArray &cqaNamespaces, // Names of the nesters. CQuickArray &cqaNames) // Namespaces of the nesters. { _ASSERTE(pCommon && (TypeFromToken(tk) == mdtTypeDef || TypeFromToken(tk) == mdtTypeRef) && !IsNilToken(tk)); if (TypeFromToken(tk) == mdtTypeDef) { return GetTDNesterHierarchy(pCommon, tk, cqaNesters, cqaNamespaces, cqaNames); } else { return GetTRNesterHierarchy(pCommon, tk, cqaNesters, cqaNamespaces, cqaNames); } } // HRESULT ImportHelper::GetNesterHierarchy() //**************************************************************************** // Get Nesting hierarchy given a TypeDef. //**************************************************************************** HRESULT ImportHelper::GetTDNesterHierarchy( IMetaModelCommon *pCommon, // Scope in which to find the hierarchy. mdTypeDef td, // TypeDef whose hierarchy is needed. CQuickArray &cqaTdNesters,// Array of Nesters. CQuickArray &cqaNamespaces, // Namespaces of the nesters. CQuickArray &cqaNames) // Names of the nesters. { LPCUTF8 szName, szNamespace; DWORD dwFlags; mdTypeDef tdNester; ULONG ulNesters; HRESULT hr = NOERROR; _ASSERTE(pCommon && TypeFromToken(td) == mdtTypeDef && !IsNilToken(td)); // Set current Nester index to 0. ulNesters = 0; // The first element in the hierarchy is the TypeDef itself. tdNester = td; // Bogus initialization to kick off the while loop. dwFlags = tdNestedPublic; // Loop as long as the TypeDef is a Nested TypeDef. while (IsTdNested(dwFlags)) { if (InvalidRid(tdNester)) IfFailGo(CLDB_E_RECORD_NOTFOUND); // Get the name and namespace for the TypeDef. IfFailGo(pCommon->CommonGetTypeDefProps( tdNester, &szNamespace, &szName, &dwFlags, NULL, NULL)); // Update the dynamic arrays. ulNesters++; IfFailGo(cqaTdNesters.ReSizeNoThrow(ulNesters)); cqaTdNesters[ulNesters-1] = tdNester; IfFailGo(cqaNamespaces.ReSizeNoThrow(ulNesters)); cqaNamespaces[ulNesters-1] = szNamespace; IfFailGo(cqaNames.ReSizeNoThrow(ulNesters)); cqaNames[ulNesters-1] = szName; IfFailGo(pCommon->CommonGetEnclosingClassOfTypeDef(tdNester, &tdNester)); } // Outermost class must have enclosing of Nil. _ASSERTE(IsNilToken(tdNester)); ErrExit: return hr; } // HRESULT ImportHelper::GetTDNesterHierarchy() //**************************************************************************** // Get Nesting hierarchy given a TypeRef. //**************************************************************************** HRESULT ImportHelper::GetTRNesterHierarchy( IMetaModelCommon *pCommon, // [IN] Scope in which to find the hierarchy. mdTypeRef tr, // [IN] TypeRef whose hierarchy is needed. CQuickArray &cqaTrNesters,// [OUT] Array of Nesters. CQuickArray &cqaNamespaces, // [OUT] Namespaces of the nesters. CQuickArray &cqaNames) // [OUT] Names of the nesters. { LPCUTF8 szNamespace; LPCUTF8 szName; mdTypeRef trNester; mdToken tkResolutionScope; ULONG ulNesters; HRESULT hr = S_OK; _ASSERTE(pCommon && TypeFromToken(tr) == mdtTypeRef && !IsNilToken(tr)); // Set current Nester index to 0. ulNesters = 0; // The first element in the hierarchy is the TypeRef itself. trNester = tr; // Loop as long as the TypeRef is a Nested TypeRef. while (TypeFromToken(trNester) == mdtTypeRef && !IsNilToken(trNester)) { // Get the name and namespace for the TypeDef. IfFailGo(pCommon->CommonGetTypeRefProps( trNester, &szNamespace, &szName, &tkResolutionScope)); // Update the dynamic arrays. ulNesters++; IfFailGo(cqaTrNesters.ReSizeNoThrow(ulNesters)); cqaTrNesters[ulNesters-1] = trNester; IfFailGo(cqaNamespaces.ReSizeNoThrow(ulNesters)); cqaNamespaces[ulNesters-1] = szNamespace; IfFailGo(cqaNames.ReSizeNoThrow(ulNesters)); cqaNames[ulNesters-1] = szName; trNester = tkResolutionScope; } ErrExit: return hr; } // HRESULT ImportHelper::GetTRNesterHierarchy() //**************************************************************************** // Create the Nesting hierarchy given the array of TypeRef names. The first // TypeRef in the array is the innermost TypeRef. //**************************************************************************** HRESULT ImportHelper::CreateNesterHierarchy( CMiniMdRW *pMiniMdEmit, // [IN] Emit scope to create the Nesters in. CQuickArray &cqaNesterNamespaces, // [IN] Array of Nester namespaces. CQuickArray &cqaNesterNames, // [IN] Array of Nester names. mdToken tkResolutionScope, // [IN] ResolutionScope for the innermost TypeRef. mdTypeRef *ptr) // [OUT] Token for the innermost TypeRef. { TypeRefRec *pRecEmit; ULONG iRecord; LPCUTF8 szName; LPCUTF8 szNamespace; mdTypeRef trNester; mdTypeRef trCur; ULONG ulNesters; HRESULT hr = S_OK; _ASSERTE(cqaNesterNames.Size() == cqaNesterNamespaces.Size() && cqaNesterNames.Size()); // Initialize the output parameter. *ptr = mdTypeRefNil; // Get count of Nesters in the hierarchy. ulNesters = (ULONG)cqaNesterNames.Size(); // For each nester try to find the corresponding TypeRef in the emit scope. // For the outermost TypeRef, ResolutionScope is what's passed in. if (tkResolutionScope == mdTokenNil) trNester = mdTypeRefNil; else trNester = tkResolutionScope; ULONG ulCurNester; for (ulCurNester = ulNesters-1; ulCurNester != (ULONG) -1; ulCurNester--) { hr = FindTypeRefByName(pMiniMdEmit, trNester, cqaNesterNamespaces[ulCurNester], cqaNesterNames[ulCurNester], &trCur); if (hr == CLDB_E_RECORD_NOTFOUND) break; else IfFailGo(hr); trNester = trCur; } if (SUCCEEDED(hr)) *ptr = trNester; else if ( hr == CLDB_E_RECORD_NOTFOUND ) { // Create TypeRef records for the part of the hierarchy for which // TypeRefs are not already present. for (;ulCurNester != (ULONG) -1; ulCurNester--) { szName = cqaNesterNames[ulCurNester]; szNamespace = cqaNesterNamespaces[ulCurNester]; IfFailGo(pMiniMdEmit->AddTypeRefRecord(&pRecEmit, &iRecord)); if (szNamespace && szNamespace[0] != '\0') { // only put the namespace if it is not an empty string and not NULL IfFailGo(pMiniMdEmit->PutString(TBL_TypeRef, TypeRefRec::COL_Namespace, pRecEmit, szNamespace)); } IfFailGo(pMiniMdEmit->PutString(TBL_TypeRef, TypeRefRec::COL_Name, pRecEmit, szName)); IfFailGo(pMiniMdEmit->PutToken(TBL_TypeRef, TypeRefRec::COL_ResolutionScope, pRecEmit, trNester)); trNester = TokenFromRid(iRecord, mdtTypeRef); IfFailGo(pMiniMdEmit->UpdateENCLog(trNester)); // Hash the name. IfFailGo(pMiniMdEmit->AddNamedItemToHash(TBL_TypeRef, trNester, szName, 0)); } *ptr = trNester; } else IfFailGo(hr); ErrExit: return hr; } // ImportHelper::CreateNesterHierarchy //**************************************************************************** // Given the arrays of names and namespaces for the Nested Type hierarchy, // find the innermost TypeRef token. The arrays start with the innermost // TypeRefs and go outwards. //**************************************************************************** HRESULT ImportHelper::FindNestedTypeRef( CMiniMdRW *pMiniMd, // [IN] Scope in which to find the TypeRef. CQuickArray &cqaNesterNamespaces, // [IN] Array of Names. CQuickArray &cqaNesterNames, // [IN] Array of Namespaces. mdToken tkResolutionScope, // [IN] Resolution scope for the outermost TypeRef. mdTypeRef *ptr) // [OUT] Inner most TypeRef token. { ULONG ulNesters; ULONG ulCurNester; HRESULT hr = S_OK; _ASSERTE(cqaNesterNames.Size() == cqaNesterNamespaces.Size() && cqaNesterNames.Size()); // Set the output parameter to Nil token. *ptr = mdTokenNil; // Get count in the hierarchy, the give TypeDef included. ulNesters = (ULONG)cqaNesterNames.Size(); // For each nester try to find the corresponding TypeRef in // the emit scope. For the outermost TypeDef enclosing class is Nil. for (ulCurNester = ulNesters-1; ulCurNester != (ULONG) -1; ulCurNester--) { IfFailGo(FindTypeRefByName(pMiniMd, tkResolutionScope, cqaNesterNamespaces[ulCurNester], cqaNesterNames[ulCurNester], &tkResolutionScope)); } *ptr = tkResolutionScope; ErrExit: return hr; } // HRESULT ImportHelper::FindNestedTypeRef() //**************************************************************************** // Given the arrays of names and namespaces for the Nested Type hierarchy, // find the innermost TypeDef token. The arrays start with the innermost // TypeDef and go outwards. //**************************************************************************** HRESULT ImportHelper::FindNestedTypeDef( CMiniMdRW *pMiniMd, // [IN] Scope in which to find the TypeRef. CQuickArray &cqaNesterNamespaces, // [IN] Array of Namespaces. CQuickArray &cqaNesterNames, // [IN] Array of Names. mdTypeDef tdNester, // [IN] Enclosing class for the Outermost TypeDef. mdTypeDef *ptd) // [OUT] Inner most TypeRef token. { ULONG ulNesters; ULONG ulCurNester; HRESULT hr = S_OK; _ASSERTE(cqaNesterNames.Size() == cqaNesterNamespaces.Size() && cqaNesterNames.Size()); // Set the output parameter to Nil token. *ptd = mdTokenNil; // Get count in the hierarchy, the give TypeDef included. ulNesters = (ULONG)cqaNesterNames.Size(); // For each nester try to find the corresponding TypeRef in // the emit scope. For the outermost TypeDef enclosing class is Nil. for (ulCurNester = ulNesters-1; ulCurNester != (ULONG) -1; ulCurNester--) { IfFailGo(FindTypeDefByName(pMiniMd, cqaNesterNamespaces[ulCurNester], cqaNesterNames[ulCurNester], tdNester, &tdNester)); } *ptd = tdNester; ErrExit: return hr; } // ImportHelper::FindNestedTypeDef #ifdef FEATURE_METADATA_EMIT //**************************************************************************** // Given the TypeDef and the corresponding assembly and module import scopes, // create a corresponding TypeRef in the given emit scope. //**************************************************************************** HRESULT ImportHelper::ImportTypeDef( CMiniMdRW * pMiniMdAssemEmit, // [IN] Assembly emit scope. CMiniMdRW * pMiniMdEmit, // [IN] Module emit scope. IMetaModelCommon * pCommonAssemImport, // [IN] Assembly import scope. const void * pbHashValue, // [IN] Hash value for import assembly. ULONG cbHashValue, // [IN] Size in bytes of hash value. IMetaModelCommon * pCommonImport, // [IN] Module import scope. mdTypeDef tdImport, // [IN] Imported TypeDef. bool bReturnTd, // [IN] If the import and emit scopes are identical, return the TypeDef. mdToken * ptkType) // [OUT] Output token for the imported type in the emit scope. { CQuickArray cqaNesters; CQuickArray cqaNesterNames; CQuickArray cqaNesterNamespaces; GUID nullguid = GUID_NULL; GUID MvidAssemImport = nullguid; GUID MvidAssemEmit = nullguid; GUID MvidImport = nullguid; GUID MvidEmit = nullguid; GUID GuidImport = GUID_NULL; LPCUTF8 szModuleImport; mdToken tkOuterRes = mdTokenNil; HRESULT hr = S_OK; BOOL bBCL = false; _ASSERTE(pMiniMdEmit && pCommonImport && ptkType); _ASSERTE(TypeFromToken(tdImport) == mdtTypeDef && tdImport != mdTypeDefNil); // Get MVIDs for import and emit, assembly and module scopes. if (pCommonAssemImport != NULL) { IfFailGo(pCommonAssemImport->CommonGetScopeProps(0, &MvidAssemImport)); } IfFailGo(pCommonImport->CommonGetScopeProps(&szModuleImport, &MvidImport)); if (pMiniMdAssemEmit != NULL) { IfFailGo(static_cast(pMiniMdAssemEmit)->CommonGetScopeProps(0, &MvidAssemEmit)); } IfFailGo(static_cast(pMiniMdEmit)->CommonGetScopeProps(0, &MvidEmit)); if (pCommonAssemImport == NULL && strcmp(szModuleImport, COM_RUNTIME_LIBRARY) == 0) { const BYTE *pBlob; // Blob with dispid. ULONG cbBlob; // Length of blob. WCHAR wzBlob[40]; // Wide char format of guid. int ix; // Loop control. hr = pCommonImport->CommonGetCustomAttributeByName(1, INTEROP_GUID_TYPE, (const void **)&pBlob, &cbBlob); if (hr != S_FALSE) { // Should be in format. Total length == 41 // <0x0001><0x24>01234567-0123-0123-0123-001122334455<0x0000> if ((cbBlob == 41) || (GET_UNALIGNED_VAL16(pBlob) == 1)) { for (ix=1; ix<=36; ++ix) wzBlob[ix] = pBlob[ix+2]; wzBlob[0] = '{'; wzBlob[37] = '}'; wzBlob[38] = 0; // It's ok that we ignore the hr here. It's not needed, but I // don't want to remove it in case a code analysis tool will complain // about not capturing return codes. hr = IIDFromString(wzBlob, &GuidImport); } } bBCL = (GuidImport == LIBID_ComPlusRuntime); } // Compute the ResolutionScope for the imported type. if (bBCL) { // This is the case that we are referring to mscorlib.dll but client does not provide the manifest for // mscorlib.dll!! Do not generate ModuleRef to the mscorlib.dll. But instead we should just leave the // ResolutionScope empty tkOuterRes = mdTokenNil; } else if (MvidAssemImport == MvidAssemEmit && MvidImport == MvidEmit) { // The TypeDef is in the same Assembly and the Same scope. if (bReturnTd) { *ptkType = tdImport; goto ErrExit; } else tkOuterRes = TokenFromRid(1, mdtModule); } else if (MvidAssemImport == MvidAssemEmit && MvidImport != MvidEmit) { // The TypeDef is in the same Assembly but a different module. // Create a ModuleRef corresponding to the import scope. IfFailGo(CreateModuleRefFromScope(pMiniMdEmit, pCommonImport, &tkOuterRes)); } else if (MvidAssemImport != MvidAssemEmit) { if (pCommonAssemImport) { // The TypeDef is from a different Assembly. // Import and Emit scopes can't be identical and be from different // Assemblies at the same time. _ASSERTE(MvidImport != MvidEmit && "Import scope can't be identical to the Emit scope and be from a different Assembly at the same time."); _ASSERTE(pCommonAssemImport); // Create an AssemblyRef corresponding to the import scope. IfFailGo(CreateAssemblyRefFromAssembly(pMiniMdAssemEmit, pMiniMdEmit, pCommonAssemImport, pbHashValue, cbHashValue, &tkOuterRes)); } else { // @FUTURE: review this fix! We may want to return error in the future. // This is to enable smc to reference mscorlib.dll while it does not have the manifest for mscorlib.dll opened. // Create a Nil ResolutionScope to the TypeRef. tkOuterRes = mdTokenNil; } } // Get the nesting hierarchy for the Type from the import scope and create // the corresponding Type hierarchy in the emit scope. Note that the non- // nested class case simply folds into this scheme. IfFailGo(GetNesterHierarchy(pCommonImport, tdImport, cqaNesters, cqaNesterNamespaces, cqaNesterNames)); IfFailGo(CreateNesterHierarchy(pMiniMdEmit, cqaNesterNamespaces, cqaNesterNames, tkOuterRes, ptkType)); ErrExit: return hr; } // ImportHelper::ImportTypeDef //**************************************************************************** // Given the TypeRef and the corresponding assembly and module import scopes, // return the corresponding token in the given emit scope. // @FUTURE: Should we look at visibility flags on ExportedTypes and TypeDefs when // handling references across Assemblies? //**************************************************************************** HRESULT ImportHelper::ImportTypeRef( CMiniMdRW *pMiniMdAssemEmit, // [IN] Assembly emit scope. CMiniMdRW *pMiniMdEmit, // [IN] Module emit scope. IMetaModelCommon *pCommonAssemImport, // [IN] Assembly import scope. const void *pbHashValue, // [IN] Hash value for import assembly. ULONG cbHashValue, // [IN] Size in bytes of hash value. IMetaModelCommon *pCommonImport, // [IN] Module import scope. mdTypeRef trImport, // [IN] Imported TypeRef. mdToken *ptkType) // [OUT] Output token for the imported type in the emit scope. { CQuickArray cqaNesters; CQuickArray cqaNesterNames; CQuickArray cqaNesterNamespaces; LPCUTF8 szScopeNameEmit; GUID nullguid = GUID_NULL; GUID MvidAssemImport = nullguid; GUID MvidAssemEmit = nullguid; GUID MvidImport = nullguid; GUID MvidEmit = nullguid; mdToken tkOuterImportRes; // ResolutionScope for the outermost TypeRef in import scope. mdToken tkOuterEmitRes = mdTokenNil; // ResolutionScope for outermost TypeRef in emit scope. HRESULT hr = S_OK; bool bAssemblyRefFromAssemScope = false; _ASSERTE(pMiniMdEmit && pCommonImport && ptkType); _ASSERTE(TypeFromToken(trImport) == mdtTypeRef); // Get MVIDs for import and emit, assembly and module scopes. if (pCommonAssemImport != NULL) { IfFailGo(pCommonAssemImport->CommonGetScopeProps(0, &MvidAssemImport)); } IfFailGo(pCommonImport->CommonGetScopeProps(0, &MvidImport)); if (pMiniMdAssemEmit != NULL) { IfFailGo(static_cast(pMiniMdAssemEmit)->CommonGetScopeProps( 0, &MvidAssemEmit)); } IfFailGo(static_cast(pMiniMdEmit)->CommonGetScopeProps( &szScopeNameEmit, &MvidEmit)); // Get the outermost resolution scope for the TypeRef being imported. IfFailGo(GetNesterHierarchy(pCommonImport, trImport, cqaNesters, cqaNesterNamespaces, cqaNesterNames)); IfFailGo(pCommonImport->CommonGetTypeRefProps( cqaNesters[cqaNesters.Size() - 1], 0, 0, &tkOuterImportRes)); // Compute the ResolutionScope for the imported type. if (MvidAssemImport == MvidAssemEmit && MvidImport == MvidEmit) { *ptkType = trImport; goto ErrExit; } else if (MvidAssemImport == MvidAssemEmit && MvidImport != MvidEmit) { // The TypeRef is in the same Assembly but a different module. if (IsNilToken(tkOuterImportRes)) { tkOuterEmitRes = tkOuterImportRes; } else if (TypeFromToken(tkOuterImportRes) == mdtModule) { // TypeRef resolved to the import module in which its defined. // if (pMiniMdAssemEmit == NULL && pCommonAssemImport == NULL) { tkOuterEmitRes = TokenFromRid(1, mdtModule); } else { // Create a ModuleRef corresponding to the import scope. IfFailGo(CreateModuleRefFromScope(pMiniMdEmit, pCommonImport, &tkOuterEmitRes)); } } else if (TypeFromToken(tkOuterImportRes) == mdtAssemblyRef) { // TypeRef is from a different Assembly. // Create a corresponding AssemblyRef in the emit scope. IfFailGo(CreateAssemblyRefFromAssemblyRef(pMiniMdAssemEmit, pMiniMdEmit, pCommonImport, tkOuterImportRes, &tkOuterEmitRes)); } else if (TypeFromToken(tkOuterImportRes) == mdtModuleRef) { // Get Name of the ModuleRef. LPCUTF8 szMRName; IfFailGo(pCommonImport->CommonGetModuleRefProps(tkOuterImportRes, &szMRName)); if (!strcmp(szMRName, szScopeNameEmit)) { // ModuleRef from import scope resolves to the emit scope. tkOuterEmitRes = TokenFromRid(1, mdtModule); } else { // ModuleRef does not correspond to the emit scope. // Create a corresponding ModuleRef. IfFailGo(CreateModuleRefFromModuleRef(pMiniMdEmit, pCommonImport, tkOuterImportRes, &tkOuterEmitRes)); } } } else if (MvidAssemImport != MvidAssemEmit) { // The TypeDef is from a different Assembly. // Import and Emit scopes can't be identical and be from different // Assemblies at the same time. _ASSERTE(MvidImport != MvidEmit && "Import scope can't be identical to the Emit scope and be from a different Assembly at the same time."); mdToken tkImplementation; // Implementation token for ExportedType. if (IsNilToken(tkOuterImportRes)) { // BUG FIX:: URT 13626 // Well, before all of the clients generate AR for mscorlib.dll reference, it is not true // that tkOuterImportRes == nil will imply that we have to find such an entry in the import manifest!! // Look for a ExportedType entry in the import Assembly. Its an error // if we don't find a ExportedType entry. mdExportedType tkExportedType; hr = pCommonAssemImport->CommonFindExportedType( cqaNesterNamespaces[cqaNesters.Size() - 1], cqaNesterNames[cqaNesters.Size() - 1], mdTokenNil, &tkExportedType); if (SUCCEEDED(hr)) { IfFailGo(pCommonAssemImport->CommonGetExportedTypeProps( tkExportedType, NULL, NULL, &tkImplementation)); if (TypeFromToken(tkImplementation) == mdtFile) { // Type is from a different Assembly. IfFailGo(CreateAssemblyRefFromAssembly(pMiniMdAssemEmit, pMiniMdEmit, pCommonAssemImport, pbHashValue, cbHashValue, &tkOuterEmitRes)); } else if (TypeFromToken(tkImplementation) == mdtAssemblyRef) { // This folds into the case where the Type is AssemblyRef. So // let it fall through to that case. // Remember that this AssemblyRef token is actually from the Manifest scope not // the module scope!!! bAssemblyRefFromAssemScope = true; tkOuterImportRes = tkImplementation; } else _ASSERTE(!"Unexpected ExportedType implementation token."); } else { // In this case, we will just move over the TypeRef with Nil ResolutionScope. hr = NOERROR; tkOuterEmitRes = mdTokenNil; } } else if (TypeFromToken(tkOuterImportRes) == mdtModule) { // Type is from a different Assembly. IfFailGo(CreateAssemblyRefFromAssembly(pMiniMdAssemEmit, pMiniMdEmit, pCommonAssemImport, pbHashValue, cbHashValue, &tkOuterEmitRes)); } // Not else if, because mdtModule case above could change // tkOuterImportRes to an AssemblyRef. if (TypeFromToken(tkOuterImportRes) == mdtAssemblyRef) { // If there is an emit assembly, see if the import assembly ref points to // it. If there is no emit assembly, the import assembly, by definition, // does not point to this one. if (pMiniMdAssemEmit == NULL || !pMiniMdAssemEmit->getCountAssemblys()) hr = S_FALSE; else { if (bAssemblyRefFromAssemScope) { // Check to see if the AssemblyRef resolves to the emit assembly. IfFailGo(CompareAssemblyRefToAssembly(pCommonAssemImport, tkOuterImportRes, static_cast(pMiniMdAssemEmit))); } else { // Check to see if the AssemblyRef resolves to the emit assembly. IfFailGo(CompareAssemblyRefToAssembly(pCommonImport, tkOuterImportRes, static_cast(pMiniMdAssemEmit))); } } if (hr == S_OK) { // The TypeRef being imported is defined in the current Assembly. // Find the ExportedType for the outermost TypeRef in the Emit assembly. mdExportedType tkExportedType; hr = FindExportedType(pMiniMdAssemEmit, cqaNesterNamespaces[cqaNesters.Size() - 1], cqaNesterNames[cqaNesters.Size() - 1], mdTokenNil, // Enclosing ExportedType. &tkExportedType); if (hr == S_OK) { // Create a ModuleRef based on the File name for the ExportedType. // If the ModuleRef corresponds to pMiniMdEmit, the function // will return S_FALSE, in which case set tkOuterEmitRes to // the Module token. hr = CreateModuleRefFromExportedType(pMiniMdAssemEmit, pMiniMdEmit, tkExportedType, &tkOuterEmitRes); if (hr == S_FALSE) tkOuterEmitRes = TokenFromRid(1, mdtModule); else IfFailGo(hr); } else if (hr == CLDB_E_RECORD_NOTFOUND) { // Find the Type in the Assembly emit scope to cover the // case where ExportedTypes may be implicitly defined. Its an // error if we can't find the Type at this point. IfFailGo(FindTypeDefByName(pMiniMdAssemEmit, cqaNesterNamespaces[cqaNesters.Size() - 1], cqaNesterNames[cqaNesters.Size() - 1], mdTokenNil, // Enclosing Type. &tkOuterEmitRes)); tkOuterEmitRes = TokenFromRid(1, mdtModule); } else { _ASSERTE(FAILED(hr)); IfFailGo(hr); } } else if (hr == S_FALSE) { // The TypeRef being imported is from a different Assembly. if (bAssemblyRefFromAssemScope) { // Create a corresponding AssemblyRef. IfFailGo(CreateAssemblyRefFromAssemblyRef(pMiniMdAssemEmit, pMiniMdEmit, pCommonAssemImport, tkOuterImportRes, &tkOuterEmitRes)); } else { // Create a corresponding AssemblyRef. IfFailGo(CreateAssemblyRefFromAssemblyRef(pMiniMdAssemEmit, pMiniMdEmit, pCommonImport, tkOuterImportRes, &tkOuterEmitRes)); } } else { _ASSERTE(FAILED(hr)); IfFailGo(hr); } } else if (TypeFromToken(tkOuterImportRes) == mdtModuleRef) { // Type is from a different Assembly. IfFailGo(CreateAssemblyRefFromAssembly(pMiniMdAssemEmit, pMiniMdEmit, pCommonAssemImport, pbHashValue, cbHashValue, &tkOuterEmitRes)); } } // Try to find the TypeDef in the emit scope. If we cannot find the // typedef, we need to introduce a typeref. // See if the Nested TypeDef is present in the Emit scope. hr = CLDB_E_RECORD_NOTFOUND; if (TypeFromToken(tkOuterEmitRes) == mdtModule && !IsNilToken(tkOuterEmitRes)) { hr = FindNestedTypeDef(pMiniMdEmit, cqaNesterNamespaces, cqaNesterNames, mdTokenNil, ptkType); // cannot assert now!! Due to the IJW workaround! // _ASSERTE(SUCCEEDED(hr)); } if (hr == CLDB_E_RECORD_NOTFOUND) { IfFailGo(CreateNesterHierarchy(pMiniMdEmit, cqaNesterNamespaces, cqaNesterNames, tkOuterEmitRes, ptkType)); } else IfFailGo(hr); ErrExit: return hr; } // ImportHelper::ImportTypeRef //****************************************************************************** // Given import scope, create a corresponding ModuleRef. //****************************************************************************** HRESULT ImportHelper::CreateModuleRefFromScope( // S_OK or error. CMiniMdRW *pMiniMdEmit, // [IN] Emit scope in which the ModuleRef is to be created. IMetaModelCommon *pCommonImport, // [IN] Import scope. mdModuleRef *ptkModuleRef) // [OUT] Output token for ModuleRef. { HRESULT hr = S_OK; LPCSTR szName; ModuleRefRec *pRecordEmit; RID iRecordEmit; // Set output to nil. *ptkModuleRef = mdTokenNil; // Get name of import scope. IfFailGo(pCommonImport->CommonGetScopeProps(&szName, 0)); // See if the ModuleRef exists in the Emit scope. hr = FindModuleRef(pMiniMdEmit, szName, ptkModuleRef); if (hr == CLDB_E_RECORD_NOTFOUND) { if (szName[0] == '\0') { // It the referenced Module does not have a proper name, use the nil token instead. LOG((LOGMD, "WARNING!!! MD ImportHelper::CreatemoduleRefFromScope but scope does not have a proper name!!!!")); // clear the error hr = NOERROR; // It is a bug to create an ModuleRef to an empty name!!! *ptkModuleRef = mdTokenNil; } else { // Create ModuleRef record and set the output parameter. IfFailGo(pMiniMdEmit->AddModuleRefRecord(&pRecordEmit, &iRecordEmit)); *ptkModuleRef = TokenFromRid(iRecordEmit, mdtModuleRef); IfFailGo(pMiniMdEmit->UpdateENCLog(*ptkModuleRef)); // It is a bug to create an ModuleRef to mscorlib.dll _ASSERTE(strcmp(szName, COM_RUNTIME_LIBRARY) != 0); // Set the name of ModuleRef. IfFailGo(pMiniMdEmit->PutString(TBL_ModuleRef, ModuleRefRec::COL_Name, pRecordEmit, szName)); } } else IfFailGo(hr); ErrExit: return hr; } // ImportHelper::CreateModuleRefFromScope //****************************************************************************** // Given an import scope and a ModuleRef, create a corresponding ModuleRef in // the given emit scope. //****************************************************************************** HRESULT ImportHelper::CreateModuleRefFromModuleRef( // S_OK or error. CMiniMdRW *pMiniMdEmit, // [IN] Emit scope. IMetaModelCommon *pCommon, // [IN] Import scope. mdModuleRef tkModuleRef, // [IN] ModuleRef token. mdModuleRef *ptkModuleRef) // [OUT] ModuleRef token in the emit scope. { HRESULT hr = S_OK; LPCSTR szName; ModuleRefRec *pRecord; RID iRecord; // Set output to Nil. *ptkModuleRef = mdTokenNil; // Get name of the ModuleRef being imported. IfFailGo(pCommon->CommonGetModuleRefProps(tkModuleRef, &szName)); // See if the ModuleRef exist in the Emit scope. hr = FindModuleRef(pMiniMdEmit, szName, ptkModuleRef); if (hr == CLDB_E_RECORD_NOTFOUND) { // Create ModuleRef record and set the output parameter. IfFailGo(pMiniMdEmit->AddModuleRefRecord(&pRecord, &iRecord)); *ptkModuleRef = TokenFromRid(iRecord, mdtModuleRef); IfFailGo(pMiniMdEmit->UpdateENCLog(*ptkModuleRef)); // Set the name of ModuleRef. IfFailGo(pMiniMdEmit->PutString(TBL_ModuleRef, ModuleRefRec::COL_Name, pRecord, szName)); } else { IfFailGo(hr); } ErrExit: return hr; } // ImportHelper::CreateModuleRefFromModuleRef //****************************************************************************** // Given a ExportedType and the Assembly emit scope, create a corresponding ModuleRef // in the give emit scope. The ExportedType being passed in must belong to the // Assembly passed in. Function returns S_FALSE if the ExportedType is implemented // by the emit scope passed in. //****************************************************************************** HRESULT ImportHelper::CreateModuleRefFromExportedType( // S_OK or error. CMiniMdRW *pAssemEmit, // [IN] Import assembly scope. CMiniMdRW *pMiniMdEmit, // [IN] Emit scope. mdExportedType tkExportedType, // [IN] ExportedType token in Assembly emit scope. mdModuleRef *ptkModuleRef) // [OUT] ModuleRef token in the emit scope. { mdFile tkFile; LPCUTF8 szFile; LPCUTF8 szScope; FileRec *pFileRec; HRESULT hr = S_OK; // Set output to nil. *ptkModuleRef = mdTokenNil; // Get the implementation token for the ExportedType. It must be a File token // since the caller should call this function only on ExportedTypes that resolve // to the same Assembly. IfFailGo(static_cast(pAssemEmit)->CommonGetExportedTypeProps( tkExportedType, NULL, NULL, &tkFile)); _ASSERTE(TypeFromToken(tkFile) == mdtFile); // Get the name of the file. IfFailGo(pAssemEmit->GetFileRecord(RidFromToken(tkFile), &pFileRec)); IfFailGo(pAssemEmit->getNameOfFile(pFileRec, &szFile)); // Get the name of the emit scope. IfFailGo(static_cast(pMiniMdEmit)->CommonGetScopeProps( &szScope, 0)); // If the file corresponds to the emit scope, return S_FALSE; if (!strcmp(szFile, szScope)) return S_FALSE; // See if a ModuleRef exists with this name. hr = FindModuleRef(pMiniMdEmit, szFile, ptkModuleRef); if (hr == CLDB_E_RECORD_NOTFOUND) { // Create ModuleRef record and set the output parameter. ModuleRefRec *pRecord; RID iRecord; IfFailGo(pMiniMdEmit->AddModuleRefRecord(&pRecord, &iRecord)); *ptkModuleRef = TokenFromRid(iRecord, mdtModuleRef); IfFailGo(pMiniMdEmit->UpdateENCLog(*ptkModuleRef)); // Set the name of ModuleRef. IfFailGo(pMiniMdEmit->PutString(TBL_ModuleRef, ModuleRefRec::COL_Name, pRecord, szFile)); } else IfFailGo(hr); ErrExit: return hr; } // ImportHelper::CreateModuleRefFromExportedType //****************************************************************************** // Given an AssemblyRef and the corresponding scope, create an AssemblyRef in // the given Module scope and Assembly scope. //****************************************************************************** HRESULT ImportHelper::CreateAssemblyRefFromAssemblyRef( CMiniMdRW *pMiniMdAssemEmit, // [IN] Assembly emit scope. CMiniMdRW *pMiniMdModuleEmit, // [IN] Module emit scope IMetaModelCommon *pCommonImport, // [IN] Scope to import the assembly ref from. mdAssemblyRef tkAssemRef, // [IN] Assembly ref to be imported. mdAssemblyRef *ptkAssemblyRef) // [OUT] AssemblyRef in the emit scope. { AssemblyRefRec *pRecordEmit; CMiniMdRW *rMiniMdRW[2]; CMiniMdRW *pMiniMdEmit; RID iRecordEmit; USHORT usMajorVersion; USHORT usMinorVersion; USHORT usBuildNumber; USHORT usRevisionNumber; DWORD dwFlags; const void *pbPublicKeyOrToken; ULONG cbPublicKeyOrToken; LPCUTF8 szName; LPCUTF8 szLocale; const void *pbHashValue; ULONG cbHashValue; HRESULT hr = S_OK; // Set output to Nil. *ptkAssemblyRef = mdTokenNil; // Get import AssemblyRef props. IfFailGo(pCommonImport->CommonGetAssemblyRefProps( tkAssemRef, &usMajorVersion, &usMinorVersion, &usBuildNumber, &usRevisionNumber, &dwFlags, &pbPublicKeyOrToken, &cbPublicKeyOrToken, &szName, &szLocale, &pbHashValue, &cbHashValue)); // Create the AssemblyRef in both the Assembly and Module emit scopes. rMiniMdRW[0] = pMiniMdAssemEmit; rMiniMdRW[1] = pMiniMdModuleEmit; for (ULONG i = 0; i < 2; i++) { pMiniMdEmit = rMiniMdRW[i]; if (!pMiniMdEmit) continue; // See if the AssemblyRef already exists in the emit scope. hr = FindAssemblyRef(pMiniMdEmit, szName, szLocale, pbPublicKeyOrToken, cbPublicKeyOrToken, usMajorVersion, usMinorVersion, usBuildNumber, usRevisionNumber, dwFlags, &tkAssemRef); if (hr == CLDB_E_RECORD_NOTFOUND) { // Create the AssemblyRef record and set the output parameter. IfFailGo(pMiniMdEmit->AddAssemblyRefRecord(&pRecordEmit, &iRecordEmit)); tkAssemRef = TokenFromRid(iRecordEmit, mdtAssemblyRef); IfFailGo(pMiniMdEmit->UpdateENCLog(tkAssemRef)); // Set parameters derived from the import Assembly. pRecordEmit->SetMajorVersion(usMajorVersion); pRecordEmit->SetMinorVersion(usMinorVersion); pRecordEmit->SetBuildNumber(usBuildNumber); pRecordEmit->SetRevisionNumber(usRevisionNumber); pRecordEmit->SetFlags(dwFlags); IfFailGo(pMiniMdEmit->PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_PublicKeyOrToken, pRecordEmit, pbPublicKeyOrToken, cbPublicKeyOrToken)); IfFailGo(pMiniMdEmit->PutString(TBL_AssemblyRef, AssemblyRefRec::COL_Name, pRecordEmit, szName)); IfFailGo(pMiniMdEmit->PutString(TBL_AssemblyRef, AssemblyRefRec::COL_Locale, pRecordEmit, szLocale)); // Set the parameters passed in for the AssemblyRef. IfFailGo(pMiniMdEmit->PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_HashValue, pRecordEmit, pbHashValue, cbHashValue)); } else IfFailGo(hr); // Set the output parameter for the AssemblyRef emitted in Module emit scope. if (i) *ptkAssemblyRef = tkAssemRef; } ErrExit: return hr; } // ImportHelper::CreateAssemblyRefFromAssemblyRef //****************************************************************************** // Given the Assembly Import scope, hash value and execution location, create // a corresponding AssemblyRef in the given assembly and module emit scope. // Set the output parameter to the AssemblyRef token emitted in the module emit // scope. //****************************************************************************** HRESULT ImportHelper::CreateAssemblyRefFromAssembly( CMiniMdRW * pMiniMdAssemEmit, // [IN] Emit assembly scope. CMiniMdRW * pMiniMdModuleEmit, // [IN] Emit module scope. IMetaModelCommon * pCommonAssemImport, // [IN] Assembly import scope. const void * pbHashValue, // [IN] Hash Blob for Assembly. ULONG cbHashValue, // [IN] Count of bytes. mdAssemblyRef * ptkAssemblyRef) // [OUT] AssemblyRef token. { #ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER return E_NOTIMPL; #else //!FEATURE_METADATA_EMIT_IN_DEBUGGER AssemblyRefRec *pRecordEmit; CMiniMdRW *rMiniMdRW[2]; CMiniMdRW *pMiniMdEmit; RID iRecordEmit; USHORT usMajorVersion; USHORT usMinorVersion; USHORT usBuildNumber; USHORT usRevisionNumber; DWORD dwFlags; const void *pbPublicKey; ULONG cbPublicKey; LPCUTF8 szName; LPCUTF8 szLocale; mdAssemblyRef tkAssemRef; HRESULT hr = S_OK; const void *pbToken = NULL; ULONG cbToken = 0; ULONG i; // Set output to Nil. *ptkAssemblyRef = mdTokenNil; // Get the Assembly props. IfFailGo(pCommonAssemImport->CommonGetAssemblyProps( &usMajorVersion, &usMinorVersion, &usBuildNumber, &usRevisionNumber, &dwFlags, &pbPublicKey, &cbPublicKey, &szName, &szLocale)); // Compress the public key into a token. if ((pbPublicKey != NULL) && (cbPublicKey != 0)) { _ASSERTE(IsAfPublicKey(dwFlags)); dwFlags &= ~afPublicKey; if (!StrongNameTokenFromPublicKey((BYTE*)pbPublicKey, cbPublicKey, (BYTE**)&pbToken, &cbToken)) IfFailGo(StrongNameErrorInfo()); } else _ASSERTE(!IsAfPublicKey(dwFlags)); // Create the AssemblyRef in both the Assembly and Module emit scopes. rMiniMdRW[0] = pMiniMdAssemEmit; rMiniMdRW[1] = pMiniMdModuleEmit; for (i = 0; i < 2; i++) { pMiniMdEmit = rMiniMdRW[i]; if (!pMiniMdEmit) continue; // See if the AssemblyRef already exists in the emit scope. hr = FindAssemblyRef(pMiniMdEmit, szName, szLocale, pbToken, cbToken, usMajorVersion, usMinorVersion, usBuildNumber, usRevisionNumber, dwFlags, &tkAssemRef); if (hr == CLDB_E_RECORD_NOTFOUND) { // Create the AssemblyRef record and set the output parameter. IfFailGo(pMiniMdEmit->AddAssemblyRefRecord(&pRecordEmit, &iRecordEmit)); tkAssemRef = TokenFromRid(iRecordEmit, mdtAssemblyRef); IfFailGo(pMiniMdEmit->UpdateENCLog(tkAssemRef)); // Set parameters derived from the import Assembly. pRecordEmit->SetMajorVersion(usMajorVersion); pRecordEmit->SetMinorVersion(usMinorVersion); pRecordEmit->SetBuildNumber(usBuildNumber); pRecordEmit->SetRevisionNumber(usRevisionNumber); pRecordEmit->SetFlags(dwFlags); IfFailGo(pMiniMdEmit->PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_PublicKeyOrToken, pRecordEmit, pbToken, cbToken)); IfFailGo(pMiniMdEmit->PutString(TBL_AssemblyRef, AssemblyRefRec::COL_Name, pRecordEmit, szName)); IfFailGo(pMiniMdEmit->PutString(TBL_AssemblyRef, AssemblyRefRec::COL_Locale, pRecordEmit, szLocale)); // Set the parameters passed in for the AssemblyRef. IfFailGo(pMiniMdEmit->PutBlob(TBL_AssemblyRef, AssemblyRefRec::COL_HashValue, pRecordEmit, pbHashValue, cbHashValue)); } else IfFailGo(hr); // Set the output parameter for the AssemblyRef emitted in Module emit scope. if (i) *ptkAssemblyRef = tkAssemRef; } ErrExit: if (pbToken) StrongNameFreeBuffer((BYTE*)pbToken); return hr; #endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER } // ImportHelper::CreateAssemblyRefFromAssembly //****************************************************************************** // Given an AssemblyRef and the corresponding scope, compare it to see if it // refers to the given Assembly. //****************************************************************************** HRESULT ImportHelper::CompareAssemblyRefToAssembly( // S_OK, S_FALSE or error. IMetaModelCommon *pCommonAssem1, // [IN] Scope that defines the AssemblyRef. mdAssemblyRef tkAssemRef, // [IN] AssemblyRef. IMetaModelCommon *pCommonAssem2) // [IN] Assembly against which the Ref is compared. { #ifdef FEATURE_METADATA_EMIT_IN_DEBUGGER return E_NOTIMPL; #else //!FEATURE_METADATA_EMIT_IN_DEBUGGER HRESULT hr; USHORT usMajorVersion1; USHORT usMinorVersion1; USHORT usBuildNumber1; USHORT usRevisionNumber1; const void *pbPublicKeyOrToken1; ULONG cbPublicKeyOrToken1; LPCUTF8 szName1; LPCUTF8 szLocale1; DWORD dwFlags1; USHORT usMajorVersion2; USHORT usMinorVersion2; USHORT usBuildNumber2; USHORT usRevisionNumber2; const void *pbPublicKey2; ULONG cbPublicKey2; LPCUTF8 szName2; LPCUTF8 szLocale2; const void *pbToken = NULL; ULONG cbToken = 0; bool fMatch; // Get the AssemblyRef props. IfFailRet(pCommonAssem1->CommonGetAssemblyRefProps( tkAssemRef, &usMajorVersion1, &usMinorVersion1, &usBuildNumber1, &usRevisionNumber1, &dwFlags1, &pbPublicKeyOrToken1, &cbPublicKeyOrToken1, &szName1, &szLocale1, NULL, NULL)); // Get the Assembly props. IfFailRet(pCommonAssem2->CommonGetAssemblyProps( &usMajorVersion2, &usMinorVersion2, &usBuildNumber2, &usRevisionNumber2, 0, &pbPublicKey2, &cbPublicKey2, &szName2, &szLocale2)); // Compare. if (usMajorVersion1 != usMajorVersion2 || usMinorVersion1 != usMinorVersion2 || usBuildNumber1 != usBuildNumber2 || usRevisionNumber1 != usRevisionNumber2 || strcmp(szName1, szName2) || strcmp(szLocale1, szLocale2)) { return S_FALSE; } // Defs always contain a full public key (or no key at all). Refs may have // no key, a full public key or a tokenized key. if ((cbPublicKeyOrToken1 && !cbPublicKey2) || (!cbPublicKeyOrToken1 && cbPublicKey2)) return S_FALSE; if (cbPublicKeyOrToken1) { // If ref contains a full public key we can just directly compare. if (IsAfPublicKey(dwFlags1) && (cbPublicKeyOrToken1 != cbPublicKey2 || memcmp(pbPublicKeyOrToken1, pbPublicKey2, cbPublicKeyOrToken1))) return S_FALSE; // Otherwise we need to compress the def public key into a token. if (!StrongNameTokenFromPublicKey((BYTE*)pbPublicKey2, cbPublicKey2, (BYTE**)&pbToken, &cbToken)) return StrongNameErrorInfo(); fMatch = cbPublicKeyOrToken1 == cbToken && !memcmp(pbPublicKeyOrToken1, pbToken, cbPublicKeyOrToken1); StrongNameFreeBuffer((BYTE*)pbToken); if (!fMatch) return S_FALSE; } return S_OK; #endif //!FEATURE_METADATA_EMIT_IN_DEBUGGER } // ImportHelper::CompareAssemblyRefToAssembly #endif //FEATURE_METADATA_EMIT