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

powermock 2.0.9 fails due to java.lang.RuntimeException: class is frozen #1154

Open
romanatazuldotcom opened this issue Apr 7, 2023 · 1 comment

Comments

@romanatazuldotcom
Copy link

pom.xml depdendencies

    <dependencies>
        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-module-junit4</artifactId>
            <version>2.0.9</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.powermock</groupId>
            <artifactId>powermock-api-mockito2</artifactId>
            <version>2.0.9</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

Reproducer test case

import org.powermock.api.mockito.PowerMockito;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.core.classloader.annotations.PrepareForTest;

import org.junit.runner.RunWith;
import junit.framework.TestCase;

class NestedClasses {
  private static Foo foo = new Foo();
  private static Bar bar = new Bar();
  static class Foo {
    void foo(Bar.Baz baz) {}
  }
  static class Bar {
    static class Baz {
    }
  }
}

@RunWith(PowerMockRunner.class)
@PrepareForTest({NestedClasses.class})
public class Test extends TestCase {
  public void test() throws Exception {
    PowerMockito.mockStatic(NestedClasses.class);
    System.out.println("Finished Foo");
  }
}

Stack trace

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.powermock.reflect.internal.WhiteboxImpl (file:/Users/roman/.m2/repository/org/powermock/powermock-reflect/2.0.9/powermock-reflect-2.0.9.jar) to method java.lang.Object.clone()
WARNING: Please consider reporting this to the maintainers of org.powermock.reflect.internal.WhiteboxImpl
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

java.lang.ExceptionInInitializerError
	at jdk.internal.reflect.GeneratedSerializationConstructorAccessor4.newInstance(Unknown Source)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
	at org.objenesis.instantiator.sun.SunReflectionFactoryInstantiator.newInstance(SunReflectionFactoryInstantiator.java:48)
	at org.objenesis.ObjenesisBase.newInstance(ObjenesisBase.java:73)
	at org.mockito.internal.creation.instance.ObjenesisInstantiator.newInstance(ObjenesisInstantiator.java:19)
	at org.mockito.internal.creation.bytebuddy.SubclassByteBuddyMockMaker.createMock(SubclassByteBuddyMockMaker.java:47)
	at org.mockito.internal.creation.bytebuddy.ByteBuddyMockMaker.createMock(ByteBuddyMockMaker.java:25)
	at org.powermock.api.mockito.mockmaker.PowerMockMaker.createMock(PowerMockMaker.java:41)
	at org.mockito.internal.util.MockUtil.createMock(MockUtil.java:35)
	at org.mockito.internal.MockitoCore.mock(MockitoCore.java:63)
	at org.mockito.Mockito.mock(Mockito.java:1908)
	at org.powermock.api.mockito.internal.mockcreation.DefaultMockCreator.createMethodInvocationControl(DefaultMockCreator.java:108)
	at org.powermock.api.mockito.internal.mockcreation.DefaultMockCreator.doCreateMock(DefaultMockCreator.java:61)
	at org.powermock.api.mockito.internal.mockcreation.DefaultMockCreator.createMock(DefaultMockCreator.java:53)
	at org.powermock.api.mockito.internal.mockcreation.DefaultMockCreator.mock(DefaultMockCreator.java:40)
	at org.powermock.api.mockito.PowerMockito.mockStatic(PowerMockito.java:62)
	at Test.test(Test.java:24)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:68)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:326)
	at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:89)
	at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:97)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:310)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:131)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.access$100(PowerMockJUnit47RunnerDelegateImpl.java:59)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner$TestExecutorStatement.evaluate(PowerMockJUnit47RunnerDelegateImpl.java:147)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.evaluateStatement(PowerMockJUnit47RunnerDelegateImpl.java:107)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:298)
	at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:87)
	at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:50)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:218)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:160)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:134)
	at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
	at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
	at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:136)
	at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:117)
	at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:57)
	at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
	at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
	at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
	at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
	at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.lang.IllegalStateException: Failed to transform class with name NestedClasses$Bar. Reason: NestedClasses$Bar$Baz class is frozen
	at org.powermock.core.classloader.javassist.JavassistMockClassLoader.defineAndTransformClass(JavassistMockClassLoader.java:119)
	at org.powermock.core.classloader.MockClassLoader.loadMockClass(MockClassLoader.java:174)
	at org.powermock.core.classloader.MockClassLoader.loadClassByThisClassLoader(MockClassLoader.java:102)
	at org.powermock.core.classloader.DeferSupportingClassLoader.loadClass1(DeferSupportingClassLoader.java:147)
	at org.powermock.core.classloader.DeferSupportingClassLoader.loadClass(DeferSupportingClassLoader.java:98)
	at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
	at java.base/java.lang.Class.forName0(Native Method)
	at java.base/java.lang.Class.forName(Class.java:315)
	at javassist.runtime.Desc.getClassObject(Desc.java:72)
	at javassist.runtime.Desc.getClassType(Desc.java:181)
	at javassist.runtime.Desc.getType(Desc.java:151)
	at javassist.runtime.Desc.getType(Desc.java:107)
	at NestedClasses.<clinit>(Test.java:10)
	... 50 more
