From 8bac003d3f2e3ac2bcdf43bde5783f3fb7186b27 Mon Sep 17 00:00:00 2001 From: Manodasan Wignarajah Date: Mon, 7 Feb 2022 14:13:40 -0800 Subject: [PATCH] Rebase over Refactor code to remove TypedObjectFactoryCacheForRuntimeClassName #1055 (#1100) --- src/WinRT.Runtime/ComWrappersSupport.cs | 54 +++++++------------ src/WinRT.Runtime/ComWrappersSupport.net5.cs | 7 ++- .../ComWrappersSupport.netstandard2.0.cs | 4 +- src/WinRT.Runtime/TypeNameSupport.cs | 18 +++++-- 4 files changed, 40 insertions(+), 43 deletions(-) diff --git a/src/WinRT.Runtime/ComWrappersSupport.cs b/src/WinRT.Runtime/ComWrappersSupport.cs index 44f27c2c2..138feb00a 100644 --- a/src/WinRT.Runtime/ComWrappersSupport.cs +++ b/src/WinRT.Runtime/ComWrappersSupport.cs @@ -29,7 +29,6 @@ namespace WinRT #endif static partial class ComWrappersSupport { - private readonly static ConcurrentDictionary> TypedObjectFactoryCacheForRuntimeClassName = new ConcurrentDictionary>(StringComparer.Ordinal); private readonly static ConcurrentDictionary> TypedObjectFactoryCacheForType = new ConcurrentDictionary>(); private readonly static ConditionalWeakTable CCWTable = new ConditionalWeakTable(); private readonly static ConcurrentDictionary> DelegateFactoryCache = new ConcurrentDictionary>(); @@ -338,7 +337,7 @@ private static Func CreateArrayFactory(Type implementation // This is used to hold the reference to the native value type object (IReference) until the actual value in it (boxed as an object) gets cleaned up by GC // This is done to avoid pointer reuse until GC cleans up the boxed object - private static ConditionalWeakTable _boxedValueReferenceCache = new(); + private static readonly ConditionalWeakTable _boxedValueReferenceCache = new(); private static Func CreateReferenceCachingFactory(Func internalFactory) { @@ -365,11 +364,21 @@ private static Func CreateCustomTypeMappingFactory(Type cu internal static Func CreateTypedRcwFactory(Type implementationType, string runtimeClassName = null) { - if (implementationType == null) + // If runtime class name is empty or "Object", then just use IInspectable. + if (implementationType == null || implementationType == typeof(object)) { // If we reach here, then we couldn't find a type that matches the runtime class name. // Fall back to using IInspectable directly. return (IInspectable obj) => obj; + } + + if (implementationType == typeof(ABI.System.Nullable_string)) + { + return CreateReferenceCachingFactory((IInspectable obj) => ABI.System.Nullable_string.GetValue(obj)); + } + else if (implementationType == typeof(ABI.System.Nullable_Type)) + { + return CreateReferenceCachingFactory((IInspectable obj) => ABI.System.Nullable_Type.GetValue(obj)); } var customHelperType = Projections.FindCustomHelperTypeMapping(implementationType, true); @@ -406,31 +415,15 @@ internal static Func CreateTypedRcwFactory(Type implementa return CreateFactoryForImplementationType(runtimeClassName, implementationType); } - internal static Func CreateTypedRcwFactory(string runtimeClassName) - { - // If runtime class name is empty or "Object", then just use IInspectable. - if (string.IsNullOrEmpty(runtimeClassName) || - string.CompareOrdinal(runtimeClassName, "Object") == 0) - { - return (IInspectable obj) => obj; - } - // PropertySet and ValueSet can return IReference but Nullable is illegal - if (string.CompareOrdinal(runtimeClassName, "Windows.Foundation.IReference`1") == 0) - { - return CreateReferenceCachingFactory((IInspectable obj) => ABI.System.Nullable_string.GetValue(obj)); - } - else if (string.CompareOrdinal(runtimeClassName, "Windows.Foundation.IReference`1") == 0) + internal static Type GetRuntimeClassForTypeCreation(IInspectable inspectable, Type staticallyDeterminedType) + { + string runtimeClassName = inspectable.GetRuntimeClassName(noThrow: true); + Type implementationType = null; + if (!string.IsNullOrEmpty(runtimeClassName)) { - return CreateReferenceCachingFactory((IInspectable obj) => ABI.System.Nullable_Type.GetValue(obj)); + implementationType = TypeNameSupport.FindTypeByNameCached(runtimeClassName); } - Type implementationType = TypeNameSupport.FindTypeByNameCached(runtimeClassName); - return CreateTypedRcwFactory(implementationType, runtimeClassName); - } - - internal static string GetRuntimeClassForTypeCreation(IInspectable inspectable, Type staticallyDeterminedType) - { - string runtimeClassName = inspectable.GetRuntimeClassName(noThrow: true); if (staticallyDeterminedType != null && staticallyDeterminedType != typeof(object)) { // We have a static type which we can use to construct the object. But, we can't just use it for all scenarios @@ -441,23 +434,16 @@ internal static string GetRuntimeClassForTypeCreation(IInspectable inspectable, // be cast to the sub class via as operator even if it is really an instance of it per rutimeclass. // To handle these scenarios, we use the runtimeclass if we find it is assignable to the statically determined type. // If it isn't, we use the statically determined type as it is a tear off. - - Type implementationType = null; - if (!string.IsNullOrEmpty(runtimeClassName)) - { - implementationType = TypeNameSupport.FindTypeByNameCached(runtimeClassName); - } - if (!(implementationType != null && (staticallyDeterminedType == implementationType || staticallyDeterminedType.IsAssignableFrom(implementationType) || staticallyDeterminedType.IsGenericType && implementationType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == staticallyDeterminedType.GetGenericTypeDefinition())))) { - runtimeClassName = TypeNameSupport.GetNameForType(staticallyDeterminedType, TypeNameGenerationFlags.GenerateBoxedName); + return staticallyDeterminedType; } } - return runtimeClassName; + return implementationType; } private readonly static ConcurrentDictionary IsIReferenceTypeCache = new ConcurrentDictionary(); diff --git a/src/WinRT.Runtime/ComWrappersSupport.net5.cs b/src/WinRT.Runtime/ComWrappersSupport.net5.cs index f039008af..33e2ba119 100644 --- a/src/WinRT.Runtime/ComWrappersSupport.net5.cs +++ b/src/WinRT.Runtime/ComWrappersSupport.net5.cs @@ -217,7 +217,6 @@ public static void InitializeComWrappers(ComWrappers wrappers = null) ComWrappers = wrappers; } - internal static Func GetTypedRcwFactory(string runtimeClassName) => TypedObjectFactoryCacheForRuntimeClassName.GetOrAdd(runtimeClassName, className => CreateTypedRcwFactory(className)); internal static Func GetTypedRcwFactory(Type implementationType) => TypedObjectFactoryCacheForType.GetOrAdd(implementationType, classType => CreateTypedRcwFactory(classType)); private static Func CreateFactoryForImplementationType(string runtimeClassName, Type implementationType) @@ -532,15 +531,15 @@ private static object CreateObject(IntPtr externalComObject) return ComWrappersSupport.GetTypedRcwFactory(ComWrappersSupport.CreateRCWType)(inspectable); } - string runtimeClassName = ComWrappersSupport.GetRuntimeClassForTypeCreation(inspectable, ComWrappersSupport.CreateRCWType); - if (string.IsNullOrEmpty(runtimeClassName)) + Type runtimeClassType = ComWrappersSupport.GetRuntimeClassForTypeCreation(inspectable, ComWrappersSupport.CreateRCWType); + if (runtimeClassType == null) { // If the external IInspectable has not implemented GetRuntimeClassName, // we use the Inspectable wrapper directly. return inspectable; } - return ComWrappersSupport.GetTypedRcwFactory(runtimeClassName)(inspectable); + return ComWrappersSupport.GetTypedRcwFactory(runtimeClassType)(inspectable); } else if (Marshal.QueryInterface(externalComObject, ref weakReferenceIID, out ptr) == 0) { diff --git a/src/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs b/src/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs index 3cc17b1cf..b5264246b 100644 --- a/src/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs +++ b/src/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs @@ -44,8 +44,8 @@ public static T CreateRcwForComObject(IntPtr ptr) if (identity.TryAs(out var inspectableRef) == 0) { var inspectable = new IInspectable(identity); - string runtimeClassName = GetRuntimeClassForTypeCreation(inspectable, typeof(T)); - runtimeWrapper = string.IsNullOrEmpty(runtimeClassName) ? inspectable : TypedObjectFactoryCacheForRuntimeClassName.GetOrAdd(runtimeClassName, className => CreateTypedRcwFactory(className))(inspectable); + Type runtimeClassType = GetRuntimeClassForTypeCreation(inspectable, typeof(T)); + runtimeWrapper = runtimeClassType == null ? inspectable : TypedObjectFactoryCacheForType.GetOrAdd(runtimeClassType, classType => CreateTypedRcwFactory(classType))(inspectable); } else if (identity.TryAs(out var weakRef) == 0) { diff --git a/src/WinRT.Runtime/TypeNameSupport.cs b/src/WinRT.Runtime/TypeNameSupport.cs index fc082604b..889d7009a 100644 --- a/src/WinRT.Runtime/TypeNameSupport.cs +++ b/src/WinRT.Runtime/TypeNameSupport.cs @@ -72,12 +72,24 @@ public static (Type type, int remaining) FindTypeByName(ReadOnlySpan runti { // Assume that anonymous types are expando objects, whether declared 'dynamic' or not. // It may be necessary to detect otherwise and return System.Object. - if(runtimeClassName.StartsWith("<>f__AnonymousType".AsSpan(), StringComparison.Ordinal)) + if (runtimeClassName.StartsWith("<>f__AnonymousType".AsSpan(), StringComparison.Ordinal)) { return (typeof(System.Dynamic.ExpandoObject), 0); + } + // PropertySet and ValueSet can return IReference but Nullable is illegal + else if (runtimeClassName.CompareTo("Windows.Foundation.IReference`1".AsSpan(), StringComparison.Ordinal) == 0) + { + return (typeof(ABI.System.Nullable_string), 0); + } + else if (runtimeClassName.CompareTo("Windows.Foundation.IReference`1".AsSpan(), StringComparison.Ordinal) == 0) + { + return (typeof(ABI.System.Nullable_Type), 0); + } + else + { + var (genericTypeName, genericTypes, remaining) = ParseGenericTypeName(runtimeClassName); + return (FindTypeByNameCore(genericTypeName, genericTypes), remaining); } - var (genericTypeName, genericTypes, remaining) = ParseGenericTypeName(runtimeClassName); - return (FindTypeByNameCore(genericTypeName, genericTypes), remaining); } ///