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

com.alibaba.bytekit.utils.AsmUtils Maven / Gradle / Ivy

package com.alibaba.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.deps.org.objectweb.asm.ClassReader;
import com.alibaba.deps.org.objectweb.asm.ClassVisitor;
import com.alibaba.deps.org.objectweb.asm.ClassWriter;
import com.alibaba.deps.org.objectweb.asm.Label;
import com.alibaba.deps.org.objectweb.asm.MethodVisitor;
import com.alibaba.deps.org.objectweb.asm.Opcodes;
import com.alibaba.deps.org.objectweb.asm.Type;
import com.alibaba.deps.org.objectweb.asm.commons.ClassRemapper;
import com.alibaba.deps.org.objectweb.asm.commons.JSRInlinerAdapter;
import com.alibaba.deps.org.objectweb.asm.commons.SimpleRemapper;
import com.alibaba.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.deps.org.objectweb.asm.tree.ClassNode;
import com.alibaba.deps.org.objectweb.asm.tree.FieldNode;
import com.alibaba.deps.org.objectweb.asm.tree.LabelNode;
import com.alibaba.deps.org.objectweb.asm.tree.LocalVariableNode;
import com.alibaba.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.alibaba.deps.org.objectweb.asm.tree.MethodNode;
import com.alibaba.deps.org.objectweb.asm.tree.TryCatchBlockNode;
import com.alibaba.deps.org.objectweb.asm.tree.TypeInsnNode;
import com.alibaba.deps.org.objectweb.asm.util.ASMifier;
import com.alibaba.deps.org.objectweb.asm.util.TraceClassVisitor;
import com.alibaba.bytekit.asm.ClassMetaClassWriter;

/**
 * 
 * @author hengyunabc
 *
 */
public class AsmUtils {
    static int MAJOR_VERSION = -1;

    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 ClassMetaClassWriter(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 ClassNode renameClass(ClassNode classNode, final String newClassName) { final String internalName = newClassName.replace('.', '/'); ClassNode renamedNode = new ClassNode(); ClassRemapper remapper = new ClassRemapper(renamedNode, new SimpleRemapper(classNode.name, internalName)); classNode.accept(remapper); return renamedNode; } public static byte[] renameClass(byte[] classBytes, final String newClassName) { final String internalName = newClassName.replace('.', '/'); ClassReader reader = new ClassReader(classBytes); ClassWriter writer = new ClassWriter(0); final SimpleRemapper renameRemapper = new SimpleRemapper(reader.getClassName(), internalName); ClassRemapper adapter = new ClassRemapper(writer, renameRemapper); reader.accept(adapter, 0); 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) { // 据jvm的规范,在 51 及之后版本的字节码里,不允许出现 jsr if (classNode.version >= 51) { return 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 void removeLineNumbers(ClassNode classNode) { for (MethodNode methodNode : classNode.methods) { MethodNode result = newMethodNode(methodNode); methodNode.accept(new MethodVisitor(Opcodes.ASM9, result) { public void visitLineNumber(int line, Label start) { } }); replaceMethod(classNode, methodNode); } } 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 isAbstract(MethodNode methodNode) { return (methodNode.access & Opcodes.ACC_ABSTRACT) != 0; } public static boolean isConstructor(MethodNode methodNode) { return methodNode.name != null && methodNode.name.equals(""); } public static boolean isNative(MethodNode methodNode) { return (methodNode.access & Opcodes.ACC_NATIVE) != 0; } 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.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; } public static String className(byte[] classBytes) { return new ClassReader(classBytes).getClassName(); } public static boolean isEnhancerByCGLIB(String className) { return className.contains("$$EnhancerBySpringCGLIB$$") || className.contains("$$EnhancerByCGLIB$$"); } private static boolean isBeforeNode(AbstractInsnNode aheadNode, AbstractInsnNode rearNode) { AbstractInsnNode node = rearNode; while (node != null) { if (node == aheadNode) { return true; } node = node.getPrevious(); } return false; } public static void fixConstructorExceptionTable(MethodNode methodNode) { // fix try-catch block start position in constructor for SpringCGLIB if (isConstructor(methodNode) && methodNode.tryCatchBlocks.size() > 0) { AbstractInsnNode enterInsnNode = findInitConstructorInstruction(methodNode); if (enterInsnNode == null) { return; } LabelNode newStarLabel = new LabelNode(); methodNode.instructions.insertBefore(enterInsnNode, newStarLabel); // if any try-catch block start before newStarLabel, then reset it for (TryCatchBlockNode tryCatchBlock : methodNode.tryCatchBlocks) { if (isBeforeNode(tryCatchBlock.start, newStarLabel)) { tryCatchBlock.start = newStarLabel; } } } } public static void fixMajorVersion(ClassNode classNode) { // https://github.com/alibaba/arthas/issues/1223 , V1_5 的major version是49 if (AsmUtils.getMajorVersion(classNode.version) < 49) { classNode.version = AsmUtils.setMajorVersion(classNode.version, 49); } } /** * 比如两份字节码的 major version,如果 from 的比 to 的要高,则设置 to 的 major version 为 from的 major * version * * @param from * @param to */ public static void updateMajorVersion(ClassNode from, ClassNode to) { int fromVersion = AsmUtils.getMajorVersion(from.version); int toVersion = AsmUtils.getMajorVersion(to.version); if (fromVersion > toVersion) { to.version = AsmUtils.setMajorVersion(to.version, fromVersion); } } /** * 如果成功获取,返回大于 0 的值 * * @return */ public static int currentJvmMajorVersion() { if (MAJOR_VERSION > 0) { return MAJOR_VERSION; } if (MAJOR_VERSION < 0) { try { InputStream resourceAsStream = ClassLoader.getSystemClassLoader() .getResourceAsStream(String.class.getName().replace('.', '/') + ".class"); byte[] bytes = IOUtils.getBytes(resourceAsStream); ClassNode classNode = AsmUtils.toClassNode(bytes); MAJOR_VERSION = AsmUtils.getMajorVersion(classNode.version); } catch (Throwable e) { // ignore MAJOR_VERSION = 0; } } return MAJOR_VERSION; } /** * 检查当前的字节码版本是否适配 * * @param classNode * @return */ public static boolean fitCurrentJvmMajorVersion(ClassNode classNode) { int currentJvmMajorVersion = currentJvmMajorVersion(); if (currentJvmMajorVersion > 0 && currentJvmMajorVersion < AsmUtils.getMajorVersion(classNode.version)) { return false; } return true; } public static String internalClassName(String className) { // TODO 支持原始类型,数组等 return className.replace('.', '/'); } public static String internalClassName(Class clazz) { // TODO 支持原始类型,数组等 return clazz.getName().replace('.', '/'); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy