FFI: Add ctype metamethods and ffi.metatype().

This commit is contained in:
Mike Pall 2011-04-12 19:15:00 +02:00
parent fa5cd010e8
commit 3b6f37dd2c
11 changed files with 426 additions and 83 deletions

View File

@ -238,6 +238,31 @@ This functions is mainly useful to override the pointer compatibility
checks or to convert pointers to addresses or vice versa. checks or to convert pointers to addresses or vice versa.
</p> </p>
<h3 id="ffi_metatype"><tt>ctype = ffi.metatype(ct, metatable)</tt></h3>
<p>
Creates a ctype object for the given <tt>ct</tt> and associates it with
a metatable. Only <tt>struct</tt>/<tt>union</tt> types, complex numbers
and vectors are allowed. Other types may be wrapped in a
<tt>struct</tt>, if needed.
</p>
<p>
The association with a metatable is permanent and cannot be changed
afterwards. Neither the contents of the <tt>metatable</tt> nor the
contents of an <tt>__index</tt> table (if any) may be modified
afterwards. The associated metatable automatically applies to all uses
of this type, no matter how the objects are created or where they
originate from. Note that pre-defined operations on types have
precedence (e.g. declared field names cannot be overriden).
</p>
<p>
All standard Lua metamethods are implemented. These are called directly,
without shortcuts and on any mix of types. For binary operations, the
left operand is checked first for a valid ctype metamethod. The
<tt>__gc</tt> metamethod only applies to <tt>struct</tt>/<tt>union</tt>
types and performs an implicit <a href="#ffi_gc"><tt>ffi.gc()</tt></a>
call during creation of an instance.
</p>
<h3 id="ffi_gc"><tt>cdata = ffi.gc(cdata, finalizer)</tt></h3> <h3 id="ffi_gc"><tt>cdata = ffi.gc(cdata, finalizer)</tt></h3>
<p> <p>
Associates a finalizer with a pointer or aggregate cdata object. The Associates a finalizer with a pointer or aggregate cdata object. The

View File

@ -582,6 +582,10 @@ Reference types are dereferenced <em>before</em> performing each of
the operations below &mdash; the operation is applied to the the operations below &mdash; the operation is applied to the
C&nbsp;type pointed to by the reference. C&nbsp;type pointed to by the reference.
</p> </p>
<p>
The pre-defined operations are always tried first before deferring to a
metamethod for a ctype (if defined).
</p>
<h3 id="cdata_array">Indexing a cdata object</h3> <h3 id="cdata_array">Indexing a cdata object</h3>
<ul> <ul>
@ -803,9 +807,10 @@ vararg functions</a>.
</p> </p>
<p> <p>
Memory areas returned by C functions (e.g. from <tt>malloc()</tt>) Memory areas returned by C functions (e.g. from <tt>malloc()</tt>)
must be manually managed, of course. Pointers to cdata objects are must be manually managed, of course (or use
indistinguishable from pointers returned by C functions (which is one <a href="ext_ffi_api.html#ffi_gc"><tt>ffi.gc()</tt></a>)). Pointers to
of the reasons why the GC cannot follow them). cdata objects are indistinguishable from pointers returned by C
functions (which is one of the reasons why the GC cannot follow them).
</p> </p>
<h2 id="clib">C Library Namespaces</h2> <h2 id="clib">C Library Namespaces</h2>
@ -977,6 +982,9 @@ two.</li>
value.</li> value.</li>
<li>Calls to C&nbsp;functions with 64 bit arguments or return values <li>Calls to C&nbsp;functions with 64 bit arguments or return values
on 32 bit CPUs.</li> on 32 bit CPUs.</li>
<li>Calls to ctype metamethods which are not plain functions.</li>
<li>ctype <tt>__newindex</tt> tables and non-string lookups in ctype
<tt>__index</tt> tables.</li>
<li>Accesses to external variables in C&nbsp;library namespaces.</li> <li>Accesses to external variables in C&nbsp;library namespaces.</li>
<li><tt>tostring()</tt> for cdata types.</li> <li><tt>tostring()</tt> for cdata types.</li>
<li>The following <a href="ext_ffi_api.html">ffi.* API</a> functions: <li>The following <a href="ext_ffi_api.html">ffi.* API</a> functions:
@ -988,7 +996,6 @@ Other missing features:
<ul> <ul>
<li>Bit operations for 64&nbsp;bit types.</li> <li>Bit operations for 64&nbsp;bit types.</li>
<li>Arithmetic for <tt>complex</tt> numbers.</li> <li>Arithmetic for <tt>complex</tt> numbers.</li>
<li>User-defined metamethods for C&nbsp;types.</li>
<li>Callbacks from C&nbsp;code to Lua functions.</li> <li>Callbacks from C&nbsp;code to Lua functions.</li>
<li>Atomic handling of <tt>errno</tt>.</li> <li>Atomic handling of <tt>errno</tt>.</li>
<li>Passing structs by value to vararg C&nbsp;functions.</li> <li>Passing structs by value to vararg C&nbsp;functions.</li>

View File

