diff --git a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyDictionary2.cs b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyDictionary2.cs index f820a6c93..051019283 100644 --- a/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyDictionary2.cs +++ b/src/WinRT.Interop.Generator/Builders/InteropTypeDefinitionBuilder.IReadOnlyDictionary2.cs @@ -1,6 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; using System.Runtime.InteropServices; using AsmResolver.DotNet; using AsmResolver.DotNet.Code.Cil; @@ -582,6 +583,39 @@ public static void ImplType( ModuleDefinition module, out TypeDefinition implType) { + TypeSignature keyType = readOnlyDictionaryType.TypeArguments[0]; + TypeSignature valueType = readOnlyDictionaryType.TypeArguments[1]; + + // Define the 'Lookup' method + MethodDefinition lookupMethod = InteropMethodDefinitionFactory.IReadOnlyDictionary2Impl.Lookup( + readOnlyDictionaryType: readOnlyDictionaryType, + lookupMethod: interopReferences.IReadOnlyDictionaryAdapter2Lookup(keyType, valueType), + interopReferences: interopReferences, + emitState: emitState, + module: module); + + // Define the 'get_Size' method + MethodDefinition sizeMethod = InteropMethodDefinitionFactory.IReadOnlyDictionary2Impl.get_Size( + readOnlyDictionaryType: readOnlyDictionaryType, + sizeMethod: interopReferences.IReadOnlyDictionaryAdapter2Size(keyType, valueType), + interopReferences: interopReferences, + module: module); + + // Define the 'HasKey' method + MethodDefinition hasKeymethod = InteropMethodDefinitionFactory.IReadOnlyDictionary2Impl.HasKey( + readOnlyDictionaryType: readOnlyDictionaryType, + hasKeyMethod: interopReferences.IReadOnlyDictionary2ContainsKey(keyType, valueType), + interopReferences: interopReferences, + emitState: emitState, + module: module); + + // Define the 'Split' method + MethodDefinition splitMethod = InteropMethodDefinitionFactory.IReadOnlyDictionary2Impl.Split( + readOnlyDictionaryType: readOnlyDictionaryType, + interopReferences: interopReferences, + emitState: emitState, + module: module); + Impl( interfaceType: ComInterfaceType.InterfaceIsIInspectable, ns: InteropUtf8NameFactory.TypeNamespace(readOnlyDictionaryType), @@ -591,7 +625,11 @@ public static void ImplType( interopReferences: interopReferences, module: module, implType: out implType, - vtableMethods: []); + vtableMethods: [ + lookupMethod, + sizeMethod, + hasKeymethod, + splitMethod]); // Track the type (it may be needed by COM interface entries for user-defined types) emitState.TrackTypeDefinition(implType, readOnlyDictionaryType, "Impl"); diff --git a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs index ba291ee87..b699cdb41 100644 --- a/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs +++ b/src/WinRT.Interop.Generator/Discovery/InteropTypeDiscovery.Generics.cs @@ -298,6 +298,22 @@ private static void TryTrackWindowsRuntimeGenericInterfaceTypeInstance( discoveryState: discoveryState, interopReferences: interopReferences, module: module); + + // Handle 'IReadOnlyDictionarySplitAdapter', which is returned by the 'IMapView.Split' method + TryTrackGenericTypeInstance( + typeSignature: interopReferences.IReadOnlyDictionarySplitAdapter2.MakeGenericReferenceType([.. typeSignature.TypeArguments]), + args: args, + discoveryState: discoveryState, + interopReferences: interopReferences, + module: module); + + // Handle 'ArraySegment.Enumerator', which is returned by 'IReadOnlyDictionarySplitAdapter.GetEnumerator()' + TryTrackGenericTypeInstance( + typeSignature: interopReferences.ArraySegment1Enumerator.MakeGenericValueType(interopReferences.KeyValuePair2.MakeGenericValueType([.. typeSignature.TypeArguments])), + args: args, + discoveryState: discoveryState, + interopReferences: interopReferences, + module: module); } else if (SignatureComparer.IgnoreVersion.Equals(typeSignature.GenericType, interopReferences.IObservableVector1)) { diff --git a/src/WinRT.Interop.Generator/Extensions/ImportExtensions.cs b/src/WinRT.Interop.Generator/Extensions/ImportExtensions.cs index 77a1f99a0..7203b47b3 100644 --- a/src/WinRT.Interop.Generator/Extensions/ImportExtensions.cs +++ b/src/WinRT.Interop.Generator/Extensions/ImportExtensions.cs @@ -33,6 +33,17 @@ public static IMethodDefOrRef Import(this IMethodDefOrRef methodDefOrRef, Module return (IMethodDefOrRef)methodDefOrRef.ImportWith(module.DefaultImporter); } + /// + /// Imports a method descriptor or reference into a module using the default reference importer. + /// + /// The instance to import. + /// The module to import into. + /// The imported . + public static IMethodDescriptor Import(this IMethodDescriptor methodDescriptor, ModuleDefinition module) + { + return (IMethodDescriptor)methodDescriptor.ImportWith(module.DefaultImporter); + } + /// /// Imports a type signature into a module using the default reference importer. /// diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyDictionary2Impl.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyDictionary2Impl.cs new file mode 100644 index 000000000..370114930 --- /dev/null +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyDictionary2Impl.cs @@ -0,0 +1,525 @@ +// 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; + +#pragma warning disable IDE1006 + +namespace WindowsRuntime.InteropGenerator.Factories; + +/// +/// A factory for interop method definitions. +/// +internal static partial class InteropMethodDefinitionFactory +{ + /// + /// Helpers for impl types for interfaces. + /// + public static class IReadOnlyDictionary2Impl + { + /// + /// Creates a for the Lookup export method. + /// + /// The for the type. + /// The interface method to invoke on . + /// 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 interfaces. + /// + public static MethodDefinition Lookup( + GenericInstanceTypeSignature readOnlyDictionaryType, + MemberReference lookupMethod, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + TypeSignature keyType = readOnlyDictionaryType.TypeArguments[0]; + TypeSignature valueType = readOnlyDictionaryType.TypeArguments[1]; + + // Define the 'Lookup' method as follows: + // + // [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + // private static int Lookup(void* thisPtr, key, * result) + MethodDefinition lookupImplMethod = new( + name: "Lookup"u8, + attributes: MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, + signature: MethodSignature.CreateStatic( + returnType: module.CorLibTypeFactory.Int32, + parameterTypes: [ + module.CorLibTypeFactory.Void.MakePointerType(), + keyType.GetAbiType(interopReferences).Import(module), + valueType.GetAbiType(interopReferences).Import(module).MakePointerType()])) + { + CustomAttributes = { InteropCustomAttributeFactory.UnmanagedCallersOnly(interopReferences, module) } + }; + + // Declare the local variables: + // [0]: '' (for 'thisObject') + // [1]: 'int' (the 'HRESULT' to return) + CilLocalVariable loc_0_thisObject = new(readOnlyDictionaryType.Import(module)); + CilLocalVariable loc_1_hresult = new(module.CorLibTypeFactory.Int32); + + // Labels for jumps + CilInstruction nop_beforeTry = new(Nop); + CilInstruction ldarg_0_tryStart = new(Ldarg_0); + CilInstruction nop_parameter1Rewrite = new(Nop); + CilInstruction ldloc_1_returnHResult = new(Ldloc_1); + CilInstruction call_catchStartMarshalException = new(Call, interopReferences.RestrictedErrorInfoExceptionMarshallerConvertToUnmanaged.Import(module)); + CilInstruction nop_convertToUnmanaged = new(Nop); + + IMethodDescriptor adapterLookupMethod; + + // Get the target 'Lookup' method (we can optimize for 'string' types) + if (keyType.IsTypeOfString()) + { + adapterLookupMethod = SignatureComparer.IgnoreVersion.Equals(readOnlyDictionaryType.GenericType, interopReferences.IReadOnlyDictionary2) + ? interopReferences.IReadOnlyDictionaryAdapterOfStringLookup(valueType) + : interopReferences.IListAdapterOfStringIndexOf(); // TODO + } + else + { + // Otherwise use the provided method directly (it will always be valid) + adapterLookupMethod = lookupMethod; + } + + // Create a method body for the 'Lookup' method + lookupImplMethod.CilMethodBody = new CilMethodBody() + { + LocalVariables = { loc_0_thisObject, loc_1_hresult }, + Instructions = + { + // Return 'E_POINTER' if the argument is 'null' + { Ldarg_2 }, + { Ldc_I4_0 }, + { Conv_U }, + { Bne_Un_S, nop_beforeTry.CreateLabel() }, + { Ldc_I4, unchecked((int)0x80004003) }, + { Ret }, + { nop_beforeTry }, + + // '.try' code + { ldarg_0_tryStart }, + { Call, interopReferences.ComInterfaceDispatchGetInstance.MakeGenericInstanceMethod(readOnlyDictionaryType).Import(module) }, + { Stloc_0 }, + { Ldarg_2 }, + { Ldloc_0 }, + { nop_parameter1Rewrite }, + { Call, adapterLookupMethod.Import(module) }, + { nop_convertToUnmanaged }, + { Ldc_I4_0 }, + { Stloc_1 }, + { Leave_S, ldloc_1_returnHResult.CreateLabel() }, + + // '.catch' code + { call_catchStartMarshalException }, + { Stloc_1 }, + { Leave_S, ldloc_1_returnHResult.CreateLabel() }, + + // Return the 'HRESULT' from location [1] + { ldloc_1_returnHResult }, + { Ret } + }, + ExceptionHandlers = + { + new CilExceptionHandler + { + HandlerType = CilExceptionHandlerType.Exception, + TryStart = ldarg_0_tryStart.CreateLabel(), + TryEnd = call_catchStartMarshalException.CreateLabel(), + HandlerStart = call_catchStartMarshalException.CreateLabel(), + HandlerEnd = ldloc_1_returnHResult.CreateLabel(), + ExceptionType = interopReferences.Exception.Import(module) + } + } + }; + + // If the key type is 'string', we use 'ReadOnlySpan' to avoid an allocation + TypeSignature parameterType = keyType.IsTypeOfString() + ? interopReferences.ReadOnlySpanChar + : keyType; + + // Track rewriting the parameter for this method + emitState.TrackManagedParameterMethodRewrite( + parameterType: parameterType, + method: lookupImplMethod, + marker: nop_parameter1Rewrite, + parameterIndex: 1); + + // Track the method for rewrite to marshal the result value + emitState.TrackRetValValueMethodRewrite( + retValType: valueType, + method: lookupImplMethod, + marker: nop_convertToUnmanaged); + + return lookupImplMethod; + } + + /// + /// Creates a for the get_Size export method. + /// + /// The for the type. + /// The interface method to invoke on . + /// The instance to use. + /// The interop module being built. + /// + /// This method can also be used to define the get_Size method for interfaces. + /// + public static MethodDefinition get_Size( + GenericInstanceTypeSignature readOnlyDictionaryType, + MemberReference sizeMethod, + InteropReferences interopReferences, + ModuleDefinition module) + { + // Define the 'get_Size' method as follows: + // + // [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + // private static int get_Size(void* thisPtr, uint* result) + MethodDefinition sizeImplMethod = new( + name: "get_Size"u8, + attributes: MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, + signature: MethodSignature.CreateStatic( + returnType: module.CorLibTypeFactory.Int32, + parameterTypes: [ + module.CorLibTypeFactory.Void.MakePointerType(), + module.CorLibTypeFactory.UInt32.MakePointerType()])) + { + CustomAttributes = { InteropCustomAttributeFactory.UnmanagedCallersOnly(interopReferences, module) } + }; + + // Declare the local variables: + // [0]: '' (for 'thisObject') + // [1]: 'int' (the 'HRESULT' to return) + CilLocalVariable loc_0_thisObject = new(readOnlyDictionaryType.Import(module)); + CilLocalVariable loc_1_hresult = new(module.CorLibTypeFactory.Int32); + + // Labels for jumps + CilInstruction nop_beforeTry = new(Nop); + CilInstruction ldarg_0_tryStart = new(Ldarg_0); + CilInstruction ldloc_1_returnHResult = new(Ldloc_1); + CilInstruction call_catchStartMarshalException = new(Call, interopReferences.RestrictedErrorInfoExceptionMarshallerConvertToUnmanaged.Import(module)); + + // Create a method body for the 'get_Size' method + sizeImplMethod.CilMethodBody = new CilMethodBody() + { + LocalVariables = { loc_0_thisObject, loc_1_hresult }, + Instructions = + { + // Return 'E_POINTER' if the argument is 'null' + { Ldarg_1 }, + { Ldc_I4_0 }, + { Conv_U }, + { Bne_Un_S, nop_beforeTry.CreateLabel() }, + { Ldc_I4, unchecked((int)0x80004003) }, + { Ret }, + { nop_beforeTry }, + + // '.try' code + { ldarg_0_tryStart }, + { Call, interopReferences.ComInterfaceDispatchGetInstance.MakeGenericInstanceMethod(readOnlyDictionaryType).Import(module) }, + { Stloc_0 }, + { Ldarg_1 }, + { Ldloc_0 }, + { Call, sizeMethod.Import(module) }, + { Stind_I4 }, + { Ldc_I4_0 }, + { Stloc_1 }, + { Leave_S, ldloc_1_returnHResult.CreateLabel() }, + + // '.catch' code + { call_catchStartMarshalException }, + { Stloc_1 }, + { Leave_S, ldloc_1_returnHResult.CreateLabel() }, + + // Return the 'HRESULT' from location [1] + { ldloc_1_returnHResult }, + { Ret } + }, + ExceptionHandlers = + { + new CilExceptionHandler + { + HandlerType = CilExceptionHandlerType.Exception, + TryStart = ldarg_0_tryStart.CreateLabel(), + TryEnd = call_catchStartMarshalException.CreateLabel(), + HandlerStart = call_catchStartMarshalException.CreateLabel(), + HandlerEnd = ldloc_1_returnHResult.CreateLabel(), + ExceptionType = interopReferences.Exception.Import(module) + } + } + }; + + return sizeImplMethod; + } + + /// + /// Creates a for the HasKey export method. + /// + /// The for the type. + /// The interface method to invoke on . + /// 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 interfaces. + /// + public static MethodDefinition HasKey( + GenericInstanceTypeSignature readOnlyDictionaryType, + MemberReference hasKeyMethod, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + TypeSignature keyType = readOnlyDictionaryType.TypeArguments[0]; + TypeSignature valueType = readOnlyDictionaryType.TypeArguments[1]; + + // Define the 'HasKey' method as follows: + // + // [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + // private static int HasKey(void* thisPtr, key, bool* result) + MethodDefinition hasKeyImplMethod = new( + name: "HasKey"u8, + attributes: MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, + signature: MethodSignature.CreateStatic( + returnType: module.CorLibTypeFactory.Int32, + parameterTypes: [ + module.CorLibTypeFactory.Void.MakePointerType(), + keyType.GetAbiType(interopReferences).Import(module), + module.CorLibTypeFactory.Boolean.MakePointerType()])) + { + CustomAttributes = { InteropCustomAttributeFactory.UnmanagedCallersOnly(interopReferences, module) } + }; + + // Declare the local variables: + // [0]: '' (for 'thisObject') + // [1]: 'int' (the 'HRESULT' to return) + CilLocalVariable loc_0_thisObject = new(readOnlyDictionaryType.Import(module)); + CilLocalVariable loc_1_hresult = new(module.CorLibTypeFactory.Int32); + + // Labels for jumps + CilInstruction nop_beforeTry = new(Nop); + CilInstruction ldarg_0_tryStart = new(Ldarg_0); + CilInstruction nop_parameter1Rewrite = new(Nop); + CilInstruction ldloc_1_returnHResult = new(Ldloc_1); + CilInstruction call_catchStartMarshalException = new(Call, interopReferences.RestrictedErrorInfoExceptionMarshallerConvertToUnmanaged.Import(module)); + CilInstruction nop_convertToUnmanaged = new(Nop); + CilInstruction callHasKeyMethod; + + // Get the target 'HasKey' method (we can optimize for 'string' types). + // We prepare the full instruction, as we need 'callvirt' in some cases. + if (keyType.IsTypeOfString()) + { + MethodSpecification hasKeyMethodSpecification = SignatureComparer.IgnoreVersion.Equals(readOnlyDictionaryType.GenericType, interopReferences.IReadOnlyDictionary2) + ? interopReferences.IReadOnlyDictionaryAdapterOfStringHasKey(valueType) + : interopReferences.IReadOnlyDictionaryAdapterOfStringHasKey(valueType); // TODO + + callHasKeyMethod = new(Call, hasKeyMethodSpecification.Import(module)); + } + else + { + // Otherwise just use 'ContainsKey' method passed as input + callHasKeyMethod = new(Callvirt, hasKeyMethod.Import(module)); + } + + // Create a method body for the 'HasKey' method + hasKeyImplMethod.CilMethodBody = new CilMethodBody() + { + LocalVariables = { loc_0_thisObject, loc_1_hresult }, + Instructions = + { + // Return 'E_POINTER' if the argument is 'null' + { Ldarg_2 }, + { Ldc_I4_0 }, + { Conv_U }, + { Bne_Un_S, nop_beforeTry.CreateLabel() }, + { Ldc_I4, unchecked((int)0x80004003) }, + { Ret }, + { nop_beforeTry }, + + // '.try' code + { ldarg_0_tryStart }, + { Call, interopReferences.ComInterfaceDispatchGetInstance.MakeGenericInstanceMethod(readOnlyDictionaryType).Import(module) }, + { Stloc_0 }, + { Ldarg_2 }, + { Ldloc_0 }, + { nop_parameter1Rewrite }, + { callHasKeyMethod }, + { nop_convertToUnmanaged }, + { Ldc_I4_0 }, + { Stloc_1 }, + { Leave_S, ldloc_1_returnHResult.CreateLabel() }, + + // '.catch' code + { call_catchStartMarshalException }, + { Stloc_1 }, + { Leave_S, ldloc_1_returnHResult.CreateLabel() }, + + // Return the 'HRESULT' from location [1] + { ldloc_1_returnHResult }, + { Ret } + }, + ExceptionHandlers = + { + new CilExceptionHandler + { + HandlerType = CilExceptionHandlerType.Exception, + TryStart = ldarg_0_tryStart.CreateLabel(), + TryEnd = call_catchStartMarshalException.CreateLabel(), + HandlerStart = call_catchStartMarshalException.CreateLabel(), + HandlerEnd = ldloc_1_returnHResult.CreateLabel(), + ExceptionType = interopReferences.Exception.Import(module) + } + } + }; + + // If the key type is 'string', we use 'ReadOnlySpan' to avoid an allocation + TypeSignature parameterType = keyType.IsTypeOfString() + ? interopReferences.ReadOnlySpanChar + : keyType; + + // Track rewriting the parameter for this method + emitState.TrackManagedParameterMethodRewrite( + parameterType: parameterType, + method: hasKeyImplMethod, + marker: nop_parameter1Rewrite, + parameterIndex: 1); + + // Track the method for rewrite to marshal the result value + emitState.TrackRetValValueMethodRewrite( + retValType: module.CorLibTypeFactory.Boolean, + method: hasKeyImplMethod, + marker: nop_convertToUnmanaged); + + return hasKeyImplMethod; + } + + /// + /// Creates a for the Split export method. + /// + /// The for the type. + /// The instance to use. + /// The emit state for this invocation. + /// The interop module being built. + public static MethodDefinition Split( + GenericInstanceTypeSignature readOnlyDictionaryType, + InteropReferences interopReferences, + InteropGeneratorEmitState emitState, + ModuleDefinition module) + { + TypeSignature keyType = readOnlyDictionaryType.TypeArguments[0]; + TypeSignature valueType = readOnlyDictionaryType.TypeArguments[1]; + + // Define the 'Split' method as follows: + // + // [UnmanagedCallersOnly(CallConvs = [typeof(CallConvMemberFunction)])] + // private static int Split(void* thisPtr, void** first, void** second) + MethodDefinition splitMethod = new( + name: "Split"u8, + attributes: MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.Static, + signature: MethodSignature.CreateStatic( + returnType: module.CorLibTypeFactory.Int32, + parameterTypes: [ + module.CorLibTypeFactory.Void.MakePointerType(), + module.CorLibTypeFactory.Void.MakePointerType().MakePointerType(), + module.CorLibTypeFactory.Void.MakePointerType().MakePointerType()])) + { + CustomAttributes = { InteropCustomAttributeFactory.UnmanagedCallersOnly(interopReferences, module) } + }; + + // Declare the local variables: + // [0]: '' (for 'thisObject') + // [1]: '' (for 'firstObject') + // [2]: '' (for 'secondObject') + // [3]: 'int' (the 'HRESULT' to return) + CilLocalVariable loc_0_thisObject = new(readOnlyDictionaryType.Import(module)); + CilLocalVariable loc_1_firstObject = new(readOnlyDictionaryType.Import(module)); + CilLocalVariable loc_2_secondObject = new(readOnlyDictionaryType.Import(module)); + CilLocalVariable loc_3_hresult = new(module.CorLibTypeFactory.Int32); + + // Labels for jumps + CilInstruction nop_beforeTry = new(Nop); + CilInstruction ldarg_0_tryStart = new(Ldarg_0); + CilInstruction ldloc_3_returnHResult = new(Ldloc_3); + CilInstruction call_catchStartMarshalException = new(Call, interopReferences.RestrictedErrorInfoExceptionMarshallerConvertToUnmanaged.Import(module)); + CilInstruction nop_firstObject_convertToUnmanaged = new(Nop); + CilInstruction nop_secondObject_convertToUnmanaged = new(Nop); + + // Create a method body for the 'Split' method + splitMethod.CilMethodBody = new CilMethodBody() + { + LocalVariables = { loc_0_thisObject, loc_1_firstObject, loc_2_secondObject, loc_3_hresult }, + Instructions = + { + // Return 'E_POINTER' if the argument is 'null' + { Ldarg_2 }, + { Ldc_I4_0 }, + { Conv_U }, + { Bne_Un_S, nop_beforeTry.CreateLabel() }, + { Ldc_I4, unchecked((int)0x80004003) }, + { Ret }, + { nop_beforeTry }, + + // '.try' code + { ldarg_0_tryStart }, + { Call, interopReferences.ComInterfaceDispatchGetInstance.MakeGenericInstanceMethod(readOnlyDictionaryType).Import(module) }, + { Stloc_0 }, + { Ldloc_0 }, + { Ldloca_S, loc_1_firstObject }, + { Ldloca_S, loc_2_secondObject }, + { Call, interopReferences.IReadOnlyDictionaryAdapter2Split(keyType, valueType).Import(module) }, + { Ldarg_1 }, + { Ldloc_1 }, + { nop_firstObject_convertToUnmanaged }, + { Ldarg_2 }, + { Ldloc_2 }, + { nop_secondObject_convertToUnmanaged }, + { Ldc_I4_0 }, + { Stloc_3 }, + { Leave_S, ldloc_3_returnHResult.CreateLabel() }, + + // '.catch' code + { call_catchStartMarshalException }, + { Stloc_3 }, + { Leave_S, ldloc_3_returnHResult.CreateLabel() }, + + // Return the 'HRESULT' from location [1] + { ldloc_3_returnHResult }, + { Ret } + }, + ExceptionHandlers = + { + new CilExceptionHandler + { + HandlerType = CilExceptionHandlerType.Exception, + TryStart = ldarg_0_tryStart.CreateLabel(), + TryEnd = call_catchStartMarshalException.CreateLabel(), + HandlerStart = call_catchStartMarshalException.CreateLabel(), + HandlerEnd = ldloc_3_returnHResult.CreateLabel(), + ExceptionType = interopReferences.Exception.Import(module) + } + } + }; + + // Track the method for rewrite to marshal the two result values + emitState.TrackRetValValueMethodRewrite( + retValType: readOnlyDictionaryType, + method: splitMethod, + marker: nop_firstObject_convertToUnmanaged); + + emitState.TrackRetValValueMethodRewrite( + retValType: readOnlyDictionaryType, + method: splitMethod, + marker: nop_secondObject_convertToUnmanaged); + + return splitMethod; + } + } +} \ No newline at end of file diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyList1Impl.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyList1Impl.cs index 82a02a032..a70f3fcd9 100644 --- a/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyList1Impl.cs +++ b/src/WinRT.Interop.Generator/Factories/InteropMethodDefinitionFactory.IReadOnlyList1Impl.cs @@ -239,6 +239,9 @@ public static MethodDefinition get_Size( /// The instance to use. /// The emit state for this invocation. /// The interop module being built. + /// + /// This method can also be used to define the IndexOf method for interfaces. + /// public static MethodDefinition IndexOf( GenericInstanceTypeSignature readOnlyListType, MemberReference indexOfMethod, @@ -356,7 +359,7 @@ public static MethodDefinition IndexOf( ? interopReferences.ReadOnlySpanChar : elementType; - // Track rewriting the two parameters for this method + // Track rewriting the parameter for this method emitState.TrackManagedParameterMethodRewrite( parameterType: parameterType, method: indexOfImplMethod, diff --git a/src/WinRT.Interop.Generator/References/InteropReferences.cs b/src/WinRT.Interop.Generator/References/InteropReferences.cs index eb97ffed1..9360c5240 100644 --- a/src/WinRT.Interop.Generator/References/InteropReferences.cs +++ b/src/WinRT.Interop.Generator/References/InteropReferences.cs @@ -133,6 +133,16 @@ public InteropReferences( /// public TypeReference Array => field ??= _corLibTypeFactory.CorLibScope.CreateTypeReference("System"u8, "Array"u8); + /// + /// Gets the for . + /// + public TypeReference ArraySegment1 => field ??= _corLibTypeFactory.CorLibScope.CreateTypeReference("System"u8, "ArraySegment`1"u8); + + /// + /// Gets the for . + /// + public TypeReference ArraySegment1Enumerator => field ??= ArraySegment1.CreateTypeReference("Enumerator"u8); + /// /// Gets the for . /// @@ -698,6 +708,21 @@ public InteropReferences( /// public TypeReference IDictionaryMethods2 => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices"u8, "IDictionaryMethods`2"u8); + /// + /// Gets the for WindowsRuntime.InteropServices.IReadOnlyDictionaryAdapter<TKey, TValue>. + /// + public TypeReference IReadOnlyDictionaryAdapter2 => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices"u8, "IReadOnlyDictionaryAdapter`2"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.IReadOnlyDictionaryAdapterExtensions. + /// + public TypeReference IReadOnlyDictionaryAdapterExtensions => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices"u8, "IReadOnlyDictionaryAdapterExtensions"u8); + + /// + /// Gets the for WindowsRuntime.InteropServices.IReadOnlyDictionarySplitAdapter2<TKey, TValue>. + /// + public TypeReference IReadOnlyDictionarySplitAdapter2 => field ??= _windowsRuntimeModule.CreateTypeReference("WindowsRuntime.InteropServices"u8, "IReadOnlyDictionarySplitAdapter`2"u8); + /// /// Gets the for WindowsRuntime.InteropServices.IReadOnlyDictionaryMethods. /// @@ -3688,6 +3713,96 @@ public MethodSpecification IReadOnlyDictionaryMethods2TryGetValue(TypeSignature .MakeGenericInstanceMethod(mapViewMethods.ToReferenceTypeSignature()); } + /// + /// Gets the for WindowsRuntime.InteropServices.IReadOnlyDictionaryAdapter<TKey, TValue>.Lookup. + /// + /// The input key type. + /// The input value type. + public MemberReference IReadOnlyDictionaryAdapter2Lookup(TypeSignature keyType, TypeSignature valueType) + { + return IReadOnlyDictionaryAdapter2 + .MakeGenericReferenceType(keyType, valueType) + .ToTypeDefOrRef() + .CreateMemberReference("Lookup"u8, MethodSignature.CreateStatic( + returnType: new GenericParameterSignature(GenericParameterType.Type, 1), + parameterTypes: [ + IReadOnlyDictionary2.MakeGenericReferenceType( + new GenericParameterSignature(GenericParameterType.Type, 0), + new GenericParameterSignature(GenericParameterType.Type, 1)), + keyType])); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.IReadOnlyDictionaryAdapter<string, TValue>.Lookup. + /// + /// The input value type. + public MethodSpecification IReadOnlyDictionaryAdapterOfStringLookup(TypeSignature valueType) + { + return IReadOnlyDictionaryAdapterExtensions + .CreateMemberReference("Lookup"u8, MethodSignature.CreateStatic( + returnType: new GenericParameterSignature(GenericParameterType.Method, 0), + genericParameterCount: 1, + parameterTypes: [ + IReadOnlyDictionary2.MakeGenericReferenceType(_corLibTypeFactory.String, new GenericParameterSignature(GenericParameterType.Method, 0)), + ReadOnlySpanChar])) + .MakeGenericInstanceMethod(valueType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.IReadOnlyDictionaryAdapter<TKey, TValue>.Size. + /// + /// The input key type. + /// The input value type. + public MemberReference IReadOnlyDictionaryAdapter2Size(TypeSignature keyType, TypeSignature valueType) + { + return IReadOnlyDictionaryAdapter2 + .MakeGenericReferenceType(keyType, valueType) + .ToTypeDefOrRef() + .CreateMemberReference("Size"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.UInt32, + parameterTypes: [IReadOnlyDictionary2.MakeGenericReferenceType( + new GenericParameterSignature(GenericParameterType.Type, 0), + new GenericParameterSignature(GenericParameterType.Type, 1))])); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.IReadOnlyDictionaryAdapter<string, TValue>.HasKey. + /// + /// The input value type. + public MethodSpecification IReadOnlyDictionaryAdapterOfStringHasKey(TypeSignature valueType) + { + return IReadOnlyDictionaryAdapterExtensions + .CreateMemberReference("HasKey"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Boolean, + genericParameterCount: 1, + parameterTypes: [ + IReadOnlyDictionary2.MakeGenericReferenceType(_corLibTypeFactory.String, new GenericParameterSignature(GenericParameterType.Method, 0)), + ReadOnlySpanChar])) + .MakeGenericInstanceMethod(valueType); + } + + /// + /// Gets the for WindowsRuntime.InteropServices.IReadOnlyDictionaryAdapter<TKey, TValue>.Split. + /// + /// The input key type. + /// The input value type. + public MemberReference IReadOnlyDictionaryAdapter2Split(TypeSignature keyType, TypeSignature valueType) + { + TypeSignature readOnlyDictionaryType = IReadOnlyDictionary2.MakeGenericReferenceType( + new GenericParameterSignature(GenericParameterType.Type, 0), + new GenericParameterSignature(GenericParameterType.Type, 1)); + + return IReadOnlyDictionaryAdapter2 + .MakeGenericReferenceType(keyType, valueType) + .ToTypeDefOrRef() + .CreateMemberReference("Split"u8, MethodSignature.CreateStatic( + returnType: _corLibTypeFactory.Void, + parameterTypes: [ + readOnlyDictionaryType, + readOnlyDictionaryType.MakeByReferenceType(), + readOnlyDictionaryType.MakeByReferenceType()])); + } + /// /// Gets the for . /// diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IListAdapter{T}.cs b/src/WinRT.Runtime2/InteropServices/Collections/IListAdapter{T}.cs index 497d1dd36..e11851411 100644 --- a/src/WinRT.Runtime2/InteropServices/Collections/IListAdapter{T}.cs +++ b/src/WinRT.Runtime2/InteropServices/Collections/IListAdapter{T}.cs @@ -73,6 +73,9 @@ public static IReadOnlyList GetView(IList list) // 'IReadOnlyList'), this allows us to not allocate anything. That is, when native // code calls 'GetView()', it would get back the same CCW instance, just through a // 'QueryInterface' call for 'IVectorView' instead. + // + // The returned 'ReadOnlyCollection' type requires special handling in 'cswinrtgen' to + // be tracked correctly. If this implementation is changed, it needs to be kept in sync. return list as IReadOnlyList ?? new ReadOnlyCollection(list); } diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IReadOnlyDictionaryAdapterExtensions.cs b/src/WinRT.Runtime2/InteropServices/Collections/IReadOnlyDictionaryAdapterExtensions.cs new file mode 100644 index 000000000..353642cf3 --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Collections/IReadOnlyDictionaryAdapterExtensions.cs @@ -0,0 +1,116 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Concurrent; +using System.Collections.Frozen; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +#pragma warning disable IDE0045, IDE0046 + +namespace WindowsRuntime.InteropServices; + +/// +/// Extensions for the type. +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static class IReadOnlyDictionaryAdapterExtensions +{ + extension(IReadOnlyDictionaryAdapter) + { + /// + /// + /// This overload can be used to avoid a allocation on the caller side. + /// + public static TValue Lookup(IReadOnlyDictionary dictionary, ReadOnlySpan key) + { + bool found; + TValue? value; + + // Try to lookup via an alternate comparer, if we can get one. This allows us to avoid materializing + // the 'string' value for the key, whenever possible. In practice, these cases should pretty much + // cover almost all scenarios. Custom dictionaries are rare. We can't use an 'is' check for + // 'Dictionary', because we could have some + // user-defined type that reimplemented 'IReadOnlyDictionary.TryGetValue' with a + // different implementation that's not guaranteed to match what the alternate lookup does. So in + // those cases, we have to fallback to that method instead, hence the explicit type checks here. + // This is not needed for 'FrozenDictionary', as only the BCL can instantiate it. + if (dictionary.GetType() == typeof(Dictionary) && + Unsafe.As>(dictionary).TryGetAlternateLookup(out Dictionary.AlternateLookup> lookup1)) + { + found = lookup1.TryGetValue(key, out value); + } + else if (dictionary.GetType() == typeof(ConcurrentDictionary) && + Unsafe.As>(dictionary).TryGetAlternateLookup(out ConcurrentDictionary.AlternateLookup> lookup2)) + { + found = lookup2.TryGetValue(key, out value); + } + else if (dictionary is FrozenDictionary candidate3 && + candidate3.TryGetAlternateLookup(out FrozenDictionary.AlternateLookup> lookup3)) + { + found = lookup3.TryGetValue(key, out value); + } + else + { + found = dictionary.TryGetValue(key.ToString(), out value); + } + + // Throw the correct exception if the lookup failed + if (!found) + { + [DoesNotReturn] + static void ThrowKeyNotFoundException() + { + throw new KeyNotFoundException("Arg_KeyNotFoundWithKey") + { + HResult = WellKnownErrorCodes.E_BOUNDS + }; + } + + ThrowKeyNotFoundException(); + } + + return value!; + } + + /// + /// Determines whether the map view contains the specified key. + /// + /// The wrapped instance. + /// The key to locate in the map view. + /// Whether the key was found. + /// + /// This overload can be used to avoid a allocation on the caller side. + /// + /// /// + public static bool HasKey(IReadOnlyDictionary dictionary, ReadOnlySpan key) + { + // Same logic as in 'Lookup' above for trying to avoid materializing the 'string' key + if (dictionary.GetType() == typeof(Dictionary) && + Unsafe.As>(dictionary).TryGetAlternateLookup(out Dictionary.AlternateLookup> lookup1)) + { + return lookup1.ContainsKey(key); + } + + if (dictionary.GetType() == typeof(ConcurrentDictionary) && + Unsafe.As>(dictionary).TryGetAlternateLookup(out ConcurrentDictionary.AlternateLookup> lookup2)) + { + return lookup2.ContainsKey(key); + } + + if (dictionary is FrozenDictionary candidate3 && + candidate3.TryGetAlternateLookup(out FrozenDictionary.AlternateLookup> lookup3)) + { + return lookup3.ContainsKey(key); + } + + return dictionary.ContainsKey(key.ToString()); + } + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IReadOnlyDictionaryAdapter{TKey, TValue}.cs b/src/WinRT.Runtime2/InteropServices/Collections/IReadOnlyDictionaryAdapter{TKey, TValue}.cs new file mode 100644 index 000000000..be32c223b --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Collections/IReadOnlyDictionaryAdapter{TKey, TValue}.cs @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +namespace WindowsRuntime.InteropServices; + +/// +/// A stateless adapter for , to be exposed as Windows.Foundation.Collections.IMapView<K, V>. +/// +/// The type of keys in the dictionary. +/// The type of values in the dictionary. +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public static class IReadOnlyDictionaryAdapter +{ + /// + /// Returns the item at the specified key in the map view. + /// + /// The wrapped instance. + /// The key to locate in the map view. + /// The value, if an item with the specified key exists. + /// + public static TValue Lookup(IReadOnlyDictionary dictionary, TKey key) + { + // Try to lookup the key, and throw the right exception if it's not found. + // This is more efficient than using the indexer and then catching the + // exception if the lookup fails, adjusting the 'HRESUT', and re-throwing. + if (!dictionary.TryGetValue(key, out TValue? value)) + { + [DoesNotReturn] + static void ThrowKeyNotFoundException() + { + throw new KeyNotFoundException("Arg_KeyNotFoundWithKey") + { + HResult = WellKnownErrorCodes.E_BOUNDS + }; + } + + ThrowKeyNotFoundException(); + } + + return value; + } + + /// + /// Gets the number of elements in the map. + /// + /// The wrapped instance. + /// The number of elements in the map. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint Size(IReadOnlyDictionary dictionary) + { + return (uint)dictionary.Count; + } + + /// + /// Splits the map view into two views. + /// + /// The wrapped instance. + /// One half of the original map. + /// The second half of the original map. + /// + public static void Split( + IReadOnlyDictionary dictionary, + [NotNullIfNotNull(nameof(second))] out IReadOnlyDictionary? first, + [NotNullIfNotNull(nameof(first))] out IReadOnlyDictionary? second) + { + // If the input dictionary doesn't have enough items, just set both halves to 'null' + if (dictionary.Count < 2) + { + first = null; + second = null; + + return; + } + + // Get the split adapter, if we don't have one already. We can reuse the data + // from the first one we create, so we don't re-allocate and re-sort the full + // set of key-value pairs from the original input dictionary. + if (dictionary is not IReadOnlyDictionarySplitAdapter splitAdapter) + { + splitAdapter = new IReadOnlyDictionarySplitAdapter(dictionary); + } + + splitAdapter.Split(out first, out second); + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/Collections/IReadOnlyDictionarySplitAdapter{TKey, TValue}.cs b/src/WinRT.Runtime2/InteropServices/Collections/IReadOnlyDictionarySplitAdapter{TKey, TValue}.cs new file mode 100644 index 000000000..aeb2ebd97 --- /dev/null +++ b/src/WinRT.Runtime2/InteropServices/Collections/IReadOnlyDictionarySplitAdapter{TKey, TValue}.cs @@ -0,0 +1,218 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; + +namespace WindowsRuntime.InteropServices; + +/// +/// An adapter for with keys sorted in ascending order. +/// +/// The type of keys in the dictionary. +/// The type of values in the dictionary. +/// +[Obsolete(WindowsRuntimeConstants.PrivateImplementationDetailObsoleteMessage, + DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId, + UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)] +[EditorBrowsable(EditorBrowsableState.Never)] +public sealed class IReadOnlyDictionarySplitAdapter : IReadOnlyDictionary +{ + /// + /// The array of key-value pairs stored in the adapter. + /// + /// + /// These items are sorted with . + /// + private readonly KeyValuePair[] _items; + + /// + /// The index of the first item in the array that can be used by this instance. + /// + private readonly int _firstItemIndex; + + /// + /// The index of the last item in the array that can be used by this instance. + /// + private readonly int _lastItemIndex; + + /// + /// The instance, if initialized. + /// + private ReadOnlyDictionaryKeyCollection? _keys; + + /// + /// The instance, if initialized. + /// + private ReadOnlyDictionaryValueCollection? _values; + + /// + /// Creates a new instance with the specified parameters. + /// + /// The array of key-value pairs to store in the adapter. + /// The index of the first item in the array that can be used by this instance. + /// The index of the last item in the array that can be used by this instance. + private IReadOnlyDictionarySplitAdapter(KeyValuePair[] items, int firstItemIndex, int lastItemIndex) + { + _items = items; + _firstItemIndex = firstItemIndex; + _lastItemIndex = lastItemIndex; + } + + /// + /// Creates a new instance with the specified parameters. + /// + /// The source instance. + internal IReadOnlyDictionarySplitAdapter(IReadOnlyDictionary dictionary) + { + ArgumentNullException.ThrowIfNull(dictionary); + + _firstItemIndex = 0; + _lastItemIndex = dictionary.Count - 1; + _items = CreateKeyValueArray(dictionary); + } + + /// + public IEnumerable Keys => _keys ??= new ReadOnlyDictionaryKeyCollection(this); + + /// + public IEnumerable Values => _values ??= new ReadOnlyDictionaryValueCollection(this); + + /// + public int Count => _lastItemIndex - _firstItemIndex + 1; + + /// + public TValue this[TKey key] + { + get + { + // Try to lookup the key, and throw the right exception if it's not found. + // We don't need to adjust the 'HRESULT' here, it'll be done separately. + if (!TryGetValue(key, out TValue? value)) + { + [DoesNotReturn] + static void ThrowKeyNotFoundException() => throw new KeyNotFoundException("Arg_KeyNotFoundWithKey"); + + ThrowKeyNotFoundException(); + } + + return value!; + } + } + + /// + /// Splits the dictionary into two instances, each representing a sorted half. + /// + /// One half of the original dictionary. + /// The second half of the original dictionary. + public void Split( + [NotNullIfNotNull(nameof(second))] out IReadOnlyDictionary? first, + [NotNullIfNotNull(nameof(first))] out IReadOnlyDictionary? second) + { + if (Count < 2) + { + first = null; + second = null; + + return; + } + + int pivot = (int)(uint)(((ulong)(uint)_firstItemIndex + (uint)_lastItemIndex) / 2UL); + + first = new IReadOnlyDictionarySplitAdapter(_items, _firstItemIndex, pivot); + second = new IReadOnlyDictionarySplitAdapter(_items, pivot + 1, _lastItemIndex); + } + + /// + public bool ContainsKey(TKey key) + { + return TryGetValue(key, out _); + } + + /// + public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) + { + KeyValuePair searchKey = new(key, default!); + + // Do a binary search for the input key (the array is sorted upon construction) + int index = Array.BinarySearch( + array: _items, + index: _firstItemIndex, + length: Count, + value: searchKey, + comparer: KeyValuePairComparer.Instance); + + // If the key was not found, just stop here + if (index < 0) + { + value = default; + + return false; + } + + // Lookup the found pair in the array and return the value + value = _items[index].Value; + + return true; + } + + /// + public IEnumerator> GetEnumerator() + { + // The returned 'ArraySegment>.Enumerator' type requires special handling in + // 'cswinrtgen' to be tracked correctly. If this implementation is changed, it needs to be kept in sync. + return new ArraySegment>(_items, _firstItemIndex, Count).GetEnumerator(); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Prepares the sorted array of key-value pairs from an input dictionary. + /// + /// The source instance. + /// The resulting sorted array of key-value pairs from an input dictionary. + private static KeyValuePair[] CreateKeyValueArray(IReadOnlyDictionary dictionary) + { + KeyValuePair[] array = new KeyValuePair[dictionary.Count]; + + // Get all key-value pairs from the dictionary (we avoid 'ToArray' to avoid using LINQ) + using (IEnumerator> enumerator = dictionary.GetEnumerator()) + { + int i = 0; + + while (enumerator.MoveNext()) + { + array[i++] = enumerator.Current; + } + } + + // Sort the items based on their keys + Array.Sort(array, KeyValuePairComparer.Instance); + + return array; + } + + /// + /// A comparer for that only checks . + /// + private sealed class KeyValuePairComparer : IComparer> + { + /// + /// The singleton instance. + /// + public static readonly KeyValuePairComparer Instance = new(); + + /// + public int Compare(KeyValuePair x, KeyValuePair y) + { + return Comparer.Default.Compare(x.Key, y.Key); + } + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime2/InteropServices/Vtables/IMapViewVftbl.cs b/src/WinRT.Runtime2/InteropServices/Vtables/IMapViewVftbl.cs deleted file mode 100644 index 0fb57d8e6..000000000 --- a/src/WinRT.Runtime2/InteropServices/Vtables/IMapViewVftbl.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Runtime.InteropServices; -using Windows.Foundation; - -namespace WindowsRuntime.InteropServices; - -/// -/// Binding type for the IMapView<K, V> interface vtable. -/// -/// -[StructLayout(LayoutKind.Sequential)] -internal unsafe struct IMapViewVftbl -{ - public delegate* unmanaged[MemberFunction] QueryInterface; - public delegate* unmanaged[MemberFunction] AddRef; - public delegate* unmanaged[MemberFunction] Release; - public delegate* unmanaged[MemberFunction] GetIids; - public delegate* unmanaged[MemberFunction] GetRuntimeClassName; - public delegate* unmanaged[MemberFunction] GetTrustLevel; - - // See notes in 'IVectorViewVftbl' regarding ABI mismatches for the by-value parameters below - public delegate* unmanaged[MemberFunction] Lookup; - public delegate* unmanaged[MemberFunction] get_Size; - public delegate* unmanaged[MemberFunction] HasKey; - public delegate* unmanaged[MemberFunction] Split; -} \ No newline at end of file