diff --git a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/ClassVersionAfterRedefine.java b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/ClassVersionAfterRedefine.java index cc601147714..7e571f7703b 100644 --- a/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/ClassVersionAfterRedefine.java +++ b/test/hotspot/jtreg/serviceability/jvmti/RedefineClasses/ClassVersionAfterRedefine.java @@ -32,68 +32,27 @@ * @run main/othervm -javaagent:redefineagent.jar ClassVersionAfterRedefine */ -import java.io.InputStream; import java.lang.reflect.Method; import static jdk.test.lib.Asserts.assertTrue; public class ClassVersionAfterRedefine extends ClassLoader { - private static String myName = ClassVersionAfterRedefine.class.getName(); - - private static byte[] getBytecodes(String name) throws Exception { - InputStream is = ClassVersionAfterRedefine.class.getResourceAsStream(name + ".class"); - byte[] buf = is.readAllBytes(); - System.out.println("sizeof(" + name + ".class) == " + buf.length); - return buf; - } - - private static int getStringIndex(String needle, byte[] buf) { - return getStringIndex(needle, buf, 0); - } - - private static int getStringIndex(String needle, byte[] buf, int offset) { - outer: - for (int i = offset; i < buf.length - offset - needle.length(); i++) { - for (int j = 0; j < needle.length(); j++) { - if (buf[i + j] != (byte)needle.charAt(j)) continue outer; - } - return i; - } - return 0; - } - - private static void replaceString(byte[] buf, String name, int index) { - for (int i = index; i < index + name.length(); i++) { - buf[i] = (byte)name.charAt(i - index); - } - } - - private static void replaceAllStrings(byte[] buf, String oldString, String newString) throws Exception { - assertTrue(oldString.length() == newString.length(), "must have same length"); - int index = -1; - while ((index = getStringIndex(oldString, buf, index + 1)) != 0) { - replaceString(buf, newString, index); - } - } - public static void main(String[] s) throws Exception { - byte[] buf = getBytecodes("TestClassOld"); - // Poor man's renaming of class "TestClassOld" to "TestClassXXX" - replaceAllStrings(buf, "TestClassOld", "TestClassXXX"); ClassVersionAfterRedefine cvar = new ClassVersionAfterRedefine(); + + byte[] buf = RedefineClassHelper.replaceClassName(cvar, "TestClassOld", "TestClassXXX"); Class old = cvar.defineClass(null, buf, 0, buf.length); Method foo = old.getMethod("foo"); Object result = foo.invoke(null); assertTrue("java-lang-String".equals(result)); System.out.println(old.getSimpleName() + ".foo() = " + result); - buf = getBytecodes("TestClassNew"); // Rename class "TestClassNew" to "TestClassXXX" so we can use it for // redefining the original version of "TestClassXXX" (i.e. "TestClassOld"). - replaceAllStrings(buf, "TestClassNew", "TestClassXXX"); - // Now redine the original version of "TestClassXXX" (i.e. "TestClassOld"). + buf = RedefineClassHelper.replaceClassName(cvar, "TestClassNew", "TestClassXXX"); + // Now redefine the original version of "TestClassXXX" (i.e. "TestClassOld"). RedefineClassHelper.redefineClass(old, buf); result = foo.invoke(null); assertTrue("java.lang.String".equals(result)); diff --git a/test/lib/RedefineClassHelper.java b/test/lib/RedefineClassHelper.java index 88f31f8ba8f..ce27fb33f44 100644 --- a/test/lib/RedefineClassHelper.java +++ b/test/lib/RedefineClassHelper.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2025, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -21,8 +21,14 @@ * questions. */ -import java.lang.instrument.Instrumentation; +import java.io.InputStream; +import java.lang.classfile.ClassElement; +import java.lang.classfile.ClassFile; +import java.lang.classfile.ClassModel; +import java.lang.constant.ClassDesc; + import java.lang.instrument.ClassDefinition; +import java.lang.instrument.Instrumentation; import jdk.test.lib.compiler.InMemoryJavaCompiler; import jdk.test.lib.helpers.ClassFileInstaller; @@ -33,6 +39,7 @@ import jdk.test.lib.helpers.ClassFileInstaller; * * See sample test in test/testlibrary_tests/RedefineClassTest.java */ + public class RedefineClassHelper { public static Instrumentation instrumentation; @@ -61,6 +68,41 @@ public class RedefineClassHelper { instrumentation.redefineClasses(new ClassDefinition(clazz, bytecode)); } + private static byte[] getBytecodes(ClassLoader loader, String name) throws Exception { + try (InputStream is = loader.getResourceAsStream(name + ".class")) { + byte[] buf = is.readAllBytes(); + System.out.println("sizeof(" + name + ".class) == " + buf.length); + return buf; + } + } + + /* + * Copy the class defined by `bytes`, replacing the name of the class with `newClassName`, + * so that both old and new classes can be compiled by jtreg for the test. + * + * @param bytes read from the original class file. + * @param newClassName new class name for the returned class representation + * @return a copy of the class represented by `bytes` but with the name `newClassName` + */ + public static byte[] replaceClassName(byte[] bytes, String newClassName) throws Exception { + ClassModel classModel = ClassFile.of().parse(bytes); + return ClassFile.of().build(ClassDesc.of(newClassName), classModel::forEach); + } + + /* + * Replace class name in bytecodes to the class we're trying to redefine, so that both + * old and new classes can be compiled with jtreg for the test. + * + * @param loader ClassLoader to find the bytes for the old class. + * @param oldClassName old class name. + * @param newClassName new class name to replace with old class name. + * @return a copy of the class represented by `bytes` but with the name `newClassName` + */ + public static byte[] replaceClassName(ClassLoader loader, String oldClassName, String newClassName) throws Exception { + byte[] buf = getBytecodes(loader, oldClassName); + return replaceClassName(buf, newClassName); + } + /** * Main method to be invoked before test to create the redefineagent.jar */