From 9b5731595ebe6dec3cf458f78d6de25b4cd5b181 Mon Sep 17 00:00:00 2001 From: Maxim Shafirov Date: Tue, 5 Jun 2012 18:14:02 +0400 Subject: [PATCH] KT-2145 Impossible to depend on Scala library --- .../impl/compiled/ClassFileStubBuilder.java | 14 +- .../DefaultClsStubBuilderFactory.java | 46 ++---- .../OutOfOrderInnerClassException.java | 28 ++++ .../impl/compiled/StubBuildingVisitor.java | 14 +- .../compiled/ClassFileStubBuilderTest.java | 146 ------------------ 5 files changed, 61 insertions(+), 187 deletions(-) create mode 100644 java/java-psi-impl/src/com/intellij/psi/impl/compiled/OutOfOrderInnerClassException.java delete mode 100644 java/java-tests/testSrc/com/intellij/psi/impl/compiled/ClassFileStubBuilderTest.java diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClassFileStubBuilder.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClassFileStubBuilder.java index 61d645b7f931..6bca28ed01ca 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClassFileStubBuilder.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClassFileStubBuilder.java @@ -16,10 +16,11 @@ package com.intellij.psi.impl.compiled; import com.intellij.openapi.extensions.Extensions; -import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.project.Project; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.impl.source.JavaFileElementType; import com.intellij.psi.stubs.BinaryFileStubBuilder; +import com.intellij.psi.stubs.PsiFileStub; import com.intellij.psi.stubs.StubElement; import com.intellij.util.cls.ClsFormatException; @@ -29,11 +30,7 @@ import com.intellij.util.cls.ClsFormatException; public class ClassFileStubBuilder implements BinaryFileStubBuilder { @Override public boolean acceptsFile(final VirtualFile file) { - final ClsStubBuilderFactory[] factories = Extensions.getExtensions(ClsStubBuilderFactory.EP_NAME); - for (ClsStubBuilderFactory factory : factories) { - if (!factory.isInnerClass(file)) return true; - } - return false; + return true; } @Override @@ -42,7 +39,8 @@ public class ClassFileStubBuilder implements BinaryFileStubBuilder { final ClsStubBuilderFactory[] factories = Extensions.getExtensions(ClsStubBuilderFactory.EP_NAME); for (ClsStubBuilderFactory factory : factories) { if (!factory.isInnerClass(file) && factory.canBeProcessed(file, content)) { - return factory.buildFileStub(file, content); + PsiFileStub stub = factory.buildFileStub(file, content); + if (stub != null) return stub; } } return null; @@ -56,4 +54,4 @@ public class ClassFileStubBuilder implements BinaryFileStubBuilder { public int getStubVersion() { return JavaFileElementType.STUB_VERSION + 3; } -} \ No newline at end of file +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/DefaultClsStubBuilderFactory.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/DefaultClsStubBuilderFactory.java index d2e031a64e9c..a0a8e956e968 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/DefaultClsStubBuilderFactory.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/DefaultClsStubBuilderFactory.java @@ -40,9 +40,15 @@ public class DefaultClsStubBuilderFactory extends ClsStubBuilderFactory { final PsiJavaFileStubImpl file = new PsiJavaFileStubImpl("do.not.know.yet", true); try { final ClassReader reader = new ClassReader(bytes); + final StubBuildingVisitor classVisitor = new StubBuildingVisitor(vFile, VirtualFileInnerClassStrategy.INSTANCE, file, 0); - reader.accept(classVisitor, ClassReader.SKIP_FRAMES); + try { + reader.accept(classVisitor, ClassReader.SKIP_FRAMES); + } + catch (OutOfOrderInnerClassException e) { + return null; + } @SuppressWarnings("unchecked") final PsiClassStub result = (PsiClassStub)classVisitor.getResult(); if (result == null) return null; @@ -63,40 +69,16 @@ public class DefaultClsStubBuilderFactory extends ClsStubBuilderFactory { @Override public boolean isInnerClass(VirtualFile file) { - return isInner(file.getNameWithoutExtension(), new ParentDirectory(file)); - } + String name = file.getNameWithoutExtension(); + int len = name.length(); + int idx = name.indexOf('$'); - static boolean isInner(final String name, final Directory directory) { - return isInner(name, 0, directory); - } - - private static boolean isInner(final String name, final int from, final Directory directory) { - final int index = name.indexOf('$', from); - return index != -1 && (containsPart(directory, name, index) || isInner(name, index + 1, directory)); - } - - private static boolean containsPart(Directory directory, String name, int endIndex) { - return endIndex > 0 && directory.contains(name.substring(0, endIndex)); - } - - interface Directory { - boolean contains(String name); - } - - private static class ParentDirectory implements Directory { - private final VirtualFile myDirectory; - private final String myExtension; - - private ParentDirectory(final VirtualFile file) { - myDirectory = file.getParent(); - myExtension = file.getExtension(); + while (idx > 0) { + if (idx + 1 < len && Character.isDigit(name.charAt(idx + 1))) return true; + idx = name.indexOf('$', idx + 1); } - @Override - public boolean contains(final String name) { - final String fullName = myExtension == null ? name : name + "." + myExtension; - return myDirectory != null && myDirectory.findChild(fullName) != null; - } + return false; } private static class VirtualFileInnerClassStrategy implements InnerClassSourceStrategy { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/OutOfOrderInnerClassException.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/OutOfOrderInnerClassException.java new file mode 100644 index 000000000000..3a144bfed2d4 --- /dev/null +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/OutOfOrderInnerClassException.java @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2012 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* + * @author max + */ +package com.intellij.psi.impl.compiled; + +public class OutOfOrderInnerClassException extends RuntimeException { + + @Override + public Throwable fillInStackTrace() { + return this; + } +} diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/StubBuildingVisitor.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/StubBuildingVisitor.java index 5b220ffaff83..217e2cab1f8c 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/StubBuildingVisitor.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/StubBuildingVisitor.java @@ -275,6 +275,9 @@ public class StubBuildingVisitor extends ClassVisitor { @Override public void visitOuterClass(final String owner, final String name, final String desc) { + if (myParent instanceof PsiFileStub) { + throw new OutOfOrderInnerClassException(); + } } @Override @@ -296,7 +299,16 @@ public class StubBuildingVisitor extends ClassVisitor { if ((access & Opcodes.ACC_SYNTHETIC) != 0) return; if (!isCorrectName(innerName)) return; - if (innerName == null || outerName == null || !getClassName(outerName).equals(myResult.getQualifiedName())) return; + if (innerName == null || outerName == null) return; + if ((getClassName(outerName) + "." + innerName).equals(myResult.getQualifiedName())) { + // Our result is inner class + + if (myParent instanceof PsiFileStub) { + throw new OutOfOrderInnerClassException(); + } + } + + if (!getClassName(outerName).equals(myResult.getQualifiedName())) return; final T innerSource = myInnersStrategy.findInnerClass(innerName, mySource); if (innerSource == null) return; diff --git a/java/java-tests/testSrc/com/intellij/psi/impl/compiled/ClassFileStubBuilderTest.java b/java/java-tests/testSrc/com/intellij/psi/impl/compiled/ClassFileStubBuilderTest.java deleted file mode 100644 index 71e7d950fcc6..000000000000 --- a/java/java-tests/testSrc/com/intellij/psi/impl/compiled/ClassFileStubBuilderTest.java +++ /dev/null @@ -1,146 +0,0 @@ -package com.intellij.psi.impl.compiled; - -import com.intellij.util.containers.ContainerUtil; -import junit.framework.TestCase; - -import java.util.HashSet; -import java.util.Set; - -/** - * Pavel.Fatin, 18.02.2010 - */ -public class ClassFileStubBuilderTest extends TestCase { - public void testOneFile() { - assertTopLevel(""); - assertTopLevel("$"); - assertTopLevel("$$"); - assertTopLevel("$$$"); - assertTopLevel("Foo"); - assertTopLevel("Foo$"); - assertTopLevel("$Foo"); - assertTopLevel("$Foo$"); - assertTopLevel("Foo$Bar"); - assertTopLevel("$Foo$Bar"); - assertTopLevel("Foo$Bar$"); - assertTopLevel("$Foo$Bar$"); - assertTopLevel("$$Foo$$Bar$$"); - } - - public void testWrongCase() { - assertTopLevel("Foo$Bar", "foo"); - assertTopLevel("foo$Bar", "Foo"); - } - - public void testShortName() { - assertTopLevel("A"); - - assertInner("Foo$B", "Foo"); - assertInner("A$Foo", "A"); - assertInner("A$B", "A"); - - assertTopLevel("A$B", "A$"); - assertTopLevel("A$B", "$B"); - assertTopLevel("A$B", "B"); - assertTopLevel("A$B", "$"); - } - - public void testShortNames() { - assertInner("A$B$C", "A"); - assertInner("A$B$C", "A$B"); - assertInner("A$B$C", "A", "A$B"); - - assertTopLevel("A$B$C", "A$"); - assertTopLevel("A$B$C", "A$B$"); - } - - public void testEmptyFileName() { - assertTopLevel("$Foo", ""); - } - - public void testNoInnerName() { - //assertTopLevel("Foo$", "Foo"); - //assertTopLevel("Foo$$", "Foo$"); - // an exception to the rule (to hide Scala "objects") - assertInner("Foo$", "Foo"); - assertInner("Foo$$", "Foo$"); - } - - public void testDollarsInName() { - assertInner("$Foo$Bar", "$Foo"); - assertInner("Foo$$Bar", "Foo"); - assertInner("Foo$$Bar", "Foo"); - assertInner("$Foo$$Bar", "$Foo"); - assertInner("$Foo$$Bar", "$Foo$"); - } - - public void testInner() { - assertInner("Foo$Bar", "Foo"); - assertInner("Foo$bar", "Foo"); - assertInner("foo$Bar", "foo"); - assertInner("foo$bar", "foo"); - } - - public void testInnerAndNoise() { - assertInner("Foo$Bar", "Foo", "Foo$", "$Bar", "Bar"); - } - - public void testTopLevel() { - assertTopLevel("Foo$Bar"); - assertTopLevel("Foo$Bar", "Foo$"); - assertTopLevel("Foo$Bar", "$Bar"); - assertTopLevel("Foo$Bar", "Bar"); - assertTopLevel("Foo$Bar", "Foo$", "$Bar", "Bar"); - } - - public void testInners() { - assertInner("Foo$Bar$Moo", "Foo"); - assertInner("Foo$Bar$Moo", "Foo$Bar"); - assertInner("Foo$Bar$Moo", "Foo", "Foo$Bar"); - } - - public void testInnersAndNoise() { - assertInner("Foo$Bar$Moo", "Foo", "$Bar", "Bar", "$Moo", "Moo", "Bar$Moo", "$Bar$Moo"); - assertInner("Foo$Bar$Moo", "Foo$Bar", "$Bar", "Bar", "$Moo", "Moo", "Bar$Moo", "$Bar$Moo"); - assertInner("Foo$Bar$Moo", "Foo", "Foo$Bar", "$Bar", "Bar", "$Moo", "Moo", "Bar$Moo", "$Bar$Moo"); - } - - public void testTopLevels() { - assertTopLevel("Foo$Bar$Moo"); - assertTopLevel("Foo$Bar$Moo", "$Bar"); - assertTopLevel("Foo$Bar$Moo", "Bar"); - assertTopLevel("Foo$Bar$Moo", "$Moo"); - assertTopLevel("Foo$Bar$Moo", "Moo"); - assertTopLevel("Foo$Bar$Moo", "Bar$Moo"); - assertTopLevel("Foo$Bar$Moo", "$Bar$Moo"); - assertTopLevel("Foo$Bar$Moo", "$Bar", "Bar", "$Moo", "Moo", "Bar$Moo", "$Bar$Moo"); - } - - private static void assertInner(String name, String... files) { - assertTrue("Class " + name + " must be identified as inner", isInner(name, files)); - } - - private static void assertTopLevel(String name, String... files) { - assertFalse("Class " + name + " must be identified as top-level", isInner(name, files)); - } - - private static boolean isInner(String name, String... files) { - Set all = new HashSet(); - ContainerUtil.addAll(all, files); - all.add(name); - return DefaultClsStubBuilderFactory.isInner(name, new DirectoryMock(all)); - } - - - private static class DirectoryMock implements DefaultClsStubBuilderFactory.Directory { - private Set myFiles; - - private DirectoryMock(Set files) { - myFiles = files; - } - - @Override - public boolean contains(String name) { - return myFiles.contains(name); - } - } -}