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.k2js.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.jet.lang.descriptors.FunctionDescriptor;
028 import org.jetbrains.jet.lang.resolve.name.Name;
029 import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
030 import org.jetbrains.jet.lexer.JetToken;
031 import org.jetbrains.k2js.translate.context.TranslationContext;
032 import org.jetbrains.k2js.translate.intrinsic.functions.basic.FunctionIntrinsic;
033 import org.jetbrains.k2js.translate.intrinsic.functions.patterns.DescriptorPredicate;
034 import org.jetbrains.k2js.translate.intrinsic.functions.patterns.NamePredicate;
035 import org.jetbrains.k2js.translate.operation.OperatorTable;
036 import org.jetbrains.k2js.translate.utils.JsAstUtils;
037 import org.jetbrains.k2js.translate.utils.JsDescriptorUtils;
038
039 import java.util.List;
040
041 import static org.jetbrains.k2js.translate.intrinsic.functions.patterns.PatternBuilder.pattern;
042
043 public enum PrimitiveBinaryOperationFIF implements FunctionIntrinsicFactory {
044 INSTANCE;
045
046 private static abstract class BinaryOperationInstrinsicBase 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 BinaryOperationInstrinsicBase RANGE_TO_INTRINSIC = new BinaryOperationInstrinsicBase() {
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 BinaryOperationInstrinsicBase CHAR_RANGE_TO_INTRINSIC = new BinaryOperationInstrinsicBase() {
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 BinaryOperationInstrinsicBase INTEGER_DIVISION_INTRINSIC = new BinaryOperationInstrinsicBase() {
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 BinaryOperationInstrinsicBase BUILTINS_COMPARE_TO_INTRINSIC = new BinaryOperationInstrinsicBase() {
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 BinaryOperationInstrinsicBase PRIMITIVE_NUMBER_COMPARE_TO_INTRINSIC = new BinaryOperationInstrinsicBase() {
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("Int|Short|Byte|Double|Float.compareTo(Char)").apply(descriptor)) {
138 return new WithCharAsSecondOperandFunctionIntrinsic(PRIMITIVE_NUMBER_COMPARE_TO_INTRINSIC);
139 }
140
141 if (pattern("Char.compareTo(Int|Short|Byte|Double|Float)").apply(descriptor)) {
142 return new WithCharAsFirstOperandFunctionIntrinsic(PRIMITIVE_NUMBER_COMPARE_TO_INTRINSIC);
143 }
144
145 if (pattern("Char.rangeTo(Char)").apply(descriptor)) {
146 return CHAR_RANGE_TO_INTRINSIC;
147 }
148
149 if (PRIMITIVE_NUMBERS_COMPARE_TO_OPERATIONS.apply(descriptor)) {
150 return PRIMITIVE_NUMBER_COMPARE_TO_INTRINSIC;
151 }
152
153
154 if (JsDescriptorUtils.isBuiltin(descriptor) && descriptor.getName().equals(OperatorConventions.COMPARE_TO)) {
155 return BUILTINS_COMPARE_TO_INTRINSIC;
156 }
157
158 if (!PREDICATE.apply(descriptor)) {
159 return null;
160 }
161
162
163 if (pattern("Int|Short|Byte.div(Int|Short|Byte)").apply(descriptor)) {
164 return INTEGER_DIVISION_INTRINSIC;
165 }
166 if (pattern("Char.div(Int|Short|Byte)").apply(descriptor)) {
167 return new WithCharAsFirstOperandFunctionIntrinsic(INTEGER_DIVISION_INTRINSIC);
168 }
169 if (pattern("Int|Short|Byte.div(Char)").apply(descriptor)) {
170 return new WithCharAsSecondOperandFunctionIntrinsic(INTEGER_DIVISION_INTRINSIC);
171 }
172 if (descriptor.getName().equals(Name.identifier("rangeTo"))) {
173 return RANGE_TO_INTRINSIC;
174 }
175 if (INT_WITH_BIT_OPERATIONS.apply(descriptor)) {
176 JsBinaryOperator op = BINARY_BITWISE_OPERATIONS.get(descriptor.getName().asString());
177 if (op != null) {
178 return new PrimitiveBinaryOperationFunctionIntrinsic(op);
179 }
180 }
181 JsBinaryOperator operator = getOperator(descriptor);
182 BinaryOperationInstrinsicBase result = new PrimitiveBinaryOperationFunctionIntrinsic(operator);
183
184 if (pattern("Char.plus|minus|times|div|mod(Int|Short|Byte|Double|Float)").apply(descriptor)) {
185 return new WithCharAsFirstOperandFunctionIntrinsic(result);
186 }
187 if (pattern("Int|Short|Byte|Double|Float.plus|minus|times|div|mod(Char)").apply(descriptor)) {
188 return new WithCharAsSecondOperandFunctionIntrinsic(result);
189 }
190 return result;
191 }
192
193 @NotNull
194 private static JsBinaryOperator getOperator(@NotNull FunctionDescriptor descriptor) {
195 JetToken token = OperatorConventions.BINARY_OPERATION_NAMES.inverse().get(descriptor.getName());
196 if (token == null) {
197 token = OperatorConventions.BOOLEAN_OPERATIONS.inverse().get(descriptor.getName());
198 }
199 if (token == null) {
200 assert descriptor.getName().asString().equals("xor");
201 return JsBinaryOperator.BIT_XOR;
202 }
203 return OperatorTable.getBinaryOperator(token);
204 }
205
206 private static class PrimitiveBinaryOperationFunctionIntrinsic extends BinaryOperationInstrinsicBase {
207
208 @NotNull
209 private final JsBinaryOperator operator;
210
211 private PrimitiveBinaryOperationFunctionIntrinsic(@NotNull JsBinaryOperator operator) {
212 this.operator = operator;
213 }
214
215 @NotNull
216 @Override
217 public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) {
218 return new JsBinaryOperation(operator, left, right);
219 }
220 }
221
222 private static class WithCharAsFirstOperandFunctionIntrinsic extends BinaryOperationInstrinsicBase {
223
224 @NotNull
225 private final BinaryOperationInstrinsicBase functionIntrinsic;
226
227 private WithCharAsFirstOperandFunctionIntrinsic(@NotNull BinaryOperationInstrinsicBase functionIntrinsic) {
228 this.functionIntrinsic = functionIntrinsic;
229 }
230
231 @NotNull
232 @Override
233 public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) {
234 return functionIntrinsic.doApply(JsAstUtils.charToInt(left), right, context);
235 }
236 }
237
238 private static class WithCharAsSecondOperandFunctionIntrinsic extends BinaryOperationInstrinsicBase {
239
240 @NotNull
241 private final BinaryOperationInstrinsicBase functionIntrinsic;
242
243 private WithCharAsSecondOperandFunctionIntrinsic(@NotNull BinaryOperationInstrinsicBase functionIntrinsic) {
244 this.functionIntrinsic = functionIntrinsic;
245 }
246
247 @NotNull
248 @Override
249 public JsExpression doApply(@NotNull JsExpression left, @NotNull JsExpression right, @NotNull TranslationContext context) {
250 return functionIntrinsic.doApply(left, JsAstUtils.charToInt(right), context);
251 }
252 }
253 }