mirror of
https://github.com/lua/lua.git
synced 2025-01-14 05:43:00 +08:00
f53eabeed8
- LUAC_VERSION is equal to LUA_VERSION_NUM, and it is stored as an int. - 'sizeof(int)' and 'sizeof(size_t)' removed from the header, as the binary format does not depend on these sizes. (It uses its own serialization for unsigned integer values.)
434 lines
10 KiB
Lua
434 lines
10 KiB
Lua
-- $Id: testes/calls.lua $
|
|
-- See Copyright Notice in file all.lua
|
|
|
|
print("testing functions and calls")
|
|
|
|
local debug = require "debug"
|
|
|
|
-- get the opportunity to test 'type' too ;)
|
|
|
|
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))
|
|
function f (x) return a:x (x) end
|
|
assert(type(f) == 'function')
|
|
assert(not pcall(type))
|
|
|
|
|
|
do -- test error in 'print' too...
|
|
local tostring = _ENV.tostring
|
|
|
|
_ENV.tostring = nil
|
|
local st, msg = pcall(print, 1)
|
|
assert(st == false and string.find(msg, "attempt to call a nil value"))
|
|
|
|
_ENV.tostring = function () return {} end
|
|
local st, msg = pcall(print, 1)
|
|
assert(st == false and string.find(msg, "must return a string"))
|
|
|
|
_ENV.tostring = tostring
|
|
end
|
|
|
|
|
|
-- testing local-function recursion
|
|
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)
|
|
|
|
-- testing declarations
|
|
a = {i = 10}
|
|
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 -- 'declare' t
|
|
function f(a,b,c) local d = 'a'; t={a,b,c,d} end
|
|
|
|
f( -- this line change must be valid
|
|
1,2)
|
|
assert(t[1] == 1 and t[2] == 2 and t[3] == nil and t[4] == 'a')
|
|
f(1,2, -- this one too
|
|
3,4)
|
|
assert(t[1] == 1 and t[2] == 2 and t[3] == 3 and t[4] == 'a')
|
|
|
|
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')
|
|
a,b = a()
|
|
assert(a == 120 and b == 3)
|
|
print('+')
|
|
|
|
function err_on_n (n)
|
|
if n==0 then error(); exit(1);
|
|
else err_on_n (n-1); exit(1);
|
|
end
|
|
end
|
|
|
|
do
|
|
function dummy (n)
|
|
if n > 0 then
|
|
assert(not pcall(err_on_n, n))
|
|
dummy(n-1)
|
|
end
|
|
end
|
|
end
|
|
|
|
dummy(10)
|
|
|
|
function deep (n)
|
|
if n>0 then deep(n-1) end
|
|
end
|
|
deep(10)
|
|
deep(180)
|
|
|
|
-- 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 -- tail calls x varargs
|
|
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)
|
|
|
|
-- tail calls x metamethods
|
|
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
|
|
|
|
print('+')
|
|
|
|
|
|
a = nil
|
|
(function (x) a=x end)(23)
|
|
assert(a == 23 and (function (x) return x*2 end)(20) == 40)
|
|
|
|
|
|
-- testing closures
|
|
|
|
-- fixed-point operator
|
|
Z = function (le)
|
|
local function a (f)
|
|
return le(function (x) return f(f)(x) end)
|
|
end
|
|
return a(a)
|
|
end
|
|
|
|
|
|
-- non-recursive factorial
|
|
|
|
F = function (f)
|
|
return function (n)
|
|
if n == 0 then return 1
|
|
else return n*f(n-1) end
|
|
end
|
|
end
|
|
|
|
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
|
|
|
|
f = g(10)
|
|
assert(f(9, 16) == 10+11+12+13+10+9+16+10)
|
|
|
|
Z, F, f = nil
|
|
print('+')
|
|
|
|
-- testing multiple returns
|
|
|
|
function unlpack (t, i)
|
|
i = i or 1
|
|
if (i <= #t) then
|
|
return t[i], unlpack(t, i+1)
|
|
end
|
|
end
|
|
|
|
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
|
|
|
|
function f() return 1,2,30,4 end
|
|
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")
|
|
|
|
|
|
-- testing calls with 'incorrect' arguments
|
|
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")
|
|
|
|
|
|
-- test for generic load
|
|
local x = "-- a comment\0\0\0\n x = 10 + \n23; \
|
|
local a = function () x = 'hi' end; \
|
|
return '\0'"
|
|
function read1 (x)
|
|
local i = 0
|
|
return function ()
|
|
collectgarbage()
|
|
i=i+1
|
|
return string.sub(x, i, i)
|
|
end
|
|
end
|
|
|
|
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")
|
|
-- cannot read text in binary mode
|
|
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() -- empty chunk
|
|
|
|
assert(not load(function () return true end))
|
|
|
|
|
|
-- small bug
|
|
local t = {nil, "return ", "3"}
|
|
f, msg = load(function () return table.remove(t, 1) end)
|
|
assert(f() == nil) -- should read the empty chunk
|
|
|
|
-- another small bug (in 5.2.1)
|
|
f = load(string.dump(function () return 1 end), nil, "b", {})
|
|
assert(type(f) == "function" and f() == 1)
|
|
|
|
|
|
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"))
|
|
|
|
assert(not pcall(string.dump, print)) -- no dump of C functions
|
|
|
|
cannotload("unexpected symbol", load(read1("*a = 123")))
|
|
cannotload("unexpected symbol", load("*a = 123"))
|
|
cannotload("hhi", load(function () error("hhi") end))
|
|
|
|
-- any value is valid for _ENV
|
|
assert(load("return _ENV", nil, nil, 123)() == 123)
|
|
|
|
|
|
-- load when _ENV is not first upvalue
|
|
local x; XX = 123
|
|
local function h ()
|
|
local y=x -- use 'x', so that it becomes 1st upvalue
|
|
return XX -- global name
|
|
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)
|
|
|
|
|
|
-- test generic load with nested functions
|
|
x = [[
|
|
return function (x)
|
|
return function (y)
|
|
return function (z)
|
|
return x+y+z
|
|
end
|
|
end
|
|
end
|
|
]]
|
|
|
|
a = assert(load(read1(x)))
|
|
assert(a()(2)(3)(10) == 15)
|
|
|
|
|
|
-- test for dump/undump with upvalues
|
|
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)) -- only 2 upvalues
|
|
x("set")
|
|
assert(x() == 23)
|
|
x("set")
|
|
assert(x() == 24)
|
|
|
|
-- test for dump/undump with many upvalues
|
|
do
|
|
local nup = 200 -- maximum number of local variables
|
|
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)) -- main chunk now has many upvalues
|
|
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
|
|
|
|
-- test for long method names
|
|
do
|
|
local t = {x = 1}
|
|
function t:_012345678901234567890123456789012345678901234567890123456789 ()
|
|
return self.x
|
|
end
|
|
assert(t:_012345678901234567890123456789012345678901234567890123456789() == 1)
|
|
end
|
|
|
|
|
|
-- test for bug in parameter adjustment
|
|
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("c4BBBc6BBBj",
|
|
"\27Lua", -- signature
|
|
(504 >> 7) & 0x7f, (504 & 0x7f) | 0x80, -- version 5.4 (504)
|
|
0, -- format
|
|
"\x19\x93\r\n\x1a\n", -- data
|
|
4, -- size of instruction
|
|
string.packsize("j"), -- sizeof(lua integer)
|
|
string.packsize("n"), -- sizeof(lua number)
|
|
0x5678 -- LUAC_INT
|
|
-- LUAC_NUM may not have a unique binary representation (padding...)
|
|
)
|
|
local c = string.dump(function () local a = 1; local b = 3; return a+b*3 end)
|
|
|
|
assert(string.sub(c, 1, #header) == header)
|
|
|
|
-- corrupted header
|
|
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
|
|
|
|
-- loading truncated binary chunks
|
|
for i = 1, #c - 1 do
|
|
local st, msg = load(string.sub(c, 1, i))
|
|
assert(not st and string.find(msg, "truncated"))
|
|
end
|
|
assert(assert(load(c))() == 10)
|
|
end
|
|
|
|
print('OK')
|
|
return deep
|