diff options
Diffstat (limited to 'src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala')
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala | 465 |
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 - } -} |