Skip to content

Commit

Permalink
Add cancelation support for Kotlin frontend.
Browse files Browse the repository at this point in the history
This currently needs to be wired in via ProgressIndicatorAndCompilationCanceledStatus. Noticeably they are missing a lot of checks in between works that causes many large 1000ms+ delays in my local testing. If it turns out to be a problem, we will need to change couple of places to add checks in more places within kotlinc.

Also unfortunately the mechanism they have relies on static state so doesn't support multiplex workers. In the future I might workaround that using thread locals but for now I kept it simple.

PiperOrigin-RevId: 728739574
  • Loading branch information
gkdn authored and copybara-github committed Feb 19, 2025
1 parent 9889594 commit 88c9c4b
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.ir.util.statements
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus

/** Creates a J2CL Java AST from Kotlin IR. */
class CompilationUnitBuilder(
Expand Down Expand Up @@ -224,6 +225,8 @@ class CompilationUnitBuilder(
private fun convertClass(irClass: IrClass): Type {
val type = Type(getNameSourcePosition(irClass), environment.getDeclarationForType(irClass))
processEnclosedBy(type) {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()

// Skip synthetic declarations. Kotlinc adds synthetic declarations like (fake) override
// members
// to help with bridge synthesis and the resolution phase.
Expand All @@ -245,8 +248,9 @@ class CompilationUnitBuilder(
return type
}

private fun convertDeclaration(irDeclaration: IrDeclaration): List<Member> =
when (irDeclaration) {
private fun convertDeclaration(irDeclaration: IrDeclaration): List<Member> {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
return when (irDeclaration) {
is IrEnumEntry -> listOf(convertEnumEntry(irDeclaration))
is IrProperty -> convertProperty(irDeclaration)
// Lowering passes can add field on object classes.
Expand All @@ -255,6 +259,7 @@ class CompilationUnitBuilder(
is IrAnonymousInitializer -> listOf(convertAnonymousInitializer(irDeclaration))
else -> throw NotImplementedError("Declaration not yet supported: $irDeclaration")
}
}

private fun convertEnumEntry(irEnumEntry: IrEnumEntry): Field {
val initializerExpression = requireNotNull(irEnumEntry.initializerExpression).expression
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,27 +77,46 @@ import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.modules.TargetId
import org.jetbrains.kotlin.progress.CompilationCanceledStatus
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus

/** A parser for Kotlin sources that builds {@code CompilationtUnit}s. */
class KotlinParser(private val problems: Problems) {

/** Returns a list of compilation units after Kotlinc parsing. */
fun parseFiles(options: FrontendOptions): Library {
val packageInfoCache = PackageInfoCache(options.classpaths, problems)
problems.abortIfCancelled()
val packageAnnotationResolver = getPackageAnnotationResolver(options, packageInfoCache)
problems.abortIfCancelled()

val kotlincDisposable = Disposer.newDisposable("J2CL Root Disposable")
val compilerConfiguration = createCompilerConfiguration(options, packageInfoCache)
problems.abortIfCancelled()

check(compilerConfiguration.getBoolean(USE_FIR)) { "Kotlin/Closure only supports > K2" }

val compilationUnits =
parseFiles(compilerConfiguration, kotlincDisposable, packageAnnotationResolver)
try {
// TODO(b/148292139): This assumes single worker for given time and doesn't work with
// multiplex workers.
ProgressIndicatorAndCompilationCanceledStatus.setCompilationCanceledStatus(
object : CompilationCanceledStatus {
override fun checkCanceled() {
problems.abortIfCancelled()
}
}
)

val compilationUnits =
parseFiles(compilerConfiguration, kotlincDisposable, packageAnnotationResolver)

return Library.newBuilder()
.setCompilationUnits(compilationUnits)
.setDisposableListener { Disposer.dispose(kotlincDisposable) }
.build()
return Library.newBuilder()
.setCompilationUnits(compilationUnits)
.setDisposableListener { Disposer.dispose(kotlincDisposable) }
.build()
} finally {
ProgressIndicatorAndCompilationCanceledStatus.setCompilationCanceledStatus(null)
}
}

private fun parseFiles(
Expand All @@ -114,21 +133,20 @@ class KotlinParser(private val problems: Problems) {
EnvironmentConfigFiles.JVM_CONFIG_FILES,
messageCollector,
)
problems.abortIfCancelled()

val module = compilerConfiguration.get(MODULES)!![0]
val diagnosticsReporter = DiagnosticReporterFactory.createPendingReporter(messageCollector)
val sources = collectSources(compilerConfiguration, projectEnvironment, messageCollector)
problems.abortIfCancelled()

val analysisResults =
@OptIn(IncrementalCompilationApi::class)
compileModuleToAnalyzedFirViaLightTreeIncrementally(
projectEnvironment,
messageCollector,
compilerConfiguration,
ModuleCompilerInput(
TargetId(module),
collectSources(compilerConfiguration, projectEnvironment, messageCollector),
compilerConfiguration,
),
ModuleCompilerInput(TargetId(module), sources, compilerConfiguration),
diagnosticsReporter,
incrementalExcludesScope = null,
)
Expand All @@ -150,6 +168,7 @@ class KotlinParser(private val problems: Problems) {
.isIrBackend(true)
.diagnosticReporter(diagnosticsReporter)
.build()
problems.abortIfCancelled()

val compilationUnitBuilderExtension =
createAndRegisterCompilationUnitBuilder(
Expand All @@ -158,6 +177,7 @@ class KotlinParser(private val problems: Problems) {
state,
packageAnnotationResolver,
)
problems.abortIfCancelled()

val unused =
analysisResults.convertToIrAndActualizeForJvm(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import org.jetbrains.kotlin.ir.backend.js.lower.inline.RemoveInlineDeclarationsW
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.symbols.UnsafeDuringIrConstructionAPI
import org.jetbrains.kotlin.ir.util.SymbolTable
import org.jetbrains.kotlin.progress.ProgressIndicatorAndCompilationCanceledStatus

/** The list of lowering passes to run in execution order. */
private val loweringPassFactories: List<J2clLoweringPassFactory> = buildList {
Expand Down Expand Up @@ -262,6 +263,7 @@ private fun IrModuleFragment.lower(
) {
for (f in files) {
try {
ProgressIndicatorAndCompilationCanceledStatus.checkCanceled()
loweringFactory(context).lower(f)
} catch (e: CompilationException) {
e.initializeFileDetails(f)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ public boolean isCancelled() {

final String[] knownDelayedCalls = {
"com.google.j2cl.transpiler.frontend.javac.JavacParser.parseFiles",
// Kotlin frontend is currently missing a lot of checks in between large chunks of works.
"com.google.j2cl.transpiler.frontend.kotlin.KotlinParser.parseFiles",
};
for (String knownDelayedCall : knownDelayedCalls) {
Expand Down

0 comments on commit 88c9c4b

Please sign in to comment.