Fix serialization for suspend function types.

Since now `suspend (Int) -> String` will be serialized as `(Int, Continuation<String>) -> Any?` + suspend flag.

Before this change such type serialized like this: Function2<Int, String> + suspend flag. And yes, type `Function2<Int, String>` isn't correct, because Function2 expect 3 type arguments.
We have special logic for this case and we deserialize such error-written types correctly.

(cherry picked from commit 3518cbe)
This commit is contained in:
Stanislav Erokhin
2017-02-01 15:03:45 +03:00
committed by Stanislav Erokhin
parent 382122470f
commit a4272caa8c
5 changed files with 145 additions and 66 deletions
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor
import org.jetbrains.kotlin.builtins.getFunctionalClassKind
import org.jetbrains.kotlin.builtins.isSuspendFunctionType
import org.jetbrains.kotlin.builtins.transformSuspendFunctionToRuntimeFunctionType
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated
@@ -451,6 +452,12 @@ class DescriptorSerializer private constructor(
return lowerBound
}
if (type.isSuspendFunctionType) {
val functionType = type(transformSuspendFunctionToRuntimeFunctionType(type))
functionType.flags = Flags.getTypeFlags(true)
return functionType
}
val descriptor = type.constructor.declarationDescriptor
when (descriptor) {
is ClassDescriptor, is TypeAliasDescriptor -> {
@@ -489,25 +496,12 @@ class DescriptorSerializer private constructor(
}
private fun fillFromPossiblyInnerType(builder: ProtoBuf.Type.Builder, type: PossiblyInnerType) {
val classifierDescriptor: ClassifierDescriptorWithTypeParameters
val isSuspendType: Boolean
val originalClassifierDescriptor = type.classifierDescriptor
if (originalClassifierDescriptor.getFunctionalClassKind() == FunctionClassDescriptor.Kind.SuspendFunction) {
classifierDescriptor = originalClassifierDescriptor.builtIns.getFunction(originalClassifierDescriptor.declaredTypeParameters.size)
isSuspendType = true
}
else {
classifierDescriptor = originalClassifierDescriptor
isSuspendType = false
}
val classifierDescriptor = type.classifierDescriptor
val classifierId = getClassifierId(classifierDescriptor)
when (classifierDescriptor) {
is ClassDescriptor -> builder.className = classifierId
is TypeAliasDescriptor -> builder.typeAliasName = classifierId
}
builder.flags = Flags.getTypeFlags(isSuspendType)
for (projection in type.arguments) {
builder.addArgument(typeArgument(projection))
@@ -32,7 +32,7 @@ class JvmMetadataVersion(vararg numbers: Int) : BinaryVersion(*numbers) {
var skipCheck: Boolean = false
@JvmField
val INSTANCE = JvmMetadataVersion(1, 1, 4)
val INSTANCE = JvmMetadataVersion(1, 1, 5)
@JvmField
val INVALID_VERSION = JvmMetadataVersion()
@@ -64,22 +64,6 @@ interface TypeMappingConfiguration<out T : Any> {
const val NON_EXISTENT_CLASS_NAME = "error/NonExistentClass"
private val FAKE_CONTINUATION_CLASS_DESCRIPTOR =
MutableClassDescriptor(
ErrorUtils.getErrorModule(),
ClassKind.INTERFACE, /* isInner = */ false, /* isExternal = */ false,
DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME.shortName(), SourceElement.NO_SOURCE
).apply {
modality = Modality.ABSTRACT
visibility = Visibilities.PUBLIC
setTypeParameterDescriptors(
TypeParameterDescriptorImpl.createWithDefaultBound(
this, Annotations.EMPTY, false, Variance.IN_VARIANCE, Name.identifier("T"), 0
).let(::listOf)
)
createTypeConstructor()
}
private val CONTINUATION_INTERNAL_NAME =
JvmClassName.byClassId(ClassId.topLevel(DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME)).internalName
@@ -93,23 +77,7 @@ fun <T : Any> mapType(
): T {
if (kotlinType.isSuspendFunctionType) {
return mapType(
createFunctionType(
kotlinType.builtIns,
kotlinType.annotations,
kotlinType.getReceiverTypeFromFunctionType(),
kotlinType.getValueParameterTypesFromFunctionType().map(TypeProjection::getType) +
KotlinTypeFactory.simpleType(
Annotations.EMPTY,
// Continuation interface is not a part of built-ins anymore, it has been moved to stdlib.
// While it must be somewhere in the dependencies, but here we don't have a reference to the module,
// and it's rather complicated to inject it by now, so we just use a fake class descriptor.
FAKE_CONTINUATION_CLASS_DESCRIPTOR.typeConstructor,
listOf(kotlinType.getReturnTypeFromFunctionType().asTypeProjection()), nullable = false
),
// TODO: names
null,
kotlinType.builtIns.nullableAnyType
),
transformSuspendFunctionToRuntimeFunctionType(kotlinType),
factory, mode, typeMappingConfiguration, descriptorTypeWriter,
writeGenericType
)
@@ -0,0 +1,100 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.builtins
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.descriptors.Visibilities
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
import org.jetbrains.kotlin.descriptors.impl.MutableClassDescriptor
import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.types.typeUtil.asTypeProjection
import org.jetbrains.kotlin.types.typeUtil.builtIns
val FAKE_CONTINUATION_CLASS_DESCRIPTOR =
MutableClassDescriptor(
EmptyPackageFragmentDescriptor(ErrorUtils.getErrorModule(), DescriptorUtils.COROUTINES_PACKAGE_FQ_NAME),
ClassKind.INTERFACE, /* isInner = */ false, /* isExternal = */ false,
DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME.shortName(), SourceElement.NO_SOURCE
).apply {
modality = Modality.ABSTRACT
visibility = Visibilities.PUBLIC
setTypeParameterDescriptors(
TypeParameterDescriptorImpl.createWithDefaultBound(
this, Annotations.EMPTY, false, Variance.IN_VARIANCE, Name.identifier("T"), 0
).let(::listOf)
)
createTypeConstructor()
}
fun transformSuspendFunctionToRuntimeFunctionType(suspendFunType: KotlinType): SimpleType {
assert(suspendFunType.isSuspendFunctionType) {
"This type should be suspend function type: $suspendFunType"
}
return createFunctionType(
suspendFunType.builtIns,
suspendFunType.annotations,
suspendFunType.getReceiverTypeFromFunctionType(),
suspendFunType.getValueParameterTypesFromFunctionType().map(TypeProjection::getType) +
KotlinTypeFactory.simpleType(
Annotations.EMPTY,
// Continuation interface is not a part of built-ins anymore, it has been moved to stdlib.
// While it must be somewhere in the dependencies, but here we don't have a reference to the module,
// and it's rather complicated to inject it by now, so we just use a fake class descriptor.
FAKE_CONTINUATION_CLASS_DESCRIPTOR.typeConstructor,
listOf(suspendFunType.getReturnTypeFromFunctionType().asTypeProjection()), nullable = false
),
// TODO: names
null,
suspendFunType.builtIns.nullableAnyType
).makeNullableAsSpecified(suspendFunType.isMarkedNullable)
}
fun transformRuntimeFunctionTypeToSuspendFunction(funType: KotlinType): SimpleType? {
assert(funType.isFunctionType) {
"This type should be function type: $funType"
}
val continuationArgumentType = funType.getValueParameterTypesFromFunctionType().lastOrNull()?.type ?: return null
if (continuationArgumentType.constructor.declarationDescriptor?.fqNameSafe != DescriptorUtils.CONTINUATION_INTERFACE_FQ_NAME
|| continuationArgumentType.arguments.size != 1
) {
return null
}
val suspendReturnType = continuationArgumentType.arguments.single().type
return createFunctionType(
funType.builtIns,
funType.annotations,
funType.getReceiverTypeFromFunctionType(),
funType.getValueParameterTypesFromFunctionType().dropLast(1).map(TypeProjection::getType),
// TODO: names
null,
suspendReturnType,
suspendFunction = true
).makeNullableAsSpecified(funType.isMarkedNullable)
}
@@ -16,18 +16,19 @@
package org.jetbrains.kotlin.serialization.deserialization
import org.jetbrains.kotlin.builtins.functions.BuiltInFictitiousFunctionClassFactory
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.builtins.transformRuntimeFunctionTypeToSuspendFunction
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.annotations.AnnotationWithTarget
import org.jetbrains.kotlin.descriptors.annotations.Annotations
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
import org.jetbrains.kotlin.serialization.Flags
import org.jetbrains.kotlin.serialization.ProtoBuf
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedAnnotationsWithPossibleTargets
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedTypeParameterDescriptor
import org.jetbrains.kotlin.types.*
import org.jetbrains.kotlin.utils.addToStdlib.check
import org.jetbrains.kotlin.utils.toReadOnlyList
import java.util.*
@@ -99,7 +100,12 @@ class TypeDeserializer(
typeArgument(constructor.parameters.getOrNull(index), proto)
}.toReadOnlyList()
val simpleType = KotlinTypeFactory.simpleType(annotations, constructor, arguments, proto.nullable)
val simpleType = if (Flags.SUSPEND_TYPE.get(proto.flags)) {
createSuspendFunctionType(annotations, constructor, arguments, proto.nullable)
}
else {
KotlinTypeFactory.simpleType(annotations, constructor, arguments, proto.nullable)
}
val abbreviatedTypeProto = proto.abbreviatedType(c.typeTable) ?: return simpleType
return simpleType.withAbbreviation(simpleType(abbreviatedTypeProto, additionalAnnotations))
@@ -108,13 +114,8 @@ class TypeDeserializer(
private fun typeConstructor(proto: ProtoBuf.Type): TypeConstructor =
when {
proto.hasClassName() -> {
if (Flags.SUSPEND_TYPE.get(proto.flags)) {
getSuspendFunctionTypeConstructor(proto)
}
else {
classDescriptors(proto.className)?.typeConstructor
?: c.components.notFoundClasses.getClass(proto, c.nameResolver, c.typeTable)
}
classDescriptors(proto.className)?.typeConstructor
?: c.components.notFoundClasses.getClass(proto, c.nameResolver, c.typeTable)
}
proto.hasTypeParameter() ->
typeParameterTypeConstructor(proto.typeParameter)
@@ -132,14 +133,30 @@ class TypeDeserializer(
else -> ErrorUtils.createErrorTypeConstructor("Unknown type")
}
private fun getSuspendFunctionTypeConstructor(proto: ProtoBuf.Type): TypeConstructor {
val classId = c.nameResolver.getClassId(proto.className)
val arity = BuiltInFictitiousFunctionClassFactory.getFunctionalClassArity(classId.shortClassName.asString(), classId.packageFqName)
return if (arity != null)
c.containingDeclaration.builtIns.getSuspendFunction(arity - 1).typeConstructor
else
ErrorUtils.createErrorTypeConstructor("Class is not a FunctionN ${classId.asString()}")
private fun createSuspendFunctionType(
annotations: Annotations,
functionTypeConstructor: TypeConstructor,
arguments: List<TypeProjection>,
nullable: Boolean
): SimpleType {
val result = when (functionTypeConstructor.parameters.size - arguments.size) {
0 -> {
val functionType = KotlinTypeFactory.simpleType(annotations, functionTypeConstructor, arguments, nullable)
functionType.check { it.isFunctionType }?.let(::transformRuntimeFunctionTypeToSuspendFunction)
}
// This case for types written by eap compiler 1.1
1 -> {
val arity = arguments.size - 1
if (arity > 0) {
KotlinTypeFactory.simpleType(annotations, functionTypeConstructor.builtIns.getSuspendFunction(arity).typeConstructor, arguments, nullable)
}
else {
null
}
}
else -> null
}
return result ?: ErrorUtils.createErrorTypeWithArguments("Bad suspend function in metadata with constructor: $functionTypeConstructor", arguments)
}
private fun typeParameterTypeConstructor(typeParameterId: Int): TypeConstructor? =