add bytekit

This commit is contained in:
hengyunabc 2019-02-22 19:49:21 +08:00
parent 27d4dec87f
commit 2a0dfdeb1b
100 changed files with 11128 additions and 0 deletions

29
apm-demo/pom.xml Normal file
View File

@ -0,0 +1,29 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.1.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-apm-demo</artifactId>
<name>arthas-apm-demo</name>
<build>
<finalName>arthas-apm-demo</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
</plugins>
</build>
</project>

127
bytekit/pom.xml Normal file
View File

@ -0,0 +1,127 @@
<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-all</artifactId>
<version>3.1.1-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-bytekit</artifactId>
<name>arthas-bytekit</name>
<repositories>
<repository>
<id>in-project</id>
<name>In Project Repo</name>
<url>file://${project.basedir}/../lib</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-analysis</artifactId>
</dependency>
<dependency>
<groupId>org.benf</groupId>
<artifactId>cfr</artifactId>
<version>0.132</version>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<version>1.7.10</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-agent</artifactId>
<version>1.7.10</version>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.9.RELEASE</version>
</dependency>
<!-- Test Artifacts -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.9.1</version>
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>1.5.9.RELEASE</version>
<scope>test</scope>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<finalName>arthas-bytekit</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
<showDeprecation>true</showDeprecation>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,22 @@
package com.taobao.arthas.bytekit;
import java.util.List;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorClassParser;
import com.taobao.arthas.bytekit.asm.matcher.ClassMatcher;
import com.taobao.arthas.bytekit.asm.matcher.MethodMatcher;
public class ByteKit {
private ClassMatcher classMatcher;
private MethodMatcher methodMatcher;
private Class<?> interceptorClass;
private InterceptorClassParser interceptorClassParser;
private List<InterceptorProcessor> interceptorProcessors;
}

View File

@ -0,0 +1,85 @@
package com.taobao.arthas.bytekit.asm;
import org.objectweb.asm.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.LocalVariablesSorter;
/**
* Adapter for to be inlined code.
*
* This adapter does all parameter renaming and replacing of the RETURN opcodes
*
*
*/
public class InliningAdapter extends LocalVariablesSorter {
private final Label end;
private LocalVariablesSorter lvs;
public InliningAdapter(LocalVariablesSorter mv, int access, String desc, Label end) {
super(Opcodes.ASM6, access, desc, mv);
this.end = end;
this.lvs = mv;
// int off = (access & Opcodes.ACC_STATIC) != 0 ?
// 0 : 1;
// Type[] args = Type.getArgumentTypes(desc);
// for (int i = args.length - 1; i >= 0; i--) {
// super.visitVarInsn(args[i].getOpcode(
// Opcodes.ISTORE), i + off);
// }
// if (off > 0) {
// super.visitVarInsn(Opcodes.ASTORE, 0);
// }
// save args to local vars
int off = (access & Opcodes.ACC_STATIC) != 0 ? 0 : 1;
Type[] args = Type.getArgumentTypes(desc);
int argsOff = off;
for(int i = 0; i < args.length; ++i) {
argsOff += args[i].getSize();
}
for(int i = args.length - 1; i >= 0; --i) {
argsOff -= args[i].getSize();
this.visitVarInsn(args[i].getOpcode(Opcodes.ISTORE), argsOff);
}
// this
if (off > 0) {
this.visitVarInsn(Opcodes.ASTORE, 0);
}
}
@Override
public void visitInsn(int opcode) {
if (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) {
super.visitJumpInsn(Opcodes.GOTO, end);
} else {
super.visitInsn(opcode);
}
}
@Override
public void visitMaxs(int stack, int locals) {
// super.visitMaxs(stack, locals);
}
@Override
protected int newLocalMapping(Type type) {
return lvs.newLocal(type);
}
@Override
public void visitVarInsn(int opcode, int var) {
super.visitVarInsn(opcode, var + this.firstLocal);
}
@Override
public void visitIincInsn(int var, int increment) {
super.visitIincInsn(var + this.firstLocal, increment);
}
@Override
public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
super.visitLocalVariable(name, desc, signature, start, end, index + this.firstLocal);
}
}

View File

@ -0,0 +1,97 @@
package com.taobao.arthas.bytekit.asm;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.GeneratorAdapter;
import org.objectweb.asm.tree.MethodNode;
/**
* @author hengyunabc 2018-01-31
*
*/
public abstract class MethodCallInliner extends GeneratorAdapter {
public class CatchBlock {
private Label start;
private Label handler;
private String type;
private Label end;
public CatchBlock(Label start, Label end, Label handler, String type) {
this.start = start;
this.end = end;
this.handler = handler;
this.type = type;
}
}
private final MethodNode toBeInlined;
private List<CatchBlock> blocks = new ArrayList<CatchBlock>();
private boolean inlining;
private boolean afterInlining;
public MethodCallInliner(int access, String name, String desc, MethodVisitor mv,
MethodNode toBeInlined) {
super(Opcodes.ASM6, mv, access, name, desc);
this.toBeInlined = toBeInlined;
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
if (!shouldBeInlined(owner, name, desc)) {
mv.visitMethodInsn(opcode, owner, name, desc, itf);
return;
}
// if (this.analyzerAdapter != null) {
// mv = new MergeFrameAdapter(this.api, this.analyzerAdapter,
// (MethodVisitor)mv);
// }
Label end = new Label();
inlining = true;
toBeInlined.instructions.resetLabels();
// pass the to be inlined method through the inlining adapter to this
toBeInlined.accept(new InliningAdapter(this, toBeInlined.access, toBeInlined.desc, end));
inlining = false;
afterInlining = true;
// visit the end label
super.visitLabel(end);
// box the return value if necessary
// Type returnType =
// Type.getMethodType(toBeInlined.desc).getReturnType();
// valueOf(returnType);
}
abstract boolean shouldBeInlined(String owner, String name, String desc);
@Override
public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
if (!inlining) {
blocks.add(new CatchBlock(start, end, handler, type));
} else {
super.visitTryCatchBlock(start, end, handler, type);
}
}
@Override
public void visitMaxs(int stack, int locals) {
for (CatchBlock b : blocks)
super.visitTryCatchBlock(b.start, b.end, b.handler, b.type);
super.visitMaxs(stack, locals);
}
@Override
public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
// swallow
}
}

View File

@ -0,0 +1,33 @@
package com.taobao.arthas.bytekit.asm;
public class MethodInfo {
private int access;
private String name;
private String desc;
public int getAccess() {
return access;
}
public void setAccess(int access) {
this.access = access;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}

View File

@ -0,0 +1,766 @@
package com.taobao.arthas.bytekit.asm;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FrameNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
import com.taobao.arthas.bytekit.utils.AsmUtils;
public class MethodProcessor {
private String owner;
/**
* maybe null
*/
private ClassNode classNode;
private MethodNode methodNode;
private final Type[] argumentTypes;
private final Type returnType;
private int nextLocals;
private static final Type BYTE_TYPE = Type.getObjectType("java/lang/Byte");
private static final Type BOOLEAN_TYPE = Type.getObjectType("java/lang/Boolean");
private static final Type SHORT_TYPE = Type.getObjectType("java/lang/Short");
private static final Type CHARACTER_TYPE = Type.getObjectType("java/lang/Character");
private static final Type INTEGER_TYPE = Type.getObjectType("java/lang/Integer");
private static final Type FLOAT_TYPE = Type.getObjectType("java/lang/Float");
private static final Type LONG_TYPE = Type.getObjectType("java/lang/Long");
private static final Type DOUBLE_TYPE = Type.getObjectType("java/lang/Double");
private static final Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
private static final Type STRING_TYPE = Type.getObjectType("java/lang/String");
private static final Type THROWABLE_TYPE = Type.getObjectType("java/lang/Throwable");
private static final Type NUMBER_TYPE = Type.getObjectType("java/lang/Number");
private static final Type OBJECT_ARRAY_TYPE = Type.getType(Object[].class);
private static final Method BOOLEAN_VALUE = Method.getMethod("boolean booleanValue()");
private static final Method CHAR_VALUE = Method.getMethod("char charValue()");
private static final Method INT_VALUE = Method.getMethod("int intValue()");
private static final Method FLOAT_VALUE = Method.getMethod("float floatValue()");
private static final Method LONG_VALUE = Method.getMethod("long longValue()");
private static final Method DOUBLE_VALUE = Method.getMethod("double doubleValue()");
public static final String DEFAULT_INNER_VARIABLE_PREFIX = "_$bytekit$_";
private final LabelNode interceptorVariableStartLabelNode = new LabelNode();
private final LabelNode interceptorVariableEndLabelNode = new LabelNode();
private AbstractInsnNode enterInsnNode;
// TODO 这里应该直接从 InsnList 里来取因为插入代码之后这个会改变的
// TODO 这个没有被使用到是不是没用的
private AbstractInsnNode lastInsnNode;
/**
* 保留中间生成的 variable的名字
*/
private boolean keepLocalVariableNames;
private String innerVariablePrefix;
private String returnVariableName;
private String throwVariableName;
private String invokeArgsVariableName;
private String monitorVariableName;
private LocalVariableNode returnVariableNode = null;
private LocalVariableNode throwVariableNode = null;
private LocalVariableNode invokeArgsVariableNode = null;
private LocalVariableNode monitorVariableNode = null; // for synchronized
private String invokeReturnVariablePrefix;
private Map<String, LocalVariableNode> invokeReturnVariableNodeMap = new HashMap<String, LocalVariableNode>();
private TryCatchBlock tryCatchBlock = null;
public MethodProcessor(final ClassNode classNode, final MethodNode methodNode) {
this(classNode, methodNode, false);
}
public MethodProcessor(final ClassNode classNode, final MethodNode methodNode, boolean keepLocalVariableNames) {
this(classNode.name, methodNode, keepLocalVariableNames);
this.classNode = classNode;
}
public MethodProcessor(final String owner, final MethodNode methodNode, boolean keepLocalVariableNames) {
this.owner = owner;
this.methodNode = methodNode;
this.nextLocals = methodNode.maxLocals;
this.argumentTypes = Type.getArgumentTypes(methodNode.desc);
this.returnType = Type.getReturnType(methodNode.desc);
this.keepLocalVariableNames = keepLocalVariableNames;
// find enter & exit instruction.
if (isConstructor()) {
this.enterInsnNode = findInitConstructorInstruction();
} else {
this.enterInsnNode = methodNode.instructions.getFirst();
}
// when the method is empty, both enterInsnNode and lastInsnNode are Opcodes.RETURN ;
this.lastInsnNode = methodNode.instructions.getLast();
// setup interceptor variables start/end label.
this.methodNode.instructions.insertBefore(this.enterInsnNode, this.interceptorVariableStartLabelNode);
this.methodNode.instructions.insert(this.lastInsnNode, this.interceptorVariableEndLabelNode);
initInnerVariablePrefix();
}
public MethodProcessor(final String owner, final MethodNode methodNode) {
this(owner, methodNode, false);
}
private void initInnerVariablePrefix() {
String prefix = DEFAULT_INNER_VARIABLE_PREFIX;
int count = 0;
while(existLocalVariableWithPrefix(prefix)) {
prefix = DEFAULT_INNER_VARIABLE_PREFIX + count + "_";
count++;
}
this.innerVariablePrefix = prefix;
returnVariableName = innerVariablePrefix + "_return";
throwVariableName = innerVariablePrefix + "_throw";
invokeArgsVariableName = innerVariablePrefix + "_invokeArgs";
monitorVariableName = innerVariablePrefix + "_monitor";
invokeReturnVariablePrefix = innerVariablePrefix + "_invokeReturn_";
}
private boolean existLocalVariableWithPrefix(String prefix) {
for (LocalVariableNode variableNode : this.methodNode.localVariables) {
if (variableNode.name.startsWith(prefix)) {
return true;
}
}
return false;
}
public LocalVariableNode initMonitorVariableNode() {
if (monitorVariableNode == null) {
monitorVariableNode = this.addInterceptorLocalVariable(monitorVariableName, OBJECT_TYPE.getDescriptor());
}
return monitorVariableNode;
}
public LocalVariableNode initThrowVariableNode() {
if (throwVariableNode == null) {
throwVariableNode = this.addInterceptorLocalVariable(throwVariableName, THROWABLE_TYPE.getDescriptor());
}
return throwVariableNode;
}
public LocalVariableNode initInvokeArgsVariableNode() {
if (invokeArgsVariableNode == null) {
invokeArgsVariableNode = this.addInterceptorLocalVariable(invokeArgsVariableName,
OBJECT_ARRAY_TYPE.getDescriptor());
}
return invokeArgsVariableNode;
}
public LocalVariableNode initReturnVariableNode() {
if (returnVariableNode == null) {
returnVariableNode = this.addInterceptorLocalVariable(returnVariableName, returnType.getDescriptor());
}
return returnVariableNode;
}
/**
*
* @param name
* @param type
* @return
*/
public LocalVariableNode initInvokeReturnVariableNode(String name, Type type) {
String key = this.invokeReturnVariablePrefix + name;
LocalVariableNode variableNode = invokeReturnVariableNodeMap.get(key);
if (variableNode == null) {
variableNode = this.addInterceptorLocalVariable(key, type.getDescriptor());
invokeReturnVariableNodeMap.put(key, variableNode);
}
return variableNode;
}
public TryCatchBlock initTryCatchBlock() {
return initTryCatchBlock(THROWABLE_TYPE.getInternalName());
}
public TryCatchBlock initTryCatchBlock(String exception) {
if( this.tryCatchBlock == null) {
this.tryCatchBlock = new TryCatchBlock(methodNode, exception);
this.methodNode.instructions.insertBefore(this.getEnterInsnNode(), tryCatchBlock.getStartLabelNode());
this.methodNode.instructions.insert(this.getLastInsnNode(), tryCatchBlock.getEndLabelNode());
InsnList instructions = new InsnList();
AsmOpUtils.throwException(instructions);
this.methodNode.instructions.insert(tryCatchBlock.getEndLabelNode(), instructions);
tryCatchBlock.sort();
}
return tryCatchBlock;
}
AbstractInsnNode findInitConstructorInstruction() {
int nested = 0;
for (AbstractInsnNode insnNode = this.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("<init>")) {
if (--nested < 0) {
// find this() or super().
return insnNode.getNext();
}
}
}
}
return null;
}
public AbstractInsnNode getEnterInsnNode() {
return enterInsnNode;
}
public AbstractInsnNode getLastInsnNode() {
return lastInsnNode;
}
public String[] getParameterTypes() {
final String[] parameterTypes = new String[this.argumentTypes.length];
for (int i = 0; i < this.argumentTypes.length; i++) {
parameterTypes[i] = this.argumentTypes[i].getClassName();
}
return parameterTypes;
}
public String[] getParameterNames() {
if (this.argumentTypes.length == 0) {
return new String[0];
}
final List<LocalVariableNode> localVariableNodes = this.methodNode.localVariables;
int localVariableStartIndex = 1;
if (isStatic()) {
// static method is none this.
localVariableStartIndex = 0;
}
if (localVariableNodes == null || localVariableNodes.size() <= localVariableStartIndex
|| (this.argumentTypes.length + localVariableStartIndex) > localVariableNodes.size()) {
// make simple argument names.
final String[] names = new String[this.argumentTypes.length];
for (int i = 0; i < this.argumentTypes.length; i++) {
final String className = this.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] = this.argumentTypes[i].getDescriptor();
}
}
return names;
}
// sort by index.
Collections.sort(localVariableNodes, new Comparator<LocalVariableNode>() {
@Override
public int compare(LocalVariableNode o1, LocalVariableNode o2) {
return o1.index - o2.index;
}
});
String[] names = new String[this.argumentTypes.length];
for (int i = 0; i < this.argumentTypes.length; i++) {
final String name = localVariableNodes.get(localVariableStartIndex++).name;
if (name != null) {
names[i] = name;
} else {
names[i] = "";
}
}
return names;
}
public Type getReturnType() {
return this.returnType;
}
private boolean hasLocalVariable(String name) {
List<LocalVariableNode> localVariableNodes = this.methodNode.localVariables;
if (localVariableNodes == null) {
return false;
}
for (LocalVariableNode node : localVariableNodes) {
if (node.name.equals(name)) {
return true;
}
}
return false;
}
public void loadThis(final InsnList instructions) {
if (isConstructor()) {
// load this.
loadVar(instructions, 0);
} else {
if (isStatic()) {
// load null.
loadNull(instructions);
} else {
// load this.
loadVar(instructions, 0);
}
}
}
void storeVar(final InsnList instructions, final int index) {
instructions.add(new VarInsnNode(Opcodes.ASTORE, index));
}
void storeInt(final InsnList instructions, final int index) {
instructions.add(new VarInsnNode(Opcodes.ISTORE, index));
}
void loadNull(final InsnList instructions) {
instructions.add(new InsnNode(Opcodes.ACONST_NULL));
}
void loadVar(final InsnList instructions, final int index) {
instructions.add(new VarInsnNode(Opcodes.ALOAD, index));
}
void loadInt(final InsnList instructions, final int index) {
instructions.add(new VarInsnNode(Opcodes.ILOAD, index));
}
boolean isReturnCode(final int opcode) {
return opcode == Opcodes.IRETURN || opcode == Opcodes.LRETURN || opcode == Opcodes.FRETURN
|| opcode == Opcodes.DRETURN || opcode == Opcodes.ARETURN || opcode == Opcodes.RETURN;
}
Type getBoxedType(final Type type) {
switch (type.getSort()) {
case Type.BYTE:
return BYTE_TYPE;
case Type.BOOLEAN:
return BOOLEAN_TYPE;
case Type.SHORT:
return SHORT_TYPE;
case Type.CHAR:
return CHARACTER_TYPE;
case Type.INT:
return INTEGER_TYPE;
case Type.FLOAT:
return FLOAT_TYPE;
case Type.LONG:
return LONG_TYPE;
case Type.DOUBLE:
return DOUBLE_TYPE;
}
return type;
}
void push(InsnList insnList, final int value) {
if (value >= -1 && value <= 5) {
insnList.add(new InsnNode(Opcodes.ICONST_0 + value));
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
insnList.add(new IntInsnNode(Opcodes.BIPUSH, value));
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
insnList.add(new IntInsnNode(Opcodes.SIPUSH, value));
} else {
insnList.add(new LdcInsnNode(value));
}
}
void push(InsnList insnList, final String value) {
if (value == null) {
insnList.add(new InsnNode(Opcodes.ACONST_NULL));
} else {
insnList.add(new LdcInsnNode(value));
}
}
void newArray(final InsnList insnList, final Type type) {
insnList.add(new TypeInsnNode(Opcodes.ANEWARRAY, type.getInternalName()));
}
void dup(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.DUP));
}
void dup2(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.DUP2));
}
void dupX1(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.DUP_X1));
}
void dupX2(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.DUP_X2));
}
void pop(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.POP));
}
void swap(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.SWAP));
}
void loadArgsVar(final InsnList instructions) {
if (this.argumentTypes.length == 0) {
// null.
loadNull(instructions);
return;
}
push(instructions, this.argumentTypes.length);
// new array
newArray(instructions, OBJECT_TYPE);
for (int i = 0; i < this.argumentTypes.length; i++) {
Type type = this.argumentTypes[i];
dup(instructions);
push(instructions, i);
// loadArg
loadArg(instructions, this.argumentTypes, i);
// box
box(instructions, type);
// arrayStore
arrayStore(instructions, OBJECT_TYPE);
}
}
void loadArgs(final InsnList instructions) {
for (int i = 0; i < this.argumentTypes.length; i++) {
loadArg(instructions, this.argumentTypes, i);
}
}
void loadArg(final InsnList instructions, Type[] argumentTypes, int i) {
final int index = getArgIndex(argumentTypes, i);
final Type type = argumentTypes[i];
instructions.add(new VarInsnNode(type.getOpcode(Opcodes.ILOAD), index));
}
int getArgIndex(final Type[] argumentTypes, final int arg) {
int index = isStatic() ? 0 : 1;
for (int i = 0; i < arg; i++) {
index += argumentTypes[i].getSize();
}
return index;
}
void box(final InsnList instructions, Type type) {
if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
return;
}
if (type == Type.VOID_TYPE) {
// push null
instructions.add(new InsnNode(Opcodes.ACONST_NULL));
} else {
Type boxed = getBoxedType(type);
// new instance.
newInstance(instructions, boxed);
if (type.getSize() == 2) {
// Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
// dupX2
dupX2(instructions);
// dupX2
dupX2(instructions);
// pop
pop(instructions);
} else {
// p -> po -> opo -> oop -> o
// dupX1
dupX1(instructions);
// swap
swap(instructions);
}
invokeConstructor(instructions, boxed, new Method("<init>", Type.VOID_TYPE, new Type[] { type }));
}
}
void unbox(final InsnList instructions, Type type) {
Type t = NUMBER_TYPE;
Method sig = null;
switch (type.getSort()) {
case Type.VOID:
return;
case Type.CHAR:
t = CHARACTER_TYPE;
sig = CHAR_VALUE;
break;
case Type.BOOLEAN:
t = BOOLEAN_TYPE;
sig = BOOLEAN_VALUE;
break;
case Type.DOUBLE:
sig = DOUBLE_VALUE;
break;
case Type.FLOAT:
sig = FLOAT_VALUE;
break;
case Type.LONG:
sig = LONG_VALUE;
break;
case Type.INT:
case Type.SHORT:
case Type.BYTE:
sig = INT_VALUE;
}
if (sig == null) {
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, type.getInternalName()));
} else {
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, t.getInternalName()));
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, t.getInternalName(), sig.getName(),
sig.getDescriptor(), false));
}
}
void arrayStore(final InsnList instructions, final Type type) {
instructions.add(new InsnNode(type.getOpcode(Opcodes.IASTORE)));
}
void arrayLoad(final InsnList instructions, final Type type) {
instructions.add(new InsnNode(type.getOpcode(Opcodes.IALOAD)));
}
void newInstance(final InsnList instructions, final Type type) {
instructions.add(new TypeInsnNode(Opcodes.NEW, type.getInternalName()));
}
void invokeConstructor(final InsnList instructions, final Type type, final Method method) {
String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() : type.getInternalName();
instructions
.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, owner, method.getName(), method.getDescriptor(), false));
}
LocalVariableNode addInterceptorLocalVariable(final String name, final String desc) {
return addLocalVariable(name, desc, this.interceptorVariableStartLabelNode,
this.interceptorVariableEndLabelNode);
}
LocalVariableNode addLocalVariable(final String name, final String desc, final LabelNode start,
final LabelNode end) {
Type type = Type.getType(desc);
int index = this.nextLocals;
this.nextLocals += type.getSize();
methodNode.maxLocals = this.nextLocals;
final LocalVariableNode node = new LocalVariableNode(name, desc, null, start, end, index);
if (keepLocalVariableNames) {
this.methodNode.localVariables.add(node);
}
return node;
}
public void returnValue(final InsnList instructions) {
instructions.add(new InsnNode(this.returnType.getOpcode(Opcodes.IRETURN)));
}
public boolean isStatic() {
return (this.methodNode.access & Opcodes.ACC_STATIC) != 0;
}
public boolean isConstructor() {
return this.methodNode.name != null && this.methodNode.name.equals("<init>");
}
public MethodNode getMethodNode() {
return methodNode;
}
public void setMethodNode(MethodNode methodNode) {
this.methodNode = methodNode;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public ClassNode getClassNode() {
return classNode;
}
public void setClassNode(ClassNode classNode) {
this.classNode = classNode;
}
/**
* TODO 可以考虑实现修改值的功能原理是传入的 args实际转化为一个stack上的slot只要在inline之后 stack上面的对应的slot保存到想要保存的位置就可以了
* @param owner
* @param tmpToInlineMethodNode
*/
public void inline(String owner, MethodNode toInlineMethodNode) {
ListIterator<AbstractInsnNode> originMethodIter = this.methodNode.instructions.iterator();
while(originMethodIter.hasNext()) {
AbstractInsnNode originMethodInsnNode = originMethodIter.next();
if (originMethodInsnNode instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) originMethodInsnNode;
if (methodInsnNode.owner.equals(owner) && methodInsnNode.name.equals(toInlineMethodNode.name)
&& methodInsnNode.desc.equals(toInlineMethodNode.desc)) {
// 要copy一份否则inline多次会出问题
MethodNode tmpToInlineMethodNode = AsmUtils.copy(toInlineMethodNode);
tmpToInlineMethodNode = AsmUtils.removeLineNumbers(tmpToInlineMethodNode);
LabelNode end = new LabelNode();
this.methodNode.instructions.insert(methodInsnNode, end);
InsnList instructions = new InsnList();
// 要先记录好当前的 maxLocals 然后再依次把 栈上的 args保存起来 后面调整 VarInsnNode index里要加上当前的 maxLocals
// save args to local vars
int currentMaxLocals = this.nextLocals;
int off = (tmpToInlineMethodNode.access & Opcodes.ACC_STATIC) != 0 ? 0 : 1;
Type[] args = Type.getArgumentTypes(tmpToInlineMethodNode.desc);
int argsOff = off;
for(int i = 0; i < args.length; ++i) {
argsOff += args[i].getSize();
}
// 记录新的 maxLocals
this.nextLocals += argsOff;
methodNode.maxLocals = this.nextLocals;
for(int i = args.length - 1; i >= 0; --i) {
argsOff -= args[i].getSize();
// this.visitVarInsn(args[i].getOpcode(Opcodes.ISTORE), argsOff);
AsmOpUtils.storeVar(instructions, args[i], currentMaxLocals + argsOff);
}
// this
if (off > 0) {
// this.visitVarInsn(Opcodes.ASTORE, 0);
AsmOpUtils.storeVar(instructions, OBJECT_TYPE, currentMaxLocals);
}
ListIterator<AbstractInsnNode> inlineIterator = tmpToInlineMethodNode.instructions.iterator();
while(inlineIterator.hasNext()) {
AbstractInsnNode abstractInsnNode = inlineIterator.next();
if(abstractInsnNode instanceof FrameNode) {
continue;
}
if(abstractInsnNode instanceof VarInsnNode) {
VarInsnNode varInsnNode = (VarInsnNode) abstractInsnNode;
varInsnNode.var += currentMaxLocals;
}
int opcode = abstractInsnNode.getOpcode();
if (opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) {
// super.visitJumpInsn(Opcodes.GOTO, end);
// instructions.add(new JumpInsnNode(Opcodes.GOTO, end));
inlineIterator.remove();
instructions.add(new JumpInsnNode(Opcodes.GOTO, end));
continue;
}
inlineIterator.remove();
instructions.add(abstractInsnNode);
}
// 插入inline之后的代码再删除掉原来的 MethodInsnNode
this.methodNode.instructions.insertBefore(methodInsnNode, instructions);
originMethodIter.remove();
// try catch 块加上然后排序
if(this.methodNode.tryCatchBlocks != null && tmpToInlineMethodNode.tryCatchBlocks != null) {
this.methodNode.tryCatchBlocks.addAll(tmpToInlineMethodNode.tryCatchBlocks);
}
this.sortTryCatchBlock();
}
}
}
}
public void sortTryCatchBlock() {
if (this.methodNode.tryCatchBlocks == null) {
return;
}
// Compares TryCatchBlockNodes by the length of their "try" block.
Collections.sort(this.methodNode.tryCatchBlocks, new Comparator<TryCatchBlockNode>() {
@Override
public int compare(TryCatchBlockNode t1, TryCatchBlockNode t2) {
int len1 = blockLength(t1);
int len2 = blockLength(t2);
return len1 - len2;
}
private int blockLength(TryCatchBlockNode block) {
final int startidx = methodNode.instructions.indexOf(block.start);
final int endidx = methodNode.instructions.indexOf(block.end);
return endidx - startidx;
}
});
// Updates the 'target' of each try catch block annotation.
for (int i = 0; i < this.methodNode.tryCatchBlocks.size(); i++) {
this.methodNode.tryCatchBlocks.get(i).updateIndex(i);
}
}
}

View File

@ -0,0 +1,64 @@
package com.taobao.arthas.bytekit.asm;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class MyTryCatchBlock {
private final MethodNode methodNode;
private final LabelNode startLabelNode = new LabelNode();
private final LabelNode endLabelNode = new LabelNode();
private final LabelNode handlerLabelNode = new LabelNode();
public MyTryCatchBlock(final MethodNode methodNode) {
this.methodNode = methodNode;
final TryCatchBlockNode tryCatchBlockNode = new TryCatchBlockNode(this.startLabelNode, this.endLabelNode, this.handlerLabelNode, "java/lang/Throwable");
if (this.methodNode.tryCatchBlocks == null) {
this.methodNode.tryCatchBlocks = new ArrayList<TryCatchBlockNode>();
}
this.methodNode.tryCatchBlocks.add(tryCatchBlockNode);
}
public LabelNode getStartLabelNode() {
return this.startLabelNode;
}
public LabelNode getEndLabelNode() {
return this.endLabelNode;
}
public LabelNode getHandlerLabelNode() {
return this.handlerLabelNode;
}
public void sort() {
if (this.methodNode.tryCatchBlocks == null) {
return;
}
// Compares TryCatchBlockNodes by the length of their "try" block.
Collections.sort(this.methodNode.tryCatchBlocks, new Comparator<TryCatchBlockNode>() {
@Override
public int compare(TryCatchBlockNode t1, TryCatchBlockNode t2) {
int len1 = blockLength(t1);
int len2 = blockLength(t2);
return len1 - len2;
}
private int blockLength(TryCatchBlockNode block) {
final int startidx = methodNode.instructions.indexOf(block.start);
final int endidx = methodNode.instructions.indexOf(block.end);
return endidx - startidx;
}
});
// Updates the 'target' of each try catch block annotation.
for (int i = 0; i < this.methodNode.tryCatchBlocks.size(); i++) {
this.methodNode.tryCatchBlocks.get(i).updateIndex(i);
}
}
}

View File

