lua
local k0aux <const> = 0
local k0 <const> = k0aux
local k1 <const> = 1
local k3 <const> = 3
local k6 <const> = k3 + (k3 << k0)
local kFF0 <const> = 0xFF0
local k3_78 <const> = 3.78
local numberVar = 42
local stringVar = "Hello, Lua!"
local booleanVar = true
local tableVar = {1, 2, 3}
local functionVar = function(x) return x * 2 end
-- 2. 条件语句
if numberVar > 50 then
print("Number is greater than 50")
elseif numberVar == 50 then
print("Number is equal to 50")
else
print("Number is less than 50")
end
for i = 1, 5 do
print("Iteration", i)
end
local counter = 0
while counter < 5 do
counter = counter + 1
print("Counter:", counter)
end
function sayHello(name)
print("Hello,", name)
end
-- 5. 函数调用
sayHello("Alice")
local person = {
name = "Bob",
age = 30,
isMale = true
}
print(person.name)
local add = function(a, b)
return a + b
end
print(add(3, 4))
local fruits = {"apple", "banana", "orange"}
for index, fruit in ipairs(fruits) do
print("Fruit", index, "is", fruit)
end
local myModule = {}
function myModule.sayGoodbye(name)
print("Goodbye,", name)
end
return myModule
function fibonacci(n)
local fib = {0, 1}
for i = 3, n do
fib[i] = fib[i - 1] + fib[i - 2]
end
return fib
end
-- 打印前n个斐波那契数
function print_fibonacci(n)
local fib = fibonacci(n)
print("前" .. n .. "个斐波那契数列:")
for i = 1, n do
io.write(fib[i] .. " ")
end
print()
end
-- 调用函数打印前10个斐波那契数
print_fibonacci(10)
function factorial(n)
if n <= 0 then
return 1
else
return n * factorial(n - 1)
end
end
local result = factorial(5)
print("Factorial of 5:", result)
local a = 10
function modifyGlobal()
a = 20
end
function accessGlobal()
print("Global a:", a)
end
modifyGlobal()
accessGlobal()
local largeNumber = 2^100
print("Large number:", largeNumber)
local largeTable = {}
for i = 1, 10^6 do
largeTable[i] = i
end
print("Large table length:", #largeTable)
local longString = ""
for i = 1, 10^5 do
longString = longString .. tostring(i)
end
print("Length of longString:", #longString)
local nestedTable = {
a = 1,
b = {
c = 2,
d = {
e = 3
}
}
}
print("Value of nestedTable.b.d.e:", nestedTable.b.d.e)
function multipleReturns()
return 1, 2, 3
end
local a, b, c = multipleReturns()
print("Values:", a, b, c)
local mixedTable = {
"first",
"second",
key1 = "value1",
key2 = "value2",
key3 = {
key1 = function ()
end,
key2 = 1
}
}
print("Value at index 2:", mixedTable[2])
print("Value of key 'key1':", mixedTable.key1)
print(mixedTable.key3.key1)
print(mixedTable.key3.key2)
print(mixedTable.key3:asd())
local value = 42
if value then
print("Value is truthy")
else
print("Value is falsy")
end
local version = "Lua 5.4"
if _VERSION ~= version then
io.stderr:write("This test suite is for ", version,
", not for ", _VERSION, "\nExiting tests")
return
end
_G.ARG = arg
_soft = rawget(_G, "_soft") or false
_port = rawget(_G, "_port") or false
_nomsg = rawget(_G, "_nomsg") or false
local usertests = rawget(_G, "_U")
if usertests then
_soft = true
_port = true
_nomsg = true
end
debug = nil
if usertests then
T = nil
else
T = rawget(_G, "T")
end
print("\n\tStarting Tests")
do
local random_x, random_y = math.randomseed()
print(string.format("random seeds: %d, %d", random_x, random_y))
end
print("current path:\n****" .. package.path .. "****\n")
local initclock = os.clock()
local lastclock = initclock
local walltime = os.time()
local collectgarbage = collectgarbage
do
local msgs = {}
function Message (m)
if not _nomsg then
print(m)
msgs[#msgs+1] = string.sub(m, 3, -3)
end
end
assert(os.setlocale"C")
local T,print,format,write,assert,type,unpack,floor =
T,print,string.format,io.write,assert,type,table.unpack,math.floor
local function F (m)
local function round (m)
m = m + 0.04999
return format("%.1f", m)
end
if m < 1000 then return m
else
m = m / 1000
if m < 1000 then return round(m).."K"
else
return round(m/1000).."M"
end
end
end
local Cstacklevel
local showmem
if not T then
local max = 0
showmem = function ()
local m = collectgarbage("count") * 1024
max = (m > max) and m or max
print(format(" ---- total memory: %s, max memory: %s ----\n",
F(m), F(max)))
end
Cstacklevel = function () return 0 end -- no info about stack level
else
showmem = function ()
T.checkmemory()
local total, numblocks, maxmem = T.totalmem()
local count = collectgarbage("count")
print(format(
"\n ---- total memory: %s (%.0fK), max use: %s, blocks: %d\n",
F(total), count, F(maxmem), numblocks))
print(format("\t(strings: %d, tables: %d, functions: %d, "..
"\n\tudata: %d, threads: %d)",
T.totalmem"string", T.totalmem"table", T.totalmem"function",
T.totalmem"userdata", T.totalmem"thread"))
end
Cstacklevel = function ()
local _, _, ncalls = T.stacklevel()
return ncalls
end
end
local Cstack = Cstacklevel()
local function report (n) print("\n***** FILE '"..n.."'*****") end
local olddofile = dofile
local dofile = function (n, strip)
showmem()
local c = os.clock()
print(string.format("time: %g (+%g)", c - initclock, c - lastclock))
lastclock = c
report(n)
local f = assert(loadfile(n))
local b = string.dump(f, strip)
f = assert(load(b))
return f()
end
dofile('main.lua')
require"tracegc".start()
report"gc.lua"
local f = assert(loadfile('gc.lua'))
f()
dofile('db.lua')
assert(dofile('calls.lua') == deep and deep)
_G.deep = nil
olddofile('strings.lua')
olddofile('literals.lua')
dofile('tpack.lua')
assert(dofile('attrib.lua') == 27)
dofile('gengc.lua')
assert(dofile('locals.lua') == 5)
dofile('constructs.lua')
dofile('code.lua', true)
if not _G._soft then
report('big.lua')
local f = coroutine.wrap(assert(loadfile('big.lua')))
assert(f() == 'b')
assert(f() == 'a')
end
dofile('cstack.lua')
dofile('nextvar.lua')
dofile('pm.lua')
dofile('utf8.lua')
dofile('api.lua')
assert(dofile('events.lua') == 12)
dofile('vararg.lua')
dofile('closure.lua')
dofile('coroutine.lua')
dofile('goto.lua', true)
dofile('errors.lua')
dofile('math.lua')
dofile('sort.lua', true)
dofile('bitwise.lua')
assert(dofile('verybig.lua', true) == 10); collectgarbage()
dofile('files.lua')
if #msgs > 0 then
local m = table.concat(msgs, "\n ")
warn("#tests not performed:\n ", m, "\n")
end
print("(there should be two warnings now)")
warn("@on")
warn("#This is ", "an expected", " warning")
warn("@off")
warn("******** THIS WARNING SHOULD NOT APPEAR **********")
warn("******** THIS WARNING ALSO SHOULD NOT APPEAR **********")
warn("@on")
warn("#This is", " another one")
assert(debug == nil)
local debug = require "debug"
print(string.format("%d-bit integers, %d-bit floats",
string.packsize("j") * 8, string.packsize("n") * 8))
debug.sethook(function (a) assert(type(a) == 'string') end, "cr")
_G.showmem = showmem
assert(Cstack == Cstacklevel(),
"should be at the same C-stack level it was when started the tests")
end
local _G, showmem, print, format, clock, time, difftime,
assert, open, warn =
_G, showmem, print, string.format, os.clock, os.time, os.difftime,
assert, io.open, warn
local fname = T and "time-debug.txt" or "time.txt"
local lasttime
if not usertests then
local f = io.open(fname)
if f then
lasttime = assert(tonumber(f:read'a'))
f:close();
else
lasttime = nil
end
end
print('cleaning all!!!!')
for n in pairs(_G) do
if not ({___Glob = 1, tostring = 1})[n] then
_G[n] = undef
end
end
collectgarbage()
collectgarbage()
collectgarbage()
collectgarbage()
collectgarbage()
collectgarbage();showmem()
local clocktime = clock() - initclock
walltime = difftime(time(), walltime)
print(format("\n\ntotal time: %.2fs (wall time: %gs)\n", clocktime, walltime))
if not usertests then
lasttime = lasttime or clocktime
local diff = (clocktime - lasttime) / lasttime
local tolerance = 0.05
if (diff >= tolerance or diff <= -tolerance) then
warn(format("#time difference from previous test: %+.1f%%",
diff * 100))
end
assert(open(fname, "w")):write(clocktime):close()
end
print("final OK !!!")
if T==nil then
(Message or print)('\n >>> testC not active: skipping API tests <<<\n')
return
end
local debug = require "debug"
local pack = table.pack
local MEMERRMSG = "not enough memory"
local function tcheck (t1, t2)
assert(t1.n == (t2.n or #t2) + 1)
for i = 2, t1.n do assert(t1[i] == t2[i - 1]) end
end
local function checkerr (msg, f, ...)
local stat, err = pcall(f, ...)
assert(not stat and string.find(err, msg))
end
print('testing C API')
local a = T.testC("pushvalue R; return 1")
assert(a == debug.getregistry())
assert(T.testC("settop 10; absindex -1; return 1") == 10)
assert(T.testC("settop 5; absindex -5; return 1") == 1)
assert(T.testC("settop 10; absindex 1; return 1") == 1)
assert(T.testC("settop 10; absindex R; return 1") < -10)
a = T.d2s(12458954321123.0)
assert(a == string.pack("d", 12458954321123.0))
assert(T.s2d(a) == 12458954321123.0)
local a,b,c = T.testC("pushnum 1; pushnum 2; pushnum 3; return 2")
assert(a == 2 and b == 3 and not c)
local f = T.makeCfunc("pushnum 1; pushnum 2; pushnum 3; return 2")
a,b,c = f()
assert(a == 2 and b == 3 and not c)
a,b,c = T.testC("pushbool 1; pushbool 2; pushbool 0; return 3")
assert(a == b and a == true and c == false)
a,b,c = T.testC"pushbool 0; pushbool 10; pushnil;\
tobool -3; tobool -3; tobool -3; return 3"
assert(a==false and b==true and c==false)
a,b,c = T.testC("gettop; return 2", 10, 20, 30, 40)
assert(a == 40 and b == 5 and not c)
local t = pack(T.testC("settop 5; return *", 2, 3))
tcheck(t, {n=4,2,3})
t = pack(T.testC("settop 0; settop 15; return 10", 3, 1, 23))
assert(t.n == 10 and t[1] == nil and t[10] == nil)
t = pack(T.testC("remove -2; return *", 2, 3, 4))
tcheck(t, {n=2,2,4})
t = pack(T.testC("insert -1; return *", 2, 3))
tcheck(t, {n=2,2,3})
t = pack(T.testC("insert 3; return *", 2, 3, 4, 5))
tcheck(t, {n=4,2,5,3,4})
t = pack(T.testC("replace 2; return *", 2, 3, 4, 5))
tcheck(t, {n=3,5,3,4})
t = pack(T.testC("replace -2; return *", 2, 3, 4, 5))
tcheck(t, {n=3,2,3,5})
t = pack(T.testC("remove 3; return *", 2, 3, 4, 5))
tcheck(t, {n=3,2,4,5})
t = pack(T.testC("copy 3 4; return *", 2, 3, 4, 5))
tcheck(t, {n=4,2,3,3,5})
t = pack(T.testC("copy -3 -1; return *", 2, 3, 4, 5))
tcheck(t, {n=4,2,3,4,3})
do
local t = {10, 20, 30, 40, 50, 60}
for i = -6, 6 do
local s = string.format("rotate 2 %d; return 7", i)
local t1 = pack(T.testC(s, 10, 20, 30, 40, 50, 60))
tcheck(t1, t)
table.insert(t, 1, table.remove(t))
end
t = pack(T.testC("rotate -2 1; return *", 10, 20, 30, 40))
tcheck(t, {10, 20, 40, 30})
t = pack(T.testC("rotate -2 -1; return *", 10, 20, 30, 40))
tcheck(t, {10, 20, 40, 30})
t = pack(T.testC("rotate -1 0; return *", 10, 20, 30, 40))
tcheck(t, {10, 20, 30, 40})
t = pack(T.testC("rotate -1 1; return *", 10, 20, 30, 40))
tcheck(t, {10, 20, 30, 40})
t = pack(T.testC("rotate 5 -1; return *", 10, 20, 30, 40))
tcheck(t, {10, 20, 30, 40})
end
T.testC([[
warningC "#This shold be a"
warningC " single "
warning "warning"
warningC "#This should be "
warning "another one"
]])
do
local f = T.makeCfunc[[
getglobal error
pushstring bola
pcall 1 1 1 # call 'error' with given handler
pushstatus
return 2 # return error message and status
]]
local msg, st = f(string.upper)
assert(st == "ERRRUN" and msg == "BOLA")
local msg, st = f(string.len)
assert(st == "ERRRUN" and msg == 4)
end
t = pack(T.testC("insert 3; pushvalue 3; remove 3; pushvalue 2; remove 2; \
insert 2; pushvalue 1; remove 1; insert 1; \
insert -2; pushvalue -2; remove -3; return *",
2, 3, 4, 5, 10, 40, 90))
tcheck(t, {n=7,2,3,4,5,10,40,90})
t = pack(T.testC("concat 5; return *", "alo", 2, 3, "joao", 12))
tcheck(t, {n=1,"alo23joao12"})
t = pack(T.testC("call 2,-1; return *",
function (a,b) return 1,2,3,4,a,b end, "alo", "joao"))
tcheck(t, {n=6,1,2,3,4,"alo", "joao"})
do
local a = {}
for i=1,1000 do a[i] = true end; a[999] = 10
local b = T.testC([[pcall 1 -1 0; pop 1; tostring -1; return 1]],
table.unpack, a)
assert(b == "10")
end
_G.AA = 14; _G.BB = "a31"
local a = {T.testC[[
getglobal AA;
getglobal BB;
getglobal BB;
setglobal AA;
return *
]]}
assert(a[2] == 14 and a[3] == "a31" and a[4] == nil and _G.AA == "a31")
_G.AA, _G.BB = nil
assert(T.testC("pushnum 10; pushnum 20; arith /; return 1") == 0.5)
assert(T.testC("pushnum 10; pushnum 20; arith -; return 1") == -10)
assert(T.testC("pushnum 10; pushnum -20; arith *; return 1") == -200)
assert(T.testC("pushnum 10; pushnum 3; arith ^; return 1") == 1000)
assert(T.testC("pushnum 10; pushstring 20; arith /; return 1") == 0.5)
assert(T.testC("pushstring 10; pushnum 20; arith -; return 1") == -10)
assert(T.testC("pushstring 10; pushstring -20; arith *; return 1") == -200)
assert(T.testC("pushstring 10; pushstring 3; arith ^; return 1") == 1000)
assert(T.testC("arith /; return 1", 2, 0) == 10.0/0)
a = T.testC("pushnum 10; pushint 3; arith \\; return 1")
assert(a == 3.0 and math.type(a) == "float")
a = T.testC("pushint 10; pushint 3; arith \\; return 1")
assert(a == 3 and math.type(a) == "integer")
a = assert(T.testC("pushint 10; pushint 3; arith +; return 1"))
assert(a == 13 and math.type(a) == "integer")
a = assert(T.testC("pushnum 10; pushint 3; arith +; return 1"))
assert(a == 13 and math.type(a) == "float")
a,b,c = T.testC([[pushnum 1;
pushstring 10; arith _;
pushstring 5; return 3]])
assert(a == 1 and b == -10 and c == "5")
local mt = {
__add = function (a,b) return setmetatable({a[1] + b[1]}, mt) end,
__mod = function (a,b) return setmetatable({a[1] % b[1]}, mt) end,
__unm = function (a) return setmetatable({a[1]* 2}, mt) end}
a,b,c = setmetatable({4}, mt),
setmetatable({8}, mt),
setmetatable({-3}, mt)
local x,y,z = T.testC("arith +; return 2", 10, a, b)
assert(x == 10 and y[1] == 12 and z == nil)
assert(T.testC("arith %; return 1", a, c)[1] == 4%-3)
assert(T.testC("arith _; arith +; arith %; return 1", b, a, c)[1] ==
8 % (4 + (-3)*2))
checkerr("divide by zero", T.testC, "arith \\", 10, 0)
checkerr("%%0", T.testC, "arith %", 10, 0)
assert(T.testC("compare LT 2 5, return 1", 3, 2, 2, 4, 2, 2))
assert(T.testC("compare LE 2 5, return 1", 3, 2, 2, 4, 2, 2))
assert(not T.testC("compare LT 3 4, return 1", 3, 2, 2, 4, 2, 2))
assert(T.testC("compare LE 3 4, return 1", 3, 2, 2, 4, 2, 2))
assert(T.testC("compare LT 5 2, return 1", 4, 2, 2, 3, 2, 2))
assert(not T.testC("compare LT 2 -3, return 1", "4", "2", "2", "3", "2", "2"))
assert(not T.testC("compare LT -3 2, return 1", "3", "2", "2", "4", "2", "2"))
assert(not T.testC("compare LT 1 4, return 1"))
assert(not T.testC("compare LE 9 1, return 1"))
assert(not T.testC("compare EQ 9 9, return 1"))
local b = {__lt = function (a,b) return a[1] < b[1] end}
local a1,a3,a4 = setmetatable({1}, b),
setmetatable({3}, b),
setmetatable({4}, b)
assert(T.testC("compare LT 2 5, return 1", a3, 2, 2, a4, 2, 2))
assert(T.testC("compare LE 2 5, return 1", a3, 2, 2, a4, 2, 2))
assert(T.testC("compare LT 5 -6, return 1", a4, 2, 2, a3, 2, 2))
a,b = T.testC("compare LT 5 -6, return 2", a1, 2, 2, a3, 2, 20)
assert(a == 20 and b == false)
a,b = T.testC("compare LE 5 -6, return 2", a1, 2, 2, a3, 2, 20)
assert(a == 20 and b == false)
a,b = T.testC("compare LE 5 -6, return 2", a1, 2, 2, a1, 2, 20)
assert(a == 20 and b == true)
do
local mt = {__lt = function (a,b) return a[1] < b[1] end,
__le = function (a,b) return a[1] <= b[1] end,
__eq = function (a,b) return a[1] == b[1] end}
local function O (x)
return setmetatable({x}, mt)
end
local a, b = T.testC("compare LT 2 3; pushint 10; return 2", O(1), O(2))
assert(a == true and b == 10)
local a, b = T.testC("compare LE 2 3; pushint 10; return 2", O(3), O(2))
assert(a == false and b == 10)
local a, b = T.testC("compare EQ 2 3; pushint 10; return 2", O(3), O(3))
assert(a == true and b == 10)
end
local t = setmetatable({x = 20}, {__len = function (t) return t.x end})
a,b,c = T.testC([[
len 2;
Llen 2;
objsize 2;
return 3
]], t)
assert(a == 20 and b == 20 and c == 0)
t.x = "234"; t[1] = 20
a,b,c = T.testC([[
len 2;
Llen 2;
objsize 2;
return 3
]], t)
assert(a == "234" and b == 234 and c == 1)
t.x = print; t[1] = 20
a,c = T.testC([[
len 2;
objsize 2;
return 2
]], t)
assert(a == print and c == 1)
a = setmetatable({x="u"}, {__concat = function (a,b) return a.x..'.'..b.x end})
x,y = T.testC([[
pushnum 5
pushvalue 2;
pushvalue 2;
concat 2;
pushvalue -2;
return 2;
]], a, a)
assert(x == a..a and y == 5)
assert(T.testC("concat 0; return 1") == "")
assert(T.testC("concat 1; return 1", "xuxu") == "xuxu")
local function B (x) return x and 1 or 0 end
local function count (x, n)
n = n or 2
local prog = [[
isnumber %d;
isstring %d;
isfunction %d;
iscfunction %d;
istable %d;
isuserdata %d;
isnil %d;
isnull %d;
return 8
]]
prog = string.format(prog, n, n, n, n, n, n, n, n)
local a,b,c,d,e,f,g,h = T.testC(prog, x)
return B(a)+B(b)+B(c)+B(d)+B(e)+B(f)+B(g)+(100*B(h))
end
assert(count(3) == 2)
assert(count('alo') == 1)
assert(count('32') == 2)
assert(count({}) == 1)
assert(count(print) == 2)
assert(count(function () end) == 1)
assert(count(nil) == 1)
assert(count(io.stdin) == 1)
assert(count(nil, 15) == 100)
local function to (s, x, n)
n = n or 2
return T.testC(string.format("%s %d; return 1", s, n), x)
end
local null = T.pushuserdata(0)
local hfunc = string.gmatch("", "")
assert(debug.getupvalue(hfunc, 1))
assert(to("tostring", {}) == nil)
assert(to("tostring", "alo") == "alo")
assert(to("tostring", 12) == "12")
assert(to("tostring", 12, 3) == nil)
assert(to("objsize", {}) == 0)
assert(to("objsize", {1,2,3}) == 3)
assert(to("objsize", "alo\0\0a") == 6)
assert(to("objsize", T.newuserdata(0)) == 0)
assert(to("objsize", T.newuserdata(101)) == 101)
assert(to("objsize", 124) == 0)
assert(to("objsize", true) == 0)
assert(to("tonumber", {}) == 0)
assert(to("tonumber", "12") == 12)
assert(to("tonumber", "s2") == 0)
assert(to("tonumber", 1, 20) == 0)
assert(to("topointer", 10) == null)
assert(to("topointer", true) == null)
assert(to("topointer", nil) == null)
assert(to("topointer", "abc") ~= null)
assert(to("topointer", string.rep("x", 10)) ==
to("topointer", string.rep("x", 10)))
do
local s1 = string.rep("x", 300)
local s2 = string.rep("x", 300)
assert(to("topointer", s1) ~= to("topointer", s2))
end
assert(to("topointer", T.pushuserdata(20)) ~= null)
assert(to("topointer", io.read) ~= null)
assert(to("topointer", hfunc) ~= null)
assert(to("topointer", function () end) ~= null)
assert(to("topointer", io.stdin) ~= null)
assert(to("func2num", 20) == 0)
assert(to("func2num", T.pushuserdata(10)) == 0)
assert(to("func2num", io.read) ~= 0)
assert(to("func2num", hfunc) ~= 0)
a = to("tocfunction", math.deg)
assert(a(3) == math.deg(3) and a == math.deg)
print("testing panic function")
do
assert(T.checkpanic("pushstring hi; error") == "hi")
assert(T.checkpanic("pushstring hi; error;",
[[checkstack 5 XX
pushstring ' alo'
pushstring ' mundo'
concat 3]]) == "hi alo mundo")
assert(T.checkpanic("loadstring 4") ==
"bad argument #4 (string expected, got no value)")
T.totalmem(T.totalmem()+10000)
assert(T.checkpanic("newuserdata 20000") == MEMERRMSG)
T.totalmem(0)
if not _soft then
local msg = T.checkpanic[[
pushstring "function f() f() end"
loadstring -1; call 0 0
getglobal f; call 0 0
]]
assert(string.find(msg, "stack overflow"))
end
assert(T.checkpanic([[
pushstring "return {__close = function () Y = 'ho'; end}"
newtable
loadstring -2
call 0 1
setmetatable -2
toclose -1
pushstring "hi"
error
]],
[[
getglobal Y
concat 2 # concat original error with global Y
]]) == "hiho")
end
if not _soft then
print("testing stack overflow")
collectgarbage("stop")
checkerr("XXXX", T.testC, "checkstack 1000023 XXXX")
checkerr("^stack overflow$", T.testC, "checkstack 1000023 ''")
local s = string.rep("pushnil;checkstack 1 XX;", 1000000)
checkerr("overflow", T.testC, s)
collectgarbage("restart")
print'+'
end
local lim = _soft and 500 or 12000
local prog = {"checkstack " .. (lim * 2 + 100) .. "msg", "newtable"}
for i = 1,lim do
prog[#prog + 1] = "pushnum " .. i
prog[#prog + 1] = "pushnum " .. i * 10
end
prog[#prog + 1] = "rawgeti R 2"
prog[#prog + 1] = "insert " .. -(2*lim + 2)
for i = 1,lim do
prog[#prog + 1] = "settable " .. -(2*(lim - i + 1) + 1)
end
prog[#prog + 1] = "return 2"
prog = table.concat(prog, ";")
local g, t = T.testC(prog)
assert(g == _G)
for i = 1,lim do assert(t[i] == i*10); t[i] = undef end
assert(next(t) == nil)
prog, g, t = nil
a = T.testC([[
loadstring 2; pcall 0 1 0;
pushvalue 3; insert -2; pcall 1 1 0;
pcall 0 0 0;
return 1
]], "XX=150", function (a) assert(a==nil); return 3 end)
assert(type(a) == 'string' and XX == 150)
_G.XX = nil
local function check3(p, ...)
local arg = {...}
assert(#arg == 3)
assert(string.find(arg[3], p))
end
check3(":1:", T.testC("loadstring 2; return *", "x="))
check3("%.", T.testC("loadfile 2; return *", "."))
check3("xxxx", T.testC("loadfile 2; return *", "xxxx"))
local function checkerrnopro (code, msg)
local th = coroutine.create(function () end)
local stt, err = pcall(T.testC, th, code)
assert(not stt and string.find(err, msg))
end
if not _soft then
collectgarbage("stop")
checkerrnopro("pushnum 3; call 0 0", "attempt to call")
print"testing stack overflow in unprotected thread"
function F () F() end
checkerrnopro("getglobal 'F'; call 0 0;", "stack overflow")
F = nil
collectgarbage("restart")
end
print"+"
do
local a = {}
local a1 = T.testC("rawsetp 2 1; return 1", a, 20)
assert(a == a1)
assert(a[T.pushuserdata(1)] == 20)
local a1, res = T.testC("rawgetp -1 1; return 2", a)
assert(a == a1 and res == 20)
end
do
local a = {}
a[a] = 10
local prog = "gettable -1; return *"
local res = {T.testC(prog, a)}
assert(#res == 2 and res[1] == prog and res[2] == 10)
local prog = "settable -2; return *"
local res = {T.testC(prog, a, 20)}
assert(a[a] == 20)
assert(#res == 1 and res[1] == prog)
a[a] = 10
local prog = "rawget -1; return *"
local res = {T.testC(prog, a)}
assert(#res == 2 and res[1] == prog and res[2] == 10)
local prog = "rawset -2; return *"
local res = {T.testC(prog, a, 20)}
assert(a[a] == 20)
assert(#res == 1 and res[1] == prog)
local prog = "rawset -1; return *"
local res = {T.testC(prog, 30, a)}
assert(a[30] == a)
assert(#res == 1 and res[1] == prog)
local prog = "settable -1; return *"
local res = {T.testC(prog, 40, a)}
assert(a[40] == a)
assert(#res == 1 and res[1] == prog)
local prog = "rawseti -1 100; return *"
local res = {T.testC(prog, a)}
assert(a[100] == a)
assert(#res == 1 and res[1] == prog)
local prog = "seti -1 200; return *"
local res = {T.testC(prog, a)}
assert(a[200] == a)
assert(#res == 1 and res[1] == prog)
end
a = {x=0, y=12}
x, y = T.testC("gettable 2; pushvalue 4; gettable 2; return 2",
a, 3, "y", 4, "x")
assert(x == 0 and y == 12)
T.testC("settable -5", a, 3, 4, "x", 15)
assert(a.x == 15)
a[a] = print
x = T.testC("gettable 2; return 1", a)
assert(x == print)
T.testC("settable 2", a, "x")
assert(a[a] == "x")
b = setmetatable({p = a}, {})
getmetatable(b).__index = function (t, i) return t.p[i] end
local k, x = T.testC("gettable 3, return 2", 4, b, 20, 35, "x")
assert(x == 15 and k == 35)
k = T.testC("getfield 2 y, return 1", b)
assert(k == 12)
getmetatable(b).__index = function (t, i) return a[i] end
getmetatable(b).__newindex = function (t, i,v ) a[i] = v end
y = T.testC("insert 2; gettable -5; return 1", 2, 3, 4, "y", b)
assert(y == 12)
k = T.testC("settable -5, return 1", b, 3, 4, "x", 16)
assert(a.x == 16 and k == 4)
a[b] = 'xuxu'
y = T.testC("gettable 2, return 1", b)
assert(y == 'xuxu')
T.testC("settable 2", b, 19)
assert(a[b] == 19)
do
local t = {_012345678901234567890123456789012345678901234567890123456789 = 32}
local a = T.testC([[
getfield 2 _012345678901234567890123456789012345678901234567890123456789
return 1
]], t)
assert(a == 32)
local a = T.testC([[
pushnum 33
setglobal _012345678901234567890123456789012345678901234567890123456789
]])
assert(_012345678901234567890123456789012345678901234567890123456789 == 33)
_012345678901234567890123456789012345678901234567890123456789 = nil
end
a = {}
t = pack(T.testC("next; return *", a, nil))
tcheck(t, {n=1,a})
a = {a=3}
t = pack(T.testC("next; return *", a, nil))
tcheck(t, {n=3,a,'a',3})
t = pack(T.testC("next; pop 1; next; return *", a, nil))
tcheck(t, {n=1,a})
do
local A = T.testC[[ pushnum 10; pushnum 20; pushcclosure 2; return 1]]
t, b, c = A([[pushvalue U0; pushvalue U1; pushvalue U2; return 3]])
assert(b == 10 and c == 20 and type(t) == 'table')
a, b = A([[tostring U3; tonumber U4; return 2]])
assert(a == nil and b == 0)
A([[pushnum 100; pushnum 200; replace U2; replace U1]])
b, c = A([[pushvalue U1; pushvalue U2; return 2]])
assert(b == 100 and c == 200)
A([[replace U2; replace U1]], {x=1}, {x=2})
b, c = A([[pushvalue U1; pushvalue U2; return 2]])
assert(b.x == 1 and c.x == 2)
T.checkmemory()
end
assert(T.testC[[isnull U1; return 1]] == true)
assert(T.testC[[isnull U100; return 1]] == true)
assert(T.testC[[pushvalue U1; return 1]] == nil)
local f = T.testC[[ pushnum 10; pushnum 20; pushcclosure 2; return 1]]
assert(T.upvalue(f, 1) == 10 and
T.upvalue(f, 2) == 20 and
T.upvalue(f, 3) == nil)
T.upvalue(f, 2, "xuxu")
assert(T.upvalue(f, 2) == "xuxu")
do
local A = "checkstack 300 msg;" ..
string.rep("pushnum 10;", 255) ..
"pushcclosure 255; return 1"
A = T.testC(A)
for i=1,255 do
assert(A(("pushvalue U%d; return 1"):format(i)) == 10)
end
assert(A("isnull U256; return 1"))
assert(not A("isnil U256; return 1"))
end
checkerr("got number", debug.setuservalue, 3, {})
checkerr("got nil", debug.setuservalue, nil, {})
checkerr("got light userdata", debug.setuservalue, T.pushuserdata(1), {})
local b = T.newuserdata(0, 10)
for i = 1, 10 do
local v, p = debug.getuservalue(b, i)
assert(v == nil and p)
end
do
local v, p = debug.getuservalue(b, -2)
assert(v == nil and not p)
local v, p = debug.getuservalue(b, 11)
assert(v == nil and not p)
end
local t = {true, false, 4.56, print, {}, b, "XYZ"}
for k, v in ipairs(t) do
debug.setuservalue(b, v, k)
end
for k, v in ipairs(t) do
local v1, p = debug.getuservalue(b, k)
assert(v1 == v and p)
end
assert(not debug.getuservalue(4))
debug.setuservalue(b, function () return 10 end, 10)
collectgarbage()
assert(debug.getuservalue(b, 10)() == 10)
debug.setuservalue(b, 134)
collectgarbage()
assert(debug.getuservalue(b) == 134)
do
local oldmode = collectgarbage("incremental")
T.gcstate("atomic")
assert(T.gccolor(b) == "black")
debug.setuservalue(b, {x = 100})
T.gcstate("pause")
assert(debug.getuservalue(b).x == 100)
collectgarbage(oldmode)
end
for i = 1, 1000 do
local bb = T.newuserdata(0, 1)
debug.setuservalue(bb, b)
b = bb
end
collectgarbage()
for i = 1, 1000 do
b = debug.getuservalue(b)
end
assert(debug.getuservalue(b).x == 100)
b = nil
local i = T.ref{}
T.unref(i)
assert(T.ref{} == i)
local Arr = {}
local Lim = 100
for i=1,Lim do
Arr[i] = T.ref({})
end
assert(T.ref(nil) == -1 and T.getref(-1) == nil)
T.unref(-1); T.unref(-1)
for i=1,Lim do
T.unref(Arr[i])
end
local function printlocks ()
local f = T.makeCfunc("gettable R; return 1")
local n = f("n")
print("n", n)
for i=0,n do
print(i, f(i))
end
end
for i=1,Lim do
Arr[i] = T.ref({})
end
for i=1,Lim,2 do
T.unref(Arr[i])
end
assert(type(T.getref(Arr[2])) == 'table')
assert(T.getref(-1) == nil)
a = T.ref({})
collectgarbage()
assert(type(T.getref(a)) == 'table')
local tt = {}
local cl = {n=0}
A = nil; B = nil
local F
F = function (x)
local udval = T.udataval(x)
table.insert(cl, udval)
local d = T.newuserdata(100)
d = nil
assert(debug.getmetatable(x).__gc == F)
assert(load("table.insert({}, {})"))()
assert(not collectgarbage())
local dummy = {}
if A ~= nil then
assert(type(A) == "userdata")
assert(T.udataval(A) == B)
debug.getmetatable(A)
end
A = x
B = udval
return 1,2,3
end
tt.__gc = F
do
collectgarbage();
collectgarbage();
local x = collectgarbage("count");
local a = T.newuserdata(5001)
assert(T.testC("objsize 2; return 1", a) == 5001)
assert(collectgarbage("count") >= x+4)
a = nil
collectgarbage();
assert(collectgarbage("count") <= x+1)
x = collectgarbage("count")
collectgarbage("stop")
for i=1,1000 do T.newuserdata(0) end
assert(collectgarbage("count") > x+10)
collectgarbage()
assert(collectgarbage("count") <= x+1)
collectgarbage()
x = collectgarbage("count")
collectgarbage("stop")
a = {__gc = function () end}
for i=1,1000 do debug.setmetatable(T.newuserdata(0), a) end
assert(collectgarbage("count") >= x+10)
collectgarbage()
assert(collectgarbage("count") >= x+10)
collectgarbage()
assert(collectgarbage("count") <= x+1)
collectgarbage("restart")
end
collectgarbage("stop")
a = T.newuserdata(0); debug.setmetatable(a, tt); local na = T.udataval(a)
b = T.newuserdata(0); debug.setmetatable(b, tt); local nb = T.udataval(b)
c = T.newuserdata(0); debug.setmetatable(c, tt); local nc = T.udataval(c)
x = T.newuserdata(4)
y = T.newuserdata(0)
checkerr("FILE%* expected, got userdata", io.input, a)
checkerr("FILE%* expected, got userdata", io.input, x)
assert(debug.getmetatable(x) == nil and debug.getmetatable(y) == nil)
local d = T.ref(a);
local e = T.ref(b);
local f = T.ref(c);
t = {T.getref(d), T.getref(e), T.getref(f)}
assert(t[1] == a and t[2] == b and t[3] == c)
t=nil; a=nil; c=nil;
T.unref(e); T.unref(f)
collectgarbage()
assert(#cl == 1 and cl[1] == nc)
x = T.getref(d)
assert(type(x) == 'userdata' and debug.getmetatable(x) == tt)
x =nil
tt.b = b
tt=nil
A = nil
b = nil
T.unref(d);
local n5 = T.newuserdata(0)
debug.setmetatable(n5, {__gc=F})
n5 = T.udataval(n5)
collectgarbage()
assert(#cl == 4)
assert(cl[2] == n5 and cl[3] == nb and cl[4] == na)
collectgarbage"restart"
a, na = {}, {}
for i=30,1,-1 do
a[i] = T.newuserdata(0)
debug.setmetatable(a[i], {__gc=F})
na[i] = T.udataval(a[i])
end
cl = {}
a = nil; collectgarbage()
assert(#cl == 30)
for i=1,30 do assert(cl[i] == na[i]) end
na = nil
for i=2,Lim,2 do
T.unref(Arr[i])
end
x = T.newuserdata(41); debug.setmetatable(x, {__gc=F})
assert(T.testC("objsize 2; return 1", x) == 41)
cl = {}
a = {[x] = 1}
x = T.udataval(x)
collectgarbage()
assert(#cl == 0)
for n in pairs(a) do a[n] = undef end
collectgarbage()
assert(#cl == 1 and cl[1] == x)
assert(T.testC("compare EQ 2 4; return 1", print, 1, print, 20))
assert(T.testC("compare EQ 3 2; return 1", 'alo', "alo"))
assert(T.testC("compare EQ 2 3; return 1", nil, nil))
assert(not T.testC("compare EQ 2 3; return 1", {}, {}))
assert(not T.testC("compare EQ 2 3; return 1"))
assert(not T.testC("compare EQ 2 3; return 1", 3))
do
local map = {}
local t = {__eq = function (a,b) return map[a] == map[b] end}
local function f(x)
local u = T.newuserdata(0)
debug.setmetatable(u, t)
map[u] = x
return u
end
assert(f(10) == f(10))
assert(f(10) ~= f(11))
assert(T.testC("compare EQ 2 3; return 1", f(10), f(10)))
assert(not T.testC("compare EQ 2 3; return 1", f(10), f(20)))
t.__eq = nil
assert(f(10) ~= f(10))
end
print'+'
_G.TT = {}
T.sethook([[
# set a line hook after 3 count hooks
sethook 4 0 '
getglobal TT;
pushvalue -3; append -2
pushvalue -2; append -2
']], "c", 3)
local a = 1
a = 1
a = 1
a = 1
a = 1
debug.sethook()
local t = _G.TT
assert(t[1] == "line")
local line = t[2]
assert(t[3] == "line" and t[4] == line + 1)
assert(t[5] == "line" and t[6] == line + 2)
assert(t[7] == nil)
_G.TT = nil
do
warn("@off")
collectgarbage("stop")
local a = {}
for i=1,20 do
a[i] = T.newuserdata(i)
end
for i=1,20,2 do
debug.setmetatable(a[i],
{__gc = function (x) error("@expected error in gc") end})
end
for i=2,20,2 do
debug.setmetatable(a[i], {__gc = function (x) load("A=A+1")() end})
end
a = nil
_G.A = 0
collectgarbage()
assert(A == 10)
collectgarbage("restart")
warn("@on")
end
_G.A = nil
do
local a = {}; local lim = 30
for i=0,lim do a[i] = T.pushuserdata(i) end
for i=0,lim do assert(T.udataval(a[i]) == i) end
for i=0,lim do assert(T.pushuserdata(i) == a[i]) end
for i=0,lim do a[a[i]] = i end
for i=0,lim do a[T.pushuserdata(i)] = i end
assert(type(tostring(a[1])) == "string")
end
T.closestate(T.newstate());
L1 = T.newstate()
assert(L1)
assert(T.doremote(L1, "X='a'; return 'a'") == 'a')
assert(#pack(T.doremote(L1, "function f () return 'alo', 3 end; f()")) == 0)
a, b = T.doremote(L1, "return f()")
assert(a == 'alo' and b == '3')
T.doremote(L1, "_ERRORMESSAGE = nil")
a, b, c = T.doremote(L1, "return sin(1)")
assert(a == nil and c == 2)
a, b, c = T.doremote(L1, "return a+")
assert(a == nil and c == 3 and type(b) == "string")
T.loadlib(L1)
a, b, c = T.doremote(L1, [[
string = require'string'
a = require'_G'; assert(a == _G and require("_G") == a)
io = require'io'; assert(type(io.read) == "function")
assert(require("io") == io)
a = require'table'; assert(type(a.insert) == "function")
a = require'debug'; assert(type(a.getlocal) == "function")
a = require'math'; assert(type(a.sin) == "function")
return string.sub('okinama', 1, 2)
]])
assert(a == "ok")
T.closestate(L1);
L1 = T.newstate()
T.loadlib(L1)
T.doremote(L1, "a = {}")
T.testC(L1, [[getglobal "a"; pushstring "x"; pushint 1;
settable -3]])
assert(T.doremote(L1, "return a.x") == "1")
T.closestate(L1)
L1 = nil
print('+')
print"testing to-be-closed variables"
do
local openresource = {}
local function newresource ()
local x = setmetatable({10}, {__close = function(y)
assert(openresource[#openresource] == y)
openresource[#openresource] = nil
y[1] = y[1] + 1
end})
openresource[#openresource + 1] = x
return x
end
local a, b = T.testC([[
call 0 1 # create resource
pushnil
toclose -2 # mark call result to be closed
toclose -1 # mark nil to be closed (will be ignored)
return 2
]], newresource)
assert(a[1] == 11 and b == nil)
assert(#openresource == 0)
local a = {T.testC([[
call 0 1 # create resource
toclose 2 # mark it to be closed
return 2
]], newresource)}
assert(type(a[1]) == "string" and a[2][1] == 11)
assert(#openresource == 0)
local a, b = pcall(T.makeCfunc[[
call 0 1 # create resource
toclose -1 # mark it to be closed
error # resource is the error object
]], newresource)
assert(a == false and b[1] == 11)
assert(#openresource == 0)
local a, b = pcall(T.makeCfunc[[
newtable # create non-closable object
toclose -1 # mark it to be closed (should raise an error)
abort # will not be executed
]])
assert(a == false and
string.find(b, "non%-closable value"))
local function check (n)
assert(#openresource == n)
end
-- closing resources with 'closeslot'
_ENV.xxx = true
local a = T.testC([[
pushvalue 2 # stack: S, NR, CH, NR
call 0 1 # create resource; stack: S, NR, CH, R
toclose -1 # mark it to be closed
pushvalue 2 # stack: S, NR, CH, R, NR
call 0 1 # create another resource; stack: S, NR, CH, R, R
toclose -1 # mark it to be closed
pushvalue 3 # stack: S, NR, CH, R, R, CH
pushint 2 # there should be two open resources
call 1 0 # stack: S, NR, CH, R, R
closeslot -1 # close second resource
pushvalue 3 # stack: S, NR, CH, R, R, CH
pushint 1 # there should be one open resource
call 1 0 # stack: S, NR, CH, R, R
closeslot 4
setglobal "xxx" # previous op. erased the slot
pop 1 # pop other resource from the stack
pushint *
return 1 # return stack size
]], newresource, check)
assert(a == 3 and _ENV.xxx == nil)
local a = T.testC([[
pushvalue 2 # stack: S, NR, CH, NR
call 0 1 # create resource; stack: S, NR, CH, R
toclose -1 # mark it to be closed
pushvalue 2 # stack: S, NR, CH, R, NR
call 0 1 # create another resource; stack: S, NR, CH, R, R
toclose -1 # mark it to be closed
pushvalue 3 # stack: S, NR, CH, R, R, CH
pushint 2 # there should be two open resources
call 1 0 # stack: S, NR, CH, R, R
pop 1 # pop second resource
pushvalue 3 # stack: S, NR, CH, R, CH
pushint 1 # there should be one open resource
call 1 0 # stack: S, NR, CH, R
pop 1 # pop other resource from the stack
pushvalue 3 # stack: S, NR, CH, CH
pushint 0 # there should be no open resources
call 1 0 # stack: S, NR, CH
pushint *
return 1 # return stack size
]], newresource, check)
assert(a == 3)
local a, b = pcall(T.makeCfunc[[
pushint 32
toclose -1
]])
assert(not a and string.find(b, "(C temporary)"))
end
print("memory-allocation errors")
checkerr("block too big", T.newuserdata, math.maxinteger)
collectgarbage()
local f = load"local a={}; for i=1,100000 do a[i]=i end"
T.alloccount(10)
checkerr(MEMERRMSG, f)
T.alloccount()
local function testbytes (s, f)
collectgarbage()
local M = T.totalmem()
local oldM = M
local a,b = nil
while true do
collectgarbage(); collectgarbage()
T.totalmem(M)
a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
T.totalmem(0)
if a and b == "OK" then break end
if b ~= "OK" and b ~= MEMERRMSG then
error(a, 0)
end
M = M + 7
end
print(string.format("minimum memory for %s: %d bytes", s, M - oldM))
return a
end
-- test memory errors; increase limit for number of allocations one
-- by one, so that we get memory errors in all allocations of a given
-- task, until there is enough allocations to complete the task without
-- errors.
local function testalloc (s, f)
collectgarbage()
local M = 0
local a,b = nil
while true do
collectgarbage(); collectgarbage()
T.alloccount(M)
a, b = T.testC("pcall 0 1 0; pushstatus; return 2", f)
T.alloccount()
if a and b == "OK" then break end
if b ~= "OK" and b ~= MEMERRMSG then
error(a, 0)
end
M = M + 1
end
print(string.format("minimum allocations for %s: %d allocations", s, M))
return a
end
local function testamem (s, f)
testalloc(s, f)
return testbytes(s, f)
end
-- doing nothing
b = testamem("doing nothing", function () return 10 end)
assert(b == 10)
testamem("state creation", function ()
local st = T.newstate()
if st then T.closestate(st) end
return st
end)
testamem("empty-table creation", function ()
return {}
end)
testamem("string creation", function ()
return "XXX" .. "YYY"
end)
testamem("coroutine creation", function()
return coroutine.create(print)
end)
testamem("to-be-closed variables", function()
local flag
do
local x <close> =
setmetatable({}, {__close = function () flag = true end})
flag = false
local x = {}
end
return flag
end)
local mt = T.testC("rawgeti R 1; return 1")
assert(type(mt) == "thread" and coroutine.running() == mt)
local function expand (n,s)
if n==0 then return "" end
local e = string.rep("=", n)
return string.format("T.doonnewstack([%s[ %s;\n collectgarbage(); %s]%s])\n",
e, s, expand(n-1,s), e)
end
G=0; collectgarbage(); a =collectgarbage("count")
load(expand(20,"G=G+1"))()
assert(G==20); collectgarbage();
G = nil
testamem("running code on new thread", function ()
return T.doonnewstack("local x=1") == 0
end)
testamem("loadstring", function ()
return load("x=1")
end)
local testprog = [[
local function foo () return end
local t = {"x"}
AA = "aaa"
for i = 1, #t do AA = AA .. t[i] end
return true
]]
_G.AA = nil
local t =os.tmpname()
local f = assert(io.open(t, "w"))
f:write(testprog)
f:close()
testamem("dofile", function ()
local a = loadfile(t)
return a and a()
end)
assert(os.remove(t))
assert(_G.AA == "aaax")
testamem("gsub", function ()
local a, b = string.gsub("alo alo", "(a)", function (x) return x..'b' end)
return (a == 'ablo ablo')
end)
testamem("dump/undump", function ()
local a = load(testprog)
local b = a and string.dump(a)
a = b and load(b)
return a and a()
end)
_G.AA = nil
local t = os.tmpname()
testamem("file creation", function ()
local f = assert(io.open(t, 'w'))
assert (not io.open"nomenaoexistente")
io.close(f);
return not loadfile'nomenaoexistente'
end)
assert(os.remove(t))
testamem("table creation", function ()
local a, lim = {}, 10
for i=1,lim do a[i] = i; a[i..'a'] = {} end
return (type(a[lim..'a']) == 'table' and a[lim] == lim)
end)
testamem("constructors", function ()
local a = {10, 20, 30, 40, 50; a=1, b=2, c=3, d=4, e=5}
return (type(a) == 'table' and a.e == 5)
end)
local a = 1
local close = nil
testamem("closure creation", function ()
function close (b)
return function (x) return b + x end
end
return (close(2)(4) == 6)
end)
testamem("using coroutines", function ()
local a = coroutine.wrap(function ()
coroutine.yield(string.rep("a", 10))
return {}
end)
assert(string.len(a()) == 10)
return a()
end)
do
local lim = 100
local a = {}; for i = 1, lim do a[i] = "01234567890123456789" end
testamem("auxiliary buffer", function ()
return (#table.concat(a, ",") == 20*lim + lim - 1)
end)
end
testamem("growing stack", function ()
local function foo (n)
if n == 0 then return 1 else return 1 + foo(n - 1) end
end
return foo(100)
end)
do
local res = T.testC([[rawcheckstack 500000; return 1]])
assert(res == false)
local L = T.newstate()
T.alloccount(0)
res = T.testC(L, [[rawcheckstack 5000; return 1]])
T.alloccount()
T.closestate(L)
assert(res == false)
end
do
local L = T.newstate()
T.alloccount(0)
T.closestate(L)
T.alloccount()
end
do
local L = T.newstate()
T.loadlib(L)
local res = (T.doremote(L, [[
_ENV = require"_G"
local T = require"T"
local a = {}
for i = 1, 1000 do a[i] = 'i' .. i end -- grow string table
local stsize, stuse = T.querystr()
assert(stuse > 1000)
local function foo (n)
if n > 0 then foo(n - 1) end
end
foo(180) -- grow stack
local _, stksize = T.stacklevel()
assert(stksize > 180)
a = nil
T.alloccount(0)
collectgarbage()
T.alloccount()
-- stack and string table could not be reallocated,
-- so they kept their sizes (without errors)
assert(select(2, T.stacklevel()) == stksize)
assert(T.querystr() == stsize)
return 'ok'
]]))
assert(res == 'ok')
T.closestate(L)
end
print'+'
local function gsub (a, b, c)
a, b = T.testC("gsub 2 3 4; gettop; return 2", a, b, c)
assert(b == 5)
return a
end
assert(gsub("alo.alo.uhuh.", ".", "//") == "alo//alo//uhuh//")
assert(gsub("alo.alo.uhuh.", "alo", "//") == "//.//.uhuh.")
assert(gsub("", "alo", "//") == "")
assert(gsub("...", ".", "/.") == "/././.")
assert(gsub("...", "...", "") == "")
local mt_xuxu, res, top = T.testC("newmetatable xuxu; gettop; return 3")
assert(type(mt_xuxu) == "table" and res and top == 3)
local d, res, top = T.testC("newmetatable xuxu; gettop; return 3")
assert(mt_xuxu == d and not res and top == 3)
d, res, top = T.testC("newmetatable xuxu1; gettop; return 3")
assert(mt_xuxu ~= d and res and top == 3)
x = T.newuserdata(0);
y = T.newuserdata(0);
T.testC("pushstring xuxu; gettable R; setmetatable 2", x)
assert(getmetatable(x) == mt_xuxu)
local res1, res2, top = T.testC([[testudata -1 xuxu
testudata 2 xuxu
gettop
return 3]], x)
assert(res1 and res2 and top == 4)
res1, res2, top = T.testC([[testudata -1 xuxu1
testudata 2 xuxu1
gettop
return 3]], x)
assert(not res1 and not res2 and top == 4)
res1, res2, top = T.testC([[testudata -1 xuxu2
testudata 2 xuxu2
gettop
return 3]], x)
assert(not res1 and not res2 and top == 4)
res1, res2, top = T.testC([[testudata -1 xuxu
testudata 2 xuxu
gettop
return 3]], y)
assert(not res1 and not res2 and top == 4)
do
local r = debug.getregistry()
assert(r.xuxu == mt_xuxu and r.xuxu1 == d)
r.xuxu = nil; r.xuxu1 = nil
end
print'OK'
print "testing require"
assert(require"string" == string)
assert(require"math" == math)
assert(require"table" == table)
assert(require"io" == io)
assert(require"os" == os)
assert(require"coroutine" == coroutine)
assert(type(package.path) == "string")
assert(type(package.cpath) == "string")
assert(type(package.loaded) == "table")
assert(type(package.preload) == "table")
assert(type(package.config) == "string")
print("package config: "..string.gsub(package.config, "\n", "|"))
do
local max = _soft and 100 or 2000
local t = {}
for i = 1,max do t[i] = string.rep("?", i%10 + 1) end
t[#t + 1] = ";"
local path = table.concat(t, ";")
local s, err = package.searchpath("xuxu", path)
assert(not s and
string.find(err, string.rep("xuxu", 10)) and
#string.gsub(err, "[^\n]", "") >= max)
local path = string.rep("?", max)
local s, err = package.searchpath("xuxu", path)
assert(not s and string.find(err, string.rep('xuxu', max)))
end
do
local oldpath = package.path
package.path = {}
local s, err = pcall(require, "no-such-file")
assert(not s and string.find(err, "package.path"))
package.path = oldpath
end
do print"testing 'require' message"
local oldpath = package.path
local oldcpath = package.cpath
package.path = "?.lua;?/?"
package.cpath = "?.so;?/init"
local st, msg = pcall(require, 'XXX')
local expected = [[module 'XXX' not found:
no field package.preload['XXX']
no file 'XXX.lua'
no file 'XXX/XXX'
no file 'XXX.so'
no file 'XXX/init']]
assert(msg == expected)
package.path = oldpath
package.cpath = oldcpath
end
print('+')
if not _port then
local dirsep = string.match(package.config, "^([^\n]+)\n")
local DIR = "libs" .. dirsep
local function D (x)
local x = string.gsub(x, "/", dirsep)
return DIR .. x
end
-- prepend DIR and pospend proper C lib. extension to a name
local function DC (x)
local ext = (dirsep == '\\') and ".dll" or ".so"
return D(x .. ext)
end
local function createfiles (files, preextras, posextras)
for n,c in pairs(files) do
io.output(D(n))
io.write(string.format(preextras, n))
io.write(c)
io.write(string.format(posextras, n))
io.close(io.output())
end
end
local function removefiles (files)
for n in pairs(files) do
os.remove(D(n))
end
end
local files = {
["names.lua"] = "do return {...} end\n",
["err.lua"] = "B = 15; a = a + 1;",
["synerr.lua"] = "B =",
["A.lua"] = "",
["B.lua"] = "assert(...=='B');require 'A'",
["A.lc"] = "",
["A"] = "",
["L"] = "",
["XXxX"] = "",
["C.lua"] = "package.loaded[...] = 25; require'C'",
}
AA = nil
local extras = [[
NAME = '%s'
REQUIRED = ...
return AA]]
createfiles(files, "", extras)
assert(package.searchpath("C.lua", D"?", "", "") == D"C.lua")
assert(package.searchpath("C.lua", D"?", ".", ".") == D"C.lua")
assert(package.searchpath("--x-", D"?", "-", "X") == D"XXxX")
assert(package.searchpath("---xX", D"?", "---", "XX") == D"XXxX")
assert(package.searchpath(D"C.lua", "?", dirsep) == D"C.lua")
assert(package.searchpath(".\\C.lua", D"?", "\\") == D"./C.lua")
local oldpath = package.path
package.path = string.gsub("D/?.lua;D/?.lc;D/?;D/??x?;D/L", "D/", DIR)
local try = function (p, n, r, ext)
NAME = nil
local rr, x = require(p)
assert(NAME == n)
assert(REQUIRED == p)
assert(rr == r)
assert(ext == x)
end
local a = require"names"
assert(a[1] == "names" and a[2] == D"names.lua")
local st, msg = pcall(require, "err")
assert(not st and string.find(msg, "arithmetic") and B == 15)
st, msg = pcall(require, "synerr")
assert(not st and string.find(msg, "error loading module"))
assert(package.searchpath("C", package.path) == D"C.lua")
assert(require"C" == 25)
assert(require"C" == 25)
AA = nil
try('B', 'B.lua', true, "libs/B.lua")
assert(package.loaded.B)
assert(require"B" == true)
assert(package.loaded.A)
assert(require"C" == 25)
package.loaded.A = nil
try('B', nil, true, nil)
try('A', 'A.lua', true, "libs/A.lua")
package.loaded.A = nil
os.remove(D'A.lua')
AA = {}
try('A', 'A.lc', AA, "libs/A.lc")
assert(package.searchpath("A", package.path) == D"A.lc")
assert(require("A") == AA)
AA = false
try('K', 'L', false, "libs/L")
try('K', 'L', false, "libs/L")
assert(rawget(_G, "_REQUIREDNAME") == nil)
AA = "x"
try("X", "XXxX", AA, "libs/XXxX")
removefiles(files)
NAME, REQUIRED, AA, B = nil
local _G = _G
package.path = string.gsub("D/?.lua;D/?/init.lua", "D/", DIR)
files = {
["P1/init.lua"] = "AA = 10",
["P1/xuxu.lua"] = "AA = 20",
}
createfiles(files, "_ENV = {}\n", "\nreturn _ENV\n")
AA = 0
local m, ext = assert(require"P1")
assert(ext == "libs/P1/init.lua")
assert(AA == 0 and m.AA == 10)
assert(require"P1" == m)
assert(require"P1" == m)
assert(package.searchpath("P1.xuxu", package.path) == D"P1/xuxu.lua")
m.xuxu, ext = assert(require"P1.xuxu")
assert(AA == 0 and m.xuxu.AA == 20)
assert(ext == "libs/P1/xuxu.lua")
assert(require"P1.xuxu" == m.xuxu)
assert(require"P1.xuxu" == m.xuxu)
assert(require"P1" == m and m.AA == 10)
removefiles(files)
AA = nil
package.path = ""
assert(not pcall(require, "file_does_not_exist"))
package.path = "??\0?"
assert(not pcall(require, "file_does_not_exist1"))
package.path = oldpath
local fname = "file_does_not_exist2"
local m, err = pcall(require, fname)
for t in string.gmatch(package.path..";"..package.cpath, "[^;]+") do
t = string.gsub(t, "?", fname)
assert(string.find(err, t, 1, true))
end
do
local searchers = package.searchers
package.searchers = 3
local st, msg = pcall(require, 'a')
assert(not st and string.find(msg, "must be a table"))
package.searchers = searchers
end
local function import(...)
local f = {...}
return function (m)
for i=1, #f do m[f[i]] = _G[f[i]] end
end
end
-- cannot change environment of a C function
assert(not pcall(module, 'XUXU'))
local p = ""
local st, err, when = package.loadlib(DC"lib1", "*")
if not st then
local f, err, when = package.loadlib("donotexist", p.."xuxu")
assert(not f and type(err) == "string" and when == "absent")
;(Message or print)('\n >>> cannot load dynamic library <<<\n')
print(err, when)
else
local f = assert(package.loadlib(DC"lib1", p.."onefunction"))
local a, b = f(15, 25)
assert(a == 25 and b == 15)
f = assert(package.loadlib(DC"lib1", p.."anotherfunc"))
assert(f(10, 20) == "10%20\n")
local f, err, when = package.loadlib(DC"lib1", p.."xuxu")
assert(not f and type(err) == "string" and when == "init")
f, err, when = package.loadlib("donotexist", p.."xuxu")
assert(not f and type(err) == "string" and when == "open")
f = assert(package.loadlib(DC"lib11", p.."luaopen_lib11"))
assert(f() == "exported")
package.cpath = DC"?"
local lib2, ext = require"lib2-v2"
assert(string.find(ext, "libs/lib2-v2", 1, true))
assert(_ENV.x == "lib2-v2" and _ENV.y == DC"lib2-v2")
assert(lib2.id("x") == true)
local fs, ext = require"lib1.sub"
assert(_ENV.x == "lib1.sub" and _ENV.y == DC"lib1")
assert(string.find(ext, "libs/lib1", 1, true))
assert(fs.id(45) == 45)
_ENV.x, _ENV.y = nil
end
_ENV = _G
do
local p = package
package = {}
p.preload.pl = function (...)
local _ENV = {...}
function xuxu (x) return x+20 end
return _ENV
end
local pl, ext = require"pl"
assert(require"pl" == pl)
assert(pl.xuxu(10) == 30)
assert(pl[1] == "pl" and pl[2] == ":preload:" and ext == ":preload:")
package = p
assert(type(package.path) == "string")
end
print('+')
end
print("testing assignments, logical operators, and constructors")
local res, res2 = 27
local a, b = 1, 2+3
assert(a==1 and b==5)
a={}
local function f() return 10, 11, 12 end
a.x, b, a[1] = 1, 2, f()
assert(a.x==1 and b==2 and a[1]==10)
a[f()], b, a[f()+3] = f(), a, 'x'
assert(a[10] == 10 and b == a and a[13] == 'x')
do
local f = function (n) local x = {}; for i=1,n do x[i]=i end;
return table.unpack(x) end;
local a,b,c
a,b = 0, f(1)
assert(a == 0 and b == 1)
a,b = 0, f(1)
assert(a == 0 and b == 1)
a,b,c = 0,5,f(4)
assert(a==0 and b==5 and c==1)
a,b,c = 0,5,f(0)
assert(a==0 and b==5 and c==nil)
end
local a, b, c, d = 1 and nil, 1 or nil, (1 and (nil or 1)), 6
assert(not a and b and c and d==6)
d = 20
a, b, c, d = f()
assert(a==10 and b==11 and c==12 and d==nil)
a,b = f(), 1, 2, 3, f()
assert(a==10 and b==1)
assert(a<b == false and a>b == true)
assert((10 and 2) == 2)
assert((10 or 2) == 10)
assert((10 or assert(nil)) == 10)
assert(not (nil and assert(nil)))
assert((nil or "alo") == "alo")
assert((nil and 10) == nil)
assert((false and 10) == false)
assert((true or 10) == true)
assert((false or 10) == 10)
assert(false ~= nil)
assert(nil ~= false)
assert(not nil == true)
assert(not not nil == false)
assert(not not 1 == true)
assert(not not a == true)
assert(not not (6 or nil) == true)
assert(not not (nil and 56) == false)
assert(not not (nil and true) == false)
assert(not 10 == false)
assert(not {} == false)
assert(not 0.5 == false)
assert(not "x" == false)
assert({} ~= {})
print('+')
a = {}
a[true] = 20
a[false] = 10
assert(a[1<2] == 20 and a[1>2] == 10)
function f(a) return a end
local a = {}
for i=3000,-3000,-1 do a[i + 0.0] = i; end
a[10e30] = "alo"; a[true] = 10; a[false] = 20
assert(a[10e30] == 'alo' and a[not 1] == 20 and a[10<20] == 10)
for i=3000,-3000,-1 do assert(a[i] == i); end
a[print] = assert
a[f] = print
a[a] = a
assert(a[a][a][a][a][print] == assert)
a[print](a[a[f]] == a[print])
assert(not pcall(function () local a = {}; a[nil] = 10 end))
assert(not pcall(function () local a = {[nil] = 10} end))
assert(a[nil] == undef)
a = nil
local a, b, c
a = {10,9,8,7,6,5,4,3,2; [-3]='a', [f]=print, a='a', b='ab'}
a, a.x, a.y = a, a[-3]
assert(a[1]==10 and a[-3]==a.a and a[f]==print and a.x=='a' and not a.y)
a[1], f(a)[2], b, c = {['alo']=assert}, 10, a[1], a[f], 6, 10, 23, f(a), 2
a[1].alo(a[2]==10 and b==10 and c==print)
a.aVeryLongName012345678901234567890123456789012345678901234567890123456789 = 10
local function foo ()
return a.aVeryLongName012345678901234567890123456789012345678901234567890123456789
end
assert(foo() == 10 and
a.aVeryLongName012345678901234567890123456789012345678901234567890123456789 ==
10)
do
local function foo ()
local _ENV <const> = 11
X = "hi"
end
local st, msg = pcall(foo)
assert(not st and string.find(msg, "number"))
end
local maxint = math.maxinteger
while maxint ~= (maxint + 0.0) or (maxint - 1) ~= (maxint - 1.0) do
maxint = maxint // 2
end
local maxintF = maxint + 0.0
assert(maxintF == maxint and math.type(maxintF) == "float" and
maxintF >= 2.0^14)
a[maxintF] = 10; a[maxintF - 1.0] = 11;
a[-maxintF] = 12; a[-maxintF + 1.0] = 13;
assert(a[maxint] == 10 and a[maxint - 1] == 11 and
a[-maxint] == 12 and a[-maxint + 1] == 13)
a[maxint] = 20
a[-maxint] = 22
assert(a[maxintF] == 20 and a[maxintF - 1.0] == 11 and
a[-maxintF] == 22 and a[-maxintF + 1.0] == 13)
a = nil
do
local a,i,j,b
a = {'a', 'b'}; i=1; j=2; b=a
i, a[i], a, j, a[j], a[i+j] = j, i, i, b, j, i
assert(i == 2 and b[1] == 1 and a == 1 and j == b and b[2] == 2 and
b[3] == 1)
a = {}
local function foo () -- assigining to upvalues
b, a.x, a = a, 10, 20
end
foo()
assert(a == 20 and b.x == 10)
end
do
local a,i,j,b
a = {'a', 'b'}; i=1; j=2; b=a
local function foo ()
i, a[i], a, j, a[j], a[i+j] = j, i, i, b, j, i
end
foo()
assert(i == 2 and b[1] == 1 and a == 1 and j == b and b[2] == 2 and
b[3] == 1)
local t = {}
(function (a) t[a], a = 10, 20 end)(1);
assert(t[1] == 10)
end
local function foo ()
local a
return function ()
local b
a, b = 3, 14
return a, b
end
end
local a, b = foo()()
assert(a == 3 and b == 14)
print('OK')
return res
if _soft then
return 'a'
end
print "testing large tables"
local debug = require"debug"
local lim = 2^18 + 1000
local prog = { "local y = {0" }
for i = 1, lim do prog[#prog + 1] = i end
prog[#prog + 1] = "}\n"
prog[#prog + 1] = "X = y\n"
prog[#prog + 1] = ("assert(X[%d] == %d)"):format(lim - 1, lim - 2)
prog[#prog + 1] = "return 0"
prog = table.concat(prog, ";")
local env = {string = string, assert = assert}
local f = assert(load(prog, nil, nil, env))
f()
assert(env.X[lim] == lim - 1 and env.X[lim + 1] == lim)
for k in pairs(env) do env[k] = undef end
setmetatable(env, {
__index = function (t, n) coroutine.yield('g'); return _G[n] end,
__newindex = function (t, n, v) coroutine.yield('s'); _G[n] = v end,
})
X = nil
local co = coroutine.wrap(f)
assert(co() == 's')
assert(co() == 'g')
assert(co() == 'g')
assert(co() == 0)
assert(X[lim] == lim - 1 and X[lim + 1] == lim)
getmetatable(env).__index = function () end
getmetatable(env).__newindex = function () end
local e, m = pcall(f)
assert(not e and m:find("global 'X'"))
getmetatable(env).__newindex = function () error("hi") end
local e, m = xpcall(f, debug.traceback)
assert(not e and m:find("'newindex'"))
f, X = nil
coroutine.yield'b'
if 2^32 == 0 then
print "testing string length overflow"
local repstrings = 192
local ssize = math.ceil(2.0^32 / repstrings) + 1
assert(repstrings * ssize > 2.0^32)
local longs = string.rep("\0", ssize)
local rep = assert(load(
"local a = ...; return " .. string.rep("a", repstrings, "..")))
local a, b = pcall(rep, longs)
assert(not a and string.find(b, "overflow"))
end
print'OK'
return 'a'
print("testing bitwise operations")
require "bwcoercion"
local numbits = string.packsize('j') * 8
assert(~0 == -1)
assert((1 << (numbits - 1)) == math.mininteger)
local a, b, c, d
a = 0xFFFFFFFFFFFFFFFF
assert(a == -1 and a & -1 == a and a & 35 == 35)
a = 0xF0F0F0F0F0F0F0F0
assert(a | -1 == -1)
assert(a ~ a == 0 and a ~ 0 == a and a ~ ~a == -1)
assert(a >> 4 == ~a)
a = 0xF0; b = 0xCC; c = 0xAA; d = 0xFD
assert(a | b ~ c & d == 0xF4)
a = 0xF0.0; b = 0xCC.0; c = "0xAA.0"; d = "0xFD.0"
assert(a | b ~ c & d == 0xF4)
a = 0xF0000000; b = 0xCC000000;
c = 0xAA000000; d = 0xFD000000
assert(a | b ~ c & d == 0xF4000000)
assert(~~a == a and ~a == -1 ~ a and -d == ~d + 1)
a = a << 32
b = b << 32
c = c << 32
d = d << 32
assert(a | b ~ c & d == 0xF4000000 << 32)
assert(~~a == a and ~a == -1 ~ a and -d == ~d + 1)
do
local code = string.format("return -1 >> %d", math.maxinteger)
assert(load(code)() == 0)
local code = string.format("return -1 >> %d", math.mininteger)
assert(load(code)() == 0)
local code = string.format("return -1 << %d", math.maxinteger)
assert(load(code)() == 0)
local code = string.format("return -1 << %d", math.mininteger)
assert(load(code)() == 0)
end
assert(-1 >> 1 == (1 << (numbits - 1)) - 1 and 1 << 31 == 0x80000000)
assert(-1 >> (numbits - 1) == 1)
assert(-1 >> numbits == 0 and
-1 >> -numbits == 0 and
-1 << numbits == 0 and
-1 << -numbits == 0)
assert(1 >> math.mininteger == 0)
assert(1 >> math.maxinteger == 0)
assert(1 << math.mininteger == 0)
assert(1 << math.maxinteger == 0)
assert((2^30 - 1) << 2^30 == 0)
assert((2^30 - 1) >> 2^30 == 0)
assert(1 >> -3 == 1 << 3 and 1000 >> 5 == 1000 << -5)
assert("0xffffffffffffffff" | 0 == -1)
assert("0xfffffffffffffffe" & "-1" == -2)
assert(" \t-0xfffffffffffffffe\n\t" & "-1" == 2)
assert(" \n -45 \t " >> " -2 " == -45 * 4)
assert("1234.0" << "5.0" == 1234 * 32)
assert("0xffff.0" ~ "0xAAAA" == 0x5555)
assert(~"0x0.000p4" == -1)
assert(("7" .. 3) << 1 == 146)
assert(0xffffffff >> (1 .. "9") == 0x1fff)
assert(10 | (1 .. "9") == 27)
do
local st, msg = pcall(function () return 4 & "a" end)
assert(string.find(msg, "'band'"))
local st, msg = pcall(function () return ~"a" end)
assert(string.find(msg, "'bnot'"))
end
assert(not pcall(function () return "0xffffffffffffffff.0" | 0 end))
assert(not pcall(function () return "0xffffffffffffffff\0" | 0 end))
print'+'
package.preload.bit32 = function () --{
-- no built-in 'bit32' library: implement it using bitwise operators
local bit = {}
function bit.bnot (a)
return ~a & 0xFFFFFFFF
end
--
-- in all vararg functions, avoid creating 'arg' table when there are
-- only 2 (or less) parameters, as 2 parameters is the common case
--
function bit.band (x, y, z, ...)
if not z then
return ((x or -1) & (y or -1)) & 0xFFFFFFFF
else
local arg = {...}
local res = x & y & z
for i = 1, #arg do res = res & arg[i] end
return res & 0xFFFFFFFF
end
end
function bit.bor (x, y, z, ...)
if not z then
return ((x or 0) | (y or 0)) & 0xFFFFFFFF
else
local arg = {...}
local res = x | y | z
for i = 1, #arg do res = res | arg[i] end
return res & 0xFFFFFFFF
end
end
function bit.bxor (x, y, z, ...)
if not z then
return ((x or 0) ~ (y or 0)) & 0xFFFFFFFF
else
local arg = {...}
local res = x ~ y ~ z
for i = 1, #arg do res = res ~ arg[i] end
return res & 0xFFFFFFFF
end
end
function bit.btest (...)
return bit.band(...) ~= 0
end
function bit.lshift (a, b)
return ((a & 0xFFFFFFFF) << b) & 0xFFFFFFFF
end
function bit.rshift (a, b)
return ((a & 0xFFFFFFFF) >> b) & 0xFFFFFFFF
end
function bit.arshift (a, b)
a = a & 0xFFFFFFFF
if b <= 0 or (a & 0x80000000) == 0 then
return (a >> b) & 0xFFFFFFFF
else
return ((a >> b) | ~(0xFFFFFFFF >> b)) & 0xFFFFFFFF
end
end
function bit.lrotate (a ,b)
b = b & 31
a = a & 0xFFFFFFFF
a = (a << b) | (a >> (32 - b))
return a & 0xFFFFFFFF
end
function bit.rrotate (a, b)
return bit.lrotate(a, -b)
end
local function checkfield (f, w)
w = w or 1
assert(f >= 0, "field cannot be negative")
assert(w > 0, "width must be positive")
assert(f + w <= 32, "trying to access non-existent bits")
return f, ~(-1 << w)
end
function bit.extract (a, f, w)
local f, mask = checkfield(f, w)
return (a >> f) & mask
end
function bit.replace (a, v, f, w)
local f, mask = checkfield(f, w)
v = v & mask
a = (a & ~(mask << f)) | (v << f)
return a & 0xFFFFFFFF
end
return bit
end --}
print("testing bitwise library")
local bit32 = require'bit32'
assert(bit32.band() == bit32.bnot(0))
assert(bit32.btest() == true)
assert(bit32.bor() == 0)
assert(bit32.bxor() == 0)
assert(bit32.band() == bit32.band(0xffffffff))
assert(bit32.band(1,2) == 0)
assert(bit32.band(-1) == 0xffffffff)
assert(bit32.band((1 << 33) - 1) == 0xffffffff)
assert(bit32.band(-(1 << 33) - 1) == 0xffffffff)
assert(bit32.band((1 << 33) + 1) == 1)
assert(bit32.band(-(1 << 33) + 1) == 1)
assert(bit32.band(-(1 << 40)) == 0)
assert(bit32.band(1 << 40) == 0)
assert(bit32.band(-(1 << 40) - 2) == 0xfffffffe)
assert(bit32.band((1 << 40) - 4) == 0xfffffffc)
assert(bit32.lrotate(0, -1) == 0)
assert(bit32.lrotate(0, 7) == 0)
assert(bit32.lrotate(0x12345678, 0) == 0x12345678)
assert(bit32.lrotate(0x12345678, 32) == 0x12345678)
assert(bit32.lrotate(0x12345678, 4) == 0x23456781)
assert(bit32.rrotate(0x12345678, -4) == 0x23456781)
assert(bit32.lrotate(0x12345678, -8) == 0x78123456)
assert(bit32.rrotate(0x12345678, 8) == 0x78123456)
assert(bit32.lrotate(0xaaaaaaaa, 2) == 0xaaaaaaaa)
assert(bit32.lrotate(0xaaaaaaaa, -2) == 0xaaaaaaaa)
for i = -50, 50 do
assert(bit32.lrotate(0x89abcdef, i) == bit32.lrotate(0x89abcdef, i%32))
end
assert(bit32.lshift(0x12345678, 4) == 0x23456780)
assert(bit32.lshift(0x12345678, 8) == 0x34567800)
assert(bit32.lshift(0x12345678, -4) == 0x01234567)
assert(bit32.lshift(0x12345678, -8) == 0x00123456)
assert(bit32.lshift(0x12345678, 32) == 0)
assert(bit32.lshift(0x12345678, -32) == 0)
assert(bit32.rshift(0x12345678, 4) == 0x01234567)
assert(bit32.rshift(0x12345678, 8) == 0x00123456)
assert(bit32.rshift(0x12345678, 32) == 0)
assert(bit32.rshift(0x12345678, -32) == 0)
assert(bit32.arshift(0x12345678, 0) == 0x12345678)
assert(bit32.arshift(0x12345678, 1) == 0x12345678 // 2)
assert(bit32.arshift(0x12345678, -1) == 0x12345678 * 2)
assert(bit32.arshift(-1, 1) == 0xffffffff)
assert(bit32.arshift(-1, 24) == 0xffffffff)
assert(bit32.arshift(-1, 32) == 0xffffffff)
assert(bit32.arshift(-1, -1) == bit32.band(-1 * 2, 0xffffffff))
assert(0x12345678 << 4 == 0x123456780)
assert(0x12345678 << 8 == 0x1234567800)
assert(0x12345678 << -4 == 0x01234567)
assert(0x12345678 << -8 == 0x00123456)
assert(0x12345678 << 32 == 0x1234567800000000)
assert(0x12345678 << -32 == 0)
assert(0x12345678 >> 4 == 0x01234567)
assert(0x12345678 >> 8 == 0x00123456)
assert(0x12345678 >> 32 == 0)
assert(0x12345678 >> -32 == 0x1234567800000000)
print("+")
local c = {0, 1, 2, 3, 10, 0x80000000, 0xaaaaaaaa, 0x55555555,
0xffffffff, 0x7fffffff}
for _, b in pairs(c) do
assert(bit32.band(b) == b)
assert(bit32.band(b, b) == b)
assert(bit32.band(b, b, b, b) == b)
assert(bit32.btest(b, b) == (b ~= 0))
assert(bit32.band(b, b, b) == b)
assert(bit32.band(b, b, b, ~b) == 0)
assert(bit32.btest(b, b, b) == (b ~= 0))
assert(bit32.band(b, bit32.bnot(b)) == 0)
assert(bit32.bor(b, bit32.bnot(b)) == bit32.bnot(0))
assert(bit32.bor(b) == b)
assert(bit32.bor(b, b) == b)
assert(bit32.bor(b, b, b) == b)
assert(bit32.bor(b, b, 0, ~b) == 0xffffffff)
assert(bit32.bxor(b) == b)
assert(bit32.bxor(b, b) == 0)
assert(bit32.bxor(b, b, b) == b)
assert(bit32.bxor(b, b, b, b) == 0)
assert(bit32.bxor(b, 0) == b)
assert(bit32.bnot(b) ~= b)
assert(bit32.bnot(bit32.bnot(b)) == b)
assert(bit32.bnot(b) == (1 << 32) - 1 - b)
assert(bit32.lrotate(b, 32) == b)
assert(bit32.rrotate(b, 32) == b)
assert(bit32.lshift(bit32.lshift(b, -4), 4) == bit32.band(b, bit32.bnot(0xf)))
assert(bit32.rshift(bit32.rshift(b, 4), -4) == bit32.band(b, bit32.bnot(0xf)))
end
c = {0, 1, 2, 3, 10, 0x800000, 0xaaaaaa, 0x555555, 0xffffff, 0x7fffff}
for _, b in pairs(c) do
for i = -40, 40 do
local x = bit32.lshift(b, i)
local y = math.floor(math.fmod(b * 2.0^i, 2.0^32))
assert(math.fmod(x - y, 2.0^32) == 0)
end
end
assert(not pcall(bit32.band, {}))
assert(not pcall(bit32.bnot, "a"))
assert(not pcall(bit32.lshift, 45))
assert(not pcall(bit32.lshift, 45, print))
assert(not pcall(bit32.rshift, 45, print))
print("+")
assert(bit32.extract(0x12345678, 0, 4) == 8)
assert(bit32.extract(0x12345678, 4, 4) == 7)
assert(bit32.extract(0xa0001111, 28, 4) == 0xa)
assert(bit32.extract(0xa0001111, 31, 1) == 1)
assert(bit32.extract(0x50000111, 31, 1) == 0)
assert(bit32.extract(0xf2345679, 0, 32) == 0xf2345679)
assert(not pcall(bit32.extract, 0, -1))
assert(not pcall(bit32.extract, 0, 32))
assert(not pcall(bit32.extract, 0, 0, 33))
assert(not pcall(bit32.extract, 0, 31, 2))
assert(bit32.replace(0x12345678, 5, 28, 4) == 0x52345678)
assert(bit32.replace(0x12345678, 0x87654321, 0, 32) == 0x87654321)
assert(bit32.replace(0, 1, 2) == 2^2)
assert(bit32.replace(0, -1, 4) == 2^4)
assert(bit32.replace(-1, 0, 31) == (1 << 31) - 1)
assert(bit32.replace(-1, 0, 1, 2) == (1 << 32) - 7)
assert(bit32.bor(3.0) == 3)
assert(bit32.bor(-4.0) == 0xfffffffc)
if 2.0^50 < 2.0^50 + 1.0 and 2.0^50 < (-1 >> 1) then
assert(bit32.bor(2.0^32 - 5.0) == 0xfffffffb)
assert(bit32.bor(-2.0^32 - 6.0) == 0xfffffffa)
assert(bit32.bor(2.0^48 - 5.0) == 0xfffffffb)
assert(bit32.bor(-2.0^48 - 6.0) == 0xfffffffa)
end
print'OK'
local tonumber, tointeger = tonumber, math.tointeger
local type, getmetatable, rawget, error = type, getmetatable, rawget, error
local strsub = string.sub
local print = print
_ENV = nil
local function toint (x)
x = tonumber(x)
if not x then
return false
end
return tointeger(x)
end
-- If operation fails, maybe second operand has a metamethod that should
-- have been called if not for this string metamethod, so try to
-- call it.
local function trymt (x, y, mtname)
if type(y) ~= "string" then
local mt = getmetatable(y)
local mm = mt and rawget(mt, mtname)
if mm then
return mm(x, y)
end
end
error("attempt to '" .. strsub(mtname, 3) ..
"' a " .. type(x) .. " with a " .. type(y), 4)
end
local function checkargs (x, y, mtname)
local xi = toint(x)
local yi = toint(y)
if xi and yi then
return xi, yi
else
return trymt(x, y, mtname), nil
end
end
local smt = getmetatable("")
smt.__band = function (x, y)
local x, y = checkargs(x, y, "__band")
return y and x & y or x
end
smt.__bor = function (x, y)
local x, y = checkargs(x, y, "__bor")
return y and x | y or x
end
smt.__bxor = function (x, y)
local x, y = checkargs(x, y, "__bxor")
return y and x ~ y or x
end
smt.__shl = function (x, y)
local x, y = checkargs(x, y, "__shl")
return y and x << y or x
end
smt.__shr = function (x, y)
local x, y = checkargs(x, y, "__shr")
return y and x >> y or x
end
smt.__bnot = function (x)
local x, y = checkargs(x, x, "__bnot")
return y and ~x or x
end
print("testing functions and calls")
local debug = require "debug"
assert(type(1<2) == 'boolean')
assert(type(true) == 'boolean' and type(false) == 'boolean')
assert(type(nil) == 'nil'
and type(-3) == 'number'
and type'x' == 'string'
and type{} == 'table'
and type(type) == 'function')
assert(type(assert) == type(print))
local function f (x) return a:x (x) end
assert(type(f) == 'function')
assert(not pcall(type))
fact = false
do
local res = 1
local function fact (n)
if n==0 then return res
else return n*fact(n-1)
end
end
assert(fact(5) == 120)
end
assert(fact == false)
fact = nil
local a = {i = 10}
local self = 20
function a:x (x) return x+self.i end
function a.y (x) return x+self end
assert(a:x(1)+10 == a.y(1))
a.t = {i=-100}
a["t"].x = function (self, a,b) return self.i+a+b end
assert(a.t:x(2,3) == -95)
do
local a = {x=0}
function a:add (x) self.x, a.y = self.x+x, 20; return self end
assert(a:add(10):add(20):add(30).x == 60 and a.y == 20)
end
local a = {b={c={}}}
function a.b.c.f1 (x) return x+1 end
function a.b.c:f2 (x,y) self[x] = y end
assert(a.b.c.f1(4) == 5)
a.b.c:f2('k', 12); assert(a.b.c.k == 12)
print('+')
t = nil
function f(a,b,c) local d = 'a'; t={a,b,c,d} end
f(
1,2)
assert(t[1] == 1 and t[2] == 2 and t[3] == nil and t[4] == 'a')
f(1,2,
3,4)
assert(t[1] == 1 and t[2] == 2 and t[3] == 3 and t[4] == 'a')
t = nil
function fat(x)
if x <= 1 then return 1
else return x*load("return fat(" .. x-1 .. ")", "")()
end
end
assert(load "load 'assert(fat(6)==720)' () ")()
a = load('return fat(5), 3')
local a,b = a()
assert(a == 120 and b == 3)
fat = nil
print('+')
local function err_on_n (n)
if n==0 then error(); exit(1);
else err_on_n (n-1); exit(1);
end
end
do
local function dummy (n)
if n > 0 then
assert(not pcall(err_on_n, n))
dummy(n-1)
end
end
dummy(10)
end
_G.deep = nil
function deep (n)
if n>0 then deep(n-1) end
end
deep(10)
deep(180)
print"testing tail calls"
function deep (n) if n>0 then return deep(n-1) else return 101 end end
assert(deep(30000) == 101)
a = {}
function a:deep (n) if n>0 then return self:deep(n-1) else return 101 end end
assert(a:deep(30000) == 101)
do
local function foo (x, ...) local a = {...}; return x, a[1], a[2] end
local function foo1 (x) return foo(10, x, x + 1) end
local a, b, c = foo1(-2)
assert(a == 10 and b == -2 and c == -1)
local t = setmetatable({}, {__call = foo})
local function foo2 (x) return t(10, x) end
a, b, c = foo2(100)
assert(a == t and b == 10 and c == 100)
a, b = (function () return foo() end)()
assert(a == nil and b == nil)
local X, Y, A
local function foo (x, y, ...) X = x; Y = y; A = {...} end
local function foo1 (...) return foo(...) end
local a, b, c = foo1()
assert(X == nil and Y == nil and #A == 0)
a, b, c = foo1(10)
assert(X == 10 and Y == nil and #A == 0)
a, b, c = foo1(10, 20)
assert(X == 10 and Y == 20 and #A == 0)
a, b, c = foo1(10, 20, 30)
assert(X == 10 and Y == 20 and #A == 1 and A[1] == 30)
end
do
local function loop ()
assert(pcall(loop))
end
local err, msg = xpcall(loop, loop)
assert(not err and string.find(msg, "error"))
end
do
local n = 10000
local function foo ()
if n == 0 then return 1023
else n = n - 1; return foo()
end
end
-- build a chain of __call metamethods ending in function 'foo'
for i = 1, 100 do
foo = setmetatable({}, {__call = foo})
end
assert(coroutine.wrap(function() return foo() end)() == 1023)
end
print('+')
do
local N = 20
local u = table.pack
for i = 1, N do
u = setmetatable({i}, {__call = u})
end
local Res = u("a", "b", "c")
assert(Res.n == N + 3)
for i = 1, N do
assert(Res[i][1] == i)
end
assert(Res[N + 1] == "a" and Res[N + 2] == "b" and Res[N + 3] == "c")
end
a = nil
(function (x) a=x end)(23)
assert(a == 23 and (function (x) return x*2 end)(20) == 40)
local Z = function (le)
local function a (f)
return le(function (x) return f(f)(x) end)
end
return a(a)
end
-- non-recursive factorial
local F = function (f)
return function (n)
if n == 0 then return 1
else return n*f(n-1) end
end
end
local fat = Z(F)
assert(fat(0) == 1 and fat(4) == 24 and Z(F)(5)==5*Z(F)(4))
local function g (z)
local function f (a,b,c,d)
return function (x,y) return a+b+c+d+a+x+y+z end
end
return f(z,z+1,z+2,z+3)
end
local f = g(10)
assert(f(9, 16) == 10+11+12+13+10+9+16+10)
print('+')
local function unlpack (t, i)
i = i or 1
if (i <= #t) then
return t[i], unlpack(t, i+1)
end
end
local function equaltab (t1, t2)
assert(#t1 == #t2)
for i = 1, #t1 do
assert(t1[i] == t2[i])
end
end
local pack = function (...) return (table.pack(...)) end
local function f() return 1,2,30,4 end
local function ret2 (a,b) return a,b end
local a,b,c,d = unlpack{1,2,3}
assert(a==1 and b==2 and c==3 and d==nil)
a = {1,2,3,4,false,10,'alo',false,assert}
equaltab(pack(unlpack(a)), a)
equaltab(pack(unlpack(a), -1), {1,-1})
a,b,c,d = ret2(f()), ret2(f())
assert(a==1 and b==1 and c==2 and d==nil)
a,b,c,d = unlpack(pack(ret2(f()), ret2(f())))
assert(a==1 and b==1 and c==2 and d==nil)
a,b,c,d = unlpack(pack(ret2(f()), (ret2(f()))))
assert(a==1 and b==1 and c==nil and d==nil)
a = ret2{ unlpack{1,2,3}, unlpack{3,2,1}, unlpack{"a", "b"}}
assert(a[1] == 1 and a[2] == 3 and a[3] == "a" and a[4] == "b")
rawget({}, "x", 1)
rawset({}, "x", 1, 2)
assert(math.sin(1,2) == math.sin(1))
table.sort({10,9,8,4,19,23,0,0}, function (a,b) return a<b end, "extra arg")
local x = "-- a comment\0\0\0\n x = 10 + \n23; \
local a = function () x = 'hi' end; \
return '\0'"
local function read1 (x)
local i = 0
return function ()
collectgarbage()
i=i+1
return string.sub(x, i, i)
end
end
local function cannotload (msg, a,b)
assert(not a and string.find(b, msg))
end
a = assert(load(read1(x), "modname", "t", _G))
assert(a() == "\0" and _G.x == 33)
assert(debug.getinfo(a).source == "modname")
cannotload("attempt to load a text chunk", load(read1(x), "modname", "b", {}))
cannotload("attempt to load a text chunk", load(x, "modname", "b"))
a = assert(load(function () return nil end))
a()
assert(not load(function () return true end))
local t = {nil, "return ", "3"}
f, msg = load(function () return table.remove(t, 1) end)
assert(f() == nil)
f = load(string.dump(function () return 1 end), nil, "b", {})
assert(type(f) == "function" and f() == 1)
do
local f = string.dump(function ()
return '01234567890123456789012345678901234567890123456789'
end)
f = load(read1(f))
assert(f() == '01234567890123456789012345678901234567890123456789')
end
do
local code =
"\x1b\x4c\x75\x61\x54\x00\x19\x93\x0d\x0a\x1a\x0a\x04\x08\x08\x78\x56\z
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x28\x77\x40\x00\x86\x40\z
\x74\x65\x6d\x70\x81\x81\x01\x00\x02\x82\x48\x00\x02\x00\xc7\x00\x01\z
\x00\x80\x80\x80\x82\x00\x00\x80\x81\x82\x78\x80\x82\x81\x86\x40\x74\z
\x65\x6d\x70"
assert(load(code))
end
x = string.dump(load("x = 1; return x"))
a = assert(load(read1(x), nil, "b"))
assert(a() == 1 and _G.x == 1)
cannotload("attempt to load a binary chunk", load(read1(x), nil, "t"))
cannotload("attempt to load a binary chunk", load(x, nil, "t"))
_G.x = nil
assert(not pcall(string.dump, print))
cannotload("unexpected symbol", load(read1("*a = 123")))
cannotload("unexpected symbol", load("*a = 123"))
cannotload("hhi", load(function () error("hhi") end))
assert(load("return _ENV", nil, nil, 123)() == 123)
local x; XX = 123
local function h ()
local y=x
return XX
end
local d = string.dump(h)
x = load(d, "", "b")
assert(debug.getupvalue(x, 2) == '_ENV')
debug.setupvalue(x, 2, _G)
assert(x() == 123)
assert(assert(load("return XX + ...", nil, nil, {XX = 13}))(4) == 17)
XX = nil
x = [[
return function (x)
return function (y)
return function (z)
return x+y+z
end
end
end
]]
a = assert(load(read1(x), "read", "t"))
assert(a()(2)(3)(10) == 15)
x = string.dump(a)
a = assert(load(read1(x), "read", "b"))
assert(a()(2)(3)(10) == 15)
local a, b = 20, 30
x = load(string.dump(function (x)
if x == "set" then a = 10+b; b = b+1 else
return a
end
end), "", "b", nil)
assert(x() == nil)
assert(debug.setupvalue(x, 1, "hi") == "a")
assert(x() == "hi")
assert(debug.setupvalue(x, 2, 13) == "b")
assert(not debug.setupvalue(x, 3, 10))
x("set")
assert(x() == 23)
x("set")
assert(x() == 24)
do
local nup = 200
local prog = {"local a1"}
for i = 2, nup do prog[#prog + 1] = ", a" .. i end
prog[#prog + 1] = " = 1"
for i = 2, nup do prog[#prog + 1] = ", " .. i end
local sum = 1
prog[#prog + 1] = "; return function () return a1"
for i = 2, nup do prog[#prog + 1] = " + a" .. i; sum = sum + i end
prog[#prog + 1] = " end"
prog = table.concat(prog)
local f = assert(load(prog))()
assert(f() == sum)
f = load(string.dump(f))
local a = 10
local h = function () return a end
for i = 1, nup do
debug.upvaluejoin(f, i, h, 1)
end
assert(f() == 10 * nup)
end
do
local t = {x = 1}
function t:_012345678901234567890123456789012345678901234567890123456789 ()
return self.x
end
assert(t:_012345678901234567890123456789012345678901234567890123456789() == 1)
end
assert((function () return nil end)(4) == nil)
assert((function () local a; return a end)(4) == nil)
assert((function (a) return a end)() == nil)
print("testing binary chunks")
do
local header = string.pack("c4BBc6BBB",
"\27Lua",
0x54,
0,
"\x19\x93\r\n\x1a\n",
4,
string.packsize("j"),
string.packsize("n")
)
local c = string.dump(function ()
local a = 1; local b = 3;
local f = function () return a + b + _ENV.c; end -- upvalues
local s1 = "a constant"
local s2 = "another constant"
return a + b * 3
end)
assert(assert(load(c))() == 10)
assert(string.sub(c, 1, #header) == header)
local ci, cn = string.unpack("jn", c, #header + 1)
assert(ci == 0x5678 and cn == 370.5)
for i = 1, #header do
local s = string.sub(c, 1, i - 1) ..
string.char(string.byte(string.sub(c, i, i)) + 1) ..
string.sub(c, i + 1, -1)
assert(#s == #c)
assert(not load(s))
end
for i = 1, #c - 1 do
local st, msg = load(string.sub(c, 1, i))
assert(not st and string.find(msg, "truncated"))
end
end
print('OK')
return deep
print "testing closures"
local A,B = 0,{g=10}
local function f(x)
local a = {}
for i=1,1000 do
local y = 0
do
a[i] = function () B.g = B.g+1; y = y+x; return y+A end
end
end
local dummy = function () return a[A] end
collectgarbage()
A = 1; assert(dummy() == a[1]); A = 0;
assert(a[1]() == x)
assert(a[3]() == x)
collectgarbage()
assert(B.g == 12)
return a
end
local a = f(10)
local x = {[1] = {}}
setmetatable(x, {__mode = 'kv'})
while x[1] do
local a = A..A..A..A
A = A+1
end
assert(a[1]() == 20+A)
assert(a[1]() == 30+A)
assert(a[2]() == 10+A)
collectgarbage()
assert(a[2]() == 20+A)
assert(a[2]() == 30+A)
assert(a[3]() == 20+A)
assert(a[8]() == 10+A)
assert(getmetatable(x).__mode == 'kv')
assert(B.g == 19)
a = {}
for i = 1, 5 do a[i] = function (x) return i + a + _ENV end end
assert(a[3] ~= a[4] and a[4] ~= a[5])
do
local a = function (x) return math.sin(_ENV[x]) end
local function f()
return a
end
assert(f() == f())
end
a = {}
for i=1,10 do
a[i] = {set = function(x) i=x end, get = function () return i end}
if i == 3 then break end
end
assert(a[4] == undef)
a[1].set(10)
assert(a[2].get() == 2)
a[2].set('a')
assert(a[3].get() == 3)
assert(a[2].get() == 'a')
a = {}
local t = {"a", "b"}
for i = 1, #t do
local k = t[i]
a[i] = {set = function(x, y) i=x; k=y end,
get = function () return i, k end}
if i == 2 then break end
end
a[1].set(10, 20)
local r,s = a[2].get()
assert(r == 2 and s == 'b')
r,s = a[1].get()
assert(r == 10 and s == 20)
a[2].set('a', 'b')
r,s = a[2].get()
assert(r == "a" and s == "b")
local f
for i=1,3 do
f = function () return i end
break
end
assert(f() == 1)
for k = 1, #t do
local v = t[k]
f = function () return k, v end
break
end
assert(({f()})[1] == 1)
assert(({f()})[2] == "a")
local b
function f(x)
local first = 1
while 1 do
if x == 3 and not first then return end
local a = 'xuxu'
b = function (op, y)
if op == 'set' then
a = x+y
else
return a
end
end
if x == 1 then do break end
elseif x == 2 then return
else if x ~= 3 then error() end
end
first = nil
end
end
for i=1,3 do
f(i)
assert(b('get') == 'xuxu')
b('set', 10); assert(b('get') == 10+i)
b = nil
end
pcall(f, 4);
assert(b('get') == 'xuxu')
b('set', 10); assert(b('get') == 14)
local y, w
function f(x)
return function (y)
return function (z) return w+x+y+z end
end
end
y = f(10)
w = 1.345
assert(y(20)(30) == 60+w)
do
local X, Y
local a = math.sin(0)
while a do
local b = 10
X = function () return b end -- closure with upvalue
if a then break end
end
do
local b = 20
Y = function () return b end -- closure with upvalue
end
assert(X() == 10 and Y() == 20)
end
local a = {}
local i = 1
repeat
local x = i
a[i] = function () i = x+1; return x end
until i > 10 or a[i]() ~= x
assert(i == 11 and a[1]() == 1 and a[3]() == 3 and i == 4)
a = {}
for i = 1, 10 do
if i % 3 == 0 then
local y = 0
a[i] = function (x) local t = y; y = x; return t end
elseif i % 3 == 1 then
goto L1
error'not here'
::L1::
local y = 1
a[i] = function (x) local t = y; y = x; return t end
elseif i % 3 == 2 then
local t
goto l4
::l4a:: a[i] = t; goto l4b
error("should never be here!")
::l4::
local y = 2
t = function (x) local t = y; y = x; return t end
goto l4a
error("should never be here!")
::l4b::
end
end
for i = 1, 10 do
assert(a[i](i * 10) == i % 3 and a[i]() == i * 10)
end
print'+'
local function t ()
local function c(a,b) assert(a=="test" and b=="OK") end
local function v(f, ...) c("test", f() ~= 1 and "FAILED" or "OK") end
local x = 1
return v(function() return x end)
end
t()
local debug = require'debug'
local foo1, foo2, foo3
do
local a , b, c = 3, 5, 7
foo1 = function () return a+b end;
foo2 = function () return b+a end;
do
local a = 10
foo3 = function () return a+b end;
end
end
assert(debug.upvalueid(foo1, 1))
assert(debug.upvalueid(foo1, 2))
assert(not 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)
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))
print'OK'
if T==nil then
(Message or print)('\n >>> testC not active: skipping opcode tests <<<\n')
return
end
print "testing code generation and optimizations"
local k0aux <const> = 0
local k0 <const> = k0aux
local k1 <const> = 1
local k3 <const> = 3
local k6 <const> = k3 + (k3 << k0)
local kFF0 <const> = 0xFF0
local k3_78 <const> = 3.78
local x, k3_78_4 <const> = 10, k3_78 / 4
assert(x == 10)
local kx <const> = "x"
local kTrue <const> = true
local kFalse <const> = false
local kNil <const> = nil
do
local function f (a)
for k,v,w in a do end
end
end
local function checkKlist (func, list)
local k = T.listk(func)
assert(#k == #list)
for i = 1, #k do
assert(k[i] == list[i] and math.type(k[i]) == math.type(list[i]))
end
end
local function foo ()
local a
a = k3;
a = 0; a = 0.0; a = -7 + 7
a = k3_78/4; a = k3_78_4
a = -k3_78/4; a = k3_78/4; a = -3.78/4
a = -3.79/4; a = 0.0; a = -0;
a = k3; a = 3.0; a = 3; a = 3.0
end
checkKlist(foo, {3.78/4, -3.78/4, -3.79/4})
foo = function (f, a)
f(100 * 1000)
f(100.0 * 1000)
f(-100 * 1000)
f(-100 * 1000.0)
f(100000)
f(100000.0)
f(-100000)
f(-100000.0)
end
checkKlist(foo, {100000, 100000.0, -100000, -100000.0})
foo = function (t, a)
t[a] = 1; t[a] = 1.0
t[a] = 1; t[a] = 1.0
t[a] = 2; t[a] = 2.0
t[a] = 0; t[a] = 0.0
t[a] = 1; t[a] = 1.0
t[a] = 2; t[a] = 2.0
t[a] = 0; t[a] = 0.0
end
checkKlist(foo, {1, 1.0, 2, 2.0, 0, 0.0})
local function check (f, ...)
local arg = {...}
local c = T.listcode(f)
for i=1, #arg do
local opcode = string.match(c[i], "%u%w+")
assert(arg[i] == opcode)
end
assert(c[#arg+2] == undef)
end
-- check that 'f' opcodes match '...' and that 'f(p) == r'.
local function checkR (f, p, r, ...)
local r1 = f(p)
assert(r == r1 and math.type(r) == math.type(r1))
check(f, ...)
end
-- check that 'a' and 'b' has the same opcodes
local function checkequal (a, b)
a = T.listcode(a)
b = T.listcode(b)
assert(#a == #b)
for i = 1, #a do
a[i] = string.gsub(a[i], '%b()', '')
b[i] = string.gsub(b[i], '%b()', '')
assert(a[i] == b[i])
end
end
-- some basic instructions
check(function () -- function does not create upvalues
(function () end){f()}
end, 'CLOSURE', 'NEWTABLE', 'EXTRAARG', 'GETTABUP', 'CALL',
'SETLIST', 'CALL', 'RETURN0')
check(function (x) -- function creates upvalues
(function () return x end){f()}
end, 'CLOSURE', 'NEWTABLE', 'EXTRAARG', 'GETTABUP', 'CALL',
'SETLIST', 'CALL', 'RETURN')
check(function ()
local kNil <const> = nil
local a,b,c
local d; local e;
local f,g,h;
d = nil; d=nil; b=nil; a=kNil; c=nil;
end, 'LOADNIL', 'RETURN0')
check(function ()
local a,b,c,d = 1,1,1,1
d=nil;c=nil;b=nil;a=nil
end, 'LOADI', 'LOADI', 'LOADI', 'LOADI', 'LOADNIL', 'RETURN0')
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
check (function (a,b,c) return a end, 'RETURN1')
check(function () while kTrue do local a = -1 end end,
'LOADI', 'JMP', 'RETURN0')
check(function () while 1 do local a = -1 end end,
'LOADI', 'JMP', 'RETURN0')
check(function () repeat local x = 1 until true end,
'LOADI', 'RETURN0')
check(function (a,b,c,d) return a..b..c..d end,
'MOVE', 'MOVE', 'MOVE', 'MOVE', 'CONCAT', 'RETURN1')
check(function () return not not nil end, 'LOADFALSE', 'RETURN1')
check(function () return not not kFalse end, 'LOADFALSE', 'RETURN1')
check(function () return not not true end, 'LOADTRUE', 'RETURN1')
check(function () return not not k3 end, 'LOADTRUE', 'RETURN1')
check(function ()
local a,b,c,d
a = b*a
c.x, a[b] = -((a + d/b - a[b]) ^ a.x), b
end,
'LOADNIL',
'MUL', 'MMBIN',
'DIV', 'MMBIN', 'ADD', 'MMBIN', 'GETTABLE', 'SUB', 'MMBIN',
'GETFIELD', 'POW', 'MMBIN', 'UNM', 'SETTABLE', 'SETFIELD', 'RETURN0')
check(function ()
local a,b
local c = kNil
a[kx] = 3.2
a.x = b
a[b] = 'x'
end,
'LOADNIL', 'SETFIELD', 'SETFIELD', 'SETTABLE', 'RETURN0')
check(function (a)
local k255 <const> = 255
a[1] = a[100]
a[k255] = a[256]
a[256] = 5
end,
'GETI', 'SETI',
'LOADI', 'GETTABLE', 'SETI',
'LOADI', 'SETTABLE', 'RETURN0')
check(function ()
local a,b
a = a - a
b = a/a
b = 5-4
end,
'LOADNIL', 'SUB', 'MMBIN', 'DIV', 'MMBIN', 'LOADI', 'RETURN0')
check(function ()
local a,b
a[kTrue] = false
end,
'LOADNIL', 'LOADTRUE', 'SETTABLE', 'RETURN0')
checkR(function (a) if a == 1 then return 2 end end, 1, 2,
'EQI', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if -4.0 == a then return 2 end end, -4, 2,
'EQI', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if a == "hi" then return 2 end end, 10, nil,
'EQK', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if a == 10000 then return 2 end end, 1, nil,
'EQK', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if -10000 == a then return 2 end end, -10000, 2,
'EQK', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if -10 <= a then return 2 end end, -10, 2,
'GEI', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if 128.0 > a then return 2 end end, 129, nil,
'LTI', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if -127.0 < a then return 2 end end, -127, nil,
'GTI', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if 10 < a then return 2 end end, 11, 2,
'GTI', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if 129 < a then return 2 end end, 130, 2,
'LOADI', 'LT', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if a >= 23.0 then return 2 end end, 25, 2,
'GEI', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if a >= 23.1 then return 2 end end, 0, nil,
'LOADK', 'LE', 'JMP', 'LOADI', 'RETURN1')
checkR(function (a) if a > 2300.0 then return 2 end end, 0, nil,
'LOADF', 'LT', 'JMP', 'LOADI', 'RETURN1')
local function checkK (func, val)
check(func, 'LOADK', 'RETURN1')
checkKlist(func, {val})
assert(func() == val)
end
local function checkI (func, val)
check(func, 'LOADI', 'RETURN1')
checkKlist(func, {})
assert(func() == val)
end
local function checkF (func, val)
check(func, 'LOADF', 'RETURN1')
checkKlist(func, {})
assert(func() == val)
end
checkF(function () return 0.0 end, 0.0)
checkI(function () return k0 end, 0)
checkI(function () return -k0//1 end, 0)
checkK(function () return 3^-1 end, 1/3)
checkK(function () return (1 + 1)^(50 + 50) end, 2^100)
checkK(function () return (-2)^(31 - 2) end, -0x20000000 + 0.0)
checkF(function () return (-k3^0 + 5) // 3.0 end, 1.0)
checkI(function () return -k3 % 5 end, 2)
checkF(function () return -((2.0^8 + -(-1)) % 8)/2 * 4 - 3 end, -5.0)
checkF(function () return -((2^8 + -(-1)) % 8)//2 * 4 - 3 end, -7.0)
checkI(function () return 0xF0.0 | 0xCC.0 ~ 0xAA & 0xFD end, 0xF4)
checkI(function () return ~(~kFF0 | kFF0) end, 0)
checkI(function () return ~~-1024.0 end, -1024)
checkI(function () return ((100 << k6) << -4) >> 2 end, 100)
local a = 17; local sbx = ((1 << a) - 1) >> 1
local border <const> = 65535
checkI(function () return border end, sbx)
checkI(function () return -border end, -sbx)
checkI(function () return border + 1 end, sbx + 1)
checkK(function () return border + 2 end, sbx + 2)
checkK(function () return -(border + 1) end, -(sbx + 1))
local border <const> = 65535.0
checkF(function () return border end, sbx + 0.0)
checkF(function () return -border end, -sbx + 0.0)
checkF(function () return border + 1 end, (sbx + 1.0))
checkK(function () return border + 2 end, (sbx + 2.0))
checkK(function () return -(border + 1) end, -(sbx + 1.0))
checkR(function (x) return x + k1 end, 10, 11, 'ADDI', 'MMBINI', 'RETURN1')
checkR(function (x) return x - 127 end, 10, -117, 'ADDI', 'MMBINI', 'RETURN1')
checkR(function (x) return 128 + x end, 0.0, 128.0,
'ADDI', 'MMBINI', 'RETURN1')
checkR(function (x) return x * -127 end, -1.0, 127.0,
'MULK', 'MMBINK', 'RETURN1')
checkR(function (x) return 20 * x end, 2, 40, 'MULK', 'MMBINK', 'RETURN1')
checkR(function (x) return x ^ -2 end, 2, 0.25, 'POWK', 'MMBINK', 'RETURN1')
checkR(function (x) return x / 40 end, 40, 1.0, 'DIVK', 'MMBINK', 'RETURN1')
checkR(function (x) return x // 1 end, 10.0, 10.0,
'IDIVK', 'MMBINK', 'RETURN1')
checkR(function (x) return x % (100 - 10) end, 91, 1,
'MODK', 'MMBINK', 'RETURN1')
checkR(function (x) return k1 << x end, 3, 8, 'SHLI', 'MMBINI', 'RETURN1')
checkR(function (x) return x << 127 end, 10, 0, 'SHRI', 'MMBINI', 'RETURN1')
checkR(function (x) return x << -127 end, 10, 0, 'SHRI', 'MMBINI', 'RETURN1')
checkR(function (x) return x >> 128 end, 8, 0, 'SHRI', 'MMBINI', 'RETURN1')
checkR(function (x) return x >> -127 end, 8, 0, 'SHRI', 'MMBINI', 'RETURN1')
checkR(function (x) return x & 1 end, 9, 1, 'BANDK', 'MMBINK', 'RETURN1')
checkR(function (x) return 10 | x end, 1, 11, 'BORK', 'MMBINK', 'RETURN1')
checkR(function (x) return -10 ~ x end, -1, 9, 'BXORK', 'MMBINK', 'RETURN1')
checkR(function (x) return x + 0.0 end, 1, 1.0, 'ADDK', 'MMBINK', 'RETURN1')
checkR(function (x) return x * -10000 end, 2, -20000,
'MULK', 'MMBINK', 'RETURN1')
checkR(function (x) return x ^ 0.5 end, 4, 2.0, 'POWK', 'MMBINK', 'RETURN1')
checkR(function (x) return x / 2.0 end, 4, 2.0, 'DIVK', 'MMBINK', 'RETURN1')
checkR(function (x) return x // 10000 end, 10000, 1,
'IDIVK', 'MMBINK', 'RETURN1')
checkR(function (x) return x % (100.0 - 10) end, 91, 1.0,
'MODK', 'MMBINK', 'RETURN1')
check(function () return -0.0 end, 'LOADF', 'UNM', 'RETURN1')
check(function () return k3/0 end, 'LOADI', 'DIVK', 'MMBINK', 'RETURN1')
check(function () return 0%0 end, 'LOADI', 'MODK', 'MMBINK', 'RETURN1')
check(function () return -4//0 end, 'LOADI', 'IDIVK', 'MMBINK', 'RETURN1')
check(function (x) return x >> 2.0 end, 'LOADF', 'SHR', 'MMBIN', 'RETURN1')
check(function (x) return x << 128 end, 'LOADI', 'SHL', 'MMBIN', 'RETURN1')
check(function (x) return x & 2.0 end, 'LOADF', 'BAND', 'MMBIN', 'RETURN1')
check(function () for i = -10, 10.5 do end end,
'LOADI', 'LOADK', 'LOADI', 'FORPREP', 'FORLOOP', 'RETURN0')
check(function () for i = 0xfffffff, 10.0, 1 do end end,
'LOADK', 'LOADF', 'LOADI', 'FORPREP', 'FORLOOP', 'RETURN0')
check(function () return -nil end, 'LOADNIL', 'UNM', 'RETURN1')
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',
'RETURN0')
do
local t
check(function () t[kx] = t.y end, 'GETTABUP', 'SETTABUP')
check(function (a) t[a()] = t[a()] end,
'MOVE', 'CALL', 'GETUPVAL', 'MOVE', 'CALL',
'GETUPVAL', 'GETTABLE', 'SETTABLE')
end
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)
check(function (a, b)
while a do
if b then break else a = a + 1 end
end
end,
'TEST', 'JMP', 'TEST', 'JMP', 'ADDI', 'MMBINI', 'JMP', 'RETURN0')
checkequal(function () return 6 or true or nil end,
function () return k6 or kTrue or kNil end)
checkequal(function () return 6 and true or nil end,
function () return k6 and kTrue or kNil end)
do
local k0 <const> = "00000000000000000000000000000000000000000000000000"
local function f1 ()
local k <const> = k0
return function ()
return function () return k end
end
end
local f2 = f1()
local f3 = f2()
assert(f3() == k0)
checkK(f3, k0)
assert(T.listk(f1)[1] == nil)
assert(T.listk(f2)[1] == nil)
end
print 'OK'
collectgarbage()
local function errmsg (code, m)
local st, msg = load(code)
assert(not st and string.find(msg, m))
end
-- cannot see label inside block
errmsg([[ goto l1; do ::l1:: end ]], "label 'l1'")
errmsg([[ do ::l1:: end goto l1; ]], "label 'l1'")
errmsg([[ ::l1:: ::l1:: ]], "label 'l1'")
errmsg([[ ::l1:: do ::l1:: end]], "label 'l1'")
errmsg([[ goto l1; local aa ::l1:: ::l2:: print(3) ]], "local 'aa'")
errmsg([[
do local bb, cc; goto l1; end
local aa
::l1:: print(3)
]], "local 'aa'")
errmsg([[ do ::l1:: end goto l1 ]], "label 'l1'")
errmsg([[ goto l1 do ::l1:: end ]], "label 'l1'")
errmsg([[
repeat
if x then goto cont end
local xuxu = 10
::cont::
until xuxu < x
]], "local 'xuxu'")
local x
do
local y = 12
goto l1
::l2:: x = x + 1; goto l3
::l1:: x = y; goto l2
end
::l3:: ::l3_1:: assert(x == 13)
do
local prog = [[
do
local a = 1
goto l%sa; a = a + 1
::l%sa:: a = a + 10
goto l%sb; a = a + 2
::l%sb:: a = a + 20
return a
end
]]
local label = string.rep("0123456789", 40)
prog = string.format(prog, label, label, label, label)
assert(assert(load(prog))() == 31)
end
do
goto l1
local a = 23
x = a
::l1::;
end
while true do
goto l4
goto l1
goto l1
local x = 45
::l1:: ;;;
end
::l4:: assert(x == 13)
if print then
goto l1
error("should not be here")
goto l2
local x
::l1:: ; ::l2:: ;;
else end
local function foo ()
local a = {}
goto l3
::l1:: a[#a + 1] = 1; goto l2;
::l2:: a[#a + 1] = 2; goto l5;
::l3::
::l3a:: a[#a + 1] = 3; goto l1;
::l4:: a[#a + 1] = 4; goto l6;
::l5:: a[#a + 1] = 5; goto l4;
::l6:: assert(a[1] == 3 and a[2] == 1 and a[3] == 2 and
a[4] == 5 and a[5] == 4)
if not a[6] then a[6] = true; goto l3a end
end
::l6:: foo()
do
local x
::L1::
local y
assert(y == nil)
y = true
if x == nil then
x = 1
goto L1
else
x = x + 1
end
assert(x == 2 and y == true)
end
do
local first = true
local a = false
if true then
goto LBL
::loop::
a = true
::LBL::
if first then
first = false
goto loop
end
end
assert(a)
end
do
goto escape
::a:: goto a
::b:: goto c
::c:: goto b
end
::escape::
local debug = require 'debug'
local function foo ()
local t = {}
do
local i = 1
local a, b, c, d
t[1] = function () return a, b, c, d end
::l1::
local b
do
local c
t[#t + 1] = function () return a, b, c, d end -- t[2], t[4], t[6]
if i > 2 then goto l2 end
do
local d
t[#t + 1] = function () return a, b, c, d end -- t[3], t[5]
i = i + 1
local a
goto l1
end
end
end
::l2:: return t
end
local a = foo()
assert(#a == 6)
for i = 2, 6 do
assert(debug.upvalueid(a[1], 1) == debug.upvalueid(a[i], 1))
end
for i = 2, 6 do
assert(debug.upvalueid(a[1], 2) ~= debug.upvalueid(a[i], 2))
assert(debug.upvalueid(a[1], 3) ~= debug.upvalueid(a[i], 3))
end
for i = 3, 5, 2 do
assert(debug.upvalueid(a[i], 2) == debug.upvalueid(a[i - 1], 2))
assert(debug.upvalueid(a[i], 3) == debug.upvalueid(a[i - 1], 3))
assert(debug.upvalueid(a[i], 2) ~= debug.upvalueid(a[i + 1], 2))
assert(debug.upvalueid(a[i], 3) ~= debug.upvalueid(a[i + 1], 3))
end
for i = 2, 6, 2 do
assert(debug.upvalueid(a[1], 4) == debug.upvalueid(a[i], 4))
end
for i = 3, 5, 2 do
for j = 1, 6 do
assert((debug.upvalueid(a[i], 4) == debug.upvalueid(a[j], 4))
== (i == j))
end
end
local function testG (a)
if a == 1 then
goto l1
error("should never be here!")
elseif a == 2 then goto l2
elseif a == 3 then goto l3
elseif a == 4 then
goto l1
error("should never be here!")
::l1:: a = a + 1
else
goto l4
::l4a:: a = a * 2; goto l4b
error("should never be here!")
::l4:: goto l4a
error("should never be here!")
::l4b::
end
do return a end
::l2:: do return "2" end
::l3:: do return "3" end
::l1:: return "1"
end
assert(testG(1) == "1")
assert(testG(2) == "2")
assert(testG(3) == "3")
assert(testG(4) == 5)
assert(testG(5) == 10)
do
local X
goto L1
::L2:: goto L3
::L1:: do
local a <close> = setmetatable({}, {__close = function () X = true end})
assert(X == nil)
if a then goto L2 end
end
::L3:: assert(X == true)
end
print'OK'