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.PrimitiveType;
023    import org.jetbrains.kotlin.descriptors.CallableDescriptor;
024    import org.jetbrains.kotlin.descriptors.ClassifierDescriptor;
025    import org.jetbrains.kotlin.name.FqName;
026    import org.jetbrains.kotlin.name.FqNameUnsafe;
027    import org.jetbrains.kotlin.name.Name;
028    import org.jetbrains.kotlin.psi.*;
029    import org.jetbrains.kotlin.resolve.DescriptorUtils;
030    import org.jetbrains.kotlin.types.KotlinType;
031    
032    import java.util.List;
033    
034    import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME;
035    import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitiveNumberClassDescriptor;
036    
037    public class RangeCodegenUtil {
038        private static final ImmutableMap<FqName, PrimitiveType> RANGE_TO_ELEMENT_TYPE;
039        private static final ImmutableMap<FqName, PrimitiveType> PROGRESSION_TO_ELEMENT_TYPE;
040    
041        private static PrimitiveType[] supportedRangeTypes() {
042            return new PrimitiveType[] {
043                    PrimitiveType.CHAR,
044                    PrimitiveType.INT,
045                    PrimitiveType.LONG,
046                    // deprecated:
047                    PrimitiveType.BYTE,
048                    PrimitiveType.SHORT,
049            };
050        }
051    
052        static {
053            ImmutableMap.Builder<FqName, PrimitiveType> rangeBuilder = ImmutableMap.builder();
054            ImmutableMap.Builder<FqName, PrimitiveType> progressionBuilder = ImmutableMap.builder();
055            for (PrimitiveType primitiveType : supportedRangeTypes()) {
056                FqName rangeClassFqName = BUILT_INS_PACKAGE_FQ_NAME.child(Name.identifier(primitiveType.getTypeName() + "Range"));
057                FqName progressionClassFqName = BUILT_INS_PACKAGE_FQ_NAME.child(Name.identifier(primitiveType.getTypeName() + "Progression"));
058                rangeBuilder.put(rangeClassFqName, primitiveType);
059                progressionBuilder.put(progressionClassFqName, primitiveType);
060            }
061            RANGE_TO_ELEMENT_TYPE = rangeBuilder.build();
062            PROGRESSION_TO_ELEMENT_TYPE = progressionBuilder.build();
063        }
064    
065        private RangeCodegenUtil() {}
066    
067        public static boolean isRange(KotlinType rangeType) {
068            return !rangeType.isMarkedNullable() && getPrimitiveRangeElementType(rangeType) != null;
069        }
070    
071        public static boolean isProgression(KotlinType rangeType) {
072            return !rangeType.isMarkedNullable() && getPrimitiveProgressionElementType(rangeType) != null;
073        }
074    
075        @Nullable
076        public static BinaryCall getRangeAsBinaryCall(@NotNull KtForExpression forExpression) {
077            // We are looking for rangeTo() calls
078            // Other binary operations will succeed too, but will be filtered out later (by examining a resolvedCall)
079            KtExpression rangeExpression = forExpression.getLoopRange();
080            assert rangeExpression != null;
081            KtExpression loopRange = KtPsiUtil.deparenthesize(rangeExpression);
082            if (loopRange instanceof KtQualifiedExpression) {
083                // a.rangeTo(b)
084                KtQualifiedExpression qualifiedExpression = (KtQualifiedExpression) loopRange;
085                KtExpression selector = qualifiedExpression.getSelectorExpression();
086                if (selector instanceof KtCallExpression) {
087                    KtCallExpression callExpression = (KtCallExpression) selector;
088                    List<? extends ValueArgument> arguments = callExpression.getValueArguments();
089                    if (arguments.size() == 1) {
090                        return new BinaryCall(qualifiedExpression.getReceiverExpression(), callExpression.getCalleeExpression(),
091                                              arguments.get(0).getArgumentExpression());
092                    }
093                }
094            }
095            else if (loopRange instanceof KtBinaryExpression) {
096                // a rangeTo b
097                // a .. b
098                KtBinaryExpression binaryExpression = (KtBinaryExpression) loopRange;
099                return new BinaryCall(binaryExpression.getLeft(), binaryExpression.getOperationReference(), binaryExpression.getRight());
100    
101            }
102            return null;
103        }
104    
105        @Nullable
106        private static PrimitiveType getPrimitiveRangeElementType(KotlinType rangeType) {
107            return getPrimitiveRangeOrProgressionElementType(rangeType, RANGE_TO_ELEMENT_TYPE);
108        }
109    
110        @Nullable
111        private static PrimitiveType getPrimitiveProgressionElementType(KotlinType rangeType) {
112            return getPrimitiveRangeOrProgressionElementType(rangeType, PROGRESSION_TO_ELEMENT_TYPE);
113        }
114    
115        @Nullable
116        private static PrimitiveType getPrimitiveRangeOrProgressionElementType(
117                @NotNull KotlinType rangeOrProgression,
118                @NotNull ImmutableMap<FqName, PrimitiveType> map
119        ) {
120            ClassifierDescriptor declarationDescriptor = rangeOrProgression.getConstructor().getDeclarationDescriptor();
121            if (declarationDescriptor == null) return null;
122            FqNameUnsafe fqName = DescriptorUtils.getFqName(declarationDescriptor);
123            if (!fqName.isSafe()) return null;
124            return map.get(fqName.toSafe());
125        }
126    
127        @Nullable
128        public static PrimitiveType getPrimitiveRangeOrProgressionElementType(@NotNull FqName rangeOrProgressionName) {
129            PrimitiveType result = RANGE_TO_ELEMENT_TYPE.get(rangeOrProgressionName);
130            return result != null ? result : PROGRESSION_TO_ELEMENT_TYPE.get(rangeOrProgressionName);
131        }
132    
133        public static boolean isRangeOrProgression(@NotNull FqName className) {
134            return getPrimitiveRangeOrProgressionElementType(className) != null;
135        }
136    
137        public static boolean isOptimizableRangeTo(CallableDescriptor rangeTo) {
138            if ("rangeTo".equals(rangeTo.getName().asString())) {
139                if (isPrimitiveNumberClassDescriptor(rangeTo.getContainingDeclaration())) {
140                    return true;
141                }
142            }
143            return false;
144        }
145    
146        public static class BinaryCall {
147            public final KtExpression left;
148            public final KtExpression op;
149            public final KtExpression right;
150    
151            private BinaryCall(KtExpression left, KtExpression op, KtExpression right) {
152                this.left = left;
153                this.op = op;
154                this.right = right;
155            }
156        }
157    }