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