From 6788383349177580ef2cd9e76a07b17d94115043 Mon Sep 17 00:00:00 2001 From: zyxwvu Shi Date: Sun, 3 Nov 2024 18:00:00 +0800 Subject: [PATCH] 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 --- src/lj_cconv.c | 10 ++++++++++ src/lj_cparse.c | 5 +++-- src/lj_crecord.c | 4 ++++ src/lj_ctype.c | 6 ++++++ src/lj_ctype.h | 1 + src/lj_gc.c | 15 ++++++++++++++- 6 files changed, 38 insertions(+), 3 deletions(-) diff --git a/src/lj_cconv.c b/src/lj_cconv.c index 419a8f45..c74c9c52 100644 --- a/src/lj_cconv.c +++ b/src/lj_cconv.c @@ -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; } diff --git a/src/lj_cparse.c b/src/lj_cparse.c index 9774f3a5..b6185dca 100644 --- a/src/lj_cparse.c +++ b/src/lj_cparse.c @@ -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. */ diff --git a/src/lj_crecord.c b/src/lj_crecord.c index e8ae426d..8b86e999 100644 --- a/src/lj_crecord.c +++ b/src/lj_crecord.c @@ -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. */ diff --git a/src/lj_ctype.c b/src/lj_ctype.c index 0f6baac9..31dae2a2 100644 --- a/src/lj_ctype.c +++ b/src/lj_ctype.c @@ -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 { diff --git a/src/lj_ctype.h b/src/lj_ctype.h index d53c4ea4..1769528c 100644 --- a/src/lj_ctype.h +++ b/src/lj_ctype.h @@ -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) \ diff --git a/src/lj_gc.c b/src/lj_gc.c index bfa4daa8..295c0383 100644 --- a/src/lj_gc.c +++ b/src/lj_gc.c @@ -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);