Skip to content

Commit f779c41

Browse files
committed
Write generic java signatures to classfiles
The code has been ported from scalac.
1 parent badc855 commit f779c41

33 files changed

+780
-18
lines changed

compiler/src/dotty/tools/backend/jvm/DottyBackendInterface.scala

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -477,8 +477,86 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
477477

478478
def getSingleOutput: Option[AbstractFile] = None // todo: implement
479479

480+
// @M don't generate java generics sigs for (members of) implementation
481+
// classes, as they are monomorphic (TODO: ok?)
482+
private final def needsGenericSignature(sym: Symbol): Boolean = !(
483+
// pp: this condition used to include sym.hasexpandedname, but this leads
484+
// to the total loss of generic information if a private member is
485+
// accessed from a closure: both the field and the accessor were generated
486+
// without it. This is particularly bad because the availability of
487+
// generic information could disappear as a consequence of a seemingly
488+
// unrelated change.
489+
ctx.base.settings.YnoGenericSig.value
490+
|| sym.is(Flags.Artifact)
491+
|| sym.is(Flags.allOf(Flags.Method, Flags.Lifted))
492+
|| sym.is(Flags.Bridge)
493+
)
494+
495+
private def verifySignature(sym: Symbol, sig: String)(implicit ctx: Context): Unit = {
496+
import scala.tools.asm.util.CheckClassAdapter
497+
def wrap(body: => Unit): Boolean =
498+
try { body; true }
499+
catch { case ex: Throwable => println(ex.getMessage); false }
500+
501+
val valid = wrap {
502+
if (sym.is(Flags.Method)) {
503+
CheckClassAdapter.checkMethodSignature(sig)
504+
}
505+
else if (sym.isTerm) {
506+
CheckClassAdapter.checkFieldSignature(sig)
507+
}
508+
else {
509+
CheckClassAdapter.checkClassSignature(sig)
510+
}
511+
}
512+
513+
if(!valid) {
514+
ctx.warning(
515+
i"""|compiler bug: created invalid generic signature for $sym in ${sym.denot.owner.showFullName}
516+
|signature: $sig
517+
|if this is reproducible, please report bug at https://github.com/lampepfl/dotty/issues
518+
""".trim, sym.pos)
519+
}
520+
}
521+
522+
/**
523+
* Generates the generic signature for `sym` before erasure.
524+
*
525+
* @param sym The symbol for which to generate a signature.
526+
* @param owner The owner of `sym`.
527+
* @return The generic signature of `sym` before erasure, as specified in the Java Virtual
528+
* Machine Specification, §4.3.4, or `null` if `sym` doesn't need a generic signature.
529+
* @see https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.3.4
530+
*/
531+
def getGenericSignature(sym: Symbol, owner: Symbol): String = {
532+
ctx.atPhase(ctx.erasurePhase) { implicit ctx =>
533+
val memberTpe =
534+
if (sym.is(Flags.Method)) sym.denot.info
535+
else owner.denot.thisType.memberInfo(sym)
536+
getGenericSignature(sym, owner, memberTpe).orNull
537+
}
538+
}
539+
540+
private def getGenericSignature(sym: Symbol, owner: Symbol, memberTpe: Type)(implicit ctx: Context): Option[String] =
541+
if (needsGenericSignature(sym)) {
542+
val erasedTypeSym = sym.denot.info.typeSymbol
543+
if (erasedTypeSym.isPrimitiveValueClass) {
544+
None
545+
} else {
546+
val jsOpt =
547+
ctx.atPhase(ctx.erasurePhase) { implicit ctx =>
548+
Erasure.javaSig(sym, memberTpe, _ => ())
549+
}
480550

481-
def getGenericSignature(sym: Symbol, owner: Symbol): String = null // todo: implement
551+
if (ctx.settings.XverifySignatures.value) {
552+
jsOpt.foreach(verifySignature(sym, _))
553+
}
554+
555+
jsOpt
556+
}
557+
} else {
558+
None
559+
}
482560

483561
def getStaticForwarderGenericSignature(sym: Symbol, moduleClass: Symbol): String = null // todo: implement
484562

