VFS: cache file type only if under read action, clear cache in write action, cleanup

to avoid races possibly leading to IDEA-245099 File type of unloaded plugin can be leaked due to file type caching

GitOrigin-RevId: 3e746a89a0e57cc2c49fcb02951e00b214a281e5
This commit is contained in:
Peter Gromov
2020-07-07 12:24:42 +00:00
committed by intellij-monorepo-bot
parent 478e1d913d
commit 8d62fcfecb
6 changed files with 14 additions and 15 deletions
@@ -712,7 +712,6 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent
}
private void unregisterFileTypeWithoutNotification(@NotNull FileType fileType) {
CachedFileType.remove(fileType);
myPatternsTable.removeAllAssociations(fileType);
myInitialAssociations.removeAllAssociations(fileType);
mySchemeManager.removeScheme(fileType);
@@ -809,6 +808,7 @@ public class FileTypeManagerImpl extends FileTypeManagerEx implements Persistent
private void fireFileTypesChanged(@Nullable FileType addedFileType, @Nullable FileType removedFileType) {
myDetectionService.clearCaches();
CachedFileType.clearCache();
ApplicationManager.getApplication().getMessageBus().syncPublisher(TOPIC).fileTypesChanged(new FileTypeEvent(this, addedFileType, removedFileType));
}
@@ -1,6 +1,7 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.vfs.newvfs.impl;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.fileTypes.FileType;
import com.intellij.psi.util.PsiModificationTracker;
import org.jetbrains.annotations.ApiStatus;
@@ -25,10 +26,12 @@ public final class CachedFileType {
}
static CachedFileType forType(@NotNull FileType fileType) {
ApplicationManager.getApplication().assertReadAccessAllowed();
return ourInterner.computeIfAbsent(fileType, CachedFileType::new);
}
public static void clearCache() {
ApplicationManager.getApplication().assertWriteAccessAllowed();
ourInterner.forEach((type, cachedType) -> {
// clear references to file types to aid plugin unloading
cachedType.fileType = null;
@@ -36,13 +39,6 @@ public final class CachedFileType {
ourInterner.clear();
}
public static void remove(@NotNull FileType type) {
CachedFileType cached = ourInterner.remove(type);
if (cached != null) {
cached.fileType = null;
}
}
static final class PsiListener implements PsiModificationTracker.Listener {
@Override
public void modificationCountChanged() {
@@ -485,7 +485,9 @@ public abstract class VirtualFileSystemEntry extends NewVirtualFile {
FileType type = cache == null ? null : cache.getUpToDateOrNull();
if (type == null) {
type = super.getFileType();
myFileType = CachedFileType.forType(type);
if (ApplicationManager.getApplication().isReadAccessAllowed()) {
myFileType = CachedFileType.forType(type);
}
}
return type;
}
@@ -1,8 +1,6 @@
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package com.intellij.openapi.vfs.newvfs.persistent;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import com.intellij.ide.IdeBundle;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.application.ApplicationNamesInfo;
@@ -20,7 +18,6 @@ import com.intellij.openapi.vfs.impl.local.LocalFileSystemImpl;
import com.intellij.openapi.vfs.newvfs.ChildInfoImpl;
import com.intellij.openapi.vfs.newvfs.FileAttribute;
import com.intellij.openapi.vfs.newvfs.events.ChildInfo;
import com.intellij.openapi.vfs.newvfs.impl.CachedFileType;
import com.intellij.openapi.vfs.newvfs.impl.FileNameCache;
import com.intellij.openapi.vfs.newvfs.impl.VirtualDirectoryImpl;
import com.intellij.openapi.vfs.newvfs.impl.VirtualFileSystemEntry;
@@ -1186,7 +1183,6 @@ public final class FSRecords {
private static void incModCount(int id) {
incLocalModCount();
CachedFileType.clearCache();
final int count = doGetModCount() + 1;
getRecords().putInt(HEADER_GLOBAL_MOD_COUNT_OFFSET, count);
@@ -1044,6 +1044,7 @@ public final class PersistentFSImpl extends PersistentFS implements Disposable {
}
private static void fireAfterEvents(@NotNull BulkFileListener publisher, @NotNull List<? extends VFileEvent> toSend) {
CachedFileType.clearCache();
((BulkFileListener)VirtualFilePointerManager.getInstance()).after(toSend);
publisher.after(toSend);
}
@@ -930,7 +930,7 @@ public class FileTypesTest extends HeavyPlatformTestCase {
bean.fileNames = ".prettierrc";
bean.implementationClass = MyTestFileType.class.getName();
Disposable disposable = registerFileType(bean);
CachedFileType.clearCache(); // normally this is done by PsiModificationTracker.Listener but it's not fired in this test
clearFileTypeCache();
assertEquals(MyTestFileType.NAME, FileTypeManager.getInstance().getFileTypeByFileName(".prettierrc").getName());
assertEquals(MyTestFileType.NAME, vFile.getFileType().getName());
@@ -939,6 +939,10 @@ public class FileTypesTest extends HeavyPlatformTestCase {
assertNull(FileTypeManager.getInstance().findFileTypeByName(MyTestFileType.NAME));
}
private static void clearFileTypeCache() {
WriteAction.run(() -> CachedFileType.clearCache()); // normally this is done by PsiModificationTracker.Listener but it's not fired in this test
}
public void testRegisterAdditionalExtensionForExistingFileType() throws IOException {
File tempFile = createTempFile(".prettierrc", "This is a text file");
VirtualFile vFile = getVirtualFile(tempFile);
@@ -948,7 +952,7 @@ public class FileTypesTest extends HeavyPlatformTestCase {
bean.name = "XML";
bean.fileNames = ".prettierrc";
Disposable disposable = registerFileType(bean);
CachedFileType.clearCache(); // normally this is done by PsiModificationTracker.Listener but it's not fired in this test
clearFileTypeCache();
assertEquals("XML", FileTypeManager.getInstance().getFileTypeByFileName(".prettierrc").getName());
assertEquals("XML", vFile.getFileType().getName());