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

org.checkerframework.dataflow.cfg.CFGProcessor Maven / Gradle / Ivy

Go to download

dataflow-shaded is a dataflow framework based on the javac compiler. It differs from the org.checkerframework:dataflow artifact in two ways. First, the packages in this artifact have been renamed to org.checkerframework.shaded.*. Second, unlike the dataflow artifact, this artifact contains the dependencies it requires.

There is a newer version: 3.42.0-eisop5
Show newest version
package org.checkerframework.dataflow.cfg;

import com.sun.source.tree.ClassTree;
import com.sun.source.tree.CompilationUnitTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.util.TreePathScanner;
import com.sun.tools.javac.util.Log;

import org.checkerframework.checker.nullness.qual.EnsuresNonNullIf;
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.checkerframework.dataflow.cfg.builder.CFGBuilder;
import org.checkerframework.dataflow.qual.Pure;
import org.checkerframework.javacutil.BasicTypeProcessor;
import org.checkerframework.javacutil.TreeUtils;

import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;

/**
 * Generate the control flow graph of a given method in a given class. See {@link
 * org.checkerframework.dataflow.cfg.visualize.CFGVisualizeLauncher} for example usage.
 */
@SupportedAnnotationTypes("*")
public class CFGProcessor extends BasicTypeProcessor {

    /**
     * Qualified name of a specified class which includes a specified method to generate the CFG
     * for.
     */
    private final String className;

    /** Name of a specified method to generate the CFG for. */
    private final String methodName;

    /** AST for source file. */
    private @Nullable CompilationUnitTree rootTree;

    /** AST node for the specified class. */
    private @Nullable ClassTree classTree;

    /** AST node for the specified method. */
    private @Nullable MethodTree methodTree;

    /** Result of CFG process; is set by {@link #typeProcessingOver}. */
    private @MonotonicNonNull CFGProcessResult result = null;

    /**
     * Create a CFG processor.
     *
     * @param className the qualified name of class which includes the specified method to generate
     *     the CFG for
     * @param methodName the name of the method to generate the CFG for
     */
    public CFGProcessor(String className, String methodName) {
        this.className = className;
        this.methodName = methodName;
    }

    /**
     * Get the CFG process result.
     *
     * @return result of cfg process
     */
    public final @Nullable CFGProcessResult getCFGProcessResult() {
        return result;
    }

    @Override
    public void typeProcessingOver() {
        if (rootTree == null) {
            result = new CFGProcessResult("Root tree is null.");
        } else if (classTree == null) {
            result = new CFGProcessResult("Method tree is null.");
        } else if (methodTree == null) {
            result = new CFGProcessResult("Class tree is null.");
        } else {
            Log log = getCompilerLog();
            if (log.nerrors > 0) {
                result = new CFGProcessResult("Compilation issued an error.");
            } else {
                ControlFlowGraph cfg =
                        CFGBuilder.build(rootTree, methodTree, classTree, processingEnv);
                result = new CFGProcessResult(cfg);
            }
        }
        super.typeProcessingOver();
    }

    @Override
    protected TreePathScanner createTreePathScanner(CompilationUnitTree root) {
        rootTree = root;
        return new TreePathScanner() {
            @Override
            public Void visitClass(ClassTree tree, Void p) {
                TypeElement el = TreeUtils.elementFromDeclaration(tree);
                if (el.getSimpleName().contentEquals(className)) {
                    classTree = tree;
                }
                return super.visitClass(tree, p);
            }

            @Override
            public Void visitMethod(MethodTree tree, Void p) {
                ExecutableElement el = TreeUtils.elementFromDeclaration(tree);
                if (el.getSimpleName().contentEquals(methodName)) {
                    methodTree = tree;
                    // Stop execution by throwing an exception. This makes sure that compilation
                    // does not proceed, and thus the AST is not modified by further phases of the
                    // compilation (and we save the work to do the compilation).
                    throw new RuntimeException();
                }
                return null;
            }
        };
    }

    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported();
    }

    /** The result of the CFG process, contains the control flow graph when successful. */
    public static class CFGProcessResult {
        /** Control flow graph. */
        private final @Nullable ControlFlowGraph controlFlowGraph;

        /** Did the CFG process succeed? */
        private final boolean isSuccess;

        /** Error message (when the CFG process failed). */
        private final @Nullable String errMsg;

        /**
         * Create the result of the CFG process. Only called if the CFG was built successfully.
         *
         * @param cfg control flow graph
         */
        /*package-private*/ CFGProcessResult(ControlFlowGraph cfg) {
            this(cfg, true, null);
        }

        /**
         * Create the result of the CFG process. Only called if the CFG was not built successfully.
         *
         * @param errMsg the error message
         */
        /*package-private*/ CFGProcessResult(String errMsg) {
            this(null, false, errMsg);
        }

        /**
         * Create the result of CFG process.
         *
         * @param cfg the control flow graph
         * @param isSuccess did the CFG process succeed?
         * @param errMsg error message (when the CFG process failed)
         */
        private CFGProcessResult(
                @Nullable ControlFlowGraph cfg, boolean isSuccess, @Nullable String errMsg) {
            this.controlFlowGraph = cfg;
            this.isSuccess = isSuccess;
            this.errMsg = errMsg;
        }

        /**
         * Check if the CFG process succeeded.
         *
         * @return true if the CFG process succeeded
         */
        @Pure
        @EnsuresNonNullIf(expression = "getCFG()", result = true)
        @EnsuresNonNullIf(expression = "getErrMsg()", result = false)
        @SuppressWarnings("nullness:contracts.conditional.postcondition.not.satisfied")
        public boolean isSuccess() {
            return isSuccess;
        }

        /**
         * Returns the generated control flow graph.
         *
         * @return the generated control flow graph
         */
        @Pure
        public @Nullable ControlFlowGraph getCFG() {
            return controlFlowGraph;
        }

        /**
         * Returns the error message.
         *
         * @return the error message
         */
        @Pure
        public @Nullable String getErrMsg() {
            return errMsg;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy