package eu.mikroskeem.bytecodehackery; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class GeneratedClassLoader extends ClassLoader { /* * Note: "Stolen: from Techcable's Event4j, * https://github.com/Techcable/Event4J/blob/master/src/main/java/net/techcable/event4j/asm/ASMEventExecutorFactory.java */ private static final ConcurrentMap loaders = new ConcurrentHashMap<>(); public static GeneratedClassLoader getLoader(ClassLoader parent) { return loaders.computeIfAbsent(Objects.requireNonNull(parent, "Null parent class-loader"), GeneratedClassLoader::new); } public Class defineClass(String name, byte[] data) { name = Objects.requireNonNull(name, "Null name").replace('/', '.'); synchronized (getClassLoadingLock(name)) { if (hasClass(name)) throw new IllegalStateException(name + " already defined"); Class c = this.define(name, Objects.requireNonNull(data, "Null data")); if (!c.getName().equals(name)) throw new IllegalArgumentException("class name " + c.getName() + " != requested name " + name); return c; } } protected GeneratedClassLoader(ClassLoader parent) { super(parent); } private Class define(String name, byte[] data) { synchronized (getClassLoadingLock(name)) { if (hasClass(name)) throw new IllegalStateException("Already has class: " + name); Class c; try { c = defineClass(name, data, 0, data.length); } catch (ClassFormatError e) { throw new IllegalArgumentException("Illegal class data", e); } resolveClass(c); return c; } } @Override public Object getClassLoadingLock(String name) { return super.getClassLoadingLock(name); } public boolean hasClass(String name) { synchronized (getClassLoadingLock(name)) { try { Class.forName(name); return true; } catch (ClassNotFoundException e) { return false; } } } }