Merge branch 'master' into ravi-distro

ravi-distro
Dibyendu Majumdar 4 years ago
commit a6caf1b402

@ -34,7 +34,7 @@ Features
* Type specific bytecodes to improve performance
* Compatibility with Lua 5.3 (see Compatibility section below)
* Generational GC from Lua 5.4
* ``defer`` statement added
* ``defer`` statement for releasing resources
* Compact JIT backend `MIR <https://github.com/vnmakarov/mir>`_; only Linux and x86-64 supported for now.
* `LLVM <http://www.llvm.org/>`_ supported as alternative JIT backend.
* A `distribution with batteries <https://github.com/dibyendumajumdar/Suravi>`_.

@ -32,6 +32,17 @@
#define upisopen(up) ((up)->v != &(up)->u.value)
#ifdef RAVI_DEFER_STATEMENT
/*
** Special "status" for 'luaF_close'
*/
/* close upvalues without running their closing methods */
#define NOCLOSINGMETH (-1)
/* close upvalues running all closing methods in protected mode */
#define CLOSEPROTECT (-2)
#endif
LUAI_FUNC Proto *luaF_newproto (lua_State *L);
LUAI_FUNC CClosure *luaF_newCclosure (lua_State *L, int nelems);

@ -226,7 +226,9 @@ extern const char lua_ident[];
LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud);
LUA_API void (lua_close) (lua_State *L);
LUA_API lua_State *(lua_newthread) (lua_State *L);
#ifdef RAVI_DEFER_STATEMENT
LUA_API int (lua_resetthread) (lua_State *L);
#endif
LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf);

