HipcGenerator.cs
1 using Microsoft.CodeAnalysis; 2 using Microsoft.CodeAnalysis.CSharp; 3 using Microsoft.CodeAnalysis.CSharp.Syntax; 4 using System.Collections.Generic; 5 using System.Linq; 6 7 namespace Ryujinx.Horizon.Generators.Hipc 8 { 9 [Generator] 10 class HipcGenerator : ISourceGenerator 11 { 12 private const string ArgVariablePrefix = "arg"; 13 private const string ResultVariableName = "result"; 14 private const string IsBufferMapAliasVariableName = "isBufferMapAlias"; 15 private const string InObjectsVariableName = "inObjects"; 16 private const string OutObjectsVariableName = "outObjects"; 17 private const string ResponseVariableName = "response"; 18 private const string OutRawDataVariableName = "outRawData"; 19 20 private const string TypeSystemBuffersReadOnlySequence = "System.Buffers.ReadOnlySequence"; 21 private const string TypeSystemMemory = "System.Memory"; 22 private const string TypeSystemReadOnlySpan = "System.ReadOnlySpan"; 23 private const string TypeSystemSpan = "System.Span"; 24 private const string TypeStructLayoutAttribute = "System.Runtime.InteropServices.StructLayoutAttribute"; 25 26 public const string CommandAttributeName = "CmifCommandAttribute"; 27 28 private const string TypeResult = "Ryujinx.Horizon.Common.Result"; 29 private const string TypeBufferAttribute = "Ryujinx.Horizon.Sdk.Sf.BufferAttribute"; 30 private const string TypeCopyHandleAttribute = "Ryujinx.Horizon.Sdk.Sf.CopyHandleAttribute"; 31 private const string TypeMoveHandleAttribute = "Ryujinx.Horizon.Sdk.Sf.MoveHandleAttribute"; 32 private const string TypeClientProcessIdAttribute = "Ryujinx.Horizon.Sdk.Sf.ClientProcessIdAttribute"; 33 private const string TypeCommandAttribute = "Ryujinx.Horizon.Sdk.Sf." + CommandAttributeName; 34 private const string TypeIServiceObject = "Ryujinx.Horizon.Sdk.Sf.IServiceObject"; 35 36 private enum Modifier 37 { 38 None, 39 Ref, 40 Out, 41 In, 42 } 43 44 private readonly struct OutParameter 45 { 46 public readonly string Name; 47 public readonly string TypeName; 48 public readonly int Index; 49 public readonly CommandArgType Type; 50 51 public OutParameter(string name, string typeName, int index, CommandArgType type) 52 { 53 Name = name; 54 TypeName = typeName; 55 Index = index; 56 Type = type; 57 } 58 } 59 60 public void Execute(GeneratorExecutionContext context) 61 { 62 HipcSyntaxReceiver syntaxReceiver = (HipcSyntaxReceiver)context.SyntaxReceiver; 63 64 foreach (var commandInterface in syntaxReceiver.CommandInterfaces) 65 { 66 if (!NeedsIServiceObjectImplementation(context.Compilation, commandInterface.ClassDeclarationSyntax)) 67 { 68 continue; 69 } 70 71 CodeGenerator generator = new CodeGenerator(); 72 string className = commandInterface.ClassDeclarationSyntax.Identifier.ToString(); 73 74 generator.AppendLine("using Ryujinx.Horizon.Common;"); 75 generator.AppendLine("using Ryujinx.Horizon.Sdk.Sf;"); 76 generator.AppendLine("using Ryujinx.Horizon.Sdk.Sf.Cmif;"); 77 generator.AppendLine("using Ryujinx.Horizon.Sdk.Sf.Hipc;"); 78 generator.AppendLine("using System;"); 79 generator.AppendLine("using System.Collections.Frozen;"); 80 generator.AppendLine("using System.Collections.Generic;"); 81 generator.AppendLine("using System.Runtime.CompilerServices;"); 82 generator.AppendLine("using System.Runtime.InteropServices;"); 83 generator.AppendLine(); 84 generator.EnterScope($"namespace {GetNamespaceName(commandInterface.ClassDeclarationSyntax)}"); 85 generator.EnterScope($"partial class {className}"); 86 87 GenerateMethodTable(generator, context.Compilation, commandInterface); 88 89 foreach (var method in commandInterface.CommandImplementations) 90 { 91 generator.AppendLine(); 92 93 GenerateMethod(generator, context.Compilation, method); 94 } 95 96 generator.LeaveScope(); 97 generator.LeaveScope(); 98 99 context.AddSource($"{GetNamespaceName(commandInterface.ClassDeclarationSyntax)}.{className}.g.cs", generator.ToString()); 100 } 101 } 102 103 private static string GetNamespaceName(SyntaxNode syntaxNode) 104 { 105 while (syntaxNode != null && !(syntaxNode is NamespaceDeclarationSyntax)) 106 { 107 syntaxNode = syntaxNode.Parent; 108 } 109 110 if (syntaxNode == null) 111 { 112 return string.Empty; 113 } 114 115 return ((NamespaceDeclarationSyntax)syntaxNode).Name.ToString(); 116 } 117 118 private static void GenerateMethodTable(CodeGenerator generator, Compilation compilation, CommandInterface commandInterface) 119 { 120 generator.EnterScope($"public IReadOnlyDictionary<int, CommandHandler> GetCommandHandlers()"); 121 122 if (commandInterface.CommandImplementations.Count == 0) 123 { 124 generator.AppendLine("return FrozenDictionary<int, CommandHandler>.Empty;"); 125 } 126 else 127 { 128 generator.EnterScope($"return FrozenDictionary.ToFrozenDictionary(new []"); 129 130 foreach (var method in commandInterface.CommandImplementations) 131 { 132 foreach (var commandId in GetAttributeArguments(compilation, method, TypeCommandAttribute, 0)) 133 { 134 string[] args = new string[method.ParameterList.Parameters.Count]; 135 136 if (args.Length == 0) 137 { 138 generator.AppendLine($"KeyValuePair.Create({commandId}, new CommandHandler({method.Identifier.Text}, Array.Empty<CommandArg>())),"); 139 } 140 else 141 { 142 int index = 0; 143 144 foreach (var parameter in method.ParameterList.Parameters) 145 { 146 string canonicalTypeName = GetCanonicalTypeNameWithGenericArguments(compilation, parameter.Type); 147 CommandArgType argType = GetCommandArgType(compilation, parameter); 148 149 string arg; 150 151 if (argType == CommandArgType.Buffer) 152 { 153 string bufferFlags = GetFirstAttributeArgument(compilation, parameter, TypeBufferAttribute, 0); 154 string bufferFixedSize = GetFirstAttributeArgument(compilation, parameter, TypeBufferAttribute, 1); 155 156 if (bufferFixedSize != null) 157 { 158 arg = $"new CommandArg({bufferFlags} | HipcBufferFlags.FixedSize, {bufferFixedSize})"; 159 } 160 else 161 { 162 arg = $"new CommandArg({bufferFlags})"; 163 } 164 } 165 else if (argType == CommandArgType.InArgument || argType == CommandArgType.OutArgument) 166 { 167 string alignment = GetTypeAlignmentExpression(compilation, parameter.Type); 168 169 arg = $"new CommandArg(CommandArgType.{argType}, Unsafe.SizeOf<{canonicalTypeName}>(), {alignment})"; 170 } 171 else 172 { 173 arg = $"new CommandArg(CommandArgType.{argType})"; 174 } 175 176 args[index++] = arg; 177 } 178 179 generator.AppendLine($"KeyValuePair.Create({commandId}, new CommandHandler({method.Identifier.Text}, {string.Join(", ", args)})),"); 180 } 181 } 182 } 183 184 generator.LeaveScope(");"); 185 } 186 187 generator.LeaveScope(); 188 } 189 190 private static IEnumerable<string> GetAttributeArguments(Compilation compilation, SyntaxNode syntaxNode, string attributeName, int argIndex) 191 { 192 ISymbol symbol = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetDeclaredSymbol(syntaxNode); 193 194 foreach (var attribute in symbol.GetAttributes()) 195 { 196 if (attribute.AttributeClass.ToDisplayString() == attributeName && (uint)argIndex < (uint)attribute.ConstructorArguments.Length) 197 { 198 yield return attribute.ConstructorArguments[argIndex].ToCSharpString(); 199 } 200 } 201 } 202 203 private static string GetFirstAttributeArgument(Compilation compilation, SyntaxNode syntaxNode, string attributeName, int argIndex) 204 { 205 return GetAttributeArguments(compilation, syntaxNode, attributeName, argIndex).FirstOrDefault(); 206 } 207 208 private static void GenerateMethod(CodeGenerator generator, Compilation compilation, MethodDeclarationSyntax method) 209 { 210 int inObjectsCount = 0; 211 int outObjectsCount = 0; 212 int buffersCount = 0; 213 214 foreach (var parameter in method.ParameterList.Parameters) 215 { 216 if (IsObject(compilation, parameter)) 217 { 218 if (IsIn(parameter)) 219 { 220 inObjectsCount++; 221 } 222 else 223 { 224 outObjectsCount++; 225 } 226 } 227 else if (IsBuffer(compilation, parameter)) 228 { 229 buffersCount++; 230 } 231 } 232 233 generator.EnterScope($"private Result {method.Identifier.Text}(" + 234 "ref ServiceDispatchContext context, " + 235 "HipcCommandProcessor processor, " + 236 "ServerMessageRuntimeMetadata runtimeMetadata, " + 237 "ReadOnlySpan<byte> inRawData, " + 238 "ref Span<CmifOutHeader> outHeader)"); 239 240 bool returnsResult = method.ReturnType != null && GetCanonicalTypeName(compilation, method.ReturnType) == TypeResult; 241 242 if (returnsResult || buffersCount != 0 || inObjectsCount != 0) 243 { 244 generator.AppendLine($"Result {ResultVariableName};"); 245 246 if (buffersCount != 0) 247 { 248 generator.AppendLine($"Span<bool> {IsBufferMapAliasVariableName} = stackalloc bool[{method.ParameterList.Parameters.Count}];"); 249 generator.AppendLine(); 250 251 generator.AppendLine($"{ResultVariableName} = processor.ProcessBuffers(ref context, {IsBufferMapAliasVariableName}, runtimeMetadata);"); 252 generator.EnterScope($"if ({ResultVariableName}.IsFailure)"); 253 generator.AppendLine($"return {ResultVariableName};"); 254 generator.LeaveScope(); 255 } 256 257 generator.AppendLine(); 258 } 259 260 List<OutParameter> outParameters = new List<OutParameter>(); 261 262 string[] args = new string[method.ParameterList.Parameters.Count]; 263 264 if (inObjectsCount != 0) 265 { 266 generator.AppendLine($"var {InObjectsVariableName} = new IServiceObject[{inObjectsCount}];"); 267 generator.AppendLine(); 268 269 generator.AppendLine($"{ResultVariableName} = processor.GetInObjects(context.Processor, {InObjectsVariableName});"); 270 generator.EnterScope($"if ({ResultVariableName}.IsFailure)"); 271 generator.AppendLine($"return {ResultVariableName};"); 272 generator.LeaveScope(); 273 generator.AppendLine(); 274 } 275 276 if (outObjectsCount != 0) 277 { 278 generator.AppendLine($"var {OutObjectsVariableName} = new IServiceObject[{outObjectsCount}];"); 279 } 280 281 int index = 0; 282 int inArgIndex = 0; 283 int outArgIndex = 0; 284 int inCopyHandleIndex = 0; 285 int inMoveHandleIndex = 0; 286 int inObjectIndex = 0; 287 288 foreach (var parameter in method.ParameterList.Parameters) 289 { 290 string name = parameter.Identifier.Text; 291 string argName = GetPrefixedArgName(name); 292 string canonicalTypeName = GetCanonicalTypeNameWithGenericArguments(compilation, parameter.Type); 293 CommandArgType argType = GetCommandArgType(compilation, parameter); 294 Modifier modifier = GetModifier(parameter); 295 bool isNonSpanBuffer = false; 296 297 if (modifier == Modifier.Out) 298 { 299 if (IsNonSpanOutBuffer(compilation, parameter)) 300 { 301 generator.AppendLine($"using var {argName} = CommandSerialization.GetWritableRegion(processor.GetBufferRange({index}));"); 302 303 argName = $"out {GenerateSpanCastElement0(canonicalTypeName, $"{argName}.Memory.Span")}"; 304 } 305 else 306 { 307 outParameters.Add(new OutParameter(argName, canonicalTypeName, outArgIndex++, argType)); 308 309 argName = $"out {canonicalTypeName} {argName}"; 310 } 311 } 312 else 313 { 314 string value = $"default({canonicalTypeName})"; 315 316 switch (argType) 317 { 318 case CommandArgType.InArgument: 319 value = $"CommandSerialization.DeserializeArg<{canonicalTypeName}>(inRawData, processor.GetInArgOffset({inArgIndex++}))"; 320 break; 321 case CommandArgType.InCopyHandle: 322 value = $"CommandSerialization.DeserializeCopyHandle(ref context, {inCopyHandleIndex++})"; 323 break; 324 case CommandArgType.InMoveHandle: 325 value = $"CommandSerialization.DeserializeMoveHandle(ref context, {inMoveHandleIndex++})"; 326 break; 327 case CommandArgType.ProcessId: 328 value = "CommandSerialization.DeserializeClientProcessId(ref context)"; 329 break; 330 case CommandArgType.InObject: 331 value = $"{InObjectsVariableName}[{inObjectIndex++}]"; 332 break; 333 case CommandArgType.Buffer: 334 if (IsMemory(compilation, parameter)) 335 { 336 value = $"CommandSerialization.GetWritableRegion(processor.GetBufferRange({index}))"; 337 } 338 else if (IsReadOnlySequence(compilation, parameter)) 339 { 340 value = $"CommandSerialization.GetReadOnlySequence(processor.GetBufferRange({index}))"; 341 } 342 else if (IsReadOnlySpan(compilation, parameter)) 343 { 344 string spanGenericTypeName = GetCanonicalTypeNameOfGenericArgument(compilation, parameter.Type, 0); 345 value = GenerateSpanCast(spanGenericTypeName, $"CommandSerialization.GetReadOnlySpan(processor.GetBufferRange({index}))"); 346 } 347 else if (IsSpan(compilation, parameter)) 348 { 349 value = $"CommandSerialization.GetWritableRegion(processor.GetBufferRange({index}))"; 350 } 351 else 352 { 353 value = $"CommandSerialization.GetRef<{canonicalTypeName}>(processor.GetBufferRange({index}))"; 354 isNonSpanBuffer = true; 355 } 356 break; 357 } 358 359 if (IsMemory(compilation, parameter)) 360 { 361 generator.AppendLine($"using var {argName} = {value};"); 362 363 argName = $"{argName}.Memory"; 364 } 365 else if (IsSpan(compilation, parameter)) 366 { 367 generator.AppendLine($"using var {argName} = {value};"); 368 369 string spanGenericTypeName = GetCanonicalTypeNameOfGenericArgument(compilation, parameter.Type, 0); 370 argName = GenerateSpanCast(spanGenericTypeName, $"{argName}.Memory.Span"); 371 } 372 else if (isNonSpanBuffer) 373 { 374 generator.AppendLine($"ref var {argName} = ref {value};"); 375 } 376 else if (argType == CommandArgType.InObject) 377 { 378 generator.EnterScope($"if (!({value} is {canonicalTypeName} {argName}))"); 379 generator.AppendLine("return SfResult.InvalidInObject;"); 380 generator.LeaveScope(); 381 } 382 else 383 { 384 generator.AppendLine($"var {argName} = {value};"); 385 } 386 } 387 388 if (modifier == Modifier.Ref) 389 { 390 argName = $"ref {argName}"; 391 } 392 else if (modifier == Modifier.In) 393 { 394 argName = $"in {argName}"; 395 } 396 397 args[index++] = argName; 398 } 399 400 if (args.Length - outParameters.Count > 0) 401 { 402 generator.AppendLine(); 403 } 404 405 if (returnsResult) 406 { 407 generator.AppendLine($"{ResultVariableName} = {method.Identifier.Text}({string.Join(", ", args)});"); 408 generator.AppendLine(); 409 410 generator.AppendLine($"Span<byte> {OutRawDataVariableName};"); 411 generator.AppendLine(); 412 413 generator.EnterScope($"if ({ResultVariableName}.IsFailure)"); 414 generator.AppendLine($"context.Processor.PrepareForErrorReply(ref context, out {OutRawDataVariableName}, runtimeMetadata);"); 415 generator.AppendLine($"CommandHandler.GetCmifOutHeaderPointer(ref outHeader, ref {OutRawDataVariableName});"); 416 generator.AppendLine($"return {ResultVariableName};"); 417 generator.LeaveScope(); 418 } 419 else 420 { 421 generator.AppendLine($"{method.Identifier.Text}({string.Join(", ", args)});"); 422 423 generator.AppendLine(); 424 generator.AppendLine($"Span<byte> {OutRawDataVariableName};"); 425 } 426 427 generator.AppendLine(); 428 429 generator.AppendLine($"var {ResponseVariableName} = context.Processor.PrepareForReply(ref context, out {OutRawDataVariableName}, runtimeMetadata);"); 430 generator.AppendLine($"CommandHandler.GetCmifOutHeaderPointer(ref outHeader, ref {OutRawDataVariableName});"); 431 generator.AppendLine(); 432 433 generator.EnterScope($"if ({OutRawDataVariableName}.Length < processor.OutRawDataSize)"); 434 generator.AppendLine("return SfResult.InvalidOutRawSize;"); 435 generator.LeaveScope(); 436 437 if (outParameters.Count != 0) 438 { 439 generator.AppendLine(); 440 441 int outCopyHandleIndex = 0; 442 int outMoveHandleIndex = outObjectsCount; 443 int outObjectIndex = 0; 444 445 for (int outIndex = 0; outIndex < outParameters.Count; outIndex++) 446 { 447 OutParameter outParameter = outParameters[outIndex]; 448 449 switch (outParameter.Type) 450 { 451 case CommandArgType.OutArgument: 452 generator.AppendLine($"CommandSerialization.SerializeArg<{outParameter.TypeName}>({OutRawDataVariableName}, processor.GetOutArgOffset({outParameter.Index}), {outParameter.Name});"); 453 break; 454 case CommandArgType.OutCopyHandle: 455 generator.AppendLine($"CommandSerialization.SerializeCopyHandle({ResponseVariableName}, {outCopyHandleIndex++}, {outParameter.Name});"); 456 break; 457 case CommandArgType.OutMoveHandle: 458 generator.AppendLine($"CommandSerialization.SerializeMoveHandle({ResponseVariableName}, {outMoveHandleIndex++}, {outParameter.Name});"); 459 break; 460 case CommandArgType.OutObject: 461 generator.AppendLine($"{OutObjectsVariableName}[{outObjectIndex++}] = {outParameter.Name};"); 462 break; 463 } 464 } 465 } 466 467 generator.AppendLine(); 468 469 if (outObjectsCount != 0 || buffersCount != 0) 470 { 471 if (outObjectsCount != 0) 472 { 473 generator.AppendLine($"processor.SetOutObjects(ref context, {ResponseVariableName}, {OutObjectsVariableName});"); 474 } 475 476 if (buffersCount != 0) 477 { 478 generator.AppendLine($"processor.SetOutBuffers({ResponseVariableName}, {IsBufferMapAliasVariableName});"); 479 } 480 481 generator.AppendLine(); 482 } 483 484 generator.AppendLine("return Result.Success;"); 485 generator.LeaveScope(); 486 } 487 488 private static string GetPrefixedArgName(string name) 489 { 490 return ArgVariablePrefix + name[0].ToString().ToUpperInvariant() + name.Substring(1); 491 } 492 493 private static string GetCanonicalTypeNameOfGenericArgument(Compilation compilation, SyntaxNode syntaxNode, int argIndex) 494 { 495 if (syntaxNode is GenericNameSyntax genericNameSyntax) 496 { 497 if ((uint)argIndex < (uint)genericNameSyntax.TypeArgumentList.Arguments.Count) 498 { 499 return GetCanonicalTypeNameWithGenericArguments(compilation, genericNameSyntax.TypeArgumentList.Arguments[argIndex]); 500 } 501 } 502 503 return GetCanonicalTypeName(compilation, syntaxNode); 504 } 505 506 private static string GetCanonicalTypeNameWithGenericArguments(Compilation compilation, SyntaxNode syntaxNode) 507 { 508 TypeInfo typeInfo = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode); 509 510 return typeInfo.Type.ToDisplayString(); 511 } 512 513 private static string GetCanonicalTypeName(Compilation compilation, SyntaxNode syntaxNode) 514 { 515 TypeInfo typeInfo = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode); 516 string typeName = typeInfo.Type.ToDisplayString(); 517 518 int genericArgsStartIndex = typeName.IndexOf('<'); 519 if (genericArgsStartIndex >= 0) 520 { 521 return typeName.Substring(0, genericArgsStartIndex); 522 } 523 524 return typeName; 525 } 526 527 private static SpecialType GetSpecialTypeName(Compilation compilation, SyntaxNode syntaxNode) 528 { 529 TypeInfo typeInfo = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode); 530 531 return typeInfo.Type.SpecialType; 532 } 533 534 private static string GetTypeAlignmentExpression(Compilation compilation, SyntaxNode syntaxNode) 535 { 536 TypeInfo typeInfo = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode); 537 538 // Since there's no way to get the alignment for a arbitrary type here, let's assume that all 539 // "special" types are primitive types aligned to their own length. 540 // Otherwise, assume that the type is a custom struct, that either defines an explicit alignment 541 // or has an alignment of 1 which is the lowest possible value. 542 if (typeInfo.Type.SpecialType == SpecialType.None) 543 { 544 string pack = GetTypeFirstNamedAttributeAgument(compilation, syntaxNode, TypeStructLayoutAttribute, "Pack"); 545 546 return pack ?? "1"; 547 } 548 else 549 { 550 return $"Unsafe.SizeOf<{typeInfo.Type.ToDisplayString()}>()"; 551 } 552 } 553 554 private static string GetTypeFirstNamedAttributeAgument(Compilation compilation, SyntaxNode syntaxNode, string attributeName, string argName) 555 { 556 ISymbol symbol = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode).Type; 557 558 foreach (var attribute in symbol.GetAttributes()) 559 { 560 if (attribute.AttributeClass.ToDisplayString() == attributeName) 561 { 562 foreach (var kv in attribute.NamedArguments) 563 { 564 if (kv.Key == argName) 565 { 566 return kv.Value.ToCSharpString(); 567 } 568 } 569 } 570 } 571 572 return null; 573 } 574 575 private static CommandArgType GetCommandArgType(Compilation compilation, ParameterSyntax parameter) 576 { 577 CommandArgType type = CommandArgType.Invalid; 578 579 if (IsIn(parameter)) 580 { 581 if (IsArgument(compilation, parameter)) 582 { 583 type = CommandArgType.InArgument; 584 } 585 else if (IsBuffer(compilation, parameter)) 586 { 587 type = CommandArgType.Buffer; 588 } 589 else if (IsCopyHandle(compilation, parameter)) 590 { 591 type = CommandArgType.InCopyHandle; 592 } 593 else if (IsMoveHandle(compilation, parameter)) 594 { 595 type = CommandArgType.InMoveHandle; 596 } 597 else if (IsObject(compilation, parameter)) 598 { 599 type = CommandArgType.InObject; 600 } 601 else if (IsProcessId(compilation, parameter)) 602 { 603 type = CommandArgType.ProcessId; 604 } 605 } 606 else if (IsOut(parameter)) 607 { 608 if (IsArgument(compilation, parameter)) 609 { 610 type = CommandArgType.OutArgument; 611 } 612 else if (IsNonSpanOutBuffer(compilation, parameter)) 613 { 614 type = CommandArgType.Buffer; 615 } 616 else if (IsCopyHandle(compilation, parameter)) 617 { 618 type = CommandArgType.OutCopyHandle; 619 } 620 else if (IsMoveHandle(compilation, parameter)) 621 { 622 type = CommandArgType.OutMoveHandle; 623 } 624 else if (IsObject(compilation, parameter)) 625 { 626 type = CommandArgType.OutObject; 627 } 628 } 629 630 return type; 631 } 632 633 private static bool IsArgument(Compilation compilation, ParameterSyntax parameter) 634 { 635 return !IsBuffer(compilation, parameter) && 636 !IsHandle(compilation, parameter) && 637 !IsObject(compilation, parameter) && 638 !IsProcessId(compilation, parameter) && 639 IsUnmanagedType(compilation, parameter.Type); 640 } 641 642 private static bool IsBuffer(Compilation compilation, ParameterSyntax parameter) 643 { 644 return HasAttribute(compilation, parameter, TypeBufferAttribute) && 645 IsValidTypeForBuffer(compilation, parameter); 646 } 647 648 private static bool IsNonSpanOutBuffer(Compilation compilation, ParameterSyntax parameter) 649 { 650 return HasAttribute(compilation, parameter, TypeBufferAttribute) && 651 IsUnmanagedType(compilation, parameter.Type); 652 } 653 654 private static bool IsValidTypeForBuffer(Compilation compilation, ParameterSyntax parameter) 655 { 656 return IsMemory(compilation, parameter) || 657 IsReadOnlySequence(compilation, parameter) || 658 IsReadOnlySpan(compilation, parameter) || 659 IsSpan(compilation, parameter) || 660 IsUnmanagedType(compilation, parameter.Type); 661 } 662 663 private static bool IsUnmanagedType(Compilation compilation, SyntaxNode syntaxNode) 664 { 665 TypeInfo typeInfo = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode); 666 667 return typeInfo.Type.IsUnmanagedType; 668 } 669 670 private static bool IsMemory(Compilation compilation, ParameterSyntax parameter) 671 { 672 return GetCanonicalTypeName(compilation, parameter.Type) == TypeSystemMemory; 673 } 674 675 private static bool IsReadOnlySequence(Compilation compilation, ParameterSyntax parameter) 676 { 677 return GetCanonicalTypeName(compilation, parameter.Type) == TypeSystemBuffersReadOnlySequence; 678 } 679 680 private static bool IsReadOnlySpan(Compilation compilation, ParameterSyntax parameter) 681 { 682 return GetCanonicalTypeName(compilation, parameter.Type) == TypeSystemReadOnlySpan; 683 } 684 685 private static bool IsSpan(Compilation compilation, ParameterSyntax parameter) 686 { 687 return GetCanonicalTypeName(compilation, parameter.Type) == TypeSystemSpan; 688 } 689 690 private static bool IsHandle(Compilation compilation, ParameterSyntax parameter) 691 { 692 return IsCopyHandle(compilation, parameter) || IsMoveHandle(compilation, parameter); 693 } 694 695 private static bool IsCopyHandle(Compilation compilation, ParameterSyntax parameter) 696 { 697 return HasAttribute(compilation, parameter, TypeCopyHandleAttribute) && 698 GetSpecialTypeName(compilation, parameter.Type) == SpecialType.System_Int32; 699 } 700 701 private static bool IsMoveHandle(Compilation compilation, ParameterSyntax parameter) 702 { 703 return HasAttribute(compilation, parameter, TypeMoveHandleAttribute) && 704 GetSpecialTypeName(compilation, parameter.Type) == SpecialType.System_Int32; 705 } 706 707 private static bool IsObject(Compilation compilation, ParameterSyntax parameter) 708 { 709 SyntaxNode syntaxNode = parameter.Type; 710 TypeInfo typeInfo = compilation.GetSemanticModel(syntaxNode.SyntaxTree).GetTypeInfo(syntaxNode); 711 712 return typeInfo.Type.ToDisplayString() == TypeIServiceObject || 713 typeInfo.Type.AllInterfaces.Any(x => x.ToDisplayString() == TypeIServiceObject); 714 } 715 716 private static bool IsProcessId(Compilation compilation, ParameterSyntax parameter) 717 { 718 return HasAttribute(compilation, parameter, TypeClientProcessIdAttribute) && 719 GetSpecialTypeName(compilation, parameter.Type) == SpecialType.System_UInt64; 720 } 721 722 private static bool IsIn(ParameterSyntax parameter) 723 { 724 return !IsOut(parameter); 725 } 726 727 private static bool IsOut(ParameterSyntax parameter) 728 { 729 return parameter.Modifiers.Any(SyntaxKind.OutKeyword); 730 } 731 732 private static Modifier GetModifier(ParameterSyntax parameter) 733 { 734 foreach (SyntaxToken syntaxToken in parameter.Modifiers) 735 { 736 if (syntaxToken.IsKind(SyntaxKind.RefKeyword)) 737 { 738 return Modifier.Ref; 739 } 740 else if (syntaxToken.IsKind(SyntaxKind.OutKeyword)) 741 { 742 return Modifier.Out; 743 } 744 else if (syntaxToken.IsKind(SyntaxKind.InKeyword)) 745 { 746 return Modifier.In; 747 } 748 } 749 750 return Modifier.None; 751 } 752 753 private static string GenerateSpanCastElement0(string targetType, string input) 754 { 755 return $"{GenerateSpanCast(targetType, input)}[0]"; 756 } 757 758 private static string GenerateSpanCast(string targetType, string input) 759 { 760 return targetType == "byte" 761 ? input 762 : $"MemoryMarshal.Cast<byte, {targetType}>({input})"; 763 } 764 765 private static bool HasAttribute(Compilation compilation, ParameterSyntax parameterSyntax, string fullAttributeName) 766 { 767 foreach (var attributeList in parameterSyntax.AttributeLists) 768 { 769 foreach (var attribute in attributeList.Attributes) 770 { 771 if (GetCanonicalTypeName(compilation, attribute) == fullAttributeName) 772 { 773 return true; 774 } 775 } 776 } 777 778 return false; 779 } 780 781 private static bool NeedsIServiceObjectImplementation(Compilation compilation, ClassDeclarationSyntax classDeclarationSyntax) 782 { 783 ITypeSymbol type = compilation.GetSemanticModel(classDeclarationSyntax.SyntaxTree).GetDeclaredSymbol(classDeclarationSyntax); 784 var serviceObjectInterface = type.AllInterfaces.FirstOrDefault(x => x.ToDisplayString() == TypeIServiceObject); 785 var interfaceMember = serviceObjectInterface?.GetMembers().FirstOrDefault(x => x.Name == "GetCommandHandlers"); 786 787 // Return true only if the class implements IServiceObject but does not actually implement the method 788 // that the interface defines, since this is the only case we want to handle, if the method already exists 789 // we have nothing to do. 790 return serviceObjectInterface != null && type.FindImplementationForInterfaceMember(interfaceMember) == null; 791 } 792 793 public void Initialize(GeneratorInitializationContext context) 794 { 795 context.RegisterForSyntaxNotifications(() => new HipcSyntaxReceiver()); 796 } 797 } 798 }