Package booton.translator

Source Code of booton.translator.Debugger$Formatter

/*
* Copyright (C) 2014 Nameless Production Committee
*
* Licensed under the MIT License (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*          http://opensource.org/licenses/mit-license.php
*/
package booton.translator;

import static booton.translator.CompilerRecorder.*;
import static jdk.internal.org.objectweb.asm.Opcodes.*;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Pattern;

import jdk.internal.org.objectweb.asm.AnnotationVisitor;
import booton.translator.Node.TryCatchFinally;

/**
* @version 2014/06/07 15:00:34
*/
public class Debugger extends AnnotationVisitor {

    /** The processing environment. */
    private static final boolean whileTest;

    /** The list for debug patterns. */
    private static final List<Pattern[]> patterns = new ArrayList();

    /** The current debugger. */
    private static Debugger debugger;

    // initialization
    static {
        // enable(".+SetFieldOnly", ".*");

        boolean flag = false;

        for (StackTraceElement element : new Error().getStackTrace()) {
            if (element.getClassName().startsWith("org.junit.")) {
                flag = true;
                break;
            }
        }
        whileTest = flag;
    }

    /** The use flag. */
    private boolean enable = false;

    /** The use flag. */
    private boolean firstTime = true;

    /** Can i use getDominator safely? */
    private boolean dominatorSafe = false;

