v4k-git-backup/tools/luaffi/test.lua

891 lines
26 KiB
Lua

-- vim: ts=4 sw=4 sts=4 et tw=78
-- Copyright (c) 2011 James R. McKaskill. See license in ffi.h
io.stdout:setvbuf('no')
local ffi = require 'ffi'
local dlls = {}
local num_ok = 0
local num_err = 0
local assert = function(a, hint1, hint2)
num_ok = (num_ok or 0) + (a == true and 1 or 0)
num_err = (num_err or 0) + (a ~= true and 1 or 0)
if a ~= true then
print('F'..num_err..'/T'..(num_ok+num_err), 'L'..debug.getinfo(2).currentline, debug.getinfo(2).name or '', hint1 or '', hint2 or '')
end
--return _G.assert(a)
end
dlls.__cdecl = ffi.load('test_cdecl')
if ffi.arch == 'x86' and ffi.os == 'Windows' then
dlls.__stdcall = ffi.load('test_stdcall')
dlls.__fastcall = ffi.load('test_fastcall')
end
local function check(a, b)
return assert(a == b, a, b)
end
print('Running test')
ffi.cdef [[
enum e8 {
FOO8,
BAR8,
};
enum e16 {
FOO16 = 1 << 8,
BAR16,
BIG16 = 1 << 14,
};
enum e32 {
FOO32 = 1 << 16,
BAR32,
BIG32 = 1 << 30,
};
int max_alignment();
bool is_msvc, is_msvc2 __asm__("is_msvc");
bool have_complex(void);
bool have_complex2() __asm__("have" /*foo*/ "\x5F" "complex"); // 5F is _
int8_t add_i8(int8_t a, int8_t b);
uint8_t add_u8(uint8_t a, uint8_t b);
int16_t add_i16(int16_t a, int16_t b);
uint16_t add_i16(uint16_t a, uint16_t b);
int32_t add_i32(int32_t a, int32_t b);
uint32_t add_u32(uint32_t a, uint32_t b);
int64_t add_i64(int64_t a, int64_t b);
uint64_t add_u64(uint64_t a, uint64_t b);
double add_d(double a, double b);
float add_f(float a, float b);
double complex add_dc(double complex a, double complex b);
float complex add_fc(float complex a, float complex b);
enum e8 inc_e8(enum e8);
enum e16 inc_e16(enum e16);
enum e32 inc_e32(enum e32);
bool not_b(bool v);
_Bool not_b2(_Bool v);
typedef bool (*fp)(bool);
fp ret_fp(fp v);
bool (*ret_fp2(bool (*)(bool)))(bool) __asm("ret_fp");
int print_i8(char* buf, int8_t val);
int print_u8(char* buf, uint8_t val);
int print_i16(char* buf, int16_t val);
int print_u16(char* buf, uint16_t val);
int print_i32(char* buf, int32_t val);
int print_u32(char* buf, uint32_t val);
int print_i64(char* buf, int64_t val);
int print_u64(char* buf, uint64_t val);
int print_s(char* buf, const char* val);
int print_b(char* buf, bool val);
int print_b2(char* buf, _Bool val);
int print_d(char* buf, double val);
int print_f(char* buf, float val);
int print_p(char* buf, void* val);
int print_dc(char* buf, double complex val);
int print_fc(char* buf, float complex val);
int print_e8(char* buf, enum e8 val);
int print_e16(char* buf, enum e16 val);
int print_e32(char* buf, enum e32 val);
int sprintf(char* buf, const char* format, ...);
// Examples from MSDN
// bit_fields1.cpp
// compile with: /LD
struct Date {
unsigned short nWeekDay : 3; // 0..7 (3 bits)
unsigned short nMonthDay : 6; // 0..31 (6 bits)
unsigned short nMonth : 5; // 0..12 (5 bits)
unsigned short nYear : 8; // 0..100 (8 bits)
};
// bit_fields2.cpp
// compile with: /LD
struct Date2 {
unsigned nWeekDay : 3; // 0..7 (3 bits)
unsigned nMonthDay : 6; // 0..31 (6 bits)
unsigned : 0; // Force alignment to next boundary.
unsigned nMonth : 5; // 0..12 (5 bits)
unsigned nYear : 8; // 0..100 (8 bits)
};
// For checking the alignment of short bitfields
struct Date3 {
char pad;
unsigned short nWeekDay : 3; // 0..7 (3 bits)
unsigned short nMonthDay : 6; // 0..31 (6 bits)
unsigned short nMonth : 5; // 0..12 (5 bits)
unsigned short nYear : 8; // 0..100 (8 bits)
};
// For checking the alignment and container of int64 bitfields
struct bit64 {
char pad;
uint64_t a : 15;
uint64_t b : 14;
uint64_t c : 13;
uint64_t d : 12;
};
// Examples from SysV X86 ABI
struct sysv1 {
int j:5;
int k:6;
int m:7;
};
struct sysv2 {
short s:9;
int j:9;
char c;
short t:9;
short u:9;
char d;
};
struct sysv3 {
char c;
short s:8;
};
union sysv4 {
char c;
short s:8;
};
struct sysv5 {
char c;
int :0;
char d;
short :9;
char e;
char :0;
};
struct sysv6 {
char c;
int :0;
char d;
int :9;
char e;
};
struct sysv7 {
int j:9;
short s:9;
char c;
short t:9;
short u:9;
};
int print_date(size_t* sz, size_t* align, char* buf, struct Date* s);
int print_date2(size_t* sz, size_t* align, char* buf, struct Date2* s);
int print_date3(size_t* sz, size_t* align, char* buf, struct Date3* d);
int print_bit64(size_t* sz, size_t* align, char* buf, struct bit64* d);
int print_sysv1(size_t* sz, size_t* align, char* buf, struct sysv1* s);
int print_sysv2(size_t* sz, size_t* align, char* buf, struct sysv2* s);
int print_sysv3(size_t* sz, size_t* align, char* buf, struct sysv3* s);
int print_sysv4(size_t* sz, size_t* align, char* buf, union sysv4* s);
int print_sysv5(size_t* sz, size_t* align, char* buf, struct sysv5* s);
int print_sysv6(size_t* sz, size_t* align, char* buf, struct sysv6* s);
int print_sysv7(size_t* sz, size_t* align, char* buf, struct sysv7* s);
struct fptr {
int (__cdecl *p)(int);
};
int call_fptr(struct fptr* s, int val);
bool g_b;
int8_t g_i8;
int16_t g_i16;
int32_t g_i32;
int64_t g_i64;
uint8_t g_u8;
uint16_t g_u16;
uint32_t g_u32;
uint64_t g_u64;
float g_f;
double g_d;
double complex g_dc;
float complex g_fc;
bool (*g_fp)(bool);
const char g_s[];
const char* g_sp;
void* g_p;
enum e8 g_e8;
enum e16 g_e16;
enum e32 g_e32;
struct Date g_date;
void set_errno(int val);
int get_errno(void);
]]
local align = [[
struct align_ALIGN_SUFFIX {
char pad;
TYPE v;
};
int print_align_ALIGN_SUFFIX(char* buf, struct align_ALIGN_SUFFIX* p);
]]
local palign = [[
#pragma pack(push)
#pragma pack(ALIGN)
]] .. align .. [[
#pragma pack(pop)
]]
local bitfields = [[
struct bcTNUM {
uintTNUM_t a : 3;
intTNUM_t b : 3;
};
struct blzTNUM {
uintTNUM_t a;
uintTNUM_t :0;
uintTNUM_t b;
};
int print_bcTNUM(size_t* sz, size_t* align, char* buf, struct bcTNUM* s);
int print_blzTNUM(size_t* sz, size_t* align, char* buf, struct blzTNUM* s);
]]
local bitalign = [[
struct ba_TNUM_BNUM {
char a;
uintTNUM_t b : BNUM;
};
struct bu_TNUM_BNUM {
char a;
uintTNUM_t :BNUM;
char b;
};
int print_ba_TNUM_BNUM(size_t* sz, size_t* align, char* buf, struct ba_TNUM_BNUM* s);
]]
local bitzero = [[
struct bz_TNUM_ZNUM_BNUM {
uint8_t a;
uintTNUM_t b : 3;
uintZNUM_t :BNUM;
uintTNUM_t c : 3;
};
int print_bz_TNUM_ZNUM_BNUM(size_t* sz, size_t* align, char* buf, struct bz_TNUM_ZNUM_BNUM* s);
]]
local i = ffi.C.i
local test_values = {
['void*'] = ffi.new('char[3]'),
['const char*'] = 'foo',
float = 3.4,
double = 5.6,
uint16_t = 65000,
uint32_t = ffi.new('uint32_t', 700000056),
uint64_t = 12345678901234,
bool = true,
_Bool = false,
['float complex'] = 3+4*i,
['double complex'] = 5+6*i,
['enum e8'] = ffi.C.FOO8,
['enum e16'] = ffi.C.FOO16,
['enum e32'] = ffi.C.FOO32,
}
local types = {
b = 'bool',
b2 = '_Bool',
d = 'double',
f = 'float',
u64 = 'uint64_t',
u32 = 'uint32_t',
u16 = 'uint16_t',
s = 'const char*',
p = 'void*',
e8 = 'enum e8',
e16 = 'enum e16',
e32 = 'enum e32',
}
local buf = ffi.new('char[256]')
local function checkbuf(kind, ret)
local str = tostring(test_values[kind]):gsub('^cdata%b<>: ', '')
if type(test_values[kind])=='number' and tonumber(str) % 1 == 0 then -- if not decimal place...
str = tostring(math.floor(tonumber(str)))
end
check(ffi.string(buf), str)
check(ret, #str)
end
local function checkalign(kind, v, ret)
--print(v)
local str = tostring(test_values[kind]):gsub('^cdata%b<>: ', '')
if type(test_values[kind])=='number' and tonumber(str) % 1 == 0 then -- if not decimal place...
str = tostring(math.floor(tonumber(str)))
end
check(ffi.string(buf), ('size %d offset %d align %d value %s'):format(ffi.sizeof(v), ffi.offsetof(v, 'v'), ffi.alignof(v, 'v'), str))
check(ret, #str)
end
local u64 = ffi.typeof('uint64_t')
local i64 = ffi.typeof('int64_t')
local first = true
for convention,c in pairs(dlls) do
check(c.add_i8(1,1), 2)
check(c.add_i8(256,1), 1)
check(c.add_i8(127,1), -128)
check(c.add_i8(-120,120), 0)
check(c.add_u8(255,1), 0)
check(c.add_u8(120,120), 240)
check(c.add_i16(2000,4000), 6000)
check(c.add_d(20, 12), 32)
check(c.add_f(40, 32), 72)
check(c.not_b(true), false)
check(c.not_b2(false), true)
check(c.inc_e8(c.FOO8), c.BAR8)
check(c.inc_e8('FOO8'), c.BAR8)
check(c.inc_e16(c.FOO16), c.BAR16)
check(c.inc_e32(c.FOO32), c.BAR32)
check(c.ret_fp(c.g_fp), c.g_fp)
check(c.ret_fp2(c.g_fp), c.g_fp)
if c.have_complex() then
check(c.add_dc(3+4*i, 4+5*i), 7+9*i)
check(c.add_fc(2+4*i, 6+8*i), 8+12*i)
types.dc = 'double complex'
types.fc = 'float complex'
else
types.dc = nil
types.fc = nil
end
check((3+4*i).re, 3)
check((3+4*i).im, 4)
check(ffi.new('complex float', 2+8*i).re, 2)
check(ffi.new('complex float', 5+6*i).im, 6)
check(c.have_complex(), c.have_complex2())
check(c.is_msvc, c.is_msvc2)
check(c.g_b, true)
check(c.g_i8, -8)
check(c.g_i16, -16)
check(c.g_i32, -32)
check(c.g_i64, i64(-64))
check(c.g_u8, 8)
check(c.g_u16, 16)
check(c.g_u32, 32)
check(c.g_u64, u64(64))
check(c.g_f, 3)
check(c.g_d, 5)
if c.have_complex() then
check(c.g_dc, 7 + 8*i)
check(c.g_fc, 6 + 9*i)
end
check(ffi.cast('void*', c.g_fp), c.g_p)
check(c.g_s, 'g_s')
check(c.g_sp, 'g_sp')
check(c.g_e8, c.FOO8)
check(c.g_e16, c.FOO16)
check(c.g_e32, c.FOO32)
check(c.g_date.nWeekDay, 1)
check(c.g_date.nMonthDay, 2)
check(c.g_date.nMonth, 3)
check(c.g_date.nYear, 4)
c.g_b = false; check(c.g_b, false)
c.g_i8 = -108; check(c.g_i8, -108)
c.g_i16 = -1016; check(c.g_i16, -1016)
c.g_i32 = -1032; check(c.g_i32, -1032)
c.g_i64 = -1064; check(c.g_i64, i64(-1064))
c.g_u8 = 208; check(c.g_u8, 208)
c.g_u16 = 2016; check(c.g_u16, 2016)
c.g_u32 = 2032; check(c.g_u32, 2032)
c.g_u64 = 2064; check(c.g_u64, u64(2064))
c.g_f = 13; check(c.g_f, 13)
c.g_d = 15; check(c.g_d, 15)
if c.have_complex() then
c.g_dc = 17+18*i; check(c.g_dc, 17+18*i)
c.g_fc = 16+19*i; check(c.g_fc, 16+19*i)
end
c.g_sp = 'foo'; check(c.g_sp, 'foo')
c.g_e8 = c.BAR8; check(c.g_e8, c.BAR8)
c.g_e16 = c.BAR16; check(c.g_e16, c.BAR16)
c.g_e32 = c.BAR32; check(c.g_e32, c.BAR32)
c.g_date.nWeekDay = 3; check(c.g_date.nWeekDay, 3)
local align_attr = c.is_msvc and [[
struct align_attr_ALIGN_SUFFIX {
char pad;
__declspec(align(ALIGN)) TYPE v;
};
int print_align_attr_ALIGN_SUFFIX(char* buf, struct align_attr_ALIGN_SUFFIX* p);
]] or [[
struct align_attr_ALIGN_SUFFIX {
char pad;
TYPE v __attribute__(aligned(ALIGN));
};
int print_align_attr_ALIGN_SUFFIX(char* buf, struct align_attr_ALIGN_SUFFIX* p);
]]
for suffix, type in pairs(types) do
local test = test_values[type]
--print('checkbuf', suffix, type, buf, test)
checkbuf(type, c['print_' .. suffix](buf, test))
if first then
ffi.cdef(align:gsub('SUFFIX', suffix):gsub('TYPE', type):gsub('ALIGN', 0))
end
local v = ffi.new('struct align_0_' .. suffix, {0, test})
checkalign(type, v, c['print_align_0_' .. suffix](buf, v))
for _,align in ipairs{1,2,4,8,16} do
if align > c.max_alignment() then
break
end
if first then
ffi.cdef(palign:gsub('SUFFIX', suffix):gsub('TYPE', type):gsub('ALIGN', align))
ffi.cdef(align_attr:gsub('SUFFIX', suffix):gsub('TYPE', type):gsub('ALIGN', align))
end
local v = ffi.new('struct align_' .. align .. '_' .. suffix, {0, test})
checkalign(type, v, c['print_align_' .. align .. '_' .. suffix](buf, v))
-- MSVC doesn't support aligned attributes on enums
if not type:match('^enum e[0-9]*$') or not c.is_msvc then
local v2 = ffi.new('struct align_attr_' .. align .. '_' .. suffix, {0, test})
checkalign(type, v2, c['print_align_attr_' .. align .. '_' .. suffix](buf, v2))
end
end
if not c.is_msvc then
if first then
local h = [[
struct align_attr_def_SUFFIX {
char pad;
TYPE v __attribute__(aligned);
};
int print_align_attr_def_SUFFIX(char* buf, struct align_attr_def_SUFFIX* p);
]]
ffi.cdef(h:gsub('SUFFIX', suffix):gsub('TYPE', type))
end
local v = ffi.new('struct align_attr_def_' .. suffix, {0, test})
checkalign(type, v, c['print_align_attr_def_' .. suffix](buf, v))
end
end
local psz = ffi.new('size_t[1]')
local palign = ffi.new('size_t[1]')
local function check_align(type, test, ret)
--print('check_align', type, test, ret, ffi.string(buf), psz[0], palign[0])
check(tonumber(palign[0]), ffi.alignof(type))
check(tonumber(psz[0]), ffi.sizeof(type))
check(ret, #test)
check(test, ffi.string(buf))
end
for _, tnum in ipairs{8, 16, 32, 64} do
if first then
ffi.cdef(bitfields:gsub('TNUM',tnum))
end
check_align('struct bc'..tnum, '1 2', c['print_bc'..tnum](psz, palign, buf, {1,2}))
check_align('struct blz'..tnum, '1 2', c['print_blz'..tnum](psz, palign, buf, {1,2}))
for _, znum in ipairs{8, 16, 32, 64} do
for _, bnum in ipairs{7, 15, 31, 63} do
if bnum > znum then
break
end
if first then
ffi.cdef(bitzero:gsub('TNUM',tnum):gsub('ZNUM',znum):gsub('BNUM', bnum))
end
check_align('struct bz_'..tnum..'_'..znum..'_'..bnum, '1 2 3', c['print_bz_'..tnum..'_'..znum..'_'..bnum](psz, palign, buf, {1,2,3}))
end
end
for _, bnum in ipairs{7, 15, 31, 63} do
if bnum > tnum then
break
end
if first then
ffi.cdef(bitalign:gsub('TNUM',tnum):gsub('BNUM',bnum))
end
check_align('struct ba_'..tnum..'_'..bnum, '1 2', c['print_ba_'..tnum..'_'..bnum](psz, palign, buf, {1,2}))
end
end
check_align('struct Date', '1 2 3 4', c.print_date(psz, palign, buf, {1,2,3,4}))
check_align('struct Date2', '1 2 3 4', c.print_date2(psz, palign, buf, {1,2,3,4}))
check_align('struct sysv1', '1 2 3', c.print_sysv1(psz, palign, buf, {1,2,3}))
check_align('struct sysv2', '1 2 3 4 5 6', c.print_sysv2(psz, palign, buf, {1,2,3,4,5,6}))
check_align('struct sysv3', '1 2', c.print_sysv3(psz, palign, buf, {1,2}))
check_align('union sysv4', '1', c.print_sysv4(psz, palign, buf, {1}))
check_align('struct sysv5', '1 2 3', c.print_sysv5(psz, palign, buf, {1,2,3}))
check_align('struct sysv6', '1 2 3', c.print_sysv6(psz, palign, buf, {1,2,3}))
check_align('struct sysv7', '1 2 3 4 5', c.print_sysv7(psz, palign, buf, {1,2,3,4,5}))
local cbs = [[
typedef const char* (*__cdecl sfunc)(const char*);
int call_i(int (*__cdecl func)(int), int arg);
float call_f(float (*__cdecl func)(float), float arg);
double call_d(double (*__cdecl func)(double), double arg);
const char* call_s(sfunc func, const char* arg);
_Bool call_b(_Bool (*__cdecl func)(_Bool), _Bool arg);
double complex call_dc(double complex (*__cdecl func)(double complex), double complex arg);
float complex call_fc(float complex (*__cdecl func)(float complex), float complex arg);
enum e8 call_e8(enum e8 (*__cdecl func)(enum e8), enum e8 arg);
enum e16 call_e16(enum e16 (*__cdecl func)(enum e16), enum e16 arg);
enum e32 call_e32(enum e32 (*__cdecl func)(enum e32), enum e32 arg);
]]
ffi.cdef(cbs:gsub('__cdecl', convention))
local u3 = ffi.new('uint64_t', 3)
check(c.call_i(function(a) return 2*a end, 3), 6)
assert(math.abs(c.call_d(function(a) return 2*a end, 3.2) - 6.4) < 0.0000000001)
assert(math.abs(c.call_f(function(a) return 2*a end, 3.2) - 6.4) < 0.000001)
check(ffi.string(c.call_s(function(s) return s + u3 end, 'foobar')), 'bar')
check(c.call_b(function(v) return not v end, true), false)
check(c.call_e8(function(v) return v + 1 end, c.FOO8), c.BAR8)
check(c.call_e16(function(v) return v + 1 end, c.FOO16), c.BAR16)
check(c.call_e32(function(v) return v + 1 end, c.FOO32), c.BAR32)
if c.have_complex() then
check(c.call_dc(function(v) return v + 2+3*i end, 4+6*i), 6+9*i)
check(c.call_fc(function(v) return v + 1+2*i end, 7+4*i), 8+6*i)
end
local u2 = ffi.new('uint64_t', 2)
local cb = ffi.new('sfunc', function(s) return s + u3 end)
check(ffi.string(cb('foobar')), 'bar')
check(ffi.string(c.call_s(cb, 'foobar')), 'bar')
cb:set(function(s) return s + u2 end)
check(ffi.string(c.call_s(cb, 'foobar')), 'obar')
local fp = ffi.new('struct fptr')
assert(fp.p == ffi.C.NULL)
fp.p = function(a) return 2*a end
assert(fp.p ~= ffi.C.NULL)
check(c.call_fptr(fp, 4), 8)
local suc, err = pcall(function() fp.p:set(function() end) end)
assert(not suc)
check(err:gsub('^.*: ',''), "can't set the function for a non-lua callback")
check(c.call_fptr({function(a) return 3*a end}, 5), 15)
local suc, err = pcall(c.call_s, function(s) error(ffi.string(s), 0) end, 'my error')
check(suc, false)
check(err, 'my error')
check(ffi.errno(), c.get_errno())
c.set_errno(3)
check(ffi.errno(), 3)
check(c.get_errno(), 3)
check(ffi.errno(4), 3)
check(ffi.errno(), 4)
check(c.get_errno(), 4)
local gccattr = {
__cdecl = 'int test_pow(int v) __attribute__((cdecl));',
__stdcall = 'int test_pow(int v) __attribute__(stdcall);',
__fastcall = '__attribute__(fastcall) int test_pow(int v);',
}
ffi.cdef(gccattr[convention])
check(c.test_pow(5), 25)
ffi.cdef [[
int va_list_size, va_list_align;
int vsnprintf(char* buf, size_t sz, const char* fmt, va_list ap);
]]
ffi.new('va_list')
assert(ffi.debug().functions.vsnprintf ~= nil)
assert(ffi.istype('va_list', ffi.new('__builtin_va_list')))
assert(ffi.istype('va_list', ffi.new('__gnuc_va_list')))
check(ffi.sizeof('va_list'), c.va_list_size)
check(ffi.alignof('va_list'), c.va_list_align)
first = false
end
local c = ffi.C
if ffi.os ~= 'Windows' then
assert(c.sprintf(buf, "%g", 5.3) == 3 and ffi.string(buf) == '5.3')
assert(c.sprintf(buf, "%d", false) == 1 and ffi.string(buf) == '0')
assert(c.sprintf(buf, "%d%g", false, 6.7) == 4 and ffi.string(buf) == '06.7')
end
assert(ffi.sizeof('uint32_t[?]', 32) == 32 * 4)
assert(ffi.sizeof(ffi.new('uint32_t[?]', 32)) == 32 * 4)
ffi.cdef [[
struct vls {
struct {
char a;
struct {
char b;
char v[?];
} c;
} d;
};
struct vls2 {
char pad;
union {
uint8_t a;
uint16_t b;
};
};
]]
assert(ffi.sizeof('struct vls', 3) == 5)
assert(ffi.sizeof(ffi.new('struct vls', 4).d.c) == 5)
assert(ffi.offsetof('struct vls2', 'a') == 2)
assert(ffi.sizeof('struct vls2') == 4)
ffi.cdef [[ static const int DUMMY = 8 << 2; ]]
assert(ffi.C.DUMMY == 32)
ffi.new('struct {const char* foo;}', {'foo'})
assert(not pcall(function()
ffi.new('struct {char* foo;}', {'ff'})
end))
local mt = {}
local vls = ffi.new(ffi.metatype('struct vls', mt), 1)
assert(not pcall(function() return vls.key end))
mt.__index = function(vls, key)
return function(vls, a, b)
return 'in index ' .. key .. ' ' .. vls.d.a .. ' ' .. a .. ' ' .. b
end
end
vls.d.a = 3
check(vls:key('a', 'b'), 'in index key 3.0 a b')
assert(not pcall(function() vls.k = 3 end))
mt.__newindex = function(vls, key, val)
error('in newindex ' .. key .. ' ' .. vls.d.a .. ' ' .. val, 0)
end
vls.d.a = 4
local suc, err = pcall(function() vls.key = 'val' end)
assert(not suc)
check(err, 'in newindex key 4.0 val')
mt.__add = function(vls, a) return vls.d.a + a end
mt.__sub = function(vls, a) return vls.d.a - a end
mt.__mul = function(vls, a) return vls.d.a * a end
mt.__div = function(vls, a) return vls.d.a / a end
mt.__mod = function(vls, a) return vls.d.a % a end
mt.__pow = function(vls, a) return vls.d.a ^ a end
mt.__eq = function(vls, a) return u64(vls.d.a) == a end
mt.__lt = function(vls, a) return u64(vls.d.a) < a end
mt.__le = function(vls, a) return u64(vls.d.a) <= a end
mt.__call = function(vls, a, b) return '__call', vls.d.a .. a .. (b or 'nil') end
mt.__unm = function(vls) return -vls.d.a end
mt.__concat = function(vls, a) return vls.d.a .. a end
mt.__len = function(vls) return vls.d.a end
mt.__tostring = function(vls) return 'string ' .. vls.d.a end
vls.d.a = 5
check(vls + 5, 10)
check(vls - 5, 0)
check(vls * 5, 25)
check(vls / 5, 1)
check(vls % 3, 2)
check(vls ^ 3, 125)
check(vls == u64(4), false)
check(vls == u64(5), true)
check(vls == u64(6), false)
check(vls < u64(4), false)
check(vls < u64(5), false)
check(vls < u64(6), true)
check(vls <= u64(4), false)
check(vls <= u64(5), true)
check(vls <= u64(6), true)
check(-vls, -5)
local a,b = vls('6')
check(a, '__call')
check(b, '5.06nil')
check(tostring(vls), 'string 5.0')
if _VERSION ~= 'Lua 5.1' then
check(vls .. 'str', '5.0str')
check(#vls, 5)
end
check(tostring(1+3*i), '1.0+3.0i')
check(tostring((1+3*i)*(2+4*i)), '-10.0+10.0i')
check(tostring((3+2*i)*(3-2*i)), '13.0')
-- Should ignore unknown attributes
ffi.cdef [[
typedef int ALenum;
__attribute__((dllimport)) void __attribute__((__cdecl__)) alEnable( ALenum capability );
]]
check(ffi.sizeof('struct {char foo[alignof(uint64_t)];}'), ffi.alignof('uint64_t'))
-- Long double is not supported yet but it should be parsed
ffi.cdef('long double foo(long double val);')
check(tostring(ffi.debug().functions.foo):match('ctype(%b<>)'), '<long double (*)(long double)>')
ffi.cdef [[
typedef int byte1 __attribute__(mode(QI));
typedef int byte2 __attribute__(mode(HI));
typedef int byte4 __attribute__(mode(SI));
typedef int byte8 __attribute__(mode(DI));
typedef unsigned ubyte8 __attribute__(mode(DI));
typedef int word __attribute__(mode(word));
typedef int pointer __attribute__(mode(pointer));
typedef int byte __attribute__(mode(byte));
typedef float float4 __attribute__(mode(SF));
typedef float float8 __attribute__(mode(DF));
]]
assert(ffi.istype('int8_t', ffi.new('byte1')))
assert(ffi.istype('int16_t', ffi.new('byte2')))
assert(ffi.istype('int32_t', ffi.new('byte4')))
assert(ffi.istype('int64_t', ffi.new('byte8')))
assert(ffi.istype('uint64_t', ffi.new('ubyte8')))
check(ffi.sizeof('void*'), ffi.sizeof('pointer'))
check(ffi.alignof('void*'), ffi.alignof('pointer'))
check(ffi.sizeof('void*'), ffi.sizeof('word'))
check(ffi.alignof('void*'), ffi.alignof('word'))
assert(ffi.istype('int8_t', ffi.new('byte')))
assert(ffi.istype('float', ffi.new('float4')))
assert(ffi.istype('double', ffi.new('float8')))
ffi.cdef('void register_foo(register int val);')
check(tostring(ffi.debug().functions.register_foo):match('%b<>'), '<void (*)(int)>')
ffi.cdef [[
typedef struct __sFILE FILE;
FILE *fopen(const char * , const char * ) __asm("_" "fopen" );
]]
assert(not ffi.istype('int', ffi.new('int*')))
assert(not ffi.istype('int[]', ffi.new('int*')))
assert(not ffi.istype('int[3]', ffi.new('int*')))
assert(not ffi.istype('int[3]', ffi.new('int[2]')))
assert(ffi.istype('const int[3]', ffi.new('const int[3]')))
assert(ffi.istype('int[3]', ffi.new('const int[3]')))
-- Crazy function pointer that takes an int and a function pointer and returns
-- a function pointer. Type of &signal.
check(tostring(ffi.typeof('void (*foo(int, void(*)(int)))(int)')):match('%b<>'), '<void (*(*)(int, void (*)(int)))(int)>')
-- Make sure we pass all arguments to tonumber
check(tonumber('FE', 16), 0xFE)
-- Allow casts from pointer to numeric types
ffi.cast('long', ffi.C.NULL)
ffi.cast('int8_t', ffi.C.NULL)
assert(not pcall(function() ffi.new('long', ffi.C.NULL) end))
-- ffi.new and ffi.cast allow unpacked struct/arrays
assert(ffi.new('int[3]', 1)[0] == 1)
assert(ffi.new('int[3]', {1})[0] == 1)
assert(ffi.new('int[3]', 1, 2)[1] == 2)
assert(ffi.new('int[3]', {1, 2})[1] == 2)
ffi.cdef[[
struct var {
char ch[?];
};
]]
local d = ffi.new('char[4]')
local v = ffi.cast('struct var*', d)
v.ch = {1,2,3,4}
assert(v.ch[3] == 4)
v.ch = "bar"
assert(v.ch[3] == 0)
assert(v.ch[2] == string.byte('r'))
assert(d[1] == string.byte('a'))
ffi.cast('char*', 1)
-- 2 arg form of ffi.copy
ffi.copy(d, 'bar')
-- unsigned should be ignored for pointer rules
ffi.cdef[[
int strncmp(const signed char *s1, const unsigned char *s2, size_t n);
]]
assert(ffi.C.strncmp("two", "three", 3) ~= 0)
ffi.fill(d, 3, 1)
assert(d[2] == 1)
ffi.fill(d, 3)
assert(d[2] == 0)
-- tests for __new
ffi.cdef[[
struct newtest {
int a;
int b;
int c;
};
]]
local tp = ffi.metatype("struct newtest", {__new =
function(tp, x, y, z)
tp = ffi.new(tp)
tp.a, tp.b, tp.c = x, y, z
return tp
end})
local v = tp(1, 2, 3)
assert(v.a == 1 and v.b == 2 and v.c == 3)
local tp = ffi.metatype("struct newtest", {__new =
function(tp, x, y, z)
tp = ffi.new(tp, {a = x, b = y, c = z})
return tp
end})
local v = tp(1, 2, 3)
assert(v.a == 1 and v.b == 2 and v.c == 3)
-- tests for __pairs and __ipairs; not iterating just testing what is returned
local tp = ffi.metatype("struct newtest",
{__pairs = function(tp) return tp.a, tp.b end, __ipairs = function(tp) return tp.b, tp.c end}
)
local v = tp(1, 2, 3)
x, y = pairs(v)
assert(x == 1 and y == 2)
x, y = ipairs(v)
assert(x == 2 and y == 3)
-- test for pointer to struct having same metamethods
local st = ffi.cdef "struct ptest {int a, b;};"
local tp = ffi.metatype("struct ptest", {__index = function(s, k) return k end, __len = function(s) return 3 end})
local a = tp(1, 2)
assert(a.banana == "banana")
assert(#a == 3)
local b = ffi.new("int[2]")
local c = ffi.cast("struct ptest *", b)
assert(c.banana == "banana") -- should have same methods
assert(#c == 3)
print('Done: '..num_ok..'/'..(num_ok+num_err)..' passed')