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