• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
87 /* We use this function as the __index metamethod when a type has both methods
88  * and an __index metamethod. */
lupb_indexmm(lua_State * L)89 int lupb_indexmm(lua_State *L) {
90   /* Look up in __index table (which is a closure param). */
91   lua_pushvalue(L, 2);
92   lua_rawget(L, lua_upvalueindex(1));
93   if (!lua_isnil(L, -1)) {
94     return 1;
95   }
96 
97   /* Not found, chain to user __index metamethod. */
98   lua_pushvalue(L, lua_upvalueindex(2));
99   lua_pushvalue(L, 1);
100   lua_pushvalue(L, 2);
101   lua_call(L, 2, 1);
102   return 1;
103 }
104 
lupb_register_type(lua_State * L,const char * name,const luaL_Reg * m,const luaL_Reg * mm)105 void lupb_register_type(lua_State *L, const char *name, const luaL_Reg *m,
106                         const luaL_Reg *mm) {
107   luaL_newmetatable(L, name);
108 
109   if (mm) {
110     lupb_setfuncs(L, mm);
111   }
112 
113   if (m) {
114     lua_createtable(L, 0, 0);  /* __index table */
115     lupb_setfuncs(L, m);
116 
117     /* Methods go in the mt's __index slot.  If the user also specified an
118      * __index metamethod, use our custom lupb_indexmm() that can check both. */
119     lua_getfield(L, -2, "__index");
120     if (lua_isnil(L, -1)) {
121       lua_pop(L, 1);
122     } else {
123       lua_pushcclosure(L, &lupb_indexmm, 2);
124     }
125     lua_setfield(L, -2, "__index");
126   }
127 
128   lua_pop(L, 1);  /* The mt. */
129 }
130 
131 /* Scalar type mapping ********************************************************/
132 
133 /* Functions that convert scalar/primitive values (numbers, strings, bool)
134  * between Lua and C/upb.  Handles type/range checking. */
135 
lupb_checkbool(lua_State * L,int narg)136 bool lupb_checkbool(lua_State *L, int narg) {
137   if (!lua_isboolean(L, narg)) {
138     luaL_error(L, "must be true or false");
139   }
140   return lua_toboolean(L, narg);
141 }
142 
143 /* Unlike luaL_checkstring(), this does not allow implicit conversion to
144  * string. */
lupb_checkstring(lua_State * L,int narg,size_t * len)145 const char *lupb_checkstring(lua_State *L, int narg, size_t *len) {
146   if (lua_type(L, narg) != LUA_TSTRING) {
147     luaL_error(L, "Expected string");
148   }
149 
150   return lua_tolstring(L, narg, len);
151 }
152 
153 /* Unlike luaL_checkinteger, these do not implicitly convert from string or
154  * round an existing double value.  We allow floating-point input, but only if
155  * the actual value is integral. */
156 #define INTCHECK(type, ctype)                                                  \
157   ctype lupb_check##type(lua_State *L, int narg) {                             \
158     double n;                                                                  \
159     ctype i;                                                                   \
160     if (lua_isinteger(L, narg)) {                                              \
161       return lua_tointeger(L, narg);                                           \
162     }                                                                          \
163                                                                                \
164     /* Prevent implicit conversion from string. */                             \
165     luaL_checktype(L, narg, LUA_TNUMBER);                                      \
166     n = lua_tonumber(L, narg);                                                 \
167                                                                                \
168     i = (ctype)n;                                                              \
169     if ((double)i != n) {                                                      \
170       /* double -> ctype truncated or rounded. */                              \
171       luaL_error(L, "number %f was not an integer or out of range for " #type, \
172                  n);                                                           \
173     }                                                                          \
174     return i;                                                                  \
175   }                                                                            \
176   void lupb_push##type(lua_State *L, ctype val) {                              \
177     /* TODO: push integer for Lua >= 5.3, 64-bit cdata for LuaJIT. */          \
178     /* This is lossy for some [u]int64 values, which isn't great, but */       \
179     /* crashing when we encounter these values seems worse. */                 \
180     lua_pushnumber(L, val);                                                    \
181   }
182 
INTCHECK(int64,int64_t)183 INTCHECK(int64,  int64_t)
184 INTCHECK(int32,  int32_t)
185 INTCHECK(uint64, uint64_t)
186 INTCHECK(uint32, uint32_t)
187 
188 double lupb_checkdouble(lua_State *L, int narg) {
189   /* If we were being really hard-nosed here, we'd check whether the input was
190    * an integer that has no precise double representation.  But doubles aren't
191    * generally expected to be exact like integers are, and worse this could
192    * cause data-dependent runtime errors: one run of the program could work fine
193    * because the integer calculations happened to be exactly representable in
194    * double, while the next could crash because of subtly different input. */
195 
196   luaL_checktype(L, narg, LUA_TNUMBER);  /* lua_tonumber() auto-converts. */
197   return lua_tonumber(L, narg);
198 }
199 
lupb_checkfloat(lua_State * L,int narg)200 float lupb_checkfloat(lua_State *L, int narg) {
201   /* We don't worry about checking whether the input can be exactly converted to
202    * float -- see above. */
203 
204   luaL_checktype(L, narg, LUA_TNUMBER);  /* lua_tonumber() auto-converts. */
205   return lua_tonumber(L, narg);
206 }
207 
lupb_pushdouble(lua_State * L,double d)208 void lupb_pushdouble(lua_State *L, double d) {
209   lua_pushnumber(L, d);
210 }
211 
lupb_pushfloat(lua_State * L,float d)212 void lupb_pushfloat(lua_State *L, float d) {
213   lua_pushnumber(L, d);
214 }
215 
216 /* Library entry point ********************************************************/
217 
luaopen_lupb(lua_State * L)218 int luaopen_lupb(lua_State *L) {
219 #if LUA_VERSION_NUM == 501
220   const struct luaL_Reg funcs[] = {{NULL, NULL}};
221   luaL_register(L, "upb_c", funcs);
222 #else
223   lua_createtable(L, 0, 8);
224 #endif
225   lupb_def_registertypes(L);
226   lupb_msg_registertypes(L);
227   return 1;  /* Return package table. */
228 }
229