Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Failure after instrumentation of code which has stackmap frames and array of more than 7 dimensions #839

Closed
quiquinSP opened this issue Feb 1, 2019 · 3 comments · Fixed by #851
Assignees
Labels
component: core type: bug 🐛 Something isn't working
Milestone

Comments

@quiquinSP
Copy link

I am in the process of migrating a SW from Java 8 to Java 11. We are using JaCoCo for code coverage and I have found a problem when running the unit tests with JaCoCo tool (using ant).

JaCoCo fails when it instantiates a class that contains a primitive array with more than 8 dimensions. Please do not ask about the motivation of having an 8 dimensional array .... legacy code I have to maintain.

I have reduced the code to the min that reproduces the problem.

  • The execution goes fine when I do not use JaCoCo/EclEmma or I use Java 8
  • I have tested with two different JaCoCo versions , 0.8.2 and 0.8.3

Steps to reproduce

JaCoCo version: tested with 0.8.2 and 0.8.3
Operating system: MacOS HighSierra 10.13.6 and CentOS Linux release 7.5.1804
Tool integration: Ant 1.10.5 and Eclipse EclEmma 3.1.1.201809121651
Java Versions: OpenJDK 11.0.1 and OpenJDK 11.0.2

Use the following piece of code

import org.junit.Test;
public class PrimitiveArrayJacocoTest {

	/**
	 * Test with a boolean array
	 */
	@Test
	public void testBooleanArray() {
		// Just instantiate the class
		new BooleanArrayJacoco();		
	}
	
	/**
	 * Test with an integer array
	 */
	@Test
	public void testIntegerArray() {	
		// Just instantiate the class
		new IntegerArrayJacoco();	
	}
	
	public class BooleanArrayJacoco {
		
		public BooleanArrayJacoco() {
		    
			// Declare a 8 dimension boolean array , if the amount of dimensions is reduced test does not fail. 
		    boolean[][][][][][][][] array = new boolean[12][][][][][][][];
		    int idxDepth0 = 0; 
		    if(array[idxDepth0] == null) {
		    	array[0] = new boolean[4][][][][][][]; // Create new node with 4 array pointers
		    }
		}
		
	}
	
	public class IntegerArrayJacoco {
		
		public IntegerArrayJacoco() {
		    
			// Declare a 8 dimension integer array , if the amount of dimensions is reduced test does not fail. 
		    int[][][][][][][][] array = new int[12][][][][][][][];
		    int idxDepth0 = 0; 
		    if(array[idxDepth0] == null) {
		    	array[0] = new int[4][][][][][][]; // Create new node with 4 array pointers
		    }
		}
		
	}

}

Expected behaviour

JaCoCo is able to run the tests with no failure.

Actual behaviour

Tests fail with the following stack trace

java.lang.NoClassDefFoundError: I
 at gaia.cu1.tools.util.lookuptree.PrimitiveArrayJacocoTest.testIntegerArray(PrimitiveArrayJacocoTest.java:44)
 at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
 at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.base/java.lang.reflect.Method.invoke(Method.java:566)
 at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
 at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
 at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
 at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
 at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
 at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
 at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
 at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
Caused by: java.lang.ClassNotFoundException: I
 at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
 at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
 at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
 ... 24 more

and running the testBooleanArray .... the same but with different ClassNotFoundException:

java.lang.NoClassDefFoundError: Z
 at gaia.cu1.tools.util.lookuptree.PrimitiveArrayJacocoTest.testBooleanArray(PrimitiveArrayJacocoTest.java:33)
 at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
 at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
 at java.base/java.lang.reflect.Method.invoke(Method.java:566)
 at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
 at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
 at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
 at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
 at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
 at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
 at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
 at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
 at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
 at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
 at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
 at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
 at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
 at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:538)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:760)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:460)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
Caused by: java.lang.ClassNotFoundException: Z
 at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
 at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
 at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
 ... 24 more
@Godin Godin self-assigned this Feb 1, 2019
@Godin
Copy link
Member

Godin commented Feb 1, 2019

Hi @quiquinSP, first of all thank you for the reproducer! 👍 ❤️

However I doubt about correctness of your statement

The execution goes fine when I use Java 8

because following reduced Example.java

public class Example {
    public static void main(String[] args) {
        boolean[][][][][][][][] a = new boolean[12][][][][][][][];
        int i = 0;
        if (a[i] == null) {
            a[0] = new boolean[4][][][][][][];
        }
        System.out.println("OK");
    }
}

demonstrates exactly the same problem even with Java 8:

$ java -version
java version "1.8.0_172"
Java(TM) SE Runtime Environment (build 1.8.0_172-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.172-b11, mixed mode)

$ javac -version
javac 1.8.0_172

$ javac -source 8 -target 8 Example.java

