LuaJIT FFI reflection library

Version: beta 2 (2013-07-06)

This is a beta release of an experimental and unofficial library. Feedback appreciated (to lua@corsix.org, or the LuaJIT mailing list).


refct = reflect.typeof(ct)

reflect.typeof returns a so-called refct object, which describes the type passed in to the function. To understand this object, one needs to appreciate LuaJIT's C type model. A refct object is one of 13 different kinds of type. For example, one of those kinds is "int", which covers all primitive integral types, and another is "ptr", which covers all pointer types. Perhaps unusually, every field within a structure is also considered to be a type, as is every argument within a function type, and every value within an enumerated type. While this may look odd, it results in a nice uniform API for type reflection. Note that typedefs are resolved by the parser, and are therefore not visible when reflected.

All refct objects have a what field, which is a string denoting the kind of type. Other fields will also be present on a refct object, but these vary according to the kind.

mt = reflect.getmetatable(ctype)

reflect.getmetatable performs the inverse of ffi.metatype - given a ctype, it returns the corresponding metatable that was passed to ffi.metatype.
Example:
reflect.getmetatable(ffi.metatype("struct {}", t)) == t

"void" kind (refct.what)

Possible attributes: size, alignment, const, volatile.

The primitive empty type, optionally with a const and/or volatile qualifier. The actual type is therefore determined by the const and volatile fields.
Examples:
reflect.typeof("void").what == "void"
reflect.typeof("const void").what == "void"

"int" kind (refct.what)

Possible attributes: size, alignment, const, volatile, bool, unsigned, long.

A primitive integral type, such as bool or [const] [volatile] [u]int(8|16|32|64)_t. The in-memory type is determined by the size and unsigned fields, and the final quantified type determined also by the bool, const, and volatile fields.
Examples:
reflect.typeof("long").what == "int"
reflect.typeof("volatile unsigned __int64").what == "int"

"float" kind (refct.what)

Possible attributes: size, alignment, const, volatile.

A primitive floating point type, either [const] [volatile] float or [const] [volatile] double.
Examples:
reflect.typeof("double").what == "float"
reflect.typeof("const float").what == "float"

"enum" kind (refct.what)

Possible attributes: name, size, alignment, type.

Methods: values, value.

An enumerated type.
Example:
ffi.cdef "enum E{X,Y};"
reflect.typeof("enum E").what == "enum"

"constant" kind (refct.what)

Possible attributes: name, type, value.

A particular value within an enumerated type.
Example:
ffi.cdef "enum Bool{False,True};"
reflect.typeof("enum Bool"):value("False").what == "constant"

"ptr" kind (refct.what)

Possible attributes: size, alignment, const, volatile, element_type.

A pointer type (note that this includes function pointers). The type being pointed to is given by the element_type attribute.
Examples:
reflect.typeof("char*").what == "ptr"
reflect.typeof("int(*)(void)").what == "ptr"

"ref" kind (refct.what)

Possible attributes: size, alignment, const, volatile, element_type.

A reference type. The type being referenced is given by the element_type attribute.
Example:
reflect.typeof("char&").what == "ref"

"array" kind (refct.what)

Possible attributes: size, alignment, const, volatile, element_type, vla, vector, complex.

An array type. The type of each element is given by the element_type attribute. The number of elements is not directly available; instead the size attribute needs to be divided by element_type.size.
Examples:
reflect.typeof("char[16]").what == "array"
reflect.typeof("int[?]").what == "array"

"struct" kind (refct.what)

Possible attributes: name, size, alignment, const, volatile, vla, transparent.

Methods: members, member.

A structure aggregate type. The members of the structure can be enumerated through the members method, or indexed through the member method.
Example:
reflect.typeof("struct{int x; int y;}").what == "struct"

"union" kind (refct.what)

Possible attributes: name, size, alignment, const, volatile, transparent.

Methods: members, member.

A union aggregate type. The members of the union can be enumerated through the members method, or indexed through the member method.
Example:
reflect.typeof("union{int x; int y;}").what == "union"

"func" kind (refct.what)

