jboss-javassist / javassist

Java bytecode engineering toolkit
www.javassist.org
Other
4.1k stars 693 forks source link

Created class looses package information when modifier comes from different package #217

Open t-8ch opened 6 years ago

t-8ch commented 6 years ago

When creating a new class (by modifying an existing one) the newly created class looses its package information. This only happens when the class using javassist is in a different package than the modified class.

When the class runs a static initializer using the package information this will result in an ExceptionInInitializerError

Reproduction case (tested with 3.23.1-GA and current git master (a3d1aa25133c2ec473e9b3ebe2bfa5b4ebe5759e): https://github.com/t-8ch/javassist-issue-lost-package-information

(Feel free to change the subject of the issue, I am not sure how to summarize it nicely)

NingZhang-e commented 6 years ago

Could you change JAVA environment to Java9 or Java10? It should be caused <Java8 defineClass issue. Such as add below snippet in pom.

<build>
  <plugins>
    <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.3</version>
        <configuration>
          <source>10</source>
          <target>10</target>
        </configuration>
      </plugin>
   </plugins>
</build>
t-8ch commented 6 years ago

Indeed this works. Unfortunately I am limited to Java8 in this project for the time being. Feel free to close the issue if it is out of scope.

NingZhang-e commented 6 years ago

Maybe my suggestion was not so precise.
No need to change source/target to 10. Only need to make sure JRE is Java9/10.

t-8ch commented 6 years ago

Well, I am limited to JRE 8. The project is a plugin for a third-party software which does not work with anything else.

NingZhang-e commented 6 years ago

Aha, there is some tricky workaround. You could create one instance before you use javassist to manipulate it. Like:

new ClassWithStaticInitializer();
...
String name = "x.x.x.ClassWithStaticInitializer";
ClassPool cp = new ClassPool(true);
CtClass ctClass = cp.get(name);

Class<?> c = ctClass.toClass();
t-8ch commented 6 years ago

Gives me

javassist.CannotCompileException: by java.lang.ClassFormatError: loader (instance of  sun/misc/Launcher$AppClassLoader): attempted  duplicate class definition for name: "x/x/x/ClassWithStaticInitializer"
    at javassist.util.proxy.DefineClassHelper.toClass(DefineClassHelper.java:250)
    at javassist.ClassPool.toClass(ClassPool.java:1120)
    at javassist.ClassPool.toClass(ClassPool.java:1083)
    at javassist.ClassPool.toClass(ClassPool.java:1041)
    at javassist.CtClass.toClass(CtClass.java:1278)
    at a.b.PatchingTest.testClassInitialization(PatchingTest.java:37)

(I had to use the code from master because of #209)

NingZhang-e commented 6 years ago

Aha, I made a mistake by typo. I created one empty class ClassWithStaticInitializer1 under x.x.x It should be new ClassWithStaticInitializer1();

BTW, has fix of #209 resolved your issue? Seems it only expose the real exception.

t-8ch commented 6 years ago

That works indeed. Thanks! (This also explains why it works for classes in the same package as the modifying class)

Depends on what you mean by "your issue". When running into the duplicate class declaration without the fix from master I only got a nondescript NPE while creating the CannotCompileException. With the fix I got the useful error I posted here.

Is this issue something that can/should be fixed in Javassist?

NingZhang-e commented 6 years ago

Currently, it will report CannotCompileException if you have something like class.getPackage() in your code. Not sure if you have workaround. And this issue will be gone after switching to Java9/10. I think @chibash is the one can decide if it should be fixed for <Java8 in Javassist.

chibash commented 6 years ago

I'll fix it if there is a good way to solve the problem in Java 8. This seems a sort of bug of Class#getPackage() in Java 8.