we're recently building a java-compiler for college and need to test our compiled files with java reflections. I've had no problem with this tool until now. We need to test if multiple files with dependecies to each other are working. So we have two byte[], one of them contains the class, which I want to test, the other one contains a class from which the first one has an object (down below). I want to generate a Class Object out of these files on which I can execute methods like "getDeclaredConstructors". The Class Object should contain the first class. But I have the problem, that I can't figure out how to make it work. Do you think you can help me?
These are the two example files I want to inspect the generated bytecode (generating the bytecode works fine):
public class MultipleClasses1 {
MultipleClasses2 anotherClass;
public MultipleClasses1() {
this.anotherClass = new MultipleClasses2();
}
public int getIFromAnotherClass() {
return this.anotherClass.i;
}
}
public class MultipleClasses2 {
int i;
public MultipleClasses2() {
this.i = 4;
}
}
This is the file which helps me to get the Class<?> Object:
public class BytecodeTestUtil {
private final Class<?> clazz;
private final Object instance;
private static final Logger LOGGER = Logger.getLogger(BytecodeTestUtil.class.getName());
public BytecodeTestUtil(List<String> sourceFilePaths, String className) throws Exception {
byte[] resultBytecode = new byte[0];
for (byte[] bytecode : Compiler.generateByteCodeArrayFromFiles(sourceFilePaths)) {
int neededLength = resultBytecode.length + bytecode.length;
byte[] result = new byte[neededLength];
System.arraycopy(resultBytecode, 0, result, 0, resultBytecode.length);
System.arraycopy(bytecode, 0, result, resultBytecode.length, bytecode.length);
resultBytecode = result;
}
byte[] finalResultBytecode = resultBytecode;
ClassLoader classLoader = new ClassLoader() {
@Override
protected Class<?> findClass(String name) {
return defineClass(name, finalResultBytecode, 0, finalResultBytecode.length);
}
};
try {
clazz = classLoader.loadClass(className);
this.instance = clazz.getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException | SecurityException | NoSuchMethodException e) {
LOGGER.log(Level.SEVERE, "Exception occur", e);
throw new RuntimeException(e);
}
}
}
As you see I have tried to just put the code of both classes in one byte[], but on compilation I just get the Error "java.lang.ClassFormatError: Extra bytes at the end of class file MultipleClasses1"
If I just load the classfile for class MultipleClasses1 I get the Exception "java.lang.NoClassDefFoundError: MultipleClasses2 (wrong name: MultipleClasses1)"
I also tried do put up this custom ClassLoader:
public class ByteClassLoader extends ClassLoader {
// Map to store the class name and its corresponding byte array
private final Map<String, byte[]> classesBytes = new HashMap<>();
public ByteClassLoader() {
super(ClassLoader.getSystemClassLoader());
}
// Method to add a class byte array to the loader
public void addClass(String name, byte[] classData) {
classesBytes.put(name, classData);
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classData = classesBytes.get(name);
if (classData == null) {
throw new ClassNotFoundException(name);
}
return defineClass(name, classData, 0, classData.length);
}
}
But that did't solve my either.
Do you have any other Ideas?
ByteClassLoader
is heading into the right direction. But as long as you don’t show how you use it or be more specific than “But that did't solve my either”, there’s no way we could help you.