refactorings
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/Exports.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/Exports.kt
index d5dc34a..7918bdf 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/Exports.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/Exports.kt
@@ -40,6 +40,8 @@
     val isStatic: Boolean = false
 ) : ExportedDeclaration()
 
+class ErrorDeclaration(val message: String) : ExportedDeclaration()
+
 class ExportedClass(
     val name: String,
     val isInterface: Boolean = false,
@@ -99,6 +101,8 @@
     joinToString("\n") { it.toTypeScript(ident) }
 
 fun ExportedDeclaration.toTypeScript(ident: String): String = ident + when (this) {
+    is ErrorDeclaration -> "namespace _Error_ { /* $message */ }"
+
     is ExportedFile ->
         "\n// File $name\n\n" + declarations.toTypeScript("")
 
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 88440d8..013c1579 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
@@ -378,12 +378,6 @@
     description = "Generate invocations to kotlin.test suite and test functions"
 )
 
-private val jsExportLoweringPhase = makeJsModulePhase(
-    ::ExportLowering,
-    name = "JsExportLowering",
-    description = "Process JS exports"
-)
-
 private val staticMembersLoweringPhase = makeJsModulePhase(
     ::StaticMembersLowering,
     name = "StaticMembersLowering",
@@ -406,7 +400,6 @@
     name = "IrModuleLowering",
     description = "IR module lowering",
     lower = validateIrBeforeLowering then
-            jsExportLoweringPhase then
             testGenerationPhase then
             expectDeclarationsRemovingPhase then
             provisionalFunctionExpressionPhase then
diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ExportLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ExportLowering.kt
index 1288c06..586bd22 100644
--- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ExportLowering.kt
+++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/ExportLowering.kt
@@ -5,7 +5,6 @@
 
 package org.jetbrains.kotlin.ir.backend.js.lower
 
-import org.jetbrains.kotlin.backend.common.FileLoweringPass
 import org.jetbrains.kotlin.backend.common.ir.isExpect
 import org.jetbrains.kotlin.backend.common.ir.isMethodOfAny
 import org.jetbrains.kotlin.descriptors.Modality
@@ -21,11 +20,7 @@
 import org.jetbrains.kotlin.ir.util.*
 import org.jetbrains.kotlin.name.FqName
 import org.jetbrains.kotlin.name.FqNameUnsafe
-
-class ExportLowering(val context: JsIrBackendContext) : FileLoweringPass {
-    override fun lower(irFile: IrFile) {
-    }
-}
+import org.jetbrains.kotlin.utils.addIfNotNull
 
 class ExportGenerator(val context: JsIrBackendContext) {
 
@@ -39,10 +34,12 @@
         }
     }
 
-    fun generateExport(module: IrModuleFragment): ExportedModule {
-        val files = context.externalPackageFragment.values + module.files
-        return ExportedModule(files.flatMap { generateExport(it) })
-    }
+    fun generateExport(module: IrModuleFragment): ExportedModule =
+        ExportedModule(
+            (context.externalPackageFragment.values + module.files).flatMap {
+                generateExport(it)
+            }
+        )
 
     private fun exportDeclaration(declaration: IrDeclaration): List<ExportedDeclaration> {
         if (declaration !is IrDeclarationWithVisibility ||
@@ -53,105 +50,56 @@
             return emptyList()
         }
 
-        if (!declaration.isExported())
-            return emptyList()
-
-        if (declaration is IrFunction && declaration.isInline && declaration.typeParameters.any { it.isReified })
+        if (!shouldDeclarationBeExported(declaration))
             return emptyList()
 
         return when (declaration) {
-            is IrSimpleFunction -> exportFunction(declaration)
-            is IrProperty -> exportProperty(declaration)
+            is IrSimpleFunction -> when {
+                declaration.correspondingPropertySymbol != null ->
+                    listOfNotNull(exportPropertyByAccessor(declaration))
+                else ->
+                    listOfNotNull(exportFunction(declaration))
+            }
+            is IrProperty -> listOfNotNull(exportProperty(declaration))
             is IrClass -> exportClass(declaration)
             is IrField -> emptyList()
-
             else -> error("Can't export declaration $declaration")
         }
     }
 
-    private fun exportFunction(function: IrSimpleFunction): List<ExportedDeclaration> {
-        if (function.isSuspend)
-            return emptyList()
-        if (function.isFakeOverride)
-            return emptyList()
+    private fun exportPropertyByAccessor(function: IrSimpleFunction): ExportedDeclaration? {
+        return null
+    }
 
-        if (function.origin == IrDeclarationOrigin.BRIDGE ||
-            function.origin == JsLoweredDeclarationOrigin.BRIDGE_TO_EXTERNAL_FUNCTION ||
-            function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER
-        ) {
-            return emptyList()
-        }
-
-        val correspondingProperty = function.correspondingPropertySymbol?.owner
-        if (correspondingProperty != null) {
-            if (correspondingProperty.getter == function) {
-                return exportProperty(correspondingProperty)
-            } else {
-                // TODO: Setter only properties?
-                return emptyList()
+    private fun exportFunction(function: IrSimpleFunction): ExportedDeclaration? =
+        when (val exportability = functionExportability(function)) {
+            is Exportability.NotNeeded -> null
+            is Exportability.Prohibited -> ErrorDeclaration(exportability.reason)
+            is Exportability.Allowed -> {
+                val parent = function.parent
+                ExportedFunction(
+                    function.getExportedIdentifier(),
+                    returnType = exportType(function.returnType),
+                    parameters = (listOfNotNull(function.extensionReceiverParameter) + function.valueParameters).map { exportParameter(it) },
+                    typeParameters = function.typeParameters.map { it.name.identifier },
+                    isMember = parent is IrClass,
+                    isStatic = function.isStaticMethodOfClass,
+                    isAbstract = parent is IrClass && !parent.isInterface && function.modality == Modality.ABSTRACT
+                )
             }
         }
 
-        if (function.isMethodOfAny())
-            return emptyList()
-
-        if (function.name.asString().endsWith("-impl"))
-            return emptyList()
-
-        // TODO: Fix properties
-
-        val name = function.getExportedIdentifier()
-
-        if (name[0].isUpperCase()) return emptyList()
-
-        if (name in allReservedWords)
-            return emptyList()
-
-        if (function.fqNameWhenAvailable == FqName("kotlin.coroutines.Continuation"))
-            return emptyList()
-
-        if (function.fqNameWhenAvailable == FqName("kotlin.Comparator"))
-            return emptyList()
-
-        val returnType = exportType(function.returnType)
-        val parameters = (listOfNotNull(function.extensionReceiverParameter) + function.valueParameters).map { exportParameter(it) }
-        val typeParameters = function.typeParameters.map { it.name.identifier }
-
-        val parent = function.parent
-
-        val isMember: Boolean
-        val isAbstract: Boolean
-        if (parent is IrClass) {
-            isAbstract = !parent.isInterface && function.modality == Modality.ABSTRACT
-            isMember = true
-        } else {
-            isAbstract = false
-            isMember = false
-        }
-
-        return listOf(
-            ExportedFunction(
-                name,
-                returnType,
-                parameters = parameters,
-                typeParameters = typeParameters,
-                isMember = isMember,
-                isStatic = function.isStaticMethodOfClass,
-                isAbstract = isAbstract
-            )
+    private fun exportConstructor(constructor: IrConstructor): ExportedDeclaration? {
+        if (!constructor.isPrimary) return null
+        val allValueParameters = listOfNotNull(constructor.extensionReceiverParameter) + constructor.valueParameters
+        return ExportedConstructor(
+            parameters = allValueParameters.map { exportParameter(it) },
+            isPrivate = false
         )
     }
 
-    private fun exportConstructor(function: IrConstructor): List<ExportedDeclaration> {
-
-        if (!function.isPrimary)
-            return emptyList()
-
-        val parameters = (listOfNotNull(function.extensionReceiverParameter) + function.valueParameters).map { exportParameter(it) }
-        return listOf(ExportedConstructor(parameters, false))
-    }
-
     private fun exportParameter(parameter: IrValueParameter): ExportedParameter {
+        // Parameter names do not matter in d.ts files. They can be renamed as we like
         var parameterName = sanitizeName(parameter.name.asString())
         if (parameterName in allReservedWords)
             parameterName = "_$parameterName"
@@ -159,23 +107,21 @@
         return ExportedParameter(parameterName, exportType(parameter.type))
     }
 
-    private fun exportProperty(property: IrProperty): List<ExportedDeclaration> {
+    private fun exportProperty(property: IrProperty): ExportedDeclaration? {
         for (accessor in listOfNotNull(property.getter, property.setter)) {
             if (accessor.extensionReceiverParameter != null)
-                return emptyList()
+                return null
             if (accessor.isFakeOverride) {
-                return emptyList()
+                return null
             }
         }
 
-        return listOf(
-            ExportedProperty(
-                property.getExportedIdentifier(),
-                exportType(property.getter!!.returnType),
-                mutable = property.isVar,
-                isMember = property.parent is IrClass,
-                isStatic = false
-            )
+        return ExportedProperty(
+            property.getExportedIdentifier(),
+            exportType(property.getter!!.returnType),
+            mutable = property.isVar,
+            isMember = property.parent is IrClass,
+            isStatic = false
         )
     }
 
@@ -188,7 +134,8 @@
                 FqName("kotlin.Error"),
                 FqName("kotlin.AssertionError"),
                 FqName("kotlin.NotImplementedError")
-            ))
+            )
+        )
             return emptyList()
 
         val name = klass.getExportedIdentifier()
@@ -212,13 +159,13 @@
 
             when (declaration) {
                 is IrSimpleFunction ->
-                    members += exportFunction(declaration)
+                    members.addIfNotNull(exportFunction(declaration))
 
                 is IrConstructor ->
-                    members += exportConstructor(declaration)
+                    members.addIfNotNull(exportConstructor(declaration))
 
                 is IrProperty ->
-                    members += exportProperty(declaration)
+                    members.addIfNotNull(exportProperty(declaration))
 
                 is IrClass ->
                     other += exportClass(declaration)
@@ -240,9 +187,9 @@
         val superInterfaces = klass.superTypes
             .filter {
                 it.classifierOrFail.isInterface &&
-                !it.isFunctionOrKFunction() &&
-                !it.isSuspendFunction() &&
-                !it.classifierOrFail.isClassWithFqName(FqNameUnsafe("kotlin.io.Serializable"))
+                        !it.isFunctionOrKFunction() &&
+                        !it.isSuspendFunction() &&
+                        !it.classifierOrFail.isClassWithFqName(FqNameUnsafe("kotlin.io.Serializable"))
             }.map { exportType(it) }
 
         val exportedClass = ExportedClass(
@@ -331,29 +278,65 @@
             else identifier
         }
 
-    private fun IrDeclarationWithName.isExported(): Boolean {
-        if (fqNameWhenAvailable in context.additionalExportedDeclarations)
+    private fun shouldDeclarationBeExported(declaration: IrDeclarationWithName): Boolean {
+
+        if (declaration.fqNameWhenAvailable in context.additionalExportedDeclarations)
             return true
 
-        if (isJsExport())
+        if (declaration.isJsExport())
             return true
 
-        if (this is IrSimpleFunction) {
-            if (correspondingPropertySymbol?.owner?.isJsExport() == true)
+        if (declaration is IrSimpleFunction) {
+            if (declaration.correspondingPropertySymbol?.owner?.isJsExport() == true)
                 return true
         }
 
-        return when (val parent = parent) {
-            is IrDeclarationWithName -> parent.isExported()
+        return when (val parent = declaration.parent) {
+            is IrDeclarationWithName -> shouldDeclarationBeExported(parent)
             is IrAnnotationContainer -> parent.isJsExport()
             else -> false
         }
     }
+
+    private fun functionExportability(function: IrSimpleFunction): Exportability {
+        if (function.isInline && function.typeParameters.any { it.isReified })
+            return Exportability.Prohibited("Inline reified function")
+        if (function.isSuspend)
+            return Exportability.Prohibited("Suspend function")
+        if (function.isFakeOverride)
+            return Exportability.NotNeeded
+        if (function.origin == IrDeclarationOrigin.BRIDGE ||
+            function.origin == JsLoweredDeclarationOrigin.BRIDGE_TO_EXTERNAL_FUNCTION ||
+            function.origin == IrDeclarationOrigin.FUNCTION_FOR_DEFAULT_PARAMETER
+        ) {
+            return Exportability.NotNeeded
+        }
+        if (function.isMethodOfAny())
+            return Exportability.NotNeeded
+        if (function.name.asString().endsWith("-impl"))
+            return Exportability.NotNeeded
+
+        // TODO: Fix properties
+        val name = function.getExportedIdentifier()
+        if (name[0].isUpperCase()) return Exportability.Prohibited("Upper case function")
+        if (name in allReservedWords)
+            return Exportability.Prohibited("Name is a reserved word")
+
+        return Exportability.Allowed
+    }
+
+
 }
 
-private val IrClassifierSymbol.isInterface get() =
-    (owner as? IrClass)?.isInterface == true
+sealed class Exportability {
+    object Allowed : Exportability()
+    object NotNeeded : Exportability()
+    class Prohibited(val reason: String) : Exportability()
+}
 
+private val IrClassifierSymbol.isInterface
+    get() =
+        (owner as? IrClass)?.isInterface == true
 
 val reservedWords = setOf(
     "break",