From c021af0fef33b3be3302ba9cd2808b52f080fbd2 Mon Sep 17 00:00:00 2001 From: Zalim Bashorov Date: Mon, 20 Mar 2017 21:10:39 +0300 Subject: [PATCH] KJS: fix non-local return inside catch block --- .../nonLocalReturnFromCatchBlock.kt | 54 +++++++++++++++++++ .../tryFinally/nonLocalReturnToCatchBlock.kt | 51 ++++++++++++++++++ .../BlackBoxInlineCodegenTestGenerated.java | 12 +++++ ...otlinAgainstInlineKotlinTestGenerated.java | 12 +++++ .../tests/generateTestDataForReservedWords.kt | 6 +-- .../kotlin/js/backend/ast/JsCatchScope.java | 4 +- .../kotlin/js/backend/ast/jsScopes.kt | 19 ++++--- .../src/com/google/gwt/dev/js/ScopeContext.kt | 11 ++-- .../NonLocalReturnsTestGenerated.java | 12 +++++ 9 files changed, 162 insertions(+), 19 deletions(-) create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnFromCatchBlock.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnToCatchBlock.kt diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnFromCatchBlock.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnFromCatchBlock.kt new file mode 100644 index 00000000000..240ec77b316 --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnFromCatchBlock.kt @@ -0,0 +1,54 @@ +// MODULE: lib +// FILE: lib.kt + +package utils + +inline fun foo(a: Int) { + bar(a) +} + +inline fun bar(a: Int) { + try { + if (a > 0) throw Exception() + log("foo($a) #1") + } + catch (e: Exception) { + myRun { + log("foo($a) #2") + if (a > 1) return + log("foo($a) #3") + } + } + log("foo($a) #4") +} + +var LOG: String = "" + +fun log(s: String): String { + LOG += s + ";" + return LOG +} + +inline fun myRun(f: () -> Unit) = f() + + +// MODULE: main(lib) +// FILE: main.kt + +import utils.* + +fun box(): String { + foo(0) + if (LOG != "foo(0) #1;foo(0) #4;") return "fail1: $LOG" + LOG = "" + + foo(1) + if (LOG != "foo(1) #2;foo(1) #3;foo(1) #4;") return "fail2: $LOG" + LOG = "" + + foo(2) + if (LOG != "foo(2) #2;") return "fail3: $LOG" + LOG = "" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnToCatchBlock.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnToCatchBlock.kt new file mode 100644 index 00000000000..319213c08c8 --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnToCatchBlock.kt @@ -0,0 +1,51 @@ +// MODULE: lib +// FILE: lib.kt + +package utils + +inline fun foo(a: Int) { + try { + if (a > 0) throw Exception() + log("foo($a)") + } + catch (e: Exception) { + bar(a) + } +} + +inline fun bar(a: Int) { + myRun { + log("bar($a) #1") + if (a == 2) return + log("bar($a) #2") + } +} + +var LOG: String = "" + +fun log(s: String): String { + LOG += s + ";" + return LOG +} + +inline fun myRun(f: () -> Unit) = f() + +// MODULE: main(lib) +// FILE: main.kt + +import utils.* + +fun box(): String { + foo(0) + if (LOG != "foo(0);") return "fail1: $LOG" + LOG = "" + + foo(1) + if (LOG != "bar(1) #1;bar(1) #2;") return "fail2: $LOG" + LOG = "" + + foo(2) + if (LOG != "bar(2) #1;") return "fail3: $LOG" + + return "OK" +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java index 6ee9482e420..c2e92e0300e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxInlineCodegenTestGenerated.java @@ -1387,12 +1387,24 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo doTest(fileName); } + @TestMetadata("nonLocalReturnFromCatchBlock.kt") + public void testNonLocalReturnFromCatchBlock() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnFromCatchBlock.kt"); + doTest(fileName); + } + @TestMetadata("nonLocalReturnFromOuterLambda.kt") public void testNonLocalReturnFromOuterLambda() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnFromOuterLambda.kt"); doTest(fileName); } + @TestMetadata("nonLocalReturnToCatchBlock.kt") + public void testNonLocalReturnToCatchBlock() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnToCatchBlock.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java index 38b9b34699a..92a4e303c0c 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/CompileKotlinAgainstInlineKotlinTestGenerated.java @@ -1387,12 +1387,24 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi doTest(fileName); } + @TestMetadata("nonLocalReturnFromCatchBlock.kt") + public void testNonLocalReturnFromCatchBlock() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnFromCatchBlock.kt"); + doTest(fileName); + } + @TestMetadata("nonLocalReturnFromOuterLambda.kt") public void testNonLocalReturnFromOuterLambda() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnFromOuterLambda.kt"); doTest(fileName); } + @TestMetadata("nonLocalReturnToCatchBlock.kt") + public void testNonLocalReturnToCatchBlock() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnToCatchBlock.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/generateTestDataForReservedWords.kt b/generators/src/org/jetbrains/kotlin/generators/tests/generateTestDataForReservedWords.kt index c3c4ddfc2e6..2b3ca08680e 100644 --- a/generators/src/org/jetbrains/kotlin/generators/tests/generateTestDataForReservedWords.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/generateTestDataForReservedWords.kt @@ -16,8 +16,8 @@ package org.jetbrains.kotlin.generators.tests -import org.jetbrains.kotlin.js.backend.ast.JsFunctionScope import org.jetbrains.kotlin.generators.util.GeneratorsFileUtil +import org.jetbrains.kotlin.js.backend.ast.JsDeclarationScope import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.renderer.KeywordStringsGenerated import java.io.File @@ -366,8 +366,8 @@ val testNotRenamedByRef = testNotRenamed("$KEYWORD_MARKER()") // KEYWORDS -val SHOULD_BE_ESCAPED = JsFunctionScope.RESERVED_WORDS.filter { it in KeywordStringsGenerated.KEYWORDS }.sorted() -val SHOULD_NOT_BE_ESCAPED = JsFunctionScope.RESERVED_WORDS.filter { it !in SHOULD_BE_ESCAPED }.sorted() +val SHOULD_BE_ESCAPED = JsDeclarationScope.RESERVED_WORDS.filter { it in KeywordStringsGenerated.KEYWORDS }.sorted() +val SHOULD_NOT_BE_ESCAPED = JsDeclarationScope.RESERVED_WORDS.filter { it !in SHOULD_BE_ESCAPED }.sorted() // all keywords by portions diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsCatchScope.java b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsCatchScope.java index ca4e3075f89..63a61110ecd 100644 --- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsCatchScope.java +++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/JsCatchScope.java @@ -10,11 +10,11 @@ import org.jetbrains.annotations.NotNull; * A special scope used only for catch blocks. It only holds a single symbol: * the catch argument's name. */ -public class JsCatchScope extends JsScope { +public class JsCatchScope extends JsDeclarationScope { private final JsName name; public JsCatchScope(JsScope parent, @NotNull String ident) { - super(parent, "Catch scope"); + super(parent, "Catch scope", true); name = new JsName(this, ident, false); } diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/jsScopes.kt b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/jsScopes.kt index e3d2bf3cda9..d2a66bda2ce 100644 --- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/jsScopes.kt +++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/jsScopes.kt @@ -16,7 +16,7 @@ package org.jetbrains.kotlin.js.backend.ast -import java.util.Stack +import java.util.* class JsObjectScope(parent: JsScope, description: String) : JsScope(parent, description) @@ -24,15 +24,18 @@ object JsDynamicScope : JsScope(null, "Scope for dynamic declarations") { override fun doCreateName(name: String) = JsName(this, name, false) } -open class JsFunctionScope(parent: JsScope, description: String) : JsScope(parent, description) { - - private val labelScopes = Stack() - private val topLabelScope: LabelScope? - get() = if (labelScopes.isNotEmpty()) labelScopes.peek() else null - +open class JsFunctionScope(parent: JsScope, description: String) : JsDeclarationScope(parent, description) { override fun hasOwnName(name: String): Boolean = RESERVED_WORDS.contains(name) || super.hasOwnName(name) open fun declareNameUnsafe(identifier: String): JsName = super.declareName(identifier) +} + +open class JsDeclarationScope(parent: JsScope, description: String, useParentScopeStack: Boolean = false) : JsScope(parent, description) { + private val labelScopes: Stack = + if (parent is JsDeclarationScope && useParentScopeStack) parent.labelScopes else Stack() + + private val topLabelScope + get() = if (labelScopes.isNotEmpty()) labelScopes.peek() else null open fun enterLabel(label: String): JsName { val scope = LabelScope(topLabelScope, label) @@ -58,7 +61,7 @@ open class JsFunctionScope(parent: JsScope, description: String) : JsScope(paren else -> ident } - labelName = JsName(this@JsFunctionScope, freshIdent, false) + labelName = JsName(this@JsDeclarationScope, freshIdent, false) } override fun findOwnName(name: String): JsName? = diff --git a/js/js.parser/src/com/google/gwt/dev/js/ScopeContext.kt b/js/js.parser/src/com/google/gwt/dev/js/ScopeContext.kt index 76fc83b0315..02bfc251d81 100644 --- a/js/js.parser/src/com/google/gwt/dev/js/ScopeContext.kt +++ b/js/js.parser/src/com/google/gwt/dev/js/ScopeContext.kt @@ -17,8 +17,7 @@ package com.google.gwt.dev.js import org.jetbrains.kotlin.js.backend.ast.* - -import java.util.Stack +import java.util.* class ScopeContext(scope: JsScope) { private val rootScope = generateSequence(scope) { it.parent }.first { it is JsRootScope } @@ -35,7 +34,7 @@ class ScopeContext(scope: JsScope) { } fun exitFunction() { - assert(currentScope is JsFunctionScope) + assert(currentScope is JsDeclarationScope) exitScope() } @@ -51,13 +50,13 @@ class ScopeContext(scope: JsScope) { } fun enterLabel(ident: String): JsName = - (currentScope as JsFunctionScope).enterLabel(ident) + (currentScope as JsDeclarationScope).enterLabel(ident) fun exitLabel() = - (currentScope as JsFunctionScope).exitLabel() + (currentScope as JsDeclarationScope).exitLabel() fun labelFor(ident: String): JsName? = - (currentScope as JsFunctionScope).findLabel(ident) + (currentScope as JsDeclarationScope).findLabel(ident) fun globalNameFor(ident: String): JsName = currentScope.findName(ident) ?: rootScope.declareName(ident) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/NonLocalReturnsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/NonLocalReturnsTestGenerated.java index 9c169cd4be7..c9b6356821d 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/NonLocalReturnsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/NonLocalReturnsTestGenerated.java @@ -155,12 +155,24 @@ public class NonLocalReturnsTestGenerated extends AbstractNonLocalReturnsTest { doTest(fileName); } + @TestMetadata("nonLocalReturnFromCatchBlock.kt") + public void testNonLocalReturnFromCatchBlock() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnFromCatchBlock.kt"); + doTest(fileName); + } + @TestMetadata("nonLocalReturnFromOuterLambda.kt") public void testNonLocalReturnFromOuterLambda() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnFromOuterLambda.kt"); doTest(fileName); } + @TestMetadata("nonLocalReturnToCatchBlock.kt") + public void testNonLocalReturnToCatchBlock() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/nonLocalReturnToCatchBlock.kt"); + doTest(fileName); + } + @TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)