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.JavaClassDescriptor;
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 JavaClassDescriptor) {
093 // Function2<T, T, Int>
094 JetType functionTypeDefault = ((JavaClassDescriptor) 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 JavaClassDescriptor 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 );
166
167 return result;
168 }
169
170 public static boolean isSamType(@NotNull JetType type) {
171 return getFunctionTypeForSamType(type) != null;
172 }
173
174 public static boolean isSamAdapterNecessary(@NotNull FunctionDescriptor fun) {
175 for (ValueParameterDescriptor param : fun.getValueParameters()) {
176 if (isSamType(param.getType())) {
177 return true;
178 }
179 }
180 return false;
181 }
182
183 @NotNull
184 public static SamAdapterDescriptor<SimpleFunctionDescriptor> createSamAdapterFunction(@NotNull final SimpleFunctionDescriptor original) {
185 final SamAdapterFunctionDescriptor result = new SamAdapterFunctionDescriptor(original);
186 return initSamAdapter(original, result, new FunctionInitializer() {
187 @Override
188 public void initialize(
189 @NotNull List<TypeParameterDescriptor> typeParameters,
190 @NotNull List<ValueParameterDescriptor> valueParameters,
191 @Nullable JetType returnType
192 ) {
193 result.initialize(
194 null,
195 original.getExpectedThisObject(),
196 typeParameters,
197 valueParameters,
198 returnType,
199 Modality.FINAL,
200 original.getVisibility()
201 );
202 }
203 });
204 }
205
206 @NotNull
207 public static SamAdapterDescriptor<ConstructorDescriptor> createSamAdapterConstructor(@NotNull final ConstructorDescriptor original) {
208 final SamAdapterConstructorDescriptor result = new SamAdapterConstructorDescriptor(original);
209 return initSamAdapter(original, result, new FunctionInitializer() {
210 @Override
211 public void initialize(
212 @NotNull List<TypeParameterDescriptor> typeParameters,
213 @NotNull List<ValueParameterDescriptor> valueParameters,
214 @Nullable JetType returnType
215 ) {
216 result.initialize(
217 typeParameters,
218 valueParameters,
219 original.getVisibility(),
220 original.getExpectedThisObject() == ReceiverParameterDescriptor.NO_RECEIVER_PARAMETER
221 );
222 }
223 });
224 }
225
226 @NotNull
227 private static <F extends FunctionDescriptor> SamAdapterDescriptor<F> initSamAdapter(
228 @NotNull F original,
229 @NotNull SamAdapterDescriptor<F> adapter,
230 @NotNull FunctionInitializer initializer
231 ) {
232 TypeParameters typeParameters = recreateAndInitializeTypeParameters(original.getTypeParameters(), adapter);
233
234 JetType returnTypeUnsubstituted = original.getReturnType();
235 JetType returnType;
236 if (returnTypeUnsubstituted == null) { // return type may be null for not yet initialized constructors
237 returnType = null;
238 }
239 else {
240 returnType = typeParameters.substitutor.substitute(returnTypeUnsubstituted, Variance.OUT_VARIANCE);
241 assert returnType != null : "couldn't substitute type: " + returnTypeUnsubstituted +
242 ", substitutor = " + typeParameters.substitutor;
243 }
244
245 List<ValueParameterDescriptor> originalValueParameters = original.getValueParameters();
246 List<ValueParameterDescriptor> valueParameters = new ArrayList<ValueParameterDescriptor>(originalValueParameters.size());
247 for (ValueParameterDescriptor originalParam : originalValueParameters) {
248 JetType originalType = originalParam.getType();
249 JetType functionType = getFunctionTypeForSamType(originalType);
250 JetType newTypeUnsubstituted = functionType != null ? functionType : originalType;
251 JetType newType = typeParameters.substitutor.substitute(newTypeUnsubstituted, Variance.IN_VARIANCE);
252 assert newType != null : "couldn't substitute type: " + newTypeUnsubstituted + ", substitutor = " + typeParameters.substitutor;
253
254 ValueParameterDescriptor newParam = new ValueParameterDescriptorImpl(
255 adapter, originalParam.getIndex(), originalParam.getAnnotations(), originalParam.getName(), newType, false, null);
256 valueParameters.add(newParam);
257 }
258
259 initializer.initialize(typeParameters.descriptors, valueParameters, returnType);
260
261 return adapter;
262 }
263
264 @NotNull
265 private static TypeParameters recreateAndInitializeTypeParameters(
266 @NotNull List<TypeParameterDescriptor> originalParameters,
267 @Nullable DeclarationDescriptor newOwner
268 ) {
269 Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> traitToFunTypeParameters =
270 DescriptorResolverUtils.recreateTypeParametersAndReturnMapping(originalParameters, newOwner);
271 TypeSubstitutor typeParametersSubstitutor = DescriptorResolverUtils.createSubstitutorForTypeParameters(traitToFunTypeParameters);
272 for (Map.Entry<TypeParameterDescriptor, TypeParameterDescriptorImpl> mapEntry : traitToFunTypeParameters.entrySet()) {
273 TypeParameterDescriptor traitTypeParameter = mapEntry.getKey();
274 TypeParameterDescriptorImpl funTypeParameter = mapEntry.getValue();
275
276 for (JetType upperBound : traitTypeParameter.getUpperBounds()) {
277 JetType upperBoundSubstituted = typeParametersSubstitutor.substitute(upperBound, Variance.INVARIANT);
278 assert upperBoundSubstituted != null : "couldn't substitute type: " + upperBound + ", substitutor = " + typeParametersSubstitutor;
279 funTypeParameter.addUpperBound(upperBoundSubstituted);
280 }
281
282 funTypeParameter.setInitialized();
283 }
284
285 List<TypeParameterDescriptor> typeParameters = new ArrayList<TypeParameterDescriptor>(traitToFunTypeParameters.values());
286 return new TypeParameters(typeParameters, typeParametersSubstitutor);
287 }
288
289 @NotNull
290 public static SimpleFunctionDescriptor getAbstractMethodOfSamType(@NotNull JetType type) {
291 return (SimpleFunctionDescriptor) getAbstractMembers(type).get(0);
292 }
293
294 @NotNull
295 public static SimpleFunctionDescriptor getAbstractMethodOfSamInterface(@NotNull ClassDescriptor samInterface) {
296 return getAbstractMethodOfSamType(samInterface.getDefaultType());
297 }
298
299 public static boolean isSamInterface(@NotNull JavaClass javaClass) {
300 return getSamInterfaceMethod(javaClass) != null;
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 @Nullable JetType returnType
341 );
342 }
343
344 private static class OnlyAbstractMethodFinder {
345 private JavaMethod foundMethod;
346 private JavaTypeSubstitutor foundClassSubstitutor;
347
348 private boolean find(@NotNull JavaClassifierType classifierType) {
349 JavaTypeSubstitutor classSubstitutor = classifierType.getSubstitutor();
350 JavaClassifier classifier = classifierType.getClassifier();
351 if (classifier == null) {
352 return false; // can't resolve class -> not a SAM interface
353 }
354 assert classifier instanceof JavaClass : "Classifier should be a class here: " + classifier;
355 JavaClass javaClass = (JavaClass) classifier;
356 if (JavaSupertypeResolver.OBJECT_FQ_NAME.equals(javaClass.getFqName())) {
357 return true;
358 }
359 for (JavaMethod method : javaClass.getMethods()) {
360 if (DescriptorResolverUtils.isObjectMethod(method)) { // e.g., ignore toString() declared in interface
361 continue;
362 }
363 if (!method.getTypeParameters().isEmpty()) {
364 return false; // if interface has generic methods, it is not a SAM interface
365 }
366
367 if (foundMethod == null) {
368 foundMethod = method;
369 foundClassSubstitutor = classSubstitutor;
370 continue;
371 }
372
373 if (!areSignaturesErasureEqual(method, classSubstitutor, foundMethod, foundClassSubstitutor)) {
374 return false; // different signatures
375 }
376 }
377
378 for (JavaClassifierType t : classifierType.getSupertypes()) {
379 if (!find(t)) {
380 return false;
381 }
382 }
383
384 return true;
385 }
386
387 /**
388 * @see com.intellij.psi.util.MethodSignatureUtil#areSignaturesErasureEqual
389 */
390 private static boolean areSignaturesErasureEqual(
391 @NotNull JavaMethod method1,
392 @NotNull JavaTypeSubstitutor substitutor1,
393 @NotNull JavaMethod method2,
394 @NotNull JavaTypeSubstitutor substitutor2
395 ) {
396 if (method1.isConstructor() != method2.isConstructor()) return false;
397 if (!method1.isConstructor() && !method1.getName().equals(method2.getName())) return false;
398
399 if (method1.isVararg() != method2.isVararg()) return false;
400
401 Collection<JavaValueParameter> parameters1 = method1.getValueParameters();
402 Collection<JavaValueParameter> parameters2 = method2.getValueParameters();
403 if (parameters1.size() != parameters2.size()) return false;
404
405 for (Iterator<JavaValueParameter> it1 = parameters1.iterator(), it2 = parameters2.iterator(); it1.hasNext(); ) {
406 JavaType type1 = DescriptorResolverUtils.erasure(substitutor1.substitute(it1.next().getType()), substitutor1);
407 JavaType type2 = DescriptorResolverUtils.erasure(substitutor2.substitute(it2.next().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 }