Light class generation for new facades/parts.

1st working (naive) implementation.
This commit is contained in:
Dmitry Petrov
2015-07-30 15:54:18 +03:00
committed by Michael Bogdanov
parent 47f5b55e65
commit ab8b5d05ed
18 changed files with 339 additions and 61 deletions
@@ -121,6 +121,18 @@ public class ClassFileFactory implements OutputFileCollection {
return codegen; return codegen;
} }
@NotNull
public PackageCodegen forFacade(@NotNull FqName facadeFqName, @NotNull Collection<JetFile> 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 @NotNull
private static List<File> toIoFilesIgnoringNonPhysical(@NotNull Collection<? extends PsiFile> psiFiles) { private static List<File> toIoFilesIgnoringNonPhysical(@NotNull Collection<? extends PsiFile> psiFiles) {
List<File> result = Lists.newArrayList(); List<File> result = Lists.newArrayList();
@@ -101,7 +101,7 @@ public class PackageCodegen {
ClassBuilder v = PackageCodegen.this.state.getFactory().newVisitor( ClassBuilder v = PackageCodegen.this.state.getFactory().newVisitor(
PackageFacade(packageFragment == null ? compiledPackageFragment : packageFragment), PackageFacade(packageFragment == null ? compiledPackageFragment : packageFragment),
packageClassType, PackagePartClassUtils.getPackageFilesWithCallables(files) packageClassType, PackagePartClassUtils.getFilesWithCallables(files)
); );
v.defineClass(sourceFile, V1_6, v.defineClass(sourceFile, V1_6,
ACC_PUBLIC | ACC_FINAL, ACC_PUBLIC | ACC_FINAL,
@@ -126,7 +126,7 @@ public class PackageCodegen {
return null; return null;
} }
List<JetFile> packageFilesWithCallables = PackagePartClassUtils.getPackageFilesWithCallables(packageFiles); List<JetFile> packageFilesWithCallables = PackagePartClassUtils.getFilesWithCallables(packageFiles);
return packageFilesWithCallables.size() == 1 ? packageFilesWithCallables.get(0) : null; return packageFilesWithCallables.size() == 1 ? packageFilesWithCallables.get(0) : null;
} }
@@ -197,7 +197,7 @@ public class CliLightClassGenerationSupport extends LightClassGenerationSupport
public Collection<PsiClass> getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope) { public Collection<PsiClass> getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope) {
Collection<JetFile> filesInPackage = findFilesForPackage(packageFqName, scope); Collection<JetFile> filesInPackage = findFilesForPackage(packageFqName, scope);
List<JetFile> filesWithCallables = PackagePartClassUtils.getPackageFilesWithCallables(filesInPackage); List<JetFile> filesWithCallables = PackagePartClassUtils.getFilesWithCallables(filesInPackage);
if (filesWithCallables.isEmpty()) return Collections.emptyList(); if (filesWithCallables.isEmpty()) return Collections.emptyList();
//noinspection RedundantTypeArguments //noinspection RedundantTypeArguments
@@ -211,6 +211,31 @@ public class CliLightClassGenerationSupport extends LightClassGenerationSupport
return bindingContext.get(BindingContext.CLASS, classOrObject); return bindingContext.get(BindingContext.CLASS, classOrObject);
} }
@NotNull
@Override
public Collection<PsiClass> getFacadeClasses(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) {
Collection<JetFile> filesInPackage = findFilesForPackage(facadeFqName.parent(), scope);
List<JetFile> filesForFacade = PackagePartClassUtils.getFilesForFacade(facadeFqName, filesInPackage);
if (filesForFacade.isEmpty()) return Collections.emptyList();
//noinspection RedundantTypeArguments
return UtilsPackage.<PsiClass>emptyOrSingletonList(
KotlinLightClassForFacade.Factory.createForFacade(psiManager, facadeFqName, scope, filesForFacade));
}
@NotNull
@Override
public Collection<JetFile> findFilesForFacade(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) {
Collection<JetFile> filesInPackage = findFilesForPackage(facadeFqName.parent(), scope);
return PackagePartClassUtils.getFilesForFacade(facadeFqName, filesInPackage);
}
@NotNull
@Override
public LightClassConstructionContext getContextForFacade(@NotNull Collection<JetFile> files) {
return getContext();
}
@NotNull @NotNull
@Override @Override
public BindingTraceContext createTrace() { public BindingTraceContext createTrace() {
@@ -376,7 +376,8 @@ public class KotlinCoreEnvironment private constructor(
with (projectEnvironment.getProject()) { with (projectEnvironment.getProject()) {
registerService(javaClass<JetScriptDefinitionProvider>(), JetScriptDefinitionProvider()) registerService(javaClass<JetScriptDefinitionProvider>(), JetScriptDefinitionProvider())
registerService(javaClass<KotlinJavaPsiFacade>(), KotlinJavaPsiFacade(this)) registerService(javaClass<KotlinJavaPsiFacade>(), KotlinJavaPsiFacade(this))
registerService(javaClass<KotlinLightClassForFacade.FileStubCache>(), KotlinLightClassForFacade.FileStubCache(this)) registerService(javaClass<KotlinLightClassForFacade.PackageFacadeStubCache>(), KotlinLightClassForFacade.PackageFacadeStubCache(this))
registerService(javaClass<KotlinLightClassForFacade.FacadeStubCache>(), KotlinLightClassForFacade.FacadeStubCache(this))
} }
} }
@@ -16,10 +16,10 @@
package org.jetbrains.kotlin.load.kotlin; package org.jetbrains.kotlin.load.kotlin;
import com.google.common.io.Files;
import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiElement;
import com.intellij.util.PathUtil; import com.intellij.util.PathUtil;
import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -28,7 +28,10 @@ import org.jetbrains.annotations.TestOnly;
import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor;
import org.jetbrains.kotlin.name.FqName; import org.jetbrains.kotlin.name.FqName;
import org.jetbrains.kotlin.name.Name; 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.resolve.jvm.JvmClassName;
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor; import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor;
import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf; import org.jetbrains.kotlin.serialization.jvm.JvmProtoBuf;
@@ -45,8 +48,10 @@ public class PackagePartClassUtils {
} }
@NotNull @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 = str.replaceAll("[^\\p{L}\\p{Digit}]", "_");
str = toUpperAscii(str.charAt(0)) + str.substring(1);
if (!Character.isJavaIdentifierStart(str.charAt(0))) { if (!Character.isJavaIdentifierStart(str.charAt(0))) {
str = "_" + str; str = "_" + str;
} }
@@ -68,21 +73,16 @@ public class PackagePartClassUtils {
} }
} }
private static final String FACADE_CLASS_NAME_SUFFIX = "Kt";
@NotNull @NotNull
public static FqName getPackagePartFqName(@NotNull FqName facadeFqName, @NotNull VirtualFile file, @Nullable JetFile jetFile) { public static FqName getPackagePartFqName(@NotNull FqName facadeFqName, @NotNull VirtualFile file, @Nullable JetFile jetFile) {
String fileName = FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.getName())); String fileName = FileUtil.getNameWithoutExtension(PathUtil.getFileName(file.getName()));
return facadeFqName.parent().child(Name.identifier(getSanitizedClassNameBase(fileName) + FACADE_CLASS_NAME_SUFFIX));
}
if (!fileName.isEmpty()) { public static boolean isFacadeClassFqName(@NotNull FqName classFqName) {
char c = fileName.charAt(0); return classFqName.shortName().asString().endsWith(FACADE_CLASS_NAME_SUFFIX);
// 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)));
} }
@NotNull @NotNull
@@ -108,7 +108,7 @@ public class PackagePartClassUtils {
} }
@NotNull @NotNull
public static List<JetFile> getPackageFilesWithCallables(@NotNull Collection<JetFile> packageFiles) { public static List<JetFile> getFilesWithCallables(@NotNull Collection<JetFile> packageFiles) {
return ContainerUtil.filter(packageFiles, new Condition<JetFile>() { return ContainerUtil.filter(packageFiles, new Condition<JetFile>() {
@Override @Override
public boolean value(JetFile packageFile) { public boolean value(JetFile packageFile) {
@@ -126,4 +126,18 @@ public class PackagePartClassUtils {
return false; return false;
} }
@NotNull
public static List<JetFile> getFilesForFacade(@NotNull FqName facadeFqName, @NotNull Collection<JetFile> 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<JetFile>() {
@Override
public boolean value(JetFile input) {
String sanitizedName = getSanitizedClassNameBase(Files.getNameWithoutExtension(input.getName()));
return expectedSanitizedName.equals(sanitizedName);
}
});
}
} }
@@ -77,7 +77,7 @@ public class IncrementalPackageFragmentProvider(
fqNamesToLoad = ( fqNamesToLoad = (
obsoletePackageParts.map { JvmClassName.byInternalName(it).getPackageFqName() } obsoletePackageParts.map { JvmClassName.byInternalName(it).getPackageFqName() }
+ PackagePartClassUtils.getPackageFilesWithCallables(sourceFiles).map { it.getPackageFqName() } + PackagePartClassUtils.getFilesWithCallables(sourceFiles).map { it.getPackageFqName() }
).toSet() ).toSet()
fqNamesToLoad.forEach { createPackageFragment(it) } fqNamesToLoad.forEach { createPackageFragment(it) }
@@ -32,6 +32,7 @@ import com.intellij.util.containers.SLRUCache;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.load.kotlin.PackageClassUtils; 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.FqName;
import org.jetbrains.kotlin.name.NamePackage; import org.jetbrains.kotlin.name.NamePackage;
import org.jetbrains.kotlin.psi.JetClassOrObject; import org.jetbrains.kotlin.psi.JetClassOrObject;
@@ -117,7 +118,10 @@ public class JavaElementFinder extends PsiElementFinder implements KotlinFinderM
findClassesAndObjects(qualifiedName, scope, answer); 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)); answer.addAll(lightClassGenerationSupport.getPackageClasses(qualifiedName.parent(), scope));
} }
@@ -66,15 +66,15 @@ import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToD
public class KotlinJavaFileStubProvider<T extends WithFileStubAndExtraDiagnostics> implements CachedValueProvider<T> { public class KotlinJavaFileStubProvider<T extends WithFileStubAndExtraDiagnostics> implements CachedValueProvider<T> {
@NotNull @NotNull
public static KotlinJavaFileStubProvider<KotlinPackageLightClassData> createForPackageClass( public static KotlinJavaFileStubProvider<KotlinFacadeLightClassData> createForPackageClass(
@NotNull final Project project, @NotNull final Project project,
@NotNull final FqName packageFqName, @NotNull final FqName packageFqName,
@NotNull final GlobalSearchScope searchScope @NotNull final GlobalSearchScope searchScope
) { ) {
return new KotlinJavaFileStubProvider<KotlinPackageLightClassData>( return new KotlinJavaFileStubProvider<KotlinFacadeLightClassData>(
project, project,
false, false,
new StubGenerationStrategy<KotlinPackageLightClassData>() { new StubGenerationStrategy<KotlinFacadeLightClassData>() {
@NotNull @NotNull
@Override @Override
public LightClassConstructionContext getContext(@NotNull Collection<JetFile> files) { public LightClassConstructionContext getContext(@NotNull Collection<JetFile> files) {
@@ -91,12 +91,12 @@ public class KotlinJavaFileStubProvider<T extends WithFileStubAndExtraDiagnostic
@NotNull @NotNull
@Override @Override
public KotlinPackageLightClassData createLightClassData( public KotlinFacadeLightClassData createLightClassData(
PsiJavaFileStub javaFileStub, PsiJavaFileStub javaFileStub,
BindingContext bindingContext, BindingContext bindingContext,
Diagnostics extraDiagnostics Diagnostics extraDiagnostics
) { ) {
return new KotlinPackageLightClassData(javaFileStub, extraDiagnostics); return new KotlinFacadeLightClassData(javaFileStub, extraDiagnostics);
} }
@NotNull @NotNull
@@ -149,6 +149,83 @@ public class KotlinJavaFileStubProvider<T extends WithFileStubAndExtraDiagnostic
); );
} }
@NotNull
public static CachedValueProvider<KotlinFacadeLightClassData> createForFacadeClass(
@NotNull final Project project,
@NotNull final FqName facadeFqName,
@NotNull final GlobalSearchScope searchScope
) {
return new KotlinJavaFileStubProvider<KotlinFacadeLightClassData>(
project,
false,
new StubGenerationStrategy<KotlinFacadeLightClassData>() {
@NotNull
@Override
public Collection<JetFile> getFiles() {
return LightClassGenerationSupport.getInstance(project).findFilesForFacade(facadeFqName, searchScope);
}
@NotNull
@Override
public FqName getPackageFqName() {
return facadeFqName.parent();
}
@NotNull
@Override
public LightClassConstructionContext getContext(@NotNull Collection<JetFile> 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<JetFile> 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 @NotNull
public static KotlinJavaFileStubProvider<OutermostKotlinClassLightClassData> createForDeclaredClass(@NotNull final JetClassOrObject classOrObject) { public static KotlinJavaFileStubProvider<OutermostKotlinClassLightClassData> createForDeclaredClass(@NotNull final JetClassOrObject classOrObject) {
return new KotlinJavaFileStubProvider<OutermostKotlinClassLightClassData>( return new KotlinJavaFileStubProvider<OutermostKotlinClassLightClassData>(
@@ -33,42 +33,70 @@ import org.jetbrains.annotations.NonNls
import org.jetbrains.kotlin.idea.JetLanguage import org.jetbrains.kotlin.idea.JetLanguage
import org.jetbrains.kotlin.load.kotlin.PackageClassUtils import org.jetbrains.kotlin.load.kotlin.PackageClassUtils
import org.jetbrains.kotlin.name.FqName 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.JetClassOrObject
import org.jetbrains.kotlin.psi.JetFile
import javax.swing.Icon
public class KotlinLightClassForFacade private constructor( public class KotlinLightClassForFacade private constructor(
manager: PsiManager, manager: PsiManager,
private val facadeClassFqName: FqName, private val facadeClassFqName: FqName,
private val searchScope: GlobalSearchScope, private val searchScope: GlobalSearchScope,
private val lightClassDataCache: CachedValue<KotlinFacadeLightClassData>,
files: Collection<JetFile> files: Collection<JetFile>
) : KotlinWrappingLightClass(manager), JetJavaMirrorMarker { ) : KotlinWrappingLightClass(manager), JetJavaMirrorMarker {
public class FileStubCache(private val project: Project) { private data class StubCacheKey(val fqName: FqName, val searchScope: GlobalSearchScope)
private data class Key(val fqName: FqName, val searchScope: GlobalSearchScope)
private inner class CacheData { public class PackageFacadeStubCache(private val project: Project) {
val cache = object : SLRUCache<Key, CachedValue<KotlinPackageLightClassData>>(20, 30) { private inner class PackageFacadeCacheData {
override fun createValue(key: Key): CachedValue<KotlinPackageLightClassData> { val cache = object : SLRUCache<StubCacheKey, CachedValue<KotlinFacadeLightClassData>>(20, 30) {
override fun createValue(key: StubCacheKey): CachedValue<KotlinFacadeLightClassData> {
val stubProvider = KotlinJavaFileStubProvider.createForPackageClass(project, key.fqName, key.searchScope) val stubProvider = KotlinJavaFileStubProvider.createForPackageClass(project, key.fqName, key.searchScope)
return CachedValuesManager.getManager(project).createCachedValue<KotlinPackageLightClassData>(stubProvider, /*trackValue = */false) return CachedValuesManager.getManager(project).createCachedValue<KotlinFacadeLightClassData>(stubProvider, /*trackValue = */false)
} }
} }
} }
private val cachedValue: CachedValue<CacheData> = CachedValuesManager.getManager(project).createCachedValue<CacheData>( private val cachedValue: CachedValue<PackageFacadeCacheData> = CachedValuesManager.getManager(project).createCachedValue<PackageFacadeCacheData>(
{ CachedValueProvider.Result.create<CacheData>(CacheData(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT) }, { CachedValueProvider.Result.create(PackageFacadeCacheData(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT) },
/*trackValue = */ false) /*trackValue = */ false)
public fun get(qualifiedName: FqName, searchScope: GlobalSearchScope): CachedValue<KotlinPackageLightClassData> { public fun get(qualifiedName: FqName, searchScope: GlobalSearchScope): CachedValue<KotlinFacadeLightClassData> {
synchronized (cachedValue) { synchronized (cachedValue) {
return cachedValue.getValue().cache.get(Key(qualifiedName, searchScope)) return cachedValue.getValue().cache.get(StubCacheKey(qualifiedName, searchScope))
} }
} }
companion object { companion object {
public fun getInstance(project: Project): FileStubCache { public fun getInstance(project: Project): PackageFacadeStubCache {
return ServiceManager.getService<FileStubCache>(project, javaClass<FileStubCache>()) return ServiceManager.getService<PackageFacadeStubCache>(project, javaClass<PackageFacadeStubCache>())
}
}
}
public class FacadeStubCache(private val project: Project) {
private inner class FacadeCacheData {
val cache = object : SLRUCache<StubCacheKey, CachedValue<KotlinFacadeLightClassData>>(20, 30) {
override fun createValue(key: StubCacheKey): CachedValue<KotlinFacadeLightClassData> {
val stubProvider = KotlinJavaFileStubProvider.createForFacadeClass(project, key.fqName, key.searchScope)
return CachedValuesManager.getManager(project).createCachedValue<KotlinFacadeLightClassData>(stubProvider, /*trackValue = */false)
}
}
}
private val cachedValue: CachedValue<FacadeCacheData> = CachedValuesManager.getManager(project).createCachedValue<FacadeCacheData>(
{ CachedValueProvider.Result.create(FacadeCacheData(), PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT) },
/*trackValue = */ false)
public fun get(qualifiedName: FqName, searchScope: GlobalSearchScope): CachedValue<KotlinFacadeLightClassData> {
synchronized (cachedValue) {
return cachedValue.getValue().cache.get(StubCacheKey(qualifiedName, searchScope))
}
}
companion object {
public fun getInstance(project: Project): FacadeStubCache {
return ServiceManager.getService<FacadeStubCache>(project, javaClass<FacadeStubCache>())
} }
} }
} }
@@ -81,9 +109,6 @@ public class KotlinLightClassForFacade private constructor(
private val packageFqName: FqName = private val packageFqName: FqName =
facadeClassFqName.parent() facadeClassFqName.parent()
private val lightClassDataCache: CachedValue<KotlinPackageLightClassData> =
FileStubCache.getInstance(getProject()).get(packageFqName, searchScope)
private val modifierList: PsiModifierList = private val modifierList: PsiModifierList =
LightModifierList(manager, JetLanguage.INSTANCE, PsiModifier.PUBLIC, PsiModifier.FINAL) 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 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 { override fun getDelegate(): PsiClass {
val psiClass = LightClassUtil.findClass(facadeClassFqName, lightClassDataCache.getValue().javaFileStub) 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" } assert(files.isNotEmpty()) { "No files for package $packageFqName" }
val packageClassFqName = PackageClassUtils.getPackageClassFqName(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<JetFile>
): 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)
} }
} }
} }
@@ -73,6 +73,17 @@ public abstract class LightClassGenerationSupport {
@NotNull @NotNull
public abstract Collection<PsiClass> getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope); public abstract Collection<PsiClass> getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope);
@Nullable @Nullable
public abstract ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject); public abstract ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject);
@NotNull
public abstract Collection<PsiClass> getFacadeClasses(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope);
@NotNull
public abstract Collection<JetFile> findFilesForFacade(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope);
@NotNull
public abstract LightClassConstructionContext getContextForFacade(@NotNull Collection<JetFile> files);
} }
@@ -33,7 +33,7 @@ interface LightClassDataForKotlinClass: LightClassData {
val jvmQualifiedName: FqName val jvmQualifiedName: FqName
} }
data class KotlinPackageLightClassData( data class KotlinFacadeLightClassData(
override val javaFileStub: PsiJavaFileStub, override val javaFileStub: PsiJavaFileStub,
override val extraDiagnostics: Diagnostics override val extraDiagnostics: Diagnostics
): LightClassData, WithFileStubAndExtraDiagnostics ): LightClassData, WithFileStubAndExtraDiagnostics
@@ -31,7 +31,7 @@ import org.jetbrains.kotlin.psi.*
public fun getJvmSignatureDiagnostics(element: PsiElement, otherDiagnostics: Diagnostics, moduleScope: GlobalSearchScope): Diagnostics? { public fun getJvmSignatureDiagnostics(element: PsiElement, otherDiagnostics: Diagnostics, moduleScope: GlobalSearchScope): Diagnostics? {
fun getDiagnosticsForPackage(file: JetFile): Diagnostics? { fun getDiagnosticsForPackage(file: JetFile): Diagnostics? {
val project = file.getProject() val project = file.getProject()
val cache = KotlinLightClassForFacade.FileStubCache.getInstance(project) val cache = KotlinLightClassForFacade.PackageFacadeStubCache.getInstance(project)
return cache[file.getPackageFqName(), moduleScope].getValue()?.extraDiagnostics return cache[file.getPackageFqName(), moduleScope].getValue()?.extraDiagnostics
} }
@@ -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 */ }
}
@@ -0,0 +1,3 @@
// SingleFileKt
public fun foo() {}
@@ -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") @TestMetadata("compiler/testData/asJava/lightClasses/nullabilityAnnotations")
@TestDataPath("$PROJECT_ROOT") @TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class) @RunWith(JUnit3RunnerWithInners.class)
@@ -84,6 +84,11 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
public LightClassConstructionContext getContextForPackage(@NotNull Collection<JetFile> files) { public LightClassConstructionContext getContextForPackage(@NotNull Collection<JetFile> files) {
assert !files.isEmpty() : "No files in package"; assert !files.isEmpty() : "No files in package";
return getContextForFiles(files);
}
@NotNull
private LightClassConstructionContext getContextForFiles(@NotNull Collection<JetFile> files) {
List<JetFile> sortedFiles = new ArrayList<JetFile>(files); List<JetFile> sortedFiles = new ArrayList<JetFile>(files);
Collections.sort(sortedFiles, scopeFileComparator); Collections.sort(sortedFiles, scopeFileComparator);
@@ -118,6 +123,14 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
return new LightClassConstructionContext(bindingContext, moduleDescriptor); return new LightClassConstructionContext(bindingContext, moduleDescriptor);
} }
@NotNull
@Override
public LightClassConstructionContext getContextForFacade(@NotNull Collection<JetFile> files) {
assert !files.isEmpty() : "No files in facade";
return getContextForFiles(files);
}
private static void forceResolvePackageDeclarations(@NotNull Collection<JetFile> files, @NotNull KotlinCodeAnalyzer session) { private static void forceResolvePackageDeclarations(@NotNull Collection<JetFile> files, @NotNull KotlinCodeAnalyzer session) {
for (JetFile file : files) { for (JetFile file : files) {
// SCRIPT: not supported // SCRIPT: not supported
@@ -272,9 +285,9 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
@Override @Override
public Collection<PsiClass> getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope) { public Collection<PsiClass> getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope) {
List<PsiClass> result = new ArrayList<PsiClass>(); List<PsiClass> result = new ArrayList<PsiClass>();
List<KotlinLightPackageClassInfo> packageClassesInfos = findPackageClassesInfos(packageFqName, scope); List<KotlinLightFacadeClassInfo> packageClassesInfos = findPackageClassesInfos(packageFqName, scope);
for (KotlinLightPackageClassInfo info : packageClassesInfos) { for (KotlinLightFacadeClassInfo info : packageClassesInfos) {
List<JetFile> files = PackagePartClassUtils.getPackageFilesWithCallables(info.getFiles()); List<JetFile> files = PackagePartClassUtils.getFilesWithCallables(info.getFiles());
if (files.isEmpty()) continue; if (files.isEmpty()) continue;
IdeaModuleInfo moduleInfo = info.getModuleInfo(); IdeaModuleInfo moduleInfo = info.getModuleInfo();
@@ -292,7 +305,7 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
} }
} }
else { else {
PsiClass clsClass = getLightClassForDecompiledPackage(packageFqName, files); PsiClass clsClass = getLightClassForDecompiledFacade(packageFqName, files);
if (clsClass != null) { if (clsClass != null) {
result.add(clsClass); result.add(clsClass);
} }
@@ -301,6 +314,60 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
return result; return result;
} }
@NotNull
@Override
public Collection<PsiClass> getFacadeClasses(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) {
List<PsiClass> result = new ArrayList<PsiClass>();
List<KotlinLightFacadeClassInfo> facadeClassesInfos = findFacadeClassesInfos(facadeFqName, scope);
for (KotlinLightFacadeClassInfo info : facadeClassesInfos) {
List<JetFile> 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<JetFile> findFilesForFacade(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) {
// TODO naive version. Production variant should use an equivalent of JetExactPackageIndex.
Collection<JetFile> packageFiles = findFilesForPackage(facadeFqName.parent(), scope);
return PackagePartClassUtils.getFilesForFacade(facadeFqName, packageFiles);
}
@NotNull
private List<KotlinLightFacadeClassInfo> findFacadeClassesInfos(FqName facadeFqName, GlobalSearchScope scope) {
// TODO naive version. Production variant should use an equivalent of JetExactPackageIndex.
Collection<JetFile> packageFiles = findFilesForPackage(facadeFqName.parent(), scope);
List<JetFile> facadeFiles = PackagePartClassUtils.getFilesForFacade(facadeFqName, packageFiles);
Map<IdeaModuleInfo, List<JetFile>> filesByInfo = groupByModuleInfo(facadeFiles);
List<KotlinLightFacadeClassInfo> result = new ArrayList<KotlinLightFacadeClassInfo>();
for (Map.Entry<IdeaModuleInfo, List<JetFile>> entry : filesByInfo.entrySet()) {
result.add(new KotlinLightFacadeClassInfo(entry.getValue(), entry.getKey()));
}
return result;
}
@Nullable @Nullable
@Override @Override
public ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject) { public ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject) {
@@ -313,11 +380,11 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
} }
@Nullable @Nullable
private static PsiClass getLightClassForDecompiledPackage(@NotNull FqName packageFqName, @NotNull List<JetFile> filesWithCallables) { private static PsiClass getLightClassForDecompiledFacade(@NotNull FqName facadeFqName, @NotNull List<JetFile> filesWithCallables) {
JetFile firstFile = filesWithCallables.iterator().next(); JetFile firstFile = filesWithCallables.iterator().next();
if (firstFile.isCompiled()) { if (firstFile.isCompiled()) {
if (filesWithCallables.size() > 1) { 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); return createLightClassForDecompiledKotlinFile(firstFile);
} }
@@ -325,23 +392,23 @@ public class IDELightClassGenerationSupport extends LightClassGenerationSupport
} }
@NotNull @NotNull
private List<KotlinLightPackageClassInfo> findPackageClassesInfos( private List<KotlinLightFacadeClassInfo> findPackageClassesInfos(
@NotNull FqName fqName, @NotNull GlobalSearchScope wholeScope @NotNull FqName fqName, @NotNull GlobalSearchScope wholeScope
) { ) {
Collection<JetFile> allFiles = findFilesForPackage(fqName, wholeScope); Collection<JetFile> allFiles = findFilesForPackage(fqName, wholeScope);
Map<IdeaModuleInfo, List<JetFile>> filesByInfo = groupByModuleInfo(allFiles); Map<IdeaModuleInfo, List<JetFile>> filesByInfo = groupByModuleInfo(allFiles);
List<KotlinLightPackageClassInfo> result = new ArrayList<KotlinLightPackageClassInfo>(); List<KotlinLightFacadeClassInfo> result = new ArrayList<KotlinLightFacadeClassInfo>();
for (Map.Entry<IdeaModuleInfo, List<JetFile>> entry : filesByInfo.entrySet()) { for (Map.Entry<IdeaModuleInfo, List<JetFile>> entry : filesByInfo.entrySet()) {
result.add(new KotlinLightPackageClassInfo(entry.getValue(), entry.getKey())); result.add(new KotlinLightFacadeClassInfo(entry.getValue(), entry.getKey()));
} }
return result; return result;
} }
private static final class KotlinLightPackageClassInfo { private static final class KotlinLightFacadeClassInfo {
private final Collection<JetFile> files; private final Collection<JetFile> files;
private final IdeaModuleInfo moduleInfo; private final IdeaModuleInfo moduleInfo;
public KotlinLightPackageClassInfo(@NotNull Collection<JetFile> files, @NotNull IdeaModuleInfo moduleInfo) { public KotlinLightFacadeClassInfo(@NotNull Collection<JetFile> files, @NotNull IdeaModuleInfo moduleInfo) {
this.files = files; this.files = files;
this.moduleInfo = moduleInfo; this.moduleInfo = moduleInfo;
} }
+5 -2
View File
@@ -206,8 +206,11 @@
<projectService serviceImplementation="org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerWorkspaceSettings"/> <projectService serviceImplementation="org.jetbrains.kotlin.idea.compiler.configuration.KotlinCompilerWorkspaceSettings"/>
<projectService serviceInterface="org.jetbrains.kotlin.asJava.KotlinLightClassForFacade$FileStubCache" <projectService serviceInterface="org.jetbrains.kotlin.asJava.KotlinLightClassForFacade$PackageFacadeStubCache"
serviceImplementation="org.jetbrains.kotlin.asJava.KotlinLightClassForFacade$FileStubCache"/> serviceImplementation="org.jetbrains.kotlin.asJava.KotlinLightClassForFacade$PackageFacadeStubCache"/>
<projectService serviceInterface="org.jetbrains.kotlin.asJava.KotlinLightClassForFacade$FacadeStubCache"
serviceImplementation="org.jetbrains.kotlin.asJava.KotlinLightClassForFacade$FacadeStubCache"/>
<projectService serviceInterface="org.jetbrains.kotlin.idea.debugger.evaluate.KotlinEvaluateExpressionCache" <projectService serviceInterface="org.jetbrains.kotlin.idea.debugger.evaluate.KotlinEvaluateExpressionCache"
serviceImplementation="org.jetbrains.kotlin.idea.debugger.evaluate.KotlinEvaluateExpressionCache"/> serviceImplementation="org.jetbrains.kotlin.idea.debugger.evaluate.KotlinEvaluateExpressionCache"/>
+1 -1
View File
@@ -34,7 +34,7 @@ class MyClass
// RESULT: 2: I // RESULT: 2: I
frame = main():9, FrameSimpleKt {frameSimple} frame = main():9, FrameSimpleKt {frameSimple}
static = static = frameSimple.FrameSimpleKt 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 = args: java.lang.String[] = {java.lang.String[0]@uniqueID} (sp = frameSimple.kt, 5)
local = val1: int = 1 (sp = frameSimple.kt, 6) local = val1: int = 1 (sp = frameSimple.kt, 6)
local = val2: frameSimple.MyClass = {frameSimple.MyClass@uniqueID} (sp = frameSimple.kt, 7) local = val2: frameSimple.MyClass = {frameSimple.MyClass@uniqueID} (sp = frameSimple.kt, 7)