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 com.google.dart.compiler.backend.js.ast.JsExpression;
020 import org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.kotlin.descriptors.ClassDescriptor;
023 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
024 import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
025 import org.jetbrains.kotlin.js.translate.context.TranslationContext;
026 import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
027 import org.jetbrains.kotlin.js.translate.reference.AccessTranslationUtils;
028 import org.jetbrains.kotlin.js.translate.reference.AccessTranslator;
029 import org.jetbrains.kotlin.js.translate.reference.BackingFieldAccessTranslator;
030 import org.jetbrains.kotlin.lexer.KtToken;
031 import org.jetbrains.kotlin.psi.*;
032 import org.jetbrains.kotlin.types.expressions.OperatorConventions;
033
034 import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
035 import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.isVariableReassignment;
036 import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getSimpleName;
037 import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.isAssignment;
038 import static org.jetbrains.kotlin.js.translate.utils.TranslationUtils.hasCorrespondingFunctionIntrinsic;
039
040 public abstract class AssignmentTranslator extends AbstractTranslator {
041
042 public static boolean isAssignmentOperator(KtToken operationToken) {
043 return (OperatorConventions.ASSIGNMENT_OPERATIONS.keySet().contains(operationToken) || isAssignment(operationToken));
044 }
045
046 @NotNull
047 public static JsExpression translate(@NotNull KtBinaryExpression expression,
048 @NotNull TranslationContext context) {
049 if (hasCorrespondingFunctionIntrinsic(context, expression)) {
050 return IntrinsicAssignmentTranslator.doTranslate(expression, context);
051 }
052 return OverloadedAssignmentTranslator.doTranslate(expression, context);
053 }
054
055 @NotNull
056 protected final KtBinaryExpression expression;
057 protected final boolean isVariableReassignment;
058
059 protected AssignmentTranslator(@NotNull KtBinaryExpression expression,
060 @NotNull TranslationContext context) {
061 super(context);
062 this.expression = expression;
063 this.isVariableReassignment = isVariableReassignment(context.bindingContext(), expression);
064 KtExpression left = expression.getLeft();
065 assert left != null : "No left-hand side: " + expression.getText();
066 }
067
068 protected final AccessTranslator createAccessTranslator(KtExpression left, boolean forceOrderOfEvaluation) {
069 if (isReferenceToBackingFieldFromConstructor(left, context())) {
070 KtSimpleNameExpression simpleName = getSimpleName(left);
071 assert simpleName != null;
072 return BackingFieldAccessTranslator.newInstance(simpleName, context());
073 } else {
074 return AccessTranslationUtils.getAccessTranslator(left, context(), forceOrderOfEvaluation);
075 }
076 }
077
078 private static boolean isReferenceToBackingFieldFromConstructor(
079 @NotNull KtExpression expression,
080 @NotNull TranslationContext context
081 ) {
082 if (expression instanceof KtSimpleNameExpression) {
083 KtSimpleNameExpression nameExpression = (KtSimpleNameExpression) expression;
084 DeclarationDescriptor descriptor = getDescriptorForReferenceExpression(context.bindingContext(), nameExpression);
085 return isReferenceToBackingFieldFromConstructor(descriptor, context);
086 }
087 else if (expression instanceof KtDotQualifiedExpression) {
088 KtDotQualifiedExpression qualifiedExpression = (KtDotQualifiedExpression) expression;
089 if (qualifiedExpression.getReceiverExpression() instanceof KtThisExpression &&
090 qualifiedExpression.getSelectorExpression() instanceof KtSimpleNameExpression) {
091 KtSimpleNameExpression nameExpression = (KtSimpleNameExpression) qualifiedExpression.getSelectorExpression();
092 DeclarationDescriptor descriptor = getDescriptorForReferenceExpression(context.bindingContext(), nameExpression);
093 return isReferenceToBackingFieldFromConstructor(descriptor, context);
094 }
095 }
096 return false;
097 }
098
099 private static boolean isReferenceToBackingFieldFromConstructor(
100 @Nullable DeclarationDescriptor descriptor,
101 @NotNull TranslationContext context
102 ) {
103 if (!(descriptor instanceof PropertyDescriptor)) {
104 return false;
105 }
106 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
107
108 if (!(context.getDeclarationDescriptor() instanceof ClassDescriptor)) {
109 return false;
110 }
111 ClassDescriptor classDescriptor = (ClassDescriptor) context.getDeclarationDescriptor();
112
113 if (classDescriptor != propertyDescriptor.getContainingDeclaration()) {
114 return false;
115 }
116
117 return !propertyDescriptor.isVar();
118 }
119 }