Skip to content

Commit 397a177

Browse files
committed
WIP use pickles from outline typing on downstream classpath
1 parent 17fa6d8 commit 397a177

File tree

7 files changed

+109
-13
lines changed

7 files changed

+109
-13
lines changed

src/compiler/scala/tools/nsc/PipelineMain.scala

Lines changed: 91 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,17 @@
44
*/
55
package scala.tools.nsc
66

7+
import java.net.URL
8+
import java.nio.ByteBuffer
79
import java.nio.file.{Files, Path, Paths}
810

911
import scala.collection.mutable
12+
import scala.reflect.internal.pickling.PickleBuffer
1013
import scala.reflect.internal.util.FakePos
14+
import scala.reflect.io.{VirtualDirectory, VirtualFile}
15+
import scala.tools.nsc.backend.{ClassfileInfo, ScalaClass, ScalaRawClass}
16+
import scala.tools.nsc.classpath.{DirectoryClassPath, VirtualDirectoryClassPath}
17+
import scala.tools.nsc.io.AbstractFile
1118
import scala.tools.nsc.reporters.{ConsoleReporter, Reporter}
1219
import scala.tools.nsc.util.ClassPath
1320

@@ -19,7 +26,53 @@ class PipelineMainClass {
1926
}
2027

2128
private var reporter: Reporter = _
29+
30+
private class PickleClassPath[G <: Global](data: mutable.AnyRefMap[G#Symbol, PickleBuffer]) {
31+
val dir = new VirtualDirectory("fakes", None)
32+
val classpath = VirtualDirectoryClassPath(dir)
33+
val dirs = mutable.Map[G#Symbol, AbstractFile]()
34+
val classInfo = mutable.Map[AbstractFile, ClassfileInfo]()
35+
def packageDir(packSymbol: G#Symbol): AbstractFile = {
36+
if (dirs.contains(packSymbol)) dirs(packSymbol)
37+
else if (packSymbol.owner.isRoot) {
38+
val subDir = dir.subdirectoryNamed(packSymbol.encodedName)
39+
dirs.put(packSymbol, subDir)
40+
subDir
41+
} else {
42+
val base = packageDir(packSymbol.owner)
43+
val subDir = base.subdirectoryNamed(packSymbol.encodedName)
44+
dirs.put(packSymbol, subDir)
45+
subDir
46+
}
47+
}
48+
for ((symbol, pickle) <- data) {
49+
val base = packageDir(symbol.owner)
50+
if (symbol.isClass) {
51+
val primary = base.fileNamed(symbol.encodedName + ".class")
52+
classInfo(primary) = ScalaClass(symbol.fullNameString, ByteBuffer.wrap(pickle.bytes))
53+
if (symbol.companionModule.exists) {
54+
val secondary = base.fileNamed(symbol.companionModule.encodedName + "$.class")
55+
classInfo(secondary) = ScalaRawClass(symbol.companionModule.fullNameString)
56+
}
57+
} else if (symbol.isModule) {
58+
if (symbol.companionClass.exists) {
59+
val primary = base.fileNamed(symbol.encodedName + ".class")
60+
classInfo(primary) = ScalaClass(symbol.fullNameString, ByteBuffer.wrap(pickle.bytes))
61+
val secondary = base.fileNamed(symbol.companionModule.encodedName + "$.class")
62+
classInfo(secondary) = ScalaRawClass(symbol.companionModule.fullNameString)
63+
} else {
64+
val primary = base.fileNamed(symbol.encodedName + "$.class")
65+
classInfo(primary) = ScalaClass(symbol.fullNameString, ByteBuffer.wrap(pickle.bytes))
66+
}
67+
}
68+
}
69+
}
70+
private val allPickleData = new java.util.concurrent.ConcurrentHashMap[Path, PickleClassPath[_]]
71+
72+
var round = 0
73+
2274
def process(args: Array[String]): Boolean = {
75+
round = 0
2376
reporter = new ConsoleReporter(new Settings(scalacError))
2477

2578
def commandFor(argFileArg: String): Task = {
@@ -39,7 +92,6 @@ class PipelineMainClass {
3992
}
4093
var toProcess = projects.toList
4194
val done = mutable.HashSet[Task]()
42-
var round = 0
4395
while (toProcess.nonEmpty) {
4496
round += 1
4597
val (nextRound, blocked) = toProcess.partition(x => dependsOn.getOrElse(x, Nil).forall(done))
@@ -53,6 +105,7 @@ class PipelineMainClass {
53105
}
54106
true
55107
}
108+
56109
private case class Task(argsFile: String, command: CompilerCommand) {
57110
override def toString: String = argsFile
58111
}
@@ -78,7 +131,42 @@ class PipelineMainClass {
78131
!reporter.hasErrors
79132
}
80133

81-
protected def newCompiler(settings: Settings): Global = Global(settings)
134+
protected def newCompiler(settings: Settings): Global = {
135+
val g = Global(settings)
136+
137+
val plugin: g.platform.ClassPathPlugin = new g.platform.ClassPathPlugin {
138+
val replacements = mutable.Buffer[PickleClassPath[_]]()
139+
override def modifyClassPath(classPath: Seq[ClassPath]): Seq[ClassPath] = {
140+
classPath.flatMap {
141+
case dcp: DirectoryClassPath =>
142+
val path = dcp.dir.toPath.toRealPath().normalize()
143+
allPickleData.get(path) match {
144+
case null =>
145+
dcp :: Nil
146+
case pcp =>
147+
replacements += pcp
148+
pcp.classpath :: dcp :: Nil // leaving the original classpath for Java compiled files for now
149+
}
150+
case cp => cp :: Nil
151+
}
152+
}
153+
154+
override def info(file: AbstractFile, clazz: g.ClassSymbol): Option[ClassfileInfo] = {
155+
file match {
156+
case vf: VirtualFile =>
157+
if (file.name.contains("ListMap"))
158+
getClass
159+
val iterator = replacements.iterator.flatMap(_.classInfo.get(vf))
160+
if (iterator.hasNext)
161+
Some(iterator.next())
162+
else None
163+
case _ => None
164+
}
165+
}
166+
}
167+
g.platform.addClassPathPlugin(plugin)
168+
g
169+
}
82170

83171
protected def doCompile(command: CompilerCommand, compiler: Global): Unit = {
84172
if (command.files.isEmpty) {
@@ -95,17 +183,15 @@ class PipelineMainClass {
95183
println("regular compilation")
96184
command.settings.Youtline.value = false
97185
command.settings.stopAfter.value = Nil
98-
val pickles = run1.symData
99186
val compiler2 = newCompiler(command.settings)
187+
allPickleData.put(command.settings.outputDirs.getSingleOutput.get.file.toPath.toRealPath().normalize(), new PickleClassPath(run1.symData))
100188
val run2 = new compiler2.Run()
101189
run2 compile command.files
102190
compiler2.reporter.finish()
103191
}
104-
105192
}
106193
}
107194

108-
109195
def main(args: Array[String]): Unit = {
110196
for (i <- 1 to 10) {
111197
val result = process(args)

src/compiler/scala/tools/nsc/backend/Platform.scala

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,6 @@ trait Platform {
5656
def info(file: AbstractFile, clazz: ClassSymbol): Option[ClassfileInfo]
5757
def modifyClassPath(classPath: Seq[ClassPath]): Seq[ClassPath] = classPath
5858
}
59-
sealed abstract class ClassfileInfo {}
60-
final case class ClassBytes(data: ByteBuffer) extends ClassfileInfo
61-
final case class ScalaRawClass(className: String) extends ClassfileInfo
62-
final case class ScalaClass(className: String, pickle: ByteBuffer) extends ClassfileInfo
63-
6459

6560
/** A list of registered classpath plugins */
6661
private var classPathPlugins: List[ClassPathPlugin] = Nil
@@ -89,3 +84,9 @@ trait Platform {
8984
}
9085
}
9186
}
87+
88+
sealed abstract class ClassfileInfo {}
89+
final case class ClassBytes(data: ByteBuffer) extends ClassfileInfo
90+
final case class ScalaRawClass(className: String) extends ClassfileInfo
91+
final case class ScalaClass(className: String, pickle: ByteBuffer) extends ClassfileInfo
92+

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,8 @@ abstract class SymbolLoaders {
270270

271271
val classPathEntries = classPath.list(packageName)
272272

273+
if (root.name.string_==("immutable"))
274+
getClass
273275
if (!root.isRoot)
274276
for (entry <- classPathEntries.classesAndSources) initializeFromClassPath(root, entry)
275277
if (!root.isEmptyPackageClass) {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import scala.reflect.internal.JavaAccFlags
1818
import scala.reflect.internal.pickling.{ByteCodecs, PickleBuffer}
1919
import scala.reflect.io.{NoAbstractFile, VirtualFile}
2020
import scala.reflect.internal.util.Collections._
21+
import scala.tools.nsc.backend.{ClassBytes, ScalaClass, ScalaRawClass}
2122
import scala.tools.nsc.util.ClassPath
2223
import scala.tools.nsc.io.AbstractFile
2324
import scala.util.control.NonFatal

src/reflect/scala/reflect/internal/Definitions.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ trait Definitions extends api.StandardDefinitions {
282282
}
283283

284284
// top types
285-
lazy val AnyClass = enterNewClass(ScalaPackageClass, tpnme.Any, Nil, ABSTRACT) markAllCompleted
285+
lazy val AnyClass = ScalaPackageClass.newClassSymbol(tpnme.Any, NoPosition, ABSTRACT)
286286
lazy val AnyRefClass = newAlias(ScalaPackageClass, tpnme.AnyRef, ObjectTpe) markAllCompleted
287287
lazy val ObjectClass = getRequiredClass(sn.Object.toString)
288288

@@ -1451,6 +1451,8 @@ trait Definitions extends api.StandardDefinitions {
14511451
// documented in JavaUniverse.init
14521452
def init() {
14531453
if (isInitialized) return
1454+
ScalaPackageClass
1455+
(AnyClass setInfoAndEnter ClassInfoType(Nil, newScope, AnyClass)).markAllCompleted
14541456
ObjectClass.initialize
14551457
ScalaPackageClass.initialize
14561458
symbolsNotPresentInBytecode

src/reflect/scala/reflect/internal/pickling/UnPickler.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,9 @@ abstract class UnPickler {
242242
else NoSymbol
243243
}
244244

245+
if (owner == definitions.ScalaPackageClass && name == tpnme.AnyRef)
246+
return definitions.AnyRefClass
247+
245248
// (1) Try name.
246249
localDummy orElse fromName(name) orElse {
247250
// (2) Try with expanded name. Can happen if references to private

test/junit/scala/tools/nsc/classpath/ClassPluginTest.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import org.junit.runner.RunWith
1111
import org.junit.runners.JUnit4
1212

1313
import scala.reflect.io.VirtualDirectory
14+
import scala.tools.nsc.backend.{ClassfileInfo, ScalaClass}
1415
import scala.tools.nsc.io.AbstractFile
1516
import scala.tools.nsc.symtab.SymbolTableForUnitTesting
1617
import scala.tools.nsc.util.ClassPath
@@ -25,7 +26,7 @@ class ClassPluginTest extends BytecodeTesting {
2526
// ... and this one to read them with a ClassPathPlugin
2627
object symbolTable extends SymbolTableForUnitTesting {
2728
val fakeClasses = Map(
28-
"fake.C" -> platform.ScalaClass("fake.C", pickleOf("package fake; class C { def foo = 42 }"))
29+
"fake.C" -> ScalaClass("fake.C", pickleOf("package fake; class C { def foo = 42 }"))
2930
)
3031
private val fakes = new VirtualDirectory("fakes", None)
3132
fakes.subdirectoryNamed("fake").fileNamed("C.class")
@@ -36,7 +37,7 @@ class ClassPluginTest extends BytecodeTesting {
3637
VirtualDirectoryClassPath(fakes) +: classPath
3738
}
3839

39-
override def info(file: AbstractFile, clazz: ClassSymbol): Option[platform.ClassfileInfo] =
40+
override def info(file: AbstractFile, clazz: ClassSymbol): Option[ClassfileInfo] =
4041
fakeClasses.get(clazz.fullNameString)
4142
}
4243
this.platform.addClassPathPlugin(classpathPlugin)

0 commit comments

Comments
 (0)