@ -0,0 +1,66 @@
package com.taobao.arthas.bytekit.asm;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TryCatchBlockNode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class TryCatchBlock {
private final MethodNode methodNode;
private final LabelNode startLabelNode = new LabelNode();
private final LabelNode endLabelNode = new LabelNode();
public TryCatchBlock(final MethodNode methodNode) {
this(methodNode, Type.getType(Throwable.class).getInternalName());
}
public TryCatchBlock(final MethodNode methodNode, String exception) {
this.methodNode = methodNode;
final TryCatchBlockNode tryCatchBlockNode = new TryCatchBlockNode(this.startLabelNode, this.endLabelNode,
this.endLabelNode, exception);
if (this.methodNode.tryCatchBlocks == null) {
this.methodNode.tryCatchBlocks = new ArrayList<TryCatchBlockNode>();
}
this.methodNode.tryCatchBlocks.add(tryCatchBlockNode);
}
public LabelNode getStartLabelNode() {
return this.startLabelNode;
}
public LabelNode getEndLabelNode() {
return this.endLabelNode;
}
public void sort() {
if (this.methodNode.tryCatchBlocks == null) {
return;
}
// Compares TryCatchBlockNodes by the length of their "try" block.
Collections.sort(this.methodNode.tryCatchBlocks, new Comparator<TryCatchBlockNode>() {
@Override
public int compare(TryCatchBlockNode t1, TryCatchBlockNode t2) {
int len1 = blockLength(t1);
int len2 = blockLength(t2);
return len1 - len2;
}
private int blockLength(TryCatchBlockNode block) {
final int startidx = methodNode.instructions.indexOf(block.start);
final int endidx = methodNode.instructions.indexOf(block.end);
return endidx - startidx;
}
});
// Updates the 'target' of each try catch block annotation.
for (int i = 0; i < this.methodNode.tryCatchBlocks.size(); i++) {
this.methodNode.tryCatchBlocks.get(i).updateIndex(i);
}
}
}

View File

@ -0,0 +1,431 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2008-10 Red Hat and individual contributors
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*
* @authors Andrew Dinn
*/
package com.taobao.arthas.bytekit.asm;
/**
* Helpoer class providing static methods for manipulating type and class names,
* field and method descriptor names etc
*/
public class TypeHelper {
public static boolean equalDescriptors(String desc1, String desc2)
{
int idx1 = 0, idx2 = 0;
int len1 = desc1.length(), len2 = desc2.length();
while (idx1 < len1) {
// check the other has not dropped off the end
if (idx2 == len2) {
if ((idx1 == (len1 - 1)) && (desc1.charAt(idx1) == '$')) {
return true;
}
return false;
}
// check type is the same
char char1 = desc1.charAt(idx1);
char char2 = desc2.charAt(idx2);
// if we have a $ at the end of the descriptor then this means any return
// type so special case this
if ((char1 == '$' && idx1 == len1 - 1) || (char2 == '$' && idx2 == len2 - 1)) {
return true;
}
// otherwise the chars must match
if (char1 != char2) {
return false;
}
// however an L indicates a class name and we allow a classname without a package
// to match a class name with a package
if (char1 == 'L') {
// ok, ensure the names must match modulo a missing package
int end1 = idx1 + 1;
int end2 = idx2 + 1;
while (end1 < len1 && desc1.charAt(end1) != ';') {
end1++;
}
while (end2 < len2 && desc2.charAt(end2) != ';') {
end2++;
}
if (end1 == len1 || end2 == len2) {
// bad format for desc!!
return false;
}
String typeName1 = desc1.substring(idx1 + 1, end1);
String typeName2 = desc2.substring(idx2 + 1, end2);
if (!typeName1.equals(typeName2)) {
int tailIdx1 = typeName1.lastIndexOf('/');
int tailIdx2 = typeName2.lastIndexOf('/');
if (tailIdx1 > 0) {
if (tailIdx2 > 0) {
// both specify packages so they must be different types
return false;
} else {
// only type 1 specifies a package so type 2 should match the tail
if (!typeName2.equals(typeName1.substring(tailIdx1 + 1))) {
return false;
}
}
} else {
if (tailIdx2 > 0) {
// only type 2 specifies a package so type 1 should match the tail
if (!typeName1.equals(typeName2.substring(tailIdx2 + 1))) {
return false;
}
} else {
// neither specify packages so they must be different types
return false;
}
}
}
// skp past ';'s
idx1 = end1;
idx2 = end2;
}
idx1++;
idx2++;
}
// check the other has not reached the end
if (idx2 != len2) {
return false;
}
return true;
}
/**
* convert a classname from canonical form to the form used to represent it externally i.e. replace
* all dots with slashes
*
* @param className the canonical name
* @return the external name
*/
public static String externalizeClass(String className)
{
return className.replace('.', '/');
}
/**
* convert a classname from external form to canonical form i.e. replace
* all slashes with dots
*
* @param className the external name
* @return the canonical name
*/
public static String internalizeClass(String className)
{
String result = className;
int length = result.length();
if (result.charAt(length - 1) == ';') {
result = result.substring(1, length - 2);
}
result = result.replace('/', '.');
return result;
}
/**
* convert a type name from canonical form to the form used to represent it externally i.e.
* replace primitive type names by the appropriate single letter types, class names
* by the externalized class name bracketed by 'L' and ';' and array names by the
* base type name preceded by '['.
*
* @param typeName the type name
* @return the external name
*/
public static String externalizeType(String typeName)
{
String externalName = "";
String[] typeAndArrayIndices = typeName.split("\\[");
String baseType = typeAndArrayIndices[0].trim();
for (int i = 1; i< typeAndArrayIndices.length; i++) {
String arrayIdx = typeAndArrayIndices[i];
if (arrayIdx.indexOf("\\]") != 0) {
externalName += '[';
}
}
for (int i = 0; i < internalNames.length; i++) {
if (internalNames[i].equals(baseType)) {
externalName += externalNames[i];
return externalName;
}
}
externalName += "L" + externalizeClass(baseType) + ";";
return externalName;
}
/**
* list of well known typenames as written in Java code
*/
final static private String[] internalNames = {
"", /* equivalent to void */
"void",
"byte",
"char",
"short",
"int",
"long",
"float",
"double",
"boolean",
"Byte",
"Character",
"Short",
"Integer",
"Long",
"Float",
"Double",
"String",
"java.lang.Byte",
"java.lang.Character",
"java.lang.Short",
"java.lang.Integer",
"java.lang.Long",
"java.lang.Float",
"java.lang.Double",
"java.lang.String"
};
/**
* list of typenames in external form corresponding to entries ni previous list
*/
final static private String[] externalNames = {
"$",
"V",
"B",
"C",
"S",
"I",
"J",
"F",
"D",
"Z",
"Ljava/lang/Byte;",
"Ljava/lang/Character;",
"Ljava/lang/Short;",
"Ljava/lang/Integer;",
"Ljava/lang/Long;",
"Ljava/lang/Float;",
"Ljava/lang/Double;",
"Ljava/lang/String;",
"Ljava/lang/Byte;",
"Ljava/lang/Character;",
"Ljava/lang/Short;",
"Ljava/lang/Integer;",
"Ljava/lang/Long;",
"Ljava/lang/Float;",
"Ljava/lang/Double;",
"Ljava/lang/String;"
};
/**
* convert a method descriptor from canonical form to the form used to represent it externally
*
* @param desc the method descriptor which must be trimmed of any surrounding white space
* @return an externalised form for the descriptor
*/
public static String externalizeDescriptor(String desc)
{
// the descriptor will start with '(' and the arguments list should end with ')' and,
// if it is not void be followed by a return type
int openIdx = desc.indexOf('(');
int closeIdx = desc.indexOf(')');
int length = desc.length();
if (openIdx != 0) {
return "";
}
if (closeIdx < 0) {
return "";
}
String retType = (closeIdx < length ? desc.substring(closeIdx + 1).trim() : "");
String externalRetType = externalizeType(retType);
String argString = desc.substring(1, closeIdx).trim();
String externalArgs = "";
if (argString.equals("")) {
externalArgs = argString;
} else {
String[] args = desc.substring(1, closeIdx).trim().split(",");
for (int i = 0; i < args.length ; i++) {
externalArgs += externalizeType(args[i]);
}
}
return "(" + externalArgs + ")" + externalRetType;
}
/**
* convert a method descriptor from the form used to represent it externally to canonical form
*
* @param desc the method descriptor which must be trimmed of any surrounding white space and start with "(".
* it must end either with ")" or with ") " followed by an exernalized return type
* @return an internalised form for the descriptor, possibly followed by a space and externalized return type
*/
public static String internalizeDescriptor(String desc)
{
StringBuffer buffer = new StringBuffer();
String sepr = "";
int argStart = desc.indexOf('(');
int argEnd = desc.indexOf(')');
int max = desc.length();
if (argStart < 0 || argEnd < 0) {
return "(...)";
}
int arrayCount = 0;
boolean addSepr = false;
buffer.append("(");
for (int idx = argStart + 1; idx < max;) {
char next = desc.charAt(idx);
if (addSepr) {
while (arrayCount > 0) {
buffer.append("[]");
arrayCount--;
}
buffer.append(sepr);
}
addSepr = true;
switch(next) {
case 'B':
{
buffer.append("byte");
}
break;
case 'C':
{
buffer.append("char");
}
break;
case 'S':
{
buffer.append("short");
}
break;
case 'I':
{
buffer.append("int");
}
break;
case 'J':
{
buffer.append("long");
}
break;
case 'Z':
{
buffer.append("boolean");
}
break;
case 'F':
{
buffer.append("float");
}
break;
case 'D':
{
buffer.append("double");
}
case 'V':
{
buffer.append("void");
}
break;
case 'L':
{
int tailIdx = idx+1;
while (tailIdx < max) {
char tailChar = desc.charAt(tailIdx);
if (tailChar == ';') {
break;
}
if (tailChar == '/')
{
tailChar = '.';
}
buffer.append(tailChar);
tailIdx++;
}
idx = tailIdx;
}
break;
case '[':
{
arrayCount++;
addSepr = false;
}
break;
case ')':
{
if (idx == argEnd - 1) {
buffer.append(")");
} else {
// leave room for return type
buffer.append(") ");
}
addSepr = false;
}
break;
default:
{
addSepr = false;
}
}
idx++;
if (idx < argEnd) {
sepr = ",";
} else {
sepr = "";
}
}
return buffer.toString();
}
/**
* split off the method name preceding the signature and return it
* @param targetMethod - the unqualified method name, possibly including signature
* @return the method name
*/
public static String parseMethodName(String targetMethod) {
int sigIdx = targetMethod.indexOf("(");
if (sigIdx > 0) {
return targetMethod.substring(0, sigIdx).trim();
} else {
return targetMethod;
}
}
/**
* split off the signature following the method name and return it
* @param targetMethod - the unqualified method name, possibly including signature
* @return the signature
*/
public static String parseMethodDescriptor(String targetMethod) {
int descIdx = targetMethod.indexOf("(");
if (descIdx >= 0) {
String desc = targetMethod.substring(descIdx, targetMethod.length()).trim();
return externalizeDescriptor(desc);
} else {
return "";
}
}
}

View File

@ -0,0 +1,33 @@
package com.taobao.arthas.bytekit.asm.binding;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
public class ArgNamesBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
String[] parameterNames = bindingContext.getMethodProcessor().getParameterNames();
AsmOpUtils.push(instructions, parameterNames.length);
AsmOpUtils.newArray(instructions, AsmOpUtils.STRING_TYPE);
for(int i = 0; i < parameterNames.length; ++i) {
AsmOpUtils.dup(instructions);
AsmOpUtils.push(instructions, i);
AsmOpUtils.push(instructions, parameterNames[i]);
AsmOpUtils.arrayStore(instructions, AsmOpUtils.STRING_TYPE);
}
}
@Override
public Type getType(BindingContext bindingContext) {
return AsmOpUtils.STRING_ARRAY_TYPE;
}
}

View File

@ -0,0 +1,20 @@
package com.taobao.arthas.bytekit.asm.binding;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
public class ArgsBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
AsmOpUtils.loadArgArray(instructions, bindingContext.getMethodProcessor().getMethodNode());
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(Object[].class);
}
}

View File

@ -0,0 +1,50 @@
package com.taobao.arthas.bytekit.asm.binding;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
/**
* TODO 这个判断是否要从stack上取数据要看 其它的binding是否需要 是否 optional这个应该是由 ArrayBinding 整体设定
* @author hengyunabc
*
*/
public class ArrayBinding extends Binding{
// TODO 数组的 type是什么
// private Type type;
List<Binding> bindingList = new ArrayList<Binding>();
public ArrayBinding(List<Binding> bindingList) {
this.bindingList = bindingList;
}
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
AsmOpUtils.push(instructions, bindingList.size());
AsmOpUtils.newArray(instructions, AsmOpUtils.OBJECT_TYPE);
for(int i = 0; i < bindingList.size(); ++i) {
AsmOpUtils.dup(instructions);
AsmOpUtils.push(instructions, i);
Binding binding = bindingList.get(i);
binding.pushOntoStack(instructions, bindingContext);
AsmOpUtils.box(instructions, binding.getType(bindingContext));
AsmOpUtils.arrayStore(instructions, AsmOpUtils.OBJECT_TYPE);
}
}
@Override
public Type getType(BindingContext bindingContext) {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -0,0 +1,323 @@
package com.taobao.arthas.bytekit.asm.binding;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;
import com.taobao.arthas.bytekit.asm.binding.annotation.BindingParser;
import com.taobao.arthas.bytekit.asm.binding.annotation.BindingParserHandler;
public abstract class Binding {
/**
* 是否可选的当不符合条件或者获取不到值时会转为 null这个不支持原始类型就像java.util.Optional 一样
* @return
*/
public boolean optional() {
return false;
}
/**
* 检查当前条件下这个binding是否可以工作比如检查field是否有这个field
* @return
*/
public boolean check(BindingContext bindingContext) {
return true;
}
/**
* 把这个binding本身放到栈上
* @param instructions
* @param bindingContext
*/
public abstract void pushOntoStack(InsnList instructions, BindingContext bindingContext);
public abstract Type getType( BindingContext bindingContext);
public boolean fromStack() {
return false;
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = ArgsBindingParser.class)
public @interface Args {
boolean optional() default false;
}
public static class ArgsBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new ArgsBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = ArgNamesBindingParser.class)
public @interface ArgNames {
boolean optional() default false;
}
public static class ArgNamesBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new ArgNamesBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = LocalVarsBindingParser.class)
public @interface LocalVars {
boolean optional() default false;
}
public static class LocalVarsBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new LocalVarsBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = LocalVarNamesBindingParser.class)
public @interface LocalVarNames {
boolean optional() default false;
}
public static class LocalVarNamesBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new LocalVarNamesBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = ClassBindingParser.class)
public @interface Class {
boolean optional() default false;
}
public static class ClassBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new ClassBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = FieldBindingParser.class)
public @interface Field {
boolean optional() default false;
java.lang.Class<?> owner() default Void.class;
java.lang.Class<?> type() default Void.class;
String name();
boolean isStatic() default false;
boolean box() default false;
}
public static class FieldBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
Field field = (Field) annotation;
Type ownerType = Type.getType(field.owner());
if(field.owner().equals(Void.class)) {
ownerType = null;
}
Type fieldType = Type.getType(field.type());
if(field.type().equals(Void.class)) {
fieldType = null;
}
return new FieldBinding(ownerType, field.name(), fieldType,
field.isStatic(), field.box());
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = InvokeArgsBindingParser.class)
public @interface InvokeArgs {
boolean optional() default false;
}
public static class InvokeArgsBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new InvokeArgsBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = InvokeReturnBindingParser.class)
public @interface InvokeReturn {
boolean optional() default false;
}
public static class InvokeReturnBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new InvokeReturnBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = InvokeMethodDeclarationBindingParser.class)
public @interface InvokeMethodDeclaration {
boolean optional() default false;
}
public static class InvokeMethodDeclarationBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new InvokeMethodDeclarationBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = MethodBindingParser.class)
public @interface Method {
boolean optional() default false;
}
public static class MethodBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new MethodBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = ReturnBindingParser.class)
public @interface Return {
boolean optional() default false;
}
public static class ReturnBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new ReturnBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = ThisBindingParser.class)
public @interface This {
}
public static class ThisBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new ThisBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = ThrowableBindingParser.class)
public @interface Throwable {
boolean optional() default false;
}
public static class ThrowableBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new ThrowableBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = LineBindingParser.class)
public @interface Line {
boolean optional() default false;
}
public static class LineBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new LineBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = MonitorBindingParser.class)
public @interface Monitor {
boolean optional() default false;
}
public static class MonitorBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new MonitorBinding();
}
}
}

View File

@ -0,0 +1,41 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.asm.location.Location;
public class BindingContext {
private MethodProcessor methodProcessor;
private Location location;
private StackSaver stackSaver;
public BindingContext(Location location, MethodProcessor methodProcessor, StackSaver stackSaver) {
this.location = location;
this.methodProcessor = methodProcessor;
this.stackSaver = stackSaver;
}
public MethodProcessor getMethodProcessor() {
return methodProcessor;
}
public void setMethodProcessor(MethodProcessor methodProcessor) {
this.methodProcessor = methodProcessor;
}
public Location getLocation() {
return location;
}
public void setLocation(Location location) {
this.location = location;
}
public StackSaver getStackSaver() {
return stackSaver;
}
public void setStackSaver(StackSaver stackSaver) {
this.stackSaver = stackSaver;
}
}

View File

@ -0,0 +1,21 @@
package com.taobao.arthas.bytekit.asm.binding;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
public class ClassBinding extends Binding{
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
String owner = bindingContext.getMethodProcessor().getOwner();
AsmOpUtils.ldc(instructions, Type.getObjectType(owner));
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(Class.class);
}
}

View File

@ -0,0 +1,95 @@
package com.taobao.arthas.bytekit.asm.binding;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.InsnList;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
import com.taobao.arthas.bytekit.utils.AsmUtils;
public class FieldBinding extends Binding {
/**
* maybe null
*/
private Type owner;
private boolean box = false;
private String name;
private boolean isStatic = false;
/**
* maybe null
*/
private Type type;
public FieldBinding(Type owner, String name, Type type, boolean isStatic, boolean box) {
this.owner = owner;
this.name = name;
this.isStatic = isStatic;
this.box = box;
this.type = type;
}
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
Type onwerType = owner;
Type fieldType = type;
boolean fieldIsStatic = isStatic;
if (owner == null) {
onwerType = Type.getObjectType(bindingContext.getMethodProcessor().getOwner());
}
// 当type是null里需要从ClassNode里查找到files确定type
MethodProcessor methodProcessor = bindingContext.getMethodProcessor();
if (fieldType == null) {
ClassNode classNode = methodProcessor.getClassNode();
if (classNode == null) {
throw new IllegalArgumentException(
"classNode is null, cann not get owner type. FieldBinding name:" + name);
}
FieldNode field = AsmUtils.findField(classNode.fields, name);
if (field == null) {
throw new IllegalArgumentException("can not find field in ClassNode. FieldBinding name:" + name);
}
fieldType = Type.getType(field.desc);
if ((field.access & Opcodes.ACC_STATIC) != 0) {
fieldIsStatic = true;
}else {
fieldIsStatic = false;
}
}
if (fieldIsStatic) {
AsmOpUtils.getStatic(instructions, onwerType, name, fieldType);
} else {
methodProcessor.loadThis(instructions);
AsmOpUtils.getField(instructions, onwerType, name, fieldType);
}
if (box) {
AsmOpUtils.box(instructions, fieldType);
}
}
@Override
public Type getType(BindingContext bindingContext) {
Type fieldType = type;
if (fieldType == null) {
ClassNode classNode = bindingContext.getMethodProcessor().getClassNode();
if (classNode == null) {
throw new IllegalArgumentException(
"classNode is null, cann not get owner type. FieldBinding name:" + name);
}
FieldNode field = AsmUtils.findField(classNode.fields, name);
if (field == null) {
throw new IllegalArgumentException("can not find field in ClassNode. FieldBinding name:" + name);
}
fieldType = Type.getType(field.desc);
}
return fieldType;
}
}

View File

@ -0,0 +1,36 @@
package com.taobao.arthas.bytekit.asm.binding;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
public class IntBinding extends Binding {
private int value;
private boolean box = true;
public IntBinding(int value) {
this(value, true);
}
public IntBinding(int value, boolean box) {
this.value = value;
this.box = box;
}
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
AsmOpUtils.push(instructions, value);
if (box) {
AsmOpUtils.box(instructions, Type.INT_TYPE);
}
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.INT_TYPE;
}
}

View File

@ -0,0 +1,47 @@
package com.taobao.arthas.bytekit.asm.binding;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LocalVariableNode;
import com.taobao.arthas.bytekit.asm.location.Location;
import com.taobao.arthas.bytekit.asm.location.Location.InvokeLocation;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
/**
* invoke 传入的参数列表有严格的限制只能在 invoke 之前
*
* TODO static 函数时在数组前传一个null进去 不然不好区分是否 static 函数调用
*
* @author hengyunabc
*
*/
public class InvokeArgsBinding extends Binding {
@Override
public boolean fromStack() {
return true;
}
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
Location location = bindingContext.getLocation();
if(location instanceof InvokeLocation) {
InvokeLocation invokeLocation = (InvokeLocation) location;
if(invokeLocation.isWhenComplete()) {
throw new IllegalArgumentException("InvokeArgsBinding can not work on InvokeLocation whenComplete is true.");
}
}else {
throw new IllegalArgumentException("current location is not invoke location. location: " + location);
}
LocalVariableNode invokeArgsVariableNode = bindingContext.getMethodProcessor().initInvokeArgsVariableNode();
AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index);
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(Object[].class);
}
}

View File

@ -0,0 +1,38 @@
package com.taobao.arthas.bytekit.asm.binding;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
import com.taobao.arthas.bytekit.utils.AsmUtils;
/**
*
* @author hengyunabc
*
*/
public class InvokeMethodDeclarationBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
AbstractInsnNode insnNode = bindingContext.getLocation().getInsnNode();
if (insnNode instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
String methodDeclaration = AsmUtils.methodDeclaration(methodInsnNode);
AsmOpUtils.push(instructions, methodDeclaration);
} else {
throw new IllegalArgumentException(
"InvokeMethodDeclarationBinding location is not MethodInsnNode, insnNode: " + insnNode);
}
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(String.class);
}
}

View File

@ -0,0 +1,62 @@
package com.taobao.arthas.bytekit.asm.binding;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodInsnNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
import com.taobao.arthas.bytekit.utils.AsmUtils;
/**
* invoke 的返回值
* @author hengyunabc
*
*/
public class InvokeReturnBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
AbstractInsnNode insnNode = bindingContext.getLocation().getInsnNode();
MethodProcessor methodProcessor = bindingContext.getMethodProcessor();
if (insnNode instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
String uniqueNameForMethod = AsmUtils.uniqueNameForMethod(methodInsnNode.owner, methodInsnNode.name,
methodInsnNode.desc);
Type invokeReturnType = Type.getMethodType(methodInsnNode.desc).getReturnType();
if(invokeReturnType.equals(Type.VOID_TYPE)) {
AsmOpUtils.push(instructions, null);
}else {
LocalVariableNode invokeReturnVariableNode = methodProcessor.initInvokeReturnVariableNode(
uniqueNameForMethod, Type.getMethodType(methodInsnNode.desc).getReturnType());
AsmOpUtils.loadVar(instructions, invokeReturnType, invokeReturnVariableNode.index);
}
} else {
throw new IllegalArgumentException(
"InvokeReturnBinding location is not MethodInsnNode, insnNode: " + insnNode);
}
}
@Override
public boolean fromStack() {
return true;
}
@Override
public Type getType(BindingContext bindingContext) {
AbstractInsnNode insnNode = bindingContext.getLocation().getInsnNode();
if (insnNode instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
Type invokeReturnType = Type.getMethodType(methodInsnNode.desc).getReturnType();
return invokeReturnType;
} else {
throw new IllegalArgumentException(
"InvokeReturnBinding location is not MethodInsnNode, insnNode: " + insnNode);
}
}
}

View File

@ -0,0 +1,30 @@
package com.taobao.arthas.bytekit.asm.binding;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LineNumberNode;
import com.taobao.arthas.bytekit.asm.location.Location;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
public class LineBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
Location location = bindingContext.getLocation();
AbstractInsnNode insnNode = location.getInsnNode();
if (insnNode instanceof LineNumberNode) {
AsmOpUtils.push(instructions, ((LineNumberNode) insnNode).line);
} else {
throw new IllegalArgumentException("LineBinding location is not LineNumberNode, insnNode: " + insnNode);
}
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(int.class);
}
}

View File

@ -0,0 +1,38 @@
package com.taobao.arthas.bytekit.asm.binding;
import java.util.List;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LocalVariableNode;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
public class LocalVarNamesBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
AbstractInsnNode currentInsnNode = bindingContext.getLocation().getInsnNode();
List<LocalVariableNode> results = AsmOpUtils
.validVariables(bindingContext.getMethodProcessor().getMethodNode().localVariables, currentInsnNode);
AsmOpUtils.push(instructions, results.size());
AsmOpUtils.newArray(instructions, AsmOpUtils.STRING_TYPE);
for (int i = 0; i < results.size(); ++i) {
AsmOpUtils.dup(instructions);
AsmOpUtils.push(instructions, i);
AsmOpUtils.push(instructions, results.get(i).name);
AsmOpUtils.arrayStore(instructions, AsmOpUtils.STRING_TYPE);
}
}
@Override
public Type getType(BindingContext bindingContext) {
return AsmOpUtils.STRING_ARRAY_TYPE;
}
}

View File

@ -0,0 +1,49 @@
package com.taobao.arthas.bytekit.asm.binding;
import java.util.List;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LocalVariableNode;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
/**
* TODO 增加一个配置是否包含 method args
* @author hengyunabc
*
*/
public class LocalVarsBinding extends Binding{
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
AbstractInsnNode currentInsnNode = bindingContext.getLocation().getInsnNode();
List<LocalVariableNode> results = AsmOpUtils
.validVariables(bindingContext.getMethodProcessor().getMethodNode().localVariables, currentInsnNode);
AsmOpUtils.push(instructions, results.size());
AsmOpUtils.newArray(instructions, AsmOpUtils.OBJECT_TYPE);
for (int i = 0; i < results.size(); ++i) {
AsmOpUtils.dup(instructions);
AsmOpUtils.push(instructions, i);
LocalVariableNode variableNode = results.get(i);
AsmOpUtils.loadVar(instructions, Type.getType(variableNode.desc), variableNode.index);
AsmOpUtils.box(instructions, Type.getType(variableNode.desc));
AsmOpUtils.arrayStore(instructions, AsmOpUtils.OBJECT_TYPE);
}
}
@Override
public Type getType(BindingContext bindingContext) {
return AsmOpUtils.OBJECT_TYPE;
}
}

View File

@ -0,0 +1,52 @@
package com.taobao.arthas.bytekit.asm.binding;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodInsnNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
/**
* @author hengyunabc
*
*/
public class MethodBinding extends Binding{
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
// 先获取类本身的 class 再调用 getDeclaredMethod 它需要一个变长参数实际上要传一个数组
/**
* @see java.lang.Class.getDeclaredMethod(String, Class<?>...)
*/
MethodProcessor methodProcessor = bindingContext.getMethodProcessor();
AsmOpUtils.ldc(instructions, Type.getObjectType(methodProcessor.getOwner()));
AsmOpUtils.push(instructions, methodProcessor.getMethodNode().name);
Type[] argumentTypes = Type.getMethodType(methodProcessor.getMethodNode().desc).getArgumentTypes();
AsmOpUtils.push(instructions, argumentTypes.length);
AsmOpUtils.newArray(instructions, Type.getType(Class.class));
for(int i = 0; i < argumentTypes.length; ++i) {
AsmOpUtils.dup(instructions);
AsmOpUtils.push(instructions, i);
AsmOpUtils.ldc(instructions, argumentTypes[i]);
AsmOpUtils.arrayStore(instructions, Type.getType(Class.class));
}
MethodInsnNode declaredMethodInsnNode = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, Type.getType(Class.class).getInternalName(),
"getDeclaredMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false);
instructions.add(declaredMethodInsnNode);
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(java.lang.reflect.Method.class);
}
}

View File

@ -0,0 +1,32 @@
package com.taobao.arthas.bytekit.asm.binding;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
import com.taobao.arthas.bytekit.utils.AsmUtils;
/**
* 提供一个完整的 method 的string包含类名并不是desc用户可以自己提取descs method的定义前面是 public
* /static 这些关键字是有限的几个后面是 throws 的异常信息
*
* @author hengyunabc
*
*/
public class MethodDeclarationBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
MethodProcessor methodProcessor = bindingContext.getMethodProcessor();
AsmOpUtils.ldc(instructions, AsmUtils.methodDeclaration(Type.getObjectType(methodProcessor.getOwner()),
methodProcessor.getMethodNode()));
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(String.class);
}
}

View File

@ -0,0 +1,41 @@
package com.taobao.arthas.bytekit.asm.binding;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LocalVariableNode;
import com.taobao.arthas.bytekit.asm.location.Location;
import com.taobao.arthas.bytekit.asm.location.Location.SyncEnterLocation;
import com.taobao.arthas.bytekit.asm.location.Location.SyncExitLocation;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
public class MonitorBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
Location location = bindingContext.getLocation();
if (location.isWhenComplete()) {
throw new IllegalArgumentException("MonitorBinding only support location whenComplete is false.");
}
if (location instanceof SyncEnterLocation || location instanceof SyncExitLocation) {
LocalVariableNode monitorVariableNode = bindingContext.getMethodProcessor().initMonitorVariableNode();
AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_TYPE, monitorVariableNode.index);
} else {
throw new IllegalArgumentException(
"MonitorBinding only support SyncEnterLocation or SyncExitLocation. location: " + location);
}
}
@Override
public boolean fromStack() {
return true;
}
@Override
public Type getType(BindingContext bindingContext) {
return AsmOpUtils.OBJECT_TYPE;
}
}

View File

@ -0,0 +1,43 @@
package com.taobao.arthas.bytekit.asm.binding;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LocalVariableNode;
import com.taobao.arthas.bytekit.asm.location.Location;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
public class ReturnBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
//check location
Location location = bindingContext.getLocation();
if (!AsmOpUtils.isReturnCode(location.getInsnNode().getOpcode())) {
throw new IllegalArgumentException("current location is not return location. location: " + location);
}
Type returnType = bindingContext.getMethodProcessor().getReturnType();
if(returnType.equals(Type.VOID_TYPE)) {
AsmOpUtils.push(instructions, null);
}else {
LocalVariableNode returnVariableNode = bindingContext.getMethodProcessor().initReturnVariableNode();
AsmOpUtils.storeVar(instructions, returnType, returnVariableNode.index);
}
}
@Override
public boolean fromStack() {
return true;
}
@Override
public Type getType(BindingContext bindingContext) {
return bindingContext.getMethodProcessor().getReturnType();
}
}

View File

@ -0,0 +1,23 @@
package com.taobao.arthas.bytekit.asm.binding;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;
/**
* return/throw/invoke 等location时需要把栈上的值保存到locals里
* @author hengyunabc
*
*/
public interface StackSaver {
/**
* 有可能在两个地方被调用1: 在最开始保存栈上的值时 2: callback函数有返回值想更新这个值时stackSaver自己内部要保证保存的locals index是一致的
* @param instructions
* @param bindingContext
*/
public void store(InsnList instructions, BindingContext bindingContext);
public void load(InsnList instructions, BindingContext bindingContext);
public Type getType(BindingContext bindingContext);
}

