summaryrefslogtreecommitdiff
path: root/upb/bindings/lua
diff options
context:
space:
mode:
authorJosh Haberman <jhaberman@gmail.com>2014-06-26 20:24:32 -0700
committerJosh Haberman <jhaberman@gmail.com>2014-06-26 20:24:32 -0700
commit2d10fa33071d52d7a35ce3b13bc459cd16a0aa33 (patch)
treebf47d38e2e1cc8ddb4711b23b26e7fd10742e07d /upb/bindings/lua
parent7d565f1e7a0f107506d3cf31ef2e33e22a504d2b (diff)
Sync from internal Google development.
Diffstat (limited to 'upb/bindings/lua')
-rw-r--r--upb/bindings/lua/table.c13
-rw-r--r--upb/bindings/lua/upb.c1736
-rw-r--r--upb/bindings/lua/upb.h106
-rw-r--r--upb/bindings/lua/upb.lua146
-rw-r--r--upb/bindings/lua/upb.pb.c106
-rw-r--r--upb/bindings/lua/upb/descriptor.c20
6 files changed, 1558 insertions, 569 deletions
diff --git a/upb/bindings/lua/table.c b/upb/bindings/lua/table.c
index 51ba324..febadf0 100644
--- a/upb/bindings/lua/table.c
+++ b/upb/bindings/lua/table.c
@@ -25,6 +25,7 @@
#include "lauxlib.h"
#include "upb/bindings/lua/upb.h"
#include "upb/def.h"
+#include "upb/symtab.h"
static void lupbtable_setnum(lua_State *L, int tab, const char *key,
lua_Number val) {
@@ -141,6 +142,12 @@ static int lupbtable_enumdef_ntoi(lua_State *L) {
return 1;
}
+static int lupbtable_symtab_symtab(lua_State *L) {
+ const upb_symtab *s = lupb_symtab_check(L, 1);
+ lupbtable_pushstrtable(L, &s->symtab);
+ return 1;
+}
+
static void lupbtable_setfieldi(lua_State *L, const char *field, int i) {
lua_pushnumber(L, i);
lua_setfield(L, -2, field);
@@ -151,11 +158,15 @@ static const struct luaL_Reg lupbtable_toplevel_m[] = {
{"msgdef_ntof", lupbtable_msgdef_ntof},
{"enumdef_iton", lupbtable_enumdef_iton},
{"enumdef_ntoi", lupbtable_enumdef_ntoi},
+ {"symtab_symtab", lupbtable_symtab_symtab},
{NULL, NULL}
};
int luaopen_upbtable(lua_State *L) {
- lupb_newlib(L, "upb.table", lupbtable_toplevel_m);
+ static char module_key;
+ if (lupb_openlib(L, &module_key, "upb.table", lupbtable_toplevel_m)) {
+ return 1;
+ }
// We define these here because they are not public.
lupbtable_setfieldi(L, "CTYPE_PTR", UPB_CTYPE_PTR);
diff --git a/upb/bindings/lua/upb.c b/upb/bindings/lua/upb.c
index 022d689..dbeb937 100644
--- a/upb/bindings/lua/upb.c
+++ b/upb/bindings/lua/upb.c
@@ -6,6 +6,21 @@
*
* A Lua extension for upb. Exposes only the core library
* (sub-libraries are exposed in other extensions).
+ *
+ * 64-bit woes: Lua can only represent numbers of type lua_Number (which is
+ * double unless the user specifically overrides this). Doubles can represent
+ * the entire range of 64-bit integers, but lose precision once the integers are
+ * greater than 2^53.
+ *
+ * Lua 5.3 is adding support for integers, which will allow for 64-bit
+ * integers (which can be interpreted as signed or unsigned).
+ *
+ * LuaJIT supports 64-bit signed and unsigned boxed representations
+ * through its "cdata" mechanism, but this is not portable to regular Lua.
+ *
+ * Hopefully Lua 5.3 will come soon enough that we can either use Lua 5.3
+ * integer support or LuaJIT 64-bit cdata for users that need the entire
+ * domain of [u]int64 values.
*/
#include <float.h>
@@ -16,8 +31,15 @@
#include "upb/bindings/lua/upb.h"
#include "upb/handlers.h"
#include "upb/pb/glue.h"
+#include "upb/shim/shim.h"
+
+static const char upb_lua[] = {
+#include "upb/bindings/lua/upb.lua.h"
+};
// Lua metatable types.
+#define LUPB_MSG "lupb.msg"
+#define LUPB_ARRAY "lupb.array"
#define LUPB_MSGDEF "lupb.msgdef"
#define LUPB_ENUMDEF "lupb.enumdef"
#define LUPB_FIELDDEF "lupb.fielddef"
@@ -26,6 +48,15 @@
// 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 *****************************************************/
+
+// Lua 5.1 and Lua 5.2 have slightly incompatible APIs. A little bit of
+// compatibility code can help hide the difference. Not too many people still
+// use Lua 5.1 but LuaJIT uses the Lua 5.1 API in some ways.
+
#if LUA_VERSION_NUM == 501
// Taken from Lua 5.2's source.
@@ -43,6 +74,12 @@ void *luaL_testudata(lua_State *L, int ud, const char *tname) {
return NULL; /* value is not a userdata with a metatable */
}
+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) {
@@ -51,11 +88,72 @@ int luaL_typerror(lua_State *L, int narg, const char *tname) {
return luaL_argerror(L, narg, msg);
}
+static void lupb_newlib(lua_State *L, const char *name, const luaL_Reg *funcs) {
+ // Lua 5.2 modules are not expected to set a global variable, so "name" is
+ // unused.
+ UPB_UNUSED(name);
+
+ // Can't use luaL_newlib(), because funcs is not the actual array.
+ // Could (micro-)optimize this a bit to count funcs for initial table size.
+ lua_createtable(L, 0, 8);
+ 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
-static const char *chkname(lua_State *L, int narg) {
+// Shims for upcoming Lua 5.3 functionality.
+bool lua_isinteger(lua_State *L, int argn) {
+ UPB_UNUSED(L);
+ UPB_UNUSED(argn);
+ return false;
+}
+
+
+/* Utility functions **********************************************************/
+
+// We store our module table in the registry, keyed by ptr.
+// For more info about the motivation/rationale, see this thread:
+// http://thread.gmane.org/gmane.comp.lang.lua.general/110632
+bool lupb_openlib(lua_State *L, void *ptr, const char *name,
+ const luaL_Reg *funcs) {
+ // Lookup cached module table.
+ lua_pushlightuserdata(L, ptr);
+ lua_rawget(L, LUA_REGISTRYINDEX);
+ if (!lua_isnil(L, -1)) {
+ return true;
+ }
+
+ lupb_newlib(L, name, funcs);
+
+ // Save module table in cache.
+ lua_pushlightuserdata(L, ptr);
+ lua_pushvalue(L, -2);
+ lua_rawset(L, LUA_REGISTRYINDEX);
+
+ 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);
+ 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;
+}
+
+const char *lupb_checkname(lua_State *L, int narg) {
size_t len;
const char *name = luaL_checklstring(L, narg, &len);
if (strlen(name) != len)
@@ -63,65 +161,110 @@ static const char *chkname(lua_State *L, int narg) {
return name;
}
-static bool chkbool(lua_State *L, int narg, const char *type) {
+bool lupb_checkbool(lua_State *L, int narg) {
if (!lua_isboolean(L, narg)) {
- luaL_error(L, "%s must be true or false", type);
+ luaL_error(L, "must be true or false");
}
return lua_toboolean(L, narg);
}
-static bool streql(const char *a, const char *b) { return strcmp(a, b) == 0; }
+// 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)
+ luaL_error(L, "Expected string");
+}
+
+// Unlike luaL_checkinteger, these do not implicitly convert from string or
+// round an existing double value. We allow floating-point input, but only if
+// the actual value is integral.
+#define INTCHECK(type, ctype) \
+ ctype lupb_check##type(lua_State *L, int narg) { \
+ if (lua_isinteger(L, narg)) { \
+ return lua_tointeger(L, narg); \
+ } \
+ \
+ /* Prevent implicit conversion from string. */ \
+ luaL_checktype(L, narg, LUA_TNUMBER); \
+ double n = lua_tonumber(L, narg); \
+ \
+ ctype i = (ctype)n; \
+ if ((double)i != n) { \
+ /* double -> ctype truncated or rounded. */ \
+ luaL_error(L, "number %f was not an integer or out of range for " #type, \
+ n); \
+ } \
+ return i; \
+ } \
+ void lupb_push##type(lua_State *L, ctype val) { \
+ /* TODO: push integer for Lua >= 5.3, 64-bit cdata for LuaJIT. */ \
+ /* This is lossy for some [u]int64 values, which isn't great, but */ \
+ /* crashing when we encounter these values seems worse. */ \
+ lua_pushnumber(L, val); \
+ }
+
+INTCHECK(int64, int64_t);
+INTCHECK(int32, int32_t);
+INTCHECK(uint64, uint64_t);
+INTCHECK(uint32, uint32_t);
-static uint32_t chkint32(lua_State *L, int narg, const char *name) {
- lua_Number n = lua_tonumber(L, narg);
- if (n > INT32_MAX || n < INT32_MIN || rint(n) != n)
- luaL_error(L, "Invalid %s", name);
- return n;
+double lupb_checkdouble(lua_State *L, int narg) {
+ // If we were being really hard-nosed here, we'd check whether the input was
+ // an integer that has no precise double representation. But doubles aren't
+ // generally expected to be exact like integers are, and worse this could
+ // cause data-dependent runtime errors: one run of the program could work fine
+ // because the integer calculations happened to be exactly representable in
+ // double, while the next could crash because of subtly different input.
+
+ luaL_checktype(L, narg, LUA_TNUMBER); // lua_tonumber() implicitly converts.
+ return lua_tonumber(L, narg);
}
-// Sets a fielddef default from the given Lua value.
-static void lupb_setdefault(lua_State *L, int narg, upb_fielddef *f) {
- if (upb_fielddef_type(f) == UPB_TYPE_BOOL) {
- upb_fielddef_setdefaultbool(f, chkbool(L, narg, "bool default"));
- } else {
- // Numeric type.
- lua_Number num = luaL_checknumber(L, narg);
- switch (upb_fielddef_type(f)) {
- case UPB_TYPE_INT32:
- case UPB_TYPE_ENUM:
- if (num > INT32_MAX || num < INT32_MIN || num != rint(num))
- luaL_error(L, "Cannot convert %f to 32-bit integer", num);
- upb_fielddef_setdefaultint32(f, num);
- break;
- case UPB_TYPE_INT64:
- if (num > INT64_MAX || num < INT64_MIN || num != rint(num))
- luaL_error(L, "Cannot convert %f to 64-bit integer", num);
- upb_fielddef_setdefaultint64(f, num);
- break;
- case UPB_TYPE_UINT32:
- if (num > UINT32_MAX || num < 0 || num != rint(num))
- luaL_error(L, "Cannot convert %f to unsigned 32-bit integer", num);
- upb_fielddef_setdefaultuint32(f, num);
- break;
- case UPB_TYPE_UINT64:
- if (num > UINT64_MAX || num < 0 || num != rint(num))
- luaL_error(L, "Cannot convert %f to unsigned 64-bit integer", num);
- upb_fielddef_setdefaultuint64(f, num);
- break;
- case UPB_TYPE_DOUBLE:
- if (num > DBL_MAX || num < -DBL_MAX) {
- // This could happen if lua_Number was long double.
- luaL_error(L, "Cannot convert %f to double", num);
- }
- upb_fielddef_setdefaultdouble(f, num);
- break;
- case UPB_TYPE_FLOAT:
- if (num > FLT_MAX || num < -FLT_MAX)
- luaL_error(L, "Cannot convert %f to float", num);
- upb_fielddef_setdefaultfloat(f, num);
- break;
- default: luaL_error(L, "invalid type");
- }
+float lupb_checkfloat(lua_State *L, int narg) {
+ // We don't worry about checking whether the input can be exactly converted to
+ // float -- see above.
+
+ luaL_checktype(L, narg, LUA_TNUMBER); // lua_tonumber() implicitly converts.
+ return lua_tonumber(L, narg);
+}
+
+void lupb_pushdouble(lua_State *L, double d) {
+ lua_pushnumber(L, d);
+}
+
+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);
}
}
@@ -132,6 +275,13 @@ void lupb_checkstatus(lua_State *L, upb_status *s) {
}
}
+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; \
@@ -139,21 +289,28 @@ void lupb_checkstatus(lua_State *L, upb_status *s) {
} while (0)
-/* refcounted *****************************************************************/
+/* lupb_refcounted ************************************************************/
-// All upb objects that use upb_refcounted share a common Lua userdata
-// representation and a common scheme for caching Lua wrapper object. They do
-// however have different metatables. Objects are cached in a weak table
-// indexed by the C pointer of the object they are caching.
+// 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.
-typedef union {
- const upb_refcounted *refcounted;
- const upb_def *def;
- upb_symtab *symtab;
-} lupb_refcounted;
+// 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;
+}
-static bool lupb_refcounted_pushwrapper(lua_State *L, const upb_refcounted *obj,
- const char *type, const void *owner) {
+bool lupb_refcounted_pushwrapper(lua_State *L, const upb_refcounted *obj,
+ const char *type, const void *ref_donor,
+ size_t size) {
if (obj == NULL) {
lua_pushnil(L);
return false;
@@ -161,30 +318,44 @@ static bool lupb_refcounted_pushwrapper(lua_State *L, const upb_refcounted *obj,
// 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).
- lupb_refcounted *ud = NULL;
lua_getfield(L, LUA_REGISTRYINDEX, LUPB_OBJCACHE);
assert(!lua_isnil(L, -1)); // Should have been created by luaopen_upb.
lua_pushlightuserdata(L, (void*)obj);
lua_rawget(L, -2);
- // Stack: objcache, cached value.
- bool create = lua_isnil(L, -1) ||
- // 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.
- ((lupb_refcounted*)lua_touserdata(L, -1))->refcounted == NULL;
+ // Stack is now: objcache, cached value.
+
+ bool create = false;
+
+ if (lua_isnil(L, -1)) {
+ create = true;
+ } else {
+ void *ud = lua_touserdata(L, -1);
+ lupb_assert(L, ud);
+ void *ud_obj;
+ 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;
+ }
+ }
+
+ void *ud = NULL;
+
if (create) {
// Remove bad cached value and push new value.
lua_pop(L, 1);
- // We take advantage of the fact that all of our objects are currently a
- // single pointer, and thus have the same layout.
- // TODO: this probably violates aliasing.
- ud = lua_newuserdata(L, sizeof(lupb_refcounted));
- ud->refcounted = obj;
- upb_refcounted_donateref(obj, owner, ud);
+ // 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);
- assert(!lua_isnil(L, -1)); // Should have been created by luaopen_upb.
+ // Should have been created by luaopen_upb.
+ lupb_assert(L, !lua_isnil(L, -1));
lua_setmetatable(L, -2);
// Set it in the cache.
@@ -195,67 +366,111 @@ static bool lupb_refcounted_pushwrapper(lua_State *L, const upb_refcounted *obj,
// Existing wrapper obj already has a ref.
ud = lua_touserdata(L, -1);
upb_refcounted_checkref(obj, ud);
- if (owner)
- upb_refcounted_unref(obj, owner);
+ if (ref_donor)
+ upb_refcounted_unref(obj, ref_donor);
}
+
lua_insert(L, -2);
lua_pop(L, 1);
return create;
}
-static void lupb_refcounted_pushnewrapper(lua_State *L, upb_refcounted *obj,
- const char *type, const void *owner) {
- bool created = lupb_refcounted_pushwrapper(L, obj, type, owner);
+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_VAR(created, created == true);
}
+static int lupb_refcounted_gc(lua_State *L) {
+ void *ud = lua_touserdata(L, 1);
+ 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.
+ void *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) {
- lupb_refcounted *r = luaL_testudata(L, narg, LUPB_MSGDEF);
- if (!r) r = luaL_testudata(L, narg, LUPB_ENUMDEF);
- if (!r) r = luaL_testudata(L, narg, LUPB_FIELDDEF);
- if (!r) luaL_typerror(L, narg, "upb def");
- if (!r->refcounted) luaL_error(L, "called into dead def");
- return r->def;
+ 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");
+
+ upb_def *ret;
+ 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_typerror(L, narg, "not allowed on frozen value");
+ 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 *owner) {
+bool lupb_def_pushwrapper(lua_State *L, const upb_def *def,
+ const void *ref_donor) {
if (def == NULL) {
lua_pushnil(L);
return false;
}
const char *type = NULL;
- switch (def->type) {
- case UPB_DEF_MSG: type = LUPB_MSGDEF; break;
+ size_t size = sizeof(void*);
+
+ 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", def->type);
}
- return lupb_refcounted_pushwrapper(L, UPB_UPCAST(def), type, owner);
+
+ bool created =
+ lupb_refcounted_pushwrapper(L, UPB_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 *owner) {
- bool created = lupb_def_pushwrapper(L, def, owner);
+ const void *ref_donor) {
+ bool created = lupb_def_pushwrapper(L, def, ref_donor);
UPB_ASSERT_VAR(created, created == true);
}
static int lupb_def_type(lua_State *L) {
const upb_def *def = lupb_def_check(L, 1);
- lua_pushnumber(L, upb_def_type(def));
+ 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));
@@ -269,234 +484,49 @@ static int lupb_def_fullname(lua_State *L) {
}
static int lupb_def_setfullname(lua_State *L) {
- CHK(upb_def_setfullname(lupb_def_checkmutable(L, 1), chkname(L, 2), &status));
+ 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}, \
{"set_full_name", lupb_def_setfullname}, \
/* lupb_fielddef **************************************************************/
-static const upb_fielddef *lupb_fielddef_check(lua_State *L, int narg) {
- lupb_refcounted *r = luaL_checkudata(L, narg, LUPB_FIELDDEF);
- if (!r) luaL_typerror(L, narg, "upb fielddef");
- if (!r->refcounted) luaL_error(L, "called into dead fielddef");
- return upb_downcast_fielddef(r->def);
+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_typerror(L, narg, "not allowed on frozen value");
+ luaL_error(L, "not allowed on frozen value");
return (upb_fielddef*)f;
}
-// Setter functions; these are called by both the constructor and the individual
-// setter API calls like field:set_type().
-
-static void lupb_fielddef_dosetdefault(lua_State *L, upb_fielddef *f,
- int narg) {
- int type = lua_type(L, narg);
- upb_fieldtype_t upbtype = upb_fielddef_type(f);
- if (type == LUA_TSTRING) {
- if (!upb_fielddef_isstring(f) && upbtype != UPB_TYPE_ENUM)
- luaL_argerror(L, narg, "field does not expect a string default");
- size_t len;
- const char *str = lua_tolstring(L, narg, &len);
- CHK(upb_fielddef_setdefaultstr(f, str, len, &status));
- } else {
- lupb_setdefault(L, narg, f);
- }
-}
-
-static void lupb_fielddef_dosetisextension(lua_State *L, upb_fielddef *f,
- int narg) {
- CHK(upb_fielddef_setisextension(f, chkbool(L, narg, "is_extension")));
-}
-
-static void lupb_fielddef_dosetlabel(lua_State *L, upb_fielddef *f, int narg) {
- int label = luaL_checkint(L, narg);
- if (!upb_fielddef_checklabel(label))
- luaL_argerror(L, narg, "invalid field label");
- upb_fielddef_setlabel(f, label);
-}
-
-static void lupb_fielddef_dosetlazy(lua_State *L, upb_fielddef *f, int narg) {
- upb_fielddef_setlazy(f, chkbool(L, narg, "lazy"));
-}
-
-static void lupb_fielddef_dosetname(lua_State *L, upb_fielddef *f, int narg) {
- CHK(upb_fielddef_setname(f, chkname(L, narg), &status));
-}
-
-static void lupb_fielddef_dosetnumber(lua_State *L, upb_fielddef *f, int narg) {
- CHK(upb_fielddef_setnumber(f, luaL_checkint(L, narg), &status));
-}
-
-static void lupb_fielddef_dosetsubdef(lua_State *L, upb_fielddef *f, int narg) {
- const upb_def *def = NULL;
- if (!lua_isnil(L, narg))
- def = lupb_def_check(L, narg);
- CHK(upb_fielddef_setsubdef(f, def, &status));
-}
-
-static void lupb_fielddef_dosetsubdefname(lua_State *L, upb_fielddef *f,
- int narg) {
- const char *name = NULL;
- if (!lua_isnil(L, narg))
- name = chkname(L, narg);
- CHK(upb_fielddef_setsubdefname(f, name, &status));
-}
-
-static void lupb_fielddef_dosetcontainingtypename(lua_State *L, upb_fielddef *f,
- int narg) {
- const char *name = NULL;
- if (!lua_isnil(L, narg))
- name = chkname(L, narg);
- CHK(upb_fielddef_setcontainingtypename(f, name, &status));
-}
-
-static void lupb_fielddef_dosettype(lua_State *L, upb_fielddef *f, int narg) {
- int type = luaL_checkint(L, narg);
- if (!upb_fielddef_checktype(type))
- luaL_argerror(L, narg, "invalid field type");
- upb_fielddef_settype(f, type);
-}
-
-static void lupb_fielddef_dosetintfmt(lua_State *L, upb_fielddef *f, int narg) {
- int32_t intfmt = luaL_checknumber(L, narg);
- if (!upb_fielddef_checkintfmt(intfmt))
- luaL_argerror(L, narg, "invalid intfmt");
- upb_fielddef_setintfmt(f, intfmt);
-}
-
-static void lupb_fielddef_dosettagdelim(lua_State *L, upb_fielddef *f,
- int narg) {
- CHK(upb_fielddef_settagdelim(f, chkbool(L, narg, "tagdelim")));
-}
-
-// Setter API calls. These use the setter functions above.
-
-static int lupb_fielddef_setcontainingtypename(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetcontainingtypename(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setdefault(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetdefault(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setisextension(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetisextension(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setlabel(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetlabel(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setlazy(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetlazy(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setname(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetname(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setnumber(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetnumber(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setsubdef(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetsubdef(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setsubdefname(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetsubdefname(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_settype(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosettype(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_setintfmt(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosetintfmt(L, f, 2);
- return 0;
-}
-
-static int lupb_fielddef_settagdelim(lua_State *L) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, 1);
- lupb_fielddef_dosettagdelim(L, f, 2);
- return 0;
-}
-
-// Constructor and other methods.
-
static int lupb_fielddef_new(lua_State *L) {
upb_fielddef *f = upb_fielddef_new(&f);
- int narg = lua_gettop(L);
-
lupb_def_pushnewrapper(L, UPB_UPCAST(f), &f);
+ return 1;
+}
- if (narg == 0) return 1;
-
- // User can specify initialization values like so:
- // upb.FieldDef{label=upb.LABEL_REQUIRED, name="my_field", number=5,
- // type=upb.TYPE_INT32, default_value=12, type_name="Foo"}
- luaL_checktype(L, 1, LUA_TTABLE);
- for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) {
- luaL_checktype(L, -2, LUA_TSTRING);
- const char *key = lua_tostring(L, -2);
- int v = -1;
- if (streql(key, "name")) lupb_fielddef_dosetname(L, f, v);
- else if (streql(key, "number")) lupb_fielddef_dosetnumber(L, f, v);
- else if (streql(key, "type")) lupb_fielddef_dosettype(L, f, v);
- else if (streql(key, "label")) lupb_fielddef_dosetlabel(L, f, v);
- else if (streql(key, "lazy")) lupb_fielddef_dosetlazy(L, f, v);
- else if (streql(key, "is_extension"))
- lupb_fielddef_dosetisextension(L, f, v);
- else if (streql(key, "containing_type_name"))
- lupb_fielddef_dosetcontainingtypename(L, f, v);
- else if (streql(key, "default_value")) ; // Defer to second pass.
- else if (streql(key, "subdef")) ; // Defer to second pass.
- else if (streql(key, "subdef_name")) ; // Defer to second pass.
- else luaL_error(L, "Cannot set fielddef member '%s'", key);
- }
+// Getters
- // Have to do these in a second pass because these depend on the type, so we
- // have to make sure the type is set if the user specified one.
- for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) {
- const char *key = lua_tostring(L, -2);
- int v = -1;
- if (streql(key, "default_value")) lupb_fielddef_dosetdefault(L, f, v);
- else if (streql(key, "subdef")) lupb_fielddef_dosetsubdef(L, f, v);
- else if (streql(key, "subdef_name")) lupb_fielddef_dosetsubdefname(L, f, v);
- }
+static int lupb_fielddef_containingtype(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ lupb_def_pushwrapper(L, UPB_UPCAST(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;
}
@@ -505,13 +535,13 @@ static int lupb_fielddef_default(lua_State *L) {
switch (upb_fielddef_type(f)) {
case UPB_TYPE_INT32:
int32:
- lua_pushnumber(L, upb_fielddef_defaultint32(f)); break;
+ lupb_pushint32(L, upb_fielddef_defaultint32(f)); break;
case UPB_TYPE_INT64:
- lua_pushnumber(L, upb_fielddef_defaultint64(f)); break;
+ lupb_pushint64(L, upb_fielddef_defaultint64(f)); break;
case UPB_TYPE_UINT32:
- lua_pushnumber(L, upb_fielddef_defaultuint32(f)); break;
+ lupb_pushuint32(L, upb_fielddef_defaultuint32(f)); break;
case UPB_TYPE_UINT64:
- lua_pushnumber(L, upb_fielddef_defaultuint64(f)); break;
+ lupb_pushuint64(L, upb_fielddef_defaultuint64(f)); break;
case UPB_TYPE_DOUBLE:
lua_pushnumber(L, upb_fielddef_defaultdouble(f)); break;
case UPB_TYPE_FLOAT:
@@ -519,11 +549,17 @@ static int lupb_fielddef_default(lua_State *L) {
case UPB_TYPE_BOOL:
lua_pushboolean(L, upb_fielddef_defaultbool(f)); break;
case UPB_TYPE_ENUM:
- if (!upb_fielddef_default_is_symbolic(f))
+ if (upb_fielddef_enumhasdefaultstr(f)) {
+ goto str;
+ } else if (upb_fielddef_enumhasdefaultint32(f)) {
goto int32;
- // Fallthrough.
+ } else {
+ lua_pushnil(L);
+ }
+ break;
case UPB_TYPE_STRING:
- case UPB_TYPE_BYTES: {
+ case UPB_TYPE_BYTES:
+ str: {
size_t len;
const char *data = upb_fielddef_defaultstr(f, &len);
lua_pushlstring(L, data, len);
@@ -539,64 +575,68 @@ 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_pushnumber(L, sel);
+ lua_pushinteger(L, sel);
return 1;
} else {
return 0;
}
}
-static int lupb_fielddef_label(lua_State *L) {
+static int lupb_fielddef_hassubdef(lua_State *L) {
const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushnumber(L, upb_fielddef_label(f));
+ lua_pushboolean(L, upb_fielddef_hassubdef(f));
return 1;
}
-static int lupb_fielddef_lazy(lua_State *L) {
+static int lupb_fielddef_index(lua_State *L) {
const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushboolean(L, upb_fielddef_lazy(f));
+ lua_pushinteger(L, upb_fielddef_index(f));
return 1;
}
-static int lupb_fielddef_name(lua_State *L) {
+static int lupb_fielddef_intfmt(lua_State *L) {
const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushstring(L, upb_fielddef_name(f));
+ lua_pushinteger(L, upb_fielddef_intfmt(f));
return 1;
}
-static int lupb_fielddef_number(lua_State *L) {
+static int lupb_fielddef_isextension(lua_State *L) {
const upb_fielddef *f = lupb_fielddef_check(L, 1);
- int32_t num = upb_fielddef_number(f);
- if (num)
- lua_pushnumber(L, num);
- else
- lua_pushnil(L);
+ lua_pushboolean(L, upb_fielddef_isextension(f));
return 1;
}
-static int lupb_fielddef_selectorbase(lua_State *L) {
+static int lupb_fielddef_istagdelim(lua_State *L) {
const upb_fielddef *f = lupb_fielddef_check(L, 1);
- if (!upb_fielddef_isfrozen(f))
- luaL_error(L, "_selectorbase is only defined for frozen fielddefs");
- lua_pushnumber(L, f->selector_base);
+ lua_pushboolean(L, upb_fielddef_istagdelim(f));
return 1;
}
-static int lupb_fielddef_hassubdef(lua_State *L) {
+static int lupb_fielddef_label(lua_State *L) {
const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushboolean(L, upb_fielddef_hassubdef(f));
+ lua_pushinteger(L, upb_fielddef_label(f));
return 1;
}
-static int lupb_fielddef_containingtype(lua_State *L) {
+static int lupb_fielddef_lazy(lua_State *L) {
const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lupb_def_pushwrapper(L, UPB_UPCAST(upb_fielddef_containingtype(f)), NULL);
+ lua_pushboolean(L, upb_fielddef_lazy(f));
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));
+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;
}
@@ -620,43 +660,147 @@ static int lupb_fielddef_subdefname(lua_State *L) {
static int lupb_fielddef_type(lua_State *L) {
const upb_fielddef *f = lupb_fielddef_check(L, 1);
if (upb_fielddef_typeisset(f))
- lua_pushnumber(L, upb_fielddef_type(f));
+ lua_pushinteger(L, upb_fielddef_type(f));
else
lua_pushnil(L);
return 1;
}
-static int lupb_fielddef_index(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushnumber(L, upb_fielddef_index(f));
- 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_intfmt(lua_State *L) {
- const upb_fielddef *f = lupb_fielddef_check(L, 1);
- lua_pushnumber(L, upb_fielddef_intfmt(f));
- return 1;
+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_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_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_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_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_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_gc(lua_State *L) {
- lupb_refcounted *r = luaL_checkudata(L, 1, LUPB_FIELDDEF);
- upb_def_unref(r->def, r);
- r->refcounted = NULL;
+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 int lupb_fielddef_selectorbase(lua_State *L) {
+ const upb_fielddef *f = lupb_fielddef_check(L, 1);
+ if (!upb_fielddef_isfrozen(f))
+ luaL_error(L, "_selectorbase is only defined for frozen fielddefs");
+ lua_pushinteger(L, f->selector_base);
+ return 1;
+}
+
static const struct luaL_Reg lupb_fielddef_m[] = {
LUPB_COMMON_DEF_METHODS
@@ -696,82 +840,64 @@ static const struct luaL_Reg lupb_fielddef_m[] = {
{NULL, NULL}
};
-static const struct luaL_Reg lupb_fielddef_mm[] = {
- {"__gc", lupb_fielddef_gc},
- {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);
+}
+
const upb_msgdef *lupb_msgdef_check(lua_State *L, int narg) {
- lupb_refcounted *r = luaL_checkudata(L, narg, LUPB_MSGDEF);
- if (!r) luaL_typerror(L, narg, LUPB_MSGDEF);
- if (!r->refcounted) luaL_error(L, "called into dead msgdef");
- return upb_downcast_msgdef(r->def);
+ 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_typerror(L, narg, "not allowed on frozen value");
+ luaL_error(L, "not allowed on frozen value");
return (upb_msgdef*)m;
}
-static int lupb_msgdef_gc(lua_State *L) {
- lupb_refcounted *r = luaL_checkudata(L, 1, LUPB_MSGDEF);
- upb_def_unref(r->def, r);
- r->refcounted = NULL;
- return 0;
-}
-
static int lupb_msgdef_new(lua_State *L) {
- int narg = lua_gettop(L);
upb_msgdef *md = upb_msgdef_new(&md);
lupb_def_pushnewrapper(L, UPB_UPCAST(md), &md);
-
- if (narg == 0) return 1;
-
- // User can specify initialization values like so:
- // upb.MessageDef{full_name="MyMessage", extstart=8000, fields={...}}
- luaL_checktype(L, 1, LUA_TTABLE);
- for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) {
- luaL_checktype(L, -2, LUA_TSTRING);
- const char *key = lua_tostring(L, -2);
-
- if (streql(key, "full_name")) { // full_name="MyMessage"
- CHK(upb_def_setfullname(UPB_UPCAST(md), chkname(L, -1), &status));
- } else if (streql(key, "fields")) { // fields={...}
- // Iterate over the list of fields.
- luaL_checktype(L, -1, LUA_TTABLE);
- for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
- upb_fielddef *f = lupb_fielddef_checkmutable(L, -1);
- CHK(upb_msgdef_addfield(md, f, NULL, &status));
- }
- } else {
- // TODO: extrange=
- luaL_error(L, "Unknown initializer key '%s'", key);
- }
- }
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_refcounted_gc(L);
+ lupb_msgdef *lmd = luaL_checkudata(L, -1, LUPB_MSGDEF);
+ free(lmd->field_offsets);
+ 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);
- luaL_checktype(L, 2, LUA_TTABLE);
- int n = lua_rawlen(L, 2);
- // TODO: add upb interface that lets us avoid this malloc/free.
- upb_fielddef **fields = malloc(n * sizeof(upb_fielddef*));
- for (int i = 0; i < n; i++) {
- lua_rawgeti(L, -1, i + 1);
- fields[i] = lupb_fielddef_checkmutable(L, -1);
- lua_pop(L, 1);
- }
-
- upb_status status = UPB_STATUS_INIT;
- upb_msgdef_addfields(m, fields, n, NULL, &status);
- free(fields);
- lupb_checkstatus(L, &status);
+ upb_fielddef *f = lupb_fielddef_checkmutable(L, 2);
+ CHK(upb_msgdef_addfield(m, f, NULL, &status));
return 0;
}
@@ -823,7 +949,9 @@ static int lupb_msgdef_fields(lua_State *L) {
const upb_msgdef *m = lupb_msgdef_check(L, 1);
upb_msg_iter *i = lua_newuserdata(L, sizeof(upb_msg_iter));
upb_msg_begin(i, m);
- lua_pushcclosure(L, &lupb_msgiter_next, 1);
+ // Need to guarantee that the msgdef outlives the iter.
+ lua_pushvalue(L, 1);
+ lua_pushcclosure(L, &lupb_msgiter_next, 2);
return 1;
}
@@ -850,65 +978,27 @@ static const struct luaL_Reg lupb_msgdef_m[] = {
/* lupb_enumdef ***************************************************************/
const upb_enumdef *lupb_enumdef_check(lua_State *L, int narg) {
- lupb_refcounted *r = luaL_checkudata(L, narg, LUPB_ENUMDEF);
- if (!r) luaL_typerror(L, narg, LUPB_ENUMDEF);
- if (!r->refcounted) luaL_error(L, "called into dead enumdef");
- return upb_downcast_enumdef(r->def);
+ 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_typerror(L, narg, "not allowed on frozen value");
+ luaL_error(L, "not allowed on frozen value");
return (upb_enumdef*)f;
}
-static int lupb_enumdef_gc(lua_State *L) {
- lupb_refcounted *r = luaL_checkudata(L, 1, LUPB_ENUMDEF);
- upb_def_unref(r->def, r);
- r->refcounted = NULL;
- return 0;
-}
-
static int lupb_enumdef_new(lua_State *L) {
- int narg = lua_gettop(L);
upb_enumdef *e = upb_enumdef_new(&e);
lupb_def_pushnewrapper(L, UPB_UPCAST(e), &e);
-
- if (narg == 0) return 1;
-
- // User can specify initialization values like so:
- // upb.EnumDef{full_name="MyEnum",
- // values={
- // {"FOO_VALUE_1", 1},
- // {"FOO_VALUE_2", 2}
- // }
- // }
- luaL_checktype(L, 1, LUA_TTABLE);
- for (lua_pushnil(L); lua_next(L, 1); lua_pop(L, 1)) {
- luaL_checktype(L, -2, LUA_TSTRING);
- const char *key = lua_tostring(L, -2);
- if (streql(key, "values")) {
- for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) {
- lua_rawgeti(L, -1, 1);
- luaL_checktype(L, -1, LUA_TSTRING);
- const char *name = lua_tostring(L, -1);
- lua_rawgeti(L, -2, 2);
- CHK(upb_enumdef_addval(e, name, chkint32(L, -1, "value"), &status));
- lua_pop(L, 2); // The key/val we got from lua_rawgeti()
- }
- } else if (streql(key, "full_name")) {
- CHK(upb_def_setfullname(UPB_UPCAST(e), chkname(L, -1), &status));
- } else {
- luaL_error(L, "Unknown initializer key '%s'", key);
- }
- }
return 1;
}
static int lupb_enumdef_add(lua_State *L) {
upb_enumdef *e = lupb_enumdef_checkmutable(L, 1);
- CHK(upb_enumdef_addval(e, chkname(L, 2), chkint32(L, 3, "value"), &status));
+ const char *name = lupb_checkname(L, 2);
+ int32_t val = lupb_checkint32(L, 3);
+ CHK(upb_enumdef_addval(e, name, val, &status));
return 0;
}
@@ -923,11 +1013,13 @@ static int lupb_enumdef_value(lua_State *L) {
int type = lua_type(L, 2);
if (type == LUA_TNUMBER) {
// Pushes "nil" for a NULL pointer.
- lua_pushstring(L, upb_enumdef_iton(e, chkint32(L, 2, "value")));
+ 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_ntoi(e, lua_tostring(L, 2), &num)) {
- lua_pushnumber(L, num);
+ if (upb_enumdef_ntoi(e, key, &num)) {
+ lua_pushinteger(L, num);
} else {
lua_pushnil(L);
}
@@ -943,7 +1035,7 @@ 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_pushnumber(L, upb_enum_iter_number(i));
+ lua_pushinteger(L, upb_enum_iter_number(i));
upb_enum_next(i);
return 2;
}
@@ -952,12 +1044,13 @@ 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);
- lua_pushcclosure(L, &lupb_enumiter_next, 1);
+ // 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_enumdef_gc},
{"__len", lupb_enumdef_len},
{NULL, NULL}
};
@@ -976,83 +1069,95 @@ static const struct luaL_Reg lupb_enumdef_m[] = {
// 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.
-upb_symtab *lupb_symtab_check(lua_State *L, int narg) {
- lupb_refcounted *r = luaL_checkudata(L, narg, LUPB_SYMTAB);
- if (!r) luaL_typerror(L, narg, LUPB_SYMTAB);
- if (!r->refcounted) luaL_error(L, "called into dead symtab");
- return r->symtab;
+const upb_symtab *lupb_symtab_check(lua_State *L, int narg) {
+ return lupb_refcounted_check(L, narg, LUPB_SYMTAB);
}
-// narg is a lua table containing a list of defs to add.
-void lupb_symtab_doadd(lua_State *L, upb_symtab *s, int narg) {
- luaL_checktype(L, narg, LUA_TTABLE);
- // Iterate over table twice. First iteration to count entries and
- // check constraints.
- int n = 0;
- for (lua_pushnil(L); lua_next(L, narg); lua_pop(L, 1)) {
- lupb_def_check(L, -1);
- ++n;
- }
+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;
+}
- // Second iteration to build deflist and layout.
- upb_def **defs = malloc(n * sizeof(*defs));
- n = 0;
- for (lua_pushnil(L); lua_next(L, narg); lua_pop(L, 1)) {
- upb_def *def = lupb_def_checkmutable(L, -1);
- defs[n++] = def;
- }
+void lupb_symtab_pushwrapper(lua_State *L, const upb_symtab *s,
+ const void *ref_donor) {
+ lupb_refcounted_pushwrapper(L, UPB_UPCAST(s), LUPB_SYMTAB, ref_donor,
+ sizeof(void *));
+}
- upb_status status = UPB_STATUS_INIT;
- upb_symtab_add(s, defs, n, NULL, &status);
- free(defs);
- lupb_checkstatus(L, &status);
+void lupb_symtab_pushnewrapper(lua_State *L, const upb_symtab *s,
+ const void *ref_donor) {
+ lupb_refcounted_pushnewrapper(L, UPB_UPCAST(s), LUPB_SYMTAB, ref_donor);
}
static int lupb_symtab_new(lua_State *L) {
- int narg = lua_gettop(L);
upb_symtab *s = upb_symtab_new(&s);
- lupb_refcounted_pushnewrapper(L, UPB_UPCAST(s), LUPB_SYMTAB, &s);
- if (narg > 0) lupb_symtab_doadd(L, s, 1);
+ lupb_symtab_pushnewrapper(L, s, &s);
return 1;
}
-static int lupb_symtab_add(lua_State *L) {
- lupb_symtab_doadd(L, lupb_symtab_check(L, 1), 2);
+static int lupb_symtab_freeze(lua_State *L) {
+ upb_symtab_freeze(lupb_symtab_checkmutable(L, 1));
return 0;
}
-static int lupb_symtab_gc(lua_State *L) {
- lupb_refcounted *r = luaL_checkudata(L, 1, LUPB_SYMTAB);
- upb_symtab_unref(r->symtab, r);
- r->refcounted = NULL;
+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);
+ luaL_checktype(L, 2, LUA_TTABLE);
+ // Iterate over table twice. First iteration to count entries and
+ // check constraints.
+ int 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().
+ upb_def **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_lookup(lua_State *L) {
- upb_symtab *s = lupb_symtab_check(L, 1);
+ const upb_symtab *s = lupb_symtab_check(L, 1);
for (int i = 2; i <= lua_gettop(L); i++) {
- const upb_def *def =
- upb_symtab_lookup(s, luaL_checkstring(L, i), &def);
- lupb_def_pushwrapper(L, def, &def);
+ 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_symtab_getdefs(lua_State *L) {
- upb_symtab *s = lupb_symtab_check(L, 1);
- upb_deftype_t type = luaL_checkint(L, 2);
- int count;
- const upb_def **defs = upb_symtab_getdefs(s, type, &defs, &count);
+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;
+}
- // Create the table in which we will return the defs.
- lua_createtable(L, count, 0);
- for (int i = 0; i < count; i++) {
- const upb_def *def = defs[i];
- lupb_def_pushwrapper(L, def, &defs);
- lua_rawseti(L, -2, i + 1);
- }
- free(defs);
+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;
}
@@ -1061,7 +1166,7 @@ static int lupb_symtab_getdefs(lua_State *L) {
// the descriptor.proto schema and the protobuf binary format.
static int lupb_symtab_load_descriptor(lua_State *L) {
size_t len;
- upb_symtab *s = lupb_symtab_check(L, 1);
+ upb_symtab *s = lupb_symtab_checkmutable(L, 1);
const char *str = luaL_checklstring(L, 2, &len);
CHK(upb_load_descriptor_into_symtab(s, str, len, &status));
return 0;
@@ -1069,67 +1174,598 @@ static int lupb_symtab_load_descriptor(lua_State *L) {
static const struct luaL_Reg lupb_symtab_m[] = {
{"add", lupb_symtab_add},
- {"getdefs", lupb_symtab_getdefs},
+ {"defs", lupb_symtab_defs},
+ {"freeze", lupb_symtab_freeze},
+ {"is_frozen", lupb_symtab_isfrozen},
{"lookup", lupb_symtab_lookup},
{"load_descriptor", lupb_symtab_load_descriptor},
{NULL, NULL}
};
-static const struct luaL_Reg lupb_symtab_mm[] = {
- {"__gc", lupb_symtab_gc},
+
+/* 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;
+ char data[];
+} 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:
+ 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.
+#define DEREF(msg, ofs, type) *(type*)(&msg->data[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) {
+ const char *fieldname = luaL_checkstring(L, fieldarg);
+ const upb_fielddef *f = upb_msgdef_ntof(lmd->md, fieldname);
+
+ 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) {
+ 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.
+ upb_msg_iter i;
+ for (upb_msg_begin(&i, lmd->md); !upb_msg_done(&i); upb_msg_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.
+
+ int idx = 1;
+ for (upb_msg_begin(&i, lmd->md); !upb_msg_done(&i); upb_msg_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_VAR(created, !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);
+
+ // 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);
+
+ lupb_msg *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 value.
+}
+
+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 msg->data[idx / 8] & (1 << (idx % 8));
+}
+
+static void lupb_msg_set(lupb_msg *msg, const upb_fielddef *f) {
+ uint16_t idx = upb_fielddef_index(f);
+ msg->data[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);
+
+ if (!upb_fielddef_isseq(f) && !lupb_msg_has(msg, f)) {
+ lua_pushnil(L);
+ return 1;
+ }
+
+ int 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);
+
+ lupb_msg_set(msg, f);
+
+ int 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) {
+ msg->data[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;
+ 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_def_pushwrapper(L, UPB_UPCAST(upb_handlers_msgdef(h)), NULL);
+ lupb_msgdef *lmd = lupb_msg_assignoffsets(L, -1);
+ upb_msg_iter i;
+ upb_msg_begin(&i, upb_handlers_msgdef(h));
+ for (; !upb_msg_done(&i); upb_msg_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 += offsetof(lupb_msg, data) * 8;
+ ofs += offsetof(lupb_msg, data);
+ 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_def_freeze(lua_State *L) {
+static int lupb_freeze(lua_State *L) {
int n = lua_gettop(L);
- upb_def **defs = malloc(n * sizeof(upb_def*));
+ // 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 (int i = 0; i < n; i++) {
// Could allow an array of defs here also.
defs[i] = lupb_def_checkmutable(L, i + 1);
}
- upb_status s = UPB_STATUS_INIT;
- upb_def_freeze(defs, n, &s);
- free(defs);
- lupb_checkstatus(L, &s);
+ CHK(upb_def_freeze(defs, n, &status));
return 0;
}
static const struct luaL_Reg lupb_toplevel_m[] = {
+ {"Array", lupb_array_new},
{"EnumDef", lupb_enumdef_new},
{"FieldDef", lupb_fielddef_new},
+ {"Message", lupb_msg_new},
{"MessageDef", lupb_msgdef_new},
{"SymbolTable", lupb_symtab_new},
- {"freeze", lupb_def_freeze},
+ {"freeze", lupb_freeze},
{NULL, NULL}
};
-// Register the given type with the given methods and metamethods.
-static void lupb_register_type(lua_State *L, const char *name,
- const luaL_Reg *m, const luaL_Reg *mm) {
+void lupb_register_type(lua_State *L, const char *name, const luaL_Reg *m,
+ const luaL_Reg *mm, bool refcount_gc) {
luaL_newmetatable(L, name);
- lupb_setfuncs(L, mm); // Register all mm in the metatable.
- lua_createtable(L, 0, 0);
- // Methods go in the mt's __index method. This implies that you can't
- // implement __index.
- lupb_setfuncs(L, m);
- lua_setfield(L, -2, "__index");
+
+ 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't
+ // implement __index and also have methods.
+ lua_getfield(L, -1, "__index");
+ lupb_assert(L, lua_isnil(L, -1));
+ lua_pop(L, 1);
+
+ lua_createtable(L, 0, 0);
+ lupb_setfuncs(L, m);
+ lua_setfield(L, -2, "__index");
+ }
+
lua_pop(L, 1); // The mt.
}
static void lupb_setfieldi(lua_State *L, const char *field, int i) {
- lua_pushnumber(L, i);
+ lua_pushinteger(L, i);
lua_setfield(L, -2, field);
}
int luaopen_upb(lua_State *L) {
- lupb_register_type(L, LUPB_MSGDEF, lupb_msgdef_m, lupb_msgdef_mm);
- 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_SYMTAB, lupb_symtab_m, lupb_symtab_mm);
+ static char module_key;
+ if (lupb_openlib(L, &module_key, "upb", 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_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);
@@ -1139,26 +1775,6 @@ int luaopen_upb(lua_State *L) {
lua_setmetatable(L, -2);
lua_setfield(L, LUA_REGISTRYINDEX, LUPB_OBJCACHE);
- lupb_newlib(L, "upb", lupb_toplevel_m);
-
- // Define a couple functions as Lua source (kept here instead of a separate
- // Lua file so that upb.so is self-contained)
- const char *lua_source =
- "return function(upb)\n"
- " upb.build_defs = function(defs)\n"
- " local symtab = upb.SymbolTable(defs)\n"
- " return symtab:getdefs(upb.DEF_ANY)\n"
- " end\n"
- "end";
-
- if (luaL_dostring(L, lua_source) != 0)
- lua_error(L);
-
- // Call the chunk that will define the extra functions on upb, passing our
- // package dictionary as the argument.
- lua_pushvalue(L, -2);
- lua_call(L, 1, 0);
-
// Register constants.
lupb_setfieldi(L, "LABEL_OPTIONAL", UPB_LABEL_OPTIONAL);
lupb_setfieldi(L, "LABEL_REQUIRED", UPB_LABEL_REQUIRED);
@@ -1220,8 +1836,14 @@ int luaopen_upb(lua_State *L) {
lupb_setfieldi(L, "HANDLER_STARTSEQ", UPB_HANDLER_STARTSEQ);
lupb_setfieldi(L, "HANDLER_ENDSEQ", UPB_HANDLER_ENDSEQ);
+ // Call the chunk that will define the extra functions on upb, passing our
+ // package dictionary as the argument.
+ if (luaL_loadbuffer(L, upb_lua, sizeof(upb_lua), "upb.lua") ||
+ lua_pcall(L, 0, LUA_MULTRET, 0)) {
+ lua_error(L);
+ }
+ lua_pushvalue(L, -2);
+ lua_call(L, 1, 0);
+
return 1; // Return package table.
}
-
-// Alternate names so that the library can be loaded as upb5_1 etc.
-int LUPB_OPENFUNC(upb)(lua_State *L) { return luaopen_upb(L); }
diff --git a/upb/bindings/lua/upb.h b/upb/bindings/lua/upb.h
index e6b4f2f..99fe8fe 100644
--- a/upb/bindings/lua/upb.h
+++ b/upb/bindings/lua/upb.h
@@ -10,36 +10,120 @@
#ifndef UPB_LUA_UPB_H_
#define UPB_LUA_UPB_H_
+#include "lauxlib.h"
#include "upb/def.h"
+#include "upb/handlers.h"
+#include "upb/symtab.h"
// Lua 5.1/5.2 compatibility code.
#if LUA_VERSION_NUM == 501
#define lua_rawlen lua_objlen
-#define lupb_newlib(L, name, l) luaL_register(L, name, l)
-#define lupb_setfuncs(L, l) luaL_register(L, NULL, l)
-#define LUPB_OPENFUNC(mod) luaopen_ ## mod ## upb5_1
+
+// Lua >= 5.2's getuservalue/setuservalue functions do not exist in prior
+// versions but the older function lua_getfenv() can provide 100% of its
+// capabilities (the reverse is not true).
+#define lua_getuservalue(L, index) lua_getfenv(L, index)
+#define lua_setuservalue(L, index) lua_setfenv(L, index)
void *luaL_testudata(lua_State *L, int ud, const char *tname);
#elif LUA_VERSION_NUM == 502
-// Lua 5.2 modules are not expected to set a global variable, so "name" is
-// unused.
-#define lupb_newlib(L, name, l) luaL_newlib(L, l)
-#define lupb_setfuncs(L, l) luaL_setfuncs(L, l, 0)
int luaL_typerror(lua_State *L, int narg, const char *tname);
-#define LUPB_OPENFUNC(mod) luaopen_ ## mod ## upb5_2
#else
#error Only Lua 5.1 and 5.2 are supported
#endif
+#define lupb_assert(L, predicate) \
+ if (!(predicate)) \
+ luaL_error(L, "internal error: %s, %s:%d ", #predicate, __FILE__, __LINE__);
+
+// Function for initializing the core library. This function is idempotent,
+// and should be called at least once before calling any of the functions that
+// construct core upb types.
+int luaopen_upb(lua_State *L);
+
+// Gets or creates a package table for a C module that is uniquely identified by
+// "ptr". The easiest way to supply a unique "ptr" is to pass the address of a
+// static variable private in the module's .c file.
+//
+// If this module has already been registered in this lua_State, pushes it and
+// returns true.
+//
+// Otherwise, creates a new module table for this module with the given name,
+// pushes it, and registers the given top-level functions in it. It also sets
+// it as a global variable, but only if the current version of Lua expects that
+// (ie Lua 5.1/LuaJIT).
+//
+// If "false" is returned, the caller is guaranteed that this lib has not been
+// registered in this Lua state before (regardless of any funny business the
+// user might have done to the global state), so the caller can safely perform
+// one-time initialization.
+bool lupb_openlib(lua_State *L, void *ptr, const char *name,
+ const luaL_Reg *funcs);
+
+// Custom check/push functions. Unlike the Lua equivalents, they are pinned to
+// specific types (instead of lua_Number, etc), and do not allow any implicit
+// conversion or data loss.
+int64_t lupb_checkint64(lua_State *L, int narg);
+int32_t lupb_checkint32(lua_State *L, int narg);
+uint64_t lupb_checkuint64(lua_State *L, int narg);
+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_checkname(lua_State *L, int narg);
+
+void lupb_pushint64(lua_State *L, int64_t val);
+void lupb_pushint32(lua_State *L, int32_t val);
+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.
+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);
const upb_enumdef *lupb_enumdef_check(lua_State *L, int narg);
-const char *lupb_checkname(lua_State *L, int narg);
-bool lupb_def_pushwrapper(lua_State *L, const upb_def *def, const void *owner);
+const upb_fielddef *lupb_fielddef_check(lua_State *L, int narg);
+const upb_symtab *lupb_symtab_check(lua_State *L, int narg);
+
+void lupb_refcounted_pushnewrapper(lua_State *L, const upb_refcounted *obj,
+ const char *type, const void *ref_donor);
+bool lupb_def_pushwrapper(lua_State *L, const upb_def *def,
+ const void *ref_donor);
void lupb_def_pushnewrapper(lua_State *L, const upb_def *def,
- const void *owner);
+ const void *ref_donor);
+void lupb_symtab_pushwrapper(lua_State *L, const upb_symtab *s,
+ const void *ref_donor);
+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);
+
+// 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, 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);
#endif // UPB_LUA_UPB_H_
diff --git a/upb/bindings/lua/upb.lua b/upb/bindings/lua/upb.lua
new file mode 100644
index 0000000..bda9dfe
--- /dev/null
+++ b/upb/bindings/lua/upb.lua
@@ -0,0 +1,146 @@
+--
+-- upb - a minimalist implementation of protocol buffers.
+--
+-- Copyright (c) 2009 Google Inc. See LICENSE for details.
+-- Author: Josh Haberman <jhaberman@gmail.com>
+--
+-- Pure-Lua support functions that are part of the "upb" module.
+-- This file is embedded and packaged into the "upb" C module binary -- it
+-- should not be installed or used directly!
+
+return function(upb)
+ -- A convenience function for building/linking/freezing defs
+ -- while maintaining their original order.
+ --
+ -- Sample usage:
+ -- local m1, m2 = upb.build_defs{
+ -- upb.MessageDef{full_name = "M1", fields = {
+ -- upb.FieldDef{
+ -- name = "m2",
+ -- number = 1,
+ -- type = upb.TYPE_MESSAGE,
+ -- subdef_name = ".M2"
+ -- },
+ -- }
+ -- },
+ -- upb.MessageDef{full_name = "M2"}
+ -- }
+ upb.build_defs = function(defs)
+ upb.SymbolTable(defs)
+ -- Lua 5.2 puts unpack in the table library.
+ return (unpack or table.unpack)(defs)
+ end
+
+ local ipairs_iter = function(array, last_index)
+ local next_index = last_index + 1
+ if next_index > #array then
+ return nil
+ end
+ return next_index, array[next_index]
+ end
+
+ -- For iterating over the indexes and values of a upb.Array.
+ --
+ -- for i, val in upb.ipairs(array) do
+ -- -- ...
+ -- end
+ upb.ipairs = function(array)
+ return ipairs_iter, array, 0
+ end
+
+ local set_named = function(obj, init)
+ for k, v in pairs(init) do
+ local func = obj["set_" .. k]
+ if not func then
+ error("Cannot set member: " .. k)
+ end
+ func(obj, v)
+ end
+ end
+
+ -- Capture references to the functions we're wrapping.
+ local RealFieldDef = upb.FieldDef
+ local RealEnumDef = upb.EnumDef
+ local RealMessageDef = upb.MessageDef
+ local RealSymbolTable = upb.SymbolTable
+
+ -- FieldDef constructor; a wrapper around the real constructor that can
+ -- set initial properties.
+ --
+ -- User can specify initialization values like so:
+ -- upb.FieldDef{label=upb.LABEL_REQUIRED, name="my_field", number=5,
+ -- type=upb.TYPE_INT32, default_value=12, type_name="Foo"}
+ upb.FieldDef = function(init)
+ local f = RealFieldDef()
+
+ if init then
+ -- Other members are often dependent on type, so set that first.
+ if init.type then
+ f:set_type(init.type)
+ init.type = nil
+ end
+
+ set_named(f, init)
+ end
+
+ return f
+ end
+
+
+ -- MessageDef constructor; a wrapper around the real constructor that can
+ -- set initial properties.
+ --
+ -- User can specify initialization values like so:
+ -- upb.MessageDef{full_name="MyMessage", extstart=8000, fields={...}}
+ upb.MessageDef = function(init)
+ local m = RealMessageDef()
+
+ if init then
+ for _, f in pairs(init.fields or {}) do
+ m:add(f)
+ end
+ init.fields = nil
+
+ set_named(m, init)
+ end
+
+ return m
+ end
+
+ -- EnumDef constructor; a wrapper around the real constructor that can
+ -- set initial properties.
+ --
+ -- User can specify initialization values like so:
+ -- upb.EnumDef{full_name="MyEnum",
+ -- values={
+ -- {"FOO_VALUE_1", 1},
+ -- {"FOO_VALUE_2", 2}
+ -- }
+ -- }
+ upb.EnumDef = function(init)
+ local e = RealEnumDef()
+
+ if init then
+ for _, val in pairs(init.values or {}) do
+ e:add(val[1], val[2])
+ end
+ init.values = nil
+
+ set_named(e, init)
+ end
+
+ return e
+ end
+
+ -- SymbolTable constructor; a wrapper around the real constructor that can
+ -- add an initial set of defs.
+ upb.SymbolTable = function(defs)
+ local s = RealSymbolTable()
+
+ if defs then
+ s:add(defs)
+ end
+
+ return s
+ end
+end
diff --git a/upb/bindings/lua/upb.pb.c b/upb/bindings/lua/upb.pb.c
new file mode 100644
index 0000000..c9f1f47
--- /dev/null
+++ b/upb/bindings/lua/upb.pb.c
@@ -0,0 +1,106 @@
+/*
+ * upb - a minimalist implementation of protocol buffers.
+ *
+ * Copyright (c) 2014 Google Inc. See LICENSE for details.
+ * Author: Josh Haberman <jhaberman@gmail.com>
+ *
+ * A Lua extension for upb.pb.
+ *
+ * Exposes all the types defined in upb/pb/{*}.h
+ * Also defines a few convenience functions on top.
+ */
+
+#include "upb/bindings/lua/upb.h"
+#include "upb/pb/decoder.h"
+
+#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);
+
+ upb_pbdecodermethodopts opts;
+ upb_pbdecodermethodopts_init(&opts, handlers);
+
+ const upb_pbdecodermethod *m = upb_pbdecodermethod_new(&opts, &m);
+ upb_handlers_unref(handlers, &handlers);
+ lupb_refcounted_pushnewrapper(L, UPB_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) {
+ size_t len;
+ const upb_pbdecodermethod *method = lupb_pbdecodermethod_check(L, 1);
+ const char *pb = lua_tolstring(L, 2, &len);
+
+ 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.
+ void *msg = lua_touserdata(L, -1);
+
+ // Handlers need this.
+ lua_getuservalue(L, -1);
+
+ upb_pbdecoder decoder;
+ upb_status status = UPB_STATUS_INIT;
+ upb_pbdecoder_init(&decoder, method, &status);
+ upb_sink sink;
+ upb_sink_reset(&sink, handlers, msg);
+ upb_pbdecoder_resetoutput(&decoder, &sink);
+ upb_bufsrc_putbuf(pb, len, upb_pbdecoder_input(&decoder));
+ // TODO: Our need to call uninit isn't longjmp-safe; what if the decode
+ // triggers a Lua error? uninit is only needed if the decoder
+ // dynamically-allocated a growing stack -- ditch this feature and live with
+ // the compile-time limit? Or have a custom allocation function that
+ // allocates Lua GC-rooted memory?
+ upb_pbdecoder_uninit(&decoder);
+ lupb_checkstatus(L, &status);
+
+ lua_pop(L, 1); // Uservalue.
+
+ return 1;
+}
+
+static const struct luaL_Reg lupb_pbdecodermethod_m[] = {
+ {"parse", lupb_pbdecodermethod_parse},
+ {NULL, NULL}
+};
+
+static const struct luaL_Reg toplevel_m[] = {
+ {"DecoderMethod", lupb_pbdecodermethod_new},
+ {NULL, NULL}
+};
+
+int luaopen_upb_pb(lua_State *L) {
+ luaopen_upb(L);
+
+ static char module_key;
+ if (lupb_openlib(L, &module_key, "upb.pb", toplevel_m)) {
+ return 1;
+ }
+
+ lupb_register_type(L, LUPB_PBDECODERMETHOD, lupb_pbdecodermethod_m, NULL,
+ true);
+
+ return 1;
+}
diff --git a/upb/bindings/lua/upb/descriptor.c b/upb/bindings/lua/upb/descriptor.c
new file mode 100644
index 0000000..9915e11
--- /dev/null
+++ b/upb/bindings/lua/upb/descriptor.c
@@ -0,0 +1,20 @@
+/*
+ * upb - a minimalist implementation of protocol buffers.
+ *
+ * Copyright (c) 2009 Google Inc. See LICENSE for details.
+ * Author: Josh Haberman <jhaberman@gmail.com>
+ *
+ * A Lua extension for upb/descriptor.
+ */
+
+#include "upb/bindings/lua/upb.h"
+
+static const struct luaL_Reg toplevel_m[] = {
+ {NULL, NULL}
+};
+
+int luaopen_upb_descriptor(lua_State *L) {
+ lupb_newlib(L, "upb.descriptor", toplevel_m);
+
+ return 1; // Return package table.
+}
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback