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.utils;
018    
019    import com.google.dart.compiler.backend.js.ast.*;
020    import com.intellij.openapi.util.Pair;
021    import com.intellij.util.SmartList;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
025    import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
026    import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
027    import org.jetbrains.jet.lang.descriptors.PropertyGetterDescriptor;
028    import org.jetbrains.jet.lang.psi.*;
029    import org.jetbrains.k2js.translate.context.TranslationContext;
030    import org.jetbrains.k2js.translate.general.Translation;
031    
032    import java.util.ArrayList;
033    import java.util.Collections;
034    import java.util.List;
035    
036    import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression;
037    import static org.jetbrains.k2js.translate.utils.JsAstUtils.assignment;
038    
039    public final class TranslationUtils {
040        private TranslationUtils() {
041        }
042    
043        @NotNull
044        public static JsPropertyInitializer translateFunctionAsEcma5PropertyDescriptor(@NotNull JsFunction function,
045                @NotNull FunctionDescriptor descriptor,
046                @NotNull TranslationContext context) {
047            if (JsDescriptorUtils.isExtension(descriptor)) {
048                return translateExtensionFunctionAsEcma5DataDescriptor(function, descriptor, context);
049            }
050            else {
051                JsStringLiteral getOrSet = context.program().getStringLiteral(descriptor instanceof PropertyGetterDescriptor ? "get" : "set");
052                return new JsPropertyInitializer(getOrSet, function);
053            }
054        }
055    
056        @NotNull
057        private static JsPropertyInitializer translateExtensionFunctionAsEcma5DataDescriptor(@NotNull JsFunction function,
058                @NotNull FunctionDescriptor descriptor, @NotNull TranslationContext context) {
059            JsObjectLiteral meta = JsAstUtils.createDataDescriptor(function, descriptor.getModality().isOverridable());
060            return new JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), meta);
061        }
062    
063        @NotNull
064        public static JsBinaryOperation isNullCheck(@NotNull JsExpression expressionToCheck) {
065            return nullCheck(expressionToCheck, false);
066        }
067    
068        @NotNull
069        public static JsBinaryOperation isNotNullCheck(@NotNull JsExpression expressionToCheck) {
070            return nullCheck(expressionToCheck, true);
071        }
072    
073        @NotNull
074        public static JsBinaryOperation nullCheck(@NotNull JsExpression expressionToCheck, boolean isNegated) {
075            JsBinaryOperator operator = isNegated ? JsBinaryOperator.NEQ : JsBinaryOperator.EQ;
076            return new JsBinaryOperation(operator, expressionToCheck, JsLiteral.NULL);
077        }
078        
079        @NotNull
080        public static List<JsExpression> translateArgumentList(@NotNull TranslationContext context,
081                @NotNull List<? extends ValueArgument> jetArguments) {
082            if (jetArguments.isEmpty()) {
083                return Collections.emptyList();
084            }
085    
086            List<JsExpression> jsArguments = new SmartList<JsExpression>();
087            for (ValueArgument argument : jetArguments) {
088                jsArguments.add(translateArgument(context, argument));
089            }
090            return jsArguments;
091        }
092    
093        @NotNull
094        private static JsExpression translateArgument(@NotNull TranslationContext context, @NotNull ValueArgument argument) {
095            JetExpression jetExpression = argument.getArgumentExpression();
096            assert jetExpression != null : "Argument with no expression";
097            return Translation.translateAsExpression(jetExpression, context);
098        }
099    
100        @NotNull
101        public static JsNameRef backingFieldReference(@NotNull TranslationContext context,
102                @NotNull PropertyDescriptor descriptor) {
103            JsName backingFieldName = context.getNameForDescriptor(descriptor);
104            return new JsNameRef(backingFieldName, JsLiteral.THIS);
105        }
106    
107        @NotNull
108        public static JsExpression assignmentToBackingField(@NotNull TranslationContext context,
109                @NotNull PropertyDescriptor descriptor,
110                @NotNull JsExpression assignTo) {
111            JsNameRef backingFieldReference = backingFieldReference(context, descriptor);
112            return assignment(backingFieldReference, assignTo);
113        }
114    
115        @Nullable
116        public static JsExpression translateInitializerForProperty(@NotNull JetProperty declaration,
117                @NotNull TranslationContext context) {
118            JsExpression jsInitExpression = null;
119            JetExpression initializer = declaration.getInitializer();
120            if (initializer != null) {
121                jsInitExpression = Translation.translateAsExpression(initializer, context);
122            }
123            return jsInitExpression;
124        }
125    
126        @NotNull
127        public static JsNameRef getQualifiedReference(@NotNull TranslationContext context, @NotNull DeclarationDescriptor descriptor) {
128            return new JsNameRef(context.getNameForDescriptor(descriptor), context.getQualifierForDescriptor(descriptor));
129        }
130    
131        @NotNull
132        public static List<JsExpression> translateExpressionList(@NotNull TranslationContext context,
133                @NotNull List<JetExpression> expressions) {
134            List<JsExpression> result = new ArrayList<JsExpression>();
135            for (JetExpression expression : expressions) {
136                result.add(Translation.translateAsExpression(expression, context));
137            }
138            return result;
139        }
140    
141        @NotNull
142        public static JsExpression translateBaseExpression(@NotNull TranslationContext context,
143                @NotNull JetUnaryExpression expression) {
144            JetExpression baseExpression = PsiUtils.getBaseExpression(expression);
145            return Translation.translateAsExpression(baseExpression, context);
146        }
147    
148        @NotNull
149        public static JsExpression translateLeftExpression(@NotNull TranslationContext context,
150                @NotNull JetBinaryExpression expression) {
151            JetExpression left = expression.getLeft();
152            assert left != null : "Binary expression should have a left expression: " + expression.getText();
153            return Translation.translateAsExpression(left, context);
154        }
155    
156        @NotNull
157        public static JsExpression translateRightExpression(@NotNull TranslationContext context,
158                @NotNull JetBinaryExpression expression) {
159            JetExpression rightExpression = expression.getRight();
160            assert rightExpression != null : "Binary expression should have a right expression";
161            return Translation.translateAsExpression(rightExpression, context);
162        }
163    
164        public static boolean hasCorrespondingFunctionIntrinsic(@NotNull TranslationContext context,
165                @NotNull JetOperationExpression expression) {
166            FunctionDescriptor operationDescriptor = getFunctionDescriptorForOperationExpression(context.bindingContext(), expression);
167    
168            if (operationDescriptor == null) return true;
169            if (context.intrinsics().getFunctionIntrinsics().getIntrinsic(operationDescriptor).exists()) return true;
170    
171            return false;
172        }
173    
174        public static boolean isCacheNeeded(@NotNull JsExpression expression) {
175            return !(expression instanceof JsLiteral) &&
176                   (!(expression instanceof JsNameRef) || ((JsNameRef) expression).getQualifier() != null);
177        }
178    
179        @NotNull
180        public static Pair<JsVars.JsVar, JsExpression> createTemporaryIfNeed(
181                @NotNull JsExpression expression,
182                @NotNull TranslationContext context
183        ) {
184            // don't create temp variable for simple expression
185            if (isCacheNeeded(expression)) {
186                return context.dynamicContext().createTemporary(expression);
187            }
188            else {
189                return Pair.create(null, expression);
190            }
191        }
192    }