From 27c7b095feded899bb77cef4ab9335fdf936c6ed Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 22 Dec 2025 20:53:47 -0800 Subject: [PATCH 1/8] Rename variable for get_Key method generation Renamed the 'currentMethod' variable to 'get_KeyMethod' in the IMapChangedEventArgs1Impl factory to better reflect its purpose and improve code clarity. --- ...ethodDefinitionFactory.IMapChangedEventArgs1Impl.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapChangedEventArgs1Impl.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapChangedEventArgs1Impl.cs index ba1fc64f3..44e0a7b8f 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapChangedEventArgs1Impl.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapChangedEventArgs1Impl.cs @@ -127,7 +127,7 @@ public static MethodDefinition Key( // // [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] // private static int get_Key(void* thisPtr, * result) - MethodDefinition currentMethod = new( + MethodDefinition get_KeyMethod = new( name: "get_Key"u8, attributes: MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, signature: MethodSignature.CreateStatic( @@ -146,8 +146,8 @@ public static MethodDefinition Key( CilInstruction call_catchStartMarshalException = new(Call, interopReferences.RestrictedErrorInfoExceptionMarshallerConvertToUnmanaged.Import(module)); CilInstruction nop_convertToUnmanaged = new(Nop); - // Create a method body for the 'get_Current' method - currentMethod.CilMethodBody = new CilMethodBody() + // Create a method body for the 'get_Key' method + get_KeyMethod.CilMethodBody = new CilMethodBody() { // Declare 1 variable: // [0]: 'int' (the 'HRESULT' to return) @@ -199,10 +199,10 @@ public static MethodDefinition Key( // Track the method for rewrite to marshal the result value emitState.TrackRetValValueMethodRewrite( retValType: keyType, - method: currentMethod, + method: get_KeyMethod, marker: nop_convertToUnmanaged); - return currentMethod; + return get_KeyMethod; } } } \ No newline at end of file From d54c3ffdd6a0035dc4d326f9efa2d86f69505c6d Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 22 Dec 2025 20:56:01 -0800 Subject: [PATCH 2/8] Add Conv_U opcode in IEnumerator1 builder Inserted the Conv_U opcode in the IL instruction sequence within InteropTypeDefinitionBuilder.IEnumerator1. This change likely ensures correct type conversion for native interop scenarios. --- .../Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs index 9eb83a639..aec259fba 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs @@ -100,6 +100,7 @@ public static void IIteratorMethods( { Stloc_1 }, { Ldloc_1 }, { Ldloca_S, loc_2_currentNative }, + { Conv_U }, { Ldloc_1 }, { Ldind_I }, { Ldfld, interopDefinitions.IEnumerator1Vftbl.GetField("get_Current"u8) }, From 59e607f34051da42fe75c5e12eced8a26df2076b Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 22 Dec 2025 21:02:51 -0800 Subject: [PATCH 3/8] Refactor IMapChangedEventArgs signatures to factory methods Replaced inline method signature definitions for IMapChangedEventArgs`1 with calls to dedicated factory methods in WellKnownTypeSignatureFactory. This improves maintainability and centralizes signature creation logic. --- .../WellKnownTypeDefinitionFactory.cs | 24 +++---------------- .../WellKnownTypeSignatureFactory.cs | 19 +++++++++++++++ 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/WellKnownTypeDefinitionFactory.cs b/src/WinRT.Interop.Generator/Factories/WellKnownTypeDefinitionFactory.cs index 2bacdf01d..d23c550b4 100644 --- a/src/WinRT.Interop.Generator/Factories/WellKnownTypeDefinitionFactory.cs +++ b/src/WinRT.Interop.Generator/Factories/WellKnownTypeDefinitionFactory.cs @@ -854,27 +854,9 @@ public static TypeDefinition IMapChangedEventArgsVftbl(InteropReferences interop MethodSignature getRuntimeClassNameType = WellKnownTypeSignatureFactory.GetRuntimeClassNameImpl(interopReferences); MethodSignature getTrustLevelType = WellKnownTypeSignatureFactory.GetTrustLevelImpl(interopReferences); - // Signature for 'delegate* unmanaged[MemberFunction]' - MethodSignature collectionChangeType = new( - attributes: CallingConventionAttributes.Unmanaged, - returnType: new CustomModifierTypeSignature( - modifierType: interopReferences.CallConvMemberFunction, - isRequired: false, - baseType: module.CorLibTypeFactory.Int32), - parameterTypes: [ - module.CorLibTypeFactory.Void.MakePointerType(), - interopReferences.CollectionChange.MakePointerType()]); - - // Signature for 'delegate* unmanaged[MemberFunction]' - MethodSignature keyType = new( - attributes: CallingConventionAttributes.Unmanaged, - returnType: new CustomModifierTypeSignature( - modifierType: interopReferences.CallConvMemberFunction, - isRequired: false, - baseType: module.CorLibTypeFactory.Int32), - parameterTypes: [ - module.CorLibTypeFactory.Void.MakePointerType(), - module.CorLibTypeFactory.Void.MakePointerType()]); + // Get the 'IMapChangedEventArgs`1' signatures + MethodSignature collectionChangeType = WellKnownTypeSignatureFactory.IMapChangedEventArgs1get_CollectionChangeImpl(interopReferences); + MethodSignature keyType = WellKnownTypeSignatureFactory.get_UntypedRetVal(interopReferences); // The vtable layout for 'IMapChangedEventArgs`1' looks like this: // diff --git a/src/WinRT.Interop.Generator/Factories/WellKnownTypeSignatureFactory.cs b/src/WinRT.Interop.Generator/Factories/WellKnownTypeSignatureFactory.cs index d3b3acf21..6bcc9a099 100644 --- a/src/WinRT.Interop.Generator/Factories/WellKnownTypeSignatureFactory.cs +++ b/src/WinRT.Interop.Generator/Factories/WellKnownTypeSignatureFactory.cs @@ -810,6 +810,25 @@ public static MethodSignature IDictionary2ClearImpl(InteropReferences interopRef return IList1ClearImpl(interopReferences); } + /// + /// Creates a type signature for the get_CollectionChange vtable entry for some map changed event args. + /// + /// The instance to use. + /// The resulting instance. + public static MethodSignature IMapChangedEventArgs1get_CollectionChangeImpl(InteropReferences interopReferences) + { + // Signature for 'delegate* unmanaged[MemberFunction]' + return new( + attributes: CallingConventionAttributes.Unmanaged, + returnType: new CustomModifierTypeSignature( + modifierType: interopReferences.CallConvMemberFunction, + isRequired: false, + baseType: interopReferences.CorLibTypeFactory.Int32), + parameterTypes: [ + interopReferences.CorLibTypeFactory.Void.MakePointerType(), + interopReferences.CollectionChange.MakePointerType()]); + } + /// /// Creates a type signature for in Guid values. /// From 273a1f947b82def9c344b0ba4da4a3c3e829bee1 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 22 Dec 2025 21:09:24 -0800 Subject: [PATCH 4/8] Refactor IMapChangedEventArgs1 Key method generation Moved the generation of the 'Key' method for IMapChangedEventArgs1 to a new InteropMethodDefinitionFactory.IMapChangedEventArgs1Methods class. The new implementation provides a complete method body and integrates with emit state for return value rewriting. Updated all call sites to use the new factory method and pass the emit state. --- ...DefinitionBuilder.IMapChangedEventArgs1.cs | 29 ++--- ...ionFactory.IMapChangedEventArgs1Methods.cs | 123 ++++++++++++++++++ .../Generation/InteropGenerator.Emit.cs | 1 + 3 files changed, 133 insertions(+), 20 deletions(-) create mode 100644 src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapChangedEventArgs1Methods.cs diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IMapChangedEventArgs1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IMapChangedEventArgs1.cs index 9bb8629da..e71bc9c99 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IMapChangedEventArgs1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IMapChangedEventArgs1.cs @@ -3,7 +3,6 @@ using System.Runtime.InteropServices; using AsmResolver.DotNet; -using AsmResolver.DotNet.Code.Cil; using AsmResolver.DotNet.Signatures; using AsmResolver.PE.DotNet.Metadata.Tables; using WindowsRuntime.InteropGenerator.Factories; @@ -27,12 +26,14 @@ public static class IMapChangedEventArgs1 /// The for the args type. /// The instance to use. /// The instance to use. + /// The emit state for this invocation. /// The interop module being built. /// The resulting methods type. public static void Methods( GenericInstanceTypeSignature argsType, InteropDefinitions interopDefinitions, InteropReferences interopReferences, + InteropGeneratorEmitState emitState, ModuleDefinition module, out TypeDefinition argsMethodsType) { @@ -70,30 +71,18 @@ public static void Methods( argsMethodsType.Methods.Add(collectionChangeMethod); - // Define the 'Key' method as follows: - // - // public static Key(WindowsRuntimeObjectReference thisReference) - MethodDefinition keyMethod = new( - name: "Key"u8, - attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, - signature: MethodSignature.CreateStatic( - returnType: elementType.Import(module), - parameterTypes: [interopReferences.WindowsRuntimeObjectReference.Import(module).ToReferenceTypeSignature()])); + // Define the 'Key' method + MethodDefinition keyMethod = InteropMethodDefinitionFactory.IMapChangedEventArgs1Methods.Key( + argsType: argsType, + interopDefinitions: interopDefinitions, + interopReferences: interopReferences, + emitState: emitState, + module: module); // Add and implement the 'Key' method argsMethodsType.AddMethodImplementation( declaration: interopReferences.IMapChangedEventArgsImpl1Key(elementType).Import(module), method: keyMethod); - - // Create a method body for the 'Key' method - keyMethod.CilMethodBody = new CilMethodBody() - { - Instructions = - { - { Ldnull }, - { Throw } // TODO - } - }; } /// diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapChangedEventArgs1Methods.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapChangedEventArgs1Methods.cs new file mode 100644 index 000000000..47de4d65d --- /dev/null +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapChangedEventArgs1Methods.cs @@ -0,0 +1,123 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using AsmResolver.DotNet; +using AsmResolver.DotNet.Code.Cil; +using AsmResolver.DotNet.Signatures; +using AsmResolver.PE.DotNet.Cil; +using AsmResolver.PE.DotNet.Metadata.Tables; +using WindowsRuntime.InteropGenerator.Generation; +using WindowsRuntime.InteropGenerator.References; +using static AsmResolver.PE.DotNet.Cil.CilOpCodes; + +namespace WindowsRuntime.InteropGenerator.Factories; + +/// +internal partial class InteropMethodDefinitionFactory +{ + /// + /// Helpers for method types for Windows.Foundation.Collections.IMapChangedEventArgs<K> interfaces. + /// + public static class IMapChangedEventArgs1Methods + { + /// + /// Creates a for the get_Key export method. + /// + /// The for the args type. + /// The instance to use. + /// The instance to use. + /// The emit state for this invocation. + /// The interop module being built. + public static MethodDefinition Key( + GenericInstanceTypeSignature argsType, + InteropDefinitions interopDefinitions, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + TypeSignature keyType = argsType.TypeArguments[0]; + TypeSignature keyAbiType = keyType.GetAbiType(interopReferences); + + // Define the 'Key' method as follows: + // + // public static Key(WindowsRuntimeObjectReference thisReference) + MethodDefinition keyMethod = new( + name: "Key"u8, + attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + signature: MethodSignature.CreateStatic( + returnType: keyType.Import(module), + parameterTypes: [interopReferences.WindowsRuntimeObjectReference.Import(module).ToReferenceTypeSignature()])) + { NoInlining = true }; + + // Declare the local variables: + // [0]: 'WindowsRuntimeObjectReferenceValue' (for 'thisValue') + // [1]: 'void*' (for 'thisPtr') + // [2]: '' (the native value that was retrieved) + CilLocalVariable loc_0_thisValue = new(interopReferences.WindowsRuntimeObjectReferenceValue.ToValueTypeSignature().Import(module)); + CilLocalVariable loc_1_thisPtr = new(module.CorLibTypeFactory.Void.MakePointerType()); + CilLocalVariable loc_2_keyNative = new(keyAbiType.Import(module)); + + // Jump labels + CilInstruction ldloca_s_0_tryStart = new(Ldloca_S, loc_0_thisValue); + CilInstruction ldloca_s_0_finallyStart = new(Ldloca_S, loc_0_thisValue); + CilInstruction nop_finallyEnd = new(Nop); + CilInstruction nop_returnValueRewrite = new(Nop); + + // Create a method body for the 'Key' method + keyMethod.CilMethodBody = new CilMethodBody() + { + LocalVariables = { loc_0_thisValue, loc_1_thisPtr, loc_2_keyNative }, + Instructions = + { + // Initialize 'thisValue' + { Ldarg_0 }, + { Callvirt, interopReferences.WindowsRuntimeObjectReferenceAsValue.Import(module) }, + { Stloc_0 }, + + // '.try' code + { ldloca_s_0_tryStart }, + { Call, interopReferences.WindowsRuntimeObjectReferenceValueGetThisPtrUnsafe.Import(module) }, + { Stloc_1 }, + { Ldloc_1 }, + { Ldloca_S, loc_2_keyNative }, + { Conv_U }, + { Ldloc_1 }, + { Ldind_I }, + { Ldfld, interopDefinitions.IMapChangedEventArgsVftbl.GetField("get_Key"u8) }, + { Calli, WellKnownTypeSignatureFactory.get_UntypedRetVal(interopReferences).Import(module).MakeStandAloneSignature() }, + { Call, interopReferences.RestrictedErrorInfoThrowExceptionForHR.Import(module) }, + { Leave_S, nop_finallyEnd.CreateLabel() }, + + // '.finally' code + { ldloca_s_0_finallyStart }, + { Call, interopReferences.WindowsRuntimeObjectReferenceValueDispose.Import(module) }, + { Endfinally }, + { nop_finallyEnd }, + + // Marshal and return the result + { nop_returnValueRewrite } + }, + ExceptionHandlers = + { + new CilExceptionHandler + { + HandlerType = CilExceptionHandlerType.Finally, + TryStart = ldloca_s_0_tryStart.CreateLabel(), + TryEnd = ldloca_s_0_finallyStart.CreateLabel(), + HandlerStart = ldloca_s_0_finallyStart.CreateLabel(), + HandlerEnd = nop_finallyEnd.CreateLabel() + } + } + }; + + // Track rewriting the return value for this method + emitState.TrackReturnValueMethodRewrite( + returnType: keyType, + method: keyMethod, + marker: nop_returnValueRewrite, + source: loc_2_keyNative); + + return keyMethod; + } + } +} \ No newline at end of file diff --git a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs index fff810735..d45f21950 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs @@ -1281,6 +1281,7 @@ private static void DefineIMapChangedEventArgsTypes( argsType: typeSignature, interopDefinitions: interopDefinitions, interopReferences: interopReferences, + emitState: emitState, module: module, argsMethodsType: out TypeDefinition argsMethodsType); From b876d4976427d4be095c4d0fac5038c4e050205a Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 22 Dec 2025 21:11:05 -0800 Subject: [PATCH 5/8] Replace typed with untyped return value signature Updated IAsyncInfoMethods to use get_UntypedRetVal instead of get_TypedRetVal for calli signatures. Removed the unused get_TypedRetVal method from WellKnownTypeSignatureFactory to simplify the code and avoid unnecessary type specificity. --- ...thodDefinitionFactory.IAsyncInfoMethods.cs | 2 +- .../WellKnownTypeSignatureFactory.cs | 20 ------------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IAsyncInfoMethods.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IAsyncInfoMethods.cs index d48778629..7ca960f53 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IAsyncInfoMethods.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IAsyncInfoMethods.cs @@ -308,7 +308,7 @@ public static MethodDefinition GetResults( { Ldloc_1 }, { Ldind_I }, { Ldfld, vftblField }, - { Calli, WellKnownTypeSignatureFactory.get_TypedRetVal(resultType.GetAbiType(interopReferences).MakePointerType(), interopReferences).Import(module).MakeStandAloneSignature() }, + { Calli, WellKnownTypeSignatureFactory.get_UntypedRetVal(interopReferences).Import(module).MakeStandAloneSignature() }, { Call, interopReferences.RestrictedErrorInfoThrowExceptionForHR.Import(module) }, { Leave_S, nop_finallyEnd.CreateLabel() }, diff --git a/src/WinRT.Interop.Generator/Factories/WellKnownTypeSignatureFactory.cs b/src/WinRT.Interop.Generator/Factories/WellKnownTypeSignatureFactory.cs index 6bcc9a099..9cd443c9f 100644 --- a/src/WinRT.Interop.Generator/Factories/WellKnownTypeSignatureFactory.cs +++ b/src/WinRT.Interop.Generator/Factories/WellKnownTypeSignatureFactory.cs @@ -203,26 +203,6 @@ public static MethodSignature set_Handler(InteropReferences interopReferences) interopReferences.CorLibTypeFactory.Void.MakePointerType()]); } - /// - /// Creates a type signature for the get accessor for some property returning a typed value. - /// - /// The type of return value. - /// The instance to use. - /// The resulting instance. - public static MethodSignature get_TypedRetVal(TypeSignature returnValue, InteropReferences interopReferences) - { - // Signature for 'delegate* unmanaged[MemberFunction]*, HRESULT>' - return new( - attributes: CallingConventionAttributes.Unmanaged, - returnType: new CustomModifierTypeSignature( - modifierType: interopReferences.CallConvMemberFunction, - isRequired: false, - baseType: interopReferences.CorLibTypeFactory.Int32), - parameterTypes: [ - interopReferences.CorLibTypeFactory.Void.MakePointerType(), - returnValue.MakePointerType()]); - } - /// /// Creates a type signature for the get accessor for some property returning an untyped value (any type). /// From 08fa1cd21f559ecd3da264b8b1212ce9eb8bcb37 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 22 Dec 2025 21:11:10 -0800 Subject: [PATCH 6/8] Add Conv_U opcode to KeyValuePair method IL Inserted the Conv_U opcode in the IL instruction sequence for KeyValuePair methods to ensure correct type conversion. This change improves the accuracy of the generated interop method definitions. --- .../InteropMethodDefinitionFactory.KeyValuePairMethods.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.KeyValuePairMethods.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.KeyValuePairMethods.cs index fbf6871ee..e4de7cb9e 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.KeyValuePairMethods.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.KeyValuePairMethods.cs @@ -70,6 +70,7 @@ public static MethodDefinition get_KeyOrValue( { { Ldarg_0 }, { Ldloca_S, loc_0_resultNative }, + { Conv_U }, { Ldarg_0 }, { Ldind_I }, { Ldfld, vftblType.GetField(vftblMethodName) }, From 185c022edaf222dd055caf0cfb0d265c8c515e78 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 22 Dec 2025 21:16:03 -0800 Subject: [PATCH 7/8] Refactor IEnumerator1Impl method generation Split HasCurrentOrMoveNext into get_HasCurrent and MoveNext methods for clarity and maintainability. Updated call sites to use the new methods and restructured method bodies for GetMany and HasCurrentOrMoveNext to better match their intended signatures and logic. --- ...teropTypeDefinitionBuilder.IEnumerator1.cs | 6 +- ...ethodDefinitionFactory.IEnumerator1Impl.cs | 163 +++++++++++------- 2 files changed, 102 insertions(+), 67 deletions(-) diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs index aec259fba..34741288d 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IEnumerator1.cs @@ -446,15 +446,13 @@ public static void ImplType( module: module); // Define the 'get_HasCurrent' method - MethodDefinition hasCurrentMethod = InteropMethodDefinitionFactory.IEnumerator1Impl.HasCurrentOrMoveNext( - nameUtf8: "get_HasCurrent"u8, + MethodDefinition hasCurrentMethod = InteropMethodDefinitionFactory.IEnumerator1Impl.get_HasCurrent( enumeratorType: enumeratorType, interopReferences: interopReferences, module: module); // Define the 'MoveNext' method - MethodDefinition moveNextMethod = InteropMethodDefinitionFactory.IEnumerator1Impl.HasCurrentOrMoveNext( - nameUtf8: "MoveNext"u8, + MethodDefinition moveNextMethod = InteropMethodDefinitionFactory.IEnumerator1Impl.MoveNext( enumeratorType: enumeratorType, interopReferences: interopReferences, module: module); diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IEnumerator1Impl.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IEnumerator1Impl.cs index 23789f3dd..de8b0dcb0 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IEnumerator1Impl.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IEnumerator1Impl.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using AsmResolver; using AsmResolver.DotNet; using AsmResolver.DotNet.Code.Cil; using AsmResolver.DotNet.Signatures; @@ -25,6 +26,42 @@ internal static partial class InteropMethodDefinitionFactory /// public static class IEnumerator1Impl { + /// + /// Creates a for the get_HasCurrent export method. + /// + /// The for the type. + /// The instance to use. + /// The interop module being built. + public static MethodDefinition get_HasCurrent( + GenericInstanceTypeSignature enumeratorType, + InteropReferences interopReferences, + ModuleDefinition module) + { + return HasCurrentOrMoveNext( + methodName: "get_HasCurrent"u8, + enumeratorType, + interopReferences: interopReferences, + module: module); + } + + /// + /// Creates a for the MoveNext export method. + /// + /// The for the type. + /// The instance to use. + /// The interop module being built. + public static MethodDefinition MoveNext( + GenericInstanceTypeSignature enumeratorType, + InteropReferences interopReferences, + ModuleDefinition module) + { + return HasCurrentOrMoveNext( + methodName: "MoveNext"u8, + enumeratorType, + interopReferences: interopReferences, + module: module); + } + /// /// Creates a for the get_Current export method. /// @@ -124,71 +161,76 @@ public static MethodDefinition get_Current( } /// - /// Creates a for the get_Current export method. + /// Creates a for the GetMany export method. /// - /// The name of the method to generate. /// The for the type. /// The instance to use. /// The interop module being built. - public static MethodDefinition HasCurrentOrMoveNext( - ReadOnlySpan nameUtf8, + public static MethodDefinition GetMany( GenericInstanceTypeSignature enumeratorType, InteropReferences interopReferences, ModuleDefinition module) { TypeSignature elementType = enumeratorType.TypeArguments[0]; - // Define the method as follows: + // Define the 'GetMany' method as follows: // // [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] - // private static int (void* thisPtr, bool* result) - MethodDefinition boolMethod = new( - name: nameUtf8, + // private static int GetMany(void* thisPtr, uint itemsSize, * items, uint* writtenCount) + MethodDefinition currentMethod = new( + name: "GetMany"u8, attributes: MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, signature: MethodSignature.CreateStatic( returnType: module.CorLibTypeFactory.Int32, parameterTypes: [ module.CorLibTypeFactory.Void.MakePointerType(), - module.CorLibTypeFactory.Boolean.MakePointerType()])) + module.CorLibTypeFactory.UInt32, + elementType.GetAbiType(interopReferences).Import(module).MakePointerType(), + module.CorLibTypeFactory.UInt32.MakePointerType()])) { CustomAttributes = { InteropCustomAttributeFactory.UnmanagedCallersOnly(interopReferences, module) } }; - // Get the reference to the target method to invoke - MemberReference adapterMethod = nameUtf8.SequenceEqual("get_HasCurrent"u8) - ? interopReferences.IEnumeratorAdapter1get_HasCurrent(elementType) - : interopReferences.IEnumeratorAdapter1MoveNext(elementType); - // Labels for jumps + CilInstruction ldc_I4_e_pointer = new(Ldc_I4, unchecked((int)0x80004003)); CilInstruction nop_beforeTry = new(Nop); - CilInstruction ldarg_1_tryStart = new(Ldarg_1); + CilInstruction ldarg_0_tryStart = new(Ldarg_0); CilInstruction ldloc_0_returnHResult = new(Ldloc_0); CilInstruction call_catchStartMarshalException = new(Call, interopReferences.RestrictedErrorInfoExceptionMarshallerConvertToUnmanaged.Import(module)); + CilInstruction nop_implementation = new(Nop); - // Create a method body for the 'get_HasCurrent' method - boolMethod.CilMethodBody = new CilMethodBody() + // Create a method body for the 'get_Current' method + currentMethod.CilMethodBody = new CilMethodBody() { - // Declare 1 variable: + // Declare 2 variables: // [0]: 'int' (the 'HRESULT' to return) - LocalVariables = { new CilLocalVariable(module.CorLibTypeFactory.Int32) }, + // [1]: 'IEnumeratorAdapter<>' (the adapter instance) + LocalVariables = + { + new CilLocalVariable(module.CorLibTypeFactory.Int32), + new CilLocalVariable(interopReferences.IEnumeratorAdapter1.MakeGenericReferenceType(elementType).Import(module)) + }, Instructions = { - // Return 'E_POINTER' if the argument is 'null' - { Ldarg_1 }, + // Return 'E_POINTER' if either pointer argument is 'null' + { Ldarg_2 }, + { Ldc_I4_0 }, + { Conv_U }, + { Beq_S, ldc_I4_e_pointer.CreateLabel() }, + { Ldarg_3 }, { Ldc_I4_0 }, { Conv_U }, { Bne_Un_S, nop_beforeTry.CreateLabel() }, - { Ldc_I4, unchecked((int)0x80004003) }, + { ldc_I4_e_pointer }, { Ret }, { nop_beforeTry }, // '.try' code - { ldarg_1_tryStart }, - { Ldarg_0 }, + { ldarg_0_tryStart }, { Call, interopReferences.ComInterfaceDispatchGetInstance.MakeGenericInstanceMethod(enumeratorType).Import(module) }, { Call, interopReferences.IEnumeratorAdapter1GetInstance(elementType).Import(module) }, - { Callvirt, adapterMethod.Import(module) }, - { Stind_I1 }, + { Stloc_1 }, + { nop_implementation }, { Ldc_I4_0 }, { Stloc_0 }, { Leave_S, ldloc_0_returnHResult.CreateLabel() }, @@ -207,7 +249,7 @@ public static MethodDefinition HasCurrentOrMoveNext( new CilExceptionHandler { HandlerType = CilExceptionHandlerType.Exception, - TryStart = ldarg_1_tryStart.CreateLabel(), + TryStart = ldarg_0_tryStart.CreateLabel(), TryEnd = call_catchStartMarshalException.CreateLabel(), HandlerStart = call_catchStartMarshalException.CreateLabel(), HandlerEnd = ldloc_0_returnHResult.CreateLabel(), @@ -216,80 +258,77 @@ public static MethodDefinition HasCurrentOrMoveNext( } }; - return boolMethod; + // TODO: replace 'nop_implementation' with the actual implementation of the method + + return currentMethod; } /// - /// Creates a for the GetMany export method. + /// Creates a for the get_Current export method. /// + /// The name of the method to generate. /// The for the type. /// The instance to use. /// The interop module being built. - public static MethodDefinition GetMany( + private static MethodDefinition HasCurrentOrMoveNext( + Utf8String methodName, GenericInstanceTypeSignature enumeratorType, InteropReferences interopReferences, ModuleDefinition module) { TypeSignature elementType = enumeratorType.TypeArguments[0]; - // Define the 'GetMany' method as follows: + // Define the method as follows: // // [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] - // private static int GetMany(void* thisPtr, uint itemsSize, * items, uint* writtenCount) - MethodDefinition currentMethod = new( - name: "GetMany"u8, + // private static int (void* thisPtr, bool* result) + MethodDefinition boolMethod = new( + name: methodName, attributes: MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, signature: MethodSignature.CreateStatic( returnType: module.CorLibTypeFactory.Int32, parameterTypes: [ module.CorLibTypeFactory.Void.MakePointerType(), - module.CorLibTypeFactory.UInt32, - elementType.GetAbiType(interopReferences).Import(module).MakePointerType(), - module.CorLibTypeFactory.UInt32.MakePointerType()])) + module.CorLibTypeFactory.Boolean.MakePointerType()])) { CustomAttributes = { InteropCustomAttributeFactory.UnmanagedCallersOnly(interopReferences, module) } }; + // Get the reference to the target method to invoke + MemberReference adapterMethod = methodName.AsSpan().SequenceEqual("get_HasCurrent"u8) + ? interopReferences.IEnumeratorAdapter1get_HasCurrent(elementType) + : interopReferences.IEnumeratorAdapter1MoveNext(elementType); + // Labels for jumps - CilInstruction ldc_I4_e_pointer = new(Ldc_I4, unchecked((int)0x80004003)); CilInstruction nop_beforeTry = new(Nop); - CilInstruction ldarg_0_tryStart = new(Ldarg_0); + CilInstruction ldarg_1_tryStart = new(Ldarg_1); CilInstruction ldloc_0_returnHResult = new(Ldloc_0); CilInstruction call_catchStartMarshalException = new(Call, interopReferences.RestrictedErrorInfoExceptionMarshallerConvertToUnmanaged.Import(module)); - CilInstruction nop_implementation = new(Nop); - // Create a method body for the 'get_Current' method - currentMethod.CilMethodBody = new CilMethodBody() + // Create a method body for the 'get_HasCurrent' method + boolMethod.CilMethodBody = new CilMethodBody() { - // Declare 2 variables: + // Declare 1 variable: // [0]: 'int' (the 'HRESULT' to return) - // [1]: 'IEnumeratorAdapter<>' (the adapter instance) - LocalVariables = - { - new CilLocalVariable(module.CorLibTypeFactory.Int32), - new CilLocalVariable(interopReferences.IEnumeratorAdapter1.MakeGenericReferenceType(elementType).Import(module)) - }, + LocalVariables = { new CilLocalVariable(module.CorLibTypeFactory.Int32) }, Instructions = { - // Return 'E_POINTER' if either pointer argument is 'null' - { Ldarg_2 }, - { Ldc_I4_0 }, - { Conv_U }, - { Beq_S, ldc_I4_e_pointer.CreateLabel() }, - { Ldarg_3 }, + // Return 'E_POINTER' if the argument is 'null' + { Ldarg_1 }, { Ldc_I4_0 }, { Conv_U }, { Bne_Un_S, nop_beforeTry.CreateLabel() }, - { ldc_I4_e_pointer }, + { Ldc_I4, unchecked((int)0x80004003) }, { Ret }, { nop_beforeTry }, // '.try' code - { ldarg_0_tryStart }, + { ldarg_1_tryStart }, + { Ldarg_0 }, { Call, interopReferences.ComInterfaceDispatchGetInstance.MakeGenericInstanceMethod(enumeratorType).Import(module) }, { Call, interopReferences.IEnumeratorAdapter1GetInstance(elementType).Import(module) }, - { Stloc_1 }, - { nop_implementation }, + { Callvirt, adapterMethod.Import(module) }, + { Stind_I1 }, { Ldc_I4_0 }, { Stloc_0 }, { Leave_S, ldloc_0_returnHResult.CreateLabel() }, @@ -308,7 +347,7 @@ public static MethodDefinition GetMany( new CilExceptionHandler { HandlerType = CilExceptionHandlerType.Exception, - TryStart = ldarg_0_tryStart.CreateLabel(), + TryStart = ldarg_1_tryStart.CreateLabel(), TryEnd = call_catchStartMarshalException.CreateLabel(), HandlerStart = call_catchStartMarshalException.CreateLabel(), HandlerEnd = ldloc_0_returnHResult.CreateLabel(), @@ -317,9 +356,7 @@ public static MethodDefinition GetMany( } }; - // TODO: replace 'nop_implementation' with the actual implementation of the method - - return currentMethod; + return boolMethod; } } } \ No newline at end of file From e0a14eca7b3c8a843e12b3689fc11f789f7b7652 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Mon, 22 Dec 2025 21:22:52 -0800 Subject: [PATCH 8/8] Refactor HasCurrentOrMoveNext to accept adapter method Updated the HasCurrentOrMoveNext method to take an explicit adapterMethod parameter instead of determining it internally. This change clarifies method usage and improves code maintainability by making dependencies explicit. --- ...eropMethodDefinitionFactory.IEnumerator1Impl.cs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IEnumerator1Impl.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IEnumerator1Impl.cs index de8b0dcb0..12462937c 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IEnumerator1Impl.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IEnumerator1Impl.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -using System; using AsmResolver; using AsmResolver.DotNet; using AsmResolver.DotNet.Code.Cil; @@ -37,8 +36,11 @@ public static MethodDefinition get_HasCurrent( InteropReferences interopReferences, ModuleDefinition module) { + TypeSignature elementType = enumeratorType.TypeArguments[0]; + return HasCurrentOrMoveNext( methodName: "get_HasCurrent"u8, + adapterMethod: interopReferences.IEnumeratorAdapter1get_HasCurrent(elementType), enumeratorType, interopReferences: interopReferences, module: module); @@ -55,8 +57,11 @@ public static MethodDefinition MoveNext( InteropReferences interopReferences, ModuleDefinition module) { + TypeSignature elementType = enumeratorType.TypeArguments[0]; + return HasCurrentOrMoveNext( methodName: "MoveNext"u8, + adapterMethod: interopReferences.IEnumeratorAdapter1MoveNext(elementType), enumeratorType, interopReferences: interopReferences, module: module); @@ -267,11 +272,13 @@ public static MethodDefinition GetMany( /// Creates a for the get_Current export method. /// /// The name of the method to generate. + /// The adapter method to forward the call to. /// The for the type. /// The instance to use. /// The interop module being built. private static MethodDefinition HasCurrentOrMoveNext( Utf8String methodName, + MemberReference adapterMethod, GenericInstanceTypeSignature enumeratorType, InteropReferences interopReferences, ModuleDefinition module) @@ -294,11 +301,6 @@ private static MethodDefinition HasCurrentOrMoveNext( CustomAttributes = { InteropCustomAttributeFactory.UnmanagedCallersOnly(interopReferences, module) } }; - // Get the reference to the target method to invoke - MemberReference adapterMethod = methodName.AsSpan().SequenceEqual("get_HasCurrent"u8) - ? interopReferences.IEnumeratorAdapter1get_HasCurrent(elementType) - : interopReferences.IEnumeratorAdapter1MoveNext(elementType); - // Labels for jumps CilInstruction nop_beforeTry = new(Nop); CilInstruction ldarg_1_tryStart = new(Ldarg_1);