diff --git a/ts-core/ts-proxy/TestClass.class b/ts-core/ts-proxy/TestClass.class new file mode 100644 index 0000000..7c859f2 Binary files /dev/null and b/ts-core/ts-proxy/TestClass.class differ diff --git a/ts-core/ts-proxy/TestClass2.class b/ts-core/ts-proxy/TestClass2.class new file mode 100644 index 0000000..ccbb7f9 Binary files /dev/null and b/ts-core/ts-proxy/TestClass2.class differ diff --git a/ts-core/ts-proxy/TestClass_InvocationHandler.class b/ts-core/ts-proxy/TestClass_InvocationHandler.class new file mode 100644 index 0000000..8a6534a Binary files /dev/null and b/ts-core/ts-proxy/TestClass_InvocationHandler.class differ diff --git a/ts-core/ts-proxy/TestClass_MethodAccess.class b/ts-core/ts-proxy/TestClass_MethodAccess.class new file mode 100644 index 0000000..d66e971 Binary files /dev/null and b/ts-core/ts-proxy/TestClass_MethodAccess.class differ diff --git a/ts-core/ts-proxy/a b/ts-core/ts-proxy/a new file mode 100644 index 0000000..e69de29 diff --git a/ts-core/ts-proxy/build.gradle.kts b/ts-core/ts-proxy/build.gradle.kts index 684afd9..64b88ec 100644 --- a/ts-core/ts-proxy/build.gradle.kts +++ b/ts-core/ts-proxy/build.gradle.kts @@ -11,6 +11,7 @@ dependencies { api(project(":ts-core")) api(project(":ts-core:ts-reflectasm")) api(group = "cglib", name = "cglib", version = "3.3.0") + implementation(group = "net.bytebuddy", name = "byte-buddy", version = "1.12.22") implementation(group = "org.apache.commons", name = "commons-lang3", version = "3.8.1") testApi(group = "junit", name = "junit", version = "4.13.2") } diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ListProxyContainer.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ListProxyContainer.kt deleted file mode 100644 index 173a541..0000000 --- a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ListProxyContainer.kt +++ /dev/null @@ -1,32 +0,0 @@ -package cn.tursom.proxy - -import cn.tursom.core.context.Context - - -class ListProxyContainer( - private val proxyList: MutableCollection = ArrayList(), -) : MutableProxyContainer { - override var lastModify: Long = System.currentTimeMillis() - private set - override val context: Context = ProxyContainer.contextEnv.newContext() - - override fun addProxy(proxy: ProxyMethod) { - lastModify = System.currentTimeMillis() - proxyList.add(proxy) - } - - override fun addAllProxy(proxy: Collection?): Boolean { - lastModify = System.currentTimeMillis() - return proxyList.addAll(proxy!!) - } - - override fun removeProxy(proxy: ProxyMethod) { - lastModify = System.currentTimeMillis() - proxyList.remove(proxy) - } - - override fun iterator(): MutableIterator { - lastModify = System.currentTimeMillis() - return proxyList.iterator() - } -} diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/MutableProxyContainer.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/MutableProxyContainer.kt deleted file mode 100644 index 444d5be..0000000 --- a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/MutableProxyContainer.kt +++ /dev/null @@ -1,7 +0,0 @@ -package cn.tursom.proxy - -interface MutableProxyContainer : ProxyContainer { - fun addProxy(proxy: ProxyMethod) - fun addAllProxy(proxy: Collection?): Boolean - fun removeProxy(proxy: ProxyMethod) -} \ No newline at end of file diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/Proxy.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/Proxy.kt index 23b9e61..30ab0f0 100644 --- a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/Proxy.kt +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/Proxy.kt @@ -1,24 +1,27 @@ package cn.tursom.proxy +import cn.tursom.core.allMethodsSequence +import cn.tursom.core.isPrivate import cn.tursom.core.uncheckedCast -import net.sf.cglib.proxy.Enhancer -import net.sf.cglib.proxy.Factory +import cn.tursom.proxy.container.ListProxyContainer +import cn.tursom.proxy.container.MutableProxyContainer +import cn.tursom.proxy.container.ProxyContainer +import cn.tursom.proxy.function.ProxyMethod +import cn.tursom.proxy.interceptor.LocalCachedProxyInterceptor +import cn.tursom.proxy.interceptor.ProxyInterceptor +import cn.tursom.reflect.final +import net.sf.cglib.proxy.* import java.util.concurrent.ConcurrentHashMap object Proxy { - object CallSuperException : Exception() - - val callSuper = object : ThreadLocal() { - override fun initialValue(): Boolean = throw CallSuperException - override fun get(): Boolean = try { - super.get() - } catch (_: CallSuperException) { - false - } - } - var defaultContainer: () -> MutableProxyContainer = { ListProxyContainer() } private val cache = ConcurrentHashMap, Class<*>>() + val directAccessorKey = ProxyContainer.ctxEnv.newKey() + + private val methodProxyFieldSignature = MethodProxy::class.java.getDeclaredField("sig1").also { + it.isAccessible = true + it.final = false + } fun getContainer(obj: Any): ProxyContainer? { if (obj !is Factory) return null @@ -38,18 +41,88 @@ object Proxy { builder: (Class) -> T, ): Pair { val target = getCachedTarget(clazz) + + val directAccessor = builder(target) val obj = builder(target) - injectCallback(obj as Factory, container) + + container.target = obj + container.ctx[directAccessorKey] = directAccessor + + injectCallback(obj as Factory, container, directAccessor) + return obj to container } inline fun get() = get(T::class.java) - inline fun get( + inline operator fun get( argumentTypes: Array>, arguments: Array, container: MutableProxyContainer = defaultContainer(), ) = get(T::class.java, argumentTypes, arguments, container) + inline operator fun get( + a1: A1, + container: MutableProxyContainer = defaultContainer(), + ) = get(arrayOf(A1::class.java), arrayOf(a1), container) + + inline operator fun < + reified T : Any, + reified A1 : Any?, + reified A2 : Any?, + > get( + a1: A1, a2: A2, + container: MutableProxyContainer = defaultContainer(), + ) = get( + arrayOf(A1::class.java, A2::class.java), + arrayOf(a1, a2), + container, + ) + + inline operator fun < + reified T : Any, + reified A1 : Any?, + reified A2 : Any?, + reified A3 : Any?, + > get( + a1: A1, a2: A2, a3: A3, + container: MutableProxyContainer = defaultContainer(), + ) = get( + arrayOf(A1::class.java, A2::class.java, A3::class.java), + arrayOf(a1, a2, a3), + container, + ) + + inline operator fun < + reified T : Any, + reified A1 : Any?, + reified A2 : Any?, + reified A3 : Any?, + reified A4 : Any?, + > get( + a1: A1, a2: A2, a3: A3, a4: A4, + container: MutableProxyContainer = defaultContainer(), + ) = get( + arrayOf(A1::class.java, A2::class.java, A3::class.java, A4::class.java), + arrayOf(a1, a2, a3, a4), + container, + ) + + inline operator fun < + reified T : Any, + reified A1 : Any?, + reified A2 : Any?, + reified A3 : Any?, + reified A4 : Any?, + reified A5 : Any?, + > get( + a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, + container: MutableProxyContainer = defaultContainer(), + ) = get( + arrayOf(A1::class.java, A2::class.java, A3::class.java, A4::class.java, A5::class.java), + arrayOf(a1, a2, a3, a4, a5), + container, + ) + operator fun get(clazz: Class, container: MutableProxyContainer = defaultContainer()) = get(clazz, container, Class::newInstance) @@ -68,23 +141,56 @@ object Proxy { if (interfaces.isNotEmpty()) { enhancer.setInterfaces(interfaces) } - enhancer.setCallbackType(ProxyInterceptor::class.java) - enhancer.setCallbackFilter { 0 } + + val methods = clazz.allMethodsSequence.filter { !it.isPrivate() }.toList() + + enhancer.setCallbackTypes(Array(methods.size) { MethodInterceptor::class.java }) + enhancer.setCallbackFilter(methods::indexOf) + return enhancer } @JvmOverloads - fun injectCallback(obj: Any, container: ProxyContainer = defaultContainer()): ProxyContainer { + fun injectCallback(obj: Any, container: ProxyContainer = defaultContainer(), target: Any = obj): ProxyContainer { obj as Factory - if (obj.getCallback(0) != null && obj.getCallback(0) != ProxyDispatcher) { + if (obj.getCallback(0) != null && obj.getCallback(0) is ProxyInterceptor) { return (obj.getCallback(0) as ProxyInterceptor).container } - obj.setCallback(0, ProxyInterceptor(container)) + val nonProxyClasses: MutableSet> = HashSet(listOf(Any::class.java)) + repeat(obj.callbacks.size) { + obj.setCallback(it, LocalCachedProxyInterceptor(container, nonProxyClasses, target)) + } return container } fun getCachedTarget(clazz: Class): Class = cache.computeIfAbsent(clazz) { newEnhancer(clazz).createClass() }.uncheckedCast() + + fun getSuperCaller( + obj: T, + ): T = getContainer(obj)?.ctx?.get(directAccessorKey).uncheckedCast() + + fun addNonProxyClass(target: Any, nonProxyClass: Class<*>): Boolean { + if (target !is Factory || + target.getCallback(0) == null || + target.getCallback(0) !is ProxyInterceptor + ) { + throw IllegalArgumentException() + } + + return (target.getCallback(0) as ProxyInterceptor).nonProxyClasses.add(nonProxyClass) + } + + fun removeNonProxyClass(target: Any, nonProxyClass: Class<*>): Boolean { + if (target !is Factory || + target.getCallback(0) == null || + target.getCallback(0) !is ProxyInterceptor + ) { + throw IllegalArgumentException() + } + + return (target.getCallback(0) as ProxyInterceptor).nonProxyClasses.remove(nonProxyClass) + } } diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyContainer.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyContainer.kt deleted file mode 100644 index 1d0f0a4..0000000 --- a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyContainer.kt +++ /dev/null @@ -1,30 +0,0 @@ -package cn.tursom.proxy - -import cn.tursom.core.context.ArrayContextEnv -import cn.tursom.core.context.Context - -interface ProxyContainer : Iterable { - companion object { - val contextEnv = ArrayContextEnv() - - inline fun ProxyContainer.forEachProxy(action: (ProxyMethod) -> Unit) { - for (t in this) { - action(t) - } - } - - inline fun ProxyContainer.forFirstProxy(action: (ProxyMethod) -> ProxyResult): ProxyResult { - for (t in this) { - val result = action(t) - if (result.success) { - return result - } - } - return ProxyResult.failed() - } - } - - // to impl, use override val context: Context = ProxyContainer.contextEnv.newContext() - val context: Context - val lastModify: Long -} \ No newline at end of file diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyDispatcher.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyDispatcher.kt deleted file mode 100644 index eec44db..0000000 --- a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyDispatcher.kt +++ /dev/null @@ -1,12 +0,0 @@ -package cn.tursom.proxy - -import net.sf.cglib.proxy.MethodInterceptor -import net.sf.cglib.proxy.MethodProxy -import java.lang.reflect.Method - -object ProxyDispatcher : MethodInterceptor { - override fun intercept(obj: Any, method: Method, args: Array, proxy: MethodProxy): Any { - Proxy.injectCallback(obj) - return proxy(obj, args) - } -} \ No newline at end of file diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyInterceptor.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyInterceptor.kt deleted file mode 100644 index 9adb1ff..0000000 --- a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyInterceptor.kt +++ /dev/null @@ -1,25 +0,0 @@ -package cn.tursom.proxy - -import net.sf.cglib.proxy.MethodInterceptor -import net.sf.cglib.proxy.MethodProxy -import java.lang.reflect.Method - -class ProxyInterceptor( - val container: ProxyContainer = ListProxyContainer(), - // disable call super can save 20% time - private val supportCallSuper: Boolean = true, -) : MethodInterceptor { - override fun intercept(obj: Any, method: Method, args: Array, proxy: MethodProxy): Any? { - if (supportCallSuper && Proxy.callSuper.get()) { - Proxy.callSuper.remove() - return proxy.invokeSuper(obj, args) - } - - val result = ProxyRunner.onProxy(obj, container, method, args, proxy) - return if (result.success) { - result.result - } else { - proxy.invokeSuper(obj, args) - } - } -} diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyMethod.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyMethod.kt deleted file mode 100644 index bcfcc6d..0000000 --- a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyMethod.kt +++ /dev/null @@ -1,150 +0,0 @@ -package cn.tursom.proxy - -import cn.tursom.proxy.annotation.ForEachProxy -import cn.tursom.proxy.annotation.ForFirstProxy -import cn.tursom.reflect.asm.ReflectAsmUtils -import com.esotericsoftware.reflectasm.MethodAccess -import net.sf.cglib.proxy.MethodProxy -import java.lang.reflect.Method -import java.util.concurrent.ConcurrentHashMap - -internal typealias ProxyMethodHandler = (proxy: ProxyMethod, args: Array) -> ProxyResult<*> - -interface ProxyMethod { - @Throws(Throwable::class) - fun onProxy( - ignored: Any, - c: ProxyContainer, - method: Method, - args: Array, - proxy: MethodProxy - ): ProxyResult<*> { - val handlerCacheMap = getHandlerCacheMap(javaClass) - val handlerCache = handlerCacheMap[method] - if (handlerCache != null) { - return if (handlerCache.success) { - handlerCache.result(this, args) - } else { - ProxyResult.failed() - } - } - - val reflectAsmHandler = ReflectASMProxyMethodInvoker[this, method, handlerCacheMap] - if (reflectAsmHandler != null) { - return reflectAsmHandler(this, args) - } - - val javaReflectHandler = JavaReflectProxyMethodInvoker[this, method, handlerCacheMap] - if (javaReflectHandler != null) { - javaReflectHandler(this, args) - } - - handlerCacheMap[method] = ProxyResult.failed() - return ProxyResult.failed() - } - - companion object { - private val handlerCacheMapMap: MutableMap< - Class, - MutableMap> - > = HashMap() - - fun getHandlerCacheMap(type: Class): MutableMap> { - var handlerCacheMap = handlerCacheMapMap[type] - if (handlerCacheMap == null) synchronized(handlerCacheMapMap) { - handlerCacheMap = handlerCacheMapMap[type] - if (handlerCacheMap == null) { - handlerCacheMap = ConcurrentHashMap() - handlerCacheMapMap[type] = handlerCacheMap!! - } - } - return handlerCacheMap!! - } - } -} - -private class ReflectASMProxyMethodInvoker( - val methodAccess: MethodAccess, - val index: Int, -) : ProxyMethodHandler { - companion object { - private val fastInvoker: MethodAccess.(Any, Int, Array) -> Any? = MethodAccess::invoke - - operator fun get( - proxy: ProxyMethod, - method: Method, - handlerCacheMap: MutableMap> - ): ProxyMethodHandler? { - val reflectAsmMethod = try { - ReflectAsmUtils.getMethod( - proxy.javaClass, - method.name, - paramTypes = method.parameterTypes, - returnType = method.returnType, - ) - } catch (e: Exception) { - e.printStackTrace() - null - } - if (reflectAsmMethod != null) { - val (methodAccess, index) = reflectAsmMethod - val invoker = ReflectASMProxyMethodInvoker(methodAccess, index) - handlerCacheMap[method] = ProxyResult.of(invoker) - return invoker - } - - return null - } - } - - override fun invoke(proxy: ProxyMethod, args: Array): ProxyResult<*> { - return ProxyResult.of(fastInvoker(methodAccess, proxy, index, args)) - } -} - -private class JavaReflectProxyMethodInvoker( - val method: Method, -) : ProxyMethodHandler { - companion object { - private val fastInvoker: Method.(Any, Array) -> Any? = Method::invoke - - operator fun get( - proxy: ProxyMethod, - method: Method, - handlerCacheMap: MutableMap> - ): ProxyMethodHandler? { - var invoker: ProxyMethodHandler? = null - - val selfMethod: Method - try { - var methodName = method.name - for (annotation in method.annotations) { - if (annotation is ForEachProxy) { - if (annotation.name.isNotEmpty()) { - methodName = annotation.name - break - } - } else if (annotation is ForFirstProxy) { - if (annotation.name.isNotEmpty()) { - methodName = annotation.name - break - } - } - } - selfMethod = proxy.javaClass.getMethod(methodName, *method.parameterTypes) - selfMethod.isAccessible = true - - invoker = JavaReflectProxyMethodInvoker(method) - - handlerCacheMap[method] = ProxyResult(invoker, true) - } catch (_: Exception) { - } - - return invoker - } - } - - override fun invoke(proxy: ProxyMethod, args: Array): ProxyResult<*> { - return ProxyResult.of(fastInvoker(method, proxy, args)) - } -} diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyMethodCache.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyMethodCache.kt deleted file mode 100644 index bdb72b5..0000000 --- a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyMethodCache.kt +++ /dev/null @@ -1,45 +0,0 @@ -package cn.tursom.proxy - -import net.sf.cglib.proxy.MethodProxy -import java.lang.reflect.Method -import java.util.concurrent.ConcurrentHashMap - -typealias ProxyMethodCacheFunction = ( - obj: Any, - c: ProxyContainer, - method: Method, - args: Array, - proxy: MethodProxy -) -> ProxyResult<*> - -class ProxyMethodCache( - private var lastModify: Long = 0, -) { - companion object { - val failed: ProxyMethodCacheFunction = { _, _, _, _, _ -> ProxyResult.failed } - } - - private val functionMap = ConcurrentHashMap() - - fun update(lastModify: Long, proxy: MethodProxy, function: ProxyMethodCacheFunction = functionMap[proxy] ?: failed) { - if (this.lastModify != lastModify) { - functionMap.clear() - } - - this.lastModify = lastModify - functionMap[proxy] = function - } - - operator fun invoke( - lastModify: Long, - obj: Any, - c: ProxyContainer, - method: Method, - args: Array, - proxy: MethodProxy, - ): ProxyResult<*>? = if (lastModify != this.lastModify) { - null - } else { - functionMap[proxy]?.invoke(obj, c, method, args, proxy) - } -} \ No newline at end of file diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyResult.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyResult.kt deleted file mode 100644 index 3c46557..0000000 --- a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyResult.kt +++ /dev/null @@ -1,22 +0,0 @@ -package cn.tursom.proxy - -import cn.tursom.core.uncheckedCast - -data class ProxyResult( - val result: R, - val success: Boolean = false, -) { - companion object { - val success: ProxyResult<*> = ProxyResult(null, true) - val failed: ProxyResult<*> = ProxyResult(null, false) - - fun of(): ProxyResult = success.uncheckedCast() - fun of(result: R): ProxyResult = if (result == null) { - success.uncheckedCast() - } else { - ProxyResult(result, true) - } - - fun failed(): ProxyResult = failed.uncheckedCast() - } -} diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyRunner.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyRunner.kt deleted file mode 100644 index d439896..0000000 --- a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/ProxyRunner.kt +++ /dev/null @@ -1,148 +0,0 @@ -package cn.tursom.proxy - -import cn.tursom.proxy.ProxyContainer.Companion.forEachProxy -import cn.tursom.proxy.ProxyContainer.Companion.forFirstProxy -import cn.tursom.proxy.annotation.ForEachProxy -import cn.tursom.proxy.annotation.ForFirstProxy -import net.sf.cglib.proxy.MethodProxy -import org.apache.commons.lang3.StringUtils -import java.lang.reflect.Method -import java.util.* - - -object ProxyRunner { - private val errMsgSearchList = arrayOf("%M", "%B", "%A") - private val proxyMethodCacheKey = ProxyContainer.contextEnv.newKey() - .withDefault { ProxyMethodCache() } - - /** - * will be call when proxy method invoke. - * 在代理方法被调用时,该方法会被调用 - */ - @Throws(Throwable::class) - fun onProxy(obj: Any, c: ProxyContainer, method: Method, args: Array, proxy: MethodProxy): ProxyResult<*> { - val cache = c.context[proxyMethodCacheKey] - cache(c.lastModify, obj, c, method, args, proxy)?.let { - return it - } - - var handler: ProxyMethodCacheFunction? = null - for (annotation in method.annotations) when (annotation) { - is ForEachProxy -> { - handler = onForeachProxy(annotation) - break - } - is ForFirstProxy -> { - handler = onForFirstProxy(annotation) - break - } - } - if (handler == null) { - //handler = ProxyContainerHandlerCache.callSuper - handler = onForFirstProxy(ForFirstProxy.EMPTY) - } - return handler(obj, c, method, args, proxy) - } - - fun onForFirstProxy(forFirstProxy: ForFirstProxy): ProxyMethodCacheFunction { - val classes = when (forFirstProxy.value.size) { - 0 -> emptyList() - 1 -> listOf(forFirstProxy.value[0].java) - else -> forFirstProxy.value.asSequence().map { it.java }.toSet() - } - return { o, c, m: Method, a, p -> - onForFirstProxy(o, c, m, a, p, forFirstProxy, classes) - } - } - - private fun onForFirstProxy( - obj: Any, - container: ProxyContainer, - method: Method, - args: Array, - proxy: MethodProxy, - forFirstProxy: ForFirstProxy, - classes: Collection>, - ): ProxyResult<*> { - val result = container.forFirstProxy { p -> - if (classes.isEmpty() || classes.stream().anyMatch { c: Class<*> -> c.isInstance(p) }) { - val result = p.onProxy(obj, container, method, args, proxy) - if (forFirstProxy.cache && result.success) { - container.context[proxyMethodCacheKey].update(container.lastModify, proxy, p::onProxy) - } - return@forFirstProxy result - } else { - return@forFirstProxy ProxyResult.failed - } - } - if (result.success) { - return result - } - - // when request not handled - if (forFirstProxy.must) { - // generate error message - var errMsg: String = forFirstProxy.errMsg - if (errMsg.isBlank()) { - errMsg = "no proxy handled on method %M" - } - val replacementList = arrayOfNulls(errMsgSearchList.size) - // todo use efficient contains - if (errMsg.contains(errMsgSearchList[0])) { - replacementList[0] = method.toString() - } - if (errMsg.contains(errMsgSearchList[1])) { - replacementList[1] = obj.toString() - } - if (errMsg.contains(errMsgSearchList[2])) { - replacementList[2] = Arrays.toString(args) - } - - errMsg = StringUtils.replaceEach(errMsg, errMsgSearchList, replacementList) - val exceptionConstructor = forFirstProxy.errClass.java.getConstructor(String::class.java) - if (forFirstProxy.cache) { - container.context[proxyMethodCacheKey].update(container.lastModify, proxy) { _, _, _, _, _ -> - throw exceptionConstructor.newInstance(errMsg) - } - } - throw exceptionConstructor.newInstance(errMsg) - } - if (forFirstProxy.cache) { - container.context[proxyMethodCacheKey].update(container.lastModify, proxy) - } - return ProxyResult.failed - } - - fun onForeachProxy(forEachProxy: ForEachProxy) = onForeachProxy(when (forEachProxy.value.size) { - 0 -> emptyList() - 1 -> listOf(forEachProxy.value[0].java) - else -> forEachProxy.value.asSequence().map { it.java }.toSet() - }, forEachProxy.cache) - - private val emptyProxyList = ArrayList() - private fun onForeachProxy( - classes: Collection>, - cache: Boolean, - ): ProxyMethodCacheFunction = { o, c, m, a, proxy -> - val proxyList = if (cache) ArrayList() else emptyProxyList - c.forEachProxy { p -> - if (classes.isEmpty() || classes.any { c: Class<*> -> c.isInstance(p) }) { - val result = p.onProxy(o, c, m, a, proxy) - if (cache && result.success) { - proxyList.add(p) - } - } - } - if (cache) { - c.context[proxyMethodCacheKey].update(c.lastModify, proxy, onCachedForeachProxy((proxyList))) - } - ProxyResult.failed() - } - - private fun onCachedForeachProxy(proxyList: List): ProxyMethodCacheFunction = { o, c, m, a, proxy -> - proxyList.forEach { p -> - p.onProxy(o, c, m, a, proxy) - } - ProxyResult.failed() - } -} diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/annotation/ForFirstProxy.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/annotation/ForFirstProxy.kt index 36122ce..355d26e 100644 --- a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/annotation/ForFirstProxy.kt +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/annotation/ForFirstProxy.kt @@ -13,7 +13,7 @@ annotation class ForFirstProxy( */ val must: Boolean = false, val errMsg: String = "", - val errClass: KClass = RuntimeException::class, + val errClass: KClass = Throwable::class, val cache: Boolean = true, ) { companion object { diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/container/ListProxyContainer.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/container/ListProxyContainer.kt new file mode 100644 index 0000000..5707c58 --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/container/ListProxyContainer.kt @@ -0,0 +1,54 @@ +package cn.tursom.proxy.container + +import cn.tursom.core.context.Context +import cn.tursom.proxy.function.ProxyMethod +import cn.tursom.proxy.util.CglibUtil +import net.sf.cglib.proxy.MethodProxy + + +class ListProxyContainer( + private val proxyList: MutableCollection = ArrayList(), +) : MutableProxyContainer { + override lateinit var target: Any + override val ctx: Context = ProxyContainer.ctxEnv.newContext() + + private fun clearCache() { + ctx[ProxyMethodCache.ctxKey].clear() + } + + override fun addProxy(proxy: Any) { + clearCache() + + proxyList.add(proxy) + if (proxy is ProxyMethod) { + proxy.onProxyAdded(this) + } + + clearCache() + } + + override fun addAllProxy(proxy: Collection?) { + clearCache() + + if (proxyList.addAll(proxy!!)) proxy.forEach { + if (it !is ProxyMethod) return@forEach + it.onProxyAdded(this) + } + + clearCache() + } + + override fun removeProxy(proxy: Any) { + clearCache() + + if (proxyList.remove(proxy) && proxy is ProxyMethod) { + proxy.onProxyRemoved(this) + } + + clearCache() + } + + override fun iterator(): Iterator { + return proxyList.iterator() + } +} diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/container/MutableProxyContainer.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/container/MutableProxyContainer.kt new file mode 100644 index 0000000..2c87c43 --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/container/MutableProxyContainer.kt @@ -0,0 +1,9 @@ +package cn.tursom.proxy.container + +interface MutableProxyContainer : ProxyContainer { + override var target: Any + + fun addProxy(proxy: Any) + fun addAllProxy(proxy: Collection?) + fun removeProxy(proxy: Any) +} \ No newline at end of file diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/container/ProxyContainer.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/container/ProxyContainer.kt new file mode 100644 index 0000000..75a8641 --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/container/ProxyContainer.kt @@ -0,0 +1,14 @@ +package cn.tursom.proxy.container + +import cn.tursom.core.context.ArrayContextEnv +import cn.tursom.core.context.Context + +interface ProxyContainer : Iterable { + companion object { + val ctxEnv = ArrayContextEnv() + } + + // to impl, use override val context: Context = ProxyContainer.contextEnv.newContext() + val ctx: Context + val target: Any +} \ No newline at end of file diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/container/ProxyMethodCache.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/container/ProxyMethodCache.kt new file mode 100644 index 0000000..ced0d22 --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/container/ProxyMethodCache.kt @@ -0,0 +1,28 @@ +package cn.tursom.proxy.container + +import cn.tursom.proxy.util.IntMap +import net.sf.cglib.proxy.MethodProxy + +class ProxyMethodCache { + companion object { + val ctxKey = ProxyContainer.ctxEnv.newKey() + .withDefault { ProxyMethodCache() } + } + + //private val functionMap = HashMap() + private val functionMap = IntMap() + + fun clear() { + synchronized(functionMap) { + functionMap.clear() + } + } + + fun update(proxy: MethodProxy, function: ProxyMethodCacheFunction) { + synchronized(this) { + functionMap[proxy.superIndex] = function + } + } + + operator fun get(proxy: MethodProxy) = functionMap[proxy.superIndex] +} diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/container/ProxyMethodCacheFunction.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/container/ProxyMethodCacheFunction.kt new file mode 100644 index 0000000..e5c9fbd --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/container/ProxyMethodCacheFunction.kt @@ -0,0 +1,13 @@ +package cn.tursom.proxy.container + +import cn.tursom.proxy.container.ProxyContainer +import net.sf.cglib.proxy.MethodProxy +import java.lang.reflect.Method + +fun interface ProxyMethodCacheFunction : ( + Any?, + ProxyContainer, + Method?, + Array?, + MethodProxy?, +) -> Any? diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/CachedOnForEachProxyImpl.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/CachedOnForEachProxyImpl.kt new file mode 100644 index 0000000..1e56739 --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/CachedOnForEachProxyImpl.kt @@ -0,0 +1,25 @@ +package cn.tursom.proxy.function + +import cn.tursom.proxy.container.ProxyContainer +import cn.tursom.proxy.container.ProxyMethodCacheFunction +import net.sf.cglib.proxy.MethodProxy +import java.lang.reflect.Method + +class CachedOnForEachProxyImpl( + private val proxyList: List, +) : ProxyMethodCacheFunction { + override fun invoke( + o: Any?, + c: ProxyContainer, + m: Method?, + a: Array?, + proxy: MethodProxy?, + ): Any? { + proxy!! + + proxyList.forEach { p -> + p(o, c, m, a, proxy) + } + return proxy.invokeSuper(o, a) + } +} \ No newline at end of file diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/CallSuperProxyMethodCacheFunction.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/CallSuperProxyMethodCacheFunction.kt new file mode 100644 index 0000000..db4181e --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/CallSuperProxyMethodCacheFunction.kt @@ -0,0 +1,18 @@ +package cn.tursom.proxy.function + +import cn.tursom.proxy.container.ProxyContainer +import cn.tursom.proxy.container.ProxyMethodCacheFunction +import net.sf.cglib.proxy.MethodProxy +import java.lang.reflect.Method + +object CallSuperProxyMethodCacheFunction : ProxyMethodCacheFunction { + override fun invoke( + obj: Any?, + c: ProxyContainer, + method: Method?, + args: Array?, + proxy: MethodProxy?, + ): Any? { + return proxy!!.invokeSuper(obj, args) + } +} \ No newline at end of file diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/JavaReflectProxyMethodInvoker.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/JavaReflectProxyMethodInvoker.kt new file mode 100644 index 0000000..1ae879c --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/JavaReflectProxyMethodInvoker.kt @@ -0,0 +1,61 @@ +package cn.tursom.proxy.function + +import cn.tursom.proxy.annotation.ForEachProxy +import cn.tursom.proxy.annotation.ForFirstProxy +import cn.tursom.proxy.container.ProxyContainer +import cn.tursom.proxy.container.ProxyMethodCacheFunction +import net.sf.cglib.proxy.MethodProxy +import java.lang.reflect.Method + +internal class JavaReflectProxyMethodInvoker( + private val self: Any, + private val method: Method, +) : ProxyMethodCacheFunction { + companion object { + private val fastInvoker: Method.(Any?, Array?) -> Any? = Method::invoke + + operator fun get( + proxy: Any, + method: Method, + ): JavaReflectProxyMethodInvoker? { + var invoker: JavaReflectProxyMethodInvoker? = null + + val selfMethod: Method + try { + var methodName = method.name + for (annotation in method.annotations) { + if (annotation is ForEachProxy) { + if (annotation.name.isNotEmpty()) { + methodName = annotation.name + break + } + } else if (annotation is ForFirstProxy) { + if (annotation.name.isNotEmpty()) { + methodName = annotation.name + break + } + } + } + selfMethod = proxy.javaClass.getMethod(methodName, *method.parameterTypes) + selfMethod.isAccessible = true + + invoker = JavaReflectProxyMethodInvoker(proxy, method) + + //handlerCacheMap[method] = ProxyResult(invoker, true) + } catch (_: Exception) { + } + + return invoker + } + } + + override fun invoke( + obj: Any?, + c: ProxyContainer, + method: Method?, + args: Array?, + proxy: MethodProxy?, + ): Any? { + return fastInvoker(this.method, self, args) + } +} diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/OnForEachProxyImpl.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/OnForEachProxyImpl.kt new file mode 100644 index 0000000..7e9958c --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/OnForEachProxyImpl.kt @@ -0,0 +1,52 @@ +package cn.tursom.proxy.function + +import cn.tursom.proxy.annotation.ForEachProxy +import cn.tursom.proxy.container.ProxyContainer +import cn.tursom.proxy.container.ProxyMethodCache +import cn.tursom.proxy.container.ProxyMethodCacheFunction +import net.sf.cglib.proxy.MethodProxy +import java.lang.reflect.Method + +class OnForEachProxyImpl( + forEachProxy: ForEachProxy, +) : ProxyMethodCacheFunction { + companion object { + private val emptyProxyList = ArrayList() + } + + private val classes: Collection> = when (forEachProxy.value.size) { + 0 -> emptyList() + 1 -> listOf(forEachProxy.value[0].java) + else -> forEachProxy.value.asSequence().map { it.java }.toSet() + } + + private val cache: Boolean = forEachProxy.cache + + override fun invoke( + o: Any?, + c: ProxyContainer, + m: Method?, + a: Array?, + proxy: MethodProxy?, + ): Any? { + m!! + proxy!! + + val proxyList = if (cache) ArrayList() else emptyProxyList + c.forEach { p -> + if (classes.isEmpty() || classes.any { c: Class<*> -> c.isInstance(p) }) { + val handler = ProxyMethod.getHandler(p, m) ?: return@forEach + handler(o, c, m, a, proxy) + if (cache) { + proxyList.add(handler) + } + } + } + if (cache) { + c.ctx[ProxyMethodCache.ctxKey].update( + proxy, CachedOnForEachProxyImpl(proxyList) + ) + } + return proxy.invokeSuper(o, a) + } +} \ No newline at end of file diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/OnForFirstProxyImpl.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/OnForFirstProxyImpl.kt new file mode 100644 index 0000000..4f1b716 --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/OnForFirstProxyImpl.kt @@ -0,0 +1,97 @@ +package cn.tursom.proxy.function + +import cn.tursom.proxy.annotation.ForFirstProxy +import cn.tursom.proxy.container.ProxyContainer +import cn.tursom.proxy.container.ProxyMethodCache +import cn.tursom.proxy.container.ProxyMethodCacheFunction +import net.sf.cglib.proxy.MethodProxy +import org.apache.commons.lang3.StringUtils +import java.lang.reflect.Constructor +import java.lang.reflect.Method +import java.util.* + +class OnForFirstProxyImpl( + private val ffpAnnotation: ForFirstProxy, +) : ProxyMethodCacheFunction { + companion object { + private val errMsgSearchList = arrayOf("%M", "%B", "%A") + } + + private val classes: Collection> = when (ffpAnnotation.value.size) { + 0 -> emptyList() + 1 -> listOf(ffpAnnotation.value[0].java) + else -> ffpAnnotation.value.asSequence().map { it.java }.toSet() + } + + override fun invoke( + obj: Any?, + container: ProxyContainer, + method: Method?, + args: Array?, + proxy: MethodProxy?, + ): Any? { + method!! + proxy!! + + container.forEach { p -> + if (classes.isNotEmpty() && classes.none { c: Class<*> -> c.isInstance(p) }) { + return@forEach + } + + val handler = ProxyMethod.getHandler(p, method) ?: return@forEach + if (ffpAnnotation.cache) { + container.ctx[ProxyMethodCache.ctxKey].update(proxy, handler) + } + return handler(obj, container, method, args, proxy) + } + + // when request not handled + if (ffpAnnotation.must) { + // generate error message + var errMsg: String = ffpAnnotation.errMsg + if (errMsg.isBlank()) { + errMsg = "no proxy handled on method %M" + } + val replacementList = arrayOfNulls(errMsgSearchList.size) + // todo use efficient contains + if (errMsg.contains(errMsgSearchList[0])) { + replacementList[0] = method.toString() + } + if (errMsg.contains(errMsgSearchList[1])) { + replacementList[1] = obj.toString() + } + if (errMsg.contains(errMsgSearchList[2])) { + replacementList[2] = Arrays.toString(args) + } + + errMsg = StringUtils.replaceEach(errMsg, errMsgSearchList, replacementList) + val exceptionConstructor = ffpAnnotation.errClass.java.getConstructor(String::class.java) + if (ffpAnnotation.cache) { + container.ctx[ProxyMethodCache.ctxKey].update( + proxy, ExceptionProxyMethodCacheFunctionImpl(exceptionConstructor, errMsg) + ) + } + throw exceptionConstructor.newInstance(errMsg) + } + if (ffpAnnotation.cache) { + container.ctx[ProxyMethodCache.ctxKey].update( + proxy, + CallSuperProxyMethodCacheFunction, + ) + } + return proxy.invokeSuper(obj, args) + } +} + +private class ExceptionProxyMethodCacheFunctionImpl( + private val exceptionConstructor: Constructor, + private val errMsg: String, +) : ProxyMethodCacheFunction { + override fun invoke( + obj: Any?, + c: ProxyContainer, + method: Method?, + args: Array?, + proxy: MethodProxy?, + ) = throw exceptionConstructor.newInstance(errMsg) +} diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/ProxyMethod.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/ProxyMethod.kt new file mode 100644 index 0000000..de873bd --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/ProxyMethod.kt @@ -0,0 +1,53 @@ +package cn.tursom.proxy.function + +import cn.tursom.proxy.container.ProxyContainer +import cn.tursom.proxy.container.ProxyMethodCacheFunction +import net.sf.cglib.proxy.MethodProxy +import java.lang.reflect.Method + +interface ProxyMethod { + fun onProxyAdded(container: ProxyContainer) { + // NO-OP + } + + fun onProxyRemoved(container: ProxyContainer) { + // NO-OP + } + + fun onProxyInvoke( + o: Any?, + c: ProxyContainer, + m: Method?, + a: Array?, + proxy: MethodProxy?, + next: ProxyMethodCacheFunction, + ): Any? { + return next(o, c, m, a, proxy) + } + + companion object { + fun getHandler(proxy: Any, method: Method): ProxyMethodCacheFunction? { + var handler = getReflectHandler(proxy, method) ?: return null + + if (proxy is ProxyMethod) { + handler = ProxyMethodInvoker(proxy, handler) + } + + return handler + } + + private fun getReflectHandler(proxy: Any, method: Method): ProxyMethodCacheFunction? { + val reflectAsmHandler = ReflectASMProxyMethodInvoker[proxy, method] + if (reflectAsmHandler != null) { + return reflectAsmHandler + } + + val javaReflectHandler = JavaReflectProxyMethodInvoker[proxy, method] + if (javaReflectHandler != null) { + return javaReflectHandler + } + + return null + } + } +} diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/ProxyMethodInvoker.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/ProxyMethodInvoker.kt new file mode 100644 index 0000000..e25c830 --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/ProxyMethodInvoker.kt @@ -0,0 +1,21 @@ +package cn.tursom.proxy.function + +import cn.tursom.proxy.container.ProxyContainer +import cn.tursom.proxy.container.ProxyMethodCacheFunction +import net.sf.cglib.proxy.MethodProxy +import java.lang.reflect.Method + +internal class ProxyMethodInvoker( + private val proxyMethod: ProxyMethod, + private val next: ProxyMethodCacheFunction, +) : ProxyMethodCacheFunction { + override fun invoke( + obj: Any?, + c: ProxyContainer, + method: Method?, + args: Array?, + proxy: MethodProxy?, + ): Any? { + return proxyMethod.onProxyInvoke(obj, c, method, args, proxy, next) + } +} diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/ReflectASMProxyMethodInvoker.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/ReflectASMProxyMethodInvoker.kt new file mode 100644 index 0000000..a7d202b --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/function/ReflectASMProxyMethodInvoker.kt @@ -0,0 +1,48 @@ +package cn.tursom.proxy.function + +import cn.tursom.proxy.container.ProxyContainer +import cn.tursom.proxy.container.ProxyMethodCacheFunction +import cn.tursom.reflect.asm.ReflectAsmUtils +import com.esotericsoftware.reflectasm.MethodAccess +import net.sf.cglib.proxy.MethodProxy +import java.lang.reflect.Method + +internal class ReflectASMProxyMethodInvoker( + private val self: Any, + private val methodAccess: MethodAccess, + private val index: Int, +) : ProxyMethodCacheFunction { + companion object { + private val fastInvoker: MethodAccess.(Any, Int, Array?) -> Any? = MethodAccess::invoke + + operator fun get( + proxy: Any, + method: Method, + ): ReflectASMProxyMethodInvoker? { + val reflectAsmMethod = try { + ReflectAsmUtils.getMethod( + proxy.javaClass, + method.name, + paramTypes = method.parameterTypes, + returnType = method.returnType, + ) + } catch (e: Exception) { + e.printStackTrace() + null + } ?: return null + + val (methodAccess, index) = reflectAsmMethod + return ReflectASMProxyMethodInvoker(proxy, methodAccess, index) + } + } + + override fun invoke( + obj: Any?, + c: ProxyContainer, + method: Method?, + args: Array?, + proxy: MethodProxy?, + ): Any? { + return fastInvoker(methodAccess, self, index, args) + } +} diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/interceptor/LocalCachedNoProxyInvocationHandler.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/interceptor/LocalCachedNoProxyInvocationHandler.kt new file mode 100644 index 0000000..5808e38 --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/interceptor/LocalCachedNoProxyInvocationHandler.kt @@ -0,0 +1,42 @@ +package cn.tursom.proxy.interceptor + +import cn.tursom.reflect.asm.ReflectAsmUtils +import com.esotericsoftware.reflectasm.MethodAccess +import net.sf.cglib.proxy.InvocationHandler +import java.lang.reflect.Method + +class LocalCachedNoProxyInvocationHandler( + val proxy: Any, +) : InvocationHandler { + companion object { + private val fastInvoker: MethodAccess.(Any, Int, Array?) -> Any? = MethodAccess::invoke + } + + private var handler: (method: Method?, args: Array?) -> Any? = DefaultHandler() + + override fun invoke(ignore: Any, method: Method?, args: Array?): Any? { + return handler(method, args) + } + + private inner class DefaultHandler : (Method?, Array?) -> Any? { + override fun invoke(method: Method?, args: Array?): Any? { + method!! + + val (methodAccess, index) = ReflectAsmUtils.getMethodByRegex( + proxy.javaClass, + "CGLIB\\\$${method.name}\\\$.*".toRegex(), + *method.parameterTypes, + method.returnType, + )!! + handler = MethodAccessHandler(methodAccess, index) + return fastInvoker(methodAccess, proxy, index, args) + } + } + + private inner class MethodAccessHandler( + private val methodAccess: MethodAccess, + private val index: Int, + ) : (Method?, Array?) -> Any? { + override fun invoke(method: Method?, args: Array?) = fastInvoker(methodAccess, proxy, index, args) + } +} \ No newline at end of file diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/interceptor/LocalCachedProxyInterceptor.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/interceptor/LocalCachedProxyInterceptor.kt new file mode 100644 index 0000000..c29d359 --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/interceptor/LocalCachedProxyInterceptor.kt @@ -0,0 +1,33 @@ +package cn.tursom.proxy.interceptor + +import cn.tursom.proxy.container.ProxyContainer +import cn.tursom.proxy.container.ProxyMethodCache +import cn.tursom.proxy.container.ProxyMethodCacheFunction +import net.sf.cglib.proxy.MethodProxy +import java.lang.reflect.Method + +class LocalCachedProxyInterceptor( + container: ProxyContainer, + nonProxyClasses: MutableSet>, + val target: Any, +) : ProxyInterceptor(container, nonProxyClasses) { + private var cache: ProxyMethodCacheFunction? = null + + override fun intercept(obj: Any?, method: Method?, args: Array?, proxy: MethodProxy): Any? { + var cache = this.cache + + if (cache != null) { + return cache(target, container, method, args, proxy) + } + + val methodCache = container.ctx[ProxyMethodCache.ctxKey] + this.cache = methodCache[proxy] + cache = this.cache + + if (cache != null) { + return cache(target, container, method, args, proxy) + } + + return super.intercept(target, method, args, proxy) + } +} diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/interceptor/NoProxyProxyInterceptor.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/interceptor/NoProxyProxyInterceptor.kt new file mode 100644 index 0000000..fc04eb2 --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/interceptor/NoProxyProxyInterceptor.kt @@ -0,0 +1,13 @@ +package cn.tursom.proxy.interceptor + +import net.sf.cglib.proxy.MethodInterceptor +import net.sf.cglib.proxy.MethodProxy +import java.lang.reflect.Method + +class NoProxyProxyInterceptor( + private val target: Any, +) : MethodInterceptor { + override fun intercept(obj: Any?, method: Method?, args: Array?, proxy: MethodProxy): Any? { + return proxy.invokeSuper(target, args) + } +} \ No newline at end of file diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/interceptor/ProxyInterceptor.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/interceptor/ProxyInterceptor.kt new file mode 100644 index 0000000..3086a09 --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/interceptor/ProxyInterceptor.kt @@ -0,0 +1,59 @@ +package cn.tursom.proxy.interceptor + +import cn.tursom.proxy.annotation.ForEachProxy +import cn.tursom.proxy.annotation.ForFirstProxy +import cn.tursom.proxy.container.ListProxyContainer +import cn.tursom.proxy.container.ProxyContainer +import cn.tursom.proxy.container.ProxyMethodCache +import cn.tursom.proxy.function.CallSuperProxyMethodCacheFunction +import cn.tursom.proxy.function.OnForEachProxyImpl +import cn.tursom.proxy.function.OnForFirstProxyImpl +import net.sf.cglib.proxy.MethodInterceptor +import net.sf.cglib.proxy.MethodProxy +import java.lang.reflect.Method + +open class ProxyInterceptor( + val container: ProxyContainer = ListProxyContainer(), + val nonProxyClasses: MutableSet> = HashSet(listOf(Any::class.java)), +) : MethodInterceptor { + override fun intercept(obj: Any?, method: Method?, args: Array?, proxy: MethodProxy): Any? { + val cache = container.ctx[ProxyMethodCache.ctxKey] + var handler = cache[proxy] + if (handler != null) { + return handler(obj, container, method, args, proxy) + } + + method!! + + nonProxyClasses.forEach { nonProxyClass -> + nonProxyClass.declaredMethods.forEach { + if (it.name == method.name && + it.returnType.isAssignableFrom(method.returnType) && + it.parameterTypes.contentEquals(method.parameterTypes) + ) { + cache.update(proxy, CallSuperProxyMethodCacheFunction) + return proxy.invokeSuper(obj, args) + } + } + } + + //var handler: ProxyMethodCacheFunction? = null + + for (annotation in method.annotations) when (annotation) { + is ForEachProxy -> { + handler = OnForEachProxyImpl(annotation) + break + } + + is ForFirstProxy -> { + handler = OnForFirstProxyImpl(annotation) + break + } + } + if (handler == null) { + handler = OnForFirstProxyImpl(ForFirstProxy.EMPTY) + } + + return handler(obj, container, method, args, proxy) + } +} diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/CglibUtil.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/util/CglibUtil.kt similarity index 90% rename from ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/CglibUtil.kt rename to ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/util/CglibUtil.kt index a336678..8be90b3 100644 --- a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/CglibUtil.kt +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/util/CglibUtil.kt @@ -1,9 +1,12 @@ -package cn.tursom.proxy +package cn.tursom.proxy.util +import cn.tursom.core.final +import cn.tursom.core.static import cn.tursom.reflect.asm.ReflectAsmUtils import net.sf.cglib.core.Signature import net.sf.cglib.proxy.Callback import net.sf.cglib.proxy.MethodProxy +import java.lang.reflect.Field object CglibUtil { fun getFactoryData(clazz: Class<*>): Any? { @@ -37,4 +40,4 @@ object CglibUtil { "CGLIB\$SET_STATIC_CALLBACKS" )!!(callbacks) } -} \ No newline at end of file +} diff --git a/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/util/IntMap.kt b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/util/IntMap.kt new file mode 100644 index 0000000..55e9450 --- /dev/null +++ b/ts-core/ts-proxy/src/main/kotlin/cn/tursom/proxy/util/IntMap.kt @@ -0,0 +1,42 @@ +package cn.tursom.proxy.util + +import cn.tursom.core.uncheckedCast + +class IntMap { + companion object { + private fun upToPowerOf2(n: Int): Int { + if (n and n - 1 == 0) { + return n + } + + var n = n + n = n or (n ushr 1) + n = n or (n ushr 2) + n = n or (n ushr 4) + n = n or (n ushr 8) + n = n or (n ushr 16) + + return n + 1 + } + } + + private var array = arrayOfNulls(32) + + fun clear() { + array = arrayOfNulls(16) + } + + operator fun get(index: Int): V? = if (index >= array.size) { + null + } else { + array[index].uncheckedCast() + } + + operator fun set(index: Int, value: V) { + if (index >= array.size) { + array = array.copyOf(upToPowerOf2(index + 1)) + } + + array[index] = value + } +} \ No newline at end of file diff --git a/ts-core/ts-proxy/src/test/kotlin/cn/tursom/proxy/Example.kt b/ts-core/ts-proxy/src/test/kotlin/cn/tursom/proxy/Example.kt index 236350d..967fede 100644 --- a/ts-core/ts-proxy/src/test/kotlin/cn/tursom/proxy/Example.kt +++ b/ts-core/ts-proxy/src/test/kotlin/cn/tursom/proxy/Example.kt @@ -1,57 +1,86 @@ package cn.tursom.proxy +import cn.tursom.core.allFieldsSequence +import cn.tursom.core.static +import com.esotericsoftware.reflectasm.MethodAccess +import net.sf.cglib.proxy.InvocationHandler +import net.sf.cglib.proxy.MethodProxy import org.junit.Test import org.objectweb.asm.ClassWriter import java.io.File class Example { + companion object{ + var bytes: ByteArray? = null + + fun saveBytes(b: ByteArray) { + bytes = b + } + } + open class TestClass protected constructor() { - //@get:ForEachProxy open var a: Int = 0 } class GetA( - private val t: TestClass, - ) : ProxyMethod { - fun getA(): Int { - Proxy.callSuper.set(true) - return t.a + 1 - } + t: TestClass, + ) { + val t: TestClass = Proxy.getSuperCaller(t) + + val a: Int + get() = t.a + 1 + + //fun getA(): Int { + // return t.a + 1 + //} } @Test fun getClass() { + val access = MethodAccess.get(TestClass::class.java) + println(access) + val writer = ClassWriter(0) val enhancer = Proxy.newEnhancer(TestClass::class.java) + + enhancer.setCallbackType(InvocationHandler::class.java) + enhancer.setCallbackFilter { 0 } + val clazz = enhancer.createClass() - CglibUtil.setStaticCallbacks(clazz, arrayOf(ProxyInterceptor())) - val instance = clazz.newInstance() + //CglibUtil.setStaticCallbacks(clazz, arrayOf(ProxyInterceptor())) + //val instance = clazz.newInstance() enhancer.generateClass(writer) - File("TestClass.class").writeBytes(writer.toByteArray()) + File("TestClass_InvocationHandler.class").writeBytes(writer.toByteArray()) + clazz.allFieldsSequence.forEach { + if (!it.static) return@forEach + + it.isAccessible = true + println("${it.name} = ${it[null]}") + + if (it.type == MethodProxy::class.java) { + val methodProxy = it[null] as MethodProxy + println(methodProxy.signature) + println(methodProxy.superName) + println(methodProxy.superIndex) + } + } + } + + @Test + fun getMethodAccessClass() { + val (t, container) = Proxy.get() + + MethodAccess.get(t.javaClass)!! + //File("TestClass_MethodAccess.class").writeBytes(bytes!!) } @Test fun test() { - //val enhancer = Enhancer() - //enhancer.setSuperclass(TestClass::class.java) - //enhancer.setCallback(ProxyDispatcher) - //val t = enhancer.create() as TestClass - //val enhancer = Enhancer() - //enhancer.setSuperclass(TestClass::class.java) - //enhancer.setCallback(ProxyRefDispatcher { - // println("on Dispatcher") - // ProxyInterceptor() - //}) - // - //val t = enhancer.create() as TestClass - //val writer = ClassWriter(0) - //enhancer.generateClass(writer) - //File("TestClass_ProxyRefDispatcher.class").writeBytes(writer.toByteArray()) val (t, container) = Proxy.get() - //val t = Proxy.getCachedTarget(TestClass::class.java).newInstance() - //val container = Proxy.injectCallback(t) - //container as MutableProxyContainer - container.addProxy(GetA(t)) + val getA = GetA(t) + println(getA.t == t) + + container.addProxy(getA) println(t.javaClass) repeat(10) {