    /**
     *
     */
    private Debugger() {
        super(ASM5);

        // update
        debugger = this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visit(String methodName, Object type) {
        if (!patterns.isEmpty()) {
            Class clazz = (Class) type;

            Iterator<Pattern[]> iterator = patterns.iterator();

            while (iterator.hasNext()) {
                Pattern[] patterns = iterator.next();

                if (patterns[0].matcher(clazz.getName()).matches() && patterns[1].matcher(methodName).matches()) {
                    enable = true;
                    return;
                }
            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void visitEnd() {
        enable = true;
    }

    /**
     * <p>
     * Register current processing target.
     * </p>
     *
     * @param className A class name regex.
     * @param methodName A method name regex.
     */
    public static void enable() {
        debugger.enable = true;
    }

    /**
     * <p>
     * Register debug target.
     * </p>
     *
     * @param className A class name regex.
     * @param methodName A method name regex.
     */
    public static void enable(String className, String methodName) {
        patterns.add(new Pattern[] {Pattern.compile(className), Pattern.compile(methodName)});
    }

    /**
     * @return
     */
    public static boolean isEnable() {
        if (debugger.enable && debugger.firstTime) {
            debugger.firstTime = false;

            printHeader(false);
        }
        return debugger.enable;
    }

    /**
     * <p>
     * Print debug message.
     * </p>
     *
     * @param values
     */
    public static void info(Object... values) {
        StringBuilder text = new StringBuilder();
        CopyOnWriteArrayList<Node> nodes = new CopyOnWriteArrayList();

        for (Object value : values) {
            if (value instanceof Node) {
                Node node = (Node) value;
                nodes.addIfAbsent(node);
                text.append("n").append(node.id);
            } else if (value instanceof List) {
                List<Node> list = (List<Node>) value;

                for (Node node : list) {
                    nodes.addIfAbsent(node);
                }
            } else if (value instanceof Set) {
                Set<Node> list = (Set<Node>) value;

                for (Node node : list) {
                    nodes.addIfAbsent(node);
                }
            } else {
                text.append(value);
            }
        }
        text.append("   ").append(link(false));

        System.out.println(text);
        System.out.println(format(nodes));
    }

    /**
     * <p>
     * Print debug message.
     * </p>
     *
     * @param values
     */
    public static void print(Object... values) {
        if (isEnable()) {
            info(values);
        }
    }

    /**
     * <p>
     * Print method info as header like.
     * </p>
     */
    public static void printHeader(boolean safe) {
        debugger.dominatorSafe = safe;

        if (isEnable()) {
            System.out.println("==== " + link(true) + " ====");
        }
    }

    /**
     * <p>
     * Create link expression.
     * </p>
     *
     * @return
     */
    private static String link(boolean head) {
        String methodName;

        if (whileTest) {
            String testClassName = computeTestClassName(getScript().source);
            String testMethodName = computeTestMethodName(testClassName);

            methodName = testMethodName == null ? getMethodName() : testMethodName;
        } else {
            methodName = getMethodName();
        }

        if (!methodName.startsWith("<")) {
            methodName = "#".concat(methodName);
        }
        return "(" + getScript().source.getName() + ".java:" + (head ? getMethodLine() : getLine()) + ") " + methodName;
    }

    /**
     * @param message
     */
    public static void print(Object message) {
        if (isEnable()) {
            System.out.println(message);
        }
    }

    /**
     * <p>
     * Dump node tree.
     * </p>
     *
     * @param node
     */
    public static void print(Node node) {
        if (node != null) {
            print(Collections.singletonList(node));
        }
    }

    /**
     * <p>
     * Dump node tree.
     * </p>
     *
     * @param nodes
     */
    public static void print(List<Node> nodes) {
        if (isEnable()) {
            System.out.println(format(nodes));
        }
    }

    /**
     * <p>
     * Dump node tree.
     * </p>
     *
     * @param nodes
     */
    public static void print(String text, List<Node> nodes) {
        print(text);
        print(nodes);
    }

    /**
     * <p>
     * Compute actual test class name.
     * </p>
     *
     * @param clazz
     * @return
     */
    private static String computeTestClassName(Class clazz) {
        String name = clazz.getName();

        int index = name.indexOf('$');

        if (index != -1) {
            name = name.substring(0, index);
        }
        return name;
    }

    /**
     * <p>
     * Compute actual test method name.
     * </p>
     *
     * @param testClassName
     * @return
     */
    private static String computeTestMethodName(String testClassName) {
        for (StackTraceElement element : new Error().getStackTrace()) {
            if (element.getClassName().equals(testClassName)) {
                return element.getMethodName();
            }
        }
        return null;
    }

    /**
     * <p>
     * Helper method to format node tree.
     * </p>
     *
     * @param nodes
     * @return
     */
    private static String format(List<Node> nodes) {
        Set<TryCatchFinally> tries = new LinkedHashSet();

        for (Node node : nodes) {
            if (node.tries != null) {
                tries.addAll(node.tries);
            }
        }

        // compute max id length
        int max = 1;

        for (Node node : nodes) {
            max = Math.max(max, String.valueOf(node.id).length());
        }

        int incoming = 0;
        int outgoing = 0;
        int backedge = 0;

        for (Node node : nodes) {
            incoming = Math.max(incoming, node.incoming.size() * max + (node.incoming.size() - 1) * 2);
            outgoing = Math.max(outgoing, node.outgoing.size() * max + (node.outgoing.size() - 1) * 2);
            backedge = Math.max(backedge, node.backedges.size() * max + (node.backedges.size() - 1) * 2);
        }

        Formatter format = new Formatter();

        for (Node node : nodes) {
            StringBuilder tryFlow = new StringBuilder();

            for (TryCatchFinally block : tries) {
                if (block.start == node) {
                    tryFlow.append("s");
                } else if (block.end == node) {
                    tryFlow.append("e");
                } else if (block.catcher == node) {
                    tryFlow.append("c");
                } else if (block.exit == node) {
                    tryFlow.append("x");
                } else {
                    tryFlow.append("  ");
                }
            }

            format.write(String.valueOf(node.id), max);
            format.write("  in : ");
            format.formatNode(node.incoming, incoming);
            format.write("out : ");
            format.formatNode(node.outgoing, outgoing);
            format.write("dom : ");
            format.formatNode(list(getDominator(node)), 1);
            format.write("doms : ");
            format.formatNode(node.dominators, node.dominators.size());
            format.write("prev : ");
            format.formatNode(list(node.previous), 1);
            format.write("next : ");
            format.formatNode(list(node.next), 1);
            format.write("dest : ");
            format.formatNode(list(node.destination), 1);

            if (backedge != 0) {
                format.write("back : ");
                format.formatNode(node.backedges, backedge);
            }
            if (!tries.isEmpty()) {
                format.write("try : ");
                format.write(tryFlow.toString(), tries.size() * 2 + 2);
            }
            format.write("code : ");
            format.formatCodeFragment(node.stack);
            format.write("\r\n");
        }
        return format.toString();
    }

    /**
     * <p>
     * Create single item list.
     * </p>
     *
     * @param node
     * @return
     */
    private static List<Node> list(Node node) {
        if (node == null) {
            return Collections.EMPTY_LIST;
        }
        return Arrays.asList(node);
    }

    /**
     * <p>
     * Helper method to compute dominator node.
     * </p>
     *
     * @param target
     * @return
     */
    private static Node getDominator(Node target) {
        if (debugger.dominatorSafe) {
            return target.getDominator();
        } else {
            return getDominator(target, new HashSet());
        }
    }

    /**
     * Compute the immediate dominator of this node.
     *
     * @return A dominator node. If this node is root, <code>null</code>.
     */
    private static Node getDominator(Node target, Set<Node> nodes) {
        if (!nodes.add(target)) {
            return null;
        }

        // check cache
        // We must search a immediate dominator.
        //
        // At first, we can ignore the older incoming nodes.
        List<Node> candidates = new CopyOnWriteArrayList(target.incoming);

        // compute backedges
        for (Node node : candidates) {
            if (target.backedges.contains(node)) {
                candidates.remove(node);
            }
        }

        int size = candidates.size();

        switch (size) {
        case 0: // this is root node
            return null;

        case 1: // only one incoming node
            return candidates.get(0);

        default: // multiple incoming nodes
            Node candidate = candidates.get(0);

            search: while (candidate != null) {
                for (int i = 1; i < size; i++) {
                    if (!hasDominator(candidates.get(i), candidate, nodes)) {
                        candidate = getDominator(candidate, nodes);
                        continue search;
                    }
                }
                return candidate;
            }
            return null;
        }
    }

    /**
     * Helper method to check whether the specified node dominate this node or not.
     *
     * @param dominator A dominator node.
     * @return A result.
     */
    private static boolean hasDominator(Node target, Node dominator, Set<Node> nodes) {
        Node current = target;

        while (current != null) {
            if (current == dominator) {
                return true;
            }
            current = getDominator(current, nodes);
        }

        // Not Found
        return false;
    }

    /**
     * @version 2012/11/30 16:09:26
     */
    private static final class Formatter {

        /** The actual buffer. */
        private final StringBuilder builder = new StringBuilder();

        /** The tab length. */
        private final int tab = 8;

        /**
         * Helper method to write message.
         *
         * @param message
         */
        private void write(String message) {
            builder.append(message);
        }

        /**
         * Helper method to write message.
         *
         * @param message
         */
        private void write(String message, int max) {
            builder.append(message);

            // calcurate required tab
            int requireTab = 1;

            while (requireTab * tab <= max) {
                requireTab++;
            }

            // calcurate remaining spaces
            int remaining = requireTab * tab - message.length();
            int actualTab = 0;

            while (0 < remaining - tab * actualTab) {
                actualTab++;
            }

            for (int i = 0; i < actualTab; i++) {
                builder.append('\t');
            }
        }

        /**
         * Helper method to write message.
         *
         * @param message
         */
        private void formatNode(List<Node> nodes, int max) {
            StringBuilder builder = new StringBuilder("[");

            for (int i = 0; i < nodes.size(); i++) {
                builder.append(String.valueOf(nodes.get(i).id));

                if (i != nodes.size() - 1) {
                    builder.append(",");
                }
            }
            builder.append("]");

            write(builder.toString(), max + 2);
        }

        /**
         * Helper method to format code fragment.
         *
         * @param operands
         */
        private void formatCodeFragment(List<Operand> operands) {
            for (int i = 0; i < operands.size(); i++) {
                Operand operand = operands.get(i);
                builder.append(operand).append(" [").append(type(operand)).append("]");

                if (i != operands.size() - 1) {
                    builder.append(" ");
                }
            }
        }

        /**
         * <p>
         * Helper method to detect type of {@link Operand}.
         * </p>
         *
         * @param operand
         * @return
         */
        private String type(Operand operand) {
            if (operand instanceof OperandString) {
                return "String";
            } else if (operand instanceof OperandExpression) {
                return "Expression";
            } else if (operand instanceof OperandCondition) {
                OperandCondition condition = (OperandCondition) operand;

                StringBuilder builder = new StringBuilder("Condition then ").append(condition.then.id);

                if (condition.elze != null) {
                    builder.append(" else ").append(condition.elze.id);
                }
                return builder.toString();
            } else if (operand instanceof OperandArray) {
                return "Array";
            } else if (operand instanceof OperandNumber) {
                return "Number";
            } else if (operand instanceof OperandEnclose) {
                return type(((OperandEnclose) operand).value) + " in Enclose";
            } else if (operand instanceof OperandAmbiguousZeroOneTernary) {
                return "TernaryCondition";
            }
            return "";
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public String toString() {
            return builder.toString();
        }
    }
}
TOP

Related Classes of booton.translator.Debugger$Formatter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.