diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java index a958cdd3952..c0e4a77fcef 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java @@ -27,10 +27,7 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.backend.common.bridges.Bridge; import org.jetbrains.kotlin.backend.common.bridges.BridgesPackage; import org.jetbrains.kotlin.codegen.annotation.AnnotatedWithOnlyTargetedAnnotations; -import org.jetbrains.kotlin.codegen.context.CodegenContext; -import org.jetbrains.kotlin.codegen.context.MethodContext; -import org.jetbrains.kotlin.codegen.context.PackageContext; -import org.jetbrains.kotlin.codegen.context.PackageFacadeContext; +import org.jetbrains.kotlin.codegen.context.*; import org.jetbrains.kotlin.codegen.optimization.OptimizationMethodVisitor; import org.jetbrains.kotlin.codegen.state.GenerationState; import org.jetbrains.kotlin.codegen.state.JetTypeMapper; @@ -163,15 +160,11 @@ public class FunctionCodegen { jvmSignature.getGenericsSignature(), getThrownExceptions(functionDescriptor, typeMapper)); - if (owner instanceof PackageFacadeContext) { - Type ownerType = ((PackageFacadeContext) owner).getDelegateToClassType(); - v.getSerializationBindings().put(IMPL_CLASS_NAME_FOR_CALLABLE, functionDescriptor, shortNameByAsmType(ownerType)); + String implClassName = CodegenContextUtil.getImplClassNameByOwnerIfRequired(owner); + if (implClassName != null) { + v.getSerializationBindings().put(IMPL_CLASS_NAME_FOR_CALLABLE, functionDescriptor, implClassName); } - else { - if (owner instanceof PackageContext) { - Type ownerType = ((PackageContext) owner).getPackagePartType(); - v.getSerializationBindings().put(IMPL_CLASS_NAME_FOR_CALLABLE, functionDescriptor, shortNameByAsmType(ownerType)); - } + if (CodegenContextUtil.isImplClassOwner(owner)) { v.getSerializationBindings().put(METHOD_FOR_FUNCTION, functionDescriptor, asmMethod); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java index f0297b5ef2e..e816a8a23e3 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/PropertyCodegen.java @@ -110,15 +110,12 @@ public class PropertyCodegen { assert kind == OwnerKind.PACKAGE || kind == OwnerKind.IMPLEMENTATION || kind == OwnerKind.TRAIT_IMPL : "Generating property with a wrong kind (" + kind + "): " + descriptor; - if (context instanceof PackageFacadeContext) { - Type ownerType = ((PackageFacadeContext) context).getDelegateToClassType(); - v.getSerializationBindings().put(IMPL_CLASS_NAME_FOR_CALLABLE, descriptor, shortNameByAsmType(ownerType)); + Type implClassType = CodegenContextUtil.getImplClassTypeByOwnerIfRequired(context); + if (implClassType != null) { + v.getSerializationBindings().put(IMPL_CLASS_NAME_FOR_CALLABLE, descriptor, shortNameByAsmType(implClassType)); } - else { - if (context instanceof PackageContext) { - Type ownerType = ((PackageContext) context).getPackagePartType(); - v.getSerializationBindings().put(IMPL_CLASS_NAME_FOR_CALLABLE, descriptor, shortNameByAsmType(ownerType)); - } + + if (CodegenContextUtil.isImplClassOwner(context)) { assert declaration != null : "Declaration is null for different context: " + context; boolean hasBackingField = hasBackingField(declaration, descriptor); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContextUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContextUtil.kt new file mode 100644 index 00000000000..5b0f25e5aa4 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/CodegenContextUtil.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.context + +import org.jetbrains.kotlin.codegen.AsmUtil +import org.jetbrains.org.objectweb.asm.Type + + +public object CodegenContextUtil { + public @JvmStatic fun getImplClassTypeByOwnerIfRequired(owner: CodegenContext<*>): Type? = + when (owner) { + is PackageFacadeContext -> + owner.delegateToClassType + is PackageContext -> + owner.packagePartType + is MultifileClassOrPartContext -> + owner.filePartType + else -> + null + } + + public @JvmStatic fun getImplClassNameByOwnerIfRequired(owner: CodegenContext<*>): String? = + getImplClassTypeByOwnerIfRequired(owner)?.let{ AsmUtil.shortNameByAsmType(it) } + + public @JvmStatic fun isImplClassOwner(owner: CodegenContext<*>): Boolean = + !(owner is PackageFacadeContext || owner is MultifileClassContext) +} \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassContext.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassContext.java index f6974d9652b..40dc1de52ee 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassContext.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassContext.java @@ -21,26 +21,14 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; import org.jetbrains.org.objectweb.asm.Type; -public class MultifileClassContext extends FieldOwnerContext { - private final Type multifileClassType; - private final Type filePartType; - +public class MultifileClassContext extends MultifileClassOrPartContext { public MultifileClassContext( PackageFragmentDescriptor descriptor, CodegenContext parent, Type multifileClassType, Type filePartType ) { - super(descriptor, OwnerKind.PACKAGE, parent, null, null, null); - this.multifileClassType = multifileClassType; - this.filePartType = filePartType; + super(descriptor, parent, multifileClassType, filePartType); } - public Type getMultifileClassType() { - return multifileClassType; - } - - public Type getFilePartType() { - return filePartType; - } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassOrPartContext.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassOrPartContext.java new file mode 100644 index 00000000000..4fb6d7237ae --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassOrPartContext.java @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2015 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.context; + +import org.jetbrains.kotlin.codegen.OwnerKind; +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; +import org.jetbrains.org.objectweb.asm.Type; + +public class MultifileClassOrPartContext extends FieldOwnerContext { + private final Type multifileClassType; + private final Type filePartType; + + public MultifileClassOrPartContext( + PackageFragmentDescriptor descriptor, + CodegenContext parent, + Type multifileClassType, + Type filePartType + ) { + super(descriptor, OwnerKind.PACKAGE, parent, null, null, null); + this.multifileClassType = multifileClassType; + this.filePartType = filePartType; + } + + public Type getMultifileClassType() { + return multifileClassType; + } + + public Type getFilePartType() { + return filePartType; + } +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassPartContext.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassPartContext.java index 7ce7bf13e77..06d02910ac6 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassPartContext.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/context/MultifileClassPartContext.java @@ -21,26 +21,14 @@ import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor; import org.jetbrains.org.objectweb.asm.Type; -public class MultifileClassPartContext extends FieldOwnerContext { - private final Type multifileClassType; - private final Type filePartType; - +public class MultifileClassPartContext extends MultifileClassOrPartContext { public MultifileClassPartContext( PackageFragmentDescriptor descriptor, CodegenContext parent, Type multifileClassType, Type filePartType ) { - super(descriptor, OwnerKind.PACKAGE, parent, null, null, null); - this.multifileClassType = multifileClassType; - this.filePartType = filePartType; + super(descriptor, parent, multifileClassType, filePartType); } - public Type getMultifileClassType() { - return multifileClassType; - } - - public Type getFilePartType() { - return filePartType; - } } diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt index 5c5207d89fc..89a55a401be 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt @@ -226,7 +226,8 @@ public abstract class AbstractBinaryClassAnnotationAndConstantLoader { + val result = arrayListOf() + val packageFqName = multifileClass.classId.packageFqName + val partsFinder = DirectoryBasedClassFinder(file.parent!!, packageFqName) + val partNames = multifileClass.classHeader.filePartClassNames ?: return emptyList() + return partNames.map { + partsFinder.findKotlinClass(ClassId(packageFqName, Name.identifier(it))) + }.filterNotNull() +} + +@Throws(ReadMultifileClassException::class) +public fun readMultifileClassPartHeaders(file: VirtualFile, multifileClass: KotlinJvmBinaryClass): List { + val classId = multifileClass.classId + val classHeader = multifileClass.classHeader + if (classHeader.filePartClassNames == null) { + throw ReadMultifileClassException("Multifile class $classId has no filePartClassNames or wrong ABI version: ${classHeader.version}") + } + else { + return findMultifileClassParts(file, multifileClass).map { it.classHeader } + } +} diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/stubBuilder/KotlinClsStubBuilder.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/stubBuilder/KotlinClsStubBuilder.kt index c6f6b4289ac..2cb64cd6c9c 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/stubBuilder/KotlinClsStubBuilder.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/stubBuilder/KotlinClsStubBuilder.kt @@ -22,7 +22,9 @@ import com.intellij.psi.compiled.ClsStubBuilder import com.intellij.psi.impl.compiled.ClassFileStubBuilder import com.intellij.psi.stubs.PsiFileStub import com.intellij.util.indexing.FileContent +import org.jetbrains.kotlin.idea.decompiler.ReadMultifileClassException import org.jetbrains.kotlin.idea.decompiler.isKotlinInternalCompiledFile +import org.jetbrains.kotlin.idea.decompiler.readMultifileClassPartHeaders import org.jetbrains.kotlin.idea.decompiler.textBuilder.DirectoryBasedClassFinder import org.jetbrains.kotlin.idea.decompiler.textBuilder.DirectoryBasedDataFinder import org.jetbrains.kotlin.idea.decompiler.textBuilder.LoggingErrorReporter @@ -83,32 +85,20 @@ public open class KotlinClsStubBuilder : ClsStubBuilder() { header.isCompatibleMultifileClassKind() -> { val packageData = JvmProtoBufUtil.readPackageDataFrom(annotationData) val context = components.createContext(packageData.nameResolver, packageFqName) - val partHeaders = readMultifileClassPartHeaders(file, header, packageFqName) - partHeaders?.let { - createMultifileClassStub(it, classId.asSingleFqName(), context) - } + val partHeaders = + try { + readMultifileClassPartHeaders(file, kotlinBinaryClass) + } + catch (e: ReadMultifileClassException) { + LOG.error(e.getMessage()) + return null + } + createMultifileClassStub(partHeaders, classId.asSingleFqName(), context) } else -> throw IllegalStateException("Should have processed " + file.getPath() + " with header $header") } } - private fun readMultifileClassPartHeaders(file: VirtualFile, header: KotlinClassHeader, packageFqName: FqName): List? { - val result = arrayListOf() - val partsFinder = DirectoryBasedClassFinder(file.parent!!, packageFqName) - val partNames = header.filePartClassNames - if (partNames != null) { - for (partName in partNames) { - val partClass = partsFinder.findKotlinClass(ClassId.topLevel(packageFqName.child(Name.identifier(partName)))) - if (partClass == null) { - LOG.error("Missing part class: $packageFqName.$partName") - return null - } - result.add(partClass.classHeader) - } - } - return result - } - private fun createStubBuilderComponents(file: VirtualFile, packageFqName: FqName): ClsStubBuilderComponents { val classFinder = DirectoryBasedClassFinder(file.getParent()!!, packageFqName) val classDataFinder = DirectoryBasedDataFinder(classFinder, LOG) diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/DecompiledTextFactory.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/DecompiledTextFactory.kt index 9120dfa3e88..d67b7438119 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/DecompiledTextFactory.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/decompiler/textBuilder/DecompiledTextFactory.kt @@ -19,12 +19,16 @@ package org.jetbrains.kotlin.idea.decompiler.textBuilder import com.intellij.openapi.util.TextRange import com.intellij.openapi.vfs.VirtualFile import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.idea.decompiler.ReadMultifileClassException +import org.jetbrains.kotlin.idea.decompiler.findMultifileClassParts import org.jetbrains.kotlin.idea.decompiler.navigation.JsMetaFileUtils +import org.jetbrains.kotlin.idea.decompiler.readMultifileClassPartHeaders import org.jetbrains.kotlin.load.java.JvmAbi import org.jetbrains.kotlin.load.kotlin.KotlinBinaryClassCache import org.jetbrains.kotlin.load.kotlin.PackageClassUtils import org.jetbrains.kotlin.load.kotlin.header.isCompatibleClassKind import org.jetbrains.kotlin.load.kotlin.header.isCompatibleFileFacadeKind +import org.jetbrains.kotlin.load.kotlin.header.isCompatibleMultifileClassKind import org.jetbrains.kotlin.load.kotlin.header.isCompatiblePackageFacadeKind import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.renderer.DescriptorRenderer @@ -47,6 +51,12 @@ public val INCOMPATIBLE_ABI_VERSION_COMMENT: String = "// Current compiler ABI version is $CURRENT_ABI_VERSION_MARKER\n" + "// File ABI version is $FILE_ABI_VERSION_MARKER" +private val REASON = "REASON" +public val FILE_CANT_BE_DECOMPILED_DUE_TO_ERRORS: String = + "// This class file has inconsistent Kotlin meta-information and can't be decompiled.\n" + + "//\n" + + "// $REASON" + public fun buildDecompiledText( classFile: VirtualFile, resolver: ResolverForDecompiler = DeserializerForDecompiler(classFile) @@ -70,6 +80,11 @@ public fun buildDecompiledText( buildDecompiledText(packageFqName, ArrayList(resolver.resolveDeclarationsInFacade(classId.asSingleFqName()))) classHeader.isCompatibleClassKind() -> buildDecompiledText(packageFqName, listOf(resolver.resolveTopLevelClass(classId)).filterNotNull()) + classHeader.isCompatibleMultifileClassKind() -> { + val partClasses = findMultifileClassParts(classFile, kotlinClass) + val partMembers = partClasses.flatMap { partClass -> resolver.resolveDeclarationsInFacade(partClass.classId.asSingleFqName()) } + buildDecompiledText(packageFqName, partMembers) + } else -> throw UnsupportedOperationException("Unknown header kind: ${classHeader.kind} ${classHeader.isCompatibleAbiVersion}") } diff --git a/idea/testData/decompiler/decompiledTextJvm/MultifileClass.expected.kt b/idea/testData/decompiler/decompiledTextJvm/MultifileClass.expected.kt new file mode 100644 index 00000000000..314d33793f9 --- /dev/null +++ b/idea/testData/decompiler/decompiledTextJvm/MultifileClass.expected.kt @@ -0,0 +1,12 @@ +// IntelliJ API Decompiler stub source generated from a class file +// Implementation of methods is not available + +package test + +public val p: kotlin.Int /* compiled code */ + +public fun f(): kotlin.Unit { /* compiled code */ } + +private var i: kotlin.Int /* compiled code */ + +public fun kotlin.Int.plus(i: kotlin.Int /* = compiled code */): kotlin.Int { /* compiled code */ } \ No newline at end of file diff --git a/idea/testData/decompiler/decompiledTextJvm/MultifileClass/MultifileClass.kt b/idea/testData/decompiler/decompiledTextJvm/MultifileClass/MultifileClass.kt new file mode 100644 index 00000000000..ac7a21c0fd0 --- /dev/null +++ b/idea/testData/decompiler/decompiledTextJvm/MultifileClass/MultifileClass.kt @@ -0,0 +1,10 @@ +@file:JvmName("MultifileClass") +@file:JvmMultifileClass +package test + +private var i = 2 + +fun Int.plus(i: Int = 1) = this + i + +class ShouldNotBeVisible1 +interface ShouldNotBeVisible2 diff --git a/idea/testData/decompiler/decompiledTextJvm/MultifileClass/andSomeMore.kt b/idea/testData/decompiler/decompiledTextJvm/MultifileClass/andSomeMore.kt new file mode 100644 index 00000000000..8b77dc6586a --- /dev/null +++ b/idea/testData/decompiler/decompiledTextJvm/MultifileClass/andSomeMore.kt @@ -0,0 +1,6 @@ +@file:JvmName("MultifileClass") +@file:JvmMultifileClass +package test + +fun f() {} +val p = 3 \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/JvmDecompiledTextTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/JvmDecompiledTextTestGenerated.java index d47b6c94b0f..03152dace4d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/JvmDecompiledTextTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/decompiler/textBuilder/JvmDecompiledTextTestGenerated.java @@ -35,6 +35,12 @@ public class JvmDecompiledTextTestGenerated extends AbstractJvmDecompiledTextTes JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/decompiler/decompiledTextJvm"), Pattern.compile("^([^\\.]+)$"), true); } + @TestMetadata("MultifileClass") + public void testMultifileClass() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/decompiler/decompiledTextJvm/MultifileClass/"); + doTest(fileName); + } + @TestMetadata("TestKt") public void testTestKt() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/decompiler/decompiledTextJvm/TestKt/");