Java 在try-finally块中嵌入方法的现有代码
我想在方法代码中添加指令。这些指令应在到达方法后和离开方法前执行。 为了确保后面的指令总是在离开前执行,我想把它们放在finally块中。 (我知道类Java 在try-finally块中嵌入方法的现有代码,java,bytecode,instrumentation,java-bytecode-asm,bytecode-manipulation,Java,Bytecode,Instrumentation,Java Bytecode Asm,Bytecode Manipulation,我想在方法代码中添加指令。这些指令应在到达方法后和离开方法前执行。 为了确保后面的指令总是在离开前执行,我想把它们放在finally块中。 (我知道类AdviceAdapter,但它不能确保在调用的方法引发异常时执行退出代码。) 我的问题是结果中的指令顺序错误。 要处理的方法: @Test public void original() { assertTrue(true); assertTrue(!(false)); } @Test public void desired()
AdviceAdapter
,但它不能确保在调用的方法引发异常时执行退出代码。)
我的问题是结果中的指令顺序错误。
要处理的方法:
@Test
public void original() {
assertTrue(true);
assertTrue(!(false));
}
@Test
public void desired() {
//some logging X
try {
assertTrue(true);
assertTrue(!(false));
}
finally {
//some logging Y
}
}
@Override
public void visitCode() {
before();
super.visitCode();
after();
}
private void before() {
insertInstructionToSetMode(LoggingMode.TESTING);
this.l0 = new Label();
this.l1 = new Label();
visitLabel(l0);
}
private void after() {
visitTryCatchBlock(l0, l1, l1, null);
Label l2 = new Label();
visitJumpInsn(GOTO, l2);
visitLabel(this.l1);
visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Throwable"});
visitVarInsn(ASTORE, 1);
insertInstructionToSetMode(LoggingMode.FRAMING);
visitVarInsn(ALOAD, 1);
visitInsn(ATHROW);
visitLabel(l2);
visitFrame(Opcodes.F_SAME, 0, null, 0, null);
insertInstructionToSetMode(LoggingMode.FRAMING);
}
private void insertInstructionToSetMode(LoggingMode mode) {
String modeValue = (mode == LoggingMode.TESTING ? FIELD_NAME_TESTING : FIELD_NAME_FRAMING);
visitFieldInsn(Opcodes.GETSTATIC, CP_LOGGING_MODE, modeValue, FIELD_DESC_LOGGING_MODE);
visitMethodInsn(INVOKESTATIC, CP_INVOCATION_LOGGER, METHOD_NAME_SET_MODE, METHOD_DESC_SET_MODE);
}
所需结果:
@Test
public void original() {
assertTrue(true);
assertTrue(!(false));
}
@Test
public void desired() {
//some logging X
try {
assertTrue(true);
assertTrue(!(false));
}
finally {
//some logging Y
}
}
@Override
public void visitCode() {
before();
super.visitCode();
after();
}
private void before() {
insertInstructionToSetMode(LoggingMode.TESTING);
this.l0 = new Label();
this.l1 = new Label();
visitLabel(l0);
}
private void after() {
visitTryCatchBlock(l0, l1, l1, null);
Label l2 = new Label();
visitJumpInsn(GOTO, l2);
visitLabel(this.l1);
visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Throwable"});
visitVarInsn(ASTORE, 1);
insertInstructionToSetMode(LoggingMode.FRAMING);
visitVarInsn(ALOAD, 1);
visitInsn(ATHROW);
visitLabel(l2);
visitFrame(Opcodes.F_SAME, 0, null, 0, null);
insertInstructionToSetMode(LoggingMode.FRAMING);
}
private void insertInstructionToSetMode(LoggingMode mode) {
String modeValue = (mode == LoggingMode.TESTING ? FIELD_NAME_TESTING : FIELD_NAME_FRAMING);
visitFieldInsn(Opcodes.GETSTATIC, CP_LOGGING_MODE, modeValue, FIELD_DESC_LOGGING_MODE);
visitMethodInsn(INVOKESTATIC, CP_INVOCATION_LOGGER, METHOD_NAME_SET_MODE, METHOD_DESC_SET_MODE);
}
(也可以在try块的第一行中记录X。)
(所需结果的字节码等于以下Java代码的字节码:)
使用ASM处理方法的我的代码:
@Test
public void original() {
assertTrue(true);
assertTrue(!(false));
}
@Test
public void desired() {
//some logging X
try {
assertTrue(true);
assertTrue(!(false));
}
finally {
//some logging Y
}
}
@Override
public void visitCode() {
before();
super.visitCode();
after();
}
private void before() {
insertInstructionToSetMode(LoggingMode.TESTING);
this.l0 = new Label();
this.l1 = new Label();
visitLabel(l0);
}
private void after() {
visitTryCatchBlock(l0, l1, l1, null);
Label l2 = new Label();
visitJumpInsn(GOTO, l2);
visitLabel(this.l1);
visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {"java/lang/Throwable"});
visitVarInsn(ASTORE, 1);
insertInstructionToSetMode(LoggingMode.FRAMING);
visitVarInsn(ALOAD, 1);
visitInsn(ATHROW);
visitLabel(l2);
visitFrame(Opcodes.F_SAME, 0, null, 0, null);
insertInstructionToSetMode(LoggingMode.FRAMING);
}
private void insertInstructionToSetMode(LoggingMode mode) {
String modeValue = (mode == LoggingMode.TESTING ? FIELD_NAME_TESTING : FIELD_NAME_FRAMING);
visitFieldInsn(Opcodes.GETSTATIC, CP_LOGGING_MODE, modeValue, FIELD_DESC_LOGGING_MODE);
visitMethodInsn(INVOKESTATIC, CP_INVOCATION_LOGGER, METHOD_NAME_SET_MODE, METHOD_DESC_SET_MODE);
}
生成的字节码(指令顺序错误):
01-02可以,但是09-10必须在原始代码(14)之后,但在返回指令之前11-14必须在03之前。您可以在测试方法之前和之后调用的方法上添加JUnit注释
@before
和@before
我不确定你的方法中的错误在哪里。但在使用AdviceAdapter进行了一些尝试和错误之后,我实现了类似的目标
看
警告:只有当一个方法恰好包含一条返回指令时,此解决方案才有效(例如:如果只引发异常,则此解决方案不起作用)。 请参阅:
我发现了问题: 调用
super.visitCode
时,现有代码不会插入到visitCode
方法中。此方法在超类中为空。这清楚地表明,现有代码是在其他点添加的
解决方案:
我在visitCode
方法中调用我的方法before
(它为需要在开头的新行添加代码)。如果操作码是返回语句,我在visitVarInsn
中调用after
@Override
public void visitCode()
{
before();
}
@Override
public void visitInsn(int opcode)
{
if (OpcodesUtil.isXRETURN(opcode))
{
after();
}
super.visitInsn(opcode);
}
(虽然
AdviceAdapter
也起了作用,但是在确保每个ClassReader
的accept
方法都被EXPAND\u FRAMES
调用时出现了一些问题。此外,它可能会建议更多的退出点,而在关闭一个try块时,这些建议就不起作用了。)谢谢您的回答。我知道,但那不是我想要的。其他@Before
或@After
(或@BeforeClass
,@AfterClass
)方法可能已经存在,然后在我的方法之前执行。请注意,return也可能引发异常。@锑:return本身(第15行)不会导致异常,因为它只是弹出并返回堆栈上的值。返回值的计算(可能引发异常)发生在返回之前的指令中,并且应该仍然在try块中。(尽管测试用例通常是无效的方法。)一般来说,当监视器处于非法状态时,返回指令本身可以引发异常。但这应该是个问题。好的,没错。你知道为什么说明书的顺序不对吗?谢谢,我试着把你的解决方案和我的比较一下。相关的字节码指令几乎相同,但现在我发现了问题所在。