JS backend: add support for break, return, continue, throw in expressions

This commit is contained in:
Michael Nedzelsky
2014-08-26 14:08:32 +04:00
parent 2296eeef84
commit 3578845e87
26 changed files with 711 additions and 131 deletions
@@ -0,0 +1,36 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.dart.compiler.backend.js.ast;
import org.jetbrains.annotations.NotNull;
public class JsEmptyExpression extends JsExpressionImpl {
JsEmptyExpression() {
}
@Override
@NotNull
public JsStatement makeStmt() {
return new JsEmpty();
}
@Override
public void accept(JsVisitor visitor) {
throw new IllegalArgumentException("empty expression should not be here during generating Javascript code");
}
}
@@ -20,6 +20,7 @@ import static com.google.dart.compiler.backend.js.ast.JsNumberLiteral.JsIntLiter
public final class JsProgram extends SourceInfoAwareJsNode {
@NotNull
private final JsEmpty emptyStatement;
@NotNull final JsExpression emptyExpression;
private JsProgramFragment[] fragments;
@@ -36,6 +37,7 @@ public final class JsProgram extends SourceInfoAwareJsNode {
setFragmentCount(1);
emptyStatement = new JsEmpty();
emptyExpression = new JsEmptyExpression();
}
@NotNull
@@ -43,6 +45,11 @@ public final class JsProgram extends SourceInfoAwareJsNode {
return emptyStatement;
}
@NotNull
public JsExpression getEmptyExpression() {
return emptyExpression;
}
public JsBlock getFragmentBlock(int fragment) {
if (fragment < 0 || fragment >= fragments.length) {
throw new IllegalArgumentException("Invalid fragment: " + fragment);
@@ -121,4 +121,15 @@ public class EvaluationOrderTest extends AbstractExpressionTest {
checkFooBoxIsOk();
}
public void testIntrinsicWithBreakContinueReturn() throws Exception {
checkFooBoxIsOk();
}
public void testCallWithBreakContinueReturn() throws Exception {
checkFooBoxIsOk();
}
public void testLoopWithBreakContinueReturnInCondition() throws Exception {
checkFooBoxIsOk();
}
}
@@ -54,7 +54,7 @@ public final class ForeachTest extends AbstractExpressionTest {
checkFooBoxIsOk();
}
public void testForWithWhile() throws Exception {
public void testLabeledForWithWhile() throws Exception {
checkFooBoxIsOk();
}
}
@@ -192,6 +192,9 @@ object SuperCallCase : FunctionCallCase {
}
fun FunctionCallInfo.translateFunctionCall(): JsExpression {
if (this.argumentsInfo.hasEmptyExpressionArgument())
return context.getEmptyExpression()
val intrinsic = DelegateFunctionIntrinsic.intrinsic(this)
return when {
@@ -286,6 +286,16 @@ public class TranslationContext {
return dynamicContext.jsBlock();
}
@NotNull
public JsEmpty getEmptyStatement() {
return program().getEmptyStatement();
}
@NotNull
public JsExpression getEmptyExpression() {
return program().getEmptyExpression();
}
@Nullable
public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
JsNameRef nameRef = captureIfNeedAndGetCapturedName(descriptor);
@@ -96,13 +96,13 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
public JsNode visitBlockExpression(@NotNull JetBlockExpression jetBlock, @NotNull TranslationContext context) {
List<JetElement> statements = jetBlock.getStatements();
JsBlock jsBlock = new JsBlock();
TranslationContext blockContext = context.innerBlock(jsBlock);
for (JetElement statement : statements) {
assert statement instanceof JetExpression : "Elements in JetBlockExpression " +
"should be of type JetExpression";
JsNode jsNode = statement.accept(this, blockContext);
if (jsNode != null) {
jsBlock.getStatements().add(convertToStatement(jsNode));
JsNode jsNode = Translation.translateExpression((JetExpression)statement, context, jsBlock);
JsStatement jsStatement = convertToStatement(jsNode);
if (!JsAstUtils.isEmptyStatement(jsStatement)) {
jsBlock.getStatements().add(jsStatement);
}
}
return jsBlock;
@@ -121,7 +121,14 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
public JsNode visitReturnExpression(@NotNull JetReturnExpression jetReturnExpression,
@NotNull TranslationContext context) {
JetExpression returned = jetReturnExpression.getReturnedExpression();
return new JsReturn(returned != null ? translateAsExpression(returned, context) : null).source(jetReturnExpression);
if (returned == null) {
return new JsReturn(null).source(jetReturnExpression);
}
JsExpression jsReturnExpression = translateAsExpression(returned, context);
if (JsAstUtils.isEmptyExpression(jsReturnExpression)) {
return context.getEmptyExpression();
}
return new JsReturn(jsReturnExpression).source(jetReturnExpression);
}
@Override
@@ -130,12 +137,9 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@NotNull TranslationContext context) {
JetExpression expressionInside = expression.getExpression();
if (expressionInside != null) {
JsNode translated = expressionInside.accept(this, context);
if (translated != null) {
return translated;
}
return Translation.translateExpression(expressionInside, context);
}
return context.program().getEmptyStatement();
return context.getEmptyStatement();
}
@Override
@@ -151,6 +155,10 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
public JsNode visitProperty(@NotNull JetProperty expression, @NotNull TranslationContext context) {
VariableDescriptor descriptor = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.VARIABLE, expression);
JsExpression initializer = translateInitializerForProperty(expression, context);
if (initializer != null && JsAstUtils.isEmptyExpression(initializer)) {
return context.getEmptyExpression();
}
JsName name = context.getNameForDescriptor(descriptor);
if (isVarCapturedInClosure(context.bindingContext(), descriptor)) {
JsNameRef alias = getCapturedVarAccessor(name.makeRef());
@@ -184,7 +192,11 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
@Override
@NotNull
public JsNode visitIfExpression(@NotNull JetIfExpression expression, @NotNull TranslationContext context) {
JsExpression testExpression = translateConditionExpression(expression.getCondition(), context);
assert expression.getCondition() != null : "condition should not ne null: " + expression.getText();
JsExpression testExpression = Translation.translateAsExpression(expression.getCondition(), context);
if (JsAstUtils.isEmptyExpression(testExpression)) {
return testExpression;
}
boolean isKotlinExpression = BindingContextUtilPackage.isUsedAsExpression(expression, context.bindingContext());
@@ -204,8 +216,7 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
}
}
JsIf ifStatement = new JsIf(testExpression, thenStatement, elseStatement);
ifStatement.source(expression);
return isKotlinExpression ? convertToExpression(ifStatement, context) : ifStatement;
return ifStatement.source(expression);
}
@Override
@@ -215,23 +226,6 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
return ReferenceTranslator.translateSimpleNameWithQualifier(expression, null, context).source(expression);
}
@NotNull
private JsExpression translateConditionExpression(@Nullable JetExpression expression,
@NotNull TranslationContext context) {
JsExpression jsCondition = translateNullableExpression(expression, context);
assert (jsCondition != null) : "Condition should not be empty";
return convertToExpression(jsCondition, context);
}
@Nullable
private JsExpression translateNullableExpression(@Nullable JetExpression expression,
@NotNull TranslationContext context) {
if (expression == null) {
return null;
}
return convertToExpression(expression.accept(this, context), context);
}
@Override
@NotNull
public JsNode visitWhileExpression(@NotNull JetWhileExpression expression, @NotNull TranslationContext context) {
@@ -286,8 +280,9 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
) {
JetExpression baseExpression = expression.getBaseExpression();
assert baseExpression != null;
return new JsLabel(context.scope().declareName(getReferencedName(expression.getTargetLabel())),
convertToStatement(baseExpression.accept(this, context))).source(expression);
JsName name = context.scope().declareName(getReferencedName(expression.getTargetLabel()));
JsStatement baseStatement = Translation.translateAsStatement(baseExpression, context);
return new JsLabel(name, baseStatement).source(expression);
}
@Override
@@ -40,12 +40,13 @@ public fun createWhile(doWhile: Boolean, expression: JetWhileExpressionBase, con
throw IllegalArgumentException("condition expression should not be null: ${expression.getText()}")
val conditionBlock = JsBlock()
var jsCondition = Translation.translateAsExpression(conditionExpression, context, conditionBlock)
val isEmptyLoopCondition = isEmptyExpression(jsCondition)
val body = expression.getBody()
var bodyStatement =
if (body != null)
Translation.translateAsStatement(body, context)
else
context.program().getEmptyStatement()
context.getEmptyStatement()
if (!conditionBlock.isEmpty()) {
val breakIfConditionIsFalseStatement = JsIf(not(jsCondition), JsBreak())
@@ -56,18 +57,29 @@ public fun createWhile(doWhile: Boolean, expression: JetWhileExpressionBase, con
// translate to: tmpSecondRun = false; do { if(tmpSecondRun) { <expr> if(!tmpExprVar) break; } else tmpSecondRun=true; <body> } while(true)
val secondRun = context.declareTemporary(JsLiteral.FALSE)
context.addStatementToCurrentBlock(secondRun.assignmentExpression().makeStmt())
conditionBlock.getStatements().add(breakIfConditionIsFalseStatement)
if (!isEmptyLoopCondition) {
conditionBlock.getStatements().add(breakIfConditionIsFalseStatement)
}
val ifStatement = JsIf(secondRun.reference(), conditionBlock, assignment(secondRun.reference(), JsLiteral.TRUE).makeStmt())
bodyBlock.getStatements().add(0, ifStatement)
}
else {
// translate to: while (true) { <expr> if(!tmpExprVar) break; <body> }
conditionBlock.getStatements().add(breakIfConditionIsFalseStatement)
bodyBlock.getStatements().addAll(0, conditionBlock.getStatements())
if (isEmptyLoopCondition) {
bodyBlock.getStatements().clear()
context.addStatementsToCurrentBlockFrom(conditionBlock)
}
else {
conditionBlock.getStatements().add(breakIfConditionIsFalseStatement)
bodyBlock.getStatements().addAll(0, conditionBlock.getStatements())
}
}
bodyStatement = bodyBlock
}
else if (isEmptyLoopCondition) {
jsCondition = JsLiteral.FALSE
}
val result = if (doWhile) JsDoWhile() else JsWhile()
result.setCondition(jsCondition)
@@ -89,7 +89,7 @@ public final class TryTranslator extends AbstractTranslator {
private JsBlock translateCatchBody(@NotNull JetCatchClause catchClause) {
JetExpression catchBody = catchClause.getCatchBody();
if (catchBody == null) {
return convertToBlock(program().getEmptyStatement());
return convertToBlock(context().getEmptyStatement());
}
return convertToBlock(Translation.translateAsStatement(catchBody, context()));
}
@@ -20,7 +20,6 @@ import com.google.dart.compiler.backend.js.ast.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage;
import org.jetbrains.k2js.translate.context.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
@@ -28,19 +27,12 @@ import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.convertToExpression;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.negated;
public final class WhenTranslator extends AbstractTranslator {
@Nullable
public static JsNode translate(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) {
WhenTranslator translator = new WhenTranslator(expression, context);
if (BindingContextUtilPackage.isUsedAsStatement(expression, context.bindingContext())) {
return translator.translateAsStatement();
}
return translator.translateAsExpression();
return new WhenTranslator(expression, context).translate();
}
@NotNull
@@ -69,12 +61,11 @@ public final class WhenTranslator extends AbstractTranslator {
}
}
@Nullable
private JsNode translateAsExpression() {
return convertToExpression(translateAsStatement(), context());
}
private JsStatement translate() {
if (expressionToMatch != null && JsAstUtils.isEmptyExpression(expressionToMatch)) {
return context().getEmptyStatement();
}
private JsStatement translateAsStatement() {
JsIf currentIf = null;
JsIf resultIf = null;
for (JetWhenEntry entry : whenExpression.getEntries()) {
@@ -88,7 +79,7 @@ public final class WhenTranslator extends AbstractTranslator {
statement = JsAstUtils.mergeStatementInBlockIfNeeded(statement, statementBlock);
if (resultIf == null) {
currentIf = new JsIf(translateConditions(entry, context()), statement);
currentIf = JsAstUtils.newJsIf(translateConditions(entry, context()), statement);
resultIf = currentIf;
}
else {
@@ -97,7 +88,7 @@ public final class WhenTranslator extends AbstractTranslator {
return resultIf;
}
JsBlock conditionsBlock = new JsBlock();
JsIf nextIf = new JsIf(translateConditions(entry, context().innerBlock(conditionsBlock)), statement);
JsIf nextIf = JsAstUtils.newJsIf(translateConditions(entry, context().innerBlock(conditionsBlock)), statement);
JsStatement statementToAdd = JsAstUtils.mergeStatementInBlockIfNeeded(nextIf, conditionsBlock);
currentIf.setElseStatement(statementToAdd);
currentIf = nextIf;
@@ -144,7 +135,8 @@ public final class WhenTranslator extends AbstractTranslator {
} else {
assert rightExpression instanceof JsNameRef : "expected JsNameRef, but: " + rightExpression;
JsNameRef result = (JsNameRef) rightExpression;
JsIf ifStatement = new JsIf(leftExpression, JsAstUtils.assignment(result, JsLiteral.TRUE).makeStmt(), rightContext.getCurrentBlock());
JsIf ifStatement = JsAstUtils.newJsIf(leftExpression, JsAstUtils.assignment(result, JsLiteral.TRUE).makeStmt(),
rightContext.getCurrentBlock());
context.addStatementToCurrentBlock(ifStatement);
return result;
}
@@ -26,6 +26,7 @@ import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.psi.JetNamedFunction;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage;
import org.jetbrains.jet.plugin.MainFunctionDetector;
import org.jetbrains.k2js.config.Config;
import org.jetbrains.k2js.facade.MainCallParameters;
@@ -36,6 +37,7 @@ import org.jetbrains.k2js.facade.exceptions.UnsupportedFeatureException;
import org.jetbrains.k2js.translate.callTranslator.CallTranslator;
import org.jetbrains.k2js.translate.context.Namer;
import org.jetbrains.k2js.translate.context.StaticContext;
import org.jetbrains.k2js.translate.context.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.declaration.PackageDeclarationTranslator;
import org.jetbrains.k2js.translate.expression.ExpressionVisitor;
@@ -46,6 +48,7 @@ import org.jetbrains.k2js.translate.test.JSTestGenerator;
import org.jetbrains.k2js.translate.test.JSTester;
import org.jetbrains.k2js.translate.test.QUnitTester;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
import org.jetbrains.k2js.translate.utils.mutator.AssignToExpressionMutator;
import java.util.Collection;
import java.util.Collections;
@@ -53,6 +56,7 @@ import java.util.List;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptor;
import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
import static org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression;
/**
* This class provides a interface which all translators use to interact with each other.
@@ -101,7 +105,7 @@ public final class Translation {
@NotNull
public static JsExpression translateAsExpression(@NotNull JetExpression expression, @NotNull TranslationContext context) {
return convertToExpression(translateExpression(expression, context), context);
return translateAsExpression(expression, context, context.dynamicContext().jsBlock());
}
@NotNull
@@ -110,7 +114,21 @@ public final class Translation {
@NotNull TranslationContext context,
@NotNull JsBlock block
) {
return convertToExpression(translateExpression(expression, context, block), context, block);
JsNode jsNode = translateExpression(expression, context, block);
if (jsNode instanceof JsExpression) {
return (JsExpression) jsNode;
}
assert jsNode instanceof JsStatement : "Unexpected node of type: " + jsNode.getClass().toString();
if (BindingContextUtilPackage.isUsedAsExpression(expression, context.bindingContext())) {
TemporaryVariable result = context.declareTemporary(null);
AssignToExpressionMutator saveResultToTemporaryMutator = new AssignToExpressionMutator(result.reference());
block.getStatements().add(mutateLastExpression(jsNode, saveResultToTemporaryMutator));
return result.reference();
}
block.getStatements().add(convertToStatement(jsNode));
return context.getEmptyExpression();
}
@NotNull
@@ -23,6 +23,7 @@ import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
import org.jetbrains.jet.lang.psi.JetBinaryExpression;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
import org.jetbrains.jet.lexer.JetToken;
@@ -111,25 +112,41 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
@NotNull
private JsExpression translateElvis() {
JsExpression leftExpression = Translation.translateAsExpression(leftJetExpression, context());
JsBlock rightBlock = new JsBlock();
JsNode rightNode = Translation.translateExpression(rightJetExpression, context(), rightBlock);
if (rightNode instanceof JsExpression && rightBlock.isEmpty()) {
return TranslationUtils.notNullConditional(leftExpression, (JsExpression) rightNode, context());
if (JsAstUtils.isEmptyExpression(leftExpression)) {
return leftExpression;
}
TemporaryVariable result = context().declareTemporary(leftExpression);
context().addStatementToCurrentBlock(result.assignmentExpression().makeStmt());
JsBlock rightBlock = new JsBlock();
JsExpression rightExpression = Translation.translateAsExpression(rightJetExpression, context(), rightBlock);
JsExpression testExpression = TranslationUtils.isNullCheck(result.reference());
JsExpression rightExpression = JsAstUtils.convertToExpression(rightNode, context(), rightBlock);
rightBlock.getStatements().add(JsAstUtils.assignment(result.reference(), rightExpression).makeStmt());
if (rightBlock.isEmpty()) {
return TranslationUtils.notNullConditional(leftExpression, rightExpression, context());
}
JsIf ifStatement = new JsIf(testExpression, rightBlock);
JsExpression result;
JsIf ifStatement;
if (BindingContextUtilPackage.isUsedAsExpression(expression, context().bindingContext())) {
if (TranslationUtils.isCacheNeeded(leftExpression)) {
TemporaryVariable resultVar = context().declareTemporary(leftExpression);
result = resultVar.reference();
context().addStatementToCurrentBlock(resultVar.assignmentExpression().makeStmt());
}
else {
result = leftExpression;
}
JsExpression testExpression = TranslationUtils.isNullCheck(result);
if (!JsAstUtils.isEmptyExpression(rightExpression)) {
rightBlock.getStatements().add(JsAstUtils.assignment(result, rightExpression).makeStmt());
}
ifStatement = JsAstUtils.newJsIf(testExpression, rightBlock);
}
else {
result = context().getEmptyExpression();
JsExpression testExpression = TranslationUtils.isNullCheck(leftExpression);
ifStatement = JsAstUtils.newJsIf(testExpression, rightBlock);
}
context().addStatementToCurrentBlock(ifStatement);
return result.reference();
return result;
}
@Nullable
@@ -140,6 +157,9 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
@NotNull
private JsExpression applyIntrinsic(@NotNull BinaryOperationIntrinsic intrinsic) {
JsExpression leftExpression = Translation.translateAsExpression(leftJetExpression, context());
if (JsAstUtils.isEmptyExpression(leftExpression)) {
return leftExpression;
}
JsBlock rightBlock = new JsBlock();
JsExpression rightExpression = Translation.translateAsExpression(rightJetExpression, context(), rightBlock);
@@ -148,6 +168,14 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
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)) {
TemporaryVariable temporaryVariable = context().declareTemporary(null);
context().addStatementToCurrentBlock(JsAstUtils.assignment(temporaryVariable.reference(), leftExpression).makeStmt());
@@ -168,6 +196,10 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
assert OperatorConventions.NOT_OVERLOADABLE.contains(token);
JsBinaryOperator operator = OperatorTable.getBinaryOperator(token);
JsExpression leftExpression = Translation.translateAsExpression(leftJetExpression, context());
if (JsAstUtils.isEmptyExpression(leftExpression)) {
return leftExpression;
}
JsBlock rightBlock = new JsBlock();
JsExpression rightExpression = Translation.translateAsExpression(rightJetExpression, context(), rightBlock);
@@ -175,26 +207,34 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
return new JsBinaryOperation(operator, leftExpression, rightExpression);
}
JsNameRef result;
if (rightExpression instanceof JsNameRef) {
result = (JsNameRef) rightExpression; // Reuse tmp variable
} else {
TemporaryVariable resultVar = context().declareTemporary(rightExpression);
result = resultVar.reference();
rightBlock.getStatements().add(resultVar.assignmentExpression().makeStmt());
}
assert token.equals(JetTokens.ANDAND) || token.equals(JetTokens.OROR) : "Unsupported binary operation: " + expression.getText();
boolean isOror = token.equals(JetTokens.OROR);
JsExpression literalResult = isOror ? JsLiteral.TRUE : JsLiteral.FALSE;
leftExpression = isOror ? not(leftExpression) : leftExpression;
JsIf ifStatement;
if (token.equals(JetTokens.ANDAND)) {
ifStatement = new JsIf(leftExpression, rightBlock, JsAstUtils.assignment(result, JsLiteral.FALSE).makeStmt());
}
else if (token.equals(JetTokens.OROR)) {
ifStatement = new JsIf(leftExpression, JsAstUtils.assignment(result, JsLiteral.TRUE).makeStmt(), rightBlock);
JsExpression result;
if (BindingContextUtilPackage.isUsedAsExpression(expression, context().bindingContext())) {
if (!JsAstUtils.isEmptyExpression(rightExpression)) {
if (rightExpression instanceof JsNameRef) {
result = rightExpression; // Reuse tmp variable
} else {
TemporaryVariable resultVar = context().declareTemporary(rightExpression);
result = resultVar.reference();
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;
}
}
else {
throw new UnsupportedOperationException("Unsupported binary operation: " + expression.getText());
ifStatement = JsAstUtils.newJsIf(leftExpression, rightBlock);
result = context().getEmptyExpression();
}
context().addStatementToCurrentBlock(ifStatement);
return result;
}
@@ -25,6 +25,7 @@ import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
import org.jetbrains.jet.lexer.JetToken;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
import static org.jetbrains.k2js.translate.utils.PsiUtils.getOperationToken;
import static org.jetbrains.k2js.translate.utils.PsiUtils.isAssignment;
@@ -45,6 +46,10 @@ public final class IntrinsicAssignmentTranslator extends AssignmentTranslator {
@NotNull
private JsExpression translate() {
if (JsAstUtils.isEmptyExpression(right)) {
return right;
}
if (isAssignment(getOperationToken(expression))) {
return translateAsPlainAssignment();
}
@@ -24,6 +24,7 @@ import org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
import org.jetbrains.k2js.translate.callTranslator.CallTranslator;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
public final class OverloadedAssignmentTranslator extends AssignmentTranslator {
@@ -44,6 +45,10 @@ public final class OverloadedAssignmentTranslator extends AssignmentTranslator {
@NotNull
private JsExpression translate() {
if (JsAstUtils.isEmptyExpression(right)) {
return right;
}
if (isVariableReassignment) {
return reassignment();
}
@@ -30,6 +30,8 @@ import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.translate.utils.AnnotationsUtils;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
import org.jetbrains.k2js.translate.utils.TranslationUtils;
import java.util.ArrayList;
import java.util.Collections;
@@ -64,11 +66,17 @@ public class CallArgumentTranslator extends AbstractTranslator {
private final List<JsExpression> translateArguments;
private final boolean hasSpreadOperator;
private final TemporaryConstVariable cachedReceiver;
private final boolean hasEmptyExpressionArgument;
public ArgumentsInfo(List<JsExpression> arguments, boolean operator, TemporaryConstVariable receiver) {
this(arguments, operator, receiver, false);
}
public ArgumentsInfo(List<JsExpression> arguments, boolean operator, TemporaryConstVariable receiver, boolean hasEmptyExpressionArgument) {
translateArguments = arguments;
hasSpreadOperator = operator;
cachedReceiver = receiver;
this.hasEmptyExpressionArgument = hasEmptyExpressionArgument;
}
@NotNull
@@ -84,9 +92,16 @@ public class CallArgumentTranslator extends AbstractTranslator {
public TemporaryConstVariable getCachedReceiver() {
return cachedReceiver;
}
public boolean hasEmptyExpressionArgument() {
return hasEmptyExpressionArgument;
}
}
public static void translateSingleArgument(
public static enum ArgumentsKind { HAS_EMPTY_EXPRESSION_ARGUMENT, HAS_NOT_EMPTY_EXPRESSION_ARGUMENT }
@NotNull
public static ArgumentsKind translateSingleArgument(
@NotNull ResolvedValueArgument actualArgument,
@NotNull List<JsExpression> result,
@NotNull TranslationContext context,
@@ -94,31 +109,41 @@ public class CallArgumentTranslator extends AbstractTranslator {
) {
List<ValueArgument> valueArguments = actualArgument.getArguments();
if (actualArgument instanceof VarargValueArgument) {
translateVarargArgument(valueArguments, result, context, shouldWrapVarargInArray);
return translateVarargArgument(valueArguments, result, context, shouldWrapVarargInArray);
}
else if (actualArgument instanceof DefaultValueArgument) {
result.add(context.namer().getUndefinedExpression());
return ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT;
}
else {
assert actualArgument instanceof ExpressionValueArgument;
assert valueArguments.size() == 1;
JetExpression argumentExpression = valueArguments.get(0).getArgumentExpression();
assert argumentExpression != null;
result.add(Translation.translateAsExpression(argumentExpression, context));
JsExpression jsExpression = Translation.translateAsExpression(argumentExpression, context);
result.add(jsExpression);
if (JsAstUtils.isEmptyExpression(jsExpression)) {
return ArgumentsKind.HAS_EMPTY_EXPRESSION_ARGUMENT;
}
else {
return ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT;
}
}
}
private static void translateVarargArgument(
@NotNull
private static ArgumentsKind translateVarargArgument(
@NotNull List<ValueArgument> arguments,
@NotNull List<JsExpression> result,
@NotNull TranslationContext context,
boolean shouldWrapVarargInArray
) {
ArgumentsKind resultKind = ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT;
if (arguments.isEmpty()) {
if (shouldWrapVarargInArray) {
result.add(new JsArrayLiteral(Collections.<JsExpression>emptyList()));
}
return;
return ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT;
}
List<JsExpression> list;
@@ -140,10 +165,15 @@ public class CallArgumentTranslator extends AbstractTranslator {
context.moveVarsFrom(argContext);
argContexts.add(argContext);
argumentsShouldBeExtractedToTmpVars = argumentsShouldBeExtractedToTmpVars || !argContext.currentBlockIsEmpty();
if (JsAstUtils.isEmptyExpression(argExpression)) {
resultKind = ArgumentsKind.HAS_EMPTY_EXPRESSION_ARGUMENT;
break;
}
}
if (argumentsShouldBeExtractedToTmpVars) {
extractArgumentsToTmpVars(list, argContexts, context);
}
return resultKind;
}
private static void extractArgumentsToTmpVars(
@@ -154,7 +184,7 @@ public class CallArgumentTranslator extends AbstractTranslator {
for(int i=0; i<argExpressions.size(); i++) {
TranslationContext argContext = argContexts.get(i);
JsExpression jsArgExpression = argExpressions.get(i);
if (argContext.currentBlockIsEmpty()) {
if (argContext.currentBlockIsEmpty() && TranslationUtils.isCacheNeeded(jsArgExpression)) {
TemporaryVariable temporaryVariable = context.declareTemporary(jsArgExpression);
context.addStatementToCurrentBlock(temporaryVariable.assignmentExpression().makeStmt());
argExpressions.set(i, temporaryVariable.reference());
@@ -192,6 +222,7 @@ public class CallArgumentTranslator extends AbstractTranslator {
}
private ArgumentsInfo translate() {
boolean hasEmptyExpressionArgument = false;
List<ValueParameterDescriptor> valueParameters = resolvedCall.getResultingDescriptor().getValueParameters();
if (valueParameters.isEmpty()) {
return new ArgumentsInfo(Collections.<JsExpression>emptyList(), false, null);
@@ -223,10 +254,15 @@ public class CallArgumentTranslator extends AbstractTranslator {
}
}
TranslationContext argContext = context().innerBlock();
translateSingleArgument(actualArgument, result, argContext, !isNativeFunctionCall && !hasSpreadOperator);
ArgumentsKind kind = translateSingleArgument(actualArgument, result, argContext, !isNativeFunctionCall && !hasSpreadOperator);
context().moveVarsFrom(argContext);
argContexts.add(argContext);
argumentsShouldBeExtractedToTmpVars = argumentsShouldBeExtractedToTmpVars || !argContext.currentBlockIsEmpty();
if (kind == ArgumentsKind.HAS_EMPTY_EXPRESSION_ARGUMENT) {
hasEmptyExpressionArgument = true;
break;
}
}
if (argumentsShouldBeExtractedToTmpVars) {
@@ -249,7 +285,7 @@ public class CallArgumentTranslator extends AbstractTranslator {
}
removeLastUndefinedArguments(result);
return new ArgumentsInfo(result, hasSpreadOperator, cachedReceiver);
return new ArgumentsInfo(result, hasSpreadOperator, cachedReceiver, hasEmptyExpressionArgument);
}
}
@@ -60,7 +60,7 @@ public final class FunctionBodyTranslator extends AbstractTranslator {
JsStatement assignStatement = assignment(jsNameRef, defaultValue).makeStmt();
JsStatement thenStatement = JsAstUtils.mergeStatementInBlockIfNeeded(assignStatement, defaultArgBlock);
JsBinaryOperation checkArgIsUndefined = equality(jsNameRef, functionBodyContext.namer().getUndefinedExpression());
JsIf jsIf = new JsIf(checkArgIsUndefined, thenStatement);
JsIf jsIf = JsAstUtils.newJsIf(checkArgIsUndefined, thenStatement);
result.add(jsIf);
}
}
@@ -21,16 +21,12 @@ import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.k2js.translate.context.Namer;
import org.jetbrains.k2js.translate.context.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.utils.mutator.AssignToExpressionMutator;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression;
public final class JsAstUtils {
private static final JsNameRef DEFINE_PROPERTY = new JsNameRef("defineProperty");
public static final JsNameRef CREATE_OBJECT = new JsNameRef("create");
@@ -71,22 +67,28 @@ public final class JsAstUtils {
}
@NotNull
public static JsExpression convertToExpression(@NotNull JsNode jsNode, @NotNull TranslationContext context) {
return convertToExpression(jsNode, context, context.dynamicContext().jsBlock());
private static JsStatement deBlockIfPossible(@NotNull JsStatement statement) {
if (statement instanceof JsBlock && ((JsBlock)statement).getStatements().size() == 1) {
return ((JsBlock)statement).getStatements().get(0);
}
else {
return statement;
}
}
@NotNull
public static JsExpression convertToExpression(@NotNull JsNode jsNode, @NotNull TranslationContext context, @NotNull JsBlock block) {
if (jsNode instanceof JsExpression) {
return (JsExpression) jsNode;
}
else {
assert jsNode instanceof JsStatement : "Unexpected node of type: " + jsNode.getClass().toString();
TemporaryVariable result = context.declareTemporary(null);
AssignToExpressionMutator saveResultToTemporaryMutator = new AssignToExpressionMutator(result.reference());
block.getStatements().add(mutateLastExpression(jsNode, saveResultToTemporaryMutator));
return result.reference();
}
public static JsIf newJsIf(
@NotNull JsExpression ifExpression,
@NotNull JsStatement thenStatement,
@Nullable JsStatement elseStatement
) {
elseStatement = elseStatement != null ? deBlockIfPossible(elseStatement) : null;
return new JsIf(ifExpression, deBlockIfPossible(thenStatement), elseStatement);
}
@NotNull
public static JsIf newJsIf(@NotNull JsExpression ifExpression, @NotNull JsStatement thenStatement) {
return newJsIf(ifExpression, thenStatement, null);
}
@Nullable
@@ -99,11 +101,22 @@ public final class JsAstUtils {
if (block.isEmpty()) {
return statement;
} else {
if (isEmptyStatement(statement)) {
return deBlockIfPossible(block);
}
block.getStatements().add(statement);
return block;
}
}
public static boolean isEmptyStatement(@NotNull JsStatement statement) {
return statement instanceof JsEmpty;
}
public static boolean isEmptyExpression(@NotNull JsExpression expression) {
return expression instanceof JsEmptyExpression;
}
@NotNull
public static JsPrefixOperation negated(@NotNull JsExpression expression) {
return new JsPrefixOperation(JsUnaryOperator.NOT, expression);
@@ -398,6 +398,7 @@ public final class TranslationUtils {
public static boolean isCacheNeeded(@NotNull JsExpression expression) {
return !(expression instanceof JsLiteral.JsValueLiteral) &&
!JsAstUtils.isEmptyExpression(expression) &&
(!(expression instanceof JsNameRef) || ((JsNameRef) expression).getQualifier() != null);
}
@@ -1,46 +1,62 @@
package foo
var global: String = ""
fun id(s: String, b: Boolean): Boolean {
global += s
return b
}
fun bar(b: Boolean): String {
if (b && return "A")
if (id("A", b) && return "A")
return "B"
else
return "C"
}
fun testBreak(b: Boolean, expected: Int) {
global = ""
var i = 0
while (i++ < 5) {
if (i == 2) b && break
if (i == 2) id("A", b) && break
}
assertEquals(expected, i, "break 1")
assertEquals("A", global, "break 1")
global = ""
i = 0
while (i++ < 5) {
if (i == 2) {
var x = b && break
var x = id("A", b) && break
}
}
assertEquals(expected, i, "break 2")
assertEquals("A", global, "break 2")
}
fun testContinue(b: Boolean, expected: Int) {
global = ""
var i = 0
var n = 0
while (i++ < 5) {
if (i == 2) b && continue
if (i == 2) id("A", b) && continue
n++
}
assertEquals(expected, n, "continue 1")
assertEquals("A", global, "continue 1")
global = ""
i = 0
n = 0
while (i++ < 5) {
if (i == 2) {
var x = b && continue
var x = id("A", b) && continue
assertEquals(x, b)
}
n++
}
assertEquals(expected, n, "continue 2")
assertEquals("A", global, "continue 2")
}
fun box(): String {
@@ -49,11 +65,33 @@ fun box(): String {
testBreak(true, 2)
testBreak(false, 6)
global = ""
var i = 0
while (i++ < 5) {
if (i == 2) break && id("A", false)
}
assertEquals(2, i, "break && false")
assertEquals("", global, "break && false")
testContinue(true, 4)
testContinue(false, 5)
assertEquals("A", bar(true))
global = ""
i = 0
var n = 0
while (i++ < 5) {
if (i == 2) continue && id("A", false)
n++
}
assertEquals(4, n, "continue && false")
assertEquals("", global, "continue && false")
assertEquals("A", bar(true), "bar")
assertEquals("A", global, "bar")
global = ""
assertEquals("C", bar(false))
assertEquals("A", global, "bar")
return "OK"
}
@@ -0,0 +1,88 @@
package foo
var global: String = ""
fun f(arg1: String, arg2: String, arg3: String): String {
global += ":f:"
return arg1 + arg2 + arg3
}
fun id(s: String): String {
global += s
return s
}
fun bar(s: String): String {
return f(id(s), if (true) return ":bar:" else return "b", id(s))
}
fun box(): String {
var b: Boolean
var i = 0
while (i++ < 5) {
if (i == 2) f(id("A"), break, id("B"))
}
assertEquals(2, i, "break 1")
assertEquals("A", global, "break 1")
global = ""
i = 0
while (i++ < 5) {
if (i == 2) {
var x = f(id("A"), break, id("B"))
}
}
assertEquals(2, i, "break 2")
assertEquals("A", global, "break 2")
global = ""
i = 0
while (i++ < 5) {
if (i == 2) {
var x = "B" + f(id("A"), break, id("B"))
}
}
assertEquals(2, i, "break 3")
assertEquals("A", global, "break 3")
global = ""
i = 0
var n = 0
while (i++ < 5) {
if (i == 2) f(id("A"), continue, id("B"))
n++
}
assertEquals(4, n, "continue 1")
assertEquals("A", global, "continue 1")
global = ""
i = 0
n = 0
while (i++ < 5) {
if (i == 2) {
var x = f(id("A"), continue, id("B"))
}
n++
}
assertEquals(4, n, "continue 2")
assertEquals("A", global, "continue 2")
global = ""
i = 0
n = 0
while (i++ < 5) {
if (i == 2) {
var x = "B" + f(id("A"), continue, id("B"))
}
n++
}
assertEquals(4, n, "continue 3")
assertEquals("A", global, "continue 3")
global = ""
assertEquals(":bar:", bar("A"))
assertEquals("A", global, "bar")
return "OK"
}
@@ -11,14 +11,14 @@ fun bar(a: A?): String {
fun testBreak(a: A?, expected: Int) {
var i = 0
while (i++ < 5) {
if (i == 2) a ?: break
while(i++<5) {
if (i==2) a ?: break
}
assertEquals(expected, i, "break 1")
i = 0
while (i++ < 5) {
if (i == 2) {
while(i++<5) {
if (i==2) {
var x = a ?: break
}
}
@@ -28,16 +28,16 @@ fun testBreak(a: A?, expected: Int) {
fun testContinue(a: A?, expected: Int) {
var i = 0
var n = 0
while (i++ < 5) {
if (i == 2) a ?: continue
while(i++<5) {
if (i==2) a ?: continue
n++
}
assertEquals(expected, n)
i = 0
n = 0
while (i++ < 5) {
if (i == 2) {
while(i++<5) {
if (i==2) {
var x = a ?: continue
}
n++
@@ -50,9 +50,23 @@ fun box(): String {
testBreak(null, 2)
testBreak(A(), 6)
var i = 0
while(i++<5) {
if (i==2) break ?: null
}
assertEquals(2, i, "break ?: null")
testContinue(null, 4)
testContinue(A(), 5)
i = 0
var n = 0
while(i++<5) {
if (i==2) continue ?: null
n++
}
assertEquals(4, n, "continue ?: null")
assertEquals("A", bar(null))
assertEquals("B", bar(A()))
@@ -0,0 +1,119 @@
package foo
var global: String = ""
class A {
var prop: Boolean = false
}
fun getA(): A = try { global += "getA"; A() } finally {}
fun barEq(): String {
10 == return "eq"
}
fun barLt(): String {
10 < (return "lt") as Int
}
fun box(): String {
var b: Boolean
var i = 0
while(i++<5) {
if (i==2) 10 == break
}
assertEquals(2, i, "break 1")
i = 0
while(i++<5) {
if (i==2) 10 < (break as Int)
}
assertEquals(2, i, "break 2")
i = 0
while(i++<5) {
if (i==2) {
var x = 10 == break;
}
}
assertEquals(2, i, "break 3")
i = 0
while(i++<5) {
if (i==2) {
var x = 10 < (break as Int)
}
}
assertEquals(2, i, "break 4")
i = 0
var bVar: Boolean
while(i++<5) {
if (i==2) {
bVar = 10 < (break as Int)
}
}
assertEquals(2, i, "break 5")
i = 0
var bVarArray = array(true, false)
while(i++<5) {
if (i==2) {
bVarArray[try { global += "A"; 0} finally {}] = 10 < (break as Int)
}
}
assertEquals(2, i, "break 6")
assertEquals("A", global, "break 6")
i = 0
global = ""
while(i++<5) {
if (i==2) {
getA().prop = 10 < (break as Int)
}
}
assertEquals(2, i, "break 7")
assertEquals("getA", global, "break 7")
i = 0
var n = 0
while(i++<5) {
if (i==2) 10 == continue
n++
}
assertEquals(4, n, "continue 1")
i = 0
n = 0
while(i++<5) {
if (i==2) 10 < (continue as Int)
n++
}
assertEquals(4, n, "continue 2")
i = 0
n = 0
while(i++<5) {
if (i==2) {
var x = 10 == continue
}
n++
}
assertEquals(4, n, "continue 3")
i = 0
n = 0
while(i++<5) {
if (i==2) {
var x = 10 < (continue as Int)
}
n++
}
assertEquals(4, n, "continue 4")
assertEquals("eq", barEq())
assertEquals("lt", barLt())
return "OK"
}
@@ -0,0 +1,98 @@
package foo
var global: String = ""
fun whileReturn(): String {
var i = 0
while(if (i<2) true else { global += ":return:"; return ":whileReturn:"}) {
i++
global += "A"
}
return ":whileReturn:after:"
}
fun whileImmediateReturn(): String {
var i = 0
while(return ":whileImmediateReturn:") {
global += "A"
}
return ":whileImmediateReturn:after"
}
fun doWhileReturn(): String {
var i = 0
do {
i++
global += "A"
}
while(if (i<2) true else { global += ":return:"; return ":doWhileReturn:"})
return ":doWhileReturn:after:"
}
fun doWhileReturnFromCondition(): String {
var i = 0
do {
global += "A"
} while(return ":doWhileReturnFromCondition:")
return ":doWhileReturnFromCondition:after"
}
fun doWhileImmediateReturn(): String {
var i = 0
do {
return ":doWhileImmediateReturn:"
global += "A"
} while(false)
return ":doWhileImmediateReturn:after"
}
fun forReturn(b: Boolean): String {
var i = 0
for(i in (if (b) 1..2 else { global += ":return:"; return ":forReturn:"})) {
global += "A"
}
return ":forReturn:after:"
}
fun box(): String {
var i = 0
// KT-5704 'while' detected as unreachable though loop is running
// TODO Remove or uncomment when issue will be fixed
//while(if (++i==10) break else continue) {}
//assertEquals(10, i)
i = 0
do { i++ } while(if (++i==10) break else continue)
assertEquals(10, i)
assertEquals(":whileReturn:", whileReturn())
assertEquals("AA:return:", global)
global = ""
assertEquals(":whileImmediateReturn:", whileImmediateReturn())
assertEquals("", global)
global = ""
assertEquals(":doWhileReturn:", doWhileReturn())
assertEquals("AA:return:", global)
global = ""
assertEquals(":doWhileReturnFromCondition:", doWhileReturnFromCondition())
assertEquals("A", global)
global = ""
assertEquals(":doWhileImmediateReturn:", doWhileImmediateReturn())
assertEquals("", global)
global = ""
assertEquals(":forReturn:after:", forReturn(true))
assertEquals("AA", global)
global = ""
assertEquals(":forReturn:", forReturn(false))
assertEquals(":return:", global)
return "OK"
}
@@ -1,46 +1,61 @@
package foo
var global: String = ""
fun id(s: String, b: Boolean): Boolean {
global += s
return b
}
fun bar(b: Boolean): String {
if (b || return "A")
if (id("A",b) || return "A")
return "B"
else
return "C"
}
fun testBreak(b: Boolean, expected: Int) {
global = ""
var i = 0
while (i++ < 5) {
if (i == 2) b || break
if (i == 2) id("A", b) || break
}
assertEquals(expected, i, "break 1")
assertEquals("A", global, "break 1")
global = ""
i = 0
while (i++ < 5) {
if (i == 2) {
var x = b || break
var x = id("A", b) || break
}
}
assertEquals(expected, i, "break 2")
assertEquals("A", global, "break 2")
}
fun testContinue(b: Boolean, expected: Int) {
global = ""
var i = 0
var n = 0
while (i++ < 5) {
if (i == 2) b || continue
if (i == 2) id("A", b) || continue
n++
}
assertEquals(expected, n, "continue 1")
assertEquals("A", global, "continue 1")
global = ""
i = 0
n = 0
while (i++ < 5) {
if (i == 2) {
var x = b || continue
var x = id("A", b) || continue
}
n++
}
assertEquals(expected, n, "continue 2")
assertEquals("A", global, "continue 2")
}
fun box(): String {
@@ -48,11 +63,32 @@ fun box(): String {
testBreak(true, 6)
testBreak(false, 2)
global = ""
var i = 0
while (i++ < 5) {
if (i == 2) break || id("A", true)
}
assertEquals(2, i, "break || true")
assertEquals("", global, "break || false")
testContinue(true, 5)
testContinue(false, 4)
global = ""
i = 0
var n = 0
while (i++ < 5) {
if (i == 2) continue || id("A", true)
n++
}
assertEquals(4, n, "continue || true")
assertEquals("", global, "continue || true")
assertEquals("B", bar(true))
assertEquals("A", global, "bar")
global = ""
assertEquals("A", bar(false))
assertEquals("A", global, "bar")
return "OK"
}
@@ -11,6 +11,9 @@ fun box(): String {
val a = if (true || if(bar("A", false)) {2} else {3} == 0) { }
assertEquals("", global)
true || if(bar("A", false)) {2} else {3} == 0
assertEquals("", global)
// Simple || Simple
global = ""
assertEquals(true, bar("A", true) || bar("B", true))