From 456fe858a096313e877f9724ce8577278048667e Mon Sep 17 00:00:00 2001 From: pyos Date: Wed, 9 Nov 2022 17:42:04 +0100 Subject: [PATCH] FE: commonize logic for value class representation building --- .../deserialization/ValueClassUtil.kt | 45 +++++++++++++ .../DeserializedClassDescriptor.kt | 65 ++++--------------- 2 files changed, 59 insertions(+), 51 deletions(-) create mode 100644 core/deserialization.common/src/org/jetbrains/kotlin/serialization/deserialization/ValueClassUtil.kt diff --git a/core/deserialization.common/src/org/jetbrains/kotlin/serialization/deserialization/ValueClassUtil.kt b/core/deserialization.common/src/org/jetbrains/kotlin/serialization/deserialization/ValueClassUtil.kt new file mode 100644 index 00000000000..a7f95382037 --- /dev/null +++ b/core/deserialization.common/src/org/jetbrains/kotlin/serialization/deserialization/ValueClassUtil.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.serialization.deserialization + +import org.jetbrains.kotlin.descriptors.InlineClassRepresentation +import org.jetbrains.kotlin.descriptors.MultiFieldValueClassRepresentation +import org.jetbrains.kotlin.descriptors.ValueClassRepresentation +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.NameResolver +import org.jetbrains.kotlin.metadata.deserialization.TypeTable +import org.jetbrains.kotlin.metadata.deserialization.inlineClassUnderlyingType +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.model.SimpleTypeMarker + +fun ProtoBuf.Class.loadValueClassRepresentation( + nameResolver: NameResolver, + typeTable: TypeTable, + typeDeserializer: (ProtoBuf.Type) -> T, + typeOfPublicProperty: (Name) -> T?, +): ValueClassRepresentation? { + if (multiFieldValueClassUnderlyingNameCount > 0) { + val names = multiFieldValueClassUnderlyingNameList.map { nameResolver.getName(it) } + val typeIdCount = multiFieldValueClassUnderlyingTypeIdCount + val typeCount = multiFieldValueClassUnderlyingTypeCount + val types = when (typeIdCount to typeCount) { + names.size to 0 -> multiFieldValueClassUnderlyingTypeIdList.map { typeTable[it] } + 0 to names.size -> multiFieldValueClassUnderlyingTypeList + else -> error("class ${nameResolver.getName(fqName)} has illegal multi-field value class representation") + }.map(typeDeserializer) + return MultiFieldValueClassRepresentation(names zip types) + } + + if (hasInlineClassUnderlyingPropertyName()) { + val propertyName = nameResolver.getName(inlineClassUnderlyingPropertyName) + val propertyType = inlineClassUnderlyingType(typeTable)?.let(typeDeserializer) + ?: typeOfPublicProperty(propertyName) + ?: error("cannot determine underlying type for value class ${nameResolver.getName(fqName)} with property $propertyName") + return InlineClassRepresentation(propertyName, propertyType) + } + + return null +} diff --git a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt index e459b9d2634..5af6b7682c9 100644 --- a/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt +++ b/core/deserialization/src/org/jetbrains/kotlin/serialization/deserialization/descriptors/DeserializedClassDescriptor.kt @@ -162,7 +162,7 @@ class DeserializedClassDescriptor( null ), Annotations.EMPTY - ); + ) } private fun computeCompanionObjectDescriptor(): ClassDescriptor? { @@ -196,60 +196,23 @@ class DeserializedClassDescriptor( override fun getValueClassRepresentation(): ValueClassRepresentation? = valueClassRepresentation() private fun computeValueClassRepresentation(): ValueClassRepresentation? { - val inlineClassRepresentation = computeInlineClassRepresentation() - val multiFieldValueClassRepresentation = computeMultiFieldValueClassRepresentation() - return when { - inlineClassRepresentation != null && multiFieldValueClassRepresentation != null -> - throw IllegalArgumentException("Class cannot have both inline class representation and multi field class representation: $this") - (isValue || isInline) && inlineClassRepresentation == null && multiFieldValueClassRepresentation == null -> - throw IllegalArgumentException("Value class has no value class representation: $this") - else -> inlineClassRepresentation ?: multiFieldValueClassRepresentation - } - } - - private fun computeInlineClassRepresentation(): InlineClassRepresentation? { if (!isInline && !isValue) return null - if (isValue && - !classProto.hasInlineClassUnderlyingPropertyName() && - !classProto.hasInlineClassUnderlyingType() && - !classProto.hasInlineClassUnderlyingTypeId() && - classProto.multiFieldValueClassUnderlyingNameCount > 0 - ) return null - - val propertyName = when { - classProto.hasInlineClassUnderlyingPropertyName() -> - c.nameResolver.getName(classProto.inlineClassUnderlyingPropertyName) - !metadataVersion.isAtLeast(1, 5, 1) -> { - // Before 1.5, inline classes did not have underlying property name & type in the metadata. - // However, they were experimental, so supposedly this logic can be removed at some point in the future. - val constructor = unsubstitutedPrimaryConstructor ?: error("Inline class has no primary constructor: $this") - constructor.valueParameters.first().name - } - else -> error("Inline class has no underlying property name in metadata: $this") + classProto.loadValueClassRepresentation(c.nameResolver, c.typeTable, c.typeDeserializer::simpleType, ::getValueClassPropertyType) + ?.let { return it } + if (!metadataVersion.isAtLeast(1, 5, 1)) { + // Before 1.5, inline classes did not have underlying property name & type in the metadata. + // However, they were experimental, so supposedly this logic can be removed at some point in the future. + val constructor = unsubstitutedPrimaryConstructor ?: error("Inline class has no primary constructor: $this") + val propertyName = constructor.valueParameters.first().name + val propertyType = getValueClassPropertyType(propertyName) ?: error("Value class has no underlying property: $this") + return InlineClassRepresentation(propertyName, propertyType) } - - val type = classProto.inlineClassUnderlyingType(c.typeTable)?.let(c.typeDeserializer::simpleType) ?: run { - val underlyingProperty = memberScope.getContributedVariables(propertyName, NoLookupLocation.FROM_DESERIALIZATION) - .singleOrNull { it.extensionReceiverParameter == null } ?: error("Value class has no underlying property: $this") - underlyingProperty.type as SimpleType - } - - return InlineClassRepresentation(propertyName, type) + return null } - private fun computeMultiFieldValueClassRepresentation(): MultiFieldValueClassRepresentation? { - val names = classProto.multiFieldValueClassUnderlyingNameList.map { c.nameResolver.getName(it) }.takeIf { it.isNotEmpty() } - ?: return null - require(isValue) { "Not a value class: $this" } - val typeIdCount = classProto.multiFieldValueClassUnderlyingTypeIdCount - val typeCount = classProto.multiFieldValueClassUnderlyingTypeCount - val types = when (typeIdCount to typeCount) { - names.size to 0 -> classProto.multiFieldValueClassUnderlyingTypeIdList.map { c.typeTable[it] } - 0 to names.size -> classProto.multiFieldValueClassUnderlyingTypeList - else -> error("Illegal multi-field value class representation: $this") - }.map { c.typeDeserializer.simpleType(it) } - return MultiFieldValueClassRepresentation(names zip types) - } + private fun getValueClassPropertyType(propertyName: Name): SimpleType? = + memberScope.getContributedVariables(propertyName, NoLookupLocation.FROM_DESERIALIZATION) + .singleOrNull { it.extensionReceiverParameter == null }?.type as SimpleType? override fun toString() = "deserialized ${if (isExpect) "expect " else ""}class $name" // not using descriptor renderer to preserve laziness