mirror of
https://github.com/alibaba/arthas.git
synced 2024-04-21 10:21:39 +00:00
move bytekit to a independent project. #1545
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
-37
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
-37
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
-37
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
-30
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
-11
@@ -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);
|
||||
|
||||
}
|
||||
-15
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-52
@@ -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 {
|
||||
|
||||
}
|
||||
-5
@@ -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 {
|
||||
|
||||
}
|
||||
-71
@@ -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;
|
||||
}
|
||||
}
|
||||
-255
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-45
@@ -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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
-49
@@ -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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
-43
@@ -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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
-72
@@ -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());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
-79
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
-82
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
-46
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
-49
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
-49
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
-46
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
-36
@@ -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;
|
||||
}
|
||||
}
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
package com.taobao.arthas.bytekit.asm.interceptor.annotation;
|
||||
|
||||
public class EmptySuppressHandler {
|
||||
|
||||
@ExceptionHandler
|
||||
public static void onSuppress() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-13
@@ -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;
|
||||
}
|
||||
-85
@@ -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;
|
||||
}
|
||||
}
|
||||
-17
@@ -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();
|
||||
|
||||
}
|
||||
-49
@@ -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() {
|
||||
}
|
||||
}
|
||||
-11
@@ -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();
|
||||
}
|
||||
}
|
||||
-50
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-10
@@ -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);
|
||||
}
|
||||
-11
@@ -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);
|
||||
}
|
||||
-24
@@ -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;
|
||||
}
|
||||
}
|
||||
-26
@@ -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;
|
||||
}
|
||||
}
|
||||
-39
@@ -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;
|
||||
}
|
||||
}
|
||||
-96
@@ -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;
|
||||
}
|
||||
}
|
||||
-243
@@ -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();
|
||||
}
|
||||
-46
@@ -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;
|
||||
}
|
||||
}
|
||||
-47
@@ -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;
|
||||
}
|
||||
}
|
||||
-34
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-38
@@ -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;
|
||||
// }
|
||||
|
||||
}
|
||||
-38
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-64
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-68
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-15
@@ -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
Reference in New Issue
Block a user