Skip to content
Snippets Groups Projects
Main.scala 10.23 KiB
/* Copyright 2009-2015 EPFL, Lausanne */

package leon

import leon.utils._
import leon.laziness.LazinessEliminationPhase

object Main {

  lazy val allPhases: List[LeonPhase[_, _]] = {
    List(
      frontends.scalac.ExtractionPhase,
      frontends.scalac.ClassgenPhase,
      utils.TypingPhase,
      FileOutputPhase,
      purescala.RestoreMethods,
      xlang.ArrayTransformation,
      xlang.EpsilonElimination,
      xlang.ImperativeCodeElimination,
      xlang.FixReportLabels,
      xlang.XLangDesugaringPhase,
      purescala.FunctionClosure,
      synthesis.SynthesisPhase,
      termination.TerminationPhase,
      verification.VerificationPhase,
      repair.RepairPhase,
      evaluators.EvaluationPhase,
      solvers.isabelle.AdaptationPhase,
      solvers.isabelle.IsabellePhase,
      transformations.InstrumentationPhase,
      invariant.engine.InferInvariantsPhase,
      laziness.LazinessEliminationPhase
    )
  }

  // Add whatever you need here.
  lazy val allComponents : Set[LeonComponent] = allPhases.toSet ++ Set(
    solvers.z3.FairZ3Component, MainComponent, SharedOptions, solvers.smtlib.SMTLIBCVC4Component, solvers.isabelle.Component
  )

  /*
   * This object holds the options that determine the selected pipeline of Leon.
   * Please put any further such options here to have them print nicely in --help message.
   */
  object MainComponent extends LeonComponent {
    val name = "main"
    val description = "Selection of Leon functionality. Default: verify"

    val optEval        = LeonStringOptionDef("eval", "Evaluate ground functions through code generation or evaluation (default: evaluation)", "default", "[code|default]")
    val optTermination = LeonFlagOptionDef("termination", "Check program termination. Can be used along --verify", false)
    val optRepair      = LeonFlagOptionDef("repair",      "Repair selected functions",                             false)
    val optSynthesis   = LeonFlagOptionDef("synthesis",   "Partial synthesis of choose() constructs",              false)
    val optIsabelle    = LeonFlagOptionDef("isabelle",    "Run Isabelle verification",                             false)
    val optNoop        = LeonFlagOptionDef("noop",        "No operation performed, just output program",           false)
    val optVerify      = LeonFlagOptionDef("verify",      "Verify function contracts",                             false)
    val optHelp        = LeonFlagOptionDef("help",        "Show help message",                                     false)
    val optInstrument = LeonFlagOptionDef("instrument", "Instrument the code for inferring time/depth/stack bounds", false)
    val optInferInv = LeonFlagOptionDef("inferInv", "Infer invariants from (instrumented) the code", false)
    val optLazyEval   	= LeonFlagOptionDef("lazy",    "Handles programs that may use the lazy construct",   false)

    override val definedOptions: Set[LeonOptionDef[Any]] =
      Set(optTermination, optRepair, optSynthesis, optIsabelle, optNoop, optHelp, optEval, optVerify, optInstrument, optInferInv, optLazyEval)
  }

  lazy val allOptions: Set[LeonOptionDef[Any]] = allComponents.flatMap(_.definedOptions)

  def displayHelp(reporter: Reporter, error: Boolean) = {

    reporter.title(MainComponent.description)
    for (opt <- MainComponent.definedOptions.toSeq.sortBy(_.name)) {
      reporter.info(opt.helpString)
    }
    reporter.info("")

    reporter.title("Additional global options")
    for (opt <- SharedOptions.definedOptions.toSeq.sortBy(_.name)) {
      reporter.info(opt.helpString)
    }
    reporter.info("")

    reporter.title("Additional options, by component:")

    for (c <- (allComponents - MainComponent - SharedOptions).toSeq.sortBy(_.name) if c.definedOptions.nonEmpty) {
      reporter.info("")
      reporter.info(s"${c.name} (${c.description})")
      for(opt <- c.definedOptions.toSeq.sortBy(_.name)) {
        // there is a non-breaking space at the beginning of the string :)
        reporter.info(opt.helpString)
      }
    }
    exit(error)
  }

  def displayVersion(reporter: Reporter) = {
    reporter.title("Leon verification and synthesis tool (http://leon.epfl.ch/)")
    reporter.info("")
  }

  private def exit(error: Boolean) = sys.exit(if (error) 1 else 0)

  def processOptions(args: Seq[String]): LeonContext = {

    val initReporter = new DefaultReporter(Set())

    val options = args.filter(_.startsWith("--")).toSet

    val files = args.filterNot(_.startsWith("-")).map(new java.io.File(_))

    val leonOptions: Set[LeonOption[Any]] = options.map { opt =>
      val (name, value) = try {
        OptionsHelpers.nameValue(opt)
      } catch {
        case _ : IllegalArgumentException =>
          initReporter.fatalError(
            s"Malformed option $opt. Options should have the form --name or --name=value"
          )
      }
      // Find respective LeonOptionDef, or report an unknown option
      val df = allOptions.find(_. name == name).getOrElse{
        initReporter.error(s"Unknown option: $name")
        displayHelp(initReporter, error = true)
      }
      df.parse(value)(initReporter)
    }

    val reporter = new DefaultReporter(
      leonOptions.collectFirst {
        case LeonOption(SharedOptions.optDebug, sections) => sections.asInstanceOf[Set[DebugSection]]
      }.getOrElse(Set[DebugSection]())
    )

    reporter.whenDebug(DebugSectionOptions) { debug =>
      debug("Options considered by Leon:")
      for (lo <- leonOptions) debug(lo.toString)
    }

    LeonContext(
      reporter = reporter,
      files = files,
      options = leonOptions.toSeq,
      interruptManager = new InterruptManager(reporter)
    )
  }