@ -386,6 +386,99 @@ application might work on some systems, but would fail in a POSIX/x64
environment. environment.
</p> </p>
<h2 id="metatype">Defining Metamethods for a C&nbsp;Type</h2>
<p>
The following code explains how to define metamethods for a C type.
We define a simple point type and add some operations to it:
</p>
<pre class="code mark">
<span class="codemark">&nbsp;
&#9312;
&#9313;
&#9314;
&#9315;
&#9316;
&#9317;</span>local ffi = require("ffi")
ffi.cdef[[
<span style="color:#00a000;">typedef struct { double x, y; } point_t;</span>
]]
local point
local mt = {
__add = function(a, b) return point(a.x+b.x, a.y+b.y) end,
__len = function(a) return math.sqrt(a.x*a.x + a.y*a.y) end,
__index = {
area = function(a) return a.x*a.x + a.y*a.y end,
},
}
point = ffi.metatype("point_t", mt)
local a = point(3, 4)
print(a.x, a.y) --> 3 4
print(#a) --> 5
print(a:area()) --> 25
local b = a + point(0.5, 8)
print(#b) --> 12.5
</pre>
<p>
Here's the step-by-step explanation:
</p>
<p>
<span class="mark">&#9312;</span> This defines the C&nbsp;type for a
two-dimensional point object.
</p>
<p>
<span class="mark">&#9313;</span> We have to declare the variable
holding the point constructor first, because it's used inside of a
metamethod.
</p>
<p>
<span class="mark">&#9314;</span> Let's define an <tt>__add</tt>
metamethod which adds the coordinates of two points and creates a new
point object. For simplicity, this function assumes that both arguments
are points. But it could be any mix of objects, if at least one operand
is of the required type (e.g. adding a point plus a number or vice
versa). Our <tt>__len</tt> metamethod returns the distance of a point to
the origin.
</p>
<p>
<span class="mark">&#9315;</span> If we run out of operators, we can
define named methods, too. Here the <tt>__index</tt> table defines an
<tt>area</tt> function. For custom indexing needs, one might want to
define <tt>__index</tt> and <tt>__newindex</tt> functions instead.
</p>
<p>
<span class="mark">&#9316;</span> This associates the metamethods with
our C&nbsp;type. This only needs to be done once. For convenience, a
constructor is returned by
<a href="ffi_ext_api.html#ffi_metatype"><tt>ffi.metatype()</tt></a>.
We're not required to use it, though. The original C&nbsp;type can still
be used e.g. to create an array of points. The metamethods automatically
apply to any and all uses of this type.
</p>
<p>
Please note that the association with a metatable is permanent and
<b>the metatable must not be modified afterwards!</b> Ditto for the
<tt>__index</tt> table.
</p>
<p>
<span class="mark">&#9317;</span> Here are some simple usage examples
for the point type and their expected results. The pre-defined
operations (such as <tt>a.x</tt>) can be freely mixed with the newly
defined metamethods. Note that <tt>area</tt> is a method and must be
called with the Lua syntax for methods: <tt>a:area()</tt>, not
<tt>a.area()</tt>.
</p>
<h2 id="idioms">Translating C&nbsp;Idioms</h2> <h2 id="idioms">Translating C&nbsp;Idioms</h2>
<p> <p>
Here's a list of common C&nbsp;idioms and their translation to the Here's a list of common C&nbsp;idioms and their translation to the

View File

@ -21,9 +21,9 @@ lib_bit.o: lib_bit.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
lib_debug.o: lib_debug.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \ lib_debug.o: lib_debug.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h \
lj_def.h lj_arch.h lj_err.h lj_errmsg.h lj_lib.h lj_libdef.h lj_def.h lj_arch.h lj_err.h lj_errmsg.h lj_lib.h lj_libdef.h
lib_ffi.o: lib_ffi.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ lib_ffi.o: lib_ffi.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_ctype.h \ lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_meta.h \
lj_cparse.h lj_cdata.h lj_cconv.h lj_carith.h lj_ccall.h lj_clib.h \ lj_ctype.h lj_cparse.h lj_cdata.h lj_cconv.h lj_carith.h lj_ccall.h \
lj_ff.h lj_ffdef.h lj_lib.h lj_libdef.h lj_clib.h lj_ff.h lj_ffdef.h lj_lib.h lj_libdef.h
lib_init.o: lib_init.c lua.h luaconf.h lauxlib.h lualib.h lj_arch.h lib_init.o: lib_init.c lua.h luaconf.h lauxlib.h lualib.h lj_arch.h
lib_io.o: lib_io.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \ lib_io.o: lib_io.c lua.h luaconf.h lauxlib.h lualib.h lj_obj.h lj_def.h \
lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_ff.h lj_ffdef.h \ lj_arch.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_ff.h lj_ffdef.h \
@ -57,8 +57,8 @@ lj_asm.o: lj_asm.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \
lj_bc.o: lj_bc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_bc.h \ lj_bc.o: lj_bc.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_bc.h \
lj_bcdef.h lj_bcdef.h
lj_carith.o: lj_carith.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ lj_carith.o: lj_carith.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_gc.h lj_err.h lj_errmsg.h lj_ctype.h lj_cconv.h lj_cdata.h \ lj_gc.h lj_err.h lj_errmsg.h lj_tab.h lj_meta.h lj_ctype.h lj_cconv.h \
lj_carith.h lj_cdata.h lj_carith.h
lj_ccall.o: lj_ccall.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ lj_ccall.o: lj_ccall.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_ctype.h lj_cconv.h lj_cdata.h \ lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_ctype.h lj_cconv.h lj_cdata.h \
lj_ccall.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_bc.h \ lj_ccall.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h lj_bc.h \
@ -77,7 +77,8 @@ lj_cparse.o: lj_cparse.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_crecord.o: lj_crecord.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ lj_crecord.o: lj_crecord.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ctype.h \ lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_frame.h lj_bc.h lj_ctype.h \
lj_gc.h lj_cparse.h lj_cconv.h lj_clib.h lj_ir.h lj_jit.h lj_iropt.h \ lj_gc.h lj_cparse.h lj_cconv.h lj_clib.h lj_ir.h lj_jit.h lj_iropt.h \
lj_trace.h lj_dispatch.h lj_traceerr.h lj_ffrecord.h lj_crecord.h lj_trace.h lj_dispatch.h lj_traceerr.h lj_record.h lj_ffrecord.h \
lj_crecord.h
lj_ctype.o: lj_ctype.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ lj_ctype.o: lj_ctype.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \
lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_ctype.h lj_gc.h lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_ctype.h
lj_dispatch.o: lj_dispatch.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ lj_dispatch.o: lj_dispatch.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \

View File

@ -18,6 +18,7 @@
#include "lj_err.h" #include "lj_err.h"
#include "lj_str.h" #include "lj_str.h"
#include "lj_tab.h" #include "lj_tab.h"
#include "lj_meta.h"
#include "lj_ctype.h" #include "lj_ctype.h"
#include "lj_cparse.h" #include "lj_cparse.h"
#include "lj_cdata.h" #include "lj_cdata.h"
@ -96,6 +97,41 @@ static int32_t ffi_checkint(lua_State *L, int narg)
#define LJLIB_MODULE_ffi_meta #define LJLIB_MODULE_ffi_meta
/* Handle ctype __index/__newindex metamethods. */
static int ffi_index_meta(lua_State *L, CTState *cts, CType *ct, MMS mm)
{
CTypeID id = ctype_typeid(cts, ct);
cTValue *tv = lj_ctype_meta(cts, id, mm);
TValue *base = L->base;
if (!tv) {
const char *s;
err_index:
s = strdata(lj_ctype_repr(L, id, NULL));
if (tvisstr(L->base+1))
lj_err_callerv(L, LJ_ERR_FFI_BADMEMBER, s, strVdata(L->base+1));
else
lj_err_callerv(L, LJ_ERR_FFI_BADIDX, s);
}
if (!tvisfunc(tv)) {
if (mm == MM_index) {
cTValue *o = lj_meta_tget(L, tv, base+1);
if (o) {
if (tvisnil(o)) goto err_index;
copyTV(L, L->top-1, o);
return 1;
}
} else {
TValue *o = lj_meta_tset(L, tv, base+1);
if (o) {
copyTV(L, o, base+2);
return 0;
}
}
tv = L->top-1;
}
return lj_meta_tailcall(L, tv);
}
LJLIB_CF(ffi_meta___index) LJLIB_REC(cdata_index 0) LJLIB_CF(ffi_meta___index) LJLIB_REC(cdata_index 0)
{ {
CTState *cts = ctype_cts(L); CTState *cts = ctype_cts(L);
@ -106,6 +142,8 @@ LJLIB_CF(ffi_meta___index) LJLIB_REC(cdata_index 0)
if (!(o+1 < L->top && tviscdata(o))) /* Also checks for presence of key. */ if (!(o+1 < L->top && tviscdata(o))) /* Also checks for presence of key. */
lj_err_argt(L, 1, LUA_TCDATA); lj_err_argt(L, 1, LUA_TCDATA);
ct = lj_cdata_index(cts, cdataV(o), o+1, &p, &qual); ct = lj_cdata_index(cts, cdataV(o), o+1, &p, &qual);
if ((qual & 1))
return ffi_index_meta(L, cts, ct, MM_index);
if (lj_cdata_get(cts, ct, L->top-1, p)) if (lj_cdata_get(cts, ct, L->top-1, p))
lj_gc_check(L); lj_gc_check(L);
return 1; return 1;
@ -121,6 +159,11 @@ LJLIB_CF(ffi_meta___newindex) LJLIB_REC(cdata_index 1)
if (!(o+2 < L->top && tviscdata(o))) /* Also checks for key and value. */ if (!(o+2 < L->top && tviscdata(o))) /* Also checks for key and value. */
lj_err_argt(L, 1, LUA_TCDATA); lj_err_argt(L, 1, LUA_TCDATA);
ct = lj_cdata_index(cts, cdataV(o), o+1, &p, &qual); ct = lj_cdata_index(cts, cdataV(o), o+1, &p, &qual);
if ((qual & 1)) {
if ((qual & CTF_CONST))
lj_err_caller(L, LJ_ERR_FFI_WRCONST);
return ffi_index_meta(L, cts, ct, MM_newindex);
}
lj_cdata_set(cts, ct, p, o+2, qual); lj_cdata_set(cts, ct, p, o+2, qual);
return 0; return 0;
} }
@ -138,7 +181,7 @@ LJLIB_CF(ffi_meta___eq) LJLIB_REC(cdata_arith MM_eq)
return ffi_arith(L); return ffi_arith(L);
} }
LJLIB_CF(ffi_meta___len) LJLIB_CF(ffi_meta___len) LJLIB_REC(cdata_arith MM_len)
{ {
return ffi_arith(L); return ffi_arith(L);
} }
@ -153,11 +196,21 @@ LJLIB_CF(ffi_meta___le) LJLIB_REC(cdata_arith MM_le)
return ffi_arith(L); return ffi_arith(L);
} }
LJLIB_CF(ffi_meta___concat) 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);
cTValue *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);
@ -168,8 +221,7 @@ LJLIB_CF(ffi_meta___call) LJLIB_REC(cdata_call)
if (cd->typeid == CTID_CTYPEID) if (cd->typeid == CTID_CTYPEID)
return lj_cf_ffi_new(L); return lj_cf_ffi_new(L);
if ((ret = lj_ccall_func(L, cd)) < 0) if ((ret = lj_ccall_func(L, cd)) < 0)
lj_err_callerv(L, LJ_ERR_FFI_BADCALL, return ffi_call_meta(L, cd->typeid);
strdata(lj_ctype_repr(L, cd->typeid, NULL)));
return ret; return ret;
} }
@ -226,6 +278,12 @@ LJLIB_CF(ffi_meta___tostring)
setstrV(L, L->top-1, lj_ctype_repr_int64(L, *(uint64_t *)cdataptr(cd), setstrV(L, L->top-1, lj_ctype_repr_int64(L, *(uint64_t *)cdataptr(cd),
(ct->info & CTF_UNSIGNED))); (ct->info & CTF_UNSIGNED)));
goto checkgc; goto checkgc;
} else if (ctype_isstruct(ct->info) || ctype_isvector(ct->info)) {
/* Handle ctype __tostring metamethod. */
CTState *cts = ctype_cts(L);
cTValue *tv = lj_ctype_meta(cts, id, MM_tostring);
if (tv)
return lj_meta_tailcall(L, tv);
} }
} }
lj_str_pushf(L, msg, strdata(lj_ctype_repr(L, id, NULL)), cdataptr(cd)); lj_str_pushf(L, msg, strdata(lj_ctype_repr(L, id, NULL)), cdataptr(cd));
@ -234,6 +292,8 @@ checkgc:
return 1; return 1;
} }
LJLIB_PUSH("ffi") LJLIB_SET(__metatable)
#include "lj_libdef.h" #include "lj_libdef.h"
/* -- C library metamethods ----------------------------------------------- */ /* -- C library metamethods ----------------------------------------------- */
@ -331,14 +391,14 @@ LJLIB_CF(ffi_new) LJLIB_REC(.)
{ {
CTState *cts = ctype_cts(L); CTState *cts = ctype_cts(L);
CTypeID id = ffi_checkctype(L, cts); CTypeID id = ffi_checkctype(L, cts);
CType *ct = ctype_raw(cts, id);
CTSize sz; CTSize sz;
CTInfo info = lj_ctype_info(cts, id, &sz); CTInfo info = lj_ctype_info(cts, id, &sz);
TValue *o = L->base+1; TValue *o = L->base+1;
GCcdata *cd; GCcdata *cd;
if ((info & CTF_VLA)) { if ((info & CTF_VLA)) {
o++; o++;
sz = lj_ctype_vlsize(cts, ctype_raw(cts, id), sz = lj_ctype_vlsize(cts, ct, (CTSize)ffi_checkint(L, 2));
(CTSize)ffi_checkint(L, 2));
} }
if (sz == CTSIZE_INVALID) if (sz == CTSIZE_INVALID)
lj_err_arg(L, 1, LJ_ERR_FFI_INVSIZE); lj_err_arg(L, 1, LJ_ERR_FFI_INVSIZE);
@ -347,8 +407,21 @@ LJLIB_CF(ffi_new) LJLIB_REC(.)
else else
cd = lj_cdata_newv(cts, id, sz, ctype_align(info)); cd = lj_cdata_newv(cts, id, sz, ctype_align(info));
setcdataV(L, o-1, cd); /* Anchor the uninitialized cdata. */ setcdataV(L, o-1, cd); /* Anchor the uninitialized cdata. */
lj_cconv_ct_init(cts, ctype_raw(cts, id), sz, cdataptr(cd), lj_cconv_ct_init(cts, ct, sz, cdataptr(cd),
o, (MSize)(L->top - o)); /* Initialize cdata. */ o, (MSize)(L->top - o)); /* Initialize cdata. */
if (ctype_isstruct(ct->info)) {
/* Handle ctype __gc metamethod. Use the fast lookup here. */
cTValue *tv = lj_tab_getint(cts->metatype, (int32_t)id);
if (tv && tvistab(tv) && (tv = lj_meta_fast(L, tabV(tv), MM_gc))) {
GCtab *t = cts->finalizer;
if (gcref(t->metatable)) {
/* Add to finalizer table, if still enabled. */
copyTV(L, lj_tab_set(L, t, o-1), tv);
lj_gc_anybarriert(L, t);
cd->marked |= LJ_GC_CDATA_FIN;
}
}
}
L->top = o; /* Only return the cdata itself. */ L->top = o; /* Only return the cdata itself. */
lj_gc_check(L); lj_gc_check(L);
return 1; return 1;
@ -521,7 +594,33 @@ LJLIB_CF(ffi_abi) LJLIB_REC(.)
#undef H_ #undef H_
LJLIB_PUSH(top-7) LJLIB_SET(!) /* Store reference to weak table. */ LJLIB_PUSH(top-8) LJLIB_SET(!) /* Store reference to metatype table. */
LJLIB_CF(ffi_metatype)
{
CTState *cts = ctype_cts(L);
CTypeID id = ffi_checkctype(L, cts);
GCtab *mt = lj_lib_checktab(L, 2);
GCtab *t = cts->metatype;
CType *ct = ctype_get(cts, id); /* Only allow raw types. */
TValue *tv;
GCcdata *cd;
if (!(ctype_isstruct(ct->info) || ctype_iscomplex(ct->info) ||
ctype_isvector(ct->info)))
lj_err_arg(L, 1, LJ_ERR_FFI_INVTYPE);
tv = lj_tab_setint(L, t, (int32_t)id);
if (!tvisnil(tv))
lj_err_caller(L, LJ_ERR_PROTMT);
settabV(L, tv, mt);
lj_gc_anybarriert(L, t);
cd = lj_cdata_new(cts, CTID_CTYPEID, 4);
*(CTypeID *)cdataptr(cd) = id;
setcdataV(L, L->top-1, cd);
lj_gc_check(L);
return 1;
}
LJLIB_PUSH(top-7) LJLIB_SET(!) /* Store reference to finalizer table. */
LJLIB_CF(ffi_gc) LJLIB_CF(ffi_gc)
{ {
@ -590,6 +689,7 @@ static void ffi_register_module(lua_State *L)
LUALIB_API int luaopen_ffi(lua_State *L) LUALIB_API int luaopen_ffi(lua_State *L)
{ {
CTState *cts = lj_ctype_init(L); CTState *cts = lj_ctype_init(L);
settabV(L, L->top++, (cts->metatype = lj_tab_new(L, 0, 0)));
cts->finalizer = ffi_finalizer(L); cts->finalizer = ffi_finalizer(L);
LJ_LIB_REG(L, NULL, ffi_meta); LJ_LIB_REG(L, NULL, ffi_meta);
/* NOBARRIER: basemt is a GC root. */ /* NOBARRIER: basemt is a GC root. */

View File

@ -2558,6 +2558,7 @@ static void asm_cnew(ASMState *as, IRIns *ir)
lj_ctype_size(cts, typeid) : (CTSize)IR(ir->op2)->i; lj_ctype_size(cts, typeid) : (CTSize)IR(ir->op2)->i;
const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_mem_newgco]; const CCallInfo *ci = &lj_ir_callinfo[IRCALL_lj_mem_newgco];
IRRef args[2]; IRRef args[2];
int gcfin = 0;
lua_assert(sz != CTSIZE_INVALID); lua_assert(sz != CTSIZE_INVALID);
args[0] = ASMREF_L; /* lua_State *L */ args[0] = ASMREF_L; /* lua_State *L */
@ -2604,12 +2605,15 @@ static void asm_cnew(ASMState *as, IRIns *ir)
} while (1); } while (1);
#endif #endif
lua_assert(sz == 4 || (sz == 8 && (LJ_64 || LJ_HASFFI))); lua_assert(sz == 4 || (sz == 8 && (LJ_64 || LJ_HASFFI)));
} else {
if (lj_ctype_meta(cts, typeid, MM_gc) != NULL)
gcfin = LJ_GC_CDATA_FIN;
} }
/* Combine initialization of marked, gct and typeid. */ /* Combine initialization of marked, gct and typeid. */
emit_movtomro(as, RID_ECX, RID_RET, offsetof(GCcdata, marked)); emit_movtomro(as, RID_ECX, RID_RET, offsetof(GCcdata, marked));
emit_gri(as, XG_ARITHi(XOg_OR), RID_ECX, emit_gri(as, XG_ARITHi(XOg_OR), RID_ECX,
(int32_t)((~LJ_TCDATA<<8)+(typeid<<16))); (int32_t)((~LJ_TCDATA<<8)+(typeid<<16)+gcfin));
emit_gri(as, XG_ARITHi(XOg_AND), RID_ECX, LJ_GC_WHITES); emit_gri(as, XG_ARITHi(XOg_AND), RID_ECX, LJ_GC_WHITES);
emit_opgl(as, XO_MOVZXb, RID_ECX, gc.currentwhite); emit_opgl(as, XO_MOVZXb, RID_ECX, gc.currentwhite);

View File

@ -9,6 +9,8 @@
#include "lj_gc.h" #include "lj_gc.h"
#include "lj_err.h" #include "lj_err.h"
#include "lj_tab.h"
#include "lj_meta.h"
#include "lj_ctype.h" #include "lj_ctype.h"
#include "lj_cconv.h" #include "lj_cconv.h"
#include "lj_cdata.h" #include "lj_cdata.h"
@ -187,6 +189,31 @@ static int carith_int64(lua_State *L, CTState *cts, CDArith *ca, MMS mm)
return 0; return 0;
} }
/* Handle ctype arithmetic metamethods. */
static int lj_carith_meta(lua_State *L, CTState *cts, CDArith *ca, MMS mm)
{
cTValue *tv = NULL;
if (tviscdata(L->base))
tv = lj_ctype_meta(cts, cdataV(L->base)->typeid, mm);
if (!tv && L->base+1 < L->top && tviscdata(L->base+1))
tv = lj_ctype_meta(cts, cdataV(L->base+1)->typeid, mm);
if (!tv) {
const char *repr[2];
int i;
for (i = 0; i < 2; i++) {
if (ca->ct[i])
repr[i] = strdata(lj_ctype_repr(L, ctype_typeid(cts, ca->ct[i]), NULL));
else
repr[i] = typename(&L->base[i]);
}
lj_err_callerv(L, mm == MM_len ? LJ_ERR_FFI_BADLEN :
mm == MM_concat ? LJ_ERR_FFI_BADCONCAT :
mm < MM_add ? LJ_ERR_FFI_BADCOMP : LJ_ERR_FFI_BADARITH,
repr[0], repr[1]);
}
return lj_meta_tailcall(L, tv);
}
/* Arithmetic operators for cdata. */ /* Arithmetic operators for cdata. */
int lj_carith_op(lua_State *L, MMS mm) int lj_carith_op(lua_State *L, MMS mm)
{ {
@ -198,22 +225,7 @@ int lj_carith_op(lua_State *L, MMS mm)
return 1; return 1;
} }
} }
/* NYI: per-cdata metamethods. */ return lj_carith_meta(L, cts, &ca, mm);
{
const char *repr[2];
int i;
for (i = 0; i < 2; i++) {
if (ca.ct[i])
repr[i] = strdata(lj_ctype_repr(L, ctype_typeid(cts, ca.ct[i]), NULL));
else
repr[i] = typename(&L->base[i]);
}
lj_err_callerv(L, mm == MM_len ? LJ_ERR_FFI_BADLEN :
mm == MM_concat ? LJ_ERR_FFI_BADCONCAT :
mm < MM_add ? LJ_ERR_FFI_BADCOMP : LJ_ERR_FFI_BADARITH,
repr[0], repr[1]);
}
return 0; /* unreachable */
} }
/* -- 64 bit integer arithmetic helpers ----------------------------------- */ /* -- 64 bit integer arithmetic helpers ----------------------------------- */

