Browse Source

FFI: Check for __new metamethod when calling a constructor.

Mike Pall 13 năm trước cách đây
mục cha
commit
8b71ab1080
4 tập tin đã thay đổi với 63 bổ sung42 xóa
  1. 9 3
      doc/ext_ffi_semantics.html
  2. 22 19
      src/lib_ffi.c
  3. 25 19
      src/lj_crecord.c
  4. 7 1
      src/lj_obj.h

+ 9 - 3
doc/ext_ffi_semantics.html

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

+ 22 - 19
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)

+ 25 - 19
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)
+void LJ_FASTCALL recff_cdata_call(jit_State *J, RecordFFData *rd)
 {
   CTState *cts = ctype_ctsG(J2G(J));
-  CType *ct = ctype_raw(cts, id);
+  GCcdata *cd = argv2cdata(J, J->base[0], &rd->argv[0]);
+  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_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);
+  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;
   }
-}
-
-void LJ_FASTCALL recff_cdata_call(jit_State *J, RecordFFData *rd)
-{
-  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);
+  /* 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)

+ 7 - 1
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,