Skip to content

Commit

Permalink
Merge pull request #1161 from hcoles/bug/synthetic_clinit
Browse files Browse the repository at this point in the history
avoid creating duplicate clinit when existing one is synthetic
  • Loading branch information
hcoles authored Mar 3, 2023
2 parents 1411fc3 + 6bb2ad4 commit 8ad249d
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.pitest.coverage;

import org.junit.Test;
import org.mockito.Mockito;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.Opcodes;
import org.pitest.bytecode.analysis.ClassTree;
import org.pitest.bytecode.analysis.MethodTree;
import org.pitest.classpath.ClassloaderByteArraySource;
import sun.pitest.CodeCoverageStore;
import sun.pitest.InvokeReceiver;

import java.util.List;
import java.util.stream.Collectors;

import static org.assertj.core.api.Assertions.assertThat;

// additional tests for coverage transformer which are
// easier to write within pitest-entry due to access to
// ClassTree
public class CoverageTransformerTest {

ClassloaderByteArraySource byteSource = ClassloaderByteArraySource.fromContext();
CoverageTransformer underTest = new CoverageTransformer(s -> true);

@Test
public void doesNotDuplicateClinitWhenSynthetic() {

byte[] bytes = bytesForClassWithSyntheticStaticInit();

CodeCoverageStore.init(Mockito.mock(InvokeReceiver.class));

byte[] transformed = underTest.transform(null, "anything", null, null, bytes);

ClassTree instrumentedClass = ClassTree.fromBytes(transformed);

List<MethodTree> clinitMethods = instrumentedClass.methods().stream()
.filter(m -> m.rawNode().name.equals("<clinit>"))
.collect(Collectors.toList());

assertThat(clinitMethods).hasSize(1);
}

private byte[] bytesForClassWithSyntheticStaticInit() {
ClassTree classWithStaticInit = ClassTree.fromBytes(byteSource.getBytes(HasStaticInit.class.getName()).get());
MethodTree clinit = classWithStaticInit.methods().stream()
.filter(m -> m.rawNode().name.equals("<clinit>"))
.findAny()
.get();

clinit.rawNode().access = Opcodes.ACC_SYNTHETIC;

byte[] bytes = asBytes(classWithStaticInit);
return bytes;
}

private byte[] asBytes(ClassTree tree) {
ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
tree.rawNode().accept(classWriter);
return classWriter.toByteArray();
}
}

class HasStaticInit {
static String FOO = "";
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ private boolean shouldInstrument(final int access, final String name,
@Override
public final MethodVisitor visitMethod(final int access, final String name,
final String desc, final String signature, final String[] exceptions) {

preVisitMethod(access, name, desc, signature, exceptions);
final MethodVisitor methodVisitor = this.cv.visitMethod(access, name, desc,
signature, exceptions);
if (shouldInstrument(access, name, desc, signature, exceptions)) {
Expand All @@ -47,6 +49,10 @@ public final MethodVisitor visitMethod(final int access, final String name,
}
}

protected void preVisitMethod(int access, String name, String desc, String signature, String[] exceptions) {
// noop
}

public abstract MethodVisitor visitMethodIfRequired(int access, String name,
String desc, String signature, String[] exceptions,
MethodVisitor methodVisitor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,18 @@ public void visit(int version, int access, String name, String signature,
this.isInterface = (access & Opcodes.ACC_INTERFACE) != 0;
}

@Override
protected void preVisitMethod(int access, String name, String desc, String signature, String[] exceptions) {
if (name.equals("<clinit>")) {
foundClinit = true;
}
}

@Override
public MethodVisitor visitMethodIfRequired(final int access,
final String name, final String desc, final String signature,
final String[] exceptions, final MethodVisitor methodVisitor) {

if (name.equals("<clinit>")) {
foundClinit = true;
}

return new CoverageAnalyser(this, this.classId, this.probeCount,
methodVisitor, access, name, desc, signature, exceptions);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public void shouldNotTransformClassesNotMatchingPredicate() {
}

@Test
public void shouldTransformClasseMatchingPredicate() {
public void shouldTransformClassesMatchingPredicate() {
final CoverageTransformer testee = new CoverageTransformer(
s -> true);
final byte[] bs = this.bytes.getBytes(String.class.getName()).get();
Expand All @@ -71,7 +71,7 @@ public void shouldTransformClasseMatchingPredicate() {
}

@Test
public void shouldGenerateValidClasses() throws IllegalClassFormatException {
public void shouldGenerateValidClasses() {
assertValidClass(String.class);
assertValidClass(Integer.class);
assertValidClass(Vector.class);
Expand Down

0 comments on commit 8ad249d

Please sign in to comment.