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 6191a2d7..6aa4fb75 100644
--- a/src/main/java/org/embeddedt/modernfix/forge/capability/CapabilityProviderDispatcherGenerator.java
+++ b/src/main/java/org/embeddedt/modernfix/forge/capability/CapabilityProviderDispatcherGenerator.java
@@ -7,6 +7,7 @@ import org.embeddedt.modernfix.ModernFix;
import org.embeddedt.modernfix.forge.capability.analysis.CapabilityAnalysisResult;
import org.embeddedt.modernfix.forge.capability.analysis.CapabilityAnalyzer;
import org.embeddedt.modernfix.forge.capability.analysis.CapabilityRef;
+import org.jetbrains.annotations.NotNull;
import org.objectweb.asm.*;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.commons.Method;
@@ -190,6 +191,8 @@ public class CapabilityProviderDispatcherGenerator {
* Resolves the actual {@link Capability} instances for all refs at class-generation time.
* Uses reflection (with {@code setAccessible}) so private fields are handled without any
* reflection bytecode appearing in the generated class.
+ *
+ * Field lookup is delegated to {@link #getRefField(Class, CapabilityRef)}
*/
private static List> resolveCapabilityValues(LinkedHashMap capRefIndices) {
@SuppressWarnings("unchecked")
@@ -199,8 +202,7 @@ public class CapabilityProviderDispatcherGenerator {
try {
Class> clazz = Class.forName(ref.owner().replace('/', '.'), false,
CapabilityProviderDispatcherGenerator.class.getClassLoader());
- Field field = clazz.getDeclaredField(ref.fieldName());
- field.setAccessible(true);
+ Field field = getRefField(clazz, ref);
caps[entry.getValue()] = (Capability>) field.get(null);
} catch (ReflectiveOperationException e) {
throw new RuntimeException("Failed to resolve capability field " + ref, e);
@@ -209,6 +211,27 @@ public class CapabilityProviderDispatcherGenerator {
return Arrays.asList(caps);
}
+ /**
+ * Resolves the {@link Field} for the given {@link CapabilityRef},
+ * falls back to the implemented interfaces if no match is found.
+ */
+ private static @NotNull Field getRefField(Class> clazz, CapabilityRef ref) throws NoSuchFieldException {
+ Field field = null;
+ try {
+ field = clazz.getDeclaredField(ref.fieldName());
+ } catch (NoSuchFieldException ignored) {
+ for (Class> iface : clazz.getInterfaces()) {
+ try {
+ field = iface.getDeclaredField(ref.fieldName());
+ break;
+ } catch (NoSuchFieldException ignored1) {}
+ }
+ }
+ if (field == null) throw new NoSuchFieldException(ref.fieldName());
+ field.setAccessible(true);
+ return field;
+ }
+
/**
* Build the dispatch list describing how each provider should be handled.
*/