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