From b775501042a054757930c12de14267729fb5f1c7 Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Fri, 23 Nov 2018 12:51:43 +0300 Subject: [PATCH] Translate KClass references to built-in types in serialization plugin correctly. Add overload with ClassDescriptor to `getPrimitiveClass` translation function and increase it visibility to public to be usable from plugin. --- .../expression/ExpressionVisitor.java | 91 ++++++++++--------- .../kotlin/js/translate/utils/utils.kt | 9 +- .../compiler/backend/js/JsCodegenUtil.kt | 11 +-- .../backend/js/SerializerJsTranslator.kt | 6 +- 4 files changed, 62 insertions(+), 55 deletions(-) diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java index 46c91680079..e06ce9a091f 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/ExpressionVisitor.java @@ -267,11 +267,13 @@ public final class ExpressionVisitor extends TranslatorVisitor { DoubleColonLHS lhs = context.bindingContext().get(DOUBLE_COLON_LHS, receiverExpression); assert lhs != null : "Class literal expression should have LHS resolved"; + ClassifierDescriptor descriptor = lhs.getType().getConstructor().getDeclarationDescriptor(); + if (lhs instanceof DoubleColonLHS.Expression && !((DoubleColonLHS.Expression) lhs).isObjectQualifier()) { JsExpression receiver = translateAsExpression(receiverExpression, context); receiver = TranslationUtils.coerce(context, receiver, context.getCurrentModule().getBuiltIns().getAnyType()); if (isPrimitiveClassLiteral(lhs.getType())) { - JsExpression primitiveExpression = getPrimitiveClass(context, lhs.getType()); + JsExpression primitiveExpression = getPrimitiveClass(context, descriptor); if (primitiveExpression != null) { return JsAstUtils.newSequence(Arrays.asList(receiver, primitiveExpression)); } @@ -279,54 +281,59 @@ public final class ExpressionVisitor extends TranslatorVisitor { return new JsInvocation(context.namer().kotlin(GET_KCLASS_FROM_EXPRESSION), receiver); } - JsExpression primitiveExpression = getPrimitiveClass(context, lhs.getType()); - if (primitiveExpression != null) return primitiveExpression; - return new JsInvocation(context.getReferenceToIntrinsic(GET_KCLASS), UtilsKt.getReferenceToJsClass(lhs.getType(), context)); + return getObjectKClass(context, descriptor); } - private static JsExpression getPrimitiveClass(@NotNull TranslationContext context, @NotNull KotlinType type) { + @NotNull + public static JsExpression getObjectKClass(@NotNull TranslationContext context, @Nullable ClassifierDescriptor descriptor) { + JsExpression primitiveExpression = getPrimitiveClass(context, descriptor); + if (primitiveExpression != null) return primitiveExpression; + return new JsInvocation(context.getReferenceToIntrinsic(GET_KCLASS), UtilsKt.getReferenceToJsClass(descriptor, context)); + } + + @Nullable + private static JsExpression getPrimitiveClass(@NotNull TranslationContext context, @Nullable ClassifierDescriptor classifierDescriptor) { if (!context.getConfig().isAtLeast(LanguageVersion.KOTLIN_1_2) || findPrimitiveClassesObject(context) == null) return null; + if (!(classifierDescriptor instanceof ClassDescriptor)) return null; + ClassDescriptor descriptor = (ClassDescriptor) classifierDescriptor; - ClassifierDescriptor descriptor = type.getConstructor().getDeclarationDescriptor(); - if (descriptor instanceof ClassDescriptor) { - FqName fqName = DescriptorUtilsKt.getFqNameSafe(descriptor); - switch (fqName.asString()) { - case "kotlin.Boolean": - case "kotlin.Byte": - case "kotlin.Short": - case "kotlin.Int": - case "kotlin.Float": - case "kotlin.Double": - case "kotlin.String": - case "kotlin.Array": - case "kotlin.Any": - case "kotlin.Throwable": - case "kotlin.Number": - case "kotlin.Nothing": - case "kotlin.BooleanArray": - case "kotlin.CharArray": - case "kotlin.ByteArray": - case "kotlin.ShortArray": - case "kotlin.IntArray": - case "kotlin.LongArray": - case "kotlin.FloatArray": - case "kotlin.DoubleArray": - return getKotlinPrimitiveClassRef(context, StringUtil.decapitalize(fqName.shortName().asString()) + "Class"); + FqName fqName = DescriptorUtilsKt.getFqNameSafe(descriptor); + switch (fqName.asString()) { + case "kotlin.Boolean": + case "kotlin.Byte": + case "kotlin.Short": + case "kotlin.Int": + case "kotlin.Float": + case "kotlin.Double": + case "kotlin.String": + case "kotlin.Array": + case "kotlin.Any": + case "kotlin.Throwable": + case "kotlin.Number": + case "kotlin.Nothing": + case "kotlin.BooleanArray": + case "kotlin.CharArray": + case "kotlin.ByteArray": + case "kotlin.ShortArray": + case "kotlin.IntArray": + case "kotlin.LongArray": + case "kotlin.FloatArray": + case "kotlin.DoubleArray": + return getKotlinPrimitiveClassRef(context, StringUtil.decapitalize(fqName.shortName().asString()) + "Class"); - default: { - if (descriptor instanceof FunctionClassDescriptor) { - FunctionClassDescriptor functionClassDescriptor = (FunctionClassDescriptor) descriptor; - if (functionClassDescriptor.getFunctionKind() == FunctionClassDescriptor.Kind.Function) { - ClassDescriptor primitivesObject = findPrimitiveClassesObject(context); - assert primitivesObject != null; - FunctionDescriptor function = DescriptorUtils.getFunctionByName( - primitivesObject.getUnsubstitutedMemberScope(), Name.identifier("functionClass")); - JsExpression functionRef = pureFqn(context.getInlineableInnerNameForDescriptor(function), null); - return new JsInvocation(functionRef, new JsIntLiteral(functionClassDescriptor.getArity())); - } + default: { + if (descriptor instanceof FunctionClassDescriptor) { + FunctionClassDescriptor functionClassDescriptor = (FunctionClassDescriptor) descriptor; + if (functionClassDescriptor.getFunctionKind() == FunctionClassDescriptor.Kind.Function) { + ClassDescriptor primitivesObject = findPrimitiveClassesObject(context); + assert primitivesObject != null; + FunctionDescriptor function = DescriptorUtils.getFunctionByName( + primitivesObject.getUnsubstitutedMemberScope(), Name.identifier("functionClass")); + JsExpression functionRef = pureFqn(context.getInlineableInnerNameForDescriptor(function), null); + return new JsInvocation(functionRef, new JsIntLiteral(functionClassDescriptor.getArity())); } - break; } + break; } } return null; diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt index b5b521152e2..f55d2ee92f4 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/utils.kt @@ -122,9 +122,10 @@ fun List.splitToRanges(classifier: (T) -> S): List, S>> { return result } -fun getReferenceToJsClass(type: KotlinType, context: TranslationContext): JsExpression { - val classifierDescriptor = type.constructor.declarationDescriptor +fun getReferenceToJsClass(type: KotlinType, context: TranslationContext): JsExpression = + getReferenceToJsClass(type.constructor.declarationDescriptor, context) +fun getReferenceToJsClass(classifierDescriptor: ClassifierDescriptor?, context: TranslationContext): JsExpression { return when (classifierDescriptor) { is ClassDescriptor -> { ReferenceTranslator.translateAsTypeReference(classifierDescriptor, context) @@ -135,10 +136,10 @@ fun getReferenceToJsClass(type: KotlinType, context: TranslationContext): JsExpr context.usageTracker()?.used(classifierDescriptor) context.captureTypeIfNeedAndGetCapturedName(classifierDescriptor) - ?: context.getNameForDescriptor(classifierDescriptor).makeRef() + ?: context.getNameForDescriptor(classifierDescriptor).makeRef() } else -> { - throw IllegalStateException("Can't get reference for $type") + throw IllegalStateException("Can't get reference for $classifierDescriptor") } } } diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt index 60397889bf3..7d4436c27e0 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt @@ -20,6 +20,7 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi import org.jetbrains.kotlin.js.translate.context.TranslationContext +import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor import org.jetbrains.kotlin.js.translate.expression.translateAndAliasParameters import org.jetbrains.kotlin.js.translate.reference.ReferenceTranslator import org.jetbrains.kotlin.js.translate.utils.JsAstUtils @@ -147,14 +148,14 @@ internal fun SerializerJsTranslator.serializerInstance( return context.serializerObjectGetter(serializerClass) } else { var args = if (serializerClass.classId == enumSerializerId || serializerClass.classId == contextSerializerId) - listOf(createGetKClassExpression(kType.toClassDescriptor!!)) + listOf(ExpressionVisitor.getObjectKClass(context, kType.toClassDescriptor!!)) else kType.arguments.map { val argSer = findTypeSerializerOrContext(module, it.type, sourceElement = serializerClass.findPsi()) val expr = serializerInstance(argSer, module, it.type, it.type.genericIndex) ?: return null if (it.type.isMarkedNullable) JsNew(nullableSerClass, listOf(expr)) else expr } if (serializerClass.classId == referenceArraySerializerId) - args = listOf(createGetKClassExpression(kType.arguments[0].type.toClassDescriptor!!)) + args + args = listOf(ExpressionVisitor.getObjectKClass(context, kType.arguments[0].type.toClassDescriptor!!)) + args val serializable = getSerializableClassDescriptorBySerializer(serializerClass) val ref = if (serializable?.declaredTypeParameters?.isNotEmpty() == true) { val desc = requireNotNull( @@ -171,12 +172,6 @@ internal fun SerializerJsTranslator.serializerInstance( } } -internal fun SerializerJsTranslator.createGetKClassExpression(classDescriptor: ClassDescriptor): JsExpression = - JsInvocation( - context.namer().kotlin("getKClass"), - context.translateQualifiedReference(classDescriptor) - ) - fun TranslationContext.buildInitializersRemapping(forClass: KtPureClassOrObject): Map = forClass.run { (bodyPropertiesDescriptorsMap(bindingContext()).mapValues { it.value.delegateExpressionOrInitializer } + primaryPropertiesDescriptorsMap(bindingContext()).mapValues { it.value.defaultValue }) diff --git a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt index fc9d8261c6f..e8aab144722 100644 --- a/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt +++ b/plugins/kotlin-serialization/kotlin-serialization-compiler/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/SerializerJsTranslator.kt @@ -25,6 +25,7 @@ import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.declaration.DeclarationBodyVisitor import org.jetbrains.kotlin.js.translate.declaration.DefaultPropertyTranslator +import org.jetbrains.kotlin.js.translate.expression.ExpressionVisitor import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.intrinsic.functions.factories.TopLevelFIF.KOTLIN_EQUALS import org.jetbrains.kotlin.js.translate.utils.JsAstUtils @@ -277,7 +278,10 @@ class SerializerJsTranslator(descriptor: ClassDescriptor, .single { !unknownSer || (it.valueParameters.size == 3) } .let { context.getNameForDescriptor(it) } val readArgs = mutableListOf(serialClassDescRef, JsIntLiteral(i)) - if (unknownSer) readArgs.add(createGetKClassExpression(property.type.toClassDescriptor!!)) + if (unknownSer) readArgs.add(ExpressionVisitor.getObjectKClass( + this@SerializerJsTranslator.context, + property.type.toClassDescriptor!! + )) JsInvocation(JsNameRef(readFunc, inputVar), readArgs) } else {