1/*******************************************************************************
2 * Copyright (c) 2009, 2018 Mountainminds GmbH & Co. KG and Contributors
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 *    Marc R. Hoffmann - initial API and implementation
10 *
11 *******************************************************************************/
12package org.jacoco.core.test.validation;
13
14import static org.junit.Assert.assertEquals;
15import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
16import static org.objectweb.asm.Opcodes.ACC_SUPER;
17import static org.objectweb.asm.Opcodes.ALOAD;
18import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
19import static org.objectweb.asm.Opcodes.RETURN;
20import static org.objectweb.asm.Opcodes.V1_1;
21import static org.objectweb.asm.Opcodes.V1_2;
22import static org.objectweb.asm.Opcodes.V1_3;
23import static org.objectweb.asm.Opcodes.V1_4;
24import static org.objectweb.asm.Opcodes.V1_5;
25import static org.objectweb.asm.Opcodes.V1_6;
26import static org.objectweb.asm.Opcodes.V1_7;
27import static org.objectweb.asm.Opcodes.V1_8;
28import static org.objectweb.asm.Opcodes.V9;
29
30import java.io.IOException;
31
32import org.jacoco.core.instr.Instrumenter;
33import org.jacoco.core.internal.instr.InstrSupport;
34import org.jacoco.core.runtime.IRuntime;
35import org.jacoco.core.runtime.SystemPropertiesRuntime;
36import org.junit.Test;
37import org.objectweb.asm.ClassReader;
38import org.objectweb.asm.ClassVisitor;
39import org.objectweb.asm.ClassWriter;
40import org.objectweb.asm.MethodVisitor;
41
42/**
43 * Test class inserted stackmap frames for different class file versions.
44 */
45public class ClassFileVersionsTest {
46
47	@Test
48	public void test_1_1() throws IOException {
49		testVersion(V1_1, false);
50	}
51
52	@Test
53	public void test_1_2() throws IOException {
54		testVersion(V1_2, false);
55	}
56
57	@Test
58	public void test_1_3() throws IOException {
59		testVersion(V1_3, false);
60	}
61
62	@Test
63	public void test_1_4() throws IOException {
64		testVersion(V1_4, false);
65	}
66
67	@Test
68	public void test_1_5() throws IOException {
69		testVersion(V1_5, false);
70	}
71
72	@Test
73	public void test_1_6() throws IOException {
74		testVersion(V1_6, true);
75	}
76
77	@Test
78	public void test_1_7() throws IOException {
79		testVersion(V1_7, true);
80	}
81
82	@Test
83	public void test_1_8() throws IOException {
84		testVersion(V1_8, true);
85	}
86
87	@Test
88	public void test_1_9() throws IOException {
89		testVersion(V9, true);
90	}
91
92	private void testVersion(int version, boolean frames) throws IOException {
93		final byte[] original = createClass(version);
94
95		IRuntime runtime = new SystemPropertiesRuntime();
96		Instrumenter instrumenter = new Instrumenter(runtime);
97		byte[] instrumented = instrumenter.instrument(original, "TestTarget");
98
99		assertFrames(instrumented, frames);
100	}
101
102	private void assertFrames(byte[] source, boolean expected) {
103		final boolean[] hasFrames = new boolean[] { false };
104		new ClassReader(source)
105				.accept(new ClassVisitor(InstrSupport.ASM_API_VERSION) {
106
107					@Override
108					public MethodVisitor visitMethod(int access, String name,
109							String desc, String signature, String[] exceptions) {
110						return new MethodVisitor(InstrSupport.ASM_API_VERSION) {
111
112							@Override
113							public void visitFrame(int type, int nLocal,
114									Object[] local, int nStack, Object[] stack) {
115								hasFrames[0] = true;
116							}
117
118						};
119					}
120
121				}, 0);
122		assertEquals(Boolean.valueOf(expected), Boolean.valueOf(hasFrames[0]));
123	}
124
125	private byte[] createClass(int version) {
126
127		ClassWriter cw = new ClassWriter(0);
128		MethodVisitor mv;
129
130		cw.visit(version, ACC_PUBLIC + ACC_SUPER, "org/jacoco/test/Sample",
131				null, "java/lang/Object", null);
132
133		mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
134		mv.visitCode();
135		mv.visitVarInsn(ALOAD, 0);
136		mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V",
137				false);
138		mv.visitInsn(RETURN);
139		mv.visitMaxs(1, 1);
140		mv.visitEnd();
141
142		cw.visitEnd();
143
144		return cw.toByteArray();
145	}
146
147}
148