001 /* 002 * Copyright 2010-2013 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.jet.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.jet.lang.descriptors.CallableDescriptor; 023 import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor; 024 import org.jetbrains.jet.lang.psi.*; 025 import org.jetbrains.jet.lang.resolve.DescriptorUtils; 026 import org.jetbrains.jet.lang.resolve.name.FqName; 027 import org.jetbrains.jet.lang.resolve.name.Name; 028 import org.jetbrains.jet.lang.types.JetType; 029 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; 030 import org.jetbrains.jet.lang.types.lang.PrimitiveType; 031 032 import java.util.List; 033 034 import static org.jetbrains.jet.codegen.AsmUtil.isPrimitiveNumberClassDescriptor; 035 import static org.jetbrains.jet.lang.types.lang.KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME; 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.isNullable() && getPrimitiveRangeElementType(rangeType) != null; 058 } 059 060 public static boolean isProgression(JetType rangeType) { 061 return !rangeType.isNullable() && 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 public static boolean isOptimizableRangeTo(CallableDescriptor rangeTo) { 119 if ("rangeTo".equals(rangeTo.getName().asString())) { 120 if (isPrimitiveNumberClassDescriptor(rangeTo.getContainingDeclaration())) { 121 return true; 122 } 123 } 124 return false; 125 } 126 127 public static class BinaryCall { 128 public final JetExpression left; 129 public final JetExpression op; 130 public final JetExpression right; 131 132 private BinaryCall(JetExpression left, JetExpression op, JetExpression right) { 133 this.left = left; 134 this.op = op; 135 this.right = right; 136 } 137 } 138 }