summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala')
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala392
1 files changed, 316 insertions, 76 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
index a9bce82acd..d8a17e975e 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
@@ -6,18 +6,23 @@
package scala.tools.nsc
package backend.jvm
+import scala.annotation.switch
+import scala.collection.concurrent.TrieMap
+import scala.reflect.internal.util.Position
import scala.tools.asm
import asm.Opcodes
-import scala.tools.asm.tree.{InnerClassNode, ClassNode}
-import opt.ByteCodeRepository
+import scala.tools.asm.tree.{MethodInsnNode, InnerClassNode, ClassNode}
+import scala.tools.nsc.backend.jvm.BTypes.{InlineInfo, MethodInlineInfo}
+import scala.tools.nsc.backend.jvm.BackendReporting._
+import scala.tools.nsc.backend.jvm.opt._
import scala.collection.convert.decorateAsScala._
/**
- * The BTypes component defines The BType class hierarchy. BTypes encapsulate all type information
+ * The BTypes component defines The BType class hierarchy. A BType stores all type information
* that is required after building the ASM nodes. This includes optimizations, generation of
* InnerClass attributes and generation of stack map frames.
*
- * This representation is immutable and independent of the compiler data structures, hence it can
+ * The representation is immutable and independent of the compiler data structures, hence it can
* be queried by concurrent threads.
*/
abstract class BTypes {
@@ -34,9 +39,24 @@ abstract class BTypes {
*/
val byteCodeRepository: ByteCodeRepository
+ val inliner: Inliner[this.type]
+
+ val callGraph: CallGraph[this.type]
+
+ val backendReporting: BackendReporting
+
// Allows to define per-run caches here and in the CallGraph component, which don't have a global
def recordPerRunCache[T <: collection.generic.Clearable](cache: T): T
+ // When building the call graph, we need to know if global inlining is allowed (the component doesn't have a global)
+ def inlineGlobalEnabled: Boolean
+
+ // When the inliner is not enabled, there's no point in adding InlineInfos to all ClassBTypes
+ def inlinerEnabled: Boolean
+
+ // Settings that define what kind of optimizer warnings are emitted.
+ def warnSettings: WarnSettings
+
/**
* A map from internal names to ClassBTypes. Every ClassBType is added to this map on its
* construction.
@@ -48,13 +68,57 @@ abstract class BTypes {
* Concurrent because stack map frames are computed when in the class writer, which might run
* on multiple classes concurrently.
*/
- val classBTypeFromInternalName: collection.concurrent.Map[InternalName, ClassBType] = recordPerRunCache(collection.concurrent.TrieMap.empty[InternalName, ClassBType])
+ val classBTypeFromInternalName: collection.concurrent.Map[InternalName, ClassBType] = recordPerRunCache(TrieMap.empty)
/**
- * Parse the classfile for `internalName` and construct the [[ClassBType]].
+ * Store the position of every MethodInsnNode during code generation. This allows each callsite
+ * in the call graph to remember its source position, which is required for inliner warnings.
+ */
+ val callsitePositions: collection.concurrent.Map[MethodInsnNode, Position] = recordPerRunCache(TrieMap.empty)
+
+ /**
+ * Contains the internal names of all classes that are defined in Java source files of the current
+ * compilation run (mixed compilation). Used for more detailed error reporting.
+ */
+ val javaDefinedClasses: collection.mutable.Set[InternalName] = recordPerRunCache(collection.mutable.Set.empty)
+
+ /**
+ * Obtain the BType for a type descriptor or internal name. For class descriptors, the ClassBType
+ * is constructed by parsing the corresponding classfile.
+ *
+ * Some JVM operations use either a full descriptor or only an internal name. Example:
+ * ANEWARRAY java/lang/String // a new array of strings (internal name for the String class)
+ * ANEWARRAY [Ljava/lang/String; // a new array of array of string (full descriptor for the String class)
+ *
+ * This method supports both descriptors and internal names.
+ */
+ def bTypeForDescriptorOrInternalNameFromClassfile(desc: String): BType = (desc(0): @switch) match {
+ case 'V' => UNIT
+ case 'Z' => BOOL
+ case 'C' => CHAR
+ case 'B' => BYTE
+ case 'S' => SHORT
+ case 'I' => INT
+ case 'F' => FLOAT
+ case 'J' => LONG
+ case 'D' => DOUBLE
+ case '[' => ArrayBType(bTypeForDescriptorOrInternalNameFromClassfile(desc.substring(1)))
+ case 'L' if desc.last == ';' => classBTypeFromParsedClassfile(desc.substring(1, desc.length - 1))
+ case _ => classBTypeFromParsedClassfile(desc)
+ }
+
+ /**
+ * Parse the classfile for `internalName` and construct the [[ClassBType]]. If the classfile cannot
+ * be found in the `byteCodeRepository`, the `info` of the resulting ClassBType is undefined.
*/
def classBTypeFromParsedClassfile(internalName: InternalName): ClassBType = {
- classBTypeFromClassNode(byteCodeRepository.classNode(internalName))
+ classBTypeFromInternalName.getOrElse(internalName, {
+ val res = ClassBType(internalName)
+ byteCodeRepository.classNode(internalName) match {
+ case Left(msg) => res.info = Left(NoClassBTypeInfoMissingBytecode(msg)); res
+ case Right(c) => setClassInfoFromParsedClassfile(c, res)
+ }
+ })
}
/**
@@ -62,11 +126,11 @@ abstract class BTypes {
*/
def classBTypeFromClassNode(classNode: ClassNode): ClassBType = {
classBTypeFromInternalName.getOrElse(classNode.name, {
- setClassInfo(classNode, ClassBType(classNode.name))
+ setClassInfoFromParsedClassfile(classNode, ClassBType(classNode.name))
})
}
- private def setClassInfo(classNode: ClassNode, classBType: ClassBType): ClassBType = {
+ private def setClassInfoFromParsedClassfile(classNode: ClassNode, classBType: ClassBType): ClassBType = {
val superClass = classNode.superName match {
case null =>
assert(classNode.name == ObjectReference.internalName, s"class with missing super type: ${classNode.name}")
@@ -89,11 +153,13 @@ abstract class BTypes {
* For local and anonymous classes, innerClassNode.outerName is null. Such classes are required
* to have an EnclosingMethod attribute declaring the outer class. So we keep those local and
* anonymous classes whose outerClass is classNode.name.
- *
*/
def nestedInCurrentClass(innerClassNode: InnerClassNode): Boolean = {
(innerClassNode.outerName != null && innerClassNode.outerName == classNode.name) ||
- (innerClassNode.outerName == null && byteCodeRepository.classNode(innerClassNode.name).outerClass == classNode.name)
+ (innerClassNode.outerName == null && {
+ val classNodeForInnerClass = byteCodeRepository.classNode(innerClassNode.name).get // TODO: don't get here, but set the info to Left at the end
+ classNodeForInnerClass.outerClass == classNode.name
+ })
}
val nestedClasses: List[ClassBType] = classNode.innerClasses.asScala.collect({
@@ -116,11 +182,58 @@ abstract class BTypes {
val staticFlag = (innerEntry.access & Opcodes.ACC_STATIC) != 0
NestedInfo(enclosingClass, Option(innerEntry.outerName), Option(innerEntry.innerName), staticFlag)
}
- classBType.info = ClassInfo(superClass, interfaces, flags, nestedClasses, nestedInfo)
+
+ val inlineInfo = inlineInfoFromClassfile(classNode)
+
+ classBType.info = Right(ClassInfo(superClass, interfaces, flags, nestedClasses, nestedInfo, inlineInfo))
classBType
}
/**
+ * Build the InlineInfo for a class. For Scala classes, the information is stored in the
+ * ScalaInlineInfo attribute. If the attribute is missing, the InlineInfo is built using the
+ * metadata available in the classfile (ACC_FINAL flags, etc).
+ */
+ def inlineInfoFromClassfile(classNode: ClassNode): InlineInfo = {
+ def fromClassfileAttribute: Option[InlineInfo] = {
+ if (classNode.attrs == null) None
+ else classNode.attrs.asScala.collect({ case a: InlineInfoAttribute => a}).headOption.map(_.inlineInfo)
+ }
+
+ def fromClassfileWithoutAttribute = {
+ val warning = {
+ val isScala = classNode.attrs != null && classNode.attrs.asScala.exists(a => a.`type` == BTypes.ScalaAttributeName || a.`type` == BTypes.ScalaSigAttributeName)
+ if (isScala) Some(NoInlineInfoAttribute(classNode.name))
+ else None
+ }
+ // when building MethodInlineInfos for the members of a ClassSymbol, we exclude those methods
+ // in scalaPrimitives. This is necessary because some of them have non-erased types, which would
+ // require special handling. Excluding is OK because they are never inlined.
+ // Here we are parsing from a classfile and we don't need to do anything special. Many of these
+ // primitives don't even exist, for example Any.isInstanceOf.
+ val methodInfos = classNode.methods.asScala.map(methodNode => {
+ val info = MethodInlineInfo(
+ effectivelyFinal = BytecodeUtils.isFinalMethod(methodNode),
+ traitMethodWithStaticImplementation = false,
+ annotatedInline = false,
+ annotatedNoInline = false)
+ (methodNode.name + methodNode.desc, info)
+ }).toMap
+ InlineInfo(
+ traitImplClassSelfType = None,
+ isEffectivelyFinal = BytecodeUtils.isFinalClass(classNode),
+ methodInfos = methodInfos,
+ warning)
+ }
+
+ // The InlineInfo is built from the classfile (not from the symbol) for all classes that are NOT
+ // being compiled. For those classes, the info is only needed if the inliner is enabled, othewise
+ // we can save the memory.
+ if (!inlinerEnabled) BTypes.EmptyInlineInfo
+ else fromClassfileAttribute getOrElse fromClassfileWithoutAttribute
+ }
+
+ /**
* A BType is either a primitive type, a ClassBType, an ArrayBType of one of these, or a MethodType
* referring to BTypes.
*/
@@ -184,7 +297,7 @@ abstract class BTypes {
* promotions (e.g. BYTE to INT). Its operation can be visualized more easily in terms of the
* Java bytecode type hierarchy.
*/
- final def conformsTo(other: BType): Boolean = {
+ final def conformsTo(other: BType): Either[NoClassBTypeInfo, Boolean] = tryEither(Right({
assert(isRef || isPrimitive, s"conformsTo cannot handle $this")
assert(other.isRef || other.isPrimitive, s"conformsTo cannot handle $other")
@@ -192,7 +305,7 @@ abstract class BTypes {
case ArrayBType(component) =>
if (other == ObjectReference || other == jlCloneableReference || other == jioSerializableReference) true
else other match {
- case ArrayBType(otherComponoent) => component.conformsTo(otherComponoent)
+ case ArrayBType(otherComponoent) => component.conformsTo(otherComponoent).orThrow
case _ => false
}
@@ -201,7 +314,7 @@ abstract class BTypes {
if (other.isBoxed) this == other
else if (other == ObjectReference) true
else other match {
- case otherClassType: ClassBType => classType.isSubtypeOf(otherClassType) // e.g., java/lang/Double conforms to java/lang/Number
+ case otherClassType: ClassBType => classType.isSubtypeOf(otherClassType).orThrow // e.g., java/lang/Double conforms to java/lang/Number
case _ => false
}
} else if (isNullType) {
@@ -211,7 +324,7 @@ abstract class BTypes {
} else if (isNothingType) {
true
} else other match {
- case otherClassType: ClassBType => classType.isSubtypeOf(otherClassType)
+ case otherClassType: ClassBType => classType.isSubtypeOf(otherClassType).orThrow
// case ArrayBType(_) => this.isNullType // documentation only, because `if (isNullType)` above covers this case
case _ =>
// isNothingType || // documentation only, because `if (isNothingType)` above covers this case
@@ -226,7 +339,7 @@ abstract class BTypes {
assert(isPrimitive && other.isPrimitive, s"Expected primitive types $this - $other")
this == other
}
- }
+ }))
/**
* Compute the upper bound of two types.
@@ -245,7 +358,7 @@ abstract class BTypes {
ObjectReference
case _: MethodBType =>
- throw new AssertionError(s"unexpected method type when computing maxType: $this")
+ assertionError(s"unexpected method type when computing maxType: $this")
}
/**
@@ -336,7 +449,7 @@ abstract class BTypes {
*/
final def maxValueType(other: BType): BType = {
- def uncomparable: Nothing = throw new AssertionError(s"Cannot compute maxValueType: $this, $other")
+ def uncomparable: Nothing = assertionError(s"Cannot compute maxValueType: $this, $other")
if (!other.isPrimitive && !other.isNothingType) uncomparable
@@ -527,7 +640,7 @@ abstract class BTypes {
* local and anonymous classes, no matter if there is an enclosing method or not. Accordingly, the
* "class" field (see below) must be always defined, while the "method" field may be null.
*
- * NOTE: When a EnclosingMethod attribute is requried (local and anonymous classes), the "outer"
+ * NOTE: When an EnclosingMethod attribute is requried (local and anonymous classes), the "outer"
* field in the InnerClass table must be null.
*
* Fields:
@@ -634,6 +747,28 @@ abstract class BTypes {
* }
* }
*
+ *
+ * Traits Members
+ * --------------
+ *
+ * Some trait methods don't exist in the generated interface, but only in the implementation class
+ * (private methods in traits for example). Since EnclosingMethod expresses a source-level property,
+ * but the source-level enclosing method doesn't exist in the classfile, we the enclosing method
+ * is null (the enclosing class is still emitted).
+ * See BCodeAsmCommon.considerAsTopLevelImplementationArtifact
+ *
+ *
+ * Implementation Classes, Specialized Classes, Delambdafy:method closure classes
+ * ------------------------------------------------------------------------------
+ *
+ * Trait implementation classes and specialized classes are always considered top-level. Again,
+ * the InnerClass / EnclosingMethod attributes describe a source-level properties. The impl
+ * classes are compilation artifacts.
+ *
+ * The same is true for delambdafy:method closure classes. These classes are generated at
+ * top-level in the delambdafy phase, no special support is required in the backend.
+ *
+ *
* Mirror Classes
* --------------
*
@@ -643,6 +778,21 @@ abstract class BTypes {
/**
* A ClassBType represents a class or interface type. The necessary information to build a
* ClassBType is extracted from compiler symbols and types, see BTypesFromSymbols.
+ *
+ * The `info` field contains either the class information on an error message why the info could
+ * not be computed. There are two reasons for an erroneous info:
+ * 1. The ClassBType was built from a class symbol that stems from a java source file, and the
+ * symbol's type could not be completed successfully (SI-9111)
+ * 2. The ClassBType should be built from a classfile, but the class could not be found on the
+ * compilation classpath.
+ *
+ * Note that all ClassBTypes required in a non-optimzied run are built during code generation from
+ * the class symbols referenced by the ASTs, so they have a valid info. Therefore the backend
+ * often invokes `info.get` (which asserts the info to exist) when reading data from the ClassBType.
+ *
+ * The inliner on the other hand uses ClassBTypes that are built from classfiles, which may have
+ * a missing info. In order not to crash the compiler unnecessarily, the inliner does not force
+ * infos using `get`, but it reports inliner warnings for missing infos that prevent inlining.
*/
final case class ClassBType(internalName: InternalName) extends RefBType {
/**
@@ -652,14 +802,14 @@ abstract class BTypes {
* B.info.nestedInfo.outerClass == A
* A.info.nestedClasses contains B
*/
- private var _info: ClassInfo = null
+ private var _info: Either[NoClassBTypeInfo, ClassInfo] = null
- def info: ClassInfo = {
+ def info: Either[NoClassBTypeInfo, ClassInfo] = {
assert(_info != null, s"ClassBType.info not yet assigned: $this")
_info
}
- def info_=(i: ClassInfo): Unit = {
+ def info_=(i: Either[NoClassBTypeInfo, ClassInfo]): Unit = {
assert(_info == null, s"Cannot set ClassBType.info multiple times: $this")
_info = i
checkInfoConsistency()
@@ -668,27 +818,29 @@ abstract class BTypes {
classBTypeFromInternalName(internalName) = this
private def checkInfoConsistency(): Unit = {
+ if (info.isLeft) return
+
// we assert some properties. however, some of the linked ClassBType (members, superClass,
// interfaces) may not yet have an `_info` (initialization of cyclic structures). so we do a
- // best-effort verification.
- def ifInit(c: ClassBType)(p: ClassBType => Boolean): Boolean = c._info == null || p(c)
+ // best-effort verification. also we don't report an error if the info is a Left.
+ def ifInit(c: ClassBType)(p: ClassBType => Boolean): Boolean = c._info == null || c.info.isLeft || p(c)
def isJLO(t: ClassBType) = t.internalName == ObjectReference.internalName
assert(!ClassBType.isInternalPhantomType(internalName), s"Cannot create ClassBType for phantom type $this")
assert(
- if (info.superClass.isEmpty) { isJLO(this) || (isCompilingPrimitive && ClassBType.hasNoSuper(internalName)) }
- else if (isInterface) isJLO(info.superClass.get)
- else !isJLO(this) && ifInit(info.superClass.get)(!_.isInterface),
- s"Invalid superClass in $this: ${info.superClass}"
+ if (info.get.superClass.isEmpty) { isJLO(this) || (isCompilingPrimitive && ClassBType.hasNoSuper(internalName)) }
+ else if (isInterface.get) isJLO(info.get.superClass.get)
+ else !isJLO(this) && ifInit(info.get.superClass.get)(!_.isInterface.get),
+ s"Invalid superClass in $this: ${info.get.superClass}"
)
assert(
- info.interfaces.forall(c => ifInit(c)(_.isInterface)),
- s"Invalid interfaces in $this: ${info.interfaces}"
+ info.get.interfaces.forall(c => ifInit(c)(_.isInterface.get)),
+ s"Invalid interfaces in $this: ${info.get.interfaces}"
)
- assert(info.nestedClasses.forall(c => ifInit(c)(_.isNestedClass)), info.nestedClasses)
+ assert(info.get.nestedClasses.forall(c => ifInit(c)(_.isNestedClass.get)), info.get.nestedClasses)
}
/**
@@ -696,20 +848,37 @@ abstract class BTypes {
*/
def simpleName: String = internalName.split("/").last
- def isInterface = (info.flags & asm.Opcodes.ACC_INTERFACE) != 0
+ def isInterface: Either[NoClassBTypeInfo, Boolean] = info.map(i => (i.flags & asm.Opcodes.ACC_INTERFACE) != 0)
+
+ def superClassesTransitive: Either[NoClassBTypeInfo, List[ClassBType]] = info.flatMap(i => i.superClass match {
+ case None => Right(Nil)
+ case Some(sc) => sc.superClassesTransitive.map(sc :: _)
+ })
- def superClassesTransitive: List[ClassBType] = info.superClass match {
- case None => Nil
- case Some(sc) => sc :: sc.superClassesTransitive
+ /**
+ * The prefix of the internal name until the last '/', or the empty string.
+ */
+ def packageInternalName: String = {
+ val name = internalName
+ name.lastIndexOf('/') match {
+ case -1 => ""
+ case i => name.substring(0, i)
+ }
}
- def isNestedClass = info.nestedInfo.isDefined
+ def isPublic: Either[NoClassBTypeInfo, Boolean] = info.map(i => (i.flags & asm.Opcodes.ACC_PUBLIC) != 0)
- def enclosingNestedClassesChain: List[ClassBType] =
- if (isNestedClass) this :: info.nestedInfo.get.enclosingClass.enclosingNestedClassesChain
- else Nil
+ def isNestedClass: Either[NoClassBTypeInfo, Boolean] = info.map(_.nestedInfo.isDefined)
+
+ def enclosingNestedClassesChain: Either[NoClassBTypeInfo, List[ClassBType]] = {
+ isNestedClass.flatMap(isNested => {
+ // if isNested is true, we know that info.get is defined, and nestedInfo.get is also defined.
+ if (isNested) info.get.nestedInfo.get.enclosingClass.enclosingNestedClassesChain.map(this :: _)
+ else Right(Nil)
+ })
+ }
- def innerClassAttributeEntry: Option[InnerClassEntry] = info.nestedInfo map {
+ def innerClassAttributeEntry: Either[NoClassBTypeInfo, Option[InnerClassEntry]] = info.map(i => i.nestedInfo map {
case NestedInfo(_, outerName, innerName, isStaticNestedClass) =>
InnerClassEntry(
internalName,
@@ -717,28 +886,39 @@ abstract class BTypes {
innerName.orNull,
GenBCode.mkFlags(
// the static flag in the InnerClass table has a special meaning, see InnerClass comment
- info.flags & ~Opcodes.ACC_STATIC,
+ i.flags & ~Opcodes.ACC_STATIC,
if (isStaticNestedClass) Opcodes.ACC_STATIC else 0
) & ClassBType.INNER_CLASSES_FLAGS
)
- }
+ })
- def isSubtypeOf(other: ClassBType): Boolean = {
- if (this == other) return true
+ def inlineInfoAttribute: Either[NoClassBTypeInfo, InlineInfoAttribute] = info.map(i => {
+ // InlineInfos are serialized for classes being compiled. For those the info was built by
+ // buildInlineInfoFromClassSymbol, which only adds a warning under SI-9111, which in turn
+ // only happens for class symbols of java source files.
+ // we could put this assertion into InlineInfoAttribute, but it is more safe to put it here
+ // where it affect only GenBCode, and not add any assertion to GenASM in 2.11.6.
+ assert(i.inlineInfo.warning.isEmpty, i.inlineInfo.warning)
+ InlineInfoAttribute(i.inlineInfo)
+ })
- if (isInterface) {
- if (other == ObjectReference) return true // interfaces conform to Object
- if (!other.isInterface) return false // this is an interface, the other is some class other than object. interfaces cannot extend classes, so the result is false.
+ def isSubtypeOf(other: ClassBType): Either[NoClassBTypeInfo, Boolean] = try {
+ if (this == other) return Right(true)
+ if (isInterface.orThrow) {
+ if (other == ObjectReference) return Right(true) // interfaces conform to Object
+ if (!other.isInterface.orThrow) return Right(false) // this is an interface, the other is some class other than object. interfaces cannot extend classes, so the result is false.
// else: this and other are both interfaces. continue to (*)
} else {
- val sc = info.superClass
- if (sc.isDefined && sc.get.isSubtypeOf(other)) return true // the superclass of this class conforms to other
- if (!other.isInterface) return false // this and other are both classes, and the superclass of this does not conform
+ val sc = info.orThrow.superClass
+ if (sc.isDefined && sc.get.isSubtypeOf(other).orThrow) return Right(true) // the superclass of this class conforms to other
+ if (!other.isInterface.orThrow) return Right(false) // this and other are both classes, and the superclass of this does not conform
// else: this is a class, the other is an interface. continue to (*)
}
// (*) check if some interface of this class conforms to other.
- info.interfaces.exists(_.isSubtypeOf(other))
+ Right(info.orThrow.interfaces.exists(_.isSubtypeOf(other).orThrow))
+ } catch {
+ case Invalid(noInfo: NoClassBTypeInfo) => Left(noInfo)
}
/**
@@ -748,34 +928,36 @@ abstract class BTypes {
* http://comments.gmane.org/gmane.comp.java.vm.languages/2293
* https://issues.scala-lang.org/browse/SI-3872
*/
- def jvmWiseLUB(other: ClassBType): ClassBType = {
+ def jvmWiseLUB(other: ClassBType): Either[NoClassBTypeInfo, ClassBType] = {
def isNotNullOrNothing(c: ClassBType) = !c.isNullType && !c.isNothingType
assert(isNotNullOrNothing(this) && isNotNullOrNothing(other), s"jvmWiseLub for null or nothing: $this - $other")
- val res: ClassBType = (this.isInterface, other.isInterface) match {
- case (true, true) =>
- // exercised by test/files/run/t4761.scala
- if (other.isSubtypeOf(this)) this
- else if (this.isSubtypeOf(other)) other
- else ObjectReference
-
- case (true, false) =>
- if (other.isSubtypeOf(this)) this else ObjectReference
-
- case (false, true) =>
- if (this.isSubtypeOf(other)) other else ObjectReference
+ tryEither {
+ val res: ClassBType = (this.isInterface.orThrow, other.isInterface.orThrow) match {
+ case (true, true) =>
+ // exercised by test/files/run/t4761.scala
+ if (other.isSubtypeOf(this).orThrow) this
+ else if (this.isSubtypeOf(other).orThrow) other
+ else ObjectReference
+
+ case (true, false) =>
+ if (other.isSubtypeOf(this).orThrow) this else ObjectReference
+
+ case (false, true) =>
+ if (this.isSubtypeOf(other).orThrow) other else ObjectReference
+
+ case _ =>
+ // TODO @lry I don't really understand the reasoning here.
+ // Both this and other are classes. The code takes (transitively) all superclasses and
+ // finds the first common one.
+ // MOST LIKELY the answer can be found here, see the comments and links by Miguel:
+ // - https://issues.scala-lang.org/browse/SI-3872
+ firstCommonSuffix(this :: this.superClassesTransitive.orThrow, other :: other.superClassesTransitive.orThrow)
+ }
- case _ =>
- // TODO @lry I don't really understand the reasoning here.
- // Both this and other are classes. The code takes (transitively) all superclasses and
- // finds the first common one.
- // MOST LIKELY the answer can be found here, see the comments and links by Miguel:
- // - https://issues.scala-lang.org/browse/SI-3872
- firstCommonSuffix(this :: this.superClassesTransitive, other :: other.superClassesTransitive)
+ assert(isNotNullOrNothing(res), s"jvmWiseLub computed: $res")
+ Right(res)
}
-
- assert(isNotNullOrNothing(res), s"jvmWiseLub computed: $res")
- res
}
private def firstCommonSuffix(as: List[ClassBType], bs: List[ClassBType]): ClassBType = {
@@ -837,9 +1019,11 @@ abstract class BTypes {
* @param nestedClasses Classes nested in this class. Those need to be added to the
* InnerClass table, see the InnerClass spec summary above.
* @param nestedInfo If this describes a nested class, information for the InnerClass table.
+ * @param inlineInfo Information about this class for the inliner.
*/
final case class ClassInfo(superClass: Option[ClassBType], interfaces: List[ClassBType], flags: Int,
- nestedClasses: List[ClassBType], nestedInfo: Option[NestedInfo])
+ nestedClasses: List[ClassBType], nestedInfo: Option[NestedInfo],
+ inlineInfo: InlineInfo)
/**
* Information required to add a class to an InnerClass table.
@@ -914,4 +1098,60 @@ object BTypes {
* But that would create overhead in a Collection[InternalName].
*/
type InternalName = String
+
+ /**
+ * Metadata about a ClassBType, used by the inliner.
+ *
+ * More information may be added in the future to enable more elaborate inlinine heuristics.
+ *
+ * @param traitImplClassSelfType `Some(tp)` if this InlineInfo describes a trait, and the `self`
+ * parameter type of the methods in the implementation class is not
+ * the trait itself. Example:
+ * trait T { self: U => def f = 1 }
+ * Generates something like:
+ * class T$class { static def f(self: U) = 1 }
+ *
+ * In order to inline a trat method call, the INVOKEINTERFACE is
+ * rewritten to an INVOKESTATIC of the impl class, so we need the
+ * self type (U) to get the right signature.
+ *
+ * `None` if the self type is the interface type, or if this
+ * InlineInfo does not describe a trait.
+ *
+ * @param isEffectivelyFinal True if the class cannot have subclasses: final classes, module
+ * classes, trait impl classes.
+ *
+ * @param methodInfos The [[MethodInlineInfo]]s for the methods declared in this class.
+ * The map is indexed by the string s"$name$descriptor" (to
+ * disambiguate overloads).
+ *
+ * @param warning Contains an warning message if an error occured when building this
+ * InlineInfo, for example if some classfile could not be found on
+ * the classpath. This warning can be reported later by the inliner.
+ */
+ final case class InlineInfo(traitImplClassSelfType: Option[InternalName],
+ isEffectivelyFinal: Boolean,
+ methodInfos: Map[String, MethodInlineInfo],
+ warning: Option[ClassInlineInfoWarning])
+
+ val EmptyInlineInfo = InlineInfo(None, false, Map.empty, None)
+
+ /**
+ * Metadata about a method, used by the inliner.
+ *
+ * @param effectivelyFinal True if the method cannot be overridden (in Scala)
+ * @param traitMethodWithStaticImplementation True if the method is an interface method method of
+ * a trait method and has a static counterpart in the
+ * implementation class.
+ * @param annotatedInline True if the method is annotated `@inline`
+ * @param annotatedNoInline True if the method is annotated `@noinline`
+ */
+ final case class MethodInlineInfo(effectivelyFinal: Boolean,
+ traitMethodWithStaticImplementation: Boolean,
+ annotatedInline: Boolean,
+ annotatedNoInline: Boolean)
+
+ // no static way (without symbol table instance) to get to nme.ScalaATTR / ScalaSignatureATTR
+ val ScalaAttributeName = "Scala"
+ val ScalaSigAttributeName = "ScalaSig"
} \ No newline at end of file