blob: 88e3903910a7a58732eea68f20756e1bec1bbf86 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2019 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:
* Marc R. Hoffmann - initial API and implementation
*
*******************************************************************************/
package org.jacoco.core.test.validation.java5;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.jacoco.core.instr.Instrumenter;
import org.jacoco.core.internal.instr.InstrSupport;
import org.jacoco.core.runtime.IRuntime;
import org.jacoco.core.runtime.SystemPropertiesRuntime;
import org.jacoco.core.test.TargetLoader;
import org.jacoco.core.test.validation.java5.targets.BooleanExpressionsTarget;
import org.jacoco.core.test.validation.java5.targets.ClassInitializerTarget;
import org.jacoco.core.test.validation.java5.targets.ConstructorsTarget;
import org.jacoco.core.test.validation.java5.targets.ControlStructureBeforeSuperConstructorTarget;
import org.jacoco.core.test.validation.java5.targets.ControlStructuresTarget;
import org.jacoco.core.test.validation.java5.targets.ExceptionsTarget;
import org.jacoco.core.test.validation.java5.targets.ExplicitInitialFrameTarget;
import org.jacoco.core.test.validation.java5.targets.FieldInitializationInTwoConstructorsTarget;
import org.jacoco.core.test.validation.java5.targets.ImplicitFieldInitializationTarget;
import org.jacoco.core.test.validation.java5.targets.InterfaceClassInitializerTarget;
import org.jacoco.core.test.validation.java5.targets.StructuredLockingTarget;
import org.junit.Test;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.util.TraceClassVisitor;
/**
* Tests whether stackmap frames are correctly adjusted.
*/
public class FramesTest {
/**
* Stack sizes calculated for instrumented classes might be sometimes bigger
* than actually needed. This is an acceptable tradeoff in favor of keeping
* track of the actual stack sizes. For test assertions we need to replace
* max stack sizes with constant value.
*/
private static class MaxStackEliminator extends ClassVisitor {
public MaxStackEliminator(ClassVisitor cv) {
super(InstrSupport.ASM_API_VERSION, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
final MethodVisitor mv = super.visitMethod(access, name, desc,
signature, exceptions);
return new MethodVisitor(InstrSupport.ASM_API_VERSION, mv) {
@Override
public void visitMaxs(int maxStack, int maxLocals) {
super.visitMaxs(-1, maxLocals);
}
};
}
}
private void testFrames(Class<?> target) throws IOException {
testFrames(TargetLoader.getClassDataAsBytes(target));
}
private void testFrames(byte[] source) throws IOException {
IRuntime runtime = new SystemPropertiesRuntime();
Instrumenter instrumenter = new Instrumenter(runtime);
source = calculateFrames(source);
byte[] actual = instrumenter.instrument(source, "TestTarget");
byte[] expected = calculateFrames(actual);
assertEquals(dump(expected), dump(actual));
}
private byte[] calculateFrames(byte[] source) {
ClassReader rc = InstrSupport.classReaderFor(source);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
// Adjust Version to 1.6 to enable frames:
rc.accept(new ClassVisitor(InstrSupport.ASM_API_VERSION, cw) {
@Override
public void visit(int version, int access, String name,
String signature, String superName, String[] interfaces) {
super.visit(Opcodes.V1_6, access, name, signature, superName,
interfaces);
}
}, 0);
return cw.toByteArray();
}
private String dump(byte[] bytes) {
final StringWriter buffer = new StringWriter();
final PrintWriter writer = new PrintWriter(buffer);
new ClassReader(bytes).accept(
new MaxStackEliminator(new TraceClassVisitor(writer)),
ClassReader.EXPAND_FRAMES);
return buffer.toString();
}
@Test
public void boolean_expressions() throws IOException {
testFrames(BooleanExpressionsTarget.class);
}
@Test
public void class_initializer() throws IOException {
testFrames(ClassInitializerTarget.class);
}
@Test
public void constructors() throws IOException {
testFrames(ConstructorsTarget.class);
}
@Test
public void control_structures() throws IOException {
testFrames(ControlStructuresTarget.class);
}
@Test
public void control_structure_before_super_constructor()
throws IOException {
testFrames(ControlStructureBeforeSuperConstructorTarget.class);
}
@Test
public void exceptions() throws IOException {
testFrames(ExceptionsTarget.class);
}
@Test
public void explicit_initial_frame() throws IOException {
testFrames(ExplicitInitialFrameTarget.class);
}
@Test
public void field_initialization_in_two_constructors() throws IOException {
testFrames(FieldInitializationInTwoConstructorsTarget.class);
}
@Test
public void implicit_field_initialization() throws IOException {
testFrames(ImplicitFieldInitializationTarget.class);
}
@Test
public void interface_class_initializer() throws IOException {
testFrames(InterfaceClassInitializerTarget.class);
}
@Test
public void structured_locking() throws IOException {
testFrames(StructuredLockingTarget.class);
}
}