
io.ebean.enhance.asm.ClassWriterWithoutClassLoading Maven / Gradle / Ivy
package io.ebean.enhance.asm;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import io.ebean.enhance.common.CommonSuperUnresolved;
/**
* ClassWriter without class loading. Fixes problems on dynamic enhancement metioned here:
* https://github.com/ebean-orm/ebean-agent/issues/59
*
* Idea taken from here:
*
* https://github.com/zygote1984/AspectualAdapters/blob/master/ALIA4J-NOIRIn-all/src/org/alia4j/noirin/transform/ClassWriterWithoutClassLoading.java
*
* @author praml
*/
public class ClassWriterWithoutClassLoading extends ClassWriter {
private final Map> type2instanceOfs = new HashMap<>();
private final Map type2superclass = new HashMap<>();
private final Map type2isInterface = new HashMap<>();
public ClassWriterWithoutClassLoading(ClassReader classReader, int flags, ClassLoader classLoader) {
super(classReader, flags, classLoader);
}
public ClassWriterWithoutClassLoading(int flags, ClassLoader classLoader) {
super(flags, classLoader);
}
@Override
protected Class> classForName(String type) throws ClassNotFoundException {
throw new UnsupportedOperationException("This classloader does not support class loading.");
}
/**
* Returns the common super type of the two given types.
*
* @param type1 the internal name of a class.
* @param type2 the internal name of another class.
* @return the internal name of the common super class of the two given classes.
*/
@Override
protected synchronized String getCommonSuperClass(final String type1, final String type2) {
try {
if (getInstanceOfs(type2).contains(type1)) {
return type1;
}
if (getInstanceOfs(type1).contains(type2)) {
return type2;
}
if (isInterface(type1) || isInterface(type2)) {
return "java/lang/Object";
} else {
String type = type1;
do {
type = getSuperclass(type);
} while (!getInstanceOfs(type2).contains(type));
return type;
}
} catch (Exception e) {
unresolved.add(new CommonSuperUnresolved(type1, type2, e.toString()));
return "java/lang/Object";
}
}
private String getSuperclass(String type) {
if (!type2superclass.containsKey(type)) {
initializeTypeHierarchyFor(type);
}
return type2superclass.get(type);
}
private boolean isInterface(String type) {
if (!type2isInterface.containsKey(type)) {
initializeTypeHierarchyFor(type);
}
return type2isInterface.get(type);
}
private Set getInstanceOfs(String type) {
if (!type2instanceOfs.containsKey(type)) {
initializeTypeHierarchyFor(type);
}
return type2instanceOfs.get(type);
}
/**
* Here we read the class at bytecode-level.
*/
private void initializeTypeHierarchyFor(final String internalTypeName) {
try (InputStream classBytes = classLoader.getResourceAsStream(internalTypeName + ".class")){
ClassReader classReader = new ClassReader(classBytes);
classReader.accept(new ClassVisitor(Opcodes.ASM5) {
@Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
type2superclass.put(internalTypeName, superName);
type2isInterface.put(internalTypeName, (access & Opcodes.ACC_INTERFACE) > 0);
Set instanceOfs = new HashSet<>();
instanceOfs.add(internalTypeName); // we are instance of ourself
if (superName != null) {
instanceOfs.add(superName);
instanceOfs.addAll(getInstanceOfs(superName));
}
for (String superInterface : interfaces) {
instanceOfs.add(superInterface);
instanceOfs.addAll(getInstanceOfs(superInterface));
}
type2instanceOfs.put(internalTypeName, instanceOfs);
}
}, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
} catch (IOException e) {
throw new IllegalArgumentException(e);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy