001/* 002 * Copyright 2010-2013 JetBrains s.r.o. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.jetbrains.jet.lang.resolve.java.kotlinSignature; 018 019import org.jetbrains.annotations.NotNull; 020import org.jetbrains.annotations.Nullable; 021import org.jetbrains.annotations.TestOnly; 022import org.jetbrains.asm4.Type; 023import org.jetbrains.jet.lang.descriptors.ClassDescriptor; 024import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor; 025import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor; 026import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl; 027import org.jetbrains.jet.lang.psi.*; 028import org.jetbrains.jet.lang.resolve.DescriptorUtils; 029import org.jetbrains.jet.lang.resolve.TypeResolver; 030import org.jetbrains.jet.lang.resolve.java.JavaToKotlinClassMap; 031import org.jetbrains.jet.lang.resolve.java.JvmClassName; 032import org.jetbrains.jet.lang.resolve.java.KotlinToJavaTypesMap; 033import org.jetbrains.jet.lang.resolve.java.TypeUsage; 034import org.jetbrains.jet.lang.resolve.scopes.JetScope; 035import org.jetbrains.jet.lang.types.*; 036import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 037import org.jetbrains.jet.renderer.DescriptorRenderer; 038 039import java.util.*; 040 041import static org.jetbrains.jet.lang.resolve.java.TypeUsage.TYPE_ARGUMENT; 042import static org.jetbrains.jet.lang.types.Variance.INVARIANT; 043 044public class TypeTransformingVisitor extends JetVisitor<JetType, Void> { 045 private static boolean strictMode = false; 046 047 private final JetType originalType; 048 private final Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameters; 049 050 private final TypeUsage typeUsage; 051 052 private TypeTransformingVisitor( 053 JetType originalType, 054 Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameters, 055 TypeUsage typeUsage 056 ) { 057 this.originalType = originalType; 058 this.typeUsage = typeUsage; 059 this.originalToAltTypeParameters = Collections.unmodifiableMap(originalToAltTypeParameters); 060 } 061 062 @NotNull 063 public static JetType computeType( 064 @NotNull JetTypeElement alternativeTypeElement, 065 @NotNull JetType originalType, 066 @NotNull Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> originalToAltTypeParameters, 067 @NotNull TypeUsage typeUsage 068 ) { 069 JetType computedType = alternativeTypeElement.accept(new TypeTransformingVisitor(originalType, originalToAltTypeParameters, typeUsage), null); 070 assert (computedType != null); 071 return computedType; 072 } 073 074 @Override 075 public JetType visitNullableType(JetNullableType nullableType, Void aVoid) { 076 if (!originalType.isNullable() && typeUsage != TYPE_ARGUMENT) { 077 throw new AlternativeSignatureMismatchException("Auto type '%s' is not-null, while type in alternative signature is nullable: '%s'", 078 DescriptorRenderer.TEXT.renderType(originalType), nullableType.getText()); 079 } 080 return TypeUtils.makeNullable(computeType(nullableType.getInnerType(), originalType, originalToAltTypeParameters, typeUsage)); 081 } 082 083 @Override 084 public JetType visitFunctionType(JetFunctionType type, Void data) { 085 return visitCommonType(type.getReceiverTypeRef() == null 086 ? KotlinBuiltIns.getInstance().getFunction(type.getParameters().size()) 087 : KotlinBuiltIns.getInstance().getExtensionFunction(type.getParameters().size()), type); 088 } 089 090 @Override 091 public JetType visitUserType(JetUserType type, Void data) { 092 JetUserType qualifier = type.getQualifier(); 093 094 //noinspection ConstantConditions 095 String shortName = type.getReferenceExpression().getReferencedName(); 096 String longName = (qualifier == null ? "" : qualifier.getText() + ".") + shortName; 097 098 return visitCommonType(longName, type); 099 } 100 101 private JetType visitCommonType(@NotNull ClassDescriptor classDescriptor, @NotNull JetTypeElement type) { 102 return visitCommonType(DescriptorUtils.getFQName(classDescriptor).toSafe().asString(), type); 103 } 104 105 private JetType visitCommonType(@NotNull String qualifiedName, @NotNull JetTypeElement type) { 106 TypeConstructor originalTypeConstructor = originalType.getConstructor(); 107 ClassifierDescriptor declarationDescriptor = originalTypeConstructor.getDeclarationDescriptor(); 108 assert declarationDescriptor != null; 109 String fqName = DescriptorUtils.getFQName(declarationDescriptor).toSafe().asString(); 110 ClassDescriptor classFromLibrary = getAutoTypeAnalogWithinBuiltins(qualifiedName); 111 if (!isSameName(qualifiedName, fqName) && classFromLibrary == null) { 112 throw new AlternativeSignatureMismatchException("Alternative signature type mismatch, expected: %s, actual: %s", qualifiedName, fqName); 113 } 114 115 TypeConstructor typeConstructor; 116 if (classFromLibrary != null) { 117 typeConstructor = classFromLibrary.getTypeConstructor(); 118 } 119 else { 120 typeConstructor = originalTypeConstructor; 121 } 122 ClassifierDescriptor typeConstructorClassifier = typeConstructor.getDeclarationDescriptor(); 123 if (typeConstructorClassifier instanceof TypeParameterDescriptor && originalToAltTypeParameters.containsKey(typeConstructorClassifier)) { 124 typeConstructor = originalToAltTypeParameters.get(typeConstructorClassifier).getTypeConstructor(); 125 } 126 127 List<TypeProjection> arguments = originalType.getArguments(); 128 129 if (arguments.size() != type.getTypeArgumentsAsTypes().size()) { 130 throw new AlternativeSignatureMismatchException("'%s' type in method signature has %d type arguments, while '%s' in alternative signature has %d of them", 131 DescriptorRenderer.TEXT.renderType(originalType), arguments.size(), type.getText(), 132 type.getTypeArgumentsAsTypes().size()); 133 } 134 135 List<TypeProjection> altArguments = new ArrayList<TypeProjection>(); 136 for (int i = 0, size = arguments.size(); i < size; i++) { 137 altArguments.add(getAltArgument(type, typeConstructor, i, arguments.get(i))); 138 } 139 140 JetScope memberScope; 141 if (typeConstructorClassifier instanceof TypeParameterDescriptor) { 142 memberScope = ((TypeParameterDescriptor) typeConstructorClassifier).getUpperBoundsAsType().getMemberScope(); 143 } 144 else if (typeConstructorClassifier instanceof ClassDescriptor) { 145 memberScope = ((ClassDescriptor) typeConstructorClassifier).getMemberScope(altArguments); 146 } 147 else { 148 throw new AssertionError("Unexpected class of type constructor classifier " 149 + (typeConstructorClassifier == null ? "null" : typeConstructorClassifier.getClass().getName())); 150 } 151 return new JetTypeImpl(originalType.getAnnotations(), typeConstructor, false, 152 altArguments, memberScope); 153 } 154 155 @NotNull 156 private TypeProjection getAltArgument( 157 @NotNull JetTypeElement type, 158 @NotNull TypeConstructor typeConstructor, 159 int i, 160 @NotNull TypeProjection originalArgument 161 ) { 162 JetTypeReference typeReference = type.getTypeArgumentsAsTypes().get(i); // process both function type and user type 163 164 if (typeReference == null) { 165 // star projection 166 assert type instanceof JetUserType 167 && ((JetUserType) type).getTypeArguments().get(i).getProjectionKind() == JetProjectionKind.STAR; 168 169 return originalArgument; 170 } 171 172 JetTypeElement argumentAlternativeTypeElement = typeReference.getTypeElement(); 173 assert argumentAlternativeTypeElement != null; 174 175 TypeParameterDescriptor parameter = typeConstructor.getParameters().get(i); 176 JetType alternativeArgumentType = computeType(argumentAlternativeTypeElement, originalArgument.getType(), originalToAltTypeParameters, TYPE_ARGUMENT); 177 Variance projectionKind = originalArgument.getProjectionKind(); 178 Variance altProjectionKind; 179 if (type instanceof JetUserType) { 180 JetTypeProjection typeProjection = ((JetUserType) type).getTypeArguments().get(i); 181 altProjectionKind = TypeResolver.resolveProjectionKind(typeProjection.getProjectionKind()); 182 if (altProjectionKind != projectionKind && projectionKind != Variance.INVARIANT) { 183 throw new AlternativeSignatureMismatchException("Projection kind mismatch, actual: %s, in alternative signature: %s", 184 projectionKind, altProjectionKind); 185 } 186 if (altProjectionKind != INVARIANT && parameter.getVariance() != INVARIANT) { 187 if (altProjectionKind == parameter.getVariance()) { 188 if (strictMode) { 189 throw new AlternativeSignatureMismatchException("Projection kind '%s' is redundant", 190 altProjectionKind, DescriptorUtils.getFQName(typeConstructor.getDeclarationDescriptor())); 191 } 192 else { 193 altProjectionKind = projectionKind; 194 } 195 } 196 else { 197 throw new AlternativeSignatureMismatchException("Projection kind '%s' is conflicting with variance of %s", 198 altProjectionKind, DescriptorUtils.getFQName(typeConstructor.getDeclarationDescriptor())); 199 } 200 } 201 } 202 else { 203 altProjectionKind = projectionKind; 204 } 205 return new TypeProjection(altProjectionKind, alternativeArgumentType); 206 } 207 208 @Nullable 209 private ClassDescriptor getAutoTypeAnalogWithinBuiltins(String qualifiedName) { 210 Type javaAnalog = KotlinToJavaTypesMap.getInstance().getJavaAnalog(originalType); 211 if (javaAnalog == null || javaAnalog.getSort() != Type.OBJECT) return null; 212 Collection<ClassDescriptor> descriptors = 213 JavaToKotlinClassMap.getInstance().mapPlatformClass(JvmClassName.byType(javaAnalog).getFqName()); 214 for (ClassDescriptor descriptor : descriptors) { 215 String fqName = DescriptorUtils.getFQName(descriptor).asString(); 216 if (isSameName(qualifiedName, fqName)) { 217 return descriptor; 218 } 219 } 220 return null; 221 } 222 223 @Override 224 public JetType visitSelfType(JetSelfType type, Void data) { 225 throw new UnsupportedOperationException("Self-types are not supported yet"); 226 } 227 228 private static boolean isSameName(String qualifiedName, String fullyQualifiedName) { 229 return fullyQualifiedName.equals(qualifiedName) || fullyQualifiedName.endsWith("." + qualifiedName); 230 } 231 232 @TestOnly 233 public static void setStrictMode(boolean strictMode) { 234 TypeTransformingVisitor.strictMode = strictMode; 235 } 236}