FFI: Add ctype metamethods and ffi.metatype().

This commit is contained in:
Mike Pall
2011-04-12 19:15:00 +02:00
parent fa5cd010e8
commit 3b6f37dd2c
11 changed files with 426 additions and 83 deletions

View File

@@ -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. */