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.annotations.NotNull;
020 import org.jetbrains.annotations.Nullable;
021 import org.jetbrains.kotlin.descriptors.ClassDescriptor;
022 import org.jetbrains.kotlin.descriptors.DeclarationDescriptor;
023 import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
024 import org.jetbrains.kotlin.js.backend.ast.JsExpression;
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, @NotNull TranslationContext context) {
048 if (hasCorrespondingFunctionIntrinsic(context, expression)) {
049 return IntrinsicAssignmentTranslator.doTranslate(expression, context);
050 }
051 return OverloadedAssignmentTranslator.doTranslate(expression, context);
052 }
053
054 @NotNull
055 protected final KtBinaryExpression expression;
056 protected final boolean isVariableReassignment;
057
058 protected AssignmentTranslator(@NotNull KtBinaryExpression expression, @NotNull TranslationContext context) {
059 super(context);
060 this.expression = expression;
061 this.isVariableReassignment = isVariableReassignment(context.bindingContext(), expression);
062 assert expression.getLeft() != null : "No left-hand side: " + expression.getText();
063 }
064
065 protected final AccessTranslator createAccessTranslator(@NotNull KtExpression left, boolean forceOrderOfEvaluation) {
066 if (isReferenceToBackingFieldFromConstructor(left, context())) {
067 KtSimpleNameExpression simpleName = getSimpleName(left);
068 assert simpleName != null;
069 return BackingFieldAccessTranslator.newInstance(simpleName, context());
070 }
071 else {
072 return AccessTranslationUtils.getAccessTranslator(left, context(), forceOrderOfEvaluation);
073 }
074 }
075
076 private static boolean isReferenceToBackingFieldFromConstructor(
077 @NotNull KtExpression expression,
078 @NotNull TranslationContext context
079 ) {
080 if (expression instanceof KtSimpleNameExpression) {
081 KtSimpleNameExpression nameExpression = (KtSimpleNameExpression) expression;
082 DeclarationDescriptor descriptor = getDescriptorForReferenceExpression(context.bindingContext(), nameExpression);
083 return isReferenceToBackingFieldFromConstructor(descriptor, context);
084 }
085 else if (expression instanceof KtDotQualifiedExpression) {
086 KtDotQualifiedExpression qualifiedExpression = (KtDotQualifiedExpression) expression;
087 if (qualifiedExpression.getReceiverExpression() instanceof KtThisExpression &&
088 qualifiedExpression.getSelectorExpression() instanceof KtSimpleNameExpression
089 ) {
090 KtSimpleNameExpression nameExpression = (KtSimpleNameExpression) qualifiedExpression.getSelectorExpression();
091 DeclarationDescriptor descriptor = getDescriptorForReferenceExpression(context.bindingContext(), nameExpression);
092 return isReferenceToBackingFieldFromConstructor(descriptor, context);
093 }
094 }
095 return false;
096 }
097
098 private static boolean isReferenceToBackingFieldFromConstructor(
099 @Nullable DeclarationDescriptor descriptor,
100 @NotNull TranslationContext context
101 ) {
102 if (!(descriptor instanceof PropertyDescriptor)) return false;
103
104 PropertyDescriptor propertyDescriptor = (PropertyDescriptor) descriptor;
105 if (!(context.getDeclarationDescriptor() instanceof ClassDescriptor)) return false;
106
107 ClassDescriptor classDescriptor = (ClassDescriptor) context.getDeclarationDescriptor();
108 if (classDescriptor != propertyDescriptor.getContainingDeclaration()) return false;
109
110 return !propertyDescriptor.isVar();
111 }
112 }