FFI: Check for __new metamethod when calling a constructor.

This commit is contained in:
Mike Pall 2012-06-20 18:24:49 +02:00
parent e9e45313e7
commit 8b71ab1080
4 changed files with 65 additions and 44 deletions

View File

@ -599,8 +599,9 @@ C type pointed to by the reference.
</p>
<p>
The pre-defined operations are always tried first before deferring to a
metamethod or index table (if any) for the corresponding ctype. An error
is raised if the metamethod lookup or index table lookup fails.
metamethod or index table (if any) for the corresponding ctype (except
for <tt>__new</tt>). An error is raised if the metamethod lookup or
index table lookup fails.
</p>
<h3 id="cdata_array">Indexing a cdata object</h3>
@ -669,7 +670,12 @@ to <tt>foo.c</tt>.
<ul>
<li><b>Constructor</b>: a ctype object can be called and used as a
<a href="ext_ffi_api.html#ffi_new">constructor</a>.</li>
<a href="ext_ffi_api.html#ffi_new">constructor</a>. This is equivalent
to <tt>ffi.new(ct, ...)</tt>, unless a <tt>__new</tt> metamethod is
defined. The <tt>__new</tt> metamethod is called with the ctype object
plus any other arguments passed to the contructor. Note that you have to
use <tt>ffi.new</tt> inside of it, since calling <tt>ct(...)</tt> would
cause infinite recursion.</li>
<li><b>C&nbsp;function call</b>: a cdata function or cdata function
pointer can be called. The passed arguments are

View File

@ -206,31 +206,34 @@ 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);
CType *ct = ctype_raw(cts, id);
cTValue *tv;
if (ctype_isptr(ct->info)) id = ctype_cid(ct->info);
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);
LJLIB_CF(ffi_meta___call) LJLIB_REC(cdata_call)
{
CTState *cts = ctype_cts(L);
GCcdata *cd = ffi_checkcdata(L, 1);
int ret;
if (cd->typeid == CTID_CTYPEID)
return lj_cf_ffi_new(L);
if ((ret = lj_ccall_func(L, cd)) < 0)
return ffi_call_meta(L, cd->typeid);
return ret;
CTypeID id = cd->typeid;
CType *ct;
cTValue *tv;
MMS mm = MM_call;
if (cd->typeid == CTID_CTYPEID) {
id = *(CTypeID *)cdataptr(cd);
mm = MM_new;
} else {
int ret = lj_ccall_func(L, cd);
if (ret >= 0)
return ret;
}
/* Handle ctype __call/__new metamethod. */
ct = ctype_raw(cts, id);
if (ctype_isptr(ct->info)) id = ctype_cid(ct->info);
tv = lj_ctype_meta(cts, id, mm);
if (tv)
return lj_meta_tailcall(L, tv);
else if (mm == MM_call)
lj_err_callerv(L, LJ_ERR_FFI_BADCALL, strdata(lj_ctype_repr(L, id, NULL)));
return lj_cf_ffi_new(L);
}
LJLIB_CF(ffi_meta___add) LJLIB_REC(cdata_arith MM_add)

View File

@ -941,30 +941,36 @@ static int crec_call(jit_State *J, RecordFFData *rd, GCcdata *cd)
return 0;
}
/* Record ctype call metamethod. */
static void crec_call_meta(jit_State *J, RecordFFData *rd, CTypeID id)
{
CTState *cts = ctype_ctsG(J2G(J));
CType *ct = ctype_raw(cts, id);
cTValue *tv;
if (ctype_isptr(ct->info)) id = ctype_cid(ct->info);
tv = lj_ctype_meta(cts, id, MM_call);
if (tv && tvisfunc(tv)) {
J->base[-1] = lj_ir_kfunc(J, funcV(tv)) | TREF_FRAME;
rd->nres = -1; /* Pending tailcall. */
} else {
/* NYI: non-function metamethods. */
lj_trace_err(J, LJ_TRERR_BADTYPE);
}
}
void LJ_FASTCALL recff_cdata_call(jit_State *J, RecordFFData *rd)
{
CTState *cts = ctype_ctsG(J2G(J));
GCcdata *cd = argv2cdata(J, J->base[0], &rd->argv[0]);
if (cd->typeid == CTID_CTYPEID)
crec_alloc(J, rd, crec_constructor(J, cd, J->base[0]));
else if (!crec_call(J, rd, cd))
crec_call_meta(J, rd, cd->typeid);
CTypeID id = cd->typeid;
CType *ct;
cTValue *tv;
MMS mm = MM_call;
if (id == CTID_CTYPEID) {
id = crec_constructor(J, cd, J->base[0]);
mm = MM_new;
} else if (crec_call(J, rd, cd)) {
return;
}
/* Record ctype __call/__new metamethod. */
ct = ctype_raw(cts, id);
if (ctype_isptr(ct->info)) id = ctype_cid(ct->info);
tv = lj_ctype_meta(cts, id, mm);
if (tv) {
if (tvisfunc(tv)) {
J->base[-1] = lj_ir_kfunc(J, funcV(tv)) | TREF_FRAME;
rd->nres = -1; /* Pending tailcall. */
return;
}
} else if (mm == MM_new) {
crec_alloc(J, rd, id);
return;
}
/* No metamethod or NYI: non-function metamethods. */
lj_trace_err(J, LJ_TRERR_BADTYPE);
}
static TRef crec_arith_int64(jit_State *J, TRef *sp, CType **s, MMS mm)

View File

@ -437,6 +437,12 @@ enum {
#define setvmstate(g, st) ((g)->vmstate = ~LJ_VMST_##st)
/* Metamethods. ORDER MM */
#ifdef LJ_HASFFI
#define MMDEF_FFI(_) _(new)
#else
#define MMDEF_FFI(_)
#endif
#ifdef LUAJIT_ENABLE_LUA52COMPAT
#define MMDEF_52(_) _(pairs) _(ipairs)
#else
@ -450,7 +456,7 @@ enum {
/* The following must be in ORDER ARITH. */ \
_(add) _(sub) _(mul) _(div) _(mod) _(pow) _(unm) \
/* The following are used in the standard libraries. */ \
_(metatable) _(tostring) MMDEF_52(_)
_(metatable) _(tostring) MMDEF_FFI(_) MMDEF_52(_)
typedef enum {
#define MMENUM(name) MM_##name,