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