From 99340d470d5fc97292ff06bd20bc6de29fcd8209 Mon Sep 17 00:00:00 2001 From: Andy Gerlicher Date: Wed, 26 Apr 2017 13:04:53 -0700 Subject: [PATCH] Use AssemblyName.Clone when possible. AssemblyName.Clone is not available in early versions of .NET Core. Until we can upgrade MSBuild to use .NET Core 2.0, we can't call the Clone method directly. This change will attempt to call .Clone using reflection and if not available fallback to the previous .NET Core behavior. Potential fix for #2015 (when running on .NET Core 2.0). --- src/Shared/AssemblyNameExtension.cs | 8 +--- src/Shared/AssemblyUtilities.cs | 46 +++++++++++++++++-- src/Tasks/AppConfig/DependentAssembly.cs | 22 ++------- .../AssemblyDependency/ReferenceTable.cs | 19 ++------ 4 files changed, 52 insertions(+), 43 deletions(-) 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..cb166d248e2 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_assemblyNameCloneMethod = typeof(AssemblyName).GetMethod("Clone"); + s_assemblylocationProperty = typeof(Assembly).GetProperty("Location", typeof(string)); + s_initialized = true; + } } } 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.