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 {
|
public final class JsProgram extends SourceInfoAwareJsNode {
|
||||||
@NotNull
|
@NotNull
|
||||||
private final JsEmpty emptyStatement;
|
private final JsEmpty emptyStatement;
|
||||||
|
@NotNull final JsExpression emptyExpression;
|
||||||
|
|
||||||
private JsProgramFragment[] fragments;
|
private JsProgramFragment[] fragments;
|
||||||
|
|
||||||
@@ -36,6 +37,7 @@ public final class JsProgram extends SourceInfoAwareJsNode {
|
|||||||
setFragmentCount(1);
|
setFragmentCount(1);
|
||||||
|
|
||||||
emptyStatement = new JsEmpty();
|
emptyStatement = new JsEmpty();
|
||||||
|
emptyExpression = new JsEmptyExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -43,6 +45,11 @@ public final class JsProgram extends SourceInfoAwareJsNode {
|
|||||||
return emptyStatement;
|
return emptyStatement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public JsExpression getEmptyExpression() {
|
||||||
|
return emptyExpression;
|
||||||
|
}
|
||||||
|
|
||||||
public JsBlock getFragmentBlock(int fragment) {
|
public JsBlock getFragmentBlock(int fragment) {
|
||||||
if (fragment < 0 || fragment >= fragments.length) {
|
if (fragment < 0 || fragment >= fragments.length) {
|
||||||
throw new IllegalArgumentException("Invalid fragment: " + fragment);
|
throw new IllegalArgumentException("Invalid fragment: " + fragment);
|
||||||
|
|||||||
@@ -121,4 +121,15 @@ public class EvaluationOrderTest extends AbstractExpressionTest {
|
|||||||
checkFooBoxIsOk();
|
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();
|
checkFooBoxIsOk();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testForWithWhile() throws Exception {
|
public void testLabeledForWithWhile() throws Exception {
|
||||||
checkFooBoxIsOk();
|
checkFooBoxIsOk();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -192,6 +192,9 @@ object SuperCallCase : FunctionCallCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun FunctionCallInfo.translateFunctionCall(): JsExpression {
|
fun FunctionCallInfo.translateFunctionCall(): JsExpression {
|
||||||
|
if (this.argumentsInfo.hasEmptyExpressionArgument())
|
||||||
|
return context.getEmptyExpression()
|
||||||
|
|
||||||
val intrinsic = DelegateFunctionIntrinsic.intrinsic(this)
|
val intrinsic = DelegateFunctionIntrinsic.intrinsic(this)
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
|
|||||||
@@ -286,6 +286,16 @@ public class TranslationContext {
|
|||||||
return dynamicContext.jsBlock();
|
return dynamicContext.jsBlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public JsEmpty getEmptyStatement() {
|
||||||
|
return program().getEmptyStatement();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public JsExpression getEmptyExpression() {
|
||||||
|
return program().getEmptyExpression();
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
|
public JsExpression getAliasForDescriptor(@NotNull DeclarationDescriptor descriptor) {
|
||||||
JsNameRef nameRef = captureIfNeedAndGetCapturedName(descriptor);
|
JsNameRef nameRef = captureIfNeedAndGetCapturedName(descriptor);
|
||||||
|
|||||||
+27
-32
@@ -96,13 +96,13 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
|||||||
public JsNode visitBlockExpression(@NotNull JetBlockExpression jetBlock, @NotNull TranslationContext context) {
|
public JsNode visitBlockExpression(@NotNull JetBlockExpression jetBlock, @NotNull TranslationContext context) {
|
||||||
List<JetElement> statements = jetBlock.getStatements();
|
List<JetElement> statements = jetBlock.getStatements();
|
||||||
JsBlock jsBlock = new JsBlock();
|
JsBlock jsBlock = new JsBlock();
|
||||||
TranslationContext blockContext = context.innerBlock(jsBlock);
|
|
||||||
for (JetElement statement : statements) {
|
for (JetElement statement : statements) {
|
||||||
assert statement instanceof JetExpression : "Elements in JetBlockExpression " +
|
assert statement instanceof JetExpression : "Elements in JetBlockExpression " +
|
||||||
"should be of type JetExpression";
|
"should be of type JetExpression";
|
||||||
JsNode jsNode = statement.accept(this, blockContext);
|
JsNode jsNode = Translation.translateExpression((JetExpression)statement, context, jsBlock);
|
||||||
if (jsNode != null) {
|
JsStatement jsStatement = convertToStatement(jsNode);
|
||||||
jsBlock.getStatements().add(convertToStatement(jsNode));
|
if (!JsAstUtils.isEmptyStatement(jsStatement)) {
|
||||||
|
jsBlock.getStatements().add(jsStatement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return jsBlock;
|
return jsBlock;
|
||||||
@@ -121,7 +121,14 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
|||||||
public JsNode visitReturnExpression(@NotNull JetReturnExpression jetReturnExpression,
|
public JsNode visitReturnExpression(@NotNull JetReturnExpression jetReturnExpression,
|
||||||
@NotNull TranslationContext context) {
|
@NotNull TranslationContext context) {
|
||||||
JetExpression returned = jetReturnExpression.getReturnedExpression();
|
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
|
@Override
|
||||||
@@ -130,12 +137,9 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
|||||||
@NotNull TranslationContext context) {
|
@NotNull TranslationContext context) {
|
||||||
JetExpression expressionInside = expression.getExpression();
|
JetExpression expressionInside = expression.getExpression();
|
||||||
if (expressionInside != null) {
|
if (expressionInside != null) {
|
||||||
JsNode translated = expressionInside.accept(this, context);
|
return Translation.translateExpression(expressionInside, context);
|
||||||
if (translated != null) {
|
|
||||||
return translated;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return context.program().getEmptyStatement();
|
return context.getEmptyStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -151,6 +155,10 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
|||||||
public JsNode visitProperty(@NotNull JetProperty expression, @NotNull TranslationContext context) {
|
public JsNode visitProperty(@NotNull JetProperty expression, @NotNull TranslationContext context) {
|
||||||
VariableDescriptor descriptor = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.VARIABLE, expression);
|
VariableDescriptor descriptor = BindingContextUtils.getNotNull(context.bindingContext(), BindingContext.VARIABLE, expression);
|
||||||
JsExpression initializer = translateInitializerForProperty(expression, context);
|
JsExpression initializer = translateInitializerForProperty(expression, context);
|
||||||
|
if (initializer != null && JsAstUtils.isEmptyExpression(initializer)) {
|
||||||
|
return context.getEmptyExpression();
|
||||||
|
}
|
||||||
|
|
||||||
JsName name = context.getNameForDescriptor(descriptor);
|
JsName name = context.getNameForDescriptor(descriptor);
|
||||||
if (isVarCapturedInClosure(context.bindingContext(), descriptor)) {
|
if (isVarCapturedInClosure(context.bindingContext(), descriptor)) {
|
||||||
JsNameRef alias = getCapturedVarAccessor(name.makeRef());
|
JsNameRef alias = getCapturedVarAccessor(name.makeRef());
|
||||||
@@ -184,7 +192,11 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
|||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsNode visitIfExpression(@NotNull JetIfExpression expression, @NotNull TranslationContext context) {
|
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());
|
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);
|
JsIf ifStatement = new JsIf(testExpression, thenStatement, elseStatement);
|
||||||
ifStatement.source(expression);
|
return ifStatement.source(expression);
|
||||||
return isKotlinExpression ? convertToExpression(ifStatement, context) : ifStatement;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -215,23 +226,6 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
|||||||
return ReferenceTranslator.translateSimpleNameWithQualifier(expression, null, context).source(expression);
|
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
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public JsNode visitWhileExpression(@NotNull JetWhileExpression expression, @NotNull TranslationContext context) {
|
public JsNode visitWhileExpression(@NotNull JetWhileExpression expression, @NotNull TranslationContext context) {
|
||||||
@@ -286,8 +280,9 @@ public final class ExpressionVisitor extends TranslatorVisitor<JsNode> {
|
|||||||
) {
|
) {
|
||||||
JetExpression baseExpression = expression.getBaseExpression();
|
JetExpression baseExpression = expression.getBaseExpression();
|
||||||
assert baseExpression != null;
|
assert baseExpression != null;
|
||||||
return new JsLabel(context.scope().declareName(getReferencedName(expression.getTargetLabel())),
|
JsName name = context.scope().declareName(getReferencedName(expression.getTargetLabel()));
|
||||||
convertToStatement(baseExpression.accept(this, context))).source(expression);
|
JsStatement baseStatement = Translation.translateAsStatement(baseExpression, context);
|
||||||
|
return new JsLabel(name, baseStatement).source(expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -40,12 +40,13 @@ public fun createWhile(doWhile: Boolean, expression: JetWhileExpressionBase, con
|
|||||||
throw IllegalArgumentException("condition expression should not be null: ${expression.getText()}")
|
throw IllegalArgumentException("condition expression should not be null: ${expression.getText()}")
|
||||||
val conditionBlock = JsBlock()
|
val conditionBlock = JsBlock()
|
||||||
var jsCondition = Translation.translateAsExpression(conditionExpression, context, conditionBlock)
|
var jsCondition = Translation.translateAsExpression(conditionExpression, context, conditionBlock)
|
||||||
|
val isEmptyLoopCondition = isEmptyExpression(jsCondition)
|
||||||
val body = expression.getBody()
|
val body = expression.getBody()
|
||||||
var bodyStatement =
|
var bodyStatement =
|
||||||
if (body != null)
|
if (body != null)
|
||||||
Translation.translateAsStatement(body, context)
|
Translation.translateAsStatement(body, context)
|
||||||
else
|
else
|
||||||
context.program().getEmptyStatement()
|
context.getEmptyStatement()
|
||||||
|
|
||||||
if (!conditionBlock.isEmpty()) {
|
if (!conditionBlock.isEmpty()) {
|
||||||
val breakIfConditionIsFalseStatement = JsIf(not(jsCondition), JsBreak())
|
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)
|
// translate to: tmpSecondRun = false; do { if(tmpSecondRun) { <expr> if(!tmpExprVar) break; } else tmpSecondRun=true; <body> } while(true)
|
||||||
val secondRun = context.declareTemporary(JsLiteral.FALSE)
|
val secondRun = context.declareTemporary(JsLiteral.FALSE)
|
||||||
context.addStatementToCurrentBlock(secondRun.assignmentExpression().makeStmt())
|
context.addStatementToCurrentBlock(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())
|
val ifStatement = JsIf(secondRun.reference(), conditionBlock, assignment(secondRun.reference(), JsLiteral.TRUE).makeStmt())
|
||||||
bodyBlock.getStatements().add(0, ifStatement)
|
bodyBlock.getStatements().add(0, ifStatement)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// translate to: while (true) { <expr> if(!tmpExprVar) break; <body> }
|
// translate to: while (true) { <expr> if(!tmpExprVar) break; <body> }
|
||||||
conditionBlock.getStatements().add(breakIfConditionIsFalseStatement)
|
if (isEmptyLoopCondition) {
|
||||||
bodyBlock.getStatements().addAll(0, conditionBlock.getStatements())
|
bodyBlock.getStatements().clear()
|
||||||
|
context.addStatementsToCurrentBlockFrom(conditionBlock)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
conditionBlock.getStatements().add(breakIfConditionIsFalseStatement)
|
||||||
|
bodyBlock.getStatements().addAll(0, conditionBlock.getStatements())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bodyStatement = bodyBlock
|
bodyStatement = bodyBlock
|
||||||
}
|
}
|
||||||
|
else if (isEmptyLoopCondition) {
|
||||||
|
jsCondition = JsLiteral.FALSE
|
||||||
|
}
|
||||||
|
|
||||||
val result = if (doWhile) JsDoWhile() else JsWhile()
|
val result = if (doWhile) JsDoWhile() else JsWhile()
|
||||||
result.setCondition(jsCondition)
|
result.setCondition(jsCondition)
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ public final class TryTranslator extends AbstractTranslator {
|
|||||||
private JsBlock translateCatchBody(@NotNull JetCatchClause catchClause) {
|
private JsBlock translateCatchBody(@NotNull JetCatchClause catchClause) {
|
||||||
JetExpression catchBody = catchClause.getCatchBody();
|
JetExpression catchBody = catchClause.getCatchBody();
|
||||||
if (catchBody == null) {
|
if (catchBody == null) {
|
||||||
return convertToBlock(program().getEmptyStatement());
|
return convertToBlock(context().getEmptyStatement());
|
||||||
}
|
}
|
||||||
return convertToBlock(Translation.translateAsStatement(catchBody, context()));
|
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.jet.lang.psi.*;
|
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.TemporaryVariable;
|
||||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
import org.jetbrains.k2js.translate.context.TranslationContext;
|
||||||
import org.jetbrains.k2js.translate.general.AbstractTranslator;
|
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.JsAstUtils;
|
||||||
import org.jetbrains.k2js.translate.utils.TranslationUtils;
|
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;
|
import static org.jetbrains.k2js.translate.utils.JsAstUtils.negated;
|
||||||
|
|
||||||
public final class WhenTranslator extends AbstractTranslator {
|
public final class WhenTranslator extends AbstractTranslator {
|
||||||
@Nullable
|
@Nullable
|
||||||
public static JsNode translate(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) {
|
public static JsNode translate(@NotNull JetWhenExpression expression, @NotNull TranslationContext context) {
|
||||||
WhenTranslator translator = new WhenTranslator(expression, context);
|
return new WhenTranslator(expression, context).translate();
|
||||||
|
|
||||||
if (BindingContextUtilPackage.isUsedAsStatement(expression, context.bindingContext())) {
|
|
||||||
return translator.translateAsStatement();
|
|
||||||
}
|
|
||||||
|
|
||||||
return translator.translateAsExpression();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -69,12 +61,11 @@ public final class WhenTranslator extends AbstractTranslator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
private JsStatement translate() {
|
||||||
private JsNode translateAsExpression() {
|
if (expressionToMatch != null && JsAstUtils.isEmptyExpression(expressionToMatch)) {
|
||||||
return convertToExpression(translateAsStatement(), context());
|
return context().getEmptyStatement();
|
||||||
}
|
}
|
||||||
|
|
||||||
private JsStatement translateAsStatement() {
|
|
||||||
JsIf currentIf = null;
|
JsIf currentIf = null;
|
||||||
JsIf resultIf = null;
|
JsIf resultIf = null;
|
||||||
for (JetWhenEntry entry : whenExpression.getEntries()) {
|
for (JetWhenEntry entry : whenExpression.getEntries()) {
|
||||||
@@ -88,7 +79,7 @@ public final class WhenTranslator extends AbstractTranslator {
|
|||||||
statement = JsAstUtils.mergeStatementInBlockIfNeeded(statement, statementBlock);
|
statement = JsAstUtils.mergeStatementInBlockIfNeeded(statement, statementBlock);
|
||||||
|
|
||||||
if (resultIf == null) {
|
if (resultIf == null) {
|
||||||
currentIf = new JsIf(translateConditions(entry, context()), statement);
|
currentIf = JsAstUtils.newJsIf(translateConditions(entry, context()), statement);
|
||||||
resultIf = currentIf;
|
resultIf = currentIf;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -97,7 +88,7 @@ public final class WhenTranslator extends AbstractTranslator {
|
|||||||
return resultIf;
|
return resultIf;
|
||||||
}
|
}
|
||||||
JsBlock conditionsBlock = new JsBlock();
|
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);
|
JsStatement statementToAdd = JsAstUtils.mergeStatementInBlockIfNeeded(nextIf, conditionsBlock);
|
||||||
currentIf.setElseStatement(statementToAdd);
|
currentIf.setElseStatement(statementToAdd);
|
||||||
currentIf = nextIf;
|
currentIf = nextIf;
|
||||||
@@ -144,7 +135,8 @@ public final class WhenTranslator extends AbstractTranslator {
|
|||||||
} else {
|
} else {
|
||||||
assert rightExpression instanceof JsNameRef : "expected JsNameRef, but: " + rightExpression;
|
assert rightExpression instanceof JsNameRef : "expected JsNameRef, but: " + rightExpression;
|
||||||
JsNameRef result = (JsNameRef) 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);
|
context.addStatementToCurrentBlock(ifStatement);
|
||||||
return result;
|
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.JetFile;
|
||||||
import org.jetbrains.jet.lang.psi.JetNamedFunction;
|
import org.jetbrains.jet.lang.psi.JetNamedFunction;
|
||||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||||
|
import org.jetbrains.jet.lang.resolve.bindingContextUtil.BindingContextUtilPackage;
|
||||||
import org.jetbrains.jet.plugin.MainFunctionDetector;
|
import org.jetbrains.jet.plugin.MainFunctionDetector;
|
||||||
import org.jetbrains.k2js.config.Config;
|
import org.jetbrains.k2js.config.Config;
|
||||||
import org.jetbrains.k2js.facade.MainCallParameters;
|
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.callTranslator.CallTranslator;
|
||||||
import org.jetbrains.k2js.translate.context.Namer;
|
import org.jetbrains.k2js.translate.context.Namer;
|
||||||
import org.jetbrains.k2js.translate.context.StaticContext;
|
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.context.TranslationContext;
|
||||||
import org.jetbrains.k2js.translate.declaration.PackageDeclarationTranslator;
|
import org.jetbrains.k2js.translate.declaration.PackageDeclarationTranslator;
|
||||||
import org.jetbrains.k2js.translate.expression.ExpressionVisitor;
|
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.JSTester;
|
||||||
import org.jetbrains.k2js.translate.test.QUnitTester;
|
import org.jetbrains.k2js.translate.test.QUnitTester;
|
||||||
import org.jetbrains.k2js.translate.utils.JsAstUtils;
|
import org.jetbrains.k2js.translate.utils.JsAstUtils;
|
||||||
|
import org.jetbrains.k2js.translate.utils.mutator.AssignToExpressionMutator;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
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.BindingUtils.getFunctionDescriptor;
|
||||||
import static org.jetbrains.k2js.translate.utils.JsAstUtils.*;
|
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.
|
* This class provides a interface which all translators use to interact with each other.
|
||||||
@@ -101,7 +105,7 @@ public final class Translation {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static JsExpression translateAsExpression(@NotNull JetExpression expression, @NotNull TranslationContext context) {
|
public static JsExpression translateAsExpression(@NotNull JetExpression expression, @NotNull TranslationContext context) {
|
||||||
return convertToExpression(translateExpression(expression, context), context);
|
return translateAsExpression(expression, context, context.dynamicContext().jsBlock());
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -110,7 +114,21 @@ public final class Translation {
|
|||||||
@NotNull TranslationContext context,
|
@NotNull TranslationContext context,
|
||||||
@NotNull JsBlock block
|
@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
|
@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.descriptors.FunctionDescriptor;
|
||||||
import org.jetbrains.jet.lang.psi.JetBinaryExpression;
|
import org.jetbrains.jet.lang.psi.JetBinaryExpression;
|
||||||
import org.jetbrains.jet.lang.psi.JetExpression;
|
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.resolve.calls.model.ResolvedCall;
|
||||||
import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
|
import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
|
||||||
import org.jetbrains.jet.lexer.JetToken;
|
import org.jetbrains.jet.lexer.JetToken;
|
||||||
@@ -111,25 +112,41 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private JsExpression translateElvis() {
|
private JsExpression translateElvis() {
|
||||||
JsExpression leftExpression = Translation.translateAsExpression(leftJetExpression, context());
|
JsExpression leftExpression = Translation.translateAsExpression(leftJetExpression, context());
|
||||||
|
if (JsAstUtils.isEmptyExpression(leftExpression)) {
|
||||||
JsBlock rightBlock = new JsBlock();
|
return leftExpression;
|
||||||
JsNode rightNode = Translation.translateExpression(rightJetExpression, context(), rightBlock);
|
|
||||||
|
|
||||||
if (rightNode instanceof JsExpression && rightBlock.isEmpty()) {
|
|
||||||
return TranslationUtils.notNullConditional(leftExpression, (JsExpression) rightNode, context());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TemporaryVariable result = context().declareTemporary(leftExpression);
|
JsBlock rightBlock = new JsBlock();
|
||||||
context().addStatementToCurrentBlock(result.assignmentExpression().makeStmt());
|
JsExpression rightExpression = Translation.translateAsExpression(rightJetExpression, context(), rightBlock);
|
||||||
|
|
||||||
JsExpression testExpression = TranslationUtils.isNullCheck(result.reference());
|
if (rightBlock.isEmpty()) {
|
||||||
JsExpression rightExpression = JsAstUtils.convertToExpression(rightNode, context(), rightBlock);
|
return TranslationUtils.notNullConditional(leftExpression, rightExpression, context());
|
||||||
rightBlock.getStatements().add(JsAstUtils.assignment(result.reference(), rightExpression).makeStmt());
|
}
|
||||||
|
|
||||||
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);
|
context().addStatementToCurrentBlock(ifStatement);
|
||||||
|
return result;
|
||||||
return result.reference();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -140,6 +157,9 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private JsExpression applyIntrinsic(@NotNull BinaryOperationIntrinsic intrinsic) {
|
private JsExpression applyIntrinsic(@NotNull BinaryOperationIntrinsic intrinsic) {
|
||||||
JsExpression leftExpression = Translation.translateAsExpression(leftJetExpression, context());
|
JsExpression leftExpression = Translation.translateAsExpression(leftJetExpression, context());
|
||||||
|
if (JsAstUtils.isEmptyExpression(leftExpression)) {
|
||||||
|
return leftExpression;
|
||||||
|
}
|
||||||
|
|
||||||
JsBlock rightBlock = new JsBlock();
|
JsBlock rightBlock = new JsBlock();
|
||||||
JsExpression rightExpression = Translation.translateAsExpression(rightJetExpression, context(), rightBlock);
|
JsExpression rightExpression = Translation.translateAsExpression(rightJetExpression, context(), rightBlock);
|
||||||
@@ -148,6 +168,14 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
|
|||||||
return intrinsic.apply(expression, leftExpression, rightExpression, context());
|
return intrinsic.apply(expression, leftExpression, rightExpression, context());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (JsAstUtils.isEmptyExpression(rightExpression)) {
|
||||||
|
if (TranslationUtils.isCacheNeeded(leftExpression)) {
|
||||||
|
context().addStatementToCurrentBlock(leftExpression.makeStmt());
|
||||||
|
}
|
||||||
|
context().addStatementsToCurrentBlockFrom(rightBlock);
|
||||||
|
return context().getEmptyExpression();
|
||||||
|
}
|
||||||
|
|
||||||
if (TranslationUtils.isCacheNeeded(leftExpression)) {
|
if (TranslationUtils.isCacheNeeded(leftExpression)) {
|
||||||
TemporaryVariable temporaryVariable = context().declareTemporary(null);
|
TemporaryVariable temporaryVariable = context().declareTemporary(null);
|
||||||
context().addStatementToCurrentBlock(JsAstUtils.assignment(temporaryVariable.reference(), leftExpression).makeStmt());
|
context().addStatementToCurrentBlock(JsAstUtils.assignment(temporaryVariable.reference(), leftExpression).makeStmt());
|
||||||
@@ -168,6 +196,10 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
|
|||||||
assert OperatorConventions.NOT_OVERLOADABLE.contains(token);
|
assert OperatorConventions.NOT_OVERLOADABLE.contains(token);
|
||||||
JsBinaryOperator operator = OperatorTable.getBinaryOperator(token);
|
JsBinaryOperator operator = OperatorTable.getBinaryOperator(token);
|
||||||
JsExpression leftExpression = Translation.translateAsExpression(leftJetExpression, context());
|
JsExpression leftExpression = Translation.translateAsExpression(leftJetExpression, context());
|
||||||
|
if (JsAstUtils.isEmptyExpression(leftExpression)) {
|
||||||
|
return leftExpression;
|
||||||
|
}
|
||||||
|
|
||||||
JsBlock rightBlock = new JsBlock();
|
JsBlock rightBlock = new JsBlock();
|
||||||
JsExpression rightExpression = Translation.translateAsExpression(rightJetExpression, context(), rightBlock);
|
JsExpression rightExpression = Translation.translateAsExpression(rightJetExpression, context(), rightBlock);
|
||||||
|
|
||||||
@@ -175,26 +207,34 @@ public final class BinaryOperationTranslator extends AbstractTranslator {
|
|||||||
return new JsBinaryOperation(operator, leftExpression, rightExpression);
|
return new JsBinaryOperation(operator, leftExpression, rightExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
JsNameRef result;
|
assert token.equals(JetTokens.ANDAND) || token.equals(JetTokens.OROR) : "Unsupported binary operation: " + expression.getText();
|
||||||
if (rightExpression instanceof JsNameRef) {
|
boolean isOror = token.equals(JetTokens.OROR);
|
||||||
result = (JsNameRef) rightExpression; // Reuse tmp variable
|
JsExpression literalResult = isOror ? JsLiteral.TRUE : JsLiteral.FALSE;
|
||||||
} else {
|
leftExpression = isOror ? not(leftExpression) : leftExpression;
|
||||||
TemporaryVariable resultVar = context().declareTemporary(rightExpression);
|
|
||||||
result = resultVar.reference();
|
|
||||||
rightBlock.getStatements().add(resultVar.assignmentExpression().makeStmt());
|
|
||||||
}
|
|
||||||
|
|
||||||
JsIf ifStatement;
|
JsIf ifStatement;
|
||||||
if (token.equals(JetTokens.ANDAND)) {
|
JsExpression result;
|
||||||
ifStatement = new JsIf(leftExpression, rightBlock, JsAstUtils.assignment(result, JsLiteral.FALSE).makeStmt());
|
if (BindingContextUtilPackage.isUsedAsExpression(expression, context().bindingContext())) {
|
||||||
}
|
if (!JsAstUtils.isEmptyExpression(rightExpression)) {
|
||||||
else if (token.equals(JetTokens.OROR)) {
|
if (rightExpression instanceof JsNameRef) {
|
||||||
ifStatement = new JsIf(leftExpression, JsAstUtils.assignment(result, JsLiteral.TRUE).makeStmt(), rightBlock);
|
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 {
|
else {
|
||||||
throw new UnsupportedOperationException("Unsupported binary operation: " + expression.getText());
|
ifStatement = JsAstUtils.newJsIf(leftExpression, rightBlock);
|
||||||
|
result = context().getEmptyExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
context().addStatementToCurrentBlock(ifStatement);
|
context().addStatementToCurrentBlock(ifStatement);
|
||||||
return result;
|
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.lang.types.expressions.OperatorConventions;
|
||||||
import org.jetbrains.jet.lexer.JetToken;
|
import org.jetbrains.jet.lexer.JetToken;
|
||||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
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.getOperationToken;
|
||||||
import static org.jetbrains.k2js.translate.utils.PsiUtils.isAssignment;
|
import static org.jetbrains.k2js.translate.utils.PsiUtils.isAssignment;
|
||||||
@@ -45,6 +46,10 @@ public final class IntrinsicAssignmentTranslator extends AssignmentTranslator {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private JsExpression translate() {
|
private JsExpression translate() {
|
||||||
|
if (JsAstUtils.isEmptyExpression(right)) {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
|
||||||
if (isAssignment(getOperationToken(expression))) {
|
if (isAssignment(getOperationToken(expression))) {
|
||||||
return translateAsPlainAssignment();
|
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.jet.lang.resolve.calls.model.ResolvedCall;
|
||||||
import org.jetbrains.k2js.translate.callTranslator.CallTranslator;
|
import org.jetbrains.k2js.translate.callTranslator.CallTranslator;
|
||||||
import org.jetbrains.k2js.translate.context.TranslationContext;
|
import org.jetbrains.k2js.translate.context.TranslationContext;
|
||||||
|
import org.jetbrains.k2js.translate.utils.JsAstUtils;
|
||||||
|
|
||||||
public final class OverloadedAssignmentTranslator extends AssignmentTranslator {
|
public final class OverloadedAssignmentTranslator extends AssignmentTranslator {
|
||||||
|
|
||||||
@@ -44,6 +45,10 @@ public final class OverloadedAssignmentTranslator extends AssignmentTranslator {
|
|||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private JsExpression translate() {
|
private JsExpression translate() {
|
||||||
|
if (JsAstUtils.isEmptyExpression(right)) {
|
||||||
|
return right;
|
||||||
|
}
|
||||||
|
|
||||||
if (isVariableReassignment) {
|
if (isVariableReassignment) {
|
||||||
return reassignment();
|
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.AbstractTranslator;
|
||||||
import org.jetbrains.k2js.translate.general.Translation;
|
import org.jetbrains.k2js.translate.general.Translation;
|
||||||
import org.jetbrains.k2js.translate.utils.AnnotationsUtils;
|
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.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -64,11 +66,17 @@ public class CallArgumentTranslator extends AbstractTranslator {
|
|||||||
private final List<JsExpression> translateArguments;
|
private final List<JsExpression> translateArguments;
|
||||||
private final boolean hasSpreadOperator;
|
private final boolean hasSpreadOperator;
|
||||||
private final TemporaryConstVariable cachedReceiver;
|
private final TemporaryConstVariable cachedReceiver;
|
||||||
|
private final boolean hasEmptyExpressionArgument;
|
||||||
|
|
||||||
public ArgumentsInfo(List<JsExpression> arguments, boolean operator, TemporaryConstVariable receiver) {
|
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;
|
translateArguments = arguments;
|
||||||
hasSpreadOperator = operator;
|
hasSpreadOperator = operator;
|
||||||
cachedReceiver = receiver;
|
cachedReceiver = receiver;
|
||||||
|
this.hasEmptyExpressionArgument = hasEmptyExpressionArgument;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
@@ -84,9 +92,16 @@ public class CallArgumentTranslator extends AbstractTranslator {
|
|||||||
public TemporaryConstVariable getCachedReceiver() {
|
public TemporaryConstVariable getCachedReceiver() {
|
||||||
return cachedReceiver;
|
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 ResolvedValueArgument actualArgument,
|
||||||
@NotNull List<JsExpression> result,
|
@NotNull List<JsExpression> result,
|
||||||
@NotNull TranslationContext context,
|
@NotNull TranslationContext context,
|
||||||
@@ -94,31 +109,41 @@ public class CallArgumentTranslator extends AbstractTranslator {
|
|||||||
) {
|
) {
|
||||||
List<ValueArgument> valueArguments = actualArgument.getArguments();
|
List<ValueArgument> valueArguments = actualArgument.getArguments();
|
||||||
if (actualArgument instanceof VarargValueArgument) {
|
if (actualArgument instanceof VarargValueArgument) {
|
||||||
translateVarargArgument(valueArguments, result, context, shouldWrapVarargInArray);
|
return translateVarargArgument(valueArguments, result, context, shouldWrapVarargInArray);
|
||||||
}
|
}
|
||||||
else if (actualArgument instanceof DefaultValueArgument) {
|
else if (actualArgument instanceof DefaultValueArgument) {
|
||||||
result.add(context.namer().getUndefinedExpression());
|
result.add(context.namer().getUndefinedExpression());
|
||||||
|
return ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assert actualArgument instanceof ExpressionValueArgument;
|
assert actualArgument instanceof ExpressionValueArgument;
|
||||||
assert valueArguments.size() == 1;
|
assert valueArguments.size() == 1;
|
||||||
JetExpression argumentExpression = valueArguments.get(0).getArgumentExpression();
|
JetExpression argumentExpression = valueArguments.get(0).getArgumentExpression();
|
||||||
assert argumentExpression != null;
|
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<ValueArgument> arguments,
|
||||||
@NotNull List<JsExpression> result,
|
@NotNull List<JsExpression> result,
|
||||||
@NotNull TranslationContext context,
|
@NotNull TranslationContext context,
|
||||||
boolean shouldWrapVarargInArray
|
boolean shouldWrapVarargInArray
|
||||||
) {
|
) {
|
||||||
|
ArgumentsKind resultKind = ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT;
|
||||||
if (arguments.isEmpty()) {
|
if (arguments.isEmpty()) {
|
||||||
if (shouldWrapVarargInArray) {
|
if (shouldWrapVarargInArray) {
|
||||||
result.add(new JsArrayLiteral(Collections.<JsExpression>emptyList()));
|
result.add(new JsArrayLiteral(Collections.<JsExpression>emptyList()));
|
||||||
}
|
}
|
||||||
return;
|
return ArgumentsKind.HAS_NOT_EMPTY_EXPRESSION_ARGUMENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<JsExpression> list;
|
List<JsExpression> list;
|
||||||
@@ -140,10 +165,15 @@ public class CallArgumentTranslator extends AbstractTranslator {
|
|||||||
context.moveVarsFrom(argContext);
|
context.moveVarsFrom(argContext);
|
||||||
argContexts.add(argContext);
|
argContexts.add(argContext);
|
||||||
argumentsShouldBeExtractedToTmpVars = argumentsShouldBeExtractedToTmpVars || !argContext.currentBlockIsEmpty();
|
argumentsShouldBeExtractedToTmpVars = argumentsShouldBeExtractedToTmpVars || !argContext.currentBlockIsEmpty();
|
||||||
|
if (JsAstUtils.isEmptyExpression(argExpression)) {
|
||||||
|
resultKind = ArgumentsKind.HAS_EMPTY_EXPRESSION_ARGUMENT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (argumentsShouldBeExtractedToTmpVars) {
|
if (argumentsShouldBeExtractedToTmpVars) {
|
||||||
extractArgumentsToTmpVars(list, argContexts, context);
|
extractArgumentsToTmpVars(list, argContexts, context);
|
||||||
}
|
}
|
||||||
|
return resultKind;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void extractArgumentsToTmpVars(
|
private static void extractArgumentsToTmpVars(
|
||||||
@@ -154,7 +184,7 @@ public class CallArgumentTranslator extends AbstractTranslator {
|
|||||||
for(int i=0; i<argExpressions.size(); i++) {
|
for(int i=0; i<argExpressions.size(); i++) {
|
||||||
TranslationContext argContext = argContexts.get(i);
|
TranslationContext argContext = argContexts.get(i);
|
||||||
JsExpression jsArgExpression = argExpressions.get(i);
|
JsExpression jsArgExpression = argExpressions.get(i);
|
||||||
if (argContext.currentBlockIsEmpty()) {
|
if (argContext.currentBlockIsEmpty() && TranslationUtils.isCacheNeeded(jsArgExpression)) {
|
||||||
TemporaryVariable temporaryVariable = context.declareTemporary(jsArgExpression);
|
TemporaryVariable temporaryVariable = context.declareTemporary(jsArgExpression);
|
||||||
context.addStatementToCurrentBlock(temporaryVariable.assignmentExpression().makeStmt());
|
context.addStatementToCurrentBlock(temporaryVariable.assignmentExpression().makeStmt());
|
||||||
argExpressions.set(i, temporaryVariable.reference());
|
argExpressions.set(i, temporaryVariable.reference());
|
||||||
@@ -192,6 +222,7 @@ public class CallArgumentTranslator extends AbstractTranslator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ArgumentsInfo translate() {
|
private ArgumentsInfo translate() {
|
||||||
|
boolean hasEmptyExpressionArgument = false;
|
||||||
List<ValueParameterDescriptor> valueParameters = resolvedCall.getResultingDescriptor().getValueParameters();
|
List<ValueParameterDescriptor> valueParameters = resolvedCall.getResultingDescriptor().getValueParameters();
|
||||||
if (valueParameters.isEmpty()) {
|
if (valueParameters.isEmpty()) {
|
||||||
return new ArgumentsInfo(Collections.<JsExpression>emptyList(), false, null);
|
return new ArgumentsInfo(Collections.<JsExpression>emptyList(), false, null);
|
||||||
@@ -223,10 +254,15 @@ public class CallArgumentTranslator extends AbstractTranslator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
TranslationContext argContext = context().innerBlock();
|
TranslationContext argContext = context().innerBlock();
|
||||||
translateSingleArgument(actualArgument, result, argContext, !isNativeFunctionCall && !hasSpreadOperator);
|
ArgumentsKind kind = translateSingleArgument(actualArgument, result, argContext, !isNativeFunctionCall && !hasSpreadOperator);
|
||||||
context().moveVarsFrom(argContext);
|
context().moveVarsFrom(argContext);
|
||||||
argContexts.add(argContext);
|
argContexts.add(argContext);
|
||||||
argumentsShouldBeExtractedToTmpVars = argumentsShouldBeExtractedToTmpVars || !argContext.currentBlockIsEmpty();
|
argumentsShouldBeExtractedToTmpVars = argumentsShouldBeExtractedToTmpVars || !argContext.currentBlockIsEmpty();
|
||||||
|
|
||||||
|
if (kind == ArgumentsKind.HAS_EMPTY_EXPRESSION_ARGUMENT) {
|
||||||
|
hasEmptyExpressionArgument = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argumentsShouldBeExtractedToTmpVars) {
|
if (argumentsShouldBeExtractedToTmpVars) {
|
||||||
@@ -249,7 +285,7 @@ public class CallArgumentTranslator extends AbstractTranslator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeLastUndefinedArguments(result);
|
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 assignStatement = assignment(jsNameRef, defaultValue).makeStmt();
|
||||||
JsStatement thenStatement = JsAstUtils.mergeStatementInBlockIfNeeded(assignStatement, defaultArgBlock);
|
JsStatement thenStatement = JsAstUtils.mergeStatementInBlockIfNeeded(assignStatement, defaultArgBlock);
|
||||||
JsBinaryOperation checkArgIsUndefined = equality(jsNameRef, functionBodyContext.namer().getUndefinedExpression());
|
JsBinaryOperation checkArgIsUndefined = equality(jsNameRef, functionBodyContext.namer().getUndefinedExpression());
|
||||||
JsIf jsIf = new JsIf(checkArgIsUndefined, thenStatement);
|
JsIf jsIf = JsAstUtils.newJsIf(checkArgIsUndefined, thenStatement);
|
||||||
result.add(jsIf);
|
result.add(jsIf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,16 +21,12 @@ import com.intellij.util.SmartList;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.k2js.translate.context.Namer;
|
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.context.TranslationContext;
|
||||||
import org.jetbrains.k2js.translate.utils.mutator.AssignToExpressionMutator;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator.mutateLastExpression;
|
|
||||||
|
|
||||||
public final class JsAstUtils {
|
public final class JsAstUtils {
|
||||||
private static final JsNameRef DEFINE_PROPERTY = new JsNameRef("defineProperty");
|
private static final JsNameRef DEFINE_PROPERTY = new JsNameRef("defineProperty");
|
||||||
public static final JsNameRef CREATE_OBJECT = new JsNameRef("create");
|
public static final JsNameRef CREATE_OBJECT = new JsNameRef("create");
|
||||||
@@ -71,22 +67,28 @@ public final class JsAstUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static JsExpression convertToExpression(@NotNull JsNode jsNode, @NotNull TranslationContext context) {
|
private static JsStatement deBlockIfPossible(@NotNull JsStatement statement) {
|
||||||
return convertToExpression(jsNode, context, context.dynamicContext().jsBlock());
|
if (statement instanceof JsBlock && ((JsBlock)statement).getStatements().size() == 1) {
|
||||||
|
return ((JsBlock)statement).getStatements().get(0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return statement;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
public static JsExpression convertToExpression(@NotNull JsNode jsNode, @NotNull TranslationContext context, @NotNull JsBlock block) {
|
public static JsIf newJsIf(
|
||||||
if (jsNode instanceof JsExpression) {
|
@NotNull JsExpression ifExpression,
|
||||||
return (JsExpression) jsNode;
|
@NotNull JsStatement thenStatement,
|
||||||
}
|
@Nullable JsStatement elseStatement
|
||||||
else {
|
) {
|
||||||
assert jsNode instanceof JsStatement : "Unexpected node of type: " + jsNode.getClass().toString();
|
elseStatement = elseStatement != null ? deBlockIfPossible(elseStatement) : null;
|
||||||
TemporaryVariable result = context.declareTemporary(null);
|
return new JsIf(ifExpression, deBlockIfPossible(thenStatement), elseStatement);
|
||||||
AssignToExpressionMutator saveResultToTemporaryMutator = new AssignToExpressionMutator(result.reference());
|
}
|
||||||
block.getStatements().add(mutateLastExpression(jsNode, saveResultToTemporaryMutator));
|
|
||||||
return result.reference();
|
@NotNull
|
||||||
}
|
public static JsIf newJsIf(@NotNull JsExpression ifExpression, @NotNull JsStatement thenStatement) {
|
||||||
|
return newJsIf(ifExpression, thenStatement, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -99,11 +101,22 @@ public final class JsAstUtils {
|
|||||||
if (block.isEmpty()) {
|
if (block.isEmpty()) {
|
||||||
return statement;
|
return statement;
|
||||||
} else {
|
} else {
|
||||||
|
if (isEmptyStatement(statement)) {
|
||||||
|
return deBlockIfPossible(block);
|
||||||
|
}
|
||||||
block.getStatements().add(statement);
|
block.getStatements().add(statement);
|
||||||
return block;
|
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
|
@NotNull
|
||||||
public static JsPrefixOperation negated(@NotNull JsExpression expression) {
|
public static JsPrefixOperation negated(@NotNull JsExpression expression) {
|
||||||
return new JsPrefixOperation(JsUnaryOperator.NOT, expression);
|
return new JsPrefixOperation(JsUnaryOperator.NOT, expression);
|
||||||
|
|||||||
@@ -398,6 +398,7 @@ public final class TranslationUtils {
|
|||||||
|
|
||||||
public static boolean isCacheNeeded(@NotNull JsExpression expression) {
|
public static boolean isCacheNeeded(@NotNull JsExpression expression) {
|
||||||
return !(expression instanceof JsLiteral.JsValueLiteral) &&
|
return !(expression instanceof JsLiteral.JsValueLiteral) &&
|
||||||
|
!JsAstUtils.isEmptyExpression(expression) &&
|
||||||
(!(expression instanceof JsNameRef) || ((JsNameRef) expression).getQualifier() != null);
|
(!(expression instanceof JsNameRef) || ((JsNameRef) expression).getQualifier() != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+44
-6
@@ -1,46 +1,62 @@
|
|||||||
package foo
|
package foo
|
||||||
|
|
||||||
|
var global: String = ""
|
||||||
|
|
||||||
|
fun id(s: String, b: Boolean): Boolean {
|
||||||
|
global += s
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
fun bar(b: Boolean): String {
|
fun bar(b: Boolean): String {
|
||||||
if (b && return "A")
|
if (id("A", b) && return "A")
|
||||||
return "B"
|
return "B"
|
||||||
else
|
else
|
||||||
return "C"
|
return "C"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testBreak(b: Boolean, expected: Int) {
|
fun testBreak(b: Boolean, expected: Int) {
|
||||||
|
global = ""
|
||||||
var i = 0
|
var i = 0
|
||||||
while (i++ < 5) {
|
while (i++ < 5) {
|
||||||
if (i == 2) b && break
|
if (i == 2) id("A", b) && break
|
||||||
}
|
}
|
||||||
assertEquals(expected, i, "break 1")
|
assertEquals(expected, i, "break 1")
|
||||||
|
assertEquals("A", global, "break 1")
|
||||||
|
|
||||||
|
global = ""
|
||||||
i = 0
|
i = 0
|
||||||
while (i++ < 5) {
|
while (i++ < 5) {
|
||||||
if (i == 2) {
|
if (i == 2) {
|
||||||
var x = b && break
|
var x = id("A", b) && break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertEquals(expected, i, "break 2")
|
assertEquals(expected, i, "break 2")
|
||||||
|
assertEquals("A", global, "break 2")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testContinue(b: Boolean, expected: Int) {
|
fun testContinue(b: Boolean, expected: Int) {
|
||||||
|
global = ""
|
||||||
var i = 0
|
var i = 0
|
||||||
var n = 0
|
var n = 0
|
||||||
while (i++ < 5) {
|
while (i++ < 5) {
|
||||||
if (i == 2) b && continue
|
if (i == 2) id("A", b) && continue
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
assertEquals(expected, n, "continue 1")
|
assertEquals(expected, n, "continue 1")
|
||||||
|
assertEquals("A", global, "continue 1")
|
||||||
|
|
||||||
|
global = ""
|
||||||
i = 0
|
i = 0
|
||||||
n = 0
|
n = 0
|
||||||
while (i++ < 5) {
|
while (i++ < 5) {
|
||||||
if (i == 2) {
|
if (i == 2) {
|
||||||
var x = b && continue
|
var x = id("A", b) && continue
|
||||||
|
assertEquals(x, b)
|
||||||
}
|
}
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
assertEquals(expected, n, "continue 2")
|
assertEquals(expected, n, "continue 2")
|
||||||
|
assertEquals("A", global, "continue 2")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
@@ -49,11 +65,33 @@ fun box(): String {
|
|||||||
testBreak(true, 2)
|
testBreak(true, 2)
|
||||||
testBreak(false, 6)
|
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(true, 4)
|
||||||
testContinue(false, 5)
|
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("C", bar(false))
|
||||||
|
assertEquals("A", global, "bar")
|
||||||
|
|
||||||
return "OK"
|
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) {
|
fun testBreak(a: A?, expected: Int) {
|
||||||
var i = 0
|
var i = 0
|
||||||
while (i++ < 5) {
|
while(i++<5) {
|
||||||
if (i == 2) a ?: break
|
if (i==2) a ?: break
|
||||||
}
|
}
|
||||||
assertEquals(expected, i, "break 1")
|
assertEquals(expected, i, "break 1")
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
while (i++ < 5) {
|
while(i++<5) {
|
||||||
if (i == 2) {
|
if (i==2) {
|
||||||
var x = a ?: break
|
var x = a ?: break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,16 +28,16 @@ fun testBreak(a: A?, expected: Int) {
|
|||||||
fun testContinue(a: A?, expected: Int) {
|
fun testContinue(a: A?, expected: Int) {
|
||||||
var i = 0
|
var i = 0
|
||||||
var n = 0
|
var n = 0
|
||||||
while (i++ < 5) {
|
while(i++<5) {
|
||||||
if (i == 2) a ?: continue
|
if (i==2) a ?: continue
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
assertEquals(expected, n)
|
assertEquals(expected, n)
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
n = 0
|
n = 0
|
||||||
while (i++ < 5) {
|
while(i++<5) {
|
||||||
if (i == 2) {
|
if (i==2) {
|
||||||
var x = a ?: continue
|
var x = a ?: continue
|
||||||
}
|
}
|
||||||
n++
|
n++
|
||||||
@@ -50,9 +50,23 @@ fun box(): String {
|
|||||||
testBreak(null, 2)
|
testBreak(null, 2)
|
||||||
testBreak(A(), 6)
|
testBreak(A(), 6)
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
while(i++<5) {
|
||||||
|
if (i==2) break ?: null
|
||||||
|
}
|
||||||
|
assertEquals(2, i, "break ?: null")
|
||||||
|
|
||||||
testContinue(null, 4)
|
testContinue(null, 4)
|
||||||
testContinue(A(), 5)
|
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("A", bar(null))
|
||||||
assertEquals("B", bar(A()))
|
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
|
package foo
|
||||||
|
|
||||||
|
var global: String = ""
|
||||||
|
|
||||||
|
fun id(s: String, b: Boolean): Boolean {
|
||||||
|
global += s
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
fun bar(b: Boolean): String {
|
fun bar(b: Boolean): String {
|
||||||
if (b || return "A")
|
if (id("A",b) || return "A")
|
||||||
return "B"
|
return "B"
|
||||||
else
|
else
|
||||||
return "C"
|
return "C"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testBreak(b: Boolean, expected: Int) {
|
fun testBreak(b: Boolean, expected: Int) {
|
||||||
|
global = ""
|
||||||
var i = 0
|
var i = 0
|
||||||
while (i++ < 5) {
|
while (i++ < 5) {
|
||||||
if (i == 2) b || break
|
if (i == 2) id("A", b) || break
|
||||||
}
|
}
|
||||||
assertEquals(expected, i, "break 1")
|
assertEquals(expected, i, "break 1")
|
||||||
|
assertEquals("A", global, "break 1")
|
||||||
|
|
||||||
|
global = ""
|
||||||
i = 0
|
i = 0
|
||||||
while (i++ < 5) {
|
while (i++ < 5) {
|
||||||
if (i == 2) {
|
if (i == 2) {
|
||||||
var x = b || break
|
var x = id("A", b) || break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertEquals(expected, i, "break 2")
|
assertEquals(expected, i, "break 2")
|
||||||
|
assertEquals("A", global, "break 2")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun testContinue(b: Boolean, expected: Int) {
|
fun testContinue(b: Boolean, expected: Int) {
|
||||||
|
global = ""
|
||||||
var i = 0
|
var i = 0
|
||||||
var n = 0
|
var n = 0
|
||||||
while (i++ < 5) {
|
while (i++ < 5) {
|
||||||
if (i == 2) b || continue
|
if (i == 2) id("A", b) || continue
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
assertEquals(expected, n, "continue 1")
|
assertEquals(expected, n, "continue 1")
|
||||||
|
assertEquals("A", global, "continue 1")
|
||||||
|
|
||||||
|
global = ""
|
||||||
i = 0
|
i = 0
|
||||||
n = 0
|
n = 0
|
||||||
while (i++ < 5) {
|
while (i++ < 5) {
|
||||||
if (i == 2) {
|
if (i == 2) {
|
||||||
var x = b || continue
|
var x = id("A", b) || continue
|
||||||
}
|
}
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
assertEquals(expected, n, "continue 2")
|
assertEquals(expected, n, "continue 2")
|
||||||
|
assertEquals("A", global, "continue 2")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
@@ -48,11 +63,32 @@ fun box(): String {
|
|||||||
testBreak(true, 6)
|
testBreak(true, 6)
|
||||||
testBreak(false, 2)
|
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(true, 5)
|
||||||
testContinue(false, 4)
|
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("B", bar(true))
|
||||||
|
assertEquals("A", global, "bar")
|
||||||
|
global = ""
|
||||||
assertEquals("A", bar(false))
|
assertEquals("A", bar(false))
|
||||||
|
assertEquals("A", global, "bar")
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
@@ -11,6 +11,9 @@ fun box(): String {
|
|||||||
val a = if (true || if(bar("A", false)) {2} else {3} == 0) { }
|
val a = if (true || if(bar("A", false)) {2} else {3} == 0) { }
|
||||||
assertEquals("", global)
|
assertEquals("", global)
|
||||||
|
|
||||||
|
true || if(bar("A", false)) {2} else {3} == 0
|
||||||
|
assertEquals("", global)
|
||||||
|
|
||||||
// Simple || Simple
|
// Simple || Simple
|
||||||
global = ""
|
global = ""
|
||||||
assertEquals(true, bar("A", true) || bar("B", true))
|
assertEquals(true, bar("A", true) || bar("B", true))
|
||||||
|
|||||||
Reference in New Issue
Block a user