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

.scala-fortify_3.3.4.1.1.4.source-code.Closure.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 com.fortify.frontend.nst
import nst.*
import nodes.*

import dotty.tools.dotc
import dotc.core.Contexts.Context
import dotc.core.Flags.*
import dotc.core.Symbols.{toDenot, NoSymbol}
import dotty.tools.dotc.util.NoSourcePosition
import dotc.core.StdNames.*

trait Closure(using ctx: Context) extends TranslatorHelpers:

  val seenSymbols: collection.mutable.ListBuffer[Symbol]

  def closure(source: SourceFile): STExternalDeclarations =
    var clazzes = Vector[STClassDecl]()
    val visited = collection.mutable.Set.empty[Symbol]
    while (seenSymbols.nonEmpty) do
      val batch = seenSymbols.toList.distinct.filterNot(visited)
      seenSymbols.clear()
      visited ++= batch
      for (symbol <- batch if symbol.source != source && includeInClosure(symbol)) do
        val clazz = toClassDecl(symbol)
        val decls = symbol.info.decls.toList
          .sortBy(_.fullName) ++ (if symbol.is(JavaDefined) && symbol.companionClass.exists
                                  then symbol.companionClass.info.decls.toList.sortBy(_.fullName)
                                  else Seq())
        // lightbend/scala-fortify#437: always include SAM methods in closure.
        // (I couldn't get `symbol.info match { case SAMType(...) => ...` to work,
        // no idea why, but this seems okay too, and anyway the `SAMType` extractor
        // is getting an additional argument in 3.4, so this is more portable)
        val sam: Symbol =  // maybe NoSymbol
          val possibles = symbol.info.possibleSamMethods
          if possibles.size == 1 then
            possibles.head.symbol
          else
            NoSymbol
        for decl <- decls if decl == sam || alwaysInclude(decl) || visited(decl) && !omitMethod(decl) do
          if (decl.is(Method)) then
            val names = decl.info.paramNamess.flatten
            clazz.addFunction(
              toFunDecl(decl,
                List.fill(names.size)(NoSymbol),
                names,
                List.fill(names.size)(NoSourcePosition),
                decl.info.paramInfoss.flatten))
          else if decl.isTerm && !decl.isOneOf(MethodOrModule) then
            val field = toFieldDecl(decl)
            clazz.addField(field)
            if symbol.is(Module)
            then field.addModifiers(NSTModifiers.Static)
        clazzes :+= clazz
      end for
    end while
    val result = new STExternalDeclarations
    for (clazz <- clazzes.sortBy(_.toString))
      result.addClass(clazz)
    result

  // lightbend/scala-fortify#456
  private def alwaysInclude(symbol: Symbol): Boolean =
    overrides(symbol).contains(defn.Any_equals)

  // Array.{apply,update} are special because their type parameter
  // isn't erased (because array types aren't erased at the JVM
  // level). That means we'll output a signature containing a generic
  // type, if we're not careful. But it's actually better to just omit
  // them entirely since they are never called in code we generate, we
  // always generate NST array syntax directly.
  private def omitMethod(symbol: Symbol): Boolean =
    symbol == defn.Array_apply ||
      symbol == defn.Array_update ||
      // also should never be called, and doesn't exist as a real method
      // anyway
      symbol == defn.String_+

  private def includeInClosure(symbol: Symbol)(using Context): Boolean =
    symbol.isClass &&
      !(symbol.is(JavaDefined) && symbol.is(ModuleClass)) &&
      !symbol.isPrimitiveValueClass




© 2015 - 2024 Weber Informatics LLC | Privacy Policy