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.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.BindingContext; 030 import org.jetbrains.kotlin.resolve.DescriptorUtils; 031 import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt; 032 import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; 033 import org.jetbrains.kotlin.types.KotlinType; 034 035 import java.util.Arrays; 036 import java.util.List; 037 038 import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.RANGES_PACKAGE_FQ_NAME; 039 import static org.jetbrains.kotlin.codegen.AsmUtil.isPrimitiveNumberClassDescriptor; 040 041 public class RangeCodegenUtil { 042 private static final ImmutableMap<FqName, PrimitiveType> RANGE_TO_ELEMENT_TYPE; 043 private static final ImmutableMap<FqName, PrimitiveType> PROGRESSION_TO_ELEMENT_TYPE; 044 045 @NotNull 046 public static List<PrimitiveType> supportedRangeTypes() { 047 return Arrays.asList(PrimitiveType.CHAR, PrimitiveType.INT, PrimitiveType.LONG); 048 } 049 050 static { 051 ImmutableMap.Builder<FqName, PrimitiveType> rangeBuilder = ImmutableMap.builder(); 052 ImmutableMap.Builder<FqName, PrimitiveType> progressionBuilder = ImmutableMap.builder(); 053 for (PrimitiveType primitiveType : supportedRangeTypes()) { 054 FqName rangeClassFqName = RANGES_PACKAGE_FQ_NAME.child(Name.identifier(primitiveType.getTypeName() + "Range")); 055 FqName progressionClassFqName = RANGES_PACKAGE_FQ_NAME.child(Name.identifier(primitiveType.getTypeName() + "Progression")); 056 rangeBuilder.put(rangeClassFqName, primitiveType); 057 progressionBuilder.put(progressionClassFqName, primitiveType); 058 } 059 RANGE_TO_ELEMENT_TYPE = rangeBuilder.build(); 060 PROGRESSION_TO_ELEMENT_TYPE = progressionBuilder.build(); 061 } 062 063 private RangeCodegenUtil() {} 064 065 public static boolean isRange(KotlinType rangeType) { 066 return !rangeType.isMarkedNullable() && getPrimitiveRangeElementType(rangeType) != null; 067 } 068 069 public static boolean isProgression(KotlinType rangeType) { 070 return !rangeType.isMarkedNullable() && getPrimitiveProgressionElementType(rangeType) != null; 071 } 072 073 @Nullable 074 public static ResolvedCall<? extends CallableDescriptor> getLoopRangeResolvedCall(@NotNull KtForExpression forExpression, @NotNull BindingContext bindingContext) { 075 KtExpression loopRange = KtPsiUtil.deparenthesize(forExpression.getLoopRange()); 076 077 if (loopRange instanceof KtQualifiedExpression) { 078 KtQualifiedExpression qualifiedExpression = (KtQualifiedExpression) loopRange; 079 KtExpression selector = qualifiedExpression.getSelectorExpression(); 080 if (selector instanceof KtCallExpression || selector instanceof KtSimpleNameExpression) { 081 return CallUtilKt.getResolvedCall(selector, bindingContext); 082 } 083 } 084 else if (loopRange instanceof KtSimpleNameExpression || loopRange instanceof KtCallExpression) { 085 return CallUtilKt.getResolvedCall(loopRange, bindingContext); 086 } 087 else if (loopRange instanceof KtBinaryExpression) { 088 return CallUtilKt.getResolvedCall(((KtBinaryExpression) loopRange).getOperationReference(), bindingContext); 089 } 090 091 return null; 092 } 093 094 @Nullable 095 private static PrimitiveType getPrimitiveRangeElementType(KotlinType rangeType) { 096 return getPrimitiveRangeOrProgressionElementType(rangeType, RANGE_TO_ELEMENT_TYPE); 097 } 098 099 @Nullable 100 private static PrimitiveType getPrimitiveProgressionElementType(KotlinType rangeType) { 101 return getPrimitiveRangeOrProgressionElementType(rangeType, PROGRESSION_TO_ELEMENT_TYPE); 102 } 103 104 @Nullable 105 private static PrimitiveType getPrimitiveRangeOrProgressionElementType( 106 @NotNull KotlinType rangeOrProgression, 107 @NotNull ImmutableMap<FqName, PrimitiveType> map 108 ) { 109 ClassifierDescriptor declarationDescriptor = rangeOrProgression.getConstructor().getDeclarationDescriptor(); 110 if (declarationDescriptor == null) return null; 111 FqNameUnsafe fqName = DescriptorUtils.getFqName(declarationDescriptor); 112 if (!fqName.isSafe()) return null; 113 return map.get(fqName.toSafe()); 114 } 115 116 @Nullable 117 public static PrimitiveType getPrimitiveRangeOrProgressionElementType(@NotNull FqName rangeOrProgressionName) { 118 PrimitiveType result = RANGE_TO_ELEMENT_TYPE.get(rangeOrProgressionName); 119 return result != null ? result : PROGRESSION_TO_ELEMENT_TYPE.get(rangeOrProgressionName); 120 } 121 122 public static boolean isRangeOrProgression(@NotNull FqName className) { 123 return getPrimitiveRangeOrProgressionElementType(className) != null; 124 } 125 126 public static boolean isOptimizableRangeTo(CallableDescriptor rangeTo) { 127 if ("rangeTo".equals(rangeTo.getName().asString())) { 128 if (isPrimitiveNumberClassDescriptor(rangeTo.getContainingDeclaration())) { 129 return true; 130 } 131 } 132 return false; 133 } 134 135 public static boolean isOptimizableDownTo(@NotNull CallableDescriptor descriptor) { 136 if (!isTopLevelInPackage(descriptor, "downTo", "kotlin.ranges")) return false; 137 138 ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter(); 139 if (extensionReceiver == null) return false; 140 ClassifierDescriptor extensionReceiverClassifier = extensionReceiver.getType().getConstructor().getDeclarationDescriptor(); 141 if (!isPrimitiveNumberClassDescriptor(extensionReceiverClassifier)) return false; 142 143 return true; 144 } 145 146 public static boolean isArrayOrPrimitiveArrayIndices(@NotNull CallableDescriptor descriptor) { 147 if (!isTopLevelInPackage(descriptor, "indices", "kotlin.collections")) return false; 148 149 ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter(); 150 if (extensionReceiver == null) return false; 151 KotlinType extensionReceiverType = extensionReceiver.getType(); 152 if (!KotlinBuiltIns.isArray(extensionReceiverType) && !KotlinBuiltIns.isPrimitiveArray(extensionReceiverType)) return false; 153 154 return true; 155 } 156 157 public static boolean isCollectionIndices(@NotNull CallableDescriptor descriptor) { 158 if (!isTopLevelInPackage(descriptor, "indices", "kotlin.collections")) return false; 159 160 ReceiverParameterDescriptor extensionReceiver = descriptor.getExtensionReceiverParameter(); 161 if (extensionReceiver == null) return false; 162 KotlinType extensionReceiverType = extensionReceiver.getType(); 163 if (!KotlinBuiltIns.isCollectionOrNullableCollection(extensionReceiverType)) return false; 164 165 return true; 166 } 167 168 private static boolean isTopLevelInPackage(@NotNull CallableDescriptor descriptor, @NotNull String name, @NotNull String packageName) { 169 if (!name.equals(descriptor.getName().asString())) return false; 170 171 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); 172 if (!(containingDeclaration instanceof PackageFragmentDescriptor)) return false; 173 String packageFqName = ((PackageFragmentDescriptor) containingDeclaration).getFqName().asString(); 174 if (!packageName.equals(packageFqName)) return false; 175 176 return true; 177 } 178 }