View File

@ -129,13 +129,7 @@ collect_attrib:
} }
} else if (tvisstr(key)) { /* String key. */ } else if (tvisstr(key)) { /* String key. */
GCstr *name = strV(key); GCstr *name = strV(key);
if (ctype_isptr(ct->info)) { /* Automatically perform '->'. */ if (ctype_isstruct(ct->info)) {
if (ctype_isstruct(ctype_rawchild(cts, ct)->info)) {
p = (uint8_t *)cdata_getptr(p, ct->size);
ct = ctype_child(cts, ct);
goto collect_attrib;
}
} if (ctype_isstruct(ct->info)) {
CTSize ofs; CTSize ofs;
CType *fct = lj_ctype_getfield(cts, ct, name, &ofs); CType *fct = lj_ctype_getfield(cts, ct, name, &ofs);
if (fct) { if (fct) {
@ -155,7 +149,7 @@ collect_attrib:
} }
} else if (cd->typeid == CTID_CTYPEID) { } else if (cd->typeid == CTID_CTYPEID) {
/* Allow indexing a (pointer to) struct constructor to get constants. */ /* Allow indexing a (pointer to) struct constructor to get constants. */
CType *sct = ct = ctype_raw(cts, *(CTypeID *)p); CType *sct = ctype_raw(cts, *(CTypeID *)p);
if (ctype_isptr(sct->info)) if (ctype_isptr(sct->info))
sct = ctype_rawchild(cts, sct); sct = ctype_rawchild(cts, sct);
if (ctype_isstruct(sct->info)) { if (ctype_isstruct(sct->info)) {
@ -165,16 +159,16 @@ collect_attrib:
return fct; return fct;
} }
} }
{ }
GCstr *s = lj_ctype_repr(cts->L, ctype_typeid(cts, ct), NULL); if (ctype_isptr(ct->info)) { /* Automatically perform '->'. */
lj_err_callerv(cts->L, LJ_ERR_FFI_BADMEMBER, strdata(s), strdata(name)); if (ctype_isstruct(ctype_rawchild(cts, ct)->info)) {
p = (uint8_t *)cdata_getptr(p, ct->size);
ct = ctype_child(cts, ct);
goto collect_attrib;
} }
} }
{ *qual |= 1; /* Lookup failed. */
GCstr *s = lj_ctype_repr(cts->L, ctype_typeid(cts, ct), NULL); return ct; /* But return the resolved raw type. */
lj_err_callerv(cts->L, LJ_ERR_FFI_BADIDX, strdata(s));
}
return NULL; /* unreachable */
} }
/* -- C data getters ------------------------------------------------------ */ /* -- C data getters ------------------------------------------------------ */

