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