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