Skip to content

Commit

Permalink
Merge pull request RIOT-OS#9726 from jcarrano/lua-loader-fix
Browse files Browse the repository at this point in the history
pkg/lua: Make the module searchers conform to the API.
  • Loading branch information
leandrolanzieri authored Jun 3, 2019
2 parents 0d090ad + 913f74b commit f56709e
Show file tree
Hide file tree
Showing 6 changed files with 235 additions and 10 deletions.
25 changes: 15 additions & 10 deletions pkg/lua/contrib/lua_loadlib.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,17 @@

/* ======================== 'searchers' functions =========================== */

/* A null address for table_len means the weak symbol was not overridden */
#define _SEARCH_BUILTINS(table, len, sname) \
((&(len) == NULL)? NULL : BINSEARCH_STR_P((table), (len), name, (sname), \
LUAR_MAX_MODULE_NAME))

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);
_SEARCH_BUILTINS(lua_riot_builtin_lua_table,
lua_riot_builtin_lua_table_len,
name);

if (lmodule != NULL) {
int load_result = luaL_loadbuffer(L, (const char *)lmodule->code,
Expand Down Expand Up @@ -79,8 +84,8 @@ static int searcher_builtin_lua(lua_State *L)
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));
lua_pushliteral(L, "\n\tModule not found in Lua-builtins");
return 1;
default:
return luaL_error(L, "error loading module '%s' from Lua-builtins: \n%s",
lua_tostring(L, 1), lua_tostring(L, 2));
Expand All @@ -90,9 +95,9 @@ static int searcher_builtin_lua(lua_State *L)
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);
_SEARCH_BUILTINS(lua_riot_builtin_c_table,
lua_riot_builtin_c_table_len,
name);

if (cmodule != NULL) {
lua_pushcfunction(L, cmodule->luaopen);
Expand All @@ -119,8 +124,8 @@ static int searcher_builtin_c(lua_State *L)
return 2;
}
else {
return luaL_error(L, "Module '%s' not found in C-builtins",
lua_tostring(L, 1));
lua_pushliteral(L, "\n\tModule not found in C-builtins");
return 1;
}
}

Expand Down
16 changes: 16 additions & 0 deletions tests/lua_loader/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
include ../Makefile.tests_common

USEPKG += lua

BOARD_WHITELIST += native samr21-xpro

ifneq ($(BOARD),native)
# This stack size is large enough to run Lua print() functions of
# various lengths. Other functions untested.
CFLAGS += -DTHREAD_STACKSIZE_MAIN='(THREAD_STACKSIZE_DEFAULT+2048)'
endif

include $(RIOTBASE)/Makefile.include

test:
tests/01-run.py
8 changes: 8 additions & 0 deletions tests/lua_loader/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Test modified LUA module loader
===============================

This test defines a bunch of dummy modules, both in
pure lua and in C. The way modules are declares is not "nice"
in the sense that it is not the way it would be done in a big
application. The goal is to test the module loader code, and not
the build tooling.
55 changes: 55 additions & 0 deletions tests/lua_loader/cmodules.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2019 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 Define a couple of dummy lua extension modules
*
* @author Juan Carrano <[email protected]>
*
* Normally one would not define more than one module in a single file, but
* these modules are exacly the same: a single table with an integer value.
*
* @}
*/

#define LUA_LIB

#include "lprefix.h"

#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"

static const luaL_Reg funcs[] = {
/* placeholder */
{ "X", NULL },
{ NULL, NULL }
};

static int _luaopen_X(lua_State *L, const char *xstring)
{
luaL_newlib(L, funcs);

lua_pushstring(L, xstring);
lua_setfield(L, -2, "X");

return 1;
}

int _luaopen_hello(lua_State *L)
{
return _luaopen_X(L, "E se deixa no céu,");
}

