From ca4ce4880143fbc4b82b9494822cd945d1a1dedc Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Mon, 1 Jun 2015 19:52:03 +0300 Subject: [PATCH] JS backend: fix compiler crash on for with empty body --- .../kotlin/js/test/semantics/ForeachTest.java | 8 ++++ .../js/translate/expression/LoopTranslator.kt | 12 +++-- .../kotlin/js/translate/utils/PsiUtils.java | 7 --- .../expression/for/cases/forWithEmptyBody.kt | 14 ++++++ ...deEffectImElementAccessAndWithEmptyBody.kt | 45 +++++++++++++++++++ 5 files changed, 75 insertions(+), 11 deletions(-) create mode 100644 js/js.translator/testData/expression/for/cases/forWithEmptyBody.kt create mode 100644 js/js.translator/testData/expression/for/cases/forWithSideEffectImElementAccessAndWithEmptyBody.kt diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ForeachTest.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ForeachTest.java index 74987888e8d..d016cbc45b7 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ForeachTest.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ForeachTest.java @@ -57,4 +57,12 @@ public final class ForeachTest extends AbstractExpressionTest { public void testLabeledForWithWhile() throws Exception { checkFooBoxIsOk(); } + + public void testForWithEmptyBody() throws Exception { + checkFooBoxIsOk(); + } + + public void testForWithSideEffectImElementAccessAndWithEmptyBody() throws Exception { + checkFooBoxIsOk(); + } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt index e9952d59335..911a82df78f 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/LoopTranslator.kt @@ -29,7 +29,6 @@ import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getIteratorFunction import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getNextFunction import org.jetbrains.kotlin.js.translate.utils.BindingUtils.getTypeForExpression import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.* -import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getLoopBody import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getLoopParameter import org.jetbrains.kotlin.js.translate.utils.PsiUtils.getLoopRange import org.jetbrains.kotlin.js.translate.utils.TemporariesUtils.temporariesInitialization @@ -127,8 +126,8 @@ public fun translateForExpression(expression: JetForExpression, context: Transla val parameterName: JsName = declareParameter() - fun translateBody(itemValue: JsExpression?): JsStatement { - val realBody = Translation.translateAsStatementAndMergeInBlockIfNeeded(getLoopBody(expression), context) + fun translateBody(itemValue: JsExpression?): JsStatement? { + val realBody = expression.getBody()?.let { Translation.translateAsStatementAndMergeInBlockIfNeeded(it, context) } if (itemValue == null && multiParameter == null) { return realBody } @@ -138,6 +137,9 @@ public fun translateForExpression(expression: JetForExpression, context: Transla newVar(parameterName, itemValue) else MultiDeclarationTranslator.translate(multiParameter, parameterName, itemValue, context) + + if (realBody == null) return JsBlock(currentVarInit) + val block = convertToBlock(realBody) block.getStatements().add(0, currentVarInit) return block @@ -230,7 +232,9 @@ public fun translateForExpression(expression: JetForExpression, context: Transla } context.addStatementToCurrentBlock(iteratorVar.assignmentExpression().makeStmt()) - return JsWhile(hasNextMethodInvocation(), translateBody(nextMethodInvocation())) + val nextInvoke = nextMethodInvocation() + val body = translateBody(nextInvoke) + return JsWhile(hasNextMethodInvocation(), body ?: nextInvoke.makeStmt()) } return when { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/PsiUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/PsiUtils.java index 9fbbe07565a..62cc3b86bf3 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/PsiUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/PsiUtils.java @@ -111,13 +111,6 @@ public final class PsiUtils { return (binaryExpression.getOperationToken() == JetTokens.IN_KEYWORD); } - @NotNull - public static JetExpression getLoopBody(@NotNull JetLoopExpression expression) { - JetExpression body = expression.getBody(); - assert body != null : "Loops cannot have null bodies."; - return body; - } - @Nullable public static JetParameter getLoopParameter(@NotNull JetForExpression expression) { return expression.getLoopParameter(); diff --git a/js/js.translator/testData/expression/for/cases/forWithEmptyBody.kt b/js/js.translator/testData/expression/for/cases/forWithEmptyBody.kt new file mode 100644 index 00000000000..5e7379291a7 --- /dev/null +++ b/js/js.translator/testData/expression/for/cases/forWithEmptyBody.kt @@ -0,0 +1,14 @@ +package foo + +fun box(): String { + for (i in 0..5); + + val r = 0..5 + for (i in r); + + for (i in arrayOf(1, 2, 3)); + + for (i in arrayOf(1, 2, 3).asList()); + + return "OK" +} diff --git a/js/js.translator/testData/expression/for/cases/forWithSideEffectImElementAccessAndWithEmptyBody.kt b/js/js.translator/testData/expression/for/cases/forWithSideEffectImElementAccessAndWithEmptyBody.kt new file mode 100644 index 00000000000..58541782541 --- /dev/null +++ b/js/js.translator/testData/expression/for/cases/forWithSideEffectImElementAccessAndWithEmptyBody.kt @@ -0,0 +1,45 @@ +package foo + +var log = "" + +class T(val id: Int) { + fun component1(): Int { + log += "($id).component1();" + return 1 + } + fun component2(): String { + log += "($id).component2();" + return "1" + } +} + +class C { + fun iterator(): Iterator = object: Iterator { + var i = 0 + var data = arrayOf(T(3), T(1), T(2)) + override fun hasNext(): Boolean { + log += "C.hasNext();" + return i < data.size() + } + + override fun next(): T { + log += "C.next();" + return data[i++] + } + } +} + +fun box(): String { + for ((a, b) in arrayOf(T(3), T(1), T(2))); + assertEquals("(3).component1();(3).component2();(1).component1();(1).component2();(2).component1();(2).component2();", log) + + + log = "" + for ((a, b) in C()); + assertEquals("C.hasNext();C.next();(3).component1();(3).component2();C.hasNext();C.next();" + + "(1).component1();(1).component2();C.hasNext();C.next();" + + "(2).component1();(2).component2();C.hasNext();", log) + + return "OK" +} +