aboutsummaryrefslogtreecommitdiff
path: root/org.jacoco.core.test/src/org/jacoco
diff options
context:
space:
mode:
authorEvgeny Mandrikov <Godin@users.noreply.github.com>2017-04-22 12:39:30 +0200
committerGitHub <noreply@github.com>2017-04-22 12:39:30 +0200
commite93053e8a54540f5394fe0938752f7421e2222ad (patch)
treed2c14bfed5730c09125531404c669b6a7e006375 /org.jacoco.core.test/src/org/jacoco
parent0e72710a54657200a17ade56501463294860baff (diff)
downloadjacoco-e93053e8a54540f5394fe0938752f7421e2222ad.tar.gz
Add filter for try-with-resources statement (#500)
Diffstat (limited to 'org.jacoco.core.test/src/org/jacoco')
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesEcjFilterTest.java624
-rw-r--r--org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesJavacFilterTest.java814
2 files changed, 1438 insertions, 0 deletions
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesEcjFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesEcjFilterTest.java
new file mode 100644
index 00000000..d3720505
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesEcjFilterTest.java
@@ -0,0 +1,624 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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 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 TryWithResourcesEcjFilterTest implements IFilterOutput {
+
+ private final TryWithResourcesEcjFilter filter = new TryWithResourcesEcjFilter();
+
+ private final MethodNode m = new MethodNode(InstrSupport.ASM_API_VERSION, 0,
+ "name", "()V", null, null);
+
+ /**
+ * ECJ for
+ *
+ * <pre>
+ * try (r0 = ...; r1 = ...; r2= ...) {
+ * ...
+ * } finally (...) {
+ * ...
+ * }
+ * ...
+ * </pre>
+ *
+ * generates
+ *
+ * <pre>
+ * ACONST_NULL
+ * ASTORE primaryExc
+ * ACONST_NULL
+ * ASTORE suppressedExc
+ * ...
+ * ASTORE r1
+ * ...
+ * ASTORE r2
+ * ...
+ * ASTORE r3
+ *
+ * ... // body
+ *
+ * ALOAD r3
+ * IFNULL r2_close
+ * ALOAD r3
+ * INVOKEVIRTUAL close:()V
+ * GOTO r2_close
+ *
+ * ASTORE primaryExc
+ * ALOAD r3
+ * IFNULL n
+ * ALOAD r3
+ * INVOKEVIRTUAL close:()V
+ * n:
+ * ALOAD primaryExc
+ * ATHROW
+ *
+ * r2_close:
+ * ALOAD r2
+ * IFNULL r1_close
+ * ALOAD r2
+ * INVOKEVIRTUAL close:()V
+ * GOTO r1_close
+ *
+ * ASTORE suppressedExc
+ * ALOAD primaryExc
+ * IFNONNULL s
+ * ALOAD suppressedExc
+ * ASTORE primaryExc
+ * GOTO e
+ * s:
+ * ALOAD primaryExc
+ * ALOAD suppressedExc
+ * IF_ACMPEQ e
+ * ALOAD primaryExc
+ * ALOAD suppressedExc
+ * INVOKEVIRTUAL java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
+ * e:
+ *
+ * ALOAD r2
+ * IFNULL n
+ * ALOAD r2
+ * INVOKEVIRTUAL close:()V
+ * n:
+ * ALOAD primaryExc
+ * ATHROW
+ *
+ * r1_close:
+ * ALOAD r1
+ * IFNULL after
+ * ALOAD r1
+ * INVOKEVIRTUAL close:()V
+ * GOTO after
+ *
+ * ASTORE suppressedExc
+ * ALOAD primaryExc
+ * IFNONNULL s
+ * ALOAD suppressedExc
+ * ASTORE primaryExc
+ * GOTO e
+ * s:
+ * ALOAD primaryExc
+ * ALOAD suppressedExc
+ * IF_ACMPEQ e
+ * ALOAD primaryExc
+ * ALOAD suppressedExc
+ * INVOKEVIRTUAL java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
+ * e:
+ *
+ * ALOAD r1
+ * IFNULL n
+ * ALOAD r1
+ * INVOKEVIRTUAL close:()V
+ * n:
+ * ALOAD primaryExc
+ * ATHROW
+ *
+ * ASTORE suppressedExc
+ * ALOAD primaryExc
+ * IFNONNULL s
+ * ALOAD suppressedExc
+ * ASTORE primaryExc
+ * GOTO e
+ * s:
+ * ALOAD primaryExc
+ * ALOAD suppressedExc
+ * IF_ACMPEQ e
+ * ALOAD primaryExc
+ * ALOAD suppressedExc
+ * INVOKEVIRTUAL java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
+ * e:
+ *
+ * ALOAD primaryExc
+ * ATHROW
+ *
+ * ... // additional handlers for catch blocks and finally on exceptional path
+ *
+ * after:
+ * ... // finally on normal path
+ * ...
+ * </pre>
+ */
+ @Test
+ public void ecj() {
+ final Range range0 = new Range();
+ final Range range1 = new Range();
+
+ final Label handler = new Label();
+ m.visitTryCatchBlock(handler, handler, handler, null);
+
+ // primaryExc = null
+ m.visitInsn(Opcodes.ACONST_NULL);
+ m.visitVarInsn(Opcodes.ASTORE, 1);
+ // suppressedExc = null
+ m.visitInsn(Opcodes.ACONST_NULL);
+ m.visitVarInsn(Opcodes.ASTORE, 2);
+
+ // body
+ m.visitInsn(Opcodes.NOP);
+
+ final Label l4 = new Label();
+ final Label l7 = new Label();
+ final Label end = new Label();
+ { // nextIsEcjClose("r0")
+ m.visitVarInsn(Opcodes.ALOAD, 5);
+ range0.fromInclusive = m.instructions.getLast();
+ m.visitJumpInsn(Opcodes.IFNULL, l4);
+ m.visitVarInsn(Opcodes.ALOAD, 5);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Fun2$Resource", "close",
+ "()V", false);
+ }
+ m.visitJumpInsn(Opcodes.GOTO, l4);
+ range0.toInclusive = m.instructions.getLast();
+ // catch (any primaryExc)
+ m.visitLabel(handler);
+ range1.fromInclusive = m.instructions.getLast();
+ m.visitVarInsn(Opcodes.ASTORE, 1);
+ { // nextIsEcjCloseAndThrow("r0")
+ m.visitVarInsn(Opcodes.ALOAD, 5);
+ Label l11 = new Label();
+ m.visitJumpInsn(Opcodes.IFNULL, l11);
+ m.visitVarInsn(Opcodes.ALOAD, 5);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Fun2$Resource", "close",
+ "()V", false);
+ m.visitLabel(l11);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitInsn(Opcodes.ATHROW);
+ }
+ m.visitLabel(l4);
+ { // nextIsEcjClose("r1")
+ m.visitVarInsn(Opcodes.ALOAD, 4);
+ m.visitJumpInsn(Opcodes.IFNULL, l7);
+ m.visitVarInsn(Opcodes.ALOAD, 4);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Fun2$Resource", "close",
+ "()V", false);
+ }
+ m.visitJumpInsn(Opcodes.GOTO, l7);
+ { // nextIsEcjSuppress
+ m.visitVarInsn(Opcodes.ASTORE, 2);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ final Label suppressStart = new Label();
+ m.visitJumpInsn(Opcodes.IFNONNULL, suppressStart);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitVarInsn(Opcodes.ASTORE, 1);
+ final Label suppressEnd = new Label();
+ m.visitJumpInsn(Opcodes.GOTO, suppressEnd);
+ m.visitLabel(suppressStart);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitJumpInsn(Opcodes.IF_ACMPEQ, suppressEnd);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable",
+ "addSuppressed", "(Ljava/lang/Throwable;)V", false);
+ m.visitLabel(suppressEnd);
+ }
+ { // nextIsEcjCloseAndThrow("r1")
+ m.visitVarInsn(Opcodes.ALOAD, 4);
+ final Label l14 = new Label();
+ m.visitJumpInsn(Opcodes.IFNULL, l14);
+ m.visitVarInsn(Opcodes.ALOAD, 4);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Fun2$Resource", "close",
+ "()V", false);
+ m.visitLabel(l14);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitInsn(Opcodes.ATHROW);
+ }
+ m.visitLabel(l7);
+ { // nextIsEcjClose("r2")
+ m.visitVarInsn(Opcodes.ALOAD, 3);
+ m.visitJumpInsn(Opcodes.IFNULL, end);
+ m.visitVarInsn(Opcodes.ALOAD, 3);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Fun2$Resource", "close",
+ "()V", false);
+ m.visitJumpInsn(Opcodes.GOTO, end);
+ }
+ { // nextIsEcjSuppress
+ m.visitVarInsn(Opcodes.ASTORE, 2);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ final Label suppressStart = new Label();
+ m.visitJumpInsn(Opcodes.IFNONNULL, suppressStart);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitVarInsn(Opcodes.ASTORE, 1);
+ final Label suppressEnd = new Label();
+ m.visitJumpInsn(Opcodes.GOTO, suppressEnd);
+ m.visitLabel(suppressStart);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitJumpInsn(Opcodes.IF_ACMPEQ, suppressEnd);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable",
+ "addSuppressed", "(Ljava/lang/Throwable;)V", false);
+ m.visitLabel(suppressEnd);
+ }
+ { // nextIsEcjCloseAndThrow("r2")
+ m.visitVarInsn(Opcodes.ALOAD, 3);
+ final Label l18 = new Label();
+ m.visitJumpInsn(Opcodes.IFNULL, l18);
+ m.visitVarInsn(Opcodes.ALOAD, 3);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Fun2$Resource", "close",
+ "()V", false);
+ m.visitLabel(l18);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitInsn(Opcodes.ATHROW);
+ }
+ { // nextIsEcjSuppress
+ m.visitVarInsn(Opcodes.ASTORE, 2);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ final Label suppressStart = new Label();
+ m.visitJumpInsn(Opcodes.IFNONNULL, suppressStart);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitVarInsn(Opcodes.ASTORE, 1);
+ final Label suppressEnd = new Label();
+ m.visitJumpInsn(Opcodes.GOTO, suppressEnd);
+ m.visitLabel(suppressStart);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitJumpInsn(Opcodes.IF_ACMPEQ, suppressEnd);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable",
+ "addSuppressed", "(Ljava/lang/Throwable;)V", false);
+ m.visitLabel(suppressEnd);
+ }
+ // throw primaryExc
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitInsn(Opcodes.ATHROW);
+ range1.toInclusive = m.instructions.getLast();
+
+ // additional handlers
+ m.visitInsn(Opcodes.NOP);
+
+ 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));
+ }
+
+ /**
+ * ECJ for
+ *
+ * <pre>
+ * try (r1 = ...; r2 = ...; r3 = ...) {
+ * return ...
+ * } finally {
+ * ...
+ * }
+ * </pre>
+ *
+ * generates
+ *
+ * <pre>
+ * ACONST_NULL
+ * astore primaryExc
+ * ACONST_NULL
+ * astore suppressedExc
+ *
+ * ...
+ * ASTORE r1
+ * ...
+ * ASTORE r2
+ * ...
+ * ASTORE r3
+ *
+ * ... // body
+ *
+ * ALOAD r3
+ * IFNULL n
+ * ALOAD r3
+ * INVOKEVIRTUAL close:()V
+ * n:
+ * ALOAD r2
+ * IFNULL n
+ * ALOAD r2
+ * INVOKEVIRTUAL close:()V
+ * n:
+ * ALOAD r1
+ * IFNULL n
+ * ALOAD r1
+ * INVOKEVIRTUAL close:()V
+ * n:
+ *
+ * ... // finally on normal path
+ * ARETURN
+ *
+ * ASTORE primaryExc
+ * ALOAD r3
+ * IFNULL n
+ * ALOAD r3
+ * INVOKEVIRTUAL close:()V
+ * n:
+ * ALOAD primaryExc
+ * ATHROW
+ *
+ * ASTORE suppressedExc
+ * ALOAD primaryExc
+ * IFNONNULL s
+ * ALOAD suppressedExc
+ * ASTORE primaryExc
+ * GOTO e
+ * s:
+ * ALOAD primaryExc
+ * ALOAD suppressedExc
+ * IF_ACMPEQ e
+ * ALOAD primaryExc
+ * ALOAD suppressedExc
+ * INVOKEVIRTUAL java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
+ * e:
+ *
+ * ALOAD r2
+ * IFNULL n
+ * ALOAD r2
+ * INVOKEVIRTUAL close:()V
+ * n:
+ * ALOAD primaryExc
+ * ATHROW
+ *
+ * ASTORE suppressedExc
+ * ALOAD primaryExc
+ * IFNONNULL s
+ * ALOAD suppressedExc
+ * ASTORE primaryExc
+ * GOTO e
+ * s:
+ * ALOAD primaryExc
+ * ALOAD suppressedExc
+ * IF_ACMPEQ e
+ * ALOAD primaryExc
+ * ALOAD suppressedExc
+ * INVOKEVIRTUAL java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
+ * e:
+ *
+ * ALOAD r1
+ * IFNULL n
+ * ALOAD r1
+ * INVOKEVIRTUAL close:()V
+ * n:
+ * ALOAD primaryExc
+ * ATHROW
+ *
+ * ASTORE suppressedExc
+ * ALOAD primaryExc
+ * IFNONNULL s
+ * ALOAD suppressedExc
+ * ASTORE primaryExc
+ * GOTO e
+ * s:
+ * ALOAD primaryExc
+ * ALOAD suppressedExc
+ * IF_ACMPEQ e
+ * ALOAD primaryExc
+ * ALOAD suppressedExc
+ * INVOKEVIRTUAL java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;)V
+ * e:
+ *
+ * ALOAD primaryExc
+ * ATHROW
+ *
+ * ... // additional handlers for catch blocks and finally on exceptional path
+ * </pre>
+ */
+ @Test
+ public void ecj_noFlowOut() {
+ final Range range0 = new Range();
+ final Range range1 = new Range();
+
+ final Label handler = new Label();
+ m.visitTryCatchBlock(handler, handler, handler, null);
+
+ // primaryExc = null
+ m.visitInsn(Opcodes.ACONST_NULL);
+ m.visitVarInsn(Opcodes.ASTORE, 1);
+ // suppressedExc = null
+ m.visitInsn(Opcodes.ACONST_NULL);
+ m.visitVarInsn(Opcodes.ASTORE, 2);
+
+ // body
+ m.visitInsn(Opcodes.NOP);
+
+ { // nextIsEcjClose("r0")
+ final Label label = new Label();
+ m.visitVarInsn(Opcodes.ALOAD, 5);
+ range0.fromInclusive = m.instructions.getLast();
+ m.visitJumpInsn(Opcodes.IFNULL, label);
+ m.visitVarInsn(Opcodes.ALOAD, 5);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Fun$Resource", "close",
+ "()V", false);
+ m.visitLabel(label);
+ }
+ { // nextIsEcjClose("r1")
+ final Label label = new Label();
+ m.visitVarInsn(Opcodes.ALOAD, 4);
+ m.visitJumpInsn(Opcodes.IFNULL, label);
+ m.visitVarInsn(Opcodes.ALOAD, 4);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Fun$Resource", "close",
+ "()V", false);
+ m.visitLabel(label);
+ }
+ { // nextIsEcjClose("r2")
+ final Label label = new Label();
+ m.visitVarInsn(Opcodes.ALOAD, 3);
+ m.visitJumpInsn(Opcodes.IFNULL, label);
+ m.visitVarInsn(Opcodes.ALOAD, 3);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Fun$Resource", "close",
+ "()V", false);
+ range0.toInclusive = m.instructions.getLast();
+ m.visitLabel(label);
+ }
+
+ // finally
+ m.visitInsn(Opcodes.NOP);
+ m.visitInsn(Opcodes.ARETURN);
+
+ // catch (any primaryExc)
+ m.visitLabel(handler);
+ range1.fromInclusive = m.instructions.getLast();
+ m.visitVarInsn(Opcodes.ASTORE, 1);
+ { // nextIsEcjCloseAndThrow("r0")
+ m.visitVarInsn(Opcodes.ALOAD, 5);
+ final Label throwLabel = new Label();
+ m.visitJumpInsn(Opcodes.IFNULL, throwLabel);
+ m.visitVarInsn(Opcodes.ALOAD, 5);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Fun$Resource", "close",
+ "()V", false);
+ m.visitLabel(throwLabel);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitInsn(Opcodes.ATHROW);
+ }
+ { // nextIsEcjSuppress
+ m.visitVarInsn(Opcodes.ASTORE, 2);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ final Label suppressStart = new Label();
+ m.visitJumpInsn(Opcodes.IFNONNULL, suppressStart);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitVarInsn(Opcodes.ASTORE, 1);
+ final Label suppressEnd = new Label();
+ m.visitJumpInsn(Opcodes.GOTO, suppressEnd);
+ m.visitLabel(suppressStart);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitJumpInsn(Opcodes.IF_ACMPEQ, suppressEnd);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable",
+ "addSuppressed", "(Ljava/lang/Throwable;)V", false);
+ m.visitLabel(suppressEnd);
+ }
+ { // nextIsEcjCloseAndThrow("r1")
+ m.visitVarInsn(Opcodes.ALOAD, 4);
+ final Label throwLabel = new Label();
+ m.visitJumpInsn(Opcodes.IFNULL, throwLabel);
+ m.visitVarInsn(Opcodes.ALOAD, 4);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Fun$Resource", "close",
+ "()V", false);
+ m.visitLabel(throwLabel);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitInsn(Opcodes.ATHROW);
+ }
+ { // nextIsEcjSuppress
+ m.visitVarInsn(Opcodes.ASTORE, 2);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ final Label suppressStart = new Label();
+ m.visitJumpInsn(Opcodes.IFNONNULL, suppressStart);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitVarInsn(Opcodes.ASTORE, 1);
+ final Label suppressEnd = new Label();
+ m.visitJumpInsn(Opcodes.GOTO, suppressEnd);
+ m.visitLabel(suppressStart);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitJumpInsn(Opcodes.IF_ACMPEQ, suppressEnd);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable",
+ "addSuppressed", "(Ljava/lang/Throwable;)V", false);
+ m.visitLabel(suppressEnd);
+ }
+ { // nextIsEcjCloseAndThrow("r2")
+ m.visitVarInsn(Opcodes.ALOAD, 3);
+ final Label throwLabel = new Label();
+ m.visitJumpInsn(Opcodes.IFNULL, throwLabel);
+ m.visitVarInsn(Opcodes.ALOAD, 3);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "Fun$Resource", "close",
+ "()V", false);
+ m.visitLabel(throwLabel);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitInsn(Opcodes.ATHROW);
+ }
+ { // nextIsEcjSuppress
+ m.visitVarInsn(Opcodes.ASTORE, 2);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ final Label suppressStart = new Label();
+ m.visitJumpInsn(Opcodes.IFNONNULL, suppressStart);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitVarInsn(Opcodes.ASTORE, 1);
+ final Label suppressEnd = new Label();
+ m.visitJumpInsn(Opcodes.GOTO, suppressEnd);
+ m.visitLabel(suppressStart);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitJumpInsn(Opcodes.IF_ACMPEQ, suppressEnd);
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitVarInsn(Opcodes.ALOAD, 2);
+ m.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Throwable",
+ "addSuppressed", "(Ljava/lang/Throwable;)V", false);
+ m.visitLabel(suppressEnd);
+ }
+ // throw primaryExc
+ m.visitVarInsn(Opcodes.ALOAD, 1);
+ m.visitInsn(Opcodes.ATHROW);
+ range1.toInclusive = m.instructions.getLast();
+
+ // additional handlers
+ m.visitInsn(Opcodes.NOP);
+
+ 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));
+ }
+
+ 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);
+ }
+
+}
diff --git a/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesJavacFilterTest.java b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesJavacFilterTest.java
new file mode 100644
index 00000000..26b4cfe1
--- /dev/null
+++ b/org.jacoco.core.test/src/org/jacoco/core/internal/analysis/filter/TryWithResourcesJavacFilterTest.java
@@ -0,0 +1,814 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2017 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 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);
+ }
+
+}