diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4f1b3ed5..39d93f48 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -9,6 +9,7 @@ on: - "craftos2-lua/**" - "craftos2-lua" - "resources/CraftOSTest.lua" + - "resources/CCT-Tests.patch" pull_request: paths: - "src/**" @@ -26,7 +27,7 @@ jobs: steps: - uses: actions/checkout@v1 - name: Download ROM - run: sudo git clone https://github.com/MCJack123/craftos2-rom /usr/local/share/craftos + run: sudo git clone --branch lua-5.2 https://github.com/MCJack123/craftos2-rom /usr/local/share/craftos - name: Install dependencies run: | sudo apt update @@ -54,7 +55,7 @@ jobs: steps: - uses: actions/checkout@v1 - name: Download ROM - run: sudo git clone https://github.com/MCJack123/craftos2-rom /usr/local/share/craftos + run: sudo git clone --branch lua-5.2 https://github.com/MCJack123/craftos2-rom /usr/local/share/craftos - name: Install dependencies run: | sudo apt update @@ -87,7 +88,7 @@ jobs: sudo apt install -y libsdl2-dev libsdl2-mixer-dev libhpdf-dev libpng++-dev libwebp-dev libpoco-dev libncurses5-dev nodejs - name: Build standalone ROM run: | - git clone https://github.com/MCJack123/craftos2-rom + git clone --branch lua-5.2 https://github.com/MCJack123/craftos2-rom cd craftos2-rom node ../resources/packStandaloneROM.js cd .. @@ -115,8 +116,8 @@ jobs: - uses: actions/checkout@v1 - name: Download ROM & CC:T run: | - sudo git clone https://github.com/MCJack123/craftos2-rom /usr/local/share/craftos - git clone --branch v1.19.4-1.108.0 https://github.com/SquidDev-CC/CC-Tweaked ../CC-Tweaked + sudo git clone --branch lua-5.2 https://github.com/MCJack123/craftos2-rom /usr/local/share/craftos + git clone --branch v1.20.1-1.109.0 https://github.com/cc-tweaked/CC-Tweaked ../CC-Tweaked patch -p1 -d ../CC-Tweaked < resources/CCT-Tests.patch - name: Install dependencies run: | @@ -145,7 +146,7 @@ jobs: steps: - uses: actions/checkout@v1 - name: Download ROM - run: git clone https://github.com/MCJack123/craftos2-rom "C:\Program Files\CraftOS-PC" + run: git clone --branch lua-5.2 https://github.com/MCJack123/craftos2-rom "C:\Program Files\CraftOS-PC" - name: Restore vcpkg cache uses: lukka/run-vcpkg@v10 with: @@ -188,8 +189,8 @@ jobs: copy x64\Release\CraftOS-PC.pdb CraftOS-PC.pdb copy x64\ReleaseC\CraftOS-PC.exe CraftOS-PC_console.exe copy x64\ReleaseC\CraftOS-PC.pdb CraftOS-PC_console.pdb - copy craftos2-lua\src\lua51.dll lua51.dll - copy craftos2-lua\src\lua51.pdb lua51.pdb + copy craftos2-lua\src\lua52.dll lua52.dll + copy craftos2-lua\src\lua52.pdb lua52.pdb # Remove buildtrees that kill the cache Remove-Item vcpkg\buildtrees\* -Force -Recurse -ErrorAction SilentlyContinue - name: Run CraftOSTest @@ -210,7 +211,7 @@ jobs: path: | CraftOS-PC.exe CraftOS-PC_console.exe - lua51.dll + lua52.dll - name: Upload artifact symbols uses: actions/upload-artifact@v2 with: @@ -218,5 +219,5 @@ jobs: path: | CraftOS-PC.pdb CraftOS-PC_console.pdb - lua51.pdb + lua52.pdb \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 02b18b40..abe00621 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -6,7 +6,7 @@ { "label": "Build", "type": "shell", - "command": "make -j8", + "command": "make", "group": { "kind": "build", "isDefault": true diff --git a/CraftOS-PC 2.vcxproj b/CraftOS-PC 2.vcxproj index ed7ec5a9..0a1164e8 100644 --- a/CraftOS-PC 2.vcxproj +++ b/CraftOS-PC 2.vcxproj @@ -350,7 +350,7 @@ $(SolutionDir)craftos2-lua\src;%(AdditionalLibraryDirectories) - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;lua51d.lib;shlwapi.lib;ws2_32.lib;wldap32.lib;wer.lib;crypt32.lib;normaliz.lib;SDL2maind.lib;iphlpapi.lib;libpng16d.lib;comctl32.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;lua52d.lib;shlwapi.lib;ws2_32.lib;wldap32.lib;wer.lib;crypt32.lib;normaliz.lib;SDL2maind.lib;iphlpapi.lib;libpng16d.lib;comctl32.lib;%(AdditionalDependencies) Console /VERBOSE:LIB %(AdditionalOptions) true @@ -367,12 +367,13 @@ true stdc11 26812;4005 + true false - copy craftos2-lua\src\lua51d.dll x64\Debug\lua51d.dll + copy craftos2-lua\src\lua52d.dll x64\Debug\lua52d.dll @@ -405,7 +406,7 @@ $(SolutionDir)craftos2-lua\src;%(AdditionalLibraryDirectories) - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;lua51.lib;shlwapi.lib;ws2_32.lib;wldap32.lib;crypt32.lib;normaliz.lib;SDL2main.lib;iphlpapi.lib;libpng16.lib;comctl32.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;lua52.lib;shlwapi.lib;ws2_32.lib;wldap32.lib;crypt32.lib;normaliz.lib;SDL2main.lib;iphlpapi.lib;libpng16.lib;comctl32.lib;%(AdditionalDependencies) Windows true @@ -420,12 +421,13 @@ stdc11 26812;4005 stdcpp17 + true false - copy craftos2-lua\src\lua51.dll x64\Release\lua51.dll + copy craftos2-lua\src\lua52.dll x64\Release\lua52.dll @@ -456,7 +458,7 @@ $(SolutionDir)craftos2-lua\src;%(AdditionalLibraryDirectories) - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;lua51.lib;shlwapi.lib;ws2_32.lib;wldap32.lib;crypt32.lib;normaliz.lib;SDL2main.lib;iphlpapi.lib;libpng16.lib;comctl32.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;lua52.lib;shlwapi.lib;ws2_32.lib;wldap32.lib;crypt32.lib;normaliz.lib;SDL2main.lib;iphlpapi.lib;libpng16.lib;comctl32.lib;%(AdditionalDependencies) Windows true @@ -471,12 +473,13 @@ stdc11 26812;4005 stdcpp17 + true false - copy craftos2-lua\src\lua51.dll x64\ReleaseStandalone\lua51.dll + copy craftos2-lua\src\lua52.dll x64\ReleaseStandalone\lua52.dll @@ -507,7 +510,7 @@ $(SolutionDir)craftos2-lua\src;%(AdditionalLibraryDirectories) - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;lua51.lib;sdl2.lib;shlwapi.lib;ws2_32.lib;wldap32.lib;crypt32.lib;normaliz.lib;SDL2main.lib;SDL2_mixer.lib;iphlpapi.lib;libpng16.lib;comctl32.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;lua52.lib;sdl2.lib;shlwapi.lib;ws2_32.lib;wldap32.lib;crypt32.lib;normaliz.lib;SDL2main.lib;SDL2_mixer.lib;iphlpapi.lib;libpng16.lib;comctl32.lib;%(AdditionalDependencies) Console true @@ -522,12 +525,13 @@ stdc17 26812;4005 stdcpp17 + true false - copy craftos2-lua\src\lua51.dll x64\ReleaseC\lua51.dll + copy craftos2-lua\src\lua52.dll x64\ReleaseC\lua52.dll diff --git a/DOCUMENTATION.md b/DOCUMENTATION.md index 340c793f..2a19ed83 100644 --- a/DOCUMENTATION.md +++ b/DOCUMENTATION.md @@ -181,14 +181,14 @@ int multiply(lua_State *L) { _declspec(dllexport) #endif int luaopen_example(lua_State *L) { - struct luaL_reg M[] = + struct luaL_Reg M[] = { {"addition", addition}, {"multiply", multiply}, {NULL,NULL} }; - luaL_register(L, "example", M); + luaL_Register(L, "example", M); return 1; } diff --git a/api/Computer.hpp b/api/Computer.hpp index 26f79559..738fa001 100644 --- a/api/Computer.hpp +++ b/api/Computer.hpp @@ -132,7 +132,7 @@ struct Computer { std::mutex openWebsocketsMutex; std::vector> startupCallbacks; // List of functions to call when starting up + a userdata to pass as the first argument - // The following fields are available in API version 10.9 and later. + // The following fields are available in API version 12.0 and later. std::vector droppedFiles; // List of files that were dropped in the current drop set private: diff --git a/api/lib.hpp b/api/lib.hpp index 35d5933d..83a7d72b 100644 --- a/api/lib.hpp +++ b/api/lib.hpp @@ -24,9 +24,9 @@ struct Computer; /// The current version of plugin support. #if defined(_WIN32) && defined(_DEBUG) -#define PLUGIN_VERSION 100010 +#define PLUGIN_VERSION 100012 #else -#define PLUGIN_VERSION 10 +#define PLUGIN_VERSION 12 #endif /// Most OS's use UTF-8/ASCII for path storage; however, Windows is contrarian and uses UTF-16. diff --git a/api/peripheral.hpp b/api/peripheral.hpp index 91bace3a..7a02bbf3 100644 --- a/api/peripheral.hpp +++ b/api/peripheral.hpp @@ -56,9 +56,7 @@ class peripheral { virtual int call(lua_State *L, const char * method)=0; // This function is deprecated, and no longer works. In fact, it did not work // in any version of the API. Just leave this as-is. -#if __cplusplus >= 201402L [[deprecated]] -#endif virtual void update() {} // This function should return a library_t containing the names of all of the // methods available to the peripheral. Only the keys, name, and size members diff --git a/craftos2-lua b/craftos2-lua index c2da216b..23a41108 160000 --- a/craftos2-lua +++ b/craftos2-lua @@ -1 +1 @@ -Subproject commit c2da216bb6375d2032ff3810fef9ff2c1d922d82 +Subproject commit 23a41108e84c59a28c542a5ef5306ef79a849fbe diff --git a/examples/ccemux.cpp b/examples/ccemux.cpp index 698c1e33..8e6347e3 100644 --- a/examples/ccemux.cpp +++ b/examples/ccemux.cpp @@ -145,11 +145,11 @@ static int ccemux_getVersion(lua_State *L) { static int ccemux_openEmu(lua_State *L) { Computer * comp = get_comp(L); - int id = 0; + int id = luaL_optinteger(L, 1, -1); if (lua_isnumber(L, 1)) id = (int)lua_tointeger(L, 1); - else if (!lua_isnoneornil(L, 1)) luaL_typerror(L, 1, "number"); - else { + if (id < 0) { std::lock_guard lock(comp->peripherals_mutex); + id = 0; while (functions->getComputerById(id) != NULL) id++; } if (functions->attachPeripheral(comp, "computer_" + std::to_string(id), "computer", NULL, "") == NULL) lua_pushnil(L); @@ -233,7 +233,7 @@ static int ccemux_detach(lua_State *L) { return 0; } -static struct luaL_reg M[] = { +static struct luaL_Reg M[] = { {"getVersion", ccemux_getVersion}, {"openEmu", ccemux_openEmu}, {"closeEmu", ccemux_closeEmu}, @@ -252,7 +252,7 @@ static PluginInfo info("ccemux", 3); extern "C" { DLLEXPORT int luaopen_ccemux(lua_State *L) { - luaL_register(L, lua_tostring(L, 1), M); + luaL_newlib(L, M); functions->addVirtualMount(get_comp(L), emuROM, "/rom"); return 1; } diff --git a/examples/ccemux.vcxproj b/examples/ccemux.vcxproj index 59f7435b..c08bf5fc 100644 --- a/examples/ccemux.vcxproj +++ b/examples/ccemux.vcxproj @@ -146,7 +146,7 @@ true true false - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;SDL2.lib;lua51.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;SDL2.lib;lua52.lib;%(AdditionalDependencies) $(SolutionDir)craftos2-lua\src @@ -212,7 +212,7 @@ Windows true false - kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;SDL2d.lib;lua51d.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;SDL2d.lib;lua52d.lib;%(AdditionalDependencies) $(SolutionDir)craftos2-lua\src diff --git a/examples/plugin_base.cpp b/examples/plugin_base.cpp index 7786e863..27863dda 100644 --- a/examples/plugin_base.cpp +++ b/examples/plugin_base.cpp @@ -15,7 +15,7 @@ extern "C" { // add your functions here... -static luaL_reg M[] = { +static luaL_Reg M[] = { // add functions here as {name, function}... {NULL, NULL} }; @@ -26,7 +26,7 @@ static PluginInfo info("myplugin"); extern "C" { // replace "myplugin" with the plugin name DLLEXPORT int luaopen_myplugin(lua_State *L) { - luaL_register(L, "myplugin", M); + luaL_newlib(L, M); return 1; } diff --git a/resources/CCT-Test-Bootstrap.lua b/resources/CCT-Test-Bootstrap.lua index 86cfc4c7..c0bdccc8 100644 --- a/resources/CCT-Test-Bootstrap.lua +++ b/resources/CCT-Test-Bootstrap.lua @@ -7,7 +7,7 @@ end for _,v in ipairs(fs.list("/")) do if not fs.isReadOnly(v) then fs.delete(v) end end _G._CCPC_FIRST_RUN = nil _G._CCPC_UPDATED_VERSION = nil -local logfile = io.open("test-log.txt", "w") +local logfile = assert(io.open("test-log.txt", "w")) io.output(logfile) shell.run("/test-rom/mcfly /test-rom/spec") logfile:close() diff --git a/resources/CCT-Tests.patch b/resources/CCT-Tests.patch index 6e41011c..a910cd85 100644 --- a/resources/CCT-Tests.patch +++ b/resources/CCT-Tests.patch @@ -9,7 +9,15 @@ diff -ruN --strip -x .DS_Store projects/core/src/test/resources/test-rom/mcfly.l diff -ruN --strip -x .DS_Store projects/core/src/test/resources/test-rom/spec/apis/fs_spec.lua b/projects/core/src/test/resources/test-rom/spec/apis/fs_spec.lua --- a/projects/core/src/test/resources/test-rom/spec/apis/fs_spec.lua 2020-06-29 02:52:34.000000000 -0400 +++ b/projects/core/src/test/resources/test-rom/spec/apis/fs_spec.lua 2020-06-29 01:24:00.000000000 -0400 -@@ -122,7 +122,7 @@ +@@ -88,7 +88,6 @@ + describe("fs.list", function() + it("fails on files", function() + expect.error(fs.list, "rom/startup.lua"):eq("/rom/startup.lua: Not a directory") +- expect.error(fs.list, "startup.lua"):eq("/startup.lua: Not a directory") + end) + + it("fails on non-existent nodes", function() +@@ -122,7 +121,7 @@ describe("fs.makeDir", function() it("fails on files", function() @@ -18,7 +26,7 @@ diff -ruN --strip -x .DS_Store projects/core/src/test/resources/test-rom/spec/ap end) it("fails on read-only mounts", function() -@@ -171,7 +171,7 @@ +@@ -171,7 +170,7 @@ end) it("returns the capacity on the root mount", function() diff --git a/resources/CraftOS-PC.exe.manifest b/resources/CraftOS-PC.exe.manifest index c00aaf30..c7645a3d 100644 --- a/resources/CraftOS-PC.exe.manifest +++ b/resources/CraftOS-PC.exe.manifest @@ -3,7 +3,7 @@ Advanced ComputerCraft Emulator diff --git a/resources/CraftOSTest.lua b/resources/CraftOSTest.lua index 1e649ffa..404cde65 100644 --- a/resources/CraftOSTest.lua +++ b/resources/CraftOSTest.lua @@ -7,9 +7,9 @@ if config and config.add then end term.setCursorPos(1, 1) term.setTextColor(colors.lightBlue) -print("CraftOSTest 1.8") +print("CraftOSTest 1.9") term.setTextColor(colors.white) -if os.version() ~= "CraftOS 1.8" then error("This test is for CraftOS 1.8.") end +if os.version() ~= "CraftOS 1.9" then error("This test is for CraftOS 1.9.") end local api_tests = {} local api = nil diff --git a/resources/Info.plist b/resources/Info.plist index 2f2a351d..57866fc1 100644 --- a/resources/Info.plist +++ b/resources/Info.plist @@ -21,13 +21,13 @@ CFBundlePackageType APPL CFBundleShortVersionString - 2.7.6 + 2.8 CFBundleSignature ???? LSApplicationCategoryType Unknown CFBundleVersion - 2.7.6 + 2.8 NSHumanReadableCopyright Copyright (C) 2019-2023 JackMacWindows. NSHighResolutionCapable diff --git a/src/Computer.cpp b/src/Computer.cpp index fe9564f5..91ca8560 100644 --- a/src/Computer.cpp +++ b/src/Computer.cpp @@ -49,6 +49,7 @@ std::unordered_set orphanedTerminals; // Context structure for yieldable load struct load_ctx { + int id; std::thread thread; std::mutex lock; std::condition_variable notify; @@ -218,6 +219,10 @@ static const char * file_reader(lua_State *L, void * ud, size_t *size) { // computer thread that it's done. The results are copied back to the // main state, and the loader returns. +std::vector load_ctx_stack; // since Lua 5.2+ stores context as ints, we can't store a pointer - + // instead, all pointers are placed in this vector and the context is an offset here + // (maybe we can store a stack index instead?) + static const char * yield_loader(lua_State *L, void* data, size_t *size) { load_ctx* ctx = (load_ctx*)data; lua_State *coro = lua_newthread(L); @@ -226,7 +231,7 @@ static const char * yield_loader(lua_State *L, void* data, size_t *size) { ctx->argcount = 0; int status; do { - status = lua_resume(coro, ctx->argcount); + status = lua_resume(coro, ctx->coro, ctx->argcount); if (status == 0) { if (lua_isnoneornil(coro, 1)) return NULL; else if (lua_isstring(coro, 1)) return lua_tolstring(coro, 1, size); @@ -249,7 +254,7 @@ static const char * yield_loader(lua_State *L, void* data, size_t *size) { } static void load_thread(load_ctx* ctx) { - int status = lua_load52(ctx->coro, yield_loader, ctx, ctx->name, ctx->mode); + int status = lua_load(ctx->coro, yield_loader, ctx, ctx->name, NULL); if (ctx->status == 3) return; std::unique_lock lock(ctx->lock); if (status == 0) { @@ -274,6 +279,8 @@ static int load_ctx_gc(lua_State *L) { } ctx->thread.join(); } + load_ctx_stack[ctx->id] = NULL; + while (!load_ctx_stack.empty() && load_ctx_stack[load_ctx_stack.size()-1] == NULL) load_ctx_stack.erase(load_ctx_stack.end()-1); ctx->thread.~thread(); ctx->lock.~mutex(); ctx->notify.~condition_variable(); @@ -282,8 +289,9 @@ static int load_ctx_gc(lua_State *L) { static int yieldable_load(lua_State *L) { load_ctx* ctx; - if (lua_vcontext(L)) { - ctx = (load_ctx*)lua_vcontext(L); + int ctxid = 0; + if (lua_getctx(L, &ctxid) == LUA_YIELD) { + ctx = load_ctx_stack[ctxid]; std::unique_lock lock(ctx->lock); ctx->status = 0; ctx->L = L; @@ -299,7 +307,7 @@ static int yieldable_load(lua_State *L) { if (status == 0) { /* OK? */ if (env != 0) { /* 'env' parameter? */ lua_pushvalue(L, env); /* environment for loaded function */ - if (!lua_setfenv(L, -2)) /* set it as 1st upvalue */ + if (!lua_setupvalue(L, 1, -2)) /* set it as 1st upvalue */ lua_pop(L, 1); /* remove 'env' if not used by previous call */ } return 1; @@ -327,6 +335,11 @@ static int yieldable_load(lua_State *L) { ctx->envidx = lua_isnoneornil(L, 4) ? 0 : 4; ctx->L = L; ctx->coro = lua_newthread(L); + for (; ctxid < load_ctx_stack.size(); ctxid++) + if (load_ctx_stack[ctxid] == NULL) break; + if (ctxid == load_ctx_stack.size()) load_ctx_stack.push_back(ctx); + else load_ctx_stack[ctxid] = ctx; + ctx->id = ctxid; lua_pushvalue(L, 1); lua_xmove(L, ctx->coro, 1); } @@ -336,12 +349,12 @@ static int yieldable_load(lua_State *L) { if (ctx->status == 1) { int argcount = ctx->argcount; ctx->argcount = lua_gettop(L) - ctx->argcount; - return lua_vyield(L, argcount, ctx); + return lua_yieldk(L, argcount, ctxid, yieldable_load); } else if (ctx->status == 3) return 0; // this should never happen } if (ctx->argcount == 1 && ctx->envidx != 0) { /* OK? */ lua_pushvalue(L, ctx->envidx); /* environment for loaded function */ - if (!lua_setfenv(L, -2)) /* set it as 1st upvalue */ + if (!lua_setupvalue(L, 1, -2)) /* set it as 1st upvalue */ lua_pop(L, 1); /* remove 'env' if not used by previous call */ } return ctx->argcount; @@ -353,6 +366,7 @@ extern int mobile_luaopen(lua_State *L); static const luaL_Reg lualibs[] = { {"", luaopen_base}, + {LUA_COLIBNAME, luaopen_coroutine}, {LUA_OSLIBNAME, luaopen_os}, {LUA_TABLIBNAME, luaopen_table}, {LUA_STRLIBNAME, luaopen_string}, @@ -434,10 +448,10 @@ void runComputer(Computer * self, const path_t& bios_name, const std::string& bi // Load libraries const luaL_Reg *lib = lualibs; + /* call open functions from 'loadedlibs' and set results to global table */ for (; lib->func; lib++) { - lua_pushcfunction(L, lib->func); - lua_pushstring(L, lib->name); - lua_call(L, 1, 0); + luaL_requiref(L, lib->name, lib->func, 1); + lua_pop(L, 1); /* remove lib */ } lua_getglobal(L, "os"); lua_getfield(L, -1, "date"); @@ -465,6 +479,8 @@ void runComputer(Computer * self, const path_t& bios_name, const std::string& bi // Override the default loader to allow yielding from `load` lua_pushcfunction(L, yieldable_load); lua_setglobal(L, "load"); + // Disable bytecode + lua_setdisableflags(L, LUA_DISABLE_BYTECODE); } // Load any plugins available @@ -531,6 +547,8 @@ void runComputer(Computer * self, const path_t& bios_name, const std::string& bi lua_setfield(L, -2, "addListener"); lua_pushnil(L); lua_setfield(L, -2, "removeListener"); + lua_pushnil(L); + lua_setfield(L, -2, "websocketServer"); lua_pop(L, 1); } lua_getglobal(L, "debug"); @@ -541,14 +559,20 @@ void runComputer(Computer * self, const path_t& bios_name, const std::string& bi lua_pop(L, 1); } if (config.serverMode) { - lua_getglobal(L, "http"); - lua_pushnil(L); - lua_setfield(L, -2, "addListener"); - lua_pushnil(L); - lua_setfield(L, -2, "removeListener"); - lua_pop(L, 1); + if (config.http_enable) { + lua_getglobal(L, "http"); + lua_pushnil(L); + lua_setfield(L, -2, "addListener"); + lua_pushnil(L); + lua_setfield(L, -2, "removeListener"); + lua_pushnil(L); + lua_setfield(L, -2, "websocketServer"); + lua_pop(L, 1); + } lua_pushnil(L); lua_setglobal(L, "mounter"); + lua_pushnil(L); + lua_setglobal(L, "config"); } // Set default globals @@ -621,17 +645,17 @@ void runComputer(Computer * self, const path_t& bios_name, const std::string& bi path_t bios_path_expanded = getROMPath() / bios_name; std::ifstream bios_file(bios_path_expanded); if (bios_file.is_open()) { - status = lua_load(self->coro, file_reader, &bios_file, "@bios.lua"); + status = lua_load(self->coro, file_reader, &bios_file, "@bios.lua", NULL); bios_file.close(); } else { status = LUA_ERRFILE; - lua_pushstring(L, strerror(errno)); + lua_pushstring(self->coro, strerror(errno)); } #endif if (status || !lua_isfunction(self->coro, -1)) { /* If something went wrong, error message is at the top of */ /* the stack */ - fprintf(stderr, "Couldn't load BIOS: %s (%s). Please make sure the CraftOS ROM is installed properly. (See https://www.craftos-pc.cc/docs/error-messages for more information.)\n", bios_path_expanded.string().c_str(), lua_tostring(L, -1)); + fprintf(stderr, "Couldn't load BIOS: %s (%s). Please make sure the CraftOS ROM is installed properly. (See https://www.craftos-pc.cc/docs/error-messages for more information.)\n", bios_path_expanded.string().c_str(), lua_tostring(self->coro, -1)); if (::config.standardsMode) displayFailure(self->term, "Error loading bios.lua"); else queueTask([bios_path_expanded](void* term)->void*{ ((Terminal*)term)->showMessage( @@ -660,13 +684,14 @@ void runComputer(Computer * self, const path_t& bios_name, const std::string& bi if (config.abortTimeout > 0 || config.standardsMode) self->eventTimeout = SDL_AddTimer(::config.standardsMode ? 7000 : ::config.abortTimeout, eventTimeoutEvent, self); #endif while (status == LUA_YIELD && self->running == 1) { - status = lua_resume(self->coro, narg); + status = lua_resume(self->coro, NULL, narg); if (status == LUA_YIELD) { - if (lua_gettop(self->coro) && lua_isstring(self->coro, -1)) narg = getNextEvent(self->coro, std::string(lua_tostring(self->coro, -1), lua_strlen(self->coro, -1))); + if (lua_gettop(self->coro) && lua_isstring(self->coro, -1)) narg = getNextEvent(self->coro, std::string(lua_tostring(self->coro, -1), lua_rawlen(self->coro, -1))); else narg = getNextEvent(self->coro, ""); } else if (status != 0 && self->running == 1) { // Catch runtime error self->running = 0; + lua_checkstack(self->coro, 4); lua_pushcfunction(self->coro, termPanic); if (lua_isstring(self->coro, -2)) lua_pushvalue(self->coro, -2); else lua_pushnil(self->coro); diff --git a/src/apis/config.cpp b/src/apis/config.cpp index 8d5c5f0d..3f198329 100644 --- a/src/apis/config.cpp +++ b/src/apis/config.cpp @@ -165,7 +165,7 @@ static int config_set(lua_State *L) { } else luaL_error(L, "Configuration option 'mount_mode' is protected"); } setConfigSetting(disable_lua51_features, boolean); else if (strcmp(name, "default_computer_settings") == 0) - config.default_computer_settings = std::string(luaL_checkstring(L, 2), lua_strlen(L, 2)); + config.default_computer_settings = std::string(luaL_checkstring(L, 2), lua_rawlen(L, 2)); setConfigSetting(logErrors, boolean); setConfigSettingI(computerSpaceLimit); setConfigSettingI(maximumFilesOpen); @@ -198,10 +198,14 @@ static int config_set(lua_State *L) { setConfigSetting(monitorsUseMouseEvents, boolean); setConfigSettingI(defaultWidth); setConfigSettingI(defaultHeight); + else if (strcmp(name, "standardsMode") == 0) { + config.standardsMode = lua_toboolean(L, 2); + lua_setdisableflags(L, config.standardsMode ? LUA_DISABLE_BYTECODE : 0); + } setConfigSetting(standardsMode, boolean); setConfigSetting(useHardwareRenderer, boolean); else if (strcmp(name, "preferredHardwareDriver") == 0) - config.preferredHardwareDriver = std::string(luaL_checkstring(L, 2), lua_strlen(L, 2)); + config.preferredHardwareDriver = std::string(luaL_checkstring(L, 2), lua_rawlen(L, 2)); setConfigSetting(useVsync, boolean); setConfigSetting(http_websocket_enabled, boolean); setConfigSettingI(http_max_websockets); @@ -224,7 +228,7 @@ static int config_set(lua_State *L) { config.http_whitelist.clear(); lua_rawgeti(L, 2, 1); for (int i = 1; lua_isstring(L, -1); i++) { - config.http_whitelist.push_back(luaL_tostring(L, -1)); + config.http_whitelist.push_back(luaL_tolstring(L, -1, NULL)); lua_pop(L, 1); lua_rawgeti(L, 2, i+1); } @@ -233,7 +237,7 @@ static int config_set(lua_State *L) { config.http_blacklist.clear(); lua_rawgeti(L, 2, 1); for (int i = 1; lua_isstring(L, -1); i++) { - config.http_blacklist.push_back(luaL_tostring(L, i)); + config.http_blacklist.push_back(luaL_tolstring(L, i, NULL)); lua_pop(L, 1); lua_rawgeti(L, 2, i+1); } @@ -242,7 +246,7 @@ static int config_set(lua_State *L) { switch (std::get<0>(userConfig[name])) { case 0: config.pluginData[name] = lua_toboolean(L, 2) ? "true" : "false"; break; case 1: config.pluginData[name] = std::to_string(luaL_checkinteger(L, 2)); break; - case 2: config.pluginData[name] = std::string(luaL_checkstring(L, 2), lua_strlen(L, 2)); break; + case 2: config.pluginData[name] = std::string(luaL_checkstring(L, 2), lua_rawlen(L, 2)); break; case 3: return luaL_error(L, "Invalid type"); // maybe fix this later? } if (std::get<1>(userConfig[name]) != nullptr) { diff --git a/src/apis/fs.cpp b/src/apis/fs.cpp index ebe96ca5..1fd40b8a 100644 --- a/src/apis/fs.cpp +++ b/src/apis/fs.cpp @@ -154,16 +154,17 @@ static int fs_list(lua_State *L) { lastCFunction = __func__; std::string str = checkstring(L, 1); const std::vector possible_paths = fixpath_multiple(get_comp(L), str); - if (possible_paths.empty()) err(L, 1, "Not a directory"); + if (possible_paths.empty()) err(L, 1, "No such file"); bool gotdir = false; std::set entries; for (const path_t& path : possible_paths) { if (std::regex_search((*path.begin()).native(), pathregex("^\\d+:"))) { try { const FileEntry &d = get_comp(L)->virtualMounts[(unsigned)std::stoul((*path.begin()).native())]->path(path.lexically_relative(*path.begin())); - gotdir = true; - if (d.isDir) for (const auto& p : d.dir) entries.insert(p.first); - else gotdir = false; + if (d.isDir) { + gotdir = true; + for (const auto& p : d.dir) entries.insert(p.first); + } } catch (...) {continue;} } else { std::error_code e; @@ -437,7 +438,14 @@ static int fs_open(lua_State *L) { lastCFunction = __func__; Computer * computer = get_comp(L); const char * mode = luaL_checkstring(L, 2); - if ((mode[0] != 'r' && mode[0] != 'w' && mode[0] != 'a') || (!(mode[1] == 'b' && mode[2] == '\0') && mode[1] != '\0')) luaL_error(L, "%s: Unsupported mode", mode); + if ( + (mode[0] != 'r' && mode[0] != 'w' && mode[0] != 'a') || + ( + !(mode[1] == '+' && mode[2] == 'b' && mode[3] == '\0') && + !(mode[1] == '+' && mode[2] == '\0') && + !(mode[1] == 'b' && mode[2] == '\0') && + mode[1] != '\0' + )) luaL_error(L, "%s: Unsupported mode", mode); std::string str = checkstring(L, 1); const path_t path = mode[0] == 'r' ? fixpath(get_comp(L), str, true) : fixpath_mkdir(get_comp(L), str); if (path.empty()) { @@ -466,7 +474,7 @@ static int fs_open(lua_State *L) { if (d.isDir) { lua_remove(L, fpid); lua_pushnil(L); - if (strcmp(mode, "r") == 0 || strcmp(mode, "rb") == 0) lua_pushfstring(L, "/%s: No such file", fixpath(computer, str, false, false).string().c_str()); + if (strchr(mode, 'r') != NULL) lua_pushfstring(L, "/%s: Not a file", fixpath(computer, str, false, false).string().c_str()); else lua_pushfstring(L, "/%s: Cannot write to directory", fixpath(computer, str, false, false).string().c_str()); return 2; } @@ -484,7 +492,7 @@ static int fs_open(lua_State *L) { std::error_code e; if (fs::is_directory(path, e)) { lua_pushnil(L); - if (strcmp(mode, "r") == 0 || strcmp(mode, "rb") == 0) lua_pushfstring(L, "/%s: No such file", fixpath(computer, str, false, false).string().c_str()); + if (strchr(mode, 'r') != NULL) lua_pushfstring(L, "/%s: Not a file", fixpath(computer, str, false, false).string().c_str()); else lua_pushfstring(L, "/%s: Cannot write to directory", fixpath(computer, str, false, false).string().c_str()); return 2; } @@ -505,14 +513,21 @@ static int fs_open(lua_State *L) { std::fstream ** fp = (std::fstream**)lua_newuserdata(L, sizeof(std::fstream*)); fpid = lua_gettop(L); std::ios::openmode flags = std::ios::binary; - if (strchr(mode, 'r')) flags |= std::ios::in; - else if (strchr(mode, 'w')) flags |= std::ios::out | std::ios::trunc; - else if (strchr(mode, 'a')) flags |= std::ios::in | std::ios::out | std::ios::ate; + if (strchr(mode, 'r')) { + flags |= std::ios::in; + if (strchr(mode, '+')) flags |= std::ios::out; + } else if (strchr(mode, 'w')) { + flags |= std::ios::out | std::ios::trunc; + if (strchr(mode, '+')) flags |= std::ios::in; + } else if (strchr(mode, 'a')) { + flags |= std::ios::in | std::ios::out | std::ios::ate; + if (strchr(mode, '+')) flags |= std::ios::in; + } *fp = new std::fstream(path, flags); if (!(*fp)->is_open()) { bool ok = false; if (strchr(mode, 'a')) { - (*fp)->open(path, std::ios::out | std::ios::trunc); + (*fp)->open(path, (flags & ~std::ios::ate) | std::ios::trunc); ok = (*fp)->is_open(); } if (!ok) { @@ -540,42 +555,24 @@ static int fs_open(lua_State *L) { lua_pushvalue(L, fpid); lua_pushcclosure(L, fs_handle_close, 1); lua_settable(L, -3); - if (strcmp(mode, "r") == 0) { - lua_pushstring(L, "readAll"); - lua_pushvalue(L, fpid); - lua_pushcclosure(L, fs_handle_readAll, 1); - lua_settable(L, -3); - - lua_pushstring(L, "readLine"); - lua_pushvalue(L, fpid); - lua_pushboolean(L, false); - lua_pushcclosure(L, fs_handle_readLine, 2); - lua_settable(L, -3); - - lua_pushstring(L, "read"); - lua_pushvalue(L, fpid); - lua_pushcclosure(L, fs_handle_readChar, 1); - lua_settable(L, -3); - } else if (strcmp(mode, "w") == 0 || strcmp(mode, "a") == 0) { - lua_pushstring(L, "write"); - lua_pushvalue(L, fpid); - lua_pushcclosure(L, fs_handle_writeString, 1); - lua_settable(L, -3); - lua_pushstring(L, "writeLine"); - lua_pushvalue(L, fpid); - lua_pushcclosure(L, fs_handle_writeLine, 1); - lua_settable(L, -3); + lua_pushstring(L, "seek"); + lua_pushvalue(L, fpid); + lua_pushcclosure(L, fs_handle_seek, 1); + lua_settable(L, -3); - lua_pushstring(L, "flush"); - lua_pushvalue(L, fpid); - lua_pushcclosure(L, fs_handle_flush, 1); - lua_settable(L, -3); - } else if (strcmp(mode, "rb") == 0) { - lua_pushstring(L, "read"); - lua_pushvalue(L, fpid); - lua_pushcclosure(L, fs_handle_readByte, 1); - lua_settable(L, -3); + if (mode[0] == 'r' || strchr(mode, '+')) { + if (strchr(mode, 'b')) { + lua_pushstring(L, "read"); + lua_pushvalue(L, fpid); + lua_pushcclosure(L, fs_handle_readByte, 1); + lua_settable(L, -3); + } else { + lua_pushstring(L, "read"); + lua_pushvalue(L, fpid); + lua_pushcclosure(L, fs_handle_readChar, 1); + lua_settable(L, -3); + } lua_pushstring(L, "readAll"); lua_pushvalue(L, fpid); @@ -584,30 +581,34 @@ static int fs_open(lua_State *L) { lua_pushstring(L, "readLine"); lua_pushvalue(L, fpid); - lua_pushboolean(L, true); - lua_pushcclosure(L, fs_handle_readLine, 2); + lua_pushcclosure(L, fs_handle_readLine, 1); lua_settable(L, -3); + } - lua_pushstring(L, "seek"); - lua_pushvalue(L, fpid); - lua_pushcclosure(L, fs_handle_seek, 1); - lua_settable(L, -3); - } else if (strcmp(mode, "wb") == 0 || strcmp(mode, "ab") == 0) { - lua_pushstring(L, "write"); + if (mode[0] == 'w' || mode[0] == 'a' || strchr(mode, '+')) { + if (strchr(mode, 'b')) { + lua_pushstring(L, "write"); + lua_pushvalue(L, fpid); + lua_pushcclosure(L, fs_handle_writeByte, 1); + lua_settable(L, -3); + } else { + lua_pushstring(L, "write"); + lua_pushvalue(L, fpid); + lua_pushcclosure(L, fs_handle_writeString, 1); + lua_settable(L, -3); + } + + lua_pushstring(L, "writeLine"); lua_pushvalue(L, fpid); - lua_pushcclosure(L, fs_handle_writeByte, 1); + lua_pushcclosure(L, fs_handle_writeLine, 1); lua_settable(L, -3); lua_pushstring(L, "flush"); lua_pushvalue(L, fpid); lua_pushcclosure(L, fs_handle_flush, 1); lua_settable(L, -3); - - lua_pushstring(L, "seek"); - lua_pushvalue(L, fpid); - lua_pushcclosure(L, fs_handle_seek, 1); - lua_settable(L, -3); } + computer->files_open++; return 1; } diff --git a/src/apis/handles/fs_handle.cpp b/src/apis/handles/fs_handle.cpp index 44d49c5d..c647a3b2 100644 --- a/src/apis/handles/fs_handle.cpp +++ b/src/apis/handles/fs_handle.cpp @@ -11,11 +11,9 @@ #include #include #include -#include #include #include #include -#include #include #include "fs_handle.hpp" #include "../../util.hpp" @@ -102,10 +100,9 @@ int fs_handle_readLine(lua_State *L) { std::string retval; std::getline(*fp, retval); if (retval.empty() && fp->eof()) return 0; - if (*retval.rbegin() == '\r' && !lua_toboolean(L, lua_upvalueindex(2))) retval = retval.substr(0, retval.size()-1); if (lua_toboolean(L, 1) && fp->good()) retval += '\n'; - const std::string out = lua_toboolean(L, lua_upvalueindex(2)) ? retval : makeASCIISafe(retval.c_str(), retval.size()); - lua_pushlstring(L, out.c_str(), out.length()); + else if (!retval.empty() && retval[retval.size()-1] == '\r') retval.resize(retval.size() - 1); + lua_pushlstring(L, retval.c_str(), retval.length()); return 1; } @@ -115,43 +112,25 @@ int fs_handle_readChar(lua_State *L) { if (fp == NULL) return luaL_error(L, "attempt to use a closed file"); if (fp->eof()) return 0; if (!fp->good()) luaL_error(L, "Could not read file"); - std::string retval; - for (int i = 0; i < luaL_optinteger(L, 1, 1) && !fp->eof(); i++) { - uint32_t codepoint; - const int c = fp->get(); - if (c == EOF) break; - else if (c > 0x7F) { - if (c & 64) { - const int c2 = fp->get(); - if (c2 == EOF) {retval += '?'; break;} - else if (c2 < 0x80 || c2 & 64) codepoint = 1U<<31; - else if (c & 32) { - const int c3 = fp->get(); - if (c3 == EOF) {retval += '?'; break;} - else if (c3 < 0x80 || c3 & 64) codepoint = 1U<<31; - else if (c & 16) { - if (c & 8) codepoint = 1U<<31; - else { - const int c4 = fp->get(); - if (c4 == EOF) {retval += '?'; break;} - else if (c4 < 0x80 || c4 & 64) codepoint = 1U<<31; - else codepoint = ((c & 0x7) << 18) | ((c2 & 0x3F) << 12) | ((c3 & 0x3F) << 6) | (c4 & 0x3F); - } - } else codepoint = ((c & 0xF) << 12) | ((c2 & 0x3F) << 6) | (c3 & 0x3F); - } else codepoint = ((c & 0x1F) << 6) | (c2 & 0x3F); - } else codepoint = 1U<<31; - } else codepoint = (unsigned char)c; - if (codepoint > 255) retval += '?'; - else { - if (codepoint == '\r') { - const int nextc = fp->get(); - if (nextc == '\n') codepoint = nextc; - else fp->putback((char)nextc); - } - retval += (char)codepoint; + if (lua_isnumber(L, 1)) { + if (lua_tointeger(L, 1) < 0) luaL_error(L, "Cannot read a negative number of characters"); + const size_t s = lua_tointeger(L, 1); + if (s == 0) { + if (fp->peek() == EOF || fp->eof()) return 0; + lua_pushstring(L, ""); + return 1; } + char* retval = new char[s]; + fp->read(retval, s); + const size_t actual = fp->gcount(); + if (actual == 0) {delete[] retval; return 0;} + lua_pushlstring(L, retval, actual); + delete[] retval; + } else { + const int retval = fp->get(); + if (retval == EOF || fp->eof()) return 0; + lua_pushlstring(L, (const char *)&retval, 1); } - lua_pushlstring(L, retval.c_str(), retval.length()); return 1; } @@ -162,6 +141,7 @@ int fs_handle_readByte(lua_State *L) { if (fp->eof()) return 0; if (!fp->good()) luaL_error(L, "Could not read file"); if (lua_isnumber(L, 1)) { + if (lua_tointeger(L, 1) < 0) luaL_error(L, "Cannot read a negative number of bytes"); const size_t s = lua_tointeger(L, 1); if (s == 0) { if (fp->peek() == EOF || fp->eof()) return 0; @@ -206,14 +186,11 @@ int fs_handle_writeString(lua_State *L) { std::iostream * fp = *(std::iostream**)lua_touserdata(L, lua_upvalueindex(1)); if (fp == NULL) luaL_error(L, "attempt to use a closed file"); if (lua_isnoneornil(L, 1)) return 0; - else if (!lua_isstring(L, 1) && !lua_isnumber(L, 1)) luaL_typerror(L, 1, "string"); + else if (!lua_isstring(L, 1) && !lua_isnumber(L, 1)) luaL_error(L, "bad argument #1 (string expected, got %s)", lua_typename(L, lua_type(L, 1))); if (fp->fail()) luaL_error(L, "Could not write file"); - std::string str(lua_tostring(L, 1), lua_strlen(L, 1)); - std::wstring wstr; - for (unsigned char c : str) wstr += (wchar_t)c; - std::wstring_convert > converter; - const std::string newstr = converter.to_bytes(wstr); - fp->write(newstr.c_str(), newstr.size()); + size_t sz = 0; + const char * str = lua_tolstring(L, 1, &sz); + fp->write(str, sz); return 0; } @@ -222,14 +199,11 @@ int fs_handle_writeLine(lua_State *L) { std::iostream * fp = *(std::iostream**)lua_touserdata(L, lua_upvalueindex(1)); if (fp == NULL) luaL_error(L, "attempt to use a closed file"); if (lua_isnoneornil(L, 1)) return 0; - else if (!lua_isstring(L, 1) && !lua_isnumber(L, 1)) luaL_typerror(L, 1, "string"); + else if (!lua_isstring(L, 1) && !lua_isnumber(L, 1)) luaL_error(L, "bad argument #1 (string expected, got %s)", lua_typename(L, lua_type(L, 1))); if (fp->fail()) luaL_error(L, "Could not write file"); - std::string str(lua_tostring(L, 1), lua_strlen(L, 1)); - std::wstring wstr; - for (unsigned char c : str) wstr += (wchar_t)c; - std::wstring_convert > converter; - const std::string newstr = converter.to_bytes(wstr); - fp->write(newstr.c_str(), newstr.size()); + size_t sz = 0; + const char * str = lua_tolstring(L, 1, &sz); + fp->write(str, sz); fp->put('\n'); return 0; } @@ -247,7 +221,7 @@ int fs_handle_writeByte(lua_State *L) { const char * str = lua_tolstring(L, 1, &sz); if (sz == 0) return 0; fp->write(str, sz); - } else luaL_typerror(L, 1, "number or string"); + } else luaL_error(L, "bad argument #1 (number or string expected, got %s)", lua_typename(L, lua_type(L, 1))); return 0; } diff --git a/src/apis/handles/http_handle.cpp b/src/apis/handles/http_handle.cpp index 884891a3..dd35ae4e 100644 --- a/src/apis/handles/http_handle.cpp +++ b/src/apis/handles/http_handle.cpp @@ -10,27 +10,15 @@ #ifndef __EMSCRIPTEN__ #include -#include -#include #include #include #include #include -#include "http_handle.hpp" #include "../../util.hpp" +#include "http_handle.hpp" using namespace Poco::Net; -struct http_handle_t { - std::string url; - HTTPClientSession * session; - HTTPResponse * handle; - std::istream * stream; - bool isBinary; - std::string failureReason; - http_handle_t(std::istream * s) : stream(s) {} -}; - struct http_res { std::string body; HTTPServerResponse * res; @@ -51,7 +39,7 @@ int http_handle_free(lua_State *L) { int http_handle_close(lua_State *L) { lastCFunction = __func__; http_handle_t** handle = (http_handle_t**)lua_touserdata(L, lua_upvalueindex(1)); - if (*handle == NULL) return luaL_error(L, "attempt to use a closed file"); + if (*handle == NULL) return 0; delete (*handle)->handle; delete (*handle)->session; delete *handle; @@ -70,17 +58,7 @@ int http_handle_readAll(lua_State *L) { ret.append(buffer, sizeof(buffer)); ret.append(buffer, handle->stream->gcount()); ret.erase(std::remove(ret.begin(), ret.end(), '\r'), ret.end()); - std::wstring_convert> converter; - std::wstring wstr; - try {wstr = converter.from_bytes(ret.c_str(), ret.c_str() + ret.length());} - catch (std::exception & e) { - fprintf(stderr, "http_handle_readAll: Error decoding UTF-8: %s\n", e.what()); - lua_pushlstring(L, ret.c_str(), ret.length()); - return 1; - } - std::string out; - for (wchar_t c : wstr) {if (c < 256) out += (char)c; else out += '?';} - lua_pushlstring(L, out.c_str(), out.length()); + lua_pushlstring(L, ret.c_str(), ret.length()); return 1; } @@ -94,7 +72,7 @@ int http_handle_readLine(lua_State *L) { if (retval.empty() && handle->stream->eof()) return 0; if (lua_toboolean(L, 1)) retval += '\n'; else if (!retval.empty() && retval[retval.size()-1] == '\r') retval = retval.substr(0, retval.size()-1); - const std::string out = handle->isBinary ? retval : makeASCIISafe(retval.c_str(), retval.size()); + const std::string out = retval; lua_pushlstring(L, out.c_str(), out.length()); return 1; } diff --git a/src/apis/handles/http_handle.hpp b/src/apis/handles/http_handle.hpp index cd74bf54..26817704 100644 --- a/src/apis/handles/http_handle.hpp +++ b/src/apis/handles/http_handle.hpp @@ -13,6 +13,14 @@ extern "C" { #include } +struct http_handle_t { + std::string url; + std::string failureReason; + Poco::Net::HTTPClientSession * session; + Poco::Net::HTTPResponse * handle; + std::istream * stream; + http_handle_t(std::istream * s) : stream(s) {} +}; extern int http_handle_free(lua_State *L); extern int http_handle_close(lua_State *L); extern int http_handle_readAll(lua_State *L); diff --git a/src/apis/http.cpp b/src/apis/http.cpp index d78cc553..0971203b 100644 --- a/src/apis/http.cpp +++ b/src/apis/http.cpp @@ -32,10 +32,10 @@ #include #include #include -#include "handles/http_handle.hpp" #include "../platform.hpp" #include "../runtime.hpp" #include "../util.hpp" +#include "handles/http_handle.hpp" #ifdef __ANDROID__ extern "C" {extern int Android_JNI_SetupThread(void);} @@ -54,21 +54,10 @@ struct http_param_t { std::unordered_map headers; std::string method; std::string old_url; - bool isBinary; bool redirect; double timeout; }; -struct http_handle_t { - std::string url; - HTTPClientSession * session; - HTTPResponse * handle; - std::istream * stream; - bool isBinary; - std::string failureReason; - http_handle_t(std::istream * s): stream(s) {} -}; - struct http_check_t { std::string url; std::string status; @@ -97,32 +86,20 @@ static std::string http_success(lua_State *L, void* data) { lua_pushcclosure(L, http_handle_readLine, 1); lua_settable(L, -3); - if (!handle->isBinary) { - lua_pushstring(L, "readAll"); - lua_pushvalue(L, -3); - lua_pushcclosure(L, http_handle_readAll, 1); - lua_settable(L, -3); - - lua_pushstring(L, "read"); - lua_pushvalue(L, -3); - lua_pushcclosure(L, http_handle_readChar, 1); - lua_settable(L, -3); - } else { - lua_pushstring(L, "readAll"); - lua_pushvalue(L, -3); - lua_pushcclosure(L, http_handle_readAllByte, 1); - lua_settable(L, -3); + lua_pushstring(L, "readAll"); + lua_pushvalue(L, -3); + lua_pushcclosure(L, http_handle_readAllByte, 1); + lua_settable(L, -3); - lua_pushstring(L, "read"); - lua_pushvalue(L, -3); - lua_pushcclosure(L, http_handle_readByte, 1); - lua_settable(L, -3); + lua_pushstring(L, "read"); + lua_pushvalue(L, -3); + lua_pushcclosure(L, http_handle_readByte, 1); + lua_settable(L, -3); - lua_pushstring(L, "seek"); - lua_pushvalue(L, -3); - lua_pushcclosure(L, http_handle_seek, 1); - lua_settable(L, -3); - } + lua_pushstring(L, "seek"); + lua_pushvalue(L, -3); + lua_pushcclosure(L, http_handle_seek, 1); + lua_settable(L, -3); lua_pushstring(L, "getResponseCode"); lua_pushvalue(L, -3); @@ -161,32 +138,20 @@ static std::string http_failure(lua_State *L, void* data) { lua_pushcclosure(L, http_handle_readLine, 1); lua_settable(L, -3); - if (!handle->isBinary) { - lua_pushstring(L, "readAll"); - lua_pushvalue(L, -3); - lua_pushcclosure(L, http_handle_readAll, 1); - lua_settable(L, -3); + lua_pushstring(L, "readAll"); + lua_pushvalue(L, -3); + lua_pushcclosure(L, http_handle_readAllByte, 1); + lua_settable(L, -3); - lua_pushstring(L, "read"); - lua_pushvalue(L, -3); - lua_pushcclosure(L, http_handle_readChar, 1); - lua_settable(L, -3); - } else { - lua_pushstring(L, "readAll"); - lua_pushvalue(L, -3); - lua_pushcclosure(L, http_handle_readAllByte, 1); - lua_settable(L, -3); - - lua_pushstring(L, "read"); - lua_pushvalue(L, -3); - lua_pushcclosure(L, http_handle_readByte, 1); - lua_settable(L, -3); - - lua_pushstring(L, "seek"); - lua_pushvalue(L, -3); - lua_pushcclosure(L, http_handle_seek, 1); - lua_settable(L, -3); - } + lua_pushstring(L, "read"); + lua_pushvalue(L, -3); + lua_pushcclosure(L, http_handle_readByte, 1); + lua_settable(L, -3); + + lua_pushstring(L, "seek"); + lua_pushvalue(L, -3); + lua_pushcclosure(L, http_handle_seek, 1); + lua_settable(L, -3); lua_pushstring(L, "getResponseCode"); lua_pushvalue(L, -3); @@ -280,12 +245,20 @@ static void downloadThread(void* arg) { else if (uri.getScheme() == "http") { session = new HTTPClientSession(uri.getHost(), uri.getPort()); } else if (uri.getScheme() == "https") { - Context::Ptr context = new Context(Context::CLIENT_USE, "", Context::VERIFY_RELAXED, 9, true, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); - addSystemCertificates(context); + try { + Context::Ptr context = new Context(Context::CLIENT_USE, "", Context::VERIFY_RELAXED, 9, true, "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); + addSystemCertificates(context); #if POCO_VERSION >= 0x010A0000 - context->disableProtocols(Context::PROTO_TLSV1_3); // Some sites break under TLS 1.3 - disable it to maintain compatibility until fixed (pocoproject/poco#3395) + context->disableProtocols(Context::PROTO_TLSV1_3); // Some sites break under TLS 1.3 - disable it to maintain compatibility until fixed (pocoproject/poco#3395) #endif - session = new HTTPSClientSession(uri.getHost(), uri.getPort(), context); + session = new HTTPSClientSession(uri.getHost(), uri.getPort(), context); + } catch (Poco::Exception &e) { + http_handle_t * err = new http_handle_t(NULL); + err->url = param->url; + err->failureReason = e.message(); + queueEvent(param->comp, http_failure, err); + goto downloadThread_finish; + } } else status = "Invalid protocol '" + uri.getScheme() + "'"; } if (!status.empty()) { @@ -382,7 +355,6 @@ static void downloadThread(void* arg) { handle->session = session; handle->handle = response; handle->url = param->old_url; - handle->isBinary = param->isBinary; if (param->redirect && handle->handle->getStatus() / 100 == 3 && handle->handle->has("Location")) { std::string location = handle->handle->get("Location"); if (location.find("://") == std::string::npos) { @@ -500,7 +472,7 @@ static int http_request(lua_State *L) { lua_pushboolean(L, false); return 1; } - if (!lua_isstring(L, 1) && !lua_istable(L, 1)) luaL_typerror(L, 1, "string or table"); + if (!lua_isstring(L, 1) && !lua_istable(L, 1)) luaL_error(L, "bad argument #1 (expected string or table, got %s)", lua_typename(L, lua_type(L, 1))); http_param_t * param = new http_param_t; param->comp = get_comp(L); if (lua_istable(L, 1)) { @@ -511,11 +483,7 @@ static int http_request(lua_State *L) { lua_pop(L, 1); lua_getfield(L, 1, "body"); if (!lua_isnil(L, -1) && !lua_isstring(L, -1)) {delete param; return luaL_error(L, "bad field 'body' (string expected, got %s)", lua_typename(L, lua_type(L, -1)));} - else if (lua_isstring(L, -1)) param->postData = std::string(lua_tostring(L, -1), lua_strlen(L, -1)); - lua_pop(L, 1); - lua_getfield(L, 1, "binary"); - if (!lua_isnil(L, -1) && !lua_isboolean(L, -1)) {delete param; return luaL_error(L, "bad field 'binary' (boolean expected, got %s)", lua_typename(L, lua_type(L, -1)));} - else if (lua_isboolean(L, -1)) param->isBinary = lua_toboolean(L, -1); + else if (lua_isstring(L, -1)) param->postData = std::string(lua_tostring(L, -1), lua_rawlen(L, -1)); lua_pop(L, 1); lua_getfield(L, 1, "method"); if (!lua_isnil(L, -1) && !lua_isstring(L, -1)) {delete param; return luaL_error(L, "bad field 'method' (string expected, got %s)", lua_typename(L, lua_type(L, -1)));} @@ -545,8 +513,7 @@ static int http_request(lua_State *L) { } else { param->url = lua_tostring(L, 1); param->old_url = param->url; - param->isBinary = false; - if (lua_isstring(L, 2)) param->postData = std::string(lua_tostring(L, 2), lua_strlen(L, 2)); + if (lua_isstring(L, 2)) param->postData = std::string(lua_tostring(L, 2), lua_rawlen(L, 2)); if (lua_istable(L, 3)) { lua_pushvalue(L, 3); lua_pushnil(L); @@ -558,7 +525,6 @@ static int http_request(lua_State *L) { } lua_pop(L, 1); } - if (lua_isboolean(L, 4)) param->isBinary = lua_toboolean(L, 4); if (lua_isstring(L, 5)) param->method = lua_tostring(L, 5); param->redirect = !lua_isboolean(L, 6) || lua_toboolean(L, 6); param->timeout = 0; @@ -777,13 +743,13 @@ static int http_removeListener(lua_State *L) { extern int os_startTimer(lua_State *L); struct ws_handle { + bool isServer; std::string url; WebSocket * ws; std::mutex lock; uint16_t port; void * clientID = NULL; ws_handle ** ud = NULL; - bool binary = false; }; struct websocket_failure_data { @@ -804,6 +770,7 @@ struct websocket_closed_data { std::string url; std::string reason; uint16_t code; + void* clientID = NULL; }; static std::string websocket_failure(lua_State *L, void* userp) { @@ -827,18 +794,6 @@ static std::string websocket_closed(lua_State *L, void* userp) { return "websocket_closed"; } -static std::string websocket_closed_server(lua_State *L, void* userp) { - ws_handle * wsh = (ws_handle*)userp; - lua_pushnumber(L, wsh->port); - lua_pushlightuserdata(L, wsh->clientID); - return "websocket_closed"; -} - -static std::string websocket_server_closed(lua_State *L, void* userp) { - lua_pushnumber(L, (ptrdiff_t)userp); - return "websocket_server_closed"; -} - // WebSocket handle functions static int websocket_free(lua_State *L) { lastCFunction = __func__; @@ -853,9 +808,9 @@ static int websocket_free(lua_State *L) { static int websocket_close(lua_State *L) { lastCFunction = __func__; ws_handle * ws = *(ws_handle**)lua_touserdata(L, lua_upvalueindex(1)); - if (ws == NULL) luaL_error(L, "attempt to use a closed file"); + if (ws == NULL) return 0; std::lock_guard lock(ws->lock); - if (ws->ws == NULL) luaL_error(L, "attempt to use a closed file"); + if (ws->ws == NULL) return 0; ws->ws = NULL; return 0; } @@ -868,14 +823,7 @@ static int websocket_send(lua_State *L) { if (ws == NULL) luaL_error(L, "attempt to use a closed file"); std::lock_guard lock(ws->lock); if (ws->ws == NULL) return luaL_error(L, "attempt to use a closed file"); - std::string buf; - if (!lua_toboolean(L, 2) && !ws->binary) { - std::wstring wstr; - for (unsigned char c : str) wstr += (wchar_t)c; - std::wstring_convert > converter; - buf = converter.to_bytes(wstr); - } else buf = str; - if (ws->ws->sendFrame(buf.c_str(), buf.size(), (int)WebSocket::FRAME_FLAG_FIN | (int)(lua_toboolean(L, 2) ? WebSocket::FRAME_BINARY : WebSocket::FRAME_TEXT)) < 1) + if (ws->ws->sendFrame(str.c_str(), str.size(), (int)WebSocket::FRAME_FLAG_FIN | (int)(lua_toboolean(L, 2) ? WebSocket::FRAME_BINARY : WebSocket::FRAME_TEXT)) < 1) websocket_close(L); return 0; } @@ -883,8 +831,8 @@ static int websocket_send(lua_State *L) { static int websocket_receive(lua_State *L) { lastCFunction = __func__; ws_handle * ws = *(ws_handle**)lua_touserdata(L, lua_upvalueindex(1)); - int tm = lua_icontext(L); - if (tm) { + int tm = 0; + if (lua_getctx(L, &tm) == LUA_YIELD) { if (lua_isstring(L, 1)) { // haha, another string scoping issue :DDD // can M$ PLEASE fix this? (maybe I need to repro & report? :thinking:) @@ -894,20 +842,27 @@ static int websocket_receive(lua_State *L) { } std::string * ev = new std::string(lua_tostring(L, 1)); std::string * url = new std::string(); - int port = 0; - if (lua_isnumber(L, 2)) port = lua_tointeger(L, 2); + int tmid = 0; + if (lua_isnumber(L, 2)) tmid = lua_tointeger(L, 2); else if (lua_isstring(L, 2)) { delete url; url = new std::string(lua_tostring(L, 2)); } - if (*ev == "websocket_message" && (ws->url.empty() ? port == ws->port : *url == ws->url) && (ws->clientID == NULL || (lua_islightuserdata(L, 5) && lua_touserdata(L, 5) == ws->clientID))) { + if (*ev == "websocket_message" && !ws->isServer && *url == ws->url) { lua_pushvalue(L, 3); lua_pushvalue(L, 4); delete ev; delete url; return 2; - } else if ((*ev == "websocket_closed" && *url == ws->url && ws->ws == NULL) || - (tm > 0 && *ev == "timer" && lua_isnumber(L, 2) && lua_tointeger(L, 2) == tm)) { + } else if (*ev == "websocket_server_message" && ws->isServer && lua_touserdata(L, 2) == ws->clientID) { + lua_pushvalue(L, 3); + lua_pushvalue(L, 4); + delete ev; + delete url; + return 2; + } else if ((*ev == "websocket_closed" && !ws->isServer && *url == ws->url && ws->ws == NULL) || + (*ev == "websocket_server_closed" && ws->isServer && lua_touserdata(L, 2) == ws->clientID && ws->ws == NULL) || + (tm > 0 && *ev == "timer" && lua_isnumber(L, 2) && tmid == tm)) { lua_pushnil(L); delete ev; delete url; @@ -933,58 +888,83 @@ static int websocket_receive(lua_State *L) { } else tm = -1; } lua_settop(L, 0); - return lua_iyield(L, 0, tm); + return lua_yieldk(L, 0, tm, websocket_receive); } +static luaL_Reg websocket_funcs[] = { + {"close", websocket_close}, + {"receive", websocket_receive}, + {"send", websocket_send}, + {NULL, NULL} +}; + static std::string websocket_success(lua_State *L, void* userp) { ws_handle ** wsh = (ws_handle**)userp; luaL_checkstack(L, 10, "Could not grow stack for websocket_success"); - if ((*wsh)->url.empty()) lua_pushnumber(L, (*wsh)->port); - else lua_pushstring(L, (*wsh)->url.c_str()); - + lua_pushstring(L, (*wsh)->url.c_str()); + lua_createtable(L, 0, 4); ws_handle ** ws = (ws_handle**)lua_newuserdata(L, sizeof(ws_handle*)); *ws = *wsh; (*wsh)->ud = ws; - int pos = lua_gettop(L); lua_createtable(L, 0, 1); lua_pushstring(L, "__gc"); lua_pushcfunction(L, websocket_free); lua_settable(L, -3); lua_setmetatable(L, -2); - - lua_createtable(L, 0, 4); - - lua_pushstring(L, "close"); - lua_pushvalue(L, pos); - lua_pushcclosure(L, websocket_close, 1); - lua_settable(L, -3); - - lua_pushstring(L, "receive"); - lua_pushvalue(L, pos); - lua_pushcclosure(L, websocket_receive, 1); - lua_settable(L, -3); - - lua_pushstring(L, "send"); - lua_pushvalue(L, pos); - lua_pushcclosure(L, websocket_send, 1); - lua_settable(L, -3); - - lua_remove(L, pos); - if ((*ws)->clientID) lua_pushlightuserdata(L, (*ws)->clientID); + luaL_setfuncs(L, websocket_funcs, 1); return "websocket_success"; } static std::string websocket_message(lua_State *L, void* userp) { ws_message * message = (ws_message*)userp; - if (message->url.empty()) lua_pushinteger(L, message->port); - else lua_pushstring(L, message->url.c_str()); + lua_pushstring(L, message->url.c_str()); pushstring(L, message->data); lua_pushboolean(L, message->binary); - if (message->clientID) lua_pushlightuserdata(L, message->clientID); delete message; return "websocket_message"; } +// websocket_server_connect: port, handle +// handles have an additional clientID member +static std::string websocket_server_connect(lua_State *L, void* userp) { + ws_handle ** wsh = (ws_handle**)userp; + luaL_checkstack(L, 10, "Could not grow stack for websocket_server_connect"); + lua_pushnumber(L, (*wsh)->port); + lua_createtable(L, 0, 4); + ws_handle ** ws = (ws_handle**)lua_newuserdata(L, sizeof(ws_handle*)); + *ws = *wsh; + (*wsh)->ud = ws; + lua_createtable(L, 0, 1); + lua_pushstring(L, "__gc"); + lua_pushcfunction(L, websocket_free); + lua_settable(L, -3); + lua_setmetatable(L, -2); + luaL_setfuncs(L, websocket_funcs, 1); + lua_pushlightuserdata(L, (*ws)->clientID); + lua_setfield(L, -2, "clientID"); + return "websocket_server_connect"; +} + +static std::string websocket_server_message(lua_State *L, void* userp) { + ws_message * message = (ws_message*)userp; + lua_pushlightuserdata(L, message->clientID); + pushstring(L, message->data); + lua_pushboolean(L, message->binary); + delete message; + return "websocket_server_message"; +} + +static std::string websocket_server_closed(lua_State *L, void* userp) { + websocket_closed_data * data = (websocket_closed_data*)userp; + lua_pushlightuserdata(L, data->clientID); + if (data->code != 0) { + pushstring(L, data->reason); + lua_pushinteger(L, data->code); + } + delete data; + return "websocket_server_closed"; +} + class websocket_server: public HTTPRequestHandler { public: Computer * comp; @@ -997,13 +977,6 @@ class websocket_server: public HTTPRequestHandler { try { ws = new WebSocket(request, response); } catch (NetException &e) { - websocket_failure_data * data = new websocket_failure_data; - data->url = ""; - data->reason = e.message(); - data->port = srv->port(); - queueEvent(comp, websocket_failure, data); - delete ws; - if (srv != NULL) { try {srv->stop();} catch (...) {} delete srv; } return; } (*retainCount)++; @@ -1014,14 +987,14 @@ class websocket_server: public HTTPRequestHandler { ws_handle ws_orig; ws_handle * wsh = &ws_orig; wsh->ws = ws; - wsh->url = ""; + wsh->isServer = true; wsh->port = srv->port(); wsh->clientID = &request; { std::lock_guard lock(comp->openWebsocketsMutex); comp->openWebsockets.push_back(&wsh); } - queueEvent(comp, websocket_success, &wsh); + queueEvent(comp, websocket_server_connect, &wsh); char * buf = new char[config.http_max_websocket_message]; while (wsh->ws) { int flags = 0; @@ -1030,34 +1003,49 @@ class websocket_server: public HTTPRequestHandler { res = ws->receiveFrame(buf, config.http_max_websocket_message, flags); if (res < 0 || (res == 0 && flags == 0)) { wsh->ws = NULL; - queueEvent(comp, websocket_closed_server, wsh); + websocket_closed_data * d = new websocket_closed_data; + d->clientID = wsh->clientID; + d->code = 0; + queueEvent(comp, websocket_server_closed, d); break; } } catch (Poco::TimeoutException &e) { if (!wsh->ws) { - queueEvent(comp, websocket_closed_server, wsh); + websocket_closed_data * d = new websocket_closed_data; + d->clientID = wsh->clientID; + d->code = 1006; + d->reason = "Timed out"; + queueEvent(comp, websocket_server_closed, d); break; } continue; } catch (NetException &e) { wsh->ws = NULL; - queueEvent(comp, websocket_closed_server, wsh); + websocket_closed_data * d = new websocket_closed_data; + d->clientID = wsh->clientID; + d->code = 1006; + d->reason = e.message(); + queueEvent(comp, websocket_server_closed, d); break; } if ((flags & 0x0f) == WebSocket::FRAME_OP_CLOSE) { wsh->ws = NULL; - queueEvent(comp, websocket_closed_server, wsh); + websocket_closed_data * d = new websocket_closed_data; + d->clientID = wsh->clientID; + if (res >= 2) { + d->code = (((uint8_t*)buf)[0] << 8) | ((uint8_t*)buf)[1]; + d->reason = std::string(buf + 2, res - 2); + } else d->code = 0; + queueEvent(comp, websocket_server_closed, d); break; } else if ((flags & 0x0f) == WebSocket::FRAME_OP_PING) { ws->sendFrame(buf, res, WebSocket::FRAME_FLAG_FIN | WebSocket::FRAME_OP_PONG); } else { ws_message * message = new ws_message; - message->url = ""; - message->port = wsh->port; + message->clientID = wsh->clientID; message->binary = (flags & WebSocket::FRAME_OP_BITMASK) == WebSocket::FRAME_OP_BINARY; - message->data = message->binary ? std::string((const char*)buf, res) : makeASCIISafe((const char*)buf, res); - message->clientID = &request; - queueEvent(comp, websocket_message, message); + message->data = std::string((const char*)buf, res); + queueEvent(comp, websocket_server_message, message); } std::this_thread::yield(); } @@ -1067,17 +1055,6 @@ class websocket_server: public HTTPRequestHandler { if (it != comp->openWebsockets.end()) comp->openWebsockets.erase(it); } try {ws->shutdown();} catch (...) {} - if (--(*retainCount) == 0 && srv != NULL) { - try {srv->stop();} - catch (...) {} - delete srv; - srv = NULL; - { - std::lock_guard lock(comp->openWebsocketsMutex); - comp->openWebsocketServers.erase(wsh->port); - } - queueEvent(comp, websocket_server_closed, (void*)(ptrdiff_t)wsh->port); - } std::lock_guard lock(wsh->lock); wsh->ws = NULL; if (wsh->ud != NULL) *wsh->ud = NULL; @@ -1104,7 +1081,7 @@ class websocket_server: public HTTPRequestHandler { } } -static void websocket_client_thread(Computer *comp, const std::string& str, const std::unordered_map& headers, bool binary, double timeout) { +static void websocket_client_thread(Computer *comp, const std::string& str, const std::unordered_map& headers, double timeout) { #ifdef __APPLE__ pthread_setname_np("WebSocket Client Thread"); #endif @@ -1193,9 +1170,9 @@ static void websocket_client_thread(Computer *comp, const std::string& str, cons #endif ws_handle wsh_orig; ws_handle * wsh = &wsh_orig; + wsh->isServer = false; wsh->url = str; wsh->ws = ws; - wsh->binary = binary; { std::lock_guard lock(comp->openWebsocketsMutex); comp->openWebsockets.push_back(&wsh); @@ -1209,7 +1186,6 @@ static void websocket_client_thread(Computer *comp, const std::string& str, cons res = ws->receiveFrame(buf, config.http_max_websocket_message, flags); if (res < 0 || (res == 0 && flags == 0)) { wsh->ws = NULL; - wsh->url = ""; websocket_closed_data * d = new websocket_closed_data; d->url = str; d->code = 0; @@ -1228,7 +1204,6 @@ static void websocket_client_thread(Computer *comp, const std::string& str, cons continue; } catch (NetException &e) { wsh->ws = NULL; - wsh->url = ""; websocket_closed_data * d = new websocket_closed_data; d->url = str; d->code = 1006; @@ -1238,7 +1213,6 @@ static void websocket_client_thread(Computer *comp, const std::string& str, cons } if ((flags & 0x0f) == WebSocket::FRAME_OP_CLOSE) { wsh->ws = NULL; - wsh->url = ""; websocket_closed_data * d = new websocket_closed_data; d->url = str; if (res >= 2) { @@ -1253,7 +1227,7 @@ static void websocket_client_thread(Computer *comp, const std::string& str, cons ws_message * message = new ws_message; message->url = str; message->binary = (flags & WebSocket::FRAME_OP_BITMASK) == WebSocket::FRAME_OP_BINARY; - message->data = (message->binary || binary) ? std::string((const char*)buf, res) : makeASCIISafe((const char*)buf, res); + message->data = std::string((const char*)buf, res); queueEvent(comp, websocket_message, message); } std::this_thread::yield(); @@ -1264,7 +1238,6 @@ static void websocket_client_thread(Computer *comp, const std::string& str, cons auto it = std::find(comp->openWebsockets.begin(), comp->openWebsockets.end(), (void*)&wsh); if (it != comp->openWebsockets.end()) comp->openWebsockets.erase(it); } - wsh->url = ""; try {ws->shutdown();} catch (...) {} std::lock_guard lock(wsh->lock); wsh->ws = NULL; @@ -1279,35 +1252,7 @@ static int http_websocket(lua_State *L) { Computer * comp = get_comp(L); if (comp->openWebsockets.size() >= (size_t)config.http_max_websockets) return luaL_error(L, "Too many websockets already open"); if (!(config.serverMode || config.vanilla) && (lua_isnoneornil(L, 1) || lua_isnumber(L, 1))) { - int port = luaL_optinteger(L, 1, 80); - if (port < 0 || port > 65535) luaL_error(L, "bad argument #1 (port out of range)"); - if (comp->openWebsocketServers.find(port) != comp->openWebsocketServers.end()) { - // if there's already an open server, reuse that - lua_pushboolean(L, true); - return 1; - } - std::unordered_map headers; - if (lua_istable(L, 2)) { - lua_pushvalue(L, 2); - lua_pushnil(L); - for (int i = 0; lua_next(L, -2); i++) { - size_t keyn = 0, valn = 0; - const char * key = lua_tolstring(L, -2, &keyn), *val = lua_tolstring(L, -1, &valn); - if (key && val) headers[std::string(key, keyn)] = std::string(val, valn); - lua_pop(L, 2); - } - lua_pop(L, 1); - } else if (!lua_isnoneornil(L, 2)) luaL_error(L, "bad argument #2 (expected table, got %s)", lua_typename(L, lua_type(L, 2))); - websocket_server::Factory * f = new websocket_server::Factory(comp, headers); - try {f->srv = new HTTPServer(f, port);} - catch (Poco::Exception& e) { - fprintf(stderr, "Could not open server: %s\n", e.displayText().c_str()); - lua_pushboolean(L, false); - lua_pushstring(L, e.displayText().c_str()); - return 2; - } - comp->openWebsocketServers.insert(port); - f->srv->start(); + luaL_error(L, "function has been replaced with http.websocketServer"); } else if (lua_istable(L, 1)) { Computer * comp = get_comp(L); if (config.http_max_websockets > 0 && comp->openWebsockets.size() >= (unsigned)config.http_max_websockets) luaL_error(L, "Too many websockets already open"); @@ -1328,14 +1273,11 @@ static int http_websocket(lua_State *L) { } } lua_pop(L, 1); - lua_getfield(L, 1, "binary"); - bool binary = lua_toboolean(L, -1); - lua_pop(L, 1); lua_getfield(L, 1, "timeout"); if (!lua_isnil(L, -1) && !lua_isnumber(L, -1)) luaL_error(L, "bad field 'timeout' (expected number, got %s)", lua_typename(L, lua_type(L, -1))); double timeout = luaL_optnumber(L, -1, config.http_timeout / 1000.0); lua_pop(L, 1); - std::thread th(websocket_client_thread, comp, url, headers, binary, timeout); + std::thread th(websocket_client_thread, comp, url, headers, timeout); setThreadName(th, "WebSocket Client Thread"); th.detach(); } else if (lua_isstring(L, 1)) { @@ -1354,8 +1296,7 @@ static int http_websocket(lua_State *L) { } lua_pop(L, 1); } - bool binary = lua_toboolean(L, 3); - std::thread th(websocket_client_thread, comp, url, headers, binary, config.http_timeout / 1000.0); + std::thread th(websocket_client_thread, comp, url, headers, config.http_timeout / 1000.0); setThreadName(th, "WebSocket Client Thread"); th.detach(); } else luaL_error(L, (config.serverMode || config.vanilla) ? "bad argument #1 (expected string or table, got %s)" : "bad argument #1 (expected string, table, number, or nil, got %s)", lua_typename(L, lua_type(L, 1))); @@ -1363,6 +1304,138 @@ static int http_websocket(lua_State *L) { return 1; } +/** + * Listens for a new connection on the server, and returns its handle. + * @param timeout (number, optional) The amount of time to wait + * @return The WebSocket handle for the connection, or nil on timeout. + * @note Server handles are the same as client handles, but with an additional + * clientID member for identifying events. + */ +static int websocket_server_listen(lua_State *L) { + lastCFunction = __func__; + websocket_server::Factory * f = *(websocket_server::Factory**)lua_touserdata(L, lua_upvalueindex(1)); + int tm = 0; + if (lua_getctx(L, &tm) == LUA_YIELD) { + if (lua_isstring(L, 1)) { + // haha, another string scoping issue :DDD + // can M$ PLEASE fix this? (maybe I need to repro & report? :thinking:) + if (f == NULL) { + lua_pushnil(L); + return 1; + } + std::string * ev = new std::string(lua_tostring(L, 1)); + if (lua_isnumber(L, 2)) { + int id = lua_tointeger(L, 2); + if (*ev == "websocket_server_connect" && id == f->srv->port()) { + lua_pushvalue(L, 3); + delete ev; + return 1; + } else if (tm > 0 && *ev == "timer" && id == tm) { + lua_pushnil(L); + delete ev; + return 1; + } else if (*ev == "terminate") { + delete ev; + return luaL_error(L, "Terminated"); + } + } + delete ev; + } + } else { + if (f == NULL) luaL_error(L, "attempt to use a closed file"); + // instead of using native timer routines, we're using os.startTimer so we can be resumed + if (!lua_isnoneornil(L, 1)) { + luaL_checknumber(L, 1); + lua_pushcfunction(L, os_startTimer); + lua_pushvalue(L, 1); + lua_call(L, 1, 1); + tm = lua_tointeger(L, -1); + lua_pop(L, 1); + } else tm = -1; + } + lua_settop(L, 0); + return lua_yieldk(L, 0, tm, websocket_server_listen); +} + +/** + * Closes the WebSocket server. + */ +static int websocket_server_close(lua_State *L) { + lastCFunction = __func__; + websocket_server::Factory * f = *(websocket_server::Factory**)lua_touserdata(L, lua_upvalueindex(1)); + if (f == NULL) return 0; + f->srv->stop(); + delete f->srv; + delete f; + *(websocket_server::Factory**)lua_touserdata(L, lua_upvalueindex(1)) = NULL; + return 0; +} + +static int websocket_server_free(lua_State *L) { + lastCFunction = __func__; + websocket_server::Factory * f = *(websocket_server::Factory**)lua_touserdata(L, 1); + if (f == NULL) return 0; + f->srv->stop(); + delete f->srv; + delete f; + *(websocket_server::Factory**)lua_touserdata(L, 1) = NULL; + return 0; +} + +static luaL_Reg websocket_server_funcs[] = { + {"listen", websocket_server_listen}, + {"close", websocket_server_close}, + {NULL, NULL} +}; + +/** + * Creates a new WebSocket server. + * @param port (number) The port to listen on + * @return Either a WebSocketServer handle, or nil + error + */ +static int http_websocketServer(lua_State *L) { + if (config.serverMode) return 0; + Computer * comp = get_comp(L); + int port = luaL_checkinteger(L, 1); + if (port < 0 || port > 65535) luaL_error(L, "bad argument #1 (port out of range)"); + if (comp->openWebsocketServers.find(port) != comp->openWebsocketServers.end()) { + // if there's already an open server, reuse that + lua_pushnil(L); + lua_pushliteral(L, "Port already in use"); + return 2; + } + std::unordered_map headers; + if (lua_istable(L, 2)) { + lua_pushvalue(L, 2); + lua_pushnil(L); + for (int i = 0; lua_next(L, -2); i++) { + size_t keyn = 0, valn = 0; + const char * key = lua_tolstring(L, -2, &keyn), *val = lua_tolstring(L, -1, &valn); + if (key && val) headers[std::string(key, keyn)] = std::string(val, valn); + lua_pop(L, 2); + } + lua_pop(L, 1); + } else if (!lua_isnoneornil(L, 2)) luaL_error(L, "bad argument #2 (expected table, got %s)", lua_typename(L, lua_type(L, 2))); + websocket_server::Factory * f = new websocket_server::Factory(comp, headers); + try {f->srv = new HTTPServer(f, port);} + catch (Poco::Exception& e) { + fprintf(stderr, "Could not open server: %s\n", e.displayText().c_str()); + lua_pushnil(L); + lua_pushstring(L, e.displayText().c_str()); + return 2; + } + comp->openWebsocketServers.insert(port); + f->srv->start(); + lua_createtable(L, 0, 2); + *(websocket_server::Factory**)lua_newuserdata(L, sizeof(websocket_server::Factory*)) = f; + lua_createtable(L, 0, 1); + lua_pushcfunction(L, websocket_server_free); + lua_setfield(L, -2, "__gc"); + lua_setmetatable(L, -2); + luaL_setfuncs(L, websocket_server_funcs, 1); + return 1; +} + #ifdef __INTELLISENSE__ #pragma endregion #endif @@ -1373,6 +1446,7 @@ static luaL_Reg http_reg[] = { {"addListener", http_addListener}, {"removeListener", http_removeListener}, {"websocket", http_websocket}, + {"websocketServer", http_websocketServer}, {NULL, NULL} }; diff --git a/src/apis/http_emscripten.cpp b/src/apis/http_emscripten.cpp index df0a29a8..6a86841d 100644 --- a/src/apis/http_emscripten.cpp +++ b/src/apis/http_emscripten.cpp @@ -26,7 +26,6 @@ struct http_check_t { }; struct http_data_t { - bool isBinary; Computer * comp; char** headers; }; @@ -38,7 +37,6 @@ struct http_param_t { std::unordered_map headers; std::string method; std::string old_url; - bool isBinary; bool redirect; }; @@ -152,22 +150,15 @@ std::string http_success(lua_State *L, void* data) { lua_pushcclosure(L, http_handle_readAll, 1); lua_settable(L, -3); - if (!((http_data_t*)(*handle)->userData)->isBinary) { - lua_pushstring(L, "readLine"); - lua_pushlightuserdata(L, handle); - lua_pushcclosure(L, http_handle_readLine, 1); - lua_settable(L, -3); + lua_pushstring(L, "read"); + lua_pushlightuserdata(L, handle); + lua_pushcclosure(L, http_handle_readByte, 1); + lua_settable(L, -3); - lua_pushstring(L, "read"); - lua_pushlightuserdata(L, handle); - lua_pushcclosure(L, http_handle_readChar, 1); - lua_settable(L, -3); - } else { - lua_pushstring(L, "read"); - lua_pushlightuserdata(L, handle); - lua_pushcclosure(L, http_handle_readByte, 1); - lua_settable(L, -3); - } + lua_pushstring(L, "readLine"); + lua_pushlightuserdata(L, handle); + lua_pushcclosure(L, http_handle_readLine, 1); + lua_settable(L, -3); lua_pushstring(L, "getResponseCode"); lua_pushlightuserdata(L, handle); @@ -221,7 +212,6 @@ int http_request(lua_State *L) { attr.onsuccess = downloadSucceeded; attr.onerror = downloadFailed; http_data_t * data = new http_data_t; - data->isBinary = lua_isboolean(L, 4) && lua_toboolean(L, 4); data->comp = get_comp(L); attr.userData = data; if (lua_isstring(L, 2)) attr.requestData = lua_tolstring(L, 2, &attr.requestDataSize); @@ -291,7 +281,7 @@ int http_checkURL(lua_State *L) { luaL_checkstring(L, 1); http_param_t * param = new http_param_t; param->comp = get_comp(L); - param->url = std::string(lua_tostring(L, 1), lua_strlen(L, 1)); + param->url = std::string(lua_tostring(L, 1), lua_rawlen(L, 1)); std::thread th(checkThread, param); setThreadName(th, "HTTP Check Thread"); th.detach(); diff --git a/src/apis/mounter.cpp b/src/apis/mounter.cpp index 43a1e56e..a5e6c7ae 100644 --- a/src/apis/mounter.cpp +++ b/src/apis/mounter.cpp @@ -12,6 +12,7 @@ #include #include #include +#include "../peripheral/debugger.hpp" #include "../platform.hpp" #include "../runtime.hpp" #include "../terminal/SDLTerminal.hpp" @@ -62,6 +63,7 @@ static int mounter_unmount(lua_State *L) { if (it == computer->mounts.end()) break; } } + if (found && computer->debugger && !computer->isDebugger) ((debugger*)computer->debugger)->resetMounts(); lua_pushboolean(L, found); return 1; } @@ -79,7 +81,7 @@ static int mounter_list(lua_State *L) { lua_pop(L, 1); // table lua_createtable(L, 1, 0); // table, entries } - lua_pushinteger(L, lua_objlen(L, -1) + 1); // table, entries, index + lua_pushinteger(L, lua_rawlen(L, -1) + 1); // table, entries, index if (std::regex_match(std::get<1>(m), std::basic_regex(path_t("\\d+:").native()))) lua_pushfstring(L, "(virtual mount:%s)", std::get<1>(m).substr(0, std::get<1>(m).size()-1).c_str()); else lua_pushstring(L, path_t(std::get<1>(m)).string().c_str()); // table, entries, index, value lua_settable(L, -3); // table, entries diff --git a/src/apis/os.cpp b/src/apis/os.cpp index a6490ca8..f91a9e61 100644 --- a/src/apis/os.cpp +++ b/src/apis/os.cpp @@ -26,7 +26,7 @@ static int os_getComputerLabel(lua_State *L) { static int os_setComputerLabel(lua_State *L) { lastCFunction = __func__; Computer * comp = get_comp(L); - comp->config->label = std::string(luaL_optstring(L, 1, ""), lua_isstring(L, 1) ? lua_strlen(L, 1) : 0); + comp->config->label = std::string(luaL_optstring(L, 1, ""), lua_isstring(L, 1) ? lua_rawlen(L, 1) : 0); if (comp->term != NULL) comp->term->setLabel(comp->config->label.empty() ? "CraftOS Terminal: " + std::string(comp->isDebugger ? "Debugger" : "Computer") + " " + std::to_string(comp->id) : "CraftOS Terminal: " + asciify(comp->config->label)); return 0; } @@ -34,7 +34,7 @@ static int os_setComputerLabel(lua_State *L) { static int os_queueEvent(lua_State *L) { lastCFunction = __func__; Computer * computer = get_comp(L); - const std::string name = std::string(luaL_checkstring(L, 1), lua_strlen(L, 1)); + const std::string name = std::string(luaL_checkstring(L, 1), lua_rawlen(L, 1)); if (!lua_checkstack(computer->paramQueue, 1)) luaL_error(L, "Could not allocate space for event"); lua_State *param = lua_newthread(computer->paramQueue); lua_remove(L, 1); diff --git a/src/apis/periphemu.cpp b/src/apis/periphemu.cpp index 6610c58d..103ddf4a 100644 --- a/src/apis/periphemu.cpp +++ b/src/apis/periphemu.cpp @@ -138,7 +138,7 @@ bool detachPeripheral(Computer * computer, const std::string& side) { static int periphemu_create(lua_State* L) { lastCFunction = __func__; - if (!lua_isstring(L, 1) && !lua_isnumber(L, 1)) return luaL_typerror(L, 1, "string or number"); + if (!lua_isstring(L, 1) && !lua_isnumber(L, 1)) return luaL_error(L, "bad argument #1 (expected string or number, got %s)", lua_typename(L, lua_type(L, 1))); Computer * computer = get_comp(L); const std::string type = luaL_checkstring(L, 2); std::string side = lua_isnumber(L, 1) ? type + "_" + std::to_string(lua_tointeger(L, 1)) : lua_tostring(L, 1); diff --git a/src/apis/redstone.cpp b/src/apis/redstone.cpp index 6ceef9a0..27a47319 100644 --- a/src/apis/redstone.cpp +++ b/src/apis/redstone.cpp @@ -19,6 +19,11 @@ static std::vector sides = { "left" }; +static const char * _typename(lua_State *L, int num) { + if (luaL_getmetafield(L, num, "__name")) return lua_tostring(L, -1); + else return luaL_typename(L, num); +} + static int rs_getSides(lua_State *L) { lua_createtable(L, 6, 0); for (int i = 0; i < 6; i++) { @@ -35,7 +40,7 @@ static int rs_getSides(lua_State *L) { static int rs_getInput(lua_State *L) { Computer * comp = get_comp(L); - if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", luaL_typename(L, 1)); + if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", _typename(L, 1)); std::string sidestr = lua_tostring(L, 1); std::transform(sidestr.begin(), sidestr.end(), sidestr.begin(), [](unsigned char c) {return std::tolower(c); }); int side = -1; @@ -52,7 +57,7 @@ static int rs_getInput(lua_State *L) { static int rs_getOutput(lua_State *L) { Computer * comp = get_comp(L); - if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", luaL_typename(L, 1)); + if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", _typename(L, 1)); std::string sidestr = lua_tostring(L, 1); std::transform(sidestr.begin(), sidestr.end(), sidestr.begin(), [](unsigned char c) {return std::tolower(c); }); int side = -1; @@ -69,7 +74,7 @@ static int rs_getOutput(lua_State *L) { static int rs_setOutput(lua_State *L) { Computer * comp = get_comp(L); - if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", luaL_typename(L, 1)); + if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", _typename(L, 1)); std::string sidestr = lua_tostring(L, 1); std::transform(sidestr.begin(), sidestr.end(), sidestr.begin(), [](unsigned char c) {return std::tolower(c); }); int side = -1; @@ -86,7 +91,7 @@ static int rs_setOutput(lua_State *L) { static int rs_getAnalogInput(lua_State *L) { Computer * comp = get_comp(L); - if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", luaL_typename(L, 1)); + if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", _typename(L, 1)); std::string sidestr = lua_tostring(L, 1); std::transform(sidestr.begin(), sidestr.end(), sidestr.begin(), [](unsigned char c) {return std::tolower(c); }); int side = -1; @@ -103,7 +108,7 @@ static int rs_getAnalogInput(lua_State *L) { static int rs_getAnalogOutput(lua_State *L) { Computer * comp = get_comp(L); - if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", luaL_typename(L, 1)); + if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", _typename(L, 1)); std::string sidestr = lua_tostring(L, 1); std::transform(sidestr.begin(), sidestr.end(), sidestr.begin(), [](unsigned char c) {return std::tolower(c); }); int side = -1; @@ -120,7 +125,7 @@ static int rs_getAnalogOutput(lua_State *L) { static int rs_setAnalogOutput(lua_State *L) { Computer * comp = get_comp(L); - if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", luaL_typename(L, 1)); + if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", _typename(L, 1)); std::string sidestr = lua_tostring(L, 1); std::transform(sidestr.begin(), sidestr.end(), sidestr.begin(), [](unsigned char c) {return std::tolower(c); }); int side = -1; @@ -131,7 +136,7 @@ static int rs_setAnalogOutput(lua_State *L) { } } if (side == -1) return luaL_error(L, "bad argument #1 (unknown option %s)", sidestr.c_str()); - if (!lua_isnumber(L, 2)) return luaL_error(L, "bad argument #2 (number expected, got %s)", luaL_typename(L, 2)); + if (!lua_isnumber(L, 2)) return luaL_error(L, "bad argument #2 (number expected, got %s)", _typename(L, 2)); const lua_Number strength = lua_tonumber(L, 2); if (isnan(strength)) return luaL_argerror(L, 2, "number expected, got nan"); if (isinf(strength)) return luaL_argerror(L, 2, "number expected, got inf"); @@ -142,7 +147,7 @@ static int rs_setAnalogOutput(lua_State *L) { static int rs_getBundledInput(lua_State *L) { Computer * comp = get_comp(L); - if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", luaL_typename(L, 1)); + if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", _typename(L, 1)); std::string sidestr = lua_tostring(L, 1); std::transform(sidestr.begin(), sidestr.end(), sidestr.begin(), [](unsigned char c) {return std::tolower(c); }); int side = -1; @@ -159,7 +164,7 @@ static int rs_getBundledInput(lua_State *L) { static int rs_getBundledOutput(lua_State *L) { Computer * comp = get_comp(L); - if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", luaL_typename(L, 1)); + if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", _typename(L, 1)); std::string sidestr = lua_tostring(L, 1); std::transform(sidestr.begin(), sidestr.end(), sidestr.begin(), [](unsigned char c) {return std::tolower(c); }); int side = -1; @@ -176,7 +181,7 @@ static int rs_getBundledOutput(lua_State *L) { static int rs_setBundledOutput(lua_State *L) { Computer * comp = get_comp(L); - if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", luaL_typename(L, 1)); + if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", _typename(L, 1)); std::string sidestr = lua_tostring(L, 1); std::transform(sidestr.begin(), sidestr.end(), sidestr.begin(), [](unsigned char c) {return std::tolower(c); }); int side = -1; @@ -187,7 +192,7 @@ static int rs_setBundledOutput(lua_State *L) { } } if (side == -1) return luaL_error(L, "bad argument #1 (unknown option %s)", sidestr.c_str()); - if (!lua_isnumber(L, 2)) return luaL_error(L, "bad argument #2 (number expected, got %s)", luaL_typename(L, 2)); + if (!lua_isnumber(L, 2)) return luaL_error(L, "bad argument #2 (number expected, got %s)", _typename(L, 2)); const lua_Number strength = lua_tonumber(L, 2); if (isnan(strength)) return luaL_argerror(L, 2, "number expected, got nan"); if (isinf(strength)) return luaL_argerror(L, 2, "number expected, got inf"); @@ -198,7 +203,7 @@ static int rs_setBundledOutput(lua_State *L) { static int rs_testBundledInput(lua_State *L) { Computer * comp = get_comp(L); - if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", luaL_typename(L, 1)); + if (lua_type(L, 1) != LUA_TSTRING) return luaL_error(L, "bad argument #1 (string expected, got %s)", _typename(L, 1)); std::string sidestr = lua_tostring(L, 1); std::transform(sidestr.begin(), sidestr.end(), sidestr.begin(), [](unsigned char c) {return std::tolower(c); }); int side = -1; @@ -209,7 +214,7 @@ static int rs_testBundledInput(lua_State *L) { } } if (side == -1) return luaL_error(L, "bad argument #1 (unknown option %s)", sidestr.c_str()); - if (!lua_isnumber(L, 2)) return luaL_error(L, "bad argument #2 (number expected, got %s)", luaL_typename(L, 2)); + if (!lua_isnumber(L, 2)) return luaL_error(L, "bad argument #2 (number expected, got %s)", _typename(L, 2)); const lua_Number mask = lua_tonumber(L, 2); if (isnan(mask)) return luaL_argerror(L, 2, "number expected, got nan"); if (isinf(mask)) return luaL_argerror(L, 2, "number expected, got inf"); @@ -218,7 +223,7 @@ static int rs_testBundledInput(lua_State *L) { return 1; } -static luaL_reg rs_reg[] = { +static luaL_Reg rs_reg[] = { {"getSides", rs_getSides}, {"getInput", rs_getInput}, {"getOutput", rs_getOutput}, diff --git a/src/apis/term.cpp b/src/apis/term.cpp index adef86f0..1b948f5b 100644 --- a/src/apis/term.cpp +++ b/src/apis/term.cpp @@ -21,7 +21,7 @@ static int term_write(lua_State *L) { lastCFunction = __func__; if (selectedRenderer == 1) { printf("%s", luaL_checkstring(L, 1)); - headlessCursorX += lua_strlen(L, 1); + headlessCursorX += lua_rawlen(L, 1); return 0; } else if (selectedRenderer == 4) printf("TW:%d;%s\n", get_comp(L)->term->id, luaL_checkstring(L, 1)); Computer * computer = get_comp(L); @@ -96,7 +96,7 @@ static int term_setCursorPos(lua_State *L) { static int term_setCursorBlink(lua_State *L) { lastCFunction = __func__; - if (!lua_isboolean(L, 1)) luaL_typerror(L, 1, "boolean"); + if (!lua_isboolean(L, 1)) luaL_error(L, "bad argument #1 (expected boolean, got %s)", lua_typename(L, lua_type(L, 1))); if (selectedRenderer != 1) { Terminal * term = get_comp(L)->term; std::lock_guard lock(term->locked); @@ -137,15 +137,19 @@ static int term_getSize(lua_State *L) { } Computer * computer = get_comp(L); Terminal * term = computer->term; - std::lock_guard lock(term->locked); - if ((lua_isboolean(L, 1) && lua_toboolean(L, 1)) || (lua_isnumber(L, 1) && lua_tonumber(L, 1) > 0)) { - lua_pushinteger(L, term->width * Terminal::fontWidth); - lua_pushinteger(L, term->height * Terminal::fontHeight); - } else if (lua_isnoneornil(L, 1) || lua_isboolean(L, 1) || (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0)) { - lua_pushinteger(L, term->width); - lua_pushinteger(L, term->height); - } else luaL_typerror(L, 1, "boolean or number"); - return 2; + { + std::lock_guard lock(term->locked); + if ((lua_isboolean(L, 1) && lua_toboolean(L, 1)) || (lua_isnumber(L, 1) && lua_tonumber(L, 1) > 0)) { + lua_pushinteger(L, term->width * Terminal::fontWidth); + lua_pushinteger(L, term->height * Terminal::fontHeight); + } else if (lua_isnoneornil(L, 1) || lua_isboolean(L, 1) || (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0)) { + lua_pushinteger(L, term->width); + lua_pushinteger(L, term->height); + } else goto error; + return 2; + } +error: + return luaL_error(L, "bad argument #1 (expected boolean or number, got %s)", lua_typename(L, lua_type(L, 1))); } static int term_clear(lua_State *L) { @@ -240,7 +244,7 @@ static int term_blit(lua_State *L) { lastCFunction = __func__; if (selectedRenderer == 1) { printf("%s", lua_tostring(L, 1)); - headlessCursorX += lua_strlen(L, 1); + headlessCursorX += lua_rawlen(L, 1); return 0; } Computer * computer = get_comp(L); @@ -321,7 +325,7 @@ static int term_setPaletteColor(lua_State *L) { static int term_setGraphicsMode(lua_State *L) { lastCFunction = __func__; - if (!lua_isboolean(L, 1) && !lua_isnumber(L, 1)) luaL_typerror(L, 1, "boolean or number"); + if (!lua_isboolean(L, 1) && !lua_isnumber(L, 1)) luaL_error(L, "bad argument #1 (expected boolean or number, got %s)", lua_typename(L, lua_type(L, 1))); Computer * computer = get_comp(L); if (selectedRenderer == 1 || selectedRenderer == 2 || !(computer->config->isColor || computer->isDebugger)) return 0; if (lua_isnumber(L, 1) && (lua_tointeger(L, 1) < 0 || lua_tointeger(L, 1) > 2)) return luaL_error(L, "bad argument #1 (invalid mode %d)", lua_tointeger(L, 1)); @@ -394,7 +398,7 @@ static int term_drawPixels(lua_State *L) { const bool isSolidFill = fillType == LUA_TNUMBER; if (!isSolidFill && fillType != LUA_TTABLE) - return luaL_typerror(L, 3, "table or number"); + return luaL_error(L, "bad argument #3 (expected table or number, got %s)", lua_typename(L, lua_type(L, 3))); bool undefinedWidth; unsigned width, height; @@ -409,7 +413,7 @@ static int term_drawPixels(lua_State *L) { } else { undefinedWidth = lua_isnoneornil(L, 4); width_ = luaL_optinteger(L, 4, 0); - height_ = luaL_optinteger(L, 5, lua_objlen(L, 3)); + height_ = luaL_optinteger(L, 5, lua_rawlen(L, 3)); } if (width_ < 0) @@ -475,7 +479,7 @@ static int term_drawPixels(lua_State *L) { } else if (lua_istable(L, -1)) { // lol const unsigned cool_width = (unsigned) undefinedWidth - ? (int) min(lua_objlen(L, -1), (size_t) (max(pixelWidth - init_x, 0))) + ? (int) min(lua_rawlen(L, -1), (size_t) (max(pixelWidth - init_x, 0))) : (int) min((int) width, pixelWidth - init_x); for (unsigned w = max(-init_x, 0); w < cool_width; w++) { @@ -519,7 +523,7 @@ static int term_getPixels(lua_State* L) { if (end_w < 0) return luaL_argerror(L, 3, "width cannot be negative"); else if (end_h < 0) return luaL_argerror(L, 4, "height cannot be negative"); else if (!lua_isnoneornil(L, 5) && !lua_isboolean(L, 5)) - return luaL_typerror(L, 5, "boolean"); + return luaL_error(L, "bad argument #5 (expected boolean, got %s)", lua_typename(L, lua_type(L, 5))); const bool use_strings = lua_toboolean(L, 5); @@ -615,21 +619,21 @@ static int term_nativePaletteColor(lua_State *L) { static int term_showMouse(lua_State *L) { lastCFunction = __func__; - if (!lua_isboolean(L, 1)) luaL_typerror(L, 1, "boolean"); + if (!lua_isboolean(L, 1)) luaL_error(L, "bad argument #1 (expected boolean, got %s)", lua_typename(L, lua_type(L, 1))); SDL_ShowCursor(lua_toboolean(L, 1)); return 0; } static int term_relativeMouse(lua_State *L) { lastCFunction = __func__; - if (!lua_isboolean(L, 1)) luaL_typerror(L, 1, "boolean"); + luaL_checktype(L, 1, LUA_TBOOLEAN); SDL_SetRelativeMouseMode((SDL_bool)lua_toboolean(L, 1)); return 0; } static int term_setFrozen(lua_State *L) { lastCFunction = __func__; - if (!lua_isboolean(L, 1)) luaL_typerror(L, 1, "boolean"); + if (!lua_isboolean(L, 1)) luaL_error(L, "bad argument #1 (expected boolean, got %s)", lua_typename(L, lua_type(L, 1))); Terminal * term = get_comp(L)->term; if (term == NULL) return 0; std::lock_guard lock(term->locked); @@ -653,7 +657,7 @@ static int term_getFrozen(lua_State *L) { return 1; } -static luaL_reg term_reg[] = { +static luaL_Reg term_reg[] = { {"write", term_write}, {"scroll", term_scroll}, {"setCursorPos", term_setCursorPos}, diff --git a/src/peripheral/debugger.cpp b/src/peripheral/debugger.cpp index 7f2c5f38..1a7d1830 100644 --- a/src/peripheral/debugger.cpp +++ b/src/peripheral/debugger.cpp @@ -414,7 +414,7 @@ static int debugger_lib_run(lua_State *L) { lua_pushcfunction(dbg->thread, _echo); lua_setfield(dbg->thread, -2, "_echo"); } - lua_setfenv(dbg->thread, -2); // ..., func (w/env) + lua_setupvalue(dbg->thread, -2, 1); // ..., func (w/env) lua_pushboolean(L, !lua_pcall(dbg->thread, 0, LUA_MULTRET, 0)); // ..., results... const int top2 = lua_gettop(dbg->thread) - top; // #{..., results...} - #{...} = #{results...} xcopy(dbg->thread, L, top2); // ... @@ -473,7 +473,7 @@ static int debugger_lib_getfenv(lua_State *L) { lua_Debug ar; lua_getstack(dbg->thread, 0, &ar); lua_getinfo(dbg->thread, "f", &ar); - lua_getfenv(dbg->thread, -1); + lua_getupvalue(dbg->thread, -1, 1); lua_xmove(dbg->thread, L, 1); lua_pop(dbg->thread, 1); return 1; @@ -766,11 +766,12 @@ static std::string debugger_print(lua_State *L, void* arg) { } static int lua_converttostring (lua_State *L) { - if (lua_icontext(L)) return 1; + int ctx = 0; + if (lua_getctx(L, &ctx) == LUA_YIELD) return 1; luaL_checkany(L, 1); if (luaL_getmetafield(L, 1, "__tostring")) { lua_pushvalue(L, 1); - lua_icall(L, 1, 1, 1); /* call metamethod */ + lua_callk(L, 1, 1, 1, lua_converttostring); /* call metamethod */ return 1; } switch (lua_type(L, 1)) { @@ -831,6 +832,7 @@ debugger::debugger(lua_State *L, const char * side) { } delete p; monitor->debugger = createDebuggerLibrary(); + for (const auto mount : computer->mounts) monitor->mounts.push_back(mount); { LockGuard lock(computers); computers->push_back(monitor); @@ -844,7 +846,7 @@ debugger::debugger(lua_State *L, const char * side) { lua_sethook(computer->coro, termHook, LUA_MASKLINE | LUA_MASKRET | LUA_MASKCALL | LUA_MASKERROR | LUA_MASKRESUME | LUA_MASKYIELD, 0); lua_sethook(L, termHook, LUA_MASKLINE | LUA_MASKRET | LUA_MASKCALL | LUA_MASKERROR | LUA_MASKRESUME | LUA_MASKYIELD, 0); lua_getfield(L, LUA_REGISTRYINDEX, "_coroutine_stack"); - for (size_t i = 1; i <= lua_objlen(L, -1); i++) { + for (size_t i = 1; i <= lua_rawlen(L, -1); i++) { lua_rawgeti(L, -1, (int)i); if (lua_isthread(L, -1)) lua_sethook(lua_tothread(L, -1), termHook, LUA_MASKLINE | LUA_MASKRET | LUA_MASKCALL | LUA_MASKERROR | LUA_MASKRESUME | LUA_MASKYIELD, 0); lua_pop(L, 1); @@ -897,6 +899,11 @@ int debugger::_deinit(lua_State *L) { return 0; } +void debugger::resetMounts() { + monitor->mounts.clear(); + for (const auto mount : computer->mounts) monitor->mounts.push_back(mount); +} + static luaL_Reg debugger_reg[] = { {"stop", NULL}, {"setBreakpoint", NULL}, diff --git a/src/peripheral/debugger.hpp b/src/peripheral/debugger.hpp index bf843ca6..b7c48733 100644 --- a/src/peripheral/debugger.hpp +++ b/src/peripheral/debugger.hpp @@ -74,6 +74,7 @@ class debugger: public peripheral { virtual int call(lua_State *L, const char * method) override; library_t getMethods() const override {return methods;} void reinitialize(lua_State *L) override; + void resetMounts(); }; #endif \ No newline at end of file diff --git a/src/peripheral/drive.cpp b/src/peripheral/drive.cpp index e1634054..33530435 100644 --- a/src/peripheral/drive.cpp +++ b/src/peripheral/drive.cpp @@ -224,7 +224,7 @@ int drive::insertDisk(lua_State *L, bool init) { #endif } else { if (init) throw std::invalid_argument("bad argument (expected string or number)"); - else luaL_typerror(L, arg, "string or number"); + else luaL_error(L, "bad argument #%d (expected string or number, got %s)", arg, lua_typename(L, lua_type(L, arg))); } return 0; // This dirty hack is because Windows randomly attempts to deallocate a std::wstring diff --git a/src/peripheral/monitor.cpp b/src/peripheral/monitor.cpp index 3bc532a9..72bdde9a 100644 --- a/src/peripheral/monitor.cpp +++ b/src/peripheral/monitor.cpp @@ -110,7 +110,7 @@ int monitor::getSize(lua_State *L) { } else if (lua_isnoneornil(L, 1) || lua_isboolean(L, 1) || (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0)) { lua_pushinteger(L, term->width); lua_pushinteger(L, term->height); - } else luaL_typerror(L, 1, "boolean or number"); + } else luaL_error(L, "bad argument #1 (expected boolean or number, got %s)", lua_typename(L, lua_type(L, 1))); return 2; } @@ -243,7 +243,7 @@ int monitor::setPaletteColor(lua_State *L) { int monitor::setGraphicsMode(lua_State *L) { lastCFunction = __func__; - if (!lua_isnumber(L, 1) && !lua_isboolean(L, 1)) luaL_typerror(L, 1, "number"); + if (!lua_isnumber(L, 1) && !lua_isboolean(L, 1)) luaL_error(L, "bad argument #1 (expected number, got %s)", lua_typename(L, lua_type(L, 1))); if (selectedRenderer == 1 || selectedRenderer == 2) return 0; if (lua_isnumber(L, 1) && (lua_tointeger(L, 1) < 0 || lua_tointeger(L, 1) > 2)) return luaL_error(L, "bad argument #1 (invalid mode %d)", lua_tointeger(L, 1)); std::lock_guard lock(term->locked); @@ -322,7 +322,7 @@ int monitor::drawPixels(lua_State *L) { const bool isSolidFill = fillType == LUA_TNUMBER; if (!isSolidFill && fillType != LUA_TTABLE) - return luaL_typerror(L, 3, "table or number"); + return luaL_error(L, "bad argument #3 (expected table or number, got %s)", lua_typename(L, lua_type(L, 3))); bool undefinedWidth; unsigned width, height; @@ -337,7 +337,7 @@ int monitor::drawPixels(lua_State *L) { } else { undefinedWidth = lua_isnoneornil(L, 4); width_ = luaL_optinteger(L, 4, 0); - height_ = luaL_optinteger(L, 5, lua_objlen(L, 3)); + height_ = luaL_optinteger(L, 5, lua_rawlen(L, 3)); } if (width_ < 0) @@ -402,7 +402,7 @@ int monitor::drawPixels(lua_State *L) { } else if (lua_istable(L, -1)) { // lol const unsigned cool_width = (unsigned) undefinedWidth - ? (int) min(lua_objlen(L, -1), (size_t) (max(pixelWidth - init_x, 0))) + ? (int) min(lua_rawlen(L, -1), (size_t) (max(pixelWidth - init_x, 0))) : (int) min((int) width, pixelWidth - init_x); for (unsigned w = max(-init_x, 0); w < cool_width; w++) { @@ -443,7 +443,7 @@ int monitor::getPixels(lua_State* L) { if (end_w < 0) return luaL_argerror(L, 3, "width cannot be negative"); else if (end_h < 0) return luaL_argerror(L, 4, "height cannot be negative"); else if (!lua_isnoneornil(L, 5) && !lua_isboolean(L, 5)) - return luaL_typerror(L, 5, "boolean"); + return luaL_error(L, "bad argument #5 (expected boolean, got %s)", lua_typename(L, lua_type(L, 5))); const bool use_strings = lua_toboolean(L, 5); @@ -527,7 +527,7 @@ int monitor::screenshot(lua_State *L) { int monitor::setFrozen(lua_State *L) { lastCFunction = __func__; - if (!lua_isboolean(L, 1)) luaL_typerror(L, 1, "boolean"); + if (!lua_isboolean(L, 1)) luaL_error(L, "bad argument #1 (expected boolean, got %s)", lua_typename(L, lua_type(L, 1))); if (term == NULL) return 0; std::lock_guard lock(term->locked); term->frozen = lua_toboolean(L, 1); diff --git a/src/peripheral/printer.cpp b/src/peripheral/printer.cpp index 02ef84c2..e5d0b9cc 100644 --- a/src/peripheral/printer.cpp +++ b/src/peripheral/printer.cpp @@ -268,7 +268,7 @@ int printer::getInkLevel(lua_State *L) { int printer::setPageTitle(lua_State *L) { lastCFunction = __func__; - title = std::string(luaL_checkstring(L, 1), lua_strlen(L, 1)); + title = std::string(luaL_checkstring(L, 1), lua_rawlen(L, 1)); return 0; } diff --git a/src/peripheral/speaker.cpp b/src/peripheral/speaker.cpp index 1b313bd7..215f8dbc 100644 --- a/src/peripheral/speaker.cpp +++ b/src/peripheral/speaker.cpp @@ -565,7 +565,7 @@ int speaker::playAudio(lua_State *L) { luaL_checktype(L, 1, LUA_TTABLE); const double volume = luaL_optnumber(L, 2, 1.0); if (volume < 0.0 || volume > 3.0) luaL_error(L, "invalid volume %f", volume); - size_t len = lua_objlen(L, 1); + size_t len = lua_rawlen(L, 1); if (len > 131072) luaL_error(L, "Audio data is too large"); else if (len == 0) luaL_error(L, "Cannot play empty audio"); if (audioQueue->size() > (config.standardsMode ? 47 : 187)) { diff --git a/src/platform/CraftOS-PC 2.rc b/src/platform/CraftOS-PC 2.rc index 11ca7644..53993765 100644 --- a/src/platform/CraftOS-PC 2.rc +++ b/src/platform/CraftOS-PC 2.rc @@ -60,8 +60,8 @@ MANIFEST RT_MANIFEST "..\\..\\resources\\CraftOS-PC.e // VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,7,5,0 - PRODUCTVERSION 2,7,5,0 + FILEVERSION 2,8,0,0 + PRODUCTVERSION 2,8,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -77,12 +77,12 @@ BEGIN BLOCK "040904b0" BEGIN VALUE "FileDescription", "CraftOS-PC" - VALUE "FileVersion", "2.7.6.0" + VALUE "FileVersion", "2.8.0.0" VALUE "InternalName", "CraftOS-PC.exe" VALUE "LegalCopyright", "Copyright (C) 2019-2023 JackMacWindows." VALUE "OriginalFilename", "CraftOS-PC.exe" VALUE "ProductName", "CraftOS-PC" - VALUE "ProductVersion", "2.7.6.0" + VALUE "ProductVersion", "2.8.0.0" END END BLOCK "VarFileInfo" diff --git a/src/plugin.cpp b/src/plugin.cpp index fe1dc283..d935bc11 100644 --- a/src/plugin.cpp +++ b/src/plugin.cpp @@ -73,7 +73,7 @@ extern bool checkIAPEligibility(const char * identifier); static const PluginFunctions function_map = { PLUGIN_VERSION, - 8, + 0, CRAFTOSPC_VERSION, selectedRenderer, &config, diff --git a/src/runtime.cpp b/src/runtime.cpp index d9eeb3e3..57cde0ed 100644 --- a/src/runtime.cpp +++ b/src/runtime.cpp @@ -22,6 +22,7 @@ #include #include "main.hpp" #include "runtime.hpp" +#include "peripheral/debugger.hpp" #include "platform.hpp" #include "terminal/SDLTerminal.hpp" #include "terminal/CLITerminal.hpp" @@ -392,6 +393,7 @@ bool addMount(Computer *comp, const path_t& real_path, const std::string& comp_p if (!selected) return false; } comp->mounts.push_back(std::make_tuple(std::list(pathc), real_path, read_only)); + if (comp->debugger && !comp->isDebugger) ((debugger*)comp->debugger)->resetMounts(); return true; } diff --git a/src/terminal/RawTerminal.cpp b/src/terminal/RawTerminal.cpp index 6c80768b..318a2a9b 100644 --- a/src/terminal/RawTerminal.cpp +++ b/src/terminal/RawTerminal.cpp @@ -724,7 +724,7 @@ static void rawInputLoop() { lua_getfield(comp->rawFileStack, -1, "readAll"); lua_call(comp->rawFileStack, 0, 1); if (lua_isnil(comp->rawFileStack, -1)) data = ""; // shouldn't happen - else data = std::string(lua_tostring(comp->rawFileStack, -1), lua_strlen(comp->rawFileStack, -1)); + else data = std::string(lua_tostring(comp->rawFileStack, -1), lua_rawlen(comp->rawFileStack, -1)); lua_pop(comp->rawFileStack, 1); lua_getfield(comp->rawFileStack, -1, "close"); lua_call(comp->rawFileStack, 0, 0); @@ -803,11 +803,11 @@ static void rawInputLoop() { lua_pushstring(comp->rawFileStack, path.c_str()); uint32_t size; if (lua_pcall(comp->rawFileStack, 1, 1, 0)) size = 0xFFFFFFFF; - else size = lua_objlen(comp->rawFileStack, -1); + else size = lua_rawlen(comp->rawFileStack, -1); out.write((char*)&size, 4); if (size != 0xFFFFFFFF) for (uint32_t i = 0; i < size; i++) { lua_rawgeti(comp->rawFileStack, -1, i + 1); - out.write(lua_tostring(comp->rawFileStack, -1), lua_strlen(comp->rawFileStack, -1)); + out.write(lua_tostring(comp->rawFileStack, -1), lua_rawlen(comp->rawFileStack, -1)); out.put(0); lua_pop(comp->rawFileStack, 1); } @@ -854,11 +854,11 @@ static void rawInputLoop() { lua_pushstring(comp->rawFileStack, path.c_str()); uint32_t size; if (lua_pcall(comp->rawFileStack, 1, 1, 0)) size = 0xFFFFFFFF; - else size = lua_objlen(comp->rawFileStack, -1); + else size = lua_rawlen(comp->rawFileStack, -1); out.write((char*)&size, 4); if (size != 0xFFFFFFFF) for (uint32_t i = 0; i < size; i++) { lua_rawgeti(comp->rawFileStack, -1, i + 1); - out.write(lua_tostring(comp->rawFileStack, -1), lua_strlen(comp->rawFileStack, -1)); + out.write(lua_tostring(comp->rawFileStack, -1), lua_rawlen(comp->rawFileStack, -1)); out.put(0); lua_pop(comp->rawFileStack, 1); } @@ -868,7 +868,7 @@ static void rawInputLoop() { lua_pushcfunction(comp->rawFileStack, findLibraryFunction(fs_lib.functions, "makeDir")); lua_pushstring(comp->rawFileStack, path.c_str()); if (lua_pcall(comp->rawFileStack, 1, 0, 0)) { - out.write(lua_tostring(comp->rawFileStack, -1), lua_strlen(comp->rawFileStack, -1)); + out.write(lua_tostring(comp->rawFileStack, -1), lua_rawlen(comp->rawFileStack, -1)); lua_pop(comp->rawFileStack, 1); } out.put(0); @@ -877,7 +877,7 @@ static void rawInputLoop() { lua_pushcfunction(comp->rawFileStack, findLibraryFunction(fs_lib.functions, "delete")); lua_pushstring(comp->rawFileStack, path.c_str()); if (lua_pcall(comp->rawFileStack, 1, 0, 0)) { - out.write(lua_tostring(comp->rawFileStack, -1), lua_strlen(comp->rawFileStack, -1)); + out.write(lua_tostring(comp->rawFileStack, -1), lua_rawlen(comp->rawFileStack, -1)); lua_pop(comp->rawFileStack, 1); } out.put(0); @@ -887,7 +887,7 @@ static void rawInputLoop() { lua_pushstring(comp->rawFileStack, path.c_str()); lua_pushstring(comp->rawFileStack, path2.c_str()); if (lua_pcall(comp->rawFileStack, 2, 0, 0)) { - out.write(lua_tostring(comp->rawFileStack, -1), lua_strlen(comp->rawFileStack, -1)); + out.write(lua_tostring(comp->rawFileStack, -1), lua_rawlen(comp->rawFileStack, -1)); lua_pop(comp->rawFileStack, 1); } out.put(0); @@ -897,7 +897,7 @@ static void rawInputLoop() { lua_pushstring(comp->rawFileStack, path.c_str()); lua_pushstring(comp->rawFileStack, path2.c_str()); if (lua_pcall(comp->rawFileStack, 2, 0, 0)) { - out.write(lua_tostring(comp->rawFileStack, -1), lua_strlen(comp->rawFileStack, -1)); + out.write(lua_tostring(comp->rawFileStack, -1), lua_rawlen(comp->rawFileStack, -1)); lua_pop(comp->rawFileStack, 1); } out.put(0); @@ -944,10 +944,10 @@ static void rawInputLoop() { lua_pushstring(comp->rawFileStack, path.c_str()); lua_pushstring(comp->rawFileStack, (std::string((reqtype & CCPC_RAW_FILE_REQUEST_OPEN_APPEND) ? "a" : "w") + ((reqtype & CCPC_RAW_FILE_REQUEST_OPEN_BINARY) ? "b" : "")).c_str()); if (lua_pcall(comp->rawFileStack, 2, 2, 0)) { - out.write(lua_tostring(comp->rawFileStack, -1), lua_strlen(comp->rawFileStack, -1) + 1); + out.write(lua_tostring(comp->rawFileStack, -1), lua_rawlen(comp->rawFileStack, -1) + 1); lua_pop(comp->rawFileStack, 1); } else if (lua_isnil(comp->rawFileStack, -2)) { - out.write(lua_tostring(comp->rawFileStack, -1), lua_strlen(comp->rawFileStack, -1) + 1); + out.write(lua_tostring(comp->rawFileStack, -1), lua_rawlen(comp->rawFileStack, -1) + 1); lua_pop(comp->rawFileStack, 2); } else { lua_pop(comp->rawFileStack, 1); diff --git a/src/terminal/TRoRTerminal.cpp b/src/terminal/TRoRTerminal.cpp index 98b7a0e6..c2bda8dc 100644 --- a/src/terminal/TRoRTerminal.cpp +++ b/src/terminal/TRoRTerminal.cpp @@ -44,7 +44,7 @@ static std::string trorEvent(lua_State *L, void* userp) { } delete str; lua_newtable(L); - lua_setfenv(L, -2); + lua_setupvalue(L, -2, 1); lua_call(L, 0, LUA_MULTRET); std::string name = lua_tostring(L, 1); lua_remove(L, 1); diff --git a/src/termsupport.cpp b/src/termsupport.cpp index 793e95cf..f56f76b1 100644 --- a/src/termsupport.cpp +++ b/src/termsupport.cpp @@ -411,17 +411,17 @@ static void noDebuggerBreak(lua_State *L, Computer * computer, lua_Debug * ar) { lua_settable(coro, -3); lua_newtable(coro); lua_pushstring(coro, "__index"); - lua_getfenv(L, -2); + lua_getupvalue(L, -2, 1); lua_xmove(L, coro, 1); lua_settable(coro, -3); lua_setmetatable(coro, -2); lua_pushstring(coro, "/rom/programs/lua.lua"); - int status = lua_resume(coro, 2); + int status = lua_resume(coro, L, 2); int narg; while (status == LUA_YIELD) { - if (lua_isstring(coro, -1)) narg = getNextEvent(coro, std::string(lua_tostring(coro, -1), lua_strlen(coro, -1))); + if (lua_isstring(coro, -1)) narg = getNextEvent(coro, std::string(lua_tostring(coro, -1), lua_rawlen(coro, -1))); else narg = getNextEvent(coro, ""); - status = lua_resume(coro, narg); + status = lua_resume(coro, L, narg); } lua_pop(L, 1); lua_pushnil(L); @@ -442,18 +442,12 @@ extern "C" { } } -extern "C" { -#ifdef _WIN32 - __declspec(dllimport) -#endif - extern const char KEY_HOOK; -} - void termHook(lua_State *L, lua_Debug *ar) { std::string name; // For some reason MSVC explodes when this isn't at the top of the function // I've had issues with it randomly moving scope boundaries around (see apis/config.cpp:101, runtime.cpp:249), // so I'm not surprised about it happening again. - if (lua_icontext(L) == 1) { + int ctx = 0; + if (lua_getctx(L, &ctx) == LUA_YIELD) { lua_pop(L, 1); return; } @@ -461,7 +455,7 @@ void termHook(lua_State *L, lua_Debug *ar) { if (computer->debugger != NULL && !computer->isDebugger && (computer->shouldDeinitDebugger || ((debugger*)computer->debugger)->running == false)) { computer->shouldDeinitDebugger = false; lua_getfield(L, LUA_REGISTRYINDEX, "_coroutine_stack"); - for (size_t i = 1; i <= lua_objlen(L, -1); i++) { + for (size_t i = 1; i <= lua_rawlen(L, -1); i++) { lua_rawgeti(L, -1, (int)i); if (lua_isthread(L, -1)) lua_sethook(lua_tothread(L, -1), NULL, 0, 0); //lua_sethook(lua_tothread(L, -1), termHook, LUA_MASKRET | LUA_MASKCALL | LUA_MASKERROR | LUA_MASKRESUME | LUA_MASKYIELD, 0); lua_pop(L, 1); @@ -523,7 +517,7 @@ void termHook(lua_State *L, lua_Debug *ar) { if (debuggerBreak(L, computer, dbg, lua_tostring(L, -2) == NULL ? "Error" : lua_tostring(L, -2))) return; } } else if (computer->debugger != NULL && !computer->isDebugger) { - if (ar->event == LUA_HOOKRET || ar->event == LUA_HOOKTAILRET) { + if (ar->event == LUA_HOOKRET) { debugger * dbg = (debugger*)computer->debugger; if (dbg->breakType == DEBUGGER_BREAK_TYPE_RETURN && dbg->thread == NULL && debuggerBreak(L, computer, dbg, "Pause")) return; if (dbg->isProfiling) { @@ -720,6 +714,7 @@ std::string termGetEvent(lua_State *L) { computer->waitingForTerminate &= ~1; return "terminate"; } else if ((computer->waitingForTerminate & 3) == 0) computer->waitingForTerminate |= 1; + else return ""; } else if (((selectedRenderer == 0 || selectedRenderer == 5) ? e.key.keysym.sym == SDLK_s : e.key.keysym.sym == 31) && (e.key.keysym.mod & KMOD_CTRL)) { if (computer->waitingForTerminate & 4) { computer->waitingForTerminate |= 8; @@ -727,6 +722,7 @@ std::string termGetEvent(lua_State *L) { computer->running = 0; return "terminate"; } else if ((computer->waitingForTerminate & 12) == 0) computer->waitingForTerminate |= 4; + else return ""; } else if (((selectedRenderer == 0 || selectedRenderer == 5) ? e.key.keysym.sym == SDLK_r : e.key.keysym.sym == 19) && (e.key.keysym.mod & KMOD_CTRL)) { if (computer->waitingForTerminate & 16) { computer->waitingForTerminate |= 32; @@ -734,6 +730,7 @@ std::string termGetEvent(lua_State *L) { computer->running = 2; return "terminate"; } else if ((computer->waitingForTerminate & 48) == 0) computer->waitingForTerminate |= 16; + else return ""; } else if (e.key.keysym.sym == SDLK_v && (e.key.keysym.mod & KMOD_SYSMOD) && SDL_HasClipboardText()) { char * text = SDL_GetClipboardText(); std::string str; @@ -759,7 +756,7 @@ std::string termGetEvent(lua_State *L) { std::string str; try {str = utf8_to_string(e.text.text, std::locale("C"));} catch (std::exception &ignored) {str = "?";} - if (!str.empty()) { + if (!str.empty() && !(computer->waitingForTerminate & 0x2A)) { #if defined(__ANDROID__) || defined(__IPHONEOS__) mobileResetModifiers(); #endif diff --git a/src/util.cpp b/src/util.cpp index 9709365e..79fd5731 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -54,12 +54,7 @@ void uncache_state(lua_State *L) { void load_library(Computer *comp, lua_State *L, const library_t& lib) { lua_newtable(L); - luaL_Reg * l = lib.functions; - for (; l->name; l++) { - if (l->func == NULL) continue; - lua_pushcclosure(L, l->func, 0); - lua_setfield(L, -2, l->name); - } + luaL_setfuncs(L, lib.functions, 0); lua_setglobal(L, lib.name); if (lib.init != NULL) lib.init(comp); } @@ -230,20 +225,29 @@ path_t fixpath(Computer *comp, const std::string& path, bool exists, bool addExt if (!found) return path_t(); } else if (pathc.size() > 1) { bool found = false; - std::string back = pathc.back(); - pathc.pop_back(); - for (const _path_t& p : max_path.second) { - path_t sstmp = p; - for (const std::string& s : pathc) sstmp /= s; - e.clear(); - if ( - (isVFSPath(p) && (nothrow(comp->virtualMounts[(unsigned)std::stoul(p.substr(0, p.size()-1))]->path(ss/back)) || - (nothrow(comp->virtualMounts[(unsigned)std::stoul(p.substr(0, p.size()-1))]->path(sstmp)) && comp->virtualMounts[(unsigned)std::stoul(p.substr(0, p.size()-1))]->path(sstmp).isDir))) || - (fs::exists(sstmp/back, e)) || (fs::is_directory(sstmp, e))) { - ss /= sstmp/back; - found = true; - break; + std::stack oldback; + while (!found && !pathc.empty()) { + found = false; + std::string back = pathc.back(); + pathc.pop_back(); + for (const _path_t& p : max_path.second) { + path_t sstmp = p; + for (const std::string& s : pathc) sstmp /= s; + e.clear(); + if ( + (isVFSPath(p) && (nothrow(comp->virtualMounts[(unsigned)std::stoul(p.substr(0, p.size()-1))]->path(ss/back)) || + (nothrow(comp->virtualMounts[(unsigned)std::stoul(p.substr(0, p.size()-1))]->path(sstmp)) && comp->virtualMounts[(unsigned)std::stoul(p.substr(0, p.size()-1))]->path(sstmp).isDir))) || + (fs::exists(sstmp/back, e)) || (fs::is_directory(sstmp, e))) { + ss /= sstmp/back; + while (!oldback.empty()) { + ss /= oldback.top(); + oldback.pop(); + } + found = true; + break; + } } + if (!found) oldback.push(back); } if (!found) return path_t(); } else { @@ -340,7 +344,7 @@ static void xcopy_internal(lua_State *from, lua_State *to, int n, int copies_slo } default: { if (luaL_callmeta(from, -1-i, "__tostring")) { - lua_pushlstring(to, lua_tostring(from, -1), lua_strlen(from, -1)); + lua_pushlstring(to, lua_tostring(from, -1), lua_rawlen(from, -1)); lua_pop(from, 1); } else lua_pushfstring(to, "<%s: %p>", lua_typename(from, lua_type(from, -1-i)), lua_topointer(from, -1-i)); break; @@ -356,19 +360,9 @@ void xcopy(lua_State *from, lua_State *to, int n) { lua_remove(to, cslot); } +// Deprecated as of CCPC v2.8; text mode no longer exists std::string makeASCIISafe(const char * retval, size_t len) { - std::wstring_convert> converter; - std::wstring wstr; - try {wstr = converter.from_bytes(retval, retval + len);} - catch (std::exception &e) { - fprintf(stderr, "fs_handle_readAll: Error decoding UTF-8: %s\n", e.what()); - std::string out; - for (size_t i = 0; i < len; i++) {if ((unsigned char)retval[i] < 128) out += retval[i]; else out += '?';} - return out; - } - std::string out; - for (wchar_t c : wstr) {if (c < 256) out += (char)c; else out += '?';} - return out; + return std::string(retval, len); } struct IPv6 {uint16_t a, b, c, d, e, f, g, h;}; diff --git a/src/util.hpp b/src/util.hpp index 05ab0812..4d87d700 100644 --- a/src/util.hpp +++ b/src/util.hpp @@ -26,8 +26,8 @@ extern "C" { #include #include -#define CRAFTOSPC_VERSION "v2.7.6" -#define CRAFTOSPC_CC_VERSION "1.108.0" +#define CRAFTOSPC_VERSION "v2.8" +#define CRAFTOSPC_CC_VERSION "1.109.2" #define CRAFTOSPC_INDEV true using path_t = std::filesystem::path; @@ -139,46 +139,28 @@ class Value { }; // For get_comp -typedef union { - void *gc; - void *p; - lua_Number n; - int b; -} lua_Value; - -typedef struct lua_TValue { - lua_Value value; int tt; -} TValue; - struct lua_State { - void *next; - unsigned char tt; - unsigned char marked; - unsigned char status; - void* top; /* first free slot in the stack */ - void* base; /* base of current function */ - void *l_G; - void *ci; /* call info for current function */ - void* ctx; /* `savedpc' of current function, or context */ - void* stack_last; /* last free slot in the stack */ - void* stack; /* stack base */ - void *end_ci; /* points after end of ci array*/ - void *base_ci; /* array of CallInfo's */ - int stacksize; - int size_ci; /* size of array `base_ci' */ - unsigned short nCcalls; /* number of nested C calls */ - unsigned short baseCcalls; /* nested C calls when resuming coroutine */ - unsigned char hookmask; - unsigned char allowhook; - int basehookcount; - int hookcount; - lua_Hook hook; - TValue l_gt; /* table of globals */ - TValue env; /* temporary place for environments */ - void *openupval; /* list of open upvalues in this stack */ - void *gclist; - struct lua_longjmp *errorJmp; /* current error recover point */ - ptrdiff_t errfunc; /* current error handling function (stack index) */ + void *next; uint8_t tt; uint8_t marked; + uint8_t status; + void* top; /* first free slot in the stack */ + void* l_G; + void *ci; /* call info for current function */ + const int *oldpc; /* last pc traced */ + void* stack_last; /* last free slot in the stack */ + void* stack; /* stack base */ + int stacksize; + unsigned short nny; /* number of non-yieldable calls in stack */ + unsigned short nCcalls; /* number of nested C calls */ + uint8_t hookmask; + uint8_t allowhook; + int basehookcount; + int hookcount; + lua_Hook hook; + void *openupval; /* list of open upvalues in this stack */ + void *gclist; + struct lua_longjmp *errorJmp; /* current error recover point */ + ptrdiff_t errfunc; /* current error handling function (stack index) */ + void* base_ci; /* CallInfo for first level (C calling Lua) */ }; inline int log2i(int num) { diff --git a/vcpkg.json b/vcpkg.json index c363e8da..1d6b2e92 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,6 +1,6 @@ { "name": "craftos-pc", - "version-string": "v2.7", + "version-string": "v2.8", "description": "Advanced ComputerCraft emulator", "dependencies": [ "sdl2",