Java tutorial
/******************************************************************************************************************* * Authors: SanAndreasP * Copyright: SanAndreasP * License: Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International * http://creativecommons.org/licenses/by-nc-sa/4.0/ *******************************************************************************************************************/ package de.sanandrew.core.manpack.transformer; import de.sanandrew.core.manpack.init.ManPackLoadingPlugin; import net.minecraft.launchwrapper.IClassTransformer; import org.apache.logging.log4j.Level; import org.objectweb.asm.Opcodes; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.MethodNode; import java.util.regex.Matcher; public class AnnotationChecker implements IClassTransformer { @Override public byte[] transform(String name, String transformedName, byte[] bytes) { ClassNode cn = ASMHelper.createClassNode(bytes); for (MethodNode method : cn.methods) { if (method.visibleAnnotations != null) { for (AnnotationNode annotation : method.visibleAnnotations) { if (annotation.desc.equals("Lde/sanandrew/core/manpack/util/annotation/ASMOverride;")) { String asmMethodName = annotation.values.get(1).toString(); MethodNode asmMethod = getSignature(asmMethodName); if (!method.name.equals(asmMethod.name)) { String err = "Attempting to override Method %s in Class %s with incompatible name %s!"; ManPackLoadingPlugin.MOD_LOG.log(Level.FATAL, String.format(err, asmMethod.name, cn.name, method.name)); throw new OverrideException(String.format("Method name %s is not equal to %s!", method.name, asmMethod.name)); } else if (!method.desc.equals(asmMethod.desc)) { String err = "Attempting to override Method %s, description %s, in Class %s with incompatible description %s!"; ManPackLoadingPlugin.MOD_LOG.log(Level.FATAL, String.format(err, asmMethod.name, asmMethod.desc, cn.name, method.desc)); throw new OverrideException(String.format("Method desc %s is not equal to %s!", method.desc, asmMethod.desc)); } else if (getAccessLevelInt(method.access) < getAccessLevelInt(asmMethod.access)) { String err = "Attempting to assign weaker access privileges ('%s') on %s in %s; should be '%s'"; ManPackLoadingPlugin.MOD_LOG.log(Level.FATAL, String.format(err, getAccessLevelName(method.access), asmMethod.name, cn.name, getAccessLevelName(asmMethod.access))); throw new OverrideException(String.format("Access level %s is weaker than %s!", getAccessLevelName(method.access), getAccessLevelName(asmMethod.access))); } else if (getAccessLevelInt(asmMethod.access) == 1) { String classPkg = cn.name.substring(0, cn.name.lastIndexOf('/')); String ownerPkg = ASMHelper.getMethodInsnNode(asmMethod.access, asmMethodName, false).owner.substring(0, cn.name.lastIndexOf('/')); if (!classPkg.equals(ownerPkg)) { String err = "Attempting to override packageLocal method %s outside of package %s in class %s, which is in package %s!"; ManPackLoadingPlugin.MOD_LOG.log(Level.FATAL, String.format(err, method.name, ownerPkg, cn.name, classPkg)); throw new OverrideException(String.format( "Class %s lies outside package %s for method %s to be overridden", cn.name, ownerPkg, classPkg)); } } else if (checkBitwiseEqual(asmMethod.access, Opcodes.ACC_DEPRECATED)) { ManPackLoadingPlugin.MOD_LOG.log(Level.WARN, String.format( "The Method %s is marked as deprecated! It is most likely that the method is " + "not injected in any superclass anymore! Thus this may not be called!", asmMethod.name)); } } } } } return bytes; } private static MethodNode getSignature(String method) { String split[] = method.split(" "); int accLvl = getAccessLevelOpcode(split[0]); Matcher mtch = ASMNames.OWNERNAME.matcher(split[1]); if (!mtch.find()) { throw new RuntimeException("SAP-Method signature invalid!"); } String name = mtch.group(2); String desc = split[2]; String sig = split.length > 3 ? split[3] : null; String throwing[] = split.length > 4 ? split[4].split(";") : null; return new MethodNode(accLvl, name, desc, sig, throwing); } private static int getAccessLevelInt(int access) { if (checkBitwiseEqual(access, Opcodes.ACC_PRIVATE)) { return 0; } else if (checkBitwiseEqual(access, Opcodes.ACC_PROTECTED)) { return 2; } else if (checkBitwiseEqual(access, Opcodes.ACC_PUBLIC)) { return 3; } else { // if ACC_PACKAGE_LOCAL return 1; } } private static String getAccessLevelName(int access) { if (checkBitwiseEqual(access, Opcodes.ACC_PRIVATE)) { return "private"; } else if (checkBitwiseEqual(access, Opcodes.ACC_PROTECTED)) { return "protected"; } else if (checkBitwiseEqual(access, Opcodes.ACC_PUBLIC)) { return "public"; } else { // if ACC_PACKAGE_LOCAL return "packageLocal"; } } private static int getAccessLevelOpcode(String accessName) { String[] accessSplit = accessName.split(";"); int ret = 0; for (String acc : accessSplit) { switch (acc) { case "private": ret |= Opcodes.ACC_PRIVATE; break; case "protected": ret |= Opcodes.ACC_PROTECTED; break; case "public": ret |= Opcodes.ACC_PUBLIC; break; case "deprecated": ret |= Opcodes.ACC_DEPRECATED; break; } } return ret; } private static boolean checkBitwiseEqual(int value, int flag) { return (value & flag) == flag; } public static class OverrideException extends RuntimeException { private static final long serialVersionUID = 7395488467026629355L; public OverrideException(String message) { super(message); } } }