Skip to content

Commit 834903a

Browse files
committed
WIP
1 parent 27c8a02 commit 834903a

13 files changed

Lines changed: 84 additions & 96 deletions

File tree

compiler/src/dotty/tools/dotc/classpath/ClassPathFactory.scala

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
*/
44
package dotty.tools.dotc.classpath
55

6-
import dotty.tools.io.{AbstractFile, ClassPath, JarArchive, VirtualDirectory}
6+
import dotty.tools.io.{AbstractFile, ClassPath, Directory, File, Path, VirtualDirectory}
77
import dotty.tools.dotc.classpath.FileUtils.isClassContainer
88
import dotty.tools.dotc.core.Contexts.*
99
import dotty.tools.dotc.interactive.LogicalSourcePath
1010
import dotty.tools.dotc.interactive.LogicalPackage
1111

12+
import java.net.{MalformedURLException, URI, URISyntaxException, URL}
1213
import java.nio.file.Files
14+
import java.util.jar.{Attributes, JarInputStream}
1315

1416
/**
1517
* Provides factory methods for classpath. When creating classpath instances for a given path,
@@ -68,7 +70,7 @@ class ClassPathFactory(precomputedSourcePackages: Option[LogicalPackage] = None)
6870
if scala.util.Properties.propOrFalse("scala.expandjavacp") then
6971
for
7072
file <- files
71-
a <- JarArchive.expandManifestPath(file.path)
73+
a <- expandManifestPath(file.path)
7274
path = java.nio.file.Paths.get(a.toURI())
7375
if Files.exists(path)
7476
yield
@@ -79,6 +81,38 @@ class ClassPathFactory(precomputedSourcePackages: Option[LogicalPackage] = None)
7981
files.map(ClassPathFactory.newClassPath) ++ expanded
8082

8183
end classesInPathImpl
84+
85+
86+
/** Expand manifest jar classpath entries: these are either urls, or paths
87+
* relative to the location of the jar.
88+
*/
89+
private def expandManifestPath(jarPath: String): List[URL] =
90+
def specToURL(spec: String, basedir: Directory): Option[URL] =
91+
try
92+
val uri = new URI(spec)
93+
if uri.isAbsolute then Some(uri.toURL)
94+
else Some(basedir.resolve(Path(spec)).toURL)
95+
catch
96+
case _: MalformedURLException | _: URISyntaxException => None
97+
98+
val file = File(jarPath)
99+
if !file.isFile then
100+
return Nil
101+
102+
val baseDir = file.parent
103+
val in = new JarInputStream(file.inputStream())
104+
val manifest =
105+
try Option(in.getManifest)
106+
finally in.close()
107+
108+
manifest match
109+
case None => Nil
110+
case Some(m) =>
111+
val attrs = m.getMainAttributes.asInstanceOf[java.util.Map[Attributes.Name, String]]
112+
attrs.get(Attributes.Name.CLASS_PATH) match
113+
case cp: String if cp.trim().nonEmpty =>
114+
cp.split("\\s+").toList.map(elem => specToURL(elem, baseDir).getOrElse((baseDir / elem).toURL))
115+
case _ => Nil
82116
}
83117

