CodeGen.cs 71 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067
  1. using Microsoft.CodeAnalysis;
  2. using Microsoft.CodeAnalysis.CSharp;
  3. using Microsoft.CodeAnalysis.CSharp.Syntax;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.IO;
  7. using System.Reflection;
  8. using System.Text;
  9. using System.Linq;
  10. using static System.Console;
  11. using System.Xml.Linq;
  12. using SoftEther.JsonRpc;
  13. using Newtonsoft.Json;
  14. using Newtonsoft.Json.Linq;
  15. using Newtonsoft.Json.Serialization;
  16. using Markdig;
  17. namespace VPNServer_JSONRPC_CodeGen
  18. {
  19. public enum TargetLang
  20. {
  21. CSharp,
  22. TypeScript,
  23. }
  24. static class CodeGenUtil
  25. {
  26. public static string AppExeDir;
  27. public static string ProjectDir;
  28. public static string VpnSrcDir;
  29. public static string OutputDir_Clients;
  30. public static string OutputDir_HamCore;
  31. static CodeGenUtil()
  32. {
  33. AppExeDir = System.AppContext.BaseDirectory;
  34. ProjectDir = AppExeDir;
  35. string tmp = AppExeDir;
  36. while (true)
  37. {
  38. try
  39. {
  40. tmp = Path.GetDirectoryName(tmp);
  41. if (Directory.GetFiles(tmp, "*.csproj").Length >= 1)
  42. {
  43. ProjectDir = tmp;
  44. break;
  45. }
  46. }
  47. catch
  48. {
  49. break;
  50. }
  51. }
  52. OutputDir_Clients = Path.Combine(ProjectDir, @"..\vpnserver-jsonrpc-clients");
  53. string root_dir = Path.Combine(ProjectDir, @"..\..");
  54. string dirname = null;
  55. if (Directory.Exists(Path.Combine(root_dir, "Main"))) dirname = "Main";
  56. if (Directory.Exists(Path.Combine(root_dir, "src"))) dirname = "src";
  57. if (string.IsNullOrEmpty(dirname)) throw new ApplicationException($"Directory '{root_dir}' is not a root dir.");
  58. VpnSrcDir = dirname;
  59. OutputDir_HamCore = Path.Combine(root_dir, dirname, @"bin\hamcore");
  60. if (Directory.Exists(OutputDir_HamCore) == false) throw new ApplicationException($"Direction '{OutputDir_HamCore}' not found.");
  61. }
  62. public static void MakeDir(string path)
  63. {
  64. try
  65. {
  66. Directory.CreateDirectory(path);
  67. }
  68. catch
  69. {
  70. }
  71. }
  72. }
  73. class CSharpSourceCode
  74. {
  75. public SyntaxTree Tree { get; }
  76. public CompilationUnitSyntax Root { get; }
  77. public SemanticModel Model { get; set; }
  78. public CSharpSourceCode(string filename) : this(File.ReadAllText(filename), filename)
  79. {
  80. }
  81. public CSharpSourceCode(string body, string filename)
  82. {
  83. this.Tree = CSharpSyntaxTree.ParseText(body, path: filename);
  84. this.Root = this.Tree.GetCompilationUnitRoot();
  85. }
  86. }
  87. class CSharpCompiler
  88. {
  89. public string AssemblyName { get; }
  90. public List<MetadataReference> ReferencesList { get; } = new List<MetadataReference>();
  91. public List<CSharpSourceCode> SourceCodeList { get; } = new List<CSharpSourceCode>();
  92. CSharpCompilation _compilation = null;
  93. public CSharpCompilation Compilation
  94. {
  95. get
  96. {
  97. if (_compilation == null)
  98. {
  99. _compilation = CSharpCompilation.Create(this.AssemblyName,
  100. this.SourceCodeList.Select(s => s.Tree),
  101. this.ReferencesList,
  102. options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, optimizationLevel: OptimizationLevel.Debug,
  103. assemblyIdentityComparer: DesktopAssemblyIdentityComparer.Default));
  104. }
  105. return _compilation;
  106. }
  107. }
  108. public CSharpCompiler(string assembly_name)
  109. {
  110. this.AssemblyName = assembly_name;
  111. }
  112. public void AddReference(MetadataReference r)
  113. {
  114. this.ReferencesList.Add(r);
  115. }
  116. public void AddReferenceByPath(string path)
  117. {
  118. AddReference(MetadataReference.CreateFromFile(path));
  119. }
  120. public void AddReferenceByType(Type type)
  121. {
  122. AddReferenceByPath(type.Assembly.Location);
  123. }
  124. public void AddReferenceByAssemblyName(string name)
  125. {
  126. var a = System.Reflection.Assembly.Load(new System.Reflection.AssemblyName(name));
  127. AddReferenceByPath(a.Location);
  128. }
  129. public void AddReferenceDotNetStandard()
  130. {
  131. var a = System.Reflection.Assembly.Load(new System.Reflection.AssemblyName("netstandard"));
  132. AddReferenceByPath(a.Location);
  133. string dir = Path.GetDirectoryName(a.Location);
  134. AddReferenceByPath(Path.Combine(dir, "System.Private.CoreLib.dll"));
  135. foreach (var refa in a.GetReferencedAssemblies())
  136. {
  137. string dll_name = Path.Combine(dir, refa.Name) + ".dll";
  138. if (File.Exists(dll_name))
  139. {
  140. AddReferenceByPath(dll_name);
  141. }
  142. }
  143. }
  144. public void AddSourceCode(CSharpSourceCode cs)
  145. {
  146. this.SourceCodeList.Add(cs);
  147. }
  148. public bool OkOrPrintErrors()
  149. {
  150. MemoryStream ms = new MemoryStream();
  151. Microsoft.CodeAnalysis.Emit.EmitResult ret = Compilation.Emit(ms);
  152. if (ret.Success)
  153. {
  154. return true;
  155. }
  156. IEnumerable<Diagnostic> failures = ret.Diagnostics.Where(diagnostic =>
  157. diagnostic.IsWarningAsError ||
  158. diagnostic.Severity == DiagnosticSeverity.Error);
  159. foreach (Diagnostic diagnostic in failures)
  160. {
  161. WriteLine(diagnostic.ToString());
  162. }
  163. return false;
  164. }
  165. public void Compile(bool test_full_compile = false)
  166. {
  167. if (test_full_compile)
  168. {
  169. if (OkOrPrintErrors() == false)
  170. {
  171. throw new ApplicationException("Compile Error.");
  172. }
  173. }
  174. foreach (CSharpSourceCode cs in this.SourceCodeList)
  175. {
  176. cs.Model = this.Compilation.GetSemanticModel(cs.Tree);
  177. }
  178. }
  179. }
  180. class GeneratedCodePart
  181. {
  182. public int Seq = 0;
  183. public string Text = "";
  184. }
  185. class GeneratedCodeSection
  186. {
  187. public List<GeneratedCodePart> PartList = new List<GeneratedCodePart>();
  188. public override string ToString()
  189. {
  190. StringWriter w = new StringWriter();
  191. var a = this.PartList.OrderBy(x => x.Seq);
  192. foreach (var b in a)
  193. {
  194. w.Write(b.Text.ToString());
  195. }
  196. return w.ToString();
  197. }
  198. public void AddPart(int seq, string text)
  199. {
  200. this.PartList.Add(new GeneratedCodePart() { Seq = seq, Text = text });
  201. }
  202. }
  203. class GeneratedCode
  204. {
  205. public GeneratedCodeSection Types = new GeneratedCodeSection();
  206. public GeneratedCodeSection Stubs = new GeneratedCodeSection();
  207. public GeneratedCodeSection Tests = new GeneratedCodeSection();
  208. public override string ToString()
  209. {
  210. StringWriter w = new StringWriter();
  211. w.WriteLine("// --- Types ---");
  212. w.Write(this.Types.ToString());
  213. w.WriteLine();
  214. w.WriteLine("// --- Stubs ---");
  215. w.Write(this.Stubs.ToString());
  216. w.WriteLine();
  217. w.WriteLine("// --- Tests ---");
  218. w.Write(this.Tests.ToString());
  219. w.WriteLine();
  220. return w.ToString();
  221. }
  222. }
  223. class GeneratedCodeForLang
  224. {
  225. public GeneratedCode TypeScript = new GeneratedCode();
  226. public string DocsRpc = "";
  227. }
  228. static class CodeGenExtensions
  229. {
  230. public static string GetDocumentStr(this ISymbol sym)
  231. {
  232. if (sym == null) return "";
  233. string xml = sym.GetDocumentationCommentXml();
  234. if (string.IsNullOrEmpty(xml)) return "";
  235. XDocument doc = XDocument.Parse(xml);
  236. var summary = doc.Descendants("summary").FirstOrDefault();
  237. string str = summary.Value;
  238. if (string.IsNullOrEmpty(str)) return "";
  239. str = str.Replace(" (Async mode)", "", StringComparison.InvariantCultureIgnoreCase);
  240. str = str.Trim();
  241. return str;
  242. }
  243. }
  244. class RpcInfo
  245. {
  246. public string Name;
  247. public string TypeName;
  248. public IMethodSymbol Symbol;
  249. public HashSet<string> InputParamMembers = new HashSet<string>();
  250. }
  251. class RpcTypeParameterInfo
  252. {
  253. public string Name;
  254. public string Type;
  255. public string Description;
  256. }
  257. class RpcTypeInfo
  258. {
  259. public string Name;
  260. public string Description;
  261. public List<RpcTypeParameterInfo> Params = new List<RpcTypeParameterInfo>();
  262. public List<string> SubTypes = new List<string>();
  263. }
  264. class CodeGen
  265. {
  266. CSharpSourceCode cs_types, cs_stubs, cs_tests;
  267. public Dictionary<string, RpcInfo> rpc_list = new Dictionary<string, RpcInfo>();
  268. public Dictionary<string, RpcTypeInfo> rpc_type_list = new Dictionary<string, RpcTypeInfo>();
  269. CSharpCompiler csc;
  270. public CodeGen()
  271. {
  272. csc = new CSharpCompiler("Test");
  273. csc.AddReferenceDotNetStandard();
  274. csc.AddReferenceByType(typeof(Newtonsoft.Json.JsonPropertyAttribute));
  275. cs_types = new CSharpSourceCode(Path.Combine(CodeGenUtil.ProjectDir, @"VpnServerRpc\VPNServerRpcTypes.cs"));
  276. csc.AddSourceCode(cs_types);
  277. cs_stubs = new CSharpSourceCode(Path.Combine(CodeGenUtil.ProjectDir, @"VpnServerRpc\VPNServerRpc.cs"));
  278. csc.AddSourceCode(cs_stubs);
  279. cs_tests = new CSharpSourceCode(Path.Combine(CodeGenUtil.ProjectDir, @"VpnServerRpcTest\VpnServerRpcTest.cs"));
  280. csc.AddSourceCode(cs_tests);
  281. csc.Compile();
  282. }
  283. void generate_types(GeneratedCodeForLang ret)
  284. {
  285. var model = cs_types.Model;
  286. var class_list = cs_types.Root.DescendantNodes().OfType<ClassDeclarationSyntax>();
  287. foreach (ClassDeclarationSyntax c in class_list)
  288. {
  289. StringWriter ts = new StringWriter();
  290. string doc = model.GetDeclaredSymbol(c).GetDocumentStr();
  291. if (string.IsNullOrEmpty(doc) == false)
  292. {
  293. ts.WriteLine($"/** {doc} */");
  294. }
  295. RpcTypeInfo info = new RpcTypeInfo()
  296. {
  297. Name = c.Identifier.Text,
  298. Description = doc,
  299. };
  300. rpc_type_list[c.Identifier.Text] = info;
  301. ts.WriteLine($"export class {c.Identifier.Text}");
  302. ts.WriteLine("{");
  303. foreach (var member in model.GetDeclaredSymbol(c).GetMembers())
  304. {
  305. string json_name = "";
  306. bool json_name_has_special_char = false;
  307. var atts = member.GetAttributes();
  308. var y = atts.Where(x => x.AttributeClass.Name == "JsonPropertyAttribute").FirstOrDefault();
  309. if (y != null)
  310. {
  311. json_name = y.ConstructorArguments.FirstOrDefault().Value.ToString();
  312. if (json_name.IndexOf(':') != -1 || json_name.IndexOf('.') != -1) json_name_has_special_char = true;
  313. }
  314. string default_value = "\"\"";
  315. string enum_type = "";
  316. switch (member)
  317. {
  318. case IFieldSymbol field:
  319. string ts_type = "";
  320. ITypeSymbol type = field.Type;
  321. switch (type.Kind)
  322. {
  323. case SymbolKind.NamedType:
  324. switch (type.Name)
  325. {
  326. case "UInt32":
  327. case "UInt64":
  328. ts_type = "number";
  329. default_value = "0";
  330. break;
  331. case "String":
  332. ts_type = "string";
  333. break;
  334. case "Boolean":
  335. ts_type = "boolean";
  336. default_value = "false";
  337. break;
  338. case "DateTime":
  339. ts_type = "Date";
  340. default_value = "new Date()";
  341. break;
  342. default:
  343. if (type.TypeKind == TypeKind.Enum)
  344. {
  345. ts_type = type.Name;
  346. enum_type = type.Name;
  347. default_value = "0";
  348. break;
  349. }
  350. throw new ApplicationException($"{c.Identifier}.{member.Name}: type.Name = {type.Name}");
  351. }
  352. break;
  353. case SymbolKind.ArrayType:
  354. ITypeSymbol type2 = ((IArrayTypeSymbol)type).ElementType;
  355. default_value = "[]";
  356. switch (type2.Kind)
  357. {
  358. case SymbolKind.NamedType:
  359. switch (type2.Name)
  360. {
  361. case "UInt32":
  362. case "UInt64":
  363. ts_type = "number[]";
  364. break;
  365. case "String":
  366. ts_type = "string[]";
  367. break;
  368. case "Boolean":
  369. ts_type = "boolean[]";
  370. break;
  371. case "Byte":
  372. ts_type = "Uint8Array";
  373. default_value = "new Uint8Array([])";
  374. break;
  375. default:
  376. if (type2.ContainingAssembly.Name == csc.AssemblyName)
  377. {
  378. ts_type = type2.Name + "[]";
  379. enum_type = type2.Name;
  380. break;
  381. }
  382. throw new ApplicationException($"{c.Identifier}.{member.Name}: type2.Name = {type2.Name}");
  383. }
  384. break;
  385. default:
  386. throw new ApplicationException($"{c.Identifier}.{member.Name}: type2.Kind = {type2.Kind}");
  387. }
  388. break;
  389. default:
  390. throw new ApplicationException($"{c.Identifier}.{member.Name}: type.Kind = {type.Kind}");
  391. }
  392. if (string.IsNullOrEmpty(ts_type) == false)
  393. {
  394. string field_name = field.Name;
  395. string doc2 = member.GetDocumentStr();
  396. if (string.IsNullOrEmpty(json_name) == false) field_name = json_name;
  397. string info_type = ts_type;
  398. string info_type2 = "";
  399. if (field_name.EndsWith("_str")) info_type2 = "ASCII";
  400. if (field_name.EndsWith("_utf")) info_type2 = "UTF8";
  401. if (field_name.EndsWith("_ip")) info_type2 = "IP address";
  402. if (field_name.EndsWith("_u32")) info_type2 = "uint32";
  403. if (field_name.EndsWith("_u64")) info_type2 = "uint64";
  404. if (field_name.EndsWith("_bin")) { info_type2 = "Base64 binary"; info_type = "string"; }
  405. string docs_add = "";
  406. if (string.IsNullOrEmpty(enum_type) == false)
  407. {
  408. Type et = Type.GetType("SoftEther.VPNServerRpc." + enum_type);
  409. if (et.IsEnum)
  410. {
  411. docs_add += "<BR>Values:";
  412. var ed = cs_types.Root.DescendantNodes().OfType<EnumDeclarationSyntax>()
  413. .Where(e => e.Identifier.Text == enum_type)
  414. .Single();
  415. foreach (var em in model.GetDeclaredSymbol(ed).GetMembers())
  416. {
  417. switch (em)
  418. {
  419. case IFieldSymbol ef:
  420. if (ef.IsConst && ef.IsDefinition)
  421. {
  422. string doc3 = em.GetDocumentStr();
  423. docs_add += $"<BR>`{ef.ConstantValue}`: {doc3}";
  424. }
  425. break;
  426. }
  427. }
  428. info_type = "number";
  429. info_type2 = "enum";
  430. }
  431. else
  432. {
  433. if (info.SubTypes.Contains(enum_type) == false)
  434. {
  435. info.SubTypes.Add(enum_type);
  436. info_type = "Array object";
  437. }
  438. }
  439. }
  440. info_type = "`" + info_type + "`";
  441. if (string.IsNullOrEmpty(info_type2) == false) info_type += " (" + info_type2 + ")";
  442. info.Params.Add(new RpcTypeParameterInfo()
  443. {
  444. Name = field_name,
  445. Type = info_type,
  446. Description = doc2 + docs_add,
  447. });
  448. if (json_name_has_special_char) field_name = $"[\"{json_name}\"]";
  449. if (string.IsNullOrEmpty(doc2) == false)
  450. {
  451. ts.WriteLine($" /** {doc2} */");
  452. }
  453. ts.WriteLine($" public {field_name}: {ts_type} = {default_value};");
  454. ts.WriteLine();
  455. }
  456. break;
  457. case IMethodSymbol method when method.MethodKind == MethodKind.Constructor:
  458. break;
  459. default:
  460. throw new ApplicationException($"{c.Identifier}.{member.Name}: type = {member.GetType()}");
  461. }
  462. }
  463. if (string.IsNullOrEmpty(doc) == false)
  464. {
  465. ts.WriteLine($" /** Constructor for the '{c.Identifier.Text}' class: {doc} */");
  466. }
  467. ts.WriteLine($" public constructor(init?: Partial<{c.Identifier.Text}>)");
  468. ts.WriteLine(" {");
  469. ts.WriteLine(" Object.assign(this, init);");
  470. ts.WriteLine(" }");
  471. ts.WriteLine("}");
  472. ts.WriteLine();
  473. ret.TypeScript.Types.AddPart(c.SpanStart, ts.ToString());
  474. }
  475. var enum_list = cs_types.Root.DescendantNodes().OfType<EnumDeclarationSyntax>();
  476. foreach (EnumDeclarationSyntax e in enum_list)
  477. {
  478. StringWriter ts = new StringWriter();
  479. string doc = model.GetDeclaredSymbol(e).GetDocumentStr();
  480. if (string.IsNullOrEmpty(doc) == false)
  481. {
  482. ts.WriteLine($"/** {doc} */");
  483. }
  484. ts.WriteLine($"export enum {e.Identifier.Text}");
  485. ts.WriteLine("{");
  486. foreach (var member in model.GetDeclaredSymbol(e).GetMembers())
  487. {
  488. switch (member)
  489. {
  490. case IFieldSymbol field:
  491. if (field.IsConst && field.IsDefinition)
  492. {
  493. string doc2 = member.GetDocumentStr();
  494. if (string.IsNullOrEmpty(doc2) == false)
  495. {
  496. ts.WriteLine($" /** {doc2} */");
  497. }
  498. ts.WriteLine($" {field.Name} = {field.ConstantValue},");
  499. ts.WriteLine();
  500. }
  501. break;
  502. }
  503. }
  504. ts.WriteLine("}");
  505. ts.WriteLine();
  506. ret.TypeScript.Types.AddPart(e.SpanStart, ts.ToString());
  507. }
  508. }
  509. void generate_stubs(GeneratedCodeForLang ret)
  510. {
  511. var model = cs_stubs.Model;
  512. var rpc_class = cs_stubs.Root.DescendantNodes().OfType<ClassDeclarationSyntax>().Where(c => c.Identifier.Text == "VpnServerRpc").First();
  513. var members = model.GetDeclaredSymbol(rpc_class).GetMembers();
  514. var methods = members.Where(m => m is IMethodSymbol).Select(m => m as IMethodSymbol).Where(m => m.IsStatic == false)
  515. .Where(m => m.IsAsync).Where(m => m.Name != "CallAsync");
  516. foreach (var method in methods)
  517. {
  518. string method_name = method.Name;
  519. if (method_name.EndsWith("Async") == false) throw new ApplicationException($"{method.Name}: method_name = {method_name}");
  520. method_name = method_name.Substring(0, method_name.Length - 5);
  521. INamedTypeSymbol ret_type = (INamedTypeSymbol)method.ReturnType;
  522. if (ret_type.Name != "Task") throw new ApplicationException($"{method.Name}: ret_type.Name = {ret_type.Name}");
  523. var ret_type_args = ret_type.TypeArguments;
  524. if (ret_type_args.Length != 1) throw new ApplicationException($"{method.Name}: type_args.Length = {ret_type_args.Length}");
  525. var ret_type_name = ret_type_args[0].Name;
  526. if (method.Parameters.Length >= 2) throw new ApplicationException($"{method.Name}: method.Parameters.Length = {method.Parameters.Length}");
  527. if (method.DeclaringSyntaxReferences.Length != 1) throw new ApplicationException($"{method.Name}: method.DeclaringSyntaxReferences.Length = {method.DeclaringSyntaxReferences.Length}");
  528. MethodDeclarationSyntax syntax = (MethodDeclarationSyntax)method.DeclaringSyntaxReferences[0].GetSyntax();
  529. if (syntax.Body != null) throw new ApplicationException($"{method.Name}: syntax.Body != null");
  530. if (syntax.ExpressionBody == null) throw new ApplicationException($"{method.Name}: syntax.ExpressionBody == null");
  531. ArrowExpressionClauseSyntax body = syntax.ExpressionBody;
  532. InvocationExpressionSyntax invoke = body.DescendantNodes().OfType<InvocationExpressionSyntax>().Single();
  533. if (model.GetSymbolInfo(invoke.Expression).Symbol.Name != "CallAsync") throw new ApplicationException($"{method.Name}: model.GetSymbolInfo(invoke.Expression).Symbol.Name = {model.GetSymbolInfo(invoke.Expression).Symbol.Name}");
  534. if (invoke.ArgumentList.Arguments.Count != 2) throw new ApplicationException($"{method.Name}: invoke.ArgumentList.Arguments.Count = {invoke.ArgumentList.Arguments.Count}");
  535. LiteralExpressionSyntax str_syntax = (LiteralExpressionSyntax)invoke.ArgumentList.Arguments[0].Expression;
  536. string str = str_syntax.Token.Text;
  537. StringWriter ts = new StringWriter();
  538. string doc2 = method.GetDocumentStr();
  539. if (string.IsNullOrEmpty(doc2) == false)
  540. {
  541. ts.WriteLine($" /** {doc2} */");
  542. }
  543. if (method.Parameters.Length == 0)
  544. {
  545. ts.WriteLine($" public {method_name} = (): Promise<{ret_type_name}> =>");
  546. ts.WriteLine(" {");
  547. ts.WriteLine($" return this.CallAsync<{ret_type_name}>({str}, new {ret_type_name}());");
  548. ts.WriteLine(" }");
  549. ts.WriteLine(" ");
  550. }
  551. else
  552. {
  553. ts.WriteLine($" public {method_name} = (in_param: {ret_type_name}): Promise<{ret_type_name}> =>");
  554. ts.WriteLine(" {");
  555. ts.WriteLine($" return this.CallAsync<{ret_type_name}>({str}, in_param);");
  556. ts.WriteLine(" }");
  557. ts.WriteLine(" ");
  558. }
  559. rpc_list[method_name] = new RpcInfo()
  560. {
  561. Name = method_name,
  562. TypeName = ret_type_name,
  563. Symbol = method,
  564. };
  565. ret.TypeScript.Stubs.AddPart(method.DeclaringSyntaxReferences[0].Span.Start, ts.ToString());
  566. }
  567. }
  568. class CcWalker : CSharpSyntaxWalker
  569. {
  570. StringWriter w = new StringWriter();
  571. List<string> lines = new List<string>();
  572. string current_line = "";
  573. int current_depth = 0;
  574. const int TabSpace = 4;
  575. CSharpSourceCode src;
  576. TargetLang lang;
  577. public CcWalker(CSharpSourceCode src, TargetLang lang) : base(SyntaxWalkerDepth.StructuredTrivia)
  578. {
  579. this.src = src;
  580. this.lang = lang;
  581. }
  582. string convert_type(string src)
  583. {
  584. if (lang == TargetLang.TypeScript)
  585. {
  586. if (src.StartsWith("Vpn"))
  587. {
  588. src = "VPN." + src;
  589. }
  590. if (src == "int" || src == "uint" || src == "long" || src == "ulong")
  591. {
  592. src = "number";
  593. }
  594. if (src == "bool")
  595. {
  596. src = "boolean";
  597. }
  598. if (src == "DateTime")
  599. {
  600. src = "Date";
  601. }
  602. }
  603. return src;
  604. }
  605. string convert_function(string src)
  606. {
  607. if (lang == TargetLang.TypeScript)
  608. {
  609. if (src == "Console.WriteLine" || src == "print_object")
  610. {
  611. src = "console.log";
  612. }
  613. if (src.StartsWith("api.") || src.StartsWith("Test_"))
  614. {
  615. src = "await " + src;
  616. }
  617. }
  618. return src;
  619. }
  620. void _emit_internal(string str, bool new_line)
  621. {
  622. if (string.IsNullOrEmpty(current_line))
  623. {
  624. current_line += new string(' ', current_depth * TabSpace);
  625. }
  626. current_line += str;
  627. if (new_line)
  628. {
  629. lines.Add(current_line);
  630. current_line = "";
  631. }
  632. }
  633. void emit_line(string str = "") => emit(str + "\r\n");
  634. void emit(string str, bool new_line)
  635. {
  636. if (new_line == false)
  637. {
  638. emit(str);
  639. }
  640. else
  641. {
  642. emit_line(str);
  643. }
  644. }
  645. void emit(string str)
  646. {
  647. string tmp = "";
  648. for (int i = 0; i < str.Length; i++)
  649. {
  650. char c = str[i];
  651. if (c == '\r') { }
  652. else if (c == '\n')
  653. {
  654. _emit_internal(tmp, true);
  655. tmp = "";
  656. }
  657. else
  658. {
  659. tmp += c;
  660. }
  661. }
  662. if (String.IsNullOrEmpty(tmp) == false)
  663. {
  664. _emit_internal(tmp, false);
  665. }
  666. }
  667. public override void VisitMethodDeclaration(MethodDeclarationSyntax node)
  668. {
  669. if (node.Identifier.Text == "print_object") return;
  670. if (lang == TargetLang.TypeScript)
  671. {
  672. emit_line();
  673. var sem = src.Model.GetDeclaredSymbol(node);
  674. string doc2 = sem.GetDocumentStr();
  675. if (string.IsNullOrEmpty(doc2) == false)
  676. {
  677. emit_line($"/** {doc2} */");
  678. }
  679. emit("async function ");
  680. emit(node.Identifier.Text);
  681. Visit(node.ParameterList);
  682. emit(": ");
  683. emit("Promise<");
  684. Visit(node.ReturnType);
  685. emit(">");
  686. emit_line("");
  687. Visit(node.Body);
  688. }
  689. else
  690. {
  691. emit("public");
  692. emit(" ");
  693. Visit(node.ReturnType);
  694. emit(" ");
  695. emit(node.Identifier.Text);
  696. Visit(node.ParameterList);
  697. emit_line("");
  698. Visit(node.Body);
  699. }
  700. }
  701. public override void VisitParameter(ParameterSyntax node)
  702. {
  703. if (lang == TargetLang.TypeScript)
  704. {
  705. emit($"{node.Identifier.Text}");
  706. emit(": ");
  707. Visit(node.Type);
  708. }
  709. else
  710. {
  711. Visit(node.Type);
  712. emit(" ");
  713. emit($"{node.Identifier.Text}");
  714. }
  715. }
  716. public override void VisitParameterList(ParameterListSyntax node)
  717. {
  718. emit("(");
  719. int num = 0;
  720. foreach (ParameterSyntax p in node.Parameters)
  721. {
  722. if (num >= 1)
  723. {
  724. emit(", ");
  725. }
  726. Visit(p);
  727. num++;
  728. }
  729. emit(")");
  730. }
  731. public override void VisitArgumentList(ArgumentListSyntax node)
  732. {
  733. emit("(");
  734. int num = 0;
  735. foreach (ArgumentSyntax arg in node.Arguments)
  736. {
  737. if (num >= 1)
  738. {
  739. emit(", ");
  740. }
  741. this.VisitArgument(arg);
  742. num++;
  743. }
  744. emit(")");
  745. }
  746. public override void VisitAssignmentExpression(AssignmentExpressionSyntax node)
  747. {
  748. if (lang == TargetLang.TypeScript)
  749. {
  750. if (node.Parent.Kind() == SyntaxKind.ObjectInitializerExpression)
  751. {
  752. Visit(node.Left);
  753. emit(": ");
  754. Visit(node.Right);
  755. }
  756. else
  757. {
  758. Visit(node.Left);
  759. emit(" = ");
  760. Visit(node.Right);
  761. }
  762. }
  763. else
  764. {
  765. Visit(node.Left);
  766. emit(" = ");
  767. Visit(node.Right);
  768. }
  769. }
  770. public override void VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
  771. {
  772. Visit(node.Expression);
  773. emit(node.OperatorToken.Text);
  774. Visit(node.Name);
  775. }
  776. public override void VisitCastExpression(CastExpressionSyntax node)
  777. {
  778. if (lang == TargetLang.TypeScript)
  779. {
  780. Visit(node.Expression);
  781. }
  782. else
  783. {
  784. emit("(");
  785. Visit(node.Type);
  786. emit(")");
  787. Visit(node.Expression);
  788. }
  789. }
  790. public override void VisitBreakStatement(BreakStatementSyntax node)
  791. {
  792. emit_line("break;");
  793. }
  794. public override void VisitReturnStatement(ReturnStatementSyntax node)
  795. {
  796. if (node.Expression == null)
  797. {
  798. emit_line("return;");
  799. }
  800. else
  801. {
  802. emit("return");
  803. emit(" ");
  804. Visit(node.Expression);
  805. emit_line(";");
  806. }
  807. }
  808. public override void VisitForEachStatement(ForEachStatementSyntax node)
  809. {
  810. if (lang == TargetLang.TypeScript)
  811. {
  812. emit("for (let ");
  813. emit(node.Identifier.Text);
  814. emit(" of ");
  815. Visit(node.Expression);
  816. emit_line(")");
  817. Visit(node.Statement);
  818. }
  819. else
  820. {
  821. emit("foreach (");
  822. Visit(node.Type);
  823. emit(" ");
  824. emit(node.Identifier.Text);
  825. emit(" in ");
  826. Visit(node.Expression);
  827. emit_line(")");
  828. Visit(node.Statement);
  829. }
  830. }
  831. public override void VisitExpressionStatement(ExpressionStatementSyntax node)
  832. {
  833. Visit(node.Expression);
  834. emit_line(";");
  835. }
  836. public override void VisitConditionalExpression(ConditionalExpressionSyntax node)
  837. {
  838. Visit(node.Condition);
  839. emit(" ? ");
  840. Visit(node.WhenTrue);
  841. emit(" : ");
  842. Visit(node.WhenFalse);
  843. }
  844. public override void VisitIfStatement(IfStatementSyntax node)
  845. {
  846. emit("if (");
  847. Visit(node.Condition);
  848. emit_line(")");
  849. Visit(node.Statement);
  850. if (node.Else != null)
  851. {
  852. if (node.Else.Statement is IfStatementSyntax)
  853. {
  854. emit("else ");
  855. }
  856. else
  857. {
  858. emit_line("else");
  859. }
  860. Visit(node.Else.Statement);
  861. }
  862. }
  863. public override void VisitInitializerExpression(InitializerExpressionSyntax node)
  864. {
  865. if (lang == TargetLang.TypeScript)
  866. {
  867. if (node.Kind() == SyntaxKind.ArrayInitializerExpression)
  868. {
  869. bool is_byte_array = false;
  870. if (node.Parent.Kind() == SyntaxKind.ArrayCreationExpression &&
  871. ((ArrayCreationExpressionSyntax)node.Parent).Type.ElementType.ToString() == "byte")
  872. {
  873. is_byte_array = true;
  874. }
  875. if (is_byte_array)
  876. {
  877. emit("new Uint8Array(");
  878. }
  879. emit("[ ");
  880. current_depth++;
  881. foreach (var exp in node.Expressions)
  882. {
  883. this.Visit(exp);
  884. emit(", ");
  885. }
  886. current_depth--;
  887. emit(" ]");
  888. if (is_byte_array)
  889. {
  890. emit(")");
  891. }
  892. }
  893. else
  894. {
  895. emit_line("{");
  896. current_depth++;
  897. foreach (var exp in node.Expressions)
  898. {
  899. this.Visit(exp);
  900. emit_line(",");
  901. }
  902. current_depth--;
  903. emit("}");
  904. }
  905. }
  906. else
  907. {
  908. if (node.Kind() == SyntaxKind.ArrayInitializerExpression)
  909. {
  910. emit("{ ");
  911. current_depth++;
  912. foreach (var exp in node.Expressions)
  913. {
  914. this.Visit(exp);
  915. emit(", ");
  916. }
  917. current_depth--;
  918. emit(" }");
  919. }
  920. else
  921. {
  922. emit_line("{");
  923. current_depth++;
  924. foreach (var exp in node.Expressions)
  925. {
  926. this.Visit(exp);
  927. emit_line(",");
  928. }
  929. current_depth--;
  930. emit("}");
  931. }
  932. }
  933. }
  934. public override void VisitArrayCreationExpression(ArrayCreationExpressionSyntax node)
  935. {
  936. if (lang == TargetLang.TypeScript)
  937. {
  938. var type = node.Type;
  939. if (node.Initializer != null)
  940. {
  941. emit(" ");
  942. Visit(node.Initializer);
  943. }
  944. else
  945. {
  946. emit("[]");
  947. }
  948. }
  949. else
  950. {
  951. var type = node.Type;
  952. emit("new ");
  953. Visit(node.Type);
  954. if (node.Initializer != null)
  955. {
  956. emit(" ");
  957. Visit(node.Initializer);
  958. }
  959. }
  960. }
  961. public override void VisitObjectCreationExpression(ObjectCreationExpressionSyntax node)
  962. {
  963. if (lang == TargetLang.TypeScript)
  964. {
  965. var type = (IdentifierNameSyntax)node.Type;
  966. if (node.Initializer == null)
  967. {
  968. emit("new ");
  969. Visit(node.Type);
  970. // emit($"new {type.Identifier.Text}");
  971. Visit(node.ArgumentList);
  972. }
  973. else
  974. {
  975. emit("new ");
  976. Visit(node.Type);
  977. emit_line("(");
  978. Visit(node.Initializer);
  979. emit(")");
  980. }
  981. }
  982. else
  983. {
  984. var type = (IdentifierNameSyntax)node.Type;
  985. emit($"new {type.Identifier.Text}");
  986. Visit(node.ArgumentList);
  987. if (node.Initializer != null)
  988. {
  989. emit_line("");
  990. Visit(node.Initializer);
  991. }
  992. }
  993. }
  994. public override void VisitLiteralExpression(LiteralExpressionSyntax node)
  995. {
  996. emit(node.Token.Text);
  997. }
  998. public override void VisitParenthesizedExpression(ParenthesizedExpressionSyntax node)
  999. {
  1000. emit("(");
  1001. base.Visit(node.Expression);
  1002. emit(")");
  1003. }
  1004. public override void VisitBinaryExpression(BinaryExpressionSyntax node)
  1005. {
  1006. base.Visit(node.Left);
  1007. emit($" {node.OperatorToken.Text} ");
  1008. base.Visit(node.Right);
  1009. }
  1010. public override void VisitIdentifierName(IdentifierNameSyntax node)
  1011. {
  1012. string name = node.Identifier.Text;
  1013. if (node.Parent.Kind() == SyntaxKind.VariableDeclaration
  1014. || node.Parent.Kind() == SyntaxKind.MethodDeclaration
  1015. || node.Parent.Kind() == SyntaxKind.SimpleMemberAccessExpression
  1016. || node.Parent.Kind() == SyntaxKind.ForEachStatement
  1017. || node.Parent.Kind() == SyntaxKind.Parameter
  1018. || node.Parent.Kind() == SyntaxKind.ObjectCreationExpression)
  1019. {
  1020. name = convert_type(name);
  1021. }
  1022. var sym = src.Model.GetSymbolInfo(node);
  1023. string json_name = "";
  1024. bool json_name_has_special_char = false;
  1025. var atts = sym.Symbol.GetAttributes();
  1026. var y = atts.Where(x => x.AttributeClass.Name == "JsonPropertyAttribute").FirstOrDefault();
  1027. if (y != null)
  1028. {
  1029. json_name = y.ConstructorArguments.FirstOrDefault().Value.ToString();
  1030. if (json_name.IndexOf(':') != -1 || json_name.IndexOf('.') != -1) json_name_has_special_char = true;
  1031. }
  1032. string field_name = name;
  1033. if (lang == TargetLang.TypeScript)
  1034. {
  1035. if (string.IsNullOrEmpty(json_name) == false) field_name = json_name;
  1036. if (json_name_has_special_char) field_name = $"[\"{json_name}\"]";
  1037. }
  1038. emit(field_name);
  1039. }
  1040. public override void VisitInvocationExpression(InvocationExpressionSyntax node)
  1041. {
  1042. string func_name = node.Expression.ToString();
  1043. func_name = convert_function(func_name);
  1044. if (lang == TargetLang.TypeScript)
  1045. {
  1046. if (func_name == "rand.Next")
  1047. {
  1048. string a = node.ArgumentList.Arguments[0].ToString();
  1049. string b = node.ArgumentList.Arguments[1].ToString();
  1050. emit($"Math.floor((Math.random() * ({b} - {a})) + {a})");
  1051. return;
  1052. }
  1053. if (func_name == "System.Threading.Thread.Sleep")
  1054. {
  1055. string a = node.ArgumentList.Arguments[0].ToString();
  1056. emit($"await new Promise((r) => setTimeout(r, {a}))");
  1057. return;
  1058. }
  1059. }
  1060. emit(func_name);
  1061. Visit(node.ArgumentList);
  1062. }
  1063. public override void VisitPredefinedType(PredefinedTypeSyntax node)
  1064. {
  1065. string name = node.Keyword.Text;
  1066. name = convert_type(name);
  1067. emit(name);
  1068. }
  1069. public override void VisitArrayRankSpecifier(ArrayRankSpecifierSyntax node)
  1070. {
  1071. emit("[");
  1072. int num = 0;
  1073. foreach (ExpressionSyntax exp in node.Sizes)
  1074. {
  1075. if (num >= 1)
  1076. {
  1077. emit(",");
  1078. }
  1079. Visit(exp);
  1080. num++;
  1081. }
  1082. emit("]");
  1083. }
  1084. public override void VisitConstructorDeclaration(ConstructorDeclarationSyntax node)
  1085. {
  1086. /*foreach (var statement in node.Body.Statements)
  1087. {
  1088. Visit(statement);
  1089. }*/
  1090. }
  1091. public override void VisitArrayType(ArrayTypeSyntax node)
  1092. {
  1093. Visit(node.ElementType);
  1094. foreach (var rank in node.RankSpecifiers)
  1095. {
  1096. Visit(rank);
  1097. }
  1098. }
  1099. public void VisitVariableDeclarator(VariableDeclaratorSyntax node, TypeSyntax type)
  1100. {
  1101. if (lang == TargetLang.TypeScript)
  1102. {
  1103. // if (node.Parent.Parent.Kind() == SyntaxKind.LocalDeclarationStatement)
  1104. {
  1105. emit("let ");
  1106. }
  1107. emit($"{node.Identifier.Text}");
  1108. emit(": ");
  1109. var type_dec = src.Model.GetTypeInfo(type);
  1110. if (type is PredefinedTypeSyntax)
  1111. {
  1112. Visit(type);
  1113. }
  1114. else if (type is ArrayTypeSyntax)
  1115. {
  1116. Visit(type);
  1117. }
  1118. else if (type is IdentifierNameSyntax)
  1119. {
  1120. Visit(type);
  1121. }
  1122. else
  1123. {
  1124. throw new ApplicationException($"VisitVariableDeclarator: {type.GetType().ToString()}");
  1125. }
  1126. if (node.Initializer != null)
  1127. {
  1128. emit(" = ");
  1129. var value = node.Initializer.Value;
  1130. base.Visit(value);
  1131. }
  1132. emit_line(";");
  1133. }
  1134. else
  1135. {
  1136. var type_dec = src.Model.GetTypeInfo(type);
  1137. if (type is PredefinedTypeSyntax)
  1138. {
  1139. Visit(type);
  1140. }
  1141. else if (type is ArrayTypeSyntax)
  1142. {
  1143. Visit(type);
  1144. }
  1145. else if (type is IdentifierNameSyntax)
  1146. {
  1147. Visit(type);
  1148. }
  1149. else
  1150. {
  1151. throw new ApplicationException($"VisitVariableDeclarator: {type.GetType().ToString()}");
  1152. }
  1153. emit($" {node.Identifier.Text}");
  1154. if (node.Initializer != null)
  1155. {
  1156. emit(" = ");
  1157. var value = node.Initializer.Value;
  1158. base.Visit(value);
  1159. }
  1160. emit_line(";");
  1161. }
  1162. }
  1163. public override void VisitVariableDeclaration(VariableDeclarationSyntax node)
  1164. {
  1165. foreach (var v in node.Variables)
  1166. {
  1167. VisitVariableDeclarator(v, node.Type);
  1168. }
  1169. }
  1170. public override void VisitLocalDeclarationStatement(LocalDeclarationStatementSyntax node)
  1171. {
  1172. Visit(node.Declaration);
  1173. }
  1174. public override void VisitFieldDeclaration(FieldDeclarationSyntax node)
  1175. {
  1176. //Visit(node.Declaration);
  1177. }
  1178. public override void VisitBlock(BlockSyntax node)
  1179. {
  1180. emit_line("{");
  1181. current_depth++;
  1182. foreach (var statement in node.Statements)
  1183. {
  1184. Visit(statement);
  1185. }
  1186. current_depth--;
  1187. emit_line("}");
  1188. }
  1189. public override void VisitClassDeclaration(ClassDeclarationSyntax node)
  1190. {
  1191. if (lang == TargetLang.TypeScript)
  1192. {
  1193. base.VisitClassDeclaration(node);
  1194. }
  1195. else
  1196. {
  1197. emit_line($"class {node.Identifier.Text}");
  1198. emit_line("{");
  1199. current_depth++;
  1200. base.VisitClassDeclaration(node);
  1201. current_depth--;
  1202. emit_line("}");
  1203. }
  1204. }
  1205. public override string ToString()
  1206. {
  1207. StringWriter w = new StringWriter();
  1208. this.lines.ForEach(x => w.WriteLine(x));
  1209. if (String.IsNullOrEmpty(this.current_line) == false) w.WriteLine(this.current_line);
  1210. return w.ToString();
  1211. }
  1212. }
  1213. void generate_tests(GeneratedCodeForLang ret)
  1214. {
  1215. var test_class = cs_tests.Root.DescendantNodes().OfType<ClassDeclarationSyntax>().Where(c => c.Identifier.Text == "VPNRPCTest").First();
  1216. CcWalker ts_walker = new CcWalker(cs_tests, TargetLang.TypeScript);
  1217. ts_walker.Visit(test_class);
  1218. ret.TypeScript.Tests.PartList.Add(new GeneratedCodePart() { Seq = 0, Text = ts_walker.ToString() });
  1219. }
  1220. void doc_write_parameters(StringWriter w, RpcTypeInfo type_info)
  1221. {
  1222. List<RpcTypeParameterInfo> plist = new List<RpcTypeParameterInfo>();
  1223. foreach (RpcTypeParameterInfo p in type_info.Params)
  1224. {
  1225. plist.Add(p);
  1226. }
  1227. foreach (string subtype in type_info.SubTypes)
  1228. {
  1229. foreach (RpcTypeParameterInfo p in rpc_type_list[subtype].Params)
  1230. {
  1231. plist.Add(p);
  1232. }
  1233. }
  1234. w.WriteLine("Name | Type | Descrption");
  1235. w.WriteLine("--- | --- | ---");
  1236. foreach (RpcTypeParameterInfo p in plist)
  1237. {
  1238. w.WriteLine($"`{p.Name}` | {p.Type} | {p.Description}");
  1239. }
  1240. }
  1241. void doc_write_function(StringWriter w, RpcInfo rpc)
  1242. {
  1243. string func_summary = rpc.Symbol.GetDocumentStr();
  1244. int index = func_summary.IndexOf(".");
  1245. if (index != -1) func_summary = func_summary.Substring(0, index + 1);
  1246. func_summary = func_summary.TrimEnd('.');
  1247. w.WriteLine($"<a id=\"{rpc.Name.ToLowerInvariant()}\"></a>");
  1248. w.WriteLine($"## \"{rpc.Name}\" RPC API - {func_summary}");
  1249. w.WriteLine("### Description");
  1250. w.WriteLine(rpc.Symbol.GetDocumentStr());
  1251. var model = cs_tests.Model;
  1252. var func = cs_tests.Root.DescendantNodes().OfType<MethodDeclarationSyntax>()
  1253. .Where(f => f.Identifier.Text == "Test_" + rpc.Name)
  1254. .Single();
  1255. var fields = func.DescendantNodes().OfType<InitializerExpressionSyntax>()
  1256. .Where(i => i.Kind() == SyntaxKind.ObjectInitializerExpression)
  1257. .SelectMany(o => o.DescendantNodes().OfType<AssignmentExpressionSyntax>())
  1258. .Where(a => a.Kind() == SyntaxKind.SimpleAssignmentExpression)
  1259. .Select(a => (a.Left as IdentifierNameSyntax));
  1260. foreach (var field in fields)
  1261. {
  1262. string json_name = field.Identifier.Text;
  1263. var sym = model.GetSymbolInfo(field);
  1264. var atts = sym.Symbol.GetAttributes();
  1265. var y = atts.Where(x => x.AttributeClass.Name == "JsonPropertyAttribute").FirstOrDefault();
  1266. if (y != null)
  1267. {
  1268. json_name = y.ConstructorArguments.FirstOrDefault().Value.ToString();
  1269. }
  1270. rpc.InputParamMembers.Add(json_name);
  1271. }
  1272. Type obj_type = Type.GetType("SoftEther.VPNServerRpc." + rpc.TypeName);
  1273. object in_object = Activator.CreateInstance(obj_type);
  1274. object out_object = Activator.CreateInstance(obj_type);
  1275. JsonRpcRequest rpc_in = new JsonRpcRequest() { Method = rpc.Name, Params = in_object, Id = "rpc_call_id", };
  1276. Type rpc_out_type = typeof(JsonRpcResponse<>).MakeGenericType(obj_type);
  1277. var rpc_out = Activator.CreateInstance(rpc_out_type);
  1278. rpc_out_type.GetProperty("Id").SetValue(rpc_out, "rpc_call_id");
  1279. rpc_out_type.GetProperty("Result").SetValue(rpc_out, out_object);
  1280. sample_fill_object(in_object);
  1281. sample_fill_object(out_object);
  1282. JsonSerializerSettings rpc_in_settings = new JsonSerializerSettings()
  1283. {
  1284. MaxDepth = 8,
  1285. NullValueHandling = NullValueHandling.Include,
  1286. ReferenceLoopHandling = ReferenceLoopHandling.Error,
  1287. PreserveReferencesHandling = PreserveReferencesHandling.None,
  1288. ContractResolver = new JSonInputContractResolver(rpc),
  1289. };
  1290. JsonSerializerSettings rpc_out_settings = new JsonSerializerSettings()
  1291. {
  1292. MaxDepth = 8,
  1293. NullValueHandling = NullValueHandling.Include,
  1294. ReferenceLoopHandling = ReferenceLoopHandling.Error,
  1295. PreserveReferencesHandling = PreserveReferencesHandling.None,
  1296. ContractResolver = new JSonOutputContractResolver(rpc),
  1297. };
  1298. string in_str = JsonConvert.SerializeObject(rpc_in, Formatting.Indented, rpc_in_settings);
  1299. string out_str = JsonConvert.SerializeObject(rpc_out, Formatting.Indented, rpc_out_settings);
  1300. w.WriteLine();
  1301. w.WriteLine("### Input JSON-RPC Format");
  1302. w.WriteLine("```json");
  1303. w.WriteLine(in_str);
  1304. w.WriteLine("```");
  1305. w.WriteLine();
  1306. w.WriteLine("### Output JSON-RPC Format");
  1307. w.WriteLine("```json");
  1308. w.WriteLine(out_str);
  1309. w.WriteLine("```");
  1310. w.WriteLine();
  1311. w.WriteLine("### Parameters");
  1312. w.WriteLine();
  1313. doc_write_parameters(w, rpc_type_list[rpc.TypeName]);
  1314. //w.WriteLine("<BR> ");
  1315. w.WriteLine();
  1316. }
  1317. class JSonOutputContractResolver : DefaultContractResolver
  1318. {
  1319. RpcInfo rpc_info;
  1320. public JSonOutputContractResolver(RpcInfo info) : base()
  1321. {
  1322. this.rpc_info = info;
  1323. }
  1324. protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
  1325. {
  1326. if (member.Name == "Error") return null;
  1327. JsonProperty ret = base.CreateProperty(member, memberSerialization);
  1328. return ret;
  1329. }
  1330. }
  1331. class JSonInputConverter : JsonConverter
  1332. {
  1333. RpcInfo rpc_info;
  1334. public JSonInputConverter(RpcInfo info)
  1335. {
  1336. this.rpc_info = info;
  1337. }
  1338. public override bool CanRead => false;
  1339. public override bool CanConvert(Type objectType)
  1340. {
  1341. return true;
  1342. }
  1343. public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
  1344. {
  1345. throw new NotImplementedException();
  1346. }
  1347. public override void WriteJson(JsonWriter w, object value, JsonSerializer serializer)
  1348. {
  1349. JToken t = JToken.FromObject(value);
  1350. List<JProperty> a = new List<JProperty>();
  1351. bool all = false;
  1352. if (rpc_info.Name == "SetHubLog") all = true;
  1353. foreach (var p1 in t.Children<JProperty>())
  1354. {
  1355. foreach (var p2 in p1.Children<JProperty>())
  1356. {
  1357. if (rpc_info.InputParamMembers.Contains(p2.Name) == false) a.Add(p2);
  1358. }
  1359. if (rpc_info.InputParamMembers.Contains(p1.Name) == false) a.Add(p1);
  1360. }
  1361. if (all == false)
  1362. {
  1363. foreach (var p in a)
  1364. {
  1365. try
  1366. {
  1367. p.Remove();
  1368. }
  1369. catch
  1370. {
  1371. }
  1372. }
  1373. }
  1374. t.WriteTo(w);
  1375. }
  1376. }
  1377. class JSonInputContractResolver : DefaultContractResolver
  1378. {
  1379. RpcInfo rpc_info;
  1380. public JSonInputContractResolver(RpcInfo info) : base()
  1381. {
  1382. this.rpc_info = info;
  1383. }
  1384. protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
  1385. {
  1386. JsonProperty ret = base.CreateProperty(member, memberSerialization);
  1387. ret.Converter = new JSonInputConverter(this.rpc_info);
  1388. return ret;
  1389. }
  1390. }
  1391. void sample_fill_object(object o)
  1392. {
  1393. Type t = o.GetType();
  1394. var fields = t.GetFields();
  1395. foreach (var field in fields)
  1396. {
  1397. Type t2 = field.FieldType;
  1398. object v = null;
  1399. if (t2 == typeof(string))
  1400. {
  1401. string tmp = field.Name.ToLowerInvariant();
  1402. if (tmp.EndsWith("_str") || tmp.EndsWith("_utf")) tmp = tmp.Substring(0, tmp.Length - 4);
  1403. if (tmp.EndsWith("_ip"))
  1404. {
  1405. if (tmp.IndexOf("mask", StringComparison.InvariantCultureIgnoreCase) == -1)
  1406. tmp = "192.168.0.1";
  1407. else
  1408. tmp = "255.255.255.255";
  1409. }
  1410. v = tmp;
  1411. }
  1412. else if (t2 == typeof(uint))
  1413. v = (uint)0;
  1414. else if (t2 == typeof(ulong))
  1415. v = (ulong)0;
  1416. else if (t2 == typeof(bool))
  1417. v = (bool)false;
  1418. else if (t2 == typeof(byte[]))
  1419. v = Encoding.UTF8.GetBytes("Hello World");
  1420. else if (t2 == typeof(DateTime))
  1421. v = new DateTime(DateTime.Now.Year + 1, 8, 1, 12, 24, 36, 123);
  1422. else if (t2.IsEnum)
  1423. {
  1424. v = (int)0;
  1425. }
  1426. else if (t2.IsArray)
  1427. {
  1428. if (t2 == typeof(uint[]))
  1429. {
  1430. v = new uint[] { 1, 2, 3 };
  1431. }
  1432. else
  1433. {
  1434. if (t2.GetArrayRank() != 1) throw new ApplicationException("Array rank != 1");
  1435. Type obj_type = t2.GetElementType();
  1436. if (obj_type.IsEnum)
  1437. {
  1438. v = new int[] { 1, 2, 3 };
  1439. }
  1440. else
  1441. {
  1442. int num = 3;
  1443. if (field.Name.IndexOf("single", StringComparison.CurrentCultureIgnoreCase) != -1)
  1444. {
  1445. num = 1;
  1446. }
  1447. object list = Activator.CreateInstance(typeof(List<>).MakeGenericType(obj_type));
  1448. for (int i = 0; i < num; i++)
  1449. {
  1450. object a = Activator.CreateInstance(obj_type);
  1451. sample_fill_object(a);
  1452. list.GetType().GetMethod("Add").Invoke(list, new object[] { a });
  1453. }
  1454. v = list.GetType().GetMethod("ToArray").Invoke(list, new object[] { } );
  1455. }
  1456. }
  1457. }
  1458. else if (t2.Name.StartsWith("Vpn"))
  1459. {
  1460. Type obj_type = Type.GetType("SoftEther.VPNServerRpc." + t2.Name);
  1461. v = Activator.CreateInstance(obj_type);
  1462. sample_fill_object(v);
  1463. }
  1464. else
  1465. {
  1466. throw new ApplicationException($"sample_fill_object: type: {t2.ToString()}");
  1467. }
  1468. field.SetValue(o, v);
  1469. }
  1470. }
  1471. void generate_documents(GeneratedCodeForLang ret)
  1472. {
  1473. StringWriter w = new StringWriter();
  1474. string doc_txt = read_text_resource("doc.txt");
  1475. w.WriteLine(doc_txt);
  1476. w.WriteLine("## Table of contents");
  1477. foreach (RpcInfo rpc in rpc_list.Values)
  1478. {
  1479. string func_summary = rpc.Symbol.GetDocumentStr();
  1480. int index = func_summary.IndexOf(".");
  1481. if (index != -1) func_summary = func_summary.Substring(0, index + 1);
  1482. func_summary = func_summary.TrimEnd('.');
  1483. w.WriteLine($"- [{rpc.Name} - {func_summary}](#{rpc.Name.ToLowerInvariant()})");
  1484. }
  1485. w.WriteLine();
  1486. w.WriteLine("***");
  1487. foreach (RpcInfo rpc in rpc_list.Values)
  1488. {
  1489. if (rpc.Name.IndexOf("Vgs", StringComparison.Ordinal) == -1)
  1490. {
  1491. doc_write_function(w, rpc);
  1492. w.WriteLine("***");
  1493. }
  1494. }
  1495. w.WriteLine($"Automatically generated at {timestamp.ToString("yyyy-MM-dd HH:mm:ss")} by vpnserver-jsonrpc-codegen. ");
  1496. w.WriteLine("Copyright (c) 2014-" + DateTime.Now.Year + " [SoftEther VPN Project](https://www.softether.org/) under the Apache License 2.0. ");
  1497. w.WriteLine();
  1498. ret.DocsRpc = w.ToString();
  1499. }
  1500. public GeneratedCodeForLang GenerateCodes()
  1501. {
  1502. GeneratedCodeForLang ret = new GeneratedCodeForLang();
  1503. generate_stubs(ret);
  1504. generate_tests(ret);
  1505. generate_types(ret);
  1506. generate_documents(ret);
  1507. return ret;
  1508. }
  1509. public void GenerateAndSaveCodes(string output_dir)
  1510. {
  1511. CodeGenUtil.MakeDir(output_dir);
  1512. WriteLine($"GenerateAndSaveCodes(): output_dir = '{output_dir}'");
  1513. WriteLine();
  1514. WriteLine("Generating codes ...");
  1515. GeneratedCodeForLang codes = GenerateCodes();
  1516. WriteLine("Generating codes: done.");
  1517. WriteLine();
  1518. output_docs(codes, output_dir);
  1519. output_csharp(Path.Combine(output_dir, "vpnserver-jsonrpc-client-csharp"));
  1520. output_typescript(codes.TypeScript, Path.Combine(output_dir, "vpnserver-jsonrpc-client-typescript"));
  1521. }
  1522. static Assembly this_assembly = Assembly.GetExecutingAssembly();
  1523. static string read_text_resource(string name)
  1524. {
  1525. var x = this_assembly.GetManifestResourceNames();
  1526. string resourceName = this_assembly.GetManifestResourceNames().Single(str => str.EndsWith(name));
  1527. using (Stream stream = this_assembly.GetManifestResourceStream(resourceName))
  1528. {
  1529. using (StreamReader reader = new StreamReader(stream))
  1530. {
  1531. return reader.ReadToEnd();
  1532. }
  1533. }
  1534. }
  1535. static string read_text_file(string name)
  1536. {
  1537. using (Stream stream = File.OpenRead(name))
  1538. {
  1539. using (StreamReader reader = new StreamReader(stream))
  1540. {
  1541. return reader.ReadToEnd();
  1542. }
  1543. }
  1544. }
  1545. static string replace_strings(string src, params string[] replace_list)
  1546. {
  1547. int i;
  1548. for (i = 0; i < replace_list.Length / 2; i++)
  1549. {
  1550. string s1 = replace_list[i * 2];
  1551. string s2 = replace_list[i * 2 + 1];
  1552. src = src.Replace(s1, s2, StringComparison.InvariantCultureIgnoreCase);
  1553. }
  1554. return src;
  1555. }
  1556. static string normalize_crlf(string src, string crlf)
  1557. {
  1558. StringReader r = new StringReader(src);
  1559. StringWriter w = new StringWriter();
  1560. w.NewLine = crlf;
  1561. while (true)
  1562. {
  1563. string line = r.ReadLine();
  1564. if (line == null) break;
  1565. w.WriteLine(line);
  1566. }
  1567. return w.ToString();
  1568. }
  1569. static void normalize(ref string str, string crlf, params string[] replace_list)
  1570. {
  1571. str = normalize_crlf(replace_strings(str, replace_list), crlf);
  1572. }
  1573. static void save(string path, string body, bool bom)
  1574. {
  1575. string dir_name = Path.GetDirectoryName(path);
  1576. CodeGenUtil.MakeDir(dir_name);
  1577. if (bom)
  1578. File.WriteAllText(path, body, Encoding.UTF8);
  1579. else
  1580. File.WriteAllText(path, body);
  1581. }
  1582. DateTime timestamp = DateTime.Now;
  1583. void output_docs(GeneratedCodeForLang c, string output_dir)
  1584. {
  1585. CodeGenUtil.MakeDir(output_dir);
  1586. save(Path.Combine(output_dir, "README.md"), c.DocsRpc, true);
  1587. var pipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
  1588. string md_html_body = Markdown.ToHtml(c.DocsRpc, pipeline);
  1589. string html = read_text_resource("md_html.html");
  1590. string[] replace_list =
  1591. {
  1592. "__BODY__", md_html_body,
  1593. };
  1594. normalize(ref html, "\r\n", replace_list);
  1595. save(Path.Combine(output_dir, "README.html"), html, true);
  1596. save(Path.Combine(CodeGenUtil.OutputDir_HamCore, "vpnserver_api_doc.html"), html, true);
  1597. }
  1598. void output_typescript(GeneratedCode c, string output_dir)
  1599. {
  1600. CodeGenUtil.MakeDir(output_dir);
  1601. string ts_rpc = read_text_resource("ts_rpc.txt");
  1602. string ts_test = read_text_resource("ts_test.txt");
  1603. string[] replace_list =
  1604. {
  1605. "__YEAR__", timestamp.Year.ToString(),
  1606. "__TESTS__", c.Tests.ToString(),
  1607. "__STUBS__", c.Stubs.ToString(),
  1608. "__TYPES__", c.Types.ToString(),
  1609. "__TIMESTAMP__", timestamp.ToString("yyyy-MM-dd HH:mm:ss"),
  1610. };
  1611. normalize(ref ts_rpc, "\n", replace_list);
  1612. normalize(ref ts_test, "\n", replace_list);
  1613. save(Path.Combine(output_dir, "vpnrpc.ts"), ts_rpc, true);
  1614. save(Path.Combine(output_dir, "sample.ts"), ts_test, true);
  1615. save(Path.Combine(output_dir + "/../vpnserver-jsonrpc-client-nodejs-package/src/", "vpnrpc.ts"), ts_rpc, true);
  1616. save(Path.Combine(output_dir + "/../vpnserver-jsonrpc-client-nodejs-package/src/", "sample.ts"), ts_test, true);
  1617. }
  1618. void output_csharp(string output_dir)
  1619. {
  1620. CodeGenUtil.MakeDir(output_dir);
  1621. string cs_proj = read_text_resource("cs_proj.txt");
  1622. string cs_sln = read_text_resource("cs_sln.txt");
  1623. string cs_main = read_text_resource("cs_main.txt");
  1624. string cs_code_jsonrpc = read_text_file(Path.Combine(CodeGenUtil.ProjectDir,
  1625. @"VpnServerRpc/JsonRpc.cs"));
  1626. string cs_code_vpnserver_rpc = read_text_file(Path.Combine(CodeGenUtil.ProjectDir,
  1627. @"VpnServerRpc/VPNServerRpc.cs"));
  1628. string cs_code_vpnserver_rpc_types = read_text_file(Path.Combine(CodeGenUtil.ProjectDir,
  1629. @"VpnServerRpc/VPNServerRpcTypes.cs"));
  1630. string cs_code_vpnserver_rpc_test = read_text_file(Path.Combine(CodeGenUtil.ProjectDir,
  1631. @"VpnServerRpcTest/VpnServerRpcTest.cs"));
  1632. string[] replace_list =
  1633. {
  1634. "__YEAR__", timestamp.Year.ToString(),
  1635. "__TIMESTAMP__", timestamp.ToString("yyyy-MM-dd HH:mm:ss"),
  1636. };
  1637. normalize(ref cs_main, "\r\n", replace_list);
  1638. normalize(ref cs_proj, "\r\n", replace_list);
  1639. normalize(ref cs_sln, "\r\n", replace_list);
  1640. normalize(ref cs_code_jsonrpc, "\r\n", replace_list);
  1641. normalize(ref cs_code_vpnserver_rpc, "\r\n", replace_list);
  1642. normalize(ref cs_code_vpnserver_rpc_types, "\r\n", replace_list);
  1643. normalize(ref cs_code_vpnserver_rpc_test, "\r\n", replace_list);
  1644. save(Path.Combine(output_dir, "vpnserver-jsonrpc-client-csharp.csproj"),
  1645. cs_proj, true);
  1646. save(Path.Combine(output_dir, "vpnserver-jsonrpc-client-csharp.sln"),
  1647. cs_sln, true);
  1648. save(Path.Combine(output_dir, @"rpc-stubs\JsonRpc.cs"),
  1649. cs_code_jsonrpc, true);
  1650. save(Path.Combine(output_dir, @"rpc-stubs\VPNServerRpc.cs"),
  1651. cs_code_vpnserver_rpc, true);
  1652. save(Path.Combine(output_dir, @"rpc-stubs\VPNServerRpcTypes.cs"),
  1653. cs_code_vpnserver_rpc_types, true);
  1654. save(Path.Combine(output_dir, @"sample\VpnServerRpcTest.cs"),
  1655. cs_code_vpnserver_rpc_test, true);
  1656. save(Path.Combine(output_dir, @"sample\Main.cs"),
  1657. cs_main, true);
  1658. }
  1659. public void Test()
  1660. {
  1661. GeneratedCodeForLang ret = GenerateCodes();
  1662. Console.WriteLine(ret.TypeScript.ToString());
  1663. return;
  1664. var model = cs_types.Model;
  1665. var type_classes = cs_types.Root.DescendantNodes()
  1666. .OfType<ClassDeclarationSyntax>();
  1667. foreach (ClassDeclarationSyntax v in type_classes)
  1668. {
  1669. WriteLine(v.Identifier.Text);
  1670. var info = model.GetDeclaredSymbol(v);
  1671. var x = info.GetMembers();
  1672. foreach (var y in x)
  1673. {
  1674. WriteLine(y.Name);
  1675. }
  1676. break;
  1677. }
  1678. Console.WriteLine();
  1679. }
  1680. }
  1681. }