├── 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 |
For empty structures and unions, size will be zero.
118 | For types which are essentially void , size will be the string "none" .
119 | For bitfields, size can have a fractional part, which will be a multiple of 1/8.
120 | For structures which terminate with a VLA, this will be the size of the fixed part of the structure.
121 | For arrays, size will be the element size multiplied by the number of elements, or the string "none" if the number of elements is not known or not fixed.
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 |
283 | Kind / method quick index
284 |
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 |
--------------------------------------------------------------------------------