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.scala174
1 files changed, 102 insertions, 72 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..3e3229d2c3 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 invocations 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,47 @@ 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 indyLambdaImplMethods: mutable.AnyRefMap[InternalName, mutable.LinkedHashSet[asm.Handle]] = recordPerRunCache(mutable.AnyRefMap())
+ def addIndyLambdaImplMethod(hostClass: InternalName, handle: Seq[asm.Handle]): Seq[asm.Handle] = {
+ if (handle.isEmpty) Nil else {
+ val set = indyLambdaImplMethods.getOrElseUpdate(hostClass, mutable.LinkedHashSet())
+ val added = handle.filterNot(set)
+ set ++= handle
+ added
+ }
+ }
+ def removeIndyLambdaImplMethod(hostClass: InternalName, handle: Seq[asm.Handle]): Unit = {
+ if (handle.nonEmpty)
+ indyLambdaImplMethods.getOrElseUpdate(hostClass, mutable.LinkedHashSet()) --= handle
+ }
+
+ def getIndyLambdaImplMethods(hostClass: InternalName): Iterable[asm.Handle] = {
+ indyLambdaImplMethods.getOrNull(hostClass) match {
+ case null => Nil
+ case xs => xs
+ }
+ }
/**
* 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 +177,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 +187,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 +244,8 @@ abstract class BTypes {
val inlineInfo = inlineInfoFromClassfile(classNode)
+ val interfaces: List[ClassBType] = classNode.interfaces.asScala.map(classBTypeFromParsedClassfile)(collection.breakOut)
+
classBType.info = Right(ClassInfo(superClass, interfaces, flags, nestedClasses, nestedInfo, inlineInfo))
classBType
}
@@ -226,22 +275,21 @@ 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)
}
// 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
+ // being compiled. For those classes, the info is only needed if the inliner is enabled, otherwise
// we can save the memory.
- if (!compilerSettings.YoptInlinerEnabled) BTypes.EmptyInlineInfo
+ if (!compilerSettings.optInlinerEnabled) BTypes.EmptyInlineInfo
else fromClassfileAttribute getOrElse fromClassfileWithoutAttribute
}
@@ -291,8 +339,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 +363,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 +372,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 +415,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 +602,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
@@ -603,7 +653,7 @@ abstract class BTypes {
* Fields in the InnerClass entries:
* - inner class: the (nested) class C we are talking about
* - outer class: the class of which C is a member. Has to be null for non-members, i.e. for
- * local and anonymous classes. NOTE: this co-incides with the presence of an
+ * local and anonymous classes. NOTE: this coincides with the presence of an
* EnclosingMethod attribute (see below)
* - inner name: A string with the simple name of the inner class. Null for anonymous classes.
* - flags: access property flags, details in JVMS, table in 4.7.6. Static flag: see
@@ -652,7 +702,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 an EnclosingMethod attribute is requried (local and anonymous classes), the "outer"
+ * NOTE: When an EnclosingMethod attribute is required (local and anonymous classes), the "outer"
* field in the InnerClass table must be null.
*
* Fields:
@@ -760,26 +810,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
- *
+ * Specialized Classes, Delambdafy:method closure classes
+ * ------------------------------------------------------
*
- * 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.
+ * 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 +878,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 +941,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 +958,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 +991,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 +1122,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)
@@ -1103,24 +1144,14 @@ 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.
+ * More information may be added in the future to enable more elaborate inline heuristics.
+ * 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 +1161,28 @@ 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])
- 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
+
+ // when inlining, local variable names of the callee are prefixed with the name of the callee method
+ val InlinedLocalVariablePrefixMaxLenght = 128
+}