diff --git a/jps/jps-plugin/jps-plugin.iml b/jps/jps-plugin/jps-plugin.iml
index a7f222b8c6d..5ccd205933f 100644
--- a/jps/jps-plugin/jps-plugin.iml
+++ b/jps/jps-plugin/jps-plugin.iml
@@ -17,8 +17,8 @@
-
-
+
+
diff --git a/jps/jps-plugin/src/META-INF/services/org.jetbrains.jet.lang.resolve.kotlin.incremental.IncrementalCacheProvider b/jps/jps-plugin/src/META-INF/services/org.jetbrains.jet.lang.resolve.kotlin.incremental.IncrementalCacheProvider
new file mode 100644
index 00000000000..eadf409aee9
--- /dev/null
+++ b/jps/jps-plugin/src/META-INF/services/org.jetbrains.jet.lang.resolve.kotlin.incremental.IncrementalCacheProvider
@@ -0,0 +1 @@
+org.jetbrains.jet.jps.incremental.IncrementalCacheProviderImpl
\ No newline at end of file
diff --git a/jps/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java b/jps/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java
index 2091b635e95..da5842fa0f1 100644
--- a/jps/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java
+++ b/jps/jps-plugin/src/org/jetbrains/jet/jps/build/KotlinBuilder.java
@@ -37,7 +37,7 @@ import org.jetbrains.jet.compiler.runner.OutputItemsCollectorImpl;
import org.jetbrains.jet.compiler.runner.SimpleOutputItem;
import org.jetbrains.jet.config.IncrementalCompilation;
import org.jetbrains.jet.jps.JpsKotlinCompilerSettings;
-import org.jetbrains.jet.lang.resolve.kotlin.incremental.IncrementalCache;
+import org.jetbrains.jet.jps.incremental.IncrementalCacheImpl;
import org.jetbrains.jet.utils.PathUtil;
import org.jetbrains.jps.ModuleChunk;
import org.jetbrains.jps.builders.BuildTarget;
@@ -209,7 +209,7 @@ public class KotlinBuilder extends ModuleLevelBuilder {
}
}
- IncrementalCache cache = new IncrementalCache(KotlinBuilderModuleScriptGenerator.getIncrementalCacheDir(context));
+ IncrementalCacheImpl cache = new IncrementalCacheImpl(KotlinBuilderModuleScriptGenerator.getIncrementalCacheDir(context));
try {
for (ModuleBuildTarget target : chunk.getTargets()) {
diff --git a/jps/jps-plugin/src/org/jetbrains/jet/jps/incremental/IncrementalCacheImpl.kt b/jps/jps-plugin/src/org/jetbrains/jet/jps/incremental/IncrementalCacheImpl.kt
new file mode 100644
index 00000000000..083e5b46c6c
--- /dev/null
+++ b/jps/jps-plugin/src/org/jetbrains/jet/jps/incremental/IncrementalCacheImpl.kt
@@ -0,0 +1,219 @@
+/*
+ * Copyright 2010-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.jet.jps.incremental
+
+import java.io.File
+import com.intellij.util.io.PersistentMap
+import com.intellij.util.io.PersistentHashMap
+import com.intellij.util.io.KeyDescriptor
+import java.io.DataOutput
+import com.intellij.util.io.IOUtil
+import java.io.DataInput
+import org.jetbrains.jet.lang.resolve.name.FqName
+import com.intellij.util.io.DataExternalizer
+import org.jetbrains.jet.lang.resolve.kotlin.VirtualFileKotlinClass
+import org.jetbrains.jet.lang.resolve.kotlin.header.KotlinClassHeader
+import org.jetbrains.jet.descriptors.serialization.BitEncoding
+import org.jetbrains.jet.utils.intellij.*
+import java.util.Arrays
+import com.intellij.util.io.IntInlineKeyDescriptor
+import org.jetbrains.org.objectweb.asm.*
+import com.intellij.util.io.EnumeratorStringDescriptor
+import org.jetbrains.jet.lang.resolve.java.JvmAnnotationNames
+import org.jetbrains.jet.lang.resolve.java.JvmClassName
+import java.util.TreeMap
+import java.util.HashSet
+import org.jetbrains.jet.lang.resolve.kotlin.incremental.IncrementalCache
+
+public class IncrementalCacheImpl(baseDir: File): IncrementalCache {
+ class object {
+ val PROTO_MAP = "proto.tab"
+ val CONSTANTS_MAP = "constants.tab"
+ val PACKAGE_SOURCES = "package-sources.tab"
+
+ private fun getConstantsHash(bytes: ByteArray): Int {
+ val result = TreeMap() // keys order should defined to check hash of a map
+
+ ClassReader(bytes).accept(object : ClassVisitor(Opcodes.ASM5) {
+ override fun visitField(access: Int, name: String, desc: String, signature: String?, value: Any?): FieldVisitor? {
+ if (value != null) {
+ result[name] = value
+ }
+ return null
+ }
+ }, ClassReader.SKIP_CODE or ClassReader.SKIP_DEBUG or ClassReader.SKIP_FRAMES)
+
+ return result.hashCode()
+ }
+ }
+
+ private val protoData: PersistentMap
+ private val constantsData: PersistentHashMap
+ = PersistentHashMap(File(baseDir, CONSTANTS_MAP), EnumeratorStringDescriptor(), IntInlineKeyDescriptor())
+
+ // Format of serialization to string: -->
+ private val packageSourcesData: PersistentHashMap
+ = PersistentHashMap(File(baseDir, PACKAGE_SOURCES), EnumeratorStringDescriptor(), EnumeratorStringDescriptor())
+
+ ;{
+ protoData = PersistentHashMap(File(baseDir, PROTO_MAP), object : KeyDescriptor {
+ override fun save(out: DataOutput, value: ClassOrPackageId?) {
+ IOUtil.writeString(value!!.moduleId, out)
+ IOUtil.writeString(value.fqName.asString(), out)
+ }
+
+ override fun read(`in`: DataInput): ClassOrPackageId {
+ val module = IOUtil.readString(`in`)!!
+ val fqName = FqName(IOUtil.readString(`in`)!!)
+ return ClassOrPackageId(module, fqName)
+ }
+
+ override fun getHashCode(value: ClassOrPackageId?): Int {
+ return value?.hashCode() ?: -1
+ }
+
+ override fun isEqual(val1: ClassOrPackageId?, val2: ClassOrPackageId?): Boolean {
+ return val1 == val2
+ }
+ }, object : DataExternalizer {
+ override fun save(out: DataOutput, value: ByteArray?) {
+ out.writeInt(value!!.size)
+ out.write(value)
+ }
+
+ override fun read(`in`: DataInput): ByteArray {
+ val length = `in`.readInt()
+ val buf = ByteArray(length)
+ `in`.readFully(buf)
+ return buf
+ }
+ })
+ }
+
+ public fun saveFileToCache(moduleId: String, sourceFiles: Collection, file: File): Boolean {
+ val fileBytes = file.readBytes()
+ val classNameAndHeader = VirtualFileKotlinClass.readClassNameAndHeader(fileBytes)
+ if (classNameAndHeader == null) return false
+
+ val (className, header) = classNameAndHeader
+ val classFqName = className.getFqNameForClassNameWithoutDollars()
+ val annotationDataEncoded = header.annotationData
+ if (annotationDataEncoded != null) {
+ val data = BitEncoding.decodeBytes(annotationDataEncoded)
+ when (header.kind) {
+ KotlinClassHeader.Kind.PACKAGE_FACADE -> {
+ return putData(moduleId, classFqName.parent(), data)
+ }
+ KotlinClassHeader.Kind.CLASS -> {
+ return putData(moduleId, classFqName, data)
+ }
+ }
+ }
+ if (header.syntheticClassKind == JvmAnnotationNames.KotlinSyntheticClass.Kind.PACKAGE_PART) {
+ assert(sourceFiles.size == 1) { "Package part from several source files: $sourceFiles" }
+ putPackagePartSourceData(moduleId, sourceFiles.first(), className)
+ return putConstantsData(className, getConstantsHash(fileBytes))
+ }
+
+ return false
+ }
+
+ private fun putData(moduleId: String, fqName: FqName, data: ByteArray): Boolean {
+ val id = ClassOrPackageId(moduleId, fqName)
+ val oldData = protoData[id]
+ if (Arrays.equals(data, oldData)) {
+ return false
+ }
+ protoData.put(id, data)
+ return true
+ }
+
+ private fun putConstantsData(packagePartClass: JvmClassName, constantsHash: Int): Boolean {
+ val key = packagePartClass.getInternalName()
+
+ val oldHash = constantsData[key]
+ if (oldHash == constantsHash) {
+ return false
+ }
+ constantsData.put(key, constantsHash)
+ return true
+ }
+
+ private fun putPackagePartSourceData(moduleId: String, sourceFile: File, className: JvmClassName?) {
+ val key = moduleId + File.pathSeparator + sourceFile.getAbsolutePath()
+
+ if (className != null) {
+ packageSourcesData.put(key, className.getInternalName())
+ }
+ else {
+ packageSourcesData.remove(key)
+ }
+ }
+
+ public fun clearPackagePartSourceData(moduleId: String, sourceFile: File) {
+ putPackagePartSourceData(moduleId, sourceFile, null)
+ }
+
+ public override fun getPackagesWithRemovedFiles(moduleId: String, compiledSourceFiles: Collection): Collection {
+ return getRemovedPackageParts(moduleId, compiledSourceFiles).map { it.getFqNameForClassNameWithoutDollars().parent() }
+ }
+
+ public override fun getRemovedPackageParts(moduleId: String, compiledSourceFiles: Collection): Collection {
+ val sourceFileToCurrentFqNameMap = HashMap()
+ for (sourceFile in compiledSourceFiles) {
+ sourceFileToCurrentFqNameMap[File(sourceFile.getVirtualFile()!!.getPath())] = sourceFile.getPackageFqName()
+ }
+
+ val result = HashSet()
+
+ packageSourcesData.processKeysWithExistingMapping { key ->
+ if (key!!.startsWith(moduleId + File.pathSeparator)) {
+ val indexOf = key.indexOf(File.pathSeparator)
+ val sourceFile = File(key.substring(indexOf + 1))
+
+ val packagePartClassName = JvmClassName.byInternalName(packageSourcesData[key]!!)
+ if (!sourceFile.exists()) {
+ result.add(packagePartClassName)
+ }
+ else {
+ val previousPackageFqName = packagePartClassName.getFqNameForClassNameWithoutDollars().parent()
+ val currentPackageFqName = sourceFileToCurrentFqNameMap[sourceFile]
+ if (currentPackageFqName != null && currentPackageFqName != previousPackageFqName) {
+ result.add(packagePartClassName)
+ }
+ }
+ }
+
+ true
+ }
+
+ return result
+ }
+
+ public override fun getPackageData(moduleId: String, fqName: FqName): ByteArray? {
+ return protoData[ClassOrPackageId(moduleId, fqName)]
+ }
+
+ public fun close() {
+ protoData.close()
+ constantsData.close()
+ packageSourcesData.close()
+ }
+
+ private data class ClassOrPackageId(val moduleId: String, val fqName: FqName) {
+ }
+}
\ No newline at end of file
diff --git a/jps/jps-plugin/src/org/jetbrains/jet/jps/incremental/IncrementlaCacheProviderImpl.kt b/jps/jps-plugin/src/org/jetbrains/jet/jps/incremental/IncrementlaCacheProviderImpl.kt
new file mode 100644
index 00000000000..5b94b207b71
--- /dev/null
+++ b/jps/jps-plugin/src/org/jetbrains/jet/jps/incremental/IncrementlaCacheProviderImpl.kt
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2010-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.jet.jps.incremental
+
+import org.jetbrains.jet.lang.resolve.kotlin.incremental.IncrementalCacheProvider
+import java.io.File
+import org.jetbrains.jet.lang.resolve.kotlin.incremental.IncrementalCache
+
+public class IncrementalCacheProviderImpl: IncrementalCacheProvider {
+ override fun getIncrementalCache(baseDir: File): IncrementalCache {
+ return IncrementalCacheImpl(baseDir)
+ }
+}
\ No newline at end of file