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 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
|
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
/*
* 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 "lua/upb.h"
#include <float.h>
#include <math.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include "lauxlib.h"
#include "upb/message/message.h"
/* Lua compatibility code *****************************************************/
/* Shims for upcoming Lua 5.3 functionality. */
#if LUA_VERSION_NUM < 503
static bool lua_isinteger(lua_State* L, int argn) {
LUPB_UNUSED(L);
LUPB_UNUSED(argn);
return false;
}
#endif
/* Utility functions **********************************************************/
void lupb_checkstatus(lua_State* L, upb_Status* s) {
if (!upb_Status_IsOk(s)) {
lua_pushstring(L, upb_Status_ErrorMessage(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, min, max) \
ctype lupb_check##type(lua_State* L, int narg) { \
if (lua_isinteger(L, narg)) { \
lua_Integer i = lua_tointeger(L, narg); \
if (i < min || i > max) { \
luaL_error( \
L, "number %d was not an integer or out of range for " #type, i); \
} \
return i; \
} \
\
/* Prevent implicit conversion from string. */ \
luaL_checktype(L, narg, LUA_TNUMBER); \
double n = lua_tonumber(L, narg); \
\
/* Check this double has no fractional part and remains in bounds. \
* Consider INT64_MIN and INT64_MAX: \
* 1. INT64_MIN -(2^63) is a power of 2, so this converts to a double. \
* 2. INT64_MAX (2^63 - 1) is not a power of 2, and conversion of \
* out-of-range integer values to a double can lead to undefined behavior. \
* On some compilers, this conversion can return 0, but it also can return \
* the max value. To deal with this, we can first divide by 2 to prevent \
* the overflow, multiply it back, and add 1 to find the true limit. */ \
double i; \
double max_value = (((double)max / 2) * 2) + 1; \
if ((modf(n, &i) != 0.0) || n < min || n >= max_value) { \
luaL_error(L, "number %f was not an integer or out of range for " #type, \
n); \
} \
return (ctype)n; \
} \
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, INT64_MIN, INT64_MAX)
INTCHECK(int32, int32_t, INT32_MIN, INT32_MAX)
INTCHECK(uint64, uint64_t, 0, UINT64_MAX)
INTCHECK(uint32, uint32_t, 0, UINT32_MAX)
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. */
}
|