1 /******************************************************************************* 2 * Copyright (c) 2009, 2021 Mountainminds GmbH & Co. KG and Contributors 3 * This program and the accompanying materials are made available under 4 * the terms of the Eclipse Public License 2.0 which is available at 5 * http://www.eclipse.org/legal/epl-2.0 6 * 7 * SPDX-License-Identifier: EPL-2.0 8 * 9 * Contributors: 10 * Marc R. Hoffmann - initial API and implementation 11 * 12 *******************************************************************************/ 13 package org.jacoco.core.instr; 14 15 import static org.junit.Assert.assertEquals; 16 import static org.junit.Assert.assertFalse; 17 import static org.objectweb.asm.Opcodes.ACC_PUBLIC; 18 import static org.objectweb.asm.Opcodes.ACC_SUPER; 19 import static org.objectweb.asm.Opcodes.ALOAD; 20 import static org.objectweb.asm.Opcodes.F_NEW; 21 import static org.objectweb.asm.Opcodes.IFEQ; 22 import static org.objectweb.asm.Opcodes.ILOAD; 23 import static org.objectweb.asm.Opcodes.INVOKESPECIAL; 24 import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL; 25 import static org.objectweb.asm.Opcodes.POP; 26 import static org.objectweb.asm.Opcodes.RETURN; 27 import static org.objectweb.asm.Opcodes.V10; 28 import static org.objectweb.asm.Opcodes.V11; 29 import static org.objectweb.asm.Opcodes.V12; 30 import static org.objectweb.asm.Opcodes.V13; 31 import static org.objectweb.asm.Opcodes.V14; 32 import static org.objectweb.asm.Opcodes.V15; 33 import static org.objectweb.asm.Opcodes.V16; 34 import static org.objectweb.asm.Opcodes.V1_1; 35 import static org.objectweb.asm.Opcodes.V1_2; 36 import static org.objectweb.asm.Opcodes.V1_3; 37 import static org.objectweb.asm.Opcodes.V1_4; 38 import static org.objectweb.asm.Opcodes.V1_5; 39 import static org.objectweb.asm.Opcodes.V1_6; 40 import static org.objectweb.asm.Opcodes.V1_7; 41 import static org.objectweb.asm.Opcodes.V1_8; 42 import static org.objectweb.asm.Opcodes.V9; 43 44 import java.io.IOException; 45 46 import org.jacoco.core.internal.instr.CondyProbeArrayStrategy; 47 import org.jacoco.core.internal.instr.InstrSupport; 48 import org.jacoco.core.runtime.IRuntime; 49 import org.jacoco.core.runtime.SystemPropertiesRuntime; 50 import org.junit.Test; 51 import org.objectweb.asm.ClassVisitor; 52 import org.objectweb.asm.ClassWriter; 53 import org.objectweb.asm.Label; 54 import org.objectweb.asm.MethodVisitor; 55 import org.objectweb.asm.Opcodes; 56 57 /** 58 * Test class inserted stackmap frames for different class file versions. 59 */ 60 public class ClassFileVersionsTest { 61 62 @Test test_1_1()63 public void test_1_1() throws IOException { 64 testVersion(V1_1, false); 65 } 66 67 @Test test_1_2()68 public void test_1_2() throws IOException { 69 testVersion(V1_2, false); 70 } 71 72 @Test test_1_3()73 public void test_1_3() throws IOException { 74 testVersion(V1_3, false); 75 } 76 77 @Test test_1_4()78 public void test_1_4() throws IOException { 79 testVersion(V1_4, false); 80 } 81 82 @Test test_1_5()83 public void test_1_5() throws IOException { 84 testVersion(V1_5, false); 85 } 86 87 @Test test_1_6()88 public void test_1_6() throws IOException { 89 testVersion(V1_6, true); 90 } 91 92 @Test test_1_7()93 public void test_1_7() throws IOException { 94 testVersion(V1_7, true); 95 } 96 97 @Test test_1_8()98 public void test_1_8() throws IOException { 99 testVersion(V1_8, true); 100 } 101 102 @Test test_9()103 public void test_9() throws IOException { 104 testVersion(V9, true); 105 } 106 107 @Test test_10()108 public void test_10() throws IOException { 109 testVersion(V10, true); 110 } 111 112 @Test test_11()113 public void test_11() throws IOException { 114 testVersion(V11, true); 115 } 116 117 @Test test_12()118 public void test_12() throws IOException { 119 testVersion(V12, true); 120 } 121 122 @Test test_13()123 public void test_13() throws IOException { 124 testVersion(V13, true); 125 } 126 127 @Test test_14()128 public void test_14() throws IOException { 129 testVersion(V14, true); 130 } 131 132 @Test test_15()133 public void test_15() throws IOException { 134 testVersion(V15, true); 135 } 136 137 @Test test_16()138 public void test_16() throws IOException { 139 testVersion(V16, true); 140 } 141 testVersion(int version, boolean frames)142 private void testVersion(int version, boolean frames) throws IOException { 143 final byte[] original = createClass(version, frames); 144 145 IRuntime runtime = new SystemPropertiesRuntime(); 146 Instrumenter instrumenter = new Instrumenter(runtime); 147 byte[] instrumented = instrumenter.instrument(original, "TestTarget"); 148 149 assertFrames(instrumented, frames); 150 } 151 assertFrames(byte[] source, final boolean expected)152 private void assertFrames(byte[] source, final boolean expected) { 153 InstrSupport.classReaderFor(source) 154 .accept(new ClassVisitor(InstrSupport.ASM_API_VERSION) { 155 156 @Override 157 public MethodVisitor visitMethod(int access, String name, 158 final String desc, String signature, 159 String[] exceptions) { 160 return new MethodVisitor(InstrSupport.ASM_API_VERSION) { 161 boolean frames = false; 162 163 @Override 164 public void visitFrame(int type, int nLocal, 165 Object[] local, int nStack, 166 Object[] stack) { 167 frames = true; 168 } 169 170 @Override 171 public void visitEnd() { 172 if (CondyProbeArrayStrategy.B_DESC 173 .equals(desc)) { 174 assertFalse( 175 "CondyProbeArrayStrategy does not need frames", 176 frames); 177 } else { 178 assertEquals(Boolean.valueOf(expected), 179 Boolean.valueOf(frames)); 180 } 181 } 182 }; 183 } 184 }, 0); 185 } 186 187 /** 188 * Creates a class that requires a frame before the return statement. Also 189 * for this class instrumentation should insert another frame. 190 * 191 * <code><pre> 192 * public class Sample { 193 * public Sample(boolean b){ 194 * if(b){ 195 * toString(); 196 * } 197 * return; 198 * } 199 * } 200 * </pre></code> 201 */ createClass(int version, boolean frames)202 private byte[] createClass(int version, boolean frames) { 203 204 ClassWriter cw = new ClassWriter(0); 205 MethodVisitor mv; 206 207 cw.visit(version, ACC_PUBLIC + ACC_SUPER, "org/jacoco/test/Sample", 208 null, "java/lang/Object", null); 209 210 mv = cw.visitMethod(ACC_PUBLIC, "<init>", "(Z)V", null, null); 211 mv.visitCode(); 212 mv.visitVarInsn(ALOAD, 0); 213 mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", 214 false); 215 mv.visitVarInsn(ILOAD, 1); 216 Label l1 = new Label(); 217 mv.visitJumpInsn(IFEQ, l1); 218 mv.visitVarInsn(ALOAD, 0); 219 mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Object", "toString", 220 "()Ljava/lang/String;", false); 221 mv.visitInsn(POP); 222 mv.visitLabel(l1); 223 if (frames) { 224 mv.visitFrame(F_NEW, 2, 225 new Object[] { "org/jacoco/test/Sample", Opcodes.INTEGER }, 226 0, new Object[] {}); 227 } 228 mv.visitInsn(RETURN); 229 mv.visitMaxs(1, 2); 230 mv.visitEnd(); 231 232 cw.visitEnd(); 233 234 return cw.toByteArray(); 235 } 236 237 } 238