summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala')
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala465
1 files changed, 0 insertions, 465 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
deleted file mode 100644
index 93f5159f89..0000000000
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
+++ /dev/null
@@ -1,465 +0,0 @@
-/* NSC -- new Scala compiler
- * Copyright 2005-2014 LAMP/EPFL
- * @author Martin Odersky
- */
-
-package scala.tools.nsc
-package backend.jvm
-
-import scala.tools.nsc.Global
-import scala.tools.nsc.backend.jvm.BTypes.{InternalName, MethodInlineInfo, InlineInfo}
-import BackendReporting.ClassSymbolInfoFailureSI9111
-import scala.tools.asm
-
-/**
- * This trait contains code shared between GenBCode and GenASM that depends on types defined in
- * the compiler cake (Global).
- */
-final class BCodeAsmCommon[G <: Global](val global: G) {
- import global._
- import definitions._
-
- val ExcludedForwarderFlags = {
- import scala.tools.nsc.symtab.Flags._
- // Should include DEFERRED but this breaks findMember.
- SPECIALIZED | LIFTED | PROTECTED | STATIC | EXPANDEDNAME | BridgeAndPrivateFlags | MACRO
- }
-
- /**
- * True for classes generated by the Scala compiler that are considered top-level in terms of
- * the InnerClass / EnclosingMethod classfile attributes. See comment in BTypes.
- */
- def considerAsTopLevelImplementationArtifact(classSym: Symbol) = {
- classSym.isImplClass || classSym.isSpecialized
- }
-
- /**
- * Cache the value of delambdafy == "inline" for each run. We need to query this value many
- * times, so caching makes sense.
- */
- object delambdafyInline {
- private var runId = -1
- private var value = false
-
- def apply(): Boolean = {
- if (runId != global.currentRunId) {
- runId = global.currentRunId
- value = settings.Ydelambdafy.value == "inline"
- }
- value
- }
- }
-
- /**
- * True if `classSym` is an anonymous class or a local class. I.e., false if `classSym` is a
- * member class. This method is used to decide if we should emit an EnclosingMethod attribute.
- * It is also used to decide whether the "owner" field in the InnerClass attribute should be
- * null.
- */
- def isAnonymousOrLocalClass(classSym: Symbol): Boolean = {
- assert(classSym.isClass, s"not a class: $classSym")
- val r = exitingPickler(classSym.isAnonymousClass) || !classSym.originalOwner.isClass
- if (r && settings.Ybackend.value == "GenBCode") {
- // this assertion only holds in GenBCode. lambda lift renames symbols and may accidentally
- // introduce `$lambda` into a class name, making `isDelambdafyFunction` true. under GenBCode
- // we prevent this, see `nonAnon` in LambdaLift.
- // phase travel necessary: after flatten, the name includes the name of outer classes.
- // if some outer name contains $lambda, a non-lambda class is considered lambda.
- assert(exitingPickler(!classSym.isDelambdafyFunction), classSym.name)
- }
- r
- }
-
- /**
- * The next enclosing definition in the source structure. Includes anonymous function classes
- * under delambdafy:inline, even though they are only generated during UnCurry.
- */
- def nextEnclosing(sym: Symbol): Symbol = {
- val origOwner = sym.originalOwner
- // 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 (delambdafyInline() && sym.rawowner.isAnonymousFunction) {
- // SI-9105: special handling for anonymous functions under delambdafy:inline.
- //
- // class C { def t = () => { def f { class Z } } }
- //
- // class C { def t = byNameMethod { def f { class Z } } }
- //
- // In both examples, the method f lambda-lifted into the anonfun class.
- //
- // In both examples, the enclosing method of Z is f, the enclosing class is the anonfun.
- // So nextEnclosing needs to return the following chain: Z - f - anonFunClassSym - ...
- //
- // In the first example, the initial owner of f is a TermSymbol named "$anonfun" (note: not the anonFunClassSym!)
- // In the second, the initial owner of f is t (no anon fun term symbol for by-name args!).
- //
- // In both cases, the rawowner of class Z is the anonFunClassSym. So the check in the `if`
- // above makes sure we don't jump over the anonymous function in the by-name argument case.
- //
- // However, we cannot directly return the rawowner: if `sym` is Z, we need to include method f
- // in the result. This is done by comparing the rawowners (read: lambdalift-targets) of `sym`
- // and `sym.originalOwner`: if they are the same, then the originalOwner is "in between", and
- // we need to return it.
- // If the rawowners are different, the symbol was not in between. In the first example, the
- // originalOwner of `f` is the anonfun-term-symbol, whose rawowner is C. So the nextEnclosing
- // of `f` is its rawowner, the anonFunClassSym.
- //
- // In delambdafy:method we don't have that problem. The f method is lambda-lifted into C,
- // not into the anonymous function class. The originalOwner chain is Z - f - C.
- if (sym.originalOwner.rawowner == sym.rawowner) sym.originalOwner
- else sym.rawowner
- } else {
- origOwner
- }
- }
-
- def nextEnclosingClass(sym: Symbol): Symbol = {
- if (sym.isClass) sym
- else nextEnclosingClass(nextEnclosing(sym))
- }
-
- def classOriginallyNestedInClass(nestedClass: Symbol, enclosingClass: Symbol) ={
- nextEnclosingClass(nextEnclosing(nestedClass)) == enclosingClass
- }
-
- /**
- * Returns the enclosing method for non-member classes. In the following example
- *
- * class A {
- * def f = {
- * class B {
- * class C
- * }
- * }
- * }
- *
- * the method returns Some(f) for B, but None for C, because C is a member class. For non-member
- * classes that are not enclosed by a method, it returns None:
- *
- * class A {
- * { class B }
- * }
- *
- * In this case, for B, we return None.
- *
- * The EnclosingMethod attribute needs to be added to non-member classes (see doc in BTypes).
- * This is a source-level property, so we need to use the originalOwner chain to reconstruct it.
- */
- private def enclosingMethodForEnclosingMethodAttribute(classSym: Symbol): Option[Symbol] = {
- assert(classSym.isClass, classSym)
-
- def doesNotExist(method: Symbol) = {
- // (1) SI-9124, some trait methods don't exist in the generated interface. see comment in BTypes.
- // (2) Value classes. Member methods of value classes exist in the generated box class. However,
- // nested methods lifted into a value class are moved to the companion object and don't exist
- // in the value class itself. We can identify such nested methods: the initial enclosing class
- // is a value class, but the current owner is some other class (the module class).
- method.owner.isTrait && method.isImplOnly || { // (1)
- val enclCls = nextEnclosingClass(method)
- exitingPickler(enclCls.isDerivedValueClass) && method.owner != enclCls // (2)
- }
- }
-
- def enclosingMethod(sym: Symbol): Option[Symbol] = {
- if (sym.isClass || sym == NoSymbol) None
- else if (sym.isMethod) {
- if (doesNotExist(sym)) None else Some(sym)
- }
- else enclosingMethod(nextEnclosing(sym))
- }
- enclosingMethod(nextEnclosing(classSym))
- }
-
- /**
- * The enclosing class for emitting the EnclosingMethod attribute. Since this is a source-level
- * property, this method looks at the originalOwner chain. See doc in BTypes.
- */
- private def enclosingClassForEnclosingMethodAttribute(classSym: Symbol): Symbol = {
- assert(classSym.isClass, classSym)
- val r = nextEnclosingClass(nextEnclosing(classSym))
- // this should be an assertion, but we are more cautious for now as it was introduced before the 2.11.6 minor release
- if (considerAsTopLevelImplementationArtifact(r)) devWarning(s"enclosing class of $classSym should not be an implementation artifact class: $r")
- r
- }
-
- final case class EnclosingMethodEntry(owner: String, name: String, methodDescriptor: String)
-
- /**
- * Data for emitting an EnclosingMethod attribute. None if `classSym` is a member class (not
- * an anonymous or local class). See doc in BTypes.
- *
- * The class is parametrized by two functions to obtain a bytecode class descriptor for a class
- * symbol, and to obtain a method signature descriptor fro a method symbol. These function depend
- * on the implementation of GenASM / GenBCode, so they need to be passed in.
- */
- def enclosingMethodAttribute(classSym: Symbol, classDesc: Symbol => String, methodDesc: Symbol => String): Option[EnclosingMethodEntry] = {
- // trait impl classes are always top-level, see comment in BTypes
- if (isAnonymousOrLocalClass(classSym) && !considerAsTopLevelImplementationArtifact(classSym)) {
- val enclosingClass = enclosingClassForEnclosingMethodAttribute(classSym)
- val methodOpt = enclosingMethodForEnclosingMethodAttribute(classSym) match {
- case some @ Some(m) =>
- if (m.owner != enclosingClass) {
- // This should never happen. In case it does, it prevents emitting an invalid
- // EnclosingMethod attribute: if the attribute specifies an enclosing method,
- // it needs to exist in the specified enclosing class.
- devWarning(s"the owner of the enclosing method ${m.locationString} should be the same as the enclosing class $enclosingClass")
- None
- } else some
- case none => none
- }
- Some(EnclosingMethodEntry(
- classDesc(enclosingClass),
- methodOpt.map(_.javaSimpleName.toString).orNull,
- methodOpt.map(methodDesc).orNull))
- } else {
- None
- }
- }
-
- /**
- * This is basically a re-implementation of sym.isStaticOwner, but using the originalOwner chain.
- *
- * The problem is that we are interested in a source-level property. Various phases changed the
- * symbol's properties in the meantime, mostly lambdalift modified (destructively) the owner.
- * Therefore, `sym.isStatic` is not what we want. For example, in
- * object T { def f { object U } }
- * the owner of U is T, so UModuleClass.isStatic is true. Phase travel does not help here.
- */
- def isOriginallyStaticOwner(sym: Symbol): Boolean = {
- sym.isPackageClass || sym.isModuleClass && isOriginallyStaticOwner(sym.originalOwner)
- }
-
- /**
- * Reconstruct the classfile flags from a Java defined class symbol.
- *
- * The implementation of this method is slightly different that `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
- )
- }
-
- /**
- * 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.
- *
- * Impl classes are always considered top-level, see comment in BTypes.
- */
- 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) => // impl classes get the lateMODULE flag in mixin
- val r = exitingPickler(sym.moduleClass)
- assert(r != NoSymbol, sym.fullLocationString)
- r
- })(collection.breakOut)
-
- lazy val AnnotationRetentionPolicyModule = AnnotationRetentionPolicyAttr.companionModule
- lazy val AnnotationRetentionPolicySourceValue = AnnotationRetentionPolicyModule.tpe.member(TermName("SOURCE"))
- lazy val AnnotationRetentionPolicyClassValue = AnnotationRetentionPolicyModule.tpe.member(TermName("CLASS"))
- lazy val AnnotationRetentionPolicyRuntimeValue = AnnotationRetentionPolicyModule.tpe.member(TermName("RUNTIME"))
-
- /** Whether an annotation should be emitted as a Java annotation
- * .initialize: if 'annot' is read from pickle, atp might be uninitialized
- */
- def shouldEmitAnnotation(annot: AnnotationInfo) = {
- annot.symbol.initialize.isJavaDefined &&
- annot.matches(ClassfileAnnotationClass) &&
- retentionPolicyOf(annot) != AnnotationRetentionPolicySourceValue &&
- annot.args.isEmpty
- }
-
- def isRuntimeVisible(annot: AnnotationInfo): Boolean = {
- annot.atp.typeSymbol.getAnnotation(AnnotationRetentionAttr) match {
- case Some(retentionAnnot) =>
- retentionAnnot.assocs.contains(nme.value -> LiteralAnnotArg(Constant(AnnotationRetentionPolicyRuntimeValue)))
- case _ =>
- // SI-8926: if the annotation class symbol doesn't have a @RetentionPolicy annotation, the
- // annotation is emitted with visibility `RUNTIME`
- true
- }
- }
-
- private def retentionPolicyOf(annot: AnnotationInfo): Symbol =
- annot.atp.typeSymbol.getAnnotation(AnnotationRetentionAttr).map(_.assocs).flatMap(assoc =>
- assoc.collectFirst {
- case (`nme`.value, LiteralAnnotArg(Constant(value: Symbol))) => value
- }).getOrElse(AnnotationRetentionPolicyClassValue)
-
- 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)
-
- // 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 = erasure.minimizeParents(allParents) 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)
- }
-
- /**
- * This is a hack to work around SI-9111. The completer of `methodSym` may report type errors. We
- * cannot change the typer context of the completer at this point and make it silent: the context
- * captured when creating the completer in the namer. However, we can temporarily replace
- * global.reporter (it's a var) to store errors.
- */
- def completeSilentlyAndCheckErroneous(sym: Symbol): Boolean = {
- if (sym.hasCompleteInfo) false
- else {
- val originalReporter = global.reporter
- val storeReporter = new reporters.StoreReporter()
- global.reporter = storeReporter
- try {
- sym.info
- } finally {
- global.reporter = originalReporter
- }
- sym.isErroneous
- }
- }
-
- /**
- * Build the [[InlineInfo]] for a class symbol.
- */
- def buildInlineInfoFromClassSymbol(classSym: Symbol, classSymToInternalName: Symbol => InternalName, methodSymToDescriptor: Symbol => String): InlineInfo = {
- val traitSelfType = if (classSym.isTrait && !classSym.isImplClass) {
- // The mixin phase uses typeOfThis for the self parameter in implementation class methods.
- val selfSym = classSym.typeOfThis.typeSymbol
- if (selfSym != classSym) Some(classSymToInternalName(selfSym)) else None
- } else {
- None
- }
-
- val isEffectivelyFinal = classSym.isEffectivelyFinal
-
- var warning = Option.empty[ClassSymbolInfoFailureSI9111]
-
- // 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 = classSym.info.decls.iterator.filter(m => m.isMethod && !scalaPrimitives.isPrimitive(m)).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))
- None
- } else {
- val name = methodSym.javaSimpleName.toString // same as in genDefDef
- val signature = name + methodSymToDescriptor(methodSym)
-
- // Some detours are required here because of changing flags (lateDEFERRED, lateMODULE):
- // 1. Why the phase travel? Concrete trait methods obtain the lateDEFERRED flag in Mixin.
- // This makes isEffectivelyFinalOrNotOverridden false, which would prevent non-final
- // but non-overridden methods of sealed traits from being inlined.
- // 2. Why the special case for `classSym.isImplClass`? Impl class symbols obtain the
- // lateMODULE flag during Mixin. During the phase travel to exitingPickler, the late
- // flag is ignored. The members are therefore not isEffectivelyFinal (their owner
- // is not a module). Since we know that all impl class members are static, we can
- // just take the shortcut.
- val effectivelyFinal = classSym.isImplClass || exitingPickler(methodSym.isEffectivelyFinalOrNotOverridden)
-
- // Identify trait interface methods that have a static implementation in the implementation
- // class. Invocations of these methods can be re-wrired directly to the static implementation
- // if they are final or the receiver is known.
- //
- // Using `erasure.needsImplMethod` is not enough: it keeps field accessors, module getters
- // and super accessors. When AddInterfaces creates the impl class, these methods are
- // initially added to it.
- //
- // The mixin phase later on filters out most of these members from the impl class (see
- // Mixin.isImplementedStatically). However, accessors for concrete lazy vals remain in the
- // impl class after mixin. So the filter in mixin is not exactly what we need here (we
- // want to identify concrete trait methods, not any accessors). So we check some symbol
- // properties manually.
- val traitMethodWithStaticImplementation = {
- import symtab.Flags._
- classSym.isTrait && !classSym.isImplClass &&
- erasure.needsImplMethod(methodSym) &&
- !methodSym.isModule &&
- !(methodSym hasFlag (ACCESSOR | SUPERACCESSOR))
- }
-
- val info = MethodInlineInfo(
- effectivelyFinal = effectivelyFinal,
- traitMethodWithStaticImplementation = traitMethodWithStaticImplementation,
- annotatedInline = methodSym.hasAnnotation(ScalaInlineClass),
- annotatedNoInline = methodSym.hasAnnotation(ScalaNoInlineClass)
- )
- Some((signature, info))
- }
- }).toMap
-
- InlineInfo(traitSelfType, isEffectivelyFinal, methodInlineInfos, warning)
- }
-}
-
-object BCodeAsmCommon {
- /**
- * Valid flags for InnerClass attribute entry.
- * See http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.6
- */
- val INNER_CLASSES_FLAGS = {
- asm.Opcodes.ACC_PUBLIC | asm.Opcodes.ACC_PRIVATE | asm.Opcodes.ACC_PROTECTED |
- asm.Opcodes.ACC_STATIC | asm.Opcodes.ACC_FINAL | asm.Opcodes.ACC_INTERFACE |
- asm.Opcodes.ACC_ABSTRACT | asm.Opcodes.ACC_SYNTHETIC | asm.Opcodes.ACC_ANNOTATION |
- asm.Opcodes.ACC_ENUM
- }
-}