diff --git a/src/WinRT.Interop.Generator/Extensions/CilInstructionExtensions.cs b/src/WinRT.Interop.Generator/Extensions/CilInstructionExtensions.cs
index 0f97ba309..140b96d6c 100644
--- a/src/WinRT.Interop.Generator/Extensions/CilInstructionExtensions.cs
+++ b/src/WinRT.Interop.Generator/Extensions/CilInstructionExtensions.cs
@@ -1,8 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
+using System.Diagnostics.CodeAnalysis;
+using AsmResolver.DotNet;
using AsmResolver.DotNet.Code.Cil;
+using AsmResolver.DotNet.Signatures;
using AsmResolver.PE.DotNet.Cil;
+using AsmResolver.PE.DotNet.Metadata.Tables;
using static AsmResolver.PE.DotNet.Cil.CilOpCodes;
namespace WindowsRuntime.InteropGenerator;
@@ -69,5 +73,33 @@ public static CilInstruction CreateLdloc(CilLocalVariable local, CilMethodBody m
int i => new CilInstruction(Ldloc, i)
};
}
+
+ ///
+ /// Create a new instruction storing a value indirectly to a target location.
+ ///
+ /// The type of value to store.
+ /// The in use.
+ /// The instruction.
+ [SuppressMessage("Style", "IDE0072", Justification = "We use 'stobj' for all other possible types.")]
+ public static CilInstruction CreateStind(TypeSignature type, ModuleDefinition module)
+ {
+ return type.ElementType switch
+ {
+ ElementType.Boolean => new CilInstruction(Stind_I1),
+ ElementType.Char => new CilInstruction(Stind_I2),
+ ElementType.I1 => new CilInstruction(Stind_I1),
+ ElementType.U1 => new CilInstruction(Stind_I1),
+ ElementType.I2 => new CilInstruction(Stind_I2),
+ ElementType.U2 => new CilInstruction(Stind_I2),
+ ElementType.I4 => new CilInstruction(Stind_I4),
+ ElementType.U4 => new CilInstruction(Stind_I4),
+ ElementType.I8 => new CilInstruction(Stind_I8),
+ ElementType.U8 => new CilInstruction(Stind_I8),
+ ElementType.R4 => new CilInstruction(Stind_R4),
+ ElementType.R8 => new CilInstruction(Stind_R8),
+ ElementType.ValueType when type.Resolve() is { IsClass: true, IsEnum: true } => new CilInstruction(Stind_I4),
+ _ => new CilInstruction(Stobj, type.Import(module).ToTypeDefOrRef()),
+ };
+ }
}
}
\ No newline at end of file
diff --git a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs
index 1ade639d2..3a7805e0e 100644
--- a/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs
+++ b/src/WinRT.Interop.Generator/Extensions/WindowsRuntimeExtensions.cs
@@ -2,6 +2,7 @@
// Licensed under the MIT License.
using System;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
using AsmResolver;
using AsmResolver.DotNet;
@@ -49,27 +50,27 @@ public bool TryGetGuidAttribute(InteropReferences interopReferences, out Guid ii
extension(ITypeDescriptor type)
{
///
- /// Checks whether an is some type.
+ /// Checks whether an is some type.
///
- /// Whether the type is some type.
+ /// Whether the type is some type.
public bool IsTypeOfGuid(InteropReferences interopReferences)
{
return SignatureComparer.IgnoreVersion.Equals(type, interopReferences.Guid);
}
///
- /// Checks whether an is some type.
+ /// Checks whether an is some type.
///
- /// Whether the type is some type.
+ /// Whether the type is some type.
public bool IsTypeOfType(InteropReferences interopReferences)
{
return SignatureComparer.IgnoreVersion.Equals(type, interopReferences.Type);
}
///
- /// Checks whether an is some type.
+ /// Checks whether an is some type.
///
- /// Whether the type is some type.
+ /// Whether the type is some type.
public bool IsTypeOfException(InteropReferences interopReferences)
{
return SignatureComparer.IgnoreVersion.Equals(type, interopReferences.Exception);
@@ -675,20 +676,50 @@ public bool IsConstructedKeyValuePairType(InteropReferences interopReferences)
}
///
- /// Checks whether a is some type.
+ /// Checks whether a is some type.
///
/// The instance to use.
- /// Whether the type is some type.
+ /// Whether the type is some type.
public bool IsConstructedNullableValueType(InteropReferences interopReferences)
{
return SignatureComparer.IgnoreVersion.Equals((signature as GenericInstanceTypeSignature)?.GenericType, interopReferences.Nullable1);
}
///
- /// Checks whether a is some or type.
+ /// Tries to extract the underlying type from a constructed type.
///
/// The instance to use.
- /// Whether the type is some or type.
+ /// The underlying nullable type, if the input type is a constructed type.
+ /// Whether was successfully retrieved.
+ public bool TryGetNullableUnderlyingType(InteropReferences interopReferences, [NotNullWhen(true)] out TypeSignature? underlyingType)
+ {
+ // First check that we have some constructed generic value type.
+ // We also check that we have a single type argument to narrow down.
+ if (signature is not GenericInstanceTypeSignature { IsValueType: true, TypeArguments: [TypeSignature typeArgument] } genericSignature)
+ {
+ underlyingType = null;
+
+ return false;
+ }
+
+ // Check that we actually have a constructed 'Nullable' type
+ if (!SignatureComparer.IgnoreVersion.Equals(genericSignature.GenericType, interopReferences.Nullable1))
+ {
+ underlyingType = null;
+
+ return false;
+ }
+
+ underlyingType = typeArgument;
+
+ return true;
+ }
+
+ ///
+ /// Checks whether a is some or type.
+ ///
+ /// The instance to use.
+ /// Whether the type is some or type.
public bool IsConstructedSpanOrReadOnlySpanType(InteropReferences interopReferences)
{
if (signature is not GenericInstanceTypeSignature genericSignature)
diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedParameter.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedParameter.cs
index e4e1003d0..f4644c925 100644
--- a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedParameter.cs
+++ b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ManagedParameter.cs
@@ -9,12 +9,15 @@
using WindowsRuntime.InteropGenerator.Errors;
using WindowsRuntime.InteropGenerator.Generation;
using WindowsRuntime.InteropGenerator.References;
+using WindowsRuntime.InteropGenerator.Resolvers;
using static AsmResolver.PE.DotNet.Cil.CilOpCodes;
namespace WindowsRuntime.InteropGenerator.Factories;
-///
-internal partial class InteropMethodRewriteFactory
+///
+/// A factory to rewrite interop method definitons, and add marshalling code as needed.
+///
+internal static partial class InteropMethodRewriteFactory
{
///
/// Contains the logic for marshalling managed parameters (i.e. parameters that are passed to managed methods).
@@ -73,49 +76,26 @@ public static void RewriteMethod(
{
body.Instructions.ReferenceReplaceRange(marker, CilInstruction.CreateLdarg(parameterIndex));
}
- else if (parameterType.IsConstructedKeyValuePairType(interopReferences))
- {
- // If the type is some constructed 'KeyValuePair<,>' type, we use the generated marshaller
- body.Instructions.ReferenceReplaceRange(marker, [
- CilInstruction.CreateLdarg(parameterIndex),
- new CilInstruction(Call, emitState.LookupTypeDefinition(parameterType, "Marshaller").GetMethod("ConvertToManaged"))]);
- }
else if (parameterType.IsConstructedNullableValueType(interopReferences))
{
- TypeSignature underlyingType = ((GenericInstanceTypeSignature)parameterType).TypeArguments[0];
-
- // For 'Nullable' return types, we need the marshaller for the instantiated 'T' type (same as for return values)
- ITypeDefOrRef marshallerType = GetValueTypeMarshallerType(underlyingType, interopReferences, emitState);
-
- // Get the right reference to the unboxing marshalling method to call
- IMethodDefOrRef marshallerMethod = marshallerType.GetMethodDefOrRef(
- name: "UnboxToManaged"u8,
- signature: MethodSignature.CreateStatic(
- returnType: parameterType,
- parameterTypes: [module.CorLibTypeFactory.Void.MakePointerType()]));
+ InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(parameterType, interopReferences, emitState);
- // Emit code similar to 'KeyValuePair<,>' above, to marshal the resulting 'Nullable' value
+ // For 'Nullable' parameters (i.e. we have an 'IReference' interface pointer), we unbox the underlying type
body.Instructions.ReferenceReplaceRange(marker, [
CilInstruction.CreateLdarg(parameterIndex),
- new CilInstruction(Call, marshallerMethod.Import(module))]);
+ new CilInstruction(Call, marshallerType.UnboxToManaged().Import(module))]);
}
else
{
// The last case handles all other value types. It doesn't matter if they possibly hold some unmanaged
// resources, as they're only being used as parameters. That means the caller is responsible for disposal.
- ITypeDefOrRef marshallerType = GetValueTypeMarshallerType(parameterType, interopReferences, emitState);
-
- // Get the reference to 'ConvertToManaged' to produce the resulting value to return
- IMethodDefOrRef marshallerMethod = marshallerType.GetMethodDefOrRef(
- name: "ConvertToManaged"u8,
- signature: MethodSignature.CreateStatic(
- returnType: parameterType,
- parameterTypes: [parameterType.GetAbiType(interopReferences)]));
+ // This case can also handle 'KeyValuePair<,>' instantiations, which are just marshalled normally too.
+ InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(parameterType, interopReferences, emitState);
// We can directly call the marshaller and return it, no 'try/finally' complexity is needed
body.Instructions.ReferenceReplaceRange(marker, [
CilInstruction.CreateLdarg(parameterIndex),
- new CilInstruction(Call, marshallerMethod.Import(module))]);
+ new CilInstruction(Call, marshallerType.ConvertToManaged().Import(module))]);
}
}
else if (parameterType.IsTypeOfString())
@@ -125,29 +105,15 @@ public static void RewriteMethod(
CilInstruction.CreateLdarg(parameterIndex),
new CilInstruction(Call, interopReferences.HStringMarshallerConvertToManaged.Import(module))]);
}
- else if (parameterType is GenericInstanceTypeSignature)
- {
- // This case (constructed interfaces or delegates) is effectively identical to marshalling 'KeyValuePair<,>' values
- body.Instructions.ReferenceReplaceRange(marker, [
- CilInstruction.CreateLdarg(parameterIndex),
- new CilInstruction(Call, emitState.LookupTypeDefinition(parameterType, "Marshaller").GetMethod("ConvertToManaged"))]);
- }
else
{
- // Get the marshaller type for all other reference types
- ITypeDefOrRef marshallerType = GetReferenceTypeMarshallerType(parameterType, interopReferences, emitState);
-
- // Get the marshalling method, with the parameter type always just being 'void*' here too
- IMethodDefOrRef marshallerMethod = marshallerType.GetMethodDefOrRef(
- name: "ConvertToManaged"u8,
- signature: MethodSignature.CreateStatic(
- returnType: parameterType,
- parameterTypes: [module.CorLibTypeFactory.Void.MakePointerType()]));
+ // Get the marshaller type for all other reference types (including generics)
+ InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(parameterType, interopReferences, emitState);
- // Marshal the value and release the original interface pointer
+ // Marshal the value normally (the caller will own the native resource)
body.Instructions.ReferenceReplaceRange(marker, [
CilInstruction.CreateLdarg(parameterIndex),
- new CilInstruction(Call, marshallerMethod.Import(module))]);
+ new CilInstruction(Call, marshallerType.ConvertToManaged().Import(module))]);
}
}
}
diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.NativeParameter.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.NativeParameter.cs
index 71acbfd64..29ac2bb6a 100644
--- a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.NativeParameter.cs
+++ b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.NativeParameter.cs
@@ -10,6 +10,7 @@
using WindowsRuntime.InteropGenerator.Errors;
using WindowsRuntime.InteropGenerator.Generation;
using WindowsRuntime.InteropGenerator.References;
+using WindowsRuntime.InteropGenerator.Resolvers;
using static AsmResolver.PE.DotNet.Cil.CilOpCodes;
#pragma warning disable CS1573
@@ -100,17 +101,7 @@ public static void RewriteMethod(
}
else if (parameterType.IsConstructedNullableValueType(interopReferences))
{
- TypeSignature underlyingType = ((GenericInstanceTypeSignature)parameterType).TypeArguments[0];
-
- // For 'Nullable' return types, we need the marshaller for the instantiated 'T' type (same as for return values)
- ITypeDefOrRef marshallerType = GetValueTypeMarshallerType(underlyingType, interopReferences, emitState);
-
- // Get the right reference to the unboxing marshalling method to call
- IMethodDefOrRef marshallerMethod = marshallerType.GetMethodDefOrRef(
- name: "BoxToUnmanaged"u8,
- signature: MethodSignature.CreateStatic(
- returnType: interopReferences.WindowsRuntimeObjectReferenceValue.ToValueTypeSignature(),
- parameterTypes: [parameterType]));
+ InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(parameterType, interopReferences, emitState);
RewriteBody(
parameterType: parameterType,
@@ -119,7 +110,7 @@ public static void RewriteMethod(
loadMarker: loadMarker,
finallyMarker: finallyMarker,
parameterIndex: parameterIndex,
- marshallerMethod: marshallerMethod,
+ marshallerMethod: marshallerType.BoxToUnmanaged(),
disposeMethod: null,
interopReferences: interopReferences,
module: module);
@@ -127,21 +118,7 @@ public static void RewriteMethod(
else
{
// The last case handles all other value types, which need explicit disposal for their ABI values
- ITypeDefOrRef marshallerType = GetValueTypeMarshallerType(parameterType, interopReferences, emitState);
-
- // Get the reference to 'ConvertToUnmanaged' to produce the resulting value to pass as argument
- IMethodDefOrRef marshallerMethod = marshallerType.GetMethodDefOrRef(
- name: "ConvertToUnmanaged"u8,
- signature: MethodSignature.CreateStatic(
- returnType: parameterType.GetAbiType(interopReferences),
- parameterTypes: [parameterType]));
-
- // Get the reference to 'Dispose' method to call on the ABI value
- IMethodDefOrRef disposeMethod = marshallerType.GetMethodDefOrRef(
- name: "Dispose"u8,
- signature: MethodSignature.CreateStatic(
- returnType: interopReferences.CorLibTypeFactory.Void,
- parameterTypes: [parameterType.GetAbiType(interopReferences)]));
+ InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(parameterType, interopReferences, emitState);
RewriteBody(
parameterType: parameterType,
@@ -150,8 +127,8 @@ public static void RewriteMethod(
loadMarker: loadMarker,
finallyMarker: finallyMarker,
parameterIndex: parameterIndex,
- marshallerMethod: marshallerMethod,
- disposeMethod: disposeMethod,
+ marshallerMethod: marshallerType.ConvertToUnmanaged(),
+ disposeMethod: marshallerType.Dispose(),
interopReferences: interopReferences,
module: module);
}
@@ -189,14 +166,7 @@ public static void RewriteMethod(
else
{
// Get the marshaller for all other types (doesn't matter if constructed generics or not)
- ITypeDefOrRef marshallerType = GetReferenceTypeMarshallerType(parameterType, interopReferences, emitState);
-
- // Get the reference to 'ConvertToUnmanaged' to produce the resulting value to pass as argument
- IMethodDefOrRef marshallerMethod = marshallerType.GetMethodDefOrRef(
- name: "ConvertToUnmanaged"u8,
- signature: MethodSignature.CreateStatic(
- returnType: interopReferences.WindowsRuntimeObjectReferenceValue.ToValueTypeSignature(),
- parameterTypes: [parameterType]));
+ InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(parameterType, interopReferences, emitState);
RewriteBody(
parameterType: parameterType,
@@ -205,7 +175,7 @@ public static void RewriteMethod(
loadMarker: loadMarker,
finallyMarker: finallyMarker,
parameterIndex: parameterIndex,
- marshallerMethod: marshallerMethod,
+ marshallerMethod: marshallerType.ConvertToUnmanaged(),
disposeMethod: null,
interopReferences: interopReferences,
module: module);
diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.RetVal.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.RetVal.cs
index 6188ebdc9..f7fea568c 100644
--- a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.RetVal.cs
+++ b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.RetVal.cs
@@ -5,10 +5,10 @@
using AsmResolver.DotNet.Code.Cil;
using AsmResolver.DotNet.Signatures;
using AsmResolver.PE.DotNet.Cil;
-using AsmResolver.PE.DotNet.Metadata.Tables;
using WindowsRuntime.InteropGenerator.Errors;
using WindowsRuntime.InteropGenerator.Generation;
using WindowsRuntime.InteropGenerator.References;
+using WindowsRuntime.InteropGenerator.Resolvers;
using static AsmResolver.PE.DotNet.Cil.CilOpCodes;
#pragma warning disable CS1573, IDE0072
@@ -71,25 +71,7 @@ public static void RewriteMethod(
// However, we must use the correct indirect store instruction for primitive types.
if (retValType.IsBlittable(interopReferences))
{
- CilInstruction storeInstruction = retValType.ElementType switch
- {
- ElementType.Boolean => new CilInstruction(Stind_I1),
- ElementType.Char => new CilInstruction(Stind_I2),
- ElementType.I1 => new CilInstruction(Stind_I1),
- ElementType.U1 => new CilInstruction(Stind_I1),
- ElementType.I2 => new CilInstruction(Stind_I2),
- ElementType.U2 => new CilInstruction(Stind_I2),
- ElementType.I4 => new CilInstruction(Stind_I4),
- ElementType.U4 => new CilInstruction(Stind_I4),
- ElementType.I8 => new CilInstruction(Stind_I8),
- ElementType.U8 => new CilInstruction(Stind_I8),
- ElementType.R4 => new CilInstruction(Stind_R4),
- ElementType.R8 => new CilInstruction(Stind_R8),
- ElementType.ValueType when retValType.Resolve() is { IsClass: true, IsEnum: true } => new CilInstruction(Stind_I4),
- _ => new CilInstruction(Stobj, retValType.Import(module).ToTypeDefOrRef()),
- };
-
- body.Instructions.ReferenceReplaceRange(marker, [storeInstruction]);
+ body.Instructions.ReferenceReplaceRange(marker, [CilInstruction.CreateStind(retValType, module)]);
}
else if (retValType.IsConstructedKeyValuePairType(interopReferences))
{
@@ -103,42 +85,24 @@ public static void RewriteMethod(
}
else if (retValType.IsConstructedNullableValueType(interopReferences))
{
- TypeSignature underlyingType = ((GenericInstanceTypeSignature)retValType).TypeArguments[0];
-
- // For 'Nullable' return types, we need the marshaller for the instantiated 'T'
- // type, as that will contain the boxing methods. See more info in 'ReturnValue'.
- ITypeDefOrRef marshallerType = GetValueTypeMarshallerType(underlyingType, interopReferences, emitState);
-
- // Get the right reference to the boxing marshalling method to call
- IMethodDefOrRef marshallerMethod = marshallerType.GetMethodDefOrRef(
- name: "BoxToManaged"u8,
- signature: MethodSignature.CreateStatic(
- returnType: retValType,
- parameterTypes: [module.CorLibTypeFactory.Void.MakePointerType()]));
+ InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(retValType, interopReferences, emitState);
// Emit code similar to 'KeyValuePair<,>' above, to marshal the resulting 'Nullable' value
RewriteBody(
body: body,
marker: marker,
- marshallerMethod: marshallerMethod,
+ marshallerMethod: marshallerType.BoxToUnmanaged(),
interopReferences: interopReferences,
module: module);
}
else
{
// For all other struct types, we just always defer to their generated marshaller type
- ITypeDefOrRef marshallerType = GetValueTypeMarshallerType(retValType, interopReferences, emitState);
-
- // Get the reference to 'ConvertToUnmanaged' to produce the resulting value to return
- IMethodDefOrRef marshallerMethod = marshallerType.GetMethodDefOrRef(
- name: "ConvertToUnmanaged"u8,
- signature: MethodSignature.CreateStatic(
- returnType: retValType,
- parameterTypes: [retValType.GetAbiType(interopReferences)]));
+ InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(retValType, interopReferences, emitState);
// Delegate to the marshaller to convert the managed value type on the evaluation stack
body.Instructions.ReferenceReplaceRange(marker, [
- new CilInstruction(Call, marshallerMethod.Import(module)),
+ new CilInstruction(Call, marshallerType.ConvertToUnmanaged().Import(module)),
new CilInstruction(Stobj, retValType.GetAbiType(interopReferences).Import(module).ToTypeDefOrRef())]);
}
}
@@ -163,33 +127,16 @@ public static void RewriteMethod(
new CilInstruction(Call, interopReferences.ExceptionMarshallerConvertToUnmanaged.Import(module)),
new CilInstruction(Stobj, interopReferences.AbiException.Import(module).ToTypeDefOrRef())]);
}
- else if (retValType is GenericInstanceTypeSignature)
- {
- // For all other generic type instantiations, we use the marshaller in 'WinRT.Interop.dll'
- RewriteBody(
- body: body,
- marker: marker,
- marshallerMethod: emitState.LookupTypeDefinition(retValType, "Marshaller").GetMethod("ConvertToUnmanaged"),
- interopReferences: interopReferences,
- module: module);
- }
else
{
// Get the marshaller type for either generic reference types, or all other reference types
- ITypeDefOrRef marshallerType = GetReferenceTypeMarshallerType(retValType, interopReferences, emitState);
-
- // Get the marshalling method for this '[retval]' type
- IMethodDefOrRef marshallerMethod = marshallerType.GetMethodDefOrRef(
- name: "ConvertToUnmanaged"u8,
- signature: MethodSignature.CreateStatic(
- returnType: interopReferences.WindowsRuntimeObjectReferenceValue.ToValueTypeSignature(),
- parameterTypes: [retValType]));
+ InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(retValType, interopReferences, emitState);
// Marshal the value and assign it to the target location
RewriteBody(
body: body,
marker: marker,
- marshallerMethod: marshallerMethod,
+ marshallerMethod: marshallerType.ConvertToUnmanaged(),
interopReferences: interopReferences,
module: module);
}
diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ReturnValue.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ReturnValue.cs
index d0e1e6785..1e40be5ca 100644
--- a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ReturnValue.cs
+++ b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.ReturnValue.cs
@@ -8,6 +8,7 @@
using WindowsRuntime.InteropGenerator.Errors;
using WindowsRuntime.InteropGenerator.Generation;
using WindowsRuntime.InteropGenerator.References;
+using WindowsRuntime.InteropGenerator.Resolvers;
using static AsmResolver.PE.DotNet.Cil.CilOpCodes;
#pragma warning disable CS1573
@@ -90,21 +91,7 @@ public static void RewriteMethod(
}
else if (returnType.IsConstructedNullableValueType(interopReferences))
{
- TypeSignature underlyingType = ((GenericInstanceTypeSignature)returnType).TypeArguments[0];
-
- // For 'Nullable' return types, we need the marshaller for the instantiated 'T'
- // type, as that will contain the unboxing methods. The 'T' in this case can be
- // a custom-mapped primitive type or a projected type. Technically speaking it can
- // never be a 'KeyValuePair<,>' or 'Nullable', because both of those are interface
- // types in the Windows Runtime type system, meaning they can't be boxed like value types.
- ITypeDefOrRef marshallerType = GetValueTypeMarshallerType(underlyingType, interopReferences, emitState);
-
- // Get the right reference to the unboxing marshalling method to call
- IMethodDefOrRef marshallerMethod = marshallerType.GetMethodDefOrRef(
- name: "UnboxToManaged"u8,
- signature: MethodSignature.CreateStatic(
- returnType: returnType,
- parameterTypes: [module.CorLibTypeFactory.Void.MakePointerType()]));
+ InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(returnType, interopReferences, emitState);
// Emit code similar to 'KeyValuePair<,>' above, to marshal the resulting 'Nullable' value
RewriteBody(
@@ -112,7 +99,7 @@ public static void RewriteMethod(
body: body,
marker: marker,
source: source,
- marshallerMethod: marshallerMethod,
+ marshallerMethod: marshallerType.UnboxToManaged(),
releaseOrDisposeMethod: interopReferences.WindowsRuntimeUnknownMarshallerFree,
module: module);
}
@@ -121,21 +108,7 @@ public static void RewriteMethod(
// Here we're marshalling a value type that is managed, meaning its ABI type will
// hold some references to unmanaged resources. In this case we need to resolve the
// marshaller type so we can both marshal the value and also clean resources after.
- ITypeDefOrRef marshallerType = GetValueTypeMarshallerType(returnType, interopReferences, emitState);
-
- // Get the reference to 'ConvertToManaged' to produce the resulting value to return
- IMethodDefOrRef marshallerMethod = marshallerType.GetMethodDefOrRef(
- name: "ConvertToManaged"u8,
- signature: MethodSignature.CreateStatic(
- returnType: returnType,
- parameterTypes: [returnType.GetAbiType(interopReferences)]));
-
- // Get the reference to 'Dispose' too, as the ABI type has some unmanaged references
- IMethodDefOrRef disposeMethod = marshallerType.GetMethodDefOrRef(
- name: "Dispose"u8,
- signature: MethodSignature.CreateStatic(
- returnType: module.CorLibTypeFactory.Void,
- parameterTypes: [returnType.GetAbiType(interopReferences)]));
+ InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(returnType, interopReferences, emitState);
// Emit code similar to the cases above, but calling 'Dispose' on the ABI type instead of releasing it
RewriteBody(
@@ -143,27 +116,20 @@ public static void RewriteMethod(
body: body,
marker: marker,
source: source,
- marshallerMethod: marshallerMethod,
- releaseOrDisposeMethod: disposeMethod,
+ marshallerMethod: marshallerType.ConvertToManaged(),
+ releaseOrDisposeMethod: marshallerType.Dispose(),
module: module);
}
else
{
// The last case is a non-blittable, unmanaged value type. That is, we still have to call
// the marshalling method to get the return value, but no resources cleanup is needed.
- ITypeDefOrRef marshallerType = GetValueTypeMarshallerType(returnType, interopReferences, emitState);
-
- // Get the reference to 'ConvertToManaged' to produce the resulting value to return
- IMethodDefOrRef marshallerMethod = marshallerType.GetMethodDefOrRef(
- name: "ConvertToManaged"u8,
- signature: MethodSignature.CreateStatic(
- returnType: returnType,
- parameterTypes: [returnType.GetAbiType(interopReferences)]));
+ InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(returnType, interopReferences, emitState);
// We can directly call the marshaller and return it, no 'try/finally' complexity is needed
body.Instructions.ReferenceReplaceRange(marker, [
CilInstruction.CreateLdloc(source, body),
- new CilInstruction(Call, marshallerMethod.Import(module)),
+ new CilInstruction(Call, marshallerType.ConvertToManaged().Import(module)),
new CilInstruction(Ret)]);
}
}
@@ -199,31 +165,10 @@ public static void RewriteMethod(
new CilInstruction(Call, interopReferences.ExceptionMarshallerConvertToManaged.Import(module)),
new CilInstruction(Ret)]);
}
- else if (returnType is GenericInstanceTypeSignature)
- {
- // This case (constructed interfaces or delegates) is effectively identical to marshalling
- // 'KeyValuePair<,>' values: the marshalling code will always be in 'WinRT.Interop.dll', the
- // ABI type will always just be 'void*', and we will always release the interface pointer.
- RewriteBody(
- returnType: returnType,
- body: body,
- marker: marker,
- source: source,
- marshallerMethod: emitState.LookupTypeDefinition(returnType, "Marshaller").GetMethod("ConvertToManaged"),
- releaseOrDisposeMethod: interopReferences.WindowsRuntimeUnknownMarshallerFree,
- module: module);
- }
else
{
// Get the marshaller type for either generic reference types, or all other reference types
- ITypeDefOrRef marshallerType = GetReferenceTypeMarshallerType(returnType, interopReferences, emitState);
-
- // Get the marshalling method, with the parameter type always just being 'void*' here too
- IMethodDefOrRef marshallerMethod = marshallerType.GetMethodDefOrRef(
- name: "ConvertToManaged"u8,
- signature: MethodSignature.CreateStatic(
- returnType: returnType,
- parameterTypes: [module.CorLibTypeFactory.Void.MakePointerType()]));
+ InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(returnType, interopReferences, emitState);
// Marshal the value and release the original interface pointer
RewriteBody(
@@ -231,7 +176,7 @@ public static void RewriteMethod(
body: body,
marker: marker,
source: source,
- marshallerMethod: marshallerMethod,
+ marshallerMethod: marshallerType.ConvertToManaged(),
releaseOrDisposeMethod: interopReferences.WindowsRuntimeUnknownMarshallerFree,
module: module);
}
diff --git a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.cs b/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.cs
deleted file mode 100644
index 39db3a7a5..000000000
--- a/src/WinRT.Interop.Generator/Factories/InteropMethodRewriteFactory.cs
+++ /dev/null
@@ -1,105 +0,0 @@
-// Copyright (c) Microsoft Corporation.
-// Licensed under the MIT License.
-
-using AsmResolver.DotNet;
-using AsmResolver.DotNet.Signatures;
-using WindowsRuntime.InteropGenerator.Generation;
-using WindowsRuntime.InteropGenerator.References;
-
-namespace WindowsRuntime.InteropGenerator.Factories;
-
-///
-/// A factory to rewrite interop method definitons, and add marshalling code as needed.
-///
-internal static partial class InteropMethodRewriteFactory
-{
- ///
- /// Get the marshaller type for a specified value type.
- ///
- /// The value type to get the marshaller type for.
- /// The instance to use.
- /// The emit state for this invocation.
- /// The marshaller type for .
- private static ITypeDefOrRef GetValueTypeMarshallerType(
- TypeSignature type,
- InteropReferences interopReferences,
- InteropGeneratorEmitState emitState)
- {
- // For generic instantiations (the only one possible is 'KeyValuePair<,>'
- // here), the marshaller type will be in the same 'WinRT.Interop.dll'.
- if (type is GenericInstanceTypeSignature)
- {
- return emitState.LookupTypeDefinition(type, "Marshaller");
- }
-
- // For primitive types, the marshaller type is in 'WinRT.Runtime.dll'.
- // In this case we can also rely on all types being under 'System'.
- if (type.IsFundamentalWindowsRuntimeType(interopReferences))
- {
- return interopReferences.WindowsRuntimeModule.CreateTypeReference(
- ns: "ABI.System"u8,
- name: $"{type.Name}Marshaller");
- }
-
- // 'TimeSpan' is custom-mapped and not blittable
- if (SignatureComparer.IgnoreVersion.Equals(type, interopReferences.TimeSpan))
- {
- return interopReferences.TimeSpanMarshaller;
- }
-
- // 'DateTimeOffset' also is custom-mapped and not blittable
- if (SignatureComparer.IgnoreVersion.Equals(type, interopReferences.DateTimeOffset))
- {
- return interopReferences.DateTimeOffsetMarshaller;
- }
-
- // In all other cases, the marshaller type will be in the declared assembly
- return type.Resolve()!.DeclaringModule!.CreateTypeReference(
- ns: $"ABI.{type.Namespace}",
- name: $"{type.Name}Marshaller");
- }
-
- ///
- /// Get the marshaller type for a specified reference type.
- ///
- /// The reference type to get the marshaller type for.
- /// The instance to use.
- /// The emit state for this invocation.
- /// The marshaller type for .
- private static ITypeDefOrRef GetReferenceTypeMarshallerType(
- TypeSignature type,
- InteropReferences interopReferences,
- InteropGeneratorEmitState emitState)
- {
- // Just like for value types, generic types have marshaller types in 'WinRT.Interop.dll'
- if (type is GenericInstanceTypeSignature)
- {
- return emitState.LookupTypeDefinition(type, "Marshaller");
- }
-
- // Special case 'object', we'll directly use 'WindowsRuntimeObjectMarshaller' for it
- if (type.IsTypeOfObject())
- {
- return interopReferences.WindowsRuntimeObjectMarshaller;
- }
-
- // For custom-mapped types, get the marshaller type from 'WinRT.Runtime.dll'
- if (type.IsTypeOfType(interopReferences) ||
- type.IsTypeOfException(interopReferences) ||
- type.IsCustomMappedWindowsRuntimeNonGenericInterfaceType(interopReferences) ||
- type.IsCustomMappedWindowsRuntimeNonGenericDelegateType(interopReferences))
- {
- return interopReferences.WindowsRuntimeModule.CreateTypeReference(
- ns: $"ABI.{type.Namespace}",
- name: $"{type.Name}Marshaller");
- }
-
- // In all other cases, the marshaller type will be in the declared assembly. Note that this
- // also includes special manually projected types, such as 'AsyncActionCompletedHandler'.
- // Even though those types are in 'WinRT.Runtime.dll', the marshaller type will also be
- // there, so trying to resolve it via the declaring module like for other types is fine.
- return type.Resolve()!.DeclaringModule!.CreateTypeReference(
- ns: $"ABI.{type.Namespace}",
- name: $"{type.Name}Marshaller");
- }
-}
\ No newline at end of file
diff --git a/src/WinRT.Interop.Generator/Resolvers/InteropMarshallerType.cs b/src/WinRT.Interop.Generator/Resolvers/InteropMarshallerType.cs
new file mode 100644
index 000000000..ed2874ee0
--- /dev/null
+++ b/src/WinRT.Interop.Generator/Resolvers/InteropMarshallerType.cs
@@ -0,0 +1,128 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using AsmResolver.DotNet;
+using AsmResolver.DotNet.Signatures;
+using WindowsRuntime.InteropGenerator.References;
+
+namespace WindowsRuntime.InteropGenerator.Resolvers;
+
+///
+/// A resolver for marshaller types for Windows Runtime types.
+///
+internal readonly ref struct InteropMarshallerType
+{
+ ///
+ /// The managed type being marshalled.
+ ///
+ private readonly TypeSignature _type;
+
+ ///
+ /// The instance to use.
+ ///
+ private readonly InteropReferences _interopReferences;
+
+ ///
+ /// The for the marshaller type for .
+ ///
+ private readonly ITypeDefOrRef _marshallerType;
+
+ ///
+ /// Creates a new instance with the specified parameters.
+ ///
+ ///
+ ///
+ ///
+ public InteropMarshallerType(
+ TypeSignature type,
+ InteropReferences interopReferences,
+ ITypeDefOrRef marshallerType)
+ {
+ _type = type;
+ _interopReferences = interopReferences;
+ _marshallerType = marshallerType;
+ }
+
+ ///
+ /// Gets the for the ConvertToManaged method for a specified type.
+ ///
+ /// The resulting value.
+ public IMethodDefOrRef ConvertToManaged()
+ {
+ return _marshallerType.GetMethodDefOrRef(
+ name: "ConvertToManaged"u8,
+ signature: MethodSignature.CreateStatic(
+ returnType: _type,
+ parameterTypes: [_type.GetAbiType(_interopReferences)]));
+ }
+
+ ///
+ /// Gets the for the ConvertToUnmanaged method for a specified type.
+ ///
+ /// The resulting value.
+ public IMethodDefOrRef ConvertToUnmanaged()
+ {
+ TypeSignature abiType = _type.GetAbiType(_interopReferences);
+
+ // If the ABI type is 'void*' and the type is not 'string', then we use 'WindowsRuntimeObjectReferenceValue'.
+ // This is because that's always used to return Windows Runtime native objects. Just 'HSTRING' is special.
+ TypeSignature returnType = abiType.IsTypeOfVoidPointer() && !_type.IsTypeOfString()
+ ? _interopReferences.WindowsRuntimeObjectReferenceValue.ToValueTypeSignature()
+ : abiType;
+
+ return _marshallerType.GetMethodDefOrRef(
+ name: "ConvertToUnmanaged"u8,
+ signature: MethodSignature.CreateStatic(
+ returnType: returnType,
+ parameterTypes: [_type]));
+ }
+
+ ///
+ /// Gets the for the BoxToUnmanaged method for a specified type.
+ ///
+ /// The resulting value.
+ public IMethodDefOrRef BoxToUnmanaged()
+ {
+ // When boxing, the parameter is either 'Nullable' for value types, or just the same type
+ TypeSignature parameterType = _type.IsValueType
+ ? _interopReferences.Nullable1.MakeGenericValueType(_type)
+ : _type;
+
+ return _marshallerType.GetMethodDefOrRef(
+ name: "BoxToUnmanaged"u8,
+ signature: MethodSignature.CreateStatic(
+ returnType: _interopReferences.WindowsRuntimeObjectReferenceValue.ToValueTypeSignature(),
+ parameterTypes: [parameterType]));
+ }
+
+ ///
+ /// Gets the for the UnboxToManaged method for a specified type.
+ ///
+ /// The resulting value.
+ public IMethodDefOrRef UnboxToManaged()
+ {
+ // When unboxing, the return type is either 'Nullable' for value types, or just the same type
+ TypeSignature returnType = _type.IsValueType
+ ? _interopReferences.Nullable1.MakeGenericValueType(_type)
+ : _type;
+
+ return _marshallerType.GetMethodDefOrRef(
+ name: "UnboxToManaged"u8,
+ signature: MethodSignature.CreateStatic(
+ returnType: returnType,
+ parameterTypes: [_interopReferences.CorLibTypeFactory.Void.MakePointerType()]));
+ }
+
+ ///
+ /// Gets the for the Dispose method for a specified type.
+ ///
+ /// The resulting value.
+ public IMethodDefOrRef Dispose()
+ {
+ return _marshallerType.GetMethodDefOrRef(
+ name: "Dispose"u8,
+ signature: MethodSignature.CreateStatic(
+ returnType: _interopReferences.CorLibTypeFactory.Void,
+ parameterTypes: [_type.GetAbiType(_interopReferences)]));
+ }
+}
diff --git a/src/WinRT.Interop.Generator/Resolvers/InteropMarshallerTypeResolver.cs b/src/WinRT.Interop.Generator/Resolvers/InteropMarshallerTypeResolver.cs
new file mode 100644
index 000000000..09f607161
--- /dev/null
+++ b/src/WinRT.Interop.Generator/Resolvers/InteropMarshallerTypeResolver.cs
@@ -0,0 +1,79 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using AsmResolver.DotNet;
+using AsmResolver.DotNet.Signatures;
+using WindowsRuntime.InteropGenerator.Generation;
+using WindowsRuntime.InteropGenerator.References;
+
+namespace WindowsRuntime.InteropGenerator.Resolvers;
+
+///
+/// A resolver for marshaller types for Windows Runtime types.
+///
+internal static class InteropMarshallerTypeResolver
+{
+ ///
+ /// Gets the marshaller type for a specified type.
+ ///
+ /// The type to get the marshaller type for.
+ /// The instance to use.
+ /// The emit state for this invocation.
+ /// The marshaller type for .
+ public static InteropMarshallerType GetMarshallerType(
+ TypeSignature type,
+ InteropReferences interopReferences,
+ InteropGeneratorEmitState emitState)
+ {
+ // First handle constructed generic types (which can be either value types or reference types)
+ if (type is GenericInstanceTypeSignature)
+ {
+ // For 'Nullable' return types, we need the marshaller for the instantiated 'T' type,
+ // as that will contain the unboxing methods. The 'T' in this case can be a custom-mapped
+ // primitive type or a projected value type. Technically speaking it can never be a
+ // 'KeyValuePair<,>' or 'Nullable', because both of those are interface types in the
+ // Windows Runtime type system, meaning they can't be boxed like value types.
+ if (type.TryGetNullableUnderlyingType(interopReferences, out TypeSignature? underlyingType))
+ {
+ return GetMarshallerType(underlyingType, interopReferences, emitState);
+ }
+
+ // For all other generic instantiations (including 'KeyValuePair<,>'), we can just look the marshaller
+ // types up. All those marshaller types will always be generated in the same 'WinRT.Interop.dll'.
+ ITypeDefOrRef marshallerType = emitState.LookupTypeDefinition(type, "Marshaller");
+
+ return new(type, interopReferences, marshallerType);
+ }
+
+ // Special case 'object', we'll directly use 'WindowsRuntimeObjectMarshaller' for it
+ if (type.IsTypeOfObject())
+ {
+ return new(type, interopReferences, interopReferences.WindowsRuntimeObjectMarshaller);
+ }
+
+ // For custom-mapped types, get the marshaller type from 'WinRT.Runtime.dll'
+ if (type.IsFundamentalWindowsRuntimeType(interopReferences) ||
+ type.IsCustomMappedWindowsRuntimeNonGenericInterfaceType(interopReferences) ||
+ type.IsCustomMappedWindowsRuntimeNonGenericDelegateType(interopReferences) ||
+ type.IsCustomMappedWindowsRuntimeNonGenericStructOrClassType(interopReferences))
+ {
+ ITypeDefOrRef marshallerType = interopReferences.WindowsRuntimeModule.CreateTypeReference(
+ ns: $"ABI.{type.Namespace}",
+ name: $"{type.Name}Marshaller");
+
+ return new(type, interopReferences, marshallerType);
+ }
+ else
+ {
+ // In all other cases, the marshaller type will be in the declared assembly. Note that this
+ // also includes special manually projected types, such as 'AsyncActionCompletedHandler'.
+ // Even though those types are in 'WinRT.Runtime.dll', the marshaller type will also be
+ // there, so trying to resolve it via the declaring module like for other types is fine.
+ ITypeDefOrRef marshallerType = type.Resolve()!.DeclaringModule!.CreateTypeReference(
+ ns: $"ABI.{type.Namespace}",
+ name: $"{type.Name}Marshaller");
+
+ return new(type, interopReferences, marshallerType);
+ }
+ }
+}
diff --git a/src/WinRT.Runtime2/ABI/System/Exception.cs b/src/WinRT.Runtime2/ABI/System/Exception.cs
index e15674e1a..37c20f295 100644
--- a/src/WinRT.Runtime2/ABI/System/Exception.cs
+++ b/src/WinRT.Runtime2/ABI/System/Exception.cs
@@ -51,7 +51,7 @@ public struct Exception
DiagnosticId = WindowsRuntimeConstants.PrivateImplementationDetailObsoleteDiagnosticId,
UrlFormat = WindowsRuntimeConstants.CsWinRTDiagnosticsUrlFormat)]
[EditorBrowsable(EditorBrowsableState.Never)]
-public static class ExceptionMarshaller
+public static unsafe class ExceptionMarshaller
{
///
/// Converts a managed to an unmanaged .
@@ -72,6 +72,20 @@ public static Exception ConvertToUnmanaged(global::System.Exception? value)
{
return RestrictedErrorInfo.GetExceptionForHR(value.Value);
}
+
+ ///
+ public static WindowsRuntimeObjectReferenceValue BoxToUnmanaged(global::System.Exception? value)
+ {
+ return value is null ? default : new((void*)WindowsRuntimeComWrappers.Default.GetOrCreateComInterfaceForObject(value, CreateComInterfaceFlags.None, in WellKnownWindowsInterfaceIIDs.IID_IReferenceOfException));
+ }
+
+ ///
+ public static global::System.Exception? UnboxToManaged(void* value)
+ {
+ Exception? abi = WindowsRuntimeValueTypeMarshaller.UnboxToManaged(value);
+
+ return abi.HasValue ? ConvertToManaged(abi.GetValueOrDefault()) : null;
+ }
}
///