J2K KotlinJavaFileStubProvider: refactor code
This commit is contained in:
+224
-229
@@ -54,11 +54,13 @@ import org.jetbrains.kotlin.resolve.BindingTraceContext
|
|||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration
|
||||||
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
|
import org.jetbrains.kotlin.utils.sure
|
||||||
|
|
||||||
class KotlinJavaFileStubProvider<T : WithFileStubAndExtraDiagnostics> private constructor(
|
class KotlinJavaFileStubProvider<T : WithFileStubAndExtraDiagnostics> private constructor(
|
||||||
private val project: Project,
|
private val project: Project,
|
||||||
private val local: Boolean,
|
private val local: Boolean,
|
||||||
private val stubGenerationStrategy: StubGenerationStrategy<T>) : CachedValueProvider<T> {
|
private val stubGenerationStrategy: StubGenerationStrategy<T>
|
||||||
|
) : CachedValueProvider<T> {
|
||||||
|
|
||||||
override fun compute(): CachedValueProvider.Result<T>? {
|
override fun compute(): CachedValueProvider.Result<T>? {
|
||||||
val packageFqName = stubGenerationStrategy.packageFqName
|
val packageFqName = stubGenerationStrategy.packageFqName
|
||||||
@@ -73,7 +75,7 @@ class KotlinJavaFileStubProvider<T : WithFileStubAndExtraDiagnostics> private co
|
|||||||
val forExtraDiagnostics = BindingTraceContext()
|
val forExtraDiagnostics = BindingTraceContext()
|
||||||
try {
|
try {
|
||||||
val stubStack = Stack<StubElement<PsiElement>>()
|
val stubStack = Stack<StubElement<PsiElement>>()
|
||||||
stubStack.push(javaFileStub)
|
stubStack.push(javaFileStub as StubElement<PsiElement>)
|
||||||
|
|
||||||
val state = GenerationState(
|
val state = GenerationState(
|
||||||
project,
|
project,
|
||||||
@@ -109,255 +111,73 @@ class KotlinJavaFileStubProvider<T : WithFileStubAndExtraDiagnostics> private co
|
|||||||
val extraDiagnostics = forExtraDiagnostics.bindingContext.diagnostics
|
val extraDiagnostics = forExtraDiagnostics.bindingContext.diagnostics
|
||||||
return CachedValueProvider.Result.create(
|
return CachedValueProvider.Result.create(
|
||||||
stubGenerationStrategy.createLightClassData(javaFileStub, bindingContext, extraDiagnostics),
|
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<KtFile>): PsiJavaFileStub {
|
private fun createJavaFileStub(packageFqName: FqName, files: Collection<KtFile>): PsiJavaFileStub {
|
||||||
val javaFileStub = PsiJavaFileStubImpl(packageFqName.asString(), true)
|
val javaFileStub = PsiJavaFileStubImpl(packageFqName.asString(), true)
|
||||||
javaFileStub.psiFactory = ClsWrapperStubPsiFactory()
|
javaFileStub.psiFactory = ClsWrapperStubPsiFactory()
|
||||||
|
|
||||||
val fakeFile = createFakeClsFile(project, packageFqName, files, object : Function0<PsiClassHolderFileStub<PsiFile>> {
|
val manager = PsiManager.getInstance(project)
|
||||||
override fun invoke(): PsiClassHolderFileStub<PsiFile> {
|
|
||||||
|
val virtualFile = getRepresentativeVirtualFile(files)
|
||||||
|
val fakeFile = object : ClsFileImpl(ClassFileViewProvider(manager, virtualFile)) {
|
||||||
|
override fun getStub(): PsiClassHolderFileStub<*> {
|
||||||
return javaFileStub
|
return javaFileStub
|
||||||
}
|
}
|
||||||
})
|
|
||||||
|
override fun getPackageName(): String {
|
||||||
|
return packageFqName.asString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fakeFile.isPhysical = false
|
||||||
|
|
||||||
javaFileStub.setPsi(fakeFile)
|
javaFileStub.setPsi(fakeFile)
|
||||||
return javaFileStub
|
return javaFileStub
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getRepresentativeVirtualFile(files: Collection<KtFile>): VirtualFile {
|
||||||
|
return files.firstOrNull()?.virtualFile.sure { "No virtual file for " + files.iterator().next() }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkForBuiltIns(fqName: FqName, files: Collection<KtFile>) {
|
||||||
|
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) "<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 {
|
companion object {
|
||||||
|
fun createForDeclaredClass(classOrObject: KtClassOrObject): KotlinJavaFileStubProvider<OutermostKotlinClassLightClassData> {
|
||||||
|
return KotlinJavaFileStubProvider(
|
||||||
|
classOrObject.project,
|
||||||
|
classOrObject.isLocal(),
|
||||||
|
StubGenerationStrategyForDeclaredClass(classOrObject)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fun createForFacadeClass(
|
fun createForFacadeClass(
|
||||||
project: Project,
|
project: Project,
|
||||||
facadeFqName: FqName,
|
facadeFqName: FqName,
|
||||||
searchScope: GlobalSearchScope): CachedValueProvider<KotlinFacadeLightClassData> {
|
searchScope: GlobalSearchScope): CachedValueProvider<KotlinFacadeLightClassData> {
|
||||||
return KotlinJavaFileStubProvider(
|
return KotlinJavaFileStubProvider(project, false, StubGenerationStrategyForFacadeClass(project, facadeFqName, searchScope))
|
||||||
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 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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fakeFile.isPhysical = false
|
|
||||||
return fakeFile
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getRepresentativeVirtualFile(files: Collection<KtFile>): 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<KtFile>) {
|
|
||||||
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) "<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<T : WithFileStubAndExtraDiagnostics> {
|
|||||||
val generateClassFilter: GenerationState.GenerateClassFilter
|
val generateClassFilter: GenerationState.GenerateClassFilter
|
||||||
fun generate(state: GenerationState, files: Collection<KtFile>)
|
fun generate(state: GenerationState, files: Collection<KtFile>)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class StubGenerationStrategyForDeclaredClass(private val classOrObject: KtClassOrObject) : 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 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 <caret>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<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 class StubGenerationStrategyForFacadeClass(
|
||||||
|
private val project: Project, private val facadeFqName: FqName, private val searchScope: GlobalSearchScope
|
||||||
|
) : 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user