1 /* <lambda>null2 * Copyright 2016-2021 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 android.annotation.SuppressLint 8 import kotlinx.coroutines.* 9 import java.lang.reflect.* 10 import java.util.* 11 import java.util.concurrent.locks.* 12 import kotlin.concurrent.* 13 14 private val throwableFields = Throwable::class.java.fieldsCountOrDefault(-1) 15 private typealias Ctor = (Throwable) -> Throwable? 16 17 private val ctorCache = try { 18 if (ANDROID_DETECTED) WeakMapCtorCache 19 else ClassValueCtorCache 20 } catch (e: Throwable) { 21 // Fallback on Java 6 or exotic setups 22 WeakMapCtorCache 23 } 24 25 @Suppress("UNCHECKED_CAST") tryCopyExceptionnull26internal fun <E : Throwable> tryCopyException(exception: E): E? { 27 // Fast path for CopyableThrowable 28 if (exception is CopyableThrowable<*>) { 29 return runCatching { exception.createCopy() as E? }.getOrNull() 30 } 31 return ctorCache.get(exception.javaClass).invoke(exception) as E? 32 } 33 createConstructornull34private fun <E : Throwable> createConstructor(clz: Class<E>): Ctor { 35 val nullResult: Ctor = { null } // Pre-cache class 36 // Skip reflective copy if an exception has additional fields (that are usually populated in user-defined constructors) 37 if (throwableFields != clz.fieldsCountOrDefault(0)) return nullResult 38 /* 39 * Try to reflectively find constructor(), constructor(message, cause), constructor(cause) or constructor(message). 40 * Exceptions are shared among coroutines, so we should copy exception before recovering current stacktrace. 41 */ 42 val constructors = clz.constructors.sortedByDescending { it.parameterTypes.size } 43 for (constructor in constructors) { 44 val result = createSafeConstructor(constructor) 45 if (result != null) return result 46 } 47 return nullResult 48 } 49 createSafeConstructornull50private fun createSafeConstructor(constructor: Constructor<*>): Ctor? { 51 val p = constructor.parameterTypes 52 return when (p.size) { 53 2 -> when { 54 p[0] == String::class.java && p[1] == Throwable::class.java -> 55 safeCtor { e -> constructor.newInstance(e.message, e) as Throwable } 56 else -> null 57 } 58 1 -> when (p[0]) { 59 Throwable::class.java -> 60 safeCtor { e -> constructor.newInstance(e) as Throwable } 61 String::class.java -> 62 safeCtor { e -> (constructor.newInstance(e.message) as Throwable).also { it.initCause(e) } } 63 else -> null 64 } 65 0 -> safeCtor { e -> (constructor.newInstance() as Throwable).also { it.initCause(e) } } 66 else -> null 67 } 68 } 69 safeCtornull70private inline fun safeCtor(crossinline block: (Throwable) -> Throwable): Ctor = 71 { e -> runCatching { block(e) }.getOrNull() } 72 fieldsCountOrDefaultnull73private fun Class<*>.fieldsCountOrDefault(defaultValue: Int) = 74 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 83 internal abstract class CtorCache { getnull84 abstract fun get(key: Class<out Throwable>): Ctor 85 } 86 87 private object WeakMapCtorCache : CtorCache() { 88 private val cacheLock = ReentrantReadWriteLock() 89 private val exceptionCtors: WeakHashMap<Class<out Throwable>, Ctor> = WeakHashMap() 90 91 override fun get(key: Class<out Throwable>): Ctor { 92 cacheLock.read { exceptionCtors[key]?.let { return it } } 93 cacheLock.write { 94 exceptionCtors[key]?.let { return it } 95 return createConstructor(key).also { exceptionCtors[key] = it } 96 } 97 } 98 } 99 100 @IgnoreJreRequirement 101 @SuppressLint("NewApi") 102 private object ClassValueCtorCache : CtorCache() { 103 private val cache = object : ClassValue<Ctor>() { computeValuenull104 override fun computeValue(type: Class<*>?): Ctor { 105 @Suppress("UNCHECKED_CAST") 106 return createConstructor(type as Class<out Throwable>) 107 } 108 } 109 getnull110 override fun get(key: Class<out Throwable>): Ctor = cache.get(key) 111 } 112