Extract common parts of built-ins and JS decompilers

This commit is contained in:
Alexander Udalov
2017-02-01 16:39:07 +03:00
parent b52b90c182
commit 297eb952bc
7 changed files with 184 additions and 202 deletions
@@ -31,6 +31,9 @@ class BuiltInsBinaryVersion(vararg numbers: Int) : BinaryVersion(*numbers) {
@JvmField
val INSTANCE = BuiltInsBinaryVersion(1, 0, 0)
@JvmField
val INVALID_VERSION = BuiltInsBinaryVersion()
fun readFrom(stream: InputStream): BuiltInsBinaryVersion {
val dataInput = DataInputStream(stream)
return BuiltInsBinaryVersion(*(1..dataInput.readInt()).map { dataInput.readInt() }.toIntArray())
@@ -18,130 +18,59 @@ package org.jetbrains.kotlin.idea.decompiler.builtIns
import com.intellij.ide.highlighter.JavaClassFileType
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.FileViewProvider
import com.intellij.psi.PsiManager
import com.intellij.psi.compiled.ClassFileDecompilers
import org.jetbrains.annotations.TestOnly
import org.jetbrains.kotlin.builtins.BuiltInSerializerProtocol
import org.jetbrains.kotlin.builtins.BuiltInsBinaryVersion
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.decompiler.KotlinDecompiledFileViewProvider
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
import org.jetbrains.kotlin.idea.decompiler.common.KotlinMetadataDeserializerForDecompiler
import org.jetbrains.kotlin.idea.decompiler.common.createIncompatibleAbiVersionDecompiledText
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText
import org.jetbrains.kotlin.idea.decompiler.textBuilder.buildDecompiledText
import org.jetbrains.kotlin.idea.decompiler.textBuilder.defaultDecompilerRendererOptions
import org.jetbrains.kotlin.idea.decompiler.common.FileWithMetadata
import org.jetbrains.kotlin.idea.decompiler.common.KotlinMetadataDecompiler
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.builtins.BuiltInsProtoBuf
import org.jetbrains.kotlin.serialization.deserialization.ClassDeserializer
import org.jetbrains.kotlin.serialization.deserialization.FlexibleTypeDeserializer
import org.jetbrains.kotlin.serialization.deserialization.MetadataPackageFragment
import org.jetbrains.kotlin.serialization.deserialization.NameResolverImpl
import org.jetbrains.kotlin.utils.addIfNotNull
import java.io.ByteArrayInputStream
class KotlinBuiltInDecompiler : ClassFileDecompilers.Full() {
private val stubBuilder = KotlinBuiltInStubBuilder()
override fun accepts(file: VirtualFile): Boolean {
return file.fileType == KotlinBuiltInFileType
}
override fun getStubBuilder() = stubBuilder
override fun createFileViewProvider(file: VirtualFile, manager: PsiManager, physical: Boolean): FileViewProvider {
return KotlinDecompiledFileViewProvider(manager, file, physical) { provider ->
if (BuiltInDefinitionFile.read(provider.virtualFile) == null) {
null
}
else {
KtDecompiledFile(provider) { file ->
buildDecompiledTextForBuiltIns(file)
}
}
}
class KotlinBuiltInDecompiler : KotlinMetadataDecompiler<BuiltInsBinaryVersion>(
KotlinBuiltInFileType, KotlinBuiltInStubBuilder(), TargetPlatform.Default, BuiltInSerializerProtocol,
FlexibleTypeDeserializer.ThrowException, BuiltInsBinaryVersion.INSTANCE, BuiltInsBinaryVersion.INVALID_VERSION
) {
override fun readFile(bytes: ByteArray, file: VirtualFile): FileWithMetadata? {
return BuiltInDefinitionFile.read(bytes, file)
}
}
private val decompilerRendererForBuiltIns = DescriptorRenderer.withOptions { defaultDecompilerRendererOptions() }
fun buildDecompiledTextForBuiltIns(builtInFile: VirtualFile): DecompiledText {
if (builtInFile.fileType != KotlinBuiltInFileType) {
error("Unexpected file type ${builtInFile.fileType}")
}
val file = BuiltInDefinitionFile.read(builtInFile)
?: error("Unexpectedly empty built-in file: $builtInFile")
when (file) {
is BuiltInDefinitionFile.Incompatible -> {
return createIncompatibleAbiVersionDecompiledText(BuiltInsBinaryVersion.INSTANCE, file.version)
}
is BuiltInDefinitionFile.Compatible -> {
val packageFqName = file.packageFqName
val resolver = KotlinMetadataDeserializerForDecompiler(
packageFqName, file.proto, file.nameResolver,
TargetPlatform.Default, BuiltInSerializerProtocol, FlexibleTypeDeserializer.ThrowException
)
val declarations = arrayListOf<DeclarationDescriptor>()
declarations.addAll(resolver.resolveDeclarationsInFacade(packageFqName))
for (classProto in file.classesToDecompile) {
val classId = file.nameResolver.getClassId(classProto.fqName)
declarations.addIfNotNull(resolver.resolveTopLevelClass(classId))
class BuiltInDefinitionFile(
proto: ProtoBuf.PackageFragment,
val packageDirectory: VirtualFile,
val isMetadata: Boolean
) : FileWithMetadata.Compatible(proto, BuiltInSerializerProtocol) {
override val classesToDecompile: List<ProtoBuf.Class>
get() = super.classesToDecompile.let { classes ->
if (isMetadata || !FILTER_OUT_CLASSES_EXISTING_AS_JVM_CLASS_FILES) classes
else classes.filter { classProto ->
shouldDecompileBuiltInClass(nameResolver.getClassId(classProto.fqName))
}
return buildDecompiledText(packageFqName, declarations, decompilerRendererForBuiltIns)
}
}
}
sealed class BuiltInDefinitionFile {
class Incompatible(val version: BuiltInsBinaryVersion) : BuiltInDefinitionFile()
class Compatible(val proto: ProtoBuf.PackageFragment,
val packageDirectory: VirtualFile,
val isMetadata: Boolean) : BuiltInDefinitionFile() {
val nameResolver = NameResolverImpl(proto.strings, proto.qualifiedNames)
val packageFqName = nameResolver.getPackageFqName(proto.`package`.getExtension(BuiltInsProtoBuf.packageFqName))
val classesToDecompile =
proto.class_List.filter { proto ->
val classId = nameResolver.getClassId(proto.fqName)
!classId.isNestedClass && classId !in ClassDeserializer.BLACK_LIST
}.let { classes ->
if (isMetadata || !FILTER_OUT_CLASSES_EXISTING_AS_JVM_CLASS_FILES) classes
else classes.filter { classProto ->
shouldDecompileBuiltInClass(nameResolver.getClassId(classProto.fqName))
}
}
private fun shouldDecompileBuiltInClass(classId: ClassId): Boolean {
val realJvmClassFileName = classId.shortClassName.asString() + "." + JavaClassFileType.INSTANCE.defaultExtension
return packageDirectory.findChild(realJvmClassFileName) == null
}
private fun shouldDecompileBuiltInClass(classId: ClassId): Boolean {
val realJvmClassFileName = classId.shortClassName.asString() + "." + JavaClassFileType.INSTANCE.defaultExtension
return packageDirectory.findChild(realJvmClassFileName) == null
}
companion object {
var FILTER_OUT_CLASSES_EXISTING_AS_JVM_CLASS_FILES = true
@TestOnly set
fun read(file: VirtualFile): BuiltInDefinitionFile? {
return read(file.contentsToByteArray(), file)
}
fun read(contents: ByteArray, file: VirtualFile): BuiltInDefinitionFile? {
fun read(contents: ByteArray, file: VirtualFile): FileWithMetadata? {
val stream = ByteArrayInputStream(contents)
val version = BuiltInsBinaryVersion.readFrom(stream)
if (!version.isCompatible()) {
return BuiltInDefinitionFile.Incompatible(version)
return FileWithMetadata.Incompatible(version)
}
val proto = ProtoBuf.PackageFragment.parseFrom(stream, BuiltInSerializerProtocol.extensionRegistry)
val result = BuiltInDefinitionFile.Compatible(proto, file.parent, file.extension == MetadataPackageFragment.METADATA_FILE_EXTENSION)
val result = BuiltInDefinitionFile(proto, file.parent, file.extension == MetadataPackageFragment.METADATA_FILE_EXTENSION)
val packageProto = result.proto.`package`
if (result.classesToDecompile.isEmpty() &&
packageProto.typeAliasCount == 0 && packageProto.functionCount == 0 && packageProto.propertyCount == 0) {
@@ -21,10 +21,11 @@ import com.intellij.psi.impl.compiled.ClassFileStubBuilder
import com.intellij.psi.stubs.PsiFileStub
import com.intellij.util.indexing.FileContent
import org.jetbrains.kotlin.builtins.BuiltInSerializerProtocol
import org.jetbrains.kotlin.serialization.deserialization.ProtoBasedClassDataFinder
import org.jetbrains.kotlin.idea.decompiler.common.AnnotationLoaderForStubBuilderImpl
import org.jetbrains.kotlin.idea.decompiler.common.FileWithMetadata
import org.jetbrains.kotlin.idea.decompiler.stubBuilder.*
import org.jetbrains.kotlin.psi.stubs.KotlinStubVersions
import org.jetbrains.kotlin.serialization.deserialization.ProtoBasedClassDataFinder
import org.jetbrains.kotlin.serialization.deserialization.ProtoContainer
import org.jetbrains.kotlin.serialization.deserialization.TypeTable
@@ -37,10 +38,10 @@ class KotlinBuiltInStubBuilder : ClsStubBuilder() {
val file = BuiltInDefinitionFile.read(content.content, virtualFile) ?: return null
when (file) {
is BuiltInDefinitionFile.Incompatible -> {
is FileWithMetadata.Incompatible -> {
return createIncompatibleAbiVersionFileStub()
}
is BuiltInDefinitionFile.Compatible -> {
is FileWithMetadata.Compatible -> {
val packageProto = file.proto.`package`
val packageFqName = file.packageFqName
val nameResolver = file.nameResolver
@@ -0,0 +1,133 @@
/*
* Copyright 2010-2017 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.idea.decompiler.common
import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.FileViewProvider
import com.intellij.psi.PsiManager
import com.intellij.psi.compiled.ClassFileDecompilers
import com.intellij.psi.compiled.ClsStubBuilder
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.decompiler.KotlinDecompiledFileViewProvider
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText
import org.jetbrains.kotlin.idea.decompiler.textBuilder.buildDecompiledText
import org.jetbrains.kotlin.idea.decompiler.textBuilder.defaultDecompilerRendererOptions
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.SerializerExtensionProtocol
import org.jetbrains.kotlin.serialization.deserialization.BinaryVersion
import org.jetbrains.kotlin.serialization.deserialization.ClassDeserializer
import org.jetbrains.kotlin.serialization.deserialization.FlexibleTypeDeserializer
import org.jetbrains.kotlin.serialization.deserialization.NameResolverImpl
import org.jetbrains.kotlin.utils.addIfNotNull
import java.io.IOException
abstract class KotlinMetadataDecompiler<out V : BinaryVersion>(
private val fileType: FileType,
private val stubBuilder: ClsStubBuilder,
private val targetPlatform: TargetPlatform,
private val serializerProtocol: SerializerExtensionProtocol,
private val flexibleTypeDeserializer: FlexibleTypeDeserializer,
private val expectedBinaryVersion: V,
private val invalidBinaryVersion: V
) : ClassFileDecompilers.Full() {
private val renderer = DescriptorRenderer.withOptions { defaultDecompilerRendererOptions() }
abstract fun readFile(bytes: ByteArray, file: VirtualFile): FileWithMetadata?
override fun accepts(file: VirtualFile) = file.fileType == fileType
override fun getStubBuilder() = stubBuilder
override fun createFileViewProvider(file: VirtualFile, manager: PsiManager, physical: Boolean): FileViewProvider {
return KotlinDecompiledFileViewProvider(manager, file, physical) { provider ->
if (readFile(provider.virtualFile) == null) {
null
}
else {
KtDecompiledFile(provider, this::buildDecompiledText)
}
}
}
private fun readFile(file: VirtualFile): FileWithMetadata? {
if (!file.isValid) return null
return try {
readFile(file.contentsToByteArray(), file)
}
catch (e: IOException) {
// This is needed because sometimes we're given VirtualFile instances that point to non-existent .jar entries.
// Such files are valid (isValid() returns true), but an attempt to read their contents results in a FileNotFoundException.
// Note that although calling "refresh()" instead of catching an exception would seem more correct here,
// it's not always allowed and also is likely to degrade performance
null
}
}
private fun buildDecompiledText(virtualFile: VirtualFile): DecompiledText {
if (virtualFile.fileType != fileType) {
error("Unexpected file type ${virtualFile.fileType}")
}
val file = readFile(virtualFile)
return when (file) {
null -> {
createIncompatibleAbiVersionDecompiledText(expectedBinaryVersion, invalidBinaryVersion)
}
is FileWithMetadata.Incompatible -> {
createIncompatibleAbiVersionDecompiledText(expectedBinaryVersion, file.version)
}
is FileWithMetadata.Compatible -> {
val packageFqName = file.packageFqName
val resolver = KotlinMetadataDeserializerForDecompiler(
packageFqName, file.proto, file.nameResolver,
targetPlatform, serializerProtocol, flexibleTypeDeserializer
)
val declarations = arrayListOf<DeclarationDescriptor>()
declarations.addAll(resolver.resolveDeclarationsInFacade(packageFqName))
for (classProto in file.classesToDecompile) {
val classId = file.nameResolver.getClassId(classProto.fqName)
declarations.addIfNotNull(resolver.resolveTopLevelClass(classId))
}
buildDecompiledText(packageFqName, declarations, renderer)
}
}
}
}
sealed class FileWithMetadata {
class Incompatible(val version: BinaryVersion) : FileWithMetadata()
open class Compatible(
val proto: ProtoBuf.PackageFragment,
serializerProtocol: SerializerExtensionProtocol
) : FileWithMetadata() {
val nameResolver = NameResolverImpl(proto.strings, proto.qualifiedNames)
val packageFqName = nameResolver.getPackageFqName(proto.`package`.getExtension(serializerProtocol.packageFqName))
open val classesToDecompile: List<ProtoBuf.Class> =
proto.class_List.filter { proto ->
val classId = nameResolver.getClassId(proto.fqName)
!classId.isNestedClass && classId !in ClassDeserializer.BLACK_LIST
}
}
}
@@ -17,123 +17,38 @@
package org.jetbrains.kotlin.idea.decompiler.js
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.FileViewProvider
import com.intellij.psi.PsiManager
import com.intellij.psi.compiled.ClassFileDecompilers
import org.jetbrains.kotlin.builtins.BuiltInSerializerProtocol
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.decompiler.KotlinDecompiledFileViewProvider
import org.jetbrains.kotlin.idea.decompiler.KtDecompiledFile
import org.jetbrains.kotlin.idea.decompiler.common.KotlinMetadataDeserializerForDecompiler
import org.jetbrains.kotlin.idea.decompiler.common.createIncompatibleAbiVersionDecompiledText
import org.jetbrains.kotlin.idea.decompiler.textBuilder.DecompiledText
import org.jetbrains.kotlin.idea.decompiler.textBuilder.buildDecompiledText
import org.jetbrains.kotlin.idea.decompiler.textBuilder.defaultDecompilerRendererOptions
import org.jetbrains.kotlin.idea.decompiler.common.FileWithMetadata
import org.jetbrains.kotlin.idea.decompiler.common.KotlinMetadataDecompiler
import org.jetbrains.kotlin.js.resolve.JsPlatform
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.deserialization.FlexibleTypeDeserializer
import org.jetbrains.kotlin.serialization.deserialization.NameResolverImpl
import org.jetbrains.kotlin.serialization.js.DynamicTypeDeserializer
import org.jetbrains.kotlin.serialization.js.JsProtoBuf
import org.jetbrains.kotlin.serialization.js.JsSerializerProtocol
import org.jetbrains.kotlin.utils.JsMetadataVersion
import org.jetbrains.kotlin.utils.addIfNotNull
import java.io.ByteArrayInputStream
import java.io.IOException
class KotlinJavaScriptMetaFileDecompiler : ClassFileDecompilers.Full() {
private val stubBuilder = KotlinJavaScriptStubBuilder()
override fun accepts(file: VirtualFile): Boolean {
return file.fileType == KotlinJavaScriptMetaFileType
}
override fun getStubBuilder() = stubBuilder
override fun createFileViewProvider(file: VirtualFile, manager: PsiManager, physical: Boolean): FileViewProvider {
return KotlinDecompiledFileViewProvider(manager, file, physical) { provider ->
KtDecompiledFile(provider, ::buildDecompiledTextFromJsMetadata)
}
class KotlinJavaScriptMetaFileDecompiler : KotlinMetadataDecompiler<JsMetadataVersion>(
KotlinJavaScriptMetaFileType, KotlinJavaScriptStubBuilder(), JsPlatform, JsSerializerProtocol, DynamicTypeDeserializer,
JsMetadataVersion.INSTANCE, JsMetadataVersion.INVALID_VERSION
) {
override fun readFile(bytes: ByteArray, file: VirtualFile): FileWithMetadata? {
return KjsmFile.read(bytes)
}
}
private val decompilerRendererForJS = DescriptorRenderer.withOptions { defaultDecompilerRendererOptions() }
// TODO: deduplicate code with KotlinBuiltInDecompiler
fun buildDecompiledTextFromJsMetadata(kjsmFile: VirtualFile): DecompiledText {
if (kjsmFile.fileType != KotlinJavaScriptMetaFileType) {
error("Unexpected file type ${kjsmFile.fileType}")
}
val file = KjsmFile.read(kjsmFile)
when (file) {
null -> {
return createIncompatibleAbiVersionDecompiledText(JsMetadataVersion.INSTANCE, JsMetadataVersion.INVALID_VERSION)
}
is KjsmFile.Incompatible -> {
return createIncompatibleAbiVersionDecompiledText(JsMetadataVersion.INSTANCE, file.version)
}
is KjsmFile.Compatible -> {
val packageFqName = file.packageFqName
val resolver = KotlinMetadataDeserializerForDecompiler(
packageFqName, file.proto, file.nameResolver,
JsPlatform, JsSerializerProtocol, DynamicTypeDeserializer
)
val declarations = arrayListOf<DeclarationDescriptor>()
declarations.addAll(resolver.resolveDeclarationsInFacade(packageFqName))
for (klass in file.classesToDecompile) {
val classId = file.nameResolver.getClassId(klass.fqName)
declarations.addIfNotNull(resolver.resolveTopLevelClass(classId))
}
return buildDecompiledText(packageFqName, declarations, decompilerRendererForJS)
}
}
}
// TODO: deduplicate code with BuiltInDefinitionFile
sealed class KjsmFile {
class Incompatible(val version: JsMetadataVersion) : KjsmFile()
class Compatible(val header: JsProtoBuf.Header, val proto: ProtoBuf.PackageFragment) : KjsmFile() {
val packageFqName = FqName(header.packageFqName)
val nameResolver = NameResolverImpl(proto.strings, proto.qualifiedNames)
val classesToDecompile =
proto.class_List.filterNot { proto -> nameResolver.getClassId(proto.fqName).isNestedClass }
}
class KjsmFile(proto: ProtoBuf.PackageFragment) : FileWithMetadata.Compatible(proto, JsSerializerProtocol) {
companion object {
fun read(file: VirtualFile): KjsmFile? {
val stream = ByteArrayInputStream(readFileContentsSafely(file) ?: return null)
fun read(bytes: ByteArray): FileWithMetadata? {
val stream = ByteArrayInputStream(bytes)
val version = JsMetadataVersion.readFrom(stream)
if (!version.isCompatible()) {
return Incompatible(version)
return FileWithMetadata.Incompatible(version)
}
val header = JsProtoBuf.Header.parseDelimitedFrom(stream)
val proto = ProtoBuf.PackageFragment.parseFrom(stream, JsSerializerProtocol.extensionRegistry)
JsProtoBuf.Header.parseDelimitedFrom(stream)
return Compatible(header, proto)
}
private fun readFileContentsSafely(file: VirtualFile): ByteArray? {
if (!file.isValid) return null
return try {
file.contentsToByteArray()
}
catch (e: IOException) {
// This is needed because sometimes we're given VirtualFile instances that point to non-existent .jar entries.
// Such files are valid (isValid() returns true), but an attempt to read their contents results in a FileNotFoundException.
// Note that although calling "refresh()" instead of catching an exception would seem more correct here,
// it's not always allowed and also is likely to degrade performance
null
}
return KjsmFile(ProtoBuf.PackageFragment.parseFrom(stream, JsSerializerProtocol.extensionRegistry))
}
}
}
@@ -20,10 +20,11 @@ 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.serialization.deserialization.ProtoBasedClassDataFinder
import org.jetbrains.kotlin.idea.decompiler.common.AnnotationLoaderForStubBuilderImpl
import org.jetbrains.kotlin.idea.decompiler.common.FileWithMetadata
import org.jetbrains.kotlin.idea.decompiler.stubBuilder.*
import org.jetbrains.kotlin.psi.stubs.KotlinStubVersions
import org.jetbrains.kotlin.serialization.deserialization.ProtoBasedClassDataFinder
import org.jetbrains.kotlin.serialization.deserialization.ProtoContainer
import org.jetbrains.kotlin.serialization.deserialization.TypeTable
import org.jetbrains.kotlin.serialization.js.JsSerializerProtocol
@@ -35,13 +36,13 @@ class KotlinJavaScriptStubBuilder : ClsStubBuilder() {
override fun buildFileStub(content: FileContent): PsiFileStub<*>? {
val virtualFile = content.file
assert(virtualFile.fileType == KotlinJavaScriptMetaFileType) { "Unexpected file type ${virtualFile.fileType}" }
val file = KjsmFile.read(virtualFile) ?: return null
val file = KjsmFile.read(content.content) ?: return null
when (file) {
is KjsmFile.Incompatible -> {
is FileWithMetadata.Incompatible -> {
return createIncompatibleAbiVersionFileStub()
}
is KjsmFile.Compatible -> {
is FileWithMetadata.Compatible -> {
val packageProto = file.proto.`package`
val packageFqName = file.packageFqName
val nameResolver = file.nameResolver
@@ -127,7 +127,7 @@ open class KotlinMetadataFileIndexBase<T>(classOfIndex: Class<T>, indexFunction:
private val INDEXER = indexer { fileContent ->
if (fileContent.fileType == KotlinBuiltInFileType && fileContent.fileName.endsWith(MetadataPackageFragment.DOT_METADATA_FILE_EXTENSION)) {
val builtins = BuiltInDefinitionFile.read(fileContent.content, fileContent.file.parent)
(builtins as? BuiltInDefinitionFile.Compatible)?.let { builtinDefFile ->
(builtins as? BuiltInDefinitionFile)?.let { builtinDefFile ->
val proto = builtinDefFile.proto
proto.class_List.singleOrNull()?.let { cls ->
indexFunction(builtinDefFile.nameResolver.getClassId(cls.fqName))