move bytekit to a independent project. #1545

This commit is contained in:
hengyunabc
2020-10-21 23:32:58 +08:00
parent 2d0b669e63
commit 8d43d8cc45
133 changed files with 8 additions and 11897 deletions
-222
View File
@@ -1,222 +0,0 @@
## ByteKit
## 目标
1. 之前的Arthas里的字节码增强,是通过asm来处理的,代码逻辑不好修改,理解困难
1. 基于ASM提供更高层的字节码处理能力,面向诊断/APM领域,不是通用的字节码库
1. ByteKit期望能提供一套简洁的API,让开发人员可以比较轻松的完成字节码增强
## 对比
| 功能 | 函数Enter/Exit注入点 | 绑定数据 | inline | 防止重复增强 | 避免装箱/拆箱开销 |origin调用替换 | `@ExceptionHandler` |
| ---- | ---- |---- | :----: |:----: | :----: |:----: | :----: |
| ByteKit | `@AtEnter` <br> `@AtExit` <br>`@AtExceptionExit` <br> `@AtFieldAccess` <br> `@AtInvoke`<br>`@AtInvokeException`<br>`@AtLine`<br>`@AtSyncEnter`<br>`@AtSyncExit`<br>`@AtThrow`| this/args/return/throw<br>field<br>locals<br>子调用入参/返回值/子调用异常<br>line number|✓|✓|✓|✓|✓|
| ByteBuddy | `OnMethodEnter`<br>`@OnMethodExit`<br> `@OnMethodExit#onThrowable()`| this/args/return/throw<br>field<br>locals|✓|✗|✓|✓|✓|
| 传统AOP | `Enter`<br>`Exit`<br>`Exception` |this/args/return/throw|✗|✗|✗|✗|✗
## 特性
### 1. 丰富的注入点支持
* `@AtEnter` 函数入口
* `@AtExit` 函数退出
* `@AtExceptionExit` 函数抛出异常
* `@AtFieldAccess` 访问field
* `@AtInvoke` 在method里的子函数调用
* `@AtInvokeException` 在method里的子函数调用抛出异常
* `@AtLine` 在指定行号
* `@AtSyncEnter` 进入同步块,比如`synchronized`
* `@AtSyncExit` 退出同步块
* `@AtThrow` 代码里显式`throw`异常点
### 2. 动态的Binding
* `@Binding.This` this对象
* `@@Binding.Class` Class对象
* `@Binding.Method` 函数调用的 Method 对象
* `@Binding.MethodName` 函数的名字
* `@Binding.MethodDesc` 函数的desc
* `@Binding.Return` 函数调用的返回值
* `@Binding.Throwable` 函数里抛出的异常
* `@Binding.Args` 函数调用的入参
* `@Binding.ArgNames` 函数调用的入参的名字
* `@Binding.LocalVars` 局部变量
* `@Binding.LocalVarNames` 局部变量的名字
* `@Binding.Field` field对象属性字段
* `@Binding.InvokeArgs` method里的子函数调用的入参
* `@Binding.InvokeReturn` method里的子函数调用的返回值
* `@Binding.InvokeMethodName` method里的子函数调用的名字
* `@Binding.InvokeMethodOwner` method里的子函数调用的类名
* `@Binding.InvokeMethodDeclaration` method里的子函数调用的desc
* `@Binding.Line` 行号
* `@Binding.Monitor` 同步块里监控的对象
### 3. 可编程的异常处理
* `@ExceptionHandler` 在插入的增强代码,可以用`try/catch`块包围起来
### 4. inline支持
增强的代码 和 异常处理代码都可以通过 inline技术内联到原来的类里,达到最理想的增强效果。
### 5. invokeOrigin 技术
通常,我们要增强一个类,就想要办法在函数前后插入一个static的回调函数,但这样子局限太大。那么有没有更灵活的方式呢?
比如有一个 hello() 函数:
```java
public String hello(String str) {
return "hello " + str;
}
```
我们想对它做增强,那么可以编写下面的代码:
```java
public String hello(String str) {
System.out.println("before");
Object value = InstrumentApi.invokeOrigin();
System.out.println("after, result: " + value);
return object;
}
```
增强后的结果是:
```java
public String hello(String str) {
System.out.println("before");
Object value = "hello " + str;
System.out.println("after, result: " + value);
return object;
}
```
**这种方式可以随意插入代码,非常灵活。**
## 示例
`ByteKitDemo.java`为例说明,[[查看源文件](src/test/java/com/example/ByteKitDemo.java)]。
### 1. 定义注入点和Binding数据
* 在下面的 SampleInterceptor 时定义了要注入 `@AtEnter`/`@AtExit`/`@AtExceptionExit`三个地方,
*`@Binding`绑定了不同的数据
*`@AtEnter`里配置了 `inline = true`,则说明插入的`SampleInterceptor#atEnter`函数本身会被inline掉
* 配置了 `suppress = RuntimeException.class``suppressHandler = PrintExceptionSuppressHandler.class`,说明插入的代码会被 `try/catch` 包围
```java
public static class SampleInterceptor {
@AtEnter(inline = true, suppress = RuntimeException.class, suppressHandler = PrintExceptionSuppressHandler.class)
public static void atEnter(@Binding.This Object object,
@Binding.Class Object clazz,
@Binding.Args Object[] args,
@Binding.MethodName String methodName,
@Binding.MethodDesc String methodDesc) {
System.out.println("atEnter, args[0]: " + args[0]);
}
@AtExit(inline = true)
public static void atExit(@Binding.Return Object returnObject) {
System.out.println("atExit, returnObject: " + returnObject);
}
@AtExceptionExit(inline = true, onException = RuntimeException.class)
public static void atExceptionExit(@Binding.Throwable RuntimeException ex,
@Binding.Field(name = "exceptionCount") int exceptionCount) {
System.out.println("atExceptionExit, ex: " + ex.getMessage() + ", field exceptionCount: " + exceptionCount);
}
}
```
### 2. @ExceptionHandler
在上面的 `@AtEnter`配置里,生成的代码会被 try/catch 包围,那么具体的内容是在`PrintExceptionSuppressHandler`
```java
public static class PrintExceptionSuppressHandler {
@ExceptionHandler(inline = true)
public static void onSuppress(@Binding.Throwable Throwable e, @Binding.Class Object clazz) {
System.out.println("exception handler: " + clazz);
e.printStackTrace();
}
}
```
### 3. 查看反编译结果
原始的Sample类是:
```java
public static class Sample {
private int exceptionCount = 0;
public String hello(String str, boolean exception) {
if (exception) {
exceptionCount++;
throw new RuntimeException("test exception, str: " + str);
}
return "hello " + str;
}
}
```
增强后的字节码,再反编译:
```java
package com.example;
public static class ByteKitDemo.Sample {
private int exceptionCount = 0;
public String hello(String string, boolean bl) {
try {
String string2 = "(Ljava/lang/String;Z)Ljava/lang/String;";
String string3 = "hello";
Object[] arrobject = new Object[]{string, new Boolean(bl)};
Class<ByteKitDemo.Sample> class_ = ByteKitDemo.Sample.class;
ByteKitDemo.Sample sample = this;
System.out.println("atEnter, args[0]: " + arrobject[0]);
}
catch (RuntimeException runtimeException) {
Class<ByteKitDemo.Sample> class_ = ByteKitDemo.Sample.class;
RuntimeException runtimeException2 = runtimeException;
System.out.println("exception handler: " + class_);
runtimeException2.printStackTrace();
}
try {
String string4;
void str;
void exception;
if (exception != false) {
++this.exceptionCount;
throw new RuntimeException("test exception, str: " + (String)str);
}
String string5 = string4 = "hello " + (String)str;
System.out.println("atExit, returnObject: " + string5);
return string4;
}
catch (RuntimeException runtimeException) {
int n = this.exceptionCount;
RuntimeException runtimeException3 = runtimeException;
System.out.println("atExceptionExit, ex: " + runtimeException3.getMessage() + ", field exceptionCount: " + n);
throw runtimeException;
}
}
}
```
-92
View File
@@ -1,92 +0,0 @@
<?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>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>arthas-bytekit</artifactId>
<name>arthas-bytekit</name>
<dependencies>
<dependency>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-common</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.alibaba.arthas</groupId>
<artifactId>arthas-repackage-asm</artifactId>
</dependency>
<dependency>
<groupId>org.benf</groupId>
<artifactId>cfr</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-agent</artifactId>
<scope>provided</scope>
<optional>true</optional>
</dependency>
<!-- Test Artifacts -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<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>
<dependency>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-demo</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</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>
@@ -1,22 +0,0 @@
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;
}
@@ -1,56 +0,0 @@
package com.taobao.arthas.bytekit.asm;
import com.alibaba.arthas.deps.org.objectweb.asm.ClassReader;
import com.alibaba.arthas.deps.org.objectweb.asm.ClassWriter;
/**
* @author vlinux
* @author hengyunabc 2020-05-29
*
*/
public class ClassLoaderAwareClassWriter extends ClassWriter {
private ClassLoader classLoader;
public ClassLoaderAwareClassWriter(int flags, ClassLoader loader) {
this(null, flags, loader);
}
public ClassLoaderAwareClassWriter(ClassReader classReader, int flags, ClassLoader loader) {
super(classReader, flags);
this.classLoader = loader;
}
/*
* 注意,为了自动计算帧的大小,有时必须计算两个类共同的父类。
* 缺省情况下,ClassWriter将会在getCommonSuperClass方法中计算这些,通过在加载这两个类进入虚拟机时,使用反射API来计算。
* 但是,如果你将要生成的几个类相互之间引用,这将会带来问题,因为引用的类可能还不存在。
* 在这种情况下,你可以重写getCommonSuperClass方法来解决这个问题。
*
* 通过重写 getCommonSuperClass() 方法,更正获取ClassLoader的方式,改成使用指定ClassLoader的方式进行。
* 规避了原有代码采用Object.class.getClassLoader()的方式
*/
@Override
protected String getCommonSuperClass(String type1, String type2) {
Class<?> c, d;
try {
c = Class.forName(type1.replace('/', '.'), false, classLoader);
d = Class.forName(type2.replace('/', '.'), false, classLoader);
} catch (Exception e) {
throw new RuntimeException(e);
}
if (c.isAssignableFrom(d)) {
return type1;
}
if (d.isAssignableFrom(c)) {
return type2;
}
if (c.isInterface() || d.isInterface()) {
return "java/lang/Object";
} else {
do {
c = c.getSuperclass();
} while (!c.isAssignableFrom(d));
return c.getName().replace('.', '/');
}
}
}
@@ -1,86 +0,0 @@
package com.taobao.arthas.bytekit.asm;
import com.alibaba.arthas.deps.org.objectweb.asm.Label;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.commons.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.ASM9, 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);
}
}
@@ -1,97 +0,0 @@
package com.taobao.arthas.bytekit.asm;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.Label;
import com.alibaba.arthas.deps.org.objectweb.asm.MethodVisitor;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.commons.GeneratorAdapter;
import com.alibaba.arthas.deps.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.ASM9, 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
}
}
@@ -1,48 +0,0 @@
package com.taobao.arthas.bytekit.asm;
/**
*
* @author hengyunabc 2019-03-18
*
*/
public class MethodInfo {
private String owner;
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;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
}
@@ -1,784 +0,0 @@
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 com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.commons.Method;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.ClassNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.FrameNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.IincInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.IntInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.JumpInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LabelNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LdcInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LocalVariableNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.TryCatchBlockNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.TypeInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.VarInsnNode;
import com.taobao.arthas.bytekit.asm.location.filter.DefaultLocationFilter;
import com.taobao.arthas.bytekit.asm.location.filter.LocationFilter;
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;
private LocationFilter locationFilter = new DefaultLocationFilter();
public MethodProcessor(final ClassNode classNode, final MethodNode methodNode) {
this(classNode, methodNode, false);
}
public MethodProcessor(final ClassNode classNode, final MethodNode methodNode, LocationFilter locationFilter) {
this(classNode, methodNode, false);
this.locationFilter = locationFilter;
}
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;
}
public LocationFilter getLocationFilter() {
return locationFilter;
}
/**
* 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;
}
//修改inline代码中的使用到局部变量的指令的var操作数(变量slot)
if(abstractInsnNode instanceof VarInsnNode) {
VarInsnNode varInsnNode = (VarInsnNode) abstractInsnNode;
varInsnNode.var += currentMaxLocals;
} else if (abstractInsnNode instanceof IincInsnNode) {
IincInsnNode iincInsnNode = (IincInsnNode) abstractInsnNode;
iincInsnNode.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);
}
}
}
@@ -1,64 +0,0 @@
package com.taobao.arthas.bytekit.asm;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LabelNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
import com.alibaba.arthas.deps.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);
}
}
}
@@ -1,66 +0,0 @@
package com.taobao.arthas.bytekit.asm;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LabelNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
import com.alibaba.arthas.deps.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);
}
}
}
@@ -1,431 +0,0 @@
/*
* 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 "";
}
}
}
@@ -1,33 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.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;
}
}
@@ -1,20 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.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 AsmOpUtils.OBJECT_ARRAY_TYPE;
}
}
@@ -1,50 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.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;
}
}
@@ -1,424 +0,0 @@
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 com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.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 static @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 static @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 static @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 static @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 static @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 static @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 static @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 static @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 = InvokeMethodNameBindingParser.class)
public static @interface InvokeMethodName {
boolean optional() default false;
}
public static class InvokeMethodNameBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new InvokeMethodNameBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = InvokeMethodOwnerBindingParser.class)
public static @interface InvokeMethodOwner {
boolean optional() default false;
}
public static class InvokeMethodOwnerBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new InvokeMethodOwnerBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = InvokeMethodDeclarationBindingParser.class)
public static @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 = InvokeInfoBindingParser.class)
public static @interface InvokeInfo {
boolean optional() default false;
}
public static class InvokeInfoBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new InvokeInfoBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = MethodBindingParser.class)
public static @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 = MethodNameBindingParser.class)
public static @interface MethodName {
boolean optional() default false;
}
public static class MethodNameBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new MethodNameBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = MethodDescBindingParser.class)
public static @interface MethodDesc {
boolean optional() default false;
}
public static class MethodDescBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new MethodDeclarationBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = MethodInfoBindingParser.class)
public static @interface MethodInfo {
boolean optional() default false;
}
public static class MethodInfoBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new MethodInfoBinding();
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = ReturnBindingParser.class)
public static @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 static @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 static @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 static @interface Line {
boolean optional() default false;
/**
* 是否精确是在某个 LineNumberNode 上。如果为true的话,会向上找到最接近的 LineNumberNode
*
* @return
*/
boolean exact() default false;
}
public static class LineBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
Line line = (Line) annotation;
return new LineBinding(line.exact());
}
}
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = MonitorBindingParser.class)
public static @interface Monitor {
boolean optional() default false;
}
public static class MonitorBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new MonitorBinding();
}
}
}
@@ -1,41 +0,0 @@
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;
}
}
@@ -1,21 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.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);
}
}
@@ -1,95 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.ClassNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.FieldNode;
import com.alibaba.arthas.deps.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;
}
}
@@ -1,36 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.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;
}
}
@@ -1,47 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.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 AsmOpUtils.OBJECT_ARRAY_TYPE;
}
}
@@ -1,64 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LineNumberNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.taobao.arthas.bytekit.asm.location.MethodInsnNodeWare;
import com.taobao.arthas.bytekit.asm.location.Location;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
/**
* 包含 owner/method name/ method desc/ line number
*
* @author hengyunabc 2020-05-14
*
*/
public class InvokeInfoBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
Location location = bindingContext.getLocation();
if (location instanceof MethodInsnNodeWare) {
MethodInsnNodeWare methodInsnNodeWare = (MethodInsnNodeWare) location;
MethodInsnNode methodInsnNode = methodInsnNodeWare.methodInsnNode();
int line = -1;
if (location.isWhenComplete() == false) {
AbstractInsnNode insnNode = methodInsnNode.getPrevious();
while (insnNode != null) {
if (insnNode instanceof LineNumberNode) {
line = ((LineNumberNode) insnNode).line;
break;
}
insnNode = insnNode.getPrevious();
}
} else {
AbstractInsnNode insnNode = methodInsnNode.getNext();
while (insnNode != null) {
if (insnNode instanceof LineNumberNode) {
line = ((LineNumberNode) insnNode).line;
break;
}
insnNode = insnNode.getNext();
}
}
String result = methodInsnNode.owner + "|" + methodInsnNode.name + "|" + methodInsnNode.desc + "|" + line;
AsmOpUtils.push(instructions, result);
} else {
throw new IllegalArgumentException(
"InvokeMethodNameBinding location is not Invocation location, location: " + location);
}
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(String.class);
}
}
@@ -1,37 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.taobao.arthas.bytekit.asm.location.MethodInsnNodeWare;
import com.taobao.arthas.bytekit.asm.location.Location;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
/**
*
* @author hengyunabc
*
*/
public class InvokeMethodDeclarationBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
Location location = bindingContext.getLocation();
if (location instanceof MethodInsnNodeWare) {
MethodInsnNodeWare methodInsnNodeWare = (MethodInsnNodeWare) location;
MethodInsnNode methodInsnNode = methodInsnNodeWare.methodInsnNode();
AsmOpUtils.push(instructions, methodInsnNode.desc);
} else {
throw new IllegalArgumentException(
"InvokeMethodDeclarationBinding location is not Invocation location, location: " + location);
}
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(String.class);
}
}
@@ -1,37 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.taobao.arthas.bytekit.asm.location.MethodInsnNodeWare;
import com.taobao.arthas.bytekit.asm.location.Location;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
/**
*
* @author hengyunabc
*
*/
public class InvokeMethodNameBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
Location location = bindingContext.getLocation();
if (location instanceof MethodInsnNodeWare) {
MethodInsnNodeWare methodInsnNodeWare = (MethodInsnNodeWare) location;
MethodInsnNode methodInsnNode = methodInsnNodeWare.methodInsnNode();
AsmOpUtils.push(instructions, methodInsnNode.name);
} else {
throw new IllegalArgumentException(
"InvokeMethodNameBinding location is not Invocation location, location: " + location);
}
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(String.class);
}
}
@@ -1,37 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.taobao.arthas.bytekit.asm.location.MethodInsnNodeWare;
import com.taobao.arthas.bytekit.asm.location.Location;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
/**
*
* @author hengyunabc 2020-05-02
*
*/
public class InvokeMethodOwnerBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
Location location = bindingContext.getLocation();
if (location instanceof MethodInsnNodeWare) {
MethodInsnNodeWare methodInsnNodeWare = (MethodInsnNodeWare) location;
MethodInsnNode methodInsnNode = methodInsnNodeWare.methodInsnNode();
AsmOpUtils.push(instructions, methodInsnNode.owner);
} else {
throw new IllegalArgumentException(
"InvokeMethodOwnerBinding location is not Invocation location, location: " + location);
}
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(String.class);
}
}
@@ -1,62 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LocalVariableNode;
import com.alibaba.arthas.deps.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);
}
}
}
@@ -1,63 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LineNumberNode;
import com.taobao.arthas.bytekit.asm.location.Location;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
/**
*
* @author hengyunabc
*
*/
public class LineBinding extends Binding {
private boolean exact;
public LineBinding(boolean exact) {
this.exact = exact;
}
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
Location location = bindingContext.getLocation();
AbstractInsnNode insnNode = location.getInsnNode();
int line = -1;
if (exact) {
if (insnNode instanceof LineNumberNode) {
line = ((LineNumberNode) insnNode).line;
} else {
throw new IllegalArgumentException("LineBinding location is not LineNumberNode, insnNode: " + insnNode);
}
} else {
if (location.isWhenComplete() == false) {
while (insnNode != null) {
if (insnNode instanceof LineNumberNode) {
line = ((LineNumberNode) insnNode).line;
break;
}
insnNode = insnNode.getPrevious();
}
} else {
while (insnNode != null) {
if (insnNode instanceof LineNumberNode) {
line = ((LineNumberNode) insnNode).line;
break;
}
insnNode = insnNode.getNext();
}
}
}
AsmOpUtils.push(instructions, line);
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(int.class);
}
}
@@ -1,38 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.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;
}
}
@@ -1,49 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.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_ARRAY_TYPE;
}
}
@@ -1,52 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.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);
}
}
@@ -1,30 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
/**
* TODO 提供一个完整的 method 的string,包含类名,并不是desc?用户可以自己提取descs method的定义,前面是 public
* /static 这些关键字,是有限的几个。后面是 throws ,的异常信息。 或者做一下取巧比如把 classname | methoname | desc 之类连起一个String
*
* @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()));
AsmOpUtils.ldc(instructions, methodProcessor.getMethodNode().desc);
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(String.class);
}
}
@@ -1,30 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
/**
* method name | method desc 的方式组织
*
* TODO 是否要有 line number ?
*
* @author hengyunabc 2020-05-16
*
*/
public class MethodInfoBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
MethodProcessor methodProcessor = bindingContext.getMethodProcessor();
MethodNode methodNode = methodProcessor.getMethodNode();
AsmOpUtils.ldc(instructions, methodNode.name + '|' + methodNode.desc);
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(String.class);
}
}
@@ -1,25 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
/**
* @author hengyunabc
*
*/
public class MethodNameBinding extends Binding {
@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
MethodProcessor methodProcessor = bindingContext.getMethodProcessor();
AsmOpUtils.ldc(instructions, methodProcessor.getMethodNode().name);
}
@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(String.class);
}
}
@@ -1,41 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.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;
}
}
@@ -1,43 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.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.loadVar(instructions, returnType, returnVariableNode.index);
}
}
@Override
public boolean fromStack() {
return true;
}
@Override
public Type getType(BindingContext bindingContext) {
return bindingContext.getMethodProcessor().getReturnType();
}
}
@@ -1,23 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.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);
}
@@ -1,18 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.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);
}
}
@@ -1,30 +0,0 @@
package com.taobao.arthas.bytekit.asm.binding;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.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);
}
}
@@ -1,11 +0,0 @@
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);
}
@@ -1,15 +0,0 @@
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();
}
@@ -1,35 +0,0 @@
package com.taobao.arthas.bytekit.asm.inst;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 在这里支持配置一个 error hander ?做为插入的异常处理的
*
* 按名字匹配,按模糊匹配??有没有这样子的需求?,按interface匹配,按基础类继承的匹配
*
* 函数的匹配,直接是名字一样,desc 一样的。 匹配有 annotation 的
*
* 只有 NewField 才是加新的field,原来类里有的field,就直接写上就可以了。
* @author hengyunabc
*
*/
@Target({ java.lang.annotation.ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Instrument {
// Instrumentation
// MatchType type() default MatchType.ExactClass;
String[] Class() default {};
String[] BaseClass() default {};
String[] Interface() default {};
String originalName() default "";
Class<? extends Throwable> suppress() default Throwable.class;
Class<?> suppressHandler() default Void.class;
}
@@ -1,32 +0,0 @@
package com.taobao.arthas.bytekit.asm.inst;
/**
*
* <pre>
* 实现这个 invokeOrigin(),需要多步处理:
*
* 传入要被替换的类,读取到标记了 @Instrument 的类。 类名不一样的话,先替换类名?
*
* 然后查找所有的 field,如果有标记了 @NewField ,则增加到要被替换的类里。
*
* 然后查找所有的函数, 再查找是否在 旧类里有同样签名的,如果有,则执行清除行号, 替换 invokeOrigin() ,再 inline 原来的旧函数
*
* 再替换函数到 旧类里。
*
* 类名有可能要替换
*
* </pre>
*
*
*
*
*
* @author hengyunabc 2019-02-25
*
*/
public class InstrumentApi {
public static final <T> T invokeOrigin() {
return null;
}
}
@@ -1,10 +0,0 @@
package com.taobao.arthas.bytekit.asm.inst;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ java.lang.annotation.ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface NewField {
}
@@ -1,125 +0,0 @@
package com.taobao.arthas.bytekit.asm.inst.impl;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.commons.Method;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.TypeInsnNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
import com.taobao.arthas.bytekit.utils.AsmUtils;
/**
*
* @author hengyunabc 2019-03-15
*
*/
public class InstrumentImpl {
public static MethodNode replaceInvokeOrigin(String originOwner, MethodNode originMethodNode,
MethodNode apmMethodNode) {
// 查找到所有的 InstrumentApi.invokeOrigin() 指令
List<MethodInsnNode> methodInsnNodes = AsmUtils.findMethodInsnNode(apmMethodNode,
"com/taobao/arthas/bytekit/asm/inst/InstrumentApi", "invokeOrigin", "()Ljava/lang/Object;");
Type originReturnType = Type.getMethodType(originMethodNode.desc).getReturnType();
for (MethodInsnNode methodInsnNode : methodInsnNodes) {
InsnList instructions = new InsnList();
AbstractInsnNode secondInsnNode = methodInsnNode.getNext();
// 如果是 非 static ,则要 load this
boolean isStatic = AsmUtils.isStatic(originMethodNode);
int opcode = isStatic ? Opcodes.INVOKESTATIC : Opcodes.INVOKEVIRTUAL;
if (!isStatic) {
AsmOpUtils.loadThis(instructions);
}
AsmOpUtils.loadArgs(instructions, originMethodNode);
MethodInsnNode originMethodInsnNode = new MethodInsnNode(opcode, originOwner, originMethodNode.name,
originMethodNode.desc, false);
// 调用原来的函数
instructions.add(originMethodInsnNode);
int sort = originReturnType.getSort();
if (sort == Type.VOID) {
if (secondInsnNode != null) {
if (secondInsnNode.getOpcode() == Opcodes.POP) {
// TODO 原来的函数没有返回值,这里要把 POP去掉。有没有可能是 POP2 ?
apmMethodNode.instructions.remove(secondInsnNode);
} else {
// TODO 原来函数没有返回值,这里有没有可能要赋值??是否要 push null?
AsmOpUtils.pushNUll(instructions);
}
}
} else if (sort >= Type.BOOLEAN && sort <= Type.DOUBLE) {
if (secondInsnNode.getOpcode() == Opcodes.POP) {
// 原来是 pop掉一个栈,如果函数返回的是 long,则要pop2
if (originReturnType.getSize() == 2) {
apmMethodNode.instructions.insert(secondInsnNode, new InsnNode(Opcodes.POP2));
apmMethodNode.instructions.remove(secondInsnNode);
}
} else {
/**
* 需要把下面两条cast和unbox的指令删掉
*
* <pre>
* CHECKCAST java/lang/Integer
* INVOKEVIRTUAL java/lang/Integer.intValue ()I
* </pre>
*/
boolean removeCheckCast = false;
if (secondInsnNode.getOpcode() == Opcodes.CHECKCAST) {
TypeInsnNode typeInsnNode = (TypeInsnNode) secondInsnNode;
// 从原始函数的返回值,获取到它对应的自动box的类
Type boxedType = AsmOpUtils.getBoxedType(originReturnType);
if (Type.getObjectType(typeInsnNode.desc).equals(boxedType)) {
AbstractInsnNode thridInsnNode = secondInsnNode.getNext();
if (thridInsnNode != null && thridInsnNode.getOpcode() == Opcodes.INVOKEVIRTUAL) {
MethodInsnNode valueInsnNode = (MethodInsnNode) thridInsnNode;
Method unBoxMethod = AsmOpUtils.getUnBoxMethod(originReturnType);
if (unBoxMethod.getDescriptor().equals(valueInsnNode.desc)
&& valueInsnNode.owner.equals(boxedType.getInternalName())) {
apmMethodNode.instructions.remove(thridInsnNode);
apmMethodNode.instructions.remove(secondInsnNode);
removeCheckCast = true;
}
}
}
}
if (!removeCheckCast) {
// 没有被转换为原始类型,也没有pop,则说明赋值给了一个对象,用类似Long.valudOf转换为Object
AsmOpUtils.box(instructions, originReturnType);
}
}
} else {// ARRAY/OBJECT
// 移掉可能有的 check cast
if (secondInsnNode.getOpcode() == Opcodes.CHECKCAST) {
TypeInsnNode typeInsnNode = (TypeInsnNode) secondInsnNode;
if (Type.getObjectType(typeInsnNode.desc).equals(originReturnType)) {
apmMethodNode.instructions.remove(secondInsnNode);
}
}
}
apmMethodNode.instructions.insertBefore(methodInsnNode, instructions);
apmMethodNode.instructions.remove(methodInsnNode);
}
MethodProcessor methodProcessor = new MethodProcessor(originOwner, apmMethodNode);
methodProcessor.inline(originOwner, originMethodNode);
return apmMethodNode;
}
}
@@ -1,52 +0,0 @@
package com.taobao.arthas.bytekit.asm.inst.impl;
import com.alibaba.arthas.deps.org.objectweb.asm.Label;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
/**
*
* @author hengyunabc 2019-03-18
*
*/
public class MethodReplaceResult {
private boolean success;
private Label start;
private Label end;
private MethodNode methodNode;
public Label getStart() {
return start;
}
public void setStart(Label start) {
this.start = start;
}
public Label getEnd() {
return end;
}
public void setEnd(Label end) {
this.end = end;
}
public MethodNode getMethodNode() {
return methodNode;
}
public void setMethodNode(MethodNode methodNode) {
this.methodNode = methodNode;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
}
@@ -1,5 +0,0 @@
package com.taobao.arthas.bytekit.asm.interceptor;
public class EnterInteceptor {
}
@@ -1,5 +0,0 @@
package com.taobao.arthas.bytekit.asm.interceptor;
public class ExceptionInterceptor {
}
@@ -1,5 +0,0 @@
package com.taobao.arthas.bytekit.asm.interceptor;
public class ExitInterceptor {
}
@@ -1,5 +0,0 @@
package com.taobao.arthas.bytekit.asm.interceptor;
public interface Inteceptor {
}
@@ -1,71 +0,0 @@
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;
}
}
@@ -1,255 +0,0 @@
package com.taobao.arthas.bytekit.asm.interceptor;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.JumpInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LabelNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.alibaba.arthas.deps.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;
/**
* 加载inlne类所需要的ClassLoader
*/
private ClassLoader classLoader;
public InterceptorProcessor(ClassLoader classLoader) {
this.classLoader = classLoader;
}
public List<Location> 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());
Class<?> forName = classLoader.loadClass(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());
Class<?> forName = classLoader.loadClass(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()));
}
return locations;
}
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;
}
}
@@ -1,45 +0,0 @@
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 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) {
LocationMatcher locationMatcher = new EnterLocationMatcher();
AtEnter atEnter = (AtEnter) annotationOnMethod;
return InterceptorParserUtils.createInterceptorProcessor(method,
locationMatcher,
atEnter.inline(),
atEnter.suppress(),
atEnter.suppressHandler());
}
}
}
@@ -1,49 +0,0 @@
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 com.alibaba.arthas.deps.org.objectweb.asm.Type;
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) {
AtExceptionExit atExceptionExit = (AtExceptionExit) annotationOnMethod;
LocationMatcher locationMatcher = new ExceptionExitLocationMatcher(Type.getInternalName(atExceptionExit.onException()));;
return InterceptorParserUtils.createInterceptorProcessor(method,
locationMatcher,
atExceptionExit.inline(),
atExceptionExit.suppress(),
atExceptionExit.suppressHandler());
}
}
}
@@ -1,43 +0,0 @@
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 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) {
AtExit atExit = (AtExit) annotationOnMethod;
LocationMatcher locationMatcher = new ExitLocationMatcher();
return InterceptorParserUtils.createInterceptorProcessor(method,
locationMatcher,
atExit.inline(),
atExit.suppress(),
atExit.suppressHandler());
}
}
}
@@ -1,72 +0,0 @@
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 com.alibaba.arthas.deps.org.objectweb.asm.Type;
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) {
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());
return InterceptorParserUtils.createInterceptorProcessor(method,
locationMatcher,
atFieldAccess.inline(),
atFieldAccess.suppress(),
atFieldAccess.suppressHandler());
}
}
}
@@ -1,79 +0,0 @@
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 com.alibaba.arthas.deps.org.objectweb.asm.Type;
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;
/**
* method name excludes
* @return
*/
String[] excludes() default {};
class InvokeInterceptorProcessorParser implements InterceptorProcessorParser {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
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);
return InterceptorParserUtils.createInterceptorProcessor(method,
locationMatcher,
atInvoke.inline(),
atInvoke.suppress(),
atInvoke.suppressHandler());
}
}
}
@@ -1,82 +0,0 @@
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 com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.AtInvokeException.InvokeExceptionInterceptorProcessorParser;
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;
/**
*
* @author hengyunabc 2020-05-03
*
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.METHOD)
@InterceptorParserHander(parserHander = InvokeExceptionInterceptorProcessorParser.class)
public @interface AtInvokeException {
boolean inline() default true;
Class<? extends Throwable> suppress() default None.class;
Class<?> suppressHandler() default Void.class;
Class<?> owner() default Void.class;
/**
* method name
*
* @return
*/
String name();
String desc() default "";
int count() default -1;
String[] excludes() default {};
class InvokeExceptionInterceptorProcessorParser implements InterceptorProcessorParser {
@Override
public InterceptorProcessor parse(Method method, Annotation annotationOnMethod) {
AtInvokeException atInvokeException = (AtInvokeException) annotationOnMethod;
String owner = null;
String desc = null;
if (!atInvokeException.owner().equals(Void.class)) {
owner = Type.getType(atInvokeException.owner()).getInternalName();
}
if (atInvokeException.desc().isEmpty()) {
desc = null;
}
List<String> excludes = new ArrayList<String>();
for (String exclude : atInvokeException.excludes()) {
excludes.add(exclude);
}
LocationMatcher locationMatcher = new InvokeLocationMatcher(owner, atInvokeException.name(), desc,
atInvokeException.count(), true, excludes, true);
return InterceptorParserUtils.createInterceptorProcessor(method,
locationMatcher,
atInvokeException.inline(),
atInvokeException.suppress(),
atInvokeException.suppressHandler());
}
}
}
@@ -1,46 +0,0 @@
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 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) {
AtLine atLine = (AtLine) annotationOnMethod;
LocationMatcher locationMatcher = new LineLocationMatcher(atLine.lines());
return InterceptorParserUtils.createInterceptorProcessor(method,
locationMatcher,
atLine.inline(),
atLine.suppress(),
atLine.suppressHandler());
}
}
}
@@ -1,49 +0,0 @@
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 com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
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) {
AtSyncEnter atSyncEnter = (AtSyncEnter) annotationOnMethod;
LocationMatcher locationMatcher = new SyncLocationMatcher(Opcodes.MONITORENTER, atSyncEnter.count(), atSyncEnter.whenComplete());
return InterceptorParserUtils.createInterceptorProcessor(method,
locationMatcher,
atSyncEnter.inline(),
atSyncEnter.suppress(),
atSyncEnter.suppressHandler());
}
}
}
@@ -1,49 +0,0 @@
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 com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
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) {
AtSyncExit atSyncExit = (AtSyncExit) annotationOnMethod;
LocationMatcher locationMatcher = new SyncLocationMatcher(Opcodes.MONITOREXIT, atSyncExit.count(), atSyncExit.whenComplete());
return InterceptorParserUtils.createInterceptorProcessor(method,
locationMatcher,
atSyncExit.inline(),
atSyncExit.suppress(),
atSyncExit.suppressHandler());
}
}
}
@@ -1,46 +0,0 @@
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 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) {
AtThrow atThrow = (AtThrow) annotationOnMethod;
LocationMatcher locationMatcher = new ThrowLocationMatcher(atThrow.count());
return InterceptorParserUtils.createInterceptorProcessor(method,
locationMatcher,
atThrow.inline(),
atThrow.suppress(),
atThrow.suppressHandler());
}
}
}
@@ -1,36 +0,0 @@
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;
}
}
@@ -1,10 +0,0 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
public class EmptySuppressHandler {
@ExceptionHandler
public static void onSuppress() {
}
}
@@ -1,13 +0,0 @@
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;
}
@@ -1,85 +0,0 @@
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 com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.taobao.arthas.bytekit.asm.binding.Binding;
import com.taobao.arthas.bytekit.asm.binding.ThrowableBinding;
import com.taobao.arthas.bytekit.asm.interceptor.InterceptorMethodConfig;
import com.taobao.arthas.bytekit.utils.AnnotationUtils;
import com.taobao.arthas.bytekit.utils.ReflectionUtils;
import com.taobao.arthas.bytekit.utils.ReflectionUtils.MethodCallback;
import com.taobao.arthas.bytekit.utils.ReflectionUtils.MethodFilter;
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;
}
}
@@ -1,17 +0,0 @@
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();
}
@@ -1,49 +0,0 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
import com.alibaba.arthas.deps.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.location.LocationMatcher;
import java.lang.reflect.Method;
import java.util.List;
public class InterceptorParserUtils {
public static InterceptorProcessor createInterceptorProcessor(
Method method,
LocationMatcher locationMatcher,
boolean inline,
Class<? extends Throwable> suppress,
Class<?> suppressHandler) {
InterceptorProcessor interceptorProcessor = new InterceptorProcessor(method.getDeclaringClass().getClassLoader());
//locationMatcher
interceptorProcessor.setLocationMatcher(locationMatcher);
//interceptorMethodConfig
InterceptorMethodConfig interceptorMethodConfig = new InterceptorMethodConfig();
interceptorProcessor.setInterceptorMethodConfig(interceptorMethodConfig);
interceptorMethodConfig.setOwner(Type.getInternalName(method.getDeclaringClass()));
interceptorMethodConfig.setMethodName(method.getName());
interceptorMethodConfig.setMethodDesc(Type.getMethodDescriptor(method));
//inline
interceptorMethodConfig.setInline(inline);
//bindings
List<Binding> bindings = BindingParserUtils.parseBindings(method);
interceptorMethodConfig.setBindings(bindings);
//errorHandlerMethodConfig
InterceptorMethodConfig errorHandlerMethodConfig = ExceptionHandlerUtils
.errorHandlerMethodConfig(suppress, suppressHandler);
if (errorHandlerMethodConfig != null) {
interceptorProcessor.setExceptionHandlerConfig(errorHandlerMethodConfig);
}
return interceptorProcessor;
}
}
@@ -1,13 +0,0 @@
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
/**
* 用于声明没有异常
* @author hengyunabc
*
*/
public class None extends Throwable {
private static final long serialVersionUID = 1L;
private None() {
}
}
@@ -1,11 +0,0 @@
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();
}
}
@@ -1,50 +0,0 @@
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 com.taobao.arthas.bytekit.asm.interceptor.InterceptorProcessor;
import com.taobao.arthas.bytekit.asm.interceptor.annotation.InterceptorParserHander;
import com.taobao.arthas.bytekit.utils.InstanceUtils;
import com.taobao.arthas.bytekit.utils.ReflectionUtils;
import com.taobao.arthas.bytekit.utils.ReflectionUtils.MethodCallback;
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;
}
}
@@ -1,10 +0,0 @@
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);
}
@@ -1,11 +0,0 @@
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);
}
@@ -1,24 +0,0 @@
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;
}
}
@@ -1,26 +0,0 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.asm.location.Location.EnterLocation;
import com.taobao.arthas.bytekit.asm.location.filter.LocationFilter;
public class EnterLocationMatcher implements LocationMatcher {
@Override
public List<Location> match(MethodProcessor methodProcessor) {
List<Location> locations = new ArrayList<Location>();
AbstractInsnNode enterInsnNode = methodProcessor.getEnterInsnNode();
LocationFilter locationFilter = methodProcessor.getLocationFilter();
if (locationFilter.allow(enterInsnNode, LocationType.ENTER, true)) {
EnterLocation enterLocation = new EnterLocation(enterInsnNode);
locations.add(enterLocation);
}
return locations;
}
}
@@ -1,39 +0,0 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LabelNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.asm.TryCatchBlock;
import com.taobao.arthas.bytekit.asm.location.Location.ExceptionExitLocation;
import com.taobao.arthas.bytekit.asm.location.filter.LocationFilter;
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);
LabelNode endLabelNode = tryCatchBlock.getEndLabelNode();
LocationFilter locationFilter = methodProcessor.getLocationFilter();
if (locationFilter.allow(endLabelNode, LocationType.EXCEPTION_EXIT, false)) {
locations.add(new ExceptionExitLocation(tryCatchBlock.getEndLabelNode()));
}
return locations;
}
}
@@ -1,50 +0,0 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnNode;
import com.taobao.arthas.bytekit.asm.MethodProcessor;
import com.taobao.arthas.bytekit.asm.location.Location.ExitLocation;
import com.taobao.arthas.bytekit.asm.location.filter.LocationFilter;
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)) {
LocationFilter locationFilter = methodProcessor.getLocationFilter();
if (locationFilter.allow(node, LocationType.EXIT, false)) {
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;
}
}
@@ -1,96 +0,0 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.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;
}
}
@@ -1,243 +0,0 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.JumpInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LabelNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.alibaba.arthas.deps.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.location.Location.InvokeExceptionExitLocation;
import com.taobao.arthas.bytekit.asm.location.Location.InvokeLocation;
import com.taobao.arthas.bytekit.asm.location.filter.LocationFilter;
import com.taobao.arthas.bytekit.utils.AsmOpUtils;
import com.taobao.arthas.bytekit.utils.MatchUtils;
/**
*
* @author hengyunabc
*
*/
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 the invoke class, such as java.* to exclude jdk
* invoke.
*/
private List<String> excludes = new ArrayList<String>();
private boolean atInvokeExcpetionExit = false;
public InvokeLocationMatcher(String owner, String methodName, String desc, int count, boolean whenComplete,
List<String> excludes, boolean atInvokeExcpetionExit) {
super();
this.owner = owner;
this.methodName = methodName;
this.desc = desc;
this.count = count;
this.whenComplete = whenComplete;
this.excludes = excludes;
this.atInvokeExcpetionExit = atInvokeExcpetionExit;
}
public InvokeLocationMatcher(String owner, String methodName, String desc, int count, boolean whenComplete,
List<String> excludes) {
this(owner, methodName, desc, count, whenComplete, excludes, false);
}
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) {
if (this.atInvokeExcpetionExit) {
return matchForException(methodProcessor);
}
List<Location> locations = new ArrayList<Location>();
AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode();
LocationFilter locationFilter = methodProcessor.getLocationFilter();
LocationType locationType = whenComplete ? LocationType.INVOKE_COMPLETED : LocationType.INVOKE;
int matchedCount = 0;
while (insnNode != null) {
if (insnNode instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
if (matchCall(methodInsnNode)) {
if(locationFilter.allow(methodInsnNode, locationType, this.whenComplete)) {
matchedCount++;
if (count <= 0 || count == matchedCount) {
InvokeLocation invokeLocation = new InvokeLocation(methodInsnNode, count, whenComplete);
locations.add(invokeLocation);
}
}
}
}
insnNode = insnNode.getNext();
}
return locations;
}
public List<Location> matchForException(MethodProcessor methodProcessor) {
List<Location> locations = new ArrayList<Location>();
AbstractInsnNode insnNode = methodProcessor.getEnterInsnNode();
MethodNode methodNode = methodProcessor.getMethodNode();
List<MethodInsnNode> methodInsnNodes = new ArrayList<MethodInsnNode>();
LocationFilter locationFilter = methodProcessor.getLocationFilter();
LocationType locationType = LocationType.INVOKE_EXCEPTION_EXIT;
int matchedCount = 0;
while (insnNode != null) {
if (insnNode instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
if (matchCall(methodInsnNode)) {
if(locationFilter.allow(methodInsnNode, locationType, this.whenComplete)) {
matchedCount++;
if (count <= 0 || count == matchedCount) {
methodInsnNodes.add(methodInsnNode);
}
}
}
}
insnNode = insnNode.getNext();
}
// insert try/catch
for (MethodInsnNode methodInsnNode : methodInsnNodes) {
TryCatchBlock tryCatchBlock = new TryCatchBlock(methodNode);
InsnList toInsert = new InsnList();
LabelNode gotoDest = new LabelNode();
LabelNode startLabelNode = tryCatchBlock.getStartLabelNode();
LabelNode endLabelNode = tryCatchBlock.getEndLabelNode();
toInsert.add(new JumpInsnNode(Opcodes.GOTO, gotoDest));
toInsert.add(endLabelNode);
AsmOpUtils.throwException(toInsert);
locations.add(new InvokeExceptionExitLocation(methodInsnNode, endLabelNode));
toInsert.add(gotoDest);
methodNode.instructions.insertBefore(methodInsnNode, startLabelNode);
methodNode.instructions.insert(methodInsnNode, toInsert);
tryCatchBlock.sort();
}
return locations;
}
private boolean matchCall(MethodInsnNode methodInsnNode) {
if (methodName != null && !methodName.isEmpty()) {
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;
}
}
@@ -1,60 +0,0 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.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;
}
}
@@ -1,696 +0,0 @@
package com.taobao.arthas.bytekit.asm.location;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.FieldInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LocalVariableNode;
import com.alibaba.arthas.deps.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 implements MethodInsnNodeWare {
/**
* 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 = false;
}
@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;
}
@Override
public MethodInsnNode methodInsnNode() {
return (MethodInsnNode) insnNode;
}
}
/**
* 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;
}
}
/**
* location identifying a method exceptional exit trigger point
*/
public static class InvokeExceptionExitLocation extends Location implements MethodInsnNodeWare {
private MethodInsnNode methodInsnNode;
public InvokeExceptionExitLocation(MethodInsnNode methodInsnNode, AbstractInsnNode insnNode) {
super(insnNode, true);
stackNeedSave = true;
this.methodInsnNode = methodInsnNode;
}
public LocationType getLocationType() {
return LocationType.INVOKE_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;
}
@Override
public MethodInsnNode methodInsnNode() {
return methodInsnNode;
}
}
}
@@ -1,11 +0,0 @@
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);
}
@@ -1,91 +0,0 @@
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,
/**
* method invoke exception
*/
INVOKE_EXCEPTION_EXIT,
/**
* 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;
}
@@ -1,9 +0,0 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.List;
public class MatchResult {
List<Location> locations;
}
@@ -1,8 +0,0 @@
package com.taobao.arthas.bytekit.asm.location;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
public interface MethodInsnNodeWare {
public MethodInsnNode methodInsnNode();
}
@@ -1,46 +0,0 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.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;
}
}
@@ -1,53 +0,0 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.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;
}
}
@@ -1,47 +0,0 @@
package com.taobao.arthas.bytekit.asm.location;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.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;
}
}
@@ -1,34 +0,0 @@
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;
}
}
@@ -1,38 +0,0 @@
package com.taobao.arthas.bytekit.asm.location.filter;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.taobao.arthas.bytekit.asm.location.LocationType;
import com.taobao.arthas.bytekit.utils.MatchUtils;
/**
*
* @author hengyunabc 2020-05-04
*
*/
public class DefaultLocationFilter implements LocationFilter {
// private List<String> signatures;
//
// public DefaultLocationFilter(List<String> signatures) {
// this.signatures = signatures;
// }
@Override
public boolean allow(AbstractInsnNode insnNode, LocationType locationType, boolean complete) {
return true;
}
// @Override
// public boolean allow(String signature) {
// for (String s : signatures) {
// if (MatchUtils.wildcardMatch(signature, s)) {
// return false;
// }
// }
//
// return true;
// }
}
@@ -1,38 +0,0 @@
package com.taobao.arthas.bytekit.asm.location.filter;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.taobao.arthas.bytekit.asm.location.LocationType;
/**
*
* @author hengyunabc 2020-05-04
*
*/
public class GroupLocationFilter implements LocationFilter {
List<LocationFilter> filters = new ArrayList<LocationFilter>();
public GroupLocationFilter(LocationFilter... filters) {
for (LocationFilter filter : filters) {
this.filters.add(filter);
}
}
public void addFilter(LocationFilter filter) {
this.filters.add(filter);
}
@Override
public boolean allow(AbstractInsnNode insnNode, LocationType locationType, boolean complete) {
for (LocationFilter filter : filters) {
if (filter.allow(insnNode, locationType, complete)) {
return true;
}
}
return false;
}
}
@@ -1,64 +0,0 @@
package com.taobao.arthas.bytekit.asm.location.filter;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.taobao.arthas.bytekit.asm.location.LocationType;
/**
*
* 检查某个 AbstractInsnNode 的前面是否有某个函数调用,如果有,则认为这个location是已被处理过的
*
* @author hengyunabc 2020-05-04
*
*/
public class InvokeCheckLocationFilter implements LocationFilter {
private String owner;
private String methodName;
private LocationType locationType;
public InvokeCheckLocationFilter(String owner, String methodName, LocationType locationType) {
this.owner = owner;
this.methodName = methodName;
this.locationType = locationType;
}
@Override
public boolean allow(AbstractInsnNode insnNode, LocationType locationType, boolean complete) {
// 只检查自己对应的 LocationType
if (!this.locationType.equals(locationType)) {
return false;
}
MethodInsnNode methodInsnNode = findMethodInsnNode(insnNode, complete);
if (methodInsnNode != null) {
if (methodInsnNode.owner.equals(this.owner) && methodInsnNode.name.equals(this.methodName)) {
return false;
}
}
return true;
}
private MethodInsnNode findMethodInsnNode(AbstractInsnNode insnNode, boolean complete) {
if (complete) {
while (insnNode != null) {
insnNode = insnNode.getNext();
if (insnNode instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
return methodInsnNode;
}
}
} else {
while (insnNode != null) {
insnNode = insnNode.getPrevious();
if (insnNode instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
return methodInsnNode;
}
}
}
return null;
}
}
@@ -1,68 +0,0 @@
package com.taobao.arthas.bytekit.asm.location.filter;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.taobao.arthas.bytekit.asm.location.LocationType;
/**
*
* 检查整个method里,是否有某个函数调用。用于检查 enter/exit/exception exit
*
* @author hengyunabc 2020-05-04
*
*/
public class InvokeContainLocationFilter implements LocationFilter {
private String owner;
private String methodName;
private LocationType locationType;
public InvokeContainLocationFilter(String owner, String methodName, LocationType locationType) {
this.owner = owner;
this.methodName = methodName;
this.locationType = locationType;
}
@Override
public boolean allow(AbstractInsnNode insnNode, LocationType locationType, boolean complete) {
// 只检查自己对应的 LocationType
if (!this.locationType.equals(locationType)) {
return false;
}
MethodInsnNode methodInsnNode = findMethodInsnNode(insnNode);
if (methodInsnNode != null) {
if (methodInsnNode.owner.equals(this.owner) && methodInsnNode.name.equals(this.methodName)) {
return false;
}
}
return true;
}
private MethodInsnNode findMethodInsnNode(AbstractInsnNode insnNode) {
AbstractInsnNode current = insnNode;
while (current != null) {
current = current.getNext();
if (current instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) current;
if (methodInsnNode.owner.equals(this.owner) && methodInsnNode.name.equals(this.methodName)) {
return methodInsnNode;
}
}
}
current = insnNode;
while (current != null) {
current = current.getPrevious();
if (current instanceof MethodInsnNode) {
MethodInsnNode methodInsnNode = (MethodInsnNode) current;
if (methodInsnNode.owner.equals(this.owner) && methodInsnNode.name.equals(this.methodName)) {
return methodInsnNode;
}
}
}
return null;
}
}
@@ -1,15 +0,0 @@
package com.taobao.arthas.bytekit.asm.location.filter;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.taobao.arthas.bytekit.asm.location.LocationType;
/**
*
* @author hengyunabc 2020-05-04
*
*/
public interface LocationFilter {
public boolean allow(AbstractInsnNode insnNode, LocationType locationType, boolean complete);
}
@@ -1,7 +0,0 @@
package com.taobao.arthas.bytekit.asm.matcher;
public interface ClassMatcher {
boolean match(String name, ClassLoader classLoader);
}
@@ -1,8 +0,0 @@
package com.taobao.arthas.bytekit.asm.matcher;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
public interface MethodMatcher {
boolean match(String className, MethodNode methodNode);
}
@@ -1,64 +0,0 @@
package com.taobao.arthas.bytekit.utils;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.security.ProtectionDomain;
import net.bytebuddy.agent.ByteBuddyAgent;
public class AgentUtils {
private static class InstrumentationHolder {
static final Instrumentation instance = ByteBuddyAgent.install();
}
public static Instrumentation install() {
return InstrumentationHolder.instance;
}
public static void redefine(Class<?> clazz, byte[] classFile)
throws ClassNotFoundException, UnmodifiableClassException {
ClassDefinition classDefinition = new ClassDefinition(clazz, classFile);
InstrumentationHolder.instance.redefineClasses(classDefinition);
}
public static void reTransform(Class<?> clazz, byte[] classFile) throws UnmodifiableClassException {
SimpleClassFileTransformer transformer = new SimpleClassFileTransformer(clazz.getClassLoader(), clazz.getName(),
classFile);
InstrumentationHolder.instance.addTransformer(transformer, true);
InstrumentationHolder.instance.retransformClasses(clazz);
InstrumentationHolder.instance.removeTransformer(transformer);
}
public static class SimpleClassFileTransformer implements ClassFileTransformer {
private byte[] classBuffer;
private ClassLoader classLoader;
private String className;
public SimpleClassFileTransformer(ClassLoader classLoader, String className, byte[] classBuffer) {
this.classLoader = classLoader;
this.className = className.replace('.', '/');
this.classBuffer = classBuffer;
}
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
if (this.classLoader == loader && className.equals(this.className)) {
return classBuffer;
}
return null;
}
}
}
@@ -1,12 +0,0 @@
package com.taobao.arthas.bytekit.utils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
public class AnnotationUtils {
public static <A extends Annotation> A findAnnotation(Method method, Class<A> annotationType) {
return method.getAnnotation(annotationType);
}
}
@@ -1,77 +0,0 @@
package com.taobao.arthas.bytekit.utils;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AnnotationNode;
/**
*
* @author hengyunabc 2020-05-04
*
*/
public class AsmAnnotationUtils {
public static List<String> queryAnnotationInfo(List<AnnotationNode> annotations, String annotationType,
String key) {
List<String> result = new ArrayList<String>();
if (annotations != null) {
for (AnnotationNode annotationNode : annotations) {
if (annotationNode.desc.equals(annotationType)) {
if (annotationNode.values != null) {
Iterator<Object> iterator = annotationNode.values.iterator();
while (iterator.hasNext()) {
String name = (String) iterator.next();
Object values = iterator.next();
if (key.equals(name)) {
result.addAll((List<String>) values);
}
}
}
}
}
}
return result;
}
public static void addAnnotationInfo(List<AnnotationNode> annotations, String annotationType, String key,
String value) {
AnnotationNode annotationNode = null;
for (AnnotationNode tmp : annotations) {
if (tmp.desc.equals(annotationType)) {
annotationNode = tmp;
}
}
if (annotationNode == null) {
annotationNode = new AnnotationNode(annotationType);
annotations.add(annotationNode);
}
if (annotationNode.values == null) {
annotationNode.values = new ArrayList<Object>();
}
// 查找有没有对应的key
String name = null;
List<String> values = null;
Iterator<Object> iterator = annotationNode.values.iterator();
while (iterator.hasNext()) {
if (key.equals(iterator.next())) {
values = (List<String>) iterator.next();
} else {
iterator.next();
}
}
if (values == null) {
values = new ArrayList<String>();
annotationNode.values.add(key);
annotationNode.values.add(values);
}
if (!values.contains(values)) {
values.add(value);
}
}
}
@@ -1,463 +0,0 @@
package com.taobao.arthas.bytekit.utils;
import java.util.ArrayList;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.commons.Method;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.FieldInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.InsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.IntInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LdcInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LocalVariableNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.TypeInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.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 BYTE_VALUE = Method.getMethod("byte byteValue()");
private static final Method SHORT_VALUE = Method.getMethod("short shortValue()");
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 boolean isBoxType(final Type type) {
if (BYTE_TYPE.equals(type) || BOOLEAN_TYPE.equals(type) || SHORT_TYPE.equals(type)
|| CHARACTER_TYPE.equals(type) || INTEGER_TYPE.equals(type) || FLOAT_TYPE.equals(type)
|| LONG_TYPE.equals(type) || DOUBLE_TYPE.equals(type)) {
return true;
}
return false;
}
public 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 Method getUnBoxMethod(final Type type) {
switch (type.getSort()) {
case Type.BYTE:
return BYTE_VALUE;
case Type.BOOLEAN:
return BOOLEAN_VALUE;
case Type.SHORT:
return SHORT_VALUE;
case Type.CHAR:
return CHAR_VALUE;
case Type.INT:
return INT_VALUE;
case Type.FLOAT:
return FLOAT_VALUE;
case Type.LONG:
return LONG_VALUE;
case Type.DOUBLE:
return DOUBLE_VALUE;
}
throw new IllegalArgumentException(type + " is not a primitive 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));
}
}
public static void pushNUll(InsnList insnList) {
insnList.add(new InsnNode(Opcodes.ACONST_NULL));
}
/**
* @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));
}
/**
*
* @param instructions
* @param type
* @see org.objectweb.asm.commons.GeneratorAdapter#unbox(Type)
*/
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 instruction to load 'this' on the stack.
* @see org.objectweb.asm.commons.GeneratorAdapter#loadThis()
* @param instructions
*/
public static void loadThis(final InsnList instructions) {
instructions.add(new VarInsnNode(Opcodes.ALOAD, 0));
}
/**
* 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;
}
}
@@ -1,586 +0,0 @@
package com.taobao.arthas.bytekit.utils;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.alibaba.arthas.deps.org.objectweb.asm.ClassReader;
import com.alibaba.arthas.deps.org.objectweb.asm.ClassVisitor;
import com.alibaba.arthas.deps.org.objectweb.asm.ClassWriter;
import com.alibaba.arthas.deps.org.objectweb.asm.Label;
import com.alibaba.arthas.deps.org.objectweb.asm.MethodVisitor;
import com.alibaba.arthas.deps.org.objectweb.asm.Opcodes;
import com.alibaba.arthas.deps.org.objectweb.asm.Type;
import com.alibaba.arthas.deps.org.objectweb.asm.commons.ClassRemapper;
import com.alibaba.arthas.deps.org.objectweb.asm.commons.JSRInlinerAdapter;
import com.alibaba.arthas.deps.org.objectweb.asm.commons.Remapper;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.ClassNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.FieldNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.LocalVariableNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.MethodNode;
import com.alibaba.arthas.deps.org.objectweb.asm.tree.TypeInsnNode;
import com.alibaba.arthas.deps.org.objectweb.asm.util.ASMifier;
import com.alibaba.arthas.deps.org.objectweb.asm.util.TraceClassVisitor;
import com.taobao.arthas.bytekit.asm.ClassLoaderAwareClassWriter;
/**
*
* @author hengyunabc
*
*/
public class AsmUtils {
public static ClassNode loadClass(Class<?> clazz) throws IOException {
String resource = clazz.getName().replace('.', '/') + ".class";
InputStream is = clazz.getClassLoader().getResourceAsStream(resource);
ClassReader cr = new ClassReader(is);
ClassNode classNode = new ClassNode();
cr.accept(classNode, ClassReader.SKIP_FRAMES);
return classNode;
}
public static ClassNode toClassNode(byte[] classBytes) {
ClassReader reader = new ClassReader(classBytes);
ClassNode result = new ClassNode(Opcodes.ASM9);
reader.accept(result, ClassReader.SKIP_FRAMES);
return result;
}
public static ClassReader toClassNode(byte[] classBytes, ClassNode classNode) {
ClassReader reader = new ClassReader(classBytes);
reader.accept(classNode, ClassReader.SKIP_FRAMES);
return reader;
}
/**
* Generate class bytes from class node.
* <br>
* <B>NOTE: must pass origin classReader for bytecode optimizations, avoiding JVM metaspace OOM.</B>
* @param classNode
* @param classLoader
* @param classReader origin class reader
* @return
*/
public static byte[] toBytes(ClassNode classNode, ClassLoader classLoader, ClassReader classReader) {
int flags = ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS;
ClassWriter writer = new ClassLoaderAwareClassWriter(classReader, flags, classLoader);
classNode.accept(writer);
return writer.toByteArray();
}
public static byte[] toBytes(ClassNode classNode) {
ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
classNode.accept(writer);
return writer.toByteArray();
}
public static byte[] renameClass(byte[] classBytes, final String newClassName) {
final String internalName = newClassName.replace('.', '/');
ClassReader reader = new ClassReader(classBytes);
ClassWriter writer = new ClassWriter(0);
class RenameRemapper extends Remapper {
private String className;
@Override
public String map(String typeName) {
if (typeName.equals(className)) {
return internalName;
}
return super.map(typeName);
}
public void setClassName(String className) {
this.className = className;
}
}
final RenameRemapper renameRemapper = new RenameRemapper();
ClassRemapper adapter = new ClassRemapper(writer, renameRemapper) {
@Override
public void visit(final int version, final int access, final String name, final String signature,
final String superName, final String[] interfaces) {
renameRemapper.setClassName(name);
super.visit(version, access, name, signature, superName, interfaces);
}
};
reader.accept(adapter, ClassReader.EXPAND_FRAMES);
writer.visitEnd();
return writer.toByteArray();
}
public static void replaceMethod(ClassNode classNode, MethodNode methodNode) {
for (int index = 0; index < classNode.methods.size(); ++index) {
MethodNode tmp = classNode.methods.get(index);
if (tmp.name.equals(methodNode.name) && tmp.desc.equals(methodNode.desc)) {
classNode.methods.set(index, methodNode);
}
}
}
public static String toASMCode(byte[] bytecode) throws IOException {
return toASMCode(bytecode, true);
}
public static String toASMCode(byte[] bytecode, boolean debug) throws IOException {
int flags = ClassReader.SKIP_DEBUG;
if (debug) {
flags = 0;
}
ClassReader cr = new ClassReader(new ByteArrayInputStream(bytecode));
StringWriter sw = new StringWriter();
cr.accept(new TraceClassVisitor(null, new ASMifier(), new PrintWriter(sw)), flags);
return sw.toString();
}
public static String toASMCode(ClassNode classNode) {
StringWriter sw = new StringWriter();
classNode.accept(new TraceClassVisitor(null, new ASMifier(), new PrintWriter(sw)));
return sw.toString();
}
public static String toASMCode(MethodNode methodNode) {
ClassNode classNode = new ClassNode();
classNode.methods.add(methodNode);
return toASMCode(classNode);
}
public static MethodNode newMethodNode(MethodNode source) {
return new MethodNode(Opcodes.ASM9, source.access, source.name, source.desc, source.signature,
source.exceptions.toArray(new String[source.exceptions.size()]));
}
public static MethodNode removeJSRInstructions(MethodNode subjectMethod) {
MethodNode result = newMethodNode(subjectMethod);
subjectMethod.accept(new JSRInlinerAdapter(result, subjectMethod.access, subjectMethod.name, subjectMethod.desc,
subjectMethod.signature,
subjectMethod.exceptions.toArray(new String[subjectMethod.exceptions.size()])));
return result;
}
public static ClassNode removeJSRInstructions(ClassNode classNode) {
ClassNode result = new ClassNode(Opcodes.ASM9);
classNode.accept(new ClassVisitor(Opcodes.ASM9, result) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature,
String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
return new JSRInlinerAdapter(mv, access, name, desc, signature, exceptions);
}
});
return result;
}
public static MethodNode removeLineNumbers(MethodNode methodNode) {
MethodNode result = newMethodNode(methodNode);
methodNode.accept(new MethodVisitor(Opcodes.ASM9, result) {
public void visitLineNumber(int line, Label start) {
}
});
return result;
}
public static MethodNode findFirstMethod(Collection<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, MethodNode target) {
return findMethod(methodNodes, target.name, target.desc);
}
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 List<MethodInsnNode> findMethodInsnNodeWithPrefix(MethodNode methodNode, String prefix) {
List<MethodInsnNode> result = new ArrayList<MethodInsnNode>();
for (AbstractInsnNode insnNode = methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode
.getNext()) {
if (insnNode instanceof MethodInsnNode) {
final MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
if(methodInsnNode.name.startsWith(prefix)) {
result.add(methodInsnNode);
}
}
}
return result;
}
public static List<MethodInsnNode> findMethodInsnNode(MethodNode methodNode, String owner, String name) {
List<MethodInsnNode> result = new ArrayList<MethodInsnNode>();
for (AbstractInsnNode insnNode = methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode
.getNext()) {
if (insnNode instanceof MethodInsnNode) {
final MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
if (methodInsnNode.owner.equals(owner) && methodInsnNode.name.equals(name)) {
result.add(methodInsnNode);
}
}
}
return result;
}
public static List<MethodInsnNode> findMethodInsnNode(MethodNode methodNode, String owner, String name,
String desc) {
List<MethodInsnNode> result = new ArrayList<MethodInsnNode>();
for (AbstractInsnNode insnNode = methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode
.getNext()) {
if (insnNode instanceof MethodInsnNode) {
final MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
if (methodInsnNode.owner.equals(owner) && methodInsnNode.name.equals(name)
&& methodInsnNode.desc.equals(desc)) {
result.add(methodInsnNode);
}
}
}
return result;
}
public static boolean containsMethodInsnNode(MethodNode methodNode, String owner, String name) {
for (AbstractInsnNode insnNode = methodNode.instructions.getFirst(); insnNode != null; insnNode = insnNode
.getNext()) {
if (insnNode instanceof MethodInsnNode) {
final MethodInsnNode methodInsnNode = (MethodInsnNode) insnNode;
if (methodInsnNode.owner.equals(owner) && methodInsnNode.name.equals(name)) {
return true;
}
}
}
return false;
}
public static boolean isStatic(MethodNode methodNode) {
return (methodNode.access & Opcodes.ACC_STATIC) != 0;
}
public static boolean isStatic(MethodInsnNode methodInsnNode) {
return methodInsnNode.getOpcode() == Opcodes.INVOKESTATIC;
}
public static boolean isConstructor(MethodNode methodNode) {
return methodNode.name != null && methodNode.name.equals("<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 ClassNode copy(ClassNode source) {
ClassNode result = new ClassNode(Opcodes.ASM9);
source.accept(new ClassVisitor(Opcodes.ASM9, result) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature,
String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
return new JSRInlinerAdapter(mv, access, name, desc, signature, exceptions);
}
});
return result;
}
public static String methodDeclaration(MethodInsnNode methodInsnNode) {
StringBuilder sb = new StringBuilder(128);
int opcode = methodInsnNode.getOpcode();
if(opcode == Opcodes.INVOKESTATIC) {
sb.append("static ");
}
Type methodType = Type.getMethodType(methodInsnNode.desc);
Type ownerType = Type.getObjectType(methodInsnNode.owner);
//skip constructor return type
if(methodInsnNode.name.equals("<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;
}
public static void addField(ClassNode classNode, FieldNode fieldNode) {
// TODO 检查是否有重复?
classNode.fields.add(fieldNode);
}
public static void addMethod(ClassNode classNode, MethodNode methodNode) {
classNode.methods.add(methodNode);
}
// TODO 是否真的 unique 了?
public static String uniqueNameForMethod(String className, String methodName, String desc) {
StringBuilder result = new StringBuilder(128);
result.append(cleanClassName(className)).append('_').append(methodName);
for(Type arg : Type.getMethodType(desc).getArgumentTypes()) {
result.append('_').append(cleanClassName(arg.getClassName()));
}
return result.toString();
}
private static String cleanClassName(String className) {
char[] charArray = className.toCharArray();
int length = charArray.length;
for(int i = 0 ; i < length; ++i) {
switch( charArray[i]) {
case '[' :
case ']' :
case '<' :
case '>' :
case ';' :
case '/' :
case '.' :
charArray[i] = '_';
break;
}
}
return new String(charArray);
}
/**
* Java ClassFile versions (the minor version is stored in the 16 most
* significant bits, and the major version in the 16 least significant bits).
* @see com.alibaba.arthas.deps.org.objectweb.asm.Opcodes#V_PREVIEW
* @param version
* @return
*/
public static int getMajorVersion(int version) {
return 0x0000FFFF & version;
}
/**
* 替换掉完整 version里的 major version
*
* @param version
* @param majorVersion
* @return
*/
public static int setMajorVersion(int version, int majorVersion) {
return (version & 0xFFFF0000) | majorVersion;
}
}
@@ -1,48 +0,0 @@
package com.taobao.arthas.bytekit.utils;
import java.lang.reflect.Field;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import sun.misc.Unsafe;
/**
*
* @author hengyunabc 2017-10-12
*
*/
public class ClassLoaderUtils {
@SuppressWarnings({ "restriction", "unchecked" })
public static URL[] getUrls(ClassLoader classLoader) {
if (classLoader instanceof URLClassLoader) {
return ((URLClassLoader) classLoader).getURLs();
}
// jdk9
if (classLoader.getClass().getName().startsWith("jdk.internal.loader.ClassLoaders$")) {
try {
Field field = Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
// jdk.internal.loader.ClassLoaders.AppClassLoader.ucp
Field ucpField = classLoader.getClass().getDeclaredField("ucp");
long ucpFieldOffset = unsafe.objectFieldOffset(ucpField);
Object ucpObject = unsafe.getObject(classLoader, ucpFieldOffset);
// jdk.internal.loader.URLClassPath.path
Field pathField = ucpField.getType().getDeclaredField("path");
long pathFieldOffset = unsafe.objectFieldOffset(pathField);
ArrayList<URL> path = (ArrayList<URL>) unsafe.getObject(ucpObject, pathFieldOffset);
return path.toArray(new URL[path.size()]);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
return null;
}
}

Some files were not shown because too many files have changed in this diff Show More