org.jacoco.core.instr.ClassFileVersionsTest.java Source code

Java tutorial

Introduction

Here is the source code for org.jacoco.core.instr.ClassFileVersionsTest.java

Source

/*******************************************************************************
 * Copyright (c) 2009, 2019 Mountainminds GmbH & Co. KG and Contributors
 * This program and the accompanying materials are made available under
 * the terms of the Eclipse Public License 2.0 which is available at
 * http://www.eclipse.org/legal/epl-2.0
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *    Marc R. Hoffmann - initial API and implementation
 *
 *******************************************************************************/
package org.jacoco.core.instr;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
import static org.objectweb.asm.Opcodes.ACC_SUPER;
import static org.objectweb.asm.Opcodes.ALOAD;
import static org.objectweb.asm.Opcodes.F_NEW;
import static org.objectweb.asm.Opcodes.IFEQ;
import static org.objectweb.asm.Opcodes.ILOAD;
import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
import static org.objectweb.asm.Opcodes.POP;
import static org.objectweb.asm.Opcodes.RETURN;
import static org.objectweb.asm.Opcodes.V10;
import static org.objectweb.asm.Opcodes.V11;
import static org.objectweb.asm.Opcodes.V12;
import static org.objectweb.asm.Opcodes.V13;
import static org.objectweb.asm.Opcodes.V1_1;
import static org.objectweb.asm.Opcodes.V1_2;
import static org.objectweb.asm.Opcodes.V1_3;
import static org.objectweb.asm.Opcodes.V1_4;
import static org.objectweb.asm.Opcodes.V1_5;
import static org.objectweb.asm.Opcodes.V1_6;
import static org.objectweb.asm.Opcodes.V1_7;
import static org.objectweb.asm.Opcodes.V1_8;
import static org.objectweb.asm.Opcodes.V9;

import java.io.IOException;

import org.jacoco.core.internal.instr.CondyProbeArrayStrategy;
import org.jacoco.core.internal.instr.InstrSupport;
import org.jacoco.core.runtime.IRuntime;
import org.jacoco.core.runtime.SystemPropertiesRuntime;
import org.junit.Test;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;

/**
 * Test class inserted stackmap frames for different class file versions.
 */
public class ClassFileVersionsTest {

    @Test
    public void test_1_1() throws IOException {
        testVersion(V1_1, false);
    }

    @Test
    public void test_1_2() throws IOException {
        testVersion(V1_2, false);
    }

    @Test
    public void test_1_3() throws IOException {
        testVersion(V1_3, false);
    }

    @Test
    public void test_1_4() throws IOException {
        testVersion(V1_4, false);
    }

    @Test
    public void test_1_5() throws IOException {
        testVersion(V1_5, false);
    }

    @Test
    public void test_1_6() throws IOException {
        testVersion(V1_6, true);
    }

    @Test
    public void test_1_7() throws IOException {
        testVersion(V1_7, true);
    }

    @Test
    public void test_1_8() throws IOException {
        testVersion(V1_8, true);
    }

    @Test
    public void test_9() throws IOException {
        testVersion(V9, true);
    }

    @Test
    public void test_10() throws IOException {
        testVersion(V10, true);
    }

    @Test
    public void test_11() throws IOException {
        testVersion(V11, true);
    }

    @Test
    public void test_12() throws IOException {
        testVersion(V12, true);
    }

    @Test
    public void test_13() throws IOException {
        testVersion(V13, true);
    }

    @Test
    public void test_14() throws IOException {
        testVersion(V13 + 1, true);
    }

    private void testVersion(int version, boolean frames) throws IOException {
        final byte[] original = createClass(version, frames);

        IRuntime runtime = new SystemPropertiesRuntime();
        Instrumenter instrumenter = new Instrumenter(runtime);
        byte[] instrumented = instrumenter.instrument(original, "TestTarget");

        assertFrames(instrumented, frames);
    }

    private void assertFrames(byte[] source, final boolean expected) {
        InstrSupport.classReaderFor(source).accept(new ClassVisitor(InstrSupport.ASM_API_VERSION) {

            @Override
            public MethodVisitor visitMethod(int access, String name, final String desc, String signature,
                    String[] exceptions) {
                return new MethodVisitor(InstrSupport.ASM_API_VERSION) {
                    boolean frames = false;

                    @Override
                    public void visitFrame(int type, int nLocal, Object[] local, int nStack, Object[] stack) {
                        frames = true;
                    }

                    @Override
                    public void visitEnd() {
                        if (CondyProbeArrayStrategy.B_DESC.equals(desc)) {
                            assertFalse("CondyProbeArrayStrategy does not need frames", frames);
                        } else {
                            assertEquals(Boolean.valueOf(expected), Boolean.valueOf(frames));
                        }
                    }
                };
            }
        }, 0);
    }

    /**
     * Creates a class that requires a frame before the return statement. Also
     * for this class instrumentation should insert another frame.
     *
     * <code><pre>
     * public class Sample {
     *   public Sample(boolean b){
     *     if(b){
     *       toString();
     *     }
     *     return;
     *   }
     * }
     * </pre></code>
     */
    private byte[] createClass(int version, boolean frames) {

        ClassWriter cw = new ClassWriter(0);
        MethodVisitor mv;

        cw.visit(version, ACC_PUBLIC + ACC_SUPER, "org/jacoco/test/Sample", null, "java/lang/Object", null);

        mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Z)V", null, null);
        mv.visitCode();
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
        mv.visitVarInsn(ILOAD, 1);
        Label l1 = new Label();
        mv.visitJumpInsn(IFEQ, l1);
        mv.visitVarInsn(ALOAD, 0);
        mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;", false);
        mv.visitInsn(POP);
        mv.visitLabel(l1);
        if (frames) {
            mv.visitFrame(F_NEW, 2, new Object[] { "org/jacoco/test/Sample", Opcodes.INTEGER }, 0, new Object[] {});
        }
        mv.visitInsn(RETURN);
        mv.visitMaxs(1, 2);
        mv.visitEnd();

        cw.visitEnd();

        return cw.toByteArray();
    }

}