diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.java b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.java index dc574055797..2084545628a 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.java +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/CliLightClassGenerationSupport.java @@ -228,7 +228,7 @@ public class CliLightClassGenerationSupport extends LightClassGenerationSupport // TODO We need a way to plug some platform-dependent stuff into LazyTopDownAnalyzer. // It already performs some ad hoc stuff for packages->files mapping, anyway. Collection filesInPackage = findFilesForPackage(facadeFqName.parent(), scope); - return PackagePartClassUtils.getFilesForFacade(facadeFqName, filesInPackage); + return PackagePartClassUtils.getFilesForPart(facadeFqName, filesInPackage); } @NotNull diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/PackagePartClassUtils.java b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/PackagePartClassUtils.java deleted file mode 100644 index 93c25e18c20..00000000000 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/PackagePartClassUtils.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2010-2015 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.kotlin.load.kotlin; - -import com.google.common.io.Files; -import com.intellij.openapi.util.Condition; -import com.intellij.openapi.util.io.FileUtil; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.util.PathUtil; -import com.intellij.util.containers.ContainerUtil; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.annotations.TestOnly; -import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; -import org.jetbrains.kotlin.name.FqName; -import org.jetbrains.kotlin.name.Name; -import org.jetbrains.kotlin.psi.JetDeclaration; -import org.jetbrains.kotlin.psi.JetFile; -import org.jetbrains.kotlin.psi.JetNamedFunction; -import org.jetbrains.kotlin.psi.JetProperty; -import org.jetbrains.kotlin.resolve.jvm.JvmClassName; -import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor; -import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf; -import org.jetbrains.org.objectweb.asm.Type; - -import java.util.Collection; -import java.util.List; - -import static org.jetbrains.kotlin.load.kotlin.PackageClassUtils.getPackageClassFqName; - -public class PackagePartClassUtils { - public static int getPathHashCode(@NotNull VirtualFile file) { - return file.getPath().toLowerCase().hashCode(); - } - - @NotNull - public static String getSanitizedClassNameBase(@NotNull String str) { - if (str.isEmpty()) return "__EMPTY__"; - str = str.replaceAll("[^\\p{L}\\p{Digit}]", "_"); - str = toUpperAscii(str.charAt(0)) + str.substring(1); - if (!Character.isJavaIdentifierStart(str.charAt(0))) { - str = "_" + str; - } - return str; - } - - @NotNull - @TestOnly - public static FqName getPackagePartFqName(@NotNull FqName facadeFqName, @NotNull VirtualFile file) { - return getPackagePartFqName(facadeFqName, file, null); - } - - private static char toUpperAscii(char ch) { - if ('a' <= ch && ch <= 'z') { - return (char) (ch - 32); - } - else { - return ch; - } - } - - private static final String FACADE_CLASS_NAME_SUFFIX = "Kt"; - - @NotNull - public static FqName getPackagePartFqName(@NotNull FqName facadeFqName, @NotNull VirtualFile file, @Nullable JetFile jetFile) { - String fileName = file.getName(); - return getStaticFacadeClassFqNameForFile(facadeFqName.parent(), fileName); - } - - @NotNull - private static FqName getStaticFacadeClassFqNameForFile(@NotNull FqName packageFqName, String fileName) { - String nameWithoutExtension = FileUtil.getNameWithoutExtension(PathUtil.getFileName(fileName)); - return packageFqName.child(Name.identifier(getSanitizedClassNameBase(nameWithoutExtension) + FACADE_CLASS_NAME_SUFFIX)); - } - - public static boolean isFacadeClassFqName(@NotNull FqName classFqName) { - return classFqName.shortName().asString().endsWith(FACADE_CLASS_NAME_SUFFIX); - } - - @NotNull - public static Type getPackagePartType(@NotNull JetFile file) { - return Type.getObjectType(getPackagePartInternalName(file)); - } - - @NotNull - public static String getPackagePartInternalName(@NotNull JetFile file) { - FqName fqName = getPackagePartFqName(file); - return JvmClassName.byFqNameWithoutInnerClasses(fqName).getInternalName(); - } - - @NotNull - public static FqName getPackagePartFqName(@NotNull JetFile file) { - return getStaticFacadeClassFqNameForFile(file.getPackageFqName(), file.getName()); - } - - @NotNull - public static FqName getPackagePartFqName(@NotNull DeserializedCallableMemberDescriptor callable) { - FqName packageFqName = ((PackageFragmentDescriptor) callable.getContainingDeclaration()).getFqName(); - return packageFqName.child(callable.getNameResolver().getName(callable.getProto().getExtension(JvmProtoBuf.implClassName))); - } - - @NotNull - public static List getFilesWithCallables(@NotNull Collection packageFiles) { - return ContainerUtil.filter(packageFiles, new Condition() { - @Override - public boolean value(JetFile packageFile) { - return fileHasCallables(packageFile); - } - }); - } - - public static boolean fileHasCallables(@NotNull JetFile file) { - for (JetDeclaration declaration : file.getDeclarations()) { - if (declaration instanceof JetProperty || declaration instanceof JetNamedFunction) { - return true; - } - } - return false; - } - - @NotNull - public static List getFilesForFacade(@NotNull FqName facadeFqName, @NotNull Collection files) { - // TODO Naive implementation. Replace it with "smarter" version. - assert isFacadeClassFqName(facadeFqName); - String facadeSimpleName = facadeFqName.shortName().asString(); - final String expectedSanitizedName = facadeSimpleName.substring(0, facadeSimpleName.length() - 2); - return ContainerUtil.filter(getFilesWithCallables(files), new Condition() { - @Override - public boolean value(JetFile input) { - String sanitizedName = getSanitizedClassNameBase(Files.getNameWithoutExtension(input.getName())); - return expectedSanitizedName.equals(sanitizedName); - } - }); - } -} diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/PackagePartClassUtils.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/PackagePartClassUtils.kt new file mode 100644 index 00000000000..0eb666739a1 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/PackagePartClassUtils.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2010-2015 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.kotlin.load.kotlin + +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.vfs.VirtualFile +import org.jetbrains.annotations.TestOnly +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.psi.JetFile +import org.jetbrains.kotlin.psi.JetNamedFunction +import org.jetbrains.kotlin.psi.JetProperty +import org.jetbrains.kotlin.resolve.jvm.JvmClassName +import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor +import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf +import org.jetbrains.org.objectweb.asm.Type +import java.util.* + + public object PackagePartClassUtils { + public @jvmStatic fun getPathHashCode(file: VirtualFile): Int = + file.path.toLowerCase().hashCode() + + private val PART_CLASS_NAME_SUFFIX = "Kt" + + private @jvmStatic fun getPartClassName(str: String): String = + if (str.isEmpty()) + "_$PART_CLASS_NAME_SUFFIX" + else + capitalizeAsJavaClassName(sanitizeAsJavaIdentifier(str)) + PART_CLASS_NAME_SUFFIX + + private @jvmStatic fun sanitizeAsJavaIdentifier(str: String): String = + str.replace("[^\\p{L}\\p{Digit}]".toRegex(), "_") + + private @jvmStatic fun capitalizeAsJavaClassName(str: String): String = + // NB use Locale.ENGLISH so that build is locale-independent. + // See Javadoc on java.lang.String.toUpperCase() for more details. + if (Character.isJavaIdentifierStart(str.charAt(0))) + str.substring(0, 1).toUpperCase(Locale.ENGLISH) + str.substring(1) + else + "_$str" + + @TestOnly + public @jvmStatic fun getDefaultPartFqName(facadeClassFqName: FqName, file: VirtualFile): FqName = + getDefaultPartFqNameForFilePath(facadeClassFqName.parent(), file.name) + + private @jvmStatic fun getDefaultPartFqNameForFilePath(packageFqName: FqName, fileName: String): FqName { + val partClassName = getPartClassName(FileUtil.getNameWithoutExtension(fileName)) + return packageFqName.child(Name.identifier(partClassName)) + } + + public @jvmStatic fun isPartClassFqName(classFqName: FqName): Boolean = + classFqName.shortName().asString().endsWith(PART_CLASS_NAME_SUFFIX) + + public @jvmStatic fun getPackagePartType(file: JetFile): Type = + Type.getObjectType(getPackagePartInternalName(file)) + + public @jvmStatic fun getPackagePartInternalName(file: JetFile): String = + JvmClassName.byFqNameWithoutInnerClasses(getPackagePartFqName(file)).internalName + + public @jvmStatic fun getPackagePartFqName(file: JetFile): FqName = + getDefaultPartFqNameForFilePath(file.packageFqName, file.name) + + public @jvmStatic fun getPackagePartFqName(callable: DeserializedCallableMemberDescriptor): FqName { + val implClassName = callable.nameResolver.getName(callable.proto.getExtension(JvmProtoBuf.implClassName)) + val packageFqName = (callable.containingDeclaration as PackageFragmentDescriptor).fqName + return packageFqName.child(implClassName) + } + + public @jvmStatic fun getFilesWithCallables(files: Collection): List = + files.filter { fileHasTopLevelCallables(it) } + + public @jvmStatic fun fileHasTopLevelCallables(file: JetFile): Boolean = + file.declarations.any { it is JetProperty || it is JetNamedFunction } + + public @jvmStatic fun getFilesForPart(partFqName: FqName, files: Collection): List = + getFilesWithCallables(files).filter { getPackagePartFqName(it) == partFqName } + +} \ No newline at end of file diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/JavaElementFinder.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/JavaElementFinder.java index 69ad26347e0..bfdff4eb5ed 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/JavaElementFinder.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/JavaElementFinder.java @@ -118,7 +118,7 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM findClassesAndObjects(qualifiedName, scope, answer); - if (PackagePartClassUtils.isFacadeClassFqName(qualifiedName)) { + if (PackagePartClassUtils.isPartClassFqName(qualifiedName)) { answer.addAll(lightClassGenerationSupport.getFacadeClasses(qualifiedName, scope)); } else if (PackageClassUtils.isPackageClassFqName(qualifiedName)) { diff --git a/idea/tests/org/jetbrains/kotlin/idea/filters/JetExceptionFilterTest.kt b/idea/tests/org/jetbrains/kotlin/idea/filters/JetExceptionFilterTest.kt index f9e21727bc1..b593b5fe6b8 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/filters/JetExceptionFilterTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/filters/JetExceptionFilterTest.kt @@ -17,10 +17,8 @@ package org.jetbrains.kotlin.idea.filters import com.intellij.execution.filters.FileHyperlinkInfo -import com.intellij.execution.filters.OpenFileHyperlinkInfo import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.fileEditor.FileDocumentManager -import com.intellij.openapi.roots.ModifiableRootModel import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.roots.OrderRootType import com.intellij.openapi.util.io.FileUtilRt @@ -32,12 +30,11 @@ import com.intellij.psi.search.GlobalSearchScope import com.intellij.refactoring.MultiFileTestCase import com.intellij.testFramework.PlatformTestCase import com.intellij.testFramework.PsiTestUtil -import com.intellij.testFramework.UsefulTestCase import junit.framework.TestCase import org.jetbrains.kotlin.idea.test.PluginTestCaseBase import org.jetbrains.kotlin.load.kotlin.PackageClassUtils.getPackageClassFqName import org.jetbrains.kotlin.load.kotlin.PackageClassUtils.getPackageClassName -import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils.getPackagePartFqName +import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.test.MockLibraryUtil import java.io.File @@ -121,15 +118,15 @@ public class JetExceptionFilterTest : MultiFileTestCase() { public fun testKt2489() { val packageClassFqName = getPackageClassFqName(FqName.ROOT) - doTest("a.kt", 3, { file -> "" + getPackagePartFqName(packageClassFqName, file) + "\$a\$f\$1" }) - doTest("main.kt", 3, { file -> "" + getPackagePartFqName(packageClassFqName, file) + "\$main\$f\$1" }) + doTest("a.kt", 3, { file -> "" + PackagePartClassUtils.getDefaultPartFqName(packageClassFqName, file) + "\$a\$f\$1" }) + doTest("main.kt", 3, { file -> "" + PackagePartClassUtils.getDefaultPartFqName(packageClassFqName, file) + "\$main\$f\$1" }) } public fun testMultiSameName() { val packageClassFqName = getPackageClassFqName(FqName("multiSameName")) // The order and the exact names do matter here - doTest("1/foo1.kt", 4, { file -> "" + getPackagePartFqName(packageClassFqName, file) + "\$foo\$f\$1" }) - doTest("2/foo2.kt", 4, { file -> "" + getPackagePartFqName(packageClassFqName, file) + "\$foo\$f\$1" }) + doTest("1/foo1.kt", 4, { file -> "" + PackagePartClassUtils.getDefaultPartFqName(packageClassFqName, file) + "\$foo\$f\$1" }) + doTest("2/foo2.kt", 4, { file -> "" + PackagePartClassUtils.getDefaultPartFqName(packageClassFqName, file) + "\$foo\$f\$1" }) } public fun testLibrarySources() { @@ -151,7 +148,7 @@ public class JetExceptionFilterTest : MultiFileTestCase() { doTest("src/lib.kt", 3, { "test.Foo" }, libRootUrl = libRootUrl) doTest("src/lib.kt", 4, { "test.Foo" }, libRootUrl = libRootUrl) - doTest("src/lib.kt", 9, { "" + getPackagePartFqName(packageClassFqName, it) }, libRootUrl = libRootUrl) - doTest("src/other.kt", 4, { "" + getPackagePartFqName(packageClassFqName, it) }, libRootUrl = libRootUrl) + doTest("src/lib.kt", 9, { "" + PackagePartClassUtils.getDefaultPartFqName(packageClassFqName, it) }, libRootUrl = libRootUrl) + doTest("src/other.kt", 4, { "" + PackagePartClassUtils.getDefaultPartFqName(packageClassFqName, it) }, libRootUrl = libRootUrl) } } diff --git a/jps-plugin/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt b/jps-plugin/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt index e6ad036a5ed..709312d0fab 100644 --- a/jps-plugin/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt +++ b/jps-plugin/test/org/jetbrains/kotlin/jps/build/KotlinJpsBuildTest.kt @@ -701,7 +701,7 @@ public class KotlinJpsBuildTest : AbstractKotlinJpsBuildTestCase() { } } - val packagePartFqName = PackagePartClassUtils.getPackagePartFqName(FqName(packageClassFqName), fakeVirtualFile) + val packagePartFqName = PackagePartClassUtils.getDefaultPartFqName(FqName(packageClassFqName), fakeVirtualFile) return klass(moduleName, AsmUtil.internalNameByFqNameWithoutInnerClasses(packagePartFqName)) }