View File

@ -0,0 +1,18 @@
package com.taobao.arthas.bytekit.asm.binding;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;
public class ThisBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
bindingContext.getMethodProcessor().loadThis(instructions);
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(Object.class);
}
}

View File

@ -0,0 +1,30 @@
package com.taobao.arthas.bytekit.asm.binding;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;
/**
* TODO 要检查 location 是否是合法的
* @author hengyunabc
*
*/
public class ThrowableBinding extends Binding {
@Override
public boolean fromStack() {
return true;
}
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
// TODO 这里从 StackSaver 里取是否合理
bindingContext.getStackSaver().load(instructions, bindingContext);
// 是否要 check cast ?
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(Throwable.class);
}
}

View File

@ -0,0 +1,11 @@
package com.taobao.arthas.bytekit.asm.binding.annotation;
import java.lang.annotation.Annotation;
import com.taobao.arthas.bytekit.asm.binding.Binding;
public interface BindingParser {
public Binding parse(Annotation annotation);
}

View File

@ -0,0 +1,15 @@
package com.taobao.arthas.bytekit.asm.binding.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.ANNOTATION_TYPE)
public @interface BindingParserHandler {
Class<? extends BindingParser> parser();
}

View File

@ -0,0 +1,5 @@
package com.taobao.arthas.bytekit.asm.interceptor;
public class EnterInteceptor {
}

View File

@ -0,0 +1,5 @@
package com.taobao.arthas.bytekit.asm.interceptor;
public class ExceptionInterceptor {
}

View File

@ -0,0 +1,5 @@
package com.taobao.arthas.bytekit.asm.interceptor;
public class ExitInterceptor {
}

View File

@ -0,0 +1,5 @@
package com.taobao.arthas.bytekit.asm.interceptor;
public interface Inteceptor {
}

View File

@ -0,0 +1,71 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import java.util.List;
import com.taobao.arthas.bytekit.asm.binding.Binding;
public class InterceptorMethodConfig {
private boolean inline;
private String owner;
private String methodName;
private String methodDesc;
private List<Binding> bindings;
/**
* 插入的代码用 try/catch 包围的异常类型
*/
private String suppress;
public boolean isInline() {
return inline;
}
public void setInline(boolean inline) {
this.inline = inline;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public String getMethodDesc() {
return methodDesc;
}
public void setMethodDesc(String methodDesc) {
this.methodDesc = methodDesc;
}
public List<Binding> getBindings() {
return bindings;
}
public void setBindings(List<Binding> bindings) {
this.bindings = bindings;
}
public String getSuppress() {
return suppress;
}
public void setSuppress(String suppress) {
this.suppress = suppress;
}
}

View File

@ -0,0 +1,240 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import java.util.List;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.JumpInsnNode;
import org.objectweb.asm.tree.LabelNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.asm.TryCatchBlock;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.binding.BindingContext;
import com.taobao.arthas.bytekit.asm.binding.StackSaver;
import com.taobao.arthas.bytekit.asm.location.Location;
import com.taobao.arthas.bytekit.asm.location.LocationMatcher;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
import com.taobao.arthas.bytekit.utils.AsmUtils;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class InterceptorProcessor {
private LocationMatcher locationMatcher;
/**
* 插入的回调函数的配置
*/
private InterceptorMethodConfig interceptorMethodConfig;
/**
* 插入的代码被 try/catch 包围的配置注意有一些location插入try/catch会可能失败因为不能确切知道栈上的情况
*/
private InterceptorMethodConfig exceptionHandlerConfig;
public void process(MethodProcessor methodProcessor) throws Exception {
List<Location> locations = locationMatcher.match(methodProcessor);
List<Binding> interceptorBindings = interceptorMethodConfig.getBindings();
for (Location location : locations) {
// 有三小段代码1: 保存当前栈上的值的 , 2: 插入的回调的 3恢复当前栈的
InsnList toInsert = new InsnList();
InsnList stackSaveInsnList = new InsnList();
InsnList stackLoadInsnList = new InsnList();
StackSaver stackSaver = null;
if(location.isStackNeedSave()) {
stackSaver = location.getStackSaver();
}
BindingContext bindingContext = new BindingContext(location, methodProcessor, stackSaver);
if(stackSaver != null) {
stackSaver.store(stackSaveInsnList, bindingContext);
stackSaver.load(stackLoadInsnList, bindingContext);
}
Type methodType = Type.getMethodType(interceptorMethodConfig.getMethodDesc());
Type[] argumentTypes = methodType.getArgumentTypes();
// 检查回调函数的参数和 binding数一致
if(interceptorBindings.size() != argumentTypes.length) {
throw new IllegalArgumentException("interceptorBindings size no equals with interceptorMethod args size.");
}
// 把当前栈上的数据保存起来
int fromStackBindingCount = 0;
for (Binding binding : interceptorBindings) {
if(binding.fromStack()) {
fromStackBindingCount++;
}
}
// 只允许一个binding从栈上保存数据
if(fromStackBindingCount > 1) {
throw new IllegalArgumentException("interceptorBindings have more than one from stack Binding.");
}
// 组装好要调用的 static 函数的参数
for(int i = 0 ; i < argumentTypes.length; ++i) {
Binding binding = interceptorBindings.get(i);
binding.pushOntoStack(toInsert, bindingContext);
// 检查 回调函数的参数类型看是否要box一下 检查是否原始类型就可以了
// 只有类型不一样时才需要判断比如两个都是 long则不用判断
Type bindingType = binding.getType(bindingContext);
if(!bindingType.equals(argumentTypes[i])) {
if(AsmOpUtils.needBox(bindingType)) {
AsmOpUtils.box(toInsert, binding.getType(bindingContext));
}
}
}
// TODO 要检查 binding 回调的函数的参数类型是否一致回调函数的类型可以是 Object或者super但是不允许一些明显的类型问题比如array转到int
toInsert.add(new MethodInsnNode(Opcodes.INVOKESTATIC, interceptorMethodConfig.getOwner(), interceptorMethodConfig.getMethodName(),
interceptorMethodConfig.getMethodDesc(), false));
if (!methodType.getReturnType().equals(Type.VOID_TYPE)) {
if (location.canChangeByReturn()) {
// 当回调函数有返回值时需要更新到之前保存的栈上
// TODO 这里应该有 type 的问题需要检查是否要 box
Type returnType = methodType.getReturnType();
Type stackSaverType = stackSaver.getType(bindingContext);
if (!returnType.equals(stackSaverType)) {
AsmOpUtils.unbox(toInsert, stackSaverType);
}
stackSaver.store(toInsert, bindingContext);
} else {
// 没有使用到回调函数的返回值的话则需要从栈上清理掉
int size = methodType.getReturnType().getSize();
if (size == 1) {
AsmOpUtils.pop(toInsert);
} else if (size == 2) {
AsmOpUtils.pop2(toInsert);
}
}
}
TryCatchBlock errorHandlerTryCatchBlock = null;
// 生成的代码用try/catch包围起来
if( exceptionHandlerConfig != null) {
LabelNode gotoDest = new LabelNode();
errorHandlerTryCatchBlock = new TryCatchBlock(methodProcessor.getMethodNode(), exceptionHandlerConfig.getSuppress());
toInsert.insertBefore(toInsert.getFirst(), errorHandlerTryCatchBlock.getStartLabelNode());
toInsert.add(new JumpInsnNode(Opcodes.GOTO, gotoDest));
toInsert.add(errorHandlerTryCatchBlock.getEndLabelNode());
// 这里怎么把栈上的数据保存起来还是强制回调函数的第一个参数是 exception后面的binding可以随便搞
// MethodInsnNode printStackTrace = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable", "printStackTrace", "()V", false);
// toInsert.add(printStackTrace);
errorHandler(methodProcessor, toInsert);
toInsert.add(gotoDest);
}
// System.err.println(Decompiler.toString(toInsert));
stackSaveInsnList.add(toInsert);
stackSaveInsnList.add(stackLoadInsnList);
if (location.isWhenComplete()) {
methodProcessor.getMethodNode().instructions.insert(location.getInsnNode(), stackSaveInsnList);
}else {
methodProcessor.getMethodNode().instructions.insertBefore(location.getInsnNode(), stackSaveInsnList);
}
if( exceptionHandlerConfig != null) {
errorHandlerTryCatchBlock.sort();
}
// inline callback
if(interceptorMethodConfig.isInline()) {
Class<?> forName = Class.forName(Type.getObjectType(interceptorMethodConfig.getOwner()).getClassName());
MethodNode toInlineMethodNode = AsmUtils.findMethod(AsmUtils.loadClass(forName).methods, interceptorMethodConfig.getMethodName(), interceptorMethodConfig.getMethodDesc());
methodProcessor.inline(interceptorMethodConfig.getOwner(), toInlineMethodNode);
}
if(exceptionHandlerConfig != null && exceptionHandlerConfig.isInline()) {
Class<?> forName = Class.forName(Type.getObjectType(exceptionHandlerConfig.getOwner()).getClassName());
MethodNode toInlineMethodNode = AsmUtils.findMethod(AsmUtils.loadClass(forName).methods, exceptionHandlerConfig.getMethodName(), exceptionHandlerConfig.getMethodDesc());
methodProcessor.inline(exceptionHandlerConfig.getOwner(), toInlineMethodNode);
}
// System.err.println(Decompiler.toString(methodProcessor.getMethodNode()));
// System.err.println(AsmUtils.toASMCode(methodProcessor.getMethodNode()));
}
}
private void errorHandler(MethodProcessor methodProcessor, InsnList insnList) {
// MethodInsnNode printStackTrace = new MethodInsnNode(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable", "printStackTrace", "()V", false);
// insnList.add(printStackTrace);
// 第一个参数要求是 throwable 或者一个exception
// 有很多 binding 并不能使用的因为location不生效
BindingContext bindingContext = new BindingContext(null, methodProcessor, null);
Type methodType = Type.getMethodType(this.exceptionHandlerConfig.getMethodDesc());
Type[] argumentTypes = methodType.getArgumentTypes();
List<Binding> bindings = this.exceptionHandlerConfig.getBindings();
if(bindings.size() + 1 != argumentTypes.length) {
throw new IllegalArgumentException("errorHandler bindings size do not match error method args size.");
}
if(!argumentTypes[0].equals(Type.getType(Throwable.class))) {
throw new IllegalArgumentException("errorHandler method first arg type must be Throwable.");
}
// 组装好要调用的 static 函数的参数
for(Binding binding: bindings) {
if(binding.fromStack()) {
throw new IllegalArgumentException("errorHandler binding can not load value from stack!");
}
binding.pushOntoStack(insnList, bindingContext);
// 检查 回调函数的参数类型看是否要box一下 检查是否原始类型就可以了
if(AsmOpUtils.needBox(binding.getType(bindingContext))) {
AsmOpUtils.box(insnList, binding.getType(bindingContext));
}
}
insnList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, exceptionHandlerConfig.getOwner(), exceptionHandlerConfig.getMethodName(),
exceptionHandlerConfig.getMethodDesc(), false));
int size = methodType.getReturnType().getSize();
if (size == 1) {
AsmOpUtils.pop(insnList);
} else if (size == 2) {
AsmOpUtils.pop2(insnList);
}
}
public LocationMatcher getLocationMatcher() {
return locationMatcher;
}
public void setLocationMatcher(LocationMatcher locationMatcher) {
this.locationMatcher = locationMatcher;
}
public InterceptorMethodConfig getInterceptorMethodConfig() {
return interceptorMethodConfig;
}
public void setInterceptorMethodConfig(InterceptorMethodConfig interceptorMethodConfig) {
this.interceptorMethodConfig = interceptorMethodConfig;
}
public InterceptorMethodConfig getExceptionHandlerConfig() {
return exceptionHandlerConfig;
}
public void setExceptionHandlerConfig(InterceptorMethodConfig exceptionHandlerConfig) {
this.exceptionHandlerConfig = exceptionHandlerConfig;
}
}

View File

@ -0,0 +1,65 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.List;
import org.objectweb.asm.Type;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorMethodConfig;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtEnter.EnterInterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.location.EnterLocationMatcher;
import com.taobao.arthas.bytekit.asm.location.LocationMatcher;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.METHOD)
@InterceptorParserHander(parserHander = EnterInterceptorProcessorParser.class)
public @interface AtEnter {
boolean inline() default true;
Class<? extends Throwable> suppress() default None.class;
Class<?> suppressHandler() default Void.class;
class EnterInterceptorProcessorParser implements InterceptorProcessorParser {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
interceptorMethodConfig.setOwner(Type.getInternalName(method.getDeclaringClass()));
interceptorMethodConfig.setMethodName(method.getName());
interceptorMethodConfig.setMethodDesc(Type.getMethodDescriptor(method));
LocationMatcher locationMatcher = new EnterLocationMatcher();
interceptorProcessor.setLocationMatcher(locationMatcher);
AtEnter atEnter = (AtEnter) annotationOnMethod;
interceptorMethodConfig.setInline(atEnter.inline());
List<Binding> bindings = BindingParserUtils.parseBindings(method);
interceptorMethodConfig.setBindings(bindings);
InterceptorMethodConfig errorHandlerMethodConfig = ExceptionHandlerUtils
.errorHandlerMethodConfig(atEnter.suppress(), atEnter.suppressHandler());
if (errorHandlerMethodConfig != null) {
interceptorProcessor.setExceptionHandlerConfig(errorHandlerMethodConfig);
}
return interceptorProcessor;
}
}
}

View File

@ -0,0 +1,69 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.List;
import org.objectweb.asm.Type;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorMethodConfig;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExceptionExit.ExceptionExitInterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.location.ExceptionExitLocationMatcher;
import com.taobao.arthas.bytekit.asm.location.LocationMatcher;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.METHOD)
@InterceptorParserHander(parserHander = ExceptionExitInterceptorProcessorParser.class)
public @interface AtExceptionExit {
boolean inline() default true;
Class<? extends Throwable> suppress() default None.class;
Class<?> suppressHandler() default Void.class;
Class<? extends Throwable> onException() default Throwable.class;
class ExceptionExitInterceptorProcessorParser implements InterceptorProcessorParser {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
interceptorMethodConfig.setOwner(Type.getInternalName(method.getDeclaringClass()));
interceptorMethodConfig.setMethodName(method.getName());
interceptorMethodConfig.setMethodDesc(Type.getMethodDescriptor(method));
AtExceptionExit atExceptionExit = (AtExceptionExit) annotationOnMethod;
interceptorMethodConfig.setInline(atExceptionExit.inline());
LocationMatcher locationMatcher = new ExceptionExitLocationMatcher(Type.getInternalName(atExceptionExit.onException()));;
interceptorProcessor.setLocationMatcher(locationMatcher);
List<Binding> bindings = BindingParserUtils.parseBindings(method);
interceptorMethodConfig.setBindings(bindings);
InterceptorMethodConfig errorHandlerMethodConfig = ExceptionHandlerUtils
.errorHandlerMethodConfig(atExceptionExit.suppress(), atExceptionExit.suppressHandler());
if (errorHandlerMethodConfig != null) {
interceptorProcessor.setExceptionHandlerConfig(errorHandlerMethodConfig);
}
return interceptorProcessor;
}
}
}

View File

@ -0,0 +1,63 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.List;
import org.objectweb.asm.Type;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorMethodConfig;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExit.ExitInterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.location.ExitLocationMatcher;
import com.taobao.arthas.bytekit.asm.location.LocationMatcher;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.METHOD)
@InterceptorParserHander(parserHander = ExitInterceptorProcessorParser.class)
public @interface AtExit {
boolean inline() default true;
Class<? extends Throwable> suppress() default None.class;
Class<?> suppressHandler() default Void.class;
class ExitInterceptorProcessorParser implements InterceptorProcessorParser {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
interceptorMethodConfig.setOwner(Type.getInternalName(method.getDeclaringClass()));
interceptorMethodConfig.setMethodName(method.getName());
interceptorMethodConfig.setMethodDesc(Type.getMethodDescriptor(method));
LocationMatcher locationMatcher = new ExitLocationMatcher();
interceptorProcessor.setLocationMatcher(locationMatcher);
AtExit atExit = (AtExit) annotationOnMethod;
interceptorMethodConfig.setInline(atExit.inline());
List<Binding> bindings = BindingParserUtils.parseBindings(method);
interceptorMethodConfig.setBindings(bindings);
InterceptorMethodConfig errorHandlerMethodConfig = ExceptionHandlerUtils
.errorHandlerMethodConfig(atExit.suppress(), atExit.suppressHandler());
if (errorHandlerMethodConfig != null) {
interceptorProcessor.setExceptionHandlerConfig(errorHandlerMethodConfig);
}
return interceptorProcessor;
}
}
}

View File

@ -0,0 +1,91 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.List;
import org.objectweb.asm.Type;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorMethodConfig;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtFieldAccess.FieldAccessInterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.location.FieldAccessLocationMatcher;
import com.taobao.arthas.bytekit.asm.location.Location;
import com.taobao.arthas.bytekit.asm.location.LocationMatcher;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.METHOD)
@InterceptorParserHander(parserHander = FieldAccessInterceptorProcessorParser.class)
public @interface AtFieldAccess {
boolean inline() default true;
Class<? extends Throwable> suppress() default None.class;
Class<?> suppressHandler() default Void.class;
java.lang.Class<?> owner() default Void.class;
java.lang.Class<?> type() default Void.class;
String name();
int count() default -1;
int flags() default Location.ACCESS_READ | Location.ACCESS_WRITE;
boolean whenComplete() default false;
class FieldAccessInterceptorProcessorParser implements InterceptorProcessorParser {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
interceptorMethodConfig.setOwner(Type.getInternalName(method.getDeclaringClass()));
interceptorMethodConfig.setMethodName(method.getName());
interceptorMethodConfig.setMethodDesc(Type.getMethodDescriptor(method));
AtFieldAccess atFieldAccess = (AtFieldAccess) annotationOnMethod;
String ownerClass = null;
String fieldDesc = null;
if(! atFieldAccess.owner().equals(Void.class)) {
ownerClass = Type.getType(atFieldAccess.owner()).getInternalName();
}
if(!atFieldAccess.type().equals(Void.class)) {
fieldDesc = Type.getType(atFieldAccess.type()).getDescriptor();
}
LocationMatcher locationMatcher = new FieldAccessLocationMatcher(
ownerClass,
fieldDesc, atFieldAccess.name(), atFieldAccess.count(),
atFieldAccess.flags(), atFieldAccess.whenComplete());
interceptorProcessor.setLocationMatcher(locationMatcher);
interceptorMethodConfig.setInline(atFieldAccess.inline());
List<Binding> bindings = BindingParserUtils.parseBindings(method);
interceptorMethodConfig.setBindings(bindings);
InterceptorMethodConfig errorHandlerMethodConfig = ExceptionHandlerUtils
.errorHandlerMethodConfig(atFieldAccess.suppress(), atFieldAccess.suppressHandler());
if (errorHandlerMethodConfig != null) {
interceptorProcessor.setExceptionHandlerConfig(errorHandlerMethodConfig);
}
return interceptorProcessor;
}
}
}

View File

@ -0,0 +1,94 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Type;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorMethodConfig;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtInvoke.InvokeInterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.location.InvokeLocationMatcher;
import com.taobao.arthas.bytekit.asm.location.LocationMatcher;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.METHOD)
@InterceptorParserHander(parserHander = InvokeInterceptorProcessorParser.class)
public @interface AtInvoke {
boolean inline() default true;
Class<? extends Throwable> suppress() default None.class;
Class<?> suppressHandler() default Void.class;
Class<?> owner() default Void.class;
String name();
String desc() default "";
int count() default -1;
boolean whenComplete() default false;
String[] excludes() default {};
class InvokeInterceptorProcessorParser implements InterceptorProcessorParser {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
interceptorMethodConfig.setOwner(Type.getInternalName(method.getDeclaringClass()));
interceptorMethodConfig.setMethodName(method.getName());
interceptorMethodConfig.setMethodDesc(Type.getMethodDescriptor(method));
AtInvoke atInvoke = (AtInvoke) annotationOnMethod;
String owner = null;
String desc = null;
if (!atInvoke.owner().equals(Void.class)) {
owner = Type.getType(atInvoke.owner()).getInternalName();
}
if (atInvoke.desc().isEmpty()) {
desc = null;
}
List<String> excludes = new ArrayList<String>();
for (String exclude : atInvoke.excludes()) {
excludes.add(exclude);
}
LocationMatcher locationMatcher = new InvokeLocationMatcher(owner, atInvoke.name(), desc, atInvoke.count(),
atInvoke.whenComplete(), excludes);
interceptorProcessor.setLocationMatcher(locationMatcher);
interceptorMethodConfig.setInline(atInvoke.inline());
List<Binding> bindings = BindingParserUtils.parseBindings(method);
interceptorMethodConfig.setBindings(bindings);
InterceptorMethodConfig errorHandlerMethodConfig = ExceptionHandlerUtils
.errorHandlerMethodConfig(atInvoke.suppress(), atInvoke.suppressHandler());
if (errorHandlerMethodConfig != null) {
interceptorProcessor.setExceptionHandlerConfig(errorHandlerMethodConfig);
}
return interceptorProcessor;
}
}
}

View File

@ -0,0 +1,67 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.List;
import org.objectweb.asm.Type;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorMethodConfig;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtLine.LineInterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.location.LineLocationMatcher;
import com.taobao.arthas.bytekit.asm.location.LocationMatcher;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.METHOD)
@InterceptorParserHander(parserHander = LineInterceptorProcessorParser.class)
public @interface AtLine {
boolean inline() default true;
Class<? extends Throwable> suppress() default None.class;
Class<?> suppressHandler() default Void.class;
int[] lines();
class LineInterceptorProcessorParser implements InterceptorProcessorParser {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
interceptorMethodConfig.setOwner(Type.getInternalName(method.getDeclaringClass()));
interceptorMethodConfig.setMethodName(method.getName());
interceptorMethodConfig.setMethodDesc(Type.getMethodDescriptor(method));
AtLine atLine = (AtLine) annotationOnMethod;
LocationMatcher locationMatcher = new LineLocationMatcher(atLine.lines());
interceptorProcessor.setLocationMatcher(locationMatcher);
interceptorMethodConfig.setInline(atLine.inline());
List<Binding> bindings = BindingParserUtils.parseBindings(method);
interceptorMethodConfig.setBindings(bindings);
InterceptorMethodConfig errorHandlerMethodConfig = ExceptionHandlerUtils
.errorHandlerMethodConfig(atLine.suppress(), atLine.suppressHandler());
if (errorHandlerMethodConfig != null) {
interceptorProcessor.setExceptionHandlerConfig(errorHandlerMethodConfig);
}
return interceptorProcessor;
}
}
}

View File

@ -0,0 +1,70 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.List;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorMethodConfig;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtSyncEnter.SyncEnterInterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.location.LocationMatcher;
import com.taobao.arthas.bytekit.asm.location.SyncLocationMatcher;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.METHOD)
@InterceptorParserHander(parserHander = SyncEnterInterceptorProcessorParser.class)
public @interface AtSyncEnter {
boolean inline() default true;
Class<? extends Throwable> suppress() default None.class;
Class<?> suppressHandler() default Void.class;
int count() default -1;
boolean whenComplete() default false;
class SyncEnterInterceptorProcessorParser implements InterceptorProcessorParser {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
interceptorMethodConfig.setOwner(Type.getInternalName(method.getDeclaringClass()));
interceptorMethodConfig.setMethodName(method.getName());
interceptorMethodConfig.setMethodDesc(Type.getMethodDescriptor(method));
AtSyncEnter atSyncEnter = (AtSyncEnter) annotationOnMethod;
LocationMatcher locationMatcher = new SyncLocationMatcher(Opcodes.MONITORENTER, atSyncEnter.count(), atSyncEnter.whenComplete());
interceptorProcessor.setLocationMatcher(locationMatcher);
interceptorMethodConfig.setInline(atSyncEnter.inline());
List<Binding> bindings = BindingParserUtils.parseBindings(method);
interceptorMethodConfig.setBindings(bindings);
InterceptorMethodConfig errorHandlerMethodConfig = ExceptionHandlerUtils
.errorHandlerMethodConfig(atSyncEnter.suppress(), atSyncEnter.suppressHandler());
if (errorHandlerMethodConfig != null) {
interceptorProcessor.setExceptionHandlerConfig(errorHandlerMethodConfig);
}
return interceptorProcessor;
}
}
}

View File

@ -0,0 +1,70 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.List;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorMethodConfig;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtSyncExit.SyncExitInterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.location.LocationMatcher;
import com.taobao.arthas.bytekit.asm.location.SyncLocationMatcher;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.METHOD)
@InterceptorParserHander(parserHander = SyncExitInterceptorProcessorParser.class)
public @interface AtSyncExit {
boolean inline() default true;
Class<? extends Throwable> suppress() default None.class;
Class<?> suppressHandler() default Void.class;
int count() default -1;
boolean whenComplete() default false;
class SyncExitInterceptorProcessorParser implements InterceptorProcessorParser {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
interceptorMethodConfig.setOwner(Type.getInternalName(method.getDeclaringClass()));
interceptorMethodConfig.setMethodName(method.getName());
interceptorMethodConfig.setMethodDesc(Type.getMethodDescriptor(method));
AtSyncExit atSyncExit = (AtSyncExit) annotationOnMethod;
LocationMatcher locationMatcher = new SyncLocationMatcher(Opcodes.MONITOREXIT, atSyncExit.count(), atSyncExit.whenComplete());
interceptorProcessor.setLocationMatcher(locationMatcher);
interceptorMethodConfig.setInline(atSyncExit.inline());
List<Binding> bindings = BindingParserUtils.parseBindings(method);
interceptorMethodConfig.setBindings(bindings);
InterceptorMethodConfig errorHandlerMethodConfig = ExceptionHandlerUtils
.errorHandlerMethodConfig(atSyncExit.suppress(), atSyncExit.suppressHandler());
if (errorHandlerMethodConfig != null) {
interceptorProcessor.setExceptionHandlerConfig(errorHandlerMethodConfig);
}
return interceptorProcessor;
}
}
}

View File

@ -0,0 +1,67 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Method;
import java.util.List;
import org.objectweb.asm.Type;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorMethodConfig;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtThrow.ThrowInterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser;
import com.taobao.arthas.bytekit.asm.location.LocationMatcher;
import com.taobao.arthas.bytekit.asm.location.ThrowLocationMatcher;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.METHOD)
@InterceptorParserHander(parserHander = ThrowInterceptorProcessorParser.class)
public @interface AtThrow {
boolean inline() default true;
Class<? extends Throwable> suppress() default None.class;
Class<?> suppressHandler() default Void.class;
int count() default -1;
class ThrowInterceptorProcessorParser implements InterceptorProcessorParser {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
InterceptorProcessor interceptorProcessor = new InterceptorProcessor();
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
interceptorMethodConfig.setOwner(Type.getInternalName(method.getDeclaringClass()));
interceptorMethodConfig.setMethodName(method.getName());
interceptorMethodConfig.setMethodDesc(Type.getMethodDescriptor(method));
AtThrow atThrow = (AtThrow) annotationOnMethod;
LocationMatcher locationMatcher = new ThrowLocationMatcher(atThrow.count());
interceptorProcessor.setLocationMatcher(locationMatcher);
interceptorMethodConfig.setInline(atThrow.inline());
List<Binding> bindings = BindingParserUtils.parseBindings(method);
interceptorMethodConfig.setBindings(bindings);
InterceptorMethodConfig errorHandlerMethodConfig = ExceptionHandlerUtils
.errorHandlerMethodConfig(atThrow.suppress(), atThrow.suppressHandler());
if (errorHandlerMethodConfig != null) {
interceptorProcessor.setExceptionHandlerConfig(errorHandlerMethodConfig);
}
return interceptorProcessor;
}
}
}

View File

@ -0,0 +1,36 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.binding.annotation.BindingParser;
import com.taobao.arthas.bytekit.asm.binding.annotation.BindingParserHandler;
import com.taobao.arthas.bytekit.utils.InstanceUtils;
public class BindingParserUtils {
public static List<Binding> parseBindings(Method method) {
// parameter 里解析出来 binding
List<Binding> bindings = new ArrayList<Binding>();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (int parameterIndex = 0; parameterIndex < parameterAnnotations.length; ++parameterIndex) {
Annotation[] annotationsOnParameter = parameterAnnotations[parameterIndex];
for (int j = 0; j < annotationsOnParameter.length; ++j) {
Annotation[] annotationsOnBinding = annotationsOnParameter[j].annotationType().getAnnotations();
for (Annotation annotationOnBinding : annotationsOnBinding) {
if (BindingParserHandler.class.isAssignableFrom(annotationOnBinding.annotationType())) {
BindingParserHandler bindingParserHandler = (BindingParserHandler) annotationOnBinding;
BindingParser bindingParser = InstanceUtils.newInstance(bindingParserHandler.parser());
Binding binding = bindingParser.parse(annotationsOnParameter[j]);
bindings.add(binding);
}
}
}
}
return bindings;
}
}

View File

@ -0,0 +1,10 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
public class EmptySuppressHandler {
@ExceptionHandler
public static void onSuppress() {
}
}

View File

@ -0,0 +1,13 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.METHOD)
public @interface ExceptionHandler {
boolean inline() default true;
}

View File

@ -0,0 +1,85 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.List;
import org.objectweb.asm.Type;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodCallback;
import org.springframework.util.ReflectionUtils.MethodFilter;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.binding.ThrowableBinding;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorMethodConfig;
public class ExceptionHandlerUtils {
public static InterceptorMethodConfig errorHandlerMethodConfig(Class<?> suppress, Class<?> handlerClass) {
// TODO 要解析 errorHander Class里的内容
final InterceptorMethodConfig errorHandlerMethodConfig = new InterceptorMethodConfig();
if(suppress.equals(None.class)) {
suppress = Throwable.class;
}
errorHandlerMethodConfig.setSuppress(Type.getType(suppress).getInternalName());
if (!handlerClass.equals(Void.class)) {
// find method with @ExceptionHandler
ReflectionUtils.doWithMethods(handlerClass, new MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
for (Annotation onMethodAnnotation : method.getAnnotations()) {
if (ExceptionHandler.class.isAssignableFrom(onMethodAnnotation.annotationType())) {
if (!Modifier.isStatic(method.getModifiers())) {
throw new IllegalArgumentException("method must be static. method: " + method);
}
ExceptionHandler handler = (ExceptionHandler) onMethodAnnotation;
errorHandlerMethodConfig.setInline(handler.inline());
List<Binding> errorHandlerBindings = BindingParserUtils.parseBindings(method);
// 检查第一个 bidning要是 Throwable Binding
if (errorHandlerBindings.size() == 0) {
throw new IllegalArgumentException(
"error handler bingins must have at least a binding");
}
if (!(errorHandlerBindings.get(0) instanceof ThrowableBinding)) {
throw new IllegalArgumentException(
"error handler bingins first binding must be ThrowableBinding.");
}
// 去掉第一个 ThrowableBinding
// TODO 可能要copy一下保证可以修改成功
errorHandlerBindings.remove(0);
errorHandlerMethodConfig.setBindings(errorHandlerBindings);
errorHandlerMethodConfig.setOwner(Type.getInternalName(method.getDeclaringClass()));
errorHandlerMethodConfig.setMethodName(method.getName());
errorHandlerMethodConfig.setMethodDesc(Type.getMethodDescriptor(method));
}
}
}
}, new MethodFilter() {
@Override
public boolean matches(Method method) {
return AnnotationUtils.findAnnotation(method, ExceptionHandler.class) != null;
}
});
}
if (errorHandlerMethodConfig.getMethodDesc() == null) {
return null;
}
return errorHandlerMethodConfig;
}
}

