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 }