[K2, IDE] Don't filter declarations from built-ins in K2 decompiler
Intercept decompiler calls to process files that belong to classpath builtins from K2 IDE differently. Their content won't be filtered w.r.t. JVM logic during decompilation. Split stub versions for .kotlin_builtins with K1 and K2 IDE. K2 currently uses a single shared symbol provider for builtins of all platforms. KotlinBuiltInDecompiler filters out duplicated declarations contained in JVM classfiles as required for K1. For K2 this logic doesn't fit: non-JVM modules (common, in particular) rely on .kotlin_builtins, because kotlin-stdlib-common doesn't contain .kotlin_builtins or .knm files for some of the built-in declarations (e.g., kotlin.Unit). IDE has to restore the missing declarations using built-ins from classloader. Filtering declarations from there using K1 logic breaks resolution of these declarations inside common modules in IDE. The change shouldn't affect JVM modules as are able to resolve all builtin symbols from the standard library before falling back to built-ins. KT-61757
This commit is contained in:
committed by
Space Team
parent
c38887ae68
commit
cf849835ba
+35
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.analysis.decompiler.psi
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.analysis.decompiler.stub.file.KotlinMetadataStubBuilder
|
||||
|
||||
/**
|
||||
* Application service that adds a constant offset to the stub version of .kotlin_builtins files.
|
||||
* The purpose of this offset is to rebuild the decompiled text and the stubs for .kotlin_builtins files after K1 <-> K2 IDE switches.
|
||||
* K1 and K2 provide different sets of declarations from .kotlin_builtins files under certain conditions,
|
||||
* see [org.jetbrains.kotlin.analysis.decompiler.psi.BuiltInDefinitionFile].
|
||||
* Not forcing a rebuild for affected decompiled files and corresponding stubs leads to a stub error.
|
||||
*/
|
||||
interface KotlinBuiltInStubVersionOffsetProvider {
|
||||
fun getVersionOffset(): Int
|
||||
|
||||
companion object {
|
||||
fun getVersionOffset(): Int =
|
||||
ApplicationManager.getApplication().getService(KotlinBuiltInStubVersionOffsetProvider::class.java)?.getVersionOffset() ?: 0
|
||||
}
|
||||
}
|
||||
|
||||
interface KotlinBuiltInDecompilationInterceptor {
|
||||
fun readFile(bytes: ByteArray, file: VirtualFile): KotlinMetadataStubBuilder.FileWithMetadata?
|
||||
|
||||
companion object {
|
||||
fun readFile(bytes: ByteArray, file: VirtualFile): KotlinMetadataStubBuilder.FileWithMetadata? =
|
||||
ApplicationManager.getApplication().getService(KotlinBuiltInDecompilationInterceptor::class.java)?.readFile(bytes, file)
|
||||
}
|
||||
}
|
||||
+22
-6
@@ -23,19 +23,19 @@ import java.io.ByteArrayInputStream
|
||||
class KotlinBuiltInDecompiler : KotlinMetadataDecompiler<BuiltInsBinaryVersion>(
|
||||
KotlinBuiltInFileType, { BuiltInSerializerProtocol },
|
||||
FlexibleTypeDeserializer.ThrowException, { BuiltInsBinaryVersion.INSTANCE }, { BuiltInsBinaryVersion.INVALID_VERSION },
|
||||
KotlinStubVersions.BUILTIN_STUB_VERSION
|
||||
stubVersionForStubBuilderAndDecompiler,
|
||||
) {
|
||||
override val metadataStubBuilder: KotlinMetadataStubBuilder =
|
||||
KotlinBuiltInMetadataStubBuilder(::readFileSafely)
|
||||
|
||||
override fun readFile(bytes: ByteArray, file: VirtualFile): KotlinMetadataStubBuilder.FileWithMetadata? {
|
||||
return BuiltInDefinitionFile.read(bytes, file)
|
||||
return KotlinBuiltInDecompilationInterceptor.readFile(bytes, file) ?: BuiltInDefinitionFile.read(bytes, file)
|
||||
}
|
||||
}
|
||||
|
||||
private class KotlinBuiltInMetadataStubBuilder(
|
||||
readFile: (VirtualFile, ByteArray) -> FileWithMetadata?,
|
||||
) : KotlinMetadataStubBuilder(KotlinStubVersions.BUILTIN_STUB_VERSION, KotlinBuiltInFileType, { BuiltInSerializerProtocol }, readFile) {
|
||||
) : KotlinMetadataStubBuilder(stubVersionForStubBuilderAndDecompiler, KotlinBuiltInFileType, { BuiltInSerializerProtocol }, readFile) {
|
||||
override fun createCallableSource(file: FileWithMetadata.Compatible, filename: String): SourceElement? {
|
||||
val fileNameForFacade = when (val withoutExtension = filename.removeSuffix(BuiltInSerializerProtocol.DOT_DEFAULT_EXTENSION)) {
|
||||
// this is the filename used in stdlib, others should match
|
||||
@@ -48,15 +48,23 @@ private class KotlinBuiltInMetadataStubBuilder(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This version is used for .kotlin_builtins and is not used for .kotlin_metadata files:
|
||||
* K1 IDE and K2 IDE produce different decompiled files and stubs for .kotlin_builtins, but not for .kotlin_metadata
|
||||
*/
|
||||
private val stubVersionForStubBuilderAndDecompiler: Int
|
||||
get() = KotlinStubVersions.BUILTIN_STUB_VERSION + KotlinBuiltInStubVersionOffsetProvider.getVersionOffset()
|
||||
|
||||
class BuiltInDefinitionFile(
|
||||
proto: ProtoBuf.PackageFragment,
|
||||
version: BuiltInsBinaryVersion,
|
||||
val packageDirectory: VirtualFile,
|
||||
val isMetadata: Boolean,
|
||||
private val filterOutClassesExistingAsClassFiles: Boolean = true,
|
||||
) : KotlinMetadataStubBuilder.FileWithMetadata.Compatible(proto, version, BuiltInSerializerProtocol) {
|
||||
override val classesToDecompile: List<ProtoBuf.Class>
|
||||
get() = super.classesToDecompile.let { classes ->
|
||||
if (isMetadata || !FILTER_OUT_CLASSES_EXISTING_AS_JVM_CLASS_FILES) classes
|
||||
if (isMetadata || !FILTER_OUT_CLASSES_EXISTING_AS_JVM_CLASS_FILES || !filterOutClassesExistingAsClassFiles) classes
|
||||
else classes.filter { classProto ->
|
||||
shouldDecompileBuiltInClass(nameResolver.getClassId(classProto.fqName))
|
||||
}
|
||||
@@ -71,7 +79,11 @@ class BuiltInDefinitionFile(
|
||||
var FILTER_OUT_CLASSES_EXISTING_AS_JVM_CLASS_FILES = true
|
||||
@TestOnly set
|
||||
|
||||
fun read(contents: ByteArray, file: VirtualFile): KotlinMetadataStubBuilder.FileWithMetadata? {
|
||||
@JvmOverloads
|
||||
fun read(
|
||||
contents: ByteArray, file: VirtualFile,
|
||||
filterOutClassesExistingAsClassFiles: Boolean = true
|
||||
): KotlinMetadataStubBuilder.FileWithMetadata? {
|
||||
val stream = ByteArrayInputStream(contents)
|
||||
|
||||
val version = BuiltInsBinaryVersion.readFrom(stream)
|
||||
@@ -81,7 +93,11 @@ class BuiltInDefinitionFile(
|
||||
|
||||
val proto = ProtoBuf.PackageFragment.parseFrom(stream, BuiltInSerializerProtocol.extensionRegistry)
|
||||
val result =
|
||||
BuiltInDefinitionFile(proto, version, file.parent, file.extension == MetadataPackageFragment.METADATA_FILE_EXTENSION)
|
||||
BuiltInDefinitionFile(
|
||||
proto, version, file.parent,
|
||||
file.extension == MetadataPackageFragment.METADATA_FILE_EXTENSION,
|
||||
filterOutClassesExistingAsClassFiles
|
||||
)
|
||||
val packageProto = result.proto.`package`
|
||||
if (result.classesToDecompile.isEmpty() &&
|
||||
packageProto.typeAliasCount == 0 && packageProto.functionCount == 0 && packageProto.propertyCount == 0
|
||||
|
||||
Reference in New Issue
Block a user