View File

@ -22,6 +22,7 @@
#include "lj_jit.h" #include "lj_jit.h"
#include "lj_iropt.h" #include "lj_iropt.h"
#include "lj_trace.h" #include "lj_trace.h"
#include "lj_record.h"
#include "lj_ffrecord.h" #include "lj_ffrecord.h"
#include "lj_crecord.h" #include "lj_crecord.h"
#include "lj_dispatch.h" #include "lj_dispatch.h"
@ -459,6 +460,41 @@ static TRef crec_reassoc_ofs(jit_State *J, TRef tr, ptrdiff_t *ofsp, MSize sz)
return tr; return tr;
} }
/* Record ctype __index/__newindex metamethods. */
static void crec_index_meta(jit_State *J, CTState *cts, CType *ct,
RecordFFData *rd)
{
CTypeID id = ctype_typeid(cts, ct);
cTValue *tv = lj_ctype_meta(cts, id, rd->data ? MM_newindex : MM_index);
if (!tv)
lj_trace_err(J, LJ_TRERR_BADTYPE);
if (tvisfunc(tv)) {
J->base[-1] = lj_ir_kfunc(J, funcV(tv)) | TREF_FRAME;
rd->nres = -1; /* Pending tailcall. */
} else if (rd->data == 0 && tvistab(tv) && tref_isstr(J->base[1])) {
/* Specialize to result of __index lookup. */
cTValue *o = lj_tab_get(J->L, tabV(tv), &rd->argv[1]);
IRType t = itype2irt(o);
if (tvisgcv(o))
J->base[0] = lj_ir_kgc(J, gcV(o), t);
else if (tvisint(o))
J->base[0] = lj_ir_kint(J, intV(o));
else if (tvisnum(o))
J->base[0] = lj_ir_knumint(J, numV(o));
else if (tvisbool(o))
J->base[0] = TREF_PRI(t);
else
lj_trace_err(J, LJ_TRERR_BADTYPE);
/* Always specialize to the key. */
emitir(IRTG(IR_EQ, IRT_STR), J->base[1], lj_ir_kstr(J, strV(&rd->argv[1])));
} else {
/* NYI: resolving of non-function metamethods. */
/* NYI: non-string keys for __index table. */
/* NYI: stores to __newindex table. */
lj_trace_err(J, LJ_TRERR_BADTYPE);
}
}
void LJ_FASTCALL recff_cdata_index(jit_State *J, RecordFFData *rd) void LJ_FASTCALL recff_cdata_index(jit_State *J, RecordFFData *rd)
{ {
TRef idx, ptr = J->base[0]; TRef idx, ptr = J->base[0];
@ -477,12 +513,13 @@ void LJ_FASTCALL recff_cdata_index(jit_State *J, RecordFFData *rd)
ptr = crec_reassoc_ofs(J, ptr, &ofs, 1); ptr = crec_reassoc_ofs(J, ptr, &ofs, 1);
} }
again:
idx = J->base[1]; idx = J->base[1];
if (tref_isnumber(idx)) { if (tref_isnumber(idx)) {
idx = lj_opt_narrow_cindex(J, idx); idx = lj_opt_narrow_cindex(J, idx);
integer_key:
if (ctype_ispointer(ct->info)) { if (ctype_ispointer(ct->info)) {
CTSize sz; CTSize sz;
integer_key:
if ((ct->info & CTF_COMPLEX)) if ((ct->info & CTF_COMPLEX))
idx = emitir(IRT(IR_BAND, IRT_INTP), idx, lj_ir_kintp(J, 1)); idx = emitir(IRT(IR_BAND, IRT_INTP), idx, lj_ir_kintp(J, 1));
sz = lj_ctype_size(cts, (sid = ctype_cid(ct->info))); sz = lj_ctype_size(cts, (sid = ctype_cid(ct->info)));
@ -495,7 +532,8 @@ void LJ_FASTCALL recff_cdata_index(jit_State *J, RecordFFData *rd)
CType *ctk = ctype_raw(cts, cdk->typeid); CType *ctk = ctype_raw(cts, cdk->typeid);
IRType t; IRType t;
if (ctype_isenum(ctk->info)) ctk = ctype_child(cts, ctk); if (ctype_isenum(ctk->info)) ctk = ctype_child(cts, ctk);
if (ctype_isinteger(ctk->info) && (t = crec_ct2irt(ctk)) != IRT_CDATA) { if (ctype_ispointer(ct->info) &&
ctype_isinteger(ctk->info) && (t = crec_ct2irt(ctk)) != IRT_CDATA) {
if (ctk->size == 8) { if (ctk->size == 8) {
idx = emitir(IRT(IR_FLOAD, t), idx, IRFL_CDATA_INT64); idx = emitir(IRT(IR_FLOAD, t), idx, IRFL_CDATA_INT64);
} else { } else {
@ -513,22 +551,15 @@ void LJ_FASTCALL recff_cdata_index(jit_State *J, RecordFFData *rd)
} }
} else if (tref_isstr(idx)) { } else if (tref_isstr(idx)) {
GCstr *name = strV(&rd->argv[1]); GCstr *name = strV(&rd->argv[1]);
/* Always specialize to the field name. */
emitir(IRTG(IR_EQ, IRT_STR), idx, lj_ir_kstr(J, name));
if (cd->typeid == CTID_CTYPEID) if (cd->typeid == CTID_CTYPEID)
ct = ctype_raw(cts, crec_constructor(J, cd, ptr)); ct = ctype_raw(cts, crec_constructor(J, cd, ptr));
if (ctype_isptr(ct->info)) { /* Automatically perform '->'. */ if (ctype_isstruct(ct->info)) {
CType *cct = ctype_rawchild(cts, ct);
if (ctype_isstruct(cct->info)) {
ct = cct;
goto index_struct;
}
} else if (ctype_isstruct(ct->info)) {
CTSize fofs; CTSize fofs;
CType *fct; CType *fct;
index_struct:
fct = lj_ctype_getfield(cts, ct, name, &fofs); fct = lj_ctype_getfield(cts, ct, name, &fofs);
if (fct) { if (fct) {
/* Always specialize to the field name. */
emitir(IRTG(IR_EQ, IRT_STR), idx, lj_ir_kstr(J, name));
if (ctype_isconstval(fct->info)) { if (ctype_isconstval(fct->info)) {
if (fct->size >= 0x80000000u && if (fct->size >= 0x80000000u &&
(ctype_child(cts, fct)->info & CTF_UNSIGNED)) { (ctype_child(cts, fct)->info & CTF_UNSIGNED)) {
@ -546,11 +577,26 @@ index_struct:
ofs += (ptrdiff_t)fofs; ofs += (ptrdiff_t)fofs;
} }
} else if (ctype_iscomplex(ct->info)) { } else if (ctype_iscomplex(ct->info)) {
if (strdata(name)[0] == 'i') ofs += (ct->size >> 1); if (name->len == 2 &&
sid = ctype_cid(ct->info); ((strdata(name)[0] == 'r' && strdata(name)[1] == 'e') ||
(strdata(name)[0] == 'i' && strdata(name)[1] == 'm'))) {
/* Always specialize to the field name. */
emitir(IRTG(IR_EQ, IRT_STR), idx, lj_ir_kstr(J, name));
if (strdata(name)[0] == 'i') ofs += (ct->size >> 1);
sid = ctype_cid(ct->info);
}
} }
} }
if (!sid) lj_trace_err(J, LJ_TRERR_BADTYPE); if (!sid) {
if (ctype_isptr(ct->info)) { /* Automatically perform '->'. */
CType *cct = ctype_rawchild(cts, ct);
if (ctype_isstruct(cct->info)) {
ct = cct;
if (tref_isstr(idx)) goto again;
}
}
return crec_index_meta(J, cts, ct, rd);
}
if (ofs) if (ofs)
ptr = emitir(IRT(IR_ADD, IRT_PTR), ptr, lj_ir_kintp(J, ofs)); ptr = emitir(IRT(IR_ADD, IRT_PTR), ptr, lj_ir_kintp(J, ofs));
@ -592,6 +638,7 @@ static void crec_alloc(jit_State *J, RecordFFData *rd, CTypeID id)
J->base[0] = emitir(IRTG(IR_CNEWI, IRT_CDATA), trid, sp); J->base[0] = emitir(IRTG(IR_CNEWI, IRT_CDATA), trid, sp);
} else { } else {
TRef trcd = emitir(IRTG(IR_CNEW, IRT_CDATA), trid, TREF_NIL); TRef trcd = emitir(IRTG(IR_CNEW, IRT_CDATA), trid, TREF_NIL);
cTValue *fin;
J->base[0] = trcd; J->base[0] = trcd;
if (J->base[1] && !J->base[2] && !lj_cconv_multi_init(d, &rd->argv[1])) { if (J->base[1] && !J->base[2] && !lj_cconv_multi_init(d, &rd->argv[1])) {
goto single_init; goto single_init;
@ -660,6 +707,24 @@ static void crec_alloc(jit_State *J, RecordFFData *rd, CTypeID id)
crec_ct_tv(J, d, dp, lj_ir_kint(J, 0), &tv); crec_ct_tv(J, d, dp, lj_ir_kint(J, 0), &tv);
} }
} }
/* Handle __gc metamethod. */
fin = lj_ctype_meta(cts, id, MM_gc);
if (fin) {
RecordIndex ix;
ix.idxchain = 0;
settabV(J->L, &ix.tabv, cts->finalizer);
ix.tab = lj_ir_ktab(J, cts->finalizer);
setboolV(&ix.keyv, 0); /* The key is new. Dummy value is ok here. */
ix.key = trcd;
copyTV(J->L, &ix.valv, fin);
if (tvisfunc(fin))
ix.val = lj_ir_kfunc(J, funcV(fin));
else if (tviscdata(fin))
ix.val = lj_ir_kgc(J, obj2gco(cdataV(fin)), IRT_CDATA);
else
lj_trace_err(J, LJ_TRERR_BADTYPE);
lj_record_idx(J, &ix);
}
} }
} }
@ -849,6 +914,27 @@ static TRef crec_arith_ptr(jit_State *J, TRef *sp, CType **s, MMS mm)
} }
} }
/* Record ctype arithmetic metamethods. */
static void crec_arith_meta(jit_State *J, CTState *cts, RecordFFData *rd)
{
cTValue *tv = NULL;
if (J->base[0]) {
if (tviscdata(&rd->argv[0]))
tv = lj_ctype_meta(cts, argv2cdata(J, J->base[0], &rd->argv[0])->typeid,
(MMS)rd->data);
if (!tv && J->base[1] && tviscdata(&rd->argv[1]))
tv = lj_ctype_meta(cts, argv2cdata(J, J->base[1], &rd->argv[1])->typeid,
(MMS)rd->data);
}
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_arith(jit_State *J, RecordFFData *rd) void LJ_FASTCALL recff_cdata_arith(jit_State *J, RecordFFData *rd)
{ {
CTState *cts = ctype_ctsG(J2G(J)); CTState *cts = ctype_ctsG(J2G(J));
@ -858,7 +944,9 @@ void LJ_FASTCALL recff_cdata_arith(jit_State *J, RecordFFData *rd)
for (i = 0; i < 2; i++) { for (i = 0; i < 2; i++) {
TRef tr = J->base[i]; TRef tr = J->base[i];
CType *ct = ctype_get(cts, CTID_DOUBLE); CType *ct = ctype_get(cts, CTID_DOUBLE);
if (tref_iscdata(tr)) { if (!tr) {
goto trymeta;
} else if (tref_iscdata(tr)) {
CTypeID id = argv2cdata(J, tr, &rd->argv[i])->typeid; CTypeID id = argv2cdata(J, tr, &rd->argv[i])->typeid;
ct = ctype_raw(cts, id); ct = ctype_raw(cts, id);
if (ctype_isptr(ct->info)) { /* Resolve pointer or reference. */ if (ctype_isptr(ct->info)) { /* Resolve pointer or reference. */
@ -876,11 +964,11 @@ void LJ_FASTCALL recff_cdata_arith(jit_State *J, RecordFFData *rd)
if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct); if (ctype_isenum(ct->info)) ct = ctype_child(cts, ct);
if (ctype_isnum(ct->info)) { if (ctype_isnum(ct->info)) {
IRType t = crec_ct2irt(ct); IRType t = crec_ct2irt(ct);
if (t == IRT_CDATA) goto err_type; if (t == IRT_CDATA) goto trymeta;
if (t == IRT_I64 || t == IRT_U64) lj_needsplit(J); if (t == IRT_I64 || t == IRT_U64) lj_needsplit(J);
tr = emitir(IRT(IR_XLOAD, t), tr, 0); tr = emitir(IRT(IR_XLOAD, t), tr, 0);
} else if (!(ctype_isptr(ct->info) || ctype_isrefarray(ct->info))) { } else if (!(ctype_isptr(ct->info) || ctype_isrefarray(ct->info))) {
goto err_type; goto trymeta;
} }
} else if (tref_isnil(tr)) { } else if (tref_isnil(tr)) {
tr = lj_ir_kptr(J, NULL); tr = lj_ir_kptr(J, NULL);
@ -888,7 +976,7 @@ void LJ_FASTCALL recff_cdata_arith(jit_State *J, RecordFFData *rd)
} else if (tref_isinteger(tr)) { } else if (tref_isinteger(tr)) {
ct = ctype_get(cts, CTID_INT32); ct = ctype_get(cts, CTID_INT32);
} else if (!tref_isnum(tr)) { } else if (!tref_isnum(tr)) {
goto err_type; goto trymeta;
} }
ok: ok:
s[i] = ct; s[i] = ct;
@ -896,21 +984,22 @@ void LJ_FASTCALL recff_cdata_arith(jit_State *J, RecordFFData *rd)
} }
{ {
TRef tr; TRef tr;
if (!(tr = crec_arith_int64(J, sp, s, (MMS)rd->data)) && if ((tr = crec_arith_int64(J, sp, s, (MMS)rd->data)) ||
!(tr = crec_arith_ptr(J, sp, s, (MMS)rd->data))) { (tr = crec_arith_ptr(J, sp, s, (MMS)rd->data))) {
err_type: J->base[0] = tr;
lj_trace_err(J, LJ_TRERR_BADTYPE); /* Fixup cdata comparisons, too. Avoids some cdata escapes. */
} if (J->postproc == LJ_POST_FIXGUARD && frame_iscont(J->L->base-1)) {
/* Fixup cdata comparisons, too. Avoids some cdata escapes. */ const BCIns *pc = frame_contpc(J->L->base-1) - 1;
if (J->postproc == LJ_POST_FIXGUARD && frame_iscont(J->L->base-1)) { if (bc_op(*pc) <= BC_ISNEP) {
const BCIns *pc = frame_contpc(J->L->base-1) - 1; setframe_pc(&J2G(J)->tmptv, pc);
if (bc_op(*pc) <= BC_ISNEP) { J2G(J)->tmptv.u32.lo = ((tref_istrue(tr) ^ bc_op(*pc)) & 1);
setframe_pc(&J2G(J)->tmptv, pc); J->postproc = LJ_POST_FIXCOMP;
J2G(J)->tmptv.u32.lo = ((tref_istrue(tr) ^ bc_op(*pc)) & 1); }
J->postproc = LJ_POST_FIXCOMP;
} }
} else {
trymeta:
crec_arith_meta(J, cts, rd);
} }
J->base[0] = tr;
} }
} }

View File

@ -306,6 +306,22 @@ CTInfo lj_ctype_info(CTState *cts, CTypeID id, CTSize *szp)
return qual; return qual;
} }
/* Get ctype metamethod. */
cTValue *lj_ctype_meta(CTState *cts, CTypeID id, MMS mm)
{
CType *ct = ctype_get(cts, id);
cTValue *tv;
while (ctype_isattrib(ct->info)) {
id = ctype_cid(ct->info);
ct = ctype_get(cts, id);
}
tv = lj_tab_getint(cts->metatype, (int32_t)id);
if (tv && tvistab(tv) &&
(tv = lj_tab_getstr(tabV(tv), mmname_str(cts->g, mm))) && !tvisnil(tv))
return tv;
return NULL;
}
/* -- C type representation ----------------------------------------------- */ /* -- C type representation ----------------------------------------------- */
/* Fixed max. length of a C type representation. */ /* Fixed max. length of a C type representation. */