View File

@ -0,0 +1,17 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import com.taobao.arthas.bytekit.asm.interceptor.parser.InterceptorProcessorParser;
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.ANNOTATION_TYPE)
public @interface InterceptorParserHander {
Class<? extends InterceptorProcessorParser> parserHander();
}

View File

@ -0,0 +1,13 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
/**
* 用于声明没有异常
* @author hengyunabc
*
*/
public class None extends Throwable {
private static final long serialVersionUID = 1L;
private None() {
}
}

View File

@ -0,0 +1,11 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
import com.taobao.arthas.bytekit.asm.binding.Binding;
public class PrintSuppressHandler {
@ExceptionHandler(inline = true)
public static void onSuppress(@Binding.Throwable Throwable e) {
e.printStackTrace();
}
}

View File

@ -0,0 +1,51 @@
package com.taobao.arthas.bytekit.asm.interceptor.parser;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.ReflectionUtils.MethodCallback;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.InterceptorParserHander;
import com.taobao.arthas.bytekit.utils.InstanceUtils;
public class DefaultInterceptorClassParser implements InterceptorClassParser {
@Override
public List<InterceptorProcessor> parse(Class<?> clazz) {
final List<InterceptorProcessor> result = new ArrayList<InterceptorProcessor>();
MethodCallback methodCallback = new MethodCallback() {
@Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
for (Annotation onMethodAnnotation : method.getAnnotations()) {
for (Annotation onAnnotation : onMethodAnnotation.annotationType().getAnnotations()) {
if (InterceptorParserHander.class.isAssignableFrom(onAnnotation.annotationType())) {
if (!Modifier.isStatic(method.getModifiers())) {
throw new IllegalArgumentException("method must be static. method: " + method);
}
InterceptorParserHander handler = (InterceptorParserHander) onAnnotation;
InterceptorProcessorParser interceptorProcessorParser = InstanceUtils
.newInstance(handler.parserHander());
InterceptorProcessor interceptorProcessor = interceptorProcessorParser.parse(method,
onMethodAnnotation);
result.add(interceptorProcessor);
}
}
}
}
};
ReflectionUtils.doWithMethods(clazz, methodCallback);
return result;
}
}

View File

@ -0,0 +1,10 @@
package com.taobao.arthas.bytekit.asm.interceptor.parser;
import java.util.List;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
public interface InterceptorClassParser {
public List<InterceptorProcessor> parse(Class<?> clazz);
}

View File

@ -0,0 +1,11 @@
package com.taobao.arthas.bytekit.asm.interceptor.parser;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
public interface InterceptorProcessorParser {
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod);
}

View File

@ -0,0 +1,24 @@
package com.taobao.arthas.bytekit.asm.location;
public abstract class AccessLocationMatcher implements LocationMatcher {
protected int count;
/**
* flags identifying which type of access should be used to identify the
* trigger. this is either ACCESS_READ, ACCESS_WRITE or an OR of these two
* values
*/
protected int flags;
/**
* flag which is false if the trigger should be inserted before the field
* access is performed and true if it should be inserted after
*/
protected boolean whenComplete;
AccessLocationMatcher(int count, int flags, boolean whenComplete) {
this.count = count;
this.flags = flags;
this.whenComplete = whenComplete;
}
}

View File

@ -0,0 +1,21 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.tree.AbstractInsnNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.asm.location.Location.EnterLocation;
public class EnterLocationMatcher implements LocationMatcher {
@Override
public List<Location> match(MethodProcessor methodProcessor) {
List<Location> locations = new ArrayList<Location>();
AbstractInsnNode enterInsnNode = methodProcessor.getEnterInsnNode();
EnterLocation enterLocation = new EnterLocation(enterInsnNode);
locations.add(enterLocation);
return locations;
}
}

View File

@ -0,0 +1,32 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Type;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.asm.TryCatchBlock;
import com.taobao.arthas.bytekit.asm.location.Location.ExceptionExitLocation;
public class ExceptionExitLocationMatcher implements LocationMatcher {
private String exception;
public ExceptionExitLocationMatcher() {
this(Type.getType(Throwable.class).getInternalName());
}
public ExceptionExitLocationMatcher(String exception) {
this.exception = exception;
}
@Override
public List<Location> match(MethodProcessor methodProcessor) {
List<Location> locations = new ArrayList<Location>();
TryCatchBlock tryCatchBlock = methodProcessor.initTryCatchBlock(exception);
locations.add(new ExceptionExitLocation(tryCatchBlock.getEndLabelNode()));
return locations;
}
}

View File

@ -0,0 +1,46 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.asm.location.Location.ExitLocation;
public class ExitLocationMatcher implements LocationMatcher {
@Override
public List<Location> match(MethodProcessor methodProcessor) {
List<Location> locations = new ArrayList<Location>();
AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode();
while (insnNode != null) {
if (insnNode instanceof InsnNode) {
InsnNode node = (InsnNode) insnNode;
if (matchExit(node)) {
ExitLocation ExitLocation = new ExitLocation(node);
locations.add(ExitLocation);
}
}
insnNode = insnNode.getNext();
}
return locations;
}
public boolean matchExit(InsnNode node) {
switch (node.getOpcode()) {
case Opcodes.RETURN: // empty stack
case Opcodes.IRETURN: // 1 before n/a after
case Opcodes.FRETURN: // 1 before n/a after
case Opcodes.ARETURN: // 1 before n/a after
case Opcodes.LRETURN: // 2 before n/a after
case Opcodes.DRETURN: // 2 before n/a after
return true;
}
return false;
}
}

View File

@ -0,0 +1,96 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.asm.location.Location.FieldAccessLocation;
public class FieldAccessLocationMatcher extends AccessLocationMatcher {
/**
* maybe null
*/
private String ownerClass;
/**
* the name of the field being accessed at the point where the trigger point
* should be inserted
*/
private String fieldName;
/**
* The field's descriptor (see {@link org.objectweb.asm.Type}). maybe null.
*/
private String fieldDesc;
public FieldAccessLocationMatcher(String ownerClass, String fieldDesc, String fieldName, int count, int flags,
boolean whenComplete) {
super(count, flags, whenComplete);
this.ownerClass = ownerClass;
this.fieldDesc = fieldDesc;
this.fieldName = fieldName;
}
@Override
public List<Location> match(MethodProcessor methodProcessor) {
List<Location> locations = new ArrayList<Location>();
AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode();
int matchedCount = 0;
while (insnNode != null) {
if (insnNode instanceof FieldInsnNode) {
FieldInsnNode fieldInsnNode = (FieldInsnNode) insnNode;
if (matchField(fieldInsnNode)) {
matchedCount++;
if (count <= 0 || count == matchedCount) {
FieldAccessLocation fieldAccessLocation = new FieldAccessLocation(fieldInsnNode, count, flags, whenComplete);
locations.add(fieldAccessLocation);
}
}
}
insnNode = insnNode.getNext();
}
return locations;
}
private boolean matchField(FieldInsnNode fieldInsnNode) {
if (!fieldName.equals(fieldInsnNode.name)) {
return false;
}
if (this.fieldDesc != null && !this.fieldDesc.equals(fieldInsnNode.desc)) {
return false;
}
switch (fieldInsnNode.getOpcode()) {
case Opcodes.GETSTATIC:
case Opcodes.GETFIELD: {
if ((flags & Location.ACCESS_READ) == 0) {
return false;
}
}
break;
case Opcodes.PUTSTATIC:
case Opcodes.PUTFIELD: {
if ((flags & Location.ACCESS_WRITE) == 0) {
return false;
}
}
break;
}
if (ownerClass != null) {
if (!ownerClass.equals(fieldInsnNode.owner)) {
return false;
}
}
return true;
}
}

View File

@ -0,0 +1,161 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.MethodInsnNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.asm.location.Location.InvokeLocation;
import com.taobao.arthas.bytekit.utils.MatchUtils;
public class InvokeLocationMatcher implements LocationMatcher {
/**
* the name of the method being invoked at the point where the trigger point
* should be inserted. maybe null, when null, match all method invoke.
*/
private String methodName;
/**
* the name of the type to which the method belongs or null if any type will
* do
*/
private String owner;
/**
* the method signature in externalised form, maybe null.
*/
private String desc;
/**
* count identifying which invocation should be taken as the trigger point.
* if not specified as a parameter this defaults to the first invocation.
*/
private int count;
/**
* flag which is false if the trigger should be inserted before the method
* invocation is performed and true if it should be inserted after
*/
private boolean whenComplete;
/**
* wildcard matcher to exclude class, such as java.* to exclude jdk invoke.
*/
private List<String> excludes = new ArrayList<String>();
public InvokeLocationMatcher(String owner, String methodName, String desc, int count, boolean whenComplete,
List<String> excludes) {
super();
this.owner = owner;
this.methodName = methodName;
this.desc = desc;
this.count = count;
this.whenComplete = whenComplete;
this.excludes = excludes;
}
public InvokeLocationMatcher(String owner, String methodName, String desc, int count, boolean whenComplete) {
this(owner, methodName, desc, count, whenComplete, new ArrayList<String>());
}
@Override
public List<Location> match(MethodProcessor methodProcessor) {
List<Location> locations = new ArrayList<Location>();
AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode();
int matchedCount = 0;
while (insnNode != null) {
if (insnNode instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
if (matchCall(methodInsnNode)) {
matchedCount++;
if (count <= 0 || count == matchedCount) {
InvokeLocation invokeLocation = new InvokeLocation(methodInsnNode, count,
whenComplete);
locations.add(invokeLocation);
}
}
}
insnNode = insnNode.getNext();
}
return locations;
}
private boolean matchCall(MethodInsnNode methodInsnNode) {
if(methodName == null || methodName.isEmpty()) {
return true;
}
if (!this.methodName.equals(methodInsnNode.name)) {
return false;
}
if (!excludes.isEmpty()) {
String ownerClassName = Type.getObjectType(methodInsnNode.owner).getClassName();
for (String exclude : excludes) {
if (MatchUtils.wildcardMatch(ownerClassName, exclude)) {
return false;
}
}
}
if (this.owner != null && !this.owner.equals(methodInsnNode.owner)) {
return false;
}
if (this.desc != null && !desc.equals(methodInsnNode.desc)) {
return false;
}
return true;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public boolean isWhenComplete() {
return whenComplete;
}
public void setWhenComplete(boolean whenComplete) {
this.whenComplete = whenComplete;
}
}

View File

@ -0,0 +1,60 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.LineNumberNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.asm.location.Location.LineLocation;
public class LineLocationMatcher implements LocationMatcher {
private List<Integer> targetLines = Collections.emptyList();
public LineLocationMatcher(int... targetLines) {
if (targetLines != null) {
ArrayList<Integer> result = new ArrayList<Integer>(targetLines.length);
for (int targetLine : targetLines) {
result.add(targetLine);
}
this.targetLines = result;
}
}
public LineLocationMatcher(List<Integer> targetLines) {
this.targetLines = targetLines;
}
@Override
public List<Location> match(MethodProcessor methodProcessor) {
List<Location> locations = new ArrayList<Location>();
AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode();
while (insnNode != null) {
if (insnNode instanceof LineNumberNode) {
LineNumberNode lineNumberNode = (LineNumberNode) insnNode;
if (match(lineNumberNode.line)) {
locations.add(new LineLocation(lineNumberNode, lineNumberNode.line));
}
}
insnNode = insnNode.getNext();
}
return locations;
}
private boolean match(int line) {
for (int targetLine : targetLines) {
if (targetLine == -1) {
return true;
} else if (line == targetLine) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,646 @@
package com.taobao.arthas.bytekit.asm.location;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodInsnNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.asm.binding.BindingContext;
import com.taobao.arthas.bytekit.asm.binding.StackSaver;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
import com.taobao.arthas.bytekit.utils.AsmUtils;
/**
* Specifies a location in a method at which a rule trigger should be inserted
*/
public abstract class Location {
AbstractInsnNode insnNode;
boolean whenComplete = false;
boolean stackNeedSave = false;
public Location(AbstractInsnNode insnNode) {
this(insnNode, false);
}
public Location(AbstractInsnNode insnNode, boolean whenComplete) {
this.insnNode = insnNode;
this.whenComplete = whenComplete;
}
public boolean isWhenComplete() {
return whenComplete;
}
public AbstractInsnNode getInsnNode() {
return insnNode;
}
/**
* 标记在这个location栈上原来的值可以被 callback 函数的return值替换掉
*
* @return
*/
public boolean canChangeByReturn() {
return false;
}
public boolean isStackNeedSave() {
return stackNeedSave;
}
public StackSaver getStackSaver() {
throw new UnsupportedOperationException("this location do not StackSaver, type:" + getLocationType());
}
/**
* identify the type of this location
*
* @return the type of this location
*/
public abstract LocationType getLocationType();
/**
* flag indicating that a field access location refers to field READ operations
*/
public static final int ACCESS_READ = 1;
/**
* flag indicating that a field access location refers to field WRITE operations
*/
public static final int ACCESS_WRITE = 2;
/**
* location identifying a method enter trigger point
*/
static class EnterLocation extends Location {
public EnterLocation(AbstractInsnNode enterInsnNode) {
super(enterInsnNode);
this.insnNode = enterInsnNode;
}
public LocationType getLocationType() {
return LocationType.ENTER;
}
}
/**
* location identifying a method line trigger point
*/
public static class LineLocation extends Location {
/**
* the line at which the trigger point should be inserted
*/
private int targetLine;
public LineLocation(AbstractInsnNode insnNode, int targetLine) {
super(insnNode);
this.targetLine = targetLine;
}
public LocationType getLocationType() {
return LocationType.LINE;
}
}
/**
* location identifying a generic access trigger point
*/
private static abstract class AccessLocation extends Location {
/**
* count identifying which access should be taken as the trigger point. if not
* specified as a parameter this defaults to the first access.
*/
protected int count;
/**
* flags identifying which type of access should be used to identify the
* trigger. this is either ACCESS_READ, ACCESS_WRITE or an OR of these two
* values
*/
protected int flags;
protected AccessLocation(AbstractInsnNode insnNode, int count, int flags, boolean whenComplete) {
super(insnNode, whenComplete);
this.count = count;
this.flags = flags;
}
public LocationType getLocationType() {
if ((flags & ACCESS_WRITE) != 0) {
if (whenComplete) {
return LocationType.WRITE_COMPLETED;
} else {
return LocationType.WRITE;
}
} else {
if (whenComplete) {
return LocationType.READ_COMPLETED;
} else {
return LocationType.READ;
}
}
}
}
/**
* location identifying a field access trigger point
*/
public static class FieldAccessLocation extends AccessLocation {
public FieldAccessLocation(FieldInsnNode fieldInsnNode, int count, int flags, boolean whenComplete) {
super(fieldInsnNode, count, flags, whenComplete);
}
}
/**
* location identifying a variable access trigger point
*/
private static class VariableAccessLocation extends AccessLocation {
/**
* the name of the variable being accessed at the point where the trigger point
* should be inserted
*/
private String variableName;
/**
* flag which is true if the name is a method parameter index such as $0, $1 etc
* otherwise false
*/
private boolean isIndex;
protected VariableAccessLocation(AbstractInsnNode insnNode, String variablename, int count, int flags,
boolean whenComplete) {
super(insnNode, count, flags, whenComplete);
this.variableName = variablename;
isIndex = variablename.matches("[0-9]+");
}
public LocationType getLocationType() {
if ((flags & ACCESS_WRITE) != 0) {
if (whenComplete) {
return LocationType.WRITE_COMPLETED;
} else {
return LocationType.WRITE;
}
} else {
if (whenComplete) {
return LocationType.READ_COMPLETED;
} else {
return LocationType.READ;
}
}
}
}
/**
* location identifying a method invocation trigger point
*/
public static class InvokeLocation extends Location {
/**
* count identifying which invocation should be taken as the trigger point. if
* not specified as a parameter this defaults to the first invocation.
*/
private int count;
public InvokeLocation(MethodInsnNode insnNode, int count, boolean whenComplete) {
super(insnNode, whenComplete);
this.count = count;
this.stackNeedSave = true;
}
@Override
public boolean canChangeByReturn() {
// 对于 invoke 只有在 complete 才能有返回值
return whenComplete;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
public LocationType getLocationType() {
if (whenComplete) {
return LocationType.INVOKE_COMPLETED;
} else {
return LocationType.INVOKE;
}
}
@Override
public StackSaver getStackSaver() {
StackSaver stackSaver = null;
if(whenComplete) {
stackSaver = new StackSaver() {
@Override
public void store(InsnList instructions, BindingContext bindingContext) {
AbstractInsnNode insnNode = bindingContext.getLocation().getInsnNode();
MethodProcessor methodProcessor = bindingContext.getMethodProcessor();
if (insnNode instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
String uniqueNameForMethod = AsmUtils.uniqueNameForMethod(methodInsnNode.owner, methodInsnNode.name,
methodInsnNode.desc);
Type invokeReturnType = Type.getMethodType(methodInsnNode.desc).getReturnType();
if(!invokeReturnType.equals(Type.VOID_TYPE)) {
LocalVariableNode invokeReturnVariableNode = methodProcessor.initInvokeReturnVariableNode(
uniqueNameForMethod, invokeReturnType);
AsmOpUtils.storeVar(instructions, invokeReturnType, invokeReturnVariableNode.index);
}
} else {
throw new IllegalArgumentException(
"InvokeReturnBinding location is not MethodInsnNode, insnNode: " + insnNode);
}
}
@Override
public void load(InsnList instructions, BindingContext bindingContext) {
AbstractInsnNode insnNode = bindingContext.getLocation().getInsnNode();
MethodProcessor methodProcessor = bindingContext.getMethodProcessor();
if (insnNode instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
String uniqueNameForMethod = AsmUtils.uniqueNameForMethod(methodInsnNode.owner, methodInsnNode.name,
methodInsnNode.desc);
Type invokeReturnType = Type.getMethodType(methodInsnNode.desc).getReturnType();
if(!invokeReturnType.equals(Type.VOID_TYPE)) {
LocalVariableNode invokeReturnVariableNode = methodProcessor.initInvokeReturnVariableNode(
uniqueNameForMethod, invokeReturnType);
AsmOpUtils.loadVar(instructions, invokeReturnType, invokeReturnVariableNode.index);
}
} else {
throw new IllegalArgumentException(
"InvokeReturnBinding location is not MethodInsnNode, insnNode: " + insnNode);
}
}
@Override
public Type getType(BindingContext bindingContext) {
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
return Type.getMethodType(methodInsnNode.desc).getReturnType();
}
};
}else {
stackSaver = new StackSaver() {
@Override
public void store(InsnList instructions, BindingContext bindingContext) {
// 需要从要调用的 函数的 des 找到参数的类型再从栈上一个个吐出来再保存到数组里
Location location = bindingContext.getLocation();
MethodProcessor methodProcessor = bindingContext.getMethodProcessor();
if(location instanceof InvokeLocation) {
InvokeLocation invokeLocation = (InvokeLocation) location;
// 如果是非 static的会有this指针
MethodInsnNode methodInsnNode = (MethodInsnNode)invokeLocation.getInsnNode();
Type methodType = Type.getMethodType(methodInsnNode.desc);
boolean isStatic = AsmUtils.isStatic(methodInsnNode);
Type[] argumentTypes = methodType.getArgumentTypes();
// 如果是非static则存放到数组的index要多 1
AsmOpUtils.push(instructions, argumentTypes.length + (isStatic ? 0 : 1));
AsmOpUtils.newArray(instructions, AsmOpUtils.OBJECT_TYPE);
LocalVariableNode invokeArgsVariableNode = methodProcessor.initInvokeArgsVariableNode();
AsmOpUtils.storeVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index);
// 从invoke的参数的后面一个个存到数组里
for(int i = argumentTypes.length - 1; i >= 0 ; --i) {
AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index);
AsmOpUtils.swap(instructions, argumentTypes[i], AsmOpUtils.OBJECT_ARRAY_TYPE);
// 如果是非static则存放到数组的index要多 1
AsmOpUtils.push(instructions, i + (isStatic ? 0 : 1));
AsmOpUtils.swap(instructions, argumentTypes[i], Type.INT_TYPE);
AsmOpUtils.box(instructions, argumentTypes[i]);
AsmOpUtils.arrayStore(instructions, AsmOpUtils.OBJECT_TYPE);
}
// 处理this
if(!isStatic) {
AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index);
AsmOpUtils.swap(instructions, AsmOpUtils.OBJECT_TYPE, AsmOpUtils.OBJECT_ARRAY_TYPE);
AsmOpUtils.push(instructions, 0);
AsmOpUtils.swap(instructions, AsmOpUtils.OBJECT_TYPE, Type.INT_TYPE);
AsmOpUtils.arrayStore(instructions, AsmOpUtils.OBJECT_TYPE);
}
}else {
throw new IllegalArgumentException("location is not a InvokeLocation, location: " + location);
}
}
@Override
public void load(InsnList instructions, BindingContext bindingContext) {
// 从数组里取出来一个个再放到栈上要检查是否要unbox
Location location = bindingContext.getLocation();
MethodProcessor methodProcessor = bindingContext.getMethodProcessor();
LocalVariableNode invokeArgsVariableNode = methodProcessor.initInvokeArgsVariableNode();
if(location instanceof InvokeLocation) {
InvokeLocation invokeLocation = (InvokeLocation) location;
// 如果是非 static的会有this指针
MethodInsnNode methodInsnNode = (MethodInsnNode)invokeLocation.getInsnNode();
Type methodType = Type.getMethodType(methodInsnNode.desc);
boolean isStatic = AsmUtils.isStatic(methodInsnNode);
Type[] argumentTypes = methodType.getArgumentTypes();
if(!isStatic) {
// 取出this
AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index);
AsmOpUtils.push(instructions, 0);
AsmOpUtils.arrayLoad(instructions, AsmOpUtils.OBJECT_TYPE);
AsmOpUtils.checkCast(instructions, Type.getObjectType(methodInsnNode.owner));
}
for(int i = 0; i < argumentTypes.length; ++i) {
AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_ARRAY_TYPE, invokeArgsVariableNode.index);
AsmOpUtils.push(instructions, i + (isStatic ? 0 : 1));
AsmOpUtils.arrayLoad(instructions, AsmOpUtils.OBJECT_TYPE);
// TODO 这里直接 unbox 就可以了unbox里带有 check cast
if(AsmOpUtils.needBox(argumentTypes[i])) {
AsmOpUtils.unbox(instructions, argumentTypes[i]);
}else {
AsmOpUtils.checkCast(instructions, argumentTypes[i]);
}
}
}else {
throw new IllegalArgumentException("location is not a InvokeLocation, location: " + location);
}
}
@Override
public Type getType(BindingContext bindingContext) {
throw new UnsupportedOperationException("InvokeLocation saver do not support getType()");
}
};
}
return stackSaver;
}
}
/**
* location identifying a synchronization trigger point
*/
public static class SyncEnterLocation extends Location {
/**
* count identifying which synchronization should be taken as the trigger point.
* if not specified as a parameter this defaults to the first synchronization.
*/
private int count;
public SyncEnterLocation(AbstractInsnNode insnNode, int count, boolean whenComplete) {
super(insnNode, whenComplete);
this.count = count;
this.whenComplete = whenComplete;
this.stackNeedSave = !whenComplete;
}
public LocationType getLocationType() {
if (whenComplete) {
return LocationType.SYNC_ENTER_COMPLETED;
} else {
return LocationType.SYNC_ENTER;
}
}
@Override
public StackSaver getStackSaver() {
return new StackSaver() {
@Override
public void store(InsnList instructions, BindingContext bindingContext) {
LocalVariableNode variableNode = bindingContext.getMethodProcessor().initMonitorVariableNode();
AsmOpUtils.storeVar(instructions, AsmOpUtils.OBJECT_TYPE, variableNode.index);
}
@Override
public void load(InsnList instructions, BindingContext bindingContext) {
LocalVariableNode variableNode = bindingContext.getMethodProcessor().initMonitorVariableNode();
AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_TYPE, variableNode.index);
}
@Override
public Type getType(BindingContext bindingContext) {
return AsmOpUtils.OBJECT_TYPE;
}
};
}
}
/**
* location identifying a synchronization trigger point
*/
public static class SyncExitLocation extends Location {
/**
* count identifying which synchronization should be taken as the trigger point.
* if not specified as a parameter this defaults to the first synchronization.
*/
private int count;
public SyncExitLocation(AbstractInsnNode insnNode, int count, boolean whenComplete) {
super(insnNode, whenComplete);
this.count = count;
this.whenComplete = whenComplete;
this.stackNeedSave = !whenComplete;
}
public LocationType getLocationType() {
if (whenComplete) {
return LocationType.SYNC_ENTER_COMPLETED;
} else {
return LocationType.SYNC_ENTER;
}
}
@Override
public StackSaver getStackSaver() {
return new StackSaver() {
@Override
public void store(InsnList instructions, BindingContext bindingContext) {
LocalVariableNode variableNode = bindingContext.getMethodProcessor().initMonitorVariableNode();
AsmOpUtils.storeVar(instructions, AsmOpUtils.OBJECT_TYPE, variableNode.index);
}
@Override
public void load(InsnList instructions, BindingContext bindingContext) {
LocalVariableNode variableNode = bindingContext.getMethodProcessor().initMonitorVariableNode();
AsmOpUtils.loadVar(instructions, AsmOpUtils.OBJECT_TYPE, variableNode.index);
}
@Override
public Type getType(BindingContext bindingContext) {
return AsmOpUtils.OBJECT_TYPE;
}
};
}
}
/**
* location identifying a throw trigger point
*/
public static class ThrowLocation extends Location {
/**
* count identifying which throw operation should be taken as the trigger point.
* if not specified as a parameter this defaults to the first throw.
*/
private int count;
public ThrowLocation(AbstractInsnNode insnNode, int count) {
super(insnNode);
this.count = count;
stackNeedSave = true;
}
@Override
public boolean canChangeByReturn() {
return true;
}
public LocationType getLocationType() {
return LocationType.THROW;
}
public StackSaver getStackSaver() {
StackSaver stackSaver = new StackSaver() {
@Override
public void store(InsnList instructions, BindingContext bindingContext) {
LocalVariableNode throwVariableNode = bindingContext.getMethodProcessor().initThrowVariableNode();
AsmOpUtils.storeVar(instructions, Type.getType(Throwable.class), throwVariableNode.index);
}
@Override
public void load(InsnList instructions, BindingContext bindingContext) {
LocalVariableNode throwVariableNode = bindingContext.getMethodProcessor().initThrowVariableNode();
AsmOpUtils.loadVar(instructions, Type.getType(Throwable.class), throwVariableNode.index);
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(Throwable.class);
}
};
return stackSaver;
}
}
/**
* location identifying a method exit trigger point
*/
public static class ExitLocation extends Location {
public ExitLocation(AbstractInsnNode insnNode) {
super(insnNode);
stackNeedSave = true;
}
@Override
public boolean canChangeByReturn() {
return true;
}
public LocationType getLocationType() {
return LocationType.EXIT;
}
public StackSaver getStackSaver() {
StackSaver stackSaver = new StackSaver() {
@Override
public void store(InsnList instructions, BindingContext bindingContext) {
Type returnType = bindingContext.getMethodProcessor().getReturnType();
if(!returnType.equals(Type.VOID_TYPE)) {
LocalVariableNode returnVariableNode = bindingContext.getMethodProcessor().initReturnVariableNode();
AsmOpUtils.storeVar(instructions, returnType, returnVariableNode.index);
}
}
@Override
public void load(InsnList instructions, BindingContext bindingContext) {
Type returnType = bindingContext.getMethodProcessor().getReturnType();
if(!returnType.equals(Type.VOID_TYPE)) {
LocalVariableNode returnVariableNode = bindingContext.getMethodProcessor().initReturnVariableNode();
AsmOpUtils.loadVar(instructions, returnType, returnVariableNode.index);
}
}
@Override
public Type getType(BindingContext bindingContext) {
return bindingContext.getMethodProcessor().getReturnType();
}
};
return stackSaver;
}
}
/**
* location identifying a method exceptional exit trigger point
*/
public static class ExceptionExitLocation extends Location {
public ExceptionExitLocation(AbstractInsnNode insnNode) {
super(insnNode, true);
stackNeedSave = true;
}
public LocationType getLocationType() {
return LocationType.EXCEPTION_EXIT;
}
public StackSaver getStackSaver() {
StackSaver stackSaver = new StackSaver() {
@Override
public void store(InsnList instructions, BindingContext bindingContext) {
LocalVariableNode throwVariableNode = bindingContext.getMethodProcessor().initThrowVariableNode();
AsmOpUtils.storeVar(instructions, Type.getType(Throwable.class), throwVariableNode.index);
}
@Override
public void load(InsnList instructions, BindingContext bindingContext) {
LocalVariableNode throwVariableNode = bindingContext.getMethodProcessor().initThrowVariableNode();
AsmOpUtils.loadVar(instructions, Type.getType(Throwable.class), throwVariableNode.index);
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(Throwable.class);
}
};
return stackSaver;
}
}
}

View File

@ -0,0 +1,11 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.List;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
public interface LocationMatcher {
public List<Location> match(MethodProcessor methodProcessor);
}

View File

