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 createConstructornull51private 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 safeCtornull71private inline fun safeCtor(crossinline block: (Throwable) -> Throwable): Ctor = 72 { e -> runCatching { block(e) }.getOrNull() } 73 <lambda>null74private fun Class<*>.fieldsCountOrDefault(defaultValue: Int) = kotlin.runCatching { fieldsCount() }.getOrDefault(defaultValue) 75 fieldsCountnull76private 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