mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
optimization: do not (re)compute the whole file text on each PSI change - will lead to quadratic nightmare in case of many small changes (part of IJPL-199461 Recursive elements processing causes SOE)
GitOrigin-RevId: a3560c64746733811da5717070cecd0e9e531928
This commit is contained in:
committed by
intellij-monorepo-bot
parent
59e42acd46
commit
f0c151a9e3
@@ -27,7 +27,9 @@ import com.intellij.psi.impl.java.stubs.PsiJavaFileStub;
|
||||
import com.intellij.psi.impl.source.resolve.ClassResolverProcessor;
|
||||
import com.intellij.psi.impl.source.resolve.SymbolCollectingProcessor;
|
||||
import com.intellij.psi.impl.source.resolve.SymbolCollectingProcessor.ResultWithContext;
|
||||
import com.intellij.psi.impl.source.tree.FileElement;
|
||||
import com.intellij.psi.impl.source.tree.JavaElementType;
|
||||
import com.intellij.psi.impl.source.tree.TreeUtil;
|
||||
import com.intellij.psi.scope.*;
|
||||
import com.intellij.psi.scope.processor.MethodsProcessor;
|
||||
import com.intellij.psi.stubs.StubElement;
|
||||
@@ -618,31 +620,36 @@ public abstract class PsiJavaFileBaseImpl extends PsiFileImpl implements PsiJava
|
||||
if (virtualFile == null) virtualFile = getViewProvider().getVirtualFile();
|
||||
|
||||
String sourceLevel = null;
|
||||
try {
|
||||
CharSequence contents = getViewProvider().getContents();
|
||||
int lineBound = Strings.indexOf(contents, "\n");
|
||||
CharSequence line = lineBound > 0 ? contents.subSequence(0, lineBound) : contents;
|
||||
if (Strings.startsWith(line, 0,"#!")) {
|
||||
List<String> params = ParametersListUtil.parse(line.toString());
|
||||
int srcIdx = params.indexOf("--source");
|
||||
if (srcIdx > 0 && srcIdx + 1 < params.size()) {
|
||||
sourceLevel = params.get(srcIdx + 1);
|
||||
LanguageLevel sheBangLevel = LanguageLevel.parse(sourceLevel);
|
||||
if (sheBangLevel != null) {
|
||||
return sheBangLevel;
|
||||
FileElement treeElement = getTreeElement();
|
||||
ASTNode firstLeaf = treeElement == null ? null : TreeUtil.findFirstLeaf(treeElement, false);
|
||||
// optimization: do not (re)compute the whole file text on each PSI change - will lead to quadratic nightmare in case of many small changes otherwise
|
||||
if (firstLeaf == null || Strings.startsWith(firstLeaf.getChars(), 0, "#!")) {
|
||||
try {
|
||||
CharSequence contents = getViewProvider().getContents();
|
||||
int lineBound = Strings.indexOf(contents, "\n");
|
||||
CharSequence line = lineBound > 0 ? contents.subSequence(0, lineBound) : contents;
|
||||
if (Strings.startsWith(line, 0, "#!")) {
|
||||
List<String> params = ParametersListUtil.parse(line.toString());
|
||||
int srcIdx = params.indexOf("--source");
|
||||
if (srcIdx > 0 && srcIdx + 1 < params.size()) {
|
||||
sourceLevel = params.get(srcIdx + 1);
|
||||
LanguageLevel sheBangLevel = LanguageLevel.parse(sourceLevel);
|
||||
if (sheBangLevel != null) {
|
||||
return sheBangLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable ignored) {
|
||||
}
|
||||
finally {
|
||||
if (!Objects.equals(sourceLevel, virtualFile.getUserData(SHEBANG_SOURCE_LEVEL)) && virtualFile.isInLocalFileSystem()) {
|
||||
virtualFile.putUserData(SHEBANG_SOURCE_LEVEL, sourceLevel);
|
||||
VirtualFile file = virtualFile;
|
||||
ApplicationManager.getApplication().invokeLater(() -> FileContentUtilCore.reparseFiles(file),
|
||||
ModalityState.nonModal(),
|
||||
ApplicationManager.getApplication().getDisposed());
|
||||
catch (Throwable ignored) {
|
||||
}
|
||||
finally {
|
||||
if (!Objects.equals(sourceLevel, virtualFile.getUserData(SHEBANG_SOURCE_LEVEL)) && virtualFile.isInLocalFileSystem()) {
|
||||
virtualFile.putUserData(SHEBANG_SOURCE_LEVEL, sourceLevel);
|
||||
VirtualFile file = virtualFile;
|
||||
ApplicationManager.getApplication().invokeLater(() -> FileContentUtilCore.reparseFiles(file),
|
||||
ModalityState.nonModal(),
|
||||
ApplicationManager.getApplication().getDisposed());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.psi.impl;
|
||||
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiElementFactory;
|
||||
import com.intellij.psi.PsiField;
|
||||
import com.intellij.testFramework.LightJavaCodeInsightTestCase;
|
||||
|
||||
public class PsiModificationStressTest extends LightJavaCodeInsightTestCase {
|
||||
public void testManySmallPSIChangesDoNotCauseQuadraticRecomputationsOfWholeFileText() {
|
||||
int N = 100_000;
|
||||
String text = " int field;\n".repeat(N);
|
||||
|
||||
PsiClass aClass = PsiElementFactory.getInstance(getProject()).createClassFromText(text, null);
|
||||
assertFalse(aClass.isPhysical());
|
||||
PsiField firstField = aClass.getFields()[0];
|
||||
// would pass only if each PSI change does not cause recomputation of the whole file text in com.intellij.psi.impl.source.PsiJavaFileBaseImpl.getLanguageLevelInner
|
||||
for (int i = 0; i < N; i++) {
|
||||
firstField.setName("f" + i);
|
||||
}
|
||||
assertFalse(aClass.isPhysical());
|
||||
assertEquals(N, aClass.getFields().length);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user