Re: C data reflection in LuaJIT

  • From: Peter Colberg <peter@xxxxxxxxxxx>
  • To: luajit@xxxxxxxxxxxxx
  • Date: Fri, 3 Oct 2014 12:53:05 -0400

On Fri, Oct 03, 2014 at 02:06:34PM +0200, Mike Pall wrote:
> Sorry, no. That would mean I'd have to maintain it as part of the
> core and update it for any changes to the internals.

I understand that this was a too big request to make.

Could you take a quick look at the attached patch and test? It adds
three minimal functions ffi.elemtypeof(), ffi.fieldtypeof(), and
ffi.fieldsof().

ffi.fieldsof() returns an iterator to retrieve the field names of a
composite type, which may then be passed to ffi.offsetof() and
ffi.fieldtypeof().

Please let me know if these minimal functions would be acceptable
maintainance-wise; then I will complete the contribution with
documentation.

Thanks,
Peter
diff --git a/src/lib_ffi.c b/src/lib_ffi.c
index 989fddd..7e61dc4 100644
--- a/src/lib_ffi.c
+++ b/src/lib_ffi.c
@@ -559,6 +559,100 @@ LJLIB_CF(ffi_typeof)      LJLIB_REC(.)
   return 1;
 }
 
+LJLIB_CF(ffi_elemtypeof)
+{
+  CTState *cts = ctype_cts(L);
+  CTypeID id = ffi_checkctype(L, cts, L->base+1);
+  CType *ct = ctype_raw(cts, id);
+  if (ctype_isref(ct->info))
+    ct = ctype_rawchild(cts, ct);
+  if (ctype_ispointer(ct->info)) {
+    GCcdata *cd = lj_cdata_new(cts, CTID_CTYPEID, 4);
+    *(CTypeID *)cdataptr(cd) = ctype_cid(ct->info);
+    setcdataV(L, L->top-1, cd);
+    lj_gc_check(L);
+    return 1;
+  }
+  return 0;
+}
+
+LJLIB_CF(ffi_fieldtypeof)
+{
+  CTState *cts = ctype_cts(L);
+  CTypeID id = ffi_checkctype(L, cts, NULL);
+  GCstr *name = lj_lib_checkstr(L, 2);
+  CType *ct = lj_ctype_rawref(cts, id);
+  CTSize ofs;
+  if ((ctype_isstruct(ct->info) || ctype_isenum(ct->info)) &&
+      ct->size != CTSIZE_INVALID) {
+    CType *fct = lj_ctype_getfield(cts, ct, name, &ofs);
+    if (fct) {
+      GCcdata *cd = lj_cdata_new(cts, CTID_CTYPEID, 4);
+      *(CTypeID *)cdataptr(cd) = ctype_cid(fct->info);
+      setcdataV(L, L->top-1, cd);
+      lj_gc_check(L);
+      return 1;
+    }
+  }
+  return 0;
+}
+
+CType *ffi_fieldsof_next(CTState *cts, CType *ct, int *i)
+{
+  while (ct->sib) {
+    ct = ctype_get(cts, ct->sib);
+    if (gcref(ct->name) != NULL) {
+      if (*i == 0) return ct;
+      (*i)--;
+    }
+    if (ctype_isxattrib(ct->info, CTA_SUBTYPE)) {
+      CType *cct = ctype_child(cts, ct);
+      while (ctype_isattrib(cct->info)) {
+        cct = ctype_child(cts, cct);
+      }
+      cct = ffi_fieldsof_next(cts, cct, i);
+      if (cct != NULL) return cct;
+    }
+  }
+  return NULL;
+}
+
+static int ffi_fieldsof_iter(lua_State *L)
+{
+  CTState *cts = ctype_cts(L);
+  GCcdata *cd = cdataV(lj_lib_upvalue(L, 1));
+  CTypeID id = *(CTypeID *)cdataptr(cd);
+  CType *ct = lj_ctype_rawref(cts, id);
+  int i, n = numV(lj_lib_upvalue(L, 2)); /* FIXME why is intV() always 0? */
+  i = n;
+  ct = ffi_fieldsof_next(cts, ct, &i);
+  if (ct != NULL) {
+    setstrV(L, L->top++, strref(ct->name));
+    setintV(lj_lib_upvalue(L, 2), n+1);
+    lj_gc_check(L);
+    return 1;
+  }
+  return 0;
+}
+
+LJLIB_CF(ffi_fieldsof)
+{
+  CTState *cts = ctype_cts(L);
+  CTypeID id = ffi_checkctype(L, cts, NULL);
+  CType *ct = lj_ctype_rawref(cts, id);
+  if ((ctype_isstruct(ct->info) || ctype_isenum(ct->info)) &&
+       ct->size != CTSIZE_INVALID) {
+    GCcdata *cd = lj_cdata_new(cts, CTID_CTYPEID, 4);
+    *(CTypeID *)cdataptr(cd) = id;
+    setcdataV(L, L->top-1, cd);
+    setintV(L->top++, 0);
+    lua_pushcclosure(L, ffi_fieldsof_iter, 2);
+    lj_gc_check(L);
+    return 1;
+  }
+  return 0;
+}
+
 LJLIB_CF(ffi_istype)   LJLIB_REC(.)
 {
   CTState *cts = ctype_cts(L);
local ffi = require("ffi")

assert(ffi.elemtypeof("double") == nil)
assert(ffi.istype("double", ffi.elemtypeof("double[5]")))
assert(ffi.istype("double", ffi.elemtypeof("double[?]")))
assert(ffi.istype("float", ffi.elemtypeof("float (&)[3]")))
assert(ffi.istype("double[3]", ffi.elemtypeof("double[5][3]")))
assert(ffi.istype("double[3][2]", ffi.elemtypeof("double[5][3][2]")))
assert(ffi.istype("double", ffi.elemtypeof("double *")))

ffi.cdef[[struct foo { double x, y, z; }]]

local s = ffi.typeof("struct { double x; int y; float z; }")
assert(ffi.istype("double", ffi.fieldtypeof(s, "x")))
assert(ffi.istype("int", ffi.fieldtypeof(s, "y")))
assert(ffi.istype("float", ffi.fieldtypeof(s, "z")))

local iter = ffi.fieldsof(s)
assert(iter() == "x")
assert(iter() == "y")
assert(iter() == "z")
assert(iter() == nil)

local iter = ffi.fieldsof("struct { struct { struct { int a; }; int b; }; 
struct { int c; } d;}")
assert(iter() == "a")
assert(iter() == "b")
assert(iter() == "d")
assert(iter() == nil)

local e = ffi.typeof("enum { A, B }")
assert(ffi.istype("int", ffi.fieldtypeof(e, "A")))
assert(ffi.istype("int", ffi.fieldtypeof(e, "B")))

local iter = ffi.fieldsof(e)
assert(iter() == "A")
assert(iter() == "B")
assert(iter() == nil)

local u = ffi.typeof("union { int X; float Y; }")
assert(ffi.istype("int", ffi.fieldtypeof(u, "X")))
assert(ffi.istype("float", ffi.fieldtypeof(u, "Y")))

local iter = ffi.fieldsof(u)
assert(iter() == "X")
assert(iter() == "Y")
assert(iter() == nil)

Other related posts: