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 }