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>
<p> <p>
The pre-defined operations are always tried first before deferring to a The pre-defined operations are always tried first before deferring to a
metamethod or index table (if any) for the corresponding ctype. An error metamethod or index table (if any) for the corresponding ctype (except
is raised if the metamethod lookup or index table lookup fails. for <tt>__new</tt>). An error is raised if the metamethod lookup or
index table lookup fails.
</p> </p>
<h3 id="cdata_array">Indexing a cdata object</h3> <h3 id="cdata_array">Indexing a cdata object</h3>
@ -669,7 +670,12 @@ to <tt>foo.c</tt>.
<ul> <ul>
<li><b>Constructor</b>: a ctype object can be called and used as a <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 <li><b>C&nbsp;function call</b>: a cdata function or cdata function
pointer can be called. The passed arguments are 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); 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. */ /* Forward declaration. */
static int lj_cf_ffi_new(lua_State *L); static int lj_cf_ffi_new(lua_State *L);
LJLIB_CF(ffi_meta___call) LJLIB_REC(cdata_call) LJLIB_CF(ffi_meta___call) LJLIB_REC(cdata_call)
{ {
CTState *cts = ctype_cts(L);
GCcdata *cd = ffi_checkcdata(L, 1); GCcdata *cd = ffi_checkcdata(L, 1);
int ret; CTypeID id = cd->typeid;
if (cd->typeid == CTID_CTYPEID) CType *ct;
return lj_cf_ffi_new(L); cTValue *tv;
if ((ret = lj_ccall_func(L, cd)) < 0) MMS mm = MM_call;
return ffi_call_meta(L, cd->typeid); if (cd->typeid == CTID_CTYPEID) {
return ret; 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) 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; 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) 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]); GCcdata *cd = argv2cdata(J, J->base[0], &rd->argv[0]);
if (cd->typeid == CTID_CTYPEID) CTypeID id = cd->typeid;
crec_alloc(J, rd, crec_constructor(J, cd, J->base[0])); CType *ct;
else if (!crec_call(J, rd, cd)) cTValue *tv;
crec_call_meta(J, rd, cd->typeid); 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) 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) #define setvmstate(g, st) ((g)->vmstate = ~LJ_VMST_##st)
/* Metamethods. ORDER MM */ /* Metamethods. ORDER MM */
#ifdef LJ_HASFFI
#define MMDEF_FFI(_) _(new)
#else
#define MMDEF_FFI(_)
#endif
#ifdef LUAJIT_ENABLE_LUA52COMPAT #ifdef LUAJIT_ENABLE_LUA52COMPAT
#define MMDEF_52(_) _(pairs) _(ipairs) #define MMDEF_52(_) _(pairs) _(ipairs)
#else #else
@ -450,7 +456,7 @@ enum {
/* The following must be in ORDER ARITH. */ \ /* The following must be in ORDER ARITH. */ \
_(add) _(sub) _(mul) _(div) _(mod) _(pow) _(unm) \ _(add) _(sub) _(mul) _(div) _(mod) _(pow) _(unm) \
/* The following are used in the standard libraries. */ \ /* The following are used in the standard libraries. */ \
_(metatable) _(tostring) MMDEF_52(_) _(metatable) _(tostring) MMDEF_FFI(_) MMDEF_52(_)
typedef enum { typedef enum {
#define MMENUM(name) MM_##name, #define MMENUM(name) MM_##name,