HipcGenerator.cs 34 KB

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