All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.taobao.arthas.bytekit.utils.AsmUtils Maven / Gradle / Ivy

The newest version!
package com.taobao.arthas.bytekit.utils;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import com.alibaba.arthas.deps.org.objectweb.asm.ClassReader;
import com.alibaba.arthas.deps.org.objectweb.asm.ClassVisitor;
import com.alibaba.arthas.deps.org.objectweb.asm.ClassWriter;
import com.alibaba.arthas.deps.org.objectweb.asm.Label;
import com.alibaba.arthas.deps.org.objectweb.asm.MethodVisitor;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.commons.ClassRemapper;
import com.alibaba.arthas.deps.org.objectweb.asm.commons.JSRInlinerAdapter;
import com.alibaba.arthas.deps.org.objectweb.asm.commons.Remapper;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.ClassNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.FieldNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LocalVariableNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.TypeInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.util.ASMifier;
import com.alibaba.arthas.deps.org.objectweb.asm.util.TraceClassVisitor;
import com.taobao.arthas.bytekit.asm.ClassLoaderAwareClassWriter;

/**
 * 
 * @author hengyunabc
 *
 */
public class AsmUtils {

	public static ClassNode loadClass(Class clazz) throws IOException {
		String resource = clazz.getName().replace('.', '/') + ".class";
		InputStream is = clazz.getClassLoader().getResourceAsStream(resource);
		ClassReader cr = new ClassReader(is);
		ClassNode classNode = new ClassNode();
		cr.accept(classNode, ClassReader.SKIP_FRAMES);
		return classNode;
	}

	public static ClassNode toClassNode(byte[] classBytes) {
		ClassReader reader = new ClassReader(classBytes);
		ClassNode result = new ClassNode(Opcodes.ASM9);
		reader.accept(result, ClassReader.SKIP_FRAMES);
		return result;
	}

	public static ClassReader toClassNode(byte[] classBytes, ClassNode classNode) {
		ClassReader reader = new ClassReader(classBytes);
		reader.accept(classNode, ClassReader.SKIP_FRAMES);
		return reader;
	}

