Skip to content

Commit c4e9632

Browse files
committed
Avoid huge literal buffer
Limit the buffer size on clear so that pathological strings or comments don't permanently allocate.
1 parent 0dc07b0 commit c4e9632

File tree

1 file changed

+35
-4
lines changed

1 file changed

+35
-4
lines changed

compiler/src/dotty/tools/dotc/parsing/Scanners.scala

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,42 @@ import config.Config
1212
import config.Printers.lexical
1313
import config.Settings.Setting
1414
import Tokens._
15-
import scala.annotation.{ switch, tailrec }
15+
import scala.annotation.{switch, tailrec}
1616
import scala.collection.mutable
1717
import scala.collection.immutable.{SortedMap, BitSet}
1818
import rewrites.Rewrites.patch
1919

20+
object Cbufs {
21+
import java.lang.StringBuilder
22+
23+
private final val TargetCapacity = 256
24+
25+
opaque type Cbuf = StringBuilder
26+
object Cbuf:
27+
def apply(): Cbuf = new StringBuilder(TargetCapacity)
28+
29+
extension StringBuilderOps on (buf: Cbuf):
30+
def clear(): Unit = {
31+
if buf.capacity() > TargetCapacity then
32+
buf.setLength(TargetCapacity)
33+
buf.trimToSize()
34+
end if
35+
buf.setLength(0)
36+
}
37+
def toCharArray: Array[Char] = {
38+
val n = buf.length()
39+
val res = new Array[Char](n)
40+
buf.getChars(0, n, res, 0)
41+
res
42+
}
43+
def append(c: Char): buf.type = { buf.append(c) ; buf }
44+
def isEmpty: Boolean = buf.length() == 0
45+
def length: Int = buf.length()
46+
def last: Char = buf.charAt(buf.length() - 1)
47+
}
48+
49+
import Cbufs._
50+
2051
object Scanners {
2152

2253
/** Offset into source character array */
@@ -99,14 +130,14 @@ object Scanners {
99130

100131
/** A character buffer for literals
101132
*/
102-
protected val litBuf = new mutable.StringBuilder
133+
protected val litBuf = Cbuf()
103134

104135
/** append Unicode character to "litBuf" buffer
105136
*/
106137
protected def putChar(c: Char): Unit = litBuf.append(c)
107138

108139
/** Return buffer contents and clear */
109-
def flushBuf(buf: StringBuilder): String = {
140+
def flushBuf(buf: Cbuf): String = {
110141
val str = buf.toString
111142
buf.clear()
112143
str
@@ -198,7 +229,7 @@ object Scanners {
198229
def getDocComment(pos: Int): Option[Comment] = docstringMap.get(pos)
199230

200231
/** A buffer for comments */
201-
private val commentBuf = new mutable.StringBuilder
232+
private val commentBuf = Cbuf()
202233

203234
private def handleMigration(keyword: Token): Token =
204235
if (keyword == ERASED && !ctx.settings.YerasedTerms.value) IDENTIFIER

0 commit comments

Comments
 (0)