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 } |