@@ -13,11 +13,13 @@ import scala.tools.nsc.backend.jvm._
1313import dotty .tools .dotc
1414import dotty .tools .dotc .backend .jvm .DottyPrimitives
1515import dotty .tools .dotc .transform .Erasure
16+ import dotty .tools .dotc .transform .SymUtils ._
1617import dotty .tools .dotc .interfaces
1718import java .util .Optional
1819
1920import scala .reflect .ClassTag
2021import dotty .tools .dotc .core ._
22+ import dotty .tools .dotc .sbt .ExtractDependencies
2123import Periods ._
2224import SymDenotations ._
2325import Contexts ._
@@ -113,14 +115,16 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
113115
114116 /* ---------------- q2 ---------------- */
115117
116- case class Item2 (arrivalPos : Int ,
117- mirror : asm.tree.ClassNode ,
118- plain : asm.tree.ClassNode ,
119- outFolder : scala.tools.nsc.io.AbstractFile ) {
118+ case class SubItem2 (classNode : asm.tree.ClassNode ,
119+ file : scala.tools.nsc.io.AbstractFile )
120+
121+ case class Item2 (arrivalPos : Int ,
122+ mirror : SubItem2 ,
123+ plain : SubItem2 ) {
120124 def isPoison = { arrivalPos == Int .MaxValue }
121125 }
122126
123- private val poison2 = Item2 (Int .MaxValue , null , null , null )
127+ private val poison2 = Item2 (Int .MaxValue , null , null )
124128 private val q2 = new _root_.java.util.LinkedList [Item2 ]
125129
126130 /* ---------------- q3 ---------------- */
@@ -134,13 +138,13 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
134138 */
135139 case class SubItem3 (
136140 jclassName : String ,
137- jclassBytes : Array [Byte ]
141+ jclassBytes : Array [Byte ],
142+ jclassFile : scala.tools.nsc.io.AbstractFile
138143 )
139144
140145 case class Item3 (arrivalPos : Int ,
141146 mirror : SubItem3 ,
142- plain : SubItem3 ,
143- outFolder : scala.tools.nsc.io.AbstractFile ) {
147+ plain : SubItem3 ) {
144148
145149 def isPoison = { arrivalPos == Int .MaxValue }
146150 }
@@ -151,15 +155,31 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
151155 else 1
152156 }
153157 }
154- private val poison3 = Item3 (Int .MaxValue , null , null , null )
158+ private val poison3 = Item3 (Int .MaxValue , null , null )
155159 private val q3 = new java.util.PriorityQueue [Item3 ](1000 , i3comparator)
156160
157161 /*
158162 * Pipeline that takes ClassDefs from queue-1, lowers them into an intermediate form, placing them on queue-2
159163 */
160164 class Worker1 (needsOutFolder : Boolean ) {
161165
162- val caseInsensitively = scala.collection.mutable.HashMap .empty[String , Symbol ]
166+ private val lowerCaseNames = mutable.HashMap .empty[String , Symbol ]
167+ private def checkForCaseConflict (javaClassName : String , classSymbol : Symbol ) = {
168+ val lowerCaseName = javaClassName.toLowerCase
169+ lowerCaseNames.get(lowerCaseName) match {
170+ case None =>
171+ lowerCaseNames.put(lowerCaseName, classSymbol)
172+ case Some (dupClassSym) =>
173+ // Order is not deterministic so we enforce lexicographic order between the duplicates for error-reporting
174+ val (cl1, cl2) =
175+ if (classSymbol.effectiveName.toString < dupClassSym.effectiveName.toString) (classSymbol, dupClassSym)
176+ else (dupClassSym, classSymbol)
177+ ctx.atPhase(ctx.typerPhase) { implicit ctx =>
178+ ctx.warning(s " ${cl1.show} differs only in case from ${cl2.showLocated}. " +
179+ " Such classes will overwrite one another on case-insensitive filesystems." , cl1.pos)
180+ }
181+ }
182+ }
163183
164184 def run (): Unit = {
165185 while (true ) {
@@ -189,30 +209,6 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
189209 val Item1 (arrivalPos, cd, cunit) = item
190210 val claszSymbol = cd.symbol
191211
192- // GenASM checks this before classfiles are emitted, https://github.com/scala/scala/commit/e4d1d930693ac75d8eb64c2c3c69f2fc22bec739
193- def checkName (claszSymbol : Symbol ): Unit = {
194- val lowercaseJavaClassName = claszSymbol.effectiveName.toString.toLowerCase
195- caseInsensitively.get(lowercaseJavaClassName) match {
196- case None =>
197- caseInsensitively.put(lowercaseJavaClassName, claszSymbol)
198- case Some (dupClassSym) =>
199- if (claszSymbol.effectiveName.toString != dupClassSym.effectiveName.toString) {
200- // Order is not deterministic so we enforce lexicographic order between the duplicates for error-reporting
201- val (cl1, cl2) =
202- if (claszSymbol.effectiveName.toString < dupClassSym.effectiveName.toString) (claszSymbol, dupClassSym)
203- else (dupClassSym, claszSymbol)
204- ctx.warning(s " Class ${cl1.effectiveName} differs only in case from ${cl2.effectiveName}. " +
205- " Such classes will overwrite one another on case-insensitive filesystems." , cl1.pos)
206- }
207- }
208- }
209- checkName(claszSymbol)
210- if (int.symHelper(claszSymbol).isModuleClass) {
211- val companionModule = claszSymbol.companionModule
212- if (int.symHelper(companionModule.owner).isPackageClass)
213- checkName(companionModule)
214- }
215-
216212 // -------------- mirror class, if needed --------------
217213 val mirrorC =
218214 if (int.symHelper(claszSymbol).isTopLevelModuleClass) {
@@ -253,12 +249,50 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
253249 }
254250
255251
252+ // ----------- create files
253+
254+ val classNodes = List (mirrorC, plainC)
255+ val classFiles = classNodes.map(cls =>
256+ if (outF != null && cls != null ) {
257+ try {
258+ checkForCaseConflict(cls.name, claszSymbol)
259+ getFileForClassfile(outF, cls.name, " .class" )
260+ } catch {
261+ case e : FileConflictException =>
262+ ctx.error(s " error writing ${cls.name}: ${e.getMessage}" )
263+ null
264+ }
265+ } else null
266+ )
267+
268+ // ----------- compiler and sbt's callbacks
269+
270+ val (fullClassName, isLocal) = ctx.atPhase(ctx.sbtExtractDependenciesPhase) { implicit ctx =>
271+ (ExtractDependencies .classNameAsString(claszSymbol), claszSymbol.isLocal)
272+ }
273+
274+ for ((cls, clsFile) <- classNodes.zip(classFiles)) {
275+ if (cls != null ) {
276+ val className = cls.name.replace('/' , '.' )
277+ if (ctx.compilerCallback != null )
278+ ctx.compilerCallback.onClassGenerated(sourceFile, convertAbstractFile(clsFile), className)
279+ if (ctx.sbtCallback != null ) {
280+ if (isLocal)
281+ ctx.sbtCallback.generatedLocalClass(sourceFile.jfile.orElse(null ), clsFile.file)
282+ else {
283+ ctx.sbtCallback.generatedNonLocalClass(sourceFile.jfile.orElse(null ), clsFile.file,
284+ className, fullClassName)
285+ }
286+ }
287+ }
288+ }
289+
256290 // ----------- hand over to pipeline-2
257291
258292 val item2 =
259293 Item2 (arrivalPos,
260- mirrorC, plainC ,
261- outF )
294+ SubItem2 ( mirrorC, classFiles( 0 )) ,
295+ SubItem2 (plainC, classFiles( 1 )) )
262296
263297 q2 add item2 // at the very end of this method so that no Worker2 thread starts mutating before we're done.
264298
@@ -288,12 +322,12 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
288322 }
289323 else {
290324 try {
291- localOptimizations(item.plain)
325+ localOptimizations(item.plain.classNode )
292326 addToQ3(item)
293327 } catch {
294328 case ex : Throwable =>
295329 ex.printStackTrace()
296- ctx.error(s " Error while emitting ${item.plain.name}\n ${ex.getMessage}" )
330+ ctx.error(s " Error while emitting ${item.plain.classNode. name}\n ${ex.getMessage}" )
297331 }
298332 }
299333 }
@@ -307,18 +341,17 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
307341 cw.toByteArray
308342 }
309343
310- val Item2 (arrivalPos, mirror, plain, outFolder ) = item
344+ val Item2 (arrivalPos, SubItem2 ( mirror, mirrorFile), SubItem2 ( plain, plainFile) ) = item
311345
312- val mirrorC = if (mirror == null ) null else SubItem3 (mirror.name, getByteArray(mirror))
313- val plainC = SubItem3 (plain.name, getByteArray(plain))
346+ val mirrorC = if (mirror == null ) null else SubItem3 (mirror.name, getByteArray(mirror), mirrorFile )
347+ val plainC = SubItem3 (plain.name, getByteArray(plain), plainFile )
314348
315349 if (AsmUtils .traceSerializedClassEnabled && plain.name.contains(AsmUtils .traceSerializedClassPattern)) {
316350 if (mirrorC != null ) AsmUtils .traceClass(mirrorC.jclassBytes)
317351 AsmUtils .traceClass(plainC.jclassBytes)
318352 }
319353
320- q3 add Item3 (arrivalPos, mirrorC, plainC, outFolder)
321-
354+ q3 add Item3 (arrivalPos, mirrorC, plainC)
322355 }
323356
324357 } // end of class BCodePhase.Worker2
@@ -416,25 +449,10 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
416449 /* Pipeline that writes classfile representations to disk. */
417450 private def drainQ3 () = {
418451
419- def sendToDisk (cfr : SubItem3 , outFolder : scala.tools.nsc.io. AbstractFile ): Unit = {
452+ def sendToDisk (cfr : SubItem3 ): Unit = {
420453 if (cfr != null ){
421- val SubItem3 (jclassName, jclassBytes) = cfr
422- try {
423- val outFile =
424- if (outFolder == null ) null
425- else getFileForClassfile(outFolder, jclassName, " .class" )
426- bytecodeWriter.writeClass(jclassName, jclassName, jclassBytes, outFile)
427-
428- val className = jclassName.replace('/' , '.' )
429- if (ctx.compilerCallback != null )
430- ctx.compilerCallback.onClassGenerated(sourceFile, convertAbstractFile(outFile), className)
431- if (ctx.sbtCallback != null )
432- ctx.sbtCallback.generatedClass(sourceFile.jfile.orElse(null ), outFile.file, className)
433- }
434- catch {
435- case e : FileConflictException =>
436- ctx.error(s " error writing $jclassName: ${e.getMessage}" )
437- }
454+ val SubItem3 (jclassName, jclassBytes, jclassFile) = cfr
455+ bytecodeWriter.writeClass(jclassName, jclassName, jclassBytes, jclassFile)
438456 }
439457 }
440458
@@ -447,9 +465,8 @@ class GenBCodePipeline(val entryPoints: List[Symbol], val int: DottyBackendInter
447465 moreComing = ! incoming.isPoison
448466 if (moreComing) {
449467 val item = incoming
450- val outFolder = item.outFolder
451- sendToDisk(item.mirror, outFolder)
452- sendToDisk(item.plain, outFolder)
468+ sendToDisk(item.mirror)
469+ sendToDisk(item.plain)
453470 expected += 1
454471 }
455472 }
0 commit comments