Skip to content

Commit 124cf2f

Browse files
committed
Merge pull request scala#4176 from mpociecha/flat-classpath2
The alternative, flat representation of classpath elements
2 parents 59832b0 + 3581187 commit 124cf2f

File tree

52 files changed

+2217
-315
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+2217
-315
lines changed

bincompat-backward.whitelist.conf

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,11 @@ filter {
203203
{
204204
matchName="scala.reflect.runtime.ThreadLocalStorage#MyThreadLocalStorage.values"
205205
problemName=MissingMethodProblem
206+
},
207+
// the below method was the unused private (sic!) method but the compatibility checker was complaining about it
208+
{
209+
matchName="scala.reflect.io.ZipArchive.scala$reflect$io$ZipArchive$$walkIterator"
210+
problemName=MissingMethodProblem
206211
}
207212
]
208213
}

bincompat-forward.whitelist.conf

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,28 @@ filter {
292292
{
293293
matchName="scala.collection.immutable.Stream.scala$collection$immutable$Stream$$loop$2"
294294
problemName=MissingMethodProblem
295+
},
296+
// changes needed by ZipArchiveFileLookup (the flat classpath representation)
297+
{
298+
matchName="scala.reflect.io.FileZipArchive.allDirs"
299+
problemName=MissingMethodProblem
300+
},
301+
{
302+
matchName="scala.reflect.io.FileZipArchive.root"
303+
problemName=MissingMethodProblem
304+
},
305+
// introduced the harmless method (instead of the repeated code in several places)
306+
{
307+
matchName="scala.reflect.runtime.Settings#MultiStringSetting.valueSetByUser"
308+
problemName=MissingMethodProblem
309+
},
310+
{
311+
matchName="scala.reflect.runtime.Settings#BooleanSetting.valueSetByUser"
312+
problemName=MissingMethodProblem
313+
},
314+
{
315+
matchName="scala.reflect.runtime.Settings#IntSetting.valueSetByUser"
316+
problemName=MissingMethodProblem
295317
}
296318
]
297319
}

