diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/NamespaceCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/NamespaceCodegen.java index 7817692fa32..8c6c0fa013e 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/NamespaceCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/NamespaceCodegen.java @@ -34,6 +34,7 @@ import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils; import org.jetbrains.jet.lang.psi.*; import org.jetbrains.jet.lang.resolve.BindingContext; import org.jetbrains.jet.lang.resolve.java.JvmClassName; +import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames; import org.jetbrains.jet.lang.resolve.java.PackageClassUtils; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.Name; @@ -84,6 +85,10 @@ public class NamespaceCodegen extends MemberCodegen { public void generate(CompilationErrorHandler errorHandler) { boolean multiFile = CodegenBinding.isMultiFileNamespace(state.getBindingContext(), name); + if (shouldGenerateNSClass(files)) { + v.getClassBuilder().newAnnotation(JvmStdlibNames.JET_PACKAGE_CLASS.getDescriptor(), true); + } + for (JetFile file : files) { VirtualFile vFile = file.getVirtualFile(); try { diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/DescriptorResolverUtils.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/DescriptorResolverUtils.java index 8ebbbe2b096..cf191a33a0f 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/DescriptorResolverUtils.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/DescriptorResolverUtils.java @@ -46,7 +46,8 @@ public final class DescriptorResolverUtils { } public static boolean isKotlinClass(@NotNull PsiClass psiClass) { - return new PsiClassWrapper(psiClass).getJetClass().isDefined() || PackageClassUtils.isPackageClass(psiClass); + PsiClassWrapper wrapper = new PsiClassWrapper(psiClass); + return wrapper.getJetClass().isDefined() || wrapper.getJetPackageClass().isDefined(); } public static boolean isInnerEnum(@NotNull PsiClass innerClass, @Nullable DeclarationDescriptor owner) { diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JvmStdlibNames.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JvmStdlibNames.java index 69b6f759c47..5f3f6ccecfe 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JvmStdlibNames.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/JvmStdlibNames.java @@ -68,7 +68,9 @@ public class JvmStdlibNames { public static final JvmClassName JET_CLASS = JvmClassName.byFqNameWithoutInnerClasses("jet.runtime.typeinfo.JetClass"); - + + public static final JvmClassName JET_PACKAGE_CLASS = JvmClassName.byFqNameWithoutInnerClasses("jet.runtime.typeinfo.JetPackageClass"); + public static final String JET_CLASS_SIGNATURE = "signature"; diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/PackageClassUtils.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/PackageClassUtils.java index e309401fc78..b36656abe81 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/PackageClassUtils.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/PackageClassUtils.java @@ -19,6 +19,7 @@ package org.jetbrains.jet.lang.resolve.java; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiClass; import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.resolve.java.kt.JetPackageClassAnnotation; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.Name; @@ -38,21 +39,7 @@ public class PackageClassUtils { return packageFQN.child(Name.identifier(getPackageClassName(packageFQN))); } - public static boolean isPackageClass(@NotNull FqName fqName) { - if (fqName.isRoot()) { - return true; - } - return getPackageClassName(fqName.parent()).equals(fqName.shortName().getName()); - } - public static boolean isPackageClass(@NotNull PsiClass psiClass) { - String qualifiedName = psiClass.getQualifiedName(); - if (qualifiedName == null) { - return false; - } - if (DEFAULT_PACKAGE.equals(qualifiedName)) { - return true; - } - return getPackageClassName(new FqName(qualifiedName).parent()).equals(psiClass.getName()); + return JetPackageClassAnnotation.get(psiClass).isDefined(); } } diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kt/JetPackageClassAnnotation.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kt/JetPackageClassAnnotation.java new file mode 100644 index 00000000000..48845354bb8 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kt/JetPackageClassAnnotation.java @@ -0,0 +1,42 @@ +/* + * Copyright 2010-2013 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.java.kt; + +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiClass; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames; +import org.jetbrains.jet.lang.resolve.java.resolver.JavaAnnotationResolver; + +public class JetPackageClassAnnotation extends PsiAnnotationWithFlags { + private static final JetPackageClassAnnotation NULL_ANNOTATION = new JetPackageClassAnnotation(null); + static { + NULL_ANNOTATION.checkInitialized(); + } + + private JetPackageClassAnnotation(@Nullable PsiAnnotation psiAnnotation) { + super(psiAnnotation); + } + + @NotNull + public static JetPackageClassAnnotation get(PsiClass psiClass) { + final PsiAnnotation annotation = JavaAnnotationResolver.findOwnAnnotation(psiClass, + JvmStdlibNames.JET_PACKAGE_CLASS.getFqName().getFqName()); + return annotation != null ? new JetPackageClassAnnotation(annotation) : NULL_ANNOTATION; + } +} diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/wrapper/PsiClassWrapper.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/wrapper/PsiClassWrapper.java index e9f3bcce786..b639da28f30 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/wrapper/PsiClassWrapper.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/wrapper/PsiClassWrapper.java @@ -17,13 +17,9 @@ package org.jetbrains.jet.lang.resolve.java.wrapper; import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiField; -import com.intellij.psi.PsiMethod; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.resolve.java.kt.JetClassAnnotation; - -import java.util.ArrayList; -import java.util.List; +import org.jetbrains.jet.lang.resolve.java.kt.JetPackageClassAnnotation; public class PsiClassWrapper { @@ -44,6 +40,8 @@ public class PsiClassWrapper { } private JetClassAnnotation jetClass; + private JetPackageClassAnnotation jetPackageClass; + @NotNull public JetClassAnnotation getJetClass() { if (jetClass == null) { @@ -51,4 +49,12 @@ public class PsiClassWrapper { } return jetClass; } + + @NotNull + public JetPackageClassAnnotation getJetPackageClass() { + if (jetPackageClass == null) { + jetPackageClass = JetPackageClassAnnotation.get(psiClass); + } + return jetPackageClass; + } } diff --git a/compiler/tests/org/jetbrains/jet/PackageClassNameTest.java b/compiler/tests/org/jetbrains/jet/PackageClassNameTest.java index a5d003f02b7..cd12a36f0e2 100644 --- a/compiler/tests/org/jetbrains/jet/PackageClassNameTest.java +++ b/compiler/tests/org/jetbrains/jet/PackageClassNameTest.java @@ -57,36 +57,6 @@ public class PackageClassNameTest { doTest(FqName.ROOT.child(Name.identifier("kotlin")), "KotlinPackage", "_DefaultPackage"); } - @Test - public void testIsPackageClass1() { - doTestIsPackageClass("", true); - } - - @Test - public void testIsPackageClass2() { - doTestIsPackageClass("kotlin", false); - } - - @Test - public void testIsPackageClass3() { - doTestIsPackageClass("kotlin.KotlinPackage", true); - } - - @Test - public void testIsPackageClass4() { - doTestIsPackageClass("kotlin.test.SomeClass", false); - } - - @Test - public void testIsPackageClass5() { - doTestIsPackageClass("kotlin.io.IoPackage", true); - } - - @Test - public void testInnerIsPackage() { - doTestIsPackageClass("kotlin.io.IoPackage$Foo", false); - } - private void doTest(@NotNull String name, @NotNull String expectedForChild, @Nullable String expectedForParent) { doTest(new FqName(name), expectedForChild, expectedForParent); } @@ -97,8 +67,4 @@ public class PackageClassNameTest { assertEquals("Wrong result for parent [" + name + "].", expectedForParent, getPackageClassName(name.parent())); } } - - private void doTestIsPackageClass(String testedName, boolean expected) { - assertEquals("Wrong result for [" + testedName + "].", expected, isPackageClass(new FqName(testedName))); - } } diff --git a/idea/src/org/jetbrains/jet/plugin/caches/JetFromJavaDescriptorHelper.java b/idea/src/org/jetbrains/jet/plugin/caches/JetFromJavaDescriptorHelper.java index 51b94df8eea..b7365c8263f 100644 --- a/idea/src/org/jetbrains/jet/plugin/caches/JetFromJavaDescriptorHelper.java +++ b/idea/src/org/jetbrains/jet/plugin/caches/JetFromJavaDescriptorHelper.java @@ -23,9 +23,9 @@ import com.intellij.psi.*; import com.intellij.psi.impl.java.stubs.index.JavaAnnotationIndex; import com.intellij.psi.impl.java.stubs.index.JavaMethodNameIndex; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.search.PsiShortNamesCache; import com.intellij.psi.util.PsiTreeUtil; import jet.runtime.typeinfo.JetClass; +import jet.runtime.typeinfo.JetPackageClass; import jet.runtime.typeinfo.JetValueParameter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -53,12 +53,12 @@ class JetFromJavaDescriptorHelper { /** * Get java equivalents for jet top level classes. */ - static PsiClass[] getClassesForJetNamespaces(Project project, GlobalSearchScope scope) { + static Collection getClassesForJetNamespaces(Project project, GlobalSearchScope scope) { /* Will iterate through short name caches Kotlin namespaces from jar a class files will be collected from java cache Kotlin namespaces classes from sources will be collected with JetShortNamesCache.getClassesByName */ - // TODO Collect all package classes - return PsiShortNamesCache.getInstance(project).getClassesByName("namespace", scope); + + return getClassesByAnnotation(JetPackageClass.class.getSimpleName(), project, scope); } /** @@ -81,16 +81,11 @@ class JetFromJavaDescriptorHelper { static Collection getCompiledClassesForTopLevelObjects(Project project, GlobalSearchScope scope) { Set jetObjectClasses = Sets.newHashSet(); - Collection annotations = JavaAnnotationIndex.getInstance().get(JetClass.class.getSimpleName(), project, scope); - for (PsiAnnotation annotation : annotations) { - PsiModifierList modifierList = (PsiModifierList) annotation.getParent(); - final PsiElement owner = modifierList.getParent(); - if (owner instanceof PsiClass) { - PsiClass psiClass = (PsiClass) owner; - JetClassAnnotation jetAnnotation = JetClassAnnotation.get(psiClass); - if (psiClass.getContainingClass() == null && jetAnnotation.kind() == JvmStdlibNames.FLAG_CLASS_KIND_OBJECT) { - jetObjectClasses.add(psiClass); - } + Collection classesByAnnotation = getClassesByAnnotation(JetClass.class.getSimpleName(), project, scope); + for (PsiClass psiClass : classesByAnnotation) { + JetClassAnnotation jetAnnotation = JetClassAnnotation.get(psiClass); + if (psiClass.getContainingClass() == null && jetAnnotation.kind() == JvmStdlibNames.FLAG_CLASS_KIND_OBJECT) { + jetObjectClasses.add(psiClass); } } @@ -161,7 +156,7 @@ class JetFromJavaDescriptorHelper { FqName classFQN = new FqName(qualifiedName); - if (PackageClassUtils.isPackageClass(classFQN)) { + if (PackageClassUtils.isPackageClass(containingClass)) { FqName classParentFQN = QualifiedNamesUtil.withoutLastSegment(classFQN); return QualifiedNamesUtil.combine(classParentFQN, Name.identifier(method.getName())); } @@ -196,4 +191,19 @@ class JetFromJavaDescriptorHelper { return selectedMethods; } + + private static Collection getClassesByAnnotation( + String annotationName, Project project, GlobalSearchScope scope + ) { + Collection classes = Sets.newHashSet(); + Collection annotations = JavaAnnotationIndex.getInstance().get(annotationName, project, scope); + for (PsiAnnotation annotation : annotations) { + PsiModifierList modifierList = (PsiModifierList) annotation.getParent(); + final PsiElement owner = modifierList.getParent(); + if (owner instanceof PsiClass) { + classes.add((PsiClass) owner); + } + } + return classes; + } } diff --git a/idea/src/org/jetbrains/jet/plugin/editor/importOptimizer/JetImportOptimizer.java b/idea/src/org/jetbrains/jet/plugin/editor/importOptimizer/JetImportOptimizer.java index 6d713428da7..8b0d3a7d5a4 100644 --- a/idea/src/org/jetbrains/jet/plugin/editor/importOptimizer/JetImportOptimizer.java +++ b/idea/src/org/jetbrains/jet/plugin/editor/importOptimizer/JetImportOptimizer.java @@ -230,7 +230,7 @@ public class JetImportOptimizer implements ImportOptimizer { return null; } - return combineClassFqNameWithMemberName(classFQN, field.getName()); + return combineClassFqNameWithMemberName(field.getContainingClass(), classFQN, field.getName()); } // TODO: Still problem with kotlin global properties imported from class files @@ -245,7 +245,7 @@ public class JetImportOptimizer implements ImportOptimizer { return classFQN; } - return combineClassFqNameWithMemberName(classFQN, method.getName()); + return combineClassFqNameWithMemberName(method.getContainingClass(), classFQN, method.getName()); } if (element instanceof PsiPackage) { @@ -256,11 +256,11 @@ public class JetImportOptimizer implements ImportOptimizer { } @Nullable - private static FqName combineClassFqNameWithMemberName(FqName classFQN, String memberName) { + private static FqName combineClassFqNameWithMemberName(PsiClass containingClass, FqName classFQN, String memberName) { if (memberName == null) { return null; } - if (PackageClassUtils.isPackageClass(classFQN)) { + if (PackageClassUtils.isPackageClass(containingClass)) { return QualifiedNamesUtil.combine(classFQN.parent(), Name.identifier(memberName)); } else { diff --git a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java index e9adfa0ee94..230785f75de 100644 --- a/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java +++ b/idea/src/org/jetbrains/jet/plugin/libraries/DecompiledDataFactory.java @@ -25,6 +25,7 @@ import com.intellij.psi.impl.compiled.ClsFileImpl; import com.intellij.psi.util.PsiTreeUtil; import jet.runtime.typeinfo.JetClass; import jet.runtime.typeinfo.JetMethod; +import jet.runtime.typeinfo.JetPackageClass; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.di.InjectorForJavaSemanticServices; import org.jetbrains.jet.lang.descriptors.*; @@ -43,6 +44,7 @@ import java.util.*; public class DecompiledDataFactory { private static final String JET_CLASS = JetClass.class.getName(); + private static final String JET_PACKAGE_CLASS = JetPackageClass.class.getName(); private static final String JET_METHOD = JetMethod.class.getName(); private static final String DECOMPILED_COMMENT = "/* compiled code */"; private static final DescriptorRenderer DESCRIPTOR_RENDERER = @@ -233,14 +235,7 @@ public class DecompiledDataFactory { } public static boolean isKotlinNamespaceClass(@NotNull PsiClass psiClass) { - if (PackageClassUtils.isPackageClass(psiClass) && !isKotlinClass(psiClass)) { - for (PsiMethod method : psiClass.getMethods()) { - if (hasAnnotation(method, JET_METHOD)) { - return true; - } - } - } - return false; + return hasAnnotation(psiClass, JET_PACKAGE_CLASS); } public static boolean isCompiledFromKotlin(@NotNull PsiClass psiClass) { diff --git a/idea/testData/completion/basic/TopLevelNonImportedExtFun.jar b/idea/testData/completion/basic/TopLevelNonImportedExtFun.jar index ea3886e9efb..87a2f061f6d 100644 Binary files a/idea/testData/completion/basic/TopLevelNonImportedExtFun.jar and b/idea/testData/completion/basic/TopLevelNonImportedExtFun.jar differ diff --git a/idea/testData/completion/basic/TopLevelNonImportedFun.jar b/idea/testData/completion/basic/TopLevelNonImportedFun.jar index ea3886e9efb..87a2f061f6d 100644 Binary files a/idea/testData/completion/basic/TopLevelNonImportedFun.jar and b/idea/testData/completion/basic/TopLevelNonImportedFun.jar differ diff --git a/runtime/src/jet/runtime/typeinfo/JetPackageClass.java b/runtime/src/jet/runtime/typeinfo/JetPackageClass.java new file mode 100644 index 00000000000..92938b7c0ed --- /dev/null +++ b/runtime/src/jet/runtime/typeinfo/JetPackageClass.java @@ -0,0 +1,27 @@ +/* + * Copyright 2010-2013 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 jet.runtime.typeinfo; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * @url http://confluence.jetbrains.net/display/JET/Jet+Signatures + */ +@Retention(RetentionPolicy.RUNTIME) +public @interface JetPackageClass { +}