diff options
93 files changed, 4081 insertions, 1687 deletions
@@ -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") @@ -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"><</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"></i> + <i id="search-icon" class="material-icons"></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"></i> + <span class='input'> + <input id='mbrsel-input' placeholder='Filter members' type='text' accesskey='/'/> + </span> + <i class="clear material-icons"></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"></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"></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"></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"></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"></i></button> + <button id="diagram-zoom-in" class="diagram-btn"><i class="material-icons"></i></button> + <button title="Toggle full-screen" id="diagram-fs" class="diagram-btn to-full"><i class="material-icons"></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"></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(" ") + 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 } – { 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"><</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 Binary files differnew file mode 100644 index 0000000000..7437fd9805 --- /dev/null +++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/lato-v11-latin-100.eot 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 Binary files differnew file mode 100644 index 0000000000..4e7128a481 --- /dev/null +++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/lato-v11-latin-100.ttf 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 Binary files differnew file mode 100644 index 0000000000..48915bb476 --- /dev/null +++ b/src/scaladoc/scala/tools/nsc/doc/html/resource/lib/lato-v11-latin-100.woff 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 |