[JS IR] Pass forward -Xir-property-lazy-initialization option

Pass forward -Xir-property-lazy-initialization option
for the incremental cache.

^KT-50175 Fixed
This commit is contained in:
Alexander Korepanov
2022-01-13 20:59:21 +03:00
committed by Space
parent 2f853175c7
commit 59173baf5a
17 changed files with 156 additions and 43 deletions
@@ -153,6 +153,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
val sourcesFiles = environmentForJS.getSourceFiles() val sourcesFiles = environmentForJS.getSourceFiles()
configurationJs.put(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE, arguments.allowKotlinPackage) configurationJs.put(CLIConfigurationKeys.ALLOW_KOTLIN_PACKAGE, arguments.allowKotlinPackage)
configurationJs.put(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION, arguments.irPropertyLazyInitialization)
if (!checkKotlinPackageUsage(environmentForJS.configuration, sourcesFiles)) return ExitCode.COMPILATION_ERROR if (!checkKotlinPackageUsage(environmentForJS.configuration, sourcesFiles)) return ExitCode.COMPILATION_ERROR
@@ -388,7 +389,6 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
arguments.irDceRuntimeDiagnostic, arguments.irDceRuntimeDiagnostic,
messageCollector messageCollector
), ),
propertyLazyInitialization = arguments.irPropertyLazyInitialization,
baseClassIntoMetadata = arguments.irBaseClassInMetadata, baseClassIntoMetadata = arguments.irBaseClassInMetadata,
safeExternalBoolean = arguments.irSafeExternalBoolean, safeExternalBoolean = arguments.irSafeExternalBoolean,
safeExternalBooleanDiagnostic = RuntimeDiagnostic.resolve( safeExternalBooleanDiagnostic = RuntimeDiagnostic.resolve(
@@ -56,7 +56,6 @@ class JsIrBackendContext(
override val scriptMode: Boolean = false, override val scriptMode: Boolean = false,
override val es6mode: Boolean = false, override val es6mode: Boolean = false,
val dceRuntimeDiagnostic: RuntimeDiagnostic? = null, val dceRuntimeDiagnostic: RuntimeDiagnostic? = null,
propertyLazyInitialization: Boolean = false,
val baseClassIntoMetadata: Boolean = false, val baseClassIntoMetadata: Boolean = false,
val safeExternalBoolean: Boolean = false, val safeExternalBoolean: Boolean = false,
val safeExternalBooleanDiagnostic: RuntimeDiagnostic? = null, val safeExternalBooleanDiagnostic: RuntimeDiagnostic? = null,
@@ -141,7 +140,7 @@ class JsIrBackendContext(
override val reflectionSymbols: ReflectionSymbols get() = intrinsics.reflectionSymbols override val reflectionSymbols: ReflectionSymbols get() = intrinsics.reflectionSymbols
override val propertyLazyInitialization: PropertyLazyInitialization = PropertyLazyInitialization( override val propertyLazyInitialization: PropertyLazyInitialization = PropertyLazyInitialization(
enabled = propertyLazyInitialization, enabled = configuration.getBoolean(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION),
eagerInitialization = symbolTable.referenceClass(getJsInternalClass("EagerInitialization")) eagerInitialization = symbolTable.referenceClass(getJsInternalClass("EagerInitialization"))
) )
@@ -51,7 +51,6 @@ fun compile(
exportedDeclarations: Set<FqName> = emptySet(), exportedDeclarations: Set<FqName> = emptySet(),
dceRuntimeDiagnostic: RuntimeDiagnostic? = null, dceRuntimeDiagnostic: RuntimeDiagnostic? = null,
es6mode: Boolean = false, es6mode: Boolean = false,
propertyLazyInitialization: Boolean,
verifySignatures: Boolean = true, verifySignatures: Boolean = true,
baseClassIntoMetadata: Boolean = false, baseClassIntoMetadata: Boolean = false,
safeExternalBoolean: Boolean = false, safeExternalBoolean: Boolean = false,
@@ -76,7 +75,6 @@ fun compile(
exportedDeclarations, exportedDeclarations,
dceRuntimeDiagnostic, dceRuntimeDiagnostic,
es6mode, es6mode,
propertyLazyInitialization,
baseClassIntoMetadata, baseClassIntoMetadata,
safeExternalBoolean, safeExternalBoolean,
safeExternalBooleanDiagnostic, safeExternalBooleanDiagnostic,
@@ -97,7 +95,6 @@ fun compileIr(
exportedDeclarations: Set<FqName>, exportedDeclarations: Set<FqName>,
dceRuntimeDiagnostic: RuntimeDiagnostic?, dceRuntimeDiagnostic: RuntimeDiagnostic?,
es6mode: Boolean, es6mode: Boolean,
propertyLazyInitialization: Boolean,
baseClassIntoMetadata: Boolean, baseClassIntoMetadata: Boolean,
safeExternalBoolean: Boolean, safeExternalBoolean: Boolean,
safeExternalBooleanDiagnostic: RuntimeDiagnostic?, safeExternalBooleanDiagnostic: RuntimeDiagnostic?,
@@ -123,7 +120,6 @@ fun compileIr(
configuration, configuration,
es6mode = es6mode, es6mode = es6mode,
dceRuntimeDiagnostic = dceRuntimeDiagnostic, dceRuntimeDiagnostic = dceRuntimeDiagnostic,
propertyLazyInitialization = propertyLazyInitialization,
baseClassIntoMetadata = baseClassIntoMetadata, baseClassIntoMetadata = baseClassIntoMetadata,
safeExternalBoolean = safeExternalBoolean, safeExternalBoolean = safeExternalBoolean,
safeExternalBooleanDiagnostic = safeExternalBooleanDiagnostic, safeExternalBooleanDiagnostic = safeExternalBooleanDiagnostic,
@@ -42,7 +42,6 @@ fun compileWithIC(
es6mode: Boolean = false, es6mode: Boolean = false,
multiModule: Boolean = false, multiModule: Boolean = false,
relativeRequirePath: Boolean = false, relativeRequirePath: Boolean = false,
propertyLazyInitialization: Boolean = false,
verifySignatures: Boolean = true, verifySignatures: Boolean = true,
baseClassIntoMetadata: Boolean = false, baseClassIntoMetadata: Boolean = false,
lowerPerModule: Boolean = false, lowerPerModule: Boolean = false,
@@ -67,7 +66,6 @@ fun compileWithIC(
configuration, configuration,
es6mode = es6mode, es6mode = es6mode,
dceRuntimeDiagnostic = dceRuntimeDiagnostic, dceRuntimeDiagnostic = dceRuntimeDiagnostic,
propertyLazyInitialization = propertyLazyInitialization,
baseClassIntoMetadata = baseClassIntoMetadata, baseClassIntoMetadata = baseClassIntoMetadata,
safeExternalBoolean = safeExternalBoolean, safeExternalBoolean = safeExternalBoolean,
safeExternalBooleanDiagnostic = safeExternalBooleanDiagnostic, safeExternalBooleanDiagnostic = safeExternalBooleanDiagnostic,
@@ -9,12 +9,13 @@ import java.io.File
import java.io.PrintWriter import java.io.PrintWriter
// TODO md5 hash // TODO md5 hash
data class CacheInfo(val path: String, val libPath: String, var flatHash: ULong, var transHash: ULong) { data class CacheInfo(val path: String, val libPath: String, var flatHash: ULong, var transHash: ULong, var configHash: ULong) {
fun save() { fun save() {
PrintWriter(File(File(path), "info")).use { PrintWriter(File(File(path), "info")).use {
it.println(libPath) it.println(libPath)
it.println(flatHash.toString(16)) it.println(flatHash.toString(16))
it.println(transHash.toString(16)) it.println(transHash.toString(16))
it.println(configHash.toString(16))
} }
} }
@@ -24,9 +25,11 @@ data class CacheInfo(val path: String, val libPath: String, var flatHash: ULong,
if (!info.exists()) return null if (!info.exists()) return null
val (libPath, flatHash, transHash) = info.readLines() val (libPath, flatHash, transHash, configHash) = info.readLines()
return CacheInfo(path, libPath, flatHash.toULong(16), transHash.toULong(16)) // safe cast for the backward compatibility with the cache from the previous compiler versions
val configHashULong = configHash.toULongOrNull(16) ?: 0UL
return CacheInfo(path, libPath, flatHash.toULong(16), transHash.toULong(16), configHashULong)
} }
} }
} }
@@ -51,7 +51,8 @@ private fun invalidateCacheForModule(
cacheProvider: PersistentCacheProvider, cacheProvider: PersistentCacheProvider,
cacheConsumer: PersistentCacheConsumer, cacheConsumer: PersistentCacheConsumer,
signatureResolver: (String, Int) -> IdSignature, signatureResolver: (String, Int) -> IdSignature,
fileFingerPrints: MutableMap<String, Hash> fileFingerPrints: MutableMap<String, Hash>,
configUpdated: Boolean
): Pair<Set<String>, Collection<String>> { ): Pair<Set<String>, Collection<String>> {
val dirtyFiles = mutableSetOf<String>() val dirtyFiles = mutableSetOf<String>()
@@ -64,7 +65,7 @@ private fun invalidateCacheForModule(
// 2. calculate new fingerprints // 2. calculate new fingerprints
val fileNewFingerprint = library.fingerprint(index) val fileNewFingerprint = library.fingerprint(index)
if (fileOldFingerprint != fileNewFingerprint) { if (fileOldFingerprint != fileNewFingerprint || configUpdated) {
fileFingerPrints[file] = fileNewFingerprint fileFingerPrints[file] = fileNewFingerprint
cachedInlineHashesForFile.remove(file) cachedInlineHashesForFile.remove(file)
@@ -203,9 +204,25 @@ private fun buildCacheForModule(
// TODO: actual way of building a cache could change in future // TODO: actual way of building a cache could change in future
cacheExecutor.execute(irModule, dependencies, deserializer, configuration, dirtyFiles, deletedFiles, cacheConsumer, emptySet(), mainArguments) cacheExecutor.execute(
irModule,
dependencies,
deserializer,
configuration,
dirtyFiles,
deletedFiles,
cacheConsumer,
emptySet(),
mainArguments
)
cacheConsumer.commitLibraryInfo(libraryInfo.libPath.toCanonicalPath(), libraryInfo.flatHash, libraryInfo.transHash, irModule.name.asString()) cacheConsumer.commitLibraryInfo(
libraryInfo.libPath.toCanonicalPath(),
libraryInfo.flatHash,
libraryInfo.transHash,
libraryInfo.configHash,
irModule.name.asString()
)
} }
private fun loadModules( private fun loadModules(
@@ -315,24 +332,11 @@ fun interface CacheExecutor {
) )
} }
private fun File.md5(): ULong { private fun calcMD5(feeder: (MessageDigest) -> Unit): ULong {
val md5 = MessageDigest.getInstance("MD5") val md5 = MessageDigest.getInstance("MD5")
feeder(md5)
fun File.process(prefix: String = "") {
if (isDirectory) {
this.listFiles()!!.sortedBy { it.name }.forEach {
md5.update((prefix + it.name).toByteArray())
it.process(prefix + it.name + "/")
}
} else {
md5.update(readBytes())
}
}
this.process()
val d = md5.digest() val d = md5.digest()
return ((d[0].toULong() and 0xFFUL) return ((d[0].toULong() and 0xFFUL)
or ((d[1].toULong() and 0xFFUL) shl 8) or ((d[1].toULong() and 0xFFUL) shl 8)
or ((d[2].toULong() and 0xFFUL) shl 16) or ((d[2].toULong() and 0xFFUL) shl 16)
@@ -344,6 +348,30 @@ private fun File.md5(): ULong {
) )
} }
private fun File.md5(): ULong {
fun File.process(md5: MessageDigest, prefix: String = "") {
if (isDirectory) {
this.listFiles()!!.sortedBy { it.name }.forEach {
md5.update((prefix + it.name).toByteArray())
it.process(md5, prefix + it.name + "/")
}
} else {
md5.update(readBytes())
}
}
return calcMD5 { this.process(it) }
}
private fun CompilerConfiguration.calcMD5(): ULong {
val importantBooleanSettingKeys = listOf(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION)
return calcMD5 {
for (key in importantBooleanSettingKeys) {
it.update(key.toString().toByteArray())
it.update(getBoolean(key).toString().toByteArray())
}
}
}
private fun checkLibrariesHash( private fun checkLibrariesHash(
libraries: Map<ModulePath, KotlinLibrary>, libraries: Map<ModulePath, KotlinLibrary>,
dependencyGraph: Map<KotlinLibrary, List<KotlinLibrary>>, dependencyGraph: Map<KotlinLibrary, List<KotlinLibrary>>,
@@ -391,8 +419,9 @@ fun actualizeCacheForModule(
mainArguments: List<String>?, mainArguments: List<String>?,
executor: CacheExecutor executor: CacheExecutor
): CacheUpdateStatus { ): CacheUpdateStatus {
val configMD5 = compilerConfiguration.calcMD5()
val modulePath = moduleName.toCanonicalPath() val modulePath = moduleName.toCanonicalPath()
val cacheInfo = CacheInfo.load(cachePath) ?: CacheInfo(cachePath, modulePath, 0UL, 0UL) val cacheInfo = CacheInfo.load(cachePath) ?: CacheInfo(cachePath, modulePath, 0UL, 0UL, configMD5)
val icCacheMap: Map<ModulePath, CacheInfo> = loadCacheInfo(icCachePaths).also { val icCacheMap: Map<ModulePath, CacheInfo> = loadCacheInfo(icCachePaths).also {
it[modulePath] = cacheInfo it[modulePath] = cacheInfo
} }
@@ -405,7 +434,9 @@ fun actualizeCacheForModule(
} }
} }
if (checkLibrariesHash(libraries, dependencyGraph, icCacheMap, modulePath)) { val configUpdated = configMD5 != cacheInfo.configHash
cacheInfo.configHash = configMD5
if (checkLibrariesHash(libraries, dependencyGraph, icCacheMap, modulePath) && !configUpdated) {
return CacheUpdateStatus.FAST_PATH // up-to-date return CacheUpdateStatus.FAST_PATH // up-to-date
} }
@@ -425,7 +456,8 @@ fun actualizeCacheForModule(
persistentCacheConsumer, persistentCacheConsumer,
irFactory, irFactory,
mainArguments, mainArguments,
executor executor,
configUpdated
) )
} }
@@ -439,7 +471,8 @@ private fun actualizeCacheForModule(
persistentCacheConsumer: PersistentCacheConsumer, persistentCacheConsumer: PersistentCacheConsumer,
irFactory: IrFactory, irFactory: IrFactory,
mainArguments: List<String>?, mainArguments: List<String>?,
cacheExecutor: CacheExecutor cacheExecutor: CacheExecutor,
configUpdated: Boolean
): CacheUpdateStatus { ): CacheUpdateStatus {
// 1. Invalidate // 1. Invalidate
val dependencies = dependencyGraph[library]!! val dependencies = dependencyGraph[library]!!
@@ -486,7 +519,8 @@ private fun actualizeCacheForModule(
currentLibraryCacheProvider, currentLibraryCacheProvider,
persistentCacheConsumer, persistentCacheConsumer,
signatureResolver, signatureResolver,
fileFingerPrints fileFingerPrints,
configUpdated
) )
if (dirtySet.isEmpty()) return CacheUpdateStatus.NO_DIRTY_FILES // up-to-date if (dirtySet.isEmpty()) return CacheUpdateStatus.NO_DIRTY_FILES // up-to-date
@@ -582,7 +616,7 @@ fun rebuildCacheForDirtyFiles(
val currentIrModule = irModules.find { it.second == library }?.first!! val currentIrModule = irModules.find { it.second == library }?.first!!
cacheConsumer.commitLibraryInfo(library.libraryFile.path.toCanonicalPath(), 0UL, 0UL, currentIrModule.name.asString()) cacheConsumer.commitLibraryInfo(library.libraryFile.path.toCanonicalPath(), 0UL, 0UL, 0UL, currentIrModule.name.asString())
buildCacheForModuleFiles( buildCacheForModuleFiles(
currentIrModule, currentIrModule,
@@ -203,7 +203,7 @@ interface PersistentCacheConsumer {
fun commitSourceMap(path: String, mapData: ByteArray) fun commitSourceMap(path: String, mapData: ByteArray)
fun invalidateForFile(path: String) fun invalidateForFile(path: String)
fun commitLibraryInfo(libraryPath: String, flatHash: ULong, transHash: ULong, moduleName: String) fun commitLibraryInfo(libraryPath: String, flatHash: ULong, transHash: ULong, configHash: ULong, moduleName: String)
companion object { companion object {
val EMPTY = object : PersistentCacheConsumer { val EMPTY = object : PersistentCacheConsumer {
@@ -231,7 +231,7 @@ interface PersistentCacheConsumer {
} }
override fun commitLibraryInfo(libraryPath: String, flatHash: ULong, transHash: ULong, moduleName: String) { override fun commitLibraryInfo(libraryPath: String, flatHash: ULong, transHash: ULong, configHash: ULong, moduleName: String) {
} }
@@ -324,7 +324,7 @@ class PersistentCacheConsumerImpl(private val cachePath: String) : PersistentCac
commitByteArrayToCacheFile(path, fileSourceMap, mapData) commitByteArrayToCacheFile(path, fileSourceMap, mapData)
} }
override fun commitLibraryInfo(libraryPath: String, flatHash: ULong, transHash: ULong, moduleName: String) { override fun commitLibraryInfo(libraryPath: String, flatHash: ULong, transHash: ULong, configHash: ULong, moduleName: String) {
val infoFile = File(File(cachePath), "info") val infoFile = File(File(cachePath), "info")
if (infoFile.exists()) { if (infoFile.exists()) {
infoFile.delete() infoFile.delete()
@@ -335,6 +335,7 @@ class PersistentCacheConsumerImpl(private val cachePath: String) : PersistentCac
it.println(libraryPath) it.println(libraryPath)
it.println(flatHash.toString(16)) it.println(flatHash.toString(16))
it.println(transHash.toString(16)) it.println(transHash.toString(16))
it.println(configHash.toString(16))
it.println(moduleName) it.println(moduleName)
} }
} }
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.CompilerConfigurationKey import org.jetbrains.kotlin.config.CompilerConfigurationKey
import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.js.config.JSConfigurationKeys
import org.jetbrains.kotlin.platform.isCommon import org.jetbrains.kotlin.platform.isCommon
import org.jetbrains.kotlin.platform.js.isJs import org.jetbrains.kotlin.platform.js.isJs
import org.jetbrains.kotlin.platform.jvm.isJvm import org.jetbrains.kotlin.platform.jvm.isJvm
@@ -26,6 +27,7 @@ import org.jetbrains.kotlin.platform.konan.isNative
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.test.ApplicationEnvironmentDisposer import org.jetbrains.kotlin.test.ApplicationEnvironmentDisposer
import org.jetbrains.kotlin.test.TestInfrastructureInternals import org.jetbrains.kotlin.test.TestInfrastructureInternals
import org.jetbrains.kotlin.test.directives.JsEnvironmentConfigurationDirectives
import org.jetbrains.kotlin.test.model.FrontendKinds import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.model.TestFile import org.jetbrains.kotlin.test.model.TestFile
import org.jetbrains.kotlin.test.model.TestModule import org.jetbrains.kotlin.test.model.TestModule
@@ -98,6 +100,9 @@ open class CompilerConfigurationProviderImpl(
fun createCompilerConfiguration(module: TestModule): CompilerConfiguration { fun createCompilerConfiguration(module: TestModule): CompilerConfiguration {
val configuration = CompilerConfiguration() val configuration = CompilerConfiguration()
configuration[CommonConfigurationKeys.MODULE_NAME] = module.name configuration[CommonConfigurationKeys.MODULE_NAME] = module.name
if (JsEnvironmentConfigurationDirectives.PROPERTY_LAZY_INITIALIZATION in module.directives) {
configuration.put(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION, true)
}
if (module.frontendKind == FrontendKinds.FIR) { if (module.frontendKind == FrontendKinds.FIR) {
configuration[CommonConfigurationKeys.USE_FIR] = true configuration[CommonConfigurationKeys.USE_FIR] = true
@@ -96,4 +96,7 @@ public class JSConfigurationKeys {
public static final CompilerConfigurationKey<Boolean> PARTIAL_LINKAGE = public static final CompilerConfigurationKey<Boolean> PARTIAL_LINKAGE =
CompilerConfigurationKey.create("allows some symbols in klibs be missed"); CompilerConfigurationKey.create("allows some symbols in klibs be missed");
public static final CompilerConfigurationKey<Boolean> PROPERTY_LAZY_INITIALIZATION =
CompilerConfigurationKey.create("perform lazy initialization for properties");
} }
@@ -29,6 +29,7 @@ abstract class AbstractJsKLibABITestCase : AbstractKlibABITestCase() {
override fun compileBinaryAndRun(project: Project, configuration: CompilerConfiguration, libraries: Collection<String>, mainModulePath: String, buildDir: File) { override fun compileBinaryAndRun(project: Project, configuration: CompilerConfiguration, libraries: Collection<String>, mainModulePath: String, buildDir: File) {
configuration.put(JSConfigurationKeys.PARTIAL_LINKAGE, true) configuration.put(JSConfigurationKeys.PARTIAL_LINKAGE, true)
configuration.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN) configuration.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN)
configuration.put(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION, true)
configuration.put(CommonConfigurationKeys.MODULE_NAME, MAIN_MODULE_NAME) configuration.put(CommonConfigurationKeys.MODULE_NAME, MAIN_MODULE_NAME)
val kLib = MainModule.Klib(mainModulePath) val kLib = MainModule.Klib(mainModulePath)
val moduleStructure = ModulesStructure(project, kLib, configuration, libraries, emptyList()) val moduleStructure = ModulesStructure(project, kLib, configuration, libraries, emptyList())
@@ -38,7 +39,6 @@ abstract class AbstractJsKLibABITestCase : AbstractKlibABITestCase() {
PhaseConfig(jsPhases), PhaseConfig(jsPhases),
IrFactoryImplForJsIC(WholeWorldStageController()), IrFactoryImplForJsIC(WholeWorldStageController()),
exportedDeclarations = setOf(FqName("box")), exportedDeclarations = setOf(FqName("box")),
propertyLazyInitialization = true,
granularity = JsGenerationGranularity.PER_MODULE, granularity = JsGenerationGranularity.PER_MODULE,
icCompatibleIr2Js = true icCompatibleIr2Js = true
) )
@@ -58,6 +58,7 @@ class JsIrBackendFacade(
override fun transform(module: TestModule, inputArtifact: BinaryArtifacts.KLib): BinaryArtifacts.Js? { override fun transform(module: TestModule, inputArtifact: BinaryArtifacts.KLib): BinaryArtifacts.Js? {
val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module) val configuration = testServices.compilerConfigurationProvider.getCompilerConfiguration(module)
val isMainModule = JsEnvironmentConfigurator.isMainModule(module, testServices) val isMainModule = JsEnvironmentConfigurator.isMainModule(module, testServices)
if (!isMainModule) return null if (!isMainModule) return null
@@ -133,7 +134,6 @@ class JsIrBackendFacade(
exportedDeclarations = setOf(FqName.fromSegments(listOfNotNull(testPackage, TEST_FUNCTION))), exportedDeclarations = setOf(FqName.fromSegments(listOfNotNull(testPackage, TEST_FUNCTION))),
dceRuntimeDiagnostic = null, dceRuntimeDiagnostic = null,
es6mode = false, es6mode = false,
propertyLazyInitialization = JsEnvironmentConfigurationDirectives.PROPERTY_LAZY_INITIALIZATION in module.directives,
baseClassIntoMetadata = false, baseClassIntoMetadata = false,
safeExternalBoolean = JsEnvironmentConfigurationDirectives.SAFE_EXTERNAL_BOOLEAN in module.directives, safeExternalBoolean = JsEnvironmentConfigurationDirectives.SAFE_EXTERNAL_BOOLEAN in module.directives,
safeExternalBooleanDiagnostic = module.directives[JsEnvironmentConfigurationDirectives.SAFE_EXTERNAL_BOOLEAN_DIAGNOSTIC].singleOrNull(), safeExternalBooleanDiagnostic = module.directives[JsEnvironmentConfigurationDirectives.SAFE_EXTERNAL_BOOLEAN_DIAGNOSTIC].singleOrNull(),
@@ -112,7 +112,7 @@ class TestModuleCache(val files: MutableMap<String, FileCache>) {
files.remove(path) files.remove(path)
} }
override fun commitLibraryInfo(libraryPath: String, flatHash: ULong, transHash: ULong, moduleName: String) { override fun commitLibraryInfo(libraryPath: String, flatHash: ULong, transHash: ULong, configHash: ULong, moduleName: String) {
storedModuleName = moduleName storedModuleName = moduleName
} }
} }
@@ -124,6 +124,51 @@ class Kotlin2JsIrGradlePluginIT : AbstractKotlin2JsGradlePluginIT(true) {
build("assemble") build("assemble")
} }
} }
@DisplayName("Check IR incremental cache invalidation by compiler args")
@GradleTest
fun testJsIrIncrementalCacheInvalidationByArgs(gradleVersion: GradleVersion) {
project("kotlin2JsIrICProject", gradleVersion) {
val buildConfig = buildGradleKts.readText()
fun setLazyInitializationArg(value: Boolean) {
buildGradleKts.writeText(buildConfig)
buildGradleKts.appendText(
"""
|
|tasks.named<org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrLink>("compileDevelopmentExecutableKotlinJs") {
| kotlinOptions {
| freeCompilerArgs += "-Xir-property-lazy-initialization=$value"
| }
|}
""".trimMargin()
)
}
fun String.testScriptOutLines() = this.lines().mapNotNull {
val trimmed = it.removePrefix(">>> TEST OUT: ")
if (trimmed == it) null else trimmed
}
// -Xir-property-lazy-initialization default is true
build("nodeRun") {
assertTasksExecuted(":compileDevelopmentExecutableKotlinJs")
assertEquals(listOf("Hello, Gradle."), output.testScriptOutLines())
}
setLazyInitializationArg(false)
build("nodeRun") {
assertTasksExecuted(":compileDevelopmentExecutableKotlinJs")
assertEquals(listOf("TOP LEVEL!", "Hello, Gradle."), output.testScriptOutLines())
}
setLazyInitializationArg(true)
build("nodeRun") {
assertTasksExecuted(":compileDevelopmentExecutableKotlinJs")
assertEquals(listOf("Hello, Gradle."), output.testScriptOutLines())
}
}
}
} }
@JsGradlePluginTests @JsGradlePluginTests
@@ -0,0 +1,19 @@
plugins {
kotlin("js")
}
group = "com.example"
version = "1.0"
repositories {
mavenCentral()
mavenLocal()
}
kotlin {
js(IR) {
binaries.executable()
nodejs {}
}
}
@@ -0,0 +1,3 @@
kotlin.code.style=official
kotlin.incremental.js.ir=true
kotlin.incremental.js.klib=true
@@ -0,0 +1,5 @@
fun main() {
println(greeting("Gradle"))
}
fun greeting(name: String) = ">>> TEST OUT: Hello, $name."
@@ -0,0 +1,2 @@
@JsExport
val topLevelForLazy = { println(">>> TEST OUT: TOP LEVEL!"); 0 }()