[ASM] Цепочка родителей класса

Версия Minecraft
1.7.10
API
Forge
236
4
22
Всем привет, опять у меня вопрос по этому долбаному ASM. Не очень уж давно спрашивал тут, как проверить есть ли определённый класс в цепочке классов-родителей трансформируемого класса, но ответ оказался нерабочим. (Тот самый пост) Я пробовал и полностью скопировать код, и "переиначить" под себя, не выходит. На текущий момент у меня есть такой метод:

Нерабочий поиск наследников:
    private void findSuperClasses(String className, ArrayList<String> list) {
        if(className == null) return;
        ClassReader classReader;
        try {
            classReader = new ClassReader(className);
        } catch(Throwable i) {
            return;
        }
        var superName = classReader.getSuperName();
        if(superName != null && !superName.equals("java/lang/Object")){
            list.add(superName);
            this.findSuperClasses(superName, list);
        }
    }
Этот рекурсивный поиск ломается на 1 же этапе, выбрасывая NPE в try catch при попытке создать класс ридер класса-родителя, из-за чего я не могу идти "в глубину" для сбора полной цепочки родителей класса...

Запускаю этот поиск родителей так::
//в методе transform
var classNode = new ClassNode(); //loombok, если что
var classReader = new ClassReader(basicClass);
classReader.accept(classNode, 0);

ArrayList<String> l = new ArrayList<>();
this.findSuperClasses(classNode.name, l);

