JS backend: add support for break, return, continue, throw in expressions
This commit is contained in:
@@ -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);
|
||||
|
||||
+27
-32
@@ -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
|
||||
|
||||
+69
-29
@@ -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;
|
||||
}
|
||||
|
||||
+5
@@ -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();
|
||||
}
|
||||
|
||||
+5
@@ -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();
|
||||
}
|
||||
|
||||
+44
-8
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+44
-6
@@ -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"
|
||||
}
|
||||
+88
@@ -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"
|
||||
}
|
||||
+22
-8
@@ -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()))
|
||||
|
||||
|
||||
+119
@@ -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"
|
||||
}
|
||||
+98
@@ -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"
|
||||
}
|
||||
+41
-5
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user