[JS][Tests] Implement previous ABI forward compatibility tests

^KT-81955 Fixed
diff --git a/compiler/config/src/org/jetbrains/kotlin/config/KlibAbiCompatibilityLevel.kt b/compiler/config/src/org/jetbrains/kotlin/config/KlibAbiCompatibilityLevel.kt
index 1aed4ba..bb66934 100644
--- a/compiler/config/src/org/jetbrains/kotlin/config/KlibAbiCompatibilityLevel.kt
+++ b/compiler/config/src/org/jetbrains/kotlin/config/KlibAbiCompatibilityLevel.kt
@@ -21,5 +21,7 @@
 
     companion object {
         val LATEST_STABLE = ABI_LEVEL_2_3
+
+        fun fromLanguageVersion(version: LanguageVersion) = entries.find { it.toString() == version.versionString }
     }
 }
diff --git a/compiler/testData/codegen/box/annotations/checkReturnValue.kt b/compiler/testData/codegen/box/annotations/checkReturnValue.kt
index b704fc7..63f0136 100644
--- a/compiler/testData/codegen/box/annotations/checkReturnValue.kt
+++ b/compiler/testData/codegen/box/annotations/checkReturnValue.kt
@@ -2,6 +2,8 @@
 // RETURN_VALUE_CHECKER_MODE: FULL
 // IGNORE_BACKEND_K1: ANY
 // WITH_STDLIB
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
+// ^^^ KT-80605: MustUseReturnValues is added since vesrion 2.3
 
 @file:MustUseReturnValues
 
diff --git a/compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNameBasedDestructuringShortForm.kt b/compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNameBasedDestructuringShortForm.kt
index f783b49..56b691a 100644
--- a/compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNameBasedDestructuringShortForm.kt
+++ b/compiler/testData/codegen/box/controlStructures/forInArrayWithIndex/forInArrayWithIndexNameBasedDestructuringShortForm.kt
@@ -1,6 +1,7 @@
 // ISSUE: KT-80243
 // IGNORE_BACKEND_K1: ANY
 // LANGUAGE: +NameBasedDestructuring, +EnableNameBasedDestructuringShortForm
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
 // WITH_STDLIB
 
 import kotlin.test.assertEquals
diff --git a/compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInCharSequenceWithIndexNameBasedDestructuringShortForm.kt b/compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInCharSequenceWithIndexNameBasedDestructuringShortForm.kt
index cfa3994..ef57663 100644
--- a/compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInCharSequenceWithIndexNameBasedDestructuringShortForm.kt
+++ b/compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInCharSequenceWithIndexNameBasedDestructuringShortForm.kt
@@ -1,6 +1,7 @@
 // ISSUE: KT-80243
 // IGNORE_BACKEND_K1: ANY
 // LANGUAGE: +NameBasedDestructuring, +EnableNameBasedDestructuringShortForm
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
 // WITH_STDLIB
 
 import kotlin.test.assertEquals
diff --git a/compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNameBasedDestructuringShortForm.kt b/compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNameBasedDestructuringShortForm.kt
index 59e56b6..4117b73 100644
--- a/compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNameBasedDestructuringShortForm.kt
+++ b/compiler/testData/codegen/box/controlStructures/forInCharSequenceWithIndex/forInStringWithIndexNameBasedDestructuringShortForm.kt
@@ -1,6 +1,7 @@
 // ISSUE: KT-80243
 // IGNORE_BACKEND_K1: ANY
 // LANGUAGE: +NameBasedDestructuring, +EnableNameBasedDestructuringShortForm
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
 // WITH_STDLIB
 
 import kotlin.test.assertEquals
diff --git a/compiler/testData/codegen/box/controlStructures/forInIterableWithIndex/forInListWithIndexNameBasedDestructuringShortForm.kt b/compiler/testData/codegen/box/controlStructures/forInIterableWithIndex/forInListWithIndexNameBasedDestructuringShortForm.kt
index 825dca2..82ba312 100644
--- a/compiler/testData/codegen/box/controlStructures/forInIterableWithIndex/forInListWithIndexNameBasedDestructuringShortForm.kt
+++ b/compiler/testData/codegen/box/controlStructures/forInIterableWithIndex/forInListWithIndexNameBasedDestructuringShortForm.kt
@@ -1,6 +1,7 @@
 // ISSUE: KT-80243
 // IGNORE_BACKEND_K1: ANY
 // LANGUAGE: +NameBasedDestructuring, +EnableNameBasedDestructuringShortForm
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
 // WITH_STDLIB
 
 import kotlin.test.assertEquals
