Skip to content

Commit

Permalink
Clean up SourceFile creation and error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
som-snytt committed Mar 26, 2022
1 parent 6f3fe05 commit 159da44
Show file tree
Hide file tree
Showing 9 changed files with 32 additions and 44 deletions.
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/CompilationUnit.scala
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ object CompilationUnit {
/** Make a compilation unit for top class `clsd` with the contents of the `unpickled` tree */
def apply(clsd: ClassDenotation, unpickled: Tree, forceTrees: Boolean)(using Context): CompilationUnit =
val file = clsd.symbol.associatedFile.nn
apply(new SourceFile(file, Array.empty[Char]), unpickled, forceTrees)
apply(SourceFile(file, Array.empty[Char]), unpickled, forceTrees)

/** Make a compilation unit, given picked bytes and unpickled tree */
def apply(source: SourceFile, unpickled: Tree, forceTrees: Boolean)(using Context): CompilationUnit = {
Expand Down
9 changes: 3 additions & 6 deletions compiler/src/dotty/tools/dotc/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -307,12 +307,9 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
def compileFromStrings(scalaSources: List[String], javaSources: List[String] = Nil): Unit = {
def sourceFile(source: String, isJava: Boolean): SourceFile = {
val uuid = java.util.UUID.randomUUID().toString
val ext = if (isJava) ".java" else ".scala"
val virtualFile = new VirtualFile(s"compileFromString-$uuid.$ext")
val writer = new BufferedWriter(new OutputStreamWriter(virtualFile.output, StandardCharsets.UTF_8.nn.name)) // buffering is still advised by javadoc
writer.write(source)
writer.close()
new SourceFile(virtualFile, Codec.UTF8)
val ext = if (isJava) "java" else "scala"
val name = s"compileFromString-$uuid.$ext"
SourceFile.virtual(name, source)
}
val sources =
scalaSources.map(sourceFile(_, isJava = false)) ++
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,14 +297,8 @@ class InteractiveDriver(val settings: List[String]) extends Driver {
cleanupTree(tree)
}

private def toSource(uri: URI, sourceCode: String): SourceFile = {
val path = Paths.get(uri)
val virtualFile = new VirtualFile(path.getFileName.toString, path.toString)
val writer = new BufferedWriter(new OutputStreamWriter(virtualFile.output, StandardCharsets.UTF_8.name))
writer.write(sourceCode)
writer.close()
new SourceFile(virtualFile, Codec.UTF8)
}
private def toSource(uri: URI, sourceCode: String): SourceFile =
SourceFile.virtual(Paths.get(uri).toString, sourceCode)

/**
* Initialize this driver and compiler.
Expand Down
32 changes: 13 additions & 19 deletions compiler/src/dotty/tools/dotc/util/SourceFile.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@ import Chars._
import scala.annotation.internal.sharable
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.util.chaining.given

import java.io.IOException
import java.nio.charset.StandardCharsets
import java.nio.file.{FileSystemException, NoSuchFileException}
import java.util.Optional
import java.util.concurrent.atomic.AtomicInteger
import java.util.regex.Pattern
Expand Down Expand Up @@ -71,15 +72,6 @@ class SourceFile(val file: AbstractFile, computeContent: => Array[Char]) extends

def maybeIncomplete: Boolean = _maybeInComplete

def this(file: AbstractFile, codec: Codec) =
// It would be cleaner to check if the file exists instead of catching
// an exception, but it turns out that Files.exists is remarkably slow,
// at least on Java 8 (https://rules.sonarsource.com/java/tag/performance/RSPEC-3725),
// this is significant enough to show up in our benchmarks.
this(file,
try new String(file.toByteArray, codec.charSet).toCharArray
catch case _: java.nio.file.NoSuchFileException => Array[Char]())

/** Tab increment; can be overridden */
def tabInc: Int = 8

Expand Down Expand Up @@ -226,9 +218,8 @@ object SourceFile {
implicit def fromContext(using Context): SourceFile = ctx.source

def virtual(name: String, content: String, maybeIncomplete: Boolean = false) =
val src = new SourceFile(new VirtualFile(name, content.getBytes(StandardCharsets.UTF_8)), scala.io.Codec.UTF8)
src._maybeInComplete = maybeIncomplete
src
SourceFile(new VirtualFile(name, content.getBytes(StandardCharsets.UTF_8)), content.toCharArray)
.tap(_._maybeInComplete = maybeIncomplete)

/** Returns the relative path of `source` within the `reference` path
*
Expand Down Expand Up @@ -273,17 +264,20 @@ object SourceFile {
ScriptSourceFile.hasScriptHeader(content)

def apply(file: AbstractFile | Null, codec: Codec): SourceFile =
// see note above re: Files.exists is remarkably slow
// Files.exists is slow on Java 8 (https://rules.sonarsource.com/java/tag/performance/RSPEC-3725),
// so cope with failure; also deal with path prefix "Not a directory".
val chars =
try
new String(file.toByteArray, codec.charSet).toCharArray
catch
case _: java.nio.file.NoSuchFileException => Array[Char]()
try new String(file.toByteArray, codec.charSet).toCharArray
catch
case _: NoSuchFileException => Array.empty[Char]
case fse: FileSystemException if fse.getMessage.endsWith("Not a directory") => Array.empty[Char]

if isScript(file, chars) then
ScriptSourceFile(file, chars)
else
new SourceFile(file, chars)
SourceFile(file, chars)

def apply(file: AbstractFile | Null, computeContent: => Array[Char]): SourceFile = new SourceFile(file, computeContent)
}

@sharable object NoSource extends SourceFile(NoAbstractFile, Array[Char]()) {
Expand Down
14 changes: 9 additions & 5 deletions compiler/src/dotty/tools/io/VirtualFile.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ class VirtualFile(val name: String, override val path: String) extends AbstractF
def this(name: String) = this(name, name)

/**
* Initializes this instance with the specified name and an
* identical path.
* Initializes this instance with the specified path
* and a name taken from the last path element.
*
* @param name the name of the virtual file to be created
* @param path the path of the virtual file to be created
* @param content the initial contents of the virtual file
* @return the created virtual file
*/
def this(name: String, content: Array[Byte]) = {
this(name)
def this(path: String, content: Array[Byte]) = {
this(VirtualFile.nameOf(path), path)
this.content = content
}

Expand Down Expand Up @@ -104,3 +104,7 @@ class VirtualFile(val name: String, override val path: String) extends AbstractF
*/
def lookupNameUnchecked(name: String, directory: Boolean): AbstractFile = unsupported()
}
object VirtualFile:
private def nameOf(path: String): String =
val i = path.lastIndexOf('/')
if i >= 0 && i < path.length - 1 then path.substring(i + 1) else path
2 changes: 1 addition & 1 deletion compiler/test/dotty/tools/dotc/parsing/ParserTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class ParserTest extends DottyTest {
parsedTrees.clear()
}

def parse(file: PlainFile): Tree = parseSource(new SourceFile(file, Codec.UTF8))
def parse(file: PlainFile): Tree = parseSource(SourceFile(file, Codec.UTF8))

private def parseSource(source: SourceFile): Tree = {
//println("***** parsing " + source.file)
Expand Down
2 changes: 1 addition & 1 deletion compiler/test/dotty/tools/dotc/parsing/ScannerTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class ScannerTest extends DottyTest {

def scan(file: PlainFile): Unit = {
//println("***** scanning " + file)
val source = new SourceFile(file, Codec.UTF8)
val source = SourceFile(file, Codec.UTF8)
val scanner = new Scanner(source)
var i = 0
while (scanner.token != EOF) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package dotty.tools.languageserver.util
import dotty.tools.languageserver.util.embedded.{CodeInRange, CodeMarker}
import dotty.tools.languageserver.util.server.TestFile

import org.eclipse.lsp4j._
import org.eclipse.lsp4j.{Location, Range, SymbolKind}

import PositionContext._

Expand Down
3 changes: 1 addition & 2 deletions scaladoc/src/dotty/tools/scaladoc/DocContext.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@ def throwableToString(t: Throwable)(using CompilerContext): String =

private def sourcePostionFor(f: File)(using CompilerContext) =
val relPath = relativePath(f.toPath)
val virtualFile = new VirtualFile(relPath.toString, relPath.toString)
val sourceFile = new SourceFile(virtualFile, Codec.UTF8)
val sourceFile = SourceFile.virtual(relPath.toString, content = "")
SourcePosition(sourceFile, Spans.NoSpan)

// TODO (https://github.com/lampepfl/scala3doc/issues/238): provide proper error handling
Expand Down

0 comments on commit 159da44

Please sign in to comment.