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.js.translate.intrinsic.functions.factories;
018
019 import com.google.common.base.Predicate;
020 import com.google.common.base.Predicates;
021 import com.google.common.collect.ImmutableMap;
022 import com.google.dart.compiler.backend.js.ast.JsBinaryOperation;
023 import com.google.dart.compiler.backend.js.ast.JsBinaryOperator;
024 import com.google.dart.compiler.backend.js.ast.JsExpression;
025 import org.jetbrains.annotations.NotNull;
026 import org.jetbrains.annotations.Nullable;
027 import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
028 import org.jetbrains.kotlin.js.patterns.DescriptorPredicate;
029 import org.jetbrains.kotlin.js.patterns.NamePredicate;
030 import org.jetbrains.kotlin.js.translate.context.TranslationContext;
031 import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic;
032 import org.jetbrains.kotlin.js.translate.operation.OperatorTable;
033 import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
034 import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils;
035 import org.jetbrains.kotlin.lexer.KtToken;
036 import org.jetbrains.kotlin.name.Name;
037 import org.jetbrains.kotlin.types.expressions.OperatorConventions;
038 import org.jetbrains.kotlin.util.OperatorNameConventions;
039
040 import java.util.List;
041
042 import static org.jetbrains.kotlin.js.patterns.PatternBuilder.pattern;
043
044 public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
045 INSTANCE;
046
047 private static abstract class BinaryOperationIntrinsicBase extends FunctionIntrinsic {
048 @NotNull
049 public abstract JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context);
050
051 @NotNull
052 @Override
053 public JsExpression apply(
054 @Nullable JsExpression receiver,
055 @NotNull List<JsExpression> arguments,
056 @NotNull TranslationContext context) {
057 assert receiver != null;
058 assert arguments.size() == 1;
059 return doApply(receiver, arguments.get(0), context);
060 }
061 }
062
063 @NotNull
064 private static final BinaryOperationIntrinsicBase RANGE_TO_INTRINSIC = new BinaryOperationIntrinsicBase() {
065 @NotNull
066 @Override
067 public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) {
068 //TODO: add tests and correct expression for reversed ranges.
069 return JsAstUtils.numberRangeTo(left, right);
070 }
071 };
072
073 @NotNull
074 private static final BinaryOperationIntrinsicBase CHAR_RANGE_TO_INTRINSIC = new BinaryOperationIntrinsicBase() {
075 @NotNull
076 @Override
077 public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) {
078 //TODO: add tests and correct expression for reversed ranges.
079 return JsAstUtils.charRangeTo(left, right);
080 }
081 };
082
083 @NotNull
084 private static final BinaryOperationIntrinsicBase INTEGER_DIVISION_INTRINSIC = new BinaryOperationIntrinsicBase() {
085 @NotNull
086 @Override
087 public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) {
088 JsBinaryOperation div = new JsBinaryOperation(JsBinaryOperator.DIV, left, right);
089 return JsAstUtils.toInt32(div);
090 }
091 };
092
093 @NotNull
094 private static final BinaryOperationIntrinsicBase BUILTINS_COMPARE_TO_INTRINSIC = new BinaryOperationIntrinsicBase() {
095 @NotNull
096 @Override
097 public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) {
098 return JsAstUtils.compareTo(left, right);
099 }
100 };
101
102 @NotNull
103 private static final BinaryOperationIntrinsicBase PRIMITIVE_NUMBER_COMPARE_TO_INTRINSIC = new BinaryOperationIntrinsicBase() {
104 @NotNull
105 @Override
106 public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) {
107 return JsAstUtils.primitiveCompareTo(left, right);
108 }
109 };
110
111 @NotNull
112 private static final NamePredicate BINARY_OPERATIONS = new NamePredicate(OperatorConventions.BINARY_OPERATION_NAMES.values());
113 private static final DescriptorPredicate PRIMITIVE_NUMBERS_BINARY_OPERATIONS =
114 pattern(NamePredicate.PRIMITIVE_NUMBERS_MAPPED_TO_PRIMITIVE_JS, BINARY_OPERATIONS);
115
116 private static final DescriptorPredicate PRIMITIVE_NUMBERS_COMPARE_TO_OPERATIONS =
117 pattern(NamePredicate.PRIMITIVE_NUMBERS_MAPPED_TO_PRIMITIVE_JS, "compareTo");
118 private static final DescriptorPredicate INT_WITH_BIT_OPERATIONS = pattern("Int.or|and|xor|shl|shr|ushr");
119 private static final DescriptorPredicate BOOLEAN_OPERATIONS = pattern("Boolean.or|and|xor");
120 private static final DescriptorPredicate STRING_PLUS = pattern("String.plus");
121
122 private static final ImmutableMap<String, JsBinaryOperator> BINARY_BITWISE_OPERATIONS = ImmutableMap.<String, JsBinaryOperator>builder()
123 .put("or", JsBinaryOperator.BIT_OR)
124 .put("and", JsBinaryOperator.BIT_AND)
125 .put("xor", JsBinaryOperator.BIT_XOR)
126 .put("shl", JsBinaryOperator.SHL)
127 .put("shr", JsBinaryOperator.SHR)
128 .put("ushr", JsBinaryOperator.SHRU)
129 .build();
130
131 private static final Predicate<FunctionDescriptor> PREDICATE = Predicates.or(PRIMITIVE_NUMBERS_BINARY_OPERATIONS, BOOLEAN_OPERATIONS,
132 STRING_PLUS, INT_WITH_BIT_OPERATIONS,
133 PRIMITIVE_NUMBERS_COMPARE_TO_OPERATIONS);
134
135 @Nullable
136 @Override
137 public FunctionIntrinsic getIntrinsic(@NotNull FunctionDescriptor descriptor) {
138 if (pattern("Char.rangeTo(Char)").apply(descriptor)) {
139 return CHAR_RANGE_TO_INTRINSIC;
140 }
141
142 if (PRIMITIVE_NUMBERS_COMPARE_TO_OPERATIONS.apply(descriptor)) {
143 return PRIMITIVE_NUMBER_COMPARE_TO_INTRINSIC;
144 }
145
146
147 if (JsDescriptorUtils.isBuiltin(descriptor) && descriptor.getName().equals(OperatorNameConventions.COMPARE_TO)) {
148 return BUILTINS_COMPARE_TO_INTRINSIC;
149 }
150
151 if (!PREDICATE.apply(descriptor)) {
152 return null;
153 }
154
155
156 if (pattern("Int|Short|Byte.div(Int|Short|Byte)").apply(descriptor)) {
157 return INTEGER_DIVISION_INTRINSIC;
158 }
159 if (descriptor.getName().equals(Name.identifier("rangeTo"))) {
160 return RANGE_TO_INTRINSIC;
161 }
162 if (INT_WITH_BIT_OPERATIONS.apply(descriptor)) {
163 JsBinaryOperator op = BINARY_BITWISE_OPERATIONS.get(descriptor.getName().asString());
164 if (op != null) {
165 return new PrimitiveBinaryOperationFunctionIntrinsic(op);
166 }
167 }
168 JsBinaryOperator operator = getOperator(descriptor);
169 BinaryOperationIntrinsicBase result = new PrimitiveBinaryOperationFunctionIntrinsic(operator);
170
171 if (pattern("Char.plus|minus(Int)").apply(descriptor)) {
172 return new CharAndIntBinaryOperationFunctionIntrinsic(result);
173 }
174 if (pattern("Char.minus(Char)").apply(descriptor)) {
175 return new CharAndCharBinaryOperationFunctionIntrinsic(result);
176 }
177 return result;
178 }
179
180 @NotNull
181 private static JsBinaryOperator getOperator(@NotNull FunctionDescriptor descriptor) {
182 KtToken token = OperatorConventions.BINARY_OPERATION_NAMES.inverse().get(descriptor.getName());
183 if (token == null) {
184 token = OperatorConventions.BOOLEAN_OPERATIONS.inverse().get(descriptor.getName());
185 }
186 if (token == null) {
187 assert descriptor.getName().asString().equals("xor");
188 return JsBinaryOperator.BIT_XOR;
189 }
190 return OperatorTable.getBinaryOperator(token);
191 }
192
193 private static class PrimitiveBinaryOperationFunctionIntrinsic extends BinaryOperationIntrinsicBase {
194
195 @NotNull
196 private final JsBinaryOperator operator;
197
198 private PrimitiveBinaryOperationFunctionIntrinsic(@NotNull JsBinaryOperator operator) {
199 this.operator = operator;
200 }
201
202 @NotNull
203 @Override
204 public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) {
205 return new JsBinaryOperation(operator, left, right);
206 }
207 }
208
209 private static class CharAndIntBinaryOperationFunctionIntrinsic extends BinaryOperationIntrinsicBase {
210
211 @NotNull
212 private final BinaryOperationIntrinsicBase functionIntrinsic;
213
214 private CharAndIntBinaryOperationFunctionIntrinsic(@NotNull BinaryOperationIntrinsicBase functionIntrinsic) {
215 this.functionIntrinsic = functionIntrinsic;
216 }
217
218 @NotNull
219 @Override
220 public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) {
221 return JsAstUtils.toChar(functionIntrinsic.doApply(JsAstUtils.charToInt(left), right, context));
222 }
223 }
224
225 private static class CharAndCharBinaryOperationFunctionIntrinsic extends BinaryOperationIntrinsicBase {
226
227 @NotNull
228 private final BinaryOperationIntrinsicBase functionIntrinsic;
229
230 private CharAndCharBinaryOperationFunctionIntrinsic(@NotNull BinaryOperationIntrinsicBase functionIntrinsic) {
231 this.functionIntrinsic = functionIntrinsic;
232 }
233
234 @NotNull
235 @Override
236 public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) {
237 return functionIntrinsic.doApply(JsAstUtils.charToInt(left), JsAstUtils.charToInt(right), context);
238 }
239 }
240 }