commit
a6caf1b402
@ -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'
|
Loading…
Reference in new issue