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

@@ -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,