Possible attributes: name, sym_name, return_type, nargs, vararg, sse_reg_params, convention.

Methods: arguments, argument.

A function aggregate type. Note that function pointers will be of the "ptr" kind, with a "func" kind as the element_type. The return type is available as the return_type attribute, while argument types can be enumerated through the arguments method, or indexed through the argument method. The number of arguments is determined from the nargs and vararg attributes.
Example:
ffi.cdef "int strcmp(const char*, const char*);"
reflect.typeof(ffi.C.strcmp).what == "func"
Example:
reflect.typeof("int(*)(void)").element_type.what == "func"

"field" kind (refct.what)

Possible attributes: name, offset, type.

An instance of a type within a structure or union, or an occurance of a type as an argument to a function.
Example:
reflect.typeof("struct{int x;}"):member("x").what == "field"
Example:
ffi.cdef "int strcmp(const char*, const char*);"
reflect.typeof(ffi.C.strcmp):argument(2).what == "field"

"bitfield" kind (refct.what)

Possible attributes: name, size, offset, type.

An instance of a type within a structure or union, which has an offset and/or size which is not a whole number of bytes.
Example:
reflect.typeof("struct{int x:2;}"):member("x").what == "bitfield"

refct.name attribute (string or nil)

Applies to: struct, union, enum, func, field, bitfield, constant.

The type's given name, or nil if the type has no name.
Examples:
reflect.typeof("struct{int x; int y;}"):member(2).name == "y"
reflect.typeof("struct{int x; int y;}").name == nil
Example:
ffi.cdef 'int sc(const char*, const char*) __asm__("strcmp");'
reflect.typeof(ffi.C.sc).name == "sc"

refct.sym_name attribute (string or nil)

Applies to: func.

The function's symbolic name, if different to its given name.
Example:
ffi.cdef 'int sc(const char*, const char*) __asm__("strcmp");'
reflect.typeof(ffi.C.sc).sym_name == "strcmp"
Example:
ffi.cdef "int strcmp(const char*, const char*);"
reflect.typeof(ffi.C.strcmp).sym_name == nil

refct.size attribute (number or string)

Applies to: int, float, struct, union, ptr, ref, array, void, enum, bitfield.

The size of the type, in bytes. For most things this will be a strictly positive integer, although that is not always the case:
Examples:
reflect.typeof("__int32").size == 4
reflect.typeof("__int32[2]").size == 8
reflect.typeof("__int32[]").size == "none"
reflect.typeof("__int32[?]").size == "none"
reflect.typeof("struct{__int32 count; __int32 data[?];}").size == 4
reflect.typeof("struct{}").size == 0
reflect.typeof("void").size == "none"
reflect.typeof("struct{int f:5;}"):member("f").size == 5 / 8

refct.offset attribute (number)

Applies to: field, bitfield.

For structure and union members, the number of bytes between the start of the containing type and the (bit)field. For a normal field, this will be a non-negative integer. For bitfields, this can have a fractional part which is a multiple of 1/8.

For function arguments, the zero-based index of the argument.
Examples:
reflect.typeof("struct{__int32 x; __int32 y; __int32 z;}"):member("z").offset == 8
reflect.typeof("struct{int x : 3; int y : 4; int z : 5;}"):member("z").offset == 7 / 8
reflect.typeof("int(*)(int x, int y)").element_type:argument("y").offset == 1

refct.alignment attribute (integer)

Applies to: int, float, struct, union, ptr, ref, array, void, enum.

The minimum alignment required by the type, in bytes. Unless explicitly overridden by an alignment qualifier, this will be the value calculated by LuaJIT's C parser. In any case, this will be a power of two.
Examples:
reflect.typeof("struct{__int32 a; __int32 b;}").alignment == 4
reflect.typeof("__declspec(align(16)) int").alignment == 16

refct.const attribute (true or nil)

Applies to: int, float, struct, union, ptr, ref, array, void.

