Nested Classes and Instrumentation

72 views
Skip to first unread message

wwaylo...@gmail.com

unread,
Mar 30, 2018, 12:45:32 PM3/30/18
to JaCoCo and EclEmma Users
When I instrument a class with Jacoco, do nested classes also get instrumented as well?

My approach for instrumentation is based on the CoreTutorial example, finding classes based on class name.

```
final byte[] instrumented;

final String resource = '/' + className.replace('.', '/') + ".class";
InputStream original = getClass().getResourceAsStream(resource);

if (original == null) {
System.err.println("No resource with name: " + resource + " found!");
continue;
}

try {
// Instrument the class to prepare for coverage collection later.
instrumented = instrumenter.instrument(original, className);
original.close();
} catch (IOException e) {
e.printStackTrace();
continue;
}
```

After instrumenting a class which contains a nested class, I load the class with the MemoryClassLoader (based on the example) and then I call:

Class<?>[] result = c.getDeclaredClasses();

where c is of type Class<?>, and is the instrumented class.

However, I'm facing an IllegalAccessError of the form:

Throwable thrown while handling command: java.lang.IllegalAccessError: org/apache/commons/collections4/CollectionUtils$EquatorWrapper
java.lang.IllegalAccessError: org/apache/commons/collections4/CollectionUtils$EquatorWrapper
at java.lang.Class.getDeclaredClasses0(Native Method)
at java.lang.Class.getDeclaredClasses(Class.java:1867)
at randoop.reflection.ClassUtil.getDeclaredClasses(ClassUtil.java:60)
at randoop.reflection.ReflectionManager.apply(ReflectionManager.java:156)
at randoop.reflection.OperationModel.addOperationsFromClasses(OperationModel.java:584)
at randoop.reflection.OperationModel.createModel(OperationModel.java:163)
at randoop.main.GenTests.handle(GenTests.java:275)
at randoop.main.Main.nonStaticMain(Main.java:65)
at randoop.main.Main.main(Main.java:29)

The stacktrace indicates that the illegal access occurred when the outer class attempted to get the nested class.

After reading the documentation on IllegalAccessError, it appears that such an error occurs if a class's definition changes "incompatibly".

I'm wondering if this is somehow related to if a nested class gets instrumented or not.

wwaylo...@gmail.com

unread,
Mar 30, 2018, 12:50:44 PM3/30/18
to JaCoCo and EclEmma Users
My above stack trace is not quite right. I had made modifications to attempt to catch and ignore the errors which resulted in the above trace.

Throwable thrown while handling command: java.lang.IllegalAccessError: tried to access class org.apache.commons.collections4.multiset.SynchronizedMultiSet$SynchronizedSet from class org.apache.commons.collections4.multiset.SynchronizedMultiSet
java.lang.IllegalAccessError: tried to access class org.apache.commons.collections4.multiset.SynchronizedMultiSet$SynchronizedSet from class org.apache.commons.collections4.multiset.SynchronizedMultiSet


at java.lang.Class.getDeclaredClasses0(Native Method)
at java.lang.Class.getDeclaredClasses(Class.java:1867)
at randoop.reflection.ClassUtil.getDeclaredClasses(ClassUtil.java:60)
at randoop.reflection.ReflectionManager.apply(ReflectionManager.java:156)

at randoop.reflection.ReflectionManager.apply(ReflectionManager.java:83)
at randoop.reflection.OperationModel.addClassTypes(OperationModel.java:513)
at randoop.reflection.OperationModel.createModel(OperationModel.java:153)


at randoop.main.GenTests.handle(GenTests.java:275)
at randoop.main.Main.nonStaticMain(Main.java:65)
at randoop.main.Main.main(Main.java:29)

This trace shows the behavior that I described in the above post. (Without any additional error handling that I haven't described). The outer class accessing the inner class causes the IllegalAccessError.

Evgeny Mandrikov

unread,
Mar 30, 2018, 3:01:37 PM3/30/18
to JaCoCo and EclEmma Users
Hi,
 
I'm wondering if this is somehow related to if a nested class gets instrumented or not.

It is unrelated.

JVM throws IllegalAccessError in many various different cases of access that is not permitted. And one of them is when these classes defined by different class loaders - in the example below class "Outer" will be defined by "MyClassLoader", while class "Inner" defined by another class loader and 

java.lang.IllegalAccessError: tried to access class Outer$Nested from class Outer

will be thrown:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;

class Outer {
   class Nested {
   }

   public static void main(String[] args) throws Exception {
       System.out.println(Nested.class);
       class MyClassLoader extends ClassLoader {
           Class<?> define(String name, byte[] bytes) {
               return defineClass(name, bytes, 0, bytes.length);
           }
       }
       MyClassLoader classLoader = new MyClassLoader();
       //classLoader.define("Outer$Nested",
       //      read(Outer.class.getResourceAsStream("Outer$Nested.class")));
       Class c = classLoader.define("Outer",
               read(Outer.class.getResourceAsStream("Outer.class")));
       c.getDeclaredClasses();
   }

   private static byte[] read(InputStream input) throws IOException {
       byte[] buffer = new byte[1024];
       ByteArrayOutputStream output = new ByteArrayOutputStream();
       int bytes;
       while ((bytes = input.read(buffer)) != -1) {
           output.write(buffer, 0, bytes);
       }
       output.close(); 
       input.close();
       return output.toByteArray();
   }
}


Same happens in your case. Uncomment two lines to define "Inner" via "MyClassLoader" and "IllegalAccessError" will go away.

Class loading on itself is a complex subject, so advice - make sure that you understand class loading and that it works in your case prior to addition of instrumentation.

Now back to the question

When I instrument a class with Jacoco, do nested classes also get instrumented as well?


Creates a instrumented version of the given class if possible.

If necessary to rephrase, then: only bytes of one and only one class are instrumented - the one that is passed as argument to this method.


Regards,
Evgeny

wwaylo...@gmail.com

unread,
Apr 1, 2018, 12:09:13 PM4/1/18
to JaCoCo and EclEmma Users
Hi Evgeny,

Thank you for your response! It really helped clarify things and I have resolved the issue! Like you said, it was exactly a class loader problem that occurred when dealing with classes and nested classes.

Thank you!

Reply all
Reply to author
Forward
This conversation is locked
You cannot reply and perform actions on locked conversations.
0 new messages