Skip to content

Commit ed8d95e

Browse files
committed
Reuse the buffer for classfile reading
Classfile parsing does re-enter when we're reading package objects or classfiles for things like `scala/native.class`. But for the most part the prior refactorings mean that we typically only parse a single classfile at a time, and as such we can profit from a one-element cache for the buffer to read this into.
1 parent 4554e02 commit ed8d95e

File tree

5 files changed

+168
-109
lines changed

5 files changed

+168
-109
lines changed

src/compiler/scala/tools/nsc/symtab/SymbolLoaders.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
package scala.tools.nsc
1414
package symtab
1515

16-
import classfile.ClassfileParser
16+
import classfile.{ClassfileParser, ReusableDataReader}
1717
import java.io.IOException
1818
import scala.reflect.internal.MissingRequirementError
1919
import scala.reflect.io.{AbstractFile, NoAbstractFile}
2020
import scala.tools.nsc.util.{ClassPath, ClassRepresentation}
21-
import scala.reflect.internal.util.StatisticsStatics
21+
import scala.reflect.internal.util.{ReusableInstance, StatisticsStatics}
2222

2323
/** This class ...
2424
*
@@ -294,11 +294,11 @@ abstract class SymbolLoaders {
294294
}
295295
}
296296
}
297-
297+
private val classFileDataReader: ReusableInstance[ReusableDataReader] = new ReusableInstance[ReusableDataReader](() => new ReusableDataReader())
298298
class ClassfileLoader(val classfile: AbstractFile, clazz: ClassSymbol, module: ModuleSymbol) extends SymbolLoader with FlagAssigningCompleter {
299299
private object classfileParser extends {
300300
val symbolTable: SymbolLoaders.this.symbolTable.type = SymbolLoaders.this.symbolTable
301-
} with ClassfileParser {
301+
} with ClassfileParser(classFileDataReader) {
302302
override protected def lookupMemberAtTyperPhaseIfPossible(sym: Symbol, name: Name): Symbol =
303303
SymbolLoaders.this.lookupMemberAtTyperPhaseIfPossible(sym, name)
304304
/*

src/compiler/scala/tools/nsc/symtab/classfile/ByteBufferDataReader.scala

Lines changed: 0 additions & 89 deletions
This file was deleted.

src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import scala.collection.mutable.{ArrayBuffer, ListBuffer}
2424
import scala.annotation.switch
2525
import scala.reflect.internal.JavaAccFlags
2626
import scala.reflect.internal.pickling.ByteCodecs
27+
import scala.reflect.internal.util.ReusableInstance
2728
import scala.reflect.io.{NoAbstractFile, VirtualFile}
2829
import scala.tools.nsc.util.ClassPath
2930
import scala.tools.nsc.io.AbstractFile
@@ -34,7 +35,7 @@ import scala.util.control.NonFatal
3435
* @author Martin Odersky
3536
* @version 1.0
3637
*/
37-
abstract class ClassfileParser {
38+
abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
3839
val symbolTable: SymbolTable {
3940
def settings: Settings
4041
}
@@ -143,14 +144,18 @@ abstract class ClassfileParser {
143144
def parse(file: AbstractFile, clazz: ClassSymbol, module: ModuleSymbol): Unit = {
144145
this.file = file
145146
pushBusy(clazz) {
146-
this.in = new ByteBufferDataReader(file.toByteBuffer)
147-
this.clazz = clazz
148-
this.staticModule = module
149-
this.isScala = false
150-
151-
parseHeader()
152-
this.pool = new ConstantPool
153-
parseClass()
147+
reader.using { reader =>
148+
this.in = reader.reset(file)
149+
this.clazz = clazz
150+
this.staticModule = module
151+
this.isScala = false
152+
153+
parseHeader()
154+
this.pool = new ConstantPool
155+
parseClass()
156+
pool = null
157+
in = null
158+
}
154159
}
155160
}
156161

@@ -482,11 +487,6 @@ abstract class ClassfileParser {
482487
n == "scala.runtime.Nothing$" || n == "scala.runtime.Null$"
483488
}
484489

485-
def release(): Unit = {
486-
pool = null
487-
in = null
488-
}
489-
490490
if (isScala) {
491491
() // We're done
492492
} else if (isScalaRaw && !isNothingOrNull) {
@@ -547,7 +547,6 @@ abstract class ClassfileParser {
547547
// We would also need to make sure that clazzTParams is populated before member type completers called sig2type.
548548
clazz.initialize
549549
}
550-
release()
551550
}
552551

553552
/** Add type parameters of enclosing classes */
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/*
2+
* Scala (https://www.scala-lang.org)
3+
*
4+
* Copyright EPFL and Lightbend, Inc.
5+
*
6+
* Licensed under Apache License 2.0
7+
* (http://www.apache.org/licenses/LICENSE-2.0).
8+
*
9+
* See the NOTICE file distributed with this work for
10+
* additional information regarding copyright ownership.
11+
*/
12+
13+
package scala.tools.nsc.symtab.classfile
14+
15+
import java.io.{ByteArrayInputStream, DataInputStream, InputStream}
16+
import java.nio.channels.Channels
17+
import java.nio.{BufferUnderflowException, ByteBuffer}
18+
19+
final class ReusableDataReader() extends DataReader {
20+
private[this] var data = new Array[Byte](32768)
21+
private[this] var bb: ByteBuffer = ByteBuffer.wrap(data)
22+
private[this] var size = 0
23+
private[this] val reader: DataInputStream = {
24+
val stream = new InputStream {
25+
override def read(): Int = try {
26+
bb.get & 0xff
27+
} catch {
28+
case _: BufferUnderflowException => -1
29+
}
30+
override def markSupported(): Boolean = false
31+
}
32+
new DataInputStream(stream)
33+
}
34+
35+
private def nextPositivePowerOfTwo(target: Int): Int = 1 << -Integer.numberOfLeadingZeros(target - 1)
36+
37+
def reset(file: scala.reflect.io.AbstractFile): this.type = {
38+
this.size = 0
39+
file.sizeOption match {
40+
case Some(size) =>
41+
if (size > data.length) {
42+
data = new Array[Byte](nextPositivePowerOfTwo(size))
43+
} else {
44+
java.util.Arrays.fill(data, 0.toByte)
45+
}
46+
val input = file.input
47+
try {
48+
var endOfInput = false
49+
while (!endOfInput) {
50+
val remaining = data.length - this.size
51+
if (remaining == 0) endOfInput = true
52+
else {
53+
val read = input.read(data, this.size, remaining)
54+
if (read < 0) endOfInput = true
55+
else this.size += read
56+
}
57+
}
58+
bb = ByteBuffer.wrap(data, 0, size)
59+
} finally {
60+
input.close()
61+
}
62+
case None =>
63+
val input = file.input
64+
try {
65+
var endOfInput = false
66+
while (!endOfInput) {
67+
val remaining = data.length - size
68+
if (remaining == 0) {
69+
data = java.util.Arrays.copyOf(data, nextPositivePowerOfTwo(size))
70+
}
71+
val read = input.read(data, this.size, data.length - this.size)
72+
if (read < 0) endOfInput = true
73+
else this.size += read
74+
}
75+
bb = ByteBuffer.wrap(data, 0, size)
76+
} finally {
77+
input.close()
78+
}
79+
}
80+
this
81+
}
82+
83+
@throws(classOf[IndexOutOfBoundsException])
84+
def nextByte: Byte = bb.get
85+
86+
def nextBytes(len: Int): Array[Byte] = {
87+
val result = new Array[Byte](len)
88+
reader.readFully(result)
89+
result
90+
}
91+
92+
def nextChar: Char = bb.getChar()
93+
94+
def nextInt: Int = bb.getInt()
95+
96+
def getChar(mybp: Int): Char = {
97+
bb.getChar(mybp)
98+
}
99+
100+
def getInt(mybp: Int): Int = {
101+
bb.getInt(mybp)
102+
}
103+
104+
def getLong(mybp: Int): Long = {
105+
bb.getLong(mybp)
106+
}
107+
108+
def getFloat(mybp: Int): Float = {
109+
bb.getFloat(mybp)
110+
}
111+
112+
def getDouble(mybp: Int): Double = {
113+
bb.getDouble(mybp)
114+
}
115+
116+
def skip(n: Int): Unit = {
117+
bb.position(bb.position() + n)
118+
}
119+
def bp: Int = bb.position()
120+
def bp_=(i: Int): Unit = {
121+
try {
122+
bb.position(i)
123+
} catch {
124+
case ex: IllegalArgumentException =>
125+
throw ex
126+
}
127+
}
128+
129+
def getByte(mybp: Int): Byte = {
130+
bb.get(mybp)
131+
}
132+
def getBytes(mybp: Int, bytes: Array[Byte]): Unit = {
133+
val saved = bb.position()
134+
bb.position(mybp)
135+
try reader.readFully(bytes)
136+
finally bb.position(saved)
137+
}
138+
def getUTF(mybp: Int, len: Int): String = {
139+
val saved = bb.position()
140+
val savedLimit = bb.limit()
141+
bb.position(mybp)
142+
bb.limit(mybp + len)
143+
try reader.readUTF()
144+
finally {
145+
bb.limit(savedLimit)
146+
bb.position(saved)
147+
}
148+
}
149+
}

src/reflect/scala/reflect/internal/Symbols.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import scala.collection.mutable.ListBuffer
2424
import util.{ Statistics, shortClassOfInstance, StatisticsStatics }
2525
import Flags._
2626
import scala.annotation.tailrec
27-
import scala.reflect.io.{ AbstractFile, NoAbstractFile }
27+
import scala.reflect.io.{AbstractFile, NoAbstractFile}
2828
import Variance._
2929

3030
trait Symbols extends api.Symbols { self: SymbolTable =>

0 commit comments

Comments
 (0)