summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/Reporting.scala2
-rwxr-xr-xsrc/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala129
-rwxr-xr-xsrc/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala25
-rw-r--r--src/compiler/scala/tools/nsc/backend/icode/GenICode.scala7
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala28
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala23
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala56
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala8
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala28
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala14
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala63
-rw-r--r--src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala72
-rw-r--r--src/compiler/scala/tools/nsc/plugins/Plugin.scala6
-rw-r--r--src/compiler/scala/tools/nsc/settings/ScalaSettings.scala37
-rw-r--r--src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala22
-rw-r--r--src/compiler/scala/tools/nsc/transform/AddInterfaces.scala2
-rw-r--r--src/compiler/scala/tools/nsc/transform/Erasure.scala10
-rw-r--r--src/compiler/scala/tools/nsc/transform/LambdaLift.scala14
-rw-r--r--src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala2
-rw-r--r--src/library/scala/collection/TraversableLike.scala2
-rw-r--r--src/library/scala/collection/TraversableOnce.scala17
-rw-r--r--src/partest-extras/scala/tools/partest/ParserTest.scala21
-rw-r--r--src/reflect/scala/reflect/internal/Symbols.scala1
-rw-r--r--src/reflect/scala/reflect/runtime/JavaMirrors.scala6
-rwxr-xr-xsrc/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala33
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala47
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala6
-rw-r--r--src/scaladoc/scala/tools/nsc/doc/html/resource/lib/template.css2
28 files changed, 451 insertions, 232 deletions
diff --git a/src/compiler/scala/tools/nsc/Reporting.scala b/src/compiler/scala/tools/nsc/Reporting.scala
index 72a4b69536..4e7a527a5a 100644
--- a/src/compiler/scala/tools/nsc/Reporting.scala
+++ b/src/compiler/scala/tools/nsc/Reporting.scala
@@ -46,7 +46,7 @@ trait Reporting extends scala.reflect.internal.Reporting { self: ast.Positions w
private val _deprecationWarnings = new ConditionalWarning("deprecation", settings.deprecation)()
private val _uncheckedWarnings = new ConditionalWarning("unchecked", settings.unchecked)()
private val _featureWarnings = new ConditionalWarning("feature", settings.feature)()
- private val _inlinerWarnings = new ConditionalWarning("inliner", settings.YinlinerWarnings)(if (settings.isBCodeAskedFor) settings.YoptWarnings.name else settings.YinlinerWarnings.name)
+ private val _inlinerWarnings = new ConditionalWarning("inliner", settings.YinlinerWarnings)(if (settings.isBCodeActive) settings.YoptWarnings.name else settings.YinlinerWarnings.name)
private val _allConditionalWarnings = List(_deprecationWarnings, _uncheckedWarnings, _featureWarnings, _inlinerWarnings)
// TODO: remove in favor of the overload that takes a Symbol, give that argument a default (NoSymbol)
diff --git a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala
index 96939e616c..52b8a51a79 100755
--- a/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/MarkupParsers.scala
@@ -6,6 +6,7 @@
package scala.tools.nsc
package ast.parser
+import scala.annotation.tailrec
import scala.collection.mutable
import mutable.{ Buffer, ArrayBuffer, ListBuffer }
import scala.util.control.ControlThrowable
@@ -172,20 +173,19 @@ trait MarkupParsers {
}
def appendText(pos: Position, ts: Buffer[Tree], txt: String): Unit = {
- def append(t: String) = ts append handle.text(pos, t)
-
- if (preserveWS) append(txt)
- else {
+ def append(text: String): Unit = {
+ val tree = handle.text(pos, text)
+ ts append tree
+ }
+ val clean = if (preserveWS) txt else {
val sb = new StringBuilder()
-
txt foreach { c =>
if (!isSpace(c)) sb append c
else if (sb.isEmpty || !isSpace(sb.last)) sb append ' '
}
-
- val trimmed = sb.toString.trim
- if (!trimmed.isEmpty) append(trimmed)
+ sb.toString.trim
}
+ if (!clean.isEmpty) append(clean)
}
/** adds entity/character to ts as side-effect
@@ -216,44 +216,75 @@ trait MarkupParsers {
if (xCheckEmbeddedBlock) ts append xEmbeddedExpr
else appendText(p, ts, xText)
- /** Returns true if it encounters an end tag (without consuming it),
- * appends trees to ts as side-effect.
+ /** At an open angle-bracket, detects an end tag
+ * or consumes CDATA, comment, PI or element.
+ * Trees are appended to `ts` as a side-effect.
+ * @return true if an end tag (without consuming it)
*/
- private def content_LT(ts: ArrayBuffer[Tree]): Boolean = {
- if (ch == '/')
- return true // end tag
-
- val toAppend = ch match {
- case '!' => nextch() ; if (ch =='[') xCharData else xComment // CDATA or Comment
- case '?' => nextch() ; xProcInstr // PI
- case _ => element // child node
+ private def content_LT(ts: ArrayBuffer[Tree]): Boolean =
+ (ch == '/') || {
+ val toAppend = ch match {
+ case '!' => nextch() ; if (ch =='[') xCharData else xComment // CDATA or Comment
+ case '?' => nextch() ; xProcInstr // PI
+ case _ => element // child node
+ }
+ ts append toAppend
+ false
}
- ts append toAppend
- false
- }
-
def content: Buffer[Tree] = {
val ts = new ArrayBuffer[Tree]
- while (true) {
- if (xEmbeddedBlock)
+ val coalescing = settings.XxmlSettings.isCoalescing
+ @tailrec def loopContent(): Unit =
+ if (xEmbeddedBlock) {
ts append xEmbeddedExpr
- else {
+ loopContent()
+ } else {
tmppos = o2p(curOffset)
ch match {
- // end tag, cdata, comment, pi or child node
- case '<' => nextch() ; if (content_LT(ts)) return ts
- // either the character '{' or an embedded scala block }
- case '{' => content_BRACE(tmppos, ts) // }
- // EntityRef or CharRef
- case '&' => content_AMP(ts)
- case SU => return ts
- // text content - here xEmbeddedBlock might be true
- case _ => appendText(tmppos, ts, xText)
+ case '<' => // end tag, cdata, comment, pi or child node
+ nextch()
+ if (!content_LT(ts)) loopContent()
+ case '{' => // } literal brace or embedded Scala block
+ content_BRACE(tmppos, ts)
+ loopContent()
+ case '&' => // EntityRef or CharRef
+ content_AMP(ts)
+ loopContent()
+ case SU => ()
+ case _ => // text content - here xEmbeddedBlock might be true
+ appendText(tmppos, ts, xText)
+ loopContent()
}
}
+ // merge text sections and strip attachments
+ def coalesce(): ArrayBuffer[Tree] = {
+ def copy() = {
+ val buf = new ArrayBuffer[Tree]
+ var acc = new StringBuilder
+ var pos: Position = NoPosition
+ def emit() = if (acc.nonEmpty) {
+ appendText(pos, buf, acc.toString)
+ acc.clear()
+ }
+ for (t <- ts)
+ t.attachments.get[handle.TextAttache] match {
+ case Some(ta) =>
+ if (acc.isEmpty) pos = ta.pos
+ acc append ta.text
+ case _ =>
+ emit()
+ buf += t
+ }
+ emit()
+ buf
+ }
+ val res = if (ts.count(_.hasAttachment[handle.TextAttache]) > 1) copy() else ts
+ for (t <- res) t.removeAttachment[handle.TextAttache]
+ res
}
- unreachable
+ loopContent()
+ if (coalescing) coalesce() else ts
}
/** '<' element ::= xmlTag1 '>' { xmlExpr | '{' simpleExpr '}' } ETag
@@ -289,20 +320,16 @@ trait MarkupParsers {
private def xText: String = {
assert(!xEmbeddedBlock, "internal error: encountered embedded block")
val buf = new StringBuilder
- def done = buf.toString
-
- while (ch != SU) {
- if (ch == '}') {
- if (charComingAfter(nextch()) == '}') nextch()
- else errorBraces()
- }
-
- buf append ch
- nextch()
- if (xCheckEmbeddedBlock || ch == '<' || ch == '&')
- return done
- }
- done
+ if (ch != SU)
+ do {
+ if (ch == '}') {
+ if (charComingAfter(nextch()) == '}') nextch()
+ else errorBraces()
+ }
+ buf append ch
+ nextch()
+ } while (!(ch == SU || xCheckEmbeddedBlock || ch == '<' || ch == '&'))
+ buf.toString
}
/** Some try/catch/finally logic used by xLiteral and xLiteralPattern. */
@@ -344,12 +371,12 @@ trait MarkupParsers {
tmppos = o2p(curOffset) // Iuli: added this line, as it seems content_LT uses tmppos when creating trees
content_LT(ts)
- // parse more XML ?
+ // parse more XML?
if (charComingAfter(xSpaceOpt()) == '<') {
do {
xSpaceOpt()
nextch()
- ts append element
+ content_LT(ts)
} while (charComingAfter(xSpaceOpt()) == '<')
handle.makeXMLseq(r2p(start, start, curOffset), ts)
}
diff --git a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala
index d2a999cdec..99399e363f 100755
--- a/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala
+++ b/src/compiler/scala/tools/nsc/ast/parser/SymbolicXMLBuilder.scala
@@ -36,6 +36,7 @@ abstract class SymbolicXMLBuilder(p: Parsers#Parser, preserveWS: Boolean) {
val _MetaData: NameType = "MetaData"
val _NamespaceBinding: NameType = "NamespaceBinding"
val _NodeBuffer: NameType = "NodeBuffer"
+ val _PCData: NameType = "PCData"
val _PrefixedAttribute: NameType = "PrefixedAttribute"
val _ProcInstr: NameType = "ProcInstr"
val _Text: NameType = "Text"
@@ -46,6 +47,7 @@ abstract class SymbolicXMLBuilder(p: Parsers#Parser, preserveWS: Boolean) {
private object xmlterms extends TermNames {
val _Null: NameType = "Null"
val __Elem: NameType = "Elem"
+ val _PCData: NameType = "PCData"
val __Text: NameType = "Text"
val _buf: NameType = "$buf"
val _md: NameType = "$md"
@@ -55,10 +57,15 @@ abstract class SymbolicXMLBuilder(p: Parsers#Parser, preserveWS: Boolean) {
val _xml: NameType = "xml"
}
- import xmltypes.{_Comment, _Elem, _EntityRef, _Group, _MetaData, _NamespaceBinding, _NodeBuffer,
- _PrefixedAttribute, _ProcInstr, _Text, _Unparsed, _UnprefixedAttribute}
+ import xmltypes.{
+ _Comment, _Elem, _EntityRef, _Group, _MetaData, _NamespaceBinding, _NodeBuffer,
+ _PCData, _PrefixedAttribute, _ProcInstr, _Text, _Unparsed, _UnprefixedAttribute
+ }
+
+ import xmlterms.{ _Null, __Elem, __Text, _buf, _md, _plus, _scope, _tmpscope, _xml }
- import xmlterms.{_Null, __Elem, __Text, _buf, _md, _plus, _scope, _tmpscope, _xml}
+ /** Attachment for trees deriving from text nodes (Text, CData, entities). Used for coalescing. */
+ case class TextAttache(pos: Position, text: String)
// convenience methods
private def LL[A](x: A*): List[List[A]] = List(List(x:_*))
@@ -108,16 +115,22 @@ abstract class SymbolicXMLBuilder(p: Parsers#Parser, preserveWS: Boolean) {
final def entityRef(pos: Position, n: String) =
atPos(pos)( New(_scala_xml_EntityRef, LL(const(n))) )
+ private def coalescing = settings.XxmlSettings.isCoalescing
+
// create scala.xml.Text here <: scala.xml.Node
final def text(pos: Position, txt: String): Tree = atPos(pos) {
- if (isPattern) makeTextPat(const(txt))
- else makeText1(const(txt))
+ val t = if (isPattern) makeTextPat(const(txt)) else makeText1(const(txt))
+ if (coalescing) t updateAttachment TextAttache(pos, txt) else t
}
def makeTextPat(txt: Tree) = Apply(_scala_xml__Text, List(txt))
def makeText1(txt: Tree) = New(_scala_xml_Text, LL(txt))
def comment(pos: Position, text: String) = atPos(pos)( Comment(const(text)) )
- def charData(pos: Position, txt: String) = atPos(pos)( makeText1(const(txt)) )
+ def charData(pos: Position, txt: String) = atPos(pos) {
+ val t = if (isPattern) Apply(_scala_xml(xmlterms._PCData), List(const(txt)))
+ else New(_scala_xml(_PCData), LL(const(txt)))
+ if (coalescing) t updateAttachment TextAttache(pos, txt) else t
+ }
def procInstr(pos: Position, target: String, txt: String) =
atPos(pos)( ProcInstr(const(target), const(txt)) )
diff --git a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
index 185fd93501..137954b52d 100644
--- a/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
+++ b/src/compiler/scala/tools/nsc/backend/icode/GenICode.scala
@@ -883,7 +883,12 @@ abstract class GenICode extends SubComponent {
case None =>
val saved = settings.uniqid
settings.uniqid.value = true
- try abort(s"symbol $sym does not exist in ${ctx.method}, which contains locals ${ctx.method.locals.mkString(",")}")
+ try {
+ val methodCode = unit.body.collect { case dd: DefDef
+ if dd.symbol == ctx.method.symbol => showCode(dd);
+ }.headOption.getOrElse("<unknown>")
+ abort(s"symbol $sym does not exist in ${ctx.method}, which contains locals ${ctx.method.locals.mkString(",")}. \nMethod code: $methodCode")
+ }
finally settings.uniqid.value = saved
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
index d8a17e975e..d690542f0e 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypes.scala
@@ -11,11 +11,12 @@ import scala.collection.concurrent.TrieMap
import scala.reflect.internal.util.Position
import scala.tools.asm
import asm.Opcodes
-import scala.tools.asm.tree.{MethodInsnNode, InnerClassNode, ClassNode}
+import scala.tools.asm.tree.{MethodNode, MethodInsnNode, InnerClassNode, ClassNode}
import scala.tools.nsc.backend.jvm.BTypes.{InlineInfo, MethodInlineInfo}
import scala.tools.nsc.backend.jvm.BackendReporting._
import scala.tools.nsc.backend.jvm.opt._
import scala.collection.convert.decorateAsScala._
+import scala.tools.nsc.settings.ScalaSettings
/**
* The BTypes component defines The BType class hierarchy. A BType stores all type information
@@ -39,6 +40,8 @@ abstract class BTypes {
*/
val byteCodeRepository: ByteCodeRepository
+ val localOpt: LocalOpt[this.type]
+
val inliner: Inliner[this.type]
val callGraph: CallGraph[this.type]
@@ -48,14 +51,9 @@ abstract class BTypes {
// Allows to define per-run caches here and in the CallGraph component, which don't have a global
def recordPerRunCache[T <: collection.generic.Clearable](cache: T): T
- // When building the call graph, we need to know if global inlining is allowed (the component doesn't have a global)
- def inlineGlobalEnabled: Boolean
-
- // When the inliner is not enabled, there's no point in adding InlineInfos to all ClassBTypes
- def inlinerEnabled: Boolean
+ // Allows access to the compiler settings for backend components that don't have a global in scope
+ def compilerSettings: ScalaSettings
- // Settings that define what kind of optimizer warnings are emitted.
- def warnSettings: WarnSettings
/**
* A map from internal names to ClassBTypes. Every ClassBType is added to this map on its
@@ -83,6 +81,18 @@ abstract class BTypes {
val javaDefinedClasses: collection.mutable.Set[InternalName] = recordPerRunCache(collection.mutable.Set.empty)
/**
+ * Cache, contains methods whose unreachable instructions are eliminated.
+ *
+ * The ASM Analyzer class does not compute any frame information for unreachable instructions.
+ * Transformations that use an analyzer (including inlining) therefore require unreachable code
+ * to be eliminated.
+ *
+ * This cache allows running dead code elimination whenever an analyzer is used. If the method
+ * is already optimized, DCE can return early.
+ */
+ val unreachableCodeEliminated: collection.mutable.Set[MethodNode] = recordPerRunCache(collection.mutable.Set.empty)
+
+ /**
* Obtain the BType for a type descriptor or internal name. For class descriptors, the ClassBType
* is constructed by parsing the corresponding classfile.
*
@@ -229,7 +239,7 @@ abstract class BTypes {
// The InlineInfo is built from the classfile (not from the symbol) for all classes that are NOT
// being compiled. For those classes, the info is only needed if the inliner is enabled, othewise
// we can save the memory.
- if (!inlinerEnabled) BTypes.EmptyInlineInfo
+ if (!compilerSettings.YoptInlinerEnabled) BTypes.EmptyInlineInfo
else fromClassfileAttribute getOrElse fromClassfileWithoutAttribute
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
index eeb6ed24a2..1b9fd5e298 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BTypesFromSymbols.scala
@@ -7,9 +7,10 @@ package scala.tools.nsc
package backend.jvm
import scala.tools.asm
-import scala.tools.nsc.backend.jvm.opt.{CallGraph, Inliner, ByteCodeRepository}
+import scala.tools.nsc.backend.jvm.opt.{LocalOpt, CallGraph, Inliner, ByteCodeRepository}
import scala.tools.nsc.backend.jvm.BTypes.{InlineInfo, MethodInlineInfo, InternalName}
import BackendReporting._
+import scala.tools.nsc.settings.ScalaSettings
/**
* This class mainly contains the method classBTypeFromSymbol, which extracts the necessary
@@ -37,6 +38,8 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
val byteCodeRepository = new ByteCodeRepository(global.classPath, javaDefinedClasses, recordPerRunCache(collection.concurrent.TrieMap.empty))
+ val localOpt: LocalOpt[this.type] = new LocalOpt(this)
+
val inliner: Inliner[this.type] = new Inliner(this)
val callGraph: CallGraph[this.type] = new CallGraph(this)
@@ -49,19 +52,7 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
def recordPerRunCache[T <: collection.generic.Clearable](cache: T): T = perRunCaches.recordCache(cache)
- def inlineGlobalEnabled: Boolean = settings.YoptInlineGlobal
-
- def inlinerEnabled: Boolean = settings.YoptInlinerEnabled
-
- def warnSettings: WarnSettings = {
- val c = settings.YoptWarningsChoices
- // cannot extract settings.YoptWarnings into a local val due to some dependent typing issue.
- WarnSettings(
- !settings.YoptWarnings.isSetByUser || settings.YoptWarnings.contains(c.atInlineFailedSummary.name) || settings.YoptWarnings.contains(c.atInlineFailed.name),
- settings.YoptWarnings.contains(c.noInlineMixed.name),
- settings.YoptWarnings.contains(c.noInlineMissingBytecode.name),
- settings.YoptWarnings.contains(c.noInlineMissingScalaInlineInfoAttr.name))
- }
+ def compilerSettings: ScalaSettings = settings
// helpers that need access to global.
// TODO @lry create a separate component, they don't belong to BTypesFromSymbols
@@ -418,8 +409,8 @@ class BTypesFromSymbols[G <: Global](val global: G) extends BTypes {
// phase travel required, see implementation of `compiles`. for nested classes, it checks if the
// enclosingTopLevelClass is being compiled. after flatten, all classes are considered top-level,
// so `compiles` would return `false`.
- if (exitingPickler(currentRun.compiles(classSym))) buildFromSymbol // InlineInfo required for classes being compiled, we have to create the classfile attribute
- else if (!inlinerEnabled) BTypes.EmptyInlineInfo // For other classes, we need the InlineInfo only inf the inliner is enabled.
+ if (exitingPickler(currentRun.compiles(classSym))) buildFromSymbol // InlineInfo required for classes being compiled, we have to create the classfile attribute
+ else if (!compilerSettings.YoptInlinerEnabled) BTypes.EmptyInlineInfo // For other classes, we need the InlineInfo only inf the inliner is enabled.
else {
// For classes not being compiled, the InlineInfo is read from the classfile attribute. This
// fixes an issue with mixed-in methods: the mixin phase enters mixin methods only to class
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala
index a06fb4bab8..d641f708d2 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/BackendReporting.scala
@@ -4,6 +4,8 @@ package backend.jvm
import scala.tools.asm.tree.{AbstractInsnNode, MethodNode}
import scala.tools.nsc.backend.jvm.BTypes.InternalName
import scala.reflect.internal.util.Position
+import scala.tools.nsc.settings.ScalaSettings
+import scala.util.control.ControlThrowable
/**
* Interface for emitting inline warnings. The interface is required because the implementation
@@ -73,24 +75,22 @@ object BackendReporting {
}
}
- case class Invalid[A](e: A) extends Exception
+ case class Invalid[A](e: A) extends ControlThrowable
/**
* See documentation of orThrow above.
*/
def tryEither[A, B](op: => Either[A, B]): Either[A, B] = try { op } catch { case Invalid(e) => Left(e.asInstanceOf[A]) }
- final case class WarnSettings(atInlineFailed: Boolean, noInlineMixed: Boolean, noInlineMissingBytecode: Boolean, noInlineMissingScalaInlineInfoAttr: Boolean)
-
sealed trait OptimizerWarning {
- def emitWarning(settings: WarnSettings): Boolean
+ def emitWarning(settings: ScalaSettings): Boolean
}
// Method filter in RightBiasedEither requires an implicit empty value. Taking the value here
// in scope allows for-comprehensions that desugar into filter calls (for example when using a
// tuple de-constructor).
implicit object emptyOptimizerWarning extends OptimizerWarning {
- def emitWarning(settings: WarnSettings): Boolean = false
+ def emitWarning(settings: ScalaSettings): Boolean = false
}
sealed trait MissingBytecodeWarning extends OptimizerWarning {
@@ -112,17 +112,17 @@ object BackendReporting {
missingClass.map(c => s" Reason:\n$c").getOrElse("")
}
- def emitWarning(settings: WarnSettings): Boolean = this match {
+ def emitWarning(settings: ScalaSettings): Boolean = this match {
case ClassNotFound(_, javaDefined) =>
- if (javaDefined) settings.noInlineMixed
- else settings.noInlineMissingBytecode
+ if (javaDefined) settings.YoptWarningNoInlineMixed
+ else settings.YoptWarningNoInlineMissingBytecode
case m @ MethodNotFound(_, _, _, missing) =>
if (m.isArrayMethod) false
- else settings.noInlineMissingBytecode || missing.exists(_.emitWarning(settings))
+ else settings.YoptWarningNoInlineMissingBytecode || missing.exists(_.emitWarning(settings))
case FieldNotFound(_, _, _, missing) =>
- settings.noInlineMissingBytecode || missing.exists(_.emitWarning(settings))
+ settings.YoptWarningNoInlineMissingBytecode || missing.exists(_.emitWarning(settings))
}
}
@@ -141,9 +141,9 @@ object BackendReporting {
s"Failed to get the type of class symbol $classFullName due to SI-9111."
}
- def emitWarning(settings: WarnSettings): Boolean = this match {
+ def emitWarning(settings: ScalaSettings): Boolean = this match {
case NoClassBTypeInfoMissingBytecode(cause) => cause.emitWarning(settings)
- case NoClassBTypeInfoClassSymbolInfoFailedSI9111(_) => settings.noInlineMissingBytecode
+ case NoClassBTypeInfoClassSymbolInfoFailedSI9111(_) => settings.YoptWarningNoInlineMissingBytecode
}
}
@@ -175,11 +175,11 @@ object BackendReporting {
cause.toString
}
- def emitWarning(settings: WarnSettings): Boolean = this match {
+ def emitWarning(settings: ScalaSettings): Boolean = this match {
case MethodInlineInfoIncomplete(_, _, _, cause) => cause.emitWarning(settings)
case MethodInlineInfoMissing(_, _, _, Some(cause)) => cause.emitWarning(settings)
- case MethodInlineInfoMissing(_, _, _, None) => settings.noInlineMissingBytecode
+ case MethodInlineInfoMissing(_, _, _, None) => settings.YoptWarningNoInlineMissingBytecode
case MethodInlineInfoError(_, _, _, cause) => cause.emitWarning(settings)
@@ -214,11 +214,21 @@ object BackendReporting {
case SynchronizedMethod(_, _, _) =>
s"Method $calleeMethodSig cannot be inlined because it is synchronized."
+
+ case StrictfpMismatch(_, _, _, callsiteClass, callsiteName, callsiteDesc) =>
+ s"""The callsite method ${BackendReporting.methodSignature(callsiteClass, callsiteName, callsiteDesc)}
+ |does not have the same strictfp mode as the callee $calleeMethodSig.
+ """.stripMargin
+
+ case ResultingMethodTooLarge(_, _, _, callsiteClass, callsiteName, callsiteDesc) =>
+ s"""The size of the callsite method ${BackendReporting.methodSignature(callsiteClass, callsiteName, callsiteDesc)}
+ |would exceed the JVM method size limit after inlining $calleeMethodSig.
+ """.stripMargin
}
- def emitWarning(settings: WarnSettings): Boolean = this match {
- case _: IllegalAccessInstruction | _: MethodWithHandlerCalledOnNonEmptyStack | _: SynchronizedMethod =>
- settings.atInlineFailed
+ def emitWarning(settings: ScalaSettings): Boolean = this match {
+ case _: IllegalAccessInstruction | _: MethodWithHandlerCalledOnNonEmptyStack | _: SynchronizedMethod | _: StrictfpMismatch | _: ResultingMethodTooLarge =>
+ settings.YoptWarningEmitAtInlineFailed
case IllegalAccessCheckFailed(_, _, _, _, _, cause) =>
cause.emitWarning(settings)
@@ -231,6 +241,10 @@ object BackendReporting {
case class MethodWithHandlerCalledOnNonEmptyStack(calleeDeclarationClass: InternalName, name: String, descriptor: String,
callsiteClass: InternalName, callsiteName: String, callsiteDesc: String) extends CannotInlineWarning
case class SynchronizedMethod(calleeDeclarationClass: InternalName, name: String, descriptor: String) extends CannotInlineWarning
+ case class StrictfpMismatch(calleeDeclarationClass: InternalName, name: String, descriptor: String,
+ callsiteClass: InternalName, callsiteName: String, callsiteDesc: String) extends CannotInlineWarning
+ case class ResultingMethodTooLarge(calleeDeclarationClass: InternalName, name: String, descriptor: String,
+ callsiteClass: InternalName, callsiteName: String, callsiteDesc: String) extends CannotInlineWarning
/**
* Used in the InlineInfo of a ClassBType, when some issue occurred obtaining the inline information.
@@ -250,11 +264,11 @@ object BackendReporting {
s"Cannot read ScalaInlineInfo version $version in classfile $internalName. Use a more recent compiler."
}
- def emitWarning(settings: WarnSettings): Boolean = this match {
- case NoInlineInfoAttribute(_) => settings.noInlineMissingScalaInlineInfoAttr
+ def emitWarning(settings: ScalaSettings): Boolean = this match {
+ case NoInlineInfoAttribute(_) => settings.YoptWarningNoInlineMissingScalaInlineInfoAttr
case ClassNotFoundWhenBuildingInlineInfoFromSymbol(cause) => cause.emitWarning(settings)
- case ClassSymbolInfoFailureSI9111(_) => settings.noInlineMissingBytecode
- case UnknownScalaInlineInfoVersion(_, _) => settings.noInlineMissingScalaInlineInfoAttr
+ case ClassSymbolInfoFailureSI9111(_) => settings.YoptWarningNoInlineMissingBytecode
+ case UnknownScalaInlineInfoVersion(_, _) => settings.YoptWarningNoInlineMissingScalaInlineInfoAttr
}
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
index be1595dc29..c6ee36d7b2 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/GenBCode.scala
@@ -14,7 +14,6 @@ import scala.reflect.internal.util.Statistics
import scala.tools.asm
import scala.tools.asm.tree.ClassNode
-import scala.tools.nsc.backend.jvm.opt.LocalOpt
/*
* Prepare in-memory representations of classfiles using the ASM Tree API, and serialize them to disk.
@@ -215,17 +214,12 @@ abstract class GenBCode extends BCodeSyncAndTry {
* - converting the plain ClassNode to byte array and placing it on queue-3
*/
class Worker2 {
- lazy val localOpt = new LocalOpt(settings)
-
def runGlobalOptimizations(): Unit = {
import scala.collection.convert.decorateAsScala._
q2.asScala foreach {
case Item2(_, _, plain, _, _) =>
// skip mirror / bean: wd don't inline into tem, and they are not used in the plain class
- if (plain != null) {
- localOpt.minimalRemoveUnreachableCode(plain)
- callGraph.addClass(plain)
- }
+ if (plain != null) callGraph.addClass(plain)
}
bTypes.inliner.runInliner()
}
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
index 14e8cccc60..201ab15177 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/BytecodeUtils.scala
@@ -10,6 +10,7 @@ package opt
import scala.annotation.{tailrec, switch}
import scala.collection.mutable
import scala.reflect.internal.util.Collections._
+import scala.tools.asm.commons.CodeSizeEvaluator
import scala.tools.asm.tree.analysis._
import scala.tools.asm.{MethodWriter, ClassWriter, Label, Opcodes}
import scala.tools.asm.tree._
@@ -21,6 +22,12 @@ import scala.tools.nsc.backend.jvm.BTypes._
object BytecodeUtils {
+ // http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.9.1
+ final val maxJVMMethodSize = 65535
+
+ // 5% margin, more than enough for the instructions added by the inliner (store / load args, null check for instance methods)
+ final val maxMethodSizeAfterInline = maxJVMMethodSize - (maxJVMMethodSize / 20)
+
object Goto {
def unapply(instruction: AbstractInsnNode): Option[JumpInsnNode] = {
if (instruction.getOpcode == Opcodes.GOTO) Some(instruction.asInstanceOf[JumpInsnNode])
@@ -83,10 +90,14 @@ object BytecodeUtils {
def isSynchronizedMethod(methodNode: MethodNode): Boolean = (methodNode.access & Opcodes.ACC_SYNCHRONIZED) != 0
+ def isNativeMethod(methodNode: MethodNode): Boolean = (methodNode.access & Opcodes.ACC_NATIVE) != 0
+
def isFinalClass(classNode: ClassNode): Boolean = (classNode.access & Opcodes.ACC_FINAL) != 0
def isFinalMethod(methodNode: MethodNode): Boolean = (methodNode.access & (Opcodes.ACC_FINAL | Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC)) != 0
+ def isStrictfpMethod(methodNode: MethodNode): Boolean = (methodNode.access & Opcodes.ACC_STRICT) != 0
+
def nextExecutableInstruction(instruction: AbstractInsnNode, alsoKeep: AbstractInsnNode => Boolean = Set()): Option[AbstractInsnNode] = {
var result = instruction
do { result = result.getNext }
@@ -215,7 +226,7 @@ object BytecodeUtils {
* to create a separate visitor for computing those values, duplicating the functionality from the
* MethodWriter.
*/
- def computeMaxLocalsMaxStack(method: MethodNode) {
+ def computeMaxLocalsMaxStack(method: MethodNode): Unit = {
val cw = new ClassWriter(ClassWriter.COMPUTE_MAXS)
val excs = method.exceptions.asScala.toArray
val mw = cw.visitMethod(method.access, method.name, method.desc, method.signature, excs).asInstanceOf[MethodWriter]
@@ -224,6 +235,21 @@ object BytecodeUtils {
method.maxStack = mw.getMaxStack
}
+ def codeSizeOKForInlining(caller: MethodNode, callee: MethodNode): Boolean = {
+ // Looking at the implementation of CodeSizeEvaluator, all instructions except tableswitch and
+ // lookupswitch are <= 8 bytes. These should be rare enough for 8 to be an OK rough upper bound.
+ def roughUpperBound(methodNode: MethodNode): Int = methodNode.instructions.size * 8
+
+ def maxSize(methodNode: MethodNode): Int = {
+ val eval = new CodeSizeEvaluator(null)
+ methodNode.accept(eval)
+ eval.getMaxSize
+ }
+
+ (roughUpperBound(caller) + roughUpperBound(callee) > maxMethodSizeAfterInline) &&
+ (maxSize(caller) + maxSize(callee) > maxMethodSizeAfterInline)
+ }
+
def removeLineNumberNodes(classNode: ClassNode): Unit = {
for (m <- classNode.methods.asScala) removeLineNumberNodes(m.instructions)
}
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 47d32c94cb..028f0f8fa6 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/CallGraph.scala
@@ -43,14 +43,14 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
// callee, we only check there for the methodInlineInfo, we should find it there.
calleeDeclarationClassBType.info.orThrow.inlineInfo.methodInfos.get(methodSignature) match {
case Some(methodInlineInfo) =>
- val canInlineFromSource = inlineGlobalEnabled || calleeSource == CompilationUnit
+ val canInlineFromSource = compilerSettings.YoptInlineGlobal || calleeSource == CompilationUnit
val isAbstract = BytecodeUtils.isAbstractMethod(calleeMethodNode)
// (1) A non-final method can be safe to inline if the receiver type is a final subclass. Example:
// class A { @inline def f = 1 }; object B extends A; B.f // can be inlined
//
- // TODO: type analysis can render more calls statically resolved. Example˜∫
+ // TODO: type analysis can render more calls statically resolved. Example:
// new A.f // can be inlined, the receiver type is known to be exactly A.
val isStaticallyResolved: Boolean = {
methodInlineInfo.effectivelyFinal ||
@@ -68,8 +68,13 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
// (2) Final trait methods can be rewritten from the interface to the static implementation
// method to enable inlining.
CallsiteInfo(
- safeToInline = canInlineFromSource && isStaticallyResolved && !isAbstract, // (1)
- safeToRewrite = canInlineFromSource && isRewritableTraitCall, // (2)
+ safeToInline =
+ canInlineFromSource &&
+ isStaticallyResolved && // (1)
+ !isAbstract &&
+ !BytecodeUtils.isConstructor(calleeMethodNode) &&
+ !BytecodeUtils.isNativeMethod(calleeMethodNode),
+ safeToRewrite = canInlineFromSource && isRewritableTraitCall, // (2)
annotatedInline = methodInlineInfo.annotatedInline,
annotatedNoInline = methodInlineInfo.annotatedNoInline,
warning = warning)
@@ -92,6 +97,7 @@ class CallGraph[BT <: BTypes](val btypes: BT) {
// TODO: for now we run a basic analyzer to get the stack height at the call site.
// once we run a more elaborate analyzer (types, nullness), we can get the stack height out of there.
+ localOpt.minimalRemoveUnreachableCode(methodNode, definingClass.internalName)
val analyzer = new AsmAnalyzer(methodNode, definingClass.internalName)
methodNode.instructions.iterator.asScala.collect({
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 e14e57d3ab..ac5c9ce2e6 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/Inliner.scala
@@ -16,7 +16,7 @@ import scala.collection.convert.decorateAsJava._
import AsmUtils._
import BytecodeUtils._
import collection.mutable
-import scala.tools.asm.tree.analysis.{SourceInterpreter, Analyzer}
+import scala.tools.asm.tree.analysis.SourceInterpreter
import BackendReporting._
import scala.tools.nsc.backend.jvm.BTypes.InternalName
@@ -24,21 +24,39 @@ class Inliner[BT <: BTypes](val btypes: BT) {
import btypes._
import callGraph._
+ def eliminateUnreachableCodeAndUpdateCallGraph(methodNode: MethodNode, definingClass: InternalName): Unit = {
+ localOpt.minimalRemoveUnreachableCode(methodNode, definingClass) foreach {
+ case invocation: MethodInsnNode => callGraph.callsites.remove(invocation)
+ case _ =>
+ }
+ }
+
def runInliner(): Unit = {
rewriteFinalTraitMethodInvocations()
for (request <- collectAndOrderInlineRequests) {
val Right(callee) = request.callee // collectAndOrderInlineRequests returns callsites with a known callee
- val r = inline(request.callsiteInstruction, request.callsiteStackHeight, request.callsiteMethod, request.callsiteClass,
- callee.callee, callee.calleeDeclarationClass,
- receiverKnownNotNull = false, keepLineNumbers = false)
-
- for (warning <- r) {
- if ((callee.annotatedInline && btypes.warnSettings.atInlineFailed) || warning.emitWarning(warnSettings)) {
- val annotWarn = if (callee.annotatedInline) " is annotated @inline but" else ""
- val msg = s"${BackendReporting.methodSignature(callee.calleeDeclarationClass.internalName, callee.callee)}$annotWarn could not be inlined:\n$warning"
- backendReporting.inlinerWarning(request.callsitePosition, msg)
+ // Inlining a method can create unreachable code. Example:
+ // def f = throw e
+ // def g = f; println() // println is unreachable after inlining f
+ // If we have an inline request for a call to g, and f has been already inlined into g, we
+ // need to run DCE before inlining g.
+ eliminateUnreachableCodeAndUpdateCallGraph(callee.callee, callee.calleeDeclarationClass.internalName)
+
+ // DCE above removes unreachable callsites from the call graph. If the inlining request denotes
+ // such an eliminated callsite, do nothing.
+ if (callGraph.callsites contains request.callsiteInstruction) {
+ val r = inline(request.callsiteInstruction, request.callsiteStackHeight, request.callsiteMethod, request.callsiteClass,
+ callee.callee, callee.calleeDeclarationClass,
+ receiverKnownNotNull = false, keepLineNumbers = false)
+
+ for (warning <- r) {
+ if ((callee.annotatedInline && btypes.compilerSettings.YoptWarningEmitAtInlineFailed) || warning.emitWarning(compilerSettings)) {
+ val annotWarn = if (callee.annotatedInline) " is annotated @inline but" else ""
+ val msg = s"${BackendReporting.methodSignature(callee.calleeDeclarationClass.internalName, callee.callee)}$annotWarn could not be inlined:\n$warning"
+ backendReporting.inlinerWarning(request.callsitePosition, msg)
+ }
}
}
}
@@ -75,7 +93,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
val res = doInlineCallsite(callsite)
if (!res) {
- if (annotatedInline && btypes.warnSettings.atInlineFailed) {
+ if (annotatedInline && 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"
@@ -86,7 +104,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
backendReporting.inlinerWarning(pos, s"$initMsg: the method is not final and may be overridden." + warnMsg)
else
backendReporting.inlinerWarning(pos, s"$initMsg." + warnMsg)
- } else if (warning.isDefined && warning.get.emitWarning(warnSettings)) {
+ } else if (warning.isDefined && warning.get.emitWarning(compilerSettings)) {
// when annotatedInline is false, and there is some warning, the callsite metadata is possibly incomplete.
backendReporting.inlinerWarning(pos, s"there was a problem determining if method ${callee.name} can be inlined: \n"+ warning.get)
}
@@ -95,7 +113,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
res
case Callsite(ins, _, _, Left(warning), _, _, pos) =>
- if (warning.emitWarning(warnSettings))
+ if (warning.emitWarning(compilerSettings))
backendReporting.inlinerWarning(pos, s"failed to determine if ${ins.name} should be inlined:\n$warning")
false
}).toList
@@ -106,7 +124,8 @@ class Inliner[BT <: BTypes](val btypes: BT) {
*/
def doInlineCallsite(callsite: Callsite): Boolean = callsite match {
case Callsite(_, _, _, Right(Callee(callee, calleeDeclClass, safeToInline, _, annotatedInline, _, warning)), _, _, pos) =>
- annotatedInline && safeToInline
+ if (compilerSettings.YoptInlineHeuristics.value == "everything") safeToInline
+ else annotatedInline && safeToInline
case _ => false
}
@@ -167,6 +186,8 @@ class Inliner[BT <: BTypes](val btypes: BT) {
// VerifyError. We run a `SourceInterpreter` to find all producer instructions of the
// receiver value and add a cast to the self type after each.
if (!selfTypeOk) {
+ // there's no need to run eliminateUnreachableCode here. building the call graph does that
+ // already, no code can become unreachable in the meantime.
val analyzer = new AsmAnalyzer(callsite.callsiteMethod, callsite.callsiteClass.internalName, new SourceInterpreter)
val receiverValue = analyzer.frameAt(callsite.callsiteInstruction).peekDown(traitMethodArgumentTypes.length)
for (i <- receiverValue.insns.asScala) {
@@ -311,6 +332,7 @@ class Inliner[BT <: BTypes](val btypes: BT) {
val localVarShift = callsiteMethod.maxLocals
clonedInstructions.iterator.asScala foreach {
case varInstruction: VarInsnNode => varInstruction.`var` += localVarShift
+ case iinc: IincInsnNode => iinc.`var` += localVarShift
case _ => ()
}
@@ -433,6 +455,9 @@ class Inliner[BT <: BTypes](val btypes: BT) {
// Remove the elided invocation from the call graph
callGraph.callsites.remove(callsiteInstruction)
+ // Inlining a method body can render some code unreachable, see example above (in runInliner).
+ unreachableCodeEliminated -= callsiteMethod
+
callsiteMethod.maxLocals += returnType.getSize + callee.maxLocals
callsiteMethod.maxStack = math.max(callsiteMethod.maxStack, callee.maxStack + callsiteStackHeight)
@@ -472,10 +497,18 @@ class Inliner[BT <: BTypes](val btypes: BT) {
callsiteStackHeight > expectedArgs
}
- if (isSynchronizedMethod(callee)) {
+ if (codeSizeOKForInlining(callsiteMethod, callee)) {
+ Some(ResultingMethodTooLarge(
+ calleeDeclarationClass.internalName, callee.name, callee.desc,
+ callsiteClass.internalName, callsiteMethod.name, callsiteMethod.desc))
+ } else if (isSynchronizedMethod(callee)) {
// Could be done by locking on the receiver, wrapping the inlined code in a try and unlocking
// in finally. But it's probably not worth the effort, scala never emits synchronized methods.
Some(SynchronizedMethod(calleeDeclarationClass.internalName, callee.name, callee.desc))
+ } else if (isStrictfpMethod(callsiteMethod) != isStrictfpMethod(callee)) {
+ Some(StrictfpMismatch(
+ calleeDeclarationClass.internalName, callee.name, callee.desc,
+ callsiteClass.internalName, callsiteMethod.name, callsiteMethod.desc))
} else if (!callee.tryCatchBlocks.isEmpty && stackHasNonParameters) {
Some(MethodWithHandlerCalledOnNonEmptyStack(
calleeDeclarationClass.internalName, callee.name, callee.desc,
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
index f6cfc5598b..5f51a94673 100644
--- a/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
+++ b/src/compiler/scala/tools/nsc/backend/jvm/opt/LocalOpt.scala
@@ -14,7 +14,6 @@ import scala.tools.asm.tree._
import scala.collection.convert.decorateAsScala._
import scala.tools.nsc.backend.jvm.BTypes.InternalName
import scala.tools.nsc.backend.jvm.opt.BytecodeUtils._
-import scala.tools.nsc.settings.ScalaSettings
/**
* Optimizations within a single method.
@@ -47,18 +46,9 @@ import scala.tools.nsc.settings.ScalaSettings
* stale labels
* - eliminate labels that are not referenced, merge sequences of label definitions.
*/
-class LocalOpt(settings: ScalaSettings) {
- /**
- * Remove unreachable code from all methods of `classNode`. See of its overload.
- *
- * @param classNode The class to optimize
- * @return `true` if unreachable code was removed from any method
- */
- def minimalRemoveUnreachableCode(classNode: ClassNode): Boolean = {
- classNode.methods.asScala.foldLeft(false) {
- case (changed, method) => minimalRemoveUnreachableCode(method, classNode.name) || changed
- }
- }
+class LocalOpt[BT <: BTypes](val btypes: BT) {
+ import LocalOptImpls._
+ import btypes._
/**
* Remove unreachable code from a method.
@@ -66,25 +56,30 @@ class LocalOpt(settings: ScalaSettings) {
* This implementation only removes instructions that are unreachable for an ASM analyzer /
* interpreter. This ensures that future analyses will not produce `null` frames. The inliner
* and call graph builder depend on this property.
+ *
+ * @return A set containing the eliminated instructions
*/
- def minimalRemoveUnreachableCode(method: MethodNode, ownerClassName: InternalName): Boolean = {
- if (method.instructions.size == 0) return false // fast path for abstract methods
+ def minimalRemoveUnreachableCode(method: MethodNode, ownerClassName: InternalName): Set[AbstractInsnNode] = {
+ if (method.instructions.size == 0) return Set.empty // fast path for abstract methods
+ if (unreachableCodeEliminated(method)) return Set.empty // we know there is no unreachable code
// For correctness, after removing unreachable code, we have to eliminate empty exception
// handlers, see scaladoc of def methodOptimizations. Removing an live handler may render more
// code unreachable and therefore requires running another round.
- def removalRound(): Boolean = {
- val (codeRemoved, liveLabels) = removeUnreachableCodeImpl(method, ownerClassName)
- if (codeRemoved) {
+ def removalRound(): Set[AbstractInsnNode] = {
+ val (removedInstructions, liveLabels) = removeUnreachableCodeImpl(method, ownerClassName)
+ val removedRecursively = if (removedInstructions.nonEmpty) {
val liveHandlerRemoved = removeEmptyExceptionHandlers(method).exists(h => liveLabels(h.start))
if (liveHandlerRemoved) removalRound()
- }
- codeRemoved
+ else Set.empty
+ } else Set.empty
+ removedInstructions ++ removedRecursively
}
- val codeRemoved = removalRound()
- if (codeRemoved) removeUnusedLocalVariableNodes(method)()
- codeRemoved
+ val removedInstructions = removalRound()
+ if (removedInstructions.nonEmpty) removeUnusedLocalVariableNodes(method)()
+ unreachableCodeEliminated += method
+ removedInstructions
}
/**
@@ -95,7 +90,7 @@ class LocalOpt(settings: ScalaSettings) {
* @return `true` if unreachable code was eliminated in some method, `false` otherwise.
*/
def methodOptimizations(clazz: ClassNode): Boolean = {
- !settings.YoptNone && clazz.methods.asScala.foldLeft(false) {
+ !compilerSettings.YoptNone && clazz.methods.asScala.foldLeft(false) {
case (changed, method) => methodOptimizations(method, clazz.name) || changed
}
}
@@ -144,15 +139,15 @@ class LocalOpt(settings: ScalaSettings) {
def removalRound(): Boolean = {
// unreachable-code, empty-handlers and simplify-jumps run until reaching a fixpoint (see doc on class LocalOpt)
- val (codeRemoved, handlersRemoved, liveHandlerRemoved) = if (settings.YoptUnreachableCode) {
- val (codeRemoved, liveLabels) = removeUnreachableCodeImpl(method, ownerClassName)
+ val (codeRemoved, handlersRemoved, liveHandlerRemoved) = if (compilerSettings.YoptUnreachableCode) {
+ val (removedInstructions, liveLabels) = removeUnreachableCodeImpl(method, ownerClassName)
val removedHandlers = removeEmptyExceptionHandlers(method)
- (codeRemoved, removedHandlers.nonEmpty, removedHandlers.exists(h => liveLabels(h.start)))
+ (removedInstructions.nonEmpty, removedHandlers.nonEmpty, removedHandlers.exists(h => liveLabels(h.start)))
} else {
(false, false, false)
}
- val jumpsChanged = if (settings.YoptSimplifyJumps) simplifyJumps(method) else false
+ val jumpsChanged = if (compilerSettings.YoptSimplifyJumps) simplifyJumps(method) else false
// Eliminating live handlers and simplifying jump instructions may render more code
// unreachable, so we need to run another round.
@@ -165,13 +160,13 @@ class LocalOpt(settings: ScalaSettings) {
// (*) Removing stale local variable descriptors is required for correctness of unreachable-code
val localsRemoved =
- if (settings.YoptCompactLocals) compactLocalVariables(method) // also removes unused
- else if (settings.YoptUnreachableCode) removeUnusedLocalVariableNodes(method)() // (*)
+ if (compilerSettings.YoptCompactLocals) compactLocalVariables(method) // also removes unused
+ else if (compilerSettings.YoptUnreachableCode) removeUnusedLocalVariableNodes(method)() // (*)
else false
- val lineNumbersRemoved = if (settings.YoptEmptyLineNumbers) removeEmptyLineNumbers(method) else false
+ val lineNumbersRemoved = if (compilerSettings.YoptEmptyLineNumbers) removeEmptyLineNumbers(method) else false
- val labelsRemoved = if (settings.YoptEmptyLabels) removeEmptyLabelNodes(method) else false
+ val labelsRemoved = if (compilerSettings.YoptEmptyLabels) removeEmptyLabelNodes(method) else false
// assert that local variable annotations are empty (we don't emit them) - otherwise we'd have
// to eliminate those covering an empty range, similar to removeUnusedLocalVariableNodes.
@@ -179,15 +174,22 @@ class LocalOpt(settings: ScalaSettings) {
assert(nullOrEmpty(method.visibleLocalVariableAnnotations), method.visibleLocalVariableAnnotations)
assert(nullOrEmpty(method.invisibleLocalVariableAnnotations), method.invisibleLocalVariableAnnotations)
+ unreachableCodeEliminated += method
+
codeHandlersOrJumpsChanged || localsRemoved || lineNumbersRemoved || labelsRemoved
}
+}
+
+object LocalOptImpls {
/**
* Removes unreachable basic blocks.
*
* TODO: rewrite, don't use computeMaxLocalsMaxStack (runs a ClassWriter) / Analyzer. Too slow.
+ *
+ * @return A set containing eliminated instructions, and a set containing all live label nodes.
*/
- def removeUnreachableCodeImpl(method: MethodNode, ownerClassName: InternalName): (Boolean, Set[LabelNode]) = {
+ def removeUnreachableCodeImpl(method: MethodNode, ownerClassName: InternalName): (Set[AbstractInsnNode], Set[LabelNode]) = {
// The data flow analysis requires the maxLocals / maxStack fields of the method to be computed.
computeMaxLocalsMaxStack(method)
val a = new Analyzer(new BasicInterpreter)
@@ -197,6 +199,7 @@ class LocalOpt(settings: ScalaSettings) {
val initialSize = method.instructions.size
var i = 0
var liveLabels = Set.empty[LabelNode]
+ var removedInstructions = Set.empty[AbstractInsnNode]
val itr = method.instructions.iterator()
while (itr.hasNext) {
itr.next() match {
@@ -209,11 +212,12 @@ class LocalOpt(settings: ScalaSettings) {
// Instruction iterators allow removing during iteration.
// Removing is O(1): instructions are doubly linked list elements.
itr.remove()
+ removedInstructions += ins
}
}
i += 1
}
- (method.instructions.size != initialSize, liveLabels)
+ (removedInstructions, liveLabels)
}
/**
diff --git a/src/compiler/scala/tools/nsc/plugins/Plugin.scala b/src/compiler/scala/tools/nsc/plugins/Plugin.scala
index 5a7a0df595..1a5529140c 100644
--- a/src/compiler/scala/tools/nsc/plugins/Plugin.scala
+++ b/src/compiler/scala/tools/nsc/plugins/Plugin.scala
@@ -60,13 +60,15 @@ abstract class Plugin {
* @return true to continue, or false to opt out
*/
def init(options: List[String], error: String => Unit): Boolean = {
- if (!options.isEmpty) error(s"Error: $name takes no options")
+ // call to deprecated method required here, we must continue to support
+ // code that subclasses that override `processOptions`.
+ processOptions(options, error)
true
}
@deprecated("use Plugin#init instead", since="2.11")
def processOptions(options: List[String], error: String => Unit): Unit = {
- init(options, error)
+ if (!options.isEmpty) error(s"Error: $name takes no options")
}
/** A description of this plugin's options, suitable as a response
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
index d273995e6e..f217d21c35 100644
--- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala
@@ -139,6 +139,18 @@ trait ScalaSettings extends AbsScalaSettings
val XnoPatmatAnalysis = BooleanSetting ("-Xno-patmat-analysis", "Don't perform exhaustivity/unreachability analysis. Also, ignore @switch annotation.")
val XfullLubs = BooleanSetting ("-Xfull-lubs", "Retains pre 2.10 behavior of less aggressive truncation of least upper bounds.")
+ // XML parsing options
+ object XxmlSettings extends MultiChoiceEnumeration {
+ val coalescing = Choice("coalescing", "Convert PCData to Text and coalesce sibling nodes")
+ def isCoalescing = Xxml contains coalescing
+ }
+ val Xxml = MultiChoiceSetting(
+ name = "-Xxml",
+ helpArg = "property",
+ descr = "Configure XML parsing",
+ domain = XxmlSettings
+ )
+
/** Compatibility stubs for options whose value name did
* not previously match the option name.
*/
@@ -256,6 +268,13 @@ trait ScalaSettings extends AbsScalaSettings
def YoptInlineGlobal = Yopt.contains(YoptChoices.inlineGlobal)
def YoptInlinerEnabled = YoptInlineProject || YoptInlineGlobal
+ val YoptInlineHeuristics = ChoiceSetting(
+ name = "-Yopt-inline-heuristics",
+ helpArg = "strategy",
+ descr = "Set the heuristics for inlining decisions.",
+ choices = List("at-inline-annotated", "everything"),
+ default = "at-inline-annotated")
+
object YoptWarningsChoices extends MultiChoiceEnumeration {
val none = Choice("none" , "No optimizer warnings.")
val atInlineFailedSummary = Choice("at-inline-failed-summary" , "One-line summary if there were @inline method calls that could not be inlined.")
@@ -267,7 +286,7 @@ trait ScalaSettings extends AbsScalaSettings
val YoptWarnings = MultiChoiceSetting(
name = "-Yopt-warnings",
- helpArg = "warnings",
+ helpArg = "warning",
descr = "Enable optimizer warnings",
domain = YoptWarningsChoices,
default = Some(List(YoptWarningsChoices.atInlineFailed.name))) withPostSetHook (self => {
@@ -275,6 +294,15 @@ trait ScalaSettings extends AbsScalaSettings
else YinlinerWarnings.value = true
})
+ def YoptWarningEmitAtInlineFailed =
+ !YoptWarnings.isSetByUser ||
+ YoptWarnings.contains(YoptWarningsChoices.atInlineFailedSummary) ||
+ YoptWarnings.contains(YoptWarningsChoices.atInlineFailed)
+
+ def YoptWarningNoInlineMixed = YoptWarnings.contains(YoptWarningsChoices.noInlineMixed)
+ def YoptWarningNoInlineMissingBytecode = YoptWarnings.contains(YoptWarningsChoices.noInlineMissingBytecode)
+ def YoptWarningNoInlineMissingScalaInlineInfoAttr = YoptWarnings.contains(YoptWarningsChoices.noInlineMissingScalaInlineInfoAttr)
+
private def removalIn212 = "This flag is scheduled for removal in 2.12. If you have a case where you need this flag then please report a bug."
object YstatisticsPhases extends MultiChoiceEnumeration { val parser, typer, patmat, erasure, cleanup, jvm = Value }
@@ -345,12 +373,7 @@ trait ScalaSettings extends AbsScalaSettings
/** Test whether this is scaladoc we're looking at */
def isScaladoc = false
- /**
- * Helper utilities for use by checkConflictingSettings()
- */
- def isBCodeActive = !isICodeAskedFor
- def isBCodeAskedFor = (Ybackend.value != "GenASM")
- def isICodeAskedFor = ((Ybackend.value == "GenASM") || optimiseSettings.exists(_.value) || writeICode.isSetByUser)
+ def isBCodeActive = Ybackend.value == "GenBCode"
object MacroExpand {
val None = "none"
diff --git a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
index 1ffa064b78..994bcd8359 100644
--- a/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
+++ b/src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
@@ -15,6 +15,7 @@ import scala.collection.mutable.{ ListBuffer, ArrayBuffer }
import scala.annotation.switch
import scala.reflect.internal.{ JavaAccFlags }
import scala.reflect.internal.pickling.{PickleBuffer, ByteCodecs}
+import scala.reflect.io.NoAbstractFile
import scala.tools.nsc.io.AbstractFile
import scala.tools.nsc.util.ClassFileLookup
@@ -1022,11 +1023,18 @@ abstract class ClassfileParser {
val sflags = jflags.toScalaFlags
val owner = ownerForFlags(jflags)
val scope = getScope(jflags)
- val innerClass = owner.newClass(name.toTypeName, NoPosition, sflags) setInfo completer
- val innerModule = owner.newModule(name.toTermName, NoPosition, sflags) setInfo completer
+ def newStub(name: Name) =
+ owner.newStubSymbol(name, s"Class file for ${entry.externalName} not found").setFlag(JAVA)
- innerModule.moduleClass setInfo loaders.moduleClassLoader
- List(innerClass, innerModule.moduleClass) foreach (_.associatedFile = file)
+ val (innerClass, innerModule) = if (file == NoAbstractFile) {
+ (newStub(name.toTypeName), newStub(name.toTermName))
+ } else {
+ val cls = owner.newClass(name.toTypeName, NoPosition, sflags) setInfo completer
+ val mod = owner.newModule(name.toTermName, NoPosition, sflags) setInfo completer
+ mod.moduleClass setInfo loaders.moduleClassLoader
+ List(cls, mod.moduleClass) foreach (_.associatedFile = file)
+ (cls, mod)
+ }
scope enter innerClass
scope enter innerModule
@@ -1046,10 +1054,8 @@ abstract class ClassfileParser {
for (entry <- innerClasses.entries) {
// create a new class member for immediate inner classes
if (entry.outerName == currentClass) {
- val file = classFileLookup.findClassFile(entry.externalName.toString) getOrElse {
- throw new AssertionError(s"Class file for ${entry.externalName} not found")
- }
- enterClassAndModule(entry, file)
+ val file = classFileLookup.findClassFile(entry.externalName.toString)
+ enterClassAndModule(entry, file.getOrElse(NoAbstractFile))
}
}
}
diff --git a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
index 3591372bbe..79776485de 100644
--- a/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
+++ b/src/compiler/scala/tools/nsc/transform/AddInterfaces.scala
@@ -207,7 +207,7 @@ abstract class AddInterfaces extends InfoTransform { self: Erasure =>
}
def transformMixinInfo(tp: Type): Type = tp match {
- case ClassInfoType(parents, decls, clazz) =>
+ case ClassInfoType(parents, decls, clazz) if clazz.isPackageClass || !clazz.isJavaDefined =>
if (clazz.needsImplClass)
implClass(clazz setFlag lateINTERFACE) // generate an impl class
diff --git a/src/compiler/scala/tools/nsc/transform/Erasure.scala b/src/compiler/scala/tools/nsc/transform/Erasure.scala
index facce9062b..9fdc3a9d72 100644
--- a/src/compiler/scala/tools/nsc/transform/Erasure.scala
+++ b/src/compiler/scala/tools/nsc/transform/Erasure.scala
@@ -188,14 +188,16 @@ abstract class Erasure extends AddInterfaces
/* Drop redundant types (ones which are implemented by some other parent) from the immediate parents.
* This is important on Android because there is otherwise an interface explosion.
*/
- def minimizeParents(parents: List[Type]): List[Type] = {
- var rest = parents
- var leaves = collection.mutable.ListBuffer.empty[Type]
+ def minimizeParents(parents: List[Type]): List[Type] = if (parents.isEmpty) parents else {
+ def isInterfaceOrTrait(sym: Symbol) = sym.isInterface || sym.isTrait
+
+ var rest = parents.tail
+ var leaves = collection.mutable.ListBuffer.empty[Type] += parents.head
while(rest.nonEmpty) {
val candidate = rest.head
val nonLeaf = leaves exists { t => t.typeSymbol isSubClass candidate.typeSymbol }
if(!nonLeaf) {
- leaves = leaves filterNot { t => candidate.typeSymbol isSubClass t.typeSymbol }
+ leaves = leaves filterNot { t => isInterfaceOrTrait(t.typeSymbol) && (candidate.typeSymbol isSubClass t.typeSymbol) }
leaves += candidate
}
rest = rest.tail
diff --git a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
index 5e2fe21eec..d1be1558b9 100644
--- a/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
+++ b/src/compiler/scala/tools/nsc/transform/LambdaLift.scala
@@ -376,7 +376,7 @@ abstract class LambdaLift extends InfoTransform {
private def addFreeArgs(pos: Position, sym: Symbol, args: List[Tree]) = {
free get sym match {
- case Some(fvs) => args ++ (fvs.toList map (fv => atPos(pos)(proxyRef(fv))))
+ case Some(fvs) => addFree(sym, free = fvs.toList map (fv => atPos(pos)(proxyRef(fv))), original = args)
case _ => args
}
}
@@ -388,9 +388,9 @@ abstract class LambdaLift extends InfoTransform {
case DefDef(_, _, _, vparams :: _, _, _) =>
val addParams = cloneSymbols(ps).map(_.setFlag(PARAM))
sym.updateInfo(
- lifted(MethodType(sym.info.params ::: addParams, sym.info.resultType)))
+ lifted(MethodType(addFree(sym, free = addParams, original = sym.info.params), sym.info.resultType)))
- copyDefDef(tree)(vparamss = List(vparams ++ freeParams))
+ copyDefDef(tree)(vparamss = List(addFree(sym, free = freeParams, original = vparams)))
case ClassDef(_, _, _, _) =>
// SI-6231
// Disabled attempt to to add getters to freeParams
@@ -571,4 +571,12 @@ abstract class LambdaLift extends InfoTransform {
}
} // class LambdaLifter
+ private def addFree[A](sym: Symbol, free: List[A], original: List[A]): List[A] = {
+ val prependFree = (
+ !sym.isConstructor // this condition is redundant for now. It will be needed if we remove the second condition in 2.12.x
+ && (settings.Ydelambdafy.value == "method" && sym.isDelambdafyTarget) // SI-8359 Makes the lambda body a viable as the target MethodHandle for a call to LambdaMetafactory
+ )
+ if (prependFree) free ::: original
+ else original ::: free
+ }
}
diff --git a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
index db81eecdf5..e0d96df062 100644
--- a/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
+++ b/src/compiler/scala/tools/nsc/typechecker/SuperAccessors.scala
@@ -322,7 +322,7 @@ abstract class SuperAccessors extends transform.Transform with transform.TypingT
case Super(_, mix) =>
if (sym.isValue && !sym.isMethod || sym.hasAccessorFlag) {
if (!settings.overrideVars)
- reporter.error(tree.pos, "super may be not be used on " + sym.accessedOrSelf)
+ reporter.error(tree.pos, "super may not be used on " + sym.accessedOrSelf)
} else if (isDisallowed(sym)) {
reporter.error(tree.pos, "super not allowed here: use this." + name.decode + " instead")
}
diff --git a/src/library/scala/collection/TraversableLike.scala b/src/library/scala/collection/TraversableLike.scala
index 32d31f0be8..96374ef653 100644
--- a/src/library/scala/collection/TraversableLike.scala
+++ b/src/library/scala/collection/TraversableLike.scala
@@ -54,7 +54,7 @@ import scala.language.higherKinds
* `HashMap` of objects. The traversal order for hash maps will
* depend on the hash codes of its elements, and these hash codes might
* differ from one run to the next. By contrast, a `LinkedHashMap`
- * is ordered because it's `foreach` method visits elements in the
+ * is ordered because its `foreach` method visits elements in the
* order they were inserted into the `HashMap`.
*
* @author Martin Odersky
diff --git a/src/library/scala/collection/TraversableOnce.scala b/src/library/scala/collection/TraversableOnce.scala
index 2eab58009c..c5b0d0f085 100644
--- a/src/library/scala/collection/TraversableOnce.scala
+++ b/src/library/scala/collection/TraversableOnce.scala
@@ -128,8 +128,21 @@ trait TraversableOnce[+A] extends Any with GenTraversableOnce[A] {
* @example `Seq("a", 1, 5L).collectFirst({ case x: Int => x*10 }) = Some(10)`
*/
def collectFirst[B](pf: PartialFunction[A, B]): Option[B] = {
- // make sure to use an iterator or `seq`
- self.toIterator.foreach(pf.runWith(b => return Some(b)))
+ // TODO 2.12 -- move out alternate implementations into child classes
+ val i: Iterator[A] = self match {
+ case it: Iterator[A] => it
+ case _: GenIterable[_] => self.toIterator // If it might be parallel, be sure to .seq or use iterator!
+ case _ => // Not parallel, not iterable--just traverse
+ self.foreach(pf.runWith(b => return Some(b)))
+ return None
+ }
+ // Presumably the fastest way to get in and out of a partial function is for a sentinel function to return itself
+ // (Tested to be lower-overhead than runWith. Would be better yet to not need to (formally) allocate it--change in 2.12.)
+ val sentinel: Function1[A, Any] = new scala.runtime.AbstractFunction1[A, Any]{ def apply(a: A) = this }
+ while (i.hasNext) {
+ val x = pf.applyOrElse(i.next, sentinel)
+ if (x.asInstanceOf[AnyRef] ne sentinel) return Some(x.asInstanceOf[B])
+ }
None
}
diff --git a/src/partest-extras/scala/tools/partest/ParserTest.scala b/src/partest-extras/scala/tools/partest/ParserTest.scala
new file mode 100644
index 0000000000..e4c92e3dc3
--- /dev/null
+++ b/src/partest-extras/scala/tools/partest/ParserTest.scala
@@ -0,0 +1,21 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2014 LAMP/EPFL
+ */
+
+package scala.tools.partest
+
+/** A class for testing parser output.
+ * Just supply the `code` and update the check file.
+ */
+abstract class ParserTest extends DirectTest {
+
+ override def extraSettings: String = "-usejavacp -Ystop-after:parser -Xprint:parser"
+
+ override def show(): Unit = {
+ // redirect err to out, for logging
+ val prevErr = System.err
+ System.setErr(System.out)
+ compile()
+ System.setErr(prevErr)
+ }
+}
diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala
index d85ec22a84..4a39712ad7 100644
--- a/src/reflect/scala/reflect/internal/Symbols.scala
+++ b/src/reflect/scala/reflect/internal/Symbols.scala
@@ -794,6 +794,7 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
final def isAnonymousFunction = isSynthetic && (name containsName tpnme.ANON_FUN_NAME)
final def isDelambdafyFunction = isSynthetic && (name containsName tpnme.DELAMBDAFY_LAMBDA_CLASS_NAME)
+ final def isDelambdafyTarget = isSynthetic && isMethod && (name containsName tpnme.ANON_FUN_NAME)
final def isDefinedInPackage = effectiveOwner.isPackageClass
final def needsFlatClasses = phase.flatClasses && rawowner != NoSymbol && !rawowner.isPackageClass
diff --git a/src/reflect/scala/reflect/runtime/JavaMirrors.scala b/src/reflect/scala/reflect/runtime/JavaMirrors.scala
index 6efac6d873..237efd004f 100644
--- a/src/reflect/scala/reflect/runtime/JavaMirrors.scala
+++ b/src/reflect/scala/reflect/runtime/JavaMirrors.scala
@@ -1285,16 +1285,12 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive
jclazz getDeclaredConstructor (effectiveParamClasses: _*)
}
- private def jArrayClass(elemClazz: jClass[_]): jClass[_] = {
- jArray.newInstance(elemClazz, 0).getClass
- }
-
/** The Java class that corresponds to given Scala type.
* Pre: Scala type is already transformed to Java level.
*/
def typeToJavaClass(tpe: Type): jClass[_] = tpe match {
case ExistentialType(_, rtpe) => typeToJavaClass(rtpe)
- case TypeRef(_, ArrayClass, List(elemtpe)) => jArrayClass(typeToJavaClass(elemtpe))
+ case TypeRef(_, ArrayClass, List(elemtpe)) => ScalaRunTime.arrayClass(typeToJavaClass(elemtpe))
case TypeRef(_, sym: ClassSymbol, _) => classToJava(sym.asClass)
case tpe @ TypeRef(_, sym: AliasTypeSymbol, _) => typeToJavaClass(tpe.dealias)
case SingleType(_, sym: ModuleSymbol) => classToJava(sym.moduleClass.asClass)
diff --git a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala
index d31b877262..fb4ed34571 100755
--- a/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/base/CommentFactoryBase.scala
@@ -281,13 +281,16 @@ trait CommentFactoryBase { this: MemberLookupBase =>
parse0(docBody, tags + (key -> value), Some(key), ls, inCodeBlock)
case line :: ls if (lastTagKey.isDefined) =>
- val key = lastTagKey.get
- val value =
- ((tags get key): @unchecked) match {
- case Some(b :: bs) => (b + endOfLine + line) :: bs
- case None => oops("lastTagKey set when no tag exists for key")
- }
- parse0(docBody, tags + (key -> value), lastTagKey, ls, inCodeBlock)
+ val newtags = if (!line.isEmpty) {
+ val key = lastTagKey.get
+ val value =
+ ((tags get key): @unchecked) match {
+ case Some(b :: bs) => (b + endOfLine + line) :: bs
+ case None => oops("lastTagKey set when no tag exists for key")
+ }
+ tags + (key -> value)
+ } else tags
+ parse0(docBody, newtags, lastTagKey, ls, inCodeBlock)
case line :: ls =>
if (docBody.length > 0) docBody append endOfLine
@@ -315,18 +318,18 @@ trait CommentFactoryBase { this: MemberLookupBase =>
val bodyTags: mutable.Map[TagKey, List[Body]] =
mutable.Map(tagsWithoutDiagram mapValues {tag => tag map (parseWikiAtSymbol(_, pos, site))} toSeq: _*)
- def oneTag(key: SimpleTagKey): Option[Body] =
+ def oneTag(key: SimpleTagKey, filterEmpty: Boolean = true): Option[Body] =
((bodyTags remove key): @unchecked) match {
- case Some(r :: rs) =>
+ case Some(r :: rs) if !(filterEmpty && r.blocks.isEmpty) =>
if (!rs.isEmpty) reporter.warning(pos, "Only one '@" + key.name + "' tag is allowed")
Some(r)
- case None => None
+ case _ => None
}
def allTags(key: SimpleTagKey): List[Body] =
- (bodyTags remove key) getOrElse Nil
+ (bodyTags remove key).getOrElse(Nil).filterNot(_.blocks.isEmpty)
- def allSymsOneTag(key: TagKey): Map[String, Body] = {
+ def allSymsOneTag(key: TagKey, filterEmpty: Boolean = true): Map[String, Body] = {
val keys: Seq[SymbolTagKey] =
bodyTags.keys.toSeq flatMap {
case stk: SymbolTagKey if (stk.name == key.name) => Some(stk)
@@ -342,11 +345,11 @@ trait CommentFactoryBase { this: MemberLookupBase =>
reporter.warning(pos, "Only one '@" + key.name + "' tag for symbol " + key.symbol + " is allowed")
(key.symbol, bs.head)
}
- Map.empty[String, Body] ++ pairs
+ Map.empty[String, Body] ++ (if (filterEmpty) pairs.filterNot(_._2.blocks.isEmpty) else pairs)
}
def linkedExceptions: Map[String, Body] = {
- val m = allSymsOneTag(SimpleTagKey("throws"))
+ val m = allSymsOneTag(SimpleTagKey("throws"), filterEmpty = false)
m.map { case (name,body) =>
val link = memberLookup(pos, name, site)
@@ -372,7 +375,7 @@ trait CommentFactoryBase { this: MemberLookupBase =>
version0 = oneTag(SimpleTagKey("version")),
since0 = oneTag(SimpleTagKey("since")),
todo0 = allTags(SimpleTagKey("todo")),
- deprecated0 = oneTag(SimpleTagKey("deprecated")),
+ deprecated0 = oneTag(SimpleTagKey("deprecated"), filterEmpty = false),
note0 = allTags(SimpleTagKey("note")),
example0 = allTags(SimpleTagKey("example")),
constructor0 = oneTag(SimpleTagKey("constructor")),
diff --git a/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala b/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala
index ce75749859..86155845b0 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/html/HtmlPage.scala
@@ -206,25 +206,42 @@ abstract class HtmlPage extends Page { thisPage =>
case tpl :: tpls => templateToHtml(tpl) ++ sep ++ templatesToHtml(tpls, sep)
}
- /** Returns the _big image name corresponding to the DocTemplate Entity (upper left icon) */
- def docEntityKindToBigImage(ety: DocTemplateEntity) =
- if (ety.isTrait && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None) "trait_to_object_big.png"
- else if (ety.isTrait) "trait_big.png"
- else if (ety.isClass && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None) "class_to_object_big.png"
- else if (ety.isClass) "class_big.png"
- else if ((ety.isAbstractType || ety.isAliasType) && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None) "type_to_object_big.png"
- else if ((ety.isAbstractType || ety.isAliasType)) "type_big.png"
- else if (ety.isObject && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None && ety.companion.get.isClass) "object_to_class_big.png"
- else if (ety.isObject && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None && ety.companion.get.isTrait) "object_to_trait_big.png"
- else if (ety.isObject && !ety.companion.isEmpty && ety.companion.get.visibility.isPublic && ety.companion.get.inSource != None && (ety.companion.get.isAbstractType || ety.companion.get.isAliasType)) "object_to_trait_big.png"
- else if (ety.isObject) "object_big.png"
- else if (ety.isPackage) "package_big.png"
- else "class_big.png" // FIXME: an entity *should* fall into one of the above categories, but AnyRef is somehow not
+ object Image extends Enumeration {
+ val Trait, Class, Type, Object, Package = Value
+ }
+
+ /** Returns the _big image name and the alt attribute
+ * corresponding to the DocTemplate Entity (upper left icon) */
+ def docEntityKindToBigImage(ety: DocTemplateEntity) = {
+ def entityToImage(e: DocTemplateEntity) =
+ if (e.isTrait) Image.Trait
+ else if (e.isClass) Image.Class
+ else if (e.isAbstractType || e.isAliasType) Image.Type
+ else if (e.isObject) Image.Object
+ else if (e.isPackage) Image.Package
+ else {
+ // FIXME: an entity *should* fall into one of the above categories,
+ // but AnyRef is somehow not
+ Image.Class
+ }
+
+ val image = entityToImage(ety)
+ val companionImage = ety.companion filter {
+ e => e.visibility.isPublic && ! e.inSource.isEmpty
+ } map { entityToImage }
+
+ (image, companionImage) match {
+ case (from, Some(to)) =>
+ ((from + "_to_" + to + "_big.png").toLowerCase, from + "/" + to)
+ case (from, None) =>
+ ((from + "_big.png").toLowerCase, from.toString)
+ }
+ }
def permalink(template: Entity, isSelf: Boolean = true): Elem =
<span class="permalink">
<a href={ memberToUrl(template, isSelf) } title="Permalink" target="_top">
- <img src={ relativeLinkTo(List("permalink.png", "lib")) } />
+ <img src={ relativeLinkTo(List("permalink.png", "lib")) } alt="Permalink" />
</a>
</span>
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 e10c54a414..c384ed7034 100644
--- a/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala
+++ b/src/scaladoc/scala/tools/nsc/doc/html/page/Template.scala
@@ -103,11 +103,13 @@ class Template(universe: doc.Universe, generator: DiagramGenerator, tpl: DocTemp
<body class={ if (tpl.isType) "type" else "value" }>
<div id="definition">
{
+ val (src, alt) = docEntityKindToBigImage(tpl)
+
tpl.companion match {
case Some(companion) if (companion.visibility.isPublic && companion.inSource != None) =>
- <a href={relativeLinkTo(companion)} title={docEntityKindToCompanionTitle(tpl)}><img src={ relativeLinkTo(List(docEntityKindToBigImage(tpl), "lib")) }/></a>
+ <a href={relativeLinkTo(companion)} title={docEntityKindToCompanionTitle(tpl)}><img alt={alt} src={ relativeLinkTo(List(src, "lib")) }/></a>
case _ =>
- <img src={ relativeLinkTo(List(docEntityKindToBigImage(tpl), "lib")) }/>
+ <img alt={alt} src={ relativeLinkTo(List(src, "lib")) }/>
}}
{ owner }
<h1>{ displayName }</h1>{
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 e129e6cf6a..e84d7c1ca6 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
@@ -210,6 +210,7 @@ dl.attributes > dd {
display: block;
padding-left: 10em;
margin-bottom: 5px;
+ min-height: 15px;
}
#template .values > h3 {
@@ -669,6 +670,7 @@ div.fullcomment dl.paramcmts > dd {
padding-left: 10px;
margin-bottom: 5px;
margin-left: 70px;
+ min-height: 15px;
}
/* Members filter tool */