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 017package org.jetbrains.k2js.translate.operation; 018 019import com.google.dart.compiler.backend.js.ast.*; 020import org.jetbrains.annotations.NotNull; 021import org.jetbrains.annotations.Nullable; 022import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; 023import org.jetbrains.jet.lang.psi.JetBinaryExpression; 024import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall; 025import org.jetbrains.jet.lang.types.expressions.OperatorConventions; 026import org.jetbrains.jet.lexer.JetToken; 027import org.jetbrains.jet.lexer.JetTokens; 028import org.jetbrains.k2js.translate.context.TranslationContext; 029import org.jetbrains.k2js.translate.general.AbstractTranslator; 030import org.jetbrains.k2js.translate.intrinsic.operation.BinaryOperationIntrinsic; 031import org.jetbrains.k2js.translate.reference.CallBuilder; 032import org.jetbrains.k2js.translate.reference.CallType; 033import org.jetbrains.k2js.translate.utils.JsAstUtils; 034 035import static org.jetbrains.k2js.translate.operation.AssignmentTranslator.isAssignmentOperator; 036import static org.jetbrains.k2js.translate.operation.CompareToTranslator.isCompareToCall; 037import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression; 038import static org.jetbrains.k2js.translate.utils.BindingUtils.getResolvedCall; 039import static org.jetbrains.k2js.translate.utils.JsAstUtils.not; 040import static org.jetbrains.k2js.translate.utils.PsiUtils.*; 041import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateLeftExpression; 042import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateRightExpression; 043 044 045public 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}