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 }