All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alibaba.bytekit.utils.Decompiler Maven / Gradle / Ivy

package com.alibaba.bytekit.utils;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.UUID;

import org.benf.cfr.reader.api.CfrDriver;
import org.benf.cfr.reader.api.OutputSinkFactory;
import org.benf.cfr.reader.api.SinkReturns.LineNumberMapping;

import com.alibaba.deps.org.objectweb.asm.tree.AbstractInsnNode;
import com.alibaba.deps.org.objectweb.asm.tree.ClassNode;
import com.alibaba.deps.org.objectweb.asm.tree.InsnList;
import com.alibaba.deps.org.objectweb.asm.tree.MethodNode;
import com.alibaba.deps.org.objectweb.asm.util.Printer;
import com.alibaba.deps.org.objectweb.asm.util.Textifier;
import com.alibaba.deps.org.objectweb.asm.util.TraceClassVisitor;
import com.alibaba.deps.org.objectweb.asm.util.TraceMethodVisitor;

/**
 * TODO com.taobao.arthas.core.util.Decompiler
 * 
 * @author hengyunabc
 *
 */
public class Decompiler {

    public static String decompile(byte[] bytecode) throws IOException {
        return decompile(bytecode, false);
    }

    public static String decompile(byte[] bytecode, boolean printLineNumber) throws IOException {
        return decompile(bytecode, null, printLineNumber);
    }

    public static String decompile(byte[] bytecode, String methodName, boolean printLineNumber) throws IOException {
        String result = "";

        File tempDirectory = new File(System.getProperty("java.io.tmpdir"));
        File file = new File(tempDirectory, UUID.randomUUID().toString());
        FileUtils.writeByteArrayToFile(file, bytecode);

        result = decompile(file.getAbsolutePath(), methodName, true, printLineNumber);
        return result;
    }

    public static String decompile(String path) throws IOException {
        byte[] byteArray = FileUtils.readFileToByteArray(new File(path));
        return decompile(byteArray);
    }

    public static String toString(MethodNode methodNode) {
        Printer printer = new Textifier();
        TraceMethodVisitor methodPrinter = new TraceMethodVisitor(printer);

        methodNode.accept(methodPrinter);

        StringWriter sw = new StringWriter();
        printer.print(new PrintWriter(sw));
        printer.getText().clear();

        return sw.toString();
    }

    public static String toString(ClassNode classNode) {
        Printer printer = new Textifier();
        StringWriter sw = new StringWriter();
        PrintWriter printWriter = new PrintWriter(sw);

        TraceClassVisitor traceClassVisitor = new TraceClassVisitor(printWriter);

        classNode.accept(traceClassVisitor);

        printer.print(printWriter);
        printer.getText().clear();

        return sw.toString();
    }

    public static String toString(InsnList insnList) {
        Printer printer = new Textifier();
        TraceMethodVisitor mp = new TraceMethodVisitor(printer);
        insnList.accept(mp);

        StringWriter sw = new StringWriter();
        printer.print(new PrintWriter(sw));
        printer.getText().clear();
        return sw.toString();
    }

    public static String toString(AbstractInsnNode insn) {
        Printer printer = new Textifier();
        TraceMethodVisitor mp = new TraceMethodVisitor(printer);
        insn.accept(mp);

        StringWriter sw = new StringWriter();
        printer.print(new PrintWriter(sw));
        printer.getText().clear();
        return sw.toString();
    }

    /**
     * @param classFilePath
     * @param methodName
     * @return
     */
    public static String decompile(String classFilePath, String methodName) {
        return decompile(classFilePath, methodName, false);
    }

    /**
     * 
     * @param classFilePath
     * @param methodName
     * @param printLineNumber
     * @return
     */
    public static String decompile(String classFilePath, String methodName, boolean printLineNumber) {
        return decompile(classFilePath, methodName, true, printLineNumber);
    }

    public static String decompile(String classFilePath, String methodName, boolean hideUnicode,
            boolean printLineNumber) {
        final StringBuilder sb = new StringBuilder(8192);
        final NavigableMap lineMapping = new TreeMap<>();
        OutputSinkFactory mySink = new OutputSinkFactory() {
            @Override
            public List getSupportedSinks(SinkType sinkType, Collection collection) {
                return Arrays.asList(SinkClass.STRING, SinkClass.DECOMPILED, SinkClass.DECOMPILED_MULTIVER,
                        SinkClass.EXCEPTION_MESSAGE, SinkClass.LINE_NUMBER_MAPPING);
            }

            @Override
            public  Sink getSink(final SinkType sinkType, final SinkClass sinkClass) {
                return sinkable -> {
                    // skip message like: Analysing type demo.MathGame
                    if (sinkType == SinkType.PROGRESS) {
                        return;
                    }
                    if (sinkType == SinkType.LINENUMBER) {
                        LineNumberMapping mapping = (LineNumberMapping) sinkable;
                        NavigableMap classFileMappings = mapping.getClassFileMappings();
                        NavigableMap mappings = mapping.getMappings();
                        if (classFileMappings != null && mappings != null) {
                            for (Entry entry : mappings.entrySet()) {
                                Integer srcLineNumber = classFileMappings.get(entry.getKey());
                                lineMapping.put(entry.getValue(), srcLineNumber);
                            }
                        }
                        return;
                    }
                    sb.append(sinkable);
                };
            }
        };

        Map options = new HashMap<>();
        options.put("hideutf", String.valueOf(hideUnicode));
        options.put("trackbytecodeloc", "true");

        /**
         * @see org.benf.cfr.reader.util.MiscConstants.Version.getVersion() Currently,
         *      the cfr version is wrong. so disable show cfr version.
         */
        options.put("showversion", "false");
        if (methodName != null) {
            options.put("methodname", methodName);
        }

        CfrDriver driver = new CfrDriver.Builder().withOptions(options).withOutputSink(mySink).build();
        List toAnalyse = new ArrayList<>();
        toAnalyse.add(classFilePath);
        driver.analyse(toAnalyse);
        String src = sb.toString();
        if (printLineNumber && !lineMapping.isEmpty()) {
            src = addLineNumber(src, lineMapping);
        }
        return src;
    }

    private static String addLineNumber(String src, Map lineMapping) {
        int maxLineNumber = 0;
        for (Integer value : lineMapping.values()) {
            if (value != null && value > maxLineNumber) {
                maxLineNumber = value;
            }
        }
        String formatStr = "/*%2d*/ ";
        String emptyStr = "       ";
        StringBuilder sb = new StringBuilder();
        String[] lines = src.split("\\R");
        ;
        if (maxLineNumber >= 1000) {
            formatStr = "/*%4d*/ ";
            emptyStr = "         ";
        } else if (maxLineNumber >= 100) {
            formatStr = "/*%3d*/ ";
            emptyStr = "        ";
        }
        int index = 0;
        for (String line : lines) {
            Integer srcLineNumber = lineMapping.get(index + 1);
            if (srcLineNumber != null) {
                sb.append(String.format(formatStr, srcLineNumber));
            } else {
                sb.append(emptyStr);
            }
            sb.append(line).append("\n");
            index++;
        }
        return sb.toString();
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy