jira.codehaus.org

  • Log In Access more options
    • Online Help
    • Keyboard Shortcuts
    • About JIRA
    • JIRA Credits
    • What?s New
  • Dashboards Access more options (Alt+d)
  • Projects Access more options (Alt+p)
  • Issues Access more options (Alt+i)
  • RVM
  • RVM-493

VM_Annotation.readValue doesn't use the correct classloader

  • Log In
  • Views
    • XML
    • Word
    • Printable

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Minor Minor
  • Resolution: Fixed
  • Affects Version/s: 2.9.3
  • Fix Version/s: 3.0
  • Component/s: None
  • Labels:
    None
  • Environment:
    prototype Jikes RVM 2.9.3+svn (r14214) using GNU Classpath 0.97.1
  • Testcase included:
    yes

Description

In VM_Annotation there seems to be at least one bug (although I suspect there might be two of them).

In line 297 of said file Jikes uses Class.forName to load the annotation's class file:

value = Class.forName(VM_Class.getUtf(constantPool, classInfoIndex).toString());

But using the single-argument Class.forName(String) causes the bootstrap classloader to be used (as VM_Annotation has been loaded by it). This leads to the following exception, when running the attached test case which the org.junit.runner.JUnitCore runner. To fix this, the three-argument version of Class.forName needs to be used, with this.classLoader as its third argument.

JUnit version 4.3.1
.E
Time: 0.02
There was 1 failure:
1) initializationError0(BugReport)
java.lang.Error: java.lang.ClassNotFoundException: org.junit.Test$Non
at .<invisible method>(Unknown Source:0)
at java.lang.VMThrowable.fillInStackTrace(VMThrowable.java:49)
at java.lang.Throwable.fillInStackTrace(Throwable.java:498)
at java.lang.Throwable.<init>(Throwable.java:159)
at java.lang.Throwable.<init>(Throwable.java:174)
at java.lang.Throwable.<init>(Throwable.java:188)
at java.lang.Error.<init>(Error.java:105)
at org.jikesrvm.classloader.VM_Method.readMethod(VM_Method.java:204)
at org.jikesrvm.classloader.VM_Class.readClass(VM_Class.java:1442)
at org.jikesrvm.classloader.VM_ClassLoader.defineClassInternal(VM_ClassLoader.java:336)
at org.jikesrvm.classloader.VM_ClassLoader.defineClassInternal(VM_ClassLoader.java:307)
at java.lang.VMClassLoader.defineClass(VMClassLoader.java:93)
at java.lang.VMClassLoader.defineClassWithTransformers(VMClassLoader.java:312)
at java.lang.ClassLoader.defineClass(ClassLoader.java:471)
at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:83)
at java.net.URLClassLoader.findClass(URLClassLoader.java:617)
at java.lang.ClassLoader.loadClass(ClassLoader.java:341)
at java.lang.ClassLoader.loadClass(ClassLoader.java:293)
at org.jikesrvm.classloader.VM_TypeReference.resolveInternal(VM_TypeReference.java:762)
at org.jikesrvm.classloader.VM_TypeReference.resolve(VM_TypeReference.java:750)
at org.jikesrvm.classloader.VM_Class.getLiteralOffset(VM_Class.java:700)
at org.jikesrvm.classloader.VM_Class.getLiteralOffset(VM_Class.java:679)
at org.jikesrvm.compilers.baseline.VM_CompilerFramework.genCode(VM_CompilerFramework.java:345)
at org.jikesrvm.compilers.baseline.VM_BaselineCompiler.compile(VM_BaselineCompiler.java:255)
at org.jikesrvm.compilers.baseline.VM_BaselineCompiledMethod.compile(VM_BaselineCompiledMethod.java:145)
at org.jikesrvm.compilers.baseline.VM_BaselineCompiler.compile(VM_BaselineCompiler.java:177)
at org.jikesrvm.compilers.common.VM_RuntimeCompiler.baselineCompile(VM_RuntimeCompiler.java:293)
at org.jikesrvm.compilers.common.VM_RuntimeCompiler.compile(VM_RuntimeCompiler.java:754)
at org.jikesrvm.classloader.VM_NormalMethod.genCode(VM_NormalMethod.java:175)
at org.jikesrvm.classloader.VM_Method.compile(VM_Method.java:695)
at org.jikesrvm.runtime.VM_DynamicLinker$DL_Helper.compileMethod(VM_DynamicLinker.java:149)
at org.jikesrvm.runtime.VM_DynamicLinker.lazyMethodInvoker(VM_DynamicLinker.java:45)
at org.junit.internal.runners.TestClassRunner.<init>(TestClassRunner.java:20)
at .<invisible method>(Unknown Source:0)
at org.jikesrvm.runtime.VM_Reflection.invoke(VM_Reflection.java:132)
at org.jikesrvm.runtime.VM_Reflection.invoke(VM_Reflection.java:45)
at java.lang.reflect.VMConstructor.construct(VMConstructor.java:121)
at java.lang.reflect.Constructor.newInstance(Constructor.java:318)
at org.junit.internal.requests.ClassRequest.buildRunner(ClassRequest.java:33)
at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:28)
at org.junit.internal.requests.ClassesRequest.getRunner(ClassesRequest.java:21)
at org.junit.runner.JUnitCore.run(JUnitCore.java:109)
at org.junit.runner.JUnitCore.run(JUnitCore.java:100)
at org.junit.runner.JUnitCore.runMain(JUnitCore.java:81)
at org.junit.runner.JUnitCore.main(JUnitCore.java:44)
Caused by: java.lang.ClassNotFoundException: org.junit.Test$Non
at .<invisible method>(Unknown Source:0)
at java.lang.VMThrowable.fillInStackTrace(VMThrowable.java:49)
at java.lang.Throwable.fillInStackTrace(Throwable.java:498)
at java.lang.Throwable.<init>(Throwable.java:159)
at java.lang.Exception.<init>(Exception.java:78)
at java.lang.ClassNotFoundException.<init>(ClassNotFoundException.java:84)
at org.jikesrvm.classloader.VM_BootstrapClassLoader.findClass(VM_BootstrapClassLoader.java:183)
at org.jikesrvm.classloader.VM_BootstrapClassLoader.loadClass(VM_BootstrapClassLoader.java:145)
at java.lang.ClassLoader.loadClass(ClassLoader.java:293)
at org.jikesrvm.classloader.VM_TypeReference.resolveInternal(VM_TypeReference.java:762)
at org.jikesrvm.classloader.VM_TypeReference.resolve(VM_TypeReference.java:750)
at java.lang.Class.forNameInternal(Class.java:678)
at java.lang.Class.forName(Class.java:95)
at org.jikesrvm.classloader.VM_Annotation.readValue(VM_Annotation.java:297)
at org.jikesrvm.classloader.VM_Annotation.readValue(VM_Annotation.java:221)
at org.jikesrvm.classloader.VM_Method.readMethod(VM_Method.java:202)
...37 more

