code cleanup, extracted more complex logic into it's own function

This commit is contained in:
nathan 2020-08-17 16:52:46 +02:00
parent 98c77488b5
commit d2caea6645

View File

@ -19,10 +19,11 @@ import com.esotericsoftware.kryo.Serializer
import com.esotericsoftware.reflectasm.MethodAccess import com.esotericsoftware.reflectasm.MethodAccess
import dorkbox.network.connection.Connection import dorkbox.network.connection.Connection
import dorkbox.util.classes.ClassHelper import dorkbox.util.classes.ClassHelper
import org.slf4j.Logger import mu.KLogger
import java.lang.reflect.Method import java.lang.reflect.Method
import java.lang.reflect.Modifier import java.lang.reflect.Modifier
import java.util.* import java.util.*
import kotlin.coroutines.Continuation
/** /**
* Utility methods for creating a method cache for a class or interface. * Utility methods for creating a method cache for a class or interface.
@ -73,14 +74,16 @@ object RmiUtils {
throw RuntimeException("Two methods with same signature! ('$o1Name', '$o2Name'") throw RuntimeException("Two methods with same signature! ('$o1Name', '$o2Name'")
} }
private fun getReflectAsmMethod(logger: Logger, clazz: Class<*>): MethodAccess? { private fun getReflectAsmMethod(logger: KLogger, clazz: Class<*>): MethodAccess? {
return try { return try {
val methodAccess = MethodAccess.get(clazz) val methodAccess = MethodAccess.get(clazz)
if (methodAccess.methodNames.size == 0 && methodAccess.parameterTypes.size == 0 && methodAccess.returnTypes.size == 0) {
if (methodAccess.methodNames.isEmpty() && methodAccess.parameterTypes.isEmpty() && methodAccess.returnTypes.isEmpty()) {
// there was NOTHING that reflectASM found, so trying to use it doesn't do us any good // there was NOTHING that reflectASM found, so trying to use it doesn't do us any good
null null
} else methodAccess } else {
methodAccess
}
} catch (e: Exception) { } catch (e: Exception) {
logger.error("Unable to create ReflectASM method access", e) logger.error("Unable to create ReflectASM method access", e)
null null
@ -91,7 +94,7 @@ object RmiUtils {
* @param iFace this is never null. * @param iFace this is never null.
* @param impl this is NULL on the rmi "client" side. This is NOT NULL on the "server" side (where the object lives) * @param impl this is NULL on the rmi "client" side. This is NOT NULL on the "server" side (where the object lives)
*/ */
fun getCachedMethods(logger: Logger, kryo: Kryo, asmEnabled: Boolean, iFace: Class<*>, impl: Class<*>?, classId: Int): Array<CachedMethod> { fun getCachedMethods(logger: KLogger, kryo: Kryo, asmEnabled: Boolean, iFace: Class<*>, impl: Class<*>?, classId: Int): Array<CachedMethod> {
var ifaceAsmMethodAccess: MethodAccess? = null var ifaceAsmMethodAccess: MethodAccess? = null
var implAsmMethodAccess: MethodAccess? = null var implAsmMethodAccess: MethodAccess? = null
@ -124,6 +127,9 @@ object RmiUtils {
if (asmEnabled) { if (asmEnabled) {
ifaceAsmMethodAccess = getReflectAsmMethod(logger, iFace) ifaceAsmMethodAccess = getReflectAsmMethod(logger, iFace)
} }
val hasConnectionOverrideMethods = hasOverwriteMethodWithConnectionParam(implMethods)
for (i in 0 until size) { for (i in 0 until size) {
val method = methods[i] val method = methods[i]
@ -132,18 +138,14 @@ object RmiUtils {
// Store the serializer for each final parameter. // Store the serializer for each final parameter.
// this is ONLY for the ORIGINAL method, not the overridden one. // this is ONLY for the ORIGINAL method, not the overridden one.
val serializers = arrayOfNulls<Serializer<*>?>(parameterTypes.size) val serializers = arrayOfNulls<Serializer<*>>(parameterTypes.size)
var ii = 0
val nn = parameterTypes.size
while (ii < nn) {
if (kryo.isFinal(parameterTypes[ii])) {
serializers[ii] = kryo.getSerializer(parameterTypes[ii])
}
ii++
}
@Suppress("UNCHECKED_CAST") /// we know this is correct, so it is safe to suppress the warning
serializers as Array<Serializer<*>>
parameterTypes.forEachIndexed { index, parameterType ->
val paramClazz = parameterTypes[index]
if (kryo.isFinal(paramClazz) || paramClazz === Continuation::class.java) {
serializers[index] = kryo.getSerializer(parameterType)
}
}
// copy because they can be overridden // copy because they can be overridden
var cachedMethod: CachedMethod? = null var cachedMethod: CachedMethod? = null
@ -156,18 +158,20 @@ object RmiUtils {
var overwrittenMethod: Method? = null var overwrittenMethod: Method? = null
// this is how we detect if the method has been changed from the interface -> implementation + connection parameter // this is how we detect if the method has been changed from the interface -> implementation + connection parameter
if (implMethods != null) { if (implMethods != null && hasConnectionOverrideMethods) {
overwrittenMethod = getOverwriteMethodWithConnectionParam(implMethods, method) overwrittenMethod = getOverwriteMethodWithConnectionParam(implMethods, method)
if (overwrittenMethod != null) { if (overwrittenMethod != null) {
if (logger.isTraceEnabled) { logger.trace {
logger.trace("Overridden method: {}.{}", impl, method.name) "Overridden method: $impl.${method.name}"
} }
// still might be null! // still might be null!
iface_OR_ImplMethodAccess = implAsmMethodAccess iface_OR_ImplMethodAccess = implAsmMethodAccess
} }
} }
if (canUseAsm) { if (canUseAsm) {
try { try {
val index = if (overwrittenMethod != null) { val index = if (overwrittenMethod != null) {
@ -209,6 +213,27 @@ object RmiUtils {
return cachedMethods as Array<CachedMethod> return cachedMethods as Array<CachedMethod>
} }
/**
* Check to see if there are ANY methods in this class that start with a "Connection" parameter.
*/
private fun hasOverwriteMethodWithConnectionParam(implMethods: Array<Method>?): Boolean {
if (implMethods == null) {
return false
}
// maybe there is a method that starts with a "Connection" parameter.
for (implMethod in implMethods) {
val implParameters = implMethod.parameterTypes
// check if the FIRST parameter is "Connection"
if (implParameters.isNotEmpty() && ClassHelper.hasInterface(Connection::class.java, implParameters[0])) {
return true
}
}
return false
}
/** /**
* This will overwrite an original (iface based) method with a method from the implementation ONLY if there is the extra 'Connection' parameter (as per above) * This will overwrite an original (iface based) method with a method from the implementation ONLY if there is the extra 'Connection' parameter (as per above)
* NOTE: does not null check * NOTE: does not null check
@ -245,15 +270,15 @@ object RmiUtils {
for (implMethod in implMethods) { for (implMethod in implMethods) {
val implName = implMethod.name val implName = implMethod.name
val implTypes = implMethod.parameterTypes val implParameters = implMethod.parameterTypes
val implLength = implTypes.size val implLength = implParameters.size
if (origLength != implLength || origName != implName) { if (origLength != implLength || origName != implName) {
continue continue
} }
// checkLength > 0 // check if the FIRST parameter is "Connection"
val shouldBeConnectionType = implTypes[0] if (ClassHelper.hasInterface(Connection::class.java, implParameters[0])) {
if (ClassHelper.hasInterface(Connection::class.java, shouldBeConnectionType)) {
// now we check to see if our "check" method is equal to our "cached" method + Connection // now we check to see if our "check" method is equal to our "cached" method + Connection
if (implLength == 1) { if (implLength == 1) {
// we only have "Connection" as a parameter // we only have "Connection" as a parameter
@ -261,7 +286,7 @@ object RmiUtils {
} else { } else {
var found = true var found = true
for (k in 1 until implLength) { for (k in 1 until implLength) {
if (origTypes[k - 1] != implTypes[k]) { if (origTypes[k - 1] != implParameters[k]) {
// make sure all the parameters match. Cannot use arrays.equals(*), because one will have "Connection" as // make sure all the parameters match. Cannot use arrays.equals(*), because one will have "Connection" as
// a parameter - so we check that the rest match // a parameter - so we check that the rest match
found = false found = false