summaryrefslogtreecommitdiff
path: root/upb/bindings
diff options
context:
space:
mode:
authorJosh Haberman <jhaberman@gmail.com>2016-09-09 14:03:25 -0700
committerJosh Haberman <jhaberman@gmail.com>2016-11-29 17:56:13 +0000
commit4b0c4ca7fb0aa9207af3398e04534b23fbb88f27 (patch)
tree045750c6262e74f366ae2ec29797d3816005c21a /upb/bindings
parent77c97fd3f29caa5c243294b5f4e6763b3ed3c36f (diff)
New upb_msg code and Lua bindings around it.
There are still some things that are unfinished, but we are at parity with what Lua had before.
Diffstat (limited to 'upb/bindings')
-rw-r--r--upb/bindings/lua/def.c1346
-rw-r--r--upb/bindings/lua/msg.c1073
-rw-r--r--upb/bindings/lua/upb.c1975
-rw-r--r--upb/bindings/lua/upb.h64
-rw-r--r--upb/bindings/lua/upb/msg.c892
-rw-r--r--upb/bindings/lua/upb/pb.c94
6 files changed, 3413 insertions, 2031 deletions
diff --git a/upb/bindings/lua/def.c b/upb/bindings/lua/def.c
new file mode 100644
index 0000000..7e41782
--- /dev/null
+++ b/upb/bindings/lua/def.c
@@ -0,0 +1,1346 @@
+
+#include <float.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lauxlib.h"
+#include "upb/bindings/lua/upb.h"
+#include "upb/def.h"
+#include "upb/pb/glue.h"
+
+#define LUPB_ENUMDEF "lupb.enumdef"
+#define LUPB_FIELDDEF "lupb.fielddef"
+#define LUPB_FILEDEF "lupb.filedef"
+#define LUPB_MSGDEF "lupb.msgdef"
+#define LUPB_ONEOFDEF "lupb.oneof"
+#define LUPB_SYMTAB "lupb.symtab"
+#define LUPB_OBJCACHE "lupb.objcache"
+
+#define CHK(pred) do { \
+ upb_status status = UPB_STATUS_INIT; \
+ pred; \
+ lupb_checkstatus(L, &status); \
+ } while (0)
+
+
+const char *lupb_checkname(lua_State *L, int narg) {
+ size_t len;
+ const char *name = luaL_checklstring(L, narg, &len);
+ if (strlen(name) != len)
+ luaL_error(L, "names cannot have embedded NULLs");
+ return name;
+}
+
+upb_fieldtype_t lupb_checkfieldtype(lua_State *L, int narg) {
+ int type = luaL_checkint(L, narg);
+ if (!upb_fielddef_checktype(type))
+ luaL_argerror(L, narg, "invalid field type");
+ return type;
+}
+
+
+/* lupb_refcounted ************************************************************/
+
+/* All upb objects that use upb_refcounted have a userdata that begins with a
+ * pointer to that object. Each type has its own metatable. Objects are cached
+ * in a weak table indexed by the C pointer of the object they are caching.
+ *
+ * Note that we consistently use memcpy() to read to/from the object. This
+ * allows the userdata to use its own struct without violating aliasing, as
+ * long as it begins with a pointer. */
+
+/* Checks type; if it matches, pulls the pointer out of the wrapper. */
+void *lupb_refcounted_check(lua_State *L, int narg, const char *type) {
+ void *ud = lua_touserdata(L, narg);
+ void *ret;
+
+ if (ud) {
+ memcpy(&ret, ud, sizeof(ret));
+ if (!ret) {
+ luaL_error(L, "called into dead object");
+ }
+ }
+
+ luaL_checkudata(L, narg, type);
+ return ret;
+}
+
+bool lupb_refcounted_pushwrapper(lua_State *L, const upb_refcounted *obj,
+ const char *type, const void *ref_donor,
+ size_t size) {
+ bool create;
+ void *ud;
+
+ if (obj == NULL) {
+ lua_pushnil(L);
+ return false;
+ }
+
+ /* Lookup our cache in the registry (we don't put our objects in the registry
+ * directly because we need our cache to be a weak table). */
+ lua_getfield(L, LUA_REGISTRYINDEX, LUPB_OBJCACHE);
+ UPB_ASSERT(!lua_isnil(L, -1)); /* Should have been created by luaopen_upb. */
+ lua_pushlightuserdata(L, (void*)obj);
+ lua_rawget(L, -2);
+ /* Stack is now: objcache, cached value. */
+
+ create = false;
+
+ if (lua_isnil(L, -1)) {
+ create = true;
+ } else {
+ void *ud = lua_touserdata(L, -1);
+ void *ud_obj;
+ lupb_assert(L, ud);
+ memcpy(&ud_obj, ud, sizeof(void*));
+
+ /* A corner case: it is possible for the value to be GC'd
+ * already, in which case we should evict this entry and create
+ * a new one. */
+ if (ud_obj == NULL) {
+ create = true;
+ }
+ }
+
+ ud = NULL;
+
+ if (create) {
+ /* Remove bad cached value and push new value. */
+ lua_pop(L, 1);
+
+ /* All of our userdata begin with a pointer to the obj. */
+ ud = lua_newuserdata(L, size);
+ memcpy(ud, &obj, sizeof(void*));
+ upb_refcounted_donateref(obj, ref_donor, ud);
+
+ luaL_getmetatable(L, type);
+ /* Should have been created by luaopen_upb. */
+ lupb_assert(L, !lua_isnil(L, -1));
+ lua_setmetatable(L, -2);
+
+ /* Set it in the cache. */
+ lua_pushlightuserdata(L, (void*)obj);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, -4);
+ } else {
+ /* Existing wrapper obj already has a ref. */
+ ud = lua_touserdata(L, -1);
+ upb_refcounted_checkref(obj, ud);
+ if (ref_donor)
+ upb_refcounted_unref(obj, ref_donor);
+ }
+
+ lua_insert(L, -2);
+ lua_pop(L, 1);
+ return create;
+}
+
+void lupb_refcounted_pushnewrapper(lua_State *L, const upb_refcounted *obj,
+ const char *type, const void *ref_donor) {
+ bool created =
+ lupb_refcounted_pushwrapper(L, obj, type, ref_donor, sizeof(void *));
+ UPB_ASSERT(created == true);
+}
+
+int lupb_refcounted_gc(lua_State *L) {
+ void *ud = lua_touserdata(L, 1);
+ void *nullp;
+ upb_refcounted *obj;
+ memcpy(&obj, ud, sizeof(obj));
+ upb_refcounted_unref(obj, ud);
+
+ /* Zero out pointer so we can detect a call into a GC'd object. */
+ nullp = NULL;
+ memcpy(ud, &nullp, sizeof(nullp));
+
+ return 0;
+}
+
+
+/* lupb_def *******************************************************************/
+
+static const upb_def *lupb_def_check(lua_State *L, int narg) {
+ upb_def *ret;
+ void *ud, *ud2;
+
+ ud = lua_touserdata(L, narg);
+ if (ud) {
+ memcpy(&ret, ud, sizeof(ret));
+ if (!ret) {
+ luaL_error(L, "called into dead object");
+ }
+ }
+
+ ud2 = luaL_testudata(L, narg, LUPB_MSGDEF);
+ if (!ud2) ud2 = luaL_testudata(L, narg, LUPB_ENUMDEF);
+ if (!ud2) ud2 = luaL_testudata(L, narg, LUPB_FIELDDEF);
+ if (!ud2) luaL_typerror(L, narg, "upb def");
+
+ return ret;
+}
+
+static upb_def *lupb_def_checkmutable(lua_State *L, int narg) {
+ const upb_def *def = lupb_def_check(L, narg);
+ if (upb_def_isfrozen(def))
+ luaL_error(L, "not allowed on frozen value");
+ return (upb_def*)def;
+}
+
+bool lupb_def_pushwrapper(lua_State *L, const upb_def *def,
+ const void *ref_donor) {
+ const char *type = NULL;
+ bool created;
+
+ if (def == NULL) {
+ lua_pushnil(L);
+ return false;
+ }
+
+ switch (upb_def_type(def)) {
+ case UPB_DEF_MSG:
+ type = LUPB_MSGDEF;
+ break;
+ case UPB_DEF_FIELD:
+ type = LUPB_FIELDDEF;
+ break;
+ case UPB_DEF_ENUM:
+ type = LUPB_ENUMDEF;
+ break;
+ default:
+ UPB_UNREACHABLE();
+ }
+
+ created = lupb_refcounted_pushwrapper(L, upb_def_upcast(def), type, ref_donor,
+ sizeof(void *));
+ return created;
+}
+
+void lupb_def_pushnewrapper(lua_State *L, const upb_def *def,
+ const void *ref_donor) {
+ bool created = lupb_def_pushwrapper(L, def, ref_donor);
+ UPB_ASSERT(created == true);
+}
+
+static int lupb_def_type(lua_State *L) {
+ const upb_def *def = lupb_def_check(L, 1);
+ lua_pushinteger(L, upb_def_type(def));
+ return 1;
+}
+
+static int lupb_def_freeze(lua_State *L) {
+ upb_def *def = lupb_def_checkmutable(L, 1);
+ CHK(upb_def_freeze(&def, 1, &status));
+ return 0;
+}
+
+static int lupb_def_isfrozen(lua_State *L) {
+ const upb_def *def = lupb_def_check(L, 1);
+ lua_pushboolean(L, upb_def_isfrozen(def));
+ return 1;
+}
+
+static int lupb_def_fullname(lua_State *L) {
+ const upb_def *def = lupb_def_check(L, 1);
+ lua_pushstring(L, upb_def_fullname(def));
+ return 1;
+}
+
+static int lupb_def_name(lua_State *L) {
+ const upb_def *def = lupb_def_check(L, 1);
+ lua_pushstring(L, upb_def_name(def));
+ return 1;
+}
+
+static int lupb_def_setfullname(lua_State *L) {
+ const char *name = lupb_checkname(L, 2);
+ CHK(upb_def_setfullname(lupb_def_checkmutable(L, 1), name, &status));
+ return 0;
+}
+
+#define LUPB_COMMON_DEF_METHODS \
+ {"def_type", lupb_def_type}, \
+ {"full_name", lupb_def_fullname}, \
+ {"freeze", lupb_def_freeze}, \
+ {"is_frozen", lupb_def_isfrozen}, \
+ {"name", lupb_def_name}, \
+ {"set_full_name", lupb_def_setfullname}, \
+
+
+/* lupb_fielddef **************************************************************/
+
+void lupb_fielddef_pushwrapper(lua_State *L, const upb_fielddef *f,
+ const void *ref_donor) {
+ lupb_def_pushwrapper(L, upb_fielddef_upcast(f), ref_donor);
+}
+
+const upb_fielddef *lupb_fielddef_check(lua_State *L, int narg) {
+ return lupb_refcounted_check(L, narg, LUPB_FIELDDEF);
+}
+
+static upb_fielddef *lupb_fielddef_checkmutable(lua_State *L, int narg) {
+ const upb_fielddef *f = lupb_fielddef_check(L, narg);
+ if (upb_fielddef_isfrozen(f))
+ luaL_error(L, "not allowed on frozen value");
+ return (upb_fielddef*)f;
+}
+
+static int lupb_fielddef_new(lua_State *L) {
+ upb_fielddef *f = upb_fielddef_new(&f);
+ lupb_def_pushnewrapper(L, upb_fielddef_upcast(f), &f);
+ return 1;
+}
+
+/* Getters */
+
+static int lupb_fielddef_containingtype(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lupb_msgdef_pushwrapper(L, upb_fielddef_containingtype(f), NULL);
+ return 1;
+}
+
+static int lupb_fielddef_containingtypename(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ lua_pushstring(L, upb_fielddef_containingtypename(f));
+ return 1;
+}
+
+static int lupb_fielddef_default(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ switch (upb_fielddef_type(f)) {
+ case UPB_TYPE_INT32:
+ int32:
+ lupb_pushint32(L, upb_fielddef_defaultint32(f)); break;
+ case UPB_TYPE_INT64:
+ lupb_pushint64(L, upb_fielddef_defaultint64(f)); break;
+ case UPB_TYPE_UINT32:
+ lupb_pushuint32(L, upb_fielddef_defaultuint32(f)); break;
+ case UPB_TYPE_UINT64:
+ lupb_pushuint64(L, upb_fielddef_defaultuint64(f)); break;
+ case UPB_TYPE_DOUBLE:
+ lua_pushnumber(L, upb_fielddef_defaultdouble(f)); break;
+ case UPB_TYPE_FLOAT:
+ lua_pushnumber(L, upb_fielddef_defaultfloat(f)); break;
+ case UPB_TYPE_BOOL:
+ lua_pushboolean(L, upb_fielddef_defaultbool(f)); break;
+ case UPB_TYPE_ENUM:
+ if (upb_fielddef_enumhasdefaultstr(f)) {
+ goto str;
+ } else if (upb_fielddef_enumhasdefaultint32(f)) {
+ goto int32;
+ } else {
+ lua_pushnil(L);
+ }
+ break;
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES:
+ str: {
+ size_t len;
+ const char *data = upb_fielddef_defaultstr(f, &len);
+ lua_pushlstring(L, data, len);
+ break;
+ }
+ case UPB_TYPE_MESSAGE:
+ return luaL_error(L, "Message fields do not have explicit defaults.");
+ }
+ return 1;
+}
+
+static int lupb_fielddef_getsel(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ upb_selector_t sel;
+ if (upb_handlers_getselector(f, luaL_checknumber(L, 2), &sel)) {
+ lua_pushinteger(L, sel);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int lupb_fielddef_hassubdef(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushboolean(L, upb_fielddef_hassubdef(f));
+ return 1;
+}
+
+static int lupb_fielddef_index(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushinteger(L, upb_fielddef_index(f));
+ return 1;
+}
+
+static int lupb_fielddef_intfmt(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushinteger(L, upb_fielddef_intfmt(f));
+ return 1;
+}
+
+static int lupb_fielddef_isextension(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushboolean(L, upb_fielddef_isextension(f));
+ return 1;
+}
+
+static int lupb_fielddef_istagdelim(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushboolean(L, upb_fielddef_istagdelim(f));
+ return 1;
+}
+
+static int lupb_fielddef_label(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushinteger(L, upb_fielddef_label(f));
+ return 1;
+}
+
+static int lupb_fielddef_lazy(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushboolean(L, upb_fielddef_lazy(f));
+ return 1;
+}
+
+static int lupb_fielddef_name(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushstring(L, upb_fielddef_name(f));
+ return 1;
+}
+
+static int lupb_fielddef_number(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ int32_t num = upb_fielddef_number(f);
+ if (num)
+ lua_pushinteger(L, num);
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+static int lupb_fielddef_packed(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lua_pushboolean(L, upb_fielddef_packed(f));
+ return 1;
+}
+
+static int lupb_fielddef_subdef(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ const upb_def *def;
+
+ if (!upb_fielddef_hassubdef(f))
+ luaL_error(L, "Tried to get subdef of non-message field");
+ def = upb_fielddef_subdef(f);
+ lupb_def_pushwrapper(L, def, NULL);
+ return 1;
+}
+
+static int lupb_fielddef_subdefname(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ if (!upb_fielddef_hassubdef(f))
+ luaL_error(L, "Tried to get subdef name of non-message field");
+ lua_pushstring(L, upb_fielddef_subdefname(f));
+ return 1;
+}
+
+static int lupb_fielddef_type(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ if (upb_fielddef_typeisset(f))
+ lua_pushinteger(L, upb_fielddef_type(f));
+ else
+ lua_pushnil(L);
+ return 1;
+}
+
+/* Setters */
+
+static int lupb_fielddef_setcontainingtypename(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ const char *name = NULL;
+ if (!lua_isnil(L, 2))
+ name = lupb_checkname(L, 2);
+ CHK(upb_fielddef_setcontainingtypename(f, name, &status));
+ return 0;
+}
+
+static int lupb_fielddef_setdefault(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+
+ switch (upb_fielddef_type(f)) {
+ case UPB_TYPE_INT32:
+ upb_fielddef_setdefaultint32(f, lupb_checkint32(L, 2));
+ break;
+ case UPB_TYPE_INT64:
+ upb_fielddef_setdefaultint64(f, lupb_checkint64(L, 2));
+ break;
+ case UPB_TYPE_UINT32:
+ upb_fielddef_setdefaultuint32(f, lupb_checkuint32(L, 2));
+ break;
+ case UPB_TYPE_UINT64:
+ upb_fielddef_setdefaultuint64(f, lupb_checkuint64(L, 2));
+ break;
+ case UPB_TYPE_DOUBLE:
+ upb_fielddef_setdefaultdouble(f, lupb_checkdouble(L, 2));
+ break;
+ case UPB_TYPE_FLOAT:
+ upb_fielddef_setdefaultfloat(f, lupb_checkfloat(L, 2));
+ break;
+ case UPB_TYPE_BOOL:
+ upb_fielddef_setdefaultbool(f, lupb_checkbool(L, 2));
+ break;
+ case UPB_TYPE_MESSAGE:
+ return luaL_error(L, "Message types cannot have defaults.");
+ case UPB_TYPE_ENUM:
+ if (lua_type(L, 2) != LUA_TSTRING) {
+ upb_fielddef_setdefaultint32(f, lupb_checkint32(L, 2));
+ break;
+ }
+ /* Else fall through and set string default. */
+ case UPB_TYPE_BYTES:
+ case UPB_TYPE_STRING: {
+ size_t len;
+ const char *str = lua_tolstring(L, 2, &len);
+ CHK(upb_fielddef_setdefaultstr(f, str, len, &status));
+ }
+ }
+ return 0;
+}
+
+static int lupb_fielddef_setisextension(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ CHK(upb_fielddef_setisextension(f, lupb_checkbool(L, 2)));
+ return 0;
+}
+
+static int lupb_fielddef_setlabel(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ int label = luaL_checkint(L, 2);
+ if (!upb_fielddef_checklabel(label))
+ luaL_argerror(L, 2, "invalid field label");
+ upb_fielddef_setlabel(f, label);
+ return 0;
+}
+
+static int lupb_fielddef_setlazy(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ upb_fielddef_setlazy(f, lupb_checkbool(L, 2));
+ return 0;
+}
+
+static int lupb_fielddef_setname(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ CHK(upb_fielddef_setname(f, lupb_checkname(L, 2), &status));
+ return 0;
+}
+
+static int lupb_fielddef_setnumber(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ CHK(upb_fielddef_setnumber(f, luaL_checkint(L, 2), &status));
+ return 0;
+}
+
+static int lupb_fielddef_setpacked(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ upb_fielddef_setpacked(f, lupb_checkbool(L, 2));
+ return 0;
+}
+
+static int lupb_fielddef_setsubdef(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ const upb_def *def = NULL;
+ if (!lua_isnil(L, 2))
+ def = lupb_def_check(L, 2);
+ CHK(upb_fielddef_setsubdef(f, def, &status));
+ return 0;
+}
+
+static int lupb_fielddef_setsubdefname(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ const char *name = NULL;
+ if (!lua_isnil(L, 2))
+ name = lupb_checkname(L, 2);
+ CHK(upb_fielddef_setsubdefname(f, name, &status));
+ return 0;
+}
+
+static int lupb_fielddef_settype(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ upb_fielddef_settype(f, lupb_checkfieldtype(L, 2));
+ return 0;
+}
+
+static int lupb_fielddef_setintfmt(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ int32_t intfmt = luaL_checknumber(L, 2);
+ if (!upb_fielddef_checkintfmt(intfmt))
+ luaL_argerror(L, 2, "invalid intfmt");
+ upb_fielddef_setintfmt(f, intfmt);
+ return 0;
+}
+
+static int lupb_fielddef_settagdelim(lua_State *L) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
+ bool is_tag_delim = lupb_checkbool(L, 2);
+ CHK(upb_fielddef_settagdelim(f, is_tag_delim));
+ return 0;
+}
+
+static const struct luaL_Reg lupb_fielddef_mm[] = {
+ {"__gc", lupb_refcounted_gc},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg lupb_fielddef_m[] = {
+ LUPB_COMMON_DEF_METHODS
+
+ {"containing_type", lupb_fielddef_containingtype},
+ {"containing_type_name", lupb_fielddef_containingtypename},
+ {"default", lupb_fielddef_default},
+ {"getsel", lupb_fielddef_getsel},
+ {"has_subdef", lupb_fielddef_hassubdef},
+ {"index", lupb_fielddef_index},
+ {"intfmt", lupb_fielddef_intfmt},
+ {"is_extension", lupb_fielddef_isextension},
+ {"istagdelim", lupb_fielddef_istagdelim},
+ {"label", lupb_fielddef_label},
+ {"lazy", lupb_fielddef_lazy},
+ {"name", lupb_fielddef_name},
+ {"number", lupb_fielddef_number},
+ {"packed", lupb_fielddef_packed},
+ {"subdef", lupb_fielddef_subdef},
+ {"subdef_name", lupb_fielddef_subdefname},
+ {"type", lupb_fielddef_type},
+
+ {"set_containing_type_name", lupb_fielddef_setcontainingtypename},
+ {"set_default", lupb_fielddef_setdefault},
+ {"set_is_extension", lupb_fielddef_setisextension},
+ {"set_label", lupb_fielddef_setlabel},
+ {"set_lazy", lupb_fielddef_setlazy},
+ {"set_name", lupb_fielddef_setname},
+ {"set_number", lupb_fielddef_setnumber},
+ {"set_packed", lupb_fielddef_setpacked},
+ {"set_subdef", lupb_fielddef_setsubdef},
+ {"set_subdef_name", lupb_fielddef_setsubdefname},
+ {"set_type", lupb_fielddef_settype},
+ {"set_intfmt", lupb_fielddef_setintfmt},
+ {"set_tagdelim", lupb_fielddef_settagdelim},
+
+ {NULL, NULL}
+};
+
+
+/* lupb_oneofdef **************************************************************/
+
+void lupb_oneofdef_pushwrapper(lua_State *L, const upb_oneofdef *o,
+ const void *ref_donor) {
+ lupb_refcounted_pushwrapper(L, upb_oneofdef_upcast(o), LUPB_ONEOFDEF,
+ ref_donor, sizeof(void *));
+}
+
+const upb_oneofdef *lupb_oneofdef_check(lua_State *L, int narg) {
+ return lupb_refcounted_check(L, narg, LUPB_ONEOFDEF);
+}
+
+static upb_oneofdef *lupb_oneofdef_checkmutable(lua_State *L, int narg) {
+ const upb_oneofdef *o = lupb_oneofdef_check(L, narg);
+ if (upb_oneofdef_isfrozen(o))
+ luaL_error(L, "not allowed on frozen value");
+ return (upb_oneofdef*)o;
+}
+
+static int lupb_oneofdef_new(lua_State *L) {
+ upb_oneofdef *o = upb_oneofdef_new(&o);
+ lupb_refcounted_pushnewrapper(L, upb_oneofdef_upcast(o), LUPB_ONEOFDEF, &o);
+ return 1;
+}
+
+/* Getters */
+
+static int lupb_oneofdef_containingtype(lua_State *L) {
+ const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
+ lupb_def_pushwrapper(L, upb_msgdef_upcast(upb_oneofdef_containingtype(o)),
+ NULL);
+ return 1;
+}
+
+static int lupb_oneofdef_field(lua_State *L) {
+ const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
+ int type = lua_type(L, 2);
+ const upb_fielddef *f;
+ if (type == LUA_TNUMBER) {
+ f = upb_oneofdef_itof(o, lua_tointeger(L, 2));
+ } else if (type == LUA_TSTRING) {
+ f = upb_oneofdef_ntofz(o, lua_tostring(L, 2));
+ } else {
+ const char *msg = lua_pushfstring(L, "number or string expected, got %s",
+ luaL_typename(L, 2));
+ return luaL_argerror(L, 2, msg);
+ }
+
+ lupb_def_pushwrapper(L, upb_fielddef_upcast(f), NULL);
+ return 1;
+}
+
+static int lupb_oneofdef_len(lua_State *L) {
+ const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
+ lua_pushinteger(L, upb_oneofdef_numfields(o));
+ return 1;
+}
+
+static int lupb_oneofdef_name(lua_State *L) {
+ const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
+ lua_pushstring(L, upb_oneofdef_name(o));
+ return 1;
+}
+
+/* Setters */
+
+static int lupb_oneofdef_add(lua_State *L) {
+ upb_oneofdef *o = lupb_oneofdef_checkmutable(L, 1);
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 2);
+ CHK(upb_oneofdef_addfield(o, f, NULL, &status));
+ return 0;
+}
+
+static int lupb_oneofdef_setname(lua_State *L) {
+ upb_oneofdef *o = lupb_oneofdef_checkmutable(L, 1);
+ CHK(upb_oneofdef_setname(o, lupb_checkname(L, 2), &status));
+ return 0;
+}
+
+static const struct luaL_Reg lupb_oneofdef_m[] = {
+ {"containing_type", lupb_oneofdef_containingtype},
+ {"field", lupb_oneofdef_field},
+ {"name", lupb_oneofdef_name},
+
+ {"add", lupb_oneofdef_add},
+ {"set_name", lupb_oneofdef_setname},
+
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg lupb_oneofdef_mm[] = {
+ {"__len", lupb_oneofdef_len},
+ {NULL, NULL}
+};
+
+
+/* lupb_msgdef ****************************************************************/
+
+typedef struct {
+ const upb_msgdef *md;
+} lupb_msgdef;
+
+void lupb_msgdef_pushwrapper(lua_State *L, const upb_msgdef *m,
+ const void *ref_donor) {
+ lupb_def_pushwrapper(L, upb_msgdef_upcast(m), ref_donor);
+}
+
+const upb_msgdef *lupb_msgdef_check(lua_State *L, int narg) {
+ return lupb_refcounted_check(L, narg, LUPB_MSGDEF);
+}
+
+static upb_msgdef *lupb_msgdef_checkmutable(lua_State *L, int narg) {
+ const upb_msgdef *m = lupb_msgdef_check(L, narg);
+ if (upb_msgdef_isfrozen(m))
+ luaL_error(L, "not allowed on frozen value");
+ return (upb_msgdef*)m;
+}
+
+static int lupb_msgdef_new(lua_State *L) {
+ upb_msgdef *md = upb_msgdef_new(&md);
+ lupb_def_pushnewrapper(L, upb_msgdef_upcast(md), &md);
+ return 1;
+}
+
+static int lupb_msgdef_add(lua_State *L) {
+ upb_msgdef *m = lupb_msgdef_checkmutable(L, 1);
+
+ /* Both oneofs and fields can be added. */
+ if (luaL_testudata(L, 2, LUPB_FIELDDEF)) {
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 2);
+ CHK(upb_msgdef_addfield(m, f, NULL, &status));
+ } else if (luaL_testudata(L, 2, LUPB_ONEOFDEF)) {
+ upb_oneofdef *o = lupb_oneofdef_checkmutable(L, 2);
+ CHK(upb_msgdef_addoneof(m, o, NULL, &status));
+ }
+ return 0;
+}
+
+static int lupb_msgdef_len(lua_State *L) {
+ const upb_msgdef *m = lupb_msgdef_check(L, 1);
+ lua_pushinteger(L, upb_msgdef_numfields(m));
+ return 1;
+}
+
+static int lupb_msgdef_field(lua_State *L) {
+ const upb_msgdef *m = lupb_msgdef_check(L, 1);
+ int type = lua_type(L, 2);
+ const upb_fielddef *f;
+ if (type == LUA_TNUMBER) {
+ f = upb_msgdef_itof(m, lua_tointeger(L, 2));
+ } else if (type == LUA_TSTRING) {
+ f = upb_msgdef_ntofz(m, lua_tostring(L, 2));
+ } else {
+ const char *msg = lua_pushfstring(L, "number or string expected, got %s",
+ luaL_typename(L, 2));
+ return luaL_argerror(L, 2, msg);
+ }
+
+ lupb_def_pushwrapper(L, upb_fielddef_upcast(f), NULL);
+ return 1;
+}
+
+static int lupb_msgdef_lookupname(lua_State *L) {
+ const upb_msgdef *m = lupb_msgdef_check(L, 1);
+ const upb_fielddef *f;
+ const upb_oneofdef *o;
+ if (!upb_msgdef_lookupnamez(m, lua_tostring(L, 2), &f, &o)) {
+ lua_pushnil(L);
+ } else if (o) {
+ lupb_oneofdef_pushwrapper(L, o, NULL);
+ } else {
+ lupb_fielddef_pushwrapper(L, f, NULL);
+ }
+ return 1;
+}
+
+static int lupb_msgfielditer_next(lua_State *L) {
+ upb_msg_field_iter *i = lua_touserdata(L, lua_upvalueindex(1));
+ if (upb_msg_field_done(i)) return 0;
+ lupb_def_pushwrapper(L, upb_fielddef_upcast(upb_msg_iter_field(i)), NULL);
+ upb_msg_field_next(i);
+ return 1;
+}
+
+static int lupb_msgdef_fields(lua_State *L) {
+ const upb_msgdef *m = lupb_msgdef_check(L, 1);
+ upb_msg_field_iter *i = lua_newuserdata(L, sizeof(upb_msg_field_iter));
+ upb_msg_field_begin(i, m);
+ /* Need to guarantee that the msgdef outlives the iter. */
+ lua_pushvalue(L, 1);
+ lua_pushcclosure(L, &lupb_msgfielditer_next, 2);
+ return 1;
+}
+
+static int lupb_msgoneofiter_next(lua_State *L) {
+ upb_msg_oneof_iter *i = lua_touserdata(L, lua_upvalueindex(1));
+ if (upb_msg_oneof_done(i)) return 0;
+ lupb_oneofdef_pushwrapper(L, upb_msg_iter_oneof(i), NULL);
+ upb_msg_oneof_next(i);
+ return 1;
+}
+
+static int lupb_msgdef_oneofs(lua_State *L) {
+ const upb_msgdef *m = lupb_msgdef_check(L, 1);
+ upb_msg_oneof_iter *i = lua_newuserdata(L, sizeof(upb_msg_oneof_iter));
+ upb_msg_oneof_begin(i, m);
+ /* Need to guarantee that the msgdef outlives the iter. */
+ lua_pushvalue(L, 1);
+ lua_pushcclosure(L, &lupb_msgoneofiter_next, 2);
+ return 1;
+}
+
+static int lupb_msgdef_mapentry(lua_State *L) {
+ const upb_msgdef *m = lupb_msgdef_check(L, 1);
+ lua_pushboolean(L, upb_msgdef_mapentry(m));
+ return 1;
+}
+
+static int lupb_msgdef_syntax(lua_State *L) {
+ const upb_msgdef *m = lupb_msgdef_check(L, 1);
+ lua_pushinteger(L, upb_msgdef_syntax(m));
+ return 1;
+}
+
+static const struct luaL_Reg lupb_msgdef_mm[] = {
+ {"__len", lupb_msgdef_len},
+ {"__gc", lupb_refcounted_gc},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg lupb_msgdef_m[] = {
+ LUPB_COMMON_DEF_METHODS
+ {"add", lupb_msgdef_add},
+ {"field", lupb_msgdef_field},
+ {"fields", lupb_msgdef_fields},
+ {"lookup_name", lupb_msgdef_lookupname},
+ {"oneofs", lupb_msgdef_oneofs},
+ {"syntax", lupb_msgdef_syntax},
+ {"_map_entry", lupb_msgdef_mapentry},
+
+ {NULL, NULL}
+};
+
+
+/* lupb_enumdef ***************************************************************/
+
+const upb_enumdef *lupb_enumdef_check(lua_State *L, int narg) {
+ return lupb_refcounted_check(L, narg, LUPB_ENUMDEF);
+}
+
+static upb_enumdef *lupb_enumdef_checkmutable(lua_State *L, int narg) {
+ const upb_enumdef *f = lupb_enumdef_check(L, narg);
+ if (upb_enumdef_isfrozen(f))
+ luaL_error(L, "not allowed on frozen value");
+ return (upb_enumdef*)f;
+}
+
+static int lupb_enumdef_new(lua_State *L) {
+ upb_enumdef *e = upb_enumdef_new(&e);
+ lupb_def_pushnewrapper(L, upb_enumdef_upcast(e), &e);
+ return 1;
+}
+
+static int lupb_enumdef_add(lua_State *L) {
+ upb_enumdef *e = lupb_enumdef_checkmutable(L, 1);
+ const char *name = lupb_checkname(L, 2);
+ int32_t val = lupb_checkint32(L, 3);
+ CHK(upb_enumdef_addval(e, name, val, &status));
+ return 0;
+}
+
+static int lupb_enumdef_len(lua_State *L) {
+ const upb_enumdef *e = lupb_enumdef_check(L, 1);
+ lua_pushinteger(L, upb_enumdef_numvals(e));
+ return 1;
+}
+
+static int lupb_enumdef_value(lua_State *L) {
+ const upb_enumdef *e = lupb_enumdef_check(L, 1);
+ int type = lua_type(L, 2);
+ if (type == LUA_TNUMBER) {
+ /* Pushes "nil" for a NULL pointer. */
+ int32_t key = lupb_checkint32(L, 2);
+ lua_pushstring(L, upb_enumdef_iton(e, key));
+ } else if (type == LUA_TSTRING) {
+ const char *key = lua_tostring(L, 2);
+ int32_t num;
+ if (upb_enumdef_ntoiz(e, key, &num)) {
+ lua_pushinteger(L, num);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ const char *msg = lua_pushfstring(L, "number or string expected, got %s",
+ luaL_typename(L, 2));
+ return luaL_argerror(L, 2, msg);
+ }
+ return 1;
+}
+
+static int lupb_enumiter_next(lua_State *L) {
+ upb_enum_iter *i = lua_touserdata(L, lua_upvalueindex(1));
+ if (upb_enum_done(i)) return 0;
+ lua_pushstring(L, upb_enum_iter_name(i));
+ lua_pushinteger(L, upb_enum_iter_number(i));
+ upb_enum_next(i);
+ return 2;
+}
+
+static int lupb_enumdef_values(lua_State *L) {
+ const upb_enumdef *e = lupb_enumdef_check(L, 1);
+ upb_enum_iter *i = lua_newuserdata(L, sizeof(upb_enum_iter));
+ upb_enum_begin(i, e);
+ /* Need to guarantee that the enumdef outlives the iter. */
+ lua_pushvalue(L, 1);
+ lua_pushcclosure(L, &lupb_enumiter_next, 2);
+ return 1;
+}
+
+static const struct luaL_Reg lupb_enumdef_mm[] = {
+ {"__gc", lupb_refcounted_gc},
+ {"__len", lupb_enumdef_len},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg lupb_enumdef_m[] = {
+ LUPB_COMMON_DEF_METHODS
+ {"add", lupb_enumdef_add},
+ {"value", lupb_enumdef_value},
+ {"values", lupb_enumdef_values},
+ {NULL, NULL}
+};
+
+
+/* lupb_filedef ***************************************************************/
+
+void lupb_filedef_pushwrapper(lua_State *L, const upb_filedef *f,
+ const void *ref_donor) {
+ lupb_refcounted_pushwrapper(L, upb_filedef_upcast(f), LUPB_FILEDEF, ref_donor,
+ sizeof(void *));
+}
+
+void lupb_filedef_pushnewrapper(lua_State *L, const upb_filedef *f,
+ const void *ref_donor) {
+ lupb_refcounted_pushnewrapper(L, upb_filedef_upcast(f), LUPB_FILEDEF,
+ ref_donor);
+}
+
+const upb_filedef *lupb_filedef_check(lua_State *L, int narg) {
+ return lupb_refcounted_check(L, narg, LUPB_FILEDEF);
+}
+
+static upb_filedef *lupb_filedef_checkmutable(lua_State *L, int narg) {
+ const upb_filedef *f = lupb_filedef_check(L, narg);
+ if (upb_filedef_isfrozen(f))
+ luaL_error(L, "not allowed on frozen value");
+ return (upb_filedef*)f;
+}
+
+static int lupb_filedef_new(lua_State *L) {
+ upb_filedef *f = upb_filedef_new(&f);
+ lupb_filedef_pushnewrapper(L, f, &f);
+ return 1;
+}
+
+static int lupb_filedef_def(lua_State *L) {
+ const upb_filedef *f = lupb_filedef_check(L, 1);
+ int index = luaL_checkint(L, 2);
+ const upb_def *def = upb_filedef_def(f, index);
+
+ if (!def) {
+ return luaL_error(L, "index out of range");
+ }
+
+ lupb_def_pushwrapper(L, def, NULL);
+ return 1;
+}
+
+static int lupb_filedef_name(lua_State *L) {
+ const upb_filedef *f = lupb_filedef_check(L, 1);
+ lua_pushstring(L, upb_filedef_name(f));
+ return 1;
+}
+
+static int lupb_filedef_package(lua_State *L) {
+ const upb_filedef *f = lupb_filedef_check(L, 1);
+ lua_pushstring(L, upb_filedef_package(f));
+ return 1;
+}
+
+static int lupb_filedef_len(lua_State *L) {
+ const upb_filedef *f = lupb_filedef_check(L, 1);
+ lua_pushinteger(L, upb_filedef_defcount(f));
+ return 1;
+}
+
+static int lupb_filedef_setname(lua_State *L) {
+ upb_filedef *f = lupb_filedef_checkmutable(L, 1);
+ CHK(upb_filedef_setname(f, lupb_checkname(L, 2), &status));
+ return 0;
+}
+
+static int lupb_filedef_setpackage(lua_State *L) {
+ upb_filedef *f = lupb_filedef_checkmutable(L, 1);
+ CHK(upb_filedef_setpackage(f, lupb_checkname(L, 2), &status));
+ return 0;
+}
+
+static int lupb_filedefiter_next(lua_State *L) {
+ const upb_filedef *f = lupb_filedef_check(L, lua_upvalueindex(1));
+ int type = lua_tointeger(L, lua_upvalueindex(2));
+ size_t i = lua_tointeger(L, lua_upvalueindex(3));
+ size_t n = upb_filedef_defcount(f);
+
+ for (; i < n; i++) {
+ const upb_def *def;
+
+ def = upb_filedef_def(f, i);
+ UPB_ASSERT(def);
+
+ if (type == UPB_DEF_ANY || upb_def_type(def) == type) {
+ lua_pushinteger(L, i + 1);
+ lua_replace(L, lua_upvalueindex(3));
+ lupb_def_pushwrapper(L, def, NULL);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int lupb_filedef_defs(lua_State *L) {
+ lupb_filedef_check(L, 1);
+ luaL_checkinteger(L, 2); /* Def type. Could make this optional. */
+ lua_pushnumber(L, 0); /* Index, starts at zero. */
+ lua_pushcclosure(L, &lupb_filedefiter_next, 3);
+ return 1;
+}
+
+static const struct luaL_Reg lupb_filedef_mm[] = {
+ {"__gc", lupb_refcounted_gc},
+ {"__len", lupb_filedef_len},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg lupb_filedef_m[] = {
+ {"def", lupb_filedef_def},
+ {"defs", lupb_filedef_defs},
+ {"name", lupb_filedef_name},
+ {"package", lupb_filedef_package},
+
+ {"set_name", lupb_filedef_setname},
+ {"set_package", lupb_filedef_setpackage},
+
+ {NULL, NULL}
+};
+
+
+/* lupb_symtab ****************************************************************/
+
+/* Inherits a ref on the symtab.
+ * Checks that narg is a proper lupb_symtab object. If it is, leaves its
+ * metatable on the stack for cache lookups/updates. */
+const upb_symtab *lupb_symtab_check(lua_State *L, int narg) {
+ return lupb_refcounted_check(L, narg, LUPB_SYMTAB);
+}
+
+static upb_symtab *lupb_symtab_checkmutable(lua_State *L, int narg) {
+ const upb_symtab *s = lupb_symtab_check(L, narg);
+ if (upb_symtab_isfrozen(s))
+ luaL_error(L, "not allowed on frozen value");
+ return (upb_symtab*)s;
+}
+
+void lupb_symtab_pushwrapper(lua_State *L, const upb_symtab *s,
+ const void *ref_donor) {
+ lupb_refcounted_pushwrapper(L, upb_symtab_upcast(s), LUPB_SYMTAB, ref_donor,
+ sizeof(void *));
+}
+
+void lupb_symtab_pushnewrapper(lua_State *L, const upb_symtab *s,
+ const void *ref_donor) {
+ lupb_refcounted_pushnewrapper(L, upb_symtab_upcast(s), LUPB_SYMTAB,
+ ref_donor);
+}
+
+static int lupb_symtab_new(lua_State *L) {
+ upb_symtab *s = upb_symtab_new(&s);
+ lupb_symtab_pushnewrapper(L, s, &s);
+ return 1;
+}
+
+static int lupb_symtab_freeze(lua_State *L) {
+ upb_symtab_freeze(lupb_symtab_checkmutable(L, 1));
+ return 0;
+}
+
+static int lupb_symtab_isfrozen(lua_State *L) {
+ lua_pushboolean(L, upb_symtab_isfrozen(lupb_symtab_check(L, 1)));
+ return 1;
+}
+
+static int lupb_symtab_add(lua_State *L) {
+ upb_symtab *s = lupb_symtab_checkmutable(L, 1);
+ int n;
+ upb_def **defs;
+
+ luaL_checktype(L, 2, LUA_TTABLE);
+ /* Iterate over table twice. First iteration to count entries and
+ * check constraints. */
+ n = 0;
+ for (lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) {
+ lupb_def_checkmutable(L, -1);
+ ++n;
+ }
+
+ /* Second iteration to build deflist.
+ * Allocate list with lua_newuserdata() so it is anchored as a GC root in
+ * case any Lua functions longjmp(). */
+ defs = lua_newuserdata(L, n * sizeof(*defs));
+ n = 0;
+ for (lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) {
+ upb_def *def = lupb_def_checkmutable(L, -1);
+ defs[n++] = def;
+ }
+
+ CHK(upb_symtab_add(s, defs, n, NULL, &status));
+ return 0;
+}
+
+static int lupb_symtab_addfile(lua_State *L) {
+ upb_symtab *s = lupb_symtab_checkmutable(L, 1);
+ upb_filedef *f = lupb_filedef_checkmutable(L, 2);
+ CHK(upb_symtab_addfile(s, f, &status));
+ return 0;
+}
+
+static int lupb_symtab_lookup(lua_State *L) {
+ const upb_symtab *s = lupb_symtab_check(L, 1);
+ int i;
+ for (i = 2; i <= lua_gettop(L); i++) {
+ const upb_def *def = upb_symtab_lookup(s, luaL_checkstring(L, i));
+ lupb_def_pushwrapper(L, def, NULL);
+ lua_replace(L, i);
+ }
+ return lua_gettop(L) - 1;
+}
+
+static int lupb_symtabiter_next(lua_State *L) {
+ upb_symtab_iter *i = lua_touserdata(L, lua_upvalueindex(1));
+ if (upb_symtab_done(i)) return 0;
+ lupb_def_pushwrapper(L, upb_symtab_iter_def(i), NULL);
+ upb_symtab_next(i);
+ return 1;
+}
+
+static int lupb_symtab_defs(lua_State *L) {
+ const upb_symtab *s = lupb_symtab_check(L, 1);
+ upb_deftype_t type = lua_gettop(L) > 1 ? luaL_checkint(L, 2) : UPB_DEF_ANY;
+ upb_symtab_iter *i = lua_newuserdata(L, sizeof(upb_symtab_iter));
+ upb_symtab_begin(i, s, type);
+ /* Need to guarantee that the symtab outlives the iter. */
+ lua_pushvalue(L, 1);
+ lua_pushcclosure(L, &lupb_symtabiter_next, 2);
+ return 1;
+}
+
+static const struct luaL_Reg lupb_symtab_m[] = {
+ {"add", lupb_symtab_add},
+ {"add_file", lupb_symtab_addfile},
+ {"defs", lupb_symtab_defs},
+ {"freeze", lupb_symtab_freeze},
+ {"is_frozen", lupb_symtab_isfrozen},
+ {"lookup", lupb_symtab_lookup},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg lupb_symtab_mm[] = {
+ {"__gc", lupb_refcounted_gc},
+ {NULL, NULL}
+};
+
+/* lupb toplevel **************************************************************/
+
+static int lupb_freeze(lua_State *L) {
+ int n = lua_gettop(L);
+ int i;
+ /* Scratch memory; lua_newuserdata() anchors it as a GC root in case any Lua
+ * functions fail. */
+ upb_def **defs = lua_newuserdata(L, n * sizeof(upb_def*));
+
+ for (i = 0; i < n; i++) {
+ /* Could allow an array of defs here also. */
+ defs[i] = lupb_def_checkmutable(L, i + 1);
+ }
+ CHK(upb_def_freeze(defs, n, &status));
+ return 0;
+}
+
+/* This is a *temporary* API that will be removed once pending refactorings are
+ * complete (it does not belong here in core because it depends on both
+ * the descriptor.proto schema and the protobuf binary format. */
+static int lupb_loaddescriptor(lua_State *L) {
+ size_t len;
+ const char *str = luaL_checklstring(L, 1, &len);
+ size_t i;
+ upb_filedef **files = NULL;
+ CHK(files = upb_loaddescriptor(str, len, &files, &status));
+
+ lua_newtable(L);
+ for (i = 1; *files; i++, files++) {
+ lupb_filedef_pushnewrapper(L, *files, &files);
+ lua_rawseti(L, -2, i);
+ }
+
+ return 1;
+}
+
+static void lupb_setfieldi(lua_State *L, const char *field, int i) {
+ lua_pushinteger(L, i);
+ lua_setfield(L, -2, field);
+}
+
+static const struct luaL_Reg lupbdef_toplevel_m[] = {
+ {"EnumDef", lupb_enumdef_new},
+ {"FieldDef", lupb_fielddef_new},
+ {"FileDef", lupb_filedef_new},
+ {"MessageDef", lupb_msgdef_new},
+ {"OneofDef", lupb_oneofdef_new},
+ {"SymbolTable", lupb_symtab_new},
+ {"freeze", lupb_freeze},
+ {"load_descriptor", lupb_loaddescriptor},
+
+ {NULL, NULL}
+};
+
+void lupb_def_registertypes(lua_State *L) {
+ lupb_setfuncs(L, lupbdef_toplevel_m);
+
+ /* Refcounted types. */
+ lupb_register_type(L, LUPB_ENUMDEF, lupb_enumdef_m, lupb_enumdef_mm);
+ lupb_register_type(L, LUPB_FIELDDEF, lupb_fielddef_m, lupb_fielddef_mm);
+ lupb_register_type(L, LUPB_FILEDEF, lupb_filedef_m, lupb_filedef_mm);
+ lupb_register_type(L, LUPB_MSGDEF, lupb_msgdef_m, lupb_msgdef_mm);
+ lupb_register_type(L, LUPB_ONEOFDEF, lupb_oneofdef_m, lupb_oneofdef_mm);
+ lupb_register_type(L, LUPB_SYMTAB, lupb_symtab_m, lupb_symtab_mm);
+
+ /* Create our object cache. */
+ lua_newtable(L);
+ lua_createtable(L, 0, 1); /* Cache metatable. */
+ lua_pushstring(L, "v"); /* Values are weak. */
+ lua_setfield(L, -2, "__mode");
+ lua_setmetatable(L, -2);
+ lua_setfield(L, LUA_REGISTRYINDEX, LUPB_OBJCACHE);
+
+ /* Register constants. */
+ lupb_setfieldi(L, "LABEL_OPTIONAL", UPB_LABEL_OPTIONAL);
+ lupb_setfieldi(L, "LABEL_REQUIRED", UPB_LABEL_REQUIRED);
+ lupb_setfieldi(L, "LABEL_REPEATED", UPB_LABEL_REPEATED);
+
+ lupb_setfieldi(L, "TYPE_DOUBLE", UPB_TYPE_DOUBLE);
+ lupb_setfieldi(L, "TYPE_FLOAT", UPB_TYPE_FLOAT);
+ lupb_setfieldi(L, "TYPE_INT64", UPB_TYPE_INT64);
+ lupb_setfieldi(L, "TYPE_UINT64", UPB_TYPE_UINT64);
+ lupb_setfieldi(L, "TYPE_INT32", UPB_TYPE_INT32);
+ lupb_setfieldi(L, "TYPE_BOOL", UPB_TYPE_BOOL);
+ lupb_setfieldi(L, "TYPE_STRING", UPB_TYPE_STRING);
+ lupb_setfieldi(L, "TYPE_MESSAGE", UPB_TYPE_MESSAGE);
+ lupb_setfieldi(L, "TYPE_BYTES", UPB_TYPE_BYTES);
+ lupb_setfieldi(L, "TYPE_UINT32", UPB_TYPE_UINT32);
+ lupb_setfieldi(L, "TYPE_ENUM", UPB_TYPE_ENUM);
+
+ lupb_setfieldi(L, "INTFMT_VARIABLE", UPB_INTFMT_VARIABLE);
+ lupb_setfieldi(L, "INTFMT_FIXED", UPB_INTFMT_FIXED);
+ lupb_setfieldi(L, "INTFMT_ZIGZAG", UPB_INTFMT_ZIGZAG);
+
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_DOUBLE", UPB_DESCRIPTOR_TYPE_DOUBLE);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_FLOAT", UPB_DESCRIPTOR_TYPE_FLOAT);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_INT64", UPB_DESCRIPTOR_TYPE_INT64);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_UINT64", UPB_DESCRIPTOR_TYPE_UINT64);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_INT32", UPB_DESCRIPTOR_TYPE_INT32);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_FIXED64", UPB_DESCRIPTOR_TYPE_FIXED64);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_FIXED32", UPB_DESCRIPTOR_TYPE_FIXED32);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_BOOL", UPB_DESCRIPTOR_TYPE_BOOL);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_STRING", UPB_DESCRIPTOR_TYPE_STRING);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_GROUP", UPB_DESCRIPTOR_TYPE_GROUP);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_MESSAGE", UPB_DESCRIPTOR_TYPE_MESSAGE);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_BYTES", UPB_DESCRIPTOR_TYPE_BYTES);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_UINT32", UPB_DESCRIPTOR_TYPE_UINT32);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_ENUM", UPB_DESCRIPTOR_TYPE_ENUM);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_SFIXED32", UPB_DESCRIPTOR_TYPE_SFIXED32);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_SFIXED64", UPB_DESCRIPTOR_TYPE_SFIXED64);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_SINT32", UPB_DESCRIPTOR_TYPE_SINT32);
+ lupb_setfieldi(L, "DESCRIPTOR_TYPE_SINT64", UPB_DESCRIPTOR_TYPE_SINT64);
+
+ lupb_setfieldi(L, "DEF_MSG", UPB_DEF_MSG);
+ lupb_setfieldi(L, "DEF_FIELD", UPB_DEF_FIELD);
+ lupb_setfieldi(L, "DEF_ENUM", UPB_DEF_ENUM);
+ lupb_setfieldi(L, "DEF_SERVICE", UPB_DEF_SERVICE);
+ lupb_setfieldi(L, "DEF_ANY", UPB_DEF_ANY);
+
+ lupb_setfieldi(L, "HANDLER_INT32", UPB_HANDLER_INT32);
+ lupb_setfieldi(L, "HANDLER_INT64", UPB_HANDLER_INT64);
+ lupb_setfieldi(L, "HANDLER_UINT32", UPB_HANDLER_UINT32);
+ lupb_setfieldi(L, "HANDLER_UINT64", UPB_HANDLER_UINT64);
+ lupb_setfieldi(L, "HANDLER_FLOAT", UPB_HANDLER_FLOAT);
+ lupb_setfieldi(L, "HANDLER_DOUBLE", UPB_HANDLER_DOUBLE);
+ lupb_setfieldi(L, "HANDLER_BOOL", UPB_HANDLER_BOOL);
+ lupb_setfieldi(L, "HANDLER_STARTSTR", UPB_HANDLER_STARTSTR);
+ lupb_setfieldi(L, "HANDLER_STRING", UPB_HANDLER_STRING);
+ lupb_setfieldi(L, "HANDLER_ENDSTR", UPB_HANDLER_ENDSTR);
+ lupb_setfieldi(L, "HANDLER_STARTSUBMSG", UPB_HANDLER_STARTSUBMSG);
+ lupb_setfieldi(L, "HANDLER_ENDSUBMSG", UPB_HANDLER_ENDSUBMSG);
+ lupb_setfieldi(L, "HANDLER_STARTSEQ", UPB_HANDLER_STARTSEQ);
+ lupb_setfieldi(L, "HANDLER_ENDSEQ", UPB_HANDLER_ENDSEQ);
+
+ lupb_setfieldi(L, "SYNTAX_PROTO2", UPB_SYNTAX_PROTO2);
+ lupb_setfieldi(L, "SYNTAX_PROTO3", UPB_SYNTAX_PROTO3);
+}
diff --git a/upb/bindings/lua/msg.c b/upb/bindings/lua/msg.c
new file mode 100644
index 0000000..6783b23
--- /dev/null
+++ b/upb/bindings/lua/msg.c
@@ -0,0 +1,1073 @@
+/*
+** lupb_msg -- Message/Array/Map objects in Lua/C that wrap upb/msg.h
+*/
+
+#include <float.h>
+#include <math.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lauxlib.h"
+#include "upb/bindings/lua/upb.h"
+#include "upb/handlers.h"
+#include "upb/msg.h"
+
+/*
+ * Message/Array/Map objects can be constructed in one of two ways:
+ *
+ * 1. To point to existing msg/array/map data inside an arena.
+ * 2. To create and uniquely own some brand new data.
+ *
+ * Case (1) is for when we've parsed some data into an arena (which is faster
+ * than parsing directly into Lua objects) or when we're pointing at some
+ * read-only data (like custom options in a def).
+ *
+ * Case (2) is for when a user creates the object directly in Lua.
+ *
+ * We use the userval of container objects (Message/Array/Map) to store
+ * references to sub-objects (Strings/Messages/Arrays/Maps). But we need to
+ * keep the userval in sync with the underlying upb_msg/upb_array/upb_map.
+ * We populate the userval lazily from the underlying data.
+ *
+ * This means that no one may remove/replace any String/Message/Array/Map
+ * field/entry in the underlying upb_{msg,array,map} behind our back. It's ok
+ * for entries to be added or for primitives to be modified, but *replacing*
+ * sub-containers is not.
+ *
+ * Luckily parse/merge follow this rule. However clear does not, so it's not
+ * safe to clear behind our back.
+ */
+
+#define LUPB_ARENA "lupb.arena"
+
+#define LUPB_MSGCLASS "lupb.msgclass"
+#define LUPB_MSGFACTORY "lupb.msgfactory"
+
+#define LUPB_ARRAY "lupb.array"
+#define LUPB_MAP "lupb.map"
+#define LUPB_MSG "lupb.msg"
+#define LUPB_STRING "lupb.string"
+
+static int lupb_msg_pushnew(lua_State *L, int narg);
+
+/* Lazily creates the uservalue if it doesn't exist. */
+static void lupb_getuservalue(lua_State *L, int index) {
+ lua_getuservalue(L, index);
+ if (lua_isnil(L, -1)) {
+ /* Lazily create and set userval. */
+ lua_pop(L, 1); /* nil. */
+ lua_pushvalue(L, index); /* userdata copy. */
+ lua_newtable(L);
+ lua_setuservalue(L, -2);
+ lua_pop(L, 1); /* userdata copy. */
+ lua_getuservalue(L, index);
+ }
+ assert(!lua_isnil(L, -1));
+}
+
+static void lupb_uservalseti(lua_State *L, int userdata, int index, int val) {
+ lupb_getuservalue(L, userdata);
+ lua_pushvalue(L, val);
+ lua_rawseti(L, -2, index);
+ lua_pop(L, 1); /* Uservalue. */
+}
+
+static void lupb_uservalgeti(lua_State *L, int userdata, int index) {
+ lupb_getuservalue(L, userdata);
+ lua_rawgeti(L, -1, index);
+ lua_insert(L, -2);
+ lua_pop(L, 1); /* Uservalue. */
+}
+
+/* Pushes a new userdata with the given metatable. */
+static void *lupb_newuserdata(lua_State *L, size_t size, const char *type) {
+ void *ret = lua_newuserdata(L, size);
+
+ /* Set metatable. */
+ luaL_getmetatable(L, type);
+ UPB_ASSERT(!lua_isnil(L, -1)); /* Should have been created by luaopen_upb. */
+ lua_setmetatable(L, -2);
+
+ /* We don't set a uservalue here -- we lazily create it later if necessary. */
+
+ return ret;
+}
+
+
+/* lupb_alloc *****************************************************************/
+
+typedef struct {
+ upb_alloc alloc;
+ lua_State *L;
+} lupb_alloc;
+
+char lupb_alloc_cache_key;
+
+static void *lupb_alloc_func(upb_alloc *alloc, void *ptr, size_t oldsize,
+ size_t size) {
+ lupb_alloc *lalloc = (lupb_alloc*)alloc;
+ void *ud;
+
+ /* We read this every time in case the user changes the Lua alloc function. */
+ lua_Alloc func = lua_getallocf(lalloc->L, &ud);
+
+ return func(ud, ptr, oldsize, size);
+}
+
+static void lupb_alloc_pushnew(lua_State *L) {
+ lupb_alloc *lalloc = lua_newuserdata(L, sizeof(lupb_alloc));
+
+ lalloc->alloc.func = &lupb_alloc_func;
+ lalloc->L = L;
+}
+
+/* Returns the global lupb_alloc func that was created in our luaopen().
+ * Callers can be guaranteed that it will be alive as long as |L| is. */
+static upb_alloc *lupb_alloc_get(lua_State *L) {
+ lupb_alloc *lalloc;
+
+ lua_pushlightuserdata(L, &lupb_alloc_cache_key);
+ lua_gettable(L, LUA_REGISTRYINDEX);
+ lalloc = lua_touserdata(L, -1);
+ UPB_ASSERT(lalloc);
+ lua_pop(L, 1);
+
+ return &lalloc->alloc;
+}
+
+static void lupb_alloc_initsingleton(lua_State *L) {
+ lua_pushlightuserdata(L, &lupb_alloc_cache_key);
+ lupb_alloc_pushnew(L);
+ lua_settable(L, LUA_REGISTRYINDEX);
+}
+
+#define ADD_BYTES(ptr, bytes) ((void*)((char*)ptr + bytes))
+
+
+/* lupb_arena *****************************************************************/
+
+/* lupb_arena only exists to wrap a upb_arena. It is never exposed to users;
+ * it is an internal memory management detail. Other objects refer to this
+ * object from their userdata to keep the arena-owned data alive. */
+
+upb_arena *lupb_arena_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_ARENA);
+}
+
+int lupb_arena_new(lua_State *L) {
+ upb_arena *a = lupb_newuserdata(L, sizeof(upb_arena), LUPB_ARENA);
+
+ /* TODO(haberman): use Lua alloc func as block allocator? Would need to
+ * verify that all cases of upb_malloc in msg/table are longjmp-safe. */
+ upb_arena_init(a);
+
+ return 1;
+}
+
+static int lupb_arena_gc(lua_State *L) {
+ upb_arena *a = lupb_arena_check(L, 1);
+ upb_arena_uninit(a);
+ return 0;
+}
+
+static const struct luaL_Reg lupb_arena_mm[] = {
+ {"__gc", lupb_arena_gc},
+ {NULL, NULL}
+};
+
+
+/* lupb_msgfactory ************************************************************/
+
+/* Userval contains a map of:
+ * [1] -> SymbolTable (to keep GC-reachable)
+ * [const upb_msgdef*] -> [lupb_msgclass userdata]
+ */
+
+#define LUPB_MSGFACTORY_SYMTAB 1
+
+typedef struct lupb_msgfactory {
+ upb_msgfactory *factory;
+} lupb_msgfactory;
+
+static int lupb_msgclass_pushnew(lua_State *L, int factory, const upb_msglayout *l);
+
+/* lupb_msgfactory helpers. */
+
+static lupb_msgfactory *lupb_msgfactory_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_MSGFACTORY);
+}
+
+static void lupb_msgfactory_pushmsgclass(lua_State *L, int narg,
+ const upb_msgdef *md) {
+ const lupb_msgfactory *lfactory = lupb_msgfactory_check(L, narg);
+
+ lupb_getuservalue(L, narg);
+ lua_pushlightuserdata(L, (void*)md);
+ lua_rawget(L, -2);
+
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1);
+ /* TODO: verify md is in symtab? */
+ lupb_msgclass_pushnew(L, narg,
+ upb_msgfactory_getlayout(lfactory->factory, md));
+
+ /* Set in userval. */
+ lua_pushlightuserdata(L, (void*)md);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, -4);
+ }
+}
+
+static int lupb_msgfactory_gc(lua_State *L) {
+ lupb_msgfactory *lfactory = lupb_msgfactory_check(L, 1);
+
+ if (lfactory->factory) {
+ upb_msgfactory_free(lfactory->factory);
+ lfactory->factory = NULL;
+ }
+
+ return 0;
+}
+
+/* lupb_msgfactory Public API. */
+
+/**
+ * lupb_msgfactory_new()
+ *
+ * Handles:
+ * msgfactory = upb.MessageFactory(symtab)
+ *
+ * Creates a new, empty MessageFactory for the given SymbolTable.
+ * Message classes will be created on demand when the user calls
+ * msgfactory.get_message_class().
+ */
+static int lupb_msgfactory_new(lua_State *L) {
+ const upb_symtab *symtab = lupb_symtab_check(L, 1);
+
+ lupb_msgfactory *lmsgfactory =
+ lupb_newuserdata(L, sizeof(lupb_msgfactory), LUPB_MSGFACTORY);
+ lmsgfactory->factory = upb_msgfactory_new(symtab);
+ lupb_uservalseti(L, -1, LUPB_MSGFACTORY_SYMTAB, 1);
+
+ return 1;
+}
+
+/**
+ * lupb_msgfactory_getmsgclass()
+ *
+ * Handles:
+ * MessageClass = factory.get_message_class(msgdef)
+ */
+static int lupb_msgfactory_getmsgclass(lua_State *L) {
+ lupb_msgfactory_pushmsgclass(L, 1, lupb_msgdef_check(L, 2));
+ return 1;
+}
+
+static const struct luaL_Reg lupb_msgfactory_m[] = {
+ {"get_message_class", lupb_msgfactory_getmsgclass},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg lupb_msgfactory_mm[] = {
+ {"__gc", lupb_msgfactory_gc},
+ {NULL, NULL}
+};
+
+
+/* lupb_msgclass **************************************************************/
+
+#define LUPB_MSGCLASS_FACTORY 1
+#define LUPB_MSGCLASS_MSGDEF 2
+
+struct lupb_msgclass {
+ const upb_msglayout *layout;
+ const lupb_msgfactory *lfactory;
+};
+
+/* Checks that the given object is a lupb_msg with the given lmsgclass. */
+static upb_msgval lupb_msg_typecheck(lua_State *L, int narg,
+ const lupb_msgclass *lmsgclass);
+
+/* Type-checks for assigning to a message field. */
+static upb_msgval lupb_array_typecheck(lua_State *L, int narg, int msg,
+ const upb_fielddef *f);
+static upb_msgval lupb_map_typecheck(lua_State *L, int narg, int msg,
+ const upb_fielddef *f);
+static const lupb_msgclass *lupb_msg_getsubmsgclass(lua_State *L, int narg,
+ const upb_fielddef *f);
+static const lupb_msgclass *lupb_msg_msgclassfor(lua_State *L, int narg,
+ const upb_msgdef *md);
+
+lupb_msgclass *lupb_msgclass_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_MSGCLASS);
+}
+
+const upb_msglayout *lupb_msgclass_getlayout(lua_State *L, int narg) {
+ return lupb_msgclass_check(L, narg)->layout;
+}
+
+const upb_handlers *lupb_msgclass_getmergehandlers(lua_State *L, int narg) {
+ lupb_msgclass *lmsgclass = lupb_msgclass_check(L, narg);
+ return upb_msgfactory_getmergehandlers(
+ lmsgclass->lfactory->factory, upb_msglayout_msgdef(lmsgclass->layout));
+}
+
+/**
+ * lupb_msgclass_typecheck()
+ *
+ * Verifies that the expected msgclass matches the actual. If not, raises a Lua
+ * error.
+ */
+static void lupb_msgclass_typecheck(lua_State *L, const lupb_msgclass *expected,
+ const lupb_msgclass *actual) {
+ if (expected != actual) {
+ luaL_error(L, "Message had incorrect type, expected '%s', got '%s'",
+ upb_msgdef_fullname(upb_msglayout_msgdef(expected->layout)),
+ upb_msgdef_fullname(upb_msglayout_msgdef(actual->layout)));
+ }
+}
+
+static const lupb_msgclass *lupb_msgclass_msgclassfor(lua_State *L, int narg,
+ const upb_msgdef *md) {
+ lupb_uservalgeti(L, narg, LUPB_MSGCLASS_FACTORY);
+ lupb_msgfactory_pushmsgclass(L, -1, md);
+ return lupb_msgclass_check(L, -1);
+}
+
+/**
+ * lupb_msgclass_getsubmsgclass()
+ *
+ * Given a MessageClass at index |narg| and the submessage field |f|, returns
+ * the message class for this field.
+ *
+ * Currently we do a hash table lookup for this. If we wanted we could try to
+ * optimize this by caching these pointers in our msgclass, in an array indexed
+ * by field index. We would still need to fall back to calling msgclassfor(),
+ * unless we wanted to eagerly create message classes for all submessages. But
+ * for big schemas that might be a lot of things to build, and we might end up
+ * not using most of them. */
+static const lupb_msgclass *lupb_msgclass_getsubmsgclass(lua_State *L, int narg,
+ const upb_fielddef *f) {
+ if (upb_fielddef_type(f) != UPB_TYPE_MESSAGE) {
+ return NULL;
+ }
+
+ return lupb_msgclass_msgclassfor(L, narg, upb_fielddef_msgsubdef(f));
+}
+
+static int lupb_msgclass_pushnew(lua_State *L, int factory, const upb_msglayout *l) {
+ const lupb_msgfactory *lfactory = lupb_msgfactory_check(L, factory);
+ lupb_msgclass *lmc = lupb_newuserdata(L, sizeof(*lmc), LUPB_MSGCLASS);
+
+ UPB_ASSERT(l);
+ lupb_uservalseti(L, -1, LUPB_MSGCLASS_FACTORY, factory);
+ lmc->layout = l;
+ lmc->lfactory = lfactory;
+
+ return 1;
+}
+
+/* MessageClass Public API. */
+
+/**
+ * lupb_msgclass_call()
+ *
+ * Handles:
+ * msg = MessageClass()
+ *
+ * Creates a new message from the given MessageClass.
+ */
+static int lupb_msgclass_call(lua_State *L) {
+ lupb_msg_pushnew(L, 1);
+ return 1;
+}
+
+static const struct luaL_Reg lupb_msgclass_mm[] = {
+ {"__call", lupb_msgclass_call},
+ {NULL, NULL}
+};
+
+
+/* upb <-> Lua type conversion ************************************************/
+
+static bool lupb_istypewrapped(upb_fieldtype_t type) {
+ return type == UPB_TYPE_STRING || type == UPB_TYPE_BYTES ||
+ type == UPB_TYPE_MESSAGE;
+}
+
+static upb_msgval lupb_tomsgval(lua_State *L, upb_fieldtype_t type, int narg,
+ const lupb_msgclass *lmsgclass) {
+ switch (type) {
+ case UPB_TYPE_INT32:
+ case UPB_TYPE_ENUM:
+ return upb_msgval_int32(lupb_checkint32(L, narg));
+ case UPB_TYPE_INT64:
+ return upb_msgval_int64(lupb_checkint64(L, narg));
+ case UPB_TYPE_UINT32:
+ return upb_msgval_uint32(lupb_checkuint32(L, narg));
+ case UPB_TYPE_UINT64:
+ return upb_msgval_uint64(lupb_checkuint64(L, narg));
+ case UPB_TYPE_DOUBLE:
+ return upb_msgval_double(lupb_checkdouble(L, narg));
+ case UPB_TYPE_FLOAT:
+ return upb_msgval_float(lupb_checkfloat(L, narg));
+ case UPB_TYPE_BOOL:
+ return upb_msgval_bool(lupb_checkbool(L, narg));
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES: {
+ size_t len;
+ const char *ptr = lupb_checkstring(L, narg, &len);
+ return upb_msgval_str(ptr, len);
+ }
+ case UPB_TYPE_MESSAGE:
+ UPB_ASSERT(lmsgclass);
+ return lupb_msg_typecheck(L, narg, lmsgclass);
+ }
+ UPB_UNREACHABLE();
+}
+
+static void lupb_pushmsgval(lua_State *L, upb_fieldtype_t type,
+ upb_msgval val) {
+ switch (type) {
+ case UPB_TYPE_INT32:
+ case UPB_TYPE_ENUM:
+ lupb_pushint32(L, upb_msgval_getint32(val));
+ return;
+ case UPB_TYPE_INT64:
+ lupb_pushint64(L, upb_msgval_getint64(val));
+ return;
+ case UPB_TYPE_UINT32:
+ lupb_pushuint32(L, upb_msgval_getuint32(val));
+ return;
+ case UPB_TYPE_UINT64:
+ lupb_pushuint64(L, upb_msgval_getuint64(val));
+ return;
+ case UPB_TYPE_DOUBLE:
+ lupb_pushdouble(L, upb_msgval_getdouble(val));
+ return;
+ case UPB_TYPE_FLOAT:
+ lupb_pushfloat(L, upb_msgval_getfloat(val));
+ return;
+ case UPB_TYPE_BOOL:
+ lua_pushboolean(L, upb_msgval_getbool(val));
+ return;
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES:
+ case UPB_TYPE_MESSAGE:
+ break; /* Shouldn't call this function. */
+ }
+ UPB_UNREACHABLE();
+}
+
+
+/* lupb_array *****************************************************************/
+
+/* A strongly typed array. Implemented by wrapping upb_array.
+ *
+ * - we only allow integer indices.
+ * - all entries must have the correct type.
+ * - we do not allow "holes" in the array; you can only assign to an existing
+ * index or one past the end (which will grow the array by one).
+ *
+ * For string/submessage entries we keep in the userval:
+ *
+ * [number index] -> [lupb_string/lupb_msg userdata]
+ */
+
+typedef struct {
+ /* Only needed for array of message. This wastes space in the non-message
+ * case but simplifies the code. Could optimize away if desired. */
+ lupb_msgclass *lmsgclass;
+ upb_array *arr;
+} lupb_array;
+
+#define ARRAY_MSGCLASS_INDEX 0
+
+static lupb_array *lupb_array_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_ARRAY);
+}
+
+/**
+ * lupb_array_typecheck()
+ *
+ * Verifies that the lupb_array object at index |narg| can be safely assigned
+ * to the field |f| of the lupb_msg object at index |msg|. If this is safe,
+ * returns a upb_msgval representing the array. Otherwise, throws a Lua error.
+ */
+static upb_msgval lupb_array_typecheck(lua_State *L, int narg, int msg,
+ const upb_fielddef *f) {
+ lupb_array *larray = lupb_array_check(L, narg);
+
+ if (upb_array_type(larray->arr) != upb_fielddef_type(f) ||
+ lupb_msg_getsubmsgclass(L, msg, f) != larray->lmsgclass) {
+ luaL_error(L, "Array had incorrect type (expected: %d, got: %d)",
+ (int)upb_fielddef_type(f), (int)upb_array_type(larray->arr));
+ }
+
+ if (upb_array_type(larray->arr) == UPB_TYPE_MESSAGE) {
+ lupb_msgclass_typecheck(L, lupb_msg_getsubmsgclass(L, msg, f),
+ larray->lmsgclass);
+ }
+
+ return upb_msgval_arr(larray->arr);
+}
+
+/**
+ * lupb_array_checkindex()
+ *
+ * Checks the array index at Lua stack index |narg| to verify that it is an
+ * integer between 1 and |max|, inclusively. Also corrects it to be zero-based
+ * for C.
+ *
+ * We use "int" because of lua_rawseti/lua_rawgeti -- can re-evaluate if we want
+ * arrays bigger than 2^31.
+ */
+static int lupb_array_checkindex(lua_State *L, int narg, uint32_t max) {
+ uint32_t n = lupb_checkuint32(L, narg);
+ if (n == 0 || n > max || n > INT_MAX) {
+ luaL_error(L, "Invalid array index: expected between 1 and %d", (int)max);
+ }
+ return n - 1; /* Lua uses 1-based indexing. :( */
+}
+
+static int lupb_array_gc(lua_State *L) {
+ lupb_array *larray = lupb_array_check(L, 1);
+ upb_array_uninit(larray->arr);
+ return 0;
+}
+
+/* lupb_array Public API */
+
+static int lupb_array_new(lua_State *L) {
+ lupb_array *larray;
+ upb_fieldtype_t type;
+ lupb_msgclass *lmsgclass = NULL;
+
+ if (lua_type(L, 1) == LUA_TNUMBER) {
+ type = lupb_checkfieldtype(L, 1);
+ } else {
+ type = UPB_TYPE_MESSAGE;
+ lmsgclass = lupb_msgclass_check(L, 1);
+ lupb_uservalseti(L, -1, ARRAY_MSGCLASS_INDEX, 1); /* GC-root lmsgclass. */
+ }
+
+ larray =
+ lupb_newuserdata(L, sizeof(*larray) + upb_array_sizeof(type), LUPB_ARRAY);
+ larray->lmsgclass = lmsgclass;
+ larray->arr = ADD_BYTES(larray, sizeof(*larray));
+ upb_array_init(larray->arr, type, lupb_alloc_get(L));
+
+ return 1;
+}
+
+static int lupb_array_newindex(lua_State *L) {
+ lupb_array *larray = lupb_array_check(L, 1);
+ upb_fieldtype_t type = upb_array_type(larray->arr);
+ uint32_t n = lupb_array_checkindex(L, 2, upb_array_size(larray->arr) + 1);
+ upb_msgval msgval = lupb_tomsgval(L, type, 3, larray->lmsgclass);
+
+ upb_array_set(larray->arr, n, msgval);
+
+ if (lupb_istypewrapped(type)) {
+ lupb_uservalseti(L, 1, n, 3);
+ }
+
+ return 0; /* 1 for chained assignments? */
+}
+
+static int lupb_array_index(lua_State *L) {
+ lupb_array *larray = lupb_array_check(L, 1);
+ upb_array *array = larray->arr;
+ uint32_t n = lupb_array_checkindex(L, 2, upb_array_size(array));
+ upb_fieldtype_t type = upb_array_type(array);
+
+ if (lupb_istypewrapped(type)) {
+ lupb_uservalgeti(L, 1, n);
+ } else {
+ lupb_pushmsgval(L, upb_array_type(array), upb_array_get(array, n));
+ }
+
+ return 1;
+}
+
+static int lupb_array_len(lua_State *L) {
+ lupb_array *larray = lupb_array_check(L, 1);
+ lua_pushnumber(L, upb_array_size(larray->arr));
+ return 1;
+}
+
+static const struct luaL_Reg lupb_array_mm[] = {
+ {"__gc", lupb_array_gc},
+ {"__index", lupb_array_index},
+ {"__len", lupb_array_len},
+ {"__newindex", lupb_array_newindex},
+ {NULL, NULL}
+};
+
+
+/* lupb_map *******************************************************************/
+
+/* A map object. Implemented by wrapping upb_map.
+ *
+ * When the value type is string/bytes/message, the userval consists of:
+ *
+ * [Lua number/string] -> [lupb_string/lupb_msg userdata]
+ *
+ * For other value types we don't use the userdata.
+ */
+
+typedef struct {
+ const lupb_msgclass *value_lmsgclass;
+ upb_map *map;
+} lupb_map;
+
+#define MAP_MSGCLASS_INDEX 0
+
+/* lupb_map internal functions */
+
+static lupb_map *lupb_map_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_ARRAY);
+}
+
+/**
+ * lupb_map_typecheck()
+ *
+ * Checks that the lupb_map at index |narg| can be safely assigned to the
+ * field |f| of the message at index |msg|. If so, returns a upb_msgval for
+ * this map. Otherwise, raises a Lua error.
+ */
+static upb_msgval lupb_map_typecheck(lua_State *L, int narg, int msg,
+ const upb_fielddef *f) {
+ lupb_map *lmap = lupb_map_check(L, narg);
+ upb_map *map = lmap->map;
+ const upb_msgdef *entry = upb_fielddef_msgsubdef(f);
+ const upb_fielddef *key_field = upb_msgdef_itof(entry, UPB_MAPENTRY_KEY);
+ const upb_fielddef *value_field = upb_msgdef_itof(entry, UPB_MAPENTRY_VALUE);
+
+ UPB_ASSERT(entry && key_field && value_field);
+
+ if (upb_map_keytype(map) != upb_fielddef_type(key_field)) {
+ luaL_error(L, "Map key type invalid");
+ }
+
+ if (upb_map_valuetype(map) != upb_fielddef_type(value_field)) {
+ luaL_error(L, "Map had incorrect value type (expected: %s, got: %s)",
+ upb_fielddef_type(value_field), upb_map_valuetype(map));
+ }
+
+ if (upb_map_valuetype(map) == UPB_TYPE_MESSAGE) {
+ lupb_msgclass_typecheck(
+ L, lupb_msg_msgclassfor(L, msg, upb_fielddef_msgsubdef(value_field)),
+ lmap->value_lmsgclass);
+ }
+
+ return upb_msgval_map(map);
+}
+
+static int lupb_map_gc(lua_State *L) {
+ lupb_map *lmap = lupb_map_check(L, 1);
+ upb_map_uninit(lmap->map);
+ return 0;
+}
+
+/* lupb_map Public API */
+
+/**
+ * lupb_map_new
+ *
+ * Handles:
+ * new_map = upb.Map(key_type, value_type)
+ */
+static int lupb_map_new(lua_State *L) {
+ lupb_map *lmap;
+ upb_fieldtype_t key_type = lupb_checkfieldtype(L, 1);
+ upb_fieldtype_t value_type;
+ lupb_msgclass *value_lmsgclass = NULL;
+
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ value_type = lupb_checkfieldtype(L, 2);
+ } else {
+ value_type = UPB_TYPE_MESSAGE;
+ }
+
+ lmap = lupb_newuserdata(
+ L, sizeof(*lmap) + upb_map_sizeof(key_type, value_type), LUPB_MAP);
+ lmap->map = ADD_BYTES(lmap, sizeof(*lmap));
+
+ if (value_type == UPB_TYPE_MESSAGE) {
+ value_lmsgclass = lupb_msgclass_check(L, 2);
+ lupb_uservalseti(L, -1, MAP_MSGCLASS_INDEX, 2); /* GC-root lmsgclass. */
+ }
+
+ lmap->value_lmsgclass = value_lmsgclass;
+ upb_map_init(lmap->map, key_type, value_type, lupb_alloc_get(L));
+
+ return 1;
+}
+
+/**
+ * lupb_map_index
+ *
+ * Handles:
+ * map[key]
+ */
+static int lupb_map_index(lua_State *L) {
+ lupb_map *lmap = lupb_map_check(L, 1);
+ upb_map *map = lmap->map;
+ upb_fieldtype_t valtype = upb_map_valuetype(map);
+ /* We don't always use "key", but this call checks the key type. */
+ upb_msgval key = lupb_tomsgval(L, upb_map_keytype(map), 2, NULL);
+
+ if (lupb_istypewrapped(valtype)) {
+ /* Userval contains the full map, lookup there by key. */
+ lupb_getuservalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_rawget(L, -2);
+
+ if (lua_isnil(L, -1)) {
+ /* TODO: lazy read from upb_map */
+ }
+ } else {
+ /* Lookup in upb_map. */
+ upb_msgval val;
+ if (upb_map_get(map, key, &val)) {
+ lupb_pushmsgval(L, upb_map_valuetype(map), val);
+ } else {
+ lua_pushnil(L);
+ }
+ }
+
+ return 1;
+}
+
+/**
+ * lupb_map_len
+ *
+ * Handles:
+ * map_len = #map
+ */
+static int lupb_map_len(lua_State *L) {
+ lupb_map *lmap = lupb_map_check(L, 1);
+ lua_pushnumber(L, upb_map_size(lmap->map));
+ return 1;
+}
+
+/**
+ * lupb_map_newindex
+ *
+ * Handles:
+ * map[key] = val
+ * map[key] = nil # to remove from map
+ */
+static int lupb_map_newindex(lua_State *L) {
+ lupb_map *lmap = lupb_map_check(L, 1);
+ upb_map *map = lmap->map;
+ upb_msgval key = lupb_tomsgval(L, upb_map_keytype(map), 2, NULL);
+
+ if (lua_isnil(L, 3)) {
+ /* Delete from map. */
+ upb_map_del(map, key);
+
+ if (lupb_istypewrapped(upb_map_valuetype(map))) {
+ /* Delete in userval. */
+ lupb_getuservalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_pushnil(L);
+ lua_rawset(L, -3);
+ lua_pop(L, 1);
+ }
+ } else {
+ /* Set in map. */
+ upb_msgval val =
+ lupb_tomsgval(L, upb_map_valuetype(map), 3, lmap->value_lmsgclass);
+
+ upb_map_set(map, key, val, NULL);
+
+ if (lupb_istypewrapped(upb_map_valuetype(map))) {
+ /* Set in userval. */
+ lupb_getuservalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_pushvalue(L, 3);
+ lua_rawset(L, -3);
+ lua_pop(L, 1);
+ }
+ }
+
+ return 0;
+}
+
+/* upb_mapiter [[[ */
+
+static int lupb_mapiter_next(lua_State *L) {
+ upb_mapiter *i = lua_touserdata(L, lua_upvalueindex(1));
+ lupb_map *lmap = lupb_map_check(L, 1);
+ upb_map *map = lmap->map;
+
+ if (upb_mapiter_done(i)) {
+ return 0;
+ }
+
+ lupb_pushmsgval(L, upb_map_keytype(map), upb_mapiter_key(i));
+ lupb_pushmsgval(L, upb_map_valuetype(map), upb_mapiter_value(i));
+ upb_mapiter_next(i);
+
+ return 2;
+}
+
+static int lupb_map_pairs(lua_State *L) {
+ lupb_map *lmap = lupb_map_check(L, 1);
+
+ if (lupb_istypewrapped(upb_map_keytype(lmap->map)) ||
+ lupb_istypewrapped(upb_map_valuetype(lmap->map))) {
+ /* Complex key or value type.
+ * Sync upb_map to userval if necessary, then iterate over userval. */
+
+ /* TODO: Lua tables don't know how many entries they have, gah!. */
+ return 1;
+ } else {
+ /* Simple key and value type, iterate over the upb_map directly. */
+ upb_mapiter *i = lua_newuserdata(L, upb_mapiter_sizeof());
+
+ upb_mapiter_begin(i, lmap->map);
+ lua_pushvalue(L, 1);
+
+ /* Upvalues are [upb_mapiter, lupb_map]. */
+ lua_pushcclosure(L, &lupb_mapiter_next, 2);
+
+ return 1;
+ }
+}
+
+/* upb_mapiter ]]] */
+
+static const struct luaL_Reg lupb_map_mm[] = {
+ {"__gc", lupb_map_gc},
+ {"__index", lupb_map_index},
+ {"__len", lupb_map_len},
+ {"__newindex", lupb_map_newindex},
+ {"__pairs", lupb_map_pairs},
+ {NULL, NULL}
+};
+
+
+/* lupb_msg *******************************************************************/
+
+/* A message object. Implemented by wrapping upb_msg.
+ *
+ * Our userval contains:
+ *
+ * - [0] -> our message class
+ * - [lupb_fieldindex(f)] -> [lupb_{string,array,map,msg} userdata]
+ *
+ * Fields with scalar number/bool types don't go in the userval.
+ */
+
+#define LUPB_MSG_MSGCLASSINDEX 0
+#define LUPB_MSG_ARENA -1
+
+int lupb_fieldindex(const upb_fielddef *f) {
+ return upb_fielddef_index(f) + 1; /* 1-based Lua arrays. */
+}
+
+
+typedef struct {
+ const lupb_msgclass *lmsgclass;
+ upb_msg *msg;
+} lupb_msg;
+
+/* lupb_msg helpers */
+
+static bool in_userval(const upb_fielddef *f) {
+ return lupb_istypewrapped(upb_fielddef_type(f)) || upb_fielddef_isseq(f) ||
+ upb_fielddef_ismap(f);
+}
+
+lupb_msg *lupb_msg_check(lua_State *L, int narg) {
+ lupb_msg *msg = luaL_checkudata(L, narg, LUPB_MSG);
+ if (!msg->lmsgclass) luaL_error(L, "called into dead msg");
+ return msg;
+}
+
+static upb_msgval lupb_msg_typecheck(lua_State *L, int narg,
+ const lupb_msgclass *lmsgclass) {
+ lupb_msg *msg = lupb_msg_check(L, narg);
+ lupb_msgclass_typecheck(L, lmsgclass, msg->lmsgclass);
+ return upb_msgval_msg(msg);
+}
+
+const upb_msgdef *lupb_msg_checkdef(lua_State *L, int narg) {
+ return upb_msglayout_msgdef(lupb_msg_check(L, narg)->lmsgclass->layout);
+}
+
+static const upb_fielddef *lupb_msg_checkfield(lua_State *L,
+ const lupb_msg *msg,
+ int fieldarg) {
+ size_t len;
+ const char *fieldname = luaL_checklstring(L, fieldarg, &len);
+ const upb_msgdef *msgdef = upb_msglayout_msgdef(msg->lmsgclass->layout);
+ const upb_fielddef *f = upb_msgdef_ntof(msgdef, fieldname, len);
+
+ if (!f) {
+ const char *msg = lua_pushfstring(L, "no such field: %s", fieldname);
+ luaL_argerror(L, fieldarg, msg);
+ return NULL; /* Never reached. */
+ }
+
+ return f;
+}
+
+static const lupb_msgclass *lupb_msg_msgclassfor(lua_State *L, int narg,
+ const upb_msgdef *md) {
+ lupb_uservalgeti(L, narg, LUPB_MSG_MSGCLASSINDEX);
+ return lupb_msgclass_msgclassfor(L, -1, md);
+}
+
+static const lupb_msgclass *lupb_msg_getsubmsgclass(lua_State *L, int narg,
+ const upb_fielddef *f) {
+ lupb_uservalgeti(L, narg, LUPB_MSG_MSGCLASSINDEX);
+ return lupb_msgclass_getsubmsgclass(L, -1, f);
+}
+
+int lupb_msg_pushref(lua_State *L, int msgclass, void *msg) {
+ lupb_msgclass *lmsgclass = lupb_msgclass_check(L, msgclass);
+ lupb_msg *lmsg = lupb_newuserdata(L, sizeof(lupb_msg), LUPB_MSG);
+
+ lmsg->lmsgclass = lmsgclass;
+ lmsg->msg = msg;
+
+ lupb_uservalseti(L, -1, LUPB_MSG_MSGCLASSINDEX, msgclass);
+ lupb_uservalseti(L, -1, LUPB_MSG_ARENA, -2);
+
+ return 1;
+}
+
+/* lupb_msg Public API */
+
+/**
+ * lupb_msg_pushnew
+ *
+ * Handles:
+ * new_msg = MessageClass()
+ */
+static int lupb_msg_pushnew(lua_State *L, int narg) {
+ lupb_msgclass *lmsgclass = lupb_msgclass_check(L, narg);
+ size_t size = sizeof(lupb_msg) + upb_msg_sizeof(lmsgclass->layout);
+ lupb_msg *lmsg = lupb_newuserdata(L, size, LUPB_MSG);
+
+ lmsg->lmsgclass = lmsgclass;
+ lmsg->msg = ADD_BYTES(lmsg, sizeof(*lmsg));
+ upb_msg_init(lmsg->msg, lmsgclass->layout, lupb_alloc_get(L));
+
+ lupb_uservalseti(L, -1, LUPB_MSG_MSGCLASSINDEX, narg);
+
+ return 1;
+}
+
+/**
+ * lupb_msg_index
+ *
+ * Handles:
+ * msg.foo
+ * msg["foo"]
+ * msg[field_descriptor] # (for extensions) (TODO)
+ */
+static int lupb_msg_index(lua_State *L) {
+ lupb_msg *lmsg = lupb_msg_check(L, 1);
+ const upb_fielddef *f = lupb_msg_checkfield(L, lmsg, 2);
+ const upb_msglayout *l = lmsg->lmsgclass->layout;
+
+ if (in_userval(f)) {
+ lupb_uservalgeti(L, 1, lupb_fieldindex(f));
+
+ if (lua_isnil(L, -1)) {
+ /* Check if we need to lazily create wrapper. */
+ if (upb_fielddef_isseq(f)) {
+ /* TODO(haberman) */
+ } else if (upb_fielddef_issubmsg(f)) {
+ /* TODO(haberman) */
+ } else {
+ UPB_ASSERT(upb_fielddef_isstring(f));
+ if (upb_msg_has(lmsg->msg, f, l)) {
+ upb_msgval val = upb_msg_get(lmsg->msg, f, l);
+ lua_pop(L, 1);
+ lua_pushlstring(L, val.str.ptr, val.str.len);
+ lupb_uservalseti(L, 1, lupb_fieldindex(f), -1);
+ }
+ }
+ }
+ } else {
+ lupb_pushmsgval(L, upb_fielddef_type(f), upb_msg_get(lmsg->msg, f, l));
+ }
+
+ return 1;
+}
+
+/**
+ * lupb_msg_newindex()
+ *
+ * Handles:
+ * msg.foo = bar
+ * msg["foo"] = bar
+ * msg[field_descriptor] = bar # (for extensions) (TODO)
+ */
+static int lupb_msg_newindex(lua_State *L) {
+ lupb_msg *lmsg = lupb_msg_check(L, 1);
+ const upb_fielddef *f = lupb_msg_checkfield(L, lmsg, 2);
+ upb_fieldtype_t type = upb_fielddef_type(f);
+ upb_msgval msgval;
+
+ /* Typecheck and get msgval. */
+
+ if (upb_fielddef_isseq(f)) {
+ msgval = lupb_array_typecheck(L, 3, 1, f);
+ } else if (upb_fielddef_ismap(f)) {
+ msgval = lupb_map_typecheck(L, 3, 1, f);
+ } else {
+ const lupb_msgclass *lmsgclass = NULL;
+
+ if (type == UPB_TYPE_MESSAGE) {
+ lmsgclass = lupb_msg_getsubmsgclass(L, 1, f);
+ }
+
+ msgval = lupb_tomsgval(L, type, 3, lmsgclass);
+ }
+
+ /* Set in upb_msg and userval (if necessary). */
+
+ upb_msg_set(lmsg->msg, f, msgval, lmsg->lmsgclass->layout);
+
+ if (in_userval(f)) {
+ lupb_uservalseti(L, 1, lupb_fieldindex(f), 3);
+ }
+
+ return 0; /* 1 for chained assignments? */
+}
+
+static const struct luaL_Reg lupb_msg_mm[] = {
+ {"__index", lupb_msg_index},
+ {"__newindex", lupb_msg_newindex},
+ {NULL, NULL}
+};
+
+
+/* lupb_msg toplevel **********************************************************/
+
+static const struct luaL_Reg lupb_msg_toplevel_m[] = {
+ {"Array", lupb_array_new},
+ {"Map", lupb_map_new},
+ {"MessageFactory", lupb_msgfactory_new},
+ {NULL, NULL}
+};
+
+void lupb_msg_registertypes(lua_State *L) {
+ lupb_setfuncs(L, lupb_msg_toplevel_m);
+
+ lupb_register_type(L, LUPB_ARENA, NULL, lupb_arena_mm);
+ lupb_register_type(L, LUPB_MSGCLASS, NULL, lupb_msgclass_mm);
+ lupb_register_type(L, LUPB_MSGFACTORY, lupb_msgfactory_m, lupb_msgfactory_mm);
+ lupb_register_type(L, LUPB_ARRAY, NULL, lupb_array_mm);
+ lupb_register_type(L, LUPB_MAP, NULL, lupb_map_mm);
+ lupb_register_type(L, LUPB_MSG, NULL, lupb_msg_mm);
+
+ lupb_alloc_initsingleton(L);
+}
diff --git a/upb/bindings/lua/upb.c b/upb/bindings/lua/upb.c
index 1483676..38fd24a 100644
--- a/upb/bindings/lua/upb.c
+++ b/upb/bindings/lua/upb.c
@@ -27,25 +27,8 @@
#include "lauxlib.h"
#include "upb/bindings/lua/upb.h"
#include "upb/handlers.h"
-#include "upb/pb/glue.h"
-#include "upb/shim/shim.h"
+#include "upb/msg.h"
-/* Lua metatable types. */
-#define LUPB_ENUMDEF "lupb.enumdef"
-#define LUPB_FIELDDEF "lupb.fielddef"
-#define LUPB_FILEDEF "lupb.filedef"
-#define LUPB_MSGDEF "lupb.msgdef"
-#define LUPB_ONEOFDEF "lupb.oneof"
-#define LUPB_SYMTAB "lupb.symtab"
-
-#define LUPB_ARRAY "lupb.array"
-#define LUPB_MSG "lupb.msg"
-
-/* Other table constants. */
-#define LUPB_OBJCACHE "lupb.objcache"
-
-static void lupb_msgdef_init(lua_State *L);
-static size_t lupb_msgdef_sizeof();
/* Lua compatibility code *****************************************************/
@@ -74,8 +57,6 @@ static void lupb_newlib(lua_State *L, const char *name, const luaL_Reg *funcs) {
luaL_register(L, name, funcs);
}
-#define lupb_setfuncs(L, l) luaL_register(L, NULL, l)
-
#elif LUA_VERSION_NUM == 502
int luaL_typerror(lua_State *L, int narg, const char *tname) {
@@ -95,8 +76,6 @@ static void lupb_newlib(lua_State *L, const char *name, const luaL_Reg *funcs) {
luaL_setfuncs(L, funcs, 0);
}
-#define lupb_setfuncs(L, l) luaL_setfuncs(L, l, 0)
-
#else
#error Only Lua 5.1 and 5.2 are supported
#endif
@@ -133,30 +112,17 @@ bool lupb_openlib(lua_State *L, void *ptr, const char *name,
return false;
}
-/* Pushes a new userdata with the given metatable and ensures that it has a
- * uservalue. */
-static void *newudata_with_userval(lua_State *L, size_t size,
- const char *type) {
- void *ret = lua_newuserdata(L, size);
-
- /* Set metatable. */
- luaL_getmetatable(L, type);
- UPB_ASSERT(!lua_isnil(L, -1)); /* Should have been created by luaopen_upb. */
- lua_setmetatable(L, -2);
-
- lua_newtable(L);
- lua_setuservalue(L, -2);
-
- return ret;
+void lupb_checkstatus(lua_State *L, upb_status *s) {
+ if (!upb_ok(s)) {
+ lua_pushstring(L, upb_status_errmsg(s));
+ lua_error(L);
+ }
}
-const char *lupb_checkname(lua_State *L, int narg) {
- size_t len;
- const char *name = luaL_checklstring(L, narg, &len);
- if (strlen(name) != len)
- luaL_error(L, "names cannot have embedded NULLs");
- return name;
-}
+/* Scalar type mapping ********************************************************/
+
+/* Functions that convert scalar/primitive values (numbers, strings, bool)
+ * between Lua and C/upb. Handles type/range checking. */
bool lupb_checkbool(lua_State *L, int narg) {
if (!lua_isboolean(L, narg)) {
@@ -167,9 +133,12 @@ bool lupb_checkbool(lua_State *L, int narg) {
/* Unlike luaL_checkstring(), this does not allow implicit conversion to
* string. */
-void lupb_checkstring(lua_State *L, int narg) {
- if (lua_type(L, narg) != LUA_TSTRING)
+const char *lupb_checkstring(lua_State *L, int narg, size_t *len) {
+ if (lua_type(L, narg) != LUA_TSTRING) {
luaL_error(L, "Expected string");
+ }
+
+ return lua_tolstring(L, narg, len);
}
/* Unlike luaL_checkinteger, these do not implicitly convert from string or
@@ -235,1839 +204,19 @@ void lupb_pushfloat(lua_State *L, float d) {
lua_pushnumber(L, d);
}
-static void lupb_checkval(lua_State *L, int narg, upb_fieldtype_t type) {
- switch(type) {
- case UPB_TYPE_INT32:
- case UPB_TYPE_ENUM:
- lupb_checkint32(L, narg);
- break;
- case UPB_TYPE_INT64:
- lupb_checkint64(L, narg);
- break;
- case UPB_TYPE_UINT32:
- lupb_checkuint32(L, narg);
- break;
- case UPB_TYPE_UINT64:
- lupb_checkuint64(L, narg);
- break;
- case UPB_TYPE_DOUBLE:
- lupb_checkdouble(L, narg);
- break;
- case UPB_TYPE_FLOAT:
- lupb_checkfloat(L, narg);
- break;
- case UPB_TYPE_BOOL:
- lupb_checkbool(L, narg);
- break;
- case UPB_TYPE_STRING:
- case UPB_TYPE_BYTES:
- lupb_checkstring(L, narg);
- break;
- case UPB_TYPE_MESSAGE:
- lupb_assert(L, false);
- }
-}
-
-void lupb_checkstatus(lua_State *L, upb_status *s) {
- if (!upb_ok(s)) {
- lua_pushstring(L, upb_status_errmsg(s));
- lua_error(L);
- }
-}
-
-static upb_fieldtype_t lupb_checkfieldtype(lua_State *L, int narg) {
- int type = luaL_checkint(L, narg);
- if (!upb_fielddef_checktype(type))
- luaL_argerror(L, narg, "invalid field type");
- return type;
-}
-
-#define CHK(pred) do { \
- upb_status status = UPB_STATUS_INIT; \
- pred; \
- lupb_checkstatus(L, &status); \
- } while (0)
-
-
-/* lupb_refcounted ************************************************************/
-
-/* All upb objects that use upb_refcounted have a userdata that begins with a
- * pointer to that object. Each type has its own metatable. Objects are cached
- * in a weak table indexed by the C pointer of the object they are caching.
- *
- * Note that we consistently use memcpy() to read to/from the object. This
- * allows the userdata to use its own struct without violating aliasing, as
- * long as it begins with a pointer. */
-
-/* Checks type; if it matches, pulls the pointer out of the wrapper. */
-void *lupb_refcounted_check(lua_State *L, int narg, const char *type) {
- void *ud = luaL_checkudata(L, narg, type);
- void *ret;
- memcpy(&ret, ud, sizeof ret);
- if (!ret) luaL_error(L, "called into dead object");
- return ret;
-}
-
-bool lupb_refcounted_pushwrapper(lua_State *L, const upb_refcounted *obj,
- const char *type, const void *ref_donor,
- size_t size) {
- bool create;
- void *ud;
-
- if (obj == NULL) {
- lua_pushnil(L);
- return false;
- }
-
- /* Lookup our cache in the registry (we don't put our objects in the registry
- * directly because we need our cache to be a weak table). */
- lua_getfield(L, LUA_REGISTRYINDEX, LUPB_OBJCACHE);
- UPB_ASSERT(!lua_isnil(L, -1)); /* Should have been created by luaopen_upb. */
- lua_pushlightuserdata(L, (void*)obj);
- lua_rawget(L, -2);
- /* Stack is now: objcache, cached value. */
-
- create = false;
-
- if (lua_isnil(L, -1)) {
- create = true;
- } else {
- void *ud = lua_touserdata(L, -1);
- void *ud_obj;
- lupb_assert(L, ud);
- memcpy(&ud_obj, ud, sizeof(void*));
-
- /* A corner case: it is possible for the value to be GC'd
- * already, in which case we should evict this entry and create
- * a new one. */
- if (ud_obj == NULL) {
- create = true;
- }
- }
-
- ud = NULL;
-
- if (create) {
- /* Remove bad cached value and push new value. */
- lua_pop(L, 1);
-
- /* All of our userdata begin with a pointer to the obj. */
- ud = lua_newuserdata(L, size);
- memcpy(ud, &obj, sizeof(void*));
- upb_refcounted_donateref(obj, ref_donor, ud);
-
- luaL_getmetatable(L, type);
- /* Should have been created by luaopen_upb. */
- lupb_assert(L, !lua_isnil(L, -1));
- lua_setmetatable(L, -2);
-
- /* Set it in the cache. */
- lua_pushlightuserdata(L, (void*)obj);
- lua_pushvalue(L, -2);
- lua_rawset(L, -4);
- } else {
- /* Existing wrapper obj already has a ref. */
- ud = lua_touserdata(L, -1);
- upb_refcounted_checkref(obj, ud);
- if (ref_donor)
- upb_refcounted_unref(obj, ref_donor);
- }
-
- lua_insert(L, -2);
- lua_pop(L, 1);
- return create;
-}
-
-void lupb_refcounted_pushnewrapper(lua_State *L, const upb_refcounted *obj,
- const char *type, const void *ref_donor) {
- bool created =
- lupb_refcounted_pushwrapper(L, obj, type, ref_donor, sizeof(void *));
- UPB_ASSERT(created == true);
-}
-
-static int lupb_refcounted_gc(lua_State *L) {
- void *ud = lua_touserdata(L, 1);
- void *nullp;
- upb_refcounted *obj;
- memcpy(&obj, ud, sizeof(obj));
- upb_refcounted_unref(obj, ud);
-
- /* Zero out pointer so we can detect a call into a GC'd object. */
- nullp = NULL;
- memcpy(ud, &nullp, sizeof(nullp));
-
- return 0;
-}
-
-static const struct luaL_Reg lupb_refcounted_mm[] = {
- {"__gc", lupb_refcounted_gc},
- {NULL, NULL}
-};
-
-
-/* lupb_def *******************************************************************/
-
-static const upb_def *lupb_def_check(lua_State *L, int narg) {
- upb_def *ret;
- void *ud = luaL_testudata(L, narg, LUPB_MSGDEF);
- if (!ud) ud = luaL_testudata(L, narg, LUPB_ENUMDEF);
- if (!ud) ud = luaL_testudata(L, narg, LUPB_FIELDDEF);
- if (!ud) luaL_typerror(L, narg, "upb def");
-
- memcpy(&ret, ud, sizeof ret);
- if (!ret) luaL_error(L, "called into dead object");
- return ret;
-}
-
-static upb_def *lupb_def_checkmutable(lua_State *L, int narg) {
- const upb_def *def = lupb_def_check(L, narg);
- if (upb_def_isfrozen(def))
- luaL_error(L, "not allowed on frozen value");
- return (upb_def*)def;
-}
-
-bool lupb_def_pushwrapper(lua_State *L, const upb_def *def,
- const void *ref_donor) {
- const char *type = NULL;
- size_t size = sizeof(void*);
- bool created;
-
- if (def == NULL) {
- lua_pushnil(L);
- return false;
- }
-
- switch (upb_def_type(def)) {
- case UPB_DEF_MSG: {
- type = LUPB_MSGDEF;
- size = lupb_msgdef_sizeof();
- break;
- }
- case UPB_DEF_ENUM: type = LUPB_ENUMDEF; break;
- case UPB_DEF_FIELD: type = LUPB_FIELDDEF; break;
- default: luaL_error(L, "unknown deftype %d", upb_def_type(def));
- }
-
- created =
- lupb_refcounted_pushwrapper(L, upb_def_upcast(def), type, ref_donor, size);
-
- if (created && upb_def_type(def) == UPB_DEF_MSG) {
- lupb_msgdef_init(L);
- }
-
- return created;
-}
-
-void lupb_def_pushnewrapper(lua_State *L, const upb_def *def,
- const void *ref_donor) {
- bool created = lupb_def_pushwrapper(L, def, ref_donor);
- UPB_ASSERT(created == true);
-}
-
-static int lupb_def_type(lua_State *L) {
- const upb_def *def = lupb_def_check(L, 1);
- lua_pushinteger(L, upb_def_type(def));
- return 1;
-}
-
-static int lupb_def_freeze(lua_State *L) {
- upb_def *def = lupb_def_checkmutable(L, 1);
- CHK(upb_def_freeze(&def, 1, &status));
- return 0;
-}
-
-static int lupb_def_isfrozen(lua_State *L) {
- const upb_def *def = lupb_def_check(L, 1);
- lua_pushboolean(L, upb_def_isfrozen(def));
- return 1;
-}
-
-static int lupb_def_fullname(lua_State *L) {
- const upb_def *def = lupb_def_check(L, 1);
- lua_pushstring(L, upb_def_fullname(def));
- return 1;
-}
-
-static int lupb_def_name(lua_State *L) {
- const upb_def *def = lupb_def_check(L, 1);
- lua_pushstring(L, upb_def_name(def));
- return 1;
-}
-
-static int lupb_def_setfullname(lua_State *L) {
- const char *name = lupb_checkname(L, 2);
- CHK(upb_def_setfullname(lupb_def_checkmutable(L, 1), name, &status));
- return 0;
-}
-
-#define LUPB_COMMON_DEF_METHODS \
- {"def_type", lupb_def_type}, \
- {"full_name", lupb_def_fullname}, \
- {"freeze", lupb_def_freeze}, \
- {"is_frozen", lupb_def_isfrozen}, \
- {"name", lupb_def_name}, \
- {"set_full_name", lupb_def_setfullname}, \
-
-
-/* lupb_fielddef **************************************************************/
-
-void lupb_fielddef_pushwrapper(lua_State *L, const upb_fielddef *f,
- const void *ref_donor) {
- lupb_def_pushwrapper(L, upb_fielddef_upcast(f), ref_donor);
-}
-
-const upb_fielddef *lupb_fielddef_check(lua_State *L, int narg) {
- return lupb_refcounted_check(L, narg, LUPB_FIELDDEF);
-}
-
-static upb_fielddef *lupb_fielddef_checkmutable(lua_State *L, int narg) {
- const upb_fielddef *f = lupb_fielddef_check(L, narg);
- if (upb_fielddef_isfrozen(f))
- luaL_error(L, "not allowed on frozen value");
- return (upb_fielddef*)f;
-}
-
-static int lupb_fielddef_new(lua_State *L) {
- upb_fielddef *f = upb_fielddef_new(&f);
- lupb_def_pushnewrapper(L, upb_fielddef_upcast(f), &f);
- return 1;
-}
-
-/* Getters */
-
-static int lupb_fielddef_containingtype(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lupb_msgdef_pushwrapper(L, upb_fielddef_containingtype(f), NULL);
- return 1;
-}
-
-static int lupb_fielddef_containingtypename(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lua_pushstring(L, upb_fielddef_containingtypename(f));
- return 1;
-}
-
-static int lupb_fielddef_default(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- switch (upb_fielddef_type(f)) {
- case UPB_TYPE_INT32:
- int32:
- lupb_pushint32(L, upb_fielddef_defaultint32(f)); break;
- case UPB_TYPE_INT64:
- lupb_pushint64(L, upb_fielddef_defaultint64(f)); break;
- case UPB_TYPE_UINT32:
- lupb_pushuint32(L, upb_fielddef_defaultuint32(f)); break;
- case UPB_TYPE_UINT64:
- lupb_pushuint64(L, upb_fielddef_defaultuint64(f)); break;
- case UPB_TYPE_DOUBLE:
- lua_pushnumber(L, upb_fielddef_defaultdouble(f)); break;
- case UPB_TYPE_FLOAT:
- lua_pushnumber(L, upb_fielddef_defaultfloat(f)); break;
- case UPB_TYPE_BOOL:
- lua_pushboolean(L, upb_fielddef_defaultbool(f)); break;
- case UPB_TYPE_ENUM:
- if (upb_fielddef_enumhasdefaultstr(f)) {
- goto str;
- } else if (upb_fielddef_enumhasdefaultint32(f)) {
- goto int32;
- } else {
- lua_pushnil(L);
- }
- break;
- case UPB_TYPE_STRING:
- case UPB_TYPE_BYTES:
- str: {
- size_t len;
- const char *data = upb_fielddef_defaultstr(f, &len);
- lua_pushlstring(L, data, len);
- break;
- }
- case UPB_TYPE_MESSAGE:
- return luaL_error(L, "Message fields do not have explicit defaults.");
- }
- return 1;
-}
-
-static int lupb_fielddef_getsel(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- upb_selector_t sel;
- if (upb_handlers_getselector(f, luaL_checknumber(L, 2), &sel)) {
- lua_pushinteger(L, sel);
- return 1;
- } else {
- return 0;
- }
-}
-
-static int lupb_fielddef_hassubdef(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushboolean(L, upb_fielddef_hassubdef(f));
- return 1;
-}
-
-static int lupb_fielddef_index(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushinteger(L, upb_fielddef_index(f));
- return 1;
-}
-
-static int lupb_fielddef_intfmt(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushinteger(L, upb_fielddef_intfmt(f));
- return 1;
-}
-
-static int lupb_fielddef_isextension(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushboolean(L, upb_fielddef_isextension(f));
- return 1;
-}
-
-static int lupb_fielddef_istagdelim(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushboolean(L, upb_fielddef_istagdelim(f));
- return 1;
-}
-
-static int lupb_fielddef_label(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushinteger(L, upb_fielddef_label(f));
- return 1;
-}
-
-static int lupb_fielddef_lazy(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushboolean(L, upb_fielddef_lazy(f));
- return 1;
-}
-
-static int lupb_fielddef_name(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushstring(L, upb_fielddef_name(f));
- return 1;
-}
-
-static int lupb_fielddef_number(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- int32_t num = upb_fielddef_number(f);
- if (num)
- lua_pushinteger(L, num);
- else
- lua_pushnil(L);
- return 1;
-}
-
-static int lupb_fielddef_packed(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushboolean(L, upb_fielddef_packed(f));
- return 1;
-}
-
-static int lupb_fielddef_subdef(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- const upb_def *def;
-
- if (!upb_fielddef_hassubdef(f))
- luaL_error(L, "Tried to get subdef of non-message field");
- def = upb_fielddef_subdef(f);
- lupb_def_pushwrapper(L, def, NULL);
- return 1;
-}
-
-static int lupb_fielddef_subdefname(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- if (!upb_fielddef_hassubdef(f))
- luaL_error(L, "Tried to get subdef name of non-message field");
- lua_pushstring(L, upb_fielddef_subdefname(f));
- return 1;
-}
-
-static int lupb_fielddef_type(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- if (upb_fielddef_typeisset(f))
- lua_pushinteger(L, upb_fielddef_type(f));
- else
- lua_pushnil(L);
- return 1;
-}
-
-/* Setters */
-
-static int lupb_fielddef_setcontainingtypename(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- const char *name = NULL;
- if (!lua_isnil(L, 2))
- name = lupb_checkname(L, 2);
- CHK(upb_fielddef_setcontainingtypename(f, name, &status));
- return 0;
-}
-
-static int lupb_fielddef_setdefault(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
-
- switch (upb_fielddef_type(f)) {
- case UPB_TYPE_INT32:
- upb_fielddef_setdefaultint32(f, lupb_checkint32(L, 2));
- break;
- case UPB_TYPE_INT64:
- upb_fielddef_setdefaultint64(f, lupb_checkint64(L, 2));
- break;
- case UPB_TYPE_UINT32:
- upb_fielddef_setdefaultuint32(f, lupb_checkuint32(L, 2));
- break;
- case UPB_TYPE_UINT64:
- upb_fielddef_setdefaultuint64(f, lupb_checkuint64(L, 2));
- break;
- case UPB_TYPE_DOUBLE:
- upb_fielddef_setdefaultdouble(f, lupb_checkdouble(L, 2));
- break;
- case UPB_TYPE_FLOAT:
- upb_fielddef_setdefaultfloat(f, lupb_checkfloat(L, 2));
- break;
- case UPB_TYPE_BOOL:
- upb_fielddef_setdefaultbool(f, lupb_checkbool(L, 2));
- break;
- case UPB_TYPE_MESSAGE:
- return luaL_error(L, "Message types cannot have defaults.");
- case UPB_TYPE_ENUM:
- if (lua_type(L, 2) != LUA_TSTRING) {
- upb_fielddef_setdefaultint32(f, lupb_checkint32(L, 2));
- break;
- }
- /* Else fall through and set string default. */
- case UPB_TYPE_BYTES:
- case UPB_TYPE_STRING: {
- size_t len;
- const char *str = lua_tolstring(L, 2, &len);
- CHK(upb_fielddef_setdefaultstr(f, str, len, &status));
- }
- }
- return 0;
-}
-
-static int lupb_fielddef_setisextension(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- CHK(upb_fielddef_setisextension(f, lupb_checkbool(L, 2)));
- return 0;
-}
-
-static int lupb_fielddef_setlabel(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- int label = luaL_checkint(L, 2);
- if (!upb_fielddef_checklabel(label))
- luaL_argerror(L, 2, "invalid field label");
- upb_fielddef_setlabel(f, label);
- return 0;
-}
-
-static int lupb_fielddef_setlazy(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- upb_fielddef_setlazy(f, lupb_checkbool(L, 2));
- return 0;
-}
-
-static int lupb_fielddef_setname(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- CHK(upb_fielddef_setname(f, lupb_checkname(L, 2), &status));
- return 0;
-}
-
-static int lupb_fielddef_setnumber(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- CHK(upb_fielddef_setnumber(f, luaL_checkint(L, 2), &status));
- return 0;
-}
-
-static int lupb_fielddef_setpacked(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- upb_fielddef_setpacked(f, lupb_checkbool(L, 2));
- return 0;
-}
-
-static int lupb_fielddef_setsubdef(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- const upb_def *def = NULL;
- if (!lua_isnil(L, 2))
- def = lupb_def_check(L, 2);
- CHK(upb_fielddef_setsubdef(f, def, &status));
- return 0;
-}
-
-static int lupb_fielddef_setsubdefname(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- const char *name = NULL;
- if (!lua_isnil(L, 2))
- name = lupb_checkname(L, 2);
- CHK(upb_fielddef_setsubdefname(f, name, &status));
- return 0;
-}
-
-static int lupb_fielddef_settype(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- upb_fielddef_settype(f, lupb_checkfieldtype(L, 2));
- return 0;
-}
-
-static int lupb_fielddef_setintfmt(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- int32_t intfmt = luaL_checknumber(L, 2);
- if (!upb_fielddef_checkintfmt(intfmt))
- luaL_argerror(L, 2, "invalid intfmt");
- upb_fielddef_setintfmt(f, intfmt);
- return 0;
-}
-
-static int lupb_fielddef_settagdelim(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- bool is_tag_delim = lupb_checkbool(L, 2);
- CHK(upb_fielddef_settagdelim(f, is_tag_delim));
- return 0;
-}
-
-static const struct luaL_Reg lupb_fielddef_m[] = {
- LUPB_COMMON_DEF_METHODS
-
- {"containing_type", lupb_fielddef_containingtype},
- {"containing_type_name", lupb_fielddef_containingtypename},
- {"default", lupb_fielddef_default},
- {"getsel", lupb_fielddef_getsel},
- {"has_subdef", lupb_fielddef_hassubdef},
- {"index", lupb_fielddef_index},
- {"intfmt", lupb_fielddef_intfmt},
- {"is_extension", lupb_fielddef_isextension},
- {"istagdelim", lupb_fielddef_istagdelim},
- {"label", lupb_fielddef_label},
- {"lazy", lupb_fielddef_lazy},
- {"name", lupb_fielddef_name},
- {"number", lupb_fielddef_number},
- {"packed", lupb_fielddef_packed},
- {"subdef", lupb_fielddef_subdef},
- {"subdef_name", lupb_fielddef_subdefname},
- {"type", lupb_fielddef_type},
-
- {"set_containing_type_name", lupb_fielddef_setcontainingtypename},
- {"set_default", lupb_fielddef_setdefault},
- {"set_is_extension", lupb_fielddef_setisextension},
- {"set_label", lupb_fielddef_setlabel},
- {"set_lazy", lupb_fielddef_setlazy},
- {"set_name", lupb_fielddef_setname},
- {"set_number", lupb_fielddef_setnumber},
- {"set_packed", lupb_fielddef_setpacked},
- {"set_subdef", lupb_fielddef_setsubdef},
- {"set_subdef_name", lupb_fielddef_setsubdefname},
- {"set_type", lupb_fielddef_settype},
- {"set_intfmt", lupb_fielddef_setintfmt},
- {"set_tagdelim", lupb_fielddef_settagdelim},
-
- {NULL, NULL}
-};
-
-
-/* lupb_oneofdef **************************************************************/
-
-void lupb_oneofdef_pushwrapper(lua_State *L, const upb_oneofdef *o,
- const void *ref_donor) {
- lupb_refcounted_pushwrapper(L, upb_oneofdef_upcast(o), LUPB_ONEOFDEF,
- ref_donor, sizeof(void *));
-}
-
-const upb_oneofdef *lupb_oneofdef_check(lua_State *L, int narg) {
- return lupb_refcounted_check(L, narg, LUPB_ONEOFDEF);
-}
-
-static upb_oneofdef *lupb_oneofdef_checkmutable(lua_State *L, int narg) {
- const upb_oneofdef *o = lupb_oneofdef_check(L, narg);
- if (upb_oneofdef_isfrozen(o))
- luaL_error(L, "not allowed on frozen value");
- return (upb_oneofdef*)o;
-}
-
-static int lupb_oneofdef_new(lua_State *L) {
- upb_oneofdef *o = upb_oneofdef_new(&o);
- lupb_refcounted_pushnewrapper(L, upb_oneofdef_upcast(o), LUPB_ONEOFDEF, &o);
- return 1;
-}
-
-/* Getters */
-
-static int lupb_oneofdef_containingtype(lua_State *L) {
- const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
- lupb_def_pushwrapper(L, upb_msgdef_upcast(upb_oneofdef_containingtype(o)),
- NULL);
- return 1;
-}
-
-static int lupb_oneofdef_field(lua_State *L) {
- const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
- int type = lua_type(L, 2);
- const upb_fielddef *f;
- if (type == LUA_TNUMBER) {
- f = upb_oneofdef_itof(o, lua_tointeger(L, 2));
- } else if (type == LUA_TSTRING) {
- f = upb_oneofdef_ntofz(o, lua_tostring(L, 2));
- } else {
- const char *msg = lua_pushfstring(L, "number or string expected, got %s",
- luaL_typename(L, 2));
- return luaL_argerror(L, 2, msg);
- }
-
- lupb_def_pushwrapper(L, upb_fielddef_upcast(f), NULL);
- return 1;
-}
-
-static int lupb_oneofdef_len(lua_State *L) {
- const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
- lua_pushinteger(L, upb_oneofdef_numfields(o));
- return 1;
-}
-
-static int lupb_oneofdef_name(lua_State *L) {
- const upb_oneofdef *o = lupb_oneofdef_check(L, 1);
- lua_pushstring(L, upb_oneofdef_name(o));
- return 1;
-}
-
-/* Setters */
-
-static int lupb_oneofdef_add(lua_State *L) {
- upb_oneofdef *o = lupb_oneofdef_checkmutable(L, 1);
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 2);
- CHK(upb_oneofdef_addfield(o, f, NULL, &status));
- return 0;
-}
-
-static int lupb_oneofdef_setname(lua_State *L) {
- upb_oneofdef *o = lupb_oneofdef_checkmutable(L, 1);
- CHK(upb_oneofdef_setname(o, lupb_checkname(L, 2), &status));
- return 0;
-}
-
-static const struct luaL_Reg lupb_oneofdef_m[] = {
- {"containing_type", lupb_oneofdef_containingtype},
- {"field", lupb_oneofdef_field},
- {"name", lupb_oneofdef_name},
-
- {"add", lupb_oneofdef_add},
- {"set_name", lupb_oneofdef_setname},
-
- {NULL, NULL}
-};
-
-static const struct luaL_Reg lupb_oneofdef_mm[] = {
- {"__len", lupb_oneofdef_len},
- {NULL, NULL}
-};
-
-
-/* lupb_msgdef ****************************************************************/
-
-typedef struct {
- const upb_msgdef *md;
-
- /* These members are initialized lazily the first time a message is created
- * for this def. */
- uint16_t *field_offsets;
- size_t msg_size;
- size_t hasbits_size;
- lua_State *L;
-} lupb_msgdef;
-
-static size_t lupb_msgdef_sizeof() {
- return sizeof(lupb_msgdef);
-}
-
-void lupb_msgdef_pushwrapper(lua_State *L, const upb_msgdef *m,
- const void *ref_donor) {
- lupb_def_pushwrapper(L, upb_msgdef_upcast(m), ref_donor);
-}
-
-const upb_msgdef *lupb_msgdef_check(lua_State *L, int narg) {
- return lupb_refcounted_check(L, narg, LUPB_MSGDEF);
-}
-
-lupb_msgdef *lupb_msgdef_check2(lua_State *L, int narg) {
- return luaL_checkudata(L, narg, LUPB_MSGDEF);
-}
-
-static upb_msgdef *lupb_msgdef_checkmutable(lua_State *L, int narg) {
- const upb_msgdef *m = lupb_msgdef_check(L, narg);
- if (upb_msgdef_isfrozen(m))
- luaL_error(L, "not allowed on frozen value");
- return (upb_msgdef*)m;
-}
-
-static int lupb_msgdef_new(lua_State *L) {
- upb_msgdef *md = upb_msgdef_new(&md);
- lupb_def_pushnewrapper(L, upb_msgdef_upcast(md), &md);
- return 1;
-}
-
-/* Unlike other refcounted types we need a custom __gc so that we free our field
- * offsets. */
-static int lupb_msgdef_gc(lua_State *L) {
- lupb_msgdef *lmd = luaL_checkudata(L, 1, LUPB_MSGDEF);
- free(lmd->field_offsets);
- lupb_refcounted_gc(L);
- return 0;
-}
-
-static void lupb_msgdef_init(lua_State *L) {
- lupb_msgdef *lmd = luaL_checkudata(L, -1, LUPB_MSGDEF);
- lmd->L = L;
- lmd->field_offsets = NULL;
-}
-
-static int lupb_msgdef_add(lua_State *L) {
- upb_msgdef *m = lupb_msgdef_checkmutable(L, 1);
-
- /* Both oneofs and fields can be added. */
- if (luaL_testudata(L, 2, LUPB_FIELDDEF)) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 2);
- CHK(upb_msgdef_addfield(m, f, NULL, &status));
- } else if (luaL_testudata(L, 2, LUPB_ONEOFDEF)) {
- upb_oneofdef *o = lupb_oneofdef_checkmutable(L, 2);
- CHK(upb_msgdef_addoneof(m, o, NULL, &status));
- }
- return 0;
-}
-
-static int lupb_msgdef_len(lua_State *L) {
- const upb_msgdef *m = lupb_msgdef_check(L, 1);
- lua_pushinteger(L, upb_msgdef_numfields(m));
- return 1;
-}
-
-static int lupb_msgdef_field(lua_State *L) {
- const upb_msgdef *m = lupb_msgdef_check(L, 1);
- int type = lua_type(L, 2);
- const upb_fielddef *f;
- if (type == LUA_TNUMBER) {
- f = upb_msgdef_itof(m, lua_tointeger(L, 2));
- } else if (type == LUA_TSTRING) {
- f = upb_msgdef_ntofz(m, lua_tostring(L, 2));
- } else {
- const char *msg = lua_pushfstring(L, "number or string expected, got %s",
- luaL_typename(L, 2));
- return luaL_argerror(L, 2, msg);
- }
-
- lupb_def_pushwrapper(L, upb_fielddef_upcast(f), NULL);
- return 1;
-}
-
-static int lupb_msgdef_lookupname(lua_State *L) {
- const upb_msgdef *m = lupb_msgdef_check(L, 1);
- const upb_fielddef *f;
- const upb_oneofdef *o;
- if (!upb_msgdef_lookupnamez(m, lua_tostring(L, 2), &f, &o)) {
- lua_pushnil(L);
- } else if (o) {
- lupb_oneofdef_pushwrapper(L, o, NULL);
- } else {
- lupb_fielddef_pushwrapper(L, f, NULL);
- }
- return 1;
-}
-
-static int lupb_msgfielditer_next(lua_State *L) {
- upb_msg_field_iter *i = lua_touserdata(L, lua_upvalueindex(1));
- if (upb_msg_field_done(i)) return 0;
- lupb_def_pushwrapper(L, upb_fielddef_upcast(upb_msg_iter_field(i)), NULL);
- upb_msg_field_next(i);
- return 1;
-}
-
-static int lupb_msgdef_fields(lua_State *L) {
- const upb_msgdef *m = lupb_msgdef_check(L, 1);
- upb_msg_field_iter *i = lua_newuserdata(L, sizeof(upb_msg_field_iter));
- upb_msg_field_begin(i, m);
- /* Need to guarantee that the msgdef outlives the iter. */
- lua_pushvalue(L, 1);
- lua_pushcclosure(L, &lupb_msgfielditer_next, 2);
- return 1;
-}
-
-static int lupb_msgoneofiter_next(lua_State *L) {
- upb_msg_oneof_iter *i = lua_touserdata(L, lua_upvalueindex(1));
- if (upb_msg_oneof_done(i)) return 0;
- lupb_oneofdef_pushwrapper(L, upb_msg_iter_oneof(i), NULL);
- upb_msg_oneof_next(i);
- return 1;
-}
-
-static int lupb_msgdef_oneofs(lua_State *L) {
- const upb_msgdef *m = lupb_msgdef_check(L, 1);
- upb_msg_oneof_iter *i = lua_newuserdata(L, sizeof(upb_msg_oneof_iter));
- upb_msg_oneof_begin(i, m);
- /* Need to guarantee that the msgdef outlives the iter. */
- lua_pushvalue(L, 1);
- lua_pushcclosure(L, &lupb_msgoneofiter_next, 2);
- return 1;
-}
-
-static int lupb_msgdef_mapentry(lua_State *L) {
- const upb_msgdef *m = lupb_msgdef_check(L, 1);
- lua_pushboolean(L, upb_msgdef_mapentry(m));
- return 1;
-}
-
-static int lupb_msgdef_syntax(lua_State *L) {
- const upb_msgdef *m = lupb_msgdef_check(L, 1);
- lua_pushinteger(L, upb_msgdef_syntax(m));
- return 1;
-}
-
-static const struct luaL_Reg lupb_msgdef_mm[] = {
- {"__gc", lupb_msgdef_gc},
- {"__len", lupb_msgdef_len},
- {NULL, NULL}
-};
-
-static const struct luaL_Reg lupb_msgdef_m[] = {
- LUPB_COMMON_DEF_METHODS
- {"add", lupb_msgdef_add},
- {"field", lupb_msgdef_field},
- {"fields", lupb_msgdef_fields},
- {"lookup_name", lupb_msgdef_lookupname},
- {"oneofs", lupb_msgdef_oneofs},
- {"syntax", lupb_msgdef_syntax},
- {"_map_entry", lupb_msgdef_mapentry},
-
- {NULL, NULL}
-};
-
-
-/* lupb_enumdef ***************************************************************/
-
-const upb_enumdef *lupb_enumdef_check(lua_State *L, int narg) {
- return lupb_refcounted_check(L, narg, LUPB_ENUMDEF);
-}
-
-static upb_enumdef *lupb_enumdef_checkmutable(lua_State *L, int narg) {
- const upb_enumdef *f = lupb_enumdef_check(L, narg);
- if (upb_enumdef_isfrozen(f))
- luaL_error(L, "not allowed on frozen value");
- return (upb_enumdef*)f;
-}
-
-static int lupb_enumdef_new(lua_State *L) {
- upb_enumdef *e = upb_enumdef_new(&e);
- lupb_def_pushnewrapper(L, upb_enumdef_upcast(e), &e);
- return 1;
-}
-
-static int lupb_enumdef_add(lua_State *L) {
- upb_enumdef *e = lupb_enumdef_checkmutable(L, 1);
- const char *name = lupb_checkname(L, 2);
- int32_t val = lupb_checkint32(L, 3);
- CHK(upb_enumdef_addval(e, name, val, &status));
- return 0;
-}
-
-static int lupb_enumdef_len(lua_State *L) {
- const upb_enumdef *e = lupb_enumdef_check(L, 1);
- lua_pushinteger(L, upb_enumdef_numvals(e));
- return 1;
-}
-
-static int lupb_enumdef_value(lua_State *L) {
- const upb_enumdef *e = lupb_enumdef_check(L, 1);
- int type = lua_type(L, 2);
- if (type == LUA_TNUMBER) {
- /* Pushes "nil" for a NULL pointer. */
- int32_t key = lupb_checkint32(L, 2);
- lua_pushstring(L, upb_enumdef_iton(e, key));
- } else if (type == LUA_TSTRING) {
- const char *key = lua_tostring(L, 2);
- int32_t num;
- if (upb_enumdef_ntoiz(e, key, &num)) {
- lua_pushinteger(L, num);
- } else {
- lua_pushnil(L);
- }
- } else {
- const char *msg = lua_pushfstring(L, "number or string expected, got %s",
- luaL_typename(L, 2));
- return luaL_argerror(L, 2, msg);
- }
- return 1;
-}
-
-static int lupb_enumiter_next(lua_State *L) {
- upb_enum_iter *i = lua_touserdata(L, lua_upvalueindex(1));
- if (upb_enum_done(i)) return 0;
- lua_pushstring(L, upb_enum_iter_name(i));
- lua_pushinteger(L, upb_enum_iter_number(i));
- upb_enum_next(i);
- return 2;
-}
-
-static int lupb_enumdef_values(lua_State *L) {
- const upb_enumdef *e = lupb_enumdef_check(L, 1);
- upb_enum_iter *i = lua_newuserdata(L, sizeof(upb_enum_iter));
- upb_enum_begin(i, e);
- /* Need to guarantee that the enumdef outlives the iter. */
- lua_pushvalue(L, 1);
- lua_pushcclosure(L, &lupb_enumiter_next, 2);
- return 1;
-}
-
-static const struct luaL_Reg lupb_enumdef_mm[] = {
- {"__len", lupb_enumdef_len},
- {NULL, NULL}
-};
-
-static const struct luaL_Reg lupb_enumdef_m[] = {
- LUPB_COMMON_DEF_METHODS
- {"add", lupb_enumdef_add},
- {"value", lupb_enumdef_value},
- {"values", lupb_enumdef_values},
- {NULL, NULL}
-};
-
-
-/* lupb_filedef ***************************************************************/
-
-void lupb_filedef_pushwrapper(lua_State *L, const upb_filedef *f,
- const void *ref_donor) {
- lupb_refcounted_pushwrapper(L, upb_filedef_upcast(f), LUPB_FILEDEF, ref_donor,
- sizeof(void *));
-}
-
-void lupb_filedef_pushnewrapper(lua_State *L, const upb_filedef *f,
- const void *ref_donor) {
- lupb_refcounted_pushnewrapper(L, upb_filedef_upcast(f), LUPB_FILEDEF,
- ref_donor);
-}
-
-const upb_filedef *lupb_filedef_check(lua_State *L, int narg) {
- return lupb_refcounted_check(L, narg, LUPB_FILEDEF);
-}
-
-static upb_filedef *lupb_filedef_checkmutable(lua_State *L, int narg) {
- const upb_filedef *f = lupb_filedef_check(L, narg);
- if (upb_filedef_isfrozen(f))
- luaL_error(L, "not allowed on frozen value");
- return (upb_filedef*)f;
-}
-
-static int lupb_filedef_new(lua_State *L) {
- upb_filedef *f = upb_filedef_new(&f);
- lupb_filedef_pushnewrapper(L, f, &f);
- return 1;
-}
-
-static int lupb_filedef_def(lua_State *L) {
- const upb_filedef *f = lupb_filedef_check(L, 1);
- int index = luaL_checkint(L, 2);
- const upb_def *def = upb_filedef_def(f, index);
-
- if (!def) {
- return luaL_error(L, "index out of range");
- }
-
- lupb_def_pushwrapper(L, def, NULL);
- return 1;
-}
-
-static int lupb_filedef_name(lua_State *L) {
- const upb_filedef *f = lupb_filedef_check(L, 1);
- lua_pushstring(L, upb_filedef_name(f));
- return 1;
-}
-
-static int lupb_filedef_package(lua_State *L) {
- const upb_filedef *f = lupb_filedef_check(L, 1);
- lua_pushstring(L, upb_filedef_package(f));
- return 1;
-}
-
-static int lupb_filedef_len(lua_State *L) {
- const upb_filedef *f = lupb_filedef_check(L, 1);
- lua_pushinteger(L, upb_filedef_defcount(f));
- return 1;
-}
-
-static int lupb_filedef_setname(lua_State *L) {
- upb_filedef *f = lupb_filedef_checkmutable(L, 1);
- CHK(upb_filedef_setname(f, lupb_checkname(L, 2), &status));
- return 0;
-}
-
-static int lupb_filedef_setpackage(lua_State *L) {
- upb_filedef *f = lupb_filedef_checkmutable(L, 1);
- CHK(upb_filedef_setpackage(f, lupb_checkname(L, 2), &status));
- return 0;
-}
-
-static int lupb_filedefiter_next(lua_State *L) {
- const upb_filedef *f = lupb_filedef_check(L, lua_upvalueindex(1));
- int type = lua_tointeger(L, lua_upvalueindex(2));
- size_t i = lua_tointeger(L, lua_upvalueindex(3));
- size_t n = upb_filedef_defcount(f);
-
- for (; i < n; i++) {
- const upb_def *def;
-
- def = upb_filedef_def(f, i);
- UPB_ASSERT(def);
-
- if (type == UPB_DEF_ANY || upb_def_type(def) == type) {
- lua_pushinteger(L, i + 1);
- lua_replace(L, lua_upvalueindex(3));
- lupb_def_pushwrapper(L, def, NULL);
- return 1;
- }
- }
-
- return 0;
-}
-
-static int lupb_filedef_defs(lua_State *L) {
- lupb_filedef_check(L, 1);
- luaL_checkinteger(L, 2); /* Def type. Could make this optional. */
- lua_pushnumber(L, 0); /* Index, starts at zero. */
- lua_pushcclosure(L, &lupb_filedefiter_next, 3);
- return 1;
-}
-
-static const struct luaL_Reg lupb_filedef_mm[] = {
- {"__len", lupb_filedef_len},
- {NULL, NULL}
-};
-
-static const struct luaL_Reg lupb_filedef_m[] = {
- {"def", lupb_filedef_def},
- {"defs", lupb_filedef_defs},
- {"name", lupb_filedef_name},
- {"package", lupb_filedef_package},
-
- {"set_name", lupb_filedef_setname},
- {"set_package", lupb_filedef_setpackage},
-
- {NULL, NULL}
-};
-
-/* lupb_symtab ****************************************************************/
-
-/* Inherits a ref on the symtab.
- * Checks that narg is a proper lupb_symtab object. If it is, leaves its
- * metatable on the stack for cache lookups/updates. */
-const upb_symtab *lupb_symtab_check(lua_State *L, int narg) {
- return lupb_refcounted_check(L, narg, LUPB_SYMTAB);
-}
-
-static upb_symtab *lupb_symtab_checkmutable(lua_State *L, int narg) {
- const upb_symtab *s = lupb_symtab_check(L, narg);
- if (upb_symtab_isfrozen(s))
- luaL_error(L, "not allowed on frozen value");
- return (upb_symtab*)s;
-}
-
-void lupb_symtab_pushwrapper(lua_State *L, const upb_symtab *s,
- const void *ref_donor) {
- lupb_refcounted_pushwrapper(L, upb_symtab_upcast(s), LUPB_SYMTAB, ref_donor,
- sizeof(void *));
-}
-
-void lupb_symtab_pushnewrapper(lua_State *L, const upb_symtab *s,
- const void *ref_donor) {
- lupb_refcounted_pushnewrapper(L, upb_symtab_upcast(s), LUPB_SYMTAB,
- ref_donor);
-}
-
-static int lupb_symtab_new(lua_State *L) {
- upb_symtab *s = upb_symtab_new(&s);
- lupb_symtab_pushnewrapper(L, s, &s);
- return 1;
-}
-
-static int lupb_symtab_freeze(lua_State *L) {
- upb_symtab_freeze(lupb_symtab_checkmutable(L, 1));
- return 0;
-}
-
-static int lupb_symtab_isfrozen(lua_State *L) {
- lua_pushboolean(L, upb_symtab_isfrozen(lupb_symtab_check(L, 1)));
- return 1;
-}
-
-static int lupb_symtab_add(lua_State *L) {
- upb_symtab *s = lupb_symtab_checkmutable(L, 1);
- int n;
- upb_def **defs;
-
- luaL_checktype(L, 2, LUA_TTABLE);
- /* Iterate over table twice. First iteration to count entries and
- * check constraints. */
- n = 0;
- for (lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) {
- lupb_def_checkmutable(L, -1);
- ++n;
- }
-
- /* Second iteration to build deflist.
- * Allocate list with lua_newuserdata() so it is anchored as a GC root in
- * case any Lua functions longjmp(). */
- defs = lua_newuserdata(L, n * sizeof(*defs));
- n = 0;
- for (lua_pushnil(L); lua_next(L, 2); lua_pop(L, 1)) {
- upb_def *def = lupb_def_checkmutable(L, -1);
- defs[n++] = def;
- }
-
- CHK(upb_symtab_add(s, defs, n, NULL, &status));
- return 0;
-}
-
-static int lupb_symtab_addfile(lua_State *L) {
- upb_symtab *s = lupb_symtab_checkmutable(L, 1);
- upb_filedef *f = lupb_filedef_checkmutable(L, 2);
- CHK(upb_symtab_addfile(s, f, &status));
- return 0;
-}
-
-static int lupb_symtab_lookup(lua_State *L) {
- const upb_symtab *s = lupb_symtab_check(L, 1);
- int i;
- for (i = 2; i <= lua_gettop(L); i++) {
- const upb_def *def = upb_symtab_lookup(s, luaL_checkstring(L, i));
- lupb_def_pushwrapper(L, def, NULL);
- lua_replace(L, i);
- }
- return lua_gettop(L) - 1;
-}
-
-static int lupb_symtabiter_next(lua_State *L) {
- upb_symtab_iter *i = lua_touserdata(L, lua_upvalueindex(1));
- if (upb_symtab_done(i)) return 0;
- lupb_def_pushwrapper(L, upb_symtab_iter_def(i), NULL);
- upb_symtab_next(i);
- return 1;
-}
-
-static int lupb_symtab_defs(lua_State *L) {
- const upb_symtab *s = lupb_symtab_check(L, 1);
- upb_deftype_t type = lua_gettop(L) > 1 ? luaL_checkint(L, 2) : UPB_DEF_ANY;
- upb_symtab_iter *i = lua_newuserdata(L, sizeof(upb_symtab_iter));
- upb_symtab_begin(i, s, type);
- /* Need to guarantee that the symtab outlives the iter. */
- lua_pushvalue(L, 1);
- lua_pushcclosure(L, &lupb_symtabiter_next, 2);
- return 1;
-}
-
-static const struct luaL_Reg lupb_symtab_m[] = {
- {"add", lupb_symtab_add},
- {"add_file", lupb_symtab_addfile},
- {"defs", lupb_symtab_defs},
- {"freeze", lupb_symtab_freeze},
- {"is_frozen", lupb_symtab_isfrozen},
- {"lookup", lupb_symtab_lookup},
- {NULL, NULL}
-};
-
-
-/* lupb_array *****************************************************************/
-
-/* A lupb_array provides a strongly-typed array.
- *
- * For the moment we store all values in the userdata's environment table /
- * userval, for simplicity. Later we may wish to move the data into raw
- * memory as both a space and time optimization
- *
- * Compared to regular Lua tables:
- *
- * - we only allow integer indices.
- * - all entries must match the type of the table.
- * - we do not allow "holes" in the array; you can only assign to an existing
- * index or one past the end (which will grow the array by one). */
-
-typedef struct {
- uint32_t size;
- upb_fieldtype_t type;
- const upb_msgdef *msgdef; /* Only when type == UPB_TYPE_MESSAGE */
-} lupb_array;
-
-static lupb_array *lupb_array_check(lua_State *L, int narg) {
- return luaL_checkudata(L, narg, LUPB_ARRAY);
-}
-
-static uint32_t lupb_array_checkindex(lua_State *L, int narg, uint32_t max) {
- uint32_t n = lupb_checkuint32(L, narg);
- if (n == 0 || n > max) { /* Lua uses 1-based indexing. :( */
- luaL_error(L, "Invalid array index.");
- }
- return n;
-}
-
-static int lupb_array_new(lua_State *L) {
- lupb_array *array = newudata_with_userval(L, sizeof(*array), LUPB_ARRAY);
- array->size = 0;
-
- if (lua_type(L, 1) == LUA_TNUMBER) {
- array->type = lupb_checkfieldtype(L, 1);
- if (array->type == UPB_TYPE_MESSAGE) {
- return luaL_error(
- L, "For message arrays construct with the specific message type.");
- }
- } else {
- array->type = UPB_TYPE_MESSAGE;
- array->msgdef = lupb_msgdef_check(L, 1);
-
- /* Store a reference to this msgdef in the environment table to ensure it
- * outlives this array. */
- lua_getuservalue(L, -1);
- lua_pushvalue(L, 1);
- lua_rawseti(L, -2, 0);
- lua_pop(L, 1); /* Pop userval. */
- }
-
- return 1;
-}
-
-static int lupb_array_newindex(lua_State *L) {
- lupb_array *array = lupb_array_check(L, 1);
- uint32_t n = lupb_array_checkindex(L, 2, array->size + 1);
-
- if (n == array->size + 1) {
- array->size++;
- }
-
- if (array->type == UPB_TYPE_MESSAGE) {
- if (array->msgdef != lupb_msg_checkdef(L, 3)) {
- return luaL_error(L, "Tried to assign wrong message type.");
- }
- } else {
- lupb_checkval(L, 3, array->type);
- }
-
- /* Write value to userval table. */
- lua_getuservalue(L, 1);
- lua_pushvalue(L, 3);
- lua_rawseti(L, -2, n);
-
- return 0; /* 1 for chained assignments? */
-}
-
-static int lupb_array_index(lua_State *L) {
- lupb_array *array = lupb_array_check(L, 1);
- uint32_t n = lupb_array_checkindex(L, 2, array->size);
-
- lua_getuservalue(L, 1);
- lua_rawgeti(L, -1, n);
- return 1;
-}
-
-static int lupb_array_len(lua_State *L) {
- lupb_array *array = lupb_array_check(L, 1);
- lua_pushnumber(L, array->size);
- return 1;
-}
-
-static const struct luaL_Reg lupb_array_mm[] = {
- {"__index", lupb_array_index},
- {"__len", lupb_array_len},
- {"__newindex", lupb_array_newindex},
- {NULL, NULL}
-};
-
-/* lupb_msg **************************************************************/
-
-/* A lupb_msg is a userdata where:
- *
- * - the userdata's memory contains hasbits and primitive fields.
- * - the userdata's environment table / uservalue contains references to string
- * fields, submessage fields, and array fields. */
-
-typedef struct {
- const lupb_msgdef *lmd;
- /* Data follows, in a flat buffer. */
-} lupb_msg;
-
-#define MSGDEF_INDEX 0
-
-static bool in_userval(const upb_fielddef *f) {
- return upb_fielddef_isseq(f) || upb_fielddef_issubmsg(f) ||
- upb_fielddef_isstring(f);
-}
-
-static size_t lupb_sizeof(lua_State *L, const upb_fielddef *f) {
- switch (upb_fielddef_type(f)) {
- case UPB_TYPE_BOOL:
- return 1;
- case UPB_TYPE_INT32:
- case UPB_TYPE_UINT32:
- case UPB_TYPE_ENUM:
- case UPB_TYPE_FLOAT:
- return 4;
- case UPB_TYPE_INT64:
- case UPB_TYPE_UINT64:
- case UPB_TYPE_DOUBLE:
- return 8;
- case UPB_TYPE_STRING:
- case UPB_TYPE_BYTES:
- case UPB_TYPE_MESSAGE:
- break;
- }
- lupb_assert(L, false);
- return 0;
-}
-
-static int div_round_up(size_t n, size_t d) {
- int ret = n / d;
- /* If there was a positive remainder, then the result was rounded down and we
- * need to compensate by adding one. */
- if (n % d > 0) ++ret;
- return ret;
-}
-
-static size_t align_up(size_t val, size_t align) {
- return val % align == 0 ? val : val + align - (val % align);
-}
-
-/* If we always read/write as a consistent type to each value, this shouldn't
- * violate aliasing.
- *
- * Note that the slightly prettier option of:
- *
- * *(type*)(&msg->data[ofs])
- *
- * ...is potentially more questionable wrt the C standard and aliasing.
- * Does the expression &msg->data[ofs] "access the stored value"? If so,
- * this would violate aliasing. So instead we use the expression:
- *
- * (char*)msg + sizeof(lupb_msg) + ofs
- *
- * ...which unambigiously is doing nothing but calculating a pointer address. */
-#define CHARPTR_AT(msg, ofs) ((char*)msg + sizeof(lupb_msg) + ofs)
-#define DEREF(msg, ofs, type) *(type*)CHARPTR_AT(msg, ofs)
-
-lupb_msg *lupb_msg_check(lua_State *L, int narg) {
- lupb_msg *msg = luaL_checkudata(L, narg, LUPB_MSG);
- if (!msg->lmd) luaL_error(L, "called into dead msg");
- return msg;
-}
-
-const upb_msgdef *lupb_msg_checkdef(lua_State *L, int narg) {
- return lupb_msg_check(L, narg)->lmd->md;
-}
-
-static const upb_fielddef *lupb_msg_checkfield(lua_State *L,
- const lupb_msgdef *lmd,
- int fieldarg) {
- size_t len;
- const char *fieldname = luaL_checklstring(L, fieldarg, &len);
- const upb_fielddef *f = upb_msgdef_ntof(lmd->md, fieldname, len);
-
- if (!f) {
- const char *msg = lua_pushfstring(L, "no such field: %s", fieldname);
- luaL_argerror(L, fieldarg, msg);
- return NULL; /* Never reached. */
- }
-
- return f;
-}
-
-/* Assigns offsets for storing data in instances of messages for this type, if
- * they have not already been assigned. "narg" should be the stack location of
- * a Lua msgdef object. It should be frozen (if it is not, we will throw an
- * error). It should not throw errors in any other case, since we may have
- * values on our stack that would leak if we longjmp'd across them.
- *
- * TODO(haberman): (if we want to avoid this and be robust against even lua
- * errors due to OOM, we should stop using upb_handlers_newfrozen() and
- * implement it ourselves with a Lua table as cache, since that would get
- * cleaned up properly on error). */
-static lupb_msgdef *lupb_msg_assignoffsets(lua_State *L, int narg) {
- int idx;
- upb_msg_field_iter i;
- lupb_msgdef *lmd = lupb_msgdef_check2(L, narg);
-
- if (!upb_msgdef_isfrozen(lmd->md))
- luaL_error(L, "msgdef must be frozen");
-
- if (lmd->field_offsets) {
- /* Already assigned. */
- return lmd;
- }
-
- {
- int n = upb_msgdef_numfields(lmd->md);
- uint16_t *offsets = malloc(sizeof(*offsets) * n);
-
- /* Offset with the raw data part; starts with hasbits. */
- size_t hasbits_size = div_round_up(n, 8);
- size_t data_ofs = hasbits_size;
- /* Index within the userval.
- * Starts at one to not collide with MSGDEF_INDEX. */
- size_t userval_idx = 1;
-
- /* Assign offsets. */
- for (upb_msg_field_begin(&i, lmd->md);
- !upb_msg_field_done(&i);
- upb_msg_field_next(&i)) {
- upb_fielddef *f = upb_msg_iter_field(&i);
- if (in_userval(f)) {
- offsets[upb_fielddef_index(f)] = userval_idx++;
- } else {
- size_t size = lupb_sizeof(L, f);
- data_ofs = align_up(data_ofs, size);
- offsets[upb_fielddef_index(f)] = data_ofs;
- data_ofs += size;
- }
- }
-
- lmd->field_offsets = offsets;
- lmd->msg_size = sizeof(lupb_msg) + data_ofs;
- lmd->hasbits_size = hasbits_size;
- }
-
- /* Now recursively assign offsets for all submessages, and also add them to
- * the uservalue to ensure that all the lupb_msgdef objects for our
- * submessages outlive us. This is particularly important if/when we build
- * handlers to populate this msgdef. */
-
- lua_pushvalue(L, narg);
- lua_newtable(L); /* This will be our userval. */
-
- idx = 1;
- for (upb_msg_field_begin(&i, lmd->md);
- !upb_msg_field_done(&i);
- upb_msg_field_next(&i)) {
- upb_fielddef *f = upb_msg_iter_field(&i);
- if (upb_fielddef_type(f) == UPB_TYPE_MESSAGE) {
- bool created = lupb_def_pushwrapper(L, upb_fielddef_subdef(f), NULL);
- UPB_ASSERT(!created);
- lupb_msg_assignoffsets(L, -1);
- lua_rawseti(L, -2, idx++); /* Append to uservalue. */
- }
- }
-
- lua_setuservalue(L, -2);
- lua_pop(L, 1); /* copy of msgdef */
-
- return lmd;
-}
-
-void lupb_msg_pushnew(lua_State *L, int narg) {
- lupb_msgdef *lmd = lupb_msg_assignoffsets(L, narg);
- lupb_msg *msg;
-
- /* Add passed-in MessageDef to a table which will become the msg's userval. */
- lua_pushvalue(L, narg);
- lua_newtable(L);
- lua_pushvalue(L, narg);
- lua_rawseti(L, -2, MSGDEF_INDEX);
-
- msg = newudata_with_userval(L, lmd->msg_size, LUPB_MSG);
- memset(msg, 0, lmd->msg_size);
-
- /* Create a msg->msgdef reference, both:
- * 1. a pointer in the userdata itself (for easy access) and */
- msg->lmd = lmd;
-
- /* 2. a reference in Lua-space from the msg's uservalue to the messagedef
- * wrapper object (so the msgdef wrapper object will always outlive us,
- * GC-wise). */
- lua_pushvalue(L, -2); /* Push the table from before. */
- lua_setuservalue(L, -2); /* Pop table, now msg is at top again. */
- lua_remove(L, -2); /* Remove table, so new message is only new val. */
-}
-
-static int lupb_msg_new(lua_State *L) {
- lupb_msg_pushnew(L, 1);
- return 1;
-}
-
-static bool lupb_msg_has(const lupb_msg *msg, const upb_fielddef *f) {
- uint16_t idx = upb_fielddef_index(f);
- return *CHARPTR_AT(msg, idx / 8) & (1 << (idx % 8));
-}
-
-static void lupb_msg_set(lupb_msg *msg, const upb_fielddef *f) {
- uint16_t idx = upb_fielddef_index(f);
- *CHARPTR_AT(msg, idx / 8) |= (1 << (idx % 8));
-}
-
-static int lupb_msg_index(lua_State *L) {
- lupb_msg *msg = lupb_msg_check(L, 1);
- const upb_fielddef *f = lupb_msg_checkfield(L, msg->lmd, 2);
- int ofs;
-
- if (!upb_fielddef_isseq(f) && !lupb_msg_has(msg, f)) {
- lua_pushnil(L);
- return 1;
- }
-
- ofs = msg->lmd->field_offsets[upb_fielddef_index(f)];
-
- if (in_userval(f)) {
- lua_getuservalue(L, 1);
- lua_pushinteger(L, ofs);
- lua_rawget(L, -2);
- } else {
- switch (upb_fielddef_type(f)) {
- case UPB_TYPE_FLOAT:
- lupb_pushfloat(L, DEREF(msg, ofs, float));
- break;
- case UPB_TYPE_DOUBLE:
- lupb_pushdouble(L, DEREF(msg, ofs, double));
- break;
- case UPB_TYPE_BOOL:
- lua_pushboolean(L, DEREF(msg, ofs, bool));
- break;
- case UPB_TYPE_ENUM:
- case UPB_TYPE_INT32:
- lupb_pushint32(L, DEREF(msg, ofs, int32_t));
- break;
- case UPB_TYPE_UINT32:
- lupb_pushuint32(L, DEREF(msg, ofs, uint32_t));
- break;
- case UPB_TYPE_INT64:
- if (LUA_VERSION_NUM < 503) {
- /* Check value? Lua < 5.3.0 has no native integer support, lua_Number
- * is probably double which can't exactly represent large int64s. */
- }
- lupb_pushint64(L, DEREF(msg, ofs, int64_t));
- break;
- case UPB_TYPE_UINT64:
- if (LUA_VERSION_NUM < 503) {
- /* Check value? Lua < 5.3.0 has no native integer support, lua_Number
- * is probably double which can't exactly represent large uint64s. */
- }
- lupb_pushuint64(L, DEREF(msg, ofs, uint64_t));
- break;
- case UPB_TYPE_STRING:
- case UPB_TYPE_BYTES:
- case UPB_TYPE_MESSAGE:
- lupb_assert(L, false);
- break;
- }
- }
-
- return 1;
-}
-
-int lupb_msg_newindex(lua_State *L) {
- lupb_msg *msg = lupb_msg_check(L, 1);
- const upb_fielddef *f = lupb_msg_checkfield(L, msg->lmd, 2);
- int ofs;
-
- lupb_msg_set(msg, f);
-
- ofs = msg->lmd->field_offsets[upb_fielddef_index(f)];
-
- if (in_userval(f)) {
- /* Type-check and then store in the userval. */
- if (upb_fielddef_isseq(f)) {
- lupb_array *array = lupb_array_check(L, 3);
- if (array->type != upb_fielddef_type(f) ||
- (array->type == UPB_TYPE_MESSAGE &&
- array->msgdef != upb_fielddef_msgsubdef(f))) {
- return luaL_error(L, "Array type mismatch");
- }
- } else if (upb_fielddef_isstring(f)) {
- lupb_checkstring(L, 3);
- } else {
- if (lupb_msg_checkdef(L, 3) != upb_fielddef_msgsubdef(f)) {
- return luaL_error(L, "Message type mismatch");
- }
- }
- lua_getuservalue(L, 1);
- lua_pushvalue(L, 3);
- lua_rawseti(L, -2, ofs);
- } else {
- switch (upb_fielddef_type(f)) {
- case UPB_TYPE_FLOAT:
- DEREF(msg, ofs, float) = lupb_checkfloat(L, 3);
- break;
- case UPB_TYPE_DOUBLE:
- DEREF(msg, ofs, double) = lupb_checkdouble(L, 3);
- break;
- case UPB_TYPE_ENUM:
- case UPB_TYPE_INT32:
- DEREF(msg, ofs, int32_t) = lupb_checkint32(L, 3);
- break;
- case UPB_TYPE_UINT32:
- DEREF(msg, ofs, uint32_t) = lupb_checkuint32(L, 3);
- break;
- case UPB_TYPE_INT64:
- DEREF(msg, ofs, int64_t) = lupb_checkint64(L, 3);
- break;
- case UPB_TYPE_UINT64:
- DEREF(msg, ofs, uint64_t) = lupb_checkuint64(L, 3);
- break;
- case UPB_TYPE_BOOL:
- DEREF(msg, ofs, bool) = lupb_checkbool(L, 3);
- break;
- case UPB_TYPE_STRING:
- case UPB_TYPE_BYTES:
- case UPB_TYPE_MESSAGE:
- lupb_assert(L, false);
- }
- }
-
- return 0; /* 1 for chained assignments? */
-}
-
-static const struct luaL_Reg lupb_msg_mm[] = {
- {"__index", lupb_msg_index},
- {"__newindex", lupb_msg_newindex},
- {NULL, NULL}
-};
-
-
-/* lupb_msg populating handlers ***********************************************/
-
-/* NOTE: doesn't support repeated or submessage fields yet. Coming soon. */
-
-typedef struct {
- uint32_t ofs;
- uint32_t hasbit;
-} lupb_handlerdata;
-
-static void lupb_sethasbit(lupb_msg *msg, uint32_t hasbit) {
- *CHARPTR_AT(msg, hasbit / 8) |= 1 << (hasbit % 8);
-}
-
-static size_t strhandler(void *closure, const void *hd, const char *str,
- size_t len, const upb_bufhandle *handle) {
- lupb_msg *msg = closure;
- const lupb_handlerdata *data = hd;
- lua_State *L = msg->lmd->L;
- UPB_UNUSED(handle);
-
- lua_pushlstring(L, str, len);
- lua_rawseti(L, -2, data->ofs);
- lupb_sethasbit(msg, data->hasbit);
- return len;
-}
-
-const void *newhandlerdata(upb_handlers *h, uint32_t ofs, uint32_t hasbit) {
- lupb_handlerdata *data = malloc(sizeof(*data));
- data->ofs = ofs;
- data->hasbit = hasbit;
- upb_handlers_addcleanup(h, data, free);
- return data;
-}
-
-void callback(const void *closure, upb_handlers *h) {
- lua_State *L = (lua_State*)closure;
- lupb_msgdef *lmd;
- upb_msg_field_iter i;
-
- lupb_def_pushwrapper(L, upb_msgdef_upcast(upb_handlers_msgdef(h)), NULL);
- lmd = lupb_msg_assignoffsets(L, -1);
- upb_msg_field_begin(&i, upb_handlers_msgdef(h));
- for (; !upb_msg_field_done(&i); upb_msg_field_next(&i)) {
- upb_fielddef *f = upb_msg_iter_field(&i);
- int hasbit = upb_fielddef_index(f);
- uint16_t ofs = lmd->field_offsets[upb_fielddef_index(f)];
- if (upb_fielddef_isseq(f)) {
- luaL_error(L, "Doesn't support repeated fields yet.");
- } else {
- switch (upb_fielddef_type(f)) {
- case UPB_TYPE_BOOL:
- case UPB_TYPE_INT32:
- case UPB_TYPE_UINT32:
- case UPB_TYPE_ENUM:
- case UPB_TYPE_FLOAT:
- case UPB_TYPE_INT64:
- case UPB_TYPE_UINT64:
- case UPB_TYPE_DOUBLE:
- hasbit += sizeof(lupb_msg) * 8;
- ofs += sizeof(lupb_msg);
- upb_shim_set(h, f, ofs, hasbit);
- break;
- case UPB_TYPE_STRING:
- case UPB_TYPE_BYTES: {
- upb_handlerattr attr = UPB_HANDLERATTR_INITIALIZER;
- upb_handlerattr_sethandlerdata(&attr, newhandlerdata(h, ofs, hasbit));
- /* XXX: does't currently handle split buffers. */
- upb_handlers_setstring(h, f, strhandler, &attr);
- upb_handlerattr_uninit(&attr);
- break;
- }
- case UPB_TYPE_MESSAGE:
- luaL_error(L, "Doesn't support submessages yet.");
- break;
- }
- }
- }
- lua_pop(L, 1); /* msgdef wrapper */
-}
-
-const upb_handlers *lupb_msg_newwritehandlers(lua_State *L, int narg,
- const void *owner) {
- lupb_msgdef *lmd = lupb_msg_assignoffsets(L, narg);
- return upb_handlers_newfrozen(lmd->md, owner, callback, L);
-}
-
-
-/* lupb toplevel **************************************************************/
-
-static int lupb_freeze(lua_State *L) {
- int n = lua_gettop(L);
- int i;
- /* Scratch memory; lua_newuserdata() anchors it as a GC root in case any Lua
- * functions fail. */
- upb_def **defs = lua_newuserdata(L, n * sizeof(upb_def*));
-
- for (i = 0; i < n; i++) {
- /* Could allow an array of defs here also. */
- defs[i] = lupb_def_checkmutable(L, i + 1);
- }
- CHK(upb_def_freeze(defs, n, &status));
- return 0;
-}
-
-/* This is a *temporary* API that will be removed once pending refactorings are
- * complete (it does not belong here in core because it depends on both
- * the descriptor.proto schema and the protobuf binary format. */
-static int lupb_loaddescriptor(lua_State *L) {
- size_t len;
- const char *str = luaL_checklstring(L, 1, &len);
- size_t i;
- upb_filedef **files = NULL;
- CHK(files = upb_loaddescriptor(str, len, &files, &status));
-
- lua_newtable(L);
- for (i = 1; *files; i++, files++) {
- lupb_filedef_pushnewrapper(L, *files, &files);
- lua_rawseti(L, -2, i);
- }
-
- return 1;
-}
static const struct luaL_Reg lupb_toplevel_m[] = {
- {"Array", lupb_array_new},
- {"EnumDef", lupb_enumdef_new},
- {"FieldDef", lupb_fielddef_new},
- {"FileDef", lupb_filedef_new},
- {"Message", lupb_msg_new},
- {"MessageDef", lupb_msgdef_new},
- {"OneofDef", lupb_oneofdef_new},
- {"SymbolTable", lupb_symtab_new},
- {"freeze", lupb_freeze},
- {"load_descriptor", lupb_loaddescriptor},
-
{NULL, NULL}
};
void lupb_register_type(lua_State *L, const char *name, const luaL_Reg *m,
- const luaL_Reg *mm, bool refcount_gc) {
+ const luaL_Reg *mm) {
luaL_newmetatable(L, name);
if (mm) {
lupb_setfuncs(L, mm);
}
- if (refcount_gc) {
- lupb_setfuncs(L, lupb_refcounted_mm);
- }
-
if (m) {
/* Methods go in the mt's __index method. This implies that you can'
* implement __index and also have methods. */
@@ -2083,102 +232,14 @@ void lupb_register_type(lua_State *L, const char *name, const luaL_Reg *m,
lua_pop(L, 1); /* The mt. */
}
-static void lupb_setfieldi(lua_State *L, const char *field, int i) {
- lua_pushinteger(L, i);
- lua_setfield(L, -2, field);
-}
-
int luaopen_upb_c(lua_State *L) {
static char module_key;
if (lupb_openlib(L, &module_key, "upb_c", lupb_toplevel_m)) {
return 1;
}
- /* Non-refcounted types. */
- lupb_register_type(L, LUPB_ARRAY, NULL, lupb_array_mm, false);
- lupb_register_type(L, LUPB_MSG, NULL, lupb_msg_mm, false);
-
- /* Refcounted types. */
- lupb_register_type(L, LUPB_ENUMDEF, lupb_enumdef_m, lupb_enumdef_mm, true);
- lupb_register_type(L, LUPB_FIELDDEF, lupb_fielddef_m, NULL, true);
- lupb_register_type(L, LUPB_FILEDEF, lupb_filedef_m, lupb_filedef_mm, true);
- lupb_register_type(L, LUPB_ONEOFDEF, lupb_oneofdef_m, lupb_oneofdef_mm, true);
- lupb_register_type(L, LUPB_SYMTAB, lupb_symtab_m, NULL, true);
-
- /* Refcounted but with custom __gc. */
- lupb_register_type(L, LUPB_MSGDEF, lupb_msgdef_m, lupb_msgdef_mm, false);
-
- /* Create our object cache. */
- lua_newtable(L);
- lua_createtable(L, 0, 1); /* Cache metatable. */
- lua_pushstring(L, "v"); /* Values are weak. */
- lua_setfield(L, -2, "__mode");
- lua_setmetatable(L, -2);
- lua_setfield(L, LUA_REGISTRYINDEX, LUPB_OBJCACHE);
-
- /* Register constants. */
- lupb_setfieldi(L, "LABEL_OPTIONAL", UPB_LABEL_OPTIONAL);
- lupb_setfieldi(L, "LABEL_REQUIRED", UPB_LABEL_REQUIRED);
- lupb_setfieldi(L, "LABEL_REPEATED", UPB_LABEL_REPEATED);
-
- lupb_setfieldi(L, "TYPE_DOUBLE", UPB_TYPE_DOUBLE);
- lupb_setfieldi(L, "TYPE_FLOAT", UPB_TYPE_FLOAT);
- lupb_setfieldi(L, "TYPE_INT64", UPB_TYPE_INT64);
- lupb_setfieldi(L, "TYPE_UINT64", UPB_TYPE_UINT64);
- lupb_setfieldi(L, "TYPE_INT32", UPB_TYPE_INT32);
- lupb_setfieldi(L, "TYPE_BOOL", UPB_TYPE_BOOL);
- lupb_setfieldi(L, "TYPE_STRING", UPB_TYPE_STRING);
- lupb_setfieldi(L, "TYPE_MESSAGE", UPB_TYPE_MESSAGE);
- lupb_setfieldi(L, "TYPE_BYTES", UPB_TYPE_BYTES);
- lupb_setfieldi(L, "TYPE_UINT32", UPB_TYPE_UINT32);
- lupb_setfieldi(L, "TYPE_ENUM", UPB_TYPE_ENUM);
-
- lupb_setfieldi(L, "INTFMT_VARIABLE", UPB_INTFMT_VARIABLE);
- lupb_setfieldi(L, "INTFMT_FIXED", UPB_INTFMT_FIXED);
- lupb_setfieldi(L, "INTFMT_ZIGZAG", UPB_INTFMT_ZIGZAG);
-
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_DOUBLE", UPB_DESCRIPTOR_TYPE_DOUBLE);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_FLOAT", UPB_DESCRIPTOR_TYPE_FLOAT);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_INT64", UPB_DESCRIPTOR_TYPE_INT64);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_UINT64", UPB_DESCRIPTOR_TYPE_UINT64);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_INT32", UPB_DESCRIPTOR_TYPE_INT32);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_FIXED64", UPB_DESCRIPTOR_TYPE_FIXED64);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_FIXED32", UPB_DESCRIPTOR_TYPE_FIXED32);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_BOOL", UPB_DESCRIPTOR_TYPE_BOOL);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_STRING", UPB_DESCRIPTOR_TYPE_STRING);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_GROUP", UPB_DESCRIPTOR_TYPE_GROUP);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_MESSAGE", UPB_DESCRIPTOR_TYPE_MESSAGE);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_BYTES", UPB_DESCRIPTOR_TYPE_BYTES);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_UINT32", UPB_DESCRIPTOR_TYPE_UINT32);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_ENUM", UPB_DESCRIPTOR_TYPE_ENUM);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_SFIXED32", UPB_DESCRIPTOR_TYPE_SFIXED32);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_SFIXED64", UPB_DESCRIPTOR_TYPE_SFIXED64);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_SINT32", UPB_DESCRIPTOR_TYPE_SINT32);
- lupb_setfieldi(L, "DESCRIPTOR_TYPE_SINT64", UPB_DESCRIPTOR_TYPE_SINT64);
-
- lupb_setfieldi(L, "DEF_MSG", UPB_DEF_MSG);
- lupb_setfieldi(L, "DEF_FIELD", UPB_DEF_FIELD);
- lupb_setfieldi(L, "DEF_ENUM", UPB_DEF_ENUM);
- lupb_setfieldi(L, "DEF_SERVICE", UPB_DEF_SERVICE);
- lupb_setfieldi(L, "DEF_ANY", UPB_DEF_ANY);
-
- lupb_setfieldi(L, "HANDLER_INT32", UPB_HANDLER_INT32);
- lupb_setfieldi(L, "HANDLER_INT64", UPB_HANDLER_INT64);
- lupb_setfieldi(L, "HANDLER_UINT32", UPB_HANDLER_UINT32);
- lupb_setfieldi(L, "HANDLER_UINT64", UPB_HANDLER_UINT64);
- lupb_setfieldi(L, "HANDLER_FLOAT", UPB_HANDLER_FLOAT);
- lupb_setfieldi(L, "HANDLER_DOUBLE", UPB_HANDLER_DOUBLE);
- lupb_setfieldi(L, "HANDLER_BOOL", UPB_HANDLER_BOOL);
- lupb_setfieldi(L, "HANDLER_STARTSTR", UPB_HANDLER_STARTSTR);
- lupb_setfieldi(L, "HANDLER_STRING", UPB_HANDLER_STRING);
- lupb_setfieldi(L, "HANDLER_ENDSTR", UPB_HANDLER_ENDSTR);
- lupb_setfieldi(L, "HANDLER_STARTSUBMSG", UPB_HANDLER_STARTSUBMSG);
- lupb_setfieldi(L, "HANDLER_ENDSUBMSG", UPB_HANDLER_ENDSUBMSG);
- lupb_setfieldi(L, "HANDLER_STARTSEQ", UPB_HANDLER_STARTSEQ);
- lupb_setfieldi(L, "HANDLER_ENDSEQ", UPB_HANDLER_ENDSEQ);
-
- lupb_setfieldi(L, "SYNTAX_PROTO2", UPB_SYNTAX_PROTO2);
- lupb_setfieldi(L, "SYNTAX_PROTO3", UPB_SYNTAX_PROTO3);
+ lupb_def_registertypes(L);
+ lupb_msg_registertypes(L);
return 1; /* Return package table. */
}
diff --git a/upb/bindings/lua/upb.h b/upb/bindings/lua/upb.h
index f37ed9a..30fc59d 100644
--- a/upb/bindings/lua/upb.h
+++ b/upb/bindings/lua/upb.h
@@ -8,6 +8,7 @@
#include "lauxlib.h"
#include "upb/def.h"
#include "upb/handlers.h"
+#include "upb/msg.h"
#include "upb/symtab.h"
/* Lua 5.1/5.2 compatibility code. */
@@ -23,10 +24,14 @@
void *luaL_testudata(lua_State *L, int ud, const char *tname);
+#define lupb_setfuncs(L, l) luaL_register(L, NULL, l)
+
#elif LUA_VERSION_NUM == 502
int luaL_typerror(lua_State *L, int narg, const char *tname);
+#define lupb_setfuncs(L, l) luaL_setfuncs(L, l, 0)
+
#else
#error Only Lua 5.1 and 5.2 are supported
#endif
@@ -69,6 +74,7 @@ uint32_t lupb_checkuint32(lua_State *L, int narg);
double lupb_checkdouble(lua_State *L, int narg);
float lupb_checkfloat(lua_State *L, int narg);
bool lupb_checkbool(lua_State *L, int narg);
+const char *lupb_checkstring(lua_State *L, int narg, size_t *len);
const char *lupb_checkname(lua_State *L, int narg);
void lupb_pushint64(lua_State *L, int64_t val);
@@ -77,10 +83,30 @@ void lupb_pushuint64(lua_State *L, uint64_t val);
void lupb_pushuint32(lua_State *L, uint32_t val);
void lupb_pushdouble(lua_State *L, double val);
void lupb_pushfloat(lua_State *L, float val);
-void lupb_pushbool(lua_State *L, bool val);
-/* Functions for getting/pushing wrappers to various types defined in the
- * core library. */
+/* Builds and returns a handlers object for populating a lupb_msg described by
+ * the MessageDef at "narg".
+ *
+ * TODO(haberman): factor this so it doesn't have to take a lua_State. We
+ * should be able to generate message handlers for a upb_msgdef that can be used
+ * across many Lua states, so we can shared JIT code across lua_States. */
+const upb_handlers *lupb_msg_newwritehandlers(lua_State *L, int narg,
+ const void *owner);
+
+/* Registers a type with the given name, methods, and metamethods.
+ * If "refcount_gc" is true, adds a __gc metamethod that does an unref.
+ * Refcounted types must be allocated with lupb_refcounted_push[new]wrapper. */
+void lupb_register_type(lua_State *L, const char *name, const luaL_Reg *m,
+ const luaL_Reg *mm);
+
+/* Checks the given upb_status and throws a Lua error if it is not ok. */
+void lupb_checkstatus(lua_State *L, upb_status *s);
+
+
+/** From def.c. ***************************************************************/
+
+upb_fieldtype_t lupb_checkfieldtype(lua_State *L, int narg);
+
void *lupb_refcounted_check(lua_State *L, int narg, const char *type);
const upb_msgdef *lupb_msg_checkdef(lua_State *L, int narg);
const upb_msgdef *lupb_msgdef_check(lua_State *L, int narg);
@@ -101,26 +127,22 @@ void lupb_symtab_pushwrapper(lua_State *L, const upb_symtab *s,
void lupb_symtab_pushnewrapper(lua_State *L, const upb_symtab *s,
const void *ref_donor);
-/* For constructing a new message. narg is the Lua value for the MessageDef
- * object. */
-void lupb_msg_pushnew(lua_State *L, int narg);
+void lupb_def_registertypes(lua_State *L);
-/* Builds and returns a handlers object for populating a lupb_msg described by
- * the MessageDef at "narg".
- *
- * TODO(haberman): factor this so it doesn't have to take a lua_State. We
- * should be able to generate message handlers for a upb_msgdef that can be used
- * across many Lua states, so we can shared JIT code across lua_States. */
-const upb_handlers *lupb_msg_newwritehandlers(lua_State *L, int narg,
- const void *owner);
+int lupb_refcounted_gc(lua_State *L);
-/* Registers a type with the given name, methods, and metamethods.
- * If "refcount_gc" is true, adds a __gc metamethod that does an unref.
- * Refcounted types must be allocated with lupb_refcounted_push[new]wrapper. */
-void lupb_register_type(lua_State *L, const char *name, const luaL_Reg *m,
- const luaL_Reg *mm, bool refcount_gc);
-/* Checks the given upb_status and throws a Lua error if it is not ok. */
-void lupb_checkstatus(lua_State *L, upb_status *s);
+/** From msg.c. ***************************************************************/
+
+struct lupb_msgclass;
+typedef struct lupb_msgclass lupb_msgclass;
+
+upb_arena *lupb_arena_check(lua_State *L, int narg);
+int lupb_arena_new(lua_State *L);
+int lupb_msg_pushref(lua_State *L, int msgclass, void *msg);
+
+const upb_msglayout *lupb_msgclass_getlayout(lua_State *L, int narg);
+const upb_handlers *lupb_msgclass_getmergehandlers(lua_State *L, int narg);
+void lupb_msg_registertypes(lua_State *L);
#endif /* UPB_LUA_UPB_H_ */
diff --git a/upb/bindings/lua/upb/msg.c b/upb/bindings/lua/upb/msg.c
new file mode 100644
index 0000000..9aa5c2b
--- /dev/null
+++ b/upb/bindings/lua/upb/msg.c
@@ -0,0 +1,892 @@
+
+/* lupb_msgfactory **************************************************************/
+
+/* Userval contains a map of:
+ * [1] = SymbolTable (to keep GC-reachable)
+ * const upb_msgdef* -> lupb_msgclass
+ */
+
+#define LUPB_MSGFACTORY_SYMTAB 1
+
+typedef struct lupb_msgfactory {
+ upb_msgfactory *factory;
+} lupb_msgfactory;
+
+static int lupb_msgclass_pushnew(lua_State *L, int factory, const upb_msglayout *l);
+
+static lupb_msgfactory *lupb_msgfactory_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_MSGFACTORY);
+}
+
+static int lupb_msgfactory_new(lua_State *L) {
+ const upb_symtab *symtab = lupb_symtab_check(L, 1);
+
+ lupb_msgfactory *lmsgfactory =
+ lupb_newuserdata(L, sizeof(lupb_msgfactory), LUPB_MSGFACTORY);
+ lmsgfactory->factory = upb_msgfactory_new(symtab);
+ lupb_uservalseti(L, -1, LUPB_MSGFACTORY_SYMTAB, 1);
+
+ return 1;
+}
+
+static int lupb_msgfactory_gc(lua_State *L) {
+ lupb_msgfactory *lfactory = lupb_msgfactory_check(L, 1);
+
+ if (lfactory->factory) {
+ upb_msgfactory_free(lfactory->factory);
+ lfactory->factory = NULL;
+ }
+
+ return 0;
+}
+
+static void lupb_msgfactory_pushmsgclass(lua_State *L, int narg,
+ const upb_msgdef *md) {
+ const lupb_msgfactory *lfactory = lupb_msgfactory_check(L, narg);
+
+ lua_getuservalue(L, narg);
+ lua_pushlightuserdata(L, (void*)md);
+ lua_rawget(L, -2);
+
+ if (lua_isnil(L, -1)) {
+ /* TODO: verify md is in symtab? */
+ lupb_msgclass_pushnew(L, narg,
+ upb_msgfactory_getlayout(lfactory->factory, md));
+
+ /* Set in userval. */
+ lua_pushlightuserdata(L, (void*)md);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, -4);
+ }
+}
+
+static int lupb_msgfactory_getmsgclass(lua_State *L) {
+ lupb_msgfactory_pushmsgclass(L, 1, lupb_msgdef_check(L, 2));
+ return 1;
+}
+
+static const struct luaL_Reg lupb_msgfactory_m[] = {
+ {"get_message_class", lupb_msgfactory_getmsgclass},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg lupb_msgfactory_mm[] = {
+ {"__gc", lupb_msgfactory_gc},
+ {NULL, NULL}
+};
+
+
+/* lupb_msgclass **************************************************************/
+
+#define LUPB_MSGCLASS_FACTORY 1
+#define LUPB_MSGCLASS_MSGDEF 2
+
+typedef struct lupb_msgclass {
+ const upb_msglayout *layout;
+ const lupb_msgfactory *lfactory;
+} lupb_msgclass;
+
+/* Checks that the given object is a lupb_msg with the given lmsgclass. */
+static upb_msgval lupb_msg_typecheck(lua_State *L, int narg,
+ const lupb_msgclass *lmsgclass);
+
+/* Type-checks for assigning to a message field. */
+static upb_msgval lupb_array_typecheck(lua_State *L, int narg, int msg,
+ const upb_fielddef *f);
+static upb_msgval lupb_map_typecheck(lua_State *L, int narg, int msg,
+ const upb_fielddef *f);
+static const lupb_msgclass *lupb_msg_getsubmsgclass(lua_State *L, int narg,
+ const upb_fielddef *f);
+static const lupb_msgclass *lupb_msg_msgclassfor(lua_State *L, int narg,
+ const upb_msgdef *md);
+
+static lupb_msgclass *lupb_msgclass_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_MSGCLASS);
+}
+
+static void lupb_msgclass_typecheck(lua_State *L, const lupb_msgclass *expected,
+ const lupb_msgclass *actual) {
+ if (expected != actual) {
+ const upb_msgdef *msgdef = upb_msglayout_msgdef(expected->layout);
+ /* TODO: better error message. */
+ luaL_typerror(L, 3, upb_msgdef_fullname(msgdef));
+ }
+}
+
+static const lupb_msgclass *lupb_msgclass_msgclassfor(lua_State *L, int narg,
+ const upb_msgdef *md) {
+ lupb_uservalgeti(L, narg, LUPB_MSGCLASS_FACTORY);
+ lupb_msgfactory_pushmsgclass(L, -1, md);
+ return lupb_msgclass_check(L, -1);
+}
+
+static const lupb_msgclass *lupb_msgclass_getsubmsgclass(lua_State *L, int narg,
+ const upb_fielddef *f) {
+ /* If we wanted we could try to optimize this by caching these pointers in our
+ * msgclass, in an array indexed by field index. We would still need to fall
+ * back to calling msgclassfor(), unless we wanted to eagerly create
+ * message classes for all submessages. But for big schemas that might be a
+ * lot of things to build, and we might end up not using most of them. */
+ return lupb_msgclass_msgclassfor(L, narg, upb_fielddef_msgsubdef(f));
+}
+
+static int lupb_msgclass_pushnew(lua_State *L, int factory, const upb_msglayout *l) {
+ const lupb_msgfactory *lfactory = lupb_msgfactory_check(L, factory);
+ lupb_msgclass *lmc = lupb_newuserdata(L, sizeof(*lmc), LUPB_MSGCLASS);
+
+ lupb_uservalseti(L, -1, LUPB_MSGCLASS_FACTORY, factory);
+ lmc->layout = l;
+ lmc->lfactory = lfactory;
+
+ return 1;
+}
+
+static int lupb_msgclass_call(lua_State *L) {
+ lupb_msg_pushnew(L, 1);
+ return 1;
+}
+
+static const struct luaL_Reg lupb_msgclass_mm[] = {
+ {"__call", lupb_msgclass_call},
+ {NULL, NULL}
+};
+
+
+
+
+/* lupb_string ****************************************************************/
+
+/* A wrapper around a Lua string.
+ *
+ * This type is NOT exposed to users. Users deal with plain Lua strings.
+ *
+ * This type exists for two reasons:
+ *
+ * 1. To provide storage for a upb_string, which is required for interoperating
+ * with upb_msg. It allows upb to visit string data structures without
+ * calling into Lua.
+ * 2. To cache a string's UTF-8 validity. We want to validate that a string is
+ * valid UTF-8 before allowing it to be assigned to a string field. However
+ * if a string is assigned from one message to another, or assigned to
+ * multiple message fields, we don't want to force the UTF-8 check again. We
+ * cache inside this object if the UTF-8 check has been performed.
+ *
+ * TODO(haberman): is this slightly too clever? If we just exposed this object
+ * directly to Lua we could get rid of the cache. But then the object we expose
+ * to users wouldn't be a true string, so expressions like this would fail:
+ *
+ * if msg.string_field == "abc" then
+ * -- ...
+ * end
+ *
+ * Instead users would have to say this, which seems like a drag:
+ *
+ * if tostring(msg.string_field) == "abc" then
+ * -- ...
+ * end
+ */
+
+typedef struct {
+ enum ValidUtf8 {
+ UTF8_UNCHECKED = 0,
+ UTF8_VALID = 1,
+ UTF8_INVALID = 2
+ } utf8_validity; /* Possibly move this into upb_string at some point. */
+ /* upb_string follows. */
+} lupb_string;
+
+#define LUPB_STRING_INDEX 1 /* The index where we reference the Lua string. */
+
+static upb_string *lupb_string_upbstr(lupb_string *lstring) {
+ return lupb_structafter(&lstring[1]);
+}
+
+static size_t lupb_string_sizeof() {
+ return lupb_sizewithstruct(sizeof(lupb_string), upb_string_sizeof());
+}
+
+/* The cache maps char* (lightuserdata) -> lupb_string userdata. The char* is
+ * the string data from a Lua string object. In practice Lua string objects
+ * have a stable char* for the actual string data, so we can safely key by this.
+ * See: http://lua-users.org/lists/lua-l/2011-06/msg00401.html
+ *
+ * The cache's values are weak, so cache entries can be collected if this string
+ * is no longer a member of any message, array, or map. Keeping real Lua
+ * strings as weak keys is not possible, because Lua does make strings subject
+ * to weak collection, so this would prevent these strings from ever being
+ * collected. */
+static void lupb_string_pushcache(lua_State *L) {
+ static char key;
+ lua_pushlightuserdata(L, &key);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+
+ /* Lazily create. */
+ if (lua_isnil(L, -1)) {
+ lua_pop(L, 1); /* nil. */
+ lua_newtable(L);
+ lua_createtable(L, 0, 1); /* Cache metatable. */
+ lua_pushstring(L, "v"); /* Values are weak. */
+ lua_setfield(L, -2, "__mode");
+ lua_setmetatable(L, -2);
+ lua_pushlightuserdata(L, &key);
+ lua_pushvalue(L, -2); /* Cache. */
+ lua_rawset(L, LUA_REGISTRYINDEX);
+ }
+}
+
+static lupb_string *lupb_string_pushwrapper(lua_State *L, int narg) {
+ const char *str;
+ size_t len;
+ lupb_string *lstring;
+
+ lupb_checkstring(L, narg);
+ str = lua_tolstring(L, narg, &len);
+ lupb_string_pushcache(L);
+ lua_pushlightuserdata(L, (void*)str);
+ lua_rawget(L, -2);
+
+ if (lua_isnil(L, -1)) {
+ /* String wasn't in cache, need to create it. */
+ lua_pop(L, 1); /* nil. */
+ lstring = lupb_newuserdata(L, lupb_string_sizeof(), LUPB_STRING);
+ lstring->utf8_validity = UTF8_UNCHECKED;
+ upb_string_set(lupb_string_upbstr(lstring), str, len);
+ lua_pushlightuserdata(L, (void*)str);
+ lua_pushvalue(L, -2);
+ /* Stack is [cache, lupb_string, str, lupb_string]. */
+ lua_rawset(L, -4);
+
+ /* Need to create a reference to the underlying string object, so
+ * lupb_string keeps it alive. */
+ lupb_uservalseti(L, -1, LUPB_STRING_INDEX, narg);
+ } else {
+ lstring = lua_touserdata(L, -1);
+ }
+
+ lua_remove(L, -2); /* cache. */
+ return lstring;
+}
+
+/* The value at narg should be a Lua string object. This will push a wrapper
+ * object (which may be from the cache). Returns a upb_string* that is valid
+ * for as long as the pushed object is alive.
+ *
+ * This object should only be used internally, and not exposed to users! */
+static upb_msgval lupb_string_pushbyteswrapper(lua_State *L, int narg) {
+ lupb_string *lstring = lupb_string_pushwrapper(L, narg);
+ return upb_msgval_str(lupb_string_upbstr(lstring));
+}
+
+/* Like lupb_string_pushbyteswrapper(), except it also validates that the string
+ * is valid UTF-8 (if we haven't already) and throws an error if not. */
+static upb_msgval lupb_string_pushstringwrapper(lua_State *L, int narg) {
+ lupb_string *lstring = lupb_string_pushwrapper(L, narg);
+
+ if (lstring->utf8_validity == UTF8_UNCHECKED) {
+ if (true /* TODO: check UTF-8 */) {
+ lstring->utf8_validity = UTF8_VALID;
+ } else {
+ lstring->utf8_validity = UTF8_INVALID;
+ }
+ }
+
+ if (lstring->utf8_validity != UTF8_VALID) {
+ luaL_error(L, "String is not valid UTF-8");
+ }
+
+ return upb_msgval_str(lupb_string_upbstr(lstring));
+}
+
+/* Given a previously pushed wrapper object, unwraps it and pushes the plain
+ * string object underneath. This is the only object we should expose to users.
+ */
+static void lupb_string_unwrap(lua_State *L, int arg) {
+ lupb_uservalgeti(L, arg, LUPB_STRING_INDEX);
+}
+
+
+/* upb <-> Lua type conversion ************************************************/
+
+static bool lupb_isstring(upb_fieldtype_t type) {
+ return type == UPB_TYPE_STRING || type == UPB_TYPE_BYTES;
+}
+
+static bool lupb_istypewrapped(upb_fieldtype_t type) {
+ return type == UPB_TYPE_STRING || type == UPB_TYPE_BYTES ||
+ type == UPB_TYPE_MESSAGE;
+}
+
+static upb_msgval lupb_tomsgval(lua_State *L, upb_fieldtype_t type, int narg,
+ const lupb_msgclass *lmsgclass,
+ bool *pushed_luaobj) {
+ switch (type) {
+ case UPB_TYPE_INT32:
+ case UPB_TYPE_ENUM:
+ return upb_msgval_int32(lupb_checkint32(L, narg));
+ case UPB_TYPE_INT64:
+ return upb_msgval_int64(lupb_checkint64(L, narg));
+ case UPB_TYPE_UINT32:
+ return upb_msgval_uint32(lupb_checkuint32(L, narg));
+ case UPB_TYPE_UINT64:
+ return upb_msgval_uint64(lupb_checkuint64(L, narg));
+ case UPB_TYPE_DOUBLE:
+ return upb_msgval_double(lupb_checkdouble(L, narg));
+ case UPB_TYPE_FLOAT:
+ return upb_msgval_float(lupb_checkfloat(L, narg));
+ case UPB_TYPE_BOOL:
+ return upb_msgval_bool(lupb_checkbool(L, narg));
+ case UPB_TYPE_STRING:
+ /* For map lookup by key, we might want a lighter-weight way of creating a
+ * temporary string. */
+ *pushed_luaobj = true;
+ return lupb_string_pushstringwrapper(L, narg);
+ case UPB_TYPE_BYTES:
+ *pushed_luaobj = true;
+ return lupb_string_pushbyteswrapper(L, narg);
+ case UPB_TYPE_MESSAGE:
+ UPB_ASSERT(lmsgclass);
+ *pushed_luaobj = true;
+ lua_pushvalue(L, narg);
+ return lupb_msg_typecheck(L, narg, lmsgclass);
+ }
+}
+
+static void lupb_pushmsgval(lua_State *L, upb_fieldtype_t type,
+ upb_msgval val) {
+ switch (type) {
+ case UPB_TYPE_INT32:
+ case UPB_TYPE_ENUM:
+ lupb_pushint32(L, upb_msgval_getint32(val));
+ break;
+ case UPB_TYPE_INT64:
+ lupb_pushint64(L, upb_msgval_getint64(val));
+ break;
+ case UPB_TYPE_UINT32:
+ lupb_pushuint32(L, upb_msgval_getuint32(val));
+ break;
+ case UPB_TYPE_UINT64:
+ lupb_pushuint64(L, upb_msgval_getuint64(val));
+ break;
+ case UPB_TYPE_DOUBLE:
+ lupb_pushdouble(L, upb_msgval_getdouble(val));
+ break;
+ case UPB_TYPE_FLOAT:
+ lupb_pushfloat(L, upb_msgval_getdouble(val));
+ break;
+ case UPB_TYPE_BOOL:
+ lupb_pushbool(L, upb_msgval_getbool(val));
+ break;
+ case UPB_TYPE_STRING:
+ case UPB_TYPE_BYTES:
+ case UPB_TYPE_MESSAGE:
+ lupb_assert(L, false);
+ }
+}
+
+
+/* lupb_array *****************************************************************/
+
+/* A strongly typed array. Implemented by wrapping upb_array.
+ *
+ * - we only allow integer indices.
+ * - all entries must have the correct type.
+ * - we do not allow "holes" in the array; you can only assign to an existing
+ * index or one past the end (which will grow the array by one).
+ */
+
+typedef struct {
+ /* Only needed for array of message. This wastes space in the non-message
+ * case but simplifies the code. Could optimize away if desired. */
+ lupb_msgclass *lmsgclass;
+
+ /* upb_array follows. */
+} lupb_array;
+
+static size_t lupb_array_sizeof(upb_fieldtype_t type) {
+ return lupb_sizewithstruct(sizeof(lupb_array), upb_array_sizeof(type));
+}
+
+static upb_array *lupb_array_upbarr(lupb_array *arr) {
+ return lupb_structafter(&arr[1]);
+}
+
+static lupb_array *lupb_array_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_ARRAY);
+}
+
+static upb_array *lupb_array_check2(lua_State *L, int narg) {
+ return lupb_array_upbarr(lupb_array_check(L, narg));
+}
+
+static upb_msgval lupb_array_typecheck(lua_State *L, int narg, int msg,
+ const upb_fielddef *f) {
+ lupb_array *larray = lupb_array_check(L, narg);
+ upb_array *array = lupb_array_upbarr(larray);
+
+ if (upb_array_type(array) != upb_fielddef_type(f) ||
+ lupb_msg_getsubmsgclass(L, msg, f) != larray->lmsgclass) {
+ luaL_error(L, "Array had incorrect type (expected: %s, got: %s)",
+ upb_fielddef_type(f), upb_array_type(array));
+ }
+
+ if (upb_array_type(array) == UPB_TYPE_MESSAGE) {
+ lupb_msgclass_typecheck(L, lupb_msg_getsubmsgclass(L, msg, f),
+ larray->lmsgclass);
+ }
+
+ return upb_msgval_arr(array);
+}
+
+/* We use "int" because of lua_rawseti/lua_rawgeti -- can re-evaluate if we want
+ * arrays bigger than 2^31. */
+static int lupb_array_checkindex(lua_State *L, int narg, uint32_t max) {
+ uint32_t n = lupb_checkuint32(L, narg);
+ if (n == 0 || n > max || n > INT_MAX) { /* Lua uses 1-based indexing. :( */
+ luaL_error(L, "Invalid array index.");
+ }
+ return n;
+}
+
+static int lupb_array_new(lua_State *L) {
+ lupb_array *larray;
+ upb_fieldtype_t type;
+ lupb_msgclass *lmsgclass = NULL;
+
+ if (lua_type(L, 1) == LUA_TNUMBER) {
+ type = lupb_checkfieldtype(L, 1);
+ } else {
+ type = UPB_TYPE_MESSAGE;
+ lmsgclass = lupb_msgclass_check(L, 1);
+ lupb_uservalseti(L, -1, MSGCLASS_INDEX, 1); /* GC-root lmsgclass. */
+ }
+
+ larray = lupb_newuserdata(L, lupb_array_sizeof(type), LUPB_ARRAY);
+ larray->lmsgclass = lmsgclass;
+ upb_array_init(lupb_array_upbarr(larray), type);
+
+ return 1;
+}
+
+static int lupb_array_gc(lua_State *L) {
+ upb_array *array = lupb_array_check2(L, 1);
+ WITH_ALLOC(upb_array_uninit(array, alloc));
+ return 0;
+}
+
+static int lupb_array_newindex(lua_State *L) {
+ lupb_array *larray = lupb_array_check(L, 1);
+ upb_array *array = lupb_array_upbarr(larray);
+ upb_fieldtype_t type = upb_array_type(array);
+ bool hasuserval = false;
+ uint32_t n = lupb_array_checkindex(L, 2, upb_array_size(array) + 1);
+ upb_msgval msgval = lupb_tomsgval(L, type, 3, larray->lmsgclass, &hasuserval);
+
+ WITH_ALLOC(upb_array_set(array, n, msgval, alloc));
+
+ if (hasuserval) {
+ lupb_uservalseti(L, 1, n, -1);
+ }
+
+ return 0; /* 1 for chained assignments? */
+}
+
+static int lupb_array_index(lua_State *L) {
+ lupb_array *larray = lupb_array_check(L, 1);
+ upb_array *array = lupb_array_upbarr(larray);
+ uint32_t n = lupb_array_checkindex(L, 2, upb_array_size(array));
+ upb_fieldtype_t type = upb_array_type(array);
+
+ if (lupb_istypewrapped(type)) {
+ lupb_uservalgeti(L, 1, n);
+ if (lupb_isstring(type)) {
+ lupb_string_unwrap(L, -1);
+ }
+ } else {
+ lupb_pushmsgval(L, upb_array_type(array), upb_array_get(array, n));
+ }
+
+ return 1;
+}
+
+static int lupb_array_len(lua_State *L) {
+ upb_array *array = lupb_array_check2(L, 1);
+ lua_pushnumber(L, upb_array_size(array));
+ return 1;
+}
+
+static const struct luaL_Reg lupb_array_mm[] = {
+ {"__gc", lupb_array_gc},
+ {"__index", lupb_array_index},
+ {"__len", lupb_array_len},
+ {"__newindex", lupb_array_newindex},
+ {NULL, NULL}
+};
+
+
+/* lupb_map *******************************************************************/
+
+/* A map object. Implemented by wrapping upb_map.
+ *
+ * When the value type is string/bytes/message, the userval consists of:
+ *
+ * [Lua number/string] -> [lupb_string/lupb_msg userdata]
+ *
+ * We always keep this synced to the underlying upb_map. For other value types
+ * we don't use the userdata, and we just read/write the underlying upb_map.
+ *
+ *
+ */
+
+typedef struct {
+ const lupb_msgclass *value_lmsgclass;
+ /* upb_map follows */
+} lupb_map;
+
+/* lupb_map internal functions */
+
+static size_t lupb_map_sizeof(upb_fieldtype_t ktype, upb_fieldtype_t vtype) {
+ return lupb_sizewithstruct(sizeof(lupb_map), upb_map_sizeof(ktype, vtype));
+}
+
+static upb_map *lupb_map_upbmap(lupb_map *lmap) {
+ return lupb_structafter(&lmap[1]);
+}
+
+static lupb_map *lupb_map_check(lua_State *L, int narg) {
+ return luaL_checkudata(L, narg, LUPB_ARRAY);
+}
+
+static upb_map *lupb_map_check2(lua_State *L, int narg) {
+ return lupb_map_upbmap(lupb_map_check(L, narg));
+}
+
+static upb_msgval lupb_map_typecheck(lua_State *L, int narg, int msg,
+ const upb_fielddef *f) {
+ lupb_map *lmap = lupb_map_check(L, narg);
+ upb_map *map = lupb_map_upbmap(lmap);
+ const upb_msgdef *entry = upb_fielddef_msgsubdef(f);
+ const upb_fielddef *key_field = upb_msgdef_itof(entry, UPB_MAPENTRY_KEY);
+ const upb_fielddef *value_field = upb_msgdef_itof(entry, UPB_MAPENTRY_VALUE);
+
+ UPB_ASSERT(entry && key_field && value_field);
+
+ if (upb_map_keytype(map) != upb_fielddef_type(key_field)) {
+ luaL_error(L, "Map key type invalid");
+ }
+
+ if (upb_map_valuetype(map) != upb_fielddef_type(value_field)) {
+ luaL_error(L, "Map had incorrect value type (expected: %s, got: %s)",
+ upb_fielddef_type(value_field), upb_map_valuetype(map));
+ }
+
+ if (upb_map_valuetype(map) == UPB_TYPE_MESSAGE) {
+ lupb_msgclass_typecheck(
+ L, lupb_msg_msgclassfor(L, msg, upb_fielddef_msgsubdef(value_field)),
+ lmap->value_lmsgclass);
+ }
+
+ return upb_msgval_map(map);
+}
+
+static void lupb_map_lazy
+
+static int lupb_map_gc(lua_State *L) {
+ upb_map *map = lupb_map_check2(L, 1);
+ WITH_ALLOC(upb_map_uninit(map, alloc));
+ return 0;
+}
+
+/* lupb_map Public API */
+
+static int lupb_map_new(lua_State *L) {
+ lupb_map *lmap;
+ upb_map *map;
+ upb_fieldtype_t key_type = lupb_checkfieldtype(L, 1);
+ upb_fieldtype_t value_type;
+ lupb_msgclass *value_lmsgclass = NULL;
+
+ if (lua_type(L, 2) == LUA_TNUMBER) {
+ value_type = lupb_checkfieldtype(L, 2);
+ } else {
+ value_type = UPB_TYPE_MESSAGE;
+ }
+
+ lmap = lupb_newuserdata(L, lupb_map_sizeof(key_type, value_type), LUPB_MAP);
+ map = lupb_map_upbmap(lmap);
+
+ if (value_type == UPB_TYPE_MESSAGE) {
+ value_lmsgclass = lupb_msgclass_check(L, 2);
+ lupb_uservalseti(L, -1, MSGCLASS_INDEX, 2); /* GC-root lmsgclass. */
+ }
+
+ lmap->value_lmsgclass = value_lmsgclass;
+ WITH_ALLOC(upb_map_init(map, key_type, value_type, alloc));
+
+ return 1;
+}
+
+static int lupb_map_index(lua_State *L) {
+ lupb_map *lmap = lupb_map_check(L, 1);
+ upb_map *map = lupb_map_upbmap(lmap);
+ upb_fieldtype_t valtype = upb_map_valuetype(map);
+ bool pushedobj;
+ /* We don't always use "key", but this call checks the key type. */
+ upb_msgval key = lupb_map_tokeymsgval(L, upb_map_keytype(map), 2);
+
+ if (lupb_istypewrapped(valtype)) {
+ /* Userval contains the full map, lookup there by key. */
+ lupb_getuservalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_rawget(L, -2);
+ } else {
+ /* Lookup in upb_map. */
+ upb_msgval val;
+ if (upb_map_get(map, key, &val)) {
+ lupb_map_pushmsgval(L, upb_map_valuetype(map), val);
+ } else {
+ lua_pushnil(L);
+ }
+ }
+
+ return 1;
+}
+
+static int lupb_map_len(lua_State *L) {
+ upb_map *map = lupb_map_check2(L, 1);
+ lua_pushnumber(L, upb_map_size(map));
+ return 1;
+}
+
+static int lupb_map_newindex(lua_State *L) {
+ lupb_map *lmap = lupb_map_check(L, 1);
+ upb_map *map = lupb_map_upbmap(lmap);
+ bool keyobj = false;
+ upb_msgval key = lupb_tomsgval(L, upb_map_keytype(map), 2, NULL, &keyobj);
+
+ if (lua_isnil(L, 3)) {
+ /* Delete from map. */
+ WITH_ALLOC(upb_map_del(map, key, alloc));
+
+ if (lupb_istypewrapped(upb_map_valuetype(map))) {
+ /* Delete in userval. */
+ lupb_getuservalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_pushnil(L);
+ lua_rawset(L, -3);
+ lua_pop(L, 1);
+ }
+ } else {
+ /* Set in map. */
+ bool valobj = false;
+ upb_msgval val = lupb_tomsgval(L, upb_map_valuetype(map), 3,
+ lmap->value_lmsgclass, &valobj);
+
+ WITH_ALLOC(upb_map_set(map, key, val, NULL, alloc));
+
+ if (valobj) {
+ /* Set in userval. */
+ lupb_getuservalue(L, 1);
+ lua_pushvalue(L, 2);
+ lua_pushvalue(L, -3);
+ lua_rawset(L, -3);
+ lua_pop(L, 1);
+ }
+ }
+
+ return 0;
+}
+
+/* upb_mapiter [[[ */
+
+static int lupb_mapiter_next(lua_State *L) {
+ upb_mapiter *i = lua_touserdata(L, lua_upvalueindex(1));
+ lupb_map *lmap = lupb_map_check(L, 1);
+ upb_map *map = lupb_map_upbmap(lmap);
+ upb_string *str = malloc(upb_string_sizeof());
+
+ if (upb_mapiter_done(i)) {
+ return 0;
+ }
+
+ lupb_map_pushmsgval(L, upb_map_keytype(map), upb_mapiter_key(i));
+ lupb_map_pushmsgval(L, upb_map_valuetype(map), upb_mapiter_value(i));
+ upb_mapiter_next(i);
+
+ free(str);
+
+ return 2;
+}
+
+static int lupb_map_pairs(lua_State *L) {
+ lupb_map *lmap = lupb_map_check(L, 1);
+ upb_map *map = lupb_map_upbmap(lmap);
+ upb_mapiter *i = lua_newuserdata(L, upb_mapiter_sizeof());
+
+ upb_mapiter_begin(i, map);
+ lua_pushvalue(L, 1);
+
+ /* Upvalues are [upb_mapiter, lupb_map]. */
+ lua_pushcclosure(L, &lupb_mapiter_next, 2);
+
+ return 1;
+}
+
+/* upb_mapiter ]]] */
+
+static const struct luaL_Reg lupb_map_mm[] = {
+ {"__gc", lupb_map_gc},
+ {"__index", lupb_map_index},
+ {"__len", lupb_map_len},
+ {"__newindex", lupb_map_newindex},
+ {"__pairs", lupb_map_pairs},
+ {NULL, NULL}
+};
+
+
+/* lupb_msg *******************************************************************/
+
+/* A message object. Implemented by wrapping upb_msg.
+ *
+ * Our userval contains:
+ *
+ * - [0] = our message class
+ * - [upb_fielddef_index(f)] = any submessage/string/map/repeated obj.
+ */
+
+#define LUPB_MSG_MSGCLASSINDEX 0
+
+typedef struct {
+ const lupb_msgclass *lmsgclass;
+ /* Data follows, in a flat buffer. */
+} lupb_msg;
+
+/* lupb_msg helpers */
+
+static bool in_userval(const upb_fielddef *f) {
+ return upb_fielddef_isseq(f) || upb_fielddef_issubmsg(f) ||
+ upb_fielddef_isstring(f) || upb_fielddef_ismap(f);
+}
+
+lupb_msg *lupb_msg_check(lua_State *L, int narg) {
+ lupb_msg *msg = luaL_checkudata(L, narg, LUPB_MSG);
+ if (!msg->lmsgclass) luaL_error(L, "called into dead msg");
+ return msg;
+}
+
+void *lupb_msg_upbmsg(lupb_msg *lmsg) {
+ return lupb_structafter(&lmsg[1]);
+}
+
+static upb_msgval lupb_msg_typecheck(lua_State *L, int narg,
+ const lupb_msgclass *lmsgclass) {
+ lupb_msg *msg = lupb_msg_check(L, narg);
+ lupb_msgclass_typecheck(L, msg->lmsgclass, lmsgclass);
+ return upb_msgval_msg(msg);
+}
+
+const upb_msgdef *lupb_msg_checkdef(lua_State *L, int narg) {
+ return upb_msglayout_msgdef(lupb_msg_check(L, narg)->lmsgclass->layout);
+}
+
+static const upb_fielddef *lupb_msg_checkfield(lua_State *L,
+ const lupb_msg *msg,
+ int fieldarg) {
+ size_t len;
+ const char *fieldname = luaL_checklstring(L, fieldarg, &len);
+ const upb_msgdef *msgdef = upb_msglayout_msgdef(msg->lmsgclass->layout);
+ const upb_fielddef *f = upb_msgdef_ntof(msgdef, fieldname, len);
+
+ if (!f) {
+ const char *msg = lua_pushfstring(L, "no such field: %s", fieldname);
+ luaL_argerror(L, fieldarg, msg);
+ return NULL; /* Never reached. */
+ }
+
+ return f;
+}
+
+static int lupb_msg_pushnew(lua_State *L, int narg) {
+ lupb_msgclass *lmsgclass = lupb_msgclass_check(L, narg);
+ size_t size = upb_msg_sizeof(lmsgclass->layout);
+ lupb_msg *msg = lupb_newuserdata(L, size, LUPB_MSG);
+
+ msg->lmsgclass = lmsgclass;
+ lupb_uservalseti(L, -1, LUPB_MSG_MSGCLASSINDEX, narg);
+
+ return 1;
+}
+
+static const lupb_msgclass *lupb_msg_msgclassfor(lua_State *L, int narg,
+ const upb_msgdef *md) {
+ lupb_uservalgeti(L, narg, LUPB_MSG_MSGCLASSINDEX);
+ return lupb_msgclass_msgclassfor(L, -1, md);
+}
+
+static const lupb_msgclass *lupb_msg_getsubmsgclass(lua_State *L, int narg,
+ const upb_fielddef *f) {
+ lupb_uservalgeti(L, narg, LUPB_MSG_MSGCLASSINDEX);
+ return lupb_msgclass_getsubmsgclass(L, -1, f);
+}
+
+/* lupb_msg Public API */
+
+static int lupb_msg_index(lua_State *L) {
+ lupb_msg *msg = lupb_msg_check(L, 1);
+ const upb_fielddef *f = lupb_msg_checkfield(L, msg, 2);
+
+ if (in_userval(f)) {
+ lupb_uservalgeti(L, 1, upb_fielddef_index(f));
+ if (upb_fielddef_isseq(f) && lua_isnil(L, -1)) {
+ /* TODO(haberman): default-construct empty array. */
+ }
+ } else {
+ lupb_pushmsgval(L, upb_fielddef_type(f),
+ upb_msg_get(msg, f, msg->lmsgclass->layout));
+ }
+
+ return 1;
+}
+
+static int lupb_msg_newindex(lua_State *L) {
+ lupb_msg *lmsg = lupb_msg_check(L, 1);
+ const upb_fielddef *f = lupb_msg_checkfield(L, lmsg, 2);
+ bool luaobj = false;
+ upb_msgval msgval;
+
+ /* Typecheck and get msgval. */
+
+ if (upb_fielddef_isseq(f)) {
+ msgval = lupb_array_typecheck(L, 3, 1, f);
+ luaobj = true;
+ } else if (upb_fielddef_ismap(f)) {
+ msgval = lupb_map_typecheck(L, 3, 1, f);
+ luaobj = true;
+ } else {
+ const lupb_msgclass *lmsgclass = NULL;
+ upb_fieldtype_t type = upb_fielddef_type(f);
+
+ if (type == UPB_TYPE_MESSAGE) {
+ lmsgclass = lupb_msg_getsubmsgclass(L, 1, f);
+ }
+
+ msgval = lupb_tomsgval(L, upb_fielddef_type(f), 3, lmsgclass, &luaobj);
+ }
+
+ /* Set in upb_msg and userval (if necessary). */
+
+ WITH_ALLOC(upb_msg_set(lmsg, f, msgval, lmsg->lmsgclass->layout, alloc));
+
+ if (luaobj) {
+ lupb_uservalseti(L, 1, upb_fielddef_index(f), -1);
+ }
+
+ return 0; /* 1 for chained assignments? */
+}
+
+static const struct luaL_Reg lupb_msg_mm[] = {
+ {"__index", lupb_msg_index},
+ {"__newindex", lupb_msg_newindex},
+ {NULL, NULL}
+};
+
+
diff --git a/upb/bindings/lua/upb/pb.c b/upb/bindings/lua/upb/pb.c
index f095a61..d6f13d9 100644
--- a/upb/bindings/lua/upb/pb.c
+++ b/upb/bindings/lua/upb/pb.c
@@ -10,59 +10,24 @@
#define LUPB_PBDECODERMETHOD "lupb.pb.decodermethod"
-#define MSGDEF_INDEX 1
-
-static upb_pbdecodermethod *lupb_pbdecodermethod_check(lua_State *L, int narg) {
- return lupb_refcounted_check(L, narg, LUPB_PBDECODERMETHOD);
-}
-
-static int lupb_pbdecodermethod_new(lua_State *L) {
- const upb_handlers *handlers = lupb_msg_newwritehandlers(L, 1, &handlers);
- const upb_pbdecodermethod *m;
-
- upb_pbdecodermethodopts opts;
- upb_pbdecodermethodopts_init(&opts, handlers);
-
- m = upb_pbdecodermethod_new(&opts, &m);
- upb_handlers_unref(handlers, &handlers);
- lupb_refcounted_pushnewrapper(
- L, upb_pbdecodermethod_upcast(m), LUPB_PBDECODERMETHOD, &m);
-
- /* We need to keep a pointer to the MessageDef (in Lua space) so we can
- * construct new messages in parse(). */
- lua_newtable(L);
- lua_pushvalue(L, 1);
- lua_rawseti(L, -2, MSGDEF_INDEX);
- lua_setuservalue(L, -2);
-
- return 1; /* The DecoderMethod wrapper. */
-}
-
-/* Unlike most of our exposed Lua functions, this does not correspond to an
- * actual method on the underlying DecoderMethod. But it's convenient, and
- * important to implement in C because we can do stack allocation and
- * initialization of our runtime structures like the Decoder and Sink. */
-static int lupb_pbdecodermethod_parse(lua_State *L) {
+static int lupb_pb_strtomessage(lua_State *L) {
size_t len;
- const upb_pbdecodermethod *method = lupb_pbdecodermethod_check(L, 1);
- const char *pb = lua_tolstring(L, 2, &len);
- void *msg;
upb_status status = UPB_STATUS_INIT;
+ const char *pb = lua_tolstring(L, 1, &len);
+ const upb_msglayout *layout = lua_touserdata(L, lua_upvalueindex(1));
+ const upb_pbdecodermethod *method = lua_touserdata(L, lua_upvalueindex(2));
+ const upb_handlers *handlers = upb_pbdecodermethod_desthandlers(method);
+
+ upb_arena *msg_arena;
upb_env env;
upb_sink sink;
upb_pbdecoder *decoder;
+ void *msg;
- const upb_handlers *handlers = upb_pbdecodermethod_desthandlers(method);
-
- lua_getuservalue(L, 1);
- lua_rawgeti(L, -1, MSGDEF_INDEX);
- lupb_assert(L, !lua_isnil(L, -1));
- lupb_msg_pushnew(L, -1); /* Push new message. */
- msg = lua_touserdata(L, -1);
-
- /* Handlers need this. */
- lua_getuservalue(L, -1);
+ lupb_arena_new(L);
+ msg_arena = lupb_arena_check(L, -1);
+ msg = upb_msg_new(layout, upb_arena_alloc(msg_arena));
upb_env_init(&env);
upb_env_reporterrorsto(&env, &status);
upb_sink_reset(&sink, handlers, msg);
@@ -76,18 +41,42 @@ static int lupb_pbdecodermethod_parse(lua_State *L) {
lupb_checkstatus(L, &status);
- lua_pop(L, 1); /* Uservalue. */
-
+ /* References the arena at the top of the stack. */
+ lupb_msg_pushref(L, lua_upvalueindex(3), msg);
return 1;
}
-static const struct luaL_Reg lupb_pbdecodermethod_m[] = {
- {"parse", lupb_pbdecodermethod_parse},
+static int lupb_pb_makestrtomsgdecoder(lua_State *L) {
+ const upb_msglayout *layout = lupb_msgclass_getlayout(L, 1);
+ const upb_handlers *handlers = lupb_msgclass_getmergehandlers(L, 1);
+ const upb_pbdecodermethod *m;
+
+ upb_pbdecodermethodopts opts;
+ upb_pbdecodermethodopts_init(&opts, handlers);
+
+ m = upb_pbdecodermethod_new(&opts, &m);
+
+ /* Push upvalues for the closure. */
+ lua_pushlightuserdata(L, (void*)layout);
+ lua_pushlightuserdata(L, (void*)m);
+ lua_pushvalue(L, 1);
+
+ /* Upvalue for the closure, only to keep the decodermethod alive. */
+ lupb_refcounted_pushnewrapper(
+ L, upb_pbdecodermethod_upcast(m), LUPB_PBDECODERMETHOD, &m);
+
+ lua_pushcclosure(L, &lupb_pb_strtomessage, 4);
+
+ return 1; /* The decoder closure. */
+}
+
+static const struct luaL_Reg decodermethod_mm[] = {
+ {"__gc", lupb_refcounted_gc},
{NULL, NULL}
};
static const struct luaL_Reg toplevel_m[] = {
- {"DecoderMethod", lupb_pbdecodermethod_new},
+ {"MakeStringToMessageDecoder", lupb_pb_makestrtomsgdecoder},
{NULL, NULL}
};
@@ -97,8 +86,7 @@ int luaopen_upb_pb_c(lua_State *L) {
return 1;
}
- lupb_register_type(L, LUPB_PBDECODERMETHOD, lupb_pbdecodermethod_m, NULL,
- true);
+ lupb_register_type(L, LUPB_PBDECODERMETHOD, NULL, decodermethod_mm);
return 1;
}
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback