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