Change directory structure

This commit is contained in:
2023-02-14 14:05:45 -05:00
parent acad4291b6
commit def6387ca3
323 changed files with 161 additions and 19581 deletions

View File

@@ -0,0 +1,99 @@
#include <stdio.h>
#include <stdlib.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
static int LUAF_createludata(lua_State *L)
{
lua_pushlightuserdata(L, (void*)321);
return 1;
}
/* A userdata that may be literally persisted */
static int LUAF_boxinteger(lua_State *L)
{
/* num */
int* ptr = (int*)lua_newuserdata(L, sizeof(int));
/* num udata */
*ptr = luaL_checkint(L, 1);
lua_newtable(L);
/* num udata mt */
lua_pushstring(L, "__persist");
/* num udata mt "__persist" */
lua_pushboolean(L, 1);
/* num udata mt "__persist" true */
lua_rawset(L, 3);
/* num udata mt */
lua_setmetatable(L, 2);
/* num udata */
return 1;
}
static int LUAF_boxboolean(lua_State *L)
{
/* bool */
char* ptr = (char*)lua_newuserdata(L, sizeof(char));
/* bool udata */
*ptr = (char)lua_toboolean(L, 1);
lua_newtable(L);
/* num udata mt */
lua_pushstring(L, "__persist");
/* num udata mt "__persist" */
lua_getglobal(L, "booleanpersist");
/* num udata mt "__persist" booleanpersist */
lua_rawset(L, 3);
/* num udata mt */
lua_setmetatable(L, 2);
/* num udata */
return 1;
}
static int LUAF_unboxboolean(lua_State *L)
{
/* udata */
lua_pushboolean(L, *(char*)lua_touserdata(L, 1));
/* udata bool */
return 1;
}
static int LUAF_onerror(lua_State *L)
{
const char* str = 0;
if(lua_gettop(L) != 0)
{
str = lua_tostring(L, -1);
printf("%s\n",str);
}
return 0;
}
int main(int argc, char** argv)
{
if (argc < 2) {
printf("Usage: persist <script> <filename>\n");
return 1;
}
lua_State* L = luaL_newstate();
luaL_openlibs(L);
lua_settop(L, 0);
lua_register(L, "createludata", LUAF_createludata);
lua_register(L, "boxinteger", LUAF_boxinteger);
lua_register(L, "boxboolean", LUAF_boxboolean);
lua_register(L, "unboxboolean", LUAF_unboxboolean);
lua_register(L, "onerror", LUAF_onerror);
lua_pushcfunction(L, LUAF_onerror);
luaL_loadfile(L, argv[1]);
lua_pushstring(L, argv[2]);
lua_pcall(L,1,0,1);
lua_close(L);
return 0;
}

View File

