From 4f13c81e18f40d9fa882981265a80eb75012b868 Mon Sep 17 00:00:00 2001 From: Thomas Watson Date: Fri, 28 Jun 2024 15:16:44 -0500 Subject: [PATCH] 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 | 64 +++++++++++++++------ 1 file changed, 45 insertions(+), 19 deletions(-) diff --git a/libraries/AP_Scripting/generator/src/main.c b/libraries/AP_Scripting/generator/src/main.c index ad817c0ac4585f..d334d6414c46cb 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,13 @@ 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, " lua_pushglobaltable(L);\n"); + fprintf(source, " lua_createtable(L, 0, 1);\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, " lua_pop(L, 1);\n"); fprintf(source, "}\n\n"); } @@ -2601,7 +2627,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");