From 0e91d3fcb0a34e0a92d8015c5d582bff937c7867 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Fri, 6 Nov 2020 17:26:03 +0100 Subject: [PATCH] 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 --- .../kotlin/backend/common/ir/IrUtils.kt | 36 ++++++++++++------- .../backend/jvm/codegen/ClassCodegen.kt | 13 +++---- .../backend/jvm/codegen/FunctionCodegen.kt | 29 +++++++++++---- .../backend/jvm/codegen/irCodegenUtils.kt | 32 +++++++---------- .../bytecodeListing/deprecated/hidden.kt | 16 +++++++++ .../bytecodeListing/deprecated/hidden.txt | 19 ++++++++++ .../codegen/BytecodeListingTestGenerated.java | 5 +++ .../ir/IrBytecodeListingTestGenerated.java | 5 +++ 8 files changed, 110 insertions(+), 45 deletions(-) create mode 100644 compiler/testData/codegen/bytecodeListing/deprecated/hidden.kt create mode 100644 compiler/testData/codegen/bytecodeListing/deprecated/hidden.txt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt index 1c7108275f2..4bc065f8b19 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/ir/IrUtils.kt @@ -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 { - val result = mutableSetOf() +fun IrSimpleFunction.allOverridden(includeSelf: Boolean = false): List { + val result = mutableListOf() 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) { - 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) } } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt index 8f42b0ec8d3..54d24e6cb2f 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ClassCodegen.kt @@ -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 } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt index ad358968031..4e8b58e86ae 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt @@ -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? { if (context.state.languageVersionSettings.supportsFeature(LanguageFeature.DoNotGenerateThrowsForDelegatedKotlinMembers) && function.origin == IrDeclarationOrigin.DELEGATED_MEMBER diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt index c2d31234600..2a7ac04af87 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/irCodegenUtils.kt @@ -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? diff --git a/compiler/testData/codegen/bytecodeListing/deprecated/hidden.kt b/compiler/testData/codegen/bytecodeListing/deprecated/hidden.kt new file mode 100644 index 00000000000..29eda695539 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/deprecated/hidden.kt @@ -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() {} +} diff --git a/compiler/testData/codegen/bytecodeListing/deprecated/hidden.txt b/compiler/testData/codegen/bytecodeListing/deprecated/hidden.txt new file mode 100644 index 00000000000..2df2d4e4037 --- /dev/null +++ b/compiler/testData/codegen/bytecodeListing/deprecated/hidden.txt @@ -0,0 +1,19 @@ +@kotlin.Metadata +public class Base { + // source: 'hidden.kt' + public method (): void + public synthetic deprecated @kotlin.Deprecated method f(): void +} + +@kotlin.Metadata +public final class Derived { + // source: 'hidden.kt' + public synthetic deprecated @kotlin.Deprecated method (): 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 +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java index 50d8609ccfe..cd4e6763474 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeListingTestGenerated.java @@ -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") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java index d6cdbbd403a..9db0cc1f2a7 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeListingTestGenerated.java @@ -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")