com.builtbroken.mc.patch.ASMUtility.java Source code

Java tutorial

Introduction

Here is the source code for com.builtbroken.mc.patch.ASMUtility.java

Source

package com.builtbroken.mc.patch;

import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.tree.*;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ListIterator;

/**
 * Contains reusable methods for dealing with ASM
 *
 * @see <a href="https://github.com/BuiltBrokenModding/VoltzEngine/blob/development/license.md">License</a> for what you can and can't do with the code.
 * Created by Dark(DarkGuardsman, Robert) on 10/11/2016.
 */
public final class ASMUtility {
    /**
     * Gets the index contained in the var inside of the method object
     *
     * @param method
     * @param varName
     * @return
     */
    public static int getVarIndex(MethodNode method, String varName) {
        for (LocalVariableNode var : method.localVariables) {
            if (var.name.equals(varName)) {
                return var.index;
            }
        }
        System.out.println("Local variables for method " + method.name);
        method.localVariables.stream().forEach(v -> System.out.println("\t" + v.name));
        throw new RuntimeException("Failed to find var " + varName);
    }

    /**
     * Gets the index in the var list from the method
     *
     * @param method
     * @param varName
     * @return
     */
    public static int getVarIndex2(MethodNode method, String varName) {
        for (int i = 0; i < method.localVariables.size(); i++) {
            LocalVariableNode var = method.localVariables.get(i);
            if (var.name.equals(varName)) {
                return i;
            }
        }
        System.out.println("Local variables for method " + method.name);
        method.localVariables.stream().forEach(v -> System.out.println("\t" + v.name));
        throw new RuntimeException("Failed to find var " + varName);
    }

    /**
     * Gets the parameter index in the list of parameters
     *
     * @param method
     * @param varName
     * @return
     */
    public static int getParamIndex(MethodNode method, String varName) {
        for (int i = 0; i < method.parameters.size(); i++) {
            ParameterNode var = method.parameters.get(i);
            if (var.name.equals(varName)) {
                return i;
            }
        }
        System.out.println("Parameters for method " + method.name);
        method.localVariables.stream().forEach(v -> System.out.println("\t" + v.name));
        throw new RuntimeException("Failed to find param " + varName);
    }

    /**
     * Starts the injection process of editing byte code
     *
     * @param bytes
     * @return
     */
    public static ClassNode startInjection(String name, byte[] bytes) {
        CoreMod.logger.info("Starting injection process for " + name);
        final ClassNode node = new ClassNode();
        final ClassReader reader = new ClassReader(bytes);
        reader.accept(node, 0);
        return node;
    }

    /**
     * Finishes the injection process of editing byte code
     *
     * @param node
     * @return
     */
    public static byte[] finishInjection(String name, ClassNode node) {
        CoreMod.logger.info("Ending injection process for " + name);
        final ClassWriter writer = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        node.accept(writer);
        byte[] data = writer.toByteArray();
        _doDebug(node, data);
        return data;
    }

    /**
     * Handles debug information at the end of every injection process
     *
     * @param node
     * @param data
     */
    public static void _doDebug(ClassNode node, byte[] data) {
        if (CoreMod.isDevMode()) {
            try {
                String cl = node.name.replace(File.separator, ".").replace("/", ".").trim();
                File file = new File("./asmTestFolder/" + cl + ".class");
                if (!file.getParentFile().exists()) {
                    file.getParentFile().mkdirs();
                }
                System.out.println("Writing ASM class[" + cl + "] to " + file);
                FileOutputStream fos = new FileOutputStream(file);
                fos.write(data);
                fos.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Gets the method with the name and sig
     *
     * @param node
     * @param name
     * @param sig
     * @return
     */
    public static MethodNode getMethod(ClassNode node, String name, String name2, String sig) {
        for (MethodNode methodNode : node.methods) {
            ObfMapping mapping = new ObfMapping(node.name, methodNode.name, methodNode.desc).toRuntime();
            if ((mapping.s_name.equals(name) || mapping.s_name.equals(name2))
                    && (sig == null || mapping.s_desc.equals(sig) || methodNode.desc.equals(sig))) {
                return methodNode;
            }
        }
        return null;
    }

    public static MethodNode getMethod(ClassNode node, String name, String name2) {
        return getMethod(node, name, name2, null);
    }

    /**
     * Gets the {@link MethodInsnNode} with the given name, does not
     * check the sig of the method
     * <p>
     * Will throw an error if the method call is not found
     *
     * @param method - method inside of the class
     * @param name   - name of the method call
     * @return
     */
    public static MethodInsnNode getMethodeNode(MethodNode method, String name, String name2) {
        ListIterator<AbstractInsnNode> it = method.instructions.iterator();
        while (it.hasNext()) {
            AbstractInsnNode next = it.next();

            if (next instanceof MethodInsnNode) {
                MethodInsnNode mnode = (MethodInsnNode) next;
                ObfMapping mapping = new ObfMapping(mnode.owner, mnode.name, mnode.desc).toRuntime();
                if (mapping.s_name.equals(name) || mapping.s_name.equals(name2)) {
                    return (MethodInsnNode) next;
                }
            }
        }
        System.out.println("Instructions for method " + method.name);
        printMethod(method);
        throw new RuntimeException("Failed to find method " + name);
    }

    /**
     * Prints out all the info about a method
     *
     * @param methodNode - method
     */
    public static void printMethod(MethodNode methodNode) {
        //TODO print rest of information
        ListIterator<AbstractInsnNode> it = methodNode.instructions.iterator();
        while (it.hasNext()) {
            AbstractInsnNode next = it.next();
            if (next instanceof MethodInsnNode) {
                System.out.println("MethodInsnNode( " + next.getOpcode() + ", " + ((MethodInsnNode) next).name
                        + ((MethodInsnNode) next).desc + " )");
            } else if (next instanceof VarInsnNode) {
                System.out.println("VarInsnNode( " + next.getOpcode() + ", " + ((VarInsnNode) next).var + " )");
            } else if (next instanceof LabelNode) {
                System.out.println("LabelNode( " + ((LabelNode) next).getLabel() + " )");
            } else if (next instanceof LineNumberNode) {
                System.out.println("LineNumberNode( " + ((LineNumberNode) next).line + " )");
            } else {
                System.out.println(next);
            }
        }
    }
}