Skip to content

8359707: Add classfile modification code to RedefineClassHelper #25857

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,67 +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();

// Poor man's renaming of class "TestClassOld" to "TestClassXXX"
byte[] buf = RedefineClassHelper.replaceAllStrings(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");
buf = RedefineClassHelper.replaceAllStrings(cvar, "TestClassNew", "TestClassXXX");
// Now redine the original version of "TestClassXXX" (i.e. "TestClassOld").
RedefineClassHelper.redefineClass(old, buf);
result = foo.invoke(null);
Expand Down
63 changes: 62 additions & 1 deletion test/lib/RedefineClassHelper.java
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -21,11 +21,14 @@
* questions.
*/

import java.io.InputStream;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.ClassDefinition;
import jdk.test.lib.compiler.InMemoryJavaCompiler;
import jdk.test.lib.helpers.ClassFileInstaller;

import static jdk.test.lib.Asserts.assertTrue;

/*
* Helper class to write tests that redefine classes.
* When main method is run, it will create a redefineagent.jar that can be used
Expand Down Expand Up @@ -61,6 +64,64 @@ public static void redefineClass(Class<?> clazz, byte[] bytecode) throws Excepti
instrumentation.redefineClasses(new ClassDefinition(clazz, bytecode));
}

private static byte[] getBytecodes(ClassLoader loader, String name) throws Exception {
InputStream is = loader.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);
}
}

/*
* 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 byteBuffer buffer of old class bytes.
* @param oldClassName old class name.
* @param newClassName new class name to replace with old class name.
*/
public static byte[] replaceAllStrings(byte[] byteBuffer, String oldClassName, String newClassName) throws Exception {
assertTrue(oldClassName.length() == newClassName.length(), "must have same length");
int index = -1;
while ((index = getStringIndex(oldClassName, byteBuffer, index + 1)) != 0) {
replaceString(byteBuffer, newClassName, index);
}
return byteBuffer;
}

/*
* 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 class.
* @param oldClassName old class name.
* @param newClassName new class name to replace with old class name.
*/
public static byte[] replaceAllStrings(ClassLoader loader, String oldClassName, String newClassName) throws Exception {
byte[] buf = getBytecodes(loader, oldClassName);
return replaceAllStrings(buf, oldClassName, newClassName);
}

/**
* Main method to be invoked before test to create the redefineagent.jar
*/
Expand Down