diff --git a/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.cpp b/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.cpp index 365fcd59f..0a788a9bd 100644 --- a/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.cpp +++ b/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.cpp @@ -4,4 +4,24 @@ namespace winrt::Alpha::implementation { + template > + Windows::Foundation::Collections::IVectorView single_threaded_vector_view(std::vector&& values = {}) + { + return make>>(std::move(values)); + } + + winrt::Windows::Foundation::Collections::IVector Class::GetStringList() + { + return winrt::single_threaded_vector({ L"alpha", L"beta" }); + } + + winrt::Windows::Foundation::Collections::IVector Class::GetIntList() + { + return winrt::single_threaded_vector({ 4, 3, 2, 1 }); + } + + winrt::Windows::Foundation::Collections::IVectorView Class::GetObjectList() + { + return single_threaded_vector_view({ *this, *this }); + } } diff --git a/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.h b/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.h index c1fe55067..9a43c81af 100644 --- a/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.h +++ b/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.h @@ -7,6 +7,10 @@ namespace winrt::Alpha::implementation struct Class : ClassT { Class() = default; + + winrt::Windows::Foundation::Collections::IVector GetStringList(); + winrt::Windows::Foundation::Collections::IVector GetIntList(); + winrt::Windows::Foundation::Collections::IVectorView GetObjectList(); }; } diff --git a/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.idl b/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.idl index 5f438a79d..dba92fe07 100644 --- a/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.idl +++ b/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.idl @@ -4,6 +4,10 @@ namespace Alpha runtimeclass Class { Class(); + + Windows.Foundation.Collections.IVector GetStringList(); + Windows.Foundation.Collections.IVector GetIntList(); + Windows.Foundation.Collections.IVectorView GetObjectList(); } interface IAlpha diff --git a/src/Samples/TestEmbedded/TestEmbeddedLibrary/TestLib.cs b/src/Samples/TestEmbedded/TestEmbeddedLibrary/TestLib.cs index 652a080f8..908f900c9 100644 --- a/src/Samples/TestEmbedded/TestEmbeddedLibrary/TestLib.cs +++ b/src/Samples/TestEmbedded/TestEmbeddedLibrary/TestLib.cs @@ -69,6 +69,48 @@ public int Test5() { return (int)abuff.Capacity; } + } + + public int Test6() + { + Alpha.Class a = new(); + + int success = 0; + var stringList = a.GetStringList(); + if (stringList.Count == 2) + { + success++; + } + + if (stringList[0] == "alpha" && stringList[1] == "beta") + { + success++; + } + + var intList = a.GetIntList(); + if (intList.Count == 4) + { + success++; + } + + int sum = 0; + foreach (var i in intList) + { + sum += i; + } + + if (sum == 10) + { + success++; + } + + var objList = a.GetObjectList(); + if ((objList[0] == objList[1])) + { + success++; + } + + return success; } } diff --git a/src/Samples/TestEmbedded/UnitTestEmbedded/TestClass.cs b/src/Samples/TestEmbedded/UnitTestEmbedded/TestClass.cs index 2cd3e6696..bd27da89b 100644 --- a/src/Samples/TestEmbedded/UnitTestEmbedded/TestClass.cs +++ b/src/Samples/TestEmbedded/UnitTestEmbedded/TestClass.cs @@ -37,6 +37,12 @@ public void Test4() public void Test5() { Assert.Equal(20, TestLib.Test5()); + } + + [Fact] + public void Test6() + { + Assert.Equal(5, TestLib.Test6()); } } } diff --git a/src/Tests/UnitTest/TestModuleInitializer.cs b/src/Tests/UnitTest/TestModuleInitializer.cs new file mode 100644 index 000000000..ca9453fa5 --- /dev/null +++ b/src/Tests/UnitTest/TestModuleInitializer.cs @@ -0,0 +1,40 @@ +using System; +using WinRT; + +#if !NET + +namespace UnitTest +{ + // In our .NET standard support, we generate most of the delegates needed by generic types as part of the projection. But for the ones + // passed or obtained as an Object, we are not able to statically detect that. On .NET Core, we are able to utilize Expression.GetDelegateType + // to dynamically create one in this case, but on .NET Framework we are not able to do that for ones with pointers in their parameters. + // This tests that scenario where the ABI delegates need to be manually declared and registered by the caller. + internal static class ProjectionTypesInitializer + { + [System.Runtime.CompilerServices.ModuleInitializer] + internal static void InitalizeProjectionTypes() + { + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(ABI.TestComponent.NonBlittable).MakeByRefType(), typeof(int) }, typeof(_get_Value_NonBlittable)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(TestComponentCSharp.EnumValue).MakeByRefType(), typeof(int) }, typeof(_get_Value_EnumValue)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(uint), typeof(ABI.TestComponent.Composable).MakeByRefType(), typeof(int) }, typeof(_get_at_Composable)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(ABI.TestComponent.Composable), typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }, typeof(_index_of_Composable)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(uint), typeof(ABI.TestComponent.Composable), typeof(int) }, typeof(_set_at_Composable)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(ABI.TestComponent.Composable), typeof(int) }, typeof(_append_Composable)); + } + + internal unsafe delegate int _get_Value_NonBlittable(void* thisPtr, out ABI.TestComponent.NonBlittable __return_value__); + internal unsafe delegate int _get_Value_EnumValue(void* thisPtr, out TestComponentCSharp.EnumValue __return_value__); + internal unsafe delegate int _get_at_Composable(void* thisPtr, uint index, out ABI.TestComponent.Composable __return_value__); + internal unsafe delegate int _index_of_Composable(void* thisPtr, ABI.TestComponent.Composable value, out uint index, out byte found); + internal unsafe delegate int _set_at_Composable(void* thisPtr, uint index, ABI.TestComponent.Composable value); + internal unsafe delegate int _append_Composable(void* thisPtr, ABI.TestComponent.Composable value); + } +} + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Method)] + internal sealed class ModuleInitializerAttribute : Attribute { } +} + +#endif diff --git a/src/WinRT.Runtime/FundamentalMarshalers.cs b/src/WinRT.Runtime/FundamentalMarshalers.cs index c173d55b9..25a690ef1 100644 --- a/src/WinRT.Runtime/FundamentalMarshalers.cs +++ b/src/WinRT.Runtime/FundamentalMarshalers.cs @@ -7,28 +7,26 @@ namespace ABI.System { internal struct Boolean { - byte value; public static bool CreateMarshaler(bool value) => value; - public static Boolean GetAbi(bool value) => new Boolean() { value = (byte)(value ? 1 : 0) }; - public static bool FromAbi(Boolean abi) => abi.value != 0; - public static unsafe void CopyAbi(bool value, IntPtr dest) => *(byte*)dest.ToPointer() = GetAbi(value).value; - public static Boolean FromManaged(bool value) => GetAbi(value); - public static unsafe void CopyManaged(bool arg, IntPtr dest) => *(byte*)dest.ToPointer() = FromManaged(arg).value; + public static byte GetAbi(bool value) => (byte)(value ? 1 : 0); + public static bool FromAbi(byte abi) => abi != 0; + public static unsafe void CopyAbi(bool value, IntPtr dest) => *(byte*)dest.ToPointer() = GetAbi(value); + public static byte FromManaged(bool value) => GetAbi(value); + public static unsafe void CopyManaged(bool arg, IntPtr dest) => *(byte*)dest.ToPointer() = FromManaged(arg); public static void DisposeMarshaler(bool m) { } - public static void DisposeAbi(Boolean abi) { } + public static void DisposeAbi(byte abi) { } } internal struct Char { - ushort value; public static char CreateMarshaler(char value) => value; - public static Char GetAbi(char value) => new Char() { value = (ushort)value }; - public static char FromAbi(Char abi) => (char)abi.value; - public static unsafe void CopyAbi(char value, IntPtr dest) => *(ushort*)dest.ToPointer() = GetAbi(value).value; - public static Char FromManaged(char value) => GetAbi(value); - public static unsafe void CopyManaged(char arg, IntPtr dest) => *(ushort*)dest.ToPointer() = FromManaged(arg).value; + public static ushort GetAbi(char value) => (ushort)value; + public static char FromAbi(ushort abi) => (char)abi; + public static unsafe void CopyAbi(char value, IntPtr dest) => *(ushort*)dest.ToPointer() = GetAbi(value); + public static ushort FromManaged(char value) => GetAbi(value); + public static unsafe void CopyManaged(char arg, IntPtr dest) => *(ushort*)dest.ToPointer() = FromManaged(arg); public static void DisposeMarshaler(char m) { } - public static void DisposeAbi(Char abi) { } + public static void DisposeAbi(ushort abi) { } } } diff --git a/src/WinRT.Runtime/Interop/StandardDelegates.cs b/src/WinRT.Runtime/Interop/StandardDelegates.cs index e0027e064..6c1e9d0d3 100644 --- a/src/WinRT.Runtime/Interop/StandardDelegates.cs +++ b/src/WinRT.Runtime/Interop/StandardDelegates.cs @@ -231,4 +231,34 @@ namespace WinRT.Interop public #endif delegate int _remove_EventHandler(IntPtr thisPtr, EventRegistrationToken token); -} + +#if !NET + internal unsafe delegate int _get_Current_IntPtr(void* thisPtr, out IntPtr __return_value__); + internal unsafe delegate int _get_Current_Type(void* thisPtr, out ABI.System.Type __return_value__); + + internal unsafe delegate int _get_At_IntPtr(void* thisPtr, uint index, out IntPtr __return_value__); + internal unsafe delegate int _get_At_Type(void* thisPtr, uint index, out ABI.System.Type __return_value__); + internal unsafe delegate int _index_Of_IntPtr(void* thisPtr, IntPtr value, out uint index, out byte found); + internal unsafe delegate int _index_Of_Type(void* thisPtr, ABI.System.Type value, out uint index, out byte found); + internal unsafe delegate int _set_At_IntPtr(void* thisPtr, uint index, IntPtr value); + internal unsafe delegate int _set_At_Type(void* thisPtr, uint index, ABI.System.Type value); + internal unsafe delegate int _append_IntPtr(void* thisPtr, IntPtr value); + internal unsafe delegate int _append_Type(void* thisPtr, ABI.System.Type value); + + internal unsafe delegate int _lookup_IntPtr_IntPtr(void* thisPtr, IntPtr key, out IntPtr value); + internal unsafe delegate int _lookup_Type_Type(void* thisPtr, ABI.System.Type key, out ABI.System.Type value); + internal unsafe delegate int _lookup_IntPtr_Type(void* thisPtr, IntPtr key, out ABI.System.Type value); + internal unsafe delegate int _lookup_Type_IntPtr(void* thisPtr, ABI.System.Type key, out IntPtr value); + internal unsafe delegate int _has_key_IntPtr(void* thisPtr, IntPtr key, out byte found); + internal unsafe delegate int _has_key_Type(void* thisPtr, ABI.System.Type key, out byte found); + internal unsafe delegate int _insert_IntPtr_IntPtr(void* thisPtr, IntPtr key, IntPtr value, out byte replaced); + internal unsafe delegate int _insert_Type_Type(void* thisPtr, ABI.System.Type key, ABI.System.Type value, out byte replaced); + internal unsafe delegate int _insert_IntPtr_Type(void* thisPtr, IntPtr key, ABI.System.Type value, out byte replaced); + internal unsafe delegate int _insert_Type_IntPtr(void* thisPtr, ABI.System.Type key, IntPtr value, out byte replaced); + + internal unsafe delegate int _invoke_IntPtr_IntPtr(void* thisPtr, IntPtr sender, IntPtr args); + internal unsafe delegate int _invoke_IntPtr_Type(void* thisPtr, IntPtr sender, ABI.System.Type args); + internal unsafe delegate int _invoke_Type_IntPtr(void* thisPtr, ABI.System.Type sender, IntPtr args); + internal unsafe delegate int _invoke_Type_Type(void* thisPtr, ABI.System.Type sender, ABI.System.Type args); +#endif +} \ No newline at end of file diff --git a/src/WinRT.Runtime/Marshalers.cs b/src/WinRT.Runtime/Marshalers.cs index f03556e72..fa7dac4d4 100644 --- a/src/WinRT.Runtime/Marshalers.cs +++ b/src/WinRT.Runtime/Marshalers.cs @@ -1519,13 +1519,24 @@ static Marshaler() } else if (type.IsValueType) { - AbiType = type.FindHelperType(); - if (AbiType != null) + if (type == typeof(bool)) { - // Could still be blittable and the 'ABI.*' type exists for other reasons (e.g. it's a mapped type) - if (AbiType.GetMethod("FromAbi", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static) == null) + AbiType = typeof(byte); + } + else if (type == typeof(char)) + { + AbiType = typeof(ushort); + } + else + { + AbiType = type.FindHelperType(); + if (AbiType != null) { - AbiType = null; + // Could still be blittable and the 'ABI.*' type exists for other reasons (e.g. it's a mapped type) + if (AbiType.GetMethod("FromAbi", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static) == null) + { + AbiType = null; + } } } diff --git a/src/WinRT.Runtime/Projections.cs b/src/WinRT.Runtime/Projections.cs index 549af24b2..38471aa77 100644 --- a/src/WinRT.Runtime/Projections.cs +++ b/src/WinRT.Runtime/Projections.cs @@ -9,6 +9,7 @@ using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Linq.Expressions; using System.Numerics; using System.Reflection; using System.Threading; @@ -541,5 +542,79 @@ internal static bool TryGetMarshalerTypeForProjectedRuntimeClass(IObjectRefer type = null; return false; } + +#if NET + internal static Type GetAbiDelegateType(params Type[] typeArgs) => Expression.GetDelegateType(typeArgs); +#else + private class DelegateTypeComparer : IEqualityComparer + { + public bool Equals(Type[] x, Type[] y) + { + return x.SequenceEqual(y); + } + + public int GetHashCode(Type[] obj) + { + int hashCode = 0; + for (int idx = 0; idx < obj.Length; idx++) + { + hashCode ^= obj[idx].GetHashCode(); + } + return hashCode; + } + } + + private static readonly ConcurrentDictionary abiDelegateCache = new(new DelegateTypeComparer()) + { + // IEnumerable + [new Type[] { typeof(void*), typeof(IntPtr).MakeByRefType(), typeof(int) }] = typeof(Interop._get_Current_IntPtr), + [new Type[] { typeof(void*), typeof(ABI.System.Type).MakeByRefType(), typeof(int) }] = typeof(Interop._get_Current_Type), + // IList / IReadOnlyList + [new Type[] { typeof(void*), typeof(uint), typeof(IntPtr).MakeByRefType(), typeof(int) }] = typeof(Interop._get_At_IntPtr), + [new Type[] { typeof(void*), typeof(uint), typeof(ABI.System.Type).MakeByRefType(), typeof(int) }] = typeof(Interop._get_At_Type), + [new Type[] { typeof(void*), typeof(IntPtr), typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }] = typeof(Interop._index_Of_IntPtr), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }] = typeof(Interop._index_Of_Type), + [new Type[] { typeof(void*), typeof(uint), typeof(IntPtr), typeof(int) }] = typeof(Interop._set_At_IntPtr), + [new Type[] { typeof(void*), typeof(uint), typeof(ABI.System.Type), typeof(int) }] = typeof(Interop._set_At_Type), + [new Type[] { typeof(void*), typeof(IntPtr), typeof(int) }] = typeof(Interop._append_IntPtr), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(int) }] = typeof(Interop._append_Type), + // IDictionary / IReadOnlyDictionary + [new Type[] { typeof(void*), typeof(IntPtr), typeof(IntPtr).MakeByRefType(), typeof(int) }] = typeof(Interop._lookup_IntPtr_IntPtr), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(ABI.System.Type).MakeByRefType(), typeof(int) }] = typeof(Interop._lookup_Type_Type), + [new Type[] { typeof(void*), typeof(IntPtr), typeof(ABI.System.Type).MakeByRefType(), typeof(int) }] = typeof(Interop._lookup_IntPtr_Type), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(IntPtr).MakeByRefType(), typeof(int) }] = typeof(Interop._lookup_Type_IntPtr), + [new Type[] { typeof(void*), typeof(IntPtr), typeof(byte).MakeByRefType(), typeof(int) }] = typeof(Interop._has_key_IntPtr), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(byte).MakeByRefType(), typeof(int) }] = typeof(Interop._has_key_Type), + [new Type[] { typeof(void*), typeof(IntPtr), typeof(IntPtr), typeof(byte).MakeByRefType(), typeof(int) }] = typeof(Interop._insert_IntPtr_IntPtr), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(ABI.System.Type), typeof(byte).MakeByRefType(), typeof(int) }] = typeof(Interop._insert_Type_Type), + [new Type[] { typeof(void*), typeof(IntPtr), typeof(ABI.System.Type), typeof(byte).MakeByRefType(), typeof(int) }] = typeof(Interop._insert_IntPtr_Type), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(IntPtr), typeof(byte).MakeByRefType(), typeof(int) }] = typeof(Interop._insert_Type_IntPtr), + // EventHandler + [new Type[] { typeof(void*), typeof(IntPtr), typeof(IntPtr), typeof(int) }] = typeof(Interop._invoke_IntPtr_IntPtr), + [new Type[] { typeof(void*), typeof(IntPtr), typeof(ABI.System.Type), typeof(int) }] = typeof(Interop._invoke_IntPtr_Type), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(IntPtr), typeof(int) }] = typeof(Interop._invoke_Type_IntPtr), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(ABI.System.Type), typeof(int) }] = typeof(Interop._invoke_Type_Type), + }; + + public static void RegisterAbiDelegate(Type[] delegateSignature, Type delegateType) + { + abiDelegateCache.TryAdd(delegateSignature, delegateType); + } + + // The .NET Standard projection can be used in both .NET Core and .NET Framework scenarios. + // With the latter, using Expression.GetDelegateType to create custom delegates with void* parameters + // doesn't seem to be supported. So we handle that by pregenerating all the ABI delegates that we need + // based on the WinMD and also by allowing apps to register their own if there are any + // that we couldn't detect (i.e. types passed as object in WinMD). + public static Type GetAbiDelegateType(params Type[] typeArgs) + { + if (abiDelegateCache.TryGetValue(typeArgs, out var delegateType)) + { + return delegateType; + } + + return Expression.GetDelegateType(typeArgs); + } +#endif } } \ No newline at end of file diff --git a/src/WinRT.Runtime/Projections/EventHandler.cs b/src/WinRT.Runtime/Projections/EventHandler.cs index dcc08c148..f1a475dcb 100644 --- a/src/WinRT.Runtime/Projections/EventHandler.cs +++ b/src/WinRT.Runtime/Projections/EventHandler.cs @@ -22,7 +22,7 @@ namespace ABI.System static class EventHandler { public static Guid PIID = GuidGenerator.CreateIID(typeof(global::System.EventHandler)); - private static readonly global::System.Type Abi_Invoke_Type = Expression.GetDelegateType(new global::System.Type[] { typeof(void*), typeof(IntPtr), Marshaler.AbiType, typeof(int) }); + private static readonly global::System.Type Abi_Invoke_Type = Projections.GetAbiDelegateType(new global::System.Type[] { typeof(void*), typeof(IntPtr), Marshaler.AbiType, typeof(int) }); private static readonly global::WinRT.Interop.IDelegateVftbl AbiToProjectionVftable; public static readonly IntPtr AbiToProjectionVftablePtr; diff --git a/src/WinRT.Runtime/Projections/IDictionary.netstandard2.0.cs b/src/WinRT.Runtime/Projections/IDictionary.netstandard2.0.cs index 731c92807..c72450c1f 100644 --- a/src/WinRT.Runtime/Projections/IDictionary.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/IDictionary.netstandard2.0.cs @@ -522,10 +522,10 @@ public struct Vftbl public global::System.Delegate Remove_5; public IDictionary_Delegates.Clear_6 Clear_6; public static Guid PIID = GuidGenerator.CreateIID(typeof(IDictionary)); - private static readonly Type Lookup_0_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, Marshaler.AbiType.MakeByRefType(), typeof(int) }); - private static readonly Type HasKey_2_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(byte).MakeByRefType(), typeof(int) }); - private static readonly Type Insert_4_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, Marshaler.AbiType, typeof(byte).MakeByRefType(), typeof(int) }); - private static readonly Type Remove_5_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(int) }); + private static readonly Type Lookup_0_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, Marshaler.AbiType.MakeByRefType(), typeof(int) }); + private static readonly Type HasKey_2_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(byte).MakeByRefType(), typeof(int) }); + private static readonly Type Insert_4_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, Marshaler.AbiType, typeof(byte).MakeByRefType(), typeof(int) }); + private static readonly Type Remove_5_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(int) }); internal unsafe Vftbl(IntPtr thisPtr) { diff --git a/src/WinRT.Runtime/Projections/IEnumerable.netstandard2.0.cs b/src/WinRT.Runtime/Projections/IEnumerable.netstandard2.0.cs index 1c4261256..29b53e963 100644 --- a/src/WinRT.Runtime/Projections/IEnumerable.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/IEnumerable.netstandard2.0.cs @@ -454,7 +454,7 @@ public struct Vftbl public IEnumerator_Delegates.MoveNext_2 MoveNext_2; public IEnumerator_Delegates.GetMany_3 GetMany_3; public static Guid PIID = GuidGenerator.CreateIID(typeof(IEnumerator)); - private static readonly Type get_Current_0_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType.MakeByRefType(), typeof(int) }); + private static readonly Type get_Current_0_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType.MakeByRefType(), typeof(int) }); internal unsafe Vftbl(IntPtr thisPtr) { diff --git a/src/WinRT.Runtime/Projections/IList.netstandard2.0.cs b/src/WinRT.Runtime/Projections/IList.netstandard2.0.cs index c7119443d..c648f65da 100644 --- a/src/WinRT.Runtime/Projections/IList.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/IList.netstandard2.0.cs @@ -476,11 +476,11 @@ public struct Vftbl public IList_Delegates.GetMany_10 GetMany_10; public IList_Delegates.ReplaceAll_11 ReplaceAll_11; public static Guid PIID = GuidGenerator.CreateIID(typeof(IList)); - private static readonly Type GetAt_0_Type = Expression.GetDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType.MakeByRefType(), typeof(int) }); - private static readonly Type IndexOf_3_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }); - private static readonly Type SetAt_4_Type = Expression.GetDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType, typeof(int) }); - private static readonly Type InsertAt_5_Type = Expression.GetDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType, typeof(int) }); - private static readonly Type Append_7_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(int) }); + private static readonly Type GetAt_0_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType.MakeByRefType(), typeof(int) }); + private static readonly Type IndexOf_3_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }); + private static readonly Type SetAt_4_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType, typeof(int) }); + private static readonly Type InsertAt_5_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType, typeof(int) }); + private static readonly Type Append_7_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(int) }); internal unsafe Vftbl(IntPtr thisPtr) { diff --git a/src/WinRT.Runtime/Projections/IReadOnlyDictionary.netstandard2.0.cs b/src/WinRT.Runtime/Projections/IReadOnlyDictionary.netstandard2.0.cs index f36c600f3..a2810097c 100644 --- a/src/WinRT.Runtime/Projections/IReadOnlyDictionary.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/IReadOnlyDictionary.netstandard2.0.cs @@ -516,8 +516,8 @@ public struct Vftbl public global::System.Delegate HasKey_2; public IReadOnlyDictionary_Delegates.Split_3 Split_3; public static Guid PIID = GuidGenerator.CreateIID(typeof(IReadOnlyDictionary)); - private static readonly Type Lookup_0_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, Marshaler.AbiType.MakeByRefType(), typeof(int) }); - private static readonly Type HasKey_2_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(byte).MakeByRefType(), typeof(int) }); + private static readonly Type Lookup_0_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, Marshaler.AbiType.MakeByRefType(), typeof(int) }); + private static readonly Type HasKey_2_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(byte).MakeByRefType(), typeof(int) }); internal unsafe Vftbl(IntPtr thisPtr) { diff --git a/src/WinRT.Runtime/Projections/IReadOnlyList.netstandard2.0.cs b/src/WinRT.Runtime/Projections/IReadOnlyList.netstandard2.0.cs index 264deaa2b..5e557703c 100644 --- a/src/WinRT.Runtime/Projections/IReadOnlyList.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/IReadOnlyList.netstandard2.0.cs @@ -222,8 +222,8 @@ public struct Vftbl public global::System.Delegate IndexOf_2; public IReadOnlyList_Delegates.GetMany_3 GetMany_3; public static Guid PIID = GuidGenerator.CreateIID(typeof(IReadOnlyList)); - private static readonly Type GetAt_0_Type = Expression.GetDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType.MakeByRefType(), typeof(int) }); - private static readonly Type IndexOf_2_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }); + private static readonly Type GetAt_0_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType.MakeByRefType(), typeof(int) }); + private static readonly Type IndexOf_2_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }); internal unsafe Vftbl(IntPtr thisPtr) { diff --git a/src/WinRT.Runtime/Projections/KeyValuePair.cs b/src/WinRT.Runtime/Projections/KeyValuePair.cs index b3831507b..2319aade7 100644 --- a/src/WinRT.Runtime/Projections/KeyValuePair.cs +++ b/src/WinRT.Runtime/Projections/KeyValuePair.cs @@ -131,8 +131,8 @@ public struct Vftbl public global::System.Delegate get_Key_0; public global::System.Delegate get_Value_1; public static Guid PIID = GuidGenerator.CreateIID(typeof(KeyValuePair)); - private static readonly Type get_Key_0_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType.MakeByRefType(), typeof(int) }); - private static readonly Type get_Value_1_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType.MakeByRefType(), typeof(int) }); + private static readonly Type get_Key_0_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType.MakeByRefType(), typeof(int) }); + private static readonly Type get_Value_1_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType.MakeByRefType(), typeof(int) }); internal unsafe Vftbl(IntPtr thisPtr) { diff --git a/src/WinRT.Runtime/Projections/Nullable.cs b/src/WinRT.Runtime/Projections/Nullable.cs index 6bddb5b33..1d191902e 100644 --- a/src/WinRT.Runtime/Projections/Nullable.cs +++ b/src/WinRT.Runtime/Projections/Nullable.cs @@ -113,7 +113,7 @@ public struct Vftbl internal IInspectable.Vftbl IInspectableVftbl; public global::System.Delegate get_Value_0; public static Guid PIID = GuidGenerator.CreateIID(typeof(Nullable)); - public static readonly global::System.Type get_Value_0_Type = Expression.GetDelegateType(new global::System.Type[] { typeof(void*), Marshaler.AbiType.MakeByRefType(), typeof(int) }); + public static readonly global::System.Type get_Value_0_Type = Projections.GetAbiDelegateType(new global::System.Type[] { typeof(void*), Marshaler.AbiType.MakeByRefType(), typeof(int) }); internal unsafe Vftbl(IntPtr thisPtr) { diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 26bc84faf..d990967d9 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -6,6 +6,7 @@ #include #include #include +#include #define INSPECTABLE_METHOD_COUNT 6 @@ -470,8 +471,7 @@ namespace cswinrt { auto eventTypeCode = w.write_temp("%", bind(eventTypeSemantics, typedef_name_type::Projected, false)); std::string eventTypeName = "_EventSource_" + eventTypeCode; - std::regex re(R"-((\ |:|<|>|,|\.))-"); - w.write("%", std::regex_replace(eventTypeName, re, "_")); + w.write("%", escape_type_name_for_identifier(eventTypeName)); } method_signature get_event_invoke_method(TypeDef const& eventType) @@ -1247,8 +1247,7 @@ namespace cswinrt { auto interfaceTypeCode = w.write_temp("%", bind(ifaceTypeSemantics, typedef_name_type::Projected, true)); std::string interfaceTypeName = "_lazy_" + interfaceTypeCode; - std::regex re(R"-((\ |:|<|>|,|\.))-"); - w.write("%", std::regex_replace(interfaceTypeName, re, "_")); + w.write("%", escape_type_name_for_identifier(interfaceTypeName)); } void write_lazy_interface_initialization(writer& w, TypeDef const& type) @@ -2030,8 +2029,7 @@ ComWrappersSupport.RegisterObjectForInterface(this, ThisPtr); { auto objRefTypeCode = w.write_temp("%", bind(ifaceTypeSemantics, typedef_name_type::Projected, true)); std::string objRefTypeName = "_objRef_" + objRefTypeCode; - std::regex re(R"-((\ |:|<|>|,|\.))-"); - w.write("%", std::regex_replace(objRefTypeName, re, "_")); + w.write("%", escape_type_name_for_identifier(objRefTypeName)); } void write_class_objrefs_definition(writer& w, TypeDef const& classType, bool replaceDefaultByInner) @@ -5677,8 +5675,9 @@ IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); { auto generic_abi_types = get_generic_abi_types(w, method_signature{ method }); - w.write("public static readonly Type %_Type = Expression.GetDelegateType(new Type[]{ typeof(void*), %typeof(int) });\n", + w.write("public static readonly Type %_Type = %(new Type[]{ typeof(void*), %typeof(int) });\n", vmethod_name, + settings.netstandard_compat ? "global::WinRT.Projections.GetAbiDelegateType" : "Expression.GetDelegateType", bind_each([&](writer& w, auto&& pair) { w.write("%, ", pair.first); @@ -6881,17 +6880,24 @@ public static Guid PIID = GuidGenerator.CreateIID(typeof(%));)", ); }, [&](writer& w) { - if (!is_generic) + if (!have_generic_params) { if (settings.netstandard_compat) { w.write("private unsafe delegate int Abi_Invoke(%);\n", bind(signature)); } - return; + + // For generic delegates without generic params, we still need Abi_Invoke_Type for the Do_ABI scenario + // due to we can't use delegates from generic types. + if (!is_generic) + { + return; + } } - w.write(R"(private static readonly Type Abi_Invoke_Type = Expression.GetDelegateType(new Type[] { typeof(void*), %typeof(int) }); + w.write(R"(private static readonly Type Abi_Invoke_Type = %(new Type[] { typeof(void*), %typeof(int) }); )", + settings.netstandard_compat ? "global::WinRT.Projections.GetAbiDelegateType" : "Expression.GetDelegateType", bind_each([&](writer& w, auto&& pair) { w.write("%, ", pair.first); @@ -6960,11 +6966,11 @@ public static Guid PIID = GuidGenerator.CreateIID(typeof(%));)", bind_list(", ", signature.params()), bind([&](writer& w) { - if (is_generic || settings.netstandard_compat) + if (have_generic_params || settings.netstandard_compat) { w.write("var abiInvoke = Marshal.GetDelegateForFunctionPointer%(_nativeDelegate.Vftbl.Invoke%);", - is_generic ? "" : "", - is_generic ? ", Abi_Invoke_Type" : ""); + have_generic_params ? "" : "", + have_generic_params ? ", Abi_Invoke_Type" : ""); } else { @@ -6972,7 +6978,7 @@ public static Guid PIID = GuidGenerator.CreateIID(typeof(%));)", bind(signature)); } }), - bind(signature, "abiInvoke", is_generic, false, is_noexcept(method)), + bind(signature, "abiInvoke", have_generic_params, false, is_noexcept(method)), // FromManaged type_name, // DisposeMarshaler @@ -7746,6 +7752,447 @@ bind(invokeMethodSig)); } } + // Checking for if this is an ABI delegate that will need to be code generated or + // whether it is one that we manually already added in Projections.cs such as for + // classes where the ABI type is IntPtr and we can handle generically. + bool is_abi_delegate_required_for_type(type_semantics const& semantics) + { + return call(semantics, + [&](guid_type) + { + return true; + }, + [&](type_definition const& type) + { + switch (get_category(type)) + { + case category::enum_type: + case category::struct_type: + return true; + default: + return false; + } + }, + [&](fundamental_type type) + { + return type != fundamental_type::String; + }, + [](auto) + { + return false; + }); + } + + void add_abi_delegates_for_type(std::string_view typeNamespace, std::string_view typeName, std::vector generics, concurrency::concurrent_unordered_set& abiDelegateEntries) + { + writer w; + if (typeNamespace == "Windows.Foundation" || typeNamespace == "Windows.Foundation.Collections") + { + if (typeName == "IIterator`1") + { + if (is_abi_delegate_required_for_type(generics[0])) + { + auto abiType = w.write_temp("%", bind(generics[0])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_get_Current_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_Current_%(void* thisPtr, out % __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%).MakeByRefType(), typeof(int) }", abiType) + }); + } + } + else if (typeName == "IKeyValuePair`2") + { + if (is_abi_delegate_required_for_type(generics[0])) + { + auto abiType = w.write_temp("%", bind(generics[0])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_get_Key_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_Key_%(void* thisPtr, out % __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%).MakeByRefType(), typeof(int) }", abiType) + }); + } + + if (is_abi_delegate_required_for_type(generics[1])) + { + auto abiType = w.write_temp("%", bind(generics[1])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_get_Value_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_Value_%(void* thisPtr, out % __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%).MakeByRefType(), typeof(int) }", abiType) + }); + } + } + else if (typeName == "IMapView`2") + { + if (is_abi_delegate_required_for_type(generics[0]) || is_abi_delegate_required_for_type(generics[1])) + { + auto keyAbiType = w.write_temp("%", bind(generics[0])); + auto escapedKeyAbiType = escape_type_name_for_identifier(keyAbiType); + + auto valueAbiType = w.write_temp("%", bind(generics[1])); + auto escapedValueAbiType = escape_type_name_for_identifier(valueAbiType); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_lookup_%_%", escapedKeyAbiType, escapedValueAbiType), + w.write_temp("internal unsafe delegate int _lookup_%_%(void* thisPtr, % key, out % value);", escapedKeyAbiType, escapedValueAbiType, keyAbiType, valueAbiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(%).MakeByRefType(), typeof(int) }", keyAbiType, valueAbiType) + }); + + if (is_abi_delegate_required_for_type(generics[0])) + { + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_has_key_%", escapedKeyAbiType), + w.write_temp("internal unsafe delegate int _has_key_%(void* thisPtr, % key, out byte found);", escapedKeyAbiType, keyAbiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(byte).MakeByRefType(), typeof(int) }", keyAbiType) + }); + } + } + } + else if (typeName == "IMap`2") + { + if (is_abi_delegate_required_for_type(generics[0]) || is_abi_delegate_required_for_type(generics[1])) + { + auto keyAbiType = w.write_temp("%", bind(generics[0])); + auto escapedKeyAbiType = escape_type_name_for_identifier(keyAbiType); + + auto valueAbiType = w.write_temp("%", bind(generics[1])); + auto escapedValueAbiType = escape_type_name_for_identifier(valueAbiType); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_lookup_%_%", escapedKeyAbiType, escapedValueAbiType), + w.write_temp("internal unsafe delegate int _lookup_%_%(void* thisPtr, % key, out % value);", escapedKeyAbiType, escapedValueAbiType, keyAbiType, valueAbiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(%).MakeByRefType(), typeof(int) }", keyAbiType, valueAbiType) + }); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_insert_%_%", escapedKeyAbiType, escapedValueAbiType), + w.write_temp("internal unsafe delegate int _insert_%_%(void* thisPtr, % key, % value, out byte replaced);", escapedKeyAbiType, escapedValueAbiType, keyAbiType, valueAbiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(%), typeof(byte).MakeByRefType(), typeof(int) }", keyAbiType, valueAbiType) + }); + + if (is_abi_delegate_required_for_type(generics[0])) + { + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_has_key_%", escapedKeyAbiType), + w.write_temp("internal unsafe delegate int _has_key_%(void* thisPtr, % key, out byte found);", escapedKeyAbiType, keyAbiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(byte).MakeByRefType(), typeof(int) }", keyAbiType) + }); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_remove_%", escapedKeyAbiType), + w.write_temp("internal unsafe delegate int _remove_%(void* thisPtr, % key);", escapedKeyAbiType, keyAbiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(int) }", keyAbiType) + }); + } + } + } + else if (typeName == "IVectorView`1") + { + if (is_abi_delegate_required_for_type(generics[0])) + { + auto abiType = w.write_temp("%", bind(generics[0])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_get_at_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_at_%(void* thisPtr, uint index, out % __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(uint), typeof(%).MakeByRefType(), typeof(int) }", abiType) + }); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_index_of_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _index_of_%(void* thisPtr, % value, out uint index, out byte found);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }", abiType) + }); + + // GetEnumerator in IVectorView + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_get_Current_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_Current_%(void* thisPtr, out % __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%).MakeByRefType(), typeof(int) }", abiType) + }); + } + } + else if (typeName == "IVector`1") + { + if (is_abi_delegate_required_for_type(generics[0])) + { + auto abiType = w.write_temp("%", bind(generics[0])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_get_at_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_at_%(void* thisPtr, uint index, out % __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(uint), typeof(%).MakeByRefType(), typeof(int) }", abiType) + }); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_index_of_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _index_of_%(void* thisPtr, % value, out uint index, out byte found);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }", abiType) + }); + + // SetAt / InsertAt + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_set_at_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _set_at_%(void* thisPtr, uint index, % value);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(uint), typeof(%), typeof(int) }", abiType) + }); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_append_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _append_%(void* thisPtr, % value);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(int) }", abiType) + }); + + // GetEnumerator in IVector + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_get_Current_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_Current_%(void* thisPtr, out % __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%).MakeByRefType(), typeof(int) }", abiType) + }); + } + } + else if (typeName == "EventHandler`1") + { + if (is_abi_delegate_required_for_type(generics[0])) + { + auto abiType = w.write_temp("%", bind(generics[0])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_invoke_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _invoke_%(void* thisPtr, IntPtr sender, % args);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(IntPtr), typeof(%), typeof(int) }", abiType) + }); + } + } + else if (typeName == "IReference`1") + { + if (is_abi_delegate_required_for_type(generics[0])) + { + auto abiType = w.write_temp("%", bind(generics[0])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_get_Value_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_Value_%(void* thisPtr, out % __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%).MakeByRefType(), typeof(int) }", abiType) + }); + } + } + else if (typeName == "IMapChangedEventArgs`1" || + typeName == "IAsyncOperation`1" || + typeName == "IAsyncOperationWithProgress`2") + { + if (is_abi_delegate_required_for_type(generics[0])) + { + auto abiType = w.write_temp("%", bind(generics[0])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_get_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_%(void* thisPtr, out % __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%).MakeByRefType(), typeof(int) }", abiType) + }); + } + + // Add ABI delegates for AsyncOperationProgressHandler as it is referenced in a property of IAsyncOperationWithProgress + // which isn't handled separately. + if (typeName == "IAsyncOperationWithProgress`2" && is_abi_delegate_required_for_type(generics[1])) + { + add_abi_delegates_for_type("Windows.Foundation", "AsyncOperationProgressHandler`2", generics, abiDelegateEntries); + } + } + else if (typeName == "TypedEventHandler`2") + { + if (is_abi_delegate_required_for_type(generics[0]) || is_abi_delegate_required_for_type(generics[1])) + { + auto senderAbiType = w.write_temp("%", bind(generics[0])); + auto escapedSenderAbiType = escape_type_name_for_identifier(senderAbiType); + + auto resultAbiType = w.write_temp("%", bind(generics[1])); + auto escapedResultAbiType = escape_type_name_for_identifier(resultAbiType); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_invoke_%_%", escapedSenderAbiType, escapedResultAbiType), + w.write_temp("internal unsafe delegate int _invoke_%_%(void* thisPtr, % sender, % args);", escapedSenderAbiType, escapedResultAbiType, senderAbiType, resultAbiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(%), typeof(int) }", senderAbiType, resultAbiType) + }); + } + } + else if (typeName == "AsyncOperationProgressHandler`2") + { + if (is_abi_delegate_required_for_type(generics[1])) + { + auto abiType = w.write_temp("%", bind(generics[1])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_invoke_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _invoke_%(void* thisPtr, IntPtr asyncInfo, % progressInfo);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(IntPtr), typeof(%), typeof(int) }", abiType) + }); + } + } + else if (typeName == "AsyncActionProgressHandler`1") + { + if (is_abi_delegate_required_for_type(generics[0])) + { + auto abiType = w.write_temp("%", bind(generics[0])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_invoke_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _invoke_%(void* thisPtr, IntPtr asyncInfo, % progressInfo);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(IntPtr), typeof(%), typeof(int) }", abiType) + }); + } + } + } + } + + void add_if_generic_type_reference(cswinrt::type_semantics const& typeSemantics, bool isArray, concurrency::concurrent_unordered_set& abiDelegateEntries) + { + call(typeSemantics, + [&](generic_type_instance const& type) + { + // Handle generics of generics where their delegate entries should also be added. + for (auto& arg : type.generic_args) + { + // None of the generics should be an array given WinRT doens't support that. + add_if_generic_type_reference(arg, false, abiDelegateEntries); + } + + add_abi_delegates_for_type(type.generic_type.TypeNamespace(), type.generic_type.TypeName(), type.generic_args, abiDelegateEntries); + }, + [&](type_definition const& type) + { + switch (get_category(type)) + { + case category::struct_type: + { + // Struct fields can have IReference generics which we should generate delegates for. + for (auto&& field : type.FieldList()) + { + auto fieldType = field.Signature().Type(); + add_if_generic_type_reference(get_type_semantics(fieldType), fieldType.is_szarray(), abiDelegateEntries); + } + break; + } + } + + // On the CCW for arrays, we also generate an IVector CCW, so we need to generate the appropriate delegates. + if (isArray) + { + std::vector genericArgs = { typeSemantics }; + add_abi_delegates_for_type("Windows.Foundation.Collections", "IVector`1", genericArgs, abiDelegateEntries); + } + }, + [&](auto) + { + // On the CCW for arrays, we also generate an IVector CCW, so we need to generate the appropriate delegates. + if (isArray) + { + std::vector genericArgs = { typeSemantics }; + add_abi_delegates_for_type("Windows.Foundation.Collections", "IVector`1", genericArgs, abiDelegateEntries); + } + } + ); + } + + void add_generic_type_references_in_interface_type(TypeDef const& interfaceType, concurrency::concurrent_unordered_set& abiDelegateEntries) + { + for (auto&& method : interfaceType.MethodList()) + { + if (is_special(method)) + { + continue; + } + + method_signature signature{ method }; + if (signature.has_params()) + { + for (auto&& param : signature.params()) + { + add_if_generic_type_reference(get_type_semantics(param.second->Type()), param.second->Type().is_szarray(), abiDelegateEntries); + } + } + + if (auto& returnSignature = signature.return_signature()) + { + add_if_generic_type_reference(get_type_semantics(returnSignature.Type()), returnSignature.Type().is_szarray(), abiDelegateEntries); + } + } + + for (auto&& prop : interfaceType.PropertyList()) + { + add_if_generic_type_reference(get_type_semantics(prop.Type().Type()), prop.Type().Type().is_szarray(), abiDelegateEntries); + } + + for (auto&& evt : interfaceType.EventList()) + { + add_if_generic_type_reference(get_type_semantics(evt.EventType()), false, abiDelegateEntries); + } + } + + void add_generic_type_references_in_type(TypeDef const& type, concurrency::concurrent_unordered_set& abiDelegateEntries) + { + switch (get_category(type)) + { + case category::delegate_type: + { + method_signature signature{ get_delegate_invoke(type) }; + if (signature.has_params()) + { + for (auto&& param : signature.params()) + { + add_if_generic_type_reference(get_type_semantics(param.second->Type()), param.second->Type().is_szarray(), abiDelegateEntries); + } + } + + if (auto& returnSignature = signature.return_signature()) + { + add_if_generic_type_reference(get_type_semantics(returnSignature.Type()), returnSignature.Type().is_szarray(), abiDelegateEntries); + } + + break; + } + case category::interface_type: + add_generic_type_references_in_interface_type(type, abiDelegateEntries); + break; + } + } + void write_file_header(writer& w) { w.write(R"(//------------------------------------------------------------------------------ diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index 32eeaed58..681fce279 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -1421,4 +1421,34 @@ namespace cswinrt return get_fast_abi_class_for_class(fast_abi_class_type.value()); } + struct generic_abi_delegate + { + std::string abi_delegate_name; + std::string abi_delegate_declaration; + std::string abi_delegate_types; + + // Hash / equality for the hast set this is added to is based on the types in the delegate + // as we do not need duplicate delegate entries from different collection types. + bool operator==(const generic_abi_delegate& entry) const + { + return abi_delegate_types == entry.abi_delegate_types; + } + }; + + std::string escape_type_name_for_identifier(std::string typeName) + { + std::regex re(R"-((\ |:|<|>|,|\.))-"); + return std::regex_replace(typeName, re, "_"); + } +} + +namespace std +{ + template<> + struct hash { + size_t operator()(const cswinrt::generic_abi_delegate& entry) const + { + return hash()(entry.abi_delegate_types); + } + }; } diff --git a/src/cswinrt/main.cpp b/src/cswinrt/main.cpp index e0741217a..55204925d 100644 --- a/src/cswinrt/main.cpp +++ b/src/cswinrt/main.cpp @@ -5,6 +5,7 @@ #include "type_writers.h" #include "code_writers.h" #include +#include namespace cswinrt { @@ -175,10 +176,11 @@ Where is one or more of: task_group group; concurrency::concurrent_unordered_map typeNameToEventDefinitionMap, typeNameToBaseTypeMap; + concurrency::concurrent_unordered_set abiDelegateEntries; bool projectionFileWritten = false; for (auto&& ns_members : c.namespaces()) { - group.add([&ns_members, &componentActivatableClasses, &projectionFileWritten, &typeNameToEventDefinitionMap, &typeNameToBaseTypeMap] + group.add([&ns_members, &componentActivatableClasses, &projectionFileWritten, &typeNameToEventDefinitionMap, &typeNameToBaseTypeMap, &abiDelegateEntries] { auto&& [ns, members] = ns_members; std::string_view currentType = ""; @@ -253,6 +255,7 @@ Where is one or more of: break; } + add_generic_type_references_in_type(type, abiDelegateEntries); written = true; requires_abi = requires_abi || type_requires_abi; } @@ -371,17 +374,63 @@ ComWrappersSupport.RegisterProjectionTypeBaseTypeMapping(TypeNameToBaseTypeNameM } } })", - typeNameToBaseTypeMap.size(), - bind([&](writer& w) { - for (auto&& [key, value] : typeNameToBaseTypeMap) - { - w.write(R"(["%"] = "%",)", key, value); - w.write("\n"); - } - })); +typeNameToBaseTypeMap.size(), +bind([&](writer& w) { + for (auto&& [key, value] : typeNameToBaseTypeMap) + { + w.write(R"(["%"] = "%",)", key, value); + w.write("\n"); + } + })); baseTypeWriter.flush_to_file(settings.output_folder / "WinRTBaseTypeMappingHelper.cs"); } + if (!abiDelegateEntries.empty() && settings.netstandard_compat) + { + writer baseTypeWriter("WinRT"); + write_file_header(baseTypeWriter); + baseTypeWriter.write(R"( +using System; + +namespace WinRT +{ +internal static class AbiDelegatesInitializer +{ + +[System.Runtime.CompilerServices.ModuleInitializer] +internal static void InitalizeAbiDelegates() +{ +% +} + +% +} +})", + bind([&](writer& w) { + for (auto&& entry : abiDelegateEntries) + { + w.write("Projections.RegisterAbiDelegate(%, typeof(%));\n", entry.abi_delegate_types, entry.abi_delegate_name); + } + + if (settings.filter.includes("Windows.Foundation.AsyncStatus")) + { + w.write("Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(IntPtr), typeof(global::Windows.Foundation.AsyncStatus), typeof(int) }, typeof(_invoke_IntPtr_AsyncStatus));\n"); + } + }), + bind([&](writer& w) { + for (auto&& entry : abiDelegateEntries) + { + w.write("%\n", entry.abi_delegate_declaration); + } + + if (settings.filter.includes("Windows.Foundation.AsyncStatus")) + { + w.write("internal unsafe delegate int _invoke_IntPtr_AsyncStatus(void* thisPtr, IntPtr asyncInfo, global::Windows.Foundation.AsyncStatus asyncStatus);\n"); + } + })); + baseTypeWriter.flush_to_file(settings.output_folder / "WinRTAbiDelegateInitializer.cs"); + } + if (projectionFileWritten) { for (auto&& string : strings::base)