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