diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java index 2765ee0173d..b4def89bf7b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ClassFileFactory.java @@ -121,6 +121,18 @@ public class ClassFileFactory implements OutputFileCollection { return codegen; } + @NotNull + public PackageCodegen forFacade(@NotNull FqName facadeFqName, @NotNull Collection files) { + assert !isDone : "Already done!"; + PackageCodegen codegen = package2codegen.get(facadeFqName); + if (codegen == null) { + codegen = new PackageCodegen(state, files, facadeFqName.parent()); + package2codegen.put(facadeFqName, codegen); + } + + return codegen; + } + @NotNull private static List toIoFilesIgnoringNonPhysical(@NotNull Collection psiFiles) { List result = Lists.newArrayList(); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegen.java index 03342e8594b..f80a113ec2d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PackageCodegen.java @@ -101,7 +101,7 @@ public class PackageCodegen { ClassBuilder v = PackageCodegen.this.state.getFactory().newVisitor( PackageFacade(packageFragment == null ? compiledPackageFragment : packageFragment), - packageClassType, PackagePartClassUtils.getPackageFilesWithCallables(files) + packageClassType, PackagePartClassUtils.getFilesWithCallables(files) ); v.defineClass(sourceFile, V1_6, ACC_PUBLIC | ACC_FINAL, @@ -126,7 +126,7 @@ public class PackageCodegen { return null; } - List packageFilesWithCallables = PackagePartClassUtils.getPackageFilesWithCallables(packageFiles); + List packageFilesWithCallables = PackagePartClassUtils.getFilesWithCallables(packageFiles); return packageFilesWithCallables.size() == 1 ? packageFilesWithCallables.get(0) : null; } 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 de8594c63b6..9b2853f697a 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 @@ -197,7 +197,7 @@ public class CliLightClassGenerationSupport extends LightClassGenerationSupport public Collection getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope) { Collection filesInPackage = findFilesForPackage(packageFqName, scope); - List filesWithCallables = PackagePartClassUtils.getPackageFilesWithCallables(filesInPackage); + List filesWithCallables = PackagePartClassUtils.getFilesWithCallables(filesInPackage); if (filesWithCallables.isEmpty()) return Collections.emptyList(); //noinspection RedundantTypeArguments @@ -211,6 +211,31 @@ public class CliLightClassGenerationSupport extends LightClassGenerationSupport return bindingContext.get(BindingContext.CLASS, classOrObject); } + @NotNull + @Override + public Collection getFacadeClasses(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) { + Collection filesInPackage = findFilesForPackage(facadeFqName.parent(), scope); + List filesForFacade = PackagePartClassUtils.getFilesForFacade(facadeFqName, filesInPackage); + if (filesForFacade.isEmpty()) return Collections.emptyList(); + + //noinspection RedundantTypeArguments + return UtilsPackage.emptyOrSingletonList( + KotlinLightClassForFacade.Factory.createForFacade(psiManager, facadeFqName, scope, filesForFacade)); + } + + @NotNull + @Override + public Collection findFilesForFacade(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) { + Collection filesInPackage = findFilesForPackage(facadeFqName.parent(), scope); + return PackagePartClassUtils.getFilesForFacade(facadeFqName, filesInPackage); + } + + @NotNull + @Override + public LightClassConstructionContext getContextForFacade(@NotNull Collection files) { + return getContext(); + } + @NotNull @Override public BindingTraceContext createTrace() { diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt index a5d88e8fea3..442ae3599eb 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/compiler/KotlinCoreEnvironment.kt @@ -376,7 +376,8 @@ public class KotlinCoreEnvironment private constructor( with (projectEnvironment.getProject()) { registerService(javaClass(), JetScriptDefinitionProvider()) registerService(javaClass(), KotlinJavaPsiFacade(this)) - registerService(javaClass(), KotlinLightClassForFacade.FileStubCache(this)) + registerService(javaClass(), KotlinLightClassForFacade.PackageFacadeStubCache(this)) + registerService(javaClass(), KotlinLightClassForFacade.FacadeStubCache(this)) } } 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 index 0e939df185e..3a84d1ffce4 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/PackagePartClassUtils.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/PackagePartClassUtils.java @@ -16,10 +16,10 @@ 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.psi.PsiElement; import com.intellij.util.PathUtil; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -28,7 +28,10 @@ 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.*; +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; @@ -45,8 +48,10 @@ public class PackagePartClassUtils { } @NotNull - private static String getSanitizedIdentifier(@NotNull String str) { + 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; } @@ -68,21 +73,16 @@ public class PackagePartClassUtils { } } + 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 = FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.getName())); + return facadeFqName.parent().child(Name.identifier(getSanitizedClassNameBase(fileName) + FACADE_CLASS_NAME_SUFFIX)); + } - if (!fileName.isEmpty()) { - char c = fileName.charAt(0); - // Character.isUpperCase also handles non-latin characters - this is not what we want in some locales. - // Use "ASCII capitalization". - if ('a' <= c && c <= 'z') { - fileName = toUpperAscii(c) + fileName.substring(1); - } - fileName += "Kt"; - } - - return facadeFqName.parent().child(Name.identifier(getSanitizedIdentifier(fileName))); + public static boolean isFacadeClassFqName(@NotNull FqName classFqName) { + return classFqName.shortName().asString().endsWith(FACADE_CLASS_NAME_SUFFIX); } @NotNull @@ -108,7 +108,7 @@ public class PackagePartClassUtils { } @NotNull - public static List getPackageFilesWithCallables(@NotNull Collection packageFiles) { + public static List getFilesWithCallables(@NotNull Collection packageFiles) { return ContainerUtil.filter(packageFiles, new Condition() { @Override public boolean value(JetFile packageFile) { @@ -126,4 +126,18 @@ public class PackagePartClassUtils { 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/incremental/IncrementalPackageFragmentProvider.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/incremental/IncrementalPackageFragmentProvider.kt index 518f492af67..c01b45f344a 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/incremental/IncrementalPackageFragmentProvider.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/load/kotlin/incremental/IncrementalPackageFragmentProvider.kt @@ -77,7 +77,7 @@ public class IncrementalPackageFragmentProvider( fqNamesToLoad = ( obsoletePackageParts.map { JvmClassName.byInternalName(it).getPackageFqName() } - + PackagePartClassUtils.getPackageFilesWithCallables(sourceFiles).map { it.getPackageFqName() } + + PackagePartClassUtils.getFilesWithCallables(sourceFiles).map { it.getPackageFqName() } ).toSet() fqNamesToLoad.forEach { createPackageFragment(it) } 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 fae86bf264c..69ad26347e0 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/JavaElementFinder.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/JavaElementFinder.java @@ -32,6 +32,7 @@ import com.intellij.util.containers.SLRUCache; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.load.kotlin.PackageClassUtils; +import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils; import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.NamePackage; import org.jetbrains.kotlin.psi.JetClassOrObject; @@ -117,7 +118,10 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM findClassesAndObjects(qualifiedName, scope, answer); - if (PackageClassUtils.isPackageClassFqName(qualifiedName)) { + if (PackagePartClassUtils.isFacadeClassFqName(qualifiedName)) { + answer.addAll(lightClassGenerationSupport.getFacadeClasses(qualifiedName, scope)); + } + else if (PackageClassUtils.isPackageClassFqName(qualifiedName)) { answer.addAll(lightClassGenerationSupport.getPackageClasses(qualifiedName.parent(), scope)); } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinJavaFileStubProvider.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinJavaFileStubProvider.java index 9665f17e08e..16369b97b4e 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinJavaFileStubProvider.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinJavaFileStubProvider.java @@ -66,15 +66,15 @@ import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToD public class KotlinJavaFileStubProvider implements CachedValueProvider { @NotNull - public static KotlinJavaFileStubProvider createForPackageClass( + public static KotlinJavaFileStubProvider createForPackageClass( @NotNull final Project project, @NotNull final FqName packageFqName, @NotNull final GlobalSearchScope searchScope ) { - return new KotlinJavaFileStubProvider( + return new KotlinJavaFileStubProvider( project, false, - new StubGenerationStrategy() { + new StubGenerationStrategy() { @NotNull @Override public LightClassConstructionContext getContext(@NotNull Collection files) { @@ -91,12 +91,12 @@ public class KotlinJavaFileStubProvider createForFacadeClass( + @NotNull final Project project, + @NotNull final FqName facadeFqName, + @NotNull final GlobalSearchScope searchScope + ) { + return new KotlinJavaFileStubProvider( + project, + false, + new StubGenerationStrategy() { + @NotNull + @Override + public Collection getFiles() { + return LightClassGenerationSupport.getInstance(project).findFilesForFacade(facadeFqName, searchScope); + } + + @NotNull + @Override + public FqName getPackageFqName() { + return facadeFqName.parent(); + } + + @NotNull + @Override + public LightClassConstructionContext getContext(@NotNull Collection files) { + return LightClassGenerationSupport.getInstance(project).getContextForFacade(files); + } + + @NotNull + @Override + public KotlinFacadeLightClassData createLightClassData( + PsiJavaFileStub javaFileStub, + BindingContext bindingContext, + Diagnostics extraDiagnostics + ) { + return new KotlinFacadeLightClassData(javaFileStub, extraDiagnostics); + } + + @Override + public GenerationState.GenerateClassFilter getGenerateClassFilter() { + return new GenerationState.GenerateClassFilter() { + @Override + public boolean shouldAnnotateClass(JetClassOrObject classOrObject) { + return shouldGenerateClass(classOrObject); + } + + @Override + public boolean shouldGenerateClass(JetClassOrObject classOrObject) { + return JetPsiUtil.isLocal(classOrObject); + } + + @Override + public boolean shouldGeneratePackagePart(JetFile jetFile) { + return true; + } + + @Override + public boolean shouldGenerateScript(JetScript script) { + return false; + } + }; + } + + @Override + public void generate(@NotNull GenerationState state, @NotNull Collection files) { + PackageCodegen codegen = state.getFactory().forFacade(facadeFqName, files); + codegen.generate(CompilationErrorHandler.THROW_EXCEPTION); + state.getFactory().asList(); + } + + @Override + public String toString() { + return StubGenerationStrategy.class.getName() + " for facade class"; + } + }); + } + @NotNull public static KotlinJavaFileStubProvider createForDeclaredClass(@NotNull final JetClassOrObject classOrObject) { return new KotlinJavaFileStubProvider( diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassForFacade.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassForFacade.kt index ce8dae99d5d..7de316b9d5c 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassForFacade.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinLightClassForFacade.kt @@ -33,42 +33,70 @@ import org.jetbrains.annotations.NonNls import org.jetbrains.kotlin.idea.JetLanguage import org.jetbrains.kotlin.load.kotlin.PackageClassUtils import org.jetbrains.kotlin.name.FqName -import org.jetbrains.kotlin.psi.JetFile -import javax.swing.* import org.jetbrains.kotlin.psi.JetClassOrObject +import org.jetbrains.kotlin.psi.JetFile +import javax.swing.Icon public class KotlinLightClassForFacade private constructor( manager: PsiManager, private val facadeClassFqName: FqName, private val searchScope: GlobalSearchScope, + private val lightClassDataCache: CachedValue, files: Collection ) : KotlinWrappingLightClass(manager), JetJavaMirrorMarker { - public class FileStubCache(private val project: Project) { - private data class Key(val fqName: FqName, val searchScope: GlobalSearchScope) + private data class StubCacheKey(val fqName: FqName, val searchScope: GlobalSearchScope) - private inner class CacheData { - val cache = object : SLRUCache>(20, 30) { - override fun createValue(key: Key): CachedValue { + public class PackageFacadeStubCache(private val project: Project) { + private inner class PackageFacadeCacheData { + val cache = object : SLRUCache>(20, 30) { + override fun createValue(key: StubCacheKey): CachedValue { val stubProvider = KotlinJavaFileStubProvider.createForPackageClass(project, key.fqName, key.searchScope) - return CachedValuesManager.getManager(project).createCachedValue(stubProvider, /*trackValue = */false) + return CachedValuesManager.getManager(project).createCachedValue(stubProvider, /*trackValue = */false) } } } - private val cachedValue: CachedValue = CachedValuesManager.getManager(project).createCachedValue( - { CachedValueProvider.Result.create(CacheData(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT) }, + private val cachedValue: CachedValue = CachedValuesManager.getManager(project).createCachedValue( + { CachedValueProvider.Result.create(PackageFacadeCacheData(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT) }, /*trackValue = */ false) - public fun get(qualifiedName: FqName, searchScope: GlobalSearchScope): CachedValue { + public fun get(qualifiedName: FqName, searchScope: GlobalSearchScope): CachedValue { synchronized (cachedValue) { - return cachedValue.getValue().cache.get(Key(qualifiedName, searchScope)) + return cachedValue.getValue().cache.get(StubCacheKey(qualifiedName, searchScope)) } } companion object { - public fun getInstance(project: Project): FileStubCache { - return ServiceManager.getService(project, javaClass()) + public fun getInstance(project: Project): PackageFacadeStubCache { + return ServiceManager.getService(project, javaClass()) + } + } + } + + public class FacadeStubCache(private val project: Project) { + private inner class FacadeCacheData { + val cache = object : SLRUCache>(20, 30) { + override fun createValue(key: StubCacheKey): CachedValue { + val stubProvider = KotlinJavaFileStubProvider.createForFacadeClass(project, key.fqName, key.searchScope) + return CachedValuesManager.getManager(project).createCachedValue(stubProvider, /*trackValue = */false) + } + } + } + + private val cachedValue: CachedValue = CachedValuesManager.getManager(project).createCachedValue( + { CachedValueProvider.Result.create(FacadeCacheData(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT) }, + /*trackValue = */ false) + + public fun get(qualifiedName: FqName, searchScope: GlobalSearchScope): CachedValue { + synchronized (cachedValue) { + return cachedValue.getValue().cache.get(StubCacheKey(qualifiedName, searchScope)) + } + } + + companion object { + public fun getInstance(project: Project): FacadeStubCache { + return ServiceManager.getService(project, javaClass()) } } } @@ -81,9 +109,6 @@ public class KotlinLightClassForFacade private constructor( private val packageFqName: FqName = facadeClassFqName.parent() - private val lightClassDataCache: CachedValue = - FileStubCache.getInstance(getProject()).get(packageFqName, searchScope) - private val modifierList: PsiModifierList = LightModifierList(manager, JetLanguage.INSTANCE, PsiModifier.PUBLIC, PsiModifier.FINAL) @@ -159,7 +184,7 @@ public class KotlinLightClassForFacade private constructor( override fun isValid() = files.all { it.isValid() } - override fun copy() = KotlinLightClassForFacade(getManager(), facadeClassFqName, searchScope, files) + override fun copy() = KotlinLightClassForFacade(getManager(), facadeClassFqName, searchScope, lightClassDataCache, files) override fun getDelegate(): PsiClass { val psiClass = LightClassUtil.findClass(facadeClassFqName, lightClassDataCache.getValue().javaFileStub) @@ -223,7 +248,24 @@ public class KotlinLightClassForFacade private constructor( assert(files.isNotEmpty()) { "No files for package $packageFqName" } val packageClassFqName = PackageClassUtils.getPackageClassFqName(packageFqName) - return KotlinLightClassForFacade(manager, packageClassFqName, searchScope, files) + val lightClassDataCache = PackageFacadeStubCache.getInstance(manager.project).get(packageFqName, searchScope) + return KotlinLightClassForFacade(manager, packageClassFqName, searchScope, lightClassDataCache, files) + } + + public fun createForFacade( + manager: PsiManager, + facadeClassFqName: FqName, + searchScope: GlobalSearchScope, + files: Collection + ): KotlinLightClassForFacade? { + if (files.any { LightClassUtil.belongsToKotlinBuiltIns(it) }) { + return null + } + + assert(files.isNotEmpty()) { "No files for facade $facadeClassFqName" } + + val lightClassDataCache = FacadeStubCache.getInstance(manager.project).get(facadeClassFqName, searchScope) + return KotlinLightClassForFacade(manager, facadeClassFqName, searchScope, lightClassDataCache, files) } } } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.java b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.java index bb92396fb70..52ccc8dc6ca 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.java +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassGenerationSupport.java @@ -73,6 +73,17 @@ public abstract class LightClassGenerationSupport { @NotNull public abstract Collection getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope); + @Nullable public abstract ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject); + + @NotNull + public abstract Collection getFacadeClasses(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope); + + @NotNull + public abstract Collection findFilesForFacade(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope); + + @NotNull + public abstract LightClassConstructionContext getContextForFacade(@NotNull Collection files); + } diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassStubWithData.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassStubWithData.kt index 27ac4ab039d..e636bc74f89 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassStubWithData.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/LightClassStubWithData.kt @@ -33,7 +33,7 @@ interface LightClassDataForKotlinClass: LightClassData { val jvmQualifiedName: FqName } -data class KotlinPackageLightClassData( +data class KotlinFacadeLightClassData( override val javaFileStub: PsiJavaFileStub, override val extraDiagnostics: Diagnostics ): LightClassData, WithFileStubAndExtraDiagnostics diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/duplicateJvmSignatureUtil.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/duplicateJvmSignatureUtil.kt index 9ca06569be0..8e677294f16 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/duplicateJvmSignatureUtil.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/duplicateJvmSignatureUtil.kt @@ -31,7 +31,7 @@ import org.jetbrains.kotlin.psi.* public fun getJvmSignatureDiagnostics(element: PsiElement, otherDiagnostics: Diagnostics, moduleScope: GlobalSearchScope): Diagnostics? { fun getDiagnosticsForPackage(file: JetFile): Diagnostics? { val project = file.getProject() - val cache = KotlinLightClassForFacade.FileStubCache.getInstance(project) + val cache = KotlinLightClassForFacade.PackageFacadeStubCache.getInstance(project) return cache[file.getPackageFqName(), moduleScope].getValue()?.extraDiagnostics } diff --git a/compiler/testData/asJava/lightClasses/facades/SingleFile.java b/compiler/testData/asJava/lightClasses/facades/SingleFile.java new file mode 100644 index 00000000000..1ff37beb523 --- /dev/null +++ b/compiler/testData/asJava/lightClasses/facades/SingleFile.java @@ -0,0 +1,4 @@ +@kotlin.jvm.internal.KotlinSyntheticClass(abiVersion = 23, kind = kotlin.jvm.internal.KotlinSyntheticClass.Kind.PACKAGE_PART) +public final class SingleFileKt { + public static final void foo() { /* compiled code */ } +} diff --git a/compiler/testData/asJava/lightClasses/facades/SingleFile.kt b/compiler/testData/asJava/lightClasses/facades/SingleFile.kt new file mode 100644 index 00000000000..894f4f770a8 --- /dev/null +++ b/compiler/testData/asJava/lightClasses/facades/SingleFile.kt @@ -0,0 +1,3 @@ +// SingleFileKt + +public fun foo() {} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/asJava/KotlinLightClassTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/asJava/KotlinLightClassTestGenerated.java index 01d5419a21c..e9fbe160e28 100644 --- a/compiler/tests/org/jetbrains/kotlin/asJava/KotlinLightClassTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/asJava/KotlinLightClassTestGenerated.java @@ -62,6 +62,21 @@ public class KotlinLightClassTestGenerated extends AbstractKotlinLightClassTest } } + @TestMetadata("compiler/testData/asJava/lightClasses/facades") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Facades extends AbstractKotlinLightClassTest { + public void testAllFilesPresentInFacades() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/asJava/lightClasses/facades"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("SingleFile.kt") + public void testSingleFile() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/asJava/lightClasses/facades/SingleFile.kt"); + doTest(fileName); + } + } + @TestMetadata("compiler/testData/asJava/lightClasses/nullabilityAnnotations") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.java index d705d62a819..6e5b274ade4 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.java +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/IDELightClassGenerationSupport.java @@ -84,6 +84,11 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport public LightClassConstructionContext getContextForPackage(@NotNull Collection files) { assert !files.isEmpty() : "No files in package"; + return getContextForFiles(files); + } + + @NotNull + private LightClassConstructionContext getContextForFiles(@NotNull Collection files) { List sortedFiles = new ArrayList(files); Collections.sort(sortedFiles, scopeFileComparator); @@ -118,6 +123,14 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport return new LightClassConstructionContext(bindingContext, moduleDescriptor); } + @NotNull + @Override + public LightClassConstructionContext getContextForFacade(@NotNull Collection files) { + assert !files.isEmpty() : "No files in facade"; + + return getContextForFiles(files); + } + private static void forceResolvePackageDeclarations(@NotNull Collection files, @NotNull KotlinCodeAnalyzer session) { for (JetFile file : files) { // SCRIPT: not supported @@ -272,9 +285,9 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport @Override public Collection getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope) { List result = new ArrayList(); - List packageClassesInfos = findPackageClassesInfos(packageFqName, scope); - for (KotlinLightPackageClassInfo info : packageClassesInfos) { - List files = PackagePartClassUtils.getPackageFilesWithCallables(info.getFiles()); + List packageClassesInfos = findPackageClassesInfos(packageFqName, scope); + for (KotlinLightFacadeClassInfo info : packageClassesInfos) { + List files = PackagePartClassUtils.getFilesWithCallables(info.getFiles()); if (files.isEmpty()) continue; IdeaModuleInfo moduleInfo = info.getModuleInfo(); @@ -292,7 +305,7 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport } } else { - PsiClass clsClass = getLightClassForDecompiledPackage(packageFqName, files); + PsiClass clsClass = getLightClassForDecompiledFacade(packageFqName, files); if (clsClass != null) { result.add(clsClass); } @@ -301,6 +314,60 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport return result; } + @NotNull + @Override + public Collection getFacadeClasses(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) { + List result = new ArrayList(); + List facadeClassesInfos = findFacadeClassesInfos(facadeFqName, scope); + for (KotlinLightFacadeClassInfo info : facadeClassesInfos) { + List files = PackagePartClassUtils.getFilesWithCallables(info.getFiles()); + if (files.isEmpty()) continue; + + IdeaModuleInfo moduleInfo = info.getModuleInfo(); + if (moduleInfo instanceof ModuleSourceInfo) { + KotlinLightClassForFacade lightClass = + KotlinLightClassForFacade.Factory.createForFacade(psiManager, facadeFqName, moduleInfo.contentScope(), files); + if (lightClass == null) continue; + + result.add(lightClass); + + if (files.size() > 1) { + for (JetFile file : files) { + result.add(new FakeLightClassForFileOfPackage(psiManager, lightClass, file)); + } + } + } + else { + PsiClass clsClass = getLightClassForDecompiledFacade(facadeFqName, files); + if (clsClass != null) { + result.add(clsClass); + } + } + } + return result; + } + + @NotNull + @Override + public Collection findFilesForFacade(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) { + // TODO naive version. Production variant should use an equivalent of JetExactPackageIndex. + Collection packageFiles = findFilesForPackage(facadeFqName.parent(), scope); + return PackagePartClassUtils.getFilesForFacade(facadeFqName, packageFiles); + } + + @NotNull + private List findFacadeClassesInfos(FqName facadeFqName, GlobalSearchScope scope) { + // TODO naive version. Production variant should use an equivalent of JetExactPackageIndex. + Collection packageFiles = findFilesForPackage(facadeFqName.parent(), scope); + List facadeFiles = PackagePartClassUtils.getFilesForFacade(facadeFqName, packageFiles); + Map> filesByInfo = groupByModuleInfo(facadeFiles); + List result = new ArrayList(); + for (Map.Entry> entry : filesByInfo.entrySet()) { + result.add(new KotlinLightFacadeClassInfo(entry.getValue(), entry.getKey())); + } + return result; + } + @Nullable @Override public ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject) { @@ -313,11 +380,11 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport } @Nullable - private static PsiClass getLightClassForDecompiledPackage(@NotNull FqName packageFqName, @NotNull List filesWithCallables) { + private static PsiClass getLightClassForDecompiledFacade(@NotNull FqName facadeFqName, @NotNull List filesWithCallables) { JetFile firstFile = filesWithCallables.iterator().next(); if (firstFile.isCompiled()) { if (filesWithCallables.size() > 1) { - LOG.error("Several files with callables for package: " + packageFqName); + LOG.error("Several files with callables for facade: " + facadeFqName); } return createLightClassForDecompiledKotlinFile(firstFile); } @@ -325,23 +392,23 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport } @NotNull - private List findPackageClassesInfos( + private List findPackageClassesInfos( @NotNull FqName fqName, @NotNull GlobalSearchScope wholeScope ) { Collection allFiles = findFilesForPackage(fqName, wholeScope); Map> filesByInfo = groupByModuleInfo(allFiles); - List result = new ArrayList(); + List result = new ArrayList(); for (Map.Entry> entry : filesByInfo.entrySet()) { - result.add(new KotlinLightPackageClassInfo(entry.getValue(), entry.getKey())); + result.add(new KotlinLightFacadeClassInfo(entry.getValue(), entry.getKey())); } return result; } - private static final class KotlinLightPackageClassInfo { + private static final class KotlinLightFacadeClassInfo { private final Collection files; private final IdeaModuleInfo moduleInfo; - public KotlinLightPackageClassInfo(@NotNull Collection files, @NotNull IdeaModuleInfo moduleInfo) { + public KotlinLightFacadeClassInfo(@NotNull Collection files, @NotNull IdeaModuleInfo moduleInfo) { this.files = files; this.moduleInfo = moduleInfo; } diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index ecaa0f75740..4ddb8b09dfb 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -206,8 +206,11 @@ - + + + diff --git a/idea/testData/debugger/tinyApp/outs/frameSimple.out b/idea/testData/debugger/tinyApp/outs/frameSimple.out index 4b738409d29..c88ad8db30d 100644 --- a/idea/testData/debugger/tinyApp/outs/frameSimple.out +++ b/idea/testData/debugger/tinyApp/outs/frameSimple.out @@ -34,7 +34,7 @@ class MyClass // RESULT: 2: I frame = main():9, FrameSimpleKt {frameSimple} static = static = frameSimple.FrameSimpleKt - field = topVal1: int = 1 (sp = null) + field = topVal1: int = 1 (sp = frameSimple.kt, 3) local = args: java.lang.String[] = {java.lang.String[0]@uniqueID} (sp = frameSimple.kt, 5) local = val1: int = 1 (sp = frameSimple.kt, 6) local = val2: frameSimple.MyClass = {frameSimple.MyClass@uniqueID} (sp = frameSimple.kt, 7)