Гуглил в Интернете, как же правильно через ASM собрать цепочку родителей класса - ответы, примерно, такие же, как и этот код. Я вставлял System.out.println`ы для отладки - className, передаваемый в конструктор - не равен нулл. Он выглядит примерно так: "path/to/class/TestClass". Интересно также то, что точно такой же поиск обычно без проблем находит всю цепочку родителей, если запускать игру из IDEA через "жука", а вот на сервере - дело плохо. Помогите, пожалуйста!
 
236
4
22
@GloomyFolken Вообщем вырезал себе нужные классы из хуклибы, но возникла 1 беда: Caused by: java.lang.ClassCircularityError: org/apache/commons/io/IOUtils. Как это можно решить? Class.forName("org.apache.commons.io.IOUtils") в классе с загрузкой ASM трансформеров или как-то по-другому? Подскажи, пожалуйста.
 
236
4
22
Вот кстати те классы, которые я себе "вырезал". Надеюсь по ним кто-нибудь сможет помочь починить краш.

ClassMetadataReader:
//Copy from HookLib
public class ClassMetadataReader {
    private static Method m;

    static {
        try {
            m = ClassLoader.class.getDeclaredMethod("findLoadedClass", String.class);
            m.setAccessible(true);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    public byte[] getClassData(String className) throws IOException {
        String classResourceName = '/' + className.replace('.', '/') + ".class";
        return IOUtils.toByteArray(ClassMetadataReader.class.getResourceAsStream(classResourceName));
    }

    public void acceptVisitor(byte[] classData, ClassVisitor visitor) {
        new ClassReader(classData).accept(visitor, 0);
    }

    public void acceptVisitor(String className, ClassVisitor visitor) throws IOException {
        acceptVisitor(getClassData(className), visitor);
    }

    public ArrayList<String> getSuperClasses(String type) {
        ArrayList<String> superclasses = new ArrayList<String>(1);
        superclasses.add(type);
        while ((type = getSuperClass(type)) != null) {
            superclasses.add(type);
        }
        Collections.reverse(superclasses);
        return superclasses;
    }

    private Class<?> getLoadedClass(String type) {
        if (m != null) {
            try {
                ClassLoader classLoader = ClassMetadataReader.class.getClassLoader();
                return (Class<?>) m.invoke(classLoader, type.replace('/', '.'));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public String getSuperClass(String type) {
        try {
            return getSuperClassASM(type);
        } catch (Exception e) {
            return getSuperClassReflect(type);
        }
    }

    protected String getSuperClassASM(String type) throws IOException {
        CheckSuperClassVisitor cv = new CheckSuperClassVisitor();
        acceptVisitor(type, cv);
        return cv.superClassName;
    }

    protected String getSuperClassReflect(String type) {
        Class<?> loadedClass = getLoadedClass(type);
        if (loadedClass != null) {
            if (loadedClass.getSuperclass() == null) return null;
            return loadedClass.getSuperclass().getName().replace('.', '/');
        }
        return "java/lang/Object";
    }

    private class CheckSuperClassVisitor extends ClassVisitor {
        String superClassName;

        public CheckSuperClassVisitor() {
            super(Opcodes.ASM5);
        }

        @Override
        public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
            this.superClassName = superName;
        }
    }

}

DeobfuscationMetadataReader:
//Copy from HookLib
public class DeobfuscationMetadataReader extends ClassMetadataReader {

    @Override
    public byte[] getClassData(String className) throws IOException {
        byte[] bytes = super.getClassData(unmap(className.replace('.', '/')));
        return deobfuscateClass(className, bytes);
    }

    private static byte[] deobfuscateClass(String className, byte[] bytes) {
        return MyModASM.deobfuscationTransformer().map((deobfuscationTransformer) -> deobfuscationTransformer.transform(className, className, bytes)).orElse(bytes);
    }

    private static String unmap(String type) {
        return MyModASM.isObfuscated() ? FMLDeobfuscatingRemapper.INSTANCE.unmap(type) : type;
    }

}

MyModASM [Тот, что в манифесте указывается]:
@IFMLLoadingPlugin.MCVersion(value = "1.7.10")
public class MyModASM implements IFMLLoadingPlugin {
    private static boolean obf, checked;
    private static Optional<DeobfuscationTransformer> deobfuscationTransformer = Optional.empty();
    private static final ClassMetadataReader deobfuscationMetadataReader = new DeobfuscationMetadataReader();

    @Override
    public String[] getASMTransformerClass() {
        return new String[] {MyFirstHook.class.getName(), MySecondHook.class.getName()};
    }

    @Override
    public String getModContainerClass() {
        return null;
    }

    @Override
    public String getSetupClass() {
        return null;
    }

    @Override
    public void injectData(Map<String, Object> data) {}

    @Override
    public String getAccessTransformerClass() {
        return null;
    }

    public static ClassMetadataReader getDeobfuscationMetadataReader() {
        return MyModASM.deobfuscationMetadataReader;
    }

    public static Optional<DeobfuscationTransformer> deobfuscationTransformer() {
        if (MyModASM.isObfuscated() && !MyModASM.deobfuscationTransformer.isPresent()) {
            MyModASM.deobfuscationTransformer = Optional.of(new DeobfuscationTransformer());
        }
        return MyModASM.deobfuscationTransformer;
    }

    public static boolean isObfuscated() {
        if (!MyModASM.checked) {
            try {
                Field deobfField = CoreModManager.class.getDeclaredField("deobfuscatedEnvironment");
                deobfField.setAccessible(true);
                MyModASM.obf = !deobfField.getBoolean(null);
            } catch (Exception var1) {
                var1.printStackTrace();
            }
            MyModASM.checked = true;
        }
        return MyModASM.obf;
    }
}
Как ни странно, если запукаю этот мод в сборке, где имеется HookLib (как отдельный мод) - всё работает. А без неё - ошибка в логе ещё до загрузки сервера [java.lang.ClassCircularityError: org/apache/commons/io/IOUtils] Помогите, пожалуйста!
 
808
3
124
Это странно, но звучит будто ты пытаешься вызвать getSuperClass для класса IOUtils из какого-то трансформеров. По идее такие библиотечные классы должны исключаться из трансформеров ещё на уровне LaunchClassLoader.

Можешь сделать костыль как сам предложил, можешь ручками отфильтровать этот класс / либу и не вызывать на нем проверку суперкласса
 

tox1cozZ

aka Agravaine
8,456
598
2,893
236
4
22
Это странно, но звучит будто ты пытаешься вызвать getSuperClass для класса IOUtils из какого-то трансформеров. По идее такие библиотечные классы должны исключаться из трансформеров ещё на уровне LaunchClassLoader.

Можешь сделать костыль как сам предложил, можешь ручками отфильтровать этот класс / либу и не вызывать на нем проверку суперкласса
Теоретически, ты прав. У меня в первом трансформере идёт поиск наследников: MyModASM.getDeobfuscationMetadataReader().getSuperClasses(name.replace('.', '/'));. А этот метод за собой тянет getClassData, который уже обращается к IOUtils из апача. Как мне поступить тогда? Добавить if(name.startWith("org.apache.commons")) return basicClass; в начало трансформера?
 
Сверху