summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-05-12 05:34:37 +0000
committerPaul Phillips <paulp@improving.org>2011-05-12 05:34:37 +0000
commit2bd6b4ae4010b3c65cb36d876c2ca94cb6d48b14 (patch)
tree1081485986e10418ddcee70b977625743f210ff4 /src
parent4869a2b28438b2175d615a55691cc202f10d0191 (diff)
downloadscala-2bd6b4ae4010b3c65cb36d876c2ca94cb6d48b14.tar.gz
scala-2bd6b4ae4010b3c65cb36d876c2ca94cb6d48b14.tar.bz2
scala-2bd6b4ae4010b3c65cb36d876c2ca94cb6d48b14.zip
Fairly ruthlessly optimized ZipArchive.
results from the profiler, but it sure isn't slower and it shed 125 lines or so. No review.
Diffstat (limited to 'src')
-rw-r--r--src/compiler/scala/tools/nsc/backend/JavaPlatform.scala17
-rw-r--r--src/compiler/scala/tools/nsc/interpreter/IMain.scala2
-rw-r--r--src/compiler/scala/tools/nsc/io/SourceReader.scala50
-rw-r--r--src/compiler/scala/tools/nsc/io/ZipArchive.scala385
-rw-r--r--src/compiler/scala/tools/nsc/util/ClassPath.scala4
-rw-r--r--src/compiler/scala/tools/util/PathResolver.scala16
6 files changed, 160 insertions, 314 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala
index 1891c8f13d..522b1ddd39 100644
--- a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala
+++ b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala
@@ -13,10 +13,10 @@ import scala.tools.util.PathResolver
trait JavaPlatform extends Platform[AbstractFile] {
import global._
- import definitions.{ BoxesRunTimeClass, getMember }
+ import definitions._
lazy val classPath = new PathResolver(settings).result
- def rootLoader = new loaders.JavaPackageLoader(classPath)
+ def rootLoader = new loaders.JavaPackageLoader(classPath)
private def depAnalysisPhase =
if (settings.make.isDefault) Nil
@@ -26,20 +26,19 @@ trait JavaPlatform extends Platform[AbstractFile] {
flatten, // get rid of inner classes
liftcode, // generate reified trees
genJVM // generate .class files
- ) ::: depAnalysisPhase
+ ) ++ depAnalysisPhase
- lazy val externalEquals = getMember(BoxesRunTimeClass, nme.equals_)
- def externalEqualsNumNum = getMember(BoxesRunTimeClass, "equalsNumNum")
- def externalEqualsNumChar = getMember(BoxesRunTimeClass, "equalsNumChar")
- def externalEqualsNumObject = getMember(BoxesRunTimeClass, "equalsNumObject")
+ lazy val externalEquals = getMember(BoxesRunTimeClass, nme.equals_)
+ lazy val externalEqualsNumNum = getMember(BoxesRunTimeClass, "equalsNumNum")
+ lazy val externalEqualsNumChar = getMember(BoxesRunTimeClass, "equalsNumChar")
+ lazy val externalEqualsNumObject = getMember(BoxesRunTimeClass, "equalsNumObject")
/** We could get away with excluding BoxedBooleanClass for the
* purpose of equality testing since it need not compare equal
* to anything but other booleans, but it should be present in
* case this is put to other uses.
*/
- def isMaybeBoxed(sym: Symbol): Boolean = {
- import definitions._
+ def isMaybeBoxed(sym: Symbol) = {
(sym == ObjectClass) ||
(sym == JavaSerializableClass) ||
(sym == ComparableClass) ||
diff --git a/src/compiler/scala/tools/nsc/interpreter/IMain.scala b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
index 8f6cd049bd..69858e8bba 100644
--- a/src/compiler/scala/tools/nsc/interpreter/IMain.scala
+++ b/src/compiler/scala/tools/nsc/interpreter/IMain.scala
@@ -244,7 +244,7 @@ class IMain(val settings: Settings, protected val out: PrintWriter) extends Impo
}
/** the compiler's classpath, as URL's */
- lazy val compilerClasspath: List[URL] = new PathResolver(settings) asURLs
+ lazy val compilerClasspath = global.classPath.asURLs
/* A single class loader is used for all commands interpreted by this Interpreter.
It would also be possible to create a new class loader for each command
diff --git a/src/compiler/scala/tools/nsc/io/SourceReader.scala b/src/compiler/scala/tools/nsc/io/SourceReader.scala
index 7c9b776eb3..37da2de83d 100644
--- a/src/compiler/scala/tools/nsc/io/SourceReader.scala
+++ b/src/compiler/scala/tools/nsc/io/SourceReader.scala
@@ -33,58 +33,38 @@ class SourceReader(decoder: CharsetDecoder, reporter: Reporter) {
"Please try specifying another one using the -encoding option")
}
- //########################################################################
- // Public Methods
-
/** Reads the file with the specified name. */
def read(filename: String): Array[Char]= read(new JFile(filename))
/** Reads the specified file. */
def read(file: JFile): Array[Char] = {
val c = new FileInputStream(file).getChannel
- try {
- read(c)
- } catch {
- case e:Exception =>
- if (true) e.printStackTrace
- reportEncodingError(file.toString())
- new Array[Char](0)
- } finally {
- c.close()
- }
+
+ try read(c)
+ catch { case e: Exception => reportEncodingError("" + file) ; Array() }
+ finally c.close()
}
/** Reads the specified file.
- *
- * @param file ...
- * @return ...
*/
def read(file: AbstractFile): Array[Char] = {
- file match {
- case p:PlainFile =>
- read(p.file) // bq: (!!!)
- case z:ZipArchive#FileEntry =>
- val c = Channels.newChannel(z.archive.getInputStream(z.entry))
- read(c)
- case _ =>
- val b = ByteBuffer.wrap(file.toByteArray)
- try {
- read(b)
- } catch {
- case e:Exception =>
- if (true) e.printStackTrace
- reportEncodingError(file.toString())
- new Array[Char](0)
- }
+ try file match {
+ case p: PlainFile => read(p.file)
+ case z: ZipArchive#FileEntry => read(Channels.newChannel(z.input))
+ case _ => read(ByteBuffer.wrap(file.toByteArray))
+ }
+ catch {
+ case e: Exception => reportEncodingError("" + file) ; Array()
}
}
/** Reads the specified byte channel. */
protected def read(input: ReadableByteChannel): Array[Char] = {
val decoder: CharsetDecoder = this.decoder.reset()
- val bytes: ByteBuffer = this.bytes; bytes.clear()
- var chars: CharBuffer = this.chars; chars.clear()
- var endOfInput: Boolean = false
+ val bytes: ByteBuffer = this.bytes; bytes.clear()
+ var chars: CharBuffer = this.chars; chars.clear()
+ var endOfInput = false
+
while (!endOfInput ) {
endOfInput = input.read(bytes) < 0
bytes.flip()
diff --git a/src/compiler/scala/tools/nsc/io/ZipArchive.scala b/src/compiler/scala/tools/nsc/io/ZipArchive.scala
index 52d6c41354..2de0ac8285 100644
--- a/src/compiler/scala/tools/nsc/io/ZipArchive.scala
+++ b/src/compiler/scala/tools/nsc/io/ZipArchive.scala
@@ -1,163 +1,106 @@
/* NSC -- new Scala compiler
* Copyright 2005-2011 LAMP/EPFL
- * @author Martin Odersky
+ * @author Paul Phillips
*/
-
package scala.tools.nsc
package io
import java.net.URL
-import java.util.Enumeration
-import java.io.{ IOException, InputStream, BufferedInputStream, ByteArrayInputStream }
+import java.io.{ IOException, InputStream, ByteArrayInputStream }
import java.util.zip.{ ZipEntry, ZipFile, ZipInputStream }
-import PartialFunction._
-
-import scala.collection.mutable.{ Map, HashMap }
-import annotation.tailrec
+import scala.collection.{ immutable, mutable }
-/**
- * @author Philippe Altherr
- * @version 1.0, 23/03/2004
+/** An abstraction for zip files and streams. Everything is written the way
+ * it is for performance: we come through here a lot on every run. Be careful
+ * about changing it.
+ *
+ * @author Philippe Altherr (original version)
+ * @author Paul Phillips (this one)
+ * @version 2.0,
*/
object ZipArchive {
def fromPath(path: Path): ZipArchive = fromFile(path.toFile)
/**
- * If the specified file <code>file</code> exists and is a readable
- * zip archive, returns an abstract file backed by it. Otherwise,
- * returns <code>null</code>.
- *
- * @param file ...
- * @return ...
+ * @param file a File
+ * @return A ZipArchive if `file` is a readable zip file, otherwise null.
*/
def fromFile(file: File): ZipArchive =
- try new ZipArchive(file, new ZipFile(file.jfile))
+ try new FileZipArchive(file, new ZipFile(file.jfile))
catch { case _: IOException => null }
/**
- * Returns an abstract directory backed by the specified archive.
+ * @param zipFile a ZipFile
+ * @return A ZipArchive if `zipFile` is a readable zip file, otherwise null.
*/
- def fromArchive(archive: ZipFile): ZipArchive =
- new ZipArchive(File(archive.getName()), archive)
+ def fromArchive(zipFile: ZipFile): ZipArchive =
+ new FileZipArchive(File(zipFile.getName()), zipFile)
/**
- * Returns an abstract directory backed by the specified archive.
+ * @param url the url of a zip file
+ * @return A ZipArchive backed by the given url.
*/
- def fromURL(url: URL): AbstractFile = new URLZipArchive(url)
-
- private[io] trait ZipTrav extends Traversable[ZipEntry] {
- def zis: () => ZipInputStream
- }
+ def fromURL(url: URL): ZipArchive = new URLZipArchive(url)
+}
- private[io] class ZipEntryTraversableClass(in: InputStream) extends ZipTrav {
- val zis = () => new ZipInputStream(in)
+trait ZipArchive extends AbstractFile {
+ self =>
- def foreach[U](f: ZipEntry => U) = {
- var in: ZipInputStream = null
- @tailrec def loop(): Unit = {
- if (in.available == 0)
- return
+ // The root of this archive, populated lazily. Implemented by File and URL.
+ def root: DirEntry
- val entry = in.getNextEntry()
- if (entry != null) {
- f(entry)
- in.closeEntry()
- loop()
- }
- }
+ // AbstractFile members with common implementations in File and URL
+ override def lookupName(name: String, directory: Boolean) = root.lookupName(name, directory)
+ override def lookupNameUnchecked(name: String, directory: Boolean) = unsupported
+ override def iterator = root.iterator
+ override def isDirectory = true
- try {
- in = zis()
- loop()
- }
- finally in.close()
- }
- }
-}
-import ZipArchive.ZipTrav
+ // Will be the root dir entry node after being populated.
+ protected val rootDirEntry = new DirEntry("/")
+ // Collected Paths -> DirEntries
+ private val dirs = newHashMap[DirEntry]() += ("/" -> rootDirEntry)
+ // Override if there's one available
+ def zipFile: ZipFile = null
-/** This abstraction aims to factor out the common code between
- * ZipArchive (backed by a zip file) and URLZipArchive (backed
- * by an InputStream.)
- */
-private[io] trait ZipContainer extends AbstractFile {
- /** Abstract types */
- type SourceType // InputStream or AbstractFile
- type CreationType // InputStream or ZipFile
+ sealed abstract class Entry(path: String) extends VirtualFile(baseName(path), path) {
+ def zipEntry: ZipEntry
+ def parent: DirEntry
- /** Abstract values */
- protected val creationSource: CreationType
- protected val root: DirEntryInterface
- protected def DirEntryConstructor: (AbstractFile, String, String) => DirEntryInterface
- protected def FileEntryConstructor: (SourceType, String, String, ZipEntry) => FileEntryInterface
- protected def ZipTravConstructor: CreationType => ZipTrav
-
- protected[io] trait EntryInterface extends VirtualFile {
- def name: String
- def path: String
+ override def container: DirEntry = parent
+ override def underlyingSource = Some(self)
+ final override def path = self + "(" + super.path + ")"
+ final override def equals(other: Any) = this eq other.asInstanceOf[AnyRef]
+ final override def hashCode = super.path.##
}
+ class DirEntry(path: String) extends Entry(path) {
+ var zipEntry: ZipEntry = _
+ val entries = newHashMap[Entry]()
- protected[io] trait DirEntryInterface extends EntryInterface {
- def source: SourceType
- val entries: Map[String, EntryInterface] = new HashMap()
- var entry: ZipEntry = _
-
- override def input = throw new Error("cannot read directories")
+ def parent = getDir(dirName(path))
override def lastModified: Long =
- if (entry ne null) entry.getTime() else super.lastModified
-
+ if (zipEntry ne null) zipEntry.getTime() else super.lastModified
override def isDirectory = true
- override def iterator: Iterator[AbstractFile] = entries.valuesIterator
- override def lookupName(name: String, directory: Boolean): AbstractFile = {
- def slashName = if (directory) name + "/" else name
- entries.getOrElse(slashName, null)
+ override def iterator = entries.valuesIterator
+ override def lookupName(name: String, directory: Boolean): Entry = {
+ if (directory) entries(name + "/")
+ else entries(name)
}
}
-
- protected[io] trait FileEntryInterface extends EntryInterface {
- def entry: ZipEntry
-
- override def lastModified: Long = entry.getTime()
- override def sizeOption = Some(entry.getSize().toInt)
+ class FileEntry(path: String, archive: ZipFile, val zipEntry: ZipEntry) extends Entry(path) {
+ def parent = getDir(path)
+ override def input = archive getInputStream zipEntry
+ override def lastModified = zipEntry.getTime()
+ override def sizeOption = Some(zipEntry.getSize().toInt)
}
- class ZipRootCreator(f: ZipRootCreator => SourceType) {
- val root = DirEntryConstructor(ZipContainer.this, "<root>", "/")
-
- // Map from paths to DirEntries
- val dirs = HashMap[String, DirEntryInterface]("/" -> root)
- val traverser = ZipTravConstructor(creationSource)
- private[this] var _parent: DirEntryInterface = _
- def parent = _parent
+ private def dirName(path: String) = splitPath(path, true)
+ private def baseName(path: String) = splitPath(path, false)
+ private def splitPath(path0: String, front: Boolean): String = {
+ val start = path0.length - 1
+ val path = if (path0.charAt(start) == '/') path0.substring(0, start) else path0
+ var i = path.length - 1
- def addEntry(entry: ZipEntry) {
- val path = entry.getName
- if (entry.isDirectory) {
- val dir: DirEntryInterface = getDir(dirs, path)
- if (dir.entry == null) dir.entry = entry
- }
- else {
- _parent = getDir(dirs, pathFront(path))
- val name = pathRear(path)
- _parent.entries(name) = FileEntryConstructor(f(this), name, path, entry)
- }
- }
-
- def apply() = {
- traverser foreach addEntry
- root
- }
- }
- // Uglified for performance.
- // protected def splitPath(path: String): (String, String) = {
- // (path lastIndexOf '/') match {
- // case -1 => ("/", path)
- // case idx => path splitAt (idx + 1)
- // }
- // }
- protected def splitPath(path: String, front: Boolean): String = {
- var i = path.length - 1
while (i >= 0 && path.charAt(i) != '/')
i -= 1
@@ -168,168 +111,84 @@ private[io] trait ZipContainer extends AbstractFile {
if (front) path.substring(0, i + 1)
else path.substring(i + 1)
}
- private def pathFront(path: String) = splitPath(path, true)
- private def pathRear(path: String) = splitPath(path, false)
- // End uglify.
-
- /**
- * Returns the abstract file in this abstract directory with the
- * specified name. If there is no such file, returns null. The
- * argument "directory" tells whether to look for a directory or
- * or a regular file.
- */
- override def lookupName(name: String, directory: Boolean): AbstractFile =
- root.lookupName(name, directory)
-
- /** Returns an abstract file with the given name. It does not
- * check that it exists.
- */
- override def lookupNameUnchecked(name: String, directory: Boolean) = unsupported
- /** Returns all abstract subfiles of this abstract directory. */
- override def iterator: Iterator[AbstractFile] = root.iterator
+ // Not using withDefault to be certain of performance.
+ private def newHashMap[T >: Null]() =
+ new mutable.HashMap[String, T] { override def default(key: String) = null }
- /**
- * Looks up the path in the given map and returns if found.
- * If not present, creates a new DirEntry, adds to both given
- * map and parent.entries, and returns it.
+ /** If the given path entry has already been created, return the DirEntry.
+ * Otherwise create it and update both dirs and the parent's dirEntries.
+ * Not using getOrElseUpdate to be sure of performance.
*/
- protected def getDir(dirs: Map[String, DirEntryInterface], path: String): DirEntryInterface =
- dirs.getOrElseUpdate(path, {
- val pathTail = path.substring(0, path.length - 1)
- val home = pathFront(pathTail)
- val name = pathRear(pathTail)
- val parent = getDir(dirs, home)
- val dir = DirEntryConstructor(parent, name, path)
-
- parent.entries(name + path.last) = dir
- dir
- })
-
- override def isDirectory = true
-}
-
-/**
- * This class implements an abstract directory backed by a zip
- * archive. We let the encoding be <code>null</code>, because we behave like
- * a directory.
- *
- * @author Philippe Altherr
- * @version 1.0, 23/03/2004
- */
-final class ZipArchive(file: File, val archive: ZipFile) extends PlainFile(file) with ZipContainer {
- self =>
-
- type SourceType = AbstractFile
- type CreationType = ZipFile
-
- protected val creationSource = archive
- protected lazy val root = new ZipRootCreator(_.parent)()
- protected def DirEntryConstructor = new DirEntry(_, _, _)
- protected def FileEntryConstructor = new FileEntry(_, _, _, _)
- protected def ZipTravConstructor = new ZipFileIterable(_)
-
- abstract class Entry(
- override val container: AbstractFile,
- name: String,
- path: String
- ) extends VirtualFile(name, path)
- {
- override def underlyingSource = Some(self)
- final override def path = self + "(" + super.path + ")"
- final def archive = self.archive
-
- override def hashCode = super.hashCode + container.hashCode
- override def equals(that : Any) =
- super.equals(that) && (cond(that) {
- case e: Entry => container == e.container
- })
+ private def getDir(path: String): DirEntry = {
+ if (dirs contains path) dirs(path)
+ else {
+ val parent = getDir(dirName(path))
+ val newEntry = new DirEntry(path)
+ parent.entries(newEntry.name) = newEntry
+ dirs(path) = newEntry
+ newEntry
+ }
}
- final class DirEntry(
- container: AbstractFile,
- name: String,
- path: String
- ) extends Entry(container, name, path) with DirEntryInterface
- {
- def source = container
- }
+ protected final def addZipEntry(entry: ZipEntry) {
+ val path = entry.getName
+ if (entry.isDirectory) {
+ val dir = getDir(path)
+ if (dir.zipEntry == null)
+ dir.zipEntry = entry
+ }
+ else {
+ val parent = getDir(dirName(path))
+ val newEntry = new FileEntry(path, zipFile, entry)
- final class FileEntry(
- container: AbstractFile,
- name: String,
- path: String,
- val entry: ZipEntry
- ) extends Entry(container, name, path) with FileEntryInterface
- {
- override def input = archive getInputStream entry
+ parent.entries(newEntry.name) = newEntry
+ }
}
+}
- class ZipFileIterable(z: ZipFile) extends Iterable[ZipEntry] with ZipTrav {
- def zis: () => ZipInputStream = null // not valid for this type
- def iterator = new Iterator[ZipEntry] {
- val enum = z.entries()
- def hasNext = enum.hasMoreElements
- def next = enum.nextElement
- }
+final class FileZipArchive(file: File, override val zipFile: ZipFile) extends PlainFile(file) with ZipArchive {
+ lazy val root: DirEntry = {
+ val enum = zipFile.entries()
+ while (enum.hasMoreElements)
+ addZipEntry(enum.nextElement)
+
+ rootDirEntry
}
}
-/**
- * This class implements an abstract directory backed by a specified
- * zip archive.
- *
- * @author Stephane Micheloud
- * @version 1.0, 29/05/2007
- */
-final class URLZipArchive(url: URL) extends AbstractFile with ZipContainer {
- type SourceType = InputStream
- type CreationType = InputStream
+final class URLZipArchive(url: URL) extends AbstractFile with ZipArchive {
+ lazy val root: DirEntry = {
+ val in = new ZipInputStream(new ByteArrayInputStream(Streamable.bytes(input)))
- protected lazy val creationSource = input
- protected lazy val root = new ZipRootCreator(x => byteInputStream(x.traverser.zis()))()
+ @annotation.tailrec def loop() {
+ if (in.available != 0) {
+ val entry = in.getNextEntry()
+ if (entry != null) {
+ addZipEntry(entry)
+ in.closeEntry()
+ loop()
+ }
+ }
+ }
+ try loop()
+ finally in.close()
- protected def DirEntryConstructor = (_, name, path) => new DirEntry(name, path)
- protected def FileEntryConstructor = new FileEntry(_, _, _, _)
- protected def ZipTravConstructor = new ZipArchive.ZipEntryTraversableClass(_)
+ rootDirEntry
+ }
def name: String = url.getFile()
def path: String = url.getPath()
def input: InputStream = url.openStream()
- def absolute: AbstractFile = this
- def lastModified: Long =
+ def lastModified =
try url.openConnection().getLastModified()
catch { case _: IOException => 0 }
/** Methods we don't support but have to implement because of the design */
- def file: JFile = null
- def create(): Unit = unsupported
- def delete(): Unit = unsupported
- def output = unsupported
+ def absolute = this
+ def file = null
+ def create() = unsupported
+ def delete() = unsupported
+ def output = unsupported
def container = unsupported
-
- abstract class Entry(name: String, path: String) extends VirtualFile(name, path) {
- final override def path = URLZipArchive.this + "(" + super.path + ")"
- override def container = URLZipArchive.this
- }
- final class DirEntry(name: String, path: String) extends Entry(name, path) with DirEntryInterface {
- def source = input
- }
- final class FileEntry(
- val in: InputStream,
- name: String,
- path: String,
- val entry: ZipEntry
- ) extends Entry(name, path) with FileEntryInterface
- {
- override def input = in
- }
-
- /** Private methods **/
- private def byteInputStream(in: InputStream): InputStream = {
- val minusOne = (-1).toByte
- val buf = new BufferedInputStream(in)
- val bytes = Iterator continually in.read().toByte takeWhile (_ != minusOne)
- new ByteArrayInputStream(bytes.toSeq.toArray)
- }
}
diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala
index c3731150c4..25c09b6c15 100644
--- a/src/compiler/scala/tools/nsc/util/ClassPath.scala
+++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala
@@ -286,12 +286,12 @@ abstract class ClassPath[T] {
case _ => None
}
- def sortString = asURLs map (_.toString) sorted
+ def sortString = join(split(asClasspathString).sorted: _*)
override def equals(that: Any) = that match {
case x: ClassPath[_] => this.sortString == x.sortString
case _ => false
}
- override def hashCode = sortString.hashCode
+ override def hashCode = sortString.##
}
/**
diff --git a/src/compiler/scala/tools/util/PathResolver.scala b/src/compiler/scala/tools/util/PathResolver.scala
index 1420499e91..1055bfdef2 100644
--- a/src/compiler/scala/tools/util/PathResolver.scala
+++ b/src/compiler/scala/tools/util/PathResolver.scala
@@ -105,10 +105,18 @@ object PathResolver {
else if (scalaLibAsDir.isDirectory) scalaLibAsDir.path
else ""
- def scalaBootClassPath = scalaLibDirFound match {
- case Some(dir) if scalaHomeExists => join(ClassPath expandDir dir.path: _*)
- case _ => ""
- }
+ // XXX It must be time for someone to figure out what all these things
+ // are intended to do. This is disabled here because it was causing all
+ // the scala jars to end up on the classpath twice: one on the boot
+ // classpath as set up by the runner (or regular classpath under -nobootcp)
+ // and then again here.
+ def scalaBootClassPath = ""
+ // scalaLibDirFound match {
+ // case Some(dir) if scalaHomeExists =>
+ // val paths = ClassPath expandDir dir.path
+ // join(paths: _*)
+ // case _ => ""
+ // }
def scalaExtDirs = Environment.scalaExtDirs