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 }