From 203a912f317a9555e81a13c836eb89e68e18986c Mon Sep 17 00:00:00 2001 From: Alexey Kudravtsev Date: Thu, 14 Jul 2011 12:08:02 +0400 Subject: [PATCH] SOE in stub builder (IDEA-25679 StackOverflowException when parsing DeepStringConcat.java) --- .../psi/impl/source/JavaFileStubBuilder.java | 7 +- .../com/intellij/psi/JavaStubBuilderTest.java | 9 +- .../psi/stubs/DefaultStubBuilder.java | 105 +++++++++++------- .../intellij/psi/stubs/LightStubBuilder.java | 6 +- .../stubs/elements/GrStubFileElementType.java | 3 +- 5 files changed, 78 insertions(+), 52 deletions(-) diff --git a/java/java-impl/src/com/intellij/psi/impl/source/JavaFileStubBuilder.java b/java/java-impl/src/com/intellij/psi/impl/source/JavaFileStubBuilder.java index 5e63fe652986..376e8823634b 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/JavaFileStubBuilder.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/JavaFileStubBuilder.java @@ -25,6 +25,7 @@ import com.intellij.psi.stubs.DefaultStubBuilder; import com.intellij.psi.stubs.StubElement; import com.intellij.psi.tree.IElementType; import com.intellij.util.io.StringRef; +import org.jetbrains.annotations.NotNull; /** * @author max @@ -34,7 +35,7 @@ public class JavaFileStubBuilder extends DefaultStubBuilder { private static final StubBuilder LIGHT_BUILDER = new JavaLightStubBuilder(); @Override - protected StubElement createStubForFile(final PsiFile file) { + protected StubElement createStubForFile(@NotNull final PsiFile file) { if (file instanceof PsiJavaFile) { final PsiJavaFile javaFile = (PsiJavaFile)file; return new PsiJavaFileStubImpl(javaFile, StringRef.fromString(javaFile.getPackageName()), false); @@ -44,7 +45,7 @@ public class JavaFileStubBuilder extends DefaultStubBuilder { } @Override - protected boolean skipChildProcessingWhenBuildingStubs(final PsiElement element, final PsiElement child) { + protected boolean skipChildProcessingWhenBuildingStubs(@NotNull final PsiElement element, @NotNull final PsiElement child) { final ASTNode node = element.getNode(); if (node == null) return false; final ASTNode childNode = child.getNode(); @@ -56,4 +57,4 @@ public class JavaFileStubBuilder extends DefaultStubBuilder { public boolean skipChildProcessingWhenBuildingStubs(final ASTNode parent, final IElementType childType) { return LIGHT_BUILDER.skipChildProcessingWhenBuildingStubs(parent, childType); } -} \ No newline at end of file +} diff --git a/java/java-tests/testSrc/com/intellij/psi/JavaStubBuilderTest.java b/java/java-tests/testSrc/com/intellij/psi/JavaStubBuilderTest.java index 45a08cbf7b46..222941f69cb9 100644 --- a/java/java-tests/testSrc/com/intellij/psi/JavaStubBuilderTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/JavaStubBuilderTest.java @@ -26,6 +26,7 @@ import com.intellij.psi.stubs.StubElement; import com.intellij.testFramework.LightIdeaTestCase; import com.intellij.testFramework.PlatformTestUtil; import com.intellij.util.ThrowableRunnable; +import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.Nullable; import java.io.File; @@ -36,7 +37,6 @@ public class JavaStubBuilderTest extends LightIdeaTestCase { @SuppressWarnings("deprecation") private static final StubBuilder OLD_BUILDER = new com.intellij.psi.impl.source.JavaFileStubBuilder(); private static final StubBuilder NEW_BUILDER = new JavaLightStubBuilder(); - private static final int SOE_TEST_DEPTH = 20000; @Override public void setUp() throws Exception { @@ -330,7 +330,8 @@ public class JavaStubBuilderTest extends LightIdeaTestCase { final StringBuilder sb = new StringBuilder(); final SecureRandom random = new SecureRandom(); sb.append("class SOE_test {\n BigInteger BIG = new BigInteger(\n"); - for (int i = 0; i < SOE_TEST_DEPTH; i++) { + int i; + for (i = 0; i < 100000; i++) { sb.append(" \"").append(Math.abs(random.nextInt())).append("\" +\n"); } sb.append(" \"\");\n}"); @@ -349,7 +350,7 @@ public class JavaStubBuilderTest extends LightIdeaTestCase { " FIELD:PsiFieldStub[BIG:BigInteger=;INITIALIZER_NOT_STORED;]\n" + " MODIFIER_LIST:PsiModifierListStub[mask=4096]\n", DebugUtil.stubTreeToString(tree)); - System.out.println("SOE depth=" + SOE_TEST_DEPTH + ", time=" + t + "ms"); + System.out.println("SOE depth=" + i + ", time=" + t + "ms"); } public void testPerformance() throws Exception { @@ -365,7 +366,7 @@ public class JavaStubBuilderTest extends LightIdeaTestCase { }).cpuBound().assertTiming(); } - private static void doTest(final String source, @Nullable final String tree) { + private static void doTest(@NonNls final String source, @NonNls @Nullable final String tree) { final PsiJavaFile file = (PsiJavaFile)createLightFile("test.java", source); final FileASTNode fileNode = file.getNode(); assertNotNull(fileNode); diff --git a/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java b/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java index c6b845a66729..d1861e553a51 100644 --- a/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java +++ b/platform/lang-api/src/com/intellij/psi/stubs/DefaultStubBuilder.java @@ -22,6 +22,7 @@ import com.intellij.psi.PsiFile; import com.intellij.psi.StubBasedPsiElement; import com.intellij.psi.StubBuilder; import com.intellij.psi.tree.IElementType; +import com.intellij.util.containers.Stack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -31,69 +32,91 @@ import org.jetbrains.annotations.Nullable; public class DefaultStubBuilder implements StubBuilder { private static final Logger LOG = Logger.getInstance("#com.intellij.psi.stubs.DefaultStubBuilder"); - public StubElement buildStubTree(final PsiFile file) { + public StubElement buildStubTree(@NotNull PsiFile file) { return buildStubTreeFor(file, createStubForFile(file)); } - protected StubElement createStubForFile(final PsiFile file) { + protected StubElement createStubForFile(@NotNull PsiFile file) { + //noinspection unchecked return new PsiFileStubImpl(file); } - private StubElement buildStubTreeFor(@NotNull PsiElement elt, StubElement parentStub) { - StubElement stub = parentStub; - if (elt instanceof StubBasedPsiElement) { - final IStubElementType type = ((StubBasedPsiElement)elt).getElementType(); + private StubElement buildStubTreeFor(@NotNull PsiElement root, @NotNull StubElement parentStub) { + Stack parentStubs = new Stack(); + Stack parentElements = new Stack(); + parentElements.push(root); + parentStubs.push(parentStub); - if (type.shouldCreateStub(elt.getNode())) { - //noinspection unchecked - stub = type.createStub(elt, parentStub); + while (!parentElements.isEmpty()) { + StubElement stub = parentStubs.pop(); + PsiElement elt = parentElements.pop(); + + if (elt instanceof StubBasedPsiElement) { + final IStubElementType type = ((StubBasedPsiElement)elt).getElementType(); + + if (type.shouldCreateStub(elt.getNode())) { + //noinspection unchecked + stub = type.createStub(elt, stub); + } } - } - else { - final ASTNode node = elt.getNode(); - final IElementType type = node == null? null : node.getElementType(); - if (type instanceof IStubElementType && ((IStubElementType)type).shouldCreateStub(node)) { - LOG.error("Non-StubBasedPsiElement requests stub creation. Stub type: " + type + ", PSI: " + elt); + else { + final ASTNode node = elt.getNode(); + final IElementType type = node == null? null : node.getElementType(); + if (type instanceof IStubElementType && ((IStubElementType)type).shouldCreateStub(node)) { + LOG.error("Non-StubBasedPsiElement requests stub creation. Stub type: " + type + ", PSI: " + elt); + } + } + + for (PsiElement child = elt.getLastChild(); child != null; child = child.getPrevSibling()) { + if (!skipChildProcessingWhenBuildingStubs(elt, child)) { + parentStubs.push(stub); + parentElements.push(child); + } } } - for (PsiElement child = elt.getFirstChild(); child != null; child = child.getNextSibling()) { - if (!skipChildProcessingWhenBuildingStubs(elt, child)) { - buildStubTreeFor(child, stub); - } - } - - return stub; + return parentStub; } - protected boolean skipChildProcessingWhenBuildingStubs(final PsiElement element, final PsiElement child) { + protected boolean skipChildProcessingWhenBuildingStubs(@NotNull PsiElement element, @NotNull PsiElement child) { return false; } - protected StubElement buildStubTreeFor(ASTNode node, StubElement parentStub) { - StubElement stub = parentStub; - IElementType nodeType = node.getElementType(); + protected StubElement buildStubTreeFor(@NotNull ASTNode root, @NotNull StubElement parentStub) { + Stack parentStubs = new Stack(); + Stack parentNodes = new Stack(); + parentNodes.push(root); + parentStubs.push(parentStub); - if (nodeType instanceof IStubElementType) { - final IStubElementType type = (IStubElementType)nodeType; + while (!parentStubs.isEmpty()) { + StubElement stub = parentStubs.pop(); + ASTNode node = parentNodes.pop(); + IElementType nodeType = node.getElementType(); - if (type.shouldCreateStub(node)) { - //noinspection unchecked - PsiElement element = node.getPsi(); - if (!(element instanceof StubBasedPsiElement)) { - LOG.error("Non-StubBasedPsiElement requests stub creation. Stub type: " + type + ", PSI: " + element); + if (nodeType instanceof IStubElementType) { + final IStubElementType type = (IStubElementType)nodeType; + + if (type.shouldCreateStub(node)) { + //noinspection unchecked + PsiElement element = node.getPsi(); + if (!(element instanceof StubBasedPsiElement)) { + LOG.error("Non-StubBasedPsiElement requests stub creation. Stub type: " + type + ", PSI: " + element); + } + //noinspection unchecked + stub = type.createStub(element, stub); + LOG.assertTrue(stub != null, element); + } + } + + for (ASTNode childNode = node.getLastChildNode(); childNode != null; childNode = childNode.getTreePrev()) { + if (!skipChildProcessingWhenBuildingStubs(node, childNode.getElementType())) { + parentNodes.push(childNode); + parentStubs.push(stub); } - stub = type.createStub(element, parentStub); } } - for (ASTNode childNode = node.getFirstChildNode(); childNode != null; childNode = childNode.getTreeNext()) { - if (!skipChildProcessingWhenBuildingStubs(node, childNode.getElementType())) { - buildStubTreeFor(childNode, stub); - } - } - - return stub; + return parentStub; } @Override diff --git a/platform/lang-api/src/com/intellij/psi/stubs/LightStubBuilder.java b/platform/lang-api/src/com/intellij/psi/stubs/LightStubBuilder.java index 6c20a61bec5f..8258cac02ced 100644 --- a/platform/lang-api/src/com/intellij/psi/stubs/LightStubBuilder.java +++ b/platform/lang-api/src/com/intellij/psi/stubs/LightStubBuilder.java @@ -96,7 +96,7 @@ public class LightStubBuilder implements StubBuilder { final StubElement stub = createStub(tree, element, parentStub); final List kids = tree.getChildren(element); - if (kids.size() > 0) { + if (!kids.isEmpty()) { if (parent != null) { parents.push(parent); childNumbers.push(childNumber); @@ -115,7 +115,7 @@ public class LightStubBuilder implements StubBuilder { } element = null; - while (parents.size() > 0) { + while (!parents.isEmpty()) { parent = parents.pop(); childNumber = childNumbers.pop(); children = kinderGarden.pop(); @@ -287,4 +287,4 @@ public class LightStubBuilder implements StubBuilder { } } } -} \ No newline at end of file +} diff --git a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java index 893daddfc483..ea0e80abeea8 100644 --- a/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java +++ b/plugins/groovy/src/org/jetbrains/plugins/groovy/lang/psi/stubs/elements/GrStubFileElementType.java @@ -21,6 +21,7 @@ import com.intellij.psi.StubBuilder; import com.intellij.psi.stubs.*; import com.intellij.psi.tree.IStubFileElementType; import com.intellij.util.io.StringRef; +import org.jetbrains.annotations.NotNull; import org.jetbrains.plugins.groovy.lang.psi.GroovyFile; import org.jetbrains.plugins.groovy.lang.psi.stubs.GrFileStub; import org.jetbrains.plugins.groovy.lang.psi.stubs.GrStubUtils; @@ -41,7 +42,7 @@ public class GrStubFileElementType extends IStubFileElementType { public StubBuilder getBuilder() { return new DefaultStubBuilder() { - protected StubElement createStubForFile(final PsiFile file) { + protected StubElement createStubForFile(@NotNull final PsiFile file) { if (file instanceof GroovyFile) { return new GrFileStub((GroovyFile)file); }