Skip to content

Commit c021af0

Browse files
committed
KJS: fix non-local return inside catch block
1 parent 29e7bcc commit c021af0

File tree

9 files changed

+162
-19
lines changed

9 files changed

+162
-19
lines changed
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// MODULE: lib
2+
// FILE: lib.kt
3+
4+
package utils
5+
6+
inline fun foo(a: Int) {
7+
bar(a)
8+
}
9+
10+
inline fun bar(a: Int) {
11+
try {
12+
if (a > 0) throw Exception()
13+
log("foo($a) #1")
14+
}
15+
catch (e: Exception) {
16+
myRun {
17+
log("foo($a) #2")
18+
if (a > 1) return
19+
log("foo($a) #3")
20+
}
21+
}
22+
log("foo($a) #4")
23+
}
24+
25+
var LOG: String = ""
26+
27+
fun log(s: String): String {
28+
LOG += s + ";"
29+
return LOG
30+
}
31+
32+
inline fun myRun(f: () -> Unit) = f()
33+
34+
35+
// MODULE: main(lib)
36+
// FILE: main.kt
37+
38+
import utils.*
39+
40+
fun box(): String {
41+
foo(0)
42+
if (LOG != "foo(0) #1;foo(0) #4;") return "fail1: $LOG"
43+
LOG = ""
44+
45+
foo(1)
46+
if (LOG != "foo(1) #2;foo(1) #3;foo(1) #4;") return "fail2: $LOG"
47+
LOG = ""
48+
49+
foo(2)
50+
if (LOG != "foo(2) #2;") return "fail3: $LOG"
51+
LOG = ""
52+
53+
return "OK"
54+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// MODULE: lib
2+
// FILE: lib.kt
3+
4+
package utils
5+
6+
inline fun foo(a: Int) {
7+
try {
8+
if (a > 0) throw Exception()
9+
log("foo($a)")
10+
}
11+
catch (e: Exception) {
12+
bar(a)
13+
}
14+
}
15+
16+
inline fun bar(a: Int) {
17+
myRun {
18+
log("bar($a) #1")
19+
if (a == 2) return
20+
log("bar($a) #2")
21+
}
22+
}
23+
24+
var LOG: String = ""
25+
26+
fun log(s: String): String {
27+
LOG += s + ";"
28+
return LOG
29+
}
30+
31+
inline fun myRun(f: () -> Unit) = f()
32+
33+
// MODULE: main(lib)
34+
// FILE: main.kt
35+
36+
import utils.*
37+
38+
fun box(): String {
39+
foo(0)
40+
if (LOG != "foo(0);") return "fail1: $LOG"
41+
LOG = ""
42+
43+
foo(1)
44+
if (LOG != "bar(1) #1;bar(1) #2;") return "fail2: $LOG"
45+
LOG = ""
46+
47+
foo(2)
48+
if (LOG != "bar(2) #1;") return "fail3: $LOG"
49+
50+
return "OK"
51+
}

compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,12 +1387,24 @@ public void testKt7273() throws Exception {
13871387
doTest(fileName);
13881388
}
13891389

1390+
@TestMetadata("nonLocalReturnFromCatchBlock.kt")
1391+
public void testNonLocalReturnFromCatchBlock() throws Exception {
1392+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnFromCatchBlock.kt");
1393+
doTest(fileName);
1394+
}
1395+
13901396
@TestMetadata("nonLocalReturnFromOuterLambda.kt")
13911397
public void testNonLocalReturnFromOuterLambda() throws Exception {
13921398
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnFromOuterLambda.kt");
13931399
doTest(fileName);
13941400
}
13951401

1402+
@TestMetadata("nonLocalReturnToCatchBlock.kt")
1403+
public void testNonLocalReturnToCatchBlock() throws Exception {
1404+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnToCatchBlock.kt");
1405+
doTest(fileName);
1406+
}
1407+
13961408
@TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite")
13971409
@TestDataPath("$PROJECT_ROOT")
13981410
@RunWith(JUnit3RunnerWithInners.class)

compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1387,12 +1387,24 @@ public void testKt7273() throws Exception {
13871387
doTest(fileName);
13881388
}
13891389

1390+
@TestMetadata("nonLocalReturnFromCatchBlock.kt")
1391+
public void testNonLocalReturnFromCatchBlock() throws Exception {
1392+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnFromCatchBlock.kt");
1393+
doTest(fileName);
1394+
}
1395+
13901396
@TestMetadata("nonLocalReturnFromOuterLambda.kt")
13911397
public void testNonLocalReturnFromOuterLambda() throws Exception {
13921398
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnFromOuterLambda.kt");
13931399
doTest(fileName);
13941400
}
13951401

1402+
@TestMetadata("nonLocalReturnToCatchBlock.kt")
1403+
public void testNonLocalReturnToCatchBlock() throws Exception {
1404+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnToCatchBlock.kt");
1405+
doTest(fileName);
1406+
}
1407+
13961408
@TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite")
13971409
@TestDataPath("$PROJECT_ROOT")
13981410
@RunWith(JUnit3RunnerWithInners.class)

generators/src/org/jetbrains/kotlin/generators/tests/generateTestDataForReservedWords.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@
1616

1717
package org.jetbrains.kotlin.generators.tests
1818

19-
import org.jetbrains.kotlin.js.backend.ast.JsFunctionScope
2019
import org.jetbrains.kotlin.generators.util.GeneratorsFileUtil
20+
import org.jetbrains.kotlin.js.backend.ast.JsDeclarationScope
2121
import org.jetbrains.kotlin.lexer.KtTokens
2222
import org.jetbrains.kotlin.renderer.KeywordStringsGenerated
2323
import java.io.File
@@ -366,8 +366,8 @@ val testNotRenamedByRef = testNotRenamed("$KEYWORD_MARKER()")
366366

367367
// KEYWORDS
368368

369-
val SHOULD_BE_ESCAPED = JsFunctionScope.RESERVED_WORDS.filter { it in KeywordStringsGenerated.KEYWORDS }.sorted()
370-
val SHOULD_NOT_BE_ESCAPED = JsFunctionScope.RESERVED_WORDS.filter { it !in SHOULD_BE_ESCAPED }.sorted()
369+
val SHOULD_BE_ESCAPED = JsDeclarationScope.RESERVED_WORDS.filter { it in KeywordStringsGenerated.KEYWORDS }.sorted()
370+
val SHOULD_NOT_BE_ESCAPED = JsDeclarationScope.RESERVED_WORDS.filter { it !in SHOULD_BE_ESCAPED }.sorted()
371371

372372
// all keywords by portions
373373

js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsCatchScope.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,11 @@
1010
* A special scope used only for catch blocks. It only holds a single symbol:
1111
* the catch argument's name.
1212
*/
13-
public class JsCatchScope extends JsScope {
13+
public class JsCatchScope extends JsDeclarationScope {
1414
private final JsName name;
1515

1616
public JsCatchScope(JsScope parent, @NotNull String ident) {
17-
super(parent, "Catch scope");
17+
super(parent, "Catch scope", true);
1818
name = new JsName(this, ident, false);
1919
}
2020

js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/jsScopes.kt

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,26 @@
1616

1717
package org.jetbrains.kotlin.js.backend.ast
1818

19-
import java.util.Stack
19+
import java.util.*
2020

2121
class JsObjectScope(parent: JsScope, description: String) : JsScope(parent, description)
2222

2323
object JsDynamicScope : JsScope(null, "Scope for dynamic declarations") {
2424
override fun doCreateName(name: String) = JsName(this, name, false)
2525
}
2626

27-
open class JsFunctionScope(parent: JsScope, description: String) : JsScope(parent, description) {
28-
29-
private val labelScopes = Stack<LabelScope>()
30-
private val topLabelScope: LabelScope?
31-
get() = if (labelScopes.isNotEmpty()) labelScopes.peek() else null
32-
27+
open class JsFunctionScope(parent: JsScope, description: String) : JsDeclarationScope(parent, description) {
3328
override fun hasOwnName(name: String): Boolean = RESERVED_WORDS.contains(name) || super.hasOwnName(name)
3429

3530
open fun declareNameUnsafe(identifier: String): JsName = super.declareName(identifier)
31+
}
32+
33+
open class JsDeclarationScope(parent: JsScope, description: String, useParentScopeStack: Boolean = false) : JsScope(parent, description) {
34+
private val labelScopes: Stack<LabelScope> =
35+
if (parent is JsDeclarationScope && useParentScopeStack) parent.labelScopes else Stack<LabelScope>()
36+
37+
private val topLabelScope
38+
get() = if (labelScopes.isNotEmpty()) labelScopes.peek() else null
3639

3740
open fun enterLabel(label: String): JsName {
3841
val scope = LabelScope(topLabelScope, label)
@@ -58,7 +61,7 @@ open class JsFunctionScope(parent: JsScope, description: String) : JsScope(paren
5861
else -> ident
5962
}
6063

61-
labelName = JsName(this@JsFunctionScope, freshIdent, false)
64+
labelName = JsName(this@JsDeclarationScope, freshIdent, false)
6265
}
6366

6467
override fun findOwnName(name: String): JsName? =

js/js.parser/src/com/google/gwt/dev/js/ScopeContext.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,7 @@
1717
package com.google.gwt.dev.js
1818

1919
import org.jetbrains.kotlin.js.backend.ast.*
20-
21-
import java.util.Stack
20+
import java.util.*
2221

2322
class ScopeContext(scope: JsScope) {
2423
private val rootScope = generateSequence(scope) { it.parent }.first { it is JsRootScope }
@@ -35,7 +34,7 @@ class ScopeContext(scope: JsScope) {
3534
}
3635

3736
fun exitFunction() {
38-
assert(currentScope is JsFunctionScope)
37+
assert(currentScope is JsDeclarationScope)
3938
exitScope()
4039
}
4140

@@ -51,13 +50,13 @@ class ScopeContext(scope: JsScope) {
5150
}
5251

5352
fun enterLabel(ident: String): JsName =
54-
(currentScope as JsFunctionScope).enterLabel(ident)
53+
(currentScope as JsDeclarationScope).enterLabel(ident)
5554

5655
fun exitLabel() =
57-
(currentScope as JsFunctionScope).exitLabel()
56+
(currentScope as JsDeclarationScope).exitLabel()
5857

5958
fun labelFor(ident: String): JsName? =
60-
(currentScope as JsFunctionScope).findLabel(ident)
59+
(currentScope as JsDeclarationScope).findLabel(ident)
6160

6261
fun globalNameFor(ident: String): JsName =
6362
currentScope.findName(ident) ?: rootScope.declareName(ident)

js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/NonLocalReturnsTestGenerated.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,24 @@ public void testKt7273() throws Exception {
155155
doTest(fileName);
156156
}
157157

158+
@TestMetadata("nonLocalReturnFromCatchBlock.kt")
159+
public void testNonLocalReturnFromCatchBlock() throws Exception {
160+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnFromCatchBlock.kt");
161+
doTest(fileName);
162+
}
163+
158164
@TestMetadata("nonLocalReturnFromOuterLambda.kt")
159165
public void testNonLocalReturnFromOuterLambda() throws Exception {
160166
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnFromOuterLambda.kt");
161167
doTest(fileName);
162168
}
163169

170+
@TestMetadata("nonLocalReturnToCatchBlock.kt")
171+
public void testNonLocalReturnToCatchBlock() throws Exception {
172+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnToCatchBlock.kt");
173+
doTest(fileName);
174+
}
175+
164176
@TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite")
165177
@TestDataPath("$PROJECT_ROOT")
166178
@RunWith(JUnit3RunnerWithInners.class)

0 commit comments

Comments
 (0)