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 017package org.jetbrains.k2js.translate.expression; 018 019import com.google.dart.compiler.backend.js.ast.*; 020import org.jetbrains.annotations.NotNull; 021import org.jetbrains.annotations.Nullable; 022import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor; 023import org.jetbrains.jet.lang.psi.*; 024import org.jetbrains.jet.lang.resolve.BindingContext; 025import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant; 026import org.jetbrains.jet.lang.resolve.constants.NullValue; 027import org.jetbrains.k2js.translate.context.TemporaryVariable; 028import org.jetbrains.k2js.translate.context.TranslationContext; 029import org.jetbrains.k2js.translate.declaration.ClassTranslator; 030import org.jetbrains.k2js.translate.expression.foreach.ForTranslator; 031import org.jetbrains.k2js.translate.general.Translation; 032import org.jetbrains.k2js.translate.general.TranslatorVisitor; 033import org.jetbrains.k2js.translate.operation.BinaryOperationTranslator; 034import org.jetbrains.k2js.translate.operation.UnaryOperationTranslator; 035import org.jetbrains.k2js.translate.reference.*; 036import org.jetbrains.k2js.translate.utils.BindingUtils; 037import org.jetbrains.k2js.translate.utils.mutator.AssignToExpressionMutator; 038 039import java.util.List; 040 041import static org.jetbrains.k2js.translate.general.Translation.translateAsExpression; 042import static org.jetbrains.k2js.translate.utils.BindingUtils.*; 043import static org.jetbrains.k2js.translate.utils.ErrorReportingUtils.message; 044import static org.jetbrains.k2js.translate.utils.JsAstUtils.*; 045import static org.jetbrains.k2js.translate.utils.PsiUtils.getObjectDeclarationName; 046import static org.jetbrains.k2js.translate.utils.TranslationUtils.translateInitializerForProperty; 047import static org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression; 048 049public final class ExpressionVisitor extends TranslatorVisitor<JsNode> { 050 @Override 051 @NotNull 052 public JsNode visitConstantExpression(@NotNull JetConstantExpression expression, 053 @NotNull TranslationContext context) { 054 CompileTimeConstant<?> compileTimeValue = context.bindingContext().get(BindingContext.COMPILE_TIME_VALUE, expression); 055 assert compileTimeValue != null; 056 057 if (compileTimeValue instanceof NullValue) { 058 return JsLiteral.NULL; 059 } 060 061 Object value = compileTimeValue.getValue(); 062 if (value instanceof Integer || value instanceof Short || value instanceof Byte) { 063 return context.program().getNumberLiteral(((Number) value).intValue()); 064 } 065 else if (value instanceof Number) { 066 return context.program().getNumberLiteral(((Number) value).doubleValue()); 067 } 068 else if (value instanceof Boolean) { 069 return JsLiteral.getBoolean((Boolean) value); 070 } 071 072 //TODO: test 073 if (value instanceof String) { 074 return context.program().getStringLiteral((String) value); 075 } 076 if (value instanceof Character) { 077 return context.program().getStringLiteral(value.toString()); 078 } 079 throw new AssertionError(message(expression, "Unsupported constant expression")); 080 } 081 082 @Override 083 @NotNull 084 public JsNode visitBlockExpression(@NotNull JetBlockExpression jetBlock, @NotNull TranslationContext context) { 085 List<JetElement> statements = jetBlock.getStatements(); 086 JsBlock jsBlock = new JsBlock(); 087 TranslationContext blockContext = context.innerBlock(jsBlock); 088 for (JetElement statement : statements) { 089 assert statement instanceof JetExpression : "Elements in JetBlockExpression " + 090 "should be of type JetExpression"; 091 JsNode jsNode = statement.accept(this, blockContext); 092 if (jsNode != null) { 093 jsBlock.getStatements().add(convertToStatement(jsNode)); 094 } 095 } 096 return jsBlock; 097 } 098 099 @Override 100 @NotNull 101 public JsNode visitReturnExpression(@NotNull JetReturnExpression jetReturnExpression, 102 @NotNull TranslationContext context) { 103 JetExpression returnedExpression = jetReturnExpression.getReturnedExpression(); 104 if (returnedExpression != null) { 105 JsExpression jsExpression = translateAsExpression(returnedExpression, context); 106 return new JsReturn(jsExpression); 107 } 108 return new JsReturn(); 109 } 110 111 @Override 112 @NotNull 113 public JsNode visitParenthesizedExpression(@NotNull JetParenthesizedExpression expression, 114 @NotNull TranslationContext context) { 115 JetExpression expressionInside = expression.getExpression(); 116 if (expressionInside != null) { 117 return expressionInside.accept(this, context); 118 } 119 return context.program().getEmptyStmt(); 120 } 121 122 @Override 123 @NotNull 124 public JsNode visitBinaryExpression(@NotNull JetBinaryExpression expression, 125 @NotNull TranslationContext context) { 126 return BinaryOperationTranslator.translate(expression, context); 127 } 128 129 @Override 130 @NotNull 131 // assume it is a local variable declaration 132 public JsNode visitProperty(@NotNull JetProperty expression, @NotNull TranslationContext context) { 133 DeclarationDescriptor descriptor = getDescriptorForElement(context.bindingContext(), expression); 134 JsName jsPropertyName = context.getNameForDescriptor(descriptor); 135 JsExpression jsInitExpression = translateInitializerForProperty(expression, context); 136 return newVar(jsPropertyName, jsInitExpression); 137 } 138 139 @Override 140 @NotNull 141 public JsNode visitCallExpression(@NotNull JetCallExpression expression, 142 @NotNull TranslationContext context) { 143 return CallExpressionTranslator.translate(expression, null, CallType.NORMAL, context); 144 } 145 146 @Override 147 @NotNull 148 public JsNode visitIfExpression(@NotNull JetIfExpression expression, @NotNull TranslationContext context) { 149 JsExpression testExpression = translateConditionExpression(expression.getCondition(), context); 150 JetExpression thenExpression = expression.getThen(); 151 JetExpression elseExpression = expression.getElse(); 152 assert thenExpression != null; 153 JsNode thenNode = thenExpression.accept(this, context); 154 JsNode elseNode = elseExpression == null ? null : elseExpression.accept(this, context); 155 156 boolean isKotlinStatement = BindingUtils.isStatement(context.bindingContext(), expression); 157 boolean canBeJsExpression = thenNode instanceof JsExpression && elseNode instanceof JsExpression; 158 if (!isKotlinStatement && canBeJsExpression) { 159 return new JsConditional(testExpression, convertToExpression(thenNode), convertToExpression(elseNode)); 160 } 161 else { 162 JsIf ifStatement = new JsIf(testExpression, convertToStatement(thenNode), elseNode == null ? null : convertToStatement(elseNode)); 163 if (isKotlinStatement) { 164 return ifStatement; 165 } 166 167 TemporaryVariable result = context.declareTemporary(null); 168 AssignToExpressionMutator saveResultToTemporaryMutator = new AssignToExpressionMutator(result.reference()); 169 context.addStatementToCurrentBlock(mutateLastExpression(ifStatement, saveResultToTemporaryMutator)); 170 return result.reference(); 171 } 172 } 173 174 @Override 175 @NotNull 176 public JsNode visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression, 177 @NotNull TranslationContext context) { 178 return ReferenceTranslator.translateSimpleName(expression, context); 179 } 180 181 182 @NotNull 183 private JsStatement translateNullableExpressionAsNotNullStatement(@Nullable JetExpression nullableExpression, 184 @NotNull TranslationContext context) { 185 if (nullableExpression == null) { 186 return context.program().getEmptyStmt(); 187 } 188 return convertToStatement(nullableExpression.accept(this, context)); 189 } 190 191 @NotNull 192 private JsExpression translateConditionExpression(@Nullable JetExpression expression, 193 @NotNull TranslationContext context) { 194 JsExpression jsCondition = translateNullableExpression(expression, context); 195 assert (jsCondition != null) : "Condition should not be empty"; 196 return convertToExpression(jsCondition); 197 } 198 199 @Nullable 200 private JsExpression translateNullableExpression(@Nullable JetExpression expression, 201 @NotNull TranslationContext context) { 202 if (expression == null) { 203 return null; 204 } 205 return convertToExpression(expression.accept(this, context)); 206 } 207 208 @Override 209 @NotNull 210 public JsNode visitWhileExpression(@NotNull JetWhileExpression expression, @NotNull TranslationContext context) { 211 return createWhile(new JsWhile(), expression, context); 212 } 213 214 @Override 215 @NotNull 216 public JsNode visitDoWhileExpression(@NotNull JetDoWhileExpression expression, @NotNull TranslationContext context) { 217 return createWhile(new JsDoWhile(), expression, context); 218 } 219 220 private JsNode createWhile(@NotNull JsWhile result, @NotNull JetWhileExpressionBase expression, @NotNull TranslationContext context) { 221 result.setCondition(translateConditionExpression(expression.getCondition(), context)); 222 result.setBody(translateNullableExpressionAsNotNullStatement(expression.getBody(), context)); 223 return result; 224 } 225 226 @Override 227 @NotNull 228 public JsNode visitStringTemplateExpression(@NotNull JetStringTemplateExpression expression, 229 @NotNull TranslationContext context) { 230 JsStringLiteral stringLiteral = resolveAsStringConstant(expression, context); 231 if (stringLiteral != null) { 232 return stringLiteral; 233 } 234 return resolveAsTemplate(expression, context); 235 } 236 237 @NotNull 238 private static JsNode resolveAsTemplate(@NotNull JetStringTemplateExpression expression, 239 @NotNull TranslationContext context) { 240 return StringTemplateTranslator.translate(expression, context); 241 } 242 243 @Nullable 244 private static JsStringLiteral resolveAsStringConstant(@NotNull JetExpression expression, 245 @NotNull TranslationContext context) { 246 Object value = getCompileTimeValue(context.bindingContext(), expression); 247 if (value == null) { 248 return null; 249 } 250 assert value instanceof String : "Compile time constant template should be a String constant."; 251 String constantString = (String) value; 252 return context.program().getStringLiteral(constantString); 253 } 254 255 @Override 256 @NotNull 257 public JsNode visitDotQualifiedExpression(@NotNull JetDotQualifiedExpression expression, 258 @NotNull TranslationContext context) { 259 return QualifiedExpressionTranslator.translateQualifiedExpression(expression, context); 260 } 261 262 @Override 263 @NotNull 264 public JsNode visitPrefixExpression(@NotNull JetPrefixExpression expression, 265 @NotNull TranslationContext context) { 266 return UnaryOperationTranslator.translate(expression, context); 267 } 268 269 @Override 270 @NotNull 271 public JsNode visitPostfixExpression(@NotNull JetPostfixExpression expression, 272 @NotNull TranslationContext context) { 273 return UnaryOperationTranslator.translate(expression, context); 274 } 275 276 @Override 277 @NotNull 278 public JsNode visitIsExpression(@NotNull JetIsExpression expression, 279 @NotNull TranslationContext context) { 280 return Translation.patternTranslator(context).translateIsExpression(expression); 281 } 282 283 @Override 284 @NotNull 285 public JsNode visitSafeQualifiedExpression(@NotNull JetSafeQualifiedExpression expression, 286 @NotNull TranslationContext context) { 287 return QualifiedExpressionTranslator.translateQualifiedExpression(expression, context); 288 } 289 290 @Override 291 @Nullable 292 public JsNode visitWhenExpression(@NotNull JetWhenExpression expression, 293 @NotNull TranslationContext context) { 294 return Translation.translateWhenExpression(expression, context); 295 } 296 297 298 @Override 299 @NotNull 300 public JsNode visitBinaryWithTypeRHSExpression(@NotNull JetBinaryExpressionWithTypeRHS expression, 301 @NotNull TranslationContext context) { 302 // we actually do not care for types in js 303 return Translation.translateExpression(expression.getLeft(), context); 304 } 305 306 @Override 307 @NotNull 308 public JsNode visitBreakExpression(@NotNull JetBreakExpression expression, 309 @NotNull TranslationContext context) { 310 return new JsBreak(); 311 } 312 313 @Override 314 @NotNull 315 public JsNode visitContinueExpression(@NotNull JetContinueExpression expression, 316 @NotNull TranslationContext context) { 317 return new JsContinue(); 318 } 319 320 @Override 321 @NotNull 322 public JsNode visitFunctionLiteralExpression(@NotNull JetFunctionLiteralExpression expression, 323 @NotNull TranslationContext context) { 324 return context.literalFunctionTranslator().translate(expression); 325 } 326 327 @Override 328 @NotNull 329 public JsNode visitThisExpression(@NotNull JetThisExpression expression, 330 @NotNull TranslationContext context) { 331 DeclarationDescriptor thisExpression = 332 getDescriptorForReferenceExpression(context.bindingContext(), expression.getInstanceReference()); 333 assert thisExpression != null : "This expression must reference a descriptor: " + expression.getText(); 334 return context.getThisObject(thisExpression); 335 } 336 337 @Override 338 @NotNull 339 public JsNode visitArrayAccessExpression(@NotNull JetArrayAccessExpression expression, 340 @NotNull TranslationContext context) { 341 return AccessTranslationUtils.translateAsGet(expression, context); 342 } 343 344 @Override 345 @NotNull 346 public JsNode visitForExpression(@NotNull JetForExpression expression, 347 @NotNull TranslationContext context) { 348 return ForTranslator.translate(expression, context); 349 } 350 351 @Override 352 @NotNull 353 public JsNode visitTryExpression(@NotNull JetTryExpression expression, 354 @NotNull TranslationContext context) { 355 return TryTranslator.translate(expression, context); 356 } 357 358 @Override 359 @NotNull 360 public JsNode visitThrowExpression(@NotNull JetThrowExpression expression, 361 @NotNull TranslationContext context) { 362 JetExpression thrownExpression = expression.getThrownExpression(); 363 assert thrownExpression != null : "Thrown expression must not be null"; 364 return new JsThrow(translateAsExpression(thrownExpression, context)); 365 } 366 367 @Override 368 @NotNull 369 public JsNode visitObjectLiteralExpression(@NotNull JetObjectLiteralExpression expression, 370 @NotNull TranslationContext context) { 371 return ClassTranslator.generateObjectLiteral(expression, context); 372 } 373 374 @Override 375 @NotNull 376 public JsNode visitObjectDeclaration(@NotNull JetObjectDeclaration expression, 377 @NotNull TranslationContext context) { 378 JetObjectDeclarationName objectDeclarationName = getObjectDeclarationName(expression); 379 DeclarationDescriptor descriptor = getDescriptorForElement(context.bindingContext(), objectDeclarationName); 380 JsName propertyName = context.getNameForDescriptor(descriptor); 381 JsExpression value = ClassTranslator.generateClassCreation(expression, context); 382 return newVar(propertyName, value); 383 } 384 385 @Override 386 @NotNull 387 public JsNode visitNamedFunction(@NotNull JetNamedFunction function, 388 @NotNull TranslationContext context) { 389 return FunctionTranslator.newInstance(function, context).translateAsLocalFunction(); 390 } 391}