diff --git a/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java b/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java index 1ced9ba3a..1e6c1391e 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java +++ b/src/main/java/net/fabricmc/loom/configuration/ifaceinject/InterfaceInjectionProcessor.java @@ -242,7 +242,7 @@ public static List fromMod(FabricModJson fabricModJson) { name = ifaceInfo.substring(0, ifaceInfo.indexOf("<")); generics = ifaceInfo.substring(ifaceInfo.indexOf("<")); - // First Generics Check, if there are generics, are them correctly written? + // First Generics Check, if there are generics, are they correctly written? SignatureReader reader = new SignatureReader("Ljava/lang/Object" + generics + ";"); CheckSignatureAdapter checker = new CheckSignatureAdapter(CheckSignatureAdapter.CLASS_SIGNATURE, null); reader.accept(checker); @@ -313,6 +313,7 @@ public void visit(int version, int access, String name, String signature, String // Second Generics Check, if there are passed generics, are all of them present in the target class? GenericsChecker checker = new GenericsChecker(Constants.ASM_VERSION, injectedInterfaces); reader.accept(checker); + checker.check(); var resultingSignature = new StringBuilder(signature); @@ -345,7 +346,7 @@ public void visitInnerClass(final String name, final String outerName, final Str @Override public void visitEnd() { // inject any necessary inner class entries - // this may produce technically incorrect bytecode cuz we don't know the actual access flags for inner class entries + // this may produce technically incorrect bytecode cuz we don't know the actual access flags for inner class entries, // but it's hopefully enough to quiet some IDE errors for (final InjectedInterface itf : injectedInterfaces) { if (this.knownInnerClasses.contains(itf.ifaceName())) { @@ -402,8 +403,8 @@ public void visitFormalTypeParameter(String name) { super.visitFormalTypeParameter(name); } - @Override - public void visitEnd() { + // Ensures that injected interfaces only use collected type parameters from the target class + public void check() { for (InjectedInterface injectedInterface : this.injectedInterfaces) { if (injectedInterface.generics() != null) { SignatureReader reader = new SignatureReader("Ljava/lang/Object" + injectedInterface.generics() + ";"); @@ -416,8 +417,6 @@ public void visitEnd() { reader.accept(confirm); } } - - super.visitEnd(); } public static class GenericsConfirm extends SignatureVisitor { diff --git a/src/test/groovy/net/fabricmc/loom/test/unit/processor/InterfaceInjectionProcessorTest.groovy b/src/test/groovy/net/fabricmc/loom/test/unit/processor/InterfaceInjectionProcessorTest.groovy index cf66e0c8e..fb0d60d3c 100644 --- a/src/test/groovy/net/fabricmc/loom/test/unit/processor/InterfaceInjectionProcessorTest.groovy +++ b/src/test/groovy/net/fabricmc/loom/test/unit/processor/InterfaceInjectionProcessorTest.groovy @@ -39,6 +39,8 @@ import net.fabricmc.loom.configuration.ifaceinject.InterfaceInjectionProcessor import net.fabricmc.loom.test.unit.processor.classes.AdvancedGenericInterface import net.fabricmc.loom.test.unit.processor.classes.AdvancedGenericTargetClass import net.fabricmc.loom.test.unit.processor.classes.DoubleGenericTargetClass +import net.fabricmc.loom.test.unit.processor.classes.DoublePassingGenericInterface +import net.fabricmc.loom.test.unit.processor.classes.DoublePassingGenericTargetClass import net.fabricmc.loom.test.unit.processor.classes.FirstGenericInterface import net.fabricmc.loom.test.unit.processor.classes.GenericInterface import net.fabricmc.loom.test.unit.processor.classes.GenericTargetClass @@ -137,6 +139,12 @@ class InterfaceInjectionProcessorTest extends Specification { loadedClass.interfaces.first().name == "net/fabricmc/loom/test/unit/proessor/classes/SelfGenericInterface" loadedClass.constructors.first().newInstance().selfGenericInjectedMethod() == null } + + // Class using double generics and passing them to the interface + "class_9" | "net/fabricmc/loom/test/unit/processor/classes/DoublePassingGenericInterface" | DoublePassingGenericTargetClass.class | { Class loadedClass -> + loadedClass.interfaces.first().name == "net/fabricmc/loom/test/unit/processor/classes/DoublePassingGenericTargetClass" + loadedClass.constructors.first().newInstance().doublePassingGenericInjectedMethod().getClass() == DoublePassingGenericTargetClass.Pair.class + } } def "nothing to inject"() { @@ -230,7 +238,10 @@ class InterfaceInjectionProcessorTest extends Specification { FirstGenericInterface.class, SecondGenericInterface.class, SelfGenericTargetClass.class, - SelfGenericInterface.class + SelfGenericInterface.class, + DoublePassingGenericTargetClass.class, + DoublePassingGenericTargetClass.Pair.class, + DoublePassingGenericInterface.class ] private static final String MAPPINGS = """ @@ -243,5 +254,7 @@ c\tclass_5\tnet/fabricmc/loom/test/unit/processor/classes/AdvancedGenericTargetC c\tclass_5\$class_6\tnet/fabricmc/loom/test/unit/processor/classes/AdvancedGenericTargetClass\$Pair c\tclass_7\tnet/fabricmc/loom/test/unit/processor/classes/DoubleGenericTargetClass c\tclass_8\tnet/fabricmc/loom/test/unit/processor/classes/SelfGenericTargetClass +c\tclass_9\tnet/fabricmc/loom/test/unit/processor/classes/DoublePassingGenericTargetClass +c\tclass_9\$class_10\tnet/fabricmc/loom/test/unit/processor/classes/DoublePassingGenericTargetClass\$Pair """.trim() } diff --git a/src/test/java/net/fabricmc/loom/test/unit/processor/classes/DoublePassingGenericInterface.java b/src/test/java/net/fabricmc/loom/test/unit/processor/classes/DoublePassingGenericInterface.java new file mode 100644 index 000000000..fc120cd12 --- /dev/null +++ b/src/test/java/net/fabricmc/loom/test/unit/processor/classes/DoublePassingGenericInterface.java @@ -0,0 +1,31 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2024 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.test.unit.processor.classes; + +public interface DoublePassingGenericInterface { + default DoublePassingGenericTargetClass.Pair doublePassingGenericInjectedMethod() { + return new DoublePassingGenericTargetClass.Pair<>(null, null); + } +} diff --git a/src/test/java/net/fabricmc/loom/test/unit/processor/classes/DoublePassingGenericTargetClass.java b/src/test/java/net/fabricmc/loom/test/unit/processor/classes/DoublePassingGenericTargetClass.java new file mode 100644 index 000000000..a6bc55466 --- /dev/null +++ b/src/test/java/net/fabricmc/loom/test/unit/processor/classes/DoublePassingGenericTargetClass.java @@ -0,0 +1,32 @@ +/* + * This file is part of fabric-loom, licensed under the MIT License (MIT). + * + * Copyright (c) 2024 FabricMC + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package net.fabricmc.loom.test.unit.processor.classes; + +public class DoublePassingGenericTargetClass { + public static class Pair { + Pair(F ignoredF, S ignoredS) { + } + } +}