From 37699ba73d2f6ab33b7ef6526048e4e6607f2283 Mon Sep 17 00:00:00 2001 From: Algent Date: Fri, 4 Sep 2026 18:07:00 +0200 Subject: [PATCH] refactor: port ScalaSignature byte sections to Java --- JAVA_MIGRATION.md | 4 ++-- JAVA_MIGRATION_HANDOFF.md | 20 ++++++++--------- docs/migration/HISTORY.md | 22 +++++++++++++++++++ .../multipart/asm/ScalaSignature.scala | 2 +- .../multipart/asm/ScalaSignatureParser.java | 10 +++++++++ 5 files changed, 45 insertions(+), 13 deletions(-) diff --git a/JAVA_MIGRATION.md b/JAVA_MIGRATION.md index 094867a..0a4f2b4 100644 --- a/JAVA_MIGRATION.md +++ b/JAVA_MIGRATION.md @@ -28,8 +28,8 @@ placement/render helpers, both generators and all built-in tile traits are Java. hollow and occlusion microblock traits retain Scala inheritance/state/super declarations over Java behavior, including client bounds/mask updates. `StackAnalyser` initialization and constant-type classification delegate to Java; retain its coordinated Scala class/companion/models. `FieldMixin.accessName` also delegates to Java while -retaining its case class. `MixinInfo.linearise` delegates its recursive collection traversal to Java. The next bounded -candidate is `ScalaSignature.Bytes.section`, with characterization before extraction. +retaining its case class. `MixinInfo.linearise` and `ScalaSignature.Bytes.section` delegate traversal and array-copy +behavior to Java. The next bounded candidate is `ScalaSignature.TypeRef.jName`, with characterization before extraction. Java-trait rewriting and compiler startup also delegate to Java helpers. Abstract Java mixins and Java-path side-only filtering are complete; multiple Scala-trait inheritance still needs its metadata. Retain trait state/accessor/super bridges and the compiler/signature/analyser model shells. diff --git a/JAVA_MIGRATION_HANDOFF.md b/JAVA_MIGRATION_HANDOFF.md index 9c476a5..a5bd31b 100644 --- a/JAVA_MIGRATION_HANDOFF.md +++ b/JAVA_MIGRATION_HANDOFF.md @@ -16,7 +16,7 @@ migration checkout; `codex/tile-compatibility-fixes` was deleted after its fixes ## Current state and next target -**429 plain-JVM tests and 237 Java 8 Forge tests pass, with zero failures/errors/skips.** Sources total **224 Java +**435 plain-JVM tests and 237 Java 8 Forge tests pass, with zero failures/errors/skips.** Sources total **224 Java files and 9 Scala files / 765 nonblank Scala lines**. The packaged inventory has 443 classes. Review follow-up: restored packet-scheduler callback mutation behavior with the original Scala hash-map traversal, @@ -24,16 +24,16 @@ virtual tile accessor dispatch throughout `TMultiPart`, and null-safe equality f Six JVM and two Forge regression cases cover the fixes. Callable signatures remain unchanged; the packet traversal callback adds one anonymous class. See the latest history entries for the individual fixes and validation. -Latest bounded target: `ASMMixinCompiler.MixinInfo.linearise` delegates to `MixinClassGenerator.linearise`. -Seven characterization tests were committed first as `d93b09d`. They pin recursive parent order and duplicates, -collection builders, mutable parents, virtual parent/collection dispatch, erased casts and failure order. Flattening -retains its `SeqLike` intermediate before append returns a `Seq`. Scala keeps the model/companion and all bridges. -All 429 frozen JVM consumers pass; checks preserve 442 retained class/member APIs, all 17 ScalaSignature payloads, -3,731 unrelated method bodies and all 116 generated dumps. The obsolete Scala traversal closure disappears; -the Java helper reuses the existing callback adapter. Evidence: `run/migration-linearise-reference/`. +Latest bounded target: `ScalaSignature.Bytes.section` delegates to `ScalaSignatureParser.section`. +Six characterization tests were committed first as `13de853`. They pin independent drop/take clamping, fresh arrays, +virtual getter order, source mutations and failure ordering. The first array copy completes before `len()` is read, +so mutations from that getter do not affect the result. Scala retains the model/companion and serialization shape. +All 435 frozen JVM consumers pass; checks preserve 442 retained class/member APIs, all 17 ScalaSignature payloads, +3,732 unrelated method bodies and all 116 generated dumps. Evidence: `run/migration-bytes-section-reference/`. +The preceding `MixinInfo.linearise` extraction is recorded in history and `run/migration-linearise-reference/`. -**Next bounded candidate: `ScalaSignature.Bytes.section`.** Characterize clamped array slicing, fresh result arrays, -virtual getter order, mutations during getter calls and null/failure behavior before extracting its body. +**Next bounded candidate: `ScalaSignature.TypeRef.jName`.** Characterize slash normalization, Any/AnyRef aliases, +virtual name dispatch and null/failure behavior while retaining the trait and path-dependent model declarations. Keep case-class/product/serialization shapes and simple model accessors. Broader shell replacement and opcode algorithm fixes remain separate work. The external ProjectRed Scala-trait fixture and ScalaSignature model bridges remain required. Actual client diff --git a/docs/migration/HISTORY.md b/docs/migration/HISTORY.md index 769a0e4..71684d3 100644 --- a/docs/migration/HISTORY.md +++ b/docs/migration/HISTORY.md @@ -1413,3 +1413,25 @@ differences belong in the [divergence ledger](../../JAVA_MIGRATION_DIVERGENCES.m are under ignored `run/migration-linearise-reference/`; use its `version.txt` for frozen version assertions. - Source totals remain 224 Java files and nine Scala files / 765 nonblank lines. The next candidate is `ScalaSignature.Bytes.section`, preserving clamping, copy boundaries and virtual getter/failure ordering. + +### 2026-09-04 — ScalaSignature byte sections + +- Committed six JVM characterization cases first as `13de853`, against the untouched Scala `Bytes.section`. + They cover independent drop/take clamping including integer extremes, fresh empty/full/partial result arrays, + source/result isolation, virtual getter reads, mutations during `pos` and `len`, and every getter failure. + A null source still evaluates `pos` before failing; an empty dropped array still evaluates `len`. +- Extracted slicing into `ScalaSignatureParser.section` using `Arrays.copyOfRange` and `Arrays.copyOf`. The first + copy remains between the `pos` and `len` reads: mutations from `pos` are visible, while mutations from `len` are + excluded. This holds even for zero/negative positions. Scala retains the case class, companion, product/copy + methods and serialization shape. Ordinary Java 8 joint compilation remains sufficient. +- Normal and clean formatting/checkstyle/build/Forge checks pass: 435 JVM tests, all 435 frozen JVM consumers and + 237 Java 8 Forge tests, with zero failures/errors/skips. All 116 generated dump names and hashes match. Both jars + contain 443 Java 8 classes; packaged sources match. The comparison preserves 442 retained class/member APIs, + all 17 ScalaSignature payloads and 3,732 unrelated method bodies. Only `Bytes.section` and the parser helper have + changed class bytes; the helper adds one package-private method. No new effective divergence is introduced. +- Evidence and rerun tools are saved under ignored `run/migration-bytes-section-reference/`, including baseline + jar/sources, frozen tests/resources, reports, dumps and logs. Use its `version.txt` with `frozen-consumers.gradle` + for the existing inlined-version tests. The prior linearisation reference remains independently reproducible. +- Source totals remain 224 Java files and nine Scala files / 765 nonblank lines. Next bounded candidate: + `ScalaSignature.TypeRef.jName`, preserving name normalization, aliases and virtual dispatch while retaining + path-dependent model declarations and trait bridges. Descriptor conversion remains a separate target. diff --git a/src/main/scala/codechicken/multipart/asm/ScalaSignature.scala b/src/main/scala/codechicken/multipart/asm/ScalaSignature.scala index 4507e85..2dadcc6 100644 --- a/src/main/scala/codechicken/multipart/asm/ScalaSignature.scala +++ b/src/main/scala/codechicken/multipart/asm/ScalaSignature.scala @@ -9,7 +9,7 @@ object ScalaSignature { case class Bytes(arr: Array[Byte], pos: Int, len: Int) { def reader = new ByteCodeReader(this) - def section = arr.drop(pos).take(len) + def section = ScalaSignatureParser.section(this) } trait Flags { diff --git a/src/main/scala/codechicken/multipart/asm/ScalaSignatureParser.java b/src/main/scala/codechicken/multipart/asm/ScalaSignatureParser.java index 3b4bc25..05146b2 100644 --- a/src/main/scala/codechicken/multipart/asm/ScalaSignatureParser.java +++ b/src/main/scala/codechicken/multipart/asm/ScalaSignatureParser.java @@ -1,5 +1,6 @@ package codechicken.multipart.asm; +import java.util.Arrays; import java.util.Objects; import codechicken.multipart.asm.ScalaSignature.Bytes; @@ -19,6 +20,15 @@ final class ScalaSignatureParser { private ScalaSignatureParser() {} + static byte[] section(Bytes bytes) { + byte[] array = bytes.arr(); + int start = bytes.pos(); + start = Math.min(array.length, Math.max(0, start)); + // Snapshot before len(): an override can mutate the original array. + byte[] remaining = Arrays.copyOfRange(array, start, array.length); + return Arrays.copyOf(remaining, Math.min(remaining.length, Math.max(0, bytes.len()))); + } + // Keep path-dependent model types out of signatures read by Scala 2.11's Java parser. static Object readTable(ScalaSignature sig, Bytes bytes) { ByteCodeReader reader = bytes.reader();