diff --git a/src/Shared/AssemblyNameExtension.cs b/src/Shared/AssemblyNameExtension.cs index 529335c8bea..482bccd1bba 100644 --- a/src/Shared/AssemblyNameExtension.cs +++ b/src/Shared/AssemblyNameExtension.cs @@ -57,7 +57,7 @@ internal enum PartialComparisonFlags : int /// between the two is done lazily on demand. /// [Serializable] - sealed internal class AssemblyNameExtension + internal sealed class AssemblyNameExtension { private AssemblyName asAssemblyName = null; private string asString = null; @@ -592,11 +592,7 @@ internal AssemblyNameExtension Clone() if (asAssemblyName != null) { -#if FEATURE_ASSEMBLYNAME_CLONE - newExtension.asAssemblyName = (AssemblyName)asAssemblyName.Clone(); -#else - newExtension.asAssemblyName = new AssemblyName(asAssemblyName.FullName); -#endif + newExtension.asAssemblyName = asAssemblyName.CloneIfPossible(); } newExtension.asString = asString; diff --git a/src/Shared/AssemblyUtilities.cs b/src/Shared/AssemblyUtilities.cs index a40a6d46268..51a8aae49ef 100644 --- a/src/Shared/AssemblyUtilities.cs +++ b/src/Shared/AssemblyUtilities.cs @@ -11,6 +11,13 @@ namespace Microsoft.Build.Shared /// internal static class AssemblyUtilities { + // True when the cached method info objects have been set. + private static bool s_initialized; + + // Cached method info + private static MethodInfo s_assemblyNameCloneMethod; + private static PropertyInfo s_assemblylocationProperty; + public static string GetAssemblyLocation(Assembly assembly) { #if FEATURE_ASSEMBLY_LOCATION @@ -18,15 +25,14 @@ public static string GetAssemblyLocation(Assembly assembly) #else // Assembly.Location is only available in .netstandard1.5, but MSBuild needs to target 1.3. // use reflection to access the property + Initialize(); - var locationProperty = assembly.GetType().GetProperty("Location", typeof(string)); - - if (locationProperty == null) + if (s_assemblylocationProperty == null) { throw new NotSupportedException("Type Assembly does not have the Location property"); } - return (string)locationProperty.GetValue(assembly); + return (string)s_assemblylocationProperty.GetValue(assembly); #endif } @@ -39,5 +45,37 @@ public static Type GetTypeInfo(this Type t) return t; } #endif + + public static AssemblyName CloneIfPossible(this AssemblyName assemblyNameToClone) + { +#if FEATURE_ASSEMBLYNAME_CLONE + return (AssemblyName) assemblyNameToClone.Clone(); +#else + + Initialize(); + + if (s_assemblyNameCloneMethod == null) + { + return new AssemblyName(assemblyNameToClone.FullName); + } + + // Try to Invoke the Clone method via reflection. If the method exists (it will on .NET + // Core 2.0 or later) use that result, otherwise use new AssemblyName(FullName). + return (AssemblyName) s_assemblyNameCloneMethod.Invoke(assemblyNameToClone, null) ?? + new AssemblyName(assemblyNameToClone.FullName); +#endif + } + + /// + /// Initialize static fields. Doesn't need to be thread safe. + /// + private static void Initialize() + { + if (s_initialized) return; + + s_initialized = true; + s_assemblyNameCloneMethod = typeof(AssemblyName).GetMethod("Clone"); + s_assemblylocationProperty = typeof(Assembly).GetProperty("Location", typeof(string)); + } } } diff --git a/src/Tasks/AppConfig/DependentAssembly.cs b/src/Tasks/AppConfig/DependentAssembly.cs index 3c70399c50b..c7252c95b3f 100644 --- a/src/Tasks/AppConfig/DependentAssembly.cs +++ b/src/Tasks/AppConfig/DependentAssembly.cs @@ -22,35 +22,23 @@ internal sealed class DependentAssembly private BindingRedirect[] _bindingRedirects = null; /// - /// The partial assemblyname, there should be no version. + /// The partial , there should be no version. /// - private AssemblyName _partialAssemblyName = null; + private AssemblyName _partialAssemblyName; /// - /// The partial assemblyname, there should be no version. + /// The partial , there should be no version. /// internal AssemblyName PartialAssemblyName { set { -#if FEATURE_ASSEMBLYNAME_CLONE - _partialAssemblyName = (AssemblyName)value.Clone(); -#else - _partialAssemblyName = new AssemblyName(value.FullName); -#endif + _partialAssemblyName = value.CloneIfPossible(); _partialAssemblyName.Version = null; } get { - if (_partialAssemblyName == null) - { - return null; - } -#if FEATURE_ASSEMBLYNAME_CLONE - return (AssemblyName)_partialAssemblyName.Clone(); -#else - return new AssemblyName(_partialAssemblyName.FullName); -#endif + return _partialAssemblyName?.CloneIfPossible(); } } diff --git a/src/Tasks/AssemblyDependency/ReferenceTable.cs b/src/Tasks/AssemblyDependency/ReferenceTable.cs index cd4bbc230e3..e6082536856 100644 --- a/src/Tasks/AssemblyDependency/ReferenceTable.cs +++ b/src/Tasks/AssemblyDependency/ReferenceTable.cs @@ -1070,14 +1070,9 @@ IEnumerable preUnificationAssemblyNames { foreach (AssemblyNameExtension preUnificationAssemblyName in preUnificationAssemblyNames) { - string name = preUnificationAssemblyName.Name; // First, unify the assembly name so that we're dealing with the right version. // Not AssemblyNameExtension because we're going to write to it. -#if FEATURE_ASSEMBLYNAME_CLONE - AssemblyNameExtension dependentAssembly = new AssemblyNameExtension((AssemblyName)preUnificationAssemblyName.AssemblyName.Clone()); -#else - AssemblyNameExtension dependentAssembly = new AssemblyNameExtension(new AssemblyName(preUnificationAssemblyName.AssemblyName.FullName)); -#endif + AssemblyNameExtension dependentAssembly = new AssemblyNameExtension(preUnificationAssemblyName.AssemblyName.CloneIfPossible()); Version unifiedVersion; bool isPrerequisite; @@ -1883,11 +1878,7 @@ out AssemblyNameReference[] conflictingReferences byte[] pkt = assemblyName.GetPublicKeyToken(); if (pkt != null && pkt.Length > 0) { -#if FEATURE_ASSEMBLYNAME_CLONE - AssemblyName baseKey = (AssemblyName)assemblyName.AssemblyName.Clone(); -#else - AssemblyName baseKey = new AssemblyName(assemblyName.AssemblyName.FullName); -#endif + AssemblyName baseKey = assemblyName.AssemblyName.CloneIfPossible(); Version version = baseKey.Version; baseKey.Version = null; string key = baseKey.ToString(); @@ -2377,11 +2368,7 @@ out string redistName foreach (DependentAssembly remappedAssembly in _remappedAssemblies) { // First, exclude anything without the simple name match -#if FEATURE_ASSEMBLYNAME_CLONE - AssemblyNameExtension comparisonAssembly = new AssemblyNameExtension((AssemblyName)remappedAssembly.PartialAssemblyName.Clone()); -#else - AssemblyNameExtension comparisonAssembly = new AssemblyNameExtension(new AssemblyName(remappedAssembly.PartialAssemblyName.FullName)); -#endif + AssemblyNameExtension comparisonAssembly = new AssemblyNameExtension(remappedAssembly.PartialAssemblyName.CloneIfPossible()); if (assemblyName.CompareBaseNameTo(comparisonAssembly) == 0) { // Comparison assembly is a partial name. Give it our version.