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.when;
018
019 import kotlin.jvm.functions.Function1;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.kotlin.codegen.ExpressionCodegen;
023 import org.jetbrains.kotlin.codegen.binding.CodegenBinding;
024 import org.jetbrains.kotlin.psi.*;
025 import org.jetbrains.kotlin.resolve.BindingContext;
026 import org.jetbrains.kotlin.resolve.constants.ConstantValue;
027 import org.jetbrains.kotlin.resolve.constants.IntegerValueConstant;
028 import org.jetbrains.kotlin.resolve.constants.NullValue;
029 import org.jetbrains.kotlin.resolve.constants.StringValue;
030 import org.jetbrains.org.objectweb.asm.Type;
031
032 import java.util.ArrayList;
033 import java.util.List;
034
035 public class SwitchCodegenUtil {
036 public static boolean checkAllItemsAreConstantsSatisfying(
037 @NotNull KtWhenExpression expression,
038 @NotNull BindingContext bindingContext,
039 Function1<ConstantValue<?>, Boolean> predicate
040 ) {
041 for (KtWhenEntry entry : expression.getEntries()) {
042 for (KtWhenCondition condition : entry.getConditions()) {
043 if (!(condition instanceof KtWhenConditionWithExpression)) {
044 return false;
045 }
046
047 // ensure that expression is constant
048 KtExpression patternExpression = ((KtWhenConditionWithExpression) condition).getExpression();
049
050 if (patternExpression == null) return false;
051
052 ConstantValue<?> constant = ExpressionCodegen.getCompileTimeConstant(patternExpression, bindingContext);
053 if (constant == null || !predicate.invoke(constant)) {
054 return false;
055 }
056 }
057 }
058
059 return true;
060 }
061
062 @NotNull
063 public static Iterable<ConstantValue<?>> getAllConstants(
064 @NotNull KtWhenExpression expression,
065 @NotNull BindingContext bindingContext
066 ) {
067 List<ConstantValue<?>> result = new ArrayList<ConstantValue<?>>();
068
069 for (KtWhenEntry entry : expression.getEntries()) {
070 addConstantsFromEntry(result, entry, bindingContext);
071 }
072
073 return result;
074 }
075
076 private static void addConstantsFromEntry(
077 @NotNull List<ConstantValue<?>> result,
078 @NotNull KtWhenEntry entry,
079 @NotNull BindingContext bindingContext
080 ) {
081 for (KtWhenCondition condition : entry.getConditions()) {
082 if (!(condition instanceof KtWhenConditionWithExpression)) continue;
083
084 KtExpression patternExpression = ((KtWhenConditionWithExpression) condition).getExpression();
085
086 assert patternExpression != null : "expression in when should not be null";
087 result.add(ExpressionCodegen.getCompileTimeConstant(patternExpression, bindingContext));
088 }
089 }
090
091 @NotNull
092 public static Iterable<ConstantValue<?>> getConstantsFromEntry(
093 @NotNull KtWhenEntry entry,
094 @NotNull BindingContext bindingContext
095 ) {
096 List<ConstantValue<?>> result = new ArrayList<ConstantValue<?>>();
097 addConstantsFromEntry(result, entry, bindingContext);
098 return result;
099 }
100
101 @Nullable
102 public static SwitchCodegen buildAppropriateSwitchCodegenIfPossible(
103 @NotNull KtWhenExpression expression,
104 boolean isStatement,
105 boolean isExhaustive,
106 @NotNull ExpressionCodegen codegen
107 ) {
108 BindingContext bindingContext = codegen.getBindingContext();
109 if (!isThereConstantEntriesButNulls(expression, bindingContext)) {
110 return null;
111 }
112
113 Type subjectType = codegen.expressionType(expression.getSubjectExpression());
114
115 WhenByEnumsMapping mapping = codegen.getBindingContext().get(CodegenBinding.MAPPING_FOR_WHEN_BY_ENUM, expression);
116
117 if (mapping != null) {
118 return new EnumSwitchCodegen(expression, isStatement, isExhaustive, codegen, mapping);
119 }
120
121 if (isIntegralConstantsSwitch(expression, subjectType, bindingContext)) {
122 return new IntegralConstantsSwitchCodegen(expression, isStatement, isExhaustive, codegen);
123 }
124
125 if (isStringConstantsSwitch(expression, subjectType, bindingContext)) {
126 return new StringSwitchCodegen(expression, isStatement, isExhaustive, codegen);
127 }
128
129 return null;
130 }
131
132 private static boolean isThereConstantEntriesButNulls(
133 @NotNull KtWhenExpression expression,
134 @NotNull BindingContext bindingContext
135 ) {
136 for (ConstantValue<?> constant : getAllConstants(expression, bindingContext)) {
137 if (constant != null && !(constant instanceof NullValue)) return true;
138 }
139
140 return false;
141 }
142
143 private static boolean isIntegralConstantsSwitch(
144 @NotNull KtWhenExpression expression,
145 @NotNull Type subjectType,
146 @NotNull BindingContext bindingContext
147 ) {
148 int typeSort = subjectType.getSort();
149
150 if (typeSort != Type.INT && typeSort != Type.CHAR && typeSort != Type.SHORT && typeSort != Type.BYTE) {
151 return false;
152 }
153
154 return checkAllItemsAreConstantsSatisfying(expression, bindingContext, new Function1<ConstantValue<?>, Boolean>() {
155 @Override
156 public Boolean invoke(
157 @NotNull ConstantValue<?> constant
158 ) {
159 return constant instanceof IntegerValueConstant;
160 }
161 });
162 }
163
164 private static boolean isStringConstantsSwitch(
165 @NotNull KtWhenExpression expression,
166 @NotNull Type subjectType,
167 @NotNull BindingContext bindingContext
168 ) {
169
170 if (!subjectType.getClassName().equals(String.class.getName())) {
171 return false;
172 }
173
174 return checkAllItemsAreConstantsSatisfying(expression, bindingContext, new Function1<ConstantValue<?>, Boolean>() {
175 @Override
176 public Boolean invoke(
177 @NotNull ConstantValue<?> constant
178 ) {
179 return constant instanceof StringValue || constant instanceof NullValue;
180 }
181 });
182 }
183 }