diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt index 5f9d754f51e..d0548aeeaa3 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt @@ -539,7 +539,7 @@ class KotlinCoreEnvironment private constructor( ourApplicationEnvironment = null Disposer.dispose(environment.parentDisposable) ZipHandler.clearFileAccessorCache() - FastJarHandler.cleanFileAccessorsCache() + (environment.jarFileSystem as? FastJarFileSystem)?.clearHandlersCache() } } diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/jarfs/FastJarFileSystem.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/jarfs/FastJarFileSystem.kt index 7036e7a0a38..a295b5315bc 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/jarfs/FastJarFileSystem.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/jarfs/FastJarFileSystem.kt @@ -9,11 +9,39 @@ import com.intellij.openapi.vfs.DeprecatedVirtualFileSystem import com.intellij.openapi.vfs.StandardFileSystems import com.intellij.openapi.vfs.VirtualFile import com.intellij.util.containers.ConcurrentFactoryMap +import com.intellij.util.io.FileAccessorCache +import java.io.File +import java.io.IOException +import java.io.RandomAccessFile +import java.nio.ByteBuffer +import java.nio.MappedByteBuffer +import java.nio.channels.FileChannel + +private typealias RandomAccessFileAndBuffer = Pair class FastJarFileSystem : DeprecatedVirtualFileSystem() { private val myHandlers: MutableMap = ConcurrentFactoryMap.createMap { key: String -> FastJarHandler(this@FastJarFileSystem, key) } + internal val cachedOpenFileHandles: FileAccessorCache = + object : FileAccessorCache(20, 10) { + @Throws(IOException::class) + override fun createAccessor(file: File): RandomAccessFileAndBuffer { + val randomAccessFile = RandomAccessFile(file, "r") + return Pair(randomAccessFile, randomAccessFile.channel.map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length())) + } + + @Throws(IOException::class) + override fun disposeAccessor(fileAccessor: RandomAccessFileAndBuffer) { + fileAccessor.first.close() + fileAccessor.second.unmapBuffer() + } + + override fun isEqual(val1: File, val2: File): Boolean { + return val1 == val2 // reference equality to handle different jars for different ZipHandlers on the same path + } + } + override fun getProtocol(): String { return StandardFileSystems.JAR_PROTOCOL } @@ -30,6 +58,7 @@ class FastJarFileSystem : DeprecatedVirtualFileSystem() { fun clearHandlersCache() { myHandlers.clear() + cachedOpenFileHandles.clear() } companion object { @@ -42,3 +71,42 @@ class FastJarFileSystem : DeprecatedVirtualFileSystem() { } } } + + +private val IS_PRIOR_9_JRE = System.getProperty("java.specification.version", "").startsWith("1.") + +internal fun ByteBuffer.unmapBuffer() { + if (!isDirect) return + + try { + if (IS_PRIOR_9_JRE) { + val cleaner = this::class.java.getMethod("cleaner") + + cleaner.isAccessible = true + + val clean = Class.forName("sun.misc.Cleaner").getMethod("clean") + clean.isAccessible = true + + clean.invoke(cleaner.invoke(this)) + } else { + val unsafeClass = try { + Class.forName("sun.misc.Unsafe") + } catch (ex: Exception) { + // jdk.internal.misc.Unsafe doesn't yet have an invokeCleaner() method, + // but that method should be added if sun.misc.Unsafe is removed. + Class.forName("jdk.internal.misc.Unsafe") + } + + val clean = unsafeClass.getMethod("invokeCleaner", ByteBuffer::class.java) + clean.isAccessible = true + + val theUnsafeField = unsafeClass.getDeclaredField("theUnsafe") + theUnsafeField.isAccessible = true + + val theUnsafe = theUnsafeField.get(null) + + clean.invoke(theUnsafe, this) + } + } catch (ex: Exception) { + } +} diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/jarfs/FastJarHandler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/jarfs/FastJarHandler.kt index df183eda624..4ddb16ea9e5 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/jarfs/FastJarHandler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/jarfs/FastJarHandler.kt @@ -143,77 +143,14 @@ class FastJarHandler(val fileSystem: FastJarFileSystem, path: String) : ZipHandl override fun contentsToByteArray(relativePath: String): ByteArray { if (relativePath == MANIFEST_PATH) return cachedManifest ?: throw FileNotFoundException("$file!/$relativePath") val zipEntryDescription = ourEntryMap[relativePath] ?: throw FileNotFoundException("$file!/$relativePath") - return cachedOpenFileHandles[file].use { + return fileSystem.cachedOpenFileHandles[file].use { synchronized(it) { it.get().second.contentsToByteArray(zipEntryDescription) } } } - - companion object { - fun cleanFileAccessorsCache() { - cachedOpenFileHandles.clear() - } - } } private const val MANIFEST_PATH = "META-INF/MANIFEST.MF" -typealias RandomAccessFileAndBuffer = Pair -private val cachedOpenFileHandles: FileAccessorCache = - object : FileAccessorCache(20, 10) { - @Throws(IOException::class) - override fun createAccessor(file: File): RandomAccessFileAndBuffer { - val randomAccessFile = RandomAccessFile(file, "r") - return Pair(randomAccessFile, randomAccessFile.channel.map(FileChannel.MapMode.READ_ONLY, 0, randomAccessFile.length())) - } - - @Throws(IOException::class) - override fun disposeAccessor(fileAccessor: RandomAccessFileAndBuffer) { - fileAccessor.first.close() - fileAccessor.second.unmapBuffer() - } - - override fun isEqual(val1: File, val2: File): Boolean { - return val1 == val2 // reference equality to handle different jars for different ZipHandlers on the same path - } - } - -private val IS_PRIOR_9_JRE = System.getProperty("java.specification.version", "").startsWith("1.") - -private fun ByteBuffer.unmapBuffer() { - if (!isDirect) return - - try { - if (IS_PRIOR_9_JRE) { - val cleaner = this::class.java.getMethod("cleaner") - - cleaner.isAccessible = true - - val clean = Class.forName("sun.misc.Cleaner").getMethod("clean") - clean.isAccessible = true - - clean.invoke(cleaner.invoke(this)) - } else { - val unsafeClass = try { - Class.forName("sun.misc.Unsafe") - } catch (ex: Exception) { - // jdk.internal.misc.Unsafe doesn't yet have an invokeCleaner() method, - // but that method should be added if sun.misc.Unsafe is removed. - Class.forName("jdk.internal.misc.Unsafe") - } - - val clean = unsafeClass.getMethod("invokeCleaner", ByteBuffer::class.java) - clean.isAccessible = true - - val theUnsafeField = unsafeClass.getDeclaredField("theUnsafe") - theUnsafeField.isAccessible = true - - val theUnsafe = theUnsafeField.get(null) - - clean.invoke(theUnsafe, this) - } - } catch (ex: Exception) { - } -} diff --git a/compiler/daemon/src/org/jetbrains/kotlin/daemon/CompileServiceImpl.kt b/compiler/daemon/src/org/jetbrains/kotlin/daemon/CompileServiceImpl.kt index 640389f3fa9..10de14ebef6 100644 --- a/compiler/daemon/src/org/jetbrains/kotlin/daemon/CompileServiceImpl.kt +++ b/compiler/daemon/src/org/jetbrains/kotlin/daemon/CompileServiceImpl.kt @@ -35,6 +35,7 @@ import org.jetbrains.kotlin.cli.common.repl.ReplEvalResult import org.jetbrains.kotlin.cli.js.K2JSCompiler import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.cli.jvm.compiler.jarfs.FastJarFileSystem import org.jetbrains.kotlin.cli.jvm.compiler.jarfs.FastJarHandler import org.jetbrains.kotlin.cli.metadata.K2MetadataCompiler import org.jetbrains.kotlin.config.Services @@ -1202,8 +1203,8 @@ class CompileServiceImpl( override fun clearJarCache() { ZipHandler.clearFileAccessorCache() - FastJarHandler.cleanFileAccessorsCache() (KotlinCoreEnvironment.applicationEnvironment?.jarFileSystem as? CoreJarFileSystem)?.clearHandlersCache() + (KotlinCoreEnvironment.applicationEnvironment?.jarFileSystem as? FastJarFileSystem)?.clearHandlersCache() } private inline fun ifAlive( diff --git a/compiler/daemon/src/org/jetbrains/kotlin/daemon/experimental/CompileServiceServerSideImpl.kt b/compiler/daemon/src/org/jetbrains/kotlin/daemon/experimental/CompileServiceServerSideImpl.kt index 14fba4800a4..9a048703269 100644 --- a/compiler/daemon/src/org/jetbrains/kotlin/daemon/experimental/CompileServiceServerSideImpl.kt +++ b/compiler/daemon/src/org/jetbrains/kotlin/daemon/experimental/CompileServiceServerSideImpl.kt @@ -23,7 +23,7 @@ import org.jetbrains.kotlin.cli.common.repl.ReplCompileResult import org.jetbrains.kotlin.cli.js.K2JSCompiler import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.cli.jvm.compiler.jarfs.FastJarHandler +import org.jetbrains.kotlin.cli.jvm.compiler.jarfs.FastJarFileSystem import org.jetbrains.kotlin.cli.metadata.K2MetadataCompiler import org.jetbrains.kotlin.config.Services import org.jetbrains.kotlin.daemon.CompileServiceImplBase @@ -669,8 +669,8 @@ class CompileServiceServerSideImpl( override suspend fun clearJarCache() { ZipHandler.clearFileAccessorCache() - FastJarHandler.cleanFileAccessorsCache() (KotlinCoreEnvironment.applicationEnvironment?.jarFileSystem as? CoreJarFileSystem)?.clearHandlersCache() + (KotlinCoreEnvironment.applicationEnvironment?.jarFileSystem as? FastJarFileSystem)?.clearHandlersCache() } private suspend fun ifAlive(