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.sam;
018
019 import org.jetbrains.annotations.NotNull;
020 import org.jetbrains.annotations.Nullable;
021 import org.jetbrains.jet.lang.descriptors.*;
022 import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor;
023 import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl;
024 import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl;
025 import org.jetbrains.jet.lang.resolve.java.descriptor.ClassDescriptorFromJvmBytecode;
026 import org.jetbrains.jet.lang.resolve.java.descriptor.SamAdapterDescriptor;
027 import org.jetbrains.jet.lang.resolve.java.descriptor.SamConstructorDescriptor;
028 import org.jetbrains.jet.lang.resolve.java.resolver.DescriptorResolverUtils;
029 import org.jetbrains.jet.lang.resolve.java.resolver.JavaSupertypeResolver;
030 import org.jetbrains.jet.lang.resolve.java.structure.*;
031 import org.jetbrains.jet.lang.resolve.name.FqName;
032 import org.jetbrains.jet.lang.resolve.name.Name;
033 import org.jetbrains.jet.lang.types.*;
034 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
035
036 import java.util.*;
037
038 import static org.jetbrains.jet.lang.types.Variance.INVARIANT;
039
040 public class SingleAbstractMethodUtils {
041 private SingleAbstractMethodUtils() {
042 }
043
044 @NotNull
045 public static List<CallableMemberDescriptor> getAbstractMembers(@NotNull JetType type) {
046 List<CallableMemberDescriptor> abstractMembers = new ArrayList<CallableMemberDescriptor>();
047 for (DeclarationDescriptor member : type.getMemberScope().getAllDescriptors()) {
048 if (member instanceof CallableMemberDescriptor && ((CallableMemberDescriptor) member).getModality() == Modality.ABSTRACT) {
049 abstractMembers.add((CallableMemberDescriptor) member);
050 }
051 }
052 return abstractMembers;
053 }
054
055 private static JetType fixProjections(@NotNull JetType functionType) {
056 //removes redundant projection kinds and detects conflicts
057
058 List<TypeParameterDescriptor> typeParameters = functionType.getConstructor().getParameters();
059 List<TypeProjection> arguments = new ArrayList<TypeProjection>(typeParameters.size());
060 for (TypeParameterDescriptor typeParameter : typeParameters) {
061 Variance variance = typeParameter.getVariance();
062 TypeProjection argument = functionType.getArguments().get(typeParameter.getIndex());
063 Variance kind = argument.getProjectionKind();
064 if (kind != INVARIANT && variance != INVARIANT) {
065 if (kind == variance) {
066 arguments.add(new TypeProjectionImpl(argument.getType()));
067 }
068 else {
069 return null;
070 }
071 }
072 else {
073 arguments.add(argument);
074 }
075 }
076 ClassifierDescriptor classifier = functionType.getConstructor().getDeclarationDescriptor();
077 assert classifier instanceof ClassDescriptor : "Not class: " + classifier;
078 return new JetTypeImpl(
079 functionType.getAnnotations(),
080 functionType.getConstructor(),
081 functionType.isNullable(),
082 arguments,
083 ((ClassDescriptor) classifier).getMemberScope(arguments)
084 );
085 }
086
087 @Nullable
088 private static JetType getFunctionTypeForSamType(@NotNull JetType samType) {
089 // e.g. samType == Comparator<String>?
090
091 ClassifierDescriptor classifier = samType.getConstructor().getDeclarationDescriptor();
092 if (classifier instanceof ClassDescriptorFromJvmBytecode) {
093 // Function2<T, T, Int>
094 JetType functionTypeDefault = ((ClassDescriptorFromJvmBytecode) classifier).getFunctionTypeForSamInterface();
095
096 if (functionTypeDefault != null) {
097 // Function2<String, String, Int>?
098 JetType substitute = TypeSubstitutor.create(samType).substitute(functionTypeDefault, Variance.INVARIANT);
099
100 return substitute == null ? null : fixProjections(TypeUtils.makeNullableAsSpecified(substitute, samType.isNullable()));
101 }
102 }
103 return null;
104 }
105
106 @NotNull
107 public static JetType getFunctionTypeForAbstractMethod(@NotNull FunctionDescriptor function) {
108 JetType returnType = function.getReturnType();
109 assert returnType != null : "function is not initialized: " + function;
110 List<ValueParameterDescriptor> valueParameters = function.getValueParameters();
111 List<JetType> parameterTypes = new ArrayList<JetType>(valueParameters.size());
112 for (ValueParameterDescriptor parameter : valueParameters) {
113 parameterTypes.add(parameter.getType());
114 }
115 return KotlinBuiltIns.getInstance().getFunctionType(
116 Collections.<AnnotationDescriptor>emptyList(), null, parameterTypes, returnType);
117 }
118
119 private static boolean isSamInterface(@NotNull ClassDescriptor klass) {
120 if (klass.getKind() != ClassKind.TRAIT) {
121 return false;
122 }
123
124 List<CallableMemberDescriptor> abstractMembers = getAbstractMembers(klass.getDefaultType());
125 if (abstractMembers.size() == 1) {
126 CallableMemberDescriptor member = abstractMembers.get(0);
127 if (member instanceof SimpleFunctionDescriptor) {
128 return member.getTypeParameters().isEmpty();
129 }
130 }
131 return false;
132 }
133
134 @NotNull
135 public static SamConstructorDescriptor createSamConstructorFunction(
136 @NotNull ClassOrNamespaceDescriptor owner,
137 @NotNull ClassDescriptorFromJvmBytecode samInterface
138 ) {
139 assert isSamInterface(samInterface) : samInterface;
140
141 SamConstructorDescriptor result = new SamConstructorDescriptor(owner, samInterface);
142
143 TypeParameters typeParameters = recreateAndInitializeTypeParameters(samInterface.getTypeConstructor().getParameters(), result);
144
145 JetType parameterTypeUnsubstituted = getFunctionTypeForSamType(samInterface.getDefaultType());
146 assert parameterTypeUnsubstituted != null : "couldn't get function type for SAM type " + samInterface.getDefaultType();
147 JetType parameterType = typeParameters.substitutor.substitute(parameterTypeUnsubstituted, Variance.IN_VARIANCE);
148 assert parameterType != null : "couldn't substitute type: " + parameterTypeUnsubstituted +
149 ", substitutor = " + typeParameters.substitutor;
150 ValueParameterDescriptor parameter = new ValueParameterDescriptorImpl(
151 result, 0, Collections.<AnnotationDescriptor>emptyList(), Name.identifier("function"), parameterType, false, null);
152
153 JetType returnType = typeParameters.substitutor.substitute(samInterface.getDefaultType(), Variance.OUT_VARIANCE);
154 assert returnType != null : "couldn't substitute type: " + samInterface.getDefaultType() +
155 ", substitutor = " + typeParameters.substitutor;
156
157 result.initialize(
158 null,
159 null,
160 typeParameters.descriptors,
161 Arrays.asList(parameter),
162 returnType,
163 Modality.FINAL,
164 samInterface.getVisibility(),
165 false
166 );
167
168 return result;
169 }
170
171 public static boolean isSamType(@NotNull JetType type) {
172 return getFunctionTypeForSamType(type) != null;
173 }
174
175 public static boolean isSamAdapterNecessary(@NotNull FunctionDescriptor fun) {
176 for (ValueParameterDescriptor param : fun.getValueParameters()) {
177 if (isSamType(param.getType())) {
178 return true;
179 }
180 }
181 return false;
182 }
183
184 @NotNull
185 public static SamAdapterDescriptor<SimpleFunctionDescriptor> createSamAdapterFunction(@NotNull final SimpleFunctionDescriptor original) {
186 final SamAdapterFunctionDescriptor result = new SamAdapterFunctionDescriptor(original);
187 return initSamAdapter(original, result, new FunctionInitializer() {
188 @Override
189 public void initialize(
190 @NotNull List<TypeParameterDescriptor> typeParameters,
191 @NotNull List<ValueParameterDescriptor> valueParameters,
192 @Nullable JetType returnType
193 ) {
194 result.initialize(
195 null,
196 original.getExpectedThisObject(),
197 typeParameters,
198 valueParameters,
199 returnType,
200 Modality.FINAL,
201 original.getVisibility(),
202 false
203 );
204 }
205 });
206 }
207
208 @NotNull
209 public static SamAdapterDescriptor<ConstructorDescriptor> createSamAdapterConstructor(@NotNull final ConstructorDescriptor original) {
210 final SamAdapterConstructorDescriptor result = new SamAdapterConstructorDescriptor(original);
211 return initSamAdapter(original, result, new FunctionInitializer() {
212 @Override
213 public void initialize(
214 @NotNull List<TypeParameterDescriptor> typeParameters,
215 @NotNull List<ValueParameterDescriptor> valueParameters,
216 @Nullable JetType returnType
217 ) {
218 result.initialize(
219 typeParameters,
220 valueParameters,
221 original.getVisibility(),
222 original.getExpectedThisObject() == ReceiverParameterDescriptor.NO_RECEIVER_PARAMETER
223 );
224 }
225 });
226 }
227
228 @NotNull
229 private static <F extends FunctionDescriptor> SamAdapterDescriptor<F> initSamAdapter(
230 @NotNull F original,
231 @NotNull SamAdapterDescriptor<F> adapter,
232 @NotNull FunctionInitializer initializer
233 ) {
234 TypeParameters typeParameters = recreateAndInitializeTypeParameters(original.getTypeParameters(), adapter);
235
236 JetType returnTypeUnsubstituted = original.getReturnType();
237 JetType returnType;
238 if (returnTypeUnsubstituted == null) { // return type may be null for not yet initialized constructors
239 returnType = null;
240 }
241 else {
242 returnType = typeParameters.substitutor.substitute(returnTypeUnsubstituted, Variance.OUT_VARIANCE);
243 assert returnType != null : "couldn't substitute type: " + returnTypeUnsubstituted +
244 ", substitutor = " + typeParameters.substitutor;
245 }
246
247 List<ValueParameterDescriptor> originalValueParameters = original.getValueParameters();
248 List<ValueParameterDescriptor> valueParameters = new ArrayList<ValueParameterDescriptor>(originalValueParameters.size());
249 for (ValueParameterDescriptor originalParam : originalValueParameters) {
250 JetType originalType = originalParam.getType();
251 JetType functionType = getFunctionTypeForSamType(originalType);
252 JetType newTypeUnsubstituted = functionType != null ? functionType : originalType;
253 JetType newType = typeParameters.substitutor.substitute(newTypeUnsubstituted, Variance.IN_VARIANCE);
254 assert newType != null : "couldn't substitute type: " + newTypeUnsubstituted + ", substitutor = " + typeParameters.substitutor;
255
256 ValueParameterDescriptor newParam = new ValueParameterDescriptorImpl(
257 adapter, originalParam.getIndex(), originalParam.getAnnotations(), originalParam.getName(), newType, false, null);
258 valueParameters.add(newParam);
259 }
260
261 initializer.initialize(typeParameters.descriptors, valueParameters, returnType);
262
263 return adapter;
264 }
265
266 @NotNull
267 private static TypeParameters recreateAndInitializeTypeParameters(
268 @NotNull List<TypeParameterDescriptor> originalParameters,
269 @Nullable DeclarationDescriptor newOwner
270 ) {
271 Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> traitToFunTypeParameters =
272 DescriptorResolverUtils.recreateTypeParametersAndReturnMapping(originalParameters, newOwner);
273 TypeSubstitutor typeParametersSubstitutor = DescriptorResolverUtils.createSubstitutorForTypeParameters(traitToFunTypeParameters);
274 for (Map.Entry<TypeParameterDescriptor, TypeParameterDescriptorImpl> mapEntry : traitToFunTypeParameters.entrySet()) {
275 TypeParameterDescriptor traitTypeParameter = mapEntry.getKey();
276 TypeParameterDescriptorImpl funTypeParameter = mapEntry.getValue();
277
278 for (JetType upperBound : traitTypeParameter.getUpperBounds()) {
279 JetType upperBoundSubstituted = typeParametersSubstitutor.substitute(upperBound, Variance.INVARIANT);
280 assert upperBoundSubstituted != null : "couldn't substitute type: " + upperBound + ", substitutor = " + typeParametersSubstitutor;
281 funTypeParameter.addUpperBound(upperBoundSubstituted);
282 }
283
284 funTypeParameter.setInitialized();
285 }
286
287 List<TypeParameterDescriptor> typeParameters = new ArrayList<TypeParameterDescriptor>(traitToFunTypeParameters.values());
288 return new TypeParameters(typeParameters, typeParametersSubstitutor);
289 }
290
291 @NotNull
292 public static SimpleFunctionDescriptor getAbstractMethodOfSamType(@NotNull JetType type) {
293 return (SimpleFunctionDescriptor) getAbstractMembers(type).get(0);
294 }
295
296 @NotNull
297 public static SimpleFunctionDescriptor getAbstractMethodOfSamInterface(@NotNull ClassDescriptor samInterface) {
298 return getAbstractMethodOfSamType(samInterface.getDefaultType());
299 }
300
301 public static boolean isSamInterface(@NotNull JavaClass javaClass) {
302 return getSamInterfaceMethod(javaClass) != null;
303 }
304
305 // Returns null if not SAM interface
306 @Nullable
307 public static JavaMethod getSamInterfaceMethod(@NotNull JavaClass javaClass) {
308 FqName fqName = javaClass.getFqName();
309 if (fqName == null || fqName.firstSegmentIs(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) {
310 return null;
311 }
312 if (!javaClass.isInterface() || javaClass.isAnnotationType()) {
313 return null;
314 }
315
316 return findOnlyAbstractMethod(javaClass);
317 }
318
319 @Nullable
320 private static JavaMethod findOnlyAbstractMethod(@NotNull JavaClass javaClass) {
321 OnlyAbstractMethodFinder finder = new OnlyAbstractMethodFinder();
322 if (finder.find(javaClass.getDefaultType())) {
323 return finder.getFoundMethod();
324 }
325 return null;
326 }
327
328 private static class TypeParameters {
329 public final List<TypeParameterDescriptor> descriptors;
330 public final TypeSubstitutor substitutor;
331
332 private TypeParameters(List<TypeParameterDescriptor> descriptors, TypeSubstitutor substitutor) {
333 this.descriptors = descriptors;
334 this.substitutor = substitutor;
335 }
336 }
337
338 private static abstract class FunctionInitializer {
339 public abstract void initialize(
340 @NotNull List<TypeParameterDescriptor> typeParameters,
341 @NotNull List<ValueParameterDescriptor> valueParameters,
342 @Nullable JetType returnType
343 );
344 }
345
346 private static class OnlyAbstractMethodFinder {
347 private JavaMethod foundMethod;
348 private JavaTypeSubstitutor foundClassSubstitutor;
349
350 private boolean find(@NotNull JavaClassifierType classifierType) {
351 JavaTypeSubstitutor classSubstitutor = classifierType.getSubstitutor();
352 JavaClassifier classifier = classifierType.getClassifier();
353 if (classifier == null) {
354 return false; // can't resolve class -> not a SAM interface
355 }
356 assert classifier instanceof JavaClass : "Classifier should be a class here: " + classifier;
357 JavaClass javaClass = (JavaClass) classifier;
358 if (JavaSupertypeResolver.OBJECT_FQ_NAME.equals(javaClass.getFqName())) {
359 return true;
360 }
361 for (JavaMethod method : javaClass.getMethods()) {
362 if (DescriptorResolverUtils.isObjectMethod(method)) { // e.g., ignore toString() declared in interface
363 continue;
364 }
365 if (!method.getTypeParameters().isEmpty()) {
366 return false; // if interface has generic methods, it is not a SAM interface
367 }
368
369 if (foundMethod == null) {
370 foundMethod = method;
371 foundClassSubstitutor = classSubstitutor;
372 continue;
373 }
374
375 if (!areSignaturesErasureEqual(method, classSubstitutor, foundMethod, foundClassSubstitutor)) {
376 return false; // different signatures
377 }
378 }
379
380 for (JavaClassifierType t : classifierType.getSupertypes()) {
381 if (!find(t)) {
382 return false;
383 }
384 }
385
386 return true;
387 }
388
389 /**
390 * @see com.intellij.psi.util.MethodSignatureUtil#areSignaturesErasureEqual
391 */
392 private static boolean areSignaturesErasureEqual(
393 @NotNull JavaMethod method1,
394 @NotNull JavaTypeSubstitutor substitutor1,
395 @NotNull JavaMethod method2,
396 @NotNull JavaTypeSubstitutor substitutor2
397 ) {
398 if (method1.isConstructor() != method2.isConstructor()) return false;
399 if (!method1.isConstructor() && !method1.getName().equals(method2.getName())) return false;
400
401 if (method1.isVararg() != method2.isVararg()) return false;
402
403 Collection<JavaValueParameter> parameters1 = method1.getValueParameters();
404 Collection<JavaValueParameter> parameters2 = method2.getValueParameters();
405 if (parameters1.size() != parameters2.size()) return false;
406
407 for (Iterator<JavaValueParameter> it1 = parameters1.iterator(), it2 = parameters2.iterator(); it1.hasNext(); ) {
408 JavaType type1 = DescriptorResolverUtils.erasure(substitutor1.substitute(it1.next().getType()), substitutor1);
409 JavaType type2 = DescriptorResolverUtils.erasure(substitutor2.substitute(it2.next().getType()), substitutor2);
410 if (!(type1 == null ? type2 == null : type1.equals(type2))) return false;
411 }
412
413 return true;
414 }
415
416 @Nullable
417 private JavaMethod getFoundMethod() {
418 return foundMethod;
419 }
420 }
421 }