• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright 2016-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.coroutines.internal
6 
7 import kotlinx.coroutines.*
8 import java.lang.reflect.*
9 import java.util.*
10 import java.util.concurrent.locks.*
11 import kotlin.concurrent.*
12 
13 private val throwableFields = Throwable::class.java.fieldsCountOrDefault(-1)
14 private val cacheLock = ReentrantReadWriteLock()
15 private typealias Ctor = (Throwable) -> Throwable?
16 // Replace it with ClassValue when Java 6 support is over
17 private val exceptionCtors: WeakHashMap<Class<out Throwable>, Ctor> = WeakHashMap()
18 
19 @Suppress("UNCHECKED_CAST")
20 internal fun <E : Throwable> tryCopyException(exception: E): E? {
21     // Fast path for CopyableThrowable
22     if (exception is CopyableThrowable<*>) {
23         return runCatching { exception.createCopy() as E? }.getOrNull()
24     }
25     // Use cached ctor if found
26     cacheLock.read { exceptionCtors[exception.javaClass] }?.let { cachedCtor ->
27         return cachedCtor(exception) as E?
28     }
29     /*
30      * Skip reflective copy if an exception has additional fields (that are usually populated in user-defined constructors)
31      */
32     if (throwableFields != exception.javaClass.fieldsCountOrDefault(0)) {
33         cacheLock.write { exceptionCtors[exception.javaClass] = { null } }
34         return null
35     }
36     /*
37      * Try to reflectively find constructor(), constructor(message, cause), constructor(cause) or constructor(message).
38      * Exceptions are shared among coroutines, so we should copy exception before recovering current stacktrace.
39      */
40     var ctor: Ctor? = null
41     val constructors = exception.javaClass.constructors.sortedByDescending { it.parameterTypes.size }
42     for (constructor in constructors) {
43         ctor = createConstructor(constructor)
44         if (ctor != null) break
45     }
46     // Store the resulting ctor to cache
47     cacheLock.write { exceptionCtors[exception.javaClass] = ctor ?: { null } }
48     return ctor?.invoke(exception) as E?
49 }
50 
createConstructornull51 private fun createConstructor(constructor: Constructor<*>): Ctor? {
52     val p = constructor.parameterTypes
53     return when (p.size) {
54         2 -> when {
55             p[0] == String::class.java && p[1] == Throwable::class.java ->
56                 safeCtor { e -> constructor.newInstance(e.message, e) as Throwable }
57             else -> null
58         }
59         1 -> when (p[0]) {
60             Throwable::class.java ->
61                 safeCtor { e -> constructor.newInstance(e) as Throwable }
62             String::class.java ->
63                 safeCtor { e -> (constructor.newInstance(e.message) as Throwable).also { it.initCause(e) } }
64             else -> null
65         }
66         0 -> safeCtor { e -> (constructor.newInstance() as Throwable).also { it.initCause(e) } }
67         else -> null
68     }
69 }
70 
safeCtornull71 private inline fun safeCtor(crossinline block: (Throwable) -> Throwable): Ctor =
72     { e -> runCatching { block(e) }.getOrNull() }
73 
<lambda>null74 private fun Class<*>.fieldsCountOrDefault(defaultValue: Int) = kotlin.runCatching { fieldsCount() }.getOrDefault(defaultValue)
75 
fieldsCountnull76 private tailrec fun Class<*>.fieldsCount(accumulator: Int = 0): Int {
77     val fieldsCount = declaredFields.count { !Modifier.isStatic(it.modifiers) }
78     val totalFields = accumulator + fieldsCount
79     val superClass = superclass ?: return totalFields
80     return superClass.fieldsCount(totalFields)
81 }
82