@ -0,0 +1,86 @@
package com.taobao.arthas.bytekit.asm.location;
public enum LocationType {
/**
* user define.
*/
USER_DEFINE,
/**
* method enter.
*
*/
ENTER,
/**
* line number.
*
*/
LINE,
/**
* field read operation.
*
*/
READ,
/**
* field read operation.
*/
READ_COMPLETED,
/**
* field write operation.
*
*/
WRITE,
/**
* field write operation.
*
*/
WRITE_COMPLETED,
/**
* method invoke operation
*
*/
INVOKE,
/**
* method invoke operation
*
*/
INVOKE_COMPLETED,
/**
* synchronize operation
*
*/
SYNC_ENTER,
/**
* synchronize operation
*
*/
SYNC_ENTER_COMPLETED,
/**
* synchronize operation
*
*/
SYNC_EXIT,
/**
* synchronize operation
*
*/
SYNC_EXIT_COMPLETED,
/**
* throw
*/
THROW,
/**
* return
*/
EXIT,
/**
* add try/catch
*/
EXCEPTION_EXIT;
}

View File

@ -0,0 +1,9 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.List;
public class MatchResult {
List<Location> locations;
}

View File

@ -0,0 +1,46 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.asm.location.Location.SyncEnterLocation;
public class SyncExitLocationMatcher implements LocationMatcher {
private int count;
boolean whenComplete;
public SyncExitLocationMatcher(int count, boolean whenComplete) {
this.count = count;
this.whenComplete = whenComplete;
}
@Override
public List<Location> match(MethodProcessor methodProcessor) {
List<Location> locations = new ArrayList<Location>();
AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode();
int matchedCount = 0;
while (insnNode != null) {
if (insnNode instanceof InsnNode) {
InsnNode node = (InsnNode) insnNode;
if (node.getOpcode() == Opcodes.MONITOREXIT) {
++matchedCount;
if (count <= 0 || count == matchedCount) {
SyncEnterLocation location = new SyncEnterLocation(node, matchedCount, whenComplete);
locations.add(location);
}
}
}
insnNode = insnNode.getNext();
}
return locations;
}
}

View File

@ -0,0 +1,53 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.asm.location.Location.SyncEnterLocation;
public class SyncLocationMatcher implements LocationMatcher {
private int count;
boolean whenComplete;
int opcode;
public SyncLocationMatcher(int opcode, int count, boolean whenComplete) {
if (!(Opcodes.MONITORENTER == opcode || Opcodes.MONITOREXIT == opcode)) {
throw new IllegalArgumentException(
"SyncLocationMatcher only support Opcodes.MONITORENTER or Opcodes.MONITOREXIT.");
}
this.opcode = opcode;
this.count = count;
this.whenComplete = whenComplete;
}
@Override
public List<Location> match(MethodProcessor methodProcessor) {
List<Location> locations = new ArrayList<Location>();
AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode();
int matchedCount = 0;
while (insnNode != null) {
if (insnNode instanceof InsnNode) {
InsnNode node = (InsnNode) insnNode;
if (node.getOpcode() == opcode) {
++matchedCount;
if (count <= 0 || count == matchedCount) {
SyncEnterLocation location = new SyncEnterLocation(node, matchedCount, whenComplete);
locations.add(location);
}
}
}
insnNode = insnNode.getNext();
}
return locations;
}
}

View File

@ -0,0 +1,47 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.InsnNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.asm.location.Location.ThrowLocation;
public class ThrowLocationMatcher implements LocationMatcher {
public ThrowLocationMatcher(int count) {
this.count = count;
}
/**
* count identifying which invocation should be taken as the trigger point.
* if not specified as a parameter this defaults to the first invocation.
*/
private int count;
@Override
public List<Location> match(MethodProcessor methodProcessor) {
List<Location> locations = new ArrayList<Location>();
AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode();
int matchedCount = 0;
while (insnNode != null) {
if (insnNode instanceof InsnNode) {
InsnNode node = (InsnNode) insnNode;
if (node.getOpcode() == Opcodes.ATHROW) {
++matchedCount;
if (count <= 0 || count == matchedCount) {
ThrowLocation location = new ThrowLocation(node, matchedCount);
locations.add(location);
}
}
}
insnNode = insnNode.getNext();
}
return locations;
}
}

View File

@ -0,0 +1,34 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.List;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
public class VariableAccessLocationMatcher extends AccessLocationMatcher {
/**
* the name of the variable being accessed at the point where the trigger
* point should be inserted
*/
private String variableName;
/**
* flag which is true if the name is a method parameter index such as $0, $1
* etc otherwise false
*/
private boolean isIndex;
protected VariableAccessLocationMatcher(String variablename, int count, int flags, boolean whenComplete) {
super(count, flags, whenComplete);
this.variableName = variablename;
isIndex = variablename.matches("[0-9]+");
}
@Override
public List<Location> match(MethodProcessor methodProcessor) {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -0,0 +1,7 @@
package com.taobao.arthas.bytekit.asm.matcher;
public interface ClassMatcher {
boolean match(String name, ClassLoader classLoader);
}

View File

@ -0,0 +1,8 @@
package com.taobao.arthas.bytekit.asm.matcher;
import org.objectweb.asm.tree.MethodNode;
public interface MethodMatcher {
boolean match(String className, MethodNode methodNode);
}

View File

@ -0,0 +1,21 @@
package com.taobao.arthas.bytekit.utils;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import net.bytebuddy.agent.ByteBuddyAgent;
public class AgentUtils {
private static class InstrumentationHolder {
static final Instrumentation instance = ByteBuddyAgent.install();
}
public static void redefine(Class<?> theClass, byte[] theClassFile)
throws ClassNotFoundException, UnmodifiableClassException {
ClassDefinition classDefinition = new ClassDefinition(theClass, theClassFile);
InstrumentationHolder.instance.redefineClasses(classDefinition);
}
}

View File

@ -0,0 +1,408 @@
package com.taobao.arthas.bytekit.utils;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.Method;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.FieldInsnNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.InsnNode;
import org.objectweb.asm.tree.IntInsnNode;
import org.objectweb.asm.tree.LdcInsnNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.tree.VarInsnNode;
public class AsmOpUtils {
private static final Type BYTE_TYPE = Type.getObjectType("java/lang/Byte");
private static final Type BOOLEAN_TYPE = Type.getObjectType("java/lang/Boolean");
private static final Type SHORT_TYPE = Type.getObjectType("java/lang/Short");
private static final Type CHARACTER_TYPE = Type.getObjectType("java/lang/Character");
private static final Type INTEGER_TYPE = Type.getObjectType("java/lang/Integer");
private static final Type FLOAT_TYPE = Type.getObjectType("java/lang/Float");
private static final Type LONG_TYPE = Type.getObjectType("java/lang/Long");
private static final Type DOUBLE_TYPE = Type.getObjectType("java/lang/Double");
public static final Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
public static final Type OBJECT_ARRAY_TYPE = Type.getType(Object[].class);
public static final Type STRING_TYPE = Type.getObjectType("java/lang/String");
public static final Type STRING_ARRAY_TYPE = Type.getType(String[].class);
private static final Type NUMBER_TYPE = Type.getObjectType("java/lang/Number");
private static final Method BOOLEAN_VALUE = Method.getMethod("boolean booleanValue()");
private static final Method CHAR_VALUE = Method.getMethod("char charValue()");
private static final Method INT_VALUE = Method.getMethod("int intValue()");
private static final Method FLOAT_VALUE = Method.getMethod("float floatValue()");
private static final Method LONG_VALUE = Method.getMethod("long longValue()");
private static final Method DOUBLE_VALUE = Method.getMethod("double doubleValue()");
static Type getBoxedType(final Type type) {
switch (type.getSort()) {
case Type.BYTE:
return BYTE_TYPE;
case Type.BOOLEAN:
return BOOLEAN_TYPE;
case Type.SHORT:
return SHORT_TYPE;
case Type.CHAR:
return CHARACTER_TYPE;
case Type.INT:
return INTEGER_TYPE;
case Type.FLOAT:
return FLOAT_TYPE;
case Type.LONG:
return LONG_TYPE;
case Type.DOUBLE:
return DOUBLE_TYPE;
}
return type;
}
public static void newInstance(final InsnList instructions, final Type type) {
instructions.add(new TypeInsnNode(Opcodes.NEW, type.getInternalName()));
}
public static void push(InsnList insnList, final int value) {
if (value >= -1 && value <= 5) {
insnList.add(new InsnNode(Opcodes.ICONST_0 + value));
} else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) {
insnList.add(new IntInsnNode(Opcodes.BIPUSH, value));
} else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) {
insnList.add(new IntInsnNode(Opcodes.SIPUSH, value));
} else {
insnList.add(new LdcInsnNode(value));
}
}
public static void push(InsnList insnList, final String value) {
if (value == null) {
insnList.add(new InsnNode(Opcodes.ACONST_NULL));
} else {
insnList.add(new LdcInsnNode(value));
}
}
/**
* @see org.objectweb.asm.tree.LdcInsnNode#cst
* @param value
*/
public static void ldc(InsnList insnList, Object value) {
insnList.add(new LdcInsnNode(value));
}
public static void newArray(final InsnList insnList, final Type type) {
insnList.add(new TypeInsnNode(Opcodes.ANEWARRAY, type.getInternalName()));
}
public static void dup(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.DUP));
}
public static void dup2(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.DUP2));
}
public static void dupX1(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.DUP_X1));
}
public static void dupX2(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.DUP_X2));
}
/**
* Generates a DUP2_X1 instruction.
*/
public static void dup2X1(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.DUP2_X1));
}
/**
* Generates a DUP2_X2 instruction.
*/
public static void dup2X2(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.DUP2_X2));
}
public static void pop(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.POP));
}
/**
* Generates a POP2 instruction.
*/
public static void pop2(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.POP2));
}
public static void swap(final InsnList insnList) {
insnList.add(new InsnNode(Opcodes.SWAP));
}
/**
* Generates the instructions to swap the top two stack values.
*
* @param prev
* type of the top - 1 stack value.
* @param type
* type of the top stack value.
*/
public static void swap(final InsnList insnList, final Type prev, final Type type) {
if (type.getSize() == 1) {
if (prev.getSize() == 1) {
swap(insnList); // same as dupX1(), pop();
} else {
dupX2(insnList);
pop(insnList);
}
} else {
if (prev.getSize() == 1) {
dup2X1(insnList);
pop2(insnList);
} else {
dup2X2(insnList);
pop2(insnList);
}
}
}
public static void box(final InsnList instructions, Type type) {
if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) {
return;
}
if (type == Type.VOID_TYPE) {
// push null
instructions.add(new InsnNode(Opcodes.ACONST_NULL));
} else {
Type boxed = getBoxedType(type);
// new instance.
newInstance(instructions, boxed);
if (type.getSize() == 2) {
// Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o
// dupX2
dupX2(instructions);
// dupX2
dupX2(instructions);
// pop
pop(instructions);
} else {
// p -> po -> opo -> oop -> o
// dupX1
dupX1(instructions);
// swap
swap(instructions);
}
invokeConstructor(instructions, boxed, new Method("<init>", Type.VOID_TYPE, new Type[] { type }));
}
}
public static void invokeConstructor(final InsnList instructions, final Type type, final Method method) {
String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() : type.getInternalName();
instructions
.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, owner, method.getName(), method.getDescriptor(), false));
}
public static void unbox(final InsnList instructions, Type type) {
Type t = NUMBER_TYPE;
Method sig = null;
switch (type.getSort()) {
case Type.VOID:
return;
case Type.CHAR:
t = CHARACTER_TYPE;
sig = CHAR_VALUE;
break;
case Type.BOOLEAN:
t = BOOLEAN_TYPE;
sig = BOOLEAN_VALUE;
break;
case Type.DOUBLE:
sig = DOUBLE_VALUE;
break;
case Type.FLOAT:
sig = FLOAT_VALUE;
break;
case Type.LONG:
sig = LONG_VALUE;
break;
case Type.INT:
case Type.SHORT:
case Type.BYTE:
sig = INT_VALUE;
}
if (sig == null) {
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, type.getInternalName()));
} else {
instructions.add(new TypeInsnNode(Opcodes.CHECKCAST, t.getInternalName()));
instructions.add(new MethodInsnNode(Opcodes.INVOKEVIRTUAL, t.getInternalName(), sig.getName(),
sig.getDescriptor(), false));
}
}
public static boolean needBox(Type type) {
switch (type.getSort()) {
case Type.BYTE:
case Type.BOOLEAN:
case Type.SHORT:
case Type.CHAR:
case Type.INT:
case Type.FLOAT:
case Type.LONG:
case Type.DOUBLE:
return true;
}
return false;
}
public static void getStatic(final InsnList insnList, final Type owner, final String name, final Type type) {
insnList.add(new FieldInsnNode(Opcodes.GETSTATIC, owner.getInternalName(), name, type.getDescriptor()));
}
/**
* Generates the instruction to push the value of a non static field on the
* stack.
*
* @param owner
* the class in which the field is defined.
* @param name
* the name of the field.
* @param type
* the type of the field.
*/
public static void getField(final InsnList insnList, final Type owner, final String name, final Type type) {
insnList.add(new FieldInsnNode(Opcodes.GETFIELD, owner.getInternalName(), name, type.getDescriptor()));
}
public static void arrayStore(final InsnList instructions, final Type type) {
instructions.add(new InsnNode(type.getOpcode(Opcodes.IASTORE)));
}
public static void arrayLoad(final InsnList instructions, final Type type) {
instructions.add(new InsnNode(type.getOpcode(Opcodes.IALOAD)));
}
/**
* Generates the instructions to load all the method arguments on the stack,
* as a single object array.
*
* @see org.objectweb.asm.commons.GeneratorAdapter#loadArgArray()
*/
public static void loadArgArray(final InsnList instructions, MethodNode methodNode) {
boolean isStatic = AsmUtils.isStatic(methodNode);
Type[] argumentTypes = Type.getArgumentTypes(methodNode.desc);
push(instructions, argumentTypes.length);
newArray(instructions, OBJECT_TYPE);
for (int i = 0; i < argumentTypes.length; i++) {
dup(instructions);
push(instructions, i);
loadArg(isStatic, instructions, argumentTypes, i);
box(instructions, argumentTypes[i]);
arrayStore(instructions, OBJECT_TYPE);
}
}
public static void loadArgs(final InsnList instructions, MethodNode methodNode) {
Type[] argumentTypes = Type.getArgumentTypes(methodNode.desc);
boolean isStatic = AsmUtils.isStatic(methodNode);
for (int i = 0; i < argumentTypes.length; i++) {
loadArg(isStatic, instructions, argumentTypes, i);
}
}
public static void loadArg(boolean staticAccess, final InsnList instructions, Type[] argumentTypes, int i) {
final int index = getArgIndex(staticAccess, argumentTypes, i);
final Type type = argumentTypes[i];
instructions.add(new VarInsnNode(type.getOpcode(Opcodes.ILOAD), index));
}
static int getArgIndex(boolean staticAccess, final Type[] argumentTypes, final int arg) {
int index = staticAccess ? 0 : 1;
for (int i = 0; i < arg; i++) {
index += argumentTypes[i].getSize();
}
return index;
}
public static void loadVar(final InsnList instructions, Type type, final int index) {
instructions.add(new VarInsnNode(type.getOpcode(Opcodes.ILOAD), index));
}
public static void storeVar(final InsnList instructions, Type type, final int index) {
instructions.add(new VarInsnNode(type.getOpcode(Opcodes.ISTORE), index));
}
/**
* Generates a type dependent instruction.
*
* @param opcode
* the instruction's opcode.
* @param type
* the instruction's operand.
*/
private static void typeInsn(final InsnList instructions, final int opcode, final Type type) {
instructions.add(new TypeInsnNode(opcode, type.getInternalName()));
}
/**
* Generates the instruction to check that the top stack value is of the
* given type.
*
* @param type
* a class or interface type.
*/
public static void checkCast(final InsnList instructions, final Type type) {
if (!type.equals(OBJECT_TYPE)) {
typeInsn(instructions, Opcodes.CHECKCAST, type);
}
}
public static void throwException(final InsnList instructions) {
instructions.add(new InsnNode(Opcodes.ATHROW));
}
public static boolean isReturnCode(final int opcode) {
return opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN;
}
public static List<LocalVariableNode> validVariables(List<LocalVariableNode> localVariables,
AbstractInsnNode currentInsnNode) {
List<LocalVariableNode> results = new ArrayList<LocalVariableNode>();
// find out current valid local variables
for (LocalVariableNode localVariableNode : localVariables) {
for (AbstractInsnNode iter = localVariableNode.start; iter != null
&& (!iter.equals(localVariableNode.end)); iter = iter.getNext()) {
if (iter.equals(currentInsnNode)) {
results.add(localVariableNode);
break;
}
}
}
return results;
}
}

View File

@ -0,0 +1,403 @@
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 org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
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.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.FieldNode;
import org.objectweb.asm.tree.LocalVariableNode;
import org.objectweb.asm.tree.MethodInsnNode;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.tree.TypeInsnNode;
import org.objectweb.asm.util.ASMifier;
import org.objectweb.asm.util.TraceClassVisitor;
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.ASM6);
reader.accept(result, ClassReader.SKIP_FRAMES);
return result;
}
public static byte[] toBytes(ClassNode classNode) {
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
classNode.accept(writer);
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.ASM6, 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 MethodNode removeLineNumbers(MethodNode methodNode) {
MethodNode result = newMethodNode(methodNode);
methodNode.accept(new MethodVisitor(Opcodes.ASM6, result) {
public void visitLineNumber(int line, Label start) {
}
});
return result;
}
public static MethodNode findFirstMethod(Collection<MethodNode> methodNodes, String name) {
for (MethodNode methodNode : methodNodes) {
if (methodNode.name.equals(name)) {
return methodNode;
}
}
return null;
}
public static List<MethodNode> findMethods(Collection<MethodNode> methodNodes, String name) {
List<MethodNode> result = new ArrayList<MethodNode>();
for (MethodNode methodNode : methodNodes) {
if (methodNode.name.equals(name)) {
result.add(methodNode);
}
}
return result;
}
public static MethodNode findMethod(Collection<MethodNode> 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("<init>")) {
if (--nested < 0) {
// find this() or super().
return insnNode.getNext();
}
}
}
}
return null;
}
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("<init>");
}
public String[] getParameterNames(MethodNode methodNode) {
Type[] argumentTypes = Type.getArgumentTypes(methodNode.desc);
if (argumentTypes.length == 0) {
return new String[0];
}
final List<LocalVariableNode> 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<LocalVariableNode>() {
@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 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("<init>")) {
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("<init>")) {
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<FieldNode> fields, String name) {
for(FieldNode field : fields) {
if(field.name.equals(name)) {
return field;
}
}
return null;
}
// 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);
}
}

View File

@ -0,0 +1,484 @@
package com.taobao.arthas.bytekit.utils;
///*
// * Copyright 2002-2017 the original author or authors.
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//
//package com.alibaba.bytekit.utils;
//
//import java.util.Collection;
//import java.util.Map;
//
///**
// * Assertion utility class that assists in validating arguments.
// *
// * <p>Useful for identifying programmer errors early and clearly at runtime.
// *
// * <p>For example, if the contract of a public method states it does not
// * allow {@code null} arguments, {@code Assert} can be used to validate that
// * contract. Doing this clearly indicates a contract violation when it
// * occurs and protects the class's invariants.
// *
// * <p>Typically used to validate method arguments rather than configuration
// * properties, to check for cases that are usually programmer errors rather
// * than configuration errors. In contrast to configuration initialization
// * code, there is usually no point in falling back to defaults in such methods.
// *
// * <p>This class is similar to JUnit's assertion library. If an argument value is
// * deemed invalid, an {@link IllegalArgumentException} is thrown (typically).
// * For example:
// *
// * <pre class="code">
// * Assert.notNull(clazz, "The class must not be null");
// * Assert.isTrue(i > 0, "The value must be greater than zero");</pre>
// *
// * <p>Mainly for internal use within the framework; consider
// * <a href="http://commons.apache.org/proper/commons-lang/">Apache's Commons Lang</a>
// * for a more comprehensive suite of {@code String} utilities.
// *
// * @author Keith Donald
// * @author Juergen Hoeller
// * @author Sam Brannen
// * @author Colin Sampaleanu
// * @author Rob Harrop
// * @since 1.1.2
// */
//public abstract class Assert {
//
// /**
// * Assert a boolean expression, throwing an {@code IllegalStateException}
// * if the expression evaluates to {@code false}.
// * <p>Call {@link #isTrue} if you wish to throw an {@code IllegalArgumentException}
// * on an assertion failure.
// * <pre class="code">Assert.state(id == null, "The id property must not already be initialized");</pre>
// * @param expression a boolean expression
// * @param message the exception message to use if the assertion fails
// * @throws IllegalStateException if {@code expression} is {@code false}
// */
// public static void state(boolean expression, String message) {
// if (!expression) {
// throw new IllegalStateException(message);
// }
// }
//
// /**
// * @deprecated as of 4.3.7, in favor of {@link #state(boolean, String)}
// */
// @Deprecated
// public static void state(boolean expression) {
// state(expression, "[Assertion failed] - this state invariant must be true");
// }
//
// /**
// * Assert a boolean expression, throwing an {@code IllegalArgumentException}
// * if the expression evaluates to {@code false}.
// * <pre class="code">Assert.isTrue(i &gt; 0, "The value must be greater than zero");</pre>
// * @param expression a boolean expression
// * @param message the exception message to use if the assertion fails
// * @throws IllegalArgumentException if {@code expression} is {@code false}
// */
// public static void isTrue(boolean expression, String message) {
// if (!expression) {
// throw new IllegalArgumentException(message);
// }
// }
//
// /**
// * @deprecated as of 4.3.7, in favor of {@link #isTrue(boolean, String)}
// */
// @Deprecated
// public static void isTrue(boolean expression) {
// isTrue(expression, "[Assertion failed] - this expression must be true");
// }
//
// /**
// * Assert that an object is {@code null}.
// * <pre class="code">Assert.isNull(value, "The value must be null");</pre>
// * @param object the object to check
// * @param message the exception message to use if the assertion fails
// * @throws IllegalArgumentException if the object is not {@code null}
// */
// public static void isNull(Object object, String message) {
// if (object != null) {
// throw new IllegalArgumentException(message);
// }
// }
//
// /**
// * @deprecated as of 4.3.7, in favor of {@link #isNull(Object, String)}
// */
// @Deprecated
// public static void isNull(Object object) {
// isNull(object, "[Assertion failed] - the object argument must be null");
// }
//
// /**
// * Assert that an object is not {@code null}.
// * <pre class="code">Assert.notNull(clazz, "The class must not be null");</pre>
// * @param object the object to check
// * @param message the exception message to use if the assertion fails
// * @throws IllegalArgumentException if the object is {@code null}
// */
// public static void notNull(Object object, String message) {
// if (object == null) {
// throw new IllegalArgumentException(message);
// }
// }
//
// /**
// * @deprecated as of 4.3.7, in favor of {@link #notNull(Object, String)}
// */
// @Deprecated
// public static void notNull(Object object) {
// notNull(object, "[Assertion failed] - this argument is required; it must not be null");
// }
//
// /**
// * Assert that the given String is not empty; that is,
// * it must not be {@code null} and not the empty String.
// * <pre class="code">Assert.hasLength(name, "Name must not be empty");</pre>
// * @param text the String to check
// * @param message the exception message to use if the assertion fails
// * @see StringUtils#hasLength
// * @throws IllegalArgumentException if the text is empty
// */
// public static void hasLength(String text, String message) {
// if (!StringUtils_hasLength(text)) {
// throw new IllegalArgumentException(message);
// }
// }
//
// /**
// * @deprecated as of 4.3.7, in favor of {@link #hasLength(String, String)}
// */
// @Deprecated
// public static void hasLength(String text) {
// hasLength(text,
// "[Assertion failed] - this String argument must have length; it must not be null or empty");
// }
//
// /**
// * Assert that the given String contains valid text content; that is, it must not
// * be {@code null} and must contain at least one non-whitespace character.
// * <pre class="code">Assert.hasText(name, "'name' must not be empty");</pre>
// * @param text the String to check
// * @param message the exception message to use if the assertion fails
// * @see StringUtils#hasText
// * @throws IllegalArgumentException if the text does not contain valid text content
// */
// public static void hasText(String text, String message) {
// if (!StringUtils_hasText(text)) {
// throw new IllegalArgumentException(message);
// }
// }
//
// /**
// * @deprecated as of 4.3.7, in favor of {@link #hasText(String, String)}
// */
// @Deprecated
// public static void hasText(String text) {
// hasText(text,
// "[Assertion failed] - this String argument must have text; it must not be null, empty, or blank");
// }
//
// /**
// * Assert that the given text does not contain the given substring.
// * <pre class="code">Assert.doesNotContain(name, "rod", "Name must not contain 'rod'");</pre>
// * @param textToSearch the text to search
// * @param substring the substring to find within the text
// * @param message the exception message to use if the assertion fails
// * @throws IllegalArgumentException if the text contains the substring
// */
// public static void doesNotContain(String textToSearch, String substring, String message) {
// if (StringUtils_hasLength(textToSearch) && StringUtils_hasLength(substring) &&
// textToSearch.contains(substring)) {
// throw new IllegalArgumentException(message);
// }
// }
//
// /**
// * @deprecated as of 4.3.7, in favor of {@link #doesNotContain(String, String, String)}
// */
// @Deprecated
// public static void doesNotContain(String textToSearch, String substring) {
// doesNotContain(textToSearch, substring,
// "[Assertion failed] - this String argument must not contain the substring [" + substring + "]");
// }
//
// /**
// * Assert that an array contains elements; that is, it must not be
// * {@code null} and must contain at least one element.
// * <pre class="code">Assert.notEmpty(array, "The array must contain elements");</pre>
// * @param array the array to check
// * @param message the exception message to use if the assertion fails
// * @throws IllegalArgumentException if the object array is {@code null} or contains no elements
// */
// public static void notEmpty(Object[] array, String message) {
// if (ObjectUtils.isEmpty(array)) {
// throw new IllegalArgumentException(message);
// }
// }
//
// /**
// * @deprecated as of 4.3.7, in favor of {@link #notEmpty(Object[], String)}
// */
// @Deprecated
// public static void notEmpty(Object[] array) {
// notEmpty(array, "[Assertion failed] - this array must not be empty: it must contain at least 1 element");
// }
//
// /**
// * Assert that an array contains no {@code null} elements.
// * <p>Note: Does not complain if the array is empty!
// * <pre class="code">Assert.noNullElements(array, "The array must contain non-null elements");</pre>
// * @param array the array to check
// * @param message the exception message to use if the assertion fails
// * @throws IllegalArgumentException if the object array contains a {@code null} element
// */
// public static void noNullElements(Object[] array, String message) {
// if (array != null) {
// for (Object element : array) {
// if (element == null) {
// throw new IllegalArgumentException(message);
// }
// }
// }
// }
//
// /**
// * @deprecated as of 4.3.7, in favor of {@link #noNullElements(Object[], String)}
// */
// @Deprecated
// public static void noNullElements(Object[] array) {
// noNullElements(array, "[Assertion failed] - this array must not contain any null elements");
// }
//
// /**
// * Assert that a collection contains elements; that is, it must not be
// * {@code null} and must contain at least one element.
// * <pre class="code">Assert.notEmpty(collection, "Collection must contain elements");</pre>
// * @param collection the collection to check
// * @param message the exception message to use if the assertion fails
// * @throws IllegalArgumentException if the collection is {@code null} or
// * contains no elements
// */
// public static void notEmpty(Collection<?> collection, String message) {
// if (collection == null || collection.isEmpty()) {
// throw new IllegalArgumentException(message);
// }
// }
//
// /**
// * @deprecated as of 4.3.7, in favor of {@link #notEmpty(Collection, String)}
// */
// @Deprecated
// public static void notEmpty(Collection<?> collection) {
// notEmpty(collection,
// "[Assertion failed] - this collection must not be empty: it must contain at least 1 element");
// }
//
// /**
// * Assert that a Map contains entries; that is, it must not be {@code null}
// * and must contain at least one entry.
// * <pre class="code">Assert.notEmpty(map, "Map must contain entries");</pre>
// * @param map the map to check
// * @param message the exception message to use if the assertion fails
// * @throws IllegalArgumentException if the map is {@code null} or contains no entries
// */
// public static void notEmpty(Map<?, ?> map, String message) {
// if ((map == null || map.isEmpty())) {
// throw new IllegalArgumentException(message);
// }
// }
//
// /**
// * @deprecated as of 4.3.7, in favor of {@link #notEmpty(Map, String)}
// */
// @Deprecated
// public static void notEmpty(Map<?, ?> map) {
// notEmpty(map, "[Assertion failed] - this map must not be empty; it must contain at least one entry");
// }
//
// /**
// * Assert that the provided object is an instance of the provided class.
// * <pre class="code">Assert.instanceOf(Foo.class, foo, "Foo expected");</pre>
// * @param type the type to check against
// * @param obj the object to check
// * @param message a message which will be prepended to provide further context.
// * If it is empty or ends in ":" or ";" or "," or ".", a full exception message
// * will be appended. If it ends in a space, the name of the offending object's
// * type will be appended. In any other case, a ":" with a space and the name
// * of the offending object's type will be appended.
// * @throws IllegalArgumentException if the object is not an instance of type
// */
// public static void isInstanceOf(Class<?> type, Object obj, String message) {
// notNull(type, "Type to check against must not be null");
// if (!type.isInstance(obj)) {
// instanceCheckFailed(type, obj, message);
// }
// }
//
// /**
// * Assert that the provided object is an instance of the provided class.
// * <pre class="code">Assert.instanceOf(Foo.class, foo);</pre>
// * @param type the type to check against
// * @param obj the object to check
// * @throws IllegalArgumentException if the object is not an instance of type
// */
// public static void isInstanceOf(Class<?> type, Object obj) {
// isInstanceOf(type, obj, "");
// }
//
// /**
// * Assert that {@code superType.isAssignableFrom(subType)} is {@code true}.
// * <pre class="code">Assert.isAssignable(Number.class, myClass, "Number expected");</pre>
// * @param superType the super type to check against
// * @param subType the sub type to check
// * @param message a message which will be prepended to provide further context.
// * If it is empty or ends in ":" or ";" or "," or ".", a full exception message
// * will be appended. If it ends in a space, the name of the offending sub type
// * will be appended. In any other case, a ":" with a space and the name of the
// * offending sub type will be appended.
// * @throws IllegalArgumentException if the classes are not assignable
// */
// public static void isAssignable(Class<?> superType, Class<?> subType, String message) {
// notNull(superType, "Super type to check against must not be null");
// if (subType == null || !superType.isAssignableFrom(subType)) {
// assignableCheckFailed(superType, subType, message);
// }
// }
//
// /**
// * Assert that {@code superType.isAssignableFrom(subType)} is {@code true}.
// * <pre class="code">Assert.isAssignable(Number.class, myClass);</pre>
// * @param superType the super type to check
// * @param subType the sub type to check
// * @throws IllegalArgumentException if the classes are not assignable
// */
// public static void isAssignable(Class<?> superType, Class<?> subType) {
// isAssignable(superType, subType, "");
// }
//
//
// private static void instanceCheckFailed(Class<?> type, Object obj, String msg) {
// String className = (obj != null ? obj.getClass().getName() : "null");
// String result = "";
// boolean defaultMessage = true;
// if (StringUtils_hasLength(msg)) {
// if (endsWithSeparator(msg)) {
// result = msg + " ";
// }
// else {
// result = messageWithTypeName(msg, className);
// defaultMessage = false;
// }
// }
// if (defaultMessage) {
// result = result + ("Object of class [" + className + "] must be an instance of " + type);
// }
// throw new IllegalArgumentException(result);
// }
//
// private static void assignableCheckFailed(Class<?> superType, Class<?> subType, String msg) {
// String result = "";
// boolean defaultMessage = true;
// if (StringUtils_hasLength(msg)) {
// if (endsWithSeparator(msg)) {
// result = msg + " ";
// }
// else {
// result = messageWithTypeName(msg, subType);
// defaultMessage = false;
// }
// }
// if (defaultMessage) {
// result = result + (subType + " is not assignable to " + superType);
// }
// throw new IllegalArgumentException(result);
// }
//
// private static boolean endsWithSeparator(String msg) {
// return (msg.endsWith(":") || msg.endsWith(";") || msg.endsWith(",") || msg.endsWith("."));
// }
//
// private static String messageWithTypeName(String msg, Object typeName) {
// return msg + (msg.endsWith(" ") ? "" : ": ") + typeName;
// }
//
//
// /**
// * Check that the given {@code CharSequence} is neither {@code null} nor
// * of length 0.
// * <p>Note: this method returns {@code true} for a {@code CharSequence}
// * that purely consists of whitespace.
// * <p><pre class="code">
// * StringUtils.hasLength(null) = false
// * StringUtils.hasLength("") = false
// * StringUtils.hasLength(" ") = true
// * StringUtils.hasLength("Hello") = true
// * </pre>
// * @param str the {@code CharSequence} to check (may be {@code null})
// * @return {@code true} if the {@code CharSequence} is not {@code null} and has length
// * @see #hasText(String)
// */
// private static boolean StringUtils_hasLength(CharSequence str) {
// return (str != null && str.length() > 0);
// }
//
// /**
// * Check whether the given {@code CharSequence} contains actual <em>text</em>.
// * <p>More specifically, this method returns {@code true} if the
// * {@code CharSequence} is not {@code null}, its length is greater than
// * 0, and it contains at least one non-whitespace character.
// * <p><pre class="code">
// * StringUtils.hasText(null) = false
// * StringUtils.hasText("") = false
// * StringUtils.hasText(" ") = false
// * StringUtils.hasText("12345") = true
// * StringUtils.hasText(" 12345 ") = true
// * </pre>
// * @param str the {@code CharSequence} to check (may be {@code null})
// * @return {@code true} if the {@code CharSequence} is not {@code null},
// * its length is greater than 0, and it does not contain whitespace only
// * @see Character#isWhitespace
// */
// private static boolean StringUtils_hasText(CharSequence str) {
// return (StringUtils_hasLength(str) && StringUtils_containsText(str));
// }
//
// private static boolean StringUtils_containsText(CharSequence str) {
// int strLen = str.length();
// for (int i = 0; i < strLen; i++) {
// if (!Character.isWhitespace(str.charAt(i))) {
// return true;
// }
// }
// return false;
// }
//
// /**
// * Check whether the given {@code String} is empty.
// * <p>This method accepts any Object as an argument, comparing it to
// * {@code null} and the empty String. As a consequence, this method
// * will never return {@code true} for a non-null non-String object.
// * <p>The Object signature is useful for general attribute handling code
// * that commonly deals with Strings but generally has to iterate over
// * Objects since attributes may e.g. be primitive value objects as well.
// * @param str the candidate String
// * @since 3.2.1
// */
// private static boolean StringUtils_isEmpty(Object str) {
// return (str == null || "".equals(str));
// }
//}

