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.JetExpression;
032    import org.jetbrains.kotlin.psi.JetUnaryExpression;
033    import org.jetbrains.kotlin.resolve.calls.tasks.TasksPackage;
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 JetUnaryExpression 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 JetUnaryExpression expression;
065        @NotNull
066        protected final CachedAccessTranslator accessTranslator;
067    
068        protected IncrementTranslator(@NotNull JetUnaryExpression expression,
069                                      @NotNull TranslationContext context) {
070            super(context);
071            this.expression = expression;
072            JetExpression 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, JetUnaryExpression expression) {
130            CallableDescriptor operationDescriptor = getCallableDescriptorForOperationExpression(context.bindingContext(), expression);
131            return TasksPackage.isDynamic(operationDescriptor);
132        }
133    }