diff --git a/compiler/testData/codegen/box/controlStructures/forInSequenceWithIndex/forInSequenceWithIndexNameBasedDestructuringShortForm.kt b/compiler/testData/codegen/box/controlStructures/forInSequenceWithIndex/forInSequenceWithIndexNameBasedDestructuringShortForm.kt
index 6a28c95..5be50ee 100644
--- a/compiler/testData/codegen/box/controlStructures/forInSequenceWithIndex/forInSequenceWithIndexNameBasedDestructuringShortForm.kt
+++ b/compiler/testData/codegen/box/controlStructures/forInSequenceWithIndex/forInSequenceWithIndexNameBasedDestructuringShortForm.kt
@@ -1,6 +1,7 @@
 // ISSUE: KT-80243
 // IGNORE_BACKEND_K1: ANY
 // LANGUAGE: +NameBasedDestructuring, +EnableNameBasedDestructuringShortForm
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
 // WITH_STDLIB
 
 import kotlin.test.assertEquals
diff --git a/compiler/testData/codegen/box/coroutines/suspendFunctionAsSupertypeWithInlinedFunInKlib.kt b/compiler/testData/codegen/box/coroutines/suspendFunctionAsSupertypeWithInlinedFunInKlib.kt
index c57cf40..e635dc5 100644
--- a/compiler/testData/codegen/box/coroutines/suspendFunctionAsSupertypeWithInlinedFunInKlib.kt
+++ b/compiler/testData/codegen/box/coroutines/suspendFunctionAsSupertypeWithInlinedFunInKlib.kt
@@ -2,7 +2,8 @@
 // LANGUAGE: +SuspendFunctionAsSupertype
 // LANGUAGE: +IrIntraModuleInlinerBeforeKlibSerialization +IrCrossModuleInlinerBeforeKlibSerialization
 // ^^^ This test demonstrates how after IR Inliner on 1st phase, there is no discrepancy in the name of value parameter of `FAKE_OVERRIDE name:invoke`
-
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
+// ^^^ IrReturnableBlockImpl serialization is not supported at ABI compatibility level 2.2:
 import kotlin.coroutines.*
 
 var failure: String? = "FAIL ILLEGAL STATE"
diff --git a/compiler/testData/codegen/box/defaultArguments/kt59326.kt b/compiler/testData/codegen/box/defaultArguments/kt59326.kt
index d99bbb3..b44b8e2 100644
--- a/compiler/testData/codegen/box/defaultArguments/kt59326.kt
+++ b/compiler/testData/codegen/box/defaultArguments/kt59326.kt
@@ -1,4 +1,6 @@
 // ISSUE: KT-59326
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
+// ^^^ KT-59326 is fixed in 2.2.20-Beta2
 
 // Test that we don't have this exception in Kotlin/JS:
 // Unhandled JavaScript exception: expected expression, got keyword 'default'
diff --git a/compiler/testData/codegen/box/inference/ifWithAssignmentAndNothingBranch.kt b/compiler/testData/codegen/box/inference/ifWithAssignmentAndNothingBranch.kt
index 16fa259..d7523ec 100644
--- a/compiler/testData/codegen/box/inference/ifWithAssignmentAndNothingBranch.kt
+++ b/compiler/testData/codegen/box/inference/ifWithAssignmentAndNothingBranch.kt
@@ -1,6 +1,7 @@
 // IGNORE_JVM_ABI_K1_K2
 // ISSUE: KT-78127
-
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
+// ^^^ KT-78127 is fixed in 2.3.0-Beta2
 fun <T : Any> materialize(): T {
     return "OK" as T
 }
