.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