From 8cf29ea4f354ee0fd38d1dadfccf102ba3f3c3de Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Wed, 20 Apr 2016 15:40:52 +0300 Subject: [PATCH] Extract JVM descriptors type mapping from backend to core Also unbind it from ASM types --- .../codegen/signature/AsmTypeFactory.kt | 32 +++ .../signature/BothSignatureWriter.java | 4 +- .../codegen/signature/JvmSignatureWriter.java | 71 ++---- .../codegen/state/KotlinTypeMapper.java | 210 +++------------- .../kotlin/codegen/state/typeMappingUtil.kt | 1 + .../kotlin/load/kotlin}/TypeMappingMode.kt | 8 +- .../load/kotlin/typeSignatureMapping.kt | 234 ++++++++++++++++++ .../org/jetbrains/kotlin/utils/functions.kt | 1 + 8 files changed, 338 insertions(+), 223 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/signature/AsmTypeFactory.kt rename {compiler/backend/src/org/jetbrains/kotlin/codegen/state => core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin}/TypeMappingMode.kt (95%) create mode 100644 core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/typeSignatureMapping.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/signature/AsmTypeFactory.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/signature/AsmTypeFactory.kt new file mode 100644 index 00000000000..54f404a7a6a --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/signature/AsmTypeFactory.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2010-2016 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.codegen.signature + +import org.jetbrains.kotlin.codegen.AsmUtil +import org.jetbrains.kotlin.load.kotlin.JvmTypeFactory +import org.jetbrains.kotlin.resolve.jvm.AsmTypes +import org.jetbrains.org.objectweb.asm.Type + +object AsmTypeFactory : JvmTypeFactory { + override fun boxType(possiblyPrimitiveType: Type) = AsmUtil.boxType(possiblyPrimitiveType) + override fun createFromString(representation: String) = Type.getType(representation) + override fun createObjectType(internalName: String) = Type.getObjectType(internalName) + override fun toString(type: Type) = type.descriptor + + override val javaLangClassType: Type + get() = AsmTypes.JAVA_CLASS_TYPE +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/signature/BothSignatureWriter.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/signature/BothSignatureWriter.java index 4a198fa2e69..3881dfd06ef 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/signature/BothSignatureWriter.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/signature/BothSignatureWriter.java @@ -67,7 +67,7 @@ public class BothSignatureWriter extends JvmSignatureWriter { * Shortcut */ @Override - public void writeAsmType(Type asmType) { + public void writeAsmType(@NotNull Type asmType) { if (asmType.getSort() != Type.OBJECT && asmType.getSort() != Type.ARRAY) { signatureVisitor().visitBaseType(asmType.getDescriptor().charAt(0)); } @@ -141,7 +141,7 @@ public class BothSignatureWriter extends JvmSignatureWriter { } @Override - public void writeTypeVariable(Name name, Type asmType) { + public void writeTypeVariable(@NotNull Name name, @NotNull Type asmType) { signatureVisitor().visitTypeVariable(name.asString()); generic = true; super.writeTypeVariable(name, asmType); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/signature/JvmSignatureWriter.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/signature/JvmSignatureWriter.java index 913fdfb5b7b..f67b797e25b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/signature/JvmSignatureWriter.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/signature/JvmSignatureWriter.java @@ -18,11 +18,10 @@ package org.jetbrains.kotlin.codegen.signature; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.name.Name; +import org.jetbrains.kotlin.load.kotlin.JvmDescriptorTypeWriter; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature; -import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature; import org.jetbrains.kotlin.types.Variance; import org.jetbrains.org.objectweb.asm.Type; import org.jetbrains.org.objectweb.asm.commons.Method; @@ -30,22 +29,27 @@ import org.jetbrains.org.objectweb.asm.commons.Method; import java.util.ArrayList; import java.util.List; -public class JvmSignatureWriter { +public class JvmSignatureWriter extends JvmDescriptorTypeWriter { private final List kotlinParameterTypes = new ArrayList(); - private int jvmCurrentTypeArrayLevel; - private Type jvmCurrentType; private Type jvmReturnType; private JvmMethodParameterKind currentParameterKind; private int currentSignatureSize = 0; - /** - * Shortcut - */ - public void writeAsmType(Type asmType) { + public JvmSignatureWriter() { + super(AsmTypeFactory.INSTANCE); + } + + @Override + public void writeClass(@NotNull Type objectType) { + writeClassBegin(objectType); + writeClassEnd(); + } + + public void writeAsmType(@NotNull Type asmType) { switch (asmType.getSort()) { case Type.OBJECT: writeClassBegin(asmType); @@ -57,30 +61,16 @@ public class JvmSignatureWriter { writeArrayEnd(); return; default: - writeAsmType0(asmType); - } - } - - private String makeArrayPrefix() { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < jvmCurrentTypeArrayLevel; ++i) { - sb.append('['); - } - return sb.toString(); - } - - protected void writeAsmType0(Type type) { - if (jvmCurrentType == null) { - jvmCurrentType = Type.getType(makeArrayPrefix() + type.getDescriptor()); + writeJvmTypeAsIs(asmType); } } public void writeClassBegin(Type asmType) { - writeAsmType0(asmType); + writeJvmTypeAsIs(asmType); } public void writeOuterClassBegin(Type resultingAsmType, String outerInternalName) { - writeAsmType0(resultingAsmType); + writeJvmTypeAsIs(resultingAsmType); } public void writeInnerClass(String name) { @@ -89,15 +79,6 @@ public class JvmSignatureWriter { public void writeClassEnd() { } - public void writeArrayType() { - if (jvmCurrentType == null) { - ++jvmCurrentTypeArrayLevel; - } - } - - public void writeArrayEnd() { - } - public void writeTypeArgument(@NotNull Variance projectionKind) { } @@ -107,10 +88,6 @@ public class JvmSignatureWriter { public void writeTypeArgumentEnd() { } - public void writeTypeVariable(Name name, Type asmType) { - writeAsmType0(asmType); - } - public void writeFormalTypeParameter(String name) { } @@ -128,8 +105,7 @@ public class JvmSignatureWriter { public void writeParametersStart() { // hacks - jvmCurrentType = null; - jvmCurrentTypeArrayLevel = 0; + clearCurrentType(); } public void writeParameterType(JvmMethodParameterKind parameterKind) { @@ -137,21 +113,20 @@ public class JvmSignatureWriter { } public void writeParameterTypeEnd() { - kotlinParameterTypes.add(new JvmMethodParameterSignature(jvmCurrentType, currentParameterKind)); - currentSignatureSize += jvmCurrentType.getSize(); + //noinspection ConstantConditions + kotlinParameterTypes.add(new JvmMethodParameterSignature(getJvmCurrentType(), currentParameterKind)); + currentSignatureSize += getJvmCurrentType().getSize(); currentParameterKind = null; - jvmCurrentType = null; - jvmCurrentTypeArrayLevel = 0; + clearCurrentType(); } public void writeReturnType() { } public void writeReturnTypeEnd() { - jvmReturnType = jvmCurrentType; - jvmCurrentType = null; - jvmCurrentTypeArrayLevel = 0; + jvmReturnType = getJvmCurrentType(); + clearCurrentType(); } public void writeSuperclass() { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java index b8ace4a6b45..a47cfff7fd7 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/KotlinTypeMapper.java @@ -19,17 +19,19 @@ package org.jetbrains.kotlin.codegen.state; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiElement; import kotlin.Pair; +import kotlin.Unit; import kotlin.collections.CollectionsKt; +import kotlin.jvm.functions.Function2; +import kotlin.jvm.functions.Function3; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.BuiltInsPackageFragment; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; -import org.jetbrains.kotlin.builtins.PrimitiveType; import org.jetbrains.kotlin.builtins.functions.FunctionClassDescriptor; import org.jetbrains.kotlin.codegen.*; import org.jetbrains.kotlin.codegen.binding.CodegenBinding; import org.jetbrains.kotlin.codegen.binding.MutableClosure; -import org.jetbrains.kotlin.codegen.binding.PsiCodegenPredictor; +import org.jetbrains.kotlin.codegen.signature.AsmTypeFactory; import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter; import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter; import org.jetbrains.kotlin.descriptors.*; @@ -45,13 +47,13 @@ import org.jetbrains.kotlin.load.java.SpecialBuiltinMembers; import org.jetbrains.kotlin.load.java.descriptors.JavaCallableMemberDescriptor; import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor; import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment; -import org.jetbrains.kotlin.load.java.typeEnhancement.TypeEnhancementKt; -import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryClass; -import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryPackageSourceElement; -import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement; +import org.jetbrains.kotlin.load.kotlin.*; import org.jetbrains.kotlin.load.kotlin.incremental.IncrementalPackageFragmentProvider; import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache; -import org.jetbrains.kotlin.name.*; +import org.jetbrains.kotlin.name.ClassId; +import org.jetbrains.kotlin.name.FqName; +import org.jetbrains.kotlin.name.Name; +import org.jetbrains.kotlin.name.SpecialNames; import org.jetbrains.kotlin.platform.JavaToKotlinClassMap; import org.jetbrains.kotlin.psi.KtExpression; import org.jetbrains.kotlin.psi.KtFile; @@ -63,9 +65,7 @@ import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument; import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt; -import org.jetbrains.kotlin.resolve.jvm.AsmTypes; import org.jetbrains.kotlin.resolve.jvm.JvmClassName; -import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind; import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterSignature; @@ -74,12 +74,11 @@ import org.jetbrains.kotlin.serialization.deserialization.DeserializedType; import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor; import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor; import org.jetbrains.kotlin.types.*; -import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt; import org.jetbrains.kotlin.util.OperatorNameConventions; import org.jetbrains.org.objectweb.asm.Type; import org.jetbrains.org.objectweb.asm.commons.Method; -import java.util.ArrayList; +import java.util.Collection; import java.util.List; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isUnit; @@ -101,6 +100,26 @@ public class KotlinTypeMapper { private final IncrementalCache incrementalCache; private final IncompatibleClassTracker incompatibleClassTracker; private final String moduleName; + private final TypeMappingConfiguration typeMappingConfiguration = new TypeMappingConfiguration() { + @NotNull + @Override + public KotlinType commonSupertype(@NotNull Collection types) { + return CommonSupertypes.commonSupertype(types); + } + + @Nullable + @Override + public Type getPredefinedTypeForClass(@NotNull ClassDescriptor classDescriptor) { + return bindingContext.get(ASM_TYPE, classDescriptor); + } + + @Override + public void processErrorType(@NotNull KotlinType kotlinType, @NotNull ClassDescriptor descriptor) { + if (classBuilderMode != ClassBuilderMode.LIGHT_CLASSES) { + throw new IllegalStateException(generateErrorMessageForErrorType(kotlinType, descriptor)); + } + } + }; public KotlinTypeMapper( @NotNull BindingContext bindingContext, @@ -364,11 +383,6 @@ public class KotlinTypeMapper { return mapType(returnType, sw, mappingMode); } - @NotNull - private Type mapType(@NotNull KotlinType jetType, @NotNull TypeMappingMode mode) { - return mapType(jetType, null, mode); - } - @NotNull public Type mapSupertype(@NotNull KotlinType jetType, @Nullable JvmSignatureWriter signatureVisitor) { return mapType(jetType, signatureVisitor, TypeMappingMode.SUPER_TYPE); @@ -411,161 +425,19 @@ public class KotlinTypeMapper { @NotNull private Type mapType( - @NotNull KotlinType jetType, - @Nullable JvmSignatureWriter signatureVisitor, + @NotNull KotlinType kotlinType, + @Nullable final JvmSignatureWriter signatureVisitor, @NotNull TypeMappingMode mode ) { - Type builtinType = mapBuiltinType(jetType); - - if (builtinType != null) { - Type asmType = mode.getNeedPrimitiveBoxing() ? boxType(builtinType) : builtinType; - writeGenericType(jetType, asmType, signatureVisitor, mode); - return asmType; - } - - TypeConstructor constructor = jetType.getConstructor(); - if (constructor instanceof IntersectionTypeConstructor) { - jetType = CommonSupertypes.commonSupertype(new ArrayList(constructor.getSupertypes())); - - // interface In - // open class A : In - // open class B : In - // commonSupertype(A, B) = In - // So replace arguments with star-projections to prevent infinite recursive mapping - // It's not very important because such types anyway are prohibited in declarations - jetType = TypeUtilsKt.replaceArgumentsWithStarProjections(jetType); - - constructor = jetType.getConstructor(); - } - DeclarationDescriptor descriptor = constructor.getDeclarationDescriptor(); - - if (descriptor == null) { - throw new UnsupportedOperationException("no descriptor for type constructor of " + jetType); - } - - if (ErrorUtils.isError(descriptor)) { - if (classBuilderMode != ClassBuilderMode.LIGHT_CLASSES) { - throw new IllegalStateException(generateErrorMessageForErrorType(jetType, descriptor)); - } - Type asmType = Type.getObjectType("error/NonExistentClass"); - if (signatureVisitor != null) { - signatureVisitor.writeAsmType(asmType); - } - return asmType; - } - - if (descriptor instanceof ClassDescriptor && KotlinBuiltIns.isArray(jetType)) { - if (jetType.getArguments().size() != 1) { - throw new UnsupportedOperationException("arrays must have one type argument"); - } - TypeProjection memberProjection = jetType.getArguments().get(0); - KotlinType memberType = memberProjection.getType(); - - Type arrayElementType; - if (memberProjection.getProjectionKind() == Variance.IN_VARIANCE) { - arrayElementType = AsmTypes.OBJECT_TYPE; - if (signatureVisitor != null) { - signatureVisitor.writeArrayType(); - signatureVisitor.writeAsmType(arrayElementType); - signatureVisitor.writeArrayEnd(); - } - } - else { - arrayElementType = boxType(mapType(memberType, mode)); - if (signatureVisitor != null) { - signatureVisitor.writeArrayType(); - mapType(memberType, signatureVisitor, mode.toGenericArgumentMode(memberProjection.getProjectionKind())); - signatureVisitor.writeArrayEnd(); - } - } - - return Type.getType("[" + arrayElementType.getDescriptor()); - } - - if (descriptor instanceof ClassDescriptor) { - Type asmType = mode.isForAnnotationParameter() && KotlinBuiltIns.isKClass((ClassDescriptor) descriptor) ? - AsmTypes.JAVA_CLASS_TYPE : - computeAsmType((ClassDescriptor) descriptor.getOriginal()); - writeGenericType(jetType, asmType, signatureVisitor, mode); - return asmType; - } - - if (descriptor instanceof TypeParameterDescriptor) { - Type type = mapType(getRepresentativeUpperBound((TypeParameterDescriptor) descriptor), mode); - if (signatureVisitor != null) { - signatureVisitor.writeTypeVariable(descriptor.getName(), type); - } - return type; - } - - throw new UnsupportedOperationException("Unknown type " + jetType); - } - - @NotNull - private static KotlinType getRepresentativeUpperBound(@NotNull TypeParameterDescriptor descriptor) { - List upperBounds = descriptor.getUpperBounds(); - assert !upperBounds.isEmpty() : "Upper bounds should not be empty: " + descriptor; - for (KotlinType upperBound : upperBounds) { - if (!isJvmInterface(upperBound)) { - return upperBound; - } - } - return CollectionsKt.first(upperBounds); - } - - @Nullable - private static Type mapBuiltinType(@NotNull KotlinType type) { - DeclarationDescriptor descriptor = type.getConstructor().getDeclarationDescriptor(); - if (!(descriptor instanceof ClassDescriptor)) return null; - - FqNameUnsafe fqName = DescriptorUtils.getFqName(descriptor); - - PrimitiveType primitiveType = KotlinBuiltIns.getPrimitiveTypeByFqName(fqName); - if (primitiveType != null) { - Type asmType = Type.getType(JvmPrimitiveType.get(primitiveType).getDesc()); - boolean isNullableInJava = TypeUtils.isNullableType(type) || TypeEnhancementKt.hasEnhancedNullability(type); - return isNullableInJava ? boxType(asmType) : asmType; - } - - PrimitiveType arrayElementType = KotlinBuiltIns.getPrimitiveTypeByArrayClassFqName(fqName); - if (arrayElementType != null) { - return Type.getType("[" + JvmPrimitiveType.get(arrayElementType).getDesc()); - } - - ClassId classId = JavaToKotlinClassMap.INSTANCE.mapKotlinToJava(fqName); - if (classId != null) { - return Type.getObjectType(JvmClassName.byClassId(classId).getInternalName()); - } - - return null; - } - - @NotNull - private Type computeAsmType(@NotNull ClassDescriptor klass) { - Type alreadyComputedType = bindingContext.get(ASM_TYPE, klass); - if (alreadyComputedType != null) { - return alreadyComputedType; - } - - Type asmType = Type.getObjectType(computeAsmTypeImpl(klass)); - assert PsiCodegenPredictor.checkPredictedNameFromPsi(klass, asmType, fileClassesProvider); - return asmType; - } - - @NotNull - private String computeAsmTypeImpl(@NotNull ClassDescriptor klass) { - DeclarationDescriptor container = klass.getContainingDeclaration(); - - String name = SpecialNames.safeIdentifier(klass.getName()).getIdentifier(); - if (container instanceof PackageFragmentDescriptor) { - FqName fqName = ((PackageFragmentDescriptor) container).getFqName(); - return fqName.isRoot() ? name : fqName.asString().replace('.', '/') + '/' + name; - } - - assert container instanceof ClassDescriptor : "Unexpected container: " + container + " for " + klass; - - String containerInternalName = computeAsmTypeImpl((ClassDescriptor) container); - return klass.getKind() == ClassKind.ENUM_ENTRY ? containerInternalName : containerInternalName + "$" + name; + return TypeSignatureMappingKt.mapType( + kotlinType, AsmTypeFactory.INSTANCE, mode, typeMappingConfiguration, signatureVisitor, + new Function3() { + @Override + public Unit invoke(KotlinType kotlinType, Type type, TypeMappingMode mode) { + writeGenericType(kotlinType, type, signatureVisitor, mode); + return Unit.INSTANCE; + } + }); } @NotNull diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/typeMappingUtil.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/typeMappingUtil.kt index df42bebfbf4..7df727e5523 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/typeMappingUtil.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/typeMappingUtil.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES as BUILTIN_NAMES import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor +import org.jetbrains.kotlin.load.kotlin.TypeMappingMode import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.descriptorUtil.firstOverridden diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/TypeMappingMode.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/TypeMappingMode.kt similarity index 95% rename from compiler/backend/src/org/jetbrains/kotlin/codegen/state/TypeMappingMode.kt rename to core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/TypeMappingMode.kt index e2941939f02..9a068f83f31 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/TypeMappingMode.kt +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/TypeMappingMode.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2016 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. @@ -14,12 +14,12 @@ * limitations under the License. */ -package org.jetbrains.kotlin.codegen.state +package org.jetbrains.kotlin.load.kotlin import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.Variance -internal class TypeMappingMode private constructor( +class TypeMappingMode private constructor( val needPrimitiveBoxing: Boolean = true, val isForAnnotationParameter: Boolean = false, // Here DeclarationSiteWildcards means wildcard generated because of declaration-site variance @@ -120,7 +120,7 @@ internal class TypeMappingMode private constructor( fun toGenericArgumentMode(effectiveVariance: Variance): TypeMappingMode = when (effectiveVariance) { Variance.IN_VARIANCE -> genericContravariantArgumentMode ?: this - Variance.INVARIANT -> genericInvariantArgumentMode ?: this + Variance.INVARIANT -> genericInvariantArgumentMode ?: this else -> genericArgumentMode ?: this } } diff --git a/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/typeSignatureMapping.kt b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/typeSignatureMapping.kt new file mode 100644 index 00000000000..47f47a64c1a --- /dev/null +++ b/core/descriptor.loader.java/src/org/jetbrains/kotlin/load/kotlin/typeSignatureMapping.kt @@ -0,0 +1,234 @@ +/* + * Copyright 2010-2016 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.load.kotlin + +import org.jetbrains.kotlin.builtins.KotlinBuiltIns +import org.jetbrains.kotlin.descriptors.ClassDescriptor +import org.jetbrains.kotlin.descriptors.ClassKind +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor +import org.jetbrains.kotlin.load.java.typeEnhancement.hasEnhancedNullability +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.name.SpecialNames +import org.jetbrains.kotlin.platform.JavaToKotlinClassMap +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe +import org.jetbrains.kotlin.resolve.jvm.JvmClassName +import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType +import org.jetbrains.kotlin.types.* +import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections +import org.jetbrains.kotlin.utils.DO_NOTHING_3 + +interface JvmTypeFactory { + fun boxType(possiblyPrimitiveType: T): T + fun createFromString(representation: String): T + fun createObjectType(internalName: String): T + fun toString(type: T): String + + val javaLangClassType: T +} + +private fun JvmTypeFactory.boxTypeIfNeeded(possiblyPrimitiveType: T, needBoxedType: Boolean) = + if (needBoxedType) boxType(possiblyPrimitiveType) else possiblyPrimitiveType + +interface TypeMappingConfiguration { + fun commonSupertype(types: Collection<@JvmSuppressWildcards KotlinType>): KotlinType + fun getPredefinedTypeForClass(classDescriptor: ClassDescriptor): T? + fun processErrorType(kotlinType: KotlinType, descriptor: ClassDescriptor) +} + +fun mapType( + kotlinType: KotlinType, + factory: JvmTypeFactory, + mode: TypeMappingMode, + typeMappingConfiguration: TypeMappingConfiguration, + descriptorTypeWriter: JvmDescriptorTypeWriter?, + writeGenericType: (KotlinType, T, TypeMappingMode) -> Unit = DO_NOTHING_3 +): T { + mapBuiltInType(kotlinType, factory)?.let { builtInType -> + val jvmType = factory.boxTypeIfNeeded(builtInType, mode.needPrimitiveBoxing) + writeGenericType(kotlinType, jvmType, mode) + return jvmType + } + + val constructor = kotlinType.constructor + if (constructor is IntersectionTypeConstructor) { + val commonSupertype = typeMappingConfiguration.commonSupertype(constructor.supertypes) + // interface In + // open class A : In + // open class B : In + // commonSupertype(A, B) = In + // So replace arguments with star-projections to prevent infinite recursive mapping + // It's not very important because such types anyway are prohibited in declarations + return mapType( + commonSupertype.replaceArgumentsWithStarProjections(), + factory, mode, typeMappingConfiguration, descriptorTypeWriter, writeGenericType) + } + + val descriptor = + constructor.declarationDescriptor + ?: throw UnsupportedOperationException("no descriptor for type constructor of " + kotlinType) + + when { + ErrorUtils.isError(descriptor) -> { + val jvmType = factory.createObjectType("error/NonExistentClass") + typeMappingConfiguration.processErrorType(kotlinType, descriptor as ClassDescriptor) + descriptorTypeWriter?.writeClass(jvmType) + return jvmType + } + + descriptor is ClassDescriptor && KotlinBuiltIns.isArray(kotlinType) -> { + if (kotlinType.arguments.size != 1) { + throw UnsupportedOperationException("arrays must have one type argument") + } + val memberProjection = kotlinType.arguments[0] + val memberType = memberProjection.type + + val arrayElementType: T + if (memberProjection.projectionKind === Variance.IN_VARIANCE) { + arrayElementType = factory.createObjectType("java/lang/Object") + descriptorTypeWriter?.apply { + writeArrayType() + writeClass(arrayElementType) + writeArrayEnd() + } + } + else { + descriptorTypeWriter?.writeArrayType() + + arrayElementType = + mapType( + memberType, factory, + mode.toGenericArgumentMode(memberProjection.projectionKind), + typeMappingConfiguration, descriptorTypeWriter, writeGenericType) + + descriptorTypeWriter?.writeArrayEnd() + } + + return factory.createFromString("[" + factory.toString(arrayElementType)) + } + + descriptor is ClassDescriptor -> { + val jvmType = + if (mode.isForAnnotationParameter && KotlinBuiltIns.isKClass(descriptor)) + factory.javaLangClassType + else + typeMappingConfiguration.getPredefinedTypeForClass(descriptor.original) + ?: factory.createObjectType(computeInternalName(descriptor.original)) + + writeGenericType(kotlinType, jvmType, mode) + + return jvmType + } + + descriptor is TypeParameterDescriptor -> { + val type = mapType(getRepresentativeUpperBound(descriptor), + factory, mode, typeMappingConfiguration, writeGenericType = DO_NOTHING_3, descriptorTypeWriter = null) + descriptorTypeWriter?.writeTypeVariable(descriptor.getName(), type); + return type + } + + else -> throw UnsupportedOperationException("Unknown type " + kotlinType) + } +} + +private fun mapBuiltInType( + type: KotlinType, + typeFactory: JvmTypeFactory +): T? { + val descriptor = type.constructor.declarationDescriptor as? ClassDescriptor ?: return null + + val fqName = descriptor.fqNameUnsafe + + val primitiveType = KotlinBuiltIns.getPrimitiveTypeByFqName(fqName) + if (primitiveType != null) { + val jvmType = typeFactory.createFromString(JvmPrimitiveType.get(primitiveType).desc) + val isNullableInJava = TypeUtils.isNullableType(type) || type.hasEnhancedNullability() + return typeFactory.boxTypeIfNeeded(jvmType, isNullableInJava) + } + + val arrayElementType = KotlinBuiltIns.getPrimitiveTypeByArrayClassFqName(fqName) + if (arrayElementType != null) { + return typeFactory.createFromString("[" + JvmPrimitiveType.get(arrayElementType).desc) + } + + val classId = JavaToKotlinClassMap.INSTANCE.mapKotlinToJava(fqName) + if (classId != null) { + return typeFactory.createObjectType(JvmClassName.byClassId(classId).internalName) + } + + return null +} + +private fun computeInternalName(klass: ClassDescriptor): String { + val container = klass.containingDeclaration + + val name = SpecialNames.safeIdentifier(klass.name).identifier + if (container is PackageFragmentDescriptor) { + val fqName = container.fqName + return if (fqName.isRoot) name else fqName.asString().replace('.', '/') + '/' + name + } + + assert(container is ClassDescriptor) { "Unexpected container: $container for $klass" } + + val containerInternalName = computeInternalName(container as ClassDescriptor) + return if (klass.kind == ClassKind.ENUM_ENTRY) containerInternalName else containerInternalName + "$" + name +} + +private fun getRepresentativeUpperBound(descriptor: TypeParameterDescriptor): KotlinType { + val upperBounds = descriptor.upperBounds + assert(!upperBounds.isEmpty()) { "Upper bounds should not be empty: " + descriptor } + + return upperBounds.firstOrNull { + val classDescriptor = it.constructor.declarationDescriptor as? ClassDescriptor ?: return@firstOrNull false + classDescriptor.kind != ClassKind.INTERFACE && classDescriptor.kind != ClassKind.ANNOTATION_CLASS + } ?: upperBounds.first() +} + +open class JvmDescriptorTypeWriter(private val jvmTypeFactory: JvmTypeFactory) { + private var jvmCurrentTypeArrayLevel: Int = 0 + protected var jvmCurrentType: T? = null + private set + + protected fun clearCurrentType() { + jvmCurrentType = null + jvmCurrentTypeArrayLevel = 0 + } + + open fun writeArrayType() { + if (jvmCurrentType == null) { + ++jvmCurrentTypeArrayLevel + } + } + + open fun writeArrayEnd() { + } + + + open public fun writeClass(objectType: T) { + writeJvmTypeAsIs(objectType) + } + + protected fun writeJvmTypeAsIs(type: T) { + if (jvmCurrentType == null) { + jvmCurrentType = jvmTypeFactory.createFromString("[".repeat(jvmCurrentTypeArrayLevel) + jvmTypeFactory.toString(type)) + } + } + + open fun writeTypeVariable(name: Name, type: T) { + writeJvmTypeAsIs(type) + } +} diff --git a/core/util.runtime/src/org/jetbrains/kotlin/utils/functions.kt b/core/util.runtime/src/org/jetbrains/kotlin/utils/functions.kt index 2813e2272d8..cf3b793995f 100644 --- a/core/util.runtime/src/org/jetbrains/kotlin/utils/functions.kt +++ b/core/util.runtime/src/org/jetbrains/kotlin/utils/functions.kt @@ -31,5 +31,6 @@ private val ALWAYS_NULL: (Any?) -> Any? = { null } fun alwaysNull(): (T) -> R? = ALWAYS_NULL as (T) -> R? val DO_NOTHING: (Any?) -> Unit = { } +val DO_NOTHING_3: (Any?, Any?, Any?) -> Unit = { x, y, z -> } fun doNothing(): (T) -> Unit = DO_NOTHING