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.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.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) {
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 return substitute == null ? null : fixProjections(TypeUtils.makeNullableAsSpecified(substitute, samType.isNullable()));
100 }
101 }
102 return null;
103 }
104
105 @NotNull
106 public static JetType getFunctionTypeForAbstractMethod(@NotNull FunctionDescriptor function) {
107 JetType returnType = function.getReturnType();
108 assert returnType != null : "function is not initialized: " + function;
109 List<ValueParameterDescriptor> valueParameters = function.getValueParameters();
110 List<JetType> parameterTypes = new ArrayList<JetType>(valueParameters.size());
111 for (ValueParameterDescriptor parameter : valueParameters) {
112 parameterTypes.add(parameter.getType());
113 }
114 return KotlinBuiltIns.getInstance().getFunctionType(
115 Annotations.EMPTY, null, parameterTypes, returnType);
116 }
117
118 private static boolean isSamInterface(@NotNull ClassDescriptor klass) {
119 if (klass.getKind() != ClassKind.TRAIT) {
120 return false;
121 }
122
123 List<CallableMemberDescriptor> abstractMembers = getAbstractMembers(klass.getDefaultType());
124 if (abstractMembers.size() == 1) {
125 CallableMemberDescriptor member = abstractMembers.get(0);
126 if (member instanceof SimpleFunctionDescriptor) {
127 return member.getTypeParameters().isEmpty();
128 }
129 }
130 return false;
131 }
132
133 @NotNull
134 public static SamConstructorDescriptor createSamConstructorFunction(
135 @NotNull ClassOrPackageFragmentDescriptor owner,
136 @NotNull JavaClassDescriptor samInterface
137 ) {
138 assert isSamInterface(samInterface) : samInterface;
139
140 SamConstructorDescriptor result = new SamConstructorDescriptor(owner, samInterface);
141
142 TypeParameters typeParameters = recreateAndInitializeTypeParameters(samInterface.getTypeConstructor().getParameters(), result);
143
144 JetType parameterTypeUnsubstituted = getFunctionTypeForSamType(samInterface.getDefaultType());
145 assert parameterTypeUnsubstituted != null : "couldn't get function type for SAM type " + samInterface.getDefaultType();
146 JetType parameterType = typeParameters.substitutor.substitute(parameterTypeUnsubstituted, Variance.IN_VARIANCE);
147 assert parameterType != null : "couldn't substitute type: " + parameterTypeUnsubstituted +
148 ", substitutor = " + typeParameters.substitutor;
149 ValueParameterDescriptor parameter = new ValueParameterDescriptorImpl(
150 result, null, 0, Annotations.EMPTY, Name.identifier("function"), parameterType, false, null);
151
152 JetType returnType = typeParameters.substitutor.substitute(samInterface.getDefaultType(), Variance.OUT_VARIANCE);
153 assert returnType != null : "couldn't substitute type: " + samInterface.getDefaultType() +
154 ", substitutor = " + typeParameters.substitutor;
155
156 result.initialize(
157 null,
158 null,
159 typeParameters.descriptors,
160 Arrays.asList(parameter),
161 returnType,
162 Modality.FINAL,
163 samInterface.getVisibility()
164 );
165
166 return result;
167 }
168
169 public static boolean isSamType(@NotNull JetType type) {
170 return getFunctionTypeForSamType(type) != null;
171 }
172
173 public static boolean isSamAdapterNecessary(@NotNull FunctionDescriptor fun) {
174 for (ValueParameterDescriptor param : fun.getValueParameters()) {
175 if (isSamType(param.getType())) {
176 return true;
177 }
178 }
179 return false;
180 }
181
182 @NotNull
183 public static SamAdapterDescriptor<SimpleFunctionDescriptor> createSamAdapterFunction(@NotNull final SimpleFunctionDescriptor original) {
184 final SamAdapterFunctionDescriptor result = new SamAdapterFunctionDescriptor(original);
185 return initSamAdapter(original, result, new FunctionInitializer() {
186 @Override
187 public void initialize(
188 @NotNull List<TypeParameterDescriptor> typeParameters,
189 @NotNull List<ValueParameterDescriptor> valueParameters,
190 @Nullable JetType returnType
191 ) {
192 result.initialize(
193 null,
194 original.getExpectedThisObject(),
195 typeParameters,
196 valueParameters,
197 returnType,
198 Modality.FINAL,
199 original.getVisibility()
200 );
201 }
202 });
203 }
204
205 @NotNull
206 public static SamAdapterDescriptor<ConstructorDescriptor> createSamAdapterConstructor(@NotNull final ConstructorDescriptor original) {
207 final SamAdapterConstructorDescriptor result = new SamAdapterConstructorDescriptor(original);
208 return initSamAdapter(original, result, new FunctionInitializer() {
209 @Override
210 public void initialize(
211 @NotNull List<TypeParameterDescriptor> typeParameters,
212 @NotNull List<ValueParameterDescriptor> valueParameters,
213 @Nullable JetType returnType
214 ) {
215 result.initialize(
216 typeParameters,
217 valueParameters,
218 original.getVisibility(),
219 original.getExpectedThisObject() == ReceiverParameterDescriptor.NO_RECEIVER_PARAMETER
220 );
221 }
222 });
223 }
224
225 @NotNull
226 private static <F extends FunctionDescriptor> SamAdapterDescriptor<F> initSamAdapter(
227 @NotNull F original,
228 @NotNull SamAdapterDescriptor<F> adapter,
229 @NotNull FunctionInitializer initializer
230 ) {
231 TypeParameters typeParameters = recreateAndInitializeTypeParameters(original.getTypeParameters(), adapter);
232
233 JetType returnTypeUnsubstituted = original.getReturnType();
234 JetType returnType;
235 if (returnTypeUnsubstituted == null) { // return type may be null for not yet initialized constructors
236 returnType = null;
237 }
238 else {
239 returnType = typeParameters.substitutor.substitute(returnTypeUnsubstituted, Variance.OUT_VARIANCE);
240 assert returnType != null : "couldn't substitute type: " + returnTypeUnsubstituted +
241 ", substitutor = " + typeParameters.substitutor;
242 }
243
244 List<ValueParameterDescriptor> originalValueParameters = original.getValueParameters();
245 List<ValueParameterDescriptor> valueParameters = new ArrayList<ValueParameterDescriptor>(originalValueParameters.size());
246 for (ValueParameterDescriptor originalParam : originalValueParameters) {
247 JetType originalType = originalParam.getType();
248 JetType functionType = getFunctionTypeForSamType(originalType);
249 JetType newTypeUnsubstituted = functionType != null ? functionType : originalType;
250 JetType newType = typeParameters.substitutor.substitute(newTypeUnsubstituted, Variance.IN_VARIANCE);
251 assert newType != null : "couldn't substitute type: " + newTypeUnsubstituted + ", substitutor = " + typeParameters.substitutor;
252
253 ValueParameterDescriptor newParam = new ValueParameterDescriptorImpl(
254 adapter, null, originalParam.getIndex(), originalParam.getAnnotations(), originalParam.getName(), newType, false, null);
255 valueParameters.add(newParam);
256 }
257
258 initializer.initialize(typeParameters.descriptors, valueParameters, returnType);
259
260 return adapter;
261 }
262
263 @NotNull
264 private static TypeParameters recreateAndInitializeTypeParameters(
265 @NotNull List<TypeParameterDescriptor> originalParameters,
266 @Nullable DeclarationDescriptor newOwner
267 ) {
268 Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> traitToFunTypeParameters =
269 DescriptorResolverUtils.recreateTypeParametersAndReturnMapping(originalParameters, newOwner);
270 TypeSubstitutor typeParametersSubstitutor = DescriptorResolverUtils.createSubstitutorForTypeParameters(traitToFunTypeParameters);
271 for (Map.Entry<TypeParameterDescriptor, TypeParameterDescriptorImpl> mapEntry : traitToFunTypeParameters.entrySet()) {
272 TypeParameterDescriptor traitTypeParameter = mapEntry.getKey();
273 TypeParameterDescriptorImpl funTypeParameter = mapEntry.getValue();
274
275 for (JetType upperBound : traitTypeParameter.getUpperBounds()) {
276 JetType upperBoundSubstituted = typeParametersSubstitutor.substitute(upperBound, Variance.INVARIANT);
277 assert upperBoundSubstituted != null : "couldn't substitute type: " + upperBound + ", substitutor = " + typeParametersSubstitutor;
278 funTypeParameter.addUpperBound(upperBoundSubstituted);
279 }
280
281 funTypeParameter.setInitialized();
282 }
283
284 List<TypeParameterDescriptor> typeParameters = new ArrayList<TypeParameterDescriptor>(traitToFunTypeParameters.values());
285 return new TypeParameters(typeParameters, typeParametersSubstitutor);
286 }
287
288 public static boolean isSamInterface(@NotNull JavaClass javaClass) {
289 return getSamInterfaceMethod(javaClass) != null;
290 }
291
292 // Returns null if not SAM interface
293 @Nullable
294 public static JavaMethod getSamInterfaceMethod(@NotNull JavaClass javaClass) {
295 FqName fqName = javaClass.getFqName();
296 if (fqName == null || fqName.firstSegmentIs(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) {
297 return null;
298 }
299 if (!javaClass.isInterface() || javaClass.isAnnotationType()) {
300 return null;
301 }
302
303 return findOnlyAbstractMethod(javaClass);
304 }
305
306 @Nullable
307 private static JavaMethod findOnlyAbstractMethod(@NotNull JavaClass javaClass) {
308 OnlyAbstractMethodFinder finder = new OnlyAbstractMethodFinder();
309 if (finder.find(javaClass.getDefaultType())) {
310 return finder.getFoundMethod();
311 }
312 return null;
313 }
314
315 private static class TypeParameters {
316 public final List<TypeParameterDescriptor> descriptors;
317 public final TypeSubstitutor substitutor;
318
319 private TypeParameters(List<TypeParameterDescriptor> descriptors, TypeSubstitutor substitutor) {
320 this.descriptors = descriptors;
321 this.substitutor = substitutor;
322 }
323 }
324
325 private static abstract class FunctionInitializer {
326 public abstract void initialize(
327 @NotNull List<TypeParameterDescriptor> typeParameters,
328 @NotNull List<ValueParameterDescriptor> valueParameters,
329 @Nullable JetType returnType
330 );
331 }
332
333 private static class OnlyAbstractMethodFinder {
334 private JavaMethod foundMethod;
335 private JavaTypeSubstitutor foundClassSubstitutor;
336
337 private boolean find(@NotNull JavaClassifierType classifierType) {
338 JavaTypeSubstitutor classSubstitutor = classifierType.getSubstitutor();
339 JavaClassifier classifier = classifierType.getClassifier();
340 if (classifier == null) {
341 return false; // can't resolve class -> not a SAM interface
342 }
343 assert classifier instanceof JavaClass : "Classifier should be a class here: " + classifier;
344 JavaClass javaClass = (JavaClass) classifier;
345 if (DescriptorResolverUtils.OBJECT_FQ_NAME.equals(javaClass.getFqName())) {
346 return true;
347 }
348 for (JavaMethod method : javaClass.getMethods()) {
349 if (DescriptorResolverUtils.isObjectMethod(method)) { // e.g., ignore toString() declared in interface
350 continue;
351 }
352 if (!method.getTypeParameters().isEmpty()) {
353 return false; // if interface has generic methods, it is not a SAM interface
354 }
355
356 if (foundMethod == null) {
357 foundMethod = method;
358 foundClassSubstitutor = classSubstitutor;
359 continue;
360 }
361
362 if (!areSignaturesErasureEqual(method, classSubstitutor, foundMethod, foundClassSubstitutor)) {
363 return false; // different signatures
364 }
365 }
366
367 for (JavaClassifierType t : classifierType.getSupertypes()) {
368 if (!find(t)) {
369 return false;
370 }
371 }
372
373 return true;
374 }
375
376 /**
377 * @see com.intellij.psi.util.MethodSignatureUtil#areSignaturesErasureEqual
378 */
379 private static boolean areSignaturesErasureEqual(
380 @NotNull JavaMethod method1,
381 @NotNull JavaTypeSubstitutor substitutor1,
382 @NotNull JavaMethod method2,
383 @NotNull JavaTypeSubstitutor substitutor2
384 ) {
385 if (method1.isConstructor() != method2.isConstructor()) return false;
386 if (!method1.isConstructor() && !method1.getName().equals(method2.getName())) return false;
387
388 if (method1.isVararg() != method2.isVararg()) return false;
389
390 Collection<JavaValueParameter> parameters1 = method1.getValueParameters();
391 Collection<JavaValueParameter> parameters2 = method2.getValueParameters();
392 if (parameters1.size() != parameters2.size()) return false;
393
394 for (Iterator<JavaValueParameter> it1 = parameters1.iterator(), it2 = parameters2.iterator(); it1.hasNext(); ) {
395 JavaType type1 = DescriptorResolverUtils.erasure(substitutor1.substitute(it1.next().getType()), substitutor1);
396 JavaType type2 = DescriptorResolverUtils.erasure(substitutor2.substitute(it2.next().getType()), substitutor2);
397 if (!(type1 == null ? type2 == null : type1.equals(type2))) return false;
398 }
399
400 return true;
401 }
402
403 @Nullable
404 private JavaMethod getFoundMethod() {
405 return foundMethod;
406 }
407 }
408 }