Inline function changed – rebuild all chunk.

Original commit: aa9f7a73c9
This commit is contained in:
Evgeny Gerashchenko
2014-07-01 22:38:35 +04:00
parent 9d428dc2ed
commit 2f72958d25
9 changed files with 91 additions and 18 deletions
@@ -16,7 +16,6 @@
package org.jetbrains.jet.jps.build; package org.jetbrains.jet.jps.build;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Key;
import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.util.io.StreamUtil; import com.intellij.openapi.util.io.StreamUtil;
@@ -237,7 +236,7 @@ public class KotlinBuilder extends ModuleLevelBuilder {
} }
cache.clearCacheForRemovedFiles(moduleIdsAndFiles, outDirectories); cache.clearCacheForRemovedFiles(moduleIdsAndFiles, outDirectories);
boolean significantChanges = false; IncrementalCacheImpl.RecompilationDecision recompilationDecision = IncrementalCacheImpl.RecompilationDecision.DO_NOTHING;
for (SimpleOutputItem outputItem : outputItemCollector.getOutputs()) { for (SimpleOutputItem outputItem : outputItemCollector.getOutputs()) {
BuildTarget<?> target = null; BuildTarget<?> target = null;
@@ -253,17 +252,20 @@ public class KotlinBuilder extends ModuleLevelBuilder {
File outputFile = outputItem.getOutputFile(); File outputFile = outputItem.getOutputFile();
if (IncrementalCompilation.ENABLED) { if (IncrementalCompilation.ENABLED) {
if (cache.saveFileToCache(target.getId(), sourceFiles, outputFile)) { IncrementalCacheImpl.RecompilationDecision newDecision = cache.saveFileToCache(target.getId(), sourceFiles, outputFile);
significantChanges = true; recompilationDecision = recompilationDecision.merge(newDecision);
}
} }
outputConsumer.registerOutputFile(target, outputFile, paths(sourceFiles)); outputConsumer.registerOutputFile(target, outputFile, paths(sourceFiles));
} }
if (IncrementalCompilation.ENABLED) { if (IncrementalCompilation.ENABLED) {
// TODO should mark dependencies as dirty, as well if (recompilationDecision == IncrementalCacheImpl.RecompilationDecision.RECOMPILE_ALL) {
if (significantChanges) { allCompiledFiles.clear();
return ExitCode.CHUNK_REBUILD_REQUIRED;
}
if (recompilationDecision == IncrementalCacheImpl.RecompilationDecision.COMPILE_OTHERS) {
// TODO should mark dependencies as dirty, as well
FSOperations.markDirty(context, chunk, new FileFilter() { FSOperations.markDirty(context, chunk, new FileFilter() {
@Override @Override
public boolean accept(@NotNull File file) { public boolean accept(@NotNull File file) {
@@ -16,6 +16,7 @@
package org.jetbrains.jet.jps.incremental package org.jetbrains.jet.jps.incremental
import org.jetbrains.jet.jps.incremental.IncrementalCacheImpl.RecompilationDecision.*
import java.io.File import java.io.File
import com.intellij.util.io.PersistentHashMap import com.intellij.util.io.PersistentHashMap
import java.io.DataOutput import java.io.DataOutput
@@ -56,10 +57,10 @@ public class IncrementalCacheImpl(val baseDir: File): IncrementalCache {
private val inlineFunctionsMap = InlineFunctionsMap() private val inlineFunctionsMap = InlineFunctionsMap()
private val packagePartMap = PackagePartMap() private val packagePartMap = PackagePartMap()
public fun saveFileToCache(moduleId: String, sourceFiles: Collection<File>, classFile: File): Boolean { public fun saveFileToCache(moduleId: String, sourceFiles: Collection<File>, classFile: File): RecompilationDecision {
val fileBytes = classFile.readBytes() val fileBytes = classFile.readBytes()
val classNameAndHeader = VirtualFileKotlinClass.readClassNameAndHeader(fileBytes) val classNameAndHeader = VirtualFileKotlinClass.readClassNameAndHeader(fileBytes)
if (classNameAndHeader == null) return false if (classNameAndHeader == null) return RecompilationDecision.DO_NOTHING
val (className, header) = classNameAndHeader val (className, header) = classNameAndHeader
val annotationDataEncoded = header.annotationData val annotationDataEncoded = header.annotationData
@@ -67,10 +68,13 @@ public class IncrementalCacheImpl(val baseDir: File): IncrementalCache {
val data = BitEncoding.decodeBytes(annotationDataEncoded) val data = BitEncoding.decodeBytes(annotationDataEncoded)
when (header.kind) { when (header.kind) {
KotlinClassHeader.Kind.PACKAGE_FACADE -> { KotlinClassHeader.Kind.PACKAGE_FACADE -> {
return protoMap.put(moduleId, className, data) return if (protoMap.put(moduleId, className, data)) COMPILE_OTHERS else DO_NOTHING
} }
KotlinClassHeader.Kind.CLASS -> { KotlinClassHeader.Kind.CLASS -> {
return inlineFunctionsMap.process(moduleId, sourceFiles.first(), fileBytes) or protoMap.put(moduleId, className, data) val inlinesChanged = inlineFunctionsMap.process(moduleId, sourceFiles.first(), fileBytes)
val protoChanged = protoMap.put(moduleId, className, data)
return if (inlinesChanged) RECOMPILE_ALL else if (protoChanged) COMPILE_OTHERS else DO_NOTHING
} }
else -> { else -> {
throw IllegalStateException("Unexpected kind with annotationData: ${header.kind}") throw IllegalStateException("Unexpected kind with annotationData: ${header.kind}")
@@ -80,10 +84,12 @@ public class IncrementalCacheImpl(val baseDir: File): IncrementalCache {
if (header.syntheticClassKind == JvmAnnotationNames.KotlinSyntheticClass.Kind.PACKAGE_PART) { if (header.syntheticClassKind == JvmAnnotationNames.KotlinSyntheticClass.Kind.PACKAGE_PART) {
assert(sourceFiles.size == 1) { "Package part from several source files: $sourceFiles" } assert(sourceFiles.size == 1) { "Package part from several source files: $sourceFiles" }
packagePartMap.putPackagePartSourceData(moduleId, sourceFiles.first(), className) packagePartMap.putPackagePartSourceData(moduleId, sourceFiles.first(), className)
return inlineFunctionsMap.process(moduleId, sourceFiles.first(), fileBytes) or constantsMap.process(moduleId, sourceFiles.first(), fileBytes) val inlinesChanged = inlineFunctionsMap.process(moduleId, sourceFiles.first(), fileBytes)
val constantsChanged = constantsMap.process(moduleId, sourceFiles.first(), fileBytes)
return if (inlinesChanged) RECOMPILE_ALL else if (constantsChanged) COMPILE_OTHERS else DO_NOTHING
} }
return false return DO_NOTHING
} }
public fun clearCacheForRemovedFiles(moduleIdsAndFiles: Collection<Pair<String, File>>, outDirectories: Map<String, File>) { public fun clearCacheForRemovedFiles(moduleIdsAndFiles: Collection<Pair<String, File>>, outDirectories: Map<String, File>) {
@@ -286,7 +292,7 @@ public class IncrementalCacheImpl(val baseDir: File): IncrementalCache {
return moduleId + File.pathSeparator + sourceFile.getAbsolutePath() return moduleId + File.pathSeparator + sourceFile.getAbsolutePath()
} }
private fun getInlineFunctionsMap(bytes: ByteArray): Map<String, Long> { private fun getInlineFunctionsMap(bytes: ByteArray): Map<String, Long>? {
val result = HashMap<String, Long>() val result = HashMap<String, Long>()
ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) { ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) {
@@ -315,21 +321,23 @@ public class IncrementalCacheImpl(val baseDir: File): IncrementalCache {
}, 0) }, 0)
return result return if (result.isEmpty()) null else result
} }
public fun process(moduleId: String, file: File, bytes: ByteArray): Boolean { public fun process(moduleId: String, file: File, bytes: ByteArray): Boolean {
return put(moduleId, file, getInlineFunctionsMap(bytes)) return put(moduleId, file, getInlineFunctionsMap(bytes))
} }
private fun put(moduleId: String, file: File, inlineFunctionsMap: Map<String, Long>): Boolean { private fun put(moduleId: String, file: File, inlineFunctionsMap: Map<String, Long>?): Boolean {
val key = getKey(moduleId, file) val key = getKey(moduleId, file)
val oldMap = map[key] val oldMap = map[key]
if (oldMap == inlineFunctionsMap) { if (oldMap == inlineFunctionsMap) {
return false return false
} }
map.put(key, inlineFunctionsMap) if (inlineFunctionsMap != null) {
map.put(key, inlineFunctionsMap)
}
return true return true
} }
@@ -436,6 +444,16 @@ public class IncrementalCacheImpl(val baseDir: File): IncrementalCache {
map.close() map.close()
} }
} }
enum class RecompilationDecision {
DO_NOTHING
COMPILE_OTHERS
RECOMPILE_ALL
fun merge(other: RecompilationDecision): RecompilationDecision {
return if (other.ordinal() > this.ordinal()) other else this
}
}
} }
private object ByteArrayExternalizer: DataExternalizer<ByteArray> { private object ByteArrayExternalizer: DataExternalizer<ByteArray> {
@@ -105,7 +105,12 @@ public class IncrementalJpsTestGenerated extends AbstractIncrementalJpsTest {
public void testIndependentClasses() throws Exception { public void testIndependentClasses() throws Exception {
doTest("jps-plugin/testData/incremental/independentClasses/"); doTest("jps-plugin/testData/incremental/independentClasses/");
} }
@TestMetadata("inlineFunctionsCircularDependency")
public void testInlineFunctionsCircularDependency() throws Exception {
doTest("jps-plugin/testData/incremental/inlineFunctionsCircularDependency/");
}
@TestMetadata("multiplePackagesModified") @TestMetadata("multiplePackagesModified")
public void testMultiplePackagesModified() throws Exception { public void testMultiplePackagesModified() throws Exception {
doTest("jps-plugin/testData/incremental/multiplePackagesModified/"); doTest("jps-plugin/testData/incremental/multiplePackagesModified/");
@@ -5,9 +5,11 @@ Compiling files:
src/inline.kt src/inline.kt
End of files End of files
Cleaning output files: Cleaning output files:
out/production/module/inline/Klass.class
out/production/module/usage/UsagePackage-usage-*.class out/production/module/usage/UsagePackage-usage-*.class
out/production/module/usage/UsagePackage.class out/production/module/usage/UsagePackage.class
End of files End of files
Compiling files: Compiling files:
src/inline.kt
src/usage.kt src/usage.kt
End of files End of files
@@ -0,0 +1,10 @@
package test
inline fun a(body: () -> Unit) {
println("i'm inline function a")
body()
}
fun main(args: Array<String>) {
b { println("to be inlined") }
}
@@ -0,0 +1,10 @@
package test
inline fun a(body: () -> Unit) {
body()
println("i'm inline function a")
}
fun main(args: Array<String>) {
b { println("to be inlined") }
}
@@ -0,0 +1,7 @@
package test
inline fun b(body: () -> Unit) {
println("I'm inline function b")
body()
a { println("To be inlined from b") }
}
@@ -0,0 +1,16 @@
Cleaning output files:
out/production/module/test/TestPackage-a-*.class
out/production/module/test/TestPackage.class
End of files
Compiling files:
src/a.kt
End of files
Cleaning output files:
out/production/module/test/TestPackage-a-*.class
out/production/module/test/TestPackage-b-*.class
out/production/module/test/TestPackage.class
End of files
Compiling files:
src/a.kt
src/b.kt
End of files
@@ -6,9 +6,12 @@ Compiling files:
src/inline.kt src/inline.kt
End of files End of files
Cleaning output files: Cleaning output files:
out/production/module/inline/InlinePackage-inline-*.class
out/production/module/inline/InlinePackage.class
out/production/module/usage/UsagePackage-usage-*.class out/production/module/usage/UsagePackage-usage-*.class
out/production/module/usage/UsagePackage.class out/production/module/usage/UsagePackage.class
End of files End of files
Compiling files: Compiling files:
src/inline.kt
src/usage.kt src/usage.kt
End of files End of files