LuaObjectGenerator.Emit.cs 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  1. using Microsoft.CodeAnalysis;
  2. using Microsoft.CodeAnalysis.CSharp;
  3. using Microsoft.CodeAnalysis.CSharp.Syntax;
  4. namespace Lua.SourceGenerator;
  5. partial class LuaObjectGenerator
  6. {
  7. static string GetLuaValuePrefix(ITypeSymbol typeSymbol, SymbolReferences references, Compilation compilation)
  8. {
  9. if (SymbolEqualityComparer.Default.Equals(typeSymbol, references.Object))
  10. {
  11. return "global::Lua.LuaValue.FromObject(";
  12. }
  13. return compilation.ClassifyCommonConversion(typeSymbol, references.LuaUserData).Exists
  14. ? "global::Lua.LuaValue.FromUserData("
  15. : "(";
  16. }
  17. static bool TryEmit(TypeMetadata typeMetadata, CodeBuilder builder, SymbolReferences references, Compilation compilation, in SourceProductionContext context, Dictionary<INamedTypeSymbol, TypeMetadata> metaDict,
  18. TempCollections tempCollections)
  19. {
  20. try
  21. {
  22. var error = false;
  23. // must be partial
  24. if (!typeMetadata.IsPartial())
  25. {
  26. context.ReportDiagnostic(Diagnostic.Create(
  27. DiagnosticDescriptors.MustBePartial,
  28. typeMetadata.Syntax.Identifier.GetLocation(),
  29. typeMetadata.Symbol.Name));
  30. error = true;
  31. }
  32. // nested is not allowed
  33. if (typeMetadata.IsNested())
  34. {
  35. context.ReportDiagnostic(Diagnostic.Create(
  36. DiagnosticDescriptors.NestedNotAllowed,
  37. typeMetadata.Syntax.Identifier.GetLocation(),
  38. typeMetadata.Symbol.Name));
  39. error = true;
  40. }
  41. // verify abstract/interface
  42. if (typeMetadata.Symbol.IsAbstract)
  43. {
  44. context.ReportDiagnostic(Diagnostic.Create(
  45. DiagnosticDescriptors.AbstractNotAllowed,
  46. typeMetadata.Syntax.Identifier.GetLocation(),
  47. typeMetadata.TypeName));
  48. error = true;
  49. }
  50. if (!ValidateMembers(typeMetadata, compilation, references, context, metaDict))
  51. {
  52. error = true;
  53. }
  54. if (error)
  55. {
  56. return false;
  57. }
  58. builder.AppendLine("// <auto-generated />");
  59. builder.AppendLine("#nullable enable");
  60. builder.AppendLine("#pragma warning disable CS0162 // Unreachable code");
  61. builder.AppendLine("#pragma warning disable CS0219 // Variable assigned but never used");
  62. builder.AppendLine("#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type.");
  63. builder.AppendLine("#pragma warning disable CS8601 // Possible null reference assignment");
  64. builder.AppendLine("#pragma warning disable CS8602 // Possible null return");
  65. builder.AppendLine("#pragma warning disable CS8604 // Possible null reference argument for parameter");
  66. builder.AppendLine("#pragma warning disable CS8631 // The type cannot be used as type parameter in the generic type or method");
  67. builder.AppendLine();
  68. var ns = typeMetadata.Symbol.ContainingNamespace;
  69. if (!ns.IsGlobalNamespace)
  70. {
  71. builder.AppendLine($"namespace {ns}");
  72. builder.BeginBlock();
  73. }
  74. var typeDeclarationKeyword = (typeMetadata.Symbol.IsRecord, typeMetadata.Symbol.IsValueType) switch
  75. {
  76. (true, true) => "record struct",
  77. (true, false) => "record",
  78. (false, true) => "struct",
  79. (false, false) => "class"
  80. };
  81. using var _ = builder.BeginBlockScope($"partial {typeDeclarationKeyword} {typeMetadata.TypeName} : global::Lua.ILuaUserData");
  82. var metamethodSet = tempCollections.Metamethods;
  83. if (!TryEmitMethods(typeMetadata, builder, references, compilation, metamethodSet, context))
  84. {
  85. return false;
  86. }
  87. if(!metamethodSet.Contains(LuaObjectMetamethod.Index))
  88. {
  89. if (!TryEmitIndexMetamethod(typeMetadata, builder, references, compilation, context, tempCollections))
  90. {
  91. return false;
  92. }
  93. metamethodSet.Add(LuaObjectMetamethod.Index);
  94. }
  95. if(!metamethodSet.Contains(LuaObjectMetamethod.NewIndex))
  96. {
  97. if (!TryEmitNewIndexMetamethod(typeMetadata, builder, references, context, tempCollections))
  98. {
  99. return false;
  100. }
  101. metamethodSet.Add(LuaObjectMetamethod.NewIndex);
  102. }
  103. if (!TryEmitMetatable(builder, metamethodSet, context))
  104. {
  105. return false;
  106. }
  107. // implicit operator
  108. builder.AppendLine($"public static implicit operator global::Lua.LuaValue({typeMetadata.FullTypeName} value)");
  109. using (builder.BeginBlockScope())
  110. {
  111. builder.AppendLine("return global::Lua.LuaValue.FromUserData(value);");
  112. }
  113. if (!ns.IsGlobalNamespace)
  114. {
  115. builder.EndBlock();
  116. }
  117. builder.AppendLine("#pragma warning restore CS0162 // Unreachable code");
  118. builder.AppendLine("#pragma warning restore CS0219 // Variable assigned but never used");
  119. builder.AppendLine("#pragma warning restore CS8600 // Converting null literal or possible null value to non-nullable type.");
  120. builder.AppendLine("#pragma warning restore CS8601 // Possible null reference assignment");
  121. builder.AppendLine("#pragma warning restore CS8602 // Possible null return");
  122. builder.AppendLine("#pragma warning restore CS8604 // Possible null reference argument for parameter");
  123. builder.AppendLine("#pragma warning restore CS8631 // The type cannot be used as type parameter in the generic type or method");
  124. return true;
  125. }
  126. catch (Exception)
  127. {
  128. return false;
  129. }
  130. }
  131. static bool ValidateMembers(TypeMetadata typeMetadata, Compilation compilation, SymbolReferences references, in SourceProductionContext context, Dictionary<INamedTypeSymbol, TypeMetadata> metaDict)
  132. {
  133. var isValid = true;
  134. foreach (var property in typeMetadata.Properties)
  135. {
  136. if (SymbolEqualityComparer.Default.Equals(property.Type, references.LuaValue))
  137. {
  138. continue;
  139. }
  140. if (SymbolEqualityComparer.Default.Equals(property.Type, references.LuaUserData))
  141. {
  142. continue;
  143. }
  144. if (SymbolEqualityComparer.Default.Equals(property.Type, typeMetadata.Symbol))
  145. {
  146. continue;
  147. }
  148. if (compilation.ClassifyConversion(property.Type, references.LuaUserData).Exists)
  149. {
  150. continue;
  151. }
  152. var conversion = compilation.ClassifyConversion(property.Type, references.LuaValue);
  153. if (!conversion.Exists && (property.Type is not INamedTypeSymbol namedTypeSymbol || !metaDict.ContainsKey(namedTypeSymbol)))
  154. {
  155. context.ReportDiagnostic(Diagnostic.Create(
  156. DiagnosticDescriptors.InvalidPropertyType,
  157. property.Symbol.Locations.FirstOrDefault(),
  158. property.Type.Name));
  159. isValid = false;
  160. }
  161. }
  162. foreach (var method in typeMetadata.Methods)
  163. {
  164. if (!method.Symbol.ReturnsVoid)
  165. {
  166. var typeSymbol = method.Symbol.ReturnType;
  167. if (method.IsAsync)
  168. {
  169. var namedType = (INamedTypeSymbol)typeSymbol;
  170. if (namedType.TypeArguments.Length == 0)
  171. {
  172. goto PARAMETERS;
  173. }
  174. typeSymbol = namedType.TypeArguments[0];
  175. }
  176. if (SymbolEqualityComparer.Default.Equals(typeSymbol, references.LuaValue))
  177. {
  178. goto PARAMETERS;
  179. }
  180. if (SymbolEqualityComparer.Default.Equals(typeSymbol, references.LuaUserData))
  181. {
  182. goto PARAMETERS;
  183. }
  184. if (SymbolEqualityComparer.Default.Equals(typeSymbol, typeMetadata.Symbol))
  185. {
  186. goto PARAMETERS;
  187. }
  188. if (compilation.ClassifyConversion(typeSymbol, references.LuaUserData).Exists)
  189. {
  190. goto PARAMETERS;
  191. }
  192. var conversion = compilation.ClassifyConversion(typeSymbol, references.LuaValue);
  193. if (!conversion.Exists && (typeSymbol is not INamedTypeSymbol namedTypeSymbol || !metaDict.ContainsKey(namedTypeSymbol)))
  194. {
  195. context.ReportDiagnostic(Diagnostic.Create(
  196. DiagnosticDescriptors.InvalidReturnType,
  197. typeSymbol.Locations.FirstOrDefault(),
  198. typeSymbol.Name));
  199. isValid = false;
  200. }
  201. }
  202. PARAMETERS:
  203. for (var index = 0; index < method.Symbol.Parameters.Length; index++)
  204. {
  205. var parameterSymbol = method.Symbol.Parameters[index];
  206. var typeSymbol = parameterSymbol.Type;
  207. if (index == method.Symbol.Parameters.Length - 1 && SymbolEqualityComparer.Default.Equals(typeSymbol, references.CancellationToken))
  208. {
  209. continue;
  210. }
  211. if (SymbolEqualityComparer.Default.Equals(typeSymbol, references.LuaValue))
  212. {
  213. continue;
  214. }
  215. if (SymbolEqualityComparer.Default.Equals(typeSymbol, references.LuaUserData))
  216. {
  217. continue;
  218. }
  219. if (SymbolEqualityComparer.Default.Equals(typeSymbol, references.Object))
  220. {
  221. continue;
  222. }
  223. if (SymbolEqualityComparer.Default.Equals(typeSymbol, typeMetadata.Symbol))
  224. {
  225. continue;
  226. }
  227. if (compilation.ClassifyConversion(typeSymbol, references.LuaUserData).Exists)
  228. {
  229. continue;
  230. }
  231. var conversion = compilation.ClassifyConversion(typeSymbol, references.LuaValue);
  232. if (!conversion.Exists && (typeSymbol is not INamedTypeSymbol namedTypeSymbol || !metaDict.ContainsKey(namedTypeSymbol)))
  233. {
  234. context.ReportDiagnostic(Diagnostic.Create(
  235. DiagnosticDescriptors.InvalidParameterType,
  236. typeSymbol.Locations.FirstOrDefault(),
  237. typeSymbol.Name));
  238. isValid = false;
  239. }
  240. }
  241. }
  242. return isValid;
  243. }
  244. static bool TryEmitIndexMetamethod(TypeMetadata typeMetadata, CodeBuilder builder, SymbolReferences references, Compilation compilation, in SourceProductionContext context, TempCollections tempCollections)
  245. {
  246. builder.AppendLine(@"static readonly global::Lua.LuaFunction __metamethod_index = new global::Lua.LuaFunction(""index"", (context, ct) =>");
  247. using (builder.BeginBlockScope())
  248. {
  249. builder.AppendLine($"var userData = context.GetArgument<{typeMetadata.FullTypeName}>(0);");
  250. builder.AppendLine("var key = context.GetArgument<global::System.String>(1);");
  251. builder.AppendLine("var result = key switch");
  252. using (builder.BeginBlockScope())
  253. {
  254. foreach (var propertyMetadata in typeMetadata.Properties)
  255. {
  256. if (propertyMetadata.IsWriteOnly)
  257. {
  258. tempCollections.InvalidMemberNames.Add(propertyMetadata.LuaMemberName);
  259. continue;
  260. }
  261. var conversionPrefix = GetLuaValuePrefix(propertyMetadata.Type, references, compilation);
  262. if (propertyMetadata.IsStatic)
  263. {
  264. builder.AppendLine(@$"""{propertyMetadata.LuaMemberName}"" => {conversionPrefix}{typeMetadata.FullTypeName}.{propertyMetadata.Symbol.Name}),");
  265. }
  266. else
  267. {
  268. builder.AppendLine(@$"""{propertyMetadata.LuaMemberName}"" => {conversionPrefix}userData.{propertyMetadata.Symbol.Name}),");
  269. }
  270. }
  271. foreach (var methodMetadata in typeMetadata.Methods
  272. .Where(x => x.HasMemberAttribute))
  273. {
  274. builder.AppendLine(@$"""{methodMetadata.LuaMemberName}"" => new global::Lua.LuaValue(__function_{methodMetadata.LuaMemberName}),");
  275. }
  276. builder.Append("_ => ");
  277. {
  278. if (tempCollections.InvalidMemberNames.Count > 0)
  279. {
  280. builder.Append("(key is ", false);
  281. for (var index = 0; index < tempCollections.InvalidMemberNames.Count; index++)
  282. {
  283. var name = tempCollections.InvalidMemberNames[index];
  284. builder.Append("\"", false);
  285. builder.Append(name, false);
  286. builder.Append("\"", false);
  287. if (index < tempCollections.InvalidMemberNames.Count - 1)
  288. {
  289. builder.Append(" or ", false);
  290. }
  291. }
  292. builder.AppendLine(")", false);
  293. using (builder.BeginIndentScope())
  294. {
  295. builder.AppendLine(@"? throw new global::Lua.LuaRuntimeException(context.State, $""'{key}' cannot be read."")");
  296. builder.AppendLine(": global::Lua.LuaValue.Nil,");
  297. }
  298. tempCollections.InvalidMemberNames.Clear();
  299. }
  300. else
  301. builder.AppendLine(@"global::Lua.LuaValue.Nil,");
  302. }
  303. }
  304. builder.AppendLine(";");
  305. builder.AppendLine("return new global::System.Threading.Tasks.ValueTask<int>(context.Return(result));");
  306. }
  307. builder.AppendLine(");");
  308. return true;
  309. }
  310. static bool TryEmitNewIndexMetamethod(TypeMetadata typeMetadata, CodeBuilder builder, SymbolReferences references, in SourceProductionContext context, TempCollections tempCollections)
  311. {
  312. builder.AppendLine(@"static readonly global::Lua.LuaFunction __metamethod_newindex = new global::Lua.LuaFunction(""newindex"", (context, ct) =>");
  313. using (builder.BeginBlockScope())
  314. {
  315. builder.AppendLine($"var userData = context.GetArgument<{typeMetadata.FullTypeName}>(0);");
  316. builder.AppendLine("var key = context.GetArgument<global::System.String>(1);");
  317. builder.AppendLine("switch (key)");
  318. using (builder.BeginBlockScope())
  319. {
  320. foreach (var propertyMetadata in typeMetadata.Properties)
  321. {
  322. if (propertyMetadata.IsReadOnly)
  323. {
  324. tempCollections.InvalidMemberNames.Add(propertyMetadata.LuaMemberName);
  325. continue;
  326. }
  327. builder.AppendLine(@$"case ""{propertyMetadata.LuaMemberName}"":");
  328. using (builder.BeginIndentScope())
  329. {
  330. if (propertyMetadata.IsStatic)
  331. {
  332. if (SymbolEqualityComparer.Default.Equals(propertyMetadata.Type, references.LuaValue))
  333. {
  334. builder.AppendLine($"{typeMetadata.FullTypeName}.{propertyMetadata.Symbol.Name} = context.GetArgument(2);");
  335. }
  336. else
  337. {
  338. builder.AppendLine($"{typeMetadata.FullTypeName}.{propertyMetadata.Symbol.Name} = context.GetArgument<{propertyMetadata.TypeFullName}>(2);");
  339. }
  340. builder.AppendLine("break;");
  341. }
  342. else
  343. {
  344. if (SymbolEqualityComparer.Default.Equals(propertyMetadata.Type, references.LuaValue))
  345. {
  346. builder.AppendLine($"userData.{propertyMetadata.Symbol.Name} = context.GetArgument(2);");
  347. }
  348. else
  349. {
  350. builder.AppendLine($"userData.{propertyMetadata.Symbol.Name} = context.GetArgument<{propertyMetadata.TypeFullName}>(2);");
  351. }
  352. builder.AppendLine("break;");
  353. }
  354. }
  355. }
  356. foreach (var methodMetadata in typeMetadata.Methods
  357. .Where(x => x.HasMemberAttribute))
  358. {
  359. tempCollections.InvalidMemberNames.Add(methodMetadata.LuaMemberName);
  360. }
  361. builder.AppendLine(@"default:");
  362. using (builder.BeginIndentScope())
  363. {
  364. if (tempCollections.InvalidMemberNames.Count > 0)
  365. {
  366. builder.AppendLine("throw new global::Lua.LuaRuntimeException(context.State,");
  367. using (builder.BeginIndentScope())
  368. {
  369. builder.Append("(key is ");
  370. for (var index = 0; index < tempCollections.InvalidMemberNames.Count; index++)
  371. {
  372. var name = tempCollections.InvalidMemberNames[index];
  373. builder.Append("\"", false);
  374. builder.Append(name, false);
  375. builder.Append("\"", false);
  376. if (index < tempCollections.InvalidMemberNames.Count - 1)
  377. {
  378. builder.Append(" or ", false);
  379. }
  380. }
  381. builder.AppendLine(")", false);
  382. using (builder.BeginIndentScope())
  383. {
  384. builder.AppendLine(@"? $""'{key}' cannot overwrite.""");
  385. tempCollections.InvalidMemberNames.Clear();
  386. builder.AppendLine(@": $""'{key}' not found."");");
  387. }
  388. }
  389. }
  390. else
  391. builder.AppendLine(@"throw new global::Lua.LuaRuntimeException(context.State, $""'{key}' not found."");");
  392. }
  393. }
  394. builder.AppendLine("return new global::System.Threading.Tasks.ValueTask<int>(context.Return());");
  395. }
  396. builder.AppendLine(");");
  397. return true;
  398. }
  399. static bool TryEmitMethods(TypeMetadata typeMetadata, CodeBuilder builder, SymbolReferences references, Compilation compilation, HashSet<LuaObjectMetamethod> metamethodSet, in SourceProductionContext context)
  400. {
  401. builder.AppendLine();
  402. foreach (var methodMetadata in typeMetadata.Methods)
  403. {
  404. string? functionName = null;
  405. if (methodMetadata.HasMemberAttribute)
  406. {
  407. functionName = $"__function_{methodMetadata.LuaMemberName}";
  408. EmitMethodFunction(functionName, methodMetadata.LuaMemberName, typeMetadata, methodMetadata, builder, references, compilation);
  409. }
  410. if (methodMetadata.HasMetamethodAttribute)
  411. {
  412. var metaMethodName = methodMetadata.Metamethod.ToString().ToLower();
  413. if (!metamethodSet.Add(methodMetadata.Metamethod))
  414. {
  415. context.ReportDiagnostic(Diagnostic.Create(
  416. DiagnosticDescriptors.DuplicateMetamethod,
  417. methodMetadata.Symbol.Locations.FirstOrDefault(),
  418. typeMetadata.TypeName,
  419. metaMethodName
  420. ));
  421. continue;
  422. }
  423. if (functionName == null)
  424. {
  425. EmitMethodFunction($"__metamethod_{metaMethodName}", metaMethodName, typeMetadata, methodMetadata, builder, references, compilation);
  426. }
  427. else
  428. {
  429. builder.AppendLine($"static global::Lua.LuaFunction __metamethod_{metaMethodName} => {functionName};");
  430. }
  431. }
  432. }
  433. return true;
  434. }
  435. static void EmitMethodFunction(string functionName, string chunkName, TypeMetadata typeMetadata, MethodMetadata methodMetadata, CodeBuilder builder, SymbolReferences references, Compilation compilation)
  436. {
  437. builder.AppendLine($@"static readonly global::Lua.LuaFunction {functionName} = new global::Lua.LuaFunction(""{chunkName}"", {(methodMetadata.IsAsync ? "async" : "")} (context, ct) =>");
  438. using (builder.BeginBlockScope())
  439. {
  440. var index = 0;
  441. if (!methodMetadata.IsStatic)
  442. {
  443. builder.AppendLine($"var userData = context.GetArgument<{typeMetadata.FullTypeName}>(0);");
  444. index++;
  445. }
  446. var hasCancellationToken = false;
  447. for (var i = 0; i < methodMetadata.Symbol.Parameters.Length; i++)
  448. {
  449. var parameter = methodMetadata.Symbol.Parameters[i];
  450. var parameterType = parameter.Type;
  451. var isParameterLuaValue = SymbolEqualityComparer.Default.Equals(parameterType, references.LuaValue);
  452. if (i == methodMetadata.Symbol.Parameters.Length - 1 && SymbolEqualityComparer.Default.Equals(parameterType, references.CancellationToken))
  453. {
  454. hasCancellationToken = true;
  455. break;
  456. }
  457. if (parameter.HasExplicitDefaultValue)
  458. {
  459. var syntax = (ParameterSyntax)parameter.DeclaringSyntaxReferences[0].GetSyntax();
  460. if (isParameterLuaValue)
  461. {
  462. builder.AppendLine($"var arg{index} = context.HasArgument({index}) ? context.GetArgument({index}) : {syntax.Default!.Value.ToFullString()};");
  463. }
  464. else
  465. {
  466. builder.AppendLine(
  467. $"var arg{index} = context.HasArgument({index}) ? context.GetArgument<{parameterType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>({index}) : {syntax.Default!.Value.ToFullString()};");
  468. }
  469. }
  470. else
  471. {
  472. if (isParameterLuaValue)
  473. {
  474. builder.AppendLine($"var arg{index} = context.GetArgument({index});");
  475. }
  476. else
  477. {
  478. builder.AppendLine($"var arg{index} = context.GetArgument<{parameterType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)}>({index});");
  479. }
  480. }
  481. index++;
  482. }
  483. if (methodMetadata.HasReturnValue)
  484. {
  485. builder.Append("var result = ");
  486. }
  487. if (methodMetadata.IsAsync)
  488. {
  489. builder.Append("await ", !methodMetadata.HasReturnValue);
  490. }
  491. if (methodMetadata.IsStatic)
  492. {
  493. builder.Append($"{typeMetadata.FullTypeName}.{methodMetadata.Symbol.Name}(", !(methodMetadata.HasReturnValue || methodMetadata.IsAsync));
  494. builder.Append(string.Join(",", Enumerable.Range(0, index).Select(x => $"arg{x}")), false);
  495. if (hasCancellationToken)
  496. {
  497. builder.Append(index > 0 ? ",ct" : "ct", false);
  498. }
  499. builder.AppendLine(");", false);
  500. }
  501. else
  502. {
  503. builder.Append($"userData.{methodMetadata.Symbol.Name}(", !(methodMetadata.HasReturnValue || methodMetadata.IsAsync));
  504. builder.Append(string.Join(",", Enumerable.Range(1, index - 1).Select(x => $"arg{x}")), false);
  505. if (hasCancellationToken)
  506. {
  507. builder.Append(index > 1 ? ",ct" : "ct", false);
  508. }
  509. builder.AppendLine(");", false);
  510. }
  511. builder.Append("return ");
  512. if (methodMetadata.HasReturnValue)
  513. {
  514. var returnType = methodMetadata.Symbol.ReturnType;
  515. if (methodMetadata.IsAsync)
  516. {
  517. var namedType = (INamedTypeSymbol)returnType;
  518. if (namedType.TypeArguments.Length == 1)
  519. {
  520. returnType = namedType.TypeArguments[0];
  521. }
  522. }
  523. var conversionPrefix = GetLuaValuePrefix(returnType, references, compilation);
  524. builder.AppendLine(methodMetadata.IsAsync ? $"context.Return({conversionPrefix}result));" : $"new global::System.Threading.Tasks.ValueTask<int>(context.Return({conversionPrefix}result)));", false);
  525. }
  526. else
  527. {
  528. builder.AppendLine(methodMetadata.IsAsync ? "context.Return();" : "new global::System.Threading.Tasks.ValueTask<int>(context.Return());", false);
  529. }
  530. }
  531. builder.AppendLine(");");
  532. builder.AppendLine();
  533. }
  534. static bool TryEmitMetatable(CodeBuilder builder, IEnumerable<LuaObjectMetamethod> metamethods, in SourceProductionContext context)
  535. {
  536. builder.AppendLine("global::Lua.LuaTable? global::Lua.ILuaUserData.Metatable");
  537. using (builder.BeginBlockScope())
  538. {
  539. builder.AppendLine("get");
  540. using (builder.BeginBlockScope())
  541. {
  542. builder.AppendLine("if (__metatable != null) return __metatable;");
  543. builder.AppendLine();
  544. builder.AppendLine("__metatable = new();");
  545. foreach (var metamethod in metamethods)
  546. {
  547. var metaMethodName = metamethod.ToString();
  548. var lowerName = metaMethodName.ToLower();
  549. builder.AppendLine($"__metatable[global::Lua.Runtime.Metamethods.{metaMethodName}] = __metamethod_{lowerName};");
  550. }
  551. builder.AppendLine("return __metatable;");
  552. }
  553. builder.AppendLine("set");
  554. using (builder.BeginBlockScope())
  555. {
  556. builder.AppendLine("__metatable = value;");
  557. }
  558. }
  559. builder.AppendLine("static global::Lua.LuaTable? __metatable;");
  560. builder.AppendLine();
  561. return true;
  562. }
  563. }