summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml5
-rw-r--r--Gemfile2
-rw-r--r--build.sbt2
-rwxr-xr-xbuild.xml4
-rw-r--r--doc/LICENSE.md4
-rw-r--r--doc/License.rtf4
-rw-r--r--spec/README.md2
-rw-r--r--spec/_config.yml2
-rw-r--r--src/compiler/scala/tools/ant/Scalac.scala2
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/AsmUtils.scala18
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeAsmCommon.scala34
-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.scala8
-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.scala4
-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/backend/jvm/opt/LocalOpt.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala2
-rw-r--r--src/compiler/scala/tools/nsc/javac/JavaParsers.scala16
-rw-r--r--src/compiler/scala/tools/nsc/plugins/Plugin.scala4
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala7
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala6
-rw-r--r--src/compiler/scala/tools/nsc/transform/LazyVals.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/Mixin.scala4
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Contexts.scala6
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Implicits.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Infer.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Namers.scala4
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala8
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/Typers.scala2
-rw-r--r--src/intellij/interactive.iml.SAMPLE1
-rw-r--r--src/intellij/repl.iml.SAMPLE1
-rw-r--r--src/intellij/scaladoc.iml.SAMPLE1
-rw-r--r--src/library-aux/scala/Any.scala2
-rw-r--r--src/library/scala/collection/immutable/Range.scala2
-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.scala9
-rw-r--r--src/reflect/scala/reflect/internal/pickling/UnPickler.scala2
-rw-r--r--src/reflect/scala/reflect/internal/tpe/TypeMaps.scala2
-rw-r--r--src/reflect/scala/reflect/internal/transform/Erasure.scala2
-rw-r--r--src/reflect/scala/reflect/runtime/JavaMirrors.scala1
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala3
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/model/CommentFactory.scala2
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/model/diagram/Diagram.scala2
-rw-r--r--src/scalap/decoder.properties2
-rw-r--r--src/scalap/scala/tools/scalap/scalax/rules/scalasig/ClassFileParser.scala6
-rw-r--r--test/files/filters1
-rw-r--r--test/files/jvm/innerClassAttribute/Test.scala4
-rw-r--r--test/files/neg/t6013/Base.java2
-rw-r--r--test/files/neg/t7214neg.scala2
-rw-r--r--test/files/neg/t9401.check4
-rw-r--r--test/files/neg/t9401.scala4
-rw-r--r--test/files/pos/t6089b.scala2
-rw-r--r--test/files/pos/t7689.scala2
-rw-r--r--test/files/pos/t9370/ThePlugin.scala31
-rw-r--r--test/files/pos/t9370/sample_2.flags1
-rw-r--r--test/files/pos/t9370/sample_2.scala6
-rw-r--r--test/files/pos/t9370/scalac-plugin.xml5
-rw-r--r--test/files/pos/t9392/client_2.scala4
-rw-r--r--test/files/pos/t9392/macro_1.scala16
-rw-r--r--test/files/pos/t9393/NamedImpl_1.java15
-rw-r--r--test/files/pos/t9393/NamedImpl_2.java15
-rw-r--r--test/files/pos/t9393/Named_1.java3
-rw-r--r--test/files/pos/t9393/Named_2.java3
-rw-r--r--test/files/pos/t9393/test_2.scala4
-rw-r--r--test/files/run/scalapInvokedynamic.check5
-rw-r--r--test/files/run/scalapInvokedynamic.scala11
-rwxr-xr-xtest/files/run/t2127.scala2
-rw-r--r--test/files/run/t7214.scala2
-rw-r--r--test/files/run/t8502b.scala46
-rw-r--r--test/files/run/t9387.scala20
-rw-r--r--test/files/run/t9387b.check1
-rw-r--r--test/files/run/t9387b.scala16
-rw-r--r--test/files/run/t9403.flags1
-rw-r--r--test/files/run/t9403/C_1.scala5
-rw-r--r--test/files/run/t9403/Test_2.scala29
-rw-r--r--test/files/run/test-cpp.scala2
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala42
-rw-r--r--test/junit/scala/tools/nsc/doc/html/HtmlDocletTest.scala2
-rw-r--r--test/junit/scala/tools/nsc/symtab/FlagsTest.scala89
-rw-r--r--test/scaladoc/filters1
95 files changed, 1154 insertions, 490 deletions
diff --git a/.travis.yml b/.travis.yml
index 6a7ac45e3d..236e002a5e 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,6 @@
+# opt-in to Travis's newer/faster container-based infrastructure
+sudo: false
+
# this builds the spec using jekyll
# based on http://www.paperplanes.de/2013/8/13/deploying-your-jekyll-blog-to-s3-with-travis-ci.html
language: ruby
@@ -17,4 +20,4 @@ after_success:
- openssl aes-256-cbc -pass "pass:$PRIV_KEY_SECRET" -in spec/id_dsa_travis.enc -out spec/id_dsa_travis -d -a
- chmod 600 spec/id_dsa_travis
- eval "$(ssh-agent)"
- - '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && ssh-add -D && ssh-add spec/id_dsa_travis && rsync -e "ssh -o StrictHostKeyChecking=no" -rzv build/spec/ scalatest@chara.epfl.ch:/home/linuxsoft/archives/scala/spec/2.11/' \ No newline at end of file
+ - '[ "${TRAVIS_PULL_REQUEST}" = "false" ] && ssh-add -D && ssh-add spec/id_dsa_travis && rsync -e "ssh -o StrictHostKeyChecking=no" -rzv build/spec/ scalatest@chara.epfl.ch:/home/linuxsoft/archives/scala/spec/2.11/'
diff --git a/Gemfile b/Gemfile
index b74696e550..c8c926dfde 100644
--- a/Gemfile
+++ b/Gemfile
@@ -4,4 +4,4 @@ source "https://rubygems.org"
gem "jekyll", "2.5.3"
gem "rouge"
# gem 's3_website'
-gem "redcarpet", "3.2.3"
+gem "redcarpet", "3.3.2"
diff --git a/build.sbt b/build.sbt
index 1e3bef2541..7690df5430 100644
--- a/build.sbt
+++ b/build.sbt
@@ -131,7 +131,7 @@ lazy val setJarLocation: Setting[_] =
lazy val scalaSubprojectSettings: Seq[Setting[_]] = commonSettings :+ setJarLocation
lazy val generatePropertiesFileSettings = Seq[Setting[_]](
- copyrightString := "Copyright 2002-2013, LAMP/EPFL",
+ copyrightString := "Copyright 2002-2015, LAMP/EPFL",
resourceGenerators in Compile += generateVersionPropertiesFile.map(file => Seq(file)).taskValue,
generateVersionPropertiesFile := generateVersionPropertiesFileImpl.value
)
diff --git a/build.xml b/build.xml
index 589e1931b8..ed4f3114d7 100755
--- a/build.xml
+++ b/build.xml
@@ -184,7 +184,7 @@ TODO:
<property name="dists.dir" value="${basedir}/dists"/>
- <property name="copyright.string" value="Copyright 2002-2013, LAMP/EPFL"/>
+ <property name="copyright.string" value="Copyright 2002-2015, LAMP/EPFL"/>
<!-- These are NOT the flags used to run SuperSabbus, but the ones written
into the script runners created with scala.tools.ant.ScalaTool -->
@@ -1196,7 +1196,7 @@ TODO:
</target>
<target name="quick.scaladoc" depends="quick.comp">
- <staged-build with="locker" stage="quick" project="scaladoc" version="scaladoc"/> </target>
+ <staged-build with="locker" stage="quick" project="scaladoc"/> </target>
<target name="quick.interactive" depends="quick.comp, quick.scaladoc">
<staged-build with="locker" stage="quick" project="interactive"/> </target>
diff --git a/doc/LICENSE.md b/doc/LICENSE.md
index 6b039afd68..55e82f64ba 100644
--- a/doc/LICENSE.md
+++ b/doc/LICENSE.md
@@ -2,9 +2,9 @@ Scala is licensed under the [BSD 3-Clause License](http://opensource.org/license
## Scala License
-Copyright (c) 2002-2013 EPFL
+Copyright (c) 2002-2015 EPFL
-Copyright (c) 2011-2013 Typesafe, Inc.
+Copyright (c) 2011-2015 Typesafe, Inc.
All rights reserved.
diff --git a/doc/License.rtf b/doc/License.rtf
index 62ec2d023c..c475bda3ef 100644
--- a/doc/License.rtf
+++ b/doc/License.rtf
@@ -10,8 +10,8 @@
\fs48 Scala License
\fs40 \
-\fs26 Copyright (c) 2002-2013 EPFL\
-Copyright (c) 2011-2013 Typesafe, Inc.\
+\fs26 Copyright (c) 2002-2015 EPFL\
+Copyright (c) 2011-2015 Typesafe, Inc.\
All rights reserved.\
\
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:\
diff --git a/spec/README.md b/spec/README.md
index 2f582dec5c..1a201fc97c 100644
--- a/spec/README.md
+++ b/spec/README.md
@@ -8,7 +8,7 @@ Third, we'd like to support different output formats. An html page per chapter w
## Editing
-We use redcarpet 3.1 and jekyll 2 to generate the html. Essentially, this is what github pages use.
+We use Jekyll 2 and [Redcarpet](https://github.com/vmg/redcarpet) to generate the html. Essentially, this is what github pages use.
## Building
diff --git a/spec/_config.yml b/spec/_config.yml
index 1052ddedb0..74ec602f8f 100644
--- a/spec/_config.yml
+++ b/spec/_config.yml
@@ -6,5 +6,3 @@ markdown: redcarpet
encoding: utf-8
redcarpet:
extensions: ["no_intra_emphasis", "fenced_code_blocks", "autolink", "tables", "with_toc_data", "strikethrough", "lax_spacing", "space_after_headers", "superscript", "footnotes"]
-# with_toc_data requires redcarpet 3.1 to get
-# pretty ID attributes for Hn headers (https://github.com/vmg/redcarpet/pull/186)
diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala
index 13bf0ef4c6..f46f014096 100644
--- a/src/compiler/scala/tools/ant/Scalac.scala
+++ b/src/compiler/scala/tools/ant/Scalac.scala
@@ -131,7 +131,7 @@ class Scalac extends ScalaMatchingTask with ScalacShared {
/** The character encoding of the files to compile. */
protected var encoding: Option[String] = None
- // the targetted backend
+ // the targeted backend
protected var backend: Option[String] = None
/** Whether to force compilation of all files or not. */
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 dec5adc9aa..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
)
}
@@ -289,7 +292,7 @@ final class BCodeAsmCommon[G <: Global](val global: G) {
lazy val AnnotationRetentionPolicyRuntimeValue = AnnotationRetentionPolicyModule.tpe.member(TermName("RUNTIME"))
/** Whether an annotation should be emitted as a Java annotation
- * .initialize: if 'annot' is read from pickle, atp might be un-initialized
+ * .initialize: if 'annot' is read from pickle, atp might be uninitialized
*/
def shouldEmitAnnotation(annot: AnnotationInfo) = {
annot.symbol.initialize.isJavaDefined &&
@@ -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 d2d510e8a9..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
@@ -443,7 +443,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
* which rethrows the caught exception once it's done with the cleanup code.
*
* A particular cleanup may in general contain LabelDefs. Care is needed when duplicating such jump-targets,
- * so as to preserve agreement wit the (also duplicated) jump-sources.
+ * so as to preserve agreement with the (also duplicated) jump-sources.
* This is achieved based on the bookkeeping provided by two maps:
* - `labelDefsAtOrUnder` lists all LabelDefs enclosed by a given Tree node (the key)
* - `labelDef` provides the LabelDef node whose symbol is used as key.
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 71686fd9d7..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
)
@@ -3031,7 +3031,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters { self =>
*
* Rationale for this normalization:
* test/files/run/private-inline.scala after -optimize is chock full of
- * BasicBlocks containing just JUMP(whereTo), where no exception handler straddles them.
+ * BasicBlocks containing just JUMP(whereto), where no exception handler straddles them.
* They should be collapsed by IMethod.normalize() but aren't.
* That was fine in FJBG times when by the time the exception table was emitted,
* it already contained "anchored" labels (ie instruction offsets were known)
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/backend/jvm/opt/LocalOpt.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
index bd5bab28b5..4132710a96 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
@@ -31,7 +31,7 @@ import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._
* catch block, and the recursive invocation is not necessary.
*
* simplify jumps
- * - various simplifications, see doc domments of individual optimizations
+ * - various simplifications, see doc comments of individual optimizations
* + changing or eliminating jumps may render some code unreachable, therefore "simplify jumps" is
* executed in a loop with "unreachable code"
*
@@ -495,7 +495,7 @@ object LocalOptImpls {
* Replace jumps to a sequence of GOTO instructions by a jump to the final destination.
*
* Jump l; [any ops]; l: GOTO m; [any ops]; m: GOTO n; [any ops]; n: NotGOTO; [...]
- * => Jump n; [rest unchaned]
+ * => Jump n; [rest unchanged]
*
* If there's a loop of GOTOs, the initial jump is replaced by one of the labels in the loop.
*/
diff --git a/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala
index 425c10d153..9f6883f03f 100644
--- a/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala
+++ b/src/compiler/scala/tools/nsc/backend/opt/InlineExceptionHandlers.scala
@@ -343,7 +343,7 @@ abstract class InlineExceptionHandlers extends SubComponent {
/**
* This function takes care of duplicating the basic block code for inlining the handler
*
- * Note: This function does not duplicate the same basic block twice. It wil contain a map of the duplicated
+ * Note: This function does not duplicate the same basic block twice. It will contain a map of the duplicated
* basic blocks
*/
private def duplicateExceptionHandlerCache(handler: BasicBlock) =
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/plugins/Plugin.scala b/src/compiler/scala/tools/nsc/plugins/Plugin.scala
index 1a5529140c..dd17750cd4 100644
--- a/src/compiler/scala/tools/nsc/plugins/Plugin.scala
+++ b/src/compiler/scala/tools/nsc/plugins/Plugin.scala
@@ -158,8 +158,8 @@ object Plugin {
def loop(qs: List[Path]): Try[PluginDescription] = qs match {
case Nil => Failure(new MissingPluginException(ps))
case p :: rest =>
- if (p.isDirectory) loadDescriptionFromFile(p.toDirectory / PluginXML)
- else if (p.isFile) loadDescriptionFromJar(p.toFile)
+ if (p.isDirectory) loadDescriptionFromFile(p.toDirectory / PluginXML) orElse loop(rest)
+ else if (p.isFile) loadDescriptionFromJar(p.toFile) orElse loop(rest)
else loop(rest)
}
loop(ps)
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
index 660028eab8..99e61d2482 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
@@ -84,6 +84,9 @@ abstract class ClassfileParser {
protected final def u2(): Int = in.nextChar.toInt
protected final def u4(): Int = in.nextInt
+ protected final def s1(): Int = in.nextByte.toInt // sign-extend the byte to int
+ protected final def s2(): Int = (in.nextByte.toInt << 8) | u1 // sign-extend and shift the first byte, or with the unsigned second byte
+
private def readInnerClassFlags() = readClassFlags()
private def readClassFlags() = JavaAccFlags classFlags u2
private def readMethodFlags() = JavaAccFlags methodFlags u2
@@ -284,7 +287,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 +865,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/symtab/classfile/ICodeReader.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
index 438a71061e..b2f5a4119d 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ICodeReader.scala
@@ -326,8 +326,8 @@ abstract class ICodeReader extends ClassfileParser {
case JVM.dconst_0 => code emit CONSTANT(Constant(0.0))
case JVM.dconst_1 => code emit CONSTANT(Constant(1.0))
- case JVM.bipush => code.emit(CONSTANT(Constant(u1))); size += 1
- case JVM.sipush => code.emit(CONSTANT(Constant(u2))); size += 2
+ case JVM.bipush => code.emit(CONSTANT(Constant(s1))); size += 1
+ case JVM.sipush => code.emit(CONSTANT(Constant(s2))); size += 2
case JVM.ldc => code.emit(CONSTANT(pool.getConstant(u1))); size += 1
case JVM.ldc_w => code.emit(CONSTANT(pool.getConstant(u2))); size += 2
case JVM.ldc2_w => code.emit(CONSTANT(pool.getConstant(u2))); size += 2
@@ -466,7 +466,7 @@ abstract class ICodeReader extends ClassfileParser {
size += 2
val local = code.getLocal(u1, INT)
code.emit(LOAD_LOCAL(local))
- code.emit(CONSTANT(Constant(u1)))
+ code.emit(CONSTANT(Constant(s1)))
code.emit(CALL_PRIMITIVE(Arithmetic(ADD, INT)))
code.emit(STORE_LOCAL(local))
diff --git a/src/compiler/scala/tools/nsc/transform/LazyVals.scala b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
index df622d4d1d..b6695efb0b 100644
--- a/src/compiler/scala/tools/nsc/transform/LazyVals.scala
+++ b/src/compiler/scala/tools/nsc/transform/LazyVals.scala
@@ -168,7 +168,7 @@ abstract class LazyVals extends Transform with TypingTransformers with ast.TreeD
/** Add the bitmap definitions to the rhs of a method definition.
* If the rhs has been tail-call transformed, insert the bitmap
* definitions inside the top-level label definition, so that each
- * iteration has the lazy values un-initialized. Otherwise add them
+ * iteration has the lazy values uninitialized. Otherwise add them
* at the very beginning of the method.
*/
private def addBitmapDefs(methSym: Symbol, rhs: Tree): Tree = {
diff --git a/src/compiler/scala/tools/nsc/transform/Mixin.scala b/src/compiler/scala/tools/nsc/transform/Mixin.scala
index 11f9483f77..25d45cc819 100644
--- a/src/compiler/scala/tools/nsc/transform/Mixin.scala
+++ b/src/compiler/scala/tools/nsc/transform/Mixin.scala
@@ -79,9 +79,9 @@ abstract class Mixin extends InfoTransform with ast.TreeDSL {
/** Does this field require an initialized bit?
* Note: fields of classes inheriting DelayedInit are not checked.
- * This is because the they are neither initialized in the constructor
+ * This is because they are neither initialized in the constructor
* nor do they have a setter (not if they are vals anyway). The usual
- * logic for setting bitmaps does therefor not work for such fields.
+ * logic for setting bitmaps does therefore not work for such fields.
* That's why they are excluded.
* Note: The `checkinit` option does not check if transient fields are initialized.
*/
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index b310e6c3a1..7a9dfda43e 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -433,7 +433,7 @@ abstract class UnCurry extends InfoTransform
val sym = tree.symbol
- // true if the taget is a lambda body that's been lifted into a method
+ // true if the target is a lambda body that's been lifted into a method
def isLiftedLambdaBody(target: Tree) = target.symbol.isLocalToBlock && target.symbol.isArtifact && target.symbol.name.containsName(nme.ANON_FUN_NAME)
val result = (
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala
index 06b39b035a..1642613b9b 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchCodeGen.scala
@@ -134,7 +134,7 @@ trait MatchCodeGen extends Interface {
trait OptimizedCodegen extends CodegenCore with TypedSubstitution with MatchMonadInterface {
override def codegen: AbsCodegen = optimizedCodegen
- // when we know we're targetting Option, do some inlining the optimizer won't do
+ // when we know we're targeting Option, do some inlining the optimizer won't do
// for example, `o.flatMap(f)` becomes `if(o == None) None else f(o.get)`, similarly for orElse and guard
// this is a special instance of the advanced inlining optimization that takes a method call on
// an object of a type that only has two concrete subclasses, and inlines both bodies, guarded by an if to distinguish the two cases
diff --git a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala
index b3aef8a20e..cca8d2dbb8 100644
--- a/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala
+++ b/src/compiler/scala/tools/nsc/transform/patmat/MatchOptimization.scala
@@ -286,8 +286,8 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis {
else Apply(Ident(defaultLabel), Nil)
val guardedBody = same.foldRight(jumpToDefault){
- // the last case may be un-guarded (we know it's the last one since fold's accum == jumpToDefault)
- // --> replace jumpToDefault by the un-guarded case's body
+ // the last case may be unguarded (we know it's the last one since fold's accum == jumpToDefault)
+ // --> replace jumpToDefault by the unguarded case's body
case (CaseDef(_, EmptyTree, b), `jumpToDefault`) => b
case (cd@CaseDef(_, g, b), els) if isGuardedCase(cd) => If(g, b, els)
}
@@ -322,7 +322,7 @@ trait MatchOptimization extends MatchTreeMaking with MatchAnalysis {
var remainingCases = cases
val collapsed = scala.collection.mutable.ListBuffer.empty[CaseDef]
- // when some of collapsed cases (except for the default case itself) did not include an un-guarded case
+ // when some of collapsed cases (except for the default case itself) did not include an unguarded case
// we'll need to emit a labeldef for the default case
var needDefault = false
diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
index a7ef5d5d2f..43f2655311 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala
@@ -168,13 +168,13 @@ trait Contexts { self: Analyzer =>
* fine grained control is needed based on the kind of error; ambiguity errors are often
* suppressed during exploratory typing, such as determining whether `a == b` in an argument
* position is an assignment or a named argument, when `Inferencer#isApplicableSafe` type checks
- * applications with and without an expected type, or whtn `Typer#tryTypedApply` tries to fit arguments to
+ * applications with and without an expected type, or when `Typer#tryTypedApply` tries to fit arguments to
* a function type with/without implicit views.
*
- * When the error policies entails error/warning buffering, the mutable [[ReportBuffer]] records
+ * When the error policies entail error/warning buffering, the mutable [[ReportBuffer]] records
* everything that is issued. It is important to note, that child Contexts created with `make`
* "inherit" the very same `ReportBuffer` instance, whereas children spawned through `makeSilent`
- * receive an separate, fresh buffer.
+ * receive a separate, fresh buffer.
*
* @param tree Tree associated with this context
* @param owner The current owner
diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
index 11984da0d7..7ec9cd74a4 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala
@@ -1361,7 +1361,7 @@ trait Implicits {
val succstart = if (stats) Statistics.startTimer(oftypeSucceedNanos) else null
// SI-6667, never search companions after an ambiguous error in in-scope implicits
- val wasAmbigious = result.isAmbiguousFailure
+ val wasAmbiguous = result.isAmbiguousFailure
// TODO: encapsulate
val previousErrs = context.reporter.errors
@@ -1371,7 +1371,7 @@ trait Implicits {
// `materializeImplicit` does some preprocessing for `pt`
// is it only meant for manifests/tags or we need to do the same for `implicitsOfExpectedType`?
- if (result.isFailure && !wasAmbigious)
+ if (result.isFailure && !wasAmbiguous)
result = searchImplicit(implicitsOfExpectedType, isLocalToCallsite = false)
if (result.isFailure)
diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
index ea0a9bb243..ab9fa26bac 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala
@@ -1117,7 +1117,7 @@ trait Infer extends Checkable {
// this is quite nasty: it destructively changes the info of the syms of e.g., method type params
// (see #3692, where the type param T's bounds were set to > : T <: T, so that parts looped)
- // the changes are rolled back by restoreTypeBounds, but might be unintentially observed in the mean time
+ // the changes are rolled back by restoreTypeBounds, but might be unintentionally observed in the mean time
def instantiateTypeVar(tvar: TypeVar) {
val tparam = tvar.origin.typeSymbol
val TypeBounds(lo0, hi0) = tparam.info.bounds
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 4b30b4e436..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)
@@ -1134,13 +1134,13 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
t hasSymbolWhich (_.accessedOrSelf == valOrDef.symbol)
case _ => false
}
- val trivialInifiniteLoop = (
+ val trivialInfiniteLoop = (
!valOrDef.isErroneous
&& !valOrDef.symbol.isValueParameter
&& valOrDef.symbol.paramss.isEmpty
&& callsSelf
)
- if (trivialInifiniteLoop)
+ if (trivialInfiniteLoop)
reporter.warning(valOrDef.rhs.pos, s"${valOrDef.symbol.fullLocationString} does nothing other than call itself recursively")
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
index b5129af9ec..fd1a6f293f 100644
--- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala
@@ -4577,7 +4577,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
typed1(atPos(tree.pos)(Block(stats, Apply(expr, args) setPos tree.pos.makeTransparent)), mode, pt)
case Apply(fun, args) =>
normalTypedApply(tree, fun, args) match {
- case ArrayInstantiation(tree1) => typed(tree1, mode, pt)
+ case ArrayInstantiation(tree1) => if (tree1.isErrorTyped) tree1 else typed(tree1, mode, pt)
case Apply(Select(fun, nme.apply), _) if treeInfo.isSuperConstrCall(fun) => TooManyArgumentListsForConstructor(tree) //SI-5696
case tree1 => tree1
}
diff --git a/src/intellij/interactive.iml.SAMPLE b/src/intellij/interactive.iml.SAMPLE
index 047b5c9069..267bd3f12b 100644
--- a/src/intellij/interactive.iml.SAMPLE
+++ b/src/intellij/interactive.iml.SAMPLE
@@ -11,5 +11,6 @@
<orderEntry type="module" module-name="library" />
<orderEntry type="module" module-name="reflect" />
<orderEntry type="library" name="starr" level="project" />
+ <orderEntry type="library" name="asm" level="project" />
</component>
</module> \ No newline at end of file
diff --git a/src/intellij/repl.iml.SAMPLE b/src/intellij/repl.iml.SAMPLE
index 7476f30131..e827a2c6d7 100644
--- a/src/intellij/repl.iml.SAMPLE
+++ b/src/intellij/repl.iml.SAMPLE
@@ -10,6 +10,7 @@
<orderEntry type="module" module-name="compiler" />
<orderEntry type="module" module-name="library" />
<orderEntry type="module" module-name="reflect" />
+ <orderEntry type="module" module-name="interactive" />
<orderEntry type="library" name="starr" level="project" />
<orderEntry type="library" name="repl-deps" level="project" />
<orderEntry type="library" name="asm" level="project" />
diff --git a/src/intellij/scaladoc.iml.SAMPLE b/src/intellij/scaladoc.iml.SAMPLE
index 4ba0a848c6..6e6d98b396 100644
--- a/src/intellij/scaladoc.iml.SAMPLE
+++ b/src/intellij/scaladoc.iml.SAMPLE
@@ -14,5 +14,6 @@
<orderEntry type="library" name="scaladoc-deps" level="project" />
<orderEntry type="library" name="ant" level="project" />
<orderEntry type="library" name="partest" level="project" />
+ <orderEntry type="library" name="asm" level="project" />
</component>
</module> \ No newline at end of file
diff --git a/src/library-aux/scala/Any.scala b/src/library-aux/scala/Any.scala
index 1c25989c30..1be186d114 100644
--- a/src/library-aux/scala/Any.scala
+++ b/src/library-aux/scala/Any.scala
@@ -38,7 +38,7 @@ abstract class Any {
* - It is reflexive: for any instance `x` of type `Any`, `x.equals(x)` should return `true`.
* - It is symmetric: for any instances `x` and `y` of type `Any`, `x.equals(y)` should return `true` if and
* only if `y.equals(x)` returns `true`.
- * - It is transitive: for any instances `x`, `y`, and `z` of type `AnyRef` if `x.equals(y)` returns `true` and
+ * - It is transitive: for any instances `x`, `y`, and `z` of type `Any` if `x.equals(y)` returns `true` and
* `y.equals(z)` returns `true`, then `x.equals(z)` should return `true`.
*
* If you override this method, you should verify that your implementation remains an equivalence relation.
diff --git a/src/library/scala/collection/immutable/Range.scala b/src/library/scala/collection/immutable/Range.scala
index 3ae8a2c342..79cd673932 100644
--- a/src/library/scala/collection/immutable/Range.scala
+++ b/src/library/scala/collection/immutable/Range.scala
@@ -285,7 +285,7 @@ extends scala.collection.AbstractSeq[Int]
*/
final override def splitAt(n: Int) = (take(n), drop(n))
- /** Creates a new range consisting of the `length - n` last elements of the range.
+ /** Creates a new range consisting of the last `n` elements of the range.
*
* $doesNotUseBuilders
*/
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 285d59c5e2..56fb1181fe 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -495,8 +495,8 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
* failure to the point when that name is used for something, which is
* often to the point of never.
*/
- def newStubSymbol(name: Name, missingMessage: String): Symbol = name match {
- case n: TypeName => new StubClassSymbol(this, n, missingMessage)
+ def newStubSymbol(name: Name, missingMessage: String, isPackage: Boolean = false): Symbol = name match {
+ case n: TypeName => if (isPackage) new StubPackageClassSymbol(this, n, missingMessage) else new StubClassSymbol(this, n, missingMessage)
case _ => new StubTermSymbol(this, name.toTermName, missingMessage)
}
@@ -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 }
@@ -1153,7 +1153,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
* phase check (if after flatten) in the (overridden) method "def owner" in
* ModuleSymbol / ClassSymbol. The `rawowner` field is not modified.
* - Owners are also changed in other situations, for example when moving trees into a new
- * lexical context, e.g. in the named/default arguments tranformation, or when translating
+ * lexical context, e.g. in the named/default arguments transformation, or when translating
* extension method definitions.
*
* In general when seeking the owner of a symbol, one should call `owner`.
@@ -3469,6 +3469,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
override def companionSymbol = fail(NoSymbol)
}
class StubClassSymbol(owner0: Symbol, name0: TypeName, val missingMessage: String) extends ClassSymbol(owner0, owner0.pos, name0) with StubSymbol
+ class StubPackageClassSymbol(owner0: Symbol, name0: TypeName, val missingMessage: String) extends PackageClassSymbol(owner0, owner0.pos, name0) with StubSymbol
class StubTermSymbol(owner0: Symbol, name0: TermName, val missingMessage: String) extends TermSymbol(owner0, owner0.pos, name0) with StubSymbol
trait FreeSymbol extends Symbol {
diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala
index 1fc7aebab0..1f643b2b23 100644
--- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala
+++ b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala
@@ -398,7 +398,7 @@ abstract class UnPickler {
val sym = readSymbolRef() match {
case stub: StubSymbol if !stub.isClass =>
// SI-8502 This allows us to create a stub for a unpickled reference to `missingPackage.Foo`.
- stub.owner.newStubSymbol(stub.name.toTypeName, stub.missingMessage)
+ stub.owner.newStubSymbol(stub.name.toTypeName, stub.missingMessage, isPackage = true)
case sym => sym
}
ThisType(sym)
diff --git a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala
index 15a87200f1..817c9706b4 100644
--- a/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala
+++ b/src/reflect/scala/reflect/internal/tpe/TypeMaps.scala
@@ -736,7 +736,7 @@ private[internal] trait TypeMaps {
substFor(sym)
case ClassInfoType(parents, decls, sym) =>
val parents1 = parents mapConserve this
- // We don't touch decls here; they will be touched when an enclosing TreeSubstitutor
+ // We don't touch decls here; they will be touched when an enclosing TreeSubstituter
// transforms the tree that defines them.
if (parents1 eq parents) tp
else ClassInfoType(parents1, decls, sym)
diff --git a/src/reflect/scala/reflect/internal/transform/Erasure.scala b/src/reflect/scala/reflect/internal/transform/Erasure.scala
index 707972242a..01e28e5642 100644
--- a/src/reflect/scala/reflect/internal/transform/Erasure.scala
+++ b/src/reflect/scala/reflect/internal/transform/Erasure.scala
@@ -90,7 +90,7 @@ trait Erasure {
}
}
- /** Does this vakue class have an underlying type that's a type parameter of
+ /** Does this value class have an underlying type that's a type parameter of
* the class itself?
* This method needs to be called at a phase no later than erasurephase
*/
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/scaladoc/scala/tools/nsc/doc/html/page/Template.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala
index 81036b4908..b5375558ac 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala
@@ -280,7 +280,7 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
{
if (Set("epfl", "EPFL").contains(tpl.universe.settings.docfooter.value))
- <div id="footer">Scala programming documentation. Copyright (c) 2003-2013 <a href="http://www.epfl.ch" target="_top">EPFL</a>, with contributions from <a href="http://typesafe.com" target="_top">Typesafe</a>.</div>
+ <div id="footer">Scala programming documentation. Copyright (c) 2003-2015 <a href="http://www.epfl.ch" target="_top">EPFL</a>, with contributions from <a href="http://typesafe.com" target="_top">Typesafe</a>.</div>
else
<div id="footer"> { tpl.universe.settings.docfooter.value } </div>
}
@@ -676,7 +676,6 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
if (diagramSvg != NodeSeq.Empty) {
<div class="toggleContainer block diagram-container" id={ id + "-container"}>
<span class="toggle diagram-link">{ description }</span>
- <a href="http://docs.scala-lang.org/overviews/scaladoc/usage.html#diagrams" target="_blank" class="diagram-help">Learn more about scaladoc diagrams</a>
<div class="diagram" id={ id }>{
diagramSvg
}</div>
diff --git a/src/scaladoc/scala/tools/nsc/doc/model/CommentFactory.scala b/src/scaladoc/scala/tools/nsc/doc/model/CommentFactory.scala
index fe157c1cc9..66ce2137f2 100644
--- a/src/scaladoc/scala/tools/nsc/doc/model/CommentFactory.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/model/CommentFactory.scala
@@ -31,7 +31,7 @@ trait CommentFactory extends base.CommentFactoryBase {
defineComment(sym, linkTarget, inTpl)
})
- /** A comment is usualy created by the parser, however for some special
+ /** A comment is usually created by the parser, however for some special
* cases we have to give some `inTpl` comments (parent class for example)
* to the comment of the symbol.
* This function manages some of those cases : Param accessor and Primary constructor */
diff --git a/src/scaladoc/scala/tools/nsc/doc/model/diagram/Diagram.scala b/src/scaladoc/scala/tools/nsc/doc/model/diagram/Diagram.scala
index 1846f375cd..e15963bda9 100644
--- a/src/scaladoc/scala/tools/nsc/doc/model/diagram/Diagram.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/model/diagram/Diagram.scala
@@ -99,7 +99,7 @@ case class NormalNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip:
/** A class or trait the thisnode can be converted to by an implicit conversion
* TODO: I think it makes more sense to use the tpe links to templates instead of the TemplateEntity for implicit nodes
- * since some implicit conversions convert the class to complex types that cannot be represented as a single tmeplate
+ * since some implicit conversions convert the class to complex types that cannot be represented as a single template
*/
case class ImplicitNode(tpe: TypeEntity, tpl: Option[TemplateEntity])(val tooltip: Option[String] = None) extends Node { override def isImplicitNode = true }
diff --git a/src/scalap/decoder.properties b/src/scalap/decoder.properties
index 961c60f48c..333f6ce715 100644
--- a/src/scalap/decoder.properties
+++ b/src/scalap/decoder.properties
@@ -1,2 +1,2 @@
version.number=2.0.1
-copyright.string=(c) 2002-2013 LAMP/EPFL
+copyright.string=(c) 2002-2015 LAMP/EPFL
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
diff --git a/test/files/filters b/test/files/filters
index 51a7507848..e91ca0eb36 100644
--- a/test/files/filters
+++ b/test/files/filters
@@ -1,6 +1,7 @@
#
#Java HotSpot(TM) 64-Bit Server VM warning: Failed to reserve shared memory (errno = 28).
Java HotSpot\(TM\) .* warning:
+OpenJDK .* warning:
# Hotspot receiving VM options through the $_JAVA_OPTIONS
# env variable outputs them on stderr
Picked up _JAVA_OPTIONS:
diff --git a/test/files/jvm/innerClassAttribute/Test.scala b/test/files/jvm/innerClassAttribute/Test.scala
index 376b3c895b..3a6737ca46 100644
--- a/test/files/jvm/innerClassAttribute/Test.scala
+++ b/test/files/jvm/innerClassAttribute/Test.scala
@@ -149,9 +149,7 @@ object Test extends BytecodeTest {
def testA11() = {
val List(ann) = innerClassNodes("A11")
- // in the java class file, the INNERCLASS attribute has more flags (public | static | abstract | interface | annotation)
- // the scala compiler has its own interpretation of java annotations ant their flags.. it only emits publicStatic.
- assertMember(ann, "JavaAnnot_1", "Ann", flags = publicStatic)
+ assertMember(ann, "JavaAnnot_1", "Ann", flags = publicAbstractInterface | Flags.ACC_STATIC | Flags.ACC_ANNOTATION)
}
def testA13() = {
diff --git a/test/files/neg/t6013/Base.java b/test/files/neg/t6013/Base.java
index b73d7fd821..ce6ee47e64 100644
--- a/test/files/neg/t6013/Base.java
+++ b/test/files/neg/t6013/Base.java
@@ -2,7 +2,7 @@ abstract public class Base {
// This must considered to be overridden by Abstract#foo based
// on the erased signatures. This special case is handled by
// `javaErasedOverridingSym` in `RefChecks`.
- public abstract void bar(java.util.List<java.lang.String> foo) { return; }
+ public void bar(java.util.List<java.lang.String> foo) { return; }
// But, a concrete method in a Java superclass must not excuse
// a deferred method in the Java subclass!
diff --git a/test/files/neg/t7214neg.scala b/test/files/neg/t7214neg.scala
index ff1ea8082d..e2b2d908d8 100644
--- a/test/files/neg/t7214neg.scala
+++ b/test/files/neg/t7214neg.scala
@@ -1,4 +1,4 @@
-// pattern matcher crashes here trying to synthesize an uneeded outer test.
+// pattern matcher crashes here trying to synthesize an unneeded outer test.
// no-symbol does not have an owner
// at scala.reflect.internal.SymbolTable.abort(SymbolTable.scala:49)
// at scala.tools.nsc.Global.abort(Global.scala:253)
diff --git a/test/files/neg/t9401.check b/test/files/neg/t9401.check
new file mode 100644
index 0000000000..638d56db63
--- /dev/null
+++ b/test/files/neg/t9401.check
@@ -0,0 +1,4 @@
+t9401.scala:3: error: cannot find class tag for element type T
+ gencastarray = new Array[T](0)
+ ^
+one error found
diff --git a/test/files/neg/t9401.scala b/test/files/neg/t9401.scala
new file mode 100644
index 0000000000..f42ecc7f74
--- /dev/null
+++ b/test/files/neg/t9401.scala
@@ -0,0 +1,4 @@
+class Resetting[T] {
+ var gencastarray: Any = null
+ gencastarray = new Array[T](0)
+}
diff --git a/test/files/pos/t6089b.scala b/test/files/pos/t6089b.scala
index ff7ca157eb..040987413e 100644
--- a/test/files/pos/t6089b.scala
+++ b/test/files/pos/t6089b.scala
@@ -1,5 +1,5 @@
// this crazy code simply tries to nest pattern matches so that the last call is in a tricky-to-determine
-// tail position (my initial tightenign of tailpos detection for SI-6089 ruled this out)
+// tail position (my initial tightening of tailpos detection for SI-6089 ruled this out)
class BKTree {
@annotation.tailrec
final def -?-[AA](a: AA): Boolean = this match {
diff --git a/test/files/pos/t7689.scala b/test/files/pos/t7689.scala
index 022e7ab7a0..72cca99bc0 100644
--- a/test/files/pos/t7689.scala
+++ b/test/files/pos/t7689.scala
@@ -2,6 +2,6 @@ object A {
// The default getter must have an explicit return type (List[_] => Int)
// This wasn't happening since e28c3edda4. That commit encoded upper/lower
// bounds of Any/Nothing as EmptyTree, which were triggering an .isEmpty
- // check in Namers#TypeTreeSubstitutor
+ // check in Namers#TypeTreeSubstituter
def x(f: List[_] => Int = _ => 3) = 9
}
diff --git a/test/files/pos/t9370/ThePlugin.scala b/test/files/pos/t9370/ThePlugin.scala
new file mode 100644
index 0000000000..cd800781dc
--- /dev/null
+++ b/test/files/pos/t9370/ThePlugin.scala
@@ -0,0 +1,31 @@
+package scala.test.plugins
+
+import scala.tools.nsc
+import nsc.Global
+import nsc.Phase
+import nsc.plugins.Plugin
+import nsc.plugins.PluginComponent
+
+class ThePlugin(val global: Global) extends Plugin {
+ import global._
+
+ val name = "timebomb"
+ val description = "Explodes if run. Maybe I haven't implemented it yet."
+ val components = List[PluginComponent](thePhase1)
+
+ private object thePhase1 extends PluginComponent {
+ val global = ThePlugin.this.global
+
+ val runsAfter = List[String]("parser")
+ override val runsBefore = List[String]("namer")
+ val phaseName = ThePlugin.this.name
+
+ def newPhase(prev: Phase) = new ThePhase(prev)
+ }
+
+ private class ThePhase(prev: Phase) extends Phase(prev) {
+ override def name = ThePlugin.this.name
+ override def run = ???
+ }
+}
+
diff --git a/test/files/pos/t9370/sample_2.flags b/test/files/pos/t9370/sample_2.flags
new file mode 100644
index 0000000000..03baca3030
--- /dev/null
+++ b/test/files/pos/t9370/sample_2.flags
@@ -0,0 +1 @@
+-Xplugin:/tmp:. -Xplugin-require:timebomb -Ystop-after:parser
diff --git a/test/files/pos/t9370/sample_2.scala b/test/files/pos/t9370/sample_2.scala
new file mode 100644
index 0000000000..7eb11b8204
--- /dev/null
+++ b/test/files/pos/t9370/sample_2.scala
@@ -0,0 +1,6 @@
+
+package sample
+
+// just a sample that is compiled with the explosive plugin disabled
+object Sample extends App {
+}
diff --git a/test/files/pos/t9370/scalac-plugin.xml b/test/files/pos/t9370/scalac-plugin.xml
new file mode 100644
index 0000000000..2558d6fd03
--- /dev/null
+++ b/test/files/pos/t9370/scalac-plugin.xml
@@ -0,0 +1,5 @@
+<plugin>
+ <name>ignored</name>
+ <classname>scala.test.plugins.ThePlugin</classname>
+</plugin>
+
diff --git a/test/files/pos/t9392/client_2.scala b/test/files/pos/t9392/client_2.scala
new file mode 100644
index 0000000000..6b706fea12
--- /dev/null
+++ b/test/files/pos/t9392/client_2.scala
@@ -0,0 +1,4 @@
+class Client {
+ Macro()
+}
+
diff --git a/test/files/pos/t9392/macro_1.scala b/test/files/pos/t9392/macro_1.scala
new file mode 100644
index 0000000000..3f67ac17b2
--- /dev/null
+++ b/test/files/pos/t9392/macro_1.scala
@@ -0,0 +1,16 @@
+import language.experimental.macros
+
+
+object Macro {
+
+ import reflect.macros.blackbox.Context
+ def impl(c: Context)(): c.Tree = {
+ import c.universe._
+ val tree = q"""class C; new C"""
+ val tree1 = c.typecheck(tree)
+ val tpe = tree1.tpe
+ val tree2 = c.typecheck(c.untypecheck(tree1))
+ q"""$tree2.asInstanceOf[$tpe]"""
+ }
+ def apply(): Any = macro impl
+}
diff --git a/test/files/pos/t9393/NamedImpl_1.java b/test/files/pos/t9393/NamedImpl_1.java
new file mode 100644
index 0000000000..02ec9b4671
--- /dev/null
+++ b/test/files/pos/t9393/NamedImpl_1.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
+ */
+package bug;
+
+import bug.Named_1;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+
+public class NamedImpl_1 implements Named_1 {
+
+ public Class<? extends Annotation> annotationType() {
+ return null;
+ }
+}
diff --git a/test/files/pos/t9393/NamedImpl_2.java b/test/files/pos/t9393/NamedImpl_2.java
new file mode 100644
index 0000000000..c87e94016d
--- /dev/null
+++ b/test/files/pos/t9393/NamedImpl_2.java
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
+ */
+package bug;
+
+import bug.Named_2;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+
+public class NamedImpl_2 implements Named_2 {
+
+ public Class<? extends Annotation> annotationType() {
+ return null;
+ }
+}
diff --git a/test/files/pos/t9393/Named_1.java b/test/files/pos/t9393/Named_1.java
new file mode 100644
index 0000000000..30a6c9839a
--- /dev/null
+++ b/test/files/pos/t9393/Named_1.java
@@ -0,0 +1,3 @@
+package bug;
+
+public @interface Named_1 {}
diff --git a/test/files/pos/t9393/Named_2.java b/test/files/pos/t9393/Named_2.java
new file mode 100644
index 0000000000..3210fb636a
--- /dev/null
+++ b/test/files/pos/t9393/Named_2.java
@@ -0,0 +1,3 @@
+package bug;
+
+public @interface Named_2 {}
diff --git a/test/files/pos/t9393/test_2.scala b/test/files/pos/t9393/test_2.scala
new file mode 100644
index 0000000000..8ea346129d
--- /dev/null
+++ b/test/files/pos/t9393/test_2.scala
@@ -0,0 +1,4 @@
+class C {
+ new bug.NamedImpl_1 // separate compilation, testing the classfile parser
+ new bug.NamedImpl_2 // mixed compilation, testing the java source parser
+}
diff --git a/test/files/run/scalapInvokedynamic.check b/test/files/run/scalapInvokedynamic.check
new file mode 100644
index 0000000000..8e4b08f234
--- /dev/null
+++ b/test/files/run/scalapInvokedynamic.check
@@ -0,0 +1,5 @@
+class C extends scala.AnyRef {
+ def this() = { /* compiled code */ }
+ def m: java.lang.String = { /* compiled code */ }
+}
+
diff --git a/test/files/run/scalapInvokedynamic.scala b/test/files/run/scalapInvokedynamic.scala
new file mode 100644
index 0000000000..670cf26662
--- /dev/null
+++ b/test/files/run/scalapInvokedynamic.scala
@@ -0,0 +1,11 @@
+class C {
+ def m = {
+ val f = (x: String) => x.trim
+ f(" H ae i ")
+ }
+}
+
+object Test extends App {
+ val testClassesDir = System.getProperty("partest.output")
+ scala.tools.scalap.Main.main(Array("-cp", testClassesDir, "C"))
+} \ No newline at end of file
diff --git a/test/files/run/t2127.scala b/test/files/run/t2127.scala
index 869d8a38d6..839c8d6a5c 100755
--- a/test/files/run/t2127.scala
+++ b/test/files/run/t2127.scala
@@ -28,5 +28,5 @@
}
-The constructor invocation of Bar is done within the scope of object Foo's constructor, and therefor the private constructor of Foo should be visible and accessible.
+The constructor invocation of Bar is done within the scope of object Foo's constructor, and therefore the private constructor of Foo should be visible and accessible.
*/
diff --git a/test/files/run/t7214.scala b/test/files/run/t7214.scala
index 15c2c24fa0..b2ef53eeab 100644
--- a/test/files/run/t7214.scala
+++ b/test/files/run/t7214.scala
@@ -1,4 +1,4 @@
-// pattern matcher crashes here trying to synthesize an uneeded outer test.
+// pattern matcher crashes here trying to synthesize an unneeded outer test.
// no-symbol does not have an owner
// at scala.reflect.internal.SymbolTable.abort(SymbolTable.scala:49)
// at scala.tools.nsc.Global.abort(Global.scala:253)
diff --git a/test/files/run/t8502b.scala b/test/files/run/t8502b.scala
new file mode 100644
index 0000000000..4f70d13bb0
--- /dev/null
+++ b/test/files/run/t8502b.scala
@@ -0,0 +1,46 @@
+import scala.tools.partest._
+import java.io.File
+
+// used to crash with an assertion failure in flatten because the type symbol created for the missing
+// package was a ClassSymbol, not a PackageClassSymbol
+// - isFlattenablePrefix(vanishingPackage) was true (wrongly)
+// - therefore flatten tried to flatten the class defined in the package, but the class is
+// top-level, vanishingClass.enclosingTopLevelClass is NoSymbol
+object Test extends StoreReporterDirectTest {
+ def code = ???
+
+ def compileCode(code: String) = {
+ val classpath = List(sys.props("partest.lib"), testOutput.path) mkString sys.props("path.separator")
+ compileString(newCompiler("-cp", classpath, "-d", testOutput.path))(code)
+ }
+
+ def show(): Unit = {
+ compileCode("""
+ class Outer {
+ class Nested extends vanishing.Vanishing
+ }
+
+ package vanishing {
+ class Vanishing
+ }
+ """)
+ assert(filteredInfos.isEmpty, filteredInfos)
+ deletePackage("vanishing")
+ compileCode("""
+ class Test {
+ def f(o: Outer): Outer = o
+ }
+ """)
+ assert(storeReporter.infos.isEmpty, storeReporter.infos.mkString("\n")) // Included a MissingRequirementError before.
+ }
+
+ def deletePackage(name: String) {
+ val directory = new File(testOutput.path, name)
+ for (f <- directory.listFiles()) {
+ assert(f.getName.endsWith(".class"))
+ assert(f.delete())
+ }
+ assert(directory.listFiles().isEmpty)
+ assert(directory.delete())
+ }
+}
diff --git a/test/files/run/t9387.scala b/test/files/run/t9387.scala
new file mode 100644
index 0000000000..3e33d19fd2
--- /dev/null
+++ b/test/files/run/t9387.scala
@@ -0,0 +1,20 @@
+class G[T]
+object G {
+ def v[T](x: T): G[T] = null
+}
+
+class A[T]
+object A {
+ def apply[T](x: => G[T]): A[T] = null
+}
+
+object T {
+ A[Unit](G.v(() => ())) // Was VerifyError
+}
+
+object Test {
+ def main(args: Array[String]): Unit = {
+ T
+ }
+
+} \ No newline at end of file
diff --git a/test/files/run/t9387b.check b/test/files/run/t9387b.check
new file mode 100644
index 0000000000..6a452c185a
--- /dev/null
+++ b/test/files/run/t9387b.check
@@ -0,0 +1 @@
+()
diff --git a/test/files/run/t9387b.scala b/test/files/run/t9387b.scala
new file mode 100644
index 0000000000..6339f4caba
--- /dev/null
+++ b/test/files/run/t9387b.scala
@@ -0,0 +1,16 @@
+object T {
+ val f: Unit = () => ()
+ println(f)
+}
+
+object U {
+ def f[T](t: T): T = t
+ f[Unit](() => ())
+}
+
+object Test {
+ def main(args: Array[String]): Unit = {
+ T
+ U
+ }
+}
diff --git a/test/files/run/t9403.flags b/test/files/run/t9403.flags
new file mode 100644
index 0000000000..307668060c
--- /dev/null
+++ b/test/files/run/t9403.flags
@@ -0,0 +1 @@
+-Ybackend:GenASM -optimize
diff --git a/test/files/run/t9403/C_1.scala b/test/files/run/t9403/C_1.scala
new file mode 100644
index 0000000000..439af1a386
--- /dev/null
+++ b/test/files/run/t9403/C_1.scala
@@ -0,0 +1,5 @@
+package p
+class C {
+ @inline final def f(x: Int): Long = 10L / (if (x < 0) -2 else 2)
+ @inline final def g(x: Int): Long = 3000L / (if (x < 0) -300 else 300)
+}
diff --git a/test/files/run/t9403/Test_2.scala b/test/files/run/t9403/Test_2.scala
new file mode 100644
index 0000000000..fb2777b9a8
--- /dev/null
+++ b/test/files/run/t9403/Test_2.scala
@@ -0,0 +1,29 @@
+import p.C
+import scala.tools.asm.Opcodes
+import scala.tools.partest.BytecodeTest
+import scala.tools.partest.ASMConverters._
+
+
+object Test extends BytecodeTest {
+ def foo(c: C, x: Int) = c.f(x)
+ def goo(c: C, x: Int) = c.g(x)
+
+ def has(i: Instruction, c: String, m: String) = {
+ val cls = loadClassNode(c)
+ val mth = convertMethod(getMethod(cls, m))
+ assert(mth.instructions.contains(i))
+ }
+
+ def show(): Unit = {
+ assert(foo(new C, -2) == -5L)
+ assert(goo(new C, -2) == -10L)
+
+ val bipush2 = IntOp(Opcodes.BIPUSH, -2)
+ has(bipush2, "p.C", "f")
+ has(bipush2, "Test$", "foo")
+
+ val sipush300 = IntOp(Opcodes.SIPUSH, -300)
+ has(sipush300, "p.C", "g")
+ has(sipush300, "Test$", "goo")
+ }
+}
diff --git a/test/files/run/test-cpp.scala b/test/files/run/test-cpp.scala
index 4e00e72658..4fca67d51e 100644
--- a/test/files/run/test-cpp.scala
+++ b/test/files/run/test-cpp.scala
@@ -46,7 +46,7 @@ object TestSetterInline {
* The access of the local variable 'y' should be replaced by the
* constant.
*/
-object TestAliasChainConstat {
+object TestAliasChainConstant {
def main(args: Array[String]): Unit = {
val x = 2
diff --git a/test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala
index 9af9ef54fc..a5b3faced8 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/analysis/ProdConsAnalyzerTest.scala
@@ -246,4 +246,46 @@ class ProdConsAnalyzerTest extends ClearAfterClass {
testSingleInsn(a.consumersOfOutputsFrom(l2i), "IRETURN")
testSingleInsn(a.producersForInputsOf(ret), "L2I")
}
+
+ @Test
+ def cyclicProdCons(): Unit = {
+ import Opcodes._
+ val m = genMethod(descriptor = "(I)I")(
+ Label(1),
+ VarOp(ILOAD, 1),
+ IntOp(BIPUSH, 10),
+ Op(IADD), // consumer of the above ILOAD
+
+ Op(ICONST_0),
+ Jump(IF_ICMPNE, Label(2)),
+
+ VarOp(ILOAD, 1),
+ VarOp(ISTORE, 1),
+ Jump(GOTO, Label(1)),
+
+ Label(2),
+ IntOp(BIPUSH, 9),
+ Op(IRETURN)
+ )
+ m.maxLocals = 2
+ m.maxStack = 2
+ val a = new ProdConsAnalyzer(m, "C")
+
+ val List(iadd) = findInstr(m, "IADD")
+ val firstLoad = iadd.getPrevious.getPrevious
+ assert(firstLoad.getOpcode == ILOAD)
+ val secondLoad = findInstr(m, "ISTORE").head.getPrevious
+ assert(secondLoad.getOpcode == ILOAD)
+
+ testSingleInsn(a.producersForValueAt(iadd, 2), "ILOAD")
+ testSingleInsn(a.initialProducersForValueAt(iadd, 2), "ParameterProducer(1)")
+ testMultiInsns(a.producersForInputsOf(firstLoad), List("ParameterProducer", "ISTORE"))
+ testMultiInsns(a.producersForInputsOf(secondLoad), List("ParameterProducer", "ISTORE"))
+
+ testSingleInsn(a.ultimateConsumersOfOutputsFrom(firstLoad), "IADD")
+ testSingleInsn(a.ultimateConsumersOfOutputsFrom(secondLoad), "IADD")
+
+ testSingleInsn(a.consumersOfOutputsFrom(firstLoad), "IADD")
+ testSingleInsn(a.consumersOfOutputsFrom(secondLoad), "ISTORE")
+ }
}
diff --git a/test/junit/scala/tools/nsc/doc/html/HtmlDocletTest.scala b/test/junit/scala/tools/nsc/doc/html/HtmlDocletTest.scala
index 13a955b55d..d17856eb54 100644
--- a/test/junit/scala/tools/nsc/doc/html/HtmlDocletTest.scala
+++ b/test/junit/scala/tools/nsc/doc/html/HtmlDocletTest.scala
@@ -10,7 +10,7 @@ import scala.tools.testing.AssertUtil._
@RunWith(classOf[JUnit4])
class HtmlDocletTest {
@Test
- def testSyntaxHighlightningUnicode() {
+ def testSyntaxHighlightingUnicode() {
val in = "unicode: …"
val out = SyntaxHigh(in).toString
diff --git a/test/junit/scala/tools/nsc/symtab/FlagsTest.scala b/test/junit/scala/tools/nsc/symtab/FlagsTest.scala
new file mode 100644
index 0000000000..fc0e8b0f6b
--- /dev/null
+++ b/test/junit/scala/tools/nsc/symtab/FlagsTest.scala
@@ -0,0 +1,89 @@
+package scala.tools.nsc
+package symtab
+
+import org.junit.Assert._
+import scala.tools.testing.AssertUtil._
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(classOf[JUnit4])
+class FlagsTest {
+ object symbolTable extends SymbolTableForUnitTesting
+ import symbolTable._
+ import Flags._
+
+ def sym = NoSymbol.newTermSymbol(nme.EMPTY)
+
+ def withFlagMask[A](mask: Long)(body: => A): A = enteringPhase(new Phase(NoPhase) {
+ override def flagMask = mask
+ def name = ""
+ def run() = ()
+ })(body)
+
+ def testTimedFlag(flag: Long, test: Symbol => Boolean, enabling: Boolean) = {
+ assertEquals(withFlagMask(InitialFlags)(test(sym.setFlag(flag))), !enabling)
+ assertEquals(withFlagMask(InitialFlags | flag)(test(sym.setFlag(flag))), enabling)
+ }
+
+ def testLate(flag: Long, test: Symbol => Boolean) = testTimedFlag(flag, test, enabling = true)
+ def testNot(flag: Long, test: Symbol => Boolean) = testTimedFlag(flag, test, enabling = false)
+
+ @Test
+ def testTimedFlags(): Unit = {
+ testLate(lateDEFERRED, _.isDeferred)
+ testLate(lateFINAL, _.isFinal)
+ testLate(lateINTERFACE, _.isInterface)
+ testLate(lateMETHOD, _.isMethod)
+ testLate(lateMODULE, _.isModule)
+ testNot(PROTECTED | notPROTECTED, _.isProtected)
+ testNot(OVERRIDE | notOVERRIDE, _.isOverride)
+ testNot(PRIVATE | notPRIVATE, _.isPrivate)
+
+ assertFalse(withFlagMask(AllFlags)(sym.setFlag(PRIVATE | notPRIVATE).isPrivate))
+
+ assertEquals(withFlagMask(InitialFlags)(sym.setFlag(PRIVATE | notPRIVATE).flags & PRIVATE), PRIVATE)
+ assertEquals(withFlagMask(AllFlags)(sym.setFlag(PRIVATE | notPRIVATE).flags & PRIVATE), 0)
+ }
+
+ @Test
+ def normalLateOverlap(): Unit = {
+ // late flags are shifted by LateShift == 47.
+ // however, the first late flag is lateDEFERRED, which is DEFERRED << 47 == (1 << 4) << 47 == 1 << 51
+ // the flags from 1 << 47 to 1 << 50 are not late flags. this is ensured by the LateFlags mask.
+
+ for (i <- 0 to 3) {
+ val f = 1L << i
+ assertEquals(withFlagMask(AllFlags)(sym.setFlag(f << LateShift).flags & f), 0) // not treated as late flag
+ }
+ for (i <- 4 to 8) {
+ val f = 1L << i
+ assertEquals(withFlagMask(AllFlags)(sym.setFlag(f << LateShift).flags & f), f) // treated as late flag
+ }
+ }
+
+ @Test
+ def normalAnti(): Unit = {
+ for (i <- 0 to 2) {
+ val f = 1L << i
+ assertEquals(withFlagMask(AllFlags)(sym.setFlag(f | (f << AntiShift)).flags & f), 0) // negated flags
+ }
+ for (i <- 3 to 7) {
+ val f = 1L << i
+ assertEquals(withFlagMask(AllFlags)(sym.setFlag(f | (f << AntiShift)).flags & f), f) // not negated
+ }
+ }
+
+ @Test
+ def lateAntiCrossCheck(): Unit = {
+ val allButNegatable = AllFlags & ~(PROTECTED | OVERRIDE | PRIVATE)
+ val lateable = 0L | DEFERRED | FINAL | INTERFACE | METHOD | MODULE
+ val lateFlags = lateable << LateShift
+ val allButLateable = AllFlags & ~lateable
+
+ assertEquals(withFlagMask(AllFlags)(sym.setFlag(AllFlags).flags), allButNegatable)
+ assertEquals(withFlagMask(AllFlags)(sym.setFlag(allButLateable).flags), allButNegatable)
+
+ assertEquals(withFlagMask(AllFlags)(sym.setFlag(lateFlags).flags), lateFlags | lateable)
+ }
+}
diff --git a/test/scaladoc/filters b/test/scaladoc/filters
index 51a7507848..e91ca0eb36 100644
--- a/test/scaladoc/filters
+++ b/test/scaladoc/filters
@@ -1,6 +1,7 @@
#
#Java HotSpot(TM) 64-Bit Server VM warning: Failed to reserve shared memory (errno = 28).
Java HotSpot\(TM\) .* warning:
+OpenJDK .* warning:
# Hotspot receiving VM options through the $_JAVA_OPTIONS
# env variable outputs them on stderr
Picked up _JAVA_OPTIONS: