[JVM] Add PSI <-> Bytecode declarations mapping to classfiles
diff --git a/compiler/arguments/resources/kotlin-compiler-arguments.json b/compiler/arguments/resources/kotlin-compiler-arguments.json index 52e961f..83d7c82 100644 --- a/compiler/arguments/resources/kotlin-compiler-arguments.json +++ b/compiler/arguments/resources/kotlin-compiler-arguments.json
@@ -6690,6 +6690,49 @@ } }, { + "name": "Xgenerate-psi-mapping", + "shortName": null, + "deprecatedName": null, + "description": { + "current": "Generate PSIMappingMetadata annotation with psi-to-bytecode mapping", + "valueInVersions": [] + }, + "delimiter": null, + "affectsCompilationOutcome": true, + "valueType": { + "type": "org.jetbrains.kotlin.arguments.dsl.types.BooleanType", + "isNullable": { + "current": false, + "valueInVersions": [] + }, + "defaultValue": { + "current": false, + "valueInVersions": [] + } + }, + "valueDescription": { + "current": null, + "valueInVersions": [] + }, + "releaseVersionsMetadata": { + "introducedVersion": "2.3.0", + "stabilizedVersion": null, + "deprecatedVersion": null, + "removedVersion": null + }, + "argumentType": { + "type": "org.jetbrains.kotlin.arguments.dsl.types.BooleanType", + "isNullable": { + "current": false, + "valueInVersions": [] + }, + "defaultValue": { + "current": false, + "valueInVersions": [] + } + } + }, + { "name": "Xgenerate-strict-metadata-version", "shortName": null, "deprecatedName": null,
diff --git a/compiler/arguments/src/org/jetbrains/kotlin/arguments/description/JvmCompilerArguments.kt b/compiler/arguments/src/org/jetbrains/kotlin/arguments/description/JvmCompilerArguments.kt index 66c966d..6cfa43c 100644 --- a/compiler/arguments/src/org/jetbrains/kotlin/arguments/description/JvmCompilerArguments.kt +++ b/compiler/arguments/src/org/jetbrains/kotlin/arguments/description/JvmCompilerArguments.kt
@@ -989,4 +989,15 @@ introducedVersion = KotlinReleaseVersion.v2_3_20, ) } + + + compilerArgument { + name = "Xgenerate-psi-mapping" + description = "Generate PSIMappingMetadata annotation with psi-to-bytecode mapping".asReleaseDependent() + valueType = BooleanType.defaultFalse + + lifecycle( + introducedVersion = KotlinReleaseVersion.v2_3_0 + ) + } }
diff --git a/compiler/backend.common.jvm/src/org/jetbrains/kotlin/PSIMetadataMappingEntry.kt b/compiler/backend.common.jvm/src/org/jetbrains/kotlin/PSIMetadataMappingEntry.kt new file mode 100644 index 0000000..74c7ac1 --- /dev/null +++ b/compiler/backend.common.jvm/src/org/jetbrains/kotlin/PSIMetadataMappingEntry.kt
@@ -0,0 +1,15 @@ +/* + * Copyright 2010-2025 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 + +class PSIMetadataMappingEntry( + val name: String, + val signature: String, + val file: String, + val packageName: String, + val startOffset: Int, + val endOffset: Int, +) \ No newline at end of file
diff --git a/compiler/backend.common.jvm/src/org/jetbrains/kotlin/codegen/AsmUtil.java b/compiler/backend.common.jvm/src/org/jetbrains/kotlin/codegen/AsmUtil.java index 1e91f84..b571c9c 100644 --- a/compiler/backend.common.jvm/src/org/jetbrains/kotlin/codegen/AsmUtil.java +++ b/compiler/backend.common.jvm/src/org/jetbrains/kotlin/codegen/AsmUtil.java
@@ -76,6 +76,12 @@ public static final String LOCAL_FUNCTION_VARIABLE_PREFIX = "$fun$"; + public static final String PSI_MAPPING_HEADER = "#PSI_MAPPING"; + + public static final String PSI_MAPPING_VERSION = "1.0"; + + public static int UTF_8_CONSTANT_MAX_LEN = 65535; + private static final ImmutableMap<Integer, JvmPrimitiveType> primitiveTypeByAsmSort; private static final ImmutableMap<Type, Type> primitiveTypeByBoxedType;
diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JvmBackendConfig.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JvmBackendConfig.kt index 252c798..a162162 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JvmBackendConfig.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JvmBackendConfig.kt
@@ -119,5 +119,7 @@ val generateDebugMetadataV2: Boolean = languageVersionSettings.apiVersion >= ApiVersion.KOTLIN_2_3 + val generatePsiMetadata: Boolean = configuration.getBoolean(JVMConfigurationKeys.GENERATE_PSI_MAPPING) + val implicitJvmExposeBoxed: Boolean = languageVersionSettings.getFlag(JvmAnalysisFlags.implicitJvmExposeBoxed) }
diff --git a/compiler/build-tools/kotlin-build-tools-api/api/kotlin-build-tools-api.api b/compiler/build-tools/kotlin-build-tools-api/api/kotlin-build-tools-api.api index 64b320d..571e274 100644 --- a/compiler/build-tools/kotlin-build-tools-api/api/kotlin-build-tools-api.api +++ b/compiler/build-tools/kotlin-build-tools-api/api/kotlin-build-tools-api.api
@@ -659,6 +659,7 @@ public static final field X_ENHANCED_COROUTINES_DEBUGGING Lorg/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments$JvmCompilerArgument; public static final field X_ENHANCE_TYPE_PARAMETER_TYPES_TO_DEF_NOT_NULL Lorg/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments$JvmCompilerArgument; public static final field X_FRIEND_PATHS Lorg/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments$JvmCompilerArgument; + public static final field X_GENERATE_PSI_MAPPING Lorg/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments$JvmCompilerArgument; public static final field X_GENERATE_STRICT_METADATA_VERSION Lorg/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments$JvmCompilerArgument; public static final field X_IGNORED_ANNOTATIONS_FOR_BRIDGES Lorg/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments$JvmCompilerArgument; public static final field X_INDY_ALLOW_ANNOTATED_LAMBDAS Lorg/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments$JvmCompilerArgument;
diff --git a/compiler/build-tools/kotlin-build-tools-api/gen/org/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments.kt b/compiler/build-tools/kotlin-build-tools-api/gen/org/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments.kt index 24b70e4..6e63e76 100644 --- a/compiler/build-tools/kotlin-build-tools-api/gen/org/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments.kt +++ b/compiler/build-tools/kotlin-build-tools-api/gen/org/jetbrains/kotlin/buildtools/api/arguments/JvmCompilerArguments.kt
@@ -265,6 +265,16 @@ JvmCompilerArgument("X_FRIEND_PATHS", KotlinReleaseVersion(1, 2, 70)) /** + * Generate PSIMappingMetadata annotation with psi-to-bytecode mapping + * + * WARNING: this option is EXPERIMENTAL and it may be changed in the future without notice or may be removed entirely. + */ + @JvmField + @ExperimentalCompilerArgument + public val X_GENERATE_PSI_MAPPING: JvmCompilerArgument<Boolean> = + JvmCompilerArgument("X_GENERATE_PSI_MAPPING", KotlinReleaseVersion(2, 3, 0)) + + /** * Generate metadata with strict version semantics (see the KDoc entry on 'Metadata.extraInt'). * * WARNING: this option is EXPERIMENTAL and it may be changed in the future without notice or may be removed entirely.
diff --git a/compiler/build-tools/kotlin-build-tools-impl/gen/org/jetbrains/kotlin/buildtools/internal/arguments/JvmCompilerArgumentsImpl.kt b/compiler/build-tools/kotlin-build-tools-impl/gen/org/jetbrains/kotlin/buildtools/internal/arguments/JvmCompilerArgumentsImpl.kt index b3218d9..e82f5ae 100644 --- a/compiler/build-tools/kotlin-build-tools-impl/gen/org/jetbrains/kotlin/buildtools/internal/arguments/JvmCompilerArgumentsImpl.kt +++ b/compiler/build-tools/kotlin-build-tools-impl/gen/org/jetbrains/kotlin/buildtools/internal/arguments/JvmCompilerArgumentsImpl.kt
@@ -57,6 +57,7 @@ import org.jetbrains.kotlin.buildtools.`internal`.arguments.JvmCompilerArgumentsImpl.Companion.X_ENHANCED_COROUTINES_DEBUGGING import org.jetbrains.kotlin.buildtools.`internal`.arguments.JvmCompilerArgumentsImpl.Companion.X_ENHANCE_TYPE_PARAMETER_TYPES_TO_DEF_NOT_NULL import org.jetbrains.kotlin.buildtools.`internal`.arguments.JvmCompilerArgumentsImpl.Companion.X_FRIEND_PATHS +import org.jetbrains.kotlin.buildtools.`internal`.arguments.JvmCompilerArgumentsImpl.Companion.X_GENERATE_PSI_MAPPING import org.jetbrains.kotlin.buildtools.`internal`.arguments.JvmCompilerArgumentsImpl.Companion.X_GENERATE_STRICT_METADATA_VERSION import org.jetbrains.kotlin.buildtools.`internal`.arguments.JvmCompilerArgumentsImpl.Companion.X_IGNORED_ANNOTATIONS_FOR_BRIDGES import org.jetbrains.kotlin.buildtools.`internal`.arguments.JvmCompilerArgumentsImpl.Companion.X_INDY_ALLOW_ANNOTATED_LAMBDAS @@ -195,6 +196,7 @@ if (X_ENHANCE_TYPE_PARAMETER_TYPES_TO_DEF_NOT_NULL in this) { arguments.enhanceTypeParameterTypesToDefNotNull = get(X_ENHANCE_TYPE_PARAMETER_TYPES_TO_DEF_NOT_NULL)} if (X_ENHANCED_COROUTINES_DEBUGGING in this) { arguments.enhancedCoroutinesDebugging = get(X_ENHANCED_COROUTINES_DEBUGGING)} if (X_FRIEND_PATHS in this) { arguments.friendPaths = get(X_FRIEND_PATHS).map { it.absolutePathStringOrThrow() }.toTypedArray()} + if (X_GENERATE_PSI_MAPPING in this) { arguments.generatePsiMapping = get(X_GENERATE_PSI_MAPPING)} if (X_GENERATE_STRICT_METADATA_VERSION in this) { arguments.strictMetadataVersionSemantics = get(X_GENERATE_STRICT_METADATA_VERSION)} if (X_IGNORED_ANNOTATIONS_FOR_BRIDGES in this) { arguments.ignoredAnnotationsForBridges = get(X_IGNORED_ANNOTATIONS_FOR_BRIDGES).toTypedArray()} if (X_INDY_ALLOW_ANNOTATED_LAMBDAS in this) { arguments.indyAllowAnnotatedLambdas = get(X_INDY_ALLOW_ANNOTATED_LAMBDAS)} @@ -281,6 +283,7 @@ try { this[X_ENHANCE_TYPE_PARAMETER_TYPES_TO_DEF_NOT_NULL] = arguments.enhanceTypeParameterTypesToDefNotNull } catch (_: NoSuchMethodError) { } try { this[X_ENHANCED_COROUTINES_DEBUGGING] = arguments.enhancedCoroutinesDebugging } catch (_: NoSuchMethodError) { } try { this[X_FRIEND_PATHS] = arguments.friendPaths.mapOrEmpty { Path(it) } } catch (_: NoSuchMethodError) { } + try { this[X_GENERATE_PSI_MAPPING] = arguments.generatePsiMapping } catch (_: NoSuchMethodError) { } try { this[X_GENERATE_STRICT_METADATA_VERSION] = arguments.strictMetadataVersionSemantics } catch (_: NoSuchMethodError) { } try { this[X_IGNORED_ANNOTATIONS_FOR_BRIDGES] = arguments.ignoredAnnotationsForBridges.toListOrEmpty() } catch (_: NoSuchMethodError) { } try { this[X_INDY_ALLOW_ANNOTATED_LAMBDAS] = arguments.indyAllowAnnotatedLambdas } catch (_: NoSuchMethodError) { } @@ -365,6 +368,7 @@ if (X_ENHANCE_TYPE_PARAMETER_TYPES_TO_DEF_NOT_NULL in this) { arguments.enhanceTypeParameterTypesToDefNotNull = get(X_ENHANCE_TYPE_PARAMETER_TYPES_TO_DEF_NOT_NULL)} if (X_ENHANCED_COROUTINES_DEBUGGING in this) { arguments.enhancedCoroutinesDebugging = get(X_ENHANCED_COROUTINES_DEBUGGING)} if (X_FRIEND_PATHS in this) { arguments.friendPaths = get(X_FRIEND_PATHS).map { it.absolutePathStringOrThrow() }.toTypedArray()} + if (X_GENERATE_PSI_MAPPING in this) { arguments.generatePsiMapping = get(X_GENERATE_PSI_MAPPING)} if (X_GENERATE_STRICT_METADATA_VERSION in this) { arguments.strictMetadataVersionSemantics = get(X_GENERATE_STRICT_METADATA_VERSION)} if (X_IGNORED_ANNOTATIONS_FOR_BRIDGES in this) { arguments.ignoredAnnotationsForBridges = get(X_IGNORED_ANNOTATIONS_FOR_BRIDGES).toTypedArray()} if (X_INDY_ALLOW_ANNOTATED_LAMBDAS in this) { arguments.indyAllowAnnotatedLambdas = get(X_INDY_ALLOW_ANNOTATED_LAMBDAS)} @@ -507,6 +511,9 @@ public val X_FRIEND_PATHS: JvmCompilerArgument<List<java.nio.`file`.Path>> = JvmCompilerArgument("X_FRIEND_PATHS") + public val X_GENERATE_PSI_MAPPING: JvmCompilerArgument<Boolean> = + JvmCompilerArgument("X_GENERATE_PSI_MAPPING") + public val X_GENERATE_STRICT_METADATA_VERSION: JvmCompilerArgument<Boolean> = JvmCompilerArgument("X_GENERATE_STRICT_METADATA_VERSION")
diff --git a/compiler/cli/cli-base/gen/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt b/compiler/cli/cli-base/gen/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt index d87f54e3..d44c352 100644 --- a/compiler/cli/cli-base/gen/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt +++ b/compiler/cli/cli-base/gen/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt
@@ -186,6 +186,16 @@ } @Argument( + value = "-Xgenerate-psi-mapping", + description = "Generate PSIMappingMetadata annotation with psi-to-bytecode mapping", + ) + var generatePsiMapping: Boolean = false + set(value) { + checkFrozen() + field = value + } + + @Argument( value = "-Xgenerate-strict-metadata-version", description = "Generate metadata with strict version semantics (see the KDoc entry on 'Metadata.extraInt').", )
diff --git a/compiler/cli/cli-base/gen/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArgumentsCopyGenerated.kt b/compiler/cli/cli-base/gen/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArgumentsCopyGenerated.kt index c0e8699..9cf5fff 100644 --- a/compiler/cli/cli-base/gen/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArgumentsCopyGenerated.kt +++ b/compiler/cli/cli-base/gen/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArgumentsCopyGenerated.kt
@@ -31,6 +31,7 @@ to.expression = from.expression to.friendPaths = from.friendPaths.copyOf() to.ignoredAnnotationsForBridges = from.ignoredAnnotationsForBridges.copyOf() + to.generatePsiMapping = from.generatePsiMapping to.includeRuntime = from.includeRuntime to.indyAllowAnnotatedLambdas = from.indyAllowAnnotatedLambdas to.inheritMultifileParts = from.inheritMultifileParts
diff --git a/compiler/cli/cli-jvm/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt b/compiler/cli/cli-jvm/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt index b94c954..e31682e 100644 --- a/compiler/cli/cli-jvm/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt +++ b/compiler/cli/cli-jvm/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt
@@ -313,6 +313,8 @@ ) module.configureFromArgs(arguments) + put(JVMConfigurationKeys.GENERATE_PSI_MAPPING, arguments.generatePsiMapping) + ModuleChunk(listOf(module)) } }
diff --git a/compiler/cli/cli-jvm/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt b/compiler/cli/cli-jvm/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt index a5dd0fd..6572be3 100644 --- a/compiler/cli/cli-jvm/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt +++ b/compiler/cli/cli-jvm/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt
@@ -127,6 +127,9 @@ "Supported modes: ${JvmWhenGenerationScheme.entries.joinToString { it.description }}" ) } + + val generatePsiMapping = arguments.generatePsiMapping + put(JVMConfigurationKeys.GENERATE_PSI_MAPPING, generatePsiMapping) } if (arguments.valueClasses) {
diff --git a/compiler/config.jvm/gen/org/jetbrains/kotlin/config/JVMConfigurationKeys.kt b/compiler/config.jvm/gen/org/jetbrains/kotlin/config/JVMConfigurationKeys.kt index 6bb7152..eef0706 100644 --- a/compiler/config.jvm/gen/org/jetbrains/kotlin/config/JVMConfigurationKeys.kt +++ b/compiler/config.jvm/gen/org/jetbrains/kotlin/config/JVMConfigurationKeys.kt
@@ -175,6 +175,9 @@ @JvmField val IGNORED_ANNOTATIONS_FOR_BRIDGES = CompilerConfigurationKey.create<List<String>>("IGNORED_ANNOTATIONS_FOR_BRIDGES") + @JvmField + val GENERATE_PSI_MAPPING = CompilerConfigurationKey.create<Boolean>("Generate PSIMappingMetadata annotation with psi-to-bytecode mapping") + } var CompilerConfiguration.outputDirectory: File? @@ -365,3 +368,9 @@ get() = getList(JVMConfigurationKeys.IGNORED_ANNOTATIONS_FOR_BRIDGES) set(value) { put(JVMConfigurationKeys.IGNORED_ANNOTATIONS_FOR_BRIDGES, value) } +var CompilerConfiguration.generatePsiMapping: Boolean + get() = getBoolean(JVMConfigurationKeys.GENERATE_PSI_MAPPING) + set(value) { put(JVMConfigurationKeys.GENERATE_PSI_MAPPING, value) } + + +
diff --git a/compiler/config/configuration-keys-generator/src/org/jetbrains/kotlin/config/keys/generator/JvmConfigurationKeysContainer.kt b/compiler/config/configuration-keys-generator/src/org/jetbrains/kotlin/config/keys/generator/JvmConfigurationKeysContainer.kt index 6f385d0..24abb2a 100644 --- a/compiler/config/configuration-keys-generator/src/org/jetbrains/kotlin/config/keys/generator/JvmConfigurationKeysContainer.kt +++ b/compiler/config/configuration-keys-generator/src/org/jetbrains/kotlin/config/keys/generator/JvmConfigurationKeysContainer.kt
@@ -60,4 +60,6 @@ val EXPRESSION_TO_EVALUATE by key<String>("Expression to evaluate in script mode.", throwOnNull = false) val WHEN_GENERATION_SCHEME by key<JvmWhenGenerationScheme>("Specifies generation scheme for type-checking 'when' expressions.") val IGNORED_ANNOTATIONS_FOR_BRIDGES by key<List<String>>("Annotations fqNames that shall be skipped while copying the annotations from the target to the bridge functions.") + val GENERATE_PSI_MAPPING by key<Boolean>("Generate PSIMappingMetadata annotation with psi-to-bytecode mapping") + }
diff --git a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/backend/Fir2IrFakeOverrideStrategy.kt b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/backend/Fir2IrFakeOverrideStrategy.kt index 4aa25f4..ec63af6 100644 --- a/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/backend/Fir2IrFakeOverrideStrategy.kt +++ b/compiler/fir/entrypoint/src/org/jetbrains/kotlin/fir/backend/Fir2IrFakeOverrideStrategy.kt
@@ -163,7 +163,10 @@ if (!fir2IrExtensions.shouldGenerateDelegatedMember(delegateTargetFromBaseType)) return when (overridableMember) { - is IrSimpleFunction -> overridableMember.updateDeclarationHeader() + is IrSimpleFunction -> { + overridableMember.updateDeclarationHeader() + overridableMember.offsetSourceForPSIMapping = delegateTargetFromBaseType + } is IrProperty -> { overridableMember.updateDeclarationHeader() overridableMember.getter?.updateDeclarationHeader()
diff --git a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/Fir2IrDataClassMembersGenerator.kt b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/Fir2IrDataClassMembersGenerator.kt index 9ffd53d..305d1eb 100644 --- a/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/Fir2IrDataClassMembersGenerator.kt +++ b/compiler/fir/fir2ir/src/org/jetbrains/kotlin/fir/backend/generators/Fir2IrDataClassMembersGenerator.kt
@@ -261,6 +261,7 @@ val valueParameter = constructorParameters[index - 1] val irProperty = irDataClassMembersGenerator.getProperty(valueParameter) irDataClassMembersGenerator.generateComponentFunction(irFunction, irProperty) + irFunction.offsetSourceForPSIMapping = irProperty } } }
diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt index 979efdc..a2ff2b7 100644 --- a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt +++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt
@@ -6,6 +6,7 @@ package org.jetbrains.kotlin.backend.jvm.codegen import com.intellij.util.ArrayUtil +import org.jetbrains.kotlin.PSIMetadataMappingEntry import org.jetbrains.kotlin.backend.common.lower.ANNOTATION_IMPLEMENTATION import org.jetbrains.kotlin.backend.jvm.* import org.jetbrains.kotlin.backend.jvm.codegen.AnnotationCodegen.Companion.annotationClass @@ -18,6 +19,9 @@ import org.jetbrains.kotlin.backend.jvm.metadata.MetadataSerializer import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap.mapKotlinToJava import org.jetbrains.kotlin.codegen.AsmUtil +import org.jetbrains.kotlin.codegen.AsmUtil.PSI_MAPPING_HEADER +import org.jetbrains.kotlin.codegen.AsmUtil.PSI_MAPPING_VERSION +import org.jetbrains.kotlin.codegen.AsmUtil.UTF_8_CONSTANT_MAX_LEN import org.jetbrains.kotlin.codegen.VersionIndependentOpcodes import org.jetbrains.kotlin.codegen.inline.* import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter @@ -28,6 +32,7 @@ import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibility import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.PsiSourceManager import org.jetbrains.kotlin.ir.builders.declarations.addFunction import org.jetbrains.kotlin.ir.declarations.* @@ -62,6 +67,7 @@ import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.jetbrains.org.objectweb.asm.* import org.jetbrains.org.objectweb.asm.commons.Method +import org.jetbrains.org.objectweb.asm.tree.MethodNode import java.io.File class ClassCodegen private constructor( @@ -155,17 +161,18 @@ generatePermittedSubclasses() } + val psiMetadataMappingEntries = mutableListOf<PSIMetadataMappingEntry>() + // Generating a method node may cause the addition of a field with an initializer if an inline function // call uses `assert` and the JVM assertions mode is enabled. To avoid concurrent modification errors, // there is a very specific generation order. // 1. Any method other than `<clinit>` can add a field and a `<clinit>` statement: for (method in irClass.declarations.filterIsInstance<IrFunction>()) { - if (method.name.asString() != "<clinit>" && - method.origin != IrDeclarationOrigin.INLINE_LAMBDA && - method.origin != IrDeclarationOrigin.ADAPTER_FOR_FUN_INTERFACE_CONSTRUCTOR && - !(method.origin == IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE && method.body == null) - ) { - generateMethod(method, smap) + if (method.name.asString() != "<clinit>" && method.origin != IrDeclarationOrigin.INLINE_LAMBDA && method.origin != IrDeclarationOrigin.ADAPTER_FOR_FUN_INTERFACE_CONSTRUCTOR && !(method.origin == IrDeclarationOrigin.ADAPTER_FOR_CALLABLE_REFERENCE && method.body == null)) { + val node = generateMethod(method, smap) ?: continue + if (config.generatePsiMetadata) { + collectPSIMetadata(method, node, psiMetadataMappingEntries) + } } } // 2. `<clinit>` itself can add a field, but the statement is generated via the `return init` hack: @@ -197,6 +204,99 @@ visitor.done(config.generateSmapCopyToAnnotation) jvmMethodSignatureClashDetector.reportErrorsTo(context.ktDiagnosticReporter) jvmFieldSignatureClashDetector.reportErrorsTo(context.ktDiagnosticReporter) + + if (config.generatePsiMetadata) { + writePSIMetadata(psiMetadataMappingEntries) + } + } + + private val IrElement.finalPSISource: IrElement + get() { + if (offsetSourceForPSIMapping == null) return this + val res = offsetSourceForPSIMapping!!.finalPSISource + offsetSourceForPSIMapping = res + return res + } + + private fun collectPSIMetadata( + method: IrFunction, + node: MethodNode, + entries: MutableList<PSIMetadataMappingEntry>, + ) { + val offsetSource = method.finalPSISource + if (offsetSource.startOffset < 0 || offsetSource.endOffset < 0) return + entries.add( + PSIMetadataMappingEntry( + node.name, + node.desc, + irClass.file.name, + irClass.file.packageFqName.asString(), + offsetSource.startOffset, + offsetSource.endOffset + ) + ) + } + + private fun writePSIMetadata(entries: List<PSIMetadataMappingEntry>) { + if (entries.isEmpty()) return + + val stringPool = linkedMapOf<String, Int>() + var stringPoolCurrentIndex = 0 + + fun addStringToPool(str: String) { + stringPool.computeIfAbsent(str) { stringPoolCurrentIndex++ } + } + + entries.forEach { entry -> + addStringToPool(entry.name) + addStringToPool(entry.signature) + addStringToPool(entry.file) + addStringToPool(entry.packageName) + } + + val psiMappingsParts = mutableListOf<StringBuilder>() + + var builderCounter = 0 + lateinit var currentBuilder: StringBuilder + + fun createNewPart(isFirst: Boolean = false) { + currentBuilder = StringBuilder().also { + it.appendLine("${PSI_MAPPING_HEADER}_${builderCounter++}") + if (isFirst) { + it.appendLine(PSI_MAPPING_VERSION) + it.appendLine("${stringPool.size}") + } + psiMappingsParts.add(it) + } + } + + createNewPart(isFirst = true) + + for (stringFromPool in stringPool.keys) { + if (currentBuilder.length + stringFromPool.length > UTF_8_CONSTANT_MAX_LEN) { + createNewPart() + } + currentBuilder.appendLine(stringFromPool) + } + + fun constantIndex(x: String) = stringPool[x] + + entries.forEach { entry -> + val encodedEntry = + "${constantIndex(entry.file)};${constantIndex(entry.packageName)};${constantIndex(entry.name)};${constantIndex(entry.signature)};${entry.startOffset};${entry.endOffset}" + if (currentBuilder.length + encodedEntry.length > UTF_8_CONSTANT_MAX_LEN) { + createNewPart() + } + currentBuilder.appendLine(encodedEntry) + } + + val psiMetadata = visitor.newAnnotation(PSI_MAPPING_METADATA_ASM_TYPE.descriptor, true) + val arrayVisitor = psiMetadata.visitArray("mappingParts") + psiMappingsParts.forEach { mappingPart -> + arrayVisitor.visit(null, mappingPart.toString()) + } + arrayVisitor.visitEnd() + psiMetadata.visitEnd() } private fun shouldSkipCodeGenerationAccordingToGenerationFilter(): Boolean { @@ -430,10 +530,10 @@ return SMAPAndMethodNode(cloneMethodNode(node), smap) } - private fun generateMethod(method: IrFunction, classSMAP: SourceMapper) { + private fun generateMethod(method: IrFunction, classSMAP: SourceMapper): MethodNode? { if (method.isFakeOverride) { jvmMethodSignatureClashDetector.trackFakeOverrideMethod(method) - return + return null } val (node, smap) = generateMethodNode(method) @@ -477,6 +577,8 @@ null -> Unit else -> error("Incorrect metadata source $metadata for:\n${method.dump()}") } + + return node } private fun generateInnerAndOuterClasses() {
diff --git a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt index be7cdeb..ad3bb82 100644 --- a/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt +++ b/compiler/ir/backend.jvm/codegen/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.backend.jvm.mapping.mapClass import org.jetbrains.kotlin.backend.jvm.mapping.mapSupertype import org.jetbrains.kotlin.builtins.StandardNames.FqNames +import org.jetbrains.kotlin.builtins.StandardNames.KOTLIN_INTERNAL_FQ_NAME import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.FrameMapBase @@ -22,6 +23,7 @@ import org.jetbrains.kotlin.codegen.inline.SourceMapper import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter import org.jetbrains.kotlin.codegen.state.JvmBackendConfig +import org.jetbrains.kotlin.codegen.topLevelClassAsmType import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.Modality @@ -35,6 +37,7 @@ import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.JvmStandardClassIds import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmClassSignature @@ -377,3 +380,6 @@ val callerTypeParam = caller.typeParameters.singleOrNull() ?: return false return calleeTypeArg.name == callerTypeParam.name && calleeTypeArg.parent.kotlinFqName == callerTypeParam.parent.kotlinFqName } + +val PSI_MAPPING_METADATA_ASM_TYPE: Type = KOTLIN_INTERNAL_FQ_NAME + .child(Name.identifier("PSIMappingMetadata")).topLevelClassAsmType() \ No newline at end of file
diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSyntheticAccessorGenerator.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSyntheticAccessorGenerator.kt index 8cb01c9..225c0df 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSyntheticAccessorGenerator.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSyntheticAccessorGenerator.kt
@@ -186,6 +186,7 @@ accessor.annotations += declaration.annotations declaration.annotations = emptyList() declaration.parameters.forEach { it.annotations = emptyList() } + accessor.offsetSourceForPSIMapping = declaration } }
diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmDefaultUtils.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmDefaultUtils.kt index 69a8f07..25cf56e 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmDefaultUtils.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/ir/JvmDefaultUtils.kt
@@ -123,6 +123,7 @@ dispatchReceiverParameter?.type = irClass.defaultType annotations = fakeOverride.annotations copyCorrespondingPropertyFrom(fakeOverride) + offsetSourceForPSIMapping = fakeOverride } }
diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/FakeOverrideCopier.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/FakeOverrideCopier.kt index 9f73511..c26614e 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/FakeOverrideCopier.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/FakeOverrideCopier.kt
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl import org.jetbrains.kotlin.ir.util.TypeRemapper import org.jetbrains.kotlin.ir.util.copyAnnotations +import org.jetbrains.kotlin.ir.util.offsetSourceForPSIMapping internal class FakeOverrideCopier( private val valueParameters: MutableMap<IrValueParameterSymbol, IrValueParameterSymbol>, @@ -46,6 +47,7 @@ } parameters = declaration.parameters.map { copyValueParameter(it, this) } returnType = typeRemapper.remapType(declaration.returnType) + offsetSourceForPSIMapping = declaration } }
diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt index 8b5b901..b81e152 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/AdditionalIrUtils.kt
@@ -363,3 +363,5 @@ fun IrClass.selectSAMOverriddenFunction(): IrSimpleFunction = selectSAMOverriddenFunctionOrNull() ?: error("${render()} should have a single abstract method to be a type of function reference") + +var IrElement.offsetSourceForPSIMapping: IrElement? by irAttribute(copyByDefault = false) \ No newline at end of file
diff --git a/libraries/stdlib/src/kotlin/internal/Annotations.kt b/libraries/stdlib/src/kotlin/internal/Annotations.kt index 5cb0181..4779784 100644 --- a/libraries/stdlib/src/kotlin/internal/Annotations.kt +++ b/libraries/stdlib/src/kotlin/internal/Annotations.kt
@@ -136,3 +136,7 @@ @Target(AnnotationTarget.FILE) @Retention(AnnotationRetention.SOURCE) internal annotation class SuppressBytecodeGeneration + +@Target(AnnotationTarget.CLASS) +@PublishedApi +internal annotation class PSIMappingMetadata(val mappingParts: Array<String>) \ No newline at end of file