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
017 package org.jetbrains.jet.lang.resolve.java.kotlinSignature;
018
019 import org.jetbrains.annotations.NotNull;
020 import org.jetbrains.annotations.Nullable;
021 import org.jetbrains.annotations.TestOnly;
022 import org.jetbrains.asm4.Type;
023 import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
024 import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
025 import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
026 import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl;
027 import org.jetbrains.jet.lang.psi.*;
028 import org.jetbrains.jet.lang.resolve.DescriptorUtils;
029 import org.jetbrains.jet.lang.resolve.TypeResolver;
030 import org.jetbrains.jet.lang.resolve.java.JavaToKotlinClassMap;
031 import org.jetbrains.jet.lang.resolve.java.JvmClassName;
032 import org.jetbrains.jet.lang.resolve.java.KotlinToJavaTypesMap;
033 import org.jetbrains.jet.lang.resolve.java.TypeUsage;
034 import org.jetbrains.jet.lang.resolve.scopes.JetScope;
035 import org.jetbrains.jet.lang.types.*;
036 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
037 import org.jetbrains.jet.renderer.DescriptorRenderer;
038
039 import java.util.*;
040
041 import static org.jetbrains.jet.lang.resolve.java.TypeUsage.TYPE_ARGUMENT;
042 import static org.jetbrains.jet.lang.types.Variance.INVARIANT;
043
044 public 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 }