diff --git a/pom.xml b/pom.xml index 94a5100..3326057 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.openautonomousconnection LuaScript - 1.0.0-BETA.1.3 + 0.0.0-STABLE.1.3 Open Autonomous Connection @@ -65,6 +65,10 @@ + + unlegitdqrk + https://repo.unlegitdqrk.dev/api/packages/UnlegitDqrk/maven + oac https://repo.open-autonomous-connection.org/api/packages/open-autonomous-connection/maven @@ -72,10 +76,6 @@ true - - unlegitdqrk - https://repo.unlegitdqrk.dev/api/packages/UnlegitDqrk/maven - diff --git a/src/main/java/org/openautonomousconnection/luascript/fx/FxEventHost.java b/src/main/java/org/openautonomousconnection/luascript/fx/FxEventHost.java index 5dbf39f..a9cc08b 100644 --- a/src/main/java/org/openautonomousconnection/luascript/fx/FxEventHost.java +++ b/src/main/java/org/openautonomousconnection/luascript/fx/FxEventHost.java @@ -11,6 +11,8 @@ import org.w3c.dom.events.EventTarget; import java.util.Map; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * EventHost implementation for JavaFX WebView using W3C DOM EventTarget listeners. @@ -22,19 +24,31 @@ public final class FxEventHost implements EventHost { private static final String GLOBAL_TARGET_ID = "__global__"; private final FxDomHost dom; - private final LuaEventRouter router; + private volatile LuaEventRouter router; private final ConcurrentHashMap elementListeners = new ConcurrentHashMap<>(); private final ConcurrentHashMap globalListeners = new ConcurrentHashMap<>(); + private final ExecutorService dispatchExecutor = Executors.newSingleThreadExecutor(r -> { + Thread t = new Thread(r, "oac-fx-event-dispatch"); + t.setDaemon(true); + return t; + }); /** * Creates a new FxEventHost. * * @param dom fx dom host + */ + public FxEventHost(FxDomHost dom) { + this.dom = Objects.requireNonNull(dom, "dom"); + } + + /** + * Sets the active Lua router for this host. + * * @param router lua router */ - public FxEventHost(FxDomHost dom, LuaEventRouter router) { - this.dom = Objects.requireNonNull(dom, "dom"); + public void setRouter(LuaEventRouter router) { this.router = Objects.requireNonNull(router, "router"); } @@ -49,7 +63,7 @@ public final class FxEventHost implements EventHost { elementListeners.computeIfAbsent(key, k -> { EventListener listener = ev -> { Map payload = FxEventPayloadExtractor.extract(ev); - router.emit(elementId, evt, payload); + dispatchExecutor.execute(() -> emitSafely(elementId, evt, payload)); }; FxThreadBridge.runAndWait(() -> { @@ -87,7 +101,7 @@ public final class FxEventHost implements EventHost { globalListeners.computeIfAbsent(evt, k -> { EventListener listener = ev -> { Map payload = FxEventPayloadExtractor.extract(ev); - router.emit(GLOBAL_TARGET_ID, evt, payload); + dispatchExecutor.execute(() -> emitSafely(GLOBAL_TARGET_ID, evt, payload)); }; FxThreadBridge.runAndWait(() -> { @@ -113,4 +127,18 @@ public final class FxEventHost implements EventHost { ((EventTarget) doc).removeEventListener(evt, listener, false); }); } + + private void emitSafely(String elementId, String eventName, Map payload) { + try { + LuaEventRouter activeRouter = router; + if (activeRouter == null) { + System.err.println("[oac.event] Dropping '" + eventName + "' for '" + elementId + "': router not initialized"); + return; + } + activeRouter.emit(elementId, eventName, payload); + } catch (RuntimeException ex) { + System.err.println("[oac.event] Failed to dispatch '" + eventName + "' for '" + elementId + "': " + ex.getMessage()); + ex.printStackTrace(System.err); + } + } } diff --git a/src/main/java/org/openautonomousconnection/luascript/runtime/FxLuaScriptEngine.java b/src/main/java/org/openautonomousconnection/luascript/runtime/FxLuaScriptEngine.java index 29d1d69..0d314ca 100644 --- a/src/main/java/org/openautonomousconnection/luascript/runtime/FxLuaScriptEngine.java +++ b/src/main/java/org/openautonomousconnection/luascript/runtime/FxLuaScriptEngine.java @@ -82,23 +82,13 @@ public final class FxLuaScriptEngine implements AutoCloseable { .sandbox(true) ); - // Create runtime first (router lives inside it). HostServices.StdoutConsole console = new HostServices.StdoutConsole("[lua] "); FxUiHost uiHost = new FxUiHost(engine, dom); FxWebViewResourceHost resourceHost = new FxWebViewResourceHost(engine); - - // runtime depends on services; events depends on runtime router. - // We'll create eventHost after runtime, then build HostServices with it. - LuaRuntime rt = new LuaRuntime(globals, new HostServices.Default(uiHost, dom, null, resourceHost, console), policy); - - FxEventHost eventHost = new FxEventHost(dom, rt.eventRouter()); - - // Rebuild services including eventHost and reinstall tables. + FxEventHost eventHost = new FxEventHost(dom); HostServices services = new HostServices.Default(uiHost, dom, eventHost, resourceHost, console); - - // Replace runtime with correct services (clean and deterministic). - rt.close(); - rt = new LuaRuntime(globals, services, policy); + LuaRuntime rt = new LuaRuntime(globals, services, policy); + eventHost.setRouter(rt.eventRouter()); rt.installStdTables(true); rt.bootstrapFromDom(); @@ -131,4 +121,4 @@ public final class FxLuaScriptEngine implements AutoCloseable { } } } -} \ No newline at end of file +} diff --git a/src/main/java/org/openautonomousconnection/luascript/security/LuaExecutionPolicy.java b/src/main/java/org/openautonomousconnection/luascript/security/LuaExecutionPolicy.java index 7df2b46..90ab59d 100644 --- a/src/main/java/org/openautonomousconnection/luascript/security/LuaExecutionPolicy.java +++ b/src/main/java/org/openautonomousconnection/luascript/security/LuaExecutionPolicy.java @@ -24,6 +24,6 @@ public record LuaExecutionPolicy(Duration timeout, long instructionLimit, int ho * @return policy */ public static LuaExecutionPolicy uiDefault() { - return new LuaExecutionPolicy(Duration.ofMillis(50), 200_000, 5_000); + return new LuaExecutionPolicy(Duration.ofSeconds(5), 200_000, 5_000); } } diff --git a/src/main/java/org/openautonomousconnection/luascript/security/LuaSecurityManager.java b/src/main/java/org/openautonomousconnection/luascript/security/LuaSecurityManager.java index fa3abfe..fbb5c7e 100644 --- a/src/main/java/org/openautonomousconnection/luascript/security/LuaSecurityManager.java +++ b/src/main/java/org/openautonomousconnection/luascript/security/LuaSecurityManager.java @@ -1,8 +1,8 @@ package org.openautonomousconnection.luascript.security; import org.luaj.vm2.*; -import org.luaj.vm2.lib.DebugLib; import org.luaj.vm2.lib.VarArgFunction; +import org.openautonomousconnection.luascript.utils.LuaGlobalsFactory; import java.util.Objects; import java.util.concurrent.*; @@ -16,6 +16,7 @@ import java.util.concurrent.atomic.AtomicLong; public final class LuaSecurityManager implements AutoCloseable { private final ExecutorService executor; + private final ConcurrentHashMap debugSetHooks = new ConcurrentHashMap<>(); /** * Creates a new LuaSecurityManager that runs all Lua code on a single dedicated daemon thread. @@ -28,8 +29,8 @@ public final class LuaSecurityManager implements AutoCloseable { }); } - private static Varargs callWithHook(Globals globals, LuaFunction function, Varargs args, LuaExecutionPolicy policy) { - final LuaValue sethook = resolveAndHideDebugSetHook(globals); + private Varargs callWithHook(Globals globals, LuaFunction function, Varargs args, LuaExecutionPolicy policy) { + final LuaValue sethook = resolveDebugSetHook(globals); final long deadlineMillis = System.currentTimeMillis() + policy.timeout().toMillis(); final AtomicLong ticks = new AtomicLong(0); @@ -84,13 +85,19 @@ public final class LuaSecurityManager implements AutoCloseable { return resumed; } - private static LuaValue resolveAndHideDebugSetHook(Globals globals) { - // Ensure DebugLib exists for hooks. - globals.load(new DebugLib()); + private LuaValue resolveDebugSetHook(Globals globals) { + LuaValue cached = debugSetHooks.get(globals); + if (cached != null) return cached; + + LuaValue internal = globals.get(LuaGlobalsFactory.INTERNAL_DEBUG_SETHOOK_KEY); + if (!internal.isnil() && internal.isfunction()) { + debugSetHooks.put(globals, internal); + return internal; + } LuaValue debugTable = globals.get("debug"); if (debugTable.isnil() || !debugTable.istable()) { - throw new IllegalStateException("Debug library not available (debug table missing)"); + throw new IllegalStateException("Debug library not available (debug table missing and no internal sethook cached)"); } LuaValue sethook = debugTable.get("sethook"); @@ -100,6 +107,7 @@ public final class LuaSecurityManager implements AutoCloseable { // Hide debug from scripts. globals.set("debug", LuaValue.NIL); + debugSetHooks.put(globals, sethook); return sethook; } @@ -140,4 +148,4 @@ public final class LuaSecurityManager implements AutoCloseable { public void close() { executor.shutdownNow(); } -} +} \ No newline at end of file diff --git a/src/main/java/org/openautonomousconnection/luascript/utils/LuaGlobalsFactory.java b/src/main/java/org/openautonomousconnection/luascript/utils/LuaGlobalsFactory.java index cfcca18..2c44049 100644 --- a/src/main/java/org/openautonomousconnection/luascript/utils/LuaGlobalsFactory.java +++ b/src/main/java/org/openautonomousconnection/luascript/utils/LuaGlobalsFactory.java @@ -22,6 +22,8 @@ import java.util.function.Consumer; */ public final class LuaGlobalsFactory { + public static final String INTERNAL_DEBUG_SETHOOK_KEY = "__oac_internal_debug_sethook"; + private LuaGlobalsFactory() { } @@ -35,12 +37,13 @@ public final class LuaGlobalsFactory { Objects.requireNonNull(options, "options"); Globals g = JsePlatform.standardGlobals(); + g.load(new DebugLib()); + cacheInternalDebugSethook(g); if (options.enableDebug) { - // Ensure debug functions are available (depending on defaults). - g.load(new DebugLib()); + // Keep debug table visible to scripts. } else { - // If sandbox is not enabled but you still want no debug, explicitly remove it. + // Keep hook support internally, but do not expose debug to scripts. g.set("debug", LuaValue.NIL); } @@ -55,6 +58,16 @@ public final class LuaGlobalsFactory { return g; } + private static void cacheInternalDebugSethook(Globals g) { + LuaValue debugTable = g.get("debug"); + if (debugTable.isnil() || !debugTable.istable()) return; + + LuaValue sethook = debugTable.get("sethook"); + if (!sethook.isnil() && sethook.isfunction()) { + g.set(INTERNAL_DEBUG_SETHOOK_KEY, sethook); + } + } + /** * Applies basic sandbox hardening to the given globals. *