@ -75,8 +75,13 @@ static int luaB_auxwrap (lua_State *L) {
lua_State *co = lua_tothread(L, lua_upvalueindex(1));
int r = auxresume(L, co, lua_gettop(L));
if (r < 0) {
#ifdef RAVI_DEFER_STATEMENT
int stat = lua_status(co);
if (stat != LUA_OK && stat != LUA_YIELD)
lua_resetthread(co); /* close variables in case of errors */
#endif
if (lua_type(L, -1) == LUA_TSTRING) { /* error object is a string? */
luaL_where(L, 1); /* add extra info */
luaL_where(L, 1); /* add extra info, if available */
lua_insert(L, -2);
lua_concat(L, 2);
}

@ -104,6 +104,12 @@ void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) {
setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling"));
break;
}
#ifdef RAVI_DEFER_STATEMENT
case CLOSEPROTECT: {
setnilvalue(oldtop); /* no error message */
break;
}
#endif
default: {
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */
break;
@ -120,6 +126,9 @@ l_noret luaD_throw (lua_State *L, int errcode) {
}
else { /* thread has no error handler */
global_State *g = G(L);
#ifdef RAVI_DEFER_STATEMENT
errcode = luaF_close(L, L->stack, errcode); /* close all upvalues */
#endif
L->status = cast_byte(errcode); /* mark it as dead */
if (g->mainthread->errorJmp) { /* main thread has a handler? */
setobjs2s(L, g->mainthread->top++, L->top - 1); /* copy error obj. */
@ -763,19 +772,17 @@ LUA_API int lua_resume (lua_State *L, lua_State *from, int nargs) {
L->nny = 0; /* allow yields */
api_checknelems(L, (L->status == LUA_OK) ? nargs + 1 : nargs);
status = luaD_rawrunprotected(L, resume, &nargs);
if (status == -1) /* error calling 'lua_resume'? */
status = LUA_ERRRUN;
else { /* continue running after recoverable errors */
while (errorstatus(status) && recover(L, status)) {
/* unroll continuation */
status = luaD_rawrunprotected(L, unroll, &status);
}
if (errorstatus(status)) { /* unrecoverable error? */
L->status = cast_byte(status); /* mark thread as 'dead' */
luaD_seterrorobj(L, status, L->top); /* push error message */
L->ci->top = L->top;
}
else lua_assert(status == L->status); /* normal end or yield */
/* continue running after recoverable errors */
while (errorstatus(status) && recover(L, status)) {
/* unroll continuation */
status = luaD_rawrunprotected(L, unroll, &status);
}
if (likely(!errorstatus(status)))
lua_assert(status == L->status); /* normal end or yield */
else { /* unrecoverable error */
L->status = cast_byte(status); /* mark thread as 'dead' */
luaD_seterrorobj(L, status, L->top); /* push error message */
L->ci->top = L->top;
}
L->nny = oldnny; /* restore 'nny' */
L->nCcalls--;

@ -123,7 +123,7 @@ static int preparetocall(lua_State *L, TValue *func, TValue *err) {
** inside the original protected call, and so any error will be handled
** there. Otherwise, a previous error already activated the original
** protected call, and so the call to the deferred method must be
** protected here. (A status == -1 behaves like a previous
** protected here. (A status == CLOSEPROTECT behaves like a previous
** error, to also run the closing method in protected mode).
** If status is OK, the call to the deferred method will be pushed
** at the top of the stack. Otherwise, values are pushed after
@ -143,7 +143,7 @@ static int calldeferredfunction(lua_State *L, StkId level, int status) {
luaD_seterrorobj(L, status, level); /* set error message */
preparetocall(L, uv, level);
int newstatus = luaD_pcall(L, calldeferred, NULL, oldtop, 0);
if (newstatus != LUA_OK && status == -1) /* first error? */
if (newstatus != LUA_OK && status == CLOSEPROTECT) /* first error? */
status = newstatus; /* this will be the new error */
else {
/* leave original error (or nil) on top */
@ -161,7 +161,7 @@ int luaF_close (lua_State *L, StkId level, int status) {
if (uv->refcount == 0) { /* no references? */
UpVal uv1 = *uv; /* copy the upvalue as we will free it below */
luaM_free(L, uv); /* free upvalue before invoking any deferred functions */
if (uv1.flags && ttisfunction(uv1.v)) {
if (status != NOCLOSINGMETH && uv1.flags && ttisfunction(uv1.v)) {
ptrdiff_t levelrel = savestack(L, level);
status = calldeferredfunction(L, uv1.v, status);
level = restorestack(L, levelrel);

@ -670,6 +670,7 @@ static int traverseLclosure (global_State *g, LClosure *cl) {
** That ensures that the entire stack have valid (non-dead) objects.
*/
static lu_mem traversethread (global_State *g, lua_State *th) {
UpVal *uv;
StkId o = th->stack;
if (o == NULL)
return 1; /* stack not completely built yet */
@ -677,6 +678,8 @@ static lu_mem traversethread (global_State *g, lua_State *th) {
th->openupval == NULL || isintwups(th));
for (; o < th->top; o++) /* mark live elements in the stack */
markvalue(g, o);
for (uv = th->openupval; uv != NULL; uv = uv->u.open.next)
markvalue(g, uv->v); /* open upvalues cannot be collected */
if (g->gcstate == GCSatomic) { /* final traversal? */
StkId lim = th->stack + th->stacksize; /* real end of stack */
for (; o < lim; o++) /* clear not-marked stack slice */

@ -60,6 +60,7 @@ typedef struct BlockCnt {
lu_byte nactvar; /* # active locals outside the block */
lu_byte upval; /* true if some variable in the block is an upvalue */
lu_byte isloop; /* true if 'block' is a loop */
lu_byte insidetbc; /* true if inside the scope of a defer stmt (i.e. defer closure var) */
} BlockCnt;
/* RAVI set debug level */
@ -843,6 +844,7 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
bl->firstlabel = fs->ls->dyd->label.n;
bl->firstgoto = fs->ls->dyd->gt.n;
bl->upval = 0;
bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc);
bl->previous = fs->bl;
fs->bl = bl;
lua_assert(fs->freereg == fs->nactvar);
@ -2231,6 +2233,7 @@ static void localfunc (LexState *ls, int defer) {
static const char funcname[] = "(deferred function)";
new_localvar(ls, luaX_newstring(ls, funcname, sizeof funcname-1), RAVI_TFUNCTION, NULL); /* new local variable */
markupval(fs, fs->nactvar);
fs->bl->insidetbc = 1; /* in the scope of a defer closure variable */
} else {
/* RAVI change - add type */
new_localvar(ls, str_checkname(ls), RAVI_TFUNCTION, NULL); /* new local variable */
@ -2349,7 +2352,7 @@ static void retstat (LexState *ls) {
nret = explist(ls, &e); /* optional return values */
if (hasmultret(e.k)) {
luaK_setmultret(fs, &e);
if (e.k == VCALL && nret == 1) { /* tail call? */
if (e.k == VCALL && nret == 1 && !fs->bl->insidetbc) { /* tail call? */
SET_OPCODE(getinstruction(fs,&e), OP_TAILCALL);
lua_assert(GETARG_A(getinstruction(fs,&e)) == fs->nactvar);
}

@ -259,7 +259,7 @@ void *ravi_alloc_f(void *msp, void *ptr, size_t osize, size_t nsize)
static void close_state (lua_State *L) {
global_State *g = G(L);
#ifdef RAVI_DEFER_STATEMENT
luaF_close(L, L->stack, -1); /* close all upvalues for this thread */
luaF_close(L, L->stack, CLOSEPROTECT); /* close all upvalues for this thread */
#else
luaF_close(L, L->stack); /* close all upvalues for this thread */
#endif
@ -310,7 +310,7 @@ LUA_API lua_State *lua_newthread (lua_State *L) {
void luaE_freethread (lua_State *L, lua_State *L1) {
LX *l = fromstate(L1);
#ifdef RAVI_DEFER_STATEMENT
luaF_close(L1, L1->stack, -1); /* close all upvalues for this thread */
luaF_close(L1, L1->stack, NOCLOSINGMETH); /* close all upvalues for this thread */
#else
luaF_close(L1, L1->stack); /* close all upvalues for this thread */
#endif
@ -320,6 +320,29 @@ void luaE_freethread (lua_State *L, lua_State *L1) {
luaM_free(L, l);
}
#ifdef RAVI_DEFER_STATEMENT
int lua_resetthread (lua_State *L) {
CallInfo *ci;
int status;
lua_lock(L);
L->ci = ci = &L->base_ci; /* unwind CallInfo list */
setnilvalue(L->stack); /* 'function' entry for basic 'ci' */
ci->func = L->stack;
ci->callstatus = 0;
status = luaF_close(L, L->stack, CLOSEPROTECT);
if (status != CLOSEPROTECT) /* real errors? */
luaD_seterrorobj(L, status, L->stack + 1);
else {
status = LUA_OK;
L->top = L->stack + 1;
}
ci->top = L->top + LUA_MINSTACK;
L->status = status;
lua_unlock(L);
return status;
}
#endif
/* TODO following should probably not live here*/
static void raviE_default_writestring(const char *s, size_t l) {

@ -1788,7 +1788,7 @@ int luaV_execute (lua_State *L) {
/* close all upvalues from previous call */
#ifdef RAVI_DEFER_STATEMENT
if (cl->p->sizep > 0)
Protect_base(luaF_close(L, oci->u.l.base, LUA_OK));
Protect_base(luaF_close(L, oci->u.l.base, NOCLOSINGMETH));
#else
if (cl->p->sizep > 0) luaF_close(L, oci->u.l.base);
#endif

@ -0,0 +1,485 @@
if ravi then
ravi.auto(true)
end
T = ravi
local opcodes_coverage = {}
opcodes_coverage.MOVE = 0
opcodes_coverage.LOADK = 0
opcodes_coverage.LOADKX = 0
opcodes_coverage.LOADBOOL = 0
opcodes_coverage.LOADNIL = 0
opcodes_coverage.GETUPVAL = 0
opcodes_coverage.GETTABUP = 0
opcodes_coverage.GETTABUP_SK = 0
opcodes_coverage.GETTABLE = 0
opcodes_coverage.SETTABUP = 0
opcodes_coverage.SETTABUP_SK = 0
opcodes_coverage.SETUPVAL = 0
opcodes_coverage.SETTABLE = 0
opcodes_coverage.NEWTABLE = 0
opcodes_coverage.SELF = 0
opcodes_coverage.ADD = 0
opcodes_coverage.SUB = 0
opcodes_coverage.MUL = 0
opcodes_coverage.MOD = 0
opcodes_coverage.POW = 0
opcodes_coverage.DIV = 0
opcodes_coverage.IDIV = 0
opcodes_coverage.BAND = 0
opcodes_coverage.BOR = 0
opcodes_coverage.BXOR = 0
opcodes_coverage.SHL = 0
opcodes_coverage.SHR = 0
opcodes_coverage.UNM = 0
opcodes_coverage.BNOT = 0
opcodes_coverage.NOT = 0
opcodes_coverage.LEN = 0
opcodes_coverage.CONCAT = 0
opcodes_coverage.JMP = 0
opcodes_coverage.EQ = 0
opcodes_coverage.LT = 0
opcodes_coverage.LE = 0
opcodes_coverage.TEST = 0
opcodes_coverage.TESTSET = 0
opcodes_coverage.CALL = 0
opcodes_coverage.TAILCALL = 0
opcodes_coverage.RETURN = 0
opcodes_coverage.FORLOOP = 0
opcodes_coverage.FORPREP = 0
opcodes_coverage.TFORCALL = 0
opcodes_coverage.TFORLOOP = 0
opcodes_coverage.SETLIST = 0
opcodes_coverage.CLOSURE = 0
opcodes_coverage.VARARG = 0
opcodes_coverage.EXTRAARG = 0
opcodes_coverage.NEW_IARRAY = 0
opcodes_coverage.NEW_FARRAY = 0
opcodes_coverage.LOADIZ = 0
opcodes_coverage.LOADFZ = 0
opcodes_coverage.UNMF = 0
opcodes_coverage.UNMI = 0
opcodes_coverage.ADDFF = 0
opcodes_coverage.ADDFI = 0
opcodes_coverage.ADDII = 0
opcodes_coverage.SUBFF = 0
opcodes_coverage.SUBFI = 0
opcodes_coverage.SUBIF = 0
opcodes_coverage.SUBII = 0
opcodes_coverage.MULFF = 0
opcodes_coverage.MULFI = 0
opcodes_coverage.MULII = 0
opcodes_coverage.DIVFF = 0
opcodes_coverage.DIVFI = 0
opcodes_coverage.DIVIF = 0
opcodes_coverage.DIVII = 0
opcodes_coverage.TOINT = 0
opcodes_coverage.TOFLT = 0
opcodes_coverage.TOIARRAY = 0
opcodes_coverage.TOFARRAY = 0
opcodes_coverage.MOVEI = 0
opcodes_coverage.MOVEF = 0
opcodes_coverage.MOVEIARRAY = 0
opcodes_coverage.MOVEFARRAY = 0
opcodes_coverage.IARRAY_GET = 0
opcodes_coverage.FARRAY_GET = 0
opcodes_coverage.IARRAY_SET = 0
opcodes_coverage.FARRAY_SET = 0
opcodes_coverage.FORLOOP_IP = 0
opcodes_coverage.FORLOOP_I1 = 0
opcodes_coverage.FORPREP_IP = 0
opcodes_coverage.FORPREP_I1 = 0
opcodes_coverage.SETUPVALI = 0
opcodes_coverage.SETUPVALF = 0
opcodes_coverage.SETUPVAL_IARRAY = 0
opcodes_coverage.SETUPVAL_FARRAY = 0
opcodes_coverage.IARRAY_SETI = 0
opcodes_coverage.FARRAY_SETF = 0
opcodes_coverage.BAND_II = 0
opcodes_coverage.BOR_II = 0
opcodes_coverage.BXOR_II = 0
opcodes_coverage.SHL_II = 0
opcodes_coverage.SHR_II = 0
opcodes_coverage.BNOT_I = 0
opcodes_coverage.EQ_II = 0
opcodes_coverage.EQ_FF = 0
opcodes_coverage.LT_II = 0
opcodes_coverage.LT_FF = 0
opcodes_coverage.LE_II = 0
opcodes_coverage.LE_FF = 0
opcodes_coverage.GETI = 0
opcodes_coverage.TABLE_GETFIELD = 0
opcodes_coverage.SETI = 0
opcodes_coverage.TABLE_SETFIELD = 0
opcodes_coverage.SETFIELD = 0
opcodes_coverage.GETFIELD = 0
opcodes_coverage.TOTAB = 0
opcodes_coverage.MOVETAB = 0
opcodes_coverage.SETUPVALT = 0
opcodes_coverage.TABLE_SELF_SK = 0
opcodes_coverage.TOTYPE = 0
opcodes_coverage.TOSTRING = 0
opcodes_coverage.TOCLOSURE = 0
opcodes_coverage.SELF_SK = 0
opcodes_coverage.DEFER = 0
local compile = function(f)
if ravi and ravi.jit() then
assert(ravi.compile(f))
end
return true
end
-- ================================================================
-- Following section is an extract from the code.lua test
-- These functions test bytecode generation, and also provide
-- helper routines that we use later on in other test cases
-- testing opcodes
function check (f, ...)
if not T then
return true
end
local arg = {...}
local c = T.listcode(f)
for i=1, #arg do
--print(arg[i], c[i])
opcodes_coverage[arg[i]] = opcodes_coverage[arg[i]]+1
assert(string.find(c[i], '- '..arg[i]..' *[AB][xs]?=%d'))
end
assert(c[#arg+2] == nil)
end
-- Test defer statement
do
local y = 0
local function x()
defer y = y + 1 end
defer y = y + 1 end
end
check(x, 'DEFER', 'CLOSURE', 'DEFER', 'CLOSURE', 'RETURN')
x()
assert(y == 2)
compile(x)
x()
assert(y == 4)
print 'Test 1 OK'
end
-- Test defer statement
do
local y = 0
local function x()
defer y = y + 1 end
error('raise error')
defer y = y + 2 end -- will not be called
end
pcall(x)
assert(y == 1)
compile(x)
pcall(x)
assert(y == 2)
print 'Test 2 OK'
end
-- Test defer statement
do
local y = 0
local function x()
defer y = y + 1 end
defer y = y + 2; error('err') end
defer y = y + 3 end
end
pcall(x)
assert(y == 6)
compile(x)
pcall(x)
assert(y == 12)
print 'Test 3 OK'
end
-- Test defer statement in tailcalls
do
local y = 0
local function x (n)
defer y = y + 1 end
if n > 0 then return x(n - 1) end
end
pcall(x, 3)
assert(y == 4)
compile(x)
pcall(x, 3)
assert(y == 8)
print 'Test 4 OK'
end
-- Simulate a test of resource closure with defer
do
local y = 0
local z = { count = 0 }
z.__index = z;
function z:new()
local object = {}
setmetatable(object, z)
return object
end
function z:open(arg)
if (arg) then
z.count = z.count + 1
return
end
y = 1
error('error opening')
end
function z.close()
z.count = z.count - 1
end
local function x(arg)
local f = z:new()
f:open(arg)
assert(z.count == 1)
defer f:close() end
end
x('filename')
assert(y == 0)
assert(z.count == 0)
pcall(x, false)
assert(z.count == 0)
assert(y == 1)
y = 0
compile(x)
compile(z.new)
compile(z.open)
compile(z.close)
x('filename')
assert(y == 0)
assert(z.count == 0)
pcall(x, false)
assert(z.count == 0)
assert(y == 1)
print 'Test 5 OK'
end
--- Test stack reallocation in defer statement
do
local function x(a) if a <= 0 then return else x(a-1) end end
local y = ravi.jit and 100 or 1000
local function z(...)
-- recursive call to make stack
defer x(y) end
return ...
end
do
local a,b,c = z(1,2,3)
assert(a == 1 and b == 2 and c == 3)
compile(x)
compile(z)
a,b,c = z(3,2,1)
assert(a == 3 and b == 2 and c == 1)
end
print 'Test 6 OK'
end
-- Adapted from Lua 5.4
local function stack(n) n = ((n == 0) or stack(n - 1)) end
local function func2close (f, x, y)
local obj = setmetatable({}, {__close = f})
if x then
return x, obj, y
else
return obj
end
end
do
local function t()
local a = {}
do
local b = false -- not to be closed
-- x is <close>
local x = setmetatable({"x"}, {__close = function (self)
a[#a + 1] = self[1] end})
defer getmetatable(x).__close(x) end
-- y is <close>
local w, y, z = func2close(function (self, err)
assert(err == nil); a[#a + 1] = "y"
end, 10, 20)
defer getmetatable(y).__close(y) end
local c = nil -- not to be closed
a[#a + 1] = "in"
assert(w == 10 and z == 20)
end
a[#a + 1] = "out"
assert(a[1] == "in" and a[2] == "y" and a[3] == "x" and a[4] == "out")
end
t()
compile(t)
t()
print 'Test 7 OK'
end
do
local function t()
local X = false
local x, closescope = func2close(function () stack(10); X = true end, 100)
assert(x == 100); x = 101; -- 'x' is not read-only
-- closing functions do not corrupt returning values
local function foo (x)
local _ = closescope
defer getmetatable(_).__close(_) end
return x, X, 23
end
local a, b, c = foo(1.5)
assert(a == 1.5 and b == false and c == 23 and X == true)
X = false
foo = function (x)
local _ = closescope
defer getmetatable(_).__close(_) end
local y = 15
return y
end
assert(foo() == 15 and X == true)
X = false
foo = function ()
local x = closescope
defer getmetatable(x).__close(x) end
return x
end
assert(foo() == closescope and X == true)
end
t()
compile(t)
t()
print 'Test 8 OK'
end
do
local function t()
-- calls cannot be tail in the scope of to-be-closed variables
local X, Y
local function foo ()
local _ = func2close(function () Y = 10 end)
defer getmetatable(_).__close(_) end
assert(X == true and Y == nil) -- 'X' not closed yet
return 1,2,3
end
local function bar ()
local _ = func2close(function () X = false end)
defer getmetatable(_).__close(_) end
X = true
do
return foo() -- not a tail call!
end
end
local a, b, c, d = bar()
assert(a == 1 and b == 2 and c == 3 and X == false and Y == 10 and d == nil)
return foo, bar
end
local f,b = t()
compile(f)
compile(b)
compile(t)
t()
print 'Test 9 OK'
end
do
local function t()
-- an error in a wrapped coroutine closes variables
local x = false
local y = false
local co = coroutine.wrap(function ()
local xv = func2close(function () x = true end)
defer getmetatable(xv).__close(xv) end
do
local yv = func2close(function () y = true end)
defer getmetatable(yv).__close(yv) end
coroutine.yield(100) -- yield doesn't close variable
end
coroutine.yield(200) -- yield doesn't close variable
error(23) -- error does
end)
local b = co()
assert(b == 100 and not x and not y)
b = co()
assert(b == 200 and not x and y)
local a, b = pcall(co)
assert(not a and b == 23 and x and y)
end
t()
compile(t)
t()
print 'Test 10 OK'
end
-- a suspended coroutine should not close its variables when collected
do
function t()
local co
co = coroutine.wrap(function()
-- should not run
local x = func2close(function () os.exit(false) end)
defer getmetatable(x).__close(x) end
co = nil
coroutine.yield()
end)
co() -- start coroutine
assert(co == nil) -- eventually it will be collected
collectgarbage()
end
t()
compile(t)
t()
print 'Test 11 OK'
end
do
local function t()
-- error in a wrapped coroutine raising errors when closing a variable
local x = 0
local co = coroutine.wrap(function ()
local xx = func2close(function () x = x + 1; error("@YYY") end)
defer getmetatable(xx).__close(xx) end
local xv = func2close(function () x = x + 1; error("@XXX") end)
defer getmetatable(xv).__close(xv) end
coroutine.yield(100)
error(200)
end)
assert(co() == 100); assert(x == 0)
local st, msg = pcall(co); assert(x == 2)
assert(not st and msg == 200) -- should get first error raised
local x = 0
local y = 0
co = coroutine.wrap(function ()
local xx = func2close(function () y = y + 1; error("YYY") end)
defer getmetatable(xx).__close(xx) end
local xv = func2close(function () x = x + 1; error("XXX") end)
defer getmetatable(xv).__close(xv) end
coroutine.yield(100)
return 200
end)
assert(co() == 100); assert(x == 0)
local st, msg = pcall(co)
assert(not st and string.find(msg, "%w+%.%w+:%d+: XXX"))
assert(x == 1 and y == 1)
end
t()
compile(t)
t()
print 'Test 12 OK'
end
print 'OK'

@ -1738,105 +1738,6 @@ print 'Test 77 OK'
--
-- Test defer statement
y = 0
function x()
defer y = y + 1 end
defer y = y + 1 end
end
check(x, 'DEFER', 'CLOSURE', 'DEFER', 'CLOSURE', 'RETURN')
x()
assert(y == 2)
compile(x)
x()
assert(y == 4)
print 'Test 78 OK'
-- Test defer statement
y = 0
function x()
defer y = y + 1 end
error('raise error')
defer y = y + 2 end -- will not be called
end
pcall(x)
assert(y == 1)
compile(x)
pcall(x)
assert(y == 2)
print 'Test 79 OK'
-- Test defer statement
y = 0
function x()
defer y = y + 1 end
defer y = y + 2; error('err') end
defer y = y + 3 end
end
pcall(x)
assert(y == 6)
compile(x)
pcall(x)
assert(y == 12)
print 'Test 80 OK'
-- Test defer statement in tailcalls
y = 0
function x (n)
defer y = y + 1 end
if n > 0 then return x(n - 1) end
end
pcall(x, 3)
assert(y == 4)
compile(x)
pcall(x, 3)
assert(y == 8)
print 'Test 81 OK'
-- Simulate a test of resource closure with defer
y = 0
z = { count = 0 }
z.__index = z;
function z:new()
local object = {}
setmetatable(object, z)
return object
end
function z:open(arg)
if (arg) then
z.count = z.count + 1
return
end
y = 1
error('error opening')
end
function z.close()
z.count = z.count - 1
end
function x(arg)
local f = z:new()
f:open(arg)
assert(z.count == 1)
defer f:close() end
end
x('filename')
assert(y == 0)
assert(z.count == 0)
pcall(x, false)
assert(z.count == 0)
assert(y == 1)
y = 0
compile(x)
compile(z.new)
compile(z.open)
compile(z.close)
x('filename')
assert(y == 0)
assert(z.count == 0)
pcall(x, false)
assert(z.count == 0)
assert(y == 1)
print 'Test 82 OK'
-- Create some slices and invoke GC
x = function ()
@ -1891,23 +1792,7 @@ compile(x)
x()
print 'Test 83 OK'
--- Test stack reallocation in defer statement
function x(a) if a <= 0 then return else x(a-1) end end
y = ravi.jit and 100 or 1000
function z(...)
-- recursive call to make stack
defer x(y) end
return ...
end
do
local a,b,c = z(1,2,3)
assert(a == 1 and b == 2 and c == 3)
compile(x)
compile(z)
a,b,c = z(3,2,1)
assert(a == 3 and b == 2 and c == 1)
end
print 'Test 84 OK'
for k,v in pairs(opcodes_coverage)
do

@ -44,6 +44,8 @@ run_lua53_tests "_port=true ravi.auto(true,1)" "Lua53 auto JIT all test failed"
run_ravi_tests language ravi_tests1.ravi "ravi.jit(false)"
run_ravi_tests language ravi_tests1.ravi "ravi.auto(true,1)"
run_ravi_tests language defer_tests.ravi "ravi.jit(false)"
run_ravi_tests language defer_tests.ravi "ravi.auto(true,1)"
run_ravi_tests language ravi_tests3.ravi "ravi.auto(true,1)"
run_ravi_tests language ravi_errors.ravi "ravi.auto(true,1)"
run_ravi_tests language basics.lua "ravi.auto(true,1)"

Loading…
Cancel
Save