84118
object ClassPathFactory {

compiler/src/dotty/tools/dotc/classpath/ZipArchiveFileLookup.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ trait ZipArchiveFileLookup[FileEntryType] extends ClassPath {
2222

2323
override def asURLs: Seq[URL] = Seq(zipFile.toURI.toURL)
2424

25-
private val archive = new FileZipArchive(zipFile.toPath, Some(release))
25+
private val archive = AbstractFile.getDirectory(zipFile.toPath, release).nn
2626

2727
override def packages(inPackage: String): Seq[PackageEntry] =
2828
findDirEntry(inPackage) match {

compiler/src/dotty/tools/dotc/plugins/Plugin.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ object Plugin {
150150
// List[(jar, Try(descriptor))] in dir
151151
def scan(d: Directory) =
152152
d.files.toList
153-
.filter(JarArchive.isJarOrZip(_))
153+
.filter(_.ext.isJarOrZip)
154154
.sortBy(_.name)
155155
.map(j => (j, loadDescriptionFromJar(j)))
156156

compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import NameOps.*
1818
import inlines.Inlines
1919
import transform.ValueClasses
2020
import transform.Pickler
21-
import dotty.tools.io.{File, FileExtension, JarArchive}
21+
import dotty.tools.io.{File, FileExtension}
2222
import util.{Property, SourceFile}
2323
import java.io.PrintWriter
2424

@@ -94,16 +94,15 @@ class ExtractAPI extends Phase {
9494
def registerProductNames(fullClassName: String, binaryClassName: String) =
9595
val pathToClassFile = s"${binaryClassName.replace('.', java.io.File.separatorChar)}.class"
9696

97+
val outDir = ctx.settings.outputDir.value
9798
val classFile = {
98-
ctx.settings.outputDir.value match {
99-
case jar: JarArchive =>
99+
if outDir.ext.isJar then
100100
// important detail here, even on Windows, Zinc expects the separator within the jar
101101
// to be the system default, (even if in the actual jar file the entry always uses '/').
102102
// see https://github.com/sbt/zinc/blob/dcddc1f9cfe542d738582c43f4840e17c053ce81/internal/compiler-bridge/src/main/scala/xsbt/JarUtils.scala#L47
103-
new java.io.File(s"$jar!$pathToClassFile")
104-
case outputDir =>
105-
new java.io.File(outputDir.file, pathToClassFile)
106-
}
103+
new java.io.File(s"${outDir.path}!$pathToClassFile")
104+
else
105+
new java.io.File(outDir.file, pathToClassFile)
107106
}
108107

109108
cb.generatedNonLocalClass(sourceFile, classFile.toPath(), binaryClassName, fullClassName)

compiler/src/dotty/tools/dotc/sbt/ExtractDependencies.scala

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import dotty.tools.dotc.core.Types.*
1818
import dotty.tools.dotc.typer.Applications.*
1919
import dotty.tools.dotc.util.{NoSourcePosition, SrcPos}
2020
import dotty.tools.io
21-
import dotty.tools.io.{AbstractFile, FileExtension, PlainFile, ZipArchive}
21+
import dotty.tools.io.AbstractFile
2222
import xsbti.UseScope
2323
import xsbti.api.DependencyContext
2424
import xsbti.api.DependencyContext.*
@@ -546,9 +546,9 @@ class DependencyRecorder {
546546
* FIXME: we still need a way to resolve the correct classfile when we split tasty and classes between
547547
* different outputs (e.g. scala2-library-bootstrapped).
548548
*/
549-
def cachedSiblingClass(pf: PlainFile): Path =
549+
def cachedSiblingClass(pf: AbstractFile): Path =
550550
siblingClassfiles.getOrElseUpdate(pf, {
551-
val jpath = pf.jpath
551+
val jpath = pf.jpath.nn
552552
jpath.getParent.resolve(jpath.getFileName.toString.stripSuffix(".tasty") + ".class")
553553
})
554554

@@ -564,17 +564,13 @@ class DependencyRecorder {
564564

565565
def processExternalDependency() = {
566566
val binaryClassName = depClass.binaryClassName
567-
depFile match {
568-
case ze: ZipArchive#Entry => // The dependency comes from a JAR
569-
ze.underlyingSource match
570-
case zip if zip.jpath != null =>
571-
binaryDependency(zip.jpath, binaryClassName)
572-
case _ =>
573-
case pf: PlainFile => // The dependency comes from a class file, Zinc handles JRT filesystem
574-
binaryDependency(if isTastyOrSig then cachedSiblingClass(pf) else pf.jpath, binaryClassName)
567+
depFile.enclosing match
568+
case Some(archive) if archive.jpath != null => // The dependency comes from a JAR
569+
binaryDependency(archive.jpath.nn, binaryClassName)
570+
case _ if depFile.jpath != null =>
571+
binaryDependency(if isTastyOrSig then cachedSiblingClass(depFile) else depFile.jpath.nn, binaryClassName)
575572
case _ =>
576-
internalError(s"Ignoring dependency $depFile of unknown class ${depFile.getClass}}", fromClass.srcPos)
577-
}
573+
internalError(s"Ignoring dependency $depFile of unknown class ${depFile.getClass}", fromClass.srcPos)
578574
}
579575

580576
if isTastyOrSig || depFile.ext.isClass then

compiler/src/dotty/tools/dotc/semanticdb/ExtractSemanticDB.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import scala.PartialFunction.condOpt
2424
import typer.ImportInfo.withRootImports
2525

2626
import dotty.tools.dotc.{semanticdb => s}
27-
import dotty.tools.io.{AbstractFile, JarArchive}
27+
import dotty.tools.io.AbstractFile
2828
import dotty.tools.dotc.semanticdb.DiagnosticOps.*
2929
import scala.util.{Using, Failure, Success}
3030
import java.nio.file.Path
@@ -53,7 +53,7 @@ private[semanticdb] class ExtractSemanticDB private (phaseMode: ExtractSemanticD
5353

5454
override def isRunnable(using Context) =
5555
import ExtractSemanticDB.{semanticdbTarget, outputDirectory}
56-
def writesToOutputJar = semanticdbTarget.isEmpty && outputDirectory.isInstanceOf[JarArchive]
56+
def writesToOutputJar = semanticdbTarget.isEmpty && outputDirectory.ext.isJar
5757
(super.isRunnable || ctx.isBestEffort) && ctx.settings.Xsemanticdb.value && !writesToOutputJar
5858

5959
// Check not needed since it does not transform trees

compiler/src/dotty/tools/io/AbstractFile.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ object AbstractFile {
4040
*/
4141
private def getDirectory(path: Path, jarVersion: String): AbstractFile | Null =
4242
if (path.isDirectory) new PlainFile(path)
43-
else if (path.isFile && path.ext.isJarOrZip) new FileZipArchive(path.jpath, Some(jarVersion))
43+
else if (path.isFile && path.ext.isJarOrZip) new FileZipArchive(path.jpath, jarVersion)
4444
else null
4545
}
4646

@@ -77,6 +77,9 @@ abstract class AbstractFile extends dotty.tools.dotc.interfaces.AbstractFile {
7777
/** Returns the containing directory of this abstract file, if any */
7878
def container: Option[AbstractFile] = None
7979

80+
/** Gets the file that encloses this file, such as an archive, if this file is enclosed. */
81+
def enclosing: Option[AbstractFile] = None
82+
8083
/** Returns the underlying File if any and null otherwise. */
8184
def file: JFile | Null = try {
8285
val jpath = this.jpath

compiler/src/dotty/tools/io/ClassPath.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ object ClassPath {
4343

4444
/* Get all subdirectories, jars, zips out of a directory. */
4545
def lsDir(dir: Directory, filt: String => Boolean = _ => true) =
46-
dir.list.filter(x => filt(x.name) && (x.isDirectory || JarArchive.isJarOrZip(x))).map(_.path).toList
46+
dir.list.filter(x => filt(x.name) && (x.isDirectory || x.ext.isJarOrZip)).map(_.path).toList
4747

4848
if (pattern == "*") lsDir(Directory("."))
4949
// On Windows the JDK supports forward slash or backslash in classpath entries

compiler/src/dotty/tools/io/JarArchive.scala

Lines changed: 11 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ import scala.jdk.CollectionConverters.*
99
* This class implements an [[AbstractFile]] backed by a jar
1010
* that be can used as the compiler's output directory.
1111
*/
12-
class JarArchive private (val jarPath: Path, root: Directory, path: Path) extends PlainDirectory(root) {
12+
class JarArchive private (underlying: Path, root: Directory) extends PlainDirectory(root) {
1313
def close(): Unit = this.synchronized(jpath.getFileSystem().close())
1414

15+
override val path: String = underlying.path
16+
1517
override def exists: Boolean = jpath.getFileSystem().isOpen() && super.exists
1618

1719
def underlyingSource: AbstractFile =
18-
new PlainFile(path)
19-
20-
override def toString: String = jarPath.toString
20+
new PlainFile(underlying)
2121
}
2222

2323
object JarArchive {
@@ -28,8 +28,12 @@ object JarArchive {
2828
open(path, create = true)
2929
}
3030

31-
/** Create a jar file. */
32-
def open(path: Path, create: Boolean = false): JarArchive = {
31+
/** Opens a jar file. */
32+
def open(path: Path): JarArchive =
33+
open(path, create = false)
34+
35+
/** Opens or creates a jar file. */
36+
private def open(path: Path, create: Boolean): JarArchive = {
3337
require(path.ext.isJar)
3438

3539
// creating a new zip file system by using the JAR URL syntax:
@@ -43,52 +47,6 @@ object JarArchive {
4347
}
4448
}
4549
val root = fs.getRootDirectories().iterator.next()
46-
new JarArchive(path, Directory(root), path)
47-
}
48-
49-
// See http://download.java.net/jdk7/docs/api/java/nio/file/Path.html
50-
// for some ideas.
51-
private val ZipMagicNumber = List[Byte](80, 75, 3, 4)
52-
private def magicNumberIsZip(f: Path) = f.isFile && {
53-
val in = f.toFile.inputStream()
54-
try
55-
val first4 = in.readNBytes(4)
56-
first4.toList == ZipMagicNumber
57-
finally
58-
in.close()
50+
new JarArchive(path, Directory(root))
5951
}
60-
61-
def isJarOrZip(f: Path): Boolean =
62-
f.ext.isJarOrZip || magicNumberIsZip(f)
63-
64-
/** Expand manifest jar classpath entries: these are either urls, or paths
65-
* relative to the location of the jar.
66-
*/
67-
def expandManifestPath(jarPath: String): List[URL] =
68-
def specToURL(spec: String, basedir: Directory): Option[URL] =
69-
try
70-
val uri = new URI(spec)
71-
if uri.isAbsolute then Some(uri.toURL)
72-
else Some(basedir.resolve(Path(spec)).toURL)
73-
catch
74-
case _: MalformedURLException | _: URISyntaxException => None
75-
76-
val file = File(jarPath)
77-
if !file.isFile then
78-
return Nil
79-
80-
val baseDir = file.parent
81-
val in = new JarInputStream(file.inputStream())
82-
val manifest =
83-
try Option(in.getManifest)
84-
finally in.close()
85-
86-
manifest match
87-
case None => Nil
88-
case Some(m) =>
89-
val attrs = m.getMainAttributes.asInstanceOf[java.util.Map[Attributes.Name, String]]
90-
attrs.get(Attributes.Name.CLASS_PATH) match
91-
case cp: String if cp.trim().nonEmpty =>
92-
cp.split("\\s+").toList.map(elem => specToURL(elem, baseDir).getOrElse((baseDir / elem).toURL))
93-
case _ => Nil
9452
}

compiler/src/dotty/tools/io/ZipArchive.scala

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import scala.collection.mutable
2222
*
2323
* ''Note: This library is considered experimental and should not be used unless you know what you are doing.''
2424
*/
25-
object ZipArchive {
25+
private[io] object ZipArchive {
2626
private[io] val closeZipFile: Boolean = sys.props.get("scala.classpath.closeZip").exists(_.toBoolean)
2727

2828
private def dirName(path: String) = splitPath(path, front = true)
@@ -42,7 +42,7 @@ object ZipArchive {
4242
}
4343
import ZipArchive.*
4444
/** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */
45-
abstract class ZipArchive(override val jpath: JPath) extends AbstractFile with Equals {
45+
private[io] abstract class ZipArchive(override val jpath: JPath) extends AbstractFile with Equals {
4646
self =>
4747

4848
override def isDirectory: Boolean = true
@@ -53,7 +53,7 @@ abstract class ZipArchive(override val jpath: JPath) extends AbstractFile with E
5353
sealed abstract class Entry(path: String, parent: Entry | Null) extends VirtualFile(path, Array.emptyByteArray) {
5454
// have to keep this name for compat with sbt's compiler-interface
5555
def getArchive: ZipFile | Null = null
56-
def underlyingSource: ZipArchive = self
56+
override def enclosing: Option[AbstractFile] = Some(self)
5757
override def container: Option[AbstractFile] = Option(parent)
5858
override def toString: String = self.path + "(" + path + ")"
5959
}
@@ -96,14 +96,12 @@ abstract class ZipArchive(override val jpath: JPath) extends AbstractFile with E
9696
def close(): Unit
9797
}
9898
/** ''Note: This library is considered experimental and should not be used unless you know what you are doing.'' */
99-
final class FileZipArchive(jpath: JPath, release: Option[String] = None) extends ZipArchive(jpath) {
99+
private[io] final class FileZipArchive(jpath: JPath, release: String) extends ZipArchive(jpath) {
100100
private def openZipFile(): ZipFile = try {
101-
release match {
102-
case Some(r) if file.nn.getName.endsWith(".jar") =>
103-
new JarFile(file, true, ZipFile.OPEN_READ, if r == "" then Runtime.version() else Runtime.Version.parse(r))
104-
case _ =>
105-
new ZipFile(file)
106-
}
101+
if file.nn.getName.endsWith(".jar") then
102+
new JarFile(file, true, ZipFile.OPEN_READ, if release == "" then Runtime.version() else Runtime.Version.parse(release))
103+
else
104+
new ZipFile(file)
107105
} catch {
108106
case ioe: IOException => throw new IOException("Error accessing " + file.nn.getPath, ioe)
109107
}
@@ -150,7 +148,7 @@ final class FileZipArchive(jpath: JPath, release: Option[String] = None) extends
150148
while (entries.hasMoreElements) {
151149
val zipEntry = entries.nextElement
152150
if (!zipEntry.getName.startsWith("META-INF/versions/")) {
153-
val zipEntryVersioned = if (release.isDefined) {
151+
val zipEntryVersioned = if (release != "") {
154152
// JARFile will return the entry for the corresponding release-dependent version here under META-INF/versions
155153
zipFile.getEntry(zipEntry.getName)
156154
} else zipEntry

0 commit comments

Comments
 (0)