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( 190 @NotNull JetCallExpression expression, 191 @NotNull TranslationContext context 192 ) { 193 if (shouldBeInlined(expression, context) && 194 BindingContextUtilPackage.isUsedAsExpression(expression, context.bindingContext())) { 195 TemporaryVariable temporaryVariable = context.declareTemporary(null); 196 197 JsNode callResult = CallExpressionTranslator.translate(expression, null, context).source(expression); 198 assert callResult instanceof JsExpression; 199 200 JsExpression assignment = JsAstUtils.assignment(temporaryVariable.reference(), (JsExpression) callResult); 201 context.addStatementToCurrentBlock(assignment.makeStmt()); 202 return temporaryVariable.reference(); 203 } else { 204 return CallExpressionTranslator.translate(expression, null, context).source(expression); 205 } 206 } 207 208 @Override 209 @NotNull 210 public JsNode visitIfExpression(@NotNull JetIfExpression expression, @NotNull TranslationContext context) { 211 assert expression.getCondition() != null : "condition should not ne null: " + expression.getText(); 212 JsExpression testExpression = Translation.translateAsExpression(expression.getCondition(), context); 213 if (JsAstUtils.isEmptyExpression(testExpression)) { 214 return testExpression; 215 } 216 217 boolean isKotlinExpression = BindingContextUtilPackage.isUsedAsExpression(expression, context.bindingContext()); 218 219 JetExpression thenExpression = expression.getThen(); 220 assert thenExpression != null : "then expression should not be null: " + expression.getText(); 221 JetExpression elseExpression = expression.getElse(); 222 223 JsStatement thenStatement = Translation.translateAsStatementAndMergeInBlockIfNeeded(thenExpression, context); 224 JsStatement elseStatement = (elseExpression != null) ? Translation.translateAsStatementAndMergeInBlockIfNeeded(elseExpression, 225 context) : null; 226 227 if (isKotlinExpression) { 228 JsExpression jsThenExpression = JsAstUtils.extractExpressionFromStatement(thenStatement); 229 JsExpression jsElseExpression = JsAstUtils.extractExpressionFromStatement(elseStatement); 230 boolean canBeJsExpression = jsThenExpression != null && jsElseExpression != null; 231 if (canBeJsExpression) { 232 return new JsConditional(testExpression, jsThenExpression, jsElseExpression).source(expression); 233 } 234 } 235 JsIf ifStatement = new JsIf(testExpression, thenStatement, elseStatement); 236 return ifStatement.source(expression); 237 } 238 239 @Override 240 @NotNull 241 public JsExpression visitSimpleNameExpression(@NotNull JetSimpleNameExpression expression, 242 @NotNull TranslationContext context) { 243 return ReferenceTranslator.translateSimpleNameWithQualifier(expression, null, context).source(expression); 244 } 245 246 @Override 247 @NotNull 248 public JsNode visitWhileExpression(@NotNull JetWhileExpression expression, @NotNull TranslationContext context) { 249 return LoopTranslatorPackage.createWhile(false, expression, context); 250 } 251 252 @Override 253 @NotNull 254 public JsNode visitDoWhileExpression(@NotNull JetDoWhileExpression expression, @NotNull TranslationContext context) { 255 return LoopTranslatorPackage.createWhile(true, expression, context); 256 } 257 258 @Override 259 @NotNull 260 public JsNode visitStringTemplateExpression(@NotNull JetStringTemplateExpression expression, 261 @NotNull TranslationContext context) { 262 JsStringLiteral stringLiteral = resolveAsStringConstant(expression, context); 263 if (stringLiteral != null) { 264 return stringLiteral; 265 } 266 return resolveAsTemplate(expression, context).source(expression); 267 } 268 269 @NotNull 270 private static JsNode resolveAsTemplate(@NotNull JetStringTemplateExpression expression, 271 @NotNull TranslationContext context) { 272 return StringTemplateTranslator.translate(expression, context); 273 } 274 275 @Nullable 276 private static JsStringLiteral resolveAsStringConstant(@NotNull JetExpression expression, 277 @NotNull TranslationContext context) { 278 Object value = getCompileTimeValue(context.bindingContext(), expression); 279 if (value == null) { 280 return null; 281 } 282 assert value instanceof String : "Compile time constant template should be a String constant."; 283 String constantString = (String) value; 284 return context.program().getStringLiteral(constantString); 285 } 286 287 @Override 288 @NotNull 289 public JsNode visitDotQualifiedExpression(@NotNull JetDotQualifiedExpression expression, 290 @NotNull TranslationContext context) { 291 return QualifiedExpressionTranslator.translateQualifiedExpression(expression, context); 292 } 293 294 @Override 295 public JsNode visitLabeledExpression( 296 @NotNull JetLabeledExpression expression, TranslationContext context 297 ) { 298 JetExpression baseExpression = expression.getBaseExpression(); 299 assert baseExpression != null; 300 JsScope scope = context.scope(); 301 assert scope instanceof JsFunctionScope: "Labeled statement is unexpected outside of function scope"; 302 JsFunctionScope functionScope = (JsFunctionScope) scope; 303 String labelIdent = getReferencedName(expression.getTargetLabel()); 304 JsName labelName = functionScope.enterLabel(labelIdent); 305 JsStatement baseStatement = Translation.translateAsStatement(baseExpression, context); 306 functionScope.exitLabel(); 307 return new JsLabel(labelName, baseStatement).source(expression); 308 } 309 310 @Override 311 @NotNull 312 public JsNode visitPrefixExpression( 313 @NotNull JetPrefixExpression expression, 314 @NotNull TranslationContext context 315 ) { 316 return UnaryOperationTranslator.translate(expression, context).source(expression); 317 } 318 319 @Override 320 @NotNull 321 public JsNode visitPostfixExpression(@NotNull JetPostfixExpression expression, 322 @NotNull TranslationContext context) { 323 return UnaryOperationTranslator.translate(expression, context).source(expression); 324 } 325 326 @Override 327 @NotNull 328 public JsNode visitIsExpression(@NotNull JetIsExpression expression, 329 @NotNull TranslationContext context) { 330 return Translation.patternTranslator(context).translateIsExpression(expression); 331 } 332 333 @Override 334 @NotNull 335 public JsNode visitSafeQualifiedExpression(@NotNull JetSafeQualifiedExpression expression, 336 @NotNull TranslationContext context) { 337 return QualifiedExpressionTranslator.translateQualifiedExpression(expression, context).source(expression); 338 } 339 340 @Override 341 @Nullable 342 public JsNode visitWhenExpression(@NotNull JetWhenExpression expression, 343 @NotNull TranslationContext context) { 344 return WhenTranslator.translate(expression, context); 345 } 346 347 @Override 348 @NotNull 349 public JsNode visitBinaryWithTypeRHSExpression(@NotNull JetBinaryExpressionWithTypeRHS expression, 350 @NotNull TranslationContext context) { 351 JsExpression jsExpression = Translation.translateAsExpression(expression.getLeft(), context); 352 353 if (expression.getOperationReference().getReferencedNameElementType() != JetTokens.AS_KEYWORD) 354 return jsExpression.source(expression); 355 356 JetTypeReference right = expression.getRight(); 357 assert right != null; 358 359 JetType rightType = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.TYPE, right); 360 JetType leftType = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.EXPRESSION_TYPE, expression.getLeft()); 361 if (TypeUtils.isNullableType(rightType) || !TypeUtils.isNullableType(leftType)) { 362 return jsExpression.source(expression); 363 } 364 365 // KT-2670 366 // we actually do not care for types in js 367 return TranslationUtils.sure(jsExpression, context).source(expression); 368 } 369 370 private static String getReferencedName(JetSimpleNameExpression expression) { 371 return expression.getReferencedName() 372 .replaceAll("^@", "") 373 .replaceAll("(?:^`(.*)`$)", "$1"); 374 } 375 376 private static JsNameRef getTargetLabel(JetExpressionWithLabel expression, TranslationContext context) { 377 JetSimpleNameExpression labelElement = expression.getTargetLabel(); 378 if (labelElement == null) { 379 return null; 380 } 381 382 String labelIdent = getReferencedName(labelElement); 383 JsScope scope = context.scope(); 384 assert scope instanceof JsFunctionScope: "Labeled statement is unexpected outside of function scope"; 385 JsName labelName = ((JsFunctionScope) scope).findLabel(labelIdent); 386 assert labelName != null; 387 return labelName.makeRef(); 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 if (InlineUtil.getInlineType(descriptor).isInline()) { 418 MetadataPackage.setStaticRef(name, alias); 419 } 420 421 return new JsVars(new JsVars.JsVar(name, alias)).source(expression); 422 } 423 424 @Override 425 @NotNull 426 public JsNode visitThisExpression(@NotNull JetThisExpression expression, @NotNull TranslationContext context) { 427 DeclarationDescriptor thisExpression = 428 getDescriptorForReferenceExpression(context.bindingContext(), expression.getInstanceReference()); 429 assert thisExpression != null : "This expression must reference a descriptor: " + expression.getText(); 430 431 return context.getDispatchReceiver(getReceiverParameterForDeclaration(thisExpression)).source(expression); 432 } 433 434 @Override 435 @NotNull 436 public JsNode visitArrayAccessExpression(@NotNull JetArrayAccessExpression expression, 437 @NotNull TranslationContext context) { 438 return AccessTranslationUtils.translateAsGet(expression, context); 439 } 440 441 @Override 442 @NotNull 443 public JsNode visitSuperExpression(@NotNull JetSuperExpression expression, @NotNull TranslationContext context) { 444 DeclarationDescriptor superClassDescriptor = context.bindingContext().get(BindingContext.REFERENCE_TARGET, expression.getInstanceReference()); 445 assert superClassDescriptor != null: message(expression); 446 return translateAsFQReference(superClassDescriptor, context); 447 } 448 449 @Override 450 @NotNull 451 public JsNode visitForExpression(@NotNull JetForExpression expression, 452 @NotNull TranslationContext context) { 453 return LoopTranslatorPackage.translateForExpression(expression, context).source(expression); 454 } 455 456 @Override 457 @NotNull 458 public JsNode visitTryExpression( 459 @NotNull JetTryExpression expression, 460 @NotNull TranslationContext context 461 ) { 462 return new TryTranslator(expression, context).translate(); 463 } 464 465 @Override 466 @NotNull 467 public JsNode visitThrowExpression(@NotNull JetThrowExpression expression, 468 @NotNull TranslationContext context) { 469 JetExpression thrownExpression = expression.getThrownExpression(); 470 assert thrownExpression != null : "Thrown expression must not be null"; 471 return new JsThrow(translateAsExpression(thrownExpression, context)).source(expression); 472 } 473 474 @Override 475 @NotNull 476 public JsNode visitObjectLiteralExpression(@NotNull JetObjectLiteralExpression expression, 477 @NotNull TranslationContext context) { 478 return ClassTranslator.generateObjectLiteral(expression.getObjectDeclaration(), context); 479 } 480 481 @Override 482 @NotNull 483 public JsNode visitObjectDeclaration(@NotNull JetObjectDeclaration expression, 484 @NotNull TranslationContext context) { 485 DeclarationDescriptor descriptor = getDescriptorForElement(context.bindingContext(), expression); 486 JsName name = context.getNameForDescriptor(descriptor); 487 JsExpression value = ClassTranslator.generateClassCreation(expression, context); 488 return newVar(name, value).source(expression); 489 } 490 }