FFI: Allow references of Lua tables in struct

New builtin CType `gctab_t` is introduced to allow references of Lua tables
within struct cdata. Pointer to GCtab is stored and transparent conversion is
performed between table TValue and gctab_t. Lua tables anchored to FFI structs
are visited by GC so they are valid as long as its owner struct cdata is alive.

gctab_t CType serves as a handy way to regain access to the owner Lua object
from the opaque userdata received in FFI callbacks.

Signed-off-by: zyxwvu Shi <i@shiyc.cn>
This commit is contained in:
zyxwvu Shi 2024-11-03 18:00:00 +08:00
parent 97813fb924
commit 6788383349
6 changed files with 38 additions and 3 deletions

View File

@ -408,6 +408,13 @@ int lj_cconv_tv_ct(CTState *cts, CType *s, CTypeID sid,
/* Create reference. */
setcdataV(cts->L, o, lj_cdata_newref(cts, sp, sid));
return 1; /* Need GC step. */
} else if (ctype_isptr(sinfo) && ctype_cid(sinfo) == CTID_GCTAB) {
GCtab *tab = *(GCtab **)sp;
if (tab && tab->gct == ~LJ_TTAB)
settabV(cts->L, o, tab);
else
setnilV(o);
return 0;
} else {
GCcdata *cd;
CTSize sz;
@ -607,6 +614,9 @@ void lj_cconv_ct_tv(CTState *cts, CType *d,
} else if (ctype_isstruct(d->info)) {
cconv_struct_tab(cts, d, dp, tabV(o), flags);
return;
} else if (ctype_isptr(d->info) && ctype_cid(d->info) == CTID_GCTAB) {
*(GCtab **)dp = tabV(o);
return;
} else {
goto err_conv;
}

View File

@ -923,8 +923,9 @@ static CTypeID cp_decl_intern(CPState *cp, CPDecl *decl)
}
}
} else if (ctype_isptr(info)) {
/* Reject pointer/ref to ref. */
if (id && ctype_isref(ctype_raw(cp->cts, id)->info))
/* Reject pointer/ref to ref/gctab_t. */
if (id && (ctype_isref(ctype_raw(cp->cts, id)->info) ||
id == CTID_GCTAB))
cp_err(cp, LJ_ERR_FFI_INVTYPE);
if (ctype_isref(info)) {
info &= ~CTF_VOLATILE; /* Refs are always const, never volatile. */

View File

@ -572,6 +572,10 @@ static TRef crec_tv_ct(jit_State *J, CType *s, CTypeID sid, TRef sp)
}
} else if (ctype_isptr(sinfo) || ctype_isenum(sinfo)) {
sp = emitir(IRT(IR_XLOAD, t), sp, 0); /* Box pointers and enums. */
if (LJ_UNLIKELY(ctype_cid(sinfo) == CTID_GCTAB)) {
emitir(IRTG(IR_NE, t), sp, lj_ir_kptr(J, NULL));
return emitconv(sp, IRT_TAB, t, 0);
}
} else if (ctype_isrefarray(sinfo) || ctype_isstruct(sinfo)) {
cts->L = J->L;
sid = lj_ctype_intern(cts, CTINFO_REF(sid), CTSIZE_PTR); /* Create ref. */

View File

@ -41,6 +41,8 @@
_("uintptr_t", UINT_PSZ) \
/* From POSIX. */ \
_("ssize_t", INT_PSZ) \
/* For anchoring Lua tables to C structs */ \
_("gctab_t", GCTAB) \
/* End of typedef list. */
/* Keywords (only the ones we actually care for). */
@ -509,6 +511,10 @@ static void ctype_repr(CTRepr *ctr, CTypeID id)
if (ctype_attrib(info) == CTA_QUAL) qual |= size;
break;
case CT_PTR:
if (LJ_UNLIKELY(ctype_cid(info) == CTID_GCTAB)) {
ctype_preplit(ctr, "gctab_t");
return;
}
if ((info & CTF_REF)) {
ctype_prepc(ctr, '&');
} else {

View File

@ -296,6 +296,7 @@ typedef struct CTState {
_(DOUBLE, 8, CT_NUM, CTF_FP|CTALIGN(3)) \
_(COMPLEX_FLOAT, 8, CT_ARRAY, CTF_COMPLEX|CTALIGN(2)|CTID_FLOAT) \
_(COMPLEX_DOUBLE, 16, CT_ARRAY, CTF_COMPLEX|CTALIGN(3)|CTID_DOUBLE) \
_(GCTAB, CTSIZE_PTR, CT_PTR, CTALIGN_PTR|CTID_GCTAB) \
_(P_VOID, CTSIZE_PTR, CT_PTR, CTALIGN_PTR|CTID_VOID) \
_(P_CVOID, CTSIZE_PTR, CT_PTR, CTALIGN_PTR|CTID_CVOID) \
_(P_CCHAR, CTSIZE_PTR, CT_PTR, CTALIGN_PTR|CTID_CCHAR) \

View File

@ -75,12 +75,25 @@ static void gc_mark(global_State *g, GCobj *o)
if (gcref(sbx->dict_mt))
gc_markobj(g, gcref(sbx->dict_mt));
}
} else if (gct == ~LJ_TCDATA) {
GCcdata *cd = gco2cd(o);
CType *ct = ctype_get(ctype_ctsG(g), cd->ctypeid);
if (LJ_UNLIKELY(ctype_isstruct(ct->info))) {
while (ct->sib) {
ct = ctype_get(ctype_ctsG(g), ct->sib);
if (ctype_cid(ct->info) == CTID_GCTAB) {
GCobj *t = *(GCobj **)((uintptr_t)cdataptr(cd) + ct->size);
if (t && t->gch.gct == ~LJ_TTAB && iswhite(t))
gc_mark(g, t);
}
}
}
} else if (LJ_UNLIKELY(gct == ~LJ_TUPVAL)) {
GCupval *uv = gco2uv(o);
gc_marktv(g, uvval(uv));
if (uv->closed)
gray2black(o); /* Closed upvalues are never gray. */
} else if (gct != ~LJ_TSTR && gct != ~LJ_TCDATA) {
} else if (gct != ~LJ_TSTR) {
lj_assertG(gct == ~LJ_TFUNC || gct == ~LJ_TTAB ||
gct == ~LJ_TTHREAD || gct == ~LJ_TPROTO || gct == ~LJ_TTRACE,
"bad GC type %d", gct);