Skip to content

Commit 96bc0e6

Browse files
committed
local classes early
1 parent 84fdce2 commit 96bc0e6

File tree

6 files changed

+95
-8
lines changed

6 files changed

+95
-8
lines changed

compiler/src/dotty/tools/backend/jvm/CodeGen.scala

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,17 +126,16 @@ class CodeGen(val int: DottyBackendInterface, val primitives: DottyPrimitives)(
126126

127127
// Creates a callback that will be evaluated in PostProcessor after creating a file
128128
private def onFileCreated(cls: ClassNode, claszSymbol: Symbol, sourceFile: util.SourceFile): AbstractFile => Unit = {
129-
val (fullClassName, isLocal) = atPhase(sbtExtractDependenciesPhase) {
130-
(ExtractDependencies.classNameAsString(claszSymbol), claszSymbol.isLocal)
129+
val isLocal = atPhase(sbtExtractDependenciesPhase) {
130+
claszSymbol.isLocal
131131
}
132132
clsFile => {
133133
val className = cls.name.replace('/', '.')
134134
if (ctx.compilerCallback != null)
135135
ctx.compilerCallback.onClassGenerated(sourceFile, convertAbstractFile(clsFile), className)
136136

137-
ctx.withIncCallback: cb =>
138-
if (isLocal) cb.generatedLocalClass(sourceFile, clsFile.jpath)
139-
else cb.generatedNonLocalClass(sourceFile, clsFile.jpath, className, fullClassName)
137+
if isLocal then
138+
ctx.withIncCallback(_.generatedLocalClass(sourceFile, clsFile.jpath))
140139
}
141140
}
142141

compiler/src/dotty/tools/dotc/sbt/ExtractAPI.scala

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,16 @@ import Trees.*
1515
import Types.*
1616
import Symbols.*
1717
import Names.*
18+
import StdNames.str
1819
import NameOps.*
1920
import inlines.Inlines
2021
import transform.ValueClasses
2122
import transform.SymUtils.*
22-
import dotty.tools.io.{File, FileExtension}
23+
import dotty.tools.io.{File, FileExtension, JarArchive}
24+
import util.{Property, SourceFile}
2325
import java.io.PrintWriter
2426

27+
import ExtractAPI.NonLocalClassSymbolsInCurrentUnits
2528

2629
import scala.collection.mutable
2730
import scala.util.hashing.MurmurHash3
@@ -62,13 +65,59 @@ class ExtractAPI extends Phase {
6265
// definitions, and `PostTyper` does not change definitions).
6366
override def runsAfter: Set[String] = Set(transform.PostTyper.name)
6467

68+
override def runOn(units: List[CompilationUnit])(using Context): List[CompilationUnit] =
69+
val nonLocalClassSymbols = new mutable.HashSet[Symbol]
70+
val ctx0 = ctx.withProperty(NonLocalClassSymbolsInCurrentUnits, Some(nonLocalClassSymbols))
71+
val units0 = super.runOn(units)(using ctx0)
72+
ctx.withIncCallback(recordNonLocalClasses(nonLocalClassSymbols, _))
73+
units0
74+
end runOn
75+
76+
private def recordNonLocalClasses(nonLocalClassSymbols: mutable.HashSet[Symbol], cb: interfaces.IncrementalCallback)(using Context): Unit =
77+
for cls <- nonLocalClassSymbols if !cls.isLocal do
78+
val sourceFile = cls.source
79+
if sourceFile.exists && cls.isDefinedInCurrentRun then
80+
recordNonLocalClass(cls, sourceFile, cb)
81+
cb.apiPhaseCompleted()
82+
cb.dependencyPhaseCompleted()
83+
84+
private def recordNonLocalClass(cls: Symbol, sourceFile: SourceFile, cb: interfaces.IncrementalCallback)(using Context): Unit =
85+
def registerProductNames(fullClassName: String, binaryClassName: String) =
86+
val pathToClassFile = s"${binaryClassName.replace('.', java.io.File.separatorChar)}.class"
87+
88+
val classFile = {
89+
ctx.settings.outputDir.value match {
90+
case jar: JarArchive =>
91+
new java.io.File(s"$jar!$pathToClassFile")
92+
case outputDir =>
93+
new java.io.File(outputDir.file, pathToClassFile)
94+
}
95+
}
96+
97+
cb.generatedNonLocalClass(sourceFile, classFile.toPath(), binaryClassName, fullClassName)
98+
end registerProductNames
99+
100+
val fullClassName = atPhase(sbtExtractDependenciesPhase) {
101+
ExtractDependencies.classNameAsString(cls)
102+
}
103+
val binaryClassName = cls.binaryClassName
104+
registerProductNames(fullClassName, binaryClassName)
105+
106+
// Register the names of top-level module symbols that emit two class files
107+
val isTopLevelUniqueModule =
108+
cls.owner.is(PackageClass) && cls.is(ModuleClass) && cls.companionClass == NoSymbol
109+
if isTopLevelUniqueModule || cls.isPackageObject then
110+
registerProductNames(fullClassName, binaryClassName.stripSuffix(str.MODULE_SUFFIX))
111+
end recordNonLocalClass
112+
65113
override def run(using Context): Unit = {
66114
val unit = ctx.compilationUnit
67115
val sourceFile = unit.source
68116
ctx.withIncCallback: cb =>
69117
cb.startSource(sourceFile)
70118

71-
val apiTraverser = new ExtractAPICollector
119+
val nonLocalClassSymbols = ctx.property(NonLocalClassSymbolsInCurrentUnits).get
120+
val apiTraverser = ExtractAPICollector(nonLocalClassSymbols)
72121
val classes = apiTraverser.apiSource(unit.tpdTree)
73122
val mainClasses = apiTraverser.mainClasses
74123

@@ -92,6 +141,8 @@ object ExtractAPI:
92141
val name: String = "sbt-api"
93142
val description: String = "sends a representation of the API of classes to sbt"
94143

144+
private val NonLocalClassSymbolsInCurrentUnits: Property.Key[mutable.HashSet[Symbol]] = Property.Key()
145+
95146
/** Extracts full (including private members) API representation out of Symbols and Types.
96147
*
97148
* The exact representation used for each type is not important: the only thing
@@ -134,7 +185,7 @@ object ExtractAPI:
134185
* without going through an intermediate representation, see
135186
* http://www.scala-sbt.org/0.13/docs/Understanding-Recompilation.html#Hashing+an+API+representation
136187
*/
137-
private class ExtractAPICollector(using Context) extends ThunkHolder {
188+
private class ExtractAPICollector(nonLocalClassSymbols: mutable.HashSet[Symbol])(using Context) extends ThunkHolder {
138189
import tpd.*
139190
import xsbti.api
140191

@@ -252,6 +303,7 @@ private class ExtractAPICollector(using Context) extends ThunkHolder {
252303
childrenOfSealedClass, topLevel, tparams)
253304

254305
allNonLocalClassesInSrc += cl
306+
nonLocalClassSymbols += sym
255307

256308
if (sym.isStatic && !sym.is(Trait) && ctx.platform.hasMainMethod(sym)) {
257309
// If sym is an object, all main methods count, otherwise only @static ones count.

compiler/src/dotty/tools/dotc/sbt/interfaces/IncrementalCallback.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
/* User code should not implement this interface, it is intended to be a wrapper around xsbti.AnalysisCallback. */
99
public interface IncrementalCallback {
10+
1011
default void api(SourceFile sourceFile, xsbti.api.ClassLike classApi) {
1112
}
1213

@@ -36,4 +37,10 @@ default void generatedLocalClass(SourceFile source, Path classFile) {
3637
default void generatedNonLocalClass(SourceFile source, Path classFile, String binaryClassName,
3738
String srcClassName) {
3839
}
40+
41+
default void apiPhaseCompleted() {
42+
}
43+
44+
default void dependencyPhaseCompleted() {
45+
}
3946
}

sbt-bridge/src/dotty/tools/xsbt/IncrementalCallback.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import dotty.tools.dotc.util.SourceFile;
44
import java.util.function.Function;
5+
import java.util.function.Predicate;
56

67
public final class IncrementalCallback implements dotty.tools.dotc.sbt.interfaces.IncrementalCallback {
78

@@ -57,4 +58,14 @@ public void generatedLocalClass(SourceFile source, java.nio.file.Path classFile)
5758
public void generatedNonLocalClass(SourceFile source, java.nio.file.Path classFile, String binaryClassName, String srcClassName) {
5859
delegate.generatedNonLocalClass(asVirtualFile.apply(source), classFile, binaryClassName, srcClassName);
5960
}
61+
62+
@Override
63+
public void apiPhaseCompleted() {
64+
delegate.apiPhaseCompleted();
65+
}
66+
67+
@Override
68+
public void dependencyPhaseCompleted() {
69+
delegate.dependencyPhaseCompleted();
70+
}
6071
}

sbt-bridge/src/dotty/tools/xsbt/OldIncrementalCallback.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,14 @@ public void generatedLocalClass(SourceFile source, java.nio.file.Path classFile)
7171
public void generatedNonLocalClass(SourceFile source, java.nio.file.Path classFile, String binaryClassName, String srcClassName) {
7272
delegate.generatedNonLocalClass(asJavaFile(source), classFile.toFile(), binaryClassName, srcClassName);
7373
}
74+
75+
@Override
76+
public void apiPhaseCompleted() {
77+
delegate.apiPhaseCompleted();
78+
}
79+
80+
@Override
81+
public void dependencyPhaseCompleted() {
82+
delegate.dependencyPhaseCompleted();
83+
}
7484
}

sbt-test/source-dependencies/compactify/src/main/scala/Nested.scala

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,11 @@ object TopLevel3
4141
class TopLevel4
4242

4343
object TopLevelModuleSuffix$
44+
45+
// will generate a package object wrapper
46+
val topLevelVal = 23
47+
48+
// explicit package object
49+
package object inner {
50+
val innerVal = 23
51+
}

0 commit comments

Comments
 (0)