Java tutorial
/* * Copyright 2014 Artem Khvastunov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and limitations under the License. */ package org.pitest.mutationtest.engine.gregor; import static org.objectweb.asm.Opcodes.ALOAD; import static org.objectweb.asm.Opcodes.ASTORE; import static org.objectweb.asm.Opcodes.ATHROW; import static org.objectweb.asm.Opcodes.GOTO; import static org.objectweb.asm.Opcodes.IFNONNULL; import static org.objectweb.asm.Opcodes.IFNULL; import static org.objectweb.asm.Opcodes.IF_ACMPEQ; import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; import static org.objectweb.asm.Opcodes.INVOKEINTERFACE; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; /** * Example of code that was generated by 1.7 compiler for try-with-resources block: * <pre><code> * } finally { * if (closeable != null) { // IFNULL * if (localThrowable2 != null) { // IFNULL * try { * closeable.close(); // INVOKEVIRTUAL or INVOKEINTERFACE * } catch (Throwable x2) { * localThrowable2.addSuppressed(x2); // INVOKEVIRTUAL * } * } else { * closeable.close(); // INVOKEVIRTUAL or INVOKEINTERFACE * } * } * } // ATHROW * </code></pre> * This class considers that only auto generated code may have such sequence without any line change. * Such an approach make sense only for <strong>javac compiler</strong>. * <p> * <strong>Eclipse Java Compiler</strong> as well as <strong>aspectj</strong> have its own opinion * how to compile try-with-resources block: * <pre><code> * } finally { * if (throwable1 == null) { // IFNONNULL * throwable1 = throwable2; * } else { * if (throwable1 != throwable2) { // IF_ACMPEQ * throwable1.addSuppressed(throwable2); // INVOKEVIRTUAL * } * } * } // ATHROW * </code></pre> * * @author Artem Khvastunov <contact@artspb.me> */ class TryWithResourcesMethodVisitor extends MethodVisitor { private static final List<Integer> JAVAC_CLASS_INS_SEQUENCE = Arrays.asList(ASTORE, // store throwable ALOAD, IFNULL, // closeable != null ALOAD, IFNULL, // localThrowable2 != null ALOAD, INVOKEVIRTUAL, GOTO, // closeable.close() ASTORE, // Throwable x2 ALOAD, ALOAD, INVOKEVIRTUAL, GOTO, // localThrowable2.addSuppressed(x2) ALOAD, INVOKEVIRTUAL, // closeable.close() ALOAD, ATHROW); // throw throwable private static final List<Integer> JAVAC_INTERFACE_INS_SEQUENCE = Arrays.asList(ASTORE, // store throwable ALOAD, IFNULL, // closeable != null ALOAD, IFNULL, // localThrowable2 != null ALOAD, INVOKEINTERFACE, GOTO, // closeable.close() ASTORE, // Throwable x2 ALOAD, ALOAD, INVOKEVIRTUAL, GOTO, // localThrowable2.addSuppressed(x2) ALOAD, INVOKEINTERFACE, // closeable.close() ALOAD, ATHROW); // throw throwable private static final List<Integer> ECJ_INS_SEQUENCE = Arrays.asList(ASTORE, // store throwable2 ALOAD, IFNONNULL, // if (throwable1 == null) ALOAD, ASTORE, GOTO, // throwable1 = throwable2; ALOAD, ALOAD, IF_ACMPEQ, // if (throwable1 != throwable2) { ALOAD, ALOAD, INVOKEVIRTUAL, // throwable1.addSuppressed(throwable2) ALOAD, ATHROW); // throw throwable1 private final PremutationClassInfo context; private final List<Integer> opcodesStack = new ArrayList<Integer>(); private int currentLineNumber; /** * @param context * to store detected line numbers */ public TryWithResourcesMethodVisitor(final PremutationClassInfo context) { super(Opcodes.ASM5); this.context = context; } @Override public void visitLineNumber(int line, Label start) { prepareToStartTracking(); currentLineNumber = line; super.visitLineNumber(line, start); } @Override public void visitVarInsn(int opcode, int var) { opcodesStack.add(opcode); super.visitVarInsn(opcode, var); } @Override public void visitJumpInsn(int opcode, Label label) { opcodesStack.add(opcode); super.visitJumpInsn(opcode, label); } @Override public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { opcodesStack.add(opcode); super.visitMethodInsn(opcode, owner, name, desc, itf); } @Override public void visitInsn(int opcode) { if (opcode == Opcodes.ATHROW) { opcodesStack.add(opcode); finishTracking(); } super.visitInsn(opcode); } private void finishTracking() { if (JAVAC_CLASS_INS_SEQUENCE.equals(opcodesStack) || JAVAC_INTERFACE_INS_SEQUENCE.equals(opcodesStack) || ECJ_INS_SEQUENCE.equals(opcodesStack)) { context.registerLineToAvoid(currentLineNumber); } prepareToStartTracking(); } private void prepareToStartTracking() { if (!opcodesStack.isEmpty()) { opcodesStack.clear(); } } }