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.operation;
018
019 import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
020 import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperation;
021 import org.jetbrains.kotlin.js.backend.ast.JsBinaryOperator;
022 import org.jetbrains.kotlin.js.backend.ast.JsBlock;
023 import org.jetbrains.kotlin.js.backend.ast.JsExpression;
024 import org.jetbrains.annotations.NotNull;
025 import org.jetbrains.kotlin.js.translate.context.TranslationContext;
026 import org.jetbrains.kotlin.js.translate.reference.AccessTranslator;
027 import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
028 import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
029 import org.jetbrains.kotlin.lexer.KtSingleValueToken;
030 import org.jetbrains.kotlin.lexer.KtToken;
031 import org.jetbrains.kotlin.psi.KtBinaryExpression;
032 import org.jetbrains.kotlin.psi.KtExpression;
033 import org.jetbrains.kotlin.types.KotlinType;
034 import org.jetbrains.kotlin.types.expressions.OperatorConventions;
035
036 import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getOperationToken;
037 import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.isAssignment;
038 import static org.jetbrains.kotlin.js.translate.utils.TranslationUtils.isSimpleNameExpressionNotDelegatedLocalVar;
039
040 public final class IntrinsicAssignmentTranslator extends AssignmentTranslator {
041 private final JsExpression right;
042 private final AccessTranslator accessTranslator;
043 private final boolean rightExpressionTrivial;
044 private final JsBlock rightBlock = new JsBlock();
045
046 @NotNull
047 public static JsExpression doTranslate(@NotNull KtBinaryExpression expression, @NotNull TranslationContext context) {
048 return (new IntrinsicAssignmentTranslator(expression, context)).translate();
049 }
050
051 private IntrinsicAssignmentTranslator(@NotNull KtBinaryExpression expression, @NotNull TranslationContext context) {
052 super(expression, context);
053
054 right = translateRightExpression(context, expression);
055 rightExpressionTrivial = rightBlock.isEmpty();
056 KtExpression left = expression.getLeft();
057 assert left != null;
058 accessTranslator = createAccessTranslator(left, !rightExpressionTrivial);
059 }
060
061 private JsExpression translateRightExpression(TranslationContext context, KtBinaryExpression expression) {
062 JsExpression result = TranslationUtils.translateRightExpression(context, expression, rightBlock);
063 KotlinType leftType = context.bindingContext().getType(expression.getLeft());
064 KotlinType rightType = context.bindingContext().getType(expression.getRight());
065 if (rightType != null && KotlinBuiltIns.isCharOrNullableChar(rightType)) {
066 if (leftType != null && KotlinBuiltIns.isStringOrNullableString(leftType)) {
067 result = JsAstUtils.charToString(result);
068 }
069 else if (leftType == null || !KotlinBuiltIns.isCharOrNullableChar(leftType)) {
070 result = JsAstUtils.charToBoxedChar(result);
071 }
072 }
073 return result;
074 }
075
076 @NotNull
077 private JsExpression translate() {
078 if (isAssignment(getOperationToken(expression))) {
079 return translateAsPlainAssignment();
080 }
081 return translateAsAssignmentOperation();
082 }
083
084 @NotNull
085 private JsExpression translateAsAssignmentOperation() {
086 if (isSimpleNameExpressionNotDelegatedLocalVar(expression.getLeft(), context()) && rightExpressionTrivial) {
087 return translateAsPlainAssignmentOperation();
088 }
089 return translateAsAssignToCounterpart();
090 }
091
092 @NotNull
093 private JsExpression translateAsAssignToCounterpart() {
094 JsBinaryOperator operator = getCounterpartOperator();
095 JsExpression oldValue = accessTranslator.translateAsGet();
096 if (!rightExpressionTrivial) {
097 oldValue = context().defineTemporary(oldValue);
098 }
099 JsBinaryOperation counterpartOperation = new JsBinaryOperation(operator, oldValue, right);
100 context().addStatementsToCurrentBlockFrom(rightBlock);
101 return accessTranslator.translateAsSet(counterpartOperation);
102 }
103
104 @NotNull
105 private JsBinaryOperator getCounterpartOperator() {
106 KtToken assignmentOperationToken = getOperationToken(expression);
107 assert assignmentOperationToken instanceof KtSingleValueToken;
108 assert OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(assignmentOperationToken);
109 KtToken counterpartToken = OperatorConventions.ASSIGNMENT_OPERATION_COUNTERPARTS.get(assignmentOperationToken);
110 assert OperatorTable.hasCorrespondingBinaryOperator(counterpartToken) :
111 "Unsupported token encountered: " + counterpartToken.toString();
112 return OperatorTable.getBinaryOperator(counterpartToken);
113 }
114
115 @NotNull
116 private JsExpression translateAsPlainAssignmentOperation() {
117 context().addStatementsToCurrentBlockFrom(rightBlock);
118 JsBinaryOperator operator = getAssignmentOperator();
119 return new JsBinaryOperation(operator, accessTranslator.translateAsGet(), right);
120 }
121
122 @NotNull
123 private JsBinaryOperator getAssignmentOperator() {
124 KtToken token = getOperationToken(expression);
125 assert token instanceof KtSingleValueToken;
126 assert OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(token);
127 assert OperatorTable.hasCorrespondingBinaryOperator(token) :
128 "Unsupported token encountered: " + token.toString();
129 return OperatorTable.getBinaryOperator(token);
130 }
131
132 @NotNull
133 private JsExpression translateAsPlainAssignment() {
134 context().addStatementsToCurrentBlockFrom(rightBlock);
135 return accessTranslator.translateAsSet(right);
136 }
137 }