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.codegen;
018    
019    import com.google.common.collect.ImmutableMap;
020    import org.jetbrains.annotations.NotNull;
021    import org.jetbrains.annotations.Nullable;
022    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
023    import org.jetbrains.kotlin.builtins.PrimitiveType;
024    import org.jetbrains.kotlin.descriptors.*;
025    import org.jetbrains.kotlin.lexer.KtTokens;
026    import org.jetbrains.kotlin.name.FqName;
027    import org.jetbrains.kotlin.name.FqNameUnsafe;
028    import org.jetbrains.kotlin.name.Name;
029    import org.jetbrains.kotlin.psi.*;
030    import org.jetbrains.kotlin.resolve.BindingContext;
031    import org.jetbrains.kotlin.resolve.DescriptorUtils;
032    import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
033    import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
034    import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
035    import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
036    import org.jetbrains.kotlin.types.KotlinType;
037    import org.jetbrains.org.objectweb.asm.Type;
038    
039    import java.util.Arrays;
040    import java.util.List;
041    
042    import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.RANGES_PACKAGE_FQ_NAME;
043    import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitiveNumberClassDescriptor;
044    
045    public class RangeCodegenUtil {
046        private static final ImmutableMap<FqName, PrimitiveType> RANGE_TO_ELEMENT_TYPE;
047        private static final ImmutableMap<FqName, PrimitiveType> PROGRESSION_TO_ELEMENT_TYPE;
048    
049        @NotNull
050        public static List<PrimitiveType> supportedRangeTypes() {
051            return Arrays.asList(PrimitiveType.CHAR, PrimitiveType.INT, PrimitiveType.LONG);
052        }
053    
054        static {
055            ImmutableMap.Builder<FqName, PrimitiveType> rangeBuilder = ImmutableMap.builder();
056            ImmutableMap.Builder<FqName, PrimitiveType> progressionBuilder = ImmutableMap.builder();
057            for (PrimitiveType primitiveType : supportedRangeTypes()) {
058                FqName rangeClassFqName = RANGES_PACKAGE_FQ_NAME.child(Name.identifier(primitiveType.getTypeName() + "Range"));
059                FqName progressionClassFqName = RANGES_PACKAGE_FQ_NAME.child(Name.identifier(primitiveType.getTypeName() + "Progression"));
060                rangeBuilder.put(rangeClassFqName, primitiveType);
061                progressionBuilder.put(progressionClassFqName, primitiveType);
062            }
063            RANGE_TO_ELEMENT_TYPE = rangeBuilder.build();
064            PROGRESSION_TO_ELEMENT_TYPE = progressionBuilder.build();
065        }
066    
067        private RangeCodegenUtil() {}
068    
069        public static boolean isRange(KotlinType rangeType) {
070            return !rangeType.isMarkedNullable() && getPrimitiveRangeElementType(rangeType) != null;
071        }
072    
073        public static boolean isProgression(KotlinType rangeType) {
074            return !rangeType.isMarkedNullable() && getPrimitiveProgressionElementType(rangeType) != null;
075        }
076    
077        @Nullable
078        public static ResolvedCall<? extends CallableDescriptor> getLoopRangeResolvedCall(@NotNull KtForExpression forExpression, @NotNull BindingContext bindingContext) {
079            KtExpression loopRange = KtPsiUtil.deparenthesize(forExpression.getLoopRange());
080    
081            if (loopRange instanceof KtQualifiedExpression) {
082                KtQualifiedExpression qualifiedExpression = (KtQualifiedExpression) loopRange;
083                KtExpression selector = qualifiedExpression.getSelectorExpression();
084                if (selector instanceof KtCallExpression || selector instanceof KtSimpleNameExpression) {
085                    return CallUtilKt.getResolvedCall(selector, bindingContext);
086                }
087            }
088            else if (loopRange instanceof KtSimpleNameExpression || loopRange instanceof KtCallExpression) {
089                return CallUtilKt.getResolvedCall(loopRange, bindingContext);
090            }
091            else if (loopRange instanceof KtBinaryExpression) {
092                return CallUtilKt.getResolvedCall(((KtBinaryExpression) loopRange).getOperationReference(), bindingContext);
093            }
094    
095            return null;
096        }
097    
098        @Nullable
099        private static PrimitiveType getPrimitiveRangeElementType(KotlinType rangeType) {
100            return getPrimitiveRangeOrProgressionElementType(rangeType, RANGE_TO_ELEMENT_TYPE);
101        }
102    
103        @Nullable
104        private static PrimitiveType getPrimitiveProgressionElementType(KotlinType rangeType) {
105            return getPrimitiveRangeOrProgressionElementType(rangeType, PROGRESSION_TO_ELEMENT_TYPE);
106        }
107    
108        @Nullable
109        private static PrimitiveType getPrimitiveRangeOrProgressionElementType(
110                @NotNull KotlinType rangeOrProgression,
111                @NotNull ImmutableMap<FqName, PrimitiveType> map
112        ) {
113            ClassifierDescriptor declarationDescriptor = rangeOrProgression.getConstructor().getDeclarationDescriptor();
114            if (declarationDescriptor == null) return null;
115            FqNameUnsafe fqName = DescriptorUtils.getFqName(declarationDescriptor);
116            if (!fqName.isSafe()) return null;
117            return map.get(fqName.toSafe());
118        }
119    
120        @Nullable
121        public static PrimitiveType getPrimitiveRangeOrProgressionElementType(@NotNull FqName rangeOrProgressionName) {
122            PrimitiveType result = RANGE_TO_ELEMENT_TYPE.get(rangeOrProgressionName);
123            return result != null ? result : PROGRESSION_TO_ELEMENT_TYPE.get(rangeOrProgressionName);
124        }
125    
126        public static boolean isRangeOrProgression(@NotNull FqName className) {
127            return getPrimitiveRangeOrProgressionElementType(className) != null;
128        }
129    
130        public static boolean isPrimitiveNumberRangeTo(CallableDescriptor rangeTo) {
131            if (!"rangeTo".equals(rangeTo.getName().asString())) return false;
132    
133            if (!isPrimitiveNumberClassDescriptor(rangeTo.getContainingDeclaration())) return false;
134    
135            return true;
136        }
137    
138        private static boolean isPrimitiveRangeToExtension(@NotNull CallableDescriptor descriptor) {
139            if (!isTopLevelInPackage(descriptor, "rangeTo", "kotlin.ranges")) return false;
140    
141            ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter();
142            if (extensionReceiver == null) return false;
143    
144            return KotlinBuiltIns.isPrimitiveType(extensionReceiver.getType());
145        }
146    
147        public static boolean isPrimitiveNumberDownTo(@NotNull CallableDescriptor descriptor) {
148            if (!isTopLevelInPackage(descriptor, "downTo", "kotlin.ranges")) return false;
149    
150            ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter();
151            if (extensionReceiver == null) return false;
152            ClassifierDescriptor extensionReceiverClassifier = extensionReceiver.getType().getConstructor().getDeclarationDescriptor();
153            if (!isPrimitiveNumberClassDescriptor(extensionReceiverClassifier)) return false;
154    
155            return true;
156        }
157    
158        public static boolean isArrayOrPrimitiveArrayIndices(@NotNull CallableDescriptor descriptor) {
159            if (!isTopLevelInPackage(descriptor, "indices", "kotlin.collections")) return false;
160    
161            ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter();
162            if (extensionReceiver == null) return false;
163            KotlinType extensionReceiverType = extensionReceiver.getType();
164            if (!KotlinBuiltIns.isArray(extensionReceiverType) && !KotlinBuiltIns.isPrimitiveArray(extensionReceiverType)) return false;
165    
166            return true;
167        }
168    
169        public static boolean isCollectionIndices(@NotNull CallableDescriptor descriptor) {
170            if (!isTopLevelInPackage(descriptor, "indices", "kotlin.collections")) return false;
171    
172            ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter();
173            if (extensionReceiver == null) return false;
174            KotlinType extensionReceiverType = extensionReceiver.getType();
175            if (!KotlinBuiltIns.isCollectionOrNullableCollection(extensionReceiverType)) return false;
176    
177            return true;
178        }
179    
180        public static boolean isCharSequenceIndices(@NotNull CallableDescriptor descriptor) {
181            if (!isTopLevelInPackage(descriptor, "indices", "kotlin.text")) return false;
182    
183            ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter();
184            if (extensionReceiver == null) return false;
185            KotlinType extensionReceiverType = extensionReceiver.getType();
186            if (!KotlinBuiltIns.isCharSequenceOrNullableCharSequence(extensionReceiverType)) return false;
187    
188            return true;
189        }
190    
191        public static boolean isPrimitiveRangeToExtension(@NotNull KtSimpleNameExpression operationReference, @NotNull BindingContext bindingContext) {
192            ResolvedCall<? extends CallableDescriptor> resolvedCall = CallUtilKt
193                    .getResolvedCallWithAssert(operationReference, bindingContext);
194            ReceiverValue receiver = resolvedCall.getDispatchReceiver();
195    
196            /*
197             * Range is optimizable if
198             * 'in' receiver is expression 'rangeTo' from stdlib package
199             * and its argument has same primitive type as generic range parameter.
200             * For non-matching primitive types (e.g. int in double range)
201             * dispatch receiver will be null, because extension method will be called.
202             */
203            if (!(receiver instanceof ExpressionReceiver)) return false;
204            ExpressionReceiver e = (ExpressionReceiver) receiver;
205    
206            ResolvedCall<? extends CallableDescriptor> resolvedReceiver =
207                    CallUtilKt.getResolvedCall(e.getExpression(), bindingContext);
208            if (resolvedReceiver == null) return false;
209    
210            return isPrimitiveRangeToExtension(resolvedReceiver.getResultingDescriptor());
211        }
212    
213        /*
214         * Checks whether for expression 'x in a..b' a..b is primitive integral range
215         * with same type as x.
216         */
217        public static boolean isPrimitiveRangeSpecializationOfType(
218                @NotNull Type argumentType,
219                @NotNull KtExpression rangeExpression,
220                @NotNull BindingContext bindingContext
221        ) {
222            if (rangeExpression instanceof KtBinaryExpression &&
223                ((KtBinaryExpression) rangeExpression).getOperationReference().getReferencedNameElementType() == KtTokens.RANGE) {
224                KotlinType kotlinType = bindingContext.getType(rangeExpression);
225                assert kotlinType != null;
226                DeclarationDescriptor descriptor = kotlinType.getConstructor().getDeclarationDescriptor();
227                if (descriptor != null) {
228                    FqNameUnsafe fqName = DescriptorUtils.getFqName(descriptor);
229                    if (fqName.equals(KotlinBuiltIns.FQ_NAMES.longRange)) {
230                        return argumentType == Type.LONG_TYPE;
231                    }
232                    if (fqName.equals(KotlinBuiltIns.FQ_NAMES.charRange) || fqName.equals(KotlinBuiltIns.FQ_NAMES.intRange)) {
233                        return AsmUtil.isIntPrimitive(argumentType);
234                    }
235                }
236            }
237    
238            return false;
239        }
240    
241        private static boolean isTopLevelInPackage(@NotNull CallableDescriptor descriptor, @NotNull String name, @NotNull String packageName) {
242            if (!name.equals(descriptor.getName().asString())) return false;
243    
244            DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
245            if (!(containingDeclaration instanceof PackageFragmentDescriptor)) return false;
246            String packageFqName = ((PackageFragmentDescriptor) containingDeclaration).getFqName().asString();
247            if (!packageName.equals(packageFqName)) return false;
248    
249            return true;
250        }
251    }