summaryrefslogtreecommitdiff
path: root/grpc/third_party/upb/upb/bindings/lua/upb.c
blob: c247c03746890f7fb777ff89cf4e3c0c3e91b131 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/*
** require("lua") -- 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 "upb/bindings/lua/upb.h"

#include <float.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>

#include "lauxlib.h"
#include "upb/msg.h"

/* Lua compatibility code *****************************************************/

/* Shims for upcoming Lua 5.3 functionality. */
static bool lua_isinteger(lua_State *L, int argn) {
  LUPB_UNUSED(L);
  LUPB_UNUSED(argn);
  return false;
}


/* Utility functions **********************************************************/

void lupb_checkstatus(lua_State *L, upb_status *s) {
  if (!upb_ok(s)) {
    lua_pushstring(L, upb_status_errmsg(s));
    lua_error(L);
  }
}

/* Pushes a new userdata with the given metatable. */
void *lupb_newuserdata(lua_State *L, size_t size, int n, const char *type) {
#if LUA_VERSION_NUM >= 504
  void *ret = lua_newuserdatauv(L, size, n);
#else
  void *ret = lua_newuserdata(L, size);
  lua_createtable(L, 0, n);
  lua_setuservalue(L, -2);
#endif

  /* Set metatable. */
  luaL_getmetatable(L, type);
  assert(!lua_isnil(L, -1));  /* Should have been created by luaopen_upb. */
  lua_setmetatable(L, -2);

  return ret;
}

#if LUA_VERSION_NUM < 504
int lua_setiuservalue(lua_State *L, int index, int n) {
  lua_getuservalue(L, index);
  lua_insert(L, -2);
  lua_rawseti(L, -2, n);
  lua_pop(L, 1);
  return 1;
}

int lua_getiuservalue(lua_State *L, int index, int n) {
  lua_getuservalue(L, index);
  lua_rawgeti(L, -1, n);
  lua_replace(L, -2);
  return 1;
}
#endif

/* We use this function as the __index metamethod when a type has both methods
 * and an __index metamethod. */
int lupb_indexmm(lua_State *L) {
  /* Look up in __index table (which is a closure param). */
  lua_pushvalue(L, 2);
  lua_rawget(L, lua_upvalueindex(1));
  if (!lua_isnil(L, -1)) {
    return 1;
  }

  /* Not found, chain to user __index metamethod. */
  lua_pushvalue(L, lua_upvalueindex(2));
  lua_pushvalue(L, 1);
  lua_pushvalue(L, 2);
  lua_call(L, 2, 1);
  return 1;
}

void lupb_register_type(lua_State *L, const char *name, const luaL_Reg *m,
                        const luaL_Reg *mm) {
  luaL_newmetatable(L, name);

  if (mm) {
    lupb_setfuncs(L, mm);
  }

  if (m) {
    lua_createtable(L, 0, 0);  /* __index table */
    lupb_setfuncs(L, m);

    /* Methods go in the mt's __index slot.  If the user also specified an
     * __index metamethod, use our custom lupb_indexmm() that can check both. */
    lua_getfield(L, -2, "__index");
    if (lua_isnil(L, -1)) {
      lua_pop(L, 1);
    } else {
      lua_pushcclosure(L, &lupb_indexmm, 2);
    }
    lua_setfield(L, -2, "__index");
  }

  lua_pop(L, 1);  /* The mt. */
}

/* Scalar type mapping ********************************************************/

/* Functions that convert scalar/primitive values (numbers, strings, bool)
 * between Lua and C/upb.  Handles type/range checking. */

bool lupb_checkbool(lua_State *L, int narg) {
  if (!lua_isboolean(L, narg)) {
    luaL_error(L, "must be true or false");
  }
  return lua_toboolean(L, narg);
}

/* Unlike luaL_checkstring(), this does not allow implicit conversion to
 * string. */
const char *lupb_checkstring(lua_State *L, int narg, size_t *len) {
  if (lua_type(L, narg) != LUA_TSTRING) {
    luaL_error(L, "Expected string");
  }

  return lua_tolstring(L, narg, len);
}

/* Unlike luaL_checkinteger, these do not implicitly convert from string or
 * 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) {                             \
    double n;                                                                  \
    ctype i;                                                                   \
    if (lua_isinteger(L, narg)) {                                              \
      return lua_tointeger(L, narg);                                           \
    }                                                                          \
                                                                               \
    /* Prevent implicit conversion from string. */                             \
    luaL_checktype(L, narg, LUA_TNUMBER);                                      \
    n = lua_tonumber(L, narg);                                                 \
                                                                               \
    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)

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() auto-converts. */
  return lua_tonumber(L, narg);
}

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() auto-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);
}

/* Library entry point ********************************************************/

int luaopen_lupb(lua_State *L) {
#if LUA_VERSION_NUM == 501
  const struct luaL_Reg funcs[] = {{NULL, NULL}};
  luaL_register(L, "upb_c", funcs);
#else
  lua_createtable(L, 0, 8);
#endif
  lupb_def_registertypes(L);
  lupb_msg_registertypes(L);
  return 1;  /* Return package table. */
}