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