diff options
Diffstat (limited to 'src/main/kotlin/me/bush/illnamethislater/Util.kt')
-rw-r--r-- | src/main/kotlin/me/bush/illnamethislater/Util.kt | 56 |
1 files changed, 42 insertions, 14 deletions
diff --git a/src/main/kotlin/me/bush/illnamethislater/Util.kt b/src/main/kotlin/me/bush/illnamethislater/Util.kt index 5285718..ab486ab 100644 --- a/src/main/kotlin/me/bush/illnamethislater/Util.kt +++ b/src/main/kotlin/me/bush/illnamethislater/Util.kt @@ -1,36 +1,34 @@ package me.bush.illnamethislater +import sun.misc.Unsafe import java.lang.reflect.Modifier -import java.util.concurrent.atomic.AtomicInteger -import java.util.concurrent.atomic.AtomicLong -import kotlin.reflect.KCallable -import kotlin.reflect.KClass -import kotlin.reflect.KProperty +import kotlin.reflect.* import kotlin.reflect.full.allSuperclasses import kotlin.reflect.full.declaredMembers -import kotlin.reflect.full.starProjectedType +import kotlin.reflect.full.isSubclassOf +import kotlin.reflect.full.isSubtypeOf import kotlin.reflect.jvm.javaField import kotlin.reflect.jvm.javaGetter // author bush // since 1.0.0 -/* +/** * Using [KClass.members] only returns public members, and * using [KClass.declaredMembers] doesn't return inherited * members. This returns all members, private and inherited. */ internal val <T : Any> KClass<T>.allMembers - get() = declaredMembers + allSuperclasses.flatMap { it.declaredMembers } + get() = (declaredMembers + allSuperclasses.flatMap { it.declaredMembers }).asSequence() -/* +/** * Checks if a [KCallable] is static on the jvm, and handles invocation accordingly. * * I am not aware of a better alternative that works with `object` classes. */ internal fun <R> KCallable<R>.handleCall(receiver: Any) = if (static) call() else call(receiver) -/* +/** * Checks if the calling [KCallable] is a static java field. * * Because kotlin likes to be funny, properties belonging to @@ -45,11 +43,41 @@ internal val KCallable<*>.static get() = if (this !is KProperty<*> || javaGetter != null) false else javaField?.let { Modifier.isStatic(it.modifiers) } ?: false -/* +/** * Finds all listeners in a class. (properties and methods) */ @Suppress("UNCHECKED_CAST") // This cannot fail internal inline val KClass<*>.listeners - get() = allMembers.asSequence().filter { - it.returnType == Listener::class.starProjectedType - } as Sequence<KCallable<Listener>> + get() = allMembers.filter { it.returnType == typeOf<Listener>() } as Sequence<KCallable<Listener>> + +internal fun interface CancelledState { + fun isCancelled(event: Any): Boolean +} + +private val unsafe = Unsafe::class.java.getDeclaredField("theUnsafe").let { + it.isAccessible = true + it.get(null) as Unsafe +} + +private val offsetMap = hashMapOf<KClass<*>, Long>() + +private val possibleNames = arrayOf("canceled", "cancelled") + +private val NOT_CANCELLABLE = CancelledState { false } + +internal fun cancelStateOf(type: KClass<*>) = when { + type.isSubclassOf(Event::class) -> CancelledState { (it as Event).cancelled } + else -> findCancelField(type)?.let { + offsetMap[type] = unsafe.objectFieldOffset(it.javaField) + CancelledState { event -> unsafe.getBoolean(event, offsetMap[type]!!) } + } ?: NOT_CANCELLABLE +} + +private fun findCancelField(type: KClass<*>) = type.allMembers + .filter { it.name in possibleNames && it.returnType == typeOf<Boolean>() } + .filterIsInstance<KMutableProperty1<*, *>>().toList().let { + if (it.isEmpty()) null else { + if (it.size != 1) TODO() // warn + it[0] + } + } |