src/compiler/scala/reflect/macros/contexts/Infrastructure.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ trait Infrastructure {
1212

1313
def compilerSettings: List[String] = universe.settings.recreateArgs
1414

15-
def classPath: List[java.net.URL] = global.classPath.asURLs
15+
def classPath: List[java.net.URL] = global.classPath.asURLs.toList
1616
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright (c) 2014 Contributor. All rights reserved.
3+
*/
4+
package scala.tools.nsc
5+
6+
import scala.io.StdIn.readLine
7+
8+
/**
9+
* Simple application to check out amount of memory used by chosen classpath representation.
10+
* It allows us to create many scalac-like calls based on specified parameters, where each main retains Global.
11+
* And we need additional tool (e.g. profiler) to measure memory consumption itself.
12+
*/
13+
object ClassPathMemoryConsumptionTester {
14+
15+
private class TestSettings extends Settings {
16+
val requiredInstances = IntSetting("-requiredInstances",
17+
"Determine how many times classpath should be loaded", 10, Some((1, 10000)), (_: String) => None)
18+
}
19+
20+
private class MainRetainsGlobal extends scala.tools.nsc.MainClass {
21+
var retainedGlobal: Global = _
22+
override def doCompile(compiler: Global) {
23+
retainedGlobal = compiler
24+
super.doCompile(compiler)
25+
}
26+
}
27+
28+
def main(args: Array[String]): Unit = {
29+
if (args contains "-help") usage()
30+
else doTest(args)
31+
}
32+
33+
private def doTest(args: Array[String]) = {
34+
val settings = loadSettings(args.toList)
35+
36+
val mains = (1 to settings.requiredInstances.value) map (_ => new MainRetainsGlobal)
37+
38+
// we need original settings without additional params to be able to use them later
39+
val baseArgs = argsWithoutRequiredInstances(args)
40+
41+
println(s"Loading classpath ${settings.requiredInstances.value} times")
42+
val startTime = System.currentTimeMillis()
43+
44+
mains map (_.process(baseArgs))
45+
46+
val elapsed = System.currentTimeMillis() - startTime
47+
println(s"Operation finished - elapsed $elapsed ms")
48+
println("Memory consumption can be now measured")
49+
50+
var textFromStdIn = ""
51+
while (textFromStdIn.toLowerCase != "exit")
52+
textFromStdIn = readLine("Type 'exit' to close application: ")
53+
}
54+
55+
/**
56+
* Prints usage information
57+
*/
58+
private def usage(): Unit =
59+
println( """Use classpath and sourcepath options like in the case of e.g. 'scala' command.
60+
| There's also one additional option:
61+
| -requiredInstances <int value> Determine how many times classpath should be loaded
62+
""".stripMargin.trim)
63+
64+
private def loadSettings(args: List[String]) = {
65+
val settings = new TestSettings()
66+
settings.processArguments(args, processAll = true)
67+
if (settings.classpath.isDefault)
68+
settings.classpath.value = sys.props("java.class.path")
69+
settings
70+
}
71+
72+
private def argsWithoutRequiredInstances(args: Array[String]) = {
73+
val instancesIndex = args.indexOf("-requiredInstances")
74+
if (instancesIndex == -1) args
75+
else args.dropRight(args.length - instancesIndex) ++ args.drop(instancesIndex + 2)
76+
}
77+
}

src/compiler/scala/tools/nsc/GenericRunnerSettings.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@
55

66
package scala.tools.nsc
77

8-
import scala.tools.util.PathResolver
8+
import java.net.URL
9+
import scala.tools.util.PathResolverFactory
910

1011
class GenericRunnerSettings(error: String => Unit) extends Settings(error) {
11-
def classpathURLs = new PathResolver(this).asURLs
12+
def classpathURLs: Seq[URL] = PathResolverFactory.create(this).resultAsURLs
1213

1314
val howtorun =
1415
ChoiceSetting(

src/compiler/scala/tools/nsc/Global.scala

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,10 @@ import scala.compat.Platform.currentTime
1414
import scala.collection.{ mutable, immutable }
1515
import io.{ SourceReader, AbstractFile, Path }
1616
import reporters.{ Reporter, ConsoleReporter }
17-
import util.{ ClassPath, MergedClassPath, StatisticsInfo, returning, stackTraceString }
17+
import util.{ ClassFileLookup, ClassPath, MergedClassPath, StatisticsInfo, returning }
1818
import scala.reflect.ClassTag
19-
import scala.reflect.internal.util.{ OffsetPosition, SourceFile, NoSourceFile, BatchSourceFile, ScriptSourceFile }
20-
import scala.reflect.internal.pickling.{ PickleBuffer, PickleFormat }
21-
import scala.reflect.io.VirtualFile
19+
import scala.reflect.internal.util.{ SourceFile, NoSourceFile, BatchSourceFile, ScriptSourceFile }
20+
import scala.reflect.internal.pickling.PickleBuffer
2221
import symtab.{ Flags, SymbolTable, SymbolTrackers }
2322
import symtab.classfile.Pickler
2423
import plugins.Plugins
@@ -35,6 +34,8 @@ import backend.opt.{ Inliners, InlineExceptionHandlers, ConstantOptimization, Cl
3534
import backend.icode.analysis._
3635
import scala.language.postfixOps
3736
import scala.tools.nsc.ast.{TreeGen => AstTreeGen}
37+
import scala.tools.nsc.classpath.FlatClassPath
38+
import scala.tools.nsc.settings.ClassPathRepresentationType
3839

3940
class Global(var currentSettings: Settings, var reporter: Reporter)
4041
extends SymbolTable
@@ -58,7 +59,12 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
5859

5960
class GlobalMirror extends Roots(NoSymbol) {
6061
val universe: self.type = self
61-
def rootLoader: LazyType = new loaders.PackageLoader(classPath)
62+
def rootLoader: LazyType = {
63+
settings.YclasspathImpl.value match {
64+
case ClassPathRepresentationType.Flat => new loaders.PackageLoaderUsingFlatClassPath(FlatClassPath.RootPackage, flatClassPath)
65+
case ClassPathRepresentationType.Recursive => new loaders.PackageLoader(recursiveClassPath)
66+
}
67+
}
6268
override def toString = "compiler mirror"
6369
}
6470
implicit val MirrorTag: ClassTag[Mirror] = ClassTag[Mirror](classOf[GlobalMirror])
@@ -104,7 +110,14 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
104110
type PlatformClassPath = ClassPath[AbstractFile]
105111
type OptClassPath = Option[PlatformClassPath]
106112

107-
def classPath: PlatformClassPath = platform.classPath
113+
def classPath: ClassFileLookup[AbstractFile] = settings.YclasspathImpl.value match {
114+
case ClassPathRepresentationType.Flat => flatClassPath
115+
case ClassPathRepresentationType.Recursive => recursiveClassPath
116+
}
117+
118+
private def recursiveClassPath: ClassPath[AbstractFile] = platform.classPath
119+
120+
private def flatClassPath: FlatClassPath = platform.flatClassPath
108121

109122
// sub-components --------------------------------------------------
110123

@@ -319,7 +332,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
319332
None
320333
}
321334

322-
val charset = ( if (settings.encoding.isSetByUser) Some(settings.encoding.value) else None ) flatMap loadCharset getOrElse {
335+
val charset = settings.encoding.valueSetByUser flatMap loadCharset getOrElse {
323336
settings.encoding.value = defaultEncoding // A mandatory charset
324337
Charset.forName(defaultEncoding)
325338
}
@@ -334,16 +347,16 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
334347
}
335348
}
336349

337-
( if (settings.sourceReader.isSetByUser) Some(settings.sourceReader.value) else None ) flatMap loadReader getOrElse {
350+
settings.sourceReader.valueSetByUser flatMap loadReader getOrElse {
338351
new SourceReader(charset.newDecoder(), reporter)
339352
}
340353
}
341354

342-
if (settings.verbose || settings.Ylogcp) {
355+
if (settings.verbose || settings.Ylogcp)
343356
reporter.echo(
344-
s"[search path for source files: ${classPath.sourcepaths.mkString(",")}]\n"+
345-
s"[search path for class files: ${classPath.asClasspathString}")
346-
}
357+
s"[search path for source files: ${classPath.asSourcePathString}]\n" +
358+
s"[search path for class files: ${classPath.asClassPathString}]"
359+
)
347360

348361
// The current division between scala.reflect.* and scala.tools.nsc.* is pretty
349362
// clunky. It is often difficult to have a setting influence something without having
@@ -846,6 +859,9 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
846859

847860
/** Extend classpath of `platform` and rescan updated packages. */
848861
def extendCompilerClassPath(urls: URL*): Unit = {
862+
if (settings.YclasspathImpl.value == ClassPathRepresentationType.Flat)
863+
throw new UnsupportedOperationException("Flat classpath doesn't support extending the compiler classpath")
864+
849865
val newClassPath = platform.classPath.mergeUrlsIntoClassPath(urls: _*)
850866
platform.currentClassPath = Some(newClassPath)
851867
// Reload all specified jars into this compiler instance
@@ -881,8 +897,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
881897
* entries on the classpath.
882898
*/
883899
def invalidateClassPathEntries(paths: String*): Unit = {
900+
if (settings.YclasspathImpl.value == ClassPathRepresentationType.Flat)
901+
throw new UnsupportedOperationException("Flat classpath doesn't support the classpath invalidation")
902+
884903
implicit object ClassPathOrdering extends Ordering[PlatformClassPath] {
885-
def compare(a:PlatformClassPath, b:PlatformClassPath) = a.asClasspathString compare b.asClasspathString
904+
def compare(a:PlatformClassPath, b:PlatformClassPath) = a.asClassPathString compare b.asClassPathString
886905
}
887906
val invalidated, failed = new mutable.ListBuffer[ClassSymbol]
888907
classPath match {
@@ -910,10 +929,10 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
910929
informProgress(s"classpath updated on entries [${subst.keys mkString ","}]")
911930
def mkClassPath(elems: Iterable[PlatformClassPath]): PlatformClassPath =
912931
if (elems.size == 1) elems.head
913-
else new MergedClassPath(elems, classPath.context)
932+
else new MergedClassPath(elems, recursiveClassPath.context)
914933
val oldEntries = mkClassPath(subst.keys)
915934
val newEntries = mkClassPath(subst.values)
916-
mergeNewEntries(newEntries, RootClass, Some(classPath), Some(oldEntries), invalidated, failed)
935+
mergeNewEntries(newEntries, RootClass, Some(recursiveClassPath), Some(oldEntries), invalidated, failed)
917936
}
918937
}
919938
def show(msg: String, syms: scala.collection.Traversable[Symbol]) =

src/compiler/scala/tools/nsc/ObjectRunner.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,14 @@ trait CommonRunner {
1818
* @throws NoSuchMethodException
1919
* @throws InvocationTargetException
2020
*/
21-
def run(urls: List[URL], objectName: String, arguments: Seq[String]) {
21+
def run(urls: Seq[URL], objectName: String, arguments: Seq[String]) {
2222
(ScalaClassLoader fromURLs urls).run(objectName, arguments)
2323
}
2424

2525
/** Catches exceptions enumerated by run (in the case of InvocationTargetException,
2626
* unwrapping it) and returns it any thrown in Left(x).
2727
*/
28-
def runAndCatch(urls: List[URL], objectName: String, arguments: Seq[String]): Either[Throwable, Boolean] = {
28+
def runAndCatch(urls: Seq[URL], objectName: String, arguments: Seq[String]): Either[Throwable, Boolean] = {
2929
try { run(urls, objectName, arguments) ; Right(true) }
3030
catch { case e: Throwable => Left(unwrap(e)) }
3131
}

src/compiler/scala/tools/nsc/PhaseAssembly.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ trait PhaseAssembly {
199199
// Add all phases in the set to the graph
200200
val graph = phasesSetToDepGraph(phasesSet)
201201

202-
val dot = if (settings.genPhaseGraph.isSetByUser) Some(settings.genPhaseGraph.value) else None
202+
val dot = settings.genPhaseGraph.valueSetByUser
203203

204204
// Output the phase dependency graph at this stage
205205
def dump(stage: Int) = dot foreach (n => graphToDotFile(graph, s"$n-$stage.dot"))

src/compiler/scala/tools/nsc/ScriptRunner.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ package tools.nsc
88

99
import io.{ AbstractFile, Directory, File, Path }
1010
import java.io.IOException
11+
import scala.tools.nsc.classpath.DirectoryFlatClassPath
1112
import scala.tools.nsc.reporters.{Reporter,ConsoleReporter}
13+
import scala.tools.nsc.settings.ClassPathRepresentationType
14+
import scala.tools.nsc.util.ClassPath.DefaultJavaContext
1215
import util.Exceptional.unwrap
1316

1417
/** An object that runs Scala code in script files.
@@ -112,8 +115,10 @@ class ScriptRunner extends HasCompileSocket {
112115
}
113116

114117
def hasClassToRun(d: Directory): Boolean = {
115-
import util.ClassPath.{ DefaultJavaContext => ctx }
116-
val cp = ctx.newClassPath(AbstractFile.getDirectory(d))
118+
val cp = settings.YclasspathImpl.value match {
119+
case ClassPathRepresentationType.Recursive => DefaultJavaContext.newClassPath(AbstractFile.getDirectory(d))
120+
case ClassPathRepresentationType.Flat => DirectoryFlatClassPath(d.jfile)
121+
}
117122
cp.findClass(mainClass).isDefined
118123
}
119124

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

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@ package scala.tools.nsc
77
package backend
88

99
import io.AbstractFile
10-
import util.{ClassPath,MergedClassPath,DeltaClassPath}
10+
import scala.tools.nsc.classpath.FlatClassPath
11+
import scala.tools.nsc.settings.ClassPathRepresentationType
12+
import scala.tools.nsc.util.{ ClassPath, DeltaClassPath, MergedClassPath }
13+
import scala.tools.util.FlatClassPathResolver
1114
import scala.tools.util.PathResolver
1215

1316
trait JavaPlatform extends Platform {
@@ -19,10 +22,20 @@ trait JavaPlatform extends Platform {
1922
private[nsc] var currentClassPath: Option[MergedClassPath[AbstractFile]] = None
2023

2124
def classPath: ClassPath[AbstractFile] = {
25+
assert(settings.YclasspathImpl.value == ClassPathRepresentationType.Recursive,
26+
"To use recursive classpath representation you must enable it with -YclasspathImpl:recursive compiler option.")
27+
2228
if (currentClassPath.isEmpty) currentClassPath = Some(new PathResolver(settings).result)
2329
currentClassPath.get
2430
}
2531

32+
private[nsc] lazy val flatClassPath: FlatClassPath = {
33+
assert(settings.YclasspathImpl.value == ClassPathRepresentationType.Flat,
34+
"To use flat classpath representation you must enable it with -YclasspathImpl:flat compiler option.")
35+
36+
new FlatClassPathResolver(settings).result
37+
}
38+
2639
/** Update classpath with a substituted subentry */
2740
def updateClassPath(subst: Map[ClassPath[AbstractFile], ClassPath[AbstractFile]]) =
2841
currentClassPath = Some(new DeltaClassPath(currentClassPath.get, subst))

0 commit comments

Comments
 (0)