If true, this type was declared with the const qualifier. Be aware that for pointer types, this refers to the const-ness of the pointer itself, and not the const-ness of the thing being pointed to.
Examples:
reflect.typeof("int").const == nil
reflect.typeof("const int").const == true
reflect.typeof("const char*").const == nil
reflect.typeof("const char*").element_type.const == true
reflect.typeof("char* const").const == true

refct.volatile attribute (true or nil)

Applies to: int, float, struct, union, ptr, ref, array, void.

If true, this type was declared with the volatile qualifier. Note that this has no meaning to the JIT compiler. Be aware that for pointer types, this refers to the volatility of the pointer itself, and not the volatility of the thing being pointed to.
Examples:
reflect.typeof("int").volatile == nil
reflect.typeof("volatile int").volatile == true

refct.element_type attribute (refct)

Applies to: ptr, ref, array.

The type being pointed to (albeit implicitly in the case of a reference).
Examples:
reflect.typeof("char*").element_type.size == 1
reflect.typeof("char&").element_type.size == 1
reflect.typeof("char[32]").element_type.size == 1

refct.type attribute (refct)

Applies to: enum, field, bitfield, constant.

For (bit)fields, the type of the field.
Examples:
reflect.typeof("struct{float x; unsigned y;}"):member("y").type.unsigned == true
reflect.typeof("int(*)(uint64_t)").element_type:argument(1).type.size == 8

refct.return_type attribute (refct)

Applies to: func.

The return type of the function.
Example:
ffi.cdef "int strcmp(const char*, const char*);"
reflect.typeof(ffi.C.strcmp).return_type.what == "int"
Example:
reflect.typeof("void*(*)(void)").element_type.return_type.what == "ptr"

refct.bool attribute (true or nil)

Applies to: int.

If true, reading from this type will give a Lua boolean rather than a Lua number.
Examples:
reflect.typeof("bool").bool == true
reflect.typeof("int").bool == nil
reflect.typeof("_Bool int").bool == true

refct.unsigned attribute (true or nil)

Applies to: int.

If true, this type denotes an unsigned integer. Otherwise, it denotes a signed integer.
Examples:
reflect.typeof("int32_t").unsigned == nil
reflect.typeof("uint32_t").unsigned == true

refct.long attribute (true or nil)

Applies to: int.

If true, this type was declared with the long qualifier. If calculating the size of the type, then use the size field rather than this field.
Examples:
reflect.typeof("long int").long == true
reflect.typeof("short int").long == nil

refct.vla attribute (true or nil)

Applies to: struct, array.

If true, this type has a variable length. Otherwise, this type has a fixed length.
Examples:
reflect.typeof("int[?]").vla == true
reflect.typeof("int[2]").vla == nil
reflect.typeof("int[]").vla == nil
reflect.typeof("struct{int num; int data[?];}").vla == true
reflect.typeof("struct{int num; int data[];}").vla == nil

refct.transparent attribute (true or nil)

Applies to: struct, union.

If true, this type is an anonymous inner type. Such types have no name, and when using the FFI normally, their fields are accessed as fields of the containing type.
Example:
for refct in reflect.typeof [[
struct {
int a;
union { int b; int c; };
struct { int d; int e; };
int f;
}
]]:members() do print(refct.transparent) end --> nil, true, true, nil

refct.vector attribute (true or nil)

Applies to: array.

refct.complex attribute (true or nil)

Applies to: array.

refct.nargs attribute (integer)

Applies to: func.

The number of fixed arguments accepted by the function. If the vararg field is true, then additional arguments are accepted.
Example:
ffi.cdef "int strcmp(const char*, const char*);"
reflect.typeof(ffi.C.strcmp).nargs == 2
Example:
ffi.cdef "int printf(const char*, ...);"
reflect.typeof(ffi.C.printf).nargs == 1

refct.vararg attribute (true or nil)

Applies to: func.

If true, the function accepts a variable number of arguments (i.e. the argument list declaration was terminated with ...).
Example:
ffi.cdef "int strcmp(const char*, const char*);"
reflect.typeof(ffi.C.strcmp).vararg == nil
Example:
ffi.cdef "int printf(const char*, ...);"
reflect.typeof(ffi.C.printf).vararg == true