compiler/src/dotty/tools/dotc/config/ScalaSettings.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class ScalaSettings extends Settings.SettingGroup {
6161
val XnoValueClasses = BooleanSetting("-Xno-value-classes", "Do not use value classes. Helps debugging.")
6262
val XreplLineWidth = IntSetting("-Xrepl-line-width", "Maximial number of columns per line for REPL output", 390)
6363
val XfatalWarnings = BooleanSetting("-Xfatal-warnings", "Fail the compilation if there are any warnings.")
64+
val XverifySignatures = BooleanSetting("-Xverify-signatures", "Verify generic signatures in generated bytecode.")
6465

6566
/** -Y "Private" settings */
6667
val overrideVars = BooleanSetting("-Yoverride-vars", "Allow vars to be overridden.")
@@ -79,6 +80,7 @@ class ScalaSettings extends Settings.SettingGroup {
7980
val YdisableFlatCpCaching = BooleanSetting("-YdisableFlatCpCaching", "Do not cache flat classpath representation of classpath elements from jars across compiler instances.")
8081

8182
val YnoImports = BooleanSetting("-Yno-imports", "Compile without importing scala.*, java.lang.*, or Predef.")
83+
val YnoGenericSig = BooleanSetting("-Yno-generic-signatures", "Suppress generation of generic signatures for Java.")
8284
val YnoPredef = BooleanSetting("-Yno-predef", "Compile without importing Predef.")
8385
val Yskip = PhasesSetting("-Yskip", "Skip")
8486
val Ydumpclasses = StringSetting("-Ydump-classes", "dir", "Dump the generated bytecode to .class files (useful for reflective compilation that utilizes in-memory classloaders).", "")

compiler/src/dotty/tools/dotc/core/Definitions.scala

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,9 +300,11 @@ class Definitions {
300300
lazy val NothingClass: ClassSymbol = enterCompleteClassSymbol(
301301
ScalaPackageClass, tpnme.Nothing, AbstractFinal, List(AnyClass.typeRef))
302302
def NothingType = NothingClass.typeRef
303+
lazy val RuntimeNothingModuleRef = ctx.requiredModuleRef("scala.runtime.Nothing")
303304
lazy val NullClass: ClassSymbol = enterCompleteClassSymbol(
304305
ScalaPackageClass, tpnme.Null, AbstractFinal, List(ObjectClass.typeRef))
305306
def NullType = NullClass.typeRef
307+
lazy val RuntimeNullModuleRef = ctx.requiredModuleRef("scala.runtime.Null")
306308

307309
lazy val ScalaPredefModuleRef = ctx.requiredModuleRef("scala.Predef")
308310
def ScalaPredefModule(implicit ctx: Context) = ScalaPredefModuleRef.symbol

compiler/src/dotty/tools/dotc/core/NameOps.scala

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -229,23 +229,8 @@ object NameOps {
229229

230230
def specializedFor(classTargs: List[Types.Type], classTargsNames: List[Name], methodTargs: List[Types.Type], methodTarsNames: List[Name])(implicit ctx: Context): name.ThisName = {
231231

232-
def typeToTag(tp: Types.Type): Name = {
233-
tp.classSymbol match {
234-
case t if t eq defn.IntClass => nme.specializedTypeNames.Int
235-
case t if t eq defn.BooleanClass => nme.specializedTypeNames.Boolean
236-
case t if t eq defn.ByteClass => nme.specializedTypeNames.Byte
237-
case t if t eq defn.LongClass => nme.specializedTypeNames.Long
238-
case t if t eq defn.ShortClass => nme.specializedTypeNames.Short
239-
case t if t eq defn.FloatClass => nme.specializedTypeNames.Float
240-
case t if t eq defn.UnitClass => nme.specializedTypeNames.Void
241-
case t if t eq defn.DoubleClass => nme.specializedTypeNames.Double
242-
case t if t eq defn.CharClass => nme.specializedTypeNames.Char
243-
case _ => nme.specializedTypeNames.Object
244-
}
245-
}
246-
247-
val methodTags: Seq[Name] = (methodTargs zip methodTarsNames).sortBy(_._2).map(x => typeToTag(x._1))
248-
val classTags: Seq[Name] = (classTargs zip classTargsNames).sortBy(_._2).map(x => typeToTag(x._1))
232+
val methodTags: Seq[Name] = (methodTargs zip methodTarsNames).sortBy(_._2).map(x => x._1.toTag)
233+
val classTags: Seq[Name] = (classTargs zip classTargsNames).sortBy(_._2).map(x => x._1.toTag)
249234

250235
name.likeSpaced(name ++ nme.specializedTypeNames.prefix ++
251236
methodTags.fold(nme.EMPTY)(_ ++ _) ++ nme.specializedTypeNames.separator ++

compiler/src/dotty/tools/dotc/core/Symbols.scala

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,18 @@ object Symbols {
531531
/** The current name of this symbol */
532532
final def name(implicit ctx: Context): ThisName = denot.name.asInstanceOf[ThisName]
533533

534+
def isHigherOrderTypeParameter(implicit ctx: Context): Boolean = this.maybeOwner.isTypeParameterOrSkolem
535+
def isTypeParameterOrSkolem(implicit ctx: Context): Boolean = this.isTypeParam
536+
def enclClassChain(implicit ctx: Context): List[Symbol] = this.maybeOwner.enclClassChain
537+
final def isDerivedValueClass(implicit ctx: Context): Boolean =
538+
isClass && !denot.is(Flags.Package) && !denot.is(Flags.Trait) &&
539+
!ctx.phase.erasedTypes && denot.info.firstParent.typeSymbol == defn.AnyValClass && !denot.isPrimitiveValueClass
540+
541+
/** If this is a derived value class, return its unbox method
542+
* or NoSymbol if it does not exist.
543+
*/
544+
def derivedValueClassUnbox(implicit ctx: Context): Symbol = NoSymbol
545+
534546
/** The source or class file from which this class or
535547
* the class containing this symbol was generated, null if not applicable.
536548
* Overridden in ClassSymbol
@@ -637,17 +649,30 @@ object Symbols {
637649
denot.asInstanceOf[ClassDenotation]
638650

639651
override protected def prefixString = "ClassSymbol"
652+
653+
override def enclClassChain(implicit ctx: Context): List[Symbol] =
654+
if (this.is(Flags.PackageClass)) Nil
655+
else this :: denot.owner.enclClassChain
656+
657+
override def derivedValueClassUnbox(implicit ctx: Context): Symbol =
658+
// (info.decl(nme.unbox)) orElse uncomment once we accept unbox methods
659+
(denot.info.decls.find(_.denot.is(Flags.ParamAccessor | Flags.Method)))
660+
640661
}
641662

642663
class ErrorSymbol(val underlying: Symbol, msg: => String)(implicit ctx: Context) extends Symbol(NoCoord, ctx.nextId) {
643664
type ThisName = underlying.ThisName
644665
denot = underlying.denot
666+
667+
override def enclClassChain(implicit ctx: Context): List[Symbol] = Nil
645668
}
646669

647670
@sharable object NoSymbol extends Symbol(NoCoord, 0) {
648671
denot = NoDenotation
649672
override def associatedFile(implicit ctx: Context): AbstractFile = NoSource.file
650673
override def recomputeDenot(lastd: SymDenotation)(implicit ctx: Context): SymDenotation = NoDenotation
674+
675+
override def enclClassChain(implicit ctx: Context): List[Symbol] = Nil
651676
}
652677

653678
implicit class Copier[N <: Name](sym: Symbol { type ThisName = N })(implicit ctx: Context) {

compiler/src/dotty/tools/dotc/core/Types.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,24 @@ object Types {
745745
else ctx.asSeenFrom(this, pre, cls)
746746
}
747747

748+
/** The JVM tag for this type, if it's a primitive, `java.lang.Object` otherwise. */
749+
final def toTag(implicit ctx: Context): Name = {
750+
classSymbol match {
751+
case t if t eq defn.IntClass => nme.specializedTypeNames.Int
752+
case t if t eq defn.BooleanClass => nme.specializedTypeNames.Boolean
753+
case t if t eq defn.ByteClass => nme.specializedTypeNames.Byte
754+
case t if t eq defn.LongClass => nme.specializedTypeNames.Long
755+
case t if t eq defn.ShortClass => nme.specializedTypeNames.Short
756+
case t if t eq defn.FloatClass => nme.specializedTypeNames.Float
757+
case t if t eq defn.UnitClass => nme.specializedTypeNames.Void
758+
case t if t eq defn.DoubleClass => nme.specializedTypeNames.Double
759+
case t if t eq defn.CharClass => nme.specializedTypeNames.Char
760+
case _ => nme.specializedTypeNames.Object
761+
}
762+
}
763+
764+
765+
748766
// ----- Subtype-related --------------------------------------------
749767

750768
/** Is this type a subtype of that type? */

0 commit comments

Comments
 (0)