#include "ScriptEngine.hpp" #include "vultc.h" #include /* The Vult engine relies on both QuickJS and LuaJIT. * * The compiler is written in OCaml but converted to JavaScript. The JavaScript * code is embedded as a string and executed by the QuickJs engine. The Vult * compiler generates Lua code that is executed by the LuaJIT engine. */ // Special version of createScriptEngine that only creates Lua engines ScriptEngine* createLuaEngine() { auto it = scriptEngineFactories.find("lua"); if (it == scriptEngineFactories.end()) return NULL; return it->second->createScriptEngine(); } struct VultEngine : ScriptEngine { // used to run the lua generated code ScriptEngine* luaEngine; // used to run the Vult compiler JSRuntime* rt = NULL; JSContext* ctx = NULL; VultEngine() { rt = JS_NewRuntime(); // Create QuickJS context ctx = JS_NewContext(rt); if (!ctx) { display("Could not create QuickJS context"); return; } JSValue global_obj = JS_GetGlobalObject(ctx); // Load the Vult compiler code JSValue val = JS_Eval(ctx, (const char*)vultc_h, vultc_h_size, "vultc.js", 0); if (JS_IsException(val)) { display("Error loading the Vult compiler"); JS_FreeValue(ctx, val); JS_FreeValue(ctx, global_obj); return; } } ~VultEngine() { if (ctx) { JS_FreeContext(ctx); } if (rt) { JS_FreeRuntime(rt); } } std::string getEngineName() override { return "Vult"; } int run(const std::string& path, const std::string& script) override { display("Loading..."); JSValue global_obj = JS_GetGlobalObject(ctx); // Put the script text in the 'code' variable JSValue code = JS_NewString(ctx, script.c_str()); JS_SetPropertyStr(ctx, global_obj, "code", code); // Put the script path in 'file' variable JSValue file = JS_NewString(ctx, path.c_str()); JS_SetPropertyStr(ctx, global_obj, "file", file); display("Compiling..."); // Call the Vult compiler to generate Lua code static const std::string testVult = R"( var result = vult.generateLua([{ file:file, code:code}],{ output:'Engine', template:'vcv-prototype'});)"; JSValue compile = JS_Eval(ctx, testVult.c_str(), testVult.size(), "Compile", 0); JS_FreeValue(ctx, code); JS_FreeValue(ctx, file); // If there are any internal errors, the execution could fail if (JS_IsException(compile)) { display("Fatal error in the Vult compiler"); JS_FreeValue(ctx, global_obj); return -1; } // Retrive the variable 'result' JSValue result = JS_GetPropertyStr(ctx, global_obj, "result"); // Get the first element of the 'result' array JSValue first = JS_GetPropertyUint32(ctx, result, 0); // Try to get the 'msg' field which is only present in error messages JSValue msg = JS_GetPropertyStr(ctx, first, "msg"); // Display the error if any if (!JS_IsUndefined(msg)) { const char* text = JS_ToCString(ctx, msg); const char* row = JS_ToCString(ctx, JS_GetPropertyStr(ctx, first, "line")); const char* col = JS_ToCString(ctx, JS_GetPropertyStr(ctx, first, "col")); // Compose the error message std::stringstream error; error << "line:" << row << ":" << col << ": " << text; WARN("Vult Error: %s", error.str().c_str()); display(error.str().c_str()); JS_FreeValue(ctx, result); JS_FreeValue(ctx, first); JS_FreeValue(ctx, msg); return -1; } // In case of no error, retrieve the generated code JSValue luacode = JS_GetPropertyStr(ctx, first, "code"); std::string luacode_str(JS_ToCString(ctx, luacode)); //WARN("Generated Code: %s", luacode_str.c_str()); luaEngine = createLuaEngine(); if (!luaEngine) { WARN("Could not create a Lua script engine"); return -1; } luaEngine->module = this->module; display("Running..."); JS_FreeValue(ctx, luacode); JS_FreeValue(ctx, first); JS_FreeValue(ctx, msg); JS_FreeValue(ctx, msg); JS_FreeValue(ctx, global_obj); return luaEngine->run(path, luacode_str); } int process() override { if (!luaEngine) return -1; return luaEngine->process(); } }; __attribute__((constructor(1000))) static void constructor() { addScriptEngine("vult"); }