
ch.epfl.scala.debugadapter.internal.scalasig.ScalaSigPrinter.scala Maven / Gradle / Ivy
/* ___ ____ ___ __ ___ ___
** / _// __// _ | / / / _ | / _ \ Scala classfile decoder
** __\ \/ /__/ __ |/ /__/ __ |/ ___/ (c) 2003-2010, LAMP/EPFL
** /____/\___/_/ |_/____/_/ |_/_/ http://scala-lang.org/
**
*/
package ch.epfl.scala.debugadapter.internal.scalasig
import java.lang.StringBuilder
import java.util.regex.Pattern
import scala.annotation.{switch, tailrec}
import scala.collection.mutable
import scala.reflect.NameTransformer
/**
* Originally copied scalap then from https://github.com/JetBrains/intellij-scala
* https://github.com/JetBrains/intellij-scala/blob/074e8f98d9789b3e7def3ade8d39e7ae770beccf/scala/decompiler/src/org/jetbrains/plugins/scala/decompiler/scalasig/ScalaSigPrinter.scala
*/
//This class is from scalap, refactored to work with new types
class ScalaSigPrinter(builder: StringBuilder) {
import ScalaSigPrinter._
def print(s: String): Unit = builder.append(s)
def result: String = builder.toString
private var visitedTypeBoundsType: Set[TypeBoundsType] = Set.empty
private val currentTypeParameters: mutable.HashMap[Symbol, String] =
new mutable.HashMap[Symbol, String]()
private def addTypeParameter(t: Symbol): Unit = {
def checkName(name: String): Boolean = {
currentTypeParameters.forall { case (_: Symbol, symbolName: String) =>
name != symbolName
}
}
if (checkName(t.name)) {
currentTypeParameters += ((t, t.name))
} else {
@tailrec
def writeWithIndex(index: Int): Unit = {
val nameWithIndex: String = s"${t.name}_$$_$index"
if (checkName(nameWithIndex)) {
currentTypeParameters += ((t, nameWithIndex))
} else writeWithIndex(index + 1)
}
writeWithIndex(1)
}
}
private def removeTypeParameter(t: Symbol): Unit = {
currentTypeParameters.remove(t)
}
val CONSTRUCTOR_NAME = ""
val INIT_NAME = "$init$"
case class TypeFlags(printRep: Boolean)
implicit object _tf extends TypeFlags(false)
def printSymbol(symbol: Symbol): Unit = { printSymbol(0, symbol) }
def printSymbolAttributes(
s: Symbol,
onNewLine: Boolean,
indent: => Unit
): Unit = s match {
case t: SymbolInfoSymbol =>
for (a <- t.attributes) {
indent; print(toString(a))
if (onNewLine) print("\n") else print(" ")
}
case _ =>
}
def printSymbol(level: Int, symbol: Symbol): Unit = {
def isSynthetic: Boolean =
symbol.isSynthetic || symbol.isCaseAccessor || symbol.isParamAccessor
val accessibilityOk = symbol match {
case _ if level == 0 => true
case _: AliasSymbol => true
case _: ObjectSymbol =>
true // non-private members of private objects may leak to the outer scope
case _ => !symbol.isPrivate
}
def indent(): Unit = { for (_ <- 1 to level) print(" ") }
if (accessibilityOk && !isSynthetic) {
symbol match {
case o: ObjectSymbol =>
printSymbolAttributes(o, onNewLine = true, indent())
indent()
if (o.name == "package" || o.name == "`package`") {
// print package object
printPackageObject(level, o)
} else {
printObject(level, o)
}
case c: ClassSymbol if !refinementClass(c) && !c.isModule =>
printSymbolAttributes(c, onNewLine = true, indent())
indent()
printClass(level, c)
case m: MethodSymbol =>
printSymbolAttributes(m, onNewLine = true, indent())
printMethod(level, m, indent _)
case a: AliasSymbol =>
printSymbolAttributes(a, onNewLine = true, indent())
indent()
printAlias(level, a)
case t: TypeSymbol
if !t.isParam && !t.name.matches("_\\$\\d+") &&
!t.name.matches("\\?(\\d)+") =>
// todo: type 0? found in Suite class from scalatest package. So this is quickfix,
// todo: we need to find why such strange type is here
printSymbolAttributes(t, onNewLine = true, indent())
indent()
printTypeSymbol(level, t)
case _ =>
}
}
}
def isCaseClassObject(o: ObjectSymbol): Boolean = {
val TypeRefType(_, Ref(classSymbol: ClassSymbol), _) = o.infoType: @unchecked
o.isFinal && (classSymbol.children.find(x => x.isCase && x.isInstanceOf[MethodSymbol]) match {
case Some(_) => true
case None => false
})
}
private def underObject(m: MethodSymbol) = m.parent match {
case Some(c: ClassSymbol) => c.isModule
case _ => false
}
private def underTrait(m: MethodSymbol) = m.parent match {
case Some(c: ClassSymbol) => c.isTrait
case _ => false
}
private def printChildren(
level: Int,
symbol: Symbol,
filterFirstCons: Boolean = false
): Unit = {
var firstConsFiltered = !filterFirstCons
for (child <- symbol.children) {
if (child.isParam && child.isType) {} // do nothing
else if (!firstConsFiltered)
child match {
case m: MethodSymbol if m.name == CONSTRUCTOR_NAME =>
firstConsFiltered = true
case _ => printSymbol(level + 1, child)
}
else printSymbol(level + 1, child)
}
}
def printWithIndent(level: Int, s: String): Unit = {
def indent(): Unit = { for (i <- 1 to level) print(" ") }
indent()
print(s)
}
def printModifiers(symbol: Symbol): Unit = {
lazy val privateWithin: Option[String] = {
symbol match {
case sym: SymbolInfoSymbol =>
sym.symbolInfo.privateWithin match {
case Some(t: Ref[Symbol]) =>
Some("[" + processName(t.get.name) + "]")
case _ => None
}
case _ => None
}
}
symbol.parent match {
case Some(cSymbol: ClassSymbol) if refinementClass(cSymbol) =>
return // no modifiers allowed inside refinements
case _ =>
}
// print private access modifier
if (symbol.isPrivate) {
print("private")
if (symbol.isLocal) print("[this] ")
else print(" ")
} else if (symbol.isProtected) {
print("protected")
if (symbol.isLocal) print("[this]")
else privateWithin foreach print
print(" ")
} else privateWithin.foreach(s => print("private" + s + " "))
if (symbol.isSealed) print("sealed ")
if (symbol.isImplicit) print("implicit ")
if (symbol.isFinal && !symbol.isInstanceOf[ObjectSymbol]) print("final ")
if (symbol.isOverride) print("override ")
if (symbol.isAbstract) symbol match {
case c @ (_: ClassSymbol | _: ObjectSymbol) if !c.isTrait =>
print("abstract ")
case _ => ()
}
if (symbol.isAbstractOverride) print("abstract override ")
if (symbol.isCase && !symbol.isMethod) print("case ")
}
private def refinementClass(c: ClassSymbol) = c.name == ""
def printClass(level: Int, c: ClassSymbol): Unit = {
if (
c.name == "" /*scala.tools.nsc.symtab.StdNames.LOCALCHILD.toString()*/
) {
print("\n")
} else if (c.name == "") { // todo: make it better to avoin '\n' char
print(" {\n")
printChildren(level, c)
printWithIndent(level, "}")
} else {
printModifiers(c)
val defaultConstructor =
if (!c.isTrait) getPrinterByConstructor(c) else ""
if (c.isTrait) print("trait ") else print("class ")
print(processName(c.name))
val it = c.infoType
val (classType, typeParams) = it match {
case PolyType(typeRef, symbols) =>
(PolyTypeWithCons(typeRef, symbols, defaultConstructor), symbols)
case ClassInfoType(a, b) if !c.isTrait =>
(ClassInfoTypeWithCons(a, b, defaultConstructor), Seq.empty)
case _ => (it, Seq.empty)
}
for (param <- typeParams) addTypeParameter(param.get)
printType(classType)
try {
print(" {")
// Print class selftype
c.thisTypeRef match {
case Some(t) =>
print("\n"); print(" this : " + toString(t.get) + " =>")
case None =>
}
print("\n")
printChildren(level, c, !c.isTrait)
printWithIndent(level, "}\n")
} finally {
for (param <- typeParams) removeTypeParameter(param.get)
}
}
}
def getClassString(level: Int, c: ClassSymbol): String = {
val printer = new ScalaSigPrinter(new StringBuilder())
printer.printClass(level, c)
printer.result
}
def getPrinterByConstructor(c: ClassSymbol): String = {
c.children.find {
case m: MethodSymbol if m.name == CONSTRUCTOR_NAME => true
case _ => false
} match {
case Some(m: MethodSymbol) =>
val printer = new ScalaSigPrinter(new StringBuilder())
printer.printPrimaryConstructor(m, c)
val res = printer.result
if (res.length() > 0 && res.charAt(0) != '(') " " + res
else res
case _ => ""
}
}
def printPrimaryConstructor(m: MethodSymbol, c: ClassSymbol): Unit = {
printModifiers(m)
printMethodType(
m.infoType,
printResult = false,
methodSymbolAsClassParam(_, c)
)(())
}
def printPackageObject(level: Int, o: ObjectSymbol): Unit = {
printModifiers(o)
print("package ")
print("object ")
val poName = o.symbolInfo.owner.get.name
print(processName(poName))
val TypeRefType(_, Ref(classSymbol: ClassSymbol), _) = o.infoType: @unchecked
printType(classSymbol)
print(" {\n")
printChildren(level, classSymbol)
printWithIndent(level, "}\n")
}
def printObject(level: Int, o: ObjectSymbol): Unit = {
printModifiers(o)
print("object ")
print(processName(o.name))
val TypeRefType(_, Ref(classSymbol: ClassSymbol), _) = o.infoType: @unchecked
printType(classSymbol)
print(" {\n")
printChildren(level, classSymbol)
printWithIndent(level, "}\n")
}
private def methodSymbolAsMethodParam(ms: MethodSymbol): String = {
val nameAndType =
processName(ms.name) + " : " + toString(ms.infoType)(TypeFlags(true))
val default = if (ms.hasDefault) compiledCodeBody else ""
nameAndType + default
}
private def methodSymbolAsClassParam(
msymb: MethodSymbol,
c: ClassSymbol
): String = {
val printer = new ScalaSigPrinter(new StringBuilder())
val methodName = msymb.name
val paramAccessors = c.children.filter {
case ms: MethodSymbol if ms.isParamAccessor && ms.name.startsWith(methodName) =>
true
case _ => false
}
val isMutable =
paramAccessors.exists(acc => isSetterFor(acc.name, methodName))
val toPrint = paramAccessors.find(m => !m.isPrivate || !m.isLocal)
toPrint match {
case Some(ms) =>
printer.printSymbolAttributes(ms, onNewLine = false, ())
printer.printModifiers(ms)
if (isMutable) printer.print("var ")
else printer.print("val ")
case _ =>
}
val nameAndType = processName(methodName) + " : " + toString(
msymb.infoType
)(TypeFlags(true))
val default = if (msymb.hasDefault) compiledCodeBody else ""
printer.print(nameAndType + default)
printer.result
}
def printMethodType(
t: Type,
printResult: Boolean,
pe: MethodSymbol => String = methodSymbolAsMethodParam
)(cont: => Unit): Unit = {
def _pmt(mt: FunctionType): Unit = {
val paramEntries = mt.paramSymbols.map({
case ms: MethodSymbol => pe(ms)
case _ => "^___^"
})
// Print parameter clauses
print(
paramEntries.mkString(
"(" + (mt match {
case _: ImplicitMethodType => "implicit "
// for Scala 2.9
case mt: MethodType if mt.paramSymbols.nonEmpty && mt.paramSymbols.head.isImplicit =>
"implicit "
case _ => ""
}),
", ",
")"
)
)
// Print result type
mt.resultType.get match {
case mt: MethodType => printMethodType(mt, printResult, pe)({})
case imt: ImplicitMethodType =>
printMethodType(imt, printResult, pe)({})
case x =>
if (printResult) {
print(" : ")
printType(x)
}
}
}
t match {
case mt @ MethodType(_, _) => _pmt(mt)
case mt @ ImplicitMethodType(_, _) => _pmt(mt)
case pt: PolyType =>
val typeParams = pt.paramSymbols
for (param <- typeParams) addTypeParameter(param)
print(typeParamString(typeParams))
try {
printMethodType(pt.typeRef.get, printResult)({})
} finally {
for (param <- typeParams) removeTypeParameter(param)
}
// todo consider another method types
case x => print(" : "); printType(x)
}
// Print rest of the symbol output
cont
}
def printMethod(level: Int, m: MethodSymbol, indent: () => Unit): Unit = {
val n = m.name
if (underObject(m) && n == CONSTRUCTOR_NAME) return
if (underTrait(m) && n == INIT_NAME) return
if (n.isDefaultParameterMethodName)
return // skip default function parameters
if (n.startsWith("super$"))
return // do not print auxiliary qualified super accessors
if (m.isAccessor && n.endsWith(setterSuffix)) return
if (m.isParamAccessor) return // do not print class parameters
if (n.startsWith(" isSetterFor(ms.name, m.name)
case _ => false
}
val keywords =
if (!m.isAccessor) "def "
else if (m.isLazy) "lazy val "
else if (hasSetter) "var "
else "val "
print(keywords)
n match {
case CONSTRUCTOR_NAME =>
print("this")
printMethodType(m.infoType, printResult = false) {
print(compiledCodeBody)
}
case name =>
val nn = processName(name)
print(nn)
val isConstantValueDefinition = m.isFinal && keywords.startsWith("val")
m.infoType match {
case isConstantType(ct) if isConstantValueDefinition =>
Constants.constantExpression(ct) match {
case Some(expr) => print(s" = $expr")
case None =>
print(s" : ${Constants.typeText(ct)} $compiledCodeBody")
}
case _ =>
val printBody = !m.isDeferred && (m.parent match {
case Some(c: ClassSymbol) if refinementClass(c) => false
case _ => true
})
printMethodType(m.infoType, printResult = true)(
{
if (printBody)
print(
compiledCodeBody /* Print body only for non-abstract methods */
)
}
)
}
}
print("\n")
}
def printAlias(level: Int, a: AliasSymbol): Unit = {
printModifiers(a)
print("type ")
print(processName(a.name))
val tp: Unit = a.infoType match {
case PolyType(typeRef, symbols) =>
printType(PolyTypeWithCons(typeRef, symbols, " = "))
case t => printType(t, " = ")
}
print("\n")
printChildren(level, a)
}
def printTypeSymbol(level: Int, t: TypeSymbol): Unit = {
print("type ")
print(processName(t.name))
t.infoType match {
case PolyType(typeRef, symbols) =>
printType(PolyTypeWithCons(typeRef, symbols, ""))
case _ => printType(t.infoType)
}
print("\n")
}
def toString(attrib: SymAnnot): String = {
val prefix = toString(attrib.typeRef, "@")
if (attrib.hasArgs) {
val argTexts = attrib.args.map(annotArgText)
val namedArgsText = attrib.namedArgs.map { case (name, value) =>
s"${processName(name)} = ${annotArgText(value)}"
}
(argTexts ++ namedArgsText).mkString(s"$prefix(", ", ", ")")
} else prefix
}
// TODO char, float, etc.
def annotArgText(arg: Any): String = {
arg match {
case s: String => quote(s)
case Name(s: String) => quote(s)
case Constant(v) => annotArgText(v)
case Ref(v) => annotArgText(v)
case AnnotArgArray(args) =>
args.map(ref => annotArgText(ref.get)).mkString("Array(", ", ", ")")
case t: Type => "classOf[%s]" format toString(t)
case null => "null"
case _ => arg.toString
}
}
def printType(sym: SymbolInfoSymbol)(implicit flags: TypeFlags): Unit =
printType(sym.infoType)(flags)
def printType(t: Type)(implicit flags: TypeFlags): Unit = print(
toString(t)(flags)
)
def printType(t: Type, sep: String)(implicit flags: TypeFlags): Unit = print(
toString(t, sep)(flags)
)
def toString(t: Type)(implicit flags: TypeFlags): String =
toString(t, "")(flags)
def toString(t: Type, level: Int)(implicit flags: TypeFlags): String =
toString(t, "", level)(flags)
private val SingletonTypePattern = """(.*?)\.type""".r
// TODO: this passing of 'level' look awful;
def toString(t: Type, sep: String, level: Int = 0)(implicit
flags: TypeFlags
): String = {
// print type itself
t match {
case ThisType(Ref(classSymbol: ClassSymbol)) if refinementClass(classSymbol) =>
sep + "this.type"
case ThisType(Ref(symbol)) =>
sep + processName(symbol.name) + ".this.type"
case SingleType(Ref(ThisType(Ref(thisSymbol: ClassSymbol))), symbol) =>
val thisSymbolName: String =
thisSymbol.name match {
case "package" =>
thisSymbol.symbolInfo.owner match {
case Ref(ex: ExternalSymbol) => processName(ex.name)
case _ => "this"
}
case name if thisSymbol.isModule => processName(name)
case name => processName(name) + ".this"
}
sep + thisSymbolName + "." + processName(symbol.name) + ".type"
case SingleType(Ref(ThisType(Ref(exSymbol: ExternalSymbol))), symbol) if exSymbol.name == "" =>
sep + processName(symbol.name) + ".type"
case SingleType(
Ref(ThisType(Ref(exSymbol: ExternalSymbol))),
Ref(symbol)
) =>
sep + processName(exSymbol.path)
.stripPrefix(".")
.removeDotPackage + "." +
processName(symbol.name) + ".type"
case SingleType(Ref(NoPrefixType), Ref(symbol)) =>
sep + processName(symbol.name) + ".type"
case SingleType(typeRef, symbol) =>
var typeRefString = toString(typeRef, level)
if (typeRefString.endsWith(".type"))
typeRefString = typeRefString.dropRight(5)
typeRefString = typeRefString.removeDotPackage
sep + typeRefString + "." + processName(symbol.name) + ".type"
case ConstantType(Ref(c)) =>
sep + Constants.typeText(c)
case TypeRefType(Ref(NoPrefixType), Ref(symbol: TypeSymbol), typeArgs)
if currentTypeParameters.isDefinedAt(symbol) =>
sep + processName(
currentTypeParameters.getOrElse(symbol, symbol.name)
) + typeArgString(typeArgs, level)
case TypeRefType(prefix, symbol, typeArgs) =>
sep + (symbol.path match {
case "scala." =>
flags match {
case TypeFlags(true) => toString(typeArgs.head, level) + "*"
case _ => "scala.Seq" + typeArgString(typeArgs, level)
}
case "scala." => "=> " + toString(typeArgs.head, level)
case _ =>
def checkContainsSelf(
self: Option[Type],
parent: Symbol
): Boolean = {
self match {
case Some(tp) =>
tp match {
case ThisType(Ref(sym)) => sym == parent
case SingleType(_, Ref(sym)) => sym == parent
case _: ConstantType => false
case TypeRefType(_, Ref(sym), _) => sym == parent
case _: TypeBoundsType => false
case RefinedType(Ref(sym), refs) =>
sym == parent || refs.exists(tp => checkContainsSelf(Some(tp), parent))
case ClassInfoType(Ref(sym), refs) =>
sym == parent || refs.exists(tp => checkContainsSelf(Some(tp), parent))
case ClassInfoTypeWithCons(Ref(sym), refs, _) =>
sym == parent || refs.exists(tp => checkContainsSelf(Some(tp), parent))
case ImplicitMethodType(_, _) => false
case MethodType(_, _) => false
case NullaryMethodType(_) => false
case PolyType(typeRef, symbols) =>
checkContainsSelf(Some(typeRef), parent) || symbols
.exists(_.get == parent)
case PolyTypeWithCons(typeRef, symbols, _) =>
checkContainsSelf(Some(typeRef), parent) || symbols
.exists(_.get == parent)
case AnnotatedType(typeRef) =>
checkContainsSelf(Some(typeRef), parent)
case AnnotatedWithSelfType(typeRef, Ref(sym), _) =>
checkContainsSelf(Some(typeRef), parent) || sym == parent
case ExistentialType(typeRef, symbols) =>
checkContainsSelf(Some(typeRef), parent) || symbols
.exists(_.get == parent)
case _ => false
}
case None => false
}
}
val prefixStr =
(prefix.get, symbol.get, toString(prefix.get, level)) match {
case (NoPrefixType, _, _) => ""
case (ThisType(Ref(objectSymbol)), _, _) if objectSymbol.isModule && !objectSymbol.isStableObject =>
val name: String = objectSymbol.name
objectSymbol match {
case classSymbol: ClassSymbol if name == "package" =>
processName(classSymbol.symbolInfo.owner.path) + "."
case _ => processName(name) + "."
}
case (ThisType(packSymbol), _, _) if !packSymbol.isType =>
processName(packSymbol.path.fixRoot) + "."
case (ThisType(Ref(classSymbol: ClassSymbol)), _, _) if refinementClass(classSymbol) =>
""
case (
ThisType(Ref(typeSymbol: ClassSymbol)),
ExternalSymbol(_, Some(parent), _),
_
)
if typeSymbol.path != parent.path && checkContainsSelf(
typeSymbol.thisTypeRef,
parent
) =>
processName(typeSymbol.name) + ".this."
case (
ThisType(typeSymbol),
ExternalSymbol(_, Some(parent), _),
_
) if typeSymbol.path != parent.path =>
processName(typeSymbol.name) + ".super[" + processName(
parent.name
) + "/*" + parent.path + "*/]."
case (_, _, SingletonTypePattern(a)) => a + "."
case (_, _, a) => a + "#"
}
// remove package object reference
val path = prefixStr.removeDotPackage
val name = processName(symbol.name)
val res = path + name
val suffix =
if (name == "_") {
symbol.get match {
case ts: TypeSymbol =>
ts.infoType match {
case t: TypeBoundsType =>
if (visitedTypeBoundsType.contains(t)) ""
else {
visitedTypeBoundsType += t
try toString(t, level)
finally visitedTypeBoundsType -= t
}
case _ => ""
}
case _ => ""
}
} else
symbol.get match {
case ex: ExternalSymbol if ex.isObject => ".type"
case _ => ""
}
val ress = res
.stripPrefix(".") + typeArgString(typeArgs, level) + suffix
ress
})
case TypeBoundsType(lower, upper) =>
val lb = toString(lower, level)
val ub = toString(upper, level)
val lbs = if (!lb.equals("scala.Nothing")) " >: " + lb else ""
val ubs = if (!ub.equals("scala.Any")) " <: " + ub else ""
lbs + ubs
case RefinedType(Ref(classSym: ClassSymbol), typeRefs) =>
val classStr = {
val text = getClassString(level + 1, classSym)
if (text.trim.stripPrefix("{").stripSuffix("}").trim.isEmpty) ""
else text
}
sep + typeRefs
.map(toString(_, level))
.mkString("", " with ", "") + classStr
case RefinedType(_, typeRefs) =>
sep + typeRefs.map(toString(_, level)).mkString("", " with ", "")
case ClassInfoType(_, typeRefs) =>
sep + typeRefs
.map(toString(_, level))
.mkString(" extends ", " with ", "")
case ClassInfoTypeWithCons(_, typeRefs, cons) =>
sep + typeRefs
.map(toString(_, level))
.mkString(cons + " extends ", " with ", "")
case ImplicitMethodType(resultType, _) => toString(resultType, sep, level)
case MethodType(resultType, _) => toString(resultType, sep, level)
case NullaryMethodType(resultType) => toString(resultType, sep, level)
case PolyType(typeRef, symbols) =>
"({ type λ" + typeParamString(symbols) + " = " + toString(
typeRef,
sep,
level
) + " })#λ"
case PolyTypeWithCons(typeRef, symbols, cons) =>
typeParamString(symbols) + cons + toString(typeRef, sep, level)
case AnnotatedType(typeRef) =>
toString(typeRef, sep, level)
case AnnotatedWithSelfType(typeRef, _, _) => toString(typeRef, sep, level)
// case DeBruijnIndexType(typeLevel, typeIndex) =>
case ExistentialType(typeRef, symbols) =>
val refs = symbols
.map(_.get)
.map(toString)
.filter(!_.startsWith("_"))
.map("type " + _)
.distinct
toString(typeRef, sep, level) + (if (refs.nonEmpty)
refs.mkString(
" forSome {",
"; ",
"}"
)
else "")
case _ => sep + t.toString
}
}
def getVariance(t: TypeSymbol): String =
if (t.isCovariant) "+" else if (t.isContravariant) "-" else ""
def toString(symbol: Symbol): String = symbol match {
case symbol: TypeSymbol =>
val attrs = (for (a <- symbol.attributes) yield toString(a)).mkString(" ")
val atrs = if (attrs.nonEmpty) attrs.trim + " " else ""
val symbolType = symbol.infoType match {
case PolyType(typeRef, symbols) =>
PolyTypeWithCons(typeRef, symbols, "")
case tp => tp
}
val name: String = currentTypeParameters.getOrElse(symbol, symbol.name)
atrs + getVariance(symbol) + processName(name) + toString(symbolType)
case _ => symbol.toString
}
def typeArgString(typeArgs: Seq[Type], level: Int): String =
if (typeArgs.isEmpty) ""
else
typeArgs
.map(toString(_, level))
.map(_.stripPrefix("=> "))
.mkString("[", ", ", "]")
def typeParamString(params: Seq[Symbol]): String =
if (params.isEmpty) ""
else params.map(toString).mkString("[", ", ", "]")
private object isConstantType {
@tailrec
def unapply(arg: Type): Option[Constant] = arg match {
case ConstantType(Ref(c)) => Some(c)
case NullaryMethodType(Ref(tpe)) => unapply(tpe)
case _ => None
}
}
private object Constants {
private def classTypeText(typeRef: TypeRefType): String = {
val ref = typeRef.symbol.get.path
val args = typeRef.typeArgs
ref + typeArgString(args, 0)
}
def typeText(ct: Constant): String =
(nonLiteralTypeText orElse
literalText orElse
symbolLiteralText).apply(ct.value)
def constantExpression(ct: Constant): Option[String] =
(constantDefinitionExpr orElse literalText).lift(ct.value)
private val nonLiteralTypeText: PartialFunction[Any, String] = {
case null => "scala.Null"
case _: Unit => "scala.Unit"
case _: Short =>
"scala.Short" // there are no literals for shorts and bytes
case _: Byte => "scala.Byte"
case Ref(typeRef: TypeRefType) =>
s"java.lang.Class[${classTypeText(typeRef)}]"
case Ref(ExternalSymbol(_, Some(Ref(parent)), _)) =>
parent.path // enum type
}
// symbol literals are not valid constant expression
private val symbolLiteralText: PartialFunction[Any, String] = { case Ref(ScalaSymbol(value)) =>
"\'" + value
}
private val constantDefinitionExpr: PartialFunction[Any, String] = {
case Ref(sym: ExternalSymbol) => sym.path // enum value
case Ref(typeRef: TypeRefType) =>
s"scala.Predef.classOf[${classTypeText(typeRef)}]" // class literal
// java numeric constants with special `toString`
// Double and Float infinities are equal, so we should check type first
case d: Double if d == java.lang.Double.POSITIVE_INFINITY =>
"java.lang.Double.POSITIVE_INFINITY"
case d: Double if d == java.lang.Double.NEGATIVE_INFINITY =>
"java.lang.Double.NEGATIVE_INFINITY"
case f: Float if f == java.lang.Float.POSITIVE_INFINITY =>
"java.lang.Float.POSITIVE_INFINITY"
case f: Float if f == java.lang.Float.NEGATIVE_INFINITY =>
"java.lang.Float.NEGATIVE_INFINITY"
// NaNs cannot be compared directly
case d: Double if java.lang.Double.isNaN(d) => "java.lang.Double.NaN"
case f: Float if java.lang.Float.isNaN(f) => "java.lang.Float.NaN"
}
private val literalText: PartialFunction[Any, String] = {
case null => "null"
case value: String => quote(value, canUseMultiline = false)
case Ref(Name(value)) => quote(value, canUseMultiline = false)
case value: Char => "\'" + value + "\'"
case value: Long => value.toString + "L"
case value: Float => value.toString + "f"
case value @ (_: Boolean | _: Int | _: Double) => value.toString
}
}
}
object ScalaSigPrinter {
val keywordList =
Set(
"true",
"false",
"null",
"abstract",
"case",
"catch",
"class",
"def",
"do",
"else",
"extends",
"final",
"finally",
"for",
"forSome",
"if",
"implicit",
"import",
"lazy",
"match",
"new",
"object",
"override",
"package",
"private",
"protected",
"return",
"sealed",
"super",
"this",
"throw",
"trait",
"try",
"type",
"val",
"var",
"while",
"with",
"yield"
)
val compiledCodeBody = " = { /* compiled code */ }"
// name may be qualified here
def processName(name: String): String = {
val parts = name.stripPrivatePrefix.split('.')
var idx = 0
while (idx < parts.length) {
parts(idx) = processSimpleName(
parts(idx)
) // no need to create intermediate array here
idx += 1
}
parts.mkString(".")
}
private def processSimpleName(name: String): String = {
name.decode.fixPlaceholderNames.fixExistentialTypeParamName.escapeNonIdentifiers
}
private def isSetterFor(setterName: String, methodName: String) = {
val correctLength =
setterName.length == methodName.length + setterSuffix.length
correctLength && setterName.startsWith(methodName) && setterName.endsWith(
setterSuffix
)
}
private val setterSuffix = "_$eq"
private val placeholderPattern = Pattern.compile("_\\$(\\d)+")
private val defaultParamMarker = "$default$"
private implicit class StringFixes(private val str: String) extends AnyVal {
def decode: String = NameTransformer.decode(str)
// noinspection MutatorLikeMethodIsParameterless
// TODO: Check that it works. Might be an issue with regex
def removeDotPackage: String = str.replaceAll("/.`package`", "")
def fixRoot: String = str.replaceAll("", "_root_")
def stripPrivatePrefix: String = {
val i = str.lastIndexOf("$$")
if (i > 0) str.substring(i + 2) else str
}
// to avoid names like this one: ?0 (from existential type parameters)
def fixExistentialTypeParamName: String = {
if (str.length() > 1 && str(0) == '?' && str(1).isDigit)
"x" + str.substring(1)
else str
}
def fixPlaceholderNames: String = {
if (str.indexOf('_') < 0) str // optimization
else placeholderPattern.matcher(str).replaceAll("_")
}
def escapeNonIdentifiers: String = {
if (!isIdentifier(str) || keywordList.contains(str) || str == "=")
"`" + str + "`"
else str
}
def isDefaultParameterMethodName: Boolean = {
val idx = str.indexOf(defaultParamMarker)
val afterMarker = idx + defaultParamMarker.length
idx > 0 && str.length > afterMarker && str.charAt(afterMarker).isDigit
}
}
private def isIdentifier(id: String): Boolean = {
// following four methods is the same like in scala.tools.nsc.util.Chars class
/** Can character start an alphanumeric Scala identifier? */
def isIdentifierStart(c: Char): Boolean =
(c == '_') || (c == '$') || Character.isUnicodeIdentifierStart(c)
/** Can character form part of an alphanumeric Scala identifier? */
def isIdentifierPart(c: Char) =
(c == '$') || Character.isUnicodeIdentifierPart(c)
/** Is character a math or other symbol in Unicode? */
def isSpecial(c: Char) = {
val chtp = Character.getType(c)
chtp == Character.MATH_SYMBOL.toInt || chtp == Character.OTHER_SYMBOL.toInt
}
/** Can character form part of a Scala operator name? */
def isOperatorPart(c: Char): Boolean = (c: @switch) match {
case '~' | '!' | '@' | '#' | '%' | '^' | '*' | '+' | '-' | '<' | '>' | '?' | ':' | '=' | '&' | '|' | '/' | '\\' =>
true
case _ => isSpecial(c)
}
def hasCommentStart(s: String) = s.contains("//") || s.contains("/*")
def lastIdentifierCharIdx(s: String): Int = {
var idx = -1
while (idx + 1 < s.length && isIdentifierPart(s.charAt(idx + 1))) {
idx += 1
}
idx
}
if (id.isEmpty || hasCommentStart(id)) return false
if (isIdentifierStart(id(0))) {
val lastIdCharIdx = lastIdentifierCharIdx(id)
if (lastIdCharIdx < 0 || lastIdCharIdx == id.length - 1) // simple id
true
else if (id.charAt(lastIdCharIdx) != '_')
false
else
id.drop(lastIdCharIdx + 1).forall(isOperatorPart)
} else if (isOperatorPart(id(0))) {
id.forall(isOperatorPart)
} else false
}
def quote(s: String, canUseMultiline: Boolean = true): String =
if (canUseMultiline && (s.contains("\n") || s.contains("\r")))
"\"\"\"" + s + "\"\"\""
else "\"" + s + "\""
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy