mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
Java: reduce call stack usage in the parser for extremely large else-if chains (IDEA-305898)
GitOrigin-RevId: 9d7be3efbe108e604fbd67ce03c7fdfe23c3fb3a
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8966e5a5b5
commit
3e7bc7ef47
@@ -18,6 +18,8 @@ import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import static com.intellij.lang.PsiBuilderUtil.*;
|
||||
import static com.intellij.lang.java.parser.BasicJavaParserUtil.*;
|
||||
import static com.intellij.psi.impl.source.BasicElementTypes.BASIC_JAVA_COMMENT_OR_WHITESPACE_BIT_SET;
|
||||
@@ -305,23 +307,37 @@ public class BasicStatementParser {
|
||||
|
||||
@NotNull
|
||||
private PsiBuilder.Marker parseIfStatement(PsiBuilder builder) {
|
||||
PsiBuilder.Marker statement = builder.mark();
|
||||
builder.advanceLexer();
|
||||
ArrayList<PsiBuilder.Marker> stack = null;
|
||||
PsiBuilder.Marker statement;
|
||||
while (true) {
|
||||
// replaced recursion with iteration plus stack to avoid huge call stack for extremely large else-if chains
|
||||
statement = builder.mark();
|
||||
builder.advanceLexer();
|
||||
|
||||
if (parseExprInParenth(builder)) {
|
||||
PsiBuilder.Marker thenStatement = parseStatement(builder);
|
||||
if (thenStatement == null) {
|
||||
error(builder, JavaPsiBundle.message("expected.statement"));
|
||||
}
|
||||
else if (expect(builder, JavaTokenType.ELSE_KEYWORD)) {
|
||||
PsiBuilder.Marker elseStatement = parseStatement(builder);
|
||||
if (elseStatement == null) {
|
||||
if (parseExprInParenth(builder)) {
|
||||
if (parseStatement(builder) == null) {
|
||||
error(builder, JavaPsiBundle.message("expected.statement"));
|
||||
}
|
||||
else if (expect(builder, JavaTokenType.ELSE_KEYWORD)) {
|
||||
if (builder.getTokenType() == JavaTokenType.IF_KEYWORD) {
|
||||
if (stack == null) stack = new ArrayList<>();
|
||||
stack.add(statement);
|
||||
continue;
|
||||
}
|
||||
else if (parseStatement(builder) == null) {
|
||||
error(builder, JavaPsiBundle.message("expected.statement"));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
done(statement, myJavaElementTypeContainer.IF_STATEMENT, myWhiteSpaceAndCommentSetHolder);
|
||||
if (stack != null) {
|
||||
for (int i = stack.size() - 1; i >= 0; i--) {
|
||||
statement = stack.get(i);
|
||||
done(statement, myJavaElementTypeContainer.IF_STATEMENT, myWhiteSpaceAndCommentSetHolder);
|
||||
}
|
||||
}
|
||||
|
||||
done(statement, myJavaElementTypeContainer.IF_STATEMENT, myWhiteSpaceAndCommentSetHolder);
|
||||
return statement;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +1,44 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.parser.statementParsing;
|
||||
|
||||
import com.intellij.java.parser.AbstractBasicJavaParsingTestCase;
|
||||
import com.intellij.java.parser.AbstractBasicJavaParsingTestConfigurator;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public abstract class AbstractBasicIfParsingTest extends AbstractBasicJavaParsingTestCase {
|
||||
public AbstractBasicIfParsingTest(@NotNull AbstractBasicJavaParsingTestConfigurator configurator) {
|
||||
super("parser-full/statementParsing/if", configurator);
|
||||
}
|
||||
|
||||
public void testNormalWithElse() { doTest(true); }
|
||||
|
||||
public void testNormalNoElse() { doTest(true); }
|
||||
|
||||
public void testUncomplete1() { doTest(true); }
|
||||
|
||||
public void testUncomplete2() { doTest(true); }
|
||||
|
||||
public void testUncomplete3() { doTest(true); }
|
||||
|
||||
public void testUncomplete4() { doTest(true); }
|
||||
|
||||
public void testUncomplete5() { doTest(true); }
|
||||
|
||||
public void testUncomplete6() { doTest(true); }
|
||||
|
||||
public void testUncomplete7() { doTest(true); }
|
||||
|
||||
public void testBigIf() throws IOException {
|
||||
String name = getTestName();
|
||||
PsiFile file = createPsiFile(name, loadFile(name + "." + myFileExt));
|
||||
var visitor = new PsiRecursiveElementWalkingVisitor() {
|
||||
int count = 0;
|
||||
@Override
|
||||
public void visitElement(@NotNull PsiElement element) {
|
||||
// visit all elements to make sure the file is parsed, because createPsiFile() is lazy
|
||||
super.visitElement(element);
|
||||
count++;
|
||||
}
|
||||
};
|
||||
file.accept(visitor);
|
||||
// psi tree is too big and too deeply nested to fit a debug string into memory
|
||||
assertEquals(46946, visitor.count);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user