From e16179b7972c38b973938377af543eb067f8f6d7 Mon Sep 17 00:00:00 2001 From: embeddedt <42941056+embeddedt@users.noreply.github.com> Date: Fri, 27 Feb 2026 19:08:06 -0500 Subject: [PATCH] Emit more debug info to the generated dispatcher classes --- ...CapabilityProviderDispatcherGenerator.java | 49 ++++++++++++++----- .../forge/capability/OriginalType.java | 17 +++++++ 2 files changed, 53 insertions(+), 13 deletions(-) create mode 100644 src/main/java/org/embeddedt/modernfix/forge/capability/OriginalType.java diff --git a/src/main/java/org/embeddedt/modernfix/forge/capability/CapabilityProviderDispatcherGenerator.java b/src/main/java/org/embeddedt/modernfix/forge/capability/CapabilityProviderDispatcherGenerator.java index 2e460737..c5c5c10e 100644 --- a/src/main/java/org/embeddedt/modernfix/forge/capability/CapabilityProviderDispatcherGenerator.java +++ b/src/main/java/org/embeddedt/modernfix/forge/capability/CapabilityProviderDispatcherGenerator.java @@ -66,6 +66,7 @@ public class CapabilityProviderDispatcherGenerator { private static final String LAZY_OPTIONAL_DESC = "Lnet/minecraftforge/common/util/LazyOptional;"; private static final String DIRECTION_DESC = "Lnet/minecraft/core/Direction;"; private static final String MAP_DESC = "Ljava/util/Map;"; + private static final String MAP_SIGNATURE = "Ljava/util/Map;Lnet/minecraftforge/common/capabilities/ICapabilityProvider;>;"; /** * Gets or generates a constructor MethodHandle for the given capability provider types. @@ -276,13 +277,20 @@ public class CapabilityProviderDispatcherGenerator { // Generate final fields for each distinct provider LinkedHashMap providerFields = collectProviderFields(dispatches); for (var entry : providerFields.entrySet()) { - cw.visitField(ACC_PRIVATE | ACC_FINAL, "provider" + entry.getKey(), entry.getValue(), null, null).visitEnd(); + FieldVisitor fv = cw.visitField(ACC_PRIVATE | ACC_FINAL, "provider" + entry.getKey(), entry.getValue(), null, null); + if (entry.getValue().equals(ICAP_PROVIDER_DESC)) { + String originalName = providerTypes.get(entry.getKey()).getName(); + AnnotationVisitor av = fv.visitAnnotation("Lorg/embeddedt/modernfix/forge/capability/OriginalType;", false); + av.visit("value", originalName); + av.visitEnd(); + } + fv.visitEnd(); } // Generate map fields for Hash dispatches for (ProviderDispatch dispatch : dispatches) { if (dispatch instanceof ProviderDispatch.Hash hash) { - cw.visitField(ACC_PRIVATE | ACC_FINAL, "capMap" + hash.mapIndex(), MAP_DESC, null, null).visitEnd(); + cw.visitField(ACC_PRIVATE | ACC_FINAL, "capMap" + hash.mapIndex(), MAP_DESC, MAP_SIGNATURE, null).visitEnd(); } } @@ -367,32 +375,36 @@ public class CapabilityProviderDispatcherGenerator { mv.visitCode(); - // Generate unrolled dispatch loop - // For each provider, call getCapability and check if present - Label endLabel = new Label(); + Label methodStart = new Label(); + Label methodEnd = new Label(); + mv.visitLabel(methodStart); String internalName = className.replace('.', '/'); String getCapDesc = "(" + CAPABILITY_DESC + DIRECTION_DESC + ")" + LAZY_OPTIONAL_DESC; + // slot 3 = LazyOptional result (all paths) + // slot 4 = ICapabilityProvider provider (Hash paths only) + boolean usesProviderLocal = dispatches.stream().anyMatch(d -> d instanceof ProviderDispatch.Hash); + for (ProviderDispatch dispatch : dispatches) { Label nextLabel = new Label(); if (dispatch instanceof ProviderDispatch.Hash hash) { - // ICapabilityProvider p = (ICapabilityProvider) this.capMapN.get(cap); + // ICapabilityProvider provider = (ICapabilityProvider) this.capMapN.get(cap); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, internalName, "capMap" + hash.mapIndex(), MAP_DESC); mv.visitVarInsn(ALOAD, 1); mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true); - mv.visitVarInsn(ASTORE, 3); + mv.visitTypeInsn(CHECKCAST, "net/minecraftforge/common/capabilities/ICapabilityProvider"); + mv.visitVarInsn(ASTORE, 4); - // if (p == null) goto next - mv.visitVarInsn(ALOAD, 3); + // if (provider == null) goto next + mv.visitVarInsn(ALOAD, 4); mv.visitJumpInsn(IFNULL, nextLabel); - // result = ((ICapabilityProvider) p).getCapability(cap, side) - mv.visitVarInsn(ALOAD, 3); - mv.visitTypeInsn(CHECKCAST, "net/minecraftforge/common/capabilities/ICapabilityProvider"); + // LazyOptional result = provider.getCapability(cap, side) + mv.visitVarInsn(ALOAD, 4); mv.visitVarInsn(ALOAD, 1); mv.visitVarInsn(ALOAD, 2); mv.visitMethodInsn(INVOKEINTERFACE, @@ -445,7 +457,6 @@ public class CapabilityProviderDispatcherGenerator { } // If no provider returned a capability, return empty - mv.visitLabel(endLabel); mv.visitMethodInsn( INVOKESTATIC, "net/minecraftforge/common/util/LazyOptional", @@ -455,6 +466,18 @@ public class CapabilityProviderDispatcherGenerator { ); mv.visitInsn(ARETURN); + mv.visitLabel(methodEnd); + + // Local variable table for clean decompilation + String capSig = CAPABILITY_DESC.replace(";", ";"); + String resultSig = LAZY_OPTIONAL_DESC.replace(";", ";"); + mv.visitLocalVariable("cap", CAPABILITY_DESC, capSig, methodStart, methodEnd, 1); + mv.visitLocalVariable("side", DIRECTION_DESC, null, methodStart, methodEnd, 2); + mv.visitLocalVariable("result", LAZY_OPTIONAL_DESC, resultSig, methodStart, methodEnd, 3); + if (usesProviderLocal) { + mv.visitLocalVariable("provider", ICAP_PROVIDER_DESC, null, methodStart, methodEnd, 4); + } + mv.visitMaxs(0, 0); // Computed by COMPUTE_MAXS mv.visitEnd(); } diff --git a/src/main/java/org/embeddedt/modernfix/forge/capability/OriginalType.java b/src/main/java/org/embeddedt/modernfix/forge/capability/OriginalType.java new file mode 100644 index 00000000..cf2143cf --- /dev/null +++ b/src/main/java/org/embeddedt/modernfix/forge/capability/OriginalType.java @@ -0,0 +1,17 @@ +package org.embeddedt.modernfix.forge.capability; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Applied to generated provider fields whose declared type has been widened to + * {@link net.minecraftforge.common.capabilities.ICapabilityProvider} because the + * concrete class is non-public or hidden. The value records the original type name. + */ +@Retention(RetentionPolicy.CLASS) +@Target(ElementType.FIELD) +public @interface OriginalType { + String value(); +}