-
Notifications
You must be signed in to change notification settings - Fork 127
Expand file tree
/
Copy pathInteropMethodRewriter.ManagedValue.cs
More file actions
103 lines (92 loc) · 5.42 KB
/
Copy pathInteropMethodRewriter.ManagedValue.cs
File metadata and controls
103 lines (92 loc) · 5.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// 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 WindowsRuntime.Generator;
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 CS8620 // TODO: remove once Roslyn bug is fixed
namespace WindowsRuntime.InteropGenerator.Rewriters;
/// <inheritdoc cref="InteropMethodRewriter"/>
internal partial class InteropMethodRewriter
{
/// <summary>
/// Contains the logic for marshalling managed values (i.e. parameters that are passed to managed methods, already on the stack).
/// </summary>
public static class ManagedValue
{
/// <summary>
/// Performs two-pass code generation on a target method to marshal an unmanaged parameter.
/// </summary>
/// <param name="parameterType">The parameter type that needs to be marshalled.</param>
/// <param name="method">The target method to perform two-pass code generation on.</param>
/// <param name="marker">The target IL instruction to replace with the right set of specialized instructions.</param>
/// <param name="interopReferences">The <see cref="InteropReferences"/> instance to use.</param>
/// <param name="emitState">The emit state for this invocation.</param>
public static void RewriteMethod(
TypeSignature parameterType,
MethodDefinition method,
CilInstruction marker,
InteropReferences interopReferences,
InteropGeneratorEmitState emitState)
{
// Validate that we do have some IL body for the input method (this should always be the case)
if (method.CilMethodBody is not CilMethodBody body)
{
throw WellKnownInteropExceptions.MethodRewriteMissingBodyError(method);
}
// If we didn't find the marker, it means the target method is either invalid
if (!body.Instructions.ReferenceContains(marker))
{
throw WellKnownInteropExceptions.MethodRewriteMarkerInstructionNotFoundError(marker, method);
}
if (parameterType.IsValueType)
{
// If the parameter type is blittable, we have nothing else to do (the value is already loaded)
if (parameterType.IsBlittable(interopReferences))
{
_ = body.Instructions.ReferenceRemove(marker);
return;
}
// Handle the other possible value types
if (parameterType.IsConstructedNullableValueType(interopReferences))
{
InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(parameterType, interopReferences, emitState);
// For 'Nullable<T>' parameters (i.e. we have an 'IReference<T>' interface pointer), we unbox the underlying type
body.Instructions.ReferenceReplaceRange(marker, new CilInstruction(Call, marshallerType.UnboxToManaged()));
}
else if (SignatureComparer.IgnoreVersion.Equals(parameterType, interopReferences.ReadOnlySpanChar))
{
// When marshalling 'ReadOnlySpan<char>' values, we also use 'HStringMarshaller', but without materializing the 'string' object
body.Instructions.ReferenceReplaceRange(marker, new CilInstruction(Call, interopReferences.HStringMarshallerConvertToManagedUnsafe));
}
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.
// 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, new CilInstruction(Call, marshallerType.ConvertToManaged()));
}
}
else if (parameterType.IsTypeOfString())
{
// When marshalling 'string' values, we must use 'HStringMarshaller' (the ABI type is not actually a COM object)
body.Instructions.ReferenceReplaceRange(marker, new CilInstruction(Call, interopReferences.HStringMarshallerConvertToManaged));
}
else
{
// Get the marshaller type for all other reference types (including generics)
InteropMarshallerType marshallerType = InteropMarshallerTypeResolver.GetMarshallerType(parameterType, interopReferences, emitState);
// Marshal the value normally (the caller will own the native resource)
body.Instructions.ReferenceReplaceRange(marker, new CilInstruction(Call, marshallerType.ConvertToManaged()));
}
}
}
}