.scala-fortify_2.13.15.1.1.4.source-code.Closure.scala Maven / Gradle / Ivy
The newest version!
/*
* 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 scala.tools.nsc
import com.fortify.frontend.nst
import nst._
import nodes._
trait Closure[T <: nsc.ast.Trees with nsc.symtab.SymbolTable]
extends TranslatorHelpers[T] {
val global: T
import global._
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) {
val batch = seenSymbols.toList.distinct.filterNot(visited)
seenSymbols.clear()
visited ++= batch
for (symbol <- batch if source != symbol.pos.source && includeInClosure(symbol)) {
val clazz = toClassDecl(symbol)
val decls = symbol.info.decls.toList
.sortBy(_.fullName) ++ (if (symbol.isJavaDefined && symbol.companion.hasCompleteInfo)
symbol.companion.info.decls.toList
.sortBy(_.fullName)
else
Seq())
// lightbend/scala-fortify#437
val sam = definitions.samOf(symbol.tpe) // maybe NoSymbol
for (decl <- decls if decl == sam || alwaysInclude(decl) || visited(decl) && !omitMethod(decl))
if (decl.isMethod)
clazz.addFunction(toFunDecl(decl, paramNamesFromSymbol(decl)))
else if (isField(decl)) {
val field = toFieldDecl(decl)
clazz.addField(field)
if (symbol.isModule)
field.addModifiers(NSTModifiers.Static)
}
clazzes :+= clazz
}
}
val result = new STExternalDeclarations
for (clazz <- clazzes.sortBy(_.toString))
result.addClass(clazz)
result
}
// in this context we only have the symbol, we don't have access
// to the tree, so we can't call vparamss. the names retrieved
// this way might not match the ones in the trees, but it doesn't
// matter during closure generation
private def paramNamesFromSymbol(symbol: Symbol): List[Symbol] = {
// we would like to just call symbol.asMethod here, but we encountered
// a strange case, involving a method-local case class, where the
// accessor for the case class companion object was spuriously considered
// to be overloaded, so .asMethod would fail even though .alternatives.size
// equaled 1. so let's just call alternatives.head instead, but also include
// this require call, to alert us to any other unexpected cases that might
// crop up in the future
require(symbol.alternatives.size == 1, symbol.alternatives.toString)
symbol.alternatives.head.paramLists.flatten
}
// lightbend/scala-fortify#456
private def alwaysInclude(symbol: Symbol): Boolean =
overrides(symbol).contains(definitions.Object_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 == definitions.Array_apply ||
symbol == definitions.Array_update ||
// also should never be called, and doesn't exist as a real method
// anyway
symbol == definitions.String_+
private def includeInClosure(symbol: Symbol): Boolean =
symbol.isClass &&
!(symbol.isJavaDefined && symbol.isModuleClass) &&
!symbol.isPrimitiveValueClass
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy