You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1782 lines
41 KiB
1782 lines
41 KiB
ravi.auto(true)
|
|
|
|
print "testing code generation and optimizations"
|
|
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.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, ...)
|
|
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
|
|
|
|
|
|
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_SK', '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[4], a[b] = -((a + d/-20.5 - a[b]) ^ a.x), b
|
|
end,
|
|
'LOADNIL',
|
|
'MUL',
|
|
'DIV', 'ADD', 'GETTABLE', 'SUB', 'GETFIELD', 'POW',
|
|
'UNM', 'SETTABLE', 'SETI', 'RETURN')
|
|
|
|
-- direct access to constants
|
|
check(function ()
|
|
local a,b
|
|
a.x = 0
|
|
a.x = b
|
|
a[b] = 'y'
|
|
a = 1 - a
|
|
b = 1/a
|
|
b = 5+4
|
|
a[true] = false
|
|
end,
|
|
'LOADNIL',
|
|
'SETFIELD', 'SETFIELD', 'SETTABLE', 'SUB', 'DIV', 'LOADK',
|
|
'SETTABLE', 'RETURN')
|
|
|
|
|
|
-- 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=1 end end,
|
|
'GETTABUP_SK', '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)
|
|
if a == 1 then goto l1
|
|
elseif a == 2 then goto l2
|
|
elseif a == 3 then goto l2
|
|
else if a == 4 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 'Lua bytecode tests OK'
|
|
-- ================================================================
|
|
|
|
|
|
local z,x,y,tryme
|
|
|
|
-- test 1
|
|
z = function(a)
|
|
return a+1
|
|
end
|
|
x = function(f)
|
|
local j = 5
|
|
j = f(j)
|
|
return j
|
|
end
|
|
check(x, 'LOADK', 'MOVE', 'MOVE', 'CALL',
|
|
'MOVE', 'RETURN', 'RETURN')
|
|
check(z, 'ADD', 'RETURN', 'RETURN')
|
|
y = x(z)
|
|
compile(z)
|
|
compile(x)
|
|
assert(x(z) == 6 and y == 6)
|
|
print("Test 1 OK")
|
|
|
|
-- test 2
|
|
x = function ()
|
|
local i, j:integer
|
|
j=0
|
|
for i=1,1000000000 do
|
|
j = j+1
|
|
end
|
|
return j
|
|
end
|
|
check(x, 'LOADNIL', 'LOADIZ', 'LOADK', 'LOADK',
|
|
'LOADK', 'LOADK', 'FORPREP_I1', 'ADDII', 'FORLOOP_I1',
|
|
'RETURN', 'RETURN')
|
|
compile(x)
|
|
assert(x() == 1000000000)
|
|
print("Test 2 OK")
|
|
|
|
-- test 3
|
|
x = function ()
|
|
local j:number
|
|
for i=1,10000000 do
|
|
j = j+1
|
|
end
|
|
return j
|
|
end
|
|
check(x, 'LOADNIL', 'LOADFZ', 'LOADK', 'LOADK',
|
|
'LOADK', 'FORPREP_I1', 'ADDFI', 'FORLOOP_I1',
|
|
'RETURN', 'RETURN')
|
|
compile(x)
|
|
assert(x() == 10000000.0)
|
|
print("Test 3 OK")
|
|
|
|
-- test 4
|
|
x = function ()
|
|
local j = 0
|
|
for i=2,6,3 do
|
|
j = i
|
|
end
|
|
return j
|
|
end
|
|
check(x, 'LOADK', 'LOADK',
|
|
'LOADK', 'LOADK', 'FORPREP_IP', 'MOVE', 'FORLOOP_IP',
|
|
'RETURN', 'RETURN')
|
|
compile(x)
|
|
assert(x() == 5)
|
|
print("Test 4 OK")
|
|
|
|
-- test 5
|
|
x = function ()
|
|
if 1 == 2 then
|
|
return 5.0
|
|
end
|
|
return 1.0
|
|
end
|
|
check(x, 'EQ_II', 'JMP', 'LOADK',
|
|
'RETURN', 'LOADK', 'RETURN', 'RETURN')
|
|
compile(x)
|
|
assert(x() == 1.0)
|
|
print("Test 5 OK")
|
|
|
|
-- test 6
|
|
x = function (a: integer, b: integer)
|
|
if a == b then
|
|
return 5.0
|
|
end
|
|
return 1.0
|
|
end
|
|
check(x, 'TOINT', 'TOINT', 'EQ_II', 'JMP', 'LOADK',
|
|
'RETURN', 'LOADK', 'RETURN', 'RETURN')
|
|
compile(x)
|
|
assert(x(1,2) == 1.0)
|
|
print("Test 6 OK")
|
|
|
|
-- test 7
|
|
x = function (y: integer)
|
|
if y < 1 then
|
|
return 1.0
|
|
elseif y >= 5 then
|
|
return 2.0
|
|
else
|
|
return 3.0
|
|
end
|
|
end
|
|
check(x, 'TOINT', 'LT_II', 'JMP', 'LOADK',
|
|
'RETURN', 'JMP', 'LE_II', 'JMP', 'LOADK',
|
|
'RETURN', 'JMP', 'LOADK', 'RETURN', 'RETURN')
|
|
compile(x)
|
|
assert(x(5) == 2.0)
|
|
assert(x(4) == 3.0)
|
|
print("Test 7 OK")
|
|
|
|
-- test 8
|
|
x = function (y: number)
|
|
if y < 1.0 then
|
|
return 1.0
|
|
elseif y >= 5.0 then
|
|
return 2.0
|
|
else
|
|
return 3.0
|
|
end
|
|
end
|
|
check(x, 'TOFLT', 'LT_FF', 'JMP', 'LOADK',
|
|
'RETURN', 'JMP', 'LE_FF', 'JMP', 'LOADK',
|
|
'RETURN', 'JMP', 'LOADK', 'RETURN', 'RETURN')
|
|
compile(x)
|
|
assert(x(5.1) == 2.0)
|
|
assert(x(4.0) == 3.0)
|
|
print("Test 8 OK")
|
|
|
|
-- test 9
|
|
x = function (y: integer, z)
|
|
if y == 1 then
|
|
if z == 1 then
|
|
return 99.0
|
|
else
|
|
return z
|
|
end
|
|
elseif y >= 5 then
|
|
return 2.0
|
|
else
|
|
return 3.0
|
|
end
|
|
end
|
|
check(x, 'TOINT', 'EQ_II', 'JMP', 'EQ',
|
|
'JMP', 'LOADK', 'RETURN', 'JMP', 'RETURN', 'JMP',
|
|
'LE_II', 'JMP', 'LOADK',
|
|
'RETURN', 'JMP', 'LOADK', 'RETURN', 'RETURN')
|
|
assert(compile(x))
|
|
assert(x(1,1) == 99.0)
|
|
assert(x(1,2) == 2)
|
|
assert(x(1,5.3) == 5.3)
|
|
assert(x(5) == 2.0)
|
|
assert(x(4) == 3.0)
|
|
print("Test 9 OK")
|
|
|
|
-- test 10
|
|
x = function()
|
|
local function tryme()
|
|
local i,j = 5,6
|
|
return i,j
|
|
end
|
|
assert(compile(tryme))
|
|
local i:integer, j:integer = tryme()
|
|
assert(i+j == 11)
|
|
end
|
|
compile(x)
|
|
check(x, 'CLOSURE', 'GETTABUP_SK', 'GETUPVAL',
|
|
'MOVE', 'CALL', 'CALL', 'MOVE', 'CALL', 'TOINT', 'TOINT',
|
|
'GETTABUP_SK', 'ADDII', 'EQ_II', 'JMP', 'LOADBOOL', 'LOADBOOL',
|
|
'CALL', 'RETURN')
|
|
x()
|
|
print("Test 10 OK")
|
|
|
|
-- test 11
|
|
function x()
|
|
local a : number[], j:number = {}
|
|
for i=1,10 do
|
|
a[i] = i
|
|
j = j + a[i]
|
|
end
|
|
return j
|
|
end
|
|
assert(x() == 55.0)
|
|
check(x, 'NEW_FARRAY', 'LOADNIL', 'LOADFZ',
|
|
'LOADK', 'LOADK', 'LOADK', 'FORPREP_I1',
|
|
'FARRAY_SET', 'FARRAY_GET', 'ADDFF',
|
|
'FORLOOP_I1', 'RETURN', 'RETURN')
|
|
compile(x)
|
|
assert(x() == 55.0)
|
|
print("Test 11 OK")
|
|
|
|
-- test 12
|
|
function pisum()
|
|
local sum : number
|
|
for j = 1,500 do
|
|
sum = 0.0
|
|
for k = 1,10000 do
|
|
sum = sum + 1.0/(k*k)
|
|
end
|
|
end
|
|
return sum
|
|
end
|
|
assert(math.abs(pisum()-1.644834071848065) < 1e-12)
|
|
check(pisum, 'LOADNIL', 'LOADFZ', 'LOADK', 'LOADK',
|
|
'LOADK', 'FORPREP_I1', 'LOADK', 'LOADK',
|
|
'LOADK', 'LOADK', 'FORPREP_I1', 'MULII',
|
|
'DIVFI', 'ADDFF', 'FORLOOP_I1', 'FORLOOP_I1',
|
|
'RETURN', 'RETURN')
|
|
assert(compile(pisum))
|
|
assert(math.abs(pisum()-1.644834071848065) < 1e-12)
|
|
print("Test 12 OK")
|
|
|
|
-- test 13
|
|
function y()
|
|
local i,j = 5.1,6.2
|
|
return i,j
|
|
end
|
|
function x(f)
|
|
local i:number, j:number = f()
|
|
return i+j
|
|
end
|
|
assert(math.abs(x(y) - 11.3) < 0.0001)
|
|
check(x, 'MOVE', 'CALL', 'TOFLT',
|
|
'TOFLT', 'ADDFF', 'RETURN', 'RETURN')
|
|
assert(compile(y))
|
|
assert(compile(x))
|
|
assert(math.abs(x(y) - 11.3) < 0.0001)
|
|
print("Test 13 OK")
|
|
|
|
-- test 14
|
|
function tryme(x,y)
|
|
if x < y then
|
|
return 1
|
|
else
|
|
return 0
|
|
end
|
|
end
|
|
check(tryme, 'LT', 'JMP', 'LOADK',
|
|
'RETURN', 'JMP', 'LOADK', 'RETURN',
|
|
'RETURN')
|
|
assert(tryme(1,2) == 1)
|
|
assert(tryme(2,1) == 0)
|
|
compile(tryme)
|
|
assert(tryme(1,2) == 1)
|
|
assert(tryme(2,1) == 0)
|
|
print("Test 14 OK")
|
|
|
|
-- test 15
|
|
function tryme(x,y)
|
|
return x < y
|
|
end
|
|
check(tryme, 'LT', 'JMP', 'LOADBOOL',
|
|
'LOADBOOL', 'RETURN', 'RETURN')
|
|
assert(tryme(1,2))
|
|
compile(tryme)
|
|
assert(tryme(1,2))
|
|
assert(not tryme(2,1))
|
|
print("Test 15 OK")
|
|
|
|
-- test 16
|
|
function tabtest(x)
|
|
x[1] = 5
|
|
return x[1]
|
|
end
|
|
assert(tabtest({}) == 5)
|
|
check(tabtest, 'SETI', 'GETI', 'RETURN', 'RETURN')
|
|
compile(tabtest)
|
|
assert(tabtest({}) == 5)
|
|
print("Test 16 OK")
|
|
|
|
-- test 17
|
|
function optest()
|
|
local a,b,c = 1, 5
|
|
c = a and b
|
|
return c
|
|
end
|
|
check(optest, 'LOADK', 'LOADK', 'LOADNIL',
|
|
'TESTSET', 'JMP', 'MOVE', 'RETURN', 'RETURN')
|
|
assert(optest() == 5)
|
|
compile(optest)
|
|
assert(optest() == 5)
|
|
print("Test 17 OK")
|
|
|
|
-- test 18
|
|
function optest()
|
|
local a,b,c = 1, 5
|
|
c = a or b
|
|
return c
|
|
end
|
|
check(optest, 'LOADK', 'LOADK', 'LOADNIL',
|
|
'TESTSET', 'JMP', 'MOVE', 'RETURN', 'RETURN')
|
|
assert(optest() == 1)
|
|
compile(optest)
|
|
assert(optest() == 1)
|
|
print("Test 18 OK")
|
|
|
|
-- test 19
|
|
function optest()
|
|
local a,b = 1, 5
|
|
if a and b then
|
|
return b
|
|
end
|
|
return a
|
|
end
|
|
check(optest, 'LOADK', 'LOADK', 'TEST', 'JMP',
|
|
'TEST', 'JMP', 'RETURN', 'RETURN', 'RETURN')
|
|
assert(optest() == 5)
|
|
compile(optest)
|
|
assert(optest() == 5)
|
|
print("Test 19 OK")
|
|
|
|
-- test 20
|
|
function optest()
|
|
local a,b = nil, 5
|
|
if a or b then
|
|
return b
|
|
end
|
|
return a
|
|
end
|
|
check(optest, 'LOADNIL', 'LOADK', 'TEST', 'JMP',
|
|
'TEST', 'JMP', 'RETURN', 'RETURN', 'RETURN')
|
|
assert(optest() == 5)
|
|
compile(optest)
|
|
assert(optest() == 5)
|
|
print("Test 20 OK")
|
|
|
|
-- test 21
|
|
z = function()
|
|
local x=function()
|
|
local j:number[] = {}
|
|
return j
|
|
end
|
|
compile(x)
|
|
y=x()
|
|
y[1] = 99.67
|
|
assert(y[1], 99.67)
|
|
assert(@integer (#y) == 1)
|
|
end
|
|
check(z, 'CLOSURE', 'GETUPVAL', 'MOVE', 'CALL',
|
|
'MOVE', 'CALL', 'SETUPVAL', 'SETTABUP', 'GETTABUP_SK', 'GETTABUP',
|
|
'LOADK', 'CALL', 'GETTABUP_SK', 'GETUPVAL', 'LEN', 'TOINT', 'EQ_II',
|
|
'JMP', 'LOADBOOL', 'LOADBOOL', 'CALL', 'RETURN')
|
|
z()
|
|
compile(z)
|
|
z()
|
|
print("Test 21 OK")
|
|
|
|
-- test 22
|
|
z = function()
|
|
local days: table = {"Sunday", "Monday", "Tuesday", "Wednesday",
|
|
"Thursday", "Friday", "Saturda"}
|
|
assert(days[1] == 'Sunday')
|
|
assert(#days == 7)
|
|
assert(days[3] == 'Tuesday')
|
|
days[7] = days[7] .. 'y'
|
|
x = function()
|
|
local t = ''
|
|
for k,v in pairs(days) do
|
|
t = t .. v
|
|
end
|
|
return t
|
|
end
|
|
assert(compile(x))
|
|
assert(x() == "SundayMondayTuesdayWednesdayThursdayFridaySaturday")
|
|
end
|
|
check(z, 'NEWTABLE', 'LOADK', 'LOADK', 'LOADK', 'LOADK',
|
|
'LOADK', 'LOADK', 'LOADK', 'SETLIST', 'GETTABUP_SK', 'GETI',
|
|
'EQ', 'JMP', 'LOADBOOL', 'LOADBOOL', 'CALL', 'GETTABUP_SK', 'LEN', 'TOINT',
|
|
'EQ_II', 'JMP', 'LOADBOOL', 'LOADBOOL', 'CALL', 'GETTABUP_SK', 'GETI',
|
|
'EQ', 'JMP', 'LOADBOOL', 'LOADBOOL', 'CALL', 'GETI', 'LOADK',
|
|
'CONCAT', 'SETI', 'CLOSURE', 'SETUPVAL', 'GETTABUP_SK', 'GETUPVAL',
|
|
'GETUPVAL', 'CALL', 'CALL', 'GETTABUP_SK', 'GETUPVAL', 'CALL', 'EQ',
|
|
'JMP', 'LOADBOOL', 'LOADBOOL', 'CALL', 'RETURN')
|
|
z()
|
|
compile(z)
|
|
z()
|
|
print("Test 22 OK")
|
|
|
|
-- test 23
|
|
x = function(a)
|
|
return not a
|
|
end
|
|
y = function(a)
|
|
return a
|
|
end
|
|
assert(compile(x))
|
|
assert(compile(y))
|
|
assert(y(x()))
|
|
assert(y(x(false)))
|
|
assert(not y(x(true)))
|
|
print("Test 23 OK")
|
|
|
|
-- test 24
|
|
t = { name_ = "ravi" }
|
|
function t:name()
|
|
return self.name_
|
|
end
|
|
function z(t)
|
|
return t:name()
|
|
end
|
|
assert(compile(t.name))
|
|
assert(compile(z))
|
|
assert(z(t) == "ravi")
|
|
print("Test 24 OK")
|
|
|
|
-- test 25
|
|
-- test ravi integer array
|
|
function f()
|
|
local x: integer[] = { 1, 5 }
|
|
local y: integer
|
|
|
|
x[3] = x[1] + x[2]
|
|
y = x[3]
|
|
|
|
return y
|
|
end
|
|
assert(compile(f))
|
|
assert(f() == 6)
|
|
print("Test 25 OK")
|
|
|
|
-- test 26
|
|
function f()
|
|
local x: number[] = { 5.0, 6.1 }
|
|
x[3] = x[1] + x[2]
|
|
return x[3]
|
|
end
|
|
--ravi.dumplua(f)
|
|
assert(compile(f))
|
|
assert(math.abs(f()-11.1) < 1e-12)
|
|
print("Test 26 OK")
|
|
|
|
-- test 27
|
|
-- Ravi arrays support for ipairs()
|
|
-- Plus special slot at [0]
|
|
x = function()
|
|
local nums: integer[] = {1, 2, 3, 4, 5, 6, 7}
|
|
local t = 0
|
|
assert(#nums == 7)
|
|
nums[0] = 558
|
|
for k,v in ipairs(nums) do
|
|
t = t + v
|
|
end
|
|
assert(nums[0] == 558)
|
|
return t
|
|
end
|
|
assert(compile(x))
|
|
assert(x() == 28)
|
|
print("Test 27 OK")
|
|
|
|
-- test 28
|
|
-- Ravi arrays support for pairs()
|
|
-- Plus special slot at [0]
|
|
x = function()
|
|
local nums: integer[] = {1, 2, 3, 4, 5, 6, 7}
|
|
local t = 0
|
|
assert(#nums == 7)
|
|
nums[0] = 558
|
|
for k,v in pairs(nums) do
|
|
t = t + v
|
|
end
|
|
assert(nums[0] == 558)
|
|
return t
|
|
end
|
|
assert(compile(x))
|
|
assert(x() == 28)
|
|
print("Test 28 OK")
|
|
|
|
-- test 29
|
|
-- test creation of arrays and slice
|
|
x = function()
|
|
local zeros: integer[] = table.intarray(10, 0)
|
|
local ones: integer[] = table.intarray(10, 1)
|
|
|
|
local a : integer[] = table.slice(ones, 1, 10)
|
|
assert(#zeros == 10)
|
|
assert(#ones == 10)
|
|
assert(#a == 10)
|
|
|
|
local i = 0
|
|
for k,v in ipairs(a) do
|
|
i = i + v
|
|
end
|
|
assert(i == 10)
|
|
local y = table.slice(ones, 10, 1)
|
|
y[1] = 115
|
|
return ones[10] == 115
|
|
end
|
|
assert(compile(x))
|
|
assert(x() == true)
|
|
print("Test 29 OK")
|
|
|
|
-- test 30
|
|
matrix = {}
|
|
matrix.new = function (m, n)
|
|
local t = {m, n, table.numarray(m*n, 0)}
|
|
return t
|
|
end
|
|
|
|
matrix.getcol = function(m, col)
|
|
local rows = m[1]
|
|
local cols = m[2]
|
|
local data = m[3]
|
|
assert(col > 0 and col <= cols)
|
|
return table.slice(data, (col-1)*rows+1, rows)
|
|
end
|
|
|
|
matrix.getdata = function(m)
|
|
return m[3]
|
|
end
|
|
|
|
matrix.datalen = function(data)
|
|
return #data
|
|
end
|
|
|
|
x = function()
|
|
local m = matrix.new(5,5)
|
|
local data: number[] = matrix.getdata(m)
|
|
local data_len: integer = matrix.datalen(data)
|
|
for i = 1,data_len do
|
|
data[i] = i
|
|
end
|
|
-- get third col
|
|
local col3 = matrix.getcol(m, 3)
|
|
-- should be 11,12,13,14,15
|
|
local n = 0
|
|
for k,v in ipairs(col3) do
|
|
n = n + v
|
|
end
|
|
-- should be 65
|
|
return n
|
|
end
|
|
|
|
assert(compile(matrix.new))
|
|
assert(compile(matrix.getcol))
|
|
assert(compile(matrix.getdata))
|
|
assert(compile(x))
|
|
assert(x() == 65)
|
|
print("Test 30 OK")
|
|
|
|
-- test 31
|
|
function testadd(a,b)
|
|
return a+b
|
|
end
|
|
assert(compile(testadd))
|
|
assert(testadd(1,1) == 2)
|
|
assert(testadd(1.5,1.6) == 3.1)
|
|
assert(testadd("1.5",1.6) == 3.1)
|
|
assert(testadd("1.5","1.6") == 3.1)
|
|
print("Test 31 OK")
|
|
|
|
-- test 32
|
|
function testsub(a,b)
|
|
return a-b
|
|
end
|
|
assert(compile(testsub))
|
|
assert(testsub(1,1) == 0)
|
|
assert(math.abs(testsub(1.5,1.6)-(-0.1)) < 1e-6)
|
|
assert(math.abs(testsub("1.5",1.6)-(-0.1)) < 1e-6)
|
|
assert(math.abs(testsub("1.5","1.6")-(-0.1)) < 1e-6)
|
|
print("Test 32 OK")
|
|
|
|
-- test 33
|
|
function testmul(a,b)
|
|
return a*b
|
|
end
|
|
assert(compile(testmul))
|
|
assert(testmul(2,2) == 4)
|
|
assert(math.abs(testmul(1.5,1.6)-2.4) < 1e-12)
|
|
assert(math.abs(testmul("1.5",1.6)-2.4) < 1e-12)
|
|
assert(math.abs(testmul("1.5","1.6")-2.4) < 1e-12)
|
|
print("Test 33 OK")
|
|
|
|
|
|
-- test 34
|
|
function testdiv(a,b)
|
|
return a/b
|
|
end
|
|
assert(compile(testdiv))
|
|
assert(testdiv(2,2) == 1.0)
|
|
assert(math.abs(testdiv(1.5,1.6)-0.9375) < 1e-12)
|
|
assert(math.abs(testdiv("1.5",1.6)-0.9375) < 1e-12)
|
|
assert(math.abs(testdiv("1.5","1.6")-0.9375) < 1e-12)
|
|
print("Test 34 OK")
|
|
|
|
|
|
-- test 35
|
|
-- this tests that within the for loop
|
|
-- the locals get mapped correctly to upvalues
|
|
function f()
|
|
local a={}
|
|
for i=1,2 do
|
|
local y:integer = 0
|
|
a[i] = function()
|
|
y = y + 10
|
|
return y;
|
|
end
|
|
end
|
|
return a
|
|
end
|
|
assert(compile(f))
|
|
t = f()
|
|
assert(#t == 2)
|
|
assert(compile(t[1]))
|
|
assert(compile(t[2]))
|
|
assert(t[1]() == 10)
|
|
assert(t[2]() == 10)
|
|
assert(t[1]() == 20)
|
|
assert(t[2]() == 20)
|
|
print("Test 35 OK")
|
|
|
|
-- test 36
|
|
-- upvalues
|
|
local x = 1
|
|
local y=function()
|
|
local f = function()
|
|
return x
|
|
end
|
|
compile(f)
|
|
return f
|
|
end
|
|
compile(y)
|
|
local f = y()
|
|
assert(f() == 1)
|
|
x=5
|
|
assert(f() == 5)
|
|
print("Test 36 OK")
|
|
|
|
-- test 37
|
|
-- upvalues
|
|
x1 = 3
|
|
local y=function()
|
|
local f = function()
|
|
return x1
|
|
end
|
|
compile(f)
|
|
return f
|
|
end
|
|
--ravi.dumplua(y)
|
|
compile(y)
|
|
local f = y()
|
|
--assert(ravi.iscompiled(f))
|
|
--ravi.dumplua(f)
|
|
--ravi.dumpllvm(f)
|
|
assert(f() == 3)
|
|
x1=5
|
|
assert(f() == 5)
|
|
print("Test 37 OK")
|
|
|
|
-- test 38
|
|
function x()
|
|
local x = 1
|
|
local f = function()
|
|
x=x+1
|
|
return x
|
|
end
|
|
return f
|
|
end
|
|
f=x()
|
|
assert(compile(f))
|
|
assert(f() == 2)
|
|
assert(f() == 3)
|
|
print("Test 38 OK")
|
|
|
|
-- test setupval, getupval
|
|
function x()
|
|
local a = 0
|
|
return function(x) a=a+x; return a; end
|
|
end
|
|
-- ravi.dumplua(x)
|
|
compile(x)
|
|
y=x()
|
|
compile(y)
|
|
assert(y(2) == 2)
|
|
assert(y(2) == 4)
|
|
assert(y(3) == 7)
|
|
print('Test 39 OK')
|
|
|
|
-- test 40
|
|
x=function(a,b)
|
|
return a%b
|
|
end
|
|
assert(compile(x))
|
|
-- When using 32-bit numbers we don't jit mod
|
|
-- if ravi.jit() then ravi.compile(x) end
|
|
assert(x(5,2) == 1)
|
|
assert(math.abs(x(5.1,2.1)-0.9) < 1e-6)
|
|
print("Test 40 OK")
|
|
|
|
-- test 41
|
|
x=function(a,b)
|
|
return a//b
|
|
end
|
|
assert(compile(x))
|
|
-- When using 32-bit numbers we don't jit idiv
|
|
-- if ravi.jit() then ravi.compile(x) end
|
|
assert(x(5,2) == 2)
|
|
assert(math.abs(x(5.5,2.1)-2.0) < 1e-6)
|
|
print("Test 41 OK")
|
|
|
|
-- test 42
|
|
x = function ()
|
|
local j = 0
|
|
for i=2.0,6.0,3.1 do
|
|
j = i
|
|
end
|
|
return j
|
|
end
|
|
|
|
if (not compile(x)) then
|
|
print("Test FAILED to compile")
|
|
end
|
|
assert(x() == 5.1)
|
|
print("Test 42 OK")
|
|
|
|
-- test parameter types
|
|
x = function (a: integer, b: number)
|
|
local i: integer = a
|
|
local j: number = b
|
|
return i+j
|
|
end
|
|
--ravi.dumplua(x)
|
|
assert(x(1,5.5) == 6.5)
|
|
compile(x)
|
|
assert(x(1,5.5) == 6.5)
|
|
print'Test 43 OK'
|
|
|
|
x=function (a:number[], b:integer)
|
|
local j: number = a[b]
|
|
return j
|
|
end
|
|
function y()
|
|
local t: number[] = { 4.2 }
|
|
return x(t, 1)
|
|
end
|
|
|
|
assert(y() == 4.2)
|
|
compile(x)
|
|
compile(y)
|
|
assert(y() == 4.2)
|
|
print'Test 44 OK'
|
|
|
|
-- test 45
|
|
function x(t) return t; end
|
|
function f()
|
|
local tt: integer[] = {1}
|
|
local ss: number[] = { 55.5 }
|
|
tt = x(tt)
|
|
ss = x(ss)
|
|
end
|
|
assert(compile(x))
|
|
assert(compile(f))
|
|
assert(pcall(f))
|
|
function f()
|
|
local tt: integer[] = {1}
|
|
tt = x({})
|
|
end
|
|
--ravi.dumplua(f)
|
|
assert(compile(f))
|
|
assert(not pcall(f))
|
|
function f()
|
|
local tt: integer[] = {1}
|
|
local ss: number[] = { 55.5 }
|
|
ss = x(tt)
|
|
end
|
|
assert(compile(f))
|
|
assert(not pcall(f))
|
|
|
|
print("Test 45 OK")
|
|
|
|
-- test 47
|
|
function test_idiv()
|
|
local t = {}
|
|
t.__idiv = function(...) return 'idiv' end
|
|
local t1=setmetatable({1,2,3}, t)
|
|
local t2=setmetatable({4,5,6}, t)
|
|
local x= function() return t1//t2 end
|
|
check(x, 'GETUPVAL', 'GETUPVAL', 'IDIV', 'RETURN', 'RETURN')
|
|
local s1=x()
|
|
assert(s1 == 'idiv')
|
|
assert(compile(t.__idiv))
|
|
assert(compile(x))
|
|
local s2=x()
|
|
assert(s1 == s2)
|
|
local x= function(t1: number, t2: number) return t1//t2 end
|
|
check(x, 'TOFLT', 'TOFLT', 'IDIV', 'RETURN', 'RETURN')
|
|
local s1=x(4.1,2.0)
|
|
assert(s1 == 2)
|
|
assert(compile(x))
|
|
local s2=x(4.1,2.0)
|
|
assert(s1 == s2)
|
|
end
|
|
compile(test_idiv)
|
|
test_idiv()
|
|
print'Test 47 (IDIV) OK'
|
|
|
|
function test_tableaccess()
|
|
-- Test specialised version of GETTABLE and SETTABLE
|
|
-- when local variable is known to be of table type
|
|
-- and key is a string
|
|
local f = function()
|
|
local t : table = {}
|
|
t.name = 'dibyendu'
|
|
t.data = {}
|
|
t.data.city = 'london'
|
|
return t.name, t.data.city
|
|
end
|
|
check(f, 'NEWTABLE', 'TABLE_SETFIELD', 'NEWTABLE',
|
|
'TABLE_SETFIELD', 'TABLE_GETFIELD', 'SETFIELD',
|
|
'TABLE_GETFIELD', 'TABLE_GETFIELD' , 'GETFIELD',
|
|
'RETURN', 'RETURN')
|
|
local a,b = f()
|
|
assert(a == 'dibyendu')
|
|
assert(b == 'london')
|
|
assert(compile(f))
|
|
a,b = f()
|
|
assert(a == 'dibyendu')
|
|
assert(b == 'london')
|
|
|
|
-- Test specialised version of GETTABLE and SETTABLE
|
|
-- when local variable is known to be of table type
|
|
-- and key is a integer
|
|
f = function()
|
|
local t : table = {}
|
|
t[1] = 'dibyendu'
|
|
t[2] = {}
|
|
t[2][1] = 'london'
|
|
return t[1], t[2][1]
|
|
end
|
|
check(f, 'NEWTABLE', 'SETI', 'NEWTABLE',
|
|
'SETI', 'GETI', 'SETI',
|
|
'GETI', 'GETI' , 'GETI',
|
|
'RETURN', 'RETURN')
|
|
local a,b = f()
|
|
assert(a == 'dibyendu')
|
|
assert(b == 'london')
|
|
assert(compile(f))
|
|
a,b = f()
|
|
assert(a == 'dibyendu')
|
|
assert(b == 'london')
|
|
end
|
|
test_tableaccess()
|
|
print 'Test 48 OK'
|
|
|
|
function test_self_s()
|
|
local t : table = {}
|
|
t.name_ = 'dibyendu majumdar'
|
|
t.name = function (t:table) return t.name_ end
|
|
assert(t:name())
|
|
return t:name()
|
|
end
|
|
check(test_self_s, 'NEWTABLE', 'TABLE_SETFIELD', 'CLOSURE',
|
|
'TABLE_SETFIELD', 'GETTABUP_SK', 'TABLE_SELF_SK', 'CALL', 'CALL', 'TABLE_SELF_SK', 'TAILCALL',
|
|
'RETURN')
|
|
assert(test_self_s() == 'dibyendu majumdar')
|
|
compile(test_self_s)
|
|
assert(test_self_s() == 'dibyendu majumdar')
|
|
print 'Test 49 OK'
|
|
|
|
-- issue #73
|
|
function bug_index_event()
|
|
local t1 = { name='dibyendu' }
|
|
local t2 = { surname='majumdar' }
|
|
t2.__index = t2
|
|
setmetatable(t1, t2)
|
|
assert(t1.name == 'dibyendu')
|
|
assert(t1.surname == 'majumdar') -- index event
|
|
assert(t1.surname == 'majumdar') -- index event
|
|
|
|
local t3: table = { name='dibyendu' }
|
|
setmetatable(t3, t2)
|
|
assert(t3.name == 'dibyendu')
|
|
assert(t3.surname == 'majumdar') -- index event
|
|
assert(t3.surname == 'majumdar') -- index event
|
|
|
|
t1 = { [1]='dibyendu' }
|
|
t2 = { [2]='majumdar' }
|
|
t2.__index = t2
|
|
setmetatable(t1, t2)
|
|
assert(t1[1] == 'dibyendu')
|
|
assert(t1[2] == 'majumdar') -- index event
|
|
assert(t1[2] == 'majumdar') -- index event
|
|
|
|
local t3: table = { [1]='dibyendu' }
|
|
setmetatable(t3, t2)
|
|
assert(t3[1] == 'dibyendu')
|
|
assert(t3[2] == 'majumdar') -- index event
|
|
assert(t3[2] == 'majumdar') -- index event
|
|
end
|
|
bug_index_event()
|
|
compile(bug_index_event)
|
|
bug_index_event()
|
|
print 'Test 50 OK'
|
|
|
|
|
|
function event_test()
|
|
local a: table = {}
|
|
setmetatable(a, {__metatable = "xuxu",
|
|
__tostring=function(x: table) return x.name end})
|
|
assert(getmetatable(a) == "xuxu")
|
|
--assert(tostring(a) == nil)
|
|
-- cannot change a protected metatable
|
|
assert(pcall(setmetatable, a, {}) == false)
|
|
a.name = "gororoba"
|
|
assert(tostring(a) == "gororoba")
|
|
end
|
|
event_test();
|
|
compile(event_test)
|
|
--ravi.dumpllvm(event_test)
|
|
event_test();
|
|
print 'Test 51 OK'
|
|
|
|
function test_intarray_as_table(t)
|
|
t.a = 'a'
|
|
--print(ravitype(t))
|
|
end
|
|
function do_test_intarray_as_table()
|
|
local iarray: integer[] = {1}
|
|
test_intarray_as_table(iarray);
|
|
end
|
|
assert(not pcall(do_test_intarray_as_table));
|
|
compile(do_test_intarray_as_table);
|
|
compile(test_intarray_as_table);
|
|
assert(not pcall(do_test_intarray_as_table));
|
|
print 'Test 52 OK'
|
|
|
|
function test_numarray_meta()
|
|
local farray : number[] = {1.1, 2.2, 3.3}
|
|
setmetatable(farray, {
|
|
__name = 'matrix',
|
|
__tostring = function() return '{' .. table.concat(farray, ",") .. '}' end
|
|
})
|
|
assert(ravitype(farray) == 'matrix')
|
|
assert(tostring(farray) == '{1.1,2.2,3.3}')
|
|
end
|
|
assert(pcall(test_numarray_meta));
|
|
compile(test_numarray_meta);
|
|
assert(pcall(test_numarray_meta));
|
|
print 'Test 53 OK'
|
|
|
|
|
|
function test_longkey()
|
|
local t: table = {}
|
|
t._01234567890123456789012345678901234567890123456789 = 4.2
|
|
assert(t._01234567890123456789012345678901234567890123456789 == 4.2)
|
|
t._01234567890 = 4.3
|
|
assert(t._01234567890 == 4.3)
|
|
t["_01234567890"] = 4.4
|
|
assert(t["_01234567890"] == 4.4)
|
|
local s = "_01234567890"
|
|
assert(t[s] == 4.4)
|
|
assert(t.s == nil)
|
|
end
|
|
check(test_longkey, 'NEWTABLE', 'SETTABLE', 'GETTABUP_SK', 'GETTABLE',
|
|
'EQ', 'JMP', 'LOADBOOL', 'LOADBOOL', 'CALL', 'TABLE_SETFIELD', 'GETTABUP_SK',
|
|
'TABLE_GETFIELD', 'EQ', 'JMP', 'LOADBOOL', 'LOADBOOL', 'CALL', 'TABLE_SETFIELD',
|
|
'GETTABUP_SK', 'TABLE_GETFIELD', 'EQ', 'JMP', 'LOADBOOL', 'LOADBOOL', 'CALL',
|
|
'LOADK', 'GETTABUP_SK', 'GETTABLE', 'EQ', 'JMP', 'LOADBOOL', 'LOADBOOL', 'CALL',
|
|
'GETTABUP_SK', 'TABLE_GETFIELD', 'EQ', 'JMP', 'LOADBOOL', 'LOADBOOL', 'CALL',
|
|
'RETURN')
|
|
assert(pcall(test_longkey));
|
|
compile(test_longkey);
|
|
assert(pcall(test_longkey));
|
|
print 'Test 54 OK'
|
|
|
|
|
|
function test_yields_in_metamethods()
|
|
--print"testing yields inside metamethods"
|
|
local mt = {
|
|
__eq = function(a:table,b:table) coroutine.yield(nil, "eq"); return a.x == b.x end,
|
|
__lt = function(a:table,b:table) coroutine.yield(nil, "lt"); return a.x < b.x end,
|
|
__le = function(a:table,b:table) coroutine.yield(nil, "le"); return a - b <= 0 end,
|
|
__add = function(a:table,b:table) coroutine.yield(nil, "add"); return a.x + b.x end,
|
|
__sub = function(a:table,b:table) coroutine.yield(nil, "sub"); return a.x - b.x end,
|
|
__mod = function(a:table,b:table) coroutine.yield(nil, "mod"); return a.x % b.x end,
|
|
__unm = function(a:table,b) coroutine.yield(nil, "unm"); return -a.x end,
|
|
__bnot = function(a:table,b) coroutine.yield(nil, "bnot"); return ~a.x end,
|
|
__shl = function(a:table,b:table) coroutine.yield(nil, "shl"); return a.x << b.x end,
|
|
__shr = function(a:table,b:table) 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:table,b:table) coroutine.yield(nil, "bor"); return a.x | b.x end,
|
|
__bxor = function(a:table,b:table) 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:table,k) coroutine.yield(nil, "idx"); return t.k[k] end,
|
|
__newindex = function (t:table,k,v) coroutine.yield(nil, "nidx"); t.k[k] = v end,
|
|
}
|
|
|
|
|
|
local function new (x)
|
|
return setmetatable({x = x, k = {}}, mt)
|
|
end
|
|
|
|
|
|
local a : table = new(10)
|
|
local b : table = new(12)
|
|
local c : table = 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 : table = setmetatable({x=1}, mt1)
|
|
local a2 : table = 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 ()
|
|
local a: table = a
|
|
a.BB = print
|
|
return a.BB
|
|
end, {"nidx", "idx"}) == print)
|
|
end
|
|
assert(pcall(test_yields_in_metamethods));
|
|
compile(test_longkey);
|
|
assert(pcall(test_yields_in_metamethods));
|
|
print 'Test 55 OK'
|
|
|
|
function test_upvaluejoin()
|
|
local debug = require'debug'
|
|
local foo1, foo2, foo3, foo4
|
|
do
|
|
local a:integer, b:integer = 3, 5
|
|
local c:number = 7.1
|
|
foo1 = function() return a+b end
|
|
foo2 = function() return b+a end
|
|
foo4 = function() return c end
|
|
do
|
|
local a: integer = 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)
|
|
-- Following will fail as typeof(foo4,1) is not same as typeof(foo1,1)
|
|
debug.upvaluejoin(foo4, 1, foo1, 1)
|
|
assert(foo4() == 7.1)
|
|
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))
|
|
end
|
|
assert(pcall(test_upvaluejoin));
|
|
compile(test_upvaluejoin);
|
|
assert(pcall(test_upvaluejoin));
|
|
print 'Test 56 OK'
|
|
|
|
function test_idiv(y: integer)
|
|
local era: integer
|
|
era = y // 400
|
|
return era
|
|
end
|
|
check(test_idiv, 'TOINT', 'LOADNIL', 'LOADIZ', 'IDIV', 'RETURN', 'RETURN')
|
|
assert(test_idiv(1900) == 4)
|
|
compile(test_idiv)
|
|
assert(test_idiv(1900) == 4)
|
|
print 'Test 57 OK'
|
|
|
|
function from_dmy1(y: integer, m: integer, d: integer)
|
|
if m <= 2 then
|
|
y = y - 1
|
|
end
|
|
local era: integer
|
|
if y >= 0 then
|
|
era = y // 400
|
|
else
|
|
era = (y-399) // 400
|
|
end
|
|
local yoe: integer = y - era * 400
|
|
local tmp: integer
|
|
if m > 2 then
|
|
tmp = -3
|
|
else
|
|
tmp = 9
|
|
end
|
|
local doy: integer = (153 * (m + tmp) + 2)//5 + d-1
|
|
local doe: integer = yoe * 365 + yoe//4 - yoe//100 + doy
|
|
return era * 146097 + doe - 719468
|
|
end
|
|
function from_dmy2(y, m, d)
|
|
if m <= 2 then
|
|
y = y - 1
|
|
end
|
|
local era
|
|
if y >= 0 then
|
|
era = y // 400
|
|
else
|
|
era = (y-399) // 400
|
|
end
|
|
local yoe = y - era * 400
|
|
local tmp
|
|
if m > 2 then
|
|
tmp = -3
|
|
else
|
|
tmp = 9
|
|
end
|
|
local doy = (153 * (m + tmp) + 2)//5 + d-1
|
|
local doe = yoe * 365 + yoe//4 - yoe//100 + doy
|
|
return era * 146097 + doe - 719468
|
|
end
|
|
assert(from_dmy1(1900, 1, 1) == -25567)
|
|
assert(from_dmy1(1900, 1, 1) == from_dmy2(1900, 1, 1))
|
|
compile(from_dmy1)
|
|
compile(from_dmy2)
|
|
assert(from_dmy1(1900, 1, 1) == -25567)
|
|
assert(from_dmy1(1900, 1, 1) == from_dmy2(1900, 1, 1))
|
|
check(from_dmy1, 'TOINT', 'TOINT', 'TOINT', 'LE_II', 'JMP',
|
|
'SUBII','LOADNIL','LOADIZ','LE_II','JMP','IDIV','JMP','SUBII',
|
|
'IDIV','MULII','SUBII','LOADNIL','LOADIZ','LT_II','JMP','LOADK',
|
|
'JMP','LOADK','ADDII','MULII','ADDII','IDIV','ADDII','SUBII',
|
|
'MULII','IDIV','ADDII','IDIV','SUBII','ADDII','MULII','ADDII',
|
|
'SUBII','RETURN','RETURN')
|
|
print 'Test 58 OK'
|
|
|
|
function x(s1: string, s2: string)
|
|
return @string( s1 .. s2 )
|
|
end
|
|
check(x, 'TOSTRING', 'TOSTRING', 'MOVE', 'MOVE', 'CONCAT', 'TOSTRING', 'RETURN', 'RETURN')
|
|
assert(x('a', 'b') == 'ab')
|
|
compile(x)
|
|
assert(x('a', 'b') == 'ab')
|
|
print 'Test 59 OK'
|
|
|
|
function x(f: closure)
|
|
local g = @closure f
|
|
return g()
|
|
end
|
|
check(x, 'TOCLOSURE', 'MOVE', 'MOVE', 'TAILCALL', 'RETURN', 'RETURN')
|
|
local called = 0
|
|
function y()
|
|
called = called + 1
|
|
end
|
|
x(y)
|
|
assert(called == 1)
|
|
compile(x)
|
|
compile(y)
|
|
x(y)
|
|
assert(called == 2)
|
|
print 'Test 60 OK'
|
|
|
|
local mt = { __name='MyType'}
|
|
debug.getregistry().MyType = mt
|
|
local t = {}
|
|
setmetatable(t, mt)
|
|
|
|
function x(s: MyType)
|
|
local assert = assert
|
|
assert(@MyType s == @MyType t)
|
|
assert(@MyType t == t)
|
|
end
|
|
x(t)
|
|
compile(x)
|
|
x(t)
|
|
print 'Test 61 OK'
|
|
|
|
function x()
|
|
local a: MyType
|
|
return function (b) a = b end
|
|
end
|
|
f = x()
|
|
assert(pcall(f, t)) -- t is of MyType so okay
|
|
assert(pcall(f, nil)) -- nil is okay
|
|
assert(not pcall(f, 'hello')) -- string not okay
|
|
ravi.compile(f)
|
|
assert(pcall(f, t))
|
|
assert(pcall(f, nil))
|
|
assert(not pcall(f, 'hello'))
|
|
print 'Test 62 OK'
|
|
|
|
function x()
|
|
local f: closure = function() end
|
|
f = nil
|
|
local f
|
|
f = 'a'
|
|
return f
|
|
end
|
|
assert(x() == 'a')
|
|
ravi.compile(x)
|
|
assert(x() == 'a')
|
|
print 'Test 63 OK'
|
|
|
|
-- Codegen bug #issue 148
|
|
function x()
|
|
assert(({pcall(function() comp 'x for __result' end)})[2]
|
|
:find'not contain __ prefix')
|
|
end
|
|
check(x, 'GETTABUP_SK', 'NEWTABLE', 'GETTABUP_SK', 'CLOSURE',
|
|
'CALL', 'SETLIST', 'GETI', 'SELF_SK', 'LOADK',
|
|
'CALL', 'CALL', 'RETURN')
|
|
print 'Test 64 OK'
|
|
|
|
x = function()
|
|
local i: integer = @integer 1
|
|
local n: number = @number 1.2
|
|
assert(i == 1 and n == 1.2)
|
|
local j = { 1, 2, 3, 4.2, 5 }
|
|
i = @integer( @integer (j[2]) )
|
|
n = @number( @number (j[4]) )
|
|
assert(i == 2)
|
|
assert(n == 4.2)
|
|
end
|
|
check(x, 'LOADK', 'LOADK', 'GETTABUP_SK', 'EQ_II',
|
|
'JMP', 'EQ_FF', 'JMP', 'LOADBOOL', 'LOADBOOL',
|
|
'CALL', 'NEWTABLE', 'LOADK', 'LOADK', 'LOADK',
|
|
'LOADK', 'LOADK', 'SETLIST', 'GETI', 'TOINT',
|
|
'MOVEI', 'GETI', 'TOFLT', 'MOVEF', 'GETTABUP_SK',
|
|
'EQ_II', 'JMP', 'LOADBOOL', 'LOADBOOL', 'CALL',
|
|
'GETTABUP_SK', 'EQ_FF', 'JMP', 'LOADBOOL', 'LOADBOOL',
|
|
'CALL', 'RETURN')
|
|
x()
|
|
compile(x)
|
|
x()
|
|
print 'Test 65 OK'
|
|
|
|
function x()
|
|
local a,b = 1,2
|
|
local c: integer,d: integer = @integer a, @integer b
|
|
assert(c == a and b == d)
|
|
end
|
|
check(x, 'LOADK', 'LOADK', 'TOINT', 'MOVE',
|
|
'TOINT', 'MOVE', 'GETTABUP_SK', 'EQ', 'JMP',
|
|
'EQ', 'JMP', 'LOADBOOL', 'LOADBOOL', 'CALL',
|
|
'RETURN')
|
|
x()
|
|
compile(x)
|
|
x()
|
|
print 'Test 66 OK'
|
|
|
|
g = { 1, 5.5 }
|
|
local h = { 10, 4.2 }
|
|
function x()
|
|
local a: integer = 11
|
|
local b: number = 6.5
|
|
local c = 6
|
|
a = @integer( g[1] ) + @integer( h[1] ) * @integer( c )
|
|
c = 3.2
|
|
b = @number( g[2] ) * @number( h[2] ) - @number( c )
|
|
return a,b
|
|
end
|
|
y,z = x()
|
|
assert(y == 61 and math.abs(z - 19.9) < 1e-5)
|
|
compile(x)
|
|
y,z = x()
|
|
assert(y == 61 and math.abs(z - 19.9) < 1e-10)
|
|
check(x, 'LOADK', 'LOADK', 'LOADK', 'GETTABUP_SK',
|
|
'GETI', 'TOINT', 'GETTABUP', 'TOINT', 'TOINT',
|
|
'MULII', 'ADDII', 'LOADK', 'GETTABUP_SK', 'GETI',
|
|
'TOFLT', 'GETTABUP', 'TOFLT', 'MULFF', 'TOFLT',
|
|
'SUBFF', 'MOVE', 'MOVE', 'RETURN', 'RETURN')
|
|
print 'Test 67 OK'
|
|
|
|
x=load 'local t: number[] = {}'
|
|
check(x, 'NEW_FARRAY', 'RETURN')
|
|
print 'Test 68 OK'
|
|
|
|
x=load 'local t = @number[] {}'
|
|
check(x, 'NEW_FARRAY', 'RETURN')
|
|
print 'Test 69 OK'
|
|
|
|
x=load 'local t: number[] = @number[] ( @number[] {} )'
|
|
check(x, 'NEW_FARRAY', 'RETURN')
|
|
print 'Test 70 OK'
|
|
|
|
function x()
|
|
return { @integer[] {1,2}, @number[] {42} }
|
|
end
|
|
check(x, 'NEWTABLE', 'NEW_IARRAY', 'LOADK', 'LOADK',
|
|
'SETLIST', 'NEW_FARRAY', 'LOADK', 'SETLIST',
|
|
'SETLIST', 'RETURN', 'RETURN')
|
|
y = x()
|
|
assert(ravitype(y[1]) == 'integer[]')
|
|
assert(ravitype(y[2]) == 'number[]')
|
|
print 'Test 71 OK'
|
|
|
|
function x()
|
|
return @integer[] @integer[] @integer[]{1}
|
|
end
|
|
check(x, 'NEW_IARRAY', 'LOADK', 'SETLIST', 'RETURN', 'RETURN')
|
|
assert(ravitype(x()) == 'integer[]')
|
|
print 'Test 72 OK'
|
|
|
|
function x()
|
|
return nil or @integer[] {1}
|
|
end
|
|
assert(ravitype(x()) == 'integer[]')
|
|
check(x, 'NEW_IARRAY', 'LOADK', 'SETLIST', 'RETURN', 'RETURN')
|
|
print 'Test 73 OK'
|
|
|
|
function x()
|
|
return @number[]( @integer[] {1} and @number[] {42} )
|
|
end
|
|
assert(ravitype(x()) == 'number[]')
|
|
assert(x()[1] == 42.0)
|
|
check(x, 'NEW_IARRAY', 'LOADK', 'SETLIST', 'TEST',
|
|
'JMP', 'NEW_FARRAY', 'LOADK', 'SETLIST', 'TOFARRAY',
|
|
'RETURN', 'RETURN')
|
|
print 'Test 74 OK'
|
|
|
|
function x()
|
|
assert(math.exp(2) == ravi.exp(2))
|
|
assert(math.log(2) == ravi.ln(2))
|
|
assert(math.exp(math.log(2)) == ravi.exp(ravi.ln(2)))
|
|
end
|
|
x()
|
|
compile(x)
|
|
x()
|
|
print 'Test 75 OK'
|
|
|
|
-- Test that % of ints results in int
|
|
function x(a: integer, b:integer)
|
|
return a%b == 0
|
|
end
|
|
check(x, 'TOINT', 'TOINT', 'MOD',
|
|
'EQ_II', 'JMP', 'LOADBOOL', 'LOADBOOL', 'RETURN', 'RETURN')
|
|
assert(x(10,5))
|
|
compile(x)
|
|
assert(x(10,5))
|
|
print 'Test 76 OK'
|
|
|
|
-- Test that ^ applied to numbers results in a number
|
|
function x(a:number, b:number)
|
|
local c:number = a^b
|
|
return c
|
|
end
|
|
check(x, 'TOFLT', 'TOFLT', 'POW', 'RETURN', 'RETURN')
|
|
assert(x(2,3) == 2^3)
|
|
compile(x)
|
|
assert(x(2,3) == 2^3)
|
|
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'
|
|
|
|
|
|
for k,v in pairs(opcodes_coverage)
|
|
do
|
|
print(k, v)
|
|
end
|
|
|
|
-- ravi.dumplua(test_yields_in_metamethods)
|
|
|
|
print 'OK'
|