├── README.md ├── api.html ├── default.css ├── reflect.lua └── test.lua /README.md: -------------------------------------------------------------------------------- 1 | LuaJIT FFI reflection library 2 | =========== 3 | 4 | Quick examples: 5 | ```lua 6 | local ffi = require "ffi" 7 | local reflect = require "reflect" 8 | 9 | ffi.cdef 'int sc(const char*, const char*) __asm__("strcmp");' 10 | print(reflect.typeof(ffi.C.sc).sym_name) --> "strcmp" 11 | 12 | for refct in reflect.typeof"int(*)(int x, int y)".element_type:arguments() do 13 | print(refct.name) 14 | end --> x, y 15 | 16 | t = {} 17 | assert(reflect.getmetatable(ffi.metatype("struct {}", t)) == t) 18 | ``` 19 | 20 | For the full API reference, see http://corsix.github.io/ffi-reflect/. 21 | -------------------------------------------------------------------------------- /api.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | LuaJIT FFI reflection library 7 | 8 | 9 | 10 | 11 |

LuaJIT FFI reflection library

12 |

Version: beta 2 (2013-07-06)

13 |

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

14 |

refct = reflect.typeof(ct)

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

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

mt = reflect.getmetatable(ctype)

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

"void" kind (refct.what)

33 |

Possible attributes: size, alignment, const, volatile.

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

"int" kind (refct.what)

39 |

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

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

"float" kind (refct.what)

46 |

Possible attributes: size, alignment, const, volatile.

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

"enum" kind (refct.what)

51 |

Possible attributes: name, size, alignment, type.

52 |

Methods: values, value.

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

"constant" kind (refct.what)

56 |

Possible attributes: name, type, value.

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

"ptr" kind (refct.what)

60 |

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

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

"ref" kind (refct.what)

66 |

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

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

"array" kind (refct.what)

70 |

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

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

"struct" kind (refct.what)

77 |

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

78 |

Methods: members, member.

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

"union" kind (refct.what)

82 |

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

83 |

Methods: members, member.

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

"func" kind (refct.what)

87 |

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

88 |

Methods: arguments, argument.

89 | A function aggregate type. Note that function pointers will be of the "ptr" kind, with 90 | a "func" kind as the element_type. The return type is available as the return_type attribute, 91 | while argument types can be enumerated through the arguments method, or indexed through 92 | the argument method. The number of arguments is determined from the nargs and vararg attributes. 93 |
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"
94 |

"field" kind (refct.what)

95 |

Possible attributes: name, offset, type.

96 | An instance of a type within a structure or union, or an occurance of a type as an argument to a function. 97 |
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"
98 |

"bitfield" kind (refct.what)

99 |

Possible attributes: name, size, offset, type.

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

refct.name attribute (string or nil)

105 |

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

106 | The type's given name, or nil if the type has no name. 107 |
Examples:
reflect.typeof("struct{int x; int y;}"):member(2).name == "y"
108 | 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"
109 |

refct.sym_name attribute (string or nil)

110 |

Applies to: func.

111 | The function's symbolic name, if different to its given name. 112 |
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
113 |

refct.size attribute (number or string)

114 |

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

115 | The size of the type, in bytes. For most things this will be a strictly positive 116 | integer, although that is not always the case: 117 | 122 | 123 |
Examples:
reflect.typeof("__int32").size == 4
124 | reflect.typeof("__int32[2]").size == 8
125 | reflect.typeof("__int32[]").size == "none"
126 | reflect.typeof("__int32[?]").size == "none"
127 | reflect.typeof("struct{__int32 count; __int32 data[?];}").size == 4
128 | reflect.typeof("struct{}").size == 0
129 | reflect.typeof("void").size == "none"
130 | reflect.typeof("struct{int f:5;}"):member("f").size == 5 / 8
131 |

refct.offset attribute (number)

132 |

Applies to: field, bitfield.

133 | 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 134 | have a fractional part which is a multiple of 1/8. 135 |

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

refct.alignment attribute (integer)

141 |

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

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

refct.const attribute (true or nil)

148 |

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

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

refct.volatile attribute (true or nil)

157 |

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

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

refct.element_type attribute (refct)

164 |

Applies to: ptr, ref, array.

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

refct.type attribute (refct)

170 |

Applies to: enum, field, bitfield, constant.

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

refct.return_type attribute (refct)

175 |

