Skip to content

Commit ae2f60c

Browse files
authored
NativeAOT: Allow Delegate and MulticastDelegate marshalling (#63219)
- Closes dotnet/runtimelab#1107 - Progress towards GtkSharp/GtkSharp#300
1 parent 84926be commit ae2f60c

File tree

9 files changed

+85
-9
lines changed

9 files changed

+85
-9
lines changed

src/coreclr/tools/Common/TypeSystem/Interop/IL/MarshalHelpers.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ internal static MarshallerKind GetMarshallerKind(
521521
else
522522
return MarshallerKind.Invalid;
523523
}
524-
else if (type.IsDelegate)
524+
else if (type.IsDelegate || InteropTypes.IsSystemDelegate(context, type) || InteropTypes.IsSystemMulticastDelegate(context, type))
525525
{
526526
if (type.HasInstantiation)
527527
{

src/coreclr/tools/Common/TypeSystem/Interop/InteropTypes.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@ public static bool IsSystemDecimal(TypeSystemContext context, TypeDesc type)
9797
return IsCoreNamedType(context, type, "System", "Decimal");
9898
}
9999

100+
public static bool IsSystemDelegate(TypeSystemContext context, TypeDesc type)
101+
{
102+
return IsCoreNamedType(context, type, "System", "Delegate");
103+
}
104+
105+
public static bool IsSystemMulticastDelegate(TypeSystemContext context, TypeDesc type)
106+
{
107+
return IsCoreNamedType(context, type, "System", "MulticastDelegate");
108+
}
109+
100110
public static bool IsSystemGuid(TypeSystemContext context, TypeDesc type)
101111
{
102112
return IsCoreNamedType(context, type, "System", "Guid");

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/ReflectionMethodBodyScanner.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2199,7 +2199,9 @@ public override bool HandleCall(MethodIL callingMethodBody, MethodDesc calledMet
21992199
if (comDangerousMethod)
22002200
{
22012201
reflectionContext.AnalyzingPattern();
2202-
reflectionContext.RecordUnrecognizedPattern(2050, $"P/invoke method '{calledMethod.GetDisplayName()}' declares a parameter with COM marshalling. Correctness of COM interop cannot be guaranteed after trimming. Interfaces and interface members might be removed.");
2202+
reflectionContext.RecordUnrecognizedPattern(
2203+
(int)DiagnosticId.CorrectnessOfCOMCannotBeGuaranteed,
2204+
new DiagnosticString(DiagnosticId.CorrectnessOfCOMCannotBeGuaranteed).GetMessage(DiagnosticUtilities.GetMethodSignatureDisplayName(calledMethod)));
22032205
}
22042206
}
22052207

src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedInteropStubManager.cs

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
using Internal.TypeSystem;
66
using Internal.TypeSystem.Interop;
77

8+
using ILCompiler.Dataflow;
89
using ILCompiler.DependencyAnalysis;
10+
using ILLink.Shared;
911

1012
using Debug = System.Diagnostics.Debug;
1113
using DependencyList = ILCompiler.DependencyAnalysisFramework.DependencyNodeCore<ILCompiler.DependencyAnalysis.NodeFactory>.DependencyList;
@@ -18,9 +20,12 @@ namespace ILCompiler
1820
/// </summary>
1921
public class UsageBasedInteropStubManager : CompilerGeneratedInteropStubManager
2022
{
21-
public UsageBasedInteropStubManager(InteropStateManager interopStateManager, PInvokeILEmitterConfiguration pInvokeILEmitterConfiguration)
23+
private Logger _logger;
24+
25+
public UsageBasedInteropStubManager(InteropStateManager interopStateManager, PInvokeILEmitterConfiguration pInvokeILEmitterConfiguration, Logger logger)
2226
: base(interopStateManager, pInvokeILEmitterConfiguration)
2327
{
28+
_logger = logger;
2429
}
2530

2631
public override void AddDependeciesDueToPInvoke(ref DependencyList dependencies, NodeFactory factory, MethodDesc method)
@@ -30,11 +35,11 @@ public override void AddDependeciesDueToPInvoke(ref DependencyList dependencies,
3035
dependencies = dependencies ?? new DependencyList();
3136

3237
MethodSignature methodSig = method.Signature;
33-
AddParameterMarshallingDependencies(ref dependencies, factory, methodSig.ReturnType);
38+
AddParameterMarshallingDependencies(ref dependencies, factory, method, methodSig.ReturnType);
3439

3540
for (int i = 0; i < methodSig.Length; i++)
3641
{
37-
AddParameterMarshallingDependencies(ref dependencies, factory, methodSig[i]);
42+
AddParameterMarshallingDependencies(ref dependencies, factory, method, methodSig[i]);
3843
}
3944
}
4045

@@ -45,13 +50,25 @@ public override void AddDependeciesDueToPInvoke(ref DependencyList dependencies,
4550
}
4651
}
4752

48-
private static void AddParameterMarshallingDependencies(ref DependencyList dependencies, NodeFactory factory, TypeDesc type)
53+
private void AddParameterMarshallingDependencies(ref DependencyList dependencies, NodeFactory factory, MethodDesc method, TypeDesc type)
4954
{
5055
if (type.IsDelegate)
5156
{
5257
dependencies.Add(factory.DelegateMarshallingData((DefType)type), "Delegate marshaling");
5358
}
5459

60+
TypeSystemContext context = type.Context;
61+
if ((type.IsWellKnownType(WellKnownType.MulticastDelegate)
62+
|| type == context.GetWellKnownType(WellKnownType.MulticastDelegate).BaseType))
63+
{
64+
var message = new DiagnosticString(DiagnosticId.CorrectnessOfAbstractDelegatesCannotBeGuaranteed).GetMessage(DiagnosticUtilities.GetMethodSignatureDisplayName(method));
65+
_logger.LogWarning(
66+
message,
67+
(int)DiagnosticId.CorrectnessOfAbstractDelegatesCannotBeGuaranteed,
68+
method,
69+
MessageSubCategory.AotAnalysis);
70+
}
71+
5572
// struct may contain delegate fields, hence we need to add dependencies for it
5673
if (type.IsByRef)
5774
type = ((ParameterizedType)type).ParameterType;
@@ -63,7 +80,7 @@ private static void AddParameterMarshallingDependencies(ref DependencyList depen
6380
if (field.IsStatic)
6481
continue;
6582

66-
AddParameterMarshallingDependencies(ref dependencies, factory, field.FieldType);
83+
AddParameterMarshallingDependencies(ref dependencies, factory, method, field.FieldType);
6784
}
6885
}
6986
}

src/coreclr/tools/aot/ILCompiler/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,7 @@ static string ILLinkify(string rootedAssembly)
705705
_trimmedAssemblies);
706706

707707
InteropStateManager interopStateManager = new InteropStateManager(typeSystemContext.GeneratedAssembly);
708-
InteropStubManager interopStubManager = new UsageBasedInteropStubManager(interopStateManager, pinvokePolicy);
708+
InteropStubManager interopStubManager = new UsageBasedInteropStubManager(interopStateManager, pinvokePolicy, logger);
709709

710710
// Unless explicitly opted in at the command line, we enable scanner for retail builds by default.
711711
// We also don't do this for multifile because scanner doesn't simulate inlining (this would be

src/coreclr/tools/aot/ILLink.Shared/DiagnosticId.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,12 @@ public enum DiagnosticId
5151

5252
// Dynamic code diagnostic ids.
5353
RequiresDynamicCode = 3050,
54-
RequiresDynamicCodeAttributeMismatch = 3051
54+
RequiresDynamicCodeAttributeMismatch = 3051,
5555
// TODO: these are all unique to NativeAOT - mono/linker repo is not aware these error codes usage.
5656
// IL3052 - COM
5757
// IL3053 - AOT analysis warnings
5858
// IL3054 - Generic cycle
59+
CorrectnessOfAbstractDelegatesCannotBeGuaranteed = 3055,
5960
}
6061

