local ffi = require("ffi")
dofile("../common/ffi_util.inc")
ffi.cdef[[
typedef struct { int x; } idx1_t;
typedef struct { int x; } idx2_t;
typedef struct { int x; } idx3_t;
typedef struct { int x,y; } arith_t;
typedef struct { void *p; } gc_t;
]]
local function ptreq(a, b)
return ffi.cast("void *", a) == ffi.cast("void *", b)
end
do
local nidx = {}
local tp = ffi.metatype("idx1_t", {
__index = { foo = 99, method = function(c, v) return v end },
__newindex = nidx,
})
fails(function() ffi.metatype("idx1_t", {}) end)
local s = tp(1234)
assert(s.foo == 99)
assert(s.x == 1234)
fails(function(s) local x = s.bar end, s)
assert(s:method(123) == 123)
s.bar = 42
assert(nidx.bar == 42)
local cs = ffi.new("const idx1_t", 9876)
assert(cs.foo == 99)
assert(cs.x == 9876)
fails(function(cs) cs.bar = 42 end, cs)
local cp = ffi.new("const idx1_t *", cs)
assert(cp.foo == 99)
assert(cp.x == 9876)
fails(function(cp) cp.bar = 42 end, cp)
end
do
local uc, uk, uv
local tp = ffi.metatype("idx2_t", {
__index = function(c, k, x, y)
assert(x == nil and y == nil)
uc, uk = c, k; return 99
end,
__newindex = function(c, k, v) uc, uk, uv = c, k, v end,
})
local s = tp(1234)
assert(s.foo == 99)
assert(ptreq(uc, s) and uk == "foo" and uv == nil); uc,uk,uv=nil,nil,nil
assert(s.x == 1234)
assert(uc == nil and uk == nil and uv == nil); uc,uk,uv=nil,nil,nil
s.bar = 42
assert(ptreq(uc, s) and uk == "bar" and uv == 42); uc,uk,uv=nil,nil,nil
s[10] = 11
assert(ptreq(uc, s) and uk == 10 and uv == 11); uc,uk,uv=nil,nil,nil
local p = ffi.new("idx2_t *", s)
assert(p.foo == 99)
assert(ptreq(uc, p) and uk == "foo" and uv == nil); uc,uk,uv=nil,nil,nil
assert(p.x == 1234)
assert(uc == nil and uk == nil and uv == nil); uc,uk,uv=nil,nil,nil
assert(ptreq(p[0], p))
assert(uc == nil and uk == nil and uv == nil); uc,uk,uv=nil,nil,nil
fails(function(p) p[0] = 11 end, p)
end
do
local uc, uk, uv
local ti, tn = {}, {}
local tp = ffi.metatype("idx3_t", {
__index = setmetatable(ti,
{ __index = function(c, k) uc, uk = c, k; return 99 end }),
__newindex = setmetatable(tn,
{ __newindex = function(c, k, v) uc, uk, uv = c, k, v end }),
})
local s = tp(1234)
assert(s.foo == 99)
assert(uc == ti and uk == "foo" and uv == nil)
uc, uk, uv = nil, nil, nil
assert(s.x == 1234)
assert(uc == nil and uk == nil and uv == nil)
s.bar = 42
assert(uc == tn and uk == "bar" and uv == 42)
uc, uk, uv = nil, nil, nil
s[10] = 11
assert(uc == tn and uk == 10 and uv == 11)
uc, uk, uv = nil, nil, nil
end
do
local tp
tp = ffi.metatype("arith_t", {
__add = function(a, b) return tp(a.x+b.x, a.y+b.y) end,
__sub = function(a, b) return tp(a.x-b.x, a.y-b.y) end,
__mul = function(a, z) return tp(a.x*z, a.y*z) end,
__div = function(z, a) return tp(a.x*z, a.y*z) end,
__concat = setmetatable({}, { __call = function(x) return 99 end }),
__len = function(x) return 2 end,
__call = function(a) return a.x+a.y end,
__tostring = function(a) return "foo" end,
__newindex = function(a, k, v) a.y = v end,
__index = {
diff = function(a) return a.x-a.y end,
},
})
local a = tp(10, 20)
local b = tp(1, 2)
local c = a + b
assert(c.x == 11 and c.y == 22)
assert(c:diff() == -11)
assert(c() == 33)
local d = a - b
assert(d.x == 9 and d.y == 18)
assert(d:diff() == -9)
assert(d() == 27)
local e = a * 3
assert(e.x == 30 and e.y == 60)
local f = 3LL / a
assert(f.x == 30 and f.y == 60)
assert(1 .. c == 99)
assert(c .. 1 == 99)
assert(c .. d == 99)
assert(tostring(c) == "foo")
assert(tostring(ffi.cast("arith_t *", c)) == "foo")
c.foo = 42
assert(c.y == 42)
local p = ffi.new("arith_t *", a)
local g1 = p + p
assert(g1.x == 20 and g1.y == 40)
local g2 = p[0] + p[0]
assert(g2.x == 20 and g2.y == 40)
assert(p() == 30)
local q = ffi.new("arith_t &", a)
fails(function(p) local y = q[0] + q[0] end, q)
local h = q + q
assert(h.x == 20 and h.y == 40)
local diff = 0
for i=1,100 do diff = a:diff() end
assert(diff == -10)
for i=1,100 do c.foo = i end
assert(c.y == 100)
local z = tp(1, 3)
for i=1,100 do z = z + a end
assert(z.x == 1001 and z.y == 2003)
local x = 0
for i=1,100 do x = x + #a end
assert(x == 200)
local x = 0
for i=1,100 do x = x + p() end
assert(x == 3000)
end
do
local count = 0
local tp = ffi.metatype("gc_t", {
__gc = function(x) count = count + 1 end,
})
local a = tp()
a = nil
collectgarbage()
assert(count == 1)
local b,c = tp(), tp()
b = nil
collectgarbage()
assert(count == 2)
c = nil
collectgarbage()
assert(count == 3)
local z
for i=1,100 do z = tp() end
z = nil
collectgarbage()
assert(count == 103)
local t = {}
for i=1,100 do t[i] = tp() end
for i=1,100 do ffi.gc(t[i], nil) end
t = nil
collectgarbage()
assert(count == 103)
end
do
local tp = ffi.metatype([[
struct {
static const int Z42 = 42;
enum { Z39 = 39 };
int x;
}]], {
__new = function(tp, x)
return ffi.new(tp, x or -1)
end,
__index = { test = function(x) return x+1 end, x = "hello" }
})
assert(tp.Z42 == 42)
assert(tp.Z39 == 39)
assert(tp.test(99) == 100)
fails(function() tp.Z42 = 1 end)
fails(function() tp.Z39 = 1 end)
assert(tp.x == "hello") fails(function() tp.x = 1 end)
local o = tp()
assert(o.Z42 == 42)
assert(o.Z39 == 39)
assert(o.test(55) == 56)
fails(function() o.Z42 = 1 end)
fails(function() o.Z39 = 1 end)
assert(o.x == -1)
o.x = 5
assert(o.x == 5)
end
do
local fb = ffi.new("struct { int x; }", 99)
local xt = ffi.metatype("struct { }", { __index = fb })
local o = xt()
assert(o.x == 99)
end