|
Description
|
FULL PRODUCT VERSION :
java version "1.5.0-beta3"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0-beta3-b60)
Java HotSpot(TM) Client VM (build 1.5.0-beta3-b60, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Linux deepspace1 2.4.22-10mdk #1 Thu Sep 18 12:30:58 CEST 2003 i686 unknown unknown GNU/Linux
A DESCRIPTION OF THE PROBLEM :
I have added a very simple example, that reproduces the problem:
The class (test.Startup) contains the instrumentation. It does nothing but replace the loaded class "test.Instrumented" with a new version (which is exactly the same as the old version). The loaded byte arrays is dumped to a file, so it is possible to compare it to the original classfile.
After reloading the class, invoking the constructor of "test.Instrumented" leads to a VerifyError. But since the classfile is exactly the same as before this should not happen.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1.) Make a directory "test"
2.) Create the three java files in this directory ("Startup.java", "Instrumentation.java", "Test.java")
3.) Save the manifest as "manifest.mf"
3.) Compile the classes
javac test/*.java
4.) Build the jar file
jar -cvfm test.jar manifest.mf test/Startup.class
5.) Run the Test
java -classpath . -javaagent:test.jar test.Test
6.) A VerifyError is thrown.
7.) Observe that result.dump has exactly the same contents as test/Instrumentation.class
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No Exception.
ACTUAL -
An VerifyError is thrown.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.VerifyError: (class: test/Instrument, method: <init> signature: ()V) Illegal constant pool index
at test.Test.main(Test.java:12)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
----------------------------------------------
Instrumented.java:
----------------------------------------------
package test;
public class Instrument {
}
----------------------------------------------
Startup.java:
----------------------------------------------
package test;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.ByteArrayOutputStream;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.ClassDefinition;
public class Startup {
private static Instrumentation instrumentation;
public static void premain(String options, Instrumentation instrumentation) {
Startup.instrumentation = instrumentation;
}
public static void init()
throws Exception {
Class[] allClasses = instrumentation.getAllLoadedClasses();
for (int i=0; i<allClasses.length; i++) {
String className = allClasses[i].getName();
if ("test.Instrument".equals(className)) {
byte[] classfileBuffer = getClassFileBuffer(className, allClasses[i].getClassLoader());
instrumentation.redefineClasses(new ClassDefinition[] {new ClassDefinition(allClasses[i], classfileBuffer)});
FileOutputStream fout = new FileOutputStream("result.dump");
fout.write(classfileBuffer);
fout.close();
}
}
}
private static final byte[] getClassFileBuffer(String className, ClassLoader cl) throws IOException {
InputStream is = cl.getResourceAsStream(className.replace('.', '/') + ".class");
byte[] buf = new byte[1024];
ByteArrayOutputStream baos = new ByteArrayOutputStream(is.available());
int read;
do {
read = is.read(buf);
if (read > -1) {
baos.write(buf, 0, read);
}
} while (read > -1);
is.close();
return baos.toByteArray();
}
}
----------------------------------------------
Test.java:
----------------------------------------------
package test;
public class Test {
public static void main(String[] args) throws Exception {
// load the class
System.out.println(Instrument.class.getName());
Startup.init();
// try to use it again -> causes VerifyError
new Instrument();
}
}
----------------------------------------------
manifest.mf:
----------------------------------------------
premain-class: test.Startup
Can-Redefine-Classes: true
---------- END SOURCE ----------
(Incident Review ID: 300972)
======================================================================
xxxxx@xxxxx 10/7/04 00:12 GMT
|
|
Evaluation
|
The test case redefines a class before it is linked. The redefine allocates a constant pool cache and rewrites the methods. However, later when the class is linked the verifier is invoked a second time on this class - on the second verification it is called to verify methods that have been re-written and hence the VerifyError. Possible solutions to examine are linking the class at redefine time (we already force all superclasses to be linked), or skip the (second) verification if the class has rewritten by redefine.
xxxxx@xxxxx 2004-08-27
Redefine now forces the class to be linked (previously only super classes
and interfaces were linked).
xxxxx@xxxxx 2004-09-28
|