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.CommonBackendContext
import org.jetbrains.kotlin.backend.common.deepCopyWithVariables import org.jetbrains.kotlin.backend.common.deepCopyWithVariables
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.DescriptorVisibility import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI 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.addConstructor
import org.jetbrains.kotlin.ir.builders.declarations.buildReceiverParameter import org.jetbrains.kotlin.ir.builders.declarations.buildReceiverParameter
import org.jetbrains.kotlin.ir.builders.declarations.buildTypeParameter import org.jetbrains.kotlin.ir.builders.declarations.buildTypeParameter
@@ -602,22 +601,35 @@ private fun IrSimpleFunction.copyAndRenameConflictingTypeParametersFrom(
val IrSymbol.isSuspend: Boolean val IrSymbol.isSuspend: Boolean
get() = this is IrSimpleFunctionSymbol && owner.isSuspend get() = this is IrSimpleFunctionSymbol && owner.isSuspend
fun IrSimpleFunction.allOverridden(includeSelf: Boolean = false): Set<IrSimpleFunction> { fun IrSimpleFunction.allOverridden(includeSelf: Boolean = false): List<IrSimpleFunction> {
val result = mutableSetOf<IrSimpleFunction>() val result = mutableListOf<IrSimpleFunction>()
if (includeSelf) { if (includeSelf) {
computeAllOverridden(this, result) result.add(this)
} else { }
for (override in overriddenSymbols) {
computeAllOverridden(override.owner, result) 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>) { private fun computeAllOverridden(function: IrSimpleFunction, result: MutableSet<IrSimpleFunction>) {
if (result.add(function)) { for (overriddenSymbol in function.overriddenSymbols) {
for (override in function.overriddenSymbols) { val override = overriddenSymbol.owner
computeAllOverridden(override.owner, result) 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.MultifileFacadeFileEntry
import org.jetbrains.kotlin.backend.jvm.lower.buildAssertionsDisabledField import org.jetbrains.kotlin.backend.jvm.lower.buildAssertionsDisabledField
import org.jetbrains.kotlin.backend.jvm.lower.hasAssertionsDisabledField import org.jetbrains.kotlin.backend.jvm.lower.hasAssertionsDisabledField
import org.jetbrains.kotlin.builtins.StandardNames
import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.inline.* import org.jetbrains.kotlin.codegen.inline.*
import org.jetbrains.kotlin.config.LanguageFeature 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.java.JvmAnnotationNames
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.metadata.jvm.serialization.JvmStringTable import org.jetbrains.kotlin.metadata.jvm.serialization.JvmStringTable
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.protobuf.MessageLite import org.jetbrains.kotlin.protobuf.MessageLite
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME 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 private val IrClass.flags: Int
get() = origin.flags or getVisibilityAccessFlagForClass() or 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 { when {
isAnnotationClass -> Opcodes.ACC_ANNOTATION or Opcodes.ACC_INTERFACE or Opcodes.ACC_ABSTRACT isAnnotationClass -> Opcodes.ACC_ANNOTATION or Opcodes.ACC_INTERFACE or Opcodes.ACC_ABSTRACT
isInterface -> 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 = private fun IrField.computeFieldFlags(context: JvmBackendContext, languageVersionSettings: LanguageVersionSettings): Int =
origin.flags or visibility.flags or origin.flags or visibility.flags or
(correspondingPropertySymbol?.owner?.callableDeprecationFlags ?: 0) or (if (isDeprecatedCallable ||
(if (shouldHaveSpecialDeprecationFlag(context)) Opcodes.ACC_DEPRECATED else 0) or correspondingPropertySymbol?.owner?.isDeprecatedCallable == true ||
(if (annotations.hasAnnotation(KOTLIN_DEPRECATED)) Opcodes.ACC_DEPRECATED else 0) or shouldHaveSpecialDeprecationFlag(context)
) Opcodes.ACC_DEPRECATED else 0) or
(if (isFinal) Opcodes.ACC_FINAL else 0) or (if (isFinal) Opcodes.ACC_FINAL else 0) or
(if (isStatic) Opcodes.ACC_STATIC else 0) or (if (isStatic) Opcodes.ACC_STATIC else 0) or
(if (hasAnnotation(VOLATILE_ANNOTATION_FQ_NAME)) Opcodes.ACC_VOLATILE 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 && parentAsClass.isJvmInterface &&
DescriptorVisibilities.isPrivate(parentAsClass.companionObject()!!.visibility) DescriptorVisibilities.isPrivate(parentAsClass.companionObject()!!.visibility)
private val KOTLIN_DEPRECATED = FqName("kotlin.Deprecated")
fun IrField.shouldHaveSpecialDeprecationFlag(context: JvmBackendContext): Boolean { fun IrField.shouldHaveSpecialDeprecationFlag(context: JvmBackendContext): Boolean {
return annotations.any { it.symbol == context.ir.symbols.javaLangDeprecatedConstructorWithDeprecatedFlag } return annotations.any { it.symbol == context.ir.symbols.javaLangDeprecatedConstructorWithDeprecatedFlag }
} }
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.backend.jvm.codegen 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.ir.ir2string
import org.jetbrains.kotlin.backend.common.lower.BOUND_RECEIVER_PARAMETER import org.jetbrains.kotlin.backend.common.lower.BOUND_RECEIVER_PARAMETER
import org.jetbrains.kotlin.backend.common.lower.BOUND_VALUE_PARAMETER import org.jetbrains.kotlin.backend.common.lower.BOUND_VALUE_PARAMETER
@@ -144,9 +145,9 @@ class FunctionCodegen(
private fun IrFunction.calculateMethodFlags(): Int { private fun IrFunction.calculateMethodFlags(): Int {
if (origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER) { if (origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER) {
return getVisibilityForDefaultArgumentStub() or Opcodes.ACC_SYNTHETIC or functionDeprecationFlags.let { return getVisibilityForDefaultArgumentStub() or Opcodes.ACC_SYNTHETIC or
if (this is IrConstructor) it else it or Opcodes.ACC_STATIC (if (isDeprecatedFunction) Opcodes.ACC_DEPRECATED else 0) or
} (if (this is IrConstructor) 0 else Opcodes.ACC_STATIC)
} }
val isVararg = valueParameters.lastOrNull()?.varargElementType != null && !isBridge() val isVararg = valueParameters.lastOrNull()?.varargElementType != null && !isBridge()
@@ -162,12 +163,17 @@ class FunctionCodegen(
// TODO transform interface modality on lowering to DefaultImpls // TODO transform interface modality on lowering to DefaultImpls
else -> if (parentAsClass.isJvmInterface && body == null) Opcodes.ACC_ABSTRACT else 0 else -> if (parentAsClass.isJvmInterface && body == null) Opcodes.ACC_ABSTRACT else 0
} }
val isSynthetic = origin.isSynthetic || hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) || val isSynthetic = origin.isSynthetic ||
(isSuspend && DescriptorVisibilities.isPrivate(visibility) && !isInline) || isReifiable() hasAnnotation(JVM_SYNTHETIC_ANNOTATION_FQ_NAME) ||
(isSuspend && DescriptorVisibilities.isPrivate(visibility) && !isInline) ||
isReifiable() ||
isDeprecatedHidden()
val isStrict = hasAnnotation(STRICTFP_ANNOTATION_FQ_NAME) val isStrict = hasAnnotation(STRICTFP_ANNOTATION_FQ_NAME)
val isSynchronized = hasAnnotation(SYNCHRONIZED_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 (isStatic) Opcodes.ACC_STATIC else 0) or
(if (isVararg) Opcodes.ACC_VARARGS else 0) or (if (isVararg) Opcodes.ACC_VARARGS else 0) or
(if (isExternal) Opcodes.ACC_NATIVE else 0) or (if (isExternal) Opcodes.ACC_NATIVE else 0) or
@@ -177,6 +183,17 @@ class FunctionCodegen(
(if (isSynchronized) Opcodes.ACC_SYNCHRONIZED else 0) (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>? { private fun getThrownExceptions(function: IrFunction): List<String>? {
if (context.state.languageVersionSettings.supportsFeature(LanguageFeature.DoNotGenerateThrowsForDelegatedKotlinMembers) && if (context.state.languageVersionSettings.supportsFeature(LanguageFeature.DoNotGenerateThrowsForDelegatedKotlinMembers) &&
function.origin == IrDeclarationOrigin.DELEGATED_MEMBER 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.backend.jvm.lower.MultifileFacadeFileEntry
import org.jetbrains.kotlin.builtins.StandardNames.FqNames import org.jetbrains.kotlin.builtins.StandardNames.FqNames
import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap 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.inline.SourceMapper
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.declarations.* 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.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.symbols.IrSymbol
@@ -338,16 +340,11 @@ fun IrClass.isOptionalAnnotationClass(): Boolean =
isAnnotationClass && isAnnotationClass &&
hasAnnotation(ExpectedActualDeclarationChecker.OPTIONAL_EXPECTATION_FQ_NAME) hasAnnotation(ExpectedActualDeclarationChecker.OPTIONAL_EXPECTATION_FQ_NAME)
val IrDeclaration.callableDeprecationFlags: Int val IrDeclaration.isAnnotatedWithDeprecated: Boolean
get() { get() = annotations.hasAnnotation(FqNames.deprecated)
val annotation = annotations.findAnnotation(FqNames.deprecated)
?: return if ((this as? IrDeclaration)?.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_FOR_COMPATIBILITY) val IrDeclaration.isDeprecatedCallable: Boolean
Opcodes.ACC_DEPRECATED get() = isAnnotatedWithDeprecated || origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS_BRIDGE_FOR_COMPATIBILITY
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
}
// We can't check for JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS because for interface methods // We can't check for JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS because for interface methods
// moved to DefaultImpls, origin is changed to DEFAULT_IMPLS // moved to DefaultImpls, origin is changed to DEFAULT_IMPLS
@@ -355,12 +352,9 @@ val IrDeclaration.callableDeprecationFlags: Int
val IrFunction.isSyntheticMethodForProperty: Boolean val IrFunction.isSyntheticMethodForProperty: Boolean
get() = name.asString().endsWith(JvmAbi.ANNOTATED_PROPERTY_METHOD_NAME_SUFFIX) get() = name.asString().endsWith(JvmAbi.ANNOTATED_PROPERTY_METHOD_NAME_SUFFIX)
val IrFunction.functionDeprecationFlags: Int val IrFunction.isDeprecatedFunction: Boolean
get() { get() = isSyntheticMethodForProperty || isDeprecatedCallable ||
val originFlags = if (isSyntheticMethodForProperty) Opcodes.ACC_DEPRECATED else 0 (this as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.isDeprecatedCallable == true
val propertyFlags = (this as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.callableDeprecationFlags ?: 0
return originFlags or propertyFlags or callableDeprecationFlags
}
@OptIn(ObsoleteDescriptorBasedAPI::class) @OptIn(ObsoleteDescriptorBasedAPI::class)
val IrDeclaration.psiElement: PsiElement? 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 { public void testDeprecatedProperty() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/deprecated/deprecatedProperty.kt"); 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") @TestMetadata("compiler/testData/codegen/bytecodeListing/inline")
@@ -727,6 +727,11 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes
public void testDeprecatedProperty() throws Exception { public void testDeprecatedProperty() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/deprecated/deprecatedProperty.kt"); 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") @TestMetadata("compiler/testData/codegen/bytecodeListing/inline")