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    }