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