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

org.opalj.support.info.ThrownExceptions.scala Maven / Gradle / Ivy

/* BSD 2-Clause License - see OPAL/LICENSE for details. */
package org.opalj
package support
package info

import java.net.URL

import org.opalj.br.Method
import org.opalj.br.analyses.BasicReport
import org.opalj.br.analyses.DefaultOneStepAnalysis
import org.opalj.br.analyses.Project
import org.opalj.br.collection.TypesSet
import org.opalj.fpcf.PropertyStoreKey
import org.opalj.fpcf.FPCFAnalysesManagerKey
import org.opalj.fpcf.PropertyStore
import org.opalj.fpcf.SomeEPS
import org.opalj.fpcf.analyses.LazyVirtualMethodThrownExceptionsAnalysis
import org.opalj.fpcf.analyses.EagerL1ThrownExceptionsAnalysis
import org.opalj.fpcf.properties.{ThrownExceptions ⇒ ThrownExceptionsProperty}
import org.opalj.util.Nanoseconds
import org.opalj.util.PerformanceEvaluation.time

/**
 * Prints out the information about the exceptions thrown by methods.
 *
 * @author Michael Eichberg
 * @author Andreas Muttschelller
 */
object ThrownExceptions extends DefaultOneStepAnalysis {

    override def title: String = "Thrown Exceptions"

    override def description: String = {
        "Computes the set of the exceptions (in)directly thrown by methods"
    }

    final val AnalysisLevelL0 = "-analysisLevel=L0"
    final val AnalysisLevelL1 = "-analysisLevel=L1"
    final val SuppressPerMethodReports = "-suppressPerMethodReports"

    override def analysisSpecificParametersDescription: String = {
        "[-analysisLevel=  (Default: L1)]\n"+
            "[-suppressPerMethodReports]"
    }

    override def checkAnalysisSpecificParameters(parameters: Seq[String]): Traversable[String] = {
        val remainingParameters =
            parameters.filter { p ⇒
                p != AnalysisLevelL0 && p != AnalysisLevelL1 && p != SuppressPerMethodReports
            }
        super.checkAnalysisSpecificParameters(remainingParameters)
    }

    def doAnalyze(
        project:       Project[URL],
        parameters:    Seq[String],
        isInterrupted: () ⇒ Boolean
    ): BasicReport = {
        var executionTime: Nanoseconds = Nanoseconds.None
        val ps: PropertyStore = time {

            if (parameters.contains(AnalysisLevelL0)) {
                // We are relying on/using the "FallbackAnalysis":
                val ps = project.get(PropertyStoreKey)
                ps.setupPhase(Set.empty) // <= ALWAYS REQUIRED.
                // We have to query the properties...
                project.allMethods foreach { m ⇒ ps.force(m, ThrownExceptionsProperty.key) }
                ps.waitOnPhaseCompletion()
                ps
            } else /* if no analysis level is specified or L1 */ {
                project.get(FPCFAnalysesManagerKey).runAll(
                    LazyVirtualMethodThrownExceptionsAnalysis,
                    EagerL1ThrownExceptionsAnalysis
                )
            }
        } { t ⇒ executionTime = t }

        val allMethods = ps.entities(ThrownExceptionsProperty.key).toIterable
        val (epsNotThrowingExceptions, otherEPS) =
            allMethods.partition(_.ub.throwsNoExceptions)
        val epsThrowingExceptions = otherEPS.filter(eps ⇒ eps.lb.types != TypesSet.SomeException)

        val methodsThrowingExceptions = epsThrowingExceptions.map(_.e.asInstanceOf[Method])
        val privateMethodsThrowingExceptionsCount = methodsThrowingExceptions.count(_.isPrivate)

        val privateMethodsNotThrowingExceptions =
            epsNotThrowingExceptions.map(_.e.asInstanceOf[Method]).filter(_.isPrivate)

        val perMethodsReport =
            if (parameters.contains(SuppressPerMethodReports))
                ""
            else {
                val epsThrowingExceptionsByClassFile = epsThrowingExceptions groupBy (_.e.asInstanceOf[Method].classFile)
                epsThrowingExceptionsByClassFile.map { e ⇒
                    val (cf, epsThrowingExceptionsPerMethod) = e
                    cf.thisType.toJava+"{"+
                        epsThrowingExceptionsPerMethod.map { eps: SomeEPS ⇒
                            val m: Method = eps.e.asInstanceOf[Method]
                            val ThrownExceptionsProperty(types) = eps.ub
                            m.descriptor.toJava(m.name)+" throws "+types.toString
                        }.toList.sorted.mkString("\n\t\t", "\n\t\t", "\n")+
                        "}"

                }.mkString("\n", "\n", "\n")
            }

        val psStatistics = ps.statistics.map(e ⇒ e._1+": "+e._2).mkString("Property Store Statistics:\n\t", "\n\t", "\n")

        val analysisStatistics: String =
            "\nStatistics:\n"+
                "#methods with a thrown exceptions property: "+
                s"${allMethods.size} (${project.methodsCount})\n"+
                "#methods with exceptions information more precise than _ <: Throwable: "+
                s"${methodsThrowingExceptions.size + epsNotThrowingExceptions.size}\n"+
                s" ... #exceptions == 0: ${epsNotThrowingExceptions.size}\n"+
                s" ... #exceptions == 0 and private: ${privateMethodsNotThrowingExceptions.size}\n"+
                s" ... #exceptions >  0 and private: $privateMethodsThrowingExceptionsCount\n"+
                s"execution time: ${executionTime.toSeconds}\n"

        BasicReport(
            psStatistics+
                "\nThrown Exceptions Information:\n"+
                perMethodsReport+"\n"+
                ps.toString(printProperties = false) +
                analysisStatistics
        )
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy