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.expression;
018
019 import com.google.dart.compiler.backend.js.ast.*;
020 import com.google.dart.compiler.backend.js.ast.metadata.MetadataPackage;
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.VariableDescriptor;
026 import org.jetbrains.jet.lang.psi.*;
027 import org.jetbrains.jet.lang.resolve.BindingContext;
028 import org.jetbrains.jet.lang.resolve.BindingContextUtils;
029 import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage;
030 import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
031 import org.jetbrains.jet.lang.resolve.constants.NullValue;
032 import org.jetbrains.jet.lang.types.JetType;
033 import org.jetbrains.jet.lang.types.TypeUtils;
034 import org.jetbrains.jet.lang.types.lang.InlineUtil;
035 import org.jetbrains.jet.lexer.JetTokens;
036 import org.jetbrains.k2js.translate.context.TemporaryVariable;
037 import org.jetbrains.k2js.translate.context.TranslationContext;
038 import org.jetbrains.k2js.translate.declaration.ClassTranslator;
039 import org.jetbrains.k2js.translate.expression.loopTranslator.LoopTranslatorPackage;
040 import org.jetbrains.k2js.translate.general.Translation;
041 import org.jetbrains.k2js.translate.general.TranslatorVisitor;
042 import org.jetbrains.k2js.translate.operation.BinaryOperationTranslator;
043 import org.jetbrains.k2js.translate.operation.UnaryOperationTranslator;
044 import org.jetbrains.k2js.translate.reference.*;
045 import org.jetbrains.k2js.translate.utils.JsAstUtils;
046 import org.jetbrains.k2js.translate.utils.TranslationUtils;
047
048 import java.util.List;
049
050 import static org.jetbrains.jet.lang.resolve.BindingContextUtils.isVarCapturedInClosure;
051 import static org.jetbrains.k2js.translate.context.Namer.getCapturedVarAccessor;
052 import static org.jetbrains.k2js.translate.general.Translation.translateAsExpression;
053 import static org.jetbrains.k2js.translate.reference.CallExpressionTranslator.shouldBeInlined;
054 import static org.jetbrains.k2js.translate.reference.ReferenceTranslator.translateAsFQReference;
055 import static org.jetbrains.k2js.translate.utils.BindingUtils.*;
056 import static org.jetbrains.k2js.translate.utils.ErrorReportingUtils.message;
057 import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToStatement;
058 import static org.jetbrains.k2js.translate.utils.JsAstUtils.newVar;
059 import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getReceiverParameterForDeclaration;
060 import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateInitializerForProperty;
061
062 public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
063 @Override
064 @NotNull
065 public JsNode visitConstantExpression(@NotNull JetConstantExpression expression, @NotNull TranslationContext context) {
066 return translateConstantExpression(expression, context).source(expression);
067 }
068
069 @NotNull
070 private static JsNode translateConstantExpression(@NotNull JetConstantExpression expression, @NotNull TranslationContext context) {
071 CompileTimeConstant<?> compileTimeValue = context.bindingContext().get(BindingContext.COMPILE_TIME_VALUE, expression);
072
073 assert compileTimeValue != null : message(expression, "Expression is not compile time value: " + expression.getText() + " ");
074
075 if (compileTimeValue instanceof NullValue) {
076 return JsLiteral.NULL;
077 }
078
079 Object value = getCompileTimeValue(context.bindingContext(), expression, compileTimeValue);
080 if (value instanceof Integer || value instanceof Short || value instanceof Byte) {
081 return context.program().getNumberLiteral(((Number) value).intValue());
082 }
083 else if (value instanceof Long) {
084 return JsAstUtils.newLong((Long) value, context);
085 }
086 else if (value instanceof Number) {
087 return context.program().getNumberLiteral(((Number) value).doubleValue());
088 }
089 else if (value instanceof Boolean) {
090 return JsLiteral.getBoolean((Boolean) value);
091 }
092
093 //TODO: test
094 if (value instanceof String) {
095 return context.program().getStringLiteral((String) value);
096 }
097 if (value instanceof Character) {
098 return context.program().getStringLiteral(value.toString());
099 }
100
101 throw new AssertionError(message(expression, "Unsupported constant expression: " + expression.getText() + " "));
102 }
103
104 @Override
105 @NotNull
106 public JsNode visitBlockExpression(@NotNull JetBlockExpression jetBlock, @NotNull TranslationContext context) {
107 List<JetElement> statements = jetBlock.getStatements();
108 JsBlock jsBlock = new JsBlock();
109 for (JetElement statement : statements) {
110 assert statement instanceof JetExpression : "Elements in JetBlockExpression " +
111 "should be of type JetExpression";
112 JsNode jsNode = Translation.translateExpression((JetExpression)statement, context, jsBlock);
113 JsStatement jsStatement = convertToStatement(jsNode);
114 if (!JsAstUtils.isEmptyStatement(jsStatement)) {
115 jsBlock.getStatements().add(jsStatement);
116 }
117 }
118 return jsBlock;
119 }
120
121 @Override
122 public JsNode visitMultiDeclaration(@NotNull JetMultiDeclaration multiDeclaration, @NotNull TranslationContext context) {
123 JetExpression jetInitializer = multiDeclaration.getInitializer();
124 assert jetInitializer != null : "Initializer for multi declaration must be not null";
125 JsExpression initializer = Translation.translateAsExpression(jetInitializer, context);
126 return MultiDeclarationTranslator.translate(multiDeclaration, context.scope().declareTemporary(), initializer, context);
127 }
128
129 @Override
130 @NotNull
131 public JsNode visitReturnExpression(@NotNull JetReturnExpression jetReturnExpression,
132 @NotNull TranslationContext context) {
133 JetExpression returned = jetReturnExpression.getReturnedExpression();
134 if (returned == null) {
135 return new JsReturn(null).source(jetReturnExpression);
136 }
137 JsExpression jsReturnExpression = translateAsExpression(returned, context);
138 if (JsAstUtils.isEmptyExpression(jsReturnExpression)) {
139 return context.getEmptyExpression();
140 }
141 return new JsReturn(jsReturnExpression).source(jetReturnExpression);
142 }
143
144 @Override
145 @NotNull
146 public JsNode visitParenthesizedExpression(@NotNull JetParenthesizedExpression expression,
147 @NotNull TranslationContext context) {
148 JetExpression expressionInside = expression.getExpression();
149 if (expressionInside != null) {
150 return Translation.translateExpression(expressionInside, context);
151 }
152 return context.getEmptyStatement();
153 }
154
155 @Override
156 @NotNull
157 public JsNode visitBinaryExpression(@NotNull JetBinaryExpression expression,
158 @NotNull TranslationContext context) {
159 return BinaryOperationTranslator.translate(expression, context);
160 }
161
162 @Override
163 @NotNull
164 // assume it is a local variable declaration
165 public JsNode visitProperty(@NotNull JetProperty expression, @NotNull TranslationContext context) {
166 VariableDescriptor descriptor = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.VARIABLE, expression);
167 JsExpression initializer = translateInitializerForProperty(expression, context);
168 if (initializer != null && JsAstUtils.isEmptyExpression(initializer)) {
169 return context.getEmptyExpression();
170 }
171
172 JsName name = context.getNameForDescriptor(descriptor);
173 if (isVarCapturedInClosure(context.bindingContext(), descriptor)) {
174 JsNameRef alias = getCapturedVarAccessor(name.makeRef());
175 initializer = JsAstUtils.wrapValue(alias, initializer == null ? JsLiteral.NULL : initializer);
176 }
177
178 return newVar(name, initializer).source(expression);
179 }
180
181 @Override
182 @NotNull
183 public JsNode visitCallableReferenceExpression(@NotNull JetCallableReferenceExpression expression, @NotNull TranslationContext context) {
184 return CallableReferenceTranslator.INSTANCE$.translate(expression, context);
185 }
186
187 @Override
188 @NotNull
189 public JsNode visitCallExpression(@NotNull JetCallExpression expression,
190 @NotNull TranslationContext context) {
191 if (shouldBeInlined(expression, context) &&
192 BindingContextUtilPackage.isUsedAsExpression(expression, context.bindingContext())) {
193 TemporaryVariable temporaryVariable = context.declareTemporary(null);
194 JsExpression callResult = CallExpressionTranslator.translate(expression, null, context).source(expression);
195 context.addStatementToCurrentBlock(JsAstUtils.assignment(temporaryVariable.reference(), callResult).makeStmt());
196 return temporaryVariable.reference();
197 } else {
198 return CallExpressionTranslator.translate(expression, null, context).source(expression);
199 }
200 }
201
202 @Override
203 @NotNull
204 public JsNode visitIfExpression(@NotNull JetIfExpression expression, @NotNull TranslationContext context) {
205 assert expression.getCondition() != null : "condition should not ne null: " + expression.getText();
206 JsExpression testExpression = Translation.translateAsExpression(expression.getCondition(), context);
207 if (JsAstUtils.isEmptyExpression(testExpression)) {
208 return testExpression;
209 }
210
211 boolean isKotlinExpression = BindingContextUtilPackage.isUsedAsExpression(expression, context.bindingContext());
212
213 JetExpression thenExpression = expression.getThen();
214 assert thenExpression != null : "then expression should not be null: " + expression.getText();
215 JetExpression elseExpression = expression.getElse();
216
217 JsStatement thenStatement = Translation.translateAsStatementAndMergeInBlockIfNeeded(thenExpression, context);
218 JsStatement elseStatement = (elseExpression != null) ? Translation.translateAsStatementAndMergeInBlockIfNeeded(elseExpression,
219 context) : null;
220
221 if (isKotlinExpression) {
222 JsExpression jsThenExpression = JsAstUtils.extractExpressionFromStatement(thenStatement);
223 JsExpression jsElseExpression = JsAstUtils.extractExpressionFromStatement(elseStatement);
224 boolean canBeJsExpression = jsThenExpression != null && jsElseExpression != null;
225 if (canBeJsExpression) {
226 return new JsConditional(testExpression, jsThenExpression, jsElseExpression).source(expression);
227 }
228 }
229 JsIf ifStatement = new JsIf(testExpression, thenStatement, elseStatement);
230 return ifStatement.source(expression);
231 }
232
233 @Override
234 @NotNull
235 public JsExpression visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression,
236 @NotNull TranslationContext context) {
237 return ReferenceTranslator.translateSimpleNameWithQualifier(expression, null, context).source(expression);
238 }
239
240 @Override
241 @NotNull
242 public JsNode visitWhileExpression(@NotNull JetWhileExpression expression, @NotNull TranslationContext context) {
243 return LoopTranslatorPackage.createWhile(false, expression, context);
244 }
245
246 @Override
247 @NotNull
248 public JsNode visitDoWhileExpression(@NotNull JetDoWhileExpression expression, @NotNull TranslationContext context) {
249 return LoopTranslatorPackage.createWhile(true, expression, context);
250 }
251
252 @Override
253 @NotNull
254 public JsNode visitStringTemplateExpression(@NotNull JetStringTemplateExpression expression,
255 @NotNull TranslationContext context) {
256 JsStringLiteral stringLiteral = resolveAsStringConstant(expression, context);
257 if (stringLiteral != null) {
258 return stringLiteral;
259 }
260 return resolveAsTemplate(expression, context).source(expression);
261 }
262
263 @NotNull
264 private static JsNode resolveAsTemplate(@NotNull JetStringTemplateExpression expression,
265 @NotNull TranslationContext context) {
266 return StringTemplateTranslator.translate(expression, context);
267 }
268
269 @Nullable
270 private static JsStringLiteral resolveAsStringConstant(@NotNull JetExpression expression,
271 @NotNull TranslationContext context) {
272 Object value = getCompileTimeValue(context.bindingContext(), expression);
273 if (value == null) {
274 return null;
275 }
276 assert value instanceof String : "Compile time constant template should be a String constant.";
277 String constantString = (String) value;
278 return context.program().getStringLiteral(constantString);
279 }
280
281 @Override
282 @NotNull
283 public JsNode visitDotQualifiedExpression(@NotNull JetDotQualifiedExpression expression,
284 @NotNull TranslationContext context) {
285 return QualifiedExpressionTranslator.translateQualifiedExpression(expression, context);
286 }
287
288 @Override
289 public JsNode visitLabeledExpression(
290 @NotNull JetLabeledExpression expression, TranslationContext context
291 ) {
292 JetExpression baseExpression = expression.getBaseExpression();
293 assert baseExpression != null;
294 JsScope scope = context.scope();
295 assert scope instanceof JsFunctionScope: "Labeled statement is unexpected outside of function scope";
296 JsName name = ((JsFunctionScope) scope).declareNameUnsafe(getReferencedName(expression.getTargetLabel()));
297 JsStatement baseStatement = Translation.translateAsStatement(baseExpression, context);
298 return new JsLabel(name, baseStatement).source(expression);
299 }
300
301 @Override
302 @NotNull
303 public JsNode visitPrefixExpression(
304 @NotNull JetPrefixExpression expression,
305 @NotNull TranslationContext context
306 ) {
307 return UnaryOperationTranslator.translate(expression, context).source(expression);
308 }
309
310 @Override
311 @NotNull
312 public JsNode visitPostfixExpression(@NotNull JetPostfixExpression expression,
313 @NotNull TranslationContext context) {
314 return UnaryOperationTranslator.translate(expression, context).source(expression);
315 }
316
317 @Override
318 @NotNull
319 public JsNode visitIsExpression(@NotNull JetIsExpression expression,
320 @NotNull TranslationContext context) {
321 return Translation.patternTranslator(context).translateIsExpression(expression);
322 }
323
324 @Override
325 @NotNull
326 public JsNode visitSafeQualifiedExpression(@NotNull JetSafeQualifiedExpression expression,
327 @NotNull TranslationContext context) {
328 return QualifiedExpressionTranslator.translateQualifiedExpression(expression, context).source(expression);
329 }
330
331 @Override
332 @Nullable
333 public JsNode visitWhenExpression(@NotNull JetWhenExpression expression,
334 @NotNull TranslationContext context) {
335 return WhenTranslator.translate(expression, context);
336 }
337
338 @Override
339 @NotNull
340 public JsNode visitBinaryWithTypeRHSExpression(@NotNull JetBinaryExpressionWithTypeRHS expression,
341 @NotNull TranslationContext context) {
342 JsExpression jsExpression = Translation.translateAsExpression(expression.getLeft(), context);
343
344 if (expression.getOperationReference().getReferencedNameElementType() != JetTokens.AS_KEYWORD)
345 return jsExpression.source(expression);
346
347 JetTypeReference right = expression.getRight();
348 assert right != null;
349
350 JetType rightType = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.TYPE, right);
351 JetType leftType = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.EXPRESSION_TYPE, expression.getLeft());
352 if (TypeUtils.isNullableType(rightType) || !TypeUtils.isNullableType(leftType)) {
353 return jsExpression.source(expression);
354 }
355
356 // KT-2670
357 // we actually do not care for types in js
358 return TranslationUtils.sure(jsExpression, context).source(expression);
359 }
360
361 private static String getReferencedName(JetSimpleNameExpression expression) {
362 return expression.getReferencedName()
363 .replaceAll("^@", "")
364 .replaceAll("(?:^`(.*)`$)", "$1") + "$";
365 }
366
367 private static JsNameRef getTargetLabel(JetExpressionWithLabel expression, TranslationContext context) {
368 JetSimpleNameExpression labelElement = expression.getTargetLabel();
369 if (labelElement == null) {
370 return null;
371 }
372 else {
373 JsName name = context.scope().findName(getReferencedName(labelElement));
374 assert name != null;
375 return name.makeRef();
376 }
377 }
378
379 @Override
380 @NotNull
381 public JsNode visitBreakExpression(@NotNull JetBreakExpression expression,
382 @NotNull TranslationContext context) {
383 return new JsBreak(getTargetLabel(expression, context)).source(expression);
384 }
385
386 @Override
387 @NotNull
388 public JsNode visitContinueExpression(@NotNull JetContinueExpression expression,
389 @NotNull TranslationContext context) {
390 return new JsContinue(getTargetLabel(expression, context)).source(expression);
391 }
392
393 @Override
394 @NotNull
395 public JsNode visitFunctionLiteralExpression(@NotNull JetFunctionLiteralExpression expression, @NotNull TranslationContext context) {
396 return new LiteralFunctionTranslator(context).translate(expression.getFunctionLiteral());
397 }
398
399 @Override
400 @NotNull
401 public JsNode visitNamedFunction(@NotNull JetNamedFunction expression, @NotNull TranslationContext context) {
402 JsExpression alias = new LiteralFunctionTranslator(context).translate(expression);
403
404 FunctionDescriptor descriptor = getFunctionDescriptor(context.bindingContext(), expression);
405 JsName name = context.getNameForDescriptor(descriptor);
406 if (InlineUtil.getInlineType(descriptor).isInline()) {
407 MetadataPackage.setStaticRef(name, alias);
408 }
409
410 return new JsVars(new JsVars.JsVar(name, alias)).source(expression);
411 }
412
413 @Override
414 @NotNull
415 public JsNode visitThisExpression(@NotNull JetThisExpression expression, @NotNull TranslationContext context) {
416 DeclarationDescriptor thisExpression =
417 getDescriptorForReferenceExpression(context.bindingContext(), expression.getInstanceReference());
418 assert thisExpression != null : "This expression must reference a descriptor: " + expression.getText();
419
420 return context.getDispatchReceiver(getReceiverParameterForDeclaration(thisExpression)).source(expression);
421 }
422
423 @Override
424 @NotNull
425 public JsNode visitArrayAccessExpression(@NotNull JetArrayAccessExpression expression,
426 @NotNull TranslationContext context) {
427 return AccessTranslationUtils.translateAsGet(expression, context);
428 }
429
430 @Override
431 @NotNull
432 public JsNode visitSuperExpression(@NotNull JetSuperExpression expression, @NotNull TranslationContext context) {
433 DeclarationDescriptor superClassDescriptor = context.bindingContext().get(BindingContext.REFERENCE_TARGET, expression.getInstanceReference());
434 assert superClassDescriptor != null: message(expression);
435 return translateAsFQReference(superClassDescriptor, context);
436 }
437
438 @Override
439 @NotNull
440 public JsNode visitForExpression(@NotNull JetForExpression expression,
441 @NotNull TranslationContext context) {
442 return LoopTranslatorPackage.translateForExpression(expression, context).source(expression);
443 }
444
445 @Override
446 @NotNull
447 public JsNode visitTryExpression(
448 @NotNull JetTryExpression expression,
449 @NotNull TranslationContext context
450 ) {
451 return new TryTranslator(expression, context).translate();
452 }
453
454 @Override
455 @NotNull
456 public JsNode visitThrowExpression(@NotNull JetThrowExpression expression,
457 @NotNull TranslationContext context) {
458 JetExpression thrownExpression = expression.getThrownExpression();
459 assert thrownExpression != null : "Thrown expression must not be null";
460 return new JsThrow(translateAsExpression(thrownExpression, context)).source(expression);
461 }
462
463 @Override
464 @NotNull
465 public JsNode visitObjectLiteralExpression(@NotNull JetObjectLiteralExpression expression,
466 @NotNull TranslationContext context) {
467 return ClassTranslator.generateObjectLiteral(expression.getObjectDeclaration(), context);
468 }
469
470 @Override
471 @NotNull
472 public JsNode visitObjectDeclaration(@NotNull JetObjectDeclaration expression,
473 @NotNull TranslationContext context) {
474 DeclarationDescriptor descriptor = getDescriptorForElement(context.bindingContext(), expression);
475 JsName name = context.getNameForDescriptor(descriptor);
476 JsExpression value = ClassTranslator.generateClassCreation(expression, context);
477 return newVar(name, value).source(expression);
478 }
479 }