@@ -0,0 +1,513 @@
local rootobj = {}
-------------------------------------------------------------------------------
-- Used by C callbacks, so let's define it first.
function booleanpersist(udata)
local b = unboxboolean(udata)
return function()
return boxboolean(b)
end
end
-------------------------------------------------------------------------------
-- Permanent values.
local permtable = { 1234 }
rootobj.testperm = permtable
-------------------------------------------------------------------------------
-- Basic value types.
rootobj.testnil = nil
rootobj.testfalse = false
rootobj.testtrue = true
rootobj.testludata = createludata()
rootobj.testseven = 7
rootobj.testfoobar = "foobar"
-------------------------------------------------------------------------------
-- Tables.
local testtbl = { a = 2, [2] = 4 }
rootobj.testtbl = testtbl
-------------------------------------------------------------------------------
-- NaNs in tables (checks that this doesn't break the internal ref table).
local nantable = {}
nantable[1] = 0/0
rootobj.testnan = nantable
-------------------------------------------------------------------------------
-- Cycles in tables.
local testloopa = {}
local testloopb = { testloopa = testloopa }
testloopa.testloopb = testloopb
rootobj.testlooptable = testloopa
-------------------------------------------------------------------------------
-- Metatables.
local twithmt = {}
setmetatable( twithmt, { __call = function() return 21 end } )
rootobj.testmt = twithmt
-------------------------------------------------------------------------------
-- Yet more metatables.
local niinmt = { a = 3 }
setmetatable(niinmt, {__newindex = function(key, val) end })
rootobj.testniinmt = niinmt
-------------------------------------------------------------------------------
-- Literal userdata.
local literaludata = boxinteger(71)
rootobj.testliteraludata = literaludata
-------------------------------------------------------------------------------
-- Functions (closures without upvalues).
local function func()
return 4
end
rootobj.testfuncreturnsfour = func
-------------------------------------------------------------------------------
-- Environment. This is really redundant in 5.2 since envs are just upvalues,
-- but it may still be considered somewhat special.
local testfenv = (function()
local _ENV = { abc = 456 }
return function()
return abc
end
end)()
rootobj.testfenv = testfenv
-------------------------------------------------------------------------------
-- Closures.
local function funcreturningclosure(n)
return function()
return n
end
end
rootobj.testclosure = funcreturningclosure(11)
rootobj.testnilclosure = funcreturningclosure(nil)
-------------------------------------------------------------------------------
-- More closures.
local function nestedfunc(n)
return (function(m) return m+2 end)(n+3)
end
rootobj.testnest = nestedfunc
-------------------------------------------------------------------------------
-- Cycles in upvalues.
local function GenerateObjects()
local Table = {}
function Table:Func()
return { Table, self }
end
function uvcycle()
return Table:Func()
end
end
GenerateObjects()
rootobj.testuvcycle = uvcycle
-------------------------------------------------------------------------------
-- Special callback for persisting tables.
--
-- Note: the __persist metamethod was deliberately disabled in the
-- luprex version of eris. Therefore this unit test was also disabled.
--
-- local sptable = { a = 3 }
--
-- setmetatable(sptable, {
-- __persist = function(tbl)
-- local a = tbl.a
-- return function()
-- return { a = a+3 }
-- end
-- end
-- })
--
-- rootobj.testsptable = sptable
-------------------------------------------------------------------------------
-- Special callbacks for persisting userdata.
rootobj.testspudata1 = boxboolean(true)
rootobj.testspudata2 = boxboolean(false)
-------------------------------------------------------------------------------
-- Reference correctness.
local sharedref = {}
refa = {sharedref = sharedref}
refb = {sharedref = sharedref}
rootobj.testsharedrefa = refa
rootobj.testsharedrefb = refb
-------------------------------------------------------------------------------
-- Shared upvalues (like reference correctness for upvalues).
local function makecounter()
local a = 0
return {
inc = function() a = a + 1 end,
cur = function() return a end
}
end
rootobj.testsharedupval = makecounter()
-------------------------------------------------------------------------------
-- Debug info.
local function debuginfo(foo)
foo = foo + foo
return debug.getlocal(1,1)
end
rootobj.testdebuginfo = debuginfo
-------------------------------------------------------------------------------
-- Suspended thread.
local function fc(i)
local ic = i + 1
coroutine.yield()
return ic*2
end
local function fb(i)
local ib = i + 1
ib = ib + fc(ib)
return ib
end
local function fa(i)
local ia = i + 1
return fb(ia)
end
local thr = coroutine.create(fa)
coroutine.resume(thr, 2)
rootobj.testthread = thr
-------------------------------------------------------------------------------
-- Not yet started thread.
rootobj.testnthread = coroutine.create(function() return func() end)
-------------------------------------------------------------------------------
-- Dead thread.
local deadthr = coroutine.create(function() return func() end)
coroutine.resume(deadthr)
rootobj.testdthread = deadthr
-------------------------------------------------------------------------------
-- Open upvalues (stored in thread stack).
local function uvinthreadfunc()
local a = 1
local b = function()
a = a+1
coroutine.yield(a)
a = a+1
end
a = a+1
b()
a = a+1
return a
end
local uvinthread = coroutine.create(uvinthreadfunc)
coroutine.resume(uvinthread)
rootobj.testuvinthread = uvinthread
-------------------------------------------------------------------------------
-- Yield across pcall.
local function protf(arg)
coroutine.yield()
error(arg, 0)
end
local function protthreadfunc()
local res, err = pcall(protf, "test")
return err
end
local protthr = coroutine.create(protthreadfunc)
coroutine.resume(protthr)
rootobj.testprotthr = protthr
-------------------------------------------------------------------------------
-- Yield across xpcall with message handler.
local function xprotthreadfunc()
local function handler(msg)
return "handler:" .. msg
end
local res, err = xpcall(protf, handler, "test")
return err
end
local xprotthr = coroutine.create(xprotthreadfunc)
coroutine.resume(xprotthr)
rootobj.testxprotthr = xprotthr
-------------------------------------------------------------------------------
-- Yield out of metafunction.
local function ymtf(arg)
coroutine.yield()
return true
end
function ymtthreadfunc()
local t = setmetatable({}, {__lt = ymtf})
return t < 5
end
local ymtthr = coroutine.create(ymtthreadfunc)
coroutine.resume(ymtthr)
rootobj.testymtthr = ymtthr
-------------------------------------------------------------------------------
-- I considered supporting the hook callback from the debug library, but then
-- Eris would also have to persist the registry table the debug library uses,
-- and things go quickly out of hand that way, so I decided against that.
--[[
function hookthrfunc()
local hookRan = false
local function callback()
print("hook!")
hookRan = true
end
debug.sethook(callback, "", 100000)
coroutine.yield("yielded")
for i = 1, 10000000 do
if hookRan then break end
end
return hookRan
end
hookthr = coroutine.create(hookthrfunc)
print(coroutine.resume(hookthr))
rootobj.testhookthr = hookthr
]]
-------------------------------------------------------------------------------
-- Deep callstacks (100 levels).
local function deepfunc(x)
x = x or 0
if x == 100 then
coroutine.yield()
return x
end
local result = deepfunc(x + 1) -- no tailcall
return result
end
local deepcall = coroutine.wrap(deepfunc)
deepcall()
rootobj.testdeep = deepcall
-------------------------------------------------------------------------------
-- Tail calls.
local function tailfunc()
local function tailer(x)
x = x or 0
if x == 100 then
coroutine.yield()
return x
end
return tailer(x + 1)
end
local result = tailer()
return result
end
function wrap(t)
local co = coroutine.create(t)
return function(...)
local res = {coroutine.resume(co, ...)}
if res[1] then return select(2, table.unpack(res)) end
error(select(2, table.unpack(res)), 0)
end
end
local tailcall = wrap(tailfunc)
tailcall()
rootobj.testtail = tailcall
-------------------------------------------------------------------------------
-- From the Lua test cases, as a more complex piece of code. Since this is
-- easier to verify visually it spams the output quite a bit, so it's disabled
-- per default.
--[[
local lifethr = coroutine.create(function()
local _ENV = { write = coroutine.yield }
-- life.lua
-- original by Dave Bollinger <DBollinger@compuserve.com> posted to lua-l
-- modified to use ANSI terminal escape sequences
-- modified to use for instead of while
-- modified for this test
ALIVE="¥" DEAD="þ"
ALIVE="O" DEAD="-"
function ARRAY2D(w,h)
local t = {w=w,h=h}
for y=1,h do
t[y] = {}
for x=1,w do
t[y][x]=0
end
end
return t
end
_CELLS = {}
-- give birth to a "shape" within the cell array
function _CELLS:spawn(shape,left,top)
for y=0,shape.h-1 do
for x=0,shape.w-1 do
self[top+y][left+x] = shape[y*shape.w+x+1]
end
end
end
-- run the CA and produce the next generation
function _CELLS:evolve(next)
local ym1,y,yp1,yi=self.h-1,self.h,1,self.h
while yi > 0 do
local xm1,x,xp1,xi=self.w-1,self.w,1,self.w
while xi > 0 do
local sum = self[ym1][xm1] + self[ym1][x] + self[ym1][xp1] +
self[y][xm1] + self[y][xp1] +
self[yp1][xm1] + self[yp1][x] + self[yp1][xp1]
next[y][x] = ((sum==2) and self[y][x]) or ((sum==3) and 1) or 0
xm1,x,xp1,xi = x,xp1,xp1+1,xi-1
end
ym1,y,yp1,yi = y,yp1,yp1+1,yi-1
end
end
-- output the array to screen
function _CELLS:draw()
local out="" -- accumulate to reduce flicker
for y=1,self.h do
for x=1,self.w do
out=out..(((self[y][x]>0) and ALIVE) or DEAD)
end
out=out.."\n"
end
write(out)
end
-- constructor
function CELLS(w,h)
local c = ARRAY2D(w,h)
c.spawn = _CELLS.spawn
c.evolve = _CELLS.evolve
c.draw = _CELLS.draw
return c
end
--
-- shapes suitable for use with spawn() above
--
HEART = { 1,0,1,1,0,1,1,1,1; w=3,h=3 }
GLIDER = { 0,0,1,1,0,1,0,1,1; w=3,h=3 }
EXPLODE = { 0,1,0,1,1,1,1,0,1,0,1,0; w=3,h=4 }
FISH = { 0,1,1,1,1,1,0,0,0,1,0,0,0,0,1,1,0,0,1,0; w=5,h=4 }
BUTTERFLY = { 1,0,0,0,1,0,1,1,1,0,1,0,0,0,1,1,0,1,0,1,1,0,0,0,1; w=5,h=5 }
-- the main routine
function LIFE(w,h)
-- create two arrays
local thisgen = CELLS(w,h)
local nextgen = CELLS(w,h)
-- create some life
-- about 1000 generations of fun, then a glider steady-state
thisgen:spawn(GLIDER,5,4)
thisgen:spawn(EXPLODE,25,10)
thisgen:spawn(FISH,4,12)
-- run until break
local gen=1
while 1 do
thisgen:evolve(nextgen)
thisgen,nextgen = nextgen,thisgen
thisgen:draw()
gen=gen+1
if gen>2000 then break end
end
end
LIFE(40,20)
end)
print(select(2, coroutine.resume(lifethr)))
print(select(2, coroutine.resume(lifethr)))
rootobj.testlife = lifethr
--]]
-------------------------------------------------------------------------------
-- Do actual persisting with some perms.
perms = {
[_ENV] = "_ENV",
[coroutine.yield] = 1,
[permtable] = 2,
[pcall] = 3,
[xpcall] = 4,
}
buf = eris.persist(perms, rootobj)
-------------------------------------------------------------------------------
-- Write to file.
outfile = io.open(..., "wb")
outfile:write(buf)
outfile:close()

View File

@@ -0,0 +1,84 @@
#include <stdio.h>
#include <stdlib.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
static int LUAF_checkludata(lua_State *L)
{
lua_pushboolean(L, lua_touserdata(L, -1) == (void*)321);
return 1;
}
static int LUAF_unboxinteger(lua_State *L)
{
lua_pushnumber(L, *((int*)lua_touserdata(L, -1)));
return 1;
}
static int LUAF_unboxboolean(lua_State *L)
{
/* udata */
lua_pushboolean(L, *(char*)lua_touserdata(L, 1));
/* udata bool */
return 1;
}
static int LUAF_boxboolean(lua_State *L)
{
/* bool */
char* ptr = (char*)lua_newuserdata(L, sizeof(char));
/* bool udata */
*ptr = (char)lua_toboolean(L, 1);
lua_newtable(L);
/* num udata mt */
lua_pushstring(L, "__persist");
/* num udata mt "__persist" */
lua_getglobal(L, "booleanpersist");
/* num udata mt "__persist" booleanpersist */
lua_rawset(L, 3);
/* num udata mt */
lua_setmetatable(L, 2);
/* num udata */
return 1;
}
static int LUAF_onerror(lua_State *L)
{
const char* str = 0;
if(lua_gettop(L) != 0)
{
str = lua_tostring(L, -1);
printf("%s\n",str);
}
return 0;
}
int main(int argc, char** argv)
{
if (argc < 2) {
printf("Usage: unpersist <script> <filename>\n");
return 1;
}
lua_State* L = luaL_newstate();
luaL_openlibs(L);
lua_settop(L, 0);
lua_register(L, "checkludata", LUAF_checkludata);
lua_register(L, "unboxinteger", LUAF_unboxinteger);
lua_register(L, "boxboolean", LUAF_boxboolean);
lua_register(L, "unboxboolean", LUAF_unboxboolean);
lua_register(L, "onerror", LUAF_onerror);
lua_pushcfunction(L, LUAF_onerror);
luaL_loadfile(L, argv[1]);
lua_pushstring(L, argv[2]);
lua_pcall(L,1,0,1);
lua_close(L);
return 0;
}

View File

@@ -0,0 +1,107 @@
permtable = { 1234 }
function testcounter(counter)
local a = counter.cur()
counter.inc()
return counter.cur() == a + 1
end
function testuvinthread(func)
local success, result = coroutine.resume(func)
return success and result == 5
end
function test(rootobj)
local passed = 0
local total = 0
local dotest = function(name, cond)
total = total + 1
if cond then
print(name, " PASSED")
passed = passed + 1
else
print(name, "*FAILED")
end
end
dotest("Permanent value ", rootobj.testperm == permtable)
dotest("Nil value ", rootobj.testnil == nil)
dotest("Boolean FALSE ", rootobj.testfalse == false)
dotest("Boolean TRUE ", rootobj.testtrue == true)
dotest("Light userdata ", checkludata(rootobj.testludata))
dotest("Number 7 ", rootobj.testseven == 7)
dotest("String 'foobar' ", rootobj.testfoobar == "foobar")
dotest("Table ", rootobj.testtbl.a == 2 and rootobj.testtbl[2] == 4)
dotest("NaN value ", rootobj.testnan[1] ~= rootobj.testnan[1])
dotest("Looped tables ", rootobj.testlooptable.testloopb.testloopa == rootobj.testlooptable)
dotest("Table metatable ", rootobj.testmt() == 21)
dotest("__newindex metamethod ", rootobj.testniinmt.a == 3)
dotest("Udata literal persist ", unboxinteger(rootobj.testliteraludata) == 71)
dotest("Func returning 4 ", rootobj.testfuncreturnsfour() == 4)
dotest("Lua closure ", rootobj.testclosure() == 11)
dotest("Function env ", rootobj.testfenv() == 456)
dotest("Nil in closure ", rootobj.testnilclosure() == nil)
dotest("Nested func ", rootobj.testnest(1) == 6)
dotest("Upvalue cycles ", rootobj.testuvcycle()[1] == rootobj.testuvcycle()[2])
dotest("Udata special persist ", unboxboolean(rootobj.testspudata1) == true and unboxboolean(rootobj.testspudata2) == false)
dotest("Identical tables ", rootobj.testsharedrefa ~= rootobj.testsharedrefb)
dotest("Shared reference ", rootobj.testsharedrefa.sharedref == rootobj.testsharedrefb.sharedref)
dotest("Shared upvalues ", testcounter(rootobj.testsharedupval))
dotest("Debug info ", (rootobj.testdebuginfo(2)) == "foo")
dotest("Thread start ", coroutine.resume(rootobj.testnthread) == true, 4)
dotest("Thread resume ", coroutine.resume(rootobj.testthread) == true, 14)
dotest("Thread dead ", coroutine.resume(rootobj.testdthread) == false)
dotest("Open upvalues ", testuvinthread(rootobj.testuvinthread))
dotest("Yielded pcall ", coroutine.resume(rootobj.testprotthr) == true, "test")
dotest("Yielded xpcall ", coroutine.resume(rootobj.testxprotthr) == true, "handler:test")
dotest("Yielded metafunc ", coroutine.resume(rootobj.testymtthr) == true, true)
dotest("Deep callstack ", rootobj.testdeep() == 100)
dotest("Tail call ", rootobj.testtail() == 100)
-- Note: the following test uses the persist metamethod, which
-- was deliberately disabled in the luprex version of eris.
-- dotest("Table special persist ", rootobj.testsptable.a == 6)
print()
if passed == total then
print("All tests passed.")
else
print(passed .. "/" .. total .. " tests passed.")
end
if rootobj.testlife then
print(select(2, coroutine.resume(rootobj.testlife)))
print(select(2, coroutine.resume(rootobj.testlife)))
end
end
-------------------------------------------------------------------------------
infile, err = io.open(..., "rb")
if infile == nil then
error("While opening: " .. (err or "unknown error"))
end
buf, err = infile:read("*a")
if buf == nil then
error("While reading: " .. (err or "unknown error"))
end
infile:close()
-------------------------------------------------------------------------------
uperms = {
_ENV = _ENV,
[1] = coroutine.yield,
[2] = permtable,
[3] = pcall,
[4] = xpcall,
}
rootobj = eris.unpersist(uperms, buf)
test(rootobj)
os.remove(...)