1 /*
  2  * Copyright (c) 2025, 2026, 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  * @summary Redefinition tests that larval_frame stackmaps are adjusted
 27  * @enablePreview
 28  * @library /test/lib
 29  * @run main RedefineClassHelper
 30  * @compile StrictFieldsOld.java StrictFieldsNew.java
 31  * @run driver jdk.test.lib.helpers.StrictProcessor
 32  *             StrictFieldsOld StrictFieldsNew
 33  * @run main/othervm -Xverify:remote -javaagent:redefineagent.jar RedefineStrictFieldsTest
 34  */
 35 
 36 import java.io.InputStream;
 37 import static jdk.test.lib.Asserts.assertTrue;
 38 
 39 // Must be placed in top-level package to avoid issues with RedefineClassHelper.
 40 public class RedefineStrictFieldsTest {
 41 
 42     // All of this should be moved to RedefineClassHelper
 43     private static byte[] getBytecodes(Class<?> thisClass, String name) throws Exception {
 44         InputStream is = thisClass.getClassLoader().getResourceAsStream(name + ".class");
 45         byte[] buf = is.readAllBytes();
 46         System.out.println("sizeof(" + name + ".class) == " + buf.length);
 47         return buf;
 48     }
 49 
 50     private static int getStringIndex(String needle, byte[] buf) {
 51         return getStringIndex(needle, buf, 0);
 52     }
 53 
 54     private static int getStringIndex(String needle, byte[] buf, int offset) {
 55         outer:
 56         for (int i = offset; i < buf.length - offset - needle.length(); i++) {
 57             for (int j = 0; j < needle.length(); j++) {
 58                 if (buf[i + j] != (byte)needle.charAt(j)) continue outer;
 59             }
 60             return i;
 61         }
 62         return 0;
 63     }
 64 
 65     private static void replaceString(byte[] buf, String name, int index) {
 66         for (int i = index; i < index + name.length(); i++) {
 67             buf[i] = (byte)name.charAt(i - index);
 68         }
 69     }
 70 
 71     private static byte[] replaceAllStrings(String oldString, String newString) throws Exception {
 72         byte [] buf = getBytecodes(RedefineStrictFieldsTest.class, oldString);
 73         assertTrue(oldString.length() == newString.length(), "must have same length");
 74         int index = -1;
 75         while ((index = getStringIndex(oldString, buf, index + 1)) != 0) {
 76             replaceString(buf, newString, index);
 77         }
 78         return buf;
 79     }
 80 
 81     // This should fail because x and y are no longer strict.
 82     static String newClassBytes = "class StrictFieldsOld { " +
 83                                      "int x; int y; " +
 84                                      "StrictFieldsOld(boolean a, boolean b) { }" +
 85                                      "public void foo() { System.out.println(x + y );}}";
 86 
 87     public static void main(java.lang.String[] unused) throws Exception {
 88 
 89         StrictFieldsOld old = new StrictFieldsOld(true, false);
 90         old.foo();
 91 
 92         // Rename class "StrictFieldsNew" to "StrictFieldsOld" so we can redefine it.
 93         byte [] buf = replaceAllStrings("StrictFieldsNew", "StrictFieldsOld");
 94         // Now redefine the original version.
 95         // If the stackmaps aren't rewritten to point to new constant pool indices, this should get a VerifyError
 96         // which RedefineClasses eats and makes into an InternalError.  Either way, this test will fail.
 97         RedefineClassHelper.redefineClass(StrictFieldsOld.class, buf);
 98         StrictFieldsOld newOld = new StrictFieldsOld(true, false);
 99         newOld.foo();  // should call the new foo
100 
101         // Redefine class without strict fields. Should get a redefinition error.
102         try {
103             RedefineClassHelper.redefineClass(StrictFieldsOld.class, newClassBytes);
104             StrictFieldsOld shouldThrow = new StrictFieldsOld(false, false);
105             shouldThrow.foo();  // Should not be called.
106             throw new RuntimeException("Redefinition should have failed");
107         } catch (java.lang.UnsupportedOperationException uoe) {
108             String msg = uoe.getMessage();
109             assertTrue(msg.contains("class redefinition failed: attempted to change the schema (add/remove fields)"), "FAILED");
110         }
111 
112     }
113 }