Перенести кусок кода в другое место (ASM)

Версия Minecraft
1.7.10

tox1cozZ

aka Agravaine
8,453
599
2,906
Собсна, нужно перенести пару строку кода в другое место.
Если что, это 711 строка в классе Chunk.
Я тут немного пытался... Просьба не смеяться)
Java:
@Override
    public byte[] transform(String name, String transformedName, byte[] basicClass) {
        if(name.equals("net.minecraft.world.chunk.Chunk") || transformedName.equals("net.minecraft.world.chunk.Chunk")){
            ClassNode classNode = new ClassNode();
            ClassReader classReader = new ClassReader(basicClass);
            classReader.accept(classNode, 0);

            for(MethodNode method : classNode.methods){
                if(method.name.equals("func_150807_a")){
                    InsnList insns = method.instructions;
                    if (insns.size() == 0) {
                        continue;
                    }
                    Iterator<AbstractInsnNode> j = insns.iterator();
                    List<AbstractInsnNode> code = Lists.newArrayList();
                    while (j.hasNext()) {
                        AbstractInsnNode in = j.next();
                        if (in instanceof MethodInsnNode) {
                            MethodInsnNode min = (MethodInsnNode)in;
                            if(min.owner.equals("net/minecraft/block/Block") && min.name.equals("onBlockAdded") && min.desc.equals("(Lnet/minecraft/world/World;III)V")){
                                for(int i = 0; i < 11; i++){
                                    AbstractInsnNode prev = min.getPrevious();
                                    code.add(prev);
                                    insns.remove(prev);
                                }
                                code.add(min);
                                insns.remove(min);
                            }
                        }else if(in instanceof FieldInsnNode) {
                            FieldInsnNode fin = (FieldInsnNode)in;
                            if(fin.owner.equals("net/minecraft/world/chunk/Chunk") && fin.name.equals("isModified") && fin.desc.equals("Z") && fin.getPrevious().getOpcode() == Opcodes.ICONST_1){
                                for(AbstractInsnNode node : code){
                                    insns.insertBefore(fin, node);
                                }
                            }
                        }
                    }

                }
            }

            ClassWriter classWriter = new ClassWriter(0);
            classNode.accept(classWriter);
            return classWriter.toByteArray();
        }

        return basicClass;
    }
Безымянный.png
 
Решение
@Agravaine, можно как то так. Это не перенос в прямом смысле, но тоже работает (сделал по отработанной схеме). Если нужен прям перенос, то могу поковырять подольше.