  def computePipeline(ctx: LeonContext): Pipeline[List[String], Any] = {

    import purescala.Definitions.Program
    import purescala.RestoreMethods
    import utils.FileOutputPhase
    import frontends.scalac.{ExtractionPhase, ClassgenPhase}
    import synthesis.SynthesisPhase
    import termination.TerminationPhase
    import xlang.FixReportLabels
    import verification.VerificationPhase
    import repair.RepairPhase
    import evaluators.EvaluationPhase
    import solvers.isabelle.IsabellePhase
    import MainComponent._
    import invariant.engine.InferInvariantsPhase
    import transformations._

    val helpF        = ctx.findOptionOrDefault(optHelp)
    val noopF        = ctx.findOptionOrDefault(optNoop)
    val synthesisF   = ctx.findOptionOrDefault(optSynthesis)
    val xlangF       = ctx.findOptionOrDefault(SharedOptions.optXLang)
    val repairF      = ctx.findOptionOrDefault(optRepair)
    val isabelleF    = ctx.findOptionOrDefault(optIsabelle)
    val terminationF = ctx.findOptionOrDefault(optTermination)
    val verifyF      = ctx.findOptionOrDefault(optVerify)
    val evalF        = ctx.findOption(optEval).isDefined
    val inferInvF = ctx.findOptionOrDefault(optInferInv)
    val instrumentF = ctx.findOptionOrDefault(optInstrument)
    val lazyevalF    = ctx.findOptionOrDefault(optLazyEval)
    val analysisF    = verifyF && terminationF

    if (helpF) {
      displayVersion(ctx.reporter)
      displayHelp(ctx.reporter, error = false)
    } else {
      val pipeBegin: Pipeline[List[String], Program] =
        ClassgenPhase andThen
        ExtractionPhase andThen
        new PreprocessingPhase(xlangF)

      val verification = if (xlangF) VerificationPhase andThen FixReportLabels else VerificationPhase

      val pipeProcess: Pipeline[Program, Any] = {
        if (noopF) RestoreMethods andThen FileOutputPhase
        else if (synthesisF) SynthesisPhase
        else if (repairF) RepairPhase
        else if (analysisF) Pipeline.both(verification, TerminationPhase)
        else if (terminationF) TerminationPhase
        else if (isabelleF) IsabellePhase
        else if (evalF) EvaluationPhase
        else if (inferInvF) InferInvariantsPhase
        else if (instrumentF) InstrumentationPhase andThen FileOutputPhase
        else if (lazyevalF) LazinessEliminationPhase
        else InstrumentationPhase andThen verification
      }

      pipeBegin andThen
        pipeProcess
    }
  }

  private var hasFatal = false

  def main(args: Array[String]) {
    val argsl = args.toList
    // Process options
    val ctx = try {
      processOptions(argsl)

    } catch {
      case LeonFatalError(None) =>
        exit(error=true)

      case LeonFatalError(Some(msg)) =>
        // For the special case of fatal errors not sent though Reporter, we
        // send them through reporter one time
        try {
          new DefaultReporter(Set()).fatalError(msg)
        } catch {
          case _: LeonFatalError =>
        }

        exit(error=true)
    }

    ctx.interruptManager.registerSignalHandler()

    val doWatch = ctx.findOptionOrDefault(SharedOptions.optWatch)

    if (doWatch) {
      val watcher = new FilesWatcher(ctx, ctx.files ++ Build.libFiles.map{ new java.io.File(_)})
      watcher.onChange {
        execute(args, ctx)
      }
    } else {
      execute(args, ctx)
    }

    exit(hasFatal)
  }

  def execute(args: Seq[String], ctx0: LeonContext): Unit = {
    val ctx = ctx0.copy(reporter = new DefaultReporter(ctx0.reporter.debugSections))

    try {
      // Compute leon pipeline
      val pipeline = computePipeline(ctx)

      val timer = ctx.timers.total.start()

      // Run pipeline
      val ctx2 = pipeline.run(ctx, args.toList) match {
        case (ctx2, (vReport: verification.VerificationReport, tReport: termination.TerminationReport)) =>
          ctx2.reporter.info(vReport.summaryString)
          ctx2.reporter.info(tReport.summaryString)
          ctx2

        case (ctx2, report: verification.VerificationReport) =>
          ctx2.reporter.info(report.summaryString)
          ctx2

        case (ctx2, report: termination.TerminationReport) =>
          ctx2.reporter.info(report.summaryString)
          ctx2

        case (ctx2, _) =>
          ctx2
      }

      timer.stop()

      ctx2.reporter.whenDebug(DebugSectionTimers) { debug =>
        ctx2.timers.outputTable(debug)
      }
      hasFatal = false
    } catch {
      case LeonFatalError(None) =>
        hasFatal = true

      case LeonFatalError(Some(msg)) =>
        // For the special case of fatal errors not sent though Reporter, we
        // send them through reporter one time
        try {
          ctx.reporter.fatalError(msg)
        } catch {
          case _: LeonFatalError =>
        }

        hasFatal = true
    }
  }

}