Skip to content

Pre-initialize TRegex in the Graal fallback classloader - #72

Merged
rubensworks merged 1 commit into
master-1.21-ltsfrom
fix/68-tregex-preload
Sep 2, 2026
Merged

Pre-initialize TRegex in the Graal fallback classloader#72
rubensworks merged 1 commit into
master-1.21-ltsfrom
fix/68-tregex-preload

Conversation

@rubensworks

Copy link
Copy Markdown
Member

Fixes #68 (regex usage breaking since 1.0.26).

The bug

Reproduced in a game test on a clean dev environment. The reporter's visible error is only a follow-on symptom:

An error occurred when reading the script "filter-drops.js" at disk 0:
java.lang.NoClassDefFoundError: Could not initialize class
com.oracle.truffle.regex.tregex.util.Loggers

The actual first failure is:

java.lang.IllegalArgumentException: Unknown language or instrument id regex,
known ids: debugger, engine, graal, sandbox
    at com.oracle.truffle.api.TruffleLogger$LoggerCache.getOrCreateLogger(TruffleLogger.java:1058)
    at com.oracle.truffle.api.TruffleLogger.getLogger(TruffleLogger.java:161)
    at com.oracle.truffle.regex.tregex.util.Loggers.<clinit>(Loggers.java:64)
    at com.oracle.truffle.regex.tregex.TRegexCompiler.shouldLogCompilationTime(TRegexCompiler.java:119)
    at com.oracle.truffle.regex.tregex.TRegexCompiler.compile(TRegexCompiler.java:70)
    at com.oracle.truffle.regex.RegexLanguage.createRegexObject(RegexLanguage.java:205)
    ...
    at com.oracle.truffle.js.nodes.access.RegExpLiteralNode.execute(RegExpLiteralNode.java:83)

TRegex resolves its static loggers the first time a regex literal 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 and does not see the packaged Graal jars (note that js is missing from the known ids too). Because this happens inside a static initializer, the failure is permanent for the whole JVM session, so every later script using a regex only ever reports the NoClassDefFoundError above, no matter how the script is edited.

Why it started at 1.0.26

Graal 25.0 resolved this id set through EngineSupport.getLanguageIds() and cached it on the logger cache (TruffleLogger$LoggerCache.knownIds). Since 25.1 it is resolved per call via EngineSupport.isKnownLoggerId(...) against LanguageCache.languages(), which depends on the current thread context classloader. That matches the report: fine on 1.0.24 (Graal 25.0.x), broken on 1.0.26 (25.2.4).

The fix

Compile a regex during the existing startup pre-load, so TRegex initializes while the context classloader still points to the Graal fallback classloader, which is where all other Graal initialization already happens.

Tests

Added testScriptsDisplayScriptRegex, which applies a script function containing a regex literal. It fails on master with exactly the reporter's NoClassDefFoundError, and passes with this change.

./gradlew build and ./gradlew runGameTestServer both pass on this branch (all 22 game tests).

master-1.20-lts

Not applicable, for two independent reasons:

  • That branch has no PackagedDependenciesLoader/UnsafeHelper. It ships Graal through shadowJar with package relocation, so Graal is on the mod's own classpath and there is no fallback classloader to be blind to. There is also no game test infrastructure there for the regression test.
  • It is on graal_version=25.0.1, which predates the Graal change described above.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DwXjG9jWXimuecAeHk5oby


Generated by Claude Code

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.<clinit>
    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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DwXjG9jWXimuecAeHk5oby
@rubensworks
rubensworks merged commit 79ac86d into master-1.21-lts Sep 2, 2026
23 checks passed
@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 33665833901

Coverage increased (+0.2%) to 46.158%

Details

  • Coverage increased (+0.2%) from the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 2589
Covered Lines: 1316
Line Coverage: 50.83%
Relevant Branches: 860
Covered Branches: 276
Branch Coverage: 32.09%
Branches in Coverage %: Yes
Coverage Strength: 22.92 hits per line

💛 - Coveralls

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants