diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/InlineTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/InlineTest.java index 9508b389c75..aa9117d33d9 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/semantics/InlineTest.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/InlineTest.java @@ -180,10 +180,6 @@ public final class InlineTest extends SingleFileTranslationWithDirectivesTest { checkFooBoxIsOkWithInlineDirectives(); } - public void testLabelNameClashing() throws Exception { - checkFooBoxIsOkWithInlineDirectives(); - } - public void testClassObject() throws Exception { checkFooBoxIsOkWithInlineDirectives(); } diff --git a/js/js.tests/test/org/jetbrains/k2js/test/semantics/LabelTest.java b/js/js.tests/test/org/jetbrains/k2js/test/semantics/LabelTest.java new file mode 100644 index 00000000000..abf37d3b50c --- /dev/null +++ b/js/js.tests/test/org/jetbrains/k2js/test/semantics/LabelTest.java @@ -0,0 +1,61 @@ +/* + * 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 org.jetbrains.k2js.test.semantics; + +import org.jetbrains.k2js.test.SingleFileTranslationWithDirectivesTest; + +public class LabelTest extends SingleFileTranslationWithDirectivesTest { + public LabelTest() { + super("labels/"); + } + + public void testSimpleLabel() throws Exception { + checkFooBoxIsOkWithInlineDirectives(); + } + + public void testSiblingLabels() throws Exception { + checkFooBoxIsOkWithInlineDirectives(); + } + + public void testNestedLabels() throws Exception { + checkFooBoxIsOkWithInlineDirectives(); + } + + public void testSimpleLabelInlined() throws Exception { + checkFooBoxIsOkWithInlineDirectives(); + } + + public void testSiblingLabelsInlined() throws Exception { + checkFooBoxIsOkWithInlineDirectives(); + } + + public void testSiblingLabelsInlinedClashing() throws Exception { + checkFooBoxIsOkWithInlineDirectives(); + } + + public void testNestedLabelsInlinedClashing() throws Exception { + checkFooBoxIsOkWithInlineDirectives(); + } + + public void testLabelWithVariableClashing() throws Exception { + checkFooBoxIsOkWithInlineDirectives(); + } + + public void testNestedLabelsInlinedClashingAtFunctionsWithClosure() throws Exception { + checkFooBoxIsOkWithInlineDirectives(); + } +} diff --git a/js/js.tests/test/org/jetbrains/k2js/test/utils/DirectiveTestUtils.java b/js/js.tests/test/org/jetbrains/k2js/test/utils/DirectiveTestUtils.java index 8d15abc6f83..96ec99c40b3 100644 --- a/js/js.tests/test/org/jetbrains/k2js/test/utils/DirectiveTestUtils.java +++ b/js/js.tests/test/org/jetbrains/k2js/test/utils/DirectiveTestUtils.java @@ -17,6 +17,7 @@ package org.jetbrains.k2js.test.utils; import com.google.dart.compiler.backend.js.ast.JsFunction; +import com.google.dart.compiler.backend.js.ast.JsLabel; import com.google.dart.compiler.backend.js.ast.JsNode; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -27,6 +28,7 @@ import static org.jetbrains.jet.InTextDirectivesUtils.findLinesWithPrefixesRemov import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.jetbrains.k2js.inline.util.UtilPackage.collectInstances; public class DirectiveTestUtils { @@ -89,12 +91,39 @@ public class DirectiveTestUtils { } }; + private static final DirectiveHandler COUNT_LABELS = new DirectiveHandler("CHECK_LABELS_COUNT") { + @Override + void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception { + String functionName = arguments.findNamedArgument("function"); + String labelName = arguments.findNamedArgument("name"); + String countStr = arguments.findNamedArgument("count"); + assert countStr != null; + int expectedCount = Integer.valueOf(countStr); + + JsNode scope = AstSearchUtil.getFunction(ast, functionName); + List labels = collectInstances(JsLabel.class, scope); + int actualCount = 0; + for (JsLabel label : labels) { + if (label.getName().getIdent().equals(labelName)) { + actualCount++; + } + } + + String message = "Label " + labelName + + " is expected to be counted " + expectedCount + + " times at function " + functionName + + " but was encountered " + actualCount + " times"; + assertEquals(message, expectedCount, actualCount); + } + }; + public static void processDirectives(@NotNull JsNode ast, @NotNull String sourceCode) throws Exception { FUNCTION_CONTAINS_NO_CALLS.process(ast, sourceCode); FUNCTION_NOT_CALLED.process(ast, sourceCode); FUNCTION_CALLED_IN_SCOPE.process(ast, sourceCode); FUNCTION_NOT_CALLED_IN_SCOPE.process(ast, sourceCode); FUNCTIONS_HAVE_SAME_LINES.process(ast, sourceCode); + COUNT_LABELS.process(ast, sourceCode); } public static void checkFunctionContainsNoCalls(JsNode node, String functionName) throws Exception { diff --git a/js/js.translator/testData/labels/cases/labelWithVariableClashing.kt b/js/js.translator/testData/labels/cases/labelWithVariableClashing.kt new file mode 100644 index 00000000000..122d5f1bd13 --- /dev/null +++ b/js/js.translator/testData/labels/cases/labelWithVariableClashing.kt @@ -0,0 +1,34 @@ +package foo + +// CHECK_LABELS_COUNT: function=test1 name=loop$ count=1 +// CHECK_LABELS_COUNT: function=test2 name=loop$ count=1 + +fun test1() { + var `loop$` = 0 + + @loop for (i in 1..10) { + `loop$` = i + if (i == 5) break@loop + } + + assertEquals(5, `loop$`, "test1") +} + +fun test2() { + var loop = 0 + + @loop for (i in 1..10) { + loop = i + if (i == 5) break@loop + } + + assertEquals(5, loop, "test2") +} + + +fun box(): String { + test1() + test2() + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/labels/cases/nestedLabels.kt b/js/js.translator/testData/labels/cases/nestedLabels.kt new file mode 100644 index 00000000000..70867078720 --- /dev/null +++ b/js/js.translator/testData/labels/cases/nestedLabels.kt @@ -0,0 +1,28 @@ +package foo + +// CHECK_LABELS_COUNT: function=test name=loop$ count=1 +// CHECK_LABELS_COUNT: function=test name=loop$_0 count=1 + +fun test() { + var i = 0 + var j = 0 + + @loop for (k in 1..10) { + @loop for (m in 1..10) { + if (m == 4) break @loop + j = m + } + + if (k == 8) break @loop + i = k + } + + assertEquals(3, j) + assertEquals(7, i) +} + +fun box(): String { + test() + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/labels/cases/nestedLabelsInlinedClashing.kt b/js/js.translator/testData/labels/cases/nestedLabelsInlinedClashing.kt new file mode 100644 index 00000000000..97e3b81d181 --- /dev/null +++ b/js/js.translator/testData/labels/cases/nestedLabelsInlinedClashing.kt @@ -0,0 +1,43 @@ +package foo + +// CHECK_CONTAINS_NO_CALLS: test +// CHECK_LABELS_COUNT: function=test name=loop$ count=1 +// CHECK_LABELS_COUNT: function=test name=loop$_0 count=1 +// CHECK_LABELS_COUNT: function=test name=loop$_1 count=1 + +class State() { + public var value: Int = 0 +} + +inline fun test1(state: State) { + @loop for (i in 1..10) { + state.value++ + if (i == 2) break@loop + } +} + +inline fun test2(state: State) { + @loop for (i in 1..10) { + test1(state) + if (i == 2) break@loop + } +} + +inline fun test3(state: State) { + @loop for (i in 1..10) { + test2(state) + if (i == 2) break@loop + } +} + +noinline fun test(state: State) { + test3(state) +} + +fun box(): String { + val state = State() + test(state) + assertEquals(8, state.value) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/labels/cases/nestedLabelsInlinedClashingAtFunctionsWithClosure.kt b/js/js.translator/testData/labels/cases/nestedLabelsInlinedClashingAtFunctionsWithClosure.kt new file mode 100644 index 00000000000..1359f68e265 --- /dev/null +++ b/js/js.translator/testData/labels/cases/nestedLabelsInlinedClashingAtFunctionsWithClosure.kt @@ -0,0 +1,42 @@ +package foo + +// CHECK_LABELS_COUNT: function=test name=loop$ count=1 +// CHECK_LABELS_COUNT: function=test name=loop$_0 count=1 +// CHECK_LABELS_COUNT: function=test name=loop$_1 count=1 + +class State() { + public var value: Int = 0 +} + +noinline fun test(state: State) { + [inline] fun test3() { + [inline] fun test2() { + [inline] fun test1() { + @loop for (i in 1..10) { + state.value++ + if (i == 2) break@loop + } + } + + @loop for (i in 1..10) { + test1() + if (i == 2) break@loop + } + } + + @loop for (i in 1..10) { + test2() + if (i == 2) break@loop + } + } + + test3() +} + +fun box(): String { + val state = State() + test(state) + assertEquals(8, state.value) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/labels/cases/siblingLabels.kt b/js/js.translator/testData/labels/cases/siblingLabels.kt new file mode 100644 index 00000000000..ba9724b6bfe --- /dev/null +++ b/js/js.translator/testData/labels/cases/siblingLabels.kt @@ -0,0 +1,27 @@ +package foo + +// CHECK_LABELS_COUNT: function=test name=loop$ count=2 + +fun test() { + var i = 0 + var j = 0 + + @loop for (m in 1..10) { + if (m == 4) break @loop + j = m + } + + @loop for (k in 1..10) { + if (k == 4) break @loop + i = k + } + + assertEquals(3, j) + assertEquals(3, i) +} + +fun box(): String { + test() + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/labels/cases/siblingLabelsInlined.kt b/js/js.translator/testData/labels/cases/siblingLabelsInlined.kt new file mode 100644 index 00000000000..4094f05df36 --- /dev/null +++ b/js/js.translator/testData/labels/cases/siblingLabelsInlined.kt @@ -0,0 +1,31 @@ +package foo + +// CHECK_NOT_CALLED: testInline +// CHECK_LABELS_COUNT: function=testNoinline name=loop$ count=2 + +inline fun testInline(): Int { + var c = 0 + + @loop for (i in 1..9) { + c++ + if (c == 2) break@loop + } + + + @loop for (j in 1..9) { + c++ + if (c == 4) break@loop + } + + return c +} + +noinline fun testNoinline(): Int { + return testInline() +} + +fun box(): String { + assertEquals(4, testNoinline()) + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/inline/cases/labelNameClashing.kt b/js/js.translator/testData/labels/cases/siblingLabelsInlinedClashing.kt similarity index 79% rename from js/js.translator/testData/inline/cases/labelNameClashing.kt rename to js/js.translator/testData/labels/cases/siblingLabelsInlinedClashing.kt index 26ee0a1273a..fb1d3d155d4 100644 --- a/js/js.translator/testData/inline/cases/labelNameClashing.kt +++ b/js/js.translator/testData/labels/cases/siblingLabelsInlinedClashing.kt @@ -1,5 +1,9 @@ package foo +// CHECK_NOT_CALLED: testLabelInline +// CHECK_LABELS_COUNT: function=testLabel name=loop$ count=1 +// CHECK_LABELS_COUNT: function=testLabel name=loop$_0 count=2 + inline fun testLabelInline(): Int { var a = 0 diff --git a/js/js.translator/testData/labels/cases/simpleLabel.kt b/js/js.translator/testData/labels/cases/simpleLabel.kt new file mode 100644 index 00000000000..7d30876c8a9 --- /dev/null +++ b/js/js.translator/testData/labels/cases/simpleLabel.kt @@ -0,0 +1,35 @@ +package foo + +// CHECK_LABELS_COUNT: function=testBreak name=loop$ count=1 +// CHECK_LABELS_COUNT: function=testContinue name=loop$ count=1 + +fun testBreak() { + var i = 0 + + @loop for (j in 1..10) { + if (j == 5) break @loop + + i = j + } + + assertEquals(4, i, "break") +} + +fun testContinue() { + var sum = 0 + + @loop for (j in 1..5) { + if (j % 2 != 0) continue @loop + + sum += j + } + + assertEquals(6, sum, "continue") +} + +fun box(): String { + testBreak() + testContinue() + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/labels/cases/simpleLabelInlined.kt b/js/js.translator/testData/labels/cases/simpleLabelInlined.kt new file mode 100644 index 00000000000..a71188764d1 --- /dev/null +++ b/js/js.translator/testData/labels/cases/simpleLabelInlined.kt @@ -0,0 +1,44 @@ +package foo + +// CHECK_NOT_CALLED: testBreak +// CHECK_NOT_CALLED: testContinue +// CHECK_LABELS_COUNT: function=testBreakNoinline name=loop$ count=1 +// CHECK_LABELS_COUNT: function=testContinueNoinline name=loop$ count=1 + +inline fun testBreak(): Int { + var i = 0 + + @loop for (j in 1..10) { + if (j == 5) break @loop + + i = j + } + + return i +} + +noinline fun testBreakNoinline() { + assertEquals(4, testBreak(), "break") +} + +inline fun testContinue(): Int { + var sum = 0 + + @loop for (j in 1..5) { + if (j % 2 != 0) continue @loop + + sum += j + } + + return sum +} + +noinline fun testContinueNoinline() { + assertEquals(6, testContinue(), "continue") +} + +fun box(): String { + testContinue() + + return "OK" +} \ No newline at end of file