Трансформер:
Java:
    private byte[] patchChunk(byte[] basicClass) {
                
        ClassNode classNode = new ClassNode();
        ClassReader classReader = new ClassReader(basicClass);
        classReader.accept(classNode, 0);
        
         String
         targetMethodName = "func_150807_a",
         worldObjFieldName = "worldObj",
         blockClassName = "net/minecraft/block/Block",
         chunkClassName = "net/minecraft/world/chunk/Chunk",
        worldClassName = "net/minecraft/world/World";
        
         int
         ifneCount = 0...
@Agravaine, можно как то так. Это не перенос в прямом смысле, но тоже работает (сделал по отработанной схеме). Если нужен прям перенос, то могу поковырять подольше.

Трансформер:
Java:
    private byte[] patchChunk(byte[] basicClass) {
                
        ClassNode classNode = new ClassNode();
        ClassReader classReader = new ClassReader(basicClass);
        classReader.accept(classNode, 0);
        
         String
         targetMethodName = "func_150807_a",
         worldObjFieldName = "worldObj",
         blockClassName = "net/minecraft/block/Block",
         chunkClassName = "net/minecraft/world/chunk/Chunk",
        worldClassName = "net/minecraft/world/World";
        
         int
         ifneCount = 0,
         ireturnCount = 0;
                                          
        for (MethodNode methodNode : classNode.methods) {
            
            if (methodNode.name.equals(targetMethodName) && methodNode.desc.equals("(IIIL" + blockClassName + ";I)Z")) {
                                                        
                AbstractInsnNode currentInsn = null;
                
                Iterator<AbstractInsnNode> insnIterator = methodNode.instructions.iterator();
              
                while (insnIterator.hasNext()) {
                    
                    currentInsn = insnIterator.next();
                    
                    if (currentInsn.getOpcode() == Opcodes.IFNE) {
                        
                        ifneCount++;
                        
                        if (ifneCount == 4) {
                            
                               InsnList nodesList = new InsnList();
                            
                            //Генерация условия "if (false)" для изоляции искомого блока кода в прежнем месте.
                            nodesList.add(new InsnNode(Opcodes.ICONST_0));
                            nodesList.add(new JumpInsnNode(Opcodes.IFEQ, ((JumpInsnNode) currentInsn).label));
                            
                            methodNode.instructions.insertBefore(currentInsn.getPrevious().getPrevious().getPrevious(), nodesList);                                           
                        }
                    }
                    
                    if (currentInsn.getOpcode() == Opcodes.IRETURN) {

                        ireturnCount++;
                        
                        if (ireturnCount == 4) {
                            
                               InsnList nodesList = new InsnList();
                            
                               //Генерация вызова статического метода с изолированным ранее блоком кода в нужном месте.
                            nodesList.add(new VarInsnNode(Opcodes.ALOAD, 0));
                            nodesList.add(new FieldInsnNode(Opcodes.GETFIELD, chunkClassName, worldObjFieldName, "L" + worldClassName + ";"));//field worldObj
                            nodesList.add(new VarInsnNode(Opcodes.ALOAD, 4));//local p_150807_4_
                            nodesList.add(new VarInsnNode(Opcodes.ILOAD, 12));//local l1
                            nodesList.add(new VarInsnNode(Opcodes.ILOAD, 2));//local p_150807_2_
                            nodesList.add(new VarInsnNode(Opcodes.ILOAD, 13));//local i2
                            nodesList.add(new MethodInsnNode(Opcodes.INVOKESTATIC, "ru/austeretony/rebind/coremod/ReBindHooks", "onBlockAddedSection", "(L" + worldClassName + ";L" + blockClassName + ";III)V", false));                           
                            
                            methodNode.instructions.insertBefore(currentInsn.getPrevious().getPrevious().getPrevious().getPrevious().getPrevious(), nodesList);                                           
                            
                            break;
                        }
                    }
                }   
                                
                break;
            }
        }

        ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);       
        classNode.accept(writer);
                
        return writer.toByteArray();   
    }


Хук:
Java:
    public static void onBlockAddedSection(World world, Block block, int xPos, int yPos, int zPos) {
                
        if (!world.isRemote) {
            
            block.onBlockAdded(world, xPos, yPos, zPos);
        }
    }


Потестил, работает. Проверь брейкпоинтами на всякий.
 
Я сделал вот так, спасибо :)
Java:
@Override
    public byte[] transform(String name, String transformedName, byte[] basicClass){
        if(name.equals("net.minecraft.world.chunk.Chunk") || transformedName.equals("apx")){
            ClassNode classNode = new ClassNode();
            ClassReader classReader = new ClassReader(basicClass);
            classReader.accept(classNode, 0);

            MethodNode method = findMethod(classNode, "func_150807_a", Type.getMethodDescriptor(Type.BOOLEAN_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.INT_TYPE, Type.getType(Block.class), Type.INT_TYPE));
            
            if(method != null){
                Iterator<AbstractInsnNode> j = method.instructions.iterator();
                while(j.hasNext()){
                    AbstractInsnNode in = j.next();
                    if(in instanceof MethodInsnNode){
                        MethodInsnNode min = (MethodInsnNode)in;
                        if(min.owner.equals(OBFUSCATION ? "aji" : "net/minecraft/block/Block") && min.name.equals(OBFUSCATION ? "b" : "onBlockAdded") && min.desc.equals(OBFUSCATION ? "(Lahb;III)V" : "(Lnet/minecraft/world/World;III)V")){
                            for(int i = 0; i < 11; i++){
                                method.instructions.remove(min.getPrevious());
                            }
                            method.instructions.remove(min);
                        }
                    }
                }
                
                AbstractInsnNode returnNode = findLastReturn(method);
                if(returnNode != null && returnNode.getPrevious().getOpcode() == ICONST_1){
                    InsnList insns = new InsnList();
                    
                    insns.add(new VarInsnNode(ALOAD, 4));
                    insns.add(new VarInsnNode(ALOAD, 0));
                    insns.add(new FieldInsnNode(GETFIELD, OBFUSCATION ? "apx" : "net/minecraft/world/chunk/Chunk", OBFUSCATION ? "e" : "worldObj", OBFUSCATION ? "Lahb;" : "Lnet/minecraft/world/World;"));
                    insns.add(new VarInsnNode(ILOAD, 12));
                    insns.add(new VarInsnNode(ILOAD, 2));
                    insns.add(new VarInsnNode(ILOAD, 13));
                    insns.add(new MethodInsnNode(INVOKEVIRTUAL, OBFUSCATION ? "aji" : "net/minecraft/block/Block", OBFUSCATION ? "b" : "onBlockAdded", OBFUSCATION ? "(Lahb;III)V" : "(Lnet/minecraft/world/World;III)V", false));
                    
                    method.instructions.insertBefore(returnNode, insns);
                }
            }

            ClassWriter classWriter = new ClassWriter(0);
            classNode.accept(classWriter);
            return classWriter.toByteArray();
        }

        return basicClass;
    }

    public static final MethodNode findMethod(ClassNode clazz, String name, String desc){
        for(MethodNode method : clazz.methods){
            if(method.name.equals(name) && method.desc.equals(desc)){
                return method;
            }
        }
        
        return null;
    }
    
    public static final AbstractInsnNode findLastReturn(MethodNode method){
        int searchFor = Type.getReturnType(method.desc).getOpcode(IRETURN);
        return findLastOpcode(method, searchFor);
    }

    public static final AbstractInsnNode findLastOpcode(MethodNode method, int opcode){
        AbstractInsnNode found = null;
        for(int i = 0; i < method.instructions.size(); i++){
            AbstractInsnNode insn = method.instructions.get(i);
            if(insn.getOpcode() == opcode){
                found = insn;
            }
        }
        return found;
    }
 
