diff --git a/JAVA_MIGRATION_DIVERGENCES.md b/JAVA_MIGRATION_DIVERGENCES.md index b28d5ec..77c4e8b 100644 --- a/JAVA_MIGRATION_DIVERGENCES.md +++ b/JAVA_MIGRATION_DIVERGENCES.md @@ -443,3 +443,49 @@ No in-repo Scala needed changing. - Complete plain-JVM suite: 61 tests, 0 failures, 0 errors. - Clean `spotlessApply checkstyleTest build`: passing. - Java 8 Forge dedicated-server suite: 3 tests, 0 failures, 0 errors. + +## 2026-08-14 — Saw Java port + +The first port outside `codechicken.multipart`. `Saw` was declared in `ItemSaw.scala`; only the trait is extracted, +and `ItemSaw` itself stays Scala. + +### Observable behavior + +No known divergence. `getMaxCuttingStrength` still asks the saw about a stack of itself, so the stack handed to +`getCuttingStrength` wraps the saw and has size 1, and the result still follows an implementor's +`getCuttingStrength` override. + +### Default method, and a shape change on ItemSaw + +`Item` does not declare `getMaxCuttingStrength`, so it becomes a real Java `default`. + +Consequence worth recording: `ItemSaw` previously carried a Scala trait forwarder for `getMaxCuttingStrength` and now +inherits the default instead, so it no longer declares that method. Three jars reference the `ItemSaw` type +(IguanaTweaksTConstruct, ProjectBlue, endercore) but none reference any of its members, and an `invokevirtual` against +`ItemSaw.getMaxCuttingStrength` would still resolve through the interface default. `harvestLevel` and +`getCuttingStrength` are unchanged. + +This is the first time a port changed the declared members of a class it did not touch. Watch for it whenever a trait +default is introduced on a type with concrete Scala implementors. + +### Unlike the other constant bridges + +`Saw$class.getMaxCuttingStrength` dispatches `getCuttingStrength` back through the interface, so an implementor's +override is honoured even when the bridge is called directly. `TFacePart$class` and `TEdgePart$class` hold constants +and deliberately ignore overrides. Both behaviors are preserved as found and are asserted separately. + +### Supported JVM API + +- `Saw` is descriptor-identical to the reference. +- `Saw$class` keeps both statics for ProjRed and adds only a private constructor. Its body casts to `Item` to build + the stack, exactly as the reference bytecode did. +- The emitted class-file set for this area is unchanged. + +### Validation + +- `SawCharacterizationTest`: 3 tests, 0 failures, 0 errors, unchanged from the Scala baseline. +- `SawBinaryCompatibilityTest`: 1 test, 0 failures, 0 errors, where the fixture reports its strength only if handed a + stack that wraps itself. +- Complete plain-JVM suite: 65 tests, 0 failures, 0 errors. +- Clean `spotlessApply checkstyleTest build`: passing. +- Java 8 Forge dedicated-server suite: 3 tests, 0 failures, 0 errors. diff --git a/src/main/scala/codechicken/microblock/ItemSaw.scala b/src/main/scala/codechicken/microblock/ItemSaw.scala index fa5af8e..e0371fb 100644 --- a/src/main/scala/codechicken/microblock/ItemSaw.scala +++ b/src/main/scala/codechicken/microblock/ItemSaw.scala @@ -21,20 +21,6 @@ import net.minecraft.client.renderer.texture.{TextureMap, IIconRegister} import codechicken.lib.render.uv.UVTranslation import codechicken.microblock.handler.MicroblockProxy -/** Interface for items that are 'saws' - */ -trait Saw extends Item { - - /** The maximum harvest level that some version of this saw is capable of - * cutting - */ - def getMaxCuttingStrength: Int = getCuttingStrength(new ItemStack(this)) - - /** The harvest level this saw is capable of cutting - */ - def getCuttingStrength(item: ItemStack): Int -} - class ItemSaw(sawTag: ConfigTag, val harvestLevel: Int) extends Item with Saw { { val maxDamage = diff --git a/src/main/scala/codechicken/microblock/Saw$class.java b/src/main/scala/codechicken/microblock/Saw$class.java new file mode 100644 index 0000000..7471dc7 --- /dev/null +++ b/src/main/scala/codechicken/microblock/Saw$class.java @@ -0,0 +1,20 @@ +package codechicken.microblock; + +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +/** + * Binary bridge for Scala implementations compiled against the original trait. Their forwarders call this static + * directly, so the body cannot delegate back to the instance method without recursing. + */ +@Deprecated +public abstract class Saw$class { + + private Saw$class() {} + + public static int getMaxCuttingStrength(Saw saw) { + return saw.getCuttingStrength(new ItemStack((Item) saw)); + } + + public static void $init$(Saw saw) {} +} diff --git a/src/main/scala/codechicken/microblock/Saw.java b/src/main/scala/codechicken/microblock/Saw.java new file mode 100644 index 0000000..31cde0e --- /dev/null +++ b/src/main/scala/codechicken/microblock/Saw.java @@ -0,0 +1,21 @@ +package codechicken.microblock; + +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +/** + * Interface for items that are 'saws'. + *

+ * Item does not declare getMaxCuttingStrength, so it can safely be a default method. Implementors must extend + * {@link Item}; the default casts to it in order to build a stack of itself, exactly as the reference did. + */ +public interface Saw { + + /** The maximum harvest level that some version of this saw is capable of cutting. */ + default int getMaxCuttingStrength() { + return getCuttingStrength(new ItemStack((Item) this)); + } + + /** The harvest level this saw is capable of cutting. */ + int getCuttingStrength(ItemStack item); +}