JVM IR: generate ACC_SYNTHETIC for deprecated-hidden declarations correctly
Extract the logic that computes ACC_SYNTHETIC flag for deprecated declarations, and use `DeprecationResolver` as the old backend does in `DescriptorAsmUtil.getCommonCallableFlags`. Creating IR-based descriptors for each function to pass it there is a bit costly though, so as a small optimization, use `allOverridden` to check if anything in the method hierarchy is deprecated. Also optimize `allOverridden` for the case of linear inheritance which is far more common. #KT-43199 Fixed
This commit is contained in:
committed by
Alexander Udalov
parent
d9efc2d922
commit
0e91d3fcb0
@@ -8,12 +8,11 @@ package org.jetbrains.kotlin.backend.common.ir
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
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.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.builders.Scope
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addConstructor
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildReceiverParameter
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.buildTypeParameter
|
||||
@@ -602,22 +601,35 @@ private fun IrSimpleFunction.copyAndRenameConflictingTypeParametersFrom(
|
||||
val IrSymbol.isSuspend: Boolean
|
||||
get() = this is IrSimpleFunctionSymbol && owner.isSuspend
|
||||
|
||||
fun IrSimpleFunction.allOverridden(includeSelf: Boolean = false): Set<IrSimpleFunction> {
|
||||
val result = mutableSetOf<IrSimpleFunction>()
|
||||
fun IrSimpleFunction.allOverridden(includeSelf: Boolean = false): List<IrSimpleFunction> {
|
||||
val result = mutableListOf<IrSimpleFunction>()
|
||||
if (includeSelf) {
|
||||
computeAllOverridden(this, result)
|
||||
} else {
|
||||
for (override in overriddenSymbols) {
|
||||
computeAllOverridden(override.owner, result)
|
||||
result.add(this)
|
||||
}
|
||||
|
||||
var current = this
|
||||
while (true) {
|
||||
val overridden = current.overriddenSymbols
|
||||
when (overridden.size) {
|
||||
0 -> return result
|
||||
1 -> {
|
||||
current = overridden[0].owner
|
||||
result.add(current)
|
||||
}
|
||||
else -> {
|
||||
val resultSet = result.toMutableSet()
|
||||
computeAllOverridden(current, resultSet)
|
||||
return resultSet.toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun computeAllOverridden(function: IrSimpleFunction, result: MutableSet<IrSimpleFunction>) {
|
||||
if (result.add(function)) {
|
||||
for (override in function.overriddenSymbols) {
|
||||
computeAllOverridden(override.owner, result)
|
||||
for (overriddenSymbol in function.overriddenSymbols) {
|
||||
val override = overriddenSymbol.owner
|
||||
if (result.add(override)) {
|
||||
computeAllOverridden(override, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-8
@@ -10,7 +10,6 @@ import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.buildAssertionsDisabledField
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.hasAssertionsDisabledField
|
||||
import org.jetbrains.kotlin.builtins.StandardNames
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.inline.*
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
@@ -31,7 +30,6 @@ import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.load.java.JvmAnnotationNames
|
||||
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
|
||||
import org.jetbrains.kotlin.metadata.jvm.serialization.JvmStringTable
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.protobuf.MessageLite
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
|
||||
@@ -447,7 +445,7 @@ class ClassCodegen private constructor(
|
||||
|
||||
private val IrClass.flags: Int
|
||||
get() = origin.flags or getVisibilityAccessFlagForClass() or
|
||||
(if (annotations.hasAnnotation(StandardNames.FqNames.deprecated)) Opcodes.ACC_DEPRECATED else 0) or
|
||||
(if (isAnnotatedWithDeprecated) Opcodes.ACC_DEPRECATED else 0) or
|
||||
when {
|
||||
isAnnotationClass -> Opcodes.ACC_ANNOTATION or Opcodes.ACC_INTERFACE or Opcodes.ACC_ABSTRACT
|
||||
isInterface -> Opcodes.ACC_INTERFACE or Opcodes.ACC_ABSTRACT
|
||||
@@ -457,9 +455,10 @@ private val IrClass.flags: Int
|
||||
|
||||
private fun IrField.computeFieldFlags(context: JvmBackendContext, languageVersionSettings: LanguageVersionSettings): Int =
|
||||
origin.flags or visibility.flags or
|
||||
(correspondingPropertySymbol?.owner?.callableDeprecationFlags ?: 0) or
|
||||
(if (shouldHaveSpecialDeprecationFlag(context)) Opcodes.ACC_DEPRECATED else 0) or
|
||||
(if (annotations.hasAnnotation(KOTLIN_DEPRECATED)) Opcodes.ACC_DEPRECATED else 0) or
|
||||
(if (isDeprecatedCallable ||
|
||||
correspondingPropertySymbol?.owner?.isDeprecatedCallable == true ||
|
||||
shouldHaveSpecialDeprecationFlag(context)
|
||||
) Opcodes.ACC_DEPRECATED else 0) or
|
||||
(if (isFinal) Opcodes.ACC_FINAL else 0) or
|
||||
(if (isStatic) Opcodes.ACC_STATIC else 0) or
|
||||
(if (hasAnnotation(VOLATILE_ANNOTATION_FQ_NAME)) Opcodes.ACC_VOLATILE else 0) or
|
||||
@@ -474,8 +473,6 @@ private fun IrField.isPrivateCompanionFieldInInterface(languageVersionSettings:
|
||||
parentAsClass.isJvmInterface &&
|
||||
DescriptorVisibilities.isPrivate(parentAsClass.companionObject()!!.visibility)
|
||||
|
||||
private val KOTLIN_DEPRECATED = FqName("kotlin.Deprecated")
|
||||
|
||||
fun IrField.shouldHaveSpecialDeprecationFlag(context: JvmBackendContext): Boolean {
|
||||
return annotations.any { it.symbol == context.ir.symbols.javaLangDeprecatedConstructorWithDeprecatedFlag }
|
||||
}
|
||||
|
||||
+23
-6
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.allOverridden
|
||||
import org.jetbrains.kotlin.backend.common.ir.ir2string
|
||||
import org.jetbrains.kotlin.backend.common.lower.BOUND_RECEIVER_PARAMETER
|
||||
import org.jetbrains.kotlin.backend.common.lower.BOUND_VALUE_PARAMETER
|
||||
@@ -144,9 +145,9 @@ class FunctionCodegen(
|
||||
|
||||
private fun IrFunction.calculateMethodFlags(): Int {
|
||||
if (origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER) {
|
||||
return getVisibilityForDefaultArgumentStub() or Opcodes.ACC_SYNTHETIC or functionDeprecationFlags.let {
|
||||
if (this is IrConstructor) it else it or Opcodes.ACC_STATIC
|
||||
}
|
||||
return getVisibilityForDefaultArgumentStub() or Opcodes.ACC_SYNTHETIC or
|
||||
(if (isDeprecatedFunction) Opcodes.ACC_DEPRECATED else 0) or
|
||||
(if (this is IrConstructor) 0 else Opcodes.ACC_STATIC)
|
||||
}
|
||||
|
||||
val isVararg = valueParameters.lastOrNull()?.varargElementType != null && !isBridge()
|
||||
@@ -162,12 +163,17 @@ class FunctionCodegen(
|
||||
// TODO transform interface modality on lowering to DefaultImpls
|
||||
else -> if (parentAsClass.isJvmInterface && body == null) Opcodes.ACC_ABSTRACT else 0
|
||||
}
|
||||
val isSynthetic = origin.isSynthetic || hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) ||
|
||||
(isSuspend && DescriptorVisibilities.isPrivate(visibility) && !isInline) || isReifiable()
|
||||
val isSynthetic = origin.isSynthetic ||
|
||||
hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) ||
|
||||
(isSuspend && DescriptorVisibilities.isPrivate(visibility) && !isInline) ||
|
||||
isReifiable() ||
|
||||
isDeprecatedHidden()
|
||||
|
||||
val isStrict = hasAnnotation(STRICTFP_ANNOTATION_FQ_NAME)
|
||||
val isSynchronized = hasAnnotation(SYNCHRONIZED_ANNOTATION_FQ_NAME)
|
||||
|
||||
return getVisibilityAccessFlag() or modalityFlag or functionDeprecationFlags or
|
||||
return getVisibilityAccessFlag() or modalityFlag or
|
||||
(if (isDeprecatedFunction) Opcodes.ACC_DEPRECATED else 0) or
|
||||
(if (isStatic) Opcodes.ACC_STATIC else 0) or
|
||||
(if (isVararg) Opcodes.ACC_VARARGS else 0) or
|
||||
(if (isExternal) Opcodes.ACC_NATIVE else 0) or
|
||||
@@ -177,6 +183,17 @@ class FunctionCodegen(
|
||||
(if (isSynchronized) Opcodes.ACC_SYNCHRONIZED else 0)
|
||||
}
|
||||
|
||||
private fun IrFunction.isDeprecatedHidden(): Boolean {
|
||||
val mightBeDeprecated = if (this is IrSimpleFunction) {
|
||||
allOverridden(true).any {
|
||||
it.isAnnotatedWithDeprecated || it.correspondingPropertySymbol?.owner?.isAnnotatedWithDeprecated == true
|
||||
}
|
||||
} else {
|
||||
isAnnotatedWithDeprecated
|
||||
}
|
||||
return mightBeDeprecated && context.state.deprecationProvider.isDeprecatedHidden(toIrBasedDescriptor())
|
||||
}
|
||||
|
||||
private fun getThrownExceptions(function: IrFunction): List<String>? {
|
||||
if (context.state.languageVersionSettings.supportsFeature(LanguageFeature.DoNotGenerateThrowsForDelegatedKotlinMembers) &&
|
||||
function.origin == IrDeclarationOrigin.DELEGATED_MEMBER
|
||||
|
||||
+13
-19
@@ -14,16 +14,18 @@ import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.MultifileFacadeFileEntry
|
||||
import org.jetbrains.kotlin.builtins.StandardNames.FqNames
|
||||
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.FrameMapBase
|
||||
import org.jetbrains.kotlin.codegen.OwnerKind
|
||||
import org.jetbrains.kotlin.codegen.SourceInfo
|
||||
import org.jetbrains.kotlin.codegen.inline.SourceMapper
|
||||
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrGetEnumValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
@@ -338,16 +340,11 @@ fun IrClass.isOptionalAnnotationClass(): Boolean =
|
||||
isAnnotationClass &&
|
||||
hasAnnotation(ExpectedActualDeclarationChecker.OPTIONAL_EXPECTATION_FQ_NAME)
|
||||
|
||||
val IrDeclaration.callableDeprecationFlags: Int
|
||||
get() {
|
||||
val annotation = annotations.findAnnotation(FqNames.deprecated)
|
||||
?: return if ((this as? IrDeclaration)?.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_FOR_COMPATIBILITY)
|
||||
Opcodes.ACC_DEPRECATED
|
||||
else 0
|
||||
val isHidden = (annotation.getValueArgument(2) as? IrGetEnumValue)?.symbol?.owner
|
||||
?.name?.asString() == DeprecationLevel.HIDDEN.name
|
||||
return Opcodes.ACC_DEPRECATED or if (isHidden) Opcodes.ACC_SYNTHETIC else 0
|
||||
}
|
||||
val IrDeclaration.isAnnotatedWithDeprecated: Boolean
|
||||
get() = annotations.hasAnnotation(FqNames.deprecated)
|
||||
|
||||
val IrDeclaration.isDeprecatedCallable: Boolean
|
||||
get() = isAnnotatedWithDeprecated || origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_FOR_COMPATIBILITY
|
||||
|
||||
// We can't check for JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS because for interface methods
|
||||
// moved to DefaultImpls, origin is changed to DEFAULT_IMPLS
|
||||
@@ -355,12 +352,9 @@ val IrDeclaration.callableDeprecationFlags: Int
|
||||
val IrFunction.isSyntheticMethodForProperty: Boolean
|
||||
get() = name.asString().endsWith(JvmAbi.ANNOTATED_PROPERTY_METHOD_NAME_SUFFIX)
|
||||
|
||||
val IrFunction.functionDeprecationFlags: Int
|
||||
get() {
|
||||
val originFlags = if (isSyntheticMethodForProperty) Opcodes.ACC_DEPRECATED else 0
|
||||
val propertyFlags = (this as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.callableDeprecationFlags ?: 0
|
||||
return originFlags or propertyFlags or callableDeprecationFlags
|
||||
}
|
||||
val IrFunction.isDeprecatedFunction: Boolean
|
||||
get() = isSyntheticMethodForProperty || isDeprecatedCallable ||
|
||||
(this as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.isDeprecatedCallable == true
|
||||
|
||||
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||
val IrDeclaration.psiElement: PsiElement?
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
@Deprecated("")
|
||||
@Suppress("DEPRECATED_SINCE_KOTLIN_OUTSIDE_KOTLIN_SUBPACKAGE")
|
||||
@DeprecatedSinceKotlin(hiddenSince = "1.0")
|
||||
fun hidden() {}
|
||||
|
||||
open class Base {
|
||||
@Deprecated("", level = DeprecationLevel.HIDDEN)
|
||||
open fun f() {}
|
||||
}
|
||||
|
||||
class Derived : Base {
|
||||
@Deprecated("", level = DeprecationLevel.HIDDEN)
|
||||
constructor()
|
||||
|
||||
override fun f() {}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
@kotlin.Metadata
|
||||
public class Base {
|
||||
// source: 'hidden.kt'
|
||||
public method <init>(): void
|
||||
public synthetic deprecated @kotlin.Deprecated method f(): void
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class Derived {
|
||||
// source: 'hidden.kt'
|
||||
public synthetic deprecated @kotlin.Deprecated method <init>(): void
|
||||
public synthetic method f(): void
|
||||
}
|
||||
|
||||
@kotlin.Metadata
|
||||
public final class HiddenKt {
|
||||
// source: 'hidden.kt'
|
||||
public synthetic deprecated final static @kotlin.Deprecated @kotlin.DeprecatedSinceKotlin method hidden(): void
|
||||
}
|
||||
+5
@@ -757,6 +757,11 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest {
|
||||
public void testDeprecatedProperty() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeListing/deprecated/deprecatedProperty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("hidden.kt")
|
||||
public void testHidden() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeListing/deprecated/hidden.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeListing/inline")
|
||||
|
||||
+5
@@ -727,6 +727,11 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes
|
||||
public void testDeprecatedProperty() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeListing/deprecated/deprecatedProperty.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("hidden.kt")
|
||||
public void testHidden() throws Exception {
|
||||
runTest("compiler/testData/codegen/bytecodeListing/deprecated/hidden.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/codegen/bytecodeListing/inline")
|
||||
|
||||
Reference in New Issue
Block a user