BCel, исправляющий метод на лету, не работает

Я пытаюсь вставить обратный вызов в метод Java с использованием BCEL, но обратный вызов никогда не вызывается. Программа работает так, как будто она вообще не оснащена приборами.

Урезанная версия того, что я сделал:

package com.github.worldsender;

import java.lang.reflect.InvocationTargetException;

import org.apache.bcel.*;
import org.apache.bcel.classfile.*;
import org.apache.bcel.generic.*;

public class CustomHook {
    public static void callback() {
        System.out.println("Success");
    }

    private static JavaClass getOriginal() {
        try {
            return Repository.lookupClass("com.github.worldsender.Foo");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException("Foo not found.", e);
        }
    }

    private static ClassGen modClass(ClassGen classGen) {
        for (Method method : classGen.getMethods()) {
            if (!method.getName().equals("main"))
                continue;
            classGen.removeMethod(method);
            MethodGen methodGen = modConstructor(classGen, method);
            classGen.addMethod(methodGen.getMethod());
            methodGen.getInstructionList().dispose();
            return classGen;
        }
        throw new RuntimeException("Method not found, abort");
    }

    private static MethodGen modConstructor(ClassGen classGen, Method constructor) {
        InstructionFactory factory = new InstructionFactory(classGen);
        ConstantPoolGen constants = classGen.getConstantPool();
        MethodGen methodGen = new MethodGen(constructor, classGen.getClassName(), constants);

        InstructionList ilist = methodGen.getInstructionList();

        String invokedClass = "com.github.worldsender.CustomHook";
        String invokedMethod = "callback";
        Type returnType = Type.VOID;
        Type[] arguments = Type.NO_ARGS;
        short invokeType = Constants.INVOKESTATIC;
        InvokeInstruction invoke = factory.createInvoke(invokedClass, invokedMethod, returnType, arguments, invokeType);

        ilist.insert(invoke);
        methodGen.stripAttributes(true);
        methodGen.setMaxStack();
        methodGen.setMaxLocals();
        return methodGen;
    }

    public static void main(String[] args) throws Exception {
        JavaClass original = getOriginal();
        ClassGen modClass = new ClassGen(original);
        modClass = modClass(modClass);

        Repository.removeClass(original);
        Repository.addClass(modClass.getJavaClass());

        Class<?> minecraftMain = Class.forName("com.github.worldsender.Foo");

        java.lang.reflect.Method meth = minecraftMain.getMethod("main", String[].class);
        meth.invoke(null, (Object) args);
    }
}
//// Other class
package com.github.worldsender;

public class Foo {
    public static void main(String[] args) {
        System.out.println("Here");
    }
}

Все что напечатано это:

Here

То, что я ожидал, было:

Success
Here

Что я делаю неправильно?

1 ответ

Решение

При звонке

Repository.addClass(modClass.getJavaClass())

вы добавляете класс в репозиторий BCEL, но не в путь к классу текущей виртуальной машины. При звонке

Class.forName("com.github.worldsender.Foo")

тем не менее, вы указываете виртуальной машине загрузить немодифицированный файл класса из пути к классу. Поэтому вы не можете наблюдать никакого эффекта. Взгляните на встроенный загрузчик классов BCEL для загрузки сгенерированного класса.

Другие вопросы по тегам