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

.scala-fortify_3.3.4.1.1.4.source-code.FortifyPlugin.scala Maven / Gradle / Ivy

/*
 * Copyright © 2016-2024 Lightbend, Inc. All rights reserved.
 * No information contained herein may be reproduced or transmitted in any form
 * or by any means without the express written permission of Lightbend, Inc.
 */

package com.lightbend.tools.fortify.plugin

import dotty.tools.dotc.plugins.PluginPhase
import java.io.File

class FortifyPlugin extends VersionSpecificPlugin:
  override val name = "fortify"
  override val description = "compile Scala to Fortify NST"
  override val optionsHelp: Option[String] = Some(PluginOptions.HelpString)
  override def init(options: List[String], error: String => Unit) =
    // the phase must be freshly constructed inside `init`,
    // as per lampepfl/dotty#18381
    val phase = FortifyPhase()
    import PluginOptions.RegexInterpolator
    options.foreach:
      case r"build=(.*)$id" =>
        phase.buildId = Some(id)
      case r"scaversion=(.*)$version" =>
        phase.scaVersion = version
      case r"out=(.*)$out" =>
        phase.outputDir = Some(File(out))
      case r"exclude=(.*)$specs" =>
        phase.excludes ++= Paths.parseExcludes(specs)
      // remaining options are undocumented
      case r"showSourceInfo=(.*)$show" =>
        phase.showSourceInfo = show.toBoolean
      case r"suppressEntitlementCheck=(.*)$suppress" =>
        phase.suppressEntitlementCheck = suppress.toBoolean
      case r"trace=(.*)$trace" =>
        phase.trace = trace.toBoolean
      case arg =>
        error(s"Bad argument: $arg")
    for
      id <- phase.buildId
      if !phase.outputDir.isDefined
    do
      phase.outputDir =
        Some(PluginOptions.pathForBuildId(phase.scaVersion, id))
    LicenseChecker.verifyEntitlements(
      phase.licensePath,
      phase.suppressEntitlementCheck,
      println)
    for
      id <- phase.buildId
      dir <- phase.outputDir
    do
      dir.mkdirs()
      java.io.File(dir.getParent, s"$id.scasession.lock")
        .createNewFile()
    List(phase)

import dotty.tools.dotc
import dotc.core.Contexts.Context
import dotc.ast.tpd.Tree

class FortifyPhase extends PluginPhase with PluginOptions:
  override val phaseName = "fortify"
  override val runsAfter = Set(dotty.tools.dotc.transform.MoveStatics.name)
  override val runsBefore = Set(dotty.tools.backend.jvm.GenBCode.name)
  val session = collection.mutable.Buffer[SessionWriter.Entry]()
  override def run(using Context): Unit =
    super.run
    if (session.isEmpty) // e.g. in REPL
      return
    import java.io.{File, FileOutputStream, OutputStreamWriter, PrintWriter}
    import java.nio.charset.StandardCharsets
    for (id <- buildId; dir <- outputDir) do
      SessionWriter.synchronized:
        val sessionFile =
          new File(dir.getParent, s"$id.scasession.increment")
        // maybe a bit janky to reuse this flag for this, but we don't want this
        // noise when doing development work
        if !suppressEntitlementCheck then
          println(s"scala-fortify: writing translated files to ${dir.getAbsolutePath}")
        val exists = sessionFile.exists
        val foStream = new FileOutputStream(sessionFile, true) // append = true
        val osWriter = new OutputStreamWriter(foStream, StandardCharsets.UTF_8)
        val writer = new PrintWriter(osWriter)
        // not sure if it's important to match the Java translator's behavior
        // of starting a new file with a blank line, but we might as well
        if (!exists)
          writer.println()
        try SessionWriter.write(
            writer,
            buildId.getOrElse(""),
            session.toSeq,
            summon[Context].settings.encoding.value)
        finally writer.close()
  override def transformUnit(tree: Tree)(using Context): Tree =
    if !Paths.isExcluded(excludes, tree.source.file.file) then
      val translator =
        if trace
        then TracingTranslator()
        else Translator()
      val actualOutputDir = outputDir.getOrElse(new File("."))
      val outputPath =
        Paths.sourcePathToNstPath(tree.source.file.file, actualOutputDir)
      outputPath.getParentFile.mkdirs()
      val writer = java.io.PrintWriter(outputPath)
      session += SessionWriter.Entry(
        tree.source.file.file, outputPath, countLines(tree.source.file.file))
      val (originalPath, lineMapper) =
        val twirl = Twirl(tree.source.content.mkString)
        twirl.originalPath match
          case Some(path) =>
            (path, twirl.mapLine)
          case None =>
            (tree.source.file.path, identity[Int])
      try
        translator.apply(
          path = tree.source.file.path,
          source = tree.source,
          tree = tree,
          lineMapper = identity,
          out = writer
        )
      finally writer.close()
    end if
    tree
  private def countLines(file: File)(using Context): Int =
    val source = io.Source.fromFile(file)(summon[Context].settings.encoding.value)
    try source.getLines().size
    finally source.close()




© 2015 - 2024 Weber Informatics LLC | Privacy Policy