| /******************************************************************************* |
| * Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Evgeny Mandrikov - initial API and implementation |
| * |
| *******************************************************************************/ |
| package org.jacoco.core.internal.analysis.filter; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.fail; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.jacoco.core.internal.instr.InstrSupport; |
| import org.junit.Test; |
| import org.objectweb.asm.Label; |
| import org.objectweb.asm.Opcodes; |
| import org.objectweb.asm.tree.AbstractInsnNode; |
| import org.objectweb.asm.tree.MethodNode; |
| |
| public class TryWithResourcesJavacFilterTest implements IFilterOutput { |
| |
| private final TryWithResourcesJavacFilter filter = new TryWithResourcesJavacFilter(); |
| |
| private final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0, |
| "name", "()V", null, null); |
| |
| /** |
| * javac 9 for |
| * |
| * <pre> |
| * try (r0 = open(...); r1 = new ...) { |
| * return ... |
| * } finally { |
| * ... |
| * } |
| * </pre> |
| * |
| * generates |
| * |
| * <pre> |
| * ... |
| * ASTORE r0 |
| * ACONST_NULL |
| * ASTORE primaryExc0 |
| * |
| * ... |
| * ASTORE r1 |
| * ACONST_NULL |
| * ASTORE primaryExc1 |
| * |
| * ... // body |
| * |
| * ALOAD primaryExc1 |
| * ALOAD r1 |
| * INVOKESTATIC $closeResource:(Ljava/lang/Throwable;Ljava/lang/AutoCloseable;)V |
| * |
| * ALOAD r0 |
| * IFNULL n |
| * ALOAD primaryExc0 |
| * ALOAD r0 |
| * INVOKESTATIC $closeResource:(Ljava/lang/Throwable;Ljava/lang/AutoCloseable;)V |
| * n: |
| * |
| * ... // finally on normal path |
| * ARETURN |
| * |
| * ASTORE t |
| * ALOAD t |
| * ASTORE primaryExc1 |
| * ALOAD t |
| * ATHROW |
| * |
| * ASTORE t |
| * ALOAD primaryExc1 |
| * ALOAD r1 |
| * INVOKESTATIC $closeResource:(Ljava/lang/Throwable;Ljava/lang/AutoCloseable;)V |
| * ALOAD t |
| * ATHROW |
| * |
| * ASTORE t |
| * ALOAD t |
| * ASTORE primaryExc0 |
| * ALOAD t |
| * ATHROW |
| * |
| * ASTORE t |
| * ALOAD r0 |
| * IFNULL n |
| * ALOAD primaryExc0 |
| * ALOAD r0 |
| * INVOKESTATIC $closeResource:(Ljava/lang/Throwable;Ljava/lang/AutoCloseable;)V |
| * n: |
| * ALOAD t |
| * ATHROW |
| * |
| * ... // additional handlers for catch blocks and finally on exceptional path |
| * </pre> |
| */ |
| @Test |
| public void javac9() { |
| final Range range0 = new Range(); |
| final Range range1 = new Range(); |
| final Range range2 = new Range(); |
| final Range range3 = new Range(); |
| |
| final Label handler1 = new Label(); |
| m.visitTryCatchBlock(handler1, handler1, handler1, |
| "java/lang/Throwable"); |
| |
| final Label handler2 = new Label(); |
| m.visitTryCatchBlock(handler2, handler2, handler2, |
| "java/lang/Throwable"); |
| |
| // r0 = open(...) |
| m.visitVarInsn(Opcodes.ASTORE, 1); |
| |
| // primaryExc0 = null |
| m.visitInsn(Opcodes.ACONST_NULL); |
| m.visitVarInsn(Opcodes.ASTORE, 2); |
| |
| // r1 = new .. |
| m.visitVarInsn(Opcodes.ASTORE, 3); |
| |
| // primaryExc1 = null |
| m.visitInsn(Opcodes.ACONST_NULL); |
| m.visitVarInsn(Opcodes.ASTORE, 4); |
| |
| // body |
| m.visitInsn(Opcodes.NOP); |
| m.visitInsn(Opcodes.ACONST_NULL); |
| m.visitVarInsn(Opcodes.ASTORE, 5); |
| |
| // $closeResource(primaryExc1, r1) |
| m.visitVarInsn(Opcodes.ALOAD, 4); |
| range0.fromInclusive = m.instructions.getLast(); |
| m.visitVarInsn(Opcodes.ALOAD, 3); |
| m.visitMethodInsn(Opcodes.INVOKESTATIC, "Fun", "$closeResource", |
| "(Ljava/lang/Throwable;Ljava/lang/AutoCloseable;)V", false); |
| range0.toInclusive = m.instructions.getLast(); |
| |
| // if (r0 != null) |
| m.visitVarInsn(Opcodes.ALOAD, 1); |
| range2.fromInclusive = m.instructions.getLast(); |
| final Label l11 = new Label(); |
| m.visitJumpInsn(Opcodes.IFNULL, l11); |
| // $closeResource(primaryExc0, r0) |
| m.visitVarInsn(Opcodes.ALOAD, 2); |
| m.visitVarInsn(Opcodes.ALOAD, 1); |
| m.visitMethodInsn(Opcodes.INVOKESTATIC, "Fun", "$closeResource", |
| "(Ljava/lang/Throwable;Ljava/lang/AutoCloseable;)V", false); |
| range2.toInclusive = m.instructions.getLast(); |
| m.visitLabel(l11); |
| |
| // finally |
| m.visitInsn(Opcodes.NOP); |
| m.visitVarInsn(Opcodes.ALOAD, 5); |
| m.visitInsn(Opcodes.ARETURN); |
| |
| // catch (Throwable t) |
| m.visitLabel(handler1); |
| range1.fromInclusive = m.instructions.getLast(); |
| m.visitVarInsn(Opcodes.ASTORE, 5); |
| // primaryExc1 = t |
| m.visitVarInsn(Opcodes.ALOAD, 5); |
| m.visitVarInsn(Opcodes.ASTORE, 4); |
| // throw t |
| m.visitVarInsn(Opcodes.ALOAD, 5); |
| m.visitInsn(Opcodes.ATHROW); |
| |
| // catch (any t) |
| m.visitVarInsn(Opcodes.ASTORE, 6); |
| // $closeResource(primaryExc1, r1) |
| m.visitVarInsn(Opcodes.ALOAD, 4); |
| m.visitVarInsn(Opcodes.ALOAD, 3); |
| m.visitMethodInsn(Opcodes.INVOKESTATIC, "Fun", "$closeResource", |
| "(Ljava/lang/Throwable;Ljava/lang/AutoCloseable;)V", false); |
| m.visitVarInsn(Opcodes.ALOAD, 6); |
| m.visitInsn(Opcodes.ATHROW); |
| range1.toInclusive = m.instructions.getLast(); |
| |
| // catch (Throwable t) |
| m.visitLabel(handler2); |
| range3.fromInclusive = m.instructions.getLast(); |
| m.visitVarInsn(Opcodes.ASTORE, 3); |
| // primaryExc0 = t |
| m.visitVarInsn(Opcodes.ALOAD, 3); |
| m.visitVarInsn(Opcodes.ASTORE, 2); |
| // throw t |
| m.visitVarInsn(Opcodes.ALOAD, 3); |
| m.visitInsn(Opcodes.ATHROW); |
| |
| // catch (any t) |
| m.visitVarInsn(Opcodes.ASTORE, 7); |
| // if (r0 != null) |
| m.visitVarInsn(Opcodes.ALOAD, 1); |
| final Label l14 = new Label(); |
| m.visitJumpInsn(Opcodes.IFNULL, l14); |
| // $closeResource(primaryExc0, r0) |
| m.visitVarInsn(Opcodes.ALOAD, 2); |
| m.visitVarInsn(Opcodes.ALOAD, 1); |
| m.visitMethodInsn(Opcodes.INVOKESTATIC, "Fun", "$closeResource", |
| "(Ljava/lang/Throwable;Ljava/lang/AutoCloseable;)V", false); |
| m.visitLabel(l14); |
| // throw t |
| m.visitVarInsn(Opcodes.ALOAD, 7); |
| m.visitInsn(Opcodes.ATHROW); |
| range3.toInclusive = m.instructions.getLast(); |
| |
| m.visitVarInsn(Opcodes.ASTORE, 8); |
| // finally |
| m.visitInsn(Opcodes.NOP); |
| m.visitVarInsn(Opcodes.ALOAD, 8); |
| m.visitInsn(Opcodes.ATHROW); |
| |
| filter.filter("Foo", "java/lang/Object", m, this); |
| |
| assertEquals(4, from.size()); |
| |
| assertEquals(range0.fromInclusive, from.get(0)); |
| assertEquals(range0.toInclusive, to.get(0)); |
| |
| assertEquals(range1.fromInclusive, from.get(1)); |
| assertEquals(range1.toInclusive, to.get(1)); |
| |
| assertEquals(range2.fromInclusive, from.get(2)); |
| assertEquals(range2.toInclusive, to.get(2)); |
| |
| assertEquals(range3.fromInclusive, from.get(3)); |
| assertEquals(range3.toInclusive, to.get(3)); |
| } |
| |
| /** |
| * javac 7 and 8 for |
| * |
| * <pre> |
| * try (r0 = ...; r1 = ...) { |
| * return ... |
| * } finally { |
| * ... |
| * } |
| * </pre> |
| * |
| * generate |
| * |
| * <pre> |
| * ... |
| * ASTORE r0 |
| * ACONST_NULL |
| * ASTORE primaryExc0 |
| * |
| * ... |
| * ASTORE r1 |
| * ACONST_NULL |
| * ASTORE primaryExc1 |
| * |
| * ... // body |
| * |
| * ALOAD r1 |
| * IFNULL n |
| * ALOAD primaryExc1 |
| * IFNULL c |
| * ALOAD r1 |
| * INVOKEINTERFACE close:()V |
| * GOTO n |
| * ASTORE t |
| * ALOAD primaryExc1 |
| * ALOAD t |
| * INVOKEVIRTUAL java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V |
| * GOTO n |
| * c: |
| * ALOAD r1 |
| * INVOKEINTERFACE close:()V |
| * n: |
| * |
| * ALOAD r0 |
| * IFNULL n |
| * ALOAD primaryExc0 |
| * IFNULL c |
| * ALOAD r0 |
| * INVOKEVIRTUAL close:()V |
| * GOTO n |
| * ASTORE t |
| * ALOAD primaryExc0 |
| * ALOAD t |
| * INVOKEVIRTUAL java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V |
| * GOTO n |
| * c: |
| * ALOAD r0 |
| * INVOKEVIRTUAL close:()V |
| * n: |
| * |
| * ... // finally on normal path |
| * ARETURN |
| * |
| * ASTORE t |
| * ALOAD t |
| * ASTORE primaryExc1 |
| * ALOAD t |
| * ATHROW |
| * |
| * ASTORE t1 |
| * ALOAD r1 |
| * IFNULL e |
| * ALOAD primaryExc1 |
| * IFNULL c |
| * ALOAD r1 |
| * INVOKEINTERFACE close:()V |
| * GOTO e |
| * ASTORE t2 |
| * ALOAD primaryExc1 |
| * ALOAD t2 |
| * INVOKEVIRTUAL java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V |
| * GOTO e |
| * c: |
| * ALOAD r1 |
| * INVOKEINTERFACE close:()V |
| * e: |
| * ALOAD t1 |
| * ATHROW |
| * |
| * ASTORE t |
| * ALOAD t |
| * ASTORE primaryExc0 |
| * ALOAD t |
| * ATHROW |
| * |
| * ASTORE t1 |
| * ALOAD r0 |
| * IFNULL e |
| * ALOAD primaryExc0 |
| * IFNULL c |
| * ALOAD r0 |
| * INVOKEVIRTUAL close:()V |
| * GOTO e |
| * ASTORE t2 |
| * ALOAD primaryExc0 |
| * ALOAD t2 |
| * INVOKEVIRTUAL java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V |
| * GOTO e |
| * c: |
| * ALOAD r0 |
| * INVOKEVIRTUAL close:()V |
| * e: |
| * ALOAD t1 |
| * ATHROW |
| * |
| * ... // additional handlers for catch blocks and finally on exceptional path |
| * </pre> |
| */ |
| @Test |
| public void javac_7_8() { |
| final Range range0 = new Range(); |
| final Range range1 = new Range(); |
| final Range range2 = new Range(); |
| final Range range3 = new Range(); |
| |
| final Label handler1 = new Label(); |
| m.visitTryCatchBlock(handler1, handler1, handler1, |
| "java/lang/Throwable"); |
| final Label handler2 = new Label(); |
| m.visitTryCatchBlock(handler2, handler2, handler2, |
| "java/lang/Throwable"); |
| |
| // r1 = ... |
| m.visitVarInsn(Opcodes.ASTORE, 1); |
| |
| // primaryExc1 = null |
| m.visitInsn(Opcodes.ACONST_NULL); |
| m.visitVarInsn(Opcodes.ASTORE, 2); |
| |
| // r2 = ... |
| m.visitVarInsn(Opcodes.ASTORE, 3); |
| // primaryExc2 = null |
| m.visitInsn(Opcodes.ACONST_NULL); |
| m.visitVarInsn(Opcodes.ASTORE, 4); |
| |
| // body |
| m.visitInsn(Opcodes.NOP); |
| |
| m.visitInsn(Opcodes.ACONST_NULL); |
| m.visitVarInsn(Opcodes.ASTORE, 5); |
| |
| final Label l15 = new Label(); |
| // if (r2 != null) |
| m.visitVarInsn(Opcodes.ALOAD, 3); |
| range0.fromInclusive = m.instructions.getLast(); |
| m.visitJumpInsn(Opcodes.IFNULL, l15); |
| // if (primaryExc2 != null) |
| m.visitVarInsn(Opcodes.ALOAD, 4); |
| final Label l26 = new Label(); |
| m.visitJumpInsn(Opcodes.IFNULL, l26); |
| // r2.close |
| m.visitVarInsn(Opcodes.ALOAD, 3); |
| m.visitMethodInsn(Opcodes.INVOKEINTERFACE, "Fun$Resource2", "close", |
| "()V", false); |
| m.visitJumpInsn(Opcodes.GOTO, l15); |
| |
| m.visitVarInsn(Opcodes.ASTORE, 6); |
| m.visitVarInsn(Opcodes.ALOAD, 4); |
| m.visitVarInsn(Opcodes.ALOAD, 6); |
| m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable", |
| "addSuppressed", "(Ljava/lang/Throwable;)V", false); |
| m.visitJumpInsn(Opcodes.GOTO, l15); |
| |
| m.visitLabel(l26); |
| |
| // r2.close |
| m.visitVarInsn(Opcodes.ALOAD, 3); |
| m.visitMethodInsn(Opcodes.INVOKEINTERFACE, "Fun$Resource2", "close", |
| "()V", false); |
| range0.toInclusive = m.instructions.getLast(); |
| m.visitLabel(l15); |
| |
| // if (r1 != null) |
| m.visitVarInsn(Opcodes.ALOAD, 1); |
| range2.fromInclusive = m.instructions.getLast(); |
| final Label l23 = new Label(); |
| m.visitJumpInsn(Opcodes.IFNULL, l23); |
| // if (primaryExc1 != null) |
| m.visitVarInsn(Opcodes.ALOAD, 2); |
| final Label l27 = new Label(); |
| m.visitJumpInsn(Opcodes.IFNULL, l27); |
| // r1.close |
| m.visitVarInsn(Opcodes.ALOAD, 1); |
| m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Fun$Resource1", "close", |
| "()V", false); |
| m.visitJumpInsn(Opcodes.GOTO, l23); |
| |
| m.visitVarInsn(Opcodes.ASTORE, 6); |
| m.visitVarInsn(Opcodes.ALOAD, 2); |
| m.visitVarInsn(Opcodes.ALOAD, 6); |
| m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable", |
| "addSuppressed", "(Ljava/lang/Throwable;)V", false); |
| m.visitJumpInsn(Opcodes.GOTO, l23); |
| |
| m.visitLabel(l27); |
| // r1.close |
| m.visitVarInsn(Opcodes.ALOAD, 1); |
| m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Fun$Resource1", "close", |
| "()V", false); |
| range2.toInclusive = m.instructions.getLast(); |
| m.visitLabel(l23); |
| |
| // finally |
| m.visitInsn(Opcodes.NOP); |
| m.visitInsn(Opcodes.ARETURN); |
| |
| // catch (Throwable t) |
| m.visitLabel(handler1); |
| range1.fromInclusive = m.instructions.getLast(); |
| m.visitVarInsn(Opcodes.ASTORE, 5); |
| // primaryExc2 = t |
| m.visitVarInsn(Opcodes.ALOAD, 5); |
| m.visitVarInsn(Opcodes.ASTORE, 4); |
| // throw t |
| m.visitVarInsn(Opcodes.ALOAD, 5); |
| m.visitInsn(Opcodes.ATHROW); |
| |
| // catch (any t) |
| m.visitVarInsn(Opcodes.ASTORE, 7); |
| // if (r2 != null) |
| m.visitVarInsn(Opcodes.ALOAD, 3); |
| final Label l28 = new Label(); |
| m.visitJumpInsn(Opcodes.IFNULL, l28); |
| // if (primaryExc2 != null) |
| m.visitVarInsn(Opcodes.ALOAD, 4); |
| final Label l29 = new Label(); |
| m.visitJumpInsn(Opcodes.IFNULL, l29); |
| // r2.close |
| m.visitVarInsn(Opcodes.ALOAD, 3); |
| m.visitMethodInsn(Opcodes.INVOKEINTERFACE, "Fun$Resource2", "close", |
| "()V", false); |
| m.visitJumpInsn(Opcodes.GOTO, l28); |
| |
| m.visitVarInsn(Opcodes.ASTORE, 8); |
| m.visitVarInsn(Opcodes.ALOAD, 4); |
| m.visitVarInsn(Opcodes.ALOAD, 8); |
| m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable", |
| "addSuppressed", "(Ljava/lang/Throwable;)V", false); |
| m.visitJumpInsn(Opcodes.GOTO, l28); |
| |
| m.visitLabel(l29); |
| // r2.close |
| m.visitVarInsn(Opcodes.ALOAD, 3); |
| m.visitMethodInsn(Opcodes.INVOKEINTERFACE, "Fun$Resource2", "close", |
| "()V", false); |
| m.visitLabel(l28); |
| // throw t |
| m.visitVarInsn(Opcodes.ALOAD, 7); |
| m.visitInsn(Opcodes.ATHROW); |
| range1.toInclusive = m.instructions.getLast(); |
| |
| // catch (Throwable t) |
| m.visitLabel(handler2); |
| range3.fromInclusive = m.instructions.getLast(); |
| m.visitVarInsn(Opcodes.ASTORE, 3); |
| // primaryExc2 = t |
| m.visitVarInsn(Opcodes.ALOAD, 3); |
| m.visitVarInsn(Opcodes.ASTORE, 2); |
| // throw t |
| m.visitVarInsn(Opcodes.ALOAD, 3); |
| m.visitInsn(Opcodes.ATHROW); |
| |
| // catch (any t) |
| m.visitVarInsn(Opcodes.ASTORE, 9); |
| // if (r1 != null) |
| m.visitVarInsn(Opcodes.ALOAD, 1); |
| final Label l30 = new Label(); |
| m.visitJumpInsn(Opcodes.IFNULL, l30); |
| // if (primaryExc1 != null) |
| m.visitVarInsn(Opcodes.ALOAD, 2); |
| final Label l31 = new Label(); |
| m.visitJumpInsn(Opcodes.IFNULL, l31); |
| // r1.close |
| m.visitVarInsn(Opcodes.ALOAD, 1); |
| m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Fun$Resource1", "close", |
| "()V", false); |
| m.visitJumpInsn(Opcodes.GOTO, l30); |
| |
| m.visitVarInsn(Opcodes.ASTORE, 10); |
| m.visitVarInsn(Opcodes.ALOAD, 2); |
| m.visitVarInsn(Opcodes.ALOAD, 10); |
| m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable", |
| "addSuppressed", "(Ljava/lang/Throwable;)V", false); |
| m.visitJumpInsn(Opcodes.GOTO, l30); |
| |
| m.visitLabel(l31); |
| // r1.close |
| m.visitVarInsn(Opcodes.ALOAD, 1); |
| m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Fun$Resource1", "close", |
| "()V", false); |
| m.visitLabel(l30); |
| // throw t |
| m.visitVarInsn(Opcodes.ALOAD, 9); |
| m.visitInsn(Opcodes.ATHROW); |
| range3.toInclusive = m.instructions.getLast(); |
| |
| m.visitVarInsn(Opcodes.ASTORE, 11); |
| // finally |
| m.visitInsn(Opcodes.NOP); |
| m.visitVarInsn(Opcodes.ALOAD, 11); |
| m.visitInsn(Opcodes.ATHROW); |
| |
| filter.filter("Foo", "java/lang/Object", m, this); |
| |
| assertEquals(4, from.size()); |
| |
| assertEquals(range0.fromInclusive, from.get(0)); |
| assertEquals(range0.toInclusive, to.get(0)); |
| |
| assertEquals(range1.fromInclusive, from.get(1)); |
| assertEquals(range1.toInclusive, to.get(1)); |
| |
| assertEquals(range2.fromInclusive, from.get(2)); |
| assertEquals(range2.toInclusive, to.get(2)); |
| |
| assertEquals(range3.fromInclusive, from.get(3)); |
| assertEquals(range3.toInclusive, to.get(3)); |
| } |
| |
| /** |
| * javac 9 for |
| * |
| * <pre> |
| * try (r = new ...) { |
| * ... |
| * } finally { |
| * ... |
| * } |
| * </pre> |
| * |
| * generates |
| * |
| * <pre> |
| * ... |
| * ASTORE r |
| * ACONST_NULL |
| * ASTORE primaryExc |
| * |
| * ... // body |
| * |
| * ALOAD primaryExc |
| * IFNULL c |
| * ALOAD r |
| * INVOKEVIRTUAL close:()V |
| * GOTO f |
| * ASTORE t |
| * ALOAD primaryExc |
| * ALOAD t |
| * NVOKEVIRTUAL java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V |
| * GOTO f |
| * c: |
| * ALOAD r |
| * INVOKEVIRTUAL close:()V |
| * GOTO f |
| * |
| * ASTORE t |
| * ALOAD t |
| * ASTORE primaryExc |
| * ALOAD t |
| * ATHROW |
| * |
| * ASTORE t |
| * ALOAD primaryExc |
| * IFNULL c |
| * ALOAD r |
| * INVOKEVIRTUAL close:()V |
| * GOTO L78 |
| * ASTORE t2 |
| * ALOAD primaryExc |
| * ALOAD t2 |
| * INVOKEVIRTUAL java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V |
| * goto e |
| * c: |
| * ALOAD r |
| * INVOKEVIRTUAL close:()V |
| * e: |
| * ALOAD t |
| * ATHROW |
| * |
| * f: |
| * ... // finally on normal path |
| * ... // additional handlers for catch blocks and finally on exceptional path |
| * ... |
| * </pre> |
| */ |
| @Test |
| public void javac9_omitted_null_check() { |
| final Range range0 = new Range(); |
| final Range range1 = new Range(); |
| |
| final Label handler = new Label(); |
| m.visitTryCatchBlock(handler, handler, handler, "java/lang/Throwable"); |
| |
| // primaryExc = null |
| m.visitInsn(Opcodes.ACONST_NULL); |
| m.visitVarInsn(Opcodes.ASTORE, 1); |
| |
| // r = new ... |
| m.visitInsn(Opcodes.NOP); |
| |
| final Label end = new Label(); |
| // "finally" on a normal path |
| { |
| // if (primaryExc != null) |
| m.visitVarInsn(Opcodes.ALOAD, 2); |
| range0.fromInclusive = m.instructions.getLast(); |
| final Label closeLabel = new Label(); |
| m.visitJumpInsn(Opcodes.IFNULL, closeLabel); |
| // r.close |
| m.visitVarInsn(Opcodes.ALOAD, 1); |
| m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Resource", "close", "()V", |
| false); |
| m.visitJumpInsn(Opcodes.GOTO, end); |
| |
| // catch (Throwable t) |
| m.visitVarInsn(Opcodes.ASTORE, 3); |
| // primaryExc.addSuppressed(t) |
| m.visitVarInsn(Opcodes.ALOAD, 2); |
| m.visitVarInsn(Opcodes.ALOAD, 3); |
| m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable", |
| "addSuppressed", "(Ljava/lang/Throwable;)V", false); |
| m.visitJumpInsn(Opcodes.GOTO, end); |
| |
| m.visitLabel(closeLabel); |
| // r.close() |
| m.visitVarInsn(Opcodes.ALOAD, 1); |
| m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Resource", "close", "()V", |
| false); |
| } |
| m.visitJumpInsn(Opcodes.GOTO, end); |
| range0.toInclusive = m.instructions.getLast(); |
| // catch (Throwable t) |
| m.visitLabel(handler); |
| { |
| range1.fromInclusive = m.instructions.getLast(); |
| m.visitVarInsn(Opcodes.ASTORE, 3); |
| // primaryExc = t |
| m.visitVarInsn(Opcodes.ALOAD, 3); |
| m.visitVarInsn(Opcodes.ASTORE, 2); |
| // throw t |
| m.visitVarInsn(Opcodes.ALOAD, 3); |
| m.visitInsn(Opcodes.ATHROW); |
| } |
| // catch (any t) |
| m.visitVarInsn(Opcodes.ASTORE, 4); |
| // "finally" on exceptional path |
| { |
| // if (primaryExc != null) |
| m.visitVarInsn(Opcodes.ALOAD, 2); |
| final Label closeLabel = new Label(); |
| m.visitJumpInsn(Opcodes.IFNULL, closeLabel); |
| m.visitVarInsn(Opcodes.ALOAD, 1); |
| m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Resource", "close", "()V", |
| false); |
| final Label finallyEndLabel = new Label(); |
| m.visitJumpInsn(Opcodes.GOTO, finallyEndLabel); |
| |
| // catch (Throwable t) |
| m.visitVarInsn(Opcodes.ASTORE, 5); |
| // primaryExc.addSuppressed(t) |
| m.visitVarInsn(Opcodes.ALOAD, 2); |
| m.visitVarInsn(Opcodes.ALOAD, 5); |
| m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable", |
| "addSuppressed", "(Ljava/lang/Throwable;)V", false); |
| m.visitJumpInsn(Opcodes.GOTO, finallyEndLabel); |
| |
| m.visitLabel(closeLabel); |
| // r.close() |
| m.visitVarInsn(Opcodes.ALOAD, 1); |
| m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Resource", "close", "()V", |
| false); |
| m.visitLabel(finallyEndLabel); |
| } |
| // throw t |
| m.visitVarInsn(Opcodes.ALOAD, 4); |
| m.visitInsn(Opcodes.ATHROW); |
| range1.toInclusive = m.instructions.getLast(); |
| |
| m.visitLabel(end); |
| |
| filter.filter("Foo", "java/lang/Object", m, this); |
| |
| assertEquals(2, from.size()); |
| |
| assertEquals(range0.fromInclusive, from.get(0)); |
| assertEquals(range0.toInclusive, to.get(0)); |
| |
| assertEquals(range1.fromInclusive, from.get(1)); |
| assertEquals(range1.toInclusive, to.get(1)); |
| } |
| |
| /** |
| * javac 9 for |
| * |
| * <pre> |
| * try (r = new ...) { |
| * throw ... |
| * } |
| * </pre> |
| * |
| * generates |
| * |
| * <pre> |
| * ... |
| * ASTORE r |
| * ACONST_NULL |
| * ASTORE primaryExc |
| * |
| * ... |
| * ATHROW |
| * |
| * ASTORE t |
| * ALOAD t |
| * ASTORE primaryExc |
| * ALOAD t |
| * ATHROW |
| * |
| * ASTORE t |
| * ALOAD primaryExc |
| * ALOAD r |
| * INVOKESTATIC $closeResource:(Ljava/lang/Throwable;Ljava/lang/AutoCloseable;)V |
| * ALOAD t |
| * ATHROW |
| * </pre> |
| */ |
| @Test |
| public void only_exceptional_path() { |
| final Label start = new Label(); |
| final Label handler = new Label(); |
| m.visitTryCatchBlock(start, handler, handler, "java/lang/Throwable"); |
| |
| m.visitLabel(start); |
| m.visitInsn(Opcodes.ATHROW); |
| m.visitLabel(handler); |
| m.visitVarInsn(Opcodes.ASTORE, 3); |
| m.visitVarInsn(Opcodes.ALOAD, 3); |
| m.visitVarInsn(Opcodes.ASTORE, 2); |
| m.visitVarInsn(Opcodes.ALOAD, 3); |
| m.visitInsn(Opcodes.ATHROW); |
| |
| m.visitVarInsn(Opcodes.ASTORE, 4); |
| m.visitVarInsn(Opcodes.ALOAD, 2); |
| m.visitVarInsn(Opcodes.ALOAD, 1); |
| m.visitMethodInsn(Opcodes.INVOKESTATIC, "Fun", "$closeResource", |
| "(Ljava/lang/Throwable;Ljava/lang/AutoCloseable;)V", false); |
| m.visitVarInsn(Opcodes.ALOAD, 4); |
| m.visitInsn(Opcodes.ATHROW); |
| |
| filter.filter("Foo", "java/lang/Object", m, this); |
| |
| assertEquals(0, from.size()); |
| } |
| |
| static class Range { |
| AbstractInsnNode fromInclusive; |
| AbstractInsnNode toInclusive; |
| } |
| |
| private final List<AbstractInsnNode> from = new ArrayList<AbstractInsnNode>(); |
| private final List<AbstractInsnNode> to = new ArrayList<AbstractInsnNode>(); |
| |
| public void ignore(AbstractInsnNode from, AbstractInsnNode to) { |
| this.from.add(from); |
| this.to.add(to); |
| } |
| |
| public void merge(final AbstractInsnNode i1, final AbstractInsnNode i2) { |
| fail(); |
| } |
| |
| } |