1 /* 2 * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8192936 27 * @summary RI does not follow the JVMTI RedefineClasses spec; need to disallow adding and deleting methods 28 * @library /test/lib 29 * @modules java.base/jdk.internal.misc 30 * @modules java.compiler 31 * java.instrument 32 * jdk.jartool/sun.tools.jar 33 * @run main RedefineClassHelper 34 * @run main/othervm -javaagent:redefineagent.jar TestAddDeleteMethods AllowAddDelete=no 35 * @run main/othervm -javaagent:redefineagent.jar -XX:+AllowRedefinitionToAddDeleteMethods TestAddDeleteMethods AllowAddDelete=yes 36 */ 37 38 import static jdk.test.lib.Asserts.assertEquals; 39 import java.lang.Runnable; 40 41 // package access top-level class to avoid problem with RedefineClassHelper 42 // and nested types. 43 class A implements Runnable { 44 private void foo() { System.out.println(" OLD foo called"); } 45 public void publicFoo() { System.out.println(" OLD publicFoo called"); } 46 private final void finalFoo() { System.out.println(" OLD finalFoo called"); } 47 private static void staticFoo() { System.out.println(" OLD staticFoo called"); } 48 public void run() { foo(); publicFoo(); finalFoo(); staticFoo(); } 49 } 50 51 class B implements Runnable { 52 public void run() { } 53 } 54 55 public class TestAddDeleteMethods { 56 static private boolean allowAddDeleteMethods = false; 57 58 static private A a; 59 static private B b; 60 61 // This redefinition is expected to always succeed. 62 public static String newA = 63 "class A implements Runnable {" + 64 "private void foo() { System.out.println(\" NEW foo called\"); }" + 65 "public void publicFoo() { System.out.println(\" NEW publicFoo called\"); }" + 66 "private final void finalFoo() { System.out.println(\" NEW finalFoo called\"); }" + 67 "private static void staticFoo() { System.out.println(\" NEW staticFoo called\"); }" + 68 "public void run() { foo(); publicFoo(); finalFoo(); staticFoo(); }" + 69 "}"; 70 71 // This redefinition is expected to always fail. 72 public static String ADeleteFoo = 73 "class A implements Runnable {" + 74 "public void publicFoo() { System.out.println(\" NEW publicFoo called\"); }" + 75 "private final void finalFoo() { System.out.println(\" NEW finalFoo called\"); }" + 76 "private static void staticFoo() { System.out.println(\" NEW staticFoo called\"); }" + 77 "public void run() { publicFoo(); finalFoo(); staticFoo(); }" + 78 "}"; 79 80 // This redefinition is expected to always fail. 81 public static String ADeletePublicFoo = 82 "class A implements Runnable {" + 83 "private void foo() { System.out.println(\" NEW foo called\"); }" + 84 "private final void finalFoo() { System.out.println(\" NEW finalFoo called\"); }" + 85 "private static void staticFoo() { System.out.println(\" NEW staticFoo called\"); }" + 86 "public void run() { foo(); finalFoo(); staticFoo(); }" + 87 "}"; 88 89 // This redefinition is expected to succeed with option -XX:+AllowRedefinitionToAddDeleteMethods. 90 public static String ADeleteFinalFoo = 91 "class A implements Runnable {" + 92 "private void foo() { System.out.println(\" NEW foo called\"); }" + 93 "public void publicFoo() { System.out.println(\" NEW publicFoo called\"); }" + 94 "private static void staticFoo() { System.out.println(\" NEW staticFoo called\"); }" + 95 "public void run() { foo(); publicFoo(); staticFoo(); }" + 96 "}"; 97 98 // This redefinition is expected to succeed with option -XX:+AllowRedefinitionToAddDeleteMethods. 99 // With compatibility option redefinition ADeleteFinalFoo already deleted finalFoo method. 100 // So, this redefinition will add it back which is expected to work. 101 public static String ADeleteStaticFoo = 102 "class A implements Runnable {" + 103 "private void foo() { System.out.println(\" NEW foo called\"); }" + 104 "public void publicFoo() { System.out.println(\" NEW publicFoo called\"); }" + 105 "private final void finalFoo() { System.out.println(\" NEW finalFoo called\"); }" + 106 "public void run() { foo(); publicFoo(); finalFoo(); }" + 107 "}"; 108 109 // This redefinition is expected to always fail. 110 public static String BAddBar = 111 "class B implements Runnable {" + 112 "private void bar() { System.out.println(\" bar called\"); }" + 113 "public void run() { bar(); }" + 114 "}"; 115 116 // This redefinition is expected to always fail. 117 public static String BAddPublicBar = 118 "class B implements Runnable {" + 119 "public void publicBar() { System.out.println(\" publicBar called\"); }" + 120 "public void run() { publicBar(); }" + 121 "}"; 122 123 // This redefinition is expected to succeed with option -XX:+AllowRedefinitionToAddDeleteMethods. 124 public static String BAddFinalBar = 125 "class B implements Runnable {" + 126 "private final void finalBar() { System.out.println(\" finalBar called\"); }" + 127 "public void run() { finalBar(); }" + 128 "}"; 129 130 // This redefinition is expected to succeed with option -XX:+AllowRedefinitionToAddDeleteMethods. 131 // With compatibility option redefinition BAddFinalBar added finalBar method. 132 // So, this redefinition will deleate it back which is expected to work. 133 public static String BAddStaticBar = 134 "class B implements Runnable {" + 135 "private static void staticBar() { System.out.println(\" staticBar called\"); }" + 136 "public void run() { staticBar(); }" + 137 "}"; 138 139 static private final String ExpMsgPrefix = "attempted to "; 140 static private final String ExpMsgPostfix = " a method"; 141 142 static private void log(String msg) { System.out.println(msg); } 143 144 public static void test(Runnable obj, String newBytes, String expSuffix, String methodName, 145 boolean expectedRedefToPass) throws Exception { 146 String expectedMessage = ExpMsgPrefix + expSuffix + ExpMsgPostfix; 147 Class klass = obj.getClass(); 148 String className = klass.getName(); 149 String expResult = expectedRedefToPass ? "PASS" : "FAIL"; 150 151 log(""); 152 log("## Test " + expSuffix + " method \'" + methodName + "\' in class " + className + 153 "; redefinition expected to " + expResult); 154 155 try { 156 RedefineClassHelper.redefineClass(klass, newBytes); 157 158 if (expectedRedefToPass) { 159 log(" Did not get UOE at redefinition as expected"); 160 } else { 161 throw new RuntimeException("Failed, expected UOE"); 162 } 163 obj.run(); 164 log(""); 165 } catch (UnsupportedOperationException uoe) { 166 String message = uoe.getMessage(); 167 168 if (expectedRedefToPass) { 169 throw new RuntimeException("Failed, unexpected UOE: " + message); 170 } else { 171 log(" Got expected UOE: " + message); 172 if (!message.endsWith(expectedMessage)) { 173 throw new RuntimeException("Expected UOE error message to end with: " + expectedMessage); 174 } 175 } 176 } 177 } 178 179 static { 180 a = new A(); 181 b = new B(); 182 } 183 184 public static void main(String[] args) throws Exception { 185 if (args.length > 0 && args[0].equals("AllowAddDelete=yes")) { 186 allowAddDeleteMethods = true; 187 } 188 189 log("## Test original class A"); 190 a.run(); 191 log(""); 192 193 log("## Test with modified method bodies in class A; redefinition expected to pass: true"); 194 RedefineClassHelper.redefineClass(A.class, newA); 195 a.run(); 196 197 test(a, ADeleteFoo, "delete", "foo", false); 198 test(a, ADeletePublicFoo, "delete", "publicFoo", false); 199 test(a, ADeleteFinalFoo, "delete", "finalFoo", allowAddDeleteMethods); 200 test(a, ADeleteStaticFoo, "delete", "staticFoo", allowAddDeleteMethods); 201 202 test(b, BAddBar, "add", "bar", false); 203 test(b, BAddPublicBar, "add", "publicBar", false); 204 test(b, BAddFinalBar, "add", "finalBar", allowAddDeleteMethods); 205 test(b, BAddStaticBar, "add", "staticBar", allowAddDeleteMethods); 206 } 207 }