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:
Alexander Udalov
2020-11-06 17:26:03 +01:00
committed by Alexander Udalov
parent d9efc2d922
commit 0e91d3fcb0
8 changed files with 110 additions and 45 deletions
@@ -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)
}
}
}
@@ -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 }
}
@@ -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
@@ -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
}
@@ -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")
@@ -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")