summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build.sbt44
-rw-r--r--build.xml2
-rw-r--r--doc/LICENSE.md4
-rw-r--r--doc/License.rtf4
-rw-r--r--project/ParserUtil.scala52
-rw-r--r--project/PartestUtil.scala92
-rw-r--r--project/ScalaOptionParser.scala129
-rw-r--r--project/VersionUtil.scala2
-rwxr-xr-xscripts/jobs/integrate/bootstrap62
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala231
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala9
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala15
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala10
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala27
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala45
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala37
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala22
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala4
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala5
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala4
-rw-r--r--src/compiler/scala/tools/nsc/transform/CleanUp.scala135
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala2
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/RefChecks.scala13
-rw-r--r--src/intellij/scala-build.iml.SAMPLE109
-rw-r--r--src/intellij/scala.ipr.SAMPLE3
-rw-r--r--src/library/scala/collection/Iterator.scala51
-rw-r--r--src/library/scala/concurrent/Future.scala12
-rw-r--r--src/library/scala/math/BigDecimal.scala18
-rw-r--r--src/library/scala/math/BigInt.scala23
-rw-r--r--src/library/scala/runtime/LambdaDeserialize.java29
-rw-r--r--src/library/scala/runtime/StructuralCallSite.java43
-rw-r--r--src/library/scala/runtime/SymbolLiteral.java20
-rw-r--r--src/library/scala/util/Properties.scala2
-rw-r--r--src/partest-extras/scala/tools/partest/ASMConverters.scala22
-rw-r--r--src/reflect/scala/reflect/internal/Definitions.scala10
-rw-r--r--src/reflect/scala/reflect/internal/StdNames.scala2
-rw-r--r--src/reflect/scala/reflect/runtime/JavaUniverseForce.scala3
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala12
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/base/comment/Comment.scala10
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/doclet/Indexer.scala2
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/Doclet.scala5
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala17
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala17
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/Page.scala15
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/page/DeprecatedIndex.scala4
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala1053
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/page/Index.scala129
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/page/IndexScript.scala94
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/page/ReferenceIndex.scala4
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala30
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.css568
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.js939
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/resource/lib/lato-v11-latin-100.eotbin0 -> 30159 bytes
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/resource/lib/lato-v11-latin-100.ttfbin0 -> 76144 bytes
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/resource/lib/lato-v11-latin-100.woffbin0 -> 33288 bytes
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/resource/lib/scheduler.js63
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.css283
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.js102
-rw-r--r--src/scalap/decoder.properties2
-rw-r--r--test/files/instrumented/indy-symbol-literal.scala19
-rw-r--r--test/files/neg/compile-time-only-a.check8
-rw-r--r--test/files/neg/t8685.check45
-rw-r--r--test/files/neg/t8685.flags1
-rw-r--r--test/files/neg/t8685.scala54
-rw-r--r--test/files/run/indy-via-macro-with-dynamic-args/Bootstrap.java17
-rw-r--r--test/files/run/indy-via-macro-with-dynamic-args/Test_2.scala6
-rw-r--r--test/files/run/indy-via-macro-with-dynamic-args/macro_1.scala33
-rw-r--r--test/files/run/indy-via-macro/Bootstrap.java16
-rw-r--r--test/files/run/indy-via-macro/Test_2.scala5
-rw-r--r--test/files/run/indy-via-macro/macro_1.scala32
-rw-r--r--test/files/run/numbereq.scala3
-rw-r--r--test/files/run/t7974.check47
-rw-r--r--test/files/specialized/fft.check2
-rw-r--r--test/junit/scala/collection/IteratorTest.scala28
-rw-r--r--test/junit/scala/issues/BytecodeTest.scala83
-rw-r--r--test/junit/scala/issues/OptimizedBytecodeTest.scala47
-rw-r--r--test/junit/scala/math/BigDecimalTest.scala5
-rw-r--r--test/junit/scala/math/BigIntTest.scala16
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala27
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/StringConcatTest.scala135
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/ClosureOptimizerTest.scala5
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala100
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOptsTest.scala138
-rw-r--r--test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala46
-rw-r--r--test/junit/scala/tools/nsc/transform/patmat/PatmatBytecodeTest.scala59
-rw-r--r--test/scaladoc/run/SI-6017.scala28
-rw-r--r--test/scaladoc/run/SI-9620.scala1
-rw-r--r--test/scaladoc/run/shortDescription-annotation.check (renamed from test/scaladoc/run/SI-6017.check)0
-rw-r--r--test/scaladoc/run/shortDescription-annotation.scala55
-rw-r--r--test/scaladoc/scalacheck/DeprecatedIndexTest.scala4
-rw-r--r--test/scaladoc/scalacheck/HtmlFactoryTest.scala43
-rw-r--r--test/scaladoc/scalacheck/IndexTest.scala12
-rw-r--r--versions.properties2
93 files changed, 4081 insertions, 1687 deletions
diff --git a/build.sbt b/build.sbt
index b31bde8078..9c3991e811 100644
--- a/build.sbt
+++ b/build.sbt
@@ -64,7 +64,6 @@ val scalaParserCombinatorsDep = withoutScalaLang("org.scala-lang.modules" %% "sc
val scalaSwingDep = withoutScalaLang("org.scala-lang.modules" %% "scala-swing" % versionNumber("scala-swing"))
val scalaXmlDep = withoutScalaLang("org.scala-lang.modules" %% "scala-xml" % versionNumber("scala-xml"))
val partestDep = withoutScalaLang("org.scala-lang.modules" %% "scala-partest" % versionNumber("partest"))
-val partestInterfaceDep = withoutScalaLang("org.scala-lang.modules" %% "scala-partest-interface" % "0.7.0")
val junitDep = "junit" % "junit" % "4.11"
val junitIntefaceDep = "com.novocode" % "junit-interface" % "0.11" % "test"
val asmDep = "org.scala-lang.modules" % "scala-asm" % versionProps("scala-asm.version")
@@ -196,7 +195,18 @@ lazy val commonSettings = clearSourceAndResourceDirectories ++ publishSettings +
},
// Remove auto-generated manifest attributes
packageOptions in Compile in packageBin := Seq.empty,
- packageOptions in Compile in packageSrc := Seq.empty
+ packageOptions in Compile in packageSrc := Seq.empty,
+
+ // Lets us CTRL-C partest without exiting SBT entirely
+ cancelable in Global := true,
+ // When we fork subprocesses, use the base directory as the working directory.
+ // This enables `sbt> partest test/files/run/t1.scala` or `sbt> scalac sandbox/test.scala`
+ baseDirectory in Compile := (baseDirectory in ThisBuild).value,
+ baseDirectory in Test := (baseDirectory in ThisBuild).value,
+
+ // Don't log process output (e.g. of forked `compiler/runMain ...Main`), just pass it
+ // directly to stdout
+ outputStrategy in run := Some(StdoutOutput)
)
/** Extra post-processing for the published POM files. These are needed to create POMs that
@@ -409,7 +419,6 @@ lazy val repl = configureAsSubproject(project)
.settings(
connectInput in run := true,
publishArtifact := false,
- outputStrategy in run := Some(StdoutOutput),
run <<= (run in Compile).partialInput(" -usejavacp") // Automatically add this so that `repl/run` works without additional arguments.
)
.dependsOn(compiler, interactive)
@@ -456,7 +465,8 @@ lazy val replJlineEmbedded = Project("repl-jline-embedded", file(".") / "target"
val outdir = (classDirectory in Compile).value
JarJar(inputs, outdir, config)
}),
- publishArtifact := false
+ publishArtifact := false,
+ connectInput in run := true
)
.dependsOn(replJline)
@@ -537,7 +547,7 @@ lazy val test = project
.settings(Defaults.itSettings: _*)
.settings(
publishArtifact := false,
- libraryDependencies ++= Seq(asmDep, partestDep, scalaXmlDep, partestInterfaceDep, scalacheckDep),
+ libraryDependencies ++= Seq(asmDep, partestDep, scalaXmlDep, scalacheckDep),
unmanagedBase in IntegrationTest := baseDirectory.value / "files" / "lib",
unmanagedJars in IntegrationTest <+= (unmanagedBase) (j => Attributed.blank(j)) map(identity),
// no main sources
@@ -546,7 +556,7 @@ lazy val test = project
sources in IntegrationTest := Seq.empty,
fork in IntegrationTest := true,
javaOptions in IntegrationTest += "-Xmx1G",
- testFrameworks += new TestFramework("scala.tools.partest.Framework"),
+ testFrameworks += new TestFramework("scala.tools.partest.sbt.Framework"),
testOptions in IntegrationTest += Tests.Setup( () => root.base.getAbsolutePath + "/pull-binary-libs.sh" ! ),
testOptions in IntegrationTest += Tests.Argument("-Dpartest.java_opts=-Xmx1024M -Xms64M -XX:MaxPermSize=128M"),
definedTests in IntegrationTest += (
@@ -767,3 +777,25 @@ def generateServiceProviderResources(services: (String, String)*): Setting[_] =
}.taskValue
buildDirectory in ThisBuild := (baseDirectory in ThisBuild).value / "build-sbt"
+
+// Add tab completion to partest
+commands += Command("partest")(_ => PartestUtil.partestParser((baseDirectory in ThisBuild).value, (baseDirectory in ThisBuild).value / "test")) { (state, parsed) =>
+ ("test/it:testOnly -- " + parsed) :: state
+}
+
+// Add tab completion to scalac et al.
+commands ++= {
+ val commands =
+ List(("scalac", "compiler", "scala.tools.nsc.Main"),
+ ("scala", "repl-jline-embedded", "scala.tools.nsc.MainGenericRunner"),
+ ("scaladoc", "scaladoc", "scala.tools.nsc.ScalaDoc"))
+
+ commands.map {
+ case (entryPoint, projectRef, mainClassName) =>
+ Command(entryPoint)(_ => ScalaOptionParser.scalaParser(entryPoint, (baseDirectory in ThisBuild).value)) { (state, parsedOptions) =>
+ (projectRef + "/runMain " + mainClassName + " -usejavacp " + parsedOptions) :: state
+ }
+ }
+}
+
+addCommandAlias("scalap", "scalap/compile:runMain scala.tools.scalap.Main -usejavacp")
diff --git a/build.xml b/build.xml
index 78c7a9a230..04c1052bea 100644
--- a/build.xml
+++ b/build.xml
@@ -183,7 +183,7 @@ TODO:
<property name="dists.dir" value="${basedir}/dists"/>
- <property name="copyright.string" value="Copyright 2002-2015, LAMP/EPFL"/>
+ <property name="copyright.string" value="Copyright 2002-2016, 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 -->
diff --git a/doc/LICENSE.md b/doc/LICENSE.md
index 5c9310a8db..dc557368cf 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-2015 EPFL
+Copyright (c) 2002-2016 EPFL
-Copyright (c) 2011-2015 Typesafe, Inc.
+Copyright (c) 2011-2016 Typesafe, Inc.
All rights reserved.
diff --git a/doc/License.rtf b/doc/License.rtf
index d2f37a4bf9..f2258077fa 100644
--- a/doc/License.rtf
+++ b/doc/License.rtf
@@ -10,8 +10,8 @@
\fs48 Scala License
\fs40 \
-\fs26 Copyright (c) 2002-2015 EPFL\
-Copyright (c) 2011-2015 Typesafe, Inc.\
+\fs26 Copyright (c) 2002-2016 EPFL\
+Copyright (c) 2011-2016 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/project/ParserUtil.scala b/project/ParserUtil.scala
new file mode 100644
index 0000000000..cdaf8831a5
--- /dev/null
+++ b/project/ParserUtil.scala
@@ -0,0 +1,52 @@
+import sbt._
+import sbt.complete.Parser._
+import sbt.complete.Parsers._
+import sbt.complete._
+
+object ParserUtil {
+ def notStartingWith(parser: Parser[String], c: Char): Parser[String] = parser & not(c ~> any.*, "value cannot start with " + c + ".")
+ def concat(p: Parser[(String, String)]): Parser[String] = {
+ p.map(x => x._1 + x._2)
+ }
+
+ def Opt(a: Parser[String]) = a.?.map(_.getOrElse(""))
+
+ val StringBasicNotStartingWithDash = notStartingWith(StringBasic, '-')
+ val IsDirectoryFilter = new SimpleFileFilter(_.isDirectory)
+ val JarOrDirectoryParser = FileParser(GlobFilter("*.jar") || IsDirectoryFilter)
+ def FileParser(fileFilter: FileFilter, dirFilter: FileFilter = AllPassFilter, base: File = file(".")) = {
+ def matching(prefix: String): List[String] = {
+ val preFile = file(prefix)
+ val cwd = base
+ val parent = Option(preFile.getParentFile).getOrElse(cwd)
+ if (preFile.exists) {
+ if (preFile.isDirectory) {
+ preFile.*(IsDirectoryFilter.&&(dirFilter) || fileFilter).get.map(_.getPath).toList
+ } else {
+ List(preFile).filter(fileFilter.accept).map(_.getPath)
+ }
+ }
+ else if (parent != null) {
+ def ensureSuffix(s: String, suffix: String) = if (s.endsWith(suffix)) s else s + suffix
+ def pathOf(f: File): String = {
+ val f1 = if (preFile.getParentFile == null) f.relativeTo(cwd).getOrElse(f) else f
+ if (f1.isDirectory && !fileFilter.accept(f1)) ensureSuffix(f1.getPath, "/") else f1.getPath
+ }
+ val childFilter = GlobFilter(preFile.name + "*") && ((IsDirectoryFilter && dirFilter) || fileFilter)
+ val children = parent.*(childFilter).get
+ children.map(pathOf).toList
+ } else Nil
+ }
+ def displayPath = Completions.single(Completion.displayOnly("<path>"))
+ token(StringBasic, TokenCompletions.fixed((seen, level) => if (seen.isEmpty) displayPath else matching(seen) match {
+ case Nil => displayPath
+ case x :: Nil =>
+ if (fileFilter.accept(file(x)))
+ Completions.strict(Set(Completion.tokenDisplay(x.stripPrefix(seen), x)))
+ else
+ Completions.strict(Set(Completion.suggestion(x.stripPrefix(seen))))
+ case xs =>
+ Completions.strict(xs.map(x => Completion.tokenDisplay(x.stripPrefix(seen), x)).toSet)
+ })).filter(!_.startsWith("-"), x => x)
+ }
+} \ No newline at end of file
diff --git a/project/PartestUtil.scala b/project/PartestUtil.scala
new file mode 100644
index 0000000000..4b18c94b47
--- /dev/null
+++ b/project/PartestUtil.scala
@@ -0,0 +1,92 @@
+import sbt._
+import sbt.complete._, Parser._, Parsers._
+
+object PartestUtil {
+ private case class TestFiles(srcPath: String, globalBase: File, testBase: File) {
+ private val testCaseDir = new SimpleFileFilter(f => f.isDirectory && f.listFiles.nonEmpty && !(f.getParentFile / (f.name + ".res")).exists)
+ private val testCaseFilter = GlobFilter("*.scala") | GlobFilter("*.java") | GlobFilter("*.res") || testCaseDir
+ private def testCaseFinder = (testBase / srcPath).*(AllPassFilter).*(testCaseFilter)
+ private val basePaths = allTestCases.map(_._2.split('/').take(3).mkString("/") + "/").distinct
+
+ def allTestCases = testCaseFinder.pair(relativeTo(globalBase))
+ def basePathExamples = new FixedSetExamples(basePaths)
+ private def equiv(f1: File, f2: File) = f1.getCanonicalFile == f2.getCanonicalFile
+ def parentChain(f: File): Iterator[File] =
+ if (f == null || !f.exists) Iterator()
+ else Iterator(f) ++ (if (f.getParentFile == null) Nil else parentChain(f.getParentFile))
+ def isParentOf(parent: File, f2: File, maxDepth: Int) =
+ parentChain(f2).take(maxDepth).exists(p1 => equiv(p1, parent))
+ def isTestCase(f: File) = {
+ val grandParent = if (f != null && f.getParentFile != null) f.getParentFile.getParentFile else null
+ grandParent != null && equiv(grandParent, testBase / srcPath) && testCaseFilter.accept(f)
+ }
+ def mayContainTestCase(f: File) = {
+ isParentOf(testBase / srcPath, f, 2) || isParentOf(f, testBase / srcPath, Int.MaxValue)
+ }
+ }
+ /** A parser for the custom `partest` command */
+ def partestParser(globalBase: File, testBase: File): Parser[String] = {
+ val knownUnaryOptions = List(
+ "--pos", "--neg", "--run", "--jvm", "--res", "--ant", "--scalap", "--specialized",
+ "--scalacheck", "--instrumented", "--presentation", "--failed", "--update-check",
+ "--show-diff", "--verbose", "--terse", "--debug", "--version", "--self-test", "--help")
+ val srcPathOption = "--srcpath"
+ val grepOption = "--grep"
+
+ // HACK: if we parse `--srpath scaladoc`, we overwrite this var. The parser for test file paths
+ // then lazily creates the examples based on the current value.
+ // TODO is there a cleaner way to do this with SBT's parser infrastructure?
+ var srcPath = "files"
+ var _testFiles: TestFiles = null
+ def testFiles = {
+ if (_testFiles == null || _testFiles.srcPath != srcPath) _testFiles = new TestFiles(srcPath, globalBase, testBase)
+ _testFiles
+ }
+ val TestPathParser = ParserUtil.FileParser(
+ new SimpleFileFilter(f => testFiles.isTestCase(f)),
+ new SimpleFileFilter(f => testFiles.mayContainTestCase(f)), globalBase)
+
+ // allow `--grep "is unchecked" | --grep *t123*, in the spirit of ./bin/partest-ack
+ // superset of the --grep built into partest itself.
+ val Grep = {
+ def expandGrep(x: String): Seq[String] = {
+ val matchingFileContent = try {
+ val Pattern = ("(?i)" + x).r
+ testFiles.allTestCases.filter {
+ case (testFile, testPath) =>
+ val assocFiles = List(".check", ".flags").map(testFile.getParentFile / _)
+ val sourceFiles = if (testFile.isFile) List(testFile) else testFile.**(AllPassFilter).get.toList
+ val allFiles = testFile :: assocFiles ::: sourceFiles
+ allFiles.exists { f => f.exists && f.isFile && Pattern.findFirstIn(IO.read(f)).isDefined }
+ }
+ } catch {
+ case _: Throwable => Nil
+ }
+ val matchingFileName = try {
+ val filter = GlobFilter("*" + x + "*")
+ testFiles.allTestCases.filter(x => filter.accept(x._1.name))
+ } catch {
+ case t: Throwable => Nil
+ }
+ (matchingFileContent ++ matchingFileName).map(_._2).distinct.sorted
+ }
+
+ val completion = Completions.strict(Set("<filename glob>", "<regex> (for source, flags or checkfile contents)").map(s => Completion.displayOnly(s)))
+ val tokenCompletion = TokenCompletions.fixed((seen, level) => completion)
+
+ val globOrPattern = StringBasic.map(expandGrep).flatMap {
+ case Seq() => failure("no tests match pattern / glob")
+ case x => success(x.mkString(" "))
+ }
+ token(grepOption <~ Space) ~> token(globOrPattern, tokenCompletion)
+ }
+
+ val SrcPath = ((token(srcPathOption) <~ Space) ~ token(StringBasic.examples(Set("files", "pending", "scaladoc")))) map {
+ case opt ~ path =>
+ srcPath = path
+ opt + " " + path
+ }
+ val P = oneOf(knownUnaryOptions.map(x => token(x))) | SrcPath | TestPathParser | Grep
+ (Space ~> repsep(P, oneOrMore(Space))).map(_.mkString(" ")).?.map(_.getOrElse("")) <~ OptSpace
+ }
+}
diff --git a/project/ScalaOptionParser.scala b/project/ScalaOptionParser.scala
new file mode 100644
index 0000000000..da8a3bf460
--- /dev/null
+++ b/project/ScalaOptionParser.scala
@@ -0,0 +1,129 @@
+import ParserUtil._
+import sbt._
+import sbt.complete.Parser._
+import sbt.complete.Parsers._
+import sbt.complete._
+
+object ScalaOptionParser {
+ /** A SBT parser for the Scala command line runners (scala, scalac, etc) */
+ def scalaParser(entryPoint: String, globalBase: File): Parser[String] = {
+ def BooleanSetting(name: String): Parser[String] =
+ token(name)
+ def StringSetting(name: String): Parser[String] = {
+ val valueParser = name match {
+ case "-d" => JarOrDirectoryParser
+ case _ => token(StringBasic, TokenCompletions.displayOnly("<value>"))
+ }
+ concat(concat(token(name ~ Space.string)) ~ valueParser)
+ }
+ def MultiStringSetting(name: String): Parser[String] =
+ concat(concat(token(name ~ ":")) ~ repsep(token(StringBasicNotStartingWithDash, TokenCompletions.displayOnly("<value>")), token(",")).map(_.mkString))
+ def IntSetting(name: String): Parser[String] =
+ concat(concat(token(name ~ ":")) ~ token(IntBasic.map(_.toString), TokenCompletions.displayOnly("<integer>")))
+ def ChoiceSetting(name: String, choices: List[String]): Parser[String] =
+ concat(token(concat(name ~ ":")) ~ token(StringBasic.examples(choices: _*)).map(_.mkString))
+ def MultiChoiceSetting(name: String, choices: List[String]): Parser[String] =
+ concat(token(concat(name ~ ":")) ~ rep1sep(token(StringBasic.examples(choices: _*)), token(",")).map(_.mkString))
+ def PathSetting(name: String): Parser[String] = {
+ concat(concat(token(name) ~ Space.string) ~ rep1sep(JarOrDirectoryParser.filter(!_.contains(":"), x => x), token(java.io.File.pathSeparator)).map(_.mkString))
+ }
+ def FileSetting(name: String): Parser[String] = {
+ concat(concat(token(name) ~ Space.string) ~ rep1sep(JarOrDirectoryParser.filter(!_.contains(":"), x => x), token(java.io.File.pathSeparator)).map(_.mkString))
+ }
+ val Phase = token(NotSpace.examples(phases: _*))
+ def PhaseSettingParser(name: String): Parser[String] = {
+ MultiChoiceSetting(name, phases)
+ }
+ def ScalaVersionSetting(name: String): Parser[String] = {
+ concat(concat(token(name ~ Space.string)) ~ token(StringBasic, TokenCompletions.displayOnly("<scala version>")))
+ }
+ val Property: Parser[String] = {
+ val PropName = concat(token("-D" ~ oneOrMore(NotSpaceClass & not('=', "not =")).string, TokenCompletions.displayOnly("-D<property name>")))
+ val EqualsValue = concat("=" ~ token(OptNotSpace, TokenCompletions.displayOnly("<property value>")))
+ concat(PropName ~ EqualsValue.?.map(_.getOrElse("")))
+ }
+
+ val sourceFile = FileParser(GlobFilter("*.scala") | GlobFilter("*.java"))
+
+ // TODO Allow JVM settings via -J-... and temporarily add them to the ForkOptions
+ val UniversalOpt = Property | oneOf(pathSettingNames.map(PathSetting) ++ phaseSettings.map(PhaseSettingParser) ++ booleanSettingNames.map(BooleanSetting) ++ stringSettingNames.map(StringSetting) ++ multiStringSettingNames.map(MultiStringSetting) ++ intSettingNames.map(IntSetting) ++ choiceSettingNames.map { case (k, v) => ChoiceSetting(k, v) } ++ multiChoiceSettingNames.map { case (k, v) => MultiChoiceSetting(k, v) } ++ scalaVersionSettings.map(ScalaVersionSetting))
+ val ScalacOpt = sourceFile | UniversalOpt
+
+ val ScalaExtraSettings = oneOf(
+ scalaChoiceSettingNames.map { case (k, v) => ChoiceSetting(k,v)}.toList
+ ++ scalaStringSettingNames.map(StringSetting)
+ ++ scalaBooleanSettingNames.map(BooleanSetting))
+ val ScalaOpt = UniversalOpt | ScalaExtraSettings
+
+ val ScalaDocExtraSettings = oneOf(
+ scalaDocBooleanSettingNames.map(BooleanSetting)
+ ++ scalaDocIntSettingNames.map(IntSetting)
+ ++ scalaDocChoiceSettingNames.map { case (k, v) => ChoiceSetting(k, v)}
+ ++ scaladocStringSettingNames.map(StringSetting)
+ ++ scaladocPathSettingNames.map(PathSetting)
+ ++ scaladocMultiStringSettingNames.map(MultiStringSetting)
+ )
+ val ScalaDocOpt = ScalacOpt | ScalaDocExtraSettings
+
+ val P = entryPoint match {
+ case "scala" =>
+ val runnable = token(StringBasicNotStartingWithDash, TokenCompletions.displayOnly("<script|class|object|jar>")).filter(!_.startsWith("-"), x => x)
+ val runnableAndArgs = concat(runnable ~ Opt(concat(Space.string ~ repsep(token(StringBasic, TokenCompletions.displayOnly("<arg>")), Space).map(_.mkString(" ")))))
+ val options = rep1sep(ScalaOpt, Space).map(_.mkString(" "))
+ Opt(Space ~> (options | concat(concat(options ~ Space.string) ~ runnableAndArgs) | runnableAndArgs))
+ case "scaladoc" =>
+ Opt(Space ~> Opt(repsep(ScalaDocOpt, Space).map(_.mkString(" "))))
+ case "scalac" =>
+ Opt(Space ~> repsep(ScalacOpt, Space).map(_.mkString(" ")))
+ }
+ P <~ token(OptSpace)
+ }
+
+ // TODO retrieve this data programatically, ala https://github.com/scala/scala-tool-support/blob/master/bash-completion/src/main/scala/BashCompletion.scala
+ private def booleanSettingNames = List("-X", "-Xcheckinit", "-Xdev", "-Xdisable-assertions", "-Xexperimental", "-Xfatal-warnings", "-Xfull-lubs", "-Xfuture", "-Xlog-free-terms", "-Xlog-free-types", "-Xlog-implicit-conversions", "-Xlog-implicits", "-Xlog-reflective-calls",
+ "-Xno-forwarders", "-Xno-patmat-analysis", "-Xno-uescape", "-Xnojline", "-Xprint-pos", "-Xprint-types", "-Xprompt", "-Xresident", "-Xshow-phases", "-Xstrict-inference", "-Xverify", "-Y",
+ "-Ybreak-cycles", "-Yclosure-elim", "-Yconst-opt", "-Ydead-code", "-Ydebug", "-Ycompact-trees", "-Ydisable-unreachable-prevention", "-YdisableFlatCpCaching", "-Ydoc-debug",
+ "-Yeta-expand-keeps-star", "-Yide-debug", "-Yinfer-argument-types", "-Yinfer-by-name", "-Yinfer-debug", "-Yinline", "-Yinline-handlers",
+ "-Yinline-warnings", "-Yissue-debug", "-Ylog-classpath", "-Ymacro-debug-lite", "-Ymacro-debug-verbose", "-Ymacro-no-expand",
+ "-Yno-completion", "-Yno-generic-signatures", "-Yno-imports", "-Yno-load-impl-class", "-Yno-predef", "-Ynooptimise",
+ "-Yoverride-objects", "-Yoverride-vars", "-Ypatmat-debug", "-Yno-adapted-args", "-Ypos-debug", "-Ypresentation-debug",
+ "-Ypresentation-strict", "-Ypresentation-verbose", "-Yquasiquote-debug", "-Yrangepos", "-Yreify-copypaste", "-Yreify-debug", "-Yrepl-class-based",
+ "-Yrepl-sync", "-Yshow-member-pos", "-Yshow-symkinds", "-Yshow-symowners", "-Yshow-syms", "-Yshow-trees", "-Yshow-trees-compact", "-Yshow-trees-stringified", "-Ytyper-debug",
+ "-Ywarn-adapted-args", "-Ywarn-dead-code", "-Ywarn-inaccessible", "-Ywarn-infer-any", "-Ywarn-nullary-override", "-Ywarn-nullary-unit", "-Ywarn-numeric-widen", "-Ywarn-unused", "-Ywarn-unused-import", "-Ywarn-value-discard",
+ "-deprecation", "-explaintypes", "-feature", "-help", "-no-specialization", "-nobootcp", "-nowarn", "-optimise", "-print", "-unchecked", "-uniqid", "-usejavacp", "-usemanifestcp", "-verbose", "-version")
+ private def stringSettingNames = List("-Xgenerate-phase-graph", "-Xmain-class", "-Xpluginsdir", "-Xshow-class", "-Xshow-object", "-Xsource-reader", "-Ydump-classes", "-Ygen-asmp",
+ "-Ygen-javap", "-Ypresentation-log", "-Ypresentation-replay", "-Yrepl-outdir", "-d", "-dependencyfile", "-encoding", "-Xscript")
+ private def pathSettingNames = List("-bootclasspath", "-classpath", "-extdirs", "-javabootclasspath", "-javaextdirs", "-sourcepath", "-toolcp")
+ private val phases = List("all", "parser", "namer", "packageobjects", "typer", "patmat", "superaccessors", "extmethods", "pickler", "refchecks", "uncurry", "tailcalls", "specialize", "explicitouter", "erasure", "posterasure", "lazyvals", "lambdalift", "constructors", "flatten", "mixin", "cleanup", "delambdafy", "icode", "jvm", "terminal")
+ private val phaseSettings = List("-Xprint-icode", "-Ystop-after", "-Yskip", "-Yshow", "-Ystop-before", "-Ybrowse", "-Ylog", "-Ycheck", "-Xprint")
+ private def multiStringSettingNames = List("-Xmacro-settings", "-Xplugin", "-Xplugin-disable", "-Xplugin-require")
+ private def intSettingNames = List("-Xmax-classfile-name", "-Xelide-below", "-Ypatmat-exhaust-depth", "-Ypresentation-delay", "-Yrecursion")
+ private def choiceSettingNames = Map[String, List[String]](
+ "-Ybackend" -> List("GenASM", "GenBCode"),
+ "-YclasspathImpl" -> List("flat", "recursive"),
+ "-Ydelambdafy" -> List("inline", "method"),
+ "-Ylinearizer" -> List("dfs", "dump", "normal", "rpo"),
+ "-Ymacro-expand" -> List("discard", "none"),
+ "-Yresolve-term-conflict" -> List("error", "object", "package"),
+ "-g" -> List("line", "none", "notailcails", "source", "vars"),
+ "-target" -> List("jvm-1.5", "jvm-1.6", "jvm-1.7", "jvm-1.8"))
+ private def multiChoiceSettingNames = Map[String, List[String]](
+ "-Xlint" -> List("adapted-args", "nullary-unit", "inaccessible", "nullary-override", "infer-any", "missing-interpolator", "doc-detached", "private-shadow", "type-parameter-shadow", "poly-implicit-overload", "option-implicit", "delayedinit-select", "by-name-right-associative", "package-object-classes", "unsound-match", "stars-align"),
+ "-language" -> List("help", "_", "dynamics", "postfixOps", "reflectiveCalls", "implicitConversions", "higherKinds", "existentials", "experimental.macros"),
+ "-Yopt" -> List("l:none", "l:default", "l:method", "l:project", "l:classpath", "unreachable-code", "simplify-jumps", "empty-line-numbers", "empty-labels", "compact-locals", "nullness-tracking", "closure-elimination", "inline-project", "inline-global"),
+ "-Ystatistics" -> List("parser", "typer", "patmat", "erasure", "cleanup", "jvm")
+ )
+ private def scalaVersionSettings = List("-Xmigration", "-Xsource")
+
+ private def scalaChoiceSettingNames = Map("-howtorun" -> List("object", "script", "jar", "guess"))
+ private def scalaStringSettingNames = List("-i", "-e")
+ private def scalaBooleanSettingNames = List("-nc", "-save")
+
+ private def scalaDocBooleanSettingNames = List("-Yuse-stupid-types", "-implicits", "-implicits-debug", "-implicits-show-all", "-implicits-sound-shadowing", "-implicits-hide", "-author", "-diagrams", "-diagrams-debug", "-raw-output", "-no-prefixes", "-no-link-warnings", "-expand-all-types", "-groups")
+ private def scalaDocIntSettingNames = List("-diagrams-max-classes", "-diagrams-max-implicits", "-diagrams-dot-timeout", "-diagrams-dot-restart")
+ private def scalaDocChoiceSettingNames = Map("-doc-format" -> List("html"))
+ private def scaladocStringSettingNames = List("-doc-title", "-doc-version", "-doc-footer", "-doc-no-compile", "-doc-source-url", "-doc-generator", "-skip-packages")
+ private def scaladocPathSettingNames = List("-doc-root-content", "-diagrams-dot-path")
+ private def scaladocMultiStringSettingNames = List("-doc-external-doc")
+
+}
diff --git a/project/VersionUtil.scala b/project/VersionUtil.scala
index f237c35c68..fab22e66d4 100644
--- a/project/VersionUtil.scala
+++ b/project/VersionUtil.scala
@@ -19,7 +19,7 @@ object VersionUtil {
)
lazy val generatePropertiesFileSettings = Seq[Setting[_]](
- copyrightString := "Copyright 2002-2015, LAMP/EPFL",
+ copyrightString := "Copyright 2002-2016, LAMP/EPFL",
resourceGenerators in Compile += generateVersionPropertiesFile.map(file => Seq(file)).taskValue,
generateVersionPropertiesFile := generateVersionPropertiesFileImpl.value
)
diff --git a/scripts/jobs/integrate/bootstrap b/scripts/jobs/integrate/bootstrap
index 410fe573f8..11cf659229 100755
--- a/scripts/jobs/integrate/bootstrap
+++ b/scripts/jobs/integrate/bootstrap
@@ -87,6 +87,7 @@ moduleVersioning=${moduleVersioning-"versions.properties"}
publishPrivateTask=${publishPrivateTask-"publish"}
publishSonatypeTaskCore=${publishSonatypeTaskCore-"publish-signed"}
publishSonatypeTaskModules=${publishSonatypeTaskModules-"publish-signed"}
+publishStarrPrivateTask=${publishStarrPrivateTask-$publishPrivateTask} # set to "init" to speed up testing of the script (if you already built STARR before)
publishLockerPrivateTask=${publishLockerPrivateTask-$publishPrivateTask} # set to "init" to speed up testing of the script (if you already built locker before)
forceRebuild=${forceRebuild-no}
@@ -208,12 +209,17 @@ sbtResolve() {
# scala-xml depends on scala-library, so sbt tries to find the scala-library of the version that we are currently building,
# which exists only in private-repo.
+docTask() {
+ if [ "$1" == "yes" ]; then echo doc; else echo set publishArtifact in packageDoc in Compile := false; fi
+}
+
buildXML() {
if [ "$XML_BUILT" != "yes" ] && [ "$forceRebuild" != "yes" ] && ( sbtResolve "org.scala-lang.modules" "scala-xml" $XML_VER )
then echo "Found scala-xml $XML_VER; not building."
else
update scala scala-xml "$XML_REF" && gfxd
- sbtBuild 'set version := "'$XML_VER'-DOC"' $clean doc 'set version := "'$XML_VER'"' test "${buildTasks[@]}"
+ doc="$(docTask $XML_BUILT)"
+ sbtBuild 'set version := "'$XML_VER'-DOC"' $clean "$doc" 'set version := "'$XML_VER'"' test "${buildTasks[@]}"
XML_BUILT="yes" # ensure the module is built and published when buildXML is invoked for the second time, see comment above
fi
}
@@ -223,7 +229,8 @@ buildParsers() {
then echo "Found scala-parser-combinators $PARSERS_VER; not building."
else
update scala scala-parser-combinators "$PARSERS_REF" && gfxd
- sbtBuild 'set version := "'$PARSERS_VER'-DOC"' $clean doc 'set version := "'$PARSERS_VER'"' test "${buildTasks[@]}"
+ doc="$(docTask $PARSERS_BUILT)"
+ sbtBuild 'set version := "'$PARSERS_VER'-DOC"' $clean "$doc" 'set version := "'$PARSERS_VER'"' test "${buildTasks[@]}"
PARSERS_BUILT="yes"
fi
}
@@ -233,26 +240,19 @@ buildPartest() {
then echo "Found scala-partest $PARTEST_VER; not building."
else
update scala scala-partest "$PARTEST_REF" && gfxd
- sbtBuild 'set version :="'$PARTEST_VER'"' 'set VersionKeys.scalaXmlVersion := "'$XML_VER'"' 'set VersionKeys.scalaCheckVersion := "'$SCALACHECK_VER'"' $clean test "${buildTasks[@]}"
+ doc="$(docTask $PARTEST_BUILT)"
+ sbtBuild 'set version :="'$PARTEST_VER'"' 'set VersionKeys.scalaXmlVersion := "'$XML_VER'"' 'set VersionKeys.scalaCheckVersion := "'$SCALACHECK_VER'"' $clean "$doc" test "${buildTasks[@]}"
PARTEST_BUILT="yes"
fi
}
-# buildPartestIface() {
-# if [ "$forceRebuild" != "yes" ] && ( sbtResolve "org.scala-lang.modules" "scala-partest-interface" $PARTEST_IFACE_VER )
-# then echo "Found scala-partest-interface $PARTEST_IFACE_VER; not building."
-# else
-# update scala scala-partest-interface "$PARTEST_IFACE_REF" && gfxd
-# sbtBuild 'set version :="'$PARTEST_IFACE_VER'"' $clean "${buildTasks[@]}"
-# fi
-# }
-
buildSwing() {
if [ "$SWING_BUILT" != "yes" ] && [ "$forceRebuild" != "yes" ] && ( sbtResolve "org.scala-lang.modules" "scala-swing" $SWING_VER )
then echo "Found scala-swing $SWING_VER; not building."
else
update scala scala-swing "$SWING_REF" && gfxd
- sbtBuild 'set version := "'$SWING_VER'"' $clean test "${buildTasks[@]}"
+ doc="$(docTask $SWING_BUILT)"
+ sbtBuild 'set version := "'$SWING_VER'"' $clean "$doc" test "${buildTasks[@]}"
SWING_BUILT="yes"
fi
}
@@ -263,7 +263,8 @@ buildScalacheck(){
then echo "Found scalacheck $SCALACHECK_VER; not building."
else
update rickynils scalacheck $SCALACHECK_REF && gfxd
- sbtBuild 'set version := "'$SCALACHECK_VER'"' 'set VersionKeys.scalaParserCombinatorsVersion := "'$PARSERS_VER'"' $clean publish # test times out NOTE: never published to sonatype
+ doc="$(docTask $SCALACHECK_BUILT)"
+ sbtBuild 'set version := "'$SCALACHECK_VER'"' 'set VersionKeys.scalaParserCombinatorsVersion := "'$PARSERS_VER'"' $clean "$doc" publish # test times out NOTE: never published to sonatype
SCALACHECK_BUILT="yes"
fi
}
@@ -277,7 +278,6 @@ buildModules() {
buildSwing
buildScalacheck
buildPartest
- # buildPartestIface
}
buildPublishedModules() {
@@ -287,7 +287,6 @@ buildPublishedModules() {
buildParsers
buildSwing
buildPartest
- # buildPartestIface
}
@@ -400,7 +399,6 @@ deriveModuleVersions() {
PARSERS_REF="v$PARSERS_VER"
SWING_REF="v$SWING_VER"
PARTEST_REF="v$PARTEST_VER"
- # PARTEST_IFACE_REF="v$PARTEST_IFACE_VER"
SCALACHECK_REF="$SCALACHECK_VER" # no `v` in their tags
else
# use HEAD as default when no revision is specified on the command line
@@ -408,7 +406,6 @@ deriveModuleVersions() {
PARSERS_REF=${PARSERS_REF-"HEAD"}
SWING_REF=${SWING_REF-"HEAD"}
PARTEST_REF=${PARTEST_REF-"HEAD"}
- # PARTEST_IFACE_REF=${PARTEST_IFACE_REF-"HEAD"}
SCALACHECK_REF=${SCALACHECK_REF-"HEAD"}
XML_VER=$(deriveVersion scala scala-xml "$XML_REF")
@@ -425,7 +422,6 @@ deriveModuleVersions() {
echo "SWING = $SWING_VER at $SWING_REF"
echo "XML = $XML_VER at $XML_REF"
- # PARTEST_IFACE_VER=${PARTEST_IFACE_VER-$(deriveVersion scala scala-partest-interface "$PARTEST_IFACE_REF")}
}
createNetrcFile() {
@@ -440,7 +436,7 @@ removeExistingBuilds() {
local netrcFile="$HOME/.credentials-private-repo-netrc"
local storageApiUrl=`echo $releaseTempRepoUrl | sed 's/\(scala-release-temp\)/api\/storage\/\1/'`
- local scalaLangModules=`curl -s $storageApiUrl/org/scala-lang | jq -r '.children | .[] | "org/scala-lang" + .uri'`
+ local scalaLangModules=`curl -s $storageApiUrl/org/scala-lang | jq -r '.children | .[] | "org/scala-lang" + .uri' | grep -v actors-migration`
for module in "org/scalacheck" $scalaLangModules; do
local artifacts=`curl -s $storageApiUrl/$module | jq -r ".children | .[] | select(.uri | contains(\"$SCALA_VER\")) | .uri"`
@@ -476,6 +472,29 @@ bootstrap() {
cd $WORKSPACE
+ #### (Optional) STARR.
+ if [ ! -z "$STARR_REF" ]; then
+ echo "### Building STARR"
+
+ STARR_DIR=./scala-starr
+ STARR_VER_SUFFIX="-$(git rev-parse --short $STARR_REF)-nightly"
+ STARR_VER=$SCALA_VER_BASE$STARR_VER_SUFFIX
+ rm -rf "$STARR_DIR"
+ (
+ git clone --reference $WORKSPACE/.git $WORKSPACE/.git $STARR_DIR
+ cd $STARR_DIR
+ git co $STARR_REF
+ ant -Dmaven.version.number=$STARR_VER\
+ -Dremote.snapshot.repository=NOPE\
+ -Dremote.release.repository=$releaseTempRepoUrl\
+ -Drepository.credentials.id=$releaseTempRepoCred\
+ -Dscalac.args.optimise=-Yopt:l:classpath\
+ -Ddocs.skip=1\
+ -Dlocker.skip=1\
+ $publishStarrPrivateTask >> $baseDir/logs/builds 2>&1
+ )
+ fi
+
#### LOCKER
echo "### Building locker"
@@ -485,8 +504,10 @@ bootstrap() {
# must publish under $SCALA_VER so that the modules will depend on this (binary) version of Scala
# publish more than just core: partest needs scalap
# in sabbus lingo, the resulting Scala build will be used as starr to build the released Scala compiler
+ if [ ! -z "$STARR_VER" ]; then SET_STARR=-Dstarr.version=$STARR_VER; fi
ant -Dmaven.version.number=$SCALA_VER\
-Dremote.snapshot.repository=NOPE\
+ $SET_STARR\
-Dremote.release.repository=$releaseTempRepoUrl\
-Drepository.credentials.id=$releaseTempRepoCred\
-Dscalac.args.optimise=-Yopt:l:classpath\
@@ -494,7 +515,6 @@ bootstrap() {
-Dlocker.skip=1\
$publishLockerPrivateTask >> $baseDir/logs/builds 2>&1
-
echo "### Building modules using locker"
# build, test and publish modules with this core
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
index 59d584c370..8a90eb9780 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeBodyBuilder.scala
@@ -202,14 +202,14 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val hasElse = !elsep.isEmpty
val postIf = if (hasElse) new asm.Label else failure
- genCond(condp, success, failure)
+ genCond(condp, success, failure, targetIfNoJump = success)
+ markProgramPoint(success)
val thenKind = tpeTK(thenp)
val elseKind = if (!hasElse) UNIT else tpeTK(elsep)
def hasUnitBranch = (thenKind == UNIT || elseKind == UNIT)
val resKind = if (hasUnitBranch) UNIT else tpeTK(tree)
- markProgramPoint(success)
genLoad(thenp, resKind)
if (hasElse) { bc goTo postIf }
markProgramPoint(failure)
@@ -234,14 +234,14 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
else if (isArrayOp(code)) genArrayOp(tree, code, expectedType)
else if (isLogicalOp(code) || isComparisonOp(code)) {
val success, failure, after = new asm.Label
- genCond(tree, success, failure)
+ genCond(tree, success, failure, targetIfNoJump = success)
// success block
- markProgramPoint(success)
- bc boolconst true
- bc goTo after
+ markProgramPoint(success)
+ bc boolconst true
+ bc goTo after
// failure block
- markProgramPoint(failure)
- bc boolconst false
+ markProgramPoint(failure)
+ bc boolconst false
// after
markProgramPoint(after)
@@ -310,6 +310,15 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
case app : Apply =>
generatedType = genApply(app, expectedType)
+ case app @ ApplyDynamic(qual, Literal(Constant(boostrapMethodRef: Symbol)) :: staticAndDynamicArgs) =>
+ val numStaticArgs = boostrapMethodRef.paramss.head.size - 3 /*JVM provided args*/
+ val (staticArgs, dynamicArgs) = staticAndDynamicArgs.splitAt(numStaticArgs)
+ val boostrapDescriptor = staticHandleFromSymbol(boostrapMethodRef)
+ val bootstrapArgs = staticArgs.map({case t @ Literal(c: Constant) => bootstrapMethodArg(c, t.pos)})
+ val descriptor = methodBTypeFromMethodType(qual.symbol.info, false)
+ genLoadArguments(dynamicArgs, qual.symbol.info.params.map(param => typeToBType(param.info)))
+ mnode.visitInvokeDynamicInsn(qual.symbol.name.encoded, descriptor.descriptor, boostrapDescriptor, bootstrapArgs : _*)
+
case ApplyDynamic(qual, args) => sys.error("No invokedynamic support yet.")
case This(qual) =>
@@ -708,7 +717,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
// for callsites marked `f(): @inline/noinline`. For nullary calls, the attachment
// is on the Select node (not on the Apply node added by UnCurry).
def checkInlineAnnotated(t: Tree): Unit = {
- if (t.hasAttachment[InlineAnnotatedAttachment]) bc.jmethod.instructions.getLast match {
+ if (t.hasAttachment[InlineAnnotatedAttachment]) lastInsn match {
case m: MethodInsnNode =>
if (app.hasAttachment[NoInlineCallsiteAttachment.type]) noInlineAnnotatedCallsites += m
else inlineAnnotatedCallsites += m
@@ -879,10 +888,24 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
* emitted instruction was an ATHROW. As explained above, it is OK to emit a second ATHROW,
* the verifiers will be happy.
*/
- emit(asm.Opcodes.ATHROW)
+ if (lastInsn.getOpcode != asm.Opcodes.ATHROW)
+ emit(asm.Opcodes.ATHROW)
} else if (from.isNullType) {
- bc drop from
- emit(asm.Opcodes.ACONST_NULL)
+ /* After loading an expression of type `scala.runtime.Null$`, introduce POP; ACONST_NULL.
+ * This is required to pass the verifier: in Scala's type system, Null conforms to any
+ * reference type. In bytecode, the type Null is represented by scala.runtime.Null$, which
+ * is not a subtype of all reference types. Example:
+ *
+ * def nl: Null = null // in bytecode, nl has return type scala.runtime.Null$
+ * val a: String = nl // OK for Scala but not for the JVM, scala.runtime.Null$ does not conform to String
+ *
+ * In order to fix the above problem, the value returned by nl is dropped and ACONST_NULL is
+ * inserted instead - after all, an expression of type scala.runtime.Null$ can only be null.
+ */
+ if (lastInsn.getOpcode != asm.Opcodes.ACONST_NULL) {
+ bc drop from
+ emit(asm.Opcodes.ACONST_NULL)
+ }
}
else (from, to) match {
case (BYTE, LONG) | (SHORT, LONG) | (CHAR, LONG) | (INT, LONG) => bc.emitT2T(INT, LONG)
@@ -999,9 +1022,17 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
case concatenations =>
bc.genStartConcat(tree.pos)
for (elem <- concatenations) {
- val kind = tpeTK(elem)
- genLoad(elem, kind)
- bc.genStringConcat(kind, elem.pos)
+ val loadedElem = elem match {
+ case Apply(boxOp, value :: Nil) if currentRun.runDefinitions.isBox(boxOp.symbol) =>
+ // Eliminate boxing of primitive values. Boxing is introduced by erasure because
+ // there's only a single synthetic `+` method "added" to the string class.
+ value
+
+ case _ => elem
+ }
+ val elemType = tpeTK(loadedElem)
+ genLoad(loadedElem, elemType)
+ bc.genConcat(elemType, loadedElem.pos)
}
bc.genEndConcat(tree.pos)
@@ -1091,53 +1122,58 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
}
/* Emit code to compare the two top-most stack values using the 'op' operator. */
- private def genCJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType) {
- if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
- bc.emitIF_ICMP(op, success)
- } else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
- bc.emitIF_ACMP(op, success)
- } else {
- (tk: @unchecked) match {
- case LONG => emit(asm.Opcodes.LCMP)
- case FLOAT =>
- if (op == TestOp.LT || op == TestOp.LE) emit(asm.Opcodes.FCMPG)
- else emit(asm.Opcodes.FCMPL)
- case DOUBLE =>
- if (op == TestOp.LT || op == TestOp.LE) emit(asm.Opcodes.DCMPG)
- else emit(asm.Opcodes.DCMPL)
+ private def genCJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType, targetIfNoJump: asm.Label) {
+ if (targetIfNoJump == success) genCJUMP(failure, success, op.negate, tk, targetIfNoJump)
+ else {
+ if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
+ bc.emitIF_ICMP(op, success)
+ } else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
+ bc.emitIF_ACMP(op, success)
+ } else {
+ (tk: @unchecked) match {
+ case LONG => emit(asm.Opcodes.LCMP)
+ case FLOAT =>
+ if (op == TestOp.LT || op == TestOp.LE) emit(asm.Opcodes.FCMPG)
+ else emit(asm.Opcodes.FCMPL)
+ case DOUBLE =>
+ if (op == TestOp.LT || op == TestOp.LE) emit(asm.Opcodes.DCMPG)
+ else emit(asm.Opcodes.DCMPL)
+ }
+ bc.emitIF(op, success)
}
- bc.emitIF(op, success)
+ if (targetIfNoJump != failure) bc goTo failure
}
- bc goTo failure
}
/* Emits code to compare (and consume) stack-top and zero using the 'op' operator */
- private def genCZJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType) {
- if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
- bc.emitIF(op, success)
- } else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
- // @unchecked because references aren't compared with GT, GE, LT, LE.
- (op : @unchecked) match {
- case TestOp.EQ => bc emitIFNULL success
- case TestOp.NE => bc emitIFNONNULL success
- }
- } else {
- (tk: @unchecked) match {
- case LONG =>
- emit(asm.Opcodes.LCONST_0)
- emit(asm.Opcodes.LCMP)
- case FLOAT =>
- emit(asm.Opcodes.FCONST_0)
- if (op == TestOp.LT || op == TestOp.LE) emit(asm.Opcodes.FCMPG)
- else emit(asm.Opcodes.FCMPL)
- case DOUBLE =>
- emit(asm.Opcodes.DCONST_0)
- if (op == TestOp.LT || op == TestOp.LE) emit(asm.Opcodes.DCMPG)
- else emit(asm.Opcodes.DCMPL)
+ private def genCZJUMP(success: asm.Label, failure: asm.Label, op: TestOp, tk: BType, targetIfNoJump: asm.Label) {
+ if (targetIfNoJump == success) genCZJUMP(failure, success, op.negate, tk, targetIfNoJump)
+ else {
+ if (tk.isIntSizedType) { // BOOL, BYTE, CHAR, SHORT, or INT
+ bc.emitIF(op, success)
+ } else if (tk.isRef) { // REFERENCE(_) | ARRAY(_)
+ op match { // references are only compared with EQ and NE
+ case TestOp.EQ => bc emitIFNULL success
+ case TestOp.NE => bc emitIFNONNULL success
+ }
+ } else {
+ (tk: @unchecked) match {
+ case LONG =>
+ emit(asm.Opcodes.LCONST_0)
+ emit(asm.Opcodes.LCMP)
+ case FLOAT =>
+ emit(asm.Opcodes.FCONST_0)
+ if (op == TestOp.LT || op == TestOp.LE) emit(asm.Opcodes.FCMPG)
+ else emit(asm.Opcodes.FCMPL)
+ case DOUBLE =>
+ emit(asm.Opcodes.DCONST_0)
+ if (op == TestOp.LT || op == TestOp.LE) emit(asm.Opcodes.DCMPG)
+ else emit(asm.Opcodes.DCMPL)
+ }
+ bc.emitIF(op, success)
}
- bc.emitIF(op, success)
+ if (targetIfNoJump != failure) bc goTo failure
}
- bc goTo failure
}
def testOpForPrimitive(primitiveCode: Int) = (primitiveCode: @switch) match {
@@ -1162,29 +1198,26 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
* Generate code for conditional expressions.
* The jump targets success/failure of the test are `then-target` and `else-target` resp.
*/
- private def genCond(tree: Tree, success: asm.Label, failure: asm.Label) {
+ private def genCond(tree: Tree, success: asm.Label, failure: asm.Label, targetIfNoJump: asm.Label) {
def genComparisonOp(l: Tree, r: Tree, code: Int) {
- val op: TestOp = testOpForPrimitive(code)
- // special-case reference (in)equality test for null (null eq x, x eq null)
- var nonNullSide: Tree = null
- if (scalaPrimitives.isReferenceEqualityOp(code) &&
- { nonNullSide = ifOneIsNull(l, r); nonNullSide != null }
- ) {
+ val op = testOpForPrimitive(code)
+ val nonNullSide = if (scalaPrimitives.isReferenceEqualityOp(code)) ifOneIsNull(l, r) else null
+ if (nonNullSide != null) {
+ // special-case reference (in)equality test for null (null eq x, x eq null)
genLoad(nonNullSide, ObjectRef)
- genCZJUMP(success, failure, op, ObjectRef)
- }
- else {
+ genCZJUMP(success, failure, op, ObjectRef, targetIfNoJump)
+ } else {
val tk = tpeTK(l).maxType(tpeTK(r))
genLoad(l, tk)
genLoad(r, tk)
- genCJUMP(success, failure, op, tk)
+ genCJUMP(success, failure, op, tk, targetIfNoJump)
}
}
- def default() = {
+ def loadAndTestBoolean() = {
genLoad(tree, BOOL)
- genCZJUMP(success, failure, TestOp.NE, BOOL)
+ genCZJUMP(success, failure, TestOp.NE, BOOL, targetIfNoJump)
}
lineNumber(tree)
@@ -1195,37 +1228,35 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
// lhs and rhs of test
lazy val Select(lhs, _) = fun
- val rhs = if (args.isEmpty) EmptyTree else args.head; // args.isEmpty only for ZNOT
+ val rhs = if (args.isEmpty) EmptyTree else args.head // args.isEmpty only for ZNOT
- def genZandOrZor(and: Boolean) { // TODO WRONG
+ def genZandOrZor(and: Boolean) {
// reaching "keepGoing" indicates the rhs should be evaluated too (ie not short-circuited).
val keepGoing = new asm.Label
- if (and) genCond(lhs, keepGoing, failure)
- else genCond(lhs, success, keepGoing)
+ if (and) genCond(lhs, keepGoing, failure, targetIfNoJump = keepGoing)
+ else genCond(lhs, success, keepGoing, targetIfNoJump = keepGoing)
markProgramPoint(keepGoing)
- genCond(rhs, success, failure)
+ genCond(rhs, success, failure, targetIfNoJump)
}
getPrimitive(fun.symbol) match {
- case ZNOT => genCond(lhs, failure, success)
+ case ZNOT => genCond(lhs, failure, success, targetIfNoJump)
case ZAND => genZandOrZor(and = true)
case ZOR => genZandOrZor(and = false)
case code =>
- // TODO !!!!!!!!!! isReferenceType, in the sense of TypeKind? (ie non-array, non-boxed, non-nothing, may be null)
if (scalaPrimitives.isUniversalEqualityOp(code) && tpeTK(lhs).isClass) {
- // `lhs` has reference type
- if (code == EQ) genEqEqPrimitive(lhs, rhs, success, failure, tree.pos)
- else genEqEqPrimitive(lhs, rhs, failure, success, tree.pos)
- }
- else if (scalaPrimitives.isComparisonOp(code))
+ // rewrite `==` to null tests and `equals`. not needed for arrays (`equals` is reference equality).
+ if (code == EQ) genEqEqPrimitive(lhs, rhs, success, failure, targetIfNoJump, tree.pos)
+ else genEqEqPrimitive(lhs, rhs, failure, success, targetIfNoJump, tree.pos)
+ } else if (scalaPrimitives.isComparisonOp(code)) {
genComparisonOp(lhs, rhs, code)
- else
- default
+ } else
+ loadAndTestBoolean()
}
- case _ => default
+ case _ => loadAndTestBoolean()
}
} // end of genCond()
@@ -1237,7 +1268,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
* @param l left-hand-side of the '=='
* @param r right-hand-side of the '=='
*/
- def genEqEqPrimitive(l: Tree, r: Tree, success: asm.Label, failure: asm.Label, pos: Position) {
+ def genEqEqPrimitive(l: Tree, r: Tree, success: asm.Label, failure: asm.Label, targetIfNoJump: asm.Label, pos: Position) {
/* True if the equality comparison is between values that require the use of the rich equality
* comparator (scala.runtime.Comparator.equals). This is the case when either side of the
@@ -1247,7 +1278,6 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
*/
val mustUseAnyComparator: Boolean = {
val areSameFinals = l.tpe.isFinalType && r.tpe.isFinalType && (l.tpe =:= r.tpe)
-
!areSameFinals && platform.isMaybeBoxed(l.tpe.typeSymbol) && platform.isMaybeBoxed(r.tpe.typeSymbol)
}
@@ -1262,23 +1292,22 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
genLoad(l, ObjectRef)
genLoad(r, ObjectRef)
genCallMethod(equalsMethod, InvokeStyle.Static, pos)
- genCZJUMP(success, failure, TestOp.NE, BOOL)
- }
- else {
+ genCZJUMP(success, failure, TestOp.NE, BOOL, targetIfNoJump)
+ } else {
if (isNull(l)) {
// null == expr -> expr eq null
genLoad(r, ObjectRef)
- genCZJUMP(success, failure, TestOp.EQ, ObjectRef)
+ genCZJUMP(success, failure, TestOp.EQ, ObjectRef, targetIfNoJump)
} else if (isNull(r)) {
// expr == null -> expr eq null
genLoad(l, ObjectRef)
- genCZJUMP(success, failure, TestOp.EQ, ObjectRef)
+ genCZJUMP(success, failure, TestOp.EQ, ObjectRef, targetIfNoJump)
} else if (isNonNullExpr(l)) {
// SI-7852 Avoid null check if L is statically non-null.
genLoad(l, ObjectRef)
genLoad(r, ObjectRef)
genCallMethod(Object_equals, InvokeStyle.Virtual, pos)
- genCZJUMP(success, failure, TestOp.NE, BOOL)
+ genCZJUMP(success, failure, TestOp.NE, BOOL, targetIfNoJump)
} else {
// l == r -> if (l eq null) r eq null else l.equals(r)
val eqEqTempLocal = locals.makeLocal(ObjectRef, nme.EQEQ_LOCAL_VAR.toString)
@@ -1289,17 +1318,17 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
genLoad(r, ObjectRef)
locals.store(eqEqTempLocal)
bc dup ObjectRef
- genCZJUMP(lNull, lNonNull, TestOp.EQ, ObjectRef)
+ genCZJUMP(lNull, lNonNull, TestOp.EQ, ObjectRef, targetIfNoJump = lNull)
markProgramPoint(lNull)
bc drop ObjectRef
locals.load(eqEqTempLocal)
- genCZJUMP(success, failure, TestOp.EQ, ObjectRef)
+ genCZJUMP(success, failure, TestOp.EQ, ObjectRef, targetIfNoJump = lNonNull)
markProgramPoint(lNonNull)
locals.load(eqEqTempLocal)
genCallMethod(Object_equals, InvokeStyle.Virtual, pos)
- genCZJUMP(success, failure, TestOp.NE, BOOL)
+ genCZJUMP(success, failure, TestOp.NE, BOOL, targetIfNoJump)
}
}
}
@@ -1327,7 +1356,7 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
val samName = sam.name.toString
val samMethodType = methodBTypeFromSymbol(sam).toASMType
- val flags = LambdaMetafactory.FLAG_SERIALIZABLE | LambdaMetafactory.FLAG_MARKERS
+ val flags = java.lang.invoke.LambdaMetafactory.FLAG_SERIALIZABLE | java.lang.invoke.LambdaMetafactory.FLAG_MARKERS
val ScalaSerializable = classBTypeFromSymbol(definitions.SerializableClass).toASMType
bc.jmethod.visitInvokeDynamicInsn(samName, invokedType, lambdaMetaFactoryBootstrapHandle,
@@ -1342,16 +1371,4 @@ abstract class BCodeBodyBuilder extends BCodeSkelBuilder {
indyLambdaHosts += cnode.name
}
}
-
- lazy val lambdaMetaFactoryBootstrapHandle =
- new asm.Handle(asm.Opcodes.H_INVOKESTATIC,
- coreBTypes.jliLambdaMetafactoryRef.internalName, sn.AltMetafactory.toString,
- MethodBType(
- List(
- coreBTypes.jliMethodHandlesLookupRef,
- coreBTypes.StringRef,
- coreBTypes.jliMethodTypeRef,
- ArrayBType(ObjectRef)),
- coreBTypes.jliCallSiteRef
- ).descriptor)
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
index 1b5ece772c..f423f3c7fe 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeHelpers.scala
@@ -1355,6 +1355,15 @@ object BCodeHelpers {
}
class TestOp(val op: Int) extends AnyVal {
+ import TestOp._
+ def negate = this match {
+ case EQ => NE
+ case NE => EQ
+ case LT => GE
+ case GE => LT
+ case GT => LE
+ case LE => GT
+ }
def opcodeIF = asm.Opcodes.IFEQ + op
def opcodeIFICMP = asm.Opcodes.IF_ICMPEQ + op
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
index 328a8187c8..0a95bc5e39 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeIdiomatic.scala
@@ -197,16 +197,19 @@ abstract class BCodeIdiomatic extends SubComponent {
/*
* can-multi-thread
*/
- final def genStringConcat(el: BType, pos: Position): Unit = {
- val jtype = el match {
+ def genConcat(elemType: BType, pos: Position): Unit = {
+ val paramType = elemType match {
case ct: ClassBType if ct.isSubtypeOf(StringRef).get => StringRef
case ct: ClassBType if ct.isSubtypeOf(jlStringBufferRef).get => jlStringBufferRef
case ct: ClassBType if ct.isSubtypeOf(jlCharSequenceRef).get => jlCharSequenceRef
- case rt: RefBType => ObjectRef
- case pt: PrimitiveBType => pt // Currently this ends up being boxed in erasure
+ // Don't match for `ArrayBType(CHAR)`, even though StringBuilder has such an overload:
+ // `"a" + Array('b')` should NOT be "ab", but "a[C@...".
+ case _: RefBType => ObjectRef
+ // jlStringBuilder does not have overloads for byte and short, but we can just use the int version
+ case BYTE | SHORT => INT
+ case pt: PrimitiveBType => pt
}
-
- val bt = MethodBType(List(jtype), jlStringBuilderRef)
+ val bt = MethodBType(List(paramType), jlStringBuilderRef)
invokevirtual(JavaStringBuilderClassName, "append", bt.descriptor, pos)
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
index 9dea21154f..96796b3244 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BCodeSkelBuilder.scala
@@ -437,9 +437,7 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
var varsInScope: List[Tuple2[Symbol, asm.Label]] = null // (local-var-sym -> start-of-scope)
// helpers around program-points.
- def lastInsn: asm.tree.AbstractInsnNode = {
- mnode.instructions.getLast
- }
+ def lastInsn: asm.tree.AbstractInsnNode = mnode.instructions.getLast
def currProgramPoint(): asm.Label = {
lastInsn match {
case labnode: asm.tree.LabelNode => labnode.getLabel
@@ -598,13 +596,11 @@ abstract class BCodeSkelBuilder extends BCodeHelpers {
genLoad(rhs, returnType)
rhs match {
- case Block(_, Return(_)) => ()
- case Return(_) => ()
+ case Return(_) | Block(_, Return(_)) | Throw(_) | Block(_, Throw(_)) => ()
case EmptyTree =>
globalError("Concrete method has no definition: " + dd + (
if (settings.debug) "(found: " + methSymbol.owner.info.decls.toList.mkString(", ") + ")"
- else "")
- )
+ else ""))
case _ =>
bc emitRETURN returnType
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
index 9bfa7dae27..3c2ee89b05 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
@@ -130,10 +130,31 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
*/
final def methodBTypeFromSymbol(methodSymbol: Symbol): MethodBType = {
assert(methodSymbol.isMethod, s"not a method-symbol: $methodSymbol")
+ methodBTypeFromMethodType(methodSymbol.info, methodSymbol.isClassConstructor || methodSymbol.isConstructor)
+ }
+
+ /**
+ * Builds a [[MethodBType]] for a method type.
+ */
+ final def methodBTypeFromMethodType(tpe: Type, isConstructor: Boolean): MethodBType = {
val resultType: BType =
- if (methodSymbol.isClassConstructor || methodSymbol.isConstructor) UNIT
- else typeToBType(methodSymbol.tpe.resultType)
- MethodBType(methodSymbol.tpe.paramTypes map typeToBType, resultType)
+ if (isConstructor) UNIT
+ else typeToBType(tpe.resultType)
+ MethodBType(tpe.paramTypes map typeToBType, resultType)
+ }
+
+ def bootstrapMethodArg(t: Constant, pos: Position): AnyRef = t match {
+ case Constant(mt: Type) => methodBTypeFromMethodType(transformedType(mt), isConstructor = false).toASMType
+ case c @ Constant(sym: Symbol) => staticHandleFromSymbol(sym)
+ case c @ Constant(value: String) => value
+ case c @ Constant(value) if c.isNonUnitAnyVal => c.value.asInstanceOf[AnyRef]
+ case _ => reporter.error(pos, "Unable to convert static argument of ApplyDynamic into a classfile constant: " + t); null
+ }
+
+ def staticHandleFromSymbol(sym: Symbol): asm.Handle = {
+ val owner = if (sym.owner.isModuleClass) sym.owner.linkedClassOfClass else sym.owner
+ val descriptor = methodBTypeFromMethodType(sym.info, isConstructor = false).descriptor
+ new asm.Handle(asm.Opcodes.H_INVOKESTATIC, classBTypeFromSymbol(owner).internalName, sym.name.encoded, descriptor)
}
/**
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala
index 6d322d1a34..3617f3d863 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/CoreBTypes.scala
@@ -1,6 +1,8 @@
package scala.tools.nsc
package backend.jvm
+import scala.annotation.switch
+import scala.tools.asm
import scala.tools.nsc.backend.jvm.BTypes.InternalName
/**
@@ -111,8 +113,10 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) {
lazy val jliMethodTypeRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.MethodType])
lazy val jliCallSiteRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.CallSite])
lazy val jliLambdaMetafactoryRef : ClassBType = classBTypeFromSymbol(requiredClass[java.lang.invoke.LambdaMetafactory])
- lazy val srLambdaDeserializerRef : ClassBType = classBTypeFromSymbol(requiredModule[scala.runtime.LambdaDeserializer.type].moduleClass)
lazy val srBoxesRunTimeRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.BoxesRunTime])
+ lazy val srSymbolLiteral : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.SymbolLiteral])
+ lazy val srStructuralCallSite : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.StructuralCallSite])
+ lazy val srLambdaDeserialize : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.LambdaDeserialize])
lazy val srBoxedUnitRef : ClassBType = classBTypeFromSymbol(requiredClass[scala.runtime.BoxedUnit])
private def methodNameAndType(cls: Symbol, name: Name, static: Boolean = false, filterOverload: Symbol => Boolean = _ => true): MethodNameAndType = {
@@ -265,6 +269,30 @@ class CoreBTypes[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes: BTFS) {
case _ => false
})
}
+
+ lazy val lambdaMetaFactoryBootstrapHandle =
+ new asm.Handle(asm.Opcodes.H_INVOKESTATIC,
+ coreBTypes.jliLambdaMetafactoryRef.internalName, sn.AltMetafactory.toString,
+ MethodBType(
+ List(
+ coreBTypes.jliMethodHandlesLookupRef,
+ coreBTypes.StringRef,
+ coreBTypes.jliMethodTypeRef,
+ ArrayBType(ObjectRef)),
+ coreBTypes.jliCallSiteRef
+ ).descriptor)
+
+ lazy val lambdaDeserializeBootstrapHandle =
+ new scala.tools.asm.Handle(scala.tools.asm.Opcodes.H_INVOKESTATIC,
+ coreBTypes.srLambdaDeserialize.internalName, sn.Bootstrap.toString,
+ MethodBType(
+ List(
+ coreBTypes.jliMethodHandlesLookupRef,
+ coreBTypes.StringRef,
+ coreBTypes.jliMethodTypeRef
+ ),
+ coreBTypes.jliCallSiteRef
+ ).descriptor)
}
/**
@@ -292,10 +320,10 @@ trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] {
def jiSerializableRef : ClassBType
def juHashMapRef : ClassBType
def juMapRef : ClassBType
+ def jliCallSiteRef : ClassBType
+ def jliMethodTypeRef : ClassBType
def jliSerializedLambdaRef : ClassBType
- def jliMethodHandlesRef : ClassBType
def jliMethodHandlesLookupRef : ClassBType
- def srLambdaDeserializerRef : ClassBType
def srBoxesRunTimeRef : ClassBType
def srBoxedUnitRef : ClassBType
@@ -316,6 +344,9 @@ trait CoreBTypesProxyGlobalIndependent[BTS <: BTypes] {
def tupleClassConstructors : Map[InternalName, MethodNameAndType]
def srJFunctionRefs: Set[InternalName]
+
+ def lambdaMetaFactoryBootstrapHandle : asm.Handle
+ def lambdaDeserializeBootstrapHandle : asm.Handle
}
/**
@@ -360,7 +391,6 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes:
def jliMethodTypeRef : ClassBType = _coreBTypes.jliMethodTypeRef
def jliCallSiteRef : ClassBType = _coreBTypes.jliCallSiteRef
def jliLambdaMetafactoryRef : ClassBType = _coreBTypes.jliLambdaMetafactoryRef
- def srLambdaDeserializerRef : ClassBType = _coreBTypes.srLambdaDeserializerRef
def srBoxesRunTimeRef : ClassBType = _coreBTypes.srBoxesRunTimeRef
def srBoxedUnitRef : ClassBType = _coreBTypes.srBoxedUnitRef
@@ -382,6 +412,10 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes:
def srJFunctionRefs: Set[InternalName] = _coreBTypes.srJFunctionRefs
+ def srSymbolLiteral : ClassBType = _coreBTypes.srSymbolLiteral
+ def srStructuralCallSite : ClassBType = _coreBTypes.srStructuralCallSite
+ def srLambdaDeserialize : ClassBType = _coreBTypes.srLambdaDeserialize
+
def typeOfArrayOp: Map[Int, BType] = _coreBTypes.typeOfArrayOp
// Some symbols. These references should probably be moved to Definitions.
@@ -394,4 +428,7 @@ final class CoreBTypesProxy[BTFS <: BTypesFromSymbols[_ <: Global]](val bTypes:
def BeanInfoAttr: Symbol = _coreBTypes.BeanInfoAttr
def String_valueOf: Symbol = _coreBTypes.String_valueOf
+
+ def lambdaMetaFactoryBootstrapHandle = _coreBTypes.lambdaMetaFactoryBootstrapHandle
+ def lambdaDeserializeBootstrapHandle = _coreBTypes.lambdaDeserializeBootstrapHandle
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala
index e8630c65d9..0d6ef93a26 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/analysis/BackendUtils.scala
@@ -64,15 +64,13 @@ class BackendUtils[BT <: BTypes](val btypes: BT) {
/**
* Add:
- * private static java.util.Map $deserializeLambdaCache$ = null
* private static Object $deserializeLambda$(SerializedLambda l) {
- * var cache = $deserializeLambdaCache$
- * if (cache eq null) {
- * cache = new java.util.HashMap()
- * $deserializeLambdaCache$ = cache
- * }
- * return scala.runtime.LambdaDeserializer.deserializeLambda(MethodHandles.lookup(), cache, l);
+ * return indy[scala.runtime.LambdaDeserialize.bootstrap](l)
* }
+ *
+ * We use invokedynamic here to enable caching within the deserializer without needing to
+ * host a static field in the enclosing class. This allows us to add this method to interfaces
+ * that define lambdas in default methods.
*/
def addLambdaDeserialize(classNode: ClassNode): Unit = {
val cw = classNode
@@ -83,37 +81,14 @@ class BackendUtils[BT <: BTypes](val btypes: BT) {
// stack map frames and invokes the `getCommonSuperClass` method. This method expects all
// ClassBTypes mentioned in the source code to exist in the map.
- val mapDesc = juMapRef.descriptor
val nilLookupDesc = MethodBType(Nil, jliMethodHandlesLookupRef).descriptor
val serlamObjDesc = MethodBType(jliSerializedLambdaRef :: Nil, ObjectRef).descriptor
- val lookupMapSerlamObjDesc = MethodBType(jliMethodHandlesLookupRef :: juMapRef :: jliSerializedLambdaRef :: Nil, ObjectRef).descriptor
-
- {
- val fv = cw.visitField(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, "$deserializeLambdaCache$", mapDesc, null, null)
- fv.visitEnd()
- }
{
val mv = cw.visitMethod(ACC_PRIVATE + ACC_STATIC + ACC_SYNTHETIC, "$deserializeLambda$", serlamObjDesc, null, null)
mv.visitCode()
- // javaBinaryName returns the internal name of a class. Also used in BTypesFromsymbols.classBTypeFromSymbol.
- mv.visitFieldInsn(GETSTATIC, classNode.name, "$deserializeLambdaCache$", mapDesc)
- mv.visitVarInsn(ASTORE, 1)
- mv.visitVarInsn(ALOAD, 1)
- val l0 = new Label()
- mv.visitJumpInsn(IFNONNULL, l0)
- mv.visitTypeInsn(NEW, juHashMapRef.internalName)
- mv.visitInsn(DUP)
- mv.visitMethodInsn(INVOKESPECIAL, juHashMapRef.internalName, "<init>", "()V", false)
- mv.visitVarInsn(ASTORE, 1)
- mv.visitVarInsn(ALOAD, 1)
- mv.visitFieldInsn(PUTSTATIC, classNode.name, "$deserializeLambdaCache$", mapDesc)
- mv.visitLabel(l0)
- mv.visitFieldInsn(GETSTATIC, srLambdaDeserializerRef.internalName, "MODULE$", srLambdaDeserializerRef.descriptor)
- mv.visitMethodInsn(INVOKESTATIC, jliMethodHandlesRef.internalName, "lookup", nilLookupDesc, false)
- mv.visitVarInsn(ALOAD, 1)
mv.visitVarInsn(ALOAD, 0)
- mv.visitMethodInsn(INVOKEVIRTUAL, srLambdaDeserializerRef.internalName, "deserializeLambda", lookupMapSerlamObjDesc, false)
+ mv.visitInvokeDynamicInsn("lambdaDeserialize", serlamObjDesc, lambdaDeserializeBootstrapHandle)
mv.visitInsn(ARETURN)
mv.visitEnd()
}
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 863bb2d10a..17255cb880 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
@@ -132,12 +132,13 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
(declarationClassNode, source) <- byteCodeRepository.classNodeAndSource(declarationClass): Either[OptimizerWarning, (ClassNode, Source)]
} yield {
val declarationClassBType = classBTypeFromClassNode(declarationClassNode)
- val CallsiteInfo(safeToInline, safeToRewrite, annotatedInline, annotatedNoInline, samParamTypes, warning) = analyzeCallsite(method, declarationClassBType, call.owner, source)
+ val CallsiteInfo(safeToInline, safeToRewrite, canInlineFromSource, annotatedInline, annotatedNoInline, samParamTypes, warning) = analyzeCallsite(method, declarationClassBType, call.owner, source)
Callee(
callee = method,
calleeDeclarationClass = declarationClassBType,
safeToInline = safeToInline,
safeToRewrite = safeToRewrite,
+ canInlineFromSource = canInlineFromSource,
annotatedInline = annotatedInline,
annotatedNoInline = annotatedNoInline,
samParamTypes = samParamTypes,
@@ -255,7 +256,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
/**
* Just a named tuple used as return type of `analyzeCallsite`.
*/
- private case class CallsiteInfo(safeToInline: Boolean, safeToRewrite: Boolean,
+ private case class CallsiteInfo(safeToInline: Boolean, safeToRewrite: Boolean, canInlineFromSource: Boolean,
annotatedInline: Boolean, annotatedNoInline: Boolean,
samParamTypes: IntMap[ClassBType],
warning: Option[CalleeInfoWarning])
@@ -316,20 +317,21 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
!BytecodeUtils.isConstructor(calleeMethodNode) &&
!BytecodeUtils.isNativeMethod(calleeMethodNode) &&
!BytecodeUtils.hasCallerSensitiveAnnotation(calleeMethodNode),
- safeToRewrite = canInlineFromSource && isRewritableTraitCall, // (2)
- annotatedInline = methodInlineInfo.annotatedInline,
- annotatedNoInline = methodInlineInfo.annotatedNoInline,
- samParamTypes = samParamTypes(calleeMethodNode, receiverType),
- warning = warning)
+ safeToRewrite = canInlineFromSource && isRewritableTraitCall, // (2)
+ canInlineFromSource = canInlineFromSource,
+ annotatedInline = methodInlineInfo.annotatedInline,
+ annotatedNoInline = methodInlineInfo.annotatedNoInline,
+ samParamTypes = samParamTypes(calleeMethodNode, receiverType),
+ warning = warning)
case None =>
val warning = MethodInlineInfoMissing(calleeDeclarationClassBType.internalName, calleeMethodNode.name, calleeMethodNode.desc, calleeDeclarationClassBType.info.orThrow.inlineInfo.warning)
- CallsiteInfo(false, false, false, false, IntMap.empty, Some(warning))
+ CallsiteInfo(false, false, false, false, false, IntMap.empty, Some(warning))
}
} catch {
case Invalid(noInfo: NoClassBTypeInfo) =>
val warning = MethodInlineInfoError(calleeDeclarationClassBType.internalName, calleeMethodNode.name, calleeMethodNode.desc, noInfo)
- CallsiteInfo(false, false, false, false, IntMap.empty, Some(warning))
+ CallsiteInfo(false, false, false, false, false, IntMap.empty, Some(warning))
}
}
@@ -393,7 +395,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
* gathering the information about this callee.
*/
final case class Callee(callee: MethodNode, calleeDeclarationClass: ClassBType,
- safeToInline: Boolean, safeToRewrite: Boolean,
+ safeToInline: Boolean, safeToRewrite: Boolean, canInlineFromSource: Boolean,
annotatedInline: Boolean, annotatedNoInline: Boolean,
samParamTypes: IntMap[ClassBType],
calleeInfoWarning: Option[CalleeInfoWarning]) {
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 b8547e1dc6..58054f85ad 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/ClosureOptimizer.scala
@@ -358,11 +358,13 @@ class ClosureOptimizer[BT <: BTypes](val btypes: BT) {
val callee = bodyMethod.map({
case (bodyMethodNode, bodyMethodDeclClass) =>
val bodyDeclClassType = classBTypeFromParsedClassfile(bodyMethodDeclClass)
+ val canInlineFromSource = compilerSettings.YoptInlineGlobal || bodyMethodIsBeingCompiled
Callee(
callee = bodyMethodNode,
calleeDeclarationClass = bodyDeclClassType,
- safeToInline = compilerSettings.YoptInlineGlobal || bodyMethodIsBeingCompiled,
+ safeToInline = canInlineFromSource,
safeToRewrite = false, // the lambda body method is not a trait interface method
+ canInlineFromSource = canInlineFromSource,
annotatedInline = false,
annotatedNoInline = false,
samParamTypes = callGraph.samParamTypes(bodyMethodNode, bodyDeclClassType),
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 f247e884ae..9847c9db58 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
@@ -83,7 +83,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
* True for statically resolved trait callsites that should be rewritten to the static implementation method.
*/
def doRewriteTraitCallsite(callsite: Callsite) = callsite.callee match {
- case Right(Callee(_, _, _, safeToRewrite, _, _, _, _)) => safeToRewrite
+ case Right(callee) => callee.safeToRewrite
case _ => false
}
@@ -101,7 +101,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
// If the callsite was eliminated by DCE, do nothing.
if (!callGraph.containsCallsite(callsite)) return
- val Right(Callee(callee, calleeDeclarationClass, _, _, annotatedInline, annotatedNoInline, samParamTypes, infoWarning)) = callsite.callee
+ val Right(Callee(callee, calleeDeclarationClass, _, _, canInlineFromSource, annotatedInline, annotatedNoInline, samParamTypes, infoWarning)) = callsite.callee
val traitMethodArgumentTypes = asm.Type.getArgumentTypes(callee.desc)
@@ -159,6 +159,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
calleeDeclarationClass = implClassBType,
safeToInline = true,
safeToRewrite = false,
+ canInlineFromSource = canInlineFromSource,
annotatedInline = annotatedInline,
annotatedNoInline = annotatedNoInline,
samParamTypes = staticCallSamParamTypes,
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala
index 89a768fd9c..7e35d0e7f0 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/InlinerHeuristics.scala
@@ -41,7 +41,7 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
compilingMethods.map(methodNode => {
var requests = Set.empty[InlineRequest]
callGraph.callsites(methodNode).valuesIterator foreach {
- case callsite @ Callsite(_, _, _, Right(Callee(callee, calleeDeclClass, safeToInline, _, calleeAnnotatedInline, _, _, callsiteWarning)), _, _, _, pos, _, _) =>
+ case callsite @ Callsite(_, _, _, Right(Callee(callee, calleeDeclClass, safeToInline, _, canInlineFromSource, calleeAnnotatedInline, _, _, callsiteWarning)), _, _, _, pos, _, _) =>
inlineRequest(callsite) match {
case Some(Right(req)) => requests += req
case Some(Left(w)) =>
@@ -52,7 +52,7 @@ class InlinerHeuristics[BT <: BTypes](val bTypes: BT) {
}
case None =>
- if (calleeAnnotatedInline && !callsite.annotatedNoInline && bTypes.compilerSettings.YoptWarningEmitAtInlineFailed) {
+ if (canInlineFromSource && calleeAnnotatedInline && !callsite.annotatedNoInline && bTypes.compilerSettings.YoptWarningEmitAtInlineFailed) {
// if the callsite is annotated @inline, we report an inline warning even if the underlying
// reason is, for example, mixed compilation (which has a separate -Yopt-warning flag).
def initMsg = s"${BackendReporting.methodSignature(calleeDeclClass.internalName, callee)} is annotated @inline but cannot be inlined"
diff --git a/src/compiler/scala/tools/nsc/transform/CleanUp.scala b/src/compiler/scala/tools/nsc/transform/CleanUp.scala
index 1d98b5da31..112dedce81 100644
--- a/src/compiler/scala/tools/nsc/transform/CleanUp.scala
+++ b/src/compiler/scala/tools/nsc/transform/CleanUp.scala
@@ -87,24 +87,6 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
/* ### CREATING THE METHOD CACHE ### */
- def addStaticVariableToClass(forName: TermName, forType: Type, forInit: Tree, isFinal: Boolean): Symbol = {
- val flags = PRIVATE | STATIC | SYNTHETIC | (
- if (isFinal) FINAL else 0
- )
-
- val varSym = currentClass.newVariable(mkTerm("" + forName), ad.pos, flags.toLong) setInfoAndEnter forType
- if (!isFinal)
- varSym.addAnnotation(VolatileAttr)
-
- val varDef = typedPos(ValDef(varSym, forInit))
- newStaticMembers append transform(varDef)
-
- val varInit = typedPos( REF(varSym) === forInit )
- newStaticInits append transform(varInit)
-
- varSym
- }
-
def addStaticMethodToClass(forBody: (Symbol, Symbol) => Tree): Symbol = {
val methSym = currentClass.newMethod(mkTerm(nme.reflMethodName.toString), ad.pos, STATIC | SYNTHETIC)
val params = methSym.newSyntheticValueParams(List(ClassClass.tpe))
@@ -115,9 +97,6 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
methSym
}
- def fromTypesToClassArrayLiteral(paramTypes: List[Type]): Tree =
- ArrayValue(TypeTree(ClassClass.tpe), paramTypes map LIT)
-
def reflectiveMethodCache(method: String, paramTypes: List[Type]): Symbol = {
/* Implementation of the cache is as follows for method "def xyz(a: A, b: B)"
(SoftReference so that it does not interfere with classloader garbage collection,
@@ -128,7 +107,7 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
var reflPoly$Cache: SoftReference[scala.runtime.MethodCache] = new SoftReference(new EmptyMethodCache())
def reflMethod$Method(forReceiver: JClass[_]): JMethod = {
- var methodCache: MethodCache = reflPoly$Cache.find(forReceiver)
+ var methodCache: StructuralCallSite = indy[StructuralCallSite.bootstrap, "(LA;LB;)Ljava/lang/Object;]
if (methodCache eq null) {
methodCache = new EmptyMethodCache
reflPoly$Cache = new SoftReference(methodCache)
@@ -137,41 +116,32 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
if (method ne null)
return method
else {
- method = ScalaRunTime.ensureAccessible(forReceiver.getMethod("xyz", reflParams$Cache))
- reflPoly$Cache = new SoftReference(methodCache.add(forReceiver, method))
+ method = ScalaRunTime.ensureAccessible(forReceiver.getMethod("xyz", methodCache.parameterTypes()))
+ methodCache.add(forReceiver, method)
return method
}
}
- */
-
- val reflParamsCacheSym: Symbol =
- addStaticVariableToClass(nme.reflParamsCacheName, arrayType(ClassClass.tpe), fromTypesToClassArrayLiteral(paramTypes), true)
-
- def mkNewPolyCache = gen.mkSoftRef(NEW(TypeTree(EmptyMethodCacheClass.tpe)))
- val reflPolyCacheSym: Symbol = addStaticVariableToClass(nme.reflPolyCacheName, SoftReferenceClass.tpe, mkNewPolyCache, false)
- def getPolyCache = gen.mkCast(fn(REF(reflPolyCacheSym), nme.get), MethodCacheClass.tpe)
+ invokedynamic is used rather than a static field for the cache to support emitting bodies of methods
+ in Java 8 interfaces, which don't support private static fields.
+ */
addStaticMethodToClass((reflMethodSym, forReceiverSym) => {
- val methodCache = reflMethodSym.newVariable(mkTerm("methodCache"), ad.pos) setInfo MethodCacheClass.tpe
+ val methodCache = reflMethodSym.newVariable(mkTerm("methodCache"), ad.pos) setInfo StructuralCallSite.tpe
val methodSym = reflMethodSym.newVariable(mkTerm("method"), ad.pos) setInfo MethodClass.tpe
+ val dummyMethodType = MethodType(NoSymbol.newSyntheticValueParams(paramTypes), AnyTpe)
BLOCK(
- ValDef(methodCache, getPolyCache),
- IF (REF(methodCache) OBJ_EQ NULL) THEN BLOCK(
- REF(methodCache) === NEW(TypeTree(EmptyMethodCacheClass.tpe)),
- REF(reflPolyCacheSym) === gen.mkSoftRef(REF(methodCache))
- ) ENDIF,
-
- ValDef(methodSym, (REF(methodCache) DOT methodCache_find)(REF(forReceiverSym))),
+ ValDef(methodCache, ApplyDynamic(gen.mkAttributedIdent(StructuralCallSite_dummy), LIT(StructuralCallSite_bootstrap) :: LIT(dummyMethodType) :: Nil).setType(StructuralCallSite.tpe)),
+ ValDef(methodSym, (REF(methodCache) DOT StructuralCallSite_find)(REF(forReceiverSym))),
IF (REF(methodSym) OBJ_NE NULL) .
THEN (Return(REF(methodSym)))
ELSE {
- def methodSymRHS = ((REF(forReceiverSym) DOT Class_getMethod)(LIT(method), REF(reflParamsCacheSym)))
- def cacheRHS = ((REF(methodCache) DOT methodCache_add)(REF(forReceiverSym), REF(methodSym)))
+ def methodSymRHS = ((REF(forReceiverSym) DOT Class_getMethod)(LIT(method), (REF(methodCache) DOT StructuralCallSite_getParameterTypes)()))
+ def cacheAdd = ((REF(methodCache) DOT StructuralCallSite_add)(REF(forReceiverSym), REF(methodSym)))
BLOCK(
REF(methodSym) === (REF(currentRun.runDefinitions.ensureAccessibleMethod) APPLY (methodSymRHS)),
- REF(reflPolyCacheSym) === gen.mkSoftRef(cacheRHS),
+ cacheAdd,
Return(REF(methodSym))
)
}
@@ -371,6 +341,8 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
reporter.error(ad.pos, "Cannot resolve overload.")
(Nil, NoType)
}
+ case NoType =>
+ abort(ad.symbol.toString)
}
typedPos {
val sym = currentOwner.newValue(mkTerm("qual"), ad.pos) setInfo qual0.tpe
@@ -448,7 +420,7 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
* refinement, where the refinement defines a parameter based on a
* type variable. */
- case tree: ApplyDynamic =>
+ case tree: ApplyDynamic if tree.symbol.owner.isRefinementClass =>
transformApplyDynamic(tree)
/* Some cleanup transformations add members to templates (classes, traits, etc).
@@ -478,46 +450,15 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
/*
* This transformation should identify Scala symbol invocations in the tree and replace them
- * with references to a static member. Also, whenever a class has at least a single symbol invocation
- * somewhere in its methods, a new static member should be created and initialized for that symbol.
- * For instance, say we have a Scala class:
- *
- * class Cls {
- * def someSymbol1 = 'Symbolic1
- * def someSymbol2 = 'Symbolic2
- * def sameSymbol1 = 'Symbolic1
- * val someSymbol3 = 'Symbolic3
- * }
- *
- * After transformation, this class looks like this:
- *
- * class Cls {
- * private <static> var symbol$1: scala.Symbol
- * private <static> var symbol$2: scala.Symbol
- * private <static> var symbol$3: scala.Symbol
- * private val someSymbol3: scala.Symbol
- *
- * private <static> def <clinit> = {
- * symbol$1 = Symbol.apply("Symbolic1")
- * symbol$2 = Symbol.apply("Symbolic2")
- * }
- *
- * private def <init> = {
- * someSymbol3 = symbol$3
- * }
- *
- * def someSymbol1 = symbol$1
- * def someSymbol2 = symbol$2
- * def sameSymbol1 = symbol$1
- * val someSymbol3 = someSymbol3
- * }
+ * with references to a statically cached instance.
*
* The reasoning behind this transformation is the following. Symbols get interned - they are stored
* in a global map which is protected with a lock. The reason for this is making equality checks
* quicker. But calling Symbol.apply, although it does return a unique symbol, accesses a locked object,
* making symbol access slow. To solve this, the unique symbol from the global symbol map in Symbol
- * is accessed only once during class loading, and after that, the unique symbol is in the static
- * member. Hence, it is cheap to both reach the unique symbol and do equality checks on it.
+ * is accessed only once during class loading, and after that, the unique symbol is in the statically
+ * initialized call site returned by invokedynamic. Hence, it is cheap to both reach the unique symbol
+ * and do equality checks on it.
*
* And, finally, be advised - Scala's Symbol literal (scala.Symbol) and the Symbol class of the compiler
* have little in common.
@@ -525,15 +466,7 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
case Apply(fn @ Select(qual, _), (arg @ Literal(Constant(symname: String))) :: Nil)
if treeInfo.isQualifierSafeToElide(qual) && fn.symbol == Symbol_apply && !currentClass.isTrait =>
- def transformApply = {
- // add the symbol name to a map if it's not there already
- val rhs = gen.mkMethodCall(Symbol_apply, arg :: Nil)
- val staticFieldSym = getSymbolStaticField(tree.pos, symname, rhs, tree)
- // create a reference to a static field
- val ntree = typedWithPos(tree.pos)(REF(staticFieldSym))
- super.transform(ntree)
- }
- transformApply
+ super.transform(treeCopy.ApplyDynamic(tree, atPos(fn.pos)(Ident(SymbolLiteral_dummy).setType(SymbolLiteral_dummy.info)), LIT(SymbolLiteral_bootstrap) :: arg :: Nil))
// Replaces `Array(Predef.wrapArray(ArrayValue(...).$asInstanceOf[...]), <tag>)`
// with just `ArrayValue(...).$asInstanceOf[...]`
@@ -550,32 +483,6 @@ abstract class CleanUp extends Statics with Transform with ast.TreeDSL {
super.transform(tree)
}
- /* Returns the symbol and the tree for the symbol field interning a reference to a symbol 'synmname'.
- * If it doesn't exist, i.e. the symbol is encountered the first time,
- * it creates a new static field definition and initialization and returns it.
- */
- private def getSymbolStaticField(pos: Position, symname: String, rhs: Tree, tree: Tree): Symbol = {
- symbolsStoredAsStatic.getOrElseUpdate(symname, {
- val theTyper = typer.atOwner(tree, currentClass)
-
- // create a symbol for the static field
- val stfieldSym = (
- currentClass.newVariable(mkTerm("symbol$"), pos, PRIVATE | STATIC | SYNTHETIC | FINAL)
- setInfoAndEnter SymbolClass.tpe
- )
-
- // create field definition and initialization
- val stfieldDef = theTyper.typedPos(pos)(ValDef(stfieldSym, rhs))
- val stfieldInit = theTyper.typedPos(pos)(REF(stfieldSym) === rhs)
-
- // add field definition to new defs
- newStaticMembers append stfieldDef
- newStaticInits append stfieldInit
-
- stfieldSym
- })
- }
-
} // CleanUpTransformer
}
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index 266a422c53..0e44751a3f 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -1150,6 +1150,8 @@ abstract class Erasure extends AddInterfaces
case DefDef(_, _, _, _, tpt, _) =>
try super.transform(tree1).clearType()
finally tpt setType specialErasure(tree1.symbol)(tree1.symbol.tpe).resultType
+ case ApplyDynamic(qual, Literal(Constant(boostrapMethodRef: Symbol)) :: _) =>
+ tree
case _ =>
super.transform(tree1).clearType()
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
index cafea55b4d..9261d6b851 100644
--- a/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/RefChecks.scala
@@ -1154,11 +1154,13 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
def toConstructor(pos: Position, tpe: Type): Tree = {
val rtpe = tpe.finalResultType
assert(rtpe.typeSymbol hasFlag CASE, tpe)
- localTyper.typedOperator {
+ val tree = localTyper.typedOperator {
atPos(pos) {
Select(New(TypeTree(rtpe)), rtpe.typeSymbol.primaryConstructor)
}
}
+ checkUndesiredProperties(rtpe.typeSymbol, tree.pos)
+ tree
}
override def transformStats(stats: List[Tree], exprOwner: Symbol): List[Tree] = {
@@ -1529,11 +1531,20 @@ abstract class RefChecks extends InfoTransform with scala.reflect.internal.trans
!tree.tpe.resultType.typeSymbol.primaryConstructor.isLessAccessibleThan(tree.symbol)
if (doTransform) {
+ def loop(t: Tree): Unit = t match {
+ case Ident(_) =>
+ checkUndesiredProperties(t.symbol, t.pos)
+ case Select(qual, _) =>
+ checkUndesiredProperties(t.symbol, t.pos)
+ loop(qual)
+ case _ =>
+ }
tree foreach {
case i@Ident(_) =>
enterReference(i.pos, i.symbol) // SI-5390 need to `enterReference` for `a` in `a.B()`
case _ =>
}
+ loop(tree)
toConstructor(tree.pos, tree.tpe)
}
else {
diff --git a/src/intellij/scala-build.iml.SAMPLE b/src/intellij/scala-build.iml.SAMPLE
new file mode 100644
index 0000000000..bf722e464f
--- /dev/null
+++ b/src/intellij/scala-build.iml.SAMPLE
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.id="scalaz-build" external.linked.project.path="$MODULE_DIR$/../../project" external.root.project.path="$MODULE_DIR$/../.." external.system.id="SBT" sbt.imports="sbt._, Keys._, dsl._, _root_.com.typesafe.sbt.SbtPgp.autoImport._, _root_.sbt.plugins.IvyPlugin, _root_.sbt.plugins.JvmPlugin, _root_.sbt.plugins.CorePlugin, _root_.sbt.plugins.JUnitXmlReportPlugin, _root_.com.typesafe.sbt.SbtPgp" sbt.resolvers="https://repo1.maven.org/maven2/|maven|public, /Users/jason/.ivy2/cache|ivy|Local cache" type="SBT_MODULE" version="4">
+ <component name="NewModuleRootManager" inherit-compiler-output="false">
+ <output url="file://$MODULE_DIR$/../../project/target/idea-classes" />
+ <output-test url="file://$MODULE_DIR$/../../project/target/idea-test-classes" />
+ <exclude-output />
+ <content url="file://$MODULE_DIR$/../../project">
+ <sourceFolder url="file://$MODULE_DIR$/../../project" isTestSource="false" />
+ <excludeFolder url="file://$MODULE_DIR$/../../project/project/target" />
+ <excludeFolder url="file://$MODULE_DIR$/../../project/target" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="module-library">
+ <library name="SBT: sbt-and-plugins">
+ <CLASSES>
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.commons/commons-lang3/jars/commons-lang3-3.3.2.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.pantsbuild/jarjar/jars/jarjar-1.6.0.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant/jars/ant-1.9.6.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.ant/ant-launcher/jars/ant-launcher-1.9.6.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.ow2.asm/asm/jars/asm-5.0.4.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.ow2.asm/asm-commons/jars/asm-commons-5.0.4.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.ow2.asm/asm-tree/jars/asm-tree-5.0.4.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.maven/maven-plugin-api/jars/maven-plugin-api-3.3.3.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.maven/maven-model/jars/maven-model-3.3.3.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.codehaus.plexus/plexus-utils/jars/plexus-utils-3.0.20.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.apache.maven/maven-artifact/jars/maven-artifact-3.3.3.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.eclipse.sisu/org.eclipse.sisu.plexus/eclipse-plugins/org.eclipse.sisu.plexus-0.3.0.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/javax.enterprise/cdi-api/jars/cdi-api-1.0.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/javax.annotation/jsr250-api/jars/jsr250-api-1.0.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/javax.inject/javax.inject/jars/javax.inject-1.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.eclipse.sisu/org.eclipse.sisu.inject/eclipse-plugins/org.eclipse.sisu.inject-0.3.0.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.codehaus.plexus/plexus-component-annotations/jars/plexus-component-annotations-1.5.5.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.codehaus.plexus/plexus-classworlds/bundles/plexus-classworlds-2.5.2.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/biz.aQute.bnd/biz.aQute.bnd/jars/biz.aQute.bnd-2.4.1.jar!/" />
+ <root url="jar://$USER_HOME$/.sbt/boot/scala-2.10.5/lib/scala-library.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/sbt/jars/sbt-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/main/jars/main-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/actions/jars/actions-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/classpath/jars/classpath-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.sbt/boot/scala-2.10.5/lib/scala-compiler.jar!/" />
+ <root url="jar://$USER_HOME$/.sbt/boot/scala-2.10.5/lib/scala-reflect.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/interface/jars/interface-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/io/jars/io-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/control/jars/control-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/launcher-interface/jars/launcher-interface-1.0.0-M1.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/completion/jars/completion-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/collections/jars/collections-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/jline/jline/jars/jline-2.11.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/api/jars/api-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/compiler-integration/jars/compiler-integration-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/incremental-compiler/jars/incremental-compiler-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/logging/jars/logging-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/process/jars/process-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/relation/jars/relation-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/compile/jars/compile-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/classfile/jars/classfile-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/persist/jars/persist-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-tools.sbinary/sbinary_2.10/jars/sbinary_2.10-0.4.2.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/compiler-ivy-integration/jars/compiler-ivy-integration-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/ivy/jars/ivy-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/cross/jars/cross-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt.ivy/ivy/jars/ivy-2.3.0-sbt-c5d1b95fdcc1e1007740ffbecf4eb07abc51ec93.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/com.jcraft/jsch/jars/jsch-0.1.46.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/serialization_2.10/jars/serialization_2.10-0.1.1.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang.modules/scala-pickling_2.10/jars/scala-pickling_2.10-0.10.0.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scalamacros/quasiquotes_2.10/jars/quasiquotes_2.10-2.0.1.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.json4s/json4s-core_2.10/jars/json4s-core_2.10-3.2.10.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.json4s/json4s-ast_2.10/jars/json4s-ast_2.10-3.2.10.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/com.thoughtworks.paranamer/paranamer/jars/paranamer-2.6.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.spire-math/jawn-parser_2.10/jars/jawn-parser_2.10-0.6.0.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.spire-math/json4s-support_2.10/jars/json4s-support_2.10-0.6.0.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/run/jars/run-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/task-system/jars/task-system-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/tasks/jars/tasks-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/tracking/jars/tracking-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/cache/jars/cache-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/testing/jars/testing-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/test-agent/jars/test-agent-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/test-interface/jars/test-interface-1.0.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/main-settings/jars/main-settings-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/apply-macro/jars/apply-macro-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/command/jars/command-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/logic/jars/logic-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/compiler-interface/jars/compiler-interface-bin-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/compiler-interface/jars/compiler-interface-src-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/precompiled-2_8_2/jars/compiler-interface-bin-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/precompiled-2_9_2/jars/compiler-interface-bin-0.13.9.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/precompiled-2_9_3/jars/compiler-interface-bin-0.13.9.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/main/srcs/main-0.13.9-sources.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang/scala-library/srcs/scala-library-2.10.5-sources.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang/scala-reflect/srcs/scala-reflect-2.10.5-sources.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-lang/scala-compiler/srcs/scala-compiler-2.10.5-sources.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/actions/srcs/actions-0.13.9-sources.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/sbt/srcs/sbt-0.13.9-sources.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/run/srcs/run-0.13.9-sources.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/test-interface/srcs/test-interface-1.0-sources.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/testing/srcs/testing-0.13.9-sources.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/tasks/srcs/tasks-0.13.9-sources.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/ivy/srcs/ivy-0.13.9-sources.jar!/" />
+ <root url="jar://$USER_HOME$/.ivy2/cache/org.scala-sbt/completion/srcs/completion-0.13.9-sources.jar!/" />
+ </SOURCES>
+ </library>
+ </orderEntry>
+ </component>
+</module> \ No newline at end of file
diff --git a/src/intellij/scala.ipr.SAMPLE b/src/intellij/scala.ipr.SAMPLE
index c4c6a2e44f..e88d6ef257 100644
--- a/src/intellij/scala.ipr.SAMPLE
+++ b/src/intellij/scala.ipr.SAMPLE
@@ -44,6 +44,7 @@
<module fileurl="file://$PROJECT_DIR$/reflect.iml" filepath="$PROJECT_DIR$/reflect.iml" />
<module fileurl="file://$PROJECT_DIR$/repl.iml" filepath="$PROJECT_DIR$/repl.iml" />
<module fileurl="file://$PROJECT_DIR$/scala.iml" filepath="$PROJECT_DIR$/scala.iml" />
+ <module fileurl="file://$PROJECT_DIR$/scala-build.iml" filepath="$PROJECT_DIR$/scala-build.iml" />
<module fileurl="file://$PROJECT_DIR$/scaladoc.iml" filepath="$PROJECT_DIR$/scaladoc.iml" />
<module fileurl="file://$PROJECT_DIR$/scalap.iml" filepath="$PROJECT_DIR$/scalap.iml" />
<module fileurl="file://$PROJECT_DIR$/test.iml" filepath="$PROJECT_DIR$/test.iml" />
@@ -123,4 +124,4 @@
<SOURCES />
</library>
</component>
-</project> \ No newline at end of file
+</project>
diff --git a/src/library/scala/collection/Iterator.scala b/src/library/scala/collection/Iterator.scala
index 17bb83e52e..518bba6b6d 100644
--- a/src/library/scala/collection/Iterator.scala
+++ b/src/library/scala/collection/Iterator.scala
@@ -10,7 +10,7 @@ package scala
package collection
import mutable.ArrayBuffer
-import scala.annotation.migration
+import scala.annotation.{tailrec, migration}
import immutable.Stream
/** The `Iterator` object provides various functions for creating specialized iterators.
@@ -166,8 +166,10 @@ object Iterator {
private[scala] final class ConcatIterator[+A](private[this] var current: Iterator[A], initial: Vector[() => Iterator[A]]) extends Iterator[A] {
@deprecated def this(initial: Vector[() => Iterator[A]]) = this(Iterator.empty, initial) // for binary compatibility
private[this] var queue: Vector[() => Iterator[A]] = initial
+ private[this] var currentHasNextChecked = false
// Advance current to the next non-empty iterator
// current is set to null when all iterators are exhausted
+ @tailrec
private[this] def advance(): Boolean = {
if (queue.isEmpty) {
current = null
@@ -176,20 +178,57 @@ object Iterator {
else {
current = queue.head()
queue = queue.tail
- current.hasNext || advance()
+ if (current.hasNext) {
+ currentHasNextChecked = true
+ true
+ } else advance()
}
}
- def hasNext = (current ne null) && (current.hasNext || advance())
- def next() = if (hasNext) current.next() else Iterator.empty.next()
+ def hasNext =
+ if (currentHasNextChecked) true
+ else if (current eq null) false
+ else if (current.hasNext) {
+ currentHasNextChecked = true
+ true
+ } else advance()
+ def next() =
+ if (hasNext) {
+ currentHasNextChecked = false
+ current.next()
+ } else Iterator.empty.next()
override def ++[B >: A](that: => GenTraversableOnce[B]): Iterator[B] =
new ConcatIterator(current, queue :+ (() => that.toIterator))
}
private[scala] final class JoinIterator[+A](lhs: Iterator[A], that: => GenTraversableOnce[A]) extends Iterator[A] {
+ private[this] var state = 0 // 0: lhs not checked, 1: lhs has next, 2: switched to rhs
private[this] lazy val rhs: Iterator[A] = that.toIterator
- def hasNext = lhs.hasNext || rhs.hasNext
- def next() = if (lhs.hasNext) lhs.next() else rhs.next()
+ def hasNext = state match {
+ case 0 =>
+ if (lhs.hasNext) {
+ state = 1
+ true
+ } else {
+ state = 2
+ rhs.hasNext
+ }
+ case 1 => true
+ case _ => rhs.hasNext
+ }
+ def next() = state match {
+ case 0 =>
+ if (lhs.hasNext) lhs.next()
+ else {
+ state = 2
+ rhs.next()
+ }
+ case 1 =>
+ state = 0
+ lhs.next()
+ case _ =>
+ rhs.next()
+ }
override def ++[B >: A](that: => GenTraversableOnce[B]) =
new ConcatIterator(this, Vector(() => that.toIterator))
diff --git a/src/library/scala/concurrent/Future.scala b/src/library/scala/concurrent/Future.scala
index e67e698a5c..b10aad0ecc 100644
--- a/src/library/scala/concurrent/Future.scala
+++ b/src/library/scala/concurrent/Future.scala
@@ -245,7 +245,17 @@ trait Future[+T] extends Awaitable[T] {
* this future. If this future is completed with an exception then the new
* future will also contain this exception.
*
- * $forComprehensionExamples
+ * Example:
+ *
+ * {{{
+ * val f = Future { "The future" }
+ * val g = f map { x: String => x + " is now!" }
+ * }}}
+ *
+ * Note that a for comprehension involving a `Future`
+ * may expand to include a call to `map` and or `flatMap`
+ * and `withFilter`. See [[scala.concurrent.Future#flatMap]] for an example of such a comprehension.
+ *
*
* @tparam S the type of the returned `Future`
* @param f the function which will be applied to the successful result of this `Future`
diff --git a/src/library/scala/math/BigDecimal.scala b/src/library/scala/math/BigDecimal.scala
index 371fd59a93..e769dfb8cb 100644
--- a/src/library/scala/math/BigDecimal.scala
+++ b/src/library/scala/math/BigDecimal.scala
@@ -397,7 +397,7 @@ object BigDecimal {
* @version 1.1
*/
final class BigDecimal(val bigDecimal: BigDec, val mc: MathContext)
-extends ScalaNumber with ScalaNumericConversions with Serializable {
+extends ScalaNumber with ScalaNumericConversions with Serializable with Ordered[BigDecimal] {
def this(bigDecimal: BigDec) = this(bigDecimal, BigDecimal.defaultMathContext)
import BigDecimal.RoundingMode._
import BigDecimal.{decimal, binary, exact}
@@ -537,22 +537,6 @@ extends ScalaNumber with ScalaNumericConversions with Serializable {
*/
def compare (that: BigDecimal): Int = this.bigDecimal compareTo that.bigDecimal
- /** Less-than-or-equals comparison of BigDecimals
- */
- def <= (that: BigDecimal): Boolean = compare(that) <= 0
-
- /** Greater-than-or-equals comparison of BigDecimals
- */
- def >= (that: BigDecimal): Boolean = compare(that) >= 0
-
- /** Less-than of BigDecimals
- */
- def < (that: BigDecimal): Boolean = compare(that) < 0
-
- /** Greater-than comparison of BigDecimals
- */
- def > (that: BigDecimal): Boolean = compare(that) > 0
-
/** Addition of BigDecimals
*/
def + (that: BigDecimal): BigDecimal = new BigDecimal(this.bigDecimal add that.bigDecimal, mc)
diff --git a/src/library/scala/math/BigInt.scala b/src/library/scala/math/BigInt.scala
index abc7371d9f..3ae3b9bf6c 100644
--- a/src/library/scala/math/BigInt.scala
+++ b/src/library/scala/math/BigInt.scala
@@ -109,7 +109,12 @@ object BigInt {
* @author Martin Odersky
* @version 1.0, 15/07/2003
*/
-final class BigInt(val bigInteger: BigInteger) extends ScalaNumber with ScalaNumericConversions with Serializable {
+final class BigInt(val bigInteger: BigInteger)
+ extends ScalaNumber
+ with ScalaNumericConversions
+ with Serializable
+ with Ordered[BigInt]
+{
/** Returns the hash code for this BigInt. */
override def hashCode(): Int =
if (isValidLong) unifiedPrimitiveHashcode()
@@ -176,22 +181,6 @@ final class BigInt(val bigInteger: BigInteger) extends ScalaNumber with ScalaNum
*/
def compare (that: BigInt): Int = this.bigInteger.compareTo(that.bigInteger)
- /** Less-than-or-equals comparison of BigInts
- */
- def <= (that: BigInt): Boolean = compare(that) <= 0
-
- /** Greater-than-or-equals comparison of BigInts
- */
- def >= (that: BigInt): Boolean = compare(that) >= 0
-
- /** Less-than of BigInts
- */
- def < (that: BigInt): Boolean = compare(that) < 0
-
- /** Greater-than comparison of BigInts
- */
- def > (that: BigInt): Boolean = compare(that) > 0
-
/** Addition of BigInts
*/
def + (that: BigInt): BigInt = new BigInt(this.bigInteger.add(that.bigInteger))
diff --git a/src/library/scala/runtime/LambdaDeserialize.java b/src/library/scala/runtime/LambdaDeserialize.java
new file mode 100644
index 0000000000..e239debf25
--- /dev/null
+++ b/src/library/scala/runtime/LambdaDeserialize.java
@@ -0,0 +1,29 @@
+package scala.runtime;
+
+
+import java.lang.invoke.*;
+import java.util.Arrays;
+import java.util.HashMap;
+
+public final class LambdaDeserialize {
+
+ private MethodHandles.Lookup lookup;
+ private final HashMap<String, MethodHandle> cache = new HashMap<>();
+ private final LambdaDeserializer$ l = LambdaDeserializer$.MODULE$;
+
+ private LambdaDeserialize(MethodHandles.Lookup lookup) {
+ this.lookup = lookup;
+ }
+
+ public Object deserializeLambda(SerializedLambda serialized) {
+ return l.deserializeLambda(lookup, cache, serialized);
+ }
+
+ public static CallSite bootstrap(MethodHandles.Lookup lookup, String invokedName,
+ MethodType invokedType) throws Throwable {
+ MethodType type = MethodType.fromMethodDescriptorString("(Ljava/lang/invoke/SerializedLambda;)Ljava/lang/Object;", lookup.getClass().getClassLoader());
+ MethodHandle deserializeLambda = lookup.findVirtual(LambdaDeserialize.class, "deserializeLambda", type);
+ MethodHandle exact = deserializeLambda.bindTo(new LambdaDeserialize(lookup)).asType(invokedType);
+ return new ConstantCallSite(exact);
+ }
+}
diff --git a/src/library/scala/runtime/StructuralCallSite.java b/src/library/scala/runtime/StructuralCallSite.java
new file mode 100644
index 0000000000..f73b4f08e6
--- /dev/null
+++ b/src/library/scala/runtime/StructuralCallSite.java
@@ -0,0 +1,43 @@
+package scala.runtime;
+
+
+import java.lang.invoke.*;
+import java.lang.ref.SoftReference;
+import java.lang.reflect.Method;
+
+public final class StructuralCallSite {
+
+ private Class<?>[] parameterTypes;
+ private SoftReference<MethodCache> cache = new SoftReference<>(new EmptyMethodCache());
+
+ private StructuralCallSite(MethodType callType) {
+ parameterTypes = callType.parameterArray();
+ }
+
+ public MethodCache get() {
+ MethodCache cache = this.cache.get();
+ if (cache == null) {
+ cache = new EmptyMethodCache();
+ this.cache = new SoftReference<>(cache);
+ }
+ return cache;
+ }
+
+ public Method find(Class<?> receiver) {
+ return get().find(receiver);
+ }
+
+ public Method add(Class<?> receiver, Method m) {
+ cache = new SoftReference<MethodCache>(get().add(receiver, m));
+ return m;
+ }
+ public Class<?>[] parameterTypes() {
+ return parameterTypes;
+ }
+
+ public static CallSite bootstrap(MethodHandles.Lookup lookup, String invokedName,
+ MethodType invokedType, MethodType reflectiveCallType) throws Throwable {
+ StructuralCallSite structuralCallSite = new StructuralCallSite(reflectiveCallType);
+ return new ConstantCallSite(MethodHandles.constant(StructuralCallSite.class, structuralCallSite));
+ }
+}
diff --git a/src/library/scala/runtime/SymbolLiteral.java b/src/library/scala/runtime/SymbolLiteral.java
new file mode 100644
index 0000000000..09a66c83d5
--- /dev/null
+++ b/src/library/scala/runtime/SymbolLiteral.java
@@ -0,0 +1,20 @@
+package scala.runtime;
+
+import java.lang.invoke.*;
+import java.util.regex.Pattern;
+
+public final class SymbolLiteral {
+ private SymbolLiteral() {
+ }
+
+ public static CallSite bootstrap(MethodHandles.Lookup lookup, String invokedName,
+ MethodType invokedType,
+ String value) throws Throwable {
+ ClassLoader classLoader = lookup.lookupClass().getClassLoader();
+ MethodType type = MethodType.fromMethodDescriptorString("(Ljava/lang/Object;)Ljava/lang/Object;", classLoader);
+ Class<?> symbolClass = Class.forName("scala.Symbol", false, classLoader);
+ MethodHandle factoryMethod = lookup.findStatic(symbolClass, "apply", type);
+ Object symbolValue = factoryMethod.invokeWithArguments(value);
+ return new ConstantCallSite(MethodHandles.constant(symbolClass, symbolValue));
+ }
+}
diff --git a/src/library/scala/util/Properties.scala b/src/library/scala/util/Properties.scala
index d4a5e2f0e8..7ea597eac9 100644
--- a/src/library/scala/util/Properties.scala
+++ b/src/library/scala/util/Properties.scala
@@ -105,7 +105,7 @@ private[scala] trait PropertiesTrait {
* or "version (unknown)" if it cannot be determined.
*/
val versionString = "version " + scalaPropOrElse("version.number", "(unknown)")
- val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2015, LAMP/EPFL")
+ val copyrightString = scalaPropOrElse("copyright.string", "Copyright 2002-2016, LAMP/EPFL")
/** This is the encoding to use reading in source files, overridden with -encoding.
* Note that it uses "prop" i.e. looks in the scala jar, not the system properties.
diff --git a/src/partest-extras/scala/tools/partest/ASMConverters.scala b/src/partest-extras/scala/tools/partest/ASMConverters.scala
index d990160ce8..a3d849a9c1 100644
--- a/src/partest-extras/scala/tools/partest/ASMConverters.scala
+++ b/src/partest-extras/scala/tools/partest/ASMConverters.scala
@@ -43,6 +43,23 @@ object ASMConverters {
case i: Invoke => i.name
case i => i.opcode
}
+
+ def summaryText: String = {
+ def comment(i: Instruction) = i match {
+ case j: Jump => s" /*${j.label.offset}*/"
+ case l: Label => s" /*${l.offset}*/"
+ case _ => ""
+ }
+ dropNonOp.map({
+ case i: Invoke => s""""${i.name}""""
+ case ins => opcodeToString(ins.opcode, ins.opcode) + comment(ins)
+ }).mkString("List(", ", ", ")")
+ }
+ }
+
+ def opcodeToString(op: Int, default: Any = "?"): String = {
+ import scala.tools.asm.util.Printer.OPCODES
+ if (OPCODES.isDefinedAt(op)) OPCODES(op) else default.toString
}
sealed abstract class Instruction extends Product {
@@ -50,12 +67,9 @@ object ASMConverters {
// toString such that the first field, "opcode: Int", is printed textually.
final override def toString() = {
- import scala.tools.asm.util.Printer.OPCODES
- def opString(op: Int) = if (OPCODES.isDefinedAt(op)) OPCODES(op) else "?"
val printOpcode = opcode != -1
-
productPrefix + (
- if (printOpcode) Iterator(opString(opcode)) ++ productIterator.drop(1)
+ if (printOpcode) Iterator(opcodeToString(opcode)) ++ productIterator.drop(1)
else productIterator
).mkString("(", ", ", ")")
}
diff --git a/src/reflect/scala/reflect/internal/Definitions.scala b/src/reflect/scala/reflect/internal/Definitions.scala
index a025407672..44eee5cbfd 100644
--- a/src/reflect/scala/reflect/internal/Definitions.scala
+++ b/src/reflect/scala/reflect/internal/Definitions.scala
@@ -461,6 +461,16 @@ trait Definitions extends api.StandardDefinitions {
lazy val MethodCacheClass = requiredClass[scala.runtime.MethodCache]
def methodCache_find = getMemberMethod(MethodCacheClass, nme.find_)
def methodCache_add = getMemberMethod(MethodCacheClass, nme.add_)
+ lazy val StructuralCallSite = getClassIfDefined("scala.runtime.StructuralCallSite")
+ def StructuralCallSite_bootstrap = getMemberMethod(StructuralCallSite.linkedClassOfClass, sn.Bootstrap)
+ // Marker for invokedynamic runtime.StructuralCall.bootstrap
+ lazy val StructuralCallSite_dummy = NoSymbol.newMethodSymbol(nme.apply).setInfo(NullaryMethodType(StructuralCallSite.tpe))
+ def StructuralCallSite_find = getMemberIfDefined(StructuralCallSite, nme.find_)
+ def StructuralCallSite_add = getMemberIfDefined(StructuralCallSite, nme.add_)
+ def StructuralCallSite_getParameterTypes = getMemberIfDefined(StructuralCallSite, nme.parameterTypes)
+ lazy val SymbolLiteral = getClassIfDefined("scala.runtime.SymbolLiteral")
+ def SymbolLiteral_bootstrap = getMemberIfDefined(SymbolLiteral.linkedClassOfClass, sn.Bootstrap)
+ def SymbolLiteral_dummy = NoSymbol.newMethodSymbol(nme.apply).setInfo(NullaryMethodType(SymbolModule.companionClass.tpe))
// XML
lazy val ScalaXmlTopScope = getModuleIfDefined("scala.xml.TopScope")
diff --git a/src/reflect/scala/reflect/internal/StdNames.scala b/src/reflect/scala/reflect/internal/StdNames.scala
index 80ed597fb7..48e912d291 100644
--- a/src/reflect/scala/reflect/internal/StdNames.scala
+++ b/src/reflect/scala/reflect/internal/StdNames.scala
@@ -696,6 +696,7 @@ trait StdNames {
val freshTermName: NameType = "freshTermName"
val freshTypeName: NameType = "freshTypeName"
val get: NameType = "get"
+ val parameterTypes: NameType = "parameterTypes"
val hashCode_ : NameType = "hashCode"
val hash_ : NameType = "hash"
val head : NameType = "head"
@@ -1170,6 +1171,7 @@ trait StdNames {
final val InvokeExact: TermName = newTermName("invokeExact")
final val AltMetafactory: TermName = newTermName("altMetafactory")
+ final val Bootstrap: TermName = newTermName("bootstrap")
val Boxed = immutable.Map[TypeName, TypeName](
tpnme.Boolean -> BoxedBoolean,
diff --git a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
index e5f77c322d..13874916cc 100644
--- a/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
+++ b/src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
@@ -284,6 +284,9 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
definitions.MethodClass
definitions.EmptyMethodCacheClass
definitions.MethodCacheClass
+ definitions.StructuralCallSite
+ definitions.StructuralCallSite_dummy
+ definitions.SymbolLiteral
definitions.ScalaXmlTopScope
definitions.ScalaXmlPackage
definitions.ReflectPackage
diff --git a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala
index 707d0c469f..8cd8a7ee09 100644
--- a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala
@@ -47,7 +47,8 @@ trait CommentFactoryBase { this: MemberLookupBase =>
groupDesc0: Map[String,Body] = Map.empty,
groupNames0: Map[String,Body] = Map.empty,
groupPrio0: Map[String,Body] = Map.empty,
- hideImplicitConversions0: List[Body] = List.empty
+ hideImplicitConversions0: List[Body] = List.empty,
+ shortDescription0: List[Body] = List.empty
): Comment = new Comment {
val body = body0 getOrElse Body(Seq.empty)
val authors = authors0
@@ -90,9 +91,13 @@ trait CommentFactoryBase { this: MemberLookupBase =>
}
}
+ override val shortDescription: Option[Text] = shortDescription0.lastOption collect {
+ case Body(List(Paragraph(Chain(List(Summary(Text(e))))))) if !e.trim.contains("\n") => Text(e)
+ }
+
override val hideImplicitConversions: List[String] =
hideImplicitConversions0 flatMap {
- case Body(List(Paragraph(Chain(List(Summary(Text(e))))))) if (!e.trim.contains("\n")) => List(e)
+ case Body(List(Paragraph(Chain(List(Summary(Text(e))))))) if !e.trim.contains("\n") => List(e)
case _ => List()
}
}
@@ -397,7 +402,8 @@ trait CommentFactoryBase { this: MemberLookupBase =>
groupDesc0 = allSymsOneTag(SimpleTagKey("groupdesc")),
groupNames0 = allSymsOneTag(SimpleTagKey("groupname")),
groupPrio0 = allSymsOneTag(SimpleTagKey("groupprio")),
- hideImplicitConversions0 = allTags(SimpleTagKey("hideImplicitConversion"))
+ hideImplicitConversions0 = allTags(SimpleTagKey("hideImplicitConversion")),
+ shortDescription0 = allTags(SimpleTagKey("shortDescription"))
)
for ((key, _) <- bodyTags)
diff --git a/src/scaladoc/scala/tools/nsc/doc/base/comment/Comment.scala b/src/scaladoc/scala/tools/nsc/doc/base/comment/Comment.scala
index 183297f2c3..eeb861e246 100644
--- a/src/scaladoc/scala/tools/nsc/doc/base/comment/Comment.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/base/comment/Comment.scala
@@ -21,7 +21,7 @@ abstract class Comment {
/** The main body of the comment that describes what the entity does and is. */
def body: Body
- private def closeHtmlTags(inline: Inline) = {
+ private def closeHtmlTags(inline: Inline): Inline = {
val stack = mutable.ListBuffer.empty[HtmlTag]
def scan(i: Inline) {
i match {
@@ -47,9 +47,10 @@ abstract class Comment {
Chain(List(inline) ++ stack.reverse)
}
- /** A shorter version of the body. Usually, this is the first sentence of the body. */
+ /** A shorter version of the body. Either from `@shortDescription` or the
+ * first sentence of the body. */
def short: Inline = {
- body.summary match {
+ shortDescription orElse body.summary match {
case Some(s) =>
closeHtmlTags(s)
case _ =>
@@ -126,6 +127,9 @@ abstract class Comment {
/** A list of implicit conversions to hide */
def hideImplicitConversions: List[String]
+ /** A short description used in the entity-view and search results */
+ def shortDescription: Option[Text]
+
override def toString =
body.toString + "\n" +
(authors map ("@author " + _.toString)).mkString("\n") +
diff --git a/src/scaladoc/scala/tools/nsc/doc/doclet/Indexer.scala b/src/scaladoc/scala/tools/nsc/doc/doclet/Indexer.scala
index 0cdd47182f..12fee69cca 100644
--- a/src/scaladoc/scala/tools/nsc/doc/doclet/Indexer.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/doclet/Indexer.scala
@@ -18,4 +18,4 @@ trait Indexer extends Generator with Universer {
indexField != null
}
-} \ No newline at end of file
+}
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/Doclet.scala b/src/scaladoc/scala/tools/nsc/doc/html/Doclet.scala
index 21c5f6bb67..cad7cf3298 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/Doclet.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/html/Doclet.scala
@@ -3,7 +3,8 @@
* @author David Bernard, Manohar Jonnalagedda
*/
-package scala.tools.nsc.doc
+package scala.tools.nsc
+package doc
package html
import doclet._
@@ -13,7 +14,7 @@ import doclet._
class Doclet extends Generator with Universer with Indexer {
def generateImpl() {
- new html.HtmlFactory(universe, index).generate()
+ new html.HtmlFactory(universe, index, new ScalaDocReporter(universe.settings)).generate()
}
}
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala b/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala
index 6076b1708c..65c540d4c5 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/html/HtmlFactory.scala
@@ -16,7 +16,8 @@ import page.diagram._
/** A class that can generate Scaladoc sites to some fixed root folder.
* @author David Bernard
* @author Gilles Dubochet */
-class HtmlFactory(val universe: doc.Universe, index: doc.Index) {
+class HtmlFactory(val universe: doc.Universe, index: doc.Index, val reporter: ScalaDocReporter) {
+ import page.{IndexScript, EntityPage}
/** The character encoding to be used for generated Scaladoc sites.
* This value is currently always UTF-8. */
@@ -34,6 +35,9 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) {
"trait_comp.svg",
"permalink.svg",
"abstract_type.svg",
+ "lato-v11-latin-100.eot",
+ "lato-v11-latin-100.ttf",
+ "lato-v11-latin-100.woff",
"lato-v11-latin-regular.eot",
"lato-v11-latin-regular.ttf",
"lato-v11-latin-regular.woff",
@@ -96,14 +100,15 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) {
libResources foreach (s => copyResource("lib/" + s))
- new page.Index(universe, index) writeFor this
- new page.IndexScript(universe, index) writeFor this
+ IndexScript(universe, index) writeFor this
+
if (index.hasDeprecatedMembers)
- new page.DeprecatedIndex(universe, index) writeFor this
+ new page.DeprecatedIndex(universe, index, reporter) writeFor this
try {
writeTemplates(_ writeFor this)
+
for (letter <- index.firstLetterIndex) {
- new html.page.ReferenceIndex(letter._1, index, universe) writeFor this
+ new html.page.ReferenceIndex(letter._1, index, universe, reporter) writeFor this
}
} finally {
DiagramStats.printStats(universe.settings)
@@ -117,7 +122,7 @@ class HtmlFactory(val universe: doc.Universe, index: doc.Index) {
def writeTemplate(tpl: DocTemplateEntity) {
if (!(written contains tpl)) {
val diagramGenerator: DiagramGenerator = new DotDiagramGenerator(universe.settings, universe.dotRunner)
- writeForThis(new page.Template(universe, diagramGenerator, tpl))
+ writeForThis(page.EntityPage(universe, diagramGenerator, tpl, reporter))
written += tpl
tpl.templates collect { case d: DocTemplateEntity => d } map writeTemplate
}
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala b/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala
index 16a2fc59d4..0eb90d8942 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala
@@ -26,6 +26,9 @@ abstract class HtmlPage extends Page { thisPage =>
/** The title of this page. */
protected def title: String
+ /** ScalaDoc reporter for error handling */
+ protected def reporter: ScalaDocReporter
+
/** The page description */
protected def description: String =
// unless overwritten, will display the title in a spaced format, keeping - and .
@@ -48,7 +51,7 @@ abstract class HtmlPage extends Page { thisPage =>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
- <meta name="viewport" content="width=device-width, initial-scale=1"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>{ title }</title>
<meta name="description" content={ description }/>
<meta name="keywords" content={ keywords }/>
@@ -281,7 +284,7 @@ abstract class HtmlPage extends Page { thisPage =>
}
}</span>
- def memberToUrl(template: Entity, isSelf: Boolean = true): String = {
+ private def memberToUrl(template: Entity, isSelf: Boolean = true): String = {
val (signature: Option[String], containingTemplate: TemplateEntity) = template match {
case dte: DocTemplateEntity if (!isSelf) => (Some(dte.signature), dte.inTemplate)
case dte: DocTemplateEntity => (None, dte)
@@ -289,12 +292,8 @@ abstract class HtmlPage extends Page { thisPage =>
case tpl => (None, tpl)
}
- def hashFromPath(templatePath: List[String]): String =
- ((templatePath.head.replace(".html", "") :: templatePath.tail).reverse).mkString(".")
-
- val containingTemplatePath = templateToPath(containingTemplate)
- val url = "../" * (containingTemplatePath.size - 1) + "index.html"
- val hash = hashFromPath(containingTemplatePath)
- s"$url#$hash" + signature.map("@" + _).getOrElse("")
+ val templatePath = templateToPath(containingTemplate)
+ val url = "../" * (templatePath.size - 1) + templatePath.reverse.mkString("/")
+ url + signature.map("#" + _).getOrElse("")
}
}
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/Page.scala b/src/scaladoc/scala/tools/nsc/doc/html/Page.scala
index 93950fd0a7..875d7919c2 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/Page.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/html/Page.scala
@@ -7,6 +7,7 @@ package scala
package tools.nsc.doc.html
import scala.tools.nsc.doc.model._
+import scala.tools.nsc.doc.base.comment
import java.io.{FileOutputStream, File}
import scala.reflect.NameTransformer
import java.nio.channels.Channels
@@ -78,7 +79,7 @@ abstract class Page {
}
val (file, pack) =
tpl match {
- case p: Package => ("package.html", p)
+ case p: Package => ("index.html", p)
case _ => downInner(doName(tpl), tpl)
}
file :: downPacks(pack)
@@ -100,4 +101,16 @@ abstract class Page {
}
relativize(thisPage.path.reverse, destPath.reverse).mkString("/")
}
+
+ protected def inlineToStr(inl: comment.Inline): String = inl match {
+ case comment.Chain(items) => items flatMap (inlineToStr(_)) mkString ""
+ case comment.Italic(in) => inlineToStr(in)
+ case comment.Bold(in) => inlineToStr(in)
+ case comment.Underline(in) => inlineToStr(in)
+ case comment.Monospace(in) => inlineToStr(in)
+ case comment.Text(text) => text
+ case comment.Summary(in) => inlineToStr(in)
+ case comment.EntityLink(comment.Text(text), _) => text
+ case _ => inl.toString
+ }
}
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/DeprecatedIndex.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/DeprecatedIndex.scala
index f257153bd7..e8cb58c732 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/page/DeprecatedIndex.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/html/page/DeprecatedIndex.scala
@@ -11,7 +11,9 @@ package page
import doc.model._
-class DeprecatedIndex(universe: Universe, index: doc.Index) extends HtmlPage {
+class DeprecatedIndex(universe: Universe, index: doc.Index, rep: ScalaDocReporter) extends HtmlPage {
+
+ def reporter = rep
def path = List("deprecated-list.html")
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala
new file mode 100644
index 0000000000..969e19c770
--- /dev/null
+++ b/src/scaladoc/scala/tools/nsc/doc/html/page/Entity.scala
@@ -0,0 +1,1053 @@
+/* NSC -- new Scala compiler
+ * Copyright 2007-2016 LAMP/EPFL
+ * @author David Bernard, Manohar Jonnalagedda, Felix Mulder
+ */
+
+package scala
+package tools
+package nsc
+package doc
+package html
+package page
+
+import base._
+import base.comment._
+
+import scala.collection.mutable
+import scala.xml.{NodeSeq, Text, UnprefixedAttribute}
+import scala.language.postfixOps
+
+import model._
+import model.diagram._
+import diagram._
+
+trait EntityPage extends HtmlPage {
+ def universe: doc.Universe
+ def generator: DiagramGenerator
+ def tpl: DocTemplateEntity
+ def reporter: ScalaDocReporter
+
+ override val path = templateToPath(tpl)
+
+ def title = {
+ val s = universe.settings
+ ( if (!s.doctitle.isDefault) s.doctitle.value + " " else "" ) +
+ ( if (!s.docversion.isDefault) s.docversion.value else "" ) +
+ ( if ((!s.doctitle.isDefault || !s.docversion.isDefault) && tpl.qualifiedName != "_root_") " - " + tpl.qualifiedName else "" )
+ }
+
+ def headers =
+ <xml:group>
+ <link href={ relativeLinkTo{List("index.css", "lib")} } media="screen" type="text/css" rel="stylesheet"/>
+ <link href={ relativeLinkTo{List("template.css", "lib")} } media="screen" type="text/css" rel="stylesheet"/>
+ <link href={ relativeLinkTo{List("diagrams.css", "lib")} } media="screen" type="text/css" rel="stylesheet" id="diagrams-css" />
+ <script type="text/javascript" src={ relativeLinkTo{List("jquery.js", "lib")} }></script>
+ <script type="text/javascript" src={ relativeLinkTo{List("jquery.panzoom.min.js", "lib")} }></script>
+ <script type="text/javascript" src={ relativeLinkTo{List("jquery.mousewheel.min.js", "lib")} }></script>
+ <script type="text/javascript" src={ relativeLinkTo{List("index.js", "lib")} }></script>
+ <script type="text/javascript" src={ relativeLinkTo{List("index.js")} }></script>
+ <script type="text/javascript" src={ relativeLinkTo{List("scheduler.js", "lib")} }></script>
+ <script type="text/javascript" src={ relativeLinkTo{List("template.js", "lib")} }></script>
+ <script type="text/javascript" src={ relativeLinkTo{List("tools.tooltip.js", "lib")} }></script>
+ { if (universe.settings.docDiagrams.value) {
+ <script type="text/javascript" src={ relativeLinkTo{List("modernizr.custom.js", "lib")} }></script>
+ <script type="text/javascript" src={ relativeLinkTo{List("diagrams.js", "lib")} } id="diagrams-js"></script>
+ } else NodeSeq.Empty }
+ <script type="text/javascript">
+ /* this variable can be used by the JS to determine the path to the root document */
+ var toRoot = '{ val p = templateToPath(tpl); "../" * (p.size - 1) }';
+ </script>
+ </xml:group>
+
+ def body =
+ <body>
+ { search }
+ <div id="search-results">
+ <div id="search-progress">
+ <div id="progress-fill"></div>
+ </div>
+ <div id="results-content">
+ <div id="entity-results"></div>
+ <div id="member-results"></div>
+ </div>
+ </div>
+ <div id="content-container" style="-webkit-overflow-scrolling: touch;">
+ <div id="content">
+ { content }
+ </div>
+ </div>
+ </body>
+
+ def search =
+ <div id="search">
+ <span id="doc-title">
+ {universe.settings.doctitle.value}
+ <span id="doc-version">
+ {
+ val version = universe.settings.docversion.value
+
+ if (version.length > "XX.XX.XX-XXX".length) {
+ reporter.warning(null,
+ s"doc-version ($version) is too long to be displayed in the webview")
+ ""
+ } else version
+ }
+ </span>
+ </span>
+ <span class="close-results"><span class="left">&lt;</span> Back</span>
+ <div id="textfilter">
+ <span class="input">
+ <input autocapitalize="none" placeholder="Search" id="index-input" type="text" accesskey="/"/>
+ <i class="clear material-icons">&#xE14C;</i>
+ <i id="search-icon" class="material-icons">&#xE8B6;</i>
+ </span>
+ </div>
+ </div>
+
+ val valueMembers =
+ tpl.methods ++ tpl.values ++ tpl.templates.filter(x => x.isObject || x.isPackage) sorted
+
+ val (absValueMembers, nonAbsValueMembers) =
+ valueMembers partition (_.isAbstract)
+
+ val (deprValueMembers, nonDeprValueMembers) =
+ nonAbsValueMembers partition (_.deprecation.isDefined)
+
+ val (concValueMembers, shadowedImplicitMembers) =
+ nonDeprValueMembers partition (!_.isShadowedOrAmbiguousImplicit)
+
+ val typeMembers =
+ tpl.abstractTypes ++ tpl.aliasTypes ++ tpl.templates.filter(x => x.isTrait || x.isClass) sorted (implicitly[Ordering[MemberEntity]])
+
+ val constructors = (tpl match {
+ case cls: Class => (cls.constructors: List[MemberEntity]).sorted
+ case _ => Nil
+ })
+
+ /* for body, there is a special case for AnyRef, otherwise AnyRef appears
+ * like a package/object this problem should be fixed, this implementation
+ * is just a patch. */
+ val content = {
+ val templateName = if (tpl.isRootPackage) "root package" else tpl.name
+ val displayName = tpl.companion match {
+ case Some(companion) if (companion.visibility.isPublic && companion.inSource != None) =>
+ <a href={relativeLinkTo(companion)} title={docEntityKindToCompanionTitle(tpl)}>{ templateName }</a>
+ case _ =>
+ templateName
+ }
+ val owner = {
+ if (tpl.isRootPackage || tpl.inTemplate.isRootPackage)
+ NodeSeq.Empty
+ else
+ <p id="owner">{ templatesToHtml(tpl.inTemplate.toRoot.reverse.tail, scala.xml.Text(".")) }</p>
+ }
+
+ <body class={ tpl.kind + (if (tpl.isType) " type" else " value") }>
+ <div id="definition">
+ {
+ val (src, alt) = docEntityKindToBigImage(tpl)
+
+ val identifier = alt.toString.substring(0,2).toLowerCase
+
+ tpl.companion match {
+ case Some(companion) if (companion.visibility.isPublic && companion.inSource != None) =>
+ <a href={relativeLinkTo(companion)} title={docEntityKindToCompanionTitle(tpl)}><div class={s"big-circle companion $identifier"}>{ identifier.substring(0,1) }</div></a>
+ case _ =>
+ <div class={ "big-circle " + alt.toString.toLowerCase }>{ identifier.substring(0,1) }</div>
+ }}
+ { owner }
+ <h1>{ displayName }</h1>{
+ if (tpl.isPackage) NodeSeq.Empty else <h3>{companionAndPackage(tpl)}</h3>
+ }{ permalink(tpl) }
+ { signature(tpl, isSelf = true) }
+ </div>
+
+ { memberToCommentHtml(tpl, tpl.inTemplate, isSelf = true) }
+
+ <div id="mbrsel">
+ <div class='toggle'></div>
+ <div id='memberfilter'>
+ <i class="material-icons arrow">&#xE037;</i>
+ <span class='input'>
+ <input id='mbrsel-input' placeholder='Filter members' type='text' accesskey='/'/>
+ </span>
+ <i class="clear material-icons">&#xE14C;</i>
+ </div>
+ <div id='filterby'>
+ <div id="order">
+ <span class="filtertype">Ordering</span>
+ <ol>
+ {
+ if (!universe.settings.docGroups.value || (tpl.members.map(_.group).distinct.length == 1))
+ NodeSeq.Empty
+ else
+ <li class="group out"><span>Grouped</span></li>
+ }
+ <li class="alpha in"><span>Alphabetic</span></li>
+ {
+ if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty)
+ NodeSeq.Empty
+ else
+ <li class="inherit out"><span>By Inheritance</span></li>
+ }
+ </ol>
+ </div>
+ { if (tpl.linearizationTemplates.isEmpty && tpl.conversions.isEmpty) NodeSeq.Empty else
+ {
+ if (!tpl.linearizationTemplates.isEmpty)
+ <div class="ancestors">
+ <span class="filtertype">Inherited<br/>
+ </span>
+ <ol id="linearization">
+ { (tpl :: tpl.linearizationTemplates).map(wte => <li class="in" name={ wte.qualifiedName }><span>{ wte.name }</span></li>) }
+ </ol>
+ </div>
+ else NodeSeq.Empty
+ } ++ {
+ if (!tpl.conversions.isEmpty)
+ <div class="ancestors">
+ <span class="filtertype">Implicitly<br/>
+ </span>
+ <ol id="implicits"> {
+ tpl.conversions.map { conv =>
+ val name = conv.conversionQualifiedName
+ val hide = universe.settings.hiddenImplicits(name)
+ <li class="in" name={ name } data-hidden={ hide.toString }><span>{ "by " + conv.conversionShortName }</span></li>
+ }
+ }
+ </ol>
+ </div>
+ else NodeSeq.Empty
+ } ++
+ <div class="ancestors">
+ <span class="filtertype"></span>
+ <ol>
+ <li class="hideall out"><span>Hide All</span></li>
+ <li class="showall in"><span>Show All</span></li>
+ </ol>
+ </div>
+ }
+ {
+ <div id="visbl">
+ <span class="filtertype">Visibility</span>
+ <ol><li class="public in"><span>Public</span></li><li class="all out"><span>All</span></li></ol>
+ </div>
+ }
+ </div>
+ </div>
+
+ <div id="template">
+ <div id="allMembers">
+ { if (constructors.isEmpty) NodeSeq.Empty else
+ <div id="constructors" class="members">
+ <h3>Instance Constructors</h3>
+ <ol>{ constructors map (memberToHtml(_, tpl)) }</ol>
+ </div>
+ }
+
+ { if (typeMembers.isEmpty) NodeSeq.Empty else
+ <div id="types" class="types members">
+ <h3>Type Members</h3>
+ <ol>{ typeMembers map (memberToHtml(_, tpl)) }</ol>
+ </div>
+ }
+
+ { if (absValueMembers.isEmpty) NodeSeq.Empty else
+ <div class="values members">
+ <h3>Abstract Value Members</h3>
+ <ol>{ absValueMembers map (memberToHtml(_, tpl)) }</ol>
+ </div>
+ }
+
+ { if (concValueMembers.isEmpty) NodeSeq.Empty else
+ <div class="values members">
+ <h3>{ if (absValueMembers.isEmpty) "Value Members" else "Concrete Value Members" }</h3>
+ <ol>{ concValueMembers map (memberToHtml(_, tpl)) }</ol>
+ </div>
+ }
+
+ { if (shadowedImplicitMembers.isEmpty) NodeSeq.Empty else
+ <div class="values members">
+ <h3>Shadowed Implicit Value Members</h3>
+ <ol>{ shadowedImplicitMembers map (memberToHtml(_, tpl)) }</ol>
+ </div>
+ }
+
+ { if (deprValueMembers.isEmpty) NodeSeq.Empty else
+ <div class="values members">
+ <h3>Deprecated Value Members</h3>
+ <ol>{ deprValueMembers map (memberToHtml(_, tpl)) }</ol>
+ </div>
+ }
+ </div>
+
+ <div id="inheritedMembers">
+ {
+ // linearization
+ NodeSeq fromSeq (for ((superTpl, superType) <- (tpl.linearizationTemplates zip tpl.linearizationTypes)) yield
+ <div class="parent" name={ superTpl.qualifiedName }>
+ <h3>Inherited from {
+ typeToHtmlWithStupidTypes(tpl, superTpl, superType)
+ }</h3>
+ </div>
+ )
+ }
+ {
+ // implicitly inherited
+ NodeSeq fromSeq (for (conversion <- (tpl.conversions)) yield
+ <div class="conversion" name={ conversion.conversionQualifiedName }>
+ <h3>Inherited by implicit conversion { conversion.conversionShortName } from
+ { typeToHtml(tpl.resultType, hasLinks = true) } to { typeToHtml(conversion.targetType, hasLinks = true) }
+ </h3>
+ </div>
+ )
+ }
+ </div>
+
+ <div id="groupedMembers">
+ {
+ val allGroups = tpl.members.map(_.group).distinct
+ val orderedGroups = allGroups.map(group => (tpl.groupPriority(group), group)).sorted.map(_._2)
+ // linearization
+ NodeSeq fromSeq (for (group <- orderedGroups) yield
+ <div class="group" name={ group }>
+ <h3>{ tpl.groupName(group) }</h3>
+ {
+ tpl.groupDescription(group) match {
+ case Some(body) => <div class="comment cmt">{ bodyToHtml(body) }</div>
+ case _ => NodeSeq.Empty
+ }
+ }
+ </div>
+ )
+ }
+ </div>
+
+ </div>
+
+ <div id="tooltip" ></div>
+
+ {
+ if (Set("epfl", "EPFL").contains(tpl.universe.settings.docfooter.value))
+ <div id="footer">Scala programming documentation. Copyright (c) 2003-2016 <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>
+ }
+ </body>
+ }
+
+ def memberToHtml(mbr: MemberEntity, inTpl: DocTemplateEntity): NodeSeq = {
+ // Sometimes it's same, do we need signatureCompat still?
+ val sig = if (mbr.signature == mbr.signatureCompat) {
+ <a id={ mbr.signature }/>
+ } else {
+ <a id={ mbr.signature }/><a id={ mbr.signatureCompat }/>
+ }
+
+ val memberComment = memberToCommentHtml(mbr, inTpl, isSelf = false)
+ <li name={ mbr.definitionName } visbl={ if (mbr.visibility.isProtected) "prt" else "pub" }
+ data-isabs={ mbr.isAbstract.toString }
+ fullComment={ if(memberComment.filter(_.label=="div").isEmpty) "no" else "yes" }
+ group={ mbr.group }>
+ { sig }
+ { signature(mbr, isSelf = false) }
+ { memberComment }
+ </li>
+ }
+
+ def memberToCommentHtml(mbr: MemberEntity, inTpl: DocTemplateEntity, isSelf: Boolean): NodeSeq = {
+ mbr match {
+ case dte: DocTemplateEntity if isSelf =>
+ // comment of class itself
+ <xml:group>
+ <div id="comment" class="fullcommenttop">{ memberToCommentBodyHtml(mbr, inTpl, isSelf = true) }</div>
+ </xml:group>
+ case _ =>
+ // comment of non-class member or non-documentented inner class
+ val commentBody = memberToCommentBodyHtml(mbr, inTpl, isSelf = false)
+ if (commentBody.isEmpty)
+ NodeSeq.Empty
+ else {
+ val shortComment = memberToShortCommentHtml(mbr, isSelf)
+ val longComment = memberToUseCaseCommentHtml(mbr, isSelf) ++ memberToCommentBodyHtml(mbr, inTpl, isSelf)
+
+ val includedLongComment = if (shortComment.text.trim == longComment.text.trim)
+ NodeSeq.Empty
+ else
+ <div class="fullcomment">{ longComment }</div>
+
+ shortComment ++ includedLongComment
+ }
+ }
+ }
+
+ def memberToUseCaseCommentHtml(mbr: MemberEntity, isSelf: Boolean): NodeSeq = {
+ mbr match {
+ case nte: NonTemplateMemberEntity if nte.isUseCase =>
+ inlineToHtml(comment.Text("[use case] "))
+ case _ => NodeSeq.Empty
+ }
+ }
+
+ def memberToShortCommentHtml(mbr: MemberEntity, isSelf: Boolean): NodeSeq =
+ mbr.comment.fold(NodeSeq.Empty) { comment =>
+ <p class="shortcomment cmt">{ memberToUseCaseCommentHtml(mbr, isSelf) }{ inlineToHtml(comment.short) }</p>
+ }
+
+ def memberToInlineCommentHtml(mbr: MemberEntity, isSelf: Boolean): NodeSeq =
+ <p class="comment cmt">{ inlineToHtml(mbr.comment.get.short) }</p>
+
+ def memberToCommentBodyHtml(mbr: MemberEntity, inTpl: DocTemplateEntity, isSelf: Boolean, isReduced: Boolean = false): NodeSeq = {
+ val s = universe.settings
+
+ val memberComment =
+ if (mbr.comment.isEmpty) NodeSeq.Empty
+ else <div class="comment cmt">{ commentToHtml(mbr.comment) }</div>
+
+ val authorComment =
+ if (! s.docAuthor || mbr.comment.isEmpty ||
+ mbr.comment.isDefined && mbr.comment.get.authors.isEmpty) NodeSeq.Empty
+ else <div class="comment cmt">
+ {if (mbr.comment.get.authors.size > 1) <h6>Authors:</h6> else <h6>Author:</h6>}
+ { mbr.comment.get.authors map bodyToHtml}
+ </div>
+
+ val paramComments = {
+ val prs: List[ParameterEntity] = mbr match {
+ case cls: Class => cls.typeParams ::: cls.valueParams.flatten
+ case trt: Trait => trt.typeParams
+ case dfe: Def => dfe.typeParams ::: dfe.valueParams.flatten
+ case ctr: Constructor => ctr.valueParams.flatten
+ case _ => Nil
+ }
+
+ def paramCommentToHtml(prs: List[ParameterEntity], comment: Comment): NodeSeq = prs match {
+
+ case (tp: TypeParam) :: rest =>
+ val paramEntry: NodeSeq = {
+ <dt class="tparam">{ tp.name }</dt><dd class="cmt">{ bodyToHtml(comment.typeParams(tp.name)) }</dd>
+ }
+ paramEntry ++ paramCommentToHtml(rest, comment)
+
+ case (vp: ValueParam) :: rest =>
+ val paramEntry: NodeSeq = {
+ <dt class="param">{ vp.name }</dt><dd class="cmt">{ bodyToHtml(comment.valueParams(vp.name)) }</dd>
+ }
+ paramEntry ++ paramCommentToHtml(rest, comment)
+
+ case _ =>
+ NodeSeq.Empty
+ }
+
+ mbr.comment.fold(NodeSeq.Empty) { comment =>
+ val cmtedPrs = prs filter {
+ case tp: TypeParam => comment.typeParams isDefinedAt tp.name
+ case vp: ValueParam => comment.valueParams isDefinedAt vp.name
+ }
+ if (cmtedPrs.isEmpty && comment.result.isEmpty) NodeSeq.Empty
+ else {
+ <dl class="paramcmts block">{
+ paramCommentToHtml(cmtedPrs, comment) ++ (
+ comment.result match {
+ case None => NodeSeq.Empty
+ case Some(cmt) =>
+ <dt>returns</dt><dd class="cmt">{ bodyToHtml(cmt) }</dd>
+ })
+ }</dl>
+ }
+ }
+ }
+
+ val implicitInformation = mbr.byConversion match {
+ case Some(conv) =>
+ <dt class="implicit">Implicit</dt> ++
+ {
+ val targetType = typeToHtml(conv.targetType, hasLinks = true)
+ val conversionMethod = conv.convertorMethod match {
+ case Left(member) => Text(member.name)
+ case Right(name) => Text(name)
+ }
+
+ // strip off the package object endings, they make things harder to follow
+ val conversionOwnerQualifiedNane = conv.convertorOwner.qualifiedName.stripSuffix(".package")
+ val conversionOwner = templateToHtml(conv.convertorOwner, conversionOwnerQualifiedNane)
+
+ val constraintText = conv.constraints match {
+ case Nil =>
+ NodeSeq.Empty
+ case List(constraint) =>
+ scala.xml.Text("This conversion will take place only if ") ++ constraintToHtml(constraint) ++ scala.xml.Text(".")
+ case List(constraint1, constraint2) =>
+ scala.xml.Text("This conversion will take place only if ") ++ constraintToHtml(constraint1) ++
+ scala.xml.Text(" and at the same time ") ++ constraintToHtml(constraint2) ++ scala.xml.Text(".")
+ case constraints =>
+ <br/> ++ "This conversion will take place only if all of the following constraints are met:" ++ <br/> ++ {
+ var index = 0
+ constraints map { constraint => scala.xml.Text({ index += 1; index } + ". ") ++ constraintToHtml(constraint) ++ <br/> }
+ }
+ }
+
+ <dd>
+ This member is added by an implicit conversion from { typeToHtml(inTpl.resultType, hasLinks = true) } to
+ { targetType } performed by method { conversionMethod } in { conversionOwner }.
+ { constraintText }
+ </dd>
+ } ++ {
+ if (mbr.isShadowedOrAmbiguousImplicit) {
+ // These are the members that are shadowing or ambiguating the current implicit
+ // see ImplicitMemberShadowing trait for more information
+ val shadowingSuggestion = {
+ val params = mbr match {
+ case d: Def => d.valueParams map (_ map (_ name) mkString("(", ", ", ")")) mkString
+ case _ => "" // no parameters
+ }
+ <br/> ++ scala.xml.Text("To access this member you can use a ") ++
+ <a href="http://stackoverflow.com/questions/2087250/what-is-the-purpose-of-type-ascription-in-scala"
+ target="_blank">type ascription</a> ++ scala.xml.Text(":") ++
+ <br/> ++ <div class="cmt"><pre>{"(" + Template.lowerFirstLetter(tpl.name) + ": " + conv.targetType.name + ")." + mbr.name + params }</pre></div>
+ }
+
+ val shadowingWarning: NodeSeq =
+ if (mbr.isShadowedImplicit)
+ scala.xml.Text("This implicitly inherited member is shadowed by one or more members in this " +
+ "class.") ++ shadowingSuggestion
+ else if (mbr.isAmbiguousImplicit)
+ scala.xml.Text("This implicitly inherited member is ambiguous. One or more implicitly " +
+ "inherited members have similar signatures, so calling this member may produce an ambiguous " +
+ "implicit conversion compiler error.") ++ shadowingSuggestion
+ else NodeSeq.Empty
+
+ <dt class="implicit">Shadowing</dt> ++
+ <dd>{ shadowingWarning }</dd>
+
+ } else NodeSeq.Empty
+ }
+ case _ =>
+ NodeSeq.Empty
+ }
+
+ // --- start attributes block vals
+ val attributes: NodeSeq = {
+ val fvs: List[comment.Paragraph] = visibility(mbr).toList
+ if (fvs.isEmpty || isReduced) NodeSeq.Empty
+ else {
+ <dt>Attributes</dt>
+ <dd>{ fvs map { fv => { inlineToHtml(fv.text) ++ scala.xml.Text(" ") } } }</dd>
+ }
+ }
+
+ val definitionClasses: NodeSeq = {
+ val inDefTpls = mbr.inDefinitionTemplates
+ if ((inDefTpls.tail.isEmpty && (inDefTpls.head == inTpl)) || isReduced) NodeSeq.Empty
+ else {
+ <dt>Definition Classes</dt>
+ <dd>{ templatesToHtml(inDefTpls, scala.xml.Text(" → ")) }</dd>
+ }
+ }
+
+ val fullSignature: NodeSeq = {
+ mbr match {
+ case nte: NonTemplateMemberEntity if nte.isUseCase =>
+ <div class="full-signature-block toggleContainer">
+ <span class="toggle">
+ <i class="material-icons">&#xE037;</i>
+ Full Signature
+ </span>
+ <div class="hiddenContent full-signature-usecase">{ signature(nte.useCaseOf.get,isSelf = true) }</div>
+ </div>
+ case _ => NodeSeq.Empty
+ }
+ }
+
+ val selfType: NodeSeq = mbr match {
+ case dtpl: DocTemplateEntity if (isSelf && !dtpl.selfType.isEmpty && !isReduced) =>
+ <dt>Self Type</dt>
+ <dd>{ typeToHtml(dtpl.selfType.get, hasLinks = true) }</dd>
+ case _ => NodeSeq.Empty
+ }
+
+ val annotations: NodeSeq = {
+ // A list of annotations which don't show their arguments, e. g. because they are shown separately.
+ val annotationsWithHiddenArguments = List("deprecated", "Deprecated", "migration")
+
+ def showArguments(annotation: Annotation) =
+ !(annotationsWithHiddenArguments.contains(annotation.qualifiedName))
+
+ if (!mbr.annotations.isEmpty) {
+ <dt>Annotations</dt>
+ <dd>{
+ mbr.annotations.map { annot =>
+ <xml:group>
+ <span class="name">@{ templateToHtml(annot.annotationClass) }</span>{
+ if (showArguments(annot)) argumentsToHtml(annot.arguments) else NodeSeq.Empty
+ }
+ </xml:group>
+ }
+ }
+ </dd>
+ } else NodeSeq.Empty
+ }
+
+ val sourceLink: NodeSeq = mbr match {
+ case dtpl: DocTemplateEntity if (isSelf && dtpl.sourceUrl.isDefined && dtpl.inSource.isDefined && !isReduced) =>
+ val (absFile, _) = dtpl.inSource.get
+ <dt>Source</dt>
+ <dd>{ <a href={ dtpl.sourceUrl.get.toString } target="_blank">{ Text(absFile.file.getName) }</a> }</dd>
+ case _ => NodeSeq.Empty
+ }
+
+ val deprecation: NodeSeq =
+ mbr.deprecation match {
+ case Some(deprecation) if !isReduced =>
+ <dt>Deprecated</dt>
+ <dd class="cmt">{ bodyToHtml(deprecation) }</dd>
+ case _ => NodeSeq.Empty
+ }
+
+ val migration: NodeSeq =
+ mbr.migration match {
+ case Some(migration) if !isReduced =>
+ <dt>Migration</dt>
+ <dd class="cmt">{ bodyToHtml(migration) }</dd>
+ case _ => NodeSeq.Empty
+ }
+
+ val mainComment: NodeSeq = mbr.comment match {
+ case Some(comment) if (! isReduced) =>
+ def orEmpty[T](it: Iterable[T])(gen: =>NodeSeq): NodeSeq =
+ if (it.isEmpty) NodeSeq.Empty else gen
+
+ val example =
+ orEmpty(comment.example) {
+ <div class="block">Example{ if (comment.example.length > 1) "s" else ""}:
+ <ol>{
+ val exampleXml: List[NodeSeq] = for (ex <- comment.example) yield
+ <li class="cmt">{ bodyToHtml(ex) }</li>
+ exampleXml.reduceLeft(_ ++ Text(", ") ++ _)
+ }</ol>
+ </div>
+ }
+
+ val version: NodeSeq =
+ orEmpty(comment.version) {
+ <dt>Version</dt>
+ <dd>{ for(body <- comment.version.toList) yield bodyToHtml(body) }</dd>
+ }
+
+ val sinceVersion: NodeSeq =
+ orEmpty(comment.since) {
+ <dt>Since</dt>
+ <dd>{ for(body <- comment.since.toList) yield bodyToHtml(body) }</dd>
+ }
+
+ val note: NodeSeq =
+ orEmpty(comment.note) {
+ <dt>Note</dt>
+ <dd>{
+ val noteXml: List[NodeSeq] = for(note <- comment.note ) yield <span class="cmt">{bodyToHtml(note)}</span>
+ noteXml.reduceLeft(_ ++ Text(", ") ++ _)
+ }</dd>
+ }
+
+ val seeAlso: NodeSeq =
+ orEmpty(comment.see) {
+ <dt>See also</dt>
+ <dd>{
+ val seeXml: List[NodeSeq] = for(see <- comment.see ) yield <span class="cmt">{bodyToHtml(see)}</span>
+ seeXml.reduceLeft(_ ++ _)
+ }</dd>
+ }
+
+ val exceptions: NodeSeq =
+ orEmpty(comment.throws) {
+ <dt>Exceptions thrown</dt>
+ <dd>{
+ val exceptionsXml: List[NodeSeq] =
+ for((name, body) <- comment.throws.toList.sortBy(_._1) ) yield
+ <span class="cmt">{bodyToHtml(body)}</span>
+ exceptionsXml.reduceLeft(_ ++ Text("") ++ _)
+ }</dd>
+ }
+
+ val todo: NodeSeq =
+ orEmpty(comment.todo) {
+ <dt>To do</dt>
+ <dd>{
+ val todoXml: List[NodeSeq] = (for(todo <- comment.todo ) yield <span class="cmt">{bodyToHtml(todo)}</span> )
+ todoXml.reduceLeft(_ ++ _)
+ }</dd>
+ }
+
+ example ++ version ++ sinceVersion ++ exceptions ++ todo ++ note ++ seeAlso
+
+ case _ => NodeSeq.Empty
+ }
+ // end attributes block vals ---
+
+ val attributesInfo = implicitInformation ++ attributes ++ definitionClasses ++ fullSignature ++ selfType ++ annotations ++ deprecation ++ migration ++ sourceLink ++ mainComment
+ val attributesBlock =
+ if (attributesInfo.isEmpty)
+ NodeSeq.Empty
+ else
+ <dl class="attributes block"> { attributesInfo }</dl>
+
+ val linearization = mbr match {
+ case dtpl: DocTemplateEntity if isSelf && !isReduced && dtpl.linearizationTemplates.nonEmpty =>
+ <div class="toggleContainer block">
+ <span class="toggle">
+ <i class="material-icons">&#xE037;</i>
+ Linear Supertypes
+ </span>
+ <div class="superTypes hiddenContent">{
+ typesToHtml(dtpl.linearizationTypes, hasLinks = true, sep = scala.xml.Text(", "))
+ }</div>
+ </div>
+ case _ => NodeSeq.Empty
+ }
+
+ val subclasses = mbr match {
+ case dtpl: DocTemplateEntity if isSelf && !isReduced =>
+ val subs = mutable.HashSet.empty[DocTemplateEntity]
+ def transitive(dtpl: DocTemplateEntity) {
+ for (sub <- dtpl.directSubClasses if !(subs contains sub)) {
+ subs add sub
+ transitive(sub)
+ }
+ }
+ transitive(dtpl)
+ if (subs.nonEmpty)
+ <div class="toggleContainer block">
+ <span class="toggle">
+ <i class="material-icons">&#xE037;</i>
+ Known Subclasses
+ </span>
+ <div class="subClasses hiddenContent">{
+ templatesToHtml(subs.toList.sorted(Entity.EntityOrdering), scala.xml.Text(", "))
+ }</div>
+ </div>
+ else NodeSeq.Empty
+ case _ => NodeSeq.Empty
+ }
+
+ def createDiagram(f: DocTemplateEntity => Option[Diagram], description: String, id: String): NodeSeq =
+ if (s.docDiagrams.value) mbr match {
+ case dtpl: DocTemplateEntity if isSelf && !isReduced =>
+ val diagram = f(dtpl)
+ if (diagram.isDefined) {
+ val diagramSvg = generator.generate(diagram.get, tpl, this)
+ if (diagramSvg != NodeSeq.Empty) {
+ <div class="toggleContainer block diagram-container" id={ id + "-container"}>
+ <span class="toggle diagram-link">
+ <i class="material-icons">&#xE037;</i>
+ { description }
+ </span>
+ <div class="diagram" id={ id }>{ diagramSvg }</div>
+ <div id="diagram-controls" class="hiddenContent">
+ <button id="diagram-zoom-out" class="diagram-btn"><i class="material-icons">&#xE15B;</i></button>
+ <button id="diagram-zoom-in" class="diagram-btn"><i class="material-icons">&#xE145;</i></button>
+ <button title="Toggle full-screen" id="diagram-fs" class="diagram-btn to-full"><i class="material-icons">&#xE5D0;</i></button>
+ </div>
+ </div>
+ } else NodeSeq.Empty
+ } else NodeSeq.Empty
+ case _ => NodeSeq.Empty
+ } else NodeSeq.Empty // diagrams not generated
+
+ val typeHierarchy = createDiagram(_.inheritanceDiagram, "Type Hierarchy", "inheritance-diagram")
+ val contentHierarchy = createDiagram(_.contentDiagram, "Content Hierarchy", "content-diagram")
+
+ memberComment ++ authorComment ++ paramComments ++ attributesBlock ++ linearization ++ subclasses ++ typeHierarchy ++ contentHierarchy
+ }
+
+ def boundsToHtml(hi: Option[TypeEntity], lo: Option[TypeEntity], hasLinks: Boolean): NodeSeq = {
+ def bound0(bnd: Option[TypeEntity], pre: String): NodeSeq = bnd match {
+ case None => NodeSeq.Empty
+ case Some(tpe) => scala.xml.Text(pre) ++ typeToHtml(tpe, hasLinks)
+ }
+ bound0(lo, " >: ") ++ bound0(hi, " <: ")
+ }
+
+ def visibility(mbr: MemberEntity): Option[comment.Paragraph] = {
+ import comment._
+ import comment.{ Text => CText }
+ mbr.visibility match {
+ case PrivateInInstance() =>
+ Some(Paragraph(CText("private[this]")))
+ case PrivateInTemplate(owner) if (owner == mbr.inTemplate) =>
+ Some(Paragraph(CText("private")))
+ case PrivateInTemplate(owner) =>
+ Some(Paragraph(Chain(List(CText("private["), EntityLink(comment.Text(owner.qualifiedName), LinkToTpl(owner)), CText("]")))))
+ case ProtectedInInstance() =>
+ Some(Paragraph(CText("protected[this]")))
+ case ProtectedInTemplate(owner) if (owner == mbr.inTemplate) =>
+ Some(Paragraph(CText("protected")))
+ case ProtectedInTemplate(owner) =>
+ Some(Paragraph(Chain(List(CText("protected["), EntityLink(comment.Text(owner.qualifiedName), LinkToTpl(owner)), CText("]")))))
+ case Public() =>
+ None
+ }
+ }
+
+ /** name, tparams, params, result */
+ def signature(mbr: MemberEntity, isSelf: Boolean, isReduced: Boolean = false): NodeSeq = {
+
+ def inside(hasLinks: Boolean, nameLink: String = ""): NodeSeq =
+ <xml:group>
+ <span class="modifier_kind">
+ <i class="material-icons unfold-arrow">&#xE037;</i>
+ <span class="modifier">{ mbr.flags.map(flag => inlineToHtml(flag.text) ++ scala.xml.Text(" ")) }</span>
+ <span class="kind">{ kindToString(mbr) }</span>
+ </span>
+ <span class="symbol">
+ {
+ val nameClass =
+ if (mbr.isImplicitlyInherited)
+ if (mbr.isShadowedOrAmbiguousImplicit)
+ "implicit shadowed"
+ else
+ "implicit"
+ else
+ "name"
+
+ val nameHtml = {
+ val value = if (mbr.isConstructor) tpl.name else mbr.name
+ val span = if (mbr.deprecation.isDefined)
+ <span class={ nameClass + " deprecated"} title={"Deprecated: "+bodyToStr(mbr.deprecation.get)}>{ value }</span>
+ else
+ <span class={ nameClass }>{ value }</span>
+ val encoded = scala.reflect.NameTransformer.encode(value)
+ if (encoded != value) {
+ span % new UnprefixedAttribute("title",
+ "gt4s: " + encoded +
+ span.attribute("title").map(
+ node => ". " + node
+ ).getOrElse(""),
+ scala.xml.Null)
+ } else {
+ span
+ }
+ }
+ if (!nameLink.isEmpty)
+ <a href={nameLink}>{nameHtml}</a>
+ else nameHtml
+ }{
+ def tparamsToHtml(mbr: Any): NodeSeq = mbr match {
+ case hk: HigherKinded =>
+ val tpss = hk.typeParams
+ if (tpss.isEmpty) NodeSeq.Empty else {
+ def tparam0(tp: TypeParam): NodeSeq =
+ <span name={ tp.name }>{ tp.variance + tp.name }{ tparamsToHtml(tp) }{ boundsToHtml(tp.hi, tp.lo, hasLinks)}</span>
+ def tparams0(tpss: List[TypeParam]): NodeSeq = (tpss: @unchecked) match {
+ case tp :: Nil => tparam0(tp)
+ case tp :: tps => tparam0(tp) ++ Text(", ") ++ tparams0(tps)
+ }
+ <span class="tparams">[{ tparams0(tpss) }]</span>
+ }
+ case _ => NodeSeq.Empty
+ }
+ tparamsToHtml(mbr)
+ }{
+ if (isReduced) NodeSeq.Empty else {
+ def paramsToHtml(vlsss: List[List[ValueParam]]): NodeSeq = {
+ def param0(vl: ValueParam): NodeSeq =
+ // notice the }{ in the next lines, they are necessary to avoid an undesired whitespace in output
+ <span name={ vl.name }>{
+ Text(vl.name)
+ }{ Text(": ") ++ typeToHtml(vl.resultType, hasLinks) }{
+ vl.defaultValue match {
+ case Some(v) => Text(" = ") ++ treeToHtml(v)
+ case None => NodeSeq.Empty
+ }
+ }</span>
+
+ def params0(vlss: List[ValueParam]): NodeSeq = vlss match {
+ case Nil => NodeSeq.Empty
+ case vl :: Nil => param0(vl)
+ case vl :: vls => param0(vl) ++ Text(", ") ++ params0(vls)
+ }
+ def implicitCheck(vlss: List[ValueParam]): NodeSeq = vlss match {
+ case vl :: vls => if(vl.isImplicit) { <span class="implicit">implicit </span> } else Text("")
+ case _ => Text("")
+ }
+ vlsss map { vlss => <span class="params">({implicitCheck(vlss) ++ params0(vlss) })</span> }
+ }
+ mbr match {
+ case cls: Class => paramsToHtml(cls.valueParams)
+ case ctr: Constructor => paramsToHtml(ctr.valueParams)
+ case dfe: Def => paramsToHtml(dfe.valueParams)
+ case _ => NodeSeq.Empty
+ }
+ }
+ }{ if (isReduced) NodeSeq.Empty else {
+ mbr match {
+ case tme: MemberEntity if (tme.isDef || tme.isVal || tme.isLazyVal || tme.isVar) =>
+ <span class="result">: { typeToHtml(tme.resultType, hasLinks) }</span>
+
+ case abt: MemberEntity with AbstractType =>
+ val b2s = boundsToHtml(abt.hi, abt.lo, hasLinks)
+ if (b2s != NodeSeq.Empty)
+ <span class="result">{ b2s }</span>
+ else NodeSeq.Empty
+
+ case alt: MemberEntity with AliasType =>
+ <span class="result"> = { typeToHtml(alt.alias, hasLinks) }</span>
+
+ case tpl: MemberTemplateEntity if !tpl.parentTypes.isEmpty =>
+ <span class="result"> extends { typeToHtml(tpl.parentTypes.map(_._2), hasLinks) }</span>
+
+ case _ => NodeSeq.Empty
+ }
+ }}
+ </span>
+ </xml:group>
+ mbr match {
+ case dte: DocTemplateEntity if !isSelf =>
+ permalink(dte, isSelf) ++ { inside(hasLinks = true, nameLink = relativeLinkTo(dte)) }
+ case _ if isSelf =>
+ <h4 id="signature" class="signature">{ inside(hasLinks = true) }</h4>
+ case _ =>
+ permalink(mbr) ++ { inside(hasLinks = true) }
+ }
+
+ }
+
+ /** */
+ def treeToHtml(tree: TreeEntity): NodeSeq = {
+
+ /** Makes text good looking in the html page : newlines and basic indentation,
+ * You must change this function if you want to improve pretty printing of default Values
+ */
+ def codeStringToXml(text: String): NodeSeq = {
+ var goodLookingXml: NodeSeq = NodeSeq.Empty
+ var indent = 0
+ for (c <- text) c match {
+ case '{' => indent+=1
+ goodLookingXml ++= Text("{")
+ case '}' => indent-=1
+ goodLookingXml ++= Text("}")
+ case '\n' =>
+ goodLookingXml++= <br/> ++ indentation
+ case _ => goodLookingXml ++= Text(c.toString)
+ }
+ def indentation:NodeSeq = {
+ var indentXml = NodeSeq.Empty
+ for (x <- 1 to indent) indentXml ++= Text("&nbsp;&nbsp;")
+ indentXml
+ }
+ goodLookingXml
+ }
+
+ var index = 0
+ val str = tree.expression
+ val length = str.length
+ var myXml: NodeSeq = NodeSeq.Empty
+ for ((from, (member, to)) <- tree.refEntity.toSeq) {
+ if (index < from) {
+ myXml ++= codeStringToXml(str.substring(index,from))
+ index = from
+ }
+ if (index == from) {
+ member match {
+ case mbr: DocTemplateEntity =>
+ val link = relativeLinkTo(mbr)
+ myXml ++= <span class="name"><a href={link}>{str.substring(from, to)}</a></span>
+ case mbr: MemberEntity =>
+ val anchor = "#" + mbr.signature
+ val link = relativeLinkTo(mbr.inTemplate)
+ myXml ++= <span class="name"><a href={link ++ anchor}>{str.substring(from, to)}</a></span>
+ }
+ index = to
+ }
+ }
+
+ if (index <= length-1)
+ myXml ++= codeStringToXml(str.substring(index, length ))
+
+ if (length < 36)
+ <span class="symbol">{ myXml }</span>
+ else
+ <span class="defval" name={ myXml }>{ "..." }</span>
+ }
+
+ private def argumentsToHtml(argss: List[ValueArgument]): NodeSeq = {
+ def argumentsToHtml0(argss: List[ValueArgument]): NodeSeq = argss match {
+ case Nil => NodeSeq.Empty
+ case arg :: Nil => argumentToHtml(arg)
+ case arg :: args => argumentToHtml(arg) ++ scala.xml.Text(", ") ++ argumentsToHtml0(args)
+ }
+ <span class="args">({ argumentsToHtml0(argss) })</span>
+ }
+
+ private def argumentToHtml(arg: ValueArgument): NodeSeq = {
+ <span>
+ {
+ arg.parameter match {
+ case Some(param) => Text(param.name + " = ")
+ case None => NodeSeq.Empty
+ }
+ }
+ { treeToHtml(arg.value) }
+ </span>
+ }
+
+ private def bodyToStr(body: comment.Body): String =
+ body.blocks flatMap (blockToStr(_)) mkString ""
+
+ private def blockToStr(block: comment.Block): String = block match {
+ case comment.Paragraph(in) => inlineToStr(in)
+ case _ => block.toString
+ }
+
+ private def typeToHtmlWithStupidTypes(tpl: TemplateEntity, superTpl: TemplateEntity, superType: TypeEntity): NodeSeq =
+ if (tpl.universe.settings.useStupidTypes.value)
+ superTpl match {
+ case dtpl: DocTemplateEntity =>
+ val sig = signature(dtpl, isSelf = false, isReduced = true) \ "_"
+ sig
+ case tpl: TemplateEntity =>
+ Text(tpl.name)
+ }
+ else
+ typeToHtml(superType, hasLinks = true)
+
+ private def constraintToHtml(constraint: Constraint): NodeSeq = constraint match {
+ case ktcc: KnownTypeClassConstraint =>
+ scala.xml.Text(ktcc.typeExplanation(ktcc.typeParamName) + " (" + ktcc.typeParamName + ": ") ++
+ templateToHtml(ktcc.typeClassEntity) ++ scala.xml.Text(")")
+ case tcc: TypeClassConstraint =>
+ scala.xml.Text(tcc.typeParamName + " is ") ++
+ <a href="http://stackoverflow.com/questions/2982276/what-is-a-context-bound-in-scala" target="_blank">
+ context-bounded</a> ++ scala.xml.Text(" by " + tcc.typeClassEntity.qualifiedName + " (" + tcc.typeParamName + ": ") ++
+ templateToHtml(tcc.typeClassEntity) ++ scala.xml.Text(")")
+ case impl: ImplicitInScopeConstraint =>
+ scala.xml.Text("an implicit value of type ") ++ typeToHtml(impl.implicitType, hasLinks = true) ++ scala.xml.Text(" is in scope")
+ case eq: EqualTypeParamConstraint =>
+ scala.xml.Text(eq.typeParamName + " is " + eq.rhs.name + " (" + eq.typeParamName + " =:= ") ++
+ typeToHtml(eq.rhs, hasLinks = true) ++ scala.xml.Text(")")
+ case bt: BoundedTypeParamConstraint =>
+ scala.xml.Text(bt.typeParamName + " is a superclass of " + bt.lowerBound.name + " and a subclass of " +
+ bt.upperBound.name + " (" + bt.typeParamName + " >: ") ++
+ typeToHtml(bt.lowerBound, hasLinks = true) ++ scala.xml.Text(" <: ") ++
+ typeToHtml(bt.upperBound, hasLinks = true) ++ scala.xml.Text(")")
+ case lb: LowerBoundedTypeParamConstraint =>
+ scala.xml.Text(lb.typeParamName + " is a superclass of " + lb.lowerBound.name + " (" + lb.typeParamName + " >: ") ++
+ typeToHtml(lb.lowerBound, hasLinks = true) ++ scala.xml.Text(")")
+ case ub: UpperBoundedTypeParamConstraint =>
+ scala.xml.Text(ub.typeParamName + " is a subclass of " + ub.upperBound.name + " (" + ub.typeParamName + " <: ") ++
+ typeToHtml(ub.upperBound, hasLinks = true) ++ scala.xml.Text(")")
+ }
+}
+
+object EntityPage {
+ def apply(
+ uni: doc.Universe,
+ gen: DiagramGenerator,
+ docTpl: DocTemplateEntity,
+ rep: ScalaDocReporter
+ ): EntityPage = new EntityPage {
+ def universe = uni
+ def generator = gen
+ def tpl = docTpl
+ def reporter = rep
+ }
+}
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/Index.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/Index.scala
index 1aa1241847..8204f413fd 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/page/Index.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/html/page/Index.scala
@@ -1,6 +1,6 @@
/* NSC -- new Scala compiler
* Copyright 2007-2013 LAMP/EPFL
- * @author David Bernard, Manohar Jonnalagedda
+ * @author David Bernard, Manohar Jonnalagedda, Felix Mulder
*/
package scala.tools.nsc
@@ -12,115 +12,60 @@ import model._
import scala.collection._
import scala.xml._
-class Index(universe: doc.Universe, val index: doc.Index) extends HtmlPage {
+class Index(universe: doc.Universe, val index: doc.Index, rep: ScalaDocReporter) extends HtmlPage {
+
+ def reporter = rep
def path = List("index.html")
- def title = {
- val s = universe.settings
- ( if (!s.doctitle.isDefault) s.doctitle.value else "" ) +
- ( if (!s.docversion.isDefault) (" " + s.docversion.value) else "" )
- }
+ def title = ""
val headers =
<xml:group>
<link href={ relativeLinkTo{List("index.css", "lib")} } media="screen" type="text/css" rel="stylesheet"/>
<script type="text/javascript" src={ relativeLinkTo{List("jquery.js", "lib")} }></script>
<script type="text/javascript" src={ relativeLinkTo{List("index.js", "lib")} }></script>
+ <script type="text/javascript" src="index.js"></script>
<script type="text/javascript" src={ relativeLinkTo{List("scheduler.js", "lib")} }></script>
</xml:group>
val body =
<body>
- { browser }
- <div id="content" class="ui-layout-center">
+ { search }
+ <div id="search-results">
+ <div id="results-content">
+ <div id="entity-results"></div>
+ <div id="member-results"></div>
+ </div>
+ </div>
+ <div id="content" style="-webkit-overflow-scrolling: touch;">
<iframe id="template" name="template" src={ relativeLinkTo{List("package.html")} }/>
</div>
</body>
- def letters: NodeSeq =
- '_' +: ('a' to 'z') map {
- char => {
- val label = if (char == '_') '#' else char.toUpper
-
- index.firstLetterIndex.get(char) match {
- case Some(_) =>
- <a target="template" href={ "index/index-" + char + ".html" }>{
- label
- }</a>
- case None => <span>{ label }</span>
- }
- }
- }
-
- def deprecated: NodeSeq = if (index.hasDeprecatedMembers)
- <a target="template" href="deprecated-list.html">deprecated</a>
- else
- <span>deprecated</span>
-
- def browser =
- <div id="browser" class="ui-layout-west">
- <div class="ui-west-center">
- <div id="filter">
- <div id="textfilter"></div>
- <div id="letters">{ letters } &#8211; { deprecated }</div>
- </div>
- <div class="pack" id="tpl">{
- def packageElem(pack: model.Package): NodeSeq = {
- <xml:group>
- { if (!pack.isRootPackage)
- <a class="tplshow" href={ relativeLinkTo(pack) } target="template">{ pack.qualifiedName }</a>
- else NodeSeq.Empty
- }
- <ol class="templates">{
- val tpls: Map[String, Seq[DocTemplateEntity]] =
- (pack.templates collect {
- case t: DocTemplateEntity if !t.isPackage && !universe.settings.hardcoded.isExcluded(t.qualifiedName) => t
- }) groupBy (_.name)
-
- val placeholderSeq: NodeSeq = <div class="placeholder"></div>
-
- def createLink(entity: DocTemplateEntity, includePlaceholder: Boolean, includeText: Boolean) = {
- val entityType = kindToString(entity)
- val linkContent = (
- { if (includePlaceholder) placeholderSeq else NodeSeq.Empty }
- ++
- { if (includeText) <span class="tplLink">{ Text(packageQualifiedName(entity)) }</span> else NodeSeq.Empty }
- )
- <a class="tplshow" href={ relativeLinkTo(entity) } target="template"><span class={ entityType }>({ Text(entityType) })</span>{ linkContent }</a>
- }
-
- for (tn <- tpls.keySet.toSeq sortBy (_.toLowerCase)) yield {
- val entities = tpls(tn)
- val row = (entities find (e => e.isPackage || e.isObject), entities find (e => e.isTrait || e.isClass))
-
- val itemContents = row match {
- case (Some(obj), None) => createLink(obj, includePlaceholder = true, includeText = true)
-
- case (maybeObj, Some(template)) =>
- val firstLink = maybeObj match {
- case Some(obj) => createLink(obj, includePlaceholder = false, includeText = false)
- case None => placeholderSeq
- }
-
- firstLink ++ createLink(template, includePlaceholder = false, includeText = true)
-
- case _ => // FIXME: this default case should not be necessary. For some reason AnyRef is not a package, object, trait, or class
- val entry = entities.head
- placeholderSeq ++ createLink(entry, includePlaceholder = false, includeText = true)
- }
-
- <li title={ entities.head.qualifiedName }>{ itemContents }</li>
- }
- }</ol>
- <ol class="packages"> {
- for (sp <- pack.packages sortBy (_.name.toLowerCase)) yield
- <li class="pack" title={ sp.qualifiedName }>{ packageElem(sp) }</li>
- }</ol>
- </xml:group>
- }
- packageElem(universe.rootPackage)
- }</div></div><script src="index.js"></script>
+ def search =
+ <div id="search">
+ <span id="doc-title">
+ {universe.settings.doctitle.value}
+ <span id="doc-version">
+ {
+ val version = universe.settings.docversion.value
+
+ if (version.length > "XX.XX.XX-XXX".length) {
+ reporter.warning(null,
+ s"doc-version ($version) is too long to be displayed in the webview")
+ ""
+ } else version
+ }
+ </span>
+ </span>
+ <span class="close-results"><span class="left">&lt;</span> Back</span>
+ <div id="textfilter">
+ <span class="input">
+ <input autocapitalize="none" placeholder="Search" id="index-input" type="text" accesskey="/"/>
+ <span class="clear">✖</span>
+ </span>
+ </div>
</div>
def packageQualifiedName(ety: DocTemplateEntity): String =
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/IndexScript.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/IndexScript.scala
index e3c94505ab..2c38036bb6 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/page/IndexScript.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/html/page/IndexScript.scala
@@ -1,16 +1,22 @@
/* NSC -- new Scala compiler
- * Copyright 2007-2013 LAMP/EPFL
- * @author David Bernard, Manohar Jonnalagedda
+ * Copyright 2007-2016 LAMP/EPFL
+ * @author David Bernard, Manohar Jonnalagedda, Felix Mulder
*/
-package scala.tools.nsc.doc.html.page
+package scala.tools.nsc.doc
+package html
+package page
import scala.tools.nsc.doc
import scala.tools.nsc.doc.model.{Package, DocTemplateEntity}
import scala.tools.nsc.doc.html.{Page, HtmlFactory}
-import scala.util.parsing.json.{JSONObject, JSONArray}
+import scala.util.parsing.json.{JSONObject, JSONArray, JSONType}
class IndexScript(universe: doc.Universe, index: doc.Index) extends Page {
+ import model._
+ import scala.tools.nsc.doc.base.comment.Text
+ import scala.collection.immutable.Map
+
def path = List("index.js")
override def writeFor(site: HtmlFactory) {
@@ -25,18 +31,22 @@ class IndexScript(universe: doc.Universe, index: doc.Index) extends Page {
val merged = mergeByQualifiedName(templates)
val ary = merged.keys.toList.sortBy(_.toLowerCase).map(key => {
- val pairs = merged(key).map(
- t => kindToString(t) -> relativeLinkTo(t)
- ) :+ ("name" -> key)
+ val pairs = merged(key).flatMap { t: DocTemplateEntity =>
+ Seq(
+ kindToString(t) -> relativeLinkTo(t),
+ "kind" -> kindToString(t),
+ "members" -> membersToJSON(t.members.filter(!_.isShadowedOrAmbiguousImplicit)),
+ "shortDescription" -> shortDesc(t))
+ }
- JSONObject(scala.collection.immutable.Map(pairs : _*))
+ JSONObject(Map(pairs : _*) + ("name" -> key))
})
pack.qualifiedName -> JSONArray(ary)
}
}).toSeq
- JSONObject(scala.collection.immutable.Map(pairs : _*))
+ JSONObject(Map(pairs : _*))
}
def mergeByQualifiedName(source: List[DocTemplateEntity]) = {
@@ -66,4 +76,70 @@ class IndexScript(universe: doc.Universe, index: doc.Index) extends Page {
}
}) : _*)
}
+
+ /** Gets the short description i.e. the first sentence of the docstring */
+ def shortDesc(mbr: MemberEntity): String = mbr.comment.fold("") { c =>
+ inlineToStr(c.short).replaceAll("\n", "")
+ }
+
+ /** Returns the json representation of the supplied members */
+ def membersToJSON(entities: List[MemberEntity]): JSONType =
+ JSONArray(entities map memberToJSON)
+
+ private def memberToJSON(mbr: MemberEntity): JSONObject = {
+ /** This function takes a member and gets eventual parameters and the
+ * return type. For example, the definition:
+ * {{{ def get(key: A): Option[B] }}}
+ * Gets turned into: "(key: A): Option[B]"
+ */
+ def memberTail: MemberEntity => String = {
+ case d: Def => d
+ .valueParams //List[List[ValueParam]]
+ .map { params =>
+ params.map(p => p.name + ": " + p.resultType.name).mkString(", ")
+ }
+ .mkString("(", ")(", "): " + d.resultType.name)
+ case v: Val => ": " + v.resultType.name
+ }
+
+ /** This function takes a member entity and return all modifiers in a
+ * string, example:
+ * {{{ lazy val scalaProps: java.util.Properties }}}
+ * Gets turned into: "lazy val"
+ */
+ def memberKindToString(mbr: MemberEntity): String = {
+ val kind = mbr.flags.map(_.text.asInstanceOf[Text].text).mkString(" ")
+ val space = if (kind == "") "" else " "
+
+ kind + space + kindToString(mbr)
+ }
+
+ /** This function turns a member entity into a JSON object that the index.js
+ * script can use to render search results
+ */
+ def jsonObject(m: MemberEntity): JSONObject =
+ JSONObject(Map(
+ "label" -> m.definitionName.replaceAll(".*#", ""), // member name
+ "member" -> m.definitionName.replaceFirst("#", "."), // full member name
+ "tail" -> memberTail(m),
+ "kind" -> memberKindToString(m), // modifiers i.e. "abstract def"
+ "link" -> memberToUrl(m))) // permalink to the member
+
+ mbr match {
+ case d: Def => jsonObject(d)
+ case v: Val => jsonObject(v)
+ case m: MemberEntity =>
+ JSONObject(Map("member" -> m.definitionName, "error" -> "unsupported entity"))
+ }
+ }
+
+ def memberToUrl(mbr: MemberEntity): String = {
+ val path = templateToPath(mbr.inTemplate).reverse.mkString("/")
+ s"$path#${mbr.signature}"
+ }
+}
+
+object IndexScript {
+ def apply(universe: doc.Universe, index: doc.Index) =
+ new IndexScript(universe, index)
}
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/page/ReferenceIndex.scala b/src/scaladoc/scala/tools/nsc/doc/html/page/ReferenceIndex.scala
index 84ee82f994..6780f17a8c 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/page/ReferenceIndex.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/html/page/ReferenceIndex.scala
@@ -12,7 +12,9 @@ package page
import doc.model._
-class ReferenceIndex(letter: Char, index: doc.Index, universe: Universe) extends HtmlPage {
+class ReferenceIndex(letter: Char, index: doc.Index, universe: Universe, rep: ScalaDocReporter) extends HtmlPage {
+
+ def reporter = rep
def path = List("index-"+letter+".html", "index")
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 d33b31d8ba..f5e909cf90 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala
@@ -21,18 +21,17 @@ import model._
import model.diagram._
import diagram._
-class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemplateEntity) extends HtmlPage {
+class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemplateEntity, rep: ScalaDocReporter) extends HtmlPage {
- val path =
- templateToPath(tpl)
+ def reporter = rep
+
+ val path = templateToPath(tpl)
def title = {
val s = universe.settings
-
- tpl.name +
- ( if (!s.doctitle.isDefault) " - " + s.doctitle.value else "" ) +
- ( if (!s.docversion.isDefault) (" " + s.docversion.value) else "" ) +
- " - " + tpl.qualifiedName
+ ( if (!s.doctitle.isDefault) s.doctitle.value + " " else "" ) +
+ ( if (!s.docversion.isDefault) s.docversion.value else "" ) +
+ ( if ((!s.doctitle.isDefault || !s.docversion.isDefault) && tpl.qualifiedName != "_root_") " - " + tpl.qualifiedName else "" )
}
val headers =
@@ -116,9 +115,9 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
<h1>{ displayName }</h1>{
if (tpl.isPackage) NodeSeq.Empty else <h3>{companionAndPackage(tpl)}</h3>
}{ permalink(tpl) }
+ { signature(tpl, isSelf = true) }
</div>
- { signature(tpl, isSelf = true) }
{ memberToCommentHtml(tpl, tpl.inTemplate, isSelf = true) }
<div id="mbrsel">
@@ -284,7 +283,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-2015 <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-2016 <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>
}
@@ -940,17 +939,6 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
case _ => block.toString
}
- private def inlineToStr(inl: comment.Inline): String = inl match {
- case comment.Chain(items) => items flatMap (inlineToStr(_)) mkString ""
- case comment.Italic(in) => inlineToStr(in)
- case comment.Bold(in) => inlineToStr(in)
- case comment.Underline(in) => inlineToStr(in)
- case comment.Monospace(in) => inlineToStr(in)
- case comment.Text(text) => text
- case comment.Summary(in) => inlineToStr(in)
- case _ => inl.toString
- }
-
private def typeToHtmlWithStupidTypes(tpl: TemplateEntity, superTpl: TemplateEntity, superType: TypeEntity): NodeSeq =
if (tpl.universe.settings.useStupidTypes.value)
superTpl match {
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.css b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.css
index 1260d860d0..ea1c358149 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.css
+++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.css
@@ -2,6 +2,17 @@
@font-face {
font-family: 'Lato';
font-style: normal;
+ font-weight: 100;
+ src: url('lato-v11-latin-regular.eot');
+ src: local('Lato'), local('Lato'),
+ url('lato-v11-latin-100.eot?#iefix') format('embedded-opentype'),
+ url('lato-v11-latin-100.woff') format('woff'),
+ url('lato-v11-latin-100.ttf') format('truetype');
+}
+
+@font-face {
+ font-family: 'Lato';
+ font-style: normal;
font-weight: 400;
src: url('lato-v11-latin-regular.eot');
src: local('Lato'), local('Lato'),
@@ -21,6 +32,27 @@
url('open-sans-v13-latin-regular.ttf') format('truetype');
}
+@font-face {
+ font-family: 'Source Code Pro';
+ font-style: normal;
+ font-weight: 400;
+ src: url('source-code-pro-v6-latin-regular.eot');
+ src: local('Source Code Pro'), local('SourceCodePro-Regular'),
+ url('source-code-pro-v6-latin-regular.eot?#iefix') format('embedded-opentype'),
+ url('source-code-pro-v6-latin-regular.woff') format('woff'),
+ url('source-code-pro-v6-latin-regular.ttf') format('truetype');
+}
+@font-face {
+ font-family: 'Source Code Pro';
+ font-style: normal;
+ font-weight: 700;
+ src: url('source-code-pro-v6-latin-700.eot');
+ src: local('Source Code Pro Bold'), local('SourceCodePro-Bold'),
+ url('source-code-pro-v6-latin-700.eot?#iefix') format('embedded-opentype'),
+ url('source-code-pro-v6-latin-700.woff') format('woff'),
+ url('source-code-pro-v6-latin-700.ttf') format('truetype');
+}
+
* {
color: inherit;
text-decoration: none;
@@ -31,18 +63,25 @@
a {
cursor: pointer;
+ text-decoration: none;
}
a:hover {
text-decoration: underline;
}
-.selected {
- background-color: #2E6D82;
+span.entity > a {
+ padding: 0.1em 0.5em;
+ margin-left: 0.2em;
+}
+
+span.entity > a.selected {
+ background-color: #C2D2DC;
+ border-radius: 0.2em;
}
html {
- background-color: #364550;
+ background-color: #f0f3f6;
box-sizing: border-box;
}
*, *:before, *:after {
@@ -56,51 +95,46 @@ textarea, input { outline: none; }
}
#browser {
- width: 16.75em;
+ width: 17.5em;
top: 0px;
left: 0;
bottom: 0px;
display: block;
position: fixed;
- background-color: #364550;
+ background-color: #f0f3f6;
}
#browser.full-screen {
- left: -15em;
+ left: -17.5em;
}
-#filter {
- position: absolute;
- display: block;
- right: 0;
- left: 0;
- top: 0;
- background-color: #364550; /* light gray */
- border-top:0;
- border-left:0;
- border-right:0;
- z-index: 99;
+#search {
+ background-color: #103a51; /* typesafe blue */
min-height: 5.5em;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 3em;
+ min-height: initial;
+ z-index: 103;
+ box-shadow: 0 0 4px rgba(0, 0, 0, 0.18), 0 4px 8px rgba(0, 0, 0, 0.28);
}
-#filter.scrolled {
- box-shadow: 0 0 8px rgba(0,0,0,0.5);
-}
-
-#filter > h1 {
+#search > h1 {
font-size: 2em;
position: absolute;
left: 0.25em;
top: 0.5em;
}
-#filter > h2 {
+#search > h2 {
position: absolute;
left: 3.8em;
top: 3em;
}
-#filter > img.scala-logo {
+#search > img.scala-logo {
width: 3em;
height: auto;
position: absolute;
@@ -108,22 +142,39 @@ textarea, input { outline: none; }
top: 0.43em;
}
-#filter > span.toggle-sidebar {
+#search > span.toggle-sidebar {
position: absolute;
top: 0.8em;
- right: 0.2em;
+ left: 0.2em;
color: #fff;
z-index: 99;
width: 1.5em;
height: 1.5em;
}
-#filter > span.toggle-sidebar:hover {
+#search > span#doc-title {
+ color: #fff;
+ position: absolute;
+ top: 0.8em;
+ left: 0;
+ width: 18em;
+ text-align: center;
+ cursor: pointer;
+ z-index: 2;
+}
+
+#search > span#doc-title > span#doc-version {
+ color: #c2c2c2;
+ font-weight: 100;
+ font-size: 0.72em;
+}
+
+#search > span.toggle-sidebar:hover {
cursor: pointer;
}
/* Pseudo element replacing UTF8-symbol "Trigram From Heaven" */
-#filter > span.toggle-sidebar:before {
+#search > span.toggle-sidebar:before {
position: absolute;
top: -0.45em;
left: 0.45em;
@@ -134,7 +185,7 @@ textarea, input { outline: none; }
box-shadow: 0 0.8em 0 1px #fff, 0 1.1em 0 1px #fff, 0 1.4em 0 1px #fff;
}
-#filter > span.toggle-sidebar:hover:before {
+#search > span.toggle-sidebar:hover:before {
-webkit-box-shadow: 0 0.8em 0 1px #c2c2c2, 0 1.1em 0 1px #c2c2c2, 0 1.4em 0 1px #c2c2c2;
box-shadow: 0 0.8em 0 1px #c2c2c2, 0 1.1em 0 1px #c2c2c2, 0 1.4em 0 1px #c2c2c2;
}
@@ -149,20 +200,29 @@ textarea, input { outline: none; }
}
#textfilter {
- position: relative;
+ position: absolute;
+ top: 0.5em;
+ bottom: 0.8em;
+ left: 0;
+ right: 0;
display: block;
- height: 20px;
- margin-top: 0.5em;
- margin-bottom: 0.8em;
+ height: 2em;
}
#textfilter > .input {
+ position: relative;
display: block;
+ padding: 0.2em;
+ max-width: 48.5em;
+ margin: 0 auto;
+}
+
+#textfilter > .input > i#search-icon {
+ color: rgba(255,255,255, 0.4);
position: absolute;
- top: 0;
- left: 0;
- padding: 0.2em 1.8em 0.2em 0.5em;
- width: 100%;
+ left: 0.34em;
+ top: 0.3em;
+ font-size: 1.3rem;
}
#textfilter > span.toggle {
@@ -199,29 +259,46 @@ textarea, input { outline: none; }
font-family: "Open Sans";
font-size: 0.85em;
height: 2em;
- padding: 0 0.5em;
+ padding: 0 0 0 2.1em;
color: #fff;
width: 100%;
border-radius: 0.2em;
background: rgba(255, 255, 255, 0.2);
}
+
+#textfilter > .input > input::-webkit-input-placeholder {
+ color: rgba(255, 255, 255, 0.4);
+}
+
+#textfilter > .input > input::-moz-placeholder {
+ color: rgba(255, 255, 255, 0.4);
+}
+
+#textfilter > .input > input:-ms-input-placeholder {
+ color: rgba(255, 255, 255, 0.4);
+}
+
+#textfilter > .input > input:-moz-placeholder {
+ color: rgba(255, 255, 255, 0.4);
+}
+
#focusfilter > .focusremove:hover {
text-decoration: none;
}
-#textfilter > .clear {
+#textfilter > .input > .clear {
display: none;
position: absolute;
font-size: 0.9em;
- top: 0.53em;
- right: 1.8em;
+ top: 0.7em;
+ right: 0.1em;
height: 23px;
width: 21px;
color: rgba(255, 255, 255, 0.4);
}
-#textfilter > .clear:hover {
+#textfilter > .input > .clear:hover {
cursor: pointer;
color: #fff;
}
@@ -230,11 +307,27 @@ textarea, input { outline: none; }
font-size: 0.9em;
position: relative;
text-align: center;
- display: block;
+ display: none;
padding: 0.6em;
background-color: #f16665;
color: #fff;
- margin-top: 0.65em;
+ margin: 3.9em 0.55em 0 0.35em;
+ border-radius: 0.2em;
+ z-index: 1;
+}
+
+div#search-progress {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 0.25em;
+}
+
+div#search-progress > div#progress-fill {
+ width: 0%;
+ background-color: #f16665;
+ transition: 0.1s;
}
#focusfilter .focuscoll {
@@ -252,7 +345,7 @@ textarea, input { outline: none; }
z-index: 99;
bottom: 0.5em;
left: 0;
- width: 16.75em;
+ width: 17.25em;
}
#kindfilter {
@@ -300,177 +393,344 @@ textarea, input { outline: none; }
color: #bbb;
}
-#tpl {
- font-size: 0.8em;
- overflow: auto;
+div#content-container {
+ position: absolute;
+ top: 0;
right: 0;
- left: 0;
bottom: 0;
- top: 6.9em !important;
- position: absolute;
+ left: 0;
+ z-index: 100;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+div#content-container > div#content {
+ -webkit-overflow-scrolling: touch;
display: block;
- background-color: #364550;
- padding-top: 0.3em;
+ overflow-y: auto;
+ max-width: 1140px;
+ margin: 5em auto 0;
}
-#tpl.packfocused {
- top: 9.5em !important;
+div#search-results {
+ color: #103a51;
+ position: absolute;
+ left: 0;
+ top: 3em;
+ right: 0;
+ bottom: 0;
+ background-color: rgb(240, 243, 246);
+ z-index: 101;
+ overflow-x: hidden;
+ display: none;
+ padding: 1em;
+ -webkit-overflow-scrolling: touch;
}
-#tpl .packfocus,
-#tpl .packhide {
- display: block;
- float: right;
- font-weight: normal;
- color: #f16665;
+div#search > span.close-results {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ position: fixed;
+ top: 0.8em;
+ left: 1em;
+ color: #fff;
+ display: none;
+ z-index: 1;
}
-#tpl .packfocus:hover,
-#tpl .packhide:hover {
- text-decoration: none;
- color: #53cdec;
+div#search > span.close-results:hover {
+ cursor: pointer;
+}
+
+div#results-content {
+ max-width: 1140px;
+ margin: 0 auto;
}
-#tpl .packages {
+div#results-content > span.search-text {
+ margin-left: 1em;
+ font-size: 1.2em;
+ float: left;
width: 100%;
- padding-left: 0;
- overflow-x: hidden;
}
-#tpl .packages > ol {
- color: #fff;
- background-color: #364550;
- padding-left: 0;
+div#results-content > span.search-text > span.query-str {
+ font-weight: 900;
}
-#tpl .packages > li > a {
- padding: 0px 5px;
+div#results-content > div > h1.result-type {
+ font-size: 1.5em;
+ margin: 1em 0 0.3em;
+ font-family: "Open Sans";
+ font-weight: 300;
+ border-bottom: 1px solid #103a51;
}
-#tpl .packages > li > a.tplshow {
- display: block;
+div#results-content > div#entity-results {
+ float: left;
+ width: 50%;
+ padding: 1em;
+ display: inline;
+}
+
+div#results-content > div#member-results {
+ float: left;
+ width: 50%;
+ padding: 1em;
+ display: inline;
+}
+
+div#results-content > div#member-results > a.package,
+div#results-content > div#entity-results > a.package {
+ font-size: 1em;
+ margin: 0 0 1em 0;
color: #f16665;
- font-weight: bold;
- display: block;
+ cursor: pointer;
}
-#tpl .packages > li > a.tplshow:hover {
- color: #53cdec;
- text-decoration: none;
+div#results-content > div#member-results > ul.entities,
+div#results-content > div#entity-results > ul.entities {
+ list-style-type: none;
+ padding-left: 0;
}
-#tpl .packages a.tplshow > .type-circle {
+div#results-content > div#member-results > ul.entities > li,
+div#results-content > div#entity-results > ul.entities > li {
+ margin: 0.5em 0;
+}
+
+div#results-content > div#member-results > ul.entities > li > .icon,
+div#results-content > div#entity-results > ul.entities > li > .icon {
float: left;
- border: 1px solid rgba(255, 255, 255, 0.4);
- height: 0.8rem;
- width: 0.8rem;
- border-radius: 0.8rem;
- color: #efefef;
- margin-top: 0.16em;
- margin-right: 0.1em;
- position: relative;
- font-size: 0.8em;
- line-height: 0.9;
- font-family: Arial, sans-serif;
- text-align: center;
- display: table-cell;
- vertical-align: middle;
+ display: inline;
+ height: 1em;
+ width: 1em;
+ margin: 0.23em 0 0;
+ cursor: pointer;
}
+div#results-content > div#member-results > ul.entities > li > .icon.class,
+div#results-content > div#entity-results > ul.entities > li > .icon.class {
+ background: url("class.svg") no-repeat center;
+ background-size: 1em 1em;
+}
-#tpl .packages a.tplshow > .type-circle > span {
- font-size: 0.9em;
+div#results-content > div#member-results > ul.entities > li > .icon.trait,
+div#results-content > div#entity-results > ul.entities > li > .icon.trait {
+ background: url("trait.svg") no-repeat center;
+ background-size: 1em 1em;
}
-#tpl .packages a.tplshow > .type-circle.class {
- background-color: #316555;
+div#results-content > div#member-results > ul.entities > li > .icon.object,
+div#results-content > div#entity-results > ul.entities > li > .icon.object {
+ background: url("object.svg") no-repeat center;
+ background-size: 1em 1em;
}
-#tpl .packages a.tplshow > .type-circle.object {
- background-color: #103A51;
+div#results-content > div#member-results > ul.entities > li > span.entity,
+div#results-content > div#entity-results > ul.entities > li > span.entity {
+ font-size: 1.1em;
+ font-weight: 900;
}
-#tpl .packages a.tplshow > .type-circle.trait {
- background-color: #19AACF;
+div#results-content > div#member-results > ul.entities > li > ul.members,
+div#results-content > div#entity-results > ul.entities > li > ul.members {
+ margin-top: 0.5em;
+ list-style-type: none;
+ font-size: 0.85em;
+ margin-left: 0.2em;
}
-#tpl ol > li.pack {
- padding: 3px 5px;
- min-height: 14px;
- background-color: #364550;
+div#results-content > div#member-results > ul.entities > li > ul.members > li,
+div#results-content > div#entity-results > ul.entities > li > ul.members > li {
+ margin: 0.5em 0;
}
-#tpl > ol > ol > li > a:hover {
- text-decoration: none;
- color: #53cdec;
+div#results-content > div#member-results > ul.entities > li > ul.members > li > span.kind,
+div#results-content > div#member-results > ul.entities > li > ul.members > li > span.tail,
+div#results-content > div#entity-results > ul.entities > li > ul.members > li > span.kind,
+div#results-content > div#entity-results > ul.entities > li > ul.members > li > span.tail {
+ margin-right: 0.6em;
+ font-family: "Source Code Pro";
}
-#tpl ol > li {
- display: block;
+div#results-content > div#member-results > ul.entities > li > ul.members > li > span.kind {
+ font-weight: 600;
}
-#tpl .templates > li {
- padding-left: 5px;
- min-height: 1.4em;
- width: 21em; /* tpl width == 20em */
+div#results-content > div#member-results > ul.entities > li > ul.members > li > a.label,
+div#results-content > div#entity-results > ul.entities > li > ul.members > li > a.label {
+ color: #2C3D9B;
+ font-family: "Source Code Pro";
}
-#tpl ol > li .icon {
- padding-right: 5px;
- bottom: -2px;
+/** Scrollpane settings needed for jquery.scrollpane.min.js */
+.jspContainer {
+ overflow: hidden;
position: relative;
}
-#tpl .templates div.placeholder {
- padding-right: 14px;
- width: 11px;
- display: inline-block;
+.jspPane {
+ position: absolute;
}
-#tpl .templates span.tplLink {
- padding-left: 7px;
+.jspVerticalBar {
+ position: absolute;
+ top: 0;
+ right: 0;
+ width: 0.6em;
+ height: 100%;
+ background: transparent;
}
-#content {
- right: 0px;
- left: 16.75em;
- bottom: 0px;
- top: 0px;
- position: fixed;
- display: block;
- overflow-y: auto;
- -webkit-overflow-scrolling: touch;
- background-color: #fff;
- z-index: 100;
+.jspHorizontalBar {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ width: 100%;
+ height: 16px;
+ background: red;
+}
+
+.jspCap {
+ display: none;
}
-#content.full-screen {
- left: 1.7em;
+.jspHorizontalBar .jspCap {
+ float: left;
}
-#content.hide-filter {
- left: 1.5em;
+.jspTrack {
+ background: #f0f3f6;
+ position: relative;
}
-#content > iframe {
+.jspDrag {
+ display: none;
+ background: rgba(0, 0, 0, 0.35);
+ position: relative;
+ top: 0;
+ left: 0;
+ cursor: pointer;
+}
+
+#tpl:hover .jspDrag {
display: block;
+}
+
+.jspHorizontalBar .jspTrack,
+.jspHorizontalBar .jspDrag {
+ float: left;
height: 100%;
- width: 100%;
}
-.ui-layout-pane {
- background: #FFF;
- overflow: auto;
+.jspArrow {
+ background: #50506d;
+ text-indent: -20000px;
+ display: block;
+ cursor: pointer;
+ padding: 0;
+ margin: 0;
+}
+
+.jspArrow.jspDisabled {
+ cursor: default;
+ background: #80808d;
}
-.ui-layout-resizer {
- background-color: #ededee; /* light gray */
- border:1px solid #bbbbbb;
- border-top:0;
- border-bottom:0;
- border-left: 0;
+.jspVerticalBar .jspArrow {
+ height: 16px;
}
-.ui-layout-toggler {
- background: #AAA;
+.jspHorizontalBar .jspArrow {
+ width: 16px;
+ float: left;
+ height: 100%;
+}
+
+.jspVerticalBar .jspArrow:focus {
+ outline: none;
+}
+
+.jspCorner {
+ background: #eeeef4;
+ float: left;
+ height: 100%;
+}
+
+/* CSS Hack for IE6 3 pixel bug */
+* html .jspCorner {
+ margin: 0 -3px 0 0;
+}
+
+/* Media query rules for smaller viewport */
+@media only screen /* Large screen with a small window */
+and (max-width: 1300px)
+{
+ #textfilter {
+ left: 17.8em;
+ right: 0.35em;
+ }
+
+ #textfilter .input {
+ max-width: none;
+ margin: 0;
+ }
+}
+
+@media only screen /* Large screen with a smaller window */
+and (max-width: 800px)
+{
+ div#results-content > div#entity-results {
+ width: 100%;
+ padding: 0em;
+ }
+
+ div#results-content > div#member-results {
+ width: 100%;
+ padding: 0em;
+ }
+}
+
+/* Media query rules specifically for mobile devices */
+@media
+screen /* HiDPI device like Nexus 5 */
+and (max-device-width: 360px)
+and (max-device-height: 640px)
+and (-webkit-device-pixel-ratio: 3)
+,
+screen /* Most mobile devices */
+and (max-device-width: 480px)
+and (orientation: portrait)
+,
+only screen /* iPhone 6 */
+and (max-device-width: 667px)
+and (-webkit-device-pixel-ratio: 2)
+{
+ div#content-container > div#content {
+ margin: 3.3em auto 0;
+ }
+
+ #search > span#doc-title {
+ width: 100%;
+ text-align: left;
+ padding-left: 0.7em;
+ top: 0.95em;
+ z-index: 1;
+ }
+
+ #search > div#textfilter {
+ z-index: 2;
+ }
+
+ #search > span#doc-title > span#doc-version {
+ display: none;
+ }
+
+ #textfilter {
+ left: 12.2em;
+ }
}
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.js b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.js
index cad4072912..caa6406bc5 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.js
+++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/index.js
@@ -1,363 +1,198 @@
// © 2009–2010 EPFL/LAMP
// code by Gilles Dubochet with contributions by Johannes Rudolph, "spiros", Marcin Kubala and Felix Mulder
-var topLevelTemplates = undefined;
-var topLevelPackages = undefined;
-
var scheduler = undefined;
-var kindFilterState = undefined;
-var focusFilterState = undefined;
-
var title = $(document).attr('title');
var lastFragment = "";
-$(document).ready(function() {
- /* check if browser is mobile, if so hide class nav */
- if( /Android|webOS|Mobi|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
- $("#browser").toggleClass("full-screen");
- $("#content").toggleClass("full-screen");
- $("#letters").toggle();
- setTimeout(function() {
- $(".packages").hide();
- $("#kindfilter").hide();
- }, 4000);
- }
-
- $('iframe').bind("load", function(){
- try {
- var subtitle = $(this).contents().find('title').text();
- $(document).attr('title', (title ? title + " - " : "") + subtitle);
- } catch (e) {
- // Chrome doesn't allow reading the iframe's contents when
- // used on the local file system.
- }
- setUrlFragmentFromFrameSrc();
- });
-
- // workaround for IE's iframe sizing lack of smartness
- if($.browser.msie) {
- function fixIFrame() {
- $('iframe').height($(window).height() )
- }
- $('iframe').bind("load",fixIFrame)
- $('iframe').bind("resize",fixIFrame)
- }
-
- scheduler = new Scheduler();
- scheduler.addLabel("init", 1);
- scheduler.addLabel("focus", 2);
- scheduler.addLabel("filter", 4);
-
- prepareEntityList();
- configureTextFilter();
- configureEntityList();
-
- setFrameSrcFromUrlFragment();
-
- // If the url fragment changes, adjust the src of iframe "template".
- $(window).bind('hashchange', function() {
- if(lastFragment != window.location.hash) {
- lastFragment = window.location.hash;
- setFrameSrcFromUrlFragment();
- }
- });
-
- // Wait until page has loaded until binding input fields, setting fold all
- setTimeout(function() {
- configureKindFilter();
-
- $("#index-input").on("focus", function() {
- $("#textfilter > .clear").show();
- });
-
- $("#index-input").on("blur", function() {
- $("#textfilter > .clear").hide();
- });
- }, 1500);
-});
-
-// Set the iframe's src according to the fragment of the current url.
-// fragment = "#scala.Either" => iframe url = "scala/Either.html"
-// fragment = "#scala.Either@isRight:Boolean" => iframe url = "scala/Either.html#isRight:Boolean"
-// fragment = "#scalaz.iteratee.package@>@>[E,A]=scalaz.iteratee.package.Iteratee[E,A]" => iframe url = "scalaz/iteratee/package.html#>@>[E,A]=scalaz.iteratee.package.Iteratee[E,A]"
-function setFrameSrcFromUrlFragment() {
-
- function extractLoc(fragment) {
- var loc = fragment.split('@')[0].replace(/\./g, "/");
- if (loc.indexOf(".html") < 0) {
- loc += ".html";
- }
- return loc;
- }
-
- function extractMemberSig(fragment) {
- var splitIdx = fragment.indexOf('@');
- if (splitIdx < 0) {
- return;
- }
- return fragment.substr(splitIdx + 1);
- }
-
- var fragment = location.hash.slice(1);
- if (fragment) {
- var locWithMemeberSig = extractLoc(fragment);
- var memberSig = extractMemberSig(fragment);
- if (memberSig) {
- locWithMemeberSig += "#" + memberSig;
- }
- frames["template"].location.replace(location.protocol + locWithMemeberSig);
- } else {
- console.log("empty fragment detected");
- frames["template"].location.replace("package.html");
- }
-}
-
-// Set the url fragment according to the src of the iframe "template".
-// iframe url = "scala/Either.html" => url fragment = "#scala.Either"
-// iframe url = "scala/Either.html#isRight:Boolean" => url fragment = "#scala.Either@isRight:Boolean"
-// iframe url = "scalaz/iteratee/package.html#>@>[E,A]=scalaz.iteratee.package.Iteratee[E,A]" => fragment = "#scalaz.iteratee.package@>@>[E,A]=scalaz.iteratee.package.Iteratee[E,A]"
-function setUrlFragmentFromFrameSrc() {
- try {
- var commonLength = location.pathname.lastIndexOf("/");
- var frameLocation = frames["template"].location;
- var relativePath = frameLocation.pathname.slice(commonLength + 1);
-
- if(!relativePath || frameLocation.pathname.indexOf("/") < 0)
- return;
-
- // Add #, remove ".html" and replace "/" with "."
- fragment = "#" + relativePath.replace(/\.html$/, "").replace(/\//g, ".");
-
- // Add the frame's hash after an @
- if(frameLocation.hash) fragment += ("@" + frameLocation.hash.slice(1));
-
- // Use replace to not add history items
- lastFragment = fragment;
- location.replace(fragment);
- }
- catch(e) {
- // Chrome doesn't allow reading the iframe's location when
- // used on the local file system.
- }
-}
-
var Index = {};
-
(function (ns) {
- function openLink(t, type) {
- var href;
- if (type == 'object') {
- href = t['object'];
- } else {
- href = t['class'] || t['trait'] || t['case class'] || t['type'];
- }
- return [
- '<a class="tplshow" target="template" href="',
- href,
- '"><div class="type-circle ',
- type,
- '"><span>',
- type.charAt(0).toLowerCase(),
- '</span></div>'
- ].join('');
- }
-
- function createPackageHeader(pack) {
- return [
- '<li class="pack">',
- '<a class="packfocus">focus</a><a class="packhide">hide</a>',
- '<a class="tplshow" target="template" href="',
- pack.replace(/\./g, '/'),
- '/package.html">',
- pack,
- '</a></li>'
- ].join('');
- };
-
- function createListItem(template) {
- var inner = '';
-
-
- if (template.object) {
- inner += openLink(template, 'object');
- }
-
- if (template['class'] || template['trait'] || template['case class'] || template['type']) {
- inner += (inner == '') ?
- '<div class="placeholder" />' : '</a>';
- inner += openLink(template, template['trait'] ? 'trait' : template['type'] ? 'type' : 'class');
- } else {
- inner += '<div class="placeholder"/>';
- }
-
- return [
- '<li>',
- inner,
- '<span class="tplLink">',
- template.name.replace(/^.*\./, ''),
- '</span></a></li>'
- ].join('');
- }
-
-
- ns.createPackageTree = function (pack, matched, focused) {
- var html = $.map(matched, function (child, i) {
- return createListItem(child);
- }).join('');
-
- var header;
- if (focused && pack == focused) {
- header = '';
- } else {
- header = createPackageHeader(pack);
- }
-
- return [
- '<ol class="packages">',
- header,
- '<ol class="templates">',
- html,
- '</ol></ol>'
- ].join('');
- }
-
+ ns.keyLength = 0;
ns.keys = function (obj) {
var result = [];
var key;
for (key in obj) {
result.push(key);
+ ns.keyLength++;
}
return result;
}
+})(Index);
- var hiddenPackages = {};
+$(document).ready(function() {
+ // Clicking #doc-title returns the user to the root package
+ $("#doc-title").click(function() { document.location = toRoot + "index.html" });
- function subPackages(pack) {
- return $.grep($('#tpl ol.packages'), function (element, index) {
- var pack = $('li.pack > .tplshow', element).text();
- return pack.indexOf(pack + '.') == 0;
- });
- }
+ scheduler = new Scheduler();
+ scheduler.addLabel("init", 1);
+ scheduler.addLabel("focus", 2);
+ scheduler.addLabel("filter", 4);
+ scheduler.addLabel("search", 5);
- ns.hidePackage = function (ol) {
- var selected = $('li.pack > .tplshow', ol).text();
- hiddenPackages[selected] = true;
+ configureTextFilter();
- $('ol.templates', ol).hide();
+ $("#index-input").on("focus", function(e) {
+ $("#textfilter > .input > .clear").show();
+ });
- $.each(subPackages(selected), function (index, element) {
- $(element).hide();
- });
- }
+ $("#index-input").on("blur", function() {
+ setTimeout(function() {
+ $("#textfilter > .input > .clear").hide();
+ }, 10);
+ });
+});
- ns.showPackage = function (ol, state) {
- var selected = $('li.pack > .tplshow', ol).text();
- hiddenPackages[selected] = false;
+/* Handles all key presses while scrolling around with keyboard shortcuts in search results */
+function handleKeyNavigation() {
+ /** Iterates both back and forth among selected elements */
+ var EntityIterator = function (litems, ritems) {
+ var it = this;
+ this.index = -1;
+
+ this.items = litems;
+ this.litems = litems;
+ this.ritems = ritems;
+
+ if (litems.length == 0)
+ this.items = ritems;
+
+ /** Returns the next entry - if trying to select past last element, it
+ * returns the last element
+ */
+ it.next = function() {
+ it.index = Math.min(it.items.length - 1, it.index + 1);
+ return $(it.items[it.index]);
+ };
- $('ol.templates', ol).show();
+ /** Returns the previous entry - will return `undefined` instead if
+ * selecting up from first element
+ */
+ it.prev = function() {
+ it.index = Math.max(-1, it.index - 1);
+ return it.index == -1 ? undefined : $(it.items[it.index]);
+ };
- $.each(subPackages(selected), function (index, element) {
- $(element).show();
+ it.right = function() {
+ if (it.ritems.length != 0) {
+ it.items = it.ritems;
+ it.index = Math.min(it.index, it.items.length - 1);
+ }
+ return $(it.items[it.index]);
+ };
- // When the filter is in "packs" state,
- // we don't want to show the `.templates`
- var key = $('li.pack > .tplshow', element).text();
- if (hiddenPackages[key] || state == 'packs') {
- $('ol.templates', element).hide();
+ it.left = function() {
+ if (it.litems.length != 0) {
+ it.items = it.litems;
+ it.index = Math.min(it.index, it.items.length - 1);
}
- });
- }
+ return $(it.items[it.index]);
+ };
+ };
-})(Index);
+ /** Scroll helper, ensures that the selected elem is inside the viewport */
+ var Scroller = function ($container) {
+ scroller = this;
+ scroller.container = $container;
+
+ scroller.scrollDown = function($elem) {
+ var yPos = $elem.offset().top; // offset relative to viewport
+ if ($container.height() < yPos) {
+ $container.animate({
+ scrollTop: $container.scrollTop() + yPos - $("#search").height() - 10
+ }, 200);
+ }
+ };
-function configureEntityList() {
- kindFilterSync();
- configureHideFilter();
- configureFocusFilter();
- textFilter();
-}
+ scroller.scrollUp = function ($elem) {
+ var yPos = $elem.offset().top; // offset relative to viewport
+ if (yPos < $("#search").height()) {
+ $container.animate({
+ scrollTop: $container.scrollTop() + yPos - $("#search").height() - 10
+ }, 200);
+ }
+ };
-/**
- * Updates the list of entities (i.e. the content of the #tpl element) from the
- * raw form generated by Scaladoc to a form suitable for display. It configures
- * links to open in the right frame. Furthermore, it sets the two reference
- * top-level entities lists (topLevelTemplates and topLevelPackages) to serve
- * as reference for resetting the list when needed.
- *
- * Be advised: this function should only be called once, on page load.
- */
-function prepareEntityList() {
- $('#tpl li.pack > a.tplshow').attr("target", "template");
- $('#tpl li.pack')
- .prepend("<a class='packhide'>hide</a>")
- .prepend("<a class='packfocus'>focus</a>");
-}
+ scroller.scrollTop = function() {
+ $container.animate({
+ scrollTop: 0
+ }, 200);
+ }
+ };
-/* Handles all key presses while scrolling around with keyboard shortcuts in left panel */
-function keyboardScrolldownLeftPane() {
scheduler.add("init", function() {
$("#textfilter input").blur();
- var $items = $("#tpl li");
- $items.first().addClass('selected');
+ var items = new EntityIterator(
+ $("div#results-content > div#entity-results > ul.entities span.entity > a").toArray(),
+ $("div#results-content > div#member-results > ul.entities span.entity > a").toArray()
+ );
- $(window).bind("keydown", function(e) {
- var $old = $items.filter('.selected'),
- $new;
+ var scroller = new Scroller($("#search-results"));
- switch ( e.keyCode ) {
+ var $old = items.next();
+ $old.addClass("selected");
+ $(window).bind("keydown", function(e) {
+ switch ( e.keyCode ) {
case 9: // tab
- $old.removeClass('selected');
+ $old.removeClass("selected");
break;
case 13: // enter
- $old.removeClass('selected');
- var $url = $old.children().filter('a:last').attr('href');
- $("#template").attr("src",$url);
+ var href = $old.attr("href");
+ location.replace(href);
+ $old.click();
+ $("#textfilter input").attr("value", "");
break;
case 27: // escape
- $old.removeClass('selected');
- $(window).unbind(e);
- $("#textfilter input").focus();
+ $("#textfilter input").attr("value", "");
+ $("div#search-results").hide();
+ $("#search > span.close-results").hide();
+ $("#search > span#doc-title").show();
+ break;
+
+ case 37: // left
+ var oldTop = $old.offset().top;
+ $old.removeClass("selected");
+ $old = items.left();
+ $old.addClass("selected");
+ (oldTop - $old.offset().top < 0 ? scroller.scrollDown : scroller.scrollUp)($old);
break;
case 38: // up
- $new = $old.prev();
-
- if (!$new.length) {
- $new = $old.parent().prev();
+ $old.removeClass('selected');
+ $old = items.prev();
+
+ if ($old === undefined) { // scroll past top
+ $(window).unbind("keydown");
+ $("#textfilter input").focus();
+ scroller.scrollTop();
+ return false;
+ } else {
+ $old.addClass("selected");
+ scroller.scrollUp($old);
}
+ break;
- if ($new.is('ol') && $new.children(':last').is('ol')) {
- $new = $new.children().children(':last');
- } else if ($new.is('ol')) {
- $new = $new.children(':last');
- }
+ case 39: // right
+ var oldTop = $old.offset().top;
+ $old.removeClass("selected");
+ $old = items.right();
+ $old.addClass("selected");
+ (oldTop - $old.offset().top < 0 ? scroller.scrollDown : scroller.scrollUp)($old);
break;
case 40: // down
- $new = $old.next();
- if (!$new.length) {
- $new = $old.parent().parent().next();
- }
- if ($new.is('ol')) {
- $new = $new.children(':first');
- }
+ $old.removeClass("selected");
+ $old = items.next();
+ $old.addClass("selected");
+ scroller.scrollDown($old);
break;
}
-
- if ($new && $new.is('li')) {
- $old.removeClass('selected');
- $new.addClass('selected');
- } else if (e.keyCode == 38) {
- $(window).unbind(e);
- $("#textfilter input").focus();
- }
});
});
}
@@ -365,49 +200,43 @@ function keyboardScrolldownLeftPane() {
/* Configures the text filter */
function configureTextFilter() {
scheduler.add("init", function() {
- $("#filter").prepend("<span class='toggle-sidebar'></span>");
- $("#textfilter").append("<span class='input'><input placeholder='Filter' id='index-input' type='text' accesskey='/'/></span><span class='clear'>✖</span>");
var input = $("#textfilter input");
- resizeFilterBlock();
input.bind('keyup', function(event) {
- if (event.keyCode == 27) { // escape
- input.attr("value", "");
- }
- if (event.keyCode == 40) { // down arrow
- $(window).unbind("keydown");
- keyboardScrolldownLeftPane();
- return false;
+ switch ( event.keyCode ) {
+ case 27: // escape
+ input.attr("value", "");
+ $("div#search-results").hide();
+ $("#search > span.close-results").hide();
+ $("#search > span#doc-title").show();
+ break;
+
+ case 38: // up arrow
+ return false;
+
+ case 40: // down arrow
+ $(window).unbind("keydown");
+ handleKeyNavigation();
+ return false;
}
- textFilter();
- });
- input.bind('keydown', function(event) {
- if (event.keyCode == 9) { // tab
- $("#template").contents().find("#mbrsel-input").focus();
- input.attr("value", "");
- return false;
- }
- textFilter();
+
+ searchAll();
});
- input.focus(function(event) { input.select(); });
});
scheduler.add("init", function() {
- $("#textfilter > .clear").click(function(){
+ $("#textfilter > .input > .clear").click(function() {
$("#textfilter input").attr("value", "");
- textFilter();
+ $("div#search-results").hide();
+ $("#search > span.close-results").hide();
+ $("#search > span#doc-title").show();
});
- $("#filter > span.toggle-sidebar").click(function() {
- $("#browser").toggleClass("full-screen");
- $("#content").toggleClass("full-screen");
- $(".packages").toggle();
- $("#letters").toggle();
- $("#kindfilter").toggle();
- });
- $(".pack").scroll(function() {
- var scroll = $(".pack").scrollTop();
- if (scroll > 0)
- $("#filter").addClass("scrolled");
- else
- $("#filter").removeClass("scrolled");
+ });
+
+ scheduler.add("init", function() {
+ $("div#search > span.close-results").click(function() {
+ $("div#search-results").hide();
+ $("#search > span.close-results").hide();
+ $("#search > span#doc-title").show();
+ $("#textfilter input").attr("value", "");
});
});
}
@@ -425,196 +254,322 @@ function compilePattern(query) {
}
}
-// Filters all focused templates and packages. This function should be made less-blocking.
-// @param query The string of the query
-function textFilter() {
- var query = $("#textfilter input").attr("value") || '';
- var queryRegExp = compilePattern(query);
-
- // if we are filtering on types, then we have to display types
- // ("display packages only" is not possible when filtering)
- if (query !== "") {
- kindFilter("all");
- }
+/** Searches packages for entites matching the search query using a regex
+ *
+ * @param {[Object]} pack: package being searched
+ * @param {RegExp} regExp: a regular expression for finding matching entities
+ */
+function searchPackage(pack, regExp) {
+ scheduler.add("search", function() {
+ var entities = Index.PACKAGES[pack];
+ var matched = [];
+ var notMatching = [];
+
+ scheduler.add("search", function() {
+ searchMembers(entities, regExp, pack);
+ });
- // Three things trigger a reload of the left pane list:
- // typeof textFilter.lastQuery === "undefined" <-- first load, there is nothing yet in the left pane
- // textFilter.lastQuery !== query <-- the filter text has changed
- // focusFilterState != null <-- a package has been "focused"
- if ((typeof textFilter.lastQuery === "undefined") || (textFilter.lastQuery !== query) || (focusFilterState != null)) {
+ entities.forEach(function (elem) {
+ regExp.test(elem.name) ? matched.push(elem) : notMatching.push(elem);
+ });
- textFilter.lastQuery = query;
+ var results = {
+ "matched": matched,
+ "package": pack
+ };
- scheduler.clear("filter");
+ scheduler.add("search", function() {
+ handleSearchedPackage(results, regExp);
+ setProgress();
+ });
+ });
+}
- $('#tpl').html('');
+function searchMembers(entities, regExp, pack) {
+ var memDiv = document.getElementById("member-results");
+ var packLink = document.createElement("a");
+ packLink.className = "package";
+ packLink.appendChild(document.createTextNode(pack));
+ packLink.style.display = "none";
+ packLink.title = pack;
+ packLink.href = toRoot + urlFriendlyEntity(pack).replace(new RegExp("\\.", "g"), "/") + "/index.html";
+ memDiv.appendChild(packLink);
+
+ var entityUl = document.createElement("ul");
+ entityUl.className = "entities";
+ memDiv.appendChild(entityUl);
+
+ entities.forEach(function(entity) {
+ var entityLi = document.createElement("li");
+ var name = entity.name.split('.').pop()
+
+ var iconElem = document.createElement("a");
+ iconElem.className = "icon " + entity.kind;
+ iconElem.title = name + " " + entity.kind;
+ iconElem.href = toRoot + entity[entity.kind];
+ entityLi.appendChild(iconElem);
+
+ if (entity.kind != "object" && entity.object) {
+ var companion = document.createElement("a");
+ companion.className = "icon object";
+ companion.title = name + " companion object";
+ companion.href = toRoot + entity.object;
+ entityLi.insertBefore(companion, iconElem);
+ } else {
+ var spacer = document.createElement("div");
+ spacer.className = "icon spacer";
+ entityLi.insertBefore(spacer, iconElem);
+ }
- var index = 0;
+ var nameElem = document.createElement("span");
+ nameElem.className = "entity";
- var searchLoop = function () {
- var packages = Index.keys(Index.PACKAGES).sort();
+ var entityUrl = document.createElement("a");
+ entityUrl.title = entity.shortDescription ? entity.shortDescription : name;
+ entityUrl.href = toRoot + entity[entity.kind];
+ entityUrl.appendChild(document.createTextNode(name));
- while (packages[index]) {
- var pack = packages[index];
- var children = Index.PACKAGES[pack];
- index++;
+ nameElem.appendChild(entityUrl);
+ entityLi.appendChild(nameElem);
- if (focusFilterState) {
- if (pack == focusFilterState ||
- pack.indexOf(focusFilterState + '.') == 0) {
- ;
- } else {
- continue;
- }
- }
+ var membersUl = document.createElement("ul");
+ membersUl.className = "members";
+ entityLi.appendChild(membersUl);
- var matched = $.grep(children, function (child, i) {
- return queryRegExp.test(child.name);
- });
- if (matched.length > 0) {
- $('#tpl').append(Index.createPackageTree(pack, matched,
- focusFilterState));
- scheduler.add('filter', searchLoop);
- return;
+ searchEntity(entity, membersUl, regExp)
+ .then(function(res) {
+ if (res.length > 0) {
+ packLink.style.display = "block";
+ entityUl.appendChild(entityLi);
}
- }
-
- $('#tpl a.packfocus').click(function () {
- focusFilter($(this).parent().parent());
- $("#tpl").addClass("packfocused");
});
- configureHideFilter();
- };
+ });
+}
- scheduler.add('filter', searchLoop);
+/** This function inserts `li` into the `ul` ordered by the li's id
+ *
+ * @param {Node} ul: the list in which to insert `li`
+ * @param {Node} li: item to insert
+ */
+function insertSorted(ul, li) {
+ var lis = ul.childNodes;
+ var beforeLi = null;
+
+ for (var i = 0; i < lis.length; i++) {
+ if (lis[i].id > li.id)
+ beforeLi = lis[i];
}
+
+ // if beforeLi == null, it will be inserted last
+ ul.insertBefore(li, beforeLi);
}
-/* Configures the hide tool by adding the hide link to all packages. */
-function configureHideFilter() {
- $('#tpl li.pack a.packhide').click(function () {
- var packhide = $(this)
- var action = packhide.text();
+/** Defines the callback when a package has been searched and searches its
+ * members
+ *
+ * It will search all entities which matched the regExp.
+ *
+ * @param {Object} res: this is the searched package. It will contain the map
+ * from the `searchPackage`function.
+ * @param {RegExp} regExp
+ */
+function handleSearchedPackage(res, regExp) {
+ $("div#search-results").show();
+ $("#search > span.close-results").show();
+ $("#search > span#doc-title").hide();
- var ol = $(this).parent().parent();
+ var searchRes = document.getElementById("results-content");
+ var entityDiv = document.getElementById("entity-results");
- if (action == "hide") {
- Index.hidePackage(ol);
- packhide.text("show");
- }
- else {
- Index.showPackage(ol, kindFilterState);
- packhide.text("hide");
- }
- return false;
- });
-}
+ var packLink = document.createElement("a");
+ packLink.className = "package";
+ packLink.title = res.package;
+ packLink.href = toRoot + urlFriendlyEntity(res.package).replace(new RegExp("\\.", "g"), "/") + "/index.html";
+ packLink.appendChild(document.createTextNode(res.package));
-/* Configures the focus tool by adding the focus bar in the filter box (initially hidden), and by adding the focus
- link to all packages. */
-function configureFocusFilter() {
- scheduler.add("init", function() {
- focusFilterState = null;
- if ($("#focusfilter").length == 0) {
- $("#filter").append("<div id='focusfilter'>focused on <span class='focuscoll'></span> <a class='focusremove'>✖</a></div>");
- $("#focusfilter > .focusremove").click(function(event) {
- textFilter();
-
- $("#focusfilter").hide();
- $("#kindfilter").show();
- $("#tpl").removeClass("packfocused");
- resizeFilterBlock();
- focusFilterState = null;
- });
- $("#focusfilter").hide();
- resizeFilterBlock();
- }
- });
- scheduler.add("init", function() {
- $('#tpl li.pack a.packfocus').click(function () {
- focusFilter($(this).parent());
- return false;
- });
- });
-}
+ if (res.matched.length == 0)
+ packLink.style.display = "none";
-/* Focuses the entity index on a specific package. To do so, it will copy the sub-templates and sub-packages of the
- focuses package into the top-level templates and packages position of the index. The original top-level
- @param package The <li> element that corresponds to the package in the entity index */
-function focusFilter(package) {
- scheduler.clear("filter");
+ entityDiv.appendChild(packLink);
- var currentFocus = $('li.pack > .tplshow', package).text();
- $("#focusfilter > .focuscoll").empty();
- $("#focusfilter > .focuscoll").append(currentFocus);
+ var ul = document.createElement("ul")
+ ul.className = "entities";
- $("#focusfilter").show();
- $("#kindfilter").hide();
- resizeFilterBlock();
- focusFilterState = currentFocus;
- kindFilterSync();
+ // Generate html list items from results
+ res.matched
+ .map(function(entity) { return listItem(entity, regExp); })
+ .forEach(function(li) { ul.appendChild(li); });
- textFilter();
+ entityDiv.appendChild(ul);
}
-function configureKindFilter() {
- scheduler.add("init", function() {
- kindFilterState = "all";
- $("#filter").append("<div id='kindfilter-container'><div id='kindfilter'><span>Fold All</span></div></div>");
-
- while(isNaN(scrollbarWidth())) {
- // wait until the width is available
- }
+/** Searches an entity asynchronously for regExp matches in an entity's members
+ *
+ * @param {Object} entity: the entity to be searched
+ * @param {Node} ul: the list in which to insert the list item created
+ * @param {RegExp} regExp
+ */
+function searchEntity(entity, ul, regExp) {
+ return new Promise(function(resolve, reject) {
+ var matchingMembers = $.grep(entity.members, function(member, i) {
+ return regExp.test(member.label);
+ });
- $("#kindfilter").css({"margin-right": (scrollbarWidth() + 7) + "px"});
- $("#kindfilter").unbind("click");
- $("#kindfilter").click(function(event) {
- $("#kindfilter").toggleClass("open");
- kindFilter("packs");
+ resolve(matchingMembers);
+ })
+ .then(function(res) {
+ res.forEach(function(elem) {
+ var kind = document.createElement("span");
+ kind.className = "kind";
+ kind.appendChild(document.createTextNode(elem.kind));
+
+ var label = document.createElement("a");
+ label.title = elem.label;
+ label.href = toRoot + elem.link;
+ label.className = "label";
+ label.appendChild(document.createTextNode(elem.label));
+
+ var tail = document.createElement("span");
+ tail.className = "tail";
+ tail.appendChild(document.createTextNode(elem.tail));
+
+ var li = document.createElement("li");
+ li.appendChild(kind);
+ li.appendChild(label);
+ li.appendChild(tail);
+
+ ul.appendChild(li);
});
- resizeFilterBlock();
+ return res;
});
}
-function kindFilter(kind) {
- if (kind == "packs") {
- kindFilterState = "packs";
- kindFilterSync();
- $("#kindfilter > span").replaceWith("<span>Unfold All</span>");
- $("#kindfilter").unbind("click");
- $("#kindfilter").click(function(event) {
- $("#kindfilter").toggleClass("open");
- kindFilter("all");
- });
- }
- else {
- kindFilterState = "all";
- kindFilterSync();
- $("#kindfilter > span").replaceWith("<span>Fold All</span>");
- $("#kindfilter").unbind("click");
- $("#kindfilter").click(function(event) {
- $("#kindfilter").toggleClass("open");
- kindFilter("packs");
- });
+/** Creates a list item representing an entity
+ *
+ * @param {Object} entity, the searched entity to be displayed
+ * @param {RegExp} regExp
+ * @return {Node} list item containing entity
+ */
+function listItem(entity, regExp) {
+ var name = entity.name.split('.').pop()
+ var nameElem = document.createElement("span");
+ nameElem.className = "entity";
+
+ var entityUrl = document.createElement("a");
+ entityUrl.title = entity.shortDescription ? entity.shortDescription : name;
+ entityUrl.href = toRoot + entity[entity.kind];
+
+ entityUrl.appendChild(document.createTextNode(name));
+ nameElem.appendChild(entityUrl);
+
+ var iconElem = document.createElement("a");
+ iconElem.className = "icon " + entity.kind;
+ iconElem.title = name + " " + entity.kind;
+ iconElem.href = toRoot + entity[entity.kind];
+
+ var li = document.createElement("li");
+ li.id = entity.name.replace(new RegExp("\\.", "g"),"-");
+ li.appendChild(iconElem);
+ li.appendChild(nameElem);
+
+ if (entity.kind != "object" && entity.object) {
+ var companion = document.createElement("a");
+ companion.title = name + " companion object";
+ companion.href = toRoot + entity.object;
+ companion.className = "icon object";
+ li.insertBefore(companion, iconElem);
+ } else {
+ var spacer = document.createElement("div");
+ spacer.className = "icon spacer";
+ li.appendChild(spacer);
}
+
+ var ul = document.createElement("ul");
+ ul.className = "members";
+
+ li.appendChild(ul);
+
+ return li;
}
-/* Applies the kind filter. */
-function kindFilterSync() {
- if (kindFilterState == "all" || focusFilterState != null) {
- $("#tpl a.packhide").text('hide');
- $("#tpl ol.templates").show();
- } else {
- $("#tpl a.packhide").text('show');
- $("#tpl ol.templates").hide();
+/** Searches all packages and entities for the current search string in
+ * the input field "#textfilter"
+ *
+ * Then shows the results in div#search-results
+ */
+function searchAll() {
+ scheduler.clear("search"); // clear previous search
+ maxJobs = 1; // clear previous max
+ var searchStr = $("#textfilter input").attr("value").trim() || '';
+
+ if (searchStr === '') {
+ $("div#search-results").hide();
+ $("#search > span.close-results").hide();
+ $("#search > span#doc-title").show();
+ return;
}
+
+ $("div#results-content > span.search-text").remove();
+
+ var memberResults = document.getElementById("member-results");
+ memberResults.innerHTML = "";
+ var memberH1 = document.createElement("h1");
+ memberH1.className = "result-type";
+ memberH1.innerHTML = "Member results";
+ memberResults.appendChild(memberH1);
+
+ var entityResults = document.getElementById("entity-results");
+ entityResults.innerHTML = "";
+ var entityH1 = document.createElement("h1");
+ entityH1.className = "result-type";
+ entityH1.innerHTML = "Entity results";
+ entityResults.appendChild(entityH1);
+
+ $("div#results-content")
+ .prepend("<span class='search-text'>"
+ +" Showing results for <span class='query-str'>\"" + searchStr + "\"</span>"
+ +"</span>");
+
+ var regExp = compilePattern(searchStr);
+
+ // Search for all entities matching query
+ Index
+ .keys(Index.PACKAGES)
+ .sort()
+ .forEach(function(elem) { searchPackage(elem, regExp); })
}
-function resizeFilterBlock() {
- $("#tpl").css("top", $("#filter").outerHeight(true));
+/** Check if user agent is associated with a known mobile browser */
+function isMobile() {
+ return /Android|webOS|Mobi|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
}
-function scrollbarWidth() {
- return $("#tpl").width() - $("#tpl")[0].clientWidth;
+function urlFriendlyEntity(entity) {
+ var corr = {
+ '\\+': '$plus',
+ ':': '$colon'
+ };
+
+ for (k in corr)
+ entity = entity.replace(new RegExp(k, 'g'), corr[k]);
+
+ return entity;
+}
+
+var maxJobs = 1;
+function setProgress() {
+ var running = scheduler.numberOfJobs("search");
+ maxJobs = Math.max(maxJobs, running);
+
+ var percent = 100 - (running / maxJobs * 100);
+ var bar = document.getElementById("progress-fill");
+ bar.style.height = "100%";
+ bar.style.width = percent + "%";
+
+ if (percent == 100) {
+ setTimeout(function() {
+ bar.style.height = 0;
+ }, 500);
+ }
}
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/lato-v11-latin-100.eot b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/lato-v11-latin-100.eot
new file mode 100644
index 0000000000..7437fd9805
--- /dev/null
+++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/lato-v11-latin-100.eot
Binary files differ
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/lato-v11-latin-100.ttf b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/lato-v11-latin-100.ttf
new file mode 100644
index 0000000000..4e7128a481
--- /dev/null
+++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/lato-v11-latin-100.ttf
Binary files differ
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/lato-v11-latin-100.woff b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/lato-v11-latin-100.woff
new file mode 100644
index 0000000000..48915bb476
--- /dev/null
+++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/lato-v11-latin-100.woff
Binary files differ
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/scheduler.js b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/scheduler.js
index 4417f5b438..750c9099fd 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/scheduler.js
+++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/scheduler.js
@@ -1,5 +1,5 @@
// © 2010 EPFL/LAMP
-// code by Gilles Dubochet
+// code by Gilles Dubochet, Felix Mulder
function Scheduler() {
var scheduler = this;
@@ -7,38 +7,44 @@ function Scheduler() {
this.timeout = undefined;
this.queues = new Array(0); // an array of work pacakges indexed by index in the labels table.
this.labels = new Array(0); // an indexed array of labels indexed by priority. This should be short.
+
this.label = function(name, priority) {
this.name = name;
this.priority = priority;
}
+
this.work = function(fn, self, args) {
this.fn = fn;
this.self = self;
this.args = args;
}
+
this.addLabel = function(name, priority) {
var idx = 0;
while (idx < scheduler.queues.length && scheduler.labels[idx].priority <= priority) { idx = idx + 1; }
scheduler.labels.splice(idx, 0, new scheduler.label(name, priority));
scheduler.queues.splice(idx, 0, new Array(0));
}
+
this.clearLabel = function(name) {
- var idx = 0;
- while (idx < scheduler.queues.length && scheduler.labels[idx].name != name) { idx = idx + 1; }
- if (idx < scheduler.queues.length && scheduler.labels[i].name == name) {
+ var idx = scheduler.indexOf(name);
+ if (idx != -1) {
scheduler.labels.splice(idx, 1);
scheduler.queues.splice(idx, 1);
}
}
+
this.nextWork = function() {
var fn = undefined;
var idx = 0;
while (idx < scheduler.queues.length && scheduler.queues[idx].length == 0) { idx = idx + 1; }
- if (idx < scheduler.queues.length && scheduler.queues[idx].length > 0) {
+
+ if (idx < scheduler.queues.length && scheduler.queues[idx].length > 0)
var fn = scheduler.queues[idx].shift();
- }
+
return fn;
}
+
this.add = function(labelName, fn, self, args) {
var doWork = function() {
scheduler.timeout = setTimeout(function() {
@@ -53,19 +59,50 @@ function Scheduler() {
}
}, resolution);
}
- var idx = 0;
- while (idx < scheduler.labels.length && scheduler.labels[idx].name != labelName) { idx = idx + 1; }
- if (idx < scheduler.queues.length && scheduler.labels[idx].name == labelName) {
+
+ var idx = scheduler.indexOf(labelName)
+ if (idx != -1) {
scheduler.queues[idx].push(new scheduler.work(fn, self, args));
if (scheduler.timeout == undefined) doWork();
+ } else {
+ throw("queue for add is non existant");
}
- else throw("queue for add is non existant");
}
+
this.clear = function(labelName) {
+ scheduler.queues[scheduler.indexOf(labelName)] = new Array();
+ }
+
+ this.indexOf = function(label) {
var idx = 0;
- while (idx < scheduler.labels.length && scheduler.labels[idx].name != labelName) { idx = idx + 1; }
- if (idx < scheduler.queues.length && scheduler.labels[idx].name == labelName) {
- scheduler.queues[idx] = new Array();
+ while (idx < scheduler.labels.length && scheduler.labels[idx].name != label)
+ idx++;
+
+ return idx < scheduler.queues.length && scheduler.labels[idx].name == label ? idx : -1;
+ }
+
+ this.queueEmpty = function(label) {
+ var idx = scheduler.indexOf(label);
+ if (idx != -1)
+ return scheduler.queues[idx].length == 0;
+ else
+ throw("queue for label '" + label + "' is non existant");
+ }
+
+ this.scheduleLast = function(label, fn) {
+ if (scheduler.queueEmpty(label)) {
+ fn();
+ } else {
+ scheduler.add(label, function() {
+ scheduler.scheduleLast(label, fn);
+ });
}
}
+
+ this.numberOfJobs = function(label) {
+ var index = scheduler.indexOf(label);
+ if (index == -1) throw("queue for label '" + label + "' non-existent");
+
+ return scheduler.queues[index].length;
+ }
};
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.css b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.css
index 43e59076ca..2265f8f045 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.css
+++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.css
@@ -29,39 +29,6 @@ a img { border: none; }
input { border-width: 0px; }
-/* Fonts */
-@font-face {
- font-family: 'Source Code Pro';
- font-style: normal;
- font-weight: 400;
- src: url('source-code-pro-v6-latin-regular.eot');
- src: local('Source Code Pro'), local('SourceCodePro-Regular'),
- url('source-code-pro-v6-latin-regular.eot?#iefix') format('embedded-opentype'),
- url('source-code-pro-v6-latin-regular.woff') format('woff'),
- url('source-code-pro-v6-latin-regular.ttf') format('truetype');
-}
-@font-face {
- font-family: 'Source Code Pro';
- font-style: normal;
- font-weight: 700;
- src: url('source-code-pro-v6-latin-700.eot');
- src: local('Source Code Pro Bold'), local('SourceCodePro-Bold'),
- url('source-code-pro-v6-latin-700.eot?#iefix') format('embedded-opentype'),
- url('source-code-pro-v6-latin-700.woff') format('woff'),
- url('source-code-pro-v6-latin-700.ttf') format('truetype');
-}
-
-@font-face {
- font-family: 'Open Sans';
- font-style: normal;
- font-weight: 400;
- src: url('open-sans-v13-latin-regular.eot');
- src: local('Open Sans'), local('OpenSans'),
- url('open-sans-v13-latin-regular.eot?#iefix') format('embedded-opentype'),
- url('open-sans-v13-latin-regular.woff') format('woff'),
- url('open-sans-v13-latin-regular.ttf') format('truetype');
-}
-
/* Page */
body {
overflow-x: hidden;
@@ -69,32 +36,24 @@ body {
background-color: #f0f3f6;
}
-html {
- box-sizing: border-box;
-}
-
-*, *:before, *:after {
- box-sizing: inherit;
-}
-
#footer {
text-align: center;
color: #858484;
bottom: 0;
- width: 100%;
height: 20px;
+ margin: 0 1em 0.5em;
}
-a[href] {
+#content-container a[href] {
text-decoration: underline;
color: #315479;
}
-a[href]:hover {
+#content-container a[href]:hover {
text-decoration: none;
}
-body.trait > div#definition {
+body.trait div#definition {
background-color: #2e6d82;
}
@@ -107,10 +66,13 @@ body.trait > div#definition {
}
#definition {
+ position: relative;
display: block;
- padding: 5px 0px;
- height: 5.5em;
+ padding: 5px 0;
padding: 0;
+ margin: 0.5em;
+ border-radius: 0.2em;
+ box-shadow: 0 0 10px rgba(0,0,0,0.2);
}
#definition > a > img {
@@ -132,8 +94,8 @@ body.trait > div#definition {
}
#definition h1 > a {
- color: #fff;
- text-decoration: none;
+ color: #fff !important;
+ text-decoration: none !important;
}
#definition #owner {
@@ -156,13 +118,10 @@ body.trait > div#definition {
min-height: 18px;
font-size: 0.9em;
padding: 8px;
- box-shadow: 0 0 0.35em 0.05em rgba(0,0,0,0.5);
- position: fixed;
- top: 6.1em;
- left: 0;
width: 100%;
- z-index: 9998;
color: #103a51;
+ border-bottom-left-radius: 0.2em;
+ border-bottom-right-radius: 0.2em;
}
#signature > span.modifier_kind {
@@ -171,6 +130,7 @@ body.trait > div#definition {
text-align: left;
width: auto;
position: static;
+ padding-left: 0;
}
#signature > span.symbol {
@@ -197,43 +157,47 @@ body.trait > div#definition {
user-select: none;
}
-.toggleContainer .toggle:before {
- cursor: pointer;
- padding-left: 15px;
- content: "â–¶";
+.toggleContainer .toggle > i {
position: absolute;
- left: -2.7em;
+ left: -1.5em;
top: 0em;
+ font-size: 1.3em;
+ transition: 0.1s;
}
-.toggleContainer .toggle.open:before {
- content: "â–¼";
- position: absolute;
- left: -2.7em;
- top: 0em;
+.toggleContainer .toggle.open > i {
+ transform: rotate(90deg);
}
.toggleContainer .hiddenContent {
margin-top: 1.5em;
}
+#memberfilter > i.arrow {
+ position: absolute;
+ top: 0.45em;
+ left: -0.9em;
+ color: #fff;
+ font-size: 1.3em;
+ opacity: 0;
+ transition: 0.1s;
+ cursor: pointer;
+}
+
+#memberfilter > i.arrow.rotate {
+ transform: rotate(90deg);
+}
+
+#memberfilter:hover > i.arrow {
+ opacity: 1;
+}
+
.value #definition {
background-color: #103A51; /* blue */
- position: fixed;
- top: 0;
- left: 0;
- width: 100%;
- z-index: 9999;
}
.type #definition {
background-color: rgba(49, 101, 85, 1); /* green */
- position: fixed;
- top: 0;
- left: 0;
- min-height: 5.5em;
- width: 100%;
- z-index: 9999;
}
.abstract.type #definition {
@@ -285,12 +249,13 @@ body.abstract.type div.big-circle {
}
#template {
- margin: 0.9em 0.75em 2em 0.75em;
+ margin: 0.9em 0.75em 0.75em;
border-radius: 0.2em;
background-color: #fff;
-webkit-box-shadow: 0 0 10px rgba(0,0,0,0.1);
box-shadow: 0 0 10px rgba(0,0,0,0.1);
padding-bottom: 0.5em;
+ overflow: hidden;
}
#order {
@@ -369,19 +334,20 @@ dl.attributes > dd {
/* Member cells */
div.members > ol {
background-color: white;
- list-style: none
+ list-style: none;
+ padding: 0 10px;
}
div.members > ol > li {
- display: block;
- padding: 5px 0 6px;
- margin: 0 10px;
+ display: table;
+ width: 100%;
position: relative;
background-color: #e1e7ed;
border-radius: 0.2em;
color: #103a51;
padding: 5px 0 5px;
margin-bottom: 0.4em;
+ min-height: 2.8em;
}
div.members > ol >li.selected,
@@ -413,17 +379,25 @@ div.members > ol > li:last-child {
display: block;
}
-.signature .modifier_kind {
- position: absolute;
+.modifier_kind {
+ font-family: "Source Code Pro";
+ font-size: 0.8rem;
+ padding-right: 0.5em;
text-align: right;
- width: 14em;
+ display: table-cell;
+ white-space: nowrap;
+ width: 16em;
+}
+
+.symbol {
+ font-family: "Source Code Pro";
}
-.signature > a > .symbol > .name {
+a > .symbol > .name {
text-decoration: underline;
}
-.signature > a:hover > .symbol > .name {
+a:hover > .symbol > .name {
text-decoration: none;
}
@@ -432,8 +406,7 @@ div.members > ol > li:last-child {
}
.signature > .symbol {
- display: block;
- padding-left: 14.7em;
+ display: inline;
}
.signature .name {
@@ -441,62 +414,65 @@ div.members > ol > li:last-child {
font-weight: bold;
}
-.signature .symbol > .implicit {
+span.symbol > span.name {
+ font-weight: bold;
+}
+
+.symbol > .implicit {
display: inline-block;
font-weight: bold;
text-decoration: underline;
color: darkgreen;
}
-.signature .symbol .shadowed {
+.symbol .shadowed {
color: darkseagreen;
}
-.signature .symbol .params > .implicit {
+.symbol .params > .implicit {
font-style: italic;
}
-.signature .symbol .deprecated {
+.symbol .deprecated {
text-decoration: line-through;
}
-.signature .symbol .params .default {
+.symbol .params .default {
font-style: italic;
}
-#template .signature.closed {
+#template .closed {
cursor: pointer;
}
-#template .signature.closed:before {
- content: "â–¶";
- position: absolute;
- left: 1em;
- top: 0.5em;
-}
-
-#template .signature.opened {
+#template .opened {
cursor: pointer;
}
-#template .signature.opened:before {
- content: "â–¼";
+i.unfold-arrow {
+ font-size: 1em;
position: absolute;
- left: 1em;
- top: 0.5em;
- color: #2C475C;
+ top: 0.55em;
+ left: 0.7em;
+ transition: 0.1s;
}
-#template .values .signature .name {
+#template .modifier_kind.opened > i.unfold-arrow {
+ transform: rotate(90deg);
+}
+
+#template .values .name {
+ font-weight: 600;
color: darkblue;
}
-#template .types .signature .name {
+#template .types .name {
+ font-weight: 600;
color: darkgreen;
}
.full-signature-usecase h4 span {
- font-size: 0.9em;
+ font-size: 0.8rem;
}
.full-signature-usecase > #signature {
@@ -505,19 +481,19 @@ div.members > ol > li:last-child {
top: 0;
}
-.full-signature-usecase > .signature.closed:before {
- content: "" !important;
-}
-
-.full-signature-usecase > .signature.opened:before {
- content: "" !important;
+/* Hide unfold arrow where appropriate */
+#template li[fullComment=no] .modifier_kind > i.unfold-arrow,
+div#definition > h4#signature > span.modifier_kind > i.unfold-arrow,
+.full-signature-usecase > .signature > .closed > i.unfold-arrow,
+.full-signature-usecase > .signature > .opened > i.unfold-arrow {
+ display: none;
}
-#template .full-signature-usecase > .signature.closed {
+#template .full-signature-usecase > .signature > .closed {
background: none;
}
-#template .full-signature-usecase > .signature.opened {
+#template .full-signature-usecase > .signature > .opened {
background: none;
}
@@ -531,8 +507,8 @@ div.members > ol > li:last-child {
#definition .morelinks {
text-align: right;
position: absolute;
- top: 40px;
- right: 10px;
+ top: 2.95em;
+ right: 1em;
width: 450px;
font-family: 'Source Sans Pro', 'Helvetica Neue', Arial, sans-serif;
}
@@ -725,11 +701,11 @@ div.fullcomment {
#template div.fullcommenttop,
#template div.fullcomment {
display:none;
- margin: 5px 0 0 14.7em;
+ margin: 0.5em 1em 0 0;
}
#template .shortcomment {
- margin: 5px 0 0 14.7em;
+ margin: 5px 0 0 0;
padding: 0;
font-family: "Open Sans";
}
@@ -739,7 +715,6 @@ div.fullcomment .block {
border-top: 2px solid #fff;
margin-top: 5px;
overflow: hidden;
- margin-right: 0.5em;
font-family: "Open Sans";
}
@@ -818,7 +793,7 @@ div.fullcomment dl.paramcmts > dd {
/* Members filter tool */
-#textfilter {
+#memberfilter {
position: relative;
display: block;
height: 2.7em;
@@ -826,7 +801,7 @@ div.fullcomment dl.paramcmts > dd {
margin-left: 1.5em;
}
-#textfilter > .input {
+#memberfilter > .input {
display: block;
position: absolute;
top: 0;
@@ -835,7 +810,7 @@ div.fullcomment dl.paramcmts > dd {
transition: 0.2s;
}
-#textfilter > .input > input {
+#memberfilter > .input > input {
color: #fff;
width: 100%;
border-radius: 0.2em;
@@ -844,17 +819,16 @@ div.fullcomment dl.paramcmts > dd {
font-family: "Open Sans";
}
-#textfilter > .clear {
+#memberfilter > .clear {
display: none;
position: absolute;
- top: 0.6em;
- right: -0.65em;
- height: 23px;
- width: 21px;
+ top: 0.55em;
color: rgba(255, 255, 255, 0.4);
+ right: 0;
+ font-size: 1.2em;
}
-#textfilter > .clear:hover {
+#memberfilter > .clear:hover {
color: #fff;
cursor: pointer;
}
@@ -893,21 +867,20 @@ div.fullcomment dl.paramcmts > dd {
opacity: 1;
}
-#mbrsel:hover #textfilter > .input {
+#mbrsel:hover #memberfilter > .input {
left: 0.7em;
}
-#mbrsel > div.toggle:before {
+#mbrsel > div.toggle > i {
cursor: pointer;
- content: "â–¶";
position: absolute;
left: 0;
top: 0;
color: #fff;
}
-#mbrsel > div.toggle.open:before {
- content: "â–¼";
+#mbrsel > div.toggle.open > i {
+ transform: rotate(90deg);
}
#mbrsel > div#filterby {
@@ -1072,24 +1045,16 @@ and (-webkit-device-pixel-ratio: 2)
min-width: 300px;
}
- .signature .modifier_kind {
- width: 10em;
- }
-
- .signature > .symbol {
- padding-left: 10.7em;
- }
-
- #template .shortcomment {
- margin-left: 10.7em;
+ #template .modifier_kind {
+ width: 1px;
+ padding-left: 2.5em;
}
- #template div.fullcommenttop, #template div.fullcomment {
- margin-left: 10.7em;
+ span.modifier_kind > span.modifier {
+ display: none;
}
#definition {
- min-width: 300px;
height: 6em;
}
@@ -1125,16 +1090,8 @@ only screen /* iPhone 6 */
and (max-device-width: 667px)
and (-webkit-device-pixel-ratio: 2)
{
- #definition {
- position: absolute !important;
- min-height: 0 !important;
- height: 4em !important;
- }
-
#signature {
- position: absolute !important;
font-size: 0.7em;
- top: 5.7em;
}
#definition > h1 {
@@ -1149,12 +1106,8 @@ and (-webkit-device-pixel-ratio: 2)
padding-top: 0.7em;
}
- div.fullcommenttop {
- margin-top: 11.6em;
- }
-
- #template .shortcomment {
- margin-left: 1em;
+ #signature > span.modifier_kind {
+ width: auto;
}
div.fullcomment dl.attributes > dt {
@@ -1167,10 +1120,6 @@ and (-webkit-device-pixel-ratio: 2)
clear: both;
}
- #template div.fullcommenttop, #template div.fullcomment {
- margin-left: 1em;
- }
-
.big-circle {
width: 3em;
height: 3em;
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.js b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.js
index 3baf0d9db7..b0719b1ed5 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.js
+++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.js
@@ -17,38 +17,16 @@ $(document).ready(function() {
});
$("#template > div > div > ol > li > span > a").click(function(e) {
- $("#template > div > div > ol > li").removeClass("selected");
- $(this).parent().parent().addClass("selected");
- var defHeight = $("#definition").height() + $("#signature").height() + 50;
- $('html,body').animate({scrollTop: $(this).offset().top - defHeight}, 500);
- });
-
- /* Handle dynamic size of signature and offset the fullcommenttop div
- * appropriately
- *
- * Some mobile devices render quite slowly, delay the margin-top
- * calculation if mobile
- */
- if(/Android|webOS|Mobi|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) {
- setTimeout(function() {
- $("div.fullcommenttop").css({
- "margin-top": $("#definition").height() + $("#signature").height() + 15
- });
- }, 1000);
- } else {
- $("div.fullcommenttop").css({
- "margin-top": $("#definition").height() + $("#signature").height() + 15
- });
- }
-
- /* When the window is resized, adjust the fullcommenttop div's offset */
- $(window).resize(function() {
- $("div.fullcommenttop").css({
- "margin-top": $("#definition").height() + $("#signature").height() + 15
- });
+ var href = $(this).attr("href");
+ if (href.indexOf("#") != -1) {
+ e.preventDefault();
+ location.hash = href.split("#").pop();
+ $("#template > div > div > ol > li").removeClass("selected");
+ var parent = $(this).parent().parent().addClass("selected");
+ $("#content-container").animate({scrollTop: $("#content-container").scrollTop() + $(this).offset().top - $("#search").height() - 22}, 500);
+ }
});
-
var controls = {
visibility: {
publicOnly: $("#visbl").find("> ol > li.public"),
@@ -93,10 +71,8 @@ $(document).ready(function() {
}
filter();
- window.scrollTo(0, 0);
- var defHeight = $("#definition").height() + $("#signature").height() + 50;
jqElemParent.addClass("selected");
- $('html,body').animate({scrollTop: jqElemParent.offset().top - defHeight}, 1000);
+ $("#content-container").animate({scrollTop: jqElemParent.offset().top - $("#search").height() - 5 }, 1000);
}
var isHiddenClass = function (name) {
@@ -116,7 +92,8 @@ $(document).ready(function() {
return isHidden(this);
}).removeClass("in").addClass("out");
- $("#mbrsel > div.toggle").click(function() {
+ $("#memberfilter > i.arrow").click(function() {
+ $(this).toggleClass("rotate");
$("#filterby").toggle();
});
@@ -124,7 +101,7 @@ $(document).ready(function() {
filter();
// Member filter box
- var input = $("#textfilter input");
+ var input = $("#memberfilter input");
input.bind("keyup", function(event) {
switch ( event.keyCode ) {
@@ -161,8 +138,8 @@ $(document).ready(function() {
input.focus(function(event) {
input.select();
});
- $("#textfilter > .clear").click(function() {
- $("#textfilter input").attr("value", "");
+ $("#memberfilter > .clear").click(function() {
+ $("#memberfilter input").attr("value", "");
filter();
});
$(document).keydown(function(event) {
@@ -262,7 +239,7 @@ $(document).ready(function() {
/* Add toggle arrows */
//var docAllSigs = $("#template li").has(".fullcomment").find(".signature");
// trying to speed things up a little bit
- var docAllSigs = $("#template li[fullComment=yes] .signature");
+ var docAllSigs = $("#template li[fullComment=yes] .modifier_kind");
function commentToggleFct(signature){
var parent = signature.parent();
@@ -271,12 +248,22 @@ $(document).ready(function() {
var vis = $(":visible", fullComment);
signature.toggleClass("closed").toggleClass("opened");
if (vis.length > 0) {
- shortComment.slideDown(100);
- fullComment.slideUp(100);
+ if (!isMobile()) {
+ shortComment.slideDown(100);
+ fullComment.slideUp(100);
+ } else {
+ fullComment.hide();
+ shortComment.show();
+ }
}
else {
- shortComment.slideUp(100);
- fullComment.slideDown(100);
+ if (!isMobile()) {
+ shortComment.slideUp(100);
+ fullComment.slideDown(100);
+ } else {
+ shortComment.hide();
+ fullComment.show();
+ }
}
};
docAllSigs.addClass("closed");
@@ -288,7 +275,13 @@ $(document).ready(function() {
function toggleShowContentFct(e){
e.toggleClass("open");
var content = $(".hiddenContent", e.parent().get(0));
- (content.is(':visible') ? content.slideUp : content.slideDown)(100);
+ if(content.is(':visible')) {
+ if (!isMobile()) content.slideUp(100);
+ else content.hide();
+ } else {
+ if (!isMobile()) content.slideDown(100);
+ else content.show();
+ }
};
$(".toggle:not(.diagram-link)").click(function() {
@@ -315,11 +308,13 @@ $(document).ready(function() {
}
$("#mbrsel-input").on("focus", function() {
- $("#textfilter > .clear").show();
+ $("#memberfilter > .clear").show();
});
$("#mbrsel-input").on("blur", function() {
- $("#textfilter > .clear").hide();
+ setTimeout(function() {
+ $("#memberfilter > .clear").hide();
+ }, 10);
});
});
@@ -447,7 +442,7 @@ function initInherit() {
/* filter used to take boolean scrollToMember */
function filter() {
- var query = $.trim($("#textfilter input").val()).toLowerCase();
+ var query = $.trim($("#memberfilter input").val()).toLowerCase();
query = query.replace(/[-[\]{}()*+?.,\\^$|#]/g, "\\$&").replace(/\s+/g, "|");
var queryRegExp = new RegExp(query, "i");
var privateMembersHidden = $("#visbl > ol > li.public").hasClass("in");
@@ -534,21 +529,24 @@ function filter() {
});
if (membersVisible)
- members.show();
+ members.show();
else
- members.hide();
+ members.hide();
};
return false;
};
-function windowTitle()
-{
+function windowTitle() {
try {
parent.document.title=document.title;
- }
- catch(e) {
+ } catch(e) {
// Chrome doesn't allow settings the parent's title when
// used on the local file system.
}
};
+
+/** Check if user agent is associated with a known mobile browser */
+function isMobile() {
+ return /Android|webOS|Mobi|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
+}
diff --git a/src/scalap/decoder.properties b/src/scalap/decoder.properties
index 333f6ce715..9bb8d130ea 100644
--- a/src/scalap/decoder.properties
+++ b/src/scalap/decoder.properties
@@ -1,2 +1,2 @@
version.number=2.0.1
-copyright.string=(c) 2002-2015 LAMP/EPFL
+copyright.string=(c) 2002-2016 LAMP/EPFL
diff --git a/test/files/instrumented/indy-symbol-literal.scala b/test/files/instrumented/indy-symbol-literal.scala
new file mode 100644
index 0000000000..a1c333cf95
--- /dev/null
+++ b/test/files/instrumented/indy-symbol-literal.scala
@@ -0,0 +1,19 @@
+import scala.tools.partest.instrumented._
+import scala.tools.partest.instrumented.Instrumentation._
+
+object Test {
+ def main(args: Array[String]): Unit = {
+ 'warmup
+ startProfiling()
+ var i = 0;
+ while (i < 2) {
+ 'foo.name
+ i += 1
+ }
+ stopProfiling()
+ // Only expect a single call to lookup the interned Symbol at each call site the defines
+ // a single literal.
+ val Symbol_apply = MethodCallTrace("scala/Symbol$", "apply", "(Ljava/lang/String;)Lscala/Symbol;")
+ assert(getStatistics.get(Symbol_apply) == Some(1), getStatistics);
+ }
+}
diff --git a/test/files/neg/compile-time-only-a.check b/test/files/neg/compile-time-only-a.check
index b1ed1d24c2..a10f8b6489 100644
--- a/test/files/neg/compile-time-only-a.check
+++ b/test/files/neg/compile-time-only-a.check
@@ -13,9 +13,15 @@ compile-time-only-a.scala:36: error: C2
compile-time-only-a.scala:38: error: C3
new C3(2)
^
+compile-time-only-a.scala:39: error: C3
+ C3(2)
+ ^
compile-time-only-a.scala:41: error: C4
new C4(2)
^
+compile-time-only-a.scala:42: error: C4
+ C4(2)
+ ^
compile-time-only-a.scala:45: error: C5
2.ext
^
@@ -73,4 +79,4 @@ compile-time-only-a.scala:75: error: placebo
compile-time-only-a.scala:75: error: placebo
@placebo def x = (2: @placebo)
^
-25 errors found
+27 errors found
diff --git a/test/files/neg/t8685.check b/test/files/neg/t8685.check
new file mode 100644
index 0000000000..1780a20b6e
--- /dev/null
+++ b/test/files/neg/t8685.check
@@ -0,0 +1,45 @@
+t8685.scala:6: warning: constructor D in class D is deprecated: ctor D is depr
+case class D @deprecated("ctor D is depr", since="now") (i: Int)
+ ^
+t8685.scala:35: warning: class C is deprecated: class C is depr
+ def f = C(42)
+ ^
+t8685.scala:37: warning: object E is deprecated: module E is depr
+ def h = E(42)
+ ^
+t8685.scala:37: warning: class E is deprecated: class E is depr
+ def h = E(42)
+ ^
+t8685.scala:38: warning: object F is deprecated: module F is depr
+ def i = F.G(42)
+ ^
+t8685.scala:39: warning: object F in object Extra is deprecated: Extra module F is depr
+ def j = Extra.F.G(42)
+ ^
+t8685.scala:43: warning: value gg in trait Applies is deprecated: member gg
+ def k = this.gg.H(0)
+ ^
+t8685.scala:45: warning: class K in object J is deprecated: Inner K is depr
+ def l = J.K(42)
+ ^
+t8685.scala:48: warning: class C is deprecated: class C is depr
+ def f = new C(42)
+ ^
+t8685.scala:49: warning: constructor D in class D is deprecated: ctor D is depr
+ def g = new D(42)
+ ^
+t8685.scala:50: warning: class E is deprecated: class E is depr
+ def h = new E(42)
+ ^
+t8685.scala:51: warning: object F is deprecated: module F is depr
+ def i = new F.G(42)
+ ^
+t8685.scala:52: warning: object F in object Extra is deprecated: Extra module F is depr
+ def j = new Extra.F.G(42)
+ ^
+t8685.scala:53: warning: class K in object J is deprecated: Inner K is depr
+ def l = new J.K(42)
+ ^
+error: No warnings can be incurred under -Xfatal-warnings.
+14 warnings found
+one error found
diff --git a/test/files/neg/t8685.flags b/test/files/neg/t8685.flags
new file mode 100644
index 0000000000..c6bfaf1f64
--- /dev/null
+++ b/test/files/neg/t8685.flags
@@ -0,0 +1 @@
+-deprecation -Xfatal-warnings
diff --git a/test/files/neg/t8685.scala b/test/files/neg/t8685.scala
new file mode 100644
index 0000000000..711680ecbd
--- /dev/null
+++ b/test/files/neg/t8685.scala
@@ -0,0 +1,54 @@
+
+
+@deprecated("class C is depr", since="now")
+case class C(i: Int)
+
+case class D @deprecated("ctor D is depr", since="now") (i: Int)
+
+@deprecated("class E is depr", since="now")
+case class E(i: Int)
+@deprecated("module E is depr", since="now")
+object E
+
+@deprecated("module F is depr", since="now")
+object F {
+ case class G(i: Int)
+}
+
+object G {
+ case class H(i: Int)
+}
+
+object Extra {
+ @deprecated("Extra module F is depr", since="now")
+ object F {
+ case class G(i: Int)
+ }
+}
+
+object J {
+ @deprecated("Inner K is depr", since="now")
+ case class K(i: Int)
+}
+
+trait Applies {
+ def f = C(42)
+ def g = D(42)
+ def h = E(42)
+ def i = F.G(42)
+ def j = Extra.F.G(42)
+
+ @deprecated("member gg", since="now")
+ val gg = G
+ def k = this.gg.H(0)
+
+ def l = J.K(42)
+}
+trait News {
+ def f = new C(42)
+ def g = new D(42)
+ def h = new E(42)
+ def i = new F.G(42)
+ def j = new Extra.F.G(42)
+ def l = new J.K(42)
+}
diff --git a/test/files/run/indy-via-macro-with-dynamic-args/Bootstrap.java b/test/files/run/indy-via-macro-with-dynamic-args/Bootstrap.java
new file mode 100644
index 0000000000..5c9ce01cf4
--- /dev/null
+++ b/test/files/run/indy-via-macro-with-dynamic-args/Bootstrap.java
@@ -0,0 +1,17 @@
+package test;
+
+import java.lang.invoke.*;
+import java.util.regex.Pattern;
+
+public final class Bootstrap {
+ private Bootstrap() {
+ }
+
+ /** Pre-compile a regex */
+ public static CallSite bootstrap(MethodHandles.Lookup lookup, String invokedName,
+ MethodType invokedType,
+ String value) throws Throwable {
+ MethodHandle Pattern_matcher = MethodHandles.lookup().findVirtual(java.util.regex.Pattern.class, "matcher", MethodType.fromMethodDescriptorString("(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;", lookup.lookupClass().getClassLoader()));
+ return new ConstantCallSite(Pattern_matcher.bindTo(Pattern.compile(value)));
+ }
+}
diff --git a/test/files/run/indy-via-macro-with-dynamic-args/Test_2.scala b/test/files/run/indy-via-macro-with-dynamic-args/Test_2.scala
new file mode 100644
index 0000000000..77c2b522c7
--- /dev/null
+++ b/test/files/run/indy-via-macro-with-dynamic-args/Test_2.scala
@@ -0,0 +1,6 @@
+object Test {
+ def main(args: Array[String]) {
+ val s = "foo!bar"
+ assert(Macro.matcher("foo.bar", s).matches == true)
+ }
+}
diff --git a/test/files/run/indy-via-macro-with-dynamic-args/macro_1.scala b/test/files/run/indy-via-macro-with-dynamic-args/macro_1.scala
new file mode 100644
index 0000000000..cb8719a235
--- /dev/null
+++ b/test/files/run/indy-via-macro-with-dynamic-args/macro_1.scala
@@ -0,0 +1,33 @@
+import java.util.regex._
+
+import scala.reflect.internal.SymbolTable
+import scala.reflect.macros.blackbox._
+import language.experimental.macros
+
+object Macro {
+ /**
+ * Equivalent to Pattern.compile(pat).matcher(text), but caches the compiled regex (using invokedynamic) if
+ * `pat` is a literal.
+ */
+ def matcher(pat: String, text: CharSequence): Matcher = macro Macro.impl
+ def impl(c: Context)(pat: c.Tree, text: c.Tree): c.Tree = {
+ def Indy(bootstrapMethod: c.Symbol, bootstrapArgs: List[c.universe.Literal], dynArgs: List[c.Tree]): c.Tree = {
+ val symtab = c.universe.asInstanceOf[SymbolTable]
+ import symtab._
+ val paramSym = NoSymbol.newTermSymbol(TermName("x")).setInfo(typeOf[CharSequence])
+ val dummySymbol = NoSymbol.newTermSymbol(TermName("matcher")).setInfo(internal.methodType(paramSym :: Nil, typeOf[java.util.regex.Matcher]))
+ val bootstrapArgTrees: List[Tree] = Literal(Constant(bootstrapMethod)).setType(NoType) :: bootstrapArgs.asInstanceOf[List[Tree]]
+ val result = ApplyDynamic(Ident(dummySymbol).setType(dummySymbol.info), bootstrapArgTrees ::: dynArgs.asInstanceOf[List[Tree]])
+ result.setType(dummySymbol.info.resultType)
+ result.asInstanceOf[c.Tree]
+ }
+ import c.universe._
+ pat match {
+ case l @ Literal(Constant(pat: String)) =>
+ val boostrapSym = typeOf[test.Bootstrap].companion.member(TermName("bootstrap"))
+ Indy(boostrapSym, l :: Nil, text :: Nil)
+ case _ =>
+ q"_root_.java.util.regex.Pattern.compile($pat).matcher($text)"
+ }
+ }
+}
diff --git a/test/files/run/indy-via-macro/Bootstrap.java b/test/files/run/indy-via-macro/Bootstrap.java
new file mode 100644
index 0000000000..af4f5dfd4f
--- /dev/null
+++ b/test/files/run/indy-via-macro/Bootstrap.java
@@ -0,0 +1,16 @@
+package test;
+
+import java.lang.invoke.*;
+import java.util.regex.Pattern;
+
+public final class Bootstrap {
+ private Bootstrap() {
+ }
+
+ /** Pre-compile a regex */
+ public static CallSite bootstrap(MethodHandles.Lookup lookup, String invokedName,
+ MethodType invokedType,
+ String value) throws Throwable {
+ return new ConstantCallSite(MethodHandles.constant(Pattern.class, Pattern.compile(value)));
+ }
+}
diff --git a/test/files/run/indy-via-macro/Test_2.scala b/test/files/run/indy-via-macro/Test_2.scala
new file mode 100644
index 0000000000..830947a46b
--- /dev/null
+++ b/test/files/run/indy-via-macro/Test_2.scala
@@ -0,0 +1,5 @@
+object Test {
+ def main(args: Array[String]) {
+ assert(Macro.compilePattern("foo.bar").matcher("foo!bar").matches)
+ }
+} \ No newline at end of file
diff --git a/test/files/run/indy-via-macro/macro_1.scala b/test/files/run/indy-via-macro/macro_1.scala
new file mode 100644
index 0000000000..66e319e262
--- /dev/null
+++ b/test/files/run/indy-via-macro/macro_1.scala
@@ -0,0 +1,32 @@
+import java.util.regex.Pattern
+
+import scala.reflect.internal.SymbolTable
+import scala.reflect.macros.blackbox._
+import language.experimental.macros
+
+object Macro {
+ /**
+ * Equivalent to Pattern.compile(s), but caches the compiled regex (using invokedynamic) if
+ * `s` is a literal.
+ */
+ def compilePattern(s: String): Pattern = macro Macro.impl
+ def impl(c: Context)(s: c.Tree): c.Tree = {
+ def Indy(bootstrapMethod: c.Symbol, bootstrapArgs: List[c.universe.Literal]): c.Tree = {
+ val symtab = c.universe.asInstanceOf[SymbolTable]
+ import symtab._
+ val dummySymbol = NoSymbol.newTermSymbol(TermName("compile")).setInfo(NullaryMethodType(typeOf[Pattern]))
+ val args: List[Tree] = Literal(Constant(bootstrapMethod)).setType(NoType) :: bootstrapArgs.asInstanceOf[List[Tree]]
+ val result = ApplyDynamic(Ident(dummySymbol).setType(dummySymbol.info), args)
+ result.setType(dummySymbol.info.resultType)
+ result.asInstanceOf[c.Tree]
+ }
+ import c.universe._
+ s match {
+ case l @ Literal(Constant(s: String)) =>
+ val boostrapSym = typeOf[test.Bootstrap].companion.member(TermName("bootstrap"))
+ Indy(boostrapSym, l :: Nil)
+ case _ =>
+ q"_root_.java.util.regex.Pattern.compile($s)"
+ }
+ }
+}
diff --git a/test/files/run/numbereq.scala b/test/files/run/numbereq.scala
index 7ce4b23cf8..1f12d0643e 100644
--- a/test/files/run/numbereq.scala
+++ b/test/files/run/numbereq.scala
@@ -1,6 +1,7 @@
object Test {
def mkNumbers(x: Int): List[AnyRef] = {
- val base = List(
+ //Use explicit AnyRef to workaround known limitation of type inference with F-Bounds
+ val base = List[AnyRef](
BigDecimal(x),
BigInt(x),
new java.lang.Double(x.toDouble),
diff --git a/test/files/run/t7974.check b/test/files/run/t7974.check
index 4eae5eb152..f649161ae9 100644
--- a/test/files/run/t7974.check
+++ b/test/files/run/t7974.check
@@ -1,26 +1,12 @@
- // access flags 0x9
- public static <clinit>()V
- GETSTATIC scala/Symbol$.MODULE$ : Lscala/Symbol$;
- LDC "Symbolic1"
- INVOKEVIRTUAL scala/Symbol$.apply (Ljava/lang/String;)Lscala/Symbol;
- PUTSTATIC Symbols.symbol$1 : Lscala/Symbol;
- GETSTATIC scala/Symbol$.MODULE$ : Lscala/Symbol$;
- LDC "Symbolic2"
- INVOKEVIRTUAL scala/Symbol$.apply (Ljava/lang/String;)Lscala/Symbol;
- PUTSTATIC Symbols.symbol$2 : Lscala/Symbol;
- GETSTATIC scala/Symbol$.MODULE$ : Lscala/Symbol$;
- LDC "Symbolic3"
- INVOKEVIRTUAL scala/Symbol$.apply (Ljava/lang/String;)Lscala/Symbol;
- PUTSTATIC Symbols.symbol$3 : Lscala/Symbol;
- RETURN
- MAXSTACK = 2
- MAXLOCALS = 0
-
-
// access flags 0x1
public someSymbol1()Lscala/Symbol;
- GETSTATIC Symbols.symbol$1 : Lscala/Symbol;
+ INVOKEDYNAMIC apply()Lscala/Symbol; [
+ // handle kind 0x6 : INVOKESTATIC
+ scala/runtime/SymbolLiteral.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;)Ljava/lang/invoke/CallSite;
+ // arguments:
+ "Symbolic1"
+ ]
ARETURN
MAXSTACK = 1
MAXLOCALS = 1
@@ -28,7 +14,12 @@
// access flags 0x1
public someSymbol2()Lscala/Symbol;
- GETSTATIC Symbols.symbol$2 : Lscala/Symbol;
+ INVOKEDYNAMIC apply()Lscala/Symbol; [
+ // handle kind 0x6 : INVOKESTATIC
+ scala/runtime/SymbolLiteral.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;)Ljava/lang/invoke/CallSite;
+ // arguments:
+ "Symbolic2"
+ ]
ARETURN
MAXSTACK = 1
MAXLOCALS = 1
@@ -36,7 +27,12 @@
// access flags 0x1
public sameSymbol1()Lscala/Symbol;
- GETSTATIC Symbols.symbol$1 : Lscala/Symbol;
+ INVOKEDYNAMIC apply()Lscala/Symbol; [
+ // handle kind 0x6 : INVOKESTATIC
+ scala/runtime/SymbolLiteral.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;)Ljava/lang/invoke/CallSite;
+ // arguments:
+ "Symbolic1"
+ ]
ARETURN
MAXSTACK = 1
MAXLOCALS = 1
@@ -56,7 +52,12 @@
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
ALOAD 0
- GETSTATIC Symbols.symbol$3 : Lscala/Symbol;
+ INVOKEDYNAMIC apply()Lscala/Symbol; [
+ // handle kind 0x6 : INVOKESTATIC
+ scala/runtime/SymbolLiteral.bootstrap(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;)Ljava/lang/invoke/CallSite;
+ // arguments:
+ "Symbolic3"
+ ]
PUTFIELD Symbols.someSymbol3 : Lscala/Symbol;
RETURN
MAXSTACK = 2
diff --git a/test/files/specialized/fft.check b/test/files/specialized/fft.check
index 74cb9bb3b5..5283c6cbe2 100644
--- a/test/files/specialized/fft.check
+++ b/test/files/specialized/fft.check
@@ -1,4 +1,4 @@
Processing 65536 items
Boxed doubles: 0
-Boxed ints: 2
+Boxed ints: 0
Boxed longs: 1179811
diff --git a/test/junit/scala/collection/IteratorTest.scala b/test/junit/scala/collection/IteratorTest.scala
index b0639ef365..4df29e36c0 100644
--- a/test/junit/scala/collection/IteratorTest.scala
+++ b/test/junit/scala/collection/IteratorTest.scala
@@ -186,4 +186,32 @@ class IteratorTest {
assertEquals(1, y.next)
assertFalse(x.hasNext) // was true, after advancing underlying iterator
}
+ // SI-9623
+ @Test def noExcessiveHasNextInJoinIterator: Unit = {
+ var counter = 0
+ val exp = List(1,2,3,1,2,3)
+ def it: Iterator[Int] = new Iterator[Int] {
+ val parent = List(1,2,3).iterator
+ def next(): Int = parent.next
+ def hasNext: Boolean = { counter += 1; parent.hasNext }
+ }
+ // Iterate separately
+ val res = new mutable.ArrayBuffer[Int]
+ it.foreach(res += _)
+ it.foreach(res += _)
+ assertSameElements(exp, res)
+ assertEquals(8, counter)
+ // JoinIterator
+ counter = 0
+ res.clear
+ (it ++ it).foreach(res += _)
+ assertSameElements(exp, res)
+ assertEquals(8, counter) // was 17
+ // ConcatIterator
+ counter = 0
+ res.clear
+ (Iterator.empty ++ it ++ it).foreach(res += _)
+ assertSameElements(exp, res)
+ assertEquals(8, counter) // was 14
+ }
}
diff --git a/test/junit/scala/issues/BytecodeTest.scala b/test/junit/scala/issues/BytecodeTest.scala
index 7260f43c87..18f8b44391 100644
--- a/test/junit/scala/issues/BytecodeTest.scala
+++ b/test/junit/scala/issues/BytecodeTest.scala
@@ -3,7 +3,7 @@ package scala.issues
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import org.junit.Test
-import scala.tools.asm.Opcodes
+import scala.tools.asm.Opcodes._
import scala.tools.nsc.backend.jvm.AsmUtils
import scala.tools.nsc.backend.jvm.CodeGenTools._
import org.junit.Assert._
@@ -105,12 +105,10 @@ class BytecodeTest extends ClearAfterClass {
val unapplyLineNumbers = getSingleMethod(module, "unapply").instructions.filter(_.isInstanceOf[LineNumber])
assert(unapplyLineNumbers == List(LineNumber(2, Label(0))), unapplyLineNumbers)
- import Opcodes._
val expected = List(
LineNumber(4, Label(0)),
LineNumber(5, Label(5)),
- Jump(IFNE, Label(11)),
- Jump(GOTO, Label(20)),
+ Jump(IFEQ, Label(20)),
LineNumber(6, Label(11)),
Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false),
@@ -133,4 +131,81 @@ class BytecodeTest extends ClearAfterClass {
}
assertSameCode(mainIns, expected)
}
+
+ @Test
+ def bytecodeForBranches(): Unit = {
+ val code =
+ """class C {
+ | def t1(b: Boolean) = if (b) 1 else 2
+ | def t2(x: Int) = if (x == 393) 1 else 2
+ | def t3(a: Array[String], b: AnyRef) = a != b && b == a
+ | def t4(a: AnyRef) = a == null || null != a
+ | def t5(a: AnyRef) = (a eq null) || (null ne a)
+ | def t6(a: Int, b: Boolean) = if ((a == 10) && b || a != 1) 1 else 2
+ | def t7(a: AnyRef, b: AnyRef) = a == b
+ | def t8(a: AnyRef) = Nil == a || "" != a
+ |}
+ """.stripMargin
+
+ val List(c) = compileClasses(compiler)(code)
+
+ // t1: no unnecessary GOTOs
+ assertSameCode(getSingleMethod(c, "t1"), List(
+ VarOp(ILOAD, 1), Jump(IFEQ, Label(6)),
+ Op(ICONST_1), Jump(GOTO, Label(9)),
+ Label(6), Op(ICONST_2),
+ Label(9), Op(IRETURN)))
+
+ // t2: no unnecessary GOTOs
+ assertSameCode(getSingleMethod(c, "t2"), List(
+ VarOp(ILOAD, 1), IntOp(SIPUSH, 393), Jump(IF_ICMPNE, Label(7)),
+ Op(ICONST_1), Jump(GOTO, Label(10)),
+ Label(7), Op(ICONST_2),
+ Label(10), Op(IRETURN)))
+
+ // t3: Array == is translated to reference equality, AnyRef == to null checks and equals
+ assertSameCode(getSingleMethod(c, "t3"), List(
+ // Array ==
+ VarOp(ALOAD, 1), VarOp(ALOAD, 2), Jump(IF_ACMPEQ, Label(23)),
+ // AnyRef ==
+ VarOp(ALOAD, 2), VarOp(ALOAD, 1), VarOp(ASTORE, 3), Op(DUP), Jump(IFNONNULL, Label(14)),
+ Op(POP), VarOp(ALOAD, 3), Jump(IFNULL, Label(19)), Jump(GOTO, Label(23)),
+ Label(14), VarOp(ALOAD, 3), Invoke(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z", false), Jump(IFEQ, Label(23)),
+ Label(19), Op(ICONST_1), Jump(GOTO, Label(26)),
+ Label(23), Op(ICONST_0),
+ Label(26), Op(IRETURN)))
+
+ val t4t5 = List(
+ VarOp(ALOAD, 1), Jump(IFNULL, Label(6)),
+ VarOp(ALOAD, 1), Jump(IFNULL, Label(10)),
+ Label(6), Op(ICONST_1), Jump(GOTO, Label(13)),
+ Label(10), Op(ICONST_0),
+ Label(13), Op(IRETURN))
+
+ // t4: one side is known null, so just a null check on the other
+ assertSameCode(getSingleMethod(c, "t4"), t4t5)
+
+ // t5: one side known null, so just a null check on the other
+ assertSameCode(getSingleMethod(c, "t5"), t4t5)
+
+ // t6: no unnecessary GOTOs
+ assertSameCode(getSingleMethod(c, "t6"), List(
+ VarOp(ILOAD, 1), IntOp(BIPUSH, 10), Jump(IF_ICMPNE, Label(7)),
+ VarOp(ILOAD, 2), Jump(IFNE, Label(12)),
+ Label(7), VarOp(ILOAD, 1), Op(ICONST_1), Jump(IF_ICMPEQ, Label(16)),
+ Label(12), Op(ICONST_1), Jump(GOTO, Label(19)),
+ Label(16), Op(ICONST_2),
+ Label(19), Op(IRETURN)))
+
+ // t7: universal equality
+ assertInvoke(getSingleMethod(c, "t7"), "scala/runtime/BoxesRunTime", "equals")
+
+ // t8: no null checks invoking equals on modules and constants
+ assertSameCode(getSingleMethod(c, "t8"), List(
+ Field(GETSTATIC, "scala/collection/immutable/Nil$", "MODULE$", "Lscala/collection/immutable/Nil$;"), VarOp(ALOAD, 1), Invoke(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z", false), Jump(IFNE, Label(10)),
+ Ldc(LDC, ""), VarOp(ALOAD, 1), Invoke(INVOKEVIRTUAL, "java/lang/Object", "equals", "(Ljava/lang/Object;)Z", false), Jump(IFNE, Label(14)),
+ Label(10), Op(ICONST_1), Jump(GOTO, Label(17)),
+ Label(14), Op(ICONST_0),
+ Label(17), Op(IRETURN)))
+ }
}
diff --git a/test/junit/scala/issues/OptimizedBytecodeTest.scala b/test/junit/scala/issues/OptimizedBytecodeTest.scala
index 3c6f1ff25e..03c0c64442 100644
--- a/test/junit/scala/issues/OptimizedBytecodeTest.scala
+++ b/test/junit/scala/issues/OptimizedBytecodeTest.scala
@@ -36,7 +36,7 @@ class OptimizedBytecodeTest extends ClearAfterClass {
|}
""".stripMargin
val List(c) = compileClasses(compiler)(code)
- assertSameCode(getSingleMethod(c, "t").instructions.dropNonOp, List(Label(0), Jump(GOTO, Label(0))))
+ assertSameCode(getSingleMethod(c, "t"), List(Label(0), Jump(GOTO, Label(0))))
}
@Test
@@ -55,17 +55,10 @@ class OptimizedBytecodeTest extends ClearAfterClass {
""".stripMargin
val List(c) = compileClasses(compiler)(code)
- assertEquals(
- getSingleMethod(c, "t").instructions.summary,
- List(LDC, ASTORE, ALOAD /*0*/, ALOAD /*1*/, "C$$$anonfun$1", IRETURN))
-
- assertEquals(
- getSingleMethod(c, "C$$$anonfun$1").instructions.summary,
- List(LDC, "C$$$anonfun$2", IRETURN))
-
- assertEquals(
- getSingleMethod(c, "C$$$anonfun$2").instructions.summary,
- List(-1 /*A*/, GOTO /*A*/))
+ assertSameSummary(getSingleMethod(c, "t"), List(
+ LDC, ASTORE, ALOAD /*0*/, ALOAD /*1*/, "C$$$anonfun$1", IRETURN))
+ assertSameSummary(getSingleMethod(c, "C$$$anonfun$1"), List(LDC, "C$$$anonfun$2", IRETURN))
+ assertSameSummary(getSingleMethod(c, "C$$$anonfun$2"), List(-1 /*A*/, GOTO /*A*/))
}
@Test
@@ -87,9 +80,7 @@ class OptimizedBytecodeTest extends ClearAfterClass {
|}
""".stripMargin
val List(c, t, tMod) = compileClasses(compiler)(code, allowMessage = _.msg.contains("not be exhaustive"))
- assertEquals(
- getSingleMethod(c, "t").instructions.summary,
- List(GETSTATIC, "$qmark$qmark$qmark", ATHROW))
+ assertSameSummary(getSingleMethod(c, "t"), List(GETSTATIC, "$qmark$qmark$qmark", ATHROW))
}
@Test
@@ -235,9 +226,7 @@ class OptimizedBytecodeTest extends ClearAfterClass {
|}
""".stripMargin
val List(c) = compileClasses(compiler)(code)
- assertEquals(
- getSingleMethod(c, "t").instructions.summary,
- List(
+ assertSameSummary(getSingleMethod(c, "t"), List(
ALOAD /*1*/, INSTANCEOF /*Some*/, IFNE /*A*/,
ALOAD /*0*/, "getInt", POP,
-1 /*A*/, BIPUSH, IRETURN))
@@ -256,13 +245,11 @@ class OptimizedBytecodeTest extends ClearAfterClass {
|}
""".stripMargin
val List(c) = compileClasses(compiler)(code)
- assertEquals(
- getSingleMethod(c, "t").instructions.summary,
- List(
- -1 /*A*/, ILOAD /*1*/, TABLESWITCH,
- -1, ALOAD, "pr", RETURN,
- -1, ALOAD, "pr", RETURN,
- -1, ILOAD, ICONST_2, ISUB, ISTORE, GOTO /*A*/))
+ assertSameSummary(getSingleMethod(c, "t"), List(
+ -1 /*A*/, ILOAD /*1*/, TABLESWITCH,
+ -1, ALOAD, "pr", RETURN,
+ -1, ALOAD, "pr", RETURN,
+ -1, ILOAD, ICONST_2, ISUB, ISTORE, GOTO /*A*/))
}
@Test
@@ -283,12 +270,10 @@ class OptimizedBytecodeTest extends ClearAfterClass {
val cls = compileClassesSeparately(List(c1, c2), extraArgs = OptimizedBytecodeTest.args)
val c = cls.find(_.name == "C").get
- assertEquals(
- getSingleMethod(c, "t").instructions.summary,
- List(
- GETSTATIC, IFNONNULL, ACONST_NULL, ATHROW, // module load and null checks not yet eliminated
- -1, ICONST_1, GETSTATIC, IFNONNULL, ACONST_NULL, ATHROW,
- -1, ICONST_2, IADD, IRETURN))
+ assertSameSummary(getSingleMethod(c, "t"), List(
+ GETSTATIC, IFNONNULL, ACONST_NULL, ATHROW, // module load and null checks not yet eliminated
+ -1, ICONST_1, GETSTATIC, IFNONNULL, ACONST_NULL, ATHROW,
+ -1, ICONST_2, IADD, IRETURN))
}
@Test
diff --git a/test/junit/scala/math/BigDecimalTest.scala b/test/junit/scala/math/BigDecimalTest.scala
index a9e2481f37..5de02cbe0c 100644
--- a/test/junit/scala/math/BigDecimalTest.scala
+++ b/test/junit/scala/math/BigDecimalTest.scala
@@ -260,4 +260,9 @@ class BigDecimalTest {
testPrecision()
testRounded()
}
+
+ @Test
+ def testIsComparable() {
+ assert(BigDecimal(0.1).isInstanceOf[java.lang.Comparable[_]])
+ }
}
diff --git a/test/junit/scala/math/BigIntTest.scala b/test/junit/scala/math/BigIntTest.scala
new file mode 100644
index 0000000000..5a5694a775
--- /dev/null
+++ b/test/junit/scala/math/BigIntTest.scala
@@ -0,0 +1,16 @@
+package scala.math
+
+import java.math.{BigInteger => BI, MathContext => MC}
+
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+
+@RunWith(classOf[JUnit4])
+class BigIntTest {
+
+ @Test
+ def testIsComparable() {
+ assert(BigInt(1).isInstanceOf[java.lang.Comparable[_]])
+ }
+}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala b/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala
index 0d353e930e..fe43ed2f6a 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/CodeGenTools.scala
@@ -164,8 +164,18 @@ object CodeGenTools {
convertMethod(m)
}
+ def assertSameCode(method: Method, expected: List[Instruction]): Unit = assertSameCode(method.instructions.dropNonOp, expected)
def assertSameCode(actual: List[Instruction], expected: List[Instruction]): Unit = {
- assertTrue(s"\nExpected: $expected\nActual : $actual", actual === expected)
+ assert(actual === expected, s"\nExpected: $expected\nActual : $actual")
+ }
+
+ def assertSameSummary(method: Method, expected: List[Any]): Unit = assertSameSummary(method.instructions, expected)
+ def assertSameSummary(actual: List[Instruction], expected: List[Any]): Unit = {
+ def expectedString = expected.map({
+ case s: String => s""""$s""""
+ case i: Int => opcodeToString(i, i)
+ }).mkString("List(", ", ", ")")
+ assert(actual.summary == expected, s"\nFound : ${actual.summaryText}\nExpected: $expectedString")
}
def assertNoInvoke(m: Method): Unit = assertNoInvoke(m.instructions)
@@ -181,6 +191,21 @@ object CodeGenTools {
}, l.stringLines)
}
+ def assertDoesNotInvoke(m: Method, method: String): Unit = assertDoesNotInvoke(m.instructions, method)
+ def assertDoesNotInvoke(l: List[Instruction], method: String): Unit = {
+ assert(!l.exists {
+ case i: Invoke => i.name == method
+ case _ => false
+ }, l.stringLines)
+ }
+
+ def assertInvokedMethods(m: Method, expected: List[String]): Unit = assertInvokedMethods(m.instructions, expected)
+ def assertInvokedMethods(l: List[Instruction], expected: List[String]): Unit = {
+ def quote(l: List[String]) = l.map(s => s""""$s"""").mkString("List(", ", ", ")")
+ val actual = l collect { case i: Invoke => i.owner + "." + i.name }
+ assert(actual == expected, s"\nFound : ${quote(actual)}\nExpected: ${quote(expected)}")
+ }
+
def getSingleMethod(classNode: ClassNode, name: String): Method =
convertMethod(classNode.methods.asScala.toList.find(_.name == name).get)
diff --git a/test/junit/scala/tools/nsc/backend/jvm/StringConcatTest.scala b/test/junit/scala/tools/nsc/backend/jvm/StringConcatTest.scala
index 80cde6c9a9..2a9b8f7198 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/StringConcatTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/StringConcatTest.scala
@@ -24,47 +24,110 @@ class StringConcatTest extends ClearAfterClass {
ClearAfterClass.stateToClear = StringConcatTest
val compiler = StringConcatTest.compiler
- val commonPreInstructions = List(Label(0), LineNumber(1, Label(0)), TypeOp(NEW, "java/lang/StringBuilder"), Op(DUP), Invoke(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false), Ldc(LDC, "abc"), Invoke(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false), VarOp(ALOAD, 0))
-
- val commonPostInstructions = List(Invoke(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false), Op(ARETURN), Label(12))
-
- def instructionsWithCommonParts(instructions: List[Instruction]) = commonPreInstructions ++ instructions ++ commonPostInstructions
-
- def instructionsForResultMethod(code: String): List[Instruction] = {
- val methods = compileMethods(compiler)(code)
- val resultMethod = methods.find(_.name == "result").get
- instructionsFromMethod(resultMethod)
- }
-
@Test
- def concatStringToStringBuilder: Unit = {
- val code = """ def string = "def"; def result = "abc" + string """
- val actualInstructions = instructionsForResultMethod(code)
- val expectedInstructions = instructionsWithCommonParts(List(Invoke(INVOKEVIRTUAL, "C", "string", "()Ljava/lang/String;", false), Invoke(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false)))
- assertSameCode(actualInstructions, expectedInstructions)
- }
+ def appendOverloadNoBoxing(): Unit = {
+ val code =
+ """class C {
+ | def t1(
+ | v: Unit,
+ | z: Boolean,
+ | c: Char,
+ | b: Byte,
+ | s: Short,
+ | i: Int,
+ | l: Long,
+ | f: Float,
+ | d: Double,
+ | str: String,
+ | sbuf: java.lang.StringBuffer,
+ | chsq: java.lang.CharSequence,
+ | chrs: Array[Char]) = str + this + v + z + c + b + s + i + f + l + d + sbuf + chsq + chrs
+ |
+ | // similar, but starting off with any2stringadd
+ | def t2(
+ | v: Unit,
+ | z: Boolean,
+ | c: Char,
+ | b: Byte,
+ | s: Short,
+ | i: Int,
+ | l: Long,
+ | f: Float,
+ | d: Double,
+ | str: String,
+ | sbuf: java.lang.StringBuffer,
+ | chsq: java.lang.CharSequence,
+ | chrs: Array[Char]) = this + str + v + z + c + b + s + i + f + l + d + sbuf + chsq + chrs
+ |}
+ """.stripMargin
+ val List(c) = compileClasses(compiler)(code)
- @Test
- def concatStringBufferToStringBuilder: Unit = {
- val code = """ def stringBuffer = new java.lang.StringBuffer("def"); def result = "abc" + stringBuffer """
- val actualInstructions = instructionsForResultMethod(code)
- val expectedInstructions = instructionsWithCommonParts(List(Invoke(INVOKEVIRTUAL, "C", "stringBuffer", "()Ljava/lang/StringBuffer;", false), Invoke(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/StringBuffer;)Ljava/lang/StringBuilder;", false)))
- assertSameCode(actualInstructions, expectedInstructions)
- }
+ def invokeNameDesc(m: String): List[String] = getSingleMethod(c, m).instructions collect {
+ case Invoke(_, _, name, desc, _) => name + desc
+ }
+ assertEquals(invokeNameDesc("t1"), List(
+ "<init>()V",
+ "append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ "append(Ljava/lang/Object;)Ljava/lang/StringBuilder;",
+ "append(Ljava/lang/Object;)Ljava/lang/StringBuilder;",
+ "append(Z)Ljava/lang/StringBuilder;",
+ "append(C)Ljava/lang/StringBuilder;",
+ "append(I)Ljava/lang/StringBuilder;",
+ "append(I)Ljava/lang/StringBuilder;",
+ "append(I)Ljava/lang/StringBuilder;",
+ "append(F)Ljava/lang/StringBuilder;",
+ "append(J)Ljava/lang/StringBuilder;",
+ "append(D)Ljava/lang/StringBuilder;",
+ "append(Ljava/lang/StringBuffer;)Ljava/lang/StringBuilder;",
+ "append(Ljava/lang/CharSequence;)Ljava/lang/StringBuilder;",
+ "append(Ljava/lang/Object;)Ljava/lang/StringBuilder;", // test that we're not using the [C overload
+ "toString()Ljava/lang/String;"))
- @Test
- def concatCharSequenceToStringBuilder: Unit = {
- val code = """ def charSequence: CharSequence = "def"; def result = "abc" + charSequence """
- val actualInstructions = instructionsForResultMethod(code)
- val expectedInstructions = instructionsWithCommonParts(List(Invoke(INVOKEVIRTUAL, "C", "charSequence", "()Ljava/lang/CharSequence;", false), Invoke(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/CharSequence;)Ljava/lang/StringBuilder;", false)))
- assertSameCode(actualInstructions, expectedInstructions)
+ assertEquals(invokeNameDesc("t2"), List(
+ "<init>()V",
+ "any2stringadd(Ljava/lang/Object;)Ljava/lang/Object;",
+ "$plus$extension(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/String;",
+ "append(Ljava/lang/String;)Ljava/lang/StringBuilder;",
+ "append(Ljava/lang/Object;)Ljava/lang/StringBuilder;",
+ "append(Z)Ljava/lang/StringBuilder;",
+ "append(C)Ljava/lang/StringBuilder;",
+ "append(I)Ljava/lang/StringBuilder;",
+ "append(I)Ljava/lang/StringBuilder;",
+ "append(I)Ljava/lang/StringBuilder;",
+ "append(F)Ljava/lang/StringBuilder;",
+ "append(J)Ljava/lang/StringBuilder;",
+ "append(D)Ljava/lang/StringBuilder;",
+ "append(Ljava/lang/StringBuffer;)Ljava/lang/StringBuilder;",
+ "append(Ljava/lang/CharSequence;)Ljava/lang/StringBuilder;",
+ "append(Ljava/lang/Object;)Ljava/lang/StringBuilder;", // test that we're not using the [C overload
+ "toString()Ljava/lang/String;"))
}
@Test
- def concatIntToStringBuilder: Unit = {
- val code = """ def int = 123; def result = "abc" + int """
- val actualInstructions = instructionsForResultMethod(code)
- val expectedInstructions = instructionsWithCommonParts(List(Invoke(INVOKEVIRTUAL, "C", "int", "()I", false), Invoke(INVOKESTATIC, "scala/runtime/BoxesRunTime", "boxToInteger", "(I)Ljava/lang/Integer;", false), Invoke(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/Object;)Ljava/lang/StringBuilder;", false)))
- assertSameCode(actualInstructions, expectedInstructions)
+ def concatPrimitiveCorrectness(): Unit = {
+ val obj: Object = new { override def toString = "TTT" }
+ def t(
+ v: Unit,
+ z: Boolean,
+ c: Char,
+ b: Byte,
+ s: Short,
+ i: Int,
+ l: Long,
+ f: Float,
+ d: Double,
+ str: String,
+ sbuf: java.lang.StringBuffer,
+ chsq: java.lang.CharSequence,
+ chrs: Array[Char]) = {
+ val s1 = str + obj + v + z + c + b + s + i + f + l + d + sbuf + chsq + chrs
+ val s2 = obj + str + v + z + c + b + s + i + f + l + d + sbuf + chsq + chrs
+ s1 + "//" + s2
+ }
+ def sbuf = { val r = new java.lang.StringBuffer(); r.append("sbuf"); r }
+ def chsq: java.lang.CharSequence = "chsq"
+ val s = t((), true, 'd', 3: Byte, 12: Short, 3, -32l, 12.3f, -4.2d, "me", sbuf, chsq, Array('a', 'b'))
+ val r = s.replaceAll("""\[C@\w+""", "<ARRAY>")
+ assertEquals(r, "meTTT()trued312312.3-32-4.2sbufchsq<ARRAY>//TTTme()trued312312.3-32-4.2sbufchsq<ARRAY>")
}
}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/ClosureOptimizerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/ClosureOptimizerTest.scala
index b314643fb1..a0b9d6b4ed 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/ClosureOptimizerTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/ClosureOptimizerTest.scala
@@ -82,7 +82,7 @@ class ClosureOptimizerTest extends ClearAfterClass {
|}
""".stripMargin
val List(c) = compileClasses(compiler)(code)
- assertSameCode(getSingleMethod(c, "t").instructions.dropNonOp,
+ assertSameCode(getSingleMethod(c, "t"),
List(VarOp(ALOAD, 1), Invoke(INVOKEVIRTUAL, "scala/collection/immutable/List", "head", "()Ljava/lang/Object;", false),
TypeOp(CHECKCAST, "java/lang/String"), Invoke(INVOKESTATIC, "C", "C$$$anonfun$1", "(Ljava/lang/String;)Ljava/lang/String;", false),
Op(ARETURN)))
@@ -103,7 +103,6 @@ class ClosureOptimizerTest extends ClearAfterClass {
|}
""".stripMargin
val List(c) = compileClasses(compiler)(code)
- assertEquals(getSingleMethod(c, "t").instructions.summary,
- List(NEW, DUP, LDC, "<init>", ATHROW))
+ assertSameSummary(getSingleMethod(c, "t"), List(NEW, DUP, LDC, "<init>", ATHROW))
}
}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
index 942b62b32c..2c8f5e794e 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/InlinerTest.scala
@@ -100,7 +100,7 @@ class InlinerTest extends ClearAfterClass {
val g = inlineTest(code)
val gConv = convertMethod(g)
- assertSameCode(gConv.instructions.dropNonOp,
+ assertSameCode(gConv,
List(
VarOp(ALOAD, 0), VarOp(ASTORE, 1), // store this
Op(ICONST_1), VarOp(ISTORE, 2), Jump(GOTO, Label(10)), // store return value
@@ -144,10 +144,10 @@ class InlinerTest extends ClearAfterClass {
VarOp(ALOAD, 2),
Op(ATHROW))
- assertSameCode(convertMethod(g).instructions.dropNonOp, gBeforeLocalOpt)
+ assertSameCode(convertMethod(g), gBeforeLocalOpt)
compiler.genBCode.bTypes.localOpt.methodOptimizations(g, "C")
- assertSameCode(convertMethod(g).instructions.dropNonOp, invokeQQQ :+ Op(ATHROW))
+ assertSameCode(convertMethod(g), invokeQQQ :+ Op(ATHROW))
}
@Test
@@ -1313,60 +1313,40 @@ class InlinerTest extends ClearAfterClass {
""".stripMargin
val List(c, _, _) = compile(code)
- assertEquals(getSingleMethod(c, "t1").instructions.summary,
- List(BIPUSH, "C$$$anonfun$1", IRETURN))
-
- assertEquals(getSingleMethod(c, "t1a").instructions.summary,
- List(LCONST_1, "C$$$anonfun$2", IRETURN))
-
- assertEquals(getSingleMethod(c, "t2").instructions.summary, List(
- ICONST_1, ICONST_2, "C$$$anonfun$3",IRETURN))
+ assertSameSummary(getSingleMethod(c, "t1"), List(BIPUSH, "C$$$anonfun$1", IRETURN))
+ assertSameSummary(getSingleMethod(c, "t1a"), List(LCONST_1, "C$$$anonfun$2", IRETURN))
+ assertSameSummary(getSingleMethod(c, "t2"), List(ICONST_1, ICONST_2, "C$$$anonfun$3",IRETURN))
// val a = new ValKl(n); new ValKl(anonfun(a.x)).x
// value class instantiation-extraction should be optimized by boxing elim
- assertEquals(getSingleMethod(c, "t3").instructions.summary, List(
+ assertSameSummary(getSingleMethod(c, "t3"), List(
NEW, DUP, ICONST_1, "<init>", ASTORE,
NEW, DUP, ALOAD, "x",
"C$$$anonfun$4",
"<init>",
"x", IRETURN))
- assertEquals(getSingleMethod(c, "t4").instructions.summary, List(
- BIPUSH, "C$$$anonfun$5", "boxToInteger", ARETURN))
-
- assertEquals(getSingleMethod(c, "t4a").instructions.summary, List(
- ICONST_1, LDC, "C$$$anonfun$6", LRETURN))
-
- assertEquals(getSingleMethod(c, "t5").instructions.summary, List(
- BIPUSH, ICONST_3, "C$$$anonfun$7", "boxToInteger", ARETURN))
-
- assertEquals(getSingleMethod(c, "t5a").instructions.summary, List(
- BIPUSH, BIPUSH, I2B, "C$$$anonfun$8", IRETURN))
-
- assertEquals(getSingleMethod(c, "t6").instructions.summary, List(
- BIPUSH, "C$$$anonfun$9", RETURN))
-
- assertEquals(getSingleMethod(c, "t7").instructions.summary, List(
- ICONST_1, "C$$$anonfun$10", RETURN))
-
- assertEquals(getSingleMethod(c, "t8").instructions.summary, List(
- ICONST_1, LDC, "C$$$anonfun$11", LRETURN))
-
- assertEquals(getSingleMethod(c, "t9").instructions.summary, List(
- ICONST_1, "boxToInteger", "C$$$anonfun$12", RETURN))
+ assertSameSummary(getSingleMethod(c, "t4"), List(BIPUSH, "C$$$anonfun$5", "boxToInteger", ARETURN))
+ assertSameSummary(getSingleMethod(c, "t4a"), List(ICONST_1, LDC, "C$$$anonfun$6", LRETURN))
+ assertSameSummary(getSingleMethod(c, "t5"), List(BIPUSH, ICONST_3, "C$$$anonfun$7", "boxToInteger", ARETURN))
+ assertSameSummary(getSingleMethod(c, "t5a"), List(BIPUSH, BIPUSH, I2B, "C$$$anonfun$8", IRETURN))
+ assertSameSummary(getSingleMethod(c, "t6"), List(BIPUSH, "C$$$anonfun$9", RETURN))
+ assertSameSummary(getSingleMethod(c, "t7"), List(ICONST_1, "C$$$anonfun$10", RETURN))
+ assertSameSummary(getSingleMethod(c, "t8"), List(ICONST_1, LDC, "C$$$anonfun$11", LRETURN))
+ assertSameSummary(getSingleMethod(c, "t9"), List(ICONST_1, "boxToInteger", "C$$$anonfun$12", RETURN))
// t9a inlines Range.foreach, which is quite a bit of code, so just testing the core
assertInvoke(getSingleMethod(c, "t9a"), "C", "C$$$anonfun$13")
- assert(getSingleMethod(c, "t9a").instructions.summary.contains("boxToInteger"))
+ assertInvoke(getSingleMethod(c, "t9a"), "scala/runtime/BoxesRunTime", "boxToInteger")
- assertEquals(getSingleMethod(c, "t10").instructions.summary, List(
+ assertSameSummary(getSingleMethod(c, "t10"), List(
ICONST_1, ISTORE,
ALOAD, ILOAD,
"C$$$anonfun$14", RETURN))
// t10a inlines Range.foreach
assertInvoke(getSingleMethod(c, "t10a"), "C", "C$$$anonfun$15")
- assert(!getSingleMethod(c, "t10a").instructions.summary.contains("boxToInteger"))
+ assertDoesNotInvoke(getSingleMethod(c, "t10a"), "boxToInteger")
}
@Test
@@ -1389,7 +1369,7 @@ class InlinerTest extends ClearAfterClass {
|}
""".stripMargin
val List(c) = compile(code)
- assertSameCode(getSingleMethod(c, "t1").instructions.dropNonOp, List(Op(ICONST_0), Op(ICONST_1), Op(IADD), Op(IRETURN)))
+ assertSameCode(getSingleMethod(c, "t1"), List(Op(ICONST_0), Op(ICONST_1), Op(IADD), Op(IRETURN)))
assertEquals(getSingleMethod(c, "t2").instructions collect { case i: Invoke => i.owner +"."+ i.name }, List(
"scala/runtime/IntRef.create", "C.C$$$anonfun$1"))
}
@@ -1430,9 +1410,9 @@ class InlinerTest extends ClearAfterClass {
|}
""".stripMargin
val List(c) = compile(code)
- assertSameCode(getSingleMethod(c, "t1").instructions.dropNonOp, List(Op(ICONST_3), Op(ICONST_4), Op(IADD), Op(IRETURN)))
- assertSameCode(getSingleMethod(c, "t2").instructions.dropNonOp, List(Op(ICONST_1), Op(ICONST_2), Op(IADD), Op(IRETURN)))
- assertSameCode(getSingleMethod(c, "t3").instructions.dropNonOp, List(Op(ICONST_1), Op(ICONST_3), Op(ISUB), Op(IRETURN)))
+ assertSameCode(getSingleMethod(c, "t1"), List(Op(ICONST_3), Op(ICONST_4), Op(IADD), Op(IRETURN)))
+ assertSameCode(getSingleMethod(c, "t2"), List(Op(ICONST_1), Op(ICONST_2), Op(IADD), Op(IRETURN)))
+ assertSameCode(getSingleMethod(c, "t3"), List(Op(ICONST_1), Op(ICONST_3), Op(ISUB), Op(IRETURN)))
assertNoInvoke(getSingleMethod(c, "t4"))
assertNoInvoke(getSingleMethod(c, "t5"))
}
@@ -1461,9 +1441,9 @@ class InlinerTest extends ClearAfterClass {
""".stripMargin
val List(c, _) = compile(code)
def casts(m: String) = getSingleMethod(c, m).instructions collect { case TypeOp(CHECKCAST, tp) => tp }
- assertSameCode(getSingleMethod(c, "t1").instructions.dropNonOp, List(VarOp(ALOAD, 1), Op(ARETURN)))
- assertSameCode(getSingleMethod(c, "t2").instructions.dropNonOp, List(VarOp(ALOAD, 1), Op(ARETURN)))
- assertSameCode(getSingleMethod(c, "t3").instructions.dropNonOp, List(VarOp(ALOAD, 1), TypeOp(CHECKCAST, "C"), Op(ARETURN)))
+ assertSameCode(getSingleMethod(c, "t1"), List(VarOp(ALOAD, 1), Op(ARETURN)))
+ assertSameCode(getSingleMethod(c, "t2"), List(VarOp(ALOAD, 1), Op(ARETURN)))
+ assertSameCode(getSingleMethod(c, "t3"), List(VarOp(ALOAD, 1), TypeOp(CHECKCAST, "C"), Op(ARETURN)))
assertEquals(casts("t4"), List("C"))
assertEquals(casts("t5"), Nil)
assertEquals(casts("t6"), Nil)
@@ -1489,12 +1469,11 @@ class InlinerTest extends ClearAfterClass {
val cls = compile(code)
val test = cls.find(_.name == "Test$").get
- assertEquals(
- getSingleMethod(test, "f").instructions.summary,
- List(GETSTATIC, "mkFoo",
- BIPUSH, ISTORE,
- IFNONNULL, ACONST_NULL, ATHROW, -1 /*label*/,
- ILOAD, ICONST_1, IADD, IRETURN))
+ assertSameSummary(getSingleMethod(test, "f"), List(
+ GETSTATIC, "mkFoo",
+ BIPUSH, ISTORE,
+ IFNONNULL, ACONST_NULL, ATHROW, -1 /*label*/,
+ ILOAD, ICONST_1, IADD, IRETURN))
}
@Test // a test taken from the test suite for the 2.11 inliner
@@ -1507,13 +1486,20 @@ class InlinerTest extends ClearAfterClass {
|}
""".stripMargin
val List(c) = compile(code)
- val t = getSingleMethod(c, "t")
// box-unbox will clean it up
- assertEquals(getSingleMethod(c, "t").instructions.summary,
- List(
- ALOAD, "C$$$anonfun$1", IFEQ /*A*/,
- "C$$$anonfun$2", IRETURN,
- -1 /*A*/, "C$$$anonfun$3", IRETURN))
+ assertSameSummary(getSingleMethod(c, "t"), List(
+ ALOAD, "C$$$anonfun$1", IFEQ /*A*/,
+ "C$$$anonfun$2", IRETURN,
+ -1 /*A*/, "C$$$anonfun$3", IRETURN))
+ }
+
+ @Test
+ def inlineProject(): Unit = {
+ val codeA = "final class A { @inline def f = 1 }"
+ val codeB = "class B { def t(a: A) = a.f }"
+ // tests that no warning is emitted
+ val List(a, b) = compileClassesSeparately(List(codeA, codeB), extraArgs = "-Yopt:l:project -Yopt-warnings")
+ assertInvoke(getSingleMethod(b, "t"), "A", "f")
}
}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOptsTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOptsTest.scala
index 423ff85f04..0a9a26cda7 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOptsTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/MethodLevelOptsTest.scala
@@ -46,8 +46,7 @@ class MethodLevelOptsTest extends ClearAfterClass {
val code = "def f = { try {} catch { case _: Throwable => 0 }; 1 }"
val m = singleMethod(methodOptCompiler)(code)
assertTrue(m.handlers.length == 0)
- assertSameCode(m.instructions.dropNonOp,
- List(Op(ICONST_1), Op(IRETURN)))
+ assertSameCode(m, List(Op(ICONST_1), Op(IRETURN)))
}
@Test
@@ -89,7 +88,7 @@ class MethodLevelOptsTest extends ClearAfterClass {
""".stripMargin
val m = singleMethod(methodOptCompiler)(code)
assertTrue(m.handlers.isEmpty)
- assertSameCode(m.instructions.dropNonOp, List(Op(ICONST_3), Op(IRETURN)))
+ assertSameCode(m, List(Op(ICONST_3), Op(IRETURN)))
}
@Test
@@ -108,8 +107,8 @@ class MethodLevelOptsTest extends ClearAfterClass {
|}
""".stripMargin
val List(c) = compileClasses(methodOptCompiler)(code)
- assertSameCode(getSingleMethod(c, "t").instructions.dropNonOp,
- List(Op(ACONST_NULL), Invoke(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;", false), Op(ARETURN)))
+ assertSameCode(getSingleMethod(c, "t"), List(
+ Op(ACONST_NULL), Invoke(INVOKEVIRTUAL, "java/lang/Object", "toString", "()Ljava/lang/String;", false), Op(ARETURN)))
}
@Test
@@ -126,8 +125,7 @@ class MethodLevelOptsTest extends ClearAfterClass {
""".stripMargin
val List(c) = compileClasses(methodOptCompiler)(code)
assertSameCode(
- getSingleMethod(c, "t").instructions.dropNonOp,
- List(Ldc(LDC, "c"), Op(ARETURN)))
+ getSingleMethod(c, "t"), List(Ldc(LDC, "c"), Op(ARETURN)))
}
@Test
@@ -147,13 +145,11 @@ class MethodLevelOptsTest extends ClearAfterClass {
""".stripMargin
val List(c) = compileClasses(methodOptCompiler)(code)
- assertEquals(
- getSingleMethod(c, "t").instructions.dropNonOp,
- List(
- Ldc(LDC, "el"), VarOp(ASTORE, 1),
- Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), VarOp(ALOAD, 1), Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false),
- Op(ACONST_NULL), VarOp(ASTORE, 1),
- Ldc(LDC, "zit"), VarOp(ASTORE, 1), VarOp(ALOAD, 1), Op(ARETURN)))
+ assertSameCode(getSingleMethod(c, "t"), List(
+ Ldc(LDC, "el"), VarOp(ASTORE, 1),
+ Field(GETSTATIC, "scala/Predef$", "MODULE$", "Lscala/Predef$;"), VarOp(ALOAD, 1), Invoke(INVOKEVIRTUAL, "scala/Predef$", "println", "(Ljava/lang/Object;)V", false),
+ Op(ACONST_NULL), VarOp(ASTORE, 1),
+ Ldc(LDC, "zit"), VarOp(ASTORE, 1), VarOp(ALOAD, 1), Op(ARETURN)))
}
@Test
@@ -172,8 +168,8 @@ class MethodLevelOptsTest extends ClearAfterClass {
|}
""".stripMargin
val List(c) = compileClasses(methodOptCompiler)(code)
- assertEquals(getSingleMethod(c, "t").instructions.dropNonOp,
- List(IntOp(BIPUSH, 23), IntOp(NEWARRAY, 5), Op(POP), VarOp(ILOAD, 1), VarOp(ILOAD, 2), Op(IADD), Op(IRETURN)))
+ assertSameCode(getSingleMethod(c, "t"), List(
+ IntOp(BIPUSH, 23), IntOp(NEWARRAY, 5), Op(POP), VarOp(ILOAD, 1), VarOp(ILOAD, 2), Op(IADD), Op(IRETURN)))
}
@Test
@@ -187,9 +183,9 @@ class MethodLevelOptsTest extends ClearAfterClass {
|}
""".stripMargin
val List(c) = compileClasses(methodOptCompiler)(code)
- assertEquals(getSingleMethod(c, "t").instructions.dropNonOp,
- List(TypeOp(NEW, "java/lang/Integer"), Ldc(LDC, "nono"), Invoke(INVOKESPECIAL, "java/lang/Integer", "<init>", "(Ljava/lang/String;)V", false),
- VarOp(ILOAD, 1), VarOp(ILOAD, 2), Op(IADD), Op(IRETURN)))
+ assertSameCode(getSingleMethod(c, "t"), List(
+ TypeOp(NEW, "java/lang/Integer"), Ldc(LDC, "nono"), Invoke(INVOKESPECIAL, "java/lang/Integer", "<init>", "(Ljava/lang/String;)V", false),
+ VarOp(ILOAD, 1), VarOp(ILOAD, 2), Op(IADD), Op(IRETURN)))
}
@Test
@@ -213,8 +209,7 @@ class MethodLevelOptsTest extends ClearAfterClass {
|}
""".stripMargin
val List(c) = compileClasses(methodOptCompiler)(code)
- assertEquals(getSingleMethod(c, "t").instructions.dropNonOp,
- List(Op(ICONST_0), Op(IRETURN)))
+ assertSameCode(getSingleMethod(c, "t"), List(Op(ICONST_0), Op(IRETURN)))
}
@Test
@@ -230,14 +225,12 @@ class MethodLevelOptsTest extends ClearAfterClass {
|}
""".stripMargin
val List(c) = compileClasses(methodOptCompiler)(code)
- assertEquals(
- getSingleMethod(c, "t").instructions.dropNonOp,
- List(
- IntOp(BIPUSH, 30), VarOp(ISTORE, 3), // no constant propagation, so we keep the store (and load below) of a const
- VarOp(ILOAD, 1),
- VarOp(ILOAD, 2),
- VarOp(ILOAD, 3),
- Invoke(INVOKESTATIC, "C", "C$$$anonfun$1", "(III)I", false), Op(IRETURN)))
+ assertSameCode(getSingleMethod(c, "t"), List(
+ IntOp(BIPUSH, 30), VarOp(ISTORE, 3), // no constant propagation, so we keep the store (and load below) of a const
+ VarOp(ILOAD, 1),
+ VarOp(ILOAD, 2),
+ VarOp(ILOAD, 3),
+ Invoke(INVOKESTATIC, "C", "C$$$anonfun$1", "(III)I", false), Op(IRETURN)))
}
@Test
@@ -342,14 +335,14 @@ class MethodLevelOptsTest extends ClearAfterClass {
assertNoInvoke(getSingleMethod(c, "t5"))
assertNoInvoke(getSingleMethod(c, "t6"))
assertNoInvoke(getSingleMethod(c, "t7"))
- assertEquals(getSingleMethod(c, "t8").instructions.summary, List(ICONST_0, IRETURN))
+ assertSameSummary(getSingleMethod(c, "t8"), List(ICONST_0, IRETURN))
assertNoInvoke(getSingleMethod(c, "t9"))
// t10: no invocation of unbox
assertEquals(getSingleMethod(c, "t10").instructions collect { case Invoke(_, owner, name, _, _) => (owner, name) }, List(
("java/lang/Integer", "valueOf"),
("C", "escape")))
- assertEquals(getSingleMethod(c, "t11").instructions.summary, List(
+ assertSameSummary(getSingleMethod(c, "t11"), List(
BIPUSH, "valueOf", ASTORE /*2*/,
BIPUSH, "valueOf", ASTORE /*3*/,
ALOAD /*0*/, ALOAD /*2*/, "escape",
@@ -410,9 +403,9 @@ class MethodLevelOptsTest extends ClearAfterClass {
|}
""".stripMargin
val List(c) = compileClasses(methodOptCompiler)(code)
- assertEquals(getSingleMethod(c, "t1").instructions.summary, List(ICONST_0, IRETURN))
+ assertSameSummary(getSingleMethod(c, "t1"), List(ICONST_0, IRETURN))
assertNoInvoke(getSingleMethod(c, "t2"))
- assertEquals(getSingleMethod(c, "t3").instructions.summary, List(LDC, LDC, LADD, LRETURN))
+ assertSameSummary(getSingleMethod(c, "t3"), List(LDC, LDC, LADD, LRETURN))
assertNoInvoke(getSingleMethod(c, "t4"))
assertEquals(getSingleMethod(c, "t5").instructions collect { case Field(_, owner, name, _) => s"$owner.$name" },
List("scala/runtime/IntRef.elem"))
@@ -475,16 +468,16 @@ class MethodLevelOptsTest extends ClearAfterClass {
""".stripMargin
val List(c) = compileClasses(methodOptCompiler)(code)
assertNoInvoke(getSingleMethod(c, "t1"))
- assertEquals(getSingleMethod(c, "t2").instructions.summary, List(ICONST_1, ICONST_3, IADD, IRETURN))
- assertEquals(getSingleMethod(c, "t3").instructions.summary, List(ICONST_3, ICONST_4, IADD, IRETURN))
- assertEquals(getSingleMethod(c, "t4").instructions.summary, List(ICONST_3, "boxToInteger", ARETURN))
+ assertSameSummary(getSingleMethod(c, "t2"), List(ICONST_1, ICONST_3, IADD, IRETURN))
+ assertSameSummary(getSingleMethod(c, "t3"), List(ICONST_3, ICONST_4, IADD, IRETURN))
+ assertSameSummary(getSingleMethod(c, "t4"), List(ICONST_3, "boxToInteger", ARETURN))
assertEquals(getSingleMethod(c, "t5").instructions collect { case Invoke(_, owner, name, _, _) => (owner, name) }, List(
("scala/runtime/BoxesRunTime", "boxToInteger"),
("scala/runtime/BoxesRunTime", "boxToInteger"),
("C", "tpl"),
("scala/Tuple2", "_1$mcI$sp")))
- assertEquals(getSingleMethod(c, "t6").instructions.summary, List(ICONST_1, ICONST_2, ISUB, IRETURN))
- assertEquals(getSingleMethod(c, "t7").instructions.summary, List(
+ assertSameSummary(getSingleMethod(c, "t6"), List(ICONST_1, ICONST_2, ISUB, IRETURN))
+ assertSameSummary(getSingleMethod(c, "t7"), List(
ICONST_1, ICONST_2, ISTORE, ISTORE,
ICONST_3, ISTORE,
ILOAD, ILOAD, IADD, ILOAD, IADD, IRETURN))
@@ -539,13 +532,13 @@ class MethodLevelOptsTest extends ClearAfterClass {
|}
""".stripMargin
val List(c) = compileClasses(methodOptCompiler)(code)
- assertEquals(getSingleMethod(c, "t1").instructions.summary, List(NEW, DUP, "<init>", ARETURN))
- assertSameCode(getSingleMethod(c, "t2").instructions.dropNonOp, List(Op(LCONST_0), Op(LRETURN)))
- assertSameCode(getSingleMethod(c, "t3").instructions.dropNonOp, List(Op(ICONST_1), Op(IRETURN)))
- assertSameCode(getSingleMethod(c, "t4").instructions.dropNonOp, List(Op(ICONST_1), Op(IRETURN)))
- assertSameCode(getSingleMethod(c, "t5").instructions.dropNonOp, List(Op(DCONST_0), Op(DRETURN)))
- assertSameCode(getSingleMethod(c, "t6").instructions.dropNonOp, List(Op(ACONST_NULL), Op(ARETURN)))
- assertSameCode(getSingleMethod(c, "t7").instructions.dropNonOp, List(Op(ICONST_0), Op(IRETURN)))
+ assertSameSummary(getSingleMethod(c, "t1"), List(NEW, DUP, "<init>", ARETURN))
+ assertSameCode(getSingleMethod(c, "t2"), List(Op(LCONST_0), Op(LRETURN)))
+ assertSameCode(getSingleMethod(c, "t3"), List(Op(ICONST_1), Op(IRETURN)))
+ assertSameCode(getSingleMethod(c, "t4"), List(Op(ICONST_1), Op(IRETURN)))
+ assertSameCode(getSingleMethod(c, "t5"), List(Op(DCONST_0), Op(DRETURN)))
+ assertSameCode(getSingleMethod(c, "t6"), List(Op(ACONST_NULL), Op(ARETURN)))
+ assertSameCode(getSingleMethod(c, "t7"), List(Op(ICONST_0), Op(IRETURN)))
}
@Test
@@ -560,8 +553,8 @@ class MethodLevelOptsTest extends ClearAfterClass {
""".stripMargin
val List(c) = compileClasses(methodOptCompiler)(code)
assertSameCode(
- getSingleMethod(c, "t").instructions.dropNonOp,
- List(VarOp(ALOAD, 1), Jump(IFNULL, Label(6)), Op(ICONST_1), Op(IRETURN), Label(6), Op(ICONST_0), Op(IRETURN)))
+ getSingleMethod(c, "t"), List(
+ VarOp(ALOAD, 1), Jump(IFNULL, Label(6)), Op(ICONST_1), Op(IRETURN), Label(6), Op(ICONST_0), Op(IRETURN)))
}
@Test
@@ -703,15 +696,8 @@ class MethodLevelOptsTest extends ClearAfterClass {
assertEquals(locals(c, "t2"), List(("this", 0), ("x", 1)))
// we don't have constant propagation (yet).
// the local var can't be optimized as a store;laod sequence, there's a GETSTATIC between the two
- assertEquals(
- textify(findAsmMethod(c, "t2")),
- getSingleMethod(c, "t2").instructions.dropNonOp.map(_.opcode),
- List(
- ICONST_2, ISTORE,
- GETSTATIC, // Predef.MODULE$
- ILOAD, INVOKESTATIC, // boxToInteger
- INVOKEVIRTUAL, // println
- RETURN))
+ assertSameSummary(getSingleMethod(c, "t2"), List(
+ ICONST_2, ISTORE, GETSTATIC, ILOAD, "boxToInteger", "println", RETURN))
assertEquals(locals(c, "t3"), List(("this", 0)))
assertEquals(locals(c, "t4"), List(("this", 0), ("x", 1)))
@@ -736,9 +722,41 @@ class MethodLevelOptsTest extends ClearAfterClass {
val t = getSingleMethod(c, "t")
assertEquals(t.handlers, Nil)
assertEquals(locals(c, "t"), List(("this", 0)))
- assertEquals(t.instructions.summary,
- List(
- GETSTATIC, LDC, "print",
- -1, GOTO))
+ assertSameSummary(t, List(GETSTATIC, LDC, "print", -1, GOTO))
+ }
+
+ @Test
+ def booleanOrderingCompare(): Unit = {
+ val code =
+ """class C {
+ | def compare(x: Boolean, y: Boolean) = (x, y) match {
+ | case (false, true) => -1
+ | case (true, false) => 1
+ | case _ => 0
+ | }
+ |}
+ """.stripMargin
+ val List(c) = compileClasses(methodOptCompiler)(code)
+ assertNoInvoke(getSingleMethod(c, "compare"))
+ }
+
+ @Test
+ def t8790(): Unit = {
+ val code =
+ """class C {
+ | def t(x: Int, y: Int): String = (x, y) match {
+ | case (7, 8) => "a"
+ | case _ => "b"
+ | }
+ |}
+ """.stripMargin
+ val List(c) = compileClasses(methodOptCompiler)(code)
+
+ assertSameSummary(getSingleMethod(c, "t"), List(
+ BIPUSH, ILOAD, IF_ICMPNE,
+ BIPUSH, ILOAD, IF_ICMPNE,
+ LDC, ASTORE, GOTO,
+ -1, LDC, ASTORE,
+ -1, ALOAD, ARETURN))
}
}
diff --git a/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala b/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala
index 86c8baa3c6..9634517e28 100644
--- a/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala
+++ b/test/junit/scala/tools/nsc/backend/jvm/opt/UnreachableCodeTest.scala
@@ -215,4 +215,50 @@ class UnreachableCodeTest extends ClearAfterClass {
assertTrue(List(FrameEntry(F_FULL, List(INTEGER, DOUBLE, Label(3)), List("java/lang/Object", Label(4))), Label(3), Label(4)) ===
List(FrameEntry(F_FULL, List(INTEGER, DOUBLE, Label(1)), List("java/lang/Object", Label(3))), Label(1), Label(3)))
}
+
+ @Test
+ def loadNullNothingBytecode(): Unit = {
+ val code =
+ """class C {
+ | def nl: Null = null
+ | def nt: Nothing = throw new Error("")
+ | def cons(a: Any) = ()
+ |
+ | def t1 = cons(null)
+ | def t2 = cons(nl)
+ | def t3 = cons(throw new Error(""))
+ | def t4 = cons(nt)
+ |}
+ """.stripMargin
+ val List(c) = compileClasses(noOptCompiler)(code)
+
+ assertSameSummary(getSingleMethod(c, "nl"), List(ACONST_NULL, ARETURN))
+
+ assertSameSummary(getSingleMethod(c, "nt"), List(
+ NEW, DUP, LDC, "<init>", ATHROW))
+
+ assertSameSummary(getSingleMethod(c, "t1"), List(
+ ALOAD, ACONST_NULL, "cons", RETURN))
+
+ // GenBCode introduces POP; ACONST_NULL after loading an expression of type scala.runtime.Null$,
+ // see comment in BCodeBodyBuilder.adapt
+ assertSameSummary(getSingleMethod(c, "t2"), List(
+ ALOAD, ALOAD, "nl", POP, ACONST_NULL, "cons", RETURN))
+
+ // the bytecode generated by GenBCode is ... ATHROW; INVOKEVIRTUAL C.cons; RETURN
+ // the ASM classfile writer creates a new basic block (creates a label) right after the ATHROW
+ // and replaces all instructions by NOP*; ATHROW, see comment in BCodeBodyBuilder.adapt
+ // NOTE: DCE is enabled by default and gets rid of the redundant code (tested below)
+ assertSameSummary(getSingleMethod(c, "t3"), List(
+ ALOAD, NEW, DUP, LDC, "<init>", ATHROW, NOP, NOP, NOP, ATHROW))
+
+ // GenBCode introduces an ATHROW after the invocation of C.nt, see BCodeBodyBuilder.adapt
+ // NOTE: DCE is enabled by default and gets rid of the redundant code (tested below)
+ assertSameSummary(getSingleMethod(c, "t4"), List(
+ ALOAD, ALOAD, "nt", ATHROW, NOP, NOP, NOP, ATHROW))
+
+ val List(cDCE) = compileClasses(dceCompiler)(code)
+ assertSameSummary(getSingleMethod(cDCE, "t3"), List(ALOAD, NEW, DUP, LDC, "<init>", ATHROW))
+ assertSameSummary(getSingleMethod(cDCE, "t4"), List(ALOAD, ALOAD, "nt", ATHROW))
+ }
}
diff --git a/test/junit/scala/tools/nsc/transform/patmat/PatmatBytecodeTest.scala b/test/junit/scala/tools/nsc/transform/patmat/PatmatBytecodeTest.scala
index 3fc3144eb2..ac558e2e21 100644
--- a/test/junit/scala/tools/nsc/transform/patmat/PatmatBytecodeTest.scala
+++ b/test/junit/scala/tools/nsc/transform/patmat/PatmatBytecodeTest.scala
@@ -18,7 +18,7 @@ import scala.tools.testing.ClearAfterClass
object PatmatBytecodeTest extends ClearAfterClass.Clearable {
var compiler = newCompiler()
- var optCompiler = newCompiler(extraArgs = "-Yopt:l:method")
+ var optCompiler = newCompiler(extraArgs = "-Yopt:l:project")
def clear(): Unit = { compiler = null; optCompiler = null }
}
@@ -96,10 +96,9 @@ class PatmatBytecodeTest extends ClearAfterClass {
""".stripMargin
val c = compileClasses(optCompiler)(code).head
- assertEquals(textify(findAsmMethod(c, "a")), getSingleMethod(c, "a").instructions.summary,
- List(
- NEW, DUP, ICONST_1, LDC, "<init>",
- "y", ARETURN))
+ assertSameSummary(getSingleMethod(c, "a"), List(
+ NEW, DUP, ICONST_1, LDC, "<init>",
+ "y", ARETURN))
}
@Test
@@ -127,12 +126,12 @@ class PatmatBytecodeTest extends ClearAfterClass {
|}
""".stripMargin
val c = compileClasses(optCompiler)(code).head
- assertEquals(textify(findAsmMethod(c, "a")), getSingleMethod(c, "a").instructions.summary,
- List(NEW, DUP, ICONST_1, "boxToInteger", LDC, "<init>", ASTORE /*1*/,
- ALOAD /*1*/, "y", ASTORE /*2*/,
- ALOAD /*1*/, "x", INSTANCEOF, IFNE /*R*/,
- NEW, DUP, ALOAD /*1*/, "<init>", ATHROW,
- /*R*/ -1, ALOAD /*2*/, ARETURN))
+ assertSameSummary(getSingleMethod(c, "a"), List(
+ NEW, DUP, ICONST_1, "boxToInteger", LDC, "<init>", ASTORE /*1*/,
+ ALOAD /*1*/, "y", ASTORE /*2*/,
+ ALOAD /*1*/, "x", INSTANCEOF, IFNE /*R*/,
+ NEW, DUP, ALOAD /*1*/, "<init>", ATHROW,
+ /*R*/ -1, ALOAD /*2*/, ARETURN))
}
@Test
@@ -156,7 +155,41 @@ class PatmatBytecodeTest extends ClearAfterClass {
-1 /*A*/ , NEW /*MatchError*/ , DUP, ALOAD /*1*/ , "<init>", ATHROW,
-1 /*B*/ , ILOAD, IRETURN)
- assertEquals(textify(findAsmMethod(c, "a")), getSingleMethod(c, "a").instructions.summary, expected)
- assertEquals(textify(findAsmMethod(c, "b")), getSingleMethod(c, "b").instructions.summary, expected)
+ assertSameSummary(getSingleMethod(c, "a"), expected)
+ assertSameSummary(getSingleMethod(c, "b"), expected)
+ }
+
+ @Test
+ def valPatterns(): Unit = {
+ val code =
+ """case class C(a: Any, b: Int) {
+ | def tplCall = ("hi", 3)
+ | @inline final def tplInline = (true, 'z')
+ |
+ | def t1 = { val (a, b) = (1, 2); a + b }
+ | def t2 = { val (a, _) = (1, 3); a }
+ | def t3 = { val (s, i) = tplCall; s.length + i }
+ | def t4 = { val (_, i) = tplCall; i }
+ | def t5 = { val (b, c) = tplInline; b || c == 'e' }
+ | def t6 = { val (_, c) = tplInline; c }
+ |
+ | def t7 = { val C(s: String, b) = this; s.length + b }
+ | def t8 = { val C(_, b) = this; b }
+ | def t9 = { val C(a, _) = C("hi", 23); a.toString }
+ |}
+ """.stripMargin
+ val List(c, cMod) = compileClasses(optCompiler)(code)
+ assertSameSummary(getSingleMethod(c, "t1"), List(ICONST_1, ICONST_2, IADD, IRETURN))
+ assertSameSummary(getSingleMethod(c, "t2"), List(ICONST_1, IRETURN))
+ assertInvokedMethods(getSingleMethod(c, "t3"), List("C.tplCall", "scala/Tuple2._1", "scala/Tuple2._2$mcI$sp", "scala/MatchError.<init>", "java/lang/String.length"))
+ assertInvokedMethods(getSingleMethod(c, "t4"), List("C.tplCall", "scala/Tuple2._2$mcI$sp", "scala/MatchError.<init>"))
+ assertNoInvoke(getSingleMethod(c, "t5"))
+ assertSameSummary(getSingleMethod(c, "t6"), List(BIPUSH, IRETURN))
+
+ // MatchError reachable because of the type pattern `s: String`
+ assertInvokedMethods(getSingleMethod(c, "t7"), List("C.a", "C.b", "scala/MatchError.<init>", "java/lang/String.length"))
+ assertSameSummary(getSingleMethod(c, "t8"), List(ALOAD, "b", IRETURN))
+ // C allocation not eliminated - constructor may have side-effects.
+ assertSameSummary(getSingleMethod(c, "t9"), List(NEW, DUP, LDC, BIPUSH, "<init>", "a", "toString", ARETURN))
}
}
diff --git a/test/scaladoc/run/SI-6017.scala b/test/scaladoc/run/SI-6017.scala
deleted file mode 100644
index 9951534c6d..0000000000
--- a/test/scaladoc/run/SI-6017.scala
+++ /dev/null
@@ -1,28 +0,0 @@
-import scala.tools.nsc.doc
-import scala.tools.nsc.doc.model._
-import scala.tools.nsc.doc.html.page.{Index, ReferenceIndex}
-import scala.tools.partest.ScaladocModelTest
-
-object Test extends ScaladocModelTest {
- override def scaladocSettings = ""
- override def code = """
- class STAR
- class Star
- """
-
- def testModel(rootPackage: Package) {
- model match {
- case Some(universe) => {
- val index = IndexModelFactory.makeIndex(universe)
- // Because "STAR" and "Star" are different
- assert(index.firstLetterIndex('s').keys.toSeq.length == 2)
-
- val indexPage = new Index(universe, index)
- val letters = indexPage.letters
- assert(letters.length > 1)
- assert(letters(0).toString == "<span>#</span>")
- }
- case _ => assert(false)
- }
- }
-}
diff --git a/test/scaladoc/run/SI-9620.scala b/test/scaladoc/run/SI-9620.scala
index 96260aad9a..cac34d1c18 100644
--- a/test/scaladoc/run/SI-9620.scala
+++ b/test/scaladoc/run/SI-9620.scala
@@ -21,7 +21,6 @@ object Test extends ScaladocModelTest {
}
"""
- // no need for special settings
def scaladocSettings = "-implicits"
def testModel(rootPackage: Package) = {
diff --git a/test/scaladoc/run/SI-6017.check b/test/scaladoc/run/shortDescription-annotation.check
index 619c56180b..619c56180b 100644
--- a/test/scaladoc/run/SI-6017.check
+++ b/test/scaladoc/run/shortDescription-annotation.check
diff --git a/test/scaladoc/run/shortDescription-annotation.scala b/test/scaladoc/run/shortDescription-annotation.scala
new file mode 100644
index 0000000000..0e2950f4f9
--- /dev/null
+++ b/test/scaladoc/run/shortDescription-annotation.scala
@@ -0,0 +1,55 @@
+import scala.tools.nsc.doc.model._
+import scala.tools.partest.ScaladocModelTest
+
+object Test extends ScaladocModelTest {
+ override def code = """
+ package a
+
+ /** This comment should not appear
+ * @shortDescription This one should appear
+ */
+ class Foo {
+ /** This comment should appear */
+ def foo: Int = 1
+
+ /** This comment should not appear
+ * @shortDescription This comment should appear
+ */
+ def goo: Int = 2
+ }
+ """
+
+ // no need for special settings
+ def scaladocSettings = ""
+
+ def testModel(rootPackage: Package) = {
+ import scala.tools.nsc.doc.base.comment._
+ import access._
+
+ def inlineToStr(inl: Inline): String = inl match {
+ case Chain(items) => items flatMap (inlineToStr(_)) mkString ""
+ case Italic(in) => inlineToStr(in)
+ case Bold(in) => inlineToStr(in)
+ case Underline(in) => inlineToStr(in)
+ case Monospace(in) => inlineToStr(in)
+ case Text(text) => text
+ case Summary(in) => inlineToStr(in)
+ case EntityLink(Text(text), _) => text
+ case _ => inl.toString
+ }
+
+ val foo = rootPackage._package("a")._class("Foo")
+
+ // Assert that the class has the correct short description
+ val classDesc = inlineToStr(foo.comment.get.short)
+ assert(classDesc == "This one should appear", classDesc)
+
+ // Assert that the `foo` method has the correct short description
+ val fooDesc = inlineToStr(foo._method("foo").comment.get.short)
+ assert(fooDesc == "This comment should appear", fooDesc)
+
+ // Assert that the `goo` method has the correct short description
+ val gooDesc = inlineToStr(foo._method("goo").comment.get.short)
+ assert(gooDesc == "This comment should appear", gooDesc)
+ }
+}
diff --git a/test/scaladoc/scalacheck/DeprecatedIndexTest.scala b/test/scaladoc/scalacheck/DeprecatedIndexTest.scala
index 4a5a2001d4..2581b879da 100644
--- a/test/scaladoc/scalacheck/DeprecatedIndexTest.scala
+++ b/test/scaladoc/scalacheck/DeprecatedIndexTest.scala
@@ -1,6 +1,7 @@
import org.scalacheck._
import org.scalacheck.Prop._
+import scala.tools.nsc.ScalaDocReporter
import scala.tools.nsc.doc
import scala.tools.nsc.doc.html.page.DeprecatedIndex
import java.net.{URLClassLoader, URLDecoder}
@@ -32,7 +33,8 @@ object Test extends Properties("IndexScript") {
def createDeprecatedScript(path: String) =
docFactory.makeUniverse(Left(List(path))) match {
case Some(universe) => {
- val index = new DeprecatedIndex(universe, indexModelFactory.makeIndex(universe))
+ val reporter = new ScalaDocReporter(universe.settings)
+ val index = new DeprecatedIndex(universe, indexModelFactory.makeIndex(universe), reporter)
Some(index)
}
case _ =>
diff --git a/test/scaladoc/scalacheck/HtmlFactoryTest.scala b/test/scaladoc/scalacheck/HtmlFactoryTest.scala
index f0f106b293..daa7de8545 100644
--- a/test/scaladoc/scalacheck/HtmlFactoryTest.scala
+++ b/test/scaladoc/scalacheck/HtmlFactoryTest.scala
@@ -26,6 +26,8 @@ object Test extends Properties("HtmlFactory") {
final val RESOURCES = "test/scaladoc/resources/"
+
+ import scala.tools.nsc.ScalaDocReporter
import scala.tools.nsc.doc.{DocFactory, Settings}
import scala.tools.nsc.doc.model.IndexModelFactory
import scala.tools.nsc.doc.html.HtmlFactory
@@ -59,7 +61,7 @@ object Test extends Properties("HtmlFactory") {
createFactory.makeUniverse(Left(List(RESOURCES+basename))) match {
case Some(universe) => {
val index = IndexModelFactory.makeIndex(universe)
- (new HtmlFactory(universe, index)).writeTemplates((page) => {
+ (new HtmlFactory(universe, index, new ScalaDocReporter(universe.settings))).writeTemplates((page) => {
result += (page.absoluteLinkTo(page.path) -> page.body)
})
}
@@ -75,7 +77,8 @@ object Test extends Properties("HtmlFactory") {
val index = IndexModelFactory.makeIndex(universe)
val pages = index.firstLetterIndex.map({
case (key, value) => {
- val page = new ReferenceIndex(key, index, universe)
+ val reporter = new ScalaDocReporter(universe.settings)
+ val page = new ReferenceIndex(key, index, universe, reporter)
page.absoluteLinkTo(page.path) -> page.body
}
})
@@ -723,9 +726,9 @@ object Test extends Properties("HtmlFactory") {
}
case _ => false
}
- property("package") = files.get("com/example/p1/package.html") != None
+ property("package") = files.get("com/example/p1/index.html") != None
- property("package object") = files("com/example/p1/package.html") match {
+ property("package object") = files("com/example/p1/index.html") match {
case node: scala.xml.Node =>
node.toString contains "com.example.p1#packageObjectMethod"
case _ => false
@@ -743,13 +746,13 @@ object Test extends Properties("HtmlFactory") {
property("SI-8514: No inconsistencies") =
checkText("SI-8514.scala")(
- (Some("a/package"),
+ (Some("a/index"),
"""class A extends AnyRef
Some doc here
Some doc here
Annotations @DeveloperApi()
""", true),
- (Some("a/package"),
+ (Some("a/index"),
"""class B extends AnyRef
Annotations @DeveloperApi()
""", true)
@@ -794,28 +797,28 @@ object Test extends Properties("HtmlFactory") {
case _ => false
}
- property("SI-8144: Members' permalink - package") = check("some/package.html") { node =>
- ("type link" |: node.assertTypeLink("../index.html#some.package")) &&
- ("member: some.pack" |: node.assertValuesLink("some.pack", "../index.html#some.package@pack"))
+ property("SI-8144: Members' permalink - package") = check("some/index.html") { node =>
+ ("type link" |: node.assertTypeLink("../some/index.html")) &&
+ ("member: some.pack" |: node.assertValuesLink("some.pack", "../some/index.html#pack"))
}
- property("SI-8144: Members' permalink - inner package") = check("some/pack/package.html") { node =>
- ("type link" |: node.assertTypeLink("../../index.html#some.pack.package")) &&
- ("member: SomeType (object)" |: node.assertValuesLink("some.pack.SomeType", "../../index.html#some.pack.package@SomeType")) &&
- ("member: SomeType (class)" |: node.assertMemberLink("types")("some.pack.SomeType", "../../index.html#some.pack.package@SomeTypeextendsAnyRef"))
+ property("SI-8144: Members' permalink - inner package") = check("some/pack/index.html") { node =>
+ ("type link" |: node.assertTypeLink("../../some/pack/index.html")) &&
+ ("member: SomeType (object)" |: node.assertValuesLink("some.pack.SomeType", "../../some/pack/index.html#SomeType")) &&
+ ("member: SomeType (class)" |: node.assertMemberLink("types")("some.pack.SomeType", "../../some/pack/index.html#SomeTypeextendsAnyRef"))
}
property("SI-8144: Members' permalink - companion object") = check("some/pack/SomeType$.html") { node =>
- ("type link" |: node.assertTypeLink("../../index.html#some.pack.SomeType$")) &&
- ("member: someVal" |: node.assertMemberLink("allMembers")("some.pack.SomeType#someVal", "../../index.html#some.pack.SomeType$@someVal:String"))
+ ("type link" |: node.assertTypeLink("../../some/pack/SomeType$.html")) &&
+ ("member: someVal" |: node.assertMemberLink("allMembers")("some.pack.SomeType#someVal", "../../some/pack/SomeType$.html#someVal:String"))
}
property("SI-8144: Members' permalink - class") = check("some/pack/SomeType.html") { node =>
- ("type link" |: node.assertTypeLink("../../index.html#some.pack.SomeType")) &&
- ("constructor " |: node.assertMemberLink("constructors")("some.pack.SomeType#<init>", "../../index.html#some.pack.SomeType@<init>(arg:String):some.pack.SomeType")) &&
- ( "member: type TypeAlias" |: node.assertMemberLink("types")("some.pack.SomeType.TypeAlias", "../../index.html#some.pack.SomeType@TypeAlias=String")) &&
- ( "member: def >#<():Int " |: node.assertValuesLink("some.pack.SomeType#>#<", "../../index.html#some.pack.SomeType@>#<():Int")) &&
- ( "member: def >@<():TypeAlias " |: node.assertValuesLink("some.pack.SomeType#>@<", "../../index.html#some.pack.SomeType@>@<():SomeType.this.TypeAlias"))
+ ("type link" |: node.assertTypeLink("../../some/pack/SomeType.html")) &&
+ ("constructor " |: node.assertMemberLink("constructors")("some.pack.SomeType#<init>", "../../some/pack/SomeType.html#<init>(arg:String):some.pack.SomeType")) &&
+ ( "member: type TypeAlias" |: node.assertMemberLink("types")("some.pack.SomeType.TypeAlias", "../../some/pack/SomeType.html#TypeAlias=String")) &&
+ ( "member: def >#<():Int " |: node.assertValuesLink("some.pack.SomeType#>#<", "../../some/pack/SomeType.html#>#<():Int")) &&
+ ( "member: def >@<():TypeAlias " |: node.assertValuesLink("some.pack.SomeType#>@<", "../../some/pack/SomeType.html#>@<():SomeType.this.TypeAlias"))
}
}
diff --git a/test/scaladoc/scalacheck/IndexTest.scala b/test/scaladoc/scalacheck/IndexTest.scala
index 7dbd2103a6..036586c21d 100644
--- a/test/scaladoc/scalacheck/IndexTest.scala
+++ b/test/scaladoc/scalacheck/IndexTest.scala
@@ -1,6 +1,7 @@
import org.scalacheck._
import org.scalacheck.Prop._
+import scala.tools.nsc.ScalaDocReporter
import scala.tools.nsc.doc
import scala.tools.nsc.doc.html.page.Index
import java.net.{URLClassLoader, URLDecoder}
@@ -47,7 +48,8 @@ object Test extends Properties("Index") {
maybeUniverse match {
case Some(universe) => {
- val index = new Index(universe, indexModelFactory.makeIndex(universe))
+ val reporter = new ScalaDocReporter(universe.settings)
+ val index = new Index(universe, indexModelFactory.makeIndex(universe), reporter)
return Some(index)
}
case _ => return None
@@ -71,14 +73,6 @@ object Test extends Properties("Index") {
case None => false
}
}
- property("browser contains a script element") = {
- createIndex("src/scaladoc/scala/tools/nsc/doc/html/page/Index.scala") match {
- case Some(index) =>
- (index.browser \ "script").size == 1
-
- case None => false
- }
- }
property("package objects in index") = {
createIndex("test/scaladoc/resources/SI-5558.scala") match {
case Some(index) =>
diff --git a/versions.properties b/versions.properties
index 7cdafc975a..c63b15a3f0 100644
--- a/versions.properties
+++ b/versions.properties
@@ -30,7 +30,7 @@ jline.version=2.12.1
scala-asm.version=5.0.4-scala-3
# external modules, used internally (not shipped)
-partest.version.number=1.0.12
+partest.version.number=1.0.13
scalacheck.version.number=1.11.6
# TODO: modularize the compiler