891 lines
26 KiB
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')
|