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