don't allow concurrent access to thread-unsafe lighter AST in indices (EA-90268 - NPE: JavaNullMethodArgumentIndex$.getNullParameterIndices)

This commit is contained in:
peter
2016-11-02 16:50:35 +01:00
parent 8c54e84341
commit a91dfb6feb
3 changed files with 56 additions and 1 deletions
@@ -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,