Skip to content

Commit d28e3be

Browse files
mandel-macaqueGitHub Actions Autoformatter
andauthored
[RGen] Allow to add pre/post conversions for delegate parameters. (#22789)
There are a number of parameters that required a number of conversions pre and post executing a delegate from a trampoline. This commit adds support for that scenario in those parameters that are by ref. --------- Co-authored-by: GitHub Actions Autoformatter <[email protected]>
1 parent 291633e commit d28e3be

File tree

3 files changed

+1079
-3
lines changed

3 files changed

+1079
-3
lines changed

src/rgen/Microsoft.Macios.Generator/Emitters/BindingSyntaxFactory.Trampoline.cs

Lines changed: 206 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Collections.Immutable;
55
using Microsoft.CodeAnalysis;
6+
using Microsoft.CodeAnalysis.CSharp;
67
using Microsoft.CodeAnalysis.CSharp.Syntax;
78
using Microsoft.Macios.Generator.DataModel;
89
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
@@ -185,19 +186,221 @@ internal static ArgumentSyntax GetTrampolineInvokeArgument (string trampolineNam
185186
return Argument (expression);
186187
}
187188

189+
internal static ImmutableArray<SyntaxNode> GetTrampolinePreInvokeByRefArgument (in DelegateParameter parameter)
190+
{
191+
// there are two cases in which we need to do something with the byref parameters:
192+
// 1. the parameter is by ref and nullable, we need to create a temporary variable to hold the value
193+
// 2. the parameter is a boolean type and we need a conversion
194+
// any other case we can just use the parameter as is and we will return an empty array
195+
var tempVariableName = Nomenclator.GetNameForTempTrampolineVariable (parameter);
196+
if (tempVariableName is null)
197+
return [];
198+
199+
if (parameter.Type.IsNullable) {
200+
// declare a new variable to hold the temp var
201+
// ParameterType? tempVariable = null;
202+
var declarationNode = LocalDeclarationStatement (
203+
VariableDeclaration (
204+
NullableType (
205+
IdentifierName (parameter.Type.FullyQualifiedName)))
206+
.WithVariables (
207+
SingletonSeparatedList (
208+
VariableDeclarator (
209+
Identifier (tempVariableName))
210+
.WithInitializer (
211+
EqualsValueClause (
212+
LiteralExpression (
213+
SyntaxKind.NullLiteralExpression))))));
214+
// check for the parameter being null and assign the value if needed.
215+
// if (parameterName is not null)
216+
// tempVariable = *parameterName;
217+
var ifNode = IfStatement (
218+
IsPatternExpression (
219+
IdentifierName (parameter.Name),
220+
UnaryPattern (
221+
ConstantPattern (
222+
LiteralExpression (
223+
SyntaxKind.NullLiteralExpression)))),
224+
ExpressionStatement (
225+
AssignmentExpression (
226+
SyntaxKind.SimpleAssignmentExpression,
227+
IdentifierName (tempVariableName),
228+
PrefixUnaryExpression (
229+
SyntaxKind.PointerIndirectionExpression,
230+
IdentifierName (parameter.Name)))));
231+
return [
232+
declarationNode,
233+
ifNode,
234+
];
235+
}
236+
237+
if (parameter.Type.SpecialType == SpecialType.System_Boolean) {
238+
// generates the following:
239+
// bool {tempVariable} = *{parameterName} != 0;
240+
var variableDeclaration = LocalDeclarationStatement (
241+
VariableDeclaration (
242+
PredefinedType (
243+
Token (SyntaxKind.BoolKeyword)))
244+
.WithVariables (
245+
SingletonSeparatedList (
246+
VariableDeclarator (
247+
Identifier (tempVariableName))
248+
.WithInitializer (
249+
EqualsValueClause (
250+
BinaryExpression (
251+
SyntaxKind.NotEqualsExpression,
252+
PrefixUnaryExpression (
253+
SyntaxKind.PointerIndirectionExpression,
254+
IdentifierName (parameter.Name)),
255+
LiteralExpression (
256+
SyntaxKind.NumericLiteralExpression,
257+
Literal (0))))))));
258+
return [variableDeclaration];
259+
}
260+
261+
// default case, we do not need to do anything
262+
return [];
263+
}
264+
265+
internal static ImmutableArray<SyntaxNode> GetTrampolinePostInvokeByRefArgument (string trampolineName,
266+
in DelegateParameter parameter)
267+
{
268+
// similar to the pre invoke case, we need to do something with the byref parameters:
269+
// 1. the parameter is by ref and nullable we need to assign the value
270+
// 2. the parameter is a boolean type we need to convert back the value from a byte
271+
var tempVariableName = Nomenclator.GetNameForTempTrampolineVariable (parameter);
272+
if (tempVariableName is null)
273+
return [];
274+
275+
if (parameter.Type.IsNullable) {
276+
// check if the temp variable has a value and assign it to the parameter
277+
// if (ParameterName is not null && TempVariable.HasValue)
278+
// *ParameterName = TempVariable.Value;
279+
var ifNode = IfStatement (
280+
BinaryExpression (
281+
SyntaxKind.LogicalAndExpression,
282+
IsPatternExpression (
283+
IdentifierName (parameter.Name),
284+
UnaryPattern (
285+
ConstantPattern (
286+
LiteralExpression (
287+
SyntaxKind.NullLiteralExpression)))),
288+
MemberAccessExpression (
289+
SyntaxKind.SimpleMemberAccessExpression,
290+
IdentifierName (tempVariableName),
291+
IdentifierName ("HasValue"))),
292+
ExpressionStatement (
293+
AssignmentExpression (
294+
SyntaxKind.SimpleAssignmentExpression,
295+
PrefixUnaryExpression (
296+
SyntaxKind.PointerIndirectionExpression,
297+
IdentifierName (parameter.Name)),
298+
MemberAccessExpression (
299+
SyntaxKind.SimpleMemberAccessExpression,
300+
IdentifierName (tempVariableName),
301+
IdentifierName ("Value")))));
302+
return [ifNode];
303+
}
304+
305+
if (parameter.Type.SpecialType == SpecialType.System_Boolean) {
306+
// set the boolean value from a byte
307+
// *ParameterName = TempVariable ? (byte) 1 : (byte) 0;
308+
var assignment = AssignmentExpression (
309+
SyntaxKind.SimpleAssignmentExpression,
310+
PrefixUnaryExpression (
311+
SyntaxKind.PointerIndirectionExpression,
312+
IdentifierName (parameter.Name)),
313+
ConditionalExpression (
314+
IdentifierName (tempVariableName),
315+
CastExpression (
316+
PredefinedType (
317+
Token (SyntaxKind.ByteKeyword)),
318+
LiteralExpression (
319+
SyntaxKind.NumericLiteralExpression,
320+
Literal (1))).WithTrailingTrivia (Space),
321+
CastExpression (
322+
PredefinedType (
323+
Token (SyntaxKind.ByteKeyword)),
324+
LiteralExpression (
325+
SyntaxKind.NumericLiteralExpression,
326+
Literal (0).WithLeadingTrivia (Space)))));
327+
return [ExpressionStatement (assignment)];
328+
}
329+
330+
if (parameter.Type.IsReferenceType) {
331+
// assign the value of the temp variable to the parameter
332+
var assignment = IfStatement (
333+
IsPatternExpression (
334+
IdentifierName (parameter.Name),
335+
UnaryPattern (
336+
ConstantPattern (
337+
LiteralExpression (
338+
SyntaxKind.NullLiteralExpression)))),
339+
ExpressionStatement (
340+
AssignmentExpression (
341+
SyntaxKind.SimpleAssignmentExpression,
342+
PrefixUnaryExpression (
343+
SyntaxKind.PointerIndirectionExpression,
344+
IdentifierName (parameter.Name)),
345+
InvocationExpression (
346+
MemberAccessExpression (
347+
SyntaxKind.SimpleMemberAccessExpression,
348+
IdentifierName ("Runtime"),
349+
IdentifierName ("RetainAndAutoreleaseNativeObject").WithTrailingTrivia (Space)))
350+
.WithArgumentList (
351+
ArgumentList (
352+
SingletonSeparatedList (
353+
Argument (
354+
IdentifierName (tempVariableName))))))));
355+
return [assignment];
356+
}
357+
return [];
358+
}
359+
360+
/// <summary>
361+
/// Returns the list of expressions that need to be executed before the trampoline is invoked. This allows to
362+
/// help the trampoline to convert the parameters to the expected types.
363+
/// </summary>
364+
/// <param name="trampolineName">The trampoline name to which the conversion is needed.</param>
365+
/// <param name="parameter">The parameters whose conversions we need.</param>
366+
/// <returns>An immutable array with the needed conversion expressions. Empty is return if no conversion
367+
/// is needed.</returns>
368+
internal static ImmutableArray<SyntaxNode> GetTrampolinePreInvokeArgumentConversions (string trampolineName,
369+
in DelegateParameter parameter)
370+
{
371+
// decide the type of conversion we need to do based on the type of the parameter
372+
return parameter switch { { IsByRef: true } => GetTrampolinePreInvokeByRefArgument (parameter),
373+
_ => []
374+
};
375+
}
376+
377+
internal static ImmutableArray<SyntaxNode> GetTrampolinePostInvokeArgumentConversions (string trampolineName,
378+
in DelegateParameter parameter)
379+
{
380+
// decide the type of conversion we need to do based on the type of the parameter
381+
return parameter switch { { IsByRef: true } => GetTrampolinePostInvokeByRefArgument (trampolineName, parameter),
382+
_ => []
383+
};
384+
}
385+
188386
/// <summary>
189387
/// Return a immutable array of arguments to be used for the trampoline invoke method. The arguments are all
190388
/// the different expressions needed to pass the parameters to the trampoline.
191389
/// </summary>
192390
/// <param name="trampolineName">The trampoline whose parameters we are generating.</param>
193391
/// <param name="delegateInfo">The delegate info of the trampoline we are generating.</param>
194392
/// <returns>An immutable array with the argument expressions needed to invoke the trampoline delegate.</returns>
195-
internal static ImmutableArray<ArgumentSyntax> GetTrampolineInvokeArguments (string trampolineName, in DelegateInfo delegateInfo)
393+
internal static ImmutableArray<TrampolineArgumentSyntax> GetTrampolineInvokeArguments (string trampolineName,
394+
in DelegateInfo delegateInfo)
196395
{
197396
// create the builder for the arguments, we already know the size of the array
198-
var bucket = ImmutableArray.CreateBuilder<ArgumentSyntax> (delegateInfo.Parameters.Length);
397+
var bucket = ImmutableArray.CreateBuilder<TrampolineArgumentSyntax> (delegateInfo.Parameters.Length);
199398
foreach (var parameter in delegateInfo.Parameters) {
200-
bucket.Add (GetTrampolineInvokeArgument (trampolineName, parameter));
399+
var argument = new TrampolineArgumentSyntax (GetTrampolineInvokeArgument (trampolineName, parameter)) {
400+
PreDelegateCallConversion = GetTrampolinePreInvokeArgumentConversions (trampolineName, parameter),
401+
PostDelegateCallConversion = GetTrampolinePostInvokeArgumentConversions (trampolineName, parameter),
402+
};
403+
bucket.Add (argument);
201404
}
202405
return bucket.ToImmutable ();
203406
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
using System.Collections.Immutable;
5+
using Microsoft.CodeAnalysis;
6+
using Microsoft.CodeAnalysis.CSharp.Syntax;
7+
8+
namespace Microsoft.Macios.Generator.Emitters;
9+
10+
/// <summary>
11+
/// Represents the conversions needed for a trampoline argument.
12+
/// </summary>
13+
readonly record struct TrampolineArgumentSyntax (ArgumentSyntax ArgumentSyntax) {
14+
/// <summary>
15+
/// The syntax to be used for the argument in the delegate call.
16+
/// </summary>
17+
public ArgumentSyntax ArgumentSyntax { get; init; } = ArgumentSyntax;
18+
19+
/// <summary>
20+
/// Collection of expressions that need to be called before the delegate call.
21+
/// </summary>
22+
public ImmutableArray<SyntaxNode> PreDelegateCallConversion { get; init; } = [];
23+
24+
/// <summary>
25+
/// Collection of expressions that need to be called after the delegate call.
26+
/// </summary>
27+
public ImmutableArray<SyntaxNode> PostDelegateCallConversion { get; init; } = [];
28+
}

0 commit comments

Comments
 (0)