From ed4411602cdd97ade38d443539ab97b310611157 Mon Sep 17 00:00:00 2001 From: danpetry Date: Wed, 28 Mar 2018 17:45:53 +0200 Subject: [PATCH 1/2] pkg/lua: Provide better integration with RIOT - Remove file related functions from loader. * All packages must be builtin. - Remove os.tmpname. - Interface with TLSF. - Don't abort() when out of memory. --- examples/lua/main.c | 55 -- examples/{lua => lua_basic}/Makefile | 8 +- examples/{lua => lua_basic}/README.md | 0 examples/lua_basic/main.c | 61 +++ examples/{lua => lua_basic}/main.lua | 0 pkg/lua/Makefile | 3 +- pkg/lua/Makefile.dep | 3 + pkg/lua/Makefile.include | 2 + pkg/lua/Makefile.lua | 12 +- pkg/lua/contrib/Makefile | 3 + pkg/lua/contrib/binsearch.c | 51 ++ pkg/lua/contrib/binsearch.h | 139 +++++ pkg/lua/contrib/lua_loadlib.c | 263 +++++++++ pkg/lua/contrib/lua_run.c | 283 ++++++++++ pkg/lua/doc.txt | 148 +++++- pkg/lua/include/lua_builtin.h | 84 +++ pkg/lua/include/lua_loadlib.h | 58 ++ pkg/lua/include/lua_run.h | 223 ++++++++ ...endency-on-nonexistent-RIOT-syscalls.patch | 62 --- .../patches/0001-Remove-luaL_newstate.patch | 62 +++ ...RSIZE-to-be-defined-in-the-command-l.patch | 33 ++ ...ke-size-of-LoadF-buffer-configurable.patch | 25 + pkg/lua/patches/0004-Remove-os.tmpname.patch | 95 ++++ ...Do-not-allocate-buffers-on-the-stack.patch | 164 ++++++ .../patches/0006-Cleanup-test-module.patch | 497 ++++++++++++++++++ .../patches/0007-Add-a-proper-makefile.patch | 358 +++++++++++++ ...o-32-bit-build-and-small-buffer-size.patch | 41 ++ 27 files changed, 2603 insertions(+), 130 deletions(-) delete mode 100644 examples/lua/main.c rename examples/{lua => lua_basic}/Makefile (93%) rename examples/{lua => lua_basic}/README.md (100%) create mode 100644 examples/lua_basic/main.c rename examples/{lua => lua_basic}/main.lua (100%) create mode 100644 pkg/lua/Makefile.dep create mode 100644 pkg/lua/contrib/Makefile create mode 100644 pkg/lua/contrib/binsearch.c create mode 100644 pkg/lua/contrib/binsearch.h create mode 100644 pkg/lua/contrib/lua_loadlib.c create mode 100644 pkg/lua/contrib/lua_run.c create mode 100644 pkg/lua/include/lua_builtin.h create mode 100644 pkg/lua/include/lua_loadlib.h create mode 100644 pkg/lua/include/lua_run.h delete mode 100644 pkg/lua/patches/0001-Remove-dependency-on-nonexistent-RIOT-syscalls.patch create mode 100644 pkg/lua/patches/0001-Remove-luaL_newstate.patch create mode 100644 pkg/lua/patches/0002-Allow-LUAL_BUFFERSIZE-to-be-defined-in-the-command-l.patch create mode 100644 pkg/lua/patches/0003-Make-size-of-LoadF-buffer-configurable.patch create mode 100644 pkg/lua/patches/0004-Remove-os.tmpname.patch create mode 100644 pkg/lua/patches/0005-Do-not-allocate-buffers-on-the-stack.patch create mode 100644 pkg/lua/patches/0006-Cleanup-test-module.patch create mode 100644 pkg/lua/patches/0007-Add-a-proper-makefile.patch create mode 100644 pkg/lua/patches/0008-Default-to-32-bit-build-and-small-buffer-size.patch diff --git a/examples/lua/main.c b/examples/lua/main.c deleted file mode 100644 index 22458153e078..000000000000 --- a/examples/lua/main.c +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2018 FU Berlin - * - * This file is subject to the terms and conditions of the GNU Lesser - * General Public License v2.1. See the file LICENSE in the top level - * directory for more details. - */ - -/** - * @ingroup examples - * @{ - * - * @file - * @brief Basic lua example application - * - * @author Daniel Petry - * - * @} - */ - -#include -#include -#include "lauxlib.h" -#include "lualib.h" - -#include "main.lua.h" - -int lua_run_script(const char *buffer, size_t buffer_len) -{ - - lua_State *L = luaL_newstate(); - - if (L == NULL) { - puts("cannot create state: not enough memory"); - return ENOMEM; - } - - luaL_openlibs(L); - luaL_loadbuffer(L, buffer, buffer_len, "lua input script"); - - if (lua_pcall(L, 0, 0, 0) != LUA_OK){ - puts("Lua script running failed"); - return EINTR; - } - - lua_close(L); - return 0; -} - -int main(void) -{ - puts("Lua RIOT build"); - lua_run_script(main_lua, main_lua_len); - return 0; -} diff --git a/examples/lua/Makefile b/examples/lua_basic/Makefile similarity index 93% rename from examples/lua/Makefile rename to examples/lua_basic/Makefile index 57c5c89fd31f..91a55145f5aa 100644 --- a/examples/lua/Makefile +++ b/examples/lua_basic/Makefile @@ -1,4 +1,4 @@ -APPLICATION = lua +APPLICATION = lua_basic # If no BOARD is found in the environment, use this default: BOARD ?= native @@ -29,12 +29,10 @@ DEVELHELP ?= 1 # Change this to 0 show compiler invocation lines by default: QUIET ?= 1 -USEMODULE += ps - ifneq ($(BOARD),native) # This stack size is large enough to run Lua print() functions of # various lengths. Other functions untested. - CFLAGS += -DTHREAD_STACKSIZE_MAIN=4096 + CFLAGS += -DTHREAD_STACKSIZE_MAIN='(THREAD_STACKSIZE_DEFAULT+2048)' endif USEPKG += lua @@ -61,6 +59,6 @@ $(LUA_PATH)/: $(LUA_H): | $(LUA_PATH)/ $(LUA_H): $(LUA_PATH)/%.lua.h: %.lua - xxd -i $< | sed 's/^unsigned/const/g' > $@ + xxd -i $< | sed 's/^unsigned/const unsigned/g' > $@ $(RIOTBUILD_CONFIG_HEADER_C): $(LUA_H) diff --git a/examples/lua/README.md b/examples/lua_basic/README.md similarity index 100% rename from examples/lua/README.md rename to examples/lua_basic/README.md diff --git a/examples/lua_basic/main.c b/examples/lua_basic/main.c new file mode 100644 index 000000000000..0763ab4dae7a --- /dev/null +++ b/examples/lua_basic/main.c @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2018 FU Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief Basic lua example application + * + * @author Daniel Petry + * + * @} + */ + +#include +#include + +#include "lauxlib.h" +#include "lualib.h" +#include "lua_run.h" + +#include "main.lua.h" + +#define LUA_MEM_SIZE (11000) +static char lua_mem[LUA_MEM_SIZE] __attribute__ ((aligned(__BIGGEST_ALIGNMENT__))); + +int lua_run_script(const uint8_t *buffer, size_t buffer_len) +{ + lua_State *L = lua_riot_newstate(lua_mem, sizeof(lua_mem), NULL); + + if (L == NULL) { + puts("cannot create state: not enough memory"); + return ENOMEM; + } + + lua_riot_openlibs(L, LUAR_LOAD_BASE); + luaL_loadbuffer(L, (const char *)buffer, buffer_len, "lua input script"); + + if (lua_pcall(L, 0, 0, 0) != LUA_OK) { + puts("Lua script running failed"); + return EINTR; + } + + lua_close(L); + return 0; +} + +int main(void) +{ + puts("Lua RIOT build"); + lua_run_script(main_lua, main_lua_len); + puts("Lua interpreter exited"); + + return 0; +} diff --git a/examples/lua/main.lua b/examples/lua_basic/main.lua similarity index 100% rename from examples/lua/main.lua rename to examples/lua_basic/main.lua diff --git a/pkg/lua/Makefile b/pkg/lua/Makefile index d2f1a775de8f..69170baa95e8 100644 --- a/pkg/lua/Makefile +++ b/pkg/lua/Makefile @@ -1,11 +1,12 @@ PKG_NAME=lua PKG_URL=https://github.com/lua/lua.git +# tag: v5-3-4 PKG_VERSION=e354c6355e7f48e087678ec49e340ca0696725b1 PKG_LICENSE=MIT .PHONY: all -all: +all: Makefile.lua @cp Makefile.lua $(PKG_BUILDDIR) "$(MAKE)" -C $(PKG_BUILDDIR) -f Makefile.lua diff --git a/pkg/lua/Makefile.dep b/pkg/lua/Makefile.dep new file mode 100644 index 000000000000..2079d072aa51 --- /dev/null +++ b/pkg/lua/Makefile.dep @@ -0,0 +1,3 @@ +USEPKG += tlsf +USEMODULE += lua-contrib +USEMODULE += printf_float diff --git a/pkg/lua/Makefile.include b/pkg/lua/Makefile.include index 9fb515a3b471..ab9d3759b2a4 100644 --- a/pkg/lua/Makefile.include +++ b/pkg/lua/Makefile.include @@ -1 +1,3 @@ INCLUDES += -I$(PKGDIRBASE)/lua +INCLUDES += -I$(RIOTPKG)/lua/include +DIRS += $(RIOTPKG)/lua/contrib diff --git a/pkg/lua/Makefile.lua b/pkg/lua/Makefile.lua index f0bd4d8a386b..b22984b2a187 100644 --- a/pkg/lua/Makefile.lua +++ b/pkg/lua/Makefile.lua @@ -1,9 +1,9 @@ -SRC := $(filter-out lua.c luac.c,$(wildcard *.c)) +SRC := $(filter-out loadlib.c lua.c luac.c,$(wildcard *.c)) -# This builds for native using POSIX system calls and some extra libraries, and -# removes a compiler warning that warns against using tmpnam(). -ifeq ($(BOARD),native) - CFLAGS += -DLUA_USE_LINUX -endif +CFLAGS += -fstack-usage -fconserve-stack \ + -DLUA_MAXCAPTURES=16 \ + -DL_MAXLENNUM=50 +# Enable these options to debug stack usage +# -Wstack-usage=128 -Wno-error=stack-usage=128 include $(RIOTBASE)/Makefile.base diff --git a/pkg/lua/contrib/Makefile b/pkg/lua/contrib/Makefile new file mode 100644 index 000000000000..50d679944127 --- /dev/null +++ b/pkg/lua/contrib/Makefile @@ -0,0 +1,3 @@ +MODULE = lua-contrib + +include $(RIOTBASE)/Makefile.base diff --git a/pkg/lua/contrib/binsearch.c b/pkg/lua/contrib/binsearch.c new file mode 100644 index 000000000000..2aa7f9626429 --- /dev/null +++ b/pkg/lua/contrib/binsearch.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2018 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @file + * + * @brief Generic binary search for tables containing strings. + * @author Juan Carrano + * + */ + +#include + +#include "binsearch.h" + +int binsearch_str(const void *start, size_t offset, size_t stride, size_t nmemb, + const char *str, size_t n) +{ + const uint8_t *cstart = (((const uint8_t *)start) + offset); + size_t lo = 0, hi = nmemb; + + while (lo < hi) { + size_t mid = (lo + hi) / 2; + const char *target = *((const char *const *)(cstart + mid * stride)); + int cmp = strncmp(str, target, n); + + if (cmp == 0) { + return mid; + } + else if (cmp < 0) { + hi = mid; + } + else { /* (cmp > 0) */ + lo = mid + 1; + } + } + return (-ENOENT); +} + +const void *binsearch_str_p(const void *start, size_t offset, size_t stride, + size_t nmemb, const char *str, size_t n) +{ + int ix = binsearch_str(start, offset, stride, nmemb, str, n); + + return (ix == (-ENOENT)) ? NULL : (const uint8_t *)start + ix * stride; +} diff --git a/pkg/lua/contrib/binsearch.h b/pkg/lua/contrib/binsearch.h new file mode 100644 index 000000000000..9be7ca3db4c0 --- /dev/null +++ b/pkg/lua/contrib/binsearch.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2018 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @internal + * @{ + * @file + * + * @brief Generic binary search for tables containing strings. + * @author Juan Carrano + * + * It is often the case that one has an array of structs, where one of the + * members of the struct is a string pointer containing a key that must be + * searched. If the array is sorted by this key and of known length, a binary + * search can be performed. + * + * To make the code generic we must reinterpret the structure array + * as an array of pointers to string with a stride (separation in bytes between + * elements) and offset (position of the first element relative to the start of + * the array) given by the struct definition. + * + * For example, given the following struct and array definitions and assuming + * a 32 bit platform with strict aligment: + * struct s1 { + * int a; // Takes up 4 bytes + * char *name; // Takes up 4 bytes + * char m; // Takes up 1 byte + * }; + * struct s1 my_table[] = {......}; + * + * Then each element of my_table will be aligned to 12 bytes. The address of the + * "name" field of the first elements will be 4 bytes more than the address of + * "my_table". With this two numbers we can compute the address of the i-th + * "name" field as: + * [address of my_table] + offset + i*stride + * Where stride=12 bytes and offset = 4 bytes. + */ + +#ifndef BINSEARCH_H +#define BINSEARCH_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Produce a compiler error if x is not an lvalue. + */ +#define _ENSURE_LVALUE(x) ((void)sizeof(&(x))) + +/** + * UNSAFE MACRO: Difference in bytes between the addresses of two consecutive + * array elements. + */ +#define _ARRAY_STRIDE(arr) ((size_t)((uint8_t *)((arr) + 1) - (uint8_t *)(arr))) + +/** + * UNSAFE MACRO: Offset in bytes from the start of the array to member "member" + * of the first element. + */ +#define _ARRAY_MEMBER_OFFS(arr, member) \ + ((size_t)((uint8_t *)(&((arr)->member)) - (uint8_t *)(arr))) + +/** + * Find the index of the array element that contains "str" in + * member "member". + * + * A compile-time error will be raised if arr is not an lvalue. This ensures the + * macro is safe. + * + * @return Index of the array element containing the string. + * @return (-ENOENT) if it is not found. + */ +#define BINSEARCH_STR(arr, nmemb, member, str, n) \ + (_ENSURE_LVALUE(arr), \ + (binsearch_str((arr), _ARRAY_MEMBER_OFFS(arr, member), _ARRAY_STRIDE(arr), \ + (nmemb), (str), (n))) \ + ) + +/** + * Find a pointer of the array element that contains "str" in + * member "member". + * + * @return Address of the element containing the string (as a void pointer). + * @return Null if it is not found. + */ +#define BINSEARCH_STR_P(arr, nmemb, member, str, n) \ + (_ENSURE_LVALUE(arr), \ + (binsearch_str_p((arr), _ARRAY_MEMBER_OFFS(arr, member), _ARRAY_STRIDE(arr), \ + (nmemb), (str), (n))) \ + ) + +/** + * Search for an array element containing a string. + * + * This does NOT check for NULL pointers, though start can be NULL of the size + * (nmemb) is zero. + * + * @param start Pointer to start of array. The array must be ordered + * according to the search string. + * @param offset Offset of member containing string within structure. This + * can be determined using offsetof. + * @param stride Difference in bytes between the addresses of two consecutive + * array elements. + * @param nmemb Number of elements in the array. + * @param str String that will be compared against. + * @param n Compare up to n characters (see strncmp()) + * + * @return Index of the array element containing the string. + * @return (-ENOENT) if it is not found. + */ +int binsearch_str(const void *start, size_t offset, size_t stride, size_t nmemb, + const char *str, size_t n); + +/** + * Like binsearch_str but returns the pointer to the element. + * + * @return Address of the element containing the string. + * @return Null if it is not found. + */ +const void *binsearch_str_p(const void *start, size_t offset, size_t stride, + size_t nmemb, const char *str, size_t n); + + +#ifdef __cplusplus +} +#endif + +/** @} */ + +#endif /* BINSEARCH_H */ diff --git a/pkg/lua/contrib/lua_loadlib.c b/pkg/lua/contrib/lua_loadlib.c new file mode 100644 index 000000000000..cedc2d193812 --- /dev/null +++ b/pkg/lua/contrib/lua_loadlib.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) 1994-2017 Lua.org, PUC-Rio. + * Copyright (C) 2018 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup pkg_lua + * @{ + * @file + * + * @brief Replacement for the lua "package" module. + * @author Juan Carrano + * @author Roberto Ierusalimschy + * + * This file replaces the loadlib.c that comes with lua. It removes support + * for files (both lua files and c shared objects) and dynamic loading since + * none of these are present in RIOT. + * + * Instead, modules are searched in statically defined tables. In the case + * of C modules, the table contains pointers to C functions that act as module + * loaders. For pure Lua modules, the source code must be given as a string + * embedded in the application binary. + * + */ + +#define loadlib_c +#define LUA_LIB + +#include "lprefix.h" + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +#include "binsearch.h" + +#include "lua_builtin.h" +#include "lua_loadlib.h" + +/* ======================== 'searchers' functions =========================== */ + +static int _ll_searcher_builtin_lua(lua_State *L, const char *name) +{ + const struct lua_riot_builtin_lua *lmodule = + BINSEARCH_STR_P(lua_riot_builtin_lua_table, + lua_riot_builtin_lua_table_len, + name, name, LUAR_MAX_MODULE_NAME); + + if (lmodule != NULL) { + int load_result = luaL_loadbuffer(L, (const char *)lmodule->code, + lmodule->code_size, + lmodule->name); + if (load_result == LUA_OK) { + lua_pushstring(L, name); /* will be 2nd argument to module */ + } + + return load_result; + } + else { + return LUAR_MODULE_NOTFOUND; + } +} + +/** + * Search in the list of pure lua modules. + * + * If the module is found, the source code is compiled and the compiled chunk + * is placed on the lua stack, followed by the module name (as a string). + */ +static int searcher_builtin_lua(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + int load_result = _ll_searcher_builtin_lua(L, name); + + switch (load_result) { + case LUA_OK: + return 2; /* there are two elements in the stack */ + case LUAR_MODULE_NOTFOUND: + return luaL_error(L, "Module '%s' not found in Lua-builtins", + lua_tostring(L, 1)); + default: + return luaL_error(L, "error loading module '%s' from Lua-builtins: \n%s", + lua_tostring(L, 1), lua_tostring(L, 2)); + } +} + +static int _ll_searcher_builtin_c(lua_State *L, const char *name) +{ + const struct lua_riot_builtin_c *cmodule = + BINSEARCH_STR_P(lua_riot_builtin_c_table, + lua_riot_builtin_c_table_len, + name, name, LUAR_MAX_MODULE_NAME); + + if (cmodule != NULL) { + lua_pushcfunction(L, cmodule->luaopen); + lua_pushstring(L, name); /* will be 2nd argument to module */ + return LUA_OK; + } + else { + return LUAR_MODULE_NOTFOUND; + } +} + +/** + * Search in the list of C lua modules. + * + * If the module is found, the loader function is loaded with lua_pushcfunction + * and is returned. + */ +static int searcher_builtin_c(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + int load_result = _ll_searcher_builtin_c(L, name); + + if (load_result == LUA_OK) { + return 2; + } + else { + return luaL_error(L, "Module '%s' not found in C-builtins", + lua_tostring(L, 1)); + } +} + +int lua_riot_getloader(lua_State *L, const char *name) +{ + int load_result; + + load_result = _ll_searcher_builtin_lua(L, name); + + if (load_result == LUAR_MODULE_NOTFOUND) { + load_result = _ll_searcher_builtin_c(L, name); + } + + return load_result; +} + +/* ======================== 'require' function ============================= */ + +static int searcher_preload(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + + lua_getfield(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); + if (lua_getfield(L, -1, name) == LUA_TNIL) { /* not found? */ + lua_pushfstring(L, "\n\tno field package.preload['%s']", name); + } + return 1; +} + +static void findloader(lua_State *L, const char *name) +{ + int i; + luaL_Buffer msg; /* to build error message */ + + luaL_buffinit(L, &msg); + /* push 'package.searchers' to index 3 in the stack */ + if (lua_getfield(L, lua_upvalueindex(1), "searchers") != LUA_TTABLE) { + luaL_error(L, "'package.searchers' must be a table"); + } + /* iterate over available searchers to find a loader */ + for (i = 1;; i++) { + if (lua_rawgeti(L, 3, i) == LUA_TNIL) { /* no more searchers? */ + lua_pop(L, 1); /* remove nil */ + luaL_pushresult(&msg); /* create error message */ + luaL_error(L, "module '%s' not found:%s", name, lua_tostring(L, -1)); + } + lua_pushstring(L, name); + lua_call(L, 1, 2); /* call it */ + if (lua_isfunction(L, -2)) { /* did it find a loader? */ + return; /* module loader found */ + } + else if (lua_isstring(L, -2)) { /* searcher returned error message? */ + lua_pop(L, 1); /* remove extra return */ + luaL_addvalue(&msg); /* concatenate error message */ + } + else { + lua_pop(L, 2); /* remove both returns */ + } + } +} + +static int ll_require(lua_State *L) +{ + const char *name = luaL_checkstring(L, 1); + + lua_settop(L, 1); /* LOADED table will be at index 2 */ + lua_getfield(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + lua_getfield(L, 2, name); /* LOADED[name] */ + if (lua_toboolean(L, -1)) { /* is it there? */ + return 1; /* package is already loaded */ + } + /* else must load package */ + lua_pop(L, 1); /* remove 'getfield' result */ + findloader(L, name); + lua_pushstring(L, name); /* pass name as argument to module loader */ + lua_insert(L, -2); /* name is 1st argument (before search data) */ + lua_call(L, 2, 1); /* run loader to load module */ + if (!lua_isnil(L, -1)) { /* non-nil return? */ + lua_setfield(L, 2, name); /* LOADED[name] = returned value */ + } + if (lua_getfield(L, 2, name) == LUA_TNIL) { /* module set no value? */ + lua_pushboolean(L, 1); /* use true as result */ + lua_pushvalue(L, -1); /* extra copy to be returned */ + lua_setfield(L, 2, name); /* LOADED[name] = true */ + } + return 1; +} + +/* ====================== 'package' module loader =========================== */ + +static const luaL_Reg pk_funcs[] = { + /* placeholders */ + { "preload", NULL }, + { "searchers", NULL }, + { "loaded", NULL }, + { NULL, NULL } +}; + + +static const luaL_Reg ll_funcs[] = { + { "require", ll_require }, + { NULL, NULL } +}; + +LUAMOD_API int luaopen_package(lua_State *L) +{ + static const lua_CFunction searchers[] = + { searcher_preload, searcher_builtin_lua, searcher_builtin_c, NULL }; + int i; + + luaL_newlib(L, pk_funcs); /* create 'package' table */ + + /* create 'searchers' table */ + lua_createtable(L, sizeof(searchers) / sizeof(searchers[0]) - 1, 0); + /* fill it with predefined searchers */ + for (i = 0; searchers[i] != NULL; i++) { + lua_pushvalue(L, -2); /* set 'package' as upvalue for all searchers */ + lua_pushcclosure(L, searchers[i], 1); + lua_rawseti(L, -2, i + 1); + } + + lua_setfield(L, -2, "searchers"); /* put it in field 'searchers' */ + + /* set field 'loaded' */ + luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_LOADED_TABLE); + lua_setfield(L, -2, "loaded"); + + /* set field 'preload' */ + luaL_getsubtable(L, LUA_REGISTRYINDEX, LUA_PRELOAD_TABLE); + lua_setfield(L, -2, "preload"); + + + lua_pushglobaltable(L); + lua_pushvalue(L, -2); /* set 'package' as upvalue for next lib */ + luaL_setfuncs(L, ll_funcs, 1); /* open lib into global table */ + lua_pop(L, 1); /* pop global table */ + return 1; /* return 'package' table */ +} + +/** @} */ diff --git a/pkg/lua/contrib/lua_run.c b/pkg/lua/contrib/lua_run.c new file mode 100644 index 000000000000..fcb57f168956 --- /dev/null +++ b/pkg/lua/contrib/lua_run.c @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2018 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup pkg_lua + * @{ + * @file + * + * @brief Convenience functions for running Lua code. + * @author Juan Carrano + * + */ + +#define LUA_LIB + +#include "lprefix.h" + +#include +#include + +#include "kernel_defines.h" +#include "tlsf.h" + +#include "lua.h" +#include "lualib.h" +#include "lauxlib.h" + +#include "lua_run.h" +#include "lua_loadlib.h" + +const char *lua_riot_str_errors[] = { + "No errors", + "Error setting up the interpreter", + "Error while loading a builtin library", + "Cannot find the specified module", + "Compilation / syntax error", + "Unprotected error (uncaught exception)", + "Out of memory", + "Internal interpreter error", + "Unknown error" +}; + +/* The lua docs state the behavior in these cases: + * + * 1. ptr=?, size=0 -> free(ptr) + therefore ptr=NULL, size=0 -> NOP + * 2. ptr=? , size!=0 -> realloc(ptr, size) + * + * The TLSF code for realloc says: + * / * Zero-size requests are treated as free. * / + * if (ptr && size == 0) + * { + * tlsf_free(tlsf, ptr); + * } + * / * Requests with NULL pointers are treated as malloc. * / + * else if (!ptr) + * { + * p = tlsf_malloc(tlsf, size); + * } + * + * Therefore it is safe to use tlsf_realloc here. + */ +static void *lua_tlsf_alloc(void *ud, void *ptr, size_t osize, size_t nsize) +{ + tlsf_t tlsf = ud; + + (void)osize; + + return tlsf_realloc(tlsf, ptr, nsize); +} + +LUALIB_API lua_State *lua_riot_newstate(void *memory, size_t mem_size, + lua_CFunction panicf) +{ + lua_State *L; + + #ifdef LUA_DEBUG + Memcontrol *mc = memory; + #endif + + /* If we are using the lua debug module, let's reserve a space for the + * memcontrol block directly. We don't use the allocator because we lose + * the pointer, so we won't be able to free it and we will get a false + * positive if we try to check for memory leaks. + */ + #ifdef LUA_DEBUG + memory = (Memcontrol *)memory + 1; + mem_size -= (uint8_t *)memory - (uint8_t *)mc; + #endif + + tlsf_t tlsf = tlsf_create_with_pool(memory, mem_size); + + #ifdef LUA_DEBUG + luaB_init_memcontrol(mc, lua_tlsf_alloc, tlsf); + L = luaB_newstate(mc); + #else + L = lua_newstate(lua_tlsf_alloc, tlsf); + #endif + + if (L != NULL) { + lua_atpanic(L, panicf); + } + + return L; +} + +static const luaL_Reg loadedlibs[LUAR_LOAD_O_ALL] = { + { "_G", luaopen_base }, + { LUA_LOADLIBNAME, luaopen_package }, + { LUA_COLIBNAME, luaopen_coroutine }, + { LUA_TABLIBNAME, luaopen_table }, + { LUA_IOLIBNAME, luaopen_io }, + { LUA_OSLIBNAME, luaopen_os }, + { LUA_STRLIBNAME, luaopen_string }, + { LUA_MATHLIBNAME, luaopen_math }, + { LUA_UTF8LIBNAME, luaopen_utf8 }, + { LUA_DBLIBNAME, luaopen_debug }, +}; + +LUALIB_API int lua_riot_openlibs(lua_State *L, uint16_t modmask) +{ + int lib_index; + + #ifdef LUA_DEBUG + luaL_requiref(L, LUA_TESTLIBNAME, luaB_opentests, 1); + lua_pop(L, 1); + #endif + + for (lib_index = 0; lib_index < LUAR_LOAD_O_ALL; + lib_index++, modmask >>= 1) { + const luaL_Reg *lib = loadedlibs + lib_index; + + if (!(modmask & 1)) { + continue; + } + /* TODO: how can the loading fail? */ + luaL_requiref(L, lib->name, lib->func, 1); + lua_pop(L, 1); /* remove lib from stack (it is already global) */ + } + + return lib_index; +} + +/** + * Jump back to a save point (defined with setjmp). + * + * @note This function never returns! + */ +NORETURN static int _jump_back(lua_State *L) +{ + jmp_buf *jump_buffer = *(jmp_buf **)lua_getextraspace(L); + + /* FIXME: I dont think it's OK to print a message */ + lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n", + lua_tostring(L, -1)); + + /* TODO: try to return some info about the error object. */ + + longjmp(*jump_buffer, 1); +} + +static int lua_riot_do_module_or_buf(const uint8_t *buf, size_t buflen, + const char *modname, void *memory, size_t mem_size, + uint16_t modmask, int *retval) +{ + jmp_buf jump_buffer; + lua_State *volatile L = NULL; + volatile int tmp_retval = 0; /* we need to make it volatile because of the + setjmp/longjmp */ + volatile int status = LUAR_EXIT; + int compilation_result; + + if (setjmp(jump_buffer)) { /* We'll teleport back here if something goes wrong*/ + status = LUAR_INTERNAL_ERR; + goto lua_riot_do_error; + } + + L = lua_riot_newstate(memory, mem_size, _jump_back); + if (L == NULL) { + status = LUAR_STARTUP_ERR; + goto lua_riot_do_error; + } + + /* lua_getextraspace() gives us a pointer to an are large enough to hold a + * pointer. + * + * We store a pointer to the jump buffer in that area. + * + * lua_getextraspace() is therefore a pointer to a pointer to jump_buffer. + */ + *(jmp_buf **)lua_getextraspace(L) = &jump_buffer; + + tmp_retval = lua_riot_openlibs(L, modmask); + if (tmp_retval != LUAR_LOAD_O_ALL) { + status = LUAR_LOAD_ERR; + goto lua_riot_do_error; + } + + if (buf == NULL) { + compilation_result = lua_riot_getloader(L, modname); + } + else { + compilation_result = luaL_loadbufferx(L, (const char *)buf, + buflen, modname, "t"); + } + + switch (compilation_result) { + case LUAR_MODULE_NOTFOUND: + status = LUAR_NOMODULE; + goto lua_riot_do_error; + case LUA_ERRSYNTAX: + status = LUAR_COMPILE_ERR; + goto lua_riot_do_error; + case LUA_ERRMEM: /* fallthrough */ + case LUA_ERRGCMM: /* fallthrough */ + default: + status = LUAR_MEMORY_ERR; + goto lua_riot_do_error; + case LUA_OK: + break; + } + + if (buf != NULL) { + lua_pushstring(L, modname); + } + + switch (lua_pcall(L, 1, 1, 0)) { + case LUA_ERRRUN: /* fallthrough */ + case LUA_ERRGCMM: /* fallthrough */ + default: + status = LUAR_RUNTIME_ERR; + puts(lua_tostring(L, -1)); + goto lua_riot_do_error; + case LUA_ERRMEM: + status = LUAR_MEMORY_ERR; + goto lua_riot_do_error; + case LUA_OK: + break; + } + + tmp_retval = lua_tonumber(L, 1); + +lua_riot_do_error: + + if (L != NULL) { + lua_riot_close(L); + } + + if (retval != NULL) { + *retval = tmp_retval; + } + + return status; +} + + +LUALIB_API int lua_riot_do_module(const char *modname, void *memory, size_t mem_size, + uint16_t modmask, int *retval) +{ + return lua_riot_do_module_or_buf(NULL, 0, modname, memory, mem_size, modmask, + retval); +} + +LUALIB_API int lua_riot_do_buffer(const uint8_t *buf, size_t buflen, void *memory, + size_t mem_size, uint16_t modmask, int *retval) +{ + return lua_riot_do_module_or_buf(buf, buflen, "=BUFFER", memory, mem_size, + modmask, retval); +} + +#define MAX_ERR_STRING ((sizeof(lua_riot_str_errors) / sizeof(*lua_riot_str_errors)) - 1) +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) + +LUALIB_API const char *lua_riot_strerror(int errn) +{ + return lua_riot_str_errors[MIN((unsigned int)errn, MAX_ERR_STRING)]; +} + +/** @} */ diff --git a/pkg/lua/doc.txt b/pkg/lua/doc.txt index 944d45c63261..50d6293ed4a5 100644 --- a/pkg/lua/doc.txt +++ b/pkg/lua/doc.txt @@ -1,6 +1,152 @@ /** * @defgroup pkg_lua Lua ported to RIOT * @ingroup pkg - * @brief Provides Lua support for RIOT + * @brief Provides a Lua interpreter for RIOT * @see https://github.com/lua/lua + * @see sys_lua + * + * # Lua programming language support + * + * ## Introduction + * + * This package embeds a [Lua 5.3](https://www.lua.org/) interpreter into RIOT. + * With a few exceptions, all the APIs mentioned in the + * [official documentation](https://www.lua.org/manual/5.3/) are available in + * this package too. + * + * ## Running Lua code. + * + * lua_run.h contains functions that make it easy to setup the interpreter and + * catch errors in a safe way. The functions from Lua's auxlib can still be used + * but then you must supply your own allocator and panic routines, load the + * builtin modules, etc. + * + * To run a chunk of code stored in an array use: + * ``` + * lua_riot_do_buffer(const char *buf, size_t buflen, void *memory, + * size_t mem_size, uint16_t modmask, int *retval); + * ``` + * The interpreter will not use the global heap for allocations, instead the + * user must supply a memory buffer. + * + * To save some memory, some builtin modules can be left out. `modmask` specifies + * which builtins to load. Note that if a builtin is not loaded by C code, then + * it cannot be loaded by Lua code later. + * + * `lua_riot_do_buffer` takes care of setting up the Lua state, registering a panic + * handler that does not crash the application, configuring an allocator, loading + * libraries, etc. + * + * To run a module as a script use `lua_riot_do_module`. This is roughly equivalent + * to executing: + * ``` + * require('modulename') + * ``` + * + * ## Memory requirements + * + * While generally efficient, the Lua interpreter was not really designed for + * constrained devices. + * + * A basic interpreter session typically requires about 12kB RAM. The stack + * but it depends on the functions used (string handling tends to use more stack). + * It also depends on the platform. + * + * There is currently no easy way to determine the stack needs other than trial + * and error. Future versions of the package will include instrumentation to + * this end. + * + * ## Adding your own modules. + * + * `lua_loadlib.c` contains two loaders, one for modules written in Lua and + * another one for C extensions. + * + * An index to the modules is stored in a table (there are two, one for each + * kind of module). The tables are indexed by the module name and must be sorted + * in ascending order by this key. + * + * The definitions for the table types are in `lua_builtin.h`. The loader module + * containes empty tables, defined as weak symbols so they can be ovewritten + * by the application. The variables that must be defined are: + * + * ``` + * /** Table containing all built in pure lua modules */ + * const struct lua_riot_builtin_lua *const lua_riot_builtin_lua_table; + * /** Number of elements of lua_riot_builtin_lua_table */ + * const size_t lua_riot_builtin_lua_table_len; + * + * /** Table containing all built in c lua modules */ + * const struct lua_riot_builtin_c *const lua_riot_builtin_c_table; + * /** Number of elements of lua_riot_builtin_c_table */ + * const size_t lua_riot_builtin_c_table_len; + * ``` + * + * Currently, these must be defined manually in the application code. In the + * future a script will generate this tables, populating them with both RIOT + * modules and the user modules. + * + * + * ## Customizations + * + * The upstream Lua code is used without with the following modifications. + * + * Modifications that affect the API: + * + * - lua.c (the main interface to the interpreter) is replaced by our own + * stripped-down version. The REPL is no longer included. + * - loadlib.c (the "package" module) is replaced by our own (simplified) + * loader. All the code dealing with files and dynamic loading has been + * removed. + * - os.tmpname() is removed as it caused compiler warnings and it is not + * really possible to use it right. Use io.tmpfile() instead. + * - The test module has been modified to allow it run in the RIOT environment. + * This is not a public API, though. + * + * Other modifications: + * + * - There is a patch changing the Makefile. This updated makefile is not used + * in the package, but is provided to aid development in a PC. + * - Some patches to reduce stack and memory usage. + * + * ### Patches + * + * A version of Lua with the patches applied is available at + * https://github.com/riot-appstore/lua. It can be downloaded and compiled in + * desktop computer, and the official test suite (https://www.lua.org/tests/) + * can then be run. + * + * Alternatively, the patches in this package can be directly applied to the + * official distribution. + * + * The updated makefile creates two standalone executables. Tests should be run + * with the debug executable. + * + * ## TODO + * + * The following features are missing and will be eventually added: + * + * - Load source code incrementally. It can be done now, but then the rest of the + * interpreter setup must be loaded manually. + * - Bindings to access RIOT functionality. + * - Support in the build system for easily including application-specific + * modules. + * - Instrumentation to measure stack consumption (and maybe prevent overflow). + * - Support for "frozen tables" (i.e. tables that live in ROM). + * - Provide a better way of supplying data to a script and getting back results. + * - Specify a function to call inside a module (????) + * - Expand this readme into a proper manual. + * + */ + +/* These are docs for the future (when we have the script to compile module tables) */ +/* + * # Running Lua and C code + * + * see \ref sys_lua for information on how to access RIOT modules from within + * Lua. + * + * While it is possible to include your application specific modules and run + * arbitrary Lua code only just using this interpreter, the \ref sys_lua module + * provides an automated way of handling Lua modules. + * */ diff --git a/pkg/lua/include/lua_builtin.h b/pkg/lua/include/lua_builtin.h new file mode 100644 index 000000000000..fcfec30bdf58 --- /dev/null +++ b/pkg/lua/include/lua_builtin.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2018 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup pkg_lua + * @{ + * @file + * + * @brief Definitions for including built-in modules. + * @author Juan Carrano + * + * The modules must be placed in the tables lua_riot_builtin_lua_table (for lua + * source code) and lua_riot_builtin_c_table (for C extensions) and the lengths + * of these tables must be in lua_riot_builtin_lua_table_len and + * lua_riot_builtin_c_table_len. + * + * These symbols are defined as weak, so there if they are not defined elsewhere + * they will default to zero (or NULL), that is, empty tables. + */ + +#ifndef LUA_BUILTIN_H +#define LUA_BUILTIN_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* Avoid compilation errors where there are no external modules defined */ +/** + * Attribute to make symbols weak. + * + * @todo This should be made part of RIOT. + */ +#define WEAK __attribute__((weak)) + +/** + * Only the first `LUAR_MAX_MODULE_NAME` characters of a module name + * will be considered when performing a lookup. + */ +#define LUAR_MAX_MODULE_NAME 64 + +/** + * Entry describing a pure lua module whose source is built into the + * application binary. + */ +struct lua_riot_builtin_lua { + const char *name; /*!< Name of the module */ + const uint8_t *code; /*!< Lua source code buffer*/ + size_t code_size; /*!< Size of the source code buffer. */ +}; + +/** + * Entry describing a c lua module built into the + * application binary. + */ +struct lua_riot_builtin_c { + const char *name; /*!< Name of the module */ + int (*luaopen)(lua_State *); /*!< Loader function. It must place the module + * table at the top of the lua stack. + * @todo Add better docs. + */ +}; + +/** Table containing all built in pure lua modules */ +extern WEAK const struct lua_riot_builtin_lua *const lua_riot_builtin_lua_table; +/** Number of elements of lua_riot_builtin_lua_table */ +extern WEAK const size_t lua_riot_builtin_lua_table_len; + +/** Table containing all built in c lua modules */ +extern WEAK const struct lua_riot_builtin_c *const lua_riot_builtin_c_table; +/** Number of elements of lua_riot_builtin_c_table */ +extern WEAK const size_t lua_riot_builtin_c_table_len; + +#ifdef __cplusplus +extern "C" } +#endif + +#endif /* LUA_BUILTIN_H */ + +/** @} */ diff --git a/pkg/lua/include/lua_loadlib.h b/pkg/lua/include/lua_loadlib.h new file mode 100644 index 000000000000..f2ce87e14ef5 --- /dev/null +++ b/pkg/lua/include/lua_loadlib.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2018 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup pkg_lua + * @{ + * @file + * + * @brief Lightweight C interface to the package loader. + * @author Juan Carrano + * + */ + +#ifndef LUA_LOADLIB_H +#define LUA_LOADLIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Error code for when a modules is not found. + * + * The numeric value is chosen so that there is no collision with Lua's + * own error codes. + */ +#define LUAR_MODULE_NOTFOUND 50 + +/** + * Load a module as a chunk. + * + * This function is a lightweight "require". It does not require the "package" + * module to be loaded and does not register the module. + * Only the builtin tables are searched. + * + * Upon sucessful execution, the compiled chunk will be at the top of the lua + * stack. + * + * @param L Initialized Lua interpreter state. + * @param name Name of the module. + * + * @return Same as lua_load. If the module is a C-module, then this will + * always succeed and return LUA_OK. + */ +int lua_riot_getloader(lua_State *L, const char *name); + +#ifdef __cplusplus +extern "C" +} +#endif + +#endif /* LUA_LOADLIB_H */ + +/** @} */ diff --git a/pkg/lua/include/lua_run.h b/pkg/lua/include/lua_run.h new file mode 100644 index 000000000000..dcdbde42f9c8 --- /dev/null +++ b/pkg/lua/include/lua_run.h @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2018 Freie Universität Berlin + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ +/** + * @ingroup pkg_lua + * @file + * @{ + * + * @brief Convenience functions for running Lua code. + * @author Juan Carrano + * + * This functions make it easy to create and use new Lua context: + * It provides: + * + * - Easy to use routines for executing modules as scrips. + * - Control over which modules get loaded. + * - Support for using a local heap allocator. + * - Out of memory handling via setjmp/longjmp. + * + * This library is not strictly required, as all of the functionality could be + * implemented in terms of the public lua api, but it covers most of the use + * cases, and thus avoids code repetition in applications. + * + */ + +#ifndef LUA_RUN_H +#define LUA_RUN_H + +#include + +#include "lua.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Convert a library index into a bit mask. + */ +#define LUAR_LOAD_FLAG(n) (((uint16_t)1) << (n)) + +/** + * Order in which the builtin libraries are loaded. + */ +enum LUAR_LOAD_ORDER { + LUAR_LOAD_O_BASE, + LUAR_LOAD_O_PACKAGE, + LUAR_LOAD_O_CORO, + LUAR_LOAD_O_TABLE, + LUAR_LOAD_O_IO, + LUAR_LOAD_O_OS, + LUAR_LOAD_O_STRING, + LUAR_LOAD_O_MATH, + LUAR_LOAD_O_UTF8, + LUAR_LOAD_O_DEBUG, + LUAR_LOAD_O_ALL, +}; + +/** Load the base globals (_G) */ +#define LUAR_LOAD_BASE LUAR_LOAD_FLAG(LUAR_LOAD_O_BASE) +/** Load ´package´ */ +#define LUAR_LOAD_PACKAGE LUAR_LOAD_FLAG(LUAR_LOAD_O_PACKAGE) +/** Load ´coroutine´ */ +#define LUAR_LOAD_CORO LUAR_LOAD_FLAG(LUAR_LOAD_O_CORO) +/** Load ´table´ */ +#define LUAR_LOAD_TABLE LUAR_LOAD_FLAG(LUAR_LOAD_O_TABLE) +/** Load ´io´ */ +#define LUAR_LOAD_IO LUAR_LOAD_FLAG(LUAR_LOAD_O_IO) +/** Load ´os´ */ +#define LUAR_LOAD_OS LUAR_LOAD_FLAG(LUAR_LOAD_O_OS) +/** Load ´string´ */ +#define LUAR_LOAD_STRING LUAR_LOAD_FLAG(LUAR_LOAD_O_STRING) +/** Load ´math´ */ +#define LUAR_LOAD_MATH LUAR_LOAD_FLAG(LUAR_LOAD_O_MATH) +/** Load ´utf8´ */ +#define LUAR_LOAD_UTF8 LUAR_LOAD_FLAG(LUAR_LOAD_O_UTF8) +/** Load ´debug´ */ +#define LUAR_LOAD_DEBUG LUAR_LOAD_FLAG(LUAR_LOAD_O_DEBUG) + +/* TODO: maybe we can implement a "restricted base" package containing a subset + * of base that is safe. */ + +#define LUAR_LOAD_ALL (0xFFFF) /** Load all standard modules */ +#define LUAR_LOAD_NONE (0x0000) /** Do not load any modules */ + +/** Errors that can be raised when running lua code. */ +enum LUAR_ERRORS { + LUAR_EXIT, /** The program exited without error. */ + LUAR_STARTUP_ERR, /** Error setting up the interpreter. */ + LUAR_LOAD_ERR, /** Error while loading libraries. */ + LUAR_NOMODULE, /** The specified module could not be found. */ + LUAR_COMPILE_ERR, /** The Lua code failed to compile. */ + LUAR_RUNTIME_ERR, /** Error in code execution. */ + LUAR_MEMORY_ERR, /** Lua could not allocate enough memory */ + LUAR_INTERNAL_ERR /** Error inside the Lua VM. + * Right now, if this happens, you may leak memory from + * the heap. If your program is the only one using the + * dynamic allocation, just clean the heap. + */ +}; + +/** + * Human-readable description of the errors + */ +extern const char *lua_riot_str_errors[]; + +/** + * Return a string describing an error from LUAR_ERRORS. + * + * @param errn Error number as returned by lua_riot_do_buffer() or + * lua_riot_do_buffer() + * + * @return A string describing the error, or "Unknown error". + */ +LUALIB_API const char *lua_riot_strerror(int errn); + +/** + * Initialize a lua state and set the panic handler. + * + * @todo Use a per-state allocator + * + * @param memory Pointer to memory region that will be used as heap for + * the allocator. Currently this functionality is not + * supported and this must be set to NULL. + * @param mem_size Size of the memory region that will be used as heap. + * Currently this functionality is not supported and this + * must be set to 0. + * @param panicf Function to be passed to lua_atpanic. If set to NULL, + * a generic function that does nothing will be used. + * + * @return the new state, or NULL if there is a memory allocation error. + */ +LUALIB_API lua_State *lua_riot_newstate(void *memory, size_t mem_size, + lua_CFunction panicf); + +/** + * Terminate the lua state. + * + * You must call this function if you want the finalizers (the __gc metamethods) + * to be called. + */ +#ifndef LUA_DEBUG + #define lua_riot_close lua_close +#else + #define lua_riot_close luaB_close +#endif /* LUA_DEBUG */ + +/** + * Open builtin libraries. + * + * This is like luaL_openlibs but it allows selecting which libraries will + * be loaded. + * + * Libraries are loaded in the order specified by the LUAR_LOAD_ORDER enum. If + * there is an error the load sequence is aborted and the index of the library + * that failed is reported. + * + * If debuging is enabled (compile with the LUA_DEBUG macro), then the test + * library will be unconditionally loaded. + * + * @param L Lua state + * @param modmask Binary mask that indicates which modules should be + * loaded. The mask is made from a combination of the + * LUAR_LOAD_* macros. + * + * @return The index of the library that failed to load, or LUAR_LOAD_O_ALL + * if all libraries were loaded. + */ +LUALIB_API int lua_riot_openlibs(lua_State *L, uint16_t modmask); + +/** + * Initialize the interpreter and run a built-in module in protected mode. + * + * In addition to running code in protected mode, this also sets a panic + * function that long-jumps back to this function, in case there is an internal + * interpreter error (LUAR_INTERNAL_ERR). + * Right now the only things that the application can are either to abort(), + * or to manually reset the heap (only if there's no other thread using it). + * + * @param modname name of the module. + * @param memory @see lua_riot_newstate() + * @param mem_size @see lua_riot_newstate() + * @param modmask @see lua_riot_newstate() + * @param[out] retval Value returned by the lua code, if it is a number, + * or zero if no value is returned or the value is not + * a number. If retval is null, the value is not stored. + * + * @return An error code ( @see LUAR_ERRORS). LUAR_EXIT indicates no error. + */ +LUALIB_API int lua_riot_do_module(const char *modname, void *memory, size_t mem_size, + uint16_t modmask, int *retval); + +/** + * Initialize the interpreter and run a user supplied buffer in protected mode. + * + * Only text data (i.e. lua source code) can be loaded by this function. The + * lua interpreter is not robust against corrupt binary code. + * + * @see lua_riot_do_module() for more information on internal errors. + * + * @param buf Text data (lua source code). + * @param buflen Size of the text data in bytes. If buf is + * a zero-terminated string, the zero must not be counted. + * @param memory @see lua_riot_newstate() + * @param mem_size @see lua_riot_newstate() + * @param modmask @see lua_riot_newstate() + * @param[out] retval @see lua_riot_do_module() + * @return @see lua_riot_do_module(). + */ +LUALIB_API int lua_riot_do_buffer(const uint8_t *buf, size_t buflen, void *memory, + size_t mem_size, uint16_t modmask, int *retval); + +#ifdef __cplusplus +extern "C" } +#endif + +/** @} */ + +#endif /* LUA_RUN_H */ diff --git a/pkg/lua/patches/0001-Remove-dependency-on-nonexistent-RIOT-syscalls.patch b/pkg/lua/patches/0001-Remove-dependency-on-nonexistent-RIOT-syscalls.patch deleted file mode 100644 index b4017f538264..000000000000 --- a/pkg/lua/patches/0001-Remove-dependency-on-nonexistent-RIOT-syscalls.patch +++ /dev/null @@ -1,62 +0,0 @@ -From b681cb20ee6bfc31b8ba23ec321140aae6e53e9d Mon Sep 17 00:00:00 2001 -From: danpetry -Date: Wed, 23 May 2018 14:09:17 +0200 -Subject: [PATCH 1/1] Remove dependency on nonexistent RIOT syscalls - -Os.rename and os.clock, in Lua, now always return an error message. -Also, l_randomizePivot, which is used by table.sort, will always -supply 0 rather than a pseudorandom number. ---- - loslib.c | 7 ++----- - ltablib.c | 4 +++- - 2 files changed, 5 insertions(+), 6 deletions(-) - -diff --git a/loslib.c b/loslib.c -index dd2bb378..f9e5cbd2 100644 ---- a/loslib.c -+++ b/loslib.c -@@ -157,9 +157,7 @@ static int os_remove (lua_State *L) { - - - static int os_rename (lua_State *L) { -- const char *fromname = luaL_checkstring(L, 1); -- const char *toname = luaL_checkstring(L, 2); -- return luaL_fileresult(L, rename(fromname, toname) == 0, NULL); -+ return luaL_error(L, "This function is not implemented in RIOT yet"); - } - - -@@ -181,8 +179,7 @@ static int os_getenv (lua_State *L) { - - - static int os_clock (lua_State *L) { -- lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); -- return 1; -+ return luaL_error(L, "This function is not implemented in RIOT yet"); - } - - -diff --git a/ltablib.c b/ltablib.c -index 588bf40d..8895b653 100644 ---- a/ltablib.c -+++ b/ltablib.c -@@ -235,13 +235,15 @@ static int unpack (lua_State *L) { - /* type for array indices */ - typedef unsigned int IdxT; - -- - /* - ** Produce a "random" 'unsigned int' to randomize pivot choice. This - ** macro is used only when 'sort' detects a big imbalance in the result - ** of a partition. (If you don't want/need this "randomness", ~0 is a - ** good choice.) - */ -+ -+#define l_randomizePivot() 0 -+ - #if !defined(l_randomizePivot) /* { */ - - #include --- -2.17.0 - diff --git a/pkg/lua/patches/0001-Remove-luaL_newstate.patch b/pkg/lua/patches/0001-Remove-luaL_newstate.patch new file mode 100644 index 000000000000..f7059b62a91d --- /dev/null +++ b/pkg/lua/patches/0001-Remove-luaL_newstate.patch @@ -0,0 +1,62 @@ +From ef0650e050057db5af2e0b4f934f294656c261a7 Mon Sep 17 00:00:00 2001 +From: Juan Carrano +Date: Tue, 24 Apr 2018 16:38:37 +0200 +Subject: [PATCH 1/8] Remove luaL_newstate. + +--- + lauxlib.c | 25 ------------------------- + lauxlib.h | 2 -- + 2 files changed, 27 deletions(-) + +diff --git a/lauxlib.c b/lauxlib.c +index 7b14ca4d..018a4347 100644 +--- a/lauxlib.c ++++ b/lauxlib.c +@@ -1005,31 +1005,6 @@ LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p, + } + + +-static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { +- (void)ud; (void)osize; /* not used */ +- if (nsize == 0) { +- free(ptr); +- return NULL; +- } +- else +- return realloc(ptr, nsize); +-} +- +- +-static int panic (lua_State *L) { +- lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n", +- lua_tostring(L, -1)); +- return 0; /* return to Lua to abort */ +-} +- +- +-LUALIB_API lua_State *luaL_newstate (void) { +- lua_State *L = lua_newstate(l_alloc, NULL); +- if (L) lua_atpanic(L, &panic); +- return L; +-} +- +- + LUALIB_API void luaL_checkversion_ (lua_State *L, lua_Number ver, size_t sz) { + const lua_Number *v = lua_version(L); + if (sz != LUAL_NUMSIZES) /* check numeric types */ +diff --git a/lauxlib.h b/lauxlib.h +index 1d65c975..77d1494d 100644 +--- a/lauxlib.h ++++ b/lauxlib.h +@@ -89,8 +89,6 @@ LUALIB_API int (luaL_loadbufferx) (lua_State *L, const char *buff, size_t sz, + const char *name, const char *mode); + LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); + +-LUALIB_API lua_State *(luaL_newstate) (void); +- + LUALIB_API lua_Integer (luaL_len) (lua_State *L, int idx); + + LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, +-- +2.17.1 + diff --git a/pkg/lua/patches/0002-Allow-LUAL_BUFFERSIZE-to-be-defined-in-the-command-l.patch b/pkg/lua/patches/0002-Allow-LUAL_BUFFERSIZE-to-be-defined-in-the-command-l.patch new file mode 100644 index 000000000000..bb674624d4d7 --- /dev/null +++ b/pkg/lua/patches/0002-Allow-LUAL_BUFFERSIZE-to-be-defined-in-the-command-l.patch @@ -0,0 +1,33 @@ +From 2e86c4b6b7061b69929d601096e3f0a086ffcc81 Mon Sep 17 00:00:00 2001 +From: Juan Carrano +Date: Thu, 3 May 2018 16:55:05 +0200 +Subject: [PATCH 2/8] Allow LUAL_BUFFERSIZE to be defined in the command line. + +--- + luaconf.h | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/luaconf.h b/luaconf.h +index 118f997a..e816db00 100644 +--- a/luaconf.h ++++ b/luaconf.h +@@ -748,12 +748,16 @@ + ** smaller buffer would force a memory allocation for each call to + ** 'string.format'.) + */ ++#ifndef LUAL_BUFFERSIZE ++ + #if LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE + #define LUAL_BUFFERSIZE 8192 + #else + #define LUAL_BUFFERSIZE ((int)(0x80 * sizeof(void*) * sizeof(lua_Integer))) + #endif + ++#endif ++ + /* }================================================================== */ + + +-- +2.17.1 + diff --git a/pkg/lua/patches/0003-Make-size-of-LoadF-buffer-configurable.patch b/pkg/lua/patches/0003-Make-size-of-LoadF-buffer-configurable.patch new file mode 100644 index 000000000000..0237936119a6 --- /dev/null +++ b/pkg/lua/patches/0003-Make-size-of-LoadF-buffer-configurable.patch @@ -0,0 +1,25 @@ +From bd9e76ed9c9044521537d66fd017a21e98aa53c3 Mon Sep 17 00:00:00 2001 +From: Juan Carrano +Date: Mon, 7 May 2018 13:29:15 +0200 +Subject: [PATCH 3/8] Make size of LoadF buffer configurable. + +--- + lauxlib.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lauxlib.c b/lauxlib.c +index 018a4347..8bd2b5af 100644 +--- a/lauxlib.c ++++ b/lauxlib.c +@@ -635,7 +635,7 @@ LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { + typedef struct LoadF { + int n; /* number of pre-read characters */ + FILE *f; /* file being read */ +- char buff[BUFSIZ]; /* area for reading file */ ++ char buff[LUAL_BUFFERSIZE]; /* area for reading file */ + } LoadF; + + +-- +2.17.1 + diff --git a/pkg/lua/patches/0004-Remove-os.tmpname.patch b/pkg/lua/patches/0004-Remove-os.tmpname.patch new file mode 100644 index 000000000000..c94f3490e433 --- /dev/null +++ b/pkg/lua/patches/0004-Remove-os.tmpname.patch @@ -0,0 +1,95 @@ +From c414df012e8382df3ae078c6dda5554e6c505e31 Mon Sep 17 00:00:00 2001 +From: Juan Carrano +Date: Mon, 7 May 2018 13:59:06 +0200 +Subject: [PATCH 4/8] Remove os.tmpname. + +It is a potential source of bugs, causes compiler warnings when building +without POSIX, behaves differently under POSIX, and cannot be fixed. + +Also, it used a stack variable whose size could be really big under certain +C standard libraries. + +Any program using os.tmpname is should be fixed. +--- + loslib.c | 51 --------------------------------------------------- + 1 file changed, 51 deletions(-) + +diff --git a/loslib.c b/loslib.c +index dd2bb378..69a5f531 100644 +--- a/loslib.c ++++ b/loslib.c +@@ -99,45 +99,6 @@ static time_t l_checktime (lua_State *L, int arg) { + /* }================================================================== */ + + +-/* +-** {================================================================== +-** Configuration for 'tmpnam': +-** By default, Lua uses tmpnam except when POSIX is available, where +-** it uses mkstemp. +-** =================================================================== +-*/ +-#if !defined(lua_tmpnam) /* { */ +- +-#if defined(LUA_USE_POSIX) /* { */ +- +-#include +- +-#define LUA_TMPNAMBUFSIZE 32 +- +-#if !defined(LUA_TMPNAMTEMPLATE) +-#define LUA_TMPNAMTEMPLATE "/tmp/lua_XXXXXX" +-#endif +- +-#define lua_tmpnam(b,e) { \ +- strcpy(b, LUA_TMPNAMTEMPLATE); \ +- e = mkstemp(b); \ +- if (e != -1) close(e); \ +- e = (e == -1); } +- +-#else /* }{ */ +- +-/* ISO C definitions */ +-#define LUA_TMPNAMBUFSIZE L_tmpnam +-#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } +- +-#endif /* } */ +- +-#endif /* } */ +-/* }================================================================== */ +- +- +- +- + static int os_execute (lua_State *L) { + const char *cmd = luaL_optstring(L, 1, NULL); + int stat = system(cmd); +@@ -163,17 +124,6 @@ static int os_rename (lua_State *L) { + } + + +-static int os_tmpname (lua_State *L) { +- char buff[LUA_TMPNAMBUFSIZE]; +- int err; +- lua_tmpnam(buff, err); +- if (err) +- return luaL_error(L, "unable to generate a unique filename"); +- lua_pushstring(L, buff); +- return 1; +-} +- +- + static int os_getenv (lua_State *L) { + lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ + return 1; +@@ -392,7 +342,6 @@ static const luaL_Reg syslib[] = { + {"rename", os_rename}, + {"setlocale", os_setlocale}, + {"time", os_time}, +- {"tmpname", os_tmpname}, + {NULL, NULL} + }; + +-- +2.17.1 + diff --git a/pkg/lua/patches/0005-Do-not-allocate-buffers-on-the-stack.patch b/pkg/lua/patches/0005-Do-not-allocate-buffers-on-the-stack.patch new file mode 100644 index 000000000000..fa9a70d17367 --- /dev/null +++ b/pkg/lua/patches/0005-Do-not-allocate-buffers-on-the-stack.patch @@ -0,0 +1,164 @@ +From 0900faaa14e95b24ad126bb64eebfed60121608b Mon Sep 17 00:00:00 2001 +From: Juan Carrano +Date: Wed, 9 May 2018 14:05:53 +0200 +Subject: [PATCH 5/8] Do not allocate buffers on the stack. + +Change luaL_Buffer so that the memory for the object is always allocated in the heap. +--- + lauxlib.c | 29 +++++++++++++++++++---------- + lauxlib.h | 1 - + lstrlib.c | 6 +++--- + ltablib.c | 2 +- + 4 files changed, 23 insertions(+), 15 deletions(-) + +diff --git a/lauxlib.c b/lauxlib.c +index 8bd2b5af..3767439f 100644 +--- a/lauxlib.c ++++ b/lauxlib.c +@@ -23,6 +23,7 @@ + */ + + #include "lua.h" ++#include "llimits.h" /* to get lua_assert */ + + #include "lauxlib.h" + +@@ -493,10 +494,9 @@ static void *newbox (lua_State *L, size_t newsize) { + + + /* +-** check whether buffer is using a userdata on the stack as a temporary +-** buffer ++** check whether buffer has been assigned a memory region. + */ +-#define buffonstack(B) ((B)->b != (B)->initb) ++#define buffallocated(B) ((B)->b != NULL) + + + /* +@@ -504,6 +504,15 @@ static void *newbox (lua_State *L, size_t newsize) { + */ + LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) { + lua_State *L = B->L; ++ ++ lua_assert((B->size == 0) == (B->b == NULL)); ++ lua_assert((B->n <= B->size)); ++ ++ /* Force an allocation if the requested size is zero and the buffer is not ++ * initialized. This avoids weird bugs */ ++ if (sz == 0 && B->size == 0) ++ sz = 1; ++ + if (B->size - B->n < sz) { /* not enough space? */ + char *newbuff; + size_t newsize = B->size * 2; /* double buffer size */ +@@ -512,11 +521,10 @@ LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) { + if (newsize < B->n || newsize - B->n < sz) + luaL_error(L, "buffer too large"); + /* create larger buffer */ +- if (buffonstack(B)) ++ if (buffallocated(B)) + newbuff = (char *)resizebox(L, -1, newsize); + else { /* no buffer yet */ + newbuff = (char *)newbox(L, newsize); +- memcpy(newbuff, B->b, B->n * sizeof(char)); /* copy original content */ + } + B->b = newbuff; + B->size = newsize; +@@ -542,7 +550,7 @@ LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { + LUALIB_API void luaL_pushresult (luaL_Buffer *B) { + lua_State *L = B->L; + lua_pushlstring(L, B->b, B->n); +- if (buffonstack(B)) { ++ if (buffallocated(B)) { + resizebox(L, -2, 0); /* delete old buffer */ + lua_remove(L, -2); /* remove its header from the stack */ + } +@@ -559,23 +567,24 @@ LUALIB_API void luaL_addvalue (luaL_Buffer *B) { + lua_State *L = B->L; + size_t l; + const char *s = lua_tolstring(L, -1, &l); +- if (buffonstack(B)) ++ if (buffallocated(B)) + lua_insert(L, -2); /* put value below buffer */ + luaL_addlstring(B, s, l); +- lua_remove(L, (buffonstack(B)) ? -2 : -1); /* remove value */ ++ lua_remove(L, -2); /* remove value */ + } + + + LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { + B->L = L; +- B->b = B->initb; ++ B->b = NULL; + B->n = 0; +- B->size = LUAL_BUFFERSIZE; ++ B->size = 0; + } + + + LUALIB_API char *luaL_buffinitsize (lua_State *L, luaL_Buffer *B, size_t sz) { + luaL_buffinit(L, B); ++ + return luaL_prepbuffsize(B, sz); + } + +diff --git a/lauxlib.h b/lauxlib.h +index 77d1494d..b11afc9a 100644 +--- a/lauxlib.h ++++ b/lauxlib.h +@@ -148,7 +148,6 @@ typedef struct luaL_Buffer { + size_t size; /* buffer size */ + size_t n; /* number of characters in buffer */ + lua_State *L; +- char initb[LUAL_BUFFERSIZE]; /* initial buffer */ + } luaL_Buffer; + + +diff --git a/lstrlib.c b/lstrlib.c +index 934b7db8..a978d07a 100644 +--- a/lstrlib.c ++++ b/lstrlib.c +@@ -773,7 +773,7 @@ static int str_gsub (lua_State *L) { + luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || + tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, + "string/function/table expected"); +- luaL_buffinit(L, &b); ++ luaL_buffinitsize(L, &b, luaL_len(L, 1)); + if (anchor) { + p++; lp--; /* skip anchor character */ + } +@@ -1020,7 +1020,7 @@ static int str_format (lua_State *L) { + const char *strfrmt = luaL_checklstring(L, arg, &sfl); + const char *strfrmt_end = strfrmt+sfl; + luaL_Buffer b; +- luaL_buffinit(L, &b); ++ luaL_buffinitsize(L, &b, LUAL_BUFFERSIZE); + while (strfrmt < strfrmt_end) { + if (*strfrmt != L_ESC) + luaL_addchar(&b, *strfrmt++); +@@ -1335,7 +1335,7 @@ static int str_pack (lua_State *L) { + size_t totalsize = 0; /* accumulate total size of result */ + initheader(L, &h); + lua_pushnil(L); /* mark to separate arguments from string buffer */ +- luaL_buffinit(L, &b); ++ luaL_buffinitsize(L, &b, LUAL_BUFFERSIZE); + while (*fmt != '\0') { + int size, ntoalign; + KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); +diff --git a/ltablib.c b/ltablib.c +index 588bf40d..0e50898b 100644 +--- a/ltablib.c ++++ b/ltablib.c +@@ -173,7 +173,7 @@ static int tconcat (lua_State *L) { + const char *sep = luaL_optlstring(L, 2, "", &lsep); + lua_Integer i = luaL_optinteger(L, 3, 1); + last = luaL_optinteger(L, 4, last); +- luaL_buffinit(L, &b); ++ luaL_buffinitsize(L, &b, LUAL_BUFFERSIZE); + for (; i < last; i++) { + addfield(L, &b, i); + luaL_addlstring(&b, sep, lsep); +-- +2.17.1 + diff --git a/pkg/lua/patches/0006-Cleanup-test-module.patch b/pkg/lua/patches/0006-Cleanup-test-module.patch new file mode 100644 index 000000000000..0d22dd9fcf6a --- /dev/null +++ b/pkg/lua/patches/0006-Cleanup-test-module.patch @@ -0,0 +1,497 @@ +From 542105cafbba381ef40c414a4025ea25d20371a2 Mon Sep 17 00:00:00 2001 +From: Juan Carrano +Date: Mon, 14 May 2018 15:19:14 +0200 +Subject: [PATCH 6/8] Cleanup test module. + +This patch makes several improvements to the test module intended +to allow testing inside embedded environments: + +- It does not rely in the standalone intepreter (lua.c). +- Removes all global variables from the test code. +- It allows the user to use the state's extra space (the vanilla + ltest does not reserve any space for the user). +- Makes the instrumented test allocator use the user supply + allocator instead of malloc/free. +- The user must provide a panic function. ltests.c no longer sets + it's own panic function. +- Loading of the test modules is controlled by the LUA_DEBUG + macro. + +This changes should enable heavily customized lua deployments +(such as would be found in an embedded microprocessor) to be +tested with minimal modifications. +--- + linit.c | 4 ++- + ltests.c | 89 ++++++++++++++++++++++++++-------------------- + ltests.h | 105 ++++++++++++++++++++++++++++++++++++------------------- + lua.c | 36 +++++++++++++++++-- + lua.h | 7 +++- + 5 files changed, 163 insertions(+), 78 deletions(-) + +diff --git a/linit.c b/linit.c +index 897ae352..c7ecd387 100644 +--- a/linit.c ++++ b/linit.c +@@ -50,7 +50,9 @@ static const luaL_Reg loadedlibs[] = { + {LUA_MATHLIBNAME, luaopen_math}, + {LUA_UTF8LIBNAME, luaopen_utf8}, + {LUA_DBLIBNAME, luaopen_debug}, +-#if defined(LUA_COMPAT_BITLIB) ++#if defined(LUA_DEBUG) ++ {LUA_TESTLIBNAME, luaB_opentests}, ++#elif defined(LUA_COMPAT_BITLIB) /* No COMPAT and DEBUG at the same time */ + {LUA_BITLIBNAME, luaopen_bit32}, + #endif + {NULL, NULL} +diff --git a/ltests.c b/ltests.c +index 6dba514a..3ca77d2c 100644 +--- a/ltests.c ++++ b/ltests.c +@@ -4,7 +4,6 @@ + ** See Copyright Notice in lua.h + */ + +-#define ltests_c + #define LUA_CORE + + #include "lprefix.h" +@@ -40,12 +39,6 @@ + #if defined(LUA_DEBUG) + + +-void *l_Trick = 0; +- +- +-int islocked = 0; +- +- + #define obj_at(L,k) (L->ci->func + (k)) + + +@@ -64,14 +57,6 @@ static void pushobject (lua_State *L, const TValue *o) { + api_incr_top(L); + } + +- +-static int tpanic (lua_State *L) { +- fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n", +- lua_tostring(L, -1)); +- return (exit(EXIT_FAILURE), 0); /* do not return to Lua */ +-} +- +- + /* + ** {====================================================================== + ** Controlled version for realloc. +@@ -103,11 +88,6 @@ typedef union Header { + + #endif + +- +-Memcontrol l_memcontrol = +- {0L, 0L, 0L, 0L, {0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L}}; +- +- + static void freeblock (Memcontrol *mc, Header *block) { + if (block) { + size_t size = block->d.size; +@@ -116,16 +96,17 @@ static void freeblock (Memcontrol *mc, Header *block) { + lua_assert(*(cast(char *, block + 1) + size + i) == MARK); + mc->objcount[block->d.type]--; + fillmem(block, sizeof(Header) + size + MARKSIZE); /* erase block */ +- free(block); /* actually free block */ ++ mc->alloc_f(mc->alloc_ud, block, size, 0); /* actually free block */ + mc->numblocks--; /* update counts */ + mc->total -= size; + } + } + + +-void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) { ++static void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) { + Memcontrol *mc = cast(Memcontrol *, ud); + Header *block = cast(Header *, b); ++ + int type; + if (mc->memlimit == 0) { /* first time? */ + char *limit = getenv("MEMLIMIT"); /* initialize memory limit */ +@@ -152,7 +133,9 @@ void *debug_realloc (void *ud, void *b, size_t oldsize, size_t size) { + size_t commonsize = (oldsize < size) ? oldsize : size; + size_t realsize = sizeof(Header) + size + MARKSIZE; + if (realsize < size) return NULL; /* arithmetic overflow! */ +- newblock = cast(Header *, malloc(realsize)); /* alloc a new block */ ++ /* alloc a new block */ ++ newblock = cast(Header *, mc->alloc_f(mc->alloc_ud, NULL, oldsize, realsize)); ++ + if (newblock == NULL) return NULL; /* really out of memory? */ + if (block) { + memcpy(newblock + 1, block + 1, commonsize); /* copy old contents */ +@@ -578,18 +561,26 @@ static int get_limits (lua_State *L) { + return 1; + } + ++static Memcontrol *get_memcontrol(lua_State *L) ++{ ++ void *ud; ++ ++ lua_getallocf(L, &ud); ++ ++ return (Memcontrol *)ud; ++} + + static int mem_query (lua_State *L) { + if (lua_isnone(L, 1)) { +- lua_pushinteger(L, l_memcontrol.total); +- lua_pushinteger(L, l_memcontrol.numblocks); +- lua_pushinteger(L, l_memcontrol.maxmem); ++ lua_pushinteger(L, get_memcontrol(L)->total); ++ lua_pushinteger(L, get_memcontrol(L)->numblocks); ++ lua_pushinteger(L, get_memcontrol(L)->maxmem); + return 3; + } + else if (lua_isnumber(L, 1)) { + unsigned long limit = cast(unsigned long, luaL_checkinteger(L, 1)); + if (limit == 0) limit = ULONG_MAX; +- l_memcontrol.memlimit = limit; ++ get_memcontrol(L)->memlimit = limit; + return 0; + } + else { +@@ -597,7 +588,7 @@ static int mem_query (lua_State *L) { + int i; + for (i = LUA_NUMTAGS - 1; i >= 0; i--) { + if (strcmp(t, ttypename(i)) == 0) { +- lua_pushinteger(L, l_memcontrol.objcount[i]); ++ lua_pushinteger(L, get_memcontrol(L)->objcount[i]); + return 1; + } + } +@@ -608,9 +599,9 @@ static int mem_query (lua_State *L) { + + static int settrick (lua_State *L) { + if (ttisnil(obj_at(L, 1))) +- l_Trick = NULL; ++ gettrick(L) = NULL; + else +- l_Trick = gcvalue(obj_at(L, 1)); ++ gettrick(L) = gcvalue(obj_at(L, 1)); + return 0; + } + +@@ -827,13 +818,22 @@ static int num2int (lua_State *L) { + return 1; + } + ++/* ugly way of getting the panic function without changing it. ++ */ ++static lua_CFunction lua_getpanic (lua_State *L) ++{ ++ lua_CFunction panicf = lua_atpanic(L, NULL); ++ lua_atpanic(L, panicf); ++ ++ return panicf; ++} + + static int newstate (lua_State *L) { + void *ud; + lua_Alloc f = lua_getallocf(L, &ud); + lua_State *L1 = lua_newstate(f, ud); + if (L1) { +- lua_atpanic(L1, tpanic); ++ lua_atpanic(L1, lua_getpanic(L)); + lua_pushlightuserdata(L, L1); + } + else +@@ -1549,19 +1549,32 @@ static const struct luaL_Reg tests_funcs[] = { + }; + + +-static void checkfinalmem (void) { +- lua_assert(l_memcontrol.numblocks == 0); +- lua_assert(l_memcontrol.total == 0); ++void luaB_init_memcontrol(Memcontrol *mc, lua_Alloc f, void *ud) ++{ ++ memset(mc, 0, sizeof(*mc)); ++ ++ mc->alloc_f = f; ++ mc->alloc_ud = ud; ++} ++ ++lua_State * luaB_newstate(Memcontrol *mc) ++{ ++ return lua_newstate(debug_realloc, mc); + } + ++void luaB_close(lua_State *L) ++{ ++ Memcontrol * l_memcontrol = get_memcontrol(L); ++ ++ lua_close(L); ++ ++ lua_assert(l_memcontrol->numblocks == 0); ++ lua_assert(l_memcontrol->total == 0); ++} + + int luaB_opentests (lua_State *L) { + void *ud; +- lua_atpanic(L, &tpanic); +- atexit(checkfinalmem); + lua_assert(lua_getallocf(L, &ud) == debug_realloc); +- lua_assert(ud == cast(void *, &l_memcontrol)); +- lua_setallocf(L, lua_getallocf(L, NULL), ud); + luaL_newlib(L, tests_funcs); + return 1; + } +diff --git a/ltests.h b/ltests.h +index 9d26fcb0..8e10670b 100644 +--- a/ltests.h ++++ b/ltests.h +@@ -4,12 +4,18 @@ + ** See Copyright Notice in lua.h + */ + ++ + #ifndef ltests_h + #define ltests_h + ++/* ++** The whole module only makes sense with LUA_DEBUG on ++*/ ++#if defined(LUA_DEBUG) + + #include + ++ + /* test Lua with no compatibility code */ + #undef LUA_COMPAT_MATHLIB + #undef LUA_COMPAT_IPAIRS +@@ -24,9 +30,6 @@ + #undef LUA_COMPAT_MODULE + + +-#define LUA_DEBUG +- +- + /* turn on assertions */ + #undef NDEBUG + #include +@@ -48,6 +51,9 @@ + + /* memory-allocator control variables */ + typedef struct Memcontrol { ++ lua_Alloc alloc_f; ++ void * alloc_ud; ++ + unsigned long numblocks; + unsigned long total; + unsigned long maxmem; +@@ -55,16 +61,6 @@ typedef struct Memcontrol { + unsigned long objcount[LUA_NUMTAGS]; + } Memcontrol; + +-LUA_API Memcontrol l_memcontrol; +- +- +-/* +-** generic variable for debug tricks +-*/ +-extern void *l_Trick; +- +- +- + /* + ** Function to traverse and check all memory used by Lua + */ +@@ -74,38 +70,72 @@ int lua_checkmemory (lua_State *L); + /* test for lock/unlock */ + + struct L_EXTRA { int lock; int *plock; }; ++ ++enum {LUA_OLDEXTRASPACE = LUA_EXTRASPACE }; + #undef LUA_EXTRASPACE +-#define LUA_EXTRASPACE sizeof(struct L_EXTRA) +-#define getlock(l) cast(struct L_EXTRA*, lua_getextraspace(l)) ++ ++struct COMP_L_EXTRA { ++ char user_extraspace[LUA_OLDEXTRASPACE]; ++ struct L_EXTRA debug_extraspace; ++ void *l_Trick; /* generic variable for debug tricks */ ++}; ++ ++#define LUA_EXTRASPACE sizeof(struct COMP_L_EXTRA) ++ ++#define getlock(l) \ ++ (cast(struct COMP_L_EXTRA*, lua_getextraspace(l))->debug_extraspace) ++ ++#define gettrick(l) \ ++ (cast(struct COMP_L_EXTRA*, lua_getextraspace(l))->l_Trick) ++ + #define luai_userstateopen(l) \ +- (getlock(l)->lock = 0, getlock(l)->plock = &(getlock(l)->lock)) ++ (gettrick(l) = 0, getlock(l).lock = 0, getlock(l).plock = &(getlock(l).lock)) + #define luai_userstateclose(l) \ +- lua_assert(getlock(l)->lock == 1 && getlock(l)->plock == &(getlock(l)->lock)) ++ lua_assert(getlock(l).lock == 1 && getlock(l).plock == &(getlock(l).lock)) + #define luai_userstatethread(l,l1) \ +- lua_assert(getlock(l1)->plock == getlock(l)->plock) ++ lua_assert(getlock(l1).plock == getlock(l).plock) + #define luai_userstatefree(l,l1) \ +- lua_assert(getlock(l)->plock == getlock(l1)->plock) +-#define lua_lock(l) lua_assert((*getlock(l)->plock)++ == 0) +-#define lua_unlock(l) lua_assert(--(*getlock(l)->plock) == 0) ++ lua_assert(getlock(l).plock == getlock(l1).plock) ++#define lua_lock(l) lua_assert((*getlock(l).plock)++ == 0) ++#define lua_unlock(l) lua_assert(--(*getlock(l).plock) == 0) + + + ++/* Load the test library and assert that the intepreter is correctly set up ++ * for testing. ++ */ + LUA_API int luaB_opentests (lua_State *L); + +-LUA_API void *debug_realloc (void *ud, void *block, +- size_t osize, size_t nsize); +- +-#if defined(lua_c) +-#define luaL_newstate() lua_newstate(debug_realloc, &l_memcontrol) +-#define luaL_openlibs(L) \ +- { (luaL_openlibs)(L); \ +- luaL_requiref(L, "T", luaB_opentests, 1); \ +- lua_pop(L, 1); } +-#endif +- +- +- +-/* change some sizes to give some bugs a chance */ ++/* Initialize the control block for the test allocator. ++ * The test allocator is a wrapper around the user supplied allocator that ++ * records diagnostic and debug information. ++ * ++ * It uses a Memcontrol structure as the "ud" userdata pointer. Inside this ++ * structure the "real" allocator is stored and will be called to perform the ++ * actual memory operations. ++ * ++ * Set f and ud to your application's allocator. ++ */ ++LUA_API void luaB_init_memcontrol(Memcontrol *mc, lua_Alloc f, void *ud); ++ ++/* Create a new state with a specially instrumented allocator. ++ * ++ * You must supply a properly initialized Memcontrol structure. ++ * */ ++LUA_API lua_State * luaB_newstate(Memcontrol *mc); ++ ++/* Close the lua state and check that all memory has been freed. ++ */ ++LUA_API void luaB_close(lua_State *L); ++ ++#define LUA_TESTLIBNAME "T" ++ ++/* Change some sizes to give some bugs a chance ++ * Activate this macro to make tests harder. ++ * This is not enabled my default because the user may want only the ++ * functionality of the test module. ++ */ ++#ifdef DEBUG_OVERRIDE_SIZES + + #undef LUAL_BUFFERSIZE + #define LUAL_BUFFERSIZE 23 +@@ -125,5 +155,8 @@ LUA_API void *debug_realloc (void *ud, void *block, + #define STRCACHE_N 23 + #define STRCACHE_M 5 + +-#endif ++#endif /* DEBUG_OVERRIDE_SIZES */ ++ ++#endif /* LUA_DEBUG */ + ++#endif /* ltests_h */ +diff --git a/lua.c b/lua.c +index 62de0f58..a41cd305 100644 +--- a/lua.c ++++ b/lua.c +@@ -592,21 +592,53 @@ static int pmain (lua_State *L) { + return 1; + } + ++static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { ++ (void)ud; (void)osize; /* not used */ ++ if (nsize == 0) { ++ free(ptr); ++ return NULL; ++ } ++ else ++ return realloc(ptr, nsize); ++} ++ ++static int panic (lua_State *L) { ++ lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n", ++ lua_tostring(L, -1)); ++ return 0; /* return to Lua to abort */ ++} + + int main (int argc, char **argv) { + int status, result; +- lua_State *L = luaL_newstate(); /* create state */ ++ lua_State *L; ++ ++ #ifdef LUA_DEBUG ++ Memcontrol mc; ++ luaB_init_memcontrol(&mc, l_alloc, NULL); ++ L = luaB_newstate(&mc); ++ #else ++ L = lua_newstate(l_alloc, NULL); ++ #endif /* LUA_DEBUG */ ++ + if (L == NULL) { + l_message(argv[0], "cannot create state: not enough memory"); + return EXIT_FAILURE; + } ++ ++ lua_atpanic(L, &panic); + lua_pushcfunction(L, &pmain); /* to call 'pmain' in protected mode */ + lua_pushinteger(L, argc); /* 1st argument */ + lua_pushlightuserdata(L, argv); /* 2nd argument */ + status = lua_pcall(L, 2, 1, 0); /* do the call */ + result = lua_toboolean(L, -1); /* get result */ + report(L, status); +- lua_close(L); ++ ++ #ifdef LUA_DEBUG ++ luaB_close(L); ++ #else ++ lua_close(L); ++ #endif /* LUA_DEBUG */ ++ + return (result && status == LUA_OK) ? EXIT_SUCCESS : EXIT_FAILURE; + } + +diff --git a/lua.h b/lua.h +index fc4e2388..b40f0d5c 100644 +--- a/lua.h ++++ b/lua.h +@@ -124,7 +124,6 @@ typedef int (*lua_Writer) (lua_State *L, const void *p, size_t sz, void *ud); + typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + + +- + /* + ** generic extra include file + */ +@@ -132,6 +131,12 @@ typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + #include LUA_USER_H + #endif + ++/* (optional) test module ++ * ++ * Note that the ltests.h file will do nothing if LUA_DEBUG is not defined. ++ * This is done this way to avoid having a conditional dependency. ++ */ ++#include "ltests.h" + + /* + ** RCS ident string +-- +2.17.1 + diff --git a/pkg/lua/patches/0007-Add-a-proper-makefile.patch b/pkg/lua/patches/0007-Add-a-proper-makefile.patch new file mode 100644 index 000000000000..f6eb73d24706 --- /dev/null +++ b/pkg/lua/patches/0007-Add-a-proper-makefile.patch @@ -0,0 +1,358 @@ +From 1cfceae0d4d4ead217af7b66ed24b3909eb22dd2 Mon Sep 17 00:00:00 2001 +From: Juan Carrano +Date: Mon, 14 May 2018 15:31:09 +0200 +Subject: [PATCH 7/8] Add a proper makefile. + +The new makefile has automatic dependencies, can buid a debug an +a non debug version simultaneously and is way cleaner. +--- + .gitignore | 7 ++ + Makefile | 116 +++++++++++++++++++++++++++++++ + makefile | 198 ----------------------------------------------------- + 3 files changed, 123 insertions(+), 198 deletions(-) + create mode 100644 .gitignore + create mode 100644 Makefile + delete mode 100644 makefile + +diff --git a/.gitignore b/.gitignore +new file mode 100644 +index 00000000..08e46c95 +--- /dev/null ++++ b/.gitignore +@@ -0,0 +1,7 @@ ++*.o ++*.gch ++*.d ++*.a ++lua ++lua-dbg ++*.patch +diff --git a/Makefile b/Makefile +new file mode 100644 +index 00000000..2735db8d +--- /dev/null ++++ b/Makefile +@@ -0,0 +1,116 @@ ++ ++RM= rm -f ++LD=CC ++ARFLAGS = rcs ++ ++CWARNS= \ ++ -Wall \ ++ -pedantic \ ++ -Wextra \ ++ -Wshadow \ ++ -Wsign-compare \ ++ -Wundef \ ++ -Wwrite-strings \ ++ -Wredundant-decls \ ++ -Wdisabled-optimization \ ++ -Waggregate-return \ ++ -Wdouble-promotion \ ++ -Wdeclaration-after-statement \ ++ -Wmissing-prototypes \ ++ -Wnested-externs \ ++ -Wstrict-prototypes \ ++ -Wc++-compat \ ++ -Wold-style-definition ++ #-Wno-aggressive-loop-optimizations # not accepted by clang \ ++ #-Wlogical-op # not accepted by clang \ ++ # the next warnings generate too much noise, so they are disabled ++ # -Wconversion -Wno-sign-conversion \ ++ # -Wsign-conversion \ ++ # -Wconversion \ ++ # -Wstrict-overflow=2 \ ++ # -Wformat=2 \ ++ # -Wcast-qual \ ++ ++# -DEXTERNMEMCHECK -DHARDSTACKTESTS -DHARDMEMTESTS -DTRACEMEM='"tempmem"' ++# -g -DLUA_USER_H='"ltests.h"' ++# -pg -malign-double ++# -DLUA_USE_CTYPE -DLUA_USE_APICHECK ++# (in clang, '-ftrapv' for runtime checks of integer overflows) ++# -fsanitize=undefined -ftrapv ++TESTFLAGS= -DDEBUG_OVERRIDE_SIZES ++ ++OFLAGS?=-O2 ++CFLAGS?= -std=c99 $(OFLAGS) $(CWARNS) ++DEPFLAGS= -MM -MP -MQ $@ -MQ $*.o -MQ $*-dbg.o ++LIBS?= -lm -ldl -lreadline ++LDFLAGS?=$(LIBS) ++ ++ifeq ($(LUA_32BITS),true) ++CFLAGS+= -DLUA_32BITS ++endif ++ ++# rwildcard ++# Use this function to recursively search for all files with a certain ++# extension ++rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d)) ++ ++ALL_C_FILES=$(call rwildcard,,*.c) ++C_FILES=$(filter-out lua.c,$(ALL_C_FILES)) ++D_FILES=$(call rwildcard,,*.d) ++O_FILES=$(call rwildcard,,*.o) ++GCH_FILES=$(call rwildcard,,*.gch) ++ ++NEEDED_OBJECTS=$(C_FILES:.c=.o) ++DBG_OBJECTS=$(C_FILES:.c=-dbg.o) ++ ++all: lua liblua.a lua-dbg liblua-dbg.a ++ ++# --------------------------- cleaning -------------------------------------- # ++ ++.PHONY: clean ++clean: $(foreach f,$(O_FILES),$(f)-clean) \ ++ $(foreach f,$(GCH_FILES),$(f)-clean) \ ++ liblua.a-clean lua-clean liblua-dbg.a-clean lua-dbg-clean ++ ++PHONY: depclean ++depclean: $(foreach dfile,$(D_FILES),$(dfile)-clean) ++ ++.PHONY: allclean ++allclean: clean depclean ++ ++%-clean: ++ $(RM) $* ++ ++# ----------------------------- DEPENDENCIES --------------------------------- # ++ ++# If we are only cleaning then ignore the dependencies ++ifneq ($(MAKECMDGOALS),depclean) ++ifneq ($(MAKECMDGOALS),clean) ++ifneq ($(MAKECMDGOALS),allclean) ++-include $(C_FILES:.c=.d) ++endif ++endif ++endif ++ ++%.d: %.c ++ $(CC) $(CFLAGS) $(DEPFLAGS) $< >$@ ++ ++# --------------------------- Output targets --------------------------------- # ++ ++liblua.a: $(NEEDED_OBJECTS) ++ ++liblua-dbg.a: $(DBG_OBJECTS) ++ ++lua: lua.o liblua.a ++ ++lua-dbg: lua-dbg.o liblua-dbg.a ++ ++# --------------------------- Implicit rules --------------------------------- # ++ ++%.a: ++ @echo $(MSG_ARCHIVING) ++ $(AR) $(ARFLAGS) $@ $^ ++ ++%-dbg.o: %.c ++ # Override optimization options ++ $(CC) $(CFLAGS) -O0 -g -dA -DLUA_DEBUG -DDEBUG_OVERRIDE_SIZES -c $< -o $@ +diff --git a/makefile b/makefile +deleted file mode 100644 +index 8160d4fb..00000000 +--- a/makefile ++++ /dev/null +@@ -1,198 +0,0 @@ +-# makefile for building Lua +-# see INSTALL for installation instructions +-# see ../Makefile and luaconf.h for further customization +- +-# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT ======================= +- +-# Warnings valid for both C and C++ +-CWARNSCPP= \ +- -pedantic \ +- -Wextra \ +- -Wshadow \ +- -Wsign-compare \ +- -Wundef \ +- -Wwrite-strings \ +- -Wredundant-decls \ +- -Wdisabled-optimization \ +- -Waggregate-return \ +- -Wdouble-promotion \ +- #-Wno-aggressive-loop-optimizations # not accepted by clang \ +- #-Wlogical-op # not accepted by clang \ +- # the next warnings generate too much noise, so they are disabled +- # -Wconversion -Wno-sign-conversion \ +- # -Wsign-conversion \ +- # -Wconversion \ +- # -Wstrict-overflow=2 \ +- # -Wformat=2 \ +- # -Wcast-qual \ +- +-# The next warnings are neither valid nor needed for C++ +-CWARNSC= -Wdeclaration-after-statement \ +- -Wmissing-prototypes \ +- -Wnested-externs \ +- -Wstrict-prototypes \ +- -Wc++-compat \ +- -Wold-style-definition \ +- +- +-CWARNS= $(CWARNSCPP) $(CWARNSC) +- +- +-# -DEXTERNMEMCHECK -DHARDSTACKTESTS -DHARDMEMTESTS -DTRACEMEM='"tempmem"' +-# -g -DLUA_USER_H='"ltests.h"' +-# -pg -malign-double +-# -DLUA_USE_CTYPE -DLUA_USE_APICHECK +-# (in clang, '-ftrapv' for runtime checks of integer overflows) +-# -fsanitize=undefined -ftrapv +-TESTS= -DLUA_USER_H='"ltests.h"' +- +-# -mtune=native -fomit-frame-pointer +-# -fno-stack-protector +-LOCAL = $(TESTS) $(CWARNS) -g +- +- +- +-# enable Linux goodies +-MYCFLAGS= $(LOCAL) -std=c99 -DLUA_USE_LINUX -DLUA_COMPAT_5_2 +-MYLDFLAGS= $(LOCAL) -Wl,-E +-MYLIBS= -ldl -lreadline +- +- +-CC= clang-3.8 +-CFLAGS= -Wall -O2 $(MYCFLAGS) +-AR= ar rcu +-RANLIB= ranlib +-RM= rm -f +- +- +- +-# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE ========= +- +- +-LIBS = -lm +- +-CORE_T= liblua.a +-CORE_O= lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o \ +- lmem.o lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o \ +- ltm.o lundump.o lvm.o lzio.o ltests.o +-AUX_O= lauxlib.o +-LIB_O= lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o lstrlib.o \ +- lutf8lib.o lbitlib.o loadlib.o lcorolib.o linit.o +- +-LUA_T= lua +-LUA_O= lua.o +- +-# LUAC_T= luac +-# LUAC_O= luac.o print.o +- +-ALL_T= $(CORE_T) $(LUA_T) $(LUAC_T) +-ALL_O= $(CORE_O) $(LUA_O) $(LUAC_O) $(AUX_O) $(LIB_O) +-ALL_A= $(CORE_T) +- +-all: $(ALL_T) +- +-o: $(ALL_O) +- +-a: $(ALL_A) +- +-$(CORE_T): $(CORE_O) $(AUX_O) $(LIB_O) +- $(AR) $@ $? +- $(RANLIB) $@ +- +-$(LUA_T): $(LUA_O) $(CORE_T) +- $(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(CORE_T) $(LIBS) $(MYLIBS) $(DL) +- +-$(LUAC_T): $(LUAC_O) $(CORE_T) +- $(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(CORE_T) $(LIBS) $(MYLIBS) +- +-clean: +- rcsclean -u +- $(RM) $(ALL_T) $(ALL_O) +- +-depend: +- @$(CC) $(CFLAGS) -MM *.c +- +-echo: +- @echo "CC = $(CC)" +- @echo "CFLAGS = $(CFLAGS)" +- @echo "AR = $(AR)" +- @echo "RANLIB = $(RANLIB)" +- @echo "RM = $(RM)" +- @echo "MYCFLAGS = $(MYCFLAGS)" +- @echo "MYLDFLAGS = $(MYLDFLAGS)" +- @echo "MYLIBS = $(MYLIBS)" +- @echo "DL = $(DL)" +- +-$(ALL_O): makefile +- +-# DO NOT EDIT +-# automatically made with 'gcc -MM l*.c' +- +-lapi.o: lapi.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ +- lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lstring.h \ +- ltable.h lundump.h lvm.h +-lauxlib.o: lauxlib.c lprefix.h lua.h luaconf.h lauxlib.h +-lbaselib.o: lbaselib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +-lbitlib.o: lbitlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +-lcode.o: lcode.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \ +- llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \ +- ldo.h lgc.h lstring.h ltable.h lvm.h +-lcorolib.o: lcorolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +-lctype.o: lctype.c lprefix.h lctype.h lua.h luaconf.h llimits.h +-ldblib.o: ldblib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +-ldebug.o: ldebug.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ +- lobject.h ltm.h lzio.h lmem.h lcode.h llex.h lopcodes.h lparser.h \ +- ldebug.h ldo.h lfunc.h lstring.h lgc.h ltable.h lvm.h +-ldo.o: ldo.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ +- lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h lopcodes.h \ +- lparser.h lstring.h ltable.h lundump.h lvm.h +-ldump.o: ldump.c lprefix.h lua.h luaconf.h lobject.h llimits.h lstate.h \ +- ltm.h lzio.h lmem.h lundump.h +-lfunc.o: lfunc.c lprefix.h lua.h luaconf.h lfunc.h lobject.h llimits.h \ +- lgc.h lstate.h ltm.h lzio.h lmem.h +-lgc.o: lgc.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ +- llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h +-linit.o: linit.c lprefix.h lua.h luaconf.h lualib.h lauxlib.h +-liolib.o: liolib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +-llex.o: llex.c lprefix.h lua.h luaconf.h lctype.h llimits.h ldebug.h \ +- lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lgc.h llex.h lparser.h \ +- lstring.h ltable.h +-lmathlib.o: lmathlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +-lmem.o: lmem.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ +- llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h +-loadlib.o: loadlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +-lobject.o: lobject.c lprefix.h lua.h luaconf.h lctype.h llimits.h \ +- ldebug.h lstate.h lobject.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h \ +- lvm.h +-lopcodes.o: lopcodes.c lprefix.h lopcodes.h llimits.h lua.h luaconf.h +-loslib.o: loslib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +-lparser.o: lparser.c lprefix.h lua.h luaconf.h lcode.h llex.h lobject.h \ +- llimits.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h \ +- ldo.h lfunc.h lstring.h lgc.h ltable.h +-lstate.o: lstate.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ +- lobject.h ltm.h lzio.h lmem.h ldebug.h ldo.h lfunc.h lgc.h llex.h \ +- lstring.h ltable.h +-lstring.o: lstring.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \ +- lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h +-lstrlib.o: lstrlib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +-ltable.o: ltable.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ +- llimits.h ltm.h lzio.h lmem.h ldo.h lgc.h lstring.h ltable.h lvm.h +-ltablib.o: ltablib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +-ltests.o: ltests.c lprefix.h lua.h luaconf.h lapi.h llimits.h lstate.h \ +- lobject.h ltm.h lzio.h lmem.h lauxlib.h lcode.h llex.h lopcodes.h \ +- lparser.h lctype.h ldebug.h ldo.h lfunc.h lstring.h lgc.h ltable.h \ +- lualib.h +-ltm.o: ltm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ +- llimits.h ltm.h lzio.h lmem.h ldo.h lstring.h lgc.h ltable.h lvm.h +-lua.o: lua.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +-lundump.o: lundump.c lprefix.h lua.h luaconf.h ldebug.h lstate.h \ +- lobject.h llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h \ +- lundump.h +-lutf8lib.o: lutf8lib.c lprefix.h lua.h luaconf.h lauxlib.h lualib.h +-lvm.o: lvm.c lprefix.h lua.h luaconf.h ldebug.h lstate.h lobject.h \ +- llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h \ +- ltable.h lvm.h +-lzio.o: lzio.c lprefix.h lua.h luaconf.h llimits.h lmem.h lstate.h \ +- lobject.h ltm.h lzio.h +- +-# (end of Makefile) +-- +2.17.1 + diff --git a/pkg/lua/patches/0008-Default-to-32-bit-build-and-small-buffer-size.patch b/pkg/lua/patches/0008-Default-to-32-bit-build-and-small-buffer-size.patch new file mode 100644 index 000000000000..f9b79b0737be --- /dev/null +++ b/pkg/lua/patches/0008-Default-to-32-bit-build-and-small-buffer-size.patch @@ -0,0 +1,41 @@ +From a3bb3e20c12dd7436fe4c3ec4c4f09b98ed0c443 Mon Sep 17 00:00:00 2001 +From: Juan Carrano +Date: Tue, 29 May 2018 11:55:49 +0200 +Subject: [PATCH 8/8] Default to 32 bit build and small buffer size. + +Set the word length and buffer size in luaconf.h so that it doesn't have to +be specified in the compiler's command line each time. + +LUAL_BUFFERSIZE can still be overrriden by -DLUAL_BUFFERSIZE=xxxx. +To force a 64 bit build, define LUA_64BIT. +--- + luaconf.h | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/luaconf.h b/luaconf.h +index e816db00..8093833d 100644 +--- a/luaconf.h ++++ b/luaconf.h +@@ -33,7 +33,9 @@ + ** ensure that all software connected to Lua will be compiled with the + ** same configuration. + */ +-/* #define LUA_32BITS */ ++#ifndef LUA_64BITS ++#define LUA_32BITS ++#endif + + + /* +@@ -753,7 +755,7 @@ + #if LUA_FLOAT_TYPE == LUA_FLOAT_LONGDOUBLE + #define LUAL_BUFFERSIZE 8192 + #else +-#define LUAL_BUFFERSIZE ((int)(0x80 * sizeof(void*) * sizeof(lua_Integer))) ++#define LUAL_BUFFERSIZE 64 + #endif + + #endif +-- +2.17.1 + From 87496f337688f351b7743a1da73c7e770042d488 Mon Sep 17 00:00:00 2001 From: Juan Carrano Date: Mon, 2 Jul 2018 14:45:31 +0200 Subject: [PATCH 2/2] examples/lua: Add REPL. This example add a module thats starts an interactive READ-EVAL- PRINT-LOOP written in Lua. --- examples/lua_REPL/Makefile | 78 ++++++++++++++++++++++++++++++ examples/lua_REPL/README.md | 32 +++++++++++++ examples/lua_REPL/main.c | 63 +++++++++++++++++++++++++ examples/lua_REPL/repl.lua | 94 +++++++++++++++++++++++++++++++++++++ 4 files changed, 267 insertions(+) create mode 100644 examples/lua_REPL/Makefile create mode 100644 examples/lua_REPL/README.md create mode 100644 examples/lua_REPL/main.c create mode 100644 examples/lua_REPL/repl.lua diff --git a/examples/lua_REPL/Makefile b/examples/lua_REPL/Makefile new file mode 100644 index 000000000000..fb397b5319ff --- /dev/null +++ b/examples/lua_REPL/Makefile @@ -0,0 +1,78 @@ +APPLICATION = lua_repl + +# If no BOARD is found in the environment, use this default: +BOARD ?= native + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +BOARD_INSUFFICIENT_MEMORY := bluepill calliope-mini cc2650-launchpad \ + cc2650stk maple-mini microbit nrf51dongle \ + nucleo-f030r8 nucleo-f031k6 nucleo-f042k6 \ + nucleo-f070rb nucleo-f072rb nucleo-f103rb \ + nucleo-f302r8 nucleo-f303k8 nucleo-f334r8 \ + nucleo-f410rb nucleo-l031k6 nucleo-l053r8 \ + opencm904 spark-core stm32f0discovery \ + stm32mindev airfy-beacon arduino-mkr1000 \ + arduino-mkrfox1200 arduino-mkrzero arduino-zero \ + b-l072z-lrwan1 cc2538dk ek-lm4f120xl feather-m0 \ + ikea-tradfri limifrog-v1 mbed_lpc1768 nrf6310 \ + nucleo-f091rc nucleo-l073rz nz32-sc151 \ + openmote-cc2538 pba-d-01-kw2x remote-pa \ + remote-reva remote-revb samd21-xpro saml21-xpro \ + samr21-xpro seeeduino_arch-pro slstk3401a \ + sltb001a slwstk6220a sodaq-autonomo \ + sodaq-explorer stk3600 stm32f3discovery \ + yunjia-nrf51822 + +BOARD_BLACKLIST := arduino-duemilanove arduino-mega2560 arduino-uno \ + chronos hifive1 jiminy-mega256rfr2 mega-xplained mips-malta \ + msb-430 msb-430h pic32-clicker pic32-wifire telosb \ + waspmote-pro wsn430-v1_3b wsn430-v1_4 z1 + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +# Uncomment the following lines to enable debugging features. +#CFLAGS_OPT = -Og +#CFLAGS += -DDEBUG_ASSERT_VERBOSE -DLUA_DEBUG + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +# This value is in excess because we are not sure of the exact requirements of +# lua (see the package's docs). It must be fixed in the future by taking +# appropriate measurements. +CFLAGS += -DTHREAD_STACKSIZE_MAIN='(THREAD_STACKSIZE_DEFAULT+7000)' + +USEPKG += lua + +include $(RIOTBASE)/Makefile.include + +# The code below generates a header file from any .lua scripts in the +# example directory. The header file contains a byte array of the +# ASCII characters in the .lua script. + +LUA_PATH := $(BINDIR)/lua + +# add directory of generated *.lua.h files to include path +CFLAGS += -I$(LUA_PATH) + +# generate .lua.h header files of .lua files +LUA = $(wildcard *.lua) + +LUA_H := $(LUA:%.lua=$(LUA_PATH)/%.lua.h) + +$(LUA_PATH)/: + @mkdir -p $@ + +# FIXME: This way of embedding lua code is not robust. A proper script will +# be included later. + +$(LUA_H): | $(LUA_PATH)/ +$(LUA_H): $(LUA_PATH)/%.lua.h: %.lua + xxd -i $< | sed 's/^unsigned/const unsigned/g' > $@ + +$(RIOTBUILD_CONFIG_HEADER_C): $(LUA_H) diff --git a/examples/lua_REPL/README.md b/examples/lua_REPL/README.md new file mode 100644 index 000000000000..fd1fb42ae9fd --- /dev/null +++ b/examples/lua_REPL/README.md @@ -0,0 +1,32 @@ +## Lua interactive interpreter + +### About + +This example shows how to run a [Lua](https://www.lua.org/) Read-Eval-Print loop. +It works in a similar way to the lua shell that comes with your operating +system's default lua installation. + + +### How to run + +Type `make all flash` to program your board. The lua interpreter communicates +via UART (like the shell). + +It is not recommended to use `make term` because the default RIOT terminal messes +up the input and output and the REPL needs multi-line input. Instead, use something +like `miniterm.py` from pyserial: + +``` +miniterm.py --eol LF --echo /dev/ttyACM0 115200 +``` + +By default only some of the builtin modules are loaded, to preserve RAM. See +the definition of `BARE_MINIMUM_MODS` in main.c. + +### Using the interpreter + +See the [Lua manual](https://www.lua.org/manual/5.3/) for the syntax of the language. + +Each piece of single or multi-line input is compiled as a chunk and run. For this +reason, issuing "local" definitions may not work as expected: the definitions +will be local to that chunk only. diff --git a/examples/lua_REPL/main.c b/examples/lua_REPL/main.c new file mode 100644 index 000000000000..f985130430ad --- /dev/null +++ b/examples/lua_REPL/main.c @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2018 Freie Universität Berlin. + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup examples + * @{ + * + * @file + * @brief Lua shell in RIOT + * + * @author Juan Carrano + * + * @} + */ + +#include +#include + +#include "lua_run.h" +#include "lua_builtin.h" +#include "repl.lua.h" + +/* The basic interpreter+repl needs about 13k ram AT Minimum but we need more + * memory in order to do interesting stuff. + */ +#define MAIN_LUA_MEM_SIZE (40000) + +static char lua_memory[MAIN_LUA_MEM_SIZE] __attribute__ ((aligned(__BIGGEST_ALIGNMENT__))); + +#define BARE_MINIMUM_MODS (LUAR_LOAD_BASE | LUAR_LOAD_IO | LUAR_LOAD_CORO | LUAR_LOAD_PACKAGE) + +const struct lua_riot_builtin_lua _lua_riot_builtin_lua_table[] = { + { "repl", repl_lua, sizeof(repl_lua) } +}; + + +const struct lua_riot_builtin_lua *const lua_riot_builtin_lua_table = _lua_riot_builtin_lua_table; + +const size_t lua_riot_builtin_lua_table_len = 1; + +int main(void) +{ + printf("Using memory range for Lua heap: %p - %p, %zu bytes\n", + lua_memory, lua_memory + MAIN_LUA_MEM_SIZE, sizeof(void *)); + + while (1) { + int status, value; + puts("This is Lua: starting interactive session\n"); + + status = lua_riot_do_module("repl", lua_memory, MAIN_LUA_MEM_SIZE, + BARE_MINIMUM_MODS, &value); + + printf("Exited. status: %s, return code %d\n", lua_riot_strerror(status), + value); + } + + return 0; +} diff --git a/examples/lua_REPL/repl.lua b/examples/lua_REPL/repl.lua new file mode 100644 index 000000000000..ff81f4241e4a --- /dev/null +++ b/examples/lua_REPL/repl.lua @@ -0,0 +1,94 @@ +--[[ + @file repl.lua + @brief Read-eval-print loop for LUA + @author Juan Carrano + Copyright (C) 2018 Freie Universität Berlin. Distributed under the GNU Lesser General Public License v2.1. +]] + +local _R_EVAL = 0 +local _R_CONT = 1 +local _R_EXIT = 2 +local _R_ERROR = 3 + +--[[ Read code from standard input (whatever stdin means for lua) + @return action_code what the eval loop should do + @return code_or_msg either code (_R_EVAL) or a message (_R_ERROR) or nil + (_R_CONT, _R_EXIT). +]] + +local function re() + io.write("L> ") + io.flush() + local ln = io.read() + + if not ln then + return _R_EXIT + elseif ln == "\n" then + return _R_CONT + end + -- Try to see if we have an expression + local maybe_code, compile_err = load("return "..ln) + -- Try to see if we have a single-line statement + if not maybe_code then + maybe_code, compile_err = load(ln) + end + -- Try a multiline statement + if not maybe_code then + -- It's not really necessary to use a coroutine, but it shows that they + -- work. + local _get_multiline = coroutine.create( + function () + coroutine.yield(ln.."\n") -- We already have the first line of input + while 1 do + io.write("L.. ") + io.flush() + local l = io.read() + if #l ~= 0 then + l = l.."\n" + end + coroutine.yield(l) + end + end + ) + local get_multiline = function() + local a, b = coroutine.resume(_get_multiline) + if a then + return b + else + return nil + end + end + + maybe_code, compile_err = load(get_multiline) + end + + if not maybe_code then + return _R_ERROR, compile_err + else + return _R_EVAL, maybe_code + end +end + +local function repl() + io.write("Welcome to the interactive interpreter\n"); + + while 1 do + local action, fn_or_message = re() + + if action == _R_EVAL then + local success, msg_or_ret = pcall(fn_or_message) + if not success then + print("Runtime error", msg_or_ret) + elseif msg_or_ret ~= nil then + print(msg_or_ret) + end + elseif action == _R_EXIT then + print() + return + elseif action == _R_ERROR then + print("Compile error:", fn_or_message) + end -- (action == _R_CONT) : do nothing + end +end + +repl()