From 99ec824fcda8dc5ee43801973b120971b363c2c9 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Sun, 30 Jun 2024 17:09:18 -0500 Subject: [PATCH 1/5] AP_Scripting: keep enum definitions in flash Saves ~100B of statically allocated RAM. --- libraries/AP_Scripting/generator/src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/AP_Scripting/generator/src/main.c b/libraries/AP_Scripting/generator/src/main.c index b2f4e300103d4..f94dc7220d5a8 100644 --- a/libraries/AP_Scripting/generator/src/main.c +++ b/libraries/AP_Scripting/generator/src/main.c @@ -2381,7 +2381,7 @@ void emit_methods(struct userdata *node) { } void emit_enum(struct userdata * data) { - fprintf(source, "struct userdata_enum %s_enums[] = {\n", data->sanatized_name); + fprintf(source, "const struct userdata_enum %s_enums[] = {\n", data->sanatized_name); struct userdata_enum *ud_enum = data->enums; while (ud_enum != NULL) { fprintf(source, " {\"%s\", %s::%s},\n", ud_enum->name, data->name, ud_enum->name); From d99d34cd7d7ef2549098350710408a480b82db84 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Sat, 15 Jun 2024 15:29:12 -0500 Subject: [PATCH 2/5] AP_Scripting: don't put userdata and ap_objects into globals They are never accessed from globals. Only their metatables are accessed, using luaL_getmetatable. Saves ~2.9K of Lua heap. --- libraries/AP_Scripting/generator/src/main.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/libraries/AP_Scripting/generator/src/main.c b/libraries/AP_Scripting/generator/src/main.c index f94dc7220d5a8..f48f4d72dc28e 100644 --- a/libraries/AP_Scripting/generator/src/main.c +++ b/libraries/AP_Scripting/generator/src/main.c @@ -2504,46 +2504,31 @@ void emit_loaders(void) { fprintf(source, " // userdata metatables\n"); fprintf(source, " for (uint32_t i = 0; i < ARRAY_SIZE(userdata_fun); i++) {\n"); fprintf(source, " luaL_newmetatable(L, userdata_fun[i].name);\n"); - fprintf(source, " lua_pushcclosure(L, userdata_fun[i].func, 0);\n"); + fprintf(source, " lua_pushcfunction(L, userdata_fun[i].func);\n"); fprintf(source, " lua_setfield(L, -2, \"__index\");\n"); fprintf(source, " if (userdata_fun[i].operators != nullptr) {\n"); fprintf(source, " luaL_setfuncs(L, userdata_fun[i].operators, 0);\n"); fprintf(source, " }\n"); - fprintf(source, " lua_pushstring(L, \"__call\");\n"); - fprintf(source, " lua_pushvalue(L, -2);\n"); - fprintf(source, " lua_settable(L, -3);\n"); - fprintf(source, " lua_pop(L, 1);\n"); - fprintf(source, " lua_newuserdata(L, 0);\n"); - fprintf(source, " luaL_getmetatable(L, userdata_fun[i].name);\n"); - fprintf(source, " lua_setmetatable(L, -2);\n"); - fprintf(source, " lua_setglobal(L, userdata_fun[i].name);\n"); fprintf(source, " }\n"); fprintf(source, "\n"); fprintf(source, " // ap object metatables\n"); fprintf(source, " for (uint32_t i = 0; i < ARRAY_SIZE(ap_object_fun); i++) {\n"); fprintf(source, " luaL_newmetatable(L, ap_object_fun[i].name);\n"); - fprintf(source, " lua_pushcclosure(L, ap_object_fun[i].func, 0);\n"); + fprintf(source, " lua_pushcfunction(L, ap_object_fun[i].func);\n"); fprintf(source, " lua_setfield(L, -2, \"__index\");\n"); - fprintf(source, " lua_pushstring(L, \"__call\");\n"); - fprintf(source, " lua_pushvalue(L, -2);\n"); - fprintf(source, " lua_settable(L, -3);\n"); fprintf(source, " lua_pop(L, 1);\n"); - fprintf(source, " lua_newuserdata(L, 0);\n"); - fprintf(source, " luaL_getmetatable(L, ap_object_fun[i].name);\n"); - fprintf(source, " lua_setmetatable(L, -2);\n"); - fprintf(source, " lua_setglobal(L, ap_object_fun[i].name);\n"); fprintf(source, " }\n"); fprintf(source, "\n"); fprintf(source, " // singleton metatables\n"); fprintf(source, " for (uint32_t i = 0; i < ARRAY_SIZE(singleton_fun); i++) {\n"); fprintf(source, " luaL_newmetatable(L, singleton_fun[i].name);\n"); - fprintf(source, " lua_pushcclosure(L, singleton_fun[i].func, 0);\n"); + fprintf(source, " lua_pushcfunction(L, singleton_fun[i].func);\n"); fprintf(source, " lua_setfield(L, -2, \"__index\");\n"); fprintf(source, " lua_pushstring(L, \"__call\");\n"); fprintf(source, " lua_pushvalue(L, -2);\n"); From 61cb06a15dc11133889f13aae4f5207be0e11b62 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Fri, 28 Jun 2024 13:31:09 -0500 Subject: [PATCH 3/5] AP_Scripting: remove __call metamethod from singleton metatables The __call metamethod was set to the metatable itself. With __call not present, Lua will try to call the metatable (and fail), which is the same behavior as with the __call metamethod set to the metatable. Saves ~2K Lua heap. --- libraries/AP_Scripting/generator/src/main.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libraries/AP_Scripting/generator/src/main.c b/libraries/AP_Scripting/generator/src/main.c index f48f4d72dc28e..1ddefb351e3e7 100644 --- a/libraries/AP_Scripting/generator/src/main.c +++ b/libraries/AP_Scripting/generator/src/main.c @@ -2527,16 +2527,11 @@ void emit_loaders(void) { fprintf(source, " // singleton metatables\n"); fprintf(source, " for (uint32_t i = 0; i < ARRAY_SIZE(singleton_fun); i++) {\n"); + fprintf(source, " lua_newuserdata(L, 0);\n"); fprintf(source, " luaL_newmetatable(L, singleton_fun[i].name);\n"); fprintf(source, " lua_pushcfunction(L, singleton_fun[i].func);\n"); fprintf(source, " lua_setfield(L, -2, \"__index\");\n"); - fprintf(source, " lua_pushstring(L, \"__call\");\n"); - fprintf(source, " lua_pushvalue(L, -2);\n"); - fprintf(source, " lua_settable(L, -3);\n"); - fprintf(source, " lua_pop(L, 1);\n"); - fprintf(source, " lua_newuserdata(L, 0);\n"); - fprintf(source, " luaL_getmetatable(L, singleton_fun[i].name);\n"); fprintf(source, " lua_setmetatable(L, -2);\n"); fprintf(source, " lua_setglobal(L, singleton_fun[i].name);\n"); fprintf(source, " }\n"); From 2fcb2951991f594252a614d09a72cc8d7c6aff07 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Sat, 15 Jun 2024 15:51:21 -0500 Subject: [PATCH 4/5] AP_Scripting: place bindings into global table The global table is then used as the __index metamethod of each state's environment table. Avoids the overhead of loading binding objects into each state. The binding objects are immutable from Lua so sandboxing is not violated. Does have the slight downside that a script can no longer know all the binding names by enumerating _ENV. Saves ~700B of memory per loaded script. --- libraries/AP_Scripting/generator/src/main.c | 32 ++++++++++----------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/libraries/AP_Scripting/generator/src/main.c b/libraries/AP_Scripting/generator/src/main.c index 1ddefb351e3e7..ad817c0ac4585 100644 --- a/libraries/AP_Scripting/generator/src/main.c +++ b/libraries/AP_Scripting/generator/src/main.c @@ -2535,12 +2535,19 @@ void emit_loaders(void) { fprintf(source, " lua_setmetatable(L, -2);\n"); fprintf(source, " lua_setglobal(L, singleton_fun[i].name);\n"); fprintf(source, " }\n"); + fprintf(source, "\n"); + fprintf(source, " // userdata creation funcs\n"); + fprintf(source, " for (uint32_t i = 0; i < ARRAY_SIZE(new_userdata); i++) {\n"); + fprintf(source, " lua_pushcfunction(L, new_userdata[i].fun);\n"); + fprintf(source, " lua_setglobal(L, new_userdata[i].name);\n"); + fprintf(source, " }\n"); fprintf(source, "\n"); + fprintf(source, "}\n\n"); } -void emit_sandbox(void) { +void emit_userdata_new_funcs(void) { struct userdata *data = parsed_userdata; fprintf(source, "const struct userdata {\n"); fprintf(source, " const char *name;\n"); @@ -2589,23 +2596,14 @@ void emit_sandbox(void) { } } fprintf(source, "};\n\n"); +} +void emit_sandbox(void) { fprintf(source, "void load_generated_sandbox(lua_State *L) {\n"); - // load the singletons - fprintf(source, " for (uint32_t i = 0; i < ARRAY_SIZE(singleton_fun); i++) {\n"); - fprintf(source, " lua_pushstring(L, singleton_fun[i].name);\n"); - fprintf(source, " lua_getglobal(L, singleton_fun[i].name);\n"); - fprintf(source, " lua_settable(L, -3);\n"); - fprintf(source, " }\n"); - - // load the userdata allactors and globals - fprintf(source, " for (uint32_t i = 0; i < ARRAY_SIZE(new_userdata); i++) {\n"); - fprintf(source, " lua_pushstring(L, new_userdata[i].name);\n"); - fprintf(source, " lua_pushcfunction(L, new_userdata[i].fun);\n"); - fprintf(source, " lua_settable(L, -3);\n"); - fprintf(source, " }\n"); - - fprintf(source, "\n"); + fprintf(source, " lua_createtable(L, 0, 1);\n"); + fprintf(source, " lua_pushglobaltable(L);\n"); + fprintf(source, " lua_setfield(L, -2, \"__index\");\n"); + fprintf(source, " lua_setmetatable(L, -2);\n"); fprintf(source, "}\n"); } @@ -3149,7 +3147,7 @@ int main(int argc, char **argv) { emit_methods(parsed_ap_objects); emit_index(parsed_ap_objects); - + emit_userdata_new_funcs(); emit_loaders(); emit_sandbox(); From b414c6b55b0deeba12e35d8ce90ae84088089dd9 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Fri, 28 Jun 2024 15:16:44 -0500 Subject: [PATCH 5/5] AP_Scripting: dynamically load some binding objects Only create the binding object (singleton metatable/userdata or C function reference) once the user first references a particular singleton or userdata creation function. Once created, the object is stored into the script's environment so it doesn't get recreated on the next reference and there isn't any further overhead. The userdatas are no longer shared between scripts which imposes a slight memory penalty for multiple scripts using the same singleton but this avoids an additional lookup time cost. Userdata and ap_objects aren't eligible for this optimization as the C++ code might want a particular metatable at any time. Saves ~9.3K Lua heap. --- libraries/AP_Scripting/generator/src/main.c | 58 ++++++++++++++------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/libraries/AP_Scripting/generator/src/main.c b/libraries/AP_Scripting/generator/src/main.c index ad817c0ac4585..d9c530978328c 100644 --- a/libraries/AP_Scripting/generator/src/main.c +++ b/libraries/AP_Scripting/generator/src/main.c @@ -2499,6 +2499,43 @@ void emit_loaders(void) { emit_type_index(parsed_singletons, "singleton"); emit_type_index(parsed_ap_objects, "ap_object"); + fprintf(source, "static int binding_index(lua_State *L) {\n"); + fprintf(source, " const char * name = luaL_checkstring(L, 2);\n"); + fprintf(source, "\n"); + fprintf(source, " bool found = false;\n"); + fprintf(source, " for (uint32_t i = 0; i < ARRAY_SIZE(singleton_fun); i++) {\n"); + fprintf(source, " if (strcmp(name, singleton_fun[i].name) == 0) {\n"); + fprintf(source, " lua_newuserdata(L, 0);\n"); + fprintf(source, " if (luaL_newmetatable(L, name)) { // need to create metatable\n"); + fprintf(source, " lua_pushcfunction(L, singleton_fun[i].func);\n"); + fprintf(source, " lua_setfield(L, -2, \"__index\");\n"); + fprintf(source, " }\n"); + fprintf(source, " lua_setmetatable(L, -2);\n"); + fprintf(source, " found = true;\n"); + fprintf(source, " break;\n"); + fprintf(source, " }\n"); + fprintf(source, " }\n"); + fprintf(source, " if (!found) {\n"); + fprintf(source, " for (uint32_t i = 0; i < ARRAY_SIZE(new_userdata); i++) {\n"); + fprintf(source, " if (strcmp(name, new_userdata[i].name) == 0) {\n"); + fprintf(source, " lua_pushcfunction(L, new_userdata[i].fun);\n"); + fprintf(source, " found = true;\n"); + fprintf(source, " break;\n"); + fprintf(source, " }\n"); + fprintf(source, " }\n"); + fprintf(source, " }\n"); + fprintf(source, " if (!found) {\n"); + fprintf(source, " return 0;\n"); + fprintf(source, " }\n"); + fprintf(source, "\n"); + fprintf(source, " // store found value to avoid a re-index\n"); + fprintf(source, " lua_pushvalue(L, -2);\n"); + fprintf(source, " lua_pushvalue(L, -2);\n"); + fprintf(source, " lua_settable(L, -5);\n"); + fprintf(source, "\n"); + fprintf(source, " return 1;\n"); + fprintf(source, "}\n\n"); + fprintf(source, "void load_generated_bindings(lua_State *L) {\n"); fprintf(source, " luaL_checkstack(L, 5, \"Out of stack\");\n"); // this is more stack space then we need, but should never fail fprintf(source, " // userdata metatables\n"); @@ -2525,24 +2562,7 @@ void emit_loaders(void) { fprintf(source, " }\n"); fprintf(source, "\n"); - fprintf(source, " // singleton metatables\n"); - fprintf(source, " for (uint32_t i = 0; i < ARRAY_SIZE(singleton_fun); i++) {\n"); - fprintf(source, " lua_newuserdata(L, 0);\n"); - fprintf(source, " luaL_newmetatable(L, singleton_fun[i].name);\n"); - fprintf(source, " lua_pushcfunction(L, singleton_fun[i].func);\n"); - fprintf(source, " lua_setfield(L, -2, \"__index\");\n"); - - fprintf(source, " lua_setmetatable(L, -2);\n"); - fprintf(source, " lua_setglobal(L, singleton_fun[i].name);\n"); - fprintf(source, " }\n"); - fprintf(source, "\n"); - - fprintf(source, " // userdata creation funcs\n"); - fprintf(source, " for (uint32_t i = 0; i < ARRAY_SIZE(new_userdata); i++) {\n"); - fprintf(source, " lua_pushcfunction(L, new_userdata[i].fun);\n"); - fprintf(source, " lua_setglobal(L, new_userdata[i].name);\n"); - fprintf(source, " }\n"); - fprintf(source, "\n"); + fprintf(source, " // singletons and userdata creation funcs are loaded dynamically\n"); fprintf(source, "}\n\n"); } @@ -2601,7 +2621,7 @@ void emit_userdata_new_funcs(void) { void emit_sandbox(void) { fprintf(source, "void load_generated_sandbox(lua_State *L) {\n"); fprintf(source, " lua_createtable(L, 0, 1);\n"); - fprintf(source, " lua_pushglobaltable(L);\n"); + fprintf(source, " lua_pushcfunction(L, binding_index);\n"); fprintf(source, " lua_setfield(L, -2, \"__index\");\n"); fprintf(source, " lua_setmetatable(L, -2);\n"); fprintf(source, "}\n");