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 );
175
176 return result;
177 }
178
179 public static boolean isSamType(@NotNull JetType type) {
180 return getFunctionTypeForSamType(type) != null;
181 }
182
183 public static boolean isSamAdapterNecessary(@NotNull FunctionDescriptor fun) {
184 for (ValueParameterDescriptor param : fun.getValueParameters()) {
185 if (isSamType(param.getType())) {
186 return true;
187 }
188 }
189 return false;
190 }
191
192 @NotNull
193 public static SamAdapterDescriptor<JavaMethodDescriptor> createSamAdapterFunction(@NotNull final JavaMethodDescriptor original) {
194 final SamAdapterFunctionDescriptor result = new SamAdapterFunctionDescriptor(original);
195 return initSamAdapter(original, result, new FunctionInitializer() {
196 @Override
197 public void initialize(
198 @NotNull List<TypeParameterDescriptor> typeParameters,
199 @NotNull List<ValueParameterDescriptor> valueParameters,
200 @NotNull JetType returnType
201 ) {
202 result.initialize(
203 null,
204 original.getDispatchReceiverParameter(),
205 typeParameters,
206 valueParameters,
207 returnType,
208 Modality.FINAL,
209 original.getVisibility()
210 );
211 }
212 });
213 }
214
215 @NotNull
216 public static SamAdapterDescriptor<JavaConstructorDescriptor> createSamAdapterConstructor(@NotNull final JavaConstructorDescriptor original) {
217 final SamAdapterConstructorDescriptor result = new SamAdapterConstructorDescriptor(original);
218 return initSamAdapter(original, result, new FunctionInitializer() {
219 @Override
220 public void initialize(
221 @NotNull List<TypeParameterDescriptor> typeParameters,
222 @NotNull List<ValueParameterDescriptor> valueParameters,
223 @NotNull JetType returnType
224 ) {
225 result.initialize(typeParameters, valueParameters, original.getVisibility());
226 result.setReturnType(returnType);
227 }
228 });
229 }
230
231 @NotNull
232 private static <F extends FunctionDescriptor> SamAdapterDescriptor<F> initSamAdapter(
233 @NotNull F original,
234 @NotNull SamAdapterDescriptor<F> adapter,
235 @NotNull FunctionInitializer initializer
236 ) {
237 TypeParameters typeParameters = recreateAndInitializeTypeParameters(original.getTypeParameters(), adapter);
238
239 JetType returnTypeUnsubstituted = original.getReturnType();
240 assert returnTypeUnsubstituted != null : "Creating SAM adapter for not initialized original: " + original;
241
242 TypeSubstitutor substitutor = typeParameters.substitutor;
243 JetType returnType = substitutor.substitute(returnTypeUnsubstituted, Variance.INVARIANT);
244 assert returnType != null : "couldn't substitute type: " + returnTypeUnsubstituted +
245 ", substitutor = " + substitutor;
246
247
248 List<ValueParameterDescriptor> valueParameters = createValueParametersForSamAdapter(original, adapter, substitutor);
249
250 initializer.initialize(typeParameters.descriptors, valueParameters, returnType);
251
252 return adapter;
253 }
254
255 public static List<ValueParameterDescriptor> createValueParametersForSamAdapter(
256 @NotNull FunctionDescriptor original,
257 @NotNull FunctionDescriptor samAdapter,
258 @NotNull TypeSubstitutor substitutor
259 ) {
260 List<ValueParameterDescriptor> originalValueParameters = original.getValueParameters();
261 List<ValueParameterDescriptor> valueParameters = new ArrayList<ValueParameterDescriptor>(originalValueParameters.size());
262 for (ValueParameterDescriptor originalParam : originalValueParameters) {
263 JetType originalType = originalParam.getType();
264 JetType functionType = getFunctionTypeForSamType(originalType);
265 JetType newTypeUnsubstituted = functionType != null ? functionType : originalType;
266 JetType newType = substitutor.substitute(newTypeUnsubstituted, Variance.IN_VARIANCE);
267 assert newType != null : "couldn't substitute type: " + newTypeUnsubstituted + ", substitutor = " + substitutor;
268
269 ValueParameterDescriptor newParam = new ValueParameterDescriptorImpl(
270 samAdapter, null, originalParam.getIndex(), originalParam.getAnnotations(),
271 originalParam.getName(), newType, false, null, SourceElement.NO_SOURCE
272 );
273 valueParameters.add(newParam);
274 }
275 return valueParameters;
276 }
277
278 @NotNull
279 private static TypeParameters recreateAndInitializeTypeParameters(
280 @NotNull List<TypeParameterDescriptor> originalParameters,
281 @Nullable DeclarationDescriptor newOwner
282 ) {
283 Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> traitToFunTypeParameters =
284 JavaResolverUtils.recreateTypeParametersAndReturnMapping(originalParameters, newOwner);
285 TypeSubstitutor typeParametersSubstitutor = JavaResolverUtils.createSubstitutorForTypeParameters(traitToFunTypeParameters);
286 for (Map.Entry<TypeParameterDescriptor, TypeParameterDescriptorImpl> mapEntry : traitToFunTypeParameters.entrySet()) {
287 TypeParameterDescriptor traitTypeParameter = mapEntry.getKey();
288 TypeParameterDescriptorImpl funTypeParameter = mapEntry.getValue();
289
290 for (JetType upperBound : traitTypeParameter.getUpperBounds()) {
291 JetType upperBoundSubstituted = typeParametersSubstitutor.substitute(upperBound, Variance.INVARIANT);
292 assert upperBoundSubstituted != null : "couldn't substitute type: " + upperBound + ", substitutor = " + typeParametersSubstitutor;
293 funTypeParameter.addUpperBound(upperBoundSubstituted);
294 }
295
296 funTypeParameter.setInitialized();
297 }
298
299 List<TypeParameterDescriptor> typeParameters = new ArrayList<TypeParameterDescriptor>(traitToFunTypeParameters.values());
300 return new TypeParameters(typeParameters, typeParametersSubstitutor);
301 }
302
303 // Returns null if not SAM interface
304 @Nullable
305 public static JavaMethod getSamInterfaceMethod(@NotNull JavaClass javaClass) {
306 FqName fqName = javaClass.getFqName();
307 if (fqName == null || fqName.firstSegmentIs(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) {
308 return null;
309 }
310 if (!javaClass.isInterface() || javaClass.isAnnotationType()) {
311 return null;
312 }
313
314 return findOnlyAbstractMethod(javaClass);
315 }
316
317 @Nullable
318 private static JavaMethod findOnlyAbstractMethod(@NotNull JavaClass javaClass) {
319 OnlyAbstractMethodFinder finder = new OnlyAbstractMethodFinder();
320 if (finder.find(javaClass.getDefaultType())) {
321 return finder.getFoundMethod();
322 }
323 return null;
324 }
325
326 private static class TypeParameters {
327 public final List<TypeParameterDescriptor> descriptors;
328 public final TypeSubstitutor substitutor;
329
330 private TypeParameters(List<TypeParameterDescriptor> descriptors, TypeSubstitutor substitutor) {
331 this.descriptors = descriptors;
332 this.substitutor = substitutor;
333 }
334 }
335
336 private static abstract class FunctionInitializer {
337 public abstract void initialize(
338 @NotNull List<TypeParameterDescriptor> typeParameters,
339 @NotNull List<ValueParameterDescriptor> valueParameters,
340 @NotNull JetType returnType
341 );
342 }
343
344 private static class OnlyAbstractMethodFinder {
345 private static final FqName OBJECT_FQ_NAME = new FqName("java.lang.Object");
346
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 (OBJECT_FQ_NAME.equals(javaClass.getFqName())) {
359 return true;
360 }
361 for (JavaMethod method : javaClass.getMethods()) {
362
363 //skip java 8 default methods
364 if (!method.isAbstract()) {
365 continue;
366 }
367
368 if (DescriptorResolverUtils.isObjectMethod(method)) { // e.g., ignore toString() declared in interface
369 continue;
370 }
371 if (!method.getTypeParameters().isEmpty()) {
372 return false; // if interface has generic methods, it is not a SAM interface
373 }
374
375 if (foundMethod == null) {
376 foundMethod = method;
377 foundClassSubstitutor = classSubstitutor;
378 continue;
379 }
380
381 if (!areSignaturesErasureEqual(method, classSubstitutor, foundMethod, foundClassSubstitutor)) {
382 return false; // different signatures
383 }
384 }
385
386 for (JavaClassifierType t : classifierType.getSupertypes()) {
387 if (!find(t)) {
388 return false;
389 }
390 }
391
392 return true;
393 }
394
395 /**
396 * @see com.intellij.psi.util.MethodSignatureUtil#areSignaturesErasureEqual
397 */
398 private static boolean areSignaturesErasureEqual(
399 @NotNull JavaMethod method1,
400 @NotNull JavaTypeSubstitutor substitutor1,
401 @NotNull JavaMethod method2,
402 @NotNull JavaTypeSubstitutor substitutor2
403 ) {
404 if (!method1.getName().equals(method2.getName())) return false;
405
406 Collection<JavaValueParameter> parameters1 = method1.getValueParameters();
407 Collection<JavaValueParameter> parameters2 = method2.getValueParameters();
408 if (parameters1.size() != parameters2.size()) return false;
409
410 for (Iterator<JavaValueParameter> it1 = parameters1.iterator(), it2 = parameters2.iterator(); it1.hasNext(); ) {
411 JavaValueParameter param1 = it1.next();
412 JavaValueParameter param2 = it2.next();
413 if (param1.isVararg() != param2.isVararg()) return false;
414
415 JavaType type1 = JavaResolverUtils.erasure(substitutor1.substitute(param1.getType()), substitutor1);
416 JavaType type2 = JavaResolverUtils.erasure(substitutor2.substitute(param2.getType()), substitutor2);
417 if (!(type1 == null ? type2 == null : type1.equals(type2))) return false;
418 }
419
420 return true;
421 }
422
423 @Nullable
424 private JavaMethod getFoundMethod() {
425 return foundMethod;
426 }
427 }
428 }