View File

@ -0,0 +1,271 @@
package com.taobao.arthas.bytekit.utils;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.benf.cfr.reader.api.ClassFileSource;
import org.benf.cfr.reader.bytecode.analysis.parse.utils.Pair;
import org.benf.cfr.reader.entities.ClassFile;
import org.benf.cfr.reader.entities.Method;
import org.benf.cfr.reader.relationship.MemberNameResolver;
import org.benf.cfr.reader.state.ClassFileSourceImpl;
import org.benf.cfr.reader.state.DCCommonState;
import org.benf.cfr.reader.state.TypeUsageCollector;
import org.benf.cfr.reader.state.TypeUsageInformation;
import org.benf.cfr.reader.util.AnalysisType;
import org.benf.cfr.reader.util.CannotLoadClassException;
import org.benf.cfr.reader.util.ConfusedCFRException;
import org.benf.cfr.reader.util.ListFactory;
import org.benf.cfr.reader.util.getopt.GetOptParser;
import org.benf.cfr.reader.util.getopt.Options;
import org.benf.cfr.reader.util.getopt.OptionsImpl;
import org.benf.cfr.reader.util.output.Dumper;
import org.benf.cfr.reader.util.output.DumperFactory;
import org.benf.cfr.reader.util.output.DumperFactoryImpl;
import org.benf.cfr.reader.util.output.IllegalIdentifierDump;
import org.benf.cfr.reader.util.output.StreamDumper;
import org.benf.cfr.reader.util.output.ToStringDumper;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.InsnList;
import org.objectweb.asm.tree.MethodNode;
import org.objectweb.asm.util.Printer;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceClassVisitor;
import org.objectweb.asm.util.TraceMethodVisitor;
public class Decompiler {
public static String decompile(byte[] bytecode) throws IOException {
String result = "";
File tempDirectory = FileUtils.getTempDirectory();
File file = new File(tempDirectory, RandomStringUtils.randomAlphabetic(8));
FileUtils.writeByteArrayToFile(file, bytecode);
result = decompile(file.getAbsolutePath(), null);
return result;
}
public static String decompile(String path) throws IOException {
byte[] byteArray = FileUtils.readFileToByteArray(new File(path));
return decompile(byteArray);
}
public static String toString(MethodNode methodNode) {
Printer printer = new Textifier();
TraceMethodVisitor methodPrinter = new TraceMethodVisitor(printer);
methodNode.accept(methodPrinter);
StringWriter sw = new StringWriter();
printer.print(new PrintWriter(sw));
printer.getText().clear();
return sw.toString();
}
public static String toString(ClassNode classNode) {
Printer printer = new Textifier();
StringWriter sw = new StringWriter();
PrintWriter printWriter = new PrintWriter(sw);
TraceClassVisitor traceClassVisitor = new TraceClassVisitor(printWriter);
classNode.accept(traceClassVisitor);
printer.print(printWriter);
printer.getText().clear();
return sw.toString();
}
public static String toString(InsnList insnList) {
Printer printer = new Textifier();
TraceMethodVisitor mp = new TraceMethodVisitor(printer);
insnList.accept(mp);
StringWriter sw = new StringWriter();
printer.print(new PrintWriter(sw));
printer.getText().clear();
return sw.toString();
}
public static String toString(AbstractInsnNode insn) {
Printer printer = new Textifier();
TraceMethodVisitor mp = new TraceMethodVisitor(printer);
insn.accept(mp);
StringWriter sw = new StringWriter();
printer.print(new PrintWriter(sw));
printer.getText().clear();
return sw.toString();
}
/**
* @see org.benf.cfr.reader.Main#main(String[])
* @param classFilePath
* @param methodName
* @return
*/
public static String decompile(String classFilePath, String methodName) {
StringBuilder result = new StringBuilder(8192);
List<String> argList = new ArrayList<String>();
argList.add(classFilePath);
if (methodName != null) {
argList.add("--methodname");
argList.add(methodName);
}
String args[] = argList.toArray(new String[0]);
GetOptParser getOptParser = new GetOptParser();
Options options = null;
List<String> files = null;
try {
Pair processedArgs = getOptParser.parse(args, OptionsImpl.getFactory());
files = (List) processedArgs.getFirst();
options = (Options) processedArgs.getSecond();
} catch (Exception e) {
getOptParser.showHelp(OptionsImpl.getFactory(), e);
System.exit(1);
}
if ((options.optionIsSet(OptionsImpl.HELP)) || (files.isEmpty())) {
getOptParser.showOptionHelp(OptionsImpl.getFactory(), options, OptionsImpl.HELP);
return "";
}
ClassFileSourceImpl classFileSource = new ClassFileSourceImpl(options);
boolean skipInnerClass = (files.size() > 1)
&& (((Boolean) options.getOption(OptionsImpl.SKIP_BATCH_INNER_CLASSES)).booleanValue());
Collections.sort(files);
for (String path : files) {
classFileSource.clearConfiguration();
DCCommonState dcCommonState = new DCCommonState(options, classFileSource);
DumperFactory dumperFactory = new DumperFactoryImpl(options);
path = classFileSource.adjustInputPath(path);
AnalysisType type = (AnalysisType) options.getOption(OptionsImpl.ANALYSE_AS);
if (type == null)
type = dcCommonState.detectClsJar(path);
if (type == AnalysisType.JAR) {
// doJar(dcCommonState, path, dumperFactory);
}
if (type == AnalysisType.CLASS)
result.append(doClass(dcCommonState, path, skipInnerClass, dumperFactory));
}
return result.toString();
}
public static String doClass(DCCommonState dcCommonState, String path, boolean skipInnerClass,
DumperFactory dumperFactory) {
StringBuilder result = new StringBuilder(8192);
Options options = dcCommonState.getOptions();
IllegalIdentifierDump illegalIdentifierDump = IllegalIdentifierDump.Factory.get(options);
Dumper d = new ToStringDumper();
try {
ClassFile c = dcCommonState.getClassFileMaybePath(path);
if ((skipInnerClass) && (c.isInnerClass()))
return "";
dcCommonState.configureWith(c);
dumperFactory.getProgressDumper().analysingType(c.getClassType());
try {
c = dcCommonState.getClassFile(c.getClassType());
} catch (CannotLoadClassException e) {
}
if (((Boolean) options.getOption(OptionsImpl.DECOMPILE_INNER_CLASSES)).booleanValue()) {
c.loadInnerClasses(dcCommonState);
}
if (((Boolean) options.getOption(OptionsImpl.RENAME_DUP_MEMBERS)).booleanValue()) {
MemberNameResolver.resolveNames(dcCommonState,
ListFactory.newList(dcCommonState.getClassCache().getLoadedTypes()));
}
c.analyseTop(dcCommonState);
TypeUsageCollector collectingDumper = new TypeUsageCollector(c);
c.collectTypeUsages(collectingDumper);
d = new StringDumper(collectingDumper.getTypeUsageInformation(), options, illegalIdentifierDump);
// d = dumperFactory.getNewTopLevelDumper(c.getClassType(), summaryDumper,
// collectingDumper.getTypeUsageInformation(), illegalIdentifierDump);
String methname = (String) options.getOption(OptionsImpl.METHODNAME);
if (methname == null)
c.dump(d);
else {
try {
for (Method method : c.getMethodByName(methname))
method.dump(d, true);
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException("No such method '" + methname + "'.");
}
}
d.print("");
result.append(d.toString());
} catch (ConfusedCFRException e) {
result.append(e.toString()).append("\n");
for (Object x : e.getStackTrace())
result.append(x).append("\n");
} catch (CannotLoadClassException e) {
result.append("Can't load the class specified:").append("\n");
result.append(e.toString()).append("\n");
} catch (RuntimeException e) {
result.append(e.toString()).append("\n");
for (Object x : e.getStackTrace())
result.append(x).append("\n");
} finally {
if (d != null)
d.close();
}
return result.toString();
}
public static class StringDumper extends StreamDumper {
private StringWriter sw = new StringWriter();
public StringDumper(TypeUsageInformation typeUsageInformation, Options options,
IllegalIdentifierDump illegalIdentifierDump) {
super(typeUsageInformation, options, illegalIdentifierDump);
}
public void addSummaryError(Method paramMethod, String paramString) {
}
public void close() {
try {
sw.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
protected void write(String source) {
sw.write(source);
}
public String toString() {
return sw.toString();
}
}
}

View File

@ -0,0 +1,13 @@
package com.taobao.arthas.bytekit.utils;
public class InstanceUtils {
public static <T> T newInstance(Class<T> clazz) {
try {
return clazz.newInstance();
} catch (Exception e) {
throw new IllegalArgumentException(e);
}
}
}

View File

@ -0,0 +1,169 @@
package com.taobao.arthas.bytekit.utils;
import java.util.ArrayList;
import java.util.Stack;
/**
* from org.apache.commons.io.FilenameUtils
*
* @author hengyunabc
*
*/
public class MatchUtils {
/**
* The wildcard matcher uses the characters '?' and '*' to represent a
* single or multiple wildcard characters.
*
* @param str
* @param wildcardMatcher
* @return
*/
public static boolean wildcardMatch(String str, String wildcardMatcher) {
return wildcardMatch(str, wildcardMatcher, false);
}
/**
* The wildcard matcher uses the characters '?' and '*' to represent a
* single or multiple wildcard characters.
*
* @param str
* @param wildcardMatcher
* @param sensitive
* if sensitive is true, str and wildcardMatcher will
* toLowerCase.
* @return
*/
public static boolean wildcardMatch(String str, String wildcardMatcher, boolean sensitive) {
if (str == null && wildcardMatcher == null) {
return true;
}
if (str == null || wildcardMatcher == null) {
return false;
}
str = convertCase(str, sensitive);
wildcardMatcher = convertCase(wildcardMatcher, sensitive);
String[] wcs = splitOnTokens(wildcardMatcher);
boolean anyChars = false;
int textIdx = 0;
int wcsIdx = 0;
Stack backtrack = new Stack();
// loop around a backtrack stack, to handle complex * matching
do {
if (backtrack.size() > 0) {
int[] array = (int[]) backtrack.pop();
wcsIdx = array[0];
textIdx = array[1];
anyChars = true;
}
// loop whilst tokens and text left to process
while (wcsIdx < wcs.length) {
if (wcs[wcsIdx].equals("?")) {
// ? so move to next text char
textIdx++;
anyChars = false;
} else if (wcs[wcsIdx].equals("*")) {
// set any chars status
anyChars = true;
if (wcsIdx == wcs.length - 1) {
textIdx = str.length();
}
} else {
// matching text token
if (anyChars) {
// any chars then try to locate text token
textIdx = str.indexOf(wcs[wcsIdx], textIdx);
if (textIdx == -1) {
// token not found
break;
}
int repeat = str.indexOf(wcs[wcsIdx], textIdx + 1);
if (repeat >= 0) {
backtrack.push(new int[] { wcsIdx, repeat });
}
} else {
// matching from current position
if (!str.startsWith(wcs[wcsIdx], textIdx)) {
// couldnt match token
break;
}
}
// matched text token, move text index to end of matched
// token
textIdx += wcs[wcsIdx].length();
anyChars = false;
}
wcsIdx++;
}
// full match
if (wcsIdx == wcs.length && textIdx == str.length()) {
return true;
}
} while (backtrack.size() > 0);
return false;
}
/**
* Splits a string into a number of tokens.
*
* @param text
* the text to split
* @return the tokens, never null
*/
static String[] splitOnTokens(String text) {
// used by wildcardMatch
// package level so a unit test may run on this
if (text.indexOf("?") == -1 && text.indexOf("*") == -1) {
return new String[] { text };
}
char[] array = text.toCharArray();
ArrayList list = new ArrayList();
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < array.length; i++) {
if (array[i] == '?' || array[i] == '*') {
if (buffer.length() != 0) {
list.add(buffer.toString());
buffer.setLength(0);
}
if (array[i] == '?') {
list.add("?");
} else if (list.size() == 0 || (i > 0 && list.get(list.size() - 1).equals("*") == false)) {
list.add("*");
}
} else {
buffer.append(array[i]);
}
}
if (buffer.length() != 0) {
list.add(buffer.toString());
}
return (String[]) list.toArray(new String[list.size()]);
}
/**
* Converts the case of the input String to a standard format. Subsequent
* operations can then use standard String methods.
*
* @param str
* the string to convert, null returns null
* @return the lower-case version if case-insensitive
*/
static String convertCase(String str, boolean sensitive) {
if (str == null) {
return null;
}
return sensitive ? str : str.toLowerCase();
}
}

View File

@ -0,0 +1,5 @@
package com.taobao.arthas.bytekit.utils;
public class MethodUtils {
}

View File

@ -0,0 +1,947 @@
package com.taobao.arthas.bytekit.utils;
///*
// * Copyright 2002-2017 the original author or authors.
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//
//package com.alibaba.bytekit.utils;
//
//import java.lang.reflect.Array;
//import java.util.Arrays;
//import java.util.Collection;
//import java.util.Map;
//
///**
// * Miscellaneous object utility methods.
// *
// * <p>Mainly for internal use within the framework.
// *
// * <p>Thanks to Alex Ruiz for contributing several enhancements to this class!
// *
// * @author Juergen Hoeller
// * @author Keith Donald
// * @author Rod Johnson
// * @author Rob Harrop
// * @author Chris Beams
// * @author Sam Brannen
// * @since 19.03.2004
// * @see ClassUtils
// * @see CollectionUtils
// * @see StringUtils
// */
//public abstract class ObjectUtils {
//
// private static final int INITIAL_HASH = 7;
// private static final int MULTIPLIER = 31;
//
// private static final String EMPTY_STRING = "";
// private static final String NULL_STRING = "null";
// private static final String ARRAY_START = "{";
// private static final String ARRAY_END = "}";
// private static final String EMPTY_ARRAY = ARRAY_START + ARRAY_END;
// private static final String ARRAY_ELEMENT_SEPARATOR = ", ";
//
//
// /**
// * Return whether the given throwable is a checked exception:
// * that is, neither a RuntimeException nor an Error.
// * @param ex the throwable to check
// * @return whether the throwable is a checked exception
// * @see java.lang.Exception
// * @see java.lang.RuntimeException
// * @see java.lang.Error
// */
// public static boolean isCheckedException(Throwable ex) {
// return !(ex instanceof RuntimeException || ex instanceof Error);
// }
//
// /**
// * Check whether the given exception is compatible with the specified
// * exception types, as declared in a throws clause.
// * @param ex the exception to check
// * @param declaredExceptions the exception types declared in the throws clause
// * @return whether the given exception is compatible
// */
// public static boolean isCompatibleWithThrowsClause(Throwable ex, Class<?>... declaredExceptions) {
// if (!isCheckedException(ex)) {
// return true;
// }
// if (declaredExceptions != null) {
// for (Class<?> declaredException : declaredExceptions) {
// if (declaredException.isInstance(ex)) {
// return true;
// }
// }
// }
// return false;
// }
//
// /**
// * Determine whether the given object is an array:
// * either an Object array or a primitive array.
// * @param obj the object to check
// */
// public static boolean isArray(Object obj) {
// return (obj != null && obj.getClass().isArray());
// }
//
// /**
// * Determine whether the given array is empty:
// * i.e. {@code null} or of zero length.
// * @param array the array to check
// * @see #isEmpty(Object)
// */
// public static boolean isEmpty(Object[] array) {
// return (array == null || array.length == 0);
// }
//
// /**
// * Determine whether the given object is empty.
// * <p>This method supports the following object types.
// * <ul>
// * <li>{@code Array}: considered empty if its length is zero</li>
// * <li>{@link CharSequence}: considered empty if its length is zero</li>
// * <li>{@link Collection}: delegates to {@link Collection#isEmpty()}</li>
// * <li>{@link Map}: delegates to {@link Map#isEmpty()}</li>
// * </ul>
// * <p>If the given object is non-null and not one of the aforementioned
// * supported types, this method returns {@code false}.
// * @param obj the object to check
// * @return {@code true} if the object is {@code null} or <em>empty</em>
// * @since 4.2
// * @see ObjectUtils#isEmpty(Object[])
// * @see StringUtils#hasLength(CharSequence)
// * @see StringUtils#isEmpty(Object)
// * @see CollectionUtils#isEmpty(java.util.Collection)
// * @see CollectionUtils#isEmpty(java.util.Map)
// */
// @SuppressWarnings("rawtypes")
// public static boolean isEmpty(Object obj) {
// if (obj == null) {
// return true;
// }
//
// if (obj instanceof CharSequence) {
// return ((CharSequence) obj).length() == 0;
// }
// if (obj.getClass().isArray()) {
// return Array.getLength(obj) == 0;
// }
// if (obj instanceof Collection) {
// return ((Collection) obj).isEmpty();
// }
// if (obj instanceof Map) {
// return ((Map) obj).isEmpty();
// }
//
// // else
// return false;
// }
//
// /**
// * Check whether the given array contains the given element.
// * @param array the array to check (may be {@code null},
// * in which case the return value will always be {@code false})
// * @param element the element to check for
// * @return whether the element has been found in the given array
// */
// public static boolean containsElement(Object[] array, Object element) {
// if (array == null) {
// return false;
// }
// for (Object arrayEle : array) {
// if (nullSafeEquals(arrayEle, element)) {
// return true;
// }
// }
// return false;
// }
//
// /**
// * Check whether the given array of enum constants contains a constant with the given name,
// * ignoring case when determining a match.
// * @param enumValues the enum values to check, typically the product of a call to MyEnum.values()
// * @param constant the constant name to find (must not be null or empty string)
// * @return whether the constant has been found in the given array
// */
// public static boolean containsConstant(Enum<?>[] enumValues, String constant) {
// return containsConstant(enumValues, constant, false);
// }
//
// /**
// * Check whether the given array of enum constants contains a constant with the given name.
// * @param enumValues the enum values to check, typically the product of a call to MyEnum.values()
// * @param constant the constant name to find (must not be null or empty string)
// * @param caseSensitive whether case is significant in determining a match
// * @return whether the constant has been found in the given array
// */
// public static boolean containsConstant(Enum<?>[] enumValues, String constant, boolean caseSensitive) {
// for (Enum<?> candidate : enumValues) {
// if (caseSensitive ?
// candidate.toString().equals(constant) :
// candidate.toString().equalsIgnoreCase(constant)) {
// return true;
// }
// }
// return false;
// }
//
// /**
// * Case insensitive alternative to {@link Enum#valueOf(Class, String)}.
// * @param <E> the concrete Enum type
// * @param enumValues the array of all Enum constants in question, usually per Enum.values()
// * @param constant the constant to get the enum value of
// * @throws IllegalArgumentException if the given constant is not found in the given array
// * of enum values. Use {@link #containsConstant(Enum[], String)} as a guard to avoid this exception.
// */
// public static <E extends Enum<?>> E caseInsensitiveValueOf(E[] enumValues, String constant) {
// for (E candidate : enumValues) {
// if (candidate.toString().equalsIgnoreCase(constant)) {
// return candidate;
// }
// }
// throw new IllegalArgumentException(
// String.format("constant [%s] does not exist in enum type %s",
// constant, enumValues.getClass().getComponentType().getName()));
// }
//
// /**
// * Append the given object to the given array, returning a new array
// * consisting of the input array contents plus the given object.
// * @param array the array to append to (can be {@code null})
// * @param obj the object to append
// * @return the new array (of the same component type; never {@code null})
// */
// public static <A, O extends A> A[] addObjectToArray(A[] array, O obj) {
// Class<?> compType = Object.class;
// if (array != null) {
// compType = array.getClass().getComponentType();
// }
// else if (obj != null) {
// compType = obj.getClass();
// }
// int newArrLength = (array != null ? array.length + 1 : 1);
// @SuppressWarnings("unchecked")
// A[] newArr = (A[]) Array.newInstance(compType, newArrLength);
// if (array != null) {
// System.arraycopy(array, 0, newArr, 0, array.length);
// }
// newArr[newArr.length - 1] = obj;
// return newArr;
// }
//
// /**
// * Convert the given array (which may be a primitive array) to an
// * object array (if necessary of primitive wrapper objects).
// * <p>A {@code null} source value will be converted to an
// * empty Object array.
// * @param source the (potentially primitive) array
// * @return the corresponding object array (never {@code null})
// * @throws IllegalArgumentException if the parameter is not an array
// */
// public static Object[] toObjectArray(Object source) {
// if (source instanceof Object[]) {
// return (Object[]) source;
// }
// if (source == null) {
// return new Object[0];
// }
// if (!source.getClass().isArray()) {
// throw new IllegalArgumentException("Source is not an array: " + source);
// }
// int length = Array.getLength(source);
// if (length == 0) {
// return new Object[0];
// }
// Class<?> wrapperType = Array.get(source, 0).getClass();
// Object[] newArray = (Object[]) Array.newInstance(wrapperType, length);
// for (int i = 0; i < length; i++) {
// newArray[i] = Array.get(source, i);
// }
// return newArray;
// }
//
//
// //---------------------------------------------------------------------
// // Convenience methods for content-based equality/hash-code handling
// //---------------------------------------------------------------------
//
// /**
// * Determine if the given objects are equal, returning {@code true} if
// * both are {@code null} or {@code false} if only one is {@code null}.
// * <p>Compares arrays with {@code Arrays.equals}, performing an equality
// * check based on the array elements rather than the array reference.
// * @param o1 first Object to compare
// * @param o2 second Object to compare
// * @return whether the given objects are equal
// * @see Object#equals(Object)
// * @see java.util.Arrays#equals
// */
// public static boolean nullSafeEquals(Object o1, Object o2) {
// if (o1 == o2) {
// return true;
// }
// if (o1 == null || o2 == null) {
// return false;
// }
// if (o1.equals(o2)) {
// return true;
// }
// if (o1.getClass().isArray() && o2.getClass().isArray()) {
// return arrayEquals(o1, o2);
// }
// return false;
// }
//
// /**
// * Compare the given arrays with {@code Arrays.equals}, performing an equality
// * check based on the array elements rather than the array reference.
// * @param o1 first array to compare
// * @param o2 second array to compare
// * @return whether the given objects are equal
// * @see #nullSafeEquals(Object, Object)
// * @see java.util.Arrays#equals
// */
// private static boolean arrayEquals(Object o1, Object o2) {
// if (o1 instanceof Object[] && o2 instanceof Object[]) {
// return Arrays.equals((Object[]) o1, (Object[]) o2);
// }
// if (o1 instanceof boolean[] && o2 instanceof boolean[]) {
// return Arrays.equals((boolean[]) o1, (boolean[]) o2);
// }
// if (o1 instanceof byte[] && o2 instanceof byte[]) {
// return Arrays.equals((byte[]) o1, (byte[]) o2);
// }
// if (o1 instanceof char[] && o2 instanceof char[]) {
// return Arrays.equals((char[]) o1, (char[]) o2);
// }
// if (o1 instanceof double[] && o2 instanceof double[]) {
// return Arrays.equals((double[]) o1, (double[]) o2);
// }
// if (o1 instanceof float[] && o2 instanceof float[]) {
// return Arrays.equals((float[]) o1, (float[]) o2);
// }
// if (o1 instanceof int[] && o2 instanceof int[]) {
// return Arrays.equals((int[]) o1, (int[]) o2);
// }
// if (o1 instanceof long[] && o2 instanceof long[]) {
// return Arrays.equals((long[]) o1, (long[]) o2);
// }
// if (o1 instanceof short[] && o2 instanceof short[]) {
// return Arrays.equals((short[]) o1, (short[]) o2);
// }
// return false;
// }
//
// /**
// * Return as hash code for the given object; typically the value of
// * {@code Object#hashCode()}}. If the object is an array,
// * this method will delegate to any of the {@code nullSafeHashCode}
// * methods for arrays in this class. If the object is {@code null},
// * this method returns 0.
// * @see Object#hashCode()
// * @see #nullSafeHashCode(Object[])
// * @see #nullSafeHashCode(boolean[])
// * @see #nullSafeHashCode(byte[])
// * @see #nullSafeHashCode(char[])
// * @see #nullSafeHashCode(double[])
// * @see #nullSafeHashCode(float[])
// * @see #nullSafeHashCode(int[])
// * @see #nullSafeHashCode(long[])
// * @see #nullSafeHashCode(short[])
// */
// public static int nullSafeHashCode(Object obj) {
// if (obj == null) {
// return 0;
// }
// if (obj.getClass().isArray()) {
// if (obj instanceof Object[]) {
// return nullSafeHashCode((Object[]) obj);
// }
// if (obj instanceof boolean[]) {
// return nullSafeHashCode((boolean[]) obj);
// }
// if (obj instanceof byte[]) {
// return nullSafeHashCode((byte[]) obj);
// }
// if (obj instanceof char[]) {
// return nullSafeHashCode((char[]) obj);
// }
// if (obj instanceof double[]) {
// return nullSafeHashCode((double[]) obj);
// }
// if (obj instanceof float[]) {
// return nullSafeHashCode((float[]) obj);
// }
// if (obj instanceof int[]) {
// return nullSafeHashCode((int[]) obj);
// }
// if (obj instanceof long[]) {
// return nullSafeHashCode((long[]) obj);
// }
// if (obj instanceof short[]) {
// return nullSafeHashCode((short[]) obj);
// }
// }
// return obj.hashCode();
// }
//
// /**
// * Return a hash code based on the contents of the specified array.
// * If {@code array} is {@code null}, this method returns 0.
// */
// public static int nullSafeHashCode(Object[] array) {
// if (array == null) {
// return 0;
// }
// int hash = INITIAL_HASH;
// for (Object element : array) {
// hash = MULTIPLIER * hash + nullSafeHashCode(element);
// }
// return hash;
// }
//
// /**
// * Return a hash code based on the contents of the specified array.
// * If {@code array} is {@code null}, this method returns 0.
// */
// public static int nullSafeHashCode(boolean[] array) {
// if (array == null) {
// return 0;
// }
// int hash = INITIAL_HASH;
// for (boolean element : array) {
// hash = MULTIPLIER * hash + hashCode(element);
// }
// return hash;
// }
//
// /**
// * Return a hash code based on the contents of the specified array.
// * If {@code array} is {@code null}, this method returns 0.
// */
// public static int nullSafeHashCode(byte[] array) {
// if (array == null) {
// return 0;
// }
// int hash = INITIAL_HASH;
// for (byte element : array) {
// hash = MULTIPLIER * hash + element;
// }
// return hash;
// }
//
// /**
// * Return a hash code based on the contents of the specified array.
// * If {@code array} is {@code null}, this method returns 0.
// */
// public static int nullSafeHashCode(char[] array) {
// if (array == null) {
// return 0;
// }
// int hash = INITIAL_HASH;
// for (char element : array) {
// hash = MULTIPLIER * hash + element;
// }
// return hash;
// }
//
// /**
// * Return a hash code based on the contents of the specified array.
// * If {@code array} is {@code null}, this method returns 0.
// */
// public static int nullSafeHashCode(double[] array) {
// if (array == null) {
// return 0;
// }
// int hash = INITIAL_HASH;
// for (double element : array) {
// hash = MULTIPLIER * hash + hashCode(element);
// }
// return hash;
// }
//
// /**
// * Return a hash code based on the contents of the specified array.
// * If {@code array} is {@code null}, this method returns 0.
// */
// public static int nullSafeHashCode(float[] array) {
// if (array == null) {
// return 0;
// }
// int hash = INITIAL_HASH;
// for (float element : array) {
// hash = MULTIPLIER * hash + hashCode(element);
// }
// return hash;
// }
//
// /**
// * Return a hash code based on the contents of the specified array.
// * If {@code array} is {@code null}, this method returns 0.
// */
// public static int nullSafeHashCode(int[] array) {
// if (array == null) {
// return 0;
// }
// int hash = INITIAL_HASH;
// for (int element : array) {
// hash = MULTIPLIER * hash + element;
// }
// return hash;
// }
//
// /**
// * Return a hash code based on the contents of the specified array.
// * If {@code array} is {@code null}, this method returns 0.
// */
// public static int nullSafeHashCode(long[] array) {
// if (array == null) {
// return 0;
// }
// int hash = INITIAL_HASH;
// for (long element : array) {
// hash = MULTIPLIER * hash + hashCode(element);
// }
// return hash;
// }
//
// /**
// * Return a hash code based on the contents of the specified array.
// * If {@code array} is {@code null}, this method returns 0.
// */
// public static int nullSafeHashCode(short[] array) {
// if (array == null) {
// return 0;
// }
// int hash = INITIAL_HASH;
// for (short element : array) {
// hash = MULTIPLIER * hash + element;
// }
// return hash;
// }
//
// /**
// * Return the same value as {@link Boolean#hashCode()}}.
// * @see Boolean#hashCode()
// */
// public static int hashCode(boolean bool) {
// return (bool ? 1231 : 1237);
// }
//
// /**
// * Return the same value as {@link Double#hashCode()}}.
// * @see Double#hashCode()
// */
// public static int hashCode(double dbl) {
// return hashCode(Double.doubleToLongBits(dbl));
// }
//
// /**
// * Return the same value as {@link Float#hashCode()}}.
// * @see Float#hashCode()
// */
// public static int hashCode(float flt) {
// return Float.floatToIntBits(flt);
// }
//
// /**
// * Return the same value as {@link Long#hashCode()}}.
// * @see Long#hashCode()
// */
// public static int hashCode(long lng) {
// return (int) (lng ^ (lng >>> 32));
// }
//
//
// //---------------------------------------------------------------------
// // Convenience methods for toString output
// //---------------------------------------------------------------------
//
// /**
// * Return a String representation of an object's overall identity.
// * @param obj the object (may be {@code null})
// * @return the object's identity as String representation,
// * or an empty String if the object was {@code null}
// */
// public static String identityToString(Object obj) {
// if (obj == null) {
// return EMPTY_STRING;
// }
// return obj.getClass().getName() + "@" + getIdentityHexString(obj);
// }
//
// /**
// * Return a hex String form of an object's identity hash code.
// * @param obj the object
// * @return the object's identity code in hex notation
// */
// public static String getIdentityHexString(Object obj) {
// return Integer.toHexString(System.identityHashCode(obj));
// }
//
// /**
// * Return a content-based String representation if {@code obj} is
// * not {@code null}; otherwise returns an empty String.
// * <p>Differs from {@link #nullSafeToString(Object)} in that it returns
// * an empty String rather than "null" for a {@code null} value.
// * @param obj the object to build a display String for
// * @return a display String representation of {@code obj}
// * @see #nullSafeToString(Object)
// */
// public static String getDisplayString(Object obj) {
// if (obj == null) {
// return EMPTY_STRING;
// }
// return nullSafeToString(obj);
// }
//
// /**
// * Determine the class name for the given object.
// * <p>Returns {@code "null"} if {@code obj} is {@code null}.
// * @param obj the object to introspect (may be {@code null})
// * @return the corresponding class name
// */
// public static String nullSafeClassName(Object obj) {
// return (obj != null ? obj.getClass().getName() : NULL_STRING);
// }
//
// /**
// * Return a String representation of the specified Object.
// * <p>Builds a String representation of the contents in case of an array.
// * Returns {@code "null"} if {@code obj} is {@code null}.
// * @param obj the object to build a String representation for
// * @return a String representation of {@code obj}
// */
// public static String nullSafeToString(Object obj) {
// if (obj == null) {
// return NULL_STRING;
// }
// if (obj instanceof String) {
// return (String) obj;
// }
// if (obj instanceof Object[]) {
// return nullSafeToString((Object[]) obj);
// }
// if (obj instanceof boolean[]) {
// return nullSafeToString((boolean[]) obj);
// }
// if (obj instanceof byte[]) {
// return nullSafeToString((byte[]) obj);
// }
// if (obj instanceof char[]) {
// return nullSafeToString((char[]) obj);
// }
// if (obj instanceof double[]) {
// return nullSafeToString((double[]) obj);
// }
// if (obj instanceof float[]) {
// return nullSafeToString((float[]) obj);
// }
// if (obj instanceof int[]) {
// return nullSafeToString((int[]) obj);
// }
// if (obj instanceof long[]) {
// return nullSafeToString((long[]) obj);
// }
// if (obj instanceof short[]) {
// return nullSafeToString((short[]) obj);
// }
// String str = obj.toString();
// return (str != null ? str : EMPTY_STRING);
// }
//
// /**
// * Return a String representation of the contents of the specified array.
// * <p>The String representation consists of a list of the array's elements,
// * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
// * by the characters {@code ", "} (a comma followed by a space). Returns
// * {@code "null"} if {@code array} is {@code null}.
// * @param array the array to build a String representation for
// * @return a String representation of {@code array}
// */
// public static String nullSafeToString(Object[] array) {
// if (array == null) {
// return NULL_STRING;
// }
// int length = array.length;
// if (length == 0) {
// return EMPTY_ARRAY;
// }
// StringBuilder sb = new StringBuilder();
// for (int i = 0; i < length; i++) {
// if (i == 0) {
// sb.append(ARRAY_START);
// }
// else {
// sb.append(ARRAY_ELEMENT_SEPARATOR);
// }
// sb.append(String.valueOf(array[i]));
// }
// sb.append(ARRAY_END);
// return sb.toString();
// }
//
// /**
// * Return a String representation of the contents of the specified array.
// * <p>The String representation consists of a list of the array's elements,
// * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
// * by the characters {@code ", "} (a comma followed by a space). Returns
// * {@code "null"} if {@code array} is {@code null}.
// * @param array the array to build a String representation for
// * @return a String representation of {@code array}
// */
// public static String nullSafeToString(boolean[] array) {
// if (array == null) {
// return NULL_STRING;
// }
// int length = array.length;
// if (length == 0) {
// return EMPTY_ARRAY;
// }
// StringBuilder sb = new StringBuilder();
// for (int i = 0; i < length; i++) {
// if (i == 0) {
// sb.append(ARRAY_START);
// }
// else {
// sb.append(ARRAY_ELEMENT_SEPARATOR);
// }
//
// sb.append(array[i]);
// }
// sb.append(ARRAY_END);
// return sb.toString();
// }
//
// /**
// * Return a String representation of the contents of the specified array.
// * <p>The String representation consists of a list of the array's elements,
// * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
// * by the characters {@code ", "} (a comma followed by a space). Returns
// * {@code "null"} if {@code array} is {@code null}.
// * @param array the array to build a String representation for
// * @return a String representation of {@code array}
// */
// public static String nullSafeToString(byte[] array) {
// if (array == null) {
// return NULL_STRING;
// }
// int length = array.length;
// if (length == 0) {
// return EMPTY_ARRAY;
// }
// StringBuilder sb = new StringBuilder();
// for (int i = 0; i < length; i++) {
// if (i == 0) {
// sb.append(ARRAY_START);
// }
// else {
// sb.append(ARRAY_ELEMENT_SEPARATOR);
// }
// sb.append(array[i]);
// }
// sb.append(ARRAY_END);
// return sb.toString();
// }
//
// /**
// * Return a String representation of the contents of the specified array.
// * <p>The String representation consists of a list of the array's elements,
// * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
// * by the characters {@code ", "} (a comma followed by a space). Returns
// * {@code "null"} if {@code array} is {@code null}.
// * @param array the array to build a String representation for
// * @return a String representation of {@code array}
// */
// public static String nullSafeToString(char[] array) {
// if (array == null) {
// return NULL_STRING;
// }
// int length = array.length;
// if (length == 0) {
// return EMPTY_ARRAY;
// }
// StringBuilder sb = new StringBuilder();
// for (int i = 0; i < length; i++) {
// if (i == 0) {
// sb.append(ARRAY_START);
// }
// else {
// sb.append(ARRAY_ELEMENT_SEPARATOR);
// }
// sb.append("'").append(array[i]).append("'");
// }
// sb.append(ARRAY_END);
// return sb.toString();
// }
//
// /**
// * Return a String representation of the contents of the specified array.
// * <p>The String representation consists of a list of the array's elements,
// * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
// * by the characters {@code ", "} (a comma followed by a space). Returns
// * {@code "null"} if {@code array} is {@code null}.
// * @param array the array to build a String representation for
// * @return a String representation of {@code array}
// */
// public static String nullSafeToString(double[] array) {
// if (array == null) {
// return NULL_STRING;
// }
// int length = array.length;
// if (length == 0) {
// return EMPTY_ARRAY;
// }
// StringBuilder sb = new StringBuilder();
// for (int i = 0; i < length; i++) {
// if (i == 0) {
// sb.append(ARRAY_START);
// }
// else {
// sb.append(ARRAY_ELEMENT_SEPARATOR);
// }
//
// sb.append(array[i]);
// }
// sb.append(ARRAY_END);
// return sb.toString();
// }
//
// /**
// * Return a String representation of the contents of the specified array.
// * <p>The String representation consists of a list of the array's elements,
// * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
// * by the characters {@code ", "} (a comma followed by a space). Returns
// * {@code "null"} if {@code array} is {@code null}.
// * @param array the array to build a String representation for
// * @return a String representation of {@code array}
// */
// public static String nullSafeToString(float[] array) {
// if (array == null) {
// return NULL_STRING;
// }
// int length = array.length;
// if (length == 0) {
// return EMPTY_ARRAY;
// }
// StringBuilder sb = new StringBuilder();
// for (int i = 0; i < length; i++) {
// if (i == 0) {
// sb.append(ARRAY_START);
// }
// else {
// sb.append(ARRAY_ELEMENT_SEPARATOR);
// }
//
// sb.append(array[i]);
// }
// sb.append(ARRAY_END);
// return sb.toString();
// }
//
// /**
// * Return a String representation of the contents of the specified array.
// * <p>The String representation consists of a list of the array's elements,
// * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
// * by the characters {@code ", "} (a comma followed by a space). Returns
// * {@code "null"} if {@code array} is {@code null}.
// * @param array the array to build a String representation for
// * @return a String representation of {@code array}
// */
// public static String nullSafeToString(int[] array) {
// if (array == null) {
// return NULL_STRING;
// }
// int length = array.length;
// if (length == 0) {
// return EMPTY_ARRAY;
// }
// StringBuilder sb = new StringBuilder();
// for (int i = 0; i < length; i++) {
// if (i == 0) {
// sb.append(ARRAY_START);
// }
// else {
// sb.append(ARRAY_ELEMENT_SEPARATOR);
// }
// sb.append(array[i]);
// }
// sb.append(ARRAY_END);
// return sb.toString();
// }
//
// /**
// * Return a String representation of the contents of the specified array.
// * <p>The String representation consists of a list of the array's elements,
// * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
// * by the characters {@code ", "} (a comma followed by a space). Returns
// * {@code "null"} if {@code array} is {@code null}.
// * @param array the array to build a String representation for
// * @return a String representation of {@code array}
// */
// public static String nullSafeToString(long[] array) {
// if (array == null) {
// return NULL_STRING;
// }
// int length = array.length;
// if (length == 0) {
// return EMPTY_ARRAY;
// }
// StringBuilder sb = new StringBuilder();
// for (int i = 0; i < length; i++) {
// if (i == 0) {
// sb.append(ARRAY_START);
// }
// else {
// sb.append(ARRAY_ELEMENT_SEPARATOR);
// }
// sb.append(array[i]);
// }
// sb.append(ARRAY_END);
// return sb.toString();
// }
//
// /**
// * Return a String representation of the contents of the specified array.
// * <p>The String representation consists of a list of the array's elements,
// * enclosed in curly braces ({@code "{}"}). Adjacent elements are separated
// * by the characters {@code ", "} (a comma followed by a space). Returns
// * {@code "null"} if {@code array} is {@code null}.
// * @param array the array to build a String representation for
// * @return a String representation of {@code array}
// */
// public static String nullSafeToString(short[] array) {
// if (array == null) {
// return NULL_STRING;
// }
// int length = array.length;
// if (length == 0) {
// return EMPTY_ARRAY;
// }
// StringBuilder sb = new StringBuilder();
// for (int i = 0; i < length; i++) {
// if (i == 0) {
// sb.append(ARRAY_START);
// }
// else {
// sb.append(ARRAY_ELEMENT_SEPARATOR);
// }
// sb.append(array[i]);
// }
// sb.append(ARRAY_END);
// return sb.toString();
// }
//
//}

View File

@ -0,0 +1,842 @@
package com.taobao.arthas.bytekit.utils;
///*
// * Copyright 2002-2017 the original author or authors.
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//
//package com.alibaba.bytekit.utils;
//
//import java.lang.reflect.Constructor;
//import java.lang.reflect.Field;
//import java.lang.reflect.InvocationTargetException;
//import java.lang.reflect.Method;
//import java.lang.reflect.Modifier;
//import java.lang.reflect.UndeclaredThrowableException;
//import java.sql.SQLException;
//import java.util.ArrayList;
//import java.util.Arrays;
//import java.util.LinkedList;
//import java.util.List;
//import java.util.Map;
//
///**
// * Simple utility class for working with the reflection API and handling
// * reflection exceptions.
// *
// * <p>Only intended for internal use.
// *
// * @author Juergen Hoeller
// * @author Rob Harrop
// * @author Rod Johnson
// * @author Costin Leau
// * @author Sam Brannen
// * @author Chris Beams
// * @since 1.2.2
// */
//public abstract class ReflectionUtils {
//
// /**
// * Naming prefix for CGLIB-renamed methods.
// * @see #isCglibRenamedMethod
// */
// private static final String CGLIB_RENAMED_METHOD_PREFIX = "CGLIB$";
//
// private static final Method[] NO_METHODS = {};
//
// private static final Field[] NO_FIELDS = {};
//
//
// /**
// * Cache for {@link Class#getDeclaredMethods()} plus equivalent default methods
// * from Java 8 based interfaces, allowing for fast iteration.
// */
// private static final Map<Class<?>, Method[]> declaredMethodsCache =
// new ConcurrentReferenceHashMap<Class<?>, Method[]>(256);
//
// /**
// * Cache for {@link Class#getDeclaredFields()}, allowing for fast iteration.
// */
// private static final Map<Class<?>, Field[]> declaredFieldsCache =
// new ConcurrentReferenceHashMap<Class<?>, Field[]>(256);
//
//
// /**
// * Attempt to find a {@link Field field} on the supplied {@link Class} with the
// * supplied {@code name}. Searches all superclasses up to {@link Object}.
// * @param clazz the class to introspect
// * @param name the name of the field
// * @return the corresponding Field object, or {@code null} if not found
// */
// public static Field findField(Class<?> clazz, String name) {
// return findField(clazz, name, null);
// }
//
// /**
// * Attempt to find a {@link Field field} on the supplied {@link Class} with the
// * supplied {@code name} and/or {@link Class type}. Searches all superclasses
// * up to {@link Object}.
// * @param clazz the class to introspect
// * @param name the name of the field (may be {@code null} if type is specified)
// * @param type the type of the field (may be {@code null} if name is specified)
// * @return the corresponding Field object, or {@code null} if not found
// */
// public static Field findField(Class<?> clazz, String name, Class<?> type) {
// Assert.notNull(clazz, "Class must not be null");
// Assert.isTrue(name != null || type != null, "Either name or type of the field must be specified");
// Class<?> searchType = clazz;
// while (Object.class != searchType && searchType != null) {
// Field[] fields = getDeclaredFields(searchType);
// for (Field field : fields) {
// if ((name == null || name.equals(field.getName())) &&
// (type == null || type.equals(field.getType()))) {
// return field;
// }
// }
// searchType = searchType.getSuperclass();
// }
// return null;
// }
//
// /**
// * Set the field represented by the supplied {@link Field field object} on the
// * specified {@link Object target object} to the specified {@code value}.
// * In accordance with {@link Field#set(Object, Object)} semantics, the new value
// * is automatically unwrapped if the underlying field has a primitive type.
// * <p>Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}.
// * @param field the field to set
// * @param target the target object on which to set the field
// * @param value the value to set (may be {@code null})
// */
// public static void setField(Field field, Object target, Object value) {
// try {
// field.set(target, value);
// }
// catch (IllegalAccessException ex) {
// handleReflectionException(ex);
// throw new IllegalStateException(
// "Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage());
// }
// }
//
// /**
// * Get the field represented by the supplied {@link Field field object} on the
// * specified {@link Object target object}. In accordance with {@link Field#get(Object)}
// * semantics, the returned value is automatically wrapped if the underlying field
// * has a primitive type.
// * <p>Thrown exceptions are handled via a call to {@link #handleReflectionException(Exception)}.
// * @param field the field to get
// * @param target the target object from which to get the field
// * @return the field's current value
// */
// public static Object getField(Field field, Object target) {
// try {
// return field.get(target);
// }
// catch (IllegalAccessException ex) {
// handleReflectionException(ex);
// throw new IllegalStateException(
// "Unexpected reflection exception - " + ex.getClass().getName() + ": " + ex.getMessage());
// }
// }
//
// /**
// * Attempt to find a {@link Method} on the supplied class with the supplied name
// * and no parameters. Searches all superclasses up to {@code Object}.
// * <p>Returns {@code null} if no {@link Method} can be found.
// * @param clazz the class to introspect
// * @param name the name of the method
// * @return the Method object, or {@code null} if none found
// */
// public static Method findMethod(Class<?> clazz, String name) {
// return findMethod(clazz, name, new Class<?>[0]);
// }
//
// /**
// * Attempt to find a {@link Method} on the supplied class with the supplied name
// * and parameter types. Searches all superclasses up to {@code Object}.
// * <p>Returns {@code null} if no {@link Method} can be found.
// * @param clazz the class to introspect
// * @param name the name of the method
// * @param paramTypes the parameter types of the method
// * (may be {@code null} to indicate any signature)
// * @return the Method object, or {@code null} if none found
// */
// public static Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) {
// Assert.notNull(clazz, "Class must not be null");
// Assert.notNull(name, "Method name must not be null");
// Class<?> searchType = clazz;
// while (searchType != null) {
// Method[] methods = (searchType.isInterface() ? searchType.getMethods() : getDeclaredMethods(searchType));
// for (Method method : methods) {
// if (name.equals(method.getName()) &&
// (paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) {
// return method;
// }
// }
// searchType = searchType.getSuperclass();
// }
// return null;
// }
//
// /**
// * Invoke the specified {@link Method} against the supplied target object with no arguments.
// * The target object can be {@code null} when invoking a static {@link Method}.
// * <p>Thrown exceptions are handled via a call to {@link #handleReflectionException}.
// * @param method the method to invoke
// * @param target the target object to invoke the method on
// * @return the invocation result, if any
// * @see #invokeMethod(java.lang.reflect.Method, Object, Object[])
// */
// public static Object invokeMethod(Method method, Object target) {
// return invokeMethod(method, target, new Object[0]);
// }
//
// /**
// * Invoke the specified {@link Method} against the supplied target object with the
// * supplied arguments. The target object can be {@code null} when invoking a
// * static {@link Method}.
// * <p>Thrown exceptions are handled via a call to {@link #handleReflectionException}.
// * @param method the method to invoke
// * @param target the target object to invoke the method on
// * @param args the invocation arguments (may be {@code null})
// * @return the invocation result, if any
// */
// public static Object invokeMethod(Method method, Object target, Object... args) {
// try {
// return method.invoke(target, args);
// }
// catch (Exception ex) {
// handleReflectionException(ex);
// }
// throw new IllegalStateException("Should never get here");
// }
//
// /**
// * Invoke the specified JDBC API {@link Method} against the supplied target
// * object with no arguments.
// * @param method the method to invoke
// * @param target the target object to invoke the method on
// * @return the invocation result, if any
// * @throws SQLException the JDBC API SQLException to rethrow (if any)
// * @see #invokeJdbcMethod(java.lang.reflect.Method, Object, Object[])
// */
// public static Object invokeJdbcMethod(Method method, Object target) throws SQLException {
// return invokeJdbcMethod(method, target, new Object[0]);
// }
//
// /**
// * Invoke the specified JDBC API {@link Method} against the supplied target
// * object with the supplied arguments.
// * @param method the method to invoke
// * @param target the target object to invoke the method on
// * @param args the invocation arguments (may be {@code null})
// * @return the invocation result, if any
// * @throws SQLException the JDBC API SQLException to rethrow (if any)
// * @see #invokeMethod(java.lang.reflect.Method, Object, Object[])
// */
// public static Object invokeJdbcMethod(Method method, Object target, Object... args) throws SQLException {
// try {
// return method.invoke(target, args);
// }
// catch (IllegalAccessException ex) {
// handleReflectionException(ex);
// }
// catch (InvocationTargetException ex) {
// if (ex.getTargetException() instanceof SQLException) {
// throw (SQLException) ex.getTargetException();
// }
// handleInvocationTargetException(ex);
// }
// throw new IllegalStateException("Should never get here");
// }
//
// /**
// * Handle the given reflection exception. Should only be called if no
// * checked exception is expected to be thrown by the target method.
// * <p>Throws the underlying RuntimeException or Error in case of an
// * InvocationTargetException with such a root cause. Throws an
// * IllegalStateException with an appropriate message or
// * UndeclaredThrowableException otherwise.
// * @param ex the reflection exception to handle
// */
// public static void handleReflectionException(Exception ex) {
// if (ex instanceof NoSuchMethodException) {
// throw new IllegalStateException("Method not found: " + ex.getMessage());
// }
// if (ex instanceof IllegalAccessException) {
// throw new IllegalStateException("Could not access method: " + ex.getMessage());
// }
// if (ex instanceof InvocationTargetException) {
// handleInvocationTargetException((InvocationTargetException) ex);
// }
// if (ex instanceof RuntimeException) {
// throw (RuntimeException) ex;
// }
// throw new UndeclaredThrowableException(ex);
// }
//
// /**
// * Handle the given invocation target exception. Should only be called if no
// * checked exception is expected to be thrown by the target method.
// * <p>Throws the underlying RuntimeException or Error in case of such a root
// * cause. Throws an UndeclaredThrowableException otherwise.
// * @param ex the invocation target exception to handle
// */
// public static void handleInvocationTargetException(InvocationTargetException ex) {
// rethrowRuntimeException(ex.getTargetException());
// }
//
// /**
// * Rethrow the given {@link Throwable exception}, which is presumably the
// * <em>target exception</em> of an {@link InvocationTargetException}.
// * Should only be called if no checked exception is expected to be thrown
// * by the target method.
// * <p>Rethrows the underlying exception cast to a {@link RuntimeException} or
// * {@link Error} if appropriate; otherwise, throws an
// * {@link UndeclaredThrowableException}.
// * @param ex the exception to rethrow
// * @throws RuntimeException the rethrown exception
// */
// public static void rethrowRuntimeException(Throwable ex) {
// if (ex instanceof RuntimeException) {
// throw (RuntimeException) ex;
// }
// if (ex instanceof Error) {
// throw (Error) ex;
// }
// throw new UndeclaredThrowableException(ex);
// }
//
// /**
// * Rethrow the given {@link Throwable exception}, which is presumably the
// * <em>target exception</em> of an {@link InvocationTargetException}.
// * Should only be called if no checked exception is expected to be thrown
// * by the target method.
// * <p>Rethrows the underlying exception cast to an {@link Exception} or
// * {@link Error} if appropriate; otherwise, throws an
// * {@link UndeclaredThrowableException}.
// * @param ex the exception to rethrow
// * @throws Exception the rethrown exception (in case of a checked exception)
// */
// public static void rethrowException(Throwable ex) throws Exception {
// if (ex instanceof Exception) {
// throw (Exception) ex;
// }
// if (ex instanceof Error) {
// throw (Error) ex;
// }
// throw new UndeclaredThrowableException(ex);
// }
//
// /**
// * Determine whether the given method explicitly declares the given
// * exception or one of its superclasses, which means that an exception
// * of that type can be propagated as-is within a reflective invocation.
// * @param method the declaring method
// * @param exceptionType the exception to throw
// * @return {@code true} if the exception can be thrown as-is;
// * {@code false} if it needs to be wrapped
// */
// public static boolean declaresException(Method method, Class<?> exceptionType) {
// Assert.notNull(method, "Method must not be null");
// Class<?>[] declaredExceptions = method.getExceptionTypes();
// for (Class<?> declaredException : declaredExceptions) {
// if (declaredException.isAssignableFrom(exceptionType)) {
// return true;
// }
// }
// return false;
// }
//
// /**
// * Determine whether the given field is a "public static final" constant.
// * @param field the field to check
// */
// public static boolean isPublicStaticFinal(Field field) {
// int modifiers = field.getModifiers();
// return (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers));
// }
//
// /**
// * Determine whether the given method is an "equals" method.
// * @see java.lang.Object#equals(Object)
// */
// public static boolean isEqualsMethod(Method method) {
// if (method == null || !method.getName().equals("equals")) {
// return false;
// }
// Class<?>[] paramTypes = method.getParameterTypes();
// return (paramTypes.length == 1 && paramTypes[0] == Object.class);
// }
//
// /**
// * Determine whether the given method is a "hashCode" method.
// * @see java.lang.Object#hashCode()
// */
// public static boolean isHashCodeMethod(Method method) {
// return (method != null && method.getName().equals("hashCode") && method.getParameterTypes().length == 0);
// }
//
// /**
// * Determine whether the given method is a "toString" method.
// * @see java.lang.Object#toString()
// */
// public static boolean isToStringMethod(Method method) {
// return (method != null && method.getName().equals("toString") && method.getParameterTypes().length == 0);
// }
//
// /**
// * Determine whether the given method is originally declared by {@link java.lang.Object}.
// */
// public static boolean isObjectMethod(Method method) {
// if (method == null) {
// return false;
// }
// try {
// Object.class.getDeclaredMethod(method.getName(), method.getParameterTypes());
// return true;
// }
// catch (Exception ex) {
// return false;
// }
// }
//
// /**
// * Determine whether the given method is a CGLIB 'renamed' method,
// * following the pattern "CGLIB$methodName$0".
// * @param renamedMethod the method to check
// * @see org.springframework.cglib.proxy.Enhancer#rename
// */
// public static boolean isCglibRenamedMethod(Method renamedMethod) {
// String name = renamedMethod.getName();
// if (name.startsWith(CGLIB_RENAMED_METHOD_PREFIX)) {
// int i = name.length() - 1;
// while (i >= 0 && Character.isDigit(name.charAt(i))) {
// i--;
// }
// return ((i > CGLIB_RENAMED_METHOD_PREFIX.length()) &&
// (i < name.length() - 1) && name.charAt(i) == '$');
// }
// return false;
// }
//
// /**
// * Make the given field accessible, explicitly setting it accessible if
// * necessary. The {@code setAccessible(true)} method is only called
// * when actually necessary, to avoid unnecessary conflicts with a JVM
// * SecurityManager (if active).
// * @param field the field to make accessible
// * @see java.lang.reflect.Field#setAccessible
// */
// public static void makeAccessible(Field field) {
// if ((!Modifier.isPublic(field.getModifiers()) ||
// !Modifier.isPublic(field.getDeclaringClass().getModifiers()) ||
// Modifier.isFinal(field.getModifiers())) && !field.isAccessible()) {
// field.setAccessible(true);
// }
// }
//
// /**
// * Make the given method accessible, explicitly setting it accessible if
// * necessary. The {@code setAccessible(true)} method is only called
// * when actually necessary, to avoid unnecessary conflicts with a JVM
// * SecurityManager (if active).
// * @param method the method to make accessible
// * @see java.lang.reflect.Method#setAccessible
// */
// public static void makeAccessible(Method method) {
// if ((!Modifier.isPublic(method.getModifiers()) ||
// !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible()) {
// method.setAccessible(true);
// }
// }
//
// /**
// * Make the given constructor accessible, explicitly setting it accessible
// * if necessary. The {@code setAccessible(true)} method is only called
// * when actually necessary, to avoid unnecessary conflicts with a JVM
// * SecurityManager (if active).
// * @param ctor the constructor to make accessible
// * @see java.lang.reflect.Constructor#setAccessible
// */
// public static void makeAccessible(Constructor<?> ctor) {
// if ((!Modifier.isPublic(ctor.getModifiers()) ||
// !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) && !ctor.isAccessible()) {
// ctor.setAccessible(true);
// }
// }
//
// /**
// * Perform the given callback operation on all matching methods of the given
// * class, as locally declared or equivalent thereof (such as default methods
// * on Java 8 based interfaces that the given class implements).
// * @param clazz the class to introspect
// * @param mc the callback to invoke for each method
// * @since 4.2
// * @see #doWithMethods
// */
// public static void doWithLocalMethods(Class<?> clazz, MethodCallback mc) {
// Method[] methods = getDeclaredMethods(clazz);
// for (Method method : methods) {
// try {
// mc.doWith(method);
// }
// catch (IllegalAccessException ex) {
// throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
// }
// }
// }
//
// /**
// * Perform the given callback operation on all matching methods of the given
// * class and superclasses.
// * <p>The same named method occurring on subclass and superclass will appear
// * twice, unless excluded by a {@link MethodFilter}.
// * @param clazz the class to introspect
// * @param mc the callback to invoke for each method
// * @see #doWithMethods(Class, MethodCallback, MethodFilter)
// */
// public static void doWithMethods(Class<?> clazz, MethodCallback mc) {
// doWithMethods(clazz, mc, null);
// }
//
// /**
// * Perform the given callback operation on all matching methods of the given
// * class and superclasses (or given interface and super-interfaces).
// * <p>The same named method occurring on subclass and superclass will appear
// * twice, unless excluded by the specified {@link MethodFilter}.
// * @param clazz the class to introspect
// * @param mc the callback to invoke for each method
// * @param mf the filter that determines the methods to apply the callback to
// */
// public static void doWithMethods(Class<?> clazz, MethodCallback mc, MethodFilter mf) {
// // Keep backing up the inheritance hierarchy.
// Method[] methods = getDeclaredMethods(clazz);
// for (Method method : methods) {
// if (mf != null && !mf.matches(method)) {
// continue;
// }
// try {
// mc.doWith(method);
// }
// catch (IllegalAccessException ex) {
// throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
// }
// }
// if (clazz.getSuperclass() != null) {
// doWithMethods(clazz.getSuperclass(), mc, mf);
// }
// else if (clazz.isInterface()) {
// for (Class<?> superIfc : clazz.getInterfaces()) {
// doWithMethods(superIfc, mc, mf);
// }
// }
// }
//
// /**
// * Get all declared methods on the leaf class and all superclasses.
// * Leaf class methods are included first.
// * @param leafClass the class to introspect
// */
// public static Method[] getAllDeclaredMethods(Class<?> leafClass) {
// final List<Method> methods = new ArrayList<Method>(32);
// doWithMethods(leafClass, new MethodCallback() {
// @Override
// public void doWith(Method method) {
// methods.add(method);
// }
// });
// return methods.toArray(new Method[methods.size()]);
// }
//
// /**
// * Get the unique set of declared methods on the leaf class and all superclasses.
// * Leaf class methods are included first and while traversing the superclass hierarchy
// * any methods found with signatures matching a method already included are filtered out.
// * @param leafClass the class to introspect
// */
// public static Method[] getUniqueDeclaredMethods(Class<?> leafClass) {
// final List<Method> methods = new ArrayList<Method>(32);
// doWithMethods(leafClass, new MethodCallback() {
// @Override
// public void doWith(Method method) {
// boolean knownSignature = false;
// Method methodBeingOverriddenWithCovariantReturnType = null;
// for (Method existingMethod : methods) {
// if (method.getName().equals(existingMethod.getName()) &&
// Arrays.equals(method.getParameterTypes(), existingMethod.getParameterTypes())) {
// // Is this a covariant return type situation?
// if (existingMethod.getReturnType() != method.getReturnType() &&
// existingMethod.getReturnType().isAssignableFrom(method.getReturnType())) {
// methodBeingOverriddenWithCovariantReturnType = existingMethod;
// }
// else {
// knownSignature = true;
// }
// break;
// }
// }
// if (methodBeingOverriddenWithCovariantReturnType != null) {
// methods.remove(methodBeingOverriddenWithCovariantReturnType);
// }
// if (!knownSignature && !isCglibRenamedMethod(method)) {
// methods.add(method);
// }
// }
// });
// return methods.toArray(new Method[methods.size()]);
// }
//
// /**
// * This variant retrieves {@link Class#getDeclaredMethods()} from a local cache
// * in order to avoid the JVM's SecurityManager check and defensive array copying.
// * In addition, it also includes Java 8 default methods from locally implemented
// * interfaces, since those are effectively to be treated just like declared methods.
// * @param clazz the class to introspect
// * @return the cached array of methods
// * @see Class#getDeclaredMethods()
// */
// private static Method[] getDeclaredMethods(Class<?> clazz) {
// Assert.notNull(clazz, "Class must not be null");
// Method[] result = declaredMethodsCache.get(clazz);
// if (result == null) {
// Method[] declaredMethods = clazz.getDeclaredMethods();
// List<Method> defaultMethods = findConcreteMethodsOnInterfaces(clazz);
// if (defaultMethods != null) {
// result = new Method[declaredMethods.length + defaultMethods.size()];
// System.arraycopy(declaredMethods, 0, result, 0, declaredMethods.length);
// int index = declaredMethods.length;
// for (Method defaultMethod : defaultMethods) {
// result[index] = defaultMethod;
// index++;
// }
// }
// else {
// result = declaredMethods;
// }
// declaredMethodsCache.put(clazz, (result.length == 0 ? NO_METHODS : result));
// }
// return result;
// }
//
// private static List<Method> findConcreteMethodsOnInterfaces(Class<?> clazz) {
// List<Method> result = null;
// for (Class<?> ifc : clazz.getInterfaces()) {
// for (Method ifcMethod : ifc.getMethods()) {
// if (!Modifier.isAbstract(ifcMethod.getModifiers())) {
// if (result == null) {
// result = new LinkedList<Method>();
// }
// result.add(ifcMethod);
// }
// }
// }
// return result;
// }
//
// /**
// * Invoke the given callback on all fields in the target class, going up the
// * class hierarchy to get all declared fields.
// * @param clazz the target class to analyze
// * @param fc the callback to invoke for each field
// * @since 4.2
// * @see #doWithFields
// */
// public static void doWithLocalFields(Class<?> clazz, FieldCallback fc) {
// for (Field field : getDeclaredFields(clazz)) {
// try {
// fc.doWith(field);
// }
// catch (IllegalAccessException ex) {
// throw new IllegalStateException("Not allowed to access field '" + field.getName() + "': " + ex);
// }
// }
// }
//
// /**
// * Invoke the given callback on all fields in the target class, going up the
// * class hierarchy to get all declared fields.
// * @param clazz the target class to analyze
// * @param fc the callback to invoke for each field
// */
// public static void doWithFields(Class<?> clazz, FieldCallback fc) {
// doWithFields(clazz, fc, null);
// }
//
// /**
// * Invoke the given callback on all fields in the target class, going up the
// * class hierarchy to get all declared fields.
// * @param clazz the target class to analyze
// * @param fc the callback to invoke for each field
// * @param ff the filter that determines the fields to apply the callback to
// */
// public static void doWithFields(Class<?> clazz, FieldCallback fc, FieldFilter ff) {
// // Keep backing up the inheritance hierarchy.
// Class<?> targetClass = clazz;
// do {
// Field[] fields = getDeclaredFields(targetClass);
// for (Field field : fields) {
// if (ff != null && !ff.matches(field)) {
// continue;
// }
// try {
// fc.doWith(field);
// }
// catch (IllegalAccessException ex) {
// throw new IllegalStateException("Not allowed to access field '" + field.getName() + "': " + ex);
// }
// }
// targetClass = targetClass.getSuperclass();
// }
// while (targetClass != null && targetClass != Object.class);
// }
//
// /**
// * This variant retrieves {@link Class#getDeclaredFields()} from a local cache
// * in order to avoid the JVM's SecurityManager check and defensive array copying.
// * @param clazz the class to introspect
// * @return the cached array of fields
// * @see Class#getDeclaredFields()
// */
// private static Field[] getDeclaredFields(Class<?> clazz) {
// Assert.notNull(clazz, "Class must not be null");
// Field[] result = declaredFieldsCache.get(clazz);
// if (result == null) {
// result = clazz.getDeclaredFields();
// declaredFieldsCache.put(clazz, (result.length == 0 ? NO_FIELDS : result));
// }
// return result;
// }
//
// /**
// * Given the source object and the destination, which must be the same class
// * or a subclass, copy all fields, including inherited fields. Designed to
// * work on objects with public no-arg constructors.
// */
// public static void shallowCopyFieldState(final Object src, final Object dest) {
// Assert.notNull(src, "Source for field copy cannot be null");
// Assert.notNull(dest, "Destination for field copy cannot be null");
// if (!src.getClass().isAssignableFrom(dest.getClass())) {
// throw new IllegalArgumentException("Destination class [" + dest.getClass().getName() +
// "] must be same or subclass as source class [" + src.getClass().getName() + "]");
// }
// doWithFields(src.getClass(), new FieldCallback() {
// @Override
// public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
// makeAccessible(field);
// Object srcValue = field.get(src);
// field.set(dest, srcValue);
// }
// }, COPYABLE_FIELDS);
// }
//
// /**
// * Clear the internal method/field cache.
// * @since 4.2.4
// */
// public static void clearCache() {
// declaredMethodsCache.clear();
// declaredFieldsCache.clear();
// }
//
//
// /**
// * Action to take on each method.
// */
// public interface MethodCallback {
//
// /**
// * Perform an operation using the given method.
// * @param method the method to operate on
// */
// void doWith(Method method) throws IllegalArgumentException, IllegalAccessException;
// }
//
//
// /**
// * Callback optionally used to filter methods to be operated on by a method callback.
// */
// public interface MethodFilter {
//
// /**
// * Determine whether the given method matches.
// * @param method the method to check
// */
// boolean matches(Method method);
// }
//
//
// /**
// * Callback interface invoked on each field in the hierarchy.
// */
// public interface FieldCallback {
//
// /**
// * Perform an operation using the given field.
// * @param field the field to operate on
// */
// void doWith(Field field) throws IllegalArgumentException, IllegalAccessException;
// }
//
//
// /**
// * Callback optionally used to filter fields to be operated on by a field callback.
// */
// public interface FieldFilter {
//
// /**
// * Determine whether the given field matches.
// * @param field the field to check
// */
// boolean matches(Field field);
// }
//
//
// /**
// * Pre-built FieldFilter that matches all non-static, non-final fields.
// */
// public static final FieldFilter COPYABLE_FIELDS = new FieldFilter() {
//
// @Override
// public boolean matches(Field field) {
// return !(Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()));
// }
// };
//
//
// /**
// * Pre-built MethodFilter that matches all non-bridge methods.
// */
// public static final MethodFilter NON_BRIDGED_METHODS = new MethodFilter() {
//
// @Override
// public boolean matches(Method method) {
// return !method.isBridge();
// }
// };
//
//
// /**
// * Pre-built MethodFilter that matches all non-bridge methods
// * which are not declared on {@code java.lang.Object}.
// */
// public static final MethodFilter USER_DECLARED_METHODS = new MethodFilter() {
//
// @Override
// public boolean matches(Method method) {
// return (!method.isBridge() && method.getDeclaringClass() != Object.class);
// }
// };
//
//}

View File

@ -0,0 +1,54 @@
package com.taobao.arthas.bytekit.utils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Type;
import org.objectweb.asm.util.CheckClassAdapter;
public class VerifyUtils {
public static void asmVerify(byte[] bytes) throws IOException {
ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
ClassReader cr = new ClassReader(inputStream);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = new CheckClassAdapter(cw);
cr.accept(cv, 0);
}
public static Object instanceVerity(byte[] bytes) throws Exception {
String name = Type.getObjectType(AsmUtils.toClassNode(bytes).name).getClassName();
URLClassLoader systemClassLoader = (URLClassLoader) ClassLoader.getSystemClassLoader();
@SuppressWarnings("resource")
ClassbyteClassLoader cl = new ClassbyteClassLoader(systemClassLoader.getURLs(),
ClassLoader.getSystemClassLoader().getParent());
cl.addClass(name, bytes);
Class<?> loadClass = cl.loadClass(name);
return loadClass.newInstance();
}
public static class ClassbyteClassLoader extends URLClassLoader {
public ClassbyteClassLoader(URL[] urls, ClassLoader cl) {
super(urls, cl);
}
public Class<?> addClass(String name, byte[] bytes) throws ClassFormatError {
Class<?> cl = defineClass(name, bytes, 0, bytes.length);
resolveClass(cl);
return cl;
}
}
}

View File

@ -0,0 +1,89 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtEnter;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtEnterTest {
@Rule
public ExpectedException expectedEx = ExpectedException.none();
@Rule
public OutputCapture capture = new OutputCapture();
public static class Sample {
long longField;
String strField;
static int intField;
public int hello(String str, boolean exception) {
if (exception) {
throw new RuntimeException("test exception");
}
return str.length();
}
public long toBeInvoke(int i , long l, String s, long ll) {
return l + ll;
}
public void testInvokeArgs() {
toBeInvoke(1, 123L, "abc", 100L);
}
}
public static class TestPrintSuppressHandler {
@ExceptionHandler(inline = false)
public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) {
System.err.println("exception handler: " + clazz);
e.printStackTrace();
}
}
public static class EnterInterceptor {
@AtEnter(inline = false
, suppress = RuntimeException.class, suppressHandler = TestPrintSuppressHandler.class
)
public static long onEnter(
@Binding.This Object object, @Binding.Class Object clazz,
@Binding.Field(name = "longField") long longField,
@Binding.Field(name = "longField") Object longFieldObject,
@Binding.Field(name = "intField") int intField,
@Binding.Field(name = "strField") String strField,
@Binding.Field(name = "intField") Object intFielObject
) {
System.err.println("onEnter, object:" + object);
return 123L;
}
}
@Test
public void testEnter() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(EnterInterceptor.class).methodMatcher("hello")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
new Sample().hello("abc", false);
System.err.println(Decompiler.decompile(bytes));
assertThat(capture.toString()).contains("onEnter, object:");
}
}

