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.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
|
||||
}
|
||||
|
||||
|
||||
+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.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()
|
||||
}
|
||||
+3
-1
@@ -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
|
||||
}
|
||||
|
||||
+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);
|
||||
}
|
||||
|
||||
@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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user