Add initial support for @JvmRecord in backend
- Write relevant class files attributes - Emit property accessors with records-convention: propertyName -> propertyName() ^KT-43677 In Progress
This commit is contained in:
@@ -43,6 +43,7 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
|
|||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall;
|
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall;
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.annotations.JvmAnnotationUtilKt;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmClassSignature;
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmClassSignature;
|
||||||
@@ -221,6 +222,10 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
|||||||
access |= ACC_ENUM;
|
access |= ACC_ENUM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (JvmAnnotationUtilKt.isJvmRecord(descriptor)) {
|
||||||
|
access |= ACC_RECORD;
|
||||||
|
}
|
||||||
|
|
||||||
v.defineClass(
|
v.defineClass(
|
||||||
myClass.getPsiOrParent(),
|
myClass.getPsiOrParent(),
|
||||||
state.getClassFileVersion(),
|
state.getClassFileVersion(),
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.resolve.InlineClassesUtilsKt;
|
|||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
|
||||||
import org.jetbrains.kotlin.resolve.calls.util.UnderscoreUtilKt;
|
import org.jetbrains.kotlin.resolve.calls.util.UnderscoreUtilKt;
|
||||||
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
|
import org.jetbrains.kotlin.resolve.constants.ConstantValue;
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.annotations.JvmAnnotationUtilKt;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKt;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature;
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature;
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature;
|
||||||
@@ -435,9 +436,10 @@ public class PropertyCodegen {
|
|||||||
v.getSerializationBindings().put(FIELD_FOR_PROPERTY, propertyDescriptor, new Pair<>(type, name));
|
v.getSerializationBindings().put(FIELD_FOR_PROPERTY, propertyDescriptor, new Pair<>(type, name));
|
||||||
|
|
||||||
if (isBackingFieldOwner) {
|
if (isBackingFieldOwner) {
|
||||||
|
String signature = isDelegate ? null : typeMapper.mapFieldSignature(kotlinType, propertyDescriptor);
|
||||||
FieldVisitor fv = builder.newField(
|
FieldVisitor fv = builder.newField(
|
||||||
JvmDeclarationOriginKt.OtherOrigin(propertyDescriptor), modifiers, name, type.getDescriptor(),
|
JvmDeclarationOriginKt.OtherOrigin(propertyDescriptor), modifiers, name, type.getDescriptor(),
|
||||||
isDelegate ? null : typeMapper.mapFieldSignature(kotlinType, propertyDescriptor), defaultValue
|
signature, defaultValue
|
||||||
);
|
);
|
||||||
|
|
||||||
if (annotatedField != null) {
|
if (annotatedField != null) {
|
||||||
@@ -450,6 +452,10 @@ public class PropertyCodegen {
|
|||||||
AnnotationCodegen.forField(fv, memberCodegen, state, skipNullabilityAnnotations)
|
AnnotationCodegen.forField(fv, memberCodegen, state, skipNullabilityAnnotations)
|
||||||
.genAnnotations(annotatedField, type, propertyDescriptor.getType(), null, additionalVisibleAnnotations);
|
.genAnnotations(annotatedField, type, propertyDescriptor.getType(), null, additionalVisibleAnnotations);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (propertyDescriptor.getContainingDeclaration() instanceof ClassDescriptor && JvmAnnotationUtilKt.isJvmRecord((ClassDescriptor) propertyDescriptor.getContainingDeclaration())) {
|
||||||
|
builder.newRecordComponent(name, type.getDescriptor(), signature);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,7 @@ import org.jetbrains.kotlin.resolve.jvm.AsmTypes.DEFAULT_CONSTRUCTOR_MARKER
|
|||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
import org.jetbrains.kotlin.resolve.jvm.annotations.isCompiledToJvmDefault
|
import org.jetbrains.kotlin.resolve.jvm.annotations.isCompiledToJvmDefault
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.annotations.isJvmRecord
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||||
@@ -583,12 +584,16 @@ class KotlinTypeMapper @JvmOverloads constructor(
|
|||||||
return when {
|
return when {
|
||||||
descriptor is PropertyAccessorDescriptor -> {
|
descriptor is PropertyAccessorDescriptor -> {
|
||||||
val property = descriptor.correspondingProperty
|
val property = descriptor.correspondingProperty
|
||||||
if (isAnnotationClass(property.containingDeclaration) &&
|
val containingDeclaration = property.containingDeclaration
|
||||||
|
|
||||||
|
if (isAnnotationClass(containingDeclaration) &&
|
||||||
(!property.hasJvmStaticAnnotation() && !descriptor.hasJvmStaticAnnotation())
|
(!property.hasJvmStaticAnnotation() && !descriptor.hasJvmStaticAnnotation())
|
||||||
) {
|
) {
|
||||||
return property.name.asString()
|
return property.name.asString()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((containingDeclaration as? ClassDescriptor)?.isJvmRecord() == true) return property.name.asString()
|
||||||
|
|
||||||
val isAccessor = property is AccessorForPropertyDescriptor
|
val isAccessor = property is AccessorForPropertyDescriptor
|
||||||
val propertyName = if (isAccessor)
|
val propertyName = if (isAccessor)
|
||||||
(property as AccessorForPropertyDescriptor).accessorSuffix
|
(property as AccessorForPropertyDescriptor).accessorSuffix
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ enum class JvmTarget(override val description: String) : TargetPlatformVersion {
|
|||||||
JVM_13("13"),
|
JVM_13("13"),
|
||||||
JVM_14("14"),
|
JVM_14("14"),
|
||||||
JVM_15("15"),
|
JVM_15("15"),
|
||||||
|
JVM_15_PREVIEW("15_PREVIEW"),
|
||||||
;
|
;
|
||||||
|
|
||||||
val bytecodeVersion: Int by lazy {
|
val bytecodeVersion: Int by lazy {
|
||||||
@@ -39,9 +40,10 @@ enum class JvmTarget(override val description: String) : TargetPlatformVersion {
|
|||||||
JVM_10 -> Opcodes.V10
|
JVM_10 -> Opcodes.V10
|
||||||
JVM_11 -> Opcodes.V11
|
JVM_11 -> Opcodes.V11
|
||||||
JVM_12 -> Opcodes.V12
|
JVM_12 -> Opcodes.V12
|
||||||
JVM_13 -> Opcodes.V12 + 1
|
JVM_13 -> Opcodes.V13
|
||||||
JVM_14 -> Opcodes.V12 + 2
|
JVM_14 -> Opcodes.V14
|
||||||
JVM_15 -> Opcodes.V12 + 3
|
JVM_15 -> Opcodes.V15
|
||||||
|
JVM_15_PREVIEW -> Opcodes.V15 + (0xffff shl 16)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ 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.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_RECORD_ANNOTATION_FQ_NAME
|
||||||
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
|
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME
|
||||||
import org.jetbrains.kotlin.resolve.jvm.annotations.TRANSIENT_ANNOTATION_FQ_NAME
|
import org.jetbrains.kotlin.resolve.jvm.annotations.TRANSIENT_ANNOTATION_FQ_NAME
|
||||||
import org.jetbrains.kotlin.resolve.jvm.annotations.VOLATILE_ANNOTATION_FQ_NAME
|
import org.jetbrains.kotlin.resolve.jvm.annotations.VOLATILE_ANNOTATION_FQ_NAME
|
||||||
@@ -306,6 +307,11 @@ class ClassCodegen private constructor(
|
|||||||
(field.metadata as? MetadataSource.Property)?.let {
|
(field.metadata as? MetadataSource.Property)?.let {
|
||||||
metadataSerializer.bindFieldMetadata(it, fieldType to fieldName)
|
metadataSerializer.bindFieldMetadata(it, fieldType to fieldName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (irClass.hasAnnotation(JVM_RECORD_ANNOTATION_FQ_NAME) && !field.isStatic) {
|
||||||
|
// TODO: Write annotations to the component
|
||||||
|
visitor.newRecordComponent(fieldName, fieldType.descriptor, fieldSignature)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val generatedInlineMethods = mutableMapOf<IrFunction, SMAPAndMethodNode>()
|
private val generatedInlineMethods = mutableMapOf<IrFunction, SMAPAndMethodNode>()
|
||||||
@@ -452,6 +458,7 @@ private val IrClass.flags: Int
|
|||||||
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
|
||||||
isEnumClass -> Opcodes.ACC_ENUM or Opcodes.ACC_SUPER or modality.flags
|
isEnumClass -> Opcodes.ACC_ENUM or Opcodes.ACC_SUPER or modality.flags
|
||||||
|
hasAnnotation(JVM_RECORD_ANNOTATION_FQ_NAME) -> Opcodes.ACC_RECORD or Opcodes.ACC_SUPER or modality.flags
|
||||||
else -> Opcodes.ACC_SUPER or modality.flags
|
else -> Opcodes.ACC_SUPER or modality.flags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -41,6 +41,7 @@ import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull
|
|||||||
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
|
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
|
||||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
|
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
|
||||||
import org.jetbrains.kotlin.name.NameUtils
|
import org.jetbrains.kotlin.name.NameUtils
|
||||||
|
import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_RECORD_ANNOTATION_FQ_NAME
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||||
@@ -83,7 +84,7 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
|
|||||||
if (property != null) {
|
if (property != null) {
|
||||||
val propertyName = property.name.asString()
|
val propertyName = property.name.asString()
|
||||||
val propertyParent = property.parentAsClass
|
val propertyParent = property.parentAsClass
|
||||||
if (propertyParent.isAnnotationClass)
|
if (propertyParent.isAnnotationClass || propertyParent.hasAnnotation(JVM_RECORD_ANNOTATION_FQ_NAME))
|
||||||
return propertyName
|
return propertyName
|
||||||
|
|
||||||
// The enum property getters <get-name> and <get-ordinal> have special names which also
|
// The enum property getters <get-name> and <get-ordinal> have special names which also
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
// !LANGUAGE: +JvmRecordSupport
|
||||||
|
// JVM_TARGET: 15_PREVIEW
|
||||||
|
// FILE: JavaClass.java
|
||||||
|
public class JavaClass {
|
||||||
|
public static String box() {
|
||||||
|
MyRec m = new MyRec<String>("O", "K");
|
||||||
|
return m.x() + m.y();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// FILE: main.kt
|
||||||
|
|
||||||
|
@JvmRecord
|
||||||
|
class MyRec<R>(val x: String, val y: R)
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
val recordComponents = MyRec::class.java.recordComponents
|
||||||
|
val x = recordComponents[0]
|
||||||
|
val y = recordComponents[1]
|
||||||
|
|
||||||
|
if (x.name != "x") return "fail 1: ${x.name}"
|
||||||
|
if (x.type != String::class.java) return "fail 2: ${x.type}"
|
||||||
|
if (x.genericSignature != null) return "fail 3: ${x.genericSignature}"
|
||||||
|
|
||||||
|
if (y.name != "y") return "fail 4: ${y.name}"
|
||||||
|
if (y.type != Any::class.java) return "fail 5: ${y.type}"
|
||||||
|
if (y.genericSignature != "TR;") return "fail 6: ${y.genericSignature}"
|
||||||
|
|
||||||
|
return JavaClass.box()
|
||||||
|
}
|
||||||
+3
-1
@@ -17,4 +17,6 @@ abstract class AbstractJdk15BlackBoxCodegenTest : AbstractCustomJDKBlackBoxCodeg
|
|||||||
|
|
||||||
override fun getAdditionalJavacArgs(): List<String> = ADDITIONAL_JAVAC_ARGS_FOR_15
|
override fun getAdditionalJavacArgs(): List<String> = ADDITIONAL_JAVAC_ARGS_FOR_15
|
||||||
override fun getAdditionalJvmArgs(): List<String> = listOf("--enable-preview")
|
override fun getAdditionalJvmArgs(): List<String> = listOf("--enable-preview")
|
||||||
}
|
|
||||||
|
override fun verifyWithDex(): Boolean = false
|
||||||
|
}
|
||||||
|
|||||||
+29
-11
@@ -28,18 +28,36 @@ public class Jdk15BlackBoxCodegenTestGenerated extends AbstractJdk15BlackBoxCode
|
|||||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/java15/box"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/java15/box"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@TestMetadata("recordDifferentPropertyOverride.kt")
|
@TestMetadata("compiler/testData/codegen/java15/box/records")
|
||||||
public void testRecordDifferentPropertyOverride() throws Exception {
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
runTest("compiler/testData/codegen/java15/box/recordDifferentPropertyOverride.kt");
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
}
|
public static class Records extends AbstractJdk15BlackBoxCodegenTest {
|
||||||
|
private void runTest(String testDataFilePath) throws Exception {
|
||||||
|
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("recordDifferentSyntheticProperty.kt")
|
public void testAllFilesPresentInRecords() throws Exception {
|
||||||
public void testRecordDifferentSyntheticProperty() throws Exception {
|
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/java15/box/records"), Pattern.compile("^(.+)\\.kt$"), null, true);
|
||||||
runTest("compiler/testData/codegen/java15/box/recordDifferentSyntheticProperty.kt");
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@TestMetadata("recordPropertyAccess.kt")
|
@TestMetadata("bytecodeShapeForJava.kt")
|
||||||
public void testRecordPropertyAccess() throws Exception {
|
public void testBytecodeShapeForJava() throws Exception {
|
||||||
runTest("compiler/testData/codegen/java15/box/recordPropertyAccess.kt");
|
runTest("compiler/testData/codegen/java15/box/records/bytecodeShapeForJava.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("recordDifferentPropertyOverride.kt")
|
||||||
|
public void testRecordDifferentPropertyOverride() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/java15/box/records/recordDifferentPropertyOverride.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("recordDifferentSyntheticProperty.kt")
|
||||||
|
public void testRecordDifferentSyntheticProperty() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/java15/box/records/recordDifferentSyntheticProperty.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("recordPropertyAccess.kt")
|
||||||
|
public void testRecordPropertyAccess() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/java15/box/records/recordPropertyAccess.kt");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user