-- 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<>)'), '') 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<>'), '') 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<>'), '') -- 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')