From 7c8ef74bd5aeab5694c481832fb8cc42e891f828 Mon Sep 17 00:00:00 2001 From: Adriaan Moors Date: Wed, 19 Oct 2016 12:56:08 -0700 Subject: Keep `skipBlockComment` tail recursive Avoid StackOverflow on big comments. Simplify `ScaladocJavaUnitScanner` while in there. TODO: Do same for `ScaladocUnitScanner`? --- .../scala/tools/nsc/javac/JavaScanners.scala | 26 +++++++++---- .../scala/tools/nsc/doc/ScaladocAnalyzer.scala | 43 ++++++---------------- 2 files changed, 30 insertions(+), 39 deletions(-) diff --git a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala index e11ac94041..bf944f1eda 100644 --- a/src/compiler/scala/tools/nsc/javac/JavaScanners.scala +++ b/src/compiler/scala/tools/nsc/javac/JavaScanners.scala @@ -577,27 +577,37 @@ trait JavaScanners extends ast.parser.ScannersCommon { } } - protected def putCommentChar(): Unit = in.next() + // Hooks for ScaladocJavaUnitScanner + protected def beginDocComment(): Unit = {} + protected def processCommentChar(): Unit = {} + protected def finishDocComment(): Unit = {} - protected def skipBlockComment(isDoc: Boolean): Unit = in.ch match { - case SU => incompleteInputError("unclosed comment") - case '*' => putCommentChar() ; if (in.ch == '/') putCommentChar() else skipBlockComment(isDoc) - case _ => putCommentChar() ; skipBlockComment(isDoc) + final protected def putCommentChar(): Unit = { processCommentChar(); in.next() } + + @tailrec final protected def skipBlockComment(isDoc: Boolean): Unit = { + if (isDoc) beginDocComment() + + in.ch match { + case SU => incompleteInputError("unclosed comment") + case '*' => putCommentChar() ; if (in.ch == '/') putCommentChar() else skipBlockComment(isDoc) + case _ => putCommentChar() ; skipBlockComment(isDoc) + } } - protected def skipLineComment(): Unit = in.ch match { + @tailrec final protected def skipLineComment(): Unit = in.ch match { case CR | LF | SU => case _ => putCommentChar() ; skipLineComment() } - protected def skipComment(): Boolean = in.ch match { - case '/' => putCommentChar() ; skipLineComment() ; true + final protected def skipComment(): Boolean = in.ch match { + case '/' => putCommentChar() ; skipLineComment() ; finishDocComment() ; true case '*' => putCommentChar() in.ch match { case '*' => skipBlockComment(isDoc = true) case _ => skipBlockComment(isDoc = false) } + finishDocComment() true case _ => false } diff --git a/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala b/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala index d8ec7b18fd..f00af9a42f 100644 --- a/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala +++ b/src/scaladoc/scala/tools/nsc/doc/ScaladocAnalyzer.scala @@ -215,51 +215,32 @@ abstract class ScaladocSyntaxAnalyzer[G <: Global](val global: G) extends Syntax } class ScaladocJavaUnitScanner(unit: CompilationUnit) extends JavaUnitScanner(unit) { - - private var docBuffer: StringBuilder = _ - private var inDocComment = false + // When `docBuffer == null`, we're not in a doc comment. + private var docBuffer: StringBuilder = null private var docStart: Int = 0 private var lastDoc: DocComment = null - override def init() = { - docBuffer = new StringBuilder - super.init() - } - // get last doc comment def flushDoc(): DocComment = try lastDoc finally lastDoc = null - override protected def putCommentChar(): Unit = { - if (inDocComment) docBuffer append in.ch - in.next - } - - override protected def skipBlockComment(isDoc: Boolean): Unit = { - // condition is true when comment is entered the first time, - // i.e. immediately after "/*" and when current character is "*" - if (!inDocComment && isDoc) { - docBuffer append "/*" + override protected def beginDocComment(): Unit = + if (docBuffer == null) { + // Comment is entered the first time, i.e. immediately after "/*" and when current character is "*" + docBuffer = new StringBuilder("/*") docStart = currentPos.start - inDocComment = true } - super.skipBlockComment(isDoc) - } - override protected def skipComment(): Boolean = { - val skipped = super.skipComment() - if (skipped && inDocComment) { + override protected def processCommentChar(): Unit = + if (docBuffer != null) docBuffer append in.ch + + override protected def finishDocComment(): Unit = + if (docBuffer != null) { val raw = docBuffer.toString val position = Position.range(unit.source, docStart, docStart, in.cpos) lastDoc = DocComment(raw, position) signalParsedDocComment(raw, position) - docBuffer.setLength(0) // clear buffer - inDocComment = false - true - } else { - skipped + docBuffer = null } - } - } class ScaladocJavaUnitParser(unit: CompilationUnit) extends { -- cgit v1.2.3