test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/TestAddDeleteMethods.java
changeset 54651 e6e327553024
parent 54585 3452d108d06d
equal deleted inserted replaced
54650:d9208a660094 54651:e6e327553024
    22  */
    22  */
    23 
    23 
    24 /*
    24 /*
    25  * @test
    25  * @test
    26  * @bug 8192936
    26  * @bug 8192936
       
    27  * @requires os.family != "solaris"
    27  * @summary RI does not follow the JVMTI RedefineClasses spec; need to disallow adding and deleting methods
    28  * @summary RI does not follow the JVMTI RedefineClasses spec; need to disallow adding and deleting methods
    28  * @library /test/lib
    29  * @library /test/lib
    29  * @modules java.base/jdk.internal.misc
    30  * @modules java.base/jdk.internal.misc
    30  * @modules java.compiler
    31  * @modules java.compiler
    31  *          java.instrument
    32  *          java.instrument
    32  *          jdk.jartool/sun.tools.jar
    33  *          jdk.jartool/sun.tools.jar
    33  * @run main RedefineClassHelper
    34  * @run main RedefineClassHelper
    34  * @run main/othervm -javaagent:redefineagent.jar TestAddDeleteMethods
    35  * @run main/othervm -javaagent:redefineagent.jar TestAddDeleteMethods AllowAddDelete=no
       
    36  * @run main/othervm -javaagent:redefineagent.jar -XX:+AllowRedefinitionToAddDeleteMethods TestAddDeleteMethods AllowAddDelete=yes
    35  */
    37  */
    36 
    38 
    37 import static jdk.test.lib.Asserts.assertEquals;
    39 import static jdk.test.lib.Asserts.assertEquals;
       
    40 import java.lang.Runnable;
    38 
    41 
    39 // package access top-level class to avoid problem with RedefineClassHelper
    42 // package access top-level class to avoid problem with RedefineClassHelper
    40 // and nested types.
    43 // and nested types.
    41 class A {
    44 class A implements Runnable {
    42     private static void foo()       { System.out.println("OLD foo called"); }
    45     private        void foo()       { System.out.println(" OLD foo called"); }
    43     private final  void finalFoo()  { System.out.println("OLD finalFoo called"); }
    46     public         void publicFoo() { System.out.println(" OLD publicFoo called"); }
    44     public         void publicFoo() { foo(); finalFoo(); }
    47     private final  void finalFoo()  { System.out.println(" OLD finalFoo called");  }
       
    48     private static void staticFoo() { System.out.println(" OLD staticFoo called"); }
       
    49     public         void run()       { foo(); publicFoo(); finalFoo(); staticFoo(); }
    45 }
    50 }
    46 
    51 
       
    52 class B implements Runnable {
       
    53     public         void run() { }
       
    54 }
       
    55 
    47 public class TestAddDeleteMethods {
    56 public class TestAddDeleteMethods {
    48     static A a;
    57     static private boolean allowAddDeleteMethods = false;
    49 
    58 
       
    59     static private A a;
       
    60     static private B b;
       
    61 
       
    62     // This redefinition is expected to always succeed.
    50     public static String newA =
    63     public static String newA =
    51         "class A {" +
    64         "class A implements Runnable {" +
    52             "private static void foo()       { System.out.println(\"NEW foo called\"); }" +
    65             "private        void foo()       { System.out.println(\" NEW foo called\"); }" +
    53             "private final  void finalFoo()  { System.out.println(\"NEW finalFoo called\"); }" +
    66             "public         void publicFoo() { System.out.println(\" NEW publicFoo called\"); }" +
    54             "public         void publicFoo() { foo(); finalFoo(); }" +
    67             "private final  void finalFoo()  { System.out.println(\" NEW finalFoo called\");  }" +
    55         "}";
    68             "private static void staticFoo() { System.out.println(\" NEW staticFoo called\"); }" +
    56 
    69             "public         void run()       { foo(); publicFoo(); finalFoo(); staticFoo(); }" +
    57     public static String newAddBar =
    70         "}";
    58         "class A {" +
    71 
    59             "private        void bar()       { System.out.println(\"NEW bar called\"); }" +
    72     // This redefinition is expected to always fail.
    60             "private static void foo()       { System.out.println(\"NEW foo called\"); }" +
    73     public static String ADeleteFoo =
    61             "private final  void finalFoo()  { System.out.println(\"NEW finalFoo called\"); }" +
    74         "class A implements Runnable {" +
    62             "public         void publicFoo() { foo(); bar(); finalFoo(); }" +
    75             "public         void publicFoo() { System.out.println(\" NEW publicFoo called\"); }" +
    63         "}";
    76             "private final  void finalFoo()  { System.out.println(\" NEW finalFoo called\");  }" +
    64 
    77             "private static void staticFoo() { System.out.println(\" NEW staticFoo called\"); }" +
    65     public static String newAddFinalBar =
    78             "public         void run()       { publicFoo(); finalFoo(); staticFoo(); }" +
    66         "class A {" +
    79         "}";
    67             "private final  void bar()       { System.out.println(\"NEW bar called\"); }" +
    80 
    68             "private static void foo()       { System.out.println(\"NEW foo called\"); }" +
    81     // This redefinition is expected to always fail.
    69             "private final  void finalFoo()  { System.out.println(\"NEW finalFoo called\"); }" +
    82     public static String ADeletePublicFoo =
    70             "public         void publicFoo() { foo(); bar(); finalFoo(); }" +
    83         "class A implements Runnable {" +
    71         "}";
    84             "private        void foo()       { System.out.println(\" NEW foo called\"); }" +
    72 
    85             "private final  void finalFoo()  { System.out.println(\" NEW finalFoo called\");  }" +
    73     public static String newAddPublicBar =
    86             "private static void staticFoo() { System.out.println(\" NEW staticFoo called\"); }" +
    74         "class A {" +
    87             "public         void run()       { foo(); finalFoo(); staticFoo(); }" +
    75             "public         void bar()       { System.out.println(\"NEW public bar called\"); }" +
    88         "}";
    76             "private static void foo()       { System.out.println(\"NEW foo called\"); }" +
    89 
    77             "private final  void finalFoo()  { System.out.println(\"NEW finalFoo called\"); }" +
    90     // This redefinition is expected to succeed with option -XX:+AllowRedefinitionToAddDeleteMethods.
    78             "public         void publicFoo() { foo(); bar(); finalFoo(); }" +
    91     public static String ADeleteFinalFoo =
    79         "}";
    92         "class A implements Runnable {" +
    80 
    93             "private        void foo()       { System.out.println(\" NEW foo called\"); }" +
    81     public static String newDeleteFoo =
    94             "public         void publicFoo() { System.out.println(\" NEW publicFoo called\"); }" +
    82         "class A {" +
    95             "private static void staticFoo() { System.out.println(\" NEW staticFoo called\"); }" +
    83             "private final  void finalFoo()  { System.out.println(\"NEW finalFoo called\"); }" +
    96             "public         void run()       { foo(); publicFoo(); staticFoo(); }" +
    84             "public         void publicFoo() { finalFoo(); }" +
    97         "}";
    85         "}";
    98 
    86 
    99     // This redefinition is expected to succeed with option -XX:+AllowRedefinitionToAddDeleteMethods.
    87     public static String newDeleteFinalFoo =
   100     // With compatibility option redefinition ADeleteFinalFoo already deleted finalFoo method.
    88         "class A {" +
   101     // So, this redefinition will add it back which is expected to work.
    89             "private static void foo()       { System.out.println(\"NEW foo called\"); }" +
   102     public static String ADeleteStaticFoo =
    90             "public         void publicFoo() { foo(); }" +
   103         "class A implements Runnable {" +
    91         "}";
   104             "private        void foo()       { System.out.println(\" NEW foo called\"); }" +
    92 
   105             "public         void publicFoo() { System.out.println(\" NEW publicFoo called\"); }" +
    93     public static String newDeletePublicFoo =
   106             "private final  void finalFoo()  { System.out.println(\" NEW finalFoo called\");  }" +
    94         "class A {" +
   107             "public         void run()       { foo(); publicFoo(); finalFoo(); }" +
    95             "private static void foo()       { System.out.println(\"NEW foo called\"); }" +
   108         "}";
    96             "private final  void finalFoo()  { System.out.println(\"NEW finalFoo called\"); }" +
   109 
       
   110     // This redefinition is expected to always fail.
       
   111     public static String BAddBar =
       
   112         "class B implements Runnable {" +
       
   113             "private        void bar()       { System.out.println(\" bar called\"); }" +
       
   114             "public         void run()       { bar(); }" +
       
   115         "}";
       
   116 
       
   117     // This redefinition is expected to always fail.
       
   118     public static String BAddPublicBar =
       
   119         "class B implements Runnable {" +
       
   120             "public         void publicBar() { System.out.println(\" publicBar called\"); }" +
       
   121             "public         void run()       { publicBar(); }" +
       
   122         "}";
       
   123 
       
   124     // This redefinition is expected to succeed with option -XX:+AllowRedefinitionToAddDeleteMethods.
       
   125     public static String BAddFinalBar =
       
   126         "class B implements Runnable {" +
       
   127             "private final  void finalBar()  { System.out.println(\" finalBar called\"); }" +
       
   128             "public         void run()       { finalBar(); }" +
       
   129         "}";
       
   130 
       
   131     // This redefinition is expected to succeed with option -XX:+AllowRedefinitionToAddDeleteMethods.
       
   132     // With compatibility option redefinition BAddFinalBar added finalBar method.
       
   133     // So, this redefinition will deleate it back which is expected to work.
       
   134     public static String BAddStaticBar =
       
   135         "class B implements Runnable {" +
       
   136             "private static void staticBar() { System.out.println(\" staticBar called\"); }" +
       
   137             "public         void run()       { staticBar(); }" +
    97         "}";
   138         "}";
    98 
   139 
    99     static private final String ExpMsgPrefix = "attempted to ";
   140     static private final String ExpMsgPrefix = "attempted to ";
   100     static private final String ExpMsgPostfix = " a method";
   141     static private final String ExpMsgPostfix = " a method";
   101 
   142 
   102     public static void test(String newBytes, String expSuffix) throws Exception {
   143     static private void log(String msg) { System.out.println(msg); }
       
   144 
       
   145     public static void test(Runnable obj, String newBytes, String expSuffix, String methodName,
       
   146                             boolean expectedRedefToPass) throws Exception {
   103         String expectedMessage = ExpMsgPrefix + expSuffix + ExpMsgPostfix;
   147         String expectedMessage = ExpMsgPrefix + expSuffix + ExpMsgPostfix;
       
   148         Class klass = obj.getClass();
       
   149         String className = klass.getName();
       
   150         String expResult = expectedRedefToPass ? "PASS" : "FAIL";
       
   151 
       
   152         log("");
       
   153         log("## Test " + expSuffix + " method \'" + methodName + "\' in class " + className +
       
   154             "; redefinition expected to " + expResult);
   104 
   155 
   105         try {
   156         try {
   106             RedefineClassHelper.redefineClass(A.class, newBytes);
   157             RedefineClassHelper.redefineClass(klass, newBytes);
   107             a.publicFoo();
   158 
   108             throw new RuntimeException("Failed, expected UOE");
   159             if (expectedRedefToPass) {
       
   160                 log(" Did not get UOE at redefinition as expected");
       
   161             } else {
       
   162                 throw new RuntimeException("Failed, expected UOE");
       
   163             }
       
   164             obj.run();
       
   165             log("");
   109         } catch (UnsupportedOperationException uoe) {
   166         } catch (UnsupportedOperationException uoe) {
   110             String message = uoe.getMessage();
   167             String message = uoe.getMessage();
   111             System.out.println("Got expected UOE " + message);
   168 
   112             if (!message.endsWith(expectedMessage)) {
   169             if (expectedRedefToPass) {
   113                 throw new RuntimeException("Expected UOE error message to end with: " + expectedMessage);
   170                 throw new RuntimeException("Failed, unexpected UOE: " + message);
       
   171             } else {
       
   172                 log(" Got expected UOE: " + message);
       
   173                 if (!message.endsWith(expectedMessage)) {
       
   174                     throw new RuntimeException("Expected UOE error message to end with: " + expectedMessage);
       
   175                 }
   114             }
   176             }
   115         }
   177         }
   116     }
   178     }
   117 
   179 
   118     static {
   180     static {
   119         a = new A();
   181         a = new A();
       
   182         b = new B();
   120     }
   183     }
   121 
   184 
   122     public static void main(String[] args) throws Exception {
   185     public static void main(String[] args) throws Exception {
   123 
   186         if (args.length > 0 && args[0].equals("AllowAddDelete=yes")) {
   124         a.publicFoo();
   187             allowAddDeleteMethods = true;
   125 
   188         }
   126         // Should pass because this only changes bytes of methods.
   189 
       
   190         log("## Test original class A");
       
   191         a.run();
       
   192         log("");
       
   193 
       
   194         log("## Test with modified method bodies in class A; redefinition expected to pass: true");
   127         RedefineClassHelper.redefineClass(A.class, newA);
   195         RedefineClassHelper.redefineClass(A.class, newA);
   128         a.publicFoo();
   196         a.run();
   129 
   197 
   130         // Add private static bar
   198         test(a, ADeleteFoo,       "delete", "foo",       false);
   131         test(newAddBar,          "add");
   199         test(a, ADeletePublicFoo, "delete", "publicFoo", false);
   132         test(newAddFinalBar,     "add");
   200         test(a, ADeleteFinalFoo,  "delete", "finalFoo",  allowAddDeleteMethods);
   133         test(newAddPublicBar,    "add");
   201         test(a, ADeleteStaticFoo, "delete", "staticFoo", allowAddDeleteMethods);
   134         test(newDeleteFoo,       "delete");
   202 
   135         test(newDeleteFinalFoo,  "delete");
   203         test(b, BAddBar,          "add", "bar",       false);
   136         test(newDeletePublicFoo, "delete");
   204         test(b, BAddPublicBar,    "add", "publicBar", false);
       
   205         test(b, BAddFinalBar,     "add", "finalBar",  allowAddDeleteMethods);
       
   206         test(b, BAddStaticBar,    "add", "staticBar", allowAddDeleteMethods);
   137     }
   207     }
   138 }
   208 }