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.util.SmartList;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.k2js.translate.context.Namer;
024    import org.jetbrains.k2js.translate.context.TranslationContext;
025    
026    import java.util.Arrays;
027    import java.util.Collections;
028    import java.util.List;
029    
030    public final class JsAstUtils {
031        private static final JsNameRef DEFINE_PROPERTY = new JsNameRef("defineProperty");
032        public static final JsNameRef CREATE_OBJECT = new JsNameRef("create");
033        private static final JsNameRef EMPTY_REF = new JsNameRef("");
034    
035        private static final JsNameRef VALUE = new JsNameRef("value");
036        private static final JsPropertyInitializer WRITABLE = new JsPropertyInitializer(new JsNameRef("writable"), JsLiteral.TRUE);
037        private static final JsPropertyInitializer ENUMERABLE = new JsPropertyInitializer(new JsNameRef("enumerable"), JsLiteral.TRUE);
038    
039        public static final String LENDS_JS_DOC_TAG = "lends";
040    
041        static {
042            JsNameRef globalObjectReference = new JsNameRef("Object");
043            DEFINE_PROPERTY.setQualifier(globalObjectReference);
044            CREATE_OBJECT.setQualifier(globalObjectReference);
045        }
046    
047        private JsAstUtils() {
048        }
049    
050        @NotNull
051        public static JsStatement convertToStatement(@NotNull JsNode jsNode) {
052            assert (jsNode instanceof JsExpression) || (jsNode instanceof JsStatement)
053                    : "Unexpected node of type: " + jsNode.getClass().toString();
054            if (jsNode instanceof JsExpression) {
055                return ((JsExpression) jsNode).makeStmt();
056            }
057            return (JsStatement) jsNode;
058        }
059    
060        @NotNull
061        public static JsBlock convertToBlock(@NotNull JsNode jsNode) {
062            if (jsNode instanceof JsBlock) {
063                return (JsBlock) jsNode;
064            }
065            return new JsBlock(convertToStatement(jsNode));
066        }
067    
068        @NotNull
069        public static JsExpression convertToExpression(@NotNull JsNode jsNode) {
070            assert jsNode instanceof JsExpression : "Unexpected node of type: " + jsNode.getClass().toString();
071            return (JsExpression) jsNode;
072        }
073    
074        @NotNull
075        public static JsPrefixOperation negated(@NotNull JsExpression expression) {
076            return new JsPrefixOperation(JsUnaryOperator.NOT, expression);
077        }
078    
079        @NotNull
080        public static JsBinaryOperation and(@NotNull JsExpression op1, @NotNull JsExpression op2) {
081            return new JsBinaryOperation(JsBinaryOperator.AND, op1, op2);
082        }
083    
084        @NotNull
085        public static JsBinaryOperation or(@NotNull JsExpression op1, @NotNull JsExpression op2) {
086            return new JsBinaryOperation(JsBinaryOperator.OR, op1, op2);
087        }
088    
089        public static void setQualifier(@NotNull JsExpression selector, @Nullable JsExpression receiver) {
090            assert (selector instanceof JsInvocation || selector instanceof JsNameRef);
091            if (selector instanceof JsInvocation) {
092                setQualifier(((JsInvocation) selector).getQualifier(), receiver);
093                return;
094            }
095            setQualifierForNameRef((JsNameRef) selector, receiver);
096        }
097    
098        private static void setQualifierForNameRef(@NotNull JsNameRef selector, @Nullable JsExpression receiver) {
099            JsExpression qualifier = selector.getQualifier();
100            if (qualifier == null) {
101                selector.setQualifier(receiver);
102            }
103            else {
104                setQualifier(qualifier, receiver);
105            }
106        }
107    
108        @NotNull
109        public static JsBinaryOperation equality(@NotNull JsExpression arg1, @NotNull JsExpression arg2) {
110            return new JsBinaryOperation(JsBinaryOperator.REF_EQ, arg1, arg2);
111        }
112    
113        @NotNull
114        public static JsBinaryOperation inequality(@NotNull JsExpression arg1, @NotNull JsExpression arg2) {
115            return new JsBinaryOperation(JsBinaryOperator.REF_NEQ, arg1, arg2);
116        }
117    
118        @NotNull
119        public static JsBinaryOperation lessThanEq(@NotNull JsExpression arg1, @NotNull JsExpression arg2) {
120            return new JsBinaryOperation(JsBinaryOperator.LTE, arg1, arg2);
121        }
122    
123        @NotNull
124        public static JsExpression assignment(@NotNull JsExpression left, @NotNull JsExpression right) {
125            return new JsBinaryOperation(JsBinaryOperator.ASG, left, right);
126        }
127    
128        @NotNull
129        public static JsBinaryOperation sum(@NotNull JsExpression left, @NotNull JsExpression right) {
130            return new JsBinaryOperation(JsBinaryOperator.ADD, left, right);
131        }
132    
133        @NotNull
134        public static JsBinaryOperation addAssign(@NotNull JsExpression left, @NotNull JsExpression right) {
135            return new JsBinaryOperation(JsBinaryOperator.ASG_ADD, left, right);
136        }
137    
138        @NotNull
139        public static JsBinaryOperation subtract(@NotNull JsExpression left, @NotNull JsExpression right) {
140            return new JsBinaryOperation(JsBinaryOperator.SUB, left, right);
141        }
142    
143        @NotNull
144        public static JsPrefixOperation not(@NotNull JsExpression expression) {
145            return new JsPrefixOperation(JsUnaryOperator.NOT, expression);
146        }
147    
148        @NotNull
149        public static JsBinaryOperation typeof(@NotNull JsExpression expression, @NotNull JsStringLiteral string) {
150            return equality(new JsPrefixOperation(JsUnaryOperator.TYPEOF, expression), string);
151        }
152    
153        @NotNull
154        public static JsVars newVar(@NotNull JsName name, @Nullable JsExpression expr) {
155            return new JsVars(new JsVars.JsVar(name, expr));
156        }
157    
158        public static void setArguments(@NotNull HasArguments invocation, @NotNull List<JsExpression> newArgs) {
159            List<JsExpression> arguments = invocation.getArguments();
160            assert arguments.isEmpty() : "Arguments already set.";
161            arguments.addAll(newArgs);
162        }
163    
164        public static void setArguments(@NotNull HasArguments invocation, JsExpression... arguments) {
165            setArguments(invocation, Arrays.asList(arguments));
166        }
167    
168        public static void setParameters(@NotNull JsFunction function, @NotNull List<JsParameter> newParams) {
169            List<JsParameter> parameters = function.getParameters();
170            assert parameters.isEmpty() : "Arguments already set.";
171            parameters.addAll(newParams);
172        }
173    
174        @NotNull
175        public static JsExpression newSequence(@NotNull List<JsExpression> expressions) {
176            assert !expressions.isEmpty();
177            if (expressions.size() == 1) {
178                return expressions.get(0);
179            }
180            JsExpression result = expressions.get(expressions.size() - 1);
181            for (int i = expressions.size() - 2; i >= 0; i--) {
182                result = new JsBinaryOperation(JsBinaryOperator.COMMA, expressions.get(i), result);
183            }
184            return result;
185        }
186    
187        @NotNull
188        public static JsFunction createFunctionWithEmptyBody(@NotNull JsScope parent) {
189            return new JsFunction(parent, new JsBlock());
190        }
191    
192        @NotNull
193        public static List<JsExpression> toStringLiteralList(@NotNull List<String> strings, @NotNull JsProgram program) {
194            if (strings.isEmpty()) {
195                return Collections.emptyList();
196            }
197    
198            List<JsExpression> result = new SmartList<JsExpression>();
199            for (String str : strings) {
200                result.add(program.getStringLiteral(str));
201            }
202            return result;
203        }
204    
205        @NotNull
206        public static JsInvocation defineProperty(
207                @NotNull String name,
208                @NotNull JsObjectLiteral value,
209                @NotNull TranslationContext context
210        ) {
211            return new JsInvocation(DEFINE_PROPERTY, JsLiteral.THIS, context.program().getStringLiteral(name), value);
212        }
213    
214        @NotNull
215        public static JsStatement defineSimpleProperty(@NotNull String name, @NotNull JsExpression value) {
216            return assignment(new JsNameRef(name, JsLiteral.THIS), value).makeStmt();
217        }
218    
219        @NotNull
220        public static JsObjectLiteral createDataDescriptor(@NotNull JsExpression value, boolean writable, boolean enumerable) {
221            JsObjectLiteral dataDescriptor = new JsObjectLiteral();
222            dataDescriptor.getPropertyInitializers().add(new JsPropertyInitializer(VALUE, value));
223            if (writable) {
224                dataDescriptor.getPropertyInitializers().add(WRITABLE);
225            }
226            if (enumerable) {
227                dataDescriptor.getPropertyInitializers().add(ENUMERABLE);
228            }
229            return dataDescriptor;
230        }
231    
232        @NotNull
233        public static JsFunction createPackage(@NotNull List<JsStatement> to, @NotNull JsScope scope) {
234            JsFunction packageBlockFunction = createFunctionWithEmptyBody(scope);
235            to.add(new JsInvocation(EMPTY_REF, new JsInvocation(packageBlockFunction)).makeStmt());
236            return packageBlockFunction;
237        }
238    
239        @NotNull
240        public static JsObjectLiteral wrapValue(@NotNull JsExpression label, @NotNull JsExpression value) {
241            return new JsObjectLiteral(Collections.singletonList(new JsPropertyInitializer(label, value)));
242        }
243    
244        public static void replaceRootReference(@NotNull JsNameRef fullQualifier, @NotNull JsExpression newQualifier) {
245            JsNameRef qualifier = fullQualifier;
246            while (true) {
247                JsExpression parent = qualifier.getQualifier();
248                assert parent instanceof JsNameRef : "unexpected qualifier: " + parent + ", original: " + fullQualifier;
249                if (((JsNameRef) parent).getQualifier() == null) {
250                    assert Namer.getRootPackageName().equals(((JsNameRef) parent).getIdent());
251                    qualifier.setQualifier(newQualifier);
252                    return;
253                }
254                qualifier = (JsNameRef) parent;
255            }
256        }
257    }