J2K CliLightClassGenerationSupport: autoconvert
This commit is contained in:
+142
-190
@@ -14,285 +14,237 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler;
|
||||
package org.jetbrains.kotlin.cli.jvm.compiler
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.PsiSearchScopeUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.SmartList;
|
||||
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.asJava.KotlinLightClassForExplicitDeclaration;
|
||||
import org.jetbrains.kotlin.asJava.KotlinLightClassForFacade;
|
||||
import org.jetbrains.kotlin.asJava.LightClassConstructionContext;
|
||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport;
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor;
|
||||
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.psi.*;
|
||||
import org.jetbrains.kotlin.resolve.*;
|
||||
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer;
|
||||
import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils;
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter;
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScope;
|
||||
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice;
|
||||
import org.jetbrains.kotlin.util.slicedMap.WritableSlice;
|
||||
import org.jetbrains.kotlin.utils.UtilsPackage;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import com.google.common.base.Predicate
|
||||
import com.google.common.collect.Collections2
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiClass
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiManager
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.psi.search.PsiSearchScopeUtil
|
||||
import com.intellij.util.Function
|
||||
import com.intellij.util.SmartList
|
||||
import com.intellij.util.containers.ContainerUtil
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.asJava.KotlinLightClassForExplicitDeclaration
|
||||
import org.jetbrains.kotlin.asJava.KotlinLightClassForFacade
|
||||
import org.jetbrains.kotlin.asJava.LightClassConstructionContext
|
||||
import org.jetbrains.kotlin.asJava.LightClassGenerationSupport
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PackageViewDescriptor
|
||||
import org.jetbrains.kotlin.load.kotlin.PackagePartClassUtils
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.lazy.KotlinCodeAnalyzer
|
||||
import org.jetbrains.kotlin.resolve.lazy.ResolveSessionUtils
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
|
||||
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
|
||||
import org.jetbrains.kotlin.utils.*
|
||||
import java.util.Collections
|
||||
|
||||
/**
|
||||
* This class solves the problem of interdependency between analyzing Kotlin code and generating JetLightClasses
|
||||
*
|
||||
|
||||
* Consider the following example:
|
||||
*
|
||||
|
||||
* KClass.kt refers to JClass.java and vice versa
|
||||
*
|
||||
|
||||
* To analyze KClass.kt we need to load descriptors from JClass.java, and to do that we need a JetLightClass instance for KClass,
|
||||
* which can only be constructed when the structure of KClass is known.
|
||||
*
|
||||
|
||||
* To mitigate this, CliLightClassGenerationSupport hold a trace that is shared between the analyzer and JetLightClasses
|
||||
*/
|
||||
public class CliLightClassGenerationSupport extends LightClassGenerationSupport implements CodeAnalyzerInitializer {
|
||||
private final PsiManager psiManager;
|
||||
private BindingContext bindingContext = null;
|
||||
private ModuleDescriptor module = null;
|
||||
public class CliLightClassGenerationSupport(project: Project) : LightClassGenerationSupport(), CodeAnalyzerInitializer {
|
||||
private val psiManager: PsiManager
|
||||
private var bindingContext: BindingContext? = null
|
||||
private var module: ModuleDescriptor? = null
|
||||
|
||||
public CliLightClassGenerationSupport(@NotNull Project project) {
|
||||
this.psiManager = PsiManager.getInstance(project);
|
||||
init {
|
||||
this.psiManager = PsiManager.getInstance(project)
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(@NotNull BindingTrace trace, @NotNull ModuleDescriptor module, @Nullable KotlinCodeAnalyzer analyzer) {
|
||||
this.bindingContext = trace.getBindingContext();
|
||||
this.module = module;
|
||||
override fun initialize(trace: BindingTrace, module: ModuleDescriptor, analyzer: KotlinCodeAnalyzer?) {
|
||||
this.bindingContext = trace.bindingContext
|
||||
this.module = module
|
||||
|
||||
if (!(trace instanceof CliBindingTrace)) {
|
||||
throw new IllegalArgumentException("Shared trace is expected to be subclass of " + CliBindingTrace.class.getSimpleName() + " class");
|
||||
if (trace !is CliBindingTrace) {
|
||||
throw IllegalArgumentException("Shared trace is expected to be subclass of " + javaClass<CliBindingTrace>().simpleName + " class")
|
||||
}
|
||||
|
||||
((CliBindingTrace) trace).setKotlinCodeAnalyzer(analyzer);
|
||||
trace.setKotlinCodeAnalyzer(analyzer)
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private BindingContext getBindingContext() {
|
||||
assert bindingContext != null : "Call initialize() first";
|
||||
return bindingContext;
|
||||
private fun getBindingContext(): BindingContext {
|
||||
assert(bindingContext != null, "Call initialize() first")
|
||||
return bindingContext
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ModuleDescriptor getModule() {
|
||||
assert module != null : "Call initialize() first";
|
||||
return module;
|
||||
private fun getModule(): ModuleDescriptor {
|
||||
assert(module != null, "Call initialize() first")
|
||||
return module
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LightClassConstructionContext getContextForPackage(@NotNull Collection<JetFile> files) {
|
||||
return getContext();
|
||||
override fun getContextForPackage(files: Collection<JetFile>): LightClassConstructionContext {
|
||||
return getContext()
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LightClassConstructionContext getContextForClassOrObject(@NotNull JetClassOrObject classOrObject) {
|
||||
return getContext();
|
||||
override fun getContextForClassOrObject(classOrObject: JetClassOrObject): LightClassConstructionContext {
|
||||
return getContext()
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private LightClassConstructionContext getContext() {
|
||||
return new LightClassConstructionContext(bindingContext, getModule());
|
||||
private fun getContext(): LightClassConstructionContext {
|
||||
return LightClassConstructionContext(bindingContext, getModule())
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<JetClassOrObject> findClassOrObjectDeclarations(@NotNull FqName fqName, @NotNull final GlobalSearchScope searchScope) {
|
||||
Collection<ClassDescriptor> classDescriptors = ResolveSessionUtils.getClassDescriptorsByFqName(getModule(), fqName);
|
||||
override fun findClassOrObjectDeclarations(fqName: FqName, searchScope: GlobalSearchScope): Collection<JetClassOrObject> {
|
||||
val classDescriptors = ResolveSessionUtils.getClassDescriptorsByFqName(getModule(), fqName)
|
||||
|
||||
return ContainerUtil.mapNotNull(classDescriptors, new Function<ClassDescriptor, JetClassOrObject>() {
|
||||
@Override
|
||||
public JetClassOrObject fun(ClassDescriptor descriptor) {
|
||||
PsiElement element = DescriptorToSourceUtils.getSourceFromDescriptor(descriptor);
|
||||
if (element instanceof JetClassOrObject && PsiSearchScopeUtil.isInScope(searchScope, element)) {
|
||||
return (JetClassOrObject) element;
|
||||
return ContainerUtil.mapNotNull(classDescriptors, object : Function<ClassDescriptor, JetClassOrObject> {
|
||||
override fun `fun`(descriptor: ClassDescriptor): JetClassOrObject? {
|
||||
val element = DescriptorToSourceUtils.getSourceFromDescriptor(descriptor)
|
||||
if (element is JetClassOrObject && PsiSearchScopeUtil.isInScope(searchScope, element)) {
|
||||
return element
|
||||
}
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<JetFile> findFilesForPackage(@NotNull FqName fqName, @NotNull final GlobalSearchScope searchScope) {
|
||||
Collection<JetFile> files = getBindingContext().get(BindingContext.PACKAGE_TO_FILES, fqName);
|
||||
override fun findFilesForPackage(fqName: FqName, searchScope: GlobalSearchScope): Collection<JetFile> {
|
||||
val files = getBindingContext().get(BindingContext.PACKAGE_TO_FILES, fqName)
|
||||
if (files != null) {
|
||||
return Collections2.filter(files, new Predicate<JetFile>() {
|
||||
@Override
|
||||
public boolean apply(JetFile input) {
|
||||
return PsiSearchScopeUtil.isInScope(searchScope, input);
|
||||
return Collections2.filter(files, object : Predicate<JetFile> {
|
||||
override fun apply(input: JetFile?): Boolean {
|
||||
return PsiSearchScopeUtil.isInScope(searchScope, input)
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
return Collections.emptyList();
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<JetClassOrObject> findClassOrObjectDeclarationsInPackage(
|
||||
@NotNull FqName packageFqName, @NotNull GlobalSearchScope searchScope
|
||||
) {
|
||||
Collection<JetFile> files = findFilesForPackage(packageFqName, searchScope);
|
||||
List<JetClassOrObject> result = new SmartList<JetClassOrObject>();
|
||||
for (JetFile file : files) {
|
||||
for (JetDeclaration declaration : file.getDeclarations()) {
|
||||
if (declaration instanceof JetClassOrObject) {
|
||||
result.add((JetClassOrObject) declaration);
|
||||
override fun findClassOrObjectDeclarationsInPackage(
|
||||
packageFqName: FqName, searchScope: GlobalSearchScope): Collection<JetClassOrObject> {
|
||||
val files = findFilesForPackage(packageFqName, searchScope)
|
||||
val result = SmartList<JetClassOrObject>()
|
||||
for (file in files) {
|
||||
for (declaration in file.declarations) {
|
||||
if (declaration is JetClassOrObject) {
|
||||
result.add(declaration)
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean packageExists(@NotNull FqName fqName, @NotNull GlobalSearchScope scope) {
|
||||
return !getModule().getPackage(fqName).isEmpty();
|
||||
override fun packageExists(fqName: FqName, scope: GlobalSearchScope): Boolean {
|
||||
return !getModule().getPackage(fqName).isEmpty()
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<FqName> getSubPackages(@NotNull FqName fqn, @NotNull GlobalSearchScope scope) {
|
||||
PackageViewDescriptor packageView = getModule().getPackage(fqn);
|
||||
Collection<DeclarationDescriptor> members = packageView.getMemberScope().getDescriptors(DescriptorKindFilter.PACKAGES, JetScope.ALL_NAME_FILTER);
|
||||
return ContainerUtil.mapNotNull(members, new Function<DeclarationDescriptor, FqName>() {
|
||||
@Override
|
||||
public FqName fun(DeclarationDescriptor member) {
|
||||
if (member instanceof PackageViewDescriptor) {
|
||||
return ((PackageViewDescriptor) member).getFqName();
|
||||
override fun getSubPackages(fqn: FqName, scope: GlobalSearchScope): Collection<FqName> {
|
||||
val packageView = getModule().getPackage(fqn)
|
||||
val members = packageView.memberScope.getDescriptors(DescriptorKindFilter.PACKAGES, JetScope.ALL_NAME_FILTER)
|
||||
return ContainerUtil.mapNotNull(members, object : Function<DeclarationDescriptor, FqName> {
|
||||
override fun `fun`(member: DeclarationDescriptor): FqName? {
|
||||
if (member is PackageViewDescriptor) {
|
||||
return member.fqName
|
||||
}
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public PsiClass getPsiClass(@NotNull JetClassOrObject classOrObject) {
|
||||
return KotlinLightClassForExplicitDeclaration.Companion.create(psiManager, classOrObject);
|
||||
override fun getPsiClass(classOrObject: JetClassOrObject): PsiClass? {
|
||||
return KotlinLightClassForExplicitDeclaration.create(psiManager, classOrObject)
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<PsiClass> getPackageClasses(@NotNull FqName packageFqName, @NotNull GlobalSearchScope scope) {
|
||||
Collection<JetFile> filesInPackage = findFilesForPackage(packageFqName, scope);
|
||||
override fun getPackageClasses(packageFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
|
||||
val filesInPackage = findFilesForPackage(packageFqName, scope)
|
||||
|
||||
List<JetFile> filesWithCallables = PackagePartClassUtils.getFilesWithCallables(filesInPackage);
|
||||
if (filesWithCallables.isEmpty()) return Collections.emptyList();
|
||||
val filesWithCallables = PackagePartClassUtils.getFilesWithCallables(filesInPackage)
|
||||
if (filesWithCallables.isEmpty()) return emptyList()
|
||||
|
||||
//noinspection RedundantTypeArguments
|
||||
return UtilsPackage.<PsiClass>emptyOrSingletonList(
|
||||
KotlinLightClassForFacade.Factory.createForPackageFacade(psiManager, packageFqName, scope, filesWithCallables));
|
||||
return emptyOrSingletonList<PsiClass>(
|
||||
KotlinLightClassForFacade.createForPackageFacade(psiManager, packageFqName, scope, filesWithCallables))
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ClassDescriptor resolveClassToDescriptor(@NotNull JetClassOrObject classOrObject) {
|
||||
return bindingContext.get(BindingContext.CLASS, classOrObject);
|
||||
override fun resolveClassToDescriptor(classOrObject: JetClassOrObject): ClassDescriptor? {
|
||||
return bindingContext!!.get(BindingContext.CLASS, classOrObject)
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<PsiClass> getFacadeClasses(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) {
|
||||
Collection<JetFile> filesForFacade = findFilesForFacade(facadeFqName, scope);
|
||||
if (filesForFacade.isEmpty()) return Collections.emptyList();
|
||||
override fun getFacadeClasses(facadeFqName: FqName, scope: GlobalSearchScope): Collection<PsiClass> {
|
||||
val filesForFacade = findFilesForFacade(facadeFqName, scope)
|
||||
if (filesForFacade.isEmpty()) return emptyList()
|
||||
|
||||
//noinspection RedundantTypeArguments
|
||||
return UtilsPackage.<PsiClass>emptyOrSingletonList(
|
||||
KotlinLightClassForFacade.Factory.createForFacade(psiManager, facadeFqName, scope, filesForFacade));
|
||||
return emptyOrSingletonList<PsiClass>(
|
||||
KotlinLightClassForFacade.createForFacade(psiManager, facadeFqName, scope, filesForFacade))
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Collection<JetFile> findFilesForFacade(@NotNull FqName facadeFqName, @NotNull GlobalSearchScope scope) {
|
||||
override fun findFilesForFacade(facadeFqName: FqName, scope: GlobalSearchScope): Collection<JetFile> {
|
||||
// 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<JetFile> filesInPackage = findFilesForPackage(facadeFqName.parent(), scope);
|
||||
return PackagePartClassUtils.getFilesForPart(facadeFqName, filesInPackage);
|
||||
val filesInPackage = findFilesForPackage(facadeFqName.parent(), scope)
|
||||
return PackagePartClassUtils.getFilesForPart(facadeFqName, filesInPackage)
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public LightClassConstructionContext getContextForFacade(@NotNull Collection<JetFile> files) {
|
||||
return getContext();
|
||||
override fun getContextForFacade(files: Collection<JetFile>): LightClassConstructionContext {
|
||||
return getContext()
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public BindingTraceContext createTrace() {
|
||||
return new NoScopeRecordCliBindingTrace();
|
||||
override fun createTrace(): BindingTraceContext {
|
||||
return NoScopeRecordCliBindingTrace()
|
||||
}
|
||||
|
||||
public static class NoScopeRecordCliBindingTrace extends CliBindingTrace {
|
||||
@Override
|
||||
public <K, V> void record(WritableSlice<K, V> slice, K key, V value) {
|
||||
if (slice == BindingContext.RESOLUTION_SCOPE || slice == BindingContext.TYPE_RESOLUTION_SCOPE
|
||||
|| slice == BindingContext.LEXICAL_SCOPE) {
|
||||
public class NoScopeRecordCliBindingTrace : CliBindingTrace() {
|
||||
override fun <K, V> record(slice: WritableSlice<K, V>, key: K, value: V) {
|
||||
if (slice === BindingContext.RESOLUTION_SCOPE || slice === BindingContext.TYPE_RESOLUTION_SCOPE || slice === BindingContext.LEXICAL_SCOPE) {
|
||||
// In the compiler there's no need to keep scopes
|
||||
return;
|
||||
return
|
||||
}
|
||||
super.record(slice, key, value);
|
||||
super.record(slice, key, value)
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return NoScopeRecordCliBindingTrace.class.getName();
|
||||
override fun toString(): String {
|
||||
return javaClass<NoScopeRecordCliBindingTrace>().name
|
||||
}
|
||||
}
|
||||
|
||||
public static class CliBindingTrace extends BindingTraceContext {
|
||||
private KotlinCodeAnalyzer kotlinCodeAnalyzer;
|
||||
public open class CliBindingTrace
|
||||
TestOnly
|
||||
constructor() : BindingTraceContext() {
|
||||
private var kotlinCodeAnalyzer: KotlinCodeAnalyzer? = null
|
||||
|
||||
@TestOnly
|
||||
public CliBindingTrace() {
|
||||
override fun toString(): String {
|
||||
return javaClass<CliBindingTrace>().name
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return CliBindingTrace.class.getName();
|
||||
public fun setKotlinCodeAnalyzer(kotlinCodeAnalyzer: KotlinCodeAnalyzer) {
|
||||
this.kotlinCodeAnalyzer = kotlinCodeAnalyzer
|
||||
}
|
||||
|
||||
public void setKotlinCodeAnalyzer(KotlinCodeAnalyzer kotlinCodeAnalyzer) {
|
||||
this.kotlinCodeAnalyzer = kotlinCodeAnalyzer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public <K, V> V get(ReadOnlySlice<K, V> slice, K key) {
|
||||
V value = super.get(slice, key);
|
||||
override fun <K, V> get(slice: ReadOnlySlice<K, V>, key: K): V? {
|
||||
val value = super.get(slice, key)
|
||||
|
||||
if (value == null) {
|
||||
if (BindingContext.FUNCTION == slice || BindingContext.VARIABLE == slice) {
|
||||
if (key instanceof JetDeclaration) {
|
||||
JetDeclaration jetDeclaration = (JetDeclaration) key;
|
||||
if (!JetPsiUtil.isLocal(jetDeclaration)) {
|
||||
kotlinCodeAnalyzer.resolveToDescriptor(jetDeclaration);
|
||||
return super.get(slice, key);
|
||||
if (BindingContext.FUNCTION === slice || BindingContext.VARIABLE === slice) {
|
||||
if (key is JetDeclaration) {
|
||||
if (!JetPsiUtil.isLocal(key)) {
|
||||
kotlinCodeAnalyzer!!.resolveToDescriptor(key)
|
||||
return super.get(slice, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
return value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user