1 /*
2 ** require("lua") -- A Lua extension for upb.
3 **
4 ** Exposes only the core library
5 ** (sub-libraries are exposed in other extensions).
6 **
7 ** 64-bit woes: Lua can only represent numbers of type lua_Number (which is
8 ** double unless the user specifically overrides this). Doubles can represent
9 ** the entire range of 64-bit integers, but lose precision once the integers are
10 ** greater than 2^53.
11 **
12 ** Lua 5.3 is adding support for integers, which will allow for 64-bit
13 ** integers (which can be interpreted as signed or unsigned).
14 **
15 ** LuaJIT supports 64-bit signed and unsigned boxed representations
16 ** through its "cdata" mechanism, but this is not portable to regular Lua.
17 **
18 ** Hopefully Lua 5.3 will come soon enough that we can either use Lua 5.3
19 ** integer support or LuaJIT 64-bit cdata for users that need the entire
20 ** domain of [u]int64 values.
21 */
22
23 #include "upb/bindings/lua/upb.h"
24
25 #include <float.h>
26 #include <math.h>
27 #include <stdlib.h>
28 #include <string.h>
29
30 #include "lauxlib.h"
31 #include "upb/msg.h"
32
33 /* Lua compatibility code *****************************************************/
34
35 /* Shims for upcoming Lua 5.3 functionality. */
lua_isinteger(lua_State * L,int argn)36 static bool lua_isinteger(lua_State *L, int argn) {
37 LUPB_UNUSED(L);
38 LUPB_UNUSED(argn);
39 return false;
40 }
41
42
43 /* Utility functions **********************************************************/
44
lupb_checkstatus(lua_State * L,upb_status * s)45 void lupb_checkstatus(lua_State *L, upb_status *s) {
46 if (!upb_ok(s)) {
47 lua_pushstring(L, upb_status_errmsg(s));
48 lua_error(L);
49 }
50 }
51
52 /* Pushes a new userdata with the given metatable. */
lupb_newuserdata(lua_State * L,size_t size,int n,const char * type)53 void *lupb_newuserdata(lua_State *L, size_t size, int n, const char *type) {
54 #if LUA_VERSION_NUM >= 504
55 void *ret = lua_newuserdatauv(L, size, n);
56 #else
57 void *ret = lua_newuserdata(L, size);
58 lua_createtable(L, 0, n);
59 lua_setuservalue(L, -2);
60 #endif
61
62 /* Set metatable. */
63 luaL_getmetatable(L, type);
64 assert(!lua_isnil(L, -1)); /* Should have been created by luaopen_upb. */
65 lua_setmetatable(L, -2);
66
67 return ret;
68 }
69
70 #if LUA_VERSION_NUM < 504
lua_setiuservalue(lua_State * L,int index,int n)71 int lua_setiuservalue(lua_State *L, int index, int n) {
72 lua_getuservalue(L, index);
73 lua_insert(L, -2);
74 lua_rawseti(L, -2, n);
75 lua_pop(L, 1);
76 return 1;
77 }
78
lua_getiuservalue(lua_State * L,int index,int n)79 int lua_getiuservalue(lua_State *L, int index, int n) {
80 lua_getuservalue(L, index);
81 lua_rawgeti(L, -1, n);
82 lua_replace(L, -2);
83 return 1;
84 }
85 #endif
86
lupb_register_type(lua_State * L,const char * name,const luaL_Reg * m,const luaL_Reg * mm)87 void lupb_register_type(lua_State *L, const char *name, const luaL_Reg *m,
88 const luaL_Reg *mm) {
89 luaL_newmetatable(L, name);
90
91 if (mm) {
92 lupb_setfuncs(L, mm);
93 }
94
95 if (m) {
96 /* Methods go in the mt's __index method. This implies that you can'
97 * implement __index and also have methods. */
98 lua_getfield(L, -1, "__index");
99 lupb_assert(L, lua_isnil(L, -1));
100 lua_pop(L, 1);
101
102 lua_createtable(L, 0, 0);
103 lupb_setfuncs(L, m);
104 lua_setfield(L, -2, "__index");
105 }
106
107 lua_pop(L, 1); /* The mt. */
108 }
109
110 /* Scalar type mapping ********************************************************/
111
112 /* Functions that convert scalar/primitive values (numbers, strings, bool)
113 * between Lua and C/upb. Handles type/range checking. */
114
lupb_checkbool(lua_State * L,int narg)115 bool lupb_checkbool(lua_State *L, int narg) {
116 if (!lua_isboolean(L, narg)) {
117 luaL_error(L, "must be true or false");
118 }
119 return lua_toboolean(L, narg);
120 }
121
122 /* Unlike luaL_checkstring(), this does not allow implicit conversion to
123 * string. */
lupb_checkstring(lua_State * L,int narg,size_t * len)124 const char *lupb_checkstring(lua_State *L, int narg, size_t *len) {
125 if (lua_type(L, narg) != LUA_TSTRING) {
126 luaL_error(L, "Expected string");
127 }
128
129 return lua_tolstring(L, narg, len);
130 }
131
132 /* Unlike luaL_checkinteger, these do not implicitly convert from string or
133 * round an existing double value. We allow floating-point input, but only if
134 * the actual value is integral. */
135 #define INTCHECK(type, ctype) \
136 ctype lupb_check##type(lua_State *L, int narg) { \
137 double n; \
138 ctype i; \
139 if (lua_isinteger(L, narg)) { \
140 return lua_tointeger(L, narg); \
141 } \
142 \
143 /* Prevent implicit conversion from string. */ \
144 luaL_checktype(L, narg, LUA_TNUMBER); \
145 n = lua_tonumber(L, narg); \
146 \
147 i = (ctype)n; \
148 if ((double)i != n) { \
149 /* double -> ctype truncated or rounded. */ \
150 luaL_error(L, "number %f was not an integer or out of range for " #type, \
151 n); \
152 } \
153 return i; \
154 } \
155 void lupb_push##type(lua_State *L, ctype val) { \
156 /* TODO: push integer for Lua >= 5.3, 64-bit cdata for LuaJIT. */ \
157 /* This is lossy for some [u]int64 values, which isn't great, but */ \
158 /* crashing when we encounter these values seems worse. */ \
159 lua_pushnumber(L, val); \
160 }
161
INTCHECK(int64,int64_t)162 INTCHECK(int64, int64_t)
163 INTCHECK(int32, int32_t)
164 INTCHECK(uint64, uint64_t)
165 INTCHECK(uint32, uint32_t)
166
167 double lupb_checkdouble(lua_State *L, int narg) {
168 /* If we were being really hard-nosed here, we'd check whether the input was
169 * an integer that has no precise double representation. But doubles aren't
170 * generally expected to be exact like integers are, and worse this could
171 * cause data-dependent runtime errors: one run of the program could work fine
172 * because the integer calculations happened to be exactly representable in
173 * double, while the next could crash because of subtly different input. */
174
175 luaL_checktype(L, narg, LUA_TNUMBER); /* lua_tonumber() auto-converts. */
176 return lua_tonumber(L, narg);
177 }
178
lupb_checkfloat(lua_State * L,int narg)179 float lupb_checkfloat(lua_State *L, int narg) {
180 /* We don't worry about checking whether the input can be exactly converted to
181 * float -- see above. */
182
183 luaL_checktype(L, narg, LUA_TNUMBER); /* lua_tonumber() auto-converts. */
184 return lua_tonumber(L, narg);
185 }
186
lupb_pushdouble(lua_State * L,double d)187 void lupb_pushdouble(lua_State *L, double d) {
188 lua_pushnumber(L, d);
189 }
190
lupb_pushfloat(lua_State * L,float d)191 void lupb_pushfloat(lua_State *L, float d) {
192 lua_pushnumber(L, d);
193 }
194
195 /* Library entry point ********************************************************/
196
luaopen_lupb(lua_State * L)197 int luaopen_lupb(lua_State *L) {
198 #if LUA_VERSION_NUM == 501
199 const struct luaL_Reg funcs[] = {{NULL, NULL}};
200 luaL_register(L, "upb_c", funcs);
201 #else
202 lua_createtable(L, 0, 8);
203 #endif
204 lupb_def_registertypes(L);
205 lupb_msg_registertypes(L);
206 return 1; /* Return package table. */
207 }
208