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

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

Open
johanhaleby opened this issue Jul 25, 2015 · 5 comments

Comments

@johanhaleby
Copy link
Collaborator

From gdel...@gmail.com on December 02, 2014 18:23:18

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 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.(JUnit4TestReference.java:33)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestClassReference.(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: http://code.google.com/p/powermock/issues/detail?id=529

@johanhaleby
Copy link
Collaborator Author

From gdel...@gmail.com on December 02, 2014 09:28:36

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 :)

@johanhaleby
Copy link
Collaborator Author

From gdel...@gmail.com on December 18, 2014 04:34:55

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)

@johanhaleby
Copy link
Collaborator Author

From gdel...@gmail.com on December 19, 2014 02:32:51

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.

@johanhaleby
Copy link
Collaborator Author

From gdel...@gmail.com on December 19, 2014 02:36:11

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 :)

thekingn0thing pushed a commit that referenced this issue Mar 30, 2020
When using the powermock-module-junit4-rule-agent there are java.lang.VerifyErrors.
For example if you have some project with Spock 1.0 and use use the rule, it works just fine.
As soon as you upgrade to 1.2 or 1.3 which dropped Java 6 support, you get various of these verify errors.

They are all about "Expected stackmap frame at this location.".

I'm no Byte Buddy expert, but from what I have read, the combination of ClassWriter.COMPUTE_MAXS with ClassReader.SKIP_FRAMES is not the best idea.
ClassReader.SKIP_FRAMES will skip reading and visiting stack maps and stack map tables.
The JavaDoc of that attribute says it is for example useful if you use ClassWriter.COMPUTE_FRAMES as it recalculates the stackmap frames anyway, so there is no need to parse and visit them.
But like it is currently, they are neither parsed, nor visited, nor computed and thus they are missing when verifying.

As far as I understood there are two ways to handle this, either use ClassWriter.COMPUTE_FRAMES and ClassReader.SKIP_FRAMES,
as then the frames are not parsed or visited, but recalculated, or do not use both as they are then parsed, visited and in
the case of this visitor just copied.

As the visitor only removes the final modifiers for methods and classes now, I renamed it,
disabled even the MAXS computation and disabled the frame skipping.

From my test it seems to work better with this setting now and also all your tests are green
(except three tests that are red for me even without my changes).

Fixes #1005
Probably fixes #956, #1024, #926, #549, #558, #873, #693, #543

Update: also re-enabled the tests that were disabled due to this error in #952
I think the exclusion of TestNG classes in commit e948b49 was also due to this error and could probably be reverted.

Work-around: Use JVM option -noverify to disable the verification that complains about the missing stackmap frames.
@Vampire
Copy link
Contributor

Vampire commented Mar 30, 2020

Probably fixed by #1043

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants