diff --git a/doc/ext_ffi_semantics.html b/doc/ext_ffi_semantics.html
index 4b498fbe..d0613aed 100644
--- a/doc/ext_ffi_semantics.html
+++ b/doc/ext_ffi_semantics.html
@@ -599,8 +599,9 @@ C type pointed to by the reference.
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 __new). An error is raised if the metamethod lookup or
+index table lookup fails.
Indexing a cdata object
@@ -669,7 +670,12 @@ to foo.c.
- Constructor: a ctype object can be called and used as a
-constructor.
+constructor. This is equivalent
+to ffi.new(ct, ...), unless a __new metamethod is
+defined. The __new metamethod is called with the ctype object
+plus any other arguments passed to the contructor. Note that you have to
+use ffi.new inside of it, since calling ct(...) would
+cause infinite recursion.
- C function call: a cdata function or cdata function
pointer can be called. The passed arguments are
diff --git a/src/lib_ffi.c b/src/lib_ffi.c
index f4097041..e7d31fe7 100644
--- a/src/lib_ffi.c
+++ b/src/lib_ffi.c
@@ -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)
diff --git a/src/lj_crecord.c b/src/lj_crecord.c
index 3097cbf0..95d09e48 100644
--- a/src/lj_crecord.c
+++ b/src/lj_crecord.c
@@ -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)
diff --git a/src/lj_obj.h b/src/lj_obj.h
index 43ed9204..d7e8ec09 100644
--- a/src/lj_obj.h
+++ b/src/lj_obj.h
@@ -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,