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 2603 additions and 130 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 */

View File

@ -1,62 +0,0 @@
From b681cb20ee6bfc31b8ba23ec321140aae6e53e9d Mon Sep 17 00:00:00 2001
From: danpetry <daniel.petry@fu-berlin.de>
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 <time.h>
--
2.17.0

View File

@ -0,0 +1,62 @@
From ef0650e050057db5af2e0b4f934f294656c261a7 Mon Sep 17 00:00:00 2001
From: Juan Carrano <j.carrano@fu-berlin.de>
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

View File

@ -0,0 +1,33 @@
From 2e86c4b6b7061b69929d601096e3f0a086ffcc81 Mon Sep 17 00:00:00 2001
From: Juan Carrano <j.carrano@fu-berlin.de>
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

View File

@ -0,0 +1,25 @@
From bd9e76ed9c9044521537d66fd017a21e98aa53c3 Mon Sep 17 00:00:00 2001
From: Juan Carrano <j.carrano@fu-berlin.de>
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

View File

@ -0,0 +1,95 @@
From c414df012e8382df3ae078c6dda5554e6c505e31 Mon Sep 17 00:00:00 2001
From: Juan Carrano <j.carrano@fu-berlin.de>
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 <unistd.h>
-
-#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

View File

@ -0,0 +1,164 @@
From 0900faaa14e95b24ad126bb64eebfed60121608b Mon Sep 17 00:00:00 2001
From: Juan Carrano <j.carrano@fu-berlin.de>
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

View File

@ -0,0 +1,497 @@
From 542105cafbba381ef40c414a4025ea25d20371a2 Mon Sep 17 00:00:00 2001
From: Juan Carrano <j.carrano@fu-berlin.de>
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 <stdlib.h>
+
/* 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 <assert.h>
@@ -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

View File

@ -0,0 +1,358 @@
From 1cfceae0d4d4ead217af7b66ed24b3909eb22dd2 Mon Sep 17 00:00:00 2001
From: Juan Carrano <j.carrano@fu-berlin.de>
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

View File

@ -0,0 +1,41 @@
From a3bb3e20c12dd7436fe4c3ec4c4f09b98ed0c443 Mon Sep 17 00:00:00 2001
From: Juan Carrano <j.carrano@fu-berlin.de>
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