diff --git a/src/main/java/net/minecraftforge/fml/common/asm/transformers/SideTransformer.java b/src/main/java/net/minecraftforge/fml/common/asm/transformers/SideTransformer.java index 68e6c1f9b9..4c1d0b5731 100644 --- a/src/main/java/net/minecraftforge/fml/common/asm/transformers/SideTransformer.java +++ b/src/main/java/net/minecraftforge/fml/common/asm/transformers/SideTransformer.java @@ -19,8 +19,10 @@ package net.minecraftforge.fml.common.asm.transformers; +import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import net.minecraft.launchwrapper.IClassTransformer; import net.minecraftforge.fml.relauncher.FMLLaunchHandler; @@ -28,7 +30,11 @@ import net.minecraftforge.fml.relauncher.SideOnly; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.Handle; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; +import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldNode; @@ -69,6 +75,8 @@ public class SideTransformer implements IClassTransformer fields.remove(); } } + + LambdaGatherer lambdaGatherer = new LambdaGatherer(); Iterator methods = classNode.methods.iterator(); while(methods.hasNext()) { @@ -80,6 +88,29 @@ public class SideTransformer implements IClassTransformer System.out.println(String.format("Removing Method: %s.%s%s", classNode.name, method.name, method.desc)); } methods.remove(); + lambdaGatherer.accept(method); + } + } + + // remove dynamic lambda methods that are inside of removed methods + List dynamicLambdaHandles = lambdaGatherer.getDynamicLambdaHandles(); + if (!dynamicLambdaHandles.isEmpty()) + { + methods = classNode.methods.iterator(); + while (methods.hasNext()) + { + MethodNode method = methods.next(); + for (Handle dynamicLambdaHandle : dynamicLambdaHandles) + { + if (method.name.equals(dynamicLambdaHandle.getName()) && method.desc.equals(dynamicLambdaHandle.getDesc())) + { + if (DEBUG) + { + System.out.println(String.format("Removing Method: %s.%s%s", classNode.name, method.name, method.desc)); + } + methods.remove(); + } + } } } @@ -120,4 +151,41 @@ public class SideTransformer implements IClassTransformer } return false; } + + private static class LambdaGatherer extends MethodVisitor { + private static final Handle META_FACTORY = new Handle(Opcodes.H_INVOKESTATIC, "java/lang/invoke/LambdaMetafactory", "metafactory", + "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"); + private final List dynamicLambdaHandles = new ArrayList(); + + public LambdaGatherer() { + super(Opcodes.ASM5); + } + + public void accept(MethodNode method) { + ListIterator insnNodeIterator = method.instructions.iterator(); + while (insnNodeIterator.hasNext()) + { + AbstractInsnNode insnNode = insnNodeIterator.next(); + if (insnNode.getType() == AbstractInsnNode.INVOKE_DYNAMIC_INSN) + { + insnNode.accept(this); + } + } + } + + @Override + public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, Object... bsmArgs) + { + if (META_FACTORY.equals(bsm)) + { + Handle dynamicLambdaHandle = (Handle) bsmArgs[1]; + dynamicLambdaHandles.add(dynamicLambdaHandle); + } + } + + public List getDynamicLambdaHandles() + { + return dynamicLambdaHandles; + } + } } diff --git a/src/main/java/net/minecraftforge/fml/common/registry/FMLControlledNamespacedRegistry.java b/src/main/java/net/minecraftforge/fml/common/registry/FMLControlledNamespacedRegistry.java index 6e8f0cdc1c..200c2f64b5 100644 --- a/src/main/java/net/minecraftforge/fml/common/registry/FMLControlledNamespacedRegistry.java +++ b/src/main/java/net/minecraftforge/fml/common/registry/FMLControlledNamespacedRegistry.java @@ -121,7 +121,7 @@ public class FMLControlledNamespacedRegistry> e { try { - ReflectionHelper.findMethod(BitSet.class, this.availabilityMap, new String[]{"trimToSize"}).invoke(this.availabilityMap); + ReflectionHelper.findMethod(BitSet.class, "trimToSize", null).invoke(this.availabilityMap); } catch (Exception e) { diff --git a/src/main/java/net/minecraftforge/fml/relauncher/ReflectionHelper.java b/src/main/java/net/minecraftforge/fml/relauncher/ReflectionHelper.java index 4017160b31..0085625373 100644 --- a/src/main/java/net/minecraftforge/fml/relauncher/ReflectionHelper.java +++ b/src/main/java/net/minecraftforge/fml/relauncher/ReflectionHelper.java @@ -18,6 +18,11 @@ */ package net.minecraftforge.fml.relauncher; +import com.google.common.base.Preconditions; +import net.minecraft.launchwrapper.Launch; +import org.apache.commons.lang3.StringUtils; + +import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -40,6 +45,11 @@ public class ReflectionHelper //this.methodNames = methodNames; } + public UnableToFindMethodException(Throwable failed) + { + super(failed); + } + } public static class UnableToFindClassException extends RuntimeException @@ -170,10 +180,13 @@ public class ReflectionHelper throw new UnableToFindClassException(classNames, err); } - + /** + * @deprecated use {@link #findMethod(Class, String, String, Class[])} + */ + @Deprecated public static Method findMethod(Class clazz, E instance, String[] methodNames, Class... methodTypes) { - Exception failed = null; + Throwable failed = null; for (String methodName : methodNames) { try @@ -187,6 +200,47 @@ public class ReflectionHelper failed = e; } } - throw new UnableToFindMethodException(methodNames, failed); + throw new UnableToFindMethodException(failed); + } + + /** + * Finds a method with the specified name and parameters in the given class and makes it accessible. + * Note: for performance, store the returned value and avoid calling this repeatedly. + *

+ * Throws an exception if the method is not found. + * + * @param clazz The class to find the method on. + * @param methodName The name of the method to find (used in developer environments, i.e. "getWorldTime"). + * @param methodObfName The obfuscated name of the method to find (used in obfuscated environments, i.e. "func_72820_D"). + * If the name you are looking for is on a class that is never obfuscated, this should be null. + * @param parameterTypes The parameter types of the method to find. + * @return The method with the specified name and parameters in the given class. + */ + @Nonnull + public static Method findMethod(@Nonnull Class clazz, @Nonnull String methodName, @Nullable String methodObfName, Class... parameterTypes) + { + Preconditions.checkNotNull(clazz); + Preconditions.checkArgument(StringUtils.isNotEmpty(methodName), "Method name cannot be empty"); + + String nameToFind; + if (methodObfName == null || (Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment")) + { + nameToFind = methodName; + } + else + { + nameToFind = methodObfName; + } + + try + { + Method m = clazz.getDeclaredMethod(nameToFind, parameterTypes); + m.setAccessible(true); + return m; + } + catch (Exception e) + { + throw new UnableToFindMethodException(e); + } } }