Caused by: java.lang.RuntimeException: NestedClasses$Bar$Baz class is frozen
	at javassist.CtClassType.checkModify(CtClassType.java:321)
	at javassist.CtBehavior.setModifiers(CtBehavior.java:178)
	at org.powermock.core.transformers.javassist.ConstructorsMockTransformer.transform(ConstructorsMockTransformer.java:64)
	at org.powermock.core.transformers.javassist.ConstructorsMockTransformer.transform(ConstructorsMockTransformer.java:51)
	at org.powermock.core.transformers.javassist.AbstractJavaAssistMockTransformer.transform(AbstractJavaAssistMockTransformer.java:40)
	at org.powermock.core.transformers.support.DefaultMockTransformerChain.transform(DefaultMockTransformerChain.java:43)
	at org.powermock.core.classloader.MockClassLoader.transformClass(MockClassLoader.java:184)
	at org.powermock.core.classloader.javassist.JavassistMockClassLoader.defineAndTransformClass(JavassistMockClassLoader.java:102)
	... 62 more

This seems to be related to be38fb2

There could be cases with classes having several levels of class nesting. Looks like the issue appears with the innermost nested class loaded before its outer class is mocked e.g. in case the sibling class of the outer class also uses the innermost nested class. In the reproducer test case below, the nested class Foo has a Bar.Baz as an argument. Mocking the class Foo causes a side effect of loading a Bar.Baz. Apparently, bytebuddy retrieves a method 'foo' arguments list which causes JVM to load the types of arguments.

Looks like a class load makes Bar.Baz 'frozen' in terms of javassist library, so when the Bar is processed with the logic introduced at be38fb2, the RuntimeException is thrown by javassist.

@romanatazuldotcom
Copy link
Author

The following change could help workaround the issue, I'm about to make a PR

diff --git a/powermock-core/src/main/java/org/powermock/core/transformers/javassist/ConstructorsMockTransformer.java b/powermock-core/src/main/java/org/powermock/core/transformers/javassist/ConstructorsMockTransformer.java
index eab84071..b289b72e 100644
--- a/powermock-core/src/main/java/org/powermock/core/transformers/javassist/ConstructorsMockTransformer.java
+++ b/powermock-core/src/main/java/org/powermock/core/transformers/javassist/ConstructorsMockTransformer.java
@@ -58,6 +58,16 @@ public class ConstructorsMockTransformer extends AbstractJavaAssistMockTransform

     private static void transform(final CtClass[] clazzArray) {
         for (CtClass nestedClazz : clazzArray) {
+            // nestedClazz could be already loaded hence frozen
+            // e.g. in case of a complex structure of classes with several
+            // levels of nesting. This nestedClazz might have been used e.g.
+            // in a sibling of the outer class (which is also nested)
+            // so mocking that sibling class might cause a class load of
+            // this nestedClazz as a side-effect.
+            // Workaround this by checking the frozen state and defrost.
+            if (nestedClazz.isFrozen()) {
+              nestedClazz.defrost();
+            }
             for (CtConstructor c : nestedClazz.getDeclaredConstructors()) {
                 final int modifiers = c.getModifiers();
                 if (!Modifier.isPublic(modifiers)) {

romanatazuldotcom added a commit to romanatazuldotcom/powermock that referenced this issue Apr 7, 2023
romanatazuldotcom added a commit to romanatazuldotcom/powermock that referenced this issue Apr 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant