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