From 61dbbb34246a054ec329ea0f5beec79e4bf787d3 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 20 Dec 2025 21:51:44 -0800 Subject: [PATCH 01/10] Generalize delegate signatures with type placeholders Replaced hardcoded HSTRING types in delegate comments with , , and placeholders to generalize the function pointer signatures for broader applicability. This improves clarity and maintainability when generating code for different types. --- .../WellKnownTypeDefinitionFactory.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/WellKnownTypeDefinitionFactory.cs b/src/WinRT.Interop.Generator/Factories/WellKnownTypeDefinitionFactory.cs index 004602fed..2bacdf01d 100644 --- a/src/WinRT.Interop.Generator/Factories/WellKnownTypeDefinitionFactory.cs +++ b/src/WinRT.Interop.Generator/Factories/WellKnownTypeDefinitionFactory.cs @@ -519,18 +519,18 @@ public static TypeDefinition IList1Vftbl( // public delegate* unmanaged[MemberFunction] GetIids; // public delegate* unmanaged[MemberFunction] GetRuntimeClassName; // public delegate* unmanaged[MemberFunction] GetTrustLevel; - // public delegate* unmanaged[MemberFunction] GetAt; + // public delegate* unmanaged[MemberFunction]*, HRESULT> GetAt; // public delegate* unmanaged[MemberFunction] get_Size; // public delegate* unmanaged[MemberFunction] GetView; - // public delegate* unmanaged[MemberFunction] IndexOf; - // public delegate* unmanaged[MemberFunction] SetAt; - // public delegate* unmanaged[MemberFunction] InsertAt; + // public delegate* unmanaged[MemberFunction], uint*, bool*, HRESULT> IndexOf; + // public delegate* unmanaged[MemberFunction], HRESULT> SetAt; + // public delegate* unmanaged[MemberFunction], HRESULT> InsertAt; // public delegate* unmanaged[MemberFunction] RemoveAt; - // public delegate* unmanaged[MemberFunction] Append; + // public delegate* unmanaged[MemberFunction], HRESULT> Append; // public delegate* unmanaged[MemberFunction] RemoveAtEnd; // public delegate* unmanaged[MemberFunction] Clear; - // public delegate* unmanaged[MemberFunction] GetMany; - // public delegate* unmanaged[MemberFunction] ReplaceAll; + // public delegate* unmanaged[MemberFunction]*, uint*, HRESULT> GetMany; + // public delegate* unmanaged[MemberFunction]*, HRESULT> ReplaceAll; vftblType.Fields.Add(new FieldDefinition("QueryInterface"u8, FieldAttributes.Public, queryInterfaceType.Import(module).MakeFunctionPointerType())); vftblType.Fields.Add(new FieldDefinition("AddRef"u8, FieldAttributes.Public, addRefType.Import(module).MakeFunctionPointerType())); vftblType.Fields.Add(new FieldDefinition("Release"u8, FieldAttributes.Public, releaseType.Import(module).MakeFunctionPointerType())); @@ -622,9 +622,9 @@ public static TypeDefinition IReadOnlyDictionary2Vftbl( // public delegate* unmanaged[MemberFunction] GetIids; // public delegate* unmanaged[MemberFunction] GetRuntimeClassName; // public delegate* unmanaged[MemberFunction] GetTrustLevel; - // public delegate* unmanaged[MemberFunction] Lookup; + // public delegate* unmanaged[MemberFunction], *, HRESULT> Lookup; // public delegate* unmanaged[MemberFunction] get_Size; - // public delegate* unmanaged[MemberFunction] HasKey; + // public delegate* unmanaged[MemberFunction], bool*, HRESULT> HasKey; // public delegate* unmanaged[MemberFunction] Split; vftblType.Fields.Add(new FieldDefinition("QueryInterface"u8, FieldAttributes.Public, queryInterfaceType.Import(module).MakeFunctionPointerType())); vftblType.Fields.Add(new FieldDefinition("AddRef"u8, FieldAttributes.Public, addRefType.Import(module).MakeFunctionPointerType())); @@ -712,12 +712,12 @@ public static TypeDefinition IDictionary2Vftbl( // public delegate* unmanaged[MemberFunction] GetIids; // public delegate* unmanaged[MemberFunction] GetRuntimeClassName; // public delegate* unmanaged[MemberFunction] GetTrustLevel; - // public delegate* unmanaged[MemberFunction] Lookup; + // public delegate* unmanaged[MemberFunction], *, HRESULT> Lookup; // public delegate* unmanaged[MemberFunction] get_Size; - // public delegate* unmanaged[MemberFunction] HasKey; + // public delegate* unmanaged[MemberFunction], bool*, HRESULT> HasKey; // public delegate* unmanaged[MemberFunction] GetView; - // public delegate* unmanaged[MemberFunction] Insert; - // public delegate* unmanaged[MemberFunction] Remove; + // public delegate* unmanaged[MemberFunction], , bool*, HRESULT> Insert; + // public delegate* unmanaged[MemberFunction], HRESULT> Remove; // public delegate* unmanaged[MemberFunction] Clear; vftblType.Fields.Add(new FieldDefinition("QueryInterface"u8, FieldAttributes.Public, queryInterfaceType.Import(module).MakeFunctionPointerType())); vftblType.Fields.Add(new FieldDefinition("AddRef"u8, FieldAttributes.Public, addRefType.Import(module).MakeFunctionPointerType())); From 7f40871e0b3c49ee701c0de59755b04632ee0e7b Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 20 Dec 2025 22:09:02 -0800 Subject: [PATCH 02/10] Update comment to reflect try/finally block usage Changed a comment to indicate that arguments are loaded inside an outer 'try/finally' block instead of 'try/catch', improving code clarity. --- .../Builders/InteropTypeDefinitionBuilder.Delegate.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.Delegate.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.Delegate.cs index fd76cf46c..618aa3711 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.Delegate.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.Delegate.cs @@ -603,7 +603,7 @@ public static void NativeDelegateType( { Stloc_0 }, { nop_try_this }, - // Arguments loading inside outer 'try/catch' block + // Arguments loading inside outer 'try/finally' block { nop_try_sender }, { nop_try_args }, From a8fc4bf359aeeeb2f486c7d2348b2a9be04563e5 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 20 Dec 2025 22:09:19 -0800 Subject: [PATCH 03/10] Add Conv_U opcode in async and vector view methods Inserted the Conv_U opcode in both IAsyncInfoMethods and IVectorViewMethods to ensure correct type conversion during interop method generation. This change improves type safety and correctness in the generated interop code. --- .../InteropMethodDefinitionFactory.IAsyncInfoMethods.cs | 1 + .../InteropMethodDefinitionFactory.IVectorViewMethods.cs | 1 + 2 files changed, 2 insertions(+) diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IAsyncInfoMethods.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IAsyncInfoMethods.cs index fd06e8ddd..d48778629 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IAsyncInfoMethods.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IAsyncInfoMethods.cs @@ -304,6 +304,7 @@ public static MethodDefinition GetResults( { Ldloc_1 }, { Ldarg_1 }, { Ldloca_S, loc_2_resultNative }, + { Conv_U }, { Ldloc_1 }, { Ldind_I }, { Ldfld, vftblField }, diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IVectorViewMethods.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IVectorViewMethods.cs index 33c7868e1..44cd82a91 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IVectorViewMethods.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IVectorViewMethods.cs @@ -85,6 +85,7 @@ public static MethodDefinition GetAt( { Ldloc_1 }, { Ldarg_1 }, { Ldloca_S, loc_2_resultNative }, + { Conv_U }, { Ldloc_1 }, { Ldind_I }, { Ldfld, vftblType.GetField("GetAt"u8) }, From 0f2604a61320200c096dd99879374c1dd9ff2687 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 20 Dec 2025 22:24:50 -0800 Subject: [PATCH 04/10] Refactor to use elementAbiType in GetAt method generation Introduces a local variable 'elementAbiType' to store the ABI type of the element and uses it consistently when defining locals and method signatures for the 'GetAt' method. This improves code clarity and ensures the correct ABI type is used throughout the method generation process. --- .../InteropMethodDefinitionFactory.IVectorViewMethods.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IVectorViewMethods.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IVectorViewMethods.cs index 44cd82a91..2b406f5ba 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IVectorViewMethods.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IVectorViewMethods.cs @@ -39,6 +39,7 @@ public static MethodDefinition GetAt( ModuleDefinition module) { TypeSignature elementType = readOnlyListType.TypeArguments[0]; + TypeSignature elementAbiType = elementType.GetAbiType(interopReferences); // Define the 'GetAt' method as follows: // @@ -59,7 +60,7 @@ public static MethodDefinition GetAt( // [2]: '' (the ABI type for the type argument) CilLocalVariable loc_0_thisValue = new(interopReferences.WindowsRuntimeObjectReferenceValue.ToValueTypeSignature().Import(module)); CilLocalVariable loc_1_thisPtr = new(module.CorLibTypeFactory.Void.MakePointerType()); - CilLocalVariable loc_2_resultNative = new(elementType.GetAbiType(interopReferences).Import(module)); + CilLocalVariable loc_2_resultNative = new(elementAbiType.Import(module)); // Jump labels CilInstruction ldloca_s_0_tryStart = new(Ldloca_S, loc_0_thisValue); @@ -92,7 +93,7 @@ public static MethodDefinition GetAt( // This 'calli' instruction is always using 'IReadOnlyList1GetAtImpl', but the signature for // the vtable slot for 'GetAt' for 'IVector' is identical, so doing so is safe in this case. - { Calli, WellKnownTypeSignatureFactory.IReadOnlyList1GetAtImpl(elementType, interopReferences).Import(module).MakeStandAloneSignature() }, + { Calli, WellKnownTypeSignatureFactory.IReadOnlyList1GetAtImpl(elementAbiType, interopReferences).Import(module).MakeStandAloneSignature() }, { Call, interopReferences.RestrictedErrorInfoThrowExceptionForHR.Import(module) }, { Leave_S, nop_finallyEnd.CreateLabel() }, From ae3cf1bd9d67b556b5e266816d69cb0d5e13898e Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sat, 20 Dec 2025 22:24:58 -0800 Subject: [PATCH 05/10] Refactor HasKey method generation for IMapView Moved the construction of the HasKey method for IMapView from InteropTypeDefinitionBuilder to a new InteropMethodDefinitionFactory.IMapViewMethods class. This centralizes method creation logic and enables more maintainable and reusable code for interop method definitions. --- ...eDefinitionBuilder.IReadOnlyDictionary2.cs | 27 ++-- ...MethodDefinitionFactory.IMapViewMethods.cs | 143 ++++++++++++++++++ .../Generation/InteropGenerator.Emit.cs | 1 + 3 files changed, 153 insertions(+), 18 deletions(-) create mode 100644 src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapViewMethods.cs diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyDictionary2.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyDictionary2.cs index 5a212b348..678b92026 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyDictionary2.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyDictionary2.cs @@ -88,12 +88,14 @@ public static void Vftbl( /// The for the type. /// The type returned by . /// The instance to use. + /// The emit state for this invocation. /// The interop module being built. /// The resulting methods type. public static void IMapViewMethods( GenericInstanceTypeSignature readOnlyDictionaryType, TypeDefinition vftblType, InteropReferences interopReferences, + InteropGeneratorEmitState emitState, ModuleDefinition module, out TypeDefinition mapViewMethodsType) { @@ -112,30 +114,19 @@ public static void IMapViewMethods( module.TopLevelTypes.Add(mapViewMethodsType); - // Define the 'HasKey' method as follows: - // - // public static bool HasKey(WindowsRuntimeObjectReference thisReference, key) - MethodDefinition hasKeyMethod = new( - name: "HasKey"u8, - attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, - signature: MethodSignature.CreateStatic( - returnType: module.CorLibTypeFactory.Boolean, - parameterTypes: [ - interopReferences.WindowsRuntimeObjectReference.Import(module).ToReferenceTypeSignature(), - keyType.Import(module)])) - { NoInlining = true }; + // Define the 'HasKey' method + MethodDefinition hasKeyMethod = InteropMethodDefinitionFactory.IMapViewMethods.HasKey( + readOnlyDictionaryType: readOnlyDictionaryType, + vftblType: vftblType, + interopReferences: interopReferences, + emitState: emitState, + module: module); // Add and implement the 'HasKey' method mapViewMethodsType.AddMethodImplementation( declaration: interopReferences.IMapViewMethodsImpl2HasKey(keyType, valueType).Import(module), method: hasKeyMethod); - // Create a method body for the 'HasKey' method - hasKeyMethod.CilMethodBody = new CilMethodBody() - { - Instructions = { { Ldnull }, { Throw } } // TODO - }; - // Define the 'Lookup' method as follows: // // public static Lookup(WindowsRuntimeObjectReference thisReference, key) diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapViewMethods.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapViewMethods.cs new file mode 100644 index 000000000..b8dd6cc9a --- /dev/null +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapViewMethods.cs @@ -0,0 +1,143 @@ +// 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 constructed IMapView<K, V> interfaces. + /// + public static class IMapViewMethods + { + /// + /// Creates a for the HasKey method for some IMapView<K, V> interface. + /// + /// The for the type. + /// The vtable type for . + /// The instance to use. + /// The emit state for this invocation. + /// The interop module being built. + /// + /// This method can also be used to define the HasKey method for IMap<K, V> interfaces. + /// + public static MethodDefinition HasKey( + GenericInstanceTypeSignature readOnlyDictionaryType, + TypeDefinition vftblType, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + TypeSignature keyType = readOnlyDictionaryType.TypeArguments[0]; + TypeSignature keyAbiType = keyType.GetAbiType(interopReferences); + + // Define the 'HasKey' method as follows: + // + // public static bool HasKey(WindowsRuntimeObjectReference thisReference, key) + MethodDefinition hasKeyMethod = new( + name: "HasKey"u8, + attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + signature: MethodSignature.CreateStatic( + returnType: module.CorLibTypeFactory.Boolean, + parameterTypes: [ + interopReferences.WindowsRuntimeObjectReference.Import(module).ToReferenceTypeSignature(), + keyType.Import(module)])) + { NoInlining = true }; + + // Declare the local variables: + // [0]: 'WindowsRuntimeObjectReferenceValue' (for 'thisValue') + // [1]: 'void*' (for 'thisPtr') + // [2]: 'bool' (for 'result') + CilLocalVariable loc_0_thisValue = new(interopReferences.WindowsRuntimeObjectReferenceValue.ToValueTypeSignature().Import(module)); + CilLocalVariable loc_1_thisPtr = new(module.CorLibTypeFactory.Void.MakePointerType()); + CilLocalVariable loc_2_result = new(interopReferences.CorLibTypeFactory.Boolean); + + // Jump labels + CilInstruction nop_try_this = new(Nop); + CilInstruction nop_try_key = new(Nop); + CilInstruction nop_ld_key = new(Nop); + CilInstruction nop_finally_key = new(Nop); + CilInstruction ldloca_s_0_finally_this = new(Ldloca_S, loc_0_thisValue); + CilInstruction ldloc_2_finally_end_this = new(Ldloc_2); + + // Create a method body for the 'GetAt' method + hasKeyMethod.CilMethodBody = new CilMethodBody() + { + LocalVariables = { loc_0_thisValue, loc_1_thisPtr, loc_2_result }, + Instructions = + { + // Initialize 'thisValue' + { Ldarg_0 }, + { Callvirt, interopReferences.WindowsRuntimeObjectReferenceAsValue.Import(module) }, + { Stloc_0 }, + { nop_try_this }, + + // Load the key, possibly inside a 'try/finally' block + { nop_try_key }, + + // '.try' code + { Ldloca_S, loc_0_thisValue }, + { Call, interopReferences.WindowsRuntimeObjectReferenceValueGetThisPtrUnsafe.Import(module) }, + { Stloc_1 }, + { Ldloc_1 }, + { nop_ld_key }, + { Ldloca_S, loc_2_result }, + { Conv_U }, + { Ldloc_1 }, + { Ldind_I }, + { Ldfld, vftblType.GetField("HasKey"u8) }, + + // This 'calli' instruction is always using 'IReadOnlyDictionary2HasKeyImpl', but the signature for + // the vtable slot for 'HasKey' for 'IMap' is identical, so doing so is safe in this case. + { Calli, WellKnownTypeSignatureFactory.IReadOnlyDictionary2HasKeyImpl(keyAbiType, interopReferences).Import(module).MakeStandAloneSignature() }, + { Call, interopReferences.RestrictedErrorInfoThrowExceptionForHR.Import(module) }, + { Leave_S, ldloc_2_finally_end_this.CreateLabel() }, + + // Optional 'finally' block for the marshalled key + { nop_finally_key }, + + // '.finally' code + { ldloca_s_0_finally_this }, + { Call, interopReferences.WindowsRuntimeObjectReferenceValueDispose.Import(module) }, + { Endfinally }, + + // return result; + { ldloc_2_finally_end_this }, + { Ret } + }, + ExceptionHandlers = + { + new CilExceptionHandler + { + HandlerType = CilExceptionHandlerType.Finally, + TryStart = nop_try_this.CreateLabel(), + TryEnd = ldloca_s_0_finally_this.CreateLabel(), + HandlerStart = ldloca_s_0_finally_this.CreateLabel(), + HandlerEnd = ldloc_2_finally_end_this.CreateLabel() + } + } + }; + + // Track rewriting the return value for this method + emitState.TrackNativeParameterMethodRewrite( + paraneterType: keyType, + method: hasKeyMethod, + tryMarker: nop_try_key, + loadMarker: nop_ld_key, + finallyMarker: nop_finally_key, + parameterIndex: 1); + + return hasKeyMethod; + } + } +} \ 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 f7c17fd92..fff810735 100644 --- a/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs +++ b/src/WinRT.Interop.Generator/Generation/InteropGenerator.Emit.cs @@ -942,6 +942,7 @@ private static void DefineIReadOnlyDictionaryTypes( readOnlyDictionaryType: typeSignature, vftblType: vftblType, interopReferences: interopReferences, + emitState: emitState, module: module, mapViewMethodsType: out TypeDefinition mapViewMethodsType); From 6a372181a32e43a4595d14c901de3d99702184c6 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 21 Dec 2025 16:52:35 -0800 Subject: [PATCH 06/10] Implement Lookup method for IReadOnlyDictionary interop Refactored Lookup method creation in InteropTypeDefinitionBuilder to use InteropMethodDefinitionFactory.IMapViewMethods.Lookup. Added full implementation of the Lookup method in InteropMethodDefinitionFactory, including method body, local variables, and parameter/return value rewriting logic for IMapView interop. --- ...eDefinitionBuilder.IReadOnlyDictionary2.cs | 25 +--- ...MethodDefinitionFactory.IMapViewMethods.cs | 132 +++++++++++++++++- 2 files changed, 138 insertions(+), 19 deletions(-) diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyDictionary2.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyDictionary2.cs index 678b92026..f820a6c93 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyDictionary2.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyDictionary2.cs @@ -127,29 +127,18 @@ public static void IMapViewMethods( declaration: interopReferences.IMapViewMethodsImpl2HasKey(keyType, valueType).Import(module), method: hasKeyMethod); - // Define the 'Lookup' method as follows: - // - // public static Lookup(WindowsRuntimeObjectReference thisReference, key) - MethodDefinition lookupMethod = new( - name: "Lookup"u8, - attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, - signature: MethodSignature.CreateStatic( - returnType: valueType.Import(module), - parameterTypes: [ - interopReferences.WindowsRuntimeObjectReference.Import(module).ToReferenceTypeSignature(), - keyType.Import(module)])) - { NoInlining = true }; + // Define the 'Lookup' method + MethodDefinition lookupMethod = InteropMethodDefinitionFactory.IMapViewMethods.Lookup( + readOnlyDictionaryType: readOnlyDictionaryType, + vftblType: vftblType, + interopReferences: interopReferences, + emitState: emitState, + module: module); // Add and implement the 'Lookup' method mapViewMethodsType.AddMethodImplementation( declaration: interopReferences.IMapViewMethodsImpl2HasKey(keyType, valueType).Import(module), method: lookupMethod); - - // Create a method body for the 'Lookup' method - lookupMethod.CilMethodBody = new CilMethodBody() - { - Instructions = { { Ldnull }, { Throw } } // TODO - }; } /// diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapViewMethods.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapViewMethods.cs index b8dd6cc9a..f5a52e354 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapViewMethods.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapViewMethods.cs @@ -70,7 +70,7 @@ public static MethodDefinition HasKey( CilInstruction ldloca_s_0_finally_this = new(Ldloca_S, loc_0_thisValue); CilInstruction ldloc_2_finally_end_this = new(Ldloc_2); - // Create a method body for the 'GetAt' method + // Create a method body for the 'HasKey' method hasKeyMethod.CilMethodBody = new CilMethodBody() { LocalVariables = { loc_0_thisValue, loc_1_thisPtr, loc_2_result }, @@ -139,5 +139,135 @@ public static MethodDefinition HasKey( return hasKeyMethod; } + + /// + /// Creates a for the Lookup method for some IMapView<K, V> interface. + /// + /// The for the type. + /// The vtable type for . + /// The instance to use. + /// The emit state for this invocation. + /// The interop module being built. + /// + /// This method can also be used to define the Lookup method for IMap<K, V> interfaces. + /// + public static MethodDefinition Lookup( + GenericInstanceTypeSignature readOnlyDictionaryType, + TypeDefinition vftblType, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + TypeSignature keyType = readOnlyDictionaryType.TypeArguments[0]; + TypeSignature valueType = readOnlyDictionaryType.TypeArguments[1]; + TypeSignature keyAbiType = keyType.GetAbiType(interopReferences); + TypeSignature valueAbiType = valueType.GetAbiType(interopReferences); + + // Define the 'Lookup' method as follows: + // + // public static Lookup(WindowsRuntimeObjectReference thisReference, key) + MethodDefinition lookupMethod = new( + name: "Lookup"u8, + attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + signature: MethodSignature.CreateStatic( + returnType: valueType.Import(module), + parameterTypes: [ + interopReferences.WindowsRuntimeObjectReference.Import(module).ToReferenceTypeSignature(), + keyType.Import(module)])) + { NoInlining = true }; + + // Declare the local variables: + // [0]: 'WindowsRuntimeObjectReferenceValue' (for 'thisValue') + // [1]: 'void*' (for 'thisPtr') + // [2]: (for 'resultNative') + CilLocalVariable loc_0_thisValue = new(interopReferences.WindowsRuntimeObjectReferenceValue.ToValueTypeSignature().Import(module)); + CilLocalVariable loc_1_thisPtr = new(module.CorLibTypeFactory.Void.MakePointerType()); + CilLocalVariable loc_2_resultNative = new(valueAbiType.Import(module)); + + // Jump labels + CilInstruction nop_try_this = new(Nop); + CilInstruction nop_try_key = new(Nop); + CilInstruction nop_ld_key = new(Nop); + CilInstruction nop_finally_key = new(Nop); + CilInstruction ldloca_s_0_finally_this = new(Ldloca_S, loc_0_thisValue); + CilInstruction nop_finally_end_this = new(Nop); + CilInstruction nop_returnValueRewrite = new(Nop); + + // Create a method body for the 'Lookup' method + lookupMethod.CilMethodBody = new CilMethodBody() + { + LocalVariables = { loc_0_thisValue, loc_1_thisPtr, loc_2_resultNative }, + Instructions = + { + // Initialize 'thisValue' + { Ldarg_0 }, + { Callvirt, interopReferences.WindowsRuntimeObjectReferenceAsValue.Import(module) }, + { Stloc_0 }, + { nop_try_this }, + + // Load the key, possibly inside a 'try/finally' block + { nop_try_key }, + + // '.try' code + { Ldloca_S, loc_0_thisValue }, + { Call, interopReferences.WindowsRuntimeObjectReferenceValueGetThisPtrUnsafe.Import(module) }, + { Stloc_1 }, + { Ldloc_1 }, + { nop_ld_key }, + { Ldloca_S, loc_2_resultNative }, + { Conv_U }, + { Ldloc_1 }, + { Ldind_I }, + { Ldfld, vftblType.GetField("Lookup"u8) }, + + // This 'calli' instruction is always using 'IReadOnlyDictionary2LookupImpl', but the signature for + // the vtable slot for 'HasKey' for 'IMap' is identical, so doing so is safe in this case. + { Calli, WellKnownTypeSignatureFactory.IReadOnlyDictionary2LookupImpl(keyAbiType, valueType, interopReferences).Import(module).MakeStandAloneSignature() }, + { Call, interopReferences.RestrictedErrorInfoThrowExceptionForHR.Import(module) }, + { Leave_S, nop_finally_end_this.CreateLabel() }, + + // Optional 'finally' block for the marshalled key + { nop_finally_key }, + + // '.finally' code + { ldloca_s_0_finally_this }, + { Call, interopReferences.WindowsRuntimeObjectReferenceValueDispose.Import(module) }, + { Endfinally }, + + // Marshal and return the result + { nop_finally_end_this }, + { nop_returnValueRewrite } + }, + ExceptionHandlers = + { + new CilExceptionHandler + { + HandlerType = CilExceptionHandlerType.Finally, + TryStart = nop_try_this.CreateLabel(), + TryEnd = ldloca_s_0_finally_this.CreateLabel(), + HandlerStart = ldloca_s_0_finally_this.CreateLabel(), + HandlerEnd = nop_finally_end_this.CreateLabel() + } + } + }; + + // Track rewriting the 'key' parameter for this method + emitState.TrackNativeParameterMethodRewrite( + paraneterType: keyType, + method: lookupMethod, + tryMarker: nop_try_key, + loadMarker: nop_ld_key, + finallyMarker: nop_finally_key, + parameterIndex: 1); + + // Track rewriting the return value for this method + emitState.TrackReturnValueMethodRewrite( + returnType: valueType, + method: lookupMethod, + marker: nop_returnValueRewrite, + source: loc_2_resultNative); + + return lookupMethod; + } } } \ No newline at end of file From 78148fad2a20dd444c4243b3b5f024df6213e362 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 21 Dec 2025 16:58:51 -0800 Subject: [PATCH 07/10] Refactor HasKey and Lookup method definitions Replaced manual construction of HasKey and Lookup methods with calls to InteropMethodDefinitionFactory.IMapViewMethods. This improves maintainability and centralizes method creation logic. --- ...teropTypeDefinitionBuilder.IDictionary2.cs | 50 ++++++------------- 1 file changed, 14 insertions(+), 36 deletions(-) diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IDictionary2.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IDictionary2.cs index b62708d51..b81e02a87 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IDictionary2.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IDictionary2.cs @@ -220,54 +220,32 @@ public static void IMapMethods( // Track the type (it's needed by 'IObservableMap') emitState.TrackTypeDefinition(mapMethodsType, dictionaryType, "IMapMethods"); - // Define the 'HasKey' method as follows: - // - // public static bool HasKey(WindowsRuntimeObjectReference thisReference, key) - MethodDefinition hasKeyMethod = new( - name: "HasKey"u8, - attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, - signature: MethodSignature.CreateStatic( - returnType: module.CorLibTypeFactory.Boolean, - parameterTypes: [ - interopReferences.WindowsRuntimeObjectReference.Import(module).ToReferenceTypeSignature(), - keyType.Import(module)])) - { NoInlining = true }; + // Define the 'HasKey' method + MethodDefinition hasKeyMethod = InteropMethodDefinitionFactory.IMapViewMethods.HasKey( + readOnlyDictionaryType: dictionaryType, + vftblType: vftblType, + interopReferences: interopReferences, + emitState: emitState, + module: module); // Add and implement the 'HasKey' method mapMethodsType.AddMethodImplementation( declaration: interopReferences.IMapMethodsImpl2HasKey(keyType, valueType).Import(module), method: hasKeyMethod); - // Create a method body for the 'HasKey' method - hasKeyMethod.CilMethodBody = new CilMethodBody() - { - Instructions = { { Ldnull }, { Throw } } // TODO - }; - - // Define the 'Lookup' method as follows: - // - // public static Lookup(WindowsRuntimeObjectReference thisReference, key) - MethodDefinition lookupMethod = new( - name: "Lookup"u8, - attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, - signature: MethodSignature.CreateStatic( - returnType: valueType.Import(module), - parameterTypes: [ - interopReferences.WindowsRuntimeObjectReference.Import(module).ToReferenceTypeSignature(), - keyType.Import(module)])) - { NoInlining = true }; + // Define the 'Lookup' method + MethodDefinition lookupMethod = InteropMethodDefinitionFactory.IMapViewMethods.Lookup( + readOnlyDictionaryType: dictionaryType, + vftblType: vftblType, + interopReferences: interopReferences, + emitState: emitState, + module: module); // Add and implement the 'Lookup' method mapMethodsType.AddMethodImplementation( declaration: interopReferences.IMapMethodsImpl2Lookup(keyType, valueType).Import(module), method: lookupMethod); - // Create a method body for the 'Lookup' method - lookupMethod.CilMethodBody = new CilMethodBody() - { - Instructions = { { Ldnull }, { Throw } } // TODO - }; - // Define the 'Insert' method as follows: // // public static bool Insert(WindowsRuntimeObjectReference thisReference, key, value) From 626ce903e92e0f97900cf7ee0b483b28d3bf4ac1 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 21 Dec 2025 17:25:34 -0800 Subject: [PATCH 08/10] Refactor IDictionary2 Insert method generation Moved the Insert method definition for IDictionary2 from InteropTypeDefinitionBuilder to a new InteropMethodDefinitionFactory.IMapMethods helper. This centralizes Insert method creation logic and improves maintainability by encapsulating method body generation and parameter marshaling. --- ...teropTypeDefinitionBuilder.IDictionary2.cs | 30 +--- ...eropMethodDefinitionFactory.IMapMethods.cs | 158 ++++++++++++++++++ 2 files changed, 167 insertions(+), 21 deletions(-) create mode 100644 src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapMethods.cs diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IDictionary2.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IDictionary2.cs index b81e02a87..018d035bc 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IDictionary2.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IDictionary2.cs @@ -95,7 +95,7 @@ public static void Vftbl( // We can share the vtable type for 'void*' when both key and value types are reference types if (isKeyReferenceType && isValueReferenceType) { - vftblType = interopDefinitions.IReadOnlyDictionary2Vftbl; + vftblType = interopDefinitions.IDictionary2Vftbl; return; } @@ -104,7 +104,7 @@ public static void Vftbl( // the vtable type. So in this case, we just always construct a specialized new type. if (!isKeyReferenceType && !isValueReferenceType) { - vftblType = WellKnownTypeDefinitionFactory.IReadOnlyDictionary2Vftbl( + vftblType = WellKnownTypeDefinitionFactory.IDictionary2Vftbl( ns: InteropUtf8NameFactory.TypeNamespace(dictionaryType), name: InteropUtf8NameFactory.TypeName(dictionaryType, "Vftbl"), keyType: keyType, @@ -246,31 +246,19 @@ public static void IMapMethods( declaration: interopReferences.IMapMethodsImpl2Lookup(keyType, valueType).Import(module), method: lookupMethod); - // Define the 'Insert' method as follows: - // - // public static bool Insert(WindowsRuntimeObjectReference thisReference, key, value) - MethodDefinition insertMethod = new( - name: "Insert"u8, - attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, - signature: MethodSignature.CreateStatic( - returnType: module.CorLibTypeFactory.Boolean, - parameterTypes: [ - interopReferences.WindowsRuntimeObjectReference.Import(module).ToReferenceTypeSignature(), - keyType.Import(module), - valueType.Import(module)])) - { NoInlining = true }; + // Define the 'Insert' method + MethodDefinition insertMethod = InteropMethodDefinitionFactory.IMapMethods.Insert( + dictionaryType: dictionaryType, + vftblType: vftblType, + interopReferences: interopReferences, + emitState: emitState, + module: module); // Add and implement the 'Insert' method mapMethodsType.AddMethodImplementation( declaration: interopReferences.IMapMethodsImpl2Insert(keyType, valueType).Import(module), method: insertMethod); - // Create a method body for the 'Insert' method - insertMethod.CilMethodBody = new CilMethodBody() - { - Instructions = { { Ldnull }, { Throw } } // TODO - }; - // Define the 'Remove' method as follows: // // public static void Remove(WindowsRuntimeObjectReference thisReference, key) diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapMethods.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapMethods.cs new file mode 100644 index 000000000..efbe77155 --- /dev/null +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapMethods.cs @@ -0,0 +1,158 @@ +// 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 constructed IMap<K, V> interfaces. + /// + public static class IMapMethods + { + /// + /// Creates a for the Insert method for some IMap<K, V> interface. + /// + /// The for the type. + /// The vtable type for . + /// The instance to use. + /// The emit state for this invocation. + /// The interop module being built. + public static MethodDefinition Insert( + GenericInstanceTypeSignature dictionaryType, + TypeDefinition vftblType, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + TypeSignature keyType = dictionaryType.TypeArguments[0]; + TypeSignature valueType = dictionaryType.TypeArguments[1]; + TypeSignature keyAbiType = keyType.GetAbiType(interopReferences); + TypeSignature valueAbiType = valueType.GetAbiType(interopReferences); + + // Define the 'Insert' method as follows: + // + // public static bool Insert(WindowsRuntimeObjectReference objectReference, key, value) + MethodDefinition insertMethod = new( + name: "Insert"u8, + attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + signature: MethodSignature.CreateStatic( + returnType: module.CorLibTypeFactory.Boolean, + parameterTypes: [ + interopReferences.WindowsRuntimeObjectReference.ToReferenceTypeSignature().Import(module), + keyType.Import(module), + valueType.Import(module)])) + { + NoInlining = true + }; + + // Declare the local variables: + // [0]: 'WindowsRuntimeObjectReferenceValue' (for 'thisValue') + // [1]: 'void*' (for 'thisPtr') + // [2]: 'bool' (for 'result') + CilLocalVariable loc_0_thisValue = new(interopReferences.WindowsRuntimeObjectReferenceValue.Import(module).ToValueTypeSignature()); + CilLocalVariable loc_1_thisPtr = new(module.CorLibTypeFactory.Void.MakePointerType()); + CilLocalVariable loc_2_result = new(interopReferences.CorLibTypeFactory.Boolean); + + // Jump labels + CilInstruction nop_try_this = new(Nop); + CilInstruction nop_try_key = new(Nop); + CilInstruction nop_try_value = new(Nop); + CilInstruction nop_ld_key = new(Nop); + CilInstruction nop_ld_value = new(Nop); + CilInstruction nop_finally_key = new(Nop); + CilInstruction nop_finally_value = new(Nop); + CilInstruction ldloca_0_finally_0 = new(Ldloca_S, loc_0_thisValue); + CilInstruction ldloc_2_finally_end_this = new(Ldloc_2); + + // Create a method body for the 'Insert' method + insertMethod.CilMethodBody = new CilMethodBody() + { + LocalVariables = { loc_0_thisValue, loc_1_thisPtr, loc_2_result }, + Instructions = + { + // Load the local [0] + { Ldarg_0 }, + { Callvirt, interopReferences.WindowsRuntimeObjectReferenceAsValue.Import(module) }, + { Stloc_0 }, + { nop_try_this }, + + // Arguments loading inside outer 'try/finally' block + { nop_try_key }, + { nop_try_value }, + + // 'Insert' call for the native delegate (and 'try' for local [2]) + { Ldloca_S, loc_0_thisValue }, + { Call, interopReferences.WindowsRuntimeObjectReferenceValueGetThisPtrUnsafe.Import(module) }, + { Stloc_1 }, + { Ldloc_1 }, + { nop_ld_key }, + { nop_ld_value }, + { Ldloca_S, loc_2_result }, + { Conv_U }, + { Ldloc_1 }, + { Ldind_I }, + { Ldfld, vftblType.GetField("Insert"u8) }, + { Calli, WellKnownTypeSignatureFactory.IDictionary2InsertImpl(keyAbiType, valueAbiType, interopReferences).Import(module).MakeStandAloneSignature() }, + { Call, interopReferences.RestrictedErrorInfoThrowExceptionForHR.Import(module) }, + { Leave_S, ldloc_2_finally_end_this.CreateLabel() }, + + // Optional 'finally' blocks for the marshalled parameters. These are intentionally + // in reverse order, as the inner-most parameter should be released first. + { nop_finally_value }, + { nop_finally_key }, + + // 'finally' for local [0] + { ldloca_0_finally_0 }, + { Call, interopReferences.WindowsRuntimeObjectReferenceValueDispose.Import(module) }, + { Endfinally }, + + // return result; + { ldloc_2_finally_end_this }, + { Ret } + }, + ExceptionHandlers = + { + // Setup 'try/finally' for local [0] + new CilExceptionHandler + { + HandlerType = CilExceptionHandlerType.Finally, + TryStart = nop_try_this.CreateLabel(), + TryEnd = ldloca_0_finally_0.CreateLabel(), + HandlerStart = ldloca_0_finally_0.CreateLabel(), + HandlerEnd = ldloc_2_finally_end_this.CreateLabel() + } + } + }; + + // Track rewriting the two parameters for this method + emitState.TrackNativeParameterMethodRewrite( + paraneterType: keyType, + method: insertMethod, + tryMarker: nop_try_key, + loadMarker: nop_ld_key, + finallyMarker: nop_finally_key, + parameterIndex: 1); + + emitState.TrackNativeParameterMethodRewrite( + paraneterType: valueType, + method: insertMethod, + tryMarker: nop_try_value, + loadMarker: nop_ld_value, + finallyMarker: nop_finally_value, + parameterIndex: 2); + + return insertMethod; + } + } +} \ No newline at end of file From 23da458aa36c59a1efbff0520f4652928162e384 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 21 Dec 2025 17:29:42 -0800 Subject: [PATCH 09/10] Implement Remove method for IMap interop generation Refactored the Remove method definition for IMap interop types to use a new factory method. The new implementation generates a complete method body with proper marshaling, exception handling, and resource cleanup, improving maintainability and correctness. --- ...teropTypeDefinitionBuilder.IDictionary2.cs | 25 ++-- ...eropMethodDefinitionFactory.IMapMethods.cs | 114 ++++++++++++++++++ 2 files changed, 121 insertions(+), 18 deletions(-) diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IDictionary2.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IDictionary2.cs index 018d035bc..0ba64c060 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IDictionary2.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IDictionary2.cs @@ -259,29 +259,18 @@ public static void IMapMethods( declaration: interopReferences.IMapMethodsImpl2Insert(keyType, valueType).Import(module), method: insertMethod); - // Define the 'Remove' method as follows: - // - // public static void Remove(WindowsRuntimeObjectReference thisReference, key) - MethodDefinition removeMethod = new( - name: "Remove"u8, - attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, - signature: MethodSignature.CreateStatic( - returnType: module.CorLibTypeFactory.Boolean, - parameterTypes: [ - interopReferences.WindowsRuntimeObjectReference.Import(module).ToReferenceTypeSignature(), - keyType.Import(module)])) - { NoInlining = true }; + // Define the 'Remove' method + MethodDefinition removeMethod = InteropMethodDefinitionFactory.IMapMethods.Remove( + dictionaryType: dictionaryType, + vftblType: vftblType, + interopReferences: interopReferences, + emitState: emitState, + module: module); // Add and implement the 'Remove' method mapMethodsType.AddMethodImplementation( declaration: interopReferences.IMapMethodsImpl2Remove(keyType, valueType).Import(module), method: removeMethod); - - // Create a method body for the 'Remove' method - removeMethod.CilMethodBody = new CilMethodBody() - { - Instructions = { { Ldnull }, { Throw } } // TODO - }; } /// diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapMethods.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapMethods.cs index efbe77155..7e5baa419 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapMethods.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapMethods.cs @@ -154,5 +154,119 @@ public static MethodDefinition Insert( return insertMethod; } + + /// + /// Creates a for the Remove method for some IMaplt;K, V> interface. + /// + /// The for the type. + /// The vtable type for . + /// The instance to use. + /// The emit state for this invocation. + /// The interop module being built. + public static MethodDefinition Remove( + GenericInstanceTypeSignature dictionaryType, + TypeDefinition vftblType, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + TypeSignature keyType = dictionaryType.TypeArguments[0]; + TypeSignature keyAbiType = keyType.GetAbiType(interopReferences); + + // Define the 'Remove' method as follows: + // + // public static bool Remove(WindowsRuntimeObjectReference thisReference, key) + MethodDefinition removeMethod = new( + name: "Remove"u8, + attributes: MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, + signature: MethodSignature.CreateStatic( + returnType: module.CorLibTypeFactory.Boolean, + parameterTypes: [ + interopReferences.WindowsRuntimeObjectReference.Import(module).ToReferenceTypeSignature(), + keyType.Import(module)])) + { NoInlining = true }; + + // Declare the local variables: + // [0]: 'WindowsRuntimeObjectReferenceValue' (for 'thisValue') + // [1]: 'void*' (for 'thisPtr') + // [2]: 'bool' (for 'result') + CilLocalVariable loc_0_thisValue = new(interopReferences.WindowsRuntimeObjectReferenceValue.ToValueTypeSignature().Import(module)); + CilLocalVariable loc_1_thisPtr = new(module.CorLibTypeFactory.Void.MakePointerType()); + CilLocalVariable loc_2_result = new(interopReferences.CorLibTypeFactory.Boolean); + + // Jump labels + CilInstruction nop_try_this = new(Nop); + CilInstruction nop_try_key = new(Nop); + CilInstruction nop_ld_key = new(Nop); + CilInstruction nop_finally_key = new(Nop); + CilInstruction ldloca_s_0_finally_this = new(Ldloca_S, loc_0_thisValue); + CilInstruction ldloc_2_finally_end_this = new(Ldloc_2); + + // Create a method body for the 'Remove' method + removeMethod.CilMethodBody = new CilMethodBody() + { + LocalVariables = { loc_0_thisValue, loc_1_thisPtr, loc_2_result }, + Instructions = + { + // Initialize 'thisValue' + { Ldarg_0 }, + { Callvirt, interopReferences.WindowsRuntimeObjectReferenceAsValue.Import(module) }, + { Stloc_0 }, + { nop_try_this }, + + // Load the key, possibly inside a 'try/finally' block + { nop_try_key }, + + // '.try' code + { Ldloca_S, loc_0_thisValue }, + { Call, interopReferences.WindowsRuntimeObjectReferenceValueGetThisPtrUnsafe.Import(module) }, + { Stloc_1 }, + { Ldloc_1 }, + { nop_ld_key }, + { Ldloca_S, loc_2_result }, + { Conv_U }, + { Ldloc_1 }, + { Ldind_I }, + { Ldfld, vftblType.GetField("Remove"u8) }, + { Calli, WellKnownTypeSignatureFactory.IDictionary2RemoveImpl(keyAbiType, interopReferences).Import(module).MakeStandAloneSignature() }, + { Call, interopReferences.RestrictedErrorInfoThrowExceptionForHR.Import(module) }, + { Leave_S, ldloc_2_finally_end_this.CreateLabel() }, + + // Optional 'finally' block for the marshalled key + { nop_finally_key }, + + // '.finally' code + { ldloca_s_0_finally_this }, + { Call, interopReferences.WindowsRuntimeObjectReferenceValueDispose.Import(module) }, + { Endfinally }, + + // return result; + { ldloc_2_finally_end_this }, + { Ret } + }, + ExceptionHandlers = + { + new CilExceptionHandler + { + HandlerType = CilExceptionHandlerType.Finally, + TryStart = nop_try_this.CreateLabel(), + TryEnd = ldloca_s_0_finally_this.CreateLabel(), + HandlerStart = ldloca_s_0_finally_this.CreateLabel(), + HandlerEnd = ldloc_2_finally_end_this.CreateLabel() + } + } + }; + + // Track rewriting the return value for this method + emitState.TrackNativeParameterMethodRewrite( + paraneterType: keyType, + method: removeMethod, + tryMarker: nop_try_key, + loadMarker: nop_ld_key, + finallyMarker: nop_finally_key, + parameterIndex: 1); + + return removeMethod; + } } } \ No newline at end of file From 9dd39aa12f4b1c0b04fd22b207b4a50d1fe95c4d Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Sun, 21 Dec 2025 17:34:21 -0800 Subject: [PATCH 10/10] Fix value type used in IReadOnlyDictionary2LookupImpl call Replaces valueType with valueAbiType in the call to WellKnownTypeSignatureFactory.IReadOnlyDictionary2LookupImpl to ensure the correct ABI type is used for the value parameter. --- .../Factories/InteropMethodDefinitionFactory.IMapViewMethods.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapViewMethods.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapViewMethods.cs index f5a52e354..eb60e9ed6 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapViewMethods.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IMapViewMethods.cs @@ -222,7 +222,7 @@ public static MethodDefinition Lookup( // This 'calli' instruction is always using 'IReadOnlyDictionary2LookupImpl', but the signature for // the vtable slot for 'HasKey' for 'IMap' is identical, so doing so is safe in this case. - { Calli, WellKnownTypeSignatureFactory.IReadOnlyDictionary2LookupImpl(keyAbiType, valueType, interopReferences).Import(module).MakeStandAloneSignature() }, + { Calli, WellKnownTypeSignatureFactory.IReadOnlyDictionary2LookupImpl(keyAbiType, valueAbiType, interopReferences).Import(module).MakeStandAloneSignature() }, { Call, interopReferences.RestrictedErrorInfoThrowExceptionForHR.Import(module) }, { Leave_S, nop_finally_end_this.CreateLabel() },