int _luaopen_world(lua_State *L)
{
return _luaopen_X(L, "como esquecida");
}
105 changes: 105 additions & 0 deletions tests/lua_loader/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
* Copyright (C) 2019 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 Test custom lua loader ("require" module)
*
* @author Juan Carrano <[email protected]>
*
* This application defines some pure-lua and some C-extension modules.
* First it tests running a module as a script (lua_riot_do_module).
*
* Then it goes into a loop reading single lines of input from stdin and
* executing them as lua source. The test script can use that to test module
* loading.
*
* @}
*/

#include <stdio.h>
#include <string.h>
#include <assert.h>

#include "lua_run.h"
#include "lua_builtin.h"

static const uint8_t pure_module_1[] = "\n\
return {a='Quando uma lua'}\n\
";

static const uint8_t pure_module_2[] = "\n\
return {a='chega de repente'}\n\
";

static const uint8_t test_script[] = "\n\
print'I am a module, hi!'\n\
";

/* The -1 is because the final '\0' is not part of the source code that should
* be read by lua.
*/
const struct lua_riot_builtin_lua _lua_riot_builtin_lua_table[] = {
{ "m1", pure_module_1, sizeof(pure_module_1) - 1 },
{ "m2", pure_module_2, sizeof(pure_module_2) - 1 },
{ "test", test_script, sizeof(test_script) - 1 }
};

extern int _luaopen_hello(lua_State *L);
extern int _luaopen_world(lua_State *L);

const struct lua_riot_builtin_c _lua_riot_builtin_c_table[] = {
{ "c1", _luaopen_hello },
{ "c2", _luaopen_world }
};

const struct lua_riot_builtin_lua *const lua_riot_builtin_lua_table = _lua_riot_builtin_lua_table;
const struct lua_riot_builtin_c *const lua_riot_builtin_c_table = _lua_riot_builtin_c_table;

const size_t lua_riot_builtin_lua_table_len =
sizeof(_lua_riot_builtin_lua_table) / sizeof(*_lua_riot_builtin_lua_table);
const size_t lua_riot_builtin_c_table_len =
sizeof(_lua_riot_builtin_c_table) / sizeof(*_lua_riot_builtin_c_table);

#define LUA_MEM_SIZE (11000)
static char lua_mem[LUA_MEM_SIZE] __attribute__ ((aligned(__BIGGEST_ALIGNMENT__)));

#define LINEBUF_SZ (32)
static char linebuf[LINEBUF_SZ];

int main(void)
{
int status;

status = lua_riot_do_module("test", lua_mem, LUA_MEM_SIZE,
LUAR_LOAD_BASE, NULL);

assert(status == LUAR_EXIT);

while (fgets(linebuf, LINEBUF_SZ, stdin) != NULL) {
int status;
size_t linelen = strlen(linebuf);

if (!linelen) {
continue;
}
else if (linebuf[linelen - 1] != '\n') {
puts("[ERROR] could not read a complete line");
break;
}

status = lua_riot_do_buffer((unsigned char *)linebuf, linelen,
lua_mem, LUA_MEM_SIZE,
LUAR_LOAD_BASE | LUAR_LOAD_PACKAGE, NULL);
assert(status == LUAR_EXIT);
}

return 0;
}
36 changes: 36 additions & 0 deletions tests/lua_loader/tests/01-run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#!/usr/bin/env python3

# Copyright (C) 2019 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.

# Tell the lua interpreter running in riot to load some modules and print
# the value of a variable inside that module.

import os
import sys

MODULE_QUERIES = [
("m1", "a", "Quando uma lua"),
("m2", "a", "chega de repente"),
("c1", "X", "E se deixa no céu,"),
("c2", "X", "como esquecida"),
]


def test(child):
# check startup message
child.expect_exact('I am a module, hi!')

# loop other defined commands and expected output
for mod, attr, val in MODULE_QUERIES:
child.sendline('print((require"{}").{})'.format(mod, attr))
child.expect_exact(val)


if __name__ == "__main__":
sys.path.append(os.path.join(os.environ['RIOTTOOLS'], 'testrunner'))
from testrunner import run
sys.exit(run(test))

0 comments on commit f56709e

Please sign in to comment.