Details

    • Type: Bug
    • Status: Resolved (View Workflow)
    • Priority: Major
    • Resolution: Done
    • Affects Version/s: 3.20.0-GA
    • Fix Version/s: 3.22.0-CR1
    • Labels:
      None
    • Environment:

      Javassist with jdk 9b112

      Description

      I am migrating a project to java 9, which also uses javassist to generate runtime code.
      One test of mine fails on jdk 9b112 while it passes on jdk 8u77.

      import static javassist.CtClass.voidType;
      
      import java.lang.reflect.Method;
      import java.lang.reflect.Modifier;
      import java.util.HashMap;
      import java.util.Map;
      
      import org.junit.Test;
      
      import javassist.ClassClassPath;
      import javassist.ClassPool;
      import javassist.CtClass;
      import javassist.CtField;
      import javassist.CtMethod;
      import javassist.CtNewMethod;
      
      public class MyTests {
      
          public static class MyObject {
              protected Object field;
              Object getField() {return field;}
              public void setField(Object field) {}
          }
      
          @Test
          public void test() throws InstantiationException, IllegalAccessException {
              Class<? extends MyObject> clazz = compile(MyObject.class);
              clazz.newInstance().setField(null);
          }
      
          /** Compile a transfer class */
          public static synchronized Class<? extends MyObject> compile(Class<?> targetClass) {
      
              // Determine class setters
              Map<String, Method> setters = extractSetters(targetClass);
      
              ClassPool classPool = ClassPool.getDefault();
              classPool.insertClassPath(new ClassClassPath(targetClass));
      
              try {
      
                  // Compile a new transfer class on the fly
                  CtClass baseClass = classPool.get(MyObject.class.getName());
                  CtClass proxyClass = classPool.makeClass(targetClass.getName() + "_Modified", baseClass);
      
                  for(Method originalSetter : setters.values()) {
                      // Create a field to hold the attribute
                      Class<?> fieldClass = originalSetter.getParameterTypes()[0];
                      CtClass fieldType = classPool.get(fieldClass.getName());
                      String fieldName = originalSetter.getName().substring(3);
                      CtField field = new CtField(fieldType, fieldName, proxyClass);
                      proxyClass.addField(field);
      
                      // Create a setter method to set that field
                      CtClass[] parameters = new CtClass[] { fieldType };
                      String setterBody = "{ System.out.println(\"Hello World\"); }";
                      CtMethod setter = CtNewMethod.make(voidType, originalSetter.getName(), parameters, new CtClass[0], setterBody, proxyClass);
                      proxyClass.addMethod(setter);
                  }
      
                  Class<? extends MyObject> javaClass = proxyClass.toClass(targetClass.getClassLoader(), targetClass.getProtectionDomain());
      
                  return javaClass;
      
              } catch(Exception e) {
                  throw new RuntimeException("Failure during transfer compilation for " + targetClass, e);
              }
          }
      
      
          /** Extract setter methods from a class */
          public static Map<String, Method> extractSetters(Class<?> cls) {
      
              Map<String, Method> setters = new HashMap<String, Method>();
              for(Method method : cls.getMethods()) {
                  // Lookup setter methods
                  if(method.getName().startsWith("set")) {
                      // Only public setters
                      int modifiers = method.getModifiers();
                      if(Modifier.isPublic(modifiers)) {
                          Class<?>[] exceptions = method.getExceptionTypes();
                          Class<?>[] parameters = method.getParameterTypes();
                          Class<?> returnType = method.getReturnType();
                          if(exceptions.length <= 0 && parameters.length == 1 && "void".equals(returnType.getName())) {
                              setters.put(method.getName(), method);
                          }
                      }
                  }
              }
              return setters;
          }
      }
      

      On jdk 8u77, the compile() function returns with success and "Hello world" is printed to the console.
      On jdk 9b112, I got the following exception

      java.lang.RuntimeException: Failure during transfer compilation for class MyTests$MyObject
          at MyTests.compile(MyTests.java:68)
          at MyTests.test(MyTests.java:29)
          at sun.reflect.NativeMethodAccessorImpl.invoke0(java.base@9-ea/Native Method)
          at sun.reflect.NativeMethodAccessorImpl.invoke(java.base@9-ea/NativeMethodAccessorImpl.java:62)
          at sun.reflect.DelegatingMethodAccessorImpl.invoke(java.base@9-ea/DelegatingMethodAccessorImpl.java:43)
          at java.lang.reflect.Method.invoke(java.base@9-ea/Method.java:531)
          at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
          at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
          at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
          at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
          at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
          at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
          at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
          at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
          at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
          at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
          at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
          at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
          at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
          at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
          at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
          at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
          at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:670)
          at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
          at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
      Caused by: javassist.NotFoundException: java.lang.Object
          at javassist.ClassPool.get(ClassPool.java:450)
          at MyTests.compile(MyTests.java:51)
          ... 24 more
      

      I suspect that is due to the jigsaw integration into the jdk.

        Gliffy Diagrams

          Attachments

            Activity

              People

              • Assignee:
                chiba Shigeru Chiba
                Reporter:
                thchuong Hoang Chuong Tran
              • Votes:
                0 Vote for this issue
                Watchers:
                10 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved: