refactor: modernize Scala signature switches

This commit is contained in:
Algent committed 2026-09-09 12:42:01 +02:00
1 parent fa0bd01ab2
commit c60abe790c
3 files changed
+54 -75

No files matched your search

+14 -8
View File
@@ -4,9 +4,9 @@
The scoped integration is implemented on `algent/java`, building on baseline `65d0cd0`. The three regression fixes
are preserved. `build.gradle` adds two tasks; `StackAnalyserLogic.visitInsn` uses the validated Java 21 pattern switch,
while `JavaTraitRegistration` and `ClassInfoLookup` use Java 21 pattern variables. The nine Scala sources, Scala 2.11.5
dependency, source layout, and normal Gradle entry points remain in place. Production tasks do not read a frozen jar
or any files under `run/jvmdg-trial/`.
`JavaTraitRegistration` and `ClassInfoLookup` use Java 21 pattern variables, and `ScalaSignatureParser` uses switch
expressions. The nine Scala sources, Scala 2.11.5 dependency, source layout, and normal Gradle entry points remain in
place. Production tasks do not read a frozen jar or any files under `run/jvmdg-trial/`.
The subsequent `StackAnalyser` initializer extraction is recorded in `JAVA_MIGRATION_HANDOFF.md`. The exact-byte
comparisons and frozen-version reproduction below describe checkpoint `5f0e329`; later helper edits need their own
@@ -75,9 +75,10 @@ Recommended order:
1. Start with package-private helpers called directly by retained Scala. `JavaTraitRegistration.java` and
`ClassInfoLookup.java` are completed follow-ups; pattern variables remove their checked casts without changing the
Scala-facing declarations. `StackAnalyserLogic.java` remains the original proven example.
2. Continue only where modern syntax produces a concrete control-flow gain. `ScalaSignatureParser.java` is the next
useful candidate because its two result-producing switches can become exhaustive switch expressions.
`ScalaTraitRegistration.java` should not move merely to restyle its erased `Some` checks.
2. Continue only where modern syntax produces a concrete control-flow gain. `ScalaSignatureParser.java` is complete:
its two result-producing switches are now switch expressions. Prefer simplifying the remaining opcode switches in
the already-modern `StackAnalyserLogic.java` before adding another build exclusion. `ScalaTraitRegistration.java`
should not move merely to restyle its erased `Some` checks.
3. Consider public core implementations such as `RedstoneInteractions$.java`, `TileMultipart.java` and the registries
only after the internal batches. Modern method bodies are technically possible, but their published ABI and frozen
behavior make the review cost higher.
@@ -106,6 +107,11 @@ The completed `ClassInfoLookup` batch has the same clean-build, 576-test, Java 8
metadata as annotations for this helper's anonymous callbacks, but the packaged classes contain no executable JVM
Downgrader API reference and the Java 8 runtime needs no added dependency.
The completed `ScalaSignatureParser` batch also passes the clean compiler boundary, all 576 JVM tests, all 289 Java 8
Forge tests and the 134-dump comparison; all 450 packaged classes remain version 52. Its non-private ABI is unchanged
and it has no executable JVM Downgrader API reference. Modern string concatenation in the same internal class lowers
to six private helper methods; these are compiler implementation details, not consumer entry points.
### fastutil audit
GTNHLib 0.11.44 currently contributes `it.unimi.dsi:fastutil:8.5.18` to `compileClasspath`. It does **not** contribute
@@ -165,8 +171,8 @@ scoped path selectively; if parsing, compilation order, ABI or downgrade support
working syntax and record the blocker and revisit condition. Do not add fragile workarounds solely for syntax.
The production source tree now contains 231 Java files and nine Scala files / 782 nonblank Scala lines. Of the 224 Java
sources in the Scala source tree, only `StackAnalyserLogic`, `JavaTraitRegistration` and `ClassInfoLookup` bypass joint
compilation. Retained models, trait
sources in the Scala source tree, only `StackAnalyserLogic`, `JavaTraitRegistration`, `ClassInfoLookup` and
`ScalaSignatureParser` bypass joint compilation. Retained models, trait
metadata, synthetic super accessors, and downstream Scala consumers prevent treating the last nine files as a
mechanical deletion queue. Modern GTNH runtime support does not remove the retained Scala compiler's Java 8
requirement. The main migration plan and working handoff carry the current API/adoption priorities and source counts.
+2
View File
@@ -15,6 +15,7 @@ def modernJavaSources = sourceSets.main.scala.matching {
include 'codechicken/multipart/asm/StackAnalyserLogic.java'
include 'codechicken/multipart/asm/JavaTraitRegistration.java'
include 'codechicken/multipart/asm/ClassInfoLookup.java'
include 'codechicken/multipart/asm/ScalaSignatureParser.java'
}
def legacyScala = tasks.named('compileScala') {
// Zinc does not invalidate joint-compiled Java annotations when Tags.VERSION changes.
@@ -23,6 +24,7 @@ def legacyScala = tasks.named('compileScala') {
exclude 'codechicken/multipart/asm/StackAnalyserLogic.java'
exclude 'codechicken/multipart/asm/JavaTraitRegistration.java'
exclude 'codechicken/multipart/asm/ClassInfoLookup.java'
exclude 'codechicken/multipart/asm/ScalaSignatureParser.java'
scalaCompileOptions.additionalParameters = ['-sourcepath', file('src/main/scala').absolutePath]
inputs.files(modernJavaSources).withPropertyName('modernJavaDeclarations').withPathSensitivity(PathSensitivity.RELATIVE)
}
@@ -157,22 +157,18 @@ final class ScalaSignatureParser {
Bytes bytes = entry.bytes();
ByteCodeReader reader = bytes.reader();
byte id = entry.id();
switch (id) {
case 1:
case 2:
return reader.readString(bytes.len());
case 3:
return sig.NoSymbol().full();
case 9:
case 10:
return switch (id) {
case 1, 2 -> reader.readString(bytes.len());
case 3 -> sig.NoSymbol().full();
case 9, 10 -> {
String name = sig.evalS(reader.readNat());
if (bytes.pos() + bytes.len() > reader.pos()) {
name = sig.evalS(reader.readNat()) + "." + name;
}
return name;
default:
throw new MatchError(Byte.valueOf(id));
}
yield name;
}
default -> throw new MatchError(Byte.valueOf(id));
};
}
static List<?> evalList(ScalaSignature sig, ByteCodeReader reader) {
@@ -185,61 +181,36 @@ final class ScalaSignatureParser {
static Object eval(ScalaSignature sig, int index, Object entry, ByteCodeReader reader, byte id) {
// The Scala optimizer constructs case classes directly, without initializing their companions.
switch (id) {
case 1:
case 2:
return sig.evalS(index);
case 3:
return sig.NoSymbol();
case 6:
return sig.new ClassSymbol(sig.evalS(reader.readNat()), sig.evalT(reader.readNat()), reader.readNat(),
reader.readNat());
case 7:
return sig.new ObjectSymbol(sig.evalS(reader.readNat()), sig.evalT(reader.readNat()), reader.readNat(),
reader.readNat());
case 8:
return sig.new MethodSymbol(sig.evalS(reader.readNat()), sig.evalT(reader.readNat()), reader.readNat(),
reader.readNat());
case 9:
case 10:
return sig.new ExternalSymbol(sig.evalS(index));
case 11:
case 12: // NoPrefixType is treated as NoType in the reference.
return sig.NoType();
case 13:
return sig.new ThisType(sig.evalT(reader.readNat()));
case 14:
return sig.new SingleType(sig.evalT(reader.readNat()), sig.evalT(reader.readNat()));
case 21:
case 48: // Retain the reference's parameterless interpretation of a bounded super type.
return sig.new ParameterlessType(sig.evalT(reader.readNat()));
case 25:
return sig.new BooleanLiteral(reader.readLong() != 0);
case 26:
return sig.new ByteLiteral((byte) reader.readLong());
case 27:
return sig.new ShortLiteral((short) reader.readLong());
case 28:
return sig.new CharLiteral((char) reader.readLong());
case 29:
return sig.new IntLiteral((int) reader.readLong());
case 30:
return sig.new LongLiteral(reader.readLong());
case 31:
return sig.new FloatLiteral(Float.intBitsToFloat((int) reader.readLong()));
case 32:
return sig.new DoubleLiteral(Double.longBitsToDouble(reader.readLong()));
case 33:
return sig.new StringLiteral(sig.evalS(reader.readNat()));
case 34:
return sig.NullLiteral();
case 35:
return sig.new TypeLiteral(sig.evalT(reader.readNat()));
case 36:
return sig.new EnumLiteral(sig.evalT(reader.readNat()));
default:
return entry;
}
return switch (id) {
case 1, 2 -> sig.evalS(index);
case 3 -> sig.NoSymbol();
case 6 -> sig.new ClassSymbol(sig.evalS(reader.readNat()), sig.evalT(reader.readNat()), reader.readNat(),
reader.readNat());
case 7 -> sig.new ObjectSymbol(sig.evalS(reader.readNat()), sig.evalT(reader.readNat()), reader.readNat(),
reader.readNat());
case 8 -> sig.new MethodSymbol(sig.evalS(reader.readNat()), sig.evalT(reader.readNat()), reader.readNat(),
reader.readNat());
case 9, 10 -> sig.new ExternalSymbol(sig.evalS(index));
// NoPrefixType is treated as NoType in the reference.
case 11, 12 -> sig.NoType();
case 13 -> sig.new ThisType(sig.evalT(reader.readNat()));
case 14 -> sig.new SingleType(sig.evalT(reader.readNat()), sig.evalT(reader.readNat()));
// Retain the reference's parameterless interpretation of a bounded super type.
case 21, 48 -> sig.new ParameterlessType(sig.evalT(reader.readNat()));
case 25 -> sig.new BooleanLiteral(reader.readLong() != 0);
case 26 -> sig.new ByteLiteral((byte) reader.readLong());
case 27 -> sig.new ShortLiteral((short) reader.readLong());
case 28 -> sig.new CharLiteral((char) reader.readLong());
case 29 -> sig.new IntLiteral((int) reader.readLong());
case 30 -> sig.new LongLiteral(reader.readLong());
case 31 -> sig.new FloatLiteral(Float.intBitsToFloat((int) reader.readLong()));
case 32 -> sig.new DoubleLiteral(Double.longBitsToDouble(reader.readLong()));
case 33 -> sig.new StringLiteral(sig.evalS(reader.readNat()));
case 34 -> sig.NullLiteral();
case 35 -> sig.new TypeLiteral(sig.evalT(reader.readNat()));
case 36 -> sig.new EnumLiteral(sig.evalT(reader.readNat()));
default -> entry;
};
}
static <T> IndexedSeq<T> collect(ScalaSignature sig, int id) {