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