Applies to: func.

176 | The return type of the function. 177 |
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"
178 |

refct.bool attribute (true or nil)

179 |

Applies to: int.

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

refct.unsigned attribute (true or nil)

185 |

Applies to: int.

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

refct.long attribute (true or nil)

190 |

Applies to: int.

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

refct.vla attribute (true or nil)

196 |

Applies to: struct, array.

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

refct.transparent attribute (true or nil)

204 |

Applies to: struct, union.

205 | If true, this type is an anonymous inner type. Such types have no name, and when using the 206 | FFI normally, their fields are accessed as fields of the containing type. 207 |
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
208 |

refct.vector attribute (true or nil)

209 |

Applies to: array.

210 | 211 |
212 |

refct.complex attribute (true or nil)

213 |

Applies to: array.

214 | 215 |
216 |

refct.nargs attribute (integer)

217 |

Applies to: func.

218 | The number of fixed arguments accepted by the function. If the vararg field is true, then 219 | additional arguments are accepted. 220 |
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
221 |

refct.vararg attribute (true or nil)

222 |

Applies to: func.

223 | If true, the function accepts a variable number of arguments (i.e. the argument list declaration was 224 | terminated with ...). 225 |
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
226 |

refct.sse_reg_params attribute (true or nil)

227 |

Applies to: func.

228 | 229 |
230 |

refct.convention attribute (string)

231 |

Applies to: func.

232 | The calling convention that the function was declared with, which will be one 233 | of: "cdecl" (the default), "thiscall", "fastcall", "stdcall". Note that 234 | on Windows, LuaJIT will automatically change __cdecl to __stdcall after the 235 | first call to the function (if appropriate). 236 |
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
237 |

refct.value attribute (integer)

238 |

Applies to: constant.

239 | 240 |
241 |
242 |

refct iterator = refct:members()

243 |

Applies to: struct, union.

244 | Returns an iterator triple which can be used in a for-in statement to enumerate 245 | the constituent members of the structure / union, in the order that they were 246 | defined. Each such member will be a refct of kind "field", "bitfield", 247 | "struct", or "union". The former two kinds will occur most of the time, with 248 | the latter two only occurring for unnamed (transparent) structures and unions. 249 | If enumerating the fields of a stucture or union, then you need to recursively 250 | enumerate these transparent members. 251 |
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
252 |

refct = refct:member(name_or_index)

253 |

Applies to: struct, union.

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

refct iterator = refct:arguments()

259 |

Applies to: func.

260 | Returns an iterator triple which can be used in a for-in statement to enumerate 261 | the arguments of the function, from left to right. Each such argument will be a refct of kind "field", 262 | having a type attribute, zero-based offset attribute, and optionally a name attribute. 263 |
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
264 |

refct = refct:argument(name_or_index)

265 |

Applies to: func.

266 | 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 267 | of arguments. 268 |
269 |

refct iterator = refct:values()

270 |

Applies to: enum.

271 | Returns an iterator triple which can be used in a for-in statement to enumerate 272 | the values which make up an enumerated type. Each such value will be a refct of kind "constant", 273 | having name and value attributes. 274 |
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
275 |

refct = refct:value(name_or_index)

276 |

Applies to: enum.

277 | 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 278 | of values. 279 |
280 |
281 |

Kind / attribute quick index

282 |
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        
283 |

Kind / method quick index

