You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

155 lines
4.1KB

  1. #include "ScriptEngine.hpp"
  2. #include "vultc.h"
  3. #include <quickjs/quickjs.h>
  4. /* The Vult engine relies on both QuickJS and LuaJIT.
  5. *
  6. * The compiler is written in OCaml but converted to JavaScript. The JavaScript
  7. * code is embedded as a string and executed by the QuickJs engine. The Vult
  8. * compiler generates Lua code that is executed by the LuaJIT engine.
  9. */
  10. // Special version of createScriptEngine that only creates Lua engines
  11. ScriptEngine* createLuaEngine() {
  12. auto it = scriptEngineFactories.find(".lua");
  13. if (it == scriptEngineFactories.end())
  14. return NULL;
  15. return it->second->createScriptEngine();
  16. }
  17. struct VultEngine : ScriptEngine {
  18. // used to run the lua generated code
  19. ScriptEngine* luaEngine;
  20. // used to run the Vult compiler
  21. JSRuntime* rt = NULL;
  22. JSContext* ctx = NULL;
  23. VultEngine() {
  24. rt = JS_NewRuntime();
  25. // Create QuickJS context
  26. ctx = JS_NewContext(rt);
  27. if (!ctx) {
  28. display("Could not create QuickJS context");
  29. return;
  30. }
  31. JSValue global_obj = JS_GetGlobalObject(ctx);
  32. // Load the Vult compiler code
  33. JSValue val =
  34. JS_Eval(ctx, (const char*)vultc_h, vultc_h_size, "vultc.js", 0);
  35. if (JS_IsException(val)) {
  36. display("Error loading the Vult compiler");
  37. JS_FreeValue(ctx, val);
  38. JS_FreeValue(ctx, global_obj);
  39. return;
  40. }
  41. }
  42. ~VultEngine() {
  43. if (ctx) {
  44. JS_FreeContext(ctx);
  45. }
  46. if (rt) {
  47. JS_FreeRuntime(rt);
  48. }
  49. }
  50. std::string getEngineName() override {
  51. return "Vult";
  52. }
  53. int run(const std::string& path, const std::string& script) override {
  54. display("Loading...");
  55. JSValue global_obj = JS_GetGlobalObject(ctx);
  56. // Put the script text in the 'code' variable
  57. JSValue code = JS_NewString(ctx, script.c_str());
  58. JS_SetPropertyStr(ctx, global_obj, "code", code);
  59. // Put the script path in 'file' variable
  60. JSValue file = JS_NewString(ctx, path.c_str());
  61. JS_SetPropertyStr(ctx, global_obj, "file", file);
  62. display("Compiling...");
  63. // Call the Vult compiler to generate Lua code
  64. static const std::string testVult = R"(
  65. var result = vult.generateLua([{ file:file, code:code}],{ output:'Engine', template:'vcv-prototype'});)";
  66. JSValue compile =
  67. JS_Eval(ctx, testVult.c_str(), testVult.size(), "Compile", 0);
  68. JS_FreeValue(ctx, code);
  69. JS_FreeValue(ctx, file);
  70. // If there are any internal errors, the execution could fail
  71. if (JS_IsException(compile)) {
  72. display("Fatal error in the Vult compiler");
  73. JS_FreeValue(ctx, global_obj);
  74. return -1;
  75. }
  76. // Retrive the variable 'result'
  77. JSValue result = JS_GetPropertyStr(ctx, global_obj, "result");
  78. // Get the first element of the 'result' array
  79. JSValue first = JS_GetPropertyUint32(ctx, result, 0);
  80. // Try to get the 'msg' field which is only present in error messages
  81. JSValue msg = JS_GetPropertyStr(ctx, first, "msg");
  82. // Display the error if any
  83. if (!JS_IsUndefined(msg)) {
  84. const char* text = JS_ToCString(ctx, msg);
  85. const char* row =
  86. JS_ToCString(ctx, JS_GetPropertyStr(ctx, first, "line"));
  87. const char* col = JS_ToCString(ctx, JS_GetPropertyStr(ctx, first, "col"));
  88. // Compose the error message
  89. std::stringstream error;
  90. error << "line:" << row << ":" << col << ": " << text;
  91. WARN("Vult Error: %s", error.str().c_str());
  92. display(error.str().c_str());
  93. JS_FreeValue(ctx, result);
  94. JS_FreeValue(ctx, first);
  95. JS_FreeValue(ctx, msg);
  96. return -1;
  97. }
  98. // In case of no error, retrieve the generated code
  99. JSValue luacode = JS_GetPropertyStr(ctx, first, "code");
  100. std::string luacode_str(JS_ToCString(ctx, luacode));
  101. WARN("Generated Code: %s", luacode_str.c_str());
  102. luaEngine = createLuaEngine();
  103. if (!luaEngine) {
  104. WARN("Could not create a Lua script engine");
  105. return -1;
  106. }
  107. luaEngine->module = this->module;
  108. display("Running...");
  109. JS_FreeValue(ctx, luacode);
  110. JS_FreeValue(ctx, first);
  111. JS_FreeValue(ctx, msg);
  112. JS_FreeValue(ctx, msg);
  113. JS_FreeValue(ctx, global_obj);
  114. return luaEngine->run(path, luacode_str);
  115. }
  116. int process() override {
  117. if (!luaEngine)
  118. return -1;
  119. return luaEngine->process();
  120. }
  121. };
  122. __attribute__((constructor(1000)))
  123. static void constructor() {
  124. addScriptEngine<VultEngine>(".vult");
  125. }