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