View File

@ -0,0 +1,84 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExceptionExit;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtExceptionExitTest {
@Rule
public ExpectedException expectedEx = ExpectedException.none();
@Rule
public OutputCapture capture = new OutputCapture();
public static class Sample {
long longField;
String strField;
static int intField;
public int hello(String str, boolean exception) {
if (exception) {
throw new RuntimeException("test exception");
}
return str.length();
}
public long toBeInvoke(int i , long l, String s, long ll) {
return l + ll;
}
public void testInvokeArgs() {
toBeInvoke(1, 123L, "abc", 100L);
}
}
public static class TestPrintSuppressHandler {
@ExceptionHandler(inline = true)
public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) {
System.err.println("exception handler: " + clazz);
System.err.println(e.getMessage());
assertThat(e).hasMessage("exception for ExceptionHandler");
}
}
public static class ExceptionExitInterceptor {
@AtExceptionExit(inline = false, onException = RuntimeException.class ,suppress = Throwable.class, suppressHandler = TestPrintSuppressHandler.class)
public static void onExceptionExit(@Binding.Throwable RuntimeException ex, @Binding.This Object object,
@Binding.Class Object clazz) {
System.err.println("AtExceptionExit, ex:" + ex);
throw new RuntimeException("exception for ExceptionHandler");
}
}
@Test
public void testExecptionExitException() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(ExceptionExitInterceptor.class).methodMatcher("hello")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
System.err.println(Decompiler.decompile(bytes));
try {
new Sample().hello("abc", true);
} catch (Exception e) {
assertThat(e).isInstanceOf(RuntimeException.class).hasMessageContaining("test exception");
}
assertThat(capture.toString()).contains("AtExceptionExit, ex:");
}
}

