JVM IR: generate synthetic $annotations methods for properties

This commit is contained in:
Alexander Udalov
2019-02-13 01:25:37 +01:00
parent c92c50aa98
commit d267f1e875
19 changed files with 110 additions and 57 deletions
@@ -350,7 +350,7 @@ public class PropertyCodegen {
Annotations annotations = descriptor.getAnnotations();
if (annotations.isEmpty()) return;
Method signature = getSyntheticMethodSignature(descriptor);
Method signature = typeMapper.mapSyntheticMethodForPropertyAnnotations(descriptor);
if (kind != OwnerKind.DEFAULT_IMPLS && CodegenContextUtil.isImplementationOwner(context, descriptor)) {
v.getSerializationBindings().put(SYNTHETIC_METHOD_FOR_PROPERTY, descriptor, signature);
}
@@ -363,14 +363,6 @@ public class PropertyCodegen {
}
}
@NotNull
private Method getSyntheticMethodSignature(@NotNull PropertyDescriptor descriptor) {
ReceiverParameterDescriptor receiver = descriptor.getExtensionReceiverParameter();
String name = JvmAbi.getSyntheticMethodNameForAnnotatedProperty(descriptor.getName());
String desc = receiver == null ? "()V" : "(" + typeMapper.mapType(receiver.getType()) + ")V";
return new Method(name, desc);
}
private void generateBackingField(
@NotNull KtNamedDeclaration element,
@NotNull PropertyDescriptor propertyDescriptor,
@@ -1834,6 +1834,14 @@ public class KotlinTypeMapper {
}
}
@NotNull
public Method mapSyntheticMethodForPropertyAnnotations(@NotNull PropertyDescriptor descriptor) {
ReceiverParameterDescriptor receiver = descriptor.getExtensionReceiverParameter();
String name = JvmAbi.getSyntheticMethodNameForAnnotatedProperty(descriptor.getName());
String desc = receiver == null ? "()V" : "(" + mapType(receiver.getType()) + ")V";
return new Method(name, desc);
}
@NotNull
public JvmMethodSignature mapScriptSignature(
@NotNull ScriptDescriptor script,
@@ -7,26 +7,35 @@ package org.jetbrains.kotlin.backend.common.lower
import org.jetbrains.kotlin.backend.common.BackendContext
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.descriptors.WrappedSimpleFunctionDescriptor
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.util.transformDeclarationsFlat
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addIfNotNull
import java.util.*
val propertiesPhase = makeIrFilePhase(
::PropertiesLowering,
fun makePropertiesPhase(originOfSyntheticMethodForAnnotations: IrDeclarationOrigin?) = makeIrFilePhase(
{ context -> PropertiesLowering(context, originOfSyntheticMethodForAnnotations) },
name = "Properties",
description = "Move fields and accessors for properties to their classes"
)
class PropertiesLowering() : IrElementTransformerVoid(), FileLoweringPass {
constructor(@Suppress("UNUSED_PARAMETER") context: BackendContext) : this()
class PropertiesLowering(
private val context: BackendContext,
private val originOfSyntheticMethodForAnnotations: IrDeclarationOrigin?
) : IrElementTransformerVoid(), FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.accept(this, null)
}
@@ -45,16 +54,43 @@ class PropertiesLowering() : IrElementTransformerVoid(), FileLoweringPass {
private fun lowerProperty(declaration: IrDeclaration, kind: ClassKind): List<IrDeclaration>? =
if (declaration is IrProperty)
ArrayList<IrDeclaration>(3).apply {
ArrayList<IrDeclaration>(4).apply {
// JvmFields in a companion object refer to companion's owners and should not be generated within companion.
if (kind != ClassKind.ANNOTATION_CLASS && declaration.backingField?.parent == declaration.parent) {
addIfNotNull(declaration.backingField)
}
addIfNotNull(declaration.getter)
addIfNotNull(declaration.setter)
if (declaration.annotations.isNotEmpty() && originOfSyntheticMethodForAnnotations != null) {
add(createSyntheticMethodForAnnotations(declaration, originOfSyntheticMethodForAnnotations))
}
}
else
null
private fun createSyntheticMethodForAnnotations(declaration: IrProperty, origin: IrDeclarationOrigin): IrFunctionImpl {
val descriptor = WrappedSimpleFunctionDescriptor(declaration.descriptor.annotations)
val symbol = IrSimpleFunctionSymbolImpl(descriptor)
// TODO: ACC_DEPRECATED
return IrFunctionImpl(
-1, -1, origin,
symbol, Name.identifier(JvmAbi.getSyntheticMethodNameForAnnotatedProperty(declaration.name)),
Visibilities.PUBLIC, Modality.OPEN, context.irBuiltIns.unitType,
isInline = false, isExternal = false, isTailrec = false, isSuspend = false
).apply {
descriptor.bind(this)
extensionReceiverParameter = declaration.getter?.extensionReceiverParameter
body = IrBlockBodyImpl(-1, -1)
// TODO: uncomment this and derive annotations from owner in wrapped descriptors
// annotations.addAll(declaration.annotations)
metadata = declaration.metadata
}
}
}
class LocalDelegatedPropertiesLowering : IrElementTransformerVoid(), FileLoweringPass {
@@ -251,7 +251,7 @@ private val varargLoweringPhase = makeJsModulePhase(
)
private val propertiesLoweringPhase = makeJsModulePhase(
{ PropertiesLowering() },
{ context -> PropertiesLowering(context, null) },
name = "PropertiesLowering",
description = "Move fields and accessors out from its property"
)
@@ -30,9 +30,11 @@ interface JvmLoweredDeclarationOrigin : IrDeclarationOrigin {
object TO_ARRAY : IrDeclarationOriginImpl("TO_ARRAY")
object JVM_STATIC_WRAPPER : IrDeclarationOriginImpl("JVM_STATIC_WRAPPER")
object JVM_OVERLOADS_WRAPPER : IrDeclarationOriginImpl("JVM_OVERLOADS_WRAPPER")
object SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS :
IrDeclarationOriginImpl("SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS", isSynthetic = true)
}
interface JvmLoweredStatementOrigin : IrStatementOrigin {
object DEFAULT_IMPLS_DELEGATION : IrStatementOriginImpl("DEFAULT_IMPL_DELEGATION")
object TO_ARRAY : IrDeclarationOriginImpl("TO_ARRAY")
}
}
@@ -48,7 +48,7 @@ internal val jvmPhases = namedIrFilePhase(
moveCompanionObjectFieldsPhase then
constPhase then
propertiesToFieldsPhase then
propertiesPhase then
makePropertiesPhase(JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS) then
renameFieldsPhase then
annotationPhase then
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.backend.jvm.codegen
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.backend.jvm.descriptors.JvmDescriptorWithExtraFlags
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.binding.CodegenBinding
@@ -240,11 +241,32 @@ open class ClassCodegen protected constructor(
private fun generateMethod(method: IrFunction) {
if (method.origin == IrDeclarationOrigin.FAKE_OVERRIDE) return
val signature = FunctionCodegen(method, this).generate()
val signature = FunctionCodegen(method, this).generate().asmMethod
val descriptor = method.metadata?.descriptor
if (descriptor != null) {
visitor.serializationBindings.put(JvmSerializationBindings.METHOD_FOR_FUNCTION, descriptor, signature.asmMethod)
val metadata = method.metadata
when (metadata) {
is MetadataSource.Property -> {
// We can't check for JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS because for interface methods
// moved to DefaultImpls, origin is changed to DEFAULT_IMPLS
// TODO: fix origin somehow, because otherwise $annotations methods in interfaces also don't have ACC_SYNTHETIC
assert(method.name.asString().endsWith(JvmAbi.ANNOTATED_PROPERTY_METHOD_NAME_SUFFIX)) { method.dump() }
val codegen = if (DescriptorUtils.isInterface(metadata.descriptor.containingDeclaration)) {
assert(irClass.origin == JvmLoweredDeclarationOrigin.DEFAULT_IMPLS) { irClass.dump() }
parentClassCodegen!!
} else {
this
}
codegen.visitor.serializationBindings.put(
JvmSerializationBindings.SYNTHETIC_METHOD_FOR_PROPERTY, metadata.descriptor, signature
)
}
is MetadataSource.Function -> {
visitor.serializationBindings.put(JvmSerializationBindings.METHOD_FOR_FUNCTION, metadata.descriptor, signature)
}
null -> {
}
else -> error("Incorrect metadata source $metadata for:\n${method.dump()}")
}
}
@@ -13,7 +13,9 @@ import org.jetbrains.kotlin.backend.common.lower.InitializersLowering.Companion.
import org.jetbrains.kotlin.backend.common.lower.VariableRemapper
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl
@@ -41,29 +43,29 @@ private class InterfaceLowering(val context: JvmBackendContext) : IrElementTrans
irClass.declarations.add(defaultImplsIrClass)
val members = defaultImplsIrClass.declarations
irClass.declarations.filterIsInstance<IrFunction>().forEach {
if (it is IrSimpleFunction && it.modality != Modality.ABSTRACT && it.origin != IrDeclarationOrigin.FAKE_OVERRIDE) {
val element = context.declarationFactory.getDefaultImplsFunction(it)
for (function in irClass.declarations) {
if (function !is IrSimpleFunction) continue
if (function.modality != Modality.ABSTRACT && function.origin != IrDeclarationOrigin.FAKE_OVERRIDE) {
val element = context.declarationFactory.getDefaultImplsFunction(function)
members.add(element)
element.body = it.body
it.body = null
element.body = function.body
function.body = null
//TODO reset modality to abstract
}
}
irClass.transformChildrenVoid(this)
//REMOVE private methods
val privateToRemove = irClass.declarations.filterIsInstance<IrFunction>().filter {
Visibilities.isPrivate(it.visibility) && (it as? IrSimpleFunction)?.name != clinitName
irClass.declarations.removeAll {
it is IrFunction && shouldRemoveFunction(it)
}
val defaultBodies = irClass.declarations.filterIsInstance<IrFunction>().filter {
it.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER
}
irClass.declarations.removeAll(privateToRemove)
irClass.declarations.removeAll(defaultBodies)
}
private fun shouldRemoveFunction(function: IrFunction): Boolean =
Visibilities.isPrivate(function.visibility) && function.name != clinitName ||
function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER ||
function.origin == JvmLoweredDeclarationOrigin.SYNTHETIC_METHOD_FOR_PROPERTY_ANNOTATIONS
}
@@ -110,5 +112,7 @@ internal fun createStaticFunctionWithReceivers(
(listOfNotNull(oldFunction.dispatchReceiverParameter, oldFunction.extensionReceiverParameter) + oldFunction.valueParameters)
.zip(valueParameters).toMap()
body = oldFunction.body?.transform(VariableRemapper(mapping), null)
metadata = oldFunction.metadata
}
}
@@ -128,6 +128,8 @@ class PropertyGenerator(declarationGenerator: DeclarationGenerator) : Declaratio
irProperty.getter = generateGetterIfRequired(ktProperty, propertyDescriptor)
irProperty.setter = generateSetterIfRequired(ktProperty, propertyDescriptor)
irProperty.metadata = MetadataSource.Property(propertyDescriptor)
}
fun generateFakeOverrideProperty(propertyDescriptor: PropertyDescriptor, ktElement: KtPureElement): IrProperty? {
@@ -39,7 +39,7 @@ interface IrFunction :
var body: IrBody?
override val metadata: MetadataSource.Function?
override val metadata: MetadataSource?
}
@@ -55,7 +55,7 @@ abstract class IrFunctionBase(
final override var body: IrBody? = null
override var metadata: MetadataSource.Function? = null
override var metadata: MetadataSource? = null
override fun <D> acceptChildren(visitor: IrElementVisitor<Unit, D>, data: D) {
typeParameters.forEach { it.accept(visitor, data) }
@@ -19,10 +19,7 @@ package org.jetbrains.kotlin.ir.declarations.impl
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.Visibility
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrProperty
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.name.Name
@@ -106,9 +103,11 @@ class IrPropertyImpl(
setter?.accept(visitor, data)
}
override var metadata: MetadataSource? = null
override fun <D> transformChildren(transformer: IrElementTransformer<D>, data: D) {
backingField = backingField?.transform(transformer, data) as? IrField
getter = getter?.run { transform(transformer, data) as IrSimpleFunction }
setter = setter?.run { transform(transformer, data) as IrSimpleFunction }
}
}
}
@@ -1,6 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_REFLECT
package zzz
@@ -1,6 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_REFLECT
// FULL_JDK
@@ -1,6 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_REFLECT
import kotlin.reflect.KMutableProperty
@@ -1,5 +1,3 @@
// !LANGUAGE: +JvmFieldInInterface +NestedClassesInAnnotations
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_REFLECT
@@ -1,5 +1,3 @@
// !LANGUAGE: +JvmFieldInInterface
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_REFLECT
@@ -1,6 +1,4 @@
// IGNORE_BACKEND: JVM_IR
// TARGET_BACKEND: JVM
// WITH_REFLECT
import kotlin.reflect.*
@@ -38,7 +38,7 @@ public final class JvmAbi {
public static final String DELEGATED_PROPERTIES_ARRAY_NAME = "$$delegatedProperties";
public static final String DELEGATE_SUPER_FIELD_PREFIX = "$$delegate_";
private static final String ANNOTATIONS_SUFFIX = "$annotations";
private static final String ANNOTATED_PROPERTY_METHOD_NAME_SUFFIX = ANNOTATIONS_SUFFIX;
public static final String ANNOTATED_PROPERTY_METHOD_NAME_SUFFIX = ANNOTATIONS_SUFFIX;
private static final String ANNOTATED_TYPEALIAS_METHOD_NAME_SUFFIX = ANNOTATIONS_SUFFIX;
public static final String INSTANCE_FIELD = "INSTANCE";