FAILURES!!!
Tests run: 1, Failures: 1

The second bug I have alluded to above manifest itself in the fact that the class Jikes attempts to load is org.junit.Test$Non; however, it ought to be org.junit.Test$None (note the trailing 'e'). Somehow, the last character gets eaten, most likely due to confusion whether Jikes has to deal with a type descriptor or an internal class name here.

  • Options
    • Sort By Name
    • Sort By Date
    • Ascending
    • Descending
    • Download All

Attachments

  1. Java Source File
    BugReport.java
    13/May/08 1:03 PM
    0.1 kB
    Andreas Sewe
  2. File
    Test.class
    14/May/08 1:31 AM
    0.6 kB
    Christoph Bockisch

Issue Links

relates to

Improvement - An improvement or enhancement to an existing feature or task. RVM-333 Annotations used for VM Pragmas (NoInline, Uninterruptible, etc) probably should not be as closed linked to classloaders

  • Major - Major loss of function.
  • Closed - The issue is considered finished, the resolution is correct. Issues which are not closed can be reopened.

Activity

Ascending order - Click to sort in descending order
  • All
  • Comments
  • Work Log
  • History
  • Activity
Hide
Permalink
Ian Rogers added a comment - 13/May/08 2:42 PM

Just to confirm the problem with the class loader, I believe a similar problem for enums has been fixed. I can't reproduce the lost 'e' problem, could you give a test case not reliant on me having to do too muck work with junit? There's another problem if a value that annotates a class is a class literal of that class then we either recurse or die with a class not found. Ie:

@interface testAnnotation {
Class value;
}
@testAnnotation{test.class}
class test {}
We can't possibly create the test.class value as the class hasn't yet been loaded. We need some way to defer this case.

Show
Ian Rogers added a comment - 13/May/08 2:42 PM Just to confirm the problem with the class loader, I believe a similar problem for enums has been fixed. I can't reproduce the lost 'e' problem, could you give a test case not reliant on me having to do too muck work with junit? There's another problem if a value that annotates a class is a class literal of that class then we either recurse or die with a class not found. Ie: @interface testAnnotation { Class value; } @testAnnotation{test.class} class test {} We can't possibly create the test.class value as the class hasn't yet been loaded. We need some way to defer this case.
Hide
Permalink
Christoph Bockisch added a comment - 14/May/08 1:31 AM

The bug of the lost 'e' is not necessarily in Jikes, but in the JUnit classes. I have attached the org.junit.Test class file extracted from the JUnit 4.3.1 distribution that we are using. You should be able to reproduce the error by loading this class.

Using a bytecode viewer, you can see that the default annotation of the method "expected" refers to a UTF8 constant pool entry with the value "Lorg/junit/Test$Node;". From the Java 5 class file format specification, it's not clear to us if this is correct or not. As far as we understand, it should rather be "org/junit/TestNode" (i.e., no L and . This is also the format that Jikes is expecting.

My guess is that the distributed class file is actually not conform with the specification, but that other virtual machines are tolerant to this error.

