From 80361bf2fb7006014d576c9fc21aeee2aaf246b7 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 7 Sep 2016 11:40:41 +0300 Subject: [PATCH] KT-13583: allow local classes to capture members of outer classes --- .../kotlin/js/test/semantics/ClosureTest.java | 4 ++ .../kotlin/js/test/semantics/ObjectTest.java | 4 ++ .../js/translate/context/UsageTracker.kt | 6 +++ .../cases/enclosingClassFromLocalClass.kt | 51 +++++++++++++++++++ .../testData/object/cases/kt3684.kt | 18 +++++++ 5 files changed, 83 insertions(+) create mode 100644 js/js.translator/testData/closure/cases/enclosingClassFromLocalClass.kt create mode 100644 js/js.translator/testData/object/cases/kt3684.kt diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ClosureTest.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ClosureTest.java index 32e2c8d298c..2bd6adc2917 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ClosureTest.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ClosureTest.java @@ -192,6 +192,10 @@ public final class ClosureTest extends SingleFileTranslationTest { checkFooBoxIsOk(); } + public void testEnclosingClassFromLocalClass() throws Exception { + checkFooBoxIsOk(); + } + public void testObjectWithInvokeOperator() throws Exception { checkFooBoxIsOk(); } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ObjectTest.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ObjectTest.java index d929c74ecf7..177256ca54b 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ObjectTest.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/ObjectTest.java @@ -63,4 +63,8 @@ public final class ObjectTest extends SingleFileTranslationTest { public void testDontPolluteObject() throws Exception { checkFooBoxIsOk(); } + + public void testKt3684() throws Exception { + checkFooBoxIsOk(); + } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/UsageTracker.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/UsageTracker.kt index 6b8714091b0..cbc75aba6c7 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/UsageTracker.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/UsageTracker.kt @@ -104,9 +104,15 @@ class UsageTracker( if (descriptor !is ReceiverParameterDescriptor) return false if (containingDescriptor !is ClassDescriptor && containingDescriptor !is ConstructorDescriptor) return false + // Class in which we are trying to capture variable val containingClass = getParentOfType(containingDescriptor, ClassDescriptor::class.java, false) ?: return false + + // Class which instance we are trying to capture val currentClass = descriptor.containingDeclaration as? ClassDescriptor ?: return false + // We always capture enclosing class if it's not outer (i.e. we are capturing members of enclosing class to a local class) + if (containingClass.containingDeclaration !is ClassDescriptor) return false + for (outerDeclaration in generateSequence(containingClass) { it.containingDeclaration as? ClassDescriptor }) { if (DescriptorUtils.isSubclass(outerDeclaration, currentClass)) return true } diff --git a/js/js.translator/testData/closure/cases/enclosingClassFromLocalClass.kt b/js/js.translator/testData/closure/cases/enclosingClassFromLocalClass.kt new file mode 100644 index 00000000000..ee9fe74eb32 --- /dev/null +++ b/js/js.translator/testData/closure/cases/enclosingClassFromLocalClass.kt @@ -0,0 +1,51 @@ +package foo + +open class A(private val x: String) { + fun foo(): String { + class B : A("fail1: simple nested") { + fun bar() = x + } + return B().bar() + } +} + +open class A1(private val x: String) { + fun foo(): String { + class B1 { + fun bar(): String { + class C1 : A1("fail2: deeply nested") { + fun baz() = x + } + return C1().baz() + } + } + return B1().bar() + } +} + +open class A2(private val x: String) { + fun foo(): String { + class B2 : A2("fail3: deeply nested") { + fun bar(): String { + class C2 { + fun baz() = x + } + return C2().baz() + } + } + return B2().bar() + } +} + +fun box(): String { + val result = A("OK").foo() + if (result != "OK") return result + + val result1 = A1("OK").foo() + if (result1 != "OK") return result1 + + val result2 = A2("OK").foo() + if (result2 != "OK") return result2 + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/object/cases/kt3684.kt b/js/js.translator/testData/object/cases/kt3684.kt new file mode 100644 index 00000000000..536b9cad265 --- /dev/null +++ b/js/js.translator/testData/object/cases/kt3684.kt @@ -0,0 +1,18 @@ +// copied from JVM backend tests +package foo + +open class X(private val n: String) { + + fun foo(): String { + return object : X("inner") { + fun print(): String { + return n; + } + }.print() + } +} + + +fun box(): String { + return X("OK").foo() +}