6162
public static class DiagnosticIdExtensions

src/coreclr/tools/aot/ILLink.Shared/SharedStrings.resx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,4 +210,7 @@
210210
<data name="CorrectnessOfCOMCannotBeGuaranteedMessage" xml:space="preserve">
211211
<value>P/invoke method '{0}' declares a parameter with COM marshalling. Correctness of COM interop cannot be guaranteed after trimming. Interfaces and interface members might be removed.</value>
212212
</data>
213+
<data name="CorrectnessOfAbstractDelegatesCannotBeGuaranteedMessage" xml:space="preserve">
214+
<value>P/invoke method '{0}' declares a parameter with an abstract delegate. Correctness of interop for abstract delegates cannot be guaranteed after native compilation: the marshalling code for the delegate might not be available. Use a non-abstract delegate type or ensure any delegate instance passed as parameter is marked with `UnmanagedFunctionPointerAttribute`.</value>
215+
</data>
213216
</root>

src/tests/nativeaot/SmokeTests/PInvoke/PInvoke.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,28 @@ private static extern bool VerifySizeParamIndex(
144144
[DllImport("PInvokeNative", CallingConvention = CallingConvention.StdCall)]
145145
static extern bool ReversePInvoke_String(Delegate_String del);
146146

147+
[DllImport("PInvokeNative", EntryPoint="ReversePInvoke_String", CallingConvention = CallingConvention.StdCall)]
148+
static extern bool ReversePInvoke_String_Delegate(Delegate del);
149+
150+
[DllImport("PInvokeNative", EntryPoint="ReversePInvoke_String", CallingConvention = CallingConvention.StdCall)]
151+
static extern bool ReversePInvoke_String_MulticastDelegate(MulticastDelegate del);
152+
153+
struct FieldDelegate
154+
{
155+
public Delegate d;
156+
}
157+
158+
struct FieldMulticastDelegate
159+
{
160+
public MulticastDelegate d;
161+
}
162+
163+
[DllImport("PInvokeNative", EntryPoint="ReversePInvoke_DelegateField", CallingConvention = CallingConvention.StdCall)]
164+
static extern bool ReversePInvoke_Field_Delegate(FieldDelegate del);
165+
166+
[DllImport("PInvokeNative", EntryPoint="ReversePInvoke_DelegateField", CallingConvention = CallingConvention.StdCall)]
167+
static extern bool ReversePInvoke_Field_MulticastDelegate(FieldMulticastDelegate del);
168+
147169
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
148170
delegate bool Delegate_OutString([MarshalAs(0x30)] out string s);
149171
[DllImport("PInvokeNative", CallingConvention = CallingConvention.StdCall)]
@@ -616,6 +638,16 @@ private static void TestDelegate()
616638
Delegate_String ds = new Delegate_String((new ClosedDelegateCLass()).GetString);
617639
ThrowIfNotEquals(true, ReversePInvoke_String(ds), "Delegate marshalling failed.");
618640

641+
ThrowIfNotEquals(true, ReversePInvoke_String_Delegate(ds), "Delegate marshalling failed.");
642+
ThrowIfNotEquals(true, ReversePInvoke_String_MulticastDelegate(ds), "Delegate marshalling failed.");
643+
644+
FieldDelegate fd;
645+
fd.d = ds;
646+
ThrowIfNotEquals(true, ReversePInvoke_Field_Delegate(fd), "Delegate marshalling failed.");
647+
FieldMulticastDelegate fmd;
648+
fmd.d = ds;
649+
ThrowIfNotEquals(true, ReversePInvoke_Field_MulticastDelegate(fmd), "Delegate marshalling failed.");
650+
619651
Delegate_OutString dos = new Delegate_OutString((out string x) =>
620652
{
621653
x = "Hello there!";

src/tests/nativeaot/SmokeTests/PInvoke/PInvokeNative.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,17 @@ DLL_EXPORT bool __stdcall ReversePInvoke_String(StringFuncPtr fnPtr)
341341
return fnPtr(str);
342342
}
343343

344+
struct DelegateFieldStruct
345+
{
346+
StringFuncPtr fnPtr;
347+
};
348+
349+
DLL_EXPORT bool __stdcall ReversePInvoke_DelegateField(DelegateFieldStruct p)
350+
{
351+
char str[] = "Hello World";
352+
return p.fnPtr(str);
353+
}
354+
344355
typedef bool(__stdcall *OutStringFuncPtr) (char **);
345356
DLL_EXPORT bool __stdcall ReversePInvoke_OutString(OutStringFuncPtr fnPtr)
346357
{

0 commit comments

Comments
 (0)