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.scala165
1 files changed, 97 insertions, 68 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
index 0c26e01322..2637d21050 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
@@ -7,15 +7,18 @@ package scala.tools.nsc
package backend.jvm
import scala.annotation.switch
+import scala.collection.{concurrent, mutable}
import scala.collection.concurrent.TrieMap
import scala.reflect.internal.util.Position
import scala.tools.asm
import asm.Opcodes
-import scala.tools.asm.tree.{MethodNode, MethodInsnNode, InnerClassNode, ClassNode}
+import scala.tools.asm.tree._
import scala.tools.nsc.backend.jvm.BTypes.{InlineInfo, MethodInlineInfo}
import scala.tools.nsc.backend.jvm.BackendReporting._
+import scala.tools.nsc.backend.jvm.analysis.BackendUtils
import scala.tools.nsc.backend.jvm.opt._
-import scala.collection.convert.decorateAsScala._
+import scala.collection.JavaConverters._
+import scala.collection.mutable.ListBuffer
import scala.tools.nsc.settings.ScalaSettings
/**
@@ -29,6 +32,8 @@ import scala.tools.nsc.settings.ScalaSettings
abstract class BTypes {
import BTypes.InternalName
+ val backendUtils: BackendUtils[this.type]
+
// Some core BTypes are required here, in class BType, where no Global instance is available.
// The Global is only available in the subclass BTypesFromSymbols. We cannot depend on the actual
// implementation (CoreBTypesProxy) here because it has members that refer to global.Symbol.
@@ -38,12 +43,14 @@ abstract class BTypes {
/**
* Tools for parsing classfiles, used by the inliner.
*/
- val byteCodeRepository: ByteCodeRepository
+ val byteCodeRepository: ByteCodeRepository[this.type]
val localOpt: LocalOpt[this.type]
val inliner: Inliner[this.type]
+ val inlinerHeuristics: InlinerHeuristics[this.type]
+
val closureOptimizer: ClosureOptimizer[this.type]
val callGraph: CallGraph[this.type]
@@ -56,7 +63,6 @@ abstract class BTypes {
// Allows access to the compiler settings for backend components that don't have a global in scope
def compilerSettings: ScalaSettings
-
/**
* A map from internal names to ClassBTypes. Every ClassBType is added to this map on its
* construction.
@@ -68,19 +74,27 @@ 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(TrieMap.empty)
+ val classBTypeFromInternalName: concurrent.Map[InternalName, ClassBType] = recordPerRunCache(TrieMap.empty)
/**
* 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)
+ val callsitePositions: concurrent.Map[MethodInsnNode, Position] = recordPerRunCache(TrieMap.empty)
+
+ /**
+ * Stores callsite instructions of invocatinos annotated `f(): @inline/noinline`.
+ * Instructions are added during code generation (BCodeBodyBuilder). The maps are then queried
+ * when building the CallGraph, every Callsite object has an annotated(No)Inline field.
+ */
+ val inlineAnnotatedCallsites: mutable.Set[MethodInsnNode] = recordPerRunCache(mutable.Set.empty)
+ val noInlineAnnotatedCallsites: mutable.Set[MethodInsnNode] = recordPerRunCache(mutable.Set.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)
+ val javaDefinedClasses: mutable.Set[InternalName] = recordPerRunCache(mutable.Set.empty)
/**
* Cache, contains methods whose unreachable instructions are eliminated.
@@ -92,12 +106,28 @@ abstract class BTypes {
* This cache allows running dead code elimination whenever an analyzer is used. If the method
* is already optimized, DCE can return early.
*/
- val unreachableCodeEliminated: collection.mutable.Set[MethodNode] = recordPerRunCache(collection.mutable.Set.empty)
+ val unreachableCodeEliminated: mutable.Set[MethodNode] = recordPerRunCache(mutable.Set.empty)
+
+ /**
+ * Cache of methods which have correct `maxLocals` / `maxStack` values assigned. This allows
+ * invoking `computeMaxLocalsMaxStack` whenever running an analyzer but performing the actual
+ * computation only when necessary.
+ */
+ val maxLocalsMaxStackComputed: mutable.Set[MethodNode] = recordPerRunCache(mutable.Set.empty)
+
+ /**
+ * Classes with indyLambda closure instantiations where the SAM type is serializable (e.g. Scala's
+ * FunctionN) need a `$deserializeLambda$` method. This map contains classes for which such a
+ * method has been generated. It is used during ordinary code generation, as well as during
+ * inlining: when inlining an indyLambda instruction into a class, we need to make sure the class
+ * has the method.
+ */
+ val indyLambdaHosts: mutable.Set[InternalName] = recordPerRunCache(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)
@@ -128,7 +158,7 @@ abstract class BTypes {
val res = ClassBType(internalName)
byteCodeRepository.classNode(internalName) match {
case Left(msg) => res.info = Left(NoClassBTypeInfoMissingBytecode(msg)); res
- case Right(c) => setClassInfoFromParsedClassfile(c, res)
+ case Right(c) => setClassInfoFromClassNode(c, res)
}
})
}
@@ -138,21 +168,19 @@ abstract class BTypes {
*/
def classBTypeFromClassNode(classNode: ClassNode): ClassBType = {
classBTypeFromInternalName.getOrElse(classNode.name, {
- setClassInfoFromParsedClassfile(classNode, ClassBType(classNode.name))
+ setClassInfoFromClassNode(classNode, ClassBType(classNode.name))
})
}
- private def setClassInfoFromParsedClassfile(classNode: ClassNode, classBType: ClassBType): ClassBType = {
+ private def setClassInfoFromClassNode(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}")
+ assert(classNode.name == ObjectRef.internalName, s"class with missing super type: ${classNode.name}")
None
case superName =>
Some(classBTypeFromParsedClassfile(superName))
}
- val interfaces: List[ClassBType] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut)
-
val flags = classNode.access
/**
@@ -197,6 +225,9 @@ abstract class BTypes {
val inlineInfo = inlineInfoFromClassfile(classNode)
+ val classfileInterfaces: List[ClassBType] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut)
+ val interfaces = classfileInterfaces.filterNot(i => inlineInfo.lateInterfaces.contains(i.internalName))
+
classBType.info = Right(ClassInfo(superClass, interfaces, flags, nestedClasses, nestedInfo, inlineInfo))
classBType
}
@@ -226,14 +257,13 @@ abstract class BTypes {
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),
+ sam = inlinerHeuristics.javaSam(classNode.name),
methodInfos = methodInfos,
warning)
}
@@ -291,8 +321,8 @@ abstract class BTypes {
final def isNonVoidPrimitiveType = isPrimitive && this != UNIT
- final def isNullType = this == RT_NULL
- final def isNothingType = this == RT_NOTHING
+ final def isNullType = this == srNullRef
+ final def isNothingType = this == srNothingRef
final def isBoxed = this.isClass && boxedClasses(this.asClassBType)
@@ -315,7 +345,7 @@ abstract class BTypes {
this match {
case ArrayBType(component) =>
- if (other == ObjectReference || other == jlCloneableReference || other == jioSerializableReference) true
+ if (other == ObjectRef || other == jlCloneableRef || other == jiSerializableRef) true
else other match {
case ArrayBType(otherComponent) => component.conformsTo(otherComponent).orThrow
case _ => false
@@ -324,7 +354,7 @@ abstract class BTypes {
case classType: ClassBType =>
if (isBoxed) {
if (other.isBoxed) this == other
- else if (other == ObjectReference) true
+ else if (other == ObjectRef) true
else other match {
case otherClassType: ClassBType => classType.isSubtypeOf(otherClassType).orThrow // e.g., java/lang/Double conforms to java/lang/Number
case _ => false
@@ -367,7 +397,7 @@ abstract class BTypes {
assert(other.isRef, s"Cannot compute maxType: $this, $other")
// Approximate `lub`. The common type of two references is always ObjectReference.
- ObjectReference
+ ObjectRef
case _: MethodBType =>
assertionError(s"unexpected method type when computing maxType: $this")
@@ -554,6 +584,8 @@ abstract class BTypes {
* Terminology
* -----------
*
+ * Diagram here: https://blogs.oracle.com/darcy/entry/nested_inner_member_and_top
+ *
* - Nested class (JLS 8): class whose declaration occurs within the body of another class
*
* - Top-level class (JLS 8): non-nested class
@@ -760,26 +792,17 @@ 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
- * ------------------------------------------------------------------------------
+ * 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.
+ * Specialized classes are always considered top-level, as the InnerClass / EnclosingMethod
+ * attributes describe a source-level properties.
*
* 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.
*
+ * See also BCodeHelpers.considerAsTopLevelImplementationArtifact.
+ *
*
* Mirror Classes
* --------------
@@ -837,7 +860,7 @@ abstract class BTypes {
// 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
+ def isJLO(t: ClassBType) = t.internalName == ObjectRef.internalName
assert(!ClassBType.isInternalPhantomType(internalName), s"Cannot create ClassBType for phantom type $this")
@@ -900,7 +923,7 @@ abstract class BTypes {
// the static flag in the InnerClass table has a special meaning, see InnerClass comment
i.flags & ~Opcodes.ACC_STATIC,
if (isStaticNestedClass) Opcodes.ACC_STATIC else 0
- ) & BCodeAsmCommon.INNER_CLASSES_FLAGS
+ ) & BCodeHelpers.INNER_CLASSES_FLAGS
)
})
@@ -917,7 +940,7 @@ abstract class BTypes {
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 == ObjectRef) 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 {
@@ -950,13 +973,13 @@ abstract class BTypes {
// exercised by test/files/run/t4761.scala
if (other.isSubtypeOf(this).orThrow) this
else if (this.isSubtypeOf(other).orThrow) other
- else ObjectReference
+ else ObjectRef
case (true, false) =>
- if (other.isSubtypeOf(this).orThrow) this else ObjectReference
+ if (other.isSubtypeOf(this).orThrow) this else ObjectRef
case (false, true) =>
- if (this.isSubtypeOf(other).orThrow) other else ObjectReference
+ if (this.isSubtypeOf(other).orThrow) other else ObjectRef
case _ =>
// TODO @lry I don't really understand the reasoning here.
@@ -1081,7 +1104,7 @@ abstract class BTypes {
*/
/**
- * Just a named pair, used in CoreBTypes.asmBoxTo/asmUnboxTo.
+ * Just a named pair, used in CoreBTypes.srBoxesRuntimeBoxToMethods/srBoxesRuntimeUnboxToMethods.
*/
final case class MethodNameAndType(name: String, methodType: MethodBType)
@@ -1104,23 +1127,13 @@ object BTypes {
* 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.
+ * Note that this class should contain information that can only be obtained from the ClassSymbol.
+ * Information that can be computed from the ClassNode should be added to the call graph instead.
*
* @param isEffectivelyFinal True if the class cannot have subclasses: final classes, module
- * classes, trait impl classes.
+ * classes.
+ *
+ * @param sam If this class is a SAM type, the SAM's "$name$descriptor".
*
* @param methodInfos The [[MethodInlineInfo]]s for the methods declared in this class.
* The map is indexed by the string s"$name$descriptor" (to
@@ -1130,29 +1143,45 @@ object BTypes {
* 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,
+ final case class InlineInfo(isEffectivelyFinal: Boolean,
+ sam: Option[String],
methodInfos: Map[String, MethodInlineInfo],
- warning: Option[ClassInlineInfoWarning])
+ warning: Option[ClassInlineInfoWarning]) {
+ /**
+ * A super call (invokespecial) to a default method T.m is only allowed if the interface T is
+ * a direct parent of the class. Super calls are introduced for example in Mixin when generating
+ * forwarder methods:
+ *
+ * trait T { override def clone(): Object = "hi" }
+ * trait U extends T
+ * class C extends U
+ *
+ * The class C gets a forwarder that invokes T.clone(). During code generation the interface T
+ * is added as direct parent to class C. Note that T is not a (direct) parent in the frontend
+ * type of class C.
+ *
+ * All interfaces that are added to a class during code generation are added to this buffer and
+ * stored in the InlineInfo classfile attribute. This ensures that the ClassBTypes for a
+ * specific class is the same no matter if it's constructed from a Symbol or from a classfile.
+ * This is tested in BTypesFromClassfileTest.
+ */
+ val lateInterfaces: ListBuffer[InternalName] = ListBuffer.empty
+ }
- val EmptyInlineInfo = InlineInfo(None, false, Map.empty, None)
+ val EmptyInlineInfo = InlineInfo(false, None, 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
+}