[JS IR] Fix export of constructor with default argument (KT-41275)

This commit is contained in:
Svyatoslav Kuzmich
2020-08-31 15:32:23 +03:00
parent 5b136516c4
commit 162d9744ff
9 changed files with 56 additions and 15 deletions
@@ -40,7 +40,7 @@ class JsIrBackendContext(
override val irBuiltIns: IrBuiltIns, override val irBuiltIns: IrBuiltIns,
val symbolTable: SymbolTable, val symbolTable: SymbolTable,
irModuleFragment: IrModuleFragment, irModuleFragment: IrModuleFragment,
val additionalExportedDeclarations: Set<FqName>, val additionalExportedDeclarationNames: Set<FqName>,
override val configuration: CompilerConfiguration, // TODO: remove configuration from backend context override val configuration: CompilerConfiguration, // TODO: remove configuration from backend context
override val scriptMode: Boolean = false, override val scriptMode: Boolean = false,
override val es6mode: Boolean = false override val es6mode: Boolean = false
@@ -65,6 +65,8 @@ class JsIrBackendContext(
val externalPackageFragment = mutableMapOf<IrFileSymbol, IrFile>() val externalPackageFragment = mutableMapOf<IrFileSymbol, IrFile>()
val externalDeclarations = hashSetOf<IrDeclaration>() val externalDeclarations = hashSetOf<IrDeclaration>()
val additionalExportedDeclarations = mutableSetOf<IrDeclaration>()
val bodilessBuiltInsPackageFragment: IrPackageFragment = IrExternalPackageFragmentImpl( val bodilessBuiltInsPackageFragment: IrPackageFragment = IrExternalPackageFragmentImpl(
DescriptorlessExternalPackageFragmentSymbol(), DescriptorlessExternalPackageFragmentSymbol(),
FqName("kotlin") FqName("kotlin")
@@ -331,9 +331,19 @@ class ExportModelGenerator(val context: JsIrBackendContext) {
if (function.isFakeOverriddenFromAny()) if (function.isFakeOverriddenFromAny())
return Exportability.NotNeeded return Exportability.NotNeeded
if (function.name.asString().endsWith("-impl"))
val nameString = function.name.asString()
if (nameString.endsWith("-impl"))
return Exportability.NotNeeded return Exportability.NotNeeded
// Workaround in case IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER is rewritten.
// TODO: Properly fix KT-41613
if (nameString.endsWith("\$") && function.valueParameters.any { "\$mask" in it.name.asString() }) {
return Exportability.NotNeeded
}
val name = function.getExportedIdentifier() val name = function.getExportedIdentifier()
// TODO: Use [] syntax instead of prohibiting // TODO: Use [] syntax instead of prohibiting
if (name in allReservedWords) if (name in allReservedWords)
@@ -379,7 +389,10 @@ private fun getExportCandidate(declaration: IrDeclaration): IrDeclarationWithNam
} }
private fun shouldDeclarationBeExported(declaration: IrDeclarationWithName, context: JsIrBackendContext): Boolean { private fun shouldDeclarationBeExported(declaration: IrDeclarationWithName, context: JsIrBackendContext): Boolean {
if (declaration.fqNameWhenAvailable in context.additionalExportedDeclarations) if (declaration.fqNameWhenAvailable in context.additionalExportedDeclarationNames)
return true
if (declaration in context.additionalExportedDeclarations)
return true return true
if (declaration.isJsExport()) if (declaration.isJsExport())
@@ -5,14 +5,15 @@
package org.jetbrains.kotlin.ir.backend.js.lower package org.jetbrains.kotlin.ir.backend.js.lower
import org.jetbrains.kotlin.backend.common.CommonBackendContext
import org.jetbrains.kotlin.backend.common.DeclarationTransformer import org.jetbrains.kotlin.backend.common.DeclarationTransformer
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.export.isExported
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.file import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.ir.util.isEffectivelyExternal import org.jetbrains.kotlin.ir.util.isEffectivelyExternal
// Move static member declarations from classes to top level // Move static member declarations from classes to top level
class StaticMembersLowering(val context: CommonBackendContext) : DeclarationTransformer { class StaticMembersLowering(val context: JsIrBackendContext) : DeclarationTransformer {
override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? { override fun transformFlat(declaration: IrDeclaration): List<IrDeclaration>? {
(declaration.parent as? IrClass)?.let { irClass -> (declaration.parent as? IrClass)?.let { irClass ->
val isStatic = when (declaration) { val isStatic = when (declaration) {
@@ -23,6 +24,11 @@ class StaticMembersLowering(val context: CommonBackendContext) : DeclarationTran
} }
if (isStatic) { if (isStatic) {
// JsExport might be inherited from parent declaration which would be broken if we move it out of its parent.
// Marking declaration as exported explicitly.
if (declaration.isExported(context)) {
context.additionalExportedDeclarations.add(declaration)
}
declaration.parent = irClass.file declaration.parent = irClass.file
irClass.file.declarations += declaration irClass.file.declarations += declaration
return listOf() return listOf()
@@ -312,7 +312,8 @@ class NameTables(
parent = parent.parent parent = parent.parent
} }
return mappedNames[mapToKey(declaration)] ?: error("Can't find name for declaration ${declaration.render()}") return mappedNames[mapToKey(declaration)]
?: error("Can't find name for declaration ${declaration.render()}")
} }
fun getNameForMemberField(field: IrField): String { fun getNameForMemberField(field: IrField): String {
@@ -316,11 +316,11 @@ private val blockDecomposerLoweringPhase = makeCustomWasmModulePhase(
// description = "Generate invocations to kotlin.test suite and test functions" // description = "Generate invocations to kotlin.test suite and test functions"
//) //)
// //
private val staticMembersLoweringPhase = makeWasmModulePhase( //private val staticMembersLoweringPhase = makeWasmModulePhase(
::StaticMembersLowering, // ::StaticMembersLowering,
name = "StaticMembersLowering", // name = "StaticMembersLowering",
description = "Move static member declarations to top-level" // description = "Move static member declarations to top-level"
) //)
private val builtInsLoweringPhase = makeWasmModulePhase( private val builtInsLoweringPhase = makeWasmModulePhase(
::BuiltInsLowering, ::BuiltInsLowering,
@@ -434,7 +434,7 @@ val wasmPhases = NamedCompilerPhase(
objectDeclarationLoweringPhase then objectDeclarationLoweringPhase then
objectUsageLoweringPhase then objectUsageLoweringPhase then
staticMembersLoweringPhase then // staticMembersLoweringPhase then
validateIrAfterLowering validateIrAfterLowering
) )
@@ -30,4 +30,8 @@ declare namespace JS_TESTS {
static delegateToPrimary(y: string): DerivedClassWithSecondaryCtor; static delegateToPrimary(y: string): DerivedClassWithSecondaryCtor;
static delegateToCreateFromInts(y: number, z: number): DerivedClassWithSecondaryCtor; static delegateToCreateFromInts(y: number, z: number): DerivedClassWithSecondaryCtor;
} }
class KotlinGreeter {
constructor(greeting: string);
readonly greeting: string;
}
} }
@@ -8,6 +8,8 @@
// TODO fix statics export in DCE-driven mode // TODO fix statics export in DCE-driven mode
// SKIP_DCE_DRIVEN // SKIP_DCE_DRIVEN
// FILE: f1.kt
@file:JsExport @file:JsExport
class ClassWithDefaultCtor { class ClassWithDefaultCtor {
@@ -54,4 +56,9 @@ class DerivedClassWithSecondaryCtor : OpenClassWithMixedConstructors {
@JsName("delegateToCreateFromInts") @JsName("delegateToCreateFromInts")
constructor(y: Int, z: Int) : super(y, z) constructor(y: Int, z: Int) : super(y, z)
} }
// FILE: f2.kt
@JsExport
class KotlinGreeter(val greeting: String = "helau")
@@ -1,5 +1,5 @@
"use strict"; "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; 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, KotlinGreeter = JS_TESTS.KotlinGreeter;
function box() { function box() {
var o1 = new ClassWithDefaultCtor(); var o1 = new ClassWithDefaultCtor();
if (o1.x !== "ClassWithDefaultCtor::x") if (o1.x !== "ClassWithDefaultCtor::x")
@@ -31,5 +31,8 @@ function box() {
var o10 = DerivedClassWithSecondaryCtor.delegateToCreateFromInts(-10, 20); var o10 = DerivedClassWithSecondaryCtor.delegateToCreateFromInts(-10, 20);
if (o10.x !== "fromStrings:-10:20") if (o10.x !== "fromStrings:-10:20")
return "Fail: DerivedClassWithSecondaryCtor.delegateToCreateFromInts"; return "Fail: DerivedClassWithSecondaryCtor.delegateToCreateFromInts";
var kg = new KotlinGreeter("Hi");
if (kg.greeting != "Hi")
return "Fail: KotlinGreeter";
return "OK"; return "OK";
} }
@@ -4,7 +4,8 @@ const {
ClassWithSecondaryCtor, ClassWithSecondaryCtor,
ClassWithMultipleSecondaryCtors, ClassWithMultipleSecondaryCtors,
DerivedClassWithSecondaryCtor, DerivedClassWithSecondaryCtor,
OpenClassWithMixedConstructors OpenClassWithMixedConstructors,
KotlinGreeter
} = JS_TESTS; } = JS_TESTS;
function box(): string { function box(): string {
@@ -43,5 +44,9 @@ function box(): string {
const o10 = DerivedClassWithSecondaryCtor.delegateToCreateFromInts(-10, 20); const o10 = DerivedClassWithSecondaryCtor.delegateToCreateFromInts(-10, 20);
if (o10.x !== "fromStrings:-10:20") return "Fail: DerivedClassWithSecondaryCtor.delegateToCreateFromInts"; if (o10.x !== "fromStrings:-10:20") return "Fail: DerivedClassWithSecondaryCtor.delegateToCreateFromInts";
const kg = new KotlinGreeter("Hi");
if (kg.greeting != "Hi") return "Fail: KotlinGreeter";
return "OK"; return "OK";
} }