diff --git a/libraries/stdlib/common/src/kotlin/ExceptionsH.kt b/libraries/stdlib/common/src/kotlin/ExceptionsH.kt index 90d627ceddc..6551c443e2a 100644 --- a/libraries/stdlib/common/src/kotlin/ExceptionsH.kt +++ b/libraries/stdlib/common/src/kotlin/ExceptionsH.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 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. */ @@ -115,4 +115,23 @@ internal class KotlinNothingValueException : RuntimeException { constructor(message: String?) : super(message) constructor(message: String?, cause: Throwable?) : super(message, cause) constructor(cause: Throwable?) : super(cause) -} \ No newline at end of file +} + + +/** + * When supported by the platform, adds the specified exception to the list of exceptions that were + * suppressed in order to deliver this exception. + */ +@SinceKotlin("1.4") +public expect fun Throwable.addSuppressed(exception: Throwable) + +/** + * Returns a list of all exceptions that were suppressed in order to deliver this exception. + * + * The list can be empty: + * - if no exceptions were suppressed; + * - if the platform doesn't support suppressed exceptions; + * - if this [Throwable] instance has disabled the suppression. + */ +@SinceKotlin("1.4") +public expect val Throwable.suppressedExceptions: List \ No newline at end of file diff --git a/libraries/stdlib/common/test/testUtils.kt b/libraries/stdlib/common/test/testUtils.kt index 0a24e2d119a..43e0e238bfd 100644 --- a/libraries/stdlib/common/test/testUtils.kt +++ b/libraries/stdlib/common/test/testUtils.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 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. */ @@ -14,4 +14,6 @@ internal expect inline fun testOnNonJvm6And7(f: () -> Unit) public expect fun testOnJvm(action: () -> Unit) public expect fun testOnJs(action: () -> Unit) -public expect val isFloat32RangeEnforced: Boolean \ No newline at end of file +public expect val isFloat32RangeEnforced: Boolean + +public expect val supportsSuppressedExceptions: Boolean \ No newline at end of file diff --git a/libraries/stdlib/jdk7/src/kotlin/internal/jdk7/JDK7PlatformImplementations.kt b/libraries/stdlib/jdk7/src/kotlin/internal/jdk7/JDK7PlatformImplementations.kt index 5cce9d93f94..6eaeff35ae3 100644 --- a/libraries/stdlib/jdk7/src/kotlin/internal/jdk7/JDK7PlatformImplementations.kt +++ b/libraries/stdlib/jdk7/src/kotlin/internal/jdk7/JDK7PlatformImplementations.kt @@ -1,17 +1,6 @@ /* - * Copyright 2010-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2010-2020 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. */ @file:Suppress("INVISIBLE_REFERENCE", "INVISIBLE_MEMBER", "CANNOT_OVERRIDE_INVISIBLE_MEMBER") @@ -23,4 +12,5 @@ internal open class JDK7PlatformImplementations : PlatformImplementations() { override fun addSuppressed(cause: Throwable, exception: Throwable) = cause.addSuppressed(exception) + override fun getSuppressed(exception: Throwable): List = exception.suppressed.asList() } diff --git a/libraries/stdlib/jdk7/testNoJdk7/ExceptionTest.kt b/libraries/stdlib/jdk7/testNoJdk7/ExceptionTest.kt index a146d1586fd..4d6da6f651e 100644 --- a/libraries/stdlib/jdk7/testNoJdk7/ExceptionTest.kt +++ b/libraries/stdlib/jdk7/testNoJdk7/ExceptionTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 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. */ @@ -15,9 +15,11 @@ class ExceptionTest { val e1 = Throwable() val e2 = Exception("Suppressed") + assertTrue(e1.suppressedExceptions.isEmpty()) e1.addSuppressedExtension(e2) assertSame(e2, e1.suppressed.singleOrNull()) + assertSame(e2, e1.suppressedExceptions.singleOrNull()) } } \ No newline at end of file diff --git a/libraries/stdlib/js/src/kotlin/throwableExtensions.kt b/libraries/stdlib/js/src/kotlin/throwableExtensions.kt new file mode 100644 index 00000000000..ddb00d9292f --- /dev/null +++ b/libraries/stdlib/js/src/kotlin/throwableExtensions.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2020 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 kotlin + +/** + * Adds the specified exception to the list of exceptions that were + * suppressed in order to deliver this exception. + */ +@SinceKotlin("1.4") +public actual fun Throwable.addSuppressed(exception: Throwable) { + if (this !== exception) { + val suppressed = this.asDynamic()._suppressed.unsafeCast?>() + if (suppressed == null) { + this.asDynamic()._suppressed = mutableListOf(exception) + } else { + suppressed.add(exception) + } + } +} + +/** + * Returns a list of all exceptions that were suppressed in order to deliver this exception. + */ +@SinceKotlin("1.4") +public actual val Throwable.suppressedExceptions: List + get() { + return this.asDynamic()._suppressed?.unsafeCast>() ?: emptyList() + } diff --git a/libraries/stdlib/js/test/core/testUtils.kt b/libraries/stdlib/js/test/core/testUtils.kt index a54215597e9..a9a6f09c6d2 100644 --- a/libraries/stdlib/js/test/core/testUtils.kt +++ b/libraries/stdlib/js/test/core/testUtils.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 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. */ @@ -23,4 +23,6 @@ public actual fun testOnJvm(action: () -> Unit) { } public actual fun testOnJs(action: () -> Unit) = action() // TODO: should be true at least in JS IR after implementing KT-24975 -public actual val isFloat32RangeEnforced: Boolean = false \ No newline at end of file +public actual val isFloat32RangeEnforced: Boolean = false + +actual val supportsSuppressedExceptions: Boolean get() = true \ No newline at end of file diff --git a/libraries/stdlib/jvm/src/kotlin/internal/PlatformImplementations.kt b/libraries/stdlib/jvm/src/kotlin/internal/PlatformImplementations.kt index e9c0e5ff488..860792a7b59 100644 --- a/libraries/stdlib/jvm/src/kotlin/internal/PlatformImplementations.kt +++ b/libraries/stdlib/jvm/src/kotlin/internal/PlatformImplementations.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 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. */ @@ -12,17 +12,29 @@ import kotlin.random.Random internal open class PlatformImplementations { - private object ReflectAddSuppressedMethod { + private object ReflectThrowable { @JvmField - public val method: Method? = Throwable::class.java.let { throwableClass -> - throwableClass.methods.find { + public val addSuppressed: Method? + @JvmField + public val getSuppressed: Method? + + init { + val throwableClass = Throwable::class.java + val throwableMethods = throwableClass.methods + addSuppressed = throwableMethods.find { it.name == "addSuppressed" && it.parameterTypes.singleOrNull() == throwableClass } + getSuppressed = throwableMethods.find { it.name == "getSuppressed" } } } public open fun addSuppressed(cause: Throwable, exception: Throwable) { - ReflectAddSuppressedMethod.method?.invoke(cause, exception) + ReflectThrowable.addSuppressed?.invoke(cause, exception) + } + + public open fun getSuppressed(exception: Throwable): List { + return ReflectThrowable.getSuppressed?.invoke(exception)?.let { (it as Array).asList() } + ?: emptyList() } public open fun getMatchResultNamedGroup(matchResult: MatchResult, name: String): MatchGroup? { diff --git a/libraries/stdlib/jvm/src/kotlin/util/Exceptions.kt b/libraries/stdlib/jvm/src/kotlin/util/Exceptions.kt index f89f89bdd38..55cb9394c0e 100644 --- a/libraries/stdlib/jvm/src/kotlin/util/Exceptions.kt +++ b/libraries/stdlib/jvm/src/kotlin/util/Exceptions.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 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. */ @@ -42,7 +42,23 @@ public val Throwable.stackTrace: Array get() = (this as java.lang.Throwable).stackTrace!! /** - * When supported by the platform adds the specified exception to the list of exceptions that were + * When supported by the platform, adds the specified exception to the list of exceptions that were * suppressed in order to deliver this exception. */ -public fun Throwable.addSuppressed(exception: Throwable) = IMPLEMENTATIONS.addSuppressed(this, exception) \ No newline at end of file +@SinceKotlin("1.1") +public actual fun Throwable.addSuppressed(exception: Throwable) { + if (this !== exception) + IMPLEMENTATIONS.addSuppressed(this, exception) +} + +/** + * Returns a list of all exceptions that were suppressed in order to deliver this exception. + * + * The list can be empty: + * - if no exceptions were suppressed; + * - if the platform doesn't support suppressed exceptions; + * - if this [Throwable] instance has disabled the suppression. + */ +@SinceKotlin("1.4") +public actual val Throwable.suppressedExceptions: List + get() = IMPLEMENTATIONS.getSuppressed(this) \ No newline at end of file diff --git a/libraries/stdlib/jvm/test/testUtilsJVM.kt b/libraries/stdlib/jvm/test/testUtilsJVM.kt index 42d783866f2..17f17129573 100644 --- a/libraries/stdlib/jvm/test/testUtilsJVM.kt +++ b/libraries/stdlib/jvm/test/testUtilsJVM.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 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. */ @@ -32,4 +32,6 @@ public actual fun testOnJs(action: () -> Unit) {} @Suppress("HasPlatformType", "UNCHECKED_CAST") public fun platformNull() = Collections.singletonList(null as T).first() -public actual val isFloat32RangeEnforced: Boolean = true \ No newline at end of file +public actual val isFloat32RangeEnforced: Boolean = true + +public actual val supportsSuppressedExceptions: Boolean get() = !isJava6 \ No newline at end of file diff --git a/libraries/stdlib/test/ExceptionTest.kt b/libraries/stdlib/test/ExceptionTest.kt index 2b6675f73d2..49424a012f7 100644 --- a/libraries/stdlib/test/ExceptionTest.kt +++ b/libraries/stdlib/test/ExceptionTest.kt @@ -1,10 +1,11 @@ /* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2020 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 test.exceptions +import test.supportsSuppressedExceptions import kotlin.test.* class ExceptionTest { @@ -67,4 +68,23 @@ class ExceptionTest { } } + @Test + fun suppressedExceptions() { + val e1 = Throwable() + + val c1 = Exception("Suppressed 1") + val c2 = Exception("Suppressed 2") + + assertTrue(e1.suppressedExceptions.isEmpty()) + + e1.addSuppressed(c1) + e1.addSuppressed(c2) + + if (supportsSuppressedExceptions) { + assertEquals(listOf(c1, c2), e1.suppressedExceptions) + } else { + assertTrue(e1.suppressedExceptions.isEmpty()) + } + } + } \ No newline at end of file diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index 2b5bb75c3bb..8feebbb9a6f 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt @@ -21,6 +21,7 @@ public abstract interface annotation class kotlin/DslMarker : java/lang/annotati public final class kotlin/ExceptionsKt { public static final fun addSuppressed (Ljava/lang/Throwable;Ljava/lang/Throwable;)V public static final fun getStackTrace (Ljava/lang/Throwable;)[Ljava/lang/StackTraceElement; + public static final fun getSuppressedExceptions (Ljava/lang/Throwable;)Ljava/util/List; } public abstract interface annotation class kotlin/Experimental : java/lang/annotation/Annotation {