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