summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJason Zaugg <jzaugg@gmail.com>2015-07-27 13:31:32 +1000
committerJason Zaugg <jzaugg@gmail.com>2015-07-27 13:31:32 +1000
commitf2d7838d904572b3dd15a9ef223ecfd1a6e14697 (patch)
tree20d04ea95d60d936f0e48c9767e50e869704d68a /src
parent4c6dcfe9341d2db47a0b0a567f5646fb9d573020 (diff)
parent241bb9ac192f13868436d9a4e2e7ae29a2e22bed (diff)
downloadscala-f2d7838d904572b3dd15a9ef223ecfd1a6e14697.tar.gz
scala-f2d7838d904572b3dd15a9ef223ecfd1a6e14697.tar.bz2
scala-f2d7838d904572b3dd15a9ef223ecfd1a6e14697.zip
Merge pull request #4657 from lrytz/backports
backports from 2.12.x
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala18
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala32
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala3
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala12
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala6
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala25
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala9
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzer.scala32
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala24
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala92
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala446
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala69
-rw-r--r--src/compiler/scala/tools/nsc/javac/JavaParsers.scala16
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala4
-rw-r--r--src/partest-extras/scala/tools/partest/ASMConverters.scala78
-rw-r--r--src/reflect/scala/reflect/internal/ClassfileConstants.scala4
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala2
-rw-r--r--src/reflect/scala/reflect/internal/FlagSets.scala2
-rw-r--r--src/reflect/scala/reflect/internal/Flags.scala155
-rw-r--r--src/reflect/scala/reflect/internal/HasFlags.scala83
-rw-r--r--src/reflect/scala/reflect/internal/Symbols.scala2
-rw-r--r--src/reflect/scala/reflect/runtime/JavaMirrors.scala1
-rw-r--r--src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala6
26 files changed, 706 insertions, 425 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala
index 0df1b2029d..cd7e0b83e8 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala
@@ -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 {
@@ -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
}
/**
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
index daa31c5dfe..93f5159f89 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala
@@ -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
)
}
@@ -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
@@ -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
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
index 40ba0c010b..67fc7923ea 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
@@ -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)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
index 6aa3a62295..65a6b82570 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
@@ -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
}
@@ -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()
@@ -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()
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
index c7c2e43f82..a9b6a312e9 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
@@ -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
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
index cf29c8090b..45d9cc3ff3 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
@@ -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
@@ -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)
@@ -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
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala
index 4fc05cafdc..b41d0de92f 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala
@@ -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
@@ -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 {
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
index 2f1cd732da..a34ab914ef 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala
@@ -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
)
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzer.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzer.scala
index 40f91cbed4..700b2f2f6c 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzer.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzer.scala
@@ -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)
@@ -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
})
@@ -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)
@@ -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
@@ -396,6 +417,8 @@ 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
}
@@ -403,6 +426,11 @@ class ProdConsAnalyzer(methodNode: MethodNode, classInternalName: InternalName)
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:
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
index 0ec550981a..df8dcc690a 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
@@ -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._
@@ -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 }
@@ -331,6 +333,26 @@ object BytecodeUtils {
}
/**
+ * 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.
*/
class AsmAnalyzer[V <: Value](methodNode: MethodNode, classInternalName: InternalName, interpreter: Interpreter[V] = new BasicInterpreter) {
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
index 8abecdb261..96455c0e38 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
@@ -9,7 +9,7 @@ package opt
import scala.reflect.internal.util.{NoPosition, Position}
import scala.tools.asm.tree.analysis.{Value, Analyzer, BasicInterpreter}
-import scala.tools.asm.{Opcodes, Type}
+import scala.tools.asm.{Opcodes, Type, Handle}
import scala.tools.asm.tree._
import scala.collection.concurrent
import scala.collection.convert.decorateAsScala._
@@ -24,7 +24,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
val callsites: concurrent.Map[MethodInsnNode, Callsite] = recordPerRunCache(concurrent.TrieMap.empty)
- val closureInstantiations: concurrent.Map[InvokeDynamicInsnNode, (MethodNode, ClassBType)] = recordPerRunCache(concurrent.TrieMap.empty)
+ val closureInstantiations: concurrent.Map[InvokeDynamicInsnNode, ClosureInstantiation] = recordPerRunCache(concurrent.TrieMap.empty)
def addClass(classNode: ClassNode): Unit = {
val classType = classBTypeFromClassNode(classNode)
@@ -33,14 +33,14 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
(calls, closureInits) = analyzeCallsites(m, classType)
} {
calls foreach (callsite => callsites(callsite.callsiteInstruction) = callsite)
- closureInits foreach (indy => closureInstantiations(indy) = (m, classType))
+ closureInits foreach (lmf => closureInstantiations(lmf.indy) = ClosureInstantiation(lmf, m, classType))
}
}
/**
* Returns a list of callsites in the method, plus a list of closure instantiation indy instructions.
*/
- def analyzeCallsites(methodNode: MethodNode, definingClass: ClassBType): (List[Callsite], List[InvokeDynamicInsnNode]) = {
+ def analyzeCallsites(methodNode: MethodNode, definingClass: ClassBType): (List[Callsite], List[LambdaMetaFactoryCall]) = {
case class CallsiteInfo(safeToInline: Boolean, safeToRewrite: Boolean,
annotatedInline: Boolean, annotatedNoInline: Boolean,
@@ -129,7 +129,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
}
val callsites = new collection.mutable.ListBuffer[Callsite]
- val closureInstantiations = new collection.mutable.ListBuffer[InvokeDynamicInsnNode]
+ val closureInstantiations = new collection.mutable.ListBuffer[LambdaMetaFactoryCall]
methodNode.instructions.iterator.asScala foreach {
case call: MethodInsnNode =>
@@ -173,8 +173,8 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
callsitePosition = callsitePositions.getOrElse(call, NoPosition)
)
- case indy: InvokeDynamicInsnNode =>
- if (closureOptimizer.isClosureInstantiation(indy)) closureInstantiations += indy
+ case LambdaMetaFactoryCall(indy, samMethodType, implMethod, instantiatedMethodType) =>
+ closureInstantiations += LambdaMetaFactoryCall(indy, samMethodType, implMethod, instantiatedMethodType)
case _ =>
}
@@ -236,4 +236,82 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
calleeInfoWarning: Option[CalleeInfoWarning]) {
assert(!(safeToInline && safeToRewrite), s"A callee of ${callee.name} can be either safeToInline or safeToRewrite, but not both.")
}
+
+ final case class ClosureInstantiation(lambdaMetaFactoryCall: LambdaMetaFactoryCall, ownerMethod: MethodNode, ownerClass: ClassBType) {
+ override def toString = s"ClosureInstantiation($lambdaMetaFactoryCall, ${ownerMethod.name + ownerMethod.desc}, $ownerClass)"
+ }
+ final case class LambdaMetaFactoryCall(indy: InvokeDynamicInsnNode, samMethodType: Type, implMethod: Handle, instantiatedMethodType: Type)
+
+ object LambdaMetaFactoryCall {
+ private val lambdaMetaFactoryInternalName: InternalName = "java/lang/invoke/LambdaMetafactory"
+
+ private val metafactoryHandle = {
+ val metafactoryMethodName: String = "metafactory"
+ val metafactoryDesc: String = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"
+ new Handle(Opcodes.H_INVOKESTATIC, lambdaMetaFactoryInternalName, metafactoryMethodName, metafactoryDesc)
+ }
+
+ private val altMetafactoryHandle = {
+ val altMetafactoryMethodName: String = "altMetafactory"
+ val altMetafactoryDesc: String = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;"
+ new Handle(Opcodes.H_INVOKESTATIC, lambdaMetaFactoryInternalName, altMetafactoryMethodName, altMetafactoryDesc)
+ }
+
+ def unapply(insn: AbstractInsnNode): Option[(InvokeDynamicInsnNode, Type, Handle, Type)] = insn match {
+ case indy: InvokeDynamicInsnNode if indy.bsm == metafactoryHandle || indy.bsm == altMetafactoryHandle =>
+ indy.bsmArgs match {
+ case Array(samMethodType: Type, implMethod: Handle, instantiatedMethodType: Type, xs@_*) => // xs binding because IntelliJ gets confused about _@_*
+ // LambdaMetaFactory performs a number of automatic adaptations when invoking the lambda
+ // implementation method (casting, boxing, unboxing, and primitive widening, see Javadoc).
+ //
+ // The closure optimizer supports only one of those adaptations: it will cast arguments
+ // to the correct type when re-writing a closure call to the body method. Example:
+ //
+ // val fun: String => String = l => l
+ // val l = List("")
+ // fun(l.head)
+ //
+ // The samMethodType of Function1 is `(Object)Object`, while the instantiatedMethodType
+ // is `(String)String`. The return type of `List.head` is `Object`.
+ //
+ // The implMethod has the signature `C$anonfun(String)String`.
+ //
+ // At the closure callsite, we have an `INVOKEINTERFACE Function1.apply (Object)Object`,
+ // so the object returned by `List.head` can be directly passed into the call (no cast).
+ //
+ // The closure object will cast the object to String before passing it to the implMethod.
+ //
+ // When re-writing the closure callsite to the implMethod, we have to insert a cast.
+ //
+ // The check below ensures that
+ // (1) the implMethod type has the expected singature (captured types plus argument types
+ // from instantiatedMethodType)
+ // (2) the receiver of the implMethod matches the first captured type
+ // (3) all parameters that are not the same in samMethodType and instantiatedMethodType
+ // are reference types, so that we can insert casts to perform the same adaptation
+ // that the closure object would.
+
+ val isStatic = implMethod.getTag == Opcodes.H_INVOKESTATIC
+ val indyParamTypes = Type.getArgumentTypes(indy.desc)
+ val instantiatedMethodArgTypes = instantiatedMethodType.getArgumentTypes
+ val expectedImplMethodType = {
+ val paramTypes = (if (isStatic) indyParamTypes else indyParamTypes.tail) ++ instantiatedMethodArgTypes
+ Type.getMethodType(instantiatedMethodType.getReturnType, paramTypes: _*)
+ }
+
+ val isIndyLambda = (
+ Type.getType(implMethod.getDesc) == expectedImplMethodType // (1)
+ && (isStatic || implMethod.getOwner == indyParamTypes(0).getInternalName) // (2)
+ && samMethodType.getArgumentTypes.corresponds(instantiatedMethodArgTypes)((samArgType, instArgType) =>
+ samArgType == instArgType || isReference(samArgType) && isReference(instArgType)) // (3)
+ )
+
+ if (isIndyLambda) Some((indy, samMethodType, implMethod, instantiatedMethodType))
+ else None
+
+ case _ => None
+ }
+ case _ => None
+ }
+ }
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
index 1648a53ed8..92b9b34006 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
@@ -8,8 +8,9 @@ package backend.jvm
package opt
import scala.annotation.switch
+import scala.collection.immutable
import scala.reflect.internal.util.NoPosition
-import scala.tools.asm.{Handle, Type, Opcodes}
+import scala.tools.asm.{Type, Opcodes}
import scala.tools.asm.tree._
import scala.tools.nsc.backend.jvm.BTypes.InternalName
import scala.tools.nsc.backend.jvm.analysis.ProdConsAnalyzer
@@ -23,90 +24,157 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
import btypes._
import callGraph._
+ /**
+ * If a closure is allocated and invoked within the same method, re-write the invocation to the
+ * closure body method.
+ *
+ * Note that the closure body method (generated by delambdafy:method) takes additional parameters
+ * for the values captured by the closure. The bytecode is transformed from
+ *
+ * [generate captured values]
+ * [closure init, capturing values]
+ * [...]
+ * [load closure object]
+ * [generate closure invocation arguments]
+ * [invoke closure.apply]
+ *
+ * to
+ *
+ * [generate captured values]
+ * [store captured values into new locals]
+ * [load the captured values from locals] // a future optimization will eliminate the closure
+ * [closure init, capturing values] // instantiation if the closure object becomes unused
+ * [...]
+ * [load closure object]
+ * [generate closure invocation arguments]
+ * [store argument values into new locals]
+ * [drop the closure object]
+ * [load captured values from locals]
+ * [load argument values from locals]
+ * [invoke the closure body method]
+ */
def rewriteClosureApplyInvocations(): Unit = {
- closureInstantiations foreach {
- case (indy, (methodNode, ownerClass)) =>
- val warnings = rewriteClosureApplyInvocations(indy, methodNode, ownerClass)
- warnings.foreach(w => backendReporting.inlinerWarning(w.pos, w.toString))
+ implicit object closureInitOrdering extends Ordering[ClosureInstantiation] {
+ override def compare(x: ClosureInstantiation, y: ClosureInstantiation): Int = {
+ val cls = x.ownerClass.internalName compareTo y.ownerClass.internalName
+ if (cls != 0) return cls
+
+ val mName = x.ownerMethod.name compareTo y.ownerMethod.name
+ if (mName != 0) return mName
+
+ val mDesc = x.ownerMethod.desc compareTo y.ownerMethod.desc
+ if (mDesc != 0) return mDesc
+
+ def pos(inst: ClosureInstantiation) = inst.ownerMethod.instructions.indexOf(inst.lambdaMetaFactoryCall.indy)
+ pos(x) - pos(y)
+ }
}
- }
- private val lambdaMetaFactoryInternalName: InternalName = "java/lang/invoke/LambdaMetafactory"
+ // Grouping the closure instantiations by method allows running the ProdConsAnalyzer only once per
+ // method. Also sort the instantiations: If there are multiple closure instantiations in a method,
+ // closure invocations need to be re-written in a consistent order for bytecode stability. The local
+ // variable slots for storing captured values depends on the order of rewriting.
+ val closureInstantiationsByMethod: Map[MethodNode, immutable.TreeSet[ClosureInstantiation]] = {
+ closureInstantiations.values.groupBy(_.ownerMethod).mapValues(immutable.TreeSet.empty ++ _)
+ }
- private val metafactoryHandle = {
- val metafactoryMethodName: String = "metafactory"
- val metafactoryDesc: String = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;"
- new Handle(H_INVOKESTATIC, lambdaMetaFactoryInternalName, metafactoryMethodName, metafactoryDesc)
- }
+ // For each closure instantiation, a list of callsites of the closure that can be re-written
+ // If a callsite cannot be rewritten, for example because the lambda body method is not accessible,
+ // a warning is returned instead.
+ val callsitesToRewrite: List[(ClosureInstantiation, List[Either[RewriteClosureApplyToClosureBodyFailed, (MethodInsnNode, Int)]])] = {
+ closureInstantiationsByMethod.iterator.flatMap({
+ case (methodNode, closureInits) =>
+ // A lazy val to ensure the analysis only runs if necessary (the value is passed by name to `closureCallsites`)
+ lazy val prodCons = new ProdConsAnalyzer(methodNode, closureInits.head.ownerClass.internalName)
+ closureInits.iterator.map(init => (init, closureCallsites(init, prodCons)))
+ }).toList // mapping to a list (not a map) to keep the sorting of closureInstantiationsByMethod
+ }
- private val altMetafactoryHandle = {
- val altMetafactoryMethodName: String = "altMetafactory"
- val altMetafactoryDesc: String = "(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;"
- new Handle(H_INVOKESTATIC, lambdaMetaFactoryInternalName, altMetafactoryMethodName, altMetafactoryDesc)
+ // Rewrite all closure callsites (or issue inliner warnings for those that cannot be rewritten)
+ for ((closureInit, callsites) <- callsitesToRewrite) {
+ // Local variables that hold the captured values and the closure invocation arguments.
+ // They are lazy vals to ensure that locals for captured values are only allocated if there's
+ // actually a callsite to rewrite (an not only warnings to be issued).
+ lazy val (localsForCapturedValues, argumentLocalsList) = localsForClosureRewrite(closureInit)
+ for (callsite <- callsites) callsite match {
+ case Left(warning) =>
+ backendReporting.inlinerWarning(warning.pos, warning.toString)
+
+ case Right((invocation, stackHeight)) =>
+ rewriteClosureApplyInvocation(closureInit, invocation, stackHeight, localsForCapturedValues, argumentLocalsList)
+ }
+ }
}
- def isClosureInstantiation(indy: InvokeDynamicInsnNode): Boolean = {
- (indy.bsm == metafactoryHandle || indy.bsm == altMetafactoryHandle) &&
- {
- indy.bsmArgs match {
- case Array(samMethodType: Type, implMethod: Handle, instantiatedMethodType: Type, xs @ _*) =>
- // LambdaMetaFactory performs a number of automatic adaptations when invoking the lambda
- // implementation method (casting, boxing, unboxing, and primitive widening, see Javadoc).
- //
- // The closure optimizer supports only one of those adaptations: it will cast arguments
- // to the correct type when re-writing a closure call to the body method. Example:
- //
- // val fun: String => String = l => l
- // val l = List("")
- // fun(l.head)
- //
- // The samMethodType of Function1 is `(Object)Object`, while the instantiatedMethodType
- // is `(String)String`. The return type of `List.head` is `Object`.
- //
- // The implMethod has the signature `C$anonfun(String)String`.
- //
- // At the closure callsite, we have an `INVOKEINTERFACE Function1.apply (Object)Object`,
- // so the object returned by `List.head` can be directly passed into the call (no cast).
- //
- // The closure object will cast the object to String before passing it to the implMethod.
- //
- // When re-writing the closure callsite to the implMethod, we have to insert a cast.
- //
- // The check below ensures that
- // (1) the implMethod type has the expected singature (captured types plus argument types
- // from instantiatedMethodType)
- // (2) the receiver of the implMethod matches the first captured type
- // (3) all parameters that are not the same in samMethodType and instantiatedMethodType
- // are reference types, so that we can insert casts to perform the same adaptation
- // that the closure object would.
-
- val isStatic = implMethod.getTag == H_INVOKESTATIC
- val indyParamTypes = Type.getArgumentTypes(indy.desc)
- val instantiatedMethodArgTypes = instantiatedMethodType.getArgumentTypes
- val expectedImplMethodType = {
- val paramTypes = (if (isStatic) indyParamTypes else indyParamTypes.tail) ++ instantiatedMethodArgTypes
- Type.getMethodType(instantiatedMethodType.getReturnType, paramTypes: _*)
- }
-
- {
- Type.getType(implMethod.getDesc) == expectedImplMethodType // (1)
- } && {
- isStatic || implMethod.getOwner == indyParamTypes(0).getInternalName // (2)
- } && {
- def isReference(t: Type) = t.getSort == Type.OBJECT || t.getSort == Type.ARRAY
- (samMethodType.getArgumentTypes, instantiatedMethodArgTypes).zipped forall {
- case (samArgType, instArgType) =>
- samArgType == instArgType || isReference(samArgType) && isReference(instArgType) // (3)
- }
- }
-
+ /**
+ * Insert instructions to store the values captured by a closure instantiation into local variables,
+ * and load the values back to the stack.
+ *
+ * Returns the list of locals holding those captured values, and a list of locals that should be
+ * used at the closure invocation callsite to store the arguments passed to the closure invocation.
+ */
+ private def localsForClosureRewrite(closureInit: ClosureInstantiation): (LocalsList, LocalsList) = {
+ val ownerMethod = closureInit.ownerMethod
+ val captureLocals = storeCaptures(closureInit)
+
+ // allocate locals for storing the arguments of the closure apply callsites.
+ // if there are multiple callsites, the same locals are re-used.
+ val argTypes = closureInit.lambdaMetaFactoryCall.samMethodType.getArgumentTypes
+ val firstArgLocal = ownerMethod.maxLocals
+
+ // The comment in the unapply method of `LambdaMetaFactoryCall` explains why we have to introduce
+ // casts for arguments that have different types in samMethodType and instantiatedMethodType.
+ val castLoadTypes = {
+ val instantiatedMethodType = closureInit.lambdaMetaFactoryCall.instantiatedMethodType
+ (argTypes, instantiatedMethodType.getArgumentTypes).zipped map {
+ case (samArgType, instantiatedArgType) if samArgType != instantiatedArgType =>
+ // the LambdaMetaFactoryCall extractor ensures that the two types are reference types,
+ // so we don't end up casting primitive values.
+ Some(instantiatedArgType)
case _ =>
- false
+ None
}
}
+ val argLocals = LocalsList.fromTypes(firstArgLocal, argTypes, castLoadTypes)
+ ownerMethod.maxLocals = firstArgLocal + argLocals.size
+
+ (captureLocals, argLocals)
+ }
+
+ /**
+ * Find all callsites of a closure within the method where the closure is allocated.
+ */
+ private def closureCallsites(closureInit: ClosureInstantiation, prodCons: => ProdConsAnalyzer): List[Either[RewriteClosureApplyToClosureBodyFailed, (MethodInsnNode, Int)]] = {
+ val ownerMethod = closureInit.ownerMethod
+ val ownerClass = closureInit.ownerClass
+ val lambdaBodyHandle = closureInit.lambdaMetaFactoryCall.implMethod
+
+ ownerMethod.instructions.iterator.asScala.collect({
+ case invocation: MethodInsnNode if isSamInvocation(invocation, closureInit, prodCons) =>
+ // TODO: This is maybe over-cautious.
+ // We are checking if the closure body method is accessible at the closure callsite.
+ // If the closure allocation has access to the body method, then the callsite (in the same
+ // method as the alloction) should have access too.
+ val bodyAccessible: Either[OptimizerWarning, Boolean] = for {
+ (bodyMethodNode, declClass) <- byteCodeRepository.methodNode(lambdaBodyHandle.getOwner, lambdaBodyHandle.getName, lambdaBodyHandle.getDesc): Either[OptimizerWarning, (MethodNode, InternalName)]
+ isAccessible <- inliner.memberIsAccessible(bodyMethodNode.access, classBTypeFromParsedClassfile(declClass), classBTypeFromParsedClassfile(lambdaBodyHandle.getOwner), ownerClass)
+ } yield {
+ isAccessible
+ }
+
+ def pos = callGraph.callsites.get(invocation).map(_.callsitePosition).getOrElse(NoPosition)
+ val stackSize: Either[RewriteClosureApplyToClosureBodyFailed, Int] = bodyAccessible match {
+ case Left(w) => Left(RewriteClosureAccessCheckFailed(pos, w))
+ case Right(false) => Left(RewriteClosureIllegalAccess(pos, ownerClass.internalName))
+ case _ => Right(prodCons.frameAt(invocation).getStackSize)
+ }
+
+ stackSize.right.map((invocation, _))
+ }).toList
}
- def isSamInvocation(invocation: MethodInsnNode, indy: InvokeDynamicInsnNode, prodCons: => ProdConsAnalyzer): Boolean = {
+ private def isSamInvocation(invocation: MethodInsnNode, closureInit: ClosureInstantiation, prodCons: => ProdConsAnalyzer): Boolean = {
+ val indy = closureInit.lambdaMetaFactoryCall.indy
if (invocation.getOpcode == INVOKESTATIC) false
else {
def closureIsReceiver = {
@@ -120,20 +188,95 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
}
invocation.name == indy.name && {
- val indySamMethodDesc = indy.bsmArgs(0).asInstanceOf[Type].getDescriptor // safe, checked in isClosureInstantiation
+ val indySamMethodDesc = closureInit.lambdaMetaFactoryCall.samMethodType.getDescriptor
indySamMethodDesc == invocation.desc
} &&
- closureIsReceiver // most expensive check last
+ closureIsReceiver // most expensive check last
+ }
+ }
+
+ private def rewriteClosureApplyInvocation(closureInit: ClosureInstantiation, invocation: MethodInsnNode, stackHeight: Int, localsForCapturedValues: LocalsList, argumentLocalsList: LocalsList): Unit = {
+ val ownerMethod = closureInit.ownerMethod
+ val lambdaBodyHandle = closureInit.lambdaMetaFactoryCall.implMethod
+
+ // store arguments
+ insertStoreOps(invocation, ownerMethod, argumentLocalsList)
+
+ // drop the closure from the stack
+ ownerMethod.instructions.insertBefore(invocation, new InsnNode(POP))
+
+ // load captured values and arguments
+ insertLoadOps(invocation, ownerMethod, localsForCapturedValues)
+ insertLoadOps(invocation, ownerMethod, argumentLocalsList)
+
+ // update maxStack
+ val capturesStackSize = localsForCapturedValues.size
+ val invocationStackHeight = stackHeight + capturesStackSize - 1 // -1 because the closure is gone
+ if (invocationStackHeight > ownerMethod.maxStack)
+ ownerMethod.maxStack = invocationStackHeight
+
+ // replace the callsite with a new call to the body method
+ val bodyOpcode = (lambdaBodyHandle.getTag: @switch) match {
+ case H_INVOKEVIRTUAL => INVOKEVIRTUAL
+ case H_INVOKESTATIC => INVOKESTATIC
+ case H_INVOKESPECIAL => INVOKESPECIAL
+ case H_INVOKEINTERFACE => INVOKEINTERFACE
+ case H_NEWINVOKESPECIAL =>
+ val insns = ownerMethod.instructions
+ insns.insertBefore(invocation, new TypeInsnNode(NEW, lambdaBodyHandle.getOwner))
+ insns.insertBefore(invocation, new InsnNode(DUP))
+ INVOKESPECIAL
}
+ val isInterface = bodyOpcode == INVOKEINTERFACE
+ val bodyInvocation = new MethodInsnNode(bodyOpcode, lambdaBodyHandle.getOwner, lambdaBodyHandle.getName, lambdaBodyHandle.getDesc, isInterface)
+ ownerMethod.instructions.insertBefore(invocation, bodyInvocation)
+
+ val returnType = Type.getReturnType(lambdaBodyHandle.getDesc)
+ fixLoadedNothingOrNullValue(returnType, bodyInvocation, ownerMethod, btypes) // see comment of that method
+
+ ownerMethod.instructions.remove(invocation)
+
+ // update the call graph
+ val originalCallsite = callGraph.callsites.remove(invocation)
+
+ // the method node is needed for building the call graph entry
+ val bodyMethod = byteCodeRepository.methodNode(lambdaBodyHandle.getOwner, lambdaBodyHandle.getName, lambdaBodyHandle.getDesc)
+ def bodyMethodIsBeingCompiled = byteCodeRepository.classNodeAndSource(lambdaBodyHandle.getOwner).map(_._2 == CompilationUnit).getOrElse(false)
+ val bodyMethodCallsite = Callsite(
+ callsiteInstruction = bodyInvocation,
+ callsiteMethod = ownerMethod,
+ callsiteClass = closureInit.ownerClass,
+ callee = bodyMethod.map({
+ case (bodyMethodNode, bodyMethodDeclClass) => Callee(
+ callee = bodyMethodNode,
+ calleeDeclarationClass = classBTypeFromParsedClassfile(bodyMethodDeclClass),
+ safeToInline = compilerSettings.YoptInlineGlobal || bodyMethodIsBeingCompiled,
+ safeToRewrite = false, // the lambda body method is not a trait interface method
+ annotatedInline = false,
+ annotatedNoInline = false,
+ calleeInfoWarning = None)
+ }),
+ argInfos = Nil,
+ callsiteStackHeight = invocationStackHeight,
+ receiverKnownNotNull = true, // see below (*)
+ callsitePosition = originalCallsite.map(_.callsitePosition).getOrElse(NoPosition)
+ )
+ // (*) The documentation in class LambdaMetafactory says:
+ // "if implMethod corresponds to an instance method, the first capture argument
+ // (corresponding to the receiver) must be non-null"
+ // Explanation: If the lambda body method is non-static, the receiver is a captured
+ // value. It can only be captured within some instance method, so we know it's non-null.
+ callGraph.callsites(bodyInvocation) = bodyMethodCallsite
}
/**
- * Stores the values captured by a closure creation into fresh local variables.
- * Returns the list of locals holding the captured values.
+ * Stores the values captured by a closure creation into fresh local variables, and loads the
+ * values back onto the stack. Returns the list of locals holding the captured values.
*/
- private def storeCaptures(indy: InvokeDynamicInsnNode, methodNode: MethodNode): LocalsList = {
+ private def storeCaptures(closureInit: ClosureInstantiation): LocalsList = {
+ val indy = closureInit.lambdaMetaFactoryCall.indy
val capturedTypes = Type.getArgumentTypes(indy.desc)
- val firstCaptureLocal = methodNode.maxLocals
+ val firstCaptureLocal = closureInit.ownerMethod.maxLocals
// This could be optimized: in many cases the captured values are produced by LOAD instructions.
// If the variable is not modified within the method, we could avoid introducing yet another
@@ -144,10 +287,10 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
// This is checked in `isClosureInstantiation`: the types of the captured variables in the indy
// instruction match exactly the corresponding parameter types in the body method.
val localsForCaptures = LocalsList.fromTypes(firstCaptureLocal, capturedTypes, castLoadTypes = _ => None)
- methodNode.maxLocals = firstCaptureLocal + localsForCaptures.size
+ closureInit.ownerMethod.maxLocals = firstCaptureLocal + localsForCaptures.size
- insertStoreOps(indy, methodNode, localsForCaptures)
- insertLoadOps(indy, methodNode, localsForCaptures)
+ insertStoreOps(indy, closureInit.ownerMethod, localsForCaptures)
+ insertLoadOps(indy, closureInit.ownerMethod, localsForCaptures)
localsForCaptures
}
@@ -184,145 +327,6 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
}
}
- def rewriteClosureApplyInvocations(indy: InvokeDynamicInsnNode, methodNode: MethodNode, ownerClass: ClassBType): List[RewriteClosureApplyToClosureBodyFailed] = {
- val lambdaBodyHandle = indy.bsmArgs(1).asInstanceOf[Handle] // safe, checked in isClosureInstantiation
-
- // Kept as a lazy val to make sure the analysis is only computed if it's actually needed.
- // ProdCons is used to identify closure body invocations (see isSamInvocation), but only if the
- // callsite has the right name and signature. If the method has no invcation instruction with
- // the right name and signature, the analysis is not executed.
- lazy val prodCons = new ProdConsAnalyzer(methodNode, ownerClass.internalName)
-
- // First collect all callsites without modifying the instructions list yet.
- // Once we start modifying the instruction list, prodCons becomes unusable.
-
- // A list of callsites and stack heights. If the invocation cannot be rewritten, a warning
- // message is stored in the stack height value.
- val invocationsToRewrite: List[(MethodInsnNode, Either[RewriteClosureApplyToClosureBodyFailed, Int])] = methodNode.instructions.iterator.asScala.collect({
- case invocation: MethodInsnNode if isSamInvocation(invocation, indy, prodCons) =>
- val bodyAccessible: Either[OptimizerWarning, Boolean] = for {
- (bodyMethodNode, declClass) <- byteCodeRepository.methodNode(lambdaBodyHandle.getOwner, lambdaBodyHandle.getName, lambdaBodyHandle.getDesc): Either[OptimizerWarning, (MethodNode, InternalName)]
- isAccessible <- inliner.memberIsAccessible(bodyMethodNode.access, classBTypeFromParsedClassfile(declClass), classBTypeFromParsedClassfile(lambdaBodyHandle.getOwner), ownerClass)
- } yield {
- isAccessible
- }
-
- def pos = callGraph.callsites.get(invocation).map(_.callsitePosition).getOrElse(NoPosition)
- val stackSize: Either[RewriteClosureApplyToClosureBodyFailed, Int] = bodyAccessible match {
- case Left(w) => Left(RewriteClosureAccessCheckFailed(pos, w))
- case Right(false) => Left(RewriteClosureIllegalAccess(pos, ownerClass.internalName))
- case _ => Right(prodCons.frameAt(invocation).getStackSize)
- }
-
- (invocation, stackSize)
- }).toList
-
- if (invocationsToRewrite.isEmpty) Nil
- else {
- // lazy val to make sure locals for captures and arguments are only allocated if there's
- // effectively a callsite to rewrite.
- lazy val (localsForCapturedValues, argumentLocalsList) = {
- val captureLocals = storeCaptures(indy, methodNode)
-
- // allocate locals for storing the arguments of the closure apply callsites.
- // if there are multiple callsites, the same locals are re-used.
- val argTypes = indy.bsmArgs(0).asInstanceOf[Type].getArgumentTypes // safe, checked in isClosureInstantiation
- val firstArgLocal = methodNode.maxLocals
-
- // The comment in `isClosureInstantiation` explains why we have to introduce casts for
- // arguments that have different types in samMethodType and instantiatedMethodType.
- val castLoadTypes = {
- val instantiatedMethodType = indy.bsmArgs(2).asInstanceOf[Type]
- (argTypes, instantiatedMethodType.getArgumentTypes).zipped map {
- case (samArgType, instantiatedArgType) if samArgType != instantiatedArgType =>
- // isClosureInstantiation ensures that the two types are reference types, so we don't
- // end up casting primitive values.
- Some(instantiatedArgType)
- case _ =>
- None
- }
- }
- val argLocals = LocalsList.fromTypes(firstArgLocal, argTypes, castLoadTypes)
- methodNode.maxLocals = firstArgLocal + argLocals.size
-
- (captureLocals, argLocals)
- }
-
- val warnings = invocationsToRewrite flatMap {
- case (invocation, Left(warning)) => Some(warning)
-
- case (invocation, Right(stackHeight)) =>
- // store arguments
- insertStoreOps(invocation, methodNode, argumentLocalsList)
-
- // drop the closure from the stack
- methodNode.instructions.insertBefore(invocation, new InsnNode(POP))
-
- // load captured values and arguments
- insertLoadOps(invocation, methodNode, localsForCapturedValues)
- insertLoadOps(invocation, methodNode, argumentLocalsList)
-
- // update maxStack
- val capturesStackSize = localsForCapturedValues.size
- val invocationStackHeight = stackHeight + capturesStackSize - 1 // -1 because the closure is gone
- if (invocationStackHeight > methodNode.maxStack)
- methodNode.maxStack = invocationStackHeight
-
- // replace the callsite with a new call to the body method
- val bodyOpcode = (lambdaBodyHandle.getTag: @switch) match {
- case H_INVOKEVIRTUAL => INVOKEVIRTUAL
- case H_INVOKESTATIC => INVOKESTATIC
- case H_INVOKESPECIAL => INVOKESPECIAL
- case H_INVOKEINTERFACE => INVOKEINTERFACE
- case H_NEWINVOKESPECIAL =>
- val insns = methodNode.instructions
- insns.insertBefore(invocation, new TypeInsnNode(NEW, lambdaBodyHandle.getOwner))
- insns.insertBefore(invocation, new InsnNode(DUP))
- INVOKESPECIAL
- }
- val isInterface = bodyOpcode == INVOKEINTERFACE
- val bodyInvocation = new MethodInsnNode(bodyOpcode, lambdaBodyHandle.getOwner, lambdaBodyHandle.getName, lambdaBodyHandle.getDesc, isInterface)
- methodNode.instructions.insertBefore(invocation, bodyInvocation)
- methodNode.instructions.remove(invocation)
-
- // update the call graph
- val originalCallsite = callGraph.callsites.remove(invocation)
-
- // the method node is needed for building the call graph entry
- val bodyMethod = byteCodeRepository.methodNode(lambdaBodyHandle.getOwner, lambdaBodyHandle.getName, lambdaBodyHandle.getDesc)
- def bodyMethodIsBeingCompiled = byteCodeRepository.classNodeAndSource(lambdaBodyHandle.getOwner).map(_._2 == CompilationUnit).getOrElse(false)
- val bodyMethodCallsite = Callsite(
- callsiteInstruction = bodyInvocation,
- callsiteMethod = methodNode,
- callsiteClass = ownerClass,
- callee = bodyMethod.map({
- case (bodyMethodNode, bodyMethodDeclClass) => Callee(
- callee = bodyMethodNode,
- calleeDeclarationClass = classBTypeFromParsedClassfile(bodyMethodDeclClass),
- safeToInline = compilerSettings.YoptInlineGlobal || bodyMethodIsBeingCompiled,
- safeToRewrite = false, // the lambda body method is not a trait interface method
- annotatedInline = false,
- annotatedNoInline = false,
- calleeInfoWarning = None)
- }),
- argInfos = Nil,
- callsiteStackHeight = invocationStackHeight,
- receiverKnownNotNull = true, // see below (*)
- callsitePosition = originalCallsite.map(_.callsitePosition).getOrElse(NoPosition)
- )
- // (*) The documentation in class LambdaMetafactory says:
- // "if implMethod corresponds to an instance method, the first capture argument
- // (corresponding to the receiver) must be non-null"
- // Explanation: If the lambda body method is non-static, the receiver is a captured
- // value. It can only be captured within some instance method, so we know it's non-null.
- callGraph.callsites(bodyInvocation) = bodyMethodCallsite
- None
- }
-
- warnings.toList
- }
- }
-
/**
* A list of local variables. Each local stores information about its type, see class [[Local]].
*/
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
index e8e848161c..8477f5461a 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
@@ -9,6 +9,7 @@ package opt
import scala.annotation.tailrec
import scala.tools.asm
+import asm.Handle
import asm.Opcodes._
import asm.tree._
import scala.collection.convert.decorateAsScala._
@@ -455,9 +456,9 @@ class Inliner[BT <: BTypes](val btypes: BT) {
case indy: InvokeDynamicInsnNode =>
callGraph.closureInstantiations.get(indy) match {
- case Some((methodNode, ownerClass)) =>
+ case Some(closureInit) =>
val newIndy = instructionMap(indy).asInstanceOf[InvokeDynamicInsnNode]
- callGraph.closureInstantiations(newIndy) = (callsiteMethod, callsiteClass)
+ callGraph.closureInstantiations(newIndy) = ClosureInstantiation(closureInit.lambdaMetaFactoryCall.copy(indy = newIndy), callsiteMethod, callsiteClass)
case None =>
}
@@ -687,9 +688,67 @@ class Inliner[BT <: BTypes](val btypes: BT) {
}
}
- case ivd: InvokeDynamicInsnNode =>
- // TODO @lry check necessary conditions to inline an indy, instead of giving up
- Right(false)
+ case _: InvokeDynamicInsnNode if destinationClass == calleeDeclarationClass =>
+ // within the same class, any indy instruction can be inlined
+ Right(true)
+
+ // does the InvokeDynamicInsnNode call LambdaMetaFactory?
+ case LambdaMetaFactoryCall(_, _, implMethod, _) =>
+ // an indy instr points to a "call site specifier" (CSP) [1]
+ // - a reference to a bootstrap method [2]
+ // - bootstrap method name
+ // - references to constant arguments, which can be:
+ // - constant (string, long, int, float, double)
+ // - class
+ // - method type (without name)
+ // - method handle
+ // - a method name+type
+ //
+ // execution [3]
+ // - resolve the CSP, yielding the boostrap method handle, the static args and the name+type
+ // - resolution entails accessibility checking [4]
+ // - execute the `invoke` method of the boostrap method handle (which is signature polymorphic, check its javadoc)
+ // - the descriptor for the call is made up from the actual arguments on the stack:
+ // - the first parameters are "MethodHandles.Lookup, String, MethodType", then the types of the constant arguments,
+ // - the return type is CallSite
+ // - the values for the call are
+ // - the bootstrap method handle of the CSP is the receiver
+ // - the Lookup object for the class in which the callsite occurs (obtained as through calling MethodHandles.lookup())
+ // - the method name of the CSP
+ // - the method type of the CSP
+ // - the constants of the CSP (primitives are not boxed)
+ // - the resulting `CallSite` object
+ // - has as `type` the method type of the CSP
+ // - is popped from the operand stack
+ // - the `invokeExact` method (signature polymorphic!) of the `target` method handle of the CallSite is invoked
+ // - the method descriptor is that of the CSP
+ // - the receiver is the target of the CallSite
+ // - the other argument values are those that were on the operand stack at the indy instruction (indyLambda: the captured values)
+ //
+ // [1] http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4.10
+ // [2] http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.23
+ // [3] http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5.invokedynamic
+ // [4] http://docs.oracle.com/javase/specs/jvms/se8/html/jvms-5.html#jvms-5.4.3
+
+ // We cannot generically check if an `invokedynamic` instruction can be safely inlined into
+ // a different class, that depends on the bootstrap method. The Lookup object passed to the
+ // bootstrap method is a capability to access private members of the callsite class. We can
+ // only move the invokedynamic to a new class if we know that the bootstrap method doesn't
+ // use this capability for otherwise non-accessible members.
+ // In the case of indyLambda, it depends on the visibility of the implMethod handle. If
+ // the implMethod is public, lambdaMetaFactory doesn't use the Lookup object's extended
+ // capability, and we can safely inline the instruction into a different class.
+
+ val methodRefClass = classBTypeFromParsedClassfile(implMethod.getOwner)
+ for {
+ (methodNode, methodDeclClassNode) <- byteCodeRepository.methodNode(methodRefClass.internalName, implMethod.getName, implMethod.getDesc): Either[OptimizerWarning, (MethodNode, InternalName)]
+ methodDeclClass = classBTypeFromParsedClassfile(methodDeclClassNode)
+ res <- memberIsAccessible(methodNode.access, methodDeclClass, methodRefClass, destinationClass)
+ } yield {
+ res
+ }
+
+ case _: InvokeDynamicInsnNode => Left(UnknownInvokeDynamicInstruction)
case ci: LdcInsnNode => ci.cst match {
case t: asm.Type => classIsAccessible(bTypeForDescriptorOrInternalNameFromClassfile(t.getInternalName), destinationClass)
diff --git a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala
index 9708cba281..eb25eb6e06 100644
--- a/src/compiler/scala/tools/nsc/javac/JavaParsers.scala
+++ b/src/compiler/scala/tools/nsc/javac/JavaParsers.scala
@@ -370,7 +370,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
flags |= Flags.FINAL
in.nextToken()
case DEFAULT =>
- flags |= Flags.DEFAULTMETHOD
+ flags |= Flags.JAVA_DEFAULTMETHOD
in.nextToken()
case NATIVE =>
addAnnot(NativeAttr)
@@ -489,8 +489,8 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
val vparams = formalParams()
if (!isVoid) rtpt = optArrayBrackets(rtpt)
optThrows()
- val isStatic = mods hasFlag Flags.STATIC
- val bodyOk = !inInterface || ((mods hasFlag Flags.DEFAULTMETHOD) || isStatic)
+ val isConcreteInterfaceMethod = !inInterface || (mods hasFlag Flags.JAVA_DEFAULTMETHOD) || (mods hasFlag Flags.STATIC)
+ val bodyOk = !(mods1 hasFlag Flags.DEFERRED) && isConcreteInterfaceMethod
val body =
if (bodyOk && in.token == LBRACE) {
methodBody()
@@ -509,7 +509,9 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
EmptyTree
}
}
- if (inInterface && !isStatic) mods1 |= Flags.DEFERRED
+ // for abstract methods (of classes), the `DEFERRED` flag is alredy set.
+ // here we also set it for interface methods that are not static and not default.
+ if (!isConcreteInterfaceMethod) mods1 |= Flags.DEFERRED
List {
atPos(pos) {
DefDef(mods1, name.toTermName, tparams, List(vparams), rtpt, body)
@@ -749,7 +751,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
val (statics, body) = typeBody(AT, name)
val templ = makeTemplate(annotationParents, body)
addCompanionObject(statics, atPos(pos) {
- ClassDef(mods, name, List(), templ)
+ ClassDef(mods | Flags.JAVA_ANNOTATION, name, List(), templ)
})
}
@@ -807,7 +809,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
if (hasAbstractMember) Flags.ABSTRACT else 0l
}
addCompanionObject(consts ::: statics ::: predefs, atPos(pos) {
- ClassDef(mods | Flags.ENUM | finalFlag | abstractFlag, name, List(),
+ ClassDef(mods | Flags.JAVA_ENUM | finalFlag | abstractFlag, name, List(),
makeTemplate(superclazz :: interfaces, body))
})
}
@@ -828,7 +830,7 @@ trait JavaParsers extends ast.parser.ParsersCommon with JavaScanners {
skipAhead()
accept(RBRACE)
}
- ValDef(Modifiers(Flags.ENUM | Flags.STABLE | Flags.JAVA | Flags.STATIC), name.toTermName, enumType, blankExpr)
+ ValDef(Modifiers(Flags.JAVA_ENUM | Flags.STABLE | Flags.JAVA | Flags.STATIC), name.toTermName, enumType, blankExpr)
}
(res, hasClassBody)
}
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
index 660028eab8..06a0299d2a 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
@@ -284,7 +284,7 @@ abstract class ClassfileParser {
def getType(index: Int): Type = getType(null, index)
def getType(sym: Symbol, index: Int): Type = sigToType(sym, getExternalName(index))
- def getSuperClass(index: Int): Symbol = if (index == 0) AnyClass else getClassSymbol(index)
+ def getSuperClass(index: Int): Symbol = if (index == 0) AnyClass else getClassSymbol(index) // the only classfile that is allowed to have `0` in the super_class is java/lang/Object (see jvm spec)
private def createConstant(index: Int): Constant = {
val start = starts(index)
@@ -862,7 +862,7 @@ abstract class ClassfileParser {
srcfile0 = settings.outputDirs.srcFilesFor(in.file, srcpath).find(_.exists)
case tpnme.CodeATTR =>
if (sym.owner.isInterface) {
- sym setFlag DEFAULTMETHOD
+ sym setFlag JAVA_DEFAULTMETHOD
log(s"$sym in ${sym.owner} is a java8+ default method.")
}
in.skip(attrLen)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Namers.scala b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
index c1655467e9..4ad81b60ae 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -145,8 +145,8 @@ trait Namers extends MethodSynthesis {
// while Scala's enum constants live directly in the class.
// We don't check for clazz.superClass == JavaEnumClass, because this causes a illegal
// cyclic reference error. See the commit message for details.
- if (context.unit.isJava) owner.companionClass.hasEnumFlag else owner.hasEnumFlag
- vd.mods.hasAllFlags(ENUM | STABLE | STATIC) && ownerHasEnumFlag
+ if (context.unit.isJava) owner.companionClass.hasJavaEnumFlag else owner.hasJavaEnumFlag
+ vd.mods.hasAllFlags(JAVA_ENUM | STABLE | STATIC) && ownerHasEnumFlag
}
def setPrivateWithin[T <: Symbol](tree: Tree, sym: T, mods: Modifiers): T =
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index 36423fa2aa..0198529ef7 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -421,7 +421,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
overrideError("cannot be used here - classes can only override abstract types")
} else if (other.isEffectivelyFinal) { // (1.2)
overrideError("cannot override final member")
- } else if (!other.isDeferredOrDefault && !other.hasFlag(DEFAULTMETHOD) && !member.isAnyOverride && !member.isSynthetic) { // (*)
+ } else if (!other.isDeferredOrJavaDefault && !other.hasFlag(JAVA_DEFAULTMETHOD) && !member.isAnyOverride && !member.isSynthetic) { // (*)
// (*) Synthetic exclusion for (at least) default getters, fixes SI-5178. We cannot assign the OVERRIDE flag to
// the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket.
if (isNeitherInClass && !(other.owner isSubClass member.owner))
@@ -604,7 +604,7 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
def checkNoAbstractMembers(): Unit = {
// Avoid spurious duplicates: first gather any missing members.
def memberList = clazz.info.nonPrivateMembersAdmitting(VBRIDGE)
- val (missing, rest) = memberList partition (m => m.isDeferredNotDefault && !ignoreDeferred(m))
+ val (missing, rest) = memberList partition (m => m.isDeferredNotJavaDefault && !ignoreDeferred(m))
// Group missing members by the name of the underlying symbol,
// to consolidate getters and setters.
val grouped = missing groupBy (sym => analyzer.underlyingSymbol(sym).name)
diff --git a/src/partest-extras/scala/tools/partest/ASMConverters.scala b/src/partest-extras/scala/tools/partest/ASMConverters.scala
index 90f314428b..b4c686473b 100644
--- a/src/partest-extras/scala/tools/partest/ASMConverters.scala
+++ b/src/partest-extras/scala/tools/partest/ASMConverters.scala
@@ -58,21 +58,24 @@ object ASMConverters {
case class Method(instructions: List[Instruction], handlers: List[ExceptionHandler], localVars: List[LocalVariable])
- case class Field (opcode: Int, owner: String, name: String, desc: String) extends Instruction
- case class Incr (opcode: Int, `var`: Int, incr: Int) extends Instruction
- case class Op (opcode: Int) extends Instruction
- case class IntOp (opcode: Int, operand: Int) extends Instruction
- case class Jump (opcode: Int, label: Label) extends Instruction
- case class Ldc (opcode: Int, cst: Any) extends Instruction
- case class LookupSwitch(opcode: Int, dflt: Label, keys: List[Int], labels: List[Label]) extends Instruction
- case class TableSwitch (opcode: Int, min: Int, max: Int, dflt: Label, labels: List[Label]) extends Instruction
- case class Invoke (opcode: Int, owner: String, name: String, desc: String, itf: Boolean) extends Instruction
- case class NewArray (opcode: Int, desc: String, dims: Int) extends Instruction
- case class TypeOp (opcode: Int, desc: String) extends Instruction
- case class VarOp (opcode: Int, `var`: Int) extends Instruction
- case class Label (offset: Int) extends Instruction { def opcode: Int = -1 }
- case class FrameEntry (`type`: Int, local: List[Any], stack: List[Any]) extends Instruction { def opcode: Int = -1 }
- case class LineNumber (line: Int, start: Label) extends Instruction { def opcode: Int = -1 }
+ case class Field (opcode: Int, owner: String, name: String, desc: String) extends Instruction
+ case class Incr (opcode: Int, `var`: Int, incr: Int) extends Instruction
+ case class Op (opcode: Int) extends Instruction
+ case class IntOp (opcode: Int, operand: Int) extends Instruction
+ case class Jump (opcode: Int, label: Label) extends Instruction
+ case class Ldc (opcode: Int, cst: Any) extends Instruction
+ case class LookupSwitch (opcode: Int, dflt: Label, keys: List[Int], labels: List[Label]) extends Instruction
+ case class TableSwitch (opcode: Int, min: Int, max: Int, dflt: Label, labels: List[Label]) extends Instruction
+ case class Invoke (opcode: Int, owner: String, name: String, desc: String, itf: Boolean) extends Instruction
+ case class InvokeDynamic(opcode: Int, name: String, desc: String, bsm: MethodHandle, bsmArgs: List[AnyRef]) extends Instruction
+ case class NewArray (opcode: Int, desc: String, dims: Int) extends Instruction
+ case class TypeOp (opcode: Int, desc: String) extends Instruction
+ case class VarOp (opcode: Int, `var`: Int) extends Instruction
+ case class Label (offset: Int) extends Instruction { def opcode: Int = -1 }
+ case class FrameEntry (`type`: Int, local: List[Any], stack: List[Any]) extends Instruction { def opcode: Int = -1 }
+ case class LineNumber (line: Int, start: Label) extends Instruction { def opcode: Int = -1 }
+
+ case class MethodHandle(tag: Int, owner: String, name: String, desc: String)
case class ExceptionHandler(start: Label, end: Label, handler: Label, desc: Option[String])
case class LocalVariable(name: String, desc: String, signature: Option[String], start: Label, end: Label, index: Int)
@@ -111,6 +114,7 @@ object ASMConverters {
case i: t.LookupSwitchInsnNode => LookupSwitch (op(i), applyLabel(i.dflt), lst(i.keys) map (x => x: Int), lst(i.labels) map applyLabel)
case i: t.TableSwitchInsnNode => TableSwitch (op(i), i.min, i.max, applyLabel(i.dflt), lst(i.labels) map applyLabel)
case i: t.MethodInsnNode => Invoke (op(i), i.owner, i.name, i.desc, i.itf)
+ case i: t.InvokeDynamicInsnNode => InvokeDynamic(op(i), i.name, i.desc, convertMethodHandle(i.bsm), convertBsmArgs(i.bsmArgs))
case i: t.MultiANewArrayInsnNode => NewArray (op(i), i.desc, i.dims)
case i: t.TypeInsnNode => TypeOp (op(i), i.desc)
case i: t.VarInsnNode => VarOp (op(i), i.`var`)
@@ -119,6 +123,13 @@ object ASMConverters {
case i: t.LineNumberNode => LineNumber (i.line, applyLabel(i.start))
}
+ private def convertBsmArgs(a: Array[Object]): List[Object] = a.map({
+ case h: asm.Handle => convertMethodHandle(h)
+ case _ => a // can be: Class, method Type, primitive constant
+ })(collection.breakOut)
+
+ private def convertMethodHandle(h: asm.Handle): MethodHandle = MethodHandle(h.getTag, h.getOwner, h.getName, h.getDesc)
+
private def convertHandlers(method: t.MethodNode): List[ExceptionHandler] = {
method.tryCatchBlocks.asScala.map(h => ExceptionHandler(applyLabel(h.start), applyLabel(h.end), applyLabel(h.handler), Option(h.`type`)))(collection.breakOut)
}
@@ -197,21 +208,28 @@ object ASMConverters {
case x => x.asInstanceOf[Object]
}
+ def unconvertMethodHandle(h: MethodHandle): asm.Handle = new asm.Handle(h.tag, h.owner, h.name, h.desc)
+ def unconvertBsmArgs(a: List[Object]): Array[Object] = a.map({
+ case h: MethodHandle => unconvertMethodHandle(h)
+ case o => o
+ })(collection.breakOut)
+
private def visitMethod(method: t.MethodNode, instruction: Instruction, asmLabel: Map[Label, asm.Label]): Unit = instruction match {
- case Field(op, owner, name, desc) => method.visitFieldInsn(op, owner, name, desc)
- case Incr(op, vr, incr) => method.visitIincInsn(vr, incr)
- case Op(op) => method.visitInsn(op)
- case IntOp(op, operand) => method.visitIntInsn(op, operand)
- case Jump(op, label) => method.visitJumpInsn(op, asmLabel(label))
- case Ldc(op, cst) => method.visitLdcInsn(cst)
- case LookupSwitch(op, dflt, keys, labels) => method.visitLookupSwitchInsn(asmLabel(dflt), keys.toArray, (labels map asmLabel).toArray)
- case TableSwitch(op, min, max, dflt, labels) => method.visitTableSwitchInsn(min, max, asmLabel(dflt), (labels map asmLabel).toArray: _*)
- case Invoke(op, owner, name, desc, itf) => method.visitMethodInsn(op, owner, name, desc, itf)
- case NewArray(op, desc, dims) => method.visitMultiANewArrayInsn(desc, dims)
- case TypeOp(op, desc) => method.visitTypeInsn(op, desc)
- case VarOp(op, vr) => method.visitVarInsn(op, vr)
- case l: Label => method.visitLabel(asmLabel(l))
- case FrameEntry(tp, local, stack) => method.visitFrame(tp, local.length, frameTypesToAsm(local, asmLabel).toArray, stack.length, frameTypesToAsm(stack, asmLabel).toArray)
- case LineNumber(line, start) => method.visitLineNumber(line, asmLabel(start))
+ case Field(op, owner, name, desc) => method.visitFieldInsn(op, owner, name, desc)
+ case Incr(op, vr, incr) => method.visitIincInsn(vr, incr)
+ case Op(op) => method.visitInsn(op)
+ case IntOp(op, operand) => method.visitIntInsn(op, operand)
+ case Jump(op, label) => method.visitJumpInsn(op, asmLabel(label))
+ case Ldc(op, cst) => method.visitLdcInsn(cst)
+ case LookupSwitch(op, dflt, keys, labels) => method.visitLookupSwitchInsn(asmLabel(dflt), keys.toArray, (labels map asmLabel).toArray)
+ case TableSwitch(op, min, max, dflt, labels) => method.visitTableSwitchInsn(min, max, asmLabel(dflt), (labels map asmLabel).toArray: _*)
+ case Invoke(op, owner, name, desc, itf) => method.visitMethodInsn(op, owner, name, desc, itf)
+ case InvokeDynamic(op, name, desc, bsm, bsmArgs) => method.visitInvokeDynamicInsn(name, desc, unconvertMethodHandle(bsm), unconvertBsmArgs(bsmArgs))
+ case NewArray(op, desc, dims) => method.visitMultiANewArrayInsn(desc, dims)
+ case TypeOp(op, desc) => method.visitTypeInsn(op, desc)
+ case VarOp(op, vr) => method.visitVarInsn(op, vr)
+ case l: Label => method.visitLabel(asmLabel(l))
+ case FrameEntry(tp, local, stack) => method.visitFrame(tp, local.length, frameTypesToAsm(local, asmLabel).toArray, stack.length, frameTypesToAsm(stack, asmLabel).toArray)
+ case LineNumber(line, start) => method.visitLineNumber(line, asmLabel(start))
}
}
diff --git a/src/reflect/scala/reflect/internal/ClassfileConstants.scala b/src/reflect/scala/reflect/internal/ClassfileConstants.scala
index 53241fb15b..e5d97e8959 100644
--- a/src/reflect/scala/reflect/internal/ClassfileConstants.scala
+++ b/src/reflect/scala/reflect/internal/ClassfileConstants.scala
@@ -344,7 +344,8 @@ object ClassfileConstants {
case JAVA_ACC_STATIC => STATIC
case JAVA_ACC_ABSTRACT => if (isAnnotation) 0L else if (isClass) ABSTRACT else DEFERRED
case JAVA_ACC_INTERFACE => if (isAnnotation) 0L else TRAIT | INTERFACE | ABSTRACT
- case JAVA_ACC_ENUM => ENUM
+ case JAVA_ACC_ENUM => JAVA_ENUM
+ case JAVA_ACC_ANNOTATION => JAVA_ANNOTATION
case _ => 0L
}
private def translateFlags(jflags: Int, baseFlags: Long, isClass: Boolean): Long = {
@@ -360,6 +361,7 @@ object ClassfileConstants {
res |= translateFlag0(jflags & JAVA_ACC_ABSTRACT)
res |= translateFlag0(jflags & JAVA_ACC_INTERFACE)
res |= translateFlag0(jflags & JAVA_ACC_ENUM)
+ res |= translateFlag0(jflags & JAVA_ACC_ANNOTATION)
res
}
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index 4e0fc1e36e..0bdf5b4647 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -815,7 +815,7 @@ trait Definitions extends api.StandardDefinitions {
// must filter out "universal" members (getClass is deferred for some reason)
val deferredMembers = (
tp membersBasedOnFlags (excludedFlags = BridgeAndPrivateFlags, requiredFlags = METHOD)
- filter (mem => mem.isDeferredNotDefault && !isUniversalMember(mem)) // TODO: test
+ filter (mem => mem.isDeferredNotJavaDefault && !isUniversalMember(mem)) // TODO: test
)
// if there is only one, it's monomorphic and has a single argument list
diff --git a/src/reflect/scala/reflect/internal/FlagSets.scala b/src/reflect/scala/reflect/internal/FlagSets.scala
index ef9c77878f..b6521634fb 100644
--- a/src/reflect/scala/reflect/internal/FlagSets.scala
+++ b/src/reflect/scala/reflect/internal/FlagSets.scala
@@ -42,7 +42,7 @@ trait FlagSets extends api.FlagSets { self: SymbolTable =>
val DEFAULTPARAM : FlagSet = Flags.DEFAULTPARAM
val PRESUPER : FlagSet = Flags.PRESUPER
val DEFAULTINIT : FlagSet = Flags.DEFAULTINIT
- val ENUM : FlagSet = Flags.ENUM
+ val ENUM : FlagSet = Flags.JAVA_ENUM
val PARAMACCESSOR : FlagSet = Flags.PARAMACCESSOR
val CASEACCESSOR : FlagSet = Flags.CASEACCESSOR
val SYNTHETIC : FlagSet = Flags.SYNTHETIC
diff --git a/src/reflect/scala/reflect/internal/Flags.scala b/src/reflect/scala/reflect/internal/Flags.scala
index 1707061817..754b96a9dd 100644
--- a/src/reflect/scala/reflect/internal/Flags.scala
+++ b/src/reflect/scala/reflect/internal/Flags.scala
@@ -15,65 +15,65 @@ import scala.collection.{ mutable, immutable }
//
// Generated by mkFlagsTable() at Thu Feb 02 20:31:52 PST 2012
//
-// 0: PROTECTED/M
-// 1: OVERRIDE/M
-// 2: PRIVATE/M
-// 3: ABSTRACT/M
-// 4: DEFERRED/M
-// 5: FINAL/M
-// 6: METHOD
-// 7: INTERFACE/M
-// 8: MODULE
-// 9: IMPLICIT/M
-// 10: SEALED/M
-// 11: CASE/M
-// 12: MUTABLE/M
-// 13: PARAM/M
-// 14: PACKAGE
-// 15: MACRO/M
-// 16: BYNAMEPARAM/M CAPTURED COVARIANT/M
-// 17: CONTRAVARIANT/M INCONSTRUCTOR LABEL
-// 18: ABSOVERRIDE/M
-// 19: LOCAL/M
-// 20: JAVA/M
-// 21: SYNTHETIC
-// 22: STABLE
-// 23: STATIC/M
-// 24: CASEACCESSOR/M
-// 25: DEFAULTPARAM/M TRAIT/M
-// 26: BRIDGE
-// 27: ACCESSOR
-// 28: SUPERACCESSOR
-// 29: PARAMACCESSOR/M
-// 30: MODULEVAR
-// 31: LAZY/M
-// 32: IS_ERROR
-// 33: OVERLOADED
-// 34: LIFTED
-// 35: EXISTENTIAL MIXEDIN
-// 36: EXPANDEDNAME
-// 37: IMPLCLASS PRESUPER/M
-// 38: TRANS_FLAG
-// 39: LOCKED
-// 40: SPECIALIZED
-// 41: DEFAULTINIT/M
-// 42: VBRIDGE
-// 43: VARARGS
-// 44: TRIEDCOOKING
-// 45: SYNCHRONIZED/M
-// 46: ARTIFACT
-// 47: DEFAULTMETHOD/M
-// 48: ENUM
-// 49:
+// 0: PROTECTED/M
+// 1: OVERRIDE/M
+// 2: PRIVATE/M
+// 3: ABSTRACT/M
+// 4: DEFERRED/M
+// 5: FINAL/M
+// 6: METHOD
+// 7: INTERFACE/M
+// 8: MODULE
+// 9: IMPLICIT/M
+// 10: SEALED/M
+// 11: CASE/M
+// 12: MUTABLE/M
+// 13: PARAM/M
+// 14: PACKAGE
+// 15: MACRO/M
+// 16: BYNAMEPARAM/M CAPTURED COVARIANT/M
+// 17: CONTRAVARIANT/M INCONSTRUCTOR LABEL
+// 18: ABSOVERRIDE/M
+// 19: LOCAL/M
+// 20: JAVA/M
+// 21: SYNTHETIC
+// 22: STABLE
+// 23: STATIC/M
+// 24: CASEACCESSOR/M
+// 25: DEFAULTPARAM/M TRAIT/M
+// 26: BRIDGE
+// 27: ACCESSOR
+// 28: SUPERACCESSOR
+// 29: PARAMACCESSOR/M
+// 30: MODULEVAR
+// 31: LAZY/M
+// 32: IS_ERROR
+// 33: OVERLOADED
+// 34: LIFTED
+// 35: EXISTENTIAL MIXEDIN
+// 36: EXPANDEDNAME
+// 37: IMPLCLASS PRESUPER/M
+// 38: TRANS_FLAG
+// 39: LOCKED
+// 40: SPECIALIZED
+// 41: DEFAULTINIT/M
+// 42: VBRIDGE
+// 43: VARARGS
+// 44: TRIEDCOOKING
+// 45: SYNCHRONIZED/M
+// 46: ARTIFACT
+// 47: JAVA_DEFAULTMETHOD/M
+// 48: JAVA_ENUM
+// 49: JAVA_ANNOTATION
// 50:
-// 51: lateDEFERRED
-// 52: lateFINAL
-// 53: lateMETHOD
-// 54: lateINTERFACE
-// 55: lateMODULE
-// 56: notPROTECTED
-// 57: notOVERRIDE
-// 58: notPRIVATE
+// 51: lateDEFERRED
+// 52: lateFINAL
+// 53: lateMETHOD
+// 54: lateINTERFACE
+// 55: lateMODULE
+// 56: notPROTECTED
+// 57: notOVERRIDE
+// 58: notPRIVATE
// 59:
// 60:
// 61:
@@ -119,8 +119,9 @@ class ModifierFlags {
final val DEFAULTINIT = 1L << 41 // symbol is initialized to the default value: used by -Xcheckinit
final val ARTIFACT = 1L << 46 // symbol should be ignored when typechecking; will be marked ACC_SYNTHETIC in bytecode
// to see which symbols are marked as ARTIFACT, see scaladocs for FlagValues.ARTIFACT
- final val DEFAULTMETHOD = 1L << 47 // symbol is a java default method
- final val ENUM = 1L << 48 // symbol is an enum
+ final val JAVA_DEFAULTMETHOD = 1L << 47 // symbol is a java default method
+ final val JAVA_ENUM = 1L << 48 // symbol is a java enum
+ final val JAVA_ANNOTATION = 1L << 49 // symbol is a java annotation
// Overridden.
def flagToString(flag: Long): String = ""
@@ -172,12 +173,28 @@ class Flags extends ModifierFlags {
final val SYNCHRONIZED = 1L << 45 // symbol is a method which should be marked ACC_SYNCHRONIZED
// ------- shift definitions -------------------------------------------------------
+ //
+ // Flags from 1L to (1L << 50) are normal flags.
+ //
+ // The flags DEFERRED (1L << 4) to MODULE (1L << 8) have a `late` counterpart. Late flags change
+ // their counterpart from 0 to 1 after a specific phase (see below). The first late flag
+ // (lateDEFERRED) is at (1L << 51), i.e., late flags are shifted by 47. The last one is (1L << 55).
+ //
+ // The flags PROTECTED (1L) to PRIVATE (1L << 2) have a `not` counterpart. Negated flags change
+ // their counterpart from 1 to 0 after a specific phase (see below). They are shifted by 56, i.e.,
+ // the first negated flag (notPROTECTED) is at (1L << 56), the last at (1L << 58).
+ //
+ // Late and negative flags are only enabled after certain phases, implemented by the phaseNewFlags
+ // method of the SubComponent, so they implement a bit of a flag history.
+ //
+ // The flags (1L << 59) to (1L << 63) are currently unused. If added to the InitialFlags mask,
+ // they could be used as normal flags.
- final val InitialFlags = 0x0001FFFFFFFFFFFFL // flags that are enabled from phase 1.
- final val LateFlags = 0x00FE000000000000L // flags that override flags in 0x1FC.
- final val AntiFlags = 0x7F00000000000000L // flags that cancel flags in 0x07F
- final val LateShift = 47L
- final val AntiShift = 56L
+ final val InitialFlags = 0x0007FFFFFFFFFFFFL // normal flags, enabled from the first phase: 1L to (1L << 50)
+ final val LateFlags = 0x00F8000000000000L // flags that override flags in (1L << 4) to (1L << 8): DEFERRED, FINAL, INTERFACE, METHOD, MODULE
+ final val AntiFlags = 0x0700000000000000L // flags that cancel flags in 1L to (1L << 2): PROTECTED, OVERRIDE, PRIVATE
+ final val LateShift = 47
+ final val AntiShift = 56
// Flags which sketchily share the same slot
// 16: BYNAMEPARAM/M CAPTURED COVARIANT/M
@@ -243,7 +260,7 @@ class Flags extends ModifierFlags {
*/
final val ExplicitFlags =
PRIVATE | PROTECTED | ABSTRACT | FINAL | SEALED |
- OVERRIDE | CASE | IMPLICIT | ABSOVERRIDE | LAZY | DEFAULTMETHOD
+ OVERRIDE | CASE | IMPLICIT | ABSOVERRIDE | LAZY | JAVA_DEFAULTMETHOD
/** The two bridge flags */
final val BridgeFlags = BRIDGE | VBRIDGE
@@ -434,9 +451,9 @@ class Flags extends ModifierFlags {
case TRIEDCOOKING => "<triedcooking>" // (1L << 44)
case SYNCHRONIZED => "<synchronized>" // (1L << 45)
case ARTIFACT => "<artifact>" // (1L << 46)
- case DEFAULTMETHOD => "<defaultmethod>" // (1L << 47)
- case ENUM => "<enum>" // (1L << 48)
- case 0x2000000000000L => "" // (1L << 49)
+ case JAVA_DEFAULTMETHOD => "<defaultmethod>" // (1L << 47)
+ case JAVA_ENUM => "<enum>" // (1L << 48)
+ case JAVA_ANNOTATION => "<annotation>" // (1L << 49)
case 0x4000000000000L => "" // (1L << 50)
case `lateDEFERRED` => "<latedeferred>" // (1L << 51)
case `lateFINAL` => "<latefinal>" // (1L << 52)
diff --git a/src/reflect/scala/reflect/internal/HasFlags.scala b/src/reflect/scala/reflect/internal/HasFlags.scala
index aa8f4c532e..5162b15206 100644
--- a/src/reflect/scala/reflect/internal/HasFlags.scala
+++ b/src/reflect/scala/reflect/internal/HasFlags.scala
@@ -79,49 +79,50 @@ trait HasFlags {
// Tests which come through cleanly: both Symbol and Modifiers use these
// identically, testing for a single flag.
- def hasAbstractFlag = hasFlag(ABSTRACT)
- def hasAccessorFlag = hasFlag(ACCESSOR)
- def hasDefault = hasFlag(DEFAULTPARAM) && hasFlag(METHOD | PARAM) // Second condition disambiguates with TRAIT
- def hasEnumFlag = hasFlag(ENUM)
+ def hasAbstractFlag = hasFlag(ABSTRACT)
+ def hasAccessorFlag = hasFlag(ACCESSOR)
+ def hasDefault = hasFlag(DEFAULTPARAM) && hasFlag(METHOD | PARAM) // Second condition disambiguates with TRAIT
+ def hasJavaEnumFlag = hasFlag(JAVA_ENUM)
+ def hasJavaAnnotationFlag = hasFlag(JAVA_ANNOTATION)
@deprecated("Use isLocalToThis instead", "2.11.0")
- def hasLocalFlag = hasFlag(LOCAL)
- def isLocalToThis = hasFlag(LOCAL)
- def hasModuleFlag = hasFlag(MODULE)
- def hasPackageFlag = hasFlag(PACKAGE)
- def hasStableFlag = hasFlag(STABLE)
- def hasStaticFlag = hasFlag(STATIC)
- def isAbstractOverride = hasFlag(ABSOVERRIDE)
- def isAnyOverride = hasFlag(OVERRIDE | ABSOVERRIDE)
- def isCase = hasFlag(CASE)
- def isCaseAccessor = hasFlag(CASEACCESSOR)
- def isDeferred = hasFlag(DEFERRED)
- def isFinal = hasFlag(FINAL)
- def isArtifact = hasFlag(ARTIFACT)
- def isImplicit = hasFlag(IMPLICIT)
- def isInterface = hasFlag(INTERFACE)
- def isJavaDefined = hasFlag(JAVA)
- def isLabel = hasAllFlags(LABEL | METHOD) && !hasAccessorFlag
- def isLazy = hasFlag(LAZY)
- def isLifted = hasFlag(LIFTED)
- def isMacro = hasFlag(MACRO)
- def isMutable = hasFlag(MUTABLE)
- def isOverride = hasFlag(OVERRIDE)
- def isParamAccessor = hasFlag(PARAMACCESSOR)
- def isPrivate = hasFlag(PRIVATE)
+ def hasLocalFlag = hasFlag(LOCAL)
+ def isLocalToThis = hasFlag(LOCAL)
+ def hasModuleFlag = hasFlag(MODULE)
+ def hasPackageFlag = hasFlag(PACKAGE)
+ def hasStableFlag = hasFlag(STABLE)
+ def hasStaticFlag = hasFlag(STATIC)
+ def isAbstractOverride = hasFlag(ABSOVERRIDE)
+ def isAnyOverride = hasFlag(OVERRIDE | ABSOVERRIDE)
+ def isCase = hasFlag(CASE)
+ def isCaseAccessor = hasFlag(CASEACCESSOR)
+ def isDeferred = hasFlag(DEFERRED)
+ def isFinal = hasFlag(FINAL)
+ def isArtifact = hasFlag(ARTIFACT)
+ def isImplicit = hasFlag(IMPLICIT)
+ def isInterface = hasFlag(INTERFACE)
+ def isJavaDefined = hasFlag(JAVA)
+ def isLabel = hasAllFlags(LABEL | METHOD) && !hasAccessorFlag
+ def isLazy = hasFlag(LAZY)
+ def isLifted = hasFlag(LIFTED)
+ def isMacro = hasFlag(MACRO)
+ def isMutable = hasFlag(MUTABLE)
+ def isOverride = hasFlag(OVERRIDE)
+ def isParamAccessor = hasFlag(PARAMACCESSOR)
+ def isPrivate = hasFlag(PRIVATE)
@deprecated ("Use `hasPackageFlag` instead", "2.11.0")
- def isPackage = hasFlag(PACKAGE)
- def isPrivateLocal = hasAllFlags(PrivateLocal)
- def isProtected = hasFlag(PROTECTED)
- def isProtectedLocal = hasAllFlags(ProtectedLocal)
- def isPublic = hasNoFlags(PRIVATE | PROTECTED) && !hasAccessBoundary
- def isSealed = hasFlag(SEALED)
- def isSpecialized = hasFlag(SPECIALIZED)
- def isSuperAccessor = hasFlag(SUPERACCESSOR)
- def isSynthetic = hasFlag(SYNTHETIC)
- def isTrait = hasFlag(TRAIT) && !hasFlag(PARAM)
-
- def isDeferredOrDefault = hasFlag(DEFERRED | DEFAULTMETHOD)
- def isDeferredNotDefault = isDeferred && !hasFlag(DEFAULTMETHOD)
+ def isPackage = hasFlag(PACKAGE)
+ def isPrivateLocal = hasAllFlags(PrivateLocal)
+ def isProtected = hasFlag(PROTECTED)
+ def isProtectedLocal = hasAllFlags(ProtectedLocal)
+ def isPublic = hasNoFlags(PRIVATE | PROTECTED) && !hasAccessBoundary
+ def isSealed = hasFlag(SEALED)
+ def isSpecialized = hasFlag(SPECIALIZED)
+ def isSuperAccessor = hasFlag(SUPERACCESSOR)
+ def isSynthetic = hasFlag(SYNTHETIC)
+ def isTrait = hasFlag(TRAIT) && !hasFlag(PARAM)
+
+ def isDeferredOrJavaDefault = hasFlag(DEFERRED | JAVA_DEFAULTMETHOD)
+ def isDeferredNotJavaDefault = isDeferred && !hasFlag(JAVA_DEFAULTMETHOD)
def flagBitsToString(bits: Long): String = {
// Fast path for common case
diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala
index 2329b30638..56fb1181fe 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -732,7 +732,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
final def flags: Long = {
if (Statistics.hotEnabled) Statistics.incCounter(flagsCount)
val fs = _rawflags & phase.flagMask
- (fs | ((fs & LateFlags) >>> LateShift)) & ~(fs >>> AntiShift)
+ (fs | ((fs & LateFlags) >>> LateShift)) & ~((fs & AntiFlags) >>> AntiShift)
}
def flags_=(fs: Long) = _rawflags = fs
def rawflags_=(x: Long) { _rawflags = x }
diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala
index 8c32a92ecd..d0670f337a 100644
--- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala
+++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala
@@ -755,6 +755,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive
val ifaces = jclazz.getGenericInterfaces.toList map typeToScala
val isAnnotation = JavaAccFlags(jclazz).isAnnotation
if (isAnnotation) AnnotationClass.tpe :: ClassfileAnnotationClass.tpe :: ifaces
+ else if (jclazz.isInterface) ObjectTpe :: ifaces // interfaces have Object as superclass in the classfile (see jvm spec), but getGenericSuperclass seems to return null
else (if (jsuperclazz == null) AnyTpe else typeToScala(jsuperclazz)) :: ifaces
} finally {
parentsLevel -= 1
diff --git a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala
index cfd750055b..eed76c3774 100644
--- a/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala
+++ b/src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala
@@ -114,6 +114,9 @@ object ClassFileParser extends ByteCodeReader {
val methodRef = memberRef("Method")
val interfaceMethodRef = memberRef("InterfaceMethod")
val nameAndType = u2 ~ u2 ^^ add1 { case name ~ descriptor => pool => "NameAndType: " + pool(name) + ", " + pool(descriptor) }
+ val methodHandle = u1 ~ u2 ^^ add1 { case referenceKind ~ referenceIndex => pool => "MethodHandle: " + referenceKind + ", " + pool(referenceIndex) }
+ val methodType = u2 ^^ add1 { case descriptorIndex => pool => "MethodType: " + pool(descriptorIndex) }
+ val invokeDynamic = u2 ~ u2 ^^ add1 { case bootstrapMethodAttrIndex ~ nameAndTypeIndex => pool => "InvokeDynamic: " + "bootstrapMethodAttrIndex = " + bootstrapMethodAttrIndex + ", " + pool(nameAndTypeIndex) }
val constantPoolEntry = u1 >> {
case 1 => utf8String
@@ -127,6 +130,9 @@ object ClassFileParser extends ByteCodeReader {
case 10 => methodRef
case 11 => interfaceMethodRef
case 12 => nameAndType
+ case 15 => methodHandle
+ case 16 => methodType
+ case 18 => invokeDynamic
}
val interfaces = u2 >> u2.times