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:
Denis.Zharkov
2020-11-27 15:56:07 +03:00
parent 26d525fa3c
commit 1b575d7903
12 changed files with 93 additions and 18 deletions
@@ -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.VariableAsFunctionResolvedCall;
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.JvmDeclarationOriginKt;
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmClassSignature;
@@ -221,6 +222,10 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
access |= ACC_ENUM;
}
if (JvmAnnotationUtilKt.isJvmRecord(descriptor)) {
access |= ACC_RECORD;
}
v.defineClass(
myClass.getPsiOrParent(),
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.util.UnderscoreUtilKt;
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.jvmSignature.JvmMethodGenericSignature;
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));
if (isBackingFieldOwner) {
String signature = isDelegate ? null : typeMapper.mapFieldSignature(kotlinType, propertyDescriptor);
FieldVisitor fv = builder.newField(
JvmDeclarationOriginKt.OtherOrigin(propertyDescriptor), modifiers, name, type.getDescriptor(),
isDelegate ? null : typeMapper.mapFieldSignature(kotlinType, propertyDescriptor), defaultValue
signature, defaultValue
);
if (annotatedField != null) {
@@ -450,6 +452,10 @@ public class PropertyCodegen {
AnnotationCodegen.forField(fv, memberCodegen, state, skipNullabilityAnnotations)
.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.JvmClassName
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.JvmMethodParameterKind
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
@@ -583,12 +584,16 @@ class KotlinTypeMapper @JvmOverloads constructor(
return when {
descriptor is PropertyAccessorDescriptor -> {
val property = descriptor.correspondingProperty
if (isAnnotationClass(property.containingDeclaration) &&
val containingDeclaration = property.containingDeclaration
if (isAnnotationClass(containingDeclaration) &&
(!property.hasJvmStaticAnnotation() && !descriptor.hasJvmStaticAnnotation())
) {
return property.name.asString()
}
if ((containingDeclaration as? ClassDescriptor)?.isJvmRecord() == true) return property.name.asString()
val isAccessor = property is AccessorForPropertyDescriptor
val propertyName = if (isAccessor)
(property as AccessorForPropertyDescriptor).accessorSuffix
@@ -29,6 +29,7 @@ enum class JvmTarget(override val description: String) : TargetPlatformVersion {
JVM_13("13"),
JVM_14("14"),
JVM_15("15"),
JVM_15_PREVIEW("15_PREVIEW"),
;
val bytecodeVersion: Int by lazy {
@@ -39,9 +40,10 @@ enum class JvmTarget(override val description: String) : TargetPlatformVersion {
JVM_10 -> Opcodes.V10
JVM_11 -> Opcodes.V11
JVM_12 -> Opcodes.V12
JVM_13 -> Opcodes.V12 + 1
JVM_14 -> Opcodes.V12 + 2
JVM_15 -> Opcodes.V12 + 3
JVM_13 -> Opcodes.V13
JVM_14 -> Opcodes.V14
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.name.Name
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.TRANSIENT_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 {
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>()
@@ -452,6 +458,7 @@ private val IrClass.flags: Int
isAnnotationClass -> Opcodes.ACC_ANNOTATION or 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
hasAnnotation(JVM_RECORD_ANNOTATION_FQ_NAME) -> Opcodes.ACC_RECORD or Opcodes.ACC_SUPER or modality.flags
else -> Opcodes.ACC_SUPER or modality.flags
}
@@ -41,6 +41,7 @@ import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
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.JvmMethodParameterKind
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
@@ -83,7 +84,7 @@ class MethodSignatureMapper(private val context: JvmBackendContext) {
if (property != null) {
val propertyName = property.name.asString()
val propertyParent = property.parentAsClass
if (propertyParent.isAnnotationClass)
if (propertyParent.isAnnotationClass || propertyParent.hasAnnotation(JVM_RECORD_ANNOTATION_FQ_NAME))
return propertyName
// 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()
}
@@ -17,4 +17,6 @@ abstract class AbstractJdk15BlackBoxCodegenTest : AbstractCustomJDKBlackBoxCodeg
override fun getAdditionalJavacArgs(): List<String> = ADDITIONAL_JAVAC_ARGS_FOR_15
override fun getAdditionalJvmArgs(): List<String> = listOf("--enable-preview")
}
override fun verifyWithDex(): Boolean = false
}
@@ -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);
}
@TestMetadata("recordDifferentPropertyOverride.kt")
public void testRecordDifferentPropertyOverride() throws Exception {
runTest("compiler/testData/codegen/java15/box/recordDifferentPropertyOverride.kt");
}
@TestMetadata("compiler/testData/codegen/java15/box/records")
@TestDataPath("$PROJECT_ROOT")
@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 testRecordDifferentSyntheticProperty() throws Exception {
runTest("compiler/testData/codegen/java15/box/recordDifferentSyntheticProperty.kt");
}
public void testAllFilesPresentInRecords() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/java15/box/records"), Pattern.compile("^(.+)\\.kt$"), null, true);
}
@TestMetadata("recordPropertyAccess.kt")
public void testRecordPropertyAccess() throws Exception {
runTest("compiler/testData/codegen/java15/box/recordPropertyAccess.kt");
@TestMetadata("bytecodeShapeForJava.kt")
public void testBytecodeShapeForJava() throws Exception {
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");
}
}
}