// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // ============================================================ // // AssemblyName.cpp // // // Implements the AssemblyName class // // ============================================================ #include "assemblyname.hpp" #include "utils.hpp" #include "textualidentityparser.hpp" #include "corpriv.h" #include "ex.h" namespace { // See https://docs.microsoft.com/dotnet/framework/reflection-and-codedom/specifying-fully-qualified-type-names#specifying-assembly-names const WCHAR* s_neutralCulture = W("neutral"); } namespace BINDER_SPACE { AssemblyName::AssemblyName() { m_cRef = 1; m_dwNameFlags = NAME_FLAG_NONE; // Default values present in every assembly name SetHave(AssemblyIdentity::IDENTITY_FLAG_CULTURE | AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN_NULL); } HRESULT AssemblyName::Init(IMDInternalImport *pIMetaDataAssemblyImport, PEKIND PeKind, mdAssemblyRef mdar /* = 0 */, BOOL fIsDefinition /* = TRUE */) { HRESULT hr = S_OK; mdAssembly mda = 0; AssemblyMetaDataInternal amd = {0}; CONST VOID *pvPublicKeyToken = NULL; DWORD dwPublicKeyToken = 0; LPCSTR pAssemblyName = NULL; DWORD dwRefOrDefFlags = 0; DWORD dwHashAlgId = 0; if (fIsDefinition) { // Get the assembly token IF_FAIL_GO(pIMetaDataAssemblyImport->GetAssemblyFromScope(&mda)); } // Get name and metadata if (fIsDefinition) { IF_FAIL_GO(pIMetaDataAssemblyImport->GetAssemblyProps( mda, // [IN] The Assembly for which to get the properties. &pvPublicKeyToken, // [OUT] Pointer to the PublicKeyToken blob. &dwPublicKeyToken, // [OUT] Count of bytes in the PublicKeyToken Blob. &dwHashAlgId, // [OUT] Hash Algorithm. &pAssemblyName, // [OUT] Name. &amd, // [OUT] Assembly MetaData. &dwRefOrDefFlags // [OUT] Flags. )); } else { IF_FAIL_GO(pIMetaDataAssemblyImport->GetAssemblyRefProps( mdar, // [IN] The Assembly for which to get the properties. &pvPublicKeyToken, // [OUT] Pointer to the PublicKeyToken blob. &dwPublicKeyToken, // [OUT] Count of bytes in the PublicKeyToken Blob. &pAssemblyName, // [OUT] Name. &amd, // [OUT] Assembly MetaData. NULL, // [OUT] Hash blob. NULL, // [OUT] Count of bytes in hash blob. &dwRefOrDefFlags // [OUT] Flags. )); } { StackSString culture; culture.SetUTF8(amd.szLocale); culture.Normalize(); SString::CIterator itr = culture.Begin(); if (culture.Find(itr, L';')) { culture = SString(culture, culture.Begin(), itr-1); } SetCulture(culture); } { StackSString assemblyName; assemblyName.SetUTF8(pAssemblyName); assemblyName.Normalize(); COUNT_T assemblyNameLength = assemblyName.GetCount(); if (assemblyNameLength == 0 || assemblyNameLength >= MAX_PATH_FNAME) { IF_FAIL_GO(FUSION_E_INVALID_NAME); } SetSimpleName(assemblyName); } // See if the assembly[def] is retargetable (ie, for a generic assembly). if (IsAfRetargetable(dwRefOrDefFlags)) { SetIsRetargetable(TRUE); } // Set ContentType if (IsAfContentType_Default(dwRefOrDefFlags)) { SetContentType(AssemblyContentType_Default); } else { IF_FAIL_GO(FUSION_E_INVALID_NAME); } // Set the assembly version { AssemblyVersion *pAssemblyVersion = GetVersion(); pAssemblyVersion->SetFeatureVersion(amd.usMajorVersion, amd.usMinorVersion); pAssemblyVersion->SetServiceVersion(amd.usBuildNumber, amd.usRevisionNumber); SetHave(AssemblyIdentity::IDENTITY_FLAG_VERSION); } // Set public key and/or public key token (if we have it) if (dwPublicKeyToken && pvPublicKeyToken) { SBuffer publicKeyOrTokenBLOB((const BYTE *) pvPublicKeyToken, dwPublicKeyToken); if (IsAfPublicKey(dwRefOrDefFlags)) { SBuffer publicKeyTokenBLOB; IF_FAIL_GO(GetTokenFromPublicKey(publicKeyOrTokenBLOB, publicKeyTokenBLOB)); GetPublicKeyTokenBLOB().Set(publicKeyTokenBLOB); } else { GetPublicKeyTokenBLOB().Set(publicKeyOrTokenBLOB); } SetHave(AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN); } SetArchitecture(PeKind); Exit: return hr; } HRESULT AssemblyName::Init(const AssemblyNameData &data) { DWORD flags = data.IdentityFlags; m_simpleName.SetUTF8(data.Name); m_version.SetFeatureVersion(data.MajorVersion, data.MinorVersion); m_version.SetServiceVersion(data.BuildNumber, data.RevisionNumber); m_cultureOrLanguage.SetUTF8(data.Culture); m_publicKeyOrTokenBLOB.Set(data.PublicKeyOrToken, data.PublicKeyOrTokenLength); if ((flags & BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY) != 0) { // Convert public key to token SBuffer publicKeyToken; HRESULT hr = GetTokenFromPublicKey(m_publicKeyOrTokenBLOB, publicKeyToken); if (FAILED(hr)) return hr; m_publicKeyOrTokenBLOB.Set(publicKeyToken); flags &= ~BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY; flags |= BINDER_SPACE::AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN; } m_kProcessorArchitecture = data.ProcessorArchitecture; m_kContentType = data.ContentType; SetHave(flags); return S_OK; } ULONG AssemblyName::AddRef() { return InterlockedIncrement(&m_cRef); } ULONG AssemblyName::Release() { ULONG ulRef = InterlockedDecrement(&m_cRef); if (ulRef == 0) { delete this; } return ulRef; } BOOL AssemblyName::IsCoreLib() { // TODO: Is this simple comparison enough? return SString::_wcsicmp(GetSimpleName().GetUnicode(), CoreLibName_W) == 0; } bool AssemblyName::IsNeutralCulture() { return m_cultureOrLanguage.IsEmpty() || SString::_wcsicmp(m_cultureOrLanguage.GetUnicode(), s_neutralCulture) == 0; } ULONG AssemblyName::Hash(DWORD dwIncludeFlags) { DWORD dwHash = 0; DWORD dwUseIdentityFlags = m_dwIdentityFlags; // Prune unwanted name parts if ((dwIncludeFlags & INCLUDE_VERSION) == 0) { dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_VERSION; } if ((dwIncludeFlags & INCLUDE_ARCHITECTURE) == 0) { dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE; } if ((dwIncludeFlags & INCLUDE_RETARGETABLE) == 0) { dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE; } if ((dwIncludeFlags & INCLUDE_CONTENT_TYPE) == 0) { dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE; } if ((dwIncludeFlags & INCLUDE_PUBLIC_KEY_TOKEN) == 0) { dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY; dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN; } if ((dwIncludeFlags & EXCLUDE_CULTURE) != 0) { dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_CULTURE; } dwHash ^= static_cast(GetSimpleName().HashCaseInsensitive()); dwHash = _rotl(dwHash, 4); if (AssemblyIdentity::Have(dwUseIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY) || AssemblyIdentity::Have(dwUseIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_PUBLIC_KEY_TOKEN)) { const BYTE *pbPublicKeyOrToken = GetPublicKeyTokenBLOB(); DWORD dwcbPublicKeyOrToken = GetPublicKeyTokenBLOB().GetSize(); _ASSERTE(pbPublicKeyOrToken != NULL); dwHash ^= HashBytes(pbPublicKeyOrToken, dwcbPublicKeyOrToken); dwHash = _rotl(dwHash, 4); } if (AssemblyIdentity::Have(dwUseIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_VERSION)) { AssemblyVersion *pAssemblyVersion = GetVersion(); dwHash ^= pAssemblyVersion->GetMajor(); dwHash = _rotl(dwHash, 8); dwHash ^= pAssemblyVersion->GetMinor(); dwHash = _rotl(dwHash, 8); dwHash ^= pAssemblyVersion->GetBuild(); dwHash = _rotl(dwHash, 8); dwHash ^= pAssemblyVersion->GetRevision(); dwHash = _rotl(dwHash, 8); } if (AssemblyIdentity::Have(dwUseIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_CULTURE)) { dwHash ^= static_cast(GetNormalizedCulture().HashCaseInsensitive()); dwHash = _rotl(dwHash, 4); } if (AssemblyIdentity::Have(dwUseIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE)) { dwHash ^= 1; dwHash = _rotl(dwHash, 4); } if (AssemblyIdentity::Have(dwUseIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE)) { dwHash ^= static_cast(GetArchitecture()); dwHash = _rotl(dwHash, 4); } if (AssemblyIdentity::Have(dwUseIdentityFlags, AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE)) { dwHash ^= static_cast(GetContentType()); dwHash = _rotl(dwHash, 4); } return static_cast(dwHash); } BOOL AssemblyName::Equals(AssemblyName *pAssemblyName, DWORD dwIncludeFlags) { BOOL fEquals = FALSE; if (GetContentType() == AssemblyContentType_WindowsRuntime) { // Assembly is meaningless for WinRT, all assemblies form one joint type namespace return (GetContentType() == pAssemblyName->GetContentType()); } if (GetSimpleName().EqualsCaseInsensitive(pAssemblyName->GetSimpleName()) && (GetContentType() == pAssemblyName->GetContentType())) { fEquals = TRUE; if ((dwIncludeFlags & EXCLUDE_CULTURE) == 0) { fEquals = GetNormalizedCulture().EqualsCaseInsensitive(pAssemblyName->GetNormalizedCulture()); } if (fEquals && (dwIncludeFlags & INCLUDE_PUBLIC_KEY_TOKEN) != 0) { fEquals = (GetPublicKeyTokenBLOB().Equals(pAssemblyName->GetPublicKeyTokenBLOB())); } if (fEquals && ((dwIncludeFlags & INCLUDE_ARCHITECTURE) != 0)) { fEquals = (GetArchitecture() == pAssemblyName->GetArchitecture()); } if (fEquals && ((dwIncludeFlags & INCLUDE_VERSION) != 0)) { fEquals = GetVersion()->Equals(pAssemblyName->GetVersion()); } if (fEquals && ((dwIncludeFlags & INCLUDE_RETARGETABLE) != 0)) { fEquals = (GetIsRetargetable() == pAssemblyName->GetIsRetargetable()); } } return fEquals; } void AssemblyName::GetDisplayName(PathString &displayName, DWORD dwIncludeFlags) { DWORD dwUseIdentityFlags = m_dwIdentityFlags; // Prune unwanted name parts if ((dwIncludeFlags & INCLUDE_VERSION) == 0) { dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_VERSION; } if ((dwIncludeFlags & INCLUDE_ARCHITECTURE) == 0) { dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_PROCESSOR_ARCHITECTURE; } if ((dwIncludeFlags & INCLUDE_RETARGETABLE) == 0) { dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_RETARGETABLE; } if ((dwIncludeFlags & INCLUDE_CONTENT_TYPE) == 0) { dwUseIdentityFlags &= ~AssemblyIdentity::IDENTITY_FLAG_CONTENT_TYPE; } TextualIdentityParser::ToString(this, dwUseIdentityFlags, displayName); } SString &AssemblyName::GetNormalizedCulture() { SString &culture = GetCulture(); if (culture.IsEmpty()) { culture.SetLiteral(s_neutralCulture); } return culture; } }; // namespace BINDER_SPACE