[JS] Support secondary constructors in JsExport
This commit is contained in:
@@ -5,7 +5,9 @@
|
||||
|
||||
package org.jetbrains.kotlin.ir.backend.js.export
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
|
||||
sealed class ExportedDeclaration
|
||||
|
||||
@@ -56,7 +58,7 @@ class ExportedClass(
|
||||
val superInterfaces: List<ExportedType> = emptyList(),
|
||||
val typeParameters: List<String>,
|
||||
val members: List<ExportedDeclaration>,
|
||||
val statics: List<ExportedDeclaration>,
|
||||
val nestedClasses: List<ExportedClass>,
|
||||
val ir: IrClass
|
||||
) : ExportedDeclaration()
|
||||
|
||||
|
||||
+6
-9
@@ -6,7 +6,6 @@
|
||||
package org.jetbrains.kotlin.ir.backend.js.export
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.isExpect
|
||||
import org.jetbrains.kotlin.backend.common.ir.isMethodOfAny
|
||||
import org.jetbrains.kotlin.config.CommonConfigurationKeys
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
@@ -23,8 +22,6 @@ import org.jetbrains.kotlin.ir.symbols.IrClassifierSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.FqNameUnsafe
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
|
||||
class ExportModelGenerator(val context: JsIrBackendContext) {
|
||||
@@ -139,14 +136,14 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
|
||||
|
||||
private fun exportClass(
|
||||
klass: IrClass
|
||||
): ExportedDeclaration? {
|
||||
): ExportedClass? {
|
||||
when (val exportability = classExportability(klass)) {
|
||||
is Exportability.Prohibited -> return ErrorDeclaration(exportability.reason)
|
||||
is Exportability.Prohibited -> return error(exportability.reason)
|
||||
is Exportability.NotNeeded -> return null
|
||||
}
|
||||
|
||||
val members = mutableListOf<ExportedDeclaration>()
|
||||
val statics = mutableListOf<ExportedDeclaration>()
|
||||
val nestedClasses = mutableListOf<ExportedClass>()
|
||||
|
||||
for (declaration in klass.declarations) {
|
||||
val candidate = getExportCandidate(declaration) ?: continue
|
||||
@@ -163,7 +160,7 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
|
||||
members.addIfNotNull(exportProperty(candidate))
|
||||
|
||||
is IrClass ->
|
||||
statics.addIfNotNull(exportClass(candidate))
|
||||
nestedClasses.addIfNotNull(exportClass(candidate))
|
||||
|
||||
is IrField -> {
|
||||
assert(candidate.correspondingPropertySymbol != null) {
|
||||
@@ -198,7 +195,7 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
|
||||
superInterfaces = superInterfaces,
|
||||
typeParameters = typeParameters,
|
||||
members = members,
|
||||
statics = statics,
|
||||
nestedClasses = nestedClasses,
|
||||
ir = klass
|
||||
)
|
||||
}
|
||||
@@ -413,4 +410,4 @@ val strictModeReservedWords = setOf(
|
||||
"yield"
|
||||
)
|
||||
|
||||
private val allReservedWords = reservedWords + strictModeReservedWords
|
||||
private val allReservedWords = reservedWords + strictModeReservedWords
|
||||
|
||||
+8
-2
@@ -8,7 +8,8 @@ package org.jetbrains.kotlin.ir.backend.js.export
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.JsAstUtils
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.defineProperty
|
||||
import org.jetbrains.kotlin.ir.backend.js.transformers.irToJs.jsAssignment
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.*
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.NameTable
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.NameTables
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
|
||||
|
||||
@@ -85,7 +86,12 @@ class ExportModelToJsStatements(
|
||||
)
|
||||
)
|
||||
).makeStmt()
|
||||
val staticsExport = declaration.statics.flatMap { generateDeclarationExport(it, newNameSpace) }
|
||||
|
||||
val staticFunctions = declaration.members.filter { it is ExportedFunction && it.isStatic }
|
||||
|
||||
val staticsExport = (staticFunctions + declaration.nestedClasses)
|
||||
.flatMap { generateDeclarationExport(it, newNameSpace) }
|
||||
|
||||
listOf(klassExport) + staticsExport
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ fun ExportedDeclaration.toTypeScript(indent: String): String = indent + when (th
|
||||
val modifiers = if (isAbstract && !isInterface) "abstract " else ""
|
||||
|
||||
val klassExport = "$modifiers$keyword $name$renderedTypeParameters$superClassClause$superInterfacesClause {\n$membersString$indent}"
|
||||
val staticsExport = if (statics.isNotEmpty()) "\n" + ExportedNamespace(name, statics).toTypeScript(indent) else ""
|
||||
val staticsExport = if (nestedClasses.isNotEmpty()) "\n" + ExportedNamespace(name, nestedClasses).toTypeScript(indent) else ""
|
||||
klassExport + staticsExport
|
||||
}
|
||||
}
|
||||
|
||||
+5
-3
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
|
||||
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
|
||||
import org.jetbrains.kotlin.ir.backend.js.utils.getJsName
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrBlockBodyImpl
|
||||
@@ -176,9 +177,10 @@ private fun buildFactoryDeclaration(constructor: IrConstructor, irClass: IrClass
|
||||
Modality.FINAL,
|
||||
constructor.isInline,
|
||||
constructor.isExternal
|
||||
).also {
|
||||
it.copyTypeParametersFrom(constructor.parentAsClass)
|
||||
it.valueParameters += constructor.valueParameters.map { p -> p.copyTo(it) }
|
||||
).also { factory ->
|
||||
factory.copyTypeParametersFrom(constructor.parentAsClass)
|
||||
factory.valueParameters += constructor.valueParameters.map { p -> p.copyTo(factory) }
|
||||
factory.annotations = constructor.annotations
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Generated
+18
@@ -29,6 +29,24 @@ public class IrJsTypeScriptExportTestGenerated extends AbstractIrJsTypeScriptExp
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/typescript-export"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||
}
|
||||
|
||||
@TestMetadata("js/js.translator/testData/typescript-export/constructors")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Constructors extends AbstractIrJsTypeScriptExportTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInConstructors() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/typescript-export/constructors"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true);
|
||||
}
|
||||
|
||||
@TestMetadata("constructors.kt")
|
||||
public void testConstructors() throws Exception {
|
||||
runTest("js/js.translator/testData/typescript-export/constructors/constructors.kt");
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("js/js.translator/testData/typescript-export/declarations")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Generated
+13
@@ -29,6 +29,19 @@ public class LegacyJsTypeScriptExportTestGenerated extends AbstractLegacyJsTypeS
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/typescript-export"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS, true);
|
||||
}
|
||||
|
||||
@TestMetadata("js/js.translator/testData/typescript-export/constructors")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class Constructors extends AbstractLegacyJsTypeScriptExportTest {
|
||||
private void runTest(String testDataFilePath) throws Exception {
|
||||
KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath);
|
||||
}
|
||||
|
||||
public void testAllFilesPresentInConstructors() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/typescript-export/constructors"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS, true);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("js/js.translator/testData/typescript-export/declarations")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1290
|
||||
|
||||
// TODO: Support JsExport on object declarations: KT-39117
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
|
||||
@JsExport
|
||||
object A {
|
||||
@JsName("js_method") fun f() = "method"
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1291
|
||||
|
||||
// TODO: Support JsExport on object declarations: KT-39117
|
||||
// IGNORE_BACKEND: JS_IR
|
||||
|
||||
@JsExport
|
||||
object A {
|
||||
@JsName("js_f") fun f(x: Int) = "f($x)"
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
declare namespace JS_TESTS {
|
||||
type Nullable<T> = T | null | undefined
|
||||
class ClassWithDefaultCtor {
|
||||
constructor()
|
||||
readonly x: string;
|
||||
}
|
||||
class ClassWithPrimaryCtor {
|
||||
constructor(x: string)
|
||||
readonly x: string;
|
||||
}
|
||||
class ClassWithSecondaryCtor {
|
||||
readonly x: string;
|
||||
static create(y: string): ClassWithSecondaryCtor
|
||||
}
|
||||
class ClassWithMultipleSecondaryCtors {
|
||||
readonly x: string;
|
||||
static createFromString(y: string): ClassWithMultipleSecondaryCtors
|
||||
static createFromInts(y: number, z: number): ClassWithMultipleSecondaryCtors
|
||||
}
|
||||
class OpenClassWithMixedConstructors {
|
||||
constructor(x: string)
|
||||
readonly x: string;
|
||||
static createFromStrings(y: string, z: string): OpenClassWithMixedConstructors
|
||||
static createFromInts(y: number, z: number): OpenClassWithMixedConstructors
|
||||
}
|
||||
class DerivedClassWithSecondaryCtor extends OpenClassWithMixedConstructors {
|
||||
static delegateToPrimary(y: string): DerivedClassWithSecondaryCtor
|
||||
static delegateToCreateFromInts(y: number, z: number): DerivedClassWithSecondaryCtor
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// TARGET_BACKEND: JS_IR
|
||||
// CHECK_TYPESCRIPT_DECLARATIONS
|
||||
// RUN_PLAIN_BOX_FUNCTION
|
||||
// SKIP_MINIFICATION
|
||||
// SKIP_NODE_JS
|
||||
|
||||
// TODO fix statics export in DCE-driven mode
|
||||
// SKIP_DCE_DRIVEN
|
||||
|
||||
@file:JsExport
|
||||
|
||||
class ClassWithDefaultCtor {
|
||||
val x = "ClassWithDefaultCtor::x"
|
||||
}
|
||||
|
||||
class ClassWithPrimaryCtor(
|
||||
val x: String
|
||||
)
|
||||
|
||||
class ClassWithSecondaryCtor {
|
||||
val x: String
|
||||
@JsName("create")
|
||||
constructor(y: String) {
|
||||
x = y
|
||||
}
|
||||
}
|
||||
|
||||
class ClassWithMultipleSecondaryCtors {
|
||||
val x: String
|
||||
|
||||
@JsName("createFromString")
|
||||
constructor(y: String) {
|
||||
x = "fromString:$y"
|
||||
}
|
||||
|
||||
@JsName("createFromInts")
|
||||
constructor(y: Int, z: Int) {
|
||||
x = "fromInts:$y:$z"
|
||||
}
|
||||
}
|
||||
|
||||
open class OpenClassWithMixedConstructors(val x: String) {
|
||||
@JsName("createFromStrings")
|
||||
constructor(y: String, z: String) : this("fromStrings:$y:$z")
|
||||
|
||||
@JsName("createFromInts")
|
||||
constructor(y: Int, z: Int) : this(y.toString(), z.toString())
|
||||
}
|
||||
|
||||
class DerivedClassWithSecondaryCtor : OpenClassWithMixedConstructors {
|
||||
@JsName("delegateToPrimary")
|
||||
constructor(y: String) : super(y)
|
||||
|
||||
@JsName("delegateToCreateFromInts")
|
||||
constructor(y: Int, z: Int) : super(y, z)
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
"use strict";
|
||||
var ClassWithDefaultCtor = JS_TESTS.ClassWithDefaultCtor, ClassWithPrimaryCtor = JS_TESTS.ClassWithPrimaryCtor, ClassWithSecondaryCtor = JS_TESTS.ClassWithSecondaryCtor, ClassWithMultipleSecondaryCtors = JS_TESTS.ClassWithMultipleSecondaryCtors, DerivedClassWithSecondaryCtor = JS_TESTS.DerivedClassWithSecondaryCtor, OpenClassWithMixedConstructors = JS_TESTS.OpenClassWithMixedConstructors;
|
||||
function box() {
|
||||
var o1 = new ClassWithDefaultCtor();
|
||||
if (o1.x !== "ClassWithDefaultCtor::x")
|
||||
return "Fail: ClassWithDefaultCtor";
|
||||
var o2 = new ClassWithPrimaryCtor("foo");
|
||||
if (o2.x !== "foo")
|
||||
return "Fail: ClassWithPrimaryCtor";
|
||||
var o3 = ClassWithSecondaryCtor.create("foo2");
|
||||
if (o3.x !== "foo2")
|
||||
return "Fail: ClassWithSecondaryCtor.create";
|
||||
var o4 = ClassWithMultipleSecondaryCtors.createFromString("foo3");
|
||||
if (o4.x !== "fromString:foo3")
|
||||
return "Fail: ClassWithMultipleSecondaryCtors.createFromString";
|
||||
var o5 = ClassWithMultipleSecondaryCtors.createFromInts(1, 2);
|
||||
if (o5.x !== "fromInts:1:2")
|
||||
return "Fail: ClassWithMultipleSecondaryCtors.createFromInts";
|
||||
var o6 = new OpenClassWithMixedConstructors("foo4");
|
||||
if (o6.x !== "foo4")
|
||||
return "Fail: OpenClassWithMixedConstructors";
|
||||
var o7 = OpenClassWithMixedConstructors.createFromStrings("foo", "bar");
|
||||
if (o7.x !== "fromStrings:foo:bar")
|
||||
return "Fail: OpenClassWithMixedConstructors.createFromStrings";
|
||||
var o8 = OpenClassWithMixedConstructors.createFromInts(10, -20);
|
||||
if (o8.x !== "fromStrings:10:-20")
|
||||
return "Fail: OpenClassWithMixedConstructors.createFromInts";
|
||||
var o9 = DerivedClassWithSecondaryCtor.delegateToPrimary("foo6");
|
||||
if (o9.x !== "foo6")
|
||||
return "Fail: DerivedClassWithSecondaryCtor.delegateToPrimary";
|
||||
var o10 = DerivedClassWithSecondaryCtor.delegateToCreateFromInts(-10, 20);
|
||||
if (o10.x !== "fromStrings:-10:20")
|
||||
return "Fail: DerivedClassWithSecondaryCtor.delegateToCreateFromInts";
|
||||
return "OK";
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
const {
|
||||
ClassWithDefaultCtor,
|
||||
ClassWithPrimaryCtor,
|
||||
ClassWithSecondaryCtor,
|
||||
ClassWithMultipleSecondaryCtors,
|
||||
DerivedClassWithSecondaryCtor,
|
||||
OpenClassWithMixedConstructors
|
||||
} = JS_TESTS;
|
||||
|
||||
function box(): string {
|
||||
const o1 = new ClassWithDefaultCtor();
|
||||
if (o1.x !== "ClassWithDefaultCtor::x") return "Fail: ClassWithDefaultCtor";
|
||||
|
||||
|
||||
const o2 = new ClassWithPrimaryCtor("foo");
|
||||
if (o2.x !== "foo") return "Fail: ClassWithPrimaryCtor";
|
||||
|
||||
|
||||
const o3 = ClassWithSecondaryCtor.create("foo2");
|
||||
if (o3.x !== "foo2") return "Fail: ClassWithSecondaryCtor.create";
|
||||
|
||||
|
||||
const o4 = ClassWithMultipleSecondaryCtors.createFromString("foo3");
|
||||
if (o4.x !== "fromString:foo3") return "Fail: ClassWithMultipleSecondaryCtors.createFromString";
|
||||
|
||||
const o5 = ClassWithMultipleSecondaryCtors.createFromInts(1, 2);
|
||||
if (o5.x !== "fromInts:1:2") return "Fail: ClassWithMultipleSecondaryCtors.createFromInts";
|
||||
|
||||
|
||||
const o6 = new OpenClassWithMixedConstructors("foo4");
|
||||
if (o6.x !== "foo4") return "Fail: OpenClassWithMixedConstructors";
|
||||
|
||||
const o7 = OpenClassWithMixedConstructors.createFromStrings("foo", "bar");
|
||||
if (o7.x !== "fromStrings:foo:bar") return "Fail: OpenClassWithMixedConstructors.createFromStrings";
|
||||
|
||||
const o8 = OpenClassWithMixedConstructors.createFromInts(10, -20);
|
||||
if (o8.x !== "fromStrings:10:-20") return "Fail: OpenClassWithMixedConstructors.createFromInts";
|
||||
|
||||
|
||||
const o9 = DerivedClassWithSecondaryCtor.delegateToPrimary("foo6");
|
||||
if (o9.x !== "foo6") return "Fail: DerivedClassWithSecondaryCtor.delegateToPrimary";
|
||||
|
||||
const o10 = DerivedClassWithSecondaryCtor.delegateToCreateFromInts(-10, 20);
|
||||
if (o10.x !== "fromStrings:-10:20") return "Fail: DerivedClassWithSecondaryCtor.delegateToCreateFromInts";
|
||||
|
||||
return "OK";
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"include": [ "./*" ],
|
||||
"extends": "../common.tsconfig.json"
|
||||
}
|
||||
Reference in New Issue
Block a user