FFI: Add ctype metamethods and ffi.metatype().
This commit is contained in:
116
src/lib_ffi.c
116
src/lib_ffi.c
@@ -18,6 +18,7 @@
|
||||
#include "lj_err.h"
|
||||
#include "lj_str.h"
|
||||
#include "lj_tab.h"
|
||||
#include "lj_meta.h"
|
||||
#include "lj_ctype.h"
|
||||
#include "lj_cparse.h"
|
||||
#include "lj_cdata.h"
|
||||
@@ -96,6 +97,41 @@ static int32_t ffi_checkint(lua_State *L, int narg)
|
||||
|
||||
#define LJLIB_MODULE_ffi_meta
|
||||
|
||||
/* Handle ctype __index/__newindex metamethods. */
|
||||
static int ffi_index_meta(lua_State *L, CTState *cts, CType *ct, MMS mm)
|
||||
{
|
||||
CTypeID id = ctype_typeid(cts, ct);
|
||||
cTValue *tv = lj_ctype_meta(cts, id, mm);
|
||||
TValue *base = L->base;
|
||||
if (!tv) {
|
||||
const char *s;
|
||||
err_index:
|
||||
s = strdata(lj_ctype_repr(L, id, NULL));
|
||||
if (tvisstr(L->base+1))
|
||||
lj_err_callerv(L, LJ_ERR_FFI_BADMEMBER, s, strVdata(L->base+1));
|
||||
else
|
||||
lj_err_callerv(L, LJ_ERR_FFI_BADIDX, s);
|
||||
}
|
||||
if (!tvisfunc(tv)) {
|
||||
if (mm == MM_index) {
|
||||
cTValue *o = lj_meta_tget(L, tv, base+1);
|
||||
if (o) {
|
||||
if (tvisnil(o)) goto err_index;
|
||||
copyTV(L, L->top-1, o);
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
TValue *o = lj_meta_tset(L, tv, base+1);
|
||||
if (o) {
|
||||
copyTV(L, o, base+2);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
tv = L->top-1;
|
||||
}
|
||||
return lj_meta_tailcall(L, tv);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___index) LJLIB_REC(cdata_index 0)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
@@ -106,6 +142,8 @@ LJLIB_CF(ffi_meta___index) LJLIB_REC(cdata_index 0)
|
||||
if (!(o+1 < L->top && tviscdata(o))) /* Also checks for presence of key. */
|
||||
lj_err_argt(L, 1, LUA_TCDATA);
|
||||
ct = lj_cdata_index(cts, cdataV(o), o+1, &p, &qual);
|
||||
if ((qual & 1))
|
||||
return ffi_index_meta(L, cts, ct, MM_index);
|
||||
if (lj_cdata_get(cts, ct, L->top-1, p))
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
@@ -121,6 +159,11 @@ LJLIB_CF(ffi_meta___newindex) LJLIB_REC(cdata_index 1)
|
||||
if (!(o+2 < L->top && tviscdata(o))) /* Also checks for key and value. */
|
||||
lj_err_argt(L, 1, LUA_TCDATA);
|
||||
ct = lj_cdata_index(cts, cdataV(o), o+1, &p, &qual);
|
||||
if ((qual & 1)) {
|
||||
if ((qual & CTF_CONST))
|
||||
lj_err_caller(L, LJ_ERR_FFI_WRCONST);
|
||||
return ffi_index_meta(L, cts, ct, MM_newindex);
|
||||
}
|
||||
lj_cdata_set(cts, ct, p, o+2, qual);
|
||||
return 0;
|
||||
}
|
||||
@@ -138,7 +181,7 @@ LJLIB_CF(ffi_meta___eq) LJLIB_REC(cdata_arith MM_eq)
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___len)
|
||||
LJLIB_CF(ffi_meta___len) LJLIB_REC(cdata_arith MM_len)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
@@ -153,11 +196,21 @@ LJLIB_CF(ffi_meta___le) LJLIB_REC(cdata_arith MM_le)
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
LJLIB_CF(ffi_meta___concat)
|
||||
LJLIB_CF(ffi_meta___concat) LJLIB_REC(cdata_arith MM_concat)
|
||||
{
|
||||
return ffi_arith(L);
|
||||
}
|
||||
|
||||
/* Handle ctype __call metamethod. */
|
||||
static int ffi_call_meta(lua_State *L, CTypeID id)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
cTValue *tv = lj_ctype_meta(cts, id, MM_call);
|
||||
if (!tv)
|
||||
lj_err_callerv(L, LJ_ERR_FFI_BADCALL, strdata(lj_ctype_repr(L, id, NULL)));
|
||||
return lj_meta_tailcall(L, tv);
|
||||
}
|
||||
|
||||
/* Forward declaration. */
|
||||
static int lj_cf_ffi_new(lua_State *L);
|
||||
|
||||
@@ -168,8 +221,7 @@ LJLIB_CF(ffi_meta___call) LJLIB_REC(cdata_call)
|
||||
if (cd->typeid == CTID_CTYPEID)
|
||||
return lj_cf_ffi_new(L);
|
||||
if ((ret = lj_ccall_func(L, cd)) < 0)
|
||||
lj_err_callerv(L, LJ_ERR_FFI_BADCALL,
|
||||
strdata(lj_ctype_repr(L, cd->typeid, NULL)));
|
||||
return ffi_call_meta(L, cd->typeid);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -226,6 +278,12 @@ LJLIB_CF(ffi_meta___tostring)
|
||||
setstrV(L, L->top-1, lj_ctype_repr_int64(L, *(uint64_t *)cdataptr(cd),
|
||||
(ct->info & CTF_UNSIGNED)));
|
||||
goto checkgc;
|
||||
} else if (ctype_isstruct(ct->info) || ctype_isvector(ct->info)) {
|
||||
/* Handle ctype __tostring metamethod. */
|
||||
CTState *cts = ctype_cts(L);
|
||||
cTValue *tv = lj_ctype_meta(cts, id, MM_tostring);
|
||||
if (tv)
|
||||
return lj_meta_tailcall(L, tv);
|
||||
}
|
||||
}
|
||||
lj_str_pushf(L, msg, strdata(lj_ctype_repr(L, id, NULL)), cdataptr(cd));
|
||||
@@ -234,6 +292,8 @@ checkgc:
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_PUSH("ffi") LJLIB_SET(__metatable)
|
||||
|
||||
#include "lj_libdef.h"
|
||||
|
||||
/* -- C library metamethods ----------------------------------------------- */
|
||||
@@ -331,14 +391,14 @@ LJLIB_CF(ffi_new) LJLIB_REC(.)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkctype(L, cts);
|
||||
CType *ct = ctype_raw(cts, id);
|
||||
CTSize sz;
|
||||
CTInfo info = lj_ctype_info(cts, id, &sz);
|
||||
TValue *o = L->base+1;
|
||||
GCcdata *cd;
|
||||
if ((info & CTF_VLA)) {
|
||||
o++;
|
||||
sz = lj_ctype_vlsize(cts, ctype_raw(cts, id),
|
||||
(CTSize)ffi_checkint(L, 2));
|
||||
sz = lj_ctype_vlsize(cts, ct, (CTSize)ffi_checkint(L, 2));
|
||||
}
|
||||
if (sz == CTSIZE_INVALID)
|
||||
lj_err_arg(L, 1, LJ_ERR_FFI_INVSIZE);
|
||||
@@ -347,8 +407,21 @@ LJLIB_CF(ffi_new) LJLIB_REC(.)
|
||||
else
|
||||
cd = lj_cdata_newv(cts, id, sz, ctype_align(info));
|
||||
setcdataV(L, o-1, cd); /* Anchor the uninitialized cdata. */
|
||||
lj_cconv_ct_init(cts, ctype_raw(cts, id), sz, cdataptr(cd),
|
||||
lj_cconv_ct_init(cts, ct, sz, cdataptr(cd),
|
||||
o, (MSize)(L->top - o)); /* Initialize cdata. */
|
||||
if (ctype_isstruct(ct->info)) {
|
||||
/* Handle ctype __gc metamethod. Use the fast lookup here. */
|
||||
cTValue *tv = lj_tab_getint(cts->metatype, (int32_t)id);
|
||||
if (tv && tvistab(tv) && (tv = lj_meta_fast(L, tabV(tv), MM_gc))) {
|
||||
GCtab *t = cts->finalizer;
|
||||
if (gcref(t->metatable)) {
|
||||
/* Add to finalizer table, if still enabled. */
|
||||
copyTV(L, lj_tab_set(L, t, o-1), tv);
|
||||
lj_gc_anybarriert(L, t);
|
||||
cd->marked |= LJ_GC_CDATA_FIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
L->top = o; /* Only return the cdata itself. */
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
@@ -521,7 +594,33 @@ LJLIB_CF(ffi_abi) LJLIB_REC(.)
|
||||
|
||||
#undef H_
|
||||
|
||||
LJLIB_PUSH(top-7) LJLIB_SET(!) /* Store reference to weak table. */
|
||||
LJLIB_PUSH(top-8) LJLIB_SET(!) /* Store reference to metatype table. */
|
||||
|
||||
LJLIB_CF(ffi_metatype)
|
||||
{
|
||||
CTState *cts = ctype_cts(L);
|
||||
CTypeID id = ffi_checkctype(L, cts);
|
||||
GCtab *mt = lj_lib_checktab(L, 2);
|
||||
GCtab *t = cts->metatype;
|
||||
CType *ct = ctype_get(cts, id); /* Only allow raw types. */
|
||||
TValue *tv;
|
||||
GCcdata *cd;
|
||||
if (!(ctype_isstruct(ct->info) || ctype_iscomplex(ct->info) ||
|
||||
ctype_isvector(ct->info)))
|
||||
lj_err_arg(L, 1, LJ_ERR_FFI_INVTYPE);
|
||||
tv = lj_tab_setint(L, t, (int32_t)id);
|
||||
if (!tvisnil(tv))
|
||||
lj_err_caller(L, LJ_ERR_PROTMT);
|
||||
settabV(L, tv, mt);
|
||||
lj_gc_anybarriert(L, t);
|
||||
cd = lj_cdata_new(cts, CTID_CTYPEID, 4);
|
||||
*(CTypeID *)cdataptr(cd) = id;
|
||||
setcdataV(L, L->top-1, cd);
|
||||
lj_gc_check(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
LJLIB_PUSH(top-7) LJLIB_SET(!) /* Store reference to finalizer table. */
|
||||
|
||||
LJLIB_CF(ffi_gc)
|
||||
{
|
||||
@@ -590,6 +689,7 @@ static void ffi_register_module(lua_State *L)
|
||||
LUALIB_API int luaopen_ffi(lua_State *L)
|
||||
{
|
||||
CTState *cts = lj_ctype_init(L);
|
||||
settabV(L, L->top++, (cts->metatype = lj_tab_new(L, 0, 0)));
|
||||
cts->finalizer = ffi_finalizer(L);
|
||||
LJ_LIB_REG(L, NULL, ffi_meta);
|
||||
/* NOBARRIER: basemt is a GC root. */
|
||||
|
||||
Reference in New Issue
Block a user