1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

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.
This commit is contained in:
danpetry 2018-03-28 17:45:53 +02:00 committed by Juan Carrano
parent 32e823acb2
commit ed4411602c
27 changed files with 1328 additions and 68 deletions

View File

@ -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 <daniel.petry@fu-berlin.de>
*
* @}
*/
#include <stdio.h>
#include <errno.h>
#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;
}

View File

@ -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)

61
examples/lua_basic/main.c Normal file
View File

@ -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 <daniel.petry@fu-berlin.de>
*
* @}
*/
#include <stdio.h>
#include <errno.h>
#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;
}

View File

@ -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

3
pkg/lua/Makefile.dep Normal file
View File

@ -0,0 +1,3 @@
USEPKG += tlsf
USEMODULE += lua-contrib
USEMODULE += printf_float

View File

@ -1 +1,3 @@
INCLUDES += -I$(PKGDIRBASE)/lua
INCLUDES += -I$(RIOTPKG)/lua/include
DIRS += $(RIOTPKG)/lua/contrib

View File

@ -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

3
pkg/lua/contrib/Makefile Normal file
View File

@ -0,0 +1,3 @@
MODULE = lua-contrib
include $(RIOTBASE)/Makefile.base

View File

@ -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 <j.carrano@fu-berlin.de>
*
*/
#include <string.h>
#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;
}

139
pkg/lua/contrib/binsearch.h Normal file
View File

@ -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 <j.carrano@fu-berlin.de>
*
* 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 <stdint.h>
#include <errno.h>
#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 */

View File

@ -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 <j.carrano@fu-berlin.de>
* @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 */
}
/** @} */

283
pkg/lua/contrib/lua_run.c Normal file
View File

@ -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 <j.carrano@fu-berlin.de>
*
*/
#define LUA_LIB
#include "lprefix.h"
#include <stdio.h>
#include <setjmp.h>
#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)];
}
/** @} */

View File

@ -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.
*
*/

View File

@ -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 <j.carrano@fu-berlin.de>
*
* 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 */
/** @} */

View File

@ -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 <j.carrano@fu-berlin.de>
*
*/
#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 */
/** @} */

223
pkg/lua/include/lua_run.h Normal file
View File

@ -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 <j.carrano@fu-berlin.de>
*
* 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 <stdint.h>
#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 */

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.