lishunli / powermock

Automatically exported from code.google.com/p/powermock
Apache License 2.0
0 stars 0 forks source link

javaagent/java 7 and custom javassist not working together: stack shape inconsistent #529

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
What steps will reproduce the problem?
1. Write an empty test with a before class method
2. add as content of the beforeClass method this:
        ClassPool cp = ClassPool.getDefault();
        CtClass cc = cp.get("java.lang.String");
        ClassFile cf = cc.getClassFile();
        List<MethodInfo> methods = cf.getMethods();
        for (MethodInfo m : methods) {
            System.out.println(m.getName());
        }
3. Add the javaagent to the VM argument : 
-javaagent:powermock-module-javaagent-1.6.0.jar, in the run classpath, set the 
javaagent jar first
4. Make sure to set the compliance of the project to 1.7 (using a JRE 7)
4. Launch test with the standard JUnit4 runner

What is the expected output? What do you see instead?
I expect the test to pass as with Java 6, instead I get:
java.lang.VerifyError: JVMVRFY012 stack shape inconsistent; class=TestPSC, 
method=beforeClass()V, pc=29
    at java.lang.J9VMInternals.prepareClassImpl(Native Method)
    at java.lang.J9VMInternals.prepare(J9VMInternals.java:430)
    at java.lang.Class.getMethod(Class.java:1061)
    at org.junit.internal.builders.SuiteMethodBuilder.hasSuiteMethod(SuiteMethodBuilder.java:18)
    at org.junit.internal.builders.SuiteMethodBuilder.runnerForClass(SuiteMethodBuilder.java:10)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
    at org.junit.internal.builders.AllDefaultPossibilitiesBuilder.runnerForClass(AllDefaultPossibilitiesBuilder.java:26)
    at org.junit.runners.model.RunnerBuilder.safeRunnerForClass(RunnerBuilder.java:59)
    at org.junit.internal.requests.ClassRequest.getRunner(ClassRequest.java:26)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.<init>(JUnit4TestReference.java:33)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestClassReference.<init>(JUnit4TestClassReference.java:25)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.createTest(JUnit4TestLoader.java:48)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestLoader.loadTests(JUnit4TestLoader.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:444)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

What version of the product are you using? On what operating system?
JUnit 4.11, Powermock 1.6.0, javassist 3.18.2-GA

Please provide any additional information below.
Launching with a compliance 1.6 works perfectly fine, launching with 1.7 fails. 
I have also tried to add the option -XX:-UseSplitVerifier. And the javaagent 
jar is first in the classpath.

Any ideas?

Thank you!

Guillaume

 The class is:

    @Rule
    public PowerMockRule rule = new PowerMockRule();

    static {
        PowerMockAgent.initializeIfNeeded();
    }

    @BeforeClass
    public static void beforeClass() throws NotFoundException, CannotCompileException {
        ClassPool cp = ClassPool.getDefault();
        CtClass cc = cp.get("java.lang.String");
        ClassFile cf = cc.getClassFile();
        List<MethodInfo> methods = cf.getMethods();
        for (MethodInfo m : methods) {
            System.out.println(m.getName());
        }
    }

    @Test
    public void test() throws Exception {
    }

Original issue reported on code.google.com by gdel...@gmail.com on 2 Dec 2014 at 5:23

GoogleCodeExporter commented 9 years ago
I forgot to say that, to isolate the problem, I made a really dummy class being 
this:

public class TestA {

    public static void main(String[] args) throws NotFoundException {
        ClassPool cp = ClassPool.getDefault();
        CtClass cc = cp.get("java.lang.String");
        ClassFile cf = cc.getClassFile();
        List<MethodInfo> methods = cf.getMethods();
        for (MethodInfo m : methods) {
            System.out.println(m.getName());
        }
    }

}

And under Java 7, it runs fine without the powermock agent, as soon as I add 
the VM argument: -javaagent:powermock-module-javaagent-1.6.0.jar
it fails with the same error. So there is something wrong between javassist and 
the powermock javaagent under Java 7, or maybe I doing something wrong :)

Original comment by gdel...@gmail.com on 2 Dec 2014 at 5:28

GoogleCodeExporter commented 9 years ago
Output with a JRE 7 SUN 32 bits is the following:

Exception in thread "main" java.lang.VerifyError: Expecting a stackmap frame at branch target 57 Exception Details: Location: TestA.main([Ljava/lang/String;)V @31: goto Reason: Expected stackmap frame at this location. Bytecode: 0000000: b800 154c 2b12 17b6 001b 4d2c b600 214e 0000010: 2db6 0027 3a04 1904 b900 2d01 003a 06a7 0000020: 001a 1906 b900 3301 00c0 0035 3a05 b200 0000030: 3b19 05b6 003f b600 4519 06b9 0049 0100 0000040: 9aff e2b2 003b 124b b600 45b1

at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Unknown Source)
at java.lang.Class.getMethod0(Unknown Source)
at java.lang.Class.getMethod(Unknown Source)
at sun.launcher.LauncherHelper.getMainMethod(Unknown Source)
at sun.launcher.LauncherHelper.checkAndLoadMain(Unknown Source)

Original comment by gdel...@gmail.com on 18 Dec 2014 at 12:34

GoogleCodeExporter commented 9 years ago
The problem seems to come from the transformer class 
org.powermock.modules.agent.DefinalizingClassTransformer which rewrites the 
bytecode of class when loading. I guess there is something wrong in the 
rewriting from the ClassWriter or PowerMockClassVisitor classes but for now I 
don't have enough knowledge nor time to tell where is the problem.

Original comment by gdel...@gmail.com on 19 Dec 2014 at 10:32

GoogleCodeExporter commented 9 years ago
Also for the moment I found a workaround/fix (don't know if there is a way to 
fix the agent to make it work without the flag). I explain here 
(http://www.notonlyanecmplace.com/java-7-enforces-bytecode-verification/) but 
basically you can just disable the bytecode verification on IBM JRE or fallback 
to the old one on SUN JRE. I'll try to see if there is a better fix so it 
passes the verification, but help from the author would be nice because it is a 
bit too much for me now :)

Original comment by gdel...@gmail.com on 19 Dec 2014 at 10:36