Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ bytecode kit for java.

* `@Binding.Line` 行号
* `@Binding.Monitor` 同步块里监控的对象
* `@Binding.MethodFirstLine` 方法的首次执行的行号
* `@Binding.MethodLastLine` 方法的末次执行的行号


### 3. 可编程的异常处理
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -434,4 +434,38 @@ public Binding parse(Annotation annotation) {
return new MonitorBinding();
}
}

@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = MethodFirstLineBindingParser.class)
public static @interface MethodFirstLine {

boolean optional() default false;

}

public static class MethodFirstLineBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new MethodFirstLineBinding();
}
}

@Documented
@Retention(RetentionPolicy.RUNTIME)
@java.lang.annotation.Target(ElementType.PARAMETER)
@BindingParserHandler(parser = MethodLastLineBindingParser.class)
public static @interface MethodLastLine {

boolean optional() default false;

}

public static class MethodLastLineBindingParser implements BindingParser {
@Override
public Binding parse(Annotation annotation) {
return new MethodLastLineBinding();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.alibaba.bytekit.asm.binding;

import com.alibaba.deps.org.objectweb.asm.Type;
import com.alibaba.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.deps.org.objectweb.asm.tree.LineNumberNode;
import com.alibaba.bytekit.utils.AsmOpUtils;

/**
* Binding for method first line number
* @author lbs
*
*/
public class MethodFirstLineBinding extends Binding {

@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
int line = -1;

// Start from the very first instruction of the method
AbstractInsnNode insnNode = bindingContext.getMethodProcessor().getMethodNode().instructions.getFirst();

// Find the first LineNumberNode
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);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.alibaba.bytekit.asm.binding;

import com.alibaba.deps.org.objectweb.asm.Type;
import com.alibaba.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.deps.org.objectweb.asm.tree.LineNumberNode;
import com.alibaba.bytekit.utils.AsmOpUtils;

/**
* Binding for method last line number
* @author lbs
*
*/
public class MethodLastLineBinding extends Binding {

@Override
public void pushOntoStack(InsnList instructions, BindingContext bindingContext) {
int line = -1;
LineNumberNode lastLineNumberNode = null;

// Start from the very first instruction of the method
AbstractInsnNode insnNode = bindingContext.getMethodProcessor().getMethodNode().instructions.getFirst();

// Find the last LineNumberNode
while (insnNode != null) {
if (insnNode instanceof LineNumberNode) {
lastLineNumberNode = (LineNumberNode) insnNode;
}
insnNode = insnNode.getNext();
}

if (lastLineNumberNode != null) {
line = lastLineNumberNode.line;
}

AsmOpUtils.push(instructions, line);
}

@Override
public Type getType(BindingContext bindingContext) {
return Type.getType(int.class);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package com.alibaba.bytekit.asm.binding;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;

import com.alibaba.bytekit.asm.interceptor.annotation.AtEnter;
import com.alibaba.bytekit.asm.interceptor.annotation.AtExit;
import com.alibaba.bytekit.asm.interceptor.annotation.AtExceptionExit;
import com.alibaba.bytekit.utils.Decompiler;
import com.alibaba.bytekit.asm.interceptor.TestHelper;

public class MethodFirstLineBindingTest {

@Rule
public OutputCapture capture = new OutputCapture();

public static class Sample {

long longField;
String strField;
static int intField;

public int hello(String str, boolean exception) {
if (exception) {
throw new RuntimeException("test exception");
}
return str.length();
}

public long toBeInvoke(int i, long l, String s, long ll) {
return l + ll;
}

public void testInvokeArgs() {
toBeInvoke(1, 123L, "abc", 100L);
}

}

public static class EnterInterceptor {

@AtEnter(inline = true)
public static void onEnter(
@Binding.This Object object,
@Binding.MethodFirstLine int firstLine,
@Binding.MethodName String methodName
) {
System.err.println("AtEnter, methodName:" + methodName + ", firstLine:" + firstLine);
}

}

public static class ExitInterceptor {

@AtExit(inline = true)
public static void onExit(
@Binding.This Object object,
@Binding.MethodFirstLine int firstLine,
@Binding.MethodName String methodName,
@Binding.Return Object returnObject
) {
System.err.println("AtExit, methodName:" + methodName + ", firstLine:" + firstLine + ", return:" + returnObject);
}

}

public static class ExceptionExitInterceptor {

@AtExceptionExit(inline = true, onException = RuntimeException.class)
public static void onExceptionExit(
@Binding.This Object object,
@Binding.MethodFirstLine int firstLine,
@Binding.MethodName String methodName,
@Binding.Throwable RuntimeException ex
) {
System.err.println("AtExceptionExit, methodName:" + methodName + ", firstLine:" + firstLine + ", ex:" + ex.getMessage());
}

}

@Test
public void testMethodLineAtEnter() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(EnterInterceptor.class).methodMatcher("hello")
.reTransform(true);
byte[] bytes = helper.process(Sample.class);

new Sample().hello("abc", false);

System.err.println(Decompiler.decompile(bytes));

assertThat(capture.toString()).contains("AtEnter, methodName:hello, firstLine:");
}

@Test
public void testMethodLineAtExit() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(ExitInterceptor.class).methodMatcher("hello")
.reTransform(true);
byte[] bytes = helper.process(Sample.class);

new Sample().hello("abc", false);

System.err.println(Decompiler.decompile(bytes));

assertThat(capture.toString()).contains("AtExit, methodName:hello, firstLine:");
}

@Test
public void testMethodLineAtExceptionExit() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(ExceptionExitInterceptor.class).methodMatcher("hello")
.reTransform(true);
byte[] bytes = helper.process(Sample.class);

try {
new Sample().hello("abc", true);
} catch (RuntimeException e) {
// expected
}

System.err.println(Decompiler.decompile(bytes));

assertThat(capture.toString()).contains("AtExceptionExit, methodName:hello, firstLine:");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package com.alibaba.bytekit.asm.binding;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.Rule;
import org.junit.Test;
import org.springframework.boot.test.rule.OutputCapture;

import com.alibaba.bytekit.asm.interceptor.annotation.AtEnter;
import com.alibaba.bytekit.asm.interceptor.annotation.AtExit;
import com.alibaba.bytekit.asm.interceptor.annotation.AtExceptionExit;
import com.alibaba.bytekit.utils.Decompiler;
import com.alibaba.bytekit.asm.interceptor.TestHelper;

public class MethodLastLineBindingTest {

@Rule
public OutputCapture capture = new OutputCapture();

public static class Sample {

long longField;
String strField;
static int intField;

public int hello(String str, boolean exception) {
if (exception) {
throw new RuntimeException("test exception");
}
return str.length();
}

public long toBeInvoke(int i, long l, String s, long ll) {
return l + ll;
}

public void testInvokeArgs() {
toBeInvoke(1, 123L, "abc", 100L);
}

}

public static class EnterInterceptor {

@AtEnter(inline = true)
public static void onEnter(
@Binding.This Object object,
@Binding.MethodLastLine int lastLine,
@Binding.MethodName String methodName
) {
System.err.println("AtEnter, methodName:" + methodName + ", lastLine:" + lastLine);
}

}

public static class ExitInterceptor {

@AtExit(inline = true)
public static void onExit(
@Binding.This Object object,
@Binding.MethodLastLine int lastLine,
@Binding.MethodName String methodName,
@Binding.Return Object returnObject
) {
System.err.println("AtExit, methodName:" + methodName + ", lastLine:" + lastLine + ", return:" + returnObject);
}

}

public static class ExceptionExitInterceptor {

@AtExceptionExit(inline = true, onException = RuntimeException.class)
public static void onExceptionExit(
@Binding.This Object object,
@Binding.MethodLastLine int lastLine,
@Binding.MethodName String methodName,
@Binding.Throwable RuntimeException ex
) {
System.err.println("AtExceptionExit, methodName:" + methodName + ", lastLine:" + lastLine + ", ex:" + ex.getMessage());
}

}

@Test
public void testMethodLastLineAtEnter() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(EnterInterceptor.class).methodMatcher("hello")
.reTransform(true);
byte[] bytes = helper.process(Sample.class);

new Sample().hello("abc", false);

System.err.println(Decompiler.decompile(bytes));

assertThat(capture.toString()).contains("AtEnter, methodName:hello, lastLine:");
}

@Test
public void testMethodLastLineAtExit() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(ExitInterceptor.class).methodMatcher("hello")
.reTransform(true);
byte[] bytes = helper.process(Sample.class);

new Sample().hello("abc", false);

System.err.println(Decompiler.decompile(bytes));

assertThat(capture.toString()).contains("AtExit, methodName:hello, lastLine:");
}

@Test
public void testMethodLastLineAtExceptionExit() throws Exception {
TestHelper helper = TestHelper.builder().interceptorClass(ExceptionExitInterceptor.class).methodMatcher("hello")
.reTransform(true);
byte[] bytes = helper.process(Sample.class);

try {
new Sample().hello("abc", true);
} catch (RuntimeException e) {
// expected
}

System.err.println(Decompiler.decompile(bytes));

assertThat(capture.toString()).contains("AtExceptionExit, methodName:hello, lastLine:");
}

}