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.PropertyDescriptor;
026 import org.jetbrains.k2js.translate.context.TranslationContext;
027
028 import java.util.Arrays;
029 import java.util.Collections;
030 import java.util.List;
031
032 public final class JsAstUtils {
033 private static final JsNameRef DEFINE_PROPERTY = new JsNameRef("defineProperty");
034 public static final JsNameRef CREATE_OBJECT = new JsNameRef("create");
035 private static final JsNameRef EMPTY_REF = new JsNameRef("");
036
037 private static final JsNameRef VALUE = new JsNameRef("value");
038 private static final JsPropertyInitializer WRITABLE = new JsPropertyInitializer(new JsNameRef("writable"), JsLiteral.TRUE);
039 private static final JsPropertyInitializer ENUMERABLE = new JsPropertyInitializer(new JsNameRef("enumerable"), JsLiteral.TRUE);
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 JsFor generateForExpression(@NotNull JsVars initExpression,
155 @NotNull JsExpression condition,
156 @NotNull JsExpression incrementExpression,
157 @NotNull JsStatement body) {
158 JsFor result = new JsFor(initExpression, condition, incrementExpression);
159 result.setBody(body);
160 return result;
161 }
162
163 @NotNull
164 public static JsVars newVar(@NotNull JsName name, @Nullable JsExpression expr) {
165 return new JsVars(new JsVars.JsVar(name, expr));
166 }
167
168 public static void setArguments(@NotNull JsInvocation invocation, @NotNull List<JsExpression> newArgs) {
169 List<JsExpression> arguments = invocation.getArguments();
170 assert arguments.isEmpty() : "Arguments already set.";
171 arguments.addAll(newArgs);
172 }
173
174 public static void setArguments(@NotNull HasArguments invocation, @NotNull List<JsExpression> newArgs) {
175 List<JsExpression> arguments = invocation.getArguments();
176 assert arguments.isEmpty() : "Arguments already set.";
177 arguments.addAll(newArgs);
178 }
179
180 public static void setArguments(@NotNull HasArguments invocation, JsExpression... arguments) {
181 setArguments(invocation, Arrays.asList(arguments));
182 }
183
184 public static void setParameters(@NotNull JsFunction function, @NotNull List<JsParameter> newParams) {
185 List<JsParameter> parameters = function.getParameters();
186 assert parameters.isEmpty() : "Arguments already set.";
187 parameters.addAll(newParams);
188 }
189
190 @NotNull
191 public static JsExpression newSequence(@NotNull List<JsExpression> expressions) {
192 assert !expressions.isEmpty();
193 if (expressions.size() == 1) {
194 return expressions.get(0);
195 }
196 JsExpression result = expressions.get(expressions.size() - 1);
197 for (int i = expressions.size() - 2; i >= 0; i--) {
198 result = new JsBinaryOperation(JsBinaryOperator.COMMA, expressions.get(i), result);
199 }
200 return result;
201 }
202
203 @NotNull
204 public static JsFunction createFunctionWithEmptyBody(@NotNull JsScope parent) {
205 return new JsFunction(parent, new JsBlock());
206 }
207
208 @NotNull
209 public static List<JsExpression> toStringLiteralList(@NotNull List<String> strings, @NotNull JsProgram program) {
210 if (strings.isEmpty()) {
211 return Collections.emptyList();
212 }
213
214 List<JsExpression> result = new SmartList<JsExpression>();
215 for (String str : strings) {
216 result.add(program.getStringLiteral(str));
217 }
218 return result;
219 }
220
221 @NotNull
222 public static JsInvocation definePropertyDataDescriptor(@NotNull PropertyDescriptor descriptor,
223 @NotNull JsExpression value,
224 @NotNull TranslationContext context) {
225 return defineProperty(context.getNameForDescriptor(descriptor).getIdent(), createPropertyDataDescriptor(descriptor, value),
226 context);
227 }
228
229 @NotNull
230 public static JsInvocation defineProperty(@NotNull String name,
231 @NotNull JsObjectLiteral value,
232 @NotNull TranslationContext context) {
233 JsInvocation invocation = new JsInvocation(DEFINE_PROPERTY);
234 invocation.getArguments().add(JsLiteral.THIS);
235 invocation.getArguments().add(context.program().getStringLiteral(name));
236 invocation.getArguments().add(value);
237 return invocation;
238 }
239
240 @NotNull
241 public static JsObjectLiteral createPropertyDataDescriptor(@NotNull FunctionDescriptor descriptor,
242 @NotNull JsExpression value) {
243 return createPropertyDataDescriptor(descriptor.getModality().isOverridable(), descriptor, value);
244 }
245
246 @NotNull
247 public static JsObjectLiteral createDataDescriptor(@NotNull JsExpression value) {
248 return createDataDescriptor(value, false);
249 }
250
251 @NotNull
252 public static JsObjectLiteral createDataDescriptor(@NotNull JsExpression value, boolean writable) {
253 JsObjectLiteral dataDescriptor = new JsObjectLiteral();
254 dataDescriptor.getPropertyInitializers().add(new JsPropertyInitializer(VALUE, value));
255 if (writable) {
256 dataDescriptor.getPropertyInitializers().add(WRITABLE);
257 }
258 return dataDescriptor;
259 }
260
261 @NotNull
262 public static JsObjectLiteral createPropertyDataDescriptor(@NotNull PropertyDescriptor descriptor,
263 @NotNull JsExpression value) {
264 return createPropertyDataDescriptor(descriptor.isVar(), descriptor, value);
265 }
266
267 @NotNull
268 private static JsObjectLiteral createPropertyDataDescriptor(boolean writable,
269 @NotNull DeclarationDescriptor descriptor,
270 @NotNull JsExpression value) {
271 JsObjectLiteral dataDescriptor = createDataDescriptor(value, writable);
272 if (AnnotationsUtils.isEnumerable(descriptor)) {
273 dataDescriptor.getPropertyInitializers().add(ENUMERABLE);
274 }
275 return dataDescriptor;
276 }
277
278 @NotNull
279 public static JsFunction createPackage(@NotNull List<JsStatement> to, @NotNull JsScope scope) {
280 JsFunction packageBlockFunction = createFunctionWithEmptyBody(scope);
281 to.add(new JsInvocation(EMPTY_REF, new JsInvocation(packageBlockFunction)).makeStmt());
282 return packageBlockFunction;
283 }
284 }