HipcGenerator.cs 32 KB

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