JVM_IR. Deprecate public access to @JvmField/const fields in private companions

#KT-25009
This commit is contained in:
Mikhael Bogdanov
2020-10-29 09:25:43 +01:00
parent 57c9afc73a
commit 5804f73ebd
11 changed files with 134 additions and 18 deletions
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
import org.jetbrains.kotlin.backend.jvm.ir.copyCorrespondingPropertyFrom
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.backend.jvm.ir.replaceThisByStaticReference
import org.jetbrains.kotlin.builtins.CompanionObjectMapping
import org.jetbrains.kotlin.builtins.isMappedIntrinsicCompanionObjectClassId
@@ -122,11 +123,21 @@ class JvmCachedDeclarations(
// It is an error to annotate only some of the fields of an interface companion with
// @JvmField, so checking the current field only should be enough.
val hasJvmField = oldField.hasAnnotation(JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME)
parent = if (oldParent.isCompanion && (!oldParent.parentAsClass.isJvmInterface || hasJvmField))
oldParent.parentAsClass
else
oldParent
annotations += oldField.annotations
if (oldParent.isCompanion && (!oldParent.parentAsClass.isJvmInterface || hasJvmField)) {
parent = oldParent.parentAsClass
annotations = if (DescriptorVisibilities.isPrivate(oldParent.visibility)) {
context.createJvmIrBuilder(this.symbol).run {
filterOutAnnotations(
DeprecationResolver.JAVA_DEPRECATED,
oldField.annotations
) + irCall(irSymbols.javaLangDeprecatedConstructorWithDeprecatedFlag)
}
} else oldField.annotations
} else {
parent = oldParent
annotations = oldField.annotations
}
initializer = oldField.initializer
?.replaceThisByStaticReference(this@JvmCachedDeclarations, oldParent, oldParent.thisReceiver!!)
?.patchDeclarationParents(this) as IrExpressionBody?
@@ -183,7 +194,7 @@ class JvmCachedDeclarations(
!it.annotations.hasAnnotation(DeprecationResolver.JAVA_DEPRECATED)
) {
this@JvmCachedDeclarations.context.createIrBuilder(it.symbol).run {
it.annotations += irCall(this@JvmCachedDeclarations.context.ir.symbols.javaLangDeprecatedConstructor)
it.annotations += irCall(this@JvmCachedDeclarations.context.ir.symbols.javaLangDeprecatedConstructorWithDeprecatedFlag)
}
}
@@ -208,12 +208,13 @@ class JvmSymbols(
klass.addFunction("desiredAssertionStatus", irBuiltIns.booleanType)
}
private val javaLangDeprecated: IrClassSymbol =
private val javaLangDeprecatedWithDeprecatedFlag: IrClassSymbol =
createClass(FqName("java.lang.Deprecated")) { klass ->
klass.addConstructor { isPrimary = true }
}
val javaLangDeprecatedConstructor = javaLangDeprecated.constructors.single()
// This annotations also implies ACC_DEPRECATED flag in generated bytecode
val javaLangDeprecatedConstructorWithDeprecatedFlag = javaLangDeprecatedWithDeprecatedFlag.constructors.single()
private val javaLangAssertionError: IrClassSymbol =
createClass(FqName("java.lang.AssertionError"), classModality = Modality.OPEN) { klass ->
@@ -281,7 +281,7 @@ class ClassCodegen private constructor(
if (field.origin == IrDeclarationOrigin.PROPERTY_DELEGATE) null
else context.methodSignatureMapper.mapFieldSignature(field)
val fieldName = field.name.asString()
val flags = field.computeFieldFlags(state.languageVersionSettings)
val flags = field.computeFieldFlags(context, state.languageVersionSettings)
val fv = visitor.newField(
field.descriptorOrigin, flags, fieldName, fieldType.descriptor,
fieldSignature, (field.initializer?.expression as? IrConst<*>)?.value
@@ -455,10 +455,10 @@ private val IrClass.flags: Int
else -> Opcodes.ACC_SUPER or modality.flags
}
private fun IrField.computeFieldFlags(languageVersionSettings: LanguageVersionSettings): Int =
private fun IrField.computeFieldFlags(context: JvmBackendContext, languageVersionSettings: LanguageVersionSettings): Int =
origin.flags or visibility.flags or
(correspondingPropertySymbol?.owner?.callableDeprecationFlags ?: 0) or
(if (shouldHaveSpecialDeprecationFlag()) Opcodes.ACC_DEPRECATED else 0) or
(if (shouldHaveSpecialDeprecationFlag(context)) Opcodes.ACC_DEPRECATED else 0) or
(if (annotations.hasAnnotation(KOTLIN_DEPRECATED)) Opcodes.ACC_DEPRECATED else 0) or
(if (isFinal) Opcodes.ACC_FINAL else 0) or
(if (isStatic) Opcodes.ACC_STATIC else 0) or
@@ -474,12 +474,10 @@ private fun IrField.isPrivateCompanionFieldInInterface(languageVersionSettings:
parentAsClass.isJvmInterface &&
DescriptorVisibilities.isPrivate(parentAsClass.companionObject()!!.visibility)
private val JAVA_LANG_DEPRECATED = FqName("java.lang.Deprecated")
private val KOTLIN_DEPRECATED = FqName("kotlin.Deprecated")
fun IrField.shouldHaveSpecialDeprecationFlag(): Boolean {
return origin == IrDeclarationOrigin.FIELD_FOR_OBJECT_INSTANCE &&
annotations.hasAnnotation(JAVA_LANG_DEPRECATED)
fun IrField.shouldHaveSpecialDeprecationFlag(context: JvmBackendContext): Boolean {
return annotations.any { it.symbol == context.ir.symbols.javaLangDeprecatedConstructorWithDeprecatedFlag }
}
private val IrDeclarationOrigin.flags: Int
@@ -7,13 +7,16 @@ package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.ClassLoweringPass
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder
import org.jetbrains.kotlin.backend.jvm.ir.replaceThisByStaticReference
import org.jetbrains.kotlin.backend.jvm.propertiesPhase
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
import org.jetbrains.kotlin.ir.builders.declarations.addProperty
import org.jetbrains.kotlin.ir.builders.declarations.buildField
import org.jetbrains.kotlin.ir.builders.irCall
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrAnonymousInitializerImpl
import org.jetbrains.kotlin.ir.expressions.*
@@ -21,10 +24,12 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrSetFieldImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrAnonymousInitializerSymbolImpl
import org.jetbrains.kotlin.ir.util.filterOutAnnotations
import org.jetbrains.kotlin.ir.util.isObject
import org.jetbrains.kotlin.ir.util.parentAsClass
import org.jetbrains.kotlin.ir.util.patchDeclarationParents
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
internal val moveOrCopyCompanionObjectFieldsPhase = makeIrFilePhase(
::MoveOrCopyCompanionObjectFieldsLowering,
@@ -124,6 +129,13 @@ private class MoveOrCopyCompanionObjectFieldsLowering(val context: JvmBackendCon
initializer = with(oldField.initializer!!) {
IrExpressionBodyImpl(startOffset, endOffset, (expression as IrConst<*>).copy())
}
if (oldProperty.parentAsClass.visibility == DescriptorVisibilities.PRIVATE) {
context.createJvmIrBuilder(this.symbol).run {
annotations = filterOutAnnotations(DeprecationResolver.JAVA_DEPRECATED, annotations) +
irCall(irSymbols.javaLangDeprecatedConstructorWithDeprecatedFlag)
}
}
}
}
}
@@ -7,8 +7,8 @@ package org.jetbrains.kotlin.backend.jvm.lower
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
@@ -18,6 +18,7 @@ import org.jetbrains.kotlin.ir.builders.irExprBody
import org.jetbrains.kotlin.ir.builders.irGetField
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.resolve.deprecation.DeprecationResolver
internal val objectClassPhase = makeIrFilePhase(
::ObjectClassLowering,
@@ -72,8 +73,9 @@ private class ObjectClassLowering(val context: JvmBackendContext) : IrElementTra
(irClass.visibility == DescriptorVisibilities.PRIVATE || irClass.visibility == DescriptorVisibilities.PROTECTED)
) {
context.createIrBuilder(irClass.symbol).run {
publicInstanceField.annotations +=
irCall(this@ObjectClassLowering.context.ir.symbols.javaLangDeprecatedConstructor)
publicInstanceField.annotations =
filterOutAnnotations(DeprecationResolver.JAVA_DEPRECATED, publicInstanceField.annotations) +
irCall(this@ObjectClassLowering.context.ir.symbols.javaLangDeprecatedConstructorWithDeprecatedFlag)
}
}
@@ -245,3 +245,7 @@ fun IrClassSymbol.getPropertySetter(name: String): IrSimpleFunctionSymbol? = own
inline fun MemberScope.findFirstFunction(name: String, predicate: (CallableMemberDescriptor) -> Boolean) =
getContributedFunctions(Name.identifier(name), NoLookupLocation.FROM_BACKEND).first(predicate)
fun filterOutAnnotations(fqName: FqName, annotations: List<IrConstructorCall>): List<IrConstructorCall> {
return annotations.filterNot { it.annotationClass.hasEqualFqName(fqName) }
}
@@ -0,0 +1,11 @@
class Foo {
@field:java.lang.Deprecated
val bar: String = "123"
@java.lang.Deprecated
val bar2: String = "123"
@java.lang.Deprecated
fun test() {}
}
@@ -0,0 +1,10 @@
@kotlin.Metadata
public final class Foo {
// source: 'javaDeprecated.kt'
private final @java.lang.Deprecated @org.jetbrains.annotations.NotNull field bar2: java.lang.String
private final @java.lang.Deprecated @org.jetbrains.annotations.NotNull field bar: java.lang.String
public method <init>(): void
public final @org.jetbrains.annotations.NotNull method getBar(): java.lang.String
public final @org.jetbrains.annotations.NotNull method getBar2(): java.lang.String
public final @java.lang.Deprecated method test(): void
}
@@ -0,0 +1,57 @@
@kotlin.Metadata
final class TestClass$Companion {
// source: 'privateCompanionFields.kt'
private method <init>(): void
public synthetic method <init>(p0: kotlin.jvm.internal.DefaultConstructorMarker): void
private final inner class TestClass$Companion
}
@kotlin.Metadata
public final class TestClass {
// source: 'privateCompanionFields.kt'
private final static @org.jetbrains.annotations.NotNull field Companion: TestClass$Companion
public deprecated static @java.lang.Deprecated @kotlin.jvm.JvmField @org.jetbrains.annotations.NotNull field test2: java.lang.String
public deprecated final static @java.lang.Deprecated @kotlin.jvm.JvmField @org.jetbrains.annotations.NotNull field test3: java.lang.String
public deprecated static @java.lang.Deprecated @kotlin.jvm.JvmField @org.jetbrains.annotations.NotNull field test: java.lang.String
public deprecated final static @java.lang.Deprecated field testConst: int
static method <clinit>(): void
public method <init>(): void
private final inner class TestClass$Companion
}
@kotlin.Metadata
final class TestConst$Companion {
// source: 'privateCompanionFields.kt'
synthetic final static field $$INSTANCE: TestConst$Companion
public final static field testConst: int
static method <clinit>(): void
private method <init>(): void
public final inner class TestConst$Companion
}
@kotlin.Metadata
public interface TestConst {
// source: 'privateCompanionFields.kt'
public synthetic final static field Companion: TestConst$Companion
public deprecated final static @java.lang.Deprecated field testConst: int
static method <clinit>(): void
public final inner class TestConst$Companion
}
@kotlin.Metadata
final class TestJvmField$Companion {
// source: 'privateCompanionFields.kt'
synthetic final static field $$INSTANCE: TestJvmField$Companion
static method <clinit>(): void
private method <init>(): void
public final inner class TestJvmField$Companion
}
@kotlin.Metadata
public interface TestJvmField {
// source: 'privateCompanionFields.kt'
public synthetic final static field Companion: TestJvmField$Companion
public deprecated final static @java.lang.Deprecated @kotlin.jvm.JvmField @org.jetbrains.annotations.NotNull field test3: java.lang.String
static method <clinit>(): void
public final inner class TestJvmField$Companion
}
@@ -84,6 +84,11 @@ public class BytecodeListingTestGenerated extends AbstractBytecodeListingTest {
runTest("compiler/testData/codegen/bytecodeListing/immutableCollection.kt");
}
@TestMetadata("javaDeprecated.kt")
public void testJavaDeprecated() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/javaDeprecated.kt");
}
@TestMetadata("jvmOverloadsAndParametersAnnotations.kt")
public void testJvmOverloadsAndParametersAnnotations() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/jvmOverloadsAndParametersAnnotations.kt");
@@ -84,6 +84,11 @@ public class IrBytecodeListingTestGenerated extends AbstractIrBytecodeListingTes
runTest("compiler/testData/codegen/bytecodeListing/immutableCollection.kt");
}
@TestMetadata("javaDeprecated.kt")
public void testJavaDeprecated() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/javaDeprecated.kt");
}
@TestMetadata("jvmOverloadsAndParametersAnnotations.kt")
public void testJvmOverloadsAndParametersAnnotations() throws Exception {
runTest("compiler/testData/codegen/bytecodeListing/jvmOverloadsAndParametersAnnotations.kt");