From cec1d98dc603d922d12bf932a0f8f46dcad77f47 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 2 Sep 2026 12:02:55 +0000 Subject: [PATCH] Pre-initialize TRegex in the Graal fallback classloader TRegex resolves its static loggers the first time a regex is compiled. That lookup validates the "regex" language id against the languages visible from the thread context classloader, which on a server thread is NeoForge's classloader that does not see the packaged Graal jars: java.lang.IllegalArgumentException: Unknown language or instrument id regex, known ids: debugger, engine, graal, sandbox at TruffleLogger$LoggerCache.getOrCreateLogger at com.oracle.truffle.regex.tregex.util.Loggers. at com.oracle.truffle.regex.tregex.TRegexCompiler.compile Because it happens inside a static initializer, the failure is permanent for the JVM session, and every later script using a regex only reports "NoClassDefFoundError: Could not initialize class com.oracle.truffle.regex.tregex.util.Loggers". Graal 25.0 cached this id set on the logger cache, so it was resolved once during startup. Since 25.1 it is resolved per call, which is why this only appeared after the Graal bump. Compiling a regex during the startup pre-load initializes TRegex while the context classloader still points to the Graal fallback classloader, where all other Graal initialization already happens. Closes #68 Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01DwXjG9jWXimuecAeHk5oby --- .../PackagedDependenciesLoader.java | 5 +++ .../gametest/GameTestsScripts.java | 32 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/main/java/org/cyclops/integratedscripting/core/packageddependencies/PackagedDependenciesLoader.java b/src/main/java/org/cyclops/integratedscripting/core/packageddependencies/PackagedDependenciesLoader.java index c95aa35b1..13858d1af 100644 --- a/src/main/java/org/cyclops/integratedscripting/core/packageddependencies/PackagedDependenciesLoader.java +++ b/src/main/java/org/cyclops/integratedscripting/core/packageddependencies/PackagedDependenciesLoader.java @@ -70,6 +70,11 @@ public static void load() throws IOException { Context.Builder build = Context.newBuilder("js"); Context con = build.build(); con.eval("js", "console.log('graaljs has been pre-loaded.')"); + // Force-initialize TRegex here as well. + // Its static loggers resolve the 'regex' language id via the thread context classloader, + // which only points to Graal within this block. + // Initializing it later (on a server thread) fails permanently for the whole JVM session. + con.eval("js", "/graaljs/.test('graaljs')"); con.close(); } finally { Thread.currentThread().setContextClassLoader(p); diff --git a/src/main/java/org/cyclops/integratedscripting/gametest/GameTestsScripts.java b/src/main/java/org/cyclops/integratedscripting/gametest/GameTestsScripts.java index f33feb641..b7c2186e9 100644 --- a/src/main/java/org/cyclops/integratedscripting/gametest/GameTestsScripts.java +++ b/src/main/java/org/cyclops/integratedscripting/gametest/GameTestsScripts.java @@ -15,6 +15,7 @@ import org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeItemStack; import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeBoolean; import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeInteger; +import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypeString; import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes; import org.cyclops.integrateddynamics.part.PartTypePanelDisplay; import org.cyclops.integratedscripting.Reference; @@ -118,6 +119,37 @@ public void testScriptsDisplayScriptOnItem(GameTestHelper helper) { }); } + @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = TIMEOUT) + public void testScriptsDisplayScriptRegex(GameTestHelper helper) { + GameTestHelpersIntegratedScripting.NetworkPositions positions = createBasicNetwork(helper, POS); + + // Write script + ScriptingNetworkHelpers.getScriptingData().setScript(positions.diskId(), Path.of("script0.js"), "function abc(a) { return /^aether:.+_gloves/.test(a); }", IScriptingData.ChangeLocation.MEMORY); + + // Create variable from script + ItemStack variableScript = createVariableForScript(helper.getLevel(), positions.diskId(), Path.of("script0.js"), "abc"); + + // Create constants as input to the script's function + ItemStack variableConst1 = createVariableForValue(helper.getLevel(), ValueTypes.STRING, ValueTypeString.ValueString.of("aether:leather_gloves")); + + // Insert all variables into the variable store + positions.variableStore().getInventory().setItem(0, variableScript); + positions.variableStore().getInventory().setItem(1, variableConst1); + + // Create variable card for applying the function + ItemStack variableApplied = createVariableForOperator(helper.getLevel(), Operators.OPERATOR_APPLY, new int[]{ + getVariableFacade(helper.getLevel(), variableScript).getId(), + getVariableFacade(helper.getLevel(), variableConst1).getId(), + }); + + // Place variable in display + Pair partAndState = placeVariableInDisplayPanel(helper.getLevel(), positions.displayPanel(), variableApplied); + + helper.succeedWhen(() -> { + assertValueEqual(partAndState.getRight().getDisplayValue(), ValueTypeBoolean.ValueBoolean.of(true)); + }); + } + @GameTest(template = TEMPLATE_EMPTY, timeoutTicks = TIMEOUT) public void testScriptsDisplayScriptInfiniteLoop(GameTestHelper helper) { GameTestHelpersIntegratedScripting.NetworkPositions positions = createBasicNetwork(helper, POS);