Karel Petránek
11/11/2021, 12:53 AMcompileProductionLibraryKotlinJs
task and it seems JS gets compiled twice - once into a klib where the compiler plugin is called correctly and later into a JS library where the compiler plugin is not invoked at all. This means that my changes are visible in the klib but not in the resulting JS library code. Is this an expected behavior? Can I get the IR plugin to execute every time a JS compilation is running? It also seems inefficient to run it twice (I don’t necessarily need the klib...).
I’m on Kotlin 1.5.31, Gradle 7.2. My Gradle configuration:
js(IR) {
moduleName = "facemoji-core"
browser {
webpackTask {
output.libraryTarget = "commonjs-module"
}
}
binaries.library()
compilations.all {
kotlinOptions {
freeCompilerArgs = freeCompilerArgs + "-Xopt-in=kotlin.ExperimentalStdlibApi -Xopt-in=kotlin.RequiresOptIn"
}
}
}
Karel Petránek
11/11/2021, 1:00 AMshikasd
11/11/2021, 11:00 AMKarel Petránek
11/11/2021, 1:09 PMshikasd
11/11/2021, 1:39 PMshikasd
11/11/2021, 1:40 PMKarel Petránek
11/11/2021, 2:13 PMshikasd
11/11/2021, 4:58 PMshikasd
11/11/2021, 4:59 PMKarel Petránek
11/11/2021, 11:07 PMclass JSExportTransformer( private val pluginContext: IrPluginContext
) : IrElementTransformerVoidWithContext() {
private val jsExportFqName = FqName("kotlin.js.JsExport")
private val jsExperimentalExportFqName = FqName("kotlin.js.ExperimentalJsExport")
private val jsExportConstructor = pluginContext.referenceClass(jsExportFqName)!!.constructors.first()
private val jsExperimentalExportConstructor = pluginContext.referenceClass(jsExperimentalExportFqName)!!.constructors.first()
private fun createExportAnnotations(): List<IrConstructorCall> {
val jsExportCall = IrConstructorCallImpl.fromSymbolOwner(
jsExportConstructor.owner.returnType,
jsExportConstructor,
)
val jsExperimentalExportCall = IrConstructorCallImpl.fromSymbolOwner(
jsExperimentalExportConstructor.owner.returnType,
jsExperimentalExportConstructor
)
return listOf(jsExperimentalExportCall, jsExportCall)
}
override fun visitClassNew(declaration: IrClass): IrStatement {
val isPublic = declaration.visibility.isPublicAPI
val isExportable = declaration.kind != ClassKind.ENUM_ENTRY && declaration.isInline
if (!isPublic || !isExportable || declaration.isExpect || declaration.isInner || declaration.isCompanion || declaration.isAnnotationClass || declaration.isAnnotatedWithDeprecated) {
return super.visitClassNew(declaration)
}
val alreadyAnnotated = declaration.annotations.any { it.type.classFqName?.toString() == jsExportFqName.toString() }
if (!alreadyAnnotated) {
declaration.annotations += createExportAnnotations()
}
return super.visitClassNew(declaration)
}
}
I checked in debugger that the code gets called (during the klib phase) and also checked the IR deserialization in the JS phase and the annotations are gone. Of course, the symbols are not exported to JS.
I invoke this transformer in the generate() method of the IR extension plugin:
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
// Checks for JS platform etc. omitted for brevity
val transformer = JSExportTransformer(pluginContext)
moduleFragment.transform(transformer, null)
}
Sorry for bothering you with this, I’ve been trying to figure out why the annotations disappear but so far I’m pretty lost as to where to look for further hints.shikasd
11/11/2021, 11:15 PMshikasd
11/11/2021, 11:17 PMKarel Petránek
11/12/2021, 7:23 AM