$  java -javaagent:jacoco-0.8.3/lib/jacocoagent.jar Example
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.NoClassDefFoundError: Z
	at java.lang.Class.getDeclaredMethods0(Native Method)
	at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
	at java.lang.Class.privateGetMethodRecursive(Class.java:3048)
	at java.lang.Class.getMethod0(Class.java:3018)
	at java.lang.Class.getMethod(Class.java:1784)
	at sun.launcher.LauncherHelper.validateMainClass(LauncherHelper.java:544)
	at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:526)
Caused by: java.lang.ClassNotFoundException: Z
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
	... 7 more

but the problem goes away when class has no stackmap frames, i.e. when compiled into Java 5 bytecode:

$ javap -v -p Example | grep "StackMapTable:" | wc -l
1

$ javap -v -p Example | grep "major"
  major version: 52

$ javac -source 5 -target 5 Example.java
warning: [options] bootstrap class path not set in conjunction with -source 1.5
warning: [options] source value 1.5 is obsolete and will be removed in a future release
warning: [options] target value 1.5 is obsolete and will be removed in a future release
warning: [options] To suppress warnings about obsolete options, use -Xlint:-options.
4 warnings

$ javap -v -p Example | grep "StackMapTable:" | wc -l
0

$ javap -v -p Example | grep "major"
  major version: 49

$ java -javaagent:jacoco-0.8.3/lib/jacocoagent.jar Example
OK

and seems to be related to the bug in ASM library - https://gitlab.ow2.org/asm/asm/issues/317866

As a temporary workaround you can exclude such classes from JaCoCo instrumentation.


P.S. and please excuse me for pedantism, however

JaCoCo fails when it instantiates a class

JaCoCo does not perform instantiation - it performs instrumentation; instantiation, initialization and etc are performed by JVM as usual.

@Godin Godin changed the title Java 11 - Jacoco failure when analyzing code with a primitive array of more than 8 dimensions Failure when instrumenting code with stackmap frames and a primitive array of more than 8 dimensions Feb 1, 2019
@Godin Godin added type: bug 🐛 Something isn't working component: core labels Feb 1, 2019
@quiquinSP
Copy link
Author

Hi @Godin,

thank you very much for your prompt reaction.

However I doubt about correctness of your statement

The execution goes fine when I use Java 8

Yes, you are right the problem was not related with Java version, with Java 8 I was using a different version of JaCoCo 0.7.9 that uses a different asm version (5.2).

As a temporary workaround you can exclude such classes from JaCoCo instrumentation.

Yes, I am going to exclude this particular test from the analysis.

@Godin Godin changed the title Failure when instrumenting code with stackmap frames and a primitive array of more than 8 dimensions Failure after instrumentation of code which has stackmap frames and a primitive array of more than 8 dimensions Feb 1, 2019
@Godin Godin added this to the 0.8.4 milestone Mar 4, 2019
@Godin Godin added this to Candidates in Current work items via automation Mar 4, 2019
@Godin
Copy link
Member

Godin commented Mar 5, 2019

For the record - the same problem with arrays of non-primitive with more than 7 dimensions:

public class Example {
    public static void main(String[] args) {
        Object[][][][][][][][] a = new Object[1][][][][][][][];
        if (a[0] == null) {
            a = null;
        }
        System.out.println("OK");
    }
}

however exception looks differently

$ javac -source 8 -target 8 Example.java

$ java -javaagent:jacoco-0.8.3/lib/jacocoagent.jar Example
Exception in thread "main" java.lang.VerifyError: Inconsistent stackmap frames at branch target 22
Exception Details:
  Location:
    Example.main([Ljava/lang/String;)V @22: aconst_null
  Reason:
    Type '[[[[[[[[Ljava/lang/Object;' (current frame, locals[2]) is not assignable to 'Ljava/lang/Object;' (stack map, locals[2])
  Current Frame:
    bci: @12
    flags: { }
    locals: { '[Ljava/lang/String;', '[Z', '[[[[[[[[Ljava/lang/Object;' }
    stack: { '[[[[[[[Ljava/lang/Object;' }
  Stackmap Frame:
    bci: @22
    flags: { }
    locals: { '[Ljava/lang/String;', '[Z', 'Ljava/lang/Object;' }
    stack: { }
  Bytecode:
    0x0000000: b800 254c 04bd 0002 4d2c 0332 c600 0a2b
    0x0000010: 0404 54a7 000c 01c0 0003 4d2b 0504 54b2
    0x0000020: 0004 1205 b600 062b 0604 54b1
  Stackmap Table:
    append_frame(@22,Object[#39],Object[#41])
    same_frame(@31)

@Godin Godin changed the title Failure after instrumentation of code which has stackmap frames and a primitive array of more than 8 dimensions Failure after instrumentation of code which has stackmap frames and array of more than 7 dimensions Mar 5, 2019
Current work items automation moved this from Candidates to Done Mar 8, 2019
@jacoco jacoco locked as resolved and limited conversation to collaborators May 8, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
component: core type: bug 🐛 Something isn't working
Projects
Development

Successfully merging a pull request may close this issue.

2 participants