[JS IR] Rework incremental cache invalidation

Replace loading the whole world IR with loading only exported (reachable) IR.

 For that purpose the direct and inverse dependency graph is used.
 It is stored in a cache directory and the cache updater keeps it up to date.
 If after loading it is found that other files must be also implicitly rebuilt
 (see rebuilt reasons below), IR for that files also will be loaded.
 This algorithm repeats until the number of implicitly rebuilt files is not 0.

 More rebuilt reasons (dirty state) have been added:
  - added file: this is a new file;
  - modified ir: ir of the file has been updated;
  - updated exports: exports from the file have been added or removed
    (e.g. a function has been used from another file);
  - updated inline imports: imported inline function has been modified
    (transitively);
  - removed inverse depends: a dependent file has been removed;
  - removed direct depends: a dependency file has been removed;
  - removed file: this file has been removed.

 Incremental cache tests has been refactored:
  - The supporting of all rebuilt reasons (dirty states) has been added;
  - New file name format "*.$suffix.kt" for the test steps has been allowed,
    so the syntax highlight works now;
  - Explicit stdlib dependency usage has been removed.
This commit is contained in:
Alexander Korepanov
2022-05-04 11:56:25 +03:00
committed by Space
parent f003f19e1d
commit 9e780afdca
439 changed files with 2904 additions and 1988 deletions
@@ -19,8 +19,10 @@ import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.ir.backend.js.*
import org.jetbrains.kotlin.ir.backend.js.ic.*
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.SourceMapsInfo
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.safeModuleName
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImplForJsIC
@@ -33,18 +35,18 @@ import org.jetbrains.kotlin.test.KotlinTestWithEnvironment
import org.jetbrains.kotlin.test.util.JUnit4Assertions
import org.junit.ComparisonFailure
import java.io.File
import kotlin.io.path.createTempDirectory
import java.util.EnumSet
abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
companion object {
private const val TEST_DATA_DIR_PATH = "js/js.translator/testData/"
private const val BOX_FUNCTION_NAME = "box"
private const val STDLIB_ALIAS = "stdlib"
private const val stdlibAlias = "stdlib"
private const val stdlibModuleName = "kotlin"
private val stdlibKlibPath = File(System.getProperty("kotlin.js.stdlib.klib.path") ?: error("Please set stdlib path")).canonicalPath
private var stdlibCacheDir: File? = null
private val STDLIB_MODULE_NAME = "kotlin".safeModuleName
private val STDLIB_KLIB = File(System.getProperty("kotlin.js.stdlib.klib.path") ?: error("Please set stdlib path")).canonicalPath
private const val boxFunctionName = "box"
private val KT_FILE_IGNORE_PATTERN = Regex("^.*\\..+\\.kt$")
}
override fun createEnvironment(): KotlinCoreEnvironment {
@@ -59,28 +61,6 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
return ModuleInfoParser(infoFile).parse(moduleName)
}
private fun initializeStdlibCache() {
if (stdlibCacheDir != null) return
val cacheDir = createTempDirectory().toFile()
cacheDir.deleteOnExit()
val cacheUpdater = CacheUpdater(
stdlibKlibPath,
listOf(stdlibKlibPath),
createConfiguration(stdlibAlias),
listOf(cacheDir.canonicalPath),
{ IrFactoryImplForJsIC(WholeWorldStageController()) },
null,
::buildCacheForModuleFiles
)
cacheUpdater.actualizeCaches { updateStatus, updatedModule ->
JUnit4Assertions.assertEquals(stdlibKlibPath, updatedModule) { "incorrect std klib path" }
JUnit4Assertions.assertTrue(updateStatus is CacheUpdateStatus.Dirty) { "std klib should be rebuilt" }
}
stdlibCacheDir = cacheDir
}
protected fun doTest(testPath: String) {
val testDirectory = File(testPath)
val testName = testDirectory.name
@@ -96,8 +76,6 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
modulesInfos[module] = parseModuleInfo(module, moduleInfo)
}
initializeStdlibCache()
val workingDir = testWorkingDir(projectInfo.name)
val sourceDir = File(workingDir, "sources").also { it.invalidateDir() }
val buildDir = File(workingDir, "build").also { it.invalidateDir() }
@@ -108,12 +86,10 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
}
private fun resolveModuleArtifact(moduleName: String, buildDir: File): File {
if (moduleName == stdlibAlias) return File(stdlibKlibPath)
return File(File(buildDir, moduleName), "$moduleName.klib")
}
private fun resolveModuleCache(moduleName: String, buildDir: File): File {
if (moduleName == stdlibAlias) return stdlibCacheDir ?: error("stdlib cache corruption")
return File(File(buildDir, moduleName), "cache")
}
@@ -121,6 +97,7 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
val copy = environment.configuration.copy()
copy.put(CommonConfigurationKeys.MODULE_NAME, moduleName)
copy.put(JSConfigurationKeys.MODULE_KIND, ModuleKind.PLAIN)
copy.put(JSConfigurationKeys.PROPERTY_LAZY_INITIALIZATION, true)
return copy
}
@@ -135,9 +112,7 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
val moduleName: String,
val modulePath: String,
val icCacheDir: String,
val directives: Set<StepDirectives>,
val dirtyFiles: List<String> = emptyList(),
val deletedFiles: List<String> = emptyList()
val expectedFileStats: Map<String, Set<String>>
)
private fun setupTestStep(projStepId: Int, module: String): TestStepInfo {
@@ -145,67 +120,64 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
val moduleSourceDir = File(sourceDir, module)
val moduleInfo = moduleInfos[module] ?: error("No module info found for $module")
val moduleStep = moduleInfo.steps[projStepId]
val deletedFiles = mutableListOf<File>()
val deletedFiles = mutableSetOf<String>()
for (modification in moduleStep.modifications) {
modification.execute(moduleTestDir, moduleSourceDir) { deletedFiles.add(it) }
modification.execute(moduleTestDir, moduleSourceDir) { deletedFiles.add(it.name) }
}
val dependencies = moduleStep.dependencies.map { resolveModuleArtifact(it, buildDir) }
val dependencies = moduleStep.dependencies.mapTo(mutableListOf(File(STDLIB_KLIB))) { resolveModuleArtifact(it, buildDir) }
val outputKlibFile = resolveModuleArtifact(module, buildDir)
val configuration = createConfiguration(module)
buildArtifact(configuration, module, moduleSourceDir, dependencies, outputKlibFile)
val expectedFileStats = if (deletedFiles.isEmpty()) {
moduleStep.expectedFileStats
} else {
moduleStep.expectedFileStats + (DirtyFileState.REMOVED_FILE.str to deletedFiles)
}
return TestStepInfo(
module.safeModuleName,
outputKlibFile.canonicalPath,
resolveModuleCache(module, buildDir).canonicalPath,
moduleStep.directives,
moduleStep.dirtyFiles.map { File(moduleSourceDir, it).canonicalPath },
deletedFiles.map { it.canonicalPath }
expectedFileStats
)
}
private fun verifyCacheUpdateStatus(stepId: Int, statuses: Map<String, CacheUpdateStatus>, testInfo: List<TestStepInfo>) {
val expectedInfo = testInfo.filter { StepDirectives.UNUSED_MODULE !in it.directives }
private fun verifyCacheUpdateStats(
stepId: Int, stats: KotlinSourceFileMap<EnumSet<DirtyFileState>>, testInfo: List<TestStepInfo>
) {
val gotStats = stats.filter { it.key.path != STDLIB_KLIB }
JUnit4Assertions.assertEquals(statuses.size, expectedInfo.size) {
"Mismatched updated modules count at step $stepId"
val checkedLibs = mutableSetOf<KotlinLibraryFile>()
for (info in testInfo) {
val libFile = KotlinLibraryFile(info.modulePath)
val updateStatus = gotStats[libFile] ?: emptyMap()
checkedLibs += libFile
val got = mutableMapOf<String, MutableSet<String>>()
for ((srcFile, dirtyStats) in updateStatus) {
for (dirtyStat in dirtyStats) {
got.getOrPut(dirtyStat.str) { mutableSetOf() }.add(File(srcFile.path).name)
}
}
JUnit4Assertions.assertSameElements(got.entries, info.expectedFileStats.entries) {
"Mismatched file stats for module [${info.moduleName}] at step $stepId"
}
}
for (info in expectedInfo) {
JUnit4Assertions.assertTrue(info.modulePath in statuses) {
"Status is missed for ${info.modulePath} at step $stepId"
}
val updateStatus = statuses[info.modulePath]!!
if (StepDirectives.FAST_PATH_UPDATE in info.directives) {
JUnit4Assertions.assertEquals(CacheUpdateStatus.FastPath, updateStatus) {
"Cache has to be checked by fast path, instead it $updateStatus at step $stepId"
}
} else {
JUnit4Assertions.assertNotEquals(CacheUpdateStatus.FastPath, updateStatus) {
"Cache has not to be checked by fast path, but it is at step $stepId"
}
}
val (updated, removed) = when (updateStatus) {
is CacheUpdateStatus.FastPath -> emptySet<String>() to emptySet()
is CacheUpdateStatus.NoDirtyFiles -> emptySet<String>() to updateStatus.removed
is CacheUpdateStatus.Dirty -> updateStatus.updated to updateStatus.removed
}
JUnit4Assertions.assertSameElements(info.dirtyFiles, updated.map { File(it).canonicalPath }) {
"Mismatched DIRTY files for module ${info.moduleName} at step $stepId"
}
JUnit4Assertions.assertSameElements(info.deletedFiles, removed.map { File(it).canonicalPath }) {
"Mismatched DELETED files for module ${info.moduleName} at step $stepId"
for (libFile in gotStats.keys) {
JUnit4Assertions.assertTrue(libFile in checkedLibs) {
"Got unexpected stats for module [${libFile.path}] at step $stepId"
}
}
}
private fun verifyJsExecutableProducerBuildModules(stepId: Int, gotRebuilt: Set<String>, expectedRebuilt: List<String>) {
val expected = expectedRebuilt.map { if (it == stdlibAlias) stdlibModuleName.safeModuleName else it.safeModuleName }
JUnit4Assertions.assertSameElements(expected, gotRebuilt) {
val expected = expectedRebuilt.map { it.safeModuleName }
val got = gotRebuilt.filter { it != STDLIB_MODULE_NAME }
JUnit4Assertions.assertSameElements(got, expected) {
"Mismatched rebuilt modules at step $stepId"
}
}
@@ -226,7 +198,7 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
files = files,
testModuleName = mainModuleName,
testPackageName = null,
testFunctionName = boxFunctionName,
testFunctionName = BOX_FUNCTION_NAME,
testFunctionArgs = "$stepId",
expectedResult = "OK",
withModuleSystem = false
@@ -240,64 +212,42 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
fun executorWithBoxExport(
currentModule: IrModuleFragment,
dependencies: Collection<IrModuleFragment>,
allModules: Collection<IrModuleFragment>,
deserializer: JsIrLinker,
configuration: CompilerConfiguration,
dirtyFiles: Collection<String>?,
artifactCache: ArtifactCache,
dirtyFiles: Collection<IrFile>,
exportedDeclarations: Set<FqName>,
mainArguments: List<String>?
) {
buildCacheForModuleFiles(
currentModule = currentModule,
dependencies = dependencies,
): List<JsIrFragmentAndBinaryAst> {
return buildCacheForModuleFiles(
mainModule = currentModule,
allModules = allModules,
deserializer = deserializer,
configuration = configuration,
dirtyFiles = dirtyFiles,
artifactCache = artifactCache,
exportedDeclarations = exportedDeclarations + FqName(boxFunctionName),
exportedDeclarations = exportedDeclarations + FqName(BOX_FUNCTION_NAME),
mainArguments = mainArguments,
)
}
fun execute() {
val stdlibInfo = TestStepInfo(
moduleName = stdlibModuleName.safeModuleName,
modulePath = stdlibKlibPath,
icCacheDir = stdlibCacheDir!!.canonicalPath,
directives = setOf(StepDirectives.FAST_PATH_UPDATE)
)
val stdlibCacheDir = resolveModuleCache(STDLIB_ALIAS, buildDir).canonicalPath
for (projStep in projectInfo.steps) {
val testInfo = projStep.order.mapTo(mutableListOf(stdlibInfo)) { setupTestStep(projStep.id, it) }
val testInfo = projStep.order.map { setupTestStep(projStep.id, it) }
val configuration = createConfiguration(projStep.order.last())
val cacheUpdater = CacheUpdater(
rootModule = testInfo.last().modulePath,
testInfo.map { it.modulePath },
configuration,
testInfo.map { it.icCacheDir },
{ IrFactoryImplForJsIC(WholeWorldStageController()) },
null,
::executorWithBoxExport
mainModule = testInfo.last().modulePath,
allModules = testInfo.mapTo(mutableListOf(STDLIB_KLIB)) { it.modulePath },
icCachePaths = testInfo.mapTo(mutableListOf(stdlibCacheDir)) { it.icCacheDir },
compilerConfiguration = configuration,
irFactory = { IrFactoryImplForJsIC(WholeWorldStageController()) },
mainArguments = null,
executor = ::executorWithBoxExport
)
val updateStatuses = mutableMapOf<String, CacheUpdateStatus>()
var icCaches = cacheUpdater.actualizeCaches { updateStatus, updatedModule -> updateStatuses[updatedModule] = updateStatus }
verifyCacheUpdateStatus(projStep.id, updateStatuses, testInfo)
val mainModuleCacheDir = icCaches.last().artifactsDir!!
// tests use one stdlib artifact for all cases, patching stdlib cache path here:
// - enable using cached js (stdlib) file through all steps in a test case
// - disable using cached js (stdlib) file through test cases
icCaches = icCaches.map {
when (it.moduleSafeName) {
stdlibModuleName.safeModuleName -> {
val stdlibDir = File(mainModuleCacheDir, stdlibAlias).apply { mkdirs() }
ModuleArtifact(stdlibModuleName, it.fileArtifacts, stdlibDir, it.forceRebuildJs)
}
else -> it
}
}
val icCaches = cacheUpdater.actualizeCaches()
verifyCacheUpdateStats(projStep.id, cacheUpdater.getDirtyFileStats(), testInfo)
val jsExecutableProducer = JsExecutableProducer(
mainModuleName = testInfo.last().moduleName,
@@ -315,9 +265,11 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
}
}
private fun String.isAllowedKtFile() = endsWith(".kt") && !KT_FILE_IGNORE_PATTERN.matches(this)
private fun File.filteredKtFiles(): Collection<File> {
assert(isDirectory && exists())
return listFiles { _, name -> name.endsWith(".kt") }!!.toList()
return listFiles { _, name -> name.isAllowedKtFile() }!!.toList()
}
private fun KotlinCoreEnvironment.createPsiFile(file: File): KtFile {
@@ -332,11 +284,7 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
}
private fun buildArtifact(
configuration: CompilerConfiguration,
moduleName: String,
sourceDir: File,
dependencies: Collection<File>,
outputKlibFile: File
configuration: CompilerConfiguration, moduleName: String, sourceDir: File, dependencies: Collection<File>, outputKlibFile: File
) {
if (outputKlibFile.exists()) outputKlibFile.delete()
@@ -345,11 +293,7 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
val sourceFiles = sourceDir.filteredKtFiles().map { environment.createPsiFile(it) }
val sourceModule = prepareAnalyzedSourceModule(
projectJs,
sourceFiles,
configuration,
dependencies.map { it.canonicalPath },
emptyList(), // TODO
projectJs, sourceFiles, configuration, dependencies.map { it.canonicalPath }, emptyList(), // TODO
AnalyzerWithCompilerReport(configuration)
)
@@ -362,7 +306,7 @@ abstract class AbstractInvalidationTest : KotlinTestWithEnvironment() {
File(buildDir, module).invalidateDir()
val testModuleDir = File(testDir, module)
testModuleDir.listFiles { _, fileName -> fileName.endsWith(".kt") }!!.forEach { file ->
testModuleDir.listFiles { _, fileName -> fileName.isAllowedKtFile() }!!.forEach { file ->
assert(!file.isDirectory)
val fileName = file.name
val workingFile = File(moduleSourceDir, fileName)
@@ -26,15 +26,15 @@ import org.jetbrains.kotlin.test.services.jsLibraryProvider
import java.io.ByteArrayInputStream
import java.io.File
private class TestArtifactCache(var moduleName: String? = null) : ArtifactCache() {
override fun fetchArtifacts(): ModuleArtifact {
private class TestArtifactCache(val moduleName: String, val binaryAsts: MutableMap<String, ByteArray> = mutableMapOf()) {
fun fetchArtifacts(): ModuleArtifact {
val deserializer = JsIrAstDeserializer()
return ModuleArtifact(
moduleName = moduleName ?: error("Module name is not set"),
moduleName = moduleName,
fileArtifacts = binaryAsts.entries.map {
SrcFileArtifact(
srcFilePath = it.key,
// TODO: It will be better to use saved fragments (this.fragments), but it doesn't work
// TODO: It will be better to use saved fragments (from JsIrFragmentAndBinaryAst), but it doesn't work
// Merger.merge() + JsNode.resolveTemporaryNames() modify fragments,
// therefore the sequential calls produce different results
fragment = deserializer.deserialize(ByteArrayInputStream(it.value))
@@ -42,13 +42,6 @@ private class TestArtifactCache(var moduleName: String? = null) : ArtifactCache(
}
)
}
fun invalidateForFile(srcPath: String) {
binaryAsts.remove(srcPath)
fragments.remove(srcPath)
}
fun getAst(srcPath: String) = binaryAsts[srcPath]
}
class JsIrIncrementalDataProvider(private val testServices: TestServices) : TestService {
@@ -76,8 +69,8 @@ class JsIrIncrementalDataProvider(private val testServices: TestServices) : Test
for (testFile in module.files) {
if (JsEnvironmentConfigurationDirectives.RECOMPILE in testFile.directives) {
val fileName = "/${testFile.name}"
oldBinaryAsts[fileName] = moduleCache.getAst(fileName) ?: error("No AST found for $fileName")
moduleCache.invalidateForFile(fileName)
oldBinaryAsts[fileName] = moduleCache.binaryAsts[fileName] ?: error("No AST found for $fileName")
moduleCache.binaryAsts.remove(fileName)
}
}
@@ -122,43 +115,50 @@ class JsIrIncrementalDataProvider(private val testServices: TestServices) : Test
mainArguments: List<String>?
) {
val canonicalPath = File(path).canonicalPath
var moduleCache = predefinedKlibHasIcCache[canonicalPath]
val predefinedModuleCache = predefinedKlibHasIcCache[canonicalPath]
if (predefinedModuleCache != null) {
icCache[canonicalPath] = predefinedModuleCache
return
}
if (moduleCache == null) {
moduleCache = icCache[canonicalPath] ?: TestArtifactCache()
val libs = allDependencies.associateBy { File(it.libraryFile.path).canonicalPath }
val libs = allDependencies.associateBy { File(it.libraryFile.path).canonicalPath }
val nameToKotlinLibrary: Map<String, KotlinLibrary> = libs.values.associateBy { it.moduleName }
val nameToKotlinLibrary: Map<String, KotlinLibrary> = libs.values.associateBy { it.moduleName }
val dependencyGraph = libs.values.associateWith {
it.manifestProperties.propertyList(KLIB_PROPERTY_DEPENDS, escapeInQuotes = true).map { depName ->
nameToKotlinLibrary[depName] ?: error("No Library found for $depName")
}
val dependencyGraph = libs.values.associateWith {
it.manifestProperties.propertyList(KLIB_PROPERTY_DEPENDS, escapeInQuotes = true).map { depName ->
nameToKotlinLibrary[depName] ?: error("No Library found for $depName")
}
}
val currentLib = libs[File(canonicalPath).canonicalPath] ?: error("Expected library at $canonicalPath")
val currentLib = libs[File(canonicalPath).canonicalPath] ?: error("Expected library at $canonicalPath")
val testPackage = extractTestPackage(testServices)
val testPackage = extractTestPackage(testServices)
moduleCache.moduleName = rebuildCacheForDirtyFiles(
currentLib,
configuration,
dependencyGraph,
dirtyFiles,
moduleCache,
IrFactoryImplForJsIC(WholeWorldStageController()),
setOf(FqName.fromSegments(listOfNotNull(testPackage, JsBoxRunner.TEST_FUNCTION))),
mainArguments,
)
val (mainModuleIr, rebuiltFiles) = rebuildCacheForDirtyFiles(
currentLib,
configuration,
dependencyGraph,
dirtyFiles,
IrFactoryImplForJsIC(WholeWorldStageController()),
setOf(FqName.fromSegments(listOfNotNull(testPackage, JsBoxRunner.TEST_FUNCTION))),
mainArguments,
)
if (canonicalPath in predefinedKlibHasIcCache) {
predefinedKlibHasIcCache[canonicalPath] = moduleCache
val moduleCache = icCache[canonicalPath] ?: TestArtifactCache(mainModuleIr.name.asString())
for (rebuiltFile in rebuiltFiles) {
if (rebuiltFile.irFile.module == mainModuleIr) {
moduleCache.binaryAsts[rebuiltFile.irFile.fileEntry.name] = rebuiltFile.binaryAst
}
}
if (canonicalPath in predefinedKlibHasIcCache) {
predefinedKlibHasIcCache[canonicalPath] = moduleCache
}
icCache[canonicalPath] = moduleCache
}
}
val TestServices.jsIrIncrementalDataProvider: JsIrIncrementalDataProvider by TestServices.testServiceAccessor()
@@ -26,10 +26,30 @@ public class InvalidationTestGenerated extends AbstractInvalidationTest {
KotlinTestUtils.runTest(this::doTest, TargetBackend.JS_IR, testDataFilePath);
}
@TestMetadata("addUpdateRemoveDependentFile")
public void testAddUpdateRemoveDependentFile() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/addUpdateRemoveDependentFile/");
}
@TestMetadata("addUpdateRemoveDependentModule")
public void testAddUpdateRemoveDependentModule() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/addUpdateRemoveDependentModule/");
}
public void testAllFilesPresentInInvalidation() throws Exception {
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/incremental/invalidation"), Pattern.compile("^([^_](.+))$"), null, TargetBackend.JS_IR, false);
}
@TestMetadata("circleExportsUpdate")
public void testCircleExportsUpdate() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/circleExportsUpdate/");
}
@TestMetadata("circleInlineImportsUpdate")
public void testCircleInlineImportsUpdate() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/circleInlineImportsUpdate/");
}
@TestMetadata("class")
public void testClass() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/class/");
@@ -50,6 +70,16 @@ public class InvalidationTestGenerated extends AbstractInvalidationTest {
runTest("js/js.translator/testData/incremental/invalidation/crossModuleReferences/");
}
@TestMetadata("eagerInitialization")
public void testEagerInitialization() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/eagerInitialization/");
}
@TestMetadata("exportsThroughInlineFunction")
public void testExportsThroughInlineFunction() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/exportsThroughInlineFunction/");
}
@TestMetadata("fakeOverride")
public void testFakeOverride() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/fakeOverride/");
@@ -180,6 +210,16 @@ public class InvalidationTestGenerated extends AbstractInvalidationTest {
runTest("js/js.translator/testData/incremental/invalidation/unicodeSerializationAndDeserialization/");
}
@TestMetadata("updateExports")
public void testUpdateExports() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/updateExports/");
}
@TestMetadata("updateExportsAndInlineImports")
public void testUpdateExportsAndInlineImports() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/updateExportsAndInlineImports/");
}
@TestMetadata("variance")
public void testVariance() throws Exception {
runTest("js/js.translator/testData/incremental/invalidation/variance/");
@@ -0,0 +1,3 @@
fun foo0() = 42
fun foo0_1() = 78
@@ -0,0 +1 @@
fun foo1() = foo0_1() / 2
@@ -0,0 +1 @@
fun foo1() = 123
@@ -0,0 +1,29 @@
STEP 0:
modifications:
U : l1a.0.kt -> l1a.kt
added file: l1a.kt
STEP 1:
modifications:
U : l1b.1.kt -> l1b.kt
added file: l1b.kt
updated exports: l1a.kt
STEP 2:
modifications:
U : l1b.2.kt -> l1b.kt
modified ir: l1b.kt
updated exports: l1a.kt
STEP 3:
updated exports: l1a.kt, l1b.kt
STEP 4:
updated exports: l1a.kt, l1b.kt
STEP 5:
modifications:
U : l1b.1.kt -> l1b.kt
modified ir: l1b.kt
updated exports: l1a.kt
STEP 6:
modifications:
D : l1b.kt
removed inverse depends: l1a.kt
STEP 7:
updated exports: l1a.kt
@@ -0,0 +1 @@
inline fun qux() = foo0()
@@ -0,0 +1 @@
inline fun qux() = foo0() + foo1()
@@ -0,0 +1 @@
inline fun qux() = foo0() + foo0_1()
@@ -0,0 +1,34 @@
STEP 0:
dependencies: lib1
modifications:
U : l2.0.kt -> l2.kt
added file: l2.kt
STEP 1:
dependencies: lib1
modifications:
U : l2.1.kt -> l2.kt
modified ir: l2.kt
STEP 2:
dependencies: lib1
STEP 3:
dependencies: lib1
modifications:
U : l2.3.kt -> l2.kt
modified ir: l2.kt
STEP 4:
dependencies: lib1
modifications:
U : l2.1.kt -> l2.kt
modified ir: l2.kt
STEP 5:
dependencies: lib1
STEP 6:
dependencies: lib1
modifications:
U : l2.0.kt -> l2.kt
modified ir: l2.kt
STEP 7:
dependencies: lib1
modifications:
U : l2.3.kt -> l2.kt
modified ir: l2.kt
@@ -0,0 +1,10 @@
fun box(stepId: Int): String {
when (stepId) {
0, 6 -> if (qux() != 42) return "Fail"
1, 5 -> if (qux() != 42 + 78 / 2) return "Fail"
2, 4 -> if (qux() != 42 + 123) return "Fail"
3, 7 -> if (qux() != 42 + 78) return "Fail"
else -> return "Unknown"
}
return "OK"
}
@@ -0,0 +1,19 @@
STEP 0:
dependencies: lib1, lib2
added file: m.kt
STEP 1:
dependencies: lib1, lib2
updated inline imports: m.kt
STEP 2:
dependencies: lib1, lib2
STEP 3..4:
dependencies: lib1, lib2
updated inline imports: m.kt
STEP 5:
dependencies: lib1, lib2
STEP 6:
dependencies: lib1, lib2
updated inline imports: m.kt
STEP 7:
dependencies: lib1, lib2
updated inline imports: m.kt
@@ -0,0 +1,17 @@
MODULES: lib1, lib2, main
STEP 0..1:
libs: lib1, lib2, main
dirty js: lib1, lib2, main
STEP 2:
libs: lib1, lib2, main
dirty js: lib1
STEP 3..4:
libs: lib1, lib2, main
dirty js: lib1, lib2, main
STEP 5:
libs: lib1, lib2, main
dirty js: lib1
STEP 6..7:
libs: lib1, lib2, main
dirty js: lib1, lib2, main
@@ -0,0 +1 @@
fun qux1() = 42 + foo() * 8
@@ -0,0 +1 @@
fun qux1() = 42
@@ -0,0 +1 @@
fun foo() = 99
@@ -0,0 +1,11 @@
STEP 0:
added file: l1a.kt, l1b.kt
STEP 1:
updated exports: l1b.kt, l1a.kt
STEP 2:
updated exports: l1a.kt
STEP 3:
STEP 4:
modifications:
U : l1a.4.kt -> l1a.kt
modified ir: l1a.kt
@@ -0,0 +1 @@
inline fun qux2() = foo() + 32
@@ -0,0 +1 @@
inline fun qux2() = foo() + 32 + qux1()
@@ -0,0 +1,15 @@
STEP 0:
STEP 1:
dependencies: lib1
modifications:
U : l2.1.kt -> l2.kt
added file: l2.kt
STEP 2:
dependencies: lib1
modifications:
U : l2.2.kt -> l2.kt
modified ir: l2.kt
STEP 3:
modifications:
D : l2.kt
STEP 4:
@@ -0,0 +1,8 @@
fun box(stepId: Int): String {
when (stepId) {
0, 3 -> if (qux1() != 42) return "Fail"
4 -> if (qux1() != 42 + 99 * 8) return "Fail"
else -> return "Unknown"
}
return "OK"
}
@@ -0,0 +1,8 @@
fun box(stepId: Int): String {
when (stepId) {
1 -> if (qux2() != 99 + 32) return "Fail"
2 -> if (qux2() != 99 + 32 + 42) return "Fail"
else -> return "Unknown"
}
return "OK"
}
@@ -0,0 +1,20 @@
STEP 0:
dependencies: lib1
modifications:
U : m.0.kt -> m.kt
added file: m.kt
STEP 1:
dependencies: lib1, lib2
modifications:
U : m.1.kt -> m.kt
modified ir: m.kt
STEP 2:
dependencies: lib1, lib2
updated inline imports: m.kt
STEP 3:
dependencies: lib1
modifications:
U : m.0.kt -> m.kt
modified ir: m.kt
STEP 4:
dependencies: lib1
@@ -0,0 +1,14 @@
MODULES: lib1, lib2, main
STEP 0:
libs: lib1, main
dirty js: lib1, main
STEP 1..2:
libs: lib1, lib2, main
dirty js: lib1, lib2, main
STEP 3:
libs: lib1, main
dirty js: lib1, main
STEP 4:
libs: lib1, main
dirty js: lib1
@@ -0,0 +1,3 @@
fun f1() = "${f1_1()} ${f2_1()} ${f3_1()}"
fun f2() = "empty"
@@ -0,0 +1,3 @@
fun f1() = "${f1_1()} ${f2_1()} ${f3_1()}"
fun f2() = f1_2()
@@ -0,0 +1,7 @@
fun f1_1() = "f1_1"
fun f1_2() = f2_2()
fun f1_3() = f2_3()
fun f1_4() = "something"
@@ -0,0 +1,5 @@
fun f2_1() = "f2_1"
fun f2_2() = f3_2()
fun f2_3() = f3_3()
@@ -0,0 +1,5 @@
fun f3_1() = "f3_1"
fun f3_2() = f1_3()
fun f3_3() = f1_4()
@@ -0,0 +1,19 @@
STEP 0:
modifications:
U : f.0.kt -> f.kt
added file: f.kt, f1.kt, f2.kt, f3.kt
STEP 1:
modifications:
U : f.1.kt -> f.kt
modified ir: f.kt
updated exports: f1.kt, f2.kt, f3.kt
STEP 2:
modifications:
U : f.0.kt -> f.kt
modified ir: f.kt
updated exports: f1.kt, f2.kt, f3.kt
STEP 3:
modifications:
U : f.1.kt -> f.kt
modified ir: f.kt
updated exports: f1.kt, f2.kt, f3.kt
@@ -0,0 +1,14 @@
fun box(stepId: Int): String {
when (stepId) {
0, 2 -> {
if (f1() != "f1_1 f2_1 f3_1") return "Fail f1"
if (f2() != "empty") return "Fail f2"
}
1, 3 -> {
if (f1() != "f1_1 f2_1 f3_1") return "Fail f1"
if (f2() != "something") return "Fail f2"
}
else -> return "Unknown"
}
return "OK"
}
@@ -0,0 +1,5 @@
STEP 0:
dependencies: lib1
added file: m.kt
STEP 1..3:
dependencies: lib1
@@ -0,0 +1,8 @@
MODULES: lib1, main
STEP 0:
libs: lib1, main
dirty js: lib1, main
STEP 1..3:
libs: lib1, main
dirty js: lib1
@@ -0,0 +1 @@
inline fun f1() = f1_2()
@@ -0,0 +1,5 @@
inline fun f1_2() = "f1_2 -> ${f2_2()}"
inline fun f1_3() = "f1_3 -> ${f2_3()}"
inline fun f1_4() = "f1_4 -> stop"
@@ -0,0 +1,5 @@
inline fun f1_2() = "f1_2 -> ${f2_2()}"
inline fun f1_3() = "f1_3 -> ${f2_3()}"
inline fun f1_4() = "f1_4 -> end"
@@ -0,0 +1,3 @@
inline fun f2_2() = "f2_2 -> ${f3_2()}"
inline fun f2_3() = "f2_3 -> ${f3_3()}"
@@ -0,0 +1,3 @@
inline fun f3_2() = "f3_2 -> ${f1_3()}"
inline fun f3_3() = "f3_3 -> ${f1_4()}"
@@ -0,0 +1,14 @@
STEP 0:
modifications:
U : f1.0.kt -> f1.kt
added file: f.kt, f1.kt, f2.kt, f3.kt
STEP 1:
modifications:
U : f1.1.kt -> f1.kt
modified ir: f1.kt
updated inline imports: f.kt, f2.kt, f3.kt
STEP 2:
modifications:
U : f1.0.kt -> f1.kt
modified ir: f1.kt
updated inline imports: f.kt, f2.kt, f3.kt
@@ -0,0 +1,12 @@
fun box(stepId: Int): String {
when (stepId) {
0, 2 -> {
if (f1() != "f1_2 -> f2_2 -> f3_2 -> f1_3 -> f2_3 -> f3_3 -> f1_4 -> stop") return "Fail"
}
1 -> {
if (f1() != "f1_2 -> f2_2 -> f3_2 -> f1_3 -> f2_3 -> f3_3 -> f1_4 -> end") return "Fail"
}
else -> return "Unknown"
}
return "OK"
}
@@ -0,0 +1,6 @@
STEP 0:
dependencies: lib1
added file: m.kt
STEP 1..2:
dependencies: lib1
updated inline imports: m.kt
@@ -0,0 +1,5 @@
MODULES: lib1, main
STEP 0..2:
libs: lib1, main
dirty js: lib1, main
@@ -1,20 +1,16 @@
STEP 0:
dependencies: stdlib
modifications:
U : l1.kt.0_simple_class.txt -> l1.kt
dirty: l1.kt
U : l1.0_simple_class.kt -> l1.kt
added file: l1.kt
STEP 1:
dependencies: stdlib
modifications:
U : l1.kt.1_data_class.txt -> l1.kt
dirty: l1.kt
U : l1.1_data_class.kt -> l1.kt
modified ir: l1.kt
STEP 2:
dependencies: stdlib
modifications:
U : l1.kt.0_simple_class.txt -> l1.kt
dirty: l1.kt
U : l1.0_simple_class.kt -> l1.kt
modified ir: l1.kt
STEP 3:
dependencies: stdlib
modifications:
U : l1.kt.2_inline_class.txt -> l1.kt
dirty: l1.kt
U : l1.2_inline_class.kt -> l1.kt
modified ir: l1.kt
@@ -1,4 +1,3 @@
fun box(stepId: Int): String {
when (stepId) {
0, 2 -> if (Demo(15) == Demo(15)) return "Fail"
@@ -1,5 +1,5 @@
STEP 0:
dependencies: stdlib, lib1
dirty: m.kt
dependencies: lib1
added file: m.kt
STEP 1..3:
dependencies: stdlib, lib1
dependencies: lib1
@@ -2,7 +2,7 @@ MODULES: lib1, main
STEP 0:
libs: lib1, main
dirty js: stdlib, lib1, main
dirty js: lib1, main
STEP 1..3:
libs: lib1, main
dirty js: stdlib, lib1
dirty js: lib1
@@ -1,4 +1,3 @@
class Demo(val x: String) {
fun foo() = "foo $x"
inline fun foo_inline() = "inline foo $x"
@@ -1,4 +1,3 @@
class Demo(val x: String) {
fun foo() = "foo $x update"
inline fun foo_inline() = "inline foo $x"
@@ -1,4 +1,3 @@
class Demo(val x: String) {
fun foo() = "foo $x update"
inline fun foo_inline() = "inline foo $x update"
@@ -1,4 +1,3 @@
class Demo(val x: String, val y: String = "default") {
fun foo() = "foo $x update"
inline fun foo_inline() = "inline foo $x update"
@@ -1,4 +1,3 @@
class Demo(val x: String, val y: String = "default") {
fun foo() = "foo $x update"
inline fun foo_inline() = "inline foo $x update"
@@ -1,4 +1,3 @@
class Demo(val x: String, val y: String = "default") {
fun foo() = "foo $x update"
inline fun foo_inline() = "inline foo $x update"
@@ -1,4 +1,3 @@
class Demo(val x: String, val y: String = "default") {
fun foo() = "foo $x update"
inline fun foo_inline() = "inline foo $x update"
@@ -1,60 +1,48 @@
STEP 0:
dependencies: stdlib
modifications:
U : l1.kt.0_init.txt -> l1.kt
dirty: l1.kt
U : l1.0_init.kt -> l1.kt
added file: l1.kt
STEP 1:
dependencies: stdlib
modifications:
U : l1.kt.1_update_fun.txt -> l1.kt
dirty: l1.kt
U : l1.1_update_fun.kt -> l1.kt
modified ir: l1.kt
STEP 2:
dependencies: stdlib
modifications:
U : l1.kt.2_update_inline_fun.txt -> l1.kt
dirty: l1.kt
U : l1.2_update_inline_fun.kt -> l1.kt
modified ir: l1.kt
STEP 3:
dependencies: stdlib
modifications:
U : l1.kt.3_add_param.txt -> l1.kt
dirty: l1.kt
U : l1.3_add_param.kt -> l1.kt
modified ir: l1.kt
STEP 4:
dependencies: stdlib
modifications:
U : l1.kt.4_add_field.txt -> l1.kt
dirty: l1.kt
U : l1.4_add_field.kt -> l1.kt
modified ir: l1.kt
STEP 5:
dependencies: stdlib
modifications:
U : l1.kt.5_update_field.txt -> l1.kt
dirty: l1.kt
U : l1.5_update_field.kt -> l1.kt
modified ir: l1.kt
STEP 6:
dependencies: stdlib
modifications:
U : l1.kt.6_update_unused_inline.txt -> l1.kt
dirty: l1.kt
U : l1.6_update_unused_inline.kt -> l1.kt
modified ir: l1.kt
STEP 7:
dependencies: stdlib
modifications:
U : l1.kt.7_add_unused_interface.txt -> l1.kt
dirty: l1.kt
U : l1.7_add_unused_interface.kt -> l1.kt
modified ir: l1.kt
STEP 8:
dependencies: stdlib
modifications:
U : l1.kt.8_use_interface.txt -> l1.kt
dirty: l1.kt
U : l1.8_use_interface.kt -> l1.kt
modified ir: l1.kt
STEP 9:
dependencies: stdlib
modifications:
U : l1.kt.9_update_interface.txt -> l1.kt
dirty: l1.kt
U : l1.9_update_interface.kt -> l1.kt
modified ir: l1.kt
STEP 10:
dependencies: stdlib
modifications:
U : l1.kt.10_update_field_type.txt -> l1.kt
dirty: l1.kt
U : l1.10_update_field_type.kt -> l1.kt
modified ir: l1.kt
STEP 11:
dependencies: stdlib
modifications:
U : l1.kt.11_update_field_type.txt -> l1.kt
dirty: l1.kt
U : l1.11_update_field_type.kt -> l1.kt
modified ir: l1.kt
@@ -1,4 +1,3 @@
fun isEqual(l: Any?, r: Any?) = if (l == r) true else null
fun box(stepId: Int): String {
@@ -1,13 +1,16 @@
STEP 0:
dependencies: stdlib, lib1
dirty: m.kt
dependencies: lib1
added file: m.kt
STEP 1:
dependencies: stdlib, lib1
STEP 2..3:
dependencies: stdlib, lib1
dirty: m.kt
dependencies: lib1
STEP 2:
dependencies: lib1
updated inline imports: m.kt
STEP 3:
dependencies: lib1
modified ir: m.kt
STEP 4..9:
dependencies: stdlib, lib1
dependencies: lib1
STEP 10..11:
dependencies: stdlib, lib1
dirty: m.kt
dependencies: lib1
modified ir: m.kt
@@ -2,7 +2,7 @@ MODULES: lib1, main
STEP 0:
libs: lib1, main
dirty js: stdlib, lib1, main
dirty js: lib1, main
STEP 1:
libs: lib1, main
dirty js: lib1
@@ -14,7 +14,7 @@ STEP 4..6:
dirty js: lib1
STEP 7:
libs: lib1, main
dirty js: stdlib, lib1
dirty js: lib1
STEP 8..9:
libs: lib1, main
dirty js: lib1
@@ -0,0 +1 @@
const val FOO = "BAR"
@@ -1 +1 @@
const val FOO = "FOO"
const val FOO = "FOO"
@@ -1 +0,0 @@
const val FOO = "BAR"
@@ -1,8 +1,6 @@
STEP 0:
dependencies: stdlib
dirty: l1.kt
added file: l1.kt
STEP 1:
dependencies: stdlib
modifications:
U : l1.kt.1 -> l1.kt
dirty: l1.kt
U : l1.1.kt -> l1.kt
modified ir: l1.kt
@@ -1,3 +1,2 @@
fun qux() = FOO
inline fun bar() = FOO
@@ -1,5 +1,5 @@
STEP 0:
dependencies: stdlib, lib1
dirty: l2.kt
dependencies: lib1
added file: l2.kt
STEP 1:
dependencies: stdlib, lib1
dependencies: lib1
@@ -1,4 +1,3 @@
fun box(stepId: Int): String {
when (stepId) {
0 -> {
@@ -1,5 +1,5 @@
STEP 0:
dependencies: stdlib, lib1, lib2
dirty: m.kt
dependencies: lib1, lib2
added file: m.kt
STEP 1:
dependencies: stdlib, lib1, lib2
dependencies: lib1, lib2
@@ -2,7 +2,7 @@ MODULES: lib1, lib2, main
STEP 0:
libs: lib1, lib2, main
dirty js: stdlib, lib1, lib2, main
dirty js: lib1, lib2, main
STEP 1:
libs: lib1, lib2, main
dirty js: lib1
@@ -1,6 +1,4 @@
STEP 0:
dependencies: stdlib
dirty: l1.kt
added file: l1.kt
STEP 1..6:
dependencies: stdlib
flags: FP
updated exports: l1.kt
@@ -1,2 +1 @@
fun qux() = foo() + 2
@@ -1,31 +1,30 @@
STEP 0:
dependencies: stdlib, lib1
dirty: l2.kt
dependencies: lib1
added file: l2.kt
STEP 1:
dependencies: stdlib, lib1
dependencies: lib1
modifications:
U : l2.kt.1.txt -> l2.kt
dirty: l2.kt
U : l2.1.kt -> l2.kt
modified ir: l2.kt
STEP 2:
dependencies: stdlib, lib1
dependencies: lib1
modifications:
U : l2.kt.2.txt -> l2.kt
dirty: l2.kt
U : l2.2.kt -> l2.kt
modified ir: l2.kt
STEP 3:
dependencies: stdlib, lib1
dependencies: lib1
modifications:
U : l2.kt.3.txt -> l2.kt
dirty: l2.kt
U : l2.3.kt -> l2.kt
modified ir: l2.kt
STEP 4:
dependencies: stdlib, lib1
dependencies: lib1
modifications:
U : l2.kt.4.txt -> l2.kt
dirty: l2.kt
U : l2.4.kt -> l2.kt
modified ir: l2.kt
STEP 5:
dependencies: stdlib, lib1
flags: FP
dependencies: lib1
STEP 6:
dependencies: stdlib, lib1
dependencies: lib1
modifications:
U : l2.kt.6.txt -> l2.kt
dirty: l2.kt
U : l2.6.kt -> l2.kt
modified ir: l2.kt
@@ -1,4 +1,3 @@
fun box(stepId: Int): String {
when (stepId) {
5 -> if (qux() != 14) return "Fail"
@@ -1,4 +1,3 @@
fun box(stepId: Int): String {
when (stepId) {
0 -> if (qux() != 44) return "Fail"
@@ -1,12 +1,12 @@
STEP 0:
dependencies: stdlib, lib1, lib2
dirty: m.kt
dependencies: lib1, lib2
added file: m.kt
STEP 1..4:
dependencies: stdlib, lib1, lib2
dependencies: lib1, lib2
STEP 5:
dependencies: stdlib, lib1, lib2
dependencies: lib1, lib2
modifications:
U : m.kt.5.txt -> m.kt
dirty: m.kt
U : m.5.kt -> m.kt
modified ir: m.kt
STEP 6:
dependencies: stdlib, lib1, lib2
dependencies: lib1, lib2
@@ -2,7 +2,7 @@ MODULES: lib1, lib2, main
STEP 0:
libs: lib1, lib2, main
dirty js: stdlib, lib1, lib2, main
dirty js: lib1, lib2, main
STEP 1..4:
libs: lib1, lib2, main
dirty js: lib1, lib2
@@ -0,0 +1,2 @@
var z1 = false
var z2 = false
@@ -0,0 +1,7 @@
//@OptIn(kotlin.ExperimentalStdlibApi::class)
//@EagerInitialization
val x = run { z1 = true; 42 }
//@OptIn(kotlin.ExperimentalStdlibApi::class)
//@EagerInitialization
val y = run { z2 = true; 117 }
@@ -0,0 +1,7 @@
@OptIn(kotlin.ExperimentalStdlibApi::class)
@EagerInitialization
val x = run { z1 = true; 42 }
//@OptIn(kotlin.ExperimentalStdlibApi::class)
//@EagerInitialization
val y = run { z2 = true; 117 }
@@ -0,0 +1,7 @@
@OptIn(kotlin.ExperimentalStdlibApi::class)
@EagerInitialization
val x = run { z1 = true; 42 }
@OptIn(kotlin.ExperimentalStdlibApi::class)
@EagerInitialization
val y = run { z2 = true; 117 }
@@ -0,0 +1,16 @@
STEP 0:
modifications:
U : l2.0.kt -> l2.kt
added file: l1.kt, l2.kt
STEP 1:
modifications:
U : l2.1.kt -> l2.kt
modified ir: l2.kt
STEP 2:
modifications:
U : l2.2.kt -> l2.kt
modified ir: l2.kt
STEP 3:
modifications:
U : l2.0.kt -> l2.kt
modified ir: l2.kt
@@ -0,0 +1,18 @@
fun box(stepId: Int): String {
when (stepId) {
0, 3 -> {
if (z1) return "Fail z1"
if (z2) return "Fail z2"
}
1 -> {
if (!z1) return "Fail z1"
if (z2) return "Fail z2"
}
2 -> {
if (!z1) return "Fail z1"
if (!z2) return "Fail z2"
}
else -> return "Unknown"
}
return "OK"
}
@@ -0,0 +1,5 @@
STEP 0:
dependencies: lib1
added file: m.kt
STEP 1..3:
dependencies: lib1
@@ -0,0 +1,8 @@
MODULES: lib1, main
STEP 0:
libs: lib1, main
dirty js: lib1, main
STEP 1..3:
libs: lib1, main
dirty js: lib1
@@ -0,0 +1,3 @@
fun foo() = "hello"
inline fun fooInline() = "hello inline 0"
@@ -0,0 +1,3 @@
fun foo() = "hello"
inline fun fooInline() = "hello inline 3"
@@ -0,0 +1,6 @@
fun foo(): String {
if (false) lib1Foo()
return "hello 5"
}
inline fun fooInline() = "hello inline 3"
@@ -0,0 +1,6 @@
fun foo(flag: Boolean = false): String {
if (flag) lib1Foo()
return "hello 6"
}
inline fun fooInline() = "hello inline 3"
@@ -0,0 +1 @@
inline fun lib1Foo() = "empty"

Some files were not shown because too many files have changed in this diff Show More