diff --git a/libraries/AP_Scripting/generator/src/main.c b/libraries/AP_Scripting/generator/src/main.c index ee3a69d5f0..531bbf6354 100644 --- a/libraries/AP_Scripting/generator/src/main.c +++ b/libraries/AP_Scripting/generator/src/main.c @@ -717,7 +717,8 @@ void emit_userdata_allocators(void) { while (node) { fprintf(source, "int new_%s(lua_State *L) {\n", node->name); fprintf(source, " luaL_checkstack(L, 2, \"Out of stack\");\n"); // ensure we have sufficent stack to push the return - fprintf(source, " %s *ud = (%s *)lua_newuserdata(L, sizeof(%s));\n", node->name, node->name, node->name); + fprintf(source, " void *ud = lua_newuserdata(L, sizeof(%s));\n", node->name); + fprintf(source, " memset(ud, 0, sizeof(%s));\n", node->name); fprintf(source, " new (ud) %s();\n", node->name); fprintf(source, " luaL_getmetatable(L, \"%s\");\n", node->name); fprintf(source, " lua_setmetatable(L, -2);\n"); @@ -1323,6 +1324,9 @@ void emit_loaders(void) { fprintf(source, " lua_setglobal(L, singleton_fun[i].name);\n"); fprintf(source, " }\n"); + fprintf(source, "\n"); + fprintf(source, " load_boxed_numerics(L);\n"); + fprintf(source, "}\n\n"); } @@ -1361,6 +1365,9 @@ void emit_sandbox(void) { fprintf(source, " lua_settable(L, -3);\n"); fprintf(source, " }\n"); + fprintf(source, "\n"); + fprintf(source, " load_boxed_numerics_sandbox(L);\n"); + // load the userdata complex functions fprintf(source, "}\n"); } @@ -1429,6 +1436,7 @@ int main(int argc, char **argv) { sanity_check_userdata(); fprintf(source, "#include \"lua_generated_bindings.h\"\n"); + fprintf(source, "#include \"lua_boxed_numerics.h\"\n"); trace(TRACE_GENERAL, "Starting emission"); diff --git a/libraries/AP_Scripting/lua_bindings.cpp b/libraries/AP_Scripting/lua_bindings.cpp index f51c3b79ec..ea9af4fe45 100644 --- a/libraries/AP_Scripting/lua_bindings.cpp +++ b/libraries/AP_Scripting/lua_bindings.cpp @@ -28,7 +28,7 @@ int lua_gcs_send_text(lua_State *L); int lua_gcs_send_text(lua_State *L) { check_arguments(L, 1, "send_text"); - const char* str = lua_tostring(L, -1); + const char* str = luaL_checkstring(L, -1); gcs().send_text(MAV_SEVERITY_INFO, str); return 0; diff --git a/libraries/AP_Scripting/lua_boxed_numerics.cpp b/libraries/AP_Scripting/lua_boxed_numerics.cpp new file mode 100644 index 0000000000..1e7787bf1a --- /dev/null +++ b/libraries/AP_Scripting/lua_boxed_numerics.cpp @@ -0,0 +1,192 @@ +#include +#include "lua_boxed_numerics.h" + +extern const AP_HAL::HAL& hal; + +int new_uint32_t(lua_State *L) { + luaL_checkstack(L, 2, "Out of stack"); + *static_cast(lua_newuserdata(L, sizeof(uint32_t))) = 0; // allocated memory is already zerod, no need to manipulate this + luaL_getmetatable(L, "uint32_t"); + lua_setmetatable(L, -2); + return 1; +} + +uint32_t coerce_to_uint32_t(lua_State *L, int arg) { + { // userdata + const uint32_t * ud = static_cast(luaL_testudata(L, arg, "uint32_t")); + if (ud != nullptr) { + return *ud; + } + } + { // integer + + // if this assert fails, you will need to add an upper bounds + // check that ensures the value isn't greater then UINT32_MAX + static_assert(sizeof(lua_Number) == sizeof(uint32_t), "32 bit integers are only supported"); + + int success; + const lua_Integer v = lua_tointegerx(L, arg, &success); + if (success && v >= 0) { + return static_cast(v); + } + } + { // float + int success; + const lua_Number v = lua_tonumberx(L, arg, &success); + if (success && v >= 0 && v <= UINT32_MAX) { + return static_cast(v); + } + } + // failure + return luaL_argerror(L, arg, "Unable to coerce to uint32_t"); +} + +#define UINT32_T_BOX_OP(name, sym) \ + static int uint32_t___##name(lua_State *L) { \ + const int args = lua_gettop(L); \ + if (args > 2) { \ + return luaL_argerror(L, args, "too many arguments"); \ + } else if (args < 2) { \ + return luaL_argerror(L, args, "too few arguments"); \ + } \ + \ + uint32_t v1 = coerce_to_uint32_t(L, 1); \ + uint32_t v2 = coerce_to_uint32_t(L, 2); \ + \ + new_uint32_t(L); \ + *static_cast(luaL_checkudata(L, -1, "uint32_t")) = v1 sym v2; \ + return 1; \ + } + +UINT32_T_BOX_OP(add, +) +UINT32_T_BOX_OP(sub, -) +UINT32_T_BOX_OP(mul, *) +UINT32_T_BOX_OP(div, /) +UINT32_T_BOX_OP(mod, %) +UINT32_T_BOX_OP(idiv, /) +UINT32_T_BOX_OP(band, &) +UINT32_T_BOX_OP(bor, |) +UINT32_T_BOX_OP(bxor, ^) +UINT32_T_BOX_OP(shl, <<) +UINT32_T_BOX_OP(shr, >>) + +#define UINT32_T_BOX_OP_BOOL(name, sym) \ + static int uint32_t___##name(lua_State *L) { \ + const int args = lua_gettop(L); \ + luaL_checkstack(L, 1, "Out of stack"); \ + if (args > 2) { \ + return luaL_argerror(L, args, "too many arguments"); \ + } else if (args < 2) { \ + return luaL_argerror(L, args, "too few arguments"); \ + } \ + \ + uint32_t v1 = coerce_to_uint32_t(L, 1); \ + uint32_t v2 = coerce_to_uint32_t(L, 2); \ + \ + lua_pushboolean(L, v1 sym v2); \ + return 1; \ + } + +UINT32_T_BOX_OP_BOOL(eq, =) +UINT32_T_BOX_OP_BOOL(lt, <) +UINT32_T_BOX_OP_BOOL(le, <=) + +#define UINT32_T_BOX_OP_UNARY(name, sym) \ + static int uint32_t___##name(lua_State *L) { \ + const int args = lua_gettop(L); \ + luaL_checkstack(L, 1, "Out of stack"); \ + if (args != 1) { \ + return luaL_argerror(L, args, "Expected 1 argument"); \ + } \ + \ + uint32_t v1 = coerce_to_uint32_t(L, 1); \ + \ + new_uint32_t(L); \ + *static_cast(luaL_checkudata(L, -1, "uint32_t")) = sym v1; \ + return 1; \ + } + +// DO NOT SUPPORT UNARY NEGATION +UINT32_T_BOX_OP_UNARY(bnot, ~) + +static int uint32_t_toint(lua_State *L) { + const int args = lua_gettop(L); + if (args != 1) { + return luaL_argerror(L, args, "Expected 1 argument"); + } + + uint32_t v = *static_cast(luaL_checkudata(L, 1, "uint32_t")); + + lua_pushinteger(L, static_cast(v)); + + return 1; +} + +static int uint32_t_tofloat(lua_State *L) { + const int args = lua_gettop(L); + if (args != 1) { + return luaL_argerror(L, args, "Expected 1 argument"); + } + + uint32_t v = *static_cast(luaL_checkudata(L, 1, "uint32_t")); + + lua_pushnumber(L, static_cast(v)); + + return 1; +} + +static int uint32_t___tostring(lua_State *L) { + const int args = lua_gettop(L); + if (args != 1) { + return luaL_argerror(L, args, "Expected 1 argument"); + } + + uint32_t v = *static_cast(luaL_checkudata(L, 1, "uint32_t")); + + char buf[32]; + hal.util->snprintf(buf, ARRAY_SIZE(buf), "%u", (unsigned)v); + + lua_pushstring(L, buf); + + return 1; +} + +const luaL_Reg uint32_t_meta[] = { + {"__add", uint32_t___add}, + {"__sub", uint32_t___sub}, + {"__mul", uint32_t___mul}, + {"__div", uint32_t___div}, + {"__mod", uint32_t___mod}, + {"__idiv", uint32_t___idiv}, + {"__band", uint32_t___band}, + {"__bor", uint32_t___bor}, + {"__bxor", uint32_t___bxor}, + {"__shl", uint32_t___shl}, + {"__shr", uint32_t___shr}, + {"__shr", uint32_t___shr}, + {"__eq", uint32_t___eq}, + {"__lt", uint32_t___lt}, + {"__le", uint32_t___le}, + {"__bnot", uint32_t___bnot}, + {"__tostring", uint32_t___tostring}, + {"toint", uint32_t_toint}, + {"tofloat", uint32_t_tofloat}, + {NULL, NULL} +}; + +void load_boxed_numerics(lua_State *L) { + luaL_checkstack(L, 5, "Out of stack"); + luaL_newmetatable(L, "uint32_t"); + luaL_setfuncs(L, uint32_t_meta, 0); + lua_pushstring(L, "__index"); + lua_pushvalue(L, -2); + lua_settable(L, -3); + lua_pop(L, 1); +} + +void load_boxed_numerics_sandbox(lua_State *L) { + // if there are ever more drivers then move to a table based solution + lua_pushstring(L, "uint32_t"); + lua_pushcfunction(L, new_uint32_t); + lua_settable(L, -3); +} diff --git a/libraries/AP_Scripting/lua_boxed_numerics.h b/libraries/AP_Scripting/lua_boxed_numerics.h new file mode 100644 index 0000000000..18d97cde71 --- /dev/null +++ b/libraries/AP_Scripting/lua_boxed_numerics.h @@ -0,0 +1,6 @@ +#pragma once + +#include "lua/src/lua.hpp" + +void load_boxed_numerics(lua_State *L); +void load_boxed_numerics_sandbox(lua_State *L); diff --git a/libraries/AP_Scripting/lua_generated_bindings.cpp b/libraries/AP_Scripting/lua_generated_bindings.cpp index 68a5b825cb..1672b84424 100644 --- a/libraries/AP_Scripting/lua_generated_bindings.cpp +++ b/libraries/AP_Scripting/lua_generated_bindings.cpp @@ -1,5 +1,6 @@ // auto generated bindings, don't manually edit #include "lua_generated_bindings.h" +#include "lua_boxed_numerics.h" #include #include #include @@ -12,7 +13,8 @@ int new_Vector2f(lua_State *L) { luaL_checkstack(L, 2, "Out of stack"); - Vector2f *ud = (Vector2f *)lua_newuserdata(L, sizeof(Vector2f)); + void *ud = lua_newuserdata(L, sizeof(Vector2f)); + memset(ud, 0, sizeof(Vector2f)); new (ud) Vector2f(); luaL_getmetatable(L, "Vector2f"); lua_setmetatable(L, -2); @@ -21,7 +23,8 @@ int new_Vector2f(lua_State *L) { int new_Vector3f(lua_State *L) { luaL_checkstack(L, 2, "Out of stack"); - Vector3f *ud = (Vector3f *)lua_newuserdata(L, sizeof(Vector3f)); + void *ud = lua_newuserdata(L, sizeof(Vector3f)); + memset(ud, 0, sizeof(Vector3f)); new (ud) Vector3f(); luaL_getmetatable(L, "Vector3f"); lua_setmetatable(L, -2); @@ -30,7 +33,8 @@ int new_Vector3f(lua_State *L) { int new_Location(lua_State *L) { luaL_checkstack(L, 2, "Out of stack"); - Location *ud = (Location *)lua_newuserdata(L, sizeof(Location)); + void *ud = lua_newuserdata(L, sizeof(Location)); + memset(ud, 0, sizeof(Location)); new (ud) Location(); luaL_getmetatable(L, "Location"); lua_setmetatable(L, -2); @@ -1792,6 +1796,8 @@ void load_generated_bindings(lua_State *L) { lua_setmetatable(L, -2); lua_setglobal(L, singleton_fun[i].name); } + + load_boxed_numerics(L); } const char *singletons[] = { @@ -1824,4 +1830,6 @@ void load_generated_sandbox(lua_State *L) { lua_pushcfunction(L, new_userdata[i].fun); lua_settable(L, -3); } + + load_boxed_numerics_sandbox(L); }