LibJF/libjf-unsafe-v0/src/main/java/io/gitlab/jfronny/libjf/unsafe/asm/patch/targeting/InterfaceImplTargetPatch.java

108 lines
4.4 KiB
Java

package io.gitlab.jfronny.libjf.unsafe.asm.patch.targeting;
import io.gitlab.jfronny.libjf.LibJf;
import io.gitlab.jfronny.libjf.unsafe.asm.AsmTransformer;
import io.gitlab.jfronny.libjf.unsafe.asm.patch.Patch;
import org.objectweb.asm.Type;
import org.objectweb.asm.tree.ClassNode;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class InterfaceImplTargetPatch implements Patch {
public static final Map<String, Set<String>> INTERFACES = new HashMap<>();
private final String targetInterface;
private final Patch methodPatch;
public InterfaceImplTargetPatch(String targetInterfaceIntermediary, Patch methodPatch) {
this.targetInterface = AsmTransformer.MAPPING_RESOLVER.mapClassName(AsmTransformer.INTERMEDIARY, targetInterfaceIntermediary).replace('.', '/');
this.methodPatch = methodPatch;
}
@Override
public void apply(ClassNode klazz) {
scanInterfaces(klazz);
if (getUpper(klazz.name).contains(targetInterface)) {
if (AsmTransformer.INSTANCE.debugLogsEnabled()) LibJf.LOGGER.info("Found " + klazz.name + " implementing " + targetInterface);
methodPatch.apply(klazz);
}
}
private static void scanInterfaces(ClassNode klazz) {
if (INTERFACES.containsKey(klazz.name)) return;
INTERFACES.put(klazz.name, new HashSet<>());
for (String anInterface : klazz.interfaces) {
INTERFACES.get(klazz.name).add(anInterface);
}
if (klazz.superName != null) {
INTERFACES.get(klazz.name).add(klazz.superName);
}
INTERFACES.put(klazz.name, Set.copyOf(INTERFACES.get(klazz.name)));
for (String s : INTERFACES.get(klazz.name)) {
String n = s.replace('/', '.');
if (AsmTransformer.isClassUnmoddable(n, AsmTransformer.INSTANCE.getCurrentConfig()))
continue;
try {
InterfaceImplTargetPatch.class.getClassLoader().loadClass(n);
} catch (Throwable e) {
throw new RuntimeException("Could not load super class " + s + " of " + klazz.name, e);
}
}
}
private static void scanInterfaces(Class<?> klazz) {
String n = Type.getInternalName(klazz);
if (INTERFACES.containsKey(n)) return;
INTERFACES.put(n, new HashSet<>());
for (Class<?> anInterface : klazz.getInterfaces()) {
INTERFACES.get(n).add(Type.getInternalName(anInterface));
}
Class<?> superC = klazz.getSuperclass();
if (superC != null) {
INTERFACES.get(n).add(Type.getInternalName(superC));
}
INTERFACES.put(n, Set.copyOf(INTERFACES.get(n)));
for (String s : INTERFACES.get(n)) {
String nn = s.replace('/', '.');
if (AsmTransformer.isClassUnmoddable(nn, AsmTransformer.INSTANCE.getCurrentConfig()))
continue;
try {
scanInterfaces(InterfaceImplTargetPatch.class.getClassLoader().loadClass(nn));
} catch (Throwable e) {
throw new RuntimeException("Could not load super class " + s + " of " + n, e);
}
}
}
public static Set<String> getUpper(String className) {
Set<String> s = INTERFACES.get(className);
if (s == null) {
if (!className.startsWith("java/")
&& !className.startsWith("com/mojang/")
&& !className.startsWith("net/minecraft/")
&& !className.startsWith("jdk/")
&& !className.startsWith("it/unimi/dsi/fastutil/")
&& !className.startsWith("com/google/")
) {
if (AsmTransformer.INSTANCE.debugLogsEnabled()) LibJf.LOGGER.info("Non-default class not considered for interface scanning: " + className);
INTERFACES.put(className, Set.of());
return Set.of();
}
try {
scanInterfaces(Class.forName(className.replace('/', '.')));
s = INTERFACES.get(className);
} catch (ClassNotFoundException e) {
LibJf.LOGGER.error("Could not get base for " + className, e);
return Set.of();
}
}
s = new HashSet<>(s);
for (String s1 : s.toArray(new String[0])) {
s.addAll(getUpper(s1));
}
return s;
}
}