284 |
voidintfloatenumconstantptrrefarraystructunionfuncfieldbitfield
members        xx   
member        xx   
arguments          x  
argument          x  
values   x         
value   x         
285 | 286 | -------------------------------------------------------------------------------- /default.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #E6ECFF; 3 | margin-left: 10%; 4 | margin-right: 10%; 5 | } 6 | 7 | .qi { 8 | border-collapse: collapse; 9 | border-bottom: 1px #BFCFFF solid; 10 | } 11 | 12 | .qi td, .qi th { 13 | border-left: 1px #BFCFFF solid; 14 | border-right: 1px #BFCFFF solid; 15 | } 16 | 17 | .qi th { 18 | padding-right: 0.2em; 19 | padding-left: 0.2em; 20 | } 21 | .qi td { 22 | text-align: center; 23 | } 24 | 25 | .qi .r0 th { 26 | border-top: 1px #BFCFFF solid; 27 | border-bottom: 1px #BFCFFF solid; 28 | } 29 | 30 | .qi .r1 { 31 | background-color: #F0F4FF; 32 | } 33 | 34 | .qi .r1 th, .qi .r2 th { 35 | text-align: right; 36 | width: 8em; 37 | } 38 | 39 | p, pre { 40 | margin: 0; 41 | padding: 0; 42 | } 43 | 44 | h2 { 45 | margin-bottom: 0; 46 | padding-bottom: 0; 47 | } 48 | 49 | div.example { 50 | padding-top: 0.25em; 51 | } 52 | 53 | div:target h2 { 54 | color: #808080; 55 | } 56 | 57 | .example pre { 58 | background-color: #F0F4FF; 59 | border: 1px #BFCFFF solid; 60 | padding: 4px; 61 | } 62 | -------------------------------------------------------------------------------- /reflect.lua: -------------------------------------------------------------------------------- 1 | --[[ LuaJIT FFI reflection Library ]]-- 2 | --[[ Copyright (C) 2014 Peter Cawley . All rights reserved. 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. 21 | --]] 22 | local ffi = require "ffi" 23 | local bit = require "bit" 24 | local reflect = {} 25 | 26 | local CTState, init_CTState 27 | local miscmap, init_miscmap 28 | 29 | local function gc_str(gcref) -- Convert a GCref (to a GCstr) into a string 30 | if gcref ~= 0 then 31 | local ts = ffi.cast("uint32_t*", gcref) 32 | return ffi.string(ts + 4, ts[3]) 33 | end 34 | end 35 | 36 | local typeinfo = rawget(ffi, "typeinfo") 37 | 38 | typeinfo = typeinfo or function(id) 39 | -- ffi.typeof is present in LuaJIT v2.1 since 8th Oct 2014 (d6ff3afc) 40 | -- this is an emulation layer for older versions of LuaJIT 41 | local ctype = (CTState or init_CTState()).tab[id] 42 | return { 43 | info = ctype.info, 44 | size = bit.bnot(ctype.size) ~= 0 and ctype.size, 45 | sib = ctype.sib ~= 0 and ctype.sib, 46 | name = gc_str(ctype.name), 47 | } 48 | end 49 | 50 | local function memptr(gcobj) 51 | return tonumber(tostring(gcobj):match"%x*$", 16) 52 | end 53 | 54 | init_CTState = function() 55 | -- Relevant minimal definitions from lj_ctype.h 56 | ffi.cdef [[ 57 | typedef struct CType { 58 | uint32_t info; 59 | uint32_t size; 60 | uint16_t sib; 61 | uint16_t next; 62 | uint32_t name; 63 | } CType; 64 | 65 | typedef struct CTState { 66 | CType *tab; 67 | uint32_t top; 68 | uint32_t sizetab; 69 | void *L; 70 | void *g; 71 | void *finalizer; 72 | void *miscmap; 73 | } CTState; 74 | ]] 75 | 76 | -- Acquire a pointer to this Lua universe's CTState 77 | local co = coroutine.create(function(f, ...) return f(...) end) 78 | local uintgc = ffi.abi"gc64" and "uint64_t" or "uint32_t" 79 | local uintgc_ptr = ffi.typeof(uintgc .. "*") 80 | local G = ffi.cast(uintgc_ptr, ffi.cast(uintgc_ptr, memptr(co))[2]) 81 | -- In global_State, `MRef ctype_state` precedes `GCRef gcroot[GCROOT_MAX]`. 82 | -- We first find (an entry in) gcroot by looking for a metamethod name string. 83 | local anchor = ffi.cast(uintgc, ffi.cast("const char*", "__index")) 84 | local i = 0 85 | while math.abs(tonumber(G[i] - anchor)) > 64 do 86 | i = i + 1 87 | end 88 | -- Since Aug 2013, `GCRef cur_L` has preceded `MRef ctype_state`. Try to find it. 89 | local ok, i2 = coroutine.resume(co, 90 | function(coptr) 91 | for i2 = i - 3, i - 20, -1 do 92 | if G[i2] == coptr then return i2 end 93 | end 94 | end, memptr(co)) 95 | if ok and i2 then 96 | -- If we found it, work forwards looking for something resembling ctype_state. 97 | for i = i2 + 2, i - 1 do 98 | local Gi = G[i] 99 | if Gi ~= 0 and bit.band(Gi, 3) == 0 then 100 | CTState = ffi.cast("CTState*", Gi) 101 | if ffi.cast(uintgc_ptr, CTState.g) == G then 102 | return CTState 103 | end 104 | end 105 | end 106 | else 107 | -- Otherwise, work backwards looking for something resembling ctype_state. 108 | -- Note that since Jun 2020, this walks over the PRNGState, which is bad. 109 | for i = i - 1, 0, -1 do 110 | local Gi = G[i] 111 | if Gi ~= 0 and bit.band(Gi, 3) == 0 then 112 | CTState = ffi.cast("CTState*", Gi) 113 | if ffi.cast(uintgc_ptr, CTState.g) == G then 114 | return CTState 115 | end 116 | end 117 | end 118 | end 119 | end 120 | 121 | init_miscmap = function() 122 | -- Acquire the CTState's miscmap table as a Lua variable 123 | local t = {}; t[0] = t 124 | local uptr = ffi.cast("uintptr_t", (CTState or init_CTState()).miscmap) 125 | if ffi.abi"gc64" then 126 | local tvalue = ffi.cast("uint64_t**", memptr(t))[2] 127 | tvalue[0] = bit.bor(bit.lshift(bit.rshift(tvalue[0], 47), 47), uptr) 128 | else 129 | local tvalue = ffi.cast("uint32_t*", memptr(t))[2] 130 | ffi.cast("uint32_t*", tvalue)[ffi.abi"le" and 0 or 1] = ffi.cast("uint32_t", uptr) 131 | end 132 | miscmap = t[0] 133 | return miscmap 134 | end 135 | 136 | -- Information for unpacking a `struct CType`. 137 | -- One table per CT_* constant, containing: 138 | -- * A name for that CT_ 139 | -- * Roles of the cid and size fields. 140 | -- * Whether the sib field is meaningful. 141 | -- * Zero or more applicable boolean flags. 142 | local CTs = {[0] = 143 | {"int", 144 | "", "size", false, 145 | {0x08000000, "bool"}, 146 | {0x04000000, "float", "subwhat"}, 147 | {0x02000000, "const"}, 148 | {0x01000000, "volatile"}, 149 | {0x00800000, "unsigned"}, 150 | {0x00400000, "long"}, 151 | }, 152 | {"struct", 153 | "", "size", true, 154 | {0x02000000, "const"}, 155 | {0x01000000, "volatile"}, 156 | {0x00800000, "union", "subwhat"}, 157 | {0x00100000, "vla"}, 158 | }, 159 | {"ptr", 160 | "element_type", "size", false, 161 | {0x02000000, "const"}, 162 | {0x01000000, "volatile"}, 163 | {0x00800000, "ref", "subwhat"}, 164 | }, 165 | {"array", 166 | "element_type", "size", false, 167 | {0x08000000, "vector"}, 168 | {0x04000000, "complex"}, 169 | {0x02000000, "const"}, 170 | {0x01000000, "volatile"}, 171 | {0x00100000, "vla"}, 172 | }, 173 | {"void", 174 | "", "size", false, 175 | {0x02000000, "const"}, 176 | {0x01000000, "volatile"}, 177 | }, 178 | {"enum", 179 | "type", "size", true, 180 | }, 181 | {"func", 182 | "return_type", "nargs", true, 183 | {0x00800000, "vararg"}, 184 | {0x00400000, "sse_reg_params"}, 185 | }, 186 | {"typedef", -- Not seen 187 | "element_type", "", false, 188 | }, 189 | {"attrib", -- Only seen internally 190 | "type", "value", true, 191 | }, 192 | {"field", 193 | "type", "offset", true, 194 | }, 195 | {"bitfield", 196 | "", "offset", true, 197 | {0x08000000, "bool"}, 198 | {0x02000000, "const"}, 199 | {0x01000000, "volatile"}, 200 | {0x00800000, "unsigned"}, 201 | }, 202 | {"constant", 203 | "type", "value", true, 204 | {0x02000000, "const"}, 205 | }, 206 | {"extern", -- Not seen 207 | "CID", "", true, 208 | }, 209 | {"kw", -- Not seen 210 | "TOK", "size", 211 | }, 212 | } 213 | 214 | -- Set of CType::cid roles which are a CTypeID. 215 | local type_keys = { 216 | element_type = true, 217 | return_type = true, 218 | value_type = true, 219 | type = true, 220 | } 221 | 222 | -- Create a metatable for each CT. 223 | local metatables = { 224 | } 225 | for _, CT in ipairs(CTs) do 226 | local what = CT[1] 227 | local mt = {__index = {}} 228 | metatables[what] = mt 229 | end 230 | 231 | -- Logic for merging an attribute CType onto the annotated CType. 232 | local CTAs = {[0] = 233 | function(a, refct) error("TODO: CTA_NONE") end, 234 | function(a, refct) error("TODO: CTA_QUAL") end, 235 | function(a, refct) 236 | a = 2^a.value 237 | refct.alignment = a 238 | refct.attributes.align = a 239 | end, 240 | function(a, refct) 241 | refct.transparent = true 242 | refct.attributes.subtype = refct.typeid 243 | end, 244 | function(a, refct) refct.sym_name = a.name end, 245 | function(a, refct) error("TODO: CTA_BAD") end, 246 | } 247 | 248 | -- C function calling conventions (CTCC_* constants in lj_refct.h) 249 | local CTCCs = {[0] = 250 | "cdecl", 251 | "thiscall", 252 | "fastcall", 253 | "stdcall", 254 | } 255 | 256 | local function refct_from_id(id) -- refct = refct_from_id(CTypeID) 257 | local ctype = typeinfo(id) 258 | local CT_code = bit.rshift(ctype.info, 28) 259 | local CT = CTs[CT_code] 260 | local what = CT[1] 261 | local refct = setmetatable({ 262 | what = what, 263 | typeid = id, 264 | name = ctype.name, 265 | }, metatables[what]) 266 | 267 | -- Interpret (most of) the CType::info field 268 | for i = 5, #CT do 269 | if bit.band(ctype.info, CT[i][1]) ~= 0 then 270 | if CT[i][3] == "subwhat" then 271 | refct.what = CT[i][2] 272 | else 273 | refct[CT[i][2]] = true 274 | end 275 | end 276 | end 277 | if CT_code <= 5 then 278 | refct.alignment = bit.lshift(1, bit.band(bit.rshift(ctype.info, 16), 15)) 279 | elseif what == "func" then 280 | refct.convention = CTCCs[bit.band(bit.rshift(ctype.info, 16), 3)] 281 | end 282 | 283 | if CT[2] ~= "" then -- Interpret the CType::cid field 284 | local k = CT[2] 285 | local cid = bit.band(ctype.info, 0xffff) 286 | if type_keys[k] then 287 | if cid == 0 then 288 | cid = nil 289 | else 290 | cid = refct_from_id(cid) 291 | end 292 | end 293 | refct[k] = cid 294 | end 295 | 296 | if CT[3] ~= "" then -- Interpret the CType::size field 297 | local k = CT[3] 298 | refct[k] = ctype.size or (k == "size" and "none") 299 | end 300 | 301 | if what == "attrib" then 302 | -- Merge leading attributes onto the type being decorated. 303 | local CTA = CTAs[bit.band(bit.rshift(ctype.info, 16), 0xff)] 304 | if refct.type then 305 | local ct = refct.type 306 | ct.attributes = {} 307 | CTA(refct, ct) 308 | ct.typeid = refct.typeid 309 | refct = ct 310 | else 311 | refct.CTA = CTA 312 | end 313 | elseif what == "bitfield" then 314 | -- Decode extra bitfield fields, and make it look like a normal field. 315 | refct.offset = refct.offset + bit.band(ctype.info, 127) / 8 316 | refct.size = bit.band(bit.rshift(ctype.info, 8), 127) / 8 317 | refct.type = { 318 | what = "int", 319 | bool = refct.bool, 320 | const = refct.const, 321 | volatile = refct.volatile, 322 | unsigned = refct.unsigned, 323 | size = bit.band(bit.rshift(ctype.info, 16), 127), 324 | } 325 | refct.bool, refct.const, refct.volatile, refct.unsigned = nil 326 | end 327 | 328 | if CT[4] then -- Merge sibling attributes onto this type. 329 | while ctype.sib do 330 | local entry = typeinfo(ctype.sib) 331 | if CTs[bit.rshift(entry.info, 28)][1] ~= "attrib" then break end 332 | if bit.band(entry.info, 0xffff) ~= 0 then break end 333 | local sib = refct_from_id(ctype.sib) 334 | sib:CTA(refct) 335 | ctype = entry 336 | end 337 | end 338 | 339 | return refct 340 | end 341 | 342 | local function sib_iter(s, refct) 343 | repeat 344 | local ctype = typeinfo(refct.typeid) 345 | if not ctype.sib then return end 346 | refct = refct_from_id(ctype.sib) 347 | until refct.what ~= "attrib" -- Pure attribs are skipped. 348 | return refct 349 | end 350 | 351 | local function siblings(refct) 352 | -- Follow to the end of the attrib chain, if any. 353 | while refct.attributes do 354 | refct = refct_from_id(refct.attributes.subtype or typeinfo(refct.typeid).sib) 355 | end 356 | 357 | return sib_iter, nil, refct 358 | end 359 | 360 | metatables.struct.__index.members = siblings 361 | metatables.func.__index.arguments = siblings 362 | metatables.enum.__index.values = siblings 363 | 364 | local function find_sibling(refct, name) 365 | local num = tonumber(name) 366 | if num then 367 | for sib in siblings(refct) do 368 | if num == 1 then 369 | return sib 370 | end 371 | num = num - 1 372 | end 373 | else 374 | for sib in siblings(refct) do 375 | if sib.name == name then 376 | return sib 377 | end 378 | end 379 | end 380 | end 381 | 382 | metatables.struct.__index.member = find_sibling 383 | metatables.func.__index.argument = find_sibling 384 | metatables.enum.__index.value = find_sibling 385 | 386 | function reflect.typeof(x) -- refct = reflect.typeof(ct) 387 | return refct_from_id(tonumber(ffi.typeof(x))) 388 | end 389 | 390 | function reflect.getmetatable(x) -- mt = reflect.getmetatable(ct) 391 | return (miscmap or init_miscmap())[-tonumber(ffi.typeof(x))] 392 | end 393 | 394 | return reflect 395 | -------------------------------------------------------------------------------- /test.lua: -------------------------------------------------------------------------------- 1 | local ffi = require "ffi" 2 | local reflect = require "reflect" 3 | assert((function()return reflect.typeof("void").what == "void" end)()) 4 | assert((function()return reflect.typeof("const void").what == "void" end)()) 5 | assert((function()return reflect.typeof("long").what == "int" end)()) 6 | assert((function()return reflect.typeof("volatile unsigned __int64").what == "int" end)()) 7 | assert((function()return reflect.typeof("double").what == "float" end)()) 8 | assert((function()return reflect.typeof("const float").what == "float" end)()) 9 | assert((function() 10 | ffi.cdef "enum E{X,Y};" 11 | return reflect.typeof("enum E").what == "enum" end)()) 12 | assert((function() 13 | ffi.cdef "enum Bool{False,True};" 14 | return reflect.typeof("enum Bool"):value("False").what == "constant" end)()) 15 | assert((function()return reflect.typeof("char*").what == "ptr" end)()) 16 | assert((function()return reflect.typeof("int(*)(void)").what == "ptr" end)()) 17 | assert((function()return reflect.typeof("char&").what == "ref" end)()) 18 | assert((function()return reflect.typeof("char[16]").what == "array" end)()) 19 | assert((function()return reflect.typeof("int[?]").what == "array" end)()) 20 | assert((function()return reflect.typeof("struct{int x; int y;}").what == "struct" end)()) 21 | assert((function()return reflect.typeof("union{int x; int y;}").what == "union" end)()) 22 | assert((function() 23 | ffi.cdef "int strcmp(const char*, const char*);" 24 | return reflect.typeof(ffi.C.strcmp).what == "func" end)()) 25 | assert((function()return reflect.typeof("int(*)(void)").element_type.what == "func" end)()) 26 | assert((function()return reflect.typeof("struct{int x;}"):member("x").what == "field" end)()) 27 | assert((function() 28 | ffi.cdef "int strcmp(const char*, const char*);" 29 | return reflect.typeof(ffi.C.strcmp):argument(2).what == "field" end)()) 30 | assert((function()return reflect.typeof("struct{int x:2;}"):member("x").what == "bitfield" end)()) 31 | assert((function()return reflect.typeof("struct{int x; int y;}"):member(2).name == "y" end)()) 32 | assert((function()return reflect.typeof("struct{int x; int y;}").name == nil end)()) 33 | assert((function() 34 | ffi.cdef 'int sc(const char*, const char*) __asm__("strcmp");' 35 | return reflect.typeof(ffi.C.sc).name == "sc" end)()) 36 | assert((function() 37 | ffi.cdef 'int sc(const char*, const char*) __asm__("strcmp");' 38 | return reflect.typeof(ffi.C.sc).sym_name == "strcmp" end)()) 39 | assert((function() 40 | ffi.cdef "int strcmp(const char*, const char*);" 41 | return reflect.typeof(ffi.C.strcmp).sym_name == nil end)()) 42 | assert((function()return reflect.typeof("__int32").size == 4 end)()) 43 | assert((function()return reflect.typeof("__int32[2]").size == 8 end)()) 44 | assert((function()return reflect.typeof("__int32[]").size == "none" end)()) 45 | assert((function()return reflect.typeof("__int32[?]").size == "none" end)()) 46 | assert((function()return reflect.typeof("struct{__int32 count; __int32 data[?];}").size == 4 end)()) 47 | assert((function()return reflect.typeof("struct{}").size == 0 end)()) 48 | assert((function()return reflect.typeof("void").size == "none" end)()) 49 | assert((function()return reflect.typeof("struct{int f:5;}"):member("f").size == 5 / 8 end)()) 50 | assert((function()return reflect.typeof("struct{__int32 x; __int32 y; __int32 z;}"):member("z").offset == 8 end)()) 51 | assert((function()return reflect.typeof("struct{int x : 3; int y : 4; int z : 5;}"):member("z").offset == 7 / 8 end)()) 52 | assert((function()return reflect.typeof("int(*)(int x, int y)").element_type:argument("y").offset == 1 end)()) 53 | assert((function()return reflect.typeof("struct{__int32 a; __int32 b;}").alignment == 4 end)()) 54 | assert((function()return reflect.typeof("__declspec(align(16)) int").alignment == 16 end)()) 55 | assert((function()return reflect.typeof("int").const == nil end)()) 56 | assert((function()return reflect.typeof("const int").const == true end)()) 57 | assert((function()return reflect.typeof("const char*").const == nil end)()) 58 | assert((function()return reflect.typeof("const char*").element_type.const == true end)()) 59 | assert((function()return reflect.typeof("char* const").const == true end)()) 60 | assert((function()return reflect.typeof("int").volatile == nil end)()) 61 | assert((function()return reflect.typeof("volatile int").volatile == true end)()) 62 | assert((function()return reflect.typeof("char*").element_type.size == 1 end)()) 63 | assert((function()return reflect.typeof("char&").element_type.size == 1 end)()) 64 | assert((function()return reflect.typeof("char[32]").element_type.size == 1 end)()) 65 | assert((function()return reflect.typeof("struct{float x; unsigned y;}"):member("y").type.unsigned == true end)()) 66 | assert((function()return reflect.typeof("int(*)(uint64_t)").element_type:argument(1).type.size == 8 end)()) 67 | assert((function() 68 | ffi.cdef "int strcmp(const char*, const char*);" 69 | return reflect.typeof(ffi.C.strcmp).return_type.what == "int" end)()) 70 | assert((function()return reflect.typeof("void*(*)(void)").element_type.return_type.what == "ptr" end)()) 71 | assert((function()return reflect.typeof("bool").bool == true end)()) 72 | assert((function()return reflect.typeof("int").bool == nil end)()) 73 | assert((function()return reflect.typeof("_Bool int").bool == true end)()) 74 | assert((function()return reflect.typeof("int32_t").unsigned == nil end)()) 75 | assert((function()return reflect.typeof("uint32_t").unsigned == true end)()) 76 | assert((function()return reflect.typeof("long int").long == true end)()) 77 | assert((function()return reflect.typeof("short int").long == nil end)()) 78 | assert((function()return reflect.typeof("int[?]").vla == true end)()) 79 | assert((function()return reflect.typeof("int[2]").vla == nil end)()) 80 | assert((function()return reflect.typeof("int[]").vla == nil end)()) 81 | assert((function()return reflect.typeof("struct{int num; int data[?];}").vla == true end)()) 82 | assert((function()return reflect.typeof("struct{int num; int data[];}").vla == nil end)()) 83 | assert((function()local pieces = {} local function print(s) pieces[#pieces + 1] = tostring(s) end 84 | for refct in reflect.typeof [[ 85 | struct { 86 | int a; 87 | union { int b; int c; }; 88 | struct { int d; int e; }; 89 | int f; 90 | } 91 | ]]:members() do print(refct.transparent) end --> nil, true, true, nil 92 | return table.concat(pieces, ", ") == "nil, true, true, nil" end)()) 93 | assert((function() 94 | ffi.cdef "int strcmp(const char*, const char*);" 95 | return reflect.typeof(ffi.C.strcmp).nargs == 2 end)()) 96 | assert((function() 97 | ffi.cdef "int printf(const char*, ...);" 98 | return reflect.typeof(ffi.C.printf).nargs == 1 end)()) 99 | assert((function() 100 | ffi.cdef "int strcmp(const char*, const char*);" 101 | return reflect.typeof(ffi.C.strcmp).vararg == nil end)()) 102 | assert((function() 103 | ffi.cdef "int printf(const char*, ...);" 104 | return reflect.typeof(ffi.C.printf).vararg == true end)()) 105 | assert((function()return reflect.typeof("int(__stdcall *)(int)").element_type.convention == "stdcall" end)()) 106 | assert((function()local pieces = {} local function print(s) pieces[#pieces + 1] = tostring(s) end 107 | if not ffi.abi "win" then return "Windows-only example" end 108 | ffi.cdef "void* LoadLibraryA(const char*)" 109 | print(reflect.typeof(ffi.C.LoadLibraryA).convention) --> cdecl 110 | ffi.C.LoadLibraryA("kernel32") 111 | print(reflect.typeof(ffi.C.LoadLibraryA).convention) --> stdcall 112 | return table.concat(pieces, ", ") == "cdecl, stdcall" end)()) 113 | assert((function()local pieces = {} local function print(s) pieces[#pieces + 1] = tostring(s) end for refct in reflect.typeof("struct{int x; int y;}"):members() do print(refct.name) end --> x, y 114 | return table.concat(pieces, ", ") == "x, y" end)()) 115 | assert((function()local pieces = {} local function print(s) pieces[#pieces + 1] = tostring(s) end 116 | for refct in reflect.typeof[[ 117 | struct { 118 | int a; 119 | union { 120 | int b; 121 | int c; 122 | }; 123 | int d : 2; 124 | struct { 125 | int e; 126 | int f; 127 | }; 128 | } 129 | ]]:members() do print(refct.what) end --> field, union, bitfield, struct 130 | return table.concat(pieces, ", ") == "field, union, bitfield, struct" end)()) 131 | assert((function()local pieces = {} local function print(s) pieces[#pieces + 1] = tostring(s) end 132 | ffi.cdef "int strcmp(const char*, const char*);" 133 | for refct in reflect.typeof(ffi.C.strcmp):arguments() do print(refct.type.what) end --> ptr, ptr 134 | return table.concat(pieces, ", ") == "ptr, ptr" end)()) 135 | assert((function()local pieces = {} local function print(s) pieces[#pieces + 1] = tostring(s) end 136 | for refct in reflect.typeof"int(*)(int x, int y)".element_type:arguments() do print(refct.name) end --> x, y 137 | return table.concat(pieces, ", ") == "x, y" end)()) 138 | assert((function()local pieces = {} local function print(s) pieces[#pieces + 1] = tostring(s) end 139 | ffi.cdef "enum EV{EV_A = 1, EV_B = 10, EV_C = 100};" 140 | for refct in reflect.typeof("enum EV"):values() do print(refct.name) end --> EV_A, EV_B, EV_C 141 | return table.concat(pieces, ", ") == "EV_A, EV_B, EV_C" end)()) 142 | assert((function()local t = {} 143 | return reflect.getmetatable(ffi.metatype("struct {}", t)) == t end)()) 144 | assert((function()local pieces = {} local function print(s) pieces[#pieces + 1] = tostring(s) end 145 | local function rec_members(refct, f) 146 | if refct.members then 147 | for refct in refct:members() do 148 | rec_members(refct, f) 149 | end 150 | else 151 | f(refct) 152 | end 153 | end 154 | rec_members(reflect.typeof [[ 155 | struct { 156 | int a; 157 | union { struct { int b; }; int c; }; 158 | struct { int d; union { int e; }; }; 159 | int f; 160 | } 161 | ]], function(refct) print(refct.name) end) 162 | return table.concat(pieces, ", ") == "a, b, c, d, e, f" end)()) 163 | print "PASS" 164 | --------------------------------------------------------------------------------