diff --git a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinJavaFileStubProvider.kt b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinJavaFileStubProvider.kt index 424c8ab3f27..77642a24b5b 100644 --- a/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinJavaFileStubProvider.kt +++ b/compiler/light-classes/src/org/jetbrains/kotlin/asJava/KotlinJavaFileStubProvider.kt @@ -54,11 +54,13 @@ import org.jetbrains.kotlin.resolve.BindingTraceContext import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics import org.jetbrains.kotlin.resolve.jvm.JvmClassName +import org.jetbrains.kotlin.utils.sure class KotlinJavaFileStubProvider private constructor( private val project: Project, private val local: Boolean, - private val stubGenerationStrategy: StubGenerationStrategy) : CachedValueProvider { + private val stubGenerationStrategy: StubGenerationStrategy +) : CachedValueProvider { override fun compute(): CachedValueProvider.Result? { val packageFqName = stubGenerationStrategy.packageFqName @@ -73,7 +75,7 @@ class KotlinJavaFileStubProvider private co val forExtraDiagnostics = BindingTraceContext() try { val stubStack = Stack>() - stubStack.push(javaFileStub) + stubStack.push(javaFileStub as StubElement) val state = GenerationState( project, @@ -109,255 +111,73 @@ class KotlinJavaFileStubProvider private co val extraDiagnostics = forExtraDiagnostics.bindingContext.diagnostics return CachedValueProvider.Result.create( stubGenerationStrategy.createLightClassData(javaFileStub, bindingContext, extraDiagnostics), - if (local) PsiModificationTracker.MODIFICATION_COUNT else PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT) + if (local) PsiModificationTracker.MODIFICATION_COUNT else PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT + ) } private fun createJavaFileStub(packageFqName: FqName, files: Collection): PsiJavaFileStub { val javaFileStub = PsiJavaFileStubImpl(packageFqName.asString(), true) javaFileStub.psiFactory = ClsWrapperStubPsiFactory() - val fakeFile = createFakeClsFile(project, packageFqName, files, object : Function0> { - override fun invoke(): PsiClassHolderFileStub { + val manager = PsiManager.getInstance(project) + + val virtualFile = getRepresentativeVirtualFile(files) + val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, virtualFile)) { + override fun getStub(): PsiClassHolderFileStub<*> { return javaFileStub } - }) + + override fun getPackageName(): String { + return packageFqName.asString() + } + } + + fakeFile.isPhysical = false javaFileStub.setPsi(fakeFile) return javaFileStub } + private fun getRepresentativeVirtualFile(files: Collection): VirtualFile { + return files.firstOrNull()?.virtualFile.sure { "No virtual file for " + files.iterator().next() } + } + + private fun checkForBuiltIns(fqName: FqName, files: Collection) { + for (file in files) { + if (LightClassUtil.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.virtualFile) + } + } + } + + private fun logErrorWithOSInfo(cause: Throwable?, fqName: FqName, virtualFile: VirtualFile?) { + val path = if (virtualFile == null) "" else virtualFile.path + LOG.error( + "Could not generate LightClass for $fqName declared in $path\nbuilt-ins dir URL is ${LightClassUtil.builtInsDirUrl}\n" + + "System: ${SystemInfo.OS_NAME} ${SystemInfo.OS_VERSION} Java Runtime: ${SystemInfo.JAVA_RUNTIME_VERSION}", + cause + ) + } + companion object { + fun createForDeclaredClass(classOrObject: KtClassOrObject): KotlinJavaFileStubProvider { + return KotlinJavaFileStubProvider( + classOrObject.project, + classOrObject.isLocal(), + StubGenerationStrategyForDeclaredClass(classOrObject) + ) + } fun createForFacadeClass( project: Project, facadeFqName: FqName, searchScope: GlobalSearchScope): CachedValueProvider { - return KotlinJavaFileStubProvider( - project, - false, - object : StubGenerationStrategy { - override val files: Collection - get() = LightClassGenerationSupport.getInstance(project).findFilesForFacade(facadeFqName, searchScope) - - override val packageFqName: FqName - get() = facadeFqName.parent() - - override fun getContext(files: Collection): 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) { - 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 { - return KotlinJavaFileStubProvider( - classOrObject.project, - classOrObject.isLocal(), - object : StubGenerationStrategy { - private val file: KtFile - get() = classOrObject.getContainingKtFile() - - override fun getContext(files: Collection): 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()) - - val fqName = predictClassFqName(bindingContext, classDescriptor) - val allInnerClasses = CodegenBinding.getAllInnerClasses(bindingContext, classDescriptor) - - val innerClassesMap = ContainerUtil.newHashMap() - 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 - 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 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) { - 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 - } - }) + return KotlinJavaFileStubProvider(project, false, StubGenerationStrategyForFacadeClass(project, facadeFqName, searchScope)) } private val LOG = Logger.getInstance(KotlinJavaFileStubProvider::class.java) - - private fun createFakeClsFile( - project: Project, - packageFqName: FqName, - files: Collection, - fileStubProvider: Function0>): ClsFileImpl { - val manager = PsiManager.getInstance(project) - - val virtualFile = getRepresentativeVirtualFile(files) - val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, virtualFile)) { - override fun getStub(): PsiClassHolderFileStub { - return fileStubProvider.invoke() - } - - override fun getPackageName(): String { - return packageFqName.asString() - } - } - - fakeFile.isPhysical = false - return fakeFile - } - - private fun getRepresentativeVirtualFile(files: Collection): VirtualFile { - val firstFile = files.iterator().next() - val virtualFile = firstFile.virtualFile - assert(virtualFile != null) { "No virtual file for " + firstFile } - return virtualFile - } - - private fun checkForBuiltIns(fqName: FqName, files: Collection) { - for (file in files) { - if (LightClassUtil.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.virtualFile) - } - } - } - - private fun logErrorWithOSInfo(cause: Throwable?, fqName: FqName, virtualFile: VirtualFile?) { - val path = if (virtualFile == null) "" else virtualFile.path - LOG.error( - "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, - cause) - } } } @@ -371,3 +191,178 @@ private interface StubGenerationStrategy { val generateClassFilter: GenerationState.GenerateClassFilter fun generate(state: GenerationState, files: Collection) } + +private class StubGenerationStrategyForDeclaredClass(private val classOrObject: KtClassOrObject) : StubGenerationStrategy { + private val file: KtFile + get() = classOrObject.getContainingKtFile() + + override fun getContext(files: Collection): 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()) + + val fqName = predictClassFqName(bindingContext, classDescriptor) + val allInnerClasses = CodegenBinding.getAllInnerClasses(bindingContext, classDescriptor) + + val innerClassesMap = ContainerUtil.newHashMap() + 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 + get() = listOf(file) + + override val packageFqName: FqName + get() = file.packageFqName + + override 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(classOrObject: KtClassOrObject): Boolean { + // Trivial: generate and analyze class we are interested in. + if (classOrObject === 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(classOrObject, classOrObject, true) || PsiTreeUtil.isAncestor(classOrObject, classOrObject, true)) { + return true + } + + // 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 O2: Z {} + // } + // } + // TODO: current method will process local classes in irrelevant declarations, it should be fixed. + // We generate all enclosing classes + + if (classOrObject.isLocal() && classOrObject.isLocal()) { + val commonParent = PsiTreeUtil.findCommonParent(classOrObject, 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) { + 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 class StubGenerationStrategyForFacadeClass( + private val project: Project, private val facadeFqName: FqName, private val searchScope: GlobalSearchScope +) : StubGenerationStrategy { + override val files: Collection + get() = LightClassGenerationSupport.getInstance(project).findFilesForFacade(facadeFqName, searchScope) + + override val packageFqName: FqName + get() = facadeFqName.parent() + + override fun getContext(files: Collection): 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) { + 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" + } +}