diff --git a/doc/ext_ffi_api.html b/doc/ext_ffi_api.html
index 16066612..503c6e4a 100644
--- a/doc/ext_ffi_api.html
+++ b/doc/ext_ffi_api.html
@@ -525,7 +525,16 @@ Returns a string representation of the value of 64 bit integers
complex numbers ("re±imi"). Otherwise
returns a string representation of the C type of a ctype object
("ctype<type>") or a cdata object
-("cdata<type>: address").
+("cdata<type>: address"), unless you
+override it with a __tostring metamethod (see
+ffi.metatype()).
+
+
+iter, obj, start = pairs(cdata)
+iter, obj, start = ipairs(cdata)
+
+Calls the __pairs or __ipairs metamethod of the
+corresponding ctype.
Extensions to the Lua Parser
diff --git a/src/lib_base.c b/src/lib_base.c
index 9702c5b4..824fc0e7 100644
--- a/src/lib_base.c
+++ b/src/lib_base.c
@@ -278,12 +278,16 @@ LJLIB_ASM(next)
return FFH_UNREACHABLE;
}
-#ifdef LUAJIT_ENABLE_LUA52COMPAT
+#if defined(LUAJIT_ENABLE_LUA52COMPAT) || LJ_HASFFI
static int ffh_pairs(lua_State *L, MMS mm)
{
TValue *o = lj_lib_checkany(L, 1);
cTValue *mo = lj_meta_lookup(L, o, mm);
- if (!tvisnil(mo)) {
+ if (
+#if !defined(LUAJIT_ENABLE_LUA52COMPAT)
+ tviscdata(o) &&
+#endif
+ !tvisnil(mo)) {
L->top = o+1; /* Only keep one argument. */
copyTV(L, L->base-1, mo); /* Replace callable. */
return FFH_TAILCALL;
diff --git a/src/lib_ffi.c b/src/lib_ffi.c
index 24a6625c..69bebefc 100644
--- a/src/lib_ffi.c
+++ b/src/lib_ffi.c
@@ -323,6 +323,30 @@ checkgc:
return 1;
}
+static int ffi_pairs(lua_State *L, MMS mm)
+{
+ CTState *cts = ctype_cts(L);
+ CTypeID id = ffi_checkcdata(L, 1)->ctypeid;
+ 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);
+ if (!tv)
+ lj_err_callerv(L, LJ_ERR_FFI_BADMM, strdata(lj_ctype_repr(L, id, NULL)),
+ strdata(mmname_str(G(L), mm)));
+ return lj_meta_tailcall(L, tv);
+}
+
+LJLIB_CF(ffi_meta___pairs)
+{
+ return ffi_pairs(L, MM_pairs);
+}
+
+LJLIB_CF(ffi_meta___ipairs)
+{
+ return ffi_pairs(L, MM_ipairs);
+}
+
LJLIB_PUSH("ffi") LJLIB_SET(__metatable)
#include "lj_libdef.h"
diff --git a/src/lj_errmsg.h b/src/lj_errmsg.h
index 5b10dea8..dd3eefa8 100644
--- a/src/lj_errmsg.h
+++ b/src/lj_errmsg.h
@@ -162,6 +162,7 @@ ERRDEF(FFI_NUMARG, "wrong number of arguments for function call")
ERRDEF(FFI_BADMEMBER, LUA_QS " has no member named " LUA_QS)
ERRDEF(FFI_BADIDX, LUA_QS " cannot be indexed")
ERRDEF(FFI_BADIDXW, LUA_QS " cannot be indexed with " LUA_QS)
+ERRDEF(FFI_BADMM, LUA_QS " has no " LUA_QS " metamethod")
ERRDEF(FFI_WRCONST, "attempt to write to constant location")
ERRDEF(FFI_NODECL, "missing declaration for symbol " LUA_QS)
ERRDEF(FFI_BADCBACK, "bad callback")
diff --git a/src/lj_obj.h b/src/lj_obj.h
index 7890e54b..67fb5ed9 100644
--- a/src/lj_obj.h
+++ b/src/lj_obj.h
@@ -447,10 +447,12 @@ enum {
#define MMDEF_FFI(_)
#endif
-#ifdef LUAJIT_ENABLE_LUA52COMPAT
-#define MMDEF_52(_) _(pairs) _(ipairs)
+#if defined(LUAJIT_ENABLE_LUA52COMPAT) || LJ_HASFFI
+#define MMDEF_PAIRS(_) _(pairs) _(ipairs)
#else
-#define MMDEF_52(_)
+#define MMDEF_PAIRS(_)
+#define MM_pairs 255
+#define MM_ipairs 255
#endif
#define MMDEF(_) \
@@ -460,7 +462,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_FFI(_) MMDEF_52(_)
+ _(metatable) _(tostring) MMDEF_FFI(_) MMDEF_PAIRS(_)
typedef enum {
#define MMENUM(name) MM_##name,