|
3 | 3 |
|
4 | 4 | using System.Collections.Immutable;
|
5 | 5 | using Microsoft.CodeAnalysis;
|
| 6 | +using Microsoft.CodeAnalysis.CSharp; |
6 | 7 | using Microsoft.CodeAnalysis.CSharp.Syntax;
|
7 | 8 | using Microsoft.Macios.Generator.DataModel;
|
8 | 9 | using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
|
@@ -185,19 +186,221 @@ internal static ArgumentSyntax GetTrampolineInvokeArgument (string trampolineNam
|
185 | 186 | return Argument (expression);
|
186 | 187 | }
|
187 | 188 |
|
| 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 | + |
188 | 386 | /// <summary>
|
189 | 387 | /// Return a immutable array of arguments to be used for the trampoline invoke method. The arguments are all
|
190 | 388 | /// the different expressions needed to pass the parameters to the trampoline.
|
191 | 389 | /// </summary>
|
192 | 390 | /// <param name="trampolineName">The trampoline whose parameters we are generating.</param>
|
193 | 391 | /// <param name="delegateInfo">The delegate info of the trampoline we are generating.</param>
|
194 | 392 | /// <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) |
196 | 395 | {
|
197 | 396 | // 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); |
199 | 398 | 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); |
201 | 404 | }
|
202 | 405 | return bucket.ToImmutable ();
|
203 | 406 | }
|
|
0 commit comments