| /******************************************************************************* |
| * 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 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 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); |
| } |
| |
| public void merge(final AbstractInsnNode i1, final AbstractInsnNode i2) { |
| fail(); |
| } |
| |
| } |