/* NSC -- new Scala compiler
* Copyright 2005-2014 LAMP/EPFL
* @author Martin Odersky
*/
package scala.tools.nsc
package backend.jvm
import scala.tools.asm
import scala.tools.nsc.backend.jvm.analysis.BackendUtils
import scala.tools.nsc.backend.jvm.opt._
import scala.tools.nsc.backend.jvm.BTypes._
import BackendReporting._
import scala.tools.nsc.settings.ScalaSettings
import scala.reflect.internal.Flags.{DEFERRED, SYNTHESIZE_IMPL_IN_SUBCLASS}
/**
* This class mainly contains the method classBTypeFromSymbol, which extracts the necessary
* information from a symbol and its type to create the corresponding ClassBType. It requires
* access to the compiler (global parameter).
*
* The mixin CoreBTypes defines core BTypes that are used in the backend. Building these BTypes
* uses classBTypeFromSymbol, hence requires access to the compiler (global).
*
* BTypesFromSymbols extends BTypes because the implementation of BTypes requires access to some
* of the core btypes. They are declared in BTypes as abstract members. Note that BTypes does
* not have access to the compiler instance.
*/
class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
import global._
import definitions._
import genBCode._
val backendUtils: BackendUtils[this.type] = new BackendUtils(this)
// Why the proxy, see documentation of class [[CoreBTypes]].
val coreBTypes = new CoreBTypesProxy[this.type](this)
import coreBTypes._
val byteCodeRepository: ByteCodeRepository[this.type] = new ByteCodeRepository(global.classPath, this)
val localOpt: LocalOpt[this.type] = new LocalOpt(this)
val inliner: Inliner[this.type] = new Inliner(this)
val inlinerHeuristics: InlinerHeuristics[this.type] = new InlinerHeuristics(this)
val closureOptimizer: ClosureOptimizer[this.type] = new ClosureOptimizer(this)
val callGraph: CallGraph[this.type] = new CallGraph(this)
val backendReporting: BackendReporting = new BackendReportingImpl(global)
final def initializeCoreBTypes(): Unit = {
coreBTypes.setBTypes(new CoreBTypes[this.type](this))
}
def recordPerRunCache[T <: collection.generic.Clearable](cache: T): T = perRunCaches.recordCache(cache)
def compilerSettings: ScalaSettings = settings
// helpers that need access to global.
// TODO @lry create a separate component, they don't belong to BTypesFromSymbols
final val strMODULE_INSTANCE_FIELD = nme.MODULE_INSTANCE_FIELD.toString
private val primitiveCompilationUnits = Set(
"Unit.scala",
"Boolean.scala",
"Char.scala",
"Byte.scala",
"Short.scala",
"Int.scala",
"Float.scala",
"Long.scala",
"Double.scala"
)
/**
* True if the current compilation unit is of a primitive class (scala.Boolean et al).
* Used only in assertions.
*/
def isCompilingPrimitive = {
primitiveCompilationUnits(currentUnit.source.file.name)
}
def isCompilingArray = {
currentUnit.source.file.name == "Array.scala"
}
// end helpers
/**
* The ClassBType for a class symbol `classSym`.
*
* The class symbol scala.Nothing is mapped to the class scala.runtime.Nothing$. Similarly,
* scala.Null is mapped to scala.runtime.Null$. This is because there exist no class files
* for the Nothing / Null. If used for example as a parameter type, we use the runtime classes
* in the classfile method signature.
*/
final def classBTypeFromSymbol(sym: Symbol): ClassBType = {
// For each java class, the scala compiler creates a class and a module (thus a module class).
// If the `sym` is a java module class, we use the java class instead. This ensures that the
// ClassBType is created from the main class (instead of the module class).
// The two symbols have the same name, so the resulting internalName is the same.
// Phase travel (exitingPickler) required for SI-6613 - linkedCoC is only reliable in early phases (nesting)
val classSym = if (sym.isJavaDefined && sym.isModuleClass) exitingPickler(sym.linkedClassOfClass) else sym
assert(classSym != NoSymbol, "Cannot create ClassBType from NoSymbol")
assert(classSym.isClass, s"Cannot create ClassBType from non-class symbol $classSym")
assertClassNotArrayNotPrimitive(classSym)
assert(!primitiveTypeToBType.contains(classSym) || isCompilingPrimitive, s"Cannot create ClassBType for primitive class symbol $classSym")
if (classSym == NothingClass) srNothingRef
else if (classSym == NullClass) srNullRef
else {
val internalName = classSym.javaBinaryNameString
classBTypeFromInternalName.getOrElse(internalName, {
// The new ClassBType is added to the map in its constructor, before we set its info. This
// allows initializing cyclic dependencies, see the comment on variable ClassBType._info.
val res = ClassBType(internalName)
if (completeSilentlyAndCheckErroneous(classSym)) {
res.info = Left(NoClassBTypeInfoClassSymbolInfoFailedSI9111(classSym.fullName))
res
} else {
setClassInfo(classSym, res)
}
})
}
}
/**
* Builds a [[MethodBType]] for a method symbol.
*/
final def methodBTypeFromSymbol(methodSymbol: Symbol): MethodBType = {
assert(methodSymbol.isMethod, s"not a method-symbol: $methodSymbol")
methodBTypeFromMethodType(methodSymbol.info, methodSymbol.isClassConstructor || methodSymbol.isConstructor)
}
/**
* Builds a [[MethodBType]] for a method type.
*/
final def methodBTypeFromMethodType(tpe: Type, isConstructor: Boolean): MethodBType = {
val resultType: BType =
if (isConstructor) UNIT
else typeToBType(tpe.resultType)
MethodBType(tpe.paramTypes map typeToBType, resultType)
}
def bootstrapMethodArg(t: Constant, pos: Position): AnyRef = t match {
case Constant(mt: Type) => methodBTypeFromMethodType(transformedType(mt), isConstructor = false).toASMType
case c @ Constant(sym: Symbol) => staticHandleFromSymbol(sym)
case c @ Constant(value: String) => value
case c @ Constant(value) if c.isNonUnitAnyVal => c.value.asInstanceOf[AnyRef]
case _ => reporter.error(pos, "Unable to convert static argument of ApplyDynamic into a classfile constant: " + t); null
}
def staticHandleFromSymbol(sym: Symbol): asm.Handle = {
val owner = if (sym.owner.isModuleClass) sym.owner.linkedClassOfClass else sym.owner
val descriptor = methodBTypeFromMethodType(sym.info, isConstructor = false).descriptor
val ownerBType = classBTypeFromSymbol(owner)
new asm.Handle(asm.Opcodes.H_INVOKESTATIC, ownerBType.internalName, sym.name.encoded, descriptor, /* itf = */ ownerBType.isInterface.get)
}
/**
* This method returns the BType for a type reference, for example a parameter type.
*/
final def typeToBType(t: Type): BType = {
import definitions.ArrayClass
/**
* Primitive types are represented as TypeRefs to the class symbol of, for example, scala.Int.
* The `primitiveTypeMap` maps those class symbols to the corresponding PrimitiveBType.
*/
def primitiveOrClassToBType(sym: Symbol): BType = {
assertClassNotArray(sym)
primitiveTypeToBType.getOrElse(sym, classBTypeFromSymbol(sym))
}
/**
* When compiling Array.scala, the type parameter T is not erased and shows up in method
* signatures, e.g. `def apply(i: Int): T`. A TypeRef for T is replaced by ObjectRef.
*/
def nonClassTypeRefToBType(sym: Symbol): ClassBType = {
assert(sym.isType && isCompilingArray, sym)
ObjectRef
}
t.dealiasWiden match {
case TypeRef(_, ArrayClass, List(arg)) => ArrayBType(typeToBType(arg)) // Array type such as Array[Int] (kept by erasure)
case TypeRef(_, sym, _) if !sym.isClass => nonClassTypeRefToBType(sym) // See comment on nonClassTypeRefToBType
case TypeRef(_, sym, _) => primitiveOrClassToBType(sym) // Common reference to a type such as scala.Int or java.lang.String
case ClassInfoType(_, _, sym) => primitiveOrClassToBType(sym) // We get here, for example, for genLoadModule, which invokes typeToBType(moduleClassSymbol.info)
/* The cases below should probably never occur. They are kept for now to avoid introducing
* new compiler crashes, but we added a warning. The compiler / library bootstrap and the
* test suite don't produce any warning.
*/
case tp =>
warning(tp.typeSymbol.pos,
s"an unexpected type representation reached the compiler backend while compiling $currentUnit: $tp. " +
"If possible, please file a bug on issues.scala-lang.org.")
tp match {
case ThisType(ArrayClass) => ObjectRef // was introduced in 9b17332f11 to fix SI-999, but this code is not reached in its test, or any other test
case ThisType(sym) => classBTypeFromSymbol(sym)
case SingleType(_, sym) => primitiveOrClassToBType(sym)
case ConstantType(_) => typeToBType(t.underlying)
case RefinedType(parents, _) => parents.map(typeToBType(_).asClassBType).reduceLeft((a, b) => a.jvmWiseLUB(b).get)
case AnnotatedType(_, t) => typeToBType(t)
case ExistentialType(_, t) => typeToBType(t)
}
}
}
def assertClassNotArray(sym: Symbol): Unit = {
assert(sym.isClass, sym)
assert(sym != definitions.ArrayClass || isCompilingArray, sym)
}
def assertClassNotArrayNotPrimitive(sym: Symbol): Unit = {
assertClassNotArray(sym)
assert(!primitiveTypeToBType.contains(sym) || isCompilingPrimitive, sym)
}
def implementedInterfaces(classSym: Symbol): List[Symbol] = {
// Additional interface parents based on annotations and other cues
def newParentForAnnotation(ann: AnnotationInfo): Option[Type] = ann.symbol match {
case RemoteAttr => Some(RemoteInterfaceClass.tpe)
case _ => None
}
// SI-9393: java annotations are interfaces, but the classfile / java source parsers make them look like classes.
def isInterfaceOrTrait(sym: Symbol) = sym.isInterface || sym.isTrait || sym.hasJavaAnnotationFlag
val classParents = {
val parents = classSym.info.parents
// SI-9393: the classfile / java source parsers add Annotation and ClassfileAnnotation to the
// parents of a java annotations. undo this for the backend (where we need classfile-level information).
if (classSym.hasJavaAnnotationFlag) parents.filterNot(c => c.typeSymbol == ClassfileAnnotationClass || c.typeSymbol == AnnotationClass)
else parents
}
val allParents = classParents ++ classSym.annotations.flatMap(newParentForAnnotation)
val minimizedParents = if (classSym.isJavaDefined) allParents else erasure.minimizeParents(allParents)
// We keep the superClass when computing minimizeParents to eliminate more interfaces.
// Example: T can be eliminated from D
// trait T
// class C extends T
// class D extends C with T
val interfaces = minimizedParents match {
case superClass :: ifs if !isInterfaceOrTrait(superClass.typeSymbol) =>
ifs
case ifs =>
// minimizeParents removes the superclass if it's redundant, for example:
// trait A
// class C extends Object with A // minimizeParents removes Object
ifs
}
interfaces.map(_.typeSymbol)
}
/**
* The member classes of a class symbol. Note that the result of this method depends on the
* current phase, for example, after lambdalift, all local classes become member of the enclosing
* class.
*
* Specialized classes are always considered top-level, see comment in BTypes.
*/
private def memberClassesForInnerClassTable(classSymbol: Symbol): List[Symbol] = classSymbol.info.decls.collect({
case sym if sym.isClass && !considerAsTopLevelImplementationArtifact(sym) =>
sym
case sym if sym.isModule && !considerAsTopLevelImplementationArtifact(sym) =>
val r = exitingPickler(sym.moduleClass)
assert(r != NoSymbol, sym.fullLocationString)
r
})(collection.breakOut)
private def setClassInfo(classSym: Symbol, classBType: ClassBType): ClassBType = {
/**
* Reconstruct the classfile flags from a Java defined class symbol.
*
* The implementation of this method is slightly different from `javaFlags` in BTypesFromSymbols.
* The javaFlags method is primarily used to map Scala symbol flags to sensible classfile flags
* that are used in the generated classfiles. For example, all classes emitted by the Scala
* compiler have ACC_PUBLIC.
*
* When building a [[ClassBType]] from a Java class symbol, the flags in the type's `info` have
* to correspond exactly to the flags in the classfile. For example, if the class is package
* protected (i.e., it doesn't have the ACC_PUBLIC flag), this needs to be reflected in the
* ClassBType. For example, the inliner needs the correct flags for access checks.
*
* Class flags are listed here:
* https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.1-200-E.1
*/
def javaClassfileFlags(classSym: Symbol): Int = {
assert(classSym.isJava, s"Expected Java class symbol, got ${classSym.fullName}")
import asm.Opcodes._
def enumFlags = ACC_ENUM | {
// Java enums have the `ACC_ABSTRACT` flag if they have a deferred method.
// We cannot trust `hasAbstractFlag`: the ClassfileParser adds `ABSTRACT` and `SEALED` to all
// Java enums for exhaustiveness checking.
val hasAbstractMethod = classSym.info.decls.exists(s => s.isMethod && s.isDeferred)
if (hasAbstractMethod) ACC_ABSTRACT else 0
}
GenBCode.mkFlags(
// SI-9393: the classfile / java source parser make java annotation symbols look like classes.
// here we recover the actual classfile flags.
if (classSym.hasJavaAnnotationFlag) ACC_ANNOTATION | ACC_INTERFACE | ACC_ABSTRACT else 0,
if (classSym.isPublic) ACC_PUBLIC else 0,
if (classSym.isFinal) ACC_FINAL else 0,
// see the link above. javac does the same: ACC_SUPER for all classes, but not interfaces.
if (classSym.isInterface) ACC_INTERFACE else ACC_SUPER,
// for Java enums, we cannot trust `hasAbstractFlag` (see comment in enumFlags)
if (!classSym.hasJavaEnumFlag && classSym.hasAbstractFlag) ACC_ABSTRACT else 0,
if (classSym.isArtifact) ACC_SYNTHETIC else 0,
if (classSym.hasJavaEnumFlag) enumFlags else 0
)
}
// Check for hasAnnotationFlag for SI-9393: the classfile / java source parsers add
// scala.annotation.Annotation as superclass to java annotations. In reality, java
// annotation classfiles have superclass Object (like any interface classfile).
val superClassSym = if (classSym.hasJavaAnnotationFlag) ObjectClass else {
val sc = classSym.superClass
// SI-9393: Java annotation classes don't have the ABSTRACT/INTERFACE flag, so they appear
// (wrongly) as superclasses. Fix this for BTypes: the java annotation will appear as interface
// (handled by method implementedInterfaces), the superclass is set to Object.
if (sc.hasJavaAnnotationFlag) ObjectClass
else sc
}
assert(
if (classSym == ObjectClass)
superClassSym == NoSymbol
else if (classSym.isInterface)
superClassSym == ObjectClass
else
// A ClassBType for a primitive class (scala.Boolean et al) is only created when compiling these classes.
((superClassSym != NoSymbol) && !superClassSym.isInterface) || (isCompilingPrimitive && primitiveTypeToBType.contains(classSym)),
s"Bad superClass for $classSym: $superClassSym"
)
val superClass = if (superClassSym == NoSymbol) None
else Some(classBTypeFromSymbol(superClassSym))
val interfaces = implementedInterfaces(classSym).map(classBTypeFromSymbol)
val flags = {
if (classSym.isJava) javaClassfileFlags(classSym) // see comment on javaClassfileFlags
else javaFlags(classSym)
}
/* The InnerClass table of a class C must contain all nested classes of C, even if they are only
* declared but not otherwise referenced in C (from the bytecode or a method / field signature).
* We collect them here.
*/
val nestedClassSymbols = {
val linkedClass = exitingPickler(classSym.linkedClassOfClass) // linkedCoC does not work properly in late phases
// The lambdalift phase lifts all nested classes to the enclosing class, so if we collect
// member classes right after lambdalift, we obtain all nested classes, including local and
// anonymous ones.
val nestedClasses = {
val allNested = exitingPhase(currentRun.lambdaliftPhase)(memberClassesForInnerClassTable(classSym))
val nested = {
// Classes nested in value classes are nested in the companion at this point. For InnerClass /
// EnclosingMethod, we use the value class as the outer class. So we remove nested classes
// from the companion that were originally nested in the value class.
if (exitingPickler(linkedClass.isDerivedValueClass)) allNested.filterNot(classOriginallyNestedInClass(_, linkedClass))
else allNested
}
if (isTopLevelModuleClass(classSym)) {
// For Java compatibility, member classes of top-level objects are treated as members of
// the top-level companion class, see comment below.
val members = exitingPickler(memberClassesForInnerClassTable(classSym))
nested diff members
} else {
nested
}
}
val companionModuleMembers = if (considerAsTopLevelImplementationArtifact(classSym)) Nil else {
// If this is a top-level class, the member classes of the companion object are added as
// members of the class. For example:
// class C { }
// object C {
// class D
// def f = { class E }
// }
// The class D is added as a member of class C. The reason is: for Java compatibility, the
// InnerClass attribute for D has "C" (NOT the module class "C$") as the outer class of D
// (done by buildNestedInfo). See comment in BTypes.
// For consistency, the InnerClass entry for D needs to be present in C - to Java it looks
// like D is a member of C, not C$.
val javaCompatMembers = {
if (linkedClass != NoSymbol && isTopLevelModuleClass(linkedClass))
// phase travel to exitingPickler: this makes sure that memberClassesForInnerClassTable only sees member
// classes, not local classes of the companion module (E in the example) that were lifted by lambdalift.
exitingPickler(memberClassesForInnerClassTable(linkedClass))
else
Nil
}
// Classes nested in value classes are nested in the companion at this point. For InnerClass /
// EnclosingMethod we use the value class as enclosing class. Here we search nested classes
// in the companion that were originally nested in the value class, and we add them as nested
// in the value class.
val valueClassCompanionMembers = {
if (linkedClass != NoSymbol && exitingPickler(classSym.isDerivedValueClass)) {
val moduleMemberClasses = exitingPhase(currentRun.lambdaliftPhase)(memberClassesForInnerClassTable(linkedClass))
moduleMemberClasses.filter(classOriginallyNestedInClass(_, classSym))
} else
Nil
}
javaCompatMembers ++ valueClassCompanionMembers
}
nestedClasses ++ companionModuleMembers
}
/**
* For nested java classes, the scala compiler creates both a class and a module (and therefore
* a module class) symbol. For example, in `class A { class B {} }`, the nestedClassSymbols
* for A contain both the class B and the module class B.
* Here we get rid of the module class B, making sure that the class B is present.
*/
val nestedClassSymbolsNoJavaModuleClasses = nestedClassSymbols.filter(s => {
if (s.isJavaDefined && s.isModuleClass) {
// We could also search in nestedClassSymbols for s.linkedClassOfClass, but sometimes that
// returns NoSymbol, so it doesn't work.
val nb = nestedClassSymbols.count(mc => mc.name == s.name && mc.owner == s.owner)
assert(nb == 2, s"Java member module without member class: $s - $nestedClassSymbols")
false
} else true
})
val nestedClasses = nestedClassSymbolsNoJavaModuleClasses.map(classBTypeFromSymbol)
val nestedInfo = buildNestedInfo(classSym)
val inlineInfo = buildInlineInfo(classSym, classBType.internalName)
classBType.info = Right(ClassInfo(superClass, interfaces, flags, nestedClasses, nestedInfo, inlineInfo))
classBType
}
private def buildNestedInfo(innerClassSym: Symbol): Option[NestedInfo] = {
assert(innerClassSym.isClass, s"Cannot build NestedInfo for non-class symbol $innerClassSym")
val isTopLevel = innerClassSym.rawowner.isPackageClass
// specialized classes are considered top-level, see comment in BTypes
if (isTopLevel || considerAsTopLevelImplementationArtifact(innerClassSym)) None
else if (innerClassSym.rawowner.isTerm) {
// This case should never be reached: the lambdalift phase mutates the rawowner field of all
// classes to be the enclosing class. SI-9392 shows an errant macro that leaves a reference
// to a local class symbol that no longer exists, which is not updated by lambdalift.
devWarning(innerClassSym.pos,
s"""The class symbol $innerClassSym with the term symbol ${innerClassSym.rawowner} as `rawowner` reached the backend.
|Most likely this indicates a stale reference to a non-existing class introduced by a macro, see SI-9392.""".stripMargin)
None
} else {
// See comment in BTypes, when is a class marked static in the InnerClass table.
val isStaticNestedClass = isOriginallyStaticOwner(innerClassSym.originalOwner)
// After lambdalift (which is where we are), the rawowner field contains the enclosing class.
val enclosingClass = {
// (1) Example java source: class C { static class D { } }
// The Scala compiler creates a class and a module symbol for C. Because D is a static
// nested class, the symbol for D is nested in the module class C (not in the class C).
// For the InnerClass attribute, we use the class symbol C, which represents the situation
// in the source code.
// (2) Java compatibility. See the big comment in BTypes that summarizes the InnerClass spec.
if ((innerClassSym.isJavaDefined && innerClassSym.rawowner.isModuleClass) || // (1)
(!isAnonymousOrLocalClass(innerClassSym) && isTopLevelModuleClass(innerClassSym.rawowner))) { // (2)
// phase travel for linkedCoC - does not always work in late phases
exitingPickler(innerClassSym.rawowner.linkedClassOfClass) match {
case NoSymbol =>
// For top-level modules without a companion class, see doc of mirrorClassClassBType.
mirrorClassClassBType(exitingPickler(innerClassSym.rawowner))
case companionClass =>
classBTypeFromSymbol(companionClass)
}
} else {
classBTypeFromSymbol(innerClassSym.rawowner)
}
}
val outerName: Option[String] = {
if (isAnonymousOrLocalClass(innerClassSym)) None
else Some(enclosingClass.internalName)
}
val innerName: Option[String] = {
// phase travel necessary: after flatten, the name includes the name of outer classes.
// if some outer name contains $anon, a non-anon class is considered anon.
if (exitingPickler(innerClassSym.isAnonymousClass || innerClassSym.isAnonymousFunction)) None
else Some(innerClassSym.rawname + innerClassSym.moduleSuffix) // moduleSuffix for module classes
}
Some(NestedInfo(enclosingClass, outerName, innerName, isStaticNestedClass))
}
}
/**
* Build the InlineInfo for a ClassBType from the class symbol.
*
* Note that the InlineInfo is only built from the symbolic information for classes that are being
* compiled. For all other classes we delegate to inlineInfoFromClassfile. The reason is that
* mixed-in methods are only added to class symbols being compiled, but not to other classes
* extending traits. Creating the InlineInfo from the symbol would prevent these mixins from being
* inlined.
*
* So for classes being compiled, the InlineInfo is created here and stored in the ScalaInlineInfo
* classfile attribute.
*/
private def buildInlineInfo(classSym: Symbol, internalName: InternalName): InlineInfo = {
def buildFromSymbol = buildInlineInfoFromClassSymbol(classSym)
// phase travel required, see implementation of `compiles`. for nested classes, it checks if the
// enclosingTopLevelClass is being compiled. after flatten, all classes are considered top-level,
// so `compiles` would return `false`.
if (exitingPickler(currentRun.compiles(classSym))) buildFromSymbol // InlineInfo required for classes being compiled, we have to create the classfile attribute
else if (!compilerSettings.optInlinerEnabled) BTypes.EmptyInlineInfo // For other classes, we need the InlineInfo only inf the inliner is enabled.
else {
// For classes not being compiled, the InlineInfo is read from the classfile attribute. This
// fixes an issue with mixed-in methods: the mixin phase enters mixin methods only to class
// symbols being compiled. For non-compiled classes, we could not build MethodInlineInfos
// for those mixin members, which prevents inlining.
byteCodeRepository.classNode(internalName) match {
case Right(classNode) =>
inlineInfoFromClassfile(classNode)
case Left(missingClass) =>
EmptyInlineInfo.copy(warning = Some(ClassNotFoundWhenBuildingInlineInfoFromSymbol(missingClass)))
}
}
}
/**
* Build the [[InlineInfo]] for a class symbol.
*/
def buildInlineInfoFromClassSymbol(classSym: Symbol): InlineInfo = {
val isEffectivelyFinal = classSym.isEffectivelyFinal
val sam = {
if (classSym.isEffectivelyFinal) None
else {
// Phase travel necessary. For example, nullary methods (getter of an abstract val) get an
// empty parameter list in uncurry and would therefore be picked as SAM.
// Similarly, the fields phases adds abstract trait setters, which should not be considered
// abstract for SAMs (they do disqualify the SAM from LMF treatment,
// but an anonymous subclasss can be spun up by scalac after making just the single abstract method concrete)
val samSym = exitingPickler(definitions.samOf(classSym.tpe))
if (samSym == NoSymbol) None
else Some(samSym.javaSimpleName.toString + methodBTypeFromSymbol(samSym).descriptor)
}
}
var warning = Option.empty[ClassSymbolInfoFailureSI9111]
def keepMember(sym: Symbol) = sym.isMethod && !scalaPrimitives.isPrimitive(sym)
val classMethods = classSym.info.decls.iterator.filter(keepMember)
val methods = if (!classSym.isJavaDefined) classMethods else {
val staticMethods = classSym.companionModule.info.decls.iterator.filter(m => !m.isConstructor && keepMember(m))
staticMethods ++ classMethods
}
// Primitive methods cannot be inlined, so there's no point in building a MethodInlineInfo. Also, some
// primitive methods (e.g., `isInstanceOf`) have non-erased types, which confuses [[typeToBType]].
val methodInlineInfos = methods.flatMap({
case methodSym =>
if (completeSilentlyAndCheckErroneous(methodSym)) {
// Happens due to SI-9111. Just don't provide any MethodInlineInfo for that method, we don't need fail the compiler.
if (!classSym.isJavaDefined) devWarning("SI-9111 should only be possible for Java classes")
warning = Some(ClassSymbolInfoFailureSI9111(classSym.fullName))
Nil
} else {
val name = methodSym.javaSimpleName.toString // same as in genDefDef
val signature = name + methodBTypeFromSymbol(methodSym).descriptor
// In `trait T { object O }`, `oSym.isEffectivelyFinalOrNotOverridden` is true, but the
// method is abstract in bytecode, `defDef.rhs.isEmpty`. Abstract methods are excluded
// so they are not marked final in the InlineInfo attribute.
//
// However, due to https://github.com/scala/scala-dev/issues/126, this currently does not
// work, the abstract accessor for O will be marked effectivelyFinal.
val effectivelyFinal = methodSym.isEffectivelyFinalOrNotOverridden && !(methodSym hasFlag DEFERRED | SYNTHESIZE_IMPL_IN_SUBCLASS)
val info = MethodInlineInfo(
effectivelyFinal = effectivelyFinal,
annotatedInline = methodSym.hasAnnotation(ScalaInlineClass),
annotatedNoInline = methodSym.hasAnnotation(ScalaNoInlineClass))
if (needsStaticImplMethod(methodSym)) {
val staticName = traitSuperAccessorName(methodSym).toString
val selfParam = methodSym.newSyntheticValueParam(methodSym.owner.typeConstructor, nme.SELF)
val staticMethodType = methodSym.info match {
case mt @ MethodType(params, res) => copyMethodType(mt, selfParam :: params, res)
}
val staticMethodSignature = staticName + methodBTypeFromMethodType(staticMethodType, isConstructor = false)
val staticMethodInfo = MethodInlineInfo(
effectivelyFinal = true,
annotatedInline = info.annotatedInline,
annotatedNoInline = info.annotatedNoInline)
if (methodSym.isMixinConstructor)
List((staticMethodSignature, staticMethodInfo))
else
List((signature, info), (staticMethodSignature, staticMethodInfo))
} else
List((signature, info))
}
}).toMap
InlineInfo(isEffectivelyFinal, sam, methodInlineInfos, warning)
}
/**
* For top-level objects without a companion class, the compiler generates a mirror class with
* static forwarders (Java compat). There's no symbol for the mirror class, but we still need a
* ClassBType (its info.nestedClasses will hold the InnerClass entries, see comment in BTypes).
*/
def mirrorClassClassBType(moduleClassSym: Symbol): ClassBType = {
assert(isTopLevelModuleClass(moduleClassSym), s"not a top-level module class: $moduleClassSym")
val internalName = moduleClassSym.javaBinaryNameString.stripSuffix(nme.MODULE_SUFFIX_STRING)
classBTypeFromInternalName.getOrElse(internalName, {
val c = ClassBType(internalName)
// class info consistent with BCodeHelpers.genMirrorClass
val nested = exitingPickler(memberClassesForInnerClassTable(moduleClassSym)) map classBTypeFromSymbol
c.info = Right(ClassInfo(
superClass = Some(ObjectRef),
interfaces = Nil,
flags = asm.Opcodes.ACC_SUPER | asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_FINAL,
nestedClasses = nested,
nestedInfo = None,
inlineInfo = EmptyInlineInfo.copy(isEffectivelyFinal = true))) // no method inline infos needed, scala never invokes methods on the mirror class
c
})
}
def beanInfoClassClassBType(mainClass: Symbol): ClassBType = {
val internalName = mainClass.javaBinaryNameString + "BeanInfo"
classBTypeFromInternalName.getOrElse(internalName, {
val c = ClassBType(internalName)
c.info = Right(ClassInfo(
superClass = Some(sbScalaBeanInfoRef),
interfaces = Nil,
flags = javaFlags(mainClass),
nestedClasses = Nil,
nestedInfo = None,
inlineInfo = EmptyInlineInfo))
c
})
}
/**
* True for module classes of package level objects. The backend will generate a mirror class for
* such objects.
*/
final def isTopLevelModuleClass(sym: Symbol): Boolean = exitingPickler {
// phase travel to pickler required for isNestedClass (looks at owner)
sym.isModuleClass && !sym.isNestedClass
}
/**
* True for module classes of modules that are top-level or owned only by objects. Module classes
* for such objects will get a MODULE$ field and a corresponding static initializer.
*/
final def isStaticModuleClass(sym: Symbol): Boolean = {
sym.isModuleClass &&
isOriginallyStaticOwner(sym.originalOwner) // isStaticModuleClass is a source-level property, see comment on isOriginallyStaticOwner
}
// legacy, to be removed when the @remote annotation gets removed
final def isRemote(s: Symbol) = s hasAnnotation definitions.RemoteAttr
final def hasPublicBitSet(flags: Int) = (flags & asm.Opcodes.ACC_PUBLIC) != 0
/**
* Return the Java modifiers for the given symbol.
* Java modifiers for classes:
* - public, abstract, final, strictfp (not used)
* for interfaces:
* - the same as for classes, without 'final'
* for fields:
* - public, private (*)
* - static, final
* for methods:
* - the same as for fields, plus:
* - abstract, synchronized (not used), strictfp (not used), native (not used)
* for all:
* - deprecated
*
* (*) protected cannot be used, since inner classes 'see' protected members,
* and they would fail verification after lifted.
*/
final def javaFlags(sym: Symbol): Int = {
// constructors of module classes should be private. introduced in b06edbc, probably to prevent
// creating module instances from java. for nested modules, the constructor needs to be public
// since they are created by the outer class and stored in a field. a java client can create
// new instances via outerClassInstance.new InnerModuleClass$().
// TODO: do this early, mark the symbol private.
val privateFlag =
sym.isPrivate || (sym.isPrimaryConstructor && isTopLevelModuleClass(sym.owner))
// Symbols marked in source as `final` have the FINAL flag. (In the past, the flag was also
// added to modules and module classes, not anymore since 296b706).
// Note that the presence of the `FINAL` flag on a symbol does not correspond 1:1 to emitting
// ACC_FINAL in bytecode.
//
// Top-level modules are marked ACC_FINAL in bytecode (even without the FINAL flag). Nested
// objects don't get the flag to allow overriding (under -Yoverride-objects, SI-5676).
//
// For fields, only eager val fields can receive ACC_FINAL. vars or lazy vals can't:
// Source: http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.3
// "Another problem is that the specification allows aggressive
// optimization of final fields. Within a thread, it is permissible to
// reorder reads of a final field with those modifications of a final
// field that do not take place in the constructor."
//
// A var or lazy val which is marked final still has meaning to the
// scala compiler. The word final is heavily overloaded unfortunately;
// for us it means "not overridable". At present you can't override
// vars regardless; this may change.
val finalFlag = (
(sym.isFinal || isTopLevelModuleClass(sym))
&& !sym.enclClass.isTrait
&& !sym.isClassConstructor
&& (!sym.isMutable || nme.isTraitSetterName(sym.name)) // lazy vals and vars and their setters cannot be final, but trait setters are
)
// Primitives are "abstract final" to prohibit instantiation
// without having to provide any implementations, but that is an
// illegal combination of modifiers at the bytecode level so
// suppress final if abstract is present.
import asm.Opcodes._
GenBCode.mkFlags(
if (privateFlag) ACC_PRIVATE else ACC_PUBLIC,
if ((sym.isDeferred && !sym.hasFlag(symtab.Flags.JAVA_DEFAULTMETHOD))|| sym.hasAbstractFlag) ACC_ABSTRACT else 0,
if (sym.isTraitOrInterface) ACC_INTERFACE else 0,
if (finalFlag && !sym.hasAbstractFlag) ACC_FINAL else 0,
if (sym.isStaticMember) ACC_STATIC else 0,
if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0,
if (sym.isArtifact) ACC_SYNTHETIC else 0,
if (sym.isClass && !sym.isTraitOrInterface) ACC_SUPER else 0,
if (sym.hasJavaEnumFlag) ACC_ENUM else 0,
if (sym.isVarargsMethod) ACC_VARARGS else 0,
if (sym.hasFlag(symtab.Flags.SYNCHRONIZED)) ACC_SYNCHRONIZED else 0,
if (sym.isDeprecated) asm.Opcodes.ACC_DEPRECATED else 0
)
}
def javaFieldFlags(sym: Symbol) = {
javaFlags(sym) | GenBCode.mkFlags(
if (sym hasAnnotation TransientAttr) asm.Opcodes.ACC_TRANSIENT else 0,
if (sym hasAnnotation VolatileAttr) asm.Opcodes.ACC_VOLATILE else 0,
if (sym.isMutable) 0 else asm.Opcodes.ACC_FINAL
)
}
}