[JS IR] Update IC depends graph after intrinsics loading

- Remove unused params from compilerWithIC wrapper.
- Move JsIrBackendContext creation logic into separate function
- Introduce and use compiler interface for IC infrastructure

On dirty rebuild we may reload IR from stdlib,
such reloading may affect files with intrinsics and builtins.
As soon as we load only exported symbols, we must track dependencies
for files with intrinsics or builtins as for others.
This patch implements the logic which updates dependencies for
the stdlib files after loading of intrinsics and builtins.

^KT-54323 Fixed
This commit is contained in:
Alexander Korepanov
2022-10-12 22:14:15 +00:00
committed by Space Team
parent 31ba1f1534
commit 7b2c125754
40 changed files with 628 additions and 170 deletions
@@ -289,7 +289,7 @@ class K2JsIrCompiler : CLICompiler<K2JSCompilerArguments>() {
compilerConfiguration = configurationJs,
irFactory = { IrFactoryImplForJsIC(WholeWorldStageController()) },
mainArguments = mainCallArguments,
executor = ::buildCacheForModuleFiles
compilerInterfaceFactory = { mainModule, cfg -> JsIrCompilerWithIC(mainModule, cfg) }
)
var tp = System.currentTimeMillis()
@@ -7,88 +7,65 @@ package org.jetbrains.kotlin.ir.backend.js
import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig
import org.jetbrains.kotlin.backend.common.phaser.PhaserState
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.checkNoUnboundSymbols
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.backend.js.ic.JsIrCompilerICInterface
import org.jetbrains.kotlin.ir.backend.js.lower.collectNativeImplementations
import org.jetbrains.kotlin.ir.backend.js.lower.generateJsTests
import org.jetbrains.kotlin.ir.backend.js.lower.moveBodilessDeclarationsToSeparatePlace
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.*
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.ExternalDependenciesGenerator
import org.jetbrains.kotlin.js.config.RuntimeDiagnostic
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors
@Suppress("UNUSED_PARAMETER")
@OptIn(ObsoleteDescriptorBasedAPI::class)
fun compileWithIC(
mainModule: IrModuleFragment,
class JsIrCompilerWithIC(
private val mainModule: IrModuleFragment,
configuration: CompilerConfiguration,
irLinker: JsIrLinker,
allModules: Collection<IrModuleFragment>,
filesToLower: Collection<IrFile>,
mainArguments: List<String>? = null,
exportedDeclarations: Set<FqName> = emptySet(),
keep: Set<String> = emptySet(),
generateFullJs: Boolean = true,
generateDceJs: Boolean = false,
dceDriven: Boolean = false,
dceRuntimeDiagnostic: RuntimeDiagnostic? = null,
es6mode: Boolean = false,
multiModule: Boolean = false,
verifySignatures: Boolean = true,
baseClassIntoMetadata: Boolean = false,
lowerPerModule: Boolean = false,
safeExternalBoolean: Boolean = false,
safeExternalBooleanDiagnostic: RuntimeDiagnostic? = null,
): List<JsIrFragmentAndBinaryAst> {
val irBuiltIns = mainModule.irBuiltins
val symbolTable = (irBuiltIns as IrBuiltInsOverDescriptors).symbolTable
es6mode: Boolean = false
) : JsIrCompilerICInterface {
private val context: JsIrBackendContext
val context = JsIrBackendContext(
mainModule.descriptor,
irBuiltIns,
symbolTable,
mainModule,
exportedDeclarations,
keep,
configuration,
es6mode = es6mode,
dceRuntimeDiagnostic = dceRuntimeDiagnostic,
baseClassIntoMetadata = baseClassIntoMetadata,
safeExternalBoolean = safeExternalBoolean,
safeExternalBooleanDiagnostic = safeExternalBooleanDiagnostic,
icCompatibleIr2Js = IcCompatibleIr2Js.IC_MODE,
)
init {
val irBuiltIns = mainModule.irBuiltins
val symbolTable = (irBuiltIns as IrBuiltInsOverDescriptors).symbolTable
// Load declarations referenced during `context` initialization
val irProviders = listOf(irLinker)
ExternalDependenciesGenerator(symbolTable, irProviders).generateUnboundSymbolsAsDependencies()
irLinker.postProcess()
irLinker.checkNoUnboundSymbols(symbolTable, "at the end of IR linkage process")
allModules.forEach {
collectNativeImplementations(context, it)
moveBodilessDeclarationsToSeparatePlace(context, it)
context = JsIrBackendContext(
mainModule.descriptor,
irBuiltIns,
symbolTable,
mainModule,
exportedDeclarations,
keep = emptySet(),
configuration = configuration,
es6mode = es6mode,
icCompatibleIr2Js = IcCompatibleIr2Js.IC_MODE,
)
}
generateJsTests(context, mainModule)
override fun compile(
allModules: Collection<IrModuleFragment>,
dirtyFiles: Collection<IrFile>,
mainArguments: List<String>?
): List<JsIrFragmentAndBinaryAst> {
allModules.forEach {
collectNativeImplementations(context, it)
moveBodilessDeclarationsToSeparatePlace(context, it)
}
lowerPreservingTags(allModules, context, PhaseConfig(jsPhases), symbolTable.irFactory.stageController as WholeWorldStageController)
generateJsTests(context, mainModule)
val transformer = IrModuleToJsTransformerTmp(context, mainArguments)
return transformer.generateBinaryAst(filesToLower, allModules)
lowerPreservingTags(allModules, context, PhaseConfig(jsPhases), context.irFactory.stageController as WholeWorldStageController)
val transformer = IrModuleToJsTransformerTmp(context, mainArguments)
return transformer.generateBinaryAst(dirtyFiles, allModules)
}
}
fun lowerPreservingTags(
modules: Iterable<IrModuleFragment>,
context: JsIrBackendContext,
phaseConfig: PhaseConfig,
controller: WholeWorldStageController
modules: Iterable<IrModuleFragment>, context: JsIrBackendContext, phaseConfig: PhaseConfig, controller: WholeWorldStageController
) {
// Lower all the things
controller.currentStage = 0
@@ -9,7 +9,6 @@ import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.backend.js.*
import org.jetbrains.kotlin.ir.backend.js.lower.serialization.ir.JsIrLinker
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsIrFragmentAndBinaryAst
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.safeModuleName
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.util.*
@@ -22,19 +21,21 @@ import org.jetbrains.kotlin.name.FqName
import java.io.File
import java.util.EnumSet
fun interface CacheExecutor {
fun execute(
mainModule: IrModuleFragment,
fun interface JsIrCompilerICInterface {
fun compile(
allModules: Collection<IrModuleFragment>,
irLinker: JsIrLinker,
configuration: CompilerConfiguration,
dirtyFiles: Collection<IrFile>,
exportedDeclarations: Set<FqName>,
mainArguments: List<String>?
): List<JsIrFragmentAndBinaryAst>
}
fun interface JsIrCompilerICInterfaceFactory {
fun createCompilerForIC(
mainModule: IrModuleFragment,
configuration: CompilerConfiguration
): JsIrCompilerICInterface
}
enum class DirtyFileState(val str: String) {
ADDED_FILE("added file"),
MODIFIED_CONFIG("modified config"),
@@ -53,7 +54,7 @@ class CacheUpdater(
private val compilerConfiguration: CompilerConfiguration,
private val irFactory: () -> IrFactory,
private val mainArguments: List<String>?,
private val executor: CacheExecutor
private val compilerInterfaceFactory: JsIrCompilerICInterfaceFactory
) {
private val signatureHashCalculator = IdSignatureHashCalculator()
@@ -323,47 +324,84 @@ class CacheUpdater(
return allSignatures
}
private fun IdSignatureHashCalculator.addAllSignatureSymbols(idSignatureToFile: Map<IdSignature, SignatureSource>) {
for ((signature, signatureSrc) in idSignatureToFile) {
addHashForSignatureIfNotExist(signature, signatureSrc.symbol)
}
}
private fun KotlinSourceFileMutableMap<DirtyFileMetadata>.getExportedSignaturesAndAddMetadata(
jsIrLinker: JsIrLinker,
irModule: IrModuleFragment,
libFile: KotlinLibraryFile,
dirtySrcFiles: Set<KotlinSourceFile>
): Map<IdSignature, SignatureSource> {
val idSignatureToFile = hashMapOf<IdSignature, SignatureSource>()
val moduleDeserializer = jsIrLinker.moduleDeserializer(irModule.descriptor)
val incrementalCache = getLibIncrementalCache(libFile)
for (fileDeserializer in moduleDeserializer.fileDeserializers()) {
val reachableSignatures = fileDeserializer.symbolDeserializer.signatureDeserializer.signatureToIndexMapping()
val maybeImportedSignatures = reachableSignatures.keys.toMutableSet()
val implementedSymbols = collectImplementedSymbol(fileDeserializer.symbolDeserializer.deserializedSymbols)
for ((signature, symbol) in implementedSymbols) {
var symbolCanBeExported = maybeImportedSignatures.remove(signature)
resolveFakeOverrideFunction(symbol)?.let { resolvedSignature ->
if (resolvedSignature !in implementedSymbols) {
maybeImportedSignatures.add(resolvedSignature)
}
symbolCanBeExported = true
}
if (symbolCanBeExported) {
idSignatureToFile[signature] = SignatureSource(libFile, KotlinSourceFile(fileDeserializer.file), symbol)
}
}
val libSrcFile = KotlinSourceFile(fileDeserializer.file)
if (libSrcFile in dirtySrcFiles) {
val metadata = incrementalCache.fetchSourceFileFullMetadata(libSrcFile)
this[libFile, libSrcFile] = DirtyFileMetadata(maybeImportedSignatures, metadata.directDependencies)
}
}
return idSignatureToFile
}
private fun DirtyFileMetadata.setAllDependencies(
idSignatureToFile: Map<IdSignature, SignatureSource>,
updatedMetadata: KotlinSourceFileMap<DirtyFileMetadata>,
libFile: KotlinLibraryFile,
srcFile: KotlinSourceFile
) {
val allImportedSignatures = addParentSignatures(maybeImportedSignatures, idSignatureToFile, libFile, srcFile)
for (importedSignature in allImportedSignatures) {
val (dependencyLib, dependencyFile) = idSignatureToFile[importedSignature] ?: continue
signatureHashCalculator[importedSignature]?.also { signatureHash ->
addDirectDependency(dependencyLib, dependencyFile, importedSignature, signatureHash)
} ?: notFoundIcError("signature $importedSignature hash", dependencyLib, dependencyFile)
updatedMetadata[dependencyLib, dependencyFile]?.also { dependencyMetadata ->
dependencyMetadata.addInverseDependency(libFile, srcFile, importedSignature)
}
}
}
private fun rebuildDirtySourceMetadata(
jsIrLinker: JsIrLinker,
loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>,
dirtySrcFiles: KotlinSourceFileMap<KotlinSourceFileExports>,
): KotlinSourceFileMap<DirtyFileMetadata> {
val idSignatureToFile = mutableMapOf<IdSignature, SignatureSource>()
val idSignatureToFile = hashMapOf<IdSignature, SignatureSource>()
val updatedMetadata = KotlinSourceFileMutableMap<DirtyFileMetadata>()
for ((lib, irModule) in loadedFragments) {
val moduleDeserializer = jsIrLinker.moduleDeserializer(irModule.descriptor)
val incrementalCache = getLibIncrementalCache(lib)
for (fileDeserializer in moduleDeserializer.fileDeserializers()) {
val libSrcFile = KotlinSourceFile(fileDeserializer.file)
val reachableSignatures = fileDeserializer.symbolDeserializer.signatureDeserializer.signatureToIndexMapping()
val maybeImportedSignatures = reachableSignatures.keys.toMutableSet()
val implementedSymbols = collectImplementedSymbol(fileDeserializer.symbolDeserializer.deserializedSymbols)
for ((signature, symbol) in implementedSymbols) {
var symbolCanBeExported = maybeImportedSignatures.remove(signature)
resolveFakeOverrideFunction(symbol)?.let { resolvedSignature ->
if (resolvedSignature !in implementedSymbols) {
maybeImportedSignatures.add(resolvedSignature)
}
symbolCanBeExported = true
}
if (symbolCanBeExported) {
signatureHashCalculator.addHashForSignatureIfNotExist(signature, symbol)
idSignatureToFile[signature] = SignatureSource(lib, libSrcFile, symbol)
}
}
if (dirtySrcFiles[lib, libSrcFile] != null) {
val metadata = incrementalCache.fetchSourceFileFullMetadata(libSrcFile)
updatedMetadata[lib, libSrcFile] = DirtyFileMetadata(maybeImportedSignatures, metadata.directDependencies)
}
}
val libDirtySrcFiles = dirtySrcFiles[lib]?.keys ?: emptySet()
idSignatureToFile += updatedMetadata.getExportedSignaturesAndAddMetadata(jsIrLinker, irModule, lib, libDirtySrcFiles)
}
signatureHashCalculator.addAllSignatureSymbols(idSignatureToFile)
for ((libFile, srcFiles) in updatedMetadata) {
val libDirtySrcFiles = dirtySrcFiles[libFile] ?: continue
for ((srcFile, internalHeader) in srcFiles) {
for ((srcFile, updatedHeader) in srcFiles) {
val dirtySrcFile = libDirtySrcFiles[srcFile] ?: continue
dirtySrcFile.inverseDependencies.forEachFile { dependentLibFile, dependentSrcFile, signatures ->
signatures.forEach { signature ->
@@ -376,17 +414,7 @@ class CacheUpdater(
}
}
val allImportedSignatures = addParentSignatures(internalHeader.maybeImportedSignatures, idSignatureToFile, libFile, srcFile)
for (importedSignature in allImportedSignatures) {
val (dependencyLib, dependencyFile) = idSignatureToFile[importedSignature] ?: continue
signatureHashCalculator[importedSignature]?.also { signatureHash ->
internalHeader.addDirectDependency(dependencyLib, dependencyFile, importedSignature, signatureHash)
} ?: notFoundIcError("signature $importedSignature hash", dependencyLib, dependencyFile)
updatedMetadata[dependencyLib, dependencyFile]?.also { dependencyMetadata ->
dependencyMetadata.addInverseDependency(libFile, srcFile, importedSignature)
}
}
updatedHeader.setAllDependencies(idSignatureToFile, updatedMetadata, libFile, srcFile)
}
}
@@ -561,6 +589,95 @@ class CacheUpdater(
return artifacts
}
private fun updateStdlibIntrinsicDependencies(
linker: JsIrLinker,
mainModule: IrModuleFragment,
loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>,
dirtyFileExports: KotlinSourceFileMap<*>
) {
val stdlibDescriptor = mainModule.descriptor.builtIns.builtInsModule
val (stdlibFile, stdlibIr) = loadedFragments.entries.find {
it.value.descriptor === stdlibDescriptor
} ?: notFoundIcError("stdlib loaded fragment")
val stdlibDirtyFiles = dirtyFileExports[stdlibFile]?.keys ?: return
signatureHashCalculator.updateInlineFunctionTransitiveHashes(listOf(stdlibIr))
val updatedMetadata = KotlinSourceFileMutableMap<DirtyFileMetadata>()
val idSignatureToFile = updatedMetadata.getExportedSignaturesAndAddMetadata(linker, stdlibIr, stdlibFile, stdlibDirtyFiles)
signatureHashCalculator.addAllSignatureSymbols(idSignatureToFile)
updatedMetadata.forEachFile { libFile, srcFile, updatedHeader ->
updatedHeader.setAllDependencies(idSignatureToFile, updatedMetadata, libFile, srcFile)
}
val incrementalCache = getLibIncrementalCache(stdlibFile)
updatedMetadata.forEachFile { libFile, srcFile, updatedHeader ->
if (libFile != stdlibFile) {
icError("unexpected lib while parsing stdlib dependencies", libFile, srcFile)
}
val cachedHeader = incrementalCache.fetchSourceFileFullMetadata(srcFile)
val needUpdate = when {
!updatedHeader.directDependencies.allFiles { lib, file, dependencies ->
cachedHeader.directDependencies[lib, file]?.keys?.containsAll(dependencies.keys) ?: dependencies.isEmpty()
} -> true
!updatedHeader.inverseDependencies.allFiles { lib, file, invDependencies ->
cachedHeader.inverseDependencies[lib, file]?.containsAll(invDependencies) ?: invDependencies.isEmpty()
} -> true
else -> false
}
if (needUpdate) {
cachedHeader.directDependencies.forEachFile { lib, file, dependencies ->
val updatedDependencies = updatedHeader.directDependencies[lib, file]
if (updatedDependencies != null) {
updatedDependencies += dependencies
} else {
updatedHeader.directDependencies[lib, file] = HashMap(dependencies)
}
}
cachedHeader.inverseDependencies.forEachFile { lib, file, dependencies ->
val updatedDependencies = updatedHeader.inverseDependencies[lib, file]
if (updatedDependencies != null) {
updatedDependencies += dependencies
} else {
updatedHeader.inverseDependencies[lib, file] = HashSet(dependencies)
}
}
incrementalCache.updateSourceFileMetadata(srcFile, updatedHeader)
}
}
}
private fun compileDirtyFiles(
linker: JsIrLinker,
loadedFragments: Map<KotlinLibraryFile, IrModuleFragment>,
dirtyFileExports: KotlinSourceFileMap<*>
): List<JsIrFragmentAndBinaryAst> {
val mainModule = loadedFragments[mainLibraryFile] ?: notFoundIcError("main lib loaded fragment", mainLibraryFile)
val compilerForIC = compilerInterfaceFactory.createCompilerForIC(mainModule, compilerConfiguration)
// Load declarations referenced during `context` initialization
linker.loadUnboundSymbols(true)
updateStdlibIntrinsicDependencies(linker, mainModule, loadedFragments, dirtyFileExports)
return compilerForIC.compile(
allModules = loadedFragments.values,
dirtyFiles = loadedFragments.flatMap { (libFile, libFragment) ->
dirtyFileExports[libFile]?.let { libDirtyFiles ->
libFragment.files.filter { file -> KotlinSourceFile(file) in libDirtyFiles }
} ?: emptyList()
},
mainArguments = mainArguments
)
}
fun actualizeCaches(eventCallback: (String) -> Unit = {}): List<ModuleArtifact> {
dirtyFileStats.clear()
@@ -601,19 +718,7 @@ class CacheUpdater(
eventCallback("final loading of updated files")
}
val rebuiltFragments = executor.execute(
mainModule = loadedIr.loadedFragments[mainLibraryFile] ?: notFoundIcError("main lib loaded fragment", mainLibraryFile),
allModules = loadedIr.loadedFragments.values,
irLinker = loadedIr.linker,
configuration = compilerConfiguration,
dirtyFiles = loadedIr.loadedFragments.flatMap { (libFile, libFragment) ->
dirtyFileExports[libFile]?.let { libDirtyFiles ->
libFragment.files.filter { file -> KotlinSourceFile(file) in libDirtyFiles }
} ?: emptyList()
},
exportedDeclarations = emptySet(),
mainArguments = mainArguments
)
val rebuiltFragments = compileDirtyFiles(loadedIr.linker, loadedIr.loadedFragments, dirtyFileExports)
eventCallback("updated files processing (lowering)")
val artifacts = buildModuleArtifactsAndCommitCache(loadedIr.linker, loadedIr.loadedFragments, rebuiltFragments)
@@ -650,33 +755,14 @@ fun rebuildCacheForDirtyFiles(
currentIrModule.files.filter { irFile -> irFile.fileEntry.name in files }
} ?: currentIrModule.files
return currentIrModule to buildCacheForModuleFiles(
mainModule = currentIrModule,
val compilerWithIC = JsIrCompilerWithIC(currentIrModule, configuration, exportedDeclarations)
// Load declarations referenced during `context` initialization
jsIrLinker.loadUnboundSymbols(true)
return currentIrModule to compilerWithIC.compile(
allModules = irModules.values,
irLinker = jsIrLinker,
configuration = configuration,
dirtyFiles = dirtyIrFiles,
exportedDeclarations = exportedDeclarations,
mainArguments = mainArguments
)
}
fun buildCacheForModuleFiles(
mainModule: IrModuleFragment,
allModules: Collection<IrModuleFragment>,
irLinker: JsIrLinker,
configuration: CompilerConfiguration,
dirtyFiles: Collection<IrFile>,
exportedDeclarations: Set<FqName>,
mainArguments: List<String>?
): List<JsIrFragmentAndBinaryAst> {
return compileWithIC(
mainModule = mainModule,
allModules = allModules,
filesToLower = dirtyFiles,
configuration = configuration,
irLinker = irLinker,
mainArguments = mainArguments,
exportedDeclarations = exportedDeclarations,
)
}
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.ir.backend.js.ic
import org.jetbrains.kotlin.backend.common.serialization.DeserializationStrategy
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.checkNoUnboundSymbols
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureDescriptor
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.languageVersionSettings
@@ -27,6 +28,13 @@ import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors
import org.jetbrains.kotlin.psi2ir.generators.TypeTranslatorImpl
import org.jetbrains.kotlin.storage.LockBasedStorageManager
internal fun JsIrLinker.loadUnboundSymbols(checkNoUnbound: Boolean) {
ExternalDependenciesGenerator(symbolTable, listOf(this)).generateUnboundSymbolsAsDependencies()
postProcess()
if (checkNoUnbound) {
checkNoUnboundSymbols(symbolTable, "at the end of IR linkage process")
}
}
internal class JsIrLinkerLoader(
private val compilerConfiguration: CompilerConfiguration,
@@ -109,8 +117,7 @@ internal class JsIrLinkerLoader(
}
}
ExternalDependenciesGenerator(jsIrLinker.symbolTable, listOf(jsIrLinker)).generateUnboundSymbolsAsDependencies()
jsIrLinker.postProcess()
jsIrLinker.loadUnboundSymbols(false)
return LoadedJsIr(jsIrLinker, irModules)
}
}
@@ -46,6 +46,9 @@ open class KotlinSourceFileMap<out T>(files: Map<KotlinLibraryFile, Map<KotlinSo
inline fun forEachFile(f: (KotlinLibraryFile, KotlinSourceFile, T) -> Unit) =
forEach { (lib, files) -> files.forEach { (file, data) -> f(lib, file, data) } }
inline fun allFiles(p: (KotlinLibraryFile, KotlinSourceFile, T) -> Boolean) =
entries.all { (lib, files) -> files.entries.all { (file, data) -> p(lib, file, data) } }
operator fun get(libFile: KotlinLibraryFile, sourceFile: KotlinSourceFile): T? = get(libFile)?.get(sourceFile)
}
@@ -226,26 +226,6 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
}
}
fun executorWithBoxExport(
currentModule: IrModuleFragment,
allModules: Collection<IrModuleFragment>,
irLinker: JsIrLinker,
configuration: CompilerConfiguration,
dirtyFiles: Collection<IrFile>,
exportedDeclarations: Set<FqName>,
mainArguments: List<String>?
): List<JsIrFragmentAndBinaryAst> {
return buildCacheForModuleFiles(
mainModule = currentModule,
allModules = allModules,
irLinker = irLinker,
configuration = configuration,
dirtyFiles = dirtyFiles,
exportedDeclarations = exportedDeclarations + FqName(BOX_FUNCTION_NAME),
mainArguments = mainArguments,
)
}
fun execute() {
val stdlibCacheDir = resolveModuleCache(STDLIB_ALIAS, buildDir).canonicalPath
for (projStep in projectInfo.steps) {
@@ -259,7 +239,7 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
compilerConfiguration = configuration,
irFactory = { IrFactoryImplForJsIC(WholeWorldStageController()) },
mainArguments = null,
executor = ::executorWithBoxExport
compilerInterfaceFactory = { mainModule, cfg -> JsIrCompilerWithIC(mainModule, cfg, setOf(FqName(BOX_FUNCTION_NAME))) }
)
val icCaches = cacheUpdater.actualizeCaches()
@@ -60,6 +60,11 @@ public class InvalidationTestGenerated extends AbstractInvalidationTest {
runTest("js/js.translator/testData/incremental/invalidation/classFunctionsAndFields/");
}
@TestMetadata("companionFunction")
public void testCompanionFunction() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/companionFunction/");
}
@TestMetadata("companionInlineFunction")
public void testCompanionInlineFunction() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/companionInlineFunction/");
@@ -90,6 +95,16 @@ public class InvalidationTestGenerated extends AbstractInvalidationTest {
runTest("js/js.translator/testData/incremental/invalidation/enum/");
}
@TestMetadata("enumsInInlineFunctions")
public void testEnumsInInlineFunctions() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/enumsInInlineFunctions/");
}
@TestMetadata("exceptionsFromInlineFunction")
public void testExceptionsFromInlineFunction() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/exceptionsFromInlineFunction/");
}
@TestMetadata("exportsThroughInlineFunction")
public void testExportsThroughInlineFunction() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/exportsThroughInlineFunction/");
@@ -0,0 +1,15 @@
interface MyInterface {
fun interfaceFunction(): String
}
class MyClass1 {
companion object {
fun companionFunction() = 0
}
}
class MyClass2 {
companion object {
fun companionFunction() = "0"
}
}
@@ -0,0 +1,15 @@
interface MyInterface {
fun interfaceFunction(): String
}
class MyClass1 {
companion object {
fun companionFunction() = 1
}
}
class MyClass2 {
companion object {
fun companionFunction() = "1"
}
}
@@ -0,0 +1,8 @@
STEP 0:
modifications:
U : l1.0.kt -> l1.kt
added file: l1.kt
STEP 1:
modifications:
U : l1.1.kt -> l1.kt
modified ir: l1.kt
@@ -0,0 +1,15 @@
inline fun makeMyInterfaceObject(x: Boolean): MyInterface {
return if (x) {
object : MyInterface {
override fun interfaceFunction(): String {
return MyClass1.companionFunction().toString()
}
}
} else {
object : MyInterface {
override fun interfaceFunction(): String {
return MyClass2.companionFunction()
}
}
}
}
@@ -0,0 +1,7 @@
STEP 0:
dependencies: lib1
modifications:
U : l2.0.kt -> l2.kt
added file: l2.kt
STEP 1:
dependencies: lib1
@@ -0,0 +1,10 @@
fun box(stepId: Int): String {
when (stepId) {
0, 1 -> {
if (makeMyInterfaceObject(false).interfaceFunction() != "$stepId") return "Fail x = false"
if (makeMyInterfaceObject(true).interfaceFunction() != "$stepId") return "Fail x = true"
}
else -> return "Unknown"
}
return "OK"
}
@@ -0,0 +1,5 @@
STEP 0:
dependencies: lib1, lib2
added file: m.kt
STEP 1:
dependencies: lib1, lib2
@@ -0,0 +1,8 @@
MODULES: lib1, lib2, main
STEP 0:
libs: lib1, lib2, main
dirty js: lib1, lib2, main
STEP 1:
libs: lib1, lib2, main
dirty js: lib1
@@ -0,0 +1,13 @@
class TestClass1 {
enum class TestEnum {
A, B
}
}
inline fun foo1(): List<Enum<*>> {
return TestClass1.TestEnum.values().toList()
}
inline fun foo2(): List<Enum<*>> {
return emptyList()
}
@@ -0,0 +1,23 @@
class TestClass1 {
enum class TestEnum {
A, B
}
}
class TestClass2 {
enum class TestEnum {
A, B, C
}
}
inline fun foo1(): List<Enum<*>> {
return TestClass1.TestEnum.values().toList()
}
inline fun foo2(): List<Enum<*>> {
return TestClass2.TestEnum.values().toList()
}
inline fun foo3(): List<Enum<*>> {
return emptyList()
}
@@ -0,0 +1,23 @@
class TestClass1 {
enum class TestEnum {
A, B
}
}
class TestClass3 {
enum class TestEnum {
A
}
}
inline fun foo1(): List<Enum<*>> {
return TestClass1.TestEnum.values().toList()
}
inline fun foo2(): List<Enum<*>> {
return emptyList()
}
inline fun foo3(): List<Enum<*>> {
return TestClass3.TestEnum.values().toList()
}
@@ -0,0 +1,11 @@
inline fun foo1(): List<Enum<*>> {
return emptyList()
}
inline fun foo2(): List<Enum<*>> {
return emptyList()
}
inline fun foo3(): List<Enum<*>> {
return emptyList()
}
@@ -0,0 +1,18 @@
STEP 0:
modifications:
U : l1.0.kt -> l1.kt
added file: l1.kt
STEP 1:
modifications:
U : l1.1.kt -> l1.kt
modified ir: l1.kt
STEP 2:
modifications:
U : l1.2.kt -> l1.kt
modified ir: l1.kt
STEP 3:
updated exports: l1.kt
STEP 4:
modifications:
U : l1.4.kt -> l1.kt
modified ir: l1.kt
@@ -0,0 +1,12 @@
class TestClass100 {
enum class TestEnum {
A, B
}
}
fun testEnums(): List<Enum<*>> {
val enums1 = foo1()
val enums2 = foo2()
return TestClass100.TestEnum.values().toList() + enums1 + enums2
}
@@ -0,0 +1,12 @@
class TestClass2 {
enum class TestEnum {
A, B
}
}
fun testEnums(): List<Enum<*>> {
val enums1 = foo1()
val enums3 = foo3()
return TestClass2.TestEnum.values().toList() + enums1 + enums3
}
@@ -0,0 +1,19 @@
STEP 0:
dependencies: lib1
modifications:
U : l2.0.kt -> l2.kt
added file: l2.kt
STEP 1:
dependencies: lib1
updated imports: l2.kt
STEP 2:
dependencies: lib1
updated imports: l2.kt
STEP 3:
dependencies: lib1
modifications:
U : l2.3.kt -> l2.kt
modified ir: l2.kt
STEP 4:
dependencies: lib1
updated imports: l2.kt
@@ -0,0 +1,27 @@
fun box(stepId: Int): String {
val enums = testEnums()
when (stepId) {
0 -> if (enums.toString() != "[A, B, A, B]") return "Fail, got $enums"
1 -> if (enums.toString() != "[A, B, A, B, A, B, C]") return "Fail, got $enums"
2 -> if (enums.toString() != "[A, B, A, B]") return "Fail, got $enums"
3 -> if (enums.toString() != "[A, B, A, B, A]") return "Fail, got $enums"
4 -> if (enums.toString() != "[A, B]") return "Fail, got $enums"
else -> return "Unknown"
}
for (i1 in 0..enums.size - 1) {
for (i2 in 0..enums.size - 1) {
if (i1 == i2) {
continue
}
if (enums[i1] == enums[i2]) {
return "Fail eq2"
}
if (enums[i1] === enums[i2]) {
return "Fail eq3"
}
}
}
return "OK"
}
@@ -0,0 +1,5 @@
STEP 0:
dependencies: lib1, lib2
added file: m.kt
STEP 1..4:
dependencies: lib1, lib2
@@ -0,0 +1,5 @@
MODULES: lib1, lib2, main
STEP 0..4:
libs: lib1, lib2, main
dirty js: lib1, lib2, main
@@ -0,0 +1 @@
inline fun foo() = 0
@@ -0,0 +1,4 @@
inline fun foo() : Int {
val x: Any? = "1"
return x as Int
}
@@ -0,0 +1,4 @@
inline fun foo() : Int {
val x: Any? = null
return x!! as Int
}
@@ -0,0 +1,4 @@
inline fun foo() : Int {
throw NumberFormatException()
return 5
}
@@ -0,0 +1 @@
inline fun foo() = 6
@@ -0,0 +1,27 @@
STEP 0:
modifications:
U : l1.0.kt -> l1.kt
added file: l1.kt
STEP 1:
modifications:
U : l1.1.kt -> l1.kt
modified ir: l1.kt
STEP 2:
STEP 3:
modifications:
U : l1.3.8.kt -> l1.kt
modified ir: l1.kt
STEP 4:
STEP 5:
modifications:
U : l1.5.kt -> l1.kt
modified ir: l1.kt
STEP 6:
modifications:
U : l1.6.kt -> l1.kt
modified ir: l1.kt
STEP 7:
STEP 8:
modifications:
U : l1.3.8.kt -> l1.kt
modified ir: l1.kt
@@ -0,0 +1,3 @@
fun qux(): Int {
return foo()
}
@@ -0,0 +1,7 @@
fun qux(): Int {
try {
return foo()
} catch(ex: ClassCastException) {
return 2
}
}
@@ -0,0 +1,7 @@
fun qux(): Int {
try {
return foo()
} catch(ex: NullPointerException) {
return 4
}
}
@@ -0,0 +1,3 @@
fun qux(): Int {
return foo() + 1
}
@@ -0,0 +1,32 @@
STEP 0:
dependencies: lib1
modifications:
U : l2.0.kt -> l2.kt
added file: l2.kt
STEP 1:
dependencies: lib1
updated imports: l2.kt
STEP 2:
dependencies: lib1
modifications:
U : l2.2.kt -> l2.kt
modified ir: l2.kt
STEP 3:
dependencies: lib1
updated imports: l2.kt
STEP 4:
dependencies: lib1
modifications:
U : l2.4.kt -> l2.kt
modified ir: l2.kt
STEP 5..6:
dependencies: lib1
updated imports: l2.kt
STEP 7:
dependencies: lib1
modifications:
U : l2.7.kt -> l2.kt
modified ir: l2.kt
STEP 8:
dependencies: lib1
updated imports: l2.kt
@@ -0,0 +1,15 @@
fun box(stepId: Int): String {
when (stepId) {
0, 2, 4, 6, 7 -> if (qux() != stepId) return "Fail"
1, 3, 5, 8 -> {
try {
val r = qux()
return "Fail, got $r"
} catch (e: Exception) {
}
}
else -> return "Unknown"
}
return "OK"
}
@@ -0,0 +1,5 @@
STEP 0:
dependencies: lib1, lib2
added file: m.kt
STEP 1..8:
dependencies: lib1, lib2
@@ -0,0 +1,26 @@
MODULES: lib1, lib2, main
STEP 0:
libs: lib1, lib2, main
dirty js: lib1, lib2, main
STEP 1:
libs: lib1, lib2, main
dirty js: lib1, lib2
STEP 2:
libs: lib1, lib2, main
dirty js: lib1, lib2, main
STEP 3:
libs: lib1, lib2, main
dirty js: lib1, lib2
STEP 4:
libs: lib1, lib2, main
dirty js: lib2, main
STEP 5..6:
libs: lib1, lib2, main
dirty js: lib1, lib2
STEP 7:
libs: lib1, lib2, main
dirty js: lib2
STEP 8:
libs: lib1, lib2, main
dirty js: lib1, lib2