From c1e6961d5013c78cdf393093792df60c8d35031b Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Thu, 31 May 2012 16:26:26 +0400 Subject: [PATCH] PSI-based declaration providers --- .../AbstractPsiBasedDeclarationProvider.java | 97 +++++++++++++ .../FileBasedDeclarationProviderFactory.java | 118 +++++++++++++++ ...BasedPackageMemberDeclarationProvider.java | 59 ++++++++ ...siBasedClassMemberDeclarationProvider.java | 52 +++++++ .../resolve/lazy/BasicLazyResolveTest.java | 137 +----------------- 5 files changed, 334 insertions(+), 129 deletions(-) create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/AbstractPsiBasedDeclarationProvider.java create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/FileBasedDeclarationProviderFactory.java create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/FileBasedPackageMemberDeclarationProvider.java create mode 100644 compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/PsiBasedClassMemberDeclarationProvider.java diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/AbstractPsiBasedDeclarationProvider.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/AbstractPsiBasedDeclarationProvider.java new file mode 100644 index 00000000000..cfbb8511edc --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/AbstractPsiBasedDeclarationProvider.java @@ -0,0 +1,97 @@ +/* + * Copyright 2010-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. + */ + +package org.jetbrains.jet.lang.resolve.lazy; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetClassOrObject; +import org.jetbrains.jet.lang.psi.JetDeclaration; +import org.jetbrains.jet.lang.psi.JetNamedFunction; +import org.jetbrains.jet.lang.psi.JetProperty; +import org.jetbrains.jet.lang.resolve.name.Name; + +import java.util.List; +import java.util.Map; + +/** + * @author abreslav + */ +public abstract class AbstractPsiBasedDeclarationProvider implements DeclarationProvider { + private final List allDeclarations = Lists.newArrayList(); + private final Multimap functions = HashMultimap.create(); + private final Multimap properties = HashMultimap.create(); + private final Map classesAndObjects = Maps.newHashMap(); + + private boolean indexCreated = false; + + private void createIndex() { + if (indexCreated) return; + indexCreated = true; + + doCreateIndex(); + } + + protected abstract void doCreateIndex(); + + protected void putToIndex(JetDeclaration declaration) { + allDeclarations.add(declaration); + if (declaration instanceof JetNamedFunction) { + JetNamedFunction namedFunction = (JetNamedFunction) declaration; + functions.put(namedFunction.getNameAsName(), namedFunction); + } + else if (declaration instanceof JetProperty) { + JetProperty property = (JetProperty) declaration; + properties.put(property.getNameAsName(), property); + } + else if (declaration instanceof JetClassOrObject) { + JetClassOrObject classOrObject = (JetClassOrObject) declaration; + classesAndObjects.put(classOrObject.getNameAsName(), classOrObject); + } + else { + throw new IllegalArgumentException("Unknown declaration: " + declaration); + } + } + + @Override + public List getAllDeclarations() { + createIndex(); + return allDeclarations; + } + + @NotNull + @Override + public List getFunctionDeclarations(@NotNull Name name) { + createIndex(); + return Lists.newArrayList(functions.get(name)); + } + + @NotNull + @Override + public List getPropertyDeclarations(@NotNull Name name) { + createIndex(); + return Lists.newArrayList(properties.get(name)); + } + + @Override + public JetClassOrObject getClassOrObjectDeclaration(@NotNull Name name) { + createIndex(); + return classesAndObjects.get(name); + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/FileBasedDeclarationProviderFactory.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/FileBasedDeclarationProviderFactory.java new file mode 100644 index 00000000000..4bf807bc438 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/FileBasedDeclarationProviderFactory.java @@ -0,0 +1,118 @@ +/* + * Copyright 2010-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. + */ + +package org.jetbrains.jet.lang.resolve.lazy; + +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Sets; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetClassOrObject; +import org.jetbrains.jet.lang.psi.JetFile; +import org.jetbrains.jet.lang.psi.JetNamespaceHeader; +import org.jetbrains.jet.lang.resolve.name.FqName; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +/** + * @author abreslav + */ +public class FileBasedDeclarationProviderFactory implements DeclarationProviderFactory { + + private final Collection allFiles; + + private final Multimap filesByPackage = HashMultimap.create(); + private final Set definedPackages = Sets.newHashSet(); + private final Map packageDeclarationProviders = Maps.newHashMap(); + private final Map classMemberDeclarationProviders = Maps.newHashMap(); + + private boolean indexed = false; + + public FileBasedDeclarationProviderFactory(@NotNull Collection files) { + this.allFiles = files; + } + + private void createIndex() { + if (indexed) return; + indexed = true; + + for (JetFile file : allFiles) { + JetNamespaceHeader header = file.getNamespaceHeader(); + if (header == null) { + throw new IllegalArgumentException("Scripts are not supported"); + } + + FqName packageFqName = new FqName(header.getQualifiedName()); + addMeAndParentPackages(packageFqName); + filesByPackage.put(packageFqName, file); + } + } + + private void addMeAndParentPackages(@NotNull FqName name) { + definedPackages.add(name); + if (!name.isRoot()) { + addMeAndParentPackages(name.parent()); + } + } + + /*package*/ boolean isPackageDeclared(@NotNull FqName packageFqName) { + createIndex(); + return definedPackages.contains(packageFqName); + } + + @Override + public DeclarationProvider getPackageMemberDeclarationProvider(@NotNull FqName packageFqName) { + createIndex(); + + DeclarationProvider declarationProvider = packageDeclarationProviders.get(packageFqName); + if (declarationProvider != null) { + return declarationProvider; + } + + if (!isPackageDeclared(packageFqName)) return null; + + FileBasedPackageMemberDeclarationProvider provider = + new FileBasedPackageMemberDeclarationProvider(packageFqName, this, filesByPackage.get(packageFqName)); + packageDeclarationProviders.put(packageFqName, provider); + + return provider; + } + + @NotNull + @Override + public ClassMemberDeclarationProvider getClassMemberDeclarationProvider(@NotNull JetClassOrObject jetClassOrObject) { + createIndex(); + + ClassMemberDeclarationProvider declarationProvider = classMemberDeclarationProviders.get(jetClassOrObject); + if (declarationProvider != null) { + return declarationProvider; + } + + JetFile file = (JetFile) jetClassOrObject.getContainingFile(); + + if (!filesByPackage.containsKey(new FqName(file.getNamespaceHeader().getQualifiedName()))) { + throw new IllegalStateException("This factory doesn't know about this class: " + jetClassOrObject); + } + + ClassMemberDeclarationProvider provider = new PsiBasedClassMemberDeclarationProvider(jetClassOrObject); + classMemberDeclarationProviders.put(jetClassOrObject, provider); + + return provider; + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/FileBasedPackageMemberDeclarationProvider.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/FileBasedPackageMemberDeclarationProvider.java new file mode 100644 index 00000000000..c06ff1cac10 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/FileBasedPackageMemberDeclarationProvider.java @@ -0,0 +1,59 @@ +/* + * Copyright 2010-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. + */ + +package org.jetbrains.jet.lang.resolve.lazy; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.resolve.name.FqName; +import org.jetbrains.jet.lang.resolve.name.Name; + +import java.util.Collection; + +/** + * @author abreslav + */ +public class FileBasedPackageMemberDeclarationProvider extends AbstractPsiBasedDeclarationProvider { + + private final FqName fqName; + private final FileBasedDeclarationProviderFactory factory; + private final Collection allFiles; + + + /*package*/ FileBasedPackageMemberDeclarationProvider( + @NotNull FqName fqName, + @NotNull FileBasedDeclarationProviderFactory factory, + @NotNull Collection allFiles + ) { + this.fqName = fqName; + this.factory = factory; + this.allFiles = allFiles; + } + + @Override + protected void doCreateIndex() { + for (JetFile file : allFiles) { + for (JetDeclaration declaration : file.getDeclarations()) { + putToIndex(declaration); + } + } + } + + @Override + public boolean isPackageDeclared(@NotNull Name name) { + return factory.isPackageDeclared(fqName.child(name)); + } +} diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/PsiBasedClassMemberDeclarationProvider.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/PsiBasedClassMemberDeclarationProvider.java new file mode 100644 index 00000000000..41f36331228 --- /dev/null +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/lazy/PsiBasedClassMemberDeclarationProvider.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010-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. + */ + +package org.jetbrains.jet.lang.resolve.lazy; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.psi.JetClassOrObject; +import org.jetbrains.jet.lang.psi.JetDeclaration; +import org.jetbrains.jet.lang.resolve.name.Name; + +/** + * @author abreslav + */ +public class PsiBasedClassMemberDeclarationProvider extends AbstractPsiBasedDeclarationProvider implements ClassMemberDeclarationProvider { + + private final JetClassOrObject classOrObject; + + public PsiBasedClassMemberDeclarationProvider(@NotNull JetClassOrObject classOrObject) { + this.classOrObject = classOrObject; + } + + @NotNull + @Override + public JetClassOrObject getOwnerClassOrObject() { + return classOrObject; + } + + @Override + protected void doCreateIndex() { + for (JetDeclaration declaration : classOrObject.getDeclarations()) { + putToIndex(declaration); + } + } + + @Override + public boolean isPackageDeclared(@NotNull Name name) { + throw new UnsupportedOperationException("Shouldn't be called on a ClassMemberDeclarationProvider"); + } +} diff --git a/compiler/tests/org/jetbrains/jet/lang/resolve/lazy/BasicLazyResolveTest.java b/compiler/tests/org/jetbrains/jet/lang/resolve/lazy/BasicLazyResolveTest.java index aaef0dbdbe0..921bb38116b 100644 --- a/compiler/tests/org/jetbrains/jet/lang/resolve/lazy/BasicLazyResolveTest.java +++ b/compiler/tests/org/jetbrains/jet/lang/resolve/lazy/BasicLazyResolveTest.java @@ -14,19 +14,15 @@ package org.jetbrains.jet.lang.resolve.lazy;/* * limitations under the License. */ -import com.google.common.base.Predicate; -import com.google.common.collect.Collections2; import com.google.common.collect.Lists; import com.intellij.openapi.Disposable; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Disposer; -import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.CompileCompilerDependenciesTest; import org.jetbrains.jet.cli.jvm.compiler.JetCoreEnvironment; import org.jetbrains.jet.lang.descriptors.*; -import org.jetbrains.jet.lang.psi.*; +import org.jetbrains.jet.lang.psi.JetPsiFactory; import org.jetbrains.jet.lang.resolve.java.CompilerSpecialMode; -import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.types.TypeConstructor; import org.jetbrains.jet.lang.types.lang.JetStandardClasses; @@ -34,7 +30,6 @@ import org.junit.After; import org.junit.Test; import java.util.Arrays; -import java.util.Collections; import java.util.List; import static org.junit.Assert.*; @@ -65,130 +60,14 @@ public class BasicLazyResolveTest { ModuleDescriptor root = new ModuleDescriptor(Name.special("")); Project project = jetCoreEnvironment.getProject(); - final JetClass jetClass = JetPsiFactory.createClass(project, "package p; class C {fun f() {}}"); - final JetClass genericJetClass = JetPsiFactory.createClass(project, "package p; open class G {open fun f(): T {} fun a() {}}"); - final JetClass genericJetClass2 = JetPsiFactory.createClass(project, "package p; class G2 : G { fun g() : E {} override fun f() : T {}}"); - final JetNamedFunction fooFunction1 = JetPsiFactory.createFunction(project, "package p; fun foo() {}"); - final JetNamedFunction fooFunction2 = JetPsiFactory.createFunction(project, "package p; fun foo(a: C) {}"); - ResolveSession session = new ResolveSession(project, root, new DeclarationProviderFactory() { - @Override - public DeclarationProvider getPackageMemberDeclarationProvider(@NotNull FqName packageFqName) { - if (packageFqName.equals(FqName.ROOT)) { - return new DeclarationProvider() { - @Override - public List getAllDeclarations() { - return Collections.emptyList(); - } - - @NotNull - @Override - public List getFunctionDeclarations(@NotNull Name name) { - return Collections.emptyList(); - } - - @NotNull - @Override - public List getPropertyDeclarations(@NotNull Name name) { - return Collections.emptyList(); - } - - @Override - public JetClassOrObject getClassOrObjectDeclaration(@NotNull Name name) { - return null; - } - - @Override - public boolean isPackageDeclared(@NotNull Name name) { - return name.equals(Name.identifier("p")); - } - }; - } - if (!packageFqName.equals(FqName.topLevel(Name.identifier("p")))) return null; - return new DeclarationProvider() { - @Override - public List getAllDeclarations() { - return Arrays.asList(jetClass, genericJetClass, genericJetClass2, fooFunction1, fooFunction2); - } - - @NotNull - @Override - public List getFunctionDeclarations(@NotNull Name name) { - if (!FOO.equals(name)) return Collections.emptyList(); - return Arrays.asList(fooFunction1, fooFunction2); - } - - @NotNull - @Override - public List getPropertyDeclarations(@NotNull Name name) { - return Collections.emptyList(); - } - - @Override - public JetClassOrObject getClassOrObjectDeclaration(@NotNull Name name) { - if (name.equals(Name.identifier("C"))) return jetClass; - if (name.equals(Name.identifier("G"))) return genericJetClass; - if (name.equals(Name.identifier("G2"))) return genericJetClass2; - return null; - } - - @Override - public boolean isPackageDeclared(@NotNull Name name) { - return false; - } - }; - } - - @NotNull - @Override - public ClassMemberDeclarationProvider getClassMemberDeclarationProvider(@NotNull JetClassOrObject jetClassOrObject) { - final JetClass jetClass = (JetClass) jetClassOrObject; - return new ClassMemberDeclarationProvider() { - @NotNull - @Override - public JetClassOrObject getOwnerClassOrObject() { - return jetClass; - } - - @Override - public List getAllDeclarations() { - return jetClass.getDeclarations(); - } - - private List filter(List list, final Class t, final Name name) { - //noinspection unchecked - return (List) Lists.newArrayList(Collections2.filter(list, new Predicate() { - @Override - public boolean apply(D d) { - return t.isInstance(d) && ((JetNamed) d).getNameAsName().equals(name); - } - })); - } - - @NotNull - @Override - public List getFunctionDeclarations(@NotNull final Name name) { - return filter(jetClass.getDeclarations(), JetNamedFunction.class, name); - } - - @NotNull - @Override - public List getPropertyDeclarations(@NotNull Name name) { - return filter(jetClass.getDeclarations(), JetProperty.class, name); - } - - @Override - public JetClassOrObject getClassOrObjectDeclaration(@NotNull Name name) { - return null; - } - - @Override - public boolean isPackageDeclared(@NotNull Name name) { - return false; - } - }; - } - }); + ResolveSession session = new ResolveSession(project, root, new FileBasedDeclarationProviderFactory(Arrays.asList( + JetPsiFactory.createFile(project, "package p; class C {fun f() {}}"), + JetPsiFactory.createFile(project, "package p; open class G {open fun f(): T {} fun a() {}}"), + JetPsiFactory.createFile(project, "package p; class G2 : G { fun g() : E {} override fun f() : T {}}"), + JetPsiFactory.createFile(project, "package p; fun foo() {}"), + JetPsiFactory.createFile(project, "package p; fun foo(a: C) {}") + ))); NamespaceDescriptor packageDescriptor = session.getPackageDescriptor(Name.identifier("p")); assertNotNull(packageDescriptor);