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.common.collect.Lists;
020 import com.google.dart.compiler.backend.js.ast.JsBinaryOperation;
021 import com.google.dart.compiler.backend.js.ast.JsBinaryOperator;
022 import com.google.dart.compiler.backend.js.ast.JsExpression;
023 import com.google.dart.compiler.util.AstUtil;
024 import com.intellij.psi.tree.IElementType;
025 import org.jetbrains.annotations.NotNull;
026 import org.jetbrains.kotlin.descriptors.CallableDescriptor;
027 import org.jetbrains.kotlin.js.translate.context.TemporaryVariable;
028 import org.jetbrains.kotlin.js.translate.context.TranslationContext;
029 import org.jetbrains.kotlin.js.translate.general.AbstractTranslator;
030 import org.jetbrains.kotlin.js.translate.reference.CachedAccessTranslator;
031 import org.jetbrains.kotlin.psi.KtExpression;
032 import org.jetbrains.kotlin.psi.KtUnaryExpression;
033 import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt;
034 import org.jetbrains.kotlin.types.expressions.OperatorConventions;
035
036 import java.util.List;
037
038 import static org.jetbrains.kotlin.js.translate.reference.AccessTranslationUtils.getCachedAccessTranslator;
039 import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
040 import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.newSequence;
041 import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getBaseExpression;
042 import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.isPrefix;
043 import static org.jetbrains.kotlin.js.translate.utils.TemporariesUtils.temporariesInitialization;
044 import static org.jetbrains.kotlin.js.translate.utils.TranslationUtils.hasCorrespondingFunctionIntrinsic;
045
046 // TODO: provide better increment translator logic
047 public abstract class IncrementTranslator extends AbstractTranslator {
048
049 public static boolean isIncrement(IElementType operationToken) {
050 //noinspection SuspiciousMethodCalls
051 return OperatorConventions.INCREMENT_OPERATIONS.contains(operationToken);
052 }
053
054 @NotNull
055 public static JsExpression translate(@NotNull KtUnaryExpression expression,
056 @NotNull TranslationContext context) {
057 if (hasCorrespondingFunctionIntrinsic(context, expression) || isDynamic(context, expression)) {
058 return IntrinsicIncrementTranslator.doTranslate(expression, context);
059 }
060 return (new OverloadedIncrementTranslator(expression, context)).translateIncrementExpression();
061 }
062
063 @NotNull
064 protected final KtUnaryExpression expression;
065 @NotNull
066 protected final CachedAccessTranslator accessTranslator;
067
068 protected IncrementTranslator(@NotNull KtUnaryExpression expression,
069 @NotNull TranslationContext context) {
070 super(context);
071 this.expression = expression;
072 KtExpression baseExpression = getBaseExpression(expression);
073 this.accessTranslator = getCachedAccessTranslator(baseExpression, context());
074 }
075
076 @NotNull
077 protected JsExpression translateIncrementExpression() {
078 return withTemporariesInitialized(doTranslateIncrementExpression());
079 }
080
081 @NotNull
082 private JsExpression doTranslateIncrementExpression() {
083 if (isPrefix(expression)) {
084 return asPrefix();
085 }
086 return asPostfix();
087 }
088
089 //TODO: decide if this expression can be optimised in case of direct access (not property)
090 @NotNull
091 private JsExpression asPrefix() {
092 // code fragment: expr(a++)
093 // generate: expr(a = a.inc(), a)
094 JsExpression getExpression = accessTranslator.translateAsGet();
095 JsExpression reassignment = variableReassignment(getExpression);
096 JsExpression getNewValue = accessTranslator.translateAsGet();
097 return new JsBinaryOperation(JsBinaryOperator.COMMA, reassignment, getNewValue);
098 }
099
100 //TODO: decide if this expression can be optimised in case of direct access (not property)
101 @NotNull
102 private JsExpression asPostfix() {
103 // code fragment: expr(a++)
104 // generate: expr( (t1 = a, t2 = t1, a = t1.inc(), t2) )
105 TemporaryVariable t1 = context().declareTemporary(accessTranslator.translateAsGet());
106 TemporaryVariable t2 = context().declareTemporary(t1.reference());
107 JsExpression variableReassignment = variableReassignment(t1.reference());
108 return AstUtil.newSequence(t1.assignmentExpression(), t2.assignmentExpression(),
109 variableReassignment, t2.reference());
110 }
111
112 @NotNull
113 private JsExpression variableReassignment(@NotNull JsExpression toCallMethodUpon) {
114 JsExpression overloadedMethodCallOnPropertyGetter = operationExpression(toCallMethodUpon);
115 return accessTranslator.translateAsSet(overloadedMethodCallOnPropertyGetter);
116 }
117
118 @NotNull
119 private JsExpression withTemporariesInitialized(@NotNull JsExpression expression) {
120 List<TemporaryVariable> temporaries = accessTranslator.declaredTemporaries();
121 List<JsExpression> expressions = Lists.newArrayList(temporariesInitialization(temporaries));
122 expressions.add(expression);
123 return newSequence(expressions);
124 }
125
126 @NotNull
127 abstract JsExpression operationExpression(@NotNull JsExpression receiver);
128
129 private static boolean isDynamic(TranslationContext context, KtUnaryExpression expression) {
130 CallableDescriptor operationDescriptor = getCallableDescriptorForOperationExpression(context.bindingContext(), expression);
131 return DynamicCallsKt.isDynamic(operationDescriptor);
132 }
133 }