diff --git a/compiler/testData/codegen/box/inline/defaultArgsLoweringWithIntraModuleInliner.kt b/compiler/testData/codegen/box/inline/defaultArgsLoweringWithIntraModuleInliner.kt
index b4e4821..5bcad18 100644
--- a/compiler/testData/codegen/box/inline/defaultArgsLoweringWithIntraModuleInliner.kt
+++ b/compiler/testData/codegen/box/inline/defaultArgsLoweringWithIntraModuleInliner.kt
@@ -1,5 +1,7 @@
 // ISSUE: KT-72446
 // LANGUAGE: +IrIntraModuleInlinerBeforeKlibSerialization
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
+// ^^^ KT-70295: symbol 'kotlin.internal/SharedVariableBox|null[0]' appeared in 2.2.20-Beta1
 // MODULE: lib
 // FILE: lib.kt
 inline fun test(
diff --git a/compiler/testData/codegen/box/ktype/nonReified_equality.kt b/compiler/testData/codegen/box/ktype/nonReified_equality.kt
index 8ad5dce..133eb1d 100644
--- a/compiler/testData/codegen/box/ktype/nonReified_equality.kt
+++ b/compiler/testData/codegen/box/ktype/nonReified_equality.kt
@@ -4,7 +4,9 @@
  */
 // WITH_STDLIB
 // WITH_REFLECT
-
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
+// ^^^ Illegal value: <T>
+//     at assertNotEquals
 
 import kotlin.test.*
 import kotlin.reflect.*
diff --git a/compiler/testData/codegen/box/reflection/classes/multiplatformKClassCreation.kt b/compiler/testData/codegen/box/reflection/classes/multiplatformKClassCreation.kt
index 5d1aa33..98c467c 100644
--- a/compiler/testData/codegen/box/reflection/classes/multiplatformKClassCreation.kt
+++ b/compiler/testData/codegen/box/reflection/classes/multiplatformKClassCreation.kt
@@ -1,5 +1,7 @@
 // KT-77372
 // WITH_REFLECT
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
+// ^^^ KT-77372: Reflection intrinsics like `createKTypeParameter` have been moved to package `kotlin.reflect.js.internal` in 2.2.20-Beta1
 
 import kotlin.test.assertEquals
 import kotlin.reflect.KClass
diff --git a/compiler/testData/codegen/box/reflection/typeOf/ktype1_anonymousObject.kt b/compiler/testData/codegen/box/reflection/typeOf/ktype1_anonymousObject.kt
index a7535d0..90e995f 100644
--- a/compiler/testData/codegen/box/reflection/typeOf/ktype1_anonymousObject.kt
+++ b/compiler/testData/codegen/box/reflection/typeOf/ktype1_anonymousObject.kt
@@ -1,6 +1,9 @@
 // DONT_TARGET_EXACT_BACKEND: JVM_IR
 // IGNORE_BACKEND_K2_MULTI_MODULE: JVM_IR, JVM_IR_SERIALIZE
 // ^ In Kotlin/JVM, KType.toString() of an anonymous object returns a synthetic name, not "???".
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
+// ^^^ In 2.2.0 there was old value `(non-denotable type)`
+//     In 2.3.0-Beta1, `Render non-denotable types as "???" on non-JVM targets` has been done.
 // WITH_STDLIB
 // WITH_REFLECT
 
diff --git a/compiler/testData/codegen/box/reflection/typeOf/noReflect/nonReifiedTypeParameters/equalsOnClassParameters.kt b/compiler/testData/codegen/box/reflection/typeOf/noReflect/nonReifiedTypeParameters/equalsOnClassParameters.kt
index ad75a1c..fec8c35 100644
--- a/compiler/testData/codegen/box/reflection/typeOf/noReflect/nonReifiedTypeParameters/equalsOnClassParameters.kt
+++ b/compiler/testData/codegen/box/reflection/typeOf/noReflect/nonReifiedTypeParameters/equalsOnClassParameters.kt
@@ -1,4 +1,7 @@
 // WITH_STDLIB
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
+// ^^^ Illegal value: <X>
+//     at assertNotEquals
 
 package test
 
diff --git a/compiler/testData/codegen/box/reflection/typeOf/noReflect/nonReifiedTypeParameters/equalsOnFunctionParameters.kt b/compiler/testData/codegen/box/reflection/typeOf/noReflect/nonReifiedTypeParameters/equalsOnFunctionParameters.kt
index 7283159..57c399c 100644
--- a/compiler/testData/codegen/box/reflection/typeOf/noReflect/nonReifiedTypeParameters/equalsOnFunctionParameters.kt
+++ b/compiler/testData/codegen/box/reflection/typeOf/noReflect/nonReifiedTypeParameters/equalsOnFunctionParameters.kt
@@ -1,5 +1,7 @@
 // WITH_STDLIB
-
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
+// ^^^ Illegal value: <X>
+//     at assertNotEquals
 package test
 
 import kotlin.reflect.typeOf
diff --git a/compiler/testData/codegen/box/reflection/typeOf/nonReifiedTypeParameters/typeParameterFlags.kt b/compiler/testData/codegen/box/reflection/typeOf/nonReifiedTypeParameters/typeParameterFlags.kt
index 79b6b3f..c54200d 100644
--- a/compiler/testData/codegen/box/reflection/typeOf/nonReifiedTypeParameters/typeParameterFlags.kt
+++ b/compiler/testData/codegen/box/reflection/typeOf/nonReifiedTypeParameters/typeParameterFlags.kt
@@ -1,5 +1,8 @@
 // WITH_REFLECT
 // WITH_STDLIB
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
+// ^^^ Expected <in IN>, actual <IN>.
+//     at assertEquals
 
 // FILE: lib.kt
 package test
diff --git a/compiler/testData/codegen/box/regressions/kt79516.kt b/compiler/testData/codegen/box/regressions/kt79516.kt
index bab0ed1..9d3e784 100644
--- a/compiler/testData/codegen/box/regressions/kt79516.kt
+++ b/compiler/testData/codegen/box/regressions/kt79516.kt
@@ -1,3 +1,5 @@
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
+// ^^^ KT-79516 is fixed in 2.2.20-Beta1
 interface Key<T, R>
 
 interface ErrorTest {
diff --git a/compiler/testData/codegen/box/typealias/importNestedTypealiasFromAnotherModule.kt b/compiler/testData/codegen/box/typealias/importNestedTypealiasFromAnotherModule.kt
index 12d8e20..f224b80 100644
--- a/compiler/testData/codegen/box/typealias/importNestedTypealiasFromAnotherModule.kt
+++ b/compiler/testData/codegen/box/typealias/importNestedTypealiasFromAnotherModule.kt
@@ -1,6 +1,7 @@
 // IGNORE_BACKEND_K1: ANY
 // ISSUE: KT-79519
 // LANGUAGE: +NestedTypeAliases
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
 
 // MODULE: lib
 // FILE: lib.kt
diff --git a/compiler/testData/codegen/boxInline/anonymousObject/capturedLocalFunReflection.kt b/compiler/testData/codegen/boxInline/anonymousObject/capturedLocalFunReflection.kt
index 6ea11bc..fd5af99 100644
--- a/compiler/testData/codegen/boxInline/anonymousObject/capturedLocalFunReflection.kt
+++ b/compiler/testData/codegen/boxInline/anonymousObject/capturedLocalFunReflection.kt
@@ -1,5 +1,7 @@
 // ISSUE: KT-78537
 // NO_CHECK_LAMBDA_INLINING
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
+// ^^^ KT-78537 is fixed in 2.3.0-Beta1
 
 // FILE: 1.kt
 package test
diff --git a/compiler/testData/codegen/boxInline/anonymousObject/capturedLocalFunReflectionCrossModule.kt b/compiler/testData/codegen/boxInline/anonymousObject/capturedLocalFunReflectionCrossModule.kt
index c134e8a..1d5e629 100644
--- a/compiler/testData/codegen/boxInline/anonymousObject/capturedLocalFunReflectionCrossModule.kt
+++ b/compiler/testData/codegen/boxInline/anonymousObject/capturedLocalFunReflectionCrossModule.kt
@@ -1,6 +1,7 @@
 // ISSUE: KT-78537
 // NO_CHECK_LAMBDA_INLINING
-
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
+// ^^^ KT-78537 is fixed in 2.3.0-Beta1
 // MODULE: lib
 // FILE: 1.kt
 inline fun <T> myRun(block: () -> T) = block()
diff --git a/compiler/testData/codegen/boxInline/multiModule/notInlinedFunFromKlib.kt b/compiler/testData/codegen/boxInline/multiModule/notInlinedFunFromKlib.kt
index 36aacdd..f641c6f 100644
--- a/compiler/testData/codegen/boxInline/multiModule/notInlinedFunFromKlib.kt
+++ b/compiler/testData/codegen/boxInline/multiModule/notInlinedFunFromKlib.kt
@@ -1,5 +1,7 @@
 // SKIP_IR_DESERIALIZATION_CHECKS
 // LANGUAGE: +IrIntraModuleInlinerBeforeKlibSerialization -IrCrossModuleInlinerBeforeKlibSerialization
+// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0
+// ^^^ IrReturnableBlockImpl serialization is not supported at ABI compatibility level 2.2:
 // DUMP_IR_AFTER_INLINE
 // WITH_STDLIB
 
diff --git a/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/klib/CustomKlibCompilerSecondPhaseTestSuppressor.kt b/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/klib/CustomKlibCompilerSecondPhaseTestSuppressor.kt
new file mode 100644
index 0000000..fe60c39
--- /dev/null
+++ b/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/klib/CustomKlibCompilerSecondPhaseTestSuppressor.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2010-2025 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.test.klib
+
+import org.jetbrains.kotlin.test.WrappedException
+import org.jetbrains.kotlin.test.directives.model.DirectivesContainer
+import org.jetbrains.kotlin.test.klib.CustomKlibCompilerTestDirectives.IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE
+import org.jetbrains.kotlin.test.model.AfterAnalysisChecker
+import org.jetbrains.kotlin.test.services.TestServices
+import org.jetbrains.kotlin.test.services.moduleStructure
+import org.junit.jupiter.api.Assumptions
+
+/**
+ * Mute (ignore) tests where the custom compiler failed to compile test data in the second (backend) phase.
+ * It's only allowed to mute such tests for a specific version of the custom compiler specified in
+ *   [IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE] directive.
+ */
+class CustomKlibCompilerSecondPhaseTestSuppressor(
+    testServices: TestServices,
+    private val customCompilerVersion: String,
+) : AfterAnalysisChecker(testServices) {
+    override val directiveContainers: List<DirectivesContainer>
+        get() = listOf(CustomKlibCompilerTestDirectives)
+
+    override fun suppressIfNeeded(failedAssertions: List<WrappedException>): List<WrappedException> {
+        if (failedAssertions.isEmpty())
+            return emptyList()
+
+        val newFailedAssertions = failedAssertions.flatMap { wrappedException ->
+            processSecondPhaseException(wrappedException)
+        }
+
+        if (newFailedAssertions.isEmpty()) {
+            // Explicitly mark the test as "ignored".
+            throw Assumptions.abort<Nothing>()
+        } else {
+            return newFailedAssertions
+        }
+    }
+
+    private fun processSecondPhaseException(wrappedException: WrappedException): List<WrappedException> {
+        val directives = testServices.moduleStructure.modules.first().directives
+        if (customCompilerVersion == "2.2.0") {
+            // Ideally, the directive `// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: 2.2.0` should be temporarily added to each failed test
+            // However, too much testData fails with the following reasons. So let's use a temporary hack which will not be needed after 2.4.0-Beta1 release
+            val wrappedExceptionText = wrappedException.cause.toString()
+            if (wrappedExceptionText.contains("IrLinkageError: Function 'assertFailsWith' can not be called: No function found for symbol"))
+                return emptyList() // KT-79094: kotlin.test.assertFailsWith has changed its signature in 2.3.0-Beta1
+            if (wrappedExceptionText.contains("IrLinkageError: Function 'assertFails' can not be called: No function found for symbol"))
+                return emptyList()// KT-79094: kotlin.test.assertFails has changed its signature in 2.3.0-Beta1
+            if (wrappedExceptionText.contains("Context parameter serialization is not supported at ABI compatibility level 2.2"))
+                return emptyList() // A hack to avoid
+        }
+        if (IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE !in directives)
+            return listOf(wrappedException)
+
+        for (prefix in directives[IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE]) {
+            if (customCompilerVersion.startsWith(prefix))
+                return emptyList()
+        }
+
+        return listOf(
+            wrappedException,
+            AssertionError(
+                "Looks like this test can be unmuted. Remove $customCompilerVersion from the $IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE directive"
+            ).wrap()
+        )
+    }
+
+    companion object {
+        private val FRONTEND_ERROR_MESSAGE_REGEX = Regex("\\S+.kt:\\d+:\\d+: error: .*")
+        private val COMPILER_EXCEPTION_MESSAGE_REGEX = Regex("exception: (org\\.jetbrains\\.kotlin\\..*|java\\..*Exception):")
+    }
+}
diff --git a/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/klib/CustomKlibCompilerTestDirectives.kt b/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/klib/CustomKlibCompilerTestDirectives.kt
index 898a1d3f..ab579a2 100644
--- a/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/klib/CustomKlibCompilerTestDirectives.kt
+++ b/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/klib/CustomKlibCompilerTestDirectives.kt
@@ -17,4 +17,14 @@
             to be ignored.
         """.trimIndent(),
     )
+
+    val IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE by stringDirective(
+        description = """
+            Ignore a KLIB forward-compatibility test (i.e. the test with the custom KLIB compiler version
+            used on the second phase of the test pipeline).
+            
+            The value of this directive is the specific compiler version prefixes where the test is expected
+            to be ignored.
+        """.trimIndent(),
+    )
 }
diff --git a/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/services/CompilerConfigurationProvider.kt b/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/services/CompilerConfigurationProvider.kt
index 2f6bf8f..c78db78 100644
--- a/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/services/CompilerConfigurationProvider.kt
+++ b/compiler/tests-common-new/testFixtures/org/jetbrains/kotlin/test/services/CompilerConfigurationProvider.kt
@@ -183,6 +183,8 @@
     val messageCollector = MessageCollectorForCompilerTests(System.err, CompilerTestMessageRenderer(module))
     configuration.messageCollector = messageCollector
     configuration.languageVersionSettings = module.languageVersionSettings
+    configuration.klibAbiCompatibilityLevel = KlibAbiCompatibilityLevel.fromLanguageVersion(module.languageVersionSettings.languageVersion)
+        ?: error("Unsupported language version: ${module.languageVersionSettings.languageVersion}")
 
     for (configurator in configurators) {
         if (compilationStage == configurator.compilationStage) {
diff --git a/js/js.tests/klib-compatibility/testFixtures/org/jetbrains/kotlin/js/test/klib/AbstractCustomJsCompilerSecondPhaseTest.kt b/js/js.tests/klib-compatibility/testFixtures/org/jetbrains/kotlin/js/test/klib/AbstractCustomJsCompilerSecondPhaseTest.kt
index 6705fe7..76b23e5 100644
--- a/js/js.tests/klib-compatibility/testFixtures/org/jetbrains/kotlin/js/test/klib/AbstractCustomJsCompilerSecondPhaseTest.kt
+++ b/js/js.tests/klib-compatibility/testFixtures/org/jetbrains/kotlin/js/test/klib/AbstractCustomJsCompilerSecondPhaseTest.kt
@@ -5,6 +5,8 @@
 
 package org.jetbrains.kotlin.js.test.klib
 
+import com.intellij.testFramework.TestDataFile
+import org.jetbrains.kotlin.config.ApiVersion
 import org.jetbrains.kotlin.config.LanguageVersion
 import org.jetbrains.kotlin.js.test.fir.setUpDefaultDirectivesForJsBoxTest
 import org.jetbrains.kotlin.js.test.ir.commonConfigurationForJsTest
@@ -16,24 +18,45 @@
 import org.jetbrains.kotlin.test.builders.jsArtifactsHandlersStep
 import org.jetbrains.kotlin.test.configuration.commonFirHandlersForCodegenTest
 import org.jetbrains.kotlin.test.directives.ConfigurationDirectives.WITH_STDLIB
+import org.jetbrains.kotlin.test.directives.LanguageSettingsDirectives.ALLOW_DANGEROUS_LANGUAGE_VERSION_TESTING
+import org.jetbrains.kotlin.test.directives.LanguageSettingsDirectives.API_VERSION
 import org.jetbrains.kotlin.test.directives.LanguageSettingsDirectives.LANGUAGE
+import org.jetbrains.kotlin.test.directives.LanguageSettingsDirectives.LANGUAGE_VERSION
+import org.jetbrains.kotlin.test.klib.CustomKlibCompilerSecondPhaseTestSuppressor
 import org.jetbrains.kotlin.test.klib.CustomKlibCompilerTestSuppressor
 import org.jetbrains.kotlin.test.model.TestFile
 import org.jetbrains.kotlin.test.runners.AbstractKotlinCompilerWithTargetBackendTest
 import org.jetbrains.kotlin.test.services.SourceFilePreprocessor
 import org.jetbrains.kotlin.test.services.TestServices
+import org.jetbrains.kotlin.utils.bind
 import org.junit.jupiter.api.Assumptions
 import org.junit.jupiter.api.Tag
 
 @Tag("custom-second-phase")
 open class AbstractCustomJsCompilerSecondPhaseTest : AbstractKotlinCompilerWithTargetBackendTest(TargetBackend.JS_IR) {
+    override fun runTest(@TestDataFile filePath: String) {
+        try {
+            super.runTest(filePath)
+        } catch (ise: java.lang.IllegalStateException) {
+            // Sadly, this kind of exception happens before the action of MetaTestConfigurator
+            // AfterAnalysisChecker cannot handle it as well.
+            // So, here's the only place to intercept the exception and ignore tests which explicitly set `// API_VERSION: XXX`
+            if (ise.message?.contains("Too many values passed to API_VERSION") == true) {
+                throw Assumptions.abort<Nothing>()
+            } else throw ise
+        }
+    }
+
     override fun configure(builder: TestConfigurationBuilder) = with(builder) {
-        // Don't run this test if KLIBs produced by the first phase are not consumable by the older compiler versions
-        // used on the second phase.
-        Assumptions.assumeTrue(customJsCompilerSettings.defaultLanguageVersion >= LanguageVersion.LATEST_STABLE)
         // KT-47200: TODO export `box()` by means of CLI configuration, and not using `JsExportBoxPreprocessor` hack.
         useSourcePreprocessor(::JsExportBoxPreprocessor)
         defaultDirectives {
+            if (customJsCompilerSettings.defaultLanguageVersion < LanguageVersion.LATEST_STABLE) {
+                +ALLOW_DANGEROUS_LANGUAGE_VERSION_TESTING
+                LANGUAGE_VERSION with customJsCompilerSettings.defaultLanguageVersion
+                API_VERSION with ApiVersion.createByLanguageVersion(customJsCompilerSettings.defaultLanguageVersion)
+                LANGUAGE with "+ExportKlibToOlderAbiVersion"
+            }
             // `js-ir-minimal-for-test` must not be used in this test at all, so need to use `kotlin-test` library via `WITH_STDLIB` directive
             // Note: attempt to use `js-ir-minimal-for-test` on 1st stage will cause unresolved symbol `assertEquals(0:0;0:0){0§<kotlin.Any?>}`
             // on 2nd stage, since this symbol is absent in `kotlin-test` library.
@@ -56,6 +79,8 @@
         useAfterAnalysisCheckers(
             // Suppress all tests that failed on the first phase if they are anyway marked as "IGNORE_BACKEND*".
             ::CustomKlibCompilerTestSuppressor,
+            // Suppress tests that failed on the first phase if they have `// IGNORE_KLIB_BACKEND_ERRORS_WITH_CUSTOM_SECOND_PHASE: X.Y.Z`
+            ::CustomKlibCompilerSecondPhaseTestSuppressor.bind(customJsCompilerSettings.version),
         )
         forTestsMatching("compiler/testData/codegen/box/properties/backingField/*") {
             defaultDirectives {