From be999564b10c42d872220a47b2e00cf6bc627f13 Mon Sep 17 00:00:00 2001 From: Sergej Jaskiewicz Date: Thu, 26 Aug 2021 16:30:23 +0300 Subject: [PATCH] [JS IR] Export nested objects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Companion objects are exported as ParentClass.Companion. Companion object's members are not exposed to its parent class — one must reference the companion object explicitly if they want to access its members. #KT-43783 Fixed --- .../js/export/ExportModelToJsStatements.kt | 8 ++- .../semantics/IrBoxJsES6TestGenerated.java | 5 ++ .../ir/semantics/IrBoxJsTestGenerated.java | 5 ++ .../js/test/semantics/BoxJsTestGenerated.java | 5 ++ .../testData/box/export/exportNestedObject.kt | 58 +++++++++++++++++++ .../declarations/declarations.kt | 2 - .../declarations/declarations__main.ts | 3 + 7 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 js/js.translator/testData/box/export/exportNestedObject.kt diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToJsStatements.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToJsStatements.kt index d346b894eef..b0b8e57a549 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToJsStatements.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelToJsStatements.kt @@ -86,9 +86,15 @@ class ExportModelToJsStatements( ) ).makeStmt() + // These are only used when exporting secondary constructors annotated with @JsName val staticFunctions = declaration.members.filter { it is ExportedFunction && it.isStatic } - val staticsExport = (staticFunctions + declaration.nestedClasses) + // Nested objects are exported as static properties + val staticProperties = declaration.members.mapNotNull { + (it as? ExportedProperty)?.takeIf { it.isStatic } + } + + val staticsExport = (staticFunctions + staticProperties + declaration.nestedClasses) .flatMap { generateDeclarationExport(it, newNameSpace) } listOf(klassExport) + staticsExport diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrBoxJsES6TestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrBoxJsES6TestGenerated.java index 5cb3e13bd12..3d002bc6980 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrBoxJsES6TestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrBoxJsES6TestGenerated.java @@ -1627,6 +1627,11 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test { runTest("js/js.translator/testData/box/export/exportNestedClass.kt"); } + @TestMetadata("exportNestedObject.kt") + public void testExportNestedObject() throws Exception { + runTest("js/js.translator/testData/box/export/exportNestedObject.kt"); + } + @TestMetadata("nonIndetifierModuleName.kt") public void testNonIndetifierModuleName() throws Exception { runTest("js/js.translator/testData/box/export/nonIndetifierModuleName.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java index c8b26f7fb4a..75748baa40b 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrBoxJsTestGenerated.java @@ -1627,6 +1627,11 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest { runTest("js/js.translator/testData/box/export/exportNestedClass.kt"); } + @TestMetadata("exportNestedObject.kt") + public void testExportNestedObject() throws Exception { + runTest("js/js.translator/testData/box/export/exportNestedObject.kt"); + } + @TestMetadata("nonIndetifierModuleName.kt") public void testNonIndetifierModuleName() throws Exception { runTest("js/js.translator/testData/box/export/nonIndetifierModuleName.kt"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index df91f1e965d..8f9f7c7e708 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -1632,6 +1632,11 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { runTest("js/js.translator/testData/box/export/exportNestedClass.kt"); } + @TestMetadata("exportNestedObject.kt") + public void testExportNestedObject() throws Exception { + runTest("js/js.translator/testData/box/export/exportNestedObject.kt"); + } + @TestMetadata("nonIndetifierModuleName.kt") public void testNonIndetifierModuleName() throws Exception { runTest("js/js.translator/testData/box/export/nonIndetifierModuleName.kt"); diff --git a/js/js.translator/testData/box/export/exportNestedObject.kt b/js/js.translator/testData/box/export/exportNestedObject.kt new file mode 100644 index 00000000000..200dacb8146 --- /dev/null +++ b/js/js.translator/testData/box/export/exportNestedObject.kt @@ -0,0 +1,58 @@ +// EXPECTED_REACHABLE_NODES: 1265 +// RUN_PLAIN_BOX_FUNCTION +// INFER_MAIN_MODULE +// SKIP_MINIFICATION +// SKIP_DCE_DRIVEN +// SKIP_NODE_JS + +// See KT-43783 + +// MODULE: nestedObjectExport +// FILE: lib.kt + +@JsExport +class Abc { + companion object AbcCompanion { + fun xyz(): String = "Companion object method OK" + + val prop: String + get() = "Companion object property OK" + } +} + +@JsExport +class Foo { + companion object { + fun xyz(): String = "Companion object method OK" + + val prop: String + get() = "Companion object property OK" + } +} + +@JsExport +sealed class MyEnum(val name: String) { + object A: MyEnum("A") + object B: MyEnum("B") + object C: MyEnum("C") +} + +// FILE: test.js + +function box() { + const abcCompanion = nestedObjectExport.Abc.AbcCompanion; + + if (abcCompanion.xyz() != 'Companion object method OK') return 'companion object function failure'; + if (abcCompanion.prop != 'Companion object property OK') return 'companion object property failure'; + + const justCompanion = nestedObjectExport.Foo.Companion; + + if (justCompanion.xyz() != 'Companion object method OK') return 'companion object function failure'; + if (justCompanion.prop != 'Companion object property OK') return 'companion object property failure'; + + if (nestedObjectExport.MyEnum.A.name != 'A') return 'MyEnum.A failure'; + if (nestedObjectExport.MyEnum.B.name != 'B') return 'MyEnum.B failure'; + if (nestedObjectExport.MyEnum.C.name != 'C') return 'MyEnum.C failure'; + + return 'OK'; +} diff --git a/js/js.translator/testData/typescript-export/declarations/declarations.kt b/js/js.translator/testData/typescript-export/declarations/declarations.kt index 88b4a6d915b..460b80fc12c 100644 --- a/js/js.translator/testData/typescript-export/declarations/declarations.kt +++ b/js/js.translator/testData/typescript-export/declarations/declarations.kt @@ -96,8 +96,6 @@ object O { fun takesO(o: O): Int = O.x + O.foo() -// Test that JsExport with companion object compiles without error. -// Usage is not supported yet. class KT_37829 { companion object { val x = 10 diff --git a/js/js.translator/testData/typescript-export/declarations/declarations__main.ts b/js/js.translator/testData/typescript-export/declarations/declarations__main.ts index 3dc5e6d1771..9fbf5937ce8 100644 --- a/js/js.translator/testData/typescript-export/declarations/declarations__main.ts +++ b/js/js.translator/testData/typescript-export/declarations/declarations__main.ts @@ -22,6 +22,7 @@ import _valCustomWithField = JS_TESTS.foo._valCustomWithField; import A4 = JS_TESTS.foo.A4; import O = JS_TESTS.foo.O; import takesO = JS_TESTS.foo.takesO; +import KT_37829 = JS_TESTS.foo.KT_37829; function assert(condition: boolean) { if (!condition) { @@ -92,5 +93,7 @@ function box(): string { assert(O.foo() === 20); assert(takesO(O) === 30); + assert(KT_37829.Companion.x == 10); + return "OK"; } \ No newline at end of file