Skip to content

Commit

Permalink
Merge pull request scala#4657 from lrytz/backports
Browse files Browse the repository at this point in the history
backports from 2.12.x
  • Loading branch information
retronym committed Jul 27, 2015
2 parents 4c6dcfe + 241bb9a commit f2d7838
Show file tree
Hide file tree
Showing 42 changed files with 952 additions and 429 deletions.
18 changes: 11 additions & 7 deletions src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import java.io.{StringWriter, PrintWriter}
import scala.tools.asm.util.{CheckClassAdapter, TraceClassVisitor, TraceMethodVisitor, Textifier}
import scala.tools.asm.{ClassWriter, Attribute, ClassReader}
import scala.collection.convert.decorateAsScala._
import scala.tools.nsc.backend.jvm.analysis.InitialProducer
import scala.tools.nsc.backend.jvm.opt.InlineInfoAttributePrototype

object AsmUtils {
Expand Down Expand Up @@ -81,13 +82,16 @@ object AsmUtils {
/**
* Returns a human-readable representation of the given instruction.
*/
def textify(insn: AbstractInsnNode): String = {
val trace = new TraceMethodVisitor(new Textifier)
insn.accept(trace)
val sw = new StringWriter
val pw = new PrintWriter(sw)
trace.p.print(pw)
sw.toString.trim
def textify(insn: AbstractInsnNode): String = insn match {
case _: InitialProducer =>
insn.toString
case _ =>
val trace = new TraceMethodVisitor(new Textifier)
insn.accept(trace)
val sw = new StringWriter
val pw = new PrintWriter(sw)
trace.p.print(pw)
sw.toString.trim
}

/**
Expand Down
32 changes: 22 additions & 10 deletions src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
Original file line number Diff line number Diff line change
Expand Up @@ -256,14 +256,17 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
if (hasAbstractMethod) ACC_ABSTRACT else 0
}
GenBCode.mkFlags(
if (classSym.isPublic) ACC_PUBLIC else 0,
if (classSym.isFinal) ACC_FINAL else 0,
// 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,
if (classSym.isInterface) ACC_INTERFACE else ACC_SUPER,
// for Java enums, we cannot trust `hasAbstractFlag` (see comment in enumFlags)
if (!classSym.hasEnumFlag && classSym.hasAbstractFlag) ACC_ABSTRACT else 0,
if (classSym.isArtifact) ACC_SYNTHETIC else 0,
if (classSym.hasEnumFlag) enumFlags else 0
if (!classSym.hasJavaEnumFlag && classSym.hasAbstractFlag) ACC_ABSTRACT else 0,
if (classSym.isArtifact) ACC_SYNTHETIC else 0,
if (classSym.hasJavaEnumFlag) enumFlags else 0
)
}

Expand Down Expand Up @@ -310,10 +313,10 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
}

private def retentionPolicyOf(annot: AnnotationInfo): Symbol =
annot.atp.typeSymbol.getAnnotation(AnnotationRetentionAttr).map(_.assocs).map(assoc =>
annot.atp.typeSymbol.getAnnotation(AnnotationRetentionAttr).map(_.assocs).flatMap(assoc =>
assoc.collectFirst {
case (`nme`.value, LiteralAnnotArg(Constant(value: Symbol))) => value
}).flatten.getOrElse(AnnotationRetentionPolicyClassValue)
}).getOrElse(AnnotationRetentionPolicyClassValue)

def implementedInterfaces(classSym: Symbol): List[Symbol] = {
// Additional interface parents based on annotations and other cues
Expand All @@ -322,9 +325,18 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
case _ => None
}

def isInterfaceOrTrait(sym: Symbol) = sym.isInterface || sym.isTrait
// 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 allParents = classSym.info.parents ++ classSym.annotations.flatMap(newParentForAnnotation)
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -632,10 +632,11 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
case _ =>
abort(s"Cannot instantiate $tpt of kind: $generatedType")
}
case Apply(_, args) if app.hasAttachment[delambdafy.LambdaMetaFactoryCapable] =>
case Apply(fun, args) if app.hasAttachment[delambdafy.LambdaMetaFactoryCapable] =>
val attachment = app.attachments.get[delambdafy.LambdaMetaFactoryCapable].get
genLoadArguments(args, paramTKs(app))
genInvokeDynamicLambda(attachment.target, attachment.arity, attachment.functionalInterface)
generatedType = asmMethodType(fun.symbol).returnType

case Apply(fun @ _, List(expr)) if currentRun.runDefinitions.isBox(fun.symbol) =>
val nativeKind = tpeTK(expr)
Expand Down
12 changes: 7 additions & 5 deletions src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,8 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
// If the `sym` is a java module class, we use the java class instead. This ensures that we
// register the class (instead of the module class) in innerClassBufferASM.
// The two symbols have the same name, so the resulting internalName is the same.
val classSym = if (sym.isJavaDefined && sym.isModuleClass) sym.linkedClassOfClass else sym
// Phase travel (exitingPickler) required for SI-6613 - linkedCoC is only reliable in early phases (nesting)
val classSym = if (sym.isJavaDefined && sym.isModuleClass) exitingPickler(sym.linkedClassOfClass) else sym
getClassBTypeAndRegisterInnerClass(classSym).internalName
}

Expand Down Expand Up @@ -714,7 +715,8 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
{
val mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, "$deserializeLambda$", "(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;", null, null)
mv.visitCode()
mv.visitFieldInsn(GETSTATIC, clazz.javaBinaryName.encoded, "$deserializeLambdaCache$", "Ljava/util/Map;")
// javaBinaryName returns the internal name of a class. Also used in BTypesFromsymbols.classBTypeFromSymbol.
mv.visitFieldInsn(GETSTATIC, clazz.javaBinaryName.toString, "$deserializeLambdaCache$", "Ljava/util/Map;")
mv.visitVarInsn(ASTORE, 1)
mv.visitVarInsn(ALOAD, 1)
val l0 = new asm.Label()
Expand All @@ -724,13 +726,13 @@ abstract class BCodeHelpers extends BCodeIdiomatic with BytecodeWriters {
mv.visitMethodInsn(INVOKESPECIAL, "java/util/HashMap", "<init>", "()V", false)
mv.visitVarInsn(ASTORE, 1)
mv.visitVarInsn(ALOAD, 1)
mv.visitFieldInsn(PUTSTATIC, clazz.javaBinaryName.encoded, "$deserializeLambdaCache$", "Ljava/util/Map;")
mv.visitFieldInsn(PUTSTATIC, clazz.javaBinaryName.toString, "$deserializeLambdaCache$", "Ljava/util/Map;")
mv.visitLabel(l0)
mv.visitFrame(asm.Opcodes.F_APPEND,1, Array("java/util/Map"), 0, null)
mv.visitFieldInsn(GETSTATIC, "scala/compat/java8/runtime/LambdaDeserializer$", "MODULE$", "Lscala/compat/java8/runtime/LambdaDeserializer$;")
mv.visitMethodInsn(INVOKESTATIC, "java/lang/invoke/MethodHandles", "lookup", "()Ljava/lang/invoke/MethodHandles$Lookup;", false)
mv.visitVarInsn(ALOAD, 1)
mv.visitVarInsn(ALOAD, 0)
mv.visitMethodInsn(INVOKESTATIC, "scala/compat/java8/runtime/LambdaDeserializer", "deserializeLambda", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/util/Map;Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;", false)
mv.visitMethodInsn(INVOKEVIRTUAL, "scala/compat/java8/runtime/LambdaDeserializer$", "deserializeLambda", "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/util/Map;Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;", false)
mv.visitInsn(ARETURN)
mv.visitEnd()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,9 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
*/
private def initJClass(jclass: asm.ClassVisitor) {

val ps = claszSymbol.info.parents
val superClass: String = if (ps.isEmpty) ObjectReference.internalName else internalName(ps.head.typeSymbol)
val interfaceNames = classBTypeFromSymbol(claszSymbol).info.get.interfaces map {
val bType = classBTypeFromSymbol(claszSymbol)
val superClass = bType.info.get.superClass.getOrElse(ObjectReference).internalName
val interfaceNames = bType.info.get.interfaces map {
case classBType =>
if (classBType.isNestedClass.get) { innerClassBufferASM += classBType }
classBType.internalName
Expand Down
25 changes: 22 additions & 3 deletions src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,18 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
}

private def setClassInfo(classSym: Symbol, classBType: ClassBType): ClassBType = {
val superClassSym = if (classSym.isImplClass) ObjectClass else classSym.superClass
// Check for isImplClass: trait implementation classes have NoSymbol as superClass
// Check for hasAnnotationFlag for SI-9393: the classfile / java source parsers add
// scala.annotation.Annotation as superclass to java annotations. In reality, java
// annotation classfiles have superclass Object (like any interface classfile).
val superClassSym = if (classSym.isImplClass || classSym.hasJavaAnnotationFlag) ObjectClass else {
val sc = classSym.superClass
// SI-9393: Java annotation classes don't have the ABSTRACT/INTERFACE flag, so they appear
// (wrongly) as superclasses. Fix this for BTypes: the java annotation will appear as interface
// (handled by method implementedInterfaces), the superclass is set to Object.
if (sc.hasJavaAnnotationFlag) ObjectClass
else sc
}
assert(
if (classSym == ObjectClass)
superClassSym == NoSymbol
Expand Down Expand Up @@ -351,7 +362,15 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
val isTopLevel = innerClassSym.rawowner.isPackageClass
// impl classes are considered top-level, see comment in BTypes
if (isTopLevel || considerAsTopLevelImplementationArtifact(innerClassSym)) None
else {
else if (innerClassSym.rawowner.isTerm) {
// This case should never be reached: the lambdalift phase mutates the rawowner field of all
// classes to be the enclosing class. SI-9392 shows an errant macro that leaves a reference
// to a local class symbol that no longer exists, which is not updated by lambdalift.
devWarning(innerClassSym.pos,
s"""The class symbol $innerClassSym with the term symbol ${innerClassSym.rawowner} as `rawowner` reached the backend.
|Most likely this indicates a stale reference to a non-existing class introduced by a macro, see SI-9392.""".stripMargin)
None
} else {
// See comment in BTypes, when is a class marked static in the InnerClass table.
val isStaticNestedClass = isOriginallyStaticOwner(innerClassSym.originalOwner)

Expand Down Expand Up @@ -559,7 +578,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0,
if (sym.isArtifact) ACC_SYNTHETIC else 0,
if (sym.isClass && !sym.isInterface) ACC_SUPER else 0,
if (sym.hasEnumFlag) ACC_ENUM else 0,
if (sym.hasJavaEnumFlag) ACC_ENUM else 0,
if (sym.isVarargsMethod) ACC_VARARGS else 0,
if (sym.hasFlag(symtab.Flags.SYNCHRONIZED)) ACC_SYNCHRONIZED else 0,
if (sym.isDeprecated) asm.Opcodes.ACC_DEPRECATED else 0
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package scala.tools.nsc
package backend.jvm

import scala.tools.asm.tree.{AbstractInsnNode, MethodNode}
import scala.tools.asm.tree.{InvokeDynamicInsnNode, AbstractInsnNode, MethodNode}
import scala.tools.nsc.backend.jvm.BTypes.InternalName
import scala.reflect.internal.util.Position
import scala.tools.nsc.settings.ScalaSettings
Expand Down Expand Up @@ -246,11 +246,16 @@ object BackendReporting {
case class ResultingMethodTooLarge(calleeDeclarationClass: InternalName, name: String, descriptor: String,
callsiteClass: InternalName, callsiteName: String, callsiteDesc: String) extends CannotInlineWarning

case object UnknownInvokeDynamicInstruction extends OptimizerWarning {
override def toString = "The callee contains an InvokeDynamic instruction with an unknown bootstrap method (not a LambdaMetaFactory)."
def emitWarning(settings: ScalaSettings): Boolean = settings.YoptWarningEmitAtInlineFailed
}

/**
* Used in `rewriteClosureApplyInvocations` when a closure apply callsite cannot be rewritten
* to the closure body method.
*/
trait RewriteClosureApplyToClosureBodyFailed extends OptimizerWarning {
sealed trait RewriteClosureApplyToClosureBodyFailed extends OptimizerWarning {
def pos: Position

override def emitWarning(settings: ScalaSettings): Boolean = this match {
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
if (sym.isBridge) ACC_BRIDGE | ACC_SYNTHETIC else 0,
if (sym.isArtifact) ACC_SYNTHETIC else 0,
if (sym.isClass && !sym.isInterface) ACC_SUPER else 0,
if (sym.hasEnumFlag) ACC_ENUM else 0,
if (sym.hasJavaEnumFlag) ACC_ENUM else 0,
if (sym.isVarargsMethod) ACC_VARARGS else 0,
if (sym.hasFlag(Flags.SYNCHRONIZED)) ACC_SYNCHRONIZED else 0
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,20 @@ import scala.collection.convert.decorateAsScala._
* copying operations.
*/
class ProdConsAnalyzer(methodNode: MethodNode, classInternalName: InternalName) {

/* Timers for benchmarking ProdCons
import scala.reflect.internal.util.Statistics._
import ProdConsAnalyzer._
val analyzerTimer = newSubTimer(classInternalName + "#" + methodNode.name + " - analysis", prodConsAnalyzerTimer)
val consumersTimer = newSubTimer(classInternalName + "#" + methodNode.name + " - consumers", prodConsAnalyzerTimer)
*/

val analyzer = new Analyzer(new InitialProducerSourceInterpreter)

// val start = analyzerTimer.start()
analyzer.analyze(classInternalName, methodNode)
// analyzerTimer.stop(start)
// println(analyzerTimer.line)

def frameAt(insn: AbstractInsnNode) = analyzer.frameAt(insn, methodNode)

Expand Down Expand Up @@ -103,7 +115,11 @@ class ProdConsAnalyzer(methodNode: MethodNode, classInternalName: InternalName)
def initialProducersForValueAt(insn: AbstractInsnNode, slot: Int): Set[AbstractInsnNode] = {
def initialProducers(insn: AbstractInsnNode, producedSlot: Int): Set[AbstractInsnNode] = {
if (isCopyOperation(insn)) {
_initialProducersCache.getOrElseUpdate((insn, producedSlot), {
val key = (insn, producedSlot)
_initialProducersCache.getOrElseUpdate(key, {
// prevent infinite recursion if an instruction is its own producer or consumer
// see cyclicProdCons in ProdConsAnalyzerTest
_initialProducersCache(key) = Set.empty
val (sourceValue, sourceValueSlot) = copyOperationSourceValue(insn, producedSlot)
sourceValue.insns.iterator.asScala.flatMap(initialProducers(_, sourceValueSlot)).toSet
})
Expand All @@ -121,7 +137,11 @@ class ProdConsAnalyzer(methodNode: MethodNode, classInternalName: InternalName)
def ultimateConsumersOfValueAt(insn: AbstractInsnNode, slot: Int): Set[AbstractInsnNode] = {
def ultimateConsumers(insn: AbstractInsnNode, consumedSlot: Int): Set[AbstractInsnNode] = {
if (isCopyOperation(insn)) {
_ultimateConsumersCache.getOrElseUpdate((insn, consumedSlot), {
val key = (insn, consumedSlot)
_ultimateConsumersCache.getOrElseUpdate(key, {
// prevent infinite recursion if an instruction is its own producer or consumer
// see cyclicProdCons in ProdConsAnalyzerTest
_ultimateConsumersCache(key) = Set.empty
for {
producedSlot <- copyOperationProducedValueSlots(insn, consumedSlot)
consumer <- consumersOfValueAt(insn.getNext, producedSlot)
Expand Down Expand Up @@ -384,6 +404,7 @@ class ProdConsAnalyzer(methodNode: MethodNode, classInternalName: InternalName)

/** For each instruction, a set of potential consumers of the produced values. */
private lazy val _consumersOfOutputsFrom: Map[AbstractInsnNode, Vector[Set[AbstractInsnNode]]] = {
// val start = consumersTimer.start()
var res = Map.empty[AbstractInsnNode, Vector[Set[AbstractInsnNode]]]
for {
insn <- methodNode.instructions.iterator.asScala
Expand All @@ -396,13 +417,20 @@ class ProdConsAnalyzer(methodNode: MethodNode, classInternalName: InternalName)
val outputIndex = producedSlots.indexOf(i)
res = res.updated(producer, currentConsumers.updated(outputIndex, currentConsumers(outputIndex) + insn))
}
// consumersTimer.stop(start)
// println(consumersTimer.line)
res
}

private val _initialProducersCache: mutable.AnyRefMap[(AbstractInsnNode, Int), Set[AbstractInsnNode]] = mutable.AnyRefMap.empty
private val _ultimateConsumersCache: mutable.AnyRefMap[(AbstractInsnNode, Int), Set[AbstractInsnNode]] = mutable.AnyRefMap.empty
}

object ProdConsAnalyzer {
import scala.reflect.internal.util.Statistics._
val prodConsAnalyzerTimer = newTimer("Time in ProdConsAnalyzer", "jvm")
}

/**
* A class for pseudo-instructions representing the initial producers of local values that have
* no producer instruction in the method:
Expand Down
24 changes: 23 additions & 1 deletion src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import scala.collection.mutable
import scala.reflect.internal.util.Collections._
import scala.tools.asm.commons.CodeSizeEvaluator
import scala.tools.asm.tree.analysis._
import scala.tools.asm.{MethodWriter, ClassWriter, Label, Opcodes}
import scala.tools.asm.{MethodWriter, ClassWriter, Label, Opcodes, Type}
import scala.tools.asm.tree._
import GenBCode._
import scala.collection.convert.decorateAsScala._
Expand Down Expand Up @@ -104,6 +104,8 @@ object BytecodeUtils {

def isStrictfpMethod(methodNode: MethodNode): Boolean = (methodNode.access & Opcodes.ACC_STRICT) != 0

def isReference(t: Type) = t.getSort == Type.OBJECT || t.getSort == Type.ARRAY

def nextExecutableInstruction(instruction: AbstractInsnNode, alsoKeep: AbstractInsnNode => Boolean = Set()): Option[AbstractInsnNode] = {
var result = instruction
do { result = result.getNext }
Expand Down Expand Up @@ -330,6 +332,26 @@ object BytecodeUtils {
)).toList
}

/**
* This method is used by optimizer components to eliminate phantom values of instruction
* that load a value of type `Nothing$` or `Null$`. Such values on the stack don't interact well
* with stack map frames.
*
* For example, `opt.getOrElse(throw e)` is re-written to an invocation of the lambda body, a
* method with return type `Nothing$`. Similarly for `opt.getOrElse(null)` and `Null$`.
*
* During bytecode generation this is handled by BCodeBodyBuilder.adapt. See the comment in that
* method which explains the issue with such phantom values.
*/
def fixLoadedNothingOrNullValue(loadedType: Type, loadInstr: AbstractInsnNode, methodNode: MethodNode, bTypes: BTypes): Unit = {
if (loadedType == bTypes.coreBTypes.RT_NOTHING.toASMType) {
methodNode.instructions.insert(loadInstr, new InsnNode(Opcodes.ATHROW))
} else if (loadedType == bTypes.coreBTypes.RT_NULL.toASMType) {
methodNode.instructions.insert(loadInstr, new InsnNode(Opcodes.ACONST_NULL))
methodNode.instructions.insert(loadInstr, new InsnNode(Opcodes.POP))
}
}

/**
* A wrapper to make ASM's Analyzer a bit easier to use.
*/
Expand Down

0 comments on commit f2d7838

Please sign in to comment.