/* * Minecraft Forge * Copyright (c) 2016. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation version 2.1 * of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ package net.minecraftforge.fml.common.asm.transformers; import java.lang.reflect.Modifier; import java.util.List; import net.minecraft.launchwrapper.IClassTransformer; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; import com.google.common.base.Predicate; import com.google.common.collect.Iterables; public class EventSubscriberTransformer implements IClassTransformer { @Override public byte[] transform(String name, String transformedName, byte[] basicClass) { if (basicClass == null) return null; ClassNode classNode = new ClassNode(); new ClassReader(basicClass).accept(classNode, 0); boolean isSubscriber = false; for (MethodNode methodNode : classNode.methods) { List<AnnotationNode> anns = methodNode.visibleAnnotations; if (anns != null && Iterables.any(anns, SubscribeEventPredicate.INSTANCE)) { if (Modifier.isPrivate(methodNode.access)) { String msg = "Cannot apply @SubscribeEvent to private method %s/%s%s"; throw new RuntimeException(String.format(msg, classNode.name, methodNode.name, methodNode.desc)); } methodNode.access = toPublic(methodNode.access); isSubscriber = true; } } if (isSubscriber) { classNode.access = toPublic(classNode.access); ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS); classNode.accept(writer); return writer.toByteArray(); } return basicClass; } private static int toPublic(int access) { return access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED) | Opcodes.ACC_PUBLIC; } private static class SubscribeEventPredicate implements Predicate<AnnotationNode> { static final SubscribeEventPredicate INSTANCE = new SubscribeEventPredicate(); @Override public boolean apply(AnnotationNode input) { return input.desc.equals("Lnet/minecraftforge/fml/common/eventhandler/SubscribeEvent;"); } } }