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.
This commit is contained in:
Leonid Startsev
2018-11-23 12:51:43 +03:00
parent 5f2cea917f
commit b775501042
4 changed files with 62 additions and 55 deletions
@@ -267,11 +267,13 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
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<JsNode> {
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;
@@ -122,9 +122,10 @@ fun <T, S> List<T>.splitToRanges(classifier: (T) -> S): List<Pair<List<T>, 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")
}
}
}
@@ -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<PropertyDescriptor, KtExpression?> = forClass.run {
(bodyPropertiesDescriptorsMap(bindingContext()).mapValues { it.value.delegateExpressionOrInitializer } +
primaryPropertiesDescriptorsMap(bindingContext()).mapValues { it.value.defaultValue })
@@ -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 {