From 87038e7d8a17a342f0b3516b7938edbba17b0468 Mon Sep 17 00:00:00 2001 From: Artem Kobzar Date: Tue, 9 Aug 2022 16:48:59 +0000 Subject: [PATCH] [K/JS] feat: add logic under the flag for strict implicit export generating inside d.ts files. --- .../common/arguments/K2JSCompilerArguments.kt | 7 + .../jetbrains/kotlin/cli/js/K2JsIrCompiler.kt | 3 + .../kotlin/ir/backend/js/JsIntrinsics.kt | 2 + .../kotlin/ir/backend/js/JsLoweringPhases.kt | 7 + .../ir/backend/js/export/ExportModel.kt | 1 - .../backend/js/export/ExportModelGenerator.kt | 91 ++++++++-- .../js/export/TransitiveExportCollector.kt | 21 ++- ...itlyExportedDeclarationsMarkingLowering.kt | 93 ++++++++++ .../ir/backend/js/utils/AnnotationUtils.kt | 8 + compiler/testData/cli/js/jsExtraHelp.out | 1 + .../JsEnvironmentConfigurationDirectives.kt | 5 + .../services/CompilerConfigurationProvider.kt | 5 + .../kotlin/js/config/JSConfigurationKeys.java | 3 + .../ir/IrJsTypeScriptExportTestGenerated.java | 16 ++ .../implicit-export/implicit-export.d.ts | 1 + .../implicit-export/implicit-export.kt | 9 +- .../typescript-export/methods/methods.d.ts | 2 +- .../strictImplicitExport/declarations.d.ts | 147 +++++++++++++++ .../strictImplicitExport/declarations.kt | 169 ++++++++++++++++++ .../declarations__main.js | 28 +++ .../declarations__main.ts | 33 ++++ .../strictImplicitExport/tsconfig.json | 4 + .../stdlib/js-ir/runtime/kotlinJsHacks.kt | 10 +- 23 files changed, 641 insertions(+), 25 deletions(-) create mode 100644 compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ImplicitlyExportedDeclarationsMarkingLowering.kt create mode 100644 js/js.translator/testData/typescript-export/strictImplicitExport/declarations.d.ts create mode 100644 js/js.translator/testData/typescript-export/strictImplicitExport/declarations.kt create mode 100644 js/js.translator/testData/typescript-export/strictImplicitExport/declarations__main.js create mode 100644 js/js.translator/testData/typescript-export/strictImplicitExport/declarations__main.ts create mode 100644 js/js.translator/testData/typescript-export/strictImplicitExport/tsconfig.json diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt index bae81f0d29c..7f57f75920a 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JSCompilerArguments.kt @@ -207,6 +207,13 @@ class K2JSCompilerArguments : CommonCompilerArguments() { ) var generateDts: Boolean by FreezableVar(false) + + @Argument( + value = "-Xstrict-implicit-export-types", + description = "Generate strict types for implicitly exported entities inside d.ts files. Available in IR backend only." + ) + var strictImplicitExportType: Boolean by FreezableVar(false) + @GradleOption(DefaultValues.BooleanTrueDefault::class) @Argument(value = "-Xtyped-arrays", description = "Translate primitive arrays to JS typed arrays") var typedArrays: Boolean by FreezableVar(true) diff --git a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt index 9784526ff86..645c3058017 100644 --- a/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt +++ b/compiler/cli/cli-js/src/org/jetbrains/kotlin/cli/js/K2JsIrCompiler.kt @@ -503,6 +503,9 @@ class K2JsIrCompiler : CLICompiler() { configuration.put(JSConfigurationKeys.FRIEND_PATHS_DISABLED, arguments.friendModulesDisabled) + configuration.put(JSConfigurationKeys.GENERATE_STRICT_IMPLICIT_EXPORT, arguments.strictImplicitExportType) + + val friendModules = arguments.friendModules if (!arguments.friendModulesDisabled && friendModules != null) { val friendPaths = friendModules diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt index 6fce1af8bce..494127b5c8f 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt @@ -311,6 +311,8 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC val doNotIntrinsifyAnnotationSymbol = context.symbolTable.referenceClass(context.getJsInternalClass("DoNotIntrinsify")) val jsFunAnnotationSymbol = context.symbolTable.referenceClass(context.getJsInternalClass("JsFun")) + val jsImplicitExportAnnotationSymbol = context.symbolTable.referenceClass(context.getJsInternalClass("JsImplicitExport")) + // TODO move CharSequence-related stiff to IntrinsifyCallsLowering val charSequenceClassSymbol = context.symbolTable.referenceClass(context.getClass(FqName("kotlin.CharSequence"))) val charSequenceLengthPropertyGetterSymbol by context.lazy2 { diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt index 5fa193fb5a9..1717b51a501 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsLoweringPhases.kt @@ -798,6 +798,12 @@ private val escapedIdentifiersLowering = makeBodyLoweringPhase( description = "Convert global variables with invalid names access to globalThis member expression" ) +private val implicitlyExportedDeclarationsMarkingLowering = makeDeclarationTransformerPhase( + ::ImplicitlyExportedDeclarationsMarkingLowering, + name = "ImplicitlyExportedDeclarationsMarkingLowering", + description = "Add @JsImplicitExport annotation to declarations which are not exported but are used inside other exported declarations as a type" +) + private val cleanupLoweringPhase = makeBodyLoweringPhase( { CleanupLowering() }, name = "CleanupLowering", @@ -920,6 +926,7 @@ val loweringList = listOf( captureStackTraceInThrowablesPhase, callsLoweringPhase, escapedIdentifiersLowering, + implicitlyExportedDeclarationsMarkingLowering, cleanupLoweringPhase, // Currently broken due to static members lowering making single-open-class // files non-recognizable as single-class files diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModel.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModel.kt index b0515b65302..1934ff46680 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModel.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModel.kt @@ -61,7 +61,6 @@ class ExportedProperty( val irSetter: IrFunction? = null, ) : ExportedDeclaration() - // TODO: Cover all cases with frontend and disable error declarations class ErrorDeclaration(val message: String) : ExportedDeclaration() diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt index e87cabca115..b1c0f38fd40 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/ExportModelGenerator.kt @@ -22,6 +22,8 @@ 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.js.config.JSConfigurationKeys +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.serialization.js.ModuleKind import org.jetbrains.kotlin.utils.addIfNotNull import org.jetbrains.kotlin.utils.addToStdlib.runIf @@ -53,7 +55,7 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac private fun exportDeclaration(declaration: IrDeclaration): ExportedDeclaration? { val candidate = getExportCandidate(declaration) ?: return null - if (!shouldDeclarationBeExported(candidate, context)) return null + if (!shouldDeclarationBeExportedImplicitlyOrExplicitly(candidate, context)) return null return when (candidate) { is IrSimpleFunction -> exportFunction(candidate) @@ -74,9 +76,10 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac } } + private fun exportFunction(function: IrSimpleFunction): ExportedDeclaration? { return when (val exportability = functionExportability(function)) { - is Exportability.NotNeeded -> null + is Exportability.NotNeeded, is Exportability.Implicit -> null is Exportability.Prohibited -> ErrorDeclaration(exportability.reason) is Exportability.Allowed -> { val parent = function.parent @@ -201,18 +204,44 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac } } + if (klass.isJsImplicitExport()) { + return Exportability.Implicit + } + if (klass.isSingleFieldValueClass) return Exportability.Prohibited("Inline class ${klass.fqNameWhenAvailable}") return Exportability.Allowed } + private fun exportDeclarationImplicitly(klass: IrClass, superTypes: Iterable): ExportedDeclaration { + val typeParameters = klass.typeParameters.map(::exportTypeParameter) + val superInterfaces = superTypes + .filter { (it.classifierOrFail.owner as? IrDeclaration)?.isExportedImplicitlyOrExplicitly(context) ?: false } + .map { exportType(it) } + .filter { it !is ExportedType.ErrorType } + + val name = klass.getExportedIdentifier() + val (members, nestedClasses) = exportClassDeclarations(klass, superTypes) + return ExportedRegularClass( + name = name, + isInterface = true, + isAbstract = false, + superClasses = emptyList(), + superInterfaces = superInterfaces, + typeParameters = typeParameters, + members = members, + nestedClasses = nestedClasses, + ir = klass + ) + } + private fun exportOrdinaryClass(klass: IrClass, superTypes: Iterable): ExportedDeclaration? { when (val exportability = classExportability(klass)) { is Exportability.Prohibited -> error(exportability.reason) - is Exportability.NotNeeded -> return null - Exportability.Allowed -> { - } + Exportability.NotNeeded -> return null + Exportability.Implicit -> return exportDeclarationImplicitly(klass, superTypes) + Exportability.Allowed -> {} } val (members, nestedClasses) = exportClassDeclarations(klass, superTypes) @@ -228,9 +257,9 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac private fun exportEnumClass(klass: IrClass, superTypes: Iterable): ExportedDeclaration? { when (val exportability = classExportability(klass)) { is Exportability.Prohibited -> error(exportability.reason) - is Exportability.NotNeeded -> return null - Exportability.Allowed -> { - } + Exportability.NotNeeded -> return null + Exportability.Implicit -> return exportDeclarationImplicitly(klass, superTypes) + Exportability.Allowed -> {} } val enumEntries = klass @@ -267,10 +296,12 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac ): ExportedClassDeclarationsInfo { val members = mutableListOf() val nestedClasses = mutableListOf() + val isImplicitlyExportedClass = klass.isJsImplicitExport() for (declaration in klass.declarations) { val candidate = getExportCandidate(declaration) ?: continue - if (!shouldDeclarationBeExported(candidate, context)) continue + if (isImplicitlyExportedClass && candidate !is IrClass) continue + if (!shouldDeclarationBeExportedImplicitlyOrExplicitly(candidate, context)) continue val processingResult = specialProcessing(candidate) if (processingResult != null) { @@ -324,13 +355,13 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac } private fun IrClass.shouldNotBeImplemented(): Boolean { - return isInterface && !isExternal + return isInterface && !isExternal || isJsImplicitExport() } private fun IrClass.shouldContainImplementationOfMagicProperty(superTypes: Iterable): Boolean { return !isExternal && superTypes.any { val superClass = it.classOrNull?.owner ?: return@any false - superClass.isInterface && superClass.isExported(context) + superClass.isInterface && superClass.isExported(context) || superClass.isJsImplicitExport() } } @@ -339,11 +370,13 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac } private fun MutableList.addMagicPropertyForInterfaceImplementation(klass: IrClass, superTypes: Iterable) { - val superTypesToInheritanceMagicProperty = superTypes.filter { it.shouldAddMagicPropertyOfSuper() } + val allSuperTypesWithMagicProperty = superTypes.filter { it.shouldAddMagicPropertyOfSuper() } - if (superTypesToInheritanceMagicProperty.isEmpty()) return + if (allSuperTypesWithMagicProperty.isEmpty()) { + return + } - val intersectionOfTypes = superTypesToInheritanceMagicProperty + val intersectionOfTypes = allSuperTypesWithMagicProperty .map { ExportedType.PropertyType(exportType(it), ExportedType.LiteralType.StringLiteralType(magicPropertyName)) } .reduce(ExportedType::IntersectionType) .let { if (klass.shouldNotBeImplemented()) ExportedType.IntersectionType(klass.generateTagType(), it) else it } @@ -356,8 +389,11 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac } private fun IrClass.isOwnMagicPropertyAdded(): Boolean { + if (isJsImplicitExport()) return true if (!isExported(context)) return false - return isInterface && !isExternal || superTypes.any { it.classOrNull?.owner?.isOwnMagicPropertyAdded() ?: false } + return isInterface && !isExternal || superTypes.any { + it.classOrNull?.owner?.isOwnMagicPropertyAdded() == true + } } private fun IrClass.generateTagType(): ExportedType { @@ -388,7 +424,7 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac .filter { it !is ExportedType.ErrorType } val superInterfaces = superTypes - .filter { it.classifierOrFail.isInterface } + .filter { it.shouldPresentInsideImplementsClause() } .map { exportType(it, false) } .filter { it !is ExportedType.ErrorType } @@ -419,9 +455,14 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac } } - private fun IrSimpleType.collectSuperTransitiveHierarchy() = + private fun IrSimpleType.collectSuperTransitiveHierarchy(): Set = transitiveExportCollector.collectSuperTypesTransitiveHierarchyFor(this) + private fun IrType.shouldPresentInsideImplementsClause(): Boolean { + val classifier = classifierOrFail + return classifier.isInterface || (classifier.owner as? IrDeclaration)?.isJsImplicitExport() == true + } + private fun exportAsEnumMember( candidate: IrDeclarationWithName, enumEntriesToOrdinal: Map @@ -460,7 +501,9 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac } private fun IrType.canBeUsedAsSuperTypeOfExportedClasses(): Boolean = - !this.isAny() && classifierOrNull != context.irBuiltIns.enumClass + !isAny() && + classifierOrNull != context.irBuiltIns.enumClass && + (classifierOrNull?.owner as? IrDeclaration)?.isJsImplicitExport() != true private fun exportTypeArgument(type: IrTypeArgument): ExportedType { if (type is IrTypeProjection) @@ -541,7 +584,7 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac classifier is IrClassSymbol -> { val klass = classifier.owner - val isExported = klass.isExported(context) + val isExported = klass.isExportedImplicitlyOrExplicitly(context) val isImplicitlyExported = !isExported && !klass.isExternal val isNonExportedExternal = klass.isExternal && !isExported val name = klass.getFqNameWithJsNameWhenAvailable(!isNonExportedExternal && generateNamespacesForPackages).asString() @@ -630,6 +673,7 @@ class ExportModelGenerator(val context: JsIrBackendContext, val generateNamespac sealed class Exportability { object Allowed : Exportability() object NotNeeded : Exportability() + object Implicit : Exportability() class Prohibited(val reason: String) : Exportability() } @@ -670,6 +714,10 @@ private fun getExportCandidate(declaration: IrDeclaration): IrDeclarationWithNam return declaration } +private fun shouldDeclarationBeExportedImplicitlyOrExplicitly(declaration: IrDeclarationWithName, context: JsIrBackendContext): Boolean { + return declaration.isJsImplicitExport() || shouldDeclarationBeExported(declaration, context) +} + private fun shouldDeclarationBeExported(declaration: IrDeclarationWithName, context: JsIrBackendContext): Boolean { if (context.additionalExportedDeclarationNames.contains(declaration.fqNameWhenAvailable)) return true @@ -727,6 +775,11 @@ fun IrDeclaration.isExported(context: JsIrBackendContext): Boolean { return shouldDeclarationBeExported(candidate, context) } +fun IrDeclaration.isExportedImplicitlyOrExplicitly(context: JsIrBackendContext): Boolean { + val candidate = getExportCandidate(this) ?: return false + return shouldDeclarationBeExportedImplicitlyOrExplicitly(candidate, context) +} + private fun DescriptorVisibility.toExportedVisibility() = when (this) { DescriptorVisibilities.PROTECTED -> ExportedVisibility.PROTECTED diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/TransitiveExportCollector.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/TransitiveExportCollector.kt index 636a974c2e2..a2fa1b22e9d 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/TransitiveExportCollector.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/export/TransitiveExportCollector.kt @@ -12,6 +12,10 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.symbols.* import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl +import org.jetbrains.kotlin.ir.backend.js.utils.isJsImplicitExport +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.superTypes private typealias SubstitutionMap = Map @@ -34,13 +38,26 @@ class TransitiveExportCollector(val context: JsIrBackendContext) { .toSet() } + private fun IrSimpleType.findNearestExportedClass(typeSubstitutionMap: SubstitutionMap): IrSimpleType? { + val classifier = classifier as? IrClassSymbol ?: return null + if (classifier.owner.isExported(context)) return substitute(typeSubstitutionMap) as IrSimpleType + + return classifier.superTypes() + .firstNotNullOfOrNull { (it as? IrSimpleType)?.findNearestExportedClass(typeSubstitutionMap) } + } + private fun IrSimpleType.collectTransitiveHierarchy(typeSubstitutionMap: SubstitutionMap): Set { val owner = classifier.owner as? IrClass ?: return emptySet() val substitutionMap = calculateTypeSubstitutionMap(typeSubstitutionMap) return when { isBuiltInClass(owner) || isStdLibClass(owner) -> emptySet() - owner.isExported(context) -> setOf(substitute(typeSubstitutionMap)) + owner.isExported(context) -> setOf(substitute(substitutionMap)) + owner.isJsImplicitExport() -> setOfNotNull( + substitute(typeSubstitutionMap), + takeIf { !owner.isInterface }?.findNearestExportedClass(substitutionMap) + ) + else -> collectSuperTypesTransitiveHierarchy(substitutionMap) } } @@ -62,4 +79,4 @@ class TransitiveExportCollector(val context: JsIrBackendContext) { } private data class ClassWithAppliedArguments(val classSymbol: IrClassSymbol, val appliedArguments: List) -} \ No newline at end of file +} diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ImplicitlyExportedDeclarationsMarkingLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ImplicitlyExportedDeclarationsMarkingLowering.kt new file mode 100644 index 00000000000..994285ec386 --- /dev/null +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ImplicitlyExportedDeclarationsMarkingLowering.kt @@ -0,0 +1,93 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.ir.backend.js.lower + +import org.jetbrains.kotlin.backend.common.DeclarationTransformer +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder +import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext +import org.jetbrains.kotlin.ir.backend.js.export.isExported +import org.jetbrains.kotlin.ir.backend.js.utils.isJsImplicitExport +import org.jetbrains.kotlin.ir.builders.irCall +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.symbols.IrClassSymbol +import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.util.constructors +import org.jetbrains.kotlin.ir.util.isPrimitiveArray +import org.jetbrains.kotlin.ir.util.parentClassOrNull +import org.jetbrains.kotlin.js.config.JSConfigurationKeys + +class ImplicitlyExportedDeclarationsMarkingLowering(private val context: JsIrBackendContext) : DeclarationTransformer { + private val strictImplicitExport = context.configuration.getBoolean(JSConfigurationKeys.GENERATE_STRICT_IMPLICIT_EXPORT) + + override fun transformFlat(declaration: IrDeclaration): List? { + if (!strictImplicitExport || !declaration.isExported(context)) return null + + val implicitlyExportedDeclarations = when (declaration) { + is IrFunction -> declaration.collectImplicitlyExportedDeclarations() + is IrClass -> declaration.collectImplicitlyExportedDeclarations() + is IrProperty -> declaration.collectImplicitlyExportedDeclarations() + else -> emptySet() + } + + implicitlyExportedDeclarations.forEach { it.markWithJsImplicitExport() } + + return null + } + + private fun IrClass.collectImplicitlyExportedDeclarations(): Set { + return typeParameters + .flatMap { it.superTypes }.toSet() + .flatMap { it.collectImplicitlyExportedDeclarations() }.toSet() + } + + + private fun IrFunction.collectImplicitlyExportedDeclarations(): Set { + val types = buildSet { + add(returnType) + addAll(valueParameters.map { it.type }) + addAll(typeParameters.flatMap { it.superTypes }) + } + + return types.flatMap { it.collectImplicitlyExportedDeclarations() }.toSet() + } + + private fun IrProperty.collectImplicitlyExportedDeclarations(): Set { + val getterImplicitlyExportedDeclarations = getter?.collectImplicitlyExportedDeclarations() ?: emptySet() + val setterImplicitlyExportedDeclarations = setter?.collectImplicitlyExportedDeclarations() ?: emptySet() + val fieldImplicitlyExportedDeclarations = backingField?.type?.collectImplicitlyExportedDeclarations() ?: emptySet() + + return getterImplicitlyExportedDeclarations + setterImplicitlyExportedDeclarations + fieldImplicitlyExportedDeclarations + } + + private fun IrType.collectImplicitlyExportedDeclarations(): Set { + if (this is IrDynamicType || this !is IrSimpleType) + return emptySet() + + val nonNullType = makeNotNull() as IrSimpleType + val classifier = nonNullType.classifier + + return when { + nonNullType.isPrimitiveType() || nonNullType.isPrimitiveArray() || nonNullType.isAny() || nonNullType.isUnit() -> emptySet() + classifier is IrTypeParameterSymbol -> classifier.owner.superTypes.flatMap { it.collectImplicitlyExportedDeclarations() } + .toSet() + + classifier is IrClassSymbol -> setOfNotNull(classifier.owner.takeIf { it.shouldBeMarkedWithImplicitExport() }) + else -> emptySet() + } + } + + private fun IrDeclaration.shouldBeMarkedWithImplicitExport(): Boolean { + return this is IrClass && !isExternal && !isExported(context) && !isJsImplicitExport() + } + + private fun IrDeclaration.markWithJsImplicitExport() { + val jsImplicitExportCtor = context.intrinsics.jsImplicitExportAnnotationSymbol.constructors.single() + annotations += context.createIrBuilder(symbol).irCall(jsImplicitExportCtor) + + parentClassOrNull?.takeIf { it.shouldBeMarkedWithImplicitExport() }?.markWithJsImplicitExport() + } +} \ No newline at end of file diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/AnnotationUtils.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/AnnotationUtils.kt index 0a3ee739b55..fbf82150039 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/AnnotationUtils.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/utils/AnnotationUtils.kt @@ -23,6 +23,7 @@ object JsAnnotations { val jsNameFqn = FqName("kotlin.js.JsName") val jsQualifierFqn = FqName("kotlin.js.JsQualifier") val jsExportFqn = FqName("kotlin.js.JsExport") + val jsImplicitExportFqn = FqName("kotlin.js.JsImplicitExport") val jsNativeGetter = FqName("kotlin.js.nativeGetter") val jsNativeSetter = FqName("kotlin.js.nativeSetter") val jsNativeInvoke = FqName("kotlin.js.nativeInvoke") @@ -34,6 +35,10 @@ object JsAnnotations { fun IrConstructorCall.getSingleConstStringArgument() = (getValueArgument(0) as IrConst).value +@Suppress("UNCHECKED_CAST") +fun IrConstructorCall.getSingleConstBooleanArgument() = + (getValueArgument(0) as IrConst).value + fun IrAnnotationContainer.getJsModule(): String? = getAnnotation(JsAnnotations.jsModuleFqn)?.getSingleConstStringArgument() @@ -58,6 +63,9 @@ fun IrAnnotationContainer.getJsFunAnnotation(): String? = fun IrAnnotationContainer.isJsExport(): Boolean = hasAnnotation(JsAnnotations.jsExportFqn) +fun IrAnnotationContainer.isJsImplicitExport(): Boolean = + hasAnnotation(JsAnnotations.jsImplicitExportFqn) + fun IrAnnotationContainer.isJsNativeGetter(): Boolean = hasAnnotation(JsAnnotations.jsNativeGetter) fun IrAnnotationContainer.isJsNativeSetter(): Boolean = hasAnnotation(JsAnnotations.jsNativeSetter) diff --git a/compiler/testData/cli/js/jsExtraHelp.out b/compiler/testData/cli/js/jsExtraHelp.out index 2c605de58e6..b5a7e640582 100644 --- a/compiler/testData/cli/js/jsExtraHelp.out +++ b/compiler/testData/cli/js/jsExtraHelp.out @@ -38,6 +38,7 @@ where advanced options include: -Xmetadata-only Generate *.meta.js and *.kjsm files only -Xpartial-linkage Allow unlinked symbols -Xrepositories= Paths to additional places where libraries could be found + -Xstrict-implicit-export-types Generate strict types for implicitly exported entities inside d.ts files. Available in IR backend only. -Xtyped-arrays Translate primitive arrays to JS typed arrays -Xwasm Use experimental WebAssembly compiler backend -Xwasm-debug-info Add debug info to WebAssembly compiled module diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/JsEnvironmentConfigurationDirectives.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/JsEnvironmentConfigurationDirectives.kt index 632aa721152..1fa1529fb49 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/JsEnvironmentConfigurationDirectives.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/directives/JsEnvironmentConfigurationDirectives.kt @@ -117,6 +117,11 @@ object JsEnvironmentConfigurationDirectives : SimpleDirectivesContainer() { applicability = DirectiveApplicability.Global ) + val GENERATE_STRICT_IMPLICIT_EXPORT by directive( + description = "enable strict implicitly exported entities types inside d.ts files", + applicability = DirectiveApplicability.Global + ) + val SAFE_EXTERNAL_BOOLEAN by directive( description = "", applicability = DirectiveApplicability.Global diff --git a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/CompilerConfigurationProvider.kt b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/CompilerConfigurationProvider.kt index fd3222ce902..58f5fe0f066 100644 --- a/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/CompilerConfigurationProvider.kt +++ b/compiler/tests-common-new/tests/org/jetbrains/kotlin/test/services/CompilerConfigurationProvider.kt @@ -125,6 +125,11 @@ fun TargetPlatform.platformToEnvironmentConfigFiles() = when { fun createCompilerConfiguration(module: TestModule, configurators: List): CompilerConfiguration { val configuration = CompilerConfiguration() configuration[CommonConfigurationKeys.MODULE_NAME] = module.name + + if (JsEnvironmentConfigurationDirectives.GENERATE_STRICT_IMPLICIT_EXPORT in module.directives) { + configuration.put(JSConfigurationKeys.GENERATE_STRICT_IMPLICIT_EXPORT, true) + } + if (module.frontendKind == FrontendKinds.FIR) { configuration[CommonConfigurationKeys.USE_FIR] = true } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java b/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java index 63f5863b9e5..8364d399ff6 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/config/JSConfigurationKeys.java @@ -103,6 +103,9 @@ public class JSConfigurationKeys { public static final CompilerConfigurationKey GENERATE_INLINE_ANONYMOUS_FUNCTIONS = CompilerConfigurationKey.create("translate lambdas into in-line anonymous functions"); + public static final CompilerConfigurationKey GENERATE_STRICT_IMPLICIT_EXPORT = + CompilerConfigurationKey.create("enable strict implicitly exported entities types inside d.ts files"); + public static final CompilerConfigurationKey WASM_ENABLE_ARRAY_RANGE_CHECKS = CompilerConfigurationKey.create("enable array range checks"); diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsTypeScriptExportTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsTypeScriptExportTestGenerated.java index 48403f6cc2d..4e1a6382538 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsTypeScriptExportTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrJsTypeScriptExportTestGenerated.java @@ -373,6 +373,22 @@ public class IrJsTypeScriptExportTestGenerated extends AbstractIrJsTypeScriptExp } } + @Nested + @TestMetadata("js/js.translator/testData/typescript-export/strictImplicitExport") + @TestDataPath("$PROJECT_ROOT") + public class StrictImplicitExport { + @Test + public void testAllFilesPresentInStrictImplicitExport() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("js/js.translator/testData/typescript-export/strictImplicitExport"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.JS_IR, true); + } + + @Test + @TestMetadata("declarations.kt") + public void testDeclarations() throws Exception { + runTest("js/js.translator/testData/typescript-export/strictImplicitExport/declarations.kt"); + } + } + @Nested @TestMetadata("js/js.translator/testData/typescript-export/visibility") @TestDataPath("$PROJECT_ROOT") diff --git a/js/js.translator/testData/typescript-export/implicit-export/implicit-export.d.ts b/js/js.translator/testData/typescript-export/implicit-export/implicit-export.d.ts index b76bd976d26..3bcba7f6510 100644 --- a/js/js.translator/testData/typescript-export/implicit-export/implicit-export.d.ts +++ b/js/js.translator/testData/typescript-export/implicit-export/implicit-export.d.ts @@ -41,5 +41,6 @@ declare namespace JS_TESTS { function bar(): Error; const console: Console; const error: WebAssembly.CompileError; + function functionWithTypeAliasInside(x: any/* foo.NonExportedGenericInterface */): any/* foo.NonExportedGenericInterface */; } } diff --git a/js/js.translator/testData/typescript-export/implicit-export/implicit-export.kt b/js/js.translator/testData/typescript-export/implicit-export/implicit-export.kt index 00306ac23fe..4217f11e140 100644 --- a/js/js.translator/testData/typescript-export/implicit-export/implicit-export.kt +++ b/js/js.translator/testData/typescript-export/implicit-export/implicit-export.kt @@ -85,4 +85,11 @@ val console: Console @JsExport val error: CompileError - get() = js("{}") \ No newline at end of file + get() = js("{}") + +typealias NotExportedTypeAlias = NonExportedGenericInterface + +@JsExport +fun functionWithTypeAliasInside(x: NotExportedTypeAlias): NotExportedTypeAlias { + return x +} \ No newline at end of file diff --git a/js/js.translator/testData/typescript-export/methods/methods.d.ts b/js/js.translator/testData/typescript-export/methods/methods.d.ts index d2264326123..8dcf0e3ba09 100644 --- a/js/js.translator/testData/typescript-export/methods/methods.d.ts +++ b/js/js.translator/testData/typescript-export/methods/methods.d.ts @@ -18,4 +18,4 @@ declare namespace JS_TESTS { inlineFun(x: number, callback: (p0: number) => void): void; } } -} \ No newline at end of file +} diff --git a/js/js.translator/testData/typescript-export/strictImplicitExport/declarations.d.ts b/js/js.translator/testData/typescript-export/strictImplicitExport/declarations.d.ts new file mode 100644 index 00000000000..e9be5f3539a --- /dev/null +++ b/js/js.translator/testData/typescript-export/strictImplicitExport/declarations.d.ts @@ -0,0 +1,147 @@ +declare namespace JS_TESTS { + type Nullable = T | null | undefined + namespace foo { + const forth: foo.Forth; + interface ExportedInterface { + readonly __doNotUseOrImplementIt: { + readonly "foo.ExportedInterface": unique symbol; + }; + } + function producer(value: number): foo.NonExportedType; + function consumer(value: foo.NonExportedType): number; + function childProducer(value: number): foo.NotExportedChildClass; + function childConsumer(value: foo.NotExportedChildClass): number; + function genericChildProducer>(value: T): foo.NotExportedChildGenericClass; + function genericChildConsumer>(value: foo.NotExportedChildGenericClass): T; + class A implements foo.NonExportedParent.NonExportedSecond.NonExportedUsedChild { + constructor(value: foo.NonExportedType); + get value(): foo.NonExportedType; + set value(value: foo.NonExportedType); + increment(t: T): foo.NonExportedType; + getNonExportedUserChild(): foo.NonExportedParent.NonExportedSecond.NonExportedUsedChild; + readonly __doNotUseOrImplementIt: foo.NonExportedParent.NonExportedSecond.NonExportedUsedChild["__doNotUseOrImplementIt"]; + } + class B implements foo.NonExportedType { + constructor(v: number); + readonly __doNotUseOrImplementIt: foo.NonExportedType["__doNotUseOrImplementIt"]; + } + class C implements foo.NonExportedInterface { + constructor(); + readonly __doNotUseOrImplementIt: foo.NonExportedInterface["__doNotUseOrImplementIt"]; + } + class D implements foo.NonExportedInterface, foo.ExportedInterface { + constructor(); + readonly __doNotUseOrImplementIt: foo.NonExportedInterface["__doNotUseOrImplementIt"] & foo.ExportedInterface["__doNotUseOrImplementIt"]; + } + class E implements foo.NonExportedType, foo.ExportedInterface { + constructor(); + readonly __doNotUseOrImplementIt: foo.NonExportedType["__doNotUseOrImplementIt"] & foo.ExportedInterface["__doNotUseOrImplementIt"]; + } + class F extends foo.A implements foo.NonExportedInterface { + constructor(); + readonly __doNotUseOrImplementIt: foo.A["__doNotUseOrImplementIt"] & foo.NonExportedInterface["__doNotUseOrImplementIt"]; + } + class G implements foo.NonExportedGenericInterface { + constructor(); + readonly __doNotUseOrImplementIt: foo.NonExportedGenericInterface["__doNotUseOrImplementIt"]; + } + class H implements foo.NonExportedGenericType { + constructor(); + readonly __doNotUseOrImplementIt: foo.NonExportedGenericType["__doNotUseOrImplementIt"]; + } + class I implements foo.NotExportedChildClass { + constructor(); + readonly __doNotUseOrImplementIt: foo.NotExportedChildClass["__doNotUseOrImplementIt"]; + } + class J implements foo.NotExportedChildGenericClass { + constructor(); + readonly __doNotUseOrImplementIt: foo.NotExportedChildGenericClass["__doNotUseOrImplementIt"]; + } + function baz(a: number): Promise; + function bar(): Error; + function pep>(x: T): void; + const console: Console; + const error: WebAssembly.CompileError; + interface IA { + readonly __doNotUseOrImplementIt: { + readonly "foo.IA": unique symbol; + }; + } + class Third extends /* foo.Second */ foo.First { + constructor(); + } + class Sixth extends /* foo.Fifth */ foo.Third implements foo.Forth, foo.IC { + constructor(); + readonly __doNotUseOrImplementIt: foo.Forth["__doNotUseOrImplementIt"] & foo.IC["__doNotUseOrImplementIt"]; + } + class First { + constructor(); + } + function acceptForthLike(forth: T): void; + function acceptMoreGenericForthLike(forth: T): void; + interface NonExportedParent { + readonly __doNotUseOrImplementIt: { + readonly "foo.NonExportedParent": unique symbol; + }; + } + namespace NonExportedParent { + interface NonExportedSecond { + readonly __doNotUseOrImplementIt: { + readonly "foo.NonExportedParent.NonExportedSecond": unique symbol; + }; + } + namespace NonExportedSecond { + interface NonExportedUsedChild { + readonly __doNotUseOrImplementIt: { + readonly "foo.NonExportedParent.NonExportedSecond.NonExportedUsedChild": unique symbol; + }; + } + } + } + interface NonExportedInterface { + readonly __doNotUseOrImplementIt: { + readonly "foo.NonExportedInterface": unique symbol; + }; + } + interface NonExportedGenericInterface { + readonly __doNotUseOrImplementIt: { + readonly "foo.NonExportedGenericInterface": unique symbol; + }; + } + interface NonExportedType { + readonly __doNotUseOrImplementIt: { + readonly "foo.NonExportedType": unique symbol; + }; + } + interface NonExportedGenericType { + readonly __doNotUseOrImplementIt: { + readonly "foo.NonExportedGenericType": unique symbol; + }; + } + interface NotExportedChildClass extends foo.NonExportedInterface, foo.NonExportedType { + readonly __doNotUseOrImplementIt: { + readonly "foo.NotExportedChildClass": unique symbol; + } & foo.NonExportedInterface["__doNotUseOrImplementIt"] & foo.NonExportedType["__doNotUseOrImplementIt"]; + } + interface NotExportedChildGenericClass extends foo.NonExportedInterface, foo.NonExportedGenericInterface, foo.NonExportedGenericType { + readonly __doNotUseOrImplementIt: { + readonly "foo.NotExportedChildGenericClass": unique symbol; + } & foo.NonExportedInterface["__doNotUseOrImplementIt"] & foo.NonExportedGenericInterface["__doNotUseOrImplementIt"] & foo.NonExportedGenericType["__doNotUseOrImplementIt"]; + } + interface IB extends foo.IA { + readonly __doNotUseOrImplementIt: { + readonly "foo.IB": unique symbol; + } & foo.IA["__doNotUseOrImplementIt"]; + } + interface IC extends foo.IB { + readonly __doNotUseOrImplementIt: { + readonly "foo.IC": unique symbol; + } & foo.IB["__doNotUseOrImplementIt"]; + } + interface Forth extends foo.Third, foo.IB, foo.IC { + readonly __doNotUseOrImplementIt: { + readonly "foo.Forth": unique symbol; + } & foo.IB["__doNotUseOrImplementIt"] & foo.IC["__doNotUseOrImplementIt"]; + } + } +} diff --git a/js/js.translator/testData/typescript-export/strictImplicitExport/declarations.kt b/js/js.translator/testData/typescript-export/strictImplicitExport/declarations.kt new file mode 100644 index 00000000000..fdfab4ccd53 --- /dev/null +++ b/js/js.translator/testData/typescript-export/strictImplicitExport/declarations.kt @@ -0,0 +1,169 @@ +// CHECK_TYPESCRIPT_DECLARATIONS +// RUN_PLAIN_BOX_FUNCTION +// SKIP_MINIFICATION +// SKIP_NODE_JS +// KJS_WITH_FULL_RUNTIME +// INFER_MAIN_MODULE +// GENERATE_STRICT_IMPLICIT_EXPORT +// MODULE: JS_TESTS +// FILE: qualified.kt +@file:JsQualifier("WebAssembly") +package qualified + +external interface CompileError + +// FILE: notQualified.kt +package notQualified + +external interface Console + +// FILE: declarations.kt + +package foo + +import notQualified.Console +import qualified.CompileError + +interface NeverUsedInsideExportedDeclarationsType + +open class NonExportedParent { + open class NonExportedSecond { + open class NonExportedUsedChild + open class NonExportedUnusedChild + } +} + +interface NonExportedInterface +interface NonExportedGenericInterface +open class NonExportedType(val value: Int) +open class NonExportedGenericType(val value: T) + +open class NotExportedChildClass : NonExportedInterface, NeverUsedInsideExportedDeclarationsType, NonExportedType(322) +open class NotExportedChildGenericClass(value: T) : NonExportedInterface, NeverUsedInsideExportedDeclarationsType, NonExportedGenericInterface, NonExportedGenericType(value) + + +@JsExport +interface ExportedInterface + +@JsExport +fun producer(value: Int): NonExportedType { + return NonExportedType(value) +} + +@JsExport +fun consumer(value: NonExportedType): Int { + return value.value +} + +@JsExport +fun childProducer(value: Int): NotExportedChildClass { + return NotExportedChildClass() +} + +@JsExport +fun childConsumer(value: NotExportedChildClass): Int { + return value.value +} + +@JsExport +fun > genericChildProducer(value: T): NotExportedChildGenericClass { + return NotExportedChildGenericClass(value) +} + +@JsExport +fun > genericChildConsumer(value: NotExportedChildGenericClass): T { + return value.value +} + +@JsExport +open class A(var value: NonExportedType): NonExportedParent.NonExportedSecond.NonExportedUsedChild() { + fun increment(t: T): NonExportedType { + return NonExportedType(value = t.value + 1) + } + + fun getNonExportedUserChild(): NonExportedParent.NonExportedSecond.NonExportedUsedChild { + return this + } +} + +@JsExport +class B(v: Int) : NonExportedType(v) + +@JsExport +class C : NonExportedInterface + +@JsExport +class D : NonExportedInterface, ExportedInterface + +@JsExport +class E : NonExportedType(42), ExportedInterface + +@JsExport +class F : A(NonExportedType(42)), NonExportedInterface + +@JsExport +class G : NonExportedGenericInterface + +@JsExport +class H : NonExportedGenericType(NonExportedType(42)) + +@JsExport +class I : NotExportedChildClass() + +@JsExport +class J : NotExportedChildGenericClass(NonExportedType(322)) + +@JsExport +fun baz(a: Int): kotlin.js.Promise { + return kotlin.js.Promise { res, rej -> res(a) } +} + +@JsExport +fun bar(): Throwable { + return Throwable("Test Error") +} + +@JsExport +fun pep(x: T) where T: NonExportedInterface, + T: NonExportedGenericInterface +{} + +@JsExport +val console: Console + get() = js("console") + +@JsExport +val error: CompileError + get() = js("{}") + +// Save hierarhy + +@JsExport +interface IA + +interface IB : IA + +interface IC : IB + +@JsExport +open class Third: Second() + +open class Forth: Third(), IB, IC + +open class Fifth: Forth() + +@JsExport +class Sixth: Fifth(), IC +@JsExport +open class First + +open class Second: First() + +@JsExport +fun acceptForthLike(forth: T) {} + +@JsExport +fun acceptMoreGenericForthLike(forth: T) where T: IB, T: IC, T: Third {} + +@JsExport +val forth = Forth() diff --git a/js/js.translator/testData/typescript-export/strictImplicitExport/declarations__main.js b/js/js.translator/testData/typescript-export/strictImplicitExport/declarations__main.js new file mode 100644 index 00000000000..4573b5ceb1e --- /dev/null +++ b/js/js.translator/testData/typescript-export/strictImplicitExport/declarations__main.js @@ -0,0 +1,28 @@ +"use strict"; +var producer = JS_TESTS.foo.producer; +var consumer = JS_TESTS.foo.consumer; +var A = JS_TESTS.foo.A; +var B = JS_TESTS.foo.B; +var childProducer = JS_TESTS.foo.childProducer; +var childConsumer = JS_TESTS.foo.childConsumer; +var genericChildProducer = JS_TESTS.foo.genericChildProducer; +var genericChildConsumer = JS_TESTS.foo.genericChildConsumer; +function assert(condition) { + if (!condition) { + throw "Assertion failed"; + } +} +function box() { + var nonExportedType = producer(42); + var a = new A(nonExportedType); + var b = new B(43); + assert(consumer(nonExportedType) == 42); + a.value = producer(24); + assert(consumer(b) == 43); + assert(consumer(a.value) == 24); + assert(consumer(a.increment(nonExportedType)) == 43); + var oneMoreNonExportedType = childProducer(322); + assert(consumer(oneMoreNonExportedType) == 322); + assert(childConsumer(oneMoreNonExportedType) == 322); + return "OK"; +} diff --git a/js/js.translator/testData/typescript-export/strictImplicitExport/declarations__main.ts b/js/js.translator/testData/typescript-export/strictImplicitExport/declarations__main.ts new file mode 100644 index 00000000000..49db57757df --- /dev/null +++ b/js/js.translator/testData/typescript-export/strictImplicitExport/declarations__main.ts @@ -0,0 +1,33 @@ +import producer = JS_TESTS.foo.producer; +import consumer = JS_TESTS.foo.consumer; +import A = JS_TESTS.foo.A; +import B = JS_TESTS.foo.B; +import childProducer = JS_TESTS.foo.childProducer; +import childConsumer = JS_TESTS.foo.childConsumer; +import genericChildProducer = JS_TESTS.foo.genericChildProducer; +import genericChildConsumer = JS_TESTS.foo.genericChildConsumer; + +function assert(condition: boolean) { + if (!condition) { + throw "Assertion failed"; + } +} + +function box(): string { + const nonExportedType = producer(42) + const a = new A(nonExportedType) + const b = new B(43) + + assert(consumer(nonExportedType) == 42) + + a.value = producer(24) + assert(consumer(b) == 43) + assert(consumer(a.value) == 24) + assert(consumer(a.increment(nonExportedType)) == 43) + + const oneMoreNonExportedType = childProducer(322) + assert(consumer(oneMoreNonExportedType) == 322) + assert(childConsumer(oneMoreNonExportedType) == 322) + + return "OK"; +} \ No newline at end of file diff --git a/js/js.translator/testData/typescript-export/strictImplicitExport/tsconfig.json b/js/js.translator/testData/typescript-export/strictImplicitExport/tsconfig.json new file mode 100644 index 00000000000..17612c56c9d --- /dev/null +++ b/js/js.translator/testData/typescript-export/strictImplicitExport/tsconfig.json @@ -0,0 +1,4 @@ +{ + "include": [ "./*" ], + "extends": "../common.tsconfig.json" +} \ No newline at end of file diff --git a/libraries/stdlib/js-ir/runtime/kotlinJsHacks.kt b/libraries/stdlib/js-ir/runtime/kotlinJsHacks.kt index 959fea1b03b..de82749f926 100644 --- a/libraries/stdlib/js-ir/runtime/kotlinJsHacks.kt +++ b/libraries/stdlib/js-ir/runtime/kotlinJsHacks.kt @@ -42,4 +42,12 @@ internal fun safePropertySet(self: dynamic, setterName: String, propName: String * Code gets inserted as is without syntax verification. */ @Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER) -internal annotation class JsFun(val code: String) \ No newline at end of file +internal annotation class JsFun(val code: String) + +/** + * The annotation is needed for annotating class declarations and type alias which are used inside exported declarations, but + * doesn't contain @JsExport annotation + * This information is used for generating special tagged types inside d.ts files, for more strict usage of implicitly exported entities + */ +@Target(AnnotationTarget.CLASS) +internal annotation class JsImplicitExport()