Show
Christoph Bockisch added a comment - 14/May/08 1:31 AM The bug of the lost 'e' is not necessarily in Jikes, but in the JUnit classes. I have attached the org.junit.Test class file extracted from the JUnit 4.3.1 distribution that we are using. You should be able to reproduce the error by loading this class. Using a bytecode viewer, you can see that the default annotation of the method "expected" refers to a UTF8 constant pool entry with the value "Lorg/junit/Test$Node;". From the Java 5 class file format specification, it's not clear to us if this is correct or not. As far as we understand, it should rather be "org/junit/TestNode" (i.e., no L and . This is also the format that Jikes is expecting. My guess is that the distributed class file is actually not conform with the specification, but that other virtual machines are tolerant to this error.
Hide
Permalink
Andreas Sewe added a comment - 20/May/08 10:43 AM

I have been digging a little bit deeper into this issue and now have a fix that solves the problem with JUnit's annotations.

As you can see in the Test.class file Christoph provided, the AnnotationDefault refers to constant pool entry #10, an UTF-8 encoded type-descriptor Lorg/junit/Test$None;. The code in VM_Annotation.readValue, however, expects a class name. Changing the method's 'c' case from

value = Class.forName(VM_Class.getUtf(constantPool, classInfoIndex).toString());

to

VM_Atom descriptor = VM_Class.getUtf(constantPool, classInfoIndex);
if (VM.VerifyAssertions) VM._assert(descriptor.isClassDescriptor());
value = Class.forName(descriptor.classNameFromDescriptor(), true, classLoader);

already solves most of the problem. (The assertion is just there to make sure the AnnotationDefault always refers to a type descriptor; I haven't found that spelled out explicitly by Sun, but so far the above code passes all tests I threw at it.)

Still, there is one issue when OPT-compiling the VM generated annotation class org.junit.Test$$: As far as I can tell, the class literal org.junit.Test.None isn't added properly to the annotation class's constant pool. It gets a type of CP_String instead of CP_Class. Thus, a fix needs to be applied to VM_Class.createAnnotationClass as well, as it is missing a value instanceof Class case:

VM_TypeReference typeRef = JikesRVMSupport.getTypeForClass((Class<?>) value).getTypeRef();
{{constantPool[nextFreeConstantPoolSlot] = packCPEntry(CP_CLASS, typeRef.getId());}}
{{defaultConstants[j] = nextFreeConstantPoolSlot;}}
j++;
nextFreeConstantPoolSlot++;

FWIW, I suspect that the above only surfaces when using the OPT-compiler as it tries to optimize away those constants – and fails with a ClassCastException when it calls, based on the wrong type, ClassLoaderProxy.getStringFromConstantPool instead of getClassFromConstantPool.

Show
Andreas Sewe added a comment - 20/May/08 10:43 AM I have been digging a little bit deeper into this issue and now have a fix that solves the problem with JUnit's annotations. As you can see in the Test.class file Christoph provided, the AnnotationDefault refers to constant pool entry #10, an UTF-8 encoded type-descriptor Lorg/junit/Test$None;. The code in VM_Annotation.readValue, however, expects a class name. Changing the method's 'c' case from value = Class.forName(VM_Class.getUtf(constantPool, classInfoIndex).toString()); to VM_Atom descriptor = VM_Class.getUtf(constantPool, classInfoIndex); if (VM.VerifyAssertions) VM._assert(descriptor.isClassDescriptor()); value = Class.forName(descriptor.classNameFromDescriptor(), true, classLoader); already solves most of the problem. (The assertion is just there to make sure the AnnotationDefault always refers to a type descriptor; I haven't found that spelled out explicitly by Sun, but so far the above code passes all tests I threw at it.) Still, there is one issue when OPT-compiling the VM generated annotation class org.junit.Test$$: As far as I can tell, the class literal org.junit.Test.None isn't added properly to the annotation class's constant pool. It gets a type of CP_String instead of CP_Class. Thus, a fix needs to be applied to VM_Class.createAnnotationClass as well, as it is missing a value instanceof Class case: VM_TypeReference typeRef = JikesRVMSupport.getTypeForClass((Class<?>) value).getTypeRef(); {{constantPool[nextFreeConstantPoolSlot] = packCPEntry(CP_CLASS, typeRef.getId());}} {{defaultConstants[j] = nextFreeConstantPoolSlot;}} j++; nextFreeConstantPoolSlot++; FWIW, I suspect that the above only surfaces when using the OPT-compiler as it tries to optimize away those constants – and fails with a ClassCastException when it calls, based on the wrong type, ClassLoaderProxy.getStringFromConstantPool instead of getClassFromConstantPool.
Hide
Permalink
Ian Rogers added a comment - 15/Jul/08 4:44 PM

Fix in r14726. Tests in r14722.

Show
Ian Rogers added a comment - 15/Jul/08 4:44 PM Fix in r14726. Tests in r14722.

People

  • Assignee:
    Ian Rogers
    Reporter:
    Andreas Sewe
Vote (0)
Watch (0)

Dates

  • Created:
    13/May/08 1:03 PM
    Updated:
    15/Jul/08 4:44 PM
    Resolved:
    15/Jul/08 4:44 PM
  • Atlassian JIRA (v5.0.4#731-sha1:3aa7374)
  • Report a problem
  • Powered by a free Atlassian JIRA open source license for Codehaus. Try JIRA - bug tracking software for your team.