CSModuleWriter.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. //
  2. // Copyright (c) 2014-2016 THUNDERBEAST GAMES LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to deal
  6. // in the Software without restriction, including without limitation the rights
  7. // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. // copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. // THE SOFTWARE.
  21. //
  22. #include <Atomic/IO/File.h>
  23. #include <Atomic/IO/FileSystem.h>
  24. #include "../JSBind.h"
  25. #include "../JSBPackage.h"
  26. #include "../JSBModule.h"
  27. #include "../JSBEnum.h"
  28. #include "../JSBClass.h"
  29. #include "../JSBFunction.h"
  30. #include "../JSBEvent.h"
  31. #include "CSTypeHelper.h"
  32. #include "CSClassWriter.h"
  33. #include "CSModuleWriter.h"
  34. namespace ToolCore
  35. {
  36. CSModuleWriter::CSModuleWriter(JSBModule *module) : JSBModuleWriter(module)
  37. {
  38. }
  39. void CSModuleWriter::WriteIncludes(String& source)
  40. {
  41. Vector<String>& includes = module_->includes_;
  42. for (unsigned i = 0; i < includes.Size(); i++)
  43. {
  44. if (includes[i].StartsWith("<"))
  45. source.AppendWithFormat("#include %s\n", includes[i].CString());
  46. else
  47. source.AppendWithFormat("#include \"%s\"\n", includes[i].CString());
  48. }
  49. Vector<JSBHeader*> allheaders;
  50. HashMap<StringHash, SharedPtr<JSBEnum> >::Iterator eitr = module_->enums_.Begin();
  51. while (eitr != module_->enums_.End())
  52. {
  53. allheaders.Push(eitr->second_->GetHeader());
  54. eitr++;
  55. }
  56. Vector<SharedPtr<JSBClass>> classes = module_->GetClasses();
  57. Vector<SharedPtr<JSBClass>>::Iterator citr = classes.Begin();
  58. while (citr != classes.End())
  59. {
  60. allheaders.Push((*citr)->GetHeader());
  61. citr++;
  62. }
  63. Vector<JSBHeader*> included;
  64. for (unsigned i = 0; i < allheaders.Size(); i++)
  65. {
  66. JSBHeader* header = allheaders.At(i);
  67. if (included.Contains(header))
  68. continue;
  69. String headerPath = GetPath(header->GetFilePath());
  70. String headerfile = GetFileNameAndExtension(header->GetFilePath());
  71. JSBind* jsbind = header->GetSubsystem<JSBind>();
  72. headerPath.Replace(jsbind->GetSourceRootFolder() + "Source/", "");
  73. source.AppendWithFormat("#include <%s%s>\n", headerPath.CString(), headerfile.CString());
  74. included.Push(header);
  75. }
  76. source += "\n#include <Atomic/Script/ScriptVector.h>\n";
  77. source += ToString("\n#include \"CSPackage%s.h\"\n", module_->GetPackage()->GetName().CString());
  78. }
  79. void CSModuleWriter::GenerateNativeSource()
  80. {
  81. String source = "// This file was autogenerated by JSBind, changes will be lost\n";
  82. String moduleGuard = module_->GetModuleDefineGuard();
  83. if (moduleGuard.Length())
  84. {
  85. source += ToString("\n%s\n", moduleGuard.CString());
  86. }
  87. source += "#ifdef ATOMIC_PLATFORM_WINDOWS\n";
  88. source += "#pragma warning(disable: 4244) // possible loss of data\n";
  89. source += "#define ATOMIC_EXPORT_API __declspec(dllexport)\n";
  90. source += "#else\n";
  91. source += "#define ATOMIC_EXPORT_API\n";
  92. source += "#endif\n";
  93. if (module_->Requires("3D"))
  94. {
  95. source += "#ifdef ATOMIC_3D\n";
  96. }
  97. WriteIncludes(source);
  98. // NOTE: We include Deserializer/Serializer here as they are interfaces
  99. // If additional interfaces are introduced, consider generalizing this
  100. source += "\n#include <Atomic/IO/Deserializer.h>\n";
  101. source += "#include <Atomic/IO/Serializer.h>\n";
  102. source += "#include <AtomicNET/NETNative/NETCore.h>\n";
  103. String ns = module_->GetPackage()->GetNamespace();
  104. source += "\n\nusing namespace " + ns + ";\n\n";
  105. source += "\n\nextern \"C\" \n{\n \n";
  106. source += "// Begin Classes\n";
  107. Vector<SharedPtr<JSBClass>> classes = module_->GetClasses();
  108. for (unsigned i = 0; i < classes.Size(); i++)
  109. {
  110. String classGuard = module_->GetClassDefineGuard(classes[i]->GetNativeName());
  111. if (classGuard.Length())
  112. {
  113. source += ToString("\n%s\n\n", classGuard.CString());
  114. }
  115. CSClassWriter clsWriter(classes[i]);
  116. clsWriter.GenerateNativeSource(source);
  117. if (classGuard.Length())
  118. {
  119. source += "#endif\n\n";
  120. }
  121. }
  122. source += "// End Classes\n\n";
  123. // end Atomic namespace
  124. source += "\n}\n";
  125. if (module_->Requires("3D"))
  126. {
  127. source += "#endif //ATOMIC_3D\n";
  128. }
  129. if (moduleGuard.Length())
  130. {
  131. source += "\n#endif\n";
  132. }
  133. JSBind* jsbind = module_->GetSubsystem<JSBind>();
  134. String filepath = jsbind->GetDestNativeFolder() + "/CSModule" + module_->name_ + ".cpp";
  135. File file(module_->GetContext());
  136. file.Open(filepath, FILE_WRITE);
  137. file.Write(source.CString(), source.Length());
  138. file.Close();
  139. }
  140. String CSModuleWriter::GetManagedPrimitiveType(JSBPrimitiveType* ptype)
  141. {
  142. if (ptype->kind_ == JSBPrimitiveType::Bool)
  143. return "bool";
  144. if (ptype->kind_ == JSBPrimitiveType::Int && ptype->isUnsigned_)
  145. return "uint";
  146. else if (ptype->kind_ == JSBPrimitiveType::Int)
  147. return "int";
  148. if (ptype->kind_ == JSBPrimitiveType::Float)
  149. return "float";
  150. if (ptype->kind_ == JSBPrimitiveType::Char)
  151. return "string"; // it technically should return "char", but the
  152. // intent is really to support a string constant
  153. return "int";
  154. }
  155. void CSModuleWriter::GenerateManagedClasses(String& source)
  156. {
  157. // get All classes and interfaces
  158. Vector<SharedPtr<JSBClass>> classes = module_->GetClasses(true);
  159. for (unsigned i = 0; i < classes.Size(); i++)
  160. {
  161. JSBClass* klass = classes.At(i);
  162. if (klass->IsNumberArray())
  163. continue;
  164. CSClassWriter clsWriter(klass);
  165. clsWriter.GenerateManagedSource(source);
  166. }
  167. }
  168. void CSModuleWriter::GenerateManagedEnumsAndConstants(String& source)
  169. {
  170. Vector<SharedPtr<JSBEnum>> enums = module_->enums_.Values();
  171. Indent();
  172. for (unsigned i = 0; i < enums.Size(); i++)
  173. {
  174. JSBEnum* jenum = enums[i];
  175. source += "\n";
  176. String line = "public enum " + jenum->GetName() + "\n";
  177. source += IndentLine(line);
  178. source += IndentLine("{\n");
  179. HashMap<String, String>& values = jenum->GetValues();
  180. HashMap<String, String>::ConstIterator itr = values.Begin();
  181. Indent();
  182. while (itr != values.End())
  183. {
  184. String name = (*itr).first_;
  185. String value = (*itr).second_;
  186. // BodyType2D enum order is assigned in RigidBody2D.h in incorrect order
  187. if (jenum->GetName() == "BodyType2D")
  188. {
  189. if (name == "BT_STATIC")
  190. value = "0";
  191. else if (name == "BT_KINEMATIC")
  192. value = "1";
  193. else if (name == "BT_DYNAMIC")
  194. value = "2";
  195. }
  196. if (value.Length())
  197. {
  198. line = name + " = " + value;
  199. }
  200. else
  201. {
  202. line = name;
  203. }
  204. itr++;
  205. if (itr != values.End())
  206. line += ",";
  207. line += "\n";
  208. source += IndentLine(line);
  209. }
  210. Dedent();
  211. source += IndentLine("}\n");
  212. }
  213. // constants
  214. HashMap<String, JSBModule::Constant>& constants = module_->GetConstants();
  215. if (constants.Size())
  216. {
  217. source += "\n";
  218. String line = "public static partial class Constants\n";
  219. source += IndentLine(line);
  220. source += IndentLine("{\n");
  221. const Vector<String>& constantsName = constants.Keys();
  222. Indent();
  223. for (unsigned i = 0; i < constantsName.Size(); i++)
  224. {
  225. const String& cname = constantsName.At(i);
  226. JSBModule::Constant& constant = constants[cname];
  227. String managedType = GetManagedPrimitiveType(constant.type);
  228. String value = constant.value;
  229. if (!value.Length())
  230. continue;
  231. //static const unsigned M_MIN_UNSIGNED = 0x00000000;
  232. // /static const unsigned M_MAX_UNSIGNED = 0xffffffff;
  233. if (cname == "M_MIN_INT")
  234. value = "int.MinValue";
  235. if (cname == "M_INFINITY")
  236. value = "float.MaxValue";
  237. if (value == "M_MAX_UNSIGNED")
  238. value = "0xffffffff";
  239. // Input stuff
  240. if (module_->GetName() == "Input")
  241. {
  242. if (cname.StartsWith("KEY_"))
  243. {
  244. if (value.Length() == 1 && (IsAlpha(value[0]) || IsDigit(value[0])))
  245. value = "'" + value + "'";
  246. }
  247. // https://raw.githubusercontent.com/flibitijibibo/SDL2-CS/master/src/SDL2.cs
  248. if (value.StartsWith("SDL_BUTTON_") || value.StartsWith("SDL_HAT_"))
  249. {
  250. value = "(int) SDL." + value;
  251. }
  252. else if (value.StartsWith("SDLK_"))
  253. {
  254. value = "(int) SDL.SDL_Keycode." + value;
  255. }
  256. else if (value.StartsWith("SDL_SCANCODE_"))
  257. {
  258. value = "(int) SDL.SDL_Scancode." + value;
  259. }
  260. else if (value.StartsWith("SDL_CONTROLLER_BUTTON_"))
  261. {
  262. value = "(int) SDL.SDL_GameControllerButton." + value;
  263. }
  264. else if (value.StartsWith("SDL_CONTROLLER_AXIS_"))
  265. {
  266. value = "(int) SDL.SDL_GameControllerAxis." + value;
  267. }
  268. }
  269. String line = "public const " + managedType + " " + cname + " = ";
  270. if (managedType == "string")
  271. line = line + "\"" + value + "\"";
  272. else line += value;
  273. if (managedType == "float" && !line.EndsWith("f") && IsDigit(line[line.Length()-1]))
  274. line += "f";
  275. line += ";\n";
  276. source += IndentLine(line);
  277. }
  278. Dedent();
  279. source += "\n";
  280. line = "}\n";
  281. source += IndentLine(line);
  282. }
  283. source += "\n";
  284. Dedent();
  285. }
  286. void CSModuleWriter::GenerateManagedNativeEvents(String& sourceOut)
  287. {
  288. Indent();
  289. String source;
  290. const Vector<SharedPtr<JSBEvent>>& events = module_->GetEvents();
  291. for (unsigned i = 0; i < events.Size(); i++)
  292. {
  293. JSBEvent* event = events[i];
  294. const String& eventID = event->GetEventID();
  295. String line = ToString("public partial class %s : NativeEventData\n", event->GetScriptEventName().CString());
  296. source += IndentLine(line);
  297. source += IndentLine("{\n\n");
  298. Indent();
  299. // parameters
  300. const Vector<JSBEvent::EventParam>& params = event->GetParameters();
  301. for (unsigned j = 0; j < params.Size(); j++)
  302. {
  303. const JSBEvent::EventParam& p = params[j];
  304. JSBClass* cls = JSBPackage::GetClassAllPackages(p.typeInfo_);
  305. String typeName = p.typeInfo_;
  306. String enumTypeName = p.enumTypeName_;
  307. if (!cls)
  308. typeName = typeName.ToLower();
  309. else
  310. typeName = cls->GetName();
  311. if (typeName == "int" || typeName == "float" ||
  312. typeName == "bool" || typeName == "string" || typeName == "enum" || cls)
  313. {
  314. bool isEnum = false;
  315. if (typeName == "enum")
  316. {
  317. isEnum = true;
  318. if (enumTypeName.Length())
  319. typeName = enumTypeName;
  320. else
  321. typeName = "int";
  322. }
  323. line = "public " + typeName + " " + p.paramName_ + "\n";
  324. source += IndentLine(line);
  325. source += IndentLine("{\n");
  326. Indent();
  327. source += IndentLine("get\n");
  328. source += IndentLine("{\n");
  329. Indent();
  330. line = "return ";
  331. if (typeName == "int")
  332. line += "scriptMap.GetInt";
  333. else if (isEnum)
  334. {
  335. line += ToString("(%s) scriptMap.GetInt", typeName.CString());
  336. }
  337. else if (typeName == "float")
  338. line += "scriptMap.GetFloat";
  339. else if (typeName == "bool")
  340. line += "scriptMap.GetBool";
  341. else if (typeName == "string")
  342. line += "scriptMap.GetString";
  343. else if (cls)
  344. {
  345. if (typeName == "Vector3" || typeName == "IntVector2")
  346. {
  347. line += "scriptMap.Get" + typeName;
  348. }
  349. else
  350. {
  351. line += ToString("scriptMap.GetPtr<%s>", cls->GetName().CString());
  352. }
  353. }
  354. line += ToString("(\"%s\");\n", p.paramName_.CString());
  355. source += IndentLine(line);
  356. Dedent();
  357. source += IndentLine("}\n");
  358. Dedent();
  359. source += IndentLine("}\n\n");
  360. }
  361. }
  362. Dedent();
  363. source += IndentLine("}\n\n");
  364. }
  365. sourceOut += source;
  366. Dedent();
  367. }
  368. void CSModuleWriter::GenerateManagedModuleClass(String& sourceOut)
  369. {
  370. Indent();
  371. String source;
  372. String line = ToString("public static partial class %sModule\n", module_->GetName().CString());
  373. source += IndentLine(line);
  374. source += IndentLine("{\n");
  375. Indent();
  376. source += IndentLine("public static void Initialize()\n");
  377. source += IndentLine("{\n");
  378. Indent();
  379. Vector<SharedPtr<JSBClass>> classes = module_->GetClasses();
  380. for (unsigned i = 0; i < classes.Size(); i++)
  381. {
  382. JSBClass* klass = classes.At(i);
  383. JSBPackage* package = module_->GetPackage();
  384. if (klass->IsNumberArray() || klass->IsAbstract())
  385. continue;
  386. const char* className = klass->GetName().CString();
  387. String classGuard = module_->GetClassDefineGuard(classes[i]->GetNativeName(), "csharp");
  388. if (classGuard.Length())
  389. {
  390. source += ToString("\n%s\n", classGuard.CString());
  391. }
  392. line = ToString("new NativeType(%s.csb_%s_%s_GetClassIDStatic (), ", className, package->GetName().CString(), className);
  393. line += ToString("typeof(%s), (IntPtr x) => { return new %s (x); } );\n",className, className);
  394. source += IndentLine(line);
  395. if (classGuard.Length())
  396. {
  397. source += ToString("#endif\n", classGuard.CString());
  398. }
  399. }
  400. source += "\n";
  401. // Native Events
  402. const Vector<SharedPtr<JSBEvent>>& events = module_->GetEvents();
  403. for (unsigned i = 0; i < events.Size(); i++)
  404. {
  405. JSBEvent* event = events[i];
  406. line = ToString("NativeEvents.RegisterEventID<%s>(\"%s\");\n", event->GetScriptEventName().CString(), event->GetEventName().CString());
  407. source += IndentLine(line);
  408. }
  409. Dedent();
  410. source += IndentLine("}\n");
  411. Dedent();
  412. source += IndentLine("}\n");
  413. sourceOut += source;
  414. Dedent();
  415. }
  416. void CSModuleWriter::GenerateManagedSource()
  417. {
  418. String source = "// Autogenerated";
  419. String moduleName = module_->GetPackage()->GetNamespace();
  420. source += "\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.InteropServices;\nusing static System.Reflection.IntrospectionExtensions;\n";
  421. if (moduleName == "Atomic")
  422. moduleName = "AtomicEngine";
  423. if (moduleName != "AtomicEngine")
  424. {
  425. source += "using AtomicEngine;\n";
  426. }
  427. source += "\n\n";
  428. source += "namespace " + moduleName + "\n";
  429. source += "{\n";
  430. GenerateManagedEnumsAndConstants(source);
  431. GenerateManagedNativeEvents(source);
  432. GenerateManagedModuleClass(source);
  433. GenerateManagedClasses(source);
  434. source += "}\n";
  435. JSBind* jsbind = module_->GetSubsystem<JSBind>();
  436. String filepath = jsbind->GetDestScriptFolder() + "/CSModule" + module_->name_ + ".cs";
  437. File file(module_->GetContext());
  438. file.Open(filepath, FILE_WRITE);
  439. file.Write(source.CString(), source.Length());
  440. file.Close();
  441. }
  442. void CSModuleWriter::GenerateSource()
  443. {
  444. GenerateNativeSource();
  445. GenerateManagedSource();
  446. }
  447. }