	/**
	 * Generate class bytes from class node.
	 * 
* NOTE: must pass origin classReader for bytecode optimizations, avoiding JVM metaspace OOM. * @param classNode * @param classLoader * @param classReader origin class reader * @return */ public static byte[] toBytes(ClassNode classNode, ClassLoader classLoader, ClassReader classReader) { int flags = ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS; ClassWriter writer = new ClassLoaderAwareClassWriter(classReader, flags, classLoader); classNode.accept(writer); return writer.toByteArray(); } public static byte[] toBytes(ClassNode classNode) { ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); classNode.accept(writer); return writer.toByteArray(); } public static byte[] renameClass(byte[] classBytes, final String newClassName) { final String internalName = newClassName.replace('.', '/'); ClassReader reader = new ClassReader(classBytes); ClassWriter writer = new ClassWriter(0); class RenameRemapper extends Remapper { private String className; @Override public String map(String typeName) { if (typeName.equals(className)) { return internalName; } return super.map(typeName); } public void setClassName(String className) { this.className = className; } } final RenameRemapper renameRemapper = new RenameRemapper(); ClassRemapper adapter = new ClassRemapper(writer, renameRemapper) { @Override public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { renameRemapper.setClassName(name); super.visit(version, access, name, signature, superName, interfaces); } }; reader.accept(adapter, ClassReader.EXPAND_FRAMES); writer.visitEnd(); return writer.toByteArray(); } public static void replaceMethod(ClassNode classNode, MethodNode methodNode) { for (int index = 0; index < classNode.methods.size(); ++index) { MethodNode tmp = classNode.methods.get(index); if (tmp.name.equals(methodNode.name) && tmp.desc.equals(methodNode.desc)) { classNode.methods.set(index, methodNode); } } } public static String toASMCode(byte[] bytecode) throws IOException { return toASMCode(bytecode, true); } public static String toASMCode(byte[] bytecode, boolean debug) throws IOException { int flags = ClassReader.SKIP_DEBUG; if (debug) { flags = 0; } ClassReader cr = new ClassReader(new ByteArrayInputStream(bytecode)); StringWriter sw = new StringWriter(); cr.accept(new TraceClassVisitor(null, new ASMifier(), new PrintWriter(sw)), flags); return sw.toString(); } public static String toASMCode(ClassNode classNode) { StringWriter sw = new StringWriter(); classNode.accept(new TraceClassVisitor(null, new ASMifier(), new PrintWriter(sw))); return sw.toString(); } public static String toASMCode(MethodNode methodNode) { ClassNode classNode = new ClassNode(); classNode.methods.add(methodNode); return toASMCode(classNode); } public static MethodNode newMethodNode(MethodNode source) { return new MethodNode(Opcodes.ASM9, source.access, source.name, source.desc, source.signature, source.exceptions.toArray(new String[source.exceptions.size()])); } public static MethodNode removeJSRInstructions(MethodNode subjectMethod) { MethodNode result = newMethodNode(subjectMethod); subjectMethod.accept(new JSRInlinerAdapter(result, subjectMethod.access, subjectMethod.name, subjectMethod.desc, subjectMethod.signature, subjectMethod.exceptions.toArray(new String[subjectMethod.exceptions.size()]))); return result; } public static ClassNode removeJSRInstructions(ClassNode classNode) { ClassNode result = new ClassNode(Opcodes.ASM9); classNode.accept(new ClassVisitor(Opcodes.ASM9, 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; } public static MethodNode removeLineNumbers(MethodNode methodNode) { MethodNode result = newMethodNode(methodNode); methodNode.accept(new MethodVisitor(Opcodes.ASM9, result) { public void visitLineNumber(int line, Label start) { } }); return result; } public static MethodNode findFirstMethod(Collection methodNodes, String name) { for (MethodNode methodNode : methodNodes) { if (methodNode.name.equals(name)) { return methodNode; } } return null; } public static List findMethods(Collection methodNodes, String name) { List result = new ArrayList(); for (MethodNode methodNode : methodNodes) { if (methodNode.name.equals(name)) { result.add(methodNode); } } return result; } public static MethodNode findMethod(Collection methodNodes, MethodNode target) { return findMethod(methodNodes, target.name, target.desc); } public static MethodNode findMethod(Collection methodNodes, String name, String desc) { for (MethodNode methodNode : methodNodes) { if (methodNode.name.equals(name) && methodNode.desc.equals(desc)) { return methodNode; } } return null; } public static AbstractInsnNode findInitConstructorInstruction(MethodNode methodNode) { int nested = 0; for (AbstractInsnNode insnNode = methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode .getNext()) { if (insnNode instanceof TypeInsnNode) { if (insnNode.getOpcode() == Opcodes.NEW) { // new object(). nested++; } } else if (insnNode instanceof MethodInsnNode) { final MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; if (methodInsnNode.getOpcode() == Opcodes.INVOKESPECIAL && methodInsnNode.name.equals("")) { if (--nested < 0) { // find this() or super(). return insnNode.getNext(); } } } } return null; } public static List findMethodInsnNodeWithPrefix(MethodNode methodNode, String prefix) { List result = new ArrayList(); for (AbstractInsnNode insnNode = methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode .getNext()) { if (insnNode instanceof MethodInsnNode) { final MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; if(methodInsnNode.name.startsWith(prefix)) { result.add(methodInsnNode); } } } return result; } public static List findMethodInsnNode(MethodNode methodNode, String owner, String name) { List result = new ArrayList(); for (AbstractInsnNode insnNode = methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode .getNext()) { if (insnNode instanceof MethodInsnNode) { final MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; if (methodInsnNode.owner.equals(owner) && methodInsnNode.name.equals(name)) { result.add(methodInsnNode); } } } return result; } public static List findMethodInsnNode(MethodNode methodNode, String owner, String name, String desc) { List result = new ArrayList(); for (AbstractInsnNode insnNode = methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode .getNext()) { if (insnNode instanceof MethodInsnNode) { final MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; if (methodInsnNode.owner.equals(owner) && methodInsnNode.name.equals(name) && methodInsnNode.desc.equals(desc)) { result.add(methodInsnNode); } } } return result; } public static boolean containsMethodInsnNode(MethodNode methodNode, String owner, String name) { for (AbstractInsnNode insnNode = methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode .getNext()) { if (insnNode instanceof MethodInsnNode) { final MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode; if (methodInsnNode.owner.equals(owner) && methodInsnNode.name.equals(name)) { return true; } } } return false; } public static boolean isStatic(MethodNode methodNode) { return (methodNode.access & Opcodes.ACC_STATIC) != 0; } public static boolean isStatic(MethodInsnNode methodInsnNode) { return methodInsnNode.getOpcode() == Opcodes.INVOKESTATIC; } public static boolean isConstructor(MethodNode methodNode) { return methodNode.name != null && methodNode.name.equals(""); } public String[] getParameterNames(MethodNode methodNode) { Type[] argumentTypes = Type.getArgumentTypes(methodNode.desc); if (argumentTypes.length == 0) { return new String[0]; } final List localVariableNodes = methodNode.localVariables; int localVariableStartIndex = 1; if (isStatic(methodNode)) { // static method is none this. localVariableStartIndex = 0; } if (localVariableNodes == null || localVariableNodes.size() <= localVariableStartIndex || (argumentTypes.length + localVariableStartIndex) > localVariableNodes.size()) { // make simple argument names. final String[] names = new String[argumentTypes.length]; for (int i = 0; i < argumentTypes.length; i++) { final String className = argumentTypes[i].getClassName(); if (className != null) { final int findIndex = className.lastIndexOf('.'); if (findIndex == -1) { names[i] = className; } else { names[i] = className.substring(findIndex + 1); } } else { names[i] = argumentTypes[i].getDescriptor(); } } return names; } // sort by index. Collections.sort(localVariableNodes, new Comparator() { @Override public int compare(LocalVariableNode o1, LocalVariableNode o2) { return o1.index - o2.index; } }); String[] names = new String[argumentTypes.length]; for (int i = 0; i < argumentTypes.length; i++) { final String name = localVariableNodes.get(localVariableStartIndex++).name; if (name != null) { names[i] = name; } else { names[i] = ""; } } return names; } public static MethodNode copy(MethodNode source) { MethodNode result = newMethodNode(source); source.accept(result); return result; } public static ClassNode copy(ClassNode source) { ClassNode result = new ClassNode(Opcodes.ASM9); source.accept(new ClassVisitor(Opcodes.ASM9, 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; } public static String methodDeclaration(MethodInsnNode methodInsnNode) { StringBuilder sb = new StringBuilder(128); int opcode = methodInsnNode.getOpcode(); if(opcode == Opcodes.INVOKESTATIC) { sb.append("static "); } Type methodType = Type.getMethodType(methodInsnNode.desc); Type ownerType = Type.getObjectType(methodInsnNode.owner); //skip constructor return type if(methodInsnNode.name.equals("")) { sb.append(ownerType.getClassName()); }else { sb.append(methodType.getReturnType().getClassName()).append(' '); sb.append(methodInsnNode.name); } sb.append('('); Type[] argumentTypes = methodType.getArgumentTypes(); for(int i = 0 ; i < argumentTypes.length; ++i) { sb.append(argumentTypes[i].getClassName()); if(i != argumentTypes.length - 1) { sb.append(", "); } } sb.append(')'); return sb.toString(); } public static String methodDeclaration(Type owner, MethodNode methodNode) { int access = methodNode.access; StringBuilder sb = new StringBuilder(128); // int ACC_PUBLIC = 0x0001; // class, field, method // int ACC_PRIVATE = 0x0002; // class, field, method // int ACC_PROTECTED = 0x0004; // class, field, method // int ACC_STATIC = 0x0008; // field, method // int ACC_FINAL = 0x0010; // class, field, method, parameter // int ACC_SUPER = 0x0020; // class // int ACC_SYNCHRONIZED = 0x0020; // method // int ACC_OPEN = 0x0020; // module // int ACC_TRANSITIVE = 0x0020; // module requires // int ACC_VOLATILE = 0x0040; // field // int ACC_BRIDGE = 0x0040; // method // int ACC_STATIC_PHASE = 0x0040; // module requires // int ACC_VARARGS = 0x0080; // method // int ACC_TRANSIENT = 0x0080; // field // int ACC_NATIVE = 0x0100; // method // int ACC_INTERFACE = 0x0200; // class // int ACC_ABSTRACT = 0x0400; // class, method // int ACC_STRICT = 0x0800; // method // int ACC_SYNTHETIC = 0x1000; // class, field, method, parameter, module * // int ACC_ANNOTATION = 0x2000; // class // int ACC_ENUM = 0x4000; // class(?) field inner // int ACC_MANDATED = 0x8000; // parameter, module, module * // int ACC_MODULE = 0x8000; // class if((access & Opcodes.ACC_PUBLIC) != 0) { sb.append("public "); } if((access & Opcodes.ACC_PRIVATE) != 0) { sb.append("private "); } if((access & Opcodes.ACC_PROTECTED) != 0) { sb.append("protected "); } if((access & Opcodes.ACC_STATIC) != 0) { sb.append("static "); } if((access & Opcodes.ACC_FINAL) != 0) { sb.append("final "); } if((access & Opcodes.ACC_SYNCHRONIZED) != 0) { sb.append("synchronized "); } if((access & Opcodes.ACC_NATIVE) != 0) { sb.append("native "); } if((access & Opcodes.ACC_ABSTRACT) != 0) { sb.append("abstract "); } Type methodType = Type.getMethodType(methodNode.desc); //skip constructor return type if(methodNode.name.equals("")) { sb.append(owner.getClassName()); }else { sb.append(methodType.getReturnType().getClassName()).append(' '); sb.append(methodNode.name); } sb.append('('); Type[] argumentTypes = methodType.getArgumentTypes(); for(int i = 0 ; i < argumentTypes.length; ++i) { sb.append(argumentTypes[i].getClassName()); if(i != argumentTypes.length - 1) { sb.append(", "); } } sb.append(')'); if(methodNode.exceptions != null) { int exceptionSize = methodNode.exceptions.size(); if( exceptionSize > 0) { sb.append(" throws"); for(int i = 0; i < exceptionSize; ++i) { sb.append(' '); sb.append(Type.getObjectType(methodNode.exceptions.get(i)).getClassName()); if(i != exceptionSize -1) { sb.append(','); } } } } return sb.toString(); } public static FieldNode findField(List fields, String name) { for(FieldNode field : fields) { if(field.name.equals(name)) { return field; } } return null; } public static void addField(ClassNode classNode, FieldNode fieldNode) { // TODO 检查是否有重复? classNode.fields.add(fieldNode); } public static void addMethod(ClassNode classNode, MethodNode methodNode) { classNode.methods.add(methodNode); } // TODO 是否真的 unique 了? public static String uniqueNameForMethod(String className, String methodName, String desc) { StringBuilder result = new StringBuilder(128); result.append(cleanClassName(className)).append('_').append(methodName); for(Type arg : Type.getMethodType(desc).getArgumentTypes()) { result.append('_').append(cleanClassName(arg.getClassName())); } return result.toString(); } private static String cleanClassName(String className) { char[] charArray = className.toCharArray(); int length = charArray.length; for(int i = 0 ; i < length; ++i) { switch( charArray[i]) { case '[' : case ']' : case '<' : case '>' : case ';' : case '/' : case '.' : charArray[i] = '_'; break; } } return new String(charArray); } /** * Java ClassFile versions (the minor version is stored in the 16 most * significant bits, and the major version in the 16 least significant bits). * @see com.alibaba.arthas.deps.org.objectweb.asm.Opcodes#V_PREVIEW * @param version * @return */ public static int getMajorVersion(int version) { return 0x0000FFFF & version; } /** * 替换掉完整 version里的 major version * * @param version * @param majorVersion * @return */ public static int setMajorVersion(int version, int majorVersion) { return (version & 0xFFFF0000) | majorVersion; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy