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.operation;
018    
019    import com.google.dart.compiler.backend.js.ast.JsBinaryOperation;
020    import com.google.dart.compiler.backend.js.ast.JsBinaryOperator;
021    import com.google.dart.compiler.backend.js.ast.JsExpression;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
025    import org.jetbrains.jet.lang.psi.JetBinaryExpression;
026    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
027    import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
028    import org.jetbrains.jet.lexer.JetToken;
029    import org.jetbrains.jet.lexer.JetTokens;
030    import org.jetbrains.k2js.translate.callTranslator.CallTranslator;
031    import org.jetbrains.k2js.translate.context.TranslationContext;
032    import org.jetbrains.k2js.translate.general.AbstractTranslator;
033    import org.jetbrains.k2js.translate.intrinsic.operation.BinaryOperationIntrinsic;
034    import org.jetbrains.k2js.translate.utils.TranslationUtils;
035    
036    import static org.jetbrains.k2js.translate.operation.AssignmentTranslator.isAssignmentOperator;
037    import static org.jetbrains.k2js.translate.operation.CompareToTranslator.isCompareToCall;
038    import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression;
039    import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionResolvedCall;
040    import static org.jetbrains.k2js.translate.utils.JsAstUtils.not;
041    import static org.jetbrains.k2js.translate.utils.PsiUtils.*;
042    import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateLeftExpression;
043    import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateRightExpression;
044    
045    
046    public final class BinaryOperationTranslator extends AbstractTranslator {
047    
048        @NotNull
049        public static JsExpression translate(@NotNull JetBinaryExpression expression,
050                @NotNull TranslationContext context) {
051            JsExpression jsExpression = new BinaryOperationTranslator(expression, context).translate();
052            return jsExpression.source(expression);
053        }
054    
055        @NotNull
056        /*package*/ static JsExpression translateAsOverloadedCall(@NotNull JetBinaryExpression expression,
057                @NotNull TranslationContext context) {
058            JsExpression jsExpression = (new BinaryOperationTranslator(expression, context)).translateAsOverloadedBinaryOperation();
059            return jsExpression.source(expression);
060        }
061    
062        @NotNull
063        private final JetBinaryExpression expression;
064    
065        @Nullable
066        private final FunctionDescriptor operationDescriptor;
067    
068        private BinaryOperationTranslator(@NotNull JetBinaryExpression expression,
069                @NotNull TranslationContext context) {
070            super(context);
071            this.expression = expression;
072            this.operationDescriptor =
073                    getFunctionDescriptorForOperationExpression(bindingContext(), expression);
074        }
075    
076        @NotNull
077        private JsExpression translate() {
078            BinaryOperationIntrinsic intrinsic = getIntrinsicForExpression();
079            if (intrinsic != null) {
080                return applyIntrinsic(intrinsic);
081            }
082            if (getOperationToken(expression).equals(JetTokens.ELVIS)) {
083                return TranslationUtils.notNullConditional(translateLeftExpression(context(), expression),
084                                                           translateRightExpression(context(), expression), context());
085            }
086            if (isAssignmentOperator(expression)) {
087                return AssignmentTranslator.translate(expression, context());
088            }
089            if (isNotOverloadable()) {
090                return translateAsUnOverloadableBinaryOperation();
091            }
092            if (isCompareToCall(expression, context())) {
093                return CompareToTranslator.translate(expression, context());
094            }
095            assert operationDescriptor != null :
096                    "Overloadable operations must have not null descriptor";
097            return translateAsOverloadedBinaryOperation();
098        }
099    
100        @Nullable
101        private BinaryOperationIntrinsic getIntrinsicForExpression() {
102            return context().intrinsics().getBinaryOperationIntrinsics().getIntrinsic(expression, context());
103        }
104    
105        @NotNull
106        private JsExpression applyIntrinsic(@NotNull BinaryOperationIntrinsic intrinsic) {
107            return intrinsic.apply(expression,
108                                   translateLeftExpression(context(), expression),
109                                   translateRightExpression(context(), expression),
110                                   context());
111        }
112    
113        private boolean isNotOverloadable() {
114            return operationDescriptor == null;
115        }
116    
117        @NotNull
118        private JsExpression translateAsUnOverloadableBinaryOperation() {
119            JetToken token = getOperationToken(expression);
120            JsBinaryOperator operator = OperatorTable.getBinaryOperator(token);
121            assert OperatorConventions.NOT_OVERLOADABLE.contains(token);
122            JsExpression left = translateLeftExpression(context(), expression);
123            JsExpression right = translateRightExpression(context(), expression);
124            return new JsBinaryOperation(operator, left, right);
125        }
126    
127    
128        @NotNull
129        private JsExpression translateAsOverloadedBinaryOperation() {
130            ResolvedCall<? extends FunctionDescriptor> resolvedCall = getFunctionResolvedCall(bindingContext(), expression.getOperationReference());
131            JsExpression result = CallTranslator.instance$.translate(context(), resolvedCall, getReceiver());
132            return mayBeWrapWithNegation(result);
133        }
134    
135        @NotNull
136        private JsExpression getReceiver() {
137            if (isInOrNotInOperation(expression)) {
138                return translateRightExpression(context(), expression);
139            } else {
140                return translateLeftExpression(context(), expression);
141            }
142        }
143    
144        @NotNull
145        private JsExpression mayBeWrapWithNegation(@NotNull JsExpression result) {
146            if (isNegatedOperation(expression)) {
147                return not(result);
148            }
149            else {
150                return result;
151            }
152        }
153    }