Skip to content

Commit 6000723

Browse files
authored
Merge pull request #822 from o-sdn-o/gui-bridge
Make Lua scripts precompiled.
2 parents 901bb9c + 4cb31a3 commit 6000723

File tree

4 files changed

+139
-32
lines changed

4 files changed

+139
-32
lines changed

src/netxs/desktopio/application.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ namespace netxs::app
2222

2323
namespace netxs::app::shared
2424
{
25-
static const auto version = "v2025.10.03";
25+
static const auto version = "v2025.10.11";
2626
static const auto repository = "https://github.com/directvt/vtm";
2727
static const auto usr_config = "~/.config/vtm/settings.xml"s;
2828
static const auto sys_config = "/etc/vtm/settings.xml"s;

src/netxs/desktopio/controls.hpp

Lines changed: 113 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ namespace netxs::events
311311
}
312312
void luna::read_args(si32 index, auto add_item)
313313
{
314-
if (lua_istable(lua, index))
314+
if (::lua_type(lua, index) == LUA_TTABLE)
315315
{
316316
::lua_pushnil(lua); // Push prev key.
317317
while (::lua_next(lua, index)) // Table is in the stack at index. { "<item " + text{ table } + " />" }
@@ -320,7 +320,7 @@ namespace netxs::events
320320
if (!key.empty()) // Allow stringable keys only.
321321
{
322322
auto val = luna::vtmlua_torawstring(lua, -1);
323-
if (val.empty() && lua_istable(lua, -1)) // Extract item list.
323+
if (val.empty() && ::lua_type(lua, -1) == LUA_TTABLE) // Extract item list.
324324
{
325325
::lua_pushnil(lua); // Push prev key.
326326
while (::lua_next(lua, -2)) // Table is in the stack at index -2. { "<key="key2=val2"/>" }
@@ -414,22 +414,35 @@ namespace netxs::events
414414
indexer.context_ref = context;
415415
indexer.script_param = std::ref((T&)param);
416416

417-
::lua_settop(lua, 0);
418-
auto error = ::luaL_loadbuffer(lua, script_body.data(), script_body.size(), "script body")
419-
|| ::lua_pcall(lua, 0, 0, 0);
417+
auto error = faux;
418+
auto [ok, lua_fx_id] = push_function_id(script_body);
419+
if (ok)
420+
{
421+
if (::lua_rawget(lua, -2) == LUA_TFUNCTION) // It is precompiled.
422+
{
423+
//if constexpr (debugmode) log("It is precompiled");
424+
error = ::lua_pcall(lua, 0, 0, 0);
425+
}
426+
else // It is not precompiled.
427+
{
428+
//if constexpr (debugmode) log("It is not precompiled");
429+
error = ::luaL_loadbuffer(lua, script_body.data(), script_body.size(), "script body")
430+
|| ::lua_pcall(lua, 0, 0, 0);
431+
}
432+
}
420433
indexer.script_param.reset();
421434
auto result = text{};
422435
if (error)
423436
{
424437
result = ::lua_tostring(lua, -1);
425438
log("%%%msg%", prompt::lua, ansi::err(result));
426-
::lua_pop(lua, 1); // Pop error message from stack.
439+
//::lua_pop(lua, 1); // Pop error message from stack.
427440
}
428441
else if (::lua_gettop(lua))
429442
{
430443
result = luna::vtmlua_torawstring(lua, -1);
431-
::lua_settop(lua, 0);
432444
}
445+
::lua_settop(lua, 0);
433446
return result;
434447
}
435448
text luna::run_script(ui::base& boss, view script_body)
@@ -456,6 +469,79 @@ namespace netxs::events
456469
log(ansi::clr(yellowlt, shadow), "\n", prompt::lua, result);
457470
script.cmd = utf::concat(shadow, "\n", prompt::lua, result);
458471
}
472+
std::pair<bool, view> luna::push_function_id(view script_body)
473+
{
474+
::lua_settop(lua, 0);
475+
// Get a table of precompiled functions from the registry.
476+
::lua_pushstring(lua, "precompiled"); // Push internal registry key 'precompiled'.
477+
if (::lua_gettable(lua, LUA_REGISTRYINDEX) == LUA_TTABLE) // Retrieve address of 'precompiled' and push it to the stack at -1.
478+
{
479+
auto script_id = script_body.data();
480+
auto memory_id = reinterpret_cast<char const*>(&script_id);
481+
auto lua_fx_id = view{ memory_id, sizeof(script_id) };
482+
//if constexpr (debugmode) log("Function id='%%'", utf::debase437(lua_fx_id));
483+
::lua_pushlstring(lua, lua_fx_id.data(), lua_fx_id.size());
484+
return std::pair{ true, lua_fx_id };
485+
}
486+
else
487+
{
488+
log("%%The table of precompiled functions is missing", prompt::lua);
489+
::lua_settop(lua, 0);
490+
return std::pair{ faux, view{} };
491+
}
492+
}
493+
bool luna::precompile_function(sptr<text>& script_body_ptr)
494+
{
495+
auto precompiled = faux;
496+
if (script_body_ptr && script_body_ptr->size())
497+
{
498+
auto& script_body = *script_body_ptr;
499+
auto [ok, lua_fx_id] = push_function_id(script_body);
500+
if (ok)
501+
{
502+
if (::lua_rawget(lua, -2) == LUA_TFUNCTION) // It is already precompiled.
503+
{
504+
//if constexpr (debugmode) log("The script is already precompiled");
505+
precompiled = true;
506+
}
507+
else // It is not precompiled yet.
508+
{
509+
::lua_pop(lua, 1); // Pop nil after the ::lua_rawget() call.
510+
::lua_pushlstring(lua, lua_fx_id.data(), lua_fx_id.size()); // Push fx id again.
511+
auto error = ::luaL_loadbuffer(lua, script_body.data(), script_body.size(), "precompilation");
512+
if (error)
513+
{
514+
auto result = ::lua_tostring(lua, -1);
515+
log("%%Precompilation error: %msg%", prompt::lua, ansi::err(result));
516+
::lua_pop(lua, 1); // Pop error message from stack.
517+
}
518+
else
519+
{
520+
//if constexpr (debugmode) log("Script precompilation is done");
521+
::lua_rawset(lua, -3);
522+
precompiled = true;
523+
}
524+
}
525+
::lua_settop(lua, 0);
526+
}
527+
}
528+
return precompiled;
529+
}
530+
void luna::remove_function(sptr<text>& script_body_ptr)
531+
{
532+
if (script_body_ptr && script_body_ptr->size() && script_body_ptr.use_count() == 1)
533+
{
534+
auto& script_body = *script_body_ptr;
535+
auto [ok, lua_fx_id] = push_function_id(script_body);
536+
if (ok)
537+
{
538+
::lua_pushnil(lua);
539+
::lua_rawset(lua, -3);
540+
::lua_settop(lua, 0);
541+
//if constexpr (debugmode) log("Function id='%%' removed", utf::debase437(lua_fx_id));
542+
}
543+
}
544+
}
459545

460546
luna::luna(auth& indexer)
461547
: indexer{ indexer },
@@ -472,14 +558,19 @@ namespace netxs::events
472558
::lua_pushlightuserdata(lua, &indexer); // Push the 'indexer' address as a record value.
473559
::lua_settable(lua, LUA_REGISTRYINDEX); // Set internal registry['indexer'] = &indexer.
474560

561+
// Set 'precompiled' internal object.
562+
::lua_pushstring(lua, "precompiled"); // Push internal registry key 'precompiled' name.
563+
::lua_createtable(lua, 0, 100); // 100 is a hint for the number of non-sequential (associative array-like) elements the table will have.
564+
::lua_settable(lua, LUA_REGISTRYINDEX); // Set internal registry['precompiled'] = <precompiled function table>.
565+
475566
// Define 'vtm' redirecting metatable.
476567
static auto vtm_metaindex = std::to_array<luaL_Reg>({{ "__index", luna::vtmlua_vtm_index },
477568
{ "__tostring", luna::vtmlua_object2string },
478569
{ "__call", luna::vtmlua_vtm_call },
479570
{ nullptr, nullptr }});
480571
::luaL_newmetatable(lua, "vtm_metaindex"); // Create a new metatable in registry and push it to the stack.
481572
::luaL_setfuncs(lua, vtm_metaindex.data(), 0); // Assign metamethods for the table which at the top of the stack.
482-
::lua_newtable(lua); // Create and push new "vtm.*" global table.
573+
::lua_createtable(lua, 0, 0); // Create and push new "vtm.*" global table.
483574
::luaL_setmetatable(lua, "vtm_metaindex"); // Set the metatable for table at -1.
484575
::lua_setglobal(lua, basename::vtm.data()); // Set global var "vtm". Pop "vtm".
485576

@@ -500,6 +591,20 @@ namespace netxs::events
500591
if (lua) ::lua_close(lua);
501592
}
502593

594+
script_ref::script_ref(auth& indexer, context_t& context, sptr<text> script_body_ptr)
595+
: indexer{ indexer },
596+
context{ context },
597+
script_body_ptr{ script_body_ptr },
598+
precompiled{ indexer.luafx.precompile_function(script_body_ptr) }
599+
{ }
600+
script_ref::~script_ref()
601+
{
602+
if (precompiled)
603+
{
604+
indexer.luafx.remove_function(script_body_ptr);
605+
}
606+
}
607+
503608
auth::auth(bool use_timer)
504609
: next_id{ 0 },
505610
context_ref{ context },

src/netxs/desktopio/events.hpp

Lines changed: 24 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,17 @@ namespace netxs::events
3232
{
3333
// Forward execution order: Execute concrete event first. Preserve subscription order. Forward means from particular to general: 1. event::group::item, 2. event::group::any
3434
// Reverse execution order: Execute global events first. Preserve subscription order. Reverse means from general to particular: 1. event::group::any, 2. event::group::item
35-
static constexpr auto counter = __COUNTER__ + 1;
36-
static constexpr auto release = __COUNTER__ - counter; // events: Run forwrad handlers with fixed param.
37-
static constexpr auto preview = __COUNTER__ - counter; // events: Run reverse handlers with fixed a param intended to change.
38-
static constexpr auto request = __COUNTER__ - counter; // events: Run forwrad a handler that provides the current value of the param. To avoid being overridden, the handler should be the only one.
39-
static constexpr auto anycast = __COUNTER__ - counter; // events: Run reverse handlers along the entire visual tree.
40-
static constexpr auto general = __COUNTER__ - counter; // events: Run forwrad handlers for all objects.
41-
static constexpr auto mousepreview = __COUNTER__ - counter; // events: Run in subscription order for all objects.
42-
static constexpr auto mouserelease = __COUNTER__ - counter; // events: Run in subscription order for all objects.
43-
static constexpr auto keybdpreview = __COUNTER__ - counter; // events: Run in subscription order for all objects.
44-
static constexpr auto keybdrelease = __COUNTER__ - counter; // events: Run in subscription order for all objects.
45-
static constexpr auto unknown = __COUNTER__ - counter; // events: .
35+
static constexpr auto counter = __COUNTER__ + 1;
36+
static constexpr auto release = __COUNTER__ - counter; // events: Run forwrad handlers with fixed param.
37+
static constexpr auto preview = __COUNTER__ - counter; // events: Run reverse handlers with fixed a param intended to change.
38+
static constexpr auto request = __COUNTER__ - counter; // events: Run forwrad a handler that provides the current value of the param. To avoid being overridden, the handler should be the only one.
39+
static constexpr auto anycast = __COUNTER__ - counter; // events: Run reverse handlers along the entire visual tree.
40+
static constexpr auto general = __COUNTER__ - counter; // events: Run forwrad handlers for all objects.
41+
static constexpr auto mousepreview = __COUNTER__ - counter; // events: Run in subscription order for object tree.
42+
static constexpr auto mouserelease = __COUNTER__ - counter; // events: Run in subscription order for object tree.
43+
static constexpr auto keybdpreview = __COUNTER__ - counter; // events: Run in subscription order for focused objects.
44+
static constexpr auto keybdrelease = __COUNTER__ - counter; // events: Run in subscription order for focused objects.
45+
static constexpr auto unknown = __COUNTER__ - counter; // events: .
4646
static constexpr auto str = std::to_array({ "release"sv,
4747
"preview"sv,
4848
"request"sv,
@@ -169,25 +169,28 @@ namespace netxs::events
169169
text run(context_t& context, view script_body, Arg&& param = {});
170170
text run_script(ui::base& object, view script_body);
171171
void run_ext_script(ui::base& object, auto& script);
172+
std::pair<bool, view> push_function_id(view script_body);
173+
bool precompile_function(sptr<text>& script_body_ptr);
174+
void remove_function(sptr<text>& script_body_ptr);
172175

173176
luna(auth& indexer);
174177
~luna();
175178
};
176179

177180
struct script_ref
178181
{
179-
std::reference_wrapper<context_t> context; // Hierarchical location index of the script owner.
180-
sptr<text> script_body_ptr; // Script body sptr.
182+
auth& indexer; // script_ref: Global object indexer.
183+
std::reference_wrapper<context_t> context; // script_ref: Hierarchical location index of the script owner.
184+
sptr<text> script_body_ptr; // script_ref: Script body sptr.
185+
bool precompiled{}; // script_ref: .
181186

182187
static text to_string(context_t& context);
183188

184-
script_ref(context_t& context, sptr<text> script_body_ptr)
185-
: context{ context },
186-
script_body_ptr{ script_body_ptr }
187-
{ }
188-
script_ref(context_t& context, auto&& script_body_ptr)
189-
: script_ref{ context, ptr::shared<text>(script_body_ptr) }
189+
script_ref(auth& indexer, context_t& context, sptr<text> script_body_ptr);
190+
script_ref(auth& indexer, context_t& context, auto&& script_body_ptr)
191+
: script_ref{ indexer, context, ptr::shared<text>(script_body_ptr) }
190192
{ }
193+
~script_ref();
191194
};
192195

193196
template<class Arg>
@@ -224,8 +227,8 @@ namespace netxs::events
224227
{
225228
if (script_ptr && script_ptr->script_body_ptr)
226229
{
227-
auto& [context, script_body_ptr] = *script_ptr;
228-
auto& script_body = *script_body_ptr;
230+
auto& context = script_ptr->context;
231+
auto& script_body = *(script_ptr->script_body_ptr);
229232
luafx.run(context, script_body, param);
230233
}
231234
else if (auto& proc = get_inst<Arg>())
@@ -888,7 +891,6 @@ namespace netxs
888891
using netxs::events::tier;
889892
using netxs::events::hook;
890893
using netxs::events::wook;
891-
//using netxs::events::sref;
892894
using netxs::events::script_ref;
893895
}
894896
namespace std

src/netxs/desktopio/input.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2202,7 +2202,7 @@ namespace netxs::input
22022202
auto [chords, is_preview] = input::bindings::get_chords(chord_str);
22032203
if (chords.size())
22042204
{
2205-
auto script_ptr = ptr::shared<script_ref>(boss.scripting_context, script_body);
2205+
auto script_ptr = ptr::shared<script_ref>(boss.indexer, boss.scripting_context, script_body);
22062206
auto reset_handler = !(script_ptr->script_body_ptr && script_ptr->script_body_ptr->size());
22072207
for (auto& binary_chord : chords) if (binary_chord.size()) // Scripts always store their sensors at the boss side, since the lifetime of base::scripting_context depends on the boss.
22082208
{

0 commit comments

Comments
 (0)