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