mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-10-02 00:57:59 +07:00
don't allow concurrent access to thread-unsafe lighter AST in indices (EA-90268 - NPE: JavaNullMethodArgumentIndex$.getNullParameterIndices)
This commit is contained in:
@@ -14,21 +14,27 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.index
|
||||
|
||||
import com.intellij.lang.FCTSBackedLighterAST
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.ReadAction
|
||||
import com.intellij.openapi.command.WriteCommandAction
|
||||
import com.intellij.psi.JavaPsiFacade
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiJavaFile
|
||||
import com.intellij.psi.impl.PsiDocumentManagerBase
|
||||
import com.intellij.psi.impl.search.JavaNullMethodArgumentUtil
|
||||
import com.intellij.psi.impl.source.PsiFileImpl
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.testFramework.BombedProgressIndicator
|
||||
import com.intellij.testFramework.PlatformTestUtil
|
||||
import com.intellij.testFramework.SkipSlowTestLocally
|
||||
import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase
|
||||
import com.intellij.util.GCUtil
|
||||
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.Future
|
||||
/**
|
||||
* @author peter
|
||||
*/
|
||||
@@ -170,4 +176,36 @@ class ConcurrentIndexTest extends JavaCodeInsightFixtureTestCase {
|
||||
for(future in futuresToWait) future.get()
|
||||
}
|
||||
}
|
||||
|
||||
void "test concurrent light AST access during uncommitted document indexing"() {
|
||||
def clazz = myFixture.addClass('class Bar { void foo(Object o) {}}')
|
||||
|
||||
def text = " foo(null);";
|
||||
for (i in 0..20) {
|
||||
text = "new Runnable() { void run() {\n " + text + "\n}}.run();"
|
||||
}
|
||||
text = "class Foo {{ " + text * 200 + "}}"
|
||||
|
||||
def file = myFixture.addFileToProject('a.java', text)
|
||||
def document = file.viewProvider.document
|
||||
for (i in 1..5) {
|
||||
WriteCommandAction.runWriteCommandAction project, {
|
||||
document.insertString(document.text.indexOf('null') + 1, ' ')
|
||||
document.insertString(document.text.indexOf('(null') + 1, ' ')
|
||||
PsiDocumentManager.getInstance(project).commitAllDocuments()
|
||||
}
|
||||
GCUtil.tryGcSoftlyReachableObjects()
|
||||
|
||||
assert file.node.lighterAST instanceof FCTSBackedLighterAST
|
||||
List<Future> futures = []
|
||||
futures << ApplicationManager.application.executeOnPooledThread { ReadAction.run {
|
||||
assert !JavaNullMethodArgumentUtil.hasNullArgument(clazz.methods[0], 0)
|
||||
} }
|
||||
futures << ApplicationManager.application.executeOnPooledThread { ReadAction.run {
|
||||
assert JavaPsiFacade.getInstance(project).findClass('Foo', GlobalSearchScope.allScope(project))
|
||||
} }
|
||||
futures.each { it.get() }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.util.indexing;
|
||||
|
||||
import com.intellij.lang.FileASTNode;
|
||||
import com.intellij.lang.Language;
|
||||
import com.intellij.lang.LighterAST;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
@@ -85,12 +86,26 @@ public class FileContentImpl extends UserDataHolderBase implements FileContent {
|
||||
LighterAST lighterAST = getUserData(IndexingDataKeys.LIGHTER_AST_NODE_KEY);
|
||||
if (lighterAST == null) {
|
||||
lighterAST = getPsiFileForPsiDependentIndex().getNode().getLighterAST();
|
||||
assert lighterAST != null;
|
||||
putUserData(IndexingDataKeys.LIGHTER_AST_NODE_KEY, lighterAST);
|
||||
}
|
||||
return lighterAST;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expand the AST to ensure {@link com.intellij.lang.FCTSBackedLighterAST} won't be used, because it's not thread-safe,
|
||||
* but unsaved documents may be indexed in many concurrent threads
|
||||
*/
|
||||
void ensureThreadSafeLighterAST() {
|
||||
ensureParsed(getPsiFile().getNode());
|
||||
ensureParsed(getPsiFileForPsiDependentIndex().getNode());
|
||||
}
|
||||
|
||||
private static void ensureParsed(@Nullable FileASTNode node) {
|
||||
if (node != null) {
|
||||
node.getFirstChildNode();
|
||||
}
|
||||
}
|
||||
|
||||
public PsiFile createFileFromText(@NotNull CharSequence text) {
|
||||
Project project = getProject();
|
||||
if (project == null) {
|
||||
@@ -232,6 +247,7 @@ public class FileContentImpl extends UserDataHolderBase implements FileContent {
|
||||
myHash = hash;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public PsiFile getPsiFileForPsiDependentIndex() {
|
||||
Document document = FileDocumentManager.getInstance().getCachedDocument(getFile());
|
||||
PsiFile psi = null;
|
||||
|
||||
@@ -1346,6 +1346,7 @@ public class FileBasedIndexImpl extends FileBasedIndex {
|
||||
}
|
||||
|
||||
initFileContent(newFc, project, dominantContentFile);
|
||||
newFc.ensureThreadSafeLighterAST();
|
||||
|
||||
if (content instanceof AuthenticContent) {
|
||||
newFc.putUserData(PlatformIdTableBuilding.EDITOR_HIGHLIGHTER,
|
||||
|
||||
Reference in New Issue
Block a user