JS: get rid of JsEmptyExpression, use JsLiteral.NULL instead, rely on optimizer to remove redundant code

This commit is contained in:
Alexey Andreev
2016-05-16 18:59:27 +03:00
parent fe5a7aaf46
commit 9edb9fabe5
19 changed files with 73 additions and 173 deletions
@@ -18,7 +18,6 @@ import static com.google.dart.compiler.backend.js.ast.JsNumberLiteral.JsIntLiter
* A JavaScript program. * A JavaScript program.
*/ */
public final class JsProgram extends SourceInfoAwareJsNode { public final class JsProgram extends SourceInfoAwareJsNode {
@NotNull final JsExpression emptyExpression;
private JsProgramFragment[] fragments; private JsProgramFragment[] fragments;
@@ -33,13 +32,6 @@ public final class JsProgram extends SourceInfoAwareJsNode {
rootScope = new JsRootScope(this); rootScope = new JsRootScope(this);
topScope = new JsObjectScope(rootScope, "Global", unitId); topScope = new JsObjectScope(rootScope, "Global", unitId);
setFragmentCount(1); setFragmentCount(1);
emptyExpression = new JsEmptyExpression();
}
@NotNull
public JsExpression getEmptyExpression() {
return emptyExpression;
} }
public JsBlock getFragmentBlock(int fragment) { public JsBlock getFragmentBlock(int fragment) {
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.diagnostics.DiagnosticUtils
import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.context.TranslationContext
import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator import org.jetbrains.kotlin.js.translate.reference.CallArgumentTranslator
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.asSyntheticStatement
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getReceiverParameterForReceiver import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getReceiverParameterForReceiver
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall import org.jetbrains.kotlin.resolve.calls.callUtil.isSafeCall
@@ -87,7 +88,7 @@ fun TranslationContext.getCallInfo(resolvedCall: ResolvedCall<out FunctionDescri
var receiverRef = explicitReceivers.extensionReceiver var receiverRef = explicitReceivers.extensionReceiver
if (receiverRef != null) { if (receiverRef != null) {
receiverRef = this.declareTemporary(null).reference() receiverRef = this.declareTemporary(null).reference()
this.addStatementToCurrentBlock(JsAstUtils.assignment(receiverRef, explicitReceivers.extensionReceiver!!).makeStmt()) addStatementToCurrentBlock(asSyntheticStatement(JsAstUtils.assignment(receiverRef, explicitReceivers.extensionReceiver!!)))
} }
ExplicitReceivers(receiverOrThisRef, receiverRef) ExplicitReceivers(receiverOrThisRef, receiverRef)
} }
@@ -16,7 +16,6 @@
package org.jetbrains.kotlin.js.translate.callTranslator package org.jetbrains.kotlin.js.translate.callTranslator
import com.google.dart.compiler.backend.js.ast.JsEmptyExpression
import com.google.dart.compiler.backend.js.ast.JsExpression import com.google.dart.compiler.backend.js.ast.JsExpression
import com.google.dart.compiler.backend.js.ast.JsName import com.google.dart.compiler.backend.js.ast.JsName
import com.google.dart.compiler.backend.js.ast.metadata.HasMetadata import com.google.dart.compiler.backend.js.ast.metadata.HasMetadata
@@ -73,19 +72,15 @@ fun VariableAccessInfo.getAccessFunctionName(): String {
} }
fun VariableAccessInfo.constructAccessExpression(ref: JsExpression): JsExpression { fun VariableAccessInfo.constructAccessExpression(ref: JsExpression): JsExpression {
if (isGetAccess()) { return if (isGetAccess()) {
return ref ref
} else { }
return if (value !is JsEmptyExpression) { else {
// This is useful when passing AST to TemporaryAssignmentElimination. It can bring // This is useful when passing AST to TemporaryAssignmentElimination. It can bring
// property assignment like `obj.propertyName = $tmp` to places where `$tmp` gets its value, // property assignment like `obj.propertyName = $tmp` to places where `$tmp` gets its value,
// but only when it's sure that no side effects possible. // but only when it's sure that no side effects possible.
(ref as? HasMetadata)?.let { it.sideEffects = SideEffectKind.PURE } (ref as? HasMetadata)?.let { it.sideEffects = SideEffectKind.PURE }
JsAstUtils.assignment(ref, value!!) JsAstUtils.assignment(ref, value!!)
}
else {
context.emptyExpression
}
} }
} }
@@ -306,10 +306,6 @@ object DynamicOperatorCallCase : FunctionCallCase() {
} }
fun FunctionCallInfo.translateFunctionCall(): JsExpression { fun FunctionCallInfo.translateFunctionCall(): JsExpression {
// When call has `continue` or `break` as one of its argument, they'll be temporarily represented ad JsEmptyExpression.
// Such call should not be translated. All side effects are already extracted by this point.
if (argumentsInfo.valueArguments.any { JsAstUtils.isEmptyExpression(it) }) return context.emptyExpression
val intrinsic = DelegateFunctionIntrinsic.intrinsic(this) val intrinsic = DelegateFunctionIntrinsic.intrinsic(this)
return when { return when {
@@ -320,11 +320,6 @@ public class TranslationContext {
return dynamicContext.jsBlock(); return dynamicContext.jsBlock();
} }
@NotNull
public JsExpression getEmptyExpression() {
return program().getEmptyExpression();
}
@Nullable @Nullable
public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) { public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
JsNameRef nameRef = captureIfNeedAndGetCapturedName(descriptor); JsNameRef nameRef = captureIfNeedAndGetCapturedName(descriptor);
@@ -101,11 +101,12 @@ class CatchTranslator(
private fun translateCatchBody(context: TranslationContext, catchClause: KtCatchClause): JsBlock { private fun translateCatchBody(context: TranslationContext, catchClause: KtCatchClause): JsBlock {
val catchBody = catchClause.catchBody val catchBody = catchClause.catchBody
val jsCatchBody = val jsCatchBody = if (catchBody != null) {
if (catchBody != null) translateAsStatementAndMergeInBlockIfNeeded(catchBody, context)
translateAsStatementAndMergeInBlockIfNeeded(catchBody, context) }
else else {
context.emptyExpression.makeStmt() JsAstUtils.asSyntheticStatement(JsLiteral.NULL)
}
return convertToBlock(jsCatchBody) return convertToBlock(jsCatchBody)
} }
@@ -73,7 +73,7 @@ import static org.jetbrains.kotlin.types.expressions.ExpressionTypingUtils.isFun
public final class ExpressionVisitor extends TranslatorVisitor<JsNode> { public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@Override @Override
protected JsNode emptyResult(@NotNull TranslationContext context) { protected JsNode emptyResult(@NotNull TranslationContext context) {
return context.getEmptyExpression(); return JsLiteral.NULL;
} }
@Override @Override
@@ -156,9 +156,6 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
} }
else { else {
JsExpression jsReturnExpression = translateAsExpression(returned, context); JsExpression jsReturnExpression = translateAsExpression(returned, context);
if (JsAstUtils.isEmptyExpression(jsReturnExpression)) {
return context.getEmptyExpression();
}
jsReturn = new JsReturn(jsReturnExpression); jsReturn = new JsReturn(jsReturnExpression);
} }
@@ -221,9 +218,6 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
VariableDescriptor descriptor = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.VARIABLE, expression); VariableDescriptor descriptor = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.VARIABLE, expression);
JsExpression initializer = translateInitializerForProperty(expression, context); JsExpression initializer = translateInitializerForProperty(expression, context);
if (initializer != null && JsAstUtils.isEmptyExpression(initializer)) {
return context.getEmptyExpression();
}
KtExpression delegateExpression = expression.getDelegateExpression(); KtExpression delegateExpression = expression.getDelegateExpression();
if (delegateExpression != null) { if (delegateExpression != null) {
@@ -267,9 +261,6 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
public JsNode visitIfExpression(@NotNull KtIfExpression expression, @NotNull TranslationContext context) { public JsNode visitIfExpression(@NotNull KtIfExpression expression, @NotNull TranslationContext context) {
assert expression.getCondition() != null : "condition should not ne null: " + expression.getText(); assert expression.getCondition() != null : "condition should not ne null: " + expression.getText();
JsExpression testExpression = Translation.translateAsExpression(expression.getCondition(), context); JsExpression testExpression = Translation.translateAsExpression(expression.getCondition(), context);
if (JsAstUtils.isEmptyExpression(testExpression)) {
return testExpression;
}
boolean isKotlinExpression = BindingContextUtilsKt.isUsedAsExpression(expression, context.bindingContext()); boolean isKotlinExpression = BindingContextUtilsKt.isUsedAsExpression(expression, context.bindingContext());
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getHasNextCallable
import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getIteratorFunction import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getIteratorFunction
import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getNextFunction import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getNextFunction
import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getTypeForExpression import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getTypeForExpression
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.* import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.*
import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getLoopParameter import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getLoopParameter
import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getLoopRange import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getLoopRange
@@ -46,7 +47,6 @@ fun createWhile(doWhile: Boolean, expression: KtWhileExpressionBase, context: Tr
throw IllegalArgumentException("condition expression should not be null: ${expression.text}") throw IllegalArgumentException("condition expression should not be null: ${expression.text}")
val conditionBlock = JsBlock() val conditionBlock = JsBlock()
var jsCondition = Translation.translateAsExpression(conditionExpression, context, conditionBlock) var jsCondition = Translation.translateAsExpression(conditionExpression, context, conditionBlock)
val isEmptyLoopCondition = isEmptyExpression(jsCondition)
val body = expression.body val body = expression.body
var bodyStatement = var bodyStatement =
if (body != null) if (body != null)
@@ -62,30 +62,18 @@ fun createWhile(doWhile: Boolean, expression: KtWhileExpressionBase, context: Tr
if (doWhile) { if (doWhile) {
// translate to: tmpSecondRun = false; do { if(tmpSecondRun) { <expr> if(!tmpExprVar) break; } else tmpSecondRun=true; <body> } while(true) // translate to: tmpSecondRun = false; do { if(tmpSecondRun) { <expr> if(!tmpExprVar) break; } else tmpSecondRun=true; <body> } while(true)
val secondRun = context.declareTemporary(JsLiteral.FALSE) val secondRun = context.declareTemporary(JsLiteral.FALSE)
context.addStatementToCurrentBlock(secondRun.assignmentExpression().makeStmt()) context.addStatementToCurrentBlock(JsAstUtils.asSyntheticStatement(secondRun.assignmentExpression()))
if (!isEmptyLoopCondition) { conditionBlock.statements.add(breakIfConditionIsFalseStatement)
conditionBlock.statements.add(breakIfConditionIsFalseStatement)
}
val ifStatement = JsIf(secondRun.reference(), conditionBlock, assignment(secondRun.reference(), JsLiteral.TRUE).makeStmt()) val ifStatement = JsIf(secondRun.reference(), conditionBlock, assignment(secondRun.reference(), JsLiteral.TRUE).makeStmt())
bodyBlock.statements.add(0, ifStatement) bodyBlock.statements.add(0, ifStatement)
} }
else { else {
// translate to: while (true) { <expr> if(!tmpExprVar) break; <body> } conditionBlock.statements.add(breakIfConditionIsFalseStatement)
if (isEmptyLoopCondition) { bodyBlock.statements.addAll(0, conditionBlock.statements)
bodyBlock.statements.clear()
context.addStatementsToCurrentBlockFrom(conditionBlock)
}
else {
conditionBlock.statements.add(breakIfConditionIsFalseStatement)
bodyBlock.statements.addAll(0, conditionBlock.statements)
}
} }
bodyStatement = bodyBlock bodyStatement = bodyBlock
} }
else if (isEmptyLoopCondition) {
jsCondition = JsLiteral.FALSE
}
val result = if (doWhile) JsDoWhile() else JsWhile() val result = if (doWhile) JsDoWhile() else JsWhile()
result.condition = jsCondition result.condition = jsCondition
@@ -159,7 +147,7 @@ fun translateForExpression(expression: KtForExpression, context: TranslationCont
val rangeStart = val rangeStart =
if (TranslationUtils.isCacheNeeded(leftExpression)) { if (TranslationUtils.isCacheNeeded(leftExpression)) {
val startVar = context.declareTemporary(leftExpression) val startVar = context.declareTemporary(leftExpression)
context.addStatementToCurrentBlock(startVar.assignmentExpression().makeStmt()) context.addStatementToCurrentBlock(asSyntheticStatement(startVar.assignmentExpression()))
startVar.reference() startVar.reference()
} }
else { else {
@@ -83,7 +83,6 @@ public final class PatternTranslator extends AbstractTranslator {
assert isCastExpression(expression): "Expected cast expression, got " + expression; assert isCastExpression(expression): "Expected cast expression, got " + expression;
KtExpression left = expression.getLeft(); KtExpression left = expression.getLeft();
JsExpression expressionToCast = Translation.translateAsExpression(left, context()); JsExpression expressionToCast = Translation.translateAsExpression(left, context());
if (JsAstUtils.isEmptyExpression(expressionToCast)) return expressionToCast;
TemporaryVariable temporary = context().declareTemporary(expressionToCast); TemporaryVariable temporary = context().declareTemporary(expressionToCast);
@@ -124,8 +123,6 @@ public final class PatternTranslator extends AbstractTranslator {
@Nullable @Nullable
public JsExpression translateIsCheck(@NotNull JsExpression subject, @Nullable KotlinType sourceType, public JsExpression translateIsCheck(@NotNull JsExpression subject, @Nullable KotlinType sourceType,
@NotNull KtTypeReference targetTypeReference) { @NotNull KtTypeReference targetTypeReference) {
if (JsAstUtils.isEmptyExpression(subject)) return subject;
KotlinType targetType = getTypeByReference(bindingContext(), targetTypeReference); KotlinType targetType = getTypeByReference(bindingContext(), targetTypeReference);
if (sourceType != null && !DynamicTypesKt.isDynamic(sourceType) && TypeUtilsKt.isSubtypeOf(sourceType, targetType)) return null; if (sourceType != null && !DynamicTypesKt.isDynamic(sourceType) && TypeUtilsKt.isSubtypeOf(sourceType, targetType)) return null;
@@ -17,7 +17,6 @@
package org.jetbrains.kotlin.js.translate.expression; package org.jetbrains.kotlin.js.translate.expression;
import com.google.dart.compiler.backend.js.ast.*; import com.google.dart.compiler.backend.js.ast.*;
import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.js.translate.context.TemporaryVariable; import org.jetbrains.kotlin.js.translate.context.TemporaryVariable;
@@ -27,7 +26,6 @@ import org.jetbrains.kotlin.js.translate.general.Translation;
import org.jetbrains.kotlin.js.translate.operation.InOperationTranslator; import org.jetbrains.kotlin.js.translate.operation.InOperationTranslator;
import org.jetbrains.kotlin.js.translate.utils.BindingUtils; import org.jetbrains.kotlin.js.translate.utils.BindingUtils;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.lexer.KtTokens; import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.psi.*; import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt; import org.jetbrains.kotlin.psi.psiUtil.PsiUtilsKt;
@@ -36,6 +34,7 @@ import org.jetbrains.kotlin.types.KotlinType;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.asSyntheticStatement;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.negated; import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.negated;
public final class WhenTranslator extends AbstractTranslator { public final class WhenTranslator extends AbstractTranslator {
@@ -58,14 +57,9 @@ public final class WhenTranslator extends AbstractTranslator {
KtExpression subject = expression.getSubjectExpression(); KtExpression subject = expression.getSubjectExpression();
if (subject != null) { if (subject != null) {
JsExpression subjectExpression = Translation.translateAsExpression(subject, context); JsExpression subjectExpression = Translation.translateAsExpression(subject, context);
if (!JsAstUtils.isEmptyExpression(subjectExpression)) { TemporaryVariable subjectVar = context.declareTemporary(null);
TemporaryVariable subjectVar = context.declareTemporary(null); context.addStatementToCurrentBlock(asSyntheticStatement(JsAstUtils.assignment(subjectVar.reference(), subjectExpression)));
JsExpressionStatement subjectAssignment = new JsExpressionStatement( subjectExpression = subjectVar.reference();
JsAstUtils.assignment(subjectVar.reference(), subjectExpression));
MetadataProperties.setSynthetic(subjectAssignment, true);
context.addStatementToCurrentBlock(subjectAssignment);
subjectExpression = subjectVar.reference();
}
expressionToMatch = subjectExpression; expressionToMatch = subjectExpression;
} }
else { else {
@@ -74,10 +68,6 @@ public final class WhenTranslator extends AbstractTranslator {
} }
private JsNode translate() { private JsNode translate() {
if (expressionToMatch != null && JsAstUtils.isEmptyExpression(expressionToMatch)) {
return JsEmpty.INSTANCE;
}
JsIf currentIf = null; JsIf currentIf = null;
JsIf resultIf = null; JsIf resultIf = null;
for (KtWhenEntry entry : whenExpression.getEntries()) { for (KtWhenEntry entry : whenExpression.getEntries()) {
@@ -106,7 +96,7 @@ public final class WhenTranslator extends AbstractTranslator {
currentIf = nextIf; currentIf = nextIf;
} }
} }
return resultIf != null ? resultIf : context().getEmptyExpression(); return resultIf != null ? resultIf : JsLiteral.NULL;
} }
@NotNull @NotNull
@@ -143,7 +143,7 @@ public final class Translation {
} }
block.getStatements().add(convertToStatement(jsNode)); block.getStatements().add(convertToStatement(jsNode));
return context.getEmptyExpression(); return JsLiteral.NULL;
} }
@NotNull @NotNull
@@ -46,6 +46,7 @@ import java.util.Collections;
import static org.jetbrains.kotlin.js.translate.operation.AssignmentTranslator.isAssignmentOperator; import static org.jetbrains.kotlin.js.translate.operation.AssignmentTranslator.isAssignmentOperator;
import static org.jetbrains.kotlin.js.translate.operation.CompareToTranslator.isCompareToCall; import static org.jetbrains.kotlin.js.translate.operation.CompareToTranslator.isCompareToCall;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression; import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.asSyntheticStatement;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.not; import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.not;
import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.*; import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.*;
@@ -124,9 +125,6 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
@NotNull @NotNull
private JsExpression translateElvis() { private JsExpression translateElvis() {
JsExpression leftExpression = Translation.translateAsExpression(leftKtExpression, context()); JsExpression leftExpression = Translation.translateAsExpression(leftKtExpression, context());
if (JsAstUtils.isEmptyExpression(leftExpression)) {
return leftExpression;
}
JsBlock rightBlock = new JsBlock(); JsBlock rightBlock = new JsBlock();
JsExpression rightExpression = Translation.translateAsExpression(rightKtExpression, context(), rightBlock); JsExpression rightExpression = Translation.translateAsExpression(rightKtExpression, context(), rightBlock);
@@ -141,19 +139,17 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
if (TranslationUtils.isCacheNeeded(leftExpression)) { if (TranslationUtils.isCacheNeeded(leftExpression)) {
TemporaryVariable resultVar = context().declareTemporary(leftExpression); TemporaryVariable resultVar = context().declareTemporary(leftExpression);
result = resultVar.reference(); result = resultVar.reference();
context().addStatementToCurrentBlock(resultVar.assignmentExpression().makeStmt()); context().addStatementToCurrentBlock(asSyntheticStatement(resultVar.assignmentExpression()));
} }
else { else {
result = leftExpression; result = leftExpression;
} }
JsExpression testExpression = TranslationUtils.isNullCheck(result); JsExpression testExpression = TranslationUtils.isNullCheck(result);
if (!JsAstUtils.isEmptyExpression(rightExpression)) { rightBlock.getStatements().add(JsAstUtils.assignment(result, rightExpression).makeStmt());
rightBlock.getStatements().add(JsAstUtils.assignment(result, rightExpression).makeStmt());
}
ifStatement = JsAstUtils.newJsIf(testExpression, rightBlock); ifStatement = JsAstUtils.newJsIf(testExpression, rightBlock);
} }
else { else {
result = context().getEmptyExpression(); result = JsLiteral.NULL;
JsExpression testExpression = TranslationUtils.isNullCheck(leftExpression); JsExpression testExpression = TranslationUtils.isNullCheck(leftExpression);
ifStatement = JsAstUtils.newJsIf(testExpression, rightBlock); ifStatement = JsAstUtils.newJsIf(testExpression, rightBlock);
} }
@@ -169,9 +165,6 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
@NotNull @NotNull
private JsExpression applyIntrinsic(@NotNull BinaryOperationIntrinsic intrinsic) { private JsExpression applyIntrinsic(@NotNull BinaryOperationIntrinsic intrinsic) {
JsExpression leftExpression = Translation.translateAsExpression(leftKtExpression, context()); JsExpression leftExpression = Translation.translateAsExpression(leftKtExpression, context());
if (JsAstUtils.isEmptyExpression(leftExpression)) {
return leftExpression;
}
JsBlock rightBlock = new JsBlock(); JsBlock rightBlock = new JsBlock();
JsExpression rightExpression = Translation.translateAsExpression(rightKtExpression, context(), rightBlock); JsExpression rightExpression = Translation.translateAsExpression(rightKtExpression, context(), rightBlock);
@@ -180,17 +173,9 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
return intrinsic.apply(expression, leftExpression, rightExpression, context()); return intrinsic.apply(expression, leftExpression, rightExpression, context());
} }
if (JsAstUtils.isEmptyExpression(rightExpression)) {
if (TranslationUtils.isCacheNeeded(leftExpression)) {
context().addStatementToCurrentBlock(leftExpression.makeStmt());
}
context().addStatementsToCurrentBlockFrom(rightBlock);
return context().getEmptyExpression();
}
if (TranslationUtils.isCacheNeeded(leftExpression)) { if (TranslationUtils.isCacheNeeded(leftExpression)) {
TemporaryVariable temporaryVariable = context().declareTemporary(null); TemporaryVariable temporaryVariable = context().declareTemporary(null);
context().addStatementToCurrentBlock(JsAstUtils.assignment(temporaryVariable.reference(), leftExpression).makeStmt()); context().addStatementToCurrentBlock(asSyntheticStatement(JsAstUtils.assignment(temporaryVariable.reference(), leftExpression)));
leftExpression = temporaryVariable.reference(); leftExpression = temporaryVariable.reference();
} }
context().addStatementsToCurrentBlockFrom(rightBlock); context().addStatementsToCurrentBlockFrom(rightBlock);
@@ -207,9 +192,6 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
assert OperatorConventions.NOT_OVERLOADABLE.contains(operationToken); assert OperatorConventions.NOT_OVERLOADABLE.contains(operationToken);
JsBinaryOperator operator = OperatorTable.getBinaryOperator(operationToken); JsBinaryOperator operator = OperatorTable.getBinaryOperator(operationToken);
JsExpression leftExpression = Translation.translateAsExpression(leftKtExpression, context()); JsExpression leftExpression = Translation.translateAsExpression(leftKtExpression, context());
if (JsAstUtils.isEmptyExpression(leftExpression)) {
return leftExpression;
}
JsBlock rightBlock = new JsBlock(); JsBlock rightBlock = new JsBlock();
JsExpression rightExpression = Translation.translateAsExpression(rightKtExpression, context(), rightBlock); JsExpression rightExpression = Translation.translateAsExpression(rightKtExpression, context(), rightBlock);
@@ -230,25 +212,19 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
JsIf ifStatement; JsIf ifStatement;
JsExpression result; JsExpression result;
if (BindingContextUtilsKt.isUsedAsExpression(expression, context().bindingContext())) { if (BindingContextUtilsKt.isUsedAsExpression(expression, context().bindingContext())) {
if (!JsAstUtils.isEmptyExpression(rightExpression)) { if (rightExpression instanceof JsNameRef) {
if (rightExpression instanceof JsNameRef) { result = rightExpression; // Reuse tmp variable
result = rightExpression; // Reuse tmp variable } else {
} else { TemporaryVariable resultVar = context().declareTemporary(rightExpression);
TemporaryVariable resultVar = context().declareTemporary(rightExpression); result = resultVar.reference();
result = resultVar.reference(); rightBlock.getStatements().add(resultVar.assignmentExpression().makeStmt());
rightBlock.getStatements().add(resultVar.assignmentExpression().makeStmt());
}
JsStatement assignmentStatement = JsAstUtils.assignment(result, literalResult).makeStmt();
ifStatement = JsAstUtils.newJsIf(leftExpression, rightBlock, assignmentStatement);
}
else {
ifStatement = JsAstUtils.newJsIf(leftExpression, rightBlock);
result = literalResult;
} }
JsStatement assignmentStatement = JsAstUtils.assignment(result, literalResult).makeStmt();
ifStatement = JsAstUtils.newJsIf(leftExpression, rightBlock, assignmentStatement);
} }
else { else {
ifStatement = JsAstUtils.newJsIf(leftExpression, rightBlock); ifStatement = JsAstUtils.newJsIf(leftExpression, rightBlock);
result = context().getEmptyExpression(); result = JsLiteral.NULL;
} }
context().addStatementToCurrentBlock(ifStatement); context().addStatementToCurrentBlock(ifStatement);
return result; return result;
@@ -28,6 +28,8 @@ import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.asSyntheticStatement;
public final class AccessTranslationUtils { public final class AccessTranslationUtils {
private AccessTranslationUtils() { private AccessTranslationUtils() {
} }
@@ -66,7 +68,8 @@ public final class AccessTranslationUtils {
JsExpression jsIndexExpression = Translation.translateAsExpression(indexExpression, context); JsExpression jsIndexExpression = Translation.translateAsExpression(indexExpression, context);
if (TranslationUtils.isCacheNeeded(jsIndexExpression)) { if (TranslationUtils.isCacheNeeded(jsIndexExpression)) {
TemporaryVariable temporaryVariable = context.declareTemporary(null); TemporaryVariable temporaryVariable = context.declareTemporary(null);
context.addStatementToCurrentBlock(JsAstUtils.assignment(temporaryVariable.reference(), jsIndexExpression).makeStmt()); context.addStatementToCurrentBlock(
asSyntheticStatement(JsAstUtils.assignment(temporaryVariable.reference(), jsIndexExpression)));
jsIndexExpression = temporaryVariable.reference(); jsIndexExpression = temporaryVariable.reference();
} }
indexesMap.put(indexExpression, jsIndexExpression); indexesMap.put(indexExpression, jsIndexExpression);
@@ -29,6 +29,7 @@ import org.jetbrains.kotlin.js.translate.general.AbstractTranslator
import org.jetbrains.kotlin.js.translate.general.Translation import org.jetbrains.kotlin.js.translate.general.Translation
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils import org.jetbrains.kotlin.js.translate.utils.JsAstUtils
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.asSyntheticStatement
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
import org.jetbrains.kotlin.psi.ValueArgument import org.jetbrains.kotlin.psi.ValueArgument
import org.jetbrains.kotlin.resolve.calls.model.* import org.jetbrains.kotlin.resolve.calls.model.*
@@ -51,11 +52,6 @@ class CallArgumentTranslator private constructor(
get() = reifiedArguments + valueArguments get() = reifiedArguments + valueArguments
} }
private enum class ArgumentsKind {
HAS_EMPTY_EXPRESSION_ARGUMENT,
HAS_NOT_EMPTY_EXPRESSION_ARGUMENT
}
private val isNativeFunctionCall = AnnotationsUtils.isNativeObject(resolvedCall.candidateDescriptor) private val isNativeFunctionCall = AnnotationsUtils.isNativeObject(resolvedCall.candidateDescriptor)
private fun removeLastUndefinedArguments(result: MutableList<JsExpression>) { private fun removeLastUndefinedArguments(result: MutableList<JsExpression>) {
@@ -85,7 +81,6 @@ class CallArgumentTranslator private constructor(
var argsBeforeVararg: List<JsExpression>? = null var argsBeforeVararg: List<JsExpression>? = null
var argumentsShouldBeExtractedToTmpVars = false var argumentsShouldBeExtractedToTmpVars = false
val argContexts = SmartList<TranslationContext>() val argContexts = SmartList<TranslationContext>()
var kind = ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT
var concatArguments: MutableList<JsExpression>? = null var concatArguments: MutableList<JsExpression>? = null
for (parameterDescriptor in valueParameters) { for (parameterDescriptor in valueParameters) {
@@ -107,15 +102,15 @@ class CallArgumentTranslator private constructor(
argsBeforeVararg = result argsBeforeVararg = result
result = SmartList<JsExpression>() result = SmartList<JsExpression>()
val list = SmartList<JsExpression>() val list = SmartList<JsExpression>()
kind = translateValueArguments(arguments, list, argContext) translateValueArguments(arguments, list, argContext)
concatArguments = prepareConcatArguments(arguments, list) concatArguments = prepareConcatArguments(arguments, list)
} }
else { else {
kind = translateVarargArgument(arguments, result, argContext, size > 1) translateVarargArgument(arguments, result, argContext, size > 1)
} }
} }
else { else {
kind = if (isNativeFunctionCall) { if (isNativeFunctionCall) {
translateValueArguments(arguments, result, argContext) translateValueArguments(arguments, result, argContext)
} }
else { else {
@@ -124,18 +119,16 @@ class CallArgumentTranslator private constructor(
} }
} }
else { else {
kind = translateSingleArgument(actualArgument, result, argContext) translateSingleArgument(actualArgument, result, argContext)
} }
context().moveVarsFrom(argContext) context().moveVarsFrom(argContext)
argContexts.add(argContext) argContexts.add(argContext)
argumentsShouldBeExtractedToTmpVars = argumentsShouldBeExtractedToTmpVars || !argContext.currentBlockIsEmpty() argumentsShouldBeExtractedToTmpVars = argumentsShouldBeExtractedToTmpVars || !argContext.currentBlockIsEmpty()
if (kind == ArgumentsKind.HAS_EMPTY_EXPRESSION_ARGUMENT) break
} }
if (argumentsShouldBeExtractedToTmpVars) { if (argumentsShouldBeExtractedToTmpVars) {
extractArguments(result, argContexts, context(), kind == ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT) extractArguments(result, argContexts, context())
} }
if (isNativeFunctionCall && hasSpreadOperator) { if (isNativeFunctionCall && hasSpreadOperator) {
@@ -186,12 +179,12 @@ class CallArgumentTranslator private constructor(
} }
private fun translateSingleArgument(actualArgument: ResolvedValueArgument, result: MutableList<JsExpression>, private fun translateSingleArgument(actualArgument: ResolvedValueArgument, result: MutableList<JsExpression>,
context: TranslationContext): ArgumentsKind { context: TranslationContext) {
val valueArguments = actualArgument.arguments val valueArguments = actualArgument.arguments
if (actualArgument is DefaultValueArgument) { if (actualArgument is DefaultValueArgument) {
result += Namer.getUndefinedExpression() result += Namer.getUndefinedExpression()
return ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT return
} }
assert(actualArgument is ExpressionValueArgument) assert(actualArgument is ExpressionValueArgument)
@@ -201,22 +194,15 @@ class CallArgumentTranslator private constructor(
val jsExpression = Translation.translateAsExpression(argumentExpression, context) val jsExpression = Translation.translateAsExpression(argumentExpression, context)
result.add(jsExpression) result.add(jsExpression)
if (JsAstUtils.isEmptyExpression(jsExpression)) {
return ArgumentsKind.HAS_EMPTY_EXPRESSION_ARGUMENT
}
else {
return ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT
}
} }
private fun translateVarargArgument(arguments: List<ValueArgument>, result: MutableList<JsExpression>, private fun translateVarargArgument(arguments: List<ValueArgument>, result: MutableList<JsExpression>,
context: TranslationContext, shouldWrapVarargInArray: Boolean): ArgumentsKind { context: TranslationContext, shouldWrapVarargInArray: Boolean) {
if (arguments.isEmpty()) { if (arguments.isEmpty()) {
if (shouldWrapVarargInArray) { if (shouldWrapVarargInArray) {
result.add(JsArrayLiteral(listOf<JsExpression>()).apply { sideEffects = SideEffectKind.DEPENDS_ON_STATE }) result.add(JsArrayLiteral(listOf<JsExpression>()).apply { sideEffects = SideEffectKind.DEPENDS_ON_STATE })
} }
return ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT return
} }
val list: MutableList<JsExpression> val list: MutableList<JsExpression>
@@ -242,8 +228,7 @@ class CallArgumentTranslator private constructor(
} }
private fun translateValueArguments(arguments: List<ValueArgument>, list: MutableList<JsExpression>, private fun translateValueArguments(arguments: List<ValueArgument>, list: MutableList<JsExpression>,
context: TranslationContext): ArgumentsKind { context: TranslationContext) {
var resultKind = ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT
val argContexts = SmartList<TranslationContext>() val argContexts = SmartList<TranslationContext>()
var argumentsShouldBeExtractedToTmpVars = false var argumentsShouldBeExtractedToTmpVars = false
for (argument in arguments) { for (argument in arguments) {
@@ -254,15 +239,10 @@ class CallArgumentTranslator private constructor(
context.moveVarsFrom(argContext) context.moveVarsFrom(argContext)
argContexts.add(argContext) argContexts.add(argContext)
argumentsShouldBeExtractedToTmpVars = argumentsShouldBeExtractedToTmpVars || !argContext.currentBlockIsEmpty() argumentsShouldBeExtractedToTmpVars = argumentsShouldBeExtractedToTmpVars || !argContext.currentBlockIsEmpty()
if (JsAstUtils.isEmptyExpression(argExpression)) {
resultKind = ArgumentsKind.HAS_EMPTY_EXPRESSION_ARGUMENT
break
}
} }
if (argumentsShouldBeExtractedToTmpVars) { if (argumentsShouldBeExtractedToTmpVars) {
extractArguments(list, argContexts, context, resultKind == ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT) extractArguments(list, argContexts, context)
} }
return resultKind
} }
private fun concatArgumentsIfNeeded(concatArguments: List<JsExpression>): JsExpression { private fun concatArgumentsIfNeeded(concatArguments: List<JsExpression>): JsExpression {
@@ -311,19 +291,14 @@ class CallArgumentTranslator private constructor(
} }
private fun extractArguments(argExpressions: MutableList<JsExpression>, argContexts: List<TranslationContext>, private fun extractArguments(argExpressions: MutableList<JsExpression>, argContexts: List<TranslationContext>,
context: TranslationContext, toTmpVars: Boolean) { context: TranslationContext) {
for (i in argExpressions.indices) { for (i in argExpressions.indices) {
val argContext = argContexts[i] val argContext = argContexts[i]
val jsArgExpression = argExpressions[i] val jsArgExpression = argExpressions[i]
if (argContext.currentBlockIsEmpty() && TranslationUtils.isCacheNeeded(jsArgExpression)) { if (argContext.currentBlockIsEmpty() && TranslationUtils.isCacheNeeded(jsArgExpression)) {
if (toTmpVars) { val temporaryVariable = context.declareTemporary(jsArgExpression)
val temporaryVariable = context.declareTemporary(jsArgExpression) context.addStatementToCurrentBlock(asSyntheticStatement(temporaryVariable.assignmentExpression()))
context.addStatementToCurrentBlock(temporaryVariable.assignmentExpression().makeStmt()) argExpressions[i] = temporaryVariable.reference()
argExpressions[i] = temporaryVariable.reference()
}
else {
context.addStatementToCurrentBlock(jsArgExpression.makeStmt())
}
} }
else { else {
context.addStatementsToCurrentBlockFrom(argContext) context.addStatementsToCurrentBlockFrom(argContext)
@@ -106,7 +106,7 @@ public final class CallExpressionTranslator extends AbstractCallExpressionTransl
int size = statements.size(); int size = statements.size();
if (size == 0) { if (size == 0) {
return program().getEmptyExpression(); return JsLiteral.NULL;
} else if (size > 1) { } else if (size > 1) {
return new JsBlock(statements); return new JsBlock(statements);
} else { } else {
@@ -45,7 +45,7 @@ object CallableReferenceTranslator {
private fun reportNotSupported(context: TranslationContext, expression: KtCallableReferenceExpression): JsExpression { private fun reportNotSupported(context: TranslationContext, expression: KtCallableReferenceExpression): JsExpression {
context.bindingTrace().report(ErrorsJs.REFERENCE_TO_BUILTIN_MEMBERS_NOT_SUPPORTED.on(expression, expression)) context.bindingTrace().report(ErrorsJs.REFERENCE_TO_BUILTIN_MEMBERS_NOT_SUPPORTED.on(expression, expression))
return context.emptyExpression return JsLiteral.NULL
} }
private fun translateForFunction(descriptor: FunctionDescriptor, context: TranslationContext, expression: KtCallableReferenceExpression): JsExpression { private fun translateForFunction(descriptor: FunctionDescriptor, context: TranslationContext, expression: KtCallableReferenceExpression): JsExpression {
@@ -32,6 +32,7 @@ import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import static org.jetbrains.kotlin.js.translate.general.Translation.translateAsExpression; import static org.jetbrains.kotlin.js.translate.general.Translation.translateAsExpression;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForReferenceExpression; import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getDescriptorForReferenceExpression;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.asSyntheticStatement;
import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getNotNullSimpleNameSelector; import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getNotNullSimpleNameSelector;
import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getSelector; import static org.jetbrains.kotlin.js.translate.utils.PsiUtils.getSelector;
@@ -46,7 +47,7 @@ public final class QualifiedExpressionTranslator {
JsExpression receiver = translateReceiver(expression, context); JsExpression receiver = translateReceiver(expression, context);
if (forceOrderOfEvaluation && receiver != null) { if (forceOrderOfEvaluation && receiver != null) {
TemporaryVariable temporaryVariable = context.declareTemporary(null); TemporaryVariable temporaryVariable = context.declareTemporary(null);
context.addStatementToCurrentBlock(JsAstUtils.assignment(temporaryVariable.reference(), receiver).makeStmt()); context.addStatementToCurrentBlock(asSyntheticStatement(JsAstUtils.assignment(temporaryVariable.reference(), receiver)));
receiver = temporaryVariable.reference(); receiver = temporaryVariable.reference();
} }
return VariableAccessTranslator.newInstance(context, getNotNullSimpleNameSelector(expression), receiver); return VariableAccessTranslator.newInstance(context, getNotNullSimpleNameSelector(expression), receiver);
@@ -56,7 +56,11 @@ public final class JsAstUtils {
assert (jsNode instanceof JsExpression) || (jsNode instanceof JsStatement) assert (jsNode instanceof JsExpression) || (jsNode instanceof JsStatement)
: "Unexpected node of type: " + jsNode.getClass().toString(); : "Unexpected node of type: " + jsNode.getClass().toString();
if (jsNode instanceof JsExpression) { if (jsNode instanceof JsExpression) {
return ((JsExpression) jsNode).makeStmt(); JsExpressionStatement statement = new JsExpressionStatement((JsExpression) jsNode);
if (jsNode instanceof JsNullLiteral) {
MetadataProperties.setSynthetic(statement, true);
}
return statement;
} }
return (JsStatement) jsNode; return (JsStatement) jsNode;
} }
@@ -118,10 +122,6 @@ public final class JsAstUtils {
return statement instanceof JsEmpty; return statement instanceof JsEmpty;
} }
public static boolean isEmptyExpression(@NotNull JsExpression expression) {
return expression instanceof JsEmptyExpression;
}
@NotNull @NotNull
public static JsInvocation invokeKotlinFunction(@NotNull String name, @NotNull JsExpression... argument) { public static JsInvocation invokeKotlinFunction(@NotNull String name, @NotNull JsExpression... argument) {
return invokeMethod(Namer.kotlinObject(), name, argument); return invokeMethod(Namer.kotlinObject(), name, argument);
@@ -247,7 +247,6 @@ public final class TranslationUtils {
public static boolean isCacheNeeded(@NotNull JsExpression expression) { public static boolean isCacheNeeded(@NotNull JsExpression expression) {
return !(expression instanceof JsLiteral.JsValueLiteral) && return !(expression instanceof JsLiteral.JsValueLiteral) &&
!JsAstUtils.isEmptyExpression(expression) &&
(!(expression instanceof JsNameRef) || ((JsNameRef) expression).getQualifier() != null); (!(expression instanceof JsNameRef) || ((JsNameRef) expression).getQualifier() != null);
} }