From 2f4972e6fda7a61425d60e8dda1d4cd2fb2facd3 Mon Sep 17 00:00:00 2001 From: Algent Date: Wed, 2 Sep 2026 11:25:04 +0200 Subject: [PATCH] refactor: port microblock generator to Java --- JAVA_MIGRATION.md | 9 +++ JAVA_MIGRATION_DIVERGENCES.md | 47 +++++++++++++++ JAVA_MIGRATION_HANDOFF.md | 26 +++++---- .../microblock/MicroblockGenerator$.java | 57 +++++++++++++++++++ .../microblock/MicroblockGenerator.java | 48 ++++++++++++++++ .../microblock/MicroblockGenerator.scala | 26 --------- 6 files changed, 177 insertions(+), 36 deletions(-) create mode 100644 src/main/scala/codechicken/microblock/MicroblockGenerator$.java create mode 100644 src/main/scala/codechicken/microblock/MicroblockGenerator.java delete mode 100644 src/main/scala/codechicken/microblock/MicroblockGenerator.scala diff --git a/JAVA_MIGRATION.md b/JAVA_MIGRATION.md index 84ab901..3fcb6b3 100644 --- a/JAVA_MIGRATION.md +++ b/JAVA_MIGRATION.md @@ -1202,3 +1202,12 @@ generated-trait compatibility work. Scala-runtime removal remains a later projec - A clean build passes all 257 plain-JVM tests and the Java 8 Forge server passes all 110 tests. All seven supported public surfaces and WR-CBE's static `recalcBounds` descriptor match the reference. `microblock/MicroblockGenerator.scala` is next. +- Characterized `MicroblockGenerator` against untouched Scala. Three plain-JVM cases freeze its facade, companion, + nested material interface, inherited `ASMMixinFactory`/`ScratchBitSet` shape, replaceable thread-local scratch state + and load-bearing calls. One Forge case freezes the complete material-added external Scala-trait path. +- Ported the facade, companion and real public static nested `IGeneratedMaterial` interface directly to Java while + leaving the generator and ScalaSignature machinery unchanged. Scratch-bit reuse, base/client selection, material + callback ordering, boxed constructor argument and the ProjectRed Scala-trait registration path are unchanged. +- A clean build passes all 260 plain-JVM tests and the Java 8 Forge server passes all 111 tests. The same three runtime + classes and every callable public descriptor match the reference, including ProjectRed's companion registration + and GuideNH's exact companion `create` method. `multipart/MultipartGenerator.scala` is next. diff --git a/JAVA_MIGRATION_DIVERGENCES.md b/JAVA_MIGRATION_DIVERGENCES.md index b1eeb3d..c7fd26d 100644 --- a/JAVA_MIGRATION_DIVERGENCES.md +++ b/JAVA_MIGRATION_DIVERGENCES.md @@ -2997,3 +2997,50 @@ inventory contains no reference to it. - Raw `javap` comparison confirms identical callable public names/descriptors for all seven supported types and bytecode-identical retained trait/helper disassembly. - Actual neighbour-driven shrink and render-bound refresh remain on the manual checklist. + +## 2026-09-02 — Microblock generator Java port + +The `MicroblockGenerator` facade, companion and nested `IGeneratedMaterial` interface are now Java. The companion +still extends the unchanged Scala `ASMMixinFactory` and implements the unchanged Scala `ScratchBitSet`; +all ScalaSignature parsing, Scala-trait registration and generated-class compilation remain in the original ASM +subsystem. + +### Observable behavior + +No known runtime divergence. Each `create` still clears and reuses the current thread's scratch `BitSet`, selects the +microblock class's base trait, adds the client trait only for client construction, then lets an +`IGeneratedMaterial` amend that same set before construction. The material ID remains a boxed `Integer` in the sole +generated constructor argument. + +The public scratch setter remains behaviorally replaceable despite being a compiler-oriented trait member. The +Forge fixture proves an external material receives only the expected initial base bit, adds a registered third-party +Scala trait, and produces a generated microblock with that trait's initialized state and dispatch. + +### ABI and reflection compatibility + +The facade, companion and nested material interface retain the same three binary names, hierarchy and callable +public names/descriptors. `IGeneratedMaterial` remains a real public static member of `MicroblockGenerator`; the +original pure abstract Scala trait emitted no `$class` helper, so none is removed. + +ProjectRed's load-bearing `MicroblockGenerator$.registerTrait(Class): int` call remains exact, and the unchanged +`ASMMixinFactory` still recognizes and compiles its external `LightMicroblock` Scala trait. GuideNH's exact reflective +`MicroblockGenerator$.create(MicroblockClass, int, boolean)` lookup also remains exact. All inherited static +forwarders, including the Scala `Seq`-shaped `construct`, keep their raw descriptors and generic surface. + +Accepted classfile-only differences are the Java facade's private constructor, the ordinary non-public Java class +initializer flag and removed Scala marker attributes. The private scratch `ThreadLocal` loses `ACC_FINAL`: Java +cannot assign a final instance field through the required public Scala-trait setter, and preserving the setter's +observable replacement behavior is more important than an unreferenced private modifier. + +### Compiler artifacts + +None. The clean reference and port jars contain the same three `MicroblockGenerator` classes. + +### Validation + +- `MicroblockGeneratorCharacterizationTest`: 3 plain-JVM tests, unchanged from the Scala baseline. +- Clean complete plain-JVM suite: 260 tests, 0 failures, 0 errors. +- Java 8 Forge dedicated-server suite: 111 tests, 0 failures, 0 errors, including material-driven addition, + initialization and dispatch of the external Scala microblock trait. +- Raw `javap` comparison confirms the exact three-class list and identical callable public names/descriptors. +- The existing ProjectRed illuminated-microblock row remains the full-pack/client manual checkpoint. diff --git a/JAVA_MIGRATION_HANDOFF.md b/JAVA_MIGRATION_HANDOFF.md index 92302e7..05202a5 100644 --- a/JAVA_MIGRATION_HANDOFF.md +++ b/JAVA_MIGRATION_HANDOFF.md @@ -12,8 +12,8 @@ what breaks, and what is left. The other documents hold the reasoning. | `JAVA_MIGRATION_MANUAL_CHECKS.md` | What no automated test can cover, and must be checked by hand in a client | | `JAVA_MIGRATION_PROFILE.md` | The focused baseline, first measured result, findings, and exact rerun command | -Branch: `algent/java`. Base: `master`. 146 commits including the API-cleanup plan and separate characterization and -port commits through micro occlusion. +Branch: `algent/java`. Base: `master`. 148 commits including the API-cleanup plan and separate characterization and +port commits through the microblock generator. ## The one rule that matters @@ -67,7 +67,7 @@ awk -F'"' '/ implements ScratchBitSet { + + public static final MicroblockGenerator$ MODULE$ = new MicroblockGenerator$(); + + private ThreadLocal codechicken$multipart$asm$ScratchBitSet$$bitSets; + + private MicroblockGenerator$() { + super(Microblock.class, Predef$.MODULE$.wrapRefArray(new Class[] { Integer.TYPE })); + ScratchBitSet$class.$init$(this); + } + + @Override + public ThreadLocal codechicken$multipart$asm$ScratchBitSet$$bitSets() { + return codechicken$multipart$asm$ScratchBitSet$$bitSets; + } + + @Override + public void codechicken$multipart$asm$ScratchBitSet$_setter_$codechicken$multipart$asm$ScratchBitSet$$bitSets_$eq( + ThreadLocal value) { + codechicken$multipart$asm$ScratchBitSet$$bitSets = value; + } + + @Override + public BitSet getBitSet() { + return ScratchBitSet$class.getBitSet(this); + } + + @Override + public BitSet freshBitSet() { + return ScratchBitSet$class.freshBitSet(this); + } + + public Microblock create(MicroblockClass microClass, int materialId, boolean client) { + BitSet traits = freshBitSet(); + traits.set(microClass.baseTraitId()); + if (client) { + traits.set(microClass.clientTraitId()); + } + + IMicroMaterial material = MicroMaterialRegistry.getMaterial(materialId); + if (material instanceof MicroblockGenerator.IGeneratedMaterial) { + ((MicroblockGenerator.IGeneratedMaterial) material).addTraits(traits, microClass, client); + } + + return construct(traits, Predef$.MODULE$.wrapRefArray(new Object[] { Integer.valueOf(materialId) })); + } +} diff --git a/src/main/scala/codechicken/microblock/MicroblockGenerator.java b/src/main/scala/codechicken/microblock/MicroblockGenerator.java new file mode 100644 index 0000000..af8c304 --- /dev/null +++ b/src/main/scala/codechicken/microblock/MicroblockGenerator.java @@ -0,0 +1,48 @@ +package codechicken.microblock; + +import java.util.BitSet; + +import scala.collection.Seq; + +public final class MicroblockGenerator { + + public interface IGeneratedMaterial { + + void addTraits(BitSet traits, MicroblockClass microClass, boolean client); + } + + private MicroblockGenerator() {} + + public static Class baseType() { + return MicroblockGenerator$.MODULE$.baseType(); + } + + @SuppressWarnings("rawtypes") + public static Object construct(BitSet traits, Seq arguments) { + return MicroblockGenerator$.MODULE$.construct(traits, arguments); + } + + public static int getId(String trait) { + return MicroblockGenerator$.MODULE$.getId(trait); + } + + public static int registerTrait(Class trait) { + return MicroblockGenerator$.MODULE$.registerTrait(trait); + } + + public static int registerTrait(String trait) { + return MicroblockGenerator$.MODULE$.registerTrait(trait); + } + + public static BitSet getBitSet() { + return MicroblockGenerator$.MODULE$.getBitSet(); + } + + public static BitSet freshBitSet() { + return MicroblockGenerator$.MODULE$.freshBitSet(); + } + + public static Microblock create(MicroblockClass microClass, int material, boolean client) { + return MicroblockGenerator$.MODULE$.create(microClass, material, client); + } +} diff --git a/src/main/scala/codechicken/microblock/MicroblockGenerator.scala b/src/main/scala/codechicken/microblock/MicroblockGenerator.scala deleted file mode 100644 index 67ce6b2..0000000 --- a/src/main/scala/codechicken/microblock/MicroblockGenerator.scala +++ /dev/null @@ -1,26 +0,0 @@ -package codechicken.microblock - -import codechicken.multipart.asm.{ScratchBitSet, ASMMixinFactory} -import java.util.BitSet - -object MicroblockGenerator - extends ASMMixinFactory(classOf[Microblock], classOf[Int]) - with ScratchBitSet { - trait IGeneratedMaterial { - def addTraits(traits: BitSet, mcrClass: MicroblockClass, client: Boolean) - } - - def create(mcrClass: MicroblockClass, material: Int, client: Boolean) = { - val bitset = freshBitSet - bitset.set(mcrClass.baseTraitId) - if (client) bitset.set(mcrClass.clientTraitId) - - MicroMaterialRegistry.getMaterial(material) match { - case genMat: IGeneratedMaterial => - genMat.addTraits(bitset, mcrClass, client) - case _ => - } - - construct(bitset, material: Integer) - } -}