refct.sse_reg_params attribute (true or nil)

Applies to: func.

refct.convention attribute (string)

Applies to: func.

The calling convention that the function was declared with, which will be one of: "cdecl" (the default), "thiscall", "fastcall", "stdcall". Note that on Windows, LuaJIT will automatically change __cdecl to __stdcall after the first call to the function (if appropriate).
Example:
reflect.typeof("int(__stdcall *)(int)").element_type.convention == "stdcall"
Example:
if not ffi.abi "win" then return "Windows-only example" end
ffi.cdef "void* LoadLibraryA(const char*)"
print(reflect.typeof(ffi.C.LoadLibraryA).convention) --> cdecl
ffi.C.LoadLibraryA("kernel32")
print(reflect.typeof(ffi.C.LoadLibraryA).convention) --> stdcall

refct.value attribute (integer)

Applies to: constant.


refct iterator = refct:members()

Applies to: struct, union.

Returns an iterator triple which can be used in a for-in statement to enumerate the constituent members of the structure / union, in the order that they were defined. Each such member will be a refct of kind "field", "bitfield", "struct", or "union". The former two kinds will occur most of the time, with the latter two only occurring for unnamed (transparent) structures and unions. If enumerating the fields of a stucture or union, then you need to recursively enumerate these transparent members.
Example:
for refct in reflect.typeof("struct{int x; int y;}"):members() do print(refct.name) end --> x, y
Example:
for refct in reflect.typeof[[
struct {
int a;
union {
int b;
int c;
};
int d : 2;
struct {
int e;
int f;
};
}
]]:members() do print(refct.what) end --> field, union, bitfield, struct

refct = refct:member(name_or_index)

Applies to: struct, union.

Like members(), but returns the first member whose name matches the given parameter, or the member given by the 1-based index, or nil if nothing matches. Note that this method takes time linear in the number of members.

refct iterator = refct:arguments()

Applies to: func.

Returns an iterator triple which can be used in a for-in statement to enumerate the arguments of the function, from left to right. Each such argument will be a refct of kind "field", having a type attribute, zero-based offset attribute, and optionally a name attribute.
Example:
ffi.cdef "int strcmp(const char*, const char*);"
for refct in reflect.typeof(ffi.C.strcmp):arguments() do print(refct.type.what) end --> ptr, ptr
Example:
for refct in reflect.typeof"int(*)(int x, int y)".element_type:arguments() do print(refct.name) end --> x, y

refct = refct:argument(name_or_index)

Applies to: func.

Like arguments(), but returns the first argument whose name matches the given parameter, or the argument given by the 1-based index, or nil if nothing matches. Note that this method takes time linear in the number of arguments.

refct iterator = refct:values()

Applies to: enum.

Returns an iterator triple which can be used in a for-in statement to enumerate the values which make up an enumerated type. Each such value will be a refct of kind "constant", having name and value attributes.
Example:
ffi.cdef "enum EV{EV_A = 1, EV_B = 10, EV_C = 100};"
for refct in reflect.typeof("enum EV"):values() do print(refct.name) end --> EV_A, EV_B, EV_C

refct = refct:value(name_or_index)

Applies to: enum.

Like values(), but returns the value whose name matches the given parameter, or the value given by the 1-based index, or nil if nothing matches. Note that this method takes time linear in the number of values.

Kind / attribute quick index

voidintfloatenumconstantptrrefarraystructunionfuncfieldbitfield
name   xx   xxxxx
sym_name          x  
sizexxxx xxxxx  x
offset           xx
alignmentxxxx xxxxx   
constxxx  xxxxx   
volatilexxx  xxxxx   
element_type     xxx     
type   xx      xx
return_type          x  
bool x           
unsigned x           
long x           
vla       xx    
transparent        xx   
vector       x     
complex       x     
nargs          x  
vararg          x  
sse_reg_params          x  
convention          x  
value    x        

Kind / method quick index

voidintfloatenumconstantptrrefarraystructunionfuncfieldbitfield
members        xx   
member        xx   
arguments          x  
argument          x  
values   x         
value   x