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    }