Files
integration/luprex/eris-master/test/persist.lua

513 lines
12 KiB
Lua

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()