Remove dynamic lambda methods when inside of SideOnly methods

Clean signatures of ReflectionHelper methods.
This commit is contained in:
James Mitchell 2017-02-12 18:43:30 -06:00 committed by LexManos
parent 6745022d46
commit 8fbf4cf115
3 changed files with 126 additions and 4 deletions

View File

@ -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<MethodNode> 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<Handle> 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<Handle> dynamicLambdaHandles = new ArrayList<Handle>();
public LambdaGatherer() {
super(Opcodes.ASM5);
}
public void accept(MethodNode method) {
ListIterator<AbstractInsnNode> 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<Handle> getDynamicLambdaHandles()
{
return dynamicLambdaHandles;
}
}
}

View File

@ -121,7 +121,7 @@ public class FMLControlledNamespacedRegistry<I extends IForgeRegistryEntry<I>> 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)
{

View File

@ -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 <E> Method findMethod(Class<? super E> 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.
* <p>
* 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);
}
}
}