diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala | 25 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala | 36 | ||||
-rwxr-xr-x | test/files/run/t5717.scala | 21 |
3 files changed, 54 insertions, 28 deletions
diff --git a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala index c1cd3204e0..66aed14d1c 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala @@ -6,12 +6,15 @@ package scala.tools.nsc package backend.jvm -import java.io.{ DataOutputStream, FileOutputStream, OutputStream, File => JFile } +import java.io.{ DataOutputStream, FileOutputStream, IOException, OutputStream, File => JFile } import scala.tools.nsc.io._ import scala.tools.nsc.util.ScalaClassLoader import java.util.jar.Attributes.Name import scala.language.postfixOps +/** Can't output a file due to the state of the file system. */ +class FileConflictException(msg: String, val file: AbstractFile) extends IOException(msg) + /** For the last mile: turning generated bytecode in memory into * something you can use. Has implementations for writing to class * files, jars, and disassembled/javap output. @@ -20,16 +23,20 @@ trait BytecodeWriters { val global: Global import global._ - private def outputDirectory(sym: Symbol): AbstractFile = ( - settings.outputDirs.outputDirFor(enteringFlatten(sym.sourceFile)) - ) - private def getFile(base: AbstractFile, /*cls.getName()*/ clsName: String, suffix: String): AbstractFile = { + private def outputDirectory(sym: Symbol): AbstractFile = + settings.outputDirs outputDirFor enteringFlatten(sym.sourceFile) + + /** + * @param clsName cls.getName + */ + private def getFile(base: AbstractFile, clsName: String, suffix: String): AbstractFile = { + def ensureDirectory(dir: AbstractFile): AbstractFile = + if (dir.isDirectory) dir + else throw new FileConflictException(s"${base.path}/$clsName$suffix: ${dir.path} is not a directory", dir) var dir = base val pathParts = clsName.split("[./]").toList - for (part <- pathParts.init) { - dir = dir.subdirectoryNamed(part) - } - dir.fileNamed(pathParts.last + suffix) + for (part <- pathParts.init) dir = ensureDirectory(dir) subdirectoryNamed part + ensureDirectory(dir) fileNamed pathParts.last + suffix } private def getFile(sym: Symbol, clsName: String, suffix: String): AbstractFile = getFile(outputDirectory(sym), clsName, suffix) diff --git a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala index 8d9c4290ce..1cc46293ae 100644 --- a/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala +++ b/src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala @@ -105,29 +105,30 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { "Such classes will overwrite one another on case-insensitive filesystems.") } - debuglog("Created new bytecode generator for " + classes.size + " classes.") + debuglog(s"Created new bytecode generator for ${classes.size} classes.") val bytecodeWriter = initBytecodeWriter(sortedClasses filter isJavaEntryPoint) val plainCodeGen = new JPlainBuilder(bytecodeWriter) val mirrorCodeGen = new JMirrorBuilder(bytecodeWriter) val beanInfoCodeGen = new JBeanInfoBuilder(bytecodeWriter) - while(!sortedClasses.isEmpty) { - val c = sortedClasses.head - + def emitFor(c: IClass) { if (isStaticModule(c.symbol) && isTopLevelModule(c.symbol)) { - if (c.symbol.companionClass == NoSymbol) { - mirrorCodeGen.genMirrorClass(c.symbol, c.cunit) - } else { - log("No mirror class for module with linked class: " + c.symbol.fullName) - } + if (c.symbol.companionClass == NoSymbol) + mirrorCodeGen genMirrorClass (c.symbol, c.cunit) + else + log(s"No mirror class for module with linked class: ${c.symbol.fullName}") } + plainCodeGen genClass c + if (c.symbol hasAnnotation BeanInfoAttr) beanInfoCodeGen genBeanInfoClass c + } - plainCodeGen.genClass(c) - - if (c.symbol hasAnnotation BeanInfoAttr) { - beanInfoCodeGen.genBeanInfoClass(c) + while (!sortedClasses.isEmpty) { + val c = sortedClasses.head + try emitFor(c) + catch { + case e: FileConflictException => + c.cunit.error(c.symbol.pos, s"error writing ${c.symbol}: ${e.getMessage}") } - sortedClasses = sortedClasses.tail classes -= c.symbol // GC opportunity } @@ -454,7 +455,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { } // ----------------------------------------------------------------------------------------- - // utitilies useful when emitting plain, mirror, and beaninfo classes. + // utilities useful when emitting plain, mirror, and beaninfo classes. // ----------------------------------------------------------------------------------------- def writeIfNotTooBig(label: String, jclassName: String, jclass: asm.ClassWriter, sym: Symbol) { @@ -1397,7 +1398,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { addInnerClasses(clasz.symbol, jclass) jclass.visitEnd() writeIfNotTooBig("" + c.symbol.name, thisName, jclass, c.symbol) - } /** @@ -2903,7 +2903,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { JAVA_LANG_OBJECT.getInternalName, EMPTY_STRING_ARRAY) - log("Dumping mirror class for '%s'".format(mirrorName)) + log(s"Dumping mirror class for '$mirrorName'") // typestate: entering mode with valid call sequences: // [ visitSource ] [ visitOuterClass ] ( visitAnnotation | visitAttribute )* @@ -2926,8 +2926,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM { mirrorClass.visitEnd() writeIfNotTooBig("" + modsym.name, mirrorName, mirrorClass, modsym) } - - } // end of class JMirrorBuilder diff --git a/test/files/run/t5717.scala b/test/files/run/t5717.scala new file mode 100755 index 0000000000..a0997f5a49 --- /dev/null +++ b/test/files/run/t5717.scala @@ -0,0 +1,21 @@ +import scala.tools.partest._ +import java.io.File + +object Test extends StoreReporterDirectTest { + def code = ??? + + def compileCode(code: String) = { + val classpath = List(sys.props("partest.lib"), testOutput.path) mkString sys.props("path.separator") + compileString(newCompiler("-cp", classpath, "-d", testOutput.path))(code) + } + // TODO + // Don't assume output is on physical disk + // Let the compiler tell us output dir + // val sc = newCompiler("-cp", classpath, "-d", testOutput.path) + // val out = sc.settings.outputDirs.getSingleOutput.get + def show(): Unit = { + // Don't crash when we find a file 'a' where package 'a' should go. + scala.reflect.io.File(testOutput.path + "/a").writeAll("a") + compileCode("package a { class B }") + } +} |