From e2edea8b217837c2216f32296a596d5b69b66347 Mon Sep 17 00:00:00 2001 From: h3xds1nz Date: Wed, 5 Aug 2026 20:18:53 +0800 Subject: [PATCH] Apply changes from https://github.com/dotnet/wpf/pull/11816 Source-PR: https://github.com/dotnet/wpf/pull/11816 Source-Head-SHA: 9c05eaaa32bff05c00d6819f885b9eed47c4da64 --- .../System/Windows/Markup/ReflectionHelper.cs | 1163 +++++++++-------- 1 file changed, 585 insertions(+), 578 deletions(-) diff --git a/src/Microsoft.DotNet.Wpf/src/Shared/System/Windows/Markup/ReflectionHelper.cs b/src/Microsoft.DotNet.Wpf/src/Shared/System/Windows/Markup/ReflectionHelper.cs index d73b5931f..9774c2287 100644 --- a/src/Microsoft.DotNet.Wpf/src/Shared/System/Windows/Markup/ReflectionHelper.cs +++ b/src/Microsoft.DotNet.Wpf/src/Shared/System/Windows/Markup/ReflectionHelper.cs @@ -1,578 +1,585 @@ -// 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. - -// -// -// -// Description: Specifies that the whitespace surrounding an element should be trimmed. -// - -using System; -using System.IO; -using System.Reflection; -using System.Diagnostics; -using System.Collections; -using System.Collections.Generic; -using System.ComponentModel; -using System.Globalization; -using System.Runtime.CompilerServices; -using MS.Internal; - -#if PBTCOMPILER -using MS.Utility; -namespace MS.Internal.Markup - -#elif WINDOWS_BASE -using MS.Utility; -using MS.Internal.WindowsBase; - -namespace System.Windows.Markup -#else -namespace System.Xaml -#endif -{ - /// - /// Class that provides helper functions for the parser to reflect on types, properties, - /// custom attributes and load assemblies. - /// - internal static class ReflectionHelper - { - // System assembly name used by GetSystemType() to provide reflection - // types at markup compile time through System.Reflection.MetadataLoadContext - private const string SystemReflectionAssemblyName = "System"; - - // MetadataLoadContext core assembly name, also used by GetMscorlibType() to provide - // reflection types at markup compile time - internal const string MscorlibReflectionAssemblyName = "mscorlib"; - -#if PBTCOMPILER - // System.Reflection.MetadataLoadContext instance - private static MetadataLoadContext _metadataLoadContext = null; - - // MetadataLoadContext Assembly cache - private static Dictionary _cachedMetadataLoadContextAssemblies = null; - private static Dictionary _cachedMetadataLoadContextAssembliesByNameNoExtension = null; - - // The local assembly that contains the baml. - private static string _localAssemblyName = string.Empty; - - internal static void Initialize(IEnumerable assemblyPaths) - { - // System.Reflection.MetadataLoadContext Assembly cache - _cachedMetadataLoadContextAssemblies = new Dictionary(StringComparer.OrdinalIgnoreCase); - _cachedMetadataLoadContextAssembliesByNameNoExtension = new Dictionary(StringComparer.OrdinalIgnoreCase); - _metadataLoadContext = new MetadataLoadContext(new PathAssemblyResolver(assemblyPaths), MscorlibReflectionAssemblyName); - _localAssemblyName = string.Empty; - } - - internal static void Dispose() - { - _cachedMetadataLoadContextAssemblies = null; - _cachedMetadataLoadContextAssembliesByNameNoExtension = null; - _localAssemblyName = string.Empty; - - _metadataLoadContext?.Dispose(); - _metadataLoadContext = null; - } -#endif - -#region Type - /// - /// Parse and get the type of the passed-in string. - /// - internal static Type GetQualifiedType(string typeName) - { - // We only parse the assembly name and type name. - // All other Type.GetType() type fragments (version, culture info, public - // key token etc) are ignored. - string[] nameFrags = typeName.Split(new char[] { ',' }, 2); - if (nameFrags.Length == 1) - { - // Treat this as an absolute name. - return Type.GetType(nameFrags[0]); - } - - Assembly a = null; - try - { - a = LoadAssembly(nameFrags[1].TrimStart(), null); - } - catch (Exception e) when (!CriticalExceptions.IsCriticalException(e)) - { - } - - // If we can't load the assembly, just return null. - if (a == null) - { - return null; - } - - try - { - return a.GetType(nameFrags[0]); - } - catch (ArgumentException) - { - } - - // If we can't get the type, just return null. - return null; - } - - internal static bool IsNullableType(Type type) - { - return (type.IsGenericType && (type.GetGenericTypeDefinition() == typeof(Nullable<>))); - } - - internal static bool IsInternalType(Type type) - { - Type origType = type; - Debug.Assert(null != type, "Type passed to IsInternalType is null"); - - // If this is an internal nested type or a parent nested public type, walk up the declaring types. - while (type.IsNestedAssembly || type.IsNestedFamORAssem || (origType != type && type.IsNestedPublic)) - { - type = type.DeclaringType; - } - - // If we're on a non-internal nested type, IsNotPublic & IsPublic will both return false. - // If we were originally on a nested type and have currently reached a parent - // top-level(non nested) type, then it must be top level internal or public type. - return type.IsNotPublic || (origType != type && type.IsPublic); - } - - /// - /// Helper for determine if the type is a public class. - /// - /// Type to check - /// True if type is public - internal static bool IsPublicType(Type type) - { - Debug.Assert(null != type, "Type passed to IsPublicType is null"); - - // If this is a nested internal type, walk up the declaring types. - while (type.IsNestedPublic) - { - type = type.DeclaringType; - } - - // If we're on a non-public nested type, IsPublic will return false. - return type.IsPublic; - } - - // Since assemblies may be loaded in regular load context as well ROL context - // we need to get the ROL type from the real type to compare a type with at compile - // time. At run-time, the same type can be used. - internal static Type GetFrameworkType(string assemblyName, Type type) - { -#if PBTCOMPILER - Assembly reflectionAssembly = LoadAssembly(assemblyName, null); - - if (reflectionAssembly != null) - { - type = reflectionAssembly.GetType(type.FullName); - } - else - { - type = null; - } -#endif - return type; - } - -#if PBTCOMPILER - internal static Type GetMscorlibType(Type type) - { - return GetFrameworkType(MscorlibReflectionAssemblyName, type); - } -#endif - - internal static Type GetSystemType(Type type) - { - return GetFrameworkType(SystemReflectionAssemblyName, type); - } - -#if WINDOWS_BASE - /// - /// Get the type to use for reflection: the custom type, if any, otherwise just the type. - /// - internal static Type GetReflectionType(object item) - { - if (item == null) - return null; - - ICustomTypeProvider ictp = item as ICustomTypeProvider; - if (ictp == null) - return item.GetType(); - else - return ictp.GetCustomType(); - } -#endif - -#endregion Type - - #region Attributes - - internal static string GetTypeConverterAttributeData(Type type, out Type converterType) - { - bool foundTC = false; - return GetCustomAttributeData(type, GetSystemType(typeof(TypeConverterAttribute)), true, ref foundTC, out converterType); - } - - internal static string GetTypeConverterAttributeData(MemberInfo mi, out Type converterType) - { - return GetCustomAttributeData(mi, GetSystemType(typeof(TypeConverterAttribute)), out converterType); - } - - // Given a ReflectionOnlyLoaded member, returns the value of a metadata attribute of - // Type attrType if set on that member. Looks only for attributes that have a ctor with - // one parameter that is of Type string or Type. - private static string GetCustomAttributeData(MemberInfo mi, Type attrType, out Type typeValue) - { - IList list = CustomAttributeData.GetCustomAttributes(mi); - string attrValue = GetCustomAttributeData(list, attrType, out typeValue, true, false); - return attrValue == null ? string.Empty : attrValue; - } - -#if PBTCOMPILER - // Given a ReflectionOnlyLoaded type, returns the value of a metadata attribute of - // Type attrType if set on that type. Looks only for attributes that have a ctor with - // one parameter that is of Type string. - internal static string GetCustomAttributeData(Type t, Type attrType, bool allowZeroArgs) - { - Type typeValue = null; - IList list = CustomAttributeData.GetCustomAttributes(t); - return GetCustomAttributeData(list, attrType, out typeValue, false, allowZeroArgs); - } -#endif - - // Helper that enumerates a list of CustomAttributeData obtained via ReflectionOnlyLoad, and - // looks for a specific attribute of Type attrType. It only looks for attribiutes with a single - // value of Type string that is passed in via a ctor. If allowTypeAlso is true, then it looks for - // values of typeof(Type) as well. - private static string GetCustomAttributeData(IList list, Type attrType, out Type typeValue, bool allowTypeAlso, bool allowZeroArgs) - { - typeValue = null; - string attrValue = null; - for (int j = 0; j < list.Count; j++) - { - attrValue = GetCustomAttributeData(list[j], attrType, out typeValue, allowTypeAlso, false, allowZeroArgs); - if (attrValue != null) - { - break; - } - } - - return attrValue; - } - - // Special version of type-based GetCustomAttributeData that does two - // additional tasks: - // 1) Retrieves the attributes even if it's defined on a base type, and - // 2) Distinguishes between "attribute found and said null" and - // "no attribute found at all" via the ref bool. - internal static string GetCustomAttributeData(Type t, - Type attrType, - bool allowTypeAlso, - ref bool attributeDataFound, - out Type typeValue) - { - typeValue = null; - attributeDataFound = false; - Type currentType = t; - string attributeDataString = null; - CustomAttributeData cad; - - while (currentType != null && !attributeDataFound) - { - IList list = CustomAttributeData.GetCustomAttributes(currentType); - - for (int j = 0; j < list.Count && !attributeDataFound; j++) - { - cad = list[j]; - - if (cad.Constructor.ReflectedType == attrType) - { - attributeDataFound = true; - attributeDataString = GetCustomAttributeData(cad, attrType, out typeValue, allowTypeAlso, false, false); - } - } - - if (!attributeDataFound) - { - currentType = currentType.BaseType; // object.BaseType is null, used as terminating condition for the while() loop. - } - } - - return attributeDataString; - } - - // Helper that inspects a specific CustomAttributeData obtained via ReflectionOnlyLoad, and - // returns its value if the Type of the attribiutes matches the passed in attrType. It only - // looks for attributes with no values or a single value of Type string that is passed in via - // a ctor. If allowTypeAlso is true, then it looks for values of typeof(Type) as well in the - // single value case. If noArgs == false and zeroArgsAllowed = true, that means 0 or 1 args - // are permissible. - private static string GetCustomAttributeData(CustomAttributeData cad, - Type attrType, - out Type typeValue, - bool allowTypeAlso, - bool noArgs, - bool zeroArgsAllowed) - { - string attrValue = null; - typeValue = null; - - // get the Constructor info - ConstructorInfo cinfo = cad.Constructor; - if (cinfo.ReflectedType == attrType) - { - // typedConstructorArguments (the Attribute constructor arguments) - // [MyAttribute("test", Name=Hello)] - // "test" is the Constructor Argument - IList constructorArguments = cad.ConstructorArguments; - if (constructorArguments.Count == 1 && !noArgs) - { - CustomAttributeTypedArgument tca = constructorArguments[0]; - attrValue = tca.Value as String; -#if PBTCOMPILER - if (attrValue == null && allowTypeAlso && tca.ArgumentType == GetMscorlibType(typeof(Type))) -#else - if (attrValue == null && allowTypeAlso && tca.ArgumentType == typeof(Type)) -#endif - { - typeValue = tca.Value as Type; - attrValue = typeValue.AssemblyQualifiedName; - } - - if (attrValue == null) - { - throw new ArgumentException(SR.Format(SR.ParserAttributeArgsLow, attrType.Name)); - } - } - else if (constructorArguments.Count == 0) - { - // zeroArgsAllowed = true for CPA for example. - // CPA with no args is valid and would mean that this type is overriding a base CPA - if (noArgs || zeroArgsAllowed) - { - attrValue = string.Empty; - } - else - { - throw new ArgumentException(SR.Format(SR.ParserAttributeArgsLow, attrType.Name)); - } - } - else - { - throw new ArgumentException(SR.Format(SR.ParserAttributeArgsHigh, attrType.Name)); - } - } - - return attrValue; - } - -#endregion Attributes - -#region Assembly Loading - -#if !PBTCOMPILER - // - // Clean up the cache entry for the given assembly, so that it can be reloaded. - // - internal static void ResetCacheForAssembly(string assemblyName) - { - string assemblyNameLookup = assemblyName.ToUpper(CultureInfo.InvariantCulture); - _loadedAssembliesHash[assemblyNameLookup] = null; - } -#endif - - internal static Assembly LoadAssembly(string assemblyName, string assemblyPath) - { -#if PBTCOMPILER - return ReflectionOnlyLoadAssembly(assemblyName, assemblyPath); -#else - return LoadAssemblyHelper(assemblyName, assemblyPath); -#endif - } - -#if !PBTCOMPILER - internal static Assembly GetAlreadyLoadedAssembly(string assemblyNameLookup) - { - return (Assembly)_loadedAssembliesHash[assemblyNameLookup]; - } - - // Loads the Assembly with the specified name at the specified optional location. - // - // assemblyName is either short name or full name. - // assemblyPath is either full file path or null. - // - private static Assembly LoadAssemblyHelper(string assemblyGivenName, string assemblyPath) - { - AssemblyName assemblyName = new AssemblyName(assemblyGivenName); - string assemblyShortName = assemblyName.Name; - assemblyShortName = assemblyShortName.ToUpper(CultureInfo.InvariantCulture); - - // Check if the assembly has already been loaded. - Assembly retassem = (Assembly)_loadedAssembliesHash[assemblyShortName]; - - if (retassem != null) - { - if (assemblyName.Version != null) - { - AssemblyName cachedName = new AssemblyName(retassem.FullName); - if (!AssemblyName.ReferenceMatchesDefinition(assemblyName, cachedName)) - { - string request = assemblyName.ToString(); - string found = cachedName.ToString(); - throw new InvalidOperationException(SR.Format(SR.ParserAssemblyLoadVersionMismatch, request, found)); - } - } - } - else - { - // Check if the current AppDomain has this assembly loaded for some other reason. - // If so, then just use that assembly and don't attempt to load another copy of it. - // Only do this if no path is provided. - if (String.IsNullOrEmpty(assemblyPath)) - retassem = SafeSecurityHelper.GetLoadedAssembly(assemblyName); - - if (retassem == null) - { - if (!String.IsNullOrEmpty(assemblyPath)) - { - // assemblyPath is set, Load the assembly from this specified place. - // the path must be full file path which contains directory, file name and extension. - Debug.Assert(!assemblyPath.EndsWith(string.Empty + Path.DirectorySeparatorChar, StringComparison.Ordinal), "the assembly path should be a full file path containing file extension"); - - // LoadFile will only override your request only if it is in the GAC - retassem = Assembly.LoadFile(assemblyPath); - } - // - // At compile time, the build task should always pass the full path of the referenced assembly, even if it - // comes from GAC. But below code snippet can run if parser wants to try loading an assembly w/o a path. - // This also makes run-time assembly load consistent with compile-time semantics. - else - { - try - { - retassem = Assembly.Load(assemblyGivenName); - } - catch (System.IO.FileNotFoundException) - { - // This may be a locally defined assembly that has not been created yet. - // To support these cases, just set a null assembly and return. This - // will fail downstream if it really was an assembly miss. - retassem = null; - } - } - } - - // Cache the assembly - if (retassem != null) - { - _loadedAssembliesHash[assemblyShortName] = retassem; - } - } - - return retassem; - } - - private static Hashtable _loadedAssembliesHash = new Hashtable(8); -#else - // returns true is sourceAssembly declares LocalAssemblyName as a friend - internal static bool IsFriendAssembly(Assembly sourceAssembly) - { - bool isFriend = false; - Type typeValue = null; - - string friendAssemblyName = string.Empty; - IList list = CustomAttributeData.GetCustomAttributes(sourceAssembly); - - for (int j = 0; j < list.Count; j++) - { - friendAssemblyName = GetCustomAttributeData(list[j], GetMscorlibType(typeof(InternalsVisibleToAttribute)), out typeValue, false, false, false); - if (friendAssemblyName != null && friendAssemblyName == LocalAssemblyName) - { - isFriend = true; - break; - } - } - - return isFriend; - } - -#if PBTCOMPILER - internal static bool IsInternalAllowedOnType(Type type) - { - return ((LocalAssemblyName == type.Assembly.GetName().Name) || IsFriendAssembly(type.Assembly)); - } -#endif - - // The local assembly that contains the baml. - internal static string LocalAssemblyName - { - get { return _localAssemblyName; } - set { _localAssemblyName = value; } - } - - internal static bool HasAlreadyReflectionOnlyLoaded(string assemblyNameLookup) - { - return GetAlreadyReflectionOnlyLoadedAssembly(assemblyNameLookup) != null; - } - - internal static Assembly GetAlreadyReflectionOnlyLoadedAssembly(string assemblyNameLookup) - { - Assembly assembly = null; - _cachedMetadataLoadContextAssembliesByNameNoExtension.TryGetValue(assemblyNameLookup, out assembly); - - return assembly; - } - - // - // For a given assembly name and its full path, Reflection-Only load the assembly directly - // from the file in disk or load the file to memory and then create assembly instance from - // memory buffer data. - // - private static Assembly ReflectionOnlyLoadAssembly(string assemblyName, string fullPathToAssembly) - { - Assembly assembly = null; - - // If the assembly path is empty, try to load assembly by name. LoadFromAssemblyName - // will result in a MetadataLoadContext.Resolve event that will contain more information about the - // requested assembly. - if (String.IsNullOrEmpty(fullPathToAssembly)) - { - return _metadataLoadContext.LoadFromAssemblyName(assemblyName); - } - else if (_cachedMetadataLoadContextAssemblies.TryGetValue(fullPathToAssembly, out assembly)) - { - return assembly; - } - else if (!String.IsNullOrEmpty(assemblyName) && _cachedMetadataLoadContextAssemblies.TryGetValue(assemblyName, out assembly)) - { - return assembly; - } - else - { - assembly = _metadataLoadContext.LoadFromAssemblyPath(fullPathToAssembly); - } - - // Add the assembly to the cache. ReflectionHelper.ReflectionOnlyLoadAssembly - // receives frequent calls requesting the same assembly. - if (assembly != null && fullPathToAssembly != null) - { - _cachedMetadataLoadContextAssemblies.Add(fullPathToAssembly, assembly); - _cachedMetadataLoadContextAssembliesByNameNoExtension.Add(Path.GetFileNameWithoutExtension(fullPathToAssembly), assembly); - } - - return assembly; - } - -#endif - - #endregion Assembly Loading - } -} - +// 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. + +// +// +// +// Description: Specifies that the whitespace surrounding an element should be trimmed. +// + +using System; +using System.IO; +using System.Reflection; +using System.Diagnostics; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Runtime.CompilerServices; +using MS.Internal; + +#if PBTCOMPILER +using MS.Utility; +namespace MS.Internal.Markup + +#elif WINDOWS_BASE +using MS.Utility; +using MS.Internal.WindowsBase; + +namespace System.Windows.Markup +#else +namespace System.Xaml +#endif +{ + /// + /// Class that provides helper functions for the parser to reflect on types, properties, + /// custom attributes and load assemblies. + /// + internal static class ReflectionHelper + { + // System assembly name used by GetSystemType() to provide reflection + // types at markup compile time through System.Reflection.MetadataLoadContext + private const string SystemReflectionAssemblyName = "System"; + + // MetadataLoadContext core assembly name, also used by GetMscorlibType() to provide + // reflection types at markup compile time + internal const string MscorlibReflectionAssemblyName = "mscorlib"; + +#if PBTCOMPILER + // System.Reflection.MetadataLoadContext instance + private static MetadataLoadContext _metadataLoadContext = null; + + // MetadataLoadContext Assembly cache + private static Dictionary _cachedMetadataLoadContextAssemblies = null; + private static Dictionary _cachedMetadataLoadContextAssembliesByNameNoExtension = null; + + // The local assembly that contains the baml. + private static string _localAssemblyName = string.Empty; + + internal static void Initialize(IEnumerable assemblyPaths) + { + // System.Reflection.MetadataLoadContext Assembly cache + _cachedMetadataLoadContextAssemblies = new Dictionary(StringComparer.OrdinalIgnoreCase); + _cachedMetadataLoadContextAssembliesByNameNoExtension = new Dictionary(StringComparer.OrdinalIgnoreCase); + _metadataLoadContext = new MetadataLoadContext(new PathAssemblyResolver(assemblyPaths), MscorlibReflectionAssemblyName); + _localAssemblyName = string.Empty; + } + + internal static void Dispose() + { + _cachedMetadataLoadContextAssemblies = null; + _cachedMetadataLoadContextAssembliesByNameNoExtension = null; + _localAssemblyName = string.Empty; + + _metadataLoadContext?.Dispose(); + _metadataLoadContext = null; + } +#endif + +#region Type + /// + /// Parse and get the type of the passed-in string. + /// + internal static Type GetQualifiedType(string typeName) + { + // We only parse the assembly name and type name. + // All other Type.GetType() type fragments (version, culture info, public + // key token etc) are ignored. + string[] nameFrags = typeName.Split(new char[] { ',' }, 2); + if (nameFrags.Length == 1) + { + // Treat this as an absolute name. + return Type.GetType(nameFrags[0]); + } + + Assembly a = null; + try + { + a = LoadAssembly(nameFrags[1].TrimStart(), null); + } + catch (Exception e) when (!CriticalExceptions.IsCriticalException(e)) + { + } + + // If we can't load the assembly, just return null. + if (a == null) + { + return null; + } + + try + { + return a.GetType(nameFrags[0]); + } + catch (ArgumentException) + { + } + + // If we can't get the type, just return null. + return null; + } + + internal static bool IsNullableType(Type type) + { +#if NET11_0_OR_GREATER + return type.GetNullableUnderlyingType() is not null; +#else + Type nullableType = _metadataLoadContext.CoreAssembly?.GetType("System.Nullable`1"); + Debug.Assert(nullableType is not null, "System.Nullable`1 type not found in MetadataLoadContext"); + + return type.IsGenericType && nullableType == type.GetGenericTypeDefinition(); +#endif + } + + internal static bool IsInternalType(Type type) + { + Type origType = type; + Debug.Assert(null != type, "Type passed to IsInternalType is null"); + + // If this is an internal nested type or a parent nested public type, walk up the declaring types. + while (type.IsNestedAssembly || type.IsNestedFamORAssem || (origType != type && type.IsNestedPublic)) + { + type = type.DeclaringType; + } + + // If we're on a non-internal nested type, IsNotPublic & IsPublic will both return false. + // If we were originally on a nested type and have currently reached a parent + // top-level(non nested) type, then it must be top level internal or public type. + return type.IsNotPublic || (origType != type && type.IsPublic); + } + + /// + /// Helper for determine if the type is a public class. + /// + /// Type to check + /// True if type is public + internal static bool IsPublicType(Type type) + { + Debug.Assert(null != type, "Type passed to IsPublicType is null"); + + // If this is a nested internal type, walk up the declaring types. + while (type.IsNestedPublic) + { + type = type.DeclaringType; + } + + // If we're on a non-public nested type, IsPublic will return false. + return type.IsPublic; + } + + // Since assemblies may be loaded in regular load context as well ROL context + // we need to get the ROL type from the real type to compare a type with at compile + // time. At run-time, the same type can be used. + internal static Type GetFrameworkType(string assemblyName, Type type) + { +#if PBTCOMPILER + Assembly reflectionAssembly = LoadAssembly(assemblyName, null); + + if (reflectionAssembly != null) + { + type = reflectionAssembly.GetType(type.FullName); + } + else + { + type = null; + } +#endif + return type; + } + +#if PBTCOMPILER + internal static Type GetMscorlibType(Type type) + { + return GetFrameworkType(MscorlibReflectionAssemblyName, type); + } +#endif + + internal static Type GetSystemType(Type type) + { + return GetFrameworkType(SystemReflectionAssemblyName, type); + } + +#if WINDOWS_BASE + /// + /// Get the type to use for reflection: the custom type, if any, otherwise just the type. + /// + internal static Type GetReflectionType(object item) + { + if (item == null) + return null; + + ICustomTypeProvider ictp = item as ICustomTypeProvider; + if (ictp == null) + return item.GetType(); + else + return ictp.GetCustomType(); + } +#endif + +#endregion Type + + #region Attributes + + internal static string GetTypeConverterAttributeData(Type type, out Type converterType) + { + bool foundTC = false; + return GetCustomAttributeData(type, GetSystemType(typeof(TypeConverterAttribute)), true, ref foundTC, out converterType); + } + + internal static string GetTypeConverterAttributeData(MemberInfo mi, out Type converterType) + { + return GetCustomAttributeData(mi, GetSystemType(typeof(TypeConverterAttribute)), out converterType); + } + + // Given a ReflectionOnlyLoaded member, returns the value of a metadata attribute of + // Type attrType if set on that member. Looks only for attributes that have a ctor with + // one parameter that is of Type string or Type. + private static string GetCustomAttributeData(MemberInfo mi, Type attrType, out Type typeValue) + { + IList list = CustomAttributeData.GetCustomAttributes(mi); + string attrValue = GetCustomAttributeData(list, attrType, out typeValue, true, false); + return attrValue == null ? string.Empty : attrValue; + } + +#if PBTCOMPILER + // Given a ReflectionOnlyLoaded type, returns the value of a metadata attribute of + // Type attrType if set on that type. Looks only for attributes that have a ctor with + // one parameter that is of Type string. + internal static string GetCustomAttributeData(Type t, Type attrType, bool allowZeroArgs) + { + Type typeValue = null; + IList list = CustomAttributeData.GetCustomAttributes(t); + return GetCustomAttributeData(list, attrType, out typeValue, false, allowZeroArgs); + } +#endif + + // Helper that enumerates a list of CustomAttributeData obtained via ReflectionOnlyLoad, and + // looks for a specific attribute of Type attrType. It only looks for attribiutes with a single + // value of Type string that is passed in via a ctor. If allowTypeAlso is true, then it looks for + // values of typeof(Type) as well. + private static string GetCustomAttributeData(IList list, Type attrType, out Type typeValue, bool allowTypeAlso, bool allowZeroArgs) + { + typeValue = null; + string attrValue = null; + for (int j = 0; j < list.Count; j++) + { + attrValue = GetCustomAttributeData(list[j], attrType, out typeValue, allowTypeAlso, false, allowZeroArgs); + if (attrValue != null) + { + break; + } + } + + return attrValue; + } + + // Special version of type-based GetCustomAttributeData that does two + // additional tasks: + // 1) Retrieves the attributes even if it's defined on a base type, and + // 2) Distinguishes between "attribute found and said null" and + // "no attribute found at all" via the ref bool. + internal static string GetCustomAttributeData(Type t, + Type attrType, + bool allowTypeAlso, + ref bool attributeDataFound, + out Type typeValue) + { + typeValue = null; + attributeDataFound = false; + Type currentType = t; + string attributeDataString = null; + CustomAttributeData cad; + + while (currentType != null && !attributeDataFound) + { + IList list = CustomAttributeData.GetCustomAttributes(currentType); + + for (int j = 0; j < list.Count && !attributeDataFound; j++) + { + cad = list[j]; + + if (cad.Constructor.ReflectedType == attrType) + { + attributeDataFound = true; + attributeDataString = GetCustomAttributeData(cad, attrType, out typeValue, allowTypeAlso, false, false); + } + } + + if (!attributeDataFound) + { + currentType = currentType.BaseType; // object.BaseType is null, used as terminating condition for the while() loop. + } + } + + return attributeDataString; + } + + // Helper that inspects a specific CustomAttributeData obtained via ReflectionOnlyLoad, and + // returns its value if the Type of the attribiutes matches the passed in attrType. It only + // looks for attributes with no values or a single value of Type string that is passed in via + // a ctor. If allowTypeAlso is true, then it looks for values of typeof(Type) as well in the + // single value case. If noArgs == false and zeroArgsAllowed = true, that means 0 or 1 args + // are permissible. + private static string GetCustomAttributeData(CustomAttributeData cad, + Type attrType, + out Type typeValue, + bool allowTypeAlso, + bool noArgs, + bool zeroArgsAllowed) + { + string attrValue = null; + typeValue = null; + + // get the Constructor info + ConstructorInfo cinfo = cad.Constructor; + if (cinfo.ReflectedType == attrType) + { + // typedConstructorArguments (the Attribute constructor arguments) + // [MyAttribute("test", Name=Hello)] + // "test" is the Constructor Argument + IList constructorArguments = cad.ConstructorArguments; + if (constructorArguments.Count == 1 && !noArgs) + { + CustomAttributeTypedArgument tca = constructorArguments[0]; + attrValue = tca.Value as String; +#if PBTCOMPILER + if (attrValue == null && allowTypeAlso && tca.ArgumentType == GetMscorlibType(typeof(Type))) +#else + if (attrValue == null && allowTypeAlso && tca.ArgumentType == typeof(Type)) +#endif + { + typeValue = tca.Value as Type; + attrValue = typeValue.AssemblyQualifiedName; + } + + if (attrValue == null) + { + throw new ArgumentException(SR.Format(SR.ParserAttributeArgsLow, attrType.Name)); + } + } + else if (constructorArguments.Count == 0) + { + // zeroArgsAllowed = true for CPA for example. + // CPA with no args is valid and would mean that this type is overriding a base CPA + if (noArgs || zeroArgsAllowed) + { + attrValue = string.Empty; + } + else + { + throw new ArgumentException(SR.Format(SR.ParserAttributeArgsLow, attrType.Name)); + } + } + else + { + throw new ArgumentException(SR.Format(SR.ParserAttributeArgsHigh, attrType.Name)); + } + } + + return attrValue; + } + +#endregion Attributes + +#region Assembly Loading + +#if !PBTCOMPILER + // + // Clean up the cache entry for the given assembly, so that it can be reloaded. + // + internal static void ResetCacheForAssembly(string assemblyName) + { + string assemblyNameLookup = assemblyName.ToUpper(CultureInfo.InvariantCulture); + _loadedAssembliesHash[assemblyNameLookup] = null; + } +#endif + + internal static Assembly LoadAssembly(string assemblyName, string assemblyPath) + { +#if PBTCOMPILER + return ReflectionOnlyLoadAssembly(assemblyName, assemblyPath); +#else + return LoadAssemblyHelper(assemblyName, assemblyPath); +#endif + } + +#if !PBTCOMPILER + internal static Assembly GetAlreadyLoadedAssembly(string assemblyNameLookup) + { + return (Assembly)_loadedAssembliesHash[assemblyNameLookup]; + } + + // Loads the Assembly with the specified name at the specified optional location. + // + // assemblyName is either short name or full name. + // assemblyPath is either full file path or null. + // + private static Assembly LoadAssemblyHelper(string assemblyGivenName, string assemblyPath) + { + AssemblyName assemblyName = new AssemblyName(assemblyGivenName); + string assemblyShortName = assemblyName.Name; + assemblyShortName = assemblyShortName.ToUpper(CultureInfo.InvariantCulture); + + // Check if the assembly has already been loaded. + Assembly retassem = (Assembly)_loadedAssembliesHash[assemblyShortName]; + + if (retassem != null) + { + if (assemblyName.Version != null) + { + AssemblyName cachedName = new AssemblyName(retassem.FullName); + if (!AssemblyName.ReferenceMatchesDefinition(assemblyName, cachedName)) + { + string request = assemblyName.ToString(); + string found = cachedName.ToString(); + throw new InvalidOperationException(SR.Format(SR.ParserAssemblyLoadVersionMismatch, request, found)); + } + } + } + else + { + // Check if the current AppDomain has this assembly loaded for some other reason. + // If so, then just use that assembly and don't attempt to load another copy of it. + // Only do this if no path is provided. + if (String.IsNullOrEmpty(assemblyPath)) + retassem = SafeSecurityHelper.GetLoadedAssembly(assemblyName); + + if (retassem == null) + { + if (!String.IsNullOrEmpty(assemblyPath)) + { + // assemblyPath is set, Load the assembly from this specified place. + // the path must be full file path which contains directory, file name and extension. + Debug.Assert(!assemblyPath.EndsWith(string.Empty + Path.DirectorySeparatorChar, StringComparison.Ordinal), "the assembly path should be a full file path containing file extension"); + + // LoadFile will only override your request only if it is in the GAC + retassem = Assembly.LoadFile(assemblyPath); + } + // + // At compile time, the build task should always pass the full path of the referenced assembly, even if it + // comes from GAC. But below code snippet can run if parser wants to try loading an assembly w/o a path. + // This also makes run-time assembly load consistent with compile-time semantics. + else + { + try + { + retassem = Assembly.Load(assemblyGivenName); + } + catch (System.IO.FileNotFoundException) + { + // This may be a locally defined assembly that has not been created yet. + // To support these cases, just set a null assembly and return. This + // will fail downstream if it really was an assembly miss. + retassem = null; + } + } + } + + // Cache the assembly + if (retassem != null) + { + _loadedAssembliesHash[assemblyShortName] = retassem; + } + } + + return retassem; + } + + private static Hashtable _loadedAssembliesHash = new Hashtable(8); +#else + // returns true is sourceAssembly declares LocalAssemblyName as a friend + internal static bool IsFriendAssembly(Assembly sourceAssembly) + { + bool isFriend = false; + Type typeValue = null; + + string friendAssemblyName = string.Empty; + IList list = CustomAttributeData.GetCustomAttributes(sourceAssembly); + + for (int j = 0; j < list.Count; j++) + { + friendAssemblyName = GetCustomAttributeData(list[j], GetMscorlibType(typeof(InternalsVisibleToAttribute)), out typeValue, false, false, false); + if (friendAssemblyName != null && friendAssemblyName == LocalAssemblyName) + { + isFriend = true; + break; + } + } + + return isFriend; + } + +#if PBTCOMPILER + internal static bool IsInternalAllowedOnType(Type type) + { + return ((LocalAssemblyName == type.Assembly.GetName().Name) || IsFriendAssembly(type.Assembly)); + } +#endif + + // The local assembly that contains the baml. + internal static string LocalAssemblyName + { + get { return _localAssemblyName; } + set { _localAssemblyName = value; } + } + + internal static bool HasAlreadyReflectionOnlyLoaded(string assemblyNameLookup) + { + return GetAlreadyReflectionOnlyLoadedAssembly(assemblyNameLookup) != null; + } + + internal static Assembly GetAlreadyReflectionOnlyLoadedAssembly(string assemblyNameLookup) + { + Assembly assembly = null; + _cachedMetadataLoadContextAssembliesByNameNoExtension.TryGetValue(assemblyNameLookup, out assembly); + + return assembly; + } + + // + // For a given assembly name and its full path, Reflection-Only load the assembly directly + // from the file in disk or load the file to memory and then create assembly instance from + // memory buffer data. + // + private static Assembly ReflectionOnlyLoadAssembly(string assemblyName, string fullPathToAssembly) + { + Assembly assembly = null; + + // If the assembly path is empty, try to load assembly by name. LoadFromAssemblyName + // will result in a MetadataLoadContext.Resolve event that will contain more information about the + // requested assembly. + if (String.IsNullOrEmpty(fullPathToAssembly)) + { + return _metadataLoadContext.LoadFromAssemblyName(assemblyName); + } + else if (_cachedMetadataLoadContextAssemblies.TryGetValue(fullPathToAssembly, out assembly)) + { + return assembly; + } + else if (!String.IsNullOrEmpty(assemblyName) && _cachedMetadataLoadContextAssemblies.TryGetValue(assemblyName, out assembly)) + { + return assembly; + } + else + { + assembly = _metadataLoadContext.LoadFromAssemblyPath(fullPathToAssembly); + } + + // Add the assembly to the cache. ReflectionHelper.ReflectionOnlyLoadAssembly + // receives frequent calls requesting the same assembly. + if (assembly != null && fullPathToAssembly != null) + { + _cachedMetadataLoadContextAssemblies.Add(fullPathToAssembly, assembly); + _cachedMetadataLoadContextAssembliesByNameNoExtension.Add(Path.GetFileNameWithoutExtension(fullPathToAssembly), assembly); + } + + return assembly; + } + +#endif + + #endregion Assembly Loading + } +} +