com.newrelic.weave.utils.WeaveUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of newrelic-weaver Show documentation
Show all versions of newrelic-weaver Show documentation
The Weaver of the Java agent.
/* * * * Copyright 2020 New Relic Corporation. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * */ package com.newrelic.weave.utils; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.newrelic.api.agent.weaver.MatchType; import com.newrelic.api.agent.weaver.Weave; import com.newrelic.api.agent.weaver.WeaveIntoAllMethods; import com.newrelic.api.agent.weaver.WeaveWithAnnotation; import com.newrelic.api.agent.weaver.Weaver; import com.newrelic.weave.MethodKey; import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.JSRInlinerAdapter; import org.objectweb.asm.commons.Method; import org.objectweb.asm.commons.Remapper; import org.objectweb.asm.tree.AbstractInsnNode; import org.objectweb.asm.tree.AnnotationNode; import org.objectweb.asm.tree.ClassNode; import org.objectweb.asm.tree.FieldNode; import org.objectweb.asm.tree.InnerClassNode; import org.objectweb.asm.tree.InsnList; import org.objectweb.asm.tree.LabelNode; import org.objectweb.asm.tree.MethodInsnNode; import org.objectweb.asm.tree.MethodNode; import org.objectweb.asm.tree.TypeInsnNode; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; /** * Weave utility methods. */ public final class WeaveUtils { /** * Version of ASM API used in the weaver. */ public static final int ASM_API_LEVEL = Opcodes.ASM9; public static final int CLASS_FILE_VERSION_OFFSET = 44; /** * No op remapper used when inlining methods. */ public static final Remapper NO_OP_REMAPPER = new Remapper() { }; /** * Constructor method name. */ public static final String INIT_NAME = "
. * * @param owner method owner * @param name method name * @param desc method desc * @return"; /** * Constructor method desc. */ public static final String INIT_DESC = "()V"; /** * Static class initializer method name. */ public static final String CLASS_INIT_NAME = " "; /** * Weaver internal ASM name. */ public static final Type WEAVER_TYPE = Type.getType(Weaver.class); /** * Weaver callOriginal() method. */ public static final Method CALL_ORIGINAL_METHOD = new Method("callOriginal", Type.getType(Object.class), new Type[0]); /** * Weaver T getClassAnnotation(Class annotationClass) method. */ public static final Method CLASS_ANNOTATION_GETTER = new Method("getClassAnnotation", Type.getType(Annotation.class), new Type[] { Type.getType(Class.class) }); /** * Weaver T getMethodAnnotation(Class annotationClass) method. */ public static final Method METHOD_ANNOTATION_GETTER = new Method("getMethodAnnotation", Type.getType(Annotation.class), new Type[] { Type.getType(Class.class) }); /** * The name of the annotation classes attribute on the {@link WeaveWithAnnotation} annotation. */ public static final String ANNOTATION_CLASSES_ATTRIBUTE_KEY = "annotationClasses"; /** * The name of the MatchType attribute on the {@link WeaveWithAnnotation} annotation. */ public static final String ANNOTATION_MATCH_TYPE_ATTRIBUTE_KEY = "type"; /** * {@link WeaveIntoAllMethods} annotation type */ public static final Type WEAVE_ALL_METHODS_TYPE = Type.getType(WeaveIntoAllMethods.class); /** * {@link WeaveWithAnnotation} annotation type */ public static final Type WEAVE_WITH_ANNOTATION_TYPE = Type.getType(WeaveWithAnnotation.class); /** * Static class initializer method. */ public static final Method CLASS_INIT_METHOD = new Method(CLASS_INIT_NAME, INIT_DESC); /** * Default constructor method. */ public static final Method DEFAULT_CONSTRUCTOR = new Method(INIT_NAME, INIT_DESC); /** * Object internal ASM name. */ public static final String JAVA_LANG_OBJECT_NAME = Type.getInternalName(Object.class); /** * Used to represent the bootstrap classloader. */ public static final ClassLoader BOOTSTRAP_PLACEHOLDER = new ClassLoader(null) { }; /** * Used to find synthetic accessor methods generated by the compiler. These are generated when inner classes * implicitly access parent class members. */ public static final Pattern SYNTHETIC_ACCESSOR_PATTERN = Pattern.compile("^access\\$[0-9]+$"); /** * We repalce Weaver.callOriginal() with an invoke of the weave method. We add this prefix to the weave class name * so the method owner+name will not collide with the original owner+name. This allows weaved code to make recursive * calls behave as expected. */ public static final String INLINER_PREFIX = "INLINER_"; /** * At runtime, this will contain the max class major version that the jvm can read. * * java 1.8 = 52
* java 1.7 = 51
* java 1.6 = 50
*/ public static final int RUNTIME_MAX_SUPPORTED_CLASS_VERSION = getRuntimeMaxSupportedClassVersion(); /** * This is the major class version that represents Java 1.6 */ public static final int JAVA_6_CLASS_VERSION = 50; public static final SetMETHODS_WE_NEVER_INSTRUMENT = ImmutableSet.of(new MethodKey("equals", "(Ljava/lang/Object;)Z"), new MethodKey("toString", "()Ljava/lang/String;"), new MethodKey("finalize", "()V"), new MethodKey("hashCode", "()I"), new MethodKey("clone", "()Ljava/lang/Object;")); /** * java.lang.Object was compiled with the max supported class version of the jvm we are running in. * * @return the class major version of java.lang.Object */ private static int getRuntimeMaxSupportedClassVersion() { try { double jvmSpecVersion = Double.parseDouble(System.getProperty("java.specification.version")); if (jvmSpecVersion >= 9) { return (int) jvmSpecVersion + CLASS_FILE_VERSION_OFFSET; } else if (jvmSpecVersion >= 1.8) { return 52; } else if (jvmSpecVersion == 1.7) { return 51; } else if (jvmSpecVersion == 1.6) { return 50; } else if (jvmSpecVersion == 1.5) { return 49; } else if (jvmSpecVersion == 1.4) { return 48; } else if (jvmSpecVersion == 1.3) { return 47; } else if (jvmSpecVersion == 1.2) { return 46; } else if (jvmSpecVersion <= 1.1) { return 45; } } catch (Throwable t) { } return 0; } private WeaveUtils() { } /** * Find a method by name and desc in the specified collection. * * @param methodNodes collection to search * @param queryNode MethodNode with name and desc to search for * @return matched method or null
if there was no match */ public static MethodNode findMatch(CollectionmethodNodes, MethodNode queryNode) { return findMatch(methodNodes, queryNode.name, queryNode.desc); } /** * Find a method by name and desc in the specified collection. * * @param methodNodes collection to search * @param queryMethod Method to search for * @return matched method or null
if there was no match */ public static MethodNode findMatch(CollectionmethodNodes, Method queryMethod) { return findMatch(methodNodes, queryMethod.getName(), queryMethod.getDescriptor()); } /** * Find a method by name and desc in the specified collection. * * @param methodNodes collection to search * @param name method name to search for * @param desc method desc to search for * @return matched method or null
if there was no match */ public static MethodNode findMatch(CollectionmethodNodes, String name, String desc) { for (MethodNode searchNode : methodNodes) { if (searchNode.name.equals(name) && searchNode.desc.equals(desc)) { return searchNode; } } return null; } /** * Find a field by name and desc in the specified collection. * * @param fieldNodes collection to search * @param name name to search for * @return matched field or null
if there was no match */ public static FieldNode findMatch(CollectionfieldNodes, String name) { for (FieldNode fieldNode : fieldNodes) { if (fieldNode.name.equals(name)) { return fieldNode; } } return null; } /** * Find a required field by name and desc in the specified collection. * * @param fieldNodes collection to search * @param name name to search for * @return matched field * @throws IllegalArgumentException if there was no match */ public static FieldNode findRequiredMatch(Collection fieldNodes, String name) { FieldNode result = findMatch(fieldNodes, name); if (result == null) { throw new IllegalArgumentException("Could not find required field name: " + name + " in specified collection."); } return result; } /** * Tests whether the specified method invocation is to code>Weaver.callOriginal() true
if owner, name, and desc correspond toWeaver.callOriginal()
*/ public static boolean isOriginalMethodInvocation(String owner, String name, String desc) { return owner.equals(WEAVER_TYPE.getInternalName()) && name.equals(CALL_ORIGINAL_METHOD.getName()) && desc.equals(CALL_ORIGINAL_METHOD.getDescriptor()); } /** * Tests whether the specified method invocation is to code>Weaver.getClassAnnotation(). * * @param owner method owner * @param name method name * @param desc method desc * @returntrue
if owner, name, and desc correspond toWeaver.getClassAnnotation()
*/ public static boolean isClassAnnotationGetter(String owner, String name, String desc) { return owner.equals(WEAVER_TYPE.getInternalName()) && name.equals(CLASS_ANNOTATION_GETTER.getName()) && desc.equals(CLASS_ANNOTATION_GETTER.getDescriptor()); } /** * Tests whether the specified method invocation is toWeaver.getMethodAnnotation()
. * * @param owner method owner * @param name method name * @param desc method desc * @returntrue
if owner, name, and desc correspond toWeaver.getMethodAnnotation()
*/ public static boolean isMethodAnnotationGetter(String owner, String name, String desc) { return owner.equals(WEAVER_TYPE.getInternalName()) && name.equals(METHOD_ANNOTATION_GETTER.getName()) && desc.equals(METHOD_ANNOTATION_GETTER.getDescriptor()); } /** * Tests whether the specified class node represents a nonstatic inner class. * * @param classNode class to test * @returntrue
if the specified class node represents a nonstatic inner class */ public static boolean isNonstaticInnerClass(ClassNode classNode) { if (classNode.innerClasses != null) { for (InnerClassNode innerClassNode : classNode.innerClasses) { if ((innerClassNode.access & Opcodes.ACC_STATIC) == 0 && innerClassNode.name.equals(classNode.name)) { return true; } } } return false; } /** * Tests whether the specified class node represents a anonymous inner class. * * @param innerClassNode class to test * @returntrue
if the specified class node represents a nonstatic inner class */ public static boolean isAnonymousInnerClass(InnerClassNode innerClassNode) { return innerClassNode.innerName == null; } /** * Tests whether the method name represents a synthetic accessor methods generated by the compiler. These are * generated when inner classes implicitly access parent class members. * * @param methodName method to test * @returntrue
if the name represents a synthetic accessor methods generated by the compiler */ public static boolean isSyntheticAccessor(String methodName) { return SYNTHETIC_ACCESSOR_PATTERN.matcher(methodName).matches(); } /** * Find the outer class type for the nested class. * * @param nestedClassNode class node representing a nested class * @return outer class type ornull
if it is not a nested class */ public static Type getOuterClassType(ClassNode nestedClassNode) { if (nestedClassNode.innerClasses != null) { for (InnerClassNode innerClassNode : nestedClassNode.innerClasses) { if (innerClassNode.name.equals(nestedClassNode.name)) { return Type.getType("L" + innerClassNode.outerName + ";"); } } } return null; } /** * Test if the specified method represents an "empty" (usually generated) constructor. * * @returntrue
if the specified method represents an "empty" constructor */ public static boolean isEmptyConstructor(MethodNode method) { if (!method.name.equals(INIT_NAME) || !method.desc.equals("()V") || method.visibleAnnotations != null || method.invisibleAnnotations != null) { return false; } final int init = 0; final int aload0 = 1; final int invokespecial = 2; int state = init; InsnList instructions = method.instructions; int size = instructions.size(); for (int i = 0; i < size; i++) { AbstractInsnNode node = instructions.get(i); switch (node.getOpcode()) { case Opcodes.ALOAD: if (state != init) { return false; } state = aload0; break; case Opcodes.INVOKESPECIAL: if (state != aload0) { return false; } state = invokespecial; break; case Opcodes.RETURN: return state == invokespecial; default: if (node.getType() == AbstractInsnNode.LINE || node.getType() == AbstractInsnNode.LABEL || node.getType() == AbstractInsnNode.FRAME) { break; } return false; } } return false; } /** * Create a new SynchronizedMethodNode with the access, name, desc, signature, and exceptions of the specified * MethodNode. * * @param source MethodNode to copy access, name, desc, signature, and exceptions from * @return new SynchronizedMethodNode with the access, name, desc, signature, and exceptions of the specified * MethodNode */ public static MethodNode newMethodNode(MethodNode source) { return new SynchronizedMethodNode(ASM_API_LEVEL, source.access, source.name, source.desc, source.signature, source.exceptions.toArray(new String[source.exceptions.size()])); } /** * Returns a copy of the specified ClassNode, stripping out all JSR/RET instructions. * * @param source ClassNode to copy */ public static ClassNode copyWithoutJSRInstructions(ClassNode source) { ClassNode result = new SynchronizedClassNode(ASM_API_LEVEL); source.accept(new ClassVisitor(ASM_API_LEVEL, result) { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); return new JSRInlinerAdapter(mv, access, name, desc, signature, exceptions); } }); return result; } /** * Returns a copy of the specified MethodNode. * * @param source MethodNode to copy */ public static MethodNode copy(MethodNode source) { MethodNode result = newMethodNode(source); source.accept(result); return result; } /** * Returns a copy of the specified ClassNode with all references to the "oldName" parameter renamed to the value * of the "newName" parameter. * * This method is best used when a template ClassNode is available and you want to make a new copy of it every time * it needs to be used so it can have a unique name. * * @param source ClassNode to copy and rename */ public static ClassNode copyAndRename(ClassNode source, final String oldName, final String newName) { ClassNode result = new SynchronizedClassNode(ASM_API_LEVEL); source.accept(new ClassVisitor(ASM_API_LEVEL, result) { @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { // Rename the Class super.visit(version, access, name.equals(oldName) ? newName : name, signature, superName, interfaces); } @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor methodVisitor = super.visitMethod(access, name, desc, signature, exceptions); return new MethodVisitor(ASM_API_LEVEL, methodVisitor) { @Override public void visitFieldInsn(int opcode, String owner, String name, String desc) { // Rename any Field instructions super.visitFieldInsn(opcode, owner.equals(oldName) ? newName : owner, name, desc); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { // Rename any Method instructions super.visitMethodInsn(opcode, owner.equals(oldName) ? newName : owner, name, desc, itf); } @Override public void visitLdcInsn(Object cst) { if (cst instanceof Type) { if (oldName.equals(((Type) cst).getInternalName())) { super.visitLdcInsn(Type.getType("L" + newName + ";")); return; } } super.visitLdcInsn(cst); } @Override public void visitTypeInsn(int opcode, String type) { if (oldName.equals(type)) { super.visitTypeInsn(opcode, newName); return; } super.visitTypeInsn(opcode, type); } }; } }); return result; } /** * Read an array of class bytes into an ASM ClassNode * * @param classBytes classBytes from a .class file * @return ASM ClassNode representation of the */ public static ClassNode convertToClassNode(byte[] classBytes) { ClassReader reader = new ClassReader(classBytes); ClassNode result = new SynchronizedClassNode(ASM_API_LEVEL); reader.accept(result, ClassReader.SKIP_FRAMES); return result; } /** * Converts an ASM {@link ClassNode} to a byte array. * * @param classNode class node to convert * @param classInfoFinder the classloader used to create the {@link PatchedClassWriter} * @return byte array representing the specified class node */ public static byte[] convertToClassBytes(ClassNode classNode, ClassInformationFinder classInfoFinder) { ClassWriter cw = new PatchedClassWriter(ClassWriter.COMPUTE_FRAMES, classInfoFinder); classNode.accept(cw); return cw.toByteArray(); } /** * Retrieve the class bytes from the specified {@link ClassFinder}, ornull
if the class could not be * found. * * @param classname internal class name * @param finder {@link ClassFinder} implementation to get bytes from * @return class bytes, ornull
if the class could not be found * @throws IOException */ public static byte[] getClassBytesFromClassFinder(String classname, ClassFinder finder) throws IOException { URL location = finder.findResource(classname); if (null == location) { return null; // no such resource } InputStream is = location.openStream(); return Streams.read(is, true); } /** * Read a ClassLoader's resource into a byte array. * * @param classname Internal or Fully qualified name of the class * @param classloader the classloader to read the resource from * @return the resource bytes (class bytes) or null if no resource was found. * @throws IOException */ public static byte[] getClassBytesFromClassLoaderResource(String classname, ClassLoader classloader) throws IOException { ClassFinder cf = new ClassLoaderFinder(classloader); return WeaveUtils.getClassBytesFromClassFinder(classname, cf); } /** * Check if the access flags indicate a private or protected access level. * * @param access ASM access flags * @returntrue
if flags indicate a private or protected access level */ public static boolean isPrivateOrProtected(int access) { return (access & Opcodes.ACC_PROTECTED) != 0 || (access & Opcodes.ACC_PRIVATE) != 0; } /** * Creates a new {@link LabelNode} which can be safely inserted into a {@link MethodNode} instruction list. * * @return a new LabelNode. */ public static LabelNode makeLabelNode() { LabelNode labelNode = new LabelNode(); labelNode.getLabel().info = labelNode; return labelNode; } /** * Pull a method node out of a class node, or null if there is no such method in the class * * @return method node matching the specified name and descriptor, ornull
if it does not exist */ public static MethodNode getMethodNode(ClassNode classnode, String methodName, String methodDesc) { for (MethodNode method : classnode.methods) { if (method.name.equals(methodName) && method.desc.equals(methodDesc)) { return method; } } return null; } /** * Returns true if both access params have the same value of all given opcode flags. * * @param access1 access flags. * @param access2 access flags. * @param opcodes flags to check. See {@link org.objectweb.asm.Opcodes} * @return True if access1 and access2 have the same value for all opcodes. * @see Opcodes */ public static boolean flagsMatch(int access1, int access2, int... opcodes) { for (int opcode : opcodes) { if (((opcode & access1) != 0) != ((opcode & access2) != 0)) { return false; } } return true; } // @formatter:off private static final MapHUMAN_READABLE_OPCODES = ImmutableMap. builder() .put(Opcodes.ACC_PUBLIC, "public") .put(Opcodes.ACC_PRIVATE, "private") .put(Opcodes.ACC_PROTECTED, "protected") .put(Opcodes.ACC_STATIC, "static") .put(Opcodes.ACC_FINAL, "final") .put(Opcodes.ACC_SYNCHRONIZED, "(synchronized|super)") .put(Opcodes.ACC_BRIDGE, "(bridge|volatile)") .put(Opcodes.ACC_NATIVE, "native") .put(Opcodes.ACC_INTERFACE, "interface") .put(Opcodes.ACC_ABSTRACT, "abstract") .put(Opcodes.ACC_STRICT, "strict") .put(Opcodes.ACC_SYNTHETIC, "synthetic") .put(Opcodes.ACC_ANNOTATION, "annotation") .put(Opcodes.ACC_ENUM, "enum") .build(); // @formatter:on /** * Passes the sum of the opcodes array to {@link WeaveUtils#humanReadableAccessFlags(int)} */ public static String humanReadableAccessFlags(int[] opcodes) { int sum = 0; for (int opcode : opcodes) { sum += opcode; } return humanReadableAccessFlags(sum); } /** * Returns a human-readable comma-separated description of the asm access flags.
* Any ints that don't map to an asm opcode flag will retain their int value. * * @param opcodes ASM access flags to check */ public static String humanReadableAccessFlags(int opcodes) { String humanReadableFlags = ""; for (int supportedOpcode : HUMAN_READABLE_OPCODES.keySet()) { if (flagsMatch(supportedOpcode, opcodes, supportedOpcode)) { if (humanReadableFlags.length() > 0) { humanReadableFlags += ","; } humanReadableFlags += HUMAN_READABLE_OPCODES.get(supportedOpcode); opcodes = opcodes ^ supportedOpcode; // remove the suported flag from the opcodes } } if (opcodes != 0) { // unknown flags. Just append the remaining int value if (humanReadableFlags.length() > 0) { humanReadableFlags += ","; } humanReadableFlags += "UnknownRemaingFlags=" + opcodes; } return humanReadableFlags; } /** * Returns true if the class being read by the {@link ClassReader} is marked with a {@link Weave} annotation. */ public static boolean isWeavedClass(ClassReader reader) { final boolean[] weaved = new boolean[] { false }; reader.accept(new ClassVisitor(WeaveUtils.ASM_API_LEVEL) { @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { if (Type.getDescriptor(Weave.class).equals(desc)) { weaved[0] = true; } return null; } }, ClassReader.SKIP_CODE); return weaved[0]; } /** * Update class versions to the max supported runtime class version. */ public static void updateClassVersion(ClassNode node) { if (node.version < RUNTIME_MAX_SUPPORTED_CLASS_VERSION) { node.version = RUNTIME_MAX_SUPPORTED_CLASS_VERSION; } } /** * java.lang.Class -> java/lang/Class.class */ public static String getClassResourceName(String binaryName) { if (binaryName.endsWith(".class")) { return binaryName; } else { return binaryName.replace('.', '/') + ".class"; } } /** * java.lang.Class -> java/lang/Class */ public static String getClassInternalName(String binaryName) { return binaryName.replaceFirst("\\.class$", "").replace('.', '/'); } /** * java/lang/Class.class -> java.lang.Class */ public static String getClassBinaryName(String resourceName) { return resourceName.replaceFirst("\\.class$", "").replace('/', '.'); } public static boolean isConstructor(String methodName) { return INIT_NAME.equals(methodName); } public static boolean isMain(int access, String methodName, String methodDesc) { return (access & Opcodes.ACC_PUBLIC) == 1 && (access & Opcodes.ACC_STATIC) == 8 && "main".equals(methodName) && "([Ljava/lang/String;)V".equals(methodDesc); } public static boolean isStaticInitializer(String methodName) { return CLASS_INIT_NAME.equals(methodName); } public static MethodInsnNode getUnboxingInstruction(Type returnType) { if (Type.INT_TYPE.equals(returnType)) { return new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Integer.class), "intValue", "()I", false); } else if (Type.BOOLEAN_TYPE.equals(returnType)) { return new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Boolean.class), "booleanValue", "()Z", false); } else if (Type.CHAR_TYPE.equals(returnType)) { return new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Character.class), "charValue", "()C", false); } else if (Type.BYTE_TYPE.equals(returnType)) { return new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Byte.class), "byteValue", "()B", false); } else if (Type.SHORT_TYPE.equals(returnType)) { return new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Short.class), "shortValue", "()S", false); } else if (Type.FLOAT_TYPE.equals(returnType)) { return new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Float.class), "floatValue", "()F", false); } else if (Type.LONG_TYPE.equals(returnType)) { return new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Long.class), "longValue", "()J", false); } else if (Type.DOUBLE_TYPE.equals(returnType)) { return new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Double.class), "doubleValue", "()D", false); } return null; } public static String getClassInternalName(Type type) { if (Type.INT_TYPE.equals(type)) { return Type.getInternalName(Integer.class); } else if (Type.BOOLEAN_TYPE.equals(type)) { return Type.getInternalName(Boolean.class); } else if (Type.CHAR_TYPE.equals(type)) { return Type.getInternalName(Character.class); } else if (Type.BYTE_TYPE.equals(type)) { return Type.getInternalName(Byte.class); } else if (Type.SHORT_TYPE.equals(type)) { return Type.getInternalName(Short.class); } else if (Type.FLOAT_TYPE.equals(type)) { return Type.getInternalName(Float.class); } else if (Type.LONG_TYPE.equals(type)) { return Type.getInternalName(Long.class); } else if (Type.DOUBLE_TYPE.equals(type)) { return Type.getInternalName(Double.class); } else { return type.getInternalName(); } } public static int getReturnOpcodeForReturnType(Type returnType) { if (Type.INT_TYPE.equals(returnType)) { return Opcodes.IRETURN; } else if (Type.BOOLEAN_TYPE.equals(returnType)) { return Opcodes.IRETURN; } else if (Type.CHAR_TYPE.equals(returnType)) { return Opcodes.IRETURN; } else if (Type.BYTE_TYPE.equals(returnType)) { return Opcodes.IRETURN; } else if (Type.SHORT_TYPE.equals(returnType)) { return Opcodes.IRETURN; } else if (Type.FLOAT_TYPE.equals(returnType)) { return Opcodes.FRETURN; } else if (Type.LONG_TYPE.equals(returnType)) { return Opcodes.LRETURN; } else if (Type.DOUBLE_TYPE.equals(returnType)) { return Opcodes.DRETURN; } else if (Type.VOID_TYPE.equals(returnType)) { return Opcodes.RETURN; } return Opcodes.ARETURN; } public static AbstractInsnNode getCheckCastInstruction(String classInternalName) { if (Type.getInternalName(Object.class).equals(classInternalName)) { // Don't need a CHECKCAST instruction return null; } return new TypeInsnNode(Opcodes.CHECKCAST, classInternalName); } /** * Returns a list of all the LabelNode instructions. * * @param instructions the instructions list to look through * @return List of LabelNode instructions found */ public static ListfindLabels(InsnList instructions) { List labels = new ArrayList<>(); AbstractInsnNode instructionNode = instructions.getFirst(); while (instructionNode != null) { if (instructionNode instanceof LabelNode) { labels.add((LabelNode) instructionNode); } instructionNode = instructionNode.getNext(); } return labels; } /** * Returns a list of all annotations found on the provided method * * @param method the method to look at for annotations * @return all annotations present on the provided method */ public static List getMethodAnnotations(MethodNode method) { List annotations = new ArrayList<>(); if (method.invisibleAnnotations != null) { annotations.addAll(method.invisibleAnnotations); } if (method.visibleAnnotations != null) { annotations.addAll(method.visibleAnnotations); } return annotations; } /** * Returns a list of all annotations found on the provided class * * @param classNode the class to look at for annotations * @return all annotations present on the provided class */ public static List getClassAnnotations(ClassNode classNode) { List annotations = new ArrayList<>(); if (classNode.invisibleAnnotations != null) { annotations.addAll(classNode.invisibleAnnotations); } if (classNode.visibleAnnotations != null) { annotations.addAll(classNode.visibleAnnotations); } return annotations; } /** * Returns a list of all annotations found on all methods of the provided class * * @param classNode the class to look at for annotations * @return all annotations present on all methods of the provided class */ public static List getMethodAnnotations(ClassNode classNode) { List annotations = new ArrayList<>(); for (MethodNode methodNode : classNode.methods) { if (methodNode.invisibleAnnotations != null) { annotations.addAll(methodNode.invisibleAnnotations); } if (methodNode.visibleAnnotations != null) { annotations.addAll(methodNode.visibleAnnotations); } } return annotations; } /** * Searches for {@link WeaveWithAnnotation#annotationClasses()} in a method's annotations. * * @param annotations annotations to search through. */ public static Set getMethodRequiredAnnotations(List annotations) { if (annotations == null) { return Collections.emptySet(); } Set result = new HashSet<>(); for (AnnotationNode visibleAnnotation : annotations) { if (WEAVE_WITH_ANNOTATION_TYPE.getDescriptor().equals(visibleAnnotation.desc)) { List
© 2015 - 2024 Weber Informatics LLC | Privacy Policy