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.utils;
018
019import com.google.dart.compiler.backend.js.ast.*;
020import com.intellij.openapi.util.Pair;
021import com.intellij.util.SmartList;
022import org.jetbrains.annotations.NotNull;
023import org.jetbrains.annotations.Nullable;
024import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
025import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
026import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
027import org.jetbrains.jet.lang.descriptors.PropertyGetterDescriptor;
028import org.jetbrains.jet.lang.psi.*;
029import org.jetbrains.k2js.translate.context.TranslationContext;
030import org.jetbrains.k2js.translate.general.Translation;
031
032import java.util.ArrayList;
033import java.util.Collections;
034import java.util.List;
035
036import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression;
037import static org.jetbrains.k2js.translate.utils.JsAstUtils.assignment;
038
039public 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}