From df8fed096cc6bf140cbb8a83765bb4a91ea44b05 Mon Sep 17 00:00:00 2001 From: Michael Nedzelsky Date: Thu, 29 Jan 2015 18:39:54 +0300 Subject: [PATCH] add support for compile time constant serialization --- .../src/BuiltInsSerializer.kt | 7 ++ .../serialization/compileTimeConstants.kt | 34 ++++++++ .../serialization/compileTimeConstants.txt | 81 +++++++++++++++++++ .../builtins/BuiltInsSerializerTest.kt | 4 + .../builtins/DebugBuiltInsProtoBuf.java | 28 +++++-- ...tBinaryClassAnnotationAndConstantLoader.kt | 8 +- .../BuiltInsAnnotationAndConstantLoader.kt | 7 +- core/serialization/src/builtins.proto | 1 + .../builtins/BuiltInsProtoBuf.java | 16 ++++ .../AnnotationAndConstantLoader.java | 3 +- .../deserialization/AnnotationDeserializer.kt | 20 ++--- .../deserialization/MemberDeserializer.kt | 2 +- 12 files changed, 187 insertions(+), 24 deletions(-) create mode 100644 compiler/testData/serialization/compileTimeConstants.kt create mode 100644 compiler/testData/serialization/compileTimeConstants.txt diff --git a/compiler/builtins-serializer/src/BuiltInsSerializer.kt b/compiler/builtins-serializer/src/BuiltInsSerializer.kt index b6a68deb0d6..b8dae0108a4 100644 --- a/compiler/builtins-serializer/src/BuiltInsSerializer.kt +++ b/compiler/builtins-serializer/src/BuiltInsSerializer.kt @@ -41,6 +41,8 @@ import org.jetbrains.kotlin.load.kotlin.DeserializedResolverUtils import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.JVMConfigurationKeys +import org.jetbrains.kotlin.resolve.constants.NullValue +import org.jetbrains.kotlin.builtins.KotlinBuiltIns private object BuiltInsSerializerExtension : SerializerExtension() { override fun serializeClass(descriptor: ClassDescriptor, proto: ProtoBuf.Class.Builder, stringTable: StringTable) { @@ -71,6 +73,11 @@ private object BuiltInsSerializerExtension : SerializerExtension() { for (annotation in callable.getAnnotations()) { proto.addExtension(BuiltInsProtoBuf.callableAnnotation, AnnotationSerializer.serializeAnnotation(annotation, stringTable)) } + val compileTimeConstant = (callable as? PropertyDescriptor)?.getCompileTimeInitializer() + if (compileTimeConstant != null && compileTimeConstant !is NullValue) { + val type = compileTimeConstant.getType(KotlinBuiltIns.getInstance()) + proto.setExtension(BuiltInsProtoBuf.compileTimeValue, AnnotationSerializer.valueProto(compileTimeConstant, type, stringTable).build()) + } } override fun serializeValueParameter( diff --git a/compiler/testData/serialization/compileTimeConstants.kt b/compiler/testData/serialization/compileTimeConstants.kt new file mode 100644 index 00000000000..467114658a4 --- /dev/null +++ b/compiler/testData/serialization/compileTimeConstants.kt @@ -0,0 +1,34 @@ +package test + +enum class Weapon { + ROCK + PAPER + SCISSORS +} + +val byteConst: Byte = 10 +val shortConst: Short = 20 +val intConst: Int = 30 +val longConst: Long = 40 +val charConst: Char = 'A' +val stringConst: String = "abcd" +val booleanConst: Boolean = true +val floatConst: Float = 2.0f +val doubleConst: Double = 3.0 +val enumConst: Weapon? = Weapon.ROCK +val arrayConst: Any = byteArray(1,2) + +class Class { + val byteConst: Byte = 10 + val shortConst: Short = 20 + val intConst: Int = 30 + val longConst: Long = 40 + val charConst: Char = 'A' + val stringConst: String = "abcd" + val booleanConst: Boolean = true + val floatConst: Float = 2.0f + val doubleConst: Double = 3.0 + val enumConst: Weapon? = Weapon.ROCK + val arrayConst: Any = byteArray(1,2) +} + diff --git a/compiler/testData/serialization/compileTimeConstants.txt b/compiler/testData/serialization/compileTimeConstants.txt new file mode 100644 index 00000000000..37669c9ea3d --- /dev/null +++ b/compiler/testData/serialization/compileTimeConstants.txt @@ -0,0 +1,81 @@ +package test + +internal val arrayConst: kotlin.Any = {1.toByte(), 2.toByte()} +internal val booleanConst: kotlin.Boolean = true +internal val byteConst: kotlin.Byte = 10.toByte() +internal val charConst: kotlin.Char = \u0041 ('A') +internal val doubleConst: kotlin.Double = 3.0.toDouble() +internal val enumConst: test.Weapon? = Weapon.ROCK +internal val floatConst: kotlin.Float = 2.0.toFloat() +internal val intConst: kotlin.Int = 30 +internal val longConst: kotlin.Long = 40.toLong() +internal val shortConst: kotlin.Short = 20.toShort() +internal val stringConst: kotlin.String = "abcd" + +internal final class Class { + public constructor Class() + internal final val arrayConst: kotlin.Any = {1.toByte(), 2.toByte()} + internal final val booleanConst: kotlin.Boolean = true + internal final val byteConst: kotlin.Byte = 10.toByte() + internal final val charConst: kotlin.Char = \u0041 ('A') + internal final val doubleConst: kotlin.Double = 3.0.toDouble() + internal final val enumConst: test.Weapon? = Weapon.ROCK + internal final val floatConst: kotlin.Float = 2.0.toFloat() + internal final val intConst: kotlin.Int = 30 + internal final val longConst: kotlin.Long = 40.toLong() + internal final val shortConst: kotlin.Short = 20.toShort() + internal final val stringConst: kotlin.String = "abcd" +} + +internal final enum class Weapon : kotlin.Enum { + public enum entry ROCK : test.Weapon { + private constructor ROCK() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: test.Weapon): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + + public class object : test.Weapon.ROCK { + private constructor () + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: test.Weapon): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + } + } + + public enum entry PAPER : test.Weapon { + private constructor PAPER() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: test.Weapon): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + + public class object : test.Weapon.PAPER { + private constructor () + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: test.Weapon): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + } + } + + public enum entry SCISSORS : test.Weapon { + private constructor SCISSORS() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: test.Weapon): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + + public class object : test.Weapon.SCISSORS { + private constructor () + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: test.Weapon): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + } + } + + private constructor Weapon() + public final override /*1*/ /*fake_override*/ fun compareTo(/*0*/ other: test.Weapon): kotlin.Int + public final override /*1*/ /*fake_override*/ fun name(): kotlin.String + public final override /*1*/ /*fake_override*/ fun ordinal(): kotlin.Int + + // Static members + public final /*synthesized*/ fun valueOf(/*0*/ value: kotlin.String): test.Weapon + public final /*synthesized*/ fun values(): kotlin.Array +} diff --git a/compiler/tests/org/jetbrains/kotlin/serialization/builtins/BuiltInsSerializerTest.kt b/compiler/tests/org/jetbrains/kotlin/serialization/builtins/BuiltInsSerializerTest.kt index fb91bd39456..cf32edb4b90 100644 --- a/compiler/tests/org/jetbrains/kotlin/serialization/builtins/BuiltInsSerializerTest.kt +++ b/compiler/tests/org/jetbrains/kotlin/serialization/builtins/BuiltInsSerializerTest.kt @@ -61,6 +61,10 @@ public class BuiltInsSerializerTest : TestCaseWithTmpdir() { doTest("simple.kt") } + fun testCompileTimeConstants() { + doTest("compileTimeConstants.kt") + } + fun testAnnotationTargets() { doTest("annotationTargets.kt") } diff --git a/compiler/tests/org/jetbrains/kotlin/serialization/builtins/DebugBuiltInsProtoBuf.java b/compiler/tests/org/jetbrains/kotlin/serialization/builtins/DebugBuiltInsProtoBuf.java index 401e2c1c5e5..cbd916edb06 100644 --- a/compiler/tests/org/jetbrains/kotlin/serialization/builtins/DebugBuiltInsProtoBuf.java +++ b/compiler/tests/org/jetbrains/kotlin/serialization/builtins/DebugBuiltInsProtoBuf.java @@ -10,6 +10,7 @@ public final class DebugBuiltInsProtoBuf { registry.add(org.jetbrains.kotlin.serialization.builtins.DebugBuiltInsProtoBuf.className); registry.add(org.jetbrains.kotlin.serialization.builtins.DebugBuiltInsProtoBuf.classAnnotation); registry.add(org.jetbrains.kotlin.serialization.builtins.DebugBuiltInsProtoBuf.callableAnnotation); + registry.add(org.jetbrains.kotlin.serialization.builtins.DebugBuiltInsProtoBuf.compileTimeValue); registry.add(org.jetbrains.kotlin.serialization.builtins.DebugBuiltInsProtoBuf.parameterAnnotation); } public static final int CLASS_NAME_FIELD_NUMBER = 150; @@ -45,6 +46,17 @@ public final class DebugBuiltInsProtoBuf { .newFileScopedGeneratedExtension( org.jetbrains.kotlin.serialization.DebugProtoBuf.Annotation.class, org.jetbrains.kotlin.serialization.DebugProtoBuf.Annotation.getDefaultInstance()); + public static final int COMPILE_TIME_VALUE_FIELD_NUMBER = 151; + /** + * extend .org.jetbrains.kotlin.serialization.Callable { ... } + */ + public static final + com.google.protobuf.GeneratedMessage.GeneratedExtension< + org.jetbrains.kotlin.serialization.DebugProtoBuf.Callable, + org.jetbrains.kotlin.serialization.DebugProtoBuf.Annotation.Argument.Value> compileTimeValue = com.google.protobuf.GeneratedMessage + .newFileScopedGeneratedExtension( + org.jetbrains.kotlin.serialization.DebugProtoBuf.Annotation.Argument.Value.class, + org.jetbrains.kotlin.serialization.DebugProtoBuf.Annotation.Argument.Value.getDefaultInstance()); public static final int PARAMETER_ANNOTATION_FIELD_NUMBER = 150; /** * extend .org.jetbrains.kotlin.serialization.Callable.ValueParameter { ... } @@ -76,11 +88,14 @@ public final class DebugBuiltInsProtoBuf { "tation:z\n\023callable_annotation\022,.org.jetb" + "rains.kotlin.serialization.Callable\030\226\001 \003", "(\0132..org.jetbrains.kotlin.serialization." + - "Annotation:\212\001\n\024parameter_annotation\022;.or" + - "g.jetbrains.kotlin.serialization.Callabl" + - "e.ValueParameter\030\226\001 \003(\0132..org.jetbrains." + - "kotlin.serialization.AnnotationB\027B\025Debug" + - "BuiltInsProtoBuf" + "Annotation:\210\001\n\022compile_time_value\022,.org." + + "jetbrains.kotlin.serialization.Callable\030" + + "\227\001 \001(\0132=.org.jetbrains.kotlin.serializat" + + "ion.Annotation.Argument.Value:\212\001\n\024parame" + + "ter_annotation\022;.org.jetbrains.kotlin.se" + + "rialization.Callable.ValueParameter\030\226\001 \003" + + "(\0132..org.jetbrains.kotlin.serialization." + + "AnnotationB\027B\025DebugBuiltInsProtoBuf" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -90,7 +105,8 @@ public final class DebugBuiltInsProtoBuf { className.internalInit(descriptor.getExtensions().get(0)); classAnnotation.internalInit(descriptor.getExtensions().get(1)); callableAnnotation.internalInit(descriptor.getExtensions().get(2)); - parameterAnnotation.internalInit(descriptor.getExtensions().get(3)); + compileTimeValue.internalInit(descriptor.getExtensions().get(3)); + parameterAnnotation.internalInit(descriptor.getExtensions().get(4)); return null; } }; diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt index c12d818bb36..e0d75ee2e22 100644 --- a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/AbstractBinaryClassAnnotationAndConstantLoader.kt @@ -31,6 +31,8 @@ import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.serialization.Flags import org.jetbrains.kotlin.serialization.deserialization.AnnotationAndConstantLoader import org.jetbrains.kotlin.load.java.JvmAnnotationNames +import org.jetbrains.kotlin.serialization.deserialization.DeserializationContext +import org.jetbrains.kotlin.types.JetType public abstract class AbstractBinaryClassAnnotationAndConstantLoader( storageManager: StorageManager, @@ -130,11 +132,11 @@ public abstract class AbstractBinaryClassAnnotationAndConstantLoader? { - // TODO: support deserialization of compile time constants in built-ins when needed - throw UnsupportedOperationException() + val value = proto.getExtension(BuiltInsProtoBuf.compileTimeValue) + return deserializer.resolveValue(expectedType, value, nameResolver) } } diff --git a/core/serialization/src/builtins.proto b/core/serialization/src/builtins.proto index 59ba30effe3..6bfe1cdbc47 100644 --- a/core/serialization/src/builtins.proto +++ b/core/serialization/src/builtins.proto @@ -32,6 +32,7 @@ extend Class { extend Callable { repeated Annotation callable_annotation = 150; + optional Annotation.Argument.Value compile_time_value = 151; } extend Callable.ValueParameter { diff --git a/core/serialization/src/org/jetbrains/kotlin/serialization/builtins/BuiltInsProtoBuf.java b/core/serialization/src/org/jetbrains/kotlin/serialization/builtins/BuiltInsProtoBuf.java index 30a8e24cb99..2d7431f98c6 100644 --- a/core/serialization/src/org/jetbrains/kotlin/serialization/builtins/BuiltInsProtoBuf.java +++ b/core/serialization/src/org/jetbrains/kotlin/serialization/builtins/BuiltInsProtoBuf.java @@ -10,6 +10,7 @@ public final class BuiltInsProtoBuf { registry.add(org.jetbrains.kotlin.serialization.builtins.BuiltInsProtoBuf.className); registry.add(org.jetbrains.kotlin.serialization.builtins.BuiltInsProtoBuf.classAnnotation); registry.add(org.jetbrains.kotlin.serialization.builtins.BuiltInsProtoBuf.callableAnnotation); + registry.add(org.jetbrains.kotlin.serialization.builtins.BuiltInsProtoBuf.compileTimeValue); registry.add(org.jetbrains.kotlin.serialization.builtins.BuiltInsProtoBuf.parameterAnnotation); } public static final int CLASS_NAME_FIELD_NUMBER = 150; @@ -57,6 +58,21 @@ public final class BuiltInsProtoBuf { 150, com.google.protobuf.WireFormat.FieldType.MESSAGE, false); + public static final int COMPILE_TIME_VALUE_FIELD_NUMBER = 151; + /** + * extend .org.jetbrains.kotlin.serialization.Callable { ... } + */ + public static final + com.google.protobuf.GeneratedMessageLite.GeneratedExtension< + org.jetbrains.kotlin.serialization.ProtoBuf.Callable, + org.jetbrains.kotlin.serialization.ProtoBuf.Annotation.Argument.Value> compileTimeValue = com.google.protobuf.GeneratedMessageLite + .newSingularGeneratedExtension( + org.jetbrains.kotlin.serialization.ProtoBuf.Callable.getDefaultInstance(), + org.jetbrains.kotlin.serialization.ProtoBuf.Annotation.Argument.Value.getDefaultInstance(), + org.jetbrains.kotlin.serialization.ProtoBuf.Annotation.Argument.Value.getDefaultInstance(), + null, + 151, + com.google.protobuf.WireFormat.FieldType.MESSAGE); public static final int PARAMETER_ANNOTATION_FIELD_NUMBER = 150; /** * extend .org.jetbrains.kotlin.serialization.Callable.ValueParameter { ... } diff --git a/core/serialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationAndConstantLoader.java b/core/serialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationAndConstantLoader.java index d665deac23e..66d363002a9 100644 --- a/core/serialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationAndConstantLoader.java +++ b/core/serialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationAndConstantLoader.java @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.serialization.deserialization; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.serialization.ProtoBuf; +import org.jetbrains.kotlin.types.JetType; import java.util.List; @@ -51,6 +52,6 @@ public interface AnnotationAndConstantLoader { @NotNull ProtoContainer container, @NotNull ProtoBuf.Callable proto, @NotNull NameResolver nameResolver, - @NotNull AnnotatedCallableKind kind + @NotNull JetType expectedType ); } diff --git a/core/serialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationDeserializer.kt b/core/serialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationDeserializer.kt index ea8be8e8c84..d4930a87147 100644 --- a/core/serialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationDeserializer.kt +++ b/core/serialization/src/org/jetbrains/kotlin/serialization/deserialization/AnnotationDeserializer.kt @@ -33,6 +33,7 @@ import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.types.ErrorUtils import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.types.Variance +import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf public class AnnotationDeserializer(private val module: ModuleDescriptor) { private val builtIns: KotlinBuiltIns @@ -62,7 +63,7 @@ public class AnnotationDeserializer(private val module: ModuleDescriptor) { return Pair(parameter, resolveValue(parameter.getType(), proto.getValue(), nameResolver)) } - private fun resolveValue( + public fun resolveValue( expectedType: JetType, value: Value, nameResolver: NameResolver @@ -90,9 +91,7 @@ public class AnnotationDeserializer(private val module: ModuleDescriptor) { AnnotationValue(deserializeAnnotation(value.getAnnotation(), nameResolver)) } Type.ARRAY -> { - if (!KotlinBuiltIns.isArray(expectedType) && - !KotlinBuiltIns.isPrimitiveArray(expectedType)) return ErrorValue.create("Unexpected argument value") - + val expectedIsArray = KotlinBuiltIns.isArray(expectedType) || KotlinBuiltIns.isPrimitiveArray(expectedType) val arrayElements = value.getArrayElementList() val actualArrayType = @@ -102,13 +101,13 @@ public class AnnotationDeserializer(private val module: ModuleDescriptor) { builtIns.getArrayType(Variance.INVARIANT, actualElementType) } else { - // In the case of empty array, no element has the element type, so we fall back to the expected type. + // In the case of empty array, no element has the element type, so we fall back to the expected type, if any. // This is not very accurate when annotation class has been changed without recompiling clients, // but should not in fact matter because the value is empty anyway - expectedType + if (expectedIsArray) expectedType else builtIns.getArrayType(Variance.INVARIANT, builtIns.getAnyType()) } - val expectedElementType = builtIns.getArrayElementType(expectedType) + val expectedElementType = builtIns.getArrayElementType(if (expectedIsArray) expectedType else actualArrayType) ArrayValue( arrayElements.map { resolveValue(expectedElementType, it, nameResolver) }, @@ -119,12 +118,13 @@ public class AnnotationDeserializer(private val module: ModuleDescriptor) { else -> error("Unsupported annotation argument type: ${value.getType()} (expected $expectedType)") } - if (result.getType(builtIns) != expectedType) { + if (result.getType(builtIns) isSubtypeOf expectedType) { + return result + } + else { // This means that an annotation class has been changed incompatibly without recompiling clients return ErrorValue.create("Unexpected argument value") } - - return result } // NOTE: see analogous code in BinaryClassAnnotationAndConstantLoaderImpl diff --git a/core/serialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt b/core/serialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt index 32e07fd73a5..56e5e709684 100644 --- a/core/serialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt +++ b/core/serialization/src/org/jetbrains/kotlin/serialization/deserialization/MemberDeserializer.kt @@ -113,7 +113,7 @@ public class MemberDeserializer(private val c: DeserializationContext) { property.setCompileTimeInitializer( c.storageManager.createNullableLazyValue { val container = c.containingDeclaration.asProtoContainer() - c.components.annotationAndConstantLoader.loadPropertyConstant(container, proto, c.nameResolver, AnnotatedCallableKind.PROPERTY) + c.components.annotationAndConstantLoader.loadPropertyConstant(container, proto, c.nameResolver, property.getReturnType()) } ) }