View File

@ -0,0 +1,93 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtExit;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtExitTest {
@Rule
public OutputCapture capture = new OutputCapture();
static class Sample {
long longField;
int intField;
String strField;
public void voidExit() {
}
public long longExit() {
return 100L;
}
public static long staticExit() {
return 999L;
}
}
public static class TestPrintSuppressHandler {
@ExceptionHandler(inline = false)
public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) {
System.err.println("exception handler: " + clazz);
e.printStackTrace();
}
}
public static class TestAccessInterceptor {
@AtExit(inline = false)
public static void atExit(@Binding.This Object object,
@Binding.Class Object clazz
,
@Binding.Return Object re
) {
System.err.println("AtFieldAccess: this" + object);
}
}
public static class ChangeReturnInterceptor {
@AtExit(inline = false, suppress = RuntimeException.class, suppressHandler = TestPrintSuppressHandler.class)
public static Object onExit(@Binding.This Object object, @Binding.Class Object clazz) {
System.err.println("onExit, object:" + object);
return 123L;
}
}
@Test
public void testExit() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(TestAccessInterceptor.class).methodMatcher("voidExit")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
new Sample().voidExit();
System.err.println(Decompiler.decompile(bytes));
assertThat(capture.toString()).contains("AtFieldAccess: this");
}
@Test
public void testExitAndChangeReturn() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(ChangeReturnInterceptor.class).methodMatcher("longExit")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
System.err.println(Decompiler.decompile(bytes));
long re = new Sample().longExit();
assertThat(re).isEqualTo(123);
assertThat(capture.toString()).contains("onExit, object:");
}
}

View File

@ -0,0 +1,60 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtFieldAccess;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtFieldAccessTest {
@Rule
public OutputCapture capture = new OutputCapture();
class Sample {
long longField;
int intField;
String strField;
public int testReadField(int ii) {
longField = 999;
return 123;
}
}
public static class TestPrintSuppressHandler {
@ExceptionHandler(inline = false)
public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) {
System.err.println("exception handler: " + clazz);
e.printStackTrace();
}
}
public static class FieldAccessInterceptor {
@AtFieldAccess(name = "longField" , inline =false)
public static void onFieldAccess(@Binding.This Object object,
@Binding.Class Object clazz) {
System.err.println("AtFieldAccess: this" + object);
}
}
@Test
public void testEnter() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(FieldAccessInterceptor.class).methodMatcher("testReadField")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
new Sample().testReadField(100);
System.err.println(Decompiler.decompile(bytes));
assertThat(capture.toString()).contains("AtFieldAccess: this");
}
}

View File

@ -0,0 +1,101 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtInvoke;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtInvokeTest {
@Rule
public OutputCapture capture = new OutputCapture();
static class Sample {
long longField;
int intField;
String strField;
public Sample(int i, long l, String s) {
staticToBeCall(i, l, s);
aaa("aaa");
}
public int testCall(int ii) {
toBeCall(ii, 123L, "");
System.err.println("abc");
aaa("abc");
return 123;
}
public void aaa(String aaa) {
return ;
}
public long toBeCall(int i , long l, String s) {
return l + i;
}
public static long staticToBeCall(int i , long l, String s) {
return l + i;
}
}
public static class TestPrintSuppressHandler {
@ExceptionHandler(inline = false)
public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) {
System.err.println("exception handler: " + clazz);
e.printStackTrace();
}
}
public static class TestAccessInterceptor {
@AtInvoke(name = "", inline = false, whenComplete=false, excludes = {"System."})
public static void onInvoke(
@Binding.This Object object,
@Binding.Class Object clazz
,
@Binding.InvokeArgs Object[] args
) {
System.err.println("onInvoke: this" + object);
}
@AtInvoke(name = "toBeCall", inline = false, whenComplete = true)
public static void onInvokeAfter(
@Binding.This Object object,
@Binding.Class Object clazz
,
@Binding.InvokeReturn Object invokeReturn
,
@Binding.InvokeMethodDeclaration String declaration
) {
System.err.println("onInvokeAfter: this" + object);
System.err.println("declaration: " + declaration);
assertThat(declaration).isEqualTo("long toBeCall(int, long, java.lang.String)");
System.err.println("invokeReturn: " + invokeReturn);
assertThat(invokeReturn).isEqualTo(100 + 123L);
}
}
@Test
public void testInvokeBefore() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(TestAccessInterceptor.class).methodMatcher("testCall")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
new Sample(100, 100L, "").testCall(100);
System.err.println(Decompiler.decompile(bytes));
assertThat(capture.toString()).contains("onInvoke: this");
}
}

View File

@ -0,0 +1,85 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtLine;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtLineTest {
@Rule
public OutputCapture capture = new OutputCapture();
static class Sample {
public int testLine(int i) {
String s = "" + i;
if(i > 0) {
String abc = s + i;
i++;
i = i * 100
+ i
- 100 + Math.max(100, i);
i += s.length() + abc.length();
}else {
if(i == -1) {
try {
System.err.println("i is -1");
throw new RuntimeException();
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
return i * 2;
}
}
public static class TestAccessInterceptor {
@AtLine(lines = { -1}, inline = false)
public static void atLine(
@Binding.This Object object,
@Binding.Class Object clazz
,
@Binding.Line int line,
@Binding.Args Object[] args
,
@Binding.ArgNames String[] argNames
,
@Binding.LocalVars Object[] vars,
@Binding.LocalVarNames String[] varNames
) {
System.err.println("atLine: this" + object);
System.err.println("line: " + line);
System.err.println("args: " + Arrays.toString(args));
System.err.println("argNames: " + Arrays.toString(argNames));
System.err.println("vars: " + Arrays.toString(vars));
System.err.println("varNames: " + Arrays.toString(varNames));
}
}
@Test
public void testLine() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(TestAccessInterceptor.class).methodMatcher("*")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
new Sample().testLine(100);
System.err.println(Decompiler.decompile(bytes));
assertThat(capture.toString()).contains("atLine: this");
}
}

View File

@ -0,0 +1,89 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtSyncEnter;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtSyncEnterTest {
@Rule
public OutputCapture capture = new OutputCapture();
static class Sample {
public int testLine(int i) {
String s = "" + i;
synchronized (s) {
if(i > 0) {
String abc = s + i;
i++;
i = i * 100
+ i
- 100 + Math.max(100, i);
i += s.length() + abc.length();
}else {
if(i == -1) {
try {
System.err.println("i is -1");
throw new RuntimeException();
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
}
return i * 2;
}
}
public static class TestInterceptor {
@AtSyncEnter(whenComplete=false, inline = false)
public static void atSyncEnter(
@Binding.This Object object,
@Binding.Class Object clazz
,
@Binding.Args Object[] args
,
@Binding.ArgNames String[] argNames
,
@Binding.LocalVars Object[] vars,
@Binding.LocalVarNames String[] varNames
,
@Binding.Monitor Object monitor
) {
System.err.println("atSyncEnter: this" + object);
System.err.println("args: " + Arrays.toString(args));
System.err.println("argNames: " + Arrays.toString(argNames));
System.err.println("vars: " + Arrays.toString(vars));
System.err.println("varNames: " + Arrays.toString(varNames));
}
}
@Test
public void test() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(TestInterceptor.class).methodMatcher("*")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
new Sample().testLine(100);
System.err.println(Decompiler.decompile(bytes));
assertThat(capture.toString()).contains("atSyncEnter: this");
}
}

View File

@ -0,0 +1,89 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtSyncExit;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtSyncExitTest {
@Rule
public OutputCapture capture = new OutputCapture();
static class Sample {
public int testLine(int i) {
String s = "" + i;
synchronized (s) {
if(i > 0) {
String abc = s + i;
i++;
i = i * 100
+ i
- 100 + Math.max(100, i);
i += s.length() + abc.length();
}else {
if(i == -1) {
try {
System.err.println("i is -1");
throw new RuntimeException();
} catch (Exception e) {
System.err.println(e.getMessage());
}
}
}
}
return i * 2;
}
}
public static class TestInterceptor {
@AtSyncExit(whenComplete=false, inline = false)
public static void atSyncExit(
@Binding.This Object object,
@Binding.Class Object clazz
,
@Binding.Args Object[] args
,
@Binding.ArgNames String[] argNames
,
@Binding.LocalVars Object[] vars,
@Binding.LocalVarNames String[] varNames
,
@Binding.Monitor Object monitor
) {
System.err.println("atSyncExit: this" + object);
System.err.println("args: " + Arrays.toString(args));
System.err.println("argNames: " + Arrays.toString(argNames));
System.err.println("vars: " + Arrays.toString(vars));
System.err.println("varNames: " + Arrays.toString(varNames));
}
}
@Test
public void test() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(TestInterceptor.class).methodMatcher("*")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
new Sample().testLine(100);
System.err.println(Decompiler.decompile(bytes));
assertThat(capture.toString()).contains("atSyncExit: this");
}
}

View File

@ -0,0 +1,76 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtThrow;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.ExceptionHandler;
import com.taobao.arthas.bytekit.utils.Decompiler;
public class AtThrowTest {
@Rule
public OutputCapture capture = new OutputCapture();
static class Sample {
public static long testThrow(int i , long l, String s) {
try {
if(i < 0) {
throw new RuntimeException("eeeee");
}
} catch (Exception e) {
System.err.println(e.getMessage());
}
return l + i;
}
}
public static class TestPrintSuppressHandler {
@ExceptionHandler(inline = false)
public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) {
System.err.println("exception handler: " + clazz);
e.printStackTrace();
}
}
public static class TestAccessInterceptor {
@AtThrow(inline = false)
public static void atThrow(
@Binding.This Object object,
@Binding.Class Object clazz
,
@Binding.LocalVars Object[] vars,
@Binding.Throwable Throwable t
) {
System.err.println("atThrow: this" + object);
System.err.println("vars: " + Arrays.toString(vars));
System.err.println("t: " + t);
assertThat(t).hasMessage("eeeee");
}
}
@Test
public void testThrow() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(TestAccessInterceptor.class).methodMatcher("testThrow")
.redefine(true);
byte[] bytes = helper.process(Sample.class);
Sample.testThrow(-1, 0, null);
System.err.println(Decompiler.decompile(bytes));
assertThat(capture.toString()).contains("atThrow: this");
}
}

View File

@ -0,0 +1,78 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import java.util.ArrayList;
import java.util.List;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
import com.taobao.arthas.bytekit.asm.interceptor.parser.DefaultInterceptorClassParser;
import com.taobao.arthas.bytekit.utils.AgentUtils;
import com.taobao.arthas.bytekit.utils.AsmUtils;
import com.taobao.arthas.bytekit.utils.MatchUtils;
import com.taobao.arthas.bytekit.utils.VerifyUtils;
public class TestHelper {
private Class<?> interceptorClass;
private boolean redefine;
private String methodMatcher = "*";
private boolean asmVerity = true;
public static TestHelper builder() {
return new TestHelper();
}
public TestHelper interceptorClass(Class<?> interceptorClass) {
this.interceptorClass = interceptorClass;
return this;
}
public TestHelper redefine(boolean redefine) {
this.redefine = redefine;
return this;
}
public TestHelper methodMatcher(String methodMatcher) {
this.methodMatcher = methodMatcher;
return this;
}
public byte[] process(Class<?> transform) throws Exception {
DefaultInterceptorClassParser defaultInterceptorClassParser = new DefaultInterceptorClassParser();
List<InterceptorProcessor> interceptorProcessors = defaultInterceptorClassParser.parse(interceptorClass);
ClassNode classNode = AsmUtils.loadClass(transform);
List<MethodNode> matchedMethods = new ArrayList<MethodNode>();
for (MethodNode methodNode : classNode.methods) {
if (MatchUtils.wildcardMatch(methodNode.name, methodMatcher)) {
matchedMethods.add(methodNode);
}
}
for (MethodNode methodNode : matchedMethods) {
MethodProcessor methodProcessor = new MethodProcessor(classNode, methodNode);
for (InterceptorProcessor interceptor : interceptorProcessors) {
interceptor.process(methodProcessor);
}
}
byte[] bytes = AsmUtils.toBytes(classNode);
if (asmVerity) {
VerifyUtils.asmVerify(bytes);
}
if (redefine) {
AgentUtils.redefine(transform, bytes);
}
return bytes;
}
}

View File

@ -0,0 +1,129 @@
package com.taobao.arthas.bytekit.utils;
import java.io.IOException;
import java.util.List;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.AbstractInsnNode;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
import com.taobao.arthas.bytekit.utils.AsmUtils;
import com.taobao.arthas.bytekit.utils.VerifyUtils;
public class AsmUtilsTest {
abstract static class TestClass {
public static synchronized List<String> sss(int i, long l, List<String> list) throws IOException, ArrayIndexOutOfBoundsException {
return null;
}
protected abstract String hello(String ss);
}
static class TestConstructorClass {
public TestConstructorClass(int i, String s) {
}
}
@Test
public void testMethodDeclaration() throws IOException {
ClassNode classNode = AsmUtils.loadClass(TestClass.class);
MethodNode sss = AsmUtils.findFirstMethod(classNode.methods, "sss");
MethodNode hello = AsmUtils.findFirstMethod(classNode.methods, "hello");
MethodNode constructor = AsmUtils.findFirstMethod(AsmUtils.loadClass(TestConstructorClass.class).methods, "<init>");
String helloDeclaration = AsmUtils.methodDeclaration(Type.getType(TestClass.class), hello);
String sssDeclaration = AsmUtils.methodDeclaration(Type.getType(TestClass.class), sss);
String constructorDeclaration = AsmUtils.methodDeclaration(Type.getType(TestConstructorClass.class), constructor);
System.err.println(helloDeclaration);
System.err.println(sssDeclaration);
System.err.println(constructorDeclaration);
Assertions.assertThat(helloDeclaration).isEqualTo("protected abstract java.lang.String hello(java.lang.String)");
Assertions.assertThat(sssDeclaration).isEqualTo(
"public static synchronized java.util.List sss(int, long, java.util.List) throws java.io.IOException, java.lang.ArrayIndexOutOfBoundsException");
Assertions.assertThat(constructorDeclaration).isEqualTo("public com.taobao.arthas.bytekit.utils.AsmUtilsTest$TestConstructorClass(int, java.lang.String)");
}
public static byte[] emptyMethodBytes() throws Exception {
ClassWriter cw = new ClassWriter(0);
MethodVisitor mv;
cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, "LEmptyClass", null, "java/lang/Object", null);
{
mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(Opcodes.ALOAD, 0);
mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
mv.visitInsn(Opcodes.RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
{
mv = cw.visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC, "emptyMethod", "()V", null, null);
// mv.visitCode();
mv.visitInsn(Opcodes.RETURN);
// mv.visitMaxs(0, 0);
// mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
@Test
public void emptyMethodTest() throws Exception {
byte[] emptyMethodBytes = emptyMethodBytes();
VerifyUtils.asmVerify(emptyMethodBytes);
VerifyUtils.instanceVerity(emptyMethodBytes);
ClassNode classNode = AsmUtils.toClassNode(emptyMethodBytes);
MethodNode methodNode = AsmUtils.findFirstMethod(classNode.methods, "emptyMethod");
AbstractInsnNode first = methodNode.instructions.getFirst();
AbstractInsnNode last = methodNode.instructions.getLast();
System.err.println(first);
System.err.println(last);
int size = methodNode.instructions.size();
for (int i = 0; i < size; ++i) {
System.err.println(methodNode.instructions.get(i));
}
// String asmCode = AsmUtils.toASMCode(classNode);
// System.err.println(asmCode);
}
private String aaa = "";
public void xxx () {
aaa = "bbb";
}
@Test
public void testFieldAccess() throws IOException {
ClassNode classNode = AsmUtils.loadClass(AsmUtilsTest.class);
MethodNode methodNode = AsmUtils.findFirstMethod(classNode.methods, "xxx");
int size = methodNode.instructions.size();
for (int i = 0; i < size; ++i) {
System.err.println(methodNode.instructions.get(i));
}
}
}

View File

@ -0,0 +1,9 @@
package com.taobao.arthas.bytekit.utils;
public class EmptyClass {
public static void emptyMethod() {
}
}

12
pom.xml
View File

@ -57,12 +57,14 @@
<modules>
<module>spy</module>
<module>common</module>
<module>bytekit</module>
<module>core</module>
<module>agent</module>
<module>client</module>
<module>memorycompiler</module>
<module>boot</module>
<module>demo</module>
<module>apm-demo</module>
<module>testcase</module>
<module>site</module>
<module>packaging</module>
@ -86,6 +88,16 @@
<artifactId>asm-commons</artifactId>
<version>7.0</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
<version>7.0</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-analysis</artifactId>
<version>7.0</version>
</dependency>
<dependency>
<groupId>com.alibaba.middleware</groupId>
<artifactId>termd-core</artifactId>