Skip to content

Commit 3468b2a

Browse files
committed
Merge pull request scala#2260 from som-snytt/issue/5717-assert-pkgdir
SI-5717 error when bytecode cannot be written
2 parents a16441c + cc485a9 commit 3468b2a

File tree

3 files changed

+54
-28
lines changed

3 files changed

+54
-28
lines changed

src/compiler/scala/tools/nsc/backend/jvm/BytecodeWriters.scala

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
package scala.tools.nsc
77
package backend.jvm
88

9-
import java.io.{ DataOutputStream, FileOutputStream, OutputStream, File => JFile }
9+
import java.io.{ DataOutputStream, FileOutputStream, IOException, OutputStream, File => JFile }
1010
import scala.tools.nsc.io._
1111
import scala.tools.nsc.util.ScalaClassLoader
1212
import java.util.jar.Attributes.Name
1313
import scala.language.postfixOps
1414

15+
/** Can't output a file due to the state of the file system. */
16+
class FileConflictException(msg: String, val file: AbstractFile) extends IOException(msg)
17+
1518
/** For the last mile: turning generated bytecode in memory into
1619
* something you can use. Has implementations for writing to class
1720
* files, jars, and disassembled/javap output.
@@ -20,16 +23,20 @@ trait BytecodeWriters {
2023
val global: Global
2124
import global._
2225

23-
private def outputDirectory(sym: Symbol): AbstractFile = (
24-
settings.outputDirs.outputDirFor(enteringFlatten(sym.sourceFile))
25-
)
26-
private def getFile(base: AbstractFile, /*cls.getName()*/ clsName: String, suffix: String): AbstractFile = {
26+
private def outputDirectory(sym: Symbol): AbstractFile =
27+
settings.outputDirs outputDirFor enteringFlatten(sym.sourceFile)
28+
29+
/**
30+
* @param clsName cls.getName
31+
*/
32+
private def getFile(base: AbstractFile, clsName: String, suffix: String): AbstractFile = {
33+
def ensureDirectory(dir: AbstractFile): AbstractFile =
34+
if (dir.isDirectory) dir
35+
else throw new FileConflictException(s"${base.path}/$clsName$suffix: ${dir.path} is not a directory", dir)
2736
var dir = base
2837
val pathParts = clsName.split("[./]").toList
29-
for (part <- pathParts.init) {
30-
dir = dir.subdirectoryNamed(part)
31-
}
32-
dir.fileNamed(pathParts.last + suffix)
38+
for (part <- pathParts.init) dir = ensureDirectory(dir) subdirectoryNamed part
39+
ensureDirectory(dir) fileNamed pathParts.last + suffix
3340
}
3441
private def getFile(sym: Symbol, clsName: String, suffix: String): AbstractFile =
3542
getFile(outputDirectory(sym), clsName, suffix)

src/compiler/scala/tools/nsc/backend/jvm/GenASM.scala

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -105,29 +105,30 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
105105
"Such classes will overwrite one another on case-insensitive filesystems.")
106106
}
107107

108-
debuglog("Created new bytecode generator for " + classes.size + " classes.")
108+
debuglog(s"Created new bytecode generator for ${classes.size} classes.")
109109
val bytecodeWriter = initBytecodeWriter(sortedClasses filter isJavaEntryPoint)
110110
val plainCodeGen = new JPlainBuilder(bytecodeWriter)
111111
val mirrorCodeGen = new JMirrorBuilder(bytecodeWriter)
112112
val beanInfoCodeGen = new JBeanInfoBuilder(bytecodeWriter)
113113

114-
while(!sortedClasses.isEmpty) {
115-
val c = sortedClasses.head
116-
114+
def emitFor(c: IClass) {
117115
if (isStaticModule(c.symbol) && isTopLevelModule(c.symbol)) {
118-
if (c.symbol.companionClass == NoSymbol) {
119-
mirrorCodeGen.genMirrorClass(c.symbol, c.cunit)
120-
} else {
121-
log("No mirror class for module with linked class: " + c.symbol.fullName)
122-
}
116+
if (c.symbol.companionClass == NoSymbol)
117+
mirrorCodeGen genMirrorClass (c.symbol, c.cunit)
118+
else
119+
log(s"No mirror class for module with linked class: ${c.symbol.fullName}")
123120
}
121+
plainCodeGen genClass c
122+
if (c.symbol hasAnnotation BeanInfoAttr) beanInfoCodeGen genBeanInfoClass c
123+
}
124124

125-
plainCodeGen.genClass(c)
126-
127-
if (c.symbol hasAnnotation BeanInfoAttr) {
128-
beanInfoCodeGen.genBeanInfoClass(c)
125+
while (!sortedClasses.isEmpty) {
126+
val c = sortedClasses.head
127+
try emitFor(c)
128+
catch {
129+
case e: FileConflictException =>
130+
c.cunit.error(c.symbol.pos, s"error writing ${c.symbol}: ${e.getMessage}")
129131
}
130-
131132
sortedClasses = sortedClasses.tail
132133
classes -= c.symbol // GC opportunity
133134
}
@@ -454,7 +455,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
454455
}
455456

456457
// -----------------------------------------------------------------------------------------
457-
// utitilies useful when emitting plain, mirror, and beaninfo classes.
458+
// utilities useful when emitting plain, mirror, and beaninfo classes.
458459
// -----------------------------------------------------------------------------------------
459460

460461
def writeIfNotTooBig(label: String, jclassName: String, jclass: asm.ClassWriter, sym: Symbol) {
@@ -1397,7 +1398,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
13971398
addInnerClasses(clasz.symbol, jclass)
13981399
jclass.visitEnd()
13991400
writeIfNotTooBig("" + c.symbol.name, thisName, jclass, c.symbol)
1400-
14011401
}
14021402

14031403
/**
@@ -2903,7 +2903,7 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
29032903
JAVA_LANG_OBJECT.getInternalName,
29042904
EMPTY_STRING_ARRAY)
29052905

2906-
log("Dumping mirror class for '%s'".format(mirrorName))
2906+
log(s"Dumping mirror class for '$mirrorName'")
29072907

29082908
// typestate: entering mode with valid call sequences:
29092909
// [ visitSource ] [ visitOuterClass ] ( visitAnnotation | visitAttribute )*
@@ -2926,8 +2926,6 @@ abstract class GenASM extends SubComponent with BytecodeWriters with GenJVMASM {
29262926
mirrorClass.visitEnd()
29272927
writeIfNotTooBig("" + modsym.name, mirrorName, mirrorClass, modsym)
29282928
}
2929-
2930-
29312929
} // end of class JMirrorBuilder
29322930

29332931

test/files/run/t5717.scala

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import scala.tools.partest._
2+
import java.io.File
3+
4+
object Test extends StoreReporterDirectTest {
5+
def code = ???
6+
7+
def compileCode(code: String) = {
8+
val classpath = List(sys.props("partest.lib"), testOutput.path) mkString sys.props("path.separator")
9+
compileString(newCompiler("-cp", classpath, "-d", testOutput.path))(code)
10+
}
11+
// TODO
12+
// Don't assume output is on physical disk
13+
// Let the compiler tell us output dir
14+
// val sc = newCompiler("-cp", classpath, "-d", testOutput.path)
15+
// val out = sc.settings.outputDirs.getSingleOutput.get
16+
def show(): Unit = {
17+
// Don't crash when we find a file 'a' where package 'a' should go.
18+
scala.reflect.io.File(testOutput.path + "/a").writeAll("a")
19+
compileCode("package a { class B }")
20+
}
21+
}

0 commit comments

Comments
 (0)