View File

@ -159,6 +159,7 @@ typedef struct CTState {
lua_State *L; /* Lua state (needed for errors and allocations). */ lua_State *L; /* Lua state (needed for errors and allocations). */
global_State *g; /* Global state. */ global_State *g; /* Global state. */
GCtab *finalizer; /* Map of cdata to finalizer. */ GCtab *finalizer; /* Map of cdata to finalizer. */
GCtab *metatype; /* Map of CTypeID to metatable. */
CTypeID1 hash[CTHASH_SIZE]; /* Hash anchors for C type table. */ CTypeID1 hash[CTHASH_SIZE]; /* Hash anchors for C type table. */
} CTState; } CTState;
@ -426,6 +427,7 @@ LJ_FUNC CType *lj_ctype_rawref(CTState *cts, CTypeID id);
LJ_FUNC CTSize lj_ctype_size(CTState *cts, CTypeID id); LJ_FUNC CTSize lj_ctype_size(CTState *cts, CTypeID id);
LJ_FUNC CTSize lj_ctype_vlsize(CTState *cts, CType *ct, CTSize nelem); LJ_FUNC CTSize lj_ctype_vlsize(CTState *cts, CType *ct, CTSize nelem);
LJ_FUNC CTInfo lj_ctype_info(CTState *cts, CTypeID id, CTSize *szp); LJ_FUNC CTInfo lj_ctype_info(CTState *cts, CTypeID id, CTSize *szp);
LJ_FUNC cTValue *lj_ctype_meta(CTState *cts, CTypeID id, MMS mm);
LJ_FUNC GCstr *lj_ctype_repr(lua_State *L, CTypeID id, GCstr *name); LJ_FUNC GCstr *lj_ctype_repr(lua_State *L, CTypeID id, GCstr *name);
LJ_FUNC GCstr *lj_ctype_repr_int64(lua_State *L, uint64_t n, int isunsigned); LJ_FUNC GCstr *lj_ctype_repr_int64(lua_State *L, uint64_t n, int isunsigned);
LJ_FUNC GCstr *lj_ctype_repr_complex(lua_State *L, void *sp, CTSize size); LJ_FUNC GCstr *lj_ctype_repr_complex(lua_State *L, void *sp, CTSize size);