Skip to content

Commit

Permalink
Rebase over Refactor code to remove TypedObjectFactoryCacheForRuntime…
Browse files Browse the repository at this point in the history
…ClassName #1055 (#1100)
  • Loading branch information
manodasanW authored Feb 7, 2022
1 parent e64e716 commit 8bac003
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 43 deletions.
54 changes: 20 additions & 34 deletions src/WinRT.Runtime/ComWrappersSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ namespace WinRT
#endif
static partial class ComWrappersSupport
{
private readonly static ConcurrentDictionary<string, Func<IInspectable, object>> TypedObjectFactoryCacheForRuntimeClassName = new ConcurrentDictionary<string, Func<IInspectable, object>>(StringComparer.Ordinal);
private readonly static ConcurrentDictionary<Type, Func<IInspectable, object>> TypedObjectFactoryCacheForType = new ConcurrentDictionary<Type, Func<IInspectable, object>>();
private readonly static ConditionalWeakTable<object, object> CCWTable = new ConditionalWeakTable<object, object>();
private readonly static ConcurrentDictionary<Type, Func<IntPtr, object>> DelegateFactoryCache = new ConcurrentDictionary<Type, Func<IntPtr, object>>();
Expand Down Expand Up @@ -338,7 +337,7 @@ private static Func<IInspectable, object> 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<object, IInspectable> _boxedValueReferenceCache = new();
private static readonly ConditionalWeakTable<object, IInspectable> _boxedValueReferenceCache = new();

private static Func<IInspectable, object> CreateReferenceCachingFactory(Func<IInspectable, object> internalFactory)
{
Expand All @@ -365,11 +364,21 @@ private static Func<IInspectable, object> CreateCustomTypeMappingFactory(Type cu

internal static Func<IInspectable, object> 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);
Expand Down Expand Up @@ -406,31 +415,15 @@ internal static Func<IInspectable, object> CreateTypedRcwFactory(Type implementa
return CreateFactoryForImplementationType(runtimeClassName, implementationType);
}

internal static Func<IInspectable, object> 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<String> but Nullable<String> is illegal
if (string.CompareOrdinal(runtimeClassName, "Windows.Foundation.IReference`1<String>") == 0)
{
return CreateReferenceCachingFactory((IInspectable obj) => ABI.System.Nullable_string.GetValue(obj));
}
else if (string.CompareOrdinal(runtimeClassName, "Windows.Foundation.IReference`1<Windows.UI.Xaml.Interop.TypeName>") == 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
Expand All @@ -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<Type, bool> IsIReferenceTypeCache = new ConcurrentDictionary<Type, bool>();
Expand Down
7 changes: 3 additions & 4 deletions src/WinRT.Runtime/ComWrappersSupport.net5.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,6 @@ public static void InitializeComWrappers(ComWrappers wrappers = null)
ComWrappers = wrappers;
}

internal static Func<IInspectable, object> GetTypedRcwFactory(string runtimeClassName) => TypedObjectFactoryCacheForRuntimeClassName.GetOrAdd(runtimeClassName, className => CreateTypedRcwFactory(className));
internal static Func<IInspectable, object> GetTypedRcwFactory(Type implementationType) => TypedObjectFactoryCacheForType.GetOrAdd(implementationType, classType => CreateTypedRcwFactory(classType));

private static Func<IInspectable, object> CreateFactoryForImplementationType(string runtimeClassName, Type implementationType)
Expand Down Expand Up @@ -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)
{
Expand Down
4 changes: 2 additions & 2 deletions src/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ public static T CreateRcwForComObject<T>(IntPtr ptr)
if (identity.TryAs<IInspectable.Vftbl>(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<ABI.WinRT.Interop.IWeakReference.Vftbl>(out var weakRef) == 0)
{
Expand Down
18 changes: 15 additions & 3 deletions src/WinRT.Runtime/TypeNameSupport.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,24 @@ public static (Type type, int remaining) FindTypeByName(ReadOnlySpan<char> 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<String> but Nullable<String> is illegal
else if (runtimeClassName.CompareTo("Windows.Foundation.IReference`1<String>".AsSpan(), StringComparison.Ordinal) == 0)
{
return (typeof(ABI.System.Nullable_string), 0);
}
else if (runtimeClassName.CompareTo("Windows.Foundation.IReference`1<Windows.UI.Xaml.Interop.TypeName>".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);
}

/// <summary>
Expand Down

0 comments on commit 8bac003

Please sign in to comment.