From 13c9d39de71b8da93159d7b785eae0c092c89da9 Mon Sep 17 00:00:00 2001 From: Ujjwal Chadha Date: Fri, 3 Sep 2021 14:25:59 -0700 Subject: [PATCH 01/13] Add fast abi interfaces --- src/Projections/Test/Test.csproj | 7 +- src/Projections/Test/TestClass.cs | 50 ++++++++ src/Tests/UnitTest/TestComponent_Tests.cs | 8 ++ src/build.cmd | 2 +- src/cswinrt.sln | 17 +++ src/cswinrt/code_writers.h | 140 ++++++++++++++++++++-- src/cswinrt/cswinrt.vcxproj | 2 +- 7 files changed, 214 insertions(+), 12 deletions(-) create mode 100644 src/Projections/Test/TestClass.cs diff --git a/src/Projections/Test/Test.csproj b/src/Projections/Test/Test.csproj index 2878ba757..7fdc7b641 100644 --- a/src/Projections/Test/Test.csproj +++ b/src/Projections/Test/Test.csproj @@ -8,8 +8,9 @@ - - + + + @@ -19,7 +20,7 @@ - TestComponent;TestComponentCSharp;test_component_base;test_component_derived; + TestComponent;TestComponentCSharp;test_component_base;test_component_derived;test_component_fast; $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.winui', '$(MicrosoftWinUIVersion)')) diff --git a/src/Projections/Test/TestClass.cs b/src/Projections/Test/TestClass.cs new file mode 100644 index 000000000..a6c4bb5ab --- /dev/null +++ b/src/Projections/Test/TestClass.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.Text; +using WinRT; +using WinRT.Interop; + +#if NET5_0 +namespace test_component_fast +{ + public class Simple2 + { + + } +} + +namespace Test +{ + public class TestClass + { + public unsafe void TestNonFastAbi() + { + var obj = ((IWinRTObject)new test_component_base.HierarchyA()).NativeObject; + var isimpleObjRef = obj.As(new Guid(2933667680u, 50922, 22718, 184, 164, 140, 64, 124, 50, 106, 202)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)isimpleObjRef.ThisPtr)[7](isimpleObjRef.ThisPtr, out var __retval)); + var x = MarshalString.FromAbi(__retval); + } + + public unsafe void TestFastAbi() + { + var simpleObjRef = ActivationFactory.ActivateInstance(); + //var isimpleObjRef = simpleObjRef.As(new Guid(3524833624u, 45974, 22850, 165, 252, 100, 16, 68, 200, 237, 121)); + var isimpleObjRef = simpleObjRef.As(new Guid(1159756813u, 34571, 24076, 162, 100, 42, 197, 9, 2, 210, 77)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)isimpleObjRef.ThisPtr)[7](isimpleObjRef.ThisPtr, out var __retval)); + var x = MarshalString.FromAbi(__retval); + } + + public void TestSimpleNonFast() + { + var x = new test_component_fast.Simple().Method1(); + var x2 = new test_component_fast.Simple().Method2(); + var x3 = new test_component_fast.Simple().Method3(); + } + + public unsafe void Main() + { + TestSimpleNonFast(); + } + } +} +#endif \ No newline at end of file diff --git a/src/Tests/UnitTest/TestComponent_Tests.cs b/src/Tests/UnitTest/TestComponent_Tests.cs index fc29de985..2a4137e61 100644 --- a/src/Tests/UnitTest/TestComponent_Tests.cs +++ b/src/Tests/UnitTest/TestComponent_Tests.cs @@ -845,6 +845,13 @@ public void TestVectorGetMany() Assert.True(composableObjects.GetRange(1, 3).SequenceEqual(interfaceSubset)); } +#if NET5_0 + [Fact] + public void FastAbi() + { + new Test.TestClass().Main(); + } +#endif // Nota Bene: this test case must always remain the final one [Fact] public void Z_Check_Coverage() @@ -852,5 +859,6 @@ public void Z_Check_Coverage() Tests.Simple(); //Assert.Equal((double)Tests.Percentage, (double)100); } + } } \ No newline at end of file diff --git a/src/build.cmd b/src/build.cmd index fc98cf84e..dd4e2ad1c 100644 --- a/src/build.cmd +++ b/src/build.cmd @@ -133,7 +133,7 @@ if not exist %nuget_dir% md %nuget_dir% if not exist %nuget_dir%\nuget.exe powershell -Command "Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/v5.8.0-preview.2/nuget.exe -OutFile %nuget_dir%\nuget.exe" %nuget_dir%\nuget update -self rem Note: packages.config-based (vcxproj) projects do not support msbuild /t:restore -call %this_dir%get_testwinrt.cmd +rem call %this_dir%get_testwinrt.cmd set NUGET_RESTORE_MSBUILD_ARGS=/p:platform="%cswinrt_platform%" call :exec %nuget_dir%\nuget.exe restore %nuget_params% %this_dir%cswinrt.sln rem: Calling nuget restore again on ObjectLifetimeTests.Lifted.csproj to prevent .props from \microsoft.testplatform.testhost\build\netcoreapp2.1 from being included. Nuget.exe erroneously imports props files. https://github.com/NuGet/Home/issues/9672 diff --git a/src/cswinrt.sln b/src/cswinrt.sln index 671ce57ee..1e46636ce 100644 --- a/src/cswinrt.sln +++ b/src/cswinrt.sln @@ -117,6 +117,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IIDOptimizer", "Perf\IIDOpt EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reunion", "Projections\Reunion\Reunion.csproj", "{B6312AD1-A59E-4F3B-AA39-20B780FE9E15}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_component_fast", "TestWinRT\test_component_fast\test_component_fast.vcxproj", "{0E0ACA62-A92F-44CF-BD41-AEB541946DF8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM @@ -482,6 +484,20 @@ Global {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|x64.Build.0 = Release|x64 {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|x86.ActiveCfg = Release|x86 {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|x86.Build.0 = Release|x86 + {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Debug|ARM.ActiveCfg = Debug|ARM + {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Debug|ARM.Build.0 = Debug|ARM + {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Debug|ARM64.ActiveCfg = Debug|Win32 + {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Debug|x64.ActiveCfg = Debug|x64 + {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Debug|x64.Build.0 = Debug|x64 + {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Debug|x86.ActiveCfg = Debug|Win32 + {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Debug|x86.Build.0 = Debug|Win32 + {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Release|ARM.ActiveCfg = Release|ARM + {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Release|ARM.Build.0 = Release|ARM + {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Release|ARM64.ActiveCfg = Release|Win32 + {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Release|x64.ActiveCfg = Release|x64 + {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Release|x64.Build.0 = Release|x64 + {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Release|x86.ActiveCfg = Release|Win32 + {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -509,6 +525,7 @@ Global {493C7729-2F21-4198-AB09-BDF56BF501D3} = {CFB651EC-DAA4-4A11-ABCD-C77F90602EB5} {AE3B0611-2FBB-42AB-A245-B4E79868A5F9} = {539DBDEF-3B49-4503-9BD3-7EB83C2179CB} {B6312AD1-A59E-4F3B-AA39-20B780FE9E15} = {6D41796B-9904-40B8-BBCB-40B2D1BAE44B} + {0E0ACA62-A92F-44CF-BD41-AEB541946DF8} = {CFB651EC-DAA4-4A11-ABCD-C77F90602EB5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5AE8C9D7-2613-4E1A-A4F2-579BAC28D0A2} diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 00bcef0b3..cbdda2df3 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -2496,7 +2496,7 @@ private % AsInternal(InterfaceTag<%> _) => ((Lazy<%>)_lazyInterfaces[typeof(%)] db_path.stem().string()); } - auto get_invoke_info(writer& w, MethodDef const& method) + auto get_invoke_info(writer& w, MethodDef const& method, uint32_t const& method_start_index = 6) { TypeDef const& type = method.Parent(); if (!settings.netstandard_compat && distance(type.GenericParam()) == 0) @@ -2504,7 +2504,7 @@ db_path.stem().string()); return std::pair{ w.write_temp("(*(delegate* unmanaged[Stdcall]<%, int>**)ThisPtr)[%]", bind(method_signature { method }), - get_vmethod_index(type, method) + 6 /* number of methods in IInspectable */), + get_vmethod_index(type, method) + method_start_index /* number of methods in IInspectable + previous methods if fastabi*/), false }; } @@ -3380,8 +3380,9 @@ global::System.Collections.Concurrent.ConcurrentDictionary 0; auto init_call_variables = [&](writer& w) @@ -3402,7 +3403,7 @@ global::System.Collections.Concurrent.ConcurrentDictionary(", ", signature.params()), bind(init_call_variables), bind(signature, invoke_target, is_generic, false, is_noexcept(method))); + totalMembers++; } for (auto&& prop : type.PropertyList()) @@ -3472,6 +3474,7 @@ global::System.Collections.Concurrent.ConcurrentDictionary(invoke_target, is_generic, marshalers, is_noexcept(prop))); } w.write("}\n"); + totalMembers++; } int index = 0; @@ -3518,7 +3521,9 @@ return %; event_source, event_source); index++; + totalMembers++; } + return totalMembers; } struct required_interface @@ -4907,8 +4912,102 @@ IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, }); } + /*struct FastAbiInterface + { + TypeDef iface; + + FastAbiInterface(TypeDef interfaceType) : iface(interfaceType) + { + } + + bool operator < (TypeDef const& otherIface) + { + + return iface.TypeNamespace() == otherIface.TypeNamespace() ? iface.TypeName() < otherIface.TypeName() : iface.TypeNamespace() < otherIface.TypeNamespace(); + } + + };*/ + + template + auto get_attribute_value(CustomAttribute const& attribute, uint32_t const arg) + { + return std::get(std::get(attribute.Value().FixedArgs()[arg].value).value); + } + + auto get_contract_version(TypeDef& iface) + { + auto version_attribute = get_attribute(iface, "Windows.Foundation.Metadata"sv, "ContractVersionAttribute"sv); + if (version_attribute) + { + + } + } + + std::optional get_execlusive_to_class_if_default(TypeDef const& iface) + { + auto exclusiveToAttribute = get_attribute(iface, "Windows.Foundation.Metadata"sv, "ExclusiveToAttribute"sv); + if (exclusiveToAttribute) + { + auto sys_type = std::get(std::get(exclusiveToAttribute.Value().FixedArgs()[0].value).value); + TypeDef exclusiveToClass = iface.get_cache().find_required(sys_type.name); + for (auto&& ifaceImpl : exclusiveToClass.InterfaceImpl()) + { + if (has_attribute(ifaceImpl, "Windows.Foundation.Metadata", "DefaultAttribute")) + { + auto&& sem = get_type_semantics(ifaceImpl.Interface()); + if (!std::holds_alternative(sem)) + { + return {}; + } + if (std::get(sem).TypeNamespace() == iface.TypeNamespace() && std::get(sem).TypeName() == iface.TypeName()) + { + return exclusiveToClass; + } + return {}; + } + } + } + return {}; + } + + std::vector get_all_exclusive_interfaces(TypeDef const& classType) + { + std::vector exclusive_interfaces; + for (auto&& ifaceImpl : classType.InterfaceImpl()) + { + auto&& sem = get_type_semantics(ifaceImpl.Interface()); + if (std::holds_alternative(sem)) + { + if (has_attribute(std::get(sem), "Windows.Foundation.Metadata"sv, "ExclusiveToAttribute"sv)) + { + exclusive_interfaces.push_back(std::get(sem)); + } + } + } + return exclusive_interfaces; + } + + std::optional> get_fast_abi_ifaces(TypeDef const& iface) + { + auto exclusive_to_class = get_execlusive_to_class_if_default(iface); + std::vector fast_abi_ifaces; + if (exclusive_to_class.has_value() && has_attribute(exclusive_to_class.value(), "Windows.Foundation.Metadata"sv, "FastAbiAttribute"sv)) + { + fast_abi_ifaces = get_all_exclusive_interfaces(exclusive_to_class.value()); + std::sort(fast_abi_ifaces.begin(), fast_abi_ifaces.end(), [](TypeDef const& iface, TypeDef const& otherIface) + { + return iface.TypeNamespace() == otherIface.TypeNamespace() ? iface.TypeName() < otherIface.TypeName() : iface.TypeNamespace() < otherIface.TypeNamespace(); + }); + return fast_abi_ifaces; + } + return {}; + } + void write_interface(writer& w, TypeDef const& type) { + auto&& fast_abi_ifaces = get_fast_abi_ifaces(type); + auto&& is_fast_abi_enabled = fast_abi_ifaces.has_value(); + XLANG_ASSERT(get_category(type) == category::interface_type); auto type_name = write_type_name_temp(w, type, "%", typedef_name_type::CCW); @@ -4925,7 +5024,20 @@ IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, is_exclusive_to(type) || is_projection_internal(type) ? "internal" : "public", type_name, bind(type, object_type{}, false, false), - bind(type) + [&](writer& w) + { + if (!is_fast_abi_enabled) + { + write_interface_member_signatures(w, type); + } + else + { + for (auto&& fast_abi_iface : fast_abi_ifaces.value()) + { + write_interface_member_signatures(w, fast_abi_iface); + } + } + } ); } @@ -5016,7 +5128,7 @@ public static Guid PIID = Vftbl.PIID; required_interface.second.adapter); } }, - bind(type), + bind(type, 6), bind(type), [&](writer& w) { for (auto required_interface : required_interfaces) @@ -5043,6 +5155,9 @@ public static class % bool write_abi_interface(writer& w, TypeDef const& type) { + auto fast_abi_ifaces = get_fast_abi_ifaces(type); + auto is_fast_abi_enabled = fast_abi_ifaces.has_value(); + bool is_generic = distance(type.GenericParam()) > 0; XLANG_ASSERT(get_category(type) == category::interface_type); auto type_name = write_type_name_temp(w, type, "%", typedef_name_type::ABI); @@ -5105,7 +5220,18 @@ AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(@), s bind_each(type.EventList())); } }), - bind(type), + [&](writer& w) { + if (!is_fast_abi_enabled) + { + write_interface_members(w, type, 6); + return; + } + auto member_start_index = 6; + for (auto fast_abi_iface : fast_abi_ifaces.value()) + { + member_start_index += write_interface_members(w, fast_abi_iface, member_start_index); + } + }, bind(type), [&](writer& w) { for (auto required_interface : required_interfaces) diff --git a/src/cswinrt/cswinrt.vcxproj b/src/cswinrt/cswinrt.vcxproj index 674c12582..1e8ae2f9e 100644 --- a/src/cswinrt/cswinrt.vcxproj +++ b/src/cswinrt/cswinrt.vcxproj @@ -47,7 +47,7 @@ kernel32.lib;user32.lib;%(AdditionalDependencies);windowsapp.lib;advapi32.lib;shlwapi.lib - true + false From 7fea6b241371b46200496f5a06de3e6b4b353ec9 Mon Sep 17 00:00:00 2001 From: Ujjwal Chadha Date: Wed, 15 Sep 2021 18:53:39 -0700 Subject: [PATCH 02/13] Hierarchy implementation --- src/Projections/Test/TestClass.cs | 45 +++- .../UnitTest/TestComponentCSharp_Tests.cs | 6 + .../MatchingRefApiCompatBaseline.net5.0.txt | 5 +- src/WinRT.Runtime/ObjectReference.cs | 22 +- src/cswinrt/code_writers.h | 239 ++++++++---------- src/cswinrt/helpers.h | 144 +++++++++++ 6 files changed, 314 insertions(+), 147 deletions(-) diff --git a/src/Projections/Test/TestClass.cs b/src/Projections/Test/TestClass.cs index a6c4bb5ab..cf0c4e3d9 100644 --- a/src/Projections/Test/TestClass.cs +++ b/src/Projections/Test/TestClass.cs @@ -25,25 +25,58 @@ public unsafe void TestNonFastAbi() var x = MarshalString.FromAbi(__retval); } + public unsafe void TestNonFastAbiComposable() + { + var obj = ((IWinRTObject)(new test_component_fast.Composition.Compositor().CreateSpriteVisual())).NativeObject; + var isimpleObjRef = obj.As(new Guid(482120691u, 63510, 21972, 176, 154, 120, 59, 133, 167, 9, 92)); + //global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)isimpleObjRef.ThisPtr)[8](isimpleObjRef.ThisPtr, out var __retval)); + var ptr = (*(delegate* unmanaged[Stdcall]**)isimpleObjRef.ThisPtr)[8](isimpleObjRef.ThisPtr); + //MarshalInspectable + //var x = MarshalString.FromAbi(__retval); + } + public unsafe void TestFastAbi() { var simpleObjRef = ActivationFactory.ActivateInstance(); - //var isimpleObjRef = simpleObjRef.As(new Guid(3524833624u, 45974, 22850, 165, 252, 100, 16, 68, 200, 237, 121)); - var isimpleObjRef = simpleObjRef.As(new Guid(1159756813u, 34571, 24076, 162, 100, 42, 197, 9, 2, 210, 77)); + var isimpleObjRef = simpleObjRef.As(new Guid(3524833624u, 45974, 22850, 165, 252, 100, 16, 68, 200, 237, 121)); + //var isimpleObjRef = simpleObjRef.As(new Guid(1159756813u, 34571, 24076, 162, 100, 42, 197, 9, 2, 210, 77)); global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)isimpleObjRef.ThisPtr)[7](isimpleObjRef.ThisPtr, out var __retval)); var x = MarshalString.FromAbi(__retval); } public void TestSimpleNonFast() { - var x = new test_component_fast.Simple().Method1(); - var x2 = new test_component_fast.Simple().Method2(); - var x3 = new test_component_fast.Simple().Method3(); + var simple = new test_component_fast.Simple(); + var x = simple.Method1(); + var x2 = simple.Method2(); + var x3 = simple.Method3(); + simple.Property1 = "Property1"; + simple.Property3 = "Property3"; + var p1 = simple.Property1; + var p2 = simple.Property2; + var p3 = simple.Property3; + + var ev = ""; + simple.Event0 += () => + { + ev = "Hello"; + }; + simple.InvokeEvent0(); } public unsafe void Main() { - TestSimpleNonFast(); + //TestSimpleNonFast(); + //TestFastAbi(); + + var x = new test_component_fast.Composition.Compositor(); + var sv = x.CreateSpriteVisual(); + sv.Offset = 10; + sv.StartAnimationGroup(); + //sv.StartAnimationGroup(); + var y = sv.Offset; + var z = 0; + //TestNonFastAbiComposable(); } } } diff --git a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs index be48ce775..8f22d463e 100644 --- a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs +++ b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs @@ -2672,5 +2672,11 @@ private async Task TestPnpPropertiesAsync() return pnpObject; }).ToList(); } + + [Fact] + private void TestFastAbi() + { + + } } } diff --git a/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net5.0.txt b/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net5.0.txt index 347a8206a..909e4f39a 100644 --- a/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net5.0.txt +++ b/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net5.0.txt @@ -3,7 +3,8 @@ TypesMustExist : Type 'System.Numerics.VectorExtensions' does not exist in the r TypesMustExist : Type 'WinRT.ComWrappersHelper' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public void WinRT.ComWrappersSupport.RegisterObjectForInterface(System.Object, System.IntPtr, System.Runtime.InteropServices.CreateObjectFlags)' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'protected void WinRT.IObjectReference.AddRef(System.Boolean)' does not exist in the reference but it does exist in the implementation. +MembersMustExist : Member 'public WinRT.ObjectReference WinRT.IObjectReference.AsKnownPtr(System.IntPtr)' does not exist in the reference but it does exist in the implementation. +MembersMustExist : Member 'public System.Int32 WinRT.IObjectReference.TryAs(System.Guid, System.IntPtr)' does not exist in the reference but it does exist in the implementation. TypesMustExist : Type 'WinRT.Interop.IWeakReference' does not exist in the reference but it does exist in the implementation. TypesMustExist : Type 'WinRT.Interop.IWeakReferenceSource' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public System.Int32 WinRT.IObjectReference.TryAs(System.Guid, System.IntPtr)' does not exist in the reference but it does exist in the implementation. -Total Issues: 7 +Total Issues: 8 diff --git a/src/WinRT.Runtime/ObjectReference.cs b/src/WinRT.Runtime/ObjectReference.cs index 9fd385317..a3e7d0de7 100644 --- a/src/WinRT.Runtime/ObjectReference.cs +++ b/src/WinRT.Runtime/ObjectReference.cs @@ -170,12 +170,22 @@ public virtual unsafe int TryAs(Guid iid, out ObjectReference objRef) return hr; } - // Used only as part of the GetInterface implementation where the - // result is an reference passed across the ABI and doesn't need to - // be tracked as an internal reference. This is separate to handle - // tear off aggregate scenario where releasing an reference can end up - // deleting the tear off interface. - public virtual unsafe int TryAs(Guid iid, out IntPtr ppv) + public virtual unsafe ObjectReference AsKnownPtr(IntPtr ptr) + { + AddRefFromTrackerSource(); + var objRef = ObjectReference.Attach(ref ptr); + objRef.IsAggregated = IsAggregated; + objRef.PreventReleaseOnDispose = IsAggregated; + objRef.ReferenceTrackerPtr = ReferenceTrackerPtr; + return objRef; + } + + // Used only as part of the GetInterface implementation where the + // result is an reference passed across the ABI and doesn't need to + // be tracked as an internal reference. This is separate to handle + // tear off aggregate scenario where releasing an reference can end up + // deleting the tear off interface. + public virtual unsafe int TryAs(Guid iid, out IntPtr ppv) { ppv = IntPtr.Zero; ThrowIfDisposed(); diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index cbdda2df3..d0767e4fa 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -2346,8 +2346,13 @@ remove => %.ErrorsChanged -= value; auto interface_abi_name = write_type_name_temp(w, interface_type, "%", typedef_name_type::ABI); auto is_default_interface = has_attribute(ii, "Windows.Foundation.Metadata", "DefaultAttribute"); - auto target = wrapper_type ? write_type_name_temp(w, interface_type, "((%) _comp)") : (is_default_interface ? "_default" : write_type_name_temp(w, interface_type, "AsInternal(new InterfaceTag<%>())")); - if (!is_default_interface && !wrapper_type) + auto is_fast_abi = has_attribute(type, "Windows.Foundation.Metadata"sv, "FastAbiAttribute"sv); + auto sem = get_type_semantics(ii.Interface()); + auto is_exclusive_interface = std::holds_alternative(sem) && has_attribute(std::get(sem), "Windows.Foundation.Metadata"sv, "ExclusiveToAttribute"sv); + auto use_default_interface = is_default_interface || (!settings.netstandard_compat && is_fast_abi && is_exclusive_interface); + + auto target = wrapper_type ? write_type_name_temp(w, interface_type, "((%) _comp)") : (use_default_interface ? "_default" : write_type_name_temp(w, interface_type, "AsInternal(new InterfaceTag<%>())")); + if (!use_default_interface && !wrapper_type) { if (settings.netstandard_compat) { @@ -2528,7 +2533,7 @@ db_path.stem().string()); void write_event_source_generic_args(writer& w, cswinrt::type_semantics eventTypeSemantics); - void write_event_source_ctor(writer& w, Event const& evt, int index) + void write_event_source_ctor(writer& w, Event const& evt, int index, int member_invoke_start_index) { if (for_typedef(w, get_type_semantics(evt.EventType()), [&](TypeDef const& eventType) { @@ -2540,8 +2545,8 @@ db_path.stem().string()); %, %))", bind(eventType), -get_invoke_info(w, add).first, -get_invoke_info(w, remove).first, +get_invoke_info(w, add, member_invoke_start_index).first, +get_invoke_info(w, remove, member_invoke_start_index).first, index); return true; } @@ -2559,8 +2564,8 @@ new %%(_obj, %))", bind(get_type_semantics(evt.EventType())), bind(get_type_semantics(evt.EventType())), - get_invoke_info(w, add).first, - get_invoke_info(w, remove).first, + get_invoke_info(w, add, member_invoke_start_index).first, + get_invoke_info(w, remove, member_invoke_start_index).first, index); } @@ -2575,11 +2580,17 @@ evt.Name()); } } - void write_event_source_tables(writer& w, TypeDef const& type) + void write_event_source_tables(writer& w, TypeDef const& type, std::optional> const& fast_abi_ifaces) { - for (auto&& evt : type.EventList()) + std::vector types; + types.push_back(type); + if (fast_abi_ifaces.has_value()) + types.insert(types.end(), fast_abi_ifaces.value().begin(), fast_abi_ifaces.value().end()); + for (auto&& type : types) { - w.write(R"( + for (auto&& evt : type.EventList()) + { + w.write(R"( private readonly static global::System.Lazy>> _%Lazy = new(); private static global::System.Runtime.CompilerServices.ConditionalWeakTable> _% => _%Lazy.Value; )", @@ -2588,7 +2599,9 @@ private static global::System.Runtime.CompilerServices.ConditionalWeakTable(get_type_semantics(evt.EventType()), typedef_name_type::Projected, false), evt.Name(), evt.Name()); + } } + } void write_interface_member_signatures(writer& w, TypeDef const& type) @@ -3380,7 +3393,7 @@ global::System.Collections.Concurrent.ConcurrentDictionary fast_abi_default_iface = {}) { int totalMembers = 0; bool generic_type = distance(type.GenericParam()) > 0; @@ -3391,7 +3404,7 @@ global::System.Collections.Concurrent.ConcurrentDictionary" : "IObjectReference", - bind(type, typedef_name_type::CCW, false)); + bind(fast_abi_default_iface.has_value() ? fast_abi_default_iface.value() : type, typedef_name_type::CCW, false)); w.write("var ThisPtr = _obj.ThisPtr;\n"); } }; @@ -3415,7 +3428,7 @@ global::System.Collections.Concurrent.ConcurrentDictionary(type, typedef_name_type::CCW, false)); + w.write("%.", bind(fast_abi_default_iface.has_value() ? fast_abi_default_iface.value() : type, typedef_name_type::CCW, false)); } }), method.Name(), @@ -3438,14 +3451,14 @@ global::System.Collections.Concurrent.ConcurrentDictionary(type, typedef_name_type::CCW, false)); + w.write("%.", bind(fast_abi_default_iface.has_value() ? fast_abi_default_iface.value() : type, typedef_name_type::CCW, false)); } }), prop.Name()); if (getter) { - auto [invoke_target, is_generic] = get_invoke_info(w, getter); + auto [invoke_target, is_generic] = get_invoke_info(w, getter, invoke_start_index); auto signature = method_signature(getter); auto marshalers = get_abi_marshalers(w, signature, is_generic, prop.Name()); w.write(R"(get @@ -3453,6 +3466,7 @@ global::System.Collections.Concurrent.ConcurrentDictionary(invoke_target, is_generic, marshalers, is_noexcept(prop))); + totalMembers++; } if (setter) { @@ -3463,7 +3477,7 @@ global::System.Collections.Concurrent.ConcurrentDictionary()"s : "((%)(IWinRTObject)this)"s; w.write("get{ return " + getter_cast + ".%; }\n", getter_interface, prop.Name()); } - auto [invoke_target, is_generic] = get_invoke_info(w, setter); + auto [invoke_target, is_generic] = get_invoke_info(w, setter, invoke_start_index); auto signature = method_signature(setter); auto marshalers = get_abi_marshalers(w, signature, is_generic, prop.Name()); marshalers[0].param_name = "value"; @@ -3472,9 +3486,9 @@ global::System.Collections.Concurrent.ConcurrentDictionary(invoke_target, is_generic, marshalers, is_noexcept(prop))); + totalMembers++; } w.write("}\n"); - totalMembers++; } int index = 0; @@ -3506,7 +3520,7 @@ return %; evt.Name(), evt.Name(), bind(init_call_variables), - bind(evt, index)); + bind(evt, index, invoke_start_index)); }), settings.netstandard_compat ? "public " : "", bind(get_type_semantics(evt.EventType()), typedef_name_type::Projected, false), @@ -3514,14 +3528,14 @@ return %; { if (!settings.netstandard_compat) { - w.write("%.", bind(type, typedef_name_type::CCW, false)); + w.write("%.", bind(fast_abi_default_iface.has_value() ? fast_abi_default_iface.value() : type, typedef_name_type::CCW, false)); } }), evt.Name(), event_source, event_source); index++; - totalMembers++; + totalMembers += 2; } return totalMembers; } @@ -4912,101 +4926,10 @@ IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, }); } - /*struct FastAbiInterface - { - TypeDef iface; - - FastAbiInterface(TypeDef interfaceType) : iface(interfaceType) - { - } - - bool operator < (TypeDef const& otherIface) - { - - return iface.TypeNamespace() == otherIface.TypeNamespace() ? iface.TypeName() < otherIface.TypeName() : iface.TypeNamespace() < otherIface.TypeNamespace(); - } - - };*/ - - template - auto get_attribute_value(CustomAttribute const& attribute, uint32_t const arg) - { - return std::get(std::get(attribute.Value().FixedArgs()[arg].value).value); - } - - auto get_contract_version(TypeDef& iface) - { - auto version_attribute = get_attribute(iface, "Windows.Foundation.Metadata"sv, "ContractVersionAttribute"sv); - if (version_attribute) - { - - } - } - - std::optional get_execlusive_to_class_if_default(TypeDef const& iface) - { - auto exclusiveToAttribute = get_attribute(iface, "Windows.Foundation.Metadata"sv, "ExclusiveToAttribute"sv); - if (exclusiveToAttribute) - { - auto sys_type = std::get(std::get(exclusiveToAttribute.Value().FixedArgs()[0].value).value); - TypeDef exclusiveToClass = iface.get_cache().find_required(sys_type.name); - for (auto&& ifaceImpl : exclusiveToClass.InterfaceImpl()) - { - if (has_attribute(ifaceImpl, "Windows.Foundation.Metadata", "DefaultAttribute")) - { - auto&& sem = get_type_semantics(ifaceImpl.Interface()); - if (!std::holds_alternative(sem)) - { - return {}; - } - if (std::get(sem).TypeNamespace() == iface.TypeNamespace() && std::get(sem).TypeName() == iface.TypeName()) - { - return exclusiveToClass; - } - return {}; - } - } - } - return {}; - } - - std::vector get_all_exclusive_interfaces(TypeDef const& classType) - { - std::vector exclusive_interfaces; - for (auto&& ifaceImpl : classType.InterfaceImpl()) - { - auto&& sem = get_type_semantics(ifaceImpl.Interface()); - if (std::holds_alternative(sem)) - { - if (has_attribute(std::get(sem), "Windows.Foundation.Metadata"sv, "ExclusiveToAttribute"sv)) - { - exclusive_interfaces.push_back(std::get(sem)); - } - } - } - return exclusive_interfaces; - } - - std::optional> get_fast_abi_ifaces(TypeDef const& iface) - { - auto exclusive_to_class = get_execlusive_to_class_if_default(iface); - std::vector fast_abi_ifaces; - if (exclusive_to_class.has_value() && has_attribute(exclusive_to_class.value(), "Windows.Foundation.Metadata"sv, "FastAbiAttribute"sv)) - { - fast_abi_ifaces = get_all_exclusive_interfaces(exclusive_to_class.value()); - std::sort(fast_abi_ifaces.begin(), fast_abi_ifaces.end(), [](TypeDef const& iface, TypeDef const& otherIface) - { - return iface.TypeNamespace() == otherIface.TypeNamespace() ? iface.TypeName() < otherIface.TypeName() : iface.TypeNamespace() < otherIface.TypeNamespace(); - }); - return fast_abi_ifaces; - } - return {}; - } - void write_interface(writer& w, TypeDef const& type) { - auto&& fast_abi_ifaces = get_fast_abi_ifaces(type); - auto&& is_fast_abi_enabled = fast_abi_ifaces.has_value(); + auto fast_abi_class = get_fast_abi_class_for_default_interface(type); + auto is_fast_abi_default_interface = fast_abi_class.has_value(); XLANG_ASSERT(get_category(type) == category::interface_type); auto type_name = write_type_name_temp(w, type, "%", typedef_name_type::CCW); @@ -5026,16 +4949,15 @@ IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, bind(type, object_type{}, false, false), [&](writer& w) { - if (!is_fast_abi_enabled) + write_interface_member_signatures(w, type); + if (!is_fast_abi_default_interface || settings.netstandard_compat) { - write_interface_member_signatures(w, type); + return; } - else + auto other_fast_abi_ifaces = get_fast_abi_ifaces_except_default(fast_abi_class.value()); + for (auto&& fast_abi_iface : other_fast_abi_ifaces.value()) { - for (auto&& fast_abi_iface : fast_abi_ifaces.value()) - { - write_interface_member_signatures(w, fast_abi_iface); - } + write_interface_member_signatures(w, fast_abi_iface); } } ); @@ -5105,7 +5027,7 @@ public static Guid PIID = Vftbl.PIID; int index = 0; for (auto&& evt : type.EventList()) { - w.write("_% = %;\n", evt.Name(), bind(evt, index++)); + w.write("_% = %;\n", evt.Name(), bind(evt, index++, 6)); } }, [&](writer& w) { @@ -5128,7 +5050,7 @@ public static Guid PIID = Vftbl.PIID; required_interface.second.adapter); } }, - bind(type, 6), + bind(type, 6, std::nullopt), bind(type), [&](writer& w) { for (auto required_interface : required_interfaces) @@ -5155,8 +5077,9 @@ public static class % bool write_abi_interface(writer& w, TypeDef const& type) { - auto fast_abi_ifaces = get_fast_abi_ifaces(type); - auto is_fast_abi_enabled = fast_abi_ifaces.has_value(); + auto fast_abi_class = get_fast_abi_class_for_default_interface(type); + auto is_fast_abi_default_interface = fast_abi_class.has_value(); + std::optional> other_fast_abi_ifaces = is_fast_abi_default_interface ? get_fast_abi_ifaces_except_default(fast_abi_class.value()) : std::nullopt; bool is_generic = distance(type.GenericParam()) > 0; XLANG_ASSERT(get_category(type) == category::interface_type); @@ -5221,18 +5144,19 @@ AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(@), s } }), [&](writer& w) { - if (!is_fast_abi_enabled) + auto member_start_index = 6; + member_start_index += write_interface_members(w, type, 6, std::nullopt); + if (!is_fast_abi_default_interface) { - write_interface_members(w, type, 6); return; } - auto member_start_index = 6; - for (auto fast_abi_iface : fast_abi_ifaces.value()) + member_start_index += get_class_hierarchy_index(fast_abi_class.value()); + for (auto fast_abi_iface : other_fast_abi_ifaces.value()) { - member_start_index += write_interface_members(w, fast_abi_iface, member_start_index); + member_start_index += write_interface_members(w, fast_abi_iface, member_start_index, std::optional(type)); } }, - bind(type), + bind(type, other_fast_abi_ifaces), [&](writer& w) { for (auto required_interface : required_interfaces) { @@ -5625,20 +5549,69 @@ private % AsInternal(InterfaceTag<%> _) => _default; bind([&](writer& w) { bool has_base_type = !std::holds_alternative(get_type_semantics(type.Extends())); + bool is_fast_abi_class = has_attribute(type, "Windows.Foundation.Metadata"sv, "FastAbiAttribute"sv); + if (is_fast_abi_class) + { + int hierarchy_index = get_class_hierarchy_index(type); + if (hierarchy_index > 0 || !type.Flags().Sealed()) + { + auto default_interface = get_default_interface(type); + auto default_iface_method_count = 0; + for_typedef(w, get_type_semantics(default_interface), [&](TypeDef iface) + { + for (auto&& method : iface.MethodList()) + { + default_iface_method_count++; + } + }); + + w.write(R"( +protected unsafe % List> GetDefaultFactories() +{ +var list = new List>(); +% +return list; +})", + hierarchy_index == 0 ? "virtual" : "override", + bind([&](writer& w) + { + for (auto i = 0; i < hierarchy_index; i++) + { + w.write("list.Add(() => (*(delegate* unmanaged[Stdcall]**)_inner.ThisPtr)[%](_inner.ThisPtr));\n", 6 + default_iface_method_count + i); + } + })); + } + } + if (!type.Flags().Sealed()) { w.write(R"( protected %(global::WinRT.DerivedComposed _)% { -_defaultLazy = new Lazy<%>(() => (%)new IInspectable(((IWinRTObject)this).NativeObject)); +% _lazyInterfaces = new Dictionary() {% }; })", type.TypeName(), has_base_type ? ":base(_)" : "", - default_interface_name, - default_interface_name, + bind([&](writer& w) + { + if (is_fast_abi_class) + { + w.write("_defaultLazy = new Lazy<%>(() => (%)(object)new SingleInterfaceOptimizedObject(typeof(%), ((IWinRTObject)this).NativeObject.AsKnownPtr(GetDefaultFactories()[%]())));", + default_interface_name, + default_interface_name, + default_interface_name, + get_class_hierarchy_index(type)); + } + else + { + w.write("_defaultLazy = new Lazy<%>(() => (%)new IInspectable(((IWinRTObject)this).NativeObject));", + default_interface_name, + default_interface_name); + } + }), bind(type)); w.write(R"( bool IWinRTObject.HasUnwrappableNativeObject => this.GetType() == typeof(%);)", diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index e95e2cdd6..4744b2211 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -994,4 +994,148 @@ namespace cswinrt return "Unknown"; } } + + template + int get_number_of_attributes(T const& row, std::string_view const& type_namespace, std::string_view const& type_name) + { + auto count = 0; + for (auto&& attribute : row.CustomAttribute()) + { + auto pair = attribute.TypeNamespaceAndName(); + + if (pair.first == type_namespace && pair.second == type_name) + { + count++; + } + } + return count; + } + + template + auto get_attribute_value(CustomAttribute const& attribute, uint32_t const arg) + { + return std::get(std::get(attribute.Value().FixedArgs()[arg].value).value); + } + + /*std::optional> get_contract_version_history(TypeDef const& iface) + { + auto version_attribute = get_attribute(iface, "Windows.Foundation.Metadata"sv, "ContractVersionAttribute"sv); + if (version_attribute) + { + auto currentContractName = get_attribute_value(version_attribute, 0); + currentContractName.name + auto x = 1; + } + return {}; + }*/ + + std::optional get_contract_version(TypeDef const& type) + { + if (!has_attribute(type, "Windows.Foundation.Metadata", "ContractVersionAttribute")) + { + return {}; + } + return get_attribute_value(get_attribute(type, "Windows.Foundation.Metadata"sv, "ContractVersionAttribute"sv), 1); + } + + std::optional get_version(TypeDef const& type) + { + if (!has_attribute(type, "Windows.Foundation.Metadata", "VersionAttribute")) + { + return {}; + } + return get_attribute_value(get_attribute(type, "Windows.Foundation.Metadata"sv, "VersionAttribute"sv), 0); + } + + std::optional get_fast_abi_class_for_default_interface(TypeDef const& iface) + { + auto exclusiveToAttribute = get_attribute(iface, "Windows.Foundation.Metadata"sv, "ExclusiveToAttribute"sv); + if (exclusiveToAttribute) + { + auto sys_type = get_attribute_value(exclusiveToAttribute, 0); + TypeDef exclusiveToClass = iface.get_cache().find_required(sys_type.name); + if (!has_attribute(exclusiveToClass, "Windows.Foundation.Metadata"sv, "FastAbiAttribute"sv)) + { + return {}; + } + for (auto&& ifaceImpl : exclusiveToClass.InterfaceImpl()) + { + if (has_attribute(ifaceImpl, "Windows.Foundation.Metadata", "DefaultAttribute")) + { + auto&& sem = get_type_semantics(ifaceImpl.Interface()); + if (std::holds_alternative(sem) && std::get(sem).TypeNamespace() == iface.TypeNamespace() && std::get(sem).TypeName() == iface.TypeName()) + { + return exclusiveToClass; + } + return {}; + } + } + } + return {}; + } + + std::vector get_exclusive_interfaces_except_default(TypeDef const& classType) + { + std::vector exclusive_interfaces; + for (auto&& ifaceImpl : classType.InterfaceImpl()) + { + if (has_attribute(ifaceImpl, "Windows.Foundation.Metadata", "DefaultAttribute")) + { + continue; + } + auto&& sem = get_type_semantics(ifaceImpl.Interface()); + if (std::holds_alternative(sem)) + { + if (has_attribute(std::get(sem), "Windows.Foundation.Metadata"sv, "ExclusiveToAttribute"sv)) + { + exclusive_interfaces.push_back(std::get(sem)); + } + } + } + return exclusive_interfaces; + } + + int get_class_hierarchy_index(TypeDef const& classType) + { + auto sem = get_type_semantics(classType.Extends()); + if (std::holds_alternative(sem)) + { + return get_class_hierarchy_index(std::get(sem)) + 1; + } + return 0; + } + + std::optional> get_fast_abi_ifaces_except_default(TypeDef const& classType) + { + std::vector fast_abi_ifaces; + if (has_attribute(classType, "Windows.Foundation.Metadata"sv, "FastAbiAttribute"sv)) + { + fast_abi_ifaces = get_exclusive_interfaces_except_default(classType); + std::sort(fast_abi_ifaces.begin(), fast_abi_ifaces.end(), [](TypeDef const& iface, TypeDef const& otherIface) + { + // compare relative contracts + auto relativeContractValueIface = -1 * get_number_of_attributes(iface, "Windows.Foundation.Metadata"sv, "PreviousContractVersionAttribute"sv); + auto relativeContractValueOtherIface = -1 * get_number_of_attributes(otherIface, "Windows.Foundation.Metadata"sv, "PreviousContractVersionAttribute"sv); + if (relativeContractValueIface != relativeContractValueOtherIface) + return relativeContractValueIface < relativeContractValueOtherIface; + + //compare contract versions if they exist + auto contractVersionIface = get_contract_version(iface); + auto contractVersionOtherIface = get_contract_version(iface); + if (contractVersionIface.has_value() && contractVersionOtherIface.has_value() && contractVersionIface.value() != contractVersionOtherIface.value()) + return contractVersionIface.value() < contractVersionOtherIface.value(); + + //compare versions + auto versionIface = get_version(iface); + auto versionOtherIface = get_version(iface); + if (versionIface.has_value() && versionOtherIface.has_value() && versionIface.value() != versionOtherIface.value()) + return versionIface.value() < versionOtherIface.value(); + + //compare strings + return iface.TypeNamespace() == otherIface.TypeNamespace() ? iface.TypeName() < otherIface.TypeName() : iface.TypeNamespace() < otherIface.TypeNamespace(); + }); + return fast_abi_ifaces; + } + return {}; + } } From b1f65ba3569961ae154e91486bb96b22a7ed8b9c Mon Sep 17 00:00:00 2001 From: UJJWAL CHADHA Date: Tue, 21 Sep 2021 09:31:38 -0700 Subject: [PATCH 03/13] Refactor and consume getter and setter in different exclusive interfaces --- src/Projections/Test/TestClass.cs | 83 -------- .../UnitTest/TestComponentCSharp_Tests.cs | 5 - src/Tests/UnitTest/TestComponent_Tests.cs | 30 ++- .../ApiCompatBaseline.net5.0.txt | 3 +- .../MatchingRefApiCompatBaseline.net5.0.txt | 3 +- src/WinRT.Runtime/ObjectReference.cs | 2 +- .../SingleInterfaceOptimizedObject.net5.cs | 7 +- src/build.cmd | 2 +- src/cswinrt/code_writers.h | 116 ++++++----- src/cswinrt/helpers.h | 184 +++++++++++------- src/get_testwinrt.cmd | 2 +- 11 files changed, 232 insertions(+), 205 deletions(-) delete mode 100644 src/Projections/Test/TestClass.cs diff --git a/src/Projections/Test/TestClass.cs b/src/Projections/Test/TestClass.cs deleted file mode 100644 index cf0c4e3d9..000000000 --- a/src/Projections/Test/TestClass.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using WinRT; -using WinRT.Interop; - -#if NET5_0 -namespace test_component_fast -{ - public class Simple2 - { - - } -} - -namespace Test -{ - public class TestClass - { - public unsafe void TestNonFastAbi() - { - var obj = ((IWinRTObject)new test_component_base.HierarchyA()).NativeObject; - var isimpleObjRef = obj.As(new Guid(2933667680u, 50922, 22718, 184, 164, 140, 64, 124, 50, 106, 202)); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)isimpleObjRef.ThisPtr)[7](isimpleObjRef.ThisPtr, out var __retval)); - var x = MarshalString.FromAbi(__retval); - } - - public unsafe void TestNonFastAbiComposable() - { - var obj = ((IWinRTObject)(new test_component_fast.Composition.Compositor().CreateSpriteVisual())).NativeObject; - var isimpleObjRef = obj.As(new Guid(482120691u, 63510, 21972, 176, 154, 120, 59, 133, 167, 9, 92)); - //global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)isimpleObjRef.ThisPtr)[8](isimpleObjRef.ThisPtr, out var __retval)); - var ptr = (*(delegate* unmanaged[Stdcall]**)isimpleObjRef.ThisPtr)[8](isimpleObjRef.ThisPtr); - //MarshalInspectable - //var x = MarshalString.FromAbi(__retval); - } - - public unsafe void TestFastAbi() - { - var simpleObjRef = ActivationFactory.ActivateInstance(); - var isimpleObjRef = simpleObjRef.As(new Guid(3524833624u, 45974, 22850, 165, 252, 100, 16, 68, 200, 237, 121)); - //var isimpleObjRef = simpleObjRef.As(new Guid(1159756813u, 34571, 24076, 162, 100, 42, 197, 9, 2, 210, 77)); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)isimpleObjRef.ThisPtr)[7](isimpleObjRef.ThisPtr, out var __retval)); - var x = MarshalString.FromAbi(__retval); - } - - public void TestSimpleNonFast() - { - var simple = new test_component_fast.Simple(); - var x = simple.Method1(); - var x2 = simple.Method2(); - var x3 = simple.Method3(); - simple.Property1 = "Property1"; - simple.Property3 = "Property3"; - var p1 = simple.Property1; - var p2 = simple.Property2; - var p3 = simple.Property3; - - var ev = ""; - simple.Event0 += () => - { - ev = "Hello"; - }; - simple.InvokeEvent0(); - } - - public unsafe void Main() - { - //TestSimpleNonFast(); - //TestFastAbi(); - - var x = new test_component_fast.Composition.Compositor(); - var sv = x.CreateSpriteVisual(); - sv.Offset = 10; - sv.StartAnimationGroup(); - //sv.StartAnimationGroup(); - var y = sv.Offset; - var z = 0; - //TestNonFastAbiComposable(); - } - } -} -#endif \ No newline at end of file diff --git a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs index 8f22d463e..30982b0ae 100644 --- a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs +++ b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs @@ -2673,10 +2673,5 @@ private async Task TestPnpPropertiesAsync() }).ToList(); } - [Fact] - private void TestFastAbi() - { - - } } } diff --git a/src/Tests/UnitTest/TestComponent_Tests.cs b/src/Tests/UnitTest/TestComponent_Tests.cs index 2a4137e61..3517eae70 100644 --- a/src/Tests/UnitTest/TestComponent_Tests.cs +++ b/src/Tests/UnitTest/TestComponent_Tests.cs @@ -847,9 +847,35 @@ public void TestVectorGetMany() #if NET5_0 [Fact] - public void FastAbi() + public void Fast_Abi_Simple() + { + var simple = new test_component_fast.Simple(); + Assert.Equal("Method1", simple.Method1()); + Assert.Equal("Method2", simple.Method2()); + Assert.Equal("Method3", simple.Method3()); + simple.Property1 = "Property1"; + simple.Property3 = "Property3"; + Assert.Equal("Property1", simple.Property1); + Assert.Equal("Property2", simple.Property2); + Assert.Equal("Property3", simple.Property3); + var ev = ""; + simple.Event0 += () => + { + ev = "Hello"; + }; + simple.InvokeEvent0(); + Assert.Equal("Hello", ev); + } + + [Fact] + public void Fast_Abi_Composition() { - new Test.TestClass().Main(); + var compositor = new test_component_fast.Composition.Compositor(); + var sv = compositor.CreateSpriteVisual(); + sv.Offset = 10; + sv.StartAnimationGroup(); + Assert.Equal(10, sv.Offset); + Assert.Equal(10, sv.Pad); } #endif // Nota Bene: this test case must always remain the final one diff --git a/src/WinRT.Runtime/ApiCompatBaseline.net5.0.txt b/src/WinRT.Runtime/ApiCompatBaseline.net5.0.txt index 9c4fa26f0..bfb59dd88 100644 --- a/src/WinRT.Runtime/ApiCompatBaseline.net5.0.txt +++ b/src/WinRT.Runtime/ApiCompatBaseline.net5.0.txt @@ -1,4 +1,5 @@ Compat issues with assembly WinRT.Runtime: MembersMustExist : Member 'public WinRT.MarshalString.HStringHeader WinRT.MarshalString.HStringHeader WinRT.MarshalString._header' does not exist in the implementation but it does exist in the contract. TypesMustExist : Type 'WinRT.MarshalString.HStringHeader' does not exist in the implementation but it does exist in the contract. -Total Issues: 2 +MembersMustExist : Member 'public void WinRT.SingleInterfaceOptimizedObject..ctor(System.Type, WinRT.IObjectReference)' does not exist in the implementation but it does exist in the contract. +Total Issues: 3 diff --git a/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net5.0.txt b/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net5.0.txt index 909e4f39a..79a13bfd4 100644 --- a/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net5.0.txt +++ b/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net5.0.txt @@ -5,6 +5,7 @@ MembersMustExist : Member 'public void WinRT.ComWrappersSupport.RegisterObjectFo MembersMustExist : Member 'protected void WinRT.IObjectReference.AddRef(System.Boolean)' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public WinRT.ObjectReference WinRT.IObjectReference.AsKnownPtr(System.IntPtr)' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public System.Int32 WinRT.IObjectReference.TryAs(System.Guid, System.IntPtr)' does not exist in the reference but it does exist in the implementation. +MembersMustExist : Member 'public void WinRT.SingleInterfaceOptimizedObject..ctor(System.Type, WinRT.IObjectReference, System.Boolean)' does not exist in the reference but it does exist in the implementation. TypesMustExist : Type 'WinRT.Interop.IWeakReference' does not exist in the reference but it does exist in the implementation. TypesMustExist : Type 'WinRT.Interop.IWeakReferenceSource' does not exist in the reference but it does exist in the implementation. -Total Issues: 8 +Total Issues: 9 diff --git a/src/WinRT.Runtime/ObjectReference.cs b/src/WinRT.Runtime/ObjectReference.cs index a3e7d0de7..393caca26 100644 --- a/src/WinRT.Runtime/ObjectReference.cs +++ b/src/WinRT.Runtime/ObjectReference.cs @@ -172,7 +172,7 @@ public virtual unsafe int TryAs(Guid iid, out ObjectReference objRef) public virtual unsafe ObjectReference AsKnownPtr(IntPtr ptr) { - AddRefFromTrackerSource(); + AddRef(true); var objRef = ObjectReference.Attach(ref ptr); objRef.IsAggregated = IsAggregated; objRef.PreventReleaseOnDispose = IsAggregated; diff --git a/src/WinRT.Runtime/SingleInterfaceOptimizedObject.net5.cs b/src/WinRT.Runtime/SingleInterfaceOptimizedObject.net5.cs index 3c8f88e00..84856f776 100644 --- a/src/WinRT.Runtime/SingleInterfaceOptimizedObject.net5.cs +++ b/src/WinRT.Runtime/SingleInterfaceOptimizedObject.net5.cs @@ -12,9 +12,14 @@ public class SingleInterfaceOptimizedObject : IWinRTObject, IDynamicInterfaceCas private Type _type; private IObjectReference _obj; - public SingleInterfaceOptimizedObject(Type type, IObjectReference objRef) + public SingleInterfaceOptimizedObject(Type type, IObjectReference objRef, bool isTypedObjRef = false) { _type = type; + if (isTypedObjRef) + { + _obj = objRef; + return; + } Type helperType = type.FindHelperType(); var vftblType = helperType.FindVftblType(); if (vftblType is null) diff --git a/src/build.cmd b/src/build.cmd index dd4e2ad1c..fc98cf84e 100644 --- a/src/build.cmd +++ b/src/build.cmd @@ -133,7 +133,7 @@ if not exist %nuget_dir% md %nuget_dir% if not exist %nuget_dir%\nuget.exe powershell -Command "Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/v5.8.0-preview.2/nuget.exe -OutFile %nuget_dir%\nuget.exe" %nuget_dir%\nuget update -self rem Note: packages.config-based (vcxproj) projects do not support msbuild /t:restore -rem call %this_dir%get_testwinrt.cmd +call %this_dir%get_testwinrt.cmd set NUGET_RESTORE_MSBUILD_ARGS=/p:platform="%cswinrt_platform%" call :exec %nuget_dir%\nuget.exe restore %nuget_params% %this_dir%cswinrt.sln rem: Calling nuget restore again on ObjectLifetimeTests.Lifted.csproj to prevent .props from \microsoft.testplatform.testhost\build\netcoreapp2.1 from being included. Nuget.exe erroneously imports props files. https://github.com/NuGet/Home/issues/9672 diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index d0767e4fa..6d82d00a1 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -1128,10 +1128,14 @@ namespace cswinrt if (has_attribute(ii, "Windows.Foundation.Metadata", "DefaultAttribute")) { continue; - } + } for_typedef(w, get_type_semantics(ii.Interface()), [&](auto interface_type) { + if (is_exclusive_to(interface_type) && is_fast_abi_class(type)) + { + return; + } auto interface_name = write_type_name_temp(w, interface_type); auto interface_abi_name = write_type_name_temp(w, interface_type, "%", typedef_name_type::ABI); @@ -2604,7 +2608,7 @@ private static global::System.Runtime.CompilerServices.ConditionalWeakTable> fast_abi_class_val = {}) { for (auto&& method : type.MethodList()) { @@ -2626,6 +2630,11 @@ private static global::System.Runtime.CompilerServices.ConditionalWeakTable fast_abi_default_iface = {}) + void write_interface_members(writer& w, TypeDef const& type, uint32_t const& invoke_start_index = 6, std::optional> fast_abi_class_val = {}) { - int totalMembers = 0; bool generic_type = distance(type.GenericParam()) > 0; auto init_call_variables = [&](writer& w) @@ -3404,7 +3412,7 @@ global::System.Collections.Concurrent.ConcurrentDictionary" : "IObjectReference", - bind(fast_abi_default_iface.has_value() ? fast_abi_default_iface.value() : type, typedef_name_type::CCW, false)); + bind(fast_abi_class_val.has_value() ? fast_abi_class_val.value().get().default_interface : type, typedef_name_type::CCW, false)); w.write("var ThisPtr = _obj.ThisPtr;\n"); } }; @@ -3428,19 +3436,25 @@ global::System.Collections.Concurrent.ConcurrentDictionary(fast_abi_default_iface.has_value() ? fast_abi_default_iface.value() : type, typedef_name_type::CCW, false)); + w.write("%.", bind(fast_abi_class_val.has_value() ? fast_abi_class_val.value().get().default_interface : type, typedef_name_type::CCW, false)); } }), method.Name(), bind_list(", ", signature.params()), bind(init_call_variables), bind(signature, invoke_target, is_generic, false, is_noexcept(method))); - totalMembers++; } for (auto&& prop : type.PropertyList()) { auto [getter, setter] = get_property_methods(prop); + if (setter && !getter && fast_abi_class_val.has_value() + && fast_abi_class_val.value().get().contains_other_interface(type) + && fast_abi_class_val.value().get().contains_getter(prop.Name())) + { + continue; //setter was already written when the getter was encountered + } + std::optional> overridden_setter = {}; w.write(R"( %unsafe % %% { @@ -3451,7 +3465,7 @@ global::System.Collections.Concurrent.ConcurrentDictionary(fast_abi_default_iface.has_value() ? fast_abi_default_iface.value() : type, typedef_name_type::CCW, false)); + w.write("%.", bind(fast_abi_class_val.has_value() ? fast_abi_class_val.value().get().default_interface : type, typedef_name_type::CCW, false)); } }), prop.Name()); @@ -3466,18 +3480,22 @@ global::System.Collections.Concurrent.ConcurrentDictionary(invoke_target, is_generic, marshalers, is_noexcept(prop))); - totalMembers++; + if (!setter && fast_abi_class_val.has_value() && fast_abi_class_val.value().get().contains_setter(prop.Name())) + { + overridden_setter = fast_abi_class_val.value().get().find_property_setter(prop.Name()); + } } - if (setter) + if (setter || overridden_setter.has_value()) { if (!getter) { - auto getter_interface = write_type_name_temp(w, - find_property_interface(w, type, prop.Name()).first, "%", typedef_name_type::ABI); + auto getter_interface = write_type_name_temp(w, find_property_interface(w, type, prop.Name()).first, "%", typedef_name_type::ABI); auto getter_cast = settings.netstandard_compat ? "As<%>()"s : "((%)(IWinRTObject)this)"s; w.write("get{ return " + getter_cast + ".%; }\n", getter_interface, prop.Name()); } - auto [invoke_target, is_generic] = get_invoke_info(w, setter, invoke_start_index); + setter = overridden_setter.has_value() ? overridden_setter.value().first : setter; + auto [invoke_target, is_generic] = get_invoke_info(w, setter, + overridden_setter.has_value() ? overridden_setter.value().second : invoke_start_index); auto signature = method_signature(setter); auto marshalers = get_abi_marshalers(w, signature, is_generic, prop.Name()); marshalers[0].param_name = "value"; @@ -3486,7 +3504,6 @@ global::System.Collections.Concurrent.ConcurrentDictionary(invoke_target, is_generic, marshalers, is_noexcept(prop))); - totalMembers++; } w.write("}\n"); } @@ -3528,16 +3545,14 @@ return %; { if (!settings.netstandard_compat) { - w.write("%.", bind(fast_abi_default_iface.has_value() ? fast_abi_default_iface.value() : type, typedef_name_type::CCW, false)); + w.write("%.", bind(fast_abi_class_val.has_value() ? fast_abi_class_val.value().get().default_interface : type, typedef_name_type::CCW, false)); } }), evt.Name(), event_source, event_source); index++; - totalMembers += 2; } - return totalMembers; } struct required_interface @@ -4928,8 +4943,13 @@ IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, void write_interface(writer& w, TypeDef const& type) { - auto fast_abi_class = get_fast_abi_class_for_default_interface(type); - auto is_fast_abi_default_interface = fast_abi_class.has_value(); + auto m_fast_abi_class = get_fast_abi_class_for_interface(type); + if (m_fast_abi_class.has_value() + && m_fast_abi_class.value().contains_other_interface(type) + && !settings.netstandard_compat) + { + return; + } XLANG_ASSERT(get_category(type) == category::interface_type); auto type_name = write_type_name_temp(w, type, "%", typedef_name_type::CCW); @@ -4949,15 +4969,19 @@ IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, bind(type, object_type{}, false, false), [&](writer& w) { - write_interface_member_signatures(w, type); - if (!is_fast_abi_default_interface || settings.netstandard_compat) + if (m_fast_abi_class.has_value() + && !settings.netstandard_compat + && is_interfaces_equal(m_fast_abi_class.value().default_interface, type)) { - return; + write_interface_member_signatures(w, type, std::optional(std::ref(m_fast_abi_class.value()))); + for (auto&& fast_abi_iface : m_fast_abi_class.value().other_interfaces) + { + write_interface_member_signatures(w, fast_abi_iface, std::optional(std::ref(m_fast_abi_class.value()))); + } } - auto other_fast_abi_ifaces = get_fast_abi_ifaces_except_default(fast_abi_class.value()); - for (auto&& fast_abi_iface : other_fast_abi_ifaces.value()) + else { - write_interface_member_signatures(w, fast_abi_iface); + write_interface_member_signatures(w, type); } } ); @@ -5077,12 +5101,16 @@ public static class % bool write_abi_interface(writer& w, TypeDef const& type) { - auto fast_abi_class = get_fast_abi_class_for_default_interface(type); - auto is_fast_abi_default_interface = fast_abi_class.has_value(); - std::optional> other_fast_abi_ifaces = is_fast_abi_default_interface ? get_fast_abi_ifaces_except_default(fast_abi_class.value()) : std::nullopt; + XLANG_ASSERT(get_category(type) == category::interface_type); + + auto m_fast_abi_class = get_fast_abi_class_for_interface(type); + if (m_fast_abi_class.has_value() + && m_fast_abi_class.value().contains_other_interface(type)) + { + return true; + } bool is_generic = distance(type.GenericParam()) > 0; - XLANG_ASSERT(get_category(type) == category::interface_type); auto type_name = write_type_name_temp(w, type, "%", typedef_name_type::ABI); auto nongenerics_class = w.write_temp("%_Delegates", bind(type, typedef_name_type::ABI, false)); @@ -5144,19 +5172,23 @@ AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(@), s } }), [&](writer& w) { - auto member_start_index = 6; - member_start_index += write_interface_members(w, type, 6, std::nullopt); - if (!is_fast_abi_default_interface) + if (!m_fast_abi_class.has_value() + || !is_interfaces_equal(m_fast_abi_class.value().default_interface, type)) { + auto& nullopt = std::nullopt; + write_interface_members(w, type, 6, nullopt); return; } - member_start_index += get_class_hierarchy_index(fast_abi_class.value()); - for (auto fast_abi_iface : other_fast_abi_ifaces.value()) + auto vtable_start_index = 6; + write_interface_members(w, type, 6, std::ref(m_fast_abi_class.value())); + vtable_start_index += distance(type.MethodList()) + get_class_hierarchy_index(m_fast_abi_class.value().class_type); + for (auto fast_abi_iface : m_fast_abi_class.value().other_interfaces) { - member_start_index += write_interface_members(w, fast_abi_iface, member_start_index, std::optional(type)); + write_interface_members(w, fast_abi_iface, vtable_start_index, std::ref(m_fast_abi_class.value())); + vtable_start_index += distance(fast_abi_iface.MethodList()); } }, - bind(type, other_fast_abi_ifaces), + bind(type, m_fast_abi_class.has_value() ? std::optional(m_fast_abi_class.value().other_interfaces) : std::nullopt), [&](writer& w) { for (auto required_interface : required_interfaces) { @@ -5549,8 +5581,7 @@ private % AsInternal(InterfaceTag<%> _) => _default; bind([&](writer& w) { bool has_base_type = !std::holds_alternative(get_type_semantics(type.Extends())); - bool is_fast_abi_class = has_attribute(type, "Windows.Foundation.Metadata"sv, "FastAbiAttribute"sv); - if (is_fast_abi_class) + if (is_fast_abi_class(type)) { int hierarchy_index = get_class_hierarchy_index(type); if (hierarchy_index > 0 || !type.Flags().Sealed()) @@ -5559,10 +5590,7 @@ private % AsInternal(InterfaceTag<%> _) => _default; auto default_iface_method_count = 0; for_typedef(w, get_type_semantics(default_interface), [&](TypeDef iface) { - for (auto&& method : iface.MethodList()) - { - default_iface_method_count++; - } + default_iface_method_count = distance(iface.MethodList()); }); w.write(R"( @@ -5597,9 +5625,9 @@ _lazyInterfaces = new Dictionary() has_base_type ? ":base(_)" : "", bind([&](writer& w) { - if (is_fast_abi_class) + if (is_fast_abi_class(type)) { - w.write("_defaultLazy = new Lazy<%>(() => (%)(object)new SingleInterfaceOptimizedObject(typeof(%), ((IWinRTObject)this).NativeObject.AsKnownPtr(GetDefaultFactories()[%]())));", + w.write("_defaultLazy = new Lazy<%>(() => (%)(object)new SingleInterfaceOptimizedObject(typeof(%), ((IWinRTObject)this).NativeObject.AsKnownPtr(GetDefaultFactories()[%]()), true));", default_interface_name, default_interface_name, default_interface_name, diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index 4744b2211..e656495a8 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -1011,24 +1011,28 @@ namespace cswinrt return count; } + int get_class_hierarchy_index(TypeDef const& classType) + { + auto sem = get_type_semantics(classType.Extends()); + if (std::holds_alternative(sem)) + { + return get_class_hierarchy_index(std::get(sem)) + 1; + } + return 0; + } + + bool is_interfaces_equal(TypeDef const& interface1, TypeDef const& interface2) + { + return interface1.TypeNamespace() == interface2.TypeNamespace() + && interface1.TypeName() == interface2.TypeName(); + } + template auto get_attribute_value(CustomAttribute const& attribute, uint32_t const arg) { return std::get(std::get(attribute.Value().FixedArgs()[arg].value).value); } - /*std::optional> get_contract_version_history(TypeDef const& iface) - { - auto version_attribute = get_attribute(iface, "Windows.Foundation.Metadata"sv, "ContractVersionAttribute"sv); - if (version_attribute) - { - auto currentContractName = get_attribute_value(version_attribute, 0); - currentContractName.name - auto x = 1; - } - return {}; - }*/ - std::optional get_contract_version(TypeDef const& type) { if (!has_attribute(type, "Windows.Foundation.Metadata", "ContractVersionAttribute")) @@ -1047,7 +1051,12 @@ namespace cswinrt return get_attribute_value(get_attribute(type, "Windows.Foundation.Metadata"sv, "VersionAttribute"sv), 0); } - std::optional get_fast_abi_class_for_default_interface(TypeDef const& iface) + bool is_fast_abi_class(TypeDef const& type) + { + return has_attribute(type, "Windows.Foundation.Metadata"sv, "FastAbiAttribute"sv); + } + + std::optional find_fast_abi_class_type(TypeDef const& iface) { auto exclusiveToAttribute = get_attribute(iface, "Windows.Foundation.Metadata"sv, "ExclusiveToAttribute"sv); if (exclusiveToAttribute) @@ -1058,84 +1067,129 @@ namespace cswinrt { return {}; } - for (auto&& ifaceImpl : exclusiveToClass.InterfaceImpl()) - { - if (has_attribute(ifaceImpl, "Windows.Foundation.Metadata", "DefaultAttribute")) - { - auto&& sem = get_type_semantics(ifaceImpl.Interface()); - if (std::holds_alternative(sem) && std::get(sem).TypeNamespace() == iface.TypeNamespace() && std::get(sem).TypeName() == iface.TypeName()) - { - return exclusiveToClass; - } - return {}; - } - } + return exclusiveToClass; } return {}; } - std::vector get_exclusive_interfaces_except_default(TypeDef const& classType) + std::pair> get_default_and_exclusive_interfaces(TypeDef const& classType) { + std::pair> exclusive_ifaces; std::vector exclusive_interfaces; for (auto&& ifaceImpl : classType.InterfaceImpl()) { + auto&& sem = get_type_semantics(ifaceImpl.Interface()); if (has_attribute(ifaceImpl, "Windows.Foundation.Metadata", "DefaultAttribute")) { - continue; + exclusive_ifaces.first = std::get(sem); } - auto&& sem = get_type_semantics(ifaceImpl.Interface()); - if (std::holds_alternative(sem)) + else if (std::holds_alternative(sem)) { if (has_attribute(std::get(sem), "Windows.Foundation.Metadata"sv, "ExclusiveToAttribute"sv)) { - exclusive_interfaces.push_back(std::get(sem)); + exclusive_ifaces.second.push_back(std::get(sem)); } } } - return exclusive_interfaces; + return exclusive_ifaces; } - int get_class_hierarchy_index(TypeDef const& classType) + void sort_fast_abi_ifaces(std::vector& fast_abi_ifaces) { - auto sem = get_type_semantics(classType.Extends()); - if (std::holds_alternative(sem)) + std::sort(fast_abi_ifaces.begin(), fast_abi_ifaces.end(), [](TypeDef const& iface, TypeDef const& otherIface) { - return get_class_hierarchy_index(std::get(sem)) + 1; - } - return 0; + // compare relative contracts + auto relativeContractValueIface = -1 * get_number_of_attributes(iface, "Windows.Foundation.Metadata"sv, "PreviousContractVersionAttribute"sv); + auto relativeContractValueOtherIface = -1 * get_number_of_attributes(otherIface, "Windows.Foundation.Metadata"sv, "PreviousContractVersionAttribute"sv); + if (relativeContractValueIface != relativeContractValueOtherIface) + return relativeContractValueIface < relativeContractValueOtherIface; + + //compare contract versions if they exist + auto contractVersionIface = get_contract_version(iface); + auto contractVersionOtherIface = get_contract_version(iface); + if (contractVersionIface.has_value() && contractVersionOtherIface.has_value() && contractVersionIface.value() != contractVersionOtherIface.value()) + return contractVersionIface.value() < contractVersionOtherIface.value(); + + //compare versions + auto versionIface = get_version(iface); + auto versionOtherIface = get_version(iface); + if (versionIface.has_value() && versionOtherIface.has_value() && versionIface.value() != versionOtherIface.value()) + return versionIface.value() < versionOtherIface.value(); + + //compare strings + return iface.TypeNamespace() == otherIface.TypeNamespace() ? iface.TypeName() < otherIface.TypeName() : iface.TypeNamespace() < otherIface.TypeNamespace(); + }); } - std::optional> get_fast_abi_ifaces_except_default(TypeDef const& classType) + struct fast_abi_class { - std::vector fast_abi_ifaces; - if (has_attribute(classType, "Windows.Foundation.Metadata"sv, "FastAbiAttribute"sv)) + const TypeDef class_type; + const TypeDef default_interface; + const std::vector other_interfaces; + + fast_abi_class(TypeDef class_type, TypeDef default_interface, std::vector other_interfaces) + : class_type(class_type), default_interface(default_interface), other_interfaces(other_interfaces) + { + int vtable_start_index = 6; + add_property_caches(default_interface, vtable_start_index); + vtable_start_index += distance(default_interface.MethodList()) + get_class_hierarchy_index(class_type); + for (auto&& other_iface : other_interfaces) + { + other_interfaces_cache.insert(std::pair(other_iface.TypeNamespace(), other_iface.TypeName())); + add_property_caches(other_iface, vtable_start_index); + vtable_start_index += distance(other_iface.MethodList()); + } + } + + bool contains_other_interface(TypeDef const& iface) { - fast_abi_ifaces = get_exclusive_interfaces_except_default(classType); - std::sort(fast_abi_ifaces.begin(), fast_abi_ifaces.end(), [](TypeDef const& iface, TypeDef const& otherIface) + return other_interfaces_cache.find(std::pair(iface.TypeNamespace(), iface.TypeName())) != other_interfaces_cache.end(); + } + + std::pair find_property_setter(std::string_view property_name) + { + return property_setters_cache[property_name]; + } + + bool contains_setter(std::string_view property_name) + { + return property_setters_cache.find(property_name) != property_setters_cache.end(); + } + + bool contains_getter(std::string_view property_name) + { + return property_getters_cache.find(property_name) != property_getters_cache.end(); + } + + private: + std::set> other_interfaces_cache; + std::map> property_setters_cache; + std::set property_getters_cache; + + void add_property_caches(TypeDef const& iface, int vtable_start_index) + { + for (auto prop : iface.PropertyList()) + { + auto&& [getter, setter] = get_property_methods(prop); + if (getter) + { + property_getters_cache.insert(prop.Name()); + } + if (setter) { - // compare relative contracts - auto relativeContractValueIface = -1 * get_number_of_attributes(iface, "Windows.Foundation.Metadata"sv, "PreviousContractVersionAttribute"sv); - auto relativeContractValueOtherIface = -1 * get_number_of_attributes(otherIface, "Windows.Foundation.Metadata"sv, "PreviousContractVersionAttribute"sv); - if (relativeContractValueIface != relativeContractValueOtherIface) - return relativeContractValueIface < relativeContractValueOtherIface; - - //compare contract versions if they exist - auto contractVersionIface = get_contract_version(iface); - auto contractVersionOtherIface = get_contract_version(iface); - if (contractVersionIface.has_value() && contractVersionOtherIface.has_value() && contractVersionIface.value() != contractVersionOtherIface.value()) - return contractVersionIface.value() < contractVersionOtherIface.value(); - - //compare versions - auto versionIface = get_version(iface); - auto versionOtherIface = get_version(iface); - if (versionIface.has_value() && versionOtherIface.has_value() && versionIface.value() != versionOtherIface.value()) - return versionIface.value() < versionOtherIface.value(); - - //compare strings - return iface.TypeNamespace() == otherIface.TypeNamespace() ? iface.TypeName() < otherIface.TypeName() : iface.TypeNamespace() < otherIface.TypeNamespace(); - }); - return fast_abi_ifaces; + property_setters_cache[prop.Name()] = std::pair(setter, vtable_start_index); + } + } } - return {}; + }; + + std::optional get_fast_abi_class_for_interface(TypeDef const& iface) + { + auto fast_abi_class_type = find_fast_abi_class_type(iface); + if (!fast_abi_class_type.has_value()) + return {}; + auto [default_iface, other_ifaces] = get_default_and_exclusive_interfaces(fast_abi_class_type.value()); + sort_fast_abi_ifaces(other_ifaces); + return fast_abi_class(fast_abi_class_type.value(), default_iface, other_ifaces); } } diff --git a/src/get_testwinrt.cmd b/src/get_testwinrt.cmd index 9188b2f86..b822f0f25 100644 --- a/src/get_testwinrt.cmd +++ b/src/get_testwinrt.cmd @@ -14,7 +14,7 @@ git checkout -f master if ErrorLevel 1 popd & exit /b !ErrorLevel! git fetch -f if ErrorLevel 1 popd & exit /b !ErrorLevel! -git reset -q --hard e7682136641caf9713268761ec8188ca452e85f9 +git reset -q --hard 7ed167bbe771c2c14781efe1444cbea01e6ae057 if ErrorLevel 1 popd & exit /b !ErrorLevel! echo Restoring Nuget %this_dir%.nuget\nuget.exe restore From 53c821c6e4e40c50d90f17c6e018e63a41b9fdac Mon Sep 17 00:00:00 2001 From: Ujjwal Chadha Date: Wed, 19 Jan 2022 10:35:46 -0800 Subject: [PATCH 04/13] fixes --- src/Tests/UnitTest/TestComponent_Tests.cs | 3 +++ src/cswinrt/code_writers.h | 2 ++ src/cswinrt/helpers.h | 4 ++-- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Tests/UnitTest/TestComponent_Tests.cs b/src/Tests/UnitTest/TestComponent_Tests.cs index 3517eae70..c28c90375 100644 --- a/src/Tests/UnitTest/TestComponent_Tests.cs +++ b/src/Tests/UnitTest/TestComponent_Tests.cs @@ -874,8 +874,11 @@ public void Fast_Abi_Composition() var sv = compositor.CreateSpriteVisual(); sv.Offset = 10; sv.StartAnimationGroup(); + Assert.Equal("", sv.Serialize(100)); Assert.Equal(10, sv.Offset); Assert.Equal(10, sv.Pad); + sv.ObjectProperty = new List { 1, 2, 3 }; + Assert.Equal(3, ((List)sv.ObjectProperty).Count); } #endif // Nota Bene: this test case must always remain the final one diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 6d82d00a1..5f33b5477 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -5175,10 +5175,12 @@ AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(@), s if (!m_fast_abi_class.has_value() || !is_interfaces_equal(m_fast_abi_class.value().default_interface, type)) { + // TODO: refactor auto& nullopt = std::nullopt; write_interface_members(w, type, 6, nullopt); return; } + // Constant for 6 auto vtable_start_index = 6; write_interface_members(w, type, 6, std::ref(m_fast_abi_class.value())); vtable_start_index += distance(type.MethodList()) + get_class_hierarchy_index(m_fast_abi_class.value().class_type); diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index e656495a8..b4f84e94e 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -1106,13 +1106,13 @@ namespace cswinrt //compare contract versions if they exist auto contractVersionIface = get_contract_version(iface); - auto contractVersionOtherIface = get_contract_version(iface); + auto contractVersionOtherIface = get_contract_version(otherIface); if (contractVersionIface.has_value() && contractVersionOtherIface.has_value() && contractVersionIface.value() != contractVersionOtherIface.value()) return contractVersionIface.value() < contractVersionOtherIface.value(); //compare versions auto versionIface = get_version(iface); - auto versionOtherIface = get_version(iface); + auto versionOtherIface = get_version(otherIface); if (versionIface.has_value() && versionOtherIface.has_value() && versionIface.value() != versionOtherIface.value()) return versionIface.value() < versionOtherIface.value(); From ccfce79b151803373cd88ebe2777a469b57d51e3 Mon Sep 17 00:00:00 2001 From: Ujjwal Chadha Date: Wed, 19 Jan 2022 14:38:06 -0800 Subject: [PATCH 05/13] Reset code writers to master --- .../MatchingRefApiCompatBaseline.net5.0.txt | 5 +- src/cswinrt/code_writers.h | 99 +++---------------- 2 files changed, 18 insertions(+), 86 deletions(-) diff --git a/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net5.0.txt b/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net5.0.txt index 8348e50c2..f0b556312 100644 --- a/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net5.0.txt +++ b/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net5.0.txt @@ -10,13 +10,14 @@ TypesMustExist : Type 'WinRT.ComWrappersHelper' does not exist in the reference MembersMustExist : Member 'public WinRT.ObjectReference WinRT.ComWrappersSupport.GetObjectReferenceForInterface(System.IntPtr)' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public void WinRT.ComWrappersSupport.RegisterObjectForInterface(System.Object, System.IntPtr, System.Runtime.InteropServices.CreateObjectFlags)' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'protected void WinRT.IObjectReference.AddRef(System.Boolean)' does not exist in the reference but it does exist in the implementation. +MembersMustExist : Member 'public WinRT.ObjectReference WinRT.IObjectReference.AsKnownPtr(System.IntPtr)' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public System.Int32 WinRT.IObjectReference.TryAs(System.Guid, System.IntPtr)' does not exist in the reference but it does exist in the implementation. +MembersMustExist : Member 'public WinRT.IObjectReference WinRT.MarshalInspectable.CreateMarshaler(T, System.Guid, System.Boolean)' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public void WinRT.MarshalString..ctor(System.String)' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public WinRT.MarshalString.Pinnable WinRT.MarshalString.CreatePinnable(System.String)' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public System.IntPtr WinRT.MarshalString.GetAbi()' does not exist in the reference but it does exist in the implementation. MembersMustExist : Member 'public System.IntPtr WinRT.MarshalString.GetAbi(WinRT.MarshalString.Pinnable)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.IObjectReference WinRT.MarshalInspectable.CreateMarshaler(T, System.Guid, System.Boolean)' does not exist in the reference but it does exist in the implementation. TypesMustExist : Type 'WinRT.MarshalString.Pinnable' does not exist in the reference but it does exist in the implementation. TypesMustExist : Type 'WinRT.Interop.IWeakReference' does not exist in the reference but it does exist in the implementation. TypesMustExist : Type 'WinRT.Interop.IWeakReferenceSource' does not exist in the reference but it does exist in the implementation. -Total Issues: 20 +Total Issues: 21 diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index abbfb1f4a..551e497ab 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -3058,7 +3058,7 @@ private % AsInternal(InterfaceTag<%> _) => % ?? Make_%(); db_path.stem().string()); } - auto get_invoke_info(writer& w, MethodDef const& method, uint32_t const& method_start_index = 6) + auto get_invoke_info(writer& w, MethodDef const& method) { TypeDef const& type = method.Parent(); if (!settings.netstandard_compat && distance(type.GenericParam()) == 0) @@ -3066,7 +3066,7 @@ db_path.stem().string()); return std::pair{ w.write_temp("(*(delegate* unmanaged[Stdcall]<%, int>**)ThisPtr)[%]", bind(method_signature { method }), - get_vmethod_index(type, method) + method_start_index /* number of methods in IInspectable + previous methods if fastabi*/), + get_vmethod_index(type, method) + 6 /* number of methods in IInspectable */), false }; } @@ -3091,7 +3091,7 @@ db_path.stem().string()); void write_event_source_generic_args(writer& w, cswinrt::type_semantics eventTypeSemantics); - void write_event_source_ctor(writer& w, Event const& evt, int index, int member_invoke_start_index) + void write_event_source_ctor(writer& w, Event const& evt, int index) { if (for_typedef(w, get_type_semantics(evt.EventType()), [&](TypeDef const& eventType) { @@ -3103,8 +3103,8 @@ db_path.stem().string()); %, %))", bind(eventType), -get_invoke_info(w, add, member_invoke_start_index).first, -get_invoke_info(w, remove, member_invoke_start_index).first, +get_invoke_info(w, add).first, +get_invoke_info(w, remove).first, index); return true; } @@ -3122,8 +3122,8 @@ new %%(_obj, %))", bind(get_type_semantics(evt.EventType())), bind(get_type_semantics(evt.EventType())), - get_invoke_info(w, add, member_invoke_start_index).first, - get_invoke_info(w, remove, member_invoke_start_index).first, + get_invoke_info(w, add).first, + get_invoke_info(w, remove).first, index); } @@ -3161,9 +3161,7 @@ private static global::System.Runtime.CompilerServices.ConditionalWeakTable> fast_abi_class_val = {}) + void write_interface_member_signatures(writer& w, TypeDef const& type) { for (auto&& method : type.MethodList()) { @@ -3185,11 +3183,6 @@ private static global::System.Runtime.CompilerServices.ConditionalWeakTable %.Unsubscribe(value); } } - void write_interface_members(writer& w, TypeDef const& type, uint32_t const& invoke_start_index = 6, std::optional> fast_abi_class_val = {}) + void write_interface_members(writer& w, TypeDef const& type) { if (is_exclusive_to(type)) { @@ -4142,7 +4135,7 @@ remove => %.Unsubscribe(value); continue; } method_signature signature{ method }; - auto [invoke_target, is_generic] = get_invoke_info(w, method, invoke_start_index); + auto [invoke_target, is_generic] = get_invoke_info(w, method); w.write(R"( %unsafe %% %%(%) {% @@ -4170,13 +4163,6 @@ remove => %.Unsubscribe(value); for (auto&& prop : type.PropertyList()) { auto [getter, setter] = get_property_methods(prop); - if (setter && !getter && fast_abi_class_val.has_value() - && fast_abi_class_val.value().get().contains_other_interface(type) - && fast_abi_class_val.value().get().contains_getter(prop.Name())) - { - continue; //setter was already written when the getter was encountered - } - std::optional> overridden_setter = {}; w.write(R"( %unsafe % %% { @@ -4194,7 +4180,7 @@ bind([&](writer& w) if (getter) { - auto [invoke_target, is_generic] = get_invoke_info(w, getter, invoke_start_index); + auto [invoke_target, is_generic] = get_invoke_info(w, getter); auto signature = method_signature(getter); auto marshalers = get_abi_marshalers(w, signature, is_generic, prop.Name()); w.write(R"(get @@ -4217,9 +4203,7 @@ return %; auto getter_cast = settings.netstandard_compat ? "As<%>()"s : "((%)(IWinRTObject)this)"s; w.write("get{ return " + getter_cast + ".%; }\n", getter_interface, prop.Name()); } - setter = overridden_setter.has_value() ? overridden_setter.value().first : setter; - auto [invoke_target, is_generic] = get_invoke_info(w, setter, - overridden_setter.has_value() ? overridden_setter.value().second : invoke_start_index); + auto [invoke_target, is_generic] = get_invoke_info(w, setter); auto signature = method_signature(setter); auto marshalers = get_abi_marshalers(w, signature, is_generic, prop.Name()); marshalers[0].param_name = "value"; @@ -5771,14 +5755,6 @@ IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, void write_interface(writer& w, TypeDef const& type) { - auto m_fast_abi_class = get_fast_abi_class_for_interface(type); - if (m_fast_abi_class.has_value() - && m_fast_abi_class.value().contains_other_interface(type) - && !settings.netstandard_compat) - { - return; - } - XLANG_ASSERT(get_category(type) == category::interface_type); auto type_name = write_type_name_temp(w, type, "%", typedef_name_type::CCW); @@ -5794,23 +5770,7 @@ IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, is_exclusive_to(type) || (is_projection_internal(type) || settings.embedded) ? "internal" : "public", type_name, bind(type, object_type{}, false, false), - [&](writer& w) - { - if (m_fast_abi_class.has_value() - && !settings.netstandard_compat - && is_interfaces_equal(m_fast_abi_class.value().default_interface, type)) - { - write_interface_member_signatures(w, type, std::optional(std::ref(m_fast_abi_class.value()))); - for (auto&& fast_abi_iface : m_fast_abi_class.value().other_interfaces) - { - write_interface_member_signatures(w, fast_abi_iface, std::optional(std::ref(m_fast_abi_class.value()))); - } - } - else - { - write_interface_member_signatures(w, type); - } - } + bind(type) ); } @@ -5879,7 +5839,7 @@ public static Guid PIID = Vftbl.PIID; int index = 0; for (auto&& evt : type.EventList()) { - w.write("_% = %;\n", evt.Name(), bind(evt, index++, 6)); + w.write("_% = %;\n", evt.Name(), bind(evt, index++)); } }, [&](writer& w) { @@ -5944,6 +5904,7 @@ public static Guid PIID = Vftbl.PIID; { bool is_generic = distance(type.GenericParam()) > 0; + XLANG_ASSERT(get_category(type) == category::interface_type); auto type_name = write_type_name_temp(w, type, "%", typedef_name_type::ABI); auto nongenerics_class = w.write_temp("%_Delegates", bind(type, typedef_name_type::ABI, false)); @@ -6411,36 +6372,6 @@ private struct InterfaceTag{}; bind([&](writer& w) { bool has_base_type = !std::holds_alternative(get_type_semantics(type.Extends())); - if (is_fast_abi_class(type)) - { - int hierarchy_index = get_class_hierarchy_index(type); - if (hierarchy_index > 0 || !type.Flags().Sealed()) - { - auto default_interface = get_default_interface(type); - auto default_iface_method_count = 0; - for_typedef(w, get_type_semantics(default_interface), [&](TypeDef iface) - { - default_iface_method_count = distance(iface.MethodList()); - }); - - w.write(R"( -protected unsafe % List> GetDefaultFactories() -{ -var list = new List>(); -% -return list; -})", - hierarchy_index == 0 ? "virtual" : "override", - bind([&](writer& w) - { - for (auto i = 0; i < hierarchy_index; i++) - { - w.write("list.Add(() => (*(delegate* unmanaged[Stdcall]**)_inner.ThisPtr)[%](_inner.ThisPtr));\n", 6 + default_iface_method_count + i); - } - })); - } - } - if (!type.Flags().Sealed()) { w.write(R"( From f3e747423b7c9266dca42c1268ef30b47ba5a969 Mon Sep 17 00:00:00 2001 From: Ujjwal Chadha Date: Fri, 21 Jan 2022 15:37:43 -0800 Subject: [PATCH 06/13] Update code_writers --- src/cswinrt/code_writers.h | 173 ++++++++++++++++++++++++++----------- src/cswinrt/helpers.h | 46 ++++++++-- 2 files changed, 160 insertions(+), 59 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 551e497ab..7c150f441 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -7,6 +7,8 @@ #include #include +#define INSPECTABLE_METHOD_COUNT 6 + namespace cswinrt { using namespace winmd::reader; @@ -2010,6 +2012,10 @@ ComWrappersSupport.RegisterObjectForInterface(this, ThisPtr); { return; } + if (is_fast_abi_class(classType) && is_exclusive_to(ifaceType) && !is_default_interface(ii)) // fast abi non default interface + { + return; + } auto objrefname = bind(semantics); @@ -2021,7 +2027,11 @@ private IObjectReference Make__%() objrefname, objrefname); - if (replaceDefaultByInner && has_attribute(ii, "Windows.Foundation.Metadata", "DefaultAttribute") && distance(ifaceType.GenericParam()) == 0) + if (!classType.Flags().Sealed() && is_fast_abi_class(classType) && is_exclusive_to(ifaceType) && is_default_interface(ii)) + { + w.write(R"(global::System.Threading.Interlocked.CompareExchange(ref __%, GetDefaultFactories()[%](), null);)", objrefname, get_class_hierarchy_index(classType)); + } + else if (replaceDefaultByInner && has_attribute(ii, "Windows.Foundation.Metadata", "DefaultAttribute") && distance(ifaceType.GenericParam()) == 0) { w.write(R"(global::System.Threading.Interlocked.CompareExchange(ref __%, _inner, null);)", objrefname); } @@ -2886,9 +2896,14 @@ remove => %.ErrorsChanged -= value; throw_invalid("Could not find property getter interface"); } + + void write_class_members(writer& w, TypeDef const& type, bool wrapper_type) { std::map>, std::optional>>> properties; + auto fast_abi_class_val = get_fast_abi_class_for_class(type); + auto default_type_semantics = get_default_iface_as_type_sem(type); + for (auto&& ii : type.InterfaceImpl()) { auto semantics = get_type_semantics(ii.Interface()); @@ -2901,7 +2916,11 @@ remove => %.ErrorsChanged -= value; auto is_default_interface = has_attribute(ii, "Windows.Foundation.Metadata", "DefaultAttribute"); auto static_iface_target = w.write_temp("%", bind(semantics, typedef_name_type::StaticAbiClass, true)); auto target = wrapper_type ? write_type_name_temp(w, interface_type, "((%) _comp)") : - (is_default_interface ? "_default" : write_type_name_temp(w, interface_type, "AsInternal(new InterfaceTag<%>())")); + (is_default_interface ? "_default" : write_type_name_temp(w, interface_type, "AsInternal(new InterfaceTag<%>())")); + + auto is_fast_abi_iface = fast_abi_class_val.has_value() && is_exclusive_to(interface_type) && !settings.netstandard_compat; + auto semantics_for_abi_call = is_fast_abi_iface ? default_type_semantics : semantics; + if (!is_default_interface && !wrapper_type) { if (settings.netstandard_compat || is_manually_generated_iface(interface_type)) @@ -2918,7 +2937,7 @@ private % AsInternal(InterfaceTag<%> _) => % ?? Make_%(); bool call_static_method = !(settings.netstandard_compat || wrapper_type || is_manually_generated_iface(interface_type)); - if(auto mapping = get_mapped_type(interface_type.TypeNamespace(), interface_type.TypeName()); mapping && mapping->has_custom_members_output) + if (auto mapping = get_mapped_type(interface_type.TypeNamespace(), interface_type.TypeName()); mapping && mapping->has_custom_members_output) { bool is_private = is_implemented_as_private_mapped_interface(w, type, interface_type); auto objref_name = w.write_temp("%", bind(semantics)); @@ -2930,11 +2949,10 @@ private % AsInternal(InterfaceTag<%> _) => % ?? Make_%(); auto is_protected_interface = has_attribute(ii, "Windows.Foundation.Metadata", "ProtectedAttribute"); auto platform_attribute = write_platform_attribute_temp(w, interface_type); - - w.write_each(interface_type.MethodList(), type, is_overridable_interface, is_protected_interface, target, platform_attribute, call_static_method ? std::optional(semantics) : std::nullopt); - w.write_each(interface_type.EventList(), type, is_overridable_interface, is_protected_interface, target, platform_attribute, call_static_method ? std::optional(semantics) : std::nullopt); - + w.write_each(interface_type.MethodList(), type, is_overridable_interface, is_protected_interface, target, platform_attribute, call_static_method ? std::optional(semantics_for_abi_call) : std::nullopt); + w.write_each(interface_type.EventList(), type, is_overridable_interface, is_protected_interface, target, platform_attribute, call_static_method ? std::optional(semantics_for_abi_call) : std::nullopt); + // Merge property getters/setters, since such may be defined across interfaces for (auto&& prop : interface_type.PropertyList()) { @@ -2943,7 +2961,7 @@ private % AsInternal(InterfaceTag<%> _) => % ?? Make_%(); auto prop_type = write_prop_type(w, prop); auto is_private = getter && is_implemented_as_private_method(w, type, getter); // for explicitly implemented interfaces, assume there is always a get. auto property_name = is_private ? w.write_temp("%.%", interface_name, prop.Name()) : std::string(prop.Name()); - auto [prop_targets, inserted] = properties.try_emplace(property_name, + auto [prop_targets, inserted] = properties.try_emplace(property_name, prop_type, getter ? target : "", getter ? platform_attribute : "", @@ -2952,8 +2970,8 @@ private % AsInternal(InterfaceTag<%> _) => % ?? Make_%(); is_overridable_interface, !is_protected_interface && !is_overridable_interface, // By default, an overridable member is protected. is_private, - call_static_method && getter ? std::optional(std::pair(semantics, prop)) : std::nullopt, - call_static_method && setter ? std::optional(std::pair(semantics, prop)) : std::nullopt + call_static_method && getter ? std::optional(std::pair(semantics_for_abi_call, prop)) : std::nullopt, + call_static_method && setter ? std::optional(std::pair(semantics_for_abi_call, prop)) : std::nullopt ); if (!inserted) { @@ -2964,14 +2982,14 @@ private % AsInternal(InterfaceTag<%> _) => % ?? Make_%(); XLANG_ASSERT(getter_target.empty()); getter_target = target; getter_platform = platform_attribute; - getter_prop = call_static_method ? std::optional(std::pair(semantics, prop)) : std::nullopt; + getter_prop = call_static_method ? std::optional(std::pair(semantics_for_abi_call, prop)) : std::nullopt; } if (setter) { XLANG_ASSERT(setter_target.empty()); setter_target = target; setter_platform = platform_attribute; - setter_prop = call_static_method ? std::optional(std::pair(semantics, prop)) : std::nullopt; + setter_prop = call_static_method ? std::optional(std::pair(semantics_for_abi_call, prop)) : std::nullopt; } is_overridable |= is_overridable_interface; is_public |= !is_overridable_interface && !is_protected_interface; @@ -2986,21 +3004,21 @@ private % AsInternal(InterfaceTag<%> _) => % ?? Make_%(); bind(interface_type, typedef_name_type::CCW, false), prop.Name(), bind([&](writer& w) - { - bool base_getter{}; - std::string base_getter_platform_attribute{}; - TypeDef getter_property_iface; - if (!getter) { - auto property_interface = find_property_interface(w, interface_type, prop.Name()); - base_getter = property_interface.second; - getter_property_iface = property_interface.first; - base_getter_platform_attribute = write_platform_attribute_temp(w, property_interface.first); + bool base_getter{}; + std::string base_getter_platform_attribute{}; + TypeDef getter_property_iface; + if (!getter) + { + auto property_interface = find_property_interface(w, interface_type, prop.Name()); + base_getter = property_interface.second; + getter_property_iface = property_interface.first; + base_getter_platform_attribute = write_platform_attribute_temp(w, property_interface.first); - } - if (getter || base_getter) - { - w.write("%get => %; ", base_getter_platform_attribute, bind([&](writer& w) { + } + if (getter || base_getter) + { + w.write("%get => %; ", base_getter_platform_attribute, bind([&](writer& w) { if (call_static_method) { auto iface = base_getter ? getter_property_iface : prop.Parent(); @@ -3011,14 +3029,14 @@ private % AsInternal(InterfaceTag<%> _) => % ?? Make_%(); { w.write("%%", is_private ? target + "." : "", prop.Name()); } - })); - } - }), + })); + } + }), bind([&](writer& w) - { - if (setter) { - w.write("set => %;", bind([&](writer& w) { + if (setter) + { + w.write("set => %;", bind([&](writer& w) { if (call_static_method) { w.write("%", bind(prop.Parent(), prop, @@ -3028,16 +3046,16 @@ private % AsInternal(InterfaceTag<%> _) => % ?? Make_%(); { w.write("%% = value", is_private ? target + "." : "", prop.Name()); } - })); - } - })); + })); + } + })); } } }; for_typedef(w, semantics, [&](auto type) - { - write_class_interface(type); - }); + { + write_class_interface(type); + }); } // Write properties with merged accessors @@ -3058,7 +3076,7 @@ private % AsInternal(InterfaceTag<%> _) => % ?? Make_%(); db_path.stem().string()); } - auto get_invoke_info(writer& w, MethodDef const& method) + auto get_invoke_info(writer& w, MethodDef const& method, uint32_t const& abi_methods_start_index = INSPECTABLE_METHOD_COUNT) { TypeDef const& type = method.Parent(); if (!settings.netstandard_compat && distance(type.GenericParam()) == 0) @@ -3066,7 +3084,7 @@ db_path.stem().string()); return std::pair{ w.write_temp("(*(delegate* unmanaged[Stdcall]<%, int>**)ThisPtr)[%]", bind(method_signature { method }), - get_vmethod_index(type, method) + 6 /* number of methods in IInspectable */), + get_vmethod_index(type, method) + abi_methods_start_index /* number of methods in IInspectable + previous methods if fastabi*/), false }; } @@ -3091,7 +3109,7 @@ db_path.stem().string()); void write_event_source_generic_args(writer& w, cswinrt::type_semantics eventTypeSemantics); - void write_event_source_ctor(writer& w, Event const& evt, int index) + void write_event_source_ctor(writer& w, Event const& evt, int index, uint32_t const& abi_methods_start_index) { if (for_typedef(w, get_type_semantics(evt.EventType()), [&](TypeDef const& eventType) { @@ -3103,8 +3121,8 @@ db_path.stem().string()); %, %))", bind(eventType), -get_invoke_info(w, add).first, -get_invoke_info(w, remove).first, +get_invoke_info(w, add, abi_methods_start_index).first, +get_invoke_info(w, remove, abi_methods_start_index).first, index); return true; } @@ -3122,8 +3140,8 @@ new %%(_obj, %))", bind(get_type_semantics(evt.EventType())), bind(get_type_semantics(evt.EventType())), - get_invoke_info(w, add).first, - get_invoke_info(w, remove).first, + get_invoke_info(w, add, abi_methods_start_index).first, + get_invoke_info(w, remove, abi_methods_start_index).first, index); } @@ -4256,10 +4274,9 @@ remove } } - void write_static_abi_class_members(writer& w, TypeDef const& iface) + void write_static_abi_class_members(writer& w, TypeDef const& iface, uint32_t const& abi_methods_start_index) { bool generic_type = distance(iface.GenericParam()) > 0; - auto init_call_variables = [&](writer& w) { if (generic_type) @@ -4276,7 +4293,7 @@ remove continue; } method_signature signature{ method }; - auto [invoke_target, is_generic] = get_invoke_info(w, method); + auto [invoke_target, is_generic] = get_invoke_info(w, method, abi_methods_start_index); w.write(R"( public static unsafe %% %(IObjectReference %%%) {%%} @@ -4297,7 +4314,7 @@ public static unsafe %% %(IObjectReference %%%) if (getter) { - auto [invoke_target, is_generic] = get_invoke_info(w, getter); + auto [invoke_target, is_generic] = get_invoke_info(w, getter, abi_methods_start_index); auto signature = method_signature(getter); auto marshalers = get_abi_marshalers(w, signature, is_generic, prop.Name()); w.write(R"(public static unsafe % get_%(IObjectReference %) @@ -4311,7 +4328,7 @@ public static unsafe %% %(IObjectReference %%%) } if (setter) { - auto [invoke_target, is_generic] = get_invoke_info(w, setter); + auto [invoke_target, is_generic] = get_invoke_info(w, setter, abi_methods_start_index); auto signature = method_signature(setter); auto marshalers = get_abi_marshalers(w, signature, is_generic, prop.Name()); marshalers[0].param_name = "value"; @@ -4348,7 +4365,7 @@ return (eventSource.Subscribe, eventSource.Unsubscribe); generic_type ? "_genericObj" : "_obj", evt.Name(), bind(init_call_variables), - bind(evt, index) + bind(evt, index, abi_methods_start_index) ); index++; } @@ -5839,7 +5856,7 @@ public static Guid PIID = Vftbl.PIID; int index = 0; for (auto&& evt : type.EventList()) { - w.write("_% = %;\n", evt.Name(), bind(evt, index++)); + w.write("_% = %;\n", evt.Name(), bind(evt, index++, INSPECTABLE_METHOD_COUNT)); } }, [&](writer& w) { @@ -5890,6 +5907,15 @@ public static Guid PIID = Vftbl.PIID; void write_static_abi_classes(writer& w, TypeDef const& iface) { + auto fast_abi_class_val = get_fast_abi_class_for_interface(iface); + if (fast_abi_class_val.has_value()) + { + if (!is_interfaces_equal(fast_abi_class_val.value().default_interface, iface)) + { + return; + } + } + w.write(R"(% static class % { % @@ -5897,7 +5923,20 @@ public static Guid PIID = Vftbl.PIID; )", is_exclusive_to(iface) ? "internal" : internal_if_embedded(), bind(iface, typedef_name_type::StaticAbiClass, false), - bind(iface)); + [&](writer& w) { + if (!fast_abi_class_val.has_value()) { + write_static_abi_class_members(w, iface, INSPECTABLE_METHOD_COUNT); + return; + } + auto abi_methods_start_index = INSPECTABLE_METHOD_COUNT; + write_static_abi_class_members(w, fast_abi_class_val.value().default_interface, abi_methods_start_index); + abi_methods_start_index += distance(fast_abi_class_val.value().default_interface.MethodList()) + get_class_hierarchy_index(fast_abi_class_val.value().class_type); + for (auto&& other_iface : fast_abi_class_val.value().other_interfaces) + { + write_static_abi_class_members(w, other_iface, abi_methods_start_index); + abi_methods_start_index += distance(other_iface.MethodList()); + } + }); } bool write_abi_interface(writer& w, TypeDef const& type) @@ -6371,6 +6410,36 @@ private struct InterfaceTag{}; type_name, bind([&](writer& w) { + if (is_fast_abi_class(type)) + { + int hierarchy_index = get_class_hierarchy_index(type); + if (!type.Flags().Sealed() || hierarchy_index > 0) + { + auto default_interface = get_default_interface(type); + auto default_iface_method_count = 0; + for_typedef(w, get_type_semantics(default_interface), [&](TypeDef iface) + { + default_iface_method_count = distance(iface.MethodList()); + }); + + w.write(R"( +protected unsafe % List> GetDefaultFactories() +{ +var list = new List>(); +% +return list; +})", + hierarchy_index == 0 ? "virtual" : "override", + bind([&](writer& w) + { + for (auto i = 0; i < hierarchy_index; i++) + { + w.write("list.Add(() => ((IWinRTObject)this).NativeObject.AsKnownPtr((*(delegate* unmanaged[Stdcall]**)_inner.ThisPtr)[%](_inner.ThisPtr)));\n", INSPECTABLE_METHOD_COUNT + default_iface_method_count + i); + } + w.write("list.Add(() => _inner);"); + })); + } + } bool has_base_type = !std::holds_alternative(get_type_semantics(type.Extends())); if (!type.Flags().Sealed()) { diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index 79e3436b7..fbc5e2127 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -6,6 +6,7 @@ namespace cswinrt using namespace winmd::reader; std::string get_mapped_element_type(ElementType elementType); + bool is_default_interface(InterfaceImpl const& ifaceImpl); static inline bool starts_with(std::string_view const& value, std::string_view const& match) noexcept { @@ -444,7 +445,7 @@ namespace cswinrt for (auto&& impl : impls) { - if (has_attribute(impl, "Windows.Foundation.Metadata", "DefaultAttribute")) + if (is_default_interface(impl)) { return impl.Interface(); } @@ -1054,7 +1055,12 @@ namespace cswinrt bool is_fast_abi_class(TypeDef const& type) { - return has_attribute(type, "Windows.Foundation.Metadata"sv, "FastAbiAttribute"sv); + return has_attribute(type, "Windows.Foundation.Metadata"sv, "FastAbiAttribute"sv) && !settings.netstandard_compat; + } + + bool is_default_interface(InterfaceImpl const& ifaceImpl) + { + return has_attribute(ifaceImpl, "Windows.Foundation.Metadata", "DefaultAttribute"); } std::optional find_fast_abi_class_type(TypeDef const& iface) @@ -1064,7 +1070,7 @@ namespace cswinrt { auto sys_type = get_attribute_value(exclusiveToAttribute, 0); TypeDef exclusiveToClass = iface.get_cache().find_required(sys_type.name); - if (!has_attribute(exclusiveToClass, "Windows.Foundation.Metadata"sv, "FastAbiAttribute"sv)) + if (!is_fast_abi_class(exclusiveToClass)) { return {}; } @@ -1080,7 +1086,7 @@ namespace cswinrt for (auto&& ifaceImpl : classType.InterfaceImpl()) { auto&& sem = get_type_semantics(ifaceImpl.Interface()); - if (has_attribute(ifaceImpl, "Windows.Foundation.Metadata", "DefaultAttribute")) + if (is_default_interface(ifaceImpl)) { exclusive_ifaces.first = std::get(sem); } @@ -1095,6 +1101,18 @@ namespace cswinrt return exclusive_ifaces; } + type_semantics get_default_iface_as_type_sem(TypeDef const& classType) + { + for (auto&& ifaceImpl : classType.InterfaceImpl()) + { + auto&& sem = get_type_semantics(ifaceImpl.Interface()); + if (is_default_interface(ifaceImpl)) + { + return sem; + } + } + } + void sort_fast_abi_ifaces(std::vector& fast_abi_ifaces) { std::sort(fast_abi_ifaces.begin(), fast_abi_ifaces.end(), [](TypeDef const& iface, TypeDef const& otherIface) @@ -1184,13 +1202,27 @@ namespace cswinrt } }; + std::optional get_fast_abi_class_for_class(TypeDef const& classType) + { + if (!is_fast_abi_class(classType)) + { + return {}; + } + auto [default_iface, other_ifaces] = get_default_and_exclusive_interfaces(classType); + sort_fast_abi_ifaces(other_ifaces); + return fast_abi_class(classType, default_iface, other_ifaces); + } + std::optional get_fast_abi_class_for_interface(TypeDef const& iface) { auto fast_abi_class_type = find_fast_abi_class_type(iface); if (!fast_abi_class_type.has_value()) return {}; - auto [default_iface, other_ifaces] = get_default_and_exclusive_interfaces(fast_abi_class_type.value()); - sort_fast_abi_ifaces(other_ifaces); - return fast_abi_class(fast_abi_class_type.value(), default_iface, other_ifaces); + return get_fast_abi_class_for_class(fast_abi_class_type.value()); + } + + auto&& is_fast_abi_enabled() + { + return true; } } From 3428176f5649d7e21c9927ffaab80af65037eba5 Mon Sep 17 00:00:00 2001 From: Ujjwal Chadha Date: Thu, 27 Jan 2022 15:11:41 -0800 Subject: [PATCH 07/13] PR feedback --- src/WinRT.Runtime/ObjectReference.cs | 12 ++++++------ src/cswinrt/code_writers.h | 12 ++++++------ src/cswinrt/helpers.h | 17 +++++++++++------ 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/WinRT.Runtime/ObjectReference.cs b/src/WinRT.Runtime/ObjectReference.cs index 3c03f875a..06302f378 100644 --- a/src/WinRT.Runtime/ObjectReference.cs +++ b/src/WinRT.Runtime/ObjectReference.cs @@ -187,12 +187,12 @@ public virtual unsafe ObjectReference AsKnownPtr(IntPtr ptr) return objRef; } - // Used only as part of the GetInterface implementation where the - // result is an reference passed across the ABI and doesn't need to - // be tracked as an internal reference. This is separate to handle - // tear off aggregate scenario where releasing an reference can end up - // deleting the tear off interface. - public virtual unsafe int TryAs(Guid iid, out IntPtr ppv) + // Used only as part of the GetInterface implementation where the + // result is an reference passed across the ABI and doesn't need to + // be tracked as an internal reference. This is separate to handle + // tear off aggregate scenario where releasing an reference can end up + // deleting the tear off interface. + public virtual unsafe int TryAs(Guid iid, out IntPtr ppv) { ppv = IntPtr.Zero; ThrowIfDisposed(); diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 7c150f441..682fa0e1f 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -2029,7 +2029,7 @@ private IObjectReference Make__%() if (!classType.Flags().Sealed() && is_fast_abi_class(classType) && is_exclusive_to(ifaceType) && is_default_interface(ii)) { - w.write(R"(global::System.Threading.Interlocked.CompareExchange(ref __%, GetDefaultFactories()[%](), null);)", objrefname, get_class_hierarchy_index(classType)); + w.write(R"(global::System.Threading.Interlocked.CompareExchange(ref __%, GetDefaultInterfaceAccessors()[%](), null);)", objrefname, get_class_hierarchy_index(classType)); } else if (replaceDefaultByInner && has_attribute(ii, "Windows.Foundation.Metadata", "DefaultAttribute") && distance(ifaceType.GenericParam()) == 0) { @@ -6423,20 +6423,20 @@ private struct InterfaceTag{}; }); w.write(R"( -protected unsafe % List> GetDefaultFactories() +protected unsafe % Func[] GetDefaultInterfaceAccessors() { -var list = new List>(); +return new Func[] { % -return list; +}; })", hierarchy_index == 0 ? "virtual" : "override", bind([&](writer& w) { for (auto i = 0; i < hierarchy_index; i++) { - w.write("list.Add(() => ((IWinRTObject)this).NativeObject.AsKnownPtr((*(delegate* unmanaged[Stdcall]**)_inner.ThisPtr)[%](_inner.ThisPtr)));\n", INSPECTABLE_METHOD_COUNT + default_iface_method_count + i); + w.write("() => ((IWinRTObject)this).NativeObject.AsKnownPtr((*(delegate* unmanaged[Stdcall]**)_inner.ThisPtr)[%](_inner.ThisPtr)),\n", INSPECTABLE_METHOD_COUNT + default_iface_method_count + i); } - w.write("list.Add(() => _inner);"); + w.write("() => _inner"); })); } } diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index fbc5e2127..a304f0196 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -1065,6 +1065,11 @@ namespace cswinrt std::optional find_fast_abi_class_type(TypeDef const& iface) { + static std::map> cache; + if (cache.find(iface) != cache.end()) + { + return cache[iface]; + } auto exclusiveToAttribute = get_attribute(iface, "Windows.Foundation.Metadata"sv, "ExclusiveToAttribute"sv); if (exclusiveToAttribute) { @@ -1072,10 +1077,13 @@ namespace cswinrt TypeDef exclusiveToClass = iface.get_cache().find_required(sys_type.name); if (!is_fast_abi_class(exclusiveToClass)) { + cache[iface] = {}; return {}; } + cache[iface] = exclusiveToClass; return exclusiveToClass; } + cache[iface] = {}; return {}; } @@ -1135,7 +1143,7 @@ namespace cswinrt if (versionIface.has_value() && versionOtherIface.has_value() && versionIface.value() != versionOtherIface.value()) return versionIface.value() < versionOtherIface.value(); - //compare strings + //compare type names return iface.TypeNamespace() == otherIface.TypeNamespace() ? iface.TypeName() < otherIface.TypeName() : iface.TypeNamespace() < otherIface.TypeNamespace(); }); } @@ -1216,13 +1224,10 @@ namespace cswinrt std::optional get_fast_abi_class_for_interface(TypeDef const& iface) { auto fast_abi_class_type = find_fast_abi_class_type(iface); - if (!fast_abi_class_type.has_value()) + if (!fast_abi_class_type.has_value()) { return {}; + } return get_fast_abi_class_for_class(fast_abi_class_type.value()); } - auto&& is_fast_abi_enabled() - { - return true; - } } From 82fa850554b4b0bdd56209bf59ac92110f4f9b59 Mon Sep 17 00:00:00 2001 From: Ujjwal Chadha Date: Thu, 3 Feb 2022 13:41:05 -0800 Subject: [PATCH 08/13] Add more tests and reduce array allocation --- src/Tests/UnitTest/TestComponent_Tests.cs | 8 +++++++- src/cswinrt/code_writers.h | 24 ++++++++++------------- src/get_testwinrt.cmd | 2 +- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/Tests/UnitTest/TestComponent_Tests.cs b/src/Tests/UnitTest/TestComponent_Tests.cs index 307fa7d67..d9d397b8d 100644 --- a/src/Tests/UnitTest/TestComponent_Tests.cs +++ b/src/Tests/UnitTest/TestComponent_Tests.cs @@ -974,7 +974,7 @@ public void TestVectorGetMany() Assert.True(composableObjects.GetRange(1, 3).SequenceEqual(interfaceSubset)); } -#if NET5_0 +#if NET [Fact] public void Fast_Abi_Simple() { @@ -982,6 +982,12 @@ public void Fast_Abi_Simple() Assert.Equal("Method1", simple.Method1()); Assert.Equal("Method2", simple.Method2()); Assert.Equal("Method3", simple.Method3()); + Assert.Equal("Method4", simple.Method4()); + Assert.Equal("Method5", simple.Method5()); + Assert.Equal("Method6", simple.Method6()); + Assert.Equal("Method7", simple.Method7()); + Assert.Equal("Method8", simple.Method8()); + Assert.Equal("Method9", simple.Method9()); simple.Property1 = "Property1"; simple.Property3 = "Property3"; Assert.Equal("Property1", simple.Property1); diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 682fa0e1f..6ab7c1fdf 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -2029,7 +2029,7 @@ private IObjectReference Make__%() if (!classType.Flags().Sealed() && is_fast_abi_class(classType) && is_exclusive_to(ifaceType) && is_default_interface(ii)) { - w.write(R"(global::System.Threading.Interlocked.CompareExchange(ref __%, GetDefaultInterfaceAccessors()[%](), null);)", objrefname, get_class_hierarchy_index(classType)); + w.write(R"(global::System.Threading.Interlocked.CompareExchange(ref __%, GetDefaultInterfaceObjRef(%), null);)", objrefname, get_class_hierarchy_index(classType)); } else if (replaceDefaultByInner && has_attribute(ii, "Windows.Foundation.Metadata", "DefaultAttribute") && distance(ifaceType.GenericParam()) == 0) { @@ -6423,21 +6423,17 @@ private struct InterfaceTag{}; }); w.write(R"( -protected unsafe % Func[] GetDefaultInterfaceAccessors() +protected unsafe % IObjectReference GetDefaultInterfaceObjRef(int hierarchyIndex) { -return new Func[] { -% -}; +if (hierarchyIndex < %) +{ + return ((IWinRTObject)this).NativeObject.AsKnownPtr((*(delegate* unmanaged[Stdcall]**)_inner.ThisPtr)[% + hierarchyIndex](_inner.ThisPtr)); +} +return _inner; })", hierarchy_index == 0 ? "virtual" : "override", - bind([&](writer& w) - { - for (auto i = 0; i < hierarchy_index; i++) - { - w.write("() => ((IWinRTObject)this).NativeObject.AsKnownPtr((*(delegate* unmanaged[Stdcall]**)_inner.ThisPtr)[%](_inner.ThisPtr)),\n", INSPECTABLE_METHOD_COUNT + default_iface_method_count + i); - } - w.write("() => _inner"); - })); + hierarchy_index, + INSPECTABLE_METHOD_COUNT + default_iface_method_count); } } bool has_base_type = !std::holds_alternative(get_type_semantics(type.Extends())); @@ -7537,4 +7533,4 @@ bind(invokeMethodSig)); typeNameToDefinitionMap[eventTypeCode] = eventClass; } } -} +} \ No newline at end of file diff --git a/src/get_testwinrt.cmd b/src/get_testwinrt.cmd index 845602b30..88776b311 100644 --- a/src/get_testwinrt.cmd +++ b/src/get_testwinrt.cmd @@ -14,7 +14,7 @@ git checkout -f master if ErrorLevel 1 popd & exit /b !ErrorLevel! git fetch -f if ErrorLevel 1 popd & exit /b !ErrorLevel! -git reset -q --hard 8700e96e29f27349ad8f904fcd27fb7e07b98a88 +git reset -q --hard 0c024d092a215533aad298fd32b6132ba4d072fd if ErrorLevel 1 popd & exit /b !ErrorLevel! echo Restoring Nuget %this_dir%.nuget\nuget.exe restore From 99078e12d338f93f1e1b5c5b20effabd54e5709d Mon Sep 17 00:00:00 2001 From: Ujjwal Chadha Date: Tue, 15 Feb 2022 20:24:46 -0800 Subject: [PATCH 09/13] Add fast abi benchmarks --- src/Benchmarks/Benchmarks.manifest | 4 +++ src/Benchmarks/QueryInterface.cs | 30 ++++++++++++++++++++++- src/Tests/UnitTest/TestComponent_Tests.cs | 3 +-- src/cswinrt/code_writers.h | 2 +- src/cswinrt/helpers.h | 4 +-- 5 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/Benchmarks/Benchmarks.manifest b/src/Benchmarks/Benchmarks.manifest index d6ed65d18..2647ba3fa 100644 --- a/src/Benchmarks/Benchmarks.manifest +++ b/src/Benchmarks/Benchmarks.manifest @@ -15,6 +15,10 @@ name="BenchmarkComponent.EventOperations" threadingModel="both" xmlns="urn:schemas-microsoft-com:winrt.v1" /> + diff --git a/src/Benchmarks/QueryInterface.cs b/src/Benchmarks/QueryInterface.cs index 1ef9d3302..ff20c917d 100644 --- a/src/Benchmarks/QueryInterface.cs +++ b/src/Benchmarks/QueryInterface.cs @@ -9,12 +9,14 @@ public class QueryInterfacePerf { ClassWithMultipleInterfaces instance; ChatMessage message; + ClassWithFastAbi fastAbiInstance; [GlobalSetup] public void Setup() { instance = new ClassWithMultipleInterfaces(); message = new ChatMessage(); + fastAbiInstance = new ClassWithFastAbi(); } [Benchmark] @@ -29,6 +31,18 @@ public int QueryNonDefaultInterface() return instance.IntProperty; } + [Benchmark] + public int QueryFastAbiDefaultInterface() + { + return fastAbiInstance.DefaultIntProperty; + } + + [Benchmark] + public int QueryFastAbiNonDefaultInterface() + { + return fastAbiInstance.NonDefaultIntProperty; + } + [Benchmark] public bool QueryNonDefaultInterface2() { @@ -79,7 +93,7 @@ public object DynamicCast() return (ClassWithMarshalingRoutines)instance.NewObject(); } - // The following 2 benchmarks try to benchmark the time taken for the first call + // The following 4 benchmarks try to benchmark the time taken for the first call // rather than the mean time over several calls. It has the overhead of the object // construction, but it can be used to track regressions to performance. [Benchmark] @@ -96,6 +110,20 @@ public int ConstructAndQueryNonDefaultInterfaceFirstCall() return instance2.IntProperty; } + [Benchmark] + public int ConstructAndQueryFastAbiDefaultInterfaceFirstCall() + { + var instance2 = new ClassWithFastAbi(); + return instance2.DefaultIntProperty; + } + + [Benchmark] + public int ConstructAndQueryFastAbiNonDefaultInterfaceFirstCall() + { + var instance2 = new ClassWithFastAbi(); + return instance2.NonDefaultIntProperty; + } + [Benchmark] public int StaticPropertyCall() { diff --git a/src/Tests/UnitTest/TestComponent_Tests.cs b/src/Tests/UnitTest/TestComponent_Tests.cs index d9d397b8d..beef474e2 100644 --- a/src/Tests/UnitTest/TestComponent_Tests.cs +++ b/src/Tests/UnitTest/TestComponent_Tests.cs @@ -974,7 +974,6 @@ public void TestVectorGetMany() Assert.True(composableObjects.GetRange(1, 3).SequenceEqual(interfaceSubset)); } -#if NET [Fact] public void Fast_Abi_Simple() { @@ -1015,7 +1014,7 @@ public void Fast_Abi_Composition() sv.ObjectProperty = new List { 1, 2, 3 }; Assert.Equal(3, ((List)sv.ObjectProperty).Count); } -#endif + // Nota Bene: this test case must always remain the final one [Fact] public void Z_Check_Coverage() diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 6ab7c1fdf..1773b710e 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -5910,7 +5910,7 @@ public static Guid PIID = Vftbl.PIID; auto fast_abi_class_val = get_fast_abi_class_for_interface(iface); if (fast_abi_class_val.has_value()) { - if (!is_interfaces_equal(fast_abi_class_val.value().default_interface, iface)) + if (!interfaces_equal(fast_abi_class_val.value().default_interface, iface)) { return; } diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index a304f0196..c6eb81dd3 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -1023,7 +1023,7 @@ namespace cswinrt return 0; } - bool is_interfaces_equal(TypeDef const& interface1, TypeDef const& interface2) + bool interfaces_equal(TypeDef const& interface1, TypeDef const& interface2) { return interface1.TypeNamespace() == interface2.TypeNamespace() && interface1.TypeName() == interface2.TypeName(); @@ -1077,13 +1077,11 @@ namespace cswinrt TypeDef exclusiveToClass = iface.get_cache().find_required(sys_type.name); if (!is_fast_abi_class(exclusiveToClass)) { - cache[iface] = {}; return {}; } cache[iface] = exclusiveToClass; return exclusiveToClass; } - cache[iface] = {}; return {}; } From 3f06f061938b395d96c4c68da434607f60b25bb1 Mon Sep 17 00:00:00 2001 From: Ujjwal Chadha Date: Tue, 15 Feb 2022 22:03:00 -0800 Subject: [PATCH 10/13] Update get_testwinrt.cmd --- src/get_testwinrt.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/get_testwinrt.cmd b/src/get_testwinrt.cmd index 6c03b990f..1e3196fe1 100644 --- a/src/get_testwinrt.cmd +++ b/src/get_testwinrt.cmd @@ -14,7 +14,7 @@ git checkout -f master if ErrorLevel 1 popd & exit /b !ErrorLevel! git fetch -f if ErrorLevel 1 popd & exit /b !ErrorLevel! -git reset -q --hard a2ed82862b5340ff4f25734d154452287d8f39b9 +git reset -q --hard 6573bf54bd4e4349272b5e30a0f526c4dfe1f535 if ErrorLevel 1 popd & exit /b !ErrorLevel! echo Restoring Nuget %this_dir%.nuget\nuget.exe restore From 3fb347728583d09a4c96d33092cd4e309a793ee3 Mon Sep 17 00:00:00 2001 From: Ujjwal Chadha Date: Wed, 16 Feb 2022 18:50:44 -0800 Subject: [PATCH 11/13] Add fast abi composable benchmarks --- src/Benchmarks/Benchmarks.manifest | 4 +++ src/Benchmarks/QueryInterface.cs | 54 ++++++++++++++++++++++++++++++ src/get_testwinrt.cmd | 2 +- 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/Benchmarks/Benchmarks.manifest b/src/Benchmarks/Benchmarks.manifest index 87c10d674..2d621768d 100644 --- a/src/Benchmarks/Benchmarks.manifest +++ b/src/Benchmarks/Benchmarks.manifest @@ -23,6 +23,10 @@ name="BenchmarkComponent.ClassWithFastAbi" threadingModel="both" xmlns="urn:schemas-microsoft-com:winrt.v1" /> + diff --git a/src/Benchmarks/QueryInterface.cs b/src/Benchmarks/QueryInterface.cs index ced88122c..b287b675f 100644 --- a/src/Benchmarks/QueryInterface.cs +++ b/src/Benchmarks/QueryInterface.cs @@ -28,6 +28,7 @@ public class QueryInterfacePerf ManagedObjectWithInterfaces managedObject; ManagedComposableObjectWithInterfaces composableObject; ClassWithFastAbi fastAbiInstance; + ClassWithFastAbiDerived fastAbiDerivedInstance; [GlobalSetup] public void Setup() @@ -37,6 +38,7 @@ public void Setup() managedObject = new ManagedObjectWithInterfaces(); composableObject = new ManagedComposableObjectWithInterfaces(); fastAbiInstance = new ClassWithFastAbi(); + fastAbiDerivedInstance = new ClassWithFastAbiDerived(); } [Benchmark] @@ -63,6 +65,30 @@ public int QueryFastAbiNonDefaultInterface() return fastAbiInstance.NonDefaultIntProperty; } + [Benchmark] + public int QueryFastAbiDerivedDefaultInterface() + { + return fastAbiDerivedInstance.DerivedDefaultIntProperty; + } + + [Benchmark] + public int QueryFastAbiComposedNonDefaultInterface() + { + return fastAbiDerivedInstance.DerivedNonDefaultIntProperty; + } + + [Benchmark] + public int QueryFastAbiComposedBaseDefaultInterface() + { + return fastAbiDerivedInstance.DefaultIntProperty; + } + + [Benchmark] + public int QueryFastAbiComposedBaseNonDefaultInterface() + { + return fastAbiDerivedInstance.NonDefaultIntProperty; + } + [Benchmark] public bool QueryNonDefaultInterface2() { @@ -144,6 +170,34 @@ public int ConstructAndQueryFastAbiNonDefaultInterfaceFirstCall() return instance2.NonDefaultIntProperty; } + [Benchmark] + public int ConstructAndQueryFastAbiDerivedDefaultInterfaceFirstCall() + { + var instance2 = new ClassWithFastAbiDerived(); + return instance2.DerivedDefaultIntProperty; + } + + [Benchmark] + public int ConstructAndQueryFastAbiDerivedNonDefaultInterfaceFirstCall() + { + var instance2 = new ClassWithFastAbiDerived(); + return instance2.DerivedNonDefaultIntProperty; + } + + [Benchmark] + public int ConstructAndQueryFastAbiDerivedBaseDefaultInterfaceFirstCall() + { + var instance2 = new ClassWithFastAbiDerived(); + return instance2.DefaultIntProperty; + } + + [Benchmark] + public int ConstructAndQueryFastAbiDerivedBaseNonDefaultInterfaceFirstCall() + { + var instance2 = new ClassWithFastAbiDerived(); + return instance2.NonDefaultIntProperty; + } + [Benchmark] public int StaticPropertyCall() { diff --git a/src/get_testwinrt.cmd b/src/get_testwinrt.cmd index 1e3196fe1..b68510ebe 100644 --- a/src/get_testwinrt.cmd +++ b/src/get_testwinrt.cmd @@ -14,7 +14,7 @@ git checkout -f master if ErrorLevel 1 popd & exit /b !ErrorLevel! git fetch -f if ErrorLevel 1 popd & exit /b !ErrorLevel! -git reset -q --hard 6573bf54bd4e4349272b5e30a0f526c4dfe1f535 +git reset -q --hard 761842f968d4f9b661653cce9e26534be11174fb if ErrorLevel 1 popd & exit /b !ErrorLevel! echo Restoring Nuget %this_dir%.nuget\nuget.exe restore From 61b377cec73c50ac54f2b64b0532f3413656816d Mon Sep 17 00:00:00 2001 From: Ujjwal Chadha Date: Wed, 23 Feb 2022 17:44:55 -0800 Subject: [PATCH 12/13] Add static and factory tests --- src/Tests/UnitTest/TestComponent_Tests.cs | 5 +++++ src/WinRT.Runtime/ObjectReference.cs | 12 ++++++++++++ src/cswinrt/code_writers.h | 4 ++-- src/cswinrt/helpers.h | 1 - src/get_testwinrt.cmd | 2 +- 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/src/Tests/UnitTest/TestComponent_Tests.cs b/src/Tests/UnitTest/TestComponent_Tests.cs index 7f2ea5fea..a493a3fc7 100644 --- a/src/Tests/UnitTest/TestComponent_Tests.cs +++ b/src/Tests/UnitTest/TestComponent_Tests.cs @@ -1089,6 +1089,11 @@ public void Box_DateTime() public void Fast_Abi_Simple() { var simple = new test_component_fast.Simple(); + Assert.NotNull(simple); + simple = new test_component_fast.Simple("Hello"); + Assert.Equal("Hello", simple.Property1); + Assert.Equal("StaticMethod1", test_component_fast.Simple.StaticMethod1()); + Assert.Equal("StaticMethod2", test_component_fast.Simple.StaticMethod2()); Assert.Equal("Method1", simple.Method1()); Assert.Equal("Method2", simple.Method2()); Assert.Equal("Method3", simple.Method3()); diff --git a/src/WinRT.Runtime/ObjectReference.cs b/src/WinRT.Runtime/ObjectReference.cs index ed48a3605..4b52a49d6 100644 --- a/src/WinRT.Runtime/ObjectReference.cs +++ b/src/WinRT.Runtime/ObjectReference.cs @@ -577,6 +577,18 @@ protected override unsafe void Release() Context.DisposeContextCallback(_contextCallbackPtr); } + public override ObjectReference AsKnownPtr(IntPtr ptr) + { + AddRef(true); + var objRef = new ObjectReferenceWithContext(ptr, Context.GetContextCallback(), Context.GetContextToken()) + { + IsAggregated = IsAggregated, + PreventReleaseOnDispose = IsAggregated, + ReferenceTrackerPtr = ReferenceTrackerPtr + }; + return objRef; + } + public override unsafe int TryAs(Guid iid, out ObjectReference objRef) { objRef = null; diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 21114fa06..de8c4c5ee 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -5916,7 +5916,7 @@ public static Guid PIID = Vftbl.PIID; auto fast_abi_class_val = get_fast_abi_class_for_interface(iface); if (fast_abi_class_val.has_value()) { - if (!interfaces_equal(fast_abi_class_val.value().default_interface, iface)) + if (fast_abi_class_val.value().contains_other_interface(iface)) { return; } @@ -5930,7 +5930,7 @@ public static Guid PIID = Vftbl.PIID; is_exclusive_to(iface) ? "internal" : internal_if_embedded(), bind(iface, typedef_name_type::StaticAbiClass, false), [&](writer& w) { - if (!fast_abi_class_val.has_value()) { + if (!fast_abi_class_val.has_value() || (!fast_abi_class_val.value().contains_other_interface(iface) && !interfaces_equal(fast_abi_class_val.value().default_interface, iface))) { write_static_abi_class_members(w, iface, INSPECTABLE_METHOD_COUNT); return; } diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index c6eb81dd3..f56286232 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -1088,7 +1088,6 @@ namespace cswinrt std::pair> get_default_and_exclusive_interfaces(TypeDef const& classType) { std::pair> exclusive_ifaces; - std::vector exclusive_interfaces; for (auto&& ifaceImpl : classType.InterfaceImpl()) { auto&& sem = get_type_semantics(ifaceImpl.Interface()); diff --git a/src/get_testwinrt.cmd b/src/get_testwinrt.cmd index b68510ebe..222ef31e9 100644 --- a/src/get_testwinrt.cmd +++ b/src/get_testwinrt.cmd @@ -14,7 +14,7 @@ git checkout -f master if ErrorLevel 1 popd & exit /b !ErrorLevel! git fetch -f if ErrorLevel 1 popd & exit /b !ErrorLevel! -git reset -q --hard 761842f968d4f9b661653cce9e26534be11174fb +git reset -q --hard 53e87ecb7dd2fc883062ea9d95296768b604f058 if ErrorLevel 1 popd & exit /b !ErrorLevel! echo Restoring Nuget %this_dir%.nuget\nuget.exe restore From ed48c48b1ba26df79285e6d9d6dbfe392d70be21 Mon Sep 17 00:00:00 2001 From: Ujjwal Chadha Date: Wed, 23 Feb 2022 18:23:06 -0800 Subject: [PATCH 13/13] Fix warning as error --- src/cswinrt/code_writers.h | 3 +-- src/cswinrt/cswinrt.vcxproj | 2 +- src/cswinrt/helpers.h | 1 + 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index de8c4c5ee..5f06c6837 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -2902,7 +2902,6 @@ remove => %.ErrorsChanged -= value; { std::map>, std::optional>>> properties; auto fast_abi_class_val = get_fast_abi_class_for_class(type); - auto default_type_semantics = get_default_iface_as_type_sem(type); for (auto&& ii : type.InterfaceImpl()) { @@ -2919,7 +2918,7 @@ remove => %.ErrorsChanged -= value; (is_default_interface ? "_default" : write_type_name_temp(w, interface_type, "AsInternal(new InterfaceTag<%>())")); auto is_fast_abi_iface = fast_abi_class_val.has_value() && is_exclusive_to(interface_type) && !settings.netstandard_compat; - auto semantics_for_abi_call = is_fast_abi_iface ? default_type_semantics : semantics; + auto semantics_for_abi_call = is_fast_abi_iface ? get_default_iface_as_type_sem(type) : semantics; if (!is_default_interface && !wrapper_type) { diff --git a/src/cswinrt/cswinrt.vcxproj b/src/cswinrt/cswinrt.vcxproj index 1e8ae2f9e..674c12582 100644 --- a/src/cswinrt/cswinrt.vcxproj +++ b/src/cswinrt/cswinrt.vcxproj @@ -47,7 +47,7 @@ kernel32.lib;user32.lib;%(AdditionalDependencies);windowsapp.lib;advapi32.lib;shlwapi.lib - false + true diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index f56286232..9c2765971 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -1116,6 +1116,7 @@ namespace cswinrt return sem; } } + throw_invalid("Class does not have a default interface"); } void sort_fast_abi_ifaces(std::vector& fast_abi_ifaces)