J2K KotlinJavaFileStubProvider: autoconvert and minor changes
This commit is contained in:
+322
-412
@@ -14,450 +14,360 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.jetbrains.kotlin.asJava;
|
package org.jetbrains.kotlin.asJava
|
||||||
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists
|
||||||
import com.intellij.openapi.diagnostic.Logger;
|
import com.intellij.openapi.diagnostic.Logger
|
||||||
import com.intellij.openapi.progress.ProcessCanceledException;
|
import com.intellij.openapi.progress.ProcessCanceledException
|
||||||
import com.intellij.openapi.project.Project;
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.openapi.util.SystemInfo;
|
import com.intellij.openapi.util.SystemInfo
|
||||||
import com.intellij.openapi.vfs.VirtualFile;
|
import com.intellij.openapi.vfs.VirtualFile
|
||||||
import com.intellij.psi.ClassFileViewProvider;
|
import com.intellij.psi.ClassFileViewProvider
|
||||||
import com.intellij.psi.PsiElement;
|
import com.intellij.psi.PsiElement
|
||||||
import com.intellij.psi.PsiFile;
|
import com.intellij.psi.PsiFile
|
||||||
import com.intellij.psi.PsiManager;
|
import com.intellij.psi.PsiManager
|
||||||
import com.intellij.psi.impl.compiled.ClsFileImpl;
|
import com.intellij.psi.impl.compiled.ClsFileImpl
|
||||||
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub;
|
import com.intellij.psi.impl.java.stubs.PsiJavaFileStub
|
||||||
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl;
|
import com.intellij.psi.impl.java.stubs.impl.PsiJavaFileStubImpl
|
||||||
import com.intellij.psi.search.GlobalSearchScope;
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.psi.stubs.PsiClassHolderFileStub;
|
import com.intellij.psi.stubs.PsiClassHolderFileStub
|
||||||
import com.intellij.psi.stubs.StubElement;
|
import com.intellij.psi.stubs.StubElement
|
||||||
import com.intellij.psi.util.CachedValueProvider;
|
import com.intellij.psi.util.CachedValueProvider
|
||||||
import com.intellij.psi.util.PsiModificationTracker;
|
import com.intellij.psi.util.PsiModificationTracker
|
||||||
import com.intellij.psi.util.PsiTreeUtil;
|
import com.intellij.psi.util.PsiTreeUtil
|
||||||
import com.intellij.util.containers.ContainerUtil;
|
import com.intellij.util.containers.ContainerUtil
|
||||||
import com.intellij.util.containers.Stack;
|
import com.intellij.util.containers.Stack
|
||||||
import kotlin.jvm.functions.Function0;
|
import org.jetbrains.kotlin.codegen.CompilationErrorHandler
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
|
||||||
import org.jetbrains.kotlin.codegen.CompilationErrorHandler;
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.codegen.KotlinCodegenFacade;
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.codegen.MultifileClassCodegen;
|
import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider
|
||||||
import org.jetbrains.kotlin.codegen.PackageCodegen;
|
import org.jetbrains.kotlin.fileClasses.getFileClassType
|
||||||
import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.codegen.context.PackageContext;
|
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||||
import org.jetbrains.kotlin.codegen.state.GenerationState;
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor;
|
import org.jetbrains.kotlin.psi.KtPsiUtil
|
||||||
import org.jetbrains.kotlin.fileClasses.FileClasses;
|
import org.jetbrains.kotlin.psi.KtScript
|
||||||
import org.jetbrains.kotlin.fileClasses.JvmFileClassInfo;
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.fileClasses.NoResolveFileClassesProvider;
|
import org.jetbrains.kotlin.resolve.BindingTraceContext
|
||||||
import org.jetbrains.kotlin.name.FqName;
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration
|
||||||
import org.jetbrains.kotlin.psi.KtClassOrObject;
|
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||||
import org.jetbrains.kotlin.psi.KtFile;
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
import org.jetbrains.kotlin.psi.KtPsiUtil;
|
|
||||||
import org.jetbrains.kotlin.psi.KtScript;
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext;
|
|
||||||
import org.jetbrains.kotlin.resolve.BindingTraceContext;
|
|
||||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics;
|
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName;
|
|
||||||
import org.jetbrains.org.objectweb.asm.Type;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
class KotlinJavaFileStubProvider<T : WithFileStubAndExtraDiagnostics> private constructor(
|
||||||
import java.util.Collections;
|
private val project: Project,
|
||||||
import java.util.Map;
|
private val local: Boolean,
|
||||||
|
private val stubGenerationStrategy: StubGenerationStrategy<T>) : CachedValueProvider<T> {
|
||||||
|
|
||||||
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
|
override fun compute(): CachedValueProvider.Result<T>? {
|
||||||
|
val packageFqName = stubGenerationStrategy.packageFqName
|
||||||
|
val files = stubGenerationStrategy.files
|
||||||
|
|
||||||
public class KotlinJavaFileStubProvider<T extends WithFileStubAndExtraDiagnostics> implements CachedValueProvider<T> {
|
checkForBuiltIns(packageFqName, files)
|
||||||
|
|
||||||
@NotNull
|
val context = stubGenerationStrategy.getContext(files)
|
||||||
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<KtFile> getFiles() {
|
|
||||||
return LightClassGenerationSupport.getInstance(project).findFilesForFacade(facadeFqName, searchScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
val javaFileStub = createJavaFileStub(packageFqName, files)
|
||||||
@Override
|
val bindingContext: BindingContext
|
||||||
public FqName getPackageFqName() {
|
val forExtraDiagnostics = BindingTraceContext()
|
||||||
return facadeFqName.parent();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public LightClassConstructionContext getContext(@NotNull Collection<KtFile> 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(KtClassOrObject classOrObject) {
|
|
||||||
return shouldGenerateClass(classOrObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldGenerateClass(KtClassOrObject classOrObject) {
|
|
||||||
return KtPsiUtil.isLocal(classOrObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldGeneratePackagePart(KtFile jetFile) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldGenerateScript(KtScript script) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void generate(@NotNull GenerationState state, @NotNull Collection<KtFile> files) {
|
|
||||||
if (!files.isEmpty()) {
|
|
||||||
KtFile representativeFile = files.iterator().next();
|
|
||||||
JvmFileClassInfo fileClassInfo = NoResolveFileClassesProvider.INSTANCE.getFileClassInfo(representativeFile);
|
|
||||||
if (!fileClassInfo.getWithJvmMultifileClass()) {
|
|
||||||
PackageCodegen codegen = state.getFactory().forPackage(representativeFile.getPackageFqName(), files);
|
|
||||||
codegen.generate(CompilationErrorHandler.THROW_EXCEPTION);
|
|
||||||
state.getFactory().asList();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MultifileClassCodegen codegen = state.getFactory().forMultifileClass(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<OutermostKotlinClassLightClassData> createForDeclaredClass(@NotNull final KtClassOrObject classOrObject) {
|
|
||||||
return new KotlinJavaFileStubProvider<OutermostKotlinClassLightClassData>(
|
|
||||||
classOrObject.getProject(),
|
|
||||||
classOrObject.isLocal(),
|
|
||||||
new StubGenerationStrategy<OutermostKotlinClassLightClassData>() {
|
|
||||||
private KtFile getFile() {
|
|
||||||
return classOrObject.getContainingKtFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public LightClassConstructionContext getContext(@NotNull Collection<KtFile> files) {
|
|
||||||
return LightClassGenerationSupport.getInstance(classOrObject.getProject()).getContextForClassOrObject(classOrObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public OutermostKotlinClassLightClassData createLightClassData(
|
|
||||||
PsiJavaFileStub javaFileStub,
|
|
||||||
BindingContext bindingContext,
|
|
||||||
Diagnostics extraDiagnostics
|
|
||||||
) {
|
|
||||||
ClassDescriptor classDescriptor = bindingContext.get(BindingContext.CLASS, classOrObject);
|
|
||||||
if (classDescriptor == null) {
|
|
||||||
return new OutermostKotlinClassLightClassData(
|
|
||||||
javaFileStub, extraDiagnostics, FqName.ROOT, classOrObject,
|
|
||||||
Collections.<KtClassOrObject, InnerKotlinClassLightClassData>emptyMap()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
FqName fqName = predictClassFqName(bindingContext, classDescriptor);
|
|
||||||
Collection<ClassDescriptor> allInnerClasses = CodegenBinding.getAllInnerClasses(bindingContext, classDescriptor);
|
|
||||||
|
|
||||||
Map<KtClassOrObject, InnerKotlinClassLightClassData> innerClassesMap = ContainerUtil.newHashMap();
|
|
||||||
for (ClassDescriptor innerClassDescriptor : allInnerClasses) {
|
|
||||||
PsiElement declaration = descriptorToDeclaration(innerClassDescriptor);
|
|
||||||
if (!(declaration instanceof KtClassOrObject)) continue;
|
|
||||||
KtClassOrObject innerClass = (KtClassOrObject) declaration;
|
|
||||||
|
|
||||||
InnerKotlinClassLightClassData innerLightClassData = new InnerKotlinClassLightClassData(
|
|
||||||
predictClassFqName(bindingContext, innerClassDescriptor),
|
|
||||||
innerClass
|
|
||||||
);
|
|
||||||
|
|
||||||
innerClassesMap.put(innerClass, innerLightClassData);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new OutermostKotlinClassLightClassData(
|
|
||||||
javaFileStub,
|
|
||||||
extraDiagnostics,
|
|
||||||
fqName,
|
|
||||||
classOrObject,
|
|
||||||
innerClassesMap
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private FqName predictClassFqName(BindingContext bindingContext, ClassDescriptor classDescriptor) {
|
|
||||||
Type asmType = CodegenBinding.getAsmType(bindingContext, classDescriptor);
|
|
||||||
//noinspection ConstantConditions
|
|
||||||
return JvmClassName.byInternalName(asmType.getClassName().replace('.', '/')).getFqNameForClassNameWithoutDollars();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Collection<KtFile> getFiles() {
|
|
||||||
return Collections.singletonList(getFile());
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public FqName getPackageFqName() {
|
|
||||||
return getFile().getPackageFqName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GenerationState.GenerateClassFilter getGenerateClassFilter() {
|
|
||||||
return new GenerationState.GenerateClassFilter() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldGeneratePackagePart(KtFile jetFile) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldAnnotateClass(KtClassOrObject classOrObject) {
|
|
||||||
return shouldGenerateClass(classOrObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldGenerateClass(KtClassOrObject generatedClassOrObject) {
|
|
||||||
// Trivial: generate and analyze class we are interested in.
|
|
||||||
if (generatedClassOrObject == classOrObject) return true;
|
|
||||||
|
|
||||||
// Process all parent classes as they are context for current class
|
|
||||||
// Process child classes because they probably affect members (heuristic)
|
|
||||||
if (PsiTreeUtil.isAncestor(generatedClassOrObject, classOrObject, true) ||
|
|
||||||
PsiTreeUtil.isAncestor(classOrObject, generatedClassOrObject, true)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (generatedClassOrObject.isLocal() && classOrObject.isLocal()) {
|
|
||||||
// Local classes should be process by CodegenAnnotatingVisitor to
|
|
||||||
// decide what class they should be placed in.
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
// class A
|
|
||||||
// fun foo() {
|
|
||||||
// trait Z: A {}
|
|
||||||
// fun bar() {
|
|
||||||
// class <caret>O2: Z {}
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TODO: current method will process local classes in irrelevant declarations, it should be fixed.
|
|
||||||
PsiElement commonParent = PsiTreeUtil.findCommonParent(generatedClassOrObject, classOrObject);
|
|
||||||
return commonParent != null && !(commonParent instanceof PsiFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldGenerateScript(KtScript script) {
|
|
||||||
// We generate all enclosing classes
|
|
||||||
return PsiTreeUtil.isAncestor(script, classOrObject, false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void generate(@NotNull GenerationState state, @NotNull Collection<KtFile> files) {
|
|
||||||
PackageCodegen packageCodegen = state.getFactory().forPackage(getPackageFqName(), files);
|
|
||||||
KtFile file = classOrObject.getContainingKtFile();
|
|
||||||
Type packagePartType = FileClasses.getFileClassType(state.getFileClassesProvider(), file);
|
|
||||||
PackageContext context = state.getRootContext().intoPackagePart(packageCodegen.getPackageFragment(), packagePartType, file);
|
|
||||||
packageCodegen.generateClassOrObject(classOrObject, context);
|
|
||||||
state.getFactory().asList();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return StubGenerationStrategy.class.getName() + " for explicit class " + classOrObject.getName();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final Logger LOG = Logger.getInstance(KotlinJavaFileStubProvider.class);
|
|
||||||
|
|
||||||
private final Project project;
|
|
||||||
private final StubGenerationStrategy<T> stubGenerationStrategy;
|
|
||||||
private final boolean local;
|
|
||||||
|
|
||||||
private KotlinJavaFileStubProvider(
|
|
||||||
@NotNull Project project,
|
|
||||||
boolean local,
|
|
||||||
@NotNull StubGenerationStrategy<T> stubGenerationStrategy
|
|
||||||
) {
|
|
||||||
this.project = project;
|
|
||||||
this.stubGenerationStrategy = stubGenerationStrategy;
|
|
||||||
this.local = local;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Result<T> compute() {
|
|
||||||
FqName packageFqName = stubGenerationStrategy.getPackageFqName();
|
|
||||||
Collection<KtFile> files = stubGenerationStrategy.getFiles();
|
|
||||||
|
|
||||||
checkForBuiltIns(packageFqName, files);
|
|
||||||
|
|
||||||
LightClassConstructionContext context = stubGenerationStrategy.getContext(files);
|
|
||||||
|
|
||||||
PsiJavaFileStub javaFileStub = createJavaFileStub(packageFqName, files);
|
|
||||||
BindingContext bindingContext;
|
|
||||||
BindingTraceContext forExtraDiagnostics = new BindingTraceContext();
|
|
||||||
try {
|
try {
|
||||||
Stack<StubElement> stubStack = new Stack<StubElement>();
|
val stubStack = Stack<StubElement<PsiElement>>()
|
||||||
stubStack.push(javaFileStub);
|
stubStack.push(javaFileStub)
|
||||||
|
|
||||||
GenerationState state = new GenerationState(
|
val state = GenerationState(
|
||||||
project,
|
project,
|
||||||
new KotlinLightClassBuilderFactory(stubStack),
|
KotlinLightClassBuilderFactory(stubStack),
|
||||||
context.getModule(),
|
context.module,
|
||||||
context.getBindingContext(),
|
context.bindingContext,
|
||||||
Lists.newArrayList(files),
|
Lists.newArrayList(files),
|
||||||
/*disable not-null assertions*/false, false,
|
/*disable not-null assertions*/false, false,
|
||||||
/*generateClassFilter=*/stubGenerationStrategy.getGenerateClassFilter(),
|
/*generateClassFilter=*/stubGenerationStrategy.generateClassFilter,
|
||||||
/*disableInline=*/false,
|
/*disableInline=*/false,
|
||||||
/*disableOptimization=*/false,
|
/*disableOptimization=*/false,
|
||||||
/*useTypeTableInSerializer=*/false,
|
/*useTypeTableInSerializer=*/false,
|
||||||
forExtraDiagnostics
|
forExtraDiagnostics)
|
||||||
);
|
KotlinCodegenFacade.prepareForCompilation(state)
|
||||||
KotlinCodegenFacade.prepareForCompilation(state);
|
|
||||||
|
|
||||||
bindingContext = state.getBindingContext();
|
bindingContext = state.bindingContext
|
||||||
|
|
||||||
stubGenerationStrategy.generate(state, files);
|
stubGenerationStrategy.generate(state, files)
|
||||||
|
|
||||||
StubElement pop = stubStack.pop();
|
val pop = stubStack.pop()
|
||||||
if (pop != javaFileStub) {
|
if (pop !== javaFileStub) {
|
||||||
LOG.error("Unbalanced stack operations: " + pop);
|
LOG.error("Unbalanced stack operations: " + pop)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ProcessCanceledException e) {
|
catch (e: ProcessCanceledException) {
|
||||||
throw e;
|
throw e
|
||||||
}
|
}
|
||||||
catch (RuntimeException e) {
|
catch (e: RuntimeException) {
|
||||||
logErrorWithOSInfo(e, packageFqName, null);
|
logErrorWithOSInfo(e, packageFqName, null)
|
||||||
throw e;
|
throw e
|
||||||
}
|
}
|
||||||
|
|
||||||
Diagnostics extraDiagnostics = forExtraDiagnostics.getBindingContext().getDiagnostics();
|
val extraDiagnostics = forExtraDiagnostics.bindingContext.diagnostics
|
||||||
return Result.create(
|
return CachedValueProvider.Result.create(
|
||||||
stubGenerationStrategy.createLightClassData(javaFileStub, bindingContext, extraDiagnostics),
|
stubGenerationStrategy.createLightClassData(javaFileStub, bindingContext, extraDiagnostics),
|
||||||
local ? PsiModificationTracker.MODIFICATION_COUNT : PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT
|
if (local) PsiModificationTracker.MODIFICATION_COUNT else PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT)
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
private fun createJavaFileStub(packageFqName: FqName, files: Collection<KtFile>): PsiJavaFileStub {
|
||||||
private static ClsFileImpl createFakeClsFile(
|
val javaFileStub = PsiJavaFileStubImpl(packageFqName.asString(), true)
|
||||||
@NotNull Project project,
|
javaFileStub.psiFactory = ClsWrapperStubPsiFactory()
|
||||||
@NotNull final FqName packageFqName,
|
|
||||||
@NotNull Collection<KtFile> files,
|
|
||||||
@NotNull final Function0<? extends PsiClassHolderFileStub> fileStubProvider
|
|
||||||
) {
|
|
||||||
PsiManager manager = PsiManager.getInstance(project);
|
|
||||||
|
|
||||||
VirtualFile virtualFile = getRepresentativeVirtualFile(files);
|
val fakeFile = createFakeClsFile(project, packageFqName, files, object : Function0<PsiClassHolderFileStub<PsiFile>> {
|
||||||
ClsFileImpl fakeFile = new ClsFileImpl(new ClassFileViewProvider(manager, virtualFile)) {
|
override fun invoke(): PsiClassHolderFileStub<PsiFile> {
|
||||||
@NotNull
|
return javaFileStub
|
||||||
@Override
|
}
|
||||||
public PsiClassHolderFileStub getStub() {
|
})
|
||||||
return fileStubProvider.invoke();
|
|
||||||
|
javaFileStub.setPsi(fakeFile)
|
||||||
|
return javaFileStub
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun createForFacadeClass(
|
||||||
|
project: Project,
|
||||||
|
facadeFqName: FqName,
|
||||||
|
searchScope: GlobalSearchScope): CachedValueProvider<KotlinFacadeLightClassData> {
|
||||||
|
return KotlinJavaFileStubProvider(
|
||||||
|
project,
|
||||||
|
false,
|
||||||
|
object : StubGenerationStrategy<KotlinFacadeLightClassData> {
|
||||||
|
override val files: Collection<KtFile>
|
||||||
|
get() = LightClassGenerationSupport.getInstance(project).findFilesForFacade(facadeFqName, searchScope)
|
||||||
|
|
||||||
|
override val packageFqName: FqName
|
||||||
|
get() = facadeFqName.parent()
|
||||||
|
|
||||||
|
override fun getContext(files: Collection<KtFile>): LightClassConstructionContext {
|
||||||
|
return LightClassGenerationSupport.getInstance(project).getContextForFacade(files)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createLightClassData(
|
||||||
|
javaFileStub: PsiJavaFileStub,
|
||||||
|
bindingContext: BindingContext,
|
||||||
|
extraDiagnostics: Diagnostics): KotlinFacadeLightClassData {
|
||||||
|
return KotlinFacadeLightClassData(javaFileStub, extraDiagnostics)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val generateClassFilter: GenerationState.GenerateClassFilter
|
||||||
|
get() = object : GenerationState.GenerateClassFilter() {
|
||||||
|
override fun shouldAnnotateClass(classOrObject: KtClassOrObject): Boolean {
|
||||||
|
return shouldGenerateClass(classOrObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shouldGenerateClass(classOrObject: KtClassOrObject): Boolean {
|
||||||
|
return KtPsiUtil.isLocal(classOrObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shouldGeneratePackagePart(jetFile: KtFile): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shouldGenerateScript(script: KtScript): Boolean {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun generate(state: GenerationState, files: Collection<KtFile>) {
|
||||||
|
if (!files.isEmpty()) {
|
||||||
|
val representativeFile = files.iterator().next()
|
||||||
|
val fileClassInfo = NoResolveFileClassesProvider.getFileClassInfo(representativeFile)
|
||||||
|
if (!fileClassInfo.withJvmMultifileClass) {
|
||||||
|
val codegen = state.factory.forPackage(representativeFile.packageFqName, files)
|
||||||
|
codegen.generate(CompilationErrorHandler.THROW_EXCEPTION)
|
||||||
|
state.factory.asList()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val codegen = state.factory.forMultifileClass(facadeFqName, files)
|
||||||
|
codegen.generate(CompilationErrorHandler.THROW_EXCEPTION)
|
||||||
|
state.factory.asList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return StubGenerationStrategy::class.java.name + " for facade class"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createForDeclaredClass(classOrObject: KtClassOrObject): KotlinJavaFileStubProvider<OutermostKotlinClassLightClassData> {
|
||||||
|
return KotlinJavaFileStubProvider(
|
||||||
|
classOrObject.project,
|
||||||
|
classOrObject.isLocal(),
|
||||||
|
object : StubGenerationStrategy<OutermostKotlinClassLightClassData> {
|
||||||
|
private val file: KtFile
|
||||||
|
get() = classOrObject.getContainingKtFile()
|
||||||
|
|
||||||
|
override fun getContext(files: Collection<KtFile>): LightClassConstructionContext {
|
||||||
|
return LightClassGenerationSupport.getInstance(classOrObject.project).getContextForClassOrObject(classOrObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun createLightClassData(
|
||||||
|
javaFileStub: PsiJavaFileStub,
|
||||||
|
bindingContext: BindingContext,
|
||||||
|
extraDiagnostics: Diagnostics): OutermostKotlinClassLightClassData {
|
||||||
|
val classDescriptor = bindingContext.get(BindingContext.CLASS, classOrObject) ?: return OutermostKotlinClassLightClassData(
|
||||||
|
javaFileStub, extraDiagnostics, FqName.ROOT, classOrObject,
|
||||||
|
emptyMap<KtClassOrObject, InnerKotlinClassLightClassData>())
|
||||||
|
|
||||||
|
val fqName = predictClassFqName(bindingContext, classDescriptor)
|
||||||
|
val allInnerClasses = CodegenBinding.getAllInnerClasses(bindingContext, classDescriptor)
|
||||||
|
|
||||||
|
val innerClassesMap = ContainerUtil.newHashMap<KtClassOrObject, InnerKotlinClassLightClassData>()
|
||||||
|
for (innerClassDescriptor in allInnerClasses) {
|
||||||
|
val declaration = descriptorToDeclaration(innerClassDescriptor)
|
||||||
|
if (declaration !is KtClassOrObject) continue
|
||||||
|
|
||||||
|
val innerLightClassData = InnerKotlinClassLightClassData(
|
||||||
|
predictClassFqName(bindingContext, innerClassDescriptor),
|
||||||
|
declaration)
|
||||||
|
|
||||||
|
innerClassesMap.put(declaration, innerLightClassData)
|
||||||
|
}
|
||||||
|
|
||||||
|
return OutermostKotlinClassLightClassData(
|
||||||
|
javaFileStub,
|
||||||
|
extraDiagnostics,
|
||||||
|
fqName,
|
||||||
|
classOrObject,
|
||||||
|
innerClassesMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun predictClassFqName(bindingContext: BindingContext, classDescriptor: ClassDescriptor): FqName {
|
||||||
|
val asmType = CodegenBinding.getAsmType(bindingContext, classDescriptor)
|
||||||
|
//noinspection ConstantConditions
|
||||||
|
return JvmClassName.byInternalName(asmType.className.replace('.', '/')).fqNameForClassNameWithoutDollars
|
||||||
|
}
|
||||||
|
|
||||||
|
override val files: Collection<KtFile>
|
||||||
|
get() = listOf(file)
|
||||||
|
|
||||||
|
override val packageFqName: FqName
|
||||||
|
get() = file.packageFqName
|
||||||
|
|
||||||
|
override // Trivial: generate and analyze class we are interested in.
|
||||||
|
// Process all parent classes as they are context for current class
|
||||||
|
// Process child classes because they probably affect members (heuristic)
|
||||||
|
// Local classes should be process by CodegenAnnotatingVisitor to
|
||||||
|
// decide what class they should be placed in.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// class A
|
||||||
|
// fun foo() {
|
||||||
|
// trait Z: A {}
|
||||||
|
// fun bar() {
|
||||||
|
// class <caret>O2: Z {}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// TODO: current method will process local classes in irrelevant declarations, it should be fixed.
|
||||||
|
// We generate all enclosing classes
|
||||||
|
val generateClassFilter: GenerationState.GenerateClassFilter
|
||||||
|
get() = object : GenerationState.GenerateClassFilter() {
|
||||||
|
|
||||||
|
override fun shouldGeneratePackagePart(jetFile: KtFile): Boolean {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shouldAnnotateClass(classOrObject: KtClassOrObject): Boolean {
|
||||||
|
return shouldGenerateClass(classOrObject)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shouldGenerateClass(generatedClassOrObject: KtClassOrObject): Boolean {
|
||||||
|
if (generatedClassOrObject === classOrObject) return true
|
||||||
|
if (PsiTreeUtil.isAncestor(generatedClassOrObject, classOrObject, true) || PsiTreeUtil.isAncestor(classOrObject, generatedClassOrObject, true)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (generatedClassOrObject.isLocal() && classOrObject.isLocal()) {
|
||||||
|
val commonParent = PsiTreeUtil.findCommonParent(generatedClassOrObject, classOrObject)
|
||||||
|
return commonParent != null && commonParent !is PsiFile
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shouldGenerateScript(script: KtScript): Boolean {
|
||||||
|
return PsiTreeUtil.isAncestor(script, classOrObject, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun generate(state: GenerationState, files: Collection<KtFile>) {
|
||||||
|
val packageCodegen = state.factory.forPackage(packageFqName, files)
|
||||||
|
val file = classOrObject.getContainingKtFile()
|
||||||
|
val packagePartType = state.fileClassesProvider.getFileClassType(file)
|
||||||
|
val context = state.rootContext.intoPackagePart(packageCodegen.packageFragment, packagePartType, file)
|
||||||
|
packageCodegen.generateClassOrObject(classOrObject, context)
|
||||||
|
state.factory.asList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
return StubGenerationStrategy::class.java.name + " for explicit class " + classOrObject.name
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private val LOG = Logger.getInstance(KotlinJavaFileStubProvider::class.java)
|
||||||
|
|
||||||
|
private fun createFakeClsFile(
|
||||||
|
project: Project,
|
||||||
|
packageFqName: FqName,
|
||||||
|
files: Collection<KtFile>,
|
||||||
|
fileStubProvider: Function0<PsiClassHolderFileStub<PsiFile>>): ClsFileImpl {
|
||||||
|
val manager = PsiManager.getInstance(project)
|
||||||
|
|
||||||
|
val virtualFile = getRepresentativeVirtualFile(files)
|
||||||
|
val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, virtualFile)) {
|
||||||
|
override fun getStub(): PsiClassHolderFileStub<PsiFile> {
|
||||||
|
return fileStubProvider.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getPackageName(): String {
|
||||||
|
return packageFqName.asString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
fakeFile.isPhysical = false
|
||||||
@Override
|
return fakeFile
|
||||||
public String getPackageName() {
|
}
|
||||||
return packageFqName.asString();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fakeFile.setPhysical(false);
|
private fun getRepresentativeVirtualFile(files: Collection<KtFile>): VirtualFile {
|
||||||
return fakeFile;
|
val firstFile = files.iterator().next()
|
||||||
}
|
val virtualFile = firstFile.virtualFile
|
||||||
|
assert(virtualFile != null) { "No virtual file for " + firstFile }
|
||||||
|
return virtualFile
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
private fun checkForBuiltIns(fqName: FqName, files: Collection<KtFile>) {
|
||||||
private PsiJavaFileStub createJavaFileStub(@NotNull FqName packageFqName, @NotNull Collection<KtFile> files) {
|
for (file in files) {
|
||||||
final PsiJavaFileStubImpl javaFileStub = new PsiJavaFileStubImpl(packageFqName.asString(), true);
|
if (LightClassUtil.belongsToKotlinBuiltIns(file)) {
|
||||||
javaFileStub.setPsiFactory(new ClsWrapperStubPsiFactory());
|
// We may not fail later due to some luck, but generating JetLightClasses for built-ins is a bad idea anyways
|
||||||
|
// If it fails later, there will be an exception logged
|
||||||
ClsFileImpl fakeFile = createFakeClsFile(project, packageFqName, files, new Function0<PsiClassHolderFileStub>() {
|
logErrorWithOSInfo(null, fqName, file.virtualFile)
|
||||||
@Override
|
}
|
||||||
public PsiClassHolderFileStub invoke() {
|
|
||||||
return javaFileStub;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
javaFileStub.setPsi(fakeFile);
|
|
||||||
return javaFileStub;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static VirtualFile getRepresentativeVirtualFile(@NotNull Collection<KtFile> files) {
|
|
||||||
KtFile firstFile = files.iterator().next();
|
|
||||||
VirtualFile virtualFile = firstFile.getVirtualFile();
|
|
||||||
assert virtualFile != null : "No virtual file for " + firstFile;
|
|
||||||
return virtualFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void checkForBuiltIns(@NotNull FqName fqName, @NotNull Collection<KtFile> files) {
|
|
||||||
for (KtFile file : files) {
|
|
||||||
if (LightClassUtil.INSTANCE$.belongsToKotlinBuiltIns(file)) {
|
|
||||||
// We may not fail later due to some luck, but generating JetLightClasses for built-ins is a bad idea anyways
|
|
||||||
// If it fails later, there will be an exception logged
|
|
||||||
logErrorWithOSInfo(null, fqName, file.getVirtualFile());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private static void logErrorWithOSInfo(@Nullable Throwable cause, @NotNull FqName fqName, @Nullable VirtualFile virtualFile) {
|
private fun logErrorWithOSInfo(cause: Throwable?, fqName: FqName, virtualFile: VirtualFile?) {
|
||||||
String path = virtualFile == null ? "<null>" : virtualFile.getPath();
|
val path = if (virtualFile == null) "<null>" else virtualFile.path
|
||||||
LOG.error(
|
LOG.error(
|
||||||
"Could not generate LightClass for " + fqName + " declared in " + path + "\n" +
|
"Could not generate LightClass for " + fqName + " declared in " + path + "\n" + "built-ins dir URL is " + LightClassUtil.builtInsDirUrl + "\n" + "System: " + SystemInfo.OS_NAME + " " + SystemInfo.OS_VERSION + " Java Runtime: " + SystemInfo.JAVA_RUNTIME_VERSION,
|
||||||
"built-ins dir URL is " + LightClassUtil.INSTANCE$.getBuiltInsDirUrl() + "\n" +
|
cause)
|
||||||
"System: " + SystemInfo.OS_NAME + " " + SystemInfo.OS_VERSION + " Java Runtime: " + SystemInfo.JAVA_RUNTIME_VERSION,
|
}
|
||||||
cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
private interface StubGenerationStrategy<T extends WithFileStubAndExtraDiagnostics> {
|
|
||||||
@NotNull Collection<KtFile> getFiles();
|
|
||||||
@NotNull FqName getPackageFqName();
|
|
||||||
|
|
||||||
@NotNull LightClassConstructionContext getContext(@NotNull Collection<KtFile> files);
|
|
||||||
@NotNull T createLightClassData(PsiJavaFileStub javaFileStub, BindingContext bindingContext, Diagnostics extraDiagnostics);
|
|
||||||
|
|
||||||
GenerationState.GenerateClassFilter getGenerateClassFilter();
|
|
||||||
void generate(@NotNull GenerationState state, @NotNull Collection<KtFile> files);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private interface StubGenerationStrategy<T : WithFileStubAndExtraDiagnostics> {
|
||||||
|
val files: Collection<KtFile>
|
||||||
|
val packageFqName: FqName
|
||||||
|
|
||||||
|
fun getContext(files: Collection<KtFile>): LightClassConstructionContext
|
||||||
|
fun createLightClassData(javaFileStub: PsiJavaFileStub, bindingContext: BindingContext, extraDiagnostics: Diagnostics): T
|
||||||
|
|
||||||
|
val generateClassFilter: GenerationState.GenerateClassFilter
|
||||||
|
fun generate(state: GenerationState, files: Collection<KtFile>)
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user