Squashed updates do get Lua51 and Lua53 working (#3075)

-  Lots of minor but nasty bugfixes to get all tests to run clean
-  core lua and test suite fixes to allow luac -F to run cleanly against test suite
-  next tranch to get LFS working
-  luac.cross -a options plus fixes from feedback
-  UART fixes and lua.c merge
-  commit of wip prior to rebaselining against current dev
-  more tweaks
This commit is contained in:
Terry Ellison 2020-04-27 01:13:38 +01:00 committed by Marcel Stör
parent 99aba34460
commit bbeb09b695
248 changed files with 41887 additions and 2262 deletions

View File

@ -109,7 +109,7 @@ ifneq (,$(findstring indows,$(OS)))
else
# It is gcc, may be cygwin
# Can we use -fdata-sections?
CCFLAGS += -ffunction-sections -fno-jump-tables -fdata-sections
CCFLAGS += -ffunction-sections -fno-jump-tables -fdata-sections -fpack-struct=4
AR = xtensa-lx106-elf-ar
CC = xtensa-lx106-elf-gcc
CXX = xtensa-lx106-elf-g++

View File

@ -15,6 +15,13 @@ TARGET = eagle
#FLAVOR = release
FLAVOR = debug
# Handle Lua Directory selector
ifeq ("$(LUA)","53")
LUA_DIR := lua53
else
LUA_DIR := lua
endif
ifndef PDIR # {
GEN_IMAGES= eagle.app.v6.out
GEN_BINS= eagle.app.v6.bin
@ -34,15 +41,15 @@ SUBDIRS= \
mbedtls \
platform \
libc \
lua \
$(LUA_DIR) \
lwip \
smart \
modules \
spiffs \
net \
net \
fatfs \
esp-gdbstub \
pm \
pm \
uzlib \
$(OPT_SEL_MKTARGETS)
@ -65,7 +72,7 @@ COMPONENTS_eagle.app.v6 = \
driver/libdriver.a \
platform/libplatform.a \
libc/liblibc.a \
lua/liblua.a \
$(LUA_DIR)/liblua.a \
lwip/liblwip.a \
smart/smart.a \
spiffs/spiffs.a \
@ -150,7 +157,7 @@ DDEFINES += \
#
# Required for each makefile to inherit from the parent
#
INCLUDES := -I $(PDIR)libc -I $(PDIR)lua -I $(PDIR)platform \
INCLUDES := -I $(PDIR)libc -I $(PDIR)$(LUA_DIR) -I $(PDIR)platform \
$(INCLUDES) -I $(PDIR) -I $(PDIR)include
PDIR := ../$(PDIR)

View File

@ -171,9 +171,20 @@ static int handle_post_command(const coap_endpoint_t *ep, coap_rw_buffer_t *scra
{
char line[LUA_MAXINPUT+1];
if (!coap_buffer_to_string(line, LUA_MAXINPUT, &inpkt->payload)) {
lua_State *L = lua_getstate();
int base = lua_gettop(L), n, status;
int l = strlen(line);
line[l] = '\n';
lua_input_string(line, l+1);
line[l++] = '\n';
/* compile and exec payload; any error or results will be left on the stack and printed */
/* TODO: consider returning output as result instead of printing */
luaL_dostring(L, line);
if ((n = lua_gettop(L) - base) > 0)
{
lua_getglobal(L, "print");
lua_insert(L, base);
lua_pcall(L, n, 0, 0);
lua_settop(L, base);
}
}
return coap_make_response(scratch, outpkt, NULL, 0, id_hi, id_lo, &inpkt->tok, COAP_RSPCODE_CONTENT, COAP_CONTENTTYPE_TEXT_PLAIN);
}

View File

@ -173,7 +173,7 @@ i2c_master_setDC(uint16 id, uint8 SDA, uint8 SCL)
while(!(READ_PERI_REG(RTC_GPIO_IN_DATA) & 1)) {}; //read SCL value until SCL goes high
}else{
// dummy read operation and empty CPU cycles to maintain equal times for low and high state
READ_PERI_REG(RTC_GPIO_IN_DATA) & 1; asm volatile("nop;nop;nop;nop;");
(void) (READ_PERI_REG(RTC_GPIO_IN_DATA) & 1); asm volatile("nop;nop;nop;nop;");
}
}
else{

View File

@ -4,8 +4,6 @@
#include <stdint.h>
#include "mem.h"
/**DEBUG**/extern void dbg_printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
static void input_handler(platform_task_param_t flag, uint8 priority);
static struct input_state {
@ -63,7 +61,7 @@ static bool uart_getc(char *c){
** The ins.data check detects up the first task call which used to initialise
** everything.
*/
int lua_main (void);
extern int lua_main (void);
static bool input_readline(void);
static void input_handler(platform_task_param_t flag, uint8 priority) {
@ -79,7 +77,7 @@ static void input_handler(platform_task_param_t flag, uint8 priority) {
/*
** The input state (ins) is private, so input_setup() exposes the necessary
** access to public properties and is called in user_init() before the Lua
** enviroment is initialised. The second routine input_setup_receive() is
** enviroment is initialised. The second routine input_setup_receive() is
** called in lua.c after the Lua environment is available to bind the Lua
** input handler. Any UART input before this receive setup is ignored.
*/
@ -112,15 +110,15 @@ void input_setprompt (const char *prompt) {
}
/*
** input_readline() is called from the input_handler() event routine which is
** input_readline() is called from the input_handler() event routine which is
** posted by the UART Rx ISR posts. This works in one of two modes depending on
** the bool ins.run_input.
** - TRUE: it clears the UART FIFO up to EOL, doing any callback and sending
** the line to Lua.
** - FALSE: it clears the UART FIFO doing callbacks according to the data_len /
** end_char break.
** - FALSE: it clears the UART FIFO doing callbacks according to the data_len
** or end_char break.
*/
void lua_input_string (const char *line, int len);
extern void lua_input_string (const char *line, int len);
static bool input_readline(void) {
char ch = NUL;
@ -182,9 +180,9 @@ static bool input_readline(void) {
} else {
while (uart_getc(&ch)) {
ins.data[ins.line_pos++] = ch;
if( ins.line_pos >= ins.len ||
(ins.data_len > 0 && ins.line_pos >= ins.data_len) ||
ch == ins.end_char ) {
if( ins.line_pos >= ins.len ||
(ins.data_len >= 0 && ins.line_pos >= ins.data_len) ||
(ins.data_len < 0 && ch == ins.end_char )) {
ins.uart_cb(ins.data, ins.line_pos);
ins.line_pos = 0;
}

View File

@ -1,5 +1,5 @@
/*
* Software PWM using soft-interrupt timer1.
* Software PWM using soft-interrupt timer1.
* Supports higher frequencies compared to Espressif provided one.
*
* Nikolay Fiykov
@ -204,7 +204,7 @@ void pwm2_setup_pin(
const uint32_t freqDivisor,
const uint32_t resolution,
const uint32_t initDuty
)
)
{
moduleData->setupData.pin[pin].pulseResolutions = resolution;
moduleData->setupData.pin[pin].divisableFrequency = divisableFreq;

View File

@ -188,16 +188,24 @@ uart0_tx_buffer(uint8 *buf, uint16 len)
* FunctionName : uart0_sendStr
* Description : use uart0 to transfer buffer
* Parameters : uint8 *buf - point to send buffer
* uint16 len - buffer len
* Returns :
*******************************************************************************/
void ICACHE_FLASH_ATTR uart0_sendStr(const char *str)
{
void ICACHE_FLASH_ATTR uart0_sendStr(const char *str) {
while(*str)
{
// uart_tx_one_char(UART0, *str++);
uart0_putc(*str++);
}
}
/******************************************************************************
* FunctionName : uart0_sendStr
* Description : use uart0 to transfer buffer
* Parameters : uint8 *buf - point to send buffer
* size_t len - buffer len
* Returns :
*******************************************************************************/
void ICACHE_FLASH_ATTR uart0_sendStrn(const char *str, size_t len) {
size_t i;
for(i = 0; i < len; i++)
uart0_putc(*str++);
}
/******************************************************************************

View File

@ -1,5 +1,5 @@
/*
* Software PWM using soft-interrupt timer1.
* Software PWM using soft-interrupt timer1.
* Supports higher frequencies compared to Espressif provided one.
*
* Nikolay Fiykov

View File

@ -115,6 +115,7 @@ void uart_init_task(os_signal_t sig_input, uint8 *flag_input);
UartConfig uart_get_config(uint8 uart_no);
void uart0_alt(uint8 on);
void uart0_sendStr(const char *str);
void uart0_sendStrn(const char *str, size_t len);
void uart0_putc(const char c);
void uart0_tx_buffer(uint8 *buf, uint16 len);
void uart_setup(uint8 uart_no);

View File

@ -2,7 +2,7 @@
#define __MODULE_H__
#include "user_modules.h"
#include "lrotable.h"
#include "lnodemcu.h"
/* Registering a module within NodeMCU is really easy these days!
*
@ -38,23 +38,6 @@
#define MODULE_PASTE_(x,y) x##y
#define MODULE_EXPAND_PASTE_(x,y) MODULE_PASTE_(x,y)
#ifdef LUA_CROSS_COMPILER
#ifdef _MSC_VER
//on msvc it is necessary to go through more pre-processor hoops to get the
//section name built; string merging does not happen in the _declspecs.
//NOTE: linker magic is invoked via the magical '$' character. Caveat editor.
#define __TOKIFY(s) .rodata1$##s
#define __TOTOK(s) __TOKIFY(s)
#define __STRINGIFY(x) #x
#define __TOSTRING(x) __STRINGIFY(x)
#define __ROSECNAME(s) __TOSTRING(__TOTOK(s))
#define LOCK_IN_SECTION(s) __declspec ( allocate( __ROSECNAME(s) ) )
#else
#define LOCK_IN_SECTION(s) __attribute__((used,unused,section(".rodata1." #s)))
#endif
#else
#define LOCK_IN_SECTION(s) __attribute__((used,unused,section(".lua_" #s)))
#endif
/* For the ROM table, we name the variable according to ( | denotes concat):
* cfgname | _module_selected | LUA_USE_MODULES_##cfgname
* where the LUA_USE_MODULES_XYZ macro is first expanded to yield either
@ -67,8 +50,8 @@
*/
#define NODEMCU_MODULE(cfgname, luaname, map, initfunc) \
const LOCK_IN_SECTION(libs) \
luaR_entry MODULE_PASTE_(lua_lib_,cfgname) = { luaname, LRO_FUNCVAL(initfunc) }; \
ROTable_entry MODULE_PASTE_(lua_lib_,cfgname) = { luaname, LRO_FUNCVAL(initfunc) }; \
const LOCK_IN_SECTION(rotable) \
luaR_entry MODULE_EXPAND_PASTE_(cfgname,MODULE_EXPAND_PASTE_(_module_selected,MODULE_PASTE_(LUA_USE_MODULES_,cfgname))) \
= {luaname, LRO_ROVAL(map ## _map)}
ROTable_entry MODULE_EXPAND_PASTE_(cfgname,MODULE_EXPAND_PASTE_(_module_selected,MODULE_PASTE_(LUA_USE_MODULES_,cfgname))) \
= {luaname, LRO_ROVAL(map)}
#endif

View File

@ -1,14 +1,14 @@
#ifndef _TASK_H_
#define _TASK_H_
/*
** The task interface is now part of the core platform interface.
** The task interface is now part of the core platform interface.
** This header is preserved for backwards compatability only.
*/
*/
#include "platform.h"
#define TASK_PRIORITY_LOW PLATFORM_TASK_PRIORITY_LOW
#define TASK_PRIORITY_MEDIUM PLATFORM_TASK_PRIORITY_MEDIUM
#define TASK_PRIORITY_HIGH PLATFORM_TASK_PRIORITY_HIGH
#define TASK_PRIORITY_LOW PLATFORM_TASK_PRIORITY_LOW
#define TASK_PRIORITY_MEDIUM PLATFORM_TASK_PRIORITY_MEDIUM
#define TASK_PRIORITY_HIGH PLATFORM_TASK_PRIORITY_HIGH
#define task_post(priority,handle,param) platform_post(priority,handle,param)
#define task_post_low(handle,param) platform_post_low(handle,param)

View File

@ -248,15 +248,15 @@
#define READLINE_INTERVAL 80
#define STRBUF_DEFAULT_INCREMENT 3
#define LUA_USE_BUILTIN_DEBUG_MINIMAL // for debug.getregistry() and debug.traceback()
#ifdef DEVELOPMENT_TOOLS
#if defined(LUA_CROSS_COMPILER) || !defined(DEVELOPMENT_USE_GDB)
#if defined(DEVELOPMENT_TOOLS) && defined(DEVELOPMENT_USE_GDB)
extern void LUA_DEBUG_HOOK (void);
#define lua_assert(x) ((x) ? (void) 0 : LUA_DEBUG_HOOK ())
#elif defined(DEVELOPMENT_TOOLS) && defined(LUA_CROSS_COMPILER)
extern void luaL_assertfail(const char *file, int line, const char *message);
#define lua_assert(x) ((x) ? (void) 0 : luaL_assertfail(__FILE__, __LINE__, #x))
#else
extern void luaL_dbgbreak(void);
#define lua_assert(x) ((x) ? (void) 0 : luaL_dbgbreak())
#endif
#define lua_assert(x) ((void) (x))
#endif
#if !defined(LUA_NUMBER_INTEGRAL) && !defined (LUA_DWORD_ALIGNED_TVALUES)

View File

@ -1,9 +1,5 @@
#include <stdio.h>
int c_stdin = 999;
int c_stdout = 1000;
int c_stderr = 1001;
#if defined( LUA_NUMBER_INTEGRAL )
#include <stdarg.h>

View File

@ -6,7 +6,6 @@
#define lapi_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
@ -25,7 +24,6 @@
#include "ltm.h"
#include "lundump.h"
#include "lvm.h"
#include "lrotable.h"
#if 0
const char lua_ident[] =
@ -242,6 +240,12 @@ LUA_API void lua_pushvalue (lua_State *L, int idx) {
LUA_API int lua_type (lua_State *L, int idx) {
StkId o = index2adr(L, idx);
return (o == luaO_nilobject) ? LUA_TNONE : basettype(o);
}
LUA_API int lua_fulltype (lua_State *L, int idx) {
StkId o = index2adr(L, idx);
return (o == luaO_nilobject) ? LUA_TNONE : ttype(o);
}
@ -362,11 +366,10 @@ LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) {
LUA_API size_t lua_objlen (lua_State *L, int idx) {
StkId o = index2adr(L, idx);
switch (ttype(o)) {
switch (basettype(o)) {
case LUA_TSTRING: return tsvalue(o)->len;
case LUA_TUSERDATA: return uvalue(o)->len;
case LUA_TTABLE: return luaH_getn(hvalue(o));
case LUA_TROTABLE: return luaH_getn_ro(rvalue(o));
case LUA_TNUMBER: {
size_t l;
lua_lock(L); /* `luaV_tostring' may create a new string */
@ -404,16 +407,14 @@ LUA_API lua_State *lua_tothread (lua_State *L, int idx) {
LUA_API const void *lua_topointer (lua_State *L, int idx) {
StkId o = index2adr(L, idx);
switch (ttype(o)) {
case LUA_TTABLE: return hvalue(o);
case LUA_TTABLE:
case LUA_TROTABLE:
return hvalue(o);
case LUA_TFUNCTION: return clvalue(o);
case LUA_TTHREAD: return thvalue(o);
case LUA_TUSERDATA:
case LUA_TLIGHTUSERDATA:
return lua_touserdata(L, idx);
case LUA_TROTABLE:
return rvalue(o);
case LUA_TLIGHTFUNCTION:
return fvalue(o);
case LUA_TUSERDATA: return lua_touserdata(L, idx);
case LUA_TLIGHTUSERDATA: return pvalue(o);
case LUA_TLIGHTFUNCTION: return fvalue(o);
default: return NULL;
}
}
@ -491,18 +492,23 @@ LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) {
LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) {
Closure *cl;
lua_lock(L);
luaC_checkGC(L);
api_checknelems(L, n);
cl = luaF_newCclosure(L, n, getcurrenv(L));
cl->c.f = fn;
L->top -= n;
while (n--)
setobj2n(L, &cl->c.upvalue[n], L->top+n);
setclvalue(L, L->top, cl);
lua_assert(iswhite(obj2gco(cl)));
api_incr_top(L);
if (n == 0) {
setfvalue(L->top, fn);
api_incr_top(L);
} else {
Closure *cl;
luaC_checkGC(L);
api_checknelems(L, n);
cl = luaF_newCclosure(L, n, getcurrenv(L));
cl->c.f = fn;
L->top -= n;
while (n--)
setobj2n(L, &cl->c.upvalue[n], L->top+n);
setclvalue(L, L->top, cl);
lua_assert(iswhite(obj2gco(cl)));
api_incr_top(L);
}
lua_unlock(L);
}
@ -522,16 +528,10 @@ LUA_API void lua_pushlightuserdata (lua_State *L, void *p) {
lua_unlock(L);
}
LUA_API void lua_pushrotable (lua_State *L, void *p) {
lua_lock(L);
setrvalue(L->top, p);
api_incr_top(L);
lua_unlock(L);
}
LUA_API void lua_pushlightfunction(lua_State *L, void *p) {
LUA_API void lua_pushrotable (lua_State *L, const ROTable *t) {
lua_lock(L);
setfvalue(L->top, p);
sethvalue(L, L->top, cast(ROTable *,t));
api_incr_top(L);
lua_unlock(L);
}
@ -561,7 +561,6 @@ LUA_API void lua_gettable (lua_State *L, int idx) {
lua_unlock(L);
}
LUA_API void lua_getfield (lua_State *L, int idx, const char *k) {
StkId t;
TValue key;
@ -579,12 +578,10 @@ LUA_API void lua_getfield (lua_State *L, int idx, const char *k) {
LUA_API void lua_rawget (lua_State *L, int idx) {
StkId t;
const TValue *res;
lua_lock(L);
t = index2adr(L, idx);
api_check(L, ttistable(t) || ttisrotable(t));
res = ttistable(t) ? luaH_get(hvalue(t), L->top - 1) : luaH_get_ro(rvalue(t), L->top - 1);
setobj2s(L, L->top - 1, res);
api_check(L, ttistable(t));
setobj2s(L, L->top - 1, luaH_get(hvalue(t), L->top - 1));
lua_unlock(L);
}
@ -593,8 +590,8 @@ LUA_API void lua_rawgeti (lua_State *L, int idx, int n) {
StkId o;
lua_lock(L);
o = index2adr(L, idx);
api_check(L, ttistable(o) || ttisrotable(o));
setobj2s(L, L->top, ttistable(o) ? luaH_getnum(hvalue(o), n) : luaH_getnum_ro(rvalue(o), n))
api_check(L, ttistable(o));
setobj2s(L, L->top, luaH_getnum(hvalue(o), n));
api_incr_top(L);
lua_unlock(L);
}
@ -615,27 +612,21 @@ LUA_API int lua_getmetatable (lua_State *L, int objindex) {
int res;
lua_lock(L);
obj = index2adr(L, objindex);
switch (ttype(obj)) {
switch (basettype(obj)) {
case LUA_TTABLE:
mt = hvalue(obj)->metatable;
break;
case LUA_TUSERDATA:
mt = uvalue(obj)->metatable;
break;
case LUA_TROTABLE:
mt = (Table*)luaR_getmeta(rvalue(obj));
break;
default:
mt = G(L)->mt[ttype(obj)];
mt = G(L)->mt[basettype(obj)];
break;
}
if (mt == NULL)
res = 0;
else {
if(luaR_isrotable(mt))
setrvalue(L->top, mt)
else
sethvalue(L, L->top, mt)
sethvalue(L, L->top, mt)
api_incr_top(L);
res = 1;
}
@ -732,37 +723,34 @@ LUA_API void lua_rawseti (lua_State *L, int idx, int n) {
LUA_API int lua_setmetatable (lua_State *L, int objindex) {
TValue *obj;
Table *mt;
int isrometa = 0;
lua_lock(L);
api_checknelems(L, 1);
obj = index2adr(L, objindex);
api_checkvalidindex(L, obj);
if (ttisnil(L->top - 1))
if (ttisnil(L->top - 1)) {
mt = NULL;
else {
api_check(L, ttistable(L->top - 1) || ttisrotable(L->top - 1));
if (ttistable(L->top - 1))
mt = hvalue(L->top - 1);
else {
mt = (Table*)rvalue(L->top - 1);
isrometa = 1;
}
} else {
api_check(L, ttistable(L->top - 1));
mt = hvalue(L->top - 1);
}
switch (ttype(obj)) {
switch (ttype(obj)) { /* use basetype to retain subtypes*/
case LUA_TTABLE: {
hvalue(obj)->metatable = mt;
if (mt && !isrometa)
if (mt && !isrotable(mt))
luaC_objbarriert(L, hvalue(obj), mt);
break;
}
case LUA_TUSERDATA: {
uvalue(obj)->metatable = mt;
if (mt && !isrometa)
if (mt && !isrotable(mt))
luaC_objbarrier(L, rawuvalue(obj), mt);
break;
}
case LUA_TISROTABLE: { /* Ignore any changes to a ROTable MT */
break;
}
default: {
G(L)->mt[ttype(obj)] = mt;
G(L)->mt[basettype(obj)] = mt;
break;
}
}
@ -913,14 +901,14 @@ LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data,
}
LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data) {
LUA_API int lua_dumpEx (lua_State *L, lua_Writer writer, void *data, int stripping) {
int status;
TValue *o;
lua_lock(L);
api_checknelems(L, 1);
o = L->top - 1;
if (isLfunction(o))
status = luaU_dump(L, clvalue(o)->l.p, writer, data, 0);
status = luaU_dump(L, clvalue(o)->l.p, writer, data, stripping);
else
status = 1;
lua_unlock(L);
@ -1038,8 +1026,8 @@ LUA_API int lua_next (lua_State *L, int idx) {
int more;
lua_lock(L);
t = index2adr(L, idx);
api_check(L, ttistable(t) || ttisrotable(t));
more = ttistable(t) ? luaH_next(L, hvalue(t), L->top - 1) : luaH_next_ro(L, rvalue(t), L->top - 1);
api_check(L, ttistable(t));
more = luaH_next(L, hvalue(t), L->top - 1);
if (more) {
api_incr_top(L);
}
@ -1148,3 +1136,15 @@ LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
return name;
}
LUA_API void lua_setegcmode( lua_State *L, int mode, int limit) {
G(L)->egcmode = mode;
G(L)->memlimit = limit;
}
LUA_API void legc_set_mode(lua_State *L, int mode, int limit) {
global_State *g = G(L);
g->egcmode = mode;
g->memlimit = limit;
}

View File

@ -4,27 +4,21 @@
** See Copyright Notice in lua.h
*/
#define LUAC_CROSS_FILE
#include "lua.h"
#include <ctype.h>
#ifdef __MINGW__
#include <errno.h>
#if defined(LUA_CROSS_COMPILER) && defined(_MSC_VER)
#undef errno //msvc #defines errno, which interferes with our #include macro
#else
#ifdef _MSC_VER //msvc #defines errno, which interferes with our #include macro
#undef errno
#endif
#include <errno.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#ifndef LUA_CROSS_COMPILER
#include "vfs.h"
#include "user_interface.h"
#else
#endif
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
/* This file uses only the official API of Lua.
** Any function declared here could be written as an application function.
@ -38,14 +32,13 @@
#include "ldo.h"
#include "lobject.h"
#include "lstate.h"
#include "legc.h"
#define FREELIST_REF 0 /* free list of references */
/* convert a stack index to positive */
#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \
lua_gettop(L) + (i) + 1)
#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \
lua_gettop(L) + (i) + 1)
// Parameters for luaI_openlib
#define LUA_USECCLOSURES 0
@ -221,7 +214,7 @@ LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) {
LUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) {
const char *msg = lua_pushfstring(L, "%s expected, got %s",
tname, luaL_typename(L, narg));
tname, lua_typename(L, narg));
return luaL_argerror(L, narg, msg);
}
@ -281,7 +274,7 @@ LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) {
return 1;
}
LUALIB_API int luaL_rometatable (lua_State *L, const char* tname, void *p) {
LUALIB_API int luaL_rometatable (lua_State *L, const char* tname, const ROTable *p) {
lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get registry.name */
if (!lua_isnil(L, -1)) /* name already in use? */
return 0; /* leave previous value on top, but return 0 */
@ -319,22 +312,6 @@ LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) {
tag_error(L, narg, t);
}
LUALIB_API void luaL_checkanyfunction (lua_State *L, int narg) {
if (lua_type(L, narg) != LUA_TFUNCTION && lua_type(L, narg) != LUA_TLIGHTFUNCTION) {
const char *msg = lua_pushfstring(L, "function or lightfunction expected, got %s",
luaL_typename(L, narg));
luaL_argerror(L, narg, msg);
}
}
LUALIB_API void luaL_checkanytable (lua_State *L, int narg) {
if (lua_type(L, narg) != LUA_TTABLE && lua_type(L, narg) != LUA_TROTABLE) {
const char *msg = lua_pushfstring(L, "table or rotable expected, got %s",
luaL_typename(L, narg));
luaL_argerror(L, narg, msg);
}
}
LUALIB_API void luaL_checkany (lua_State *L, int narg) {
if (lua_type(L, narg) == LUA_TNONE)
@ -453,7 +430,7 @@ LUALIB_API void luaI_openlib (lua_State *L, const char *libname,
for (i=0; i<nup; i++) /* copy upvalues to the top */
lua_pushvalue(L, -nup);
if (ftype == LUA_USELIGHTFUNCTIONS)
lua_pushlightfunction(L, l->func);
lua_pushcfunction(L, l->func);
else
lua_pushcclosure(L, l->func, nup);
lua_setfield(L, -(nup+2), l->name);
@ -565,7 +542,7 @@ LUALIB_API const char *luaL_findtable (lua_State *L, int idx,
lua_pushvalue(L, -2);
lua_settable(L, -4); /* set new table into field */
}
else if (!lua_istable(L, -1) && !lua_isrotable(L, -1)) { /* field has a non-table value? */
else if (!lua_istable(L, -1)) { /* field has a non-table value? */
lua_pop(L, 2); /* remove table and value */
return fname; /* return problematic part of the name */
}
@ -714,14 +691,32 @@ LUALIB_API void luaL_unref (lua_State *L, int t, int ref) {
** =======================================================
*/
#ifdef LUA_CROSS_COMPILER
typedef struct LoadF {
int extraline;
#ifdef LUA_CROSS_COMPILER
FILE *f;
#else
int f;
#endif
char buff[LUAL_BUFFERSIZE];
} LoadF;
#ifdef LUA_CROSS_COMPILER
# define freopen_bin(f,fn) freopen(f,"rb",fn)
# define read_buff(b,f) fread(b, 1, sizeof (b), f)
#else
# define strerror(n) ""
#undef feof
# define feof(f) vfs_eof(f)
#undef fopen
# define fopen(f, m) vfs_open(f, m)
# define freopen_bin(fn,f) ((void) vfs_close(f), vfs_open(fn, "r"))
#undef getc
# define getc(f) vfs_getc(f)
#undef ungetc
# define ungetc(c,f) vfs_ungetc(c, f)
# define read_buff(b,f) vfs_read(f, b, sizeof (b))
#endif
static const char *getF (lua_State *L, void *ud, size_t *size) {
LoadF *lf = (LoadF *)ud;
@ -732,7 +727,7 @@ static const char *getF (lua_State *L, void *ud, size_t *size) {
return "\n";
}
if (feof(lf->f)) return NULL;
*size = fread(lf->buff, 1, sizeof(lf->buff), lf->f);
*size = read_buff(lf->buff, lf->f);
return (*size > 0) ? lf->buff : NULL;
}
@ -752,14 +747,19 @@ LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
int c;
int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
lf.extraline = 0;
if (filename == NULL) {
#ifdef LUA_CROSS_COMPILER
lua_pushliteral(L, "=stdin");
lf.f = c_stdin;
lf.f = stdin;
#else
return luaL_error(L, "filename is NULL");
#endif
}
else {
lua_pushfstring(L, "@%s", filename);
lf.f = fopen(filename, "r");
if (lf.f == NULL) return errfile(L, "open", fnameindex);
if (!lf.f) return errfile(L, "open", fnameindex);
}
c = getc(lf.f);
if (c == '#') { /* Unix exec. file? */
@ -768,8 +768,8 @@ LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
if (c == '\n') c = getc(lf.f);
}
if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */
if (lf.f == NULL) return errfile(L, "reopen", fnameindex);
lf.f = freopen_bin(filename, lf.f); /* reopen in binary mode */
if (!lf.f) return errfile(L, "reopen", fnameindex);
/* skip eventual `#!...' */
while ((c = getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) {}
@ -777,91 +777,21 @@ LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
}
ungetc(c, lf.f);
status = lua_load(L, getF, &lf, lua_tostring(L, -1));
#ifdef LUA_CROSS_COMPILER
readstatus = ferror(lf.f);
if (filename) fclose(lf.f); /* close file (even in case of errors) */
if (readstatus) {
lua_settop(L, fnameindex); /* ignore results from `lua_load' */
return errfile(L, "read", fnameindex);
}
lua_remove(L, fnameindex);
return status;
}
#else
typedef struct LoadFSF {
int extraline;
int f;
char buff[LUAL_BUFFERSIZE];
} LoadFSF;
static const char *getFSF (lua_State *L, void *ud, size_t *size) {
LoadFSF *lf = (LoadFSF *)ud;
(void)L;
if (L == NULL && size == NULL) // Direct mode check
return NULL;
if (lf->extraline) {
lf->extraline = 0;
*size = 1;
return "\n";
}
if (vfs_eof(lf->f)) return NULL;
*size = vfs_read(lf->f, lf->buff, sizeof(lf->buff));
return (*size > 0) ? lf->buff : NULL;
}
static int errfsfile (lua_State *L, const char *what, int fnameindex) {
const char *filename = lua_tostring(L, fnameindex) + 1;
lua_pushfstring(L, "cannot %s %s", what, filename);
lua_remove(L, fnameindex);
return LUA_ERRFILE;
}
LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) {
LoadFSF lf;
int status, c;
int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */
lf.extraline = 0;
if (filename == NULL) {
return luaL_error(L, "filename is NULL");
}
else {
lua_pushfstring(L, "@%s", filename);
lf.f = vfs_open(filename, "r");
if (!lf.f) return errfsfile(L, "open", fnameindex);
}
// if(fs_size(lf.f)>LUAL_BUFFERSIZE)
// return luaL_error(L, "file is too big");
c = vfs_getc(lf.f);
if (c == '#') { /* Unix exec. file? */
lf.extraline = 1;
while ((c = vfs_getc(lf.f)) != VFS_EOF && c != '\n') ; /* skip first line */
if (c == '\n') c = vfs_getc(lf.f);
}
if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */
vfs_close(lf.f);
lf.f = vfs_open(filename, "r"); /* reopen in binary mode */
if (!lf.f) return errfsfile(L, "reopen", fnameindex);
/* skip eventual `#!...' */
while ((c = vfs_getc(lf.f)) != VFS_EOF && c != LUA_SIGNATURE[0]) ;
lf.extraline = 0;
}
vfs_ungetc(c, lf.f);
status = lua_load(L, getFSF, &lf, lua_tostring(L, -1));
(void) readstatus; /* avoid compile error */
if (filename) vfs_close(lf.f); /* close file (even in case of errors) */
#endif
lua_remove(L, fnameindex);
return status;
}
#endif
typedef struct LoadS {
const char *s;
@ -959,7 +889,7 @@ LUALIB_API void luaL_assertfail(const char *file, int line, const char *message)
#endif
}
#if defined(DEVELOPMENT_USE_GDB) && !defined(LUA_CROSS_COMPILER)
#ifdef DEVELOPMENT_USE_GDB
/*
* This is a simple stub used by lua_assert() if DEVELOPMENT_USE_GDB is defined.
* Instead of crashing out with an assert error, this hook starts the GDB remote
@ -969,9 +899,14 @@ LUALIB_API void luaL_assertfail(const char *file, int line, const char *message)
* is the option to exit the interactive session and start the Xtensa remote GDB
* which will then sync up with the remote GDB client to allow forensics of the error.
*/
#ifdef LUA_CROSS_COMPILER
LUALIB_API void lua_debugbreak(void) {
puts(" lua_debugbreak "); /* allows BT analysis of assert fails */
}
#else
extern void gdbstub_init(void);
LUALIB_API void luaL_dbgbreak(void) {
LUALIB_API void lua_debugbreak(void) {
static int repeat_entry = 0;
if (repeat_entry == 0) {
dbg_printf("Start up the gdb stub if not already started\n");
@ -981,17 +916,13 @@ LUALIB_API void luaL_dbgbreak(void) {
asm("break 0,0" ::);
}
#endif
#endif
static int panic (lua_State *L) {
(void)L; /* to avoid warnings */
#if defined(LUA_USE_STDIO)
fprintf(c_stderr, "PANIC: unprotected error in call to Lua API (%s)\n",
lua_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n",
lua_tostring(L, -1));
#else
luai_writestringerror("PANIC: unprotected error in call to Lua API (%s)\n",
lua_tostring(L, -1));
#endif
while (1) {}
return 0;
}

View File

@ -60,11 +60,9 @@ LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg,
LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg);
LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t);
LUALIB_API void (luaL_checkany) (lua_State *L, int narg);
LUALIB_API void (luaL_checkanyfunction) (lua_State *L, int narg);
LUALIB_API void (luaL_checkanytable) (lua_State *L, int narg);
LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname);
LUALIB_API int (luaL_rometatable) (lua_State *L, const char* tname, void *p);
LUALIB_API int (luaL_rometatable) (lua_State *L, const char* tname, const ROTable *p);
LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname);
LUALIB_API void (luaL_where) (lua_State *L, int lvl);
@ -76,11 +74,8 @@ LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def,
LUALIB_API int (luaL_ref) (lua_State *L, int t);
LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref);
#ifdef LUA_CROSS_COMPILER
LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename);
#else
LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename);
#endif
LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz,
const char *name);
LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s);
@ -111,6 +106,8 @@ LUALIB_API void luaL_assertfail(const char *file, int line, const char *message)
#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d)))
#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n)))
#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d)))
#define luaL_checktable(L,n) luaL_checktype(L, (n), LUA_TTABLE);
#define luaL_checkfunction(L,n) luaL_checktype(L, (n), LUA_TFUNCTION);
#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i)))
@ -163,6 +160,11 @@ LUALIB_API void (luaL_pushresult) (luaL_Buffer *B);
/* }====================================================== */
LUALIB_API int luaL_traceback (lua_State *L);
LUALIB_API int luaL_pcallx (lua_State *L, int narg, int nres);
LUALIB_API int luaL_posttask( lua_State* L, int prio );
/* }====================================================== */
/* compatibility with ref system */

View File

@ -8,15 +8,15 @@
#define lbaselib_c
#define LUA_LIB
#define LUAC_CROSS_FILE
#include "lua.h"
#include "lnodemcu.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "lauxlib.h"
#include "lualib.h"
#include "lrotable.h"
/*
@ -25,6 +25,10 @@
** model but changing `fputs' to put the strings at a proper place
** (a console window or a log file, for instance).
*/
#ifdef LUA_CROSS_COMPILER
#undef puts
#define puts(s) printf("%s",s)
#endif
static int luaB_print (lua_State *L) {
int n = lua_gettop(L); /* number of arguments */
int i;
@ -101,7 +105,7 @@ static int luaB_getmetatable (lua_State *L) {
static int luaB_setmetatable (lua_State *L) {
int t = lua_type(L, 2);
luaL_checktype(L, 1, LUA_TTABLE);
luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE || t == LUA_TROTABLE, 2,
luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2,
"nil or table expected");
if (luaL_getmetafield(L, 1, "__metatable"))
luaL_error(L, "cannot change a protected metatable");
@ -164,7 +168,7 @@ static int luaB_rawequal (lua_State *L) {
static int luaB_rawget (lua_State *L) {
luaL_checkanytable(L, 1);
luaL_checktable(L, 1);
luaL_checkany(L, 2);
lua_settop(L, 2);
lua_rawget(L, 1);
@ -172,7 +176,7 @@ static int luaB_rawget (lua_State *L) {
}
static int luaB_rawset (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
luaL_checktable(L, 1);
luaL_checkany(L, 2);
luaL_checkany(L, 3);
lua_settop(L, 3);
@ -222,7 +226,7 @@ static int luaB_type (lua_State *L) {
static int luaB_next (lua_State *L) {
luaL_checkanytable(L, 1);
luaL_checktable(L, 1);
lua_settop(L, 2); /* create a 2nd argument if there isn't one */
if (lua_next(L, 1))
return 2;
@ -234,7 +238,7 @@ static int luaB_next (lua_State *L) {
static int luaB_pairs (lua_State *L) {
luaL_checkanytable(L, 1);
luaL_checktable(L, 1);
lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */
lua_pushvalue(L, 1); /* state, */
lua_pushnil(L); /* and initial value */
@ -244,7 +248,7 @@ static int luaB_pairs (lua_State *L) {
static int ipairsaux (lua_State *L) {
int i = luaL_checkint(L, 2);
luaL_checkanytable(L, 1);
luaL_checktable(L, 1);
i++; /* next value */
lua_pushinteger(L, i);
lua_rawgeti(L, 1, i);
@ -253,7 +257,7 @@ static int ipairsaux (lua_State *L) {
static int luaB_ipairs (lua_State *L) {
luaL_checkanytable(L, 1);
luaL_checktable(L, 1);
lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */
lua_pushvalue(L, 1); /* state, */
lua_pushinteger(L, 0); /* and initial value */
@ -451,29 +455,24 @@ static int luaB_newproxy (lua_State *L) {
return 1;
}
#include "lrotable.h"
LROT_EXTERN(lua_rotable_base);
/*
* Separate ROTables are used for the base functions and library ROTables, with
* the base functions ROTable declared below. The library ROTable is chained
* from this using its __index meta-method.
*
* ESP builds use specific linker directives to marshal all the ROTable entries
* for the library modules into a single ROTable in the PSECT ".lua_rotable".
* This is not practical on Posix builds using a standard GNU link, so the
* equivalent ROTable for the core libraries defined in linit.c for the cross-
* compiler build.
*/
LROT_EXTERN(lua_rotables);
LROT_PUBLIC_BEGIN(base_func_meta)
LROT_TABENTRY( __index, lua_rotables )
LROT_END(base_func, base_func_meta, LROT_MASK_INDEX)
LROT_PUBLIC_BEGIN(base_func)
** ESP builds use specific linker directives to marshal all the ROTable entries
** for the library modules including the base library into an entry vector in
** the PSECT ".lua_rotable" including the base library entries; this is bound
** into a ROTable in linit.c which then hooked into the __index metaentry for
** _G so that base library and ROM tables are directly resolved through _G.
**
** The host-based luac.cross builds which must use a standard GNU link or
** MSVC so this linker-specfic assembly approach can't be used. In this case
** luaopen_base returns a base_func ROTable so a two cascade resolution. See
** description in init.c for further details.
*/
#ifdef LUA_CROSS_COMPILER
LROT_BEGIN(base_func, NULL, 0)
#else
LROT_ENTRIES_IN_SECTION(base_func, rotable)
#endif
LROT_FUNCENTRY(assert, luaB_assert)
LROT_FUNCENTRY(collectgarbage, luaB_collectgarbage)
LROT_FUNCENTRY(dofile, luaB_dofile)
@ -498,13 +497,11 @@ LROT_PUBLIC_BEGIN(base_func)
LROT_FUNCENTRY(type, luaB_type)
LROT_FUNCENTRY(unpack, luaB_unpack)
LROT_FUNCENTRY(xpcall, luaB_xpcall)
LROT_TABENTRY(__metatable, base_func_meta)
LROT_END(base_func, base_func_meta, LROT_MASK_INDEX)
LROT_BEGIN(G_meta)
LROT_TABENTRY( __index, base_func )
LROT_END(G_meta, NULL, 0)
#ifdef LUA_CROSS_COMPILER
LROT_END(base_func, NULL, 0)
#else
LROT_BREAK(base_func)
#endif
/*
** {======================================================
@ -634,14 +631,14 @@ static int luaB_corunning (lua_State *L) {
return 1;
}
LROT_PUBLIC_BEGIN(co_funcs)
LROT_BEGIN(co_funcs, NULL, 0)
LROT_FUNCENTRY( create, luaB_cocreate )
LROT_FUNCENTRY( resume, luaB_coresume )
LROT_FUNCENTRY( running, luaB_corunning )
LROT_FUNCENTRY( status, luaB_costatus )
LROT_FUNCENTRY( wrap, luaB_cowrap )
LROT_FUNCENTRY( yield, luaB_yield )
LROT_END (co_funcs, NULL, 0)
LROT_END(co_funcs, NULL, 0)
/* }====================================================== */
@ -650,19 +647,13 @@ static void auxopen (lua_State *L, const char *name,
lua_CFunction f, lua_CFunction u) {
lua_pushcfunction(L, u);
lua_pushcclosure(L, f, 1);
lua_setfield(L, -2, name);
lua_setglobal(L, name);
}
static void base_open (lua_State *L) {
/* set global _G */
extern LROT_TABLE(rotables);
LUALIB_API int luaopen_base (lua_State *L) {
lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setglobal(L, "_G");
/* open lib into global table */
luaL_register_light(L, "_G", &((luaL_Reg) {0}));
lua_pushrotable(L, LROT_TABLEREF(G_meta));
lua_setmetatable(L, LUA_GLOBALSINDEX);
lua_settable(L, LUA_GLOBALSINDEX); /* set global _G */
lua_pushliteral(L, LUA_VERSION);
lua_setglobal(L, "_VERSION"); /* set global _VERSION */
/* `ipairs' and `pairs' need auxliliary functions as upvalues */
@ -670,16 +661,15 @@ static void base_open (lua_State *L) {
auxopen(L, "pairs", luaB_pairs, luaB_next);
/* `newproxy' needs a weaktable as upvalue */
lua_createtable(L, 0, 1); /* new table `w' */
lua_pushvalue(L, -1); /* `w' will be its own metatable */
lua_setmetatable(L, -2);
lua_pushliteral(L, "kv");
lua_setfield(L, -2, "__mode"); /* metatable(w).__mode = "kv" */
lua_pushcclosure(L, luaB_newproxy, 1);
lua_pushvalue(L, -1); /* `w' will be its own metatable */
lua_setmetatable(L, -2);
lua_pushcclosure(L, luaB_newproxy, 1); /* Upval is table w */
lua_setglobal(L, "newproxy"); /* set global `newproxy' */
}
LUALIB_API int luaopen_base (lua_State *L) {
base_open(L);
return 1;
lua_pushrotable(L, LROT_TABLEREF(rotables));
lua_setglobal(L, "__index");
lua_pushvalue(L, LUA_GLOBALSINDEX); /* _G is its own metatable */
lua_setmetatable(L, LUA_GLOBALSINDEX);
return 0;
}

View File

@ -7,7 +7,6 @@
#define lcode_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
#include <stdlib.h>

View File

@ -7,7 +7,6 @@
#define ldblib_c
#define LUA_LIB
#define LUAC_CROSS_FILE
#include "lua.h"
#include <stdio.h>
@ -18,7 +17,7 @@
#include "lualib.h"
#include "lstring.h"
#include "lflash.h"
#include "lrotable.h"
#include "lnodemcu.h"
#include "user_modules.h"
@ -144,7 +143,7 @@ static int db_getinfo (lua_State *L) {
return 1;
}
}
else if (lua_isfunction(L, arg+1) || lua_islightfunction(L, arg+1)) {
else if (lua_isfunction(L, arg+1)) {
lua_pushfstring(L, ">%s", options);
options = lua_tostring(L, -1);
lua_pushvalue(L, arg+1);
@ -302,7 +301,7 @@ static int db_sethook (lua_State *L) {
}
else {
const char *smask = luaL_checkstring(L, arg+2);
luaL_checkanyfunction(L, arg+1);
luaL_checkfunction(L, arg+1);
count = luaL_optint(L, arg+3, 0);
func = hookf; mask = makemask(smask, count);
}
@ -340,10 +339,10 @@ static int db_debug (lua_State *L) {
for (;;) {
char buffer[LUA_MAXINPUT];
#if defined(LUA_USE_STDIO)
fputs("lua_debug> ", c_stderr);
if (fgets(buffer, sizeof(buffer), c_stdin) == 0 ||
fputs("lua_debug> ", stderr);
if (fgets(buffer, sizeof(buffer), stdin) == 0 ||
#else
// luai_writestringerror("%s", "lua_debug>");
// lua_writestringerror("%s", "lua_debug>");
if (lua_readline(L, buffer, "lua_debug>") == 0 ||
#endif
strcmp(buffer, "cont\n") == 0)
@ -351,10 +350,10 @@ static int db_debug (lua_State *L) {
if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") ||
lua_pcall(L, 0, 0, 0)) {
#if defined(LUA_USE_STDIO)
fputs(lua_tostring(L, -1), c_stderr);
fputs("\n", c_stderr);
fputs(lua_tostring(L, -1), stderr);
fputs("\n", stderr);
#else
luai_writestringerror("%s\n", lua_tostring(L, -1));
lua_writestringerror("%s\n", lua_tostring(L, -1));
#endif
}
lua_settop(L, 0); /* remove eventual returns */
@ -365,7 +364,7 @@ static int db_debug (lua_State *L) {
#define LEVELS1 12 /* size of the first part of the stack */
#define LEVELS2 10 /* size of the second part of the stack */
int debug_errorfb (lua_State *L) {
static int debug_errorfb (lua_State *L) {
int level;
int firstpart = 1; /* still before eventual `...' */
int arg;
@ -417,7 +416,7 @@ int debug_errorfb (lua_State *L) {
return 1;
}
LROT_PUBLIC_BEGIN(dblib)
LROT_BEGIN(dblib, NULL, 0)
#ifndef LUA_USE_BUILTIN_DEBUG_MINIMAL
LROT_FUNCENTRY( debug, db_debug )
LROT_FUNCENTRY( getfenv, db_getfenv )

View File

@ -7,7 +7,6 @@
#define ldebug_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
#include <string.h>
@ -327,21 +326,21 @@ LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
lua_lock(L);
if (*what == '>') {
StkId func = L->top - 1;
luai_apicheck(L, ttisfunction(func) || ttislightfunction(func));
luai_apicheck(L, ttisfunction(func));
what++; /* skip the '>' */
if (ttisfunction(func))
f = clvalue(func);
else
if (ttislightfunction(func))
plight = fvalue(func);
else
f = clvalue(func);
L->top--; /* pop function */
}
else if (ar->i_ci != 0) { /* no tail call? */
ci = L->base_ci + ar->i_ci;
lua_assert(ttisfunction(ci->func) || ttislightfunction(ci->func));
if (ttisfunction(ci->func))
f = clvalue(ci->func);
else
lua_assert(ttisfunction(ci->func));
if (ttislightfunction(ci->func))
plight = fvalue(ci->func);
else
f = clvalue(ci->func);
}
status = auxgetinfo(L, what, ar, f, plight, ci);
if (strchr(what, 'f')) {
@ -721,7 +720,7 @@ static void addinfo (lua_State *L, const char *msg) {
void luaG_errormsg (lua_State *L) {
if (L->errfunc != 0) { /* is there an error handling function? */
StkId errfunc = restorestack(L, L->errfunc);
if (!ttisfunction(errfunc) && !ttislightfunction(errfunc)) luaD_throw(L, LUA_ERRERR);
if (!ttisfunction(errfunc)) luaD_throw(L, LUA_ERRERR);
setobjs2s(L, L->top, L->top - 1); /* move argument */
setobjs2s(L, L->top - 1, errfunc); /* push function */
incr_top(L);

View File

@ -8,7 +8,6 @@
#define ldo_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
#include <string.h>
@ -254,13 +253,13 @@ static StkId adjust_varargs (lua_State *L, Proto *p, int actual) {
return base;
}
static StkId tryfuncTM (lua_State *L, StkId func) {
const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL);
StkId p;
ptrdiff_t funcr = savestack(L, func);
if (!ttisfunction(tm))
if (!ttisfunction(tm)) {
luaG_typeerror(L, func, "call");
}
/* Open a hole inside the stack at `func' */
for (p = L->top; p > func; p--) setobjs2s(L, p, p-1);
incr_top(L);
@ -279,10 +278,10 @@ static StkId tryfuncTM (lua_State *L, StkId func) {
int luaD_precall (lua_State *L, StkId func, int nresults) {
ptrdiff_t funcr;
LClosure *cl = NULL;
if (!ttisfunction(func) && !ttislightfunction(func)) /* `func' is not a function? */
if (!ttisfunction(func)) /* `func' is not a function? */
func = tryfuncTM(L, func); /* check the `function' tag method */
funcr = savestack(L, func);
if (ttisfunction(func))
if (!ttislightfunction(func))
cl = &clvalue(func)->l;
L->ci->savedpc = L->savedpc;
if (cl && !cl->isC) { /* Lua function? prepare its call */
@ -332,10 +331,10 @@ int luaD_precall (lua_State *L, StkId func, int nresults) {
if (L->hookmask & LUA_MASKCALL)
luaD_callhook(L, LUA_HOOKCALL, -1);
lua_unlock(L);
if (ttisfunction(ci->func))
n = (*curr_func(L)->c.f)(L); /* do the actual call */
else
if (ttislightfunction(ci->func))
n = ((lua_CFunction)fvalue(ci->func))(L); /* do the actual call */
else
n = (*curr_func(L)->c.f)(L); /* do the actual call */
lua_lock(L);
if (n < 0) /* yielding? */
return PCRYIELD;

View File

@ -6,7 +6,6 @@
#define ldump_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
#include <string.h>

View File

@ -1,12 +0,0 @@
// Lua EGC (Emergeny Garbage Collector) interface
#include "legc.h"
#include "lstate.h"
void legc_set_mode(lua_State *L, int mode, int limit) {
global_State *g = G(L);
g->egcmode = mode;
g->memlimit = limit;
}

View File

@ -1,17 +0,0 @@
// Lua EGC (Emergeny Garbage Collector) interface
#ifndef __LEGC_H__
#define __LEGC_H__
#include "lstate.h"
// EGC operations modes
#define EGC_NOT_ACTIVE 0 // EGC disabled
#define EGC_ON_ALLOC_FAILURE 1 // run EGC on allocation failure
#define EGC_ON_MEM_LIMIT 2 // run EGC when an upper memory limit is hit
#define EGC_ALWAYS 4 // always run EGC before an allocation
void legc_set_mode(lua_State *L, int mode, int limit);
#endif

View File

@ -5,7 +5,6 @@
#define lflash_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
#include "lobject.h"
@ -89,9 +88,8 @@ void dumpStrt(stringtable *tb, const char *type) {
for (i=0; i<tb->size; i++)
for(o = tb->hash[i], j=0; o; (o=o->gch.next), j++ ) {
TString *ts =cast(TString *, o);
NODE_DBG("%5d %5d %08x %08x %5d %1s %s\n",
i, j, (size_t) ts, ts->tsv.hash, ts->tsv.len,
ts_isreadonly(ts) ? "R" : " ", getstr(ts));
NODE_DBG("%5d %5d %08x %08x %5d %s\n",
i, j, (size_t) ts, ts->tsv.hash, ts->tsv.len, getstr(ts));
}
}
@ -200,7 +198,6 @@ static void procFirstPass (void);
* Library function called by node.flashreload(filename).
*/
LUALIB_API int luaN_reload_reboot (lua_State *L) {
// luaL_dbgbreak();
const char *fn = lua_tostring(L, 1), *msg = "";
int status;

View File

@ -6,7 +6,6 @@
#define lfunc_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
#include <string.h>
@ -146,16 +145,14 @@ void luaF_freeproto (lua_State *L, Proto *f) {
luaM_freearray(L, f->k, f->sizek, TValue);
luaM_freearray(L, f->locvars, f->sizelocvars, struct LocVar);
luaM_freearray(L, f->upvalues, f->sizeupvalues, TString *);
if (!proto_isreadonly(f)) {
luaM_freearray(L, f->code, f->sizecode, Instruction);
luaM_freearray(L, f->code, f->sizecode, Instruction);
#ifdef LUA_OPTIMIZE_DEBUG
if (f->packedlineinfo) {
luaM_freearray(L, f->packedlineinfo, strlen(cast(char *, f->packedlineinfo))+1, unsigned char);
}
#else
luaM_freearray(L, f->lineinfo, f->sizelineinfo, int);
#endif
if (f->packedlineinfo) {
luaM_freearray(L, f->packedlineinfo, strlen(cast(char *, f->packedlineinfo))+1, unsigned char);
}
#else
luaM_freearray(L, f->lineinfo, f->sizelineinfo, int);
#endif
luaM_free(L, f);
}

View File

@ -6,7 +6,6 @@
#define lgc_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
#include <string.h>
@ -21,7 +20,6 @@
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#include "lrotable.h"
#define GCSTEPSIZE 1024u
#define GCSWEEPMAX 40
@ -54,7 +52,6 @@
#define markobject(g,t) { if (iswhite(obj2gco(t))) \
reallymarkobject(g, obj2gco(t)); }
#define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause)
@ -81,7 +78,7 @@ static void reallymarkobject (global_State *g, GCObject *o) {
case LUA_TUSERDATA: {
Table *mt = gco2u(o)->metatable;
gray2black(o); /* udata are never gray */
if (mt && !luaR_isrotable(mt)) markobject(g, mt);
if (mt && isrwtable(mt)) markobject(g, mt);
markobject(g, gco2u(o)->env);
return;
}
@ -159,7 +156,6 @@ size_t luaC_separateudata (lua_State *L, int all) {
return deadmem;
}
static int traversetable (global_State *g, Table *h) {
int i;
int weakkey = 0;
@ -167,7 +163,7 @@ static int traversetable (global_State *g, Table *h) {
const TValue *mode = luaO_nilobject;
if (h->metatable) {
if (!luaR_isrotable(h->metatable))
if (isrwtable(h->metatable))
markobject(g, h->metatable);
mode = gfasttm(g, h->metatable, TM_MODE);
}
@ -330,13 +326,11 @@ static l_mem propagatemark (global_State *g) {
sizeof(TValue) * p->sizek +
sizeof(LocVar) * p->sizelocvars +
sizeof(TString *) * p->sizeupvalues +
(proto_isreadonly(p) ? 0 : sizeof(Instruction) * p->sizecode +
sizeof(Instruction) * p->sizecode +
#ifdef LUA_OPTIMIZE_DEBUG
(p->packedlineinfo ?
strlen(cast(char *, p->packedlineinfo))+1 :
0));
(p->packedlineinfo ? strlen(cast(char *, p->packedlineinfo))+1 : 0);
#else
sizeof(int) * p->sizelineinfo);
sizeof(int) * p->sizelineinfo;
#endif
}
default: lua_assert(0); return 0;
@ -522,7 +516,7 @@ void luaC_freeall (lua_State *L) {
static void markmt (global_State *g) {
int i;
for (i=0; i<NUM_TAGS; i++)
if (g->mt[i] && !luaR_isrotable(g->mt[i])) markobject(g, g->mt[i]);
if (g->mt[i] && isrwtable(g->mt[i])) markobject(g, g->mt[i]);
}
@ -712,7 +706,7 @@ void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) {
global_State *g = G(L);
lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause);
lua_assert(o->gch.tt != LUA_TTABLE);
lua_assert((gettt(o) & LUA_TMASK) != LUA_TTABLE);
/* must keep invariant? */
if (g->gcstate == GCSpropagate)
reallymarkobject(g, v); /* Restore invariant */

View File

@ -7,109 +7,106 @@
#define linit_c
#define LUA_LIB
#define LUAC_CROSS_FILE
/*
** NodeMCU uses RO segment based static ROTable declarations for all library
** tables, including the index of library tables itself (the ROM table). These
** tables can be moved from RAM to flash ROM on the ESPs.
**
** On the ESPs targets, we can marshal the table entries through linker-based
** PSECTs to enable the library initiation tables to be bound during the link
** process rather than being statically declared here. This simplifies the
** addition of new modules and configuring builds with a subset of the total
** modules available.
**
** Such a linker-based approach is not practical for cross compiler builds that
** must link on a range of platforms, and where we don't have control of PSECT
** placement. However unlike the target builds, the luac.cross builds only
** use a small and fixed list of libraries and so in this case all of libraries
** are defined here, avoiding the need for linker magic on host builds.
**
** Note that a separate ROTable is defined in lbaselib.c on luac.cross builds
** for the base functions. (These use linker based entries on target builds)
** and there is a metatable index cascade from _G to this base function table
** to the master rotables table. In the target build, the linker marshals the
** table, hence the LROT_BREAK() macros which don't 0 terminate the lists and
** skip generating the ROtable header.
*/
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include "luaconf.h"
#include "module.h"
#include "lstate.h"
#include "lnodemcu.h"
extern LROT_TABLE(strlib);
extern LROT_TABLE(tab_funcs);
extern LROT_TABLE(dblib);
extern LROT_TABLE(co_funcs);
extern LROT_TABLE(math);
#define LROT_ROM_ENTRIES \
LROT_TABENTRY( string, strlib ) \
LROT_TABENTRY( table, tab_funcs ) \
LROT_TABENTRY( debug, dblib) \
LROT_TABENTRY( coroutine, co_funcs ) \
LROT_TABENTRY( math, math ) \
LROT_TABENTRY( ROM, rotables )
#define LROT_LIB_ENTRIES \
LROT_FUNCENTRY( _G, luaopen_base ) /* This MUST be called first */ \
LROT_FUNCENTRY( package, luaopen_package ) \
LROT_FUNCENTRY( string, luaopen_string ) \
LROT_FUNCENTRY( debug, luaopen_debug )
LROT_EXTERN(strlib);
LROT_EXTERN(tab_funcs);
LROT_EXTERN(dblib);
LROT_EXTERN(co_funcs);
LROT_EXTERN(math);
#if defined(LUA_CROSS_COMPILER)
LROT_EXTERN(oslib);
LROT_EXTERN(iolib);
#endif
/*
* The NodeMCU Lua initalisation has been adapted to use linker-based module
* registration. This uses a PSECT naming convention to allow the ROTable and
* initialisation function entries to be collected by the linker into two
* consoliated ROTables. This significantly simplifies adding new modules and
* configuring builds with a small subset of the total modules.
*
* This linker-based approach is not practical for cross compiler builds which
* must link on a range of platforms, and where we don't have control of PSECT
* placement. However unlike the target builds, the luac.cross builds only
* used a small and fixed list of libraries and so in this case the table can
* be defined in this source file, so in this case all library ROTables are
* defined here, avoiding the need for linker magic is avoided on host builds.
*
* Note that a separate ROTable is defined in lbaselib.c for the base functions
* and there is a metatable index cascade from _G to this base function table to
* the master rotables table. In the target build, the linker marshals the
* table, hence the LROT_BREAK() macros which don't 0 terminate the lists.
*/
extern LROT_TABLE(base_func);
LROT_BEGIN(rotables_meta, NULL, LROT_MASK_INDEX)
LROT_TABENTRY( _index, base_func)
LROT_END(rotables_meta, NULL, LROT_MASK_INDEX)
#ifdef _MSC_VER
//MSVC requires us to declare these sections before we refer to them
#pragma section(__ROSECNAME(A), read)
#pragma section(__ROSECNAME(zzzzzzzz), read)
#pragma section(__ROSECNAME(libs), read)
#pragma section(__ROSECNAME(rotable), read)
//These help us to find the beginning and ending of the RO data. NOTE: linker
//magic is used; the section names are lexically sorted, so 'a' and 'z' are
//important to keep the other sections lexically between these two dummy
//variables. Check your mapfile output if you need to fiddle with this stuff.
const LOCK_IN_SECTION(A) char _ro_start[1] = {0};
const LOCK_IN_SECTION(zzzzzzzz) char _ro_end[1] = {0};
#endif
LROT_PUBLIC_TABLE(lua_rotables)
LROT_PUBLIC_BEGIN(LOCK_IN_SECTION(rotable) lua_rotables)
LROT_TABENTRY( string, strlib )
LROT_TABENTRY( table, tab_funcs )
LROT_TABENTRY( debug, dblib)
LROT_TABENTRY( coroutine, co_funcs )
LROT_TABENTRY( math, math )
LROT_TABENTRY( ROM, lua_rotables )
#ifdef LUA_CROSS_COMPILER
extern LROT_TABLE(oslib);
extern LROT_TABLE(iolib);
LROT_BEGIN(rotables, LROT_TABLEREF(rotables_meta), 0)
LROT_ROM_ENTRIES
LROT_TABENTRY( os, oslib )
LROT_TABENTRY( io, iolib )
LROT_END(lua_rotables, NULL, 0)
#else
LROT_BREAK(lua_rotables)
#endif
LROT_END(rotables, LROT_TABLEREF(rotables_meta), 0)
LROT_PUBLIC_BEGIN(LOCK_IN_SECTION(libs) lua_libs)
LROT_FUNCENTRY( _, luaopen_base )
LROT_FUNCENTRY( package, luaopen_package )
LROT_FUNCENTRY( string, luaopen_string )
LROT_FUNCENTRY( table, luaopen_table )
LROT_FUNCENTRY( debug, luaopen_debug )
#ifndef LUA_CROSS_COMPILER
LROT_BREAK(lua_rotables)
#else
LROT_BEGIN(lua_libs, NULL, 0)
LROT_LIB_ENTRIES
LROT_FUNCENTRY( io, luaopen_io )
LROT_END( lua_libs, NULL, 0)
LROT_END(lua_libs, NULL, 0)
#else
extern const ROTable_entry lua_libs_base[];
extern const ROTable_entry lua_rotable_base[];
ROTable rotables_ROTable;
LROT_ENTRIES_IN_SECTION(rotables, rotable)
LROT_ROM_ENTRIES
LROT_BREAK(rotables)
LROT_ENTRIES_IN_SECTION(lua_libs, libs)
LROT_LIB_ENTRIES
LROT_BREAK(lua_libs)
#endif
#ifndef LUA_CROSS_COMPILER
extern void luaL_dbgbreak(void);
#endif
void luaL_openlibs (lua_State *L) {
lua_pushrotable(L, LROT_TABLEREF(lua_libs));
lua_pushnil(L); /* first key */
/* loop round and open libraries */
#ifndef LUA_CROSS_COMPILER
// luaL_dbgbreak(); // This is a test point for debugging library start ups
#ifdef LUA_CROSS_COMPILER
const ROTable_entry *p = LROT_TABLEREF(lua_libs)->entry;
#else
const ROTable_entry *p = lua_libs_base;
lua_createrotable(L, LROT_TABLEREF(rotables), lua_rotable_base, NULL);
#endif
while (lua_next(L, -2) != 0) {
if (lua_islightfunction(L,-1) &&
fvalue(L->top-1)) { // only process function entries
lua_pushvalue(L, -2);
while (p->key) {
if (ttislightfunction(&p->value) && fvalue(&p->value)) {
lua_pushcfunction(L, fvalue(&p->value));
lua_pushstring(L, p->key);
lua_call(L, 1, 0); // call luaopen_XXX(libname)
} else {
lua_pop(L, 1);
}
p++;
}
lua_pop(L, 1); //cleanup stack
}

View File

@ -7,7 +7,6 @@
#define llex_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
#include <ctype.h>

View File

@ -7,7 +7,6 @@
#define lmathlib_c
#define LUA_LIB
#define LUAC_CROSS_FILE
#include "lua.h"
#include <stdlib.h>
@ -15,7 +14,7 @@
#include "lauxlib.h"
#include "lualib.h"
#include "lrotable.h"
#include "lnodemcu.h"
#undef PI
#define PI (3.14159265358979323846)
@ -309,7 +308,7 @@ static int math_randomseed (lua_State *L) {
return 0;
}
LROT_PUBLIC_BEGIN(math)
LROT_BEGIN(math, NULL, 0)
#ifdef LUA_NUMBER_INTEGRAL
LROT_FUNCENTRY( abs, math_abs )
LROT_FUNCENTRY( ceil, math_identity )

View File

@ -7,7 +7,6 @@
#define lmem_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"

View File

@ -18,10 +18,9 @@
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#include "lrotable.h"
#include "lnodemcu.h"
#include "platform.h"
extern int debug_errorfb (lua_State *L);
/*
** Error Reporting Task. We can't pass a string parameter to the error reporter
** directly through the task interface the call is wrapped in a C closure with
@ -31,43 +30,42 @@ static int report_traceback (lua_State *L) {
// **Temp** lua_rawgeti(L, LUA_REGISTRYINDEX, G(L)->error_reporter);
lua_getglobal(L, "print");
lua_pushvalue(L, lua_upvalueindex(1));
lua_call(L, 1, 0); /* Using an error handler would cause an infinite loop! */
lua_call(L, 1, 0); /* Using an error handler would cause an infinite loop! */
return 0;
}
/*
** Catch all error handler for CB calls. This uses debug.traceback() to
** generate a full Lua traceback.
** generate a full Lua traceback.
*/
int luaN_traceback (lua_State *L) {
LUALIB_API int luaL_traceback (lua_State *L) {
if (lua_isstring(L, 1)) {
lua_pushlightfunction(L, &debug_errorfb);
lua_getglobal(L, "debug");
lua_getfield(L, -1, "traceback");
lua_remove(L, -2);
lua_pushvalue(L, 1); /* pass error message */
lua_pushinteger(L, 2); /* skip this function and traceback */
lua_call(L, 2, 1); /* call debug.traceback and return it as a string */
lua_pushcclosure(L, report_traceback, 1); /* report with str as upval */
luaN_posttask(L, LUA_TASK_HIGH);
}
luaL_posttask(L, LUA_TASK_HIGH);
}
return 0;
}
/*
** Use in CBs and other C functions to call a Lua function. This includes
** an error handler which will catch any error and then post this to the
** an error handler which will catch any error and then post this to the
** registered reporter function as a separate follow-on task.
*/
int luaN_call (lua_State *L, int narg, int nres, int doGC) { // [-narg, +0, v]
LUALIB_API int luaL_pcallx (lua_State *L, int narg, int nres) { // [-narg, +0, v]
int status;
int base = lua_gettop(L) - narg;
lua_pushcfunction(L, luaN_traceback);
lua_pushcfunction(L, luaL_traceback);
lua_insert(L, base); /* put under args */
status = lua_pcall(L, narg, (nres < 0 ? LUA_MULTRET : nres), base);
lua_remove(L, base); /* remove traceback function */
if (status && nres >=0)
lua_settop(L, base + nres); /* balance the stack on error */
/* force a complete garbage collection if requested */
if (doGC)
lua_gc(L, LUA_GCCOLLECT, 0);
lua_settop(L, base + nres); /* balance the stack on error */
return status;
}
@ -84,21 +82,21 @@ static void do_task (platform_task_param_t task_fn_ref, uint8_t prio) {
/* Pop the CB func from the Reg */
//dbg_printf("calling Reg[%u]\n", task_fn_ref);
lua_rawgeti(L, LUA_REGISTRYINDEX, (int) task_fn_ref);
luaL_checkanyfunction(L, -1);
luaL_checkfunction(L, -1);
luaL_unref(L, LUA_REGISTRYINDEX, (int) task_fn_ref);
lua_pushinteger(L, prio);
luaN_call (L, 1, 0, 1);
luaL_pcallx(L, 1, 0);
}
/*
** Schedule a Lua function for task execution
*/
#include "lstate.h" /*DEBUG*/
LUA_API int luaN_posttask( lua_State* L, int prio ) { // [-1, +0, -]
LUALIB_API int luaL_posttask( lua_State* L, int prio ) { // [-1, +0, -]
if (!task_handle)
task_handle = platform_task_get_id(do_task);
if (!lua_isanyfunction(L, -1) || prio < LUA_TASK_LOW|| prio > LUA_TASK_HIGH)
if (!lua_isfunction(L, -1) || prio < LUA_TASK_LOW|| prio > LUA_TASK_HIGH)
luaL_error(L, "invalid posk task");
//void *cl = clvalue(L->top-1);
int task_fn_ref = luaL_ref(L, LUA_REGISTRYINDEX);
@ -110,3 +108,29 @@ LUA_API int luaN_posttask( lua_State* L, int prio ) { // [-1, +0, -]
return task_fn_ref;
}
LUA_API void lua_createrotable (lua_State *L, ROTable *t, const ROTable_entry *e, ROTable *mt) {
int i, j;
lu_byte flags = ~0;
const char *plast = (char *)"_";
for (i = 0; e[i].key; i++) {
if (e[i].key[0] == '_' && strcmp(e[i].key,plast)) {
plast = e[i].key;
lua_pushstring(L,e[i].key);
for (j=0; j<TM_EQ; j++){
if(rawtsvalue(L->top-1)==G(L)->tmname[i]) {
flags |= cast_byte(1u<<i);
break;
}
}
lua_pop(L,1);
}
}
t->next = (GCObject *)1;
t->tt = LUA_TROTABLE;
t->marked = LROT_MARKED;
t->flags = flags;
t->lsizenode = i;
t->metatable = cast(Table *, mt);
t->entry = cast(ROTable_entry *, e);
}

67
app/lua/lnodemcu.h Normal file
View File

@ -0,0 +1,67 @@
/* Read-only tables for Lua */
#ifndef lnodemcu_h
#define lnodemcu_h
#include "lua.h"
#include "lobject.h"
#include "llimits.h"
#include "ltm.h"
#ifdef LUA_USE_HOST
#define LRO_STRKEY(k) k
#define LOCK_IN_SECTION(s)
#else
#define LRO_STRKEY(k) ((STORE_ATTR char *) k)
#define LOCK_IN_SECTION(s) __attribute__((used,unused,section(".lua_" #s)))
#endif
/* Macros one can use to define rotable entries */
#define LRO_FUNCVAL(v) {{.p = v}, LUA_TLIGHTFUNCTION}
#define LRO_LUDATA(v) {{.p = cast(void*,v)}, LUA_TLIGHTUSERDATA}
#define LRO_NILVAL {{.p = NULL}, LUA_TNIL}
#define LRO_NUMVAL(v) {{.n = v}, LUA_TNUMBER}
#define LRO_INTVAL(v) LRO_NUMVAL(v)
#define LRO_FLOATVAL(v) LRO_NUMVAL(v)
#define LRO_ROVAL(v) {{.gc = cast(GCObject *, &(v ## _ROTable))}, LUA_TROTABLE}
#define LROT_MARKED 0 //<<<<<<<<<<*** TBD *** >>>>>>>>>>>
#define LROT_FUNCENTRY(n,f) {LRO_STRKEY(#n), LRO_FUNCVAL(f)},
#define LROT_LUDENTRY(n,x) {LRO_STRKEY(#n), LRO_LUDATA(x)},
#define LROT_NUMENTRY(n,x) {LRO_STRKEY(#n), LRO_NUMVAL(x)},
#define LROT_INTENTRY(n,x) LROT_NUMENTRY(n,x)
#define LROT_FLOATENTRY(n,x) LROT_NUMENTRY(n,x)
#define LROT_TABENTRY(n,t) {LRO_STRKEY(#n), LRO_ROVAL(t)},
#define LROT_TABLE(rt) const ROTable rt ## _ROTable
#define LROT_ENTRYREF(rt) (rt ##_entries)
#define LROT_TABLEREF(rt) (&rt ##_ROTable)
#define LROT_BEGIN(rt,mt,f) LROT_TABLE(rt); \
static const ROTable_entry rt ## _entries[] = {
#define LROT_ENTRIES_IN_SECTION(rt,s) \
static const ROTable_entry LOCK_IN_SECTION(s) rt ## _entries[] = {
#define LROT_END(rt,mt,f) {NULL, LRO_NILVAL} }; \
const ROTable rt ## _ROTable = { \
(GCObject *)1, LUA_TROTABLE, LROT_MARKED, \
cast(lu_byte, ~(f)), (sizeof(rt ## _entries)/sizeof(ROTable_entry)) - 1, \
cast(Table *, mt), cast(ROTable_entry *, rt ## _entries) };
#define LROT_BREAK(rt) };
#define LROT_MASK(m) cast(lu_byte, 1<<TM_ ## m)
/*
* These are statically coded can be any combination of the fast index tags
* listed in ltm.h: EQ, GC, INDEX, LEN, MODE, NEWINDEX or combined by anding
* GC+INDEX is the only common combination used, hence the combinaton macro
*/
#define LROT_MASK_EQ LROT_MASK(EQ)
#define LROT_MASK_GC LROT_MASK(GC)
#define LROT_MASK_INDEX LROT_MASK(INDEX)
#define LROT_MASK_LEN LROT_MASK(LEN)
#define LROT_MASK_MODE LROT_MASK(MODE)
#define LROT_MASK_NEWINDEX LROT_MASK(NEWINDEX)
#define LROT_MASK_GC_INDEX (LROT_MASK_GC | LROT_MASK_INDEX)
/* Maximum length of a rotable name and of a string key*/
#endif

View File

@ -11,7 +11,6 @@
#define loadlib_c
#define LUA_LIB
#define LUAC_CROSS_FILE
#include "lua.h"
#include <stdlib.h>
@ -24,7 +23,7 @@
#include "lauxlib.h"
#include "lualib.h"
#include "lrotable.h"
#include "lnodemcu.h"
/* prefix for open functions in C libraries */
#define LUA_POF "luaopen_"
@ -394,11 +393,7 @@ static int loader_Lua (lua_State *L) {
const char *name = luaL_checkstring(L, 1);
filename = findfile(L, name, "path");
if (filename == NULL) return 1; /* library not found in this path */
#ifdef LUA_CROSS_COMPILER
if (luaL_loadfile(L, filename) != 0)
#else
if (luaL_loadfile(L, filename) != 0)
#endif
loaderror(L, filename);
return 1; /* library loaded successfully */
}
@ -657,9 +652,9 @@ static const luaL_Reg ll_funcs[] = {
static const lua_CFunction loaders[] =
{loader_preload, loader_Lua, loader_C, loader_Croot, NULL};
LROT_PUBLIC_BEGIN(lmt)
LROT_FUNCENTRY(__gc,gctm)
LROT_END(lmt,lmt, LROT_MASK_GC)
LROT_BEGIN(lmt, NULL, LROT_MASK_GC)
LROT_FUNCENTRY( __gc, gctm )
LROT_END(lmt, NULL, LROT_MASK_GC)
LUALIB_API int luaopen_package (lua_State *L) {
int i;

View File

@ -1,3 +1,4 @@
/*
** $Id: lobject.c,v 2.22.1.1 2007/12/27 13:02:25 roberto Exp $
** Some generic functions over Lua objects
@ -7,7 +8,6 @@
#define lobject_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
#include <stdio.h>
@ -88,7 +88,7 @@ int luaO_rawequalObj (const TValue *t1, const TValue *t2) {
case LUA_TLIGHTUSERDATA:
return pvalue(t1) == pvalue(t2);
case LUA_TROTABLE:
return rvalue(t1) == rvalue(t2);
return hvalue(t1) == hvalue(t2);
case LUA_TLIGHTFUNCTION:
return fvalue(t1) == fvalue(t2);
default:

View File

@ -28,15 +28,18 @@
#define LUA_TUPVAL (LAST_TAG+2)
#define LUA_TDEADKEY (LAST_TAG+3)
#ifdef __XTENSA__
#ifdef LUA_USE_ESP
/*
** force aligned access to critical fields in Flash-based structures
** wo is the offset of aligned word in bytes 0,4,8,..
** bo is the field within the word in bits 0..31
**
** Note that this returns a lu_int32 as returning a byte can cause the
** gcc code generator to emit an extra extui instruction.
*/
#define GET_BYTE_FN(name,t,wo,bo) \
static inline lu_byte get ## name(void *o) { \
lu_byte res; /* extract named field */ \
static inline lu_int32 get ## name(void *o) { \
lu_int32 res; /* extract named field */ \
asm ("l32i %0, %1, " #wo "; extui %0, %0, " #bo ", 8;" : "=r"(res) : "r"(o) : );\
return res; }
#else
@ -91,43 +94,47 @@ typedef union {
#define TValuefields Value value; int tt
#define LUA_TVALUE_NIL {NULL}, LUA_TNIL
#if defined(LUA_PACK_TVALUES) && !defined(LUA_CROSS_COMPILER)
#pragma pack(4)
#ifdef LUA_USE_ESP
# pragma pack(4)
#endif
typedef struct lua_TValue {
TValuefields;
} TValue;
#if defined(LUA_PACK_TVALUES) && !defined(LUA_CROSS_COMPILER)
#pragma pack()
#ifdef LUA_USE_ESP
# pragma pack()
#endif
/* Macros to test type */
#define ttisnil(o) (ttype(o) == LUA_TNIL)
#define ttisnumber(o) (ttype(o) == LUA_TNUMBER)
#define ttisstring(o) (ttype(o) == LUA_TSTRING)
#define ttistable(o) (ttype(o) == LUA_TTABLE)
#define ttisfunction(o) (ttype(o) == LUA_TFUNCTION)
#define ttisboolean(o) (ttype(o) == LUA_TBOOLEAN)
#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA)
#define ttisthread(o) (ttype(o) == LUA_TTHREAD)
#define ttisnil(o) (ttype(o) == LUA_TNIL)
#define ttisnumber(o) (ttype(o) == LUA_TNUMBER)
#define ttisstring(o) (ttype(o) == LUA_TSTRING)
#define ttistable(o) (basettype(o) == LUA_TTABLE)
#define ttisrwtable(o) (type(o) == LUA_TTABLE)
#define ttisrotable(o) (ttype(o) & LUA_TISROTABLE)
#define ttisboolean(o) (ttype(o) == LUA_TBOOLEAN)
#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA)
#define ttisthread(o) (ttype(o) == LUA_TTHREAD)
#define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA)
#define ttisrotable(o) (ttype(o) == LUA_TROTABLE)
#define ttislightfunction(o) (ttype(o) == LUA_TLIGHTFUNCTION)
#define ttisanyfunction(o) (ttisfunction(o) || ttislightfunction(o))
#define ttislightfunction(o) (ttype(o) == LUA_TLIGHTFUNCTION)
#define ttisclfunction(o) (ttype(o) == LUA_TFUNCTION)
#define ttisfunction(o) (basettype(o) == LUA_TFUNCTION)
/* Macros to access values */
#define ttype(o) ((void) (o)->value, (o)->tt)
#define basettype(o) ((void) (o)->value, ((o)->tt & LUA_TMASK))
#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc)
#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p)
#define rvalue(o) check_exp(ttisrotable(o), (o)->value.p)
#define fvalue(o) check_exp(ttislightfunction(o), (o)->value.p)
#define fvalue(o) check_exp(ttislightfunction(o), (o)->value.p)
#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n)
#define rawtsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts)
#define tsvalue(o) (&rawtsvalue(o)->tsv)
#define rawuvalue(o) check_exp(ttisuserdata(o), &(o)->value.gc->u)
#define uvalue(o) (&rawuvalue(o)->uv)
#define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl)
#define clvalue(o) check_exp(ttisclfunction(o), &(o)->value.gc->cl)
#define hvalue(o) check_exp(ttistable(o), &(o)->value.gc->h)
#define bvalue(o) check_exp(ttisboolean(o), (o)->value.b)
#define thvalue(o) check_exp(ttisthread(o), &(o)->value.gc->th)
@ -154,9 +161,6 @@ typedef struct lua_TValue {
#define setpvalue(obj,x) \
{ void *i_x = (x); TValue *i_o=(obj); i_o->value.p=i_x; i_o->tt=LUA_TLIGHTUSERDATA; }
#define setrvalue(obj,x) \
{ void *i_x = (x); TValue *i_o=(obj); i_o->value.p=i_x; i_o->tt=LUA_TROTABLE; }
#define setfvalue(obj,x) \
{ void *i_x = (x); TValue *i_o=(obj); i_o->value.p=i_x; i_o->tt=LUA_TLIGHTFUNCTION; }
@ -190,7 +194,7 @@ typedef struct lua_TValue {
#define sethvalue(L,obj,x) \
{ GCObject *i_x = cast(GCObject *, (x)); \
TValue *i_o=(obj); \
i_o->value.gc=i_x; i_o->tt=LUA_TTABLE; \
i_o->value.gc=i_x; i_o->tt=gettt(x); \
checkliveness(G(L),i_o); }
#define setptvalue(L,obj,x) \
@ -225,7 +229,7 @@ typedef struct lua_TValue {
#define setttype(obj, stt) ((void) (obj)->value, (obj)->tt = (stt))
#define iscollectable(o) (ttype(o) >= LUA_TSTRING)
#define iscollectable(o) (ttype(o) >= LUA_TSTRING && ttype(o) <= LUA_TMASK)
typedef TValue *StkId; /* index to stack elements */
@ -243,19 +247,10 @@ typedef union TString {
} tsv;
} TString;
#ifdef LUA_CROSS_COMPILER
#define isreadonly(o) (0)
#else
#define isreadonly(o) ((o).marked & READONLYMASK)
#endif
#define ts_isreadonly(ts) isreadonly((ts)->tsv)
#define getstr(ts) (ts_isreadonly(ts) ? \
cast(const char *, *(const char**)((ts) + 1)) : \
cast(const char *, (ts) + 1))
#define getstr(ts) cast(const char *, (ts) + 1)
#define svalue(o) getstr(rawtsvalue(o))
typedef union Udata {
L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */
struct {
@ -266,7 +261,12 @@ typedef union Udata {
} uv;
} Udata;
#ifdef LUA_CROSS_COMPILER
#define isreadonly(o) (0)
#else
#define isreadonly(o) (getmarked(o) & READONLYMASK)
#endif
#define islfs(o) (getmarked(o) & LFSMASK)
/*
@ -301,7 +301,6 @@ typedef struct Proto {
lu_byte is_vararg;
lu_byte maxstacksize;
} Proto;
#define proto_isreadonly(p) isreadonly(*(p))
/* masks for new-style vararg */
@ -371,6 +370,18 @@ typedef union Closure {
** Tables
*/
/*
** Common Table fields for both table versions (like CommonHeader in
** macro form, to be included in table structure definitions).
**
** Note that the sethvalue() macro works much like the setsvalue()
** macro and handles the abstracted type. the hvalue(o) macro can be
** used to access CommonTable fields and rw Table fields
*/
#define CommonTable CommonHeader; \
lu_byte flags; lu_byte lsizenode; struct Table *metatable;
typedef union TKey {
struct {
TValuefields;
@ -388,10 +399,7 @@ typedef struct Node {
typedef struct Table {
CommonHeader;
lu_byte flags; /* 1<<p means tagmethod(p) is not present */
lu_byte lsizenode; /* log2 of size of `node' array */
struct Table *metatable;
CommonTable;
TValue *array; /* array part */
Node *node;
Node *lastfree; /* any free position is before this position */
@ -399,7 +407,23 @@ typedef struct Table {
int sizearray; /* size of `array' array */
} Table;
typedef const struct luaR_entry ROTable;
GET_BYTE_FN(flags,Table,4,16)
GET_BYTE_FN(lsizenode,Table,4,24)
typedef struct ROTable_entry {
const char *key;
const TValue value;
} ROTable_entry;
typedef struct ROTable {
/* next always has the value (GCObject *)((size_t) 1); */
/* flags & 1<<p means tagmethod(p) is not present */
/* lsizenode is unused */
/* Like TStrings, the ROTable_entry vector follows the ROTable */
CommonTable;
ROTable_entry *entry;
} ROTable;
/*
** `module' operation for hashing (size is always a power of 2)

View File

@ -6,9 +6,7 @@
#define lopcodes_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "luac_cross.h"
#include "lopcodes.h"
/* ORDER OP */

View File

@ -7,7 +7,6 @@
#define lparser_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
#include <string.h>

View File

@ -1,53 +0,0 @@
/* Read-only tables helper */
#ifndef lrodefs_h
#define lrodefs_h
#include "lrotable.h"
#undef LUA_REG_TYPE
#undef LSTRKEY
#undef LNILKEY
#undef LNUMKEY
#undef LFUNCVAL
#undef LNUMVAL
#undef LROVAL
#undef LNILVAL
#undef LREGISTER
#if LUA_OPTIMIZE_MEMORY >=1
#define LUA_REG_TYPE luaR_entry
#define LSTRKEY LRO_STRKEY
#define LNUMKEY LRO_NUMKEY
#define LNILKEY LRO_NILKEY
#define LFUNCVAL LRO_FUNCVAL
#define LUDATA LRO_LUDATA
#define LNUMVAL LRO_NUMVAL
#define LROVAL LRO_ROVAL
#define LNILVAL LRO_NILVAL
#define LREGISTER(L, name, table) return 0
#else
#define LUA_REG_TYPE luaL_reg
#define LSTRKEY(x) x
#define LNILKEY NULL
#define LFUNCVAL(x) x
#define LNILVAL NULL
#define LREGISTER(L, name, table)\
luaL_register(L, name, table);\
return 1
#endif
#define LROT_TABLE(t) static const LUA_REG_TYPE t ## _map[];
#define LROT_TABLEREF(t) ((void *) t ## _map)
#define LROT_BEGIN(t) static const LUA_REG_TYPE t ## _map [] = {
#define LROT_PUBLIC_BEGIN(t) const LUA_REG_TYPE t ## _map[] = {
#define LROT_EXTERN(t) extern const LUA_REG_TYPE t ## _map[]
#define LROT_TABENTRY(n,t) {LRO_STRKEY(#n), LRO_ROVAL(t ## _map)},
#define LROT_FUNCENTRY(n,f) {LRO_STRKEY(#n), LRO_FUNCVAL(f)},
#define LROT_NUMENTRY(n,x) {LRO_STRKEY(#n), LRO_NUMVAL(x)},
#define LROT_LUDENTRY(n,x) {LRO_STRKEY(#n), LRO_LUDATA((void *) x)},
#define LROT_END(t,mt, f) {LRO_NILKEY, LRO_NILVAL} };
#define LREGISTER(L, name, table) return 0
#endif /* lrodefs_h */

View File

@ -1,168 +0,0 @@
/* Read-only tables for Lua */
#define LUAC_CROSS_FILE
#include "lua.h"
#include <string.h>
#include "lrotable.h"
#include "lauxlib.h"
#include "lstring.h"
#include "lobject.h"
#include "lapi.h"
#ifdef _MSC_VER
#define ALIGNED_STRING (__declspec( align( 4 ) ) char*)
#else
#define ALIGNED_STRING (__attribute__((aligned(4))) char *)
#endif
#define LA_LINES 32
#define LA_SLOTS 4
//#define COLLECT_STATS
/*
* All keyed ROtable access passes through luaR_findentry(). ROTables
* are simply a list of <key><TValue value> pairs. The existing algo
* did a linear scan of this vector of pairs looking for a match.
*
* A N×M lookaside cache has been added, with a simple hash on the key's
* TString addr and the ROTable addr to identify one of N lines. Each
* line has M slots which are scanned. This is all done in RAM and is
* perhaps 20x faster than the corresponding random Flash accesses which
* will cause flash faults.
*
* If a match is found and the table addresses match, then this entry is
* probed first. In practice the hit-rate here is over 99% so the code
* rarely fails back to doing the linear scan in ROM.
*
* Note that this hash does a couple of prime multiples and a modulus 2^X
* with is all evaluated in H/W, and adequately randomizes the lookup.
*/
#define HASH(a,b) (unsigned)((((519*(size_t)(a)))>>4) + ((b) ? (b)->tsv.hash: 0))
typedef struct {
unsigned hash;
unsigned addr:24;
unsigned ndx:8;
} cache_line_t;
static cache_line_t cache [LA_LINES][LA_SLOTS];
#ifdef COLLECT_STATS
unsigned cache_stats[3];
#define COUNT(i) cache_stats[i]++
#else
#define COUNT(i)
#endif
static int lookup_cache(unsigned hash, ROTable *rotable) {
int i = (hash>>2) & (LA_LINES-1), j;
for (j = 0; j<LA_SLOTS; j++) {
cache_line_t cl = cache[i][j];
if (cl.hash == hash && ((size_t)rotable & 0xffffffu) == cl.addr) {
COUNT(0);
return cl.ndx;
}
}
COUNT(1);
return -1;
}
static void update_cache(unsigned hash, ROTable *rotable, unsigned ndx) {
int i = (hash)>>2 & (LA_LINES-1), j;
#ifndef _MSC_VER
cache_line_t cl = {hash, (size_t) rotable, ndx};
#else
cache_line_t cl; // MSC doesn't allow non-scalar initialisers, which
cl.hash = hash; // is a pity because xtensa gcc generates optimum
cl.addr = (size_t) rotable; // code using them.
cl.ndx = ndx;
#endif
COUNT(2);
if (ndx>0xffu)
return;
for (j = LA_SLOTS-1; j>0; j--)
cache[i][j] = cache[i][j-1];
cache[i][0] = cl;
}
/*
* Find a string key entry in a rotable and return it. Note that this internally
* uses a null key to denote a metatable search.
*/
const TValue* luaR_findentry(ROTable *rotable, TString *key, unsigned *ppos) {
const luaR_entry *pentry = rotable;
const char *strkey = key ? getstr(key) : ALIGNED_STRING "__metatable" ;
unsigned hash = HASH(rotable, key);
unsigned i = 0;
int j = lookup_cache(hash, rotable);
unsigned l = key ? key->tsv.len : sizeof("__metatable")-1;
if (pentry) {
if (j >= 0 && !strcmp(pentry[j].key, strkey)) {
if (ppos)
*ppos = j;
//dbg_printf("%3d hit %p %s\n", (hash>>2) & (LA_LINES-1), rotable, strkey);
return &pentry[j].value;
}
/*
* The invariants for 1st word comparison are deferred to here since they
* aren't needed if there is a cache hit. Note that the termination null
* is included so a "on\0" has a mask of 0xFFFFFF and "a\0" has 0xFFFF.
*/
unsigned name4, mask4 = l > 2 ? (~0u) : (~0u)>>((3-l)*8);
memcpy(&name4, strkey, sizeof(name4));
for(;pentry->key != NULL; i++, pentry++) {
if (((*(unsigned *)pentry->key ^ name4) & mask4) == 0 &&
!strcmp(pentry->key, strkey)) {
//dbg_printf("%p %s hit after %d probes \n", rotable, strkey, (int)(rotable-pentry));
if (ppos)
*ppos = i;
update_cache(hash, rotable, pentry - rotable);
//dbg_printf("%3d %3d %p %s\n", (hash>>2) & (LA_LINES-1), (int)(pentry-rotable), rotable, strkey);
return &pentry->value;
}
}
}
//dbg_printf("%p %s miss after %d probes \n", rotable, strkey, (int)(rotable-pentry));
return luaO_nilobject;
}
/* Find the metatable of a given table */
void* luaR_getmeta(ROTable *rotable) {
const TValue *res = luaR_findentry(rotable, NULL, NULL);
return res && ttisrotable(res) ? rvalue(res) : NULL;
}
static void luaR_next_helper(lua_State *L, ROTable *pentries, int pos,
TValue *key, TValue *val) {
if (pentries[pos].key) {
/* Found an entry */
setsvalue(L, key, luaS_new(L, pentries[pos].key));
setobj2s(L, val, &pentries[pos].value);
} else {
setnilvalue(key);
setnilvalue(val);
}
}
/* next (used for iteration) */
void luaR_next(lua_State *L, ROTable *rotable, TValue *key, TValue *val) {
unsigned keypos;
/* Special case: if key is nil, return the first element of the rotable */
if (ttisnil(key))
luaR_next_helper(L, rotable, 0, key, val);
else if (ttisstring(key)) {
/* Find the previous key again */
if (ttisstring(key)) {
luaR_findentry(rotable, rawtsvalue(key), &keypos);
}
/* Advance to next key */
keypos ++;
luaR_next_helper(L, rotable, keypos, key, val);
}
}

View File

@ -1,101 +0,0 @@
/* Read-only tables for Lua */
#ifndef lrotable_h
#define lrotable_h
#include "lua.h"
#include "luaconf.h"
#include "lobject.h"
#include "llimits.h"
#include "lrotable.h"
/* Macros one can use to define rotable entries */
#define LRO_FUNCVAL(v) {{.p = v}, LUA_TLIGHTFUNCTION}
#define LRO_LUDATA(v) {{.p = v}, LUA_TLIGHTUSERDATA}
#define LRO_NUMVAL(v) {{.n = v}, LUA_TNUMBER}
#define LRO_ROVAL(v) {{.p = (void*)v}, LUA_TROTABLE}
#define LRO_NILVAL {{.p = NULL}, LUA_TNIL}
#ifdef LUA_CROSS_COMPILER
#define LRO_STRKEY(k) k
#else
#define LRO_STRKEY(k) ((STORE_ATTR char *) k)
#endif
#define LROT_TABLE(t) static const LUA_REG_TYPE t ## _map[];
#define LROT_PUBLIC_TABLE(t) const LUA_REG_TYPE t ## _map[];
#define LROT_TABLEREF(t) ((void *) t ## _map)
#define LROT_BEGIN(t) static const LUA_REG_TYPE t ## _map [] = {
#define LROT_PUBLIC_BEGIN(t) const LUA_REG_TYPE t ## _map[] = {
#define LROT_EXTERN(t) extern const LUA_REG_TYPE t ## _map[]
#define LROT_TABENTRY(n,t) {LRO_STRKEY(#n), LRO_ROVAL(t ## _map)},
#define LROT_FUNCENTRY(n,f) {LRO_STRKEY(#n), LRO_FUNCVAL(f)},
#define LROT_NUMENTRY(n,x) {LRO_STRKEY(#n), LRO_NUMVAL(x)},
#define LROT_LUDENTRY(n,x) {LRO_STRKEY(#n), LRO_LUDATA((void *) x)},
#define LROT_END(t,mt, f) {NULL, LRO_NILVAL} };
#define LROT_BREAK(t) };
#define LUA_REG_TYPE luaR_entry
#define LREGISTER(L, name, table) return 0
/* Maximum length of a rotable name and of a string key*/
#define LUA_MAX_ROTABLE_NAME 32
/* Type of a numeric key in a rotable */
typedef int luaR_numkey;
/* An entry in the read only table */
typedef struct luaR_entry {
const char *key;
const TValue value;
} luaR_entry;
/*
* The current ROTable implmentation is a vector of luaR_entry terminated by a
* nil record. The convention is to use ROtable * to refer to the entire vector
* as a logical ROTable.
*/
typedef const struct luaR_entry ROTable;
const TValue* luaR_findentry(ROTable *tab, TString *key, unsigned *ppos);
const TValue* luaR_findentryN(ROTable *tab, luaR_numkey numkey, unsigned *ppos);
void luaR_next(lua_State *L, ROTable *tab, TValue *key, TValue *val);
void* luaR_getmeta(ROTable *tab);
int luaR_isrotable(void *p);
/*
* Set inRO check depending on platform. Note that this implementation needs
* to work on both the host (luac.cross) and ESP targets. The luac.cross
* VM is used for the -e option, and is primarily used to be able to debug
* VM changes on the more developer-friendly hot gdb environment.
*/
#if defined(LUA_CROSS_COMPILER)
#if defined(_MSC_VER)
//msvc build uses these dummy vars to locate the beginning and ending addresses of the RO data
extern const char _ro_start[], _ro_end[];
#define IN_RODATA_AREA(p) (((const char*)(p)) >= _ro_start && ((const char *)(p)) <= _ro_end)
#else /* one of the POSIX variants */
#if defined(__CYGWIN__)
#define _RODATA_END __end__
#elif defined(__MINGW32__)
#define _RODATA_END end
#else
#define _RODATA_END _edata
#endif
extern const char _RODATA_END[];
#define IN_RODATA_AREA(p) (((const char *)(p)) < _RODATA_END)
#endif /* defined(_MSC_VER) */
#else /* xtensa tool chain for ESP target */
extern const char _irom0_text_start[];
extern const char _irom0_text_end[];
#define IN_RODATA_AREA(p) (((const char *)(p)) >= _irom0_text_start && ((const char *)(p)) <= _irom0_text_end)
#endif /* defined(LUA_CROSS_COMPILER) */
/* Return 1 if the given pointer is a rotable */
#define luaR_isrotable(p) IN_RODATA_AREA(p)
#endif

View File

@ -7,7 +7,6 @@
#define lstate_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
@ -230,9 +229,6 @@ lua_State *lua_open(void) {
return lua_crtstate;
}
lua_State *lua_getstate(void) {
return lua_crtstate;
}
LUA_API void lua_close (lua_State *L) {
#ifndef LUA_CROSS_COMPILER
lua_sethook( L, NULL, 0, 0 );

View File

@ -56,7 +56,7 @@ typedef struct CallInfo {
#define curr_func(L) (ttisfunction(L->ci->func) ? clvalue(L->ci->func) : NULL)
#define curr_func(L) (ttisclfunction(L->ci->func) ? clvalue(L->ci->func) : NULL)
#define ci_func(ci) (ttisfunction((ci)->func) ? clvalue((ci)->func) : NULL)
#define f_isLua(ci) (!ttislightfunction((ci)->func) && !ci_func(ci)->c.isC)
#define isLua(ci) (ttisfunction((ci)->func) && f_isLua(ci))
@ -160,7 +160,7 @@ union GCObject {
#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u))
#define gco2u(o) (&rawgco2u(o)->uv)
#define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl))
#define gco2h(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h))
#define gco2h(o) check_exp(((o)->gch.tt & LUA_TMASK) == LUA_TTABLE, &((o)->h))
#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p))
#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv))
#define ngcotouv(o) \

View File

@ -8,7 +8,6 @@
#define lstring_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
#include <string.h>
@ -18,8 +17,6 @@
#include "lstate.h"
#include "lstring.h"
#define LUAS_READONLY_STRING 1
#define LUAS_REGULAR_STRING 0
void luaS_resize (lua_State *L, int newsize) {
stringtable *tb;
@ -53,26 +50,20 @@ void luaS_resize (lua_State *L, int newsize) {
}
static TString *newlstr (lua_State *L, const char *str, size_t l,
unsigned int h, int readonly) {
unsigned int h) {
TString *ts;
stringtable *tb;
stringtable *tb = &G(L)->strt;
if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char))
luaM_toobig(L);
tb = &G(L)->strt;
if ((tb->nuse + 1) > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2)
luaS_resize(L, tb->size*2); /* too crowded */
ts = cast(TString *, luaM_malloc(L, sizeof(TString) + (readonly ? sizeof(char**) : (l+1)*sizeof(char))));
ts = cast(TString *, luaM_malloc(L, sizeof(TString) + (l+1)*sizeof(char)));
ts->tsv.len = l;
ts->tsv.hash = h;
ts->tsv.marked = luaC_white(G(L));
ts->tsv.tt = LUA_TSTRING;
if (!readonly) {
memcpy(ts+1, str, l*sizeof(char));
((char *)(ts+1))[l] = '\0'; /* ending 0 */
} else {
*(char **)(ts+1) = (char *)str;
l_setbit((ts)->tsv.marked, READONLYBIT);
}
memcpy(ts+1, str, l*sizeof(char));
((char *)(ts+1))[l] = '\0'; /* ending 0 */
h = lmod(h, tb->size);
ts->tsv.next = tb->hash[h]; /* chain new entry */
tb->hash[h] = obj2gco(ts);
@ -80,14 +71,6 @@ static TString *newlstr (lua_State *L, const char *str, size_t l,
return ts;
}
static int lua_is_ptr_in_ro_area(const char *p) {
#ifdef LUA_CROSS_COMPILER
return 0; // TStrings are never in RO in luac.cross
#else
return IN_RODATA_AREA(p);
#endif
}
/*
* The string algorithm has been modified to be LFS-friendly. The previous eLua
* algo used the address of the string was in flash and the string was >4 bytes
@ -128,11 +111,7 @@ LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l) {
}
}
#endif
/* New additions to the RAM strt are tagged as readonly if the string address
* is in the CTEXT segment (target only, not luac.cross) */
int readonly = (lua_is_ptr_in_ro_area(str) && l+1 > sizeof(char**) &&
l == strlen(str) ? LUAS_READONLY_STRING : LUAS_REGULAR_STRING);
return newlstr(L, str, l, h, readonly); /* not found */
return newlstr(L, str, l, h); /* not found */
}

View File

@ -13,8 +13,7 @@
#include "lstate.h"
#define sizestring(s) (sizeof(union TString)+(testbit(getmarked(s), READONLYBIT) ? sizeof(char **) : ((s)->len+1)*sizeof(char)))
#define sizestring(s) (sizeof(union TString)+((s)->len+1)*sizeof(char))
#define sizeudata(u) (sizeof(union Udata)+(u)->len)
#define luaS_new(L, s) (luaS_newlstr(L, s, strlen(s)))

View File

@ -7,7 +7,6 @@
#define lstrlib_c
#define LUA_LIB
#define LUAC_CROSS_FILE
#include "lua.h"
#include <stdio.h>
@ -15,7 +14,7 @@
#include "lauxlib.h"
#include "lualib.h"
#include "lrotable.h"
#include "lnodemcu.h"
/* macro to `unsign' a character */
#define uchar(c) ((unsigned char)(c))
@ -824,7 +823,21 @@ static int str_format (lua_State *L) {
return 1;
}
LROT_PUBLIC_BEGIN(strlib)
static int str_format2 (lua_State *L) {
if (lua_type(L, 2) == LUA_TTABLE) {
int i,n=lua_objlen(L,2);
lua_settop(L,2);
for (i = 1; i <= n; i++)
lua_rawgeti(L, 2, i);
lua_remove(L, 2);
}
return str_format(L);
}
LROT_BEGIN(strlib, NULL, LROT_MASK_INDEX)
LROT_TABENTRY( __index, strlib )
LROT_FUNCENTRY( __mod, str_format2 )
LROT_FUNCENTRY( byte, str_byte )
LROT_FUNCENTRY( char, str_char )
LROT_FUNCENTRY( dump, str_dump )
@ -844,8 +857,7 @@ LROT_PUBLIC_BEGIN(strlib)
LROT_FUNCENTRY( reverse, str_reverse )
LROT_FUNCENTRY( sub, str_sub )
LROT_FUNCENTRY( upper, str_upper )
LROT_TABENTRY( __index, strlib )
LROT_END(strlib, NULL, 0) // OR DO WE NEED LRTO_MASK_INDEX **TODO**
LROT_END(strlib, NULL, LROT_MASK_INDEX)
/*
** Open string library

View File

@ -20,7 +20,6 @@
#define ltable_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
#include <math.h>
@ -33,7 +32,8 @@
#include "lobject.h"
#include "lstate.h"
#include "ltable.h"
#include "lrotable.h"
#include "lstring.h"
/*
** max size of array part is 2^MAXBITS
@ -68,6 +68,10 @@
*/
#define numints cast_int(sizeof(lua_Number)/sizeof(int))
static const TValue* rotable_findentry(ROTable *rotable, TString *key, unsigned *ppos);
static void rotable_next_helper(lua_State *L, ROTable *pentries, int pos,
TValue *key, TValue *val);
static void rotable_next(lua_State *L, ROTable *rotable, TValue *key, TValue *val);
#define dummynode (&dummynode_)
@ -105,11 +109,11 @@ static Node *mainposition (const Table *t, const TValue *key) {
return hashstr(t, rawtsvalue(key));
case LUA_TBOOLEAN:
return hashboolean(t, bvalue(key));
case LUA_TROTABLE:
return hashpointer(t, rvalue(key));
case LUA_TLIGHTUSERDATA:
case LUA_TLIGHTFUNCTION:
return hashpointer(t, pvalue(key));
case LUA_TROTABLE:
return hashpointer(t, hvalue(key));
default:
return hashpointer(t, gcvalue(key));
}
@ -163,7 +167,12 @@ static int findindex (lua_State *L, Table *t, StkId key) {
int luaH_next (lua_State *L, Table *t, StkId key) {
int i = findindex(L, t, key); /* find original element */
int i;
if (isrotable(t)) {
rotable_next(L, (ROTable *) t, key, key+1);
return ttisnil(key) ? 0 : 1;
}
i = findindex(L, t, key); /* find original element */
for (i++; i < t->sizearray; i++) { /* try first array part */
if (!ttisnil(&t->array[i])) { /* a non-nil value? */
setnvalue(key, cast_num(i+1));
@ -182,12 +191,6 @@ int luaH_next (lua_State *L, Table *t, StkId key) {
}
int luaH_next_ro (lua_State *L, void *t, StkId key) {
luaR_next(L, t, key, key+1);
return ttisnil(key) ? 0 : 1;
}
/*
** {=============================================================
** Rehash
@ -563,6 +566,8 @@ static TValue *newkey (lua_State *L, Table *t, const TValue *key) {
** search function for integers
*/
const TValue *luaH_getnum (Table *t, int key) {
if (isrotable(t))
return luaO_nilobject;
/* (1 <= key && key <= t->sizearray) */
if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray))
return &t->array[key-1];
@ -578,17 +583,16 @@ const TValue *luaH_getnum (Table *t, int key) {
}
}
/* same thing for rotables */
const TValue *luaH_getnum_ro (void *t, int key) {
const TValue *res = NULL; // integer values not supported: luaR_findentryN(t, key, NULL);
return res ? res : luaO_nilobject;
}
/*
** search function for strings
*/
const TValue *luaH_getstr (Table *t, TString *key) {
if (isrotable(t)) {
if (key->tsv.len>LUA_MAX_ROTABLE_NAME)
return luaO_nilobject;
return rotable_findentry((ROTable*) t, key, NULL);
}
Node *n = hashstr(t, key);
do { /* check whether `key' is somewhere in the chain */
if (ttisstring(gkey(n)) && rawtsvalue(gkey(n)) == key)
@ -598,63 +602,41 @@ const TValue *luaH_getstr (Table *t, TString *key) {
return luaO_nilobject;
}
/* same thing for rotables */
const TValue *luaH_getstr_ro (void *t, TString *key) {
if (!t || key->tsv.len>LUA_MAX_ROTABLE_NAME)
return luaO_nilobject;
return luaR_findentry(t, key, NULL);
}
/*
** main search function
*/
const TValue *luaH_get (Table *t, const TValue *key) {
switch (ttype(key)) {
case LUA_TNIL: return luaO_nilobject;
case LUA_TSTRING: return luaH_getstr(t, rawtsvalue(key));
case LUA_TNUMBER: {
int k;
lua_Number n = nvalue(key);
lua_number2int(k, n);
if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */
return luaH_getnum(t, k); /* use specialized version */
/* else go through */
}
default: {
Node *n = mainposition(t, key);
do { /* check whether `key' is somewhere in the chain */
if (luaO_rawequalObj(key2tval(n), key))
return gval(n); /* that's it */
else n = gnext(n);
} while (n);
return luaO_nilobject;
}
}
}
/* same thing for rotables */
const TValue *luaH_get_ro (void *t, const TValue *key) {
switch (ttype(key)) {
case LUA_TNIL: return luaO_nilobject;
case LUA_TSTRING: return luaH_getstr_ro(t, rawtsvalue(key));
case LUA_TNUMBER: {
int k;
lua_Number n = nvalue(key);
lua_number2int(k, n);
if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */
return luaH_getnum_ro(t, k); /* use specialized version */
/* else go through */
}
default: {
return luaO_nilobject;
}
int type = ttype(key);
if (type == LUA_TNIL)
return luaO_nilobject;
if (type == LUA_TSTRING)
return luaH_getstr(t, rawtsvalue(key));
if (isrotable(t))
return luaO_nilobject;
if (type == LUA_TNUMBER) {
int k;
lua_Number n = nvalue(key);
lua_number2int(k, n);
if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */
return luaH_getnum(t, k); /* use specialized version */
}
/* default */
Node *n = mainposition(t, key);
do { /* check whether `key' is somewhere in the chain */
if (luaO_rawequalObj(key2tval(n), key))
return gval(n); /* that's it */
else n = gnext(n);
} while (n);
return luaO_nilobject;
}
TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
const TValue *p = luaH_get(t, key);
const TValue *p;
if (isrotable(t))
luaG_runerror(L, "table is readonly");
p = luaH_get(t, key);
t->flags = 0;
if (p != luaO_nilobject)
return cast(TValue *, p);
@ -668,7 +650,10 @@ TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
TValue *luaH_setnum (lua_State *L, Table *t, int key) {
const TValue *p = luaH_getnum(t, key);
const TValue *p;
if (isrotable(t))
luaG_runerror(L, "table is readonly");
p = luaH_getnum(t, key);
if (p != luaO_nilobject)
return cast(TValue *, p);
else {
@ -680,7 +665,10 @@ TValue *luaH_setnum (lua_State *L, Table *t, int key) {
TValue *luaH_setstr (lua_State *L, Table *t, TString *key) {
const TValue *p = luaH_getstr(t, key);
const TValue *p;
if (isrotable(t))
luaG_runerror(L, "table is readonly");
p = luaH_getstr(t, key);
if (p != luaO_nilobject)
return cast(TValue *, p);
else {
@ -720,7 +708,10 @@ static int unbound_search (Table *t, unsigned int j) {
** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
*/
int luaH_getn (Table *t) {
unsigned int j = t->sizearray;
unsigned int j;
if(isrotable(t))
return 0;
j = t->sizearray;
if (j > 0 && ttisnil(&t->array[j - 1])) {
/* there is a boundary in the array part: (binary) search for it */
unsigned int i = 0;
@ -737,13 +728,138 @@ int luaH_getn (Table *t) {
else return unbound_search(t, j);
}
/* same thing for rotables */
int luaH_getn_ro (void *t) {
return 0; // Integer Keys are not currently supported for ROTables
}
int luaH_isdummy (Node *n) { return n == dummynode; }
/*
** All keyed ROTable access passes through rotable_findentry(). ROTables
** are simply a list of <key><TValue value> pairs.
**
** The global KeyCache is used to avoid a relatively expensive Flash memory
** vector scan. A simple hash on the key's TString addr and the ROTable
** addr selects the cache line. The line's slots are then scanned for a
** hit.
**
** Unlike the standard hast which uses a prime line count therefore requires
** the use of modulus operation which is expensive on an IoT processor
** without H/W divide. This hash is power of 2 based which might not be
** quite so uniform but can be calcuated without using H/W-based instructions.
**
** If a match is found and the table addresses match, then this entry is
** probed first. In practice the hit-rate here is over 99% so the code
** rarely fails back to doing the linear scan in ROM.
** Note that this hash does a couple of prime multiples and a modulus 2^X
** with is all evaluated in H/W, and adequately randomizes the lookup.
*/
#define LA_LINES 32
#define LA_SLOTS 4
static size_t cache [LA_LINES][LA_SLOTS];
#define HASH(a,b) ((((29*(size_t)(a)) ^ (37*((b)->tsv.hash)))>>4) % LA_LINES)
#define NDX_SHFT 24
#define ADDR_MASK (((size_t) 1<<24)-1)
/*
* Find a string key entry in a rotable and return it. Note that this internally
* uses a null key to denote a metatable search.
*/
static const TValue* rotable_findentry(ROTable *t, TString *key, unsigned *ppos) {
const ROTable_entry *e = cast(const ROTable_entry *, t->entry);
const int tl = getlsizenode(t);
const char *strkey = getstr(key);
size_t *cl = cache[HASH(t, key)];
int i, j = 1, l;
if (!e || gettt(key) != LUA_TSTRING)
return luaO_nilobject;
l = key->tsv.len;
/* scan the ROTable lookaside cache and return if hit found */
for (i=0; i<LA_SLOTS; i++) {
int cl_ndx = cl[i] >> NDX_SHFT;
if ((((size_t)t - cl[i]) & ADDR_MASK) == 0 && cl_ndx < tl &&
strcmp(e[cl_ndx].key, strkey) == 0) {
if (ppos)
*ppos = cl_ndx;
return &e[cl_ndx].value;
}
}
/*
* A lot of search misses are metavalues, but tables typically only have at
* most a couple of them, so these are always put at the front of the table
* in ascending order and the metavalue scan short circuits using a straight
* strcmp()
*/
lu_int32 name4 = *(lu_int32 *) strkey;
if (*(char*)&name4 == '_') {
for(i = 0; i < tl; i++) {
j = strcmp(e[i].key, strkey);
if (j>=0)
break;
}
} else {
/*
* Ordinary (non-meta) keys can be unsorted. This is for legacy compatiblity,
* plus misses are pretty rare in this case. The masked name4 comparison is
* safe 4-byte comparison that nearly always avoids the more costly strcmp()
* for an actual hit validation.
*/
lu_int32 mask4 = l > 2 ? (~0u) : (~0u)>>((3-l)*8);
for(i = 0; i < tl; i++) {
if (((*(lu_int32 *)e[i].key ^ name4) & mask4) != 0)
continue;
j = strcmp(e[i].key, strkey);
if (j==0)
break;
}
}
if (j)
return luaO_nilobject;
if (ppos)
*ppos = i;
/* In the case of a hit, update the lookaside cache */
for (j = LA_SLOTS-1; j>0; j--)
cl[j] = cl[j-1];
cl[0] = ((size_t)t & ADDR_MASK) + (i << NDX_SHFT);
return &e[i].value;
}
static void rotable_next_helper(lua_State *L, ROTable *t, int pos,
TValue *key, TValue *val) {
const ROTable_entry *e = cast(const ROTable_entry *, t->entry);
if (pos < getlsizenode(t)) {
/* Found an entry */
setsvalue(L, key, luaS_new(L, e[pos].key));
setobj2s(L, val, &e[pos].value);
} else {
setnilvalue(key);
setnilvalue(val);
}
}
/* next (used for iteration) */
static void rotable_next(lua_State *L, ROTable *t, TValue *key, TValue *val) {
unsigned keypos = getlsizenode(t);
/* Special case: if key is nil, return the first element of the rotable */
if (ttisnil(key))
rotable_next_helper(L, t, 0, key, val);
else if (ttisstring(key)) {
/* Find the previous key again */
if (ttisstring(key)) {
rotable_findentry(t, rawtsvalue(key), &keypos);
}
/* Advance to next key */
rotable_next_helper(L, t, ++keypos, key, val);
}
}
#if defined(LUA_DEBUG)
Node *luaH_mainposition (const Table *t, const TValue *key) {
return mainposition(t, key);

View File

@ -16,7 +16,8 @@
#define gnext(n) ((n)->i_key.nk.next)
#define key2tval(n) (&(n)->i_key.tvk)
#define isrotable(t) (gettt(t)==LUA_TROTABLE)
#define isrwtable(t) (gettt(t)==LUA_TTABLE)
LUAI_FUNC const TValue *luaH_getnum (Table *t, int key);
LUAI_FUNC const TValue *luaH_getnum_ro (void *t, int key);
@ -36,6 +37,8 @@ LUAI_FUNC int luaH_getn (Table *t);
LUAI_FUNC int luaH_getn_ro (void *t);
LUAI_FUNC int luaH_isdummy (Node *n);
#define LUA_MAX_ROTABLE_NAME 32
#if defined(LUA_DEBUG)
LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key);
#endif

View File

@ -7,13 +7,12 @@
#define ltablib_c
#define LUA_LIB
#define LUAC_CROSS_FILE
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "lrotable.h"
#include "lnodemcu.h"
#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n))
@ -22,7 +21,7 @@
static int foreachi (lua_State *L) {
int i;
int n = aux_getn(L, 1);
luaL_checkanyfunction(L, 2);
luaL_checkfunction(L, 2);
for (i=1; i <= n; i++) {
lua_pushvalue(L, 2); /* function */
lua_pushinteger(L, i); /* 1st argument */
@ -38,7 +37,7 @@ static int foreachi (lua_State *L) {
static int foreach (lua_State *L) {
luaL_checktype(L, 1, LUA_TTABLE);
luaL_checkanyfunction(L, 2);
luaL_checkfunction(L, 2);
lua_pushnil(L); /* first key */
while (lua_next(L, 1)) {
lua_pushvalue(L, 2); /* function */
@ -266,7 +265,7 @@ static int sort (lua_State *L) {
/* }====================================================== */
LROT_PUBLIC_BEGIN(tab_funcs)
LROT_BEGIN(tab_funcs, NULL, 0)
LROT_FUNCENTRY( concat, tconcat )
LROT_FUNCENTRY( foreach, foreach )
LROT_FUNCENTRY( foreachi, foreachi )

View File

@ -5,37 +5,35 @@
*/
#include <string.h>
#define ltm_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
#include <string.h>
#include "lobject.h"
#include "lstate.h"
#include "lgc.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#include "lrotable.h"
/* These must be correspond to the LUA_T* defines in lua.h */
const char *const luaT_typenames[] = {
"nil", "boolean", "romtable", "lightfunction", "userdata", "number",
"string", "table", "function", "userdata", "thread",
"nil", "boolean", "lightfunction","number", // base type = 0, 1, 2, 3
"string", "table", "function", "userdata", "thread", // base type = 4, 5, 6, 7, 8
"proto", "upval"
};
void luaT_init (lua_State *L) {
static const char *const luaT_eventname[] = { /* ORDER TM */
"__index", "__newindex",
"__gc", "__mode", "__eq",
"__add", "__sub", "__mul", "__div", "__mod",
"__pow", "__unm", "__len", "__lt", "__le",
"__concat", "__call"
"__concat", "__call",
"__metatable"
};
int i;
for (i=0; i<TM_N; i++) {
@ -50,22 +48,14 @@ void luaT_init (lua_State *L) {
** tag methods
*/
const TValue *luaT_gettm (Table *events, TMS event, TString *ename) {
const TValue *tm;
const TValue *tm = luaH_getstr(events, ename);
lua_assert(event <= TM_EQ);
if (luaR_isrotable(events)) {
tm = luaH_getstr_ro(events, ename);
if (ttisnil(tm)) { /* no tag method? */
if (isrwtable(events)) /* if this a (RW) Table */
events->flags |= cast_byte(1u<<event); /* then cache this fact */
return NULL;
}
} else {
tm = luaH_getstr(events, ename);
if (ttisnil(tm)) { /* no tag method? */
events->flags |= cast_byte(1u<<event); /* cache this fact */
return NULL;
}
}
return tm;
else return tm;
}
@ -73,21 +63,14 @@ const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, TMS event) {
Table *mt;
switch (ttype(o)) {
case LUA_TTABLE:
mt = hvalue(o)->metatable;
break;
case LUA_TROTABLE:
mt = (Table*)luaR_getmeta(rvalue(o));
mt = hvalue(o)->metatable;
break;
case LUA_TUSERDATA:
mt = uvalue(o)->metatable;
break;
default:
mt = G(L)->mt[ttype(o)];
mt = G(L)->mt[basettype(o)];
}
if (!mt)
return luaO_nilobject;
else if (luaR_isrotable(mt))
return luaH_getstr_ro(mt, G(L)->tmname[event]);
else
return luaH_getstr(mt, G(L)->tmname[event]);
return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject);
}

View File

@ -7,10 +7,6 @@
#ifndef ltm_h
#define ltm_h
#include "lobject.h"
#include "lrotable.h"
/*
* WARNING: if you change the order of this enumeration,
* grep "ORDER TM"
@ -33,13 +29,16 @@ typedef enum {
TM_LE,
TM_CONCAT,
TM_CALL,
TM_METATABLE,
TM_N /* number of elements in the enum */
} TMS;
#define gfasttm(g,et,e) ((et) == NULL ? NULL : \
(!luaR_isrotable(et) && ((et)->flags & (1u<<(e)))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))
//#include "lobject.h"
#define fasttm(l,et,e) gfasttm(G(l), et, e)
#define gfasttm(g,et,e) ((et) == NULL ? NULL : \
(getflags(et) & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))
#define fasttm(l,et,e) gfasttm(G(l), et, e)
LUAI_DATA const char *const luaT_typenames[];

View File

@ -1,303 +1,12 @@
/*
** NodeMCU Lua 5.1 main initiator and comand interpreter
** See Copyright Notice in lua.h
**
** Note this is largely a backport of some new Lua 5.3 version but
** with API changes for Lua 5.1 compatability
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "user_version.h"
#include "driver/input.h"
#define lua_c
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
#include "legc.h"
#include "llimits.h"
#define LUA_VERSION_51
#include "os_type.h"
#include "../lua53/lua.c"
extern int debug_errorfb (lua_State *L);
extern int pipe_create(lua_State *L);
extern int pipe_read(lua_State *L);
extern int pipe_unread(lua_State *L);
#ifndef LUA_INIT_STRING
#define LUA_INIT_STRING "@init.lua"
#endif
static int MLref = LUA_NOREF;
lua_State *globalL = NULL;
static int pmain (lua_State *L);
static int dojob (lua_State *L);
static void l_message (const char *msg) {
luai_writestringerror("%s\n", msg);
}
static int report (lua_State *L, int status) {
if (status && !lua_isnil(L, -1)) {
const char *msg = lua_tostring(L, -1);
if (msg == NULL) msg = "(error object is not a string)";
l_message(msg);
lua_pop(L, 1);
}
return status;
}
static void l_print(lua_State *L, int n) {
lua_getglobal(L, "print");
lua_insert(L, -n-1);
if (lua_pcall(L, n, 0, 0) != 0)
l_message(lua_pushfstring(L, "error calling " LUA_QL("print") " (%s)",
lua_tostring(L, -1)));
}
static int traceback (lua_State *L) {
if (lua_isstring(L, 1)) {
lua_pushlightfunction(L, &debug_errorfb);
lua_pushvalue(L, 1); /* pass error message */
lua_pushinteger(L, 2); /* skip this function and traceback */
lua_call(L, 2, 1); /* call debug.traceback */
}
lua_settop(L, 1);
return 1;
}
static int docall (lua_State *L, int narg, int clear) {
int status;
int base = lua_gettop(L) - narg; /* function index */
lua_pushlightfunction(L, &traceback); /* push traceback function */
lua_insert(L, base); /* put it under chunk and args */
status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
lua_remove(L, base); /* remove traceback function */
/* force a complete garbage collection in case of errors */
if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
return status;
}
static void print_version (lua_State *L) {
lua_pushliteral (L, "\n" NODE_VERSION " build " BUILD_DATE
" powered by " LUA_RELEASE " on SDK ");
lua_pushstring (L, SDK_VERSION);
lua_concat (L, 2);
const char *msg = lua_tostring (L, -1);
l_message (msg);
lua_pop (L, 1);
}
static int dofile (lua_State *L, const char *name) {
int status = luaL_loadfile(L, name) || docall(L, 0, 1);
return report(L, status);
}
static int dostring (lua_State *L, const char *s, const char *name) {
int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1);
return report(L, status);
}
static const char *get_prompt (lua_State *L, int firstline) {
const char *p;
lua_getglobal(L, firstline ? "_PROMPT" : "_PROMPT2");
p = lua_tostring(L, -1);
if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2);
lua_pop(L, 1); /* remove global */
return p;
}
#define EOFMARK LUA_QL("<eof>")
static int incomplete (lua_State *L, int status) {
if (status == LUA_ERRSYNTAX) {
size_t lmsg;
const char *msg = lua_tolstring(L, -1, &lmsg);
if (!strcmp(msg+lmsg-sizeof(EOFMARK)+1, EOFMARK)) {
lua_pop(L, 1);
return 1;
}
}
return 0;
}
/*
** dojob is the CB reader for the input pipe and follows the calling convention
** for pipe reader CBs. It has one argument: the stdin pipe that it is reading.
*/
static int dojob (lua_State *L) {
size_t l;
int status;
const char *prompt;
//dbg_printf("dojob entered\n");
lua_settop(L, 1); /* pipe obj at S[1] */
lua_pushlightfunction(L, &pipe_read); /* pobj:read at S[2] */
lua_pushvalue(L, 1); /* dup pobj to S[3] */
lua_pushliteral(L, "\n+"); /* S[4] = "\n+" */
lua_call(L, 2, 1); /* S[2] = pobj:read("\n+") */
const char* b = lua_tolstring(L, 2, &l); /* b = NULL if S[2] is nil */
if ((lua_isnil(L, 2) || l == 0)) {
/* If the pipe is empty then return false to suppress automatic reposting */
//dbg_printf("stdin empty\n");
lua_pushboolean(L, false);
return 1; /* return false */
}
if (b[l-1] != '\n') {
//dbg_printf("unreading part line\n");
/* likewise if not CR terminated, then unread and ditto */
lua_pushlightfunction(L, &pipe_unread); /* pobj:read at S[1] */
lua_insert(L, 1);
lua_call(L, 2, 0); /* pobj:unread(line) */
lua_pushboolean(L, false);
return 1; /* return false */
} else {
//dbg_printf("popping CR terminated string(%d) %s", l-1, b);
}
/*
* Now we can process a proper CR terminated line
*/
lua_pushlstring(L, b, --l); /* remove end CR */
lua_remove(L, 2);
b = lua_tostring(L, 2);
if (MLref != LUA_NOREF) {
/* processing multiline */
lua_rawgeti(L, LUA_REGISTRYINDEX, MLref); /* insert prev lines(s) */
lua_pushliteral(L, "\n"); /* insert CR */
lua_pushvalue(L, 2); /* dup new line */
lua_concat(L, 3); /* concat all 3 */
lua_remove(L, 2); /* and shift down to S[2] */
} else if (b[0] == '=') {
/* If firstline and of the format =<expression> */
lua_pushfstring(L, "return %s", b+1);
lua_remove(L, 2);
}
/* ToS is at S[2] which contains the putative chunk to be compiled */
status = luaL_loadbuffer(L, lua_tostring(L, 2), lua_strlen(L, 2), "=stdin");
if (incomplete(L, status)) {
/* Store line back in the Reg mlref sot */
if (MLref == LUA_NOREF) {
MLref = luaL_ref(L, LUA_REGISTRYINDEX);
} else {
lua_rawseti(L, LUA_REGISTRYINDEX, MLref);
}
} else {
/* compile finished OK or with hard error */
lua_remove(L, 2); /* remove line because now redundant */
if (MLref!= LUA_NOREF) { /* also remove multiline if it exists */
luaL_unref(L, LUA_REGISTRYINDEX, MLref);
MLref= LUA_NOREF;
}
/* Execute the compiled chunk of successful */
if (status == 0) {
status = docall(L, 0, 0);
}
/* print any returned results or error message */
if (status && !lua_isnil(L, -1))
l_print(L, 1);
if (status == 0 && lua_gettop(L) - 1)
l_print(L, lua_gettop(L) - 1);
lua_settop(L, 2);
if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0);
}
prompt = get_prompt(L, MLref!= LUA_NOREF ? 0 : 1);
input_setprompt(prompt);
puts(prompt);
lua_pushnil(L);
return 1; /* return nil will retask if pipe not empty */
}
/*
* Kick off library and UART input handling before opening the module
* libraries. Note that as this is all done within a Lua task, so error
* handling is left to the Lua task traceback mechanism.
*/
extern void luaL_dbgbreak(void);
static int pmain (lua_State *L) {
const char *init = LUA_INIT_STRING;
globalL = L;
lua_gc(L, LUA_GCSTOP, 0); /* stop GC during initialization */
luaL_openlibs(L); /* open libraries */
lua_gc(L, LUA_GCRESTART, 0); /* restart GC and set EGC mode */
legc_set_mode( L, EGC_ALWAYS, 4096 );
lua_settop(L, 0);
lua_pushliteral(L, "stdin");
lua_pushlightfunction(L, &pipe_create);
lua_pushlightfunction(L, &dojob);
lua_pushinteger(L, LUA_TASK_LOW);
lua_call(L, 2, 1); /* ToS = pipe.create(dojob, low_priority) */
lua_rawset(L, LUA_REGISTRYINDEX); /* and stash input pipe in Reg["stdin"] */
input_setup(LUA_MAXINPUT, get_prompt(L, 1));
lua_input_string(" \n", 2); /* queue CR to issue first prompt */
#if !defined(DISABLE_STARTUP_BANNER)
print_version(L);
#endif
/* and last of all, kick off application initialisation */
if (init[0] == '@')
dofile(L, init+1);
else
dostring(L, init, LUA_INIT);
return 0;
}
/*
** The system initialisation CB nodemcu_init() calls lua_main() to startup
** the Lua environment by calling lua_open() which initiates the core Lua VM.
** The initialisation of the libraries, etc. is carried out by pmain in a
** separate Lua task, which also kicks off the user application through the
** LUA_INIT_STRING hook.
*/
void lua_main (void) {
lua_State *L = lua_open(); /* create state */
if (L == NULL) {
l_message("cannot create state: not enough memory");
return;
}
lua_pushlightfunction(L, &pmain); /* Call 'pmain' as a high priority task */
luaN_posttask(L, LUA_TASK_HIGH);
}
/*
** The Lua interpreter is event-driven and task-oriented in NodeMCU rather than
** based on a readline poll loop as in the standard implementation. Input lines
** can come from one of two sources: the application can "push" lines for the
** interpreter to compile and execute, or they can come from the UART. To
** minimise application blocking, the lines are queued in a pipe when received,
** with the Lua interpreter task attached to the pipe as its reader task. This
** CB processes one line of input per task execution.
**
** Even though lines can be emitted from independent sources (the UART and the
** node API), and they could in theory get interleaved, the strategy here is
** "let the programmer beware": interactive input will normally only occur in
** development and injected input occur in telnet type applications. If there
** is a need for interlocks, then the application should handle this.
*/
//static int n = 0;
void lua_input_string (const char *line, int len) {
lua_State *L = globalL;
lua_getfield(L, LUA_REGISTRYINDEX, "stdin");
lua_rawgeti(L, -1, 1); /* get the pipe_write from stdin[1] */
lua_insert(L, -2); /* stick above the pipe */
lua_pushlstring(L, line, len);
//const char*b = lua_tostring(L, -1);
//dbg_printf("Pushing (%u): %s", len, b);
lua_call(L, 2, 0); /* stdin:write(line) */
}

View File

@ -8,9 +8,6 @@
#ifndef lua_h
#define lua_h
#ifdef LUAC_CROSS_FILE
#include "luac_cross.h"
#endif
#include <stdint.h>
#include "stdarg.h"
#include "stddef.h"
@ -42,7 +39,8 @@
#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i))
/* thread status; 0 is OK */
/* thread status */
#define LUA_OK 0
#define LUA_YIELD 1
#define LUA_ERRRUN 2
#define LUA_ERRSYNTAX 3
@ -73,18 +71,22 @@ typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);
** basic types
*/
#define LUA_TNONE (-1)
#define LUA_TNIL 0
#define LUA_TBOOLEAN 1
#define LUA_TROTABLE 2
#define LUA_TLIGHTFUNCTION 3
#define LUA_TLIGHTUSERDATA 4
#define LUA_TNUMBER 5
#define LUA_TSTRING 6
#define LUA_TTABLE 7
#define LUA_TFUNCTION 8
#define LUA_TUSERDATA 9
#define LUA_TTHREAD 10
#define LUA_TLIGHTUSERDATA 2
#define LUA_TNUMBER 3
#define LUA_TSTRING 4
#define LUA_TTABLE 5
#define LUA_TFUNCTION 6
#define LUA_TUSERDATA 7
#define LUA_TTHREAD 8
#define LUA_TISROTABLE (1<<4)
#define LUA_TISLIGHTFUNC (1<<5)
#define LUA_TMASK 15
#define LUA_TROTABLE (LUA_TTABLE + LUA_TISROTABLE)
#define LUA_TLIGHTFUNCTION (LUA_TFUNCTION + LUA_TISLIGHTFUNC)
/* minimum Lua stack available to a C function */
#define LUA_MINSTACK 20
@ -143,6 +145,7 @@ LUA_API int (lua_isstring) (lua_State *L, int idx);
LUA_API int (lua_iscfunction) (lua_State *L, int idx);
LUA_API int (lua_isuserdata) (lua_State *L, int idx);
LUA_API int (lua_type) (lua_State *L, int idx);
LUA_API int (lua_fulltype) (lua_State *L, int idx);
LUA_API const char *(lua_typename) (lua_State *L, int tp);
LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2);
@ -174,8 +177,6 @@ LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
LUA_API void (lua_pushboolean) (lua_State *L, int b);
LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
LUA_API void (lua_pushlightfunction) (lua_State *L, void *p);
LUA_API void (lua_pushrotable) (lua_State *L, void *p);
LUA_API int (lua_pushthread) (lua_State *L);
@ -212,7 +213,7 @@ LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud);
LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt,
const char *chunkname);
LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data);
LUA_API int (lua_dumpEx) (lua_State *L, lua_Writer writer, void *data, int stripping);
/*
@ -272,11 +273,9 @@ LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);
#define lua_strlen(L,i) lua_objlen(L, (i))
#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION)
#define lua_islightfunction(L,n) (lua_type(L, (n)) == LUA_TLIGHTFUNCTION)
#define lua_isanyfunction(L,n) (lua_isfunction(L,n) || lua_islightfunction(L,n))
#define lua_islightfunction(L,n) (lua_fulltype(L, (n)) == LUA_TLIGHTFUNCTION)
#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE)
#define lua_isrotable(L,n) (lua_type(L, (n)) == LUA_TROTABLE)
#define lua_isanytable(L,n) (lua_istable(L,n) || lua_isrotable(L,n))
#define lua_isrotable(L,n) (lua_fulltype(L, (n)) == LUA_TROTABLE)
#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA)
#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL)
#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN)
@ -292,14 +291,20 @@ LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud);
#define lua_tostring(L,i) lua_tolstring(L, (i), NULL)
#define lua_dump(L,w,d) lua_dumpEx(L,w,d,0)
/* error codes from cross-compiler returned by lua_dumpEx */
/* target integer is too small to hold a value */
#define LUA_ERR_CC_INTOVERFLOW 101
/* target lua_Number is integral but a constant is non-integer */
#define LUA_ERR_CC_NOTINTEGER 102
/*
** compatibility macros and functions
*/
// BogdanM: modified for eLua interrupt support
//#define lua_open() luaL_newstate()
lua_State* lua_open(void);
lua_State* lua_getstate(void);
@ -377,20 +382,46 @@ struct lua_Debug {
/* }====================================================================== */
typedef struct ROTable ROTable;
typedef struct ROTable_entry ROTable_entry;
LUA_API void (lua_pushrotable) (lua_State *L, const ROTable *p);
LUA_API void (lua_createrotable) (lua_State *L, ROTable *t, const ROTable_entry *e, ROTable *mt);
#define EGC_NOT_ACTIVE 0 // EGC disabled
#define EGC_ON_ALLOC_FAILURE 1 // run EGC on allocation failure
#define EGC_ON_MEM_LIMIT 2 // run EGC when an upper memory limit is hit
#define EGC_ALWAYS 4 // always run EGC before an allocation
#ifdef LUA_USE_ESP
#ifndef LUA_CROSS_COMPILER
#define LUA_QUEUE_APP 0
#define LUA_QUEUE_UART 1
#define LUA_TASK_LOW 0
#define LUA_TASK_MEDIUM 1
#define LUA_TASK_HIGH 2
void lua_main (void);
void lua_input_string (const char *line, int len);
int luaN_posttask (lua_State* L, int prio);
int luaN_call (lua_State *L, int narg, int res, int dogc);
/**DEBUG**/extern void dbg_printf(const char *fmt, ...) __attribute__ ((format (printf, 1, 2)));
/**DEBUG**/extern void dbg_printf(const char *fmt, ...)
__attribute__ ((format (printf, 1, 2)));
#define luaN_freearray(L,b,l) luaM_freearray(L,b,l,sizeof(*b));
LUA_API void lua_setegcmode(lua_State *L, int mode, int limit);
#else
#define ICACHE_RODATA_ATTR
#define dbg_printf printf
#endif
extern void lua_debugbreak(void);
// EGC operations modes
#define EGC_NOT_ACTIVE 0 // EGC disabled
#define EGC_ON_ALLOC_FAILURE 1 // run EGC on allocation failure
#define EGC_ON_MEM_LIMIT 2 // run EGC when an upper memory limit is hit
#define EGC_ALWAYS 4 // always run EGC before an allocation
void legc_set_mode(lua_State *L, int mode, int limit);
/******************************************************************************
* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved.

View File

@ -1,19 +0,0 @@
/*
** Header to allow luac.cross compile within NodeMCU
** See Copyright Notice in lua.h
*/
#ifndef luac_cross_h
#define luac_cross_h
#ifdef LUA_CROSS_COMPILER
#define ICACHE_RODATA_ATTR
#define c_stderr stderr
#define c_stdin stdin
#define c_stdout stdout
#define dbg_printf printf
#else
#endif /* LUA_CROSS_COMPILER */
#endif /* luac_cross_h */

View File

@ -8,30 +8,41 @@
summary ?= @true
CCFLAGS:= -I.. -I../../include -I../../uzlib
CCFLAGS:= -I. -I.. -I../../include -I../../uzlib
LDFLAGS:= -L$(SDK_DIR)/lib -L$(SDK_DIR)/ld -lm -ldl -Wl,-Map=mapfile
CCFLAGS += -Wall
DEFINES += -DLUA_CROSS_COMPILER
TARGET = host
ifeq ($(FLAVOR),debug)
CCFLAGS += -O0 -g
TARGET_LDFLAGS += -O0 -g
DEFINES += -DLUA_DEBUG_BUILD
VERBOSE ?=
V ?= $(VERBOSE)
ifeq ("$(V)","1")
export summary := @true
else
export summary := @echo
# disable echoing of commands, directory names
MAKEFLAGS += --silent -w
endif # $(V)==1
DEBUG ?=
ifeq ("$(DEBUG)","1")
FLAVOR = debug
CCFLAGS += -O0 -ggdb
TARGET_LDFLAGS += -O0 -ggdb
DEFINES += -DLUA_CROSS_COMPILER -DLUA_DEBUG_BUILD -DDEVELOPMENT_TOOLS -DDEVELOPMENT_USE_GDB
else
FLAVOR = release
CCFLAGS += -O2
TARGET_LDFLAGS += -O2
endif
DEFINES += -DLUA_CROSS_COMPILER
endif # DEBUG
LUACSRC := luac.c lflashimg.c liolib.c loslib.c print.c
LUASRC := lapi.c lauxlib.c lbaselib.c lcode.c ldblib.c ldebug.c \
ldo.c ldump.c lfunc.c lgc.c linit.c llex.c \
lmathlib.c lmem.c loadlib.c lobject.c lopcodes.c lparser.c \
lrotable.c lstate.c lstring.c lstrlib.c ltable.c ltablib.c \
lstate.c lstring.c lstrlib.c ltable.c ltablib.c \
ltm.c lundump.c lvm.c lzio.c
UZSRC := uzlib_deflate.c crc32.c

View File

@ -6,7 +6,6 @@
#define LUAC_CROSS_FILE
#include "luac_cross.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>

View File

@ -17,7 +17,7 @@
#include "lauxlib.h"
#include "lualib.h"
#include "lrotable.h"
#include "lnodemcu.h"
#define IO_INPUT 1
#define IO_OUTPUT 2
@ -439,7 +439,10 @@ static int f_flush (lua_State *L) {
return pushresult(L, fflush(tofile(L)) == 0, NULL);
}
LROT_PUBLIC_BEGIN(iolib)
LROT_TABLE(iolib);
LROT_BEGIN(iolib, NULL, LROT_MASK_GC_INDEX)
LROT_TABENTRY( __index, iolib )
LROT_FUNCENTRY( close, io_close )
LROT_FUNCENTRY( flush, io_flush )
LROT_FUNCENTRY( input, io_input )
@ -449,10 +452,14 @@ LROT_PUBLIC_BEGIN(iolib)
LROT_FUNCENTRY( read, io_read )
LROT_FUNCENTRY( type, io_type )
LROT_FUNCENTRY( write, io_write )
LROT_TABENTRY( __index, iolib )
LROT_END(iolib, NULL, 0)
LROT_END(iolib, NULL, LROT_MASK_GC_INDEX)
LROT_BEGIN(flib)
LROT_TABLE(flib);
LROT_BEGIN(flib, NULL, LROT_MASK_GC_INDEX)
LROT_FUNCENTRY( __gc, io_gc )
LROT_TABENTRY( __index, flib )
LROT_FUNCENTRY( __tostring, io_tostring )
LROT_FUNCENTRY( close, io_close )
LROT_FUNCENTRY( flush, f_flush )
LROT_FUNCENTRY( lines, f_lines )
@ -460,9 +467,6 @@ LROT_BEGIN(flib)
LROT_FUNCENTRY( seek, f_seek )
LROT_FUNCENTRY( setvbuf, f_setvbuf )
LROT_FUNCENTRY( write, f_write )
LROT_FUNCENTRY( __gc, io_gc )
LROT_FUNCENTRY( __tostring, io_tostring )
LROT_TABENTRY( __index, flib )
LROT_END(flib, NULL, LROT_MASK_GC_INDEX)
static const luaL_Reg io_base[] = {{NULL, NULL}};

View File

@ -6,7 +6,6 @@
#define LUAC_CROSS_FILE
#include "luac_cross.h"
#include <errno.h>
#include <locale.h>
#include <stdlib.h>
@ -20,7 +19,7 @@
#include "lauxlib.h"
#include "lualib.h"
#include "lrotable.h"
#include "lnodemcu.h"
static int os_pushresult (lua_State *L, int i, const char *filename) {
int en = errno; /* calls to Lua API may change this value */
@ -220,7 +219,7 @@ static int os_exit (lua_State *L) {
exit(luaL_optint(L, 1, EXIT_SUCCESS));
}
LROT_PUBLIC_BEGIN(oslib)
LROT_BEGIN(oslib, NULL, 0)
LROT_FUNCENTRY( clock, os_clock )
LROT_FUNCENTRY( date, os_date )
#if !defined LUA_NUMBER_INTEGRAL

View File

@ -5,16 +5,15 @@
*/
#define LUAC_CROSS_FILE
#define luac_c
#define LUA_CORE
#include "luac_cross.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define luac_c
#define LUA_CORE
#include "lua.h"
#include "lauxlib.h"
@ -290,8 +289,8 @@ static int pmain(lua_State* L)
if (!lua_checkstack(L,argc)) fatal("too many input files");
if (execute)
{
if (luaL_loadfile(L,execute)!=0) fatal(lua_tostring(L,-1));
luaL_openlibs(L);
if (luaL_loadfile(L,execute)!=0) fatal(lua_tostring(L,-1));
lua_pushstring(L, execute);
if (lua_pcall(L, 1, 1, 0)) fatal(lua_tostring(L,-1));
if (!lua_isfunction(L, -1))

View File

@ -6,7 +6,6 @@
#define LUAC_CROSS_FILE
#include "luac_cross.h"
#include <ctype.h>
#include <stdio.h>

View File

@ -13,6 +13,13 @@
#include <stdbool.h>
#include "user_config.h"
#ifdef __XTENSA__
# define LUA_USE_ESP
#else
# define LUA_USE_HOST
#endif
/*
** ==================================================================
** Search for "@@" to find all configurable definitions.
@ -266,16 +273,6 @@
#endif
/*
@@ LUA_PROMPT is the default prompt used by stand-alone Lua.
@@ LUA_PROMPT2 is the default continuation prompt used by stand-alone Lua.
** CHANGE them if you want different prompts. (You can also change the
** prompts dynamically, assigning to globals _PROMPT/_PROMPT2.)
*/
#define LUA_PROMPT "> "
#define LUA_PROMPT2 ">> "
/*
@@ LUA_PROGNAME is the default name for the stand-alone Lua program.
** CHANGE it if your stand-alone interpreter has a different name and
@ -300,25 +297,6 @@
** CHANGE them if you want to improve this functionality (e.g., by using
** GNU readline and history facilities).
*/
#if defined(LUA_USE_STDIO)
#if defined(LUA_CROSS_COMPILER) && defined(LUA_USE_READLINE)
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL)
#define lua_saveline(L,idx) \
if (lua_strlen(L,idx) > 0) /* non-empty line? */ \
add_history(lua_tostring(L, idx)); /* add it to history */
#define lua_freeline(L,b) ((void)L, free(b))
#else // #if defined(LUA_CROSS_COMPILER) && defined(LUA_USE_READLINE)
#define lua_readline(L,b,p) \
((void)L, fputs(p, c_stdout), fflush(c_stdout), /* show prompt */ \
fgets(b, LUA_MAXINPUT, c_stdin) != NULL) /* get line */
#define lua_saveline(L,idx) { (void)L; (void)idx; }
#define lua_freeline(L,b) { (void)L; (void)b; }
#endif // #if defined(LUA_USE_READLINE)
#else // #if defined(LUA_USE_STDIO)
#define lua_readline(L,b,p) (readline4lua(p, b, LUA_MAXINPUT))
#define lua_saveline(L,idx) { (void)L; (void)idx; }
@ -326,48 +304,29 @@
extern int readline4lua(const char *prompt, char *buffer, int length);
#endif // #if defined(LUA_USE_STDIO)
/*
@@ luai_writestring/luai_writeline define how 'print' prints its results.
@@ lua_writestring/luai_writeline define how 'print' prints its results.
** They are only used in libraries and the stand-alone program. (The #if
** avoids including 'stdio.h' everywhere.)
*/
#if !defined(LUA_USE_STDIO)
#define luai_writestring(s, l) puts(s)
#define luai_writeline() puts("\n")
#endif // defined(LUA_USE_STDIO)
#ifdef LUA_USE_ESP
#define lua_writestring(s,l) output_redirect((s),(l))
#else
#define lua_writestring(s,l) fwrite((s), sizeof(char), (l), stdout)
#endif
#define luai_writeline() lua_writestring("\n",1)
/*
@@ luai_writestringerror defines how to print error messages.
@@ lua_writestringerror defines how to print error messages.
** (A format string with one argument is enough for Lua...)
*/
#if !defined(LUA_USE_STDIO)
#define luai_writestringerror(s,p) dbg_printf((s), (p))
#endif // defined(LUA_USE_STDIO)
#ifdef LUA_USE_ESP
#define lua_writestringerror(s,p) dbg_printf((s), (p))
#else
#define lua_writestringerror(s,p) fprintf(stderr, (s), (p))
#endif
/* }================================================================== */
/*
@@ LUAI_GCPAUSE defines the default pause between garbage-collector cycles
@* as a percentage.
** CHANGE it if you want the GC to run faster or slower (higher values
** mean larger pauses which mean slower collection.) You can also change
** this value dynamically.
*/
#define LUAI_GCPAUSE 110 /* 110% (wait memory to grow 10% before next gc) */
/*
@@ LUAI_GCMUL defines the default speed of garbage collection relative to
@* memory allocation as a percentage.
** CHANGE it if you want to change the granularity of the garbage
** collection. (Higher values mean coarser collections. 0 represents
** infinity, where each step performs a full collection.) You can also
** change this value dynamically.
*/
#define LUAI_GCPAUSE 110 /* 110% (wait memory to grow 10% before next gc) */
#define LUAI_GCMUL 200 /* GC runs 'twice the speed' of memory allocation */
@ -898,4 +857,6 @@ union luai_Cast { double l_d; long l_l; };
#error "Pipes not supported NodeMCU firmware"
#endif
#define LUA_DEBUG_HOOK lua_debugbreak
#endif

View File

@ -6,7 +6,6 @@
#define lundump_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
#include <string.h>
@ -164,16 +163,9 @@ static TString* LoadString(LoadState* S)
return NULL;
else
{
char* s;
if (!luaZ_direct_mode(S->Z)) {
s = luaZ_openspace(S->L,S->b,size);
LoadBlock(S,s,size);
return luaS_newlstr(S->L,s,size-1); /* remove trailing zero */
} else {
s = (char*)luaZ_get_crt_address(S->Z);
LoadBlock(S,NULL,size);
return luaS_newlstr(S->L,s,size-1);
}
char* s = luaZ_openspace(S->L,S->b,size);
LoadBlock(S,s,size);
return luaS_newlstr(S->L,s,size-1); /* remove trailing zero */
}
}
@ -181,13 +173,8 @@ static void LoadCode(LoadState* S, Proto* f)
{
int n=LoadInt(S);
Align4(S);
if (!luaZ_direct_mode(S->Z)) {
f->code=luaM_newvector(S->L,n,Instruction);
LoadVector(S,f->code,n,sizeof(Instruction));
} else {
f->code=(Instruction*)luaZ_get_crt_address(S->Z);
LoadVector(S,NULL,n,sizeof(Instruction));
}
f->code=luaM_newvector(S->L,n,Instruction);
LoadVector(S,f->code,n,sizeof(Instruction));
f->sizecode=n;
}
@ -238,24 +225,14 @@ static void LoadDebug(LoadState* S, Proto* f)
#ifdef LUA_OPTIMIZE_DEBUG
if(n) {
if (!luaZ_direct_mode(S->Z)) {
f->packedlineinfo=luaM_newvector(S->L,n,unsigned char);
LoadBlock(S,f->packedlineinfo,n);
} else {
f->packedlineinfo=(unsigned char*)luaZ_get_crt_address(S->Z);
LoadBlock(S,NULL,n);
}
f->packedlineinfo=luaM_newvector(S->L,n,unsigned char);
LoadBlock(S,f->packedlineinfo,n);
} else {
f->packedlineinfo=NULL;
}
#else
if (!luaZ_direct_mode(S->Z)) {
f->lineinfo=luaM_newvector(S->L,n,int);
LoadVector(S,f->lineinfo,n,sizeof(int));
} else {
f->lineinfo=(int*)luaZ_get_crt_address(S->Z);
LoadVector(S,NULL,n,sizeof(int));
}
f->lineinfo=luaM_newvector(S->L,n,int);
LoadVector(S,f->lineinfo,n,sizeof(int));
f->sizelineinfo=n;
#endif
n=LoadInt(S);
@ -280,7 +257,6 @@ static Proto* LoadFunction(LoadState* S, TString* p)
Proto* f;
if (++S->L->nCcalls > LUAI_MAXCCALLS) error(S,"code too deep");
f=luaF_newproto(S->L);
if (luaZ_direct_mode(S->Z)) l_setbit((f)->marked, READONLYBIT);
setptvalue2s(S->L,S->L->top,f); incr_top(S->L);
f->source=LoadString(S); if (f->source==NULL) f->source=p;
f->linedefined=LoadInt(S);

View File

@ -50,11 +50,4 @@ LUAI_FUNC void luaU_print (const Proto* f, int full);
/* size of header of binary files */
#define LUAC_HEADERSIZE 12
/* error codes from cross-compiler */
/* target integer is too small to hold a value */
#define LUA_ERR_CC_INTOVERFLOW 101
/* target lua_Number is integral but a constant is non-integer */
#define LUA_ERR_CC_NOTINTEGER 102
#endif

View File

@ -7,7 +7,6 @@
#define lvm_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
#include <stdio.h>
@ -25,7 +24,6 @@
#include "ltable.h"
#include "ltm.h"
#include "lvm.h"
#include "lrotable.h"
/* limit for table tag-method chains (to avoid loops) */
@ -134,26 +132,17 @@ void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) {
if (ttistable(t)) { /* `t' is a table? */
Table *h = hvalue(t);
const TValue *res = luaH_get(h, key); /* do a primitive get */
if (!ttisnil(res) || /* result is no nil? */
if (!ttisnil(res) || /* result is no nil? */
(tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */
setobj2s(L, val, res);
return;
}
/* else will try the tag method */
} else if (ttisrotable(t)) { /* `t' is a table? */
void *h = rvalue(t);
const TValue *res = luaH_get_ro(h, key); /* do a primitive get */
if (!ttisnil(res) || /* result is no nil? */
(tm = fasttm(L, (Table*)luaR_getmeta(h), TM_INDEX)) == NULL) { /* or no TM? */
setobj2s(L, val, res);
return;
}
/* else will try the tag method */
}
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))
luaG_typeerror(L, t, "index");
if (ttisfunction(tm) || ttislightfunction(tm)) {
if (ttisfunction(tm)) {
callTMres(L, val, tm, t, key);
return;
}
@ -193,7 +182,7 @@ void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
luaG_typeerror(L, t, "index");
if (ttisfunction(tm) || ttislightfunction(tm)) {
if (ttisfunction(tm)) {
L->top--;
unfixedstack(L);
callTM(L, tm, t, key, val);
@ -305,8 +294,6 @@ int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2) {
case LUA_TNIL: return 1;
case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2));
case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */
case LUA_TROTABLE:
return rvalue(t1) == rvalue(t2);
case LUA_TLIGHTUSERDATA:
case LUA_TLIGHTFUNCTION:
return pvalue(t1) == pvalue(t2);
@ -316,6 +303,8 @@ int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2) {
TM_EQ);
break; /* will try TM */
}
case LUA_TROTABLE:
return hvalue(t1) == hvalue(t2);
case LUA_TTABLE: {
if (hvalue(t1) == hvalue(t2)) return 1;
tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ);
@ -440,7 +429,6 @@ static void Arith (lua_State *L, StkId ra, const TValue *rb,
}
void luaV_execute (lua_State *L, int nexeccalls) {
LClosure *cl;
StkId base;
@ -582,10 +570,9 @@ void luaV_execute (lua_State *L, int nexeccalls) {
}
case OP_LEN: {
const TValue *rb = RB(i);
switch (ttype(rb)) {
case LUA_TTABLE:
case LUA_TROTABLE: {
setnvalue(ra, ttistable(rb) ? cast_num(luaH_getn(hvalue(rb))) : cast_num(luaH_getn_ro(rvalue(rb))));
switch (basettype(rb)) {
case LUA_TTABLE: {
setnvalue(ra, cast_num(luaH_getn(hvalue(rb))));
break;
}
case LUA_TSTRING: {

View File

@ -7,7 +7,6 @@
#define lzio_c
#define LUA_CORE
#define LUAC_CROSS_FILE
#include "lua.h"
#include <string.h>
@ -49,7 +48,7 @@ void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) {
z->L = L;
z->reader = reader;
z->data = data;
z->n = z->i = 0;
z->n = 0;
z->p = NULL;
}
@ -64,7 +63,6 @@ size_t luaZ_read (ZIO *z, void *b, size_t n) {
if (b)
memcpy(b, z->p, m);
z->n -= m;
z->i += m;
z->p += m;
if (b)
b = (char *)b + m;

View File

@ -43,9 +43,6 @@ typedef struct Mbuffer {
#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0)
#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0)
#define luaZ_get_base_address(zio) ((const char *)((zio)->reader(NULL, (zio)->data, NULL)))
#define luaZ_direct_mode(zio) (luaZ_get_base_address(zio) != NULL)
#define luaZ_get_crt_address(zio) (luaZ_get_base_address(zio) + (zio)->i)
LUAI_FUNC char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n);
LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader,
@ -59,7 +56,6 @@ LUAI_FUNC int luaZ_lookahead (ZIO *z);
struct Zio {
size_t n; /* bytes still unread */
size_t i; /* buffer offset */
const char *p; /* current position in buffer */
lua_Reader reader;
void* data; /* additional data */

40
app/lua53/Makefile Normal file
View File

@ -0,0 +1,40 @@
#############################################################
# Required variables for each makefile
# Discard this section from all parent makefiles
# Expected variables (with automatic defaults):
# CSRCS (all "C" files in the dir)
# SUBDIRS (all subdirs with a Makefile)
# GEN_LIBS - list of libs to be generated ()
# GEN_IMAGES - list of images to be generated ()
# COMPONENTS_xxx - a list of libs/objs in the form
# subdir/lib to be extracted and rolled up into
# a generated lib/image xxx.a ()
#
ifndef PDIR
SUBDIRS = host
GEN_LIBS = liblua.a
endif
STD_CFLAGS=-std=gnu11 -Wimplicit -Wall
#############################################################
# Configuration i.e. compile options etc.
# Target specific stuff (defines etc.) goes in here!
#
#DEFINES += -DDEVELOPMENT_TOOLS -DDEVELOPMENT_USE_GDB -DDEVELOPMENT_BREAK_ON_STARTUP_PIN=1
#EXTRA_CCFLAGS += -ggdb -O0
#############################################################
# Recursion Magic - Don't touch this!!
#
INCLUDES := $(INCLUDES) -I $(PDIR)include
INCLUDES += -I ./
INCLUDES += -I ../spiffs
INCLUDES += -I ../libc
INCLUDES += -I ../modules
INCLUDES += -I ../platform
INCLUDES += -I ../uzlib
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile

111
app/lua53/host/Makefile Normal file
View File

@ -0,0 +1,111 @@
#
# This Makefile is called from the core Makefile hierarchy which is a hierarchical
# make which uses parent callbacks to implement inheritance. However if luac_cross
# build stands outside this, it uses the host toolchain to implement a separate
# host build of the luac.cross image.
#
.NOTPARALLEL:
CCFLAGS:= -I. -I.. -I../../include -I../../uzlib
LDFLAGS:= -L$(SDK_DIR)/lib -L$(SDK_DIR)/ld -lm -ldl -Wl,-Map=mapfile
CCFLAGS += -Wall
TARGET = host
VERBOSE ?=
V ?= $(VERBOSE)
ifeq ("$(V)","1")
export summary := @true
else
export summary := @echo
# disable echoing of commands, directory names
# MAKEFLAGS += --silent -w
endif # $(V)==1
DEBUG ?=
ifeq ("$(DEBUG)","1")
FLAVOR = debug
CCFLAGS += -O0 -ggdb
TARGET_LDFLAGS += -O0 -ggdb
DEFINES += -DLUA_DEBUG_BUILD -DDEVELOPMENT_TOOLS -DDEVELOPMENT_USE_GDB
else
FLAVOR = release
CCFLAGS += -O2
TARGET_LDFLAGS += -O2
endif # DEBUG
LUACSRC := luac.c liolib.c loslib.c
LUASRC := lapi.c lauxlib.c lbaselib.c lcode.c lcorolib.c lctype.c \
ldblib.c ldebug.c ldo.c ldump.c lfunc.c lgc.c \
linit.c llex.c lmathlib.c lmem.c loadlib.c lnodemcu.c \
lobject.c lopcodes.c lparser.c lstate.c lstring.c lstrlib.c \
ltable.c ltablib.c ltm.c lundump.c lutf8lib.c lvm.c \
lzio.c
UZSRC := uzlib_deflate.c crc32.c
TEST ?=
ifeq ("$(TEST)","1")
DEFINES += -DLUA_ENABLE_TEST
LUACSRC += ltests.c
endif # $(TEST)==1
#
# This relies on the files being unique on the vpath
#
SRC := $(LUACSRC) $(LUASRC) $(UZSRC)
vpath %.c .:..:../../libc:../../uzlib
ODIR := .output/$(TARGET)/$(FLAVOR)/obj
OBJS := $(SRC:%.c=$(ODIR)/%.o)
DEPS := $(SRC:%.c=$(ODIR)/%.d)
CFLAGS = $(CCFLAGS) $(DEFINES) $(EXTRA_CCFLAGS) $(STD_CFLAGS) $(INCLUDES)
DFLAGS = $(CCFLAGS) $(DDEFINES) $(EXTRA_CCFLAGS) $(STD_CFLAGS) $(INCLUDES)
CC := $(WRAPCC) gcc
ECHO := echo
BUILD_TYPE := $(shell $(CC) $(EXTRA_CCFLAGS) -E -dM - <../../include/user_config.h | grep LUA_NUMBER_INTEGRAL | wc -l)
ifeq ($(BUILD_TYPE),0)
IMAGE := ../../../luac.cross
else
IMAGE := ../../../luac.cross.int
endif
.PHONY: test clean all
all: $(DEPS) $(IMAGE)
$(IMAGE) : $(OBJS)
$(summary) HOSTLD $@
$(CC) $(OBJS) -o $@ $(LDFLAGS)
test :
@echo CC: $(CC)
@echo SRC: $(SRC)
@echo OBJS: $(OBJS)
@echo DEPS: $(DEPS)
@echo IMAGE: $(IMAGE)
clean :
$(RM) -r $(ODIR)
ifneq ($(MAKECMDGOALS),clean)
-include $(DEPS)
endif
$(ODIR)/%.o: %.c
@mkdir -p $(ODIR);
$(summary) HOSTCC $(CURDIR)/$<
$(CC) $(if $(findstring $<,$(DSRCS)),$(DFLAGS),$(CFLAGS)) $(COPTS_$(*F)) -o $@ -c $<
$(ODIR)/%.d: %.c
@mkdir -p $(ODIR);
$(summary) DEPEND: HOSTCC $(CURDIR)/$<
@set -e; rm -f $@; \
$(CC) -M $(CFLAGS) $< > $@.$$$$; \
sed 's,\($*\.o\)[ :]*,$(ODIR)/\1 $@ : ,g' < $@.$$$$ > $@; \
rm -f $@.$$$$

776
app/lua53/host/liolib.c Normal file
View File

@ -0,0 +1,776 @@
/*
** $Id: liolib.c,v 2.151.1.1 2017/04/19 17:29:57 roberto Exp $
** Standard I/O (and system) library
** See Copyright Notice in lua.h
*/
#define liolib_c
#define LUA_LIB
#include "lprefix.h"
#include <ctype.h>
#include <errno.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
/*
** Change this macro to accept other modes for 'fopen' besides
** the standard ones.
*/
#if !defined(l_checkmode)
/* accepted extensions to 'mode' in 'fopen' */
#if !defined(L_MODEEXT)
#define L_MODEEXT "b"
#endif
/* Check whether 'mode' matches '[rwa]%+?[L_MODEEXT]*' */
static int l_checkmode (const char *mode) {
return (*mode != '\0' && strchr("rwa", *(mode++)) != NULL &&
(*mode != '+' || (++mode, 1)) && /* skip if char is '+' */
(strspn(mode, L_MODEEXT) == strlen(mode))); /* check extensions */
}
#endif
/*
** {======================================================
** l_popen spawns a new process connected to the current
** one through the file streams.
** =======================================================
*/
#if !defined(l_popen) /* { */
#if defined(LUA_USE_POSIX) /* { */
#define l_popen(L,c,m) (fflush(NULL), popen(c,m))
#define l_pclose(L,file) (pclose(file))
#elif defined(LUA_USE_WINDOWS) /* }{ */
#define l_popen(L,c,m) (_popen(c,m))
#define l_pclose(L,file) (_pclose(file))
#else /* }{ */
/* ISO C definitions */
#define l_popen(L,c,m) \
((void)((void)c, m), \
luaL_error(L, "'popen' not supported"), \
(FILE*)0)
#define l_pclose(L,file) ((void)L, (void)file, -1)
#endif /* } */
#endif /* } */
/* }====================================================== */
#if !defined(l_getc) /* { */
#if defined(LUA_USE_POSIX)
#define l_getc(f) getc_unlocked(f)
#define l_lockfile(f) flockfile(f)
#define l_unlockfile(f) funlockfile(f)
#else
#define l_getc(f) getc(f)
#define l_lockfile(f) ((void)0)
#define l_unlockfile(f) ((void)0)
#endif
#endif /* } */
/*
** {======================================================
** l_fseek: configuration for longer offsets
** =======================================================
*/
#if !defined(l_fseek) /* { */
#if defined(LUA_USE_POSIX) /* { */
#include <sys/types.h>
#define l_fseek(f,o,w) fseeko(f,o,w)
#define l_ftell(f) ftello(f)
#define l_seeknum off_t
#elif defined(LUA_USE_WINDOWS) && !defined(_CRTIMP_TYPEINFO) \
&& defined(_MSC_VER) && (_MSC_VER >= 1400) /* }{ */
/* Windows (but not DDK) and Visual C++ 2005 or higher */
#define l_fseek(f,o,w) _fseeki64(f,o,w)
#define l_ftell(f) _ftelli64(f)
#define l_seeknum __int64
#else /* }{ */
/* ISO C definitions */
#define l_fseek(f,o,w) fseek(f,o,w)
#define l_ftell(f) ftell(f)
#define l_seeknum long
#endif /* } */
#endif /* } */
/* }====================================================== */
#define IO_PREFIX "_IO_"
#define IOPREF_LEN (sizeof(IO_PREFIX)/sizeof(char) - 1)
#define IO_INPUT (IO_PREFIX "input")
#define IO_OUTPUT (IO_PREFIX "output")
typedef luaL_Stream LStream;
#define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE))
#define isclosed(p) ((p)->closef == NULL)
static int io_type (lua_State *L) {
LStream *p;
luaL_checkany(L, 1);
p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE);
if (p == NULL)
lua_pushnil(L); /* not a file */
else if (isclosed(p))
lua_pushliteral(L, "closed file");
else
lua_pushliteral(L, "file");
return 1;
}
static int f_tostring (lua_State *L) {
LStream *p = tolstream(L);
if (isclosed(p))
lua_pushliteral(L, "file (closed)");
else
lua_pushfstring(L, "file (%p)", p->f);
return 1;
}
static FILE *tofile (lua_State *L) {
LStream *p = tolstream(L);
if (isclosed(p))
luaL_error(L, "attempt to use a closed file");
lua_assert(p->f);
return p->f;
}
/*
** When creating file handles, always creates a 'closed' file handle
** before opening the actual file; so, if there is a memory error, the
** handle is in a consistent state.
*/
static LStream *newprefile (lua_State *L) {
LStream *p = (LStream *)lua_newuserdata(L, sizeof(LStream));
p->closef = NULL; /* mark file handle as 'closed' */
luaL_setmetatable(L, LUA_FILEHANDLE);
return p;
}
/*
** Calls the 'close' function from a file handle. The 'volatile' avoids
** a bug in some versions of the Clang compiler (e.g., clang 3.0 for
** 32 bits).
*/
static int aux_close (lua_State *L) {
LStream *p = tolstream(L);
volatile lua_CFunction cf = p->closef;
p->closef = NULL; /* mark stream as closed */
return (*cf)(L); /* close it */
}
static int f_close (lua_State *L) {
tofile(L); /* make sure argument is an open stream */
return aux_close(L);
}
static int io_close (lua_State *L) {
if (lua_isnone(L, 1)) /* no argument? */
lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use standard output */
return f_close(L);
}
static int f_gc (lua_State *L) {
LStream *p = tolstream(L);
if (!isclosed(p) && p->f != NULL)
aux_close(L); /* ignore closed and incompletely open files */
return 0;
}
/*
** function to close regular files
*/
static int io_fclose (lua_State *L) {
LStream *p = tolstream(L);
int res = fclose(p->f);
return luaL_fileresult(L, (res == 0), NULL);
}
static LStream *newfile (lua_State *L) {
LStream *p = newprefile(L);
p->f = NULL;
p->closef = &io_fclose;
return p;
}
static void opencheck (lua_State *L, const char *fname, const char *mode) {
LStream *p = newfile(L);
p->f = fopen(fname, mode);
if (p->f == NULL)
luaL_error(L, "cannot open file '%s' (%s)", fname, strerror(errno));
}
static int io_open (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r");
LStream *p = newfile(L);
const char *md = mode; /* to traverse/check mode */
luaL_argcheck(L, l_checkmode(md), 2, "invalid mode");
p->f = fopen(filename, mode);
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
}
/*
** function to close 'popen' files
*/
static int io_pclose (lua_State *L) {
LStream *p = tolstream(L);
return luaL_execresult(L, l_pclose(L, p->f));
}
static int io_popen (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r");
LStream *p = newprefile(L);
p->f = l_popen(L, filename, mode);
p->closef = &io_pclose;
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
}
static int io_tmpfile (lua_State *L) {
LStream *p = newfile(L);
p->f = tmpfile();
return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1;
}
static FILE *getiofile (lua_State *L, const char *findex) {
LStream *p;
lua_getfield(L, LUA_REGISTRYINDEX, findex);
p = (LStream *)lua_touserdata(L, -1);
if (isclosed(p))
luaL_error(L, "standard %s file is closed", findex + IOPREF_LEN);
return p->f;
}
static int g_iofile (lua_State *L, const char *f, const char *mode) {
if (!lua_isnoneornil(L, 1)) {
const char *filename = lua_tostring(L, 1);
if (filename)
opencheck(L, filename, mode);
else {
tofile(L); /* check that it's a valid file handle */
lua_pushvalue(L, 1);
}
lua_setfield(L, LUA_REGISTRYINDEX, f);
}
/* return current value */
lua_getfield(L, LUA_REGISTRYINDEX, f);
return 1;
}
static int io_input (lua_State *L) {
return g_iofile(L, IO_INPUT, "r");
}
static int io_output (lua_State *L) {
return g_iofile(L, IO_OUTPUT, "w");
}
static int io_readline (lua_State *L);
/*
** maximum number of arguments to 'f:lines'/'io.lines' (it + 3 must fit
** in the limit for upvalues of a closure)
*/
#define MAXARGLINE 250
static void aux_lines (lua_State *L, int toclose) {
int n = lua_gettop(L) - 1; /* number of arguments to read */
luaL_argcheck(L, n <= MAXARGLINE, MAXARGLINE + 2, "too many arguments");
lua_pushinteger(L, n); /* number of arguments to read */
lua_pushboolean(L, toclose); /* close/not close file when finished */
lua_rotate(L, 2, 2); /* move 'n' and 'toclose' to their positions */
lua_pushcclosure(L, io_readline, 3 + n);
}
static int f_lines (lua_State *L) {
tofile(L); /* check that it's a valid file handle */
aux_lines(L, 0);
return 1;
}
static int io_lines (lua_State *L) {
int toclose;
if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */
if (lua_isnil(L, 1)) { /* no file name? */
lua_getfield(L, LUA_REGISTRYINDEX, IO_INPUT); /* get default input */
lua_replace(L, 1); /* put it at index 1 */
tofile(L); /* check that it's a valid file handle */
toclose = 0; /* do not close it after iteration */
}
else { /* open a new file */
const char *filename = luaL_checkstring(L, 1);
opencheck(L, filename, "r");
lua_replace(L, 1); /* put file at index 1 */
toclose = 1; /* close it after iteration */
}
aux_lines(L, toclose);
return 1;
}
/*
** {======================================================
** READ
** =======================================================
*/
/* maximum length of a numeral */
#if !defined (L_MAXLENNUM)
#define L_MAXLENNUM 200
#endif
/* auxiliary structure used by 'read_number' */
typedef struct {
FILE *f; /* file being read */
int c; /* current character (look ahead) */
int n; /* number of elements in buffer 'buff' */
char buff[L_MAXLENNUM + 1]; /* +1 for ending '\0' */
} RN;
/*
** Add current char to buffer (if not out of space) and read next one
*/
static int nextc (RN *rn) {
if (rn->n >= L_MAXLENNUM) { /* buffer overflow? */
rn->buff[0] = '\0'; /* invalidate result */
return 0; /* fail */
}
else {
rn->buff[rn->n++] = rn->c; /* save current char */
rn->c = l_getc(rn->f); /* read next one */
return 1;
}
}
/*
** Accept current char if it is in 'set' (of size 2)
*/
static int test2 (RN *rn, const char *set) {
if (rn->c == set[0] || rn->c == set[1])
return nextc(rn);
else return 0;
}
/*
** Read a sequence of (hex)digits
*/
static int readdigits (RN *rn, int hex) {
int count = 0;
while ((hex ? isxdigit(rn->c) : isdigit(rn->c)) && nextc(rn))
count++;
return count;
}
/*
** Read a number: first reads a valid prefix of a numeral into a buffer.
** Then it calls 'lua_stringtonumber' to check whether the format is
** correct and to convert it to a Lua number
*/
static int read_number (lua_State *L, FILE *f) {
RN rn;
int count = 0;
int hex = 0;
char decp[2];
rn.f = f; rn.n = 0;
decp[0] = lua_getlocaledecpoint(); /* get decimal point from locale */
decp[1] = '.'; /* always accept a dot */
l_lockfile(rn.f);
do { rn.c = l_getc(rn.f); } while (isspace(rn.c)); /* skip spaces */
test2(&rn, "-+"); /* optional signal */
if (test2(&rn, "00")) {
if (test2(&rn, "xX")) hex = 1; /* numeral is hexadecimal */
else count = 1; /* count initial '0' as a valid digit */
}
count += readdigits(&rn, hex); /* integral part */
if (test2(&rn, decp)) /* decimal point? */
count += readdigits(&rn, hex); /* fractional part */
if (count > 0 && test2(&rn, (hex ? "pP" : "eE"))) { /* exponent mark? */
test2(&rn, "-+"); /* exponent signal */
readdigits(&rn, 0); /* exponent digits */
}
ungetc(rn.c, rn.f); /* unread look-ahead char */
l_unlockfile(rn.f);
rn.buff[rn.n] = '\0'; /* finish string */
if (lua_stringtonumber(L, rn.buff)) /* is this a valid number? */
return 1; /* ok */
else { /* invalid format */
lua_pushnil(L); /* "result" to be removed */
return 0; /* read fails */
}
}
static int test_eof (lua_State *L, FILE *f) {
int c = getc(f);
ungetc(c, f); /* no-op when c == EOF */
lua_pushliteral(L, "");
return (c != EOF);
}
static int read_line (lua_State *L, FILE *f, int chop) {
luaL_Buffer b;
int c = '\0';
luaL_buffinit(L, &b);
while (c != EOF && c != '\n') { /* repeat until end of line */
char *buff = luaL_prepbuffer(&b); /* preallocate buffer */
int i = 0;
l_lockfile(f); /* no memory errors can happen inside the lock */
while (i < LUAL_BUFFERSIZE && (c = l_getc(f)) != EOF && c != '\n')
buff[i++] = c;
l_unlockfile(f);
luaL_addsize(&b, i);
}
if (!chop && c == '\n') /* want a newline and have one? */
luaL_addchar(&b, c); /* add ending newline to result */
luaL_pushresult(&b); /* close buffer */
/* return ok if read something (either a newline or something else) */
return (c == '\n' || lua_rawlen(L, -1) > 0);
}
static void read_all (lua_State *L, FILE *f) {
size_t nr;
luaL_Buffer b;
luaL_buffinit(L, &b);
do { /* read file in chunks of LUAL_BUFFERSIZE bytes */
char *p = luaL_prepbuffer(&b);
nr = fread(p, sizeof(char), LUAL_BUFFERSIZE, f);
luaL_addsize(&b, nr);
} while (nr == LUAL_BUFFERSIZE);
luaL_pushresult(&b); /* close buffer */
}
static int read_chars (lua_State *L, FILE *f, size_t n) {
size_t nr; /* number of chars actually read */
char *p;
luaL_Buffer b;
luaL_buffinit(L, &b);
p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */
nr = fread(p, sizeof(char), n, f); /* try to read 'n' chars */
luaL_addsize(&b, nr);
luaL_pushresult(&b); /* close buffer */
return (nr > 0); /* true iff read something */
}
static int g_read (lua_State *L, FILE *f, int first) {
int nargs = lua_gettop(L) - 1;
int success;
int n;
clearerr(f);
if (nargs == 0) { /* no arguments? */
success = read_line(L, f, 1);
n = first+1; /* to return 1 result */
}
else { /* ensure stack space for all results and for auxlib's buffer */
luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments");
success = 1;
for (n = first; nargs-- && success; n++) {
if (lua_type(L, n) == LUA_TNUMBER) {
size_t l = (size_t)luaL_checkinteger(L, n);
success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l);
}
else {
const char *p = luaL_checkstring(L, n);
if (*p == '*') p++; /* skip optional '*' (for compatibility) */
switch (*p) {
case 'n': /* number */
success = read_number(L, f);
break;
case 'l': /* line */
success = read_line(L, f, 1);
break;
case 'L': /* line with end-of-line */
success = read_line(L, f, 0);
break;
case 'a': /* file */
read_all(L, f); /* read entire file */
success = 1; /* always success */
break;
default:
return luaL_argerror(L, n, "invalid format");
}
}
}
}
if (ferror(f))
return luaL_fileresult(L, 0, NULL);
if (!success) {
lua_pop(L, 1); /* remove last result */
lua_pushnil(L); /* push nil instead */
}
return n - first;
}
static int io_read (lua_State *L) {
return g_read(L, getiofile(L, IO_INPUT), 1);
}
static int f_read (lua_State *L) {
return g_read(L, tofile(L), 2);
}
static int io_readline (lua_State *L) {
LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1));
int i;
int n = (int)lua_tointeger(L, lua_upvalueindex(2));
if (isclosed(p)) /* file is already closed? */
return luaL_error(L, "file is already closed");
lua_settop(L , 1);
luaL_checkstack(L, n, "too many arguments");
for (i = 1; i <= n; i++) /* push arguments to 'g_read' */
lua_pushvalue(L, lua_upvalueindex(3 + i));
n = g_read(L, p->f, 2); /* 'n' is number of results */
lua_assert(n > 0); /* should return at least a nil */
if (lua_toboolean(L, -n)) /* read at least one value? */
return n; /* return them */
else { /* first result is nil: EOF or error */
if (n > 1) { /* is there error information? */
/* 2nd result is error message */
return luaL_error(L, "%s", lua_tostring(L, -n + 1));
}
if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */
lua_settop(L, 0);
lua_pushvalue(L, lua_upvalueindex(1));
aux_close(L); /* close it */
}
return 0;
}
}
/* }====================================================== */
static int g_write (lua_State *L, FILE *f, int arg) {
int nargs = lua_gettop(L) - arg;
int status = 1;
for (; nargs--; arg++) {
if (lua_type(L, arg) == LUA_TNUMBER) {
/* optimization: could be done exactly as for strings */
int len = lua_isinteger(L, arg)
? fprintf(f, LUA_INTEGER_FMT,
(LUAI_UACINT)lua_tointeger(L, arg))
: fprintf(f, LUA_NUMBER_FMT,
(LUAI_UACNUMBER)lua_tonumber(L, arg));
status = status && (len > 0);
}
else {
size_t l;
const char *s = luaL_checklstring(L, arg, &l);
status = status && (fwrite(s, sizeof(char), l, f) == l);
}
}
if (status) return 1; /* file handle already on stack top */
else return luaL_fileresult(L, status, NULL);
}
static int io_write (lua_State *L) {
return g_write(L, getiofile(L, IO_OUTPUT), 1);
}
static int f_write (lua_State *L) {
FILE *f = tofile(L);
lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */
return g_write(L, f, 2);
}
static int f_seek (lua_State *L) {
static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END};
static const char *const modenames[] = {"set", "cur", "end", NULL};
FILE *f = tofile(L);
int op = luaL_checkoption(L, 2, "cur", modenames);
lua_Integer p3 = luaL_optinteger(L, 3, 0);
l_seeknum offset = (l_seeknum)p3;
luaL_argcheck(L, (lua_Integer)offset == p3, 3,
"not an integer in proper range");
op = l_fseek(f, offset, mode[op]);
if (op)
return luaL_fileresult(L, 0, NULL); /* error */
else {
lua_pushinteger(L, (lua_Integer)l_ftell(f));
return 1;
}
}
static int f_setvbuf (lua_State *L) {
static const int mode[] = {_IONBF, _IOFBF, _IOLBF};
static const char *const modenames[] = {"no", "full", "line", NULL};
FILE *f = tofile(L);
int op = luaL_checkoption(L, 2, NULL, modenames);
lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE);
int res = setvbuf(f, NULL, mode[op], (size_t)sz);
return luaL_fileresult(L, res == 0, NULL);
}
static int io_flush (lua_State *L) {
return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL);
}
static int f_flush (lua_State *L) {
return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL);
}
/*
** functions for 'io' library
*/
static const luaL_Reg iolib[] = {
{"close", io_close},
{"flush", io_flush},
{"input", io_input},
{"lines", io_lines},
{"open", io_open},
{"output", io_output},
{"popen", io_popen},
{"read", io_read},
{"tmpfile", io_tmpfile},
{"type", io_type},
{"write", io_write},
{NULL, NULL}
};
/*
** methods for file handles
*/
static const luaL_Reg flib[] = {
{"close", f_close},
{"flush", f_flush},
{"lines", f_lines},
{"read", f_read},
{"seek", f_seek},
{"setvbuf", f_setvbuf},
{"write", f_write},
{"__gc", f_gc},
{"__tostring", f_tostring},
{NULL, NULL}
};
static void createmeta (lua_State *L) {
luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */
lua_pushvalue(L, -1); /* push metatable */
lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
luaL_setfuncs(L, flib, 0); /* add file methods to new metatable */
lua_pop(L, 1); /* pop new metatable */
}
/*
** function to (not) close the standard files stdin, stdout, and stderr
*/
static int io_noclose (lua_State *L) {
LStream *p = tolstream(L);
p->closef = &io_noclose; /* keep file opened */
lua_pushnil(L);
lua_pushliteral(L, "cannot close standard file");
return 2;
}
static void createstdfile (lua_State *L, FILE *f, const char *k,
const char *fname) {
LStream *p = newprefile(L);
p->f = f;
p->closef = &io_noclose;
if (k != NULL) {
lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, k); /* add file to registry */
}
lua_setfield(L, -2, fname); /* add file to module */
}
LUAMOD_API int luaopen_io (lua_State *L) {
luaL_newlib(L, iolib); /* new module */
createmeta(L);
/* create (and set) default files */
createstdfile(L, stdin, IO_INPUT, "stdin");
createstdfile(L, stdout, IO_OUTPUT, "stdout");
createstdfile(L, stderr, NULL, "stderr");
return 1;
}

409
app/lua53/host/loslib.c Normal file
View File

@ -0,0 +1,409 @@
/*
** $Id: loslib.c,v 1.65.1.1 2017/04/19 17:29:57 roberto Exp $
** Standard Operating System library
** See Copyright Notice in lua.h
*/
#define loslib_c
#define LUA_LIB
#include "lprefix.h"
#include <errno.h>
#include <locale.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
/*
** {==================================================================
** List of valid conversion specifiers for the 'strftime' function;
** options are grouped by length; group of length 2 start with '||'.
** ===================================================================
*/
#if !defined(LUA_STRFTIMEOPTIONS) /* { */
/* options for ANSI C 89 (only 1-char options) */
#define L_STRFTIMEC89 "aAbBcdHIjmMpSUwWxXyYZ%"
/* options for ISO C 99 and POSIX */
#define L_STRFTIMEC99 "aAbBcCdDeFgGhHIjmMnprRStTuUVwWxXyYzZ%" \
"||" "EcECExEXEyEY" "OdOeOHOIOmOMOSOuOUOVOwOWOy" /* two-char options */
/* options for Windows */
#define L_STRFTIMEWIN "aAbBcdHIjmMpSUwWxXyYzZ%" \
"||" "#c#x#d#H#I#j#m#M#S#U#w#W#y#Y" /* two-char options */
#if defined(LUA_USE_WINDOWS)
#define LUA_STRFTIMEOPTIONS L_STRFTIMEWIN
#elif defined(LUA_USE_C89)
#define LUA_STRFTIMEOPTIONS L_STRFTIMEC89
#else /* C99 specification */
#define LUA_STRFTIMEOPTIONS L_STRFTIMEC99
#endif
#endif /* } */
/* }================================================================== */
/*
** {==================================================================
** Configuration for time-related stuff
** ===================================================================
*/
#if !defined(l_time_t) /* { */
/*
** type to represent time_t in Lua
*/
#define l_timet lua_Integer
#define l_pushtime(L,t) lua_pushinteger(L,(lua_Integer)(t))
static time_t l_checktime (lua_State *L, int arg) {
lua_Integer t = luaL_checkinteger(L, arg);
luaL_argcheck(L, (time_t)t == t, arg, "time out-of-bounds");
return (time_t)t;
}
#endif /* } */
#if !defined(l_gmtime) /* { */
/*
** By default, Lua uses gmtime/localtime, except when POSIX is available,
** where it uses gmtime_r/localtime_r
*/
#if defined(LUA_USE_POSIX) /* { */
#define l_gmtime(t,r) gmtime_r(t,r)
#define l_localtime(t,r) localtime_r(t,r)
#else /* }{ */
/* ISO C definitions */
#define l_gmtime(t,r) ((void)(r)->tm_sec, gmtime(t))
#define l_localtime(t,r) ((void)(r)->tm_sec, localtime(t))
#endif /* } */
#endif /* } */
/* }================================================================== */
/*
** {==================================================================
** Configuration for 'tmpnam':
** By default, Lua uses tmpnam except when POSIX is available, where
** it uses mkstemp.
** ===================================================================
*/
#if !defined(lua_tmpnam) /* { */
#if defined(__GNUC__) /* { */
#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);
if (cmd != NULL)
return luaL_execresult(L, stat);
else {
lua_pushboolean(L, stat); /* true if there is a shell */
return 1;
}
}
static int os_remove (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
return luaL_fileresult(L, remove(filename) == 0, filename);
}
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);
}
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;
}
static int os_clock (lua_State *L) {
lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC);
return 1;
}
/*
** {======================================================
** Time/Date operations
** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S,
** wday=%w+1, yday=%j, isdst=? }
** =======================================================
*/
static void setfield (lua_State *L, const char *key, int value) {
lua_pushinteger(L, value);
lua_setfield(L, -2, key);
}
static void setboolfield (lua_State *L, const char *key, int value) {
if (value < 0) /* undefined? */
return; /* does not set field */
lua_pushboolean(L, value);
lua_setfield(L, -2, key);
}
/*
** Set all fields from structure 'tm' in the table on top of the stack
*/
static void setallfields (lua_State *L, struct tm *stm) {
setfield(L, "sec", stm->tm_sec);
setfield(L, "min", stm->tm_min);
setfield(L, "hour", stm->tm_hour);
setfield(L, "day", stm->tm_mday);
setfield(L, "month", stm->tm_mon + 1);
setfield(L, "year", stm->tm_year + 1900);
setfield(L, "wday", stm->tm_wday + 1);
setfield(L, "yday", stm->tm_yday + 1);
setboolfield(L, "isdst", stm->tm_isdst);
}
static int getboolfield (lua_State *L, const char *key) {
int res;
res = (lua_getfield(L, -1, key) == LUA_TNIL) ? -1 : lua_toboolean(L, -1);
lua_pop(L, 1);
return res;
}
/* maximum value for date fields (to avoid arithmetic overflows with 'int') */
#if !defined(L_MAXDATEFIELD)
#define L_MAXDATEFIELD (INT_MAX / 2)
#endif
static int getfield (lua_State *L, const char *key, int d, int delta) {
int isnum;
int t = lua_getfield(L, -1, key); /* get field and its type */
lua_Integer res = lua_tointegerx(L, -1, &isnum);
if (!isnum) { /* field is not an integer? */
if (t != LUA_TNIL) /* some other value? */
return luaL_error(L, "field '%s' is not an integer", key);
else if (d < 0) /* absent field; no default? */
return luaL_error(L, "field '%s' missing in date table", key);
res = d;
}
else {
if (!(-L_MAXDATEFIELD <= res && res <= L_MAXDATEFIELD))
return luaL_error(L, "field '%s' is out-of-bound", key);
res -= delta;
}
lua_pop(L, 1);
return (int)res;
}
static const char *checkoption (lua_State *L, const char *conv,
ptrdiff_t convlen, char *buff) {
const char *option = LUA_STRFTIMEOPTIONS;
int oplen = 1; /* length of options being checked */
for (; *option != '\0' && oplen <= convlen; option += oplen) {
if (*option == '|') /* next block? */
oplen++; /* will check options with next length (+1) */
else if (memcmp(conv, option, oplen) == 0) { /* match? */
memcpy(buff, conv, oplen); /* copy valid option to buffer */
buff[oplen] = '\0';
return conv + oplen; /* return next item */
}
}
luaL_argerror(L, 1,
lua_pushfstring(L, "invalid conversion specifier '%%%s'", conv));
return conv; /* to avoid warnings */
}
/* maximum size for an individual 'strftime' item */
#define SIZETIMEFMT 250
static int os_date (lua_State *L) {
size_t slen;
const char *s = luaL_optlstring(L, 1, "%c", &slen);
time_t t = luaL_opt(L, l_checktime, 2, time(NULL));
const char *se = s + slen; /* 's' end */
struct tm tmr, *stm;
if (*s == '!') { /* UTC? */
stm = l_gmtime(&t, &tmr);
s++; /* skip '!' */
}
else
stm = l_localtime(&t, &tmr);
if (stm == NULL) /* invalid date? */
return luaL_error(L,
"time result cannot be represented in this installation");
if (strcmp(s, "*t") == 0) {
lua_createtable(L, 0, 9); /* 9 = number of fields */
setallfields(L, stm);
}
else {
char cc[4]; /* buffer for individual conversion specifiers */
luaL_Buffer b;
cc[0] = '%';
luaL_buffinit(L, &b);
while (s < se) {
if (*s != '%') /* not a conversion specifier? */
luaL_addchar(&b, *s++);
else {
size_t reslen;
char *buff = luaL_prepbuffsize(&b, SIZETIMEFMT);
s++; /* skip '%' */
s = checkoption(L, s, se - s, cc + 1); /* copy specifier to 'cc' */
reslen = strftime(buff, SIZETIMEFMT, cc, stm);
luaL_addsize(&b, reslen);
}
}
luaL_pushresult(&b);
}
return 1;
}
static int os_time (lua_State *L) {
time_t t;
if (lua_isnoneornil(L, 1)) /* called without args? */
t = time(NULL); /* get current time */
else {
struct tm ts;
luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 1); /* make sure table is at the top */
ts.tm_sec = getfield(L, "sec", 0, 0);
ts.tm_min = getfield(L, "min", 0, 0);
ts.tm_hour = getfield(L, "hour", 12, 0);
ts.tm_mday = getfield(L, "day", -1, 0);
ts.tm_mon = getfield(L, "month", -1, 1);
ts.tm_year = getfield(L, "year", -1, 1900);
ts.tm_isdst = getboolfield(L, "isdst");
t = mktime(&ts);
setallfields(L, &ts); /* update fields with normalized values */
}
if (t != (time_t)(l_timet)t || t == (time_t)(-1))
return luaL_error(L,
"time result cannot be represented in this installation");
l_pushtime(L, t);
return 1;
}
static int os_difftime (lua_State *L) {
time_t t1 = l_checktime(L, 1);
time_t t2 = l_checktime(L, 2);
lua_pushnumber(L, (lua_Number)difftime(t1, t2));
return 1;
}
/* }====================================================== */
static int os_setlocale (lua_State *L) {
static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY,
LC_NUMERIC, LC_TIME};
static const char *const catnames[] = {"all", "collate", "ctype", "monetary",
"numeric", "time", NULL};
const char *l = luaL_optstring(L, 1, NULL);
int op = luaL_checkoption(L, 2, "all", catnames);
lua_pushstring(L, setlocale(cat[op], l));
return 1;
}
static int os_exit (lua_State *L) {
int status;
if (lua_isboolean(L, 1))
status = (lua_toboolean(L, 1) ? EXIT_SUCCESS : EXIT_FAILURE);
else
status = (int)luaL_optinteger(L, 1, EXIT_SUCCESS);
if (lua_toboolean(L, 2))
lua_close(L);
if (L) exit(status); /* 'if' to avoid warnings for unreachable 'return' */
return 0;
}
static const luaL_Reg syslib[] = {
{"clock", os_clock},
{"date", os_date},
{"difftime", os_difftime},
{"execute", os_execute},
{"exit", os_exit},
{"getenv", os_getenv},
{"remove", os_remove},
{"rename", os_rename},
{"setlocale", os_setlocale},
{"time", os_time},
{"tmpname", os_tmpname},
{NULL, NULL}
};
/* }====================================================== */
LUAMOD_API int luaopen_os (lua_State *L) {
luaL_newlib(L, syslib);
return 1;
}

1597
app/lua53/host/ltests.c Normal file

File diff suppressed because it is too large Load Diff

131
app/lua53/host/ltests.h Normal file
View File

@ -0,0 +1,131 @@
/*
** $Id: ltests.h,v 2.50 2016/07/19 17:13:00 roberto Exp $
** Internal Header for Debugging of the Lua Implementation
** See Copyright Notice in lua.h
*/
#ifndef ltests_h
#define ltests_h
#include <stdlib.h>
#if 0 /* test Lua with compatibility code */
#undef LUA_COMPAT_MATHLIB
#undef LUA_COMPAT_IPAIRS
#undef LUA_COMPAT_BITLIB
#undef LUA_COMPAT_APIINTCASTS
#undef LUA_COMPAT_FLOATSTRING
#undef LUA_COMPAT_UNPACK
#undef LUA_COMPAT_LOADERS
#undef LUA_COMPAT_LOG10
#undef LUA_COMPAT_LOADSTRING
#undef LUA_COMPAT_MAXN
#undef LUA_COMPAT_MODULE
#endif
#define LUA_DEBUG
/* turn on assertions */
#undef NDEBUG
#include <assert.h>
#ifndef lua_assert
#define lua_assert(c) assert(c)
#endif
/* to avoid warnings, and to make sure value is really unused */
#define UNUSED(x) (x=0, (void)(x))
/* test for sizes in 'l_sprintf' (make sure whole buffer is available) */
#undef l_sprintf
#if !defined(LUA_USE_C89)
#define l_sprintf(s,sz,f,i) (memset(s,0xAB,sz), snprintf(s,sz,f,i))
#else
#define l_sprintf(s,sz,f,i) (memset(s,0xAB,sz), sprintf(s,f,i))
#endif
/* memory-allocator control variables */
typedef struct Memcontrol {
unsigned long numblocks;
unsigned long total;
unsigned long maxmem;
unsigned long memlimit;
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
*/
int lua_checkmemory (lua_State *L);
/* test for lock/unlock */
struct L_EXTRA { int lock; int *plock; };
#undef LUA_EXTRASPACE
#define LUA_EXTRASPACE sizeof(struct L_EXTRA)
#define getlock(l) cast(struct L_EXTRA*, lua_getextraspace(l))
#define luai_userstateopen(l) \
(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))
#define luai_userstatethread(l,l1) \
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_API int luaB_opentests (lua_State *L);
LUA_API void *debug_realloc (void *ud, void *block,
size_t osize, size_t nsize);
#if defined(luac_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 */
#undef LUAL_BUFFERSIZE
#define LUAL_BUFFERSIZE 23
#define MINSTRTABSIZE 2
#define MAXINDEXRK 1
/* make stack-overflow tests run faster */
#undef LUAI_MAXSTACK
#define LUAI_MAXSTACK 50000
#undef LUAI_USER_ALIGNMENT_T
#define LUAI_USER_ALIGNMENT_T union { char b[sizeof(void*) * 8]; }
#define STRCACHE_N 16
#define STRCACHE_M 5
#endif

614
app/lua53/host/luac.c Normal file
View File

@ -0,0 +1,614 @@
/*
** $Id: luac.c,v 1.76 2018/06/19 01:32:02 lhf Exp $
** Lua compiler (saves bytecodes to files; also lists bytecodes)
** See Copyright Notice in lua.h
*/
#define luac_c
#define LUA_CORE
#include "lprefix.h"
#include <alloca.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include "ldebug.h"
#include "lnodemcu.h"
#include "lobject.h"
#include "lstate.h"
#include "lstring.h"
#include "lundump.h"
static void PrintFunction(const Proto* f, int full);
#define luaU_print PrintFunction
#define PROGNAME "luac.cross" /* default program name */
#define OUTPUT PROGNAME ".out" /* default output file */
static int listing=0; /* list bytecodes? */
static int dumping=1; /* dump bytecodes? */
static int stripping=0; /* strip debug information? */
static char Output[]={ OUTPUT }; /* default output file name */
static const char* output=Output; /* actual output file name */
static const char* progname=PROGNAME; /* actual program name */
static int flash = 0; /* output flash image */
static lu_int32 address = 0; /* output flash image at absolute location */
static lu_int32 maxSize = 0x40000; /* maximuum uncompressed image size */
static int lookup = 0; /* output lookup-style master combination header */
static const char *execute; /* executed a Lua file */
char *LFSimageName;
#define IROM0_SEG 0x40200000ul
#define IROM0_SEGMAX 0x00100000ul
#define IROM_OFFSET(a) (cast(lu_int32, (a)) - IROM0_SEG)
static void fatal(const char *message) {
fprintf(stderr, "%s: %s\n", progname, message);
exit(EXIT_FAILURE);
}
static void cannot(const char *what) {
fprintf(stderr, "%s: cannot %s %s: %s\n", progname, what, output, strerror(errno));
exit(EXIT_FAILURE);
}
static void usage(const char *message) {
if ( *message == '-')
fprintf(stderr, "%s: unrecognized option '%s'\n", progname, message);
else
fprintf(stderr, "%s: %s\n", progname, message);
fprintf(stderr,
"usage: %s [options] [filenames]\n"
"Available options are:\n"
" -l list (use -l -l for full listing)\n"
" -o name output to file 'name' (default is \"%s\")\n"
" -e name execute a lua source file\n"
" -f output a flash image file\n"
" -F name load a flash image file\n"
" -a addr generate an absolute, rather than "
"position independent flash image file\n"
" (use with -F LFSimage -o absLFSimage to "
"convert an image to absolute format)\n"
" -i generate lookup combination master (default with option -f)\n"
" -m size maximum LFS image in bytes\n"
" -p parse only\n"
" -s strip debug information\n"
" -v show version information\n"
" -- stop handling options\n"
" - stop handling options and process stdin\n", progname, Output);
exit(EXIT_FAILURE);
}
#define IS(s) (strcmp(argv[i],s)==0)
static int doargs(int argc, char *argv[]) {
int i;
int version = 0;
lu_int32 offset = 0;
if (argv[0] != NULL && *argv[0] != 0) progname = argv[0];
for (i = 1; i < argc; i++) {
if ( *argv[i] != '-') { /* end of options; keep it */
break;
} else if (IS("--")) { /* end of options; skip it */
++i;
if (version) ++version;
break;
} else if (IS("-")) { /* end of options; use stdin */
break;
} else if (IS("-e")) { /* execute a lua source file file */
execute = argv[++i];
if (execute == NULL || *execute == 0 || *execute == '-')
usage("\"-e\" needs a file argument");
} else if (IS("-F")) { /* execute a lua source file file */
LFSimageName = argv[++i];
if (LFSimageName == NULL || *LFSimageName == 0 || *LFSimageName == '-')
usage("\"-F\" needs an LFS image file argument");
} else if (IS("-f")) { /* Flash image file */
flash = lookup = 1;
} else if (IS("-a")) { /* Absolue flash image file */
flash = lookup = 1;
address = strtol(argv[++i], NULL, 0);
offset = IROM_OFFSET(address);
if (offset == 0 || offset > IROM0_SEGMAX)
usage("\"-a\" absolute address must be valid flash address");
} else if (IS("-i")) { /* lookup */
lookup = 1;
} else if (IS("-l")) { /* list */
++listing;
} else if (IS("-m")) { /* specify a maximum image size */
flash = lookup = 1;
maxSize = strtol(argv[++i], NULL, 0);
if (maxSize & 0xFFF)
usage("\"-e\" maximum size must be a multiple of 4,096");
} else if (IS("-o")) { /* output file */
output = argv[++i];
if (output == NULL || *output == 0 || ( *output == '-' && output[1] != 0))
usage("'-o' needs argument");
if (IS("-")) output = NULL;
} else if (IS("-p")) { /* parse only */
dumping = 0;
} else if (IS("-s")) { /* strip debug information */
stripping = 1;
} else if (IS("-v")) { /* show version */
++version;
} else { /* unknown option */
usage(argv[i]);
}
}
if (offset>0 && (output == NULL || LFSimageName == NULL ||
execute != NULL || i != argc))
usage("'-a' also requires '-o' and '-f' options without lua source files");
if (i == argc && (listing || !dumping)) {
dumping = 0;
argv[--i] = Output;
}
if (version) {
printf("%s\n", LUA_COPYRIGHT);
if (version == argc - 1) exit(EXIT_SUCCESS);
}
return i;
}
static const char *corename(lua_State *L, const TString *filename, int *len) {
const char *fn = getstr(filename) + 1;
const char *s = strrchr(fn, '/');
if (!s) s = strrchr(fn, '\\');
s = s ? s + 1 : fn;
while ( *s == '.') s++;
const char *e = strchr(s, '.');
if (len)
*len = e ? e - s : strlen(s);
return s;
}
/*
** If the luac command line includes multiple files or has the -f option
** then luac generates a main function to reference all sub-main prototypes.
** This is one of two types:
** Type 0 The standard luac combination main
** Type 1 A lookup wrapper that is used for LFS image dumps
*/
#define toproto(L, i) getproto(L->top + (i))
static const Proto *combine(lua_State *L, int n, int type) {
if (n == 1 && type == 0) {
return toproto(L, -1);
} else {
Proto *f;
int i, j;
/*
* Generate a minimal proto with 1 return, emtpy p, k & uv vectors
*/
if (luaL_loadbuffer(L, "\n", strlen("\n"), "=("PROGNAME ")") != LUA_OK)
fatal(lua_tostring(L, -1));
f = toproto(L, -1);
/*
* Allocate the vector for and bind the sub-protos
*/
luaM_reallocvector(L, f->p, f->sizep, n, Proto *);
f->sizep = n;
for (i = 0; i < n; i++) {
f->p[i] = toproto(L, i - n - 1);
if (f->p[i]->sizeupvalues > 0)
f->p[i]->upvalues[0].instack = 0;
}
f->numparams = 0;
f->maxstacksize = 1;
if (type == 1) {
/*
* For Type 1 main(), add a k vector of strings naming the corresponding
* protos with the Unixtime of the compile appended.
*/
luaM_reallocvector(L, f->k, f->sizek, n+1, TValue);
f->sizek = n + 1;
for (i = 0; i < n; i++) {
int len;
const char *name = corename(L, f->p[i]->source, &len);
TString* sname = luaS_newlstr(L, name, len);
for (j = 0; j < i; j++) {
if (tsvalue(f->k+j) == sname)
fatal(lua_pushfstring(L, "Cannot have duplicate files ('%s') in LFS", name));
}
setsvalue2n(L, f->k+i, sname);
}
setivalue(f->k+n, (lua_Integer) time(NULL));
}
return f;
}
}
static int writer(lua_State *L, const void *p, size_t size, void *u) {
UNUSED(L);
return (fwrite(p, size, 1, ((FILE **)u)[0]) != 1) && (size != 0);
}
static int msghandler (lua_State *L) {
const char *msg = lua_tostring(L, 1);
if (msg == NULL) /* is error object not a string? */
msg = lua_pushfstring(L, "(error object is a %s value)", luaL_typename(L, 1));
luaL_traceback(L, L, msg, 1); /* append a standard traceback */
return 1; /* return the traceback */
}
static int dofile (lua_State *L, const char *name) {
int status = luaL_loadfile(L, name);
if (status == LUA_OK) {
int base = lua_gettop(L);
lua_pushcfunction(L, msghandler); /* push message handler */
lua_insert(L, base); /* put it under function and args */
status = lua_pcall(L, 0, 0, base);
lua_remove(L, base); /* remove message handler from the stack */
}
if (status != LUA_OK) {
fprintf(stderr, "%s: %s\n", PROGNAME, lua_tostring(L, -1));
lua_pop(L, 1); /* remove message */
}
return status;
}
/*
** This function is an inintended consequence of constraints in ltable.c
** rotable_findentry(). The file list generates a ROTable in LFS and the
** rule for ROTables is that metavalue entries must be at the head of the
** ROTableentry list so argv file names with basenames starting with "__"
** must be head of the list. This is a botch. Sorry.
*/
static void reorderfiles(lua_State *L, int argc, char **list, char **argv) {
int i, j;
for (i = 0; i < argc; i++ ) {
TString *file = luaS_new(L,argv[i]);
if (strcmp("__", corename(L, file, NULL))) {
list[i] = argv[i]; /* add to the end of the new list */
} else {
for (j = 0; j < i; j++)
list[j+1] = list[j];
list[0] = argv[i]; /* add to the start of the new list */
}
}
}
static int pmain(lua_State *L) {
int argc = (int) lua_tointeger(L, 1);
char **argv = (char **) lua_touserdata(L, 2);
char **filelist = alloca(argc * sizeof(char *));
const Proto *f;
int i, status;
if (!lua_checkstack(L, argc + 1))
fatal("too many input files");
if (execute || address) {
luaL_openlibs(L); /* the nodemcu open will throw to signal an LFS reload */
status = dofile(L, execute);
if (status != LUA_OK)
return 0;
}
if (argc == 0)
return 0;
reorderfiles(L, argc, filelist, argv);
for (i = 0; i < argc; i++) {
const char *filename = IS("-") ? NULL : filelist[i];
if (luaL_loadfile(L, filename) != LUA_OK)
fatal(lua_tostring(L, -1));
//TODO: if strip = 2, replace proto->source by basename
}
f = combine(L, argc + (execute ? 1 : 0), lookup);
if (listing) luaU_print(f, listing > 1);
if (dumping) {
int result;
FILE *D = (output == NULL) ? stdout : fopen(output, "wb");
if (D == NULL) cannot("open");
lua_lock(L);
if (flash) {
UNUSED(address);
UNUSED(maxSize);
result = luaU_DumpAllProtos(L, f, writer, &D, stripping);
} else {
result = luaU_dump(L, f, writer, cast(void *, &D), stripping);
}
lua_unlock(L);
if (result == LUA_ERR_CC_INTOVERFLOW)
fatal("value too big or small for target integer type");
if (result == LUA_ERR_CC_NOTINTEGER)
fatal("target lua_Number is integral but fractional value found");
if (ferror(D)) cannot("write");
if (fclose(D)) cannot("close");
}
return 0;
}
int main(int argc, char *argv[]) {
lua_State *L;
int i = doargs(argc, argv);
int j, status;
argc -= i; argv += i;
if (argc <= 0 && execute == 0 && address == 0) usage("no input files given");
if (address)
luaN_setabsolute(address);
for (j = 0; j < 2 ; j++) {
L = luaL_newstate();
if (L == NULL) fatal("not enough memory for state");
lua_pushcfunction(L, &pmain);
lua_pushinteger(L, argc);
lua_pushlightuserdata(L, argv);
status = lua_pcall(L, 2, 0, 0);
if (status != LUA_OK) {
if (lua_isboolean(L,-1) && lua_toboolean(L,-1)) {
/*An LFS image has been loaded */
if (address) { /* write out as absolute image and exit */
lu_int32 size = cast(LFSHeader *, LFSregion)->flash_size;
FILE *af = fopen(output, "wb");
if (af == NULL) cannot("open");
if (fwrite(LFSregion, size, 1, af) != 1) cannot("write");
fclose(af);
exit(0);
}
/*otherwise simulate a restart */
lua_close(L);
continue; /* and loop around once more simulating restart */
}
char *err = strdup(lua_tostring(L, -1));
lua_close(L);
fatal(err);
}
lua_close(L);
break;
}
return EXIT_SUCCESS;
}
/*
** $Id: luac.c,v 1.76 2018/06/19 01:32:02 lhf Exp $
** print bytecodes
** See Copyright Notice in lua.h
*/
#include <ctype.h>
#include <stdio.h>
#define luac_c
#define LUA_CORE
#include "ldebug.h"
#include "lobject.h"
#include "lopcodes.h"
#define VOID(p) ((const void*)(p))
static void PrintString(const TString* ts)
{
const char* s=getstr(ts);
size_t i,n=tsslen(ts);
printf("%c",'"');
for (i=0; i<n; i++)
{
int c=(int)(unsigned char)s[i];
switch (c)
{
case '"': printf("\\\""); break;
case '\\': printf("\\\\"); break;
case '\a': printf("\\a"); break;
case '\b': printf("\\b"); break;
case '\f': printf("\\f"); break;
case '\n': printf("\\n"); break;
case '\r': printf("\\r"); break;
case '\t': printf("\\t"); break;
case '\v': printf("\\v"); break;
default: if (isprint(c))
printf("%c",c);
else
printf("\\%03d",c);
}
}
printf("%c",'"');
}
static void PrintConstant(const Proto* f, int i)
{
const TValue* o=&f->k[i];
switch (ttype(o))
{
case LUA_TNIL:
printf("nil");
break;
case LUA_TBOOLEAN:
printf(bvalue(o) ? "true" : "false");
break;
case LUA_TNUMFLT:
{
char buff[100];
sprintf(buff,LUA_NUMBER_FMT,fltvalue(o));
printf("%s",buff);
if (buff[strspn(buff,"-0123456789")]=='\0') printf(".0");
break;
}
case LUA_TNUMINT:
printf(LUA_INTEGER_FMT,ivalue(o));
break;
case LUA_TSHRSTR: case LUA_TLNGSTR:
PrintString(tsvalue(o));
break;
default: /* cannot happen */
printf("? type=%d",ttype(o));
break;
}
}
#define UPVALNAME(x) ((f->upvalues[x].name) ? getstr(f->upvalues[x].name) : "-")
#define MYK(x) (-1-(x))
static void PrintCode(const Proto* f)
{
const Instruction* code=f->code;
int pc,n=f->sizecode;
for (pc=0; pc<n; pc++)
{
Instruction i=code[pc];
OpCode o=GET_OPCODE(i);
int a=GETARG_A(i);
int b=GETARG_B(i);
int c=GETARG_C(i);
int ax=GETARG_Ax(i);
int bx=GETARG_Bx(i);
int sbx=GETARG_sBx(i);
int line=getfuncline(f,pc);
printf("\t%d\t",pc+1);
if (line>0) printf("[%d]\t",line); else printf("[-]\t");
printf("%-9s\t",luaP_opnames[o]);
switch (getOpMode(o))
{
case iABC:
printf("%d",a);
if (getBMode(o)!=OpArgN) printf(" %d",ISK(b) ? (MYK(INDEXK(b))) : b);
if (getCMode(o)!=OpArgN) printf(" %d",ISK(c) ? (MYK(INDEXK(c))) : c);
break;
case iABx:
printf("%d",a);
if (getBMode(o)==OpArgK) printf(" %d",MYK(bx));
if (getBMode(o)==OpArgU) printf(" %d",bx);
break;
case iAsBx:
printf("%d %d",a,sbx);
break;
case iAx:
printf("%d",MYK(ax));
break;
}
switch (o)
{
case OP_LOADK:
printf("\t; "); PrintConstant(f,bx);
break;
case OP_GETUPVAL:
case OP_SETUPVAL:
printf("\t; %s",UPVALNAME(b));
break;
case OP_GETTABUP:
printf("\t; %s",UPVALNAME(b));
if (ISK(c)) { printf(" "); PrintConstant(f,INDEXK(c)); }
break;
case OP_SETTABUP:
printf("\t; %s",UPVALNAME(a));
if (ISK(b)) { printf(" "); PrintConstant(f,INDEXK(b)); }
if (ISK(c)) { printf(" "); PrintConstant(f,INDEXK(c)); }
break;
case OP_GETTABLE:
case OP_SELF:
if (ISK(c)) { printf("\t; "); PrintConstant(f,INDEXK(c)); }
break;
case OP_SETTABLE:
case OP_ADD:
case OP_SUB:
case OP_MUL:
case OP_MOD:
case OP_POW:
case OP_DIV:
case OP_IDIV:
case OP_BAND:
case OP_BOR:
case OP_BXOR:
case OP_SHL:
case OP_SHR:
case OP_EQ:
case OP_LT:
case OP_LE:
if (ISK(b) || ISK(c))
{
printf("\t; ");
if (ISK(b)) PrintConstant(f,INDEXK(b)); else printf("-");
printf(" ");
if (ISK(c)) PrintConstant(f,INDEXK(c)); else printf("-");
}
break;
case OP_JMP:
case OP_FORLOOP:
case OP_FORPREP:
case OP_TFORLOOP:
printf("\t; to %d",sbx+pc+2);
break;
case OP_CLOSURE:
printf("\t; %p",VOID(f->p[bx]));
break;
case OP_SETLIST:
if (c==0) printf("\t; %d",(int)code[++pc]); else printf("\t; %d",c);
break;
case OP_EXTRAARG:
printf("\t; "); PrintConstant(f,ax);
break;
default:
break;
}
printf("\n");
}
}
#define SS(x) ((x==1)?"":"s")
#define S(x) (int)(x),SS(x)
static void PrintHeader(const Proto* f)
{
const char* s=f->source ? getstr(f->source) : "=?";
if (*s=='@' || *s=='=')
s++;
else if (*s==LUA_SIGNATURE[0])
s="(bstring)";
else
s="(string)";
printf("\n%s <%s:%d,%d> (%d instruction%s at %p)\n",
(f->linedefined==0)?"main":"function",s,
f->linedefined,f->lastlinedefined,
S(f->sizecode),VOID(f));
printf("%d%s param%s, %d slot%s, %d upvalue%s, ",
(int)(f->numparams),f->is_vararg?"+":"",SS(f->numparams),
S(f->maxstacksize),S(f->sizeupvalues));
printf("%d local%s, %d constant%s, %d function%s\n",
S(f->sizelocvars),S(f->sizek),S(f->sizep));
}
static void PrintDebug(const Proto* f)
{
int i,n;
n=f->sizek;
printf("constants (%d) for %p:\n",n,VOID(f));
for (i=0; i<n; i++)
{
printf("\t%d\t",i+1);
PrintConstant(f,i);
printf("\n");
}
n=f->sizelocvars;
printf("locals (%d) for %p:\n",n,VOID(f));
for (i=0; i<n; i++)
{
printf("\t%d\t%s\t%d\t%d\n",
i,getstr(f->locvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1);
}
n=f->sizeupvalues;
printf("upvalues (%d) for %p:\n",n,VOID(f));
for (i=0; i<n; i++)
{
printf("\t%d\t%s\t%d\t%d\n",
i,UPVALNAME(i),f->upvalues[i].instack,f->upvalues[i].idx);
}
}
static void PrintFunction(const Proto* f, int full)
{
int i,n=f->sizep;
PrintHeader(f);
PrintCode(f);
if (full) PrintDebug(f);
for (i=0; i<n; i++) PrintFunction(f->p[i],full);
}

291
app/lua53/host/tests/all.lua Executable file
View File

@ -0,0 +1,291 @@
#!../lua
-- $Id: all.lua,v 1.95 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice at the end of this file
local version = "Lua 5.3"
if _VERSION ~= version then
io.stderr:write("\nThis test suite is for ", version, ", not for ", _VERSION,
"\nExiting tests\n")
return
end
_G._ARG = arg -- save arg for other tests
-- next variables control the execution of some tests
-- true means no test (so an undefined variable does not skip a test)
-- defaults are for Linux; test everything.
-- Make true to avoid long or memory consuming tests
_soft = rawget(_G, "_soft") or false
-- Make true to avoid non-portable tests
_port = rawget(_G, "_port") or false
-- Make true to avoid messages about tests not performed
_nomsg = rawget(_G, "_nomsg") or false
local usertests = rawget(_G, "_U")
if usertests then
-- tests for sissies ;) Avoid problems
_soft = true
_port = true
_nomsg = true
end
-- tests should require debug when needed
debug = nil
if usertests then
T = nil -- no "internal" tests for user tests
else
T = rawget(_G, "T") -- avoid problems with 'strict' module
end
math.randomseed(0)
--[=[
example of a long [comment],
[[spanning several [lines]]]
]=]
print("current path:\n****" .. package.path .. "****\n")
local initclock = os.clock()
local lastclock = initclock
local walltime = os.time()
local collectgarbage = collectgarbage
do -- (
-- track messages for tests not performed
local msgs = {}
function Message (m)
if not _nomsg then
print(m)
msgs[#msgs+1] = string.sub(m, 3, -3)
end
end
assert(os.setlocale"C")
local T,print,format,write,assert,type,unpack,floor =
T,print,string.format,io.write,assert,type,table.unpack,math.floor
-- use K for 1000 and M for 1000000 (not 2^10 -- 2^20)
local function F (m)
local function round (m)
m = m + 0.04999
return format("%.1f", m) -- keep one decimal digit
end
if m < 1000 then return m
else
m = m / 1000
if m < 1000 then return round(m).."K"
else
return round(m/1000).."M"
end
end
end
local showmem
if not T then
local max = 0
showmem = function ()
local m = collectgarbage("count") * 1024
max = (m > max) and m or max
print(format(" ---- total memory: %s, max memory: %s ----\n",
F(m), F(max)))
end
else
showmem = function ()
T.checkmemory()
local total, numblocks, maxmem = T.totalmem()
local count = collectgarbage("count")
print(format(
"\n ---- total memory: %s (%.0fK), max use: %s, blocks: %d\n",
F(total), count, F(maxmem), numblocks))
print(format("\t(strings: %d, tables: %d, functions: %d, "..
"\n\tudata: %d, threads: %d)",
T.totalmem"string", T.totalmem"table", T.totalmem"function",
T.totalmem"userdata", T.totalmem"thread"))
end
end
--
-- redefine dofile to run files through dump/undump
--
local function report (n) print("\n***** FILE '"..n.."'*****") end
local olddofile = dofile
local dofile = function (n, strip)
showmem()
local c = os.clock()
print(string.format("time: %g (+%g)", c - initclock, c - lastclock))
lastclock = c
report(n)
local f = assert(loadfile(n))
local b = string.dump(f, strip)
f = assert(load(b))
return f()
end
dofile('main.lua')
do
local next, setmetatable, stderr = next, setmetatable, io.stderr
-- track collections
local mt = {}
-- each time a table is collected, remark it for finalization
-- on next cycle
mt.__gc = function (o)
stderr:write'.' -- mark progress
local n = setmetatable(o, mt) -- remark it
end
local n = setmetatable({}, mt) -- create object
end
report"gc.lua"
local f = assert(loadfile('gc.lua'))
f=nil -- NodeMCU removed f()
dofile('db.lua')
assert(dofile('calls.lua') == deep and deep)
olddofile('strings.lua')
olddofile('literals.lua')
dofile('tpack.lua')
assert(dofile('attrib.lua') == 27)
assert(dofile('locals.lua') == 5)
dofile('constructs.lua')
dofile('code.lua', true)
if not _G._soft then
report('big.lua')
local f = coroutine.wrap(assert(loadfile('big.lua')))
assert(f() == 'b')
assert(f() == 'a')
end
dofile('nextvar.lua')
dofile('pm.lua')
dofile('utf8.lua')
dofile('api.lua')
assert(dofile('events.lua') == 12)
dofile('vararg.lua')
dofile('closure.lua')
dofile('coroutine.lua')
dofile('goto.lua', true)
dofile('errors.lua')
dofile('math.lua')
dofile('sort.lua', true)
dofile('bitwise.lua')
assert(dofile('verybig.lua', true) == 10); collectgarbage()
dofile('files.lua')
if #msgs > 0 then
print("\ntests not performed:")
for i=1,#msgs do
print(msgs[i])
end
print()
end
-- no test module should define 'debug'
-- assert(debug == nil) -- NodeMCU. debug is always defined in ROM
local debug = require "debug"
print(string.format("%d-bit integers, %d-bit floats",
string.packsize("j") * 8, string.packsize("n") * 8))
debug.sethook(function (a) assert(type(a) == 'string') end, "cr")
-- to survive outside block
_G.showmem = showmem
end --)
local _G, showmem, print, format, clock, time, difftime, assert, open =
_G, showmem, print, string.format, os.clock, os.time, os.difftime,
assert, io.open
-- file with time of last performed test
local fname = T and "time-debug.txt" or "time.txt"
local lasttime
if not usertests then
-- open file with time of last performed test
local f = io.open(fname)
if f then
lasttime = assert(tonumber(f:read'a'))
f:close();
else -- no such file; assume it is recording time for first time
lasttime = nil
end
end
-- erase (almost) all globals
print('cleaning all!!!!')
for n in pairs(_G) do
if not ({___Glob = 1, tostring = 1})[n] then
_G[n] = nil
end
end
collectgarbage()
collectgarbage()
collectgarbage()
collectgarbage()
collectgarbage()
collectgarbage();showmem()
local clocktime = clock() - initclock
walltime = difftime(time(), walltime)
print(format("\n\ntotal time: %.2fs (wall time: %gs)\n", clocktime, walltime))
if not usertests then
lasttime = lasttime or clocktime -- if no last time, ignore difference
-- check whether current test time differs more than 5% from last time
local diff = (clocktime - lasttime) / lasttime
local tolerance = 0.05 -- 5%
if (diff >= tolerance or diff <= -tolerance) then
print(format("WARNING: time difference from previous test: %+.1f%%",
diff * 100))
end
assert(open(fname, "w")):write(clocktime):close()
end
print("final OK !!!")
--[[
*****************************************************************************
* Copyright (C) 1994-2016 Lua.org, PUC-Rio.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*****************************************************************************
]]

1173
app/lua53/host/tests/api.lua Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,476 @@
-- $Id: attrib.lua,v 1.65 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
print "testing require"
assert(require"string" == string)
assert(require"math" == math)
assert(require"table" == table)
assert(require"io" == io)
assert(require"os" == os)
assert(require"coroutine" == coroutine)
assert(type(package.path) == "string")
--[[NodeMCU doesn't support dynamic C loading
assert(type(package.cpath) == "string")
]]
assert(type(package.loaded) == "table")
assert(type(package.preload) == "table")
assert(type(package.config) == "string")
print("package config: "..string.gsub(package.config, "\n", "|"))
--[[TODO: NodeMCU doesn't support dynamic C loading
do
-- create a path with 'max' templates,
-- each with 1-10 repetitions of '?'
local max = _soft and 100 or 2000
local t = {}
for i = 1,max do t[i] = string.rep("?", i%10 + 1) end
t[#t + 1] = ";" -- empty template
local path = table.concat(t, ";")
-- use that path in a search
local s, err = package.searchpath("xuxu", path)
-- search fails; check that message has an occurence of
-- '??????????' with ? replaced by xuxu and at least 'max' lines
assert(not s and
string.find(err, string.rep("xuxu", 10)) and
#string.gsub(err, "[^\n]", "") >= max)
-- path with one very long template
local path = string.rep("?", max)
local s, err = package.searchpath("xuxu", path)
assert(not s and string.find(err, string.rep('xuxu', max)))
end
]]
do
local oldpath = package.path
package.path = {}
local s, err = pcall(require, "no-such-file")
assert(not s and string.find(err, "package.path"))
package.path = oldpath
end
print('+')
-- The next tests for 'require' assume some specific directories and
-- libraries.
--[=[TODO: NodeMCU doesn't support dynamic loading and rich FS. Might to use a subset here
if not _port then --[
local dirsep = string.match(package.config, "^([^\n]+)\n")
-- auxiliary directory with C modules and temporary files
local DIR = "libs" .. dirsep
-- prepend DIR to a name and correct directory separators
local function D (x)
x = string.gsub(x, "/", dirsep)
return DIR .. x
end
-- prepend DIR and pospend proper C lib. extension to a name
local function DC (x)
local ext = (dirsep == '\\') and ".dll" or ".so"
return D(x .. ext)
end
local function createfiles (files, preextras, posextras)
for n,c in pairs(files) do
io.output(D(n))
io.write(string.format(preextras, n))
io.write(c)
io.write(string.format(posextras, n))
io.close(io.output())
end
end
function removefiles (files)
for n in pairs(files) do
os.remove(D(n))
end
end
local files = {
["names.lua"] = "do return {...} end\n",
["err.lua"] = "B = 15; a = a + 1;",
["synerr.lua"] = "B =",
["A.lua"] = "",
["B.lua"] = "assert(...=='B');require 'A'",
["A.lc"] = "",
["A"] = "",
["L"] = "",
["XXxX"] = "",
["C.lua"] = "package.loaded[...] = 25; require'C'",
}
AA = nil
local extras = [[
NAME = '%s'
REQUIRED = ...
return AA]]
createfiles(files, "", extras)
-- testing explicit "dir" separator in 'searchpath'
assert(package.searchpath("C.lua", D"?", "", "") == D"C.lua")
assert(package.searchpath("C.lua", D"?", ".", ".") == D"C.lua")
assert(package.searchpath("--x-", D"?", "-", "X") == D"XXxX")
assert(package.searchpath("---xX", D"?", "---", "XX") == D"XXxX")
assert(package.searchpath(D"C.lua", "?", dirsep) == D"C.lua")
assert(package.searchpath(".\\C.lua", D"?", "\\") == D"./C.lua")
local oldpath = package.path
package.path = string.gsub("D/?.lua;D/?.lc;D/?;D/??x?;D/L", "D/", DIR)
local try = function (p, n, r)
NAME = nil
local rr = require(p)
assert(NAME == n)
assert(REQUIRED == p)
assert(rr == r)
end
a = require"names"
assert(a[1] == "names" and a[2] == D"names.lua")
_G.a = nil
local st, msg = pcall(require, "err")
assert(not st and string.find(msg, "arithmetic") and B == 15)
st, msg = pcall(require, "synerr")
assert(not st and string.find(msg, "error loading module"))
assert(package.searchpath("C", package.path) == D"C.lua")
assert(require"C" == 25)
assert(require"C" == 25)
AA = nil
try('B', 'B.lua', true)
assert(package.loaded.B)
assert(require"B" == true)
assert(package.loaded.A)
assert(require"C" == 25)
package.loaded.A = nil
try('B', nil, true) -- should not reload package
try('A', 'A.lua', true)
package.loaded.A = nil
os.remove(D'A.lua')
AA = {}
try('A', 'A.lc', AA) -- now must find second option
assert(package.searchpath("A", package.path) == D"A.lc")
assert(require("A") == AA)
AA = false
try('K', 'L', false) -- default option
try('K', 'L', false) -- default option (should reload it)
assert(rawget(_G, "_REQUIREDNAME") == nil)
AA = "x"
try("X", "XXxX", AA)
removefiles(files)
-- testing require of sub-packages
local _G = _G
package.path = string.gsub("D/?.lua;D/?/init.lua", "D/", DIR)
files = {
["P1/init.lua"] = "AA = 10",
["P1/xuxu.lua"] = "AA = 20",
}
createfiles(files, "_ENV = {}\n", "\nreturn _ENV\n")
AA = 0
local m = assert(require"P1")
assert(AA == 0 and m.AA == 10)
assert(require"P1" == m)
assert(require"P1" == m)
assert(package.searchpath("P1.xuxu", package.path) == D"P1/xuxu.lua")
m.xuxu = assert(require"P1.xuxu")
assert(AA == 0 and m.xuxu.AA == 20)
assert(require"P1.xuxu" == m.xuxu)
assert(require"P1.xuxu" == m.xuxu)
assert(require"P1" == m and m.AA == 10)
removefiles(files)
package.path = ""
assert(not pcall(require, "file_does_not_exist"))
package.path = "??\0?"
assert(not pcall(require, "file_does_not_exist1"))
package.path = oldpath
-- check 'require' error message
local fname = "file_does_not_exist2"
local m, err = pcall(require, fname)
for t in string.gmatch(package.path..";"..package.cpath, "[^;]+") do
t = string.gsub(t, "?", fname)
assert(string.find(err, t, 1, true))
end
do -- testing 'package.searchers' not being a table
local searchers = package.searchers
package.searchers = 3
local st, msg = pcall(require, 'a')
assert(not st and string.find(msg, "must be a table"))
package.searchers = searchers
end
local function import(...)
local f = {...}
return function (m)
for i=1, #f do m[f[i]] = _G[f[i]] end
end
end
-- cannot change environment of a C function
assert(not pcall(module, 'XUXU'))
-- testing require of C libraries
local p = "" -- On Mac OS X, redefine this to "_"
-- check whether loadlib works in this system
local st, err, when = package.loadlib(DC"lib1", "*")
if not st then
local f, err, when = package.loadlib("donotexist", p.."xuxu")
assert(not f and type(err) == "string" and when == "absent")
;(Message or print)('\n >>> cannot load dynamic library <<<\n')
print(err, when)
else
-- tests for loadlib
local f = assert(package.loadlib(DC"lib1", p.."onefunction"))
local a, b = f(15, 25)
assert(a == 25 and b == 15)
f = assert(package.loadlib(DC"lib1", p.."anotherfunc"))
assert(f(10, 20) == "10%20\n")
-- check error messages
local f, err, when = package.loadlib(DC"lib1", p.."xuxu")
assert(not f and type(err) == "string" and when == "init")
f, err, when = package.loadlib("donotexist", p.."xuxu")
assert(not f and type(err) == "string" and when == "open")
-- symbols from 'lib1' must be visible to other libraries
f = assert(package.loadlib(DC"lib11", p.."luaopen_lib11"))
assert(f() == "exported")
-- test C modules with prefixes in names
package.cpath = DC"?"
local lib2 = require"lib2-v2"
-- check correct access to global environment and correct
-- parameters
assert(_ENV.x == "lib2-v2" and _ENV.y == DC"lib2-v2")
assert(lib2.id("x") == "x")
-- test C submodules
local fs = require"lib1.sub"
assert(_ENV.x == "lib1.sub" and _ENV.y == DC"lib1")
assert(fs.id(45) == 45)
end
_ENV = _G
-- testing preload
do
local p = package
package = {}
p.preload.pl = function (...)
local _ENV = {...}
function xuxu (x) return x+20 end
return _ENV
end
local pl = require"pl"
assert(require"pl" == pl)
assert(pl.xuxu(10) == 30)
assert(pl[1] == "pl" and pl[2] == nil)
package = p
assert(type(package.path) == "string")
end
print('+')
end --]
--]=]
print("testing assignments, logical operators, and constructors")
local res, res2 = 27
a, b = 1, 2+3
assert(a==1 and b==5)
a={}
function f() return 10, 11, 12 end
a.x, b, a[1] = 1, 2, f()
assert(a.x==1 and b==2 and a[1]==10)
a[f()], b, a[f()+3] = f(), a, 'x'
assert(a[10] == 10 and b == a and a[13] == 'x')
do
local f = function (n) local x = {}; for i=1,n do x[i]=i end;
return table.unpack(x) end;
local a,b,c
a,b = 0, f(1)
assert(a == 0 and b == 1)
A,b = 0, f(1)
assert(A == 0 and b == 1)
a,b,c = 0,5,f(4)
assert(a==0 and b==5 and c==1)
a,b,c = 0,5,f(0)
assert(a==0 and b==5 and c==nil)
end
a, b, c, d = 1 and nil, 1 or nil, (1 and (nil or 1)), 6
assert(not a and b and c and d==6)
d = 20
a, b, c, d = f()
assert(a==10 and b==11 and c==12 and d==nil)
a,b = f(), 1, 2, 3, f()
assert(a==10 and b==1)
assert(a<b == false and a>b == true)
assert((10 and 2) == 2)
assert((10 or 2) == 10)
assert((10 or assert(nil)) == 10)
assert(not (nil and assert(nil)))
assert((nil or "alo") == "alo")
assert((nil and 10) == nil)
assert((false and 10) == false)
assert((true or 10) == true)
assert((false or 10) == 10)
assert(false ~= nil)
assert(nil ~= false)
assert(not nil == true)
assert(not not nil == false)
assert(not not 1 == true)
assert(not not a == true)
assert(not not (6 or nil) == true)
assert(not not (nil and 56) == false)
assert(not not (nil and true) == false)
assert(not 10 == false)
assert(not {} == false)
assert(not 0.5 == false)
assert(not "x" == false)
assert({} ~= {})
print('+')
a = {}
a[true] = 20
a[false] = 10
assert(a[1<2] == 20 and a[1>2] == 10)
function f(a) return a end
local a = {}
for i=3000,-3000,-1 do a[i + 0.0] = i; end
a[10e30] = "alo"; a[true] = 10; a[false] = 20
assert(a[10e30] == 'alo' and a[not 1] == 20 and a[10<20] == 10)
for i=3000,-3000,-1 do assert(a[i] == i); end
a[print] = assert
a[f] = print
a[a] = a
assert(a[a][a][a][a][print] == assert)
a[print](a[a[f]] == a[print])
assert(not pcall(function () local a = {}; a[nil] = 10 end))
assert(not pcall(function () local a = {[nil] = 10} end))
assert(a[nil] == nil)
a = nil
a = {10,9,8,7,6,5,4,3,2; [-3]='a', [f]=print, a='a', b='ab'}
a, a.x, a.y = a, a[-3]
assert(a[1]==10 and a[-3]==a.a and a[f]==print and a.x=='a' and not a.y)
a[1], f(a)[2], b, c = {['alo']=assert}, 10, a[1], a[f], 6, 10, 23, f(a), 2
a[1].alo(a[2]==10 and b==10 and c==print)
-- test of large float/integer indices
-- compute maximum integer where all bits fit in a float
local maxint = math.maxinteger
while maxint - 1.0 == maxint - 0.0 do -- trim (if needed) to fit in a float
maxint = maxint // 2
end
maxintF = maxint + 0.0 -- float version
assert(math.type(maxintF) == "float" and maxintF >= 2.0^14)
-- floats and integers must index the same places
a[maxintF] = 10; a[maxintF - 1.0] = 11;
a[-maxintF] = 12; a[-maxintF + 1.0] = 13;
assert(a[maxint] == 10 and a[maxint - 1] == 11 and
a[-maxint] == 12 and a[-maxint + 1] == 13)
a[maxint] = 20
a[-maxint] = 22
assert(a[maxintF] == 20 and a[maxintF - 1.0] == 11 and
a[-maxintF] == 22 and a[-maxintF + 1.0] == 13)
a = nil
-- test conflicts in multiple assignment
do
local a,i,j,b
a = {'a', 'b'}; i=1; j=2; b=a
i, a[i], a, j, a[j], a[i+j] = j, i, i, b, j, i
assert(i == 2 and b[1] == 1 and a == 1 and j == b and b[2] == 2 and
b[3] == 1)
end
-- repeat test with upvalues
do
local a,i,j,b
a = {'a', 'b'}; i=1; j=2; b=a
local function foo ()
i, a[i], a, j, a[j], a[i+j] = j, i, i, b, j, i
end
foo()
assert(i == 2 and b[1] == 1 and a == 1 and j == b and b[2] == 2 and
b[3] == 1)
local t = {}
(function (a) t[a], a = 10, 20 end)(1);
assert(t[1] == 10)
end
-- bug in 5.2 beta
local function foo ()
local a
return function ()
local b
a, b = 3, 14 -- local and upvalue have same index
return a, b
end
end
local a, b = foo()()
assert(a == 3 and b == 14)
print('OK')
return res

View File

@ -0,0 +1,83 @@
-- $Id: big.lua,v 1.32 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
if _soft then
return 'a'
end
print "testing large tables"
local debug = require"debug"
-- NodeMCU: limit big size to IoT scales
local lim = 50000
local prog = { "local y = {0" }
for i = 1, lim do prog[#prog + 1] = i end
prog[#prog + 1] = "}\n"
prog[#prog + 1] = "X = y\n"
prog[#prog + 1] = ("assert(X[%d] == %d)"):format(lim - 1, lim - 2)
prog[#prog + 1] = "return 0"
prog = table.concat(prog, ";")
local env = {string = string, assert = assert}
local f = assert(load(prog, nil, nil, env))
f()
assert(env.X[lim] == lim - 1 and env.X[lim + 1] == lim)
for k in pairs(env) do env[k] = nil end
-- yields during accesses larger than K (in RK)
setmetatable(env, {
__index = function (t, n) coroutine.yield('g'); return _G[n] end,
__newindex = function (t, n, v) coroutine.yield('s'); _G[n] = v end,
})
X = nil
co = coroutine.wrap(f)
assert(co() == 's')
assert(co() == 'g')
assert(co() == 'g')
assert(co() == 0)
assert(X[lim] == lim - 1 and X[lim + 1] == lim)
-- errors in accesses larger than K (in RK)
getmetatable(env).__index = function () end
getmetatable(env).__newindex = function () end
local e, m = pcall(f)
assert(not e and m:find("global 'X'"))
-- errors in metamethods
getmetatable(env).__newindex = function () error("hi") end
local e, m = xpcall(f, debug.traceback)
assert(not e and m:find("'__newindex'"))
f, X = nil
coroutine.yield'b'
if 2^32 == 0 then -- (small integers) {
print "testing string length overflow"
local repstrings = 192 -- number of strings to be concatenated
local ssize = math.ceil(2.0^32 / repstrings) + 1 -- size of each string
assert(repstrings * ssize > 2.0^32) -- it should be larger than maximum size
local longs = string.rep("\0", ssize) -- create one long string
-- create function to concatentate 'repstrings' copies of its argument
local rep = assert(load(
"local a = ...; return " .. string.rep("a", repstrings, "..")))
local a, b = pcall(rep, longs) -- call that function
-- it should fail without creating string (result would be too large)
assert(not a and string.find(b, "overflow"))
end -- }
print'OK'
return 'a'

328
app/lua53/host/tests/bitwise.lua Executable file
View File

@ -0,0 +1,328 @@
-- $Id: bitwise.lua,v 1.26 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
print("testing bitwise operations")
local numbits = string.packsize('j') * 8
assert(~0 == -1)
assert((1 << (numbits - 1)) == math.mininteger)
-- basic tests for bitwise operators;
-- use variables to avoid constant folding
local a, b, c, d
a = 0xFFFFFFFFFFFFFFFF
assert(a == -1 and a & -1 == a and a & 35 == 35)
a = 0xF0F0F0F0F0F0F0F0
assert(a | -1 == -1)
assert(a ~ a == 0 and a ~ 0 == a and a ~ ~a == -1)
assert(a >> 4 == ~a)
a = 0xF0; b = 0xCC; c = 0xAA; d = 0xFD
assert(a | b ~ c & d == 0xF4)
a = 0xF0.0; b = 0xCC.0; c = "0xAA.0"; d = "0xFD.0"
assert(a | b ~ c & d == 0xF4)
a = 0xF0000000; b = 0xCC000000;
c = 0xAA000000; d = 0xFD000000
assert(a | b ~ c & d == 0xF4000000)
assert(~~a == a and ~a == -1 ~ a and -d == ~d + 1)
a = a << 32
b = b << 32
c = c << 32
d = d << 32
assert(a | b ~ c & d == 0xF4000000 << 32)
assert(~~a == a and ~a == -1 ~ a and -d == ~d + 1)
assert(-1 >> 1 == (1 << (numbits - 1)) - 1 and 1 << 31 == 0x80000000)
assert(-1 >> (numbits - 1) == 1)
assert(-1 >> numbits == 0 and
-1 >> -numbits == 0 and
-1 << numbits == 0 and
-1 << -numbits == 0)
assert((2^30 - 1) << 2^30 == 0)
assert((2^30 - 1) >> 2^30 == 0)
assert(1 >> -3 == 1 << 3 and 1000 >> 5 == 1000 << -5)
-- coercion from strings to integers
assert("0xffffffffffffffff" | 0 == -1)
assert("0xfffffffffffffffe" & "-1" == -2)
assert(" \t-0xfffffffffffffffe\n\t" & "-1" == 2)
assert(" \n -45 \t " >> " -2 " == -45 * 4)
-- out of range number
assert(not pcall(function () return "0xffffffffffffffff.0" | 0 end))
-- embedded zeros
assert(not pcall(function () return "0xffffffffffffffff\0" | 0 end))
print'+'
package.preload.bit32 = function () --{
-- no built-in 'bit32' library: implement it using bitwise operators
local bit = {}
function bit.bnot (a)
return ~a & 0xFFFFFFFF
end
--
-- in all vararg functions, avoid creating 'arg' table when there are
-- only 2 (or less) parameters, as 2 parameters is the common case
--
function bit.band (x, y, z, ...)
if not z then
return ((x or -1) & (y or -1)) & 0xFFFFFFFF
else
local arg = {...}
local res = x & y & z
for i = 1, #arg do res = res & arg[i] end
return res & 0xFFFFFFFF
end
end
function bit.bor (x, y, z, ...)
if not z then
return ((x or 0) | (y or 0)) & 0xFFFFFFFF
else
local arg = {...}
local res = x | y | z
for i = 1, #arg do res = res | arg[i] end
return res & 0xFFFFFFFF
end
end
function bit.bxor (x, y, z, ...)
if not z then
return ((x or 0) ~ (y or 0)) & 0xFFFFFFFF
else
local arg = {...}
local res = x ~ y ~ z
for i = 1, #arg do res = res ~ arg[i] end
return res & 0xFFFFFFFF
end
end
function bit.btest (...)
return bit.band(...) ~= 0
end
function bit.lshift (a, b)
return ((a & 0xFFFFFFFF) << b) & 0xFFFFFFFF
end
function bit.rshift (a, b)
return ((a & 0xFFFFFFFF) >> b) & 0xFFFFFFFF
end
function bit.arshift (a, b)
a = a & 0xFFFFFFFF
if b <= 0 or (a & 0x80000000) == 0 then
return (a >> b) & 0xFFFFFFFF
else
return ((a >> b) | ~(0xFFFFFFFF >> b)) & 0xFFFFFFFF
end
end
function bit.lrotate (a ,b)
b = b & 31
a = a & 0xFFFFFFFF
a = (a << b) | (a >> (32 - b))
return a & 0xFFFFFFFF
end
function bit.rrotate (a, b)
return bit.lrotate(a, -b)
end
local function checkfield (f, w)
w = w or 1
assert(f >= 0, "field cannot be negative")
assert(w > 0, "width must be positive")
assert(f + w <= 32, "trying to access non-existent bits")
return f, ~(-1 << w)
end
function bit.extract (a, f, w)
local f, mask = checkfield(f, w)
return (a >> f) & mask
end
function bit.replace (a, v, f, w)
local f, mask = checkfield(f, w)
v = v & mask
a = (a & ~(mask << f)) | (v << f)
return a & 0xFFFFFFFF
end
return bit
end --}
print("testing bitwise library")
local bit32 = require'bit32'
assert(bit32.band() == bit32.bnot(0))
assert(bit32.btest() == true)
assert(bit32.bor() == 0)
assert(bit32.bxor() == 0)
assert(bit32.band() == bit32.band(0xffffffff))
assert(bit32.band(1,2) == 0)
-- out-of-range numbers
assert(bit32.band(-1) == 0xffffffff)
assert(bit32.band((1 << 33) - 1) == 0xffffffff)
assert(bit32.band(-(1 << 33) - 1) == 0xffffffff)
assert(bit32.band((1 << 33) + 1) == 1)
assert(bit32.band(-(1 << 33) + 1) == 1)
assert(bit32.band(-(1 << 40)) == 0)
assert(bit32.band(1 << 40) == 0)
assert(bit32.band(-(1 << 40) - 2) == 0xfffffffe)
assert(bit32.band((1 << 40) - 4) == 0xfffffffc)
assert(bit32.lrotate(0, -1) == 0)
assert(bit32.lrotate(0, 7) == 0)
assert(bit32.lrotate(0x12345678, 0) == 0x12345678)
assert(bit32.lrotate(0x12345678, 32) == 0x12345678)
assert(bit32.lrotate(0x12345678, 4) == 0x23456781)
assert(bit32.rrotate(0x12345678, -4) == 0x23456781)
assert(bit32.lrotate(0x12345678, -8) == 0x78123456)
assert(bit32.rrotate(0x12345678, 8) == 0x78123456)
assert(bit32.lrotate(0xaaaaaaaa, 2) == 0xaaaaaaaa)
assert(bit32.lrotate(0xaaaaaaaa, -2) == 0xaaaaaaaa)
for i = -50, 50 do
assert(bit32.lrotate(0x89abcdef, i) == bit32.lrotate(0x89abcdef, i%32))
end
assert(bit32.lshift(0x12345678, 4) == 0x23456780)
assert(bit32.lshift(0x12345678, 8) == 0x34567800)
assert(bit32.lshift(0x12345678, -4) == 0x01234567)
assert(bit32.lshift(0x12345678, -8) == 0x00123456)
assert(bit32.lshift(0x12345678, 32) == 0)
assert(bit32.lshift(0x12345678, -32) == 0)
assert(bit32.rshift(0x12345678, 4) == 0x01234567)
assert(bit32.rshift(0x12345678, 8) == 0x00123456)
assert(bit32.rshift(0x12345678, 32) == 0)
assert(bit32.rshift(0x12345678, -32) == 0)
assert(bit32.arshift(0x12345678, 0) == 0x12345678)
assert(bit32.arshift(0x12345678, 1) == 0x12345678 // 2)
assert(bit32.arshift(0x12345678, -1) == 0x12345678 * 2)
assert(bit32.arshift(-1, 1) == 0xffffffff)
assert(bit32.arshift(-1, 24) == 0xffffffff)
assert(bit32.arshift(-1, 32) == 0xffffffff)
assert(bit32.arshift(-1, -1) == bit32.band(-1 * 2, 0xffffffff))
assert(0x12345678 << 4 == 0x123456780)
assert(0x12345678 << 8 == 0x1234567800)
assert(0x12345678 << -4 == 0x01234567)
assert(0x12345678 << -8 == 0x00123456)
assert(0x12345678 << 32 == 0x1234567800000000)
assert(0x12345678 << -32 == 0)
assert(0x12345678 >> 4 == 0x01234567)
assert(0x12345678 >> 8 == 0x00123456)
assert(0x12345678 >> 32 == 0)
assert(0x12345678 >> -32 == 0x1234567800000000)
print("+")
-- some special cases
local c = {0, 1, 2, 3, 10, 0x80000000, 0xaaaaaaaa, 0x55555555,
0xffffffff, 0x7fffffff}
for _, b in pairs(c) do
assert(bit32.band(b) == b)
assert(bit32.band(b, b) == b)
assert(bit32.band(b, b, b, b) == b)
assert(bit32.btest(b, b) == (b ~= 0))
assert(bit32.band(b, b, b) == b)
assert(bit32.band(b, b, b, ~b) == 0)
assert(bit32.btest(b, b, b) == (b ~= 0))
assert(bit32.band(b, bit32.bnot(b)) == 0)
assert(bit32.bor(b, bit32.bnot(b)) == bit32.bnot(0))
assert(bit32.bor(b) == b)
assert(bit32.bor(b, b) == b)
assert(bit32.bor(b, b, b) == b)
assert(bit32.bor(b, b, 0, ~b) == 0xffffffff)
assert(bit32.bxor(b) == b)
assert(bit32.bxor(b, b) == 0)
assert(bit32.bxor(b, b, b) == b)
assert(bit32.bxor(b, b, b, b) == 0)
assert(bit32.bxor(b, 0) == b)
assert(bit32.bnot(b) ~= b)
assert(bit32.bnot(bit32.bnot(b)) == b)
assert(bit32.bnot(b) == (1 << 32) - 1 - b)
assert(bit32.lrotate(b, 32) == b)
assert(bit32.rrotate(b, 32) == b)
assert(bit32.lshift(bit32.lshift(b, -4), 4) == bit32.band(b, bit32.bnot(0xf)))
assert(bit32.rshift(bit32.rshift(b, 4), -4) == bit32.band(b, bit32.bnot(0xf)))
end
-- for this test, use at most 24 bits (mantissa of a single float)
c = {0, 1, 2, 3, 10, 0x800000, 0xaaaaaa, 0x555555, 0xffffff, 0x7fffff}
for _, b in pairs(c) do
for i = -40, 40 do
local x = bit32.lshift(b, i)
local y = math.floor(math.fmod(b * 2.0^i, 2.0^32))
assert(math.fmod(x - y, 2.0^32) == 0)
end
end
assert(not pcall(bit32.band, {}))
assert(not pcall(bit32.bnot, "a"))
assert(not pcall(bit32.lshift, 45))
assert(not pcall(bit32.lshift, 45, print))
assert(not pcall(bit32.rshift, 45, print))
print("+")
-- testing extract/replace
assert(bit32.extract(0x12345678, 0, 4) == 8)
assert(bit32.extract(0x12345678, 4, 4) == 7)
assert(bit32.extract(0xa0001111, 28, 4) == 0xa)
assert(bit32.extract(0xa0001111, 31, 1) == 1)
assert(bit32.extract(0x50000111, 31, 1) == 0)
assert(bit32.extract(0xf2345679, 0, 32) == 0xf2345679)
assert(not pcall(bit32.extract, 0, -1))
assert(not pcall(bit32.extract, 0, 32))
assert(not pcall(bit32.extract, 0, 0, 33))
assert(not pcall(bit32.extract, 0, 31, 2))
assert(bit32.replace(0x12345678, 5, 28, 4) == 0x52345678)
assert(bit32.replace(0x12345678, 0x87654321, 0, 32) == 0x87654321)
assert(bit32.replace(0, 1, 2) == 2^2)
assert(bit32.replace(0, -1, 4) == 2^4)
assert(bit32.replace(-1, 0, 31) == (1 << 31) - 1)
assert(bit32.replace(-1, 0, 1, 2) == (1 << 32) - 7)
-- testing conversion of floats
assert(bit32.bor(3.0) == 3)
assert(bit32.bor(-4.0) == 0xfffffffc)
-- large floats and large-enough integers?
if 2.0^50 < 2.0^50 + 1.0 and 2.0^50 < (-1 >> 1) then
assert(bit32.bor(2.0^32 - 5.0) == 0xfffffffb)
assert(bit32.bor(-2.0^32 - 6.0) == 0xfffffffa)
assert(bit32.bor(2.0^48 - 5.0) == 0xfffffffb)
assert(bit32.bor(-2.0^48 - 6.0) == 0xfffffffa)
end
print'OK'

View File

@ -0,0 +1,400 @@
-- $Id: calls.lua,v 1.60 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
print("testing functions and calls")
local debug = require "debug"
-- get the opportunity to test 'type' too ;)
assert(type(1<2) == 'boolean')
assert(type(true) == 'boolean' and type(false) == 'boolean')
assert(type(nil) == 'nil'
and type(-3) == 'number'
and type'x' == 'string'
and type{} == 'table'
and type(type) == 'function')
assert(type(assert) == type(print))
function f (x) return a:x (x) end
assert(type(f) == 'function')
assert(not pcall(type))
do -- test error in 'print' too...
-- NodeMCU setting tostring to nil does work with ROM searchlist use numeric override instead
_ENV.tostring = 1
local st, msg = pcall(print, 1)
assert(st == false and string.find(msg, "attempt to call a number value"))
_ENV.tostring = function () return {} end
local st, msg = pcall(print, 1)
assert(st == false and string.find(msg, "must return a string"))
_ENV.tostring = nil
end
-- testing local-function recursion
fact = false
do
local res = 1
local function fact (n)
if n==0 then return res
else return n*fact(n-1)
end
end
assert(fact(5) == 120)
end
assert(fact == false)
-- testing declarations
a = {i = 10}
self = 20
function a:x (x) return x+self.i end
function a.y (x) return x+self end
assert(a:x(1)+10 == a.y(1))
a.t = {i=-100}
a["t"].x = function (self, a,b) return self.i+a+b end
assert(a.t:x(2,3) == -95)
do
local a = {x=0}
function a:add (x) self.x, a.y = self.x+x, 20; return self end
assert(a:add(10):add(20):add(30).x == 60 and a.y == 20)
end
local a = {b={c={}}}
function a.b.c.f1 (x) return x+1 end
function a.b.c:f2 (x,y) self[x] = y end
assert(a.b.c.f1(4) == 5)
a.b.c:f2('k', 12); assert(a.b.c.k == 12)
print('+')
t = nil -- 'declare' t
function f(a,b,c) local d = 'a'; t={a,b,c,d} end
f( -- this line change must be valid
1,2)
assert(t[1] == 1 and t[2] == 2 and t[3] == nil and t[4] == 'a')
f(1,2, -- this one too
3,4)
assert(t[1] == 1 and t[2] == 2 and t[3] == 3 and t[4] == 'a')
function fat(x)
if x <= 1 then return 1
else return x*load("return fat(" .. x-1 .. ")", "")()
end
end
assert(load "load 'assert(fat(6)==720)' () ")()
a = load('return fat(5), 3')
a,b = a()
assert(a == 120 and b == 3)
print('+')
function err_on_n (n)
if n==0 then error(); exit(1);
else err_on_n (n-1); exit(1);
end
end
do
function dummy (n)
if n > 0 then
assert(not pcall(err_on_n, n))
dummy(n-1)
end
end
end
dummy(10)
function deep (n)
if n>0 then deep(n-1) end
end
deep(10)
deep(200)
-- testing tail call
function deep (n) if n>0 then return deep(n-1) else return 101 end end
assert(deep(30000) == 101)
a = {}
function a:deep (n) if n>0 then return self:deep(n-1) else return 101 end end
assert(a:deep(30000) == 101)
print('+')
a = nil
(function (x) a=x end)(23)
assert(a == 23 and (function (x) return x*2 end)(20) == 40)
-- testing closures
-- fixed-point operator
Z = function (le)
local function a (f)
return le(function (x) return f(f)(x) end)
end
return a(a)
end
-- non-recursive factorial
F = function (f)
return function (n)
if n == 0 then return 1
else return n*f(n-1) end
end
end
fat = Z(F)
assert(fat(0) == 1 and fat(4) == 24 and Z(F)(5)==5*Z(F)(4))
local function g (z)
local function f (a,b,c,d)
return function (x,y) return a+b+c+d+a+x+y+z end
end
return f(z,z+1,z+2,z+3)
end
f = g(10)
assert(f(9, 16) == 10+11+12+13+10+9+16+10)
Z, F, f = nil
print('+')
-- testing multiple returns
function unlpack (t, i)
i = i or 1
if (i <= #t) then
return t[i], unlpack(t, i+1)
end
end
function equaltab (t1, t2)
assert(#t1 == #t2)
for i = 1, #t1 do
assert(t1[i] == t2[i])
end
end
local pack = function (...) return (table.pack(...)) end
function f() return 1,2,30,4 end
function ret2 (a,b) return a,b end
local a,b,c,d = unlpack{1,2,3}
assert(a==1 and b==2 and c==3 and d==nil)
a = {1,2,3,4,false,10,'alo',false,assert}
equaltab(pack(unlpack(a)), a)
equaltab(pack(unlpack(a), -1), {1,-1})
a,b,c,d = ret2(f()), ret2(f())
assert(a==1 and b==1 and c==2 and d==nil)
a,b,c,d = unlpack(pack(ret2(f()), ret2(f())))
assert(a==1 and b==1 and c==2 and d==nil)
a,b,c,d = unlpack(pack(ret2(f()), (ret2(f()))))
assert(a==1 and b==1 and c==nil and d==nil)
a = ret2{ unlpack{1,2,3}, unlpack{3,2,1}, unlpack{"a", "b"}}
assert(a[1] == 1 and a[2] == 3 and a[3] == "a" and a[4] == "b")
-- testing calls with 'incorrect' arguments
rawget({}, "x", 1)
rawset({}, "x", 1, 2)
assert(math.sin(1,2) == math.sin(1))
table.sort({10,9,8,4,19,23,0,0}, function (a,b) return a<b end, "extra arg")
-- test for generic load
local x = "-- a comment\0\0\0\n x = 10 + \n23; \
local a = function () x = 'hi' end; \
return '\0'"
function read1 (x)
local i = 0
return function ()
collectgarbage()
i=i+1
return string.sub(x, i, i)
end
end
function cannotload (msg, a,b)
assert(not a and string.find(b, msg))
end
a = assert(load(read1(x), "modname", "t", _G))
assert(a() == "\0" and _G.x == 33)
assert(debug.getinfo(a).source == "modname")
-- cannot read text in binary mode
cannotload("attempt to load a text chunk", load(read1(x), "modname", "b", {}))
cannotload("attempt to load a text chunk", load(x, "modname", "b"))
a = assert(load(function () return nil end))
a() -- empty chunk
assert(not load(function () return true end))
-- small bug
local t = {nil, "return ", "3"}
f, msg = load(function () return table.remove(t, 1) end)
assert(f() == nil) -- should read the empty chunk
-- another small bug (in 5.2.1)
f = load(string.dump(function () return 1 end), nil, "b", {})
assert(type(f) == "function" and f() == 1)
x = string.dump(load("x = 1; return x"))
a = assert(load(read1(x), nil, "b"))
assert(a() == 1 and _G.x == 1)
cannotload("attempt to load a binary chunk", load(read1(x), nil, "t"))
cannotload("attempt to load a binary chunk", load(x, nil, "t"))
assert(not pcall(string.dump, print)) -- no dump of C functions
cannotload("unexpected symbol", load(read1("*a = 123")))
cannotload("unexpected symbol", load("*a = 123"))
cannotload("hhi", load(function () error("hhi") end))
-- any value is valid for _ENV
assert(load("return _ENV", nil, nil, 123)() == 123)
-- load when _ENV is not first upvalue
local x; XX = 123
local function h ()
local y=x -- use 'x', so that it becomes 1st upvalue
return XX -- global name
end
local d = string.dump(h)
x = load(d, "", "b")
assert(debug.getupvalue(x, 2) == '_ENV')
debug.setupvalue(x, 2, _G)
assert(x() == 123)
assert(assert(load("return XX + ...", nil, nil, {XX = 13}))(4) == 17)
-- test generic load with nested functions
x = [[
return function (x)
return function (y)
return function (z)
return x+y+z
end
end
end
]]
a = assert(load(read1(x)))
assert(a()(2)(3)(10) == 15)
-- test for dump/undump with upvalues
local a, b = 20, 30
x = load(string.dump(function (x)
if x == "set" then a = 10+b; b = b+1 else
return a
end
end), "", "b", nil)
assert(x() == nil)
assert(debug.setupvalue(x, 1, "hi") == "a")
assert(x() == "hi")
assert(debug.setupvalue(x, 2, 13) == "b")
assert(not debug.setupvalue(x, 3, 10)) -- only 2 upvalues
x("set")
assert(x() == 23)
x("set")
assert(x() == 24)
-- test for dump/undump with many upvalues
do
local nup = 200 -- maximum number of local variables
local prog = {"local a1"}
for i = 2, nup do prog[#prog + 1] = ", a" .. i end
prog[#prog + 1] = " = 1"
for i = 2, nup do prog[#prog + 1] = ", " .. i end
local sum = 1
prog[#prog + 1] = "; return function () return a1"
for i = 2, nup do prog[#prog + 1] = " + a" .. i; sum = sum + i end
prog[#prog + 1] = " end"
prog = table.concat(prog)
local f = assert(load(prog))()
assert(f() == sum)
f = load(string.dump(f)) -- main chunk now has many upvalues
local a = 10
local h = function () return a end
for i = 1, nup do
debug.upvaluejoin(f, i, h, 1)
end
assert(f() == 10 * nup)
end
-- test for long method names
do
local t = {x = 1}
function t:_012345678901234567890123456789012345678901234567890123456789 ()
return self.x
end
assert(t:_012345678901234567890123456789012345678901234567890123456789() == 1)
end
-- test for bug in parameter adjustment
assert((function () return nil end)(4) == nil)
assert((function () local a; return a end)(4) == nil)
assert((function (a) return a end)() == nil)
--[==[ NodeMCU code formats are different
print("testing binary chunks")
do
local header = string.pack("c4BBc6BBBBBj",
"\27Lua", -- signature
5*16 + 3, -- version 5.3
10, -- format
"\x19\x93\r\n\x1a\n", -- data
string.packsize("i"), -- sizeof(int)
string.packsize("T"), -- sizeof(size_t)
4, -- size of instruction
string.packsize("j"), -- sizeof(lua integer)
string.packsize("n"), -- sizeof(lua number)
0x5678 -- LUAC_INT
-- LUAC_NUM may not have a unique binary representation (padding...)
)
local c = string.dump(function () local a = 1; local b = 3; return a+b*3 end)
assert(string.sub(c, 1, #header) == header)
-- corrupted header
for i = 1, #header do
local s = string.sub(c, 1, i - 1) ..
string.char(string.byte(string.sub(c, i, i)) + 1) ..
string.sub(c, i + 1, -1)
assert(#s == #c)
assert(not load(s))
end
-- loading truncated binary chunks
for i = 1, #c - 1 do
local st, msg = load(string.sub(c, 1, i))
assert(not st and string.find(msg, "truncated"))
end
assert(assert(load(c))() == 10)
end
]==]
print('OK')
return deep

View File

@ -0,0 +1,250 @@
-- $Id: closure.lua,v 1.59 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
print "testing closures"
local A,B = 0,{g=10}
function f(x)
local a = {}
for i=1,1000 do
local y = 0
do
a[i] = function () B.g = B.g+1; y = y+x; return y+A end
end
end
local dummy = function () return a[A] end
collectgarbage()
A = 1; assert(dummy() == a[1]); A = 0;
assert(a[1]() == x)
assert(a[3]() == x)
collectgarbage()
assert(B.g == 12)
return a
end
local a = f(10)
-- force a GC in this level
local x = {[1] = {}} -- to detect a GC
setmetatable(x, {__mode = 'kv'})
while x[1] do -- repeat until GC
local a = A..A..A..A -- create garbage
A = A+1
end
assert(a[1]() == 20+A)
assert(a[1]() == 30+A)
assert(a[2]() == 10+A)
collectgarbage()
assert(a[2]() == 20+A)
assert(a[2]() == 30+A)
assert(a[3]() == 20+A)
assert(a[8]() == 10+A)
assert(getmetatable(x).__mode == 'kv')
assert(B.g == 19)
-- testing equality
a = {}
for i = 1, 5 do a[i] = function (x) return x + a + _ENV end end
--[[This relies on Proto caching which is not implemented for NodeMCU
assert(a[3] == a[4] and a[4] == a[5])
]]
for i = 1, 5 do a[i] = function (x) return i + a + _ENV end end
assert(a[3] ~= a[4] and a[4] ~= a[5])
local function f()
return function (x) return math.sin(_ENV[x]) end
end
--[[This relies on Proto caching which is not implemented for NodeMCU
assert(f() == f())
]]
-- testing closures with 'for' control variable
a = {}
for i=1,10 do
a[i] = {set = function(x) i=x end, get = function () return i end}
if i == 3 then break end
end
assert(a[4] == nil)
a[1].set(10)
assert(a[2].get() == 2)
a[2].set('a')
assert(a[3].get() == 3)
assert(a[2].get() == 'a')
a = {}
local t = {"a", "b"}
for i = 1, #t do
local k = t[i]
a[i] = {set = function(x, y) i=x; k=y end,
get = function () return i, k end}
if i == 2 then break end
end
a[1].set(10, 20)
local r,s = a[2].get()
assert(r == 2 and s == 'b')
r,s = a[1].get()
assert(r == 10 and s == 20)
a[2].set('a', 'b')
r,s = a[2].get()
assert(r == "a" and s == "b")
-- testing closures with 'for' control variable x break
for i=1,3 do
f = function () return i end
break
end
assert(f() == 1)
for k = 1, #t do
local v = t[k]
f = function () return k, v end
break
end
assert(({f()})[1] == 1)
assert(({f()})[2] == "a")
-- testing closure x break x return x errors
local b
function f(x)
local first = 1
while 1 do
if x == 3 and not first then return end
local a = 'xuxu'
b = function (op, y)
if op == 'set' then
a = x+y
else
return a
end
end
if x == 1 then do break end
elseif x == 2 then return
else if x ~= 3 then error() end
end
first = nil
end
end
for i=1,3 do
f(i)
assert(b('get') == 'xuxu')
b('set', 10); assert(b('get') == 10+i)
b = nil
end
pcall(f, 4);
assert(b('get') == 'xuxu')
b('set', 10); assert(b('get') == 14)
local w
-- testing multi-level closure
function f(x)
return function (y)
return function (z) return w+x+y+z end
end
end
y = f(10)
w = 1.345
assert(y(20)(30) == 60+w)
-- testing closures x repeat-until
local a = {}
local i = 1
repeat
local x = i
a[i] = function () i = x+1; return x end
until i > 10 or a[i]() ~= x
assert(i == 11 and a[1]() == 1 and a[3]() == 3 and i == 4)
-- testing closures created in 'then' and 'else' parts of 'if's
a = {}
for i = 1, 10 do
if i % 3 == 0 then
local y = 0
a[i] = function (x) local t = y; y = x; return t end
elseif i % 3 == 1 then
goto L1
error'not here'
::L1::
local y = 1
a[i] = function (x) local t = y; y = x; return t end
elseif i % 3 == 2 then
local t
goto l4
::l4a:: a[i] = t; goto l4b
error("should never be here!")
::l4::
local y = 2
t = function (x) local t = y; y = x; return t end
goto l4a
error("should never be here!")
::l4b::
end
end
for i = 1, 10 do
assert(a[i](i * 10) == i % 3 and a[i]() == i * 10)
end
print'+'
-- test for correctly closing upvalues in tail calls of vararg functions
local function t ()
local function c(a,b) assert(a=="test" and b=="OK") end
local function v(f, ...) c("test", f() ~= 1 and "FAILED" or "OK") end
local x = 1
return v(function() return x end)
end
t()
-- test for debug manipulation of upvalues
local debug = require'debug'
do
local a , b, c = 3, 5, 7
foo1 = function () return a+b end;
foo2 = function () return b+a end;
do
local a = 10
foo3 = function () return a+b end;
end
end
assert(debug.upvalueid(foo1, 1))
assert(debug.upvalueid(foo1, 2))
assert(not pcall(debug.upvalueid, foo1, 3))
assert(debug.upvalueid(foo1, 1) == debug.upvalueid(foo2, 2))
assert(debug.upvalueid(foo1, 2) == debug.upvalueid(foo2, 1))
assert(debug.upvalueid(foo3, 1))
assert(debug.upvalueid(foo1, 1) ~= debug.upvalueid(foo3, 1))
assert(debug.upvalueid(foo1, 2) == debug.upvalueid(foo3, 2))
assert(debug.upvalueid(string.gmatch("x", "x"), 1) ~= nil)
assert(foo1() == 3 + 5 and foo2() == 5 + 3)
debug.upvaluejoin(foo1, 2, foo2, 2)
assert(foo1() == 3 + 3 and foo2() == 5 + 3)
assert(foo3() == 10 + 5)
debug.upvaluejoin(foo3, 2, foo2, 1)
assert(foo3() == 10 + 5)
debug.upvaluejoin(foo3, 2, foo2, 2)
assert(foo3() == 10 + 3)
assert(not pcall(debug.upvaluejoin, foo1, 3, foo2, 1))
assert(not pcall(debug.upvaluejoin, foo1, 1, foo2, 3))
assert(not pcall(debug.upvaluejoin, foo1, 0, foo2, 1))
assert(not pcall(debug.upvaluejoin, print, 1, foo2, 1))
assert(not pcall(debug.upvaluejoin, {}, 1, foo2, 1))
assert(not pcall(debug.upvaluejoin, foo1, 1, print, 1))
print'OK'

View File

@ -0,0 +1,239 @@
-- $Id: code.lua,v 1.42 2016/11/07 13:04:32 roberto Exp $
-- See Copyright Notice in file all.lua
if T==nil then
(Message or print)('\n >>> testC not active: skipping opcode tests <<<\n')
return
end
print "testing code generation and optimizations"
-- this code gave an error for the code checker
do
local function f (a)
for k,v,w in a do end
end
end
-- testing reuse in constant table
local function checkKlist (func, list)
local k = T.listk(func)
assert(#k == #list)
for i = 1, #k do
assert(k[i] == list[i] and math.type(k[i]) == math.type(list[i]))
end
end
local function foo ()
local a
a = 3;
a = 0; a = 0.0; a = -7 + 7
a = 3.78/4; a = 3.78/4
a = -3.78/4; a = 3.78/4; a = -3.78/4
a = -3.79/4; a = 0.0; a = -0;
a = 3; a = 3.0; a = 3; a = 3.0
end
checkKlist(foo, {3, 0, 0.0, 3.78/4, -3.78/4, -3.79/4, 3.0})
-- testing opcodes
function check (f, ...)
local arg = {...}
local c = T.listcode(f)
for i=1, #arg do
-- print(arg[i], c[i])
assert(string.find(c[i], '- '..arg[i]..' *%d'))
end
assert(c[#arg+2] == nil)
end
function checkequal (a, b)
a = T.listcode(a)
b = T.listcode(b)
for i = 1, #a do
a[i] = string.gsub(a[i], '%b()', '') -- remove line number
b[i] = string.gsub(b[i], '%b()', '') -- remove line number
assert(a[i] == b[i])
end
end
-- some basic instructions
check(function ()
(function () end){f()}
end, 'CLOSURE', 'NEWTABLE', 'GETTABUP', 'CALL', 'SETLIST', 'CALL', 'RETURN')
-- sequence of LOADNILs
check(function ()
local a,b,c
local d; local e;
local f,g,h;
d = nil; d=nil; b=nil; a=nil; c=nil;
end, 'LOADNIL', 'RETURN')
check(function ()
local a,b,c,d = 1,1,1,1
d=nil;c=nil;b=nil;a=nil
end, 'LOADK', 'LOADK', 'LOADK', 'LOADK', 'LOADNIL', 'RETURN')
do
local a,b,c,d = 1,1,1,1
d=nil;c=nil;b=nil;a=nil
assert(a == nil and b == nil and c == nil and d == nil)
end
-- single return
check (function (a,b,c) return a end, 'RETURN')
-- infinite loops
check(function () while true do local a = -1 end end,
'LOADK', 'JMP', 'RETURN')
check(function () while 1 do local a = -1 end end,
'LOADK', 'JMP', 'RETURN')
check(function () repeat local x = 1 until true end,
'LOADK', 'RETURN')
-- concat optimization
check(function (a,b,c,d) return a..b..c..d end,
'MOVE', 'MOVE', 'MOVE', 'MOVE', 'CONCAT', 'RETURN')
-- not
check(function () return not not nil end, 'LOADBOOL', 'RETURN')
check(function () return not not false end, 'LOADBOOL', 'RETURN')
check(function () return not not true end, 'LOADBOOL', 'RETURN')
check(function () return not not 1 end, 'LOADBOOL', 'RETURN')
-- direct access to locals
check(function ()
local a,b,c,d
a = b*2
c[2], a[b] = -((a + d/2 - a[b]) ^ a.x), b
end,
'LOADNIL',
'MUL',
'DIV', 'ADD', 'GETTABLE', 'SUB', 'GETTABLE', 'POW',
'UNM', 'SETTABLE', 'SETTABLE', 'RETURN')
-- direct access to constants
check(function ()
local a,b
a.x = 3.2
a.x = b
a[b] = 'x'
end,
'LOADNIL', 'SETTABLE', 'SETTABLE', 'SETTABLE', 'RETURN')
check(function ()
local a,b
a = 1 - a
b = 1/a
b = 5-4
end,
'LOADNIL', 'SUB', 'DIV', 'LOADK', 'RETURN')
check(function ()
local a,b
a[true] = false
end,
'LOADNIL', 'SETTABLE', 'RETURN')
-- constant folding
local function checkK (func, val)
check(func, 'LOADK', 'RETURN')
local k = T.listk(func)
assert(#k == 1 and k[1] == val and math.type(k[1]) == math.type(val))
assert(func() == val)
end
checkK(function () return 0.0 end, 0.0)
checkK(function () return 0 end, 0)
checkK(function () return -0//1 end, 0)
checkK(function () return 3^-1 end, 1/3)
checkK(function () return (1 + 1)^(50 + 50) end, 2^100)
checkK(function () return (-2)^(31 - 2) end, -0x20000000 + 0.0)
checkK(function () return (-3^0 + 5) // 3.0 end, 1.0)
checkK(function () return -3 % 5 end, 2)
checkK(function () return -((2.0^8 + -(-1)) % 8)/2 * 4 - 3 end, -5.0)
checkK(function () return -((2^8 + -(-1)) % 8)//2 * 4 - 3 end, -7.0)
checkK(function () return 0xF0.0 | 0xCC.0 ~ 0xAA & 0xFD end, 0xF4)
checkK(function () return ~(~0xFF0 | 0xFF0) end, 0)
checkK(function () return ~~-100024.0 end, -100024)
checkK(function () return ((100 << 6) << -4) >> 2 end, 100)
-- no foldings
check(function () return -0.0 end, 'LOADK', 'UNM', 'RETURN')
check(function () return 3/0 end, 'DIV', 'RETURN')
check(function () return 0%0 end, 'MOD', 'RETURN')
check(function () return -4//0 end, 'IDIV', 'RETURN')
-- bug in constant folding for 5.1
check(function () return -nil end, 'LOADNIL', 'UNM', 'RETURN')
check(function ()
local a,b,c
b[c], a = c, b
b[a], a = c, b
a, b = c, a
a = a
end,
'LOADNIL',
'MOVE', 'MOVE', 'SETTABLE',
'MOVE', 'MOVE', 'MOVE', 'SETTABLE',
'MOVE', 'MOVE', 'MOVE',
-- no code for a = a
'RETURN')
-- x == nil , x ~= nil
checkequal(function () if (a==nil) then a=1 end; if a~=nil then a=1 end end,
function () if (a==9) then a=1 end; if a~=9 then a=1 end end)
check(function () if a==nil then a='a' end end,
'GETTABUP', 'EQ', 'JMP', 'SETTABUP', 'RETURN')
-- de morgan
checkequal(function () local a; if not (a or b) then b=a end end,
function () local a; if (not a and not b) then b=a end end)
checkequal(function (l) local a; return 0 <= a and a <= l end,
function (l) local a; return not (not(a >= 0) or not(a <= l)) end)
-- if-goto optimizations
check(function (a, b, c, d, e)
if a == b then goto l1
elseif a == c then goto l2
elseif a == d then goto l2
else if a == e then goto l3
else goto l3
end
end
::l1:: ::l2:: ::l3:: ::l4::
end, 'EQ', 'JMP', 'EQ', 'JMP', 'EQ', 'JMP', 'EQ', 'JMP', 'JMP', 'RETURN')
checkequal(
function (a) while a < 10 do a = a + 1 end end,
function (a) ::L2:: if not(a < 10) then goto L1 end; a = a + 1;
goto L2; ::L1:: end
)
checkequal(
function (a) while a < 10 do a = a + 1 end end,
function (a) while true do if not(a < 10) then break end; a = a + 1; end end
)
print 'OK'

View File

@ -0,0 +1,313 @@
-- $Id: constructs.lua,v 1.41 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
;;print "testing syntax";;
local debug = require "debug"
local function checkload (s, msg)
assert(string.find(select(2, load(s)), msg))
end
-- testing semicollons
do ;;; end
; do ; a = 3; assert(a == 3) end;
;
-- invalid operations should not raise errors when not executed
if false then a = 3 // 0; a = 0 % 0 end
-- testing priorities
assert(2^3^2 == 2^(3^2));
assert(2^3*4 == (2^3)*4);
assert(2.0^-2 == 1/4 and -2^- -2 == - - -4);
assert(not nil and 2 and not(2>3 or 3<2));
assert(-3-1-5 == 0+0-9);
assert(-2^2 == -4 and (-2)^2 == 4 and 2*2-3-1 == 0);
assert(-3%5 == 2 and -3+5 == 2)
assert(2*1+3/3 == 3 and 1+2 .. 3*1 == "33");
assert(not(2+1 > 3*1) and "a".."b" > "a");
assert("7" .. 3 << 1 == 146)
assert(10 >> 1 .. "9" == 0)
assert(10 | 1 .. "9" == 27)
assert(0xF0 | 0xCC ~ 0xAA & 0xFD == 0xF4)
assert(0xFD & 0xAA ~ 0xCC | 0xF0 == 0xF4)
assert(0xF0 & 0x0F + 1 == 0x10)
assert(3^4//2^3//5 == 2)
assert(-3+4*5//2^3^2//9+4%10/3 == (-3)+(((4*5)//(2^(3^2)))//9)+((4%10)/3))
assert(not ((true or false) and nil))
assert( true or false and nil)
-- old bug
assert((((1 or false) and true) or false) == true)
assert((((nil and true) or false) and true) == false)
local a,b = 1,nil;
assert(-(1 or 2) == -1 and (1 and 2)+(-1.25 or -4) == 0.75);
x = ((b or a)+1 == 2 and (10 or a)+1 == 11); assert(x);
x = (((2<3) or 1) == true and (2<3 and 4) == 4); assert(x);
x,y=1,2;
assert((x>y) and x or y == 2);
x,y=2,1;
assert((x>y) and x or y == 2);
assert(1234567890 == tonumber('1234567890') and 1234567890+1 == 1234567891)
-- silly loops
repeat until 1; repeat until true;
while false do end; while nil do end;
do -- test old bug (first name could not be an `upvalue')
local a; function f(x) x={a=1}; x={x=1}; x={G=1} end
end
function f (i)
if type(i) ~= 'number' then return i,'jojo'; end;
if i > 0 then return i, f(i-1); end;
end
x = {f(3), f(5), f(10);};
assert(x[1] == 3 and x[2] == 5 and x[3] == 10 and x[4] == 9 and x[12] == 1);
assert(x[nil] == nil)
x = {f'alo', f'xixi', nil};
assert(x[1] == 'alo' and x[2] == 'xixi' and x[3] == nil);
x = {f'alo'..'xixi'};
assert(x[1] == 'aloxixi')
x = {f{}}
assert(x[2] == 'jojo' and type(x[1]) == 'table')
local f = function (i)
if i < 10 then return 'a';
elseif i < 20 then return 'b';
elseif i < 30 then return 'c';
end;
end
assert(f(3) == 'a' and f(12) == 'b' and f(26) == 'c' and f(100) == nil)
for i=1,1000 do break; end;
n=100;
i=3;
t = {};
a=nil
while not a do
a=0; for i=1,n do for i=i,1,-1 do a=a+1; t[i]=1; end; end;
end
assert(a == n*(n+1)/2 and i==3);
assert(t[1] and t[n] and not t[0] and not t[n+1])
function f(b)
local x = 1;
repeat
local a;
if b==1 then local b=1; x=10; break
elseif b==2 then x=20; break;
elseif b==3 then x=30;
else local a,b,c,d=math.sin(1); x=x+1;
end
until x>=12;
return x;
end;
assert(f(1) == 10 and f(2) == 20 and f(3) == 30 and f(4)==12)
local f = function (i)
if i < 10 then return 'a'
elseif i < 20 then return 'b'
elseif i < 30 then return 'c'
else return 8
end
end
assert(f(3) == 'a' and f(12) == 'b' and f(26) == 'c' and f(100) == 8)
local a, b = nil, 23
x = {f(100)*2+3 or a, a or b+2}
assert(x[1] == 19 and x[2] == 25)
x = {f=2+3 or a, a = b+2}
assert(x.f == 5 and x.a == 25)
a={y=1}
x = {a.y}
assert(x[1] == 1)
function f(i)
while 1 do
if i>0 then i=i-1;
else return; end;
end;
end;
function g(i)
while 1 do
if i>0 then i=i-1
else return end
end
end
f(10); g(10);
do
function f () return 1,2,3; end
local a, b, c = f();
assert(a==1 and b==2 and c==3)
a, b, c = (f());
assert(a==1 and b==nil and c==nil)
end
local a,b = 3 and f();
assert(a==1 and b==nil)
function g() f(); return; end;
assert(g() == nil)
function g() return nil or f() end
a,b = g()
assert(a==1 and b==nil)
print'+';
f = [[
return function ( a , b , c , d , e )
local x = a >= b or c or ( d and e ) or nil
return x
end , { a = 1 , b = 2 >= 1 , } or { 1 };
]]
f = string.gsub(f, "%s+", "\n"); -- force a SETLINE between opcodes
f,a = load(f)();
assert(a.a == 1 and a.b)
function g (a,b,c,d,e)
if not (a>=b or c or d and e or nil) then return 0; else return 1; end;
end
function h (a,b,c,d,e)
while (a>=b or c or (d and e) or nil) do return 1; end;
return 0;
end;
assert(f(2,1) == true and g(2,1) == 1 and h(2,1) == 1)
assert(f(1,2,'a') == 'a' and g(1,2,'a') == 1 and h(1,2,'a') == 1)
assert(f(1,2,'a')
~= -- force SETLINE before nil
nil, "")
assert(f(1,2,'a') == 'a' and g(1,2,'a') == 1 and h(1,2,'a') == 1)
assert(f(1,2,nil,1,'x') == 'x' and g(1,2,nil,1,'x') == 1 and
h(1,2,nil,1,'x') == 1)
assert(f(1,2,nil,nil,'x') == nil and g(1,2,nil,nil,'x') == 0 and
h(1,2,nil,nil,'x') == 0)
assert(f(1,2,nil,1,nil) == nil and g(1,2,nil,1,nil) == 0 and
h(1,2,nil,1,nil) == 0)
assert(1 and 2<3 == true and 2<3 and 'a'<'b' == true)
x = 2<3 and not 3; assert(x==false)
x = 2<1 or (2>1 and 'a'); assert(x=='a')
do
local a; if nil then a=1; else a=2; end; -- this nil comes as PUSHNIL 2
assert(a==2)
end
function F(a)
assert(debug.getinfo(1, "n").name == 'F')
return a,2,3
end
a,b = F(1)~=nil; assert(a == true and b == nil);
a,b = F(nil)==nil; assert(a == true and b == nil)
----------------------------------------------------------------
------------------------------------------------------------------
-- sometimes will be 0, sometimes will not...
_ENV.GLOB1 = math.floor(os.time()) % 2
-- basic expressions with their respective values
local basiccases = {
{"nil", nil},
{"false", false},
{"true", true},
{"10", 10},
{"(0==_ENV.GLOB1)", 0 == _ENV.GLOB1},
}
print('testing short-circuit optimizations (' .. _ENV.GLOB1 .. ')')
-- operators with their respective values
local binops = {
{" and ", function (a,b) if not a then return a else return b end end},
{" or ", function (a,b) if a then return a else return b end end},
}
local cases = {}
-- creates all combinations of '(cases[i] op cases[n-i])' plus
-- 'not(cases[i] op cases[n-i])' (syntax + value)
local function createcases (n)
local res = {}
for i = 1, n - 1 do
for _, v1 in ipairs(cases[i]) do
for _, v2 in ipairs(cases[n - i]) do
for _, op in ipairs(binops) do
local t = {
"(" .. v1[1] .. op[1] .. v2[1] .. ")",
op[2](v1[2], v2[2])
}
res[#res + 1] = t
res[#res + 1] = {"not" .. t[1], not t[2]}
end
end
end
end
return res
end
-- do not do too many combinations for soft tests
local level = _soft and 3 or 4
cases[1] = basiccases
for i = 2, level do cases[i] = createcases(i) end
print("+")
local prog = [[if %s then IX = true end; return %s]]
local i = 0
for n = 1, level do
for _, v in pairs(cases[n]) do
local s = v[1]
local p = load(string.format(prog, s, s), "")
IX = false
assert(p() == v[2] and IX == not not v[2])
i = i + 1
if i % 60000 == 0 then print('+') end
end
end
------------------------------------------------------------------
-- testing some syntax errors (chosen through 'gcov')
checkload("for x do", "expected")
checkload("x:call", "expected")
if not _soft then
-- control structure too long
local s = string.rep("a = a + 1\n", 2^18)
s = "while true do " .. s .. "end"
checkload(s, "too long")
end
print'OK'

View File

@ -0,0 +1,874 @@
-- $Id: coroutine.lua,v 1.42 2016/11/07 13:03:20 roberto Exp $
-- See Copyright Notice in file all.lua
print "testing coroutines"
local debug = require'debug'
local f
local main, ismain = coroutine.running()
assert(type(main) == "thread" and ismain)
assert(not coroutine.resume(main))
assert(not coroutine.isyieldable())
assert(not pcall(coroutine.yield))
-- trivial errors
assert(not pcall(coroutine.resume, 0))
assert(not pcall(coroutine.status, 0))
-- tests for multiple yield/resume arguments
local function eqtab (t1, t2)
assert(#t1 == #t2)
for i = 1, #t1 do
local v = t1[i]
assert(t2[i] == v)
end
end
_G.x = nil -- declare x
function foo (a, ...)
local x, y = coroutine.running()
assert(x == f and y == false)
-- next call should not corrupt coroutine (but must fail,
-- as it attempts to resume the running coroutine)
assert(coroutine.resume(f) == false)
assert(coroutine.status(f) == "running")
local arg = {...}
assert(coroutine.isyieldable())
for i=1,#arg do
_G.x = {coroutine.yield(table.unpack(arg[i]))}
end
return table.unpack(a)
end
f = coroutine.create(foo)
assert(type(f) == "thread" and coroutine.status(f) == "suspended")
assert(string.find(tostring(f), "thread"))
local s,a,b,c,d
s,a,b,c,d = coroutine.resume(f, {1,2,3}, {}, {1}, {'a', 'b', 'c'})
assert(s and a == nil and coroutine.status(f) == "suspended")
s,a,b,c,d = coroutine.resume(f)
eqtab(_G.x, {})
assert(s and a == 1 and b == nil)
s,a,b,c,d = coroutine.resume(f, 1, 2, 3)
eqtab(_G.x, {1, 2, 3})
assert(s and a == 'a' and b == 'b' and c == 'c' and d == nil)
s,a,b,c,d = coroutine.resume(f, "xuxu")
eqtab(_G.x, {"xuxu"})
assert(s and a == 1 and b == 2 and c == 3 and d == nil)
assert(coroutine.status(f) == "dead")
s, a = coroutine.resume(f, "xuxu")
assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead")
-- yields in tail calls
local function foo (i) return coroutine.yield(i) end
f = coroutine.wrap(function ()
for i=1,10 do
assert(foo(i) == _G.x)
end
return 'a'
end)
for i=1,10 do _G.x = i; assert(f(i) == i) end
_G.x = 'xuxu'; assert(f('xuxu') == 'a')
-- recursive
function pf (n, i)
coroutine.yield(n)
pf(n*i, i+1)
end
f = coroutine.wrap(pf)
local s=1
for i=1,10 do
assert(f(1, 1) == s)
s = s*i
end
-- sieve
function gen (n)
return coroutine.wrap(function ()
for i=2,n do coroutine.yield(i) end
end)
end
function filter (p, g)
return coroutine.wrap(function ()
while 1 do
local n = g()
if n == nil then return end
if math.fmod(n, p) ~= 0 then coroutine.yield(n) end
end
end)
end
local x = gen(100)
local a = {}
while 1 do
local n = x()
if n == nil then break end
table.insert(a, n)
x = filter(n, x)
end
assert(#a == 25 and a[#a] == 97)
x, a = nil
-- yielding across C boundaries
co = coroutine.wrap(function()
assert(not pcall(table.sort,{1,2,3}, coroutine.yield))
assert(coroutine.isyieldable())
coroutine.yield(20)
return 30
end)
assert(co() == 20)
assert(co() == 30)
local f = function (s, i) return coroutine.yield(i) end
local f1 = coroutine.wrap(function ()
return xpcall(pcall, function (...) return ... end,
function ()
local s = 0
for i in f, nil, 1 do pcall(function () s = s + i end) end
error({s})
end)
end)
f1()
for i = 1, 10 do assert(f1(i) == i) end
local r1, r2, v = f1(nil)
assert(r1 and not r2 and v[1] == (10 + 1)*10/2)
function f (a, b) a = coroutine.yield(a); error{a + b} end
function g(x) return x[1]*2 end
co = coroutine.wrap(function ()
coroutine.yield(xpcall(f, g, 10, 20))
end)
assert(co() == 10)
r, msg = co(100)
assert(not r and msg == 240)
-- unyieldable C call
do
local function f (c)
assert(not coroutine.isyieldable())
return c .. c
end
local co = coroutine.wrap(function (c)
assert(coroutine.isyieldable())
local s = string.gsub("a", ".", f)
return s
end)
assert(co() == "aa")
end
-- errors in coroutines
function foo ()
assert(debug.getinfo(1).currentline == debug.getinfo(foo).linedefined + 1)
assert(debug.getinfo(2).currentline == debug.getinfo(goo).linedefined)
coroutine.yield(3)
error(foo)
end
function goo() foo() end
x = coroutine.wrap(goo)
assert(x() == 3)
local a,b = pcall(x)
assert(not a and b == foo)
x = coroutine.create(goo)
a,b = coroutine.resume(x)
assert(a and b == 3)
a,b = coroutine.resume(x)
assert(not a and b == foo and coroutine.status(x) == "dead")
a,b = coroutine.resume(x)
assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead")
-- co-routines x for loop
function all (a, n, k)
if k == 0 then coroutine.yield(a)
else
for i=1,n do
a[k] = i
all(a, n, k-1)
end
end
end
local a = 0
for t in coroutine.wrap(function () all({}, 5, 4) end) do
a = a+1
end
assert(a == 5^4)
-- access to locals of collected corroutines
local C = {}; setmetatable(C, {__mode = "kv"})
local x = coroutine.wrap (function ()
local a = 10
local function f () a = a+10; return a end
while true do
a = a+1
coroutine.yield(f)
end
end)
C[1] = x;
local f = x()
assert(f() == 21 and x()() == 32 and x() == f)
x = nil
collectgarbage()
assert(C[1] == nil)
assert(f() == 43 and f() == 53)
-- old bug: attempt to resume itself
function co_func (current_co)
assert(coroutine.running() == current_co)
assert(coroutine.resume(current_co) == false)
coroutine.yield(10, 20)
assert(coroutine.resume(current_co) == false)
coroutine.yield(23)
return 10
end
local co = coroutine.create(co_func)
local a,b,c = coroutine.resume(co, co)
assert(a == true and b == 10 and c == 20)
a,b = coroutine.resume(co, co)
assert(a == true and b == 23)
a,b = coroutine.resume(co, co)
assert(a == true and b == 10)
assert(coroutine.resume(co, co) == false)
assert(coroutine.resume(co, co) == false)
-- other old bug when attempting to resume itself
-- (trigger C-code assertions)
do
local A = coroutine.running()
local B = coroutine.create(function() return coroutine.resume(A) end)
local st, res = coroutine.resume(B)
assert(st == true and res == false)
A = coroutine.wrap(function() return pcall(A, 1) end)
st, res = A()
assert(not st and string.find(res, "non%-suspended"))
end
-- attempt to resume 'normal' coroutine
local co1, co2
co1 = coroutine.create(function () return co2() end)
co2 = coroutine.wrap(function ()
assert(coroutine.status(co1) == 'normal')
assert(not coroutine.resume(co1))
coroutine.yield(3)
end)
a,b = coroutine.resume(co1)
assert(a and b == 3)
assert(coroutine.status(co1) == 'dead')
-- infinite recursion of coroutines
a = function(a) coroutine.wrap(a)(a) end
assert(not pcall(a, a))
a = nil
-- access to locals of erroneous coroutines
local x = coroutine.create (function ()
local a = 10
_G.f = function () a=a+1; return a end
error('x')
end)
assert(not coroutine.resume(x))
-- overwrite previous position of local `a'
assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1))
assert(_G.f() == 11)
assert(_G.f() == 12)
if not T then
(Message or print)('\n >>> testC not active: skipping yield/hook tests <<<\n')
else
print "testing yields inside hooks"
local turn
function fact (t, x)
assert(turn == t)
if x == 0 then return 1
else return x*fact(t, x-1)
end
end
local A, B = 0, 0
local x = coroutine.create(function ()
T.sethook("yield 0", "", 2)
A = fact("A", 6)
end)
local y = coroutine.create(function ()
T.sethook("yield 0", "", 3)
B = fact("B", 7)
end)
while A==0 or B==0 do -- A ~= 0 when 'x' finishes (similar for 'B','y')
if A==0 then turn = "A"; assert(T.resume(x)) end
if B==0 then turn = "B"; assert(T.resume(y)) end
end
assert(B // A == 7) -- fact(7) // fact(6)
local line = debug.getinfo(1, "l").currentline + 2 -- get line number
local function foo ()
local x = 10 --<< this line is 'line'
x = x + 10
_G.XX = x
end
-- testing yields in line hook
local co = coroutine.wrap(function ()
T.sethook("setglobal X; yield 0", "l", 0); foo(); return 10 end)
_G.XX = nil;
_G.X = nil; co(); assert(_G.X == line)
_G.X = nil; co(); assert(_G.X == line + 1)
_G.X = nil; co(); assert(_G.X == line + 2 and _G.XX == nil)
_G.X = nil; co(); assert(_G.X == line + 3 and _G.XX == 20)
assert(co() == 10)
-- testing yields in count hook
co = coroutine.wrap(function ()
T.sethook("yield 0", "", 1); foo(); return 10 end)
_G.XX = nil;
local c = 0
repeat c = c + 1; local a = co() until a == 10
assert(_G.XX == 20 and c >= 5)
co = coroutine.wrap(function ()
T.sethook("yield 0", "", 2); foo(); return 10 end)
_G.XX = nil;
local c = 0
repeat c = c + 1; local a = co() until a == 10
assert(_G.XX == 20 and c >= 5)
_G.X = nil; _G.XX = nil
do
-- testing debug library on a coroutine suspended inside a hook
-- (bug in 5.2/5.3)
c = coroutine.create(function (a, ...)
T.sethook("yield 0", "l") -- will yield on next two lines
assert(a == 10)
return ...
end)
assert(coroutine.resume(c, 1, 2, 3)) -- start coroutine
local n,v = debug.getlocal(c, 0, 1) -- check its local
assert(n == "a" and v == 1)
n,v = debug.getlocal(c, 0, -1) -- check varargs
assert(v == 2)
n,v = debug.getlocal(c, 0, -2)
assert(v == 3)
assert(debug.setlocal(c, 0, 1, 10)) -- test 'setlocal'
assert(debug.setlocal(c, 0, -2, 20))
local t = debug.getinfo(c, 0) -- test 'getinfo'
assert(t.currentline == t.linedefined + 1)
assert(not debug.getinfo(c, 1)) -- no other level
assert(coroutine.resume(c)) -- run next line
v = {coroutine.resume(c)} -- finish coroutine
assert(v[1] == true and v[2] == 2 and v[3] == 20 and v[4] == nil)
assert(not coroutine.resume(c))
end
do
-- testing debug library on last function in a suspended coroutine
-- (bug in 5.2/5.3)
local c = coroutine.create(function () T.testC("yield 1", 10, 20) end)
local a, b = coroutine.resume(c)
assert(a and b == 20)
assert(debug.getinfo(c, 0).linedefined == -1)
a, b = debug.getlocal(c, 0, 2)
assert(b == 10)
end
print "testing coroutine API"
-- reusing a thread
assert(T.testC([[
newthread # create thread
pushvalue 2 # push body
pushstring 'a a a' # push argument
xmove 0 3 2 # move values to new thread
resume -1, 1 # call it first time
pushstatus
xmove 3 0 0 # move results back to stack
setglobal X # result
setglobal Y # status
pushvalue 2 # push body (to call it again)
pushstring 'b b b'
xmove 0 3 2
resume -1, 1 # call it again
pushstatus
xmove 3 0 0
return 1 # return result
]], function (...) return ... end) == 'b b b')
assert(X == 'a a a' and Y == 'OK')
-- resuming running coroutine
C = coroutine.create(function ()
return T.testC([[
pushnum 10;
pushnum 20;
resume -3 2;
pushstatus
gettop;
return 3]], C)
end)
local a, b, c, d = coroutine.resume(C)
assert(a == true and string.find(b, "non%-suspended") and
c == "ERRRUN" and d == 4)
a, b, c, d = T.testC([[
rawgeti R 1 # get main thread
pushnum 10;
pushnum 20;
resume -3 2;
pushstatus
gettop;
return 4]])
assert(a == coroutine.running() and string.find(b, "non%-suspended") and
c == "ERRRUN" and d == 4)
-- using a main thread as a coroutine
local state = T.newstate()
T.loadlib(state)
assert(T.doremote(state, [[
coroutine = require'coroutine';
X = function (x) coroutine.yield(x, 'BB'); return 'CC' end;
return 'ok']]))
t = table.pack(T.testC(state, [[
rawgeti R 1 # get main thread
pushstring 'XX'
getglobal X # get function for body
pushstring AA # arg
resume 1 1 # 'resume' shadows previous stack!
gettop
setglobal T # top
setglobal B # second yielded value
setglobal A # fist yielded value
rawgeti R 1 # get main thread
pushnum 5 # arg (noise)
resume 1 1 # after coroutine ends, previous stack is back
pushstatus
return *
]]))
assert(t.n == 4 and t[2] == 'XX' and t[3] == 'CC' and t[4] == 'OK')
assert(T.doremote(state, "return T") == '2')
assert(T.doremote(state, "return A") == 'AA')
assert(T.doremote(state, "return B") == 'BB')
T.closestate(state)
print'+'
end
-- leaving a pending coroutine open
_X = coroutine.wrap(function ()
local a = 10
local x = function () a = a+1 end
coroutine.yield()
end)
_X()
if not _soft then
-- bug (stack overflow)
local j = 2^9
local lim = 1000000 -- (C stack limit; assume 32-bit machine)
local t = {lim - 10, lim - 5, lim - 1, lim, lim + 1}
for i = 1, #t do
local j = t[i]
co = coroutine.create(function()
local t = {}
for i = 1, j do t[i] = i end
return table.unpack(t)
end)
local r, msg = coroutine.resume(co)
assert(not r)
end
co = nil
end
assert(coroutine.running() == main)
print"+"
print"testing yields inside metamethods"
local mt = {
__eq = function(a,b) coroutine.yield(nil, "eq"); return a.x == b.x end,
__lt = function(a,b) coroutine.yield(nil, "lt"); return a.x < b.x end,
__le = function(a,b) coroutine.yield(nil, "le"); return a - b <= 0 end,
__add = function(a,b) coroutine.yield(nil, "add"); return a.x + b.x end,
__sub = function(a,b) coroutine.yield(nil, "sub"); return a.x - b.x end,
__mod = function(a,b) coroutine.yield(nil, "mod"); return a.x % b.x end,
__unm = function(a,b) coroutine.yield(nil, "unm"); return -a.x end,
__bnot = function(a,b) coroutine.yield(nil, "bnot"); return ~a.x end,
__shl = function(a,b) coroutine.yield(nil, "shl"); return a.x << b.x end,
__shr = function(a,b) coroutine.yield(nil, "shr"); return a.x >> b.x end,
__band = function(a,b)
a = type(a) == "table" and a.x or a
b = type(b) == "table" and b.x or b
coroutine.yield(nil, "band")
return a & b
end,
__bor = function(a,b) coroutine.yield(nil, "bor"); return a.x | b.x end,
__bxor = function(a,b) coroutine.yield(nil, "bxor"); return a.x ~ b.x end,
__concat = function(a,b)
coroutine.yield(nil, "concat");
a = type(a) == "table" and a.x or a
b = type(b) == "table" and b.x or b
return a .. b
end,
__index = function (t,k) coroutine.yield(nil, "idx"); return t.k[k] end,
__newindex = function (t,k,v) coroutine.yield(nil, "nidx"); t.k[k] = v end,
}
local function new (x)
return setmetatable({x = x, k = {}}, mt)
end
local a = new(10)
local b = new(12)
local c = new"hello"
local function run (f, t)
local i = 1
local c = coroutine.wrap(f)
while true do
local res, stat = c()
if res then assert(t[i] == nil); return res, t end
assert(stat == t[i])
i = i + 1
end
end
assert(run(function () if (a>=b) then return '>=' else return '<' end end,
{"le", "sub"}) == "<")
-- '<=' using '<'
mt.__le = nil
assert(run(function () if (a<=b) then return '<=' else return '>' end end,
{"lt"}) == "<=")
assert(run(function () if (a==b) then return '==' else return '~=' end end,
{"eq"}) == "~=")
assert(run(function () return a & b + a end, {"add", "band"}) == 2)
assert(run(function () return a % b end, {"mod"}) == 10)
assert(run(function () return ~a & b end, {"bnot", "band"}) == ~10 & 12)
assert(run(function () return a | b end, {"bor"}) == 10 | 12)
assert(run(function () return a ~ b end, {"bxor"}) == 10 ~ 12)
assert(run(function () return a << b end, {"shl"}) == 10 << 12)
assert(run(function () return a >> b end, {"shr"}) == 10 >> 12)
assert(run(function () return a..b end, {"concat"}) == "1012")
assert(run(function() return a .. b .. c .. a end,
{"concat", "concat", "concat"}) == "1012hello10")
assert(run(function() return "a" .. "b" .. a .. "c" .. c .. b .. "x" end,
{"concat", "concat", "concat"}) == "ab10chello12x")
do -- a few more tests for comparsion operators
local mt1 = {
__le = function (a,b)
coroutine.yield(10)
return
(type(a) == "table" and a.x or a) <= (type(b) == "table" and b.x or b)
end,
__lt = function (a,b)
coroutine.yield(10)
return
(type(a) == "table" and a.x or a) < (type(b) == "table" and b.x or b)
end,
}
local mt2 = { __lt = mt1.__lt } -- no __le
local function run (f)
local co = coroutine.wrap(f)
local res
repeat
res = co()
until res ~= 10
return res
end
local function test ()
local a1 = setmetatable({x=1}, mt1)
local a2 = setmetatable({x=2}, mt2)
assert(a1 < a2)
assert(a1 <= a2)
assert(1 < a2)
assert(1 <= a2)
assert(2 > a1)
assert(2 >= a2)
return true
end
run(test)
end
assert(run(function ()
a.BB = print
return a.BB
end, {"nidx", "idx"}) == print)
-- getuptable & setuptable
do local _ENV = _ENV
f = function () AAA = BBB + 1; return AAA end
end
g = new(10); g.k.BBB = 10;
debug.setupvalue(f, 1, g)
assert(run(f, {"idx", "nidx", "idx"}) == 11)
assert(g.k.AAA == 11)
print"+"
print"testing yields inside 'for' iterators"
local f = function (s, i)
if i%2 == 0 then coroutine.yield(nil, "for") end
if i < s then return i + 1 end
end
assert(run(function ()
local s = 0
for i in f, 4, 0 do s = s + i end
return s
end, {"for", "for", "for"}) == 10)
-- tests for coroutine API
if T==nil then
(Message or print)('\n >>> testC not active: skipping coroutine API tests <<<\n')
return
end
print('testing coroutine API')
local function apico (...)
local x = {...}
return coroutine.wrap(function ()
return T.testC(table.unpack(x))
end)
end
local a = {apico(
[[
pushstring errorcode
pcallk 1 0 2;
invalid command (should not arrive here)
]],
[[return *]],
"stackmark",
error
)()}
assert(#a == 4 and
a[3] == "stackmark" and
a[4] == "errorcode" and
_G.status == "ERRRUN" and
_G.ctx == 2) -- 'ctx' to pcallk
local co = apico(
"pushvalue 2; pushnum 10; pcallk 1 2 3; invalid command;",
coroutine.yield,
"getglobal status; getglobal ctx; pushvalue 2; pushstring a; pcallk 1 0 4; invalid command",
"getglobal status; getglobal ctx; return *")
assert(co() == 10)
assert(co(20, 30) == 'a')
a = {co()}
assert(#a == 10 and
a[2] == coroutine.yield and
a[5] == 20 and a[6] == 30 and
a[7] == "YIELD" and a[8] == 3 and
a[9] == "YIELD" and a[10] == 4)
assert(not pcall(co)) -- coroutine is dead now
f = T.makeCfunc("pushnum 3; pushnum 5; yield 1;")
co = coroutine.wrap(function ()
assert(f() == 23); assert(f() == 23); return 10
end)
assert(co(23,16) == 5)
assert(co(23,16) == 5)
assert(co(23,16) == 10)
-- testing coroutines with C bodies
f = T.makeCfunc([[
pushnum 102
yieldk 1 U2
cannot be here!
]],
[[ # continuation
pushvalue U3 # accessing upvalues inside a continuation
pushvalue U4
return *
]], 23, "huu")
x = coroutine.wrap(f)
assert(x() == 102)
eqtab({x()}, {23, "huu"})
f = T.makeCfunc[[pushstring 'a'; pushnum 102; yield 2; ]]
a, b, c, d = T.testC([[newthread; pushvalue 2; xmove 0 3 1; resume 3 0;
pushstatus; xmove 3 0 0; resume 3 0; pushstatus;
return 4; ]], f)
assert(a == 'YIELD' and b == 'a' and c == 102 and d == 'OK')
-- testing chain of suspendable C calls
local count = 3 -- number of levels
f = T.makeCfunc([[
remove 1; # remove argument
pushvalue U3; # get selection function
call 0 1; # call it (result is 'f' or 'yield')
pushstring hello # single argument for selected function
pushupvalueindex 2; # index of continuation program
callk 1 -1 .; # call selected function
errorerror # should never arrive here
]],
[[
# continuation program
pushnum 34 # return value
return * # return all results
]],
function () -- selection function
count = count - 1
if count == 0 then return coroutine.yield
else return f
end
end
)
co = coroutine.wrap(function () return f(nil) end)
assert(co() == "hello") -- argument to 'yield'
a = {co()}
-- three '34's (one from each pending C call)
assert(#a == 3 and a[1] == a[2] and a[2] == a[3] and a[3] == 34)
-- testing yields with continuations
co = coroutine.wrap(function (...) return
T.testC([[ # initial function
yieldk 1 2
cannot be here!
]],
[[ # 1st continuation
yieldk 0 3
cannot be here!
]],
[[ # 2nd continuation
yieldk 0 4
cannot be here!
]],
[[ # 3th continuation
pushvalue 6 # function which is last arg. to 'testC' here
pushnum 10; pushnum 20;
pcall 2 0 0 # call should throw an error and return to next line
pop 1 # remove error message
pushvalue 6
getglobal status; getglobal ctx
pcallk 2 2 5 # call should throw an error and jump to continuation
cannot be here!
]],
[[ # 4th (and last) continuation
return *
]],
-- function called by 3th continuation
function (a,b) x=a; y=b; error("errmsg") end,
...
)
end)
local a = {co(3,4,6)}
assert(a[1] == 6 and a[2] == nil)
a = {co()}; assert(a[1] == nil and _G.status == "YIELD" and _G.ctx == 2)
a = {co()}; assert(a[1] == nil and _G.status == "YIELD" and _G.ctx == 3)
a = {co(7,8)};
-- original arguments
assert(type(a[1]) == 'string' and type(a[2]) == 'string' and
type(a[3]) == 'string' and type(a[4]) == 'string' and
type(a[5]) == 'string' and type(a[6]) == 'function')
-- arguments left from fist resume
assert(a[7] == 3 and a[8] == 4)
-- arguments to last resume
assert(a[9] == 7 and a[10] == 8)
-- error message and nothing more
assert(a[11]:find("errmsg") and #a == 11)
-- check arguments to pcallk
assert(x == "YIELD" and y == 4)
assert(not pcall(co)) -- coroutine should be dead
-- bug in nCcalls
local co = coroutine.wrap(function ()
local a = {pcall(pcall,pcall,pcall,pcall,pcall,pcall,pcall,error,"hi")}
return pcall(assert, table.unpack(a))
end)
local a = {co()}
assert(a[10] == "hi")
print'OK'

861
app/lua53/host/tests/db.lua Normal file
View File

@ -0,0 +1,861 @@
-- $Id: db.lua,v 1.79 2016/11/07 13:02:34 roberto Exp $
-- See Copyright Notice in file all.lua
-- testing debug library
local debug = require "debug"
local function dostring(s) return assert(load(s))() end
print"testing debug library and debug information"
do
local a=1
end
assert(not debug.gethook())
local testline = 19 -- line where 'test' is defined
function test (s, l, p) -- this must be line 19
collectgarbage() -- avoid gc during trace
local function f (event, line)
assert(event == 'line')
local l = table.remove(l, 1)
if p then print(l, line) end
assert(l == line, "wrong trace!!")
end
debug.sethook(f,"l"); load(s)(); debug.sethook()
assert(#l == 0)
end
do
assert(not pcall(debug.getinfo, print, "X")) -- invalid option
assert(not debug.getinfo(1000)) -- out of range level
assert(not debug.getinfo(-1)) -- out of range level
local a = debug.getinfo(print)
assert(a.what == "C" and a.short_src == "[C]")
a = debug.getinfo(print, "L")
assert(a.activelines == nil)
local b = debug.getinfo(test, "SfL")
assert(b.name == nil and b.what == "Lua" and b.linedefined == testline and
b.lastlinedefined == b.linedefined + 10 and
b.func == test and not string.find(b.short_src, "%["))
assert(b.activelines[b.linedefined + 1] and
b.activelines[b.lastlinedefined])
assert(not b.activelines[b.linedefined] and
not b.activelines[b.lastlinedefined + 1])
end
-- test file and string names truncation
a = "function f () end"
local function dostring (s, x) return load(s, x)() end
dostring(a)
assert(debug.getinfo(f).short_src == string.format('[string "%s"]', a))
dostring(a..string.format("; %s\n=1", string.rep('p', 400)))
assert(string.find(debug.getinfo(f).short_src, '^%[string [^\n]*%.%.%."%]$'))
dostring(a..string.format("; %s=1", string.rep('p', 400)))
assert(string.find(debug.getinfo(f).short_src, '^%[string [^\n]*%.%.%."%]$'))
dostring("\n"..a)
assert(debug.getinfo(f).short_src == '[string "..."]')
dostring(a, "")
assert(debug.getinfo(f).short_src == '[string ""]')
dostring(a, "@xuxu")
assert(debug.getinfo(f).short_src == "xuxu")
dostring(a, "@"..string.rep('p', 1000)..'t')
assert(string.find(debug.getinfo(f).short_src, "^%.%.%.p*t$"))
dostring(a, "=xuxu")
assert(debug.getinfo(f).short_src == "xuxu")
dostring(a, string.format("=%s", string.rep('x', 500)))
assert(string.find(debug.getinfo(f).short_src, "^x*$"))
dostring(a, "=")
assert(debug.getinfo(f).short_src == "")
a = nil; f = nil;
repeat
local g = {x = function ()
local a = debug.getinfo(2)
assert(a.name == 'f' and a.namewhat == 'local')
a = debug.getinfo(1)
assert(a.name == 'x' and a.namewhat == 'field')
return 'xixi'
end}
local f = function () return 1+1 and (not 1 or g.x()) end
assert(f() == 'xixi')
g = debug.getinfo(f)
assert(g.what == "Lua" and g.func == f and g.namewhat == "" and not g.name)
function f (x, name) -- local!
name = name or 'f'
local a = debug.getinfo(1)
assert(a.name == name and a.namewhat == 'local')
return x
end
-- breaks in different conditions
if 3>4 then break end; f()
if 3<4 then a=1 else break end; f()
while 1 do local x=10; break end; f()
local b = 1
if 3>4 then return math.sin(1) end; f()
a = 3<4; f()
a = 3<4 or 1; f()
repeat local x=20; if 4>3 then f() else break end; f() until 1
g = {}
f(g).x = f(2) and f(10)+f(9)
assert(g.x == f(19))
function g(x) if not x then return 3 end return (x('a', 'x')) end
assert(g(f) == 'a')
until 1
test([[if
math.sin(1)
then
a=1
else
a=2
end
]], {2,3,4,7})
test([[--
if nil then
a=1
else
a=2
end
]], {2,5,6})
test([[a=1
repeat
a=a+1
until a==3
]], {1,3,4,3,4})
test([[ do
return
end
]], {2})
test([[local a
a=1
while a<=3 do
a=a+1
end
]], {1,2,3,4,3,4,3,4,3,5})
test([[while math.sin(1) do
if math.sin(1)
then break
end
end
a=1]], {1,2,3,6})
test([[for i=1,3 do
a=i
end
]], {1,2,1,2,1,2,1,3})
test([[for i,v in pairs{'a','b'} do
a=tostring(i) .. v
end
]], {1,2,1,2,1,3})
test([[for i=1,4 do a=1 end]], {1,1,1,1,1})
print'+'
-- invalid levels in [gs]etlocal
assert(not pcall(debug.getlocal, 20, 1))
assert(not pcall(debug.setlocal, -1, 1, 10))
-- parameter names
local function foo (a,b,...) local d, e end
local co = coroutine.create(foo)
assert(debug.getlocal(foo, 1) == 'a')
assert(debug.getlocal(foo, 2) == 'b')
assert(not debug.getlocal(foo, 3))
assert(debug.getlocal(co, foo, 1) == 'a')
assert(debug.getlocal(co, foo, 2) == 'b')
assert(not debug.getlocal(co, foo, 3))
assert(not debug.getlocal(print, 1))
-- varargs
local function foo (a, ...)
local t = table.pack(...)
for i = 1, t.n do
local n, v = debug.getlocal(1, -i)
assert(n == "(*vararg)" and v == t[i])
end
assert(not debug.getlocal(1, -(t.n + 1)))
assert(not debug.setlocal(1, -(t.n + 1), 30))
if t.n > 0 then
(function (x)
assert(debug.setlocal(2, -1, x) == "(*vararg)")
assert(debug.setlocal(2, -t.n, x) == "(*vararg)")
end)(430)
assert(... == 430)
end
end
foo()
foo(print)
foo(200, 3, 4)
local a = {}
for i = 1, (_soft and 100 or 1000) do a[i] = i end
foo(table.unpack(a))
a = nil
-- access to vararg in non-vararg function
local function foo () return debug.getlocal(1, -1) end
assert(not foo(10))
do -- test hook presence in debug info
assert(not debug.gethook())
local count = 0
local function f ()
assert(debug.getinfo(1).namewhat == "hook")
local sndline = string.match(debug.traceback(), "\n(.-)\n")
assert(string.find(sndline, "hook"))
count = count + 1
end
debug.sethook(f, "l")
local a = 0
_ENV.a = a
a = 1
debug.sethook()
assert(count == 4)
end
a = {}; L = nil
local glob = 1
local oldglob = glob
debug.sethook(function (e,l)
collectgarbage() -- force GC during a hook
local f, m, c = debug.gethook()
assert(m == 'crl' and c == 0)
if e == "line" then
if glob ~= oldglob then
L = l-1 -- get the first line where "glob" has changed
oldglob = glob
end
elseif e == "call" then
local f = debug.getinfo(2, "f").func
a[f] = 1
else assert(e == "return")
end
end, "crl")
function f(a,b)
collectgarbage()
local _, x = debug.getlocal(1, 1)
local _, y = debug.getlocal(1, 2)
assert(x == a and y == b)
assert(debug.setlocal(2, 3, "pera") == "AA".."AA")
assert(debug.setlocal(2, 4, "ma<EFBFBD><EFBFBD>") == "B")
x = debug.getinfo(2)
assert(x.func == g and x.what == "Lua" and x.name == 'g' and
x.nups == 2 and string.find(x.source, "^@.*db%.lua$"))
glob = glob+1
assert(debug.getinfo(1, "l").currentline == L+1)
assert(debug.getinfo(1, "l").currentline == L+2)
end
function foo()
glob = glob+1
assert(debug.getinfo(1, "l").currentline == L+1)
end; foo() -- set L
-- check line counting inside strings and empty lines
_ = 'alo\
alo' .. [[
]]
--[[
]]
assert(debug.getinfo(1, "l").currentline == L+11) -- check count of lines
function g(...)
local arg = {...}
do local a,b,c; a=math.sin(40); end
local feijao
local AAAA,B = "xuxu", "mam<EFBFBD>o"
f(AAAA,B)
assert(AAAA == "pera" and B == "ma<EFBFBD><EFBFBD>")
do
local B = 13
local x,y = debug.getlocal(1,5)
assert(x == 'B' and y == 13)
end
end
g()
assert(a[f] and a[g] and a[assert] and a[debug.getlocal] and not a[print])
-- tests for manipulating non-registered locals (C and Lua temporaries)
local n, v = debug.getlocal(0, 1)
assert(v == 0 and n == "(*temporary)")
local n, v = debug.getlocal(0, 2)
assert(v == 2 and n == "(*temporary)")
assert(not debug.getlocal(0, 3))
assert(not debug.getlocal(0, 0))
function f()
assert(select(2, debug.getlocal(2,3)) == 1)
assert(not debug.getlocal(2,4))
debug.setlocal(2, 3, 10)
return 20
end
function g(a,b) return (a+1) + f() end
assert(g(0,0) == 30)
debug.sethook(nil);
assert(debug.gethook() == nil)
-- testing access to function arguments
local function collectlocals (level)
local tab = {}
for i = 1, math.huge do
local n, v = debug.getlocal(level + 1, i)
if not (n and string.find(n, "^[a-zA-Z0-9_]+$")) then
break -- consider only real variables
end
tab[n] = v
end
return tab
end
X = nil
a = {}
function a:f (a, b, ...) local arg = {...}; local c = 13 end
debug.sethook(function (e)
assert(e == "call")
dostring("XX = 12") -- test dostring inside hooks
-- testing errors inside hooks
assert(not pcall(load("a='joao'+1")))
debug.sethook(function (e, l)
assert(debug.getinfo(2, "l").currentline == l)
local f,m,c = debug.gethook()
assert(e == "line")
assert(m == 'l' and c == 0)
debug.sethook(nil) -- hook is called only once
assert(not X) -- check that
X = collectlocals(2)
end, "l")
end, "c")
a:f(1,2,3,4,5)
assert(X.self == a and X.a == 1 and X.b == 2 and X.c == nil)
assert(XX == 12)
assert(debug.gethook() == nil)
-- testing access to local variables in return hook (bug in 5.2)
do
local function foo (a, b)
do local x,y,z end
local c, d = 10, 20
return
end
local function aux ()
if debug.getinfo(2).name == "foo" then
foo = nil -- to signal that it found 'foo'
local tab = {a = 100, b = 200, c = 10, d = 20}
for n, v in pairs(collectlocals(2)) do
assert(tab[n] == v)
tab[n] = nil
end
assert(next(tab) == nil) -- 'tab' must be empty
end
end
debug.sethook(aux, "r"); foo(100, 200); debug.sethook()
assert(foo == nil)
end
-- testing upvalue access
local function getupvalues (f)
local t = {}
local i = 1
while true do
local name, value = debug.getupvalue(f, i)
if not name then break end
assert(not t[name])
t[name] = value
i = i + 1
end
return t
end
local a,b,c = 1,2,3
local function foo1 (a) b = a; return c end
local function foo2 (x) a = x; return c+b end
assert(not debug.getupvalue(foo1, 3))
assert(not debug.getupvalue(foo1, 0))
assert(not debug.setupvalue(foo1, 3, "xuxu"))
local t = getupvalues(foo1)
assert(t.a == nil and t.b == 2 and t.c == 3)
t = getupvalues(foo2)
assert(t.a == 1 and t.b == 2 and t.c == 3)
assert(debug.setupvalue(foo1, 1, "xuxu") == "b")
assert(({debug.getupvalue(foo2, 3)})[2] == "xuxu")
-- upvalues of C functions are allways "called" "" (the empty string)
assert(debug.getupvalue(string.gmatch("x", "x"), 1) == "")
-- testing count hooks
local a=0
debug.sethook(function (e) a=a+1 end, "", 1)
a=0; for i=1,1000 do end; assert(1000 < a and a < 1012)
debug.sethook(function (e) a=a+1 end, "", 4)
a=0; for i=1,1000 do end; assert(250 < a and a < 255)
local f,m,c = debug.gethook()
assert(m == "" and c == 4)
debug.sethook(function (e) a=a+1 end, "", 4000)
a=0; for i=1,1000 do end; assert(a == 0)
do
debug.sethook(print, "", 2^24 - 1) -- count upperbound
local f,m,c = debug.gethook()
assert(({debug.gethook()})[3] == 2^24 - 1)
end
debug.sethook()
-- tests for tail calls
local function f (x)
if x then
assert(debug.getinfo(1, "S").what == "Lua")
assert(debug.getinfo(1, "t").istailcall == true)
local tail = debug.getinfo(2)
assert(tail.func == g1 and tail.istailcall == true)
assert(debug.getinfo(3, "S").what == "main")
print"+"
end
end
function g(x) return f(x) end
function g1(x) g(x) end
local function h (x) local f=g1; return f(x) end
h(true)
local b = {}
debug.sethook(function (e) table.insert(b, e) end, "cr")
h(false)
debug.sethook()
local res = {"return", -- first return (from sethook)
"call", "tail call", "call", "tail call",
"return", "return",
"call", -- last call (to sethook)
}
for i = 1, #res do assert(res[i] == table.remove(b, 1)) end
b = 0
debug.sethook(function (e)
if e == "tail call" then
b = b + 1
assert(debug.getinfo(2, "t").istailcall == true)
else
assert(debug.getinfo(2, "t").istailcall == false)
end
end, "c")
h(false)
debug.sethook()
assert(b == 2) -- two tail calls
lim = _soft and 3000 or 30000
local function foo (x)
if x==0 then
assert(debug.getinfo(2).what == "main")
local info = debug.getinfo(1)
assert(info.istailcall == true and info.func == foo)
else return foo(x-1)
end
end
foo(lim)
print"+"
-- testing local function information
co = load[[
local A = function ()
return x
end
return
]]
local a = 0
-- 'A' should be visible to debugger only after its complete definition
debug.sethook(function (e, l)
if l == 3 then a = a + 1; assert(debug.getlocal(2, 1) == "(*temporary)")
elseif l == 4 then a = a + 1; assert(debug.getlocal(2, 1) == "A")
end
end, "l")
co() -- run local function definition
debug.sethook() -- turn off hook
assert(a == 2) -- ensure all two lines where hooked
-- testing traceback
assert(debug.traceback(print) == print)
assert(debug.traceback(print, 4) == print)
assert(string.find(debug.traceback("hi", 4), "^hi\n"))
assert(string.find(debug.traceback("hi"), "^hi\n"))
assert(not string.find(debug.traceback("hi"), "'debug.traceback'"))
assert(string.find(debug.traceback("hi", 0), "'debug.traceback'"))
assert(string.find(debug.traceback(), "^stack traceback:\n"))
do -- C-function names in traceback
local st, msg = (function () return pcall end)()(debug.traceback)
assert(st == true and string.find(msg, "pcall"))
end
-- testing nparams, nups e isvararg
local t = debug.getinfo(print, "u")
assert(t.isvararg == true and t.nparams == 0 and t.nups == 0)
t = debug.getinfo(function (a,b,c) end, "u")
assert(t.isvararg == false and t.nparams == 3 and t.nups == 0)
t = debug.getinfo(function (a,b,...) return t[a] end, "u")
assert(t.isvararg == true and t.nparams == 2 and t.nups == 1)
t = debug.getinfo(1) -- main
assert(t.isvararg == true and t.nparams == 0 and t.nups == 1 and
debug.getupvalue(t.func, 1) == "_ENV")
-- testing debugging of coroutines
local function checktraceback (co, p, level)
local tb = debug.traceback(co, nil, level)
local i = 0
for l in string.gmatch(tb, "[^\n]+\n?") do
assert(i == 0 or string.find(l, p[i]))
i = i+1
end
assert(p[i] == nil)
end
local function f (n)
if n > 0 then f(n-1)
else coroutine.yield() end
end
local co = coroutine.create(f)
coroutine.resume(co, 3)
checktraceback(co, {"yield", "db.lua", "db.lua", "db.lua", "db.lua"})
checktraceback(co, {"db.lua", "db.lua", "db.lua", "db.lua"}, 1)
checktraceback(co, {"db.lua", "db.lua", "db.lua"}, 2)
checktraceback(co, {"db.lua"}, 4)
checktraceback(co, {}, 40)
co = coroutine.create(function (x)
local a = 1
coroutine.yield(debug.getinfo(1, "l"))
coroutine.yield(debug.getinfo(1, "l").currentline)
return a
end)
local tr = {}
local foo = function (e, l) if l then table.insert(tr, l) end end
debug.sethook(co, foo, "lcr")
local _, l = coroutine.resume(co, 10)
local x = debug.getinfo(co, 1, "lfLS")
assert(x.currentline == l.currentline and x.activelines[x.currentline])
assert(type(x.func) == "function")
for i=x.linedefined + 1, x.lastlinedefined do
assert(x.activelines[i])
x.activelines[i] = nil
end
assert(next(x.activelines) == nil) -- no 'extra' elements
assert(not debug.getinfo(co, 2))
local a,b = debug.getlocal(co, 1, 1)
assert(a == "x" and b == 10)
a,b = debug.getlocal(co, 1, 2)
assert(a == "a" and b == 1)
debug.setlocal(co, 1, 2, "hi")
assert(debug.gethook(co) == foo)
assert(#tr == 2 and
tr[1] == l.currentline-1 and tr[2] == l.currentline)
a,b,c = pcall(coroutine.resume, co)
assert(a and b and c == l.currentline+1)
checktraceback(co, {"yield", "in function <"})
a,b = coroutine.resume(co)
assert(a and b == "hi")
assert(#tr == 4 and tr[4] == l.currentline+2)
assert(debug.gethook(co) == foo)
assert(not debug.gethook())
checktraceback(co, {})
-- check get/setlocal in coroutines
co = coroutine.create(function (x)
local a, b = coroutine.yield(x)
assert(a == 100 and b == nil)
return x
end)
a, b = coroutine.resume(co, 10)
assert(a and b == 10)
a, b = debug.getlocal(co, 1, 1)
assert(a == "x" and b == 10)
assert(not debug.getlocal(co, 1, 5))
assert(debug.setlocal(co, 1, 1, 30) == "x")
assert(not debug.setlocal(co, 1, 5, 40))
a, b = coroutine.resume(co, 100)
assert(a and b == 30)
-- check traceback of suspended (or dead with error) coroutines
function f(i) if i==0 then error(i) else coroutine.yield(); f(i-1) end end
co = coroutine.create(function (x) f(x) end)
a, b = coroutine.resume(co, 3)
t = {"'coroutine.yield'", "'f'", "in function <"}
while coroutine.status(co) == "suspended" do
checktraceback(co, t)
a, b = coroutine.resume(co)
table.insert(t, 2, "'f'") -- one more recursive call to 'f'
end
t[1] = "'error'"
checktraceback(co, t)
-- test acessing line numbers of a coroutine from a resume inside
-- a C function (this is a known bug in Lua 5.0)
local function g(x)
coroutine.yield(x)
end
local function f (i)
debug.sethook(function () end, "l")
for j=1,1000 do
g(i+j)
end
end
local co = coroutine.wrap(f)
co(10)
pcall(co)
pcall(co)
assert(type(debug.getregistry()) == "table")
-- test tagmethod information
local a = {}
local function f (t)
local info = debug.getinfo(1);
assert(info.namewhat == "metamethod")
a.op = info.name
return info.name
end
setmetatable(a, {
__index = f; __add = f; __div = f; __mod = f; __concat = f; __pow = f;
__mul = f; __idiv = f; __unm = f; __len = f; __sub = f;
__shl = f; __shr = f; __bor = f; __bxor = f;
__eq = f; __le = f; __lt = f; __unm = f; __len = f; __band = f;
__bnot = f;
})
local b = setmetatable({}, getmetatable(a))
assert(a[3] == "__index" and a^3 == "__pow" and a..a == "__concat")
assert(a/3 == "__div" and 3%a == "__mod")
assert(a+3 == "__add" and 3-a == "__sub" and a*3 == "__mul" and
-a == "__unm" and #a == "__len" and a&3 == "__band")
assert(a|3 == "__bor" and 3~a == "__bxor" and a<<3 == "__shl" and
a>>1 == "__shr")
assert (a==b and a.op == "__eq")
assert (a>=b and a.op == "__le")
assert (a>b and a.op == "__lt")
assert(~a == "__bnot")
do -- testing for-iterator name
local function f()
assert(debug.getinfo(1).name == "for iterator")
end
for i in f do end
end
do -- testing debug info for finalizers
local name = nil
-- create a piece of garbage with a finalizer
setmetatable({}, {__gc = function ()
local t = debug.getinfo(2) -- get callee information
assert(t.namewhat == "metamethod")
name = t.name
end})
-- repeat until previous finalizer runs (setting 'name')
repeat local a = {} until name
assert(name == "__gc")
end
do
print("testing traceback sizes")
local function countlines (s)
return select(2, string.gsub(s, "\n", ""))
end
local function deep (lvl, n)
if lvl == 0 then
return (debug.traceback("message", n))
else
return (deep(lvl-1, n))
end
end
local function checkdeep (total, start)
local s = deep(total, start)
local rest = string.match(s, "^message\nstack traceback:\n(.*)$")
local cl = countlines(rest)
-- at most 10 lines in first part, 11 in second, plus '...'
assert(cl <= 10 + 11 + 1)
local brk = string.find(rest, "%.%.%.")
if brk then -- does message have '...'?
local rest1 = string.sub(rest, 1, brk)
local rest2 = string.sub(rest, brk, #rest)
assert(countlines(rest1) == 10 and countlines(rest2) == 11)
else
assert(cl == total - start + 2)
end
end
for d = 1, 51, 10 do
for l = 1, d do
-- use coroutines to ensure complete control of the stack
coroutine.wrap(checkdeep)(d, l)
end
end
end
print("testing debug functions on chunk without debug info")
prog = [[-- program to be loaded without debug information
local debug = require'debug'
local a = 12 -- a local variable
local n, v = debug.getlocal(1, 1)
assert(n == "(*temporary)" and v == debug) -- unkown name but known value
n, v = debug.getlocal(1, 2)
assert(n == "(*temporary)" and v == 12) -- unkown name but known value
-- a function with an upvalue
local f = function () local x; return a end
n, v = debug.getupvalue(f, 1)
assert(n == "(*no name)" and v == 12)
assert(debug.setupvalue(f, 1, 13) == "(*no name)")
assert(a == 13)
local t = debug.getinfo(f)
assert(t.name == nil and t.linedefined > 0 and
t.lastlinedefined == t.linedefined
-- and t.short_src == "?"
)
assert(debug.getinfo(1).currentline == -1)
t = debug.getinfo(f, "L").activelines
assert(next(t) == nil) -- active lines are empty
-- dump/load a function without debug info
f = load(string.dump(f))
t = debug.getinfo(f)
assert(t.name == nil and t.linedefined > 0 and
t.lastlinedefined == t.linedefined
-- and t.short_src == "?"
)
assert(debug.getinfo(1).currentline == -1)
return a
]]
-- load 'prog' without debug info
local f = assert(load(string.dump(load(prog), true)))
assert(f() == 13)
do -- tests for 'source' in binary dumps
local prog = [[
return function (x)
return function (y)
return x + y
end
end
]]
local name = string.rep("x", 1000)
local p = assert(load(prog, name))
-- load 'p' as a binary chunk with debug information
local c = string.dump(p)
assert(#c > 1000 and #c < 2000) -- no repetition of 'source' in dump
local f = assert(load(c))
local g = f()
local h = g(3)
assert(h(5) == 8)
assert(debug.getinfo(f).source == name and -- all functions have 'source'
debug.getinfo(g).source == name and
debug.getinfo(h).source == name)
-- again, without debug info
local c = string.dump(p, true)
--[=[ TODO: NodeMCU need to work out why there is a source in the dmp
assert(#c < 500) -- no 'source' in dump
local f = assert(load(c))
local g = f()
local h = g(30)
assert(h(50) == 80)
assert(debug.getinfo(f).source == '=?' and -- no function has 'source'
debug.getinfo(g).source == '=?' and
debug.getinfo(h).source == '=?')
]=]
end
print"OK"

View File

@ -0,0 +1,538 @@
-- $Id: errors.lua,v 1.94 2016/12/21 19:23:02 roberto Exp $
-- See Copyright Notice in file all.lua
print("testing errors")
local debug = require"debug"
--[[ NodeMCU: uses getmetatable(_G) so remove this check
-- avoid problems with 'strict' module (which may generate other error messages)
local mt = getmetatable(_G) or {}
local oldmm = mt.__index
mt.__index = nil
]]
local function checkerr (msg, f, ...)
local st, err = pcall(f, ...)
assert(not st and string.find(err, msg))
end
local function doit (s)
local f, msg = load(s)
if f == nil then return msg end
local cond, msg = pcall(f)
return (not cond) and msg
end
local function checkmessage (prog, msg)
local m = doit(prog)
assert(string.find(m, msg, 1, true))
end
local function checksyntax (prog, extra, token, line)
local msg = doit(prog)
if not string.find(token, "^<%a") and not string.find(token, "^char%(")
then token = "'"..token.."'" end
token = string.gsub(token, "(%p)", "%%%1")
local pt = string.format([[^%%[string ".*"%%]:%d: .- near %s$]],
line, token)
assert(string.find(msg, pt))
assert(string.find(msg, msg, 1, true))
end
-- test error message with no extra info
assert(doit("error('hi', 0)") == 'hi')
-- test error message with no info
assert(doit("error()") == nil)
-- test common errors/errors that crashed in the past
assert(doit("table.unpack({}, 1, n=2^30)"))
assert(doit("a=math.sin()"))
assert(not doit("tostring(1)") and doit("tostring()"))
assert(doit"tonumber()")
assert(doit"repeat until 1; a")
assert(doit"return;;")
assert(doit"assert(false)")
assert(doit"assert(nil)")
assert(doit("function a (... , ...) end"))
assert(doit("function a (, ...) end"))
assert(doit("local t={}; t = t[#t] + 1"))
checksyntax([[
local a = {4
]], "'}' expected (to close '{' at line 1)", "<eof>", 3)
-- tests for better error messages
checkmessage("a = {} + 1", "arithmetic")
checkmessage("a = {} | 1", "bitwise operation")
checkmessage("a = {} < 1", "attempt to compare")
checkmessage("a = {} <= 1", "attempt to compare")
checkmessage("a=1; bbbb=2; a=math.sin(3)+bbbb(3)", "global 'bbbb'")
checkmessage("a={}; do local a=1 end a:bbbb(3)", "method 'bbbb'")
checkmessage("local a={}; a.bbbb(3)", "field 'bbbb'")
assert(not string.find(doit"a={13}; local bbbb=1; a[bbbb](3)", "'bbbb'"))
checkmessage("a={13}; local bbbb=1; a[bbbb](3)", "number")
checkmessage("a=(1)..{}", "a table value")
checkmessage("a = #print", "length of a function value")
checkmessage("a = #3", "length of a number value")
aaa = nil
checkmessage("aaa.bbb:ddd(9)", "global 'aaa'")
checkmessage("local aaa={bbb=1}; aaa.bbb:ddd(9)", "field 'bbb'")
checkmessage("local aaa={bbb={}}; aaa.bbb:ddd(9)", "method 'ddd'")
checkmessage("local a,b,c; (function () a = b+1 end)()", "upvalue 'b'")
assert(not doit"local aaa={bbb={ddd=next}}; aaa.bbb:ddd(nil)")
-- upvalues being indexed do not go to the stack
checkmessage("local a,b,cc; (function () a = cc[1] end)()", "upvalue 'cc'")
checkmessage("local a,b,cc; (function () a.x = 1 end)()", "upvalue 'a'")
checkmessage("local _ENV = {x={}}; a = a + 1", "global 'a'")
checkmessage("b=1; local aaa='a'; x=aaa+b", "local 'aaa'")
checkmessage("aaa={}; x=3/aaa", "global 'aaa'")
checkmessage("aaa='2'; b=nil;x=aaa*b", "global 'b'")
checkmessage("aaa={}; x=-aaa", "global 'aaa'")
-- short circuit
checkmessage("a=1; local a,bbbb=2,3; a = math.sin(1) and bbbb(3)",
"local 'bbbb'")
checkmessage("a=1; local a,bbbb=2,3; a = bbbb(1) or a(3)", "local 'bbbb'")
checkmessage("local a,b,c,f = 1,1,1; f((a and b) or c)", "local 'f'")
checkmessage("local a,b,c = 1,1,1; ((a and b) or c)()", "call a number value")
assert(not string.find(doit"aaa={}; x=(aaa or aaa)+(aaa and aaa)", "'aaa'"))
assert(not string.find(doit"aaa={}; (aaa or aaa)()", "'aaa'"))
checkmessage("print(print < 10)", "function with number")
checkmessage("print(print < print)", "two function values")
checkmessage("print('10' < 10)", "string with number")
checkmessage("print(10 < '23')", "number with string")
-- float->integer conversions
checkmessage("local a = 2.0^100; x = a << 2", "local a")
checkmessage("local a = 1 >> 2.0^100", "has no integer representation")
checkmessage("local a = '10' << 2.0^100", "has no integer representation")
checkmessage("local a = 2.0^100 & 1", "has no integer representation")
checkmessage("local a = 2.0^100 & '1'", "has no integer representation")
checkmessage("local a = 2.0 | 1e40", "has no integer representation")
checkmessage("local a = 2e100 ~ 1", "has no integer representation")
checkmessage("string.sub('a', 2.0^100)", "has no integer representation")
checkmessage("string.rep('a', 3.3)", "has no integer representation")
checkmessage("return 6e40 & 7", "has no integer representation")
checkmessage("return 34 << 7e30", "has no integer representation")
checkmessage("return ~-3e40", "has no integer representation")
checkmessage("return ~-3.009", "has no integer representation")
checkmessage("return 3.009 & 1", "has no integer representation")
checkmessage("return 34 >> {}", "table value")
checkmessage("a = 24 // 0", "divide by zero")
checkmessage("a = 1 % 0", "'n%0'")
-- passing light userdata instead of full userdata
_G.D = debug
checkmessage([[
-- create light udata
local x = D.upvalueid(function () return debug end, 1)
D.setuservalue(x, {})
]], "light userdata")
_G.D = nil
do -- named objects (field '__name')
checkmessage("math.sin(io.input())", "(number expected, got FILE*)")
_G.XX = setmetatable({}, {__name = "My Type"})
assert(string.find(tostring(XX), "^My Type"))
checkmessage("io.input(XX)", "(FILE* expected, got My Type)")
checkmessage("return XX + 1", "on a My Type value")
checkmessage("return ~io.stdin", "on a FILE* value")
checkmessage("return XX < XX", "two My Type values")
checkmessage("return {} < XX", "table with My Type")
checkmessage("return XX < io.stdin", "My Type with FILE*")
_G.XX = nil
end
-- global functions
checkmessage("(io.write or print){}", "io.write")
checkmessage("(collectgarbage or print){}", "collectgarbage")
-- errors in functions without debug info
do
local f = function (a) return a + 1 end
f = assert(load(string.dump(f, true)))
assert(f(3) == 4)
print (pcall(f, {}))
checkerr(":%-1:", f, {}) -- NodeMCU known issue with retaining source info
-- code with a move to a local var ('OP_MOV A B' with A<B)
f = function () local a; a = {}; return a + 2 end
-- no debug info (so that 'a' is unknown)
f = assert(load(string.dump(f, true)))
-- symbolic execution should not get lost
checkerr(":%-1:.*table value", f) -- NodeMCU known issue with retaining source info
end
-- tests for field accesses after RK limit
local t = {}
for i = 1, 1000 do
t[i] = "a = x" .. i
end
local s = table.concat(t, "; ")
t = nil
checkmessage(s.."; a = bbb + 1", "global 'bbb'")
checkmessage("local _ENV=_ENV;"..s.."; a = bbb + 1", "global 'bbb'")
checkmessage(s.."; local t = {}; a = t.bbb + 1", "field 'bbb'")
checkmessage(s.."; local t = {}; t:bbb()", "method 'bbb'")
checkmessage([[aaa=9
repeat until 3==3
local x=math.sin(math.cos(3))
if math.sin(1) == x then return math.sin(1) end -- tail call
local a,b = 1, {
{x='a'..'b'..'c', y='b', z=x},
{1,2,3,4,5} or 3+3<=3+3,
3+1>3+1,
{d = x and aaa[x or y]}}
]], "global 'aaa'")
checkmessage([[
local x,y = {},1
if math.sin(1) == 0 then return 3 end -- return
x.a()]], "field 'a'")
checkmessage([[
prefix = nil
insert = nil
while 1 do
local a
if nil then break end
insert(prefix, a)
end]], "global 'insert'")
checkmessage([[ -- tail call
return math.sin("a")
]], "'sin'")
checkmessage([[collectgarbage("nooption")]], "invalid option")
checkmessage([[x = print .. "a"]], "concatenate")
checkmessage([[x = "a" .. false]], "concatenate")
checkmessage([[x = {} .. 2]], "concatenate")
checkmessage("getmetatable(io.stdin).__gc()", "no value")
checkmessage([[
local Var
local function main()
NoSuchName (function() Var=0 end)
end
main()
]], "global 'NoSuchName'")
print'+'
a = {}; setmetatable(a, {__index = string})
checkmessage("a:sub()", "bad self")
checkmessage("string.sub('a', {})", "#2")
checkmessage("('a'):sub{}", "#1")
checkmessage("table.sort({1,2,3}, table.sort)", "'table.sort'")
checkmessage("string.gsub('s', 's', setmetatable)", "'setmetatable'")
-- tests for errors in coroutines
local function f (n)
local c = coroutine.create(f)
local a,b = coroutine.resume(c)
return b
end
assert(string.find(f(), "C stack overflow"))
checkmessage("coroutine.yield()", "outside a coroutine")
f = coroutine.wrap(function () table.sort({1,2,3}, coroutine.yield) end)
checkerr("yield across", f)
-- testing size of 'source' info; size of buffer for that info is
-- LUA_IDSIZE, declared as 60 in luaconf. Get one position for '\0'.
idsize = 60 - 1
local function checksize (source)
-- syntax error
local _, msg = load("x", source)
msg = string.match(msg, "^([^:]*):") -- get source (1st part before ':')
assert(msg:len() <= idsize)
end
for i = 60 - 10, 60 + 10 do -- check border cases around 60
checksize("@" .. string.rep("x", i)) -- file names
checksize(string.rep("x", i - 10)) -- string sources
checksize("=" .. string.rep("x", i)) -- exact sources
end
-- testing line error
local function lineerror (s, l)
local err,msg = pcall(load(s))
local line = string.match(msg, ":(%d+):")
assert((line and line+0) == l)
end
lineerror("local a\n for i=1,'a' do \n print(i) \n end", 2)
lineerror("\n local a \n for k,v in 3 \n do \n print(k) \n end", 3)
lineerror("\n\n for k,v in \n 3 \n do \n print(k) \n end", 4)
lineerror("function a.x.y ()\na=a+1\nend", 1)
lineerror("a = \na\n+\n{}", 3)
lineerror("a = \n3\n+\n(\n4\n/\nprint)", 6)
lineerror("a = \nprint\n+\n(\n4\n/\n7)", 3)
lineerror("a\n=\n-\n\nprint\n;", 3)
lineerror([[
a
(
23)
]], 1)
lineerror([[
local a = {x = 13}
a
.
x
(
23
)
]], 2)
lineerror([[
local a = {x = 13}
a
.
x
(
23 + a
)
]], 6)
local p = [[
function g() f() end
function f(x) error('a', X) end
g()
]]
X=3;lineerror((p), 3)
X=0;lineerror((p), nil)
X=1;lineerror((p), 2)
X=2;lineerror((p), 1)
if not _soft then
-- several tests that exaust the Lua stack
collectgarbage()
print"testing stack overflow"
C = 0
local l = debug.getinfo(1, "l").currentline; function y () C=C+1; y() end
local function checkstackmessage (m)
return (string.find(m, "^.-:%d+: stack overflow"))
end
-- repeated stack overflows (to check stack recovery)
assert(checkstackmessage(doit('y()')))
print('+')
assert(checkstackmessage(doit('y()')))
print('+')
assert(checkstackmessage(doit('y()')))
print('+')
-- error lines in stack overflow
C = 0
local l1
local function g(x)
l1 = debug.getinfo(x, "l").currentline; y()
end
local _, stackmsg = xpcall(g, debug.traceback, 1)
print('+')
local stack = {}
for line in string.gmatch(stackmsg, "[^\n]*") do
local curr = string.match(line, ":(%d+):")
if curr then table.insert(stack, tonumber(curr)) end
end
local i=1
while stack[i] ~= l1 do
assert(stack[i] == l)
i = i+1
end
assert(i > 15)
-- error in error handling
local res, msg = xpcall(error, error)
assert(not res and type(msg) == 'string')
print('+')
local function f (x)
if x==0 then error('a\n')
else
local aux = function () return f(x-1) end
local a,b = xpcall(aux, aux)
return a,b
end
end
f(3)
local function loop (x,y,z) return 1 + loop(x, y, z) end
local res, msg = xpcall(loop, function (m)
assert(string.find(m, "stack overflow"))
checkerr("error handling", loop)
assert(math.sin(0) == 0)
return 15
end)
assert(msg == 15)
local f = function ()
for i = 999900, 1000000, 1 do table.unpack({}, 1, i) end
end
checkerr("too many results", f)
end
do
-- non string messages
local t = {}
local res, msg = pcall(function () error(t) end)
assert(not res and msg == t)
res, msg = pcall(function () error(nil) end)
assert(not res and msg == nil)
local function f() error{msg='x'} end
res, msg = xpcall(f, function (r) return {msg=r.msg..'y'} end)
assert(msg.msg == 'xy')
-- 'assert' with extra arguments
res, msg = pcall(assert, false, "X", t)
assert(not res and msg == "X")
-- 'assert' with no message
res, msg = pcall(function () assert(false) end)
local line = string.match(msg, "%w+%.lua:(%d+): assertion failed!$")
assert(tonumber(line) == debug.getinfo(1, "l").currentline - 2)
-- 'assert' with non-string messages
res, msg = pcall(assert, false, t)
assert(not res and msg == t)
res, msg = pcall(assert, nil, nil)
assert(not res and msg == nil)
-- 'assert' without arguments
res, msg = pcall(assert)
assert(not res and string.find(msg, "value expected"))
end
-- xpcall with arguments
a, b, c = xpcall(string.find, error, "alo", "al")
assert(a and b == 1 and c == 2)
a, b, c = xpcall(string.find, function (x) return {} end, true, "al")
assert(not a and type(b) == "table" and c == nil)
print("testing tokens in error messages")
checksyntax("syntax error", "", "error", 1)
checksyntax("1.000", "", "1.000", 1)
checksyntax("[[a]]", "", "[[a]]", 1)
checksyntax("'aa'", "", "'aa'", 1)
checksyntax("while << do end", "", "<<", 1)
checksyntax("for >> do end", "", ">>", 1)
-- test invalid non-printable char in a chunk
checksyntax("a\1a = 1", "", "<\\1>", 1)
-- test 255 as first char in a chunk
checksyntax("\255a = 1", "", "<\\255>", 1)
doit('I = load("a=9+"); a=3')
assert(a==3 and I == nil)
print('+')
lim = 1000
if _soft then lim = 100 end
for i=1,lim do
doit('a = ')
doit('a = 4+nil')
end
-- testing syntax limits
local maxClevel = 200 -- LUAI_MAXCCALLS (in llimits.h)
local function testrep (init, rep, close, repc)
local s = init .. string.rep(rep, maxClevel - 10) .. close ..
string.rep(repc, maxClevel - 10)
assert(load(s)) -- 190 levels is OK
s = init .. string.rep(rep, maxClevel + 1)
checkmessage(s, "too many C levels")
end
testrep("local a; a", ",a", "= 1", ",1") -- multiple assignment
testrep("local a; a=", "{", "0", "}")
testrep("local a; a=", "(", "2", ")")
testrep("local a; ", "a(", "2", ")")
testrep("", "do ", "", " end")
testrep("", "while a do ", "", " end")
testrep("local a; ", "if a then else ", "", " end")
testrep("", "function foo () ", "", " end")
testrep("local a; a=", "a..", "a", "")
testrep("local a; a=", "a^", "a", "")
checkmessage("a = f(x" .. string.rep(",x", 260) .. ")", "too many registers")
-- testing other limits
-- upvalues
local lim = 127
local s = "local function fooA ()\n local "
for j = 1,lim do
s = s.."a"..j..", "
end
s = s.."b,c\n"
s = s.."local function fooB ()\n local "
for j = 1,lim do
s = s.."b"..j..", "
end
s = s.."b\n"
s = s.."function fooC () return b+c"
local c = 1+2
for j = 1,lim do
s = s.."+a"..j.."+b"..j
c = c + 2
end
s = s.."\nend end end"
local a,b = load(s)
assert(c > 255 and string.find(b, "too many upvalues") and
string.find(b, "line 5"))
-- local variables
s = "\nfunction foo ()\n local "
for j = 1,300 do
s = s.."a"..j..", "
end
s = s.."b\n"
local a,b = load(s)
assert(string.find(b, "line 2") and string.find(b, "too many local variables"))
print('OK')

View File

@ -0,0 +1,456 @@
-- $Id: events.lua,v 1.45 2016/12/21 19:23:02 roberto Exp $
-- See Copyright Notice in file all.lua
print('testing metatables')
local debug = require'debug'
X = 20; B = 30
_ENV = setmetatable({}, {__index=_G})
collectgarbage()
X = X+10
assert(X == 30 and _G.X == 20)
B = false
assert(B == false)
B = nil
assert(B == 30)
assert(getmetatable{} == nil)
assert(getmetatable(4) == nil)
assert(getmetatable(nil) == nil)
a={name = "NAME"}; setmetatable(a, {__metatable = "xuxu",
__tostring=function(x) return x.name end})
assert(getmetatable(a) == "xuxu")
assert(tostring(a) == "NAME")
-- cannot change a protected metatable
assert(pcall(setmetatable, a, {}) == false)
a.name = "gororoba"
assert(tostring(a) == "gororoba")
local a, t = {10,20,30; x="10", y="20"}, {}
assert(setmetatable(a,t) == a)
assert(getmetatable(a) == t)
assert(setmetatable(a,nil) == a)
assert(getmetatable(a) == nil)
assert(setmetatable(a,t) == a)
function f (t, i, e)
assert(not e)
local p = rawget(t, "parent")
return (p and p[i]+3), "dummy return"
end
t.__index = f
a.parent = {z=25, x=12, [4] = 24}
assert(a[1] == 10 and a.z == 28 and a[4] == 27 and a.x == "10")
collectgarbage()
a = setmetatable({}, t)
function f(t, i, v) rawset(t, i, v-3) end
setmetatable(t, t) -- causes a bug in 5.1 !
t.__newindex = f
a[1] = 30; a.x = "101"; a[5] = 200
assert(a[1] == 27 and a.x == 98 and a[5] == 197)
do -- bug in Lua 5.3.2
local mt = {}
mt.__newindex = mt
local t = setmetatable({}, mt)
t[1] = 10 -- will segfault on some machines
assert(mt[1] == 10)
end
local c = {}
a = setmetatable({}, t)
t.__newindex = c
a[1] = 10; a[2] = 20; a[3] = 90
assert(c[1] == 10 and c[2] == 20 and c[3] == 90)
do
local a;
a = setmetatable({}, {__index = setmetatable({},
{__index = setmetatable({},
{__index = function (_,n) return a[n-3]+4, "lixo" end})})})
a[0] = 20
for i=0,10 do
assert(a[i*3] == 20 + i*4)
end
end
do -- newindex
local foi
local a = {}
for i=1,10 do a[i] = 0; a['a'..i] = 0; end
setmetatable(a, {__newindex = function (t,k,v) foi=true; rawset(t,k,v) end})
foi = false; a[1]=0; assert(not foi)
foi = false; a['a1']=0; assert(not foi)
foi = false; a['a11']=0; assert(foi)
foi = false; a[11]=0; assert(foi)
foi = false; a[1]=nil; assert(not foi)
foi = false; a[1]=nil; assert(foi)
end
setmetatable(t, nil)
function f (t, ...) return t, {...} end
t.__call = f
do
local x,y = a(table.unpack{'a', 1})
assert(x==a and y[1]=='a' and y[2]==1 and y[3]==nil)
x,y = a()
assert(x==a and y[1]==nil)
end
local b = setmetatable({}, t)
setmetatable(b,t)
function f(op)
return function (...) cap = {[0] = op, ...} ; return (...) end
end
t.__add = f("add")
t.__sub = f("sub")
t.__mul = f("mul")
t.__div = f("div")
t.__idiv = f("idiv")
t.__mod = f("mod")
t.__unm = f("unm")
t.__pow = f("pow")
t.__len = f("len")
t.__band = f("band")
t.__bor = f("bor")
t.__bxor = f("bxor")
t.__shl = f("shl")
t.__shr = f("shr")
t.__bnot = f("bnot")
assert(b+5 == b)
assert(cap[0] == "add" and cap[1] == b and cap[2] == 5 and cap[3]==nil)
assert(b+'5' == b)
assert(cap[0] == "add" and cap[1] == b and cap[2] == '5' and cap[3]==nil)
assert(5+b == 5)
assert(cap[0] == "add" and cap[1] == 5 and cap[2] == b and cap[3]==nil)
assert('5'+b == '5')
assert(cap[0] == "add" and cap[1] == '5' and cap[2] == b and cap[3]==nil)
b=b-3; assert(getmetatable(b) == t)
assert(5-a == 5)
assert(cap[0] == "sub" and cap[1] == 5 and cap[2] == a and cap[3]==nil)
assert('5'-a == '5')
assert(cap[0] == "sub" and cap[1] == '5' and cap[2] == a and cap[3]==nil)
assert(a*a == a)
assert(cap[0] == "mul" and cap[1] == a and cap[2] == a and cap[3]==nil)
assert(a/0 == a)
assert(cap[0] == "div" and cap[1] == a and cap[2] == 0 and cap[3]==nil)
assert(a%2 == a)
assert(cap[0] == "mod" and cap[1] == a and cap[2] == 2 and cap[3]==nil)
assert(a // (1/0) == a)
assert(cap[0] == "idiv" and cap[1] == a and cap[2] == 1/0 and cap[3]==nil)
assert(a & "hi" == a)
assert(cap[0] == "band" and cap[1] == a and cap[2] == "hi" and cap[3]==nil)
assert(a | "hi" == a)
assert(cap[0] == "bor" and cap[1] == a and cap[2] == "hi" and cap[3]==nil)
assert("hi" ~ a == "hi")
assert(cap[0] == "bxor" and cap[1] == "hi" and cap[2] == a and cap[3]==nil)
assert(-a == a)
assert(cap[0] == "unm" and cap[1] == a)
assert(a^4 == a)
assert(cap[0] == "pow" and cap[1] == a and cap[2] == 4 and cap[3]==nil)
assert(a^'4' == a)
assert(cap[0] == "pow" and cap[1] == a and cap[2] == '4' and cap[3]==nil)
assert(4^a == 4)
assert(cap[0] == "pow" and cap[1] == 4 and cap[2] == a and cap[3]==nil)
assert('4'^a == '4')
assert(cap[0] == "pow" and cap[1] == '4' and cap[2] == a and cap[3]==nil)
assert(#a == a)
assert(cap[0] == "len" and cap[1] == a)
assert(~a == a)
assert(cap[0] == "bnot" and cap[1] == a)
assert(a << 3 == a)
assert(cap[0] == "shl" and cap[1] == a and cap[2] == 3)
assert(1.5 >> a == 1.5)
assert(cap[0] == "shr" and cap[1] == 1.5 and cap[2] == a)
-- test for rawlen
t = setmetatable({1,2,3}, {__len = function () return 10 end})
assert(#t == 10 and rawlen(t) == 3)
assert(rawlen"abc" == 3)
assert(not pcall(rawlen, io.stdin))
assert(not pcall(rawlen, 34))
assert(not pcall(rawlen))
-- rawlen for long strings
assert(rawlen(string.rep('a', 1000)) == 1000)
t = {}
t.__lt = function (a,b,c)
collectgarbage()
assert(c == nil)
if type(a) == 'table' then a = a.x end
if type(b) == 'table' then b = b.x end
return a<b, "dummy"
end
function Op(x) return setmetatable({x=x}, t) end
local function test ()
assert(not(Op(1)<Op(1)) and (Op(1)<Op(2)) and not(Op(2)<Op(1)))
assert(not(1 < Op(1)) and (Op(1) < 2) and not(2 < Op(1)))
assert(not(Op('a')<Op('a')) and (Op('a')<Op('b')) and not(Op('b')<Op('a')))
assert(not('a' < Op('a')) and (Op('a') < 'b') and not(Op('b') < Op('a')))
assert((Op(1)<=Op(1)) and (Op(1)<=Op(2)) and not(Op(2)<=Op(1)))
assert((Op('a')<=Op('a')) and (Op('a')<=Op('b')) and not(Op('b')<=Op('a')))
assert(not(Op(1)>Op(1)) and not(Op(1)>Op(2)) and (Op(2)>Op(1)))
assert(not(Op('a')>Op('a')) and not(Op('a')>Op('b')) and (Op('b')>Op('a')))
assert((Op(1)>=Op(1)) and not(Op(1)>=Op(2)) and (Op(2)>=Op(1)))
assert((1 >= Op(1)) and not(1 >= Op(2)) and (Op(2) >= 1))
assert((Op('a')>=Op('a')) and not(Op('a')>=Op('b')) and (Op('b')>=Op('a')))
assert(('a' >= Op('a')) and not(Op('a') >= 'b') and (Op('b') >= Op('a')))
end
test()
t.__le = function (a,b,c)
assert(c == nil)
if type(a) == 'table' then a = a.x end
if type(b) == 'table' then b = b.x end
return a<=b, "dummy"
end
test() -- retest comparisons, now using both `lt' and `le'
-- test `partial order'
local function rawSet(x)
local y = {}
for _,k in pairs(x) do y[k] = 1 end
return y
end
local function Set(x)
return setmetatable(rawSet(x), t)
end
t.__lt = function (a,b)
for k in pairs(a) do
if not b[k] then return false end
b[k] = nil
end
return next(b) ~= nil
end
t.__le = nil
assert(Set{1,2,3} < Set{1,2,3,4})
assert(not(Set{1,2,3,4} < Set{1,2,3,4}))
assert((Set{1,2,3,4} <= Set{1,2,3,4}))
assert((Set{1,2,3,4} >= Set{1,2,3,4}))
assert((Set{1,3} <= Set{3,5})) -- wrong!! model needs a `le' method ;-)
t.__le = function (a,b)
for k in pairs(a) do
if not b[k] then return false end
end
return true
end
assert(not (Set{1,3} <= Set{3,5})) -- now its OK!
assert(not(Set{1,3} <= Set{3,5}))
assert(not(Set{1,3} >= Set{3,5}))
t.__eq = function (a,b)
for k in pairs(a) do
if not b[k] then return false end
b[k] = nil
end
return next(b) == nil
end
local s = Set{1,3,5}
assert(s == Set{3,5,1})
assert(not rawequal(s, Set{3,5,1}))
assert(rawequal(s, s))
assert(Set{1,3,5,1} == rawSet{3,5,1})
assert(rawSet{1,3,5,1} == Set{3,5,1})
assert(Set{1,3,5} ~= Set{3,5,1,6})
-- '__eq' is not used for table accesses
t[Set{1,3,5}] = 1
assert(t[Set{1,3,5}] == nil)
if not T then
(Message or print)('\n >>> testC not active: skipping tests for \z
userdata equality <<<\n')
else
local u1 = T.newuserdata(0)
local u2 = T.newuserdata(0)
local u3 = T.newuserdata(0)
assert(u1 ~= u2 and u1 ~= u3)
debug.setuservalue(u1, 1);
debug.setuservalue(u2, 2);
debug.setuservalue(u3, 1);
debug.setmetatable(u1, {__eq = function (a, b)
return debug.getuservalue(a) == debug.getuservalue(b)
end})
debug.setmetatable(u2, {__eq = function (a, b)
return true
end})
assert(u1 == u3 and u3 == u1 and u1 ~= u2)
assert(u2 == u1 and u2 == u3 and u3 == u2)
assert(u2 ~= {}) -- different types cannot be equal
end
t.__concat = function (a,b,c)
assert(c == nil)
if type(a) == 'table' then a = a.val end
if type(b) == 'table' then b = b.val end
if A then return a..b
else
return setmetatable({val=a..b}, t)
end
end
c = {val="c"}; setmetatable(c, t)
d = {val="d"}; setmetatable(d, t)
A = true
assert(c..d == 'cd')
assert(0 .."a".."b"..c..d.."e".."f"..(5+3).."g" == "0abcdef8g")
A = false
assert((c..d..c..d).val == 'cdcd')
x = c..d
assert(getmetatable(x) == t and x.val == 'cd')
x = 0 .."a".."b"..c..d.."e".."f".."g"
assert(x.val == "0abcdefg")
-- concat metamethod x numbers (bug in 5.1.1)
c = {}
local x
setmetatable(c, {__concat = function (a,b)
assert(type(a) == "number" and b == c or type(b) == "number" and a == c)
return c
end})
assert(c..5 == c and 5 .. c == c)
assert(4 .. c .. 5 == c and 4 .. 5 .. 6 .. 7 .. c == c)
-- test comparison compatibilities
local t1, t2, c, d
t1 = {}; c = {}; setmetatable(c, t1)
d = {}
t1.__eq = function () return true end
t1.__lt = function () return true end
setmetatable(d, t1)
assert(c == d and c < d and not(d <= c))
t2 = {}
t2.__eq = t1.__eq
t2.__lt = t1.__lt
setmetatable(d, t2)
assert(c == d and c < d and not(d <= c))
-- test for several levels of calls
local i
local tt = {
__call = function (t, ...)
i = i+1
if t.f then return t.f(...)
else return {...}
end
end
}
local a = setmetatable({}, tt)
local b = setmetatable({f=a}, tt)
local c = setmetatable({f=b}, tt)
i = 0
x = c(3,4,5)
assert(i == 3 and x[1] == 3 and x[3] == 5)
assert(_G.X == 20)
print'+'
local _g = _G
_ENV = setmetatable({}, {__index=function (_,k) return _g[k] end})
a = {}
rawset(a, "x", 1, 2, 3)
assert(a.x == 1 and rawget(a, "x", 3) == 1)
print '+'
-- testing metatables for basic types
mt = {__index = function (a,b) return a+b end,
__len = function (x) return math.floor(x) end}
debug.setmetatable(10, mt)
assert(getmetatable(-2) == mt)
assert((10)[3] == 13)
assert((10)["3"] == 13)
assert(#3.45 == 3)
debug.setmetatable(23, nil)
assert(getmetatable(-2) == nil)
debug.setmetatable(true, mt)
assert(getmetatable(false) == mt)
mt.__index = function (a,b) return a or b end
assert((true)[false] == true)
assert((false)[false] == false)
debug.setmetatable(false, nil)
assert(getmetatable(true) == nil)
debug.setmetatable(nil, mt)
assert(getmetatable(nil) == mt)
mt.__add = function (a,b) return (a or 0) + (b or 0) end
assert(10 + nil == 10)
assert(nil + 23 == 23)
assert(nil + nil == 0)
debug.setmetatable(nil, nil)
assert(getmetatable(nil) == nil)
debug.setmetatable(nil, {})
-- loops in delegation
a = {}; setmetatable(a, a); a.__index = a; a.__newindex = a
assert(not pcall(function (a,b) return a[b] end, a, 10))
assert(not pcall(function (a,b,c) a[b] = c end, a, 10, true))
-- bug in 5.1
T, K, V = nil
grandparent = {}
grandparent.__newindex = function(t,k,v) T=t; K=k; V=v end
parent = {}
parent.__newindex = parent
setmetatable(parent, grandparent)
child = setmetatable({}, parent)
child.foo = 10 --> CRASH (on some machines)
assert(T == parent and K == "foo" and V == 10)
print 'OK'
return 12

View File

@ -0,0 +1,793 @@
-- $Id: files.lua,v 1.95 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
local debug = require "debug"
local maxint = math.maxinteger
assert(type(os.getenv"PATH") == "string")
assert(io.input(io.stdin) == io.stdin)
assert(not pcall(io.input, "non-existent-file"))
assert(io.output(io.stdout) == io.stdout)
local function testerr (msg, f, ...)
local stat, err = pcall(f, ...)
return (not stat and string.find(err, msg, 1, true))
end
local function checkerr (msg, f, ...)
assert(testerr(msg, f, ...))
end
-- cannot close standard files
assert(not io.close(io.stdin) and
not io.stdout:close() and
not io.stderr:close())
assert(type(io.input()) == "userdata" and io.type(io.output()) == "file")
assert(type(io.stdin) == "userdata" and io.type(io.stderr) == "file")
assert(not io.type(8))
local a = {}; setmetatable(a, {})
assert(not io.type(a))
assert(getmetatable(io.input()).__name == "FILE*")
local a,b,c = io.open('xuxu_nao_existe')
assert(not a and type(b) == "string" and type(c) == "number")
a,b,c = io.open('/a/b/c/d', 'w')
assert(not a and type(b) == "string" and type(c) == "number")
local file = os.tmpname()
local f, msg = io.open(file, "w")
if not f then
(Message or print)("'os.tmpname' file cannot be open; skipping file tests")
else --{ most tests here need tmpname
f:close()
print('testing i/o')
local otherfile = os.tmpname()
checkerr("invalid mode", io.open, file, "rw")
checkerr("invalid mode", io.open, file, "rb+")
checkerr("invalid mode", io.open, file, "r+bk")
checkerr("invalid mode", io.open, file, "")
checkerr("invalid mode", io.open, file, "+")
checkerr("invalid mode", io.open, file, "b")
assert(io.open(file, "r+b")):close()
assert(io.open(file, "r+")):close()
assert(io.open(file, "rb")):close()
assert(os.setlocale('C', 'all'))
io.input(io.stdin); io.output(io.stdout);
os.remove(file)
assert(not loadfile(file))
checkerr("", dofile, file)
assert(not io.open(file))
io.output(file)
assert(io.output() ~= io.stdout)
if not _port then -- invalid seek
local status, msg, code = io.stdin:seek("set", 1000)
assert(not status and type(msg) == "string" and type(code) == "number")
end
assert(io.output():seek() == 0)
assert(io.write("alo alo"):seek() == string.len("alo alo"))
assert(io.output():seek("cur", -3) == string.len("alo alo")-3)
assert(io.write("joao"))
assert(io.output():seek("end") == string.len("alo joao"))
assert(io.output():seek("set") == 0)
assert(io.write('"<22>lo"', "{a}\n", "second line\n", "third line \n"))
assert(io.write('<EFBFBD>fourth_line'))
io.output(io.stdout)
collectgarbage() -- file should be closed by GC
assert(io.input() == io.stdin and rawequal(io.output(), io.stdout))
print('+')
-- test GC for files
collectgarbage()
for i=1,120 do
for i=1,5 do
io.input(file)
assert(io.open(file, 'r'))
io.lines(file)
end
collectgarbage()
end
io.input():close()
io.close()
assert(os.rename(file, otherfile))
assert(not os.rename(file, otherfile))
io.output(io.open(otherfile, "ab"))
assert(io.write("\n\n\t\t ", 3450, "\n"));
io.close()
-- test writing/reading numbers
f = assert(io.open(file, "w"))
f:write(maxint, '\n')
f:write(string.format("0X%x\n", maxint))
f:write("0xABCp-3", '\n')
f:write(0, '\n')
f:write(-maxint, '\n')
f:write(string.format("0x%X\n", -maxint))
f:write("-0xABCp-3", '\n')
assert(f:close())
f = assert(io.open(file, "r"))
assert(f:read("n") == maxint)
assert(f:read("n") == maxint)
assert(f:read("n") == 0xABCp-3)
assert(f:read("n") == 0)
assert(f:read("*n") == -maxint) -- test old format (with '*')
assert(f:read("n") == -maxint)
assert(f:read("*n") == -0xABCp-3) -- test old format (with '*')
assert(f:close())
assert(os.remove(file))
-- test yielding during 'dofile'
f = assert(io.open(file, "w"))
f:write[[
local x, z = coroutine.yield(10)
local y = coroutine.yield(20)
return x + y * z
]]
assert(f:close())
f = coroutine.wrap(dofile)
assert(f(file) == 10)
print(f(100, 101) == 20)
assert(f(200) == 100 + 200 * 101)
assert(os.remove(file))
f = assert(io.open(file, "w"))
-- test number termination
f:write[[
-12.3- -0xffff+ .3|5.E-3X +234e+13E 0xDEADBEEFDEADBEEFx
0x1.13Ap+3e
]]
-- very long number
f:write("1234"); for i = 1, 1000 do f:write("0") end; f:write("\n")
-- invalid sequences (must read and discard valid prefixes)
f:write[[
.e+ 0.e; --; 0xX;
]]
assert(f:close())
f = assert(io.open(file, "r"))
assert(f:read("n") == -12.3); assert(f:read(1) == "-")
assert(f:read("n") == -0xffff); assert(f:read(2) == "+ ")
assert(f:read("n") == 0.3); assert(f:read(1) == "|")
assert(f:read("n") == 5e-3); assert(f:read(1) == "X")
assert(f:read("n") == 234e13); assert(f:read(1) == "E")
assert(f:read("n") == 0Xdeadbeefdeadbeef); assert(f:read(2) == "x\n")
assert(f:read("n") == 0x1.13aP3); assert(f:read(1) == "e")
do -- attempt to read too long number
assert(f:read("n") == nil) -- fails
local s = f:read("L") -- read rest of line
assert(string.find(s, "^00*\n$")) -- lots of 0's left
end
assert(not f:read("n")); assert(f:read(2) == "e+")
assert(not f:read("n")); assert(f:read(1) == ";")
assert(not f:read("n")); assert(f:read(2) == "-;")
assert(not f:read("n")); assert(f:read(1) == "X")
assert(not f:read("n")); assert(f:read(1) == ";")
assert(not f:read("n")); assert(not f:read(0)) -- end of file
assert(f:close())
assert(os.remove(file))
-- test line generators
assert(not pcall(io.lines, "non-existent-file"))
assert(os.rename(otherfile, file))
io.output(otherfile)
local n = 0
local f = io.lines(file)
while f() do n = n + 1 end;
assert(n == 6) -- number of lines in the file
checkerr("file is already closed", f)
checkerr("file is already closed", f)
-- copy from file to otherfile
n = 0
for l in io.lines(file) do io.write(l, "\n"); n = n + 1 end
io.close()
assert(n == 6)
-- copy from otherfile back to file
local f = assert(io.open(otherfile))
assert(io.type(f) == "file")
io.output(file)
assert(not io.output():read())
n = 0
for l in f:lines() do io.write(l, "\n"); n = n + 1 end
assert(tostring(f):sub(1, 5) == "file ")
assert(f:close()); io.close()
assert(n == 6)
checkerr("closed file", io.close, f)
assert(tostring(f) == "file (closed)")
assert(io.type(f) == "closed file")
io.input(file)
f = io.open(otherfile):lines()
n = 0
for l in io.lines() do assert(l == f()); n = n + 1 end
f = nil; collectgarbage()
assert(n == 6)
assert(os.remove(otherfile))
do -- bug in 5.3.1
io.output(otherfile)
io.write(string.rep("a", 300), "\n")
io.close()
local t ={}; for i = 1, 250 do t[i] = 1 end
t = {io.lines(otherfile, table.unpack(t))()}
-- everything ok here
assert(#t == 250 and t[1] == 'a' and t[#t] == 'a')
t[#t + 1] = 1 -- one too many
checkerr("too many arguments", io.lines, otherfile, table.unpack(t))
collectgarbage() -- ensure 'otherfile' is closed
assert(os.remove(otherfile))
end
io.input(file)
do -- test error returns
local a,b,c = io.input():write("xuxu")
assert(not a and type(b) == "string" and type(c) == "number")
end
checkerr("invalid format", io.read, "x")
assert(io.read(0) == "") -- not eof
assert(io.read(5, 'l') == '"<22>lo"')
assert(io.read(0) == "")
assert(io.read() == "second line")
local x = io.input():seek()
assert(io.read() == "third line ")
assert(io.input():seek("set", x))
assert(io.read('L') == "third line \n")
assert(io.read(1) == "<EFBFBD>")
assert(io.read(string.len"fourth_line") == "fourth_line")
assert(io.input():seek("cur", -string.len"fourth_line"))
assert(io.read() == "fourth_line")
assert(io.read() == "") -- empty line
assert(io.read('n') == 3450)
assert(io.read(1) == '\n')
assert(io.read(0) == nil) -- end of file
assert(io.read(1) == nil) -- end of file
assert(io.read(30000) == nil) -- end of file
assert(({io.read(1)})[2] == nil)
assert(io.read() == nil) -- end of file
assert(({io.read()})[2] == nil)
assert(io.read('n') == nil) -- end of file
assert(({io.read('n')})[2] == nil)
assert(io.read('a') == '') -- end of file (OK for 'a')
assert(io.read('a') == '') -- end of file (OK for 'a')
collectgarbage()
print('+')
io.close(io.input())
checkerr(" input file is closed", io.read)
assert(os.remove(file))
local t = '0123456789'
for i=1,10 do t = t..t; end
assert(string.len(t) == 10*2^10)
io.output(file)
io.write("alo"):write("\n")
io.close()
checkerr(" output file is closed", io.write)
local f = io.open(file, "a+b")
io.output(f)
collectgarbage()
assert(io.write(' ' .. t .. ' '))
assert(io.write(';', 'end of file\n'))
f:flush(); io.flush()
f:close()
print('+')
io.input(file)
assert(io.read() == "alo")
assert(io.read(1) == ' ')
assert(io.read(string.len(t)) == t)
assert(io.read(1) == ' ')
assert(io.read(0))
assert(io.read('a') == ';end of file\n')
assert(io.read(0) == nil)
assert(io.close(io.input()))
-- test errors in read/write
do
local function ismsg (m)
-- error message is not a code number
return (type(m) == "string" and tonumber(m) == nil)
end
-- read
local f = io.open(file, "w")
local r, m, c = f:read()
assert(not r and ismsg(m) and type(c) == "number")
assert(f:close())
-- write
f = io.open(file, "r")
r, m, c = f:write("whatever")
assert(not r and ismsg(m) and type(c) == "number")
assert(f:close())
-- lines
f = io.open(file, "w")
r, m = pcall(f:lines())
assert(r == false and ismsg(m))
assert(f:close())
end
assert(os.remove(file))
-- test for L format
io.output(file); io.write"\n\nline\nother":close()
io.input(file)
assert(io.read"L" == "\n")
assert(io.read"L" == "\n")
assert(io.read"L" == "line\n")
assert(io.read"L" == "other")
assert(io.read"L" == nil)
io.input():close()
local f = assert(io.open(file))
local s = ""
for l in f:lines("L") do s = s .. l end
assert(s == "\n\nline\nother")
f:close()
io.input(file)
s = ""
for l in io.lines(nil, "L") do s = s .. l end
assert(s == "\n\nline\nother")
io.input():close()
s = ""
for l in io.lines(file, "L") do s = s .. l end
assert(s == "\n\nline\nother")
s = ""
for l in io.lines(file, "l") do s = s .. l end
assert(s == "lineother")
io.output(file); io.write"a = 10 + 34\na = 2*a\na = -a\n":close()
local t = {}
load(io.lines(file, "L"), nil, nil, t)()
assert(t.a == -((10 + 34) * 2))
-- test for multipe arguments in 'lines'
io.output(file); io.write"0123456789\n":close()
for a,b in io.lines(file, 1, 1) do
if a == "\n" then assert(b == nil)
else assert(tonumber(a) == tonumber(b) - 1)
end
end
for a,b,c in io.lines(file, 1, 2, "a") do
assert(a == "0" and b == "12" and c == "3456789\n")
end
for a,b,c in io.lines(file, "a", 0, 1) do
if a == "" then break end
assert(a == "0123456789\n" and b == nil and c == nil)
end
collectgarbage() -- to close file in previous iteration
io.output(file); io.write"00\n10\n20\n30\n40\n":close()
for a, b in io.lines(file, "n", "n") do
if a == 40 then assert(b == nil)
else assert(a == b - 10)
end
end
-- test load x lines
io.output(file);
io.write[[
local y
= X
X =
X *
2 +
X;
X =
X
- y;
]]:close()
_G.X = 1
assert(not load(io.lines(file)))
collectgarbage() -- to close file in previous iteration
load(io.lines(file, "L"))()
assert(_G.X == 2)
load(io.lines(file, 1))()
assert(_G.X == 4)
load(io.lines(file, 3))()
assert(_G.X == 8)
print('+')
local x1 = "string\n\n\\com \"\"''coisas [[estranhas]] ]]'"
io.output(file)
assert(io.write(string.format("x2 = %q\n-- comment without ending EOS", x1)))
io.close()
assert(loadfile(file))()
assert(x1 == x2)
print('+')
assert(os.remove(file))
assert(not os.remove(file))
assert(not os.remove(otherfile))
-- testing loadfile
local function testloadfile (s, expres)
io.output(file)
if s then io.write(s) end
io.close()
local res = assert(loadfile(file))()
assert(os.remove(file))
assert(res == expres)
end
-- loading empty file
testloadfile(nil, nil)
-- loading file with initial comment without end of line
testloadfile("# a non-ending comment", nil)
-- checking Unicode BOM in files
testloadfile("\xEF\xBB\xBF# some comment\nreturn 234", 234)
testloadfile("\xEF\xBB\xBFreturn 239", 239)
testloadfile("\xEF\xBB\xBF", nil) -- empty file with a BOM
-- checking line numbers in files with initial comments
testloadfile("# a comment\nreturn require'debug'.getinfo(1).currentline", 2)
-- loading binary file
io.output(io.open(file, "wb"))
assert(io.write(string.dump(function () return 10, '\0alo\255', 'hi' end)))
io.close()
a, b, c = assert(loadfile(file))()
assert(a == 10 and b == "\0alo\255" and c == "hi")
assert(os.remove(file))
-- bug in 5.2.1
do
io.output(io.open(file, "wb"))
-- save function with no upvalues
assert(io.write(string.dump(function () return 1 end)))
io.close()
f = assert(loadfile(file, "b", {}))
assert(type(f) == "function" and f() == 1)
assert(os.remove(file))
end
-- loading binary file with initial comment
io.output(io.open(file, "wb"))
assert(io.write("#this is a comment for a binary file\0\n",
string.dump(function () return 20, '\0\0\0' end)))
io.close()
a, b, c = assert(loadfile(file))()
assert(a == 20 and b == "\0\0\0" and c == nil)
assert(os.remove(file))
-- 'loadfile' with 'env'
do
local f = io.open(file, 'w')
f:write[[
if (...) then a = 15; return b, c, d
else return _ENV
end
]]
f:close()
local t = {b = 12, c = "xuxu", d = print}
local f = assert(loadfile(file, 't', t))
local b, c, d = f(1)
assert(t.a == 15 and b == 12 and c == t.c and d == print)
assert(f() == t)
f = assert(loadfile(file, 't', nil))
assert(f() == nil)
f = assert(loadfile(file))
assert(f() == _G)
assert(os.remove(file))
end
-- 'loadfile' x modes
do
io.open(file, 'w'):write("return 10"):close()
local s, m = loadfile(file, 'b')
assert(not s and string.find(m, "a text chunk"))
io.open(file, 'w'):write("\27 return 10"):close()
local s, m = loadfile(file, 't')
assert(not s and string.find(m, "a binary chunk"))
assert(os.remove(file))
end
io.output(file)
assert(io.write("qualquer coisa\n"))
assert(io.write("mais qualquer coisa"))
io.close()
assert(io.output(assert(io.open(otherfile, 'wb')))
:write("outra coisa\0\1\3\0\0\0\0\255\0")
:close())
local filehandle = assert(io.open(file, 'r+'))
local otherfilehandle = assert(io.open(otherfile, 'rb'))
assert(filehandle ~= otherfilehandle)
assert(type(filehandle) == "userdata")
assert(filehandle:read('l') == "qualquer coisa")
io.input(otherfilehandle)
assert(io.read(string.len"outra coisa") == "outra coisa")
assert(filehandle:read('l') == "mais qualquer coisa")
filehandle:close();
assert(type(filehandle) == "userdata")
io.input(otherfilehandle)
assert(io.read(4) == "\0\1\3\0")
assert(io.read(3) == "\0\0\0")
assert(io.read(0) == "") -- 255 is not eof
assert(io.read(1) == "\255")
assert(io.read('a') == "\0")
assert(not io.read(0))
assert(otherfilehandle == io.input())
otherfilehandle:close()
assert(os.remove(file))
assert(os.remove(otherfile))
collectgarbage()
io.output(file)
:write[[
123.4 -56e-2 not a number
second line
third line
and the rest of the file
]]
:close()
io.input(file)
local _,a,b,c,d,e,h,__ = io.read(1, 'n', 'n', 'l', 'l', 'l', 'a', 10)
assert(io.close(io.input()))
assert(_ == ' ' and __ == nil)
assert(type(a) == 'number' and a==123.4 and b==-56e-2)
assert(d=='second line' and e=='third line')
assert(h==[[
and the rest of the file
]])
assert(os.remove(file))
collectgarbage()
-- testing buffers
do
local f = assert(io.open(file, "w"))
local fr = assert(io.open(file, "r"))
assert(f:setvbuf("full", 2000))
f:write("x")
assert(fr:read("all") == "") -- full buffer; output not written yet
f:close()
fr:seek("set")
assert(fr:read("all") == "x") -- `close' flushes it
f = assert(io.open(file), "w")
assert(f:setvbuf("no"))
f:write("x")
fr:seek("set")
assert(fr:read("all") == "x") -- no buffer; output is ready
f:close()
f = assert(io.open(file, "a"))
assert(f:setvbuf("line"))
f:write("x")
fr:seek("set", 1)
assert(fr:read("all") == "") -- line buffer; no output without `\n'
f:write("a\n"):seek("set", 1)
assert(fr:read("all") == "xa\n") -- now we have a whole line
f:close(); fr:close()
assert(os.remove(file))
end
if not _soft then
print("testing large files (> BUFSIZ)")
io.output(file)
for i=1,5001 do io.write('0123456789123') end
io.write('\n12346'):close()
io.input(file)
local x = io.read('a')
io.input():seek('set', 0)
local y = io.read(30001)..io.read(1005)..io.read(0)..
io.read(1)..io.read(100003)
assert(x == y and string.len(x) == 5001*13 + 6)
io.input():seek('set', 0)
y = io.read() -- huge line
assert(x == y..'\n'..io.read())
assert(io.read() == nil)
io.close(io.input())
assert(os.remove(file))
x = nil; y = nil
end
_port = true -- NodeMCU
if not _port then
local progname
do -- get name of running executable
local arg = arg or _ARG
local i = 0
while arg[i] do i = i - 1 end
progname = '"' .. arg[i + 1] .. '"'
end
print("testing popen/pclose and execute")
local tests = {
-- command, what, code
{"ls > /dev/null", "ok"},
{"not-to-be-found-command", "exit"},
{"exit 3", "exit", 3},
{"exit 129", "exit", 129},
{"kill -s HUP $$", "signal", 1},
{"kill -s KILL $$", "signal", 9},
{"sh -c 'kill -s HUP $$'", "exit"},
{progname .. ' -e " "', "ok"},
{progname .. ' -e "os.exit(0, true)"', "ok"},
{progname .. ' -e "os.exit(20, true)"', "exit", 20},
}
print("\n(some error messages are expected now)")
for _, v in ipairs(tests) do
local x, y, z = io.popen(v[1]):close()
local x1, y1, z1 = os.execute(v[1])
assert(x == x1 and y == y1 and z == z1)
if v[2] == "ok" then
assert(x and y == 'exit' and z == 0)
else
assert(not x and y == v[2]) -- correct status and 'what'
-- correct code if known (but always different from 0)
assert((v[3] == nil and z > 0) or v[3] == z)
end
end
end
-- testing tmpfile
f = io.tmpfile()
assert(io.type(f) == "file")
f:write("alo")
f:seek("set")
assert(f:read"a" == "alo")
end --}
print'+'
print("testing date/time")
assert(os.date("") == "")
assert(os.date("!") == "")
assert(os.date("\0\0") == "\0\0")
assert(os.date("!\0\0") == "\0\0")
local x = string.rep("a", 10000)
assert(os.date(x) == x)
local t = os.time()
D = os.date("*t", t)
assert(os.date(string.rep("%d", 1000), t) ==
string.rep(os.date("%d", t), 1000))
assert(os.date(string.rep("%", 200)) == string.rep("%", 100))
local t = os.time()
D = os.date("*t", t)
load(os.date([[assert(D.year==%Y and D.month==%m and D.day==%d and
D.hour==%H and D.min==%M and D.sec==%S and
D.wday==%w+1 and D.yday==%j and type(D.isdst) == 'boolean')]], t))()
checkerr("invalid conversion specifier", os.date, "%")
checkerr("invalid conversion specifier", os.date, "%9")
checkerr("invalid conversion specifier", os.date, "%")
checkerr("invalid conversion specifier", os.date, "%O")
checkerr("invalid conversion specifier", os.date, "%E")
checkerr("invalid conversion specifier", os.date, "%Ea")
checkerr("not an integer", os.time, {year=1000, month=1, day=1, hour='x'})
checkerr("not an integer", os.time, {year=1000, month=1, day=1, hour=1.5})
checkerr("missing", os.time, {hour = 12}) -- missing date
if not _port then
-- test Posix-specific modifiers
assert(type(os.date("%Ex")) == 'string')
assert(type(os.date("%Oy")) == 'string')
-- test out-of-range dates (at least for Unix)
if maxint >= 2^62 then -- cannot do these tests in Small Lua
-- no arith overflows
checkerr("out-of-bound", os.time, {year = -maxint, month = 1, day = 1})
if string.packsize("i") == 4 then -- 4-byte ints
if testerr("out-of-bound", os.date, "%Y", 2^40) then
-- time_t has 4 bytes and therefore cannot represent year 4000
print(" 4-byte time_t")
checkerr("cannot be represented", os.time, {year=4000, month=1, day=1})
else
-- time_t has 8 bytes; an int year cannot represent a huge time
print(" 8-byte time_t")
checkerr("cannot be represented", os.date, "%Y", 2^60)
-- it should have no problems with year 4000
assert(tonumber(os.time{year=4000, month=1, day=1}))
end
else -- 8-byte ints
-- assume time_t has 8 bytes too
print(" 8-byte time_t")
assert(tonumber(os.date("%Y", 2^60)))
-- but still cannot represent a huge year
checkerr("cannot be represented", os.time, {year=2^60, month=1, day=1})
end
end
end
D = os.date("!*t", t)
load(os.date([[!assert(D.year==%Y and D.month==%m and D.day==%d and
D.hour==%H and D.min==%M and D.sec==%S and
D.wday==%w+1 and D.yday==%j and type(D.isdst) == 'boolean')]], t))()
do
local D = os.date("*t")
local t = os.time(D)
assert(type(D.isdst) == 'boolean')
D.isdst = nil
local t1 = os.time(D)
assert(t == t1) -- if isdst is absent uses correct default
end
t = os.time(D)
D.year = D.year-1;
local t1 = os.time(D)
-- allow for leap years
assert(math.abs(os.difftime(t,t1)/(24*3600) - 365) < 2)
-- should not take more than 1 second to execute these two lines
t = os.time()
t1 = os.time(os.date("*t"))
local diff = os.difftime(t1,t)
assert(0 <= diff and diff <= 1)
diff = os.difftime(t,t1)
assert(-1 <= diff and diff <= 0)
local t1 = os.time{year=2000, month=10, day=1, hour=23, min=12}
local t2 = os.time{year=2000, month=10, day=1, hour=23, min=10, sec=19}
assert(os.difftime(t1,t2) == 60*2-19)
-- since 5.3.3, 'os.time' normalizes table fields
t1 = {year = 2005, month = 1, day = 1, hour = 1, min = 0, sec = -3602}
os.time(t1)
assert(t1.day == 31 and t1.month == 12 and t1.year == 2004 and
t1.hour == 23 and t1.min == 59 and t1.sec == 58 and
t1.yday == 366)
io.output(io.stdout)
local t = os.date('%d %m %Y %H %M %S')
local d, m, a, h, min, s = string.match(t,
"(%d+) (%d+) (%d+) (%d+) (%d+) (%d+)")
d = tonumber(d)
m = tonumber(m)
a = tonumber(a)
h = tonumber(h)
min = tonumber(min)
s = tonumber(s)
io.write(string.format('test done on %2.2d/%2.2d/%d', d, m, a))
io.write(string.format(', at %2.2d:%2.2d:%2.2d\n', h, min, s))
io.write(string.format('%s\n', _VERSION))

630
app/lua53/host/tests/gc.lua Normal file
View File

@ -0,0 +1,630 @@
-- $Id: gc.lua,v 1.72 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
print('testing garbage collection')
local debug = require"debug"
collectgarbage()
assert(collectgarbage("isrunning"))
local function gcinfo () return collectgarbage"count" * 1024 end
-- test weird parameters
do
-- save original parameters
local a = collectgarbage("setpause", 200)
local b = collectgarbage("setstepmul", 200)
local t = {0, 2, 10, 90, 500, 5000, 30000, 0x7ffffffe}
for i = 1, #t do
local p = t[i]
for j = 1, #t do
local m = t[j]
collectgarbage("setpause", p)
collectgarbage("setstepmul", m)
collectgarbage("step", 0)
collectgarbage("step", 10000)
end
end
-- restore original parameters
collectgarbage("setpause", a)
collectgarbage("setstepmul", b)
collectgarbage()
end
_G["while"] = 234
limit = 5000
local function GC1 ()
local u
local b -- must be declared after 'u' (to be above it in the stack)
local finish = false
u = setmetatable({}, {__gc = function () finish = true end})
b = {34}
repeat u = {} until finish
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
finish = false; local i = 1
u = setmetatable({}, {__gc = function () finish = true end})
repeat i = i + 1; u = tostring(i) .. tostring(i) until finish
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
finish = false
u = setmetatable({}, {__gc = function () finish = true end})
repeat local i; u = function () return i end until finish
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
end
local function GC2 ()
local u
local finish = false
u = {setmetatable({}, {__gc = function () finish = true end})}
b = {34}
repeat u = {{}} until finish
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
finish = false; local i = 1
u = {setmetatable({}, {__gc = function () finish = true end})}
repeat i = i + 1; u = {tostring(i) .. tostring(i)} until finish
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
finish = false
u = {setmetatable({}, {__gc = function () finish = true end})}
repeat local i; u = {function () return i end} until finish
assert(b[1] == 34) -- 'u' was collected, but 'b' was not
end
local function GC() GC1(); GC2() end
contCreate = 0
print('tables')
while contCreate <= limit do
local a = {}; a = nil
contCreate = contCreate+1
end
a = "a"
contCreate = 0
print('strings')
while contCreate <= limit do
a = contCreate .. "b";
a = string.gsub(a, '(%d%d*)', string.upper)
a = "a"
contCreate = contCreate+1
end
contCreate = 0
a = {}
print('functions')
function a:test ()
while contCreate <= limit do
load(string.format("function temp(a) return 'a%d' end", contCreate), "")()
assert(temp() == string.format('a%d', contCreate))
contCreate = contCreate+1
end
end
a:test()
-- collection of functions without locals, globals, etc.
do local f = function () end end
print("functions with errors")
prog = [[
do
a = 10;
function foo(x,y)
a = sin(a+0.456-0.23e-12);
return function (z) return sin(%x+z) end
end
local x = function (w) a=a+w; end
end
]]
do
local step = 1
if _soft then step = 13 end
for i=1, string.len(prog), step do
for j=i, string.len(prog), step do
pcall(load(string.sub(prog, i, j), ""))
end
end
end
foo = nil
print('long strings')
x = "01234567890123456789012345678901234567890123456789012345678901234567890123456789"
assert(string.len(x)==80)
s = ''
n = 0
k = math.min(300, (math.maxinteger // 80) // 2)
while n < k do s = s..x; n=n+1; j=tostring(n) end
assert(string.len(s) == k*80)
s = string.sub(s, 1, 10000)
s, i = string.gsub(s, '(%d%d%d%d)', '')
assert(i==10000 // 4)
s = nil
x = nil
assert(_G["while"] == 234)
print("steps")
print("steps (2)")
local function dosteps (siz)
assert(not collectgarbage("isrunning"))
collectgarbage()
assert(not collectgarbage("isrunning"))
local a = {}
for i=1,100 do a[i] = {{}}; local b = {} end
local x = gcinfo()
local i = 0
repeat -- do steps until it completes a collection cycle
i = i+1
until collectgarbage("step", siz)
assert(gcinfo() < x)
return i
end
collectgarbage"stop"
--[[TODO NodeMCU GC configuration non-default
if not _port then
-- test the "size" of basic GC steps (whatever they mean...)
assert(dosteps(0) > 10)
assert(dosteps(10) < dosteps(2))
end
-- collector should do a full collection with so many steps
assert(dosteps(20000) == 1)
assert(collectgarbage("step", 20000) == true)
assert(collectgarbage("step", 20000) == true)
assert(not collectgarbage("isrunning"))
]]
do -- NodeMCU more work needed
print ('OK')
return
end
collectgarbage"restart"
assert(collectgarbage("isrunning"))
if not _port then
-- test the pace of the collector
collectgarbage(); collectgarbage()
local x = gcinfo()
collectgarbage"stop"
assert(not collectgarbage("isrunning"))
repeat
local a = {}
until gcinfo() > 3 * x
collectgarbage"restart"
assert(collectgarbage("isrunning"))
repeat
local a = {}
until gcinfo() <= x * 2
end
print("clearing tables")
lim = 15
a = {}
-- fill a with `collectable' indices
for i=1,lim do a[{}] = i end
b = {}
for k,v in pairs(a) do b[k]=v end
-- remove all indices and collect them
for n in pairs(b) do
a[n] = nil
assert(type(n) == 'table' and next(n) == nil)
collectgarbage()
end
b = nil
collectgarbage()
for n in pairs(a) do error'cannot be here' end
for i=1,lim do a[i] = i end
for i=1,lim do assert(a[i] == i) end
print('weak tables')
a = {}; setmetatable(a, {__mode = 'k'});
-- fill a with some `collectable' indices
for i=1,lim do a[{}] = i end
-- and some non-collectable ones
for i=1,lim do a[i] = i end
for i=1,lim do local s=string.rep('@', i); a[s] = s..'#' end
collectgarbage()
local i = 0
for k,v in pairs(a) do assert(k==v or k..'#'==v); i=i+1 end
assert(i == 2*lim)
a = {}; setmetatable(a, {__mode = 'v'});
a[1] = string.rep('b', 21)
collectgarbage()
assert(a[1]) -- strings are *values*
a[1] = nil
-- fill a with some `collectable' values (in both parts of the table)
for i=1,lim do a[i] = {} end
for i=1,lim do a[i..'x'] = {} end
-- and some non-collectable ones
for i=1,lim do local t={}; a[t]=t end
for i=1,lim do a[i+lim]=i..'x' end
collectgarbage()
local i = 0
for k,v in pairs(a) do assert(k==v or k-lim..'x' == v); i=i+1 end
assert(i == 2*lim)
a = {}; setmetatable(a, {__mode = 'vk'});
local x, y, z = {}, {}, {}
-- keep only some items
a[1], a[2], a[3] = x, y, z
a[string.rep('$', 11)] = string.rep('$', 11)
-- fill a with some `collectable' values
for i=4,lim do a[i] = {} end
for i=1,lim do a[{}] = i end
for i=1,lim do local t={}; a[t]=t end
collectgarbage()
assert(next(a) ~= nil)
local i = 0
for k,v in pairs(a) do
assert((k == 1 and v == x) or
(k == 2 and v == y) or
(k == 3 and v == z) or k==v);
i = i+1
end
assert(i == 4)
x,y,z=nil
collectgarbage()
assert(next(a) == string.rep('$', 11))
-- 'bug' in 5.1
a = {}
local t = {x = 10}
local C = setmetatable({key = t}, {__mode = 'v'})
local C1 = setmetatable({[t] = 1}, {__mode = 'k'})
a.x = t -- this should not prevent 't' from being removed from
-- weak table 'C' by the time 'a' is finalized
setmetatable(a, {__gc = function (u)
assert(C.key == nil)
assert(type(next(C1)) == 'table')
end})
a, t = nil
collectgarbage()
collectgarbage()
assert(next(C) == nil and next(C1) == nil)
C, C1 = nil
-- ephemerons
local mt = {__mode = 'k'}
a = {{10},{20},{30},{40}}; setmetatable(a, mt)
x = nil
for i = 1, 100 do local n = {}; a[n] = {k = {x}}; x = n end
GC()
local n = x
local i = 0
while n do n = a[n].k[1]; i = i + 1 end
assert(i == 100)
x = nil
GC()
for i = 1, 4 do assert(a[i][1] == i * 10); a[i] = nil end
assert(next(a) == nil)
local K = {}
a[K] = {}
for i=1,10 do a[K][i] = {}; a[a[K][i]] = setmetatable({}, mt) end
x = nil
local k = 1
for j = 1,100 do
local n = {}; local nk = k%10 + 1
a[a[K][nk]][n] = {x, k = k}; x = n; k = nk
end
GC()
local n = x
local i = 0
while n do local t = a[a[K][k]][n]; n = t[1]; k = t.k; i = i + 1 end
assert(i == 100)
K = nil
GC()
-- assert(next(a) == nil)
-- testing errors during GC
do
collectgarbage("stop") -- stop collection
local u = {}
local s = {}; setmetatable(s, {__mode = 'k'})
setmetatable(u, {__gc = function (o)
local i = s[o]
s[i] = true
assert(not s[i - 1]) -- check proper finalization order
if i == 8 then error("here") end -- error during GC
end})
for i = 6, 10 do
local n = setmetatable({}, getmetatable(u))
s[n] = i
end
assert(not pcall(collectgarbage))
for i = 8, 10 do assert(s[i]) end
for i = 1, 5 do
local n = setmetatable({}, getmetatable(u))
s[n] = i
end
collectgarbage()
for i = 1, 10 do assert(s[i]) end
getmetatable(u).__gc = false
-- __gc errors with non-string messages
setmetatable({}, {__gc = function () error{} end})
local a, b = pcall(collectgarbage)
assert(not a and type(b) == "string" and string.find(b, "error in __gc"))
end
print '+'
-- testing userdata
if T==nil then
(Message or print)('\n >>> testC not active: skipping userdata GC tests <<<\n')
else
local function newproxy(u)
return debug.setmetatable(T.newuserdata(0), debug.getmetatable(u))
end
collectgarbage("stop") -- stop collection
local u = newproxy(nil)
debug.setmetatable(u, {__gc = true})
local s = 0
local a = {[u] = 0}; setmetatable(a, {__mode = 'vk'})
for i=1,10 do a[newproxy(u)] = i end
for k in pairs(a) do assert(getmetatable(k) == getmetatable(u)) end
local a1 = {}; for k,v in pairs(a) do a1[k] = v end
for k,v in pairs(a1) do a[v] = k end
for i =1,10 do assert(a[i]) end
getmetatable(u).a = a1
getmetatable(u).u = u
do
local u = u
getmetatable(u).__gc = function (o)
assert(a[o] == 10-s)
assert(a[10-s] == nil) -- udata already removed from weak table
assert(getmetatable(o) == getmetatable(u))
assert(getmetatable(o).a[o] == 10-s)
s=s+1
end
end
a1, u = nil
assert(next(a) ~= nil)
collectgarbage()
assert(s==11)
collectgarbage()
assert(next(a) == nil) -- finalized keys are removed in two cycles
end
-- __gc x weak tables
local u = setmetatable({}, {__gc = true})
-- __gc metamethod should be collected before running
setmetatable(getmetatable(u), {__mode = "v"})
getmetatable(u).__gc = function (o) os.exit(1) end -- cannot happen
u = nil
collectgarbage()
local u = setmetatable({}, {__gc = true})
local m = getmetatable(u)
m.x = {[{0}] = 1; [0] = {1}}; setmetatable(m.x, {__mode = "kv"});
m.__gc = function (o)
assert(next(getmetatable(o).x) == nil)
m = 10
end
u, m = nil
collectgarbage()
assert(m==10)
-- errors during collection
u = setmetatable({}, {__gc = function () error "!!!" end})
u = nil
assert(not pcall(collectgarbage))
if not _soft then
print("deep structures")
local a = {}
for i = 1,200000 do
a = {next = a}
end
collectgarbage()
end
-- create many threads with self-references and open upvalues
print("self-referenced threads")
local thread_id = 0
local threads = {}
local function fn (thread)
local x = {}
threads[thread_id] = function()
thread = x
end
coroutine.yield()
end
while thread_id < 1000 do
local thread = coroutine.create(fn)
coroutine.resume(thread, thread)
thread_id = thread_id + 1
end
-- Create a closure (function inside 'f') with an upvalue ('param') that
-- points (through a table) to the closure itself and to the thread
-- ('co' and the initial value of 'param') where closure is running.
-- Then, assert that table (and therefore everything else) will be
-- collected.
do
local collected = false -- to detect collection
collectgarbage(); collectgarbage("stop")
do
local function f (param)
;(function ()
assert(type(f) == 'function' and type(param) == 'thread')
param = {param, f}
setmetatable(param, {__gc = function () collected = true end})
coroutine.yield(100)
end)()
end
local co = coroutine.create(f)
assert(coroutine.resume(co, co))
end
-- Now, thread and closure are not reacheable any more;
-- two collections are needed to break cycle
collectgarbage()
assert(not collected)
collectgarbage()
assert(collected)
collectgarbage("restart")
end
do
collectgarbage()
collectgarbage"stop"
local x = gcinfo()
repeat
for i=1,1000 do _ENV.a = {} end
collectgarbage("step", 0) -- steps should not unblock the collector
until gcinfo() > 2 * x
collectgarbage"restart"
end
if T then -- tests for weird cases collecting upvalues
local function foo ()
local a = {x = 20}
coroutine.yield(function () return a.x end) -- will run collector
assert(a.x == 20) -- 'a' is 'ok'
a = {x = 30} -- create a new object
assert(T.gccolor(a) == "white") -- of course it is new...
coroutine.yield(100) -- 'a' is still local to this thread
end
local t = setmetatable({}, {__mode = "kv"})
collectgarbage(); collectgarbage('stop')
-- create coroutine in a weak table, so it will never be marked
t.co = coroutine.wrap(foo)
local f = t.co() -- create function to access local 'a'
T.gcstate("atomic") -- ensure all objects are traversed
assert(T.gcstate() == "atomic")
assert(t.co() == 100) -- resume coroutine, creating new table for 'a'
assert(T.gccolor(t.co) == "white") -- thread was not traversed
T.gcstate("pause") -- collect thread, but should mark 'a' before that
assert(t.co == nil and f() == 30) -- ensure correct access to 'a'
collectgarbage("restart")
-- test barrier in sweep phase (advance cleaning of upvalue to white)
local u = T.newuserdata(0) -- create a userdata
collectgarbage()
collectgarbage"stop"
T.gcstate"atomic"
T.gcstate"sweepallgc"
local x = {}
assert(T.gccolor(u) == "black") -- upvalue is "old" (black)
assert(T.gccolor(x) == "white") -- table is "new" (white)
debug.setuservalue(u, x) -- trigger barrier
assert(T.gccolor(u) == "white") -- upvalue changed to white
collectgarbage"restart"
print"+"
end
if T then
local debug = require "debug"
collectgarbage("stop")
local x = T.newuserdata(0)
local y = T.newuserdata(0)
debug.setmetatable(y, {__gc = true}) -- bless the new udata before...
debug.setmetatable(x, {__gc = true}) -- ...the old one
assert(T.gccolor(y) == "white")
T.checkmemory()
collectgarbage("restart")
end
if T then
print("emergency collections")
collectgarbage()
collectgarbage()
T.totalmem(T.totalmem() + 200)
for i=1,200 do local a = {} end
T.totalmem(0)
collectgarbage()
local t = T.totalmem("table")
local a = {{}, {}, {}} -- create 4 new tables
assert(T.totalmem("table") == t + 4)
t = T.totalmem("function")
a = function () end -- create 1 new closure
assert(T.totalmem("function") == t + 1)
t = T.totalmem("thread")
a = coroutine.create(function () end) -- create 1 new coroutine
assert(T.totalmem("thread") == t + 1)
end
-- create an object to be collected when state is closed
do
local setmetatable,assert,type,print,getmetatable =
setmetatable,assert,type,print,getmetatable
local tt = {}
tt.__gc = function (o)
assert(getmetatable(o) == tt)
-- create new objects during GC
local a = 'xuxu'..(10+3)..'joao', {}
___Glob = o -- ressurect object!
setmetatable({}, tt) -- creates a new one with same metatable
print(">>> closing state " .. "<<<\n")
end
local u = setmetatable({}, tt)
___Glob = {u} -- avoid object being collected before program end
end
-- create several objects to raise errors when collected while closing state
do
local mt = {__gc = function (o) return o + 1 end}
for i = 1,10 do
-- create object and preserve it until the end
table.insert(___Glob, setmetatable({}, mt))
end
end
-- just to make sure
assert(collectgarbage'isrunning')
print('OK')

View File

@ -0,0 +1,232 @@
-- $Id: goto.lua,v 1.13 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
collectgarbage()
local function errmsg (code, m)
local st, msg = load(code)
assert(not st and string.find(msg, m))
end
-- cannot see label inside block
errmsg([[ goto l1; do ::l1:: end ]], "label 'l1'")
errmsg([[ do ::l1:: end goto l1; ]], "label 'l1'")
-- repeated label
errmsg([[ ::l1:: ::l1:: ]], "label 'l1'")
-- undefined label
errmsg([[ goto l1; local aa ::l1:: ::l2:: print(3) ]], "local 'aa'")
-- jumping over variable definition
errmsg([[
do local bb, cc; goto l1; end
local aa
::l1:: print(3)
]], "local 'aa'")
-- jumping into a block
errmsg([[ do ::l1:: end goto l1 ]], "label 'l1'")
errmsg([[ goto l1 do ::l1:: end ]], "label 'l1'")
-- cannot continue a repeat-until with variables
errmsg([[
repeat
if x then goto cont end
local xuxu = 10
::cont::
until xuxu < x
]], "local 'xuxu'")
-- simple gotos
local x
do
local y = 12
goto l1
::l2:: x = x + 1; goto l3
::l1:: x = y; goto l2
end
::l3:: ::l3_1:: assert(x == 13)
-- long labels
do
local prog = [[
do
local a = 1
goto l%sa; a = a + 1
::l%sa:: a = a + 10
goto l%sb; a = a + 2
::l%sb:: a = a + 20
return a
end
]]
local label = string.rep("0123456789", 40)
prog = string.format(prog, label, label, label, label)
assert(assert(load(prog))() == 31)
end
-- goto to correct label when nested
do goto l3; ::l3:: end -- does not loop jumping to previous label 'l3'
-- ok to jump over local dec. to end of block
do
goto l1
local a = 23
x = a
::l1::;
end
while true do
goto l4
goto l1 -- ok to jump over local dec. to end of block
goto l1 -- multiple uses of same label
local x = 45
::l1:: ;;;
end
::l4:: assert(x == 13)
if print then
goto l1 -- ok to jump over local dec. to end of block
error("should not be here")
goto l2 -- ok to jump over local dec. to end of block
local x
::l1:: ; ::l2:: ;;
else end
-- to repeat a label in a different function is OK
local function foo ()
local a = {}
goto l3
::l1:: a[#a + 1] = 1; goto l2;
::l2:: a[#a + 1] = 2; goto l5;
::l3::
::l3a:: a[#a + 1] = 3; goto l1;
::l4:: a[#a + 1] = 4; goto l6;
::l5:: a[#a + 1] = 5; goto l4;
::l6:: assert(a[1] == 3 and a[2] == 1 and a[3] == 2 and
a[4] == 5 and a[5] == 4)
if not a[6] then a[6] = true; goto l3a end -- do it twice
end
::l6:: foo()
do -- bug in 5.2 -> 5.3.2
local x
::L1::
local y -- cannot join this SETNIL with previous one
assert(y == nil)
y = true
if x == nil then
x = 1
goto L1
else
x = x + 1
end
assert(x == 2 and y == true)
end
--------------------------------------------------------------------------------
-- testing closing of upvalues
local debug = require 'debug'
local function foo ()
local t = {}
do
local i = 1
local a, b, c, d
t[1] = function () return a, b, c, d end
::l1::
local b
do
local c
t[#t + 1] = function () return a, b, c, d end -- t[2], t[4], t[6]
if i > 2 then goto l2 end
do
local d
t[#t + 1] = function () return a, b, c, d end -- t[3], t[5]
i = i + 1
local a
goto l1
end
end
end
::l2:: return t
end
local a = foo()
assert(#a == 6)
-- all functions share same 'a'
for i = 2, 6 do
assert(debug.upvalueid(a[1], 1) == debug.upvalueid(a[i], 1))
end
-- 'b' and 'c' are shared among some of them
for i = 2, 6 do
-- only a[1] uses external 'b'/'b'
assert(debug.upvalueid(a[1], 2) ~= debug.upvalueid(a[i], 2))
assert(debug.upvalueid(a[1], 3) ~= debug.upvalueid(a[i], 3))
end
for i = 3, 5, 2 do
-- inner functions share 'b'/'c' with previous ones
assert(debug.upvalueid(a[i], 2) == debug.upvalueid(a[i - 1], 2))
assert(debug.upvalueid(a[i], 3) == debug.upvalueid(a[i - 1], 3))
-- but not with next ones
assert(debug.upvalueid(a[i], 2) ~= debug.upvalueid(a[i + 1], 2))
assert(debug.upvalueid(a[i], 3) ~= debug.upvalueid(a[i + 1], 3))
end
-- only external 'd' is shared
for i = 2, 6, 2 do
assert(debug.upvalueid(a[1], 4) == debug.upvalueid(a[i], 4))
end
-- internal 'd's are all different
for i = 3, 5, 2 do
for j = 1, 6 do
assert((debug.upvalueid(a[i], 4) == debug.upvalueid(a[j], 4))
== (i == j))
end
end
--------------------------------------------------------------------------------
-- testing if x goto optimizations
local function testG (a)
if a == 1 then
goto l1
error("should never be here!")
elseif a == 2 then goto l2
elseif a == 3 then goto l3
elseif a == 4 then
goto l1 -- go to inside the block
error("should never be here!")
::l1:: a = a + 1 -- must go to 'if' end
else
goto l4
::l4a:: a = a * 2; goto l4b
error("should never be here!")
::l4:: goto l4a
error("should never be here!")
::l4b::
end
do return a end
::l2:: do return "2" end
::l3:: do return "3" end
::l1:: return "1"
end
assert(testG(1) == "1")
assert(testG(2) == "2")
assert(testG(3) == "3")
assert(testG(4) == 5)
assert(testG(5) == 10)
--------------------------------------------------------------------------------
print'OK'

View File

@ -0,0 +1,302 @@
-- $Id: literals.lua,v 1.36 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
print('testing scanner')
local debug = require "debug"
local function dostring (x) return assert(load(x), "")() end
dostring("x \v\f = \t\r 'a\0a' \v\f\f")
assert(x == 'a\0a' and string.len(x) == 3)
-- escape sequences
assert('\n\"\'\\' == [[
"'\]])
assert(string.find("\a\b\f\n\r\t\v", "^%c%c%c%c%c%c%c$"))
-- assume ASCII just for tests:
assert("\09912" == 'c12')
assert("\99ab" == 'cab')
assert("\099" == '\99')
assert("\099\n" == 'c\10')
assert('\0\0\0alo' == '\0' .. '\0\0' .. 'alo')
assert(010 .. 020 .. -030 == "1020-30")
-- hexadecimal escapes
assert("\x00\x05\x10\x1f\x3C\xfF\xe8" == "\0\5\16\31\60\255\232")
local function lexstring (x, y, n)
local f = assert(load('return ' .. x ..
', require"debug".getinfo(1).currentline', ''))
local s, l = f()
assert(s == y and l == n)
end
lexstring("'abc\\z \n efg'", "abcefg", 2)
lexstring("'abc\\z \n\n\n'", "abc", 4)
lexstring("'\\z \n\t\f\v\n'", "", 3)
lexstring("[[\nalo\nalo\n\n]]", "alo\nalo\n\n", 5)
lexstring("[[\nalo\ralo\n\n]]", "alo\nalo\n\n", 5)
lexstring("[[\nalo\ralo\r\n]]", "alo\nalo\n", 4)
lexstring("[[\ralo\n\ralo\r\n]]", "alo\nalo\n", 4)
lexstring("[[alo]\n]alo]]", "alo]\n]alo", 2)
assert("abc\z
def\z
ghi\z
" == 'abcdefghi')
-- UTF-8 sequences
assert("\u{0}\u{00000000}\x00\0" == string.char(0, 0, 0, 0))
-- limits for 1-byte sequences
assert("\u{0}\u{7F}" == "\x00\z\x7F")
-- limits for 2-byte sequences
assert("\u{80}\u{7FF}" == "\xC2\x80\z\xDF\xBF")
-- limits for 3-byte sequences
assert("\u{800}\u{FFFF}" == "\xE0\xA0\x80\z\xEF\xBF\xBF")
-- limits for 4-byte sequences
assert("\u{10000}\u{10FFFF}" == "\xF0\x90\x80\x80\z\xF4\x8F\xBF\xBF")
-- Error in escape sequences
local function lexerror (s, err)
local st, msg = load('return ' .. s, '')
if err ~= '<eof>' then err = err .. "'" end
assert(not st and string.find(msg, "near .-" .. err))
end
lexerror([["abc\x"]], [[\x"]])
lexerror([["abc\x]], [[\x]])
lexerror([["\x]], [[\x]])
lexerror([["\x5"]], [[\x5"]])
lexerror([["\x5]], [[\x5]])
lexerror([["\xr"]], [[\xr]])
lexerror([["\xr]], [[\xr]])
lexerror([["\x.]], [[\x.]])
lexerror([["\x8%"]], [[\x8%%]])
lexerror([["\xAG]], [[\xAG]])
lexerror([["\g"]], [[\g]])
lexerror([["\g]], [[\g]])
lexerror([["\."]], [[\%.]])
lexerror([["\999"]], [[\999"]])
lexerror([["xyz\300"]], [[\300"]])
lexerror([[" \256"]], [[\256"]])
-- errors in UTF-8 sequences
lexerror([["abc\u{110000}"]], [[abc\u{110000]]) -- too large
lexerror([["abc\u11r"]], [[abc\u1]]) -- missing '{'
lexerror([["abc\u"]], [[abc\u"]]) -- missing '{'
lexerror([["abc\u{11r"]], [[abc\u{11r]]) -- missing '}'
lexerror([["abc\u{11"]], [[abc\u{11"]]) -- missing '}'
lexerror([["abc\u{11]], [[abc\u{11]]) -- missing '}'
lexerror([["abc\u{r"]], [[abc\u{r]]) -- no digits
-- unfinished strings
lexerror("[=[alo]]", "<eof>")
lexerror("[=[alo]=", "<eof>")
lexerror("[=[alo]", "<eof>")
lexerror("'alo", "<eof>")
lexerror("'alo \\z \n\n", "<eof>")
lexerror("'alo \\z", "<eof>")
lexerror([['alo \98]], "<eof>")
-- valid characters in variable names
for i = 0, 255 do
local s = string.char(i)
assert(not string.find(s, "[a-zA-Z_]") == not load(s .. "=1", ""))
assert(not string.find(s, "[a-zA-Z_0-9]") ==
not load("a" .. s .. "1 = 1", ""))
end
-- long variable names
var1 = string.rep('a', 15000) .. '1'
var2 = string.rep('a', 15000) .. '2'
prog = string.format([[
%s = 5
%s = %s + 1
return function () return %s - %s end
]], var1, var2, var1, var1, var2)
local f = dostring(prog)
assert(_G[var1] == 5 and _G[var2] == 6 and f() == -1)
var1, var2, f = nil
print('+')
-- escapes --
assert("\n\t" == [[
]])
assert([[
$debug]] == "\n $debug")
assert([[ [ ]] ~= [[ ] ]])
-- long strings --
b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789"
assert(string.len(b) == 960)
prog = [=[
print('+')
a1 = [["this is a 'string' with several 'quotes'"]]
a2 = "'quotes'"
assert(string.find(a1, a2) == 34)
print('+')
a1 = [==[temp = [[an arbitrary value]]; ]==]
assert(load(a1))()
assert(temp == 'an arbitrary value')
-- long strings --
b = "001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789001234567890123456789012345678901234567891234567890123456789012345678901234567890012345678901234567890123456789012345678912345678901234567890123456789012345678900123456789012345678901234567890123456789123456789012345678901234567890123456789"
assert(string.len(b) == 960)
print('+')
a = [[00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
00123456789012345678901234567890123456789123456789012345678901234567890123456789
]]
assert(string.len(a) == 1863)
assert(string.sub(a, 1, 40) == string.sub(b, 1, 40))
x = 1
]=]
print('+')
x = nil
dostring(prog)
assert(x)
prog = nil
a = nil
b = nil
-- testing line ends
prog = [[
a = 1 -- a comment
b = 2
x = [=[
hi
]=]
y = "\
hello\r\n\
"
return require"debug".getinfo(1).currentline
]]
for _, n in pairs{"\n", "\r", "\n\r", "\r\n"} do
local prog, nn = string.gsub(prog, "\n", n)
assert(dostring(prog) == nn)
assert(_G.x == "hi\n" and _G.y == "\nhello\r\n\n")
end
-- testing comments and strings with long brackets
a = [==[]=]==]
assert(a == "]=")
a = [==[[===[[=[]]=][====[]]===]===]==]
assert(a == "[===[[=[]]=][====[]]===]===")
a = [====[[===[[=[]]=][====[]]===]===]====]
assert(a == "[===[[=[]]=][====[]]===]===")
a = [=[]]]]]]]]]=]
assert(a == "]]]]]]]]")
--[===[
x y z [==[ blu foo
]==
]
]=]==]
error error]=]===]
-- generate all strings of four of these chars
local x = {"=", "[", "]", "\n"}
local len = 4
local function gen (c, n)
if n==0 then coroutine.yield(c)
else
for _, a in pairs(x) do
gen(c..a, n-1)
end
end
end
for s in coroutine.wrap(function () gen("", len) end) do
assert(s == load("return [====[\n"..s.."]====]", "")())
end
-- testing decimal point locale
if os.setlocale("pt_BR") or os.setlocale("ptb") then
assert(tonumber("3,4") == 3.4 and tonumber"3.4" == 3.4)
assert(tonumber(" -.4 ") == -0.4)
assert(tonumber(" +0x.41 ") == 0X0.41)
assert(not load("a = (3,4)"))
assert(assert(load("return 3.4"))() == 3.4)
assert(assert(load("return .4,3"))() == .4)
assert(assert(load("return 4."))() == 4.)
assert(assert(load("return 4.+.5"))() == 4.5)
assert(" 0x.1 " + " 0x,1" + "-0X.1\t" == 0x0.1)
assert(tonumber"inf" == nil and tonumber"NAN" == nil)
assert(assert(load(string.format("return %q", 4.51)))() == 4.51)
local a,b = load("return 4.5.")
assert(string.find(b, "'4%.5%.'"))
assert(os.setlocale("C"))
else
(Message or print)(
'\n >>> pt_BR locale not available: skipping decimal point tests <<<\n')
end
-- testing %q x line ends
local s = "a string with \r and \n and \r\n and \n\r"
local c = string.format("return %q", s)
assert(assert(load(c))() == s)
-- testing errors
assert(not load"a = 'non-ending string")
assert(not load"a = 'non-ending string\n'")
assert(not load"a = '\\345'")
assert(not load"a = [=x]")
print('OK')

View File

@ -0,0 +1,162 @@
-- $Id: locals.lua,v 1.37 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
print('testing local variables and environments')
local debug = require"debug"
-- bug in 5.1:
local function f(x) x = nil; return x end
assert(f(10) == nil)
local function f() local x; return x end
assert(f(10) == nil)
local function f(x) x = nil; local y; return x, y end
assert(f(10) == nil and select(2, f(20)) == nil)
do
local i = 10
do local i = 100; assert(i==100) end
do local i = 1000; assert(i==1000) end
assert(i == 10)
if i ~= 10 then
local i = 20
else
local i = 30
assert(i == 30)
end
end
f = nil
local f
x = 1
a = nil
load('local a = {}')()
assert(a == nil)
function f (a)
local _1, _2, _3, _4, _5
local _6, _7, _8, _9, _10
local x = 3
local b = a
local c,d = a,b
if (d == b) then
local x = 'q'
x = b
assert(x == 2)
else
assert(nil)
end
assert(x == 3)
local f = 10
end
local b=10
local a; repeat local b; a,b=1,2; assert(a+1==b); until a+b==3
assert(x == 1)
f(2)
assert(type(f) == 'function')
local function getenv (f)
local a,b = debug.getupvalue(f, 1)
assert(a == '_ENV')
return b
end
-- test for global table of loaded chunks
assert(getenv(load"a=3") == _G)
local c = {}; local f = load("a = 3", nil, nil, c)
assert(getenv(f) == c)
assert(c.a == nil)
f()
assert(c.a == 3)
-- old test for limits for special instructions (now just a generic test)
do
local i = 2
local p = 4 -- p == 2^i
repeat
for j=-3,3 do
assert(load(string.format([[local a=%s;
a=a+%s;
assert(a ==2^%s)]], j, p-j, i), '')) ()
assert(load(string.format([[local a=%s;
a=a-%s;
assert(a==-2^%s)]], -j, p-j, i), '')) ()
assert(load(string.format([[local a,b=0,%s;
a=b-%s;
assert(a==-2^%s)]], -j, p-j, i), '')) ()
end
p = 2 * p; i = i + 1
until p <= 0
end
print'+'
if rawget(_G, "querytab") then
-- testing clearing of dead elements from tables
collectgarbage("stop") -- stop GC
local a = {[{}] = 4, [3] = 0, alo = 1,
a1234567890123456789012345678901234567890 = 10}
local t = querytab(a)
for k,_ in pairs(a) do a[k] = nil end
collectgarbage() -- restore GC and collect dead fiels in `a'
for i=0,t-1 do
local k = querytab(a, i)
assert(k == nil or type(k) == 'number' or k == 'alo')
end
end
-- testing lexical environments
assert(_ENV == _G)
do
local dummy
local _ENV = (function (...) return ... end)(_G, dummy) -- {
do local _ENV = {assert=assert}; assert(true) end
mt = {_G = _G}
local foo,x
A = false -- "declare" A
do local _ENV = mt
function foo (x)
A = x
do local _ENV = _G; A = 1000 end
return function (x) return A .. x end
end
end
assert(getenv(foo) == mt)
x = foo('hi'); assert(mt.A == 'hi' and A == 1000)
assert(x('*') == mt.A .. '*')
do local _ENV = {assert=assert, A=10};
do local _ENV = {assert=assert, A=20};
assert(A==20);x=A
end
assert(A==10 and x==20)
end
assert(x==20)
print('OK')
return 5,f
end -- }

View File

@ -0,0 +1,383 @@
# testing special comment on first line
-- $Id: main.lua,v 1.65 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
-- most (all?) tests here assume a reasonable "Unix-like" shell
_port=true
if _port then return end
-- use only "double quotes" inside shell scripts (better change to
-- run on Windows)
print ("testing stand-alone interpreter")
assert(os.execute()) -- machine has a system command
local arg = arg or _ARG
local prog = os.tmpname()
local otherprog = os.tmpname()
local out = os.tmpname()
local progname
do
local i = 0
while arg[i] do i=i-1 end
progname = arg[i+1]
end
print("progname: "..progname)
local prepfile = function (s, p)
p = p or prog
io.output(p)
io.write(s)
assert(io.close())
end
local function getoutput ()
io.input(out)
local t = io.read("a")
io.input():close()
assert(os.remove(out))
return t
end
local function checkprogout (s)
local t = getoutput()
for line in string.gmatch(s, ".-\n") do
assert(string.find(t, line, 1, true))
end
end
local function checkout (s)
local t = getoutput()
if s ~= t then print(string.format("'%s' - '%s'\n", s, t)) end
assert(s == t)
return t
end
local function RUN (p, ...)
p = string.gsub(p, "lua", '"'..progname..'"', 1)
local s = string.format(p, ...)
assert(os.execute(s))
end
local function NoRun (msg, p, ...)
p = string.gsub(p, "lua", '"'..progname..'"', 1)
local s = string.format(p, ...)
s = string.format("%s 2> %s", s, out) -- will send error to 'out'
assert(not os.execute(s))
assert(string.find(getoutput(), msg, 1, true)) -- check error message
end
RUN('lua -v')
print(string.format("(temporary program file used in these tests: %s)", prog))
-- running stdin as a file
prepfile""
RUN('lua - < %s > %s', prog, out)
checkout("")
prepfile[[
print(
1, a
)
]]
RUN('lua - < %s > %s', prog, out)
checkout("1\tnil\n")
RUN('echo "print(10)\nprint(2)\n" | lua > %s', out)
checkout("10\n2\n")
-- test option '-'
RUN('echo "print(arg[1])" | lua - -h > %s', out)
checkout("-h\n")
-- test environment variables used by Lua
prepfile("print(package.path)")
-- test LUA_PATH
RUN('env LUA_INIT= LUA_PATH=x lua %s > %s', prog, out)
checkout("x\n")
-- test LUA_PATH_version
RUN('env LUA_INIT= LUA_PATH_5_3=y LUA_PATH=x lua %s > %s', prog, out)
checkout("y\n")
-- test LUA_CPATH
prepfile("print(package.cpath)")
RUN('env LUA_INIT= LUA_CPATH=xuxu lua %s > %s', prog, out)
checkout("xuxu\n")
-- test LUA_CPATH_version
RUN('env LUA_INIT= LUA_CPATH_5_3=yacc LUA_CPATH=x lua %s > %s', prog, out)
checkout("yacc\n")
-- test LUA_INIT (and its access to 'arg' table)
prepfile("print(X)")
RUN('env LUA_INIT="X=tonumber(arg[1])" lua %s 3.2 > %s', prog, out)
checkout("3.2\n")
-- test LUA_INIT_version
prepfile("print(X)")
RUN('env LUA_INIT_5_3="X=10" LUA_INIT="X=3" lua %s > %s', prog, out)
checkout("10\n")
-- test LUA_INIT for files
prepfile("x = x or 10; print(x); x = x + 1")
RUN('env LUA_INIT="@%s" lua %s > %s', prog, prog, out)
checkout("10\n11\n")
-- test errors in LUA_INIT
NoRun('LUA_INIT:1: msg', 'env LUA_INIT="error(\'msg\')" lua')
-- test option '-E'
local defaultpath, defaultCpath
do
prepfile("print(package.path, package.cpath)")
RUN('env LUA_INIT="error(10)" LUA_PATH=xxx LUA_CPATH=xxx lua -E %s > %s',
prog, out)
local out = getoutput()
defaultpath = string.match(out, "^(.-)\t")
defaultCpath = string.match(out, "\t(.-)$")
end
-- paths did not changed
assert(not string.find(defaultpath, "xxx") and
string.find(defaultpath, "lua") and
not string.find(defaultCpath, "xxx") and
string.find(defaultCpath, "lua"))
-- test replacement of ';;' to default path
local function convert (p)
prepfile("print(package.path)")
RUN('env LUA_PATH="%s" lua %s > %s', p, prog, out)
local expected = getoutput()
expected = string.sub(expected, 1, -2) -- cut final end of line
assert(string.gsub(p, ";;", ";"..defaultpath..";") == expected)
end
convert(";")
convert(";;")
convert(";;;")
convert(";;;;")
convert(";;;;;")
convert(";;a;;;bc")
-- test -l over multiple libraries
prepfile("print(1); a=2; return {x=15}")
prepfile(("print(a); print(_G['%s'].x)"):format(prog), otherprog)
RUN('env LUA_PATH="?;;" lua -l %s -l%s -lstring -l io %s > %s', prog, otherprog, otherprog, out)
checkout("1\n2\n15\n2\n15\n")
-- test 'arg' table
local a = [[
assert(#arg == 3 and arg[1] == 'a' and
arg[2] == 'b' and arg[3] == 'c')
assert(arg[-1] == '--' and arg[-2] == "-e " and arg[-3] == '%s')
assert(arg[4] == nil and arg[-4] == nil)
local a, b, c = ...
assert(... == 'a' and a == 'a' and b == 'b' and c == 'c')
]]
a = string.format(a, progname)
prepfile(a)
RUN('lua "-e " -- %s a b c', prog) -- "-e " runs an empty command
-- test 'arg' availability in libraries
prepfile"assert(arg)"
prepfile("assert(arg)", otherprog)
RUN('env LUA_PATH="?;;" lua -l%s - < %s', prog, otherprog)
-- test messing up the 'arg' table
RUN('echo "print(...)" | lua -e "arg[1] = 100" - > %s', out)
checkout("100\n")
NoRun("'arg' is not a table", 'echo "" | lua -e "arg = 1" -')
-- test error in 'print'
RUN('echo 10 | lua -e "print=nil" -i > /dev/null 2> %s', out)
assert(string.find(getoutput(), "error calling 'print'"))
-- test 'debug.debug'
RUN('echo "io.stderr:write(1000)\ncont" | lua -e "require\'debug\'.debug()" 2> %s', out)
checkout("lua_debug> 1000lua_debug> ")
-- test many arguments
prepfile[[print(({...})[30])]]
RUN('lua %s %s > %s', prog, string.rep(" a", 30), out)
checkout("a\n")
RUN([[lua "-eprint(1)" -ea=3 -e "print(a)" > %s]], out)
checkout("1\n3\n")
-- test iteractive mode
prepfile[[
(6*2-6) -- ===
a =
10
print(a)
a]]
RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
checkprogout("6\n10\n10\n\n")
prepfile("a = [[b\nc\nd\ne]]\n=a")
RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
checkprogout("b\nc\nd\ne\n\n")
prompt = "alo"
prepfile[[ --
a = 2
]]
RUN([[lua "-e_PROMPT='%s'" -i < %s > %s]], prompt, prog, out)
local t = getoutput()
assert(string.find(t, prompt .. ".*" .. prompt .. ".*" .. prompt))
-- test for error objects
prepfile[[
debug = require "debug"
m = {x=0}
setmetatable(m, {__tostring = function(x)
return tostring(debug.getinfo(4).currentline + x.x)
end})
error(m)
]]
NoRun(progname .. ": 6\n", [[lua %s]], prog)
prepfile("error{}")
NoRun("error object is a table value", [[lua %s]], prog)
-- chunk broken in many lines
s = [=[ --
function f ( x )
local a = [[
xuxu
]]
local b = "\
xuxu\n"
if x == 11 then return 1 + 12 , 2 + 20 end --[[ test multiple returns ]]
return x + 1
--\\
end
return( f( 100 ) )
assert( a == b )
do return f( 11 ) end ]=]
s = string.gsub(s, ' ', '\n\n') -- change all spaces for newlines
prepfile(s)
RUN([[lua -e"_PROMPT='' _PROMPT2=''" -i < %s > %s]], prog, out)
checkprogout("101\n13\t22\n\n")
prepfile[[#comment in 1st line without \n at the end]]
RUN('lua %s', prog)
prepfile[[#test line number when file starts with comment line
debug = require"debug"
print(debug.getinfo(1).currentline)
]]
RUN('lua %s > %s', prog, out)
checkprogout('3')
-- close Lua with an open file
prepfile(string.format([[io.output(%q); io.write('alo')]], out))
RUN('lua %s', prog)
checkout('alo')
-- bug in 5.2 beta (extra \0 after version line)
RUN([[lua -v -e"print'hello'" > %s]], out)
t = getoutput()
assert(string.find(t, "PUC%-Rio\nhello"))
-- testing os.exit
prepfile("os.exit(nil, true)")
RUN('lua %s', prog)
prepfile("os.exit(0, true)")
RUN('lua %s', prog)
prepfile("os.exit(true, true)")
RUN('lua %s', prog)
prepfile("os.exit(1, true)")
NoRun("", "lua %s", prog) -- no message
prepfile("os.exit(false, true)")
NoRun("", "lua %s", prog) -- no message
-- remove temporary files
assert(os.remove(prog))
assert(os.remove(otherprog))
assert(not os.remove(out))
-- invalid options
NoRun("unrecognized option '-h'", "lua -h")
NoRun("unrecognized option '---'", "lua ---")
NoRun("unrecognized option '-Ex'", "lua -Ex")
NoRun("unrecognized option '-vv'", "lua -vv")
NoRun("unrecognized option '-iv'", "lua -iv")
NoRun("'-e' needs argument", "lua -e")
NoRun("syntax error", "lua -e a")
NoRun("'-l' needs argument", "lua -l")
if T then -- auxiliary library?
print("testing 'not enough memory' to create a state")
NoRun("not enough memory", "env MEMLIMIT=100 lua")
end
print('+')
print('testing Ctrl C')
do
-- interrupt a script
local function kill (pid)
return os.execute(string.format('kill -INT %d 2> /dev/null', pid))
end
-- function to run a script in background, returning its output file
-- descriptor and its pid
local function runback (luaprg)
-- shell script to run 'luaprg' in background and echo its pid
local shellprg = string.format('%s -e "%s" & echo $!', progname, luaprg)
local f = io.popen(shellprg, "r") -- run shell script
local pid = f:read() -- get pid for Lua script
print("(if test fails now, it may leave a Lua script running in \z
background, pid " .. pid .. ")")
return f, pid
end
-- Lua script that runs protected infinite loop and then prints '42'
local f, pid = runback[[
pcall(function () print(12); while true do end end); print(42)]]
-- wait until script is inside 'pcall'
assert(f:read() == "12")
kill(pid) -- send INT signal to Lua script
-- check that 'pcall' captured the exception and script continued running
assert(f:read() == "42") -- expected output
assert(f:close())
print("done")
-- Lua script in a long unbreakable search
local f, pid = runback[[
print(15); string.find(string.rep('a', 100000), '.*b')]]
-- wait (so script can reach the loop)
assert(f:read() == "15")
assert(os.execute("sleep 1"))
-- must send at least two INT signals to stop this Lua script
local n = 100
for i = 0, 100 do -- keep sending signals
if not kill(pid) then -- until it fails
n = i -- number of non-failed kills
break
end
end
assert(f:close())
assert(n >= 2)
print(string.format("done (with %d kills)", n))
end
print("OK")

View File

@ -0,0 +1,824 @@
-- $Id: math.lua,v 1.78 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
print("testing numbers and math lib")
local minint = math.mininteger
local maxint = math.maxinteger
local intbits = math.floor(math.log(maxint, 2) + 0.5) + 1
assert((1 << intbits) == 0)
assert(minint == 1 << (intbits - 1))
assert(maxint == minint - 1)
-- number of bits in the mantissa of a floating-point number
local floatbits = 24
do
local p = 2.0^floatbits
while p < p + 1.0 do
p = p * 2.0
floatbits = floatbits + 1
end
end
local function isNaN (x)
return (x ~= x)
end
assert(isNaN(0/0))
assert(not isNaN(1/0))
do
local x = 2.0^floatbits
assert(x > x - 1.0 and x == x + 1.0)
print(string.format("%d-bit integers, %d-bit (mantissa) floats",
intbits, floatbits))
end
assert(math.type(0) == "integer" and math.type(0.0) == "float"
and math.type("10") == nil)
local function checkerror (msg, f, ...)
local s, err = pcall(f, ...)
assert(not s and string.find(err, msg))
end
local msgf2i = "number.* has no integer representation"
-- float equality
function eq (a,b,limit)
if not limit then
if floatbits >= 50 then limit = 1E-11
else limit = 1E-5
end
end
-- a == b needed for +inf/-inf
return a == b or math.abs(a-b) <= limit
end
-- equality with types
function eqT (a,b)
return a == b and math.type(a) == math.type(b)
end
-- basic float notation
assert(0e12 == 0 and .0 == 0 and 0. == 0 and .2e2 == 20 and 2.E-1 == 0.2)
do
local a,b,c = "2", " 3e0 ", " 10 "
assert(a+b == 5 and -b == -3 and b+"2" == 5 and "10"-c == 0)
assert(type(a) == 'string' and type(b) == 'string' and type(c) == 'string')
assert(a == "2" and b == " 3e0 " and c == " 10 " and -c == -" 10 ")
assert(c%a == 0 and a^b == 08)
a = 0
assert(a == -a and 0 == -0)
end
do
local x = -1
local mz = 0/x -- minus zero
t = {[0] = 10, 20, 30, 40, 50}
assert(t[mz] == t[0] and t[-0] == t[0])
end
do -- tests for 'modf'
local a,b = math.modf(3.5)
assert(a == 3.0 and b == 0.5)
a,b = math.modf(-2.5)
assert(a == -2.0 and b == -0.5)
a,b = math.modf(-3e23)
assert(a == -3e23 and b == 0.0)
a,b = math.modf(3e35)
assert(a == 3e35 and b == 0.0)
a,b = math.modf(-1/0) -- -inf
assert(a == -1/0 and b == 0.0)
a,b = math.modf(1/0) -- inf
assert(a == 1/0 and b == 0.0)
a,b = math.modf(0/0) -- NaN
assert(isNaN(a) and isNaN(b))
a,b = math.modf(3) -- integer argument
assert(eqT(a, 3) and eqT(b, 0.0))
a,b = math.modf(minint)
assert(eqT(a, minint) and eqT(b, 0.0))
end
assert(math.huge > 10e30)
assert(-math.huge < -10e30)
-- integer arithmetic
assert(minint < minint + 1)
assert(maxint - 1 < maxint)
assert(0 - minint == minint)
assert(minint * minint == 0)
assert(maxint * maxint * maxint == maxint)
-- testing floor division and conversions
for _, i in pairs{-16, -15, -3, -2, -1, 0, 1, 2, 3, 15} do
for _, j in pairs{-16, -15, -3, -2, -1, 1, 2, 3, 15} do
for _, ti in pairs{0, 0.0} do -- try 'i' as integer and as float
for _, tj in pairs{0, 0.0} do -- try 'j' as integer and as float
local x = i + ti
local y = j + tj
assert(i//j == math.floor(i/j))
end
end
end
end
assert(1//0.0 == 1/0)
assert(-1 // 0.0 == -1/0)
assert(eqT(3.5 // 1.5, 2.0))
assert(eqT(3.5 // -1.5, -3.0))
assert(maxint // maxint == 1)
assert(maxint // 1 == maxint)
assert((maxint - 1) // maxint == 0)
assert(maxint // (maxint - 1) == 1)
assert(minint // minint == 1)
assert(minint // minint == 1)
assert((minint + 1) // minint == 0)
assert(minint // (minint + 1) == 1)
assert(minint // 1 == minint)
assert(minint // -1 == -minint)
assert(minint // -2 == 2^(intbits - 2))
assert(maxint // -1 == -maxint)
-- negative exponents
do
assert(2^-3 == 1 / 2^3)
assert(eq((-3)^-3, 1 / (-3)^3))
for i = -3, 3 do -- variables avoid constant folding
for j = -3, 3 do
-- domain errors (0^(-n)) are not portable
if not _port or i ~= 0 or j > 0 then
assert(eq(i^j, 1 / i^(-j)))
end
end
end
end
-- comparison between floats and integers (border cases)
if floatbits < intbits then
assert(2.0^floatbits == (1 << floatbits))
assert(2.0^floatbits - 1.0 == (1 << floatbits) - 1.0)
assert(2.0^floatbits - 1.0 ~= (1 << floatbits))
-- float is rounded, int is not
assert(2.0^floatbits + 1.0 ~= (1 << floatbits) + 1)
else -- floats can express all integers with full accuracy
assert(maxint == maxint + 0.0)
assert(maxint - 1 == maxint - 1.0)
assert(minint + 1 == minint + 1.0)
assert(maxint ~= maxint - 1.0)
end
assert(maxint + 0.0 == 2.0^(intbits - 1) - 1.0)
assert(minint + 0.0 == minint)
assert(minint + 0.0 == -2.0^(intbits - 1))
-- order between floats and integers
assert(1 < 1.1); assert(not (1 < 0.9))
assert(1 <= 1.1); assert(not (1 <= 0.9))
assert(-1 < -0.9); assert(not (-1 < -1.1))
assert(1 <= 1.1); assert(not (-1 <= -1.1))
assert(-1 < -0.9); assert(not (-1 < -1.1))
assert(-1 <= -0.9); assert(not (-1 <= -1.1))
assert(minint <= minint + 0.0)
assert(minint + 0.0 <= minint)
assert(not (minint < minint + 0.0))
assert(not (minint + 0.0 < minint))
assert(maxint < minint * -1.0)
assert(maxint <= minint * -1.0)
do
local fmaxi1 = 2^(intbits - 1)
assert(maxint < fmaxi1)
assert(maxint <= fmaxi1)
assert(not (fmaxi1 <= maxint))
assert(minint <= -2^(intbits - 1))
assert(-2^(intbits - 1) <= minint)
end
if floatbits < intbits then
print("testing order (floats cannot represent all integers)")
local fmax = 2^floatbits
local ifmax = fmax | 0
assert(fmax < ifmax + 1)
assert(fmax - 1 < ifmax)
assert(-(fmax - 1) > -ifmax)
assert(not (fmax <= ifmax - 1))
assert(-fmax > -(ifmax + 1))
assert(not (-fmax >= -(ifmax - 1)))
assert(fmax/2 - 0.5 < ifmax//2)
assert(-(fmax/2 - 0.5) > -ifmax//2)
assert(maxint < 2^intbits)
assert(minint > -2^intbits)
assert(maxint <= 2^intbits)
assert(minint >= -2^intbits)
else
print("testing order (floats can represent all integers)")
assert(maxint < maxint + 1.0)
assert(maxint < maxint + 0.5)
assert(maxint - 1.0 < maxint)
assert(maxint - 0.5 < maxint)
assert(not (maxint + 0.0 < maxint))
assert(maxint + 0.0 <= maxint)
assert(not (maxint < maxint + 0.0))
assert(maxint + 0.0 <= maxint)
assert(maxint <= maxint + 0.0)
assert(not (maxint + 1.0 <= maxint))
assert(not (maxint + 0.5 <= maxint))
assert(not (maxint <= maxint - 1.0))
assert(not (maxint <= maxint - 0.5))
assert(minint < minint + 1.0)
assert(minint < minint + 0.5)
assert(minint <= minint + 0.5)
assert(minint - 1.0 < minint)
assert(minint - 1.0 <= minint)
assert(not (minint + 0.0 < minint))
assert(not (minint + 0.5 < minint))
assert(not (minint < minint + 0.0))
assert(minint + 0.0 <= minint)
assert(minint <= minint + 0.0)
assert(not (minint + 1.0 <= minint))
assert(not (minint + 0.5 <= minint))
assert(not (minint <= minint - 1.0))
end
do
local NaN = 0/0
assert(not (NaN < 0))
assert(not (NaN > minint))
assert(not (NaN <= -9))
assert(not (NaN <= maxint))
assert(not (NaN < maxint))
assert(not (minint <= NaN))
assert(not (minint < NaN))
end
-- avoiding errors at compile time
local function checkcompt (msg, code)
checkerror(msg, assert(load(code)))
end
checkcompt("divide by zero", "return 2 // 0")
checkcompt(msgf2i, "return 2.3 >> 0")
checkcompt(msgf2i, ("return 2.0^%d & 1"):format(intbits - 1))
checkcompt("field 'huge'", "return math.huge << 1")
checkcompt(msgf2i, ("return 1 | 2.0^%d"):format(intbits - 1))
checkcompt(msgf2i, "return 2.3 ~ '0.0'")
-- testing overflow errors when converting from float to integer (runtime)
local function f2i (x) return x | x end
checkerror(msgf2i, f2i, math.huge) -- +inf
checkerror(msgf2i, f2i, -math.huge) -- -inf
checkerror(msgf2i, f2i, 0/0) -- NaN
if floatbits < intbits then
-- conversion tests when float cannot represent all integers
assert(maxint + 1.0 == maxint + 0.0)
assert(minint - 1.0 == minint + 0.0)
checkerror(msgf2i, f2i, maxint + 0.0)
assert(f2i(2.0^(intbits - 2)) == 1 << (intbits - 2))
assert(f2i(-2.0^(intbits - 2)) == -(1 << (intbits - 2)))
assert((2.0^(floatbits - 1) + 1.0) // 1 == (1 << (floatbits - 1)) + 1)
-- maximum integer representable as a float
local mf = maxint - (1 << (floatbits - intbits)) + 1
assert(f2i(mf + 0.0) == mf) -- OK up to here
mf = mf + 1
assert(f2i(mf + 0.0) ~= mf) -- no more representable
else
-- conversion tests when float can represent all integers
assert(maxint + 1.0 > maxint)
assert(minint - 1.0 < minint)
assert(f2i(maxint + 0.0) == maxint)
checkerror("no integer rep", f2i, maxint + 1.0)
checkerror("no integer rep", f2i, minint - 1.0)
end
-- 'minint' should be representable as a float no matter the precision
assert(f2i(minint + 0.0) == minint)
-- testing numeric strings
assert("2" + 1 == 3)
assert("2 " + 1 == 3)
assert(" -2 " + 1 == -1)
assert(" -0xa " + 1 == -9)
-- Literal integer Overflows (new behavior in 5.3.3)
do
-- no overflows
assert(eqT(tonumber(tostring(maxint)), maxint))
assert(eqT(tonumber(tostring(minint)), minint))
-- add 1 to last digit as a string (it cannot be 9...)
local function incd (n)
local s = string.format("%d", n)
s = string.gsub(s, "%d$", function (d)
assert(d ~= '9')
return string.char(string.byte(d) + 1)
end)
return s
end
-- 'tonumber' with overflow by 1
assert(eqT(tonumber(incd(maxint)), maxint + 1.0))
assert(eqT(tonumber(incd(minint)), minint - 1.0))
-- large numbers
assert(eqT(tonumber("1"..string.rep("0", 30)), 1e30))
assert(eqT(tonumber("-1"..string.rep("0", 30)), -1e30))
-- hexa format still wraps around
assert(eqT(tonumber("0x1"..string.rep("0", 30)), 0))
-- lexer in the limits
assert(minint == load("return " .. minint)())
assert(eqT(maxint, load("return " .. maxint)()))
assert(eqT(10000000000000000000000.0, 10000000000000000000000))
assert(eqT(-10000000000000000000000.0, -10000000000000000000000))
end
-- testing 'tonumber'
-- 'tonumber' with numbers
assert(tonumber(3.4) == 3.4)
assert(eqT(tonumber(3), 3))
assert(eqT(tonumber(maxint), maxint) and eqT(tonumber(minint), minint))
assert(tonumber(1/0) == 1/0)
-- 'tonumber' with strings
assert(tonumber("0") == 0)
assert(tonumber("") == nil)
assert(tonumber(" ") == nil)
assert(tonumber("-") == nil)
assert(tonumber(" -0x ") == nil)
assert(tonumber{} == nil)
assert(tonumber'+0.01' == 1/100 and tonumber'+.01' == 0.01 and
tonumber'.01' == 0.01 and tonumber'-1.' == -1 and
tonumber'+1.' == 1)
assert(tonumber'+ 0.01' == nil and tonumber'+.e1' == nil and
tonumber'1e' == nil and tonumber'1.0e+' == nil and
tonumber'.' == nil)
assert(tonumber('-012') == -010-2)
assert(tonumber('-1.2e2') == - - -120)
assert(tonumber("0xffffffffffff") == (1 << (4*12)) - 1)
assert(tonumber("0x"..string.rep("f", (intbits//4))) == -1)
assert(tonumber("-0x"..string.rep("f", (intbits//4))) == 1)
-- testing 'tonumber' with base
assert(tonumber(' 001010 ', 2) == 10)
assert(tonumber(' 001010 ', 10) == 001010)
assert(tonumber(' -1010 ', 2) == -10)
assert(tonumber('10', 36) == 36)
assert(tonumber(' -10 ', 36) == -36)
assert(tonumber(' +1Z ', 36) == 36 + 35)
assert(tonumber(' -1z ', 36) == -36 + -35)
assert(tonumber('-fFfa', 16) == -(10+(16*(15+(16*(15+(16*15)))))))
assert(tonumber(string.rep('1', (intbits - 2)), 2) + 1 == 2^(intbits - 2))
assert(tonumber('ffffFFFF', 16)+1 == (1 << 32))
assert(tonumber('0ffffFFFF', 16)+1 == (1 << 32))
assert(tonumber('-0ffffffFFFF', 16) - 1 == -(1 << 40))
for i = 2,36 do
local i2 = i * i
local i10 = i2 * i2 * i2 * i2 * i2 -- i^10
assert(tonumber('\t10000000000\t', i) == i10)
end
if not _soft then
-- tests with very long numerals
assert(tonumber("0x"..string.rep("f", 13)..".0") == 2.0^(4*13) - 1)
assert(tonumber("0x"..string.rep("f", 150)..".0") == 2.0^(4*150) - 1)
assert(tonumber("0x"..string.rep("f", 300)..".0") == 2.0^(4*300) - 1)
assert(tonumber("0x"..string.rep("f", 500)..".0") == 2.0^(4*500) - 1)
assert(tonumber('0x3.' .. string.rep('0', 1000)) == 3)
assert(tonumber('0x' .. string.rep('0', 1000) .. 'a') == 10)
assert(tonumber('0x0.' .. string.rep('0', 13).."1") == 2.0^(-4*14))
assert(tonumber('0x0.' .. string.rep('0', 150).."1") == 2.0^(-4*151))
assert(tonumber('0x0.' .. string.rep('0', 300).."1") == 2.0^(-4*301))
assert(tonumber('0x0.' .. string.rep('0', 500).."1") == 2.0^(-4*501))
assert(tonumber('0xe03' .. string.rep('0', 1000) .. 'p-4000') == 3587.0)
assert(tonumber('0x.' .. string.rep('0', 1000) .. '74p4004') == 0x7.4)
end
-- testing 'tonumber' for invalid formats
local function f (...)
if select('#', ...) == 1 then
return (...)
else
return "***"
end
end
assert(f(tonumber('fFfa', 15)) == nil)
assert(f(tonumber('099', 8)) == nil)
assert(f(tonumber('1\0', 2)) == nil)
assert(f(tonumber('', 8)) == nil)
assert(f(tonumber(' ', 9)) == nil)
assert(f(tonumber(' ', 9)) == nil)
assert(f(tonumber('0xf', 10)) == nil)
assert(f(tonumber('inf')) == nil)
assert(f(tonumber(' INF ')) == nil)
assert(f(tonumber('Nan')) == nil)
assert(f(tonumber('nan')) == nil)
assert(f(tonumber(' ')) == nil)
assert(f(tonumber('')) == nil)
assert(f(tonumber('1 a')) == nil)
assert(f(tonumber('1 a', 2)) == nil)
assert(f(tonumber('1\0')) == nil)
assert(f(tonumber('1 \0')) == nil)
assert(f(tonumber('1\0 ')) == nil)
assert(f(tonumber('e1')) == nil)
assert(f(tonumber('e 1')) == nil)
assert(f(tonumber(' 3.4.5 ')) == nil)
-- testing 'tonumber' for invalid hexadecimal formats
assert(tonumber('0x') == nil)
assert(tonumber('x') == nil)
assert(tonumber('x3') == nil)
assert(tonumber('0x3.3.3') == nil) -- two decimal points
assert(tonumber('00x2') == nil)
assert(tonumber('0x 2') == nil)
assert(tonumber('0 x2') == nil)
assert(tonumber('23x') == nil)
assert(tonumber('- 0xaa') == nil)
assert(tonumber('-0xaaP ') == nil) -- no exponent
assert(tonumber('0x0.51p') == nil)
assert(tonumber('0x5p+-2') == nil)
-- testing hexadecimal numerals
assert(0x10 == 16 and 0xfff == 2^12 - 1 and 0XFB == 251)
assert(0x0p12 == 0 and 0x.0p-3 == 0)
assert(0xFFFFFFFF == (1 << 32) - 1)
assert(tonumber('+0x2') == 2)
assert(tonumber('-0xaA') == -170)
assert(tonumber('-0xffFFFfff') == -(1 << 32) + 1)
-- possible confusion with decimal exponent
assert(0E+1 == 0 and 0xE+1 == 15 and 0xe-1 == 13)
-- floating hexas
assert(tonumber(' 0x2.5 ') == 0x25/16)
assert(tonumber(' -0x2.5 ') == -0x25/16)
assert(tonumber(' +0x0.51p+8 ') == 0x51)
assert(0x.FfffFFFF == 1 - '0x.00000001')
assert('0xA.a' + 0 == 10 + 10/16)
assert(0xa.aP4 == 0XAA)
assert(0x4P-2 == 1)
assert(0x1.1 == '0x1.' + '+0x.1')
assert(0Xabcdef.0 == 0x.ABCDEFp+24)
assert(1.1 == 1.+.1)
assert(100.0 == 1E2 and .01 == 1e-2)
assert(1111111111 - 1111111110 == 1000.00e-03)
assert(1.1 == '1.'+'.1')
assert(tonumber'1111111111' - tonumber'1111111110' ==
tonumber" +0.001e+3 \n\t")
assert(0.1e-30 > 0.9E-31 and 0.9E30 < 0.1e31)
assert(0.123456 > 0.123455)
assert(tonumber('+1.23E18') == 1.23*10.0^18)
-- testing order operators
assert(not(1<1) and (1<2) and not(2<1))
assert(not('a'<'a') and ('a'<'b') and not('b'<'a'))
assert((1<=1) and (1<=2) and not(2<=1))
assert(('a'<='a') and ('a'<='b') and not('b'<='a'))
assert(not(1>1) and not(1>2) and (2>1))
assert(not('a'>'a') and not('a'>'b') and ('b'>'a'))
assert((1>=1) and not(1>=2) and (2>=1))
assert(('a'>='a') and not('a'>='b') and ('b'>='a'))
assert(1.3 < 1.4 and 1.3 <= 1.4 and not (1.3 < 1.3) and 1.3 <= 1.3)
-- testing mod operator
assert(eqT(-4 % 3, 2))
assert(eqT(4 % -3, -2))
assert(eqT(-4.0 % 3, 2.0))
assert(eqT(4 % -3.0, -2.0))
assert(math.pi - math.pi % 1 == 3)
assert(math.pi - math.pi % 0.001 == 3.141)
assert(eqT(minint % minint, 0))
assert(eqT(maxint % maxint, 0))
assert((minint + 1) % minint == minint + 1)
assert((maxint - 1) % maxint == maxint - 1)
assert(minint % maxint == maxint - 1)
assert(minint % -1 == 0)
assert(minint % -2 == 0)
assert(maxint % -2 == -1)
-- non-portable tests because Windows C library cannot compute
-- fmod(1, huge) correctly
if not _port then
local function anan (x) assert(isNaN(x)) end -- assert Not a Number
anan(0.0 % 0)
anan(1.3 % 0)
anan(math.huge % 1)
anan(math.huge % 1e30)
anan(-math.huge % 1e30)
anan(-math.huge % -1e30)
assert(1 % math.huge == 1)
assert(1e30 % math.huge == 1e30)
assert(1e30 % -math.huge == -math.huge)
assert(-1 % math.huge == math.huge)
assert(-1 % -math.huge == -1)
end
-- testing unsigned comparisons
assert(math.ult(3, 4))
assert(not math.ult(4, 4))
assert(math.ult(-2, -1))
assert(math.ult(2, -1))
assert(not math.ult(-2, -2))
assert(math.ult(maxint, minint))
assert(not math.ult(minint, maxint))
assert(eq(math.sin(-9.8)^2 + math.cos(-9.8)^2, 1))
assert(eq(math.tan(math.pi/4), 1))
assert(eq(math.sin(math.pi/2), 1) and eq(math.cos(math.pi/2), 0))
assert(eq(math.atan(1), math.pi/4) and eq(math.acos(0), math.pi/2) and
eq(math.asin(1), math.pi/2))
assert(eq(math.deg(math.pi/2), 90) and eq(math.rad(90), math.pi/2))
assert(math.abs(-10.43) == 10.43)
assert(eqT(math.abs(minint), minint))
assert(eqT(math.abs(maxint), maxint))
assert(eqT(math.abs(-maxint), maxint))
assert(eq(math.atan(1,0), math.pi/2))
assert(math.fmod(10,3) == 1)
assert(eq(math.sqrt(10)^2, 10))
assert(eq(math.log(2, 10), math.log(2)/math.log(10)))
assert(eq(math.log(2, 2), 1))
assert(eq(math.log(9, 3), 2))
assert(eq(math.exp(0), 1))
assert(eq(math.sin(10), math.sin(10%(2*math.pi))))
assert(tonumber(' 1.3e-2 ') == 1.3e-2)
assert(tonumber(' -1.00000000000001 ') == -1.00000000000001)
-- testing constant limits
-- 2^23 = 8388608
assert(8388609 + -8388609 == 0)
assert(8388608 + -8388608 == 0)
assert(8388607 + -8388607 == 0)
do -- testing floor & ceil
assert(eqT(math.floor(3.4), 3))
assert(eqT(math.ceil(3.4), 4))
assert(eqT(math.floor(-3.4), -4))
assert(eqT(math.ceil(-3.4), -3))
assert(eqT(math.floor(maxint), maxint))
assert(eqT(math.ceil(maxint), maxint))
assert(eqT(math.floor(minint), minint))
assert(eqT(math.floor(minint + 0.0), minint))
assert(eqT(math.ceil(minint), minint))
assert(eqT(math.ceil(minint + 0.0), minint))
assert(math.floor(1e50) == 1e50)
assert(math.ceil(1e50) == 1e50)
assert(math.floor(-1e50) == -1e50)
assert(math.ceil(-1e50) == -1e50)
for _, p in pairs{31,32,63,64} do
assert(math.floor(2^p) == 2^p)
assert(math.floor(2^p + 0.5) == 2^p)
assert(math.ceil(2^p) == 2^p)
assert(math.ceil(2^p - 0.5) == 2^p)
end
checkerror("number expected", math.floor, {})
checkerror("number expected", math.ceil, print)
assert(eqT(math.tointeger(minint), minint))
assert(eqT(math.tointeger(minint .. ""), minint))
assert(eqT(math.tointeger(maxint), maxint))
assert(eqT(math.tointeger(maxint .. ""), maxint))
assert(eqT(math.tointeger(minint + 0.0), minint))
assert(math.tointeger(0.0 - minint) == nil)
assert(math.tointeger(math.pi) == nil)
assert(math.tointeger(-math.pi) == nil)
assert(math.floor(math.huge) == math.huge)
assert(math.ceil(math.huge) == math.huge)
assert(math.tointeger(math.huge) == nil)
assert(math.floor(-math.huge) == -math.huge)
assert(math.ceil(-math.huge) == -math.huge)
assert(math.tointeger(-math.huge) == nil)
assert(math.tointeger("34.0") == 34)
assert(math.tointeger("34.3") == nil)
assert(math.tointeger({}) == nil)
assert(math.tointeger(0/0) == nil) -- NaN
end
-- testing fmod for integers
for i = -6, 6 do
for j = -6, 6 do
if j ~= 0 then
local mi = math.fmod(i, j)
local mf = math.fmod(i + 0.0, j)
assert(mi == mf)
assert(math.type(mi) == 'integer' and math.type(mf) == 'float')
if (i >= 0 and j >= 0) or (i <= 0 and j <= 0) or mi == 0 then
assert(eqT(mi, i % j))
end
end
end
end
assert(eqT(math.fmod(minint, minint), 0))
assert(eqT(math.fmod(maxint, maxint), 0))
assert(eqT(math.fmod(minint + 1, minint), minint + 1))
assert(eqT(math.fmod(maxint - 1, maxint), maxint - 1))
checkerror("zero", math.fmod, 3, 0)
do -- testing max/min
checkerror("value expected", math.max)
checkerror("value expected", math.min)
assert(eqT(math.max(3), 3))
assert(eqT(math.max(3, 5, 9, 1), 9))
assert(math.max(maxint, 10e60) == 10e60)
assert(eqT(math.max(minint, minint + 1), minint + 1))
assert(eqT(math.min(3), 3))
assert(eqT(math.min(3, 5, 9, 1), 1))
assert(math.min(3.2, 5.9, -9.2, 1.1) == -9.2)
assert(math.min(1.9, 1.7, 1.72) == 1.7)
assert(math.min(-10e60, minint) == -10e60)
assert(eqT(math.min(maxint, maxint - 1), maxint - 1))
assert(eqT(math.min(maxint - 2, maxint, maxint - 1), maxint - 2))
end
-- testing implicit convertions
local a,b = '10', '20'
assert(a*b == 200 and a+b == 30 and a-b == -10 and a/b == 0.5 and -b == -20)
assert(a == '10' and b == '20')
do
print("testing -0 and NaN")
local mz, z = -0.0, 0.0
assert(mz == z)
assert(1/mz < 0 and 0 < 1/z)
local a = {[mz] = 1}
assert(a[z] == 1 and a[mz] == 1)
a[z] = 2
assert(a[z] == 2 and a[mz] == 2)
local inf = math.huge * 2 + 1
mz, z = -1/inf, 1/inf
assert(mz == z)
assert(1/mz < 0 and 0 < 1/z)
local NaN = inf - inf
assert(NaN ~= NaN)
assert(not (NaN < NaN))
assert(not (NaN <= NaN))
assert(not (NaN > NaN))
assert(not (NaN >= NaN))
assert(not (0 < NaN) and not (NaN < 0))
local NaN1 = 0/0
assert(NaN ~= NaN1 and not (NaN <= NaN1) and not (NaN1 <= NaN))
local a = {}
assert(not pcall(rawset, a, NaN, 1))
assert(a[NaN] == nil)
a[1] = 1
assert(not pcall(rawset, a, NaN, 1))
assert(a[NaN] == nil)
-- strings with same binary representation as 0.0 (might create problems
-- for constant manipulation in the pre-compiler)
local a1, a2, a3, a4, a5 = 0, 0, "\0\0\0\0\0\0\0\0", 0, "\0\0\0\0\0\0\0\0"
assert(a1 == a2 and a2 == a4 and a1 ~= a3)
assert(a3 == a5)
end
print("testing 'math.random'")
math.randomseed(0)
do -- test random for floats
local max = -math.huge
local min = math.huge
for i = 0, 20000 do
local t = math.random()
assert(0 <= t and t < 1)
max = math.max(max, t)
min = math.min(min, t)
if eq(max, 1, 0.001) and eq(min, 0, 0.001) then
goto ok
end
end
-- loop ended without satisfing condition
assert(false)
::ok::
end
do
local function aux (p, lim) -- test random for small intervals
local x1, x2
if #p == 1 then x1 = 1; x2 = p[1]
else x1 = p[1]; x2 = p[2]
end
local mark = {}; local count = 0 -- to check that all values appeared
for i = 0, lim or 2000 do
local t = math.random(table.unpack(p))
assert(x1 <= t and t <= x2)
if not mark[t] then -- new value
mark[t] = true
count = count + 1
end
if count == x2 - x1 + 1 then -- all values appeared; OK
goto ok
end
end
-- loop ended without satisfing condition
assert(false)
::ok::
end
aux({-10,0})
aux({6})
aux({-10, 10})
aux({minint, minint})
aux({maxint, maxint})
aux({minint, minint + 9})
aux({maxint - 3, maxint})
end
do
local function aux(p1, p2) -- test random for large intervals
local max = minint
local min = maxint
local n = 200
local mark = {}; local count = 0 -- to count how many different values
for _ = 1, n do
local t = math.random(p1, p2)
max = math.max(max, t)
min = math.min(min, t)
if not mark[t] then -- new value
mark[t] = true
count = count + 1
end
end
-- at least 80% of values are different
assert(count >= n * 0.8)
-- min and max not too far from formal min and max
local diff = (p2 - p1) // 8
assert(min < p1 + diff and max > p2 - diff)
end
aux(0, maxint)
aux(1, maxint)
aux(minint, -1)
aux(minint // 2, maxint // 2)
end
for i=1,100 do
assert(math.random(maxint) > 0)
assert(math.random(minint, -1) < 0)
end
assert(not pcall(math.random, 1, 2, 3)) -- too many arguments
-- empty interval
assert(not pcall(math.random, minint + 1, minint))
assert(not pcall(math.random, maxint, maxint - 1))
assert(not pcall(math.random, maxint, minint))
-- interval too large
assert(not pcall(math.random, minint, 0))
assert(not pcall(math.random, -1, maxint))
assert(not pcall(math.random, minint // 2, maxint // 2 + 1))
print('OK')

View File

@ -0,0 +1,639 @@
-- $Id: nextvar.lua,v 1.79 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
print('testing tables, next, and for')
local function checkerror (msg, f, ...)
local s, err = pcall(f, ...)
assert(not s and string.find(err, msg))
end
local a = {}
-- make sure table has lots of space in hash part
for i=1,100 do a[i.."+"] = true end
for i=1,100 do a[i.."+"] = nil end
-- fill hash part with numeric indices testing size operator
for i=1,100 do
a[i] = true
assert(#a == i)
end
-- testing ipairs
local x = 0
for k,v in ipairs{10,20,30;x=12} do
x = x + 1
assert(k == x and v == x * 10)
end
for _ in ipairs{x=12, y=24} do assert(nil) end
-- test for 'false' x ipair
x = false
local i = 0
for k,v in ipairs{true,false,true,false} do
i = i + 1
x = not x
assert(x == v)
end
assert(i == 4)
-- iterator function is always the same
assert(type(ipairs{}) == 'function' and ipairs{} == ipairs{})
if not T then
(Message or print)
('\n >>> testC not active: skipping tests for table sizes <<<\n')
else --[
-- testing table sizes
local function log2 (x) return math.log(x, 2) end
local function mp2 (n) -- minimum power of 2 >= n
local mp = 2^math.ceil(log2(n))
assert(n == 0 or (mp/2 < n and n <= mp))
return mp
end
local function fb (n)
local r, nn = T.int2fb(n)
assert(r < 256)
return nn
end
-- test fb function
for a = 1, 10000 do -- all numbers up to 10^4
local n = fb(a)
assert(a <= n and n <= a*1.125)
end
local a = 1024 -- plus a few up to 2 ^30
local lim = 2^30
while a < lim do
local n = fb(a)
assert(a <= n and n <= a*1.125)
a = math.ceil(a*1.3)
end
local function check (t, na, nh)
local a, h = T.querytab(t)
if a ~= na or h ~= nh then
print(na, nh, a, h)
assert(nil)
end
end
-- testing C library sizes
do
local s = 0
for _ in pairs(math) do s = s + 1 end
check(math, 0, s) -- NodeMCU: ROM tables such as math are exactly sized.
end
-- testing constructor sizes
local lim = 40
local s = 'return {'
for i=1,lim do
s = s..i..','
local s = s
for k=0,lim do
local t = load(s..'}', '')()
assert(#t == i)
check(t, fb(i), mp2(k))
s = string.format('%sa%d=%d,', s, k, k)
end
end
-- tests with unknown number of elements
local a = {}
for i=1,lim do a[i] = i end -- build auxiliary table
for k=0,lim do
local a = {table.unpack(a,1,k)}
assert(#a == k)
check(a, k, 0)
a = {1,2,3,table.unpack(a,1,k)}
check(a, k+3, 0)
assert(#a == k + 3)
end
-- testing tables dynamically built
local lim = 130
local a = {}; a[2] = 1; check(a, 0, 1)
a = {}; a[0] = 1; check(a, 0, 1); a[2] = 1; check(a, 0, 2)
a = {}; a[0] = 1; a[1] = 1; check(a, 1, 1)
a = {}
for i = 1,lim do
a[i] = 1
assert(#a == i)
check(a, mp2(i), 0)
end
a = {}
for i = 1,lim do
a['a'..i] = 1
assert(#a == 0)
check(a, 0, mp2(i))
end
a = {}
for i=1,16 do a[i] = i end
check(a, 16, 0)
do
for i=1,11 do a[i] = nil end
for i=30,50 do a[i] = nil end -- force a rehash (?)
check(a, 0, 8) -- only 5 elements in the table
a[10] = 1
for i=30,50 do a[i] = nil end -- force a rehash (?)
check(a, 0, 8) -- only 6 elements in the table
for i=1,14 do a[i] = nil end
for i=18,50 do a[i] = nil end -- force a rehash (?)
check(a, 0, 4) -- only 2 elements ([15] and [16])
end
-- reverse filling
for i=1,lim do
local a = {}
for i=i,1,-1 do a[i] = i end -- fill in reverse
check(a, mp2(i), 0)
end
-- size tests for vararg
lim = 35
function foo (n, ...)
local arg = {...}
check(arg, n, 0)
assert(select('#', ...) == n)
arg[n+1] = true
check(arg, mp2(n+1), 0)
arg.x = true
check(arg, mp2(n+1), 1)
end
local a = {}
for i=1,lim do a[i] = true; foo(i, table.unpack(a)) end
end --]
-- test size operation on empty tables
assert(#{} == 0)
assert(#{nil} == 0)
assert(#{nil, nil} == 0)
assert(#{nil, nil, nil} == 0)
assert(#{nil, nil, nil, nil} == 0)
print'+'
local nofind = {}
a,b,c = 1,2,3
a,b,c = nil
-- next uses always the same iteraction function
assert(next{} == next{})
-- NodeMCU add optional table to find() and find1() for ROM support
local function find (name, t)
t = t or _G
local n,v
while 1 do
n,v = next(t, n)
if not n then return nofind end
assert(v ~= nil)
if n == name then return v end
end
end
local function find1 (name, t)
t = t or _G
for n,v in pairs(t) do
if n==name then return v end
end
return nil -- not found
end
local romG = ROM._G or ROM
assert(print==find("print",romG) and print == find1("print",romG))
assert(romG["print"]==find("print",romG))
assert(assert==find1("assert",romG))
assert(nofind==find("return"))
assert(not find1("return"))
_G["ret" .. "urn"] = nil
assert(nofind==find("return"))
_G["xxx"] = 1
assert(xxx==find("xxx"))
-- invalid key to 'next'
checkerror("invalid key", next, {10,20}, 3)
-- both 'pairs' and 'ipairs' need an argument
checkerror("bad argument", pairs)
checkerror("bad argument", ipairs)
print('+')
a = {}
for i=0,100 do
if math.fmod(i,10) ~= 0 then
a['x'..i] = i
end
end
n = {n=0}
for i,v in pairs(a) do
n.n = n.n+1
assert(i and v and a[i] == v)
end
assert(n.n == 90)
a = nil
do -- clear global table
local a = {}
for n,v in pairs(_G) do a[n]=v end
for n,v in pairs(a) do
if not package.loaded[n] and type(v) ~= "function" and
not string.find(n, "^[%u_]") then
_G[n] = nil
end
collectgarbage()
end
end
--
local function checknext (a)
local b = {}
do local k,v = next(a); while k do b[k] = v; k,v = next(a,k) end end
for k,v in pairs(b) do assert(a[k] == v) end
for k,v in pairs(a) do assert(b[k] == v) end
end
checknext{1,x=1,y=2,z=3}
checknext{1,2,x=1,y=2,z=3}
checknext{1,2,3,x=1,y=2,z=3}
checknext{1,2,3,4,x=1,y=2,z=3}
checknext{1,2,3,4,5,x=1,y=2,z=3}
assert(#{} == 0)
assert(#{[-1] = 2} == 0)
assert(#{1,2,3,nil,nil} == 3)
for i=0,40 do
local a = {}
for j=1,i do a[j]=j end
assert(#a == i)
end
-- 'maxn' is now deprecated, but it is easily defined in Lua.
--[[ NodeMCU: because table is in ROM we need to create RW wrapper to
extend the ROM table, and we can then modify this. Everything else
works fine thank to meta magic. ]]
table = setmetatable({}, {__index=table}) -- NodeMCU
function table.maxn (t)
local max = 0
for k in pairs(t) do
max = (type(k) == 'number') and math.max(max, k) or max
end
return max
end
assert(table.maxn{} == 0)
assert(table.maxn{["1000"] = true} == 0)
assert(table.maxn{["1000"] = true, [24.5] = 3} == 24.5)
assert(table.maxn{[1000] = true} == 1000)
assert(table.maxn{[10] = true, [100*math.pi] = print} == 100*math.pi)
-- table = nil -- NodeMCU: reset to ROM table
-- int overflow
a = {}
for i=0,50 do a[2^i] = true end
assert(a[#a])
print('+')
-- erasing values
local t = {[{1}] = 1, [{2}] = 2, [string.rep("x ", 4)] = 3,
[100.3] = 4, [4] = 5}
local n = 0
for k, v in pairs( t ) do
n = n+1
assert(t[k] == v)
t[k] = nil
collectgarbage()
assert(t[k] == nil)
end
assert(n == 5)
local function test (a)
assert(not pcall(table.insert, a, 2, 20));
table.insert(a, 10); table.insert(a, 2, 20);
table.insert(a, 1, -1); table.insert(a, 40);
table.insert(a, #a+1, 50)
table.insert(a, 2, -2)
assert(not pcall(table.insert, a, 0, 20));
assert(not pcall(table.insert, a, #a + 2, 20));
assert(table.remove(a,1) == -1)
assert(table.remove(a,1) == -2)
assert(table.remove(a,1) == 10)
assert(table.remove(a,1) == 20)
assert(table.remove(a,1) == 40)
assert(table.remove(a,1) == 50)
assert(table.remove(a,1) == nil)
assert(table.remove(a) == nil)
assert(table.remove(a, #a) == nil)
end
a = {n=0, [-7] = "ban"}
test(a)
assert(a.n == 0 and a[-7] == "ban")
a = {[-7] = "ban"};
test(a)
assert(a.n == nil and #a == 0 and a[-7] == "ban")
a = {[-1] = "ban"}
test(a)
assert(#a == 0 and table.remove(a) == nil and a[-1] == "ban")
a = {[0] = "ban"}
assert(#a == 0 and table.remove(a) == "ban" and a[0] == nil)
table.insert(a, 1, 10); table.insert(a, 1, 20); table.insert(a, 1, -1)
assert(table.remove(a) == 10)
assert(table.remove(a) == 20)
assert(table.remove(a) == -1)
assert(table.remove(a) == nil)
a = {'c', 'd'}
table.insert(a, 3, 'a')
table.insert(a, 'b')
assert(table.remove(a, 1) == 'c')
assert(table.remove(a, 1) == 'd')
assert(table.remove(a, 1) == 'a')
assert(table.remove(a, 1) == 'b')
assert(table.remove(a, 1) == nil)
assert(#a == 0 and a.n == nil)
a = {10,20,30,40}
assert(table.remove(a, #a + 1) == nil)
assert(not pcall(table.remove, a, 0))
assert(a[#a] == 40)
assert(table.remove(a, #a) == 40)
assert(a[#a] == 30)
assert(table.remove(a, 2) == 20)
assert(a[#a] == 30 and #a == 2)
do -- testing table library with metamethods
local function test (proxy, t)
for i = 1, 10 do
table.insert(proxy, 1, i)
end
assert(#proxy == 10 and #t == 10)
for i = 1, 10 do
assert(t[i] == 11 - i)
end
table.sort(proxy)
for i = 1, 10 do
assert(t[i] == i and proxy[i] == i)
end
assert(table.concat(proxy, ",") == "1,2,3,4,5,6,7,8,9,10")
for i = 1, 8 do
assert(table.remove(proxy, 1) == i)
end
assert(#proxy == 2 and #t == 2)
local a, b, c = table.unpack(proxy)
assert(a == 9 and b == 10 and c == nil)
end
-- all virtual
local t = {}
local proxy = setmetatable({}, {
__len = function () return #t end,
__index = t,
__newindex = t,
})
test(proxy, t)
-- only __newindex
local count = 0
t = setmetatable({}, {
__newindex = function (t,k,v) count = count + 1; rawset(t,k,v) end})
test(t, t)
assert(count == 10) -- after first 10, all other sets are not new
-- no __newindex
t = setmetatable({}, {
__index = function (_,k) return k + 1 end,
__len = function (_) return 5 end})
assert(table.concat(t, ";") == "2;3;4;5;6")
end
if not T then
(Message or print)
('\n >>> testC not active: skipping tests for table library on non-tables <<<\n')
else --[
local debug = require'debug'
local tab = {10, 20, 30}
local mt = {}
local u = T.newuserdata(0)
checkerror("table expected", table.insert, u, 40)
checkerror("table expected", table.remove, u)
debug.setmetatable(u, mt)
checkerror("table expected", table.insert, u, 40)
checkerror("table expected", table.remove, u)
mt.__index = tab
checkerror("table expected", table.insert, u, 40)
checkerror("table expected", table.remove, u)
mt.__newindex = tab
checkerror("table expected", table.insert, u, 40)
checkerror("table expected", table.remove, u)
mt.__len = function () return #tab end
table.insert(u, 40)
assert(#u == 4 and #tab == 4 and u[4] == 40 and tab[4] == 40)
assert(table.remove(u) == 40)
table.insert(u, 1, 50)
assert(#u == 4 and #tab == 4 and u[4] == 30 and tab[1] == 50)
mt.__newindex = nil
mt.__len = nil
local tab2 = {}
local u2 = T.newuserdata(0)
debug.setmetatable(u2, {__newindex = function (_, k, v) tab2[k] = v end})
table.move(u, 1, 4, 1, u2)
assert(#tab2 == 4 and tab2[1] == tab[1] and tab2[4] == tab[4])
end -- ]
print('+')
a = {}
for i=1,1000 do
a[i] = i; a[i-1] = nil
end
assert(next(a,nil) == 1000 and next(a,1000) == nil)
assert(next({}) == nil)
assert(next({}, nil) == nil)
for a,b in pairs{} do error"not here" end
for i=1,0 do error'not here' end
for i=0,1,-1 do error'not here' end
a = nil; for i=1,1 do assert(not a); a=1 end; assert(a)
a = nil; for i=1,1,-1 do assert(not a); a=1 end; assert(a)
do
print("testing floats in numeric for")
local a
-- integer count
a = 0; for i=1, 1, 1 do a=a+1 end; assert(a==1)
a = 0; for i=10000, 1e4, -1 do a=a+1 end; assert(a==1)
a = 0; for i=1, 0.99999, 1 do a=a+1 end; assert(a==0)
a = 0; for i=9999, 1e4, -1 do a=a+1 end; assert(a==0)
a = 0; for i=1, 0.99999, -1 do a=a+1 end; assert(a==1)
-- float count
a = 0; for i=0, 0.999999999, 0.1 do a=a+1 end; assert(a==10)
a = 0; for i=1.0, 1, 1 do a=a+1 end; assert(a==1)
a = 0; for i=-1.5, -1.5, 1 do a=a+1 end; assert(a==1)
a = 0; for i=1e6, 1e6, -1 do a=a+1 end; assert(a==1)
a = 0; for i=1.0, 0.99999, 1 do a=a+1 end; assert(a==0)
a = 0; for i=99999, 1e5, -1.0 do a=a+1 end; assert(a==0)
a = 0; for i=1.0, 0.99999, -1 do a=a+1 end; assert(a==1)
end
-- conversion
a = 0; for i="10","1","-2" do a=a+1 end; assert(a==5)
do -- checking types
local c
local function checkfloat (i)
assert(math.type(i) == "float")
c = c + 1
end
c = 0; for i = 1.0, 10 do checkfloat(i) end
assert(c == 10)
c = 0; for i = -1, -10, -1.0 do checkfloat(i) end
assert(c == 10)
local function checkint (i)
assert(math.type(i) == "integer")
c = c + 1
end
local m = math.maxinteger
c = 0; for i = m, m - 10, -1 do checkint(i) end
assert(c == 11)
c = 0; for i = 1, 10.9 do checkint(i) end
assert(c == 10)
c = 0; for i = 10, 0.001, -1 do checkint(i) end
assert(c == 10)
c = 0; for i = 1, "10.8" do checkint(i) end
assert(c == 10)
c = 0; for i = 9, "3.4", -1 do checkint(i) end
assert(c == 6)
c = 0; for i = 0, " -3.4 ", -1 do checkint(i) end
assert(c == 4)
c = 0; for i = 100, "96.3", -2 do checkint(i) end
assert(c == 2)
c = 0; for i = 1, math.huge do if i > 10 then break end; checkint(i) end
assert(c == 10)
c = 0; for i = -1, -math.huge, -1 do
if i < -10 then break end; checkint(i)
end
assert(c == 10)
for i = math.mininteger, -10e100 do assert(false) end
for i = math.maxinteger, 10e100, -1 do assert(false) end
end
collectgarbage()
-- testing generic 'for'
local function f (n, p)
local t = {}; for i=1,p do t[i] = i*10 end
return function (_,n)
if n > 0 then
n = n-1
return n, table.unpack(t)
end
end, nil, n
end
local x = 0
for n,a,b,c,d in f(5,3) do
x = x+1
assert(a == 10 and b == 20 and c == 30 and d == nil)
end
assert(x == 5)
-- testing __pairs and __ipairs metamethod
a = {}
do
local x,y,z = pairs(a)
assert(type(x) == 'function' and y == a and z == nil)
end
local function foo (e,i)
assert(e == a)
if i <= 10 then return i+1, i+2 end
end
local function foo1 (e,i)
i = i + 1
assert(e == a)
if i <= e.n then return i,a[i] end
end
setmetatable(a, {__pairs = function (x) return foo, x, 0 end})
local i = 0
for k,v in pairs(a) do
i = i + 1
assert(k == i and v == k+1)
end
a.n = 5
a[3] = 30
-- testing ipairs with metamethods
a = {n=10}
setmetatable(a, { __index = function (t,k)
if k <= t.n then return k * 10 end
end})
i = 0
for k,v in ipairs(a) do
i = i + 1
assert(k == i and v == i * 10)
end
assert(i == a.n)
print"OK"

374
app/lua53/host/tests/pm.lua Normal file
View File

@ -0,0 +1,374 @@
-- $Id: pm.lua,v 1.48 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
print('testing pattern matching')
local function checkerror (msg, f, ...)
local s, err = pcall(f, ...)
assert(not s and string.find(err, msg))
end
function f(s, p)
local i,e = string.find(s, p)
if i then return string.sub(s, i, e) end
end
a,b = string.find('', '') -- empty patterns are tricky
assert(a == 1 and b == 0);
a,b = string.find('alo', '')
assert(a == 1 and b == 0)
a,b = string.find('a\0o a\0o a\0o', 'a', 1) -- first position
assert(a == 1 and b == 1)
a,b = string.find('a\0o a\0o a\0o', 'a\0o', 2) -- starts in the midle
assert(a == 5 and b == 7)
a,b = string.find('a\0o a\0o a\0o', 'a\0o', 9) -- starts in the midle
assert(a == 9 and b == 11)
a,b = string.find('a\0a\0a\0a\0\0ab', '\0ab', 2); -- finds at the end
assert(a == 9 and b == 11);
a,b = string.find('a\0a\0a\0a\0\0ab', 'b') -- last position
assert(a == 11 and b == 11)
assert(string.find('a\0a\0a\0a\0\0ab', 'b\0') == nil) -- check ending
assert(string.find('', '\0') == nil)
assert(string.find('alo123alo', '12') == 4)
assert(string.find('alo123alo', '^12') == nil)
assert(string.match("aaab", ".*b") == "aaab")
assert(string.match("aaa", ".*a") == "aaa")
assert(string.match("b", ".*b") == "b")
assert(string.match("aaab", ".+b") == "aaab")
assert(string.match("aaa", ".+a") == "aaa")
assert(not string.match("b", ".+b"))
assert(string.match("aaab", ".?b") == "ab")
assert(string.match("aaa", ".?a") == "aa")
assert(string.match("b", ".?b") == "b")
assert(f('aloALO', '%l*') == 'alo')
assert(f('aLo_ALO', '%a*') == 'aLo')
assert(f(" \n\r*&\n\r xuxu \n\n", "%g%g%g+") == "xuxu")
assert(f('aaab', 'a*') == 'aaa');
assert(f('aaa', '^.*$') == 'aaa');
assert(f('aaa', 'b*') == '');
assert(f('aaa', 'ab*a') == 'aa')
assert(f('aba', 'ab*a') == 'aba')
assert(f('aaab', 'a+') == 'aaa')
assert(f('aaa', '^.+$') == 'aaa')
assert(f('aaa', 'b+') == nil)
assert(f('aaa', 'ab+a') == nil)
assert(f('aba', 'ab+a') == 'aba')
assert(f('a$a', '.$') == 'a')
assert(f('a$a', '.%$') == 'a$')
assert(f('a$a', '.$.') == 'a$a')
assert(f('a$a', '$$') == nil)
assert(f('a$b', 'a$') == nil)
assert(f('a$a', '$') == '')
assert(f('', 'b*') == '')
assert(f('aaa', 'bb*') == nil)
assert(f('aaab', 'a-') == '')
assert(f('aaa', '^.-$') == 'aaa')
assert(f('aabaaabaaabaaaba', 'b.*b') == 'baaabaaabaaab')
assert(f('aabaaabaaabaaaba', 'b.-b') == 'baaab')
assert(f('alo xo', '.o$') == 'xo')
assert(f(' \n isto <20> assim', '%S%S*') == 'isto')
assert(f(' \n isto <20> assim', '%S*$') == 'assim')
assert(f(' \n isto <20> assim', '[a-z]*$') == 'assim')
assert(f('um caracter ? extra', '[^%sa-z]') == '?')
assert(f('', 'a?') == '')
assert(f('<EFBFBD>', '<EFBFBD>?') == '<EFBFBD>')
assert(f('<EFBFBD>bl', '<EFBFBD>?b?l?') == '<EFBFBD>bl')
assert(f(' <20>bl', '<EFBFBD>?b?l?') == '')
assert(f('aa', '^aa?a?a') == 'aa')
assert(f(']]]<5D>b', '[^]]') == '<EFBFBD>')
assert(f("0alo alo", "%x*") == "0a")
assert(f("alo alo", "%C+") == "alo alo")
print('+')
function f1(s, p)
p = string.gsub(p, "%%([0-9])", function (s)
return "%" .. (tonumber(s)+1)
end)
p = string.gsub(p, "^(^?)", "%1()", 1)
p = string.gsub(p, "($?)$", "()%1", 1)
local t = {string.match(s, p)}
return string.sub(s, t[1], t[#t] - 1)
end
assert(f1('alo alx 123 b\0o b\0o', '(..*) %1') == "b\0o b\0o")
assert(f1('axz123= 4= 4 34', '(.+)=(.*)=%2 %1') == '3= 4= 4 3')
assert(f1('=======', '^(=*)=%1$') == '=======')
assert(string.match('==========', '^([=]*)=%1$') == nil)
local function range (i, j)
if i <= j then
return i, range(i+1, j)
end
end
local abc = string.char(range(0, 255));
assert(string.len(abc) == 256)
function strset (p)
local res = {s=''}
string.gsub(abc, p, function (c) res.s = res.s .. c end)
return res.s
end;
assert(string.len(strset('[\200-\210]')) == 11)
assert(strset('[a-z]') == "abcdefghijklmnopqrstuvwxyz")
assert(strset('[a-z%d]') == strset('[%da-uu-z]'))
assert(strset('[a-]') == "-a")
assert(strset('[^%W]') == strset('[%w]'))
assert(strset('[]%%]') == '%]')
assert(strset('[a%-z]') == '-az')
assert(strset('[%^%[%-a%]%-b]') == '-[]^ab')
assert(strset('%Z') == strset('[\1-\255]'))
assert(strset('.') == strset('[\1-\255%z]'))
print('+');
assert(string.match("alo xyzK", "(%w+)K") == "xyz")
assert(string.match("254 K", "(%d*)K") == "")
assert(string.match("alo ", "(%w*)$") == "")
assert(string.match("alo ", "(%w+)$") == nil)
assert(string.find("(<28>lo)", "%(<28>") == 1)
local a, b, c, d, e = string.match("<EFBFBD>lo alo", "^(((.).).* (%w*))$")
assert(a == '<EFBFBD>lo alo' and b == '<EFBFBD>l' and c == '<EFBFBD>' and d == 'alo' and e == nil)
a, b, c, d = string.match('0123456789', '(.+(.?)())')
assert(a == '0123456789' and b == '' and c == 11 and d == nil)
print('+')
assert(string.gsub('<EFBFBD>lo <20>lo', '<EFBFBD>', 'x') == 'xlo xlo')
assert(string.gsub('alo <20>lo ', ' +$', '') == 'alo <20>lo') -- trim
assert(string.gsub(' alo alo ', '^%s*(.-)%s*$', '%1') == 'alo alo') -- double trim
assert(string.gsub('alo alo \n 123\n ', '%s+', ' ') == 'alo alo 123 ')
t = "ab<EFBFBD> d"
a, b = string.gsub(t, '(.)', '%1@')
assert('@'..a == string.gsub(t, '', '@') and b == 5)
a, b = string.gsub('ab<EFBFBD>d', '(.)', '%0@', 2)
assert(a == 'a@b@<40>d' and b == 2)
assert(string.gsub('alo alo', '()[al]', '%1') == '12o 56o')
assert(string.gsub("abc=xyz", "(%w*)(%p)(%w+)", "%3%2%1-%0") ==
"xyz=abc-abc=xyz")
assert(string.gsub("abc", "%w", "%1%0") == "aabbcc")
assert(string.gsub("abc", "%w+", "%0%1") == "abcabc")
assert(string.gsub('<EFBFBD><EFBFBD><EFBFBD>', '$', '\0<EFBFBD><EFBFBD>') == '<EFBFBD><EFBFBD><EFBFBD>\0<EFBFBD><EFBFBD>')
assert(string.gsub('', '^', 'r') == 'r')
assert(string.gsub('', '$', 'r') == 'r')
print('+')
do -- new (5.3.3) semantics for empty matches
assert(string.gsub("a b cd", " *", "-") == "-a-b-c-d-")
local res = ""
local sub = "a \nbc\t\td"
local i = 1
for p, e in string.gmatch(sub, "()%s*()") do
res = res .. string.sub(sub, i, p - 1) .. "-"
i = e
end
assert(res == "-a-b-c-d-")
end
assert(string.gsub("um (dois) tres (quatro)", "(%(%w+%))", string.upper) ==
"um (DOIS) tres (QUATRO)")
do
local function setglobal (n,v) rawset(_G, n, v) end
string.gsub("a=roberto,roberto=a", "(%w+)=(%w%w*)", setglobal)
assert(_G.a=="roberto" and _G.roberto=="a")
end
function f(a,b) return string.gsub(a,'.',b) end
assert(string.gsub("trocar tudo em |teste|b| <20> |beleza|al|", "|([^|]*)|([^|]*)|", f) ==
"trocar tudo em bbbbb <20> alalalalalal")
local function dostring (s) return load(s, "")() or "" end
assert(string.gsub("alo $a='x'$ novamente $return a$",
"$([^$]*)%$",
dostring) == "alo novamente x")
x = string.gsub("$x=string.gsub('alo', '.', string.upper)$ assim vai para $return x$",
"$([^$]*)%$", dostring)
assert(x == ' assim vai para ALO')
t = {}
s = 'a alo jose joao'
r = string.gsub(s, '()(%w+)()', function (a,w,b)
assert(string.len(w) == b-a);
t[a] = b-a;
end)
assert(s == r and t[1] == 1 and t[3] == 3 and t[7] == 4 and t[13] == 4)
function isbalanced (s)
return string.find(string.gsub(s, "%b()", ""), "[()]") == nil
end
assert(isbalanced("(9 ((8))(\0) 7) \0\0 a b ()(c)() a"))
assert(not isbalanced("(9 ((8) 7) a b (\0 c) a"))
assert(string.gsub("alo 'oi' alo", "%b''", '"') == 'alo " alo')
local t = {"apple", "orange", "lime"; n=0}
assert(string.gsub("x and x and x", "x", function () t.n=t.n+1; return t[t.n] end)
== "apple and orange and lime")
t = {n=0}
string.gsub("first second word", "%w%w*", function (w) t.n=t.n+1; t[t.n] = w end)
assert(t[1] == "first" and t[2] == "second" and t[3] == "word" and t.n == 3)
t = {n=0}
assert(string.gsub("first second word", "%w+",
function (w) t.n=t.n+1; t[t.n] = w end, 2) == "first second word")
assert(t[1] == "first" and t[2] == "second" and t[3] == nil)
checkerror("invalid replacement value %(a table%)",
string.gsub, "alo", ".", {a = {}})
checkerror("invalid capture index %%2", string.gsub, "alo", ".", "%2")
checkerror("invalid capture index %%0", string.gsub, "alo", "(%0)", "a")
checkerror("invalid capture index %%1", string.gsub, "alo", "(%1)", "a")
checkerror("invalid use of '%%'", string.gsub, "alo", ".", "%x")
-- bug since 2.5 (C-stack overflow)
do
local function f (size)
local s = string.rep("a", size)
local p = string.rep(".?", size)
return pcall(string.match, s, p)
end
local r, m = f(80)
assert(r and #m == 80)
r, m = f(200000)
assert(not r and string.find(m, "too complex"))
end
if not _soft then
print("big strings")
local a = string.rep('a', 300000)
assert(string.find(a, '^a*.?$'))
assert(not string.find(a, '^a*.?b$'))
assert(string.find(a, '^a-.?$'))
-- bug in 5.1.2
a = string.rep('a', 10000) .. string.rep('b', 10000)
assert(not pcall(string.gsub, a, 'b'))
end
-- recursive nest of gsubs
function rev (s)
return string.gsub(s, "(.)(.+)", function (c,s1) return rev(s1)..c end)
end
local x = "abcdef"
assert(rev(rev(x)) == x)
-- gsub with tables
assert(string.gsub("alo alo", ".", {}) == "alo alo")
assert(string.gsub("alo alo", "(.)", {a="AA", l=""}) == "AAo AAo")
assert(string.gsub("alo alo", "(.).", {a="AA", l="K"}) == "AAo AAo")
assert(string.gsub("alo alo", "((.)(.?))", {al="AA", o=false}) == "AAo AAo")
assert(string.gsub("alo alo", "().", {'x','yy','zzz'}) == "xyyzzz alo")
t = {}; setmetatable(t, {__index = function (t,s) return string.upper(s) end})
assert(string.gsub("a alo b hi", "%w%w+", t) == "a ALO b HI")
-- tests for gmatch
local a = 0
for i in string.gmatch('abcde', '()') do assert(i == a+1); a=i end
assert(a==6)
t = {n=0}
for w in string.gmatch("first second word", "%w+") do
t.n=t.n+1; t[t.n] = w
end
assert(t[1] == "first" and t[2] == "second" and t[3] == "word")
t = {3, 6, 9}
for i in string.gmatch ("xuxx uu ppar r", "()(.)%2") do
assert(i == table.remove(t, 1))
end
assert(#t == 0)
t = {}
for i,j in string.gmatch("13 14 10 = 11, 15= 16, 22=23", "(%d+)%s*=%s*(%d+)") do
t[tonumber(i)] = tonumber(j)
end
a = 0
for k,v in pairs(t) do assert(k+1 == v+0); a=a+1 end
assert(a == 3)
-- tests for `%f' (`frontiers')
assert(string.gsub("aaa aa a aaa a", "%f[%w]a", "x") == "xaa xa x xaa x")
assert(string.gsub("[[]] [][] [[[[", "%f[[].", "x") == "x[]] x]x] x[[[")
assert(string.gsub("01abc45de3", "%f[%d]", ".") == ".01abc.45de.3")
assert(string.gsub("01abc45 de3x", "%f[%D]%w", ".") == "01.bc45 de3.")
assert(string.gsub("function", "%f[\1-\255]%w", ".") == ".unction")
assert(string.gsub("function", "%f[^\1-\255]", ".") == "function.")
assert(string.find("a", "%f[a]") == 1)
assert(string.find("a", "%f[^%z]") == 1)
assert(string.find("a", "%f[^%l]") == 2)
assert(string.find("aba", "%f[a%z]") == 3)
assert(string.find("aba", "%f[%z]") == 4)
assert(not string.find("aba", "%f[%l%z]"))
assert(not string.find("aba", "%f[^%l%z]"))
local i, e = string.find(" alo aalo allo", "%f[%S].-%f[%s].-%f[%S]")
assert(i == 2 and e == 5)
local k = string.match(" alo aalo allo", "%f[%S](.-%f[%s].-%f[%S])")
assert(k == 'alo ')
local a = {1, 5, 9, 14, 17,}
for k in string.gmatch("alo alo th02 is 1hat", "()%f[%w%d]") do
assert(table.remove(a, 1) == k)
end
assert(#a == 0)
-- malformed patterns
local function malform (p, m)
m = m or "malformed"
local r, msg = pcall(string.find, "a", p)
assert(not r and string.find(msg, m))
end
malform("(.", "unfinished capture")
malform(".)", "invalid pattern capture")
malform("[a")
malform("[]")
malform("[^]")
malform("[a%]")
malform("[a%")
malform("%b")
malform("%ba")
malform("%")
malform("%f", "missing")
-- \0 in patterns
assert(string.match("ab\0\1\2c", "[\0-\2]+") == "\0\1\2")
assert(string.match("ab\0\1\2c", "[\0-\0]+") == "\0")
assert(string.find("b$a", "$\0?") == 2)
assert(string.find("abc\0efg", "%\0") == 4)
assert(string.match("abc\0efg\0\1e\1g", "%b\0\1") == "\0efg\0\1e\1")
assert(string.match("abc\0\0\0", "%\0+") == "\0\0\0")
assert(string.match("abc\0\0\0", "%\0%\0?") == "\0\0")
-- magic char after \0
assert(string.find("abc\0\0","\0.") == 4)
assert(string.find("abcx\0\0abc\0abc","x\0\0abc\0a.") == 4)
print('OK')

View File

@ -0,0 +1,310 @@
-- $Id: sort.lua,v 1.38 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
print "testing (parts of) table library"
print "testing unpack"
local unpack = table.unpack
local maxI = math.maxinteger
local minI = math.mininteger
local function checkerror (msg, f, ...)
local s, err = pcall(f, ...)
assert(not s and string.find(err, msg))
end
checkerror("wrong number of arguments", table.insert, {}, 2, 3, 4)
local x,y,z,a,n
a = {}; lim = _soft and 200 or 2000
for i=1, lim do a[i]=i end
assert(select(lim, unpack(a)) == lim and select('#', unpack(a)) == lim)
x = unpack(a)
assert(x == 1)
x = {unpack(a)}
assert(#x == lim and x[1] == 1 and x[lim] == lim)
x = {unpack(a, lim-2)}
assert(#x == 3 and x[1] == lim-2 and x[3] == lim)
x = {unpack(a, 10, 6)}
assert(next(x) == nil) -- no elements
x = {unpack(a, 11, 10)}
assert(next(x) == nil) -- no elements
x,y = unpack(a, 10, 10)
assert(x == 10 and y == nil)
x,y,z = unpack(a, 10, 11)
assert(x == 10 and y == 11 and z == nil)
a,x = unpack{1}
assert(a==1 and x==nil)
a,x = unpack({1,2}, 1, 1)
assert(a==1 and x==nil)
do
local maxi = (1 << 31) - 1 -- maximum value for an int (usually)
local mini = -(1 << 31) -- minimum value for an int (usually)
checkerror("too many results", unpack, {}, 0, maxi)
checkerror("too many results", unpack, {}, 1, maxi)
checkerror("too many results", unpack, {}, 0, maxI)
checkerror("too many results", unpack, {}, 1, maxI)
checkerror("too many results", unpack, {}, mini, maxi)
checkerror("too many results", unpack, {}, -maxi, maxi)
checkerror("too many results", unpack, {}, minI, maxI)
unpack({}, maxi, 0)
unpack({}, maxi, 1)
unpack({}, maxI, minI)
pcall(unpack, {}, 1, maxi + 1)
local a, b = unpack({[maxi] = 20}, maxi, maxi)
assert(a == 20 and b == nil)
a, b = unpack({[maxi] = 20}, maxi - 1, maxi)
assert(a == nil and b == 20)
local t = {[maxI - 1] = 12, [maxI] = 23}
a, b = unpack(t, maxI - 1, maxI); assert(a == 12 and b == 23)
a, b = unpack(t, maxI, maxI); assert(a == 23 and b == nil)
a, b = unpack(t, maxI, maxI - 1); assert(a == nil and b == nil)
t = {[minI] = 12.3, [minI + 1] = 23.5}
a, b = unpack(t, minI, minI + 1); assert(a == 12.3 and b == 23.5)
a, b = unpack(t, minI, minI); assert(a == 12.3 and b == nil)
a, b = unpack(t, minI + 1, minI); assert(a == nil and b == nil)
end
do -- length is not an integer
local t = setmetatable({}, {__len = function () return 'abc' end})
assert(#t == 'abc')
checkerror("object length is not an integer", table.insert, t, 1)
end
print "testing pack"
a = table.pack()
assert(a[1] == nil and a.n == 0)
a = table.pack(table)
assert(a[1] == table and a.n == 1)
a = table.pack(nil, nil, nil, nil)
assert(a[1] == nil and a.n == 4)
-- testing move
do
checkerror("table expected", table.move, 1, 2, 3, 4)
local function eqT (a, b)
for k, v in pairs(a) do assert(b[k] == v) end
for k, v in pairs(b) do assert(a[k] == v) end
end
local a = table.move({10,20,30}, 1, 3, 2) -- move forward
eqT(a, {10,10,20,30})
-- move forward with overlap of 1
a = table.move({10, 20, 30}, 1, 3, 3)
eqT(a, {10, 20, 10, 20, 30})
-- moving to the same table (not being explicit about it)
a = {10, 20, 30, 40}
table.move(a, 1, 4, 2, a)
eqT(a, {10, 10, 20, 30, 40})
a = table.move({10,20,30}, 2, 3, 1) -- move backward
eqT(a, {20,30,30})
a = {} -- move to new table
assert(table.move({10,20,30}, 1, 3, 1, a) == a)
eqT(a, {10,20,30})
a = {}
assert(table.move({10,20,30}, 1, 0, 3, a) == a) -- empty move (no move)
eqT(a, {})
a = table.move({10,20,30}, 1, 10, 1) -- move to the same place
eqT(a, {10,20,30})
-- moving on the fringes
a = table.move({[maxI - 2] = 1, [maxI - 1] = 2, [maxI] = 3},
maxI - 2, maxI, -10, {})
eqT(a, {[-10] = 1, [-9] = 2, [-8] = 3})
a = table.move({[minI] = 1, [minI + 1] = 2, [minI + 2] = 3},
minI, minI + 2, -10, {})
eqT(a, {[-10] = 1, [-9] = 2, [-8] = 3})
a = table.move({45}, 1, 1, maxI)
eqT(a, {45, [maxI] = 45})
a = table.move({[maxI] = 100}, maxI, maxI, minI)
eqT(a, {[minI] = 100, [maxI] = 100})
a = table.move({[minI] = 100}, minI, minI, maxI)
eqT(a, {[minI] = 100, [maxI] = 100})
a = setmetatable({}, {
__index = function (_,k) return k * 10 end,
__newindex = error})
local b = table.move(a, 1, 10, 3, {})
eqT(a, {})
eqT(b, {nil,nil,10,20,30,40,50,60,70,80,90,100})
b = setmetatable({""}, {
__index = error,
__newindex = function (t,k,v)
t[1] = string.format("%s(%d,%d)", t[1], k, v)
end})
table.move(a, 10, 13, 3, b)
assert(b[1] == "(3,100)(4,110)(5,120)(6,130)")
local stat, msg = pcall(table.move, b, 10, 13, 3, b)
assert(not stat and msg == b)
end
do
-- for very long moves, just check initial accesses and interrupt
-- move with an error
local function checkmove (f, e, t, x, y)
local pos1, pos2
local a = setmetatable({}, {
__index = function (_,k) pos1 = k end,
__newindex = function (_,k) pos2 = k; error() end, })
local st, msg = pcall(table.move, a, f, e, t)
assert(not st and not msg and pos1 == x and pos2 == y)
end
checkmove(1, maxI, 0, 1, 0)
checkmove(0, maxI - 1, 1, maxI - 1, maxI)
checkmove(minI, -2, -5, -2, maxI - 6)
checkmove(minI + 1, -1, -2, -1, maxI - 3)
checkmove(minI, -2, 0, minI, 0) -- non overlapping
checkmove(minI + 1, -1, 1, minI + 1, 1) -- non overlapping
end
checkerror("too many", table.move, {}, 0, maxI, 1)
checkerror("too many", table.move, {}, -1, maxI - 1, 1)
checkerror("too many", table.move, {}, minI, -1, 1)
checkerror("too many", table.move, {}, minI, maxI, 1)
checkerror("wrap around", table.move, {}, 1, maxI, 2)
checkerror("wrap around", table.move, {}, 1, 2, maxI)
checkerror("wrap around", table.move, {}, minI, -2, 2)
print"testing sort"
-- strange lengths
local a = setmetatable({}, {__len = function () return -1 end})
assert(#a == -1)
table.sort(a, error) -- should not compare anything
a = setmetatable({}, {__len = function () return maxI end})
checkerror("too big", table.sort, a)
-- test checks for invalid order functions
local function check (t)
local function f(a, b) assert(a and b); return true end
checkerror("invalid order function", table.sort, t, f)
end
check{1,2,3,4}
check{1,2,3,4,5}
check{1,2,3,4,5,6}
function check (a, f)
f = f or function (x,y) return x<y end;
for n = #a, 2, -1 do
assert(not f(a[n], a[n-1]))
end
end
a = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep",
"Oct", "Nov", "Dec"}
table.sort(a)
check(a)
function perm (s, n)
n = n or #s
if n == 1 then
local t = {unpack(s)}
table.sort(t)
check(t)
else
for i = 1, n do
s[i], s[n] = s[n], s[i]
perm(s, n - 1)
s[i], s[n] = s[n], s[i]
end
end
end
perm{}
perm{1}
perm{1,2}
perm{1,2,3}
perm{1,2,3,4}
perm{2,2,3,4}
perm{1,2,3,4,5}
perm{1,2,3,3,5}
perm{1,2,3,4,5,6}
perm{2,2,3,3,5,6}
function timesort (a, n, func, msg, pre)
local x = os.clock()
table.sort(a, func)
x = (os.clock() - x) * 1000
pre = pre or ""
print(string.format("%ssorting %d %s elements in %.2f msec.", pre, n, msg, x))
check(a, func)
end
limit = 50000
if _soft then limit = 5000 end
a = {}
for i=1,limit do
a[i] = math.random()
end
timesort(a, limit, nil, "random")
timesort(a, limit, nil, "sorted", "re-")
a = {}
for i=1,limit do
a[i] = math.random()
end
x = os.clock(); i=0
table.sort(a, function(x,y) i=i+1; return y<x end)
x = (os.clock() - x) * 1000
print(string.format("Invert-sorting other %d elements in %.2f msec., with %i comparisons",
limit, x, i))
check(a, function(x,y) return y<x end)
table.sort{} -- empty array
for i=1,limit do a[i] = false end
timesort(a, limit, function(x,y) return nil end, "equal")
for i,v in pairs(a) do assert(v == false) end
A = {"<EFBFBD>lo", "\0first :-)", "alo", "then this one", "45", "and a new"}
table.sort(A)
check(A)
table.sort(A, function (x, y)
load(string.format("A[%q] = ''", x), "")()
collectgarbage()
return x<y
end)
tt = {__lt = function (a,b) return a.val < b.val end}
a = {}
for i=1,10 do a[i] = {val=math.random(100)}; setmetatable(a[i], tt); end
table.sort(a)
check(a, tt.__lt)
check(a)
print"OK"

View File

@ -0,0 +1,279 @@
-- $Id: api.lua,v 1.147 2016/11/07 13:06:25 roberto Exp $
-- See Copyright Notice in file all.lua
if T==nil then
(Message or print)('\n >>> testC not active: skipping API tests <<<\n')
return
end
local debug = require "debug"
local pack = table.pack
function tcheck (t1, t2)
assert(t1.n == (t2.n or #t2) + 1)
for i = 2, t1.n do assert(t1[i] == t2[i - 1]) end
end
local function checkerr (msg, f, ...)
local stat, err = pcall(f, ...)
assert(not stat and string.find(err, msg))
end
print('testing C API')
--[=[
do
local f = T.makeCfunc[[
getglobal error
pushstring bola
pcall 1 1 1 # call 'error' with given handler
pushstatus
return 2 # return error message and status
]]
local msg, st = f({}) -- invalid handler
assert(st == "ERRERR" and string.find(msg, "error handling"))
local msg, st = f(nil) -- invalid handler
assert(st == "ERRERR" and string.find(msg, "error handling"))
local a = setmetatable({}, {__call = function (_, x) return x:upper() end})
local msg, st = f(a) -- callable handler
assert(st == "ERRRUN" and msg == "BOLA")
end
do -- test returning more results than fit in the caller stack
local a = {}
for i=1,1000 do a[i] = true end; a[999] = 10
local b = T.testC([[pcall 1 -1 0; pop 1; tostring -1; return 1]],
table.unpack, a)
assert(b == "10")
end
-- testing globals
_G.a = 14; _G.b = "a31"
local a = {T.testC[[
getglobal a;
getglobal b;
getglobal b;
setglobal a;
return *
]]}
assert(a[2] == 14 and a[3] == "a31" and a[4] == nil and _G.a == "a31")
-- colect in cl the `val' of all collected userdata
tt = {}
cl = {n=0}
A = nil; B = nil
local F
F = function (x)
local udval = T.udataval(x)
table.insert(cl, udval)
local d = T.newuserdata(100) -- cria lixo
d = nil
assert(debug.getmetatable(x).__gc == F)
assert(load("table.insert({}, {})"))() -- cria mais lixo
collectgarbage() -- forca coleta de lixo durante coleta!
assert(debug.getmetatable(x).__gc == F) -- coleta anterior nao melou isso?
local dummy = {} -- cria lixo durante coleta
if A ~= nil then
assert(type(A) == "userdata")
assert(T.udataval(A) == B)
debug.getmetatable(A) -- just acess it
end
A = x -- ressucita userdata
B = udval
return 1,2,3
end
tt.__gc = F
-- test whether udate collection frees memory in the right time
do
collectgarbage();
collectgarbage();
local x = collectgarbage("count");
local a = T.newuserdata(5001)
assert(T.testC("objsize 2; return 1", a) == 5001)
assert(collectgarbage("count") >= x+4)
a = nil
collectgarbage();
assert(collectgarbage("count") <= x+1)
-- udata without finalizer
x = collectgarbage("count")
collectgarbage("stop")
for i=1,1000 do T.newuserdata(0) end
assert(collectgarbage("count") > x+10)
collectgarbage()
assert(collectgarbage("count") <= x+1)
-- udata with finalizer
collectgarbage()
x = collectgarbage("count")
collectgarbage("stop")
a = {__gc = function () end}
for i=1,1000 do debug.setmetatable(T.newuserdata(0), a) end
assert(collectgarbage("count") >= x+10)
collectgarbage() -- this collection only calls TM, without freeing memory
assert(collectgarbage("count") >= x+10)
collectgarbage() -- now frees memory
assert(collectgarbage("count") <= x+1)
collectgarbage("restart")
end
collectgarbage("stop")
-- create 3 userdatas with tag `tt'
a = T.newuserdata(0); debug.setmetatable(a, tt); na = T.udataval(a)
b = T.newuserdata(0); debug.setmetatable(b, tt); nb = T.udataval(b)
c = T.newuserdata(0); debug.setmetatable(c, tt); nc = T.udataval(c)
-- create userdata without meta table
x = T.newuserdata(4)
y = T.newuserdata(0)
-- checkerr("FILE%* expected, got userdata", io.input, a)
-- checkerr("FILE%* expected, got userdata", io.input, x)
assert(debug.getmetatable(x) == nil and debug.getmetatable(y) == nil)
d=T.ref(a);
e=T.ref(b);
f=T.ref(c);
t = {T.getref(d), T.getref(e), T.getref(f)}
assert(t[1] == a and t[2] == b and t[3] == c)
t=nil; a=nil; c=nil;
T.unref(e); T.unref(f)
collectgarbage()
]=]
-------------------------------------------------------------------------
-- testing memory limits
-------------------------------------------------------------------------
T.totalmem(T.totalmem()+45*2048) -- set low memory limit
local function fillstack(x,a,b,c,d,e,f,g,h,i)
local j,k,l,m,n,o,p,q,r = a,b,c,d,e,f,g,h,i
if x > 0 then fillstack(x-1,j,k,l,m,n,o,p,q,r) end
end
local calls = 1
local function gcfunc(ud) -- upval: calls
local j,k,l,m,n,o,p,q,r = 0,0,0,0,0,0,0,0,0
print (('GC for %s called with %u fillstacks'):format(tostring(ud),calls))
calls = calls + 10
fillstack(calls,j,k,l,m,n,o,p,q,r)
end
local a ={}
collectgarbage("restart")
collectgarbage("setpause",120)
collectgarbage("setstepmul",1000)
for i = 1, 20 do
local UD = T.newuserdata(2048); debug.setmetatable(UD, {__gc = gcfunc})
a[i] = UD
print (('a[%u] = %s'):format(i, tostring(UD)))
end
for i = 1, 20 do
j = (513 * i % 20) + 1
k = (13 + 257 * i % 20) + 1
calls = calls + 1
local UD = T.newuserdata(2048); debug.setmetatable(UD, {__gc = gcfunc})
print (('setting a[%u] = nil, a[%u] = %s'):format(j, k, tostring(UD)))
a[j]=nil; a[k] = UD
end
print ("Done")
--[=[
checkerr("block too big", T.newuserdata, math.maxinteger)
collectgarbage()
T.totalmem(T.totalmem()+5000) -- set low memory limit (+5k)
checkerr("not enough memory", load"local a={}; for i=1,100000 do a[i]=i end")
T.totalmem(0) -- restore high limit
-- test memory errors; increase memory limit in small steps, so that
-- we get memory errors in different parts of a given task, up to there
-- is enough memory to complete the task without errors
function testamem (s, f)
collectgarbage(); collectgarbage()
local M = T.totalmem()
local oldM = M
local a,b = nil
while 1 do
M = M+7 -- increase memory limit in small steps
T.totalmem(M)
a, b = pcall(f)
T.totalmem(0) -- restore high limit
if a and b then break end -- stop when no more errors
collectgarbage()
if not a and not -- `real' error?
(string.find(b, "memory") or string.find(b, "overflow")) then
error(b, 0) -- propagate it
end
end
print("\nlimit for " .. s .. ": " .. M-oldM)
return b
end
-- testing memory errors when creating a new state
b = testamem("state creation", T.newstate)
T.closestate(b); -- close new state
-- testing luaL_newmetatable
local mt_xuxu, res, top = T.testC("newmetatable xuxu; gettop; return 3")
assert(type(mt_xuxu) == "table" and res and top == 3)
local d, res, top = T.testC("newmetatable xuxu; gettop; return 3")
assert(mt_xuxu == d and not res and top == 3)
d, res, top = T.testC("newmetatable xuxu1; gettop; return 3")
assert(mt_xuxu ~= d and res and top == 3)
x = T.newuserdata(0);
y = T.newuserdata(0);
T.testC("pushstring xuxu; gettable R; setmetatable 2", x)
assert(getmetatable(x) == mt_xuxu)
-- testing luaL_testudata
-- correct metatable
local res1, res2, top = T.testC([[testudata -1 xuxu
testudata 2 xuxu
gettop
return 3]], x)
assert(res1 and res2 and top == 4)
-- wrong metatable
res1, res2, top = T.testC([[testudata -1 xuxu1
testudata 2 xuxu1
gettop
return 3]], x)
assert(not res1 and not res2 and top == 4)
-- non-existent type
res1, res2, top = T.testC([[testudata -1 xuxu2
testudata 2 xuxu2
gettop
return 3]], x)
assert(not res1 and not res2 and top == 4)
-- userdata has no metatable
res1, res2, top = T.testC([[testudata -1 xuxu
testudata 2 xuxu
gettop
return 3]], y)
assert(not res1 and not res2 and top == 4)
-- erase metatables
do
local r = debug.getregistry()
assert(r.xuxu == mt_xuxu and r.xuxu1 == d)
r.xuxu = nil; r.xuxu1 = nil
end
]=]
print'OK'

View File

@ -0,0 +1,380 @@
-- $Id: strings.lua,v 1.87 2016/12/21 19:23:02 roberto Exp $
-- See Copyright Notice in file all.lua
print('testing strings and string library')
local maxi, mini = math.maxinteger, math.mininteger
local function checkerror (msg, f, ...)
local s, err = pcall(f, ...)
assert(not s and string.find(err, msg))
end
-- testing string comparisons
assert('alo' < 'alo1')
assert('' < 'a')
assert('alo\0alo' < 'alo\0b')
assert('alo\0alo\0\0' > 'alo\0alo\0')
assert('alo' < 'alo\0')
assert('alo\0' > 'alo')
assert('\0' < '\1')
assert('\0\0' < '\0\1')
assert('\1\0a\0a' <= '\1\0a\0a')
assert(not ('\1\0a\0b' <= '\1\0a\0a'))
assert('\0\0\0' < '\0\0\0\0')
assert(not('\0\0\0\0' < '\0\0\0'))
assert('\0\0\0' <= '\0\0\0\0')
assert(not('\0\0\0\0' <= '\0\0\0'))
assert('\0\0\0' <= '\0\0\0')
assert('\0\0\0' >= '\0\0\0')
assert(not ('\0\0b' < '\0\0a\0'))
-- testing string.sub
assert(string.sub("123456789",2,4) == "234")
assert(string.sub("123456789",7) == "789")
assert(string.sub("123456789",7,6) == "")
assert(string.sub("123456789",7,7) == "7")
assert(string.sub("123456789",0,0) == "")
assert(string.sub("123456789",-10,10) == "123456789")
assert(string.sub("123456789",1,9) == "123456789")
assert(string.sub("123456789",-10,-20) == "")
assert(string.sub("123456789",-1) == "9")
assert(string.sub("123456789",-4) == "6789")
assert(string.sub("123456789",-6, -4) == "456")
assert(string.sub("123456789", mini, -4) == "123456")
assert(string.sub("123456789", mini, maxi) == "123456789")
assert(string.sub("123456789", mini, mini) == "")
assert(string.sub("\000123456789",3,5) == "234")
assert(("\000123456789"):sub(8) == "789")
-- testing string.find
assert(string.find("123456789", "345") == 3)
a,b = string.find("123456789", "345")
assert(string.sub("123456789", a, b) == "345")
assert(string.find("1234567890123456789", "345", 3) == 3)
assert(string.find("1234567890123456789", "345", 4) == 13)
assert(string.find("1234567890123456789", "346", 4) == nil)
assert(string.find("1234567890123456789", ".45", -9) == 13)
assert(string.find("abcdefg", "\0", 5, 1) == nil)
assert(string.find("", "") == 1)
assert(string.find("", "", 1) == 1)
assert(not string.find("", "", 2))
assert(string.find('', 'aaa', 1) == nil)
assert(('alo(.)alo'):find('(.)', 1, 1) == 4)
assert(string.len("") == 0)
assert(string.len("\0\0\0") == 3)
assert(string.len("1234567890") == 10)
assert(#"" == 0)
assert(#"\0\0\0" == 3)
assert(#"1234567890" == 10)
-- testing string.byte/string.char
assert(string.byte("a") == 97)
assert(string.byte("\xe4") > 127)
assert(string.byte(string.char(255)) == 255)
assert(string.byte(string.char(0)) == 0)
assert(string.byte("\0") == 0)
assert(string.byte("\0\0alo\0x", -1) == string.byte('x'))
assert(string.byte("ba", 2) == 97)
assert(string.byte("\n\n", 2, -1) == 10)
assert(string.byte("\n\n", 2, 2) == 10)
assert(string.byte("") == nil)
assert(string.byte("hi", -3) == nil)
assert(string.byte("hi", 3) == nil)
assert(string.byte("hi", 9, 10) == nil)
assert(string.byte("hi", 2, 1) == nil)
assert(string.char() == "")
assert(string.char(0, 255, 0) == "\0\255\0")
assert(string.char(0, string.byte("\xe4"), 0) == "\0\xe4\0")
assert(string.char(string.byte("\xe4l\0<EFBFBD>u", 1, -1)) == "\xe4l\0<EFBFBD>u")
assert(string.char(string.byte("\xe4l\0<EFBFBD>u", 1, 0)) == "")
assert(string.char(string.byte("\xe4l\0<EFBFBD>u", -10, 100)) == "\xe4l\0<EFBFBD>u")
assert(string.upper("ab\0c") == "AB\0C")
assert(string.lower("\0ABCc%$") == "\0abcc%$")
assert(string.rep('teste', 0) == '')
assert(string.rep('t<EFBFBD>s\00t<EFBFBD>', 2) == 't<EFBFBD>s\0t<EFBFBD>t<EFBFBD>s\000t<EFBFBD>')
assert(string.rep('', 10) == '')
if string.packsize("i") == 4 then
-- result length would be 2^31 (int overflow)
checkerror("too large", string.rep, 'aa', (1 << 30))
checkerror("too large", string.rep, 'a', (1 << 30), ',')
end
-- repetitions with separator
assert(string.rep('teste', 0, 'xuxu') == '')
assert(string.rep('teste', 1, 'xuxu') == 'teste')
assert(string.rep('\1\0\1', 2, '\0\0') == '\1\0\1\0\0\1\0\1')
assert(string.rep('', 10, '.') == string.rep('.', 9))
assert(not pcall(string.rep, "aa", maxi // 2 + 10))
assert(not pcall(string.rep, "", maxi // 2 + 10, "aa"))
assert(string.reverse"" == "")
assert(string.reverse"\0\1\2\3" == "\3\2\1\0")
assert(string.reverse"\0001234" == "4321\0")
for i=0,30 do assert(string.len(string.rep('a', i)) == i) end
assert(type(tostring(nil)) == 'string')
assert(type(tostring(12)) == 'string')
assert(string.find(tostring{}, 'table:'))
assert(string.find(tostring(print), 'function:'))
assert(#tostring('\0') == 1)
assert(tostring(true) == "true")
assert(tostring(false) == "false")
assert(tostring(-1203) == "-1203")
assert(tostring(1203.125) == "1203.125")
assert(tostring(-0.5) == "-0.5")
assert(tostring(-32767) == "-32767")
if math.tointeger(2147483647) then -- no overflow? (32 bits)
assert(tostring(-2147483647) == "-2147483647")
end
if math.tointeger(4611686018427387904) then -- no overflow? (64 bits)
assert(tostring(4611686018427387904) == "4611686018427387904")
assert(tostring(-4611686018427387904) == "-4611686018427387904")
end
if tostring(0.0) == "0.0" then -- "standard" coercion float->string
assert('' .. 12 == '12' and 12.0 .. '' == '12.0')
assert(tostring(-1203 + 0.0) == "-1203.0")
else -- compatible coercion
assert(tostring(0.0) == "0")
assert('' .. 12 == '12' and 12.0 .. '' == '12')
assert(tostring(-1203 + 0.0) == "-1203")
end
x = '"<22>lo"\n\\'
assert(string.format('%q%s', x, x) == '"\\"<22>lo\\"\\\n\\\\""<22>lo"\n\\')
assert(string.format('%q', "\0") == [["\0"]])
assert(load(string.format('return %q', x))() == x)
x = "\0\1\0023\5\0009"
assert(load(string.format('return %q', x))() == x)
assert(string.format("\0%c\0%c%x\0", string.byte("\xe4"), string.byte("b"), 140) ==
"\0\xe4\0b8c\0")
assert(string.format('') == "")
assert(string.format("%c",34)..string.format("%c",48)..string.format("%c",90)..string.format("%c",100) ==
string.format("%c%c%c%c", 34, 48, 90, 100))
assert(string.format("%s\0 is not \0%s", 'not be', 'be') == 'not be\0 is not \0be')
assert(string.format("%%%d %010d", 10, 23) == "%10 0000000023")
assert(tonumber(string.format("%f", 10.3)) == 10.3)
x = string.format('"%-50s"', 'a')
assert(#x == 52)
assert(string.sub(x, 1, 4) == '"a ')
assert(string.format("-%.20s.20s", string.rep("%", 2000)) ==
"-"..string.rep("%", 20)..".20s")
assert(string.format('"-%20s.20s"', string.rep("%", 2000)) ==
string.format("%q", "-"..string.rep("%", 2000)..".20s"))
do
local function checkQ (v)
local s = string.format("%q", v)
local nv = load("return " .. s)()
assert(v == nv and math.type(v) == math.type(nv))
end
checkQ("\0\0\1\255\u{234}")
checkQ(math.maxinteger)
checkQ(math.mininteger)
checkQ(math.pi)
checkQ(0.1)
checkQ(true)
checkQ(nil)
checkQ(false)
checkerror("no literal", string.format, "%q", {})
end
assert(string.format("\0%s\0", "\0\0\1") == "\0\0\0\1\0")
checkerror("contains zeros", string.format, "%10s", "\0")
-- format x tostring
assert(string.format("%s %s", nil, true) == "nil true")
assert(string.format("%s %.4s", false, true) == "false true")
assert(string.format("%.3s %.3s", false, true) == "fal tru")
local m = setmetatable({}, {__tostring = function () return "hello" end,
__name = "hi"})
assert(string.format("%s %.10s", m, m) == "hello hello")
getmetatable(m).__tostring = nil -- will use '__name' from now on
assert(string.format("%.4s", m) == "hi: ")
getmetatable(m).__tostring = function () return {} end
checkerror("'__tostring' must return a string", tostring, m)
assert(string.format("%x", 0.0) == "0")
assert(string.format("%02x", 0.0) == "00")
assert(string.format("%08X", 0xFFFFFFFF) == "FFFFFFFF")
assert(string.format("%+08d", 31501) == "+0031501")
assert(string.format("%+08d", -30927) == "-0030927")
--[[NodeMCU: this fails as a result of format size limitations
do -- longest number that can be formatted
local i = 1
local j = 10000
while i + 1 < j do -- binary search for maximum finite float
local m = (i + j) // 2
if 10^m < math.huge then i = m else j = m end
end
assert(10^i < math.huge and 10^j == math.huge)
local s = string.format('%.99f', -(10^i))
assert(string.len(s) >= i + 101)
assert(tonumber(s) == -(10^i)) --
end
]]
-- testing large numbers for format
do -- assume at least 32 bits
local max, min = 0x7fffffff, -0x80000000 -- "large" for 32 bits
assert(string.sub(string.format("%8x", -1), -8) == "ffffffff")
assert(string.format("%x", max) == "7fffffff")
assert(string.sub(string.format("%x", min), -8) == "80000000")
assert(string.format("%d", max) == "2147483647")
assert(string.format("%d", min) == "-2147483648")
assert(string.format("%u", 0xffffffff) == "4294967295")
assert(string.format("%o", 0xABCD) == "125715")
max, min = 0x7fffffffffffffff, -0x8000000000000000
if max > 2.0^53 then -- only for 64 bits
assert(string.format("%x", (2^52 | 0) - 1) == "fffffffffffff")
assert(string.format("0x%8X", 0x8f000003) == "0x8F000003")
assert(string.format("%d", 2^53) == "9007199254740992")
assert(string.format("%i", -2^53) == "-9007199254740992")
assert(string.format("%x", max) == "7fffffffffffffff")
assert(string.format("%x", min) == "8000000000000000")
assert(string.format("%d", max) == "9223372036854775807")
assert(string.format("%d", min) == "-9223372036854775808")
assert(string.format("%u", ~(-1 << 64)) == "18446744073709551615")
assert(tostring(1234567890123) == '1234567890123')
end
end
do print("testing 'format %a %A'")
local function matchhexa (n)
local s = string.format("%a", n)
-- result matches ISO C requirements
assert(string.find(s, "^%-?0x[1-9a-f]%.?[0-9a-f]*p[-+]?%d+$"))
assert(tonumber(s) == n) -- and has full precision
s = string.format("%A", n)
assert(string.find(s, "^%-?0X[1-9A-F]%.?[0-9A-F]*P[-+]?%d+$"))
assert(tonumber(s) == n)
end
for _, n in ipairs{0.1, -0.1, 1/3, -1/3, 1e30, -1e30,
-45/247, 1, -1, 2, -2, 3e-20, -3e-20} do
matchhexa(n)
end
assert(string.find(string.format("%A", 0.0), "^0X0%.?0?P%+?0$"))
assert(string.find(string.format("%a", -0.0), "^%-0x0%.?0?p%+?0$"))
if not _port then -- test inf, -inf, NaN, and -0.0
assert(string.find(string.format("%a", 1/0), "^inf"))
assert(string.find(string.format("%A", -1/0), "^%-INF"))
assert(string.find(string.format("%a", 0/0), "^%-?nan"))
assert(string.find(string.format("%a", -0.0), "^%-0x0"))
end
if not pcall(string.format, "%.3a", 0) then
(Message or print)("\n >>> modifiers for format '%a' not available <<<\n")
else
assert(string.find(string.format("%+.2A", 12), "^%+0X%x%.%x0P%+?%d$"))
assert(string.find(string.format("%.4A", -12), "^%-0X%x%.%x000P%+?%d$"))
end
end
-- errors in format
local function check (fmt, msg)
checkerror(msg, string.format, fmt, 10)
end
local aux = string.rep('0', 600)
check("%100.3d", "too long")
check("%1"..aux..".3d", "too long")
check("%1.100d", "too long")
check("%10.1"..aux.."004d", "too long")
check("%t", "invalid option")
check("%"..aux.."d", "repeated flags")
check("%d %d", "no value")
assert(load("return 1\n--comment without ending EOL")() == 1)
checkerror("table expected", table.concat, 3)
assert(table.concat{} == "")
assert(table.concat({}, 'x') == "")
assert(table.concat({'\0', '\0\1', '\0\1\2'}, '.\0.') == "\0.\0.\0\1.\0.\0\1\2")
local a = {}; for i=1,300 do a[i] = "xuxu" end
assert(table.concat(a, "123").."123" == string.rep("xuxu123", 300))
assert(table.concat(a, "b", 20, 20) == "xuxu")
assert(table.concat(a, "", 20, 21) == "xuxuxuxu")
assert(table.concat(a, "x", 22, 21) == "")
assert(table.concat(a, "3", 299) == "xuxu3xuxu")
assert(table.concat({}, "x", maxi, maxi - 1) == "")
assert(table.concat({}, "x", mini + 1, mini) == "")
assert(table.concat({}, "x", maxi, mini) == "")
assert(table.concat({[maxi] = "alo"}, "x", maxi, maxi) == "alo")
assert(table.concat({[maxi] = "alo", [maxi - 1] = "y"}, "-", maxi - 1, maxi)
== "y-alo")
assert(not pcall(table.concat, {"a", "b", {}}))
a = {"a","b","c"}
assert(table.concat(a, ",", 1, 0) == "")
assert(table.concat(a, ",", 1, 1) == "a")
assert(table.concat(a, ",", 1, 2) == "a,b")
assert(table.concat(a, ",", 2) == "b,c")
assert(table.concat(a, ",", 3) == "c")
assert(table.concat(a, ",", 4) == "")
_port = true -- NodeMCU: to support for locals
if not _port then
local locales = { "ptb", "pt_BR.iso88591", "ISO-8859-1" }
local function trylocale (w)
for i = 1, #locales do
if os.setlocale(locales[i], w) then
print(string.format("'%s' locale set to '%s'", w, locales[i]))
return locales[i]
end
end
print(string.format("'%s' locale not found", w))
return false
end
if trylocale("collate") then
assert("alo" < "<EFBFBD>lo" and "<EFBFBD>lo" < "amo")
end
if trylocale("ctype") then
assert(string.gsub("<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>", "%a", "x") == "xxxxx")
assert(string.gsub("<EFBFBD><EFBFBD><EFBFBD><EFBFBD>", "%l", "x") == "x<EFBFBD>x<EFBFBD>")
assert(string.gsub("<EFBFBD><EFBFBD><EFBFBD><EFBFBD>", "%u", "x") == "<EFBFBD>x<EFBFBD>x")
assert(string.upper"<EFBFBD><EFBFBD><EFBFBD>{xuxu}<7D><>o" == "<EFBFBD><EFBFBD><EFBFBD>{XUXU}<7D><>O")
end
os.setlocale("C")
assert(os.setlocale() == 'C')
assert(os.setlocale(nil, "numeric") == 'C')
end
-- bug in Lua 5.3.2
-- 'gmatch' iterator does not work across coroutines
do
local f = string.gmatch("1 2 3 4 5", "%d+")
assert(f() == "1")
co = coroutine.wrap(f)
assert(co() == "2")
end
print('OK')

View File

@ -0,0 +1,322 @@
-- $Id: tpack.lua,v 1.13 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
local pack = string.pack
local packsize = string.packsize
local unpack = string.unpack
print "testing pack/unpack"
-- maximum size for integers
local NB = 16
local sizeshort = packsize("h")
local sizeint = packsize("i")
local sizelong = packsize("l")
local sizesize_t = packsize("T")
local sizeLI = packsize("j")
local sizefloat = packsize("f")
local sizedouble = packsize("d")
local sizenumber = packsize("n")
local little = (pack("i2", 1) == "\1\0")
local align = packsize("!xXi16")
assert(1 <= sizeshort and sizeshort <= sizeint and sizeint <= sizelong and
sizefloat <= sizedouble)
print("platform:")
print(string.format(
"\tshort %d, int %d, long %d, size_t %d, float %d, double %d,\n\z
\tlua Integer %d, lua Number %d",
sizeshort, sizeint, sizelong, sizesize_t, sizefloat, sizedouble,
sizeLI, sizenumber))
print("\t" .. (little and "little" or "big") .. " endian")
print("\talignment: " .. align)
-- check errors in arguments
function checkerror (msg, f, ...)
local status, err = pcall(f, ...)
-- print(status, err, msg)
assert(not status and string.find(err, msg))
end
-- minimum behavior for integer formats
assert(unpack("B", pack("B", 0xff)) == 0xff)
assert(unpack("b", pack("b", 0x7f)) == 0x7f)
assert(unpack("b", pack("b", -0x80)) == -0x80)
assert(unpack("H", pack("H", 0xffff)) == 0xffff)
assert(unpack("h", pack("h", 0x7fff)) == 0x7fff)
assert(unpack("h", pack("h", -0x8000)) == -0x8000)
assert(unpack("L", pack("L", 0xffffffff)) == 0xffffffff)
assert(unpack("l", pack("l", 0x7fffffff)) == 0x7fffffff)
assert(unpack("l", pack("l", -0x80000000)) == -0x80000000)
for i = 1, NB do
-- small numbers with signal extension ("\xFF...")
local s = string.rep("\xff", i)
assert(pack("i" .. i, -1) == s)
assert(packsize("i" .. i) == #s)
assert(unpack("i" .. i, s) == -1)
-- small unsigned number ("\0...\xAA")
s = "\xAA" .. string.rep("\0", i - 1)
assert(pack("<I" .. i, 0xAA) == s)
assert(unpack("<I" .. i, s) == 0xAA)
assert(pack(">I" .. i, 0xAA) == s:reverse())
assert(unpack(">I" .. i, s:reverse()) == 0xAA)
end
do
local lnum = 0x13121110090807060504030201
local s = pack("<j", lnum)
assert(unpack("<j", s) == lnum)
assert(unpack("<i" .. sizeLI + 1, s .. "\0") == lnum)
assert(unpack("<i" .. sizeLI + 1, s .. "\0") == lnum)
for i = sizeLI + 1, NB do
local s = pack("<j", -lnum)
assert(unpack("<j", s) == -lnum)
-- strings with (correct) extra bytes
assert(unpack("<i" .. i, s .. ("\xFF"):rep(i - sizeLI)) == -lnum)
assert(unpack(">i" .. i, ("\xFF"):rep(i - sizeLI) .. s:reverse()) == -lnum)
assert(unpack("<I" .. i, s .. ("\0"):rep(i - sizeLI)) == -lnum)
-- overflows
checkerror("does not fit", unpack, "<I" .. i, ("\x00"):rep(i - 1) .. "\1")
checkerror("does not fit", unpack, ">i" .. i, "\1" .. ("\x00"):rep(i - 1))
end
end
for i = 1, sizeLI do
local lstr = "\1\2\3\4\5\6\7\8\9\10\11\12\13"
local lnum = 0x13121110090807060504030201
local n = lnum & (~(-1 << (i * 8)))
local s = string.sub(lstr, 1, i)
assert(pack("<i" .. i, n) == s)
assert(pack(">i" .. i, n) == s:reverse())
assert(unpack(">i" .. i, s:reverse()) == n)
end
-- sign extension
do
local u = 0xf0
for i = 1, sizeLI - 1 do
assert(unpack("<i"..i, "\xf0"..("\xff"):rep(i - 1)) == -16)
assert(unpack(">I"..i, "\xf0"..("\xff"):rep(i - 1)) == u)
u = u * 256 + 0xff
end
end
-- mixed endianness
do
assert(pack(">i2 <i2", 10, 20) == "\0\10\20\0")
local a, b = unpack("<i2 >i2", "\10\0\0\20")
assert(a == 10 and b == 20)
assert(pack("=i4", 2001) == pack("i4", 2001))
end
print("testing invalid formats")
checkerror("out of limits", pack, "i0", 0)
checkerror("out of limits", pack, "i" .. NB + 1, 0)
checkerror("out of limits", pack, "!" .. NB + 1, 0)
checkerror("%(17%) out of limits %[1,16%]", pack, "Xi" .. NB + 1)
checkerror("invalid format option 'r'", pack, "i3r", 0)
checkerror("16%-byte integer", unpack, "i16", string.rep('\3', 16))
checkerror("not power of 2", pack, "!4i3", 0);
checkerror("missing size", pack, "c", "")
checkerror("variable%-length format", packsize, "s")
checkerror("variable%-length format", packsize, "z")
-- overflow in option size (error will be in digit after limit)
checkerror("invalid format", packsize, "c1" .. string.rep("0", 40))
if packsize("i") == 4 then
-- result would be 2^31 (2^3 repetitions of 2^28 strings)
local s = string.rep("c268435456", 2^3)
checkerror("too large", packsize, s)
-- one less is OK
s = string.rep("c268435456", 2^3 - 1) .. "c268435455"
assert(packsize(s) == 0x7fffffff)
end
-- overflow in packing
for i = 1, sizeLI - 1 do
local umax = (1 << (i * 8)) - 1
local max = umax >> 1
local min = ~max
checkerror("overflow", pack, "<I" .. i, -1)
checkerror("overflow", pack, "<I" .. i, min)
checkerror("overflow", pack, ">I" .. i, umax + 1)
checkerror("overflow", pack, ">i" .. i, umax)
checkerror("overflow", pack, ">i" .. i, max + 1)
checkerror("overflow", pack, "<i" .. i, min - 1)
assert(unpack(">i" .. i, pack(">i" .. i, max)) == max)
assert(unpack("<i" .. i, pack("<i" .. i, min)) == min)
assert(unpack(">I" .. i, pack(">I" .. i, umax)) == umax)
end
-- Lua integer size
assert(unpack(">j", pack(">j", math.maxinteger)) == math.maxinteger)
assert(unpack("<j", pack("<j", math.mininteger)) == math.mininteger)
assert(unpack("<J", pack("<j", -1)) == -1) -- maximum unsigned integer
if little then
assert(pack("f", 24) == pack("<f", 24))
else
assert(pack("f", 24) == pack(">f", 24))
end
print "testing pack/unpack of floating-point numbers"
for _, n in ipairs{0, -1.1, 1.9, 1/0, -1/0, 1e20, -1e20, 0.1, 2000.7} do
assert(unpack("n", pack("n", n)) == n)
assert(unpack("<n", pack("<n", n)) == n)
assert(unpack(">n", pack(">n", n)) == n)
assert(pack("<f", n) == pack(">f", n):reverse())
assert(pack(">d", n) == pack("<d", n):reverse())
end
-- for non-native precisions, test only with "round" numbers
for _, n in ipairs{0, -1.5, 1/0, -1/0, 1e10, -1e9, 0.5, 2000.25} do
assert(unpack("<f", pack("<f", n)) == n)
assert(unpack(">f", pack(">f", n)) == n)
assert(unpack("<d", pack("<d", n)) == n)
assert(unpack(">d", pack(">d", n)) == n)
end
print "testing pack/unpack of strings"
do
local s = string.rep("abc", 1000)
assert(pack("zB", s, 247) == s .. "\0\xF7")
local s1, b = unpack("zB", s .. "\0\xF9")
assert(b == 249 and s1 == s)
s1 = pack("s", s)
assert(unpack("s", s1) == s)
checkerror("does not fit", pack, "s1", s)
checkerror("contains zeros", pack, "z", "alo\0");
for i = 2, NB do
local s1 = pack("s" .. i, s)
assert(unpack("s" .. i, s1) == s and #s1 == #s + i)
end
end
do
local x = pack("s", "alo")
checkerror("too short", unpack, "s", x:sub(1, -2))
checkerror("too short", unpack, "c5", "abcd")
checkerror("out of limits", pack, "s100", "alo")
end
do
assert(pack("c0", "") == "")
assert(packsize("c0") == 0)
assert(unpack("c0", "") == "")
assert(pack("<! c3", "abc") == "abc")
assert(packsize("<! c3") == 3)
assert(pack(">!4 c6", "abcdef") == "abcdef")
assert(pack("c3", "123") == "123")
assert(pack("c0", "") == "")
assert(pack("c8", "123456") == "123456\0\0")
assert(pack("c88", "") == string.rep("\0", 88))
assert(pack("c188", "ab") == "ab" .. string.rep("\0", 188 - 2))
local a, b, c = unpack("!4 z c3", "abcdefghi\0xyz")
assert(a == "abcdefghi" and b == "xyz" and c == 14)
checkerror("longer than", pack, "c3", "1234")
end
-- testing multiple types and sequence
do
local x = pack("<b h b f d f n i", 1, 2, 3, 4, 5, 6, 7, 8)
assert(#x == packsize("<b h b f d f n i"))
local a, b, c, d, e, f, g, h = unpack("<b h b f d f n i", x)
assert(a == 1 and b == 2 and c == 3 and d == 4 and e == 5 and f == 6 and
g == 7 and h == 8)
end
print "testing alignment"
do
assert(pack(" < i1 i2 ", 2, 3) == "\2\3\0") -- no alignment by default
local x = pack(">!8 b Xh i4 i8 c1 Xi8", -12, 100, 200, "\xEC")
assert(#x == packsize(">!8 b Xh i4 i8 c1 Xi8"))
assert(x == "\xf4" .. "\0\0\0" ..
"\0\0\0\100" ..
"\0\0\0\0\0\0\0\xC8" ..
"\xEC" .. "\0\0\0\0\0\0\0")
local a, b, c, d, pos = unpack(">!8 c1 Xh i4 i8 b Xi8 XI XH", x)
assert(a == "\xF4" and b == 100 and c == 200 and d == -20 and (pos - 1) == #x)
x = pack(">!4 c3 c4 c2 z i4 c5 c2 Xi4",
"abc", "abcd", "xz", "hello", 5, "world", "xy")
assert(x == "abcabcdxzhello\0\0\0\0\0\5worldxy\0")
local a, b, c, d, e, f, g, pos = unpack(">!4 c3 c4 c2 z i4 c5 c2 Xh Xi4", x)
assert(a == "abc" and b == "abcd" and c == "xz" and d == "hello" and
e == 5 and f == "world" and g == "xy" and (pos - 1) % 4 == 0)
x = pack(" b b Xd b Xb x", 1, 2, 3)
assert(packsize(" b b Xd b Xb x") == 4)
assert(x == "\1\2\3\0")
a, b, c, pos = unpack("bbXdb", x)
assert(a == 1 and b == 2 and c == 3 and pos == #x)
-- only alignment
assert(packsize("!8 xXi8") == 8)
local pos = unpack("!8 xXi8", "0123456701234567"); assert(pos == 9)
assert(packsize("!8 xXi2") == 2)
local pos = unpack("!8 xXi2", "0123456701234567"); assert(pos == 3)
assert(packsize("!2 xXi2") == 2)
local pos = unpack("!2 xXi2", "0123456701234567"); assert(pos == 3)
assert(packsize("!2 xXi8") == 2)
local pos = unpack("!2 xXi8", "0123456701234567"); assert(pos == 3)
assert(packsize("!16 xXi16") == 16)
local pos = unpack("!16 xXi16", "0123456701234567"); assert(pos == 17)
checkerror("invalid next option", pack, "X")
checkerror("invalid next option", unpack, "XXi", "")
checkerror("invalid next option", unpack, "X i", "")
checkerror("invalid next option", pack, "Xc1")
end
do -- testing initial position
local x = pack("i4i4i4i4", 1, 2, 3, 4)
for pos = 1, 16, 4 do
local i, p = unpack("i4", x, pos)
assert(i == pos//4 + 1 and p == pos + 4)
end
-- with alignment
for pos = 0, 12 do -- will always round position to power of 2
local i, p = unpack("!4 i4", x, pos + 1)
assert(i == (pos + 3)//4 + 1 and p == i*4 + 1)
end
-- negative indices
local i, p = unpack("!4 i4", x, -4)
assert(i == 4 and p == 17)
local i, p = unpack("!4 i4", x, -7)
assert(i == 4 and p == 17)
local i, p = unpack("!4 i4", x, -#x)
assert(i == 1 and p == 5)
-- limits
for i = 1, #x + 1 do
assert(unpack("c0", x, i) == "")
end
checkerror("out of string", unpack, "c0", x, 0)
checkerror("out of string", unpack, "c0", x, #x + 2)
checkerror("out of string", unpack, "c0", x, -(#x + 1))
end
print "OK"

View File

@ -0,0 +1,210 @@
-- $Id: utf8.lua,v 1.12 2016/11/07 13:11:28 roberto Exp $
-- See Copyright Notice in file all.lua
print "testing UTF-8 library"
local utf8 = require'utf8'
local function checkerror (msg, f, ...)
local s, err = pcall(f, ...)
assert(not s and string.find(err, msg))
end
local function len (s)
return #string.gsub(s, "[\x80-\xBF]", "")
end
local justone = "^" .. utf8.charpattern .. "$"
-- 't' is the list of codepoints of 's'
local function checksyntax (s, t)
local ts = {"return '"}
for i = 1, #t do ts[i + 1] = string.format("\\u{%x}", t[i]) end
ts[#t + 2] = "'"
ts = table.concat(ts)
assert(assert(load(ts))() == s)
end
assert(utf8.offset("alo", 5) == nil)
assert(utf8.offset("alo", -4) == nil)
-- 't' is the list of codepoints of 's'
local function check (s, t)
local l = utf8.len(s)
assert(#t == l and len(s) == l)
assert(utf8.char(table.unpack(t)) == s)
assert(utf8.offset(s, 0) == 1)
checksyntax(s, t)
local t1 = {utf8.codepoint(s, 1, -1)}
assert(#t == #t1)
for i = 1, #t do assert(t[i] == t1[i]) end
for i = 1, l do
local pi = utf8.offset(s, i) -- position of i-th char
local pi1 = utf8.offset(s, 2, pi) -- position of next char
assert(string.find(string.sub(s, pi, pi1 - 1), justone))
assert(utf8.offset(s, -1, pi1) == pi)
assert(utf8.offset(s, i - l - 1) == pi)
assert(pi1 - pi == #utf8.char(utf8.codepoint(s, pi)))
for j = pi, pi1 - 1 do
assert(utf8.offset(s, 0, j) == pi)
end
for j = pi + 1, pi1 - 1 do
assert(not utf8.len(s, j))
end
assert(utf8.len(s, pi, pi) == 1)
assert(utf8.len(s, pi, pi1 - 1) == 1)
assert(utf8.len(s, pi) == l - i + 1)
assert(utf8.len(s, pi1) == l - i)
assert(utf8.len(s, 1, pi) == i)
end
local i = 0
for p, c in utf8.codes(s) do
i = i + 1
assert(c == t[i] and p == utf8.offset(s, i))
assert(utf8.codepoint(s, p) == c)
end
assert(i == #t)
i = 0
for p, c in utf8.codes(s) do
i = i + 1
assert(c == t[i] and p == utf8.offset(s, i))
end
assert(i == #t)
i = 0
for c in string.gmatch(s, utf8.charpattern) do
i = i + 1
assert(c == utf8.char(t[i]))
end
assert(i == #t)
for i = 1, l do
assert(utf8.offset(s, i) == utf8.offset(s, i - l - 1, #s + 1))
end
end
do -- error indication in utf8.len
local function check (s, p)
local a, b = utf8.len(s)
assert(not a and b == p)
end
check("abc\xE3def", 4)
check("汉字\x80", #("汉字") + 1)
check("\xF4\x9F\xBF", 1)
check("\xF4\x9F\xBF\xBF", 1)
end
-- error in utf8.codes
checkerror("invalid UTF%-8 code",
function ()
local s = "ab\xff"
for c in utf8.codes(s) do assert(c) end
end)
-- error in initial position for offset
checkerror("position out of range", utf8.offset, "abc", 1, 5)
checkerror("position out of range", utf8.offset, "abc", 1, -4)
checkerror("position out of range", utf8.offset, "", 1, 2)
checkerror("position out of range", utf8.offset, "", 1, -1)
checkerror("continuation byte", utf8.offset, "𦧺", 1, 2)
checkerror("continuation byte", utf8.offset, "𦧺", 1, 2)
checkerror("continuation byte", utf8.offset, "\x80", 1)
local s = "hello World"
local t = {string.byte(s, 1, -1)}
for i = 1, utf8.len(s) do assert(t[i] == string.byte(s, i)) end
check(s, t)
check("汉字/漢字", {27721, 23383, 47, 28450, 23383,})
do
local s = "áéí\128"
local t = {utf8.codepoint(s,1,#s - 1)}
assert(#t == 3 and t[1] == 225 and t[2] == 233 and t[3] == 237)
checkerror("invalid UTF%-8 code", utf8.codepoint, s, 1, #s)
checkerror("out of range", utf8.codepoint, s, #s + 1)
t = {utf8.codepoint(s, 4, 3)}
assert(#t == 0)
checkerror("out of range", utf8.codepoint, s, -(#s + 1), 1)
checkerror("out of range", utf8.codepoint, s, 1, #s + 1)
end
assert(utf8.char() == "")
assert(utf8.char(97, 98, 99) == "abc")
assert(utf8.codepoint(utf8.char(0x10FFFF)) == 0x10FFFF)
checkerror("value out of range", utf8.char, 0x10FFFF + 1)
local function invalid (s)
checkerror("invalid UTF%-8 code", utf8.codepoint, s)
assert(not utf8.len(s))
end
-- UTF-8 representation for 0x11ffff (value out of valid range)
invalid("\xF4\x9F\xBF\xBF")
-- overlong sequences
invalid("\xC0\x80") -- zero
invalid("\xC1\xBF") -- 0x7F (should be coded in 1 byte)
invalid("\xE0\x9F\xBF") -- 0x7FF (should be coded in 2 bytes)
invalid("\xF0\x8F\xBF\xBF") -- 0xFFFF (should be coded in 3 bytes)
-- invalid bytes
invalid("\x80") -- continuation byte
invalid("\xBF") -- continuation byte
invalid("\xFE") -- invalid byte
invalid("\xFF") -- invalid byte
-- empty string
check("", {})
-- minimum and maximum values for each sequence size
s = "\0 \x7F\z
\xC2\x80 \xDF\xBF\z
\xE0\xA0\x80 \xEF\xBF\xBF\z
\xF0\x90\x80\x80 \xF4\x8F\xBF\xBF"
s = string.gsub(s, " ", "")
check(s, {0,0x7F, 0x80,0x7FF, 0x800,0xFFFF, 0x10000,0x10FFFF})
x = "日本語a-4\0éó"
check(x, {26085, 26412, 35486, 97, 45, 52, 0, 233, 243})
-- Supplementary Characters
check("𣲷𠜎𠱓𡁻𠵼ab𠺢",
{0x23CB7, 0x2070E, 0x20C53, 0x2107B, 0x20D7C, 0x61, 0x62, 0x20EA2,})
check("𨳊𩶘𦧺𨳒𥄫𤓓\xF4\x8F\xBF\xBF",
{0x28CCA, 0x29D98, 0x269FA, 0x28CD2, 0x2512B, 0x244D3, 0x10ffff})
local i = 0
for p, c in string.gmatch(x, "()(" .. utf8.charpattern .. ")") do
i = i + 1
assert(utf8.offset(x, i) == p)
assert(utf8.len(x, p) == utf8.len(x) - i + 1)
assert(utf8.len(c) == 1)
for j = 1, #c - 1 do
assert(utf8.offset(x, 0, p + j - 1) == p)
end
end
print'ok'

Some files were not shown because too many files have changed in this diff Show More