diff --git a/radio/src/thirdparty/Lua/CMakeLists.txt b/radio/src/thirdparty/Lua/CMakeLists.txt new file mode 100644 index 000000000..ffb3d67bb --- /dev/null +++ b/radio/src/thirdparty/Lua/CMakeLists.txt @@ -0,0 +1,61 @@ +cmake_minimum_required(VERSION 3.10) +project(edgetx-luac C) + +# Native host build of luac that produces bytecode compatible with +# EdgeTX's 32-bit Lua 5.3 configuration (float numbers, int integers). +# +# Usage: +# cmake -S radio/src/thirdparty/Lua -B build-luac +# cmake --build build-luac +# +# The resulting 'edgetx-luac' binary can compile scripts for any EdgeTX radio: +# edgetx-luac -s -o script.luac script.lua + +set(LUA_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src) + +set(LUA_CORE_SRC + ${LUA_DIR}/lapi.c + ${LUA_DIR}/lcode.c + ${LUA_DIR}/lctype.c + ${LUA_DIR}/ldebug.c + ${LUA_DIR}/ldo.c + ${LUA_DIR}/ldump.c + ${LUA_DIR}/lfunc.c + ${LUA_DIR}/lgc.c + ${LUA_DIR}/llex.c + ${LUA_DIR}/lmem.c + ${LUA_DIR}/lobject.c + ${LUA_DIR}/lopcodes.c + ${LUA_DIR}/lparser.c + ${LUA_DIR}/lstate.c + ${LUA_DIR}/lstring.c + ${LUA_DIR}/ltable.c + ${LUA_DIR}/ltm.c + ${LUA_DIR}/lundump.c + ${LUA_DIR}/lvm.c + ${LUA_DIR}/lzio.c + ${LUA_DIR}/lauxlib.c +) + +add_executable(edgetx-luac + ${LUA_DIR}/luac.c + ${LUA_CORE_SRC} +) + +target_compile_definitions(edgetx-luac PRIVATE + LUA_HOST_BUILD + LUA_CROSS_COMPILER + LUA_COMPAT_5_2 +) + +target_include_directories(edgetx-luac PRIVATE ${LUA_DIR}) + +# Link math library on Unix +if(UNIX) + target_link_libraries(edgetx-luac m) +endif() + +set_target_properties(edgetx-luac PROPERTIES + OUTPUT_NAME edgetx-luac + C_STANDARD 99 +) diff --git a/radio/src/thirdparty/Lua/build-wasm.sh b/radio/src/thirdparty/Lua/build-wasm.sh new file mode 100755 index 000000000..c2f77fc7d --- /dev/null +++ b/radio/src/thirdparty/Lua/build-wasm.sh @@ -0,0 +1,82 @@ +#!/bin/bash +# +# Build edgetx-luac.wasm — a browser-ready WASM module that compiles +# Lua source to EdgeTX-compatible bytecode entirely in memory. +# +# Requirements: wasi-sdk (default /opt/wasi-sdk) +# +# Usage: +# cd radio/src/thirdparty/Lua +# ./build-wasm.sh +# +# The output edgetx-luac.wasm is placed in the current directory. + +set -euo pipefail + +WASI_SDK="${WASI_SDK:-/opt/wasi-sdk}" +CC="${WASI_SDK}/bin/clang" + +if [ ! -x "$CC" ]; then + echo "Error: wasi-sdk clang not found at $CC" >&2 + echo "Set WASI_SDK to your wasi-sdk installation path." >&2 + exit 1 +fi + +SRCDIR="$(cd "$(dirname "$0")/src" && pwd)" + +LUA_CORE_SRC=( + "$SRCDIR/lapi.c" + "$SRCDIR/lcode.c" + "$SRCDIR/lctype.c" + "$SRCDIR/ldebug.c" + "$SRCDIR/ldo.c" + "$SRCDIR/ldump.c" + "$SRCDIR/lfunc.c" + "$SRCDIR/lgc.c" + "$SRCDIR/llex.c" + "$SRCDIR/lmem.c" + "$SRCDIR/lobject.c" + "$SRCDIR/lopcodes.c" + "$SRCDIR/lparser.c" + "$SRCDIR/lstate.c" + "$SRCDIR/lstring.c" + "$SRCDIR/ltable.c" + "$SRCDIR/ltm.c" + "$SRCDIR/lundump.c" + "$SRCDIR/lvm.c" + "$SRCDIR/lzio.c" + "$SRCDIR/lauxlib.c" +) + +echo "Building edgetx-luac.wasm..." + +"$CC" \ + --target=wasm32-wasi \ + --sysroot="${WASI_SDK}/share/wasi-sysroot" \ + -Os \ + -ffunction-sections \ + -fdata-sections \ + -mllvm -wasm-enable-sjlj \ + -DLUA_HOST_BUILD \ + -DLUA_CROSS_COMPILER \ + -DLUA_COMPAT_5_2 \ + -I"$SRCDIR" \ + -mexec-model=reactor \ + -Wl,--gc-sections \ + -Wl,--strip-debug \ + -Wl,--export-dynamic \ + -Wl,-mllvm,-wasm-enable-sjlj \ + -nodefaultlibs -lc -lsetjmp \ + -o edgetx-luac.wasm \ + "$SRCDIR/luac_wasm.c" \ + "${LUA_CORE_SRC[@]}" + +SIZE=$(wc -c < edgetx-luac.wasm | tr -d ' ') +echo "Done: edgetx-luac.wasm (${SIZE} bytes)" + +# Show WASI imports (ideally very few / none needed at runtime) +if command -v wasm-objdump &>/dev/null; then + echo "" + echo "WASI imports:" + wasm-objdump -j Import -x edgetx-luac.wasm 2>/dev/null | grep wasi || echo " (none)" +fi diff --git a/radio/src/thirdparty/Lua/src/luac.c b/radio/src/thirdparty/Lua/src/luac.c index 67309d07a..388e96772 100644 --- a/radio/src/thirdparty/Lua/src/luac.c +++ b/radio/src/thirdparty/Lua/src/luac.c @@ -9,7 +9,9 @@ #include "lprefix.h" +#if defined(__GNUC__) || defined(__clang__) #include +#endif #include #include #include @@ -21,7 +23,6 @@ #include "lualib.h" #include "lauxlib.h" #include "ldebug.h" -#include "lnodemcu.h" #include "lobject.h" #include "lstate.h" #include "lstring.h" @@ -163,7 +164,7 @@ static int pmain(lua_State *L) { if (argc == 0) return 0; for (i = 0; i < argc; i++) { - const char *filename = IS("-") ? NULL : filelist[i]; + const char *filename = IS("-") ? NULL : argv[i]; if (luaL_loadfile(L, filename) != LUA_OK) fatal(lua_tostring(L, -1)); } diff --git a/radio/src/thirdparty/Lua/src/luac_wasm.c b/radio/src/thirdparty/Lua/src/luac_wasm.c new file mode 100644 index 000000000..14d16633e --- /dev/null +++ b/radio/src/thirdparty/Lua/src/luac_wasm.c @@ -0,0 +1,120 @@ +/* +** EdgeTX Lua cross-compiler — WASM entry point +** +** Exposes a minimal API for compiling Lua source to bytecode entirely +** in memory, suitable for running in a browser via WebAssembly. +** +** Exported functions: +** compile(src_ptr, src_len, strip) → positive length on success, negative on error +** get_output_ptr() → pointer to compiled bytecode +** get_error_ptr() → pointer to error message string +** get_error_len() → length of error message +** wasm_malloc(size) → allocate memory (for JS to write source into) +** wasm_free(ptr) → free allocated memory +*/ + +#define luac_c +#define LUA_CORE + +#include "lprefix.h" + +#include +#include + +#include "lua.h" +#include "lauxlib.h" + +/* --------------- output buffer --------------- */ + +static unsigned char *out_buf; +static size_t out_len; +static size_t out_cap; + +static int writer(lua_State *L, const void *p, size_t size, void *ud) { + (void)L; (void)ud; + if (out_len + size > out_cap) { + size_t new_cap = out_cap ? out_cap * 2 : 4096; + while (new_cap < out_len + size) + new_cap *= 2; + unsigned char *nb = realloc(out_buf, new_cap); + if (!nb) return 1; + out_buf = nb; + out_cap = new_cap; + } + memcpy(out_buf + out_len, p, size); + out_len += size; + return 0; +} + +/* --------------- error message --------------- */ + +static char error_msg[512]; +static int error_len; + +/* --------------- exported API --------------- */ + +#define EXPORT __attribute__((export_name(#name))) +/* clang wasm export attribute helper */ +#define WASM_EXPORT(name) __attribute__((export_name(#name))) name + +/* +** Compile Lua source to bytecode. +** Returns the bytecode length (>0) on success, or a negative error code: +** -1 could not create Lua state +** -2 syntax / compile error (call get_error_ptr/len for message) +** -3 bytecode dump failed +*/ +int WASM_EXPORT(compile)(const char *source, int source_len, int strip) { + error_msg[0] = '\0'; + error_len = 0; + + /* reset output */ + free(out_buf); + out_buf = NULL; + out_len = 0; + out_cap = 0; + + lua_State *L = luaL_newstate(); + if (!L) return -1; + + int status = luaL_loadbuffer(L, source, (size_t)source_len, "input"); + if (status != LUA_OK) { + const char *err = lua_tostring(L, -1); + if (err) { + size_t n = strlen(err); + if (n >= sizeof(error_msg)) n = sizeof(error_msg) - 1; + memcpy(error_msg, err, n); + error_msg[n] = '\0'; + error_len = (int)n; + } + lua_close(L); + return -2; + } + + /* The compiled chunk is a function at the top of the stack. + * lua_dump serialises it via the writer callback. */ + status = lua_dump(L, writer, NULL, strip); + lua_close(L); + + return status == 0 ? (int)out_len : -3; +} + +unsigned char *WASM_EXPORT(get_output_ptr)(void) { + return out_buf; +} + +char *WASM_EXPORT(get_error_ptr)(void) { + return error_msg; +} + +int WASM_EXPORT(get_error_len)(void) { + return error_len; +} + +void *WASM_EXPORT(wasm_malloc)(int size) { + return malloc((size_t)size); +} + +void WASM_EXPORT(wasm_free)(void *ptr) { + free(ptr); +} diff --git a/radio/src/thirdparty/Lua/src/luaconf.h b/radio/src/thirdparty/Lua/src/luaconf.h index 4f679b032..15c53762d 100644 --- a/radio/src/thirdparty/Lua/src/luaconf.h +++ b/radio/src/thirdparty/Lua/src/luaconf.h @@ -462,10 +462,12 @@ #undef lua_str2number #define lua_str2number(s,p) ((lua_Number)strtod((s), (p))) +#if !defined(LUA_HOST_BUILD) // For print #include "debug.h" #define lua_writestring(s,l) TRACE_DEBUG_WP("%s", s); #define lua_writeline() TRACE_DEBUG_WP("\n"); #define lua_writestringerror(s,p) TRACE_DEBUG_WP(s, p); +#endif #endif diff --git a/radio/src/thirdparty/Lua/wasm/.gitignore b/radio/src/thirdparty/Lua/wasm/.gitignore new file mode 100644 index 000000000..b9f02c05d --- /dev/null +++ b/radio/src/thirdparty/Lua/wasm/.gitignore @@ -0,0 +1,2 @@ +.wrangler +*.wasm diff --git a/radio/src/thirdparty/Lua/wasm/edgetx-logo.png b/radio/src/thirdparty/Lua/wasm/edgetx-logo.png new file mode 100644 index 000000000..9c5aca4af Binary files /dev/null and b/radio/src/thirdparty/Lua/wasm/edgetx-logo.png differ diff --git a/radio/src/thirdparty/Lua/wasm/index.html b/radio/src/thirdparty/Lua/wasm/index.html new file mode 100644 index 000000000..1238b779d --- /dev/null +++ b/radio/src/thirdparty/Lua/wasm/index.html @@ -0,0 +1,342 @@ + + + + + +EdgeTX Lua Compiler + + + + + + +
+ EdgeTX +

Lua Compiler

+
+

Compile Lua scripts to EdgeTX bytecode — runs entirely in your browser

+ +
+ + +
+ + +
+ + +
+
+ +
+
+ +
+ Powered by EdgeTX Lua 5.3 + — compiled to WebAssembly, no server needed +
+ + + +