Hi, Sergey! Thanks for the patch! Please, see the commends below. -- Best regards, Evgeniy Temirgaleev > > From: Sergey Kaplun > To: Sergey Bronnikov , Evgeniy Temirgaleev > > Cc: tarantool-patches@dev.tarantool.org, Sergey Kaplun > > Date: Thursday, June 25, 2026 11:29 PM +03:00 > This patch extends dumped information for the given cdata object. Now > it resolves the given `CType` and prints it in the format similar to the > `__tostring` metamethod. The `lj-ctype` command is introduced to dump > this information where there is only the `CType` pointer but no cdata > associated with it. > > `__or__` and `__ror__` metamethods are monkey-patched for the LLDB value > object. In `__sub__` metamethod for LLDB pointers `GetPointeeType()` is > used to get the pointee type instead of the incorrectly used > `GetDereferencedType()` which always returns the same type with size 8. > Casting from negative values to the unsigned values is supported to > check `CTF_UCHAR`. > > Part of tarantool/tarantool#4808 > --- > src/luajit_dbg.py | 333 +++++++++++++++++- > .../debug-extension-tests.py | 208 ++++++++++- > 2 files changed, 535 insertions(+), 6 deletions(-) > > diff --git a/src/luajit_dbg.py b/src/luajit_dbg.py > index fd6ca8a5..62cd65d5 100644 > --- a/src/luajit_dbg.py > +++ b/src/luajit_dbg.py > @@ -386,6 +386,8 @@ class _LLDBDebugger(Debugger): > pack_flag = ' else: > pack_flag = ' + # Cast to unsigned. > Is /unsigned/uint64_t/ clearly? > > + raw_value &= 0xFFFFFFFFFFFFFFFF > raw_data = struct.pack(pack_flag, raw_value) > sbdata = lldb.SBData() > sbdata.SetData( > @@ -482,6 +484,9 @@ class _LLDBDebugger(Debugger): > def lldb__lt__(lldbval, other): > return int(lldbval) < int(other) > > + def lldb__or__(lldbval, other): > + return int(lldbval) | int(other) > + > def lldb__str__(lldbval): > # Instead of default GetSummary. > if not lldbval.sbvalue.TypeIsPointerType(): > @@ -512,8 +517,8 @@ class _LLDBDebugger(Debugger): > lldbval_tp = sbval.GetType() > other_tp = osbval.GetType() > # Subtract pointers of the same size only. > - elsz = lldbval_tp.GetDereferencedType().size > - if other_tp.GetDereferencedType().size != elsz: > + elsz = lldbval_tp.GetPointeeType().size > + if other_tp.GetPointeeType().size != elsz: > raise Exception( > 'Attempt to substruct {otp} from {stp}'.format( > stp=lldbval_tp.name, > @@ -536,6 +541,8 @@ class _LLDBDebugger(Debugger): > lldb.value.__index__ = lldb__index__ > lldb.value.__le__ = lldb__le__ > lldb.value.__lt__ = lldb__lt__ > + lldb.value.__or__ = lldb__or__ > + lldb.value.__ror__ = lldb__or__ # Same semantics. > lldb.value.__str__ = lldb__str__ > lldb.value.__sub__ = lldb__sub__ > > @@ -1352,6 +1359,119 @@ def lightudV(tv): > return gcval(tv['gcr']) > > > +# FFI. > + > + > +def ctype_ctsG(g): > + return mref('CTState *', g['ctype_state']) > + > + > +def ctype_get(cts, id): > + return dbg.address(cts['tab'][id]) > + > + > +# Externally visible types. > +CT_NUM = 0 # Integer or floating-point numbers. > +CT_STRUCT = 1 # Struct or union. > +CT_PTR = 2 # Pointer or reference. > +CT_ARRAY = 3 # Array or complex type. > +CT_MAYCONVERT = CT_ARRAY > +CT_VOID = 4 # Void type. > +CT_ENUM = 5 # Enumeration. > +CT_HASSIZE = CT_ENUM # Last type where ct->size holds the actual size. > +CT_FUNC = 6 # Function. > +CT_TYPEDEF = 7 # Typedef. > +CT_ATTRIB = 8 # Miscellaneous attributes. > + > +# Common types. > +CTID_CTYPEID = 21 > + > +# C type info flags. > +CTF_BOOL = 0x08000000 # Boolean: NUM, BITFIELD. > +CTF_FP = 0x04000000 # Floating-point: NUM. > +CTF_CONST = 0x02000000 # Const qualifier. > +CTF_VOLATILE = 0x01000000 # Volatile qualifier. > +CTF_UNSIGNED = 0x00800000 # Unsigned: NUM, BITFIELD. > +CTF_LONG = 0x00400000 # Long: NUM. > +CTF_VLA = 0x00100000 # Variable-length: ARRAY, STRUCT. > +CTF_REF = 0x00800000 # Reference: PTR. > +CTF_VECTOR = 0x08000000 # Vector: ARRAY. > +CTF_COMPLEX = 0x04000000 # Complex: ARRAY. > +CTF_UNION = 0x00800000 # Union: STRUCT. > +CTF_VARARG = 0x00800000 # Vararg: FUNC. > +CTF_SSEREGPARM = 0x00400000 # SSE register parameters: FUNC. > + > +CTF_UCHAR = CTF_UNSIGNED if int(dbg.cast('char', -1)) > 0 else 0 > + > +CTMASK_ATTRIB = 255 # Max. 256 attributes. > +CTSHIFT_ATTRIB = 16 > + > +# Attribute numbers. > +CTA_QUAL = 1 # Unmerged qualifiers. > + > +CTSHIFT_NUM = 28 > +CTMASK_CID = 0x0000ffff > +CTMASK_NUM = 0xf0000000 # Max. 16 type numbers. > + > +# Special sizes. > +CTSIZE_INVALID = 0xffffffff > +DWORDSZ = 4 > +QWORDSZ = 8 > + > + > +def ctype_type(info): > + return info >> CTSHIFT_NUM > + > + > +def ctype_attrib(info): > + return (info >> CTSHIFT_ATTRIB) & CTMASK_ATTRIB > + > + > +def ctinfo(ct, flags): > May we name this function ‘CTINFO’ as in ‘lj_ctype.h’? Or leave a comment with an original name for quick grep. > > + return (tou32(ct) << CTSHIFT_NUM) + flags > + > + > +def ctype_isptr(info): > + return ctype_type(info) == CT_PTR > + > + > +def ctype_iscomplex(info): > + return (info & (CTMASK_NUM | CTF_COMPLEX)) == ctinfo(CT_ARRAY, > CTF_COMPLEX) > + > + > +def ctype_isinteger(info): > + return (info & (CTMASK_NUM | CTF_BOOL | CTF_FP)) == ctinfo(CT_NUM, 0) > + > + > +def ctype_isrefarray(info): > + return (info & (CTMASK_NUM | CTF_VECTOR | CTF_COMPLEX)) == \ > + ctinfo(CT_ARRAY, 0) > + > + > +def ctype_cid(info): > Let’s put these function definitions in the ‘lj_ctype.h’ order? May we group the definitions by corresponding C files also? # lj_ctype.h … # lj_cdata.h … # lj_xxx.c … > > + return info & CTMASK_CID > + > + > +def ctype_child(cts, ctype): > + return ctype_get(cts, ctype_cid(ctype['info'])) > + > + > +def cdataptr(cd): > + return dbg.cast('void *', (cd + 1)) > + > + > +def cdata_getptr(p, size): > + if LJ_64 and size == 4: > + return dbg.cast('void *', dbg.cast('uint32_t *', p)[0]) > + else: > assert for size == 8 ? > > + return dbg.cast('void *', dbg.cast('uint64_t *', p)[0]) > + > + > +# Get C type ID for a C type. > +def ctype_typeid(cts, ct): > + return ct - cts['tab'] > + > + > # JIT engine. > > > @@ -1951,7 +2071,26 @@ def dump_lj_gco_trace(gcobj): > > > def dump_lj_gco_cdata(gcobj): > - return 'cdata @ {}'.format(strx64(gcobj)) > + cdata = dbg.cast('struct GCcdata *', gcobj) > + cts = ctype_ctsG(G(L())) > + cid = cdata['ctypeid'] > + ctype = ctype_get(cts, cid) > + info = ctype['info'] > + size = ctype['size'] > + value = '' > + if ctype_iscomplex(info): > + value = cdata_val_complex(cdata, ctype) > + elif size == 8 and ctype_isinteger(info): > + value = cdata_val_int64(cdata, ctype) > + else: > + value = cdataptr(cdata) > + if ctype_isptr(info): > + value = cdata_getptr(value, size) > + return 'cdata @ {addr} {ctype} {value}'.format( > + addr=strx64(gcobj), > + ctype=dump_ctype(ctype), > + value=value, > + ) > > > def dump_lj_gco_tab(gcobj): > @@ -2281,6 +2420,176 @@ def dump_func(func): > return 'fast function #{}\n'.format(int(ffid)) > > > +# FFI dumpers. > + > + > +def cdata_val_int64(cdata, ctype): > + info = ctype['info'] > + isunsigned = info & CTF_UNSIGNED > + cdataval = cdataptr(cdata) > + valueptr = None > + usuffix = '' > + if isunsigned: > + usuffix = 'U' > + valueptr = dbg.cast('uint64_t *', cdataval) > + else: > + valueptr = dbg.cast('int64_t *', cdataval) > + return str(valueptr[0]) + usuffix + 'LL' > + > + > +def cdata_val_complex(cdata, ctype): > + size = ctype['size'] > + cdataval = cdataptr(cdata) > + casttype = None > + if size == QWORDSZ * 2: > + casttype = 'double *' > + else: > + assert size == DWORDSZ * 2, 'bad (complex float) size' > + casttype = 'float *' > + re = dbg.cast(casttype, cdataval)[0] > + im = dbg.cast(casttype, cdataval)[1] > + sign = '+' if im > 0 else '' > + return '{re}{sign}{im}i'.format(re=re, im=im, sign=sign) > + > + > +def ctype_preplit(ctypestr, lit): > + # Prevent extra space in the end of the string. > + space = ' ' if ctypestr != '' else '' > + return lit + space + ctypestr > + > + > +def ctype_prepqual(ctypestr, info): > + if (info & CTF_VOLATILE): > + ctypestr = ctype_preplit(ctypestr, 'volatile') > + if (info & CTF_CONST): > + ctypestr = ctype_preplit(ctypestr, 'const') > + return ctypestr > + > + > +def ctype_preptype(cts, ctypestr, ctype, qual, tp): > + nameref = gcref(ctype['name']) > + if nameref: > + ctypestr = ctype_preplit(ctypestr, re.sub('"', '', strdata(nameref))) > + else: > + ctypestr = ctype_preplit(ctypestr, str(ctype_typeid(cts, ctype))) > + ctypestr = ctype_preplit(ctypestr, tp) > + ctypestr = ctype_prepqual(ctypestr, qual) > + return ctypestr > + > + > +def ctype_prepnum(ctypestr, info, size): > Func proto differs with lj_ctype.c (static void ctype_prepnum(CTRepr *ctr, uint32_t n)). It seems, you move some of ctype_repr() code here. Let’s comment it? > > + if info & CTF_BOOL: > + ctypestr = ctype_preplit(ctypestr, 'bool') > + elif info & CTF_FP: > + if size == QWORDSZ: > + ctypestr = ctype_preplit(ctypestr, 'double') > + elif size == DWORDSZ: > + ctypestr = ctype_preplit(ctypestr, 'float') > + else: > + assert size == QWORDSZ * 2, 'bad (long double) size' > + ctypestr = ctype_preplit(ctypestr, 'long double') > + elif size == 1: > + if not ((info ^ CTF_UCHAR) & CTF_UNSIGNED): > + ctypestr = ctype_preplit(ctypestr, 'char') > + elif CTF_UCHAR: > + ctypestr = ctype_preplit(ctypestr, 'signed char') > > > + else: > + ctypestr = ctype_preplit(ctypestr, 'unsigned char') > + elif size < 8: > + if size == 4: > + ctypestr = ctype_preplit(ctypestr, 'int') > + else: > + assert size == DWORDSZ // 2, 'bad (short) size' > + ctypestr = ctype_preplit(ctypestr, 'short') > + if info & CTF_UNSIGNED: > + ctypestr = ctype_preplit(ctypestr, 'unsigned') > + else: > + size_t = '{u}int{sz}_t'.format( > + u='u' if info & CTF_UNSIGNED else '', > + sz=size * 8, > + ) > + ctypestr = ctype_preplit(ctypestr, size_t) > + return ctypestr > + > + > +def ctype_repr(cts, id): > + ctype = ctype_get(cts, id) > + ctypestr = '' > + qual = 0 > + ptrto = 0 > + while True: > + info = ctype['info'] > + size = ctype['size'] > + ctp = ctype_type(info) > + if ctp == CT_NUM: > + ctypestr = ctype_prepnum(ctypestr, info, size) > + return ctype_prepqual(ctypestr, qual | info) > + elif ctp == CT_VOID: > + ctypestr = ctype_preplit(ctypestr, 'void') > + return ctype_prepqual(ctypestr, qual | info) > + elif ctp == CT_STRUCT: > + tp = 'union' if (info & CTF_UNION) else 'struct' > + return ctype_preptype(cts, ctypestr, ctype, qual, tp) > + elif ctp == CT_ENUM: > + if id == CTID_CTYPEID: > + return ctype_preplit(ctypestr, 'ctype') > + return ctype_preptype(cts, ctypestr, ctype, qual, 'enum') > + elif ctp == CT_ATTRIB: > + if ctype_attrib(info) == CTA_QUAL: > + qual |= size > + elif ctp == CT_PTR: > + if info & CTF_REF: > + ctypestr = ctype_preplit(ctypestr, '&') > + else: > + ctypestr = ctype_prepqual(ctypestr, qual | info) > + if LJ_64 and size == 4: > + ctypestr = ctype_preplit(ctypestr, '__ptr32') > + ctypestr = ctype_preplit(ctypestr, '*') > + qual = 0 > + ptrto = 1 > + elif ctp == CT_ARRAY: > + if ctype_isrefarray(info): > + if ptrto: > + ptrto = 0 > + ctypestr = '(' + ctypestr + ')' > + arrsize = '' > + if size != CTSIZE_INVALID: > + child_size = ctype_child(cts, ctype)['size'] > + arrsize = str(int(size / child_size) if child_size > 0 > + else 0) > + elif info & CTF_VLA: > + arrsize = '?' > + ctypestr = ctypestr + '[{}]'.format(arrsize) > + elif ctype_iscomplex(info): > + if size == DWORDSZ * 2: > + ctypestr = ctype_preplit(ctypestr, 'float') > + else: > + assert size == QWORDSZ * 2, 'bad (complex double) size' > + return ctype_preplit(ctypestr, 'complex') > + else: > + ctypestr = ctype_preplit( > + ctypestr, > + '__attribute__((vector_size({})))'.format(size) > + ) > + elif ctp == CT_FUNC: > + if ptrto: > + ptrto = 0 > + ctypestr = '(' + ctypestr + ')' > + ctypestr += '()' > + ctype = ctype_child(cts, ctype) > + return 'NYI' > + > + > +def dump_ctype(ct): > Also, it seems, it will be easy to read to code, if it will be possible to distinguish between ported functions and extension itself ones. May be by use the ‘dbg_’ prefix for extension function names. > > + cts = ctype_ctsG(G(L())) > + cid = ctype_typeid(cts, ct) > + name = ctype_repr(cts, cid) > + return '[{id}] <{name}>'.format( > + id=cid, > + name=name, > + ) > + > + > # JIT dumpers. > > > @@ -2294,7 +2603,8 @@ def dump_call_func(trace, callop): > assert IRS[cdt_idx_irk['o']] == 'KINT', \ > 'unexpected IR for ctype storage' > ctype_idx = cdt_idx_irk['i'] > - ctype = 'ctype: {}'.format(ctype_idx) > + cts = ctype_ctsG(G(L())) > + ctype = 'ctype: {}'.format(dump_ctype(ctype_get(cts, ctype_idx))) > > func_str = '' > if callop < 0: > @@ -2652,6 +2962,20 @@ https://github.com/tarantool/tarantool/wiki/LuaJIT-Bytecodes > . > )) > > > +class LJDumpCType(dbg.LJBase): > + ''' > +lj-ctype > + > +The command receives a pointer of the corresponding CType > +and dumps the ID and the name for this C data type. > + ''' > + > + def execute(self, arg): > + dbg.write('{}\n'.format( > + dump_ctype(dbg.cast('CType *', dbg.eval(arg))) > + )) > + > + > class LJDumpFunc(dbg.LJBase): > ''' > lj-func > @@ -2979,6 +3303,7 @@ def load(event=None): > dbg.initialize_extension({ > 'lj-arch': LJDumpArch, > 'lj-bc': LJDumpBC, > + 'lj-ctype': LJDumpCType, > 'lj-func': LJDumpFunc, > 'lj-gc': LJGC, > 'lj-gco': LJDumpGCobj, > diff --git a/test/tarantool-debugger-tests/debug-extension-tests.py > b/test/tarantool-debugger-tests/debug-extension-tests.py > index 76543daa..fc5d2c7b 100644 > --- a/test/tarantool-debugger-tests/debug-extension-tests.py > +++ b/test/tarantool-debugger-tests/debug-extension-tests.py > @@ -227,6 +227,7 @@ class TestLoad(TestCaseBase): > pattern = ( > r'lj-arch command initialized\n' > r'lj-bc command initialized\n' > + r'lj-ctype command initialized\n' > r'lj-func command initialized\n' > r'lj-gc command initialized\n' > r'lj-gco command initialized\n' > @@ -331,7 +332,7 @@ GCO_RX = ( > r'Lua function @ ' + RX_ADDR + r', [0-9]+ upvalues, .+:[0-9]+\n' > r'C function @ ' + RX_ADDR + r'\n' > r'fast function #[0-9]+\n' > - r'cdata @ ' + RX_ADDR + r'\n' > + r'cdata @ ' + RX_ADDR + r' \[\d+\] 0x0\n' > r'table @ ' + RX_ADDR + r' \(asize: \d+, hmask: ' + RX_HASH + r'\)\n' > r'userdata @ ' + RX_ADDR + r'\n' > ) > @@ -817,7 +818,9 @@ class TestLJIRCallXSCType(TestCaseBase): > 'trace()\n' > 'print()\n' > ) > - pattern = r'int CALLXS .* [' + RX_ADDR + r'\]\(.*\) ctype: \d+' > + pattern = ( > + r'int CALLXS .* [' + RX_ADDR + r'\]\(.*\) ctype: \[\d+\] ' > + ) > > > class TestLJJSlotsBase(TestCaseBase): > @@ -838,6 +841,207 @@ class TestLJJSlotsBase(TestCaseBase): > ) > > > +def cdata_rx(tpstr, suffix=None): > + return r'cdata @ ' + RX_ADDR + r' \[\d+\] <' + tpstr + '> ' + ( > + RX_ADDR if not suffix else suffix > + ) > + > + > +CHAR_SIGNED = machine in ['arm64', 'aarch64'] and sys.platform != > 'darwin' > +HAS_LONG_DOUBLE = not (machine in ['arm64', 'aarch64'] and > + sys.platform == 'darwin') > + > + > +class TestLJCTypePrim(TestCaseBase): > + location = 'lj_cf_print' > + extension_cmds = ( > + 'n\n' # Load L. > + 'lj-tv L->base\n' > + 'lj-tv L->base + 1\n' > + 'lj-tv L->base + 2\n' > + 'lj-tv L->base + 3\n' > + 'lj-tv L->base + 4\n' > + 'lj-tv L->base + 5\n' > + 'lj-tv L->base + 6\n' > + 'lj-tv L->base + 7\n' > + 'lj-tv L->base + 8\n' > + 'lj-tv L->base + 9\n' > + 'lj-tv L->base + 10\n' > + 'lj-tv L->base + 11\n' > + 'lj-tv L->base + 12\n' > + 'lj-tv L->base + 13\n' > + 'lj-tv L->base + 14\n' > + 'lj-tv L->base + 15\n' > + 'lj-tv L->base + 16\n' > + 'lj-tv L->base + 17\n' > + 'lj-tv L->base + 18\n' > + 'lj-tv L->base + 19\n' > + 'lj-tv L->base + 20\n' > + 'lj-tv L->base + 21\n' > + 'lj-tv L->base + 22\n' > + ) > + lua_script = ( > + 'local ffi = require("ffi")\n' > + 'print(\n' > + ' ffi.new("bool"),\n' > + ' ffi.new("char"),\n' > + ' ffi.new("signed char"),\n' > + ' ffi.new("unsigned char"),\n' > + ' ffi.new("int"),\n' > + ' ffi.new("short"),\n' > + ' ffi.new("unsigned"),\n' > + ' ffi.new("int8_t"),\n' > + ' ffi.new("int16_t"),\n' > + ' ffi.new("int32_t"),\n' > + ' ffi.new("int64_t"),\n' > + ' ffi.new("uint8_t"),\n' > + ' ffi.new("uint64_t"),\n' > + ' ffi.new("float"),\n' > + ' ffi.new("double"),\n' > + ' ffi.new("long double"),\n' > + ' 1i,\n' > + ' ffi.new("complex float", 1, -2),\n' > + ' ffi.new("const volatile int"),\n' > + ' ffi.new("void *"),\n' > + ' ffi.new("void * __ptr32"),\n' > + ' ffi.new("int &"),\n' > + ' ffi.typeof(1LL)\n' > + ')\n' > + ) > + pattern = ( > + cdata_rx('bool') + r'\n' + > + cdata_rx('char') + r'\n' + > + cdata_rx(('signed ' if CHAR_SIGNED else '') + 'char') + r'\n' + > + cdata_rx(('unsigned ' if not CHAR_SIGNED else '') + 'char') + r'\n' + > + cdata_rx('int') + r'\n' + > + cdata_rx('short') + r'\n' + > + cdata_rx('unsigned int') + r'\n' + > + cdata_rx(('signed ' if CHAR_SIGNED else '') + 'char') + r'\n' + > + cdata_rx('short') + r'\n' + > + cdata_rx('int') + r'\n' + > + cdata_rx('int64_t', '0LL') + r'\n' + > + cdata_rx(('unsigned ' if not CHAR_SIGNED else '') + 'char') + r'\n' + > + cdata_rx('uint64_t', '0ULL') + r'\n' + > + cdata_rx('float') + r'\n' + > + cdata_rx('double') + r'\n' + > + cdata_rx(('long ' if HAS_LONG_DOUBLE else '') + 'double') + r'\n' + > + cdata_rx('complex', r'0\+1i') + r'\n' + > + cdata_rx('complex float', '1-2i') + r'\n' + > + cdata_rx('const volatile int') + r'\n' + > + cdata_rx(r'void \*') + r'\n' + > + cdata_rx(r'void \* __ptr32') + r'\n' + > + cdata_rx('int &') + r'\n' + > + cdata_rx('ctype') + r'\n' > + ) > + > + > +class TestLJCTypeStructUnionEnum(TestCaseBase): > + location = 'lj_cf_print' > + extension_cmds = ( > + 'n\n' # Load L. > + 'lj-tv L->base\n' > + 'lj-tv L->base + 1\n' > + 'lj-tv L->base + 2\n' > + 'lj-tv L->base + 3\n' > + ) > + lua_script = ( > + 'local ffi = require("ffi")\n' > + 'ffi.cdef[[\n' > + ' struct test {int a;};\n' > + ']]\n' > + 'print(\n' > + ' ffi.new("struct test"),\n' > + ' ffi.new("struct {int a;}"),\n' > + ' ffi.new("union {int a;}"),\n' > + ' ffi.new("enum {ENUM1}")\n' > + ')\n' > + ) > + pattern = ( > + cdata_rx('struct test') + r'\n' + > + cdata_rx(r'struct \d+') + r'\n' + > + cdata_rx(r'union \d+') + r'\n' + > + cdata_rx(r'enum \d+') + r'\n' > + ) > + > + > +class TestLJCTypeArray(TestCaseBase): > + location = 'lj_cf_print' > + extension_cmds = ( > + 'n\n' # Load L. > + 'lj-tv L->base\n' > + 'lj-tv L->base + 1\n' > + 'lj-tv L->base + 2\n' > + 'lj-tv L->base + 3\n' > + 'lj-tv L->base + 4\n' > + 'lj-tv L->base + 5\n' > + 'lj-tv L->base + 6\n' > + ) > + lua_script = ( > + 'local ffi = require("ffi")\n' > + 'print(\n' > + ' ffi.new("char [0]"),\n' > + ' ffi.new("int [1]"),\n' > + ' ffi.new("complex [2]"),\n' > + ' ffi.new("complex float [3]"),\n' > + ' ffi.new("float __attribute__((vector_size(4)))"),\n' > + ' ffi.new("int (&)[5]"),\n' > + ' ffi.new("int[?]", 6)\n' > + ')\n' > + ) > + pattern = ( > + cdata_rx(r'char \[0\]') + r'\n' + > + cdata_rx(r'int \[1\]') + r'\n' + > + cdata_rx(r'complex \[2\]') + r'\n' + > + cdata_rx(r'complex float \[3\]') + r'\n' + > + cdata_rx(r'float __attribute__\(\(vector_size\(4\)\)\)') + r'\n' + > + cdata_rx(r'int \(&\)\[5\]') + r'\n' + > + cdata_rx(r'int \[\?\]') + r'\n' > + ) > + > + > +class TestLJCTypeFunc(TestCaseBase): > + location = 'lj_cf_print' > + extension_cmds = ( > + 'n\n' # Load L. > + 'lj-tv L->base\n' > + 'lj-tv L->base + 1\n' > + 'lj-tv L->base + 2\n' > + ) > + lua_script = ( > + 'local ffi = require("ffi")\n' > + 'ffi.cdef[[void getpid(void);]]\n' > + 'print(\n' > + ' ffi.C.getpid,\n' > + ' ffi.new("int (*)()"),\n' > + ' ffi.new("int (*(*)(void))[2]")\n' > + ')\n' > + ) > + pattern = ( > + cdata_rx(r'void \(\)') + r'\n' + > + cdata_rx(r'int \(\*\)\(\)') + r'\n' + > + cdata_rx(r'int \(\* \(\*\)\(\)\)\[2\]') + r'\n' > + ) > + > + > +class TestLJCTypeBase(TestCaseBase): > + location = 'lj_cf_ffi_new' > + extension_cmds = ( > + # Load `ct`. Skip inlined functions for LLDB. > The extension command set is common for GDB and LLDB. Does we skip for GDB also? > > + 'n\n' > + 'n\n' > + 'n\n' > + 'n\n' > + 'n\n' > + 'n\n' > + 'lj-ctype ct\n' > + ) > + lua_script = ( > + 'local ffi = require("ffi")\n' > + 'ffi.new("int")\n' > + ) > + pattern = r'\[\d+\] ' > + > + > for test_cls in TestCaseBase.__subclasses__(): > test_cls.test = lambda self: self.check() > > -- > 2.54.0 >