Могу предложить альтернативный вариант, красивее(хотя возможны проблемы с нумерацией строк в обфусцированном виде):
HooksPlugin:
Java:
public class HooksPlugin implements IFMLLoadingPlugin,IClassTransformer {

    @Override
    public String[] getASMTransformerClass() {
        return new String[] {getClass().getName()};
    }

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

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

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

    }

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

    @Override
    public byte[] transform(String name, String transformedName, byte[] basicClass) {
        switch (transformedName) {
            case "ru.whitewarrior.survivaltech.coremod.Acessor": {
                ClassReader cr = new ClassReader(basicClass);
                ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
                TestVisitor cv = new TestVisitor(cw);
                cr.accept(cv, ClassReader.SKIP_FRAMES);
                return cw.toByteArray();
            }
        }
        return basicClass;
    }
}
TestVisitor
Java:
public class TestVisitor extends ClassVisitor {
    public TestVisitor(ClassVisitor cv) {
        super(Opcodes.ASM5, cv);
    }
    @Override
    public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
        //Этот кусок кода служит для того чтобы коструктор Acessor'а не изменялся, если в классе методов много, нужно проверять имя.
        if(name.equals("<init>")) return super.visitMethod(access,name,desc,signature,exceptions);
        
        return new MethodVisitor(Opcodes.ASM5, super.visitMethod(access, name, desc, signature, exceptions)) {
            @Override
            public void visitLineNumber(int line, Label start) {
                if(line == 8){// Вставляем вызов своего метода в 8 строчку
                    super.visitVarInsn(Opcodes.ILOAD, 0);
                    super.visitMethodInsn(Opcodes.INVOKESTATIC, "ru/whitewarrior/survivaltech/Core", "hookInMethod", "(I)V", false);
                }
                super.visitLineNumber(line, start);
            }
        };
    }
}
Core
Java:
@Mod.EventHandler
public void preInit(FMLPreInitializationEvent event){
    int dq = Acessor.doGet();
}
public static void hookInMethod(int localvar1){
    System.out.println("Core.hookInMethod");
    System.out.println(localvar1);
}
Acessor
Java:
/*1*/package ru.whitewarrior.survivaltech.coremod;
/*2*/
/*3*/public class Acessor {
/*4*/    public static int staticVar = 1;
/*5*/    public static int doGet() {
/*6*/        staticVar *= 2;
/*7*/        int localVariable = staticVar * 10 - 1;
/*8*/        return localVariable;// в начало этой строчки инжекстим хук
/*9*/    }
/*10*/}
1530707057887.png
1530707498988.png
 
Назад
Сверху