faster proxy

This commit is contained in:
tursom 2023-01-20 17:50:37 +08:00
parent d170678182
commit 3ae1044999
43 changed files with 634 additions and 236 deletions

View File

@ -1,7 +1,7 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.6.20"
kotlin("jvm") version "1.8.0"
`maven-publish`
id("ts-gradle")
}
@ -10,12 +10,7 @@ allprojects {
group = "cn.tursom"
version = "1.0-SNAPSHOT"
repositories {
//mavenCentral()
maven {
url = uri("https://nvm.tursom.cn/repository/maven-public/")
}
}
useTursomRepositories()
tasks.withType<JavaCompile> {
tasks.withType<KotlinCompile>().configureEach {
@ -23,9 +18,7 @@ allprojects {
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
}
if (project.gradle.startParameter.taskNames.firstOrNull { taskName ->
taskName.endsWith(":test")
} == null) {
if (!isTestRunning) {
tasks.withType<Test> {
enabled = false
}
@ -37,6 +30,8 @@ allprojects {
kotlinOptions.freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
//kotlinOptions.useIR = true
}
autoConfigPublish()
}
@ -50,7 +45,3 @@ dependencies {
artifacts {
archives(tasks["kotlinSourcesJar"])
}
publishing {
publish(this)
}

View File

@ -19,3 +19,6 @@ android.useAndroidX=true
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
project.groupId=cn.tursom
project.version=1.0-SNAPSHOT

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -15,6 +15,4 @@ dependencies {
testApi(group = "junit", name = "junit", version = "4.13.2")
}
autoConfigPublish()

View File

@ -4,7 +4,7 @@ import cn.tursom.core.uncheckedCast
import java.util.concurrent.atomic.AtomicInteger
class ArrayContextEnv : ContextEnv {
val envId = ContextEnv.newEnvId()
override val envId = ContextEnv.newEnvId()
private val idGenerator = AtomicInteger()
private val emptyContext = EmptyArrayContext(envId, idGenerator)

View File

@ -8,6 +8,8 @@ interface ContextEnv {
fun newEnvId() = contextEnvIdGenerator.incrementAndGet()
}
val envId: Int
fun emptyContext(): Context = newContext()
fun newContext(): Context
fun <T> newKey(): ContextKey<T>

View File

@ -4,7 +4,7 @@ import cn.tursom.core.uncheckedCast
import java.util.concurrent.atomic.AtomicInteger
class HashMapContextEnv : ContextEnv {
val envId = ContextEnv.newEnvId()
override val envId = ContextEnv.newEnvId()
private val idGenerator = AtomicInteger()
override fun newContext(): Context = HashMapContext(envId)

View File

@ -1,12 +1,33 @@
import java.math.BigInteger
fun main() {
val two = BigInteger.valueOf(2)
val three = BigInteger.valueOf(3)
val ten = BigInteger.TEN
var i = BigInteger.ONE
repeat(1000) {
i *= two
print("${i / ten / ten / ten % ten}")
enum class Status(val id: Int) : (Int) -> Int {
S1(1), S2(2), S3(3);
override fun invoke(i: Int): Int {
return 0
}
infix fun or(status: Status) = status.id or id
infix fun or(status: Int) = id or status
}
infix fun Int.or(status: Status) = this or status.id
inline fun <reified T> Any.instanceOf() = this is T
fun main() {
println(1.instanceOf<Int>())
println(1L.instanceOf<Int>())
println(1f.instanceOf<Int>())
//var i1 = 1 or Status.S1
//
//val two = BigInteger.valueOf(2)
//val three = BigInteger.valueOf(3)
//val ten = BigInteger.TEN
//var i = BigInteger.ONE
//repeat(1000) {
// i *= two
// print("${i / ten / ten / ten % ten}")
//}
}

View File

@ -0,0 +1,8 @@
import kotlinx.coroutines.delay
tailrec fun pow(i: Int, n: Int = 1): Int = if (i <= 0) n else pow(i - 1, i * n)
suspend fun main() {
delay(pow(10).toLong())
println("finished")
}

View File

@ -0,0 +1,16 @@
package cn.tursom.core.curry
import cn.tursom.core.allMemberPropertiesSequence
import java.util.concurrent.ConcurrentHashMap
fun example(f: (Int) -> (Int) -> (Int) -> Int) {
f(1)(2)(3)
f(1, 2, 3)
}
fun example2(f: (Int, Int, Int) -> Int) {
f(1, 2, 3)
f(1, 2)(3)
}

View File

@ -8,10 +8,12 @@ import kotlin.reflect.KProperty
* 如果你还需要对这个属性进行加锁你就可以在后方加一个 .locked 即可
* 如果还需要用指定的锁在后面再加一个 (lock) 就玩成了
* 使用例
* ```
* class XXXImpl: XXX, FieldChangeListener by FieldChangeListenerImpl() {
* val lock = ReentrantLock()
* val field by listened(0).locker(lock)
* }
* ```
*/
interface DelegatedField<in T, out V> {
/**

View File

@ -0,0 +1,49 @@
package cn.tursom.core.delegation.observer
import cn.tursom.core.delegation.expirable
import cn.tursom.core.delegation.filter
import cn.tursom.core.delegation.notNull
import java.lang.Thread.sleep
class A {
@OptIn(Listenable::class)
var field by listenable(0)
.filter { _, new ->
new >= 0
}
.expirable(100)
.notNull {
throw IllegalAccessError()
}
@OptIn(Listenable::class)
var canServer by listenable(true)
@OptIn(Listenable::class)
var count: Int by listenable(0)
}
fun f(obj: A) {
obj::field.listen { old: Int?, new: Int? ->
println("old: $old, new: $new")
}
obj::canServer.listen { old, new ->
println(new)
}
obj::count.listen { old, new ->
}
}
fun main() {
val a = A()
f(a)
a.field = 1
println(a.field)
sleep(1000)
println(a.field)
}

View File

View File

@ -11,7 +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 = "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")
}

View File

@ -0,0 +1,34 @@
package cn.tursom.proxy.function;
import cn.tursom.proxy.container.ProxyContainer;
import cn.tursom.proxy.container.ProxyMethodCacheFunction;
import com.esotericsoftware.reflectasm.MethodAccess;
import net.sf.cglib.proxy.MethodProxy;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.Method;
public class JavaReflectASMProxyMethodInvoker implements ProxyMethodCacheFunction {
private final Object self;
private final MethodAccess methodAccess;
private final int index;
public JavaReflectASMProxyMethodInvoker(Object self, MethodAccess methodAccess, int index) {
this.self = self;
this.methodAccess = methodAccess;
this.index = index;
}
@Nullable
@Override
public Object invoke(
@Nullable Object obj,
@NotNull ProxyContainer c,
@Nullable Method method,
@Nullable Object[] args,
@Nullable MethodProxy proxy
) {
return methodAccess.invoke(self, index, args);
}
}

View File

@ -7,6 +7,7 @@ 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.CachedMethodInterceptor
import cn.tursom.proxy.interceptor.LocalCachedProxyInterceptor
import cn.tursom.proxy.interceptor.ProxyInterceptor
import cn.tursom.reflect.final
@ -23,14 +24,18 @@ object Proxy {
it.final = false
}
fun getContainer(obj: Any): ProxyContainer? {
if (obj !is Factory) return null
fun getContainer(obj: Factory): ProxyContainer? {
val interceptor = obj.getCallback(0) as? ProxyInterceptor ?: return null
return interceptor.container
}
fun getMutableContainer(obj: Factory): MutableProxyContainer? {
val interceptor = obj.getCallback(0) as? ProxyInterceptor ?: return null
return interceptor.container as? MutableProxyContainer
}
fun addProxy(obj: Any, proxy: ProxyMethod): Boolean {
val container = getContainer(obj) as? MutableProxyContainer ?: return false
val container = getContainer(obj as Factory) as? MutableProxyContainer ?: return false
container.addProxy(proxy)
return true
}
@ -48,7 +53,7 @@ object Proxy {
container.target = obj
container.ctx[directAccessorKey] = directAccessor
injectCallback(obj as Factory, container, directAccessor)
injectCallback(obj as Factory, container, directAccessor as Factory)
return obj to container
}
@ -137,6 +142,7 @@ object Proxy {
fun <T : Any> newEnhancer(clazz: Class<T>, vararg interfaces: Class<*>): Enhancer {
val enhancer = Enhancer()
enhancer.setSuperclass(clazz)
if (interfaces.isNotEmpty()) {
enhancer.setInterfaces(interfaces)
@ -151,15 +157,17 @@ object Proxy {
}
@JvmOverloads
fun injectCallback(obj: Any, container: ProxyContainer = defaultContainer(), target: Any = obj): ProxyContainer {
obj as Factory
fun injectCallback(
obj: Factory,
container: ProxyContainer = defaultContainer(),
target: Factory = obj,
): ProxyContainer {
if (obj.getCallback(0) != null && obj.getCallback(0) is ProxyInterceptor) {
return (obj.getCallback(0) as ProxyInterceptor).container
}
val nonProxyClasses: MutableSet<Class<*>> = HashSet(listOf(Any::class.java))
repeat(obj.callbacks.size) {
obj.setCallback(it, LocalCachedProxyInterceptor(container, nonProxyClasses, target))
obj.setCallback(it, LocalCachedProxyInterceptor(container, target))
}
return container
}
@ -170,27 +178,30 @@ object Proxy {
fun <T : Any> getSuperCaller(
obj: T,
): T = getContainer(obj)?.ctx?.get(directAccessorKey).uncheckedCast()
): T = getContainer(obj as Factory)?.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 addNonProxyClass(target: Factory, nonProxyClass: Class<*>): Boolean {
val container = getMutableContainer(target) ?: throw IllegalArgumentException()
return container.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()
}
fun removeNonProxyClass(target: Factory, nonProxyClass: Class<*>): Boolean {
val container = getMutableContainer(target) ?: throw IllegalArgumentException()
return container.nonProxyClasses.remove(nonProxyClass)
}
return (target.getCallback(0) as ProxyInterceptor).nonProxyClasses.remove(nonProxyClass)
fun clearCallbackCache(target: Factory, container: ProxyContainer) {
target.callbacks.forEachIndexed { index, callback ->
if (callback == null) {
target.setCallback(index, LocalCachedProxyInterceptor(container, target))
return@forEachIndexed
}
if (callback !is CachedMethodInterceptor) {
return@forEachIndexed
}
callback.clearCache()
}
}
}

View File

@ -1,19 +1,22 @@
package cn.tursom.proxy.container
import cn.tursom.core.context.Context
import cn.tursom.proxy.Proxy
import cn.tursom.proxy.function.ProxyMethod
import cn.tursom.proxy.util.CglibUtil
import net.sf.cglib.proxy.MethodProxy
import net.sf.cglib.proxy.Factory
class ListProxyContainer(
private val proxyList: MutableCollection<Any> = ArrayList(),
override val nonProxyClasses: MutableSet<Class<*>> = HashSet(listOf(Any::class.java)),
) : MutableProxyContainer {
override lateinit var target: Any
override val ctx: Context = ProxyContainer.ctxEnv.newContext()
private fun clearCache() {
ctx[ProxyMethodCache.ctxKey].clear()
Proxy.clearCallbackCache(target as Factory, this)
}
override fun addProxy(proxy: Any) {

View File

@ -2,6 +2,7 @@ package cn.tursom.proxy.container
interface MutableProxyContainer : ProxyContainer {
override var target: Any
override val nonProxyClasses: MutableSet<Class<*>>
fun addProxy(proxy: Any)
fun addAllProxy(proxy: Collection<Any>?)

View File

@ -11,4 +11,5 @@ interface ProxyContainer : Iterable<Any> {
// to impl, use override val context: Context = ProxyContainer.contextEnv.newContext()
val ctx: Context
val target: Any
val nonProxyClasses: Set<Class<*>>
}

View File

@ -1,7 +1,9 @@
package cn.tursom.proxy.container
import cn.tursom.proxy.function.ProxyMethod
import cn.tursom.proxy.util.IntMap
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
class ProxyMethodCache {
companion object {
@ -18,9 +20,22 @@ class ProxyMethodCache {
}
}
fun update(proxy: MethodProxy, function: ProxyMethodCacheFunction) {
fun update(
obj: Any,
container: ProxyContainer,
method: Method,
proxy: MethodProxy,
function: ProxyMethodCacheFunction,
) {
var handler = function
container.forEach {
if (it !is ProxyMethod) return@forEach
handler = it.onProxyHandlerCacheUpdate(handler, obj, container, method, proxy)
}
synchronized(this) {
functionMap[proxy.superIndex] = function
functionMap[proxy.superIndex] = handler
}
}

View File

@ -1,13 +1,14 @@
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<out Any?>?,
MethodProxy?,
) -> Any?
fun interface ProxyMethodCacheFunction {
operator fun invoke(
obj: Any?,
c: ProxyContainer,
method: Method?,
args: Array<out Any?>?,
proxy: MethodProxy?,
): Any?
}

View File

@ -15,11 +15,10 @@ class CachedOnForEachProxyImpl(
a: Array<out Any?>?,
proxy: MethodProxy?,
): Any? {
proxy!!
proxyList.forEach { p ->
p(o, c, m, a, proxy)
}
return proxy.invokeSuper(o, a)
return proxy!!.invokeSuper(o, a)
}
}

View File

@ -2,10 +2,22 @@ package cn.tursom.proxy.function
import cn.tursom.proxy.container.ProxyContainer
import cn.tursom.proxy.container.ProxyMethodCacheFunction
import cn.tursom.reflect.asm.ReflectAsmUtils
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
object CallSuperProxyMethodCacheFunction : ProxyMethodCacheFunction {
operator fun get(obj: Any, method: Method): ProxyMethodCacheFunction {
val (methodAccess, index) = ReflectAsmUtils.getMethodByRegex(
obj.javaClass,
"CGLIB\\\$${method.name}\\\$.*".toRegex(),
*method.parameterTypes,
method.returnType,
) ?: return CallSuperProxyMethodCacheFunction
return ReflectASMProxyMethodInvoker(obj, methodAccess, index).toJava()
}
override fun invoke(
obj: Any?,
c: ProxyContainer,

View File

@ -4,21 +4,20 @@ 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 cn.tursom.reflect.asm.ReflectAsmInvoker
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
internal class JavaReflectProxyMethodInvoker(
internal class JvmReflectProxyMethodInvoker(
private val self: Any,
private val method: Method,
) : ProxyMethodCacheFunction {
companion object {
private val fastInvoker: Method.(Any?, Array<out Any?>?) -> Any? = Method::invoke
operator fun get(
proxy: Any,
method: Method,
): JavaReflectProxyMethodInvoker? {
var invoker: JavaReflectProxyMethodInvoker? = null
): JvmReflectProxyMethodInvoker? {
var invoker: JvmReflectProxyMethodInvoker? = null
val selfMethod: Method
try {
@ -39,9 +38,7 @@ internal class JavaReflectProxyMethodInvoker(
selfMethod = proxy.javaClass.getMethod(methodName, *method.parameterTypes)
selfMethod.isAccessible = true
invoker = JavaReflectProxyMethodInvoker(proxy, method)
//handlerCacheMap[method] = ProxyResult(invoker, true)
invoker = JvmReflectProxyMethodInvoker(proxy, method)
} catch (_: Exception) {
}
@ -56,6 +53,6 @@ internal class JavaReflectProxyMethodInvoker(
args: Array<out Any?>?,
proxy: MethodProxy?,
): Any? {
return fastInvoker(this.method, self, args)
return ReflectAsmInvoker.invoke(this.method, self, args)
}
}

View File

@ -30,7 +30,6 @@ class OnForEachProxyImpl(
proxy: MethodProxy?,
): Any? {
m!!
proxy!!
val proxyList = if (cache) ArrayList() else emptyProxyList
c.forEach { p ->
@ -42,9 +41,13 @@ class OnForEachProxyImpl(
}
}
}
proxy!!
if (cache) {
c.ctx[ProxyMethodCache.ctxKey].update(
proxy, CachedOnForEachProxyImpl(proxyList)
o!!, c, m, proxy,
CachedOnForEachProxyImpl(proxyList),
)
}
return proxy.invokeSuper(o, a)

View File

@ -32,6 +32,7 @@ class OnForFirstProxyImpl(
): Any? {
method!!
proxy!!
obj!!
container.forEach { p ->
if (classes.isNotEmpty() && classes.none { c: Class<*> -> c.isInstance(p) }) {
@ -40,7 +41,7 @@ class OnForFirstProxyImpl(
val handler = ProxyMethod.getHandler(p, method) ?: return@forEach
if (ffpAnnotation.cache) {
container.ctx[ProxyMethodCache.ctxKey].update(proxy, handler)
container.ctx[ProxyMethodCache.ctxKey].update(obj, container, method, proxy, handler)
}
return handler(obj, container, method, args, proxy)
}
@ -68,15 +69,15 @@ class OnForFirstProxyImpl(
val exceptionConstructor = ffpAnnotation.errClass.java.getConstructor(String::class.java)
if (ffpAnnotation.cache) {
container.ctx[ProxyMethodCache.ctxKey].update(
proxy, ExceptionProxyMethodCacheFunctionImpl(exceptionConstructor, errMsg)
obj, container, method, proxy, ExceptionProxyMethodCacheFunctionImpl(exceptionConstructor, errMsg)
)
}
throw exceptionConstructor.newInstance(errMsg)
}
if (ffpAnnotation.cache) {
container.ctx[ProxyMethodCache.ctxKey].update(
proxy,
CallSuperProxyMethodCacheFunction,
obj, container, method, proxy,
CallSuperProxyMethodCacheFunction[obj, method]
)
}
return proxy.invokeSuper(obj, args)

View File

@ -14,6 +14,13 @@ interface ProxyMethod {
// NO-OP
}
fun onGet(f: ProxyMethodCacheFunction, method: Method): ProxyMethodCacheFunction = f
/**
* disabled on default.
*
* override onCached to enable it
*/
fun onProxyInvoke(
o: Any?,
c: ProxyContainer,
@ -25,12 +32,25 @@ interface ProxyMethod {
return next(o, c, m, a, proxy)
}
/**
* 当有缓存更新时被调用
*
* 你可以以此替换缓存方法
*/
fun onProxyHandlerCacheUpdate(
f: ProxyMethodCacheFunction,
obj: Any,
container: ProxyContainer,
method: Method,
proxy: MethodProxy,
) = f
companion object {
fun getHandler(proxy: Any, method: Method): ProxyMethodCacheFunction? {
var handler = getReflectHandler(proxy, method) ?: return null
if (proxy is ProxyMethod) {
handler = ProxyMethodInvoker(proxy, handler)
handler = proxy.onGet(handler, method)
}
return handler
@ -39,10 +59,10 @@ interface ProxyMethod {
private fun getReflectHandler(proxy: Any, method: Method): ProxyMethodCacheFunction? {
val reflectAsmHandler = ReflectASMProxyMethodInvoker[proxy, method]
if (reflectAsmHandler != null) {
return reflectAsmHandler
return reflectAsmHandler.toJava()
}
val javaReflectHandler = JavaReflectProxyMethodInvoker[proxy, method]
val javaReflectHandler = JvmReflectProxyMethodInvoker[proxy, method]
if (javaReflectHandler != null) {
return javaReflectHandler
}

View File

@ -2,6 +2,7 @@ package cn.tursom.proxy.function
import cn.tursom.proxy.container.ProxyContainer
import cn.tursom.proxy.container.ProxyMethodCacheFunction
import cn.tursom.reflect.asm.ReflectAsmInvoker
import cn.tursom.reflect.asm.ReflectAsmUtils
import com.esotericsoftware.reflectasm.MethodAccess
import net.sf.cglib.proxy.MethodProxy
@ -13,19 +14,35 @@ internal class ReflectASMProxyMethodInvoker(
private val index: Int,
) : ProxyMethodCacheFunction {
companion object {
private val fastInvoker: MethodAccess.(Any, Int, Array<out Any?>?) -> Any? = MethodAccess::invoke
operator fun get(
proxy: Any,
method: Method,
): ReflectASMProxyMethodInvoker? {
val reflectAsmMethod = try {
ReflectAsmUtils.getMethod(
val methodParamTypes = method.parameterTypes
val matchedMethods = ReflectAsmUtils.getMethodSequence(
proxy.javaClass,
method.name,
paramTypes = method.parameterTypes,
paramTypes = methodParamTypes,
returnType = method.returnType,
)
).toList()
when {
matchedMethods.isEmpty() -> null
matchedMethods.size == 1 -> matchedMethods.first()
else -> matchedMethods.minBy { (methodAccess, methodIndex) ->
var parameterIndex = 0
methodAccess.parameterTypes[methodIndex].sumOf { parameterType ->
if (methodParamTypes[parameterIndex++] != parameterType) {
0L
} else {
1L
}
}
}
}
} catch (e: Exception) {
e.printStackTrace()
null
@ -36,6 +53,8 @@ internal class ReflectASMProxyMethodInvoker(
}
}
fun toJava() = JavaReflectASMProxyMethodInvoker(self, methodAccess, index)
override fun invoke(
obj: Any?,
c: ProxyContainer,
@ -43,6 +62,6 @@ internal class ReflectASMProxyMethodInvoker(
args: Array<out Any?>?,
proxy: MethodProxy?,
): Any? {
return fastInvoker(methodAccess, self, index, args)
return ReflectAsmInvoker.invoke(methodAccess, self, index, args)
}
}

View File

@ -0,0 +1,7 @@
package cn.tursom.proxy.interceptor
import net.sf.cglib.proxy.MethodInterceptor
interface CachedMethodInterceptor : MethodInterceptor {
fun clearCache()
}

View File

@ -1,5 +1,6 @@
package cn.tursom.proxy.interceptor
import cn.tursom.reflect.asm.ReflectAsmInvoker
import cn.tursom.reflect.asm.ReflectAsmUtils
import com.esotericsoftware.reflectasm.MethodAccess
import net.sf.cglib.proxy.InvocationHandler
@ -8,10 +9,6 @@ import java.lang.reflect.Method
class LocalCachedNoProxyInvocationHandler(
val proxy: Any,
) : InvocationHandler {
companion object {
private val fastInvoker: MethodAccess.(Any, Int, Array<out Any?>?) -> Any? = MethodAccess::invoke
}
private var handler: (method: Method?, args: Array<out Any>?) -> Any? = DefaultHandler()
override fun invoke(ignore: Any, method: Method?, args: Array<out Any>?): Any? {
@ -29,7 +26,7 @@ class LocalCachedNoProxyInvocationHandler(
method.returnType,
)!!
handler = MethodAccessHandler(methodAccess, index)
return fastInvoker(methodAccess, proxy, index, args)
return ReflectAsmInvoker.invoke(methodAccess, proxy, index, args)
}
}
@ -37,6 +34,7 @@ class LocalCachedNoProxyInvocationHandler(
private val methodAccess: MethodAccess,
private val index: Int,
) : (Method?, Array<out Any>?) -> Any? {
override fun invoke(method: Method?, args: Array<out Any>?) = fastInvoker(methodAccess, proxy, index, args)
override fun invoke(method: Method?, args: Array<out Any>?) =
ReflectAsmInvoker.invoke(methodAccess, proxy, index, args)
}
}

View File

@ -3,29 +3,36 @@ 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.Factory
import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
class LocalCachedProxyInterceptor(
container: ProxyContainer,
nonProxyClasses: MutableSet<Class<*>>,
val target: Any,
) : ProxyInterceptor(container, nonProxyClasses) {
val target: Factory,
) : ProxyInterceptor(container), CachedMethodInterceptor {
private var cache: ProxyMethodCacheFunction? = null
private val c: ProxyContainer = container
override fun intercept(obj: Any?, method: Method?, args: Array<out Any?>?, proxy: MethodProxy): Any? {
override fun clearCache() {
cache = null
}
override fun intercept(obj: Any?, method: Method?, args: Array<out Any?>?, proxy: MethodProxy?): Any? {
var cache = this.cache
if (cache != null) {
return cache(target, container, method, args, proxy)
return cache(target, c, method, args, proxy)
}
val methodCache = container.ctx[ProxyMethodCache.ctxKey]
proxy!!
val methodCache = c.ctx[ProxyMethodCache.ctxKey]
this.cache = methodCache[proxy]
cache = this.cache
if (cache != null) {
return cache(target, container, method, args, proxy)
return cache(target, c, method, args, proxy)
}
return super.intercept(target, method, args, proxy)

View File

@ -13,25 +13,25 @@ import net.sf.cglib.proxy.MethodProxy
import java.lang.reflect.Method
open class ProxyInterceptor(
val container: ProxyContainer = ListProxyContainer(),
val nonProxyClasses: MutableSet<Class<*>> = HashSet(listOf(Any::class.java)),
val container: ProxyContainer = ListProxyContainer(nonProxyClasses = HashSet(listOf(Any::class.java))),
) : MethodInterceptor {
override fun intercept(obj: Any?, method: Method?, args: Array<out Any?>?, proxy: MethodProxy): Any? {
override fun intercept(obj: Any?, method: Method?, args: Array<out Any?>?, proxy: MethodProxy?): Any? {
val cache = container.ctx[ProxyMethodCache.ctxKey]
var handler = cache[proxy]
var handler = cache[proxy!!]
if (handler != null) {
return handler(obj, container, method, args, proxy)
}
obj!!
method!!
nonProxyClasses.forEach { nonProxyClass ->
container.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)
cache.update(obj, container, method, proxy, CallSuperProxyMethodCacheFunction[obj, method])
return proxy.invokeSuper(obj, args)
}
}

View File

@ -2,37 +2,32 @@ package cn.tursom.proxy
import cn.tursom.core.allFieldsSequence
import cn.tursom.core.static
import cn.tursom.proxy.container.ProxyContainer
import cn.tursom.proxy.container.ProxyMethodCacheFunction
import cn.tursom.proxy.function.ProxyMethod
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
import java.lang.reflect.Method
class Example {
companion object{
companion object {
var bytes: ByteArray? = null
fun saveBytes(b: ByteArray) {
bytes = b
}
}
open class TestClass protected constructor() {
open var a: Int = 0
open var a: Int? = 0
}
class GetA(
t: TestClass,
) {
) : ProxyMethod {
val t: TestClass = Proxy.getSuperCaller(t)
val a: Int
get() = t.a + 1
//fun getA(): Int {
// return t.a + 1
//}
val a get() = (t.a ?: 0) + 1
}
@Test
@ -95,9 +90,35 @@ class Example {
container.addProxy(GetA(t))
println(t.javaClass)
repeat(1000000000) {
for (l in 0..10_000_000_000L) {
t.a = t.a
//println(t.a)
}
}
interface IntContainer {
var i: Int?
}
class IntContainerImpl(override var i: Int?) : IntContainer
class IntProxy(
private val c: IntContainer,
) : IntContainer {
override var i: Int?
get() = (c.i ?: 0) + 1
set(value) {
c.i = value
}
}
private val a: IntContainerImpl = IntContainerImpl(0)
@Test
fun benchmarkOrigin() {
val p: IntContainer = IntProxy(a)
for (l in 0..10000000000L) {
p.i = p.i
}
}
}

View File

@ -0,0 +1,19 @@
package cn.tursom.reflect.asm;
import com.esotericsoftware.reflectasm.MethodAccess;
import org.jetbrains.annotations.Nullable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectAsmInvoker {
@Nullable
public static Object invoke(MethodAccess methodAccess, Object obj, int index, Object[] args) {
return methodAccess.invoke(obj, index, args);
}
@Nullable
public static Object invoke(Method method, Object obj, Object[] args) throws InvocationTargetException, IllegalAccessException {
return method.invoke(obj, args);
}
}

View File

@ -62,6 +62,7 @@ object ReflectAsmUtils {
} catch (_: Exception) {
}
}
fieldAccessList.add(MethodAccess.get(analyzeFieldAccessClass)!!)
methodAccessMap[this] = fieldAccessList
fieldAccessList
}
@ -84,6 +85,41 @@ object ReflectAsmUtils {
return null
}
fun getMethodSequence(
clazz: Class<*>,
methodName: String,
vararg paramTypes: Class<*>,
returnType: Class<*> = Any::class.java,
) = sequence {
clazz.methodAccessList.forEach { methodAccess ->
repeat(methodAccess.methodNames.size) { i ->
if (methodAccess.methodNames[i] == methodName && methodAccess.parameterTypes[i] match paramTypes &&
methodAccess.returnTypes[i] canCast returnType
) {
yield(methodAccess to i)
}
}
}
}
fun getMethodByRegex(
clazz: Class<*>,
methodName: Regex,
vararg paramTypes: Class<*>,
returnType: Class<*> = Any::class.java,
): Pair<MethodAccess, Int>? {
clazz.methodAccessList.forEach { methodAccess ->
repeat(methodAccess.methodNames.size) { i ->
if (methodName.matches(methodAccess.methodNames[i]) && methodAccess.parameterTypes[i] match paramTypes &&
methodAccess.returnTypes[i] canCast returnType
) {
return methodAccess to i
}
}
}
return null
}
fun getMethod(method: Method): Pair<MethodAccess, Int>? = getMethod(
method.declaringClass,
method.name,

View File

@ -1,6 +1,6 @@
package cn.tursom.database.ktorm.ext
import cn.tursom.proxy.ProxyMethod
import cn.tursom.proxy.function.ProxyMethod
import cn.tursom.reflect.asm.ReflectAsmKtField
import org.ktorm.expression.SqlExpression
import org.ktorm.expression.SqlFormatter

View File

@ -2,7 +2,7 @@
import java.util.*
plugins {
kotlin("jvm") version "1.3.72"
kotlin("jvm") version "1.7.10"
`java-gradle-plugin`
`maven-publish`
}
@ -14,10 +14,10 @@ try {
ext.set(k.toString(), v)
try {
setProperty(k.toString(), v)
} catch (e: Exception) {
} catch (_: Exception) {
}
}
} catch (e: Exception) {
} catch (_: Exception) {
}
group = "cn.tursom"
@ -25,11 +25,13 @@ group = "cn.tursom"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral()
maven {
url = uri("https://nvm.tursom.cn/repository/maven-public/")
}
}
dependencies {
implementation(group = "org.yaml", name = "snakeyaml", version = "1.29")
implementation(group = "org.yaml", name = "snakeyaml", version = "1.33")
implementation(fileTree(mapOf("dir" to "libs", "include" to arrayOf("*.jar"))))
}
@ -60,7 +62,7 @@ publishing {
}
}
} catch (e: Exception) {
println("cannot push to repository tursom")
println("cannot push to repository tursom: ${e.javaClass}: ${e.message}")
}
try {
maven {

View File

@ -1,12 +1,15 @@
import org.gradle.api.DomainObjectCollection
import org.gradle.api.NamedDomainObjectCollection
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.dsl.RepositoryHandler
import org.gradle.api.artifacts.repositories.PasswordCredentials
import org.gradle.api.publish.PublicationContainer
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.internal.authentication.DefaultBasicAuthentication
import java.util.concurrent.TimeUnit
var nettyVersion = "4.1.68.Final"
fun Project.excludeTest() {
if (gradle.startParameter.taskNames.firstOrNull { taskName ->
taskName.contains("test", true)
@ -17,92 +20,13 @@ fun Project.excludeTest() {
named("compileTestJava") { it.enabled = false }
try {
named("compileTestKotlin") { it.enabled = false }
} catch (e: Exception) {
} catch (_: Exception) {
}
named("processTestResources") { it.enabled = false }
}
}
}
fun Project.publish(publish: PublishingExtension) {
val properties = rootProject.properties
publish.repositories { repositoryHandler ->
try {
repositoryHandler.maven { repository ->
repository.name = "tursom"
repository.url = if (version.toString().endsWith("SNAPSHOT")) {
uri("https://nvm.tursom.cn/repository/maven-snapshots/")
} else {
uri("https://nvm.tursom.cn/repository/maven-releases/")
}
repository.credentials { credentials ->
val artifactoryUser: String = rootProject.ext["tursom.artifactoryUser"]!!.toString()
val artifactoryPassword: String = rootProject.ext["tursom.artifactoryPassword"]!!.toString()
credentials.username = artifactoryUser
credentials.password = artifactoryPassword
}
}
} catch (e: Exception) {
println("cannot publish to repository tursom:\n${e.javaClass}: ${e.message}")
}
val repositoriesRegex = "publishRepositories\\.[a-zA-z][a-zA-z0-9]*".toRegex()
properties.keys.asSequence().filter {
it matches repositoriesRegex
}.forEach { repositoryName ->
try {
val artifactoryUser = rootProject.ext["$repositoryName.artifactoryUser"]?.toString()
?: throw Exception("no artifactory user found")
val artifactoryPassword = rootProject.ext["$repositoryName.artifactoryPassword"]?.toString()
?: throw Exception("no artifactory password found")
repositoryHandler.maven { repository ->
repository.name = properties["$repository.name"]?.toString()
?: repositoryName.substringAfterLast('.')
val releasesRepoUrl = properties["$repositoryName.release"]?.let {
uri(it.toString())
}
val snapshotRepoUrl = properties["$repositoryName.snapshot"]?.let {
uri(it.toString())
}
val repoUrl = properties["$repositoryName.url"]?.let {
uri(it.toString())
}
repository.url = if (version.toString().endsWith("SNAPSHOT") && snapshotRepoUrl != null) {
snapshotRepoUrl
} else {
releasesRepoUrl
} ?: repoUrl ?: throw Exception("no repo found")
repository.credentials {
it.username = artifactoryUser
it.password = artifactoryPassword
}
}
} catch (e: Exception) {
println(
"cannot publish to repository ${repositoryName.substringAfterLast('.')}:\n" +
"${e.javaClass}: ${e.message}"
)
}
}
}
publish.publications {
it.create("maven", MavenPublication::class.java) { mavenPublication ->
mavenPublication.groupId = project.group.toString()
mavenPublication.artifactId = project.name
mavenPublication.version = project.version.toString()
try {
mavenPublication.from(components.getByName("java"))
} catch (e: Exception) {
}
try {
mavenPublication.artifact(tasks.getByName("kotlinSourcesJar"))
} catch (e: Exception) {
}
}
}
}
fun DomainObjectCollection<Configuration>.noExpire() {
all {
it.resolutionStrategy.cacheChangingModulesFor(0, TimeUnit.SECONDS)
@ -110,7 +34,7 @@ fun DomainObjectCollection<Configuration>.noExpire() {
}
}
fun Project.userTursomRepositories(
fun Project.useTursomRepositories(
useAliyunMirror: Boolean = false,
mavenCentral: Boolean = false,
tursom: Boolean = true
@ -132,6 +56,19 @@ fun Project.userTursomRepositories(
}
try {
configurations.noExpire()
} catch (e: Exception) {
} catch (_: Exception) {
}
}
fun <T> NamedDomainObjectCollection<T>.contains(name: String) = try {
findByName(name)
} catch (e: Exception) {
null
} != null
operator fun Project.get(key: String) = ext[key]?.toString()
val Project.isTestRunning
get() = gradle.startParameter.taskNames.firstOrNull { taskName ->
taskName.endsWith(":test")
} != null

View File

@ -1,5 +1,6 @@
package cn.tursom.gradle
import contains
import excludeTest
import ext
import org.gradle.api.DefaultTask
@ -37,19 +38,7 @@ class TursomGradlePlugin : Plugin<Project> {
target.excludeTest()
try {
target.extensions.configure<PublishingExtension>("publishing") {
target.publish(it)
}
} catch (e: Exception) {
}
if (try {
target.tasks.findByName("install")
} catch (e: Exception) {
null
} == null
) run install@{
if (!target.tasks.contains("install")) run install@{
val publishToMavenLocal = target.tasks.findByName("publishToMavenLocal") ?: return@install
target.tasks.register("install", DefaultTask::class.java) {
it.finalizedBy(publishToMavenLocal)
@ -85,6 +74,7 @@ fun put(target: Project, key: String, value: Any?) {
null -> return
is String, is Byte, is Short, is Int, is Long, is Float, is Double, is Char ->
setProperty(target, key, value)
else -> {
setProperty(target, key, value)
if (value is Map<*, *>) {

View File

@ -0,0 +1 @@
var nettyVersion = "4.1.68.Final"

View File

@ -0,0 +1,176 @@
import org.gradle.api.Project
import org.gradle.api.artifacts.dsl.RepositoryHandler
import org.gradle.api.artifacts.repositories.PasswordCredentials
import org.gradle.api.publish.PublicationContainer
import org.gradle.api.publish.PublishingExtension
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.authentication.http.BasicAuthentication
fun Project.autoConfigPublish() {
try {
extensions.configure("publishing", ::publish)
} catch (e: Exception) {
println("auto config publish failed: ${e.javaClass} ${e.message}")
}
}
fun Project.publish(publish: PublishingExtension) {
publish.repositories { repositoryHandler ->
createTursomPublishRepository(repositoryHandler)
scanAndCreatePublishRepository(repositoryHandler)
}
publish.publications(::createMavenPublications)
}
/**
* create publish repository for tursom's server
*/
private fun Project.createTursomPublishRepository(repositoryHandler: RepositoryHandler) {
try {
repositoryHandler.maven { repository ->
repository.name = "tursom"
val version = getVersionWithProperties()
if (warnVersionNotSet(version)) {
return@maven
}
repository.url = if (version.endsWith("SNAPSHOT")) {
uri("https://nvm.tursom.cn/repository/maven-snapshots/")
} else {
uri("https://nvm.tursom.cn/repository/maven-releases/")
}
repository.authentication{ ac ->
ac.forEach {
}
}
repository.credentials(PasswordCredentials::class.java) { credentials ->
val artifactoryUser: String = rootProject.ext["tursom.artifactoryUser"]!!.toString()
val artifactoryPassword: String = rootProject.ext["tursom.artifactoryPassword"]!!.toString()
credentials.username = artifactoryUser
credentials.password = artifactoryPassword
}
}
} catch (e: Exception) {
println("cannot publish to repository tursom:\n${e.javaClass}: ${e.message}")
}
}
/**
* scan properties begin with "publishRepositories" to create user defined publish repository
*/
private fun Project.scanAndCreatePublishRepository(repositoryHandler: RepositoryHandler) {
val properties = rootProject.properties
val repositoriesRegex = "^publishRepositories\\.[a-zA-z][a-zA-z0-9]*$".toRegex()
properties.keys.asSequence().filter {
it matches repositoriesRegex
}.forEach { repositoryName ->
try {
val artifactoryUser = rootProject.ext["$repositoryName.artifactoryUser"]?.toString()
?: throw Exception("no artifactory user found")
val artifactoryPassword = rootProject.ext["$repositoryName.artifactoryPassword"]?.toString()
?: throw Exception("no artifactory password found")
repositoryHandler.maven { repository ->
repository.name = properties["$repository.name"]?.toString()
?: repositoryName.substringAfterLast('.')
val releasesRepoUrl = properties["$repositoryName.release"]?.let {
uri(it.toString())
}
val snapshotRepoUrl = properties["$repositoryName.snapshot"]?.let {
uri(it.toString())
}
val repoUrl = properties["$repositoryName.url"]?.let {
uri(it.toString())
}
repository.url = if (version.toString().endsWith("SNAPSHOT") && snapshotRepoUrl != null) {
snapshotRepoUrl
} else {
releasesRepoUrl
} ?: repoUrl ?: throw Exception("no repo found")
repository.credentials {
it.username = artifactoryUser
it.password = artifactoryPassword
}
}
} catch (e: Exception) {
println(
"cannot publish to repository ${repositoryName.substringAfterLast('.')}:\n" +
"${e.javaClass}: ${e.message}"
)
}
}
}
private fun Project.createMavenPublications(pc: PublicationContainer) {
pc.maybeCreate("maven", MavenPublication::class.java).let { mavenPublication ->
val groupId = project.group.toString()
.ifBlank { ext.properties["project.groupId"]?.toString() }
if (warnGroupIdNotSet(groupId)) {
return
}
val version = getVersionWithProperties()
if (warnVersionNotSet(version)) {
return
}
mavenPublication.groupId = groupId
mavenPublication.artifactId = project.name
mavenPublication.version = version
try {
mavenPublication.from(components.getByName("java"))
} catch (_: Exception) {
}
try {
mavenPublication.artifact(tasks.getByName("kotlinSourcesJar"))
} catch (_: Exception) {
}
}
}
/**
* warn user that group id is not set.
* @return true if group id is not set
* false if group id is set.
*/
private fun Project.warnGroupIdNotSet(groupId: String?) = if (groupId.isNullOrEmpty()) {
println(
"cannot publish to maven of project $this cause group id is not set. " +
"you can specific property \"project.groupId\" to solve it."
)
true
} else {
false
}
/**
* warn user that group id is not set.
* @return true if group id is not set
* false if group id is set.
*/
private fun Project.warnVersionNotSet(version: String?) = if (isEmptyVersion(version)) {
println(
"cannot publish to maven of project $this cause version is not set. " +
"you can specific property \"project.version\" to solve it."
)
true
} else {
false
}
private fun isEmptyVersion(version: String?) = version.isNullOrEmpty() || version == Project.DEFAULT_VERSION
private fun Project.getVersionWithProperties(): String {
var version = this.version.toString()
if (isEmptyVersion(version)) {
version = ext["project.version"]?.toString() ?: Project.DEFAULT_VERSION
}
return version
}

View File

@ -313,7 +313,6 @@ open class RoutedHttpHandler(
fun autoReturn(result: Any?, content: HttpContent, doLog: Boolean = true) {
if (content.finished || result is Unit) return
if (doLog) log?.debug("{}: autoReturn: {}", content.remoteAddress, result)
result ?: return
when (result) {
null -> content.finish(404)
is ByteBuffer -> content.finishText(result)
@ -336,7 +335,6 @@ open class RoutedHttpHandler(
fun finishHtml(result: Any?, content: HttpContent, doLog: Boolean = true) {
if (content.finished || result is Unit) return
if (doLog) log?.debug("{} finishHtml {}", content.remoteAddress, result)
result ?: return
when (result) {
null -> content.finish(404)
is ByteBuffer -> content.finishHtml(result)
@ -355,7 +353,6 @@ open class RoutedHttpHandler(
fun finishText(result: Any?, content: HttpContent, doLog: Boolean = true) {
if (content.finished || result is Unit) return
if (doLog) log?.debug("{} finishText {}", content.remoteAddress, result)
result ?: return
when (result) {
null -> content.finish(404)
is ByteBuffer -> content.finishText(result)
@ -374,7 +371,6 @@ open class RoutedHttpHandler(
fun finishJson(result: Any?, content: HttpContent, doLog: Boolean = true) {
if (content.finished || result is Unit) return
if (doLog) log?.debug("{} finishJson {}", content.remoteAddress, result)
result ?: return
when (result) {
null -> content.finish(404)
is ByteBuffer -> content.finishJson(result)

View File

@ -15,6 +15,7 @@ class NettyChunkedInput(private val chunked: Chunked) : ChunkedInput<ByteBuf> {
override fun length() = chunked.length
override fun isEndOfInput(): Boolean = chunked.endOfInput
@Deprecated("Deprecated in Java", ReplaceWith("readChunk(allocator)"))
override fun readChunk(ctx: ChannelHandlerContext?): ByteBuf = readChunk()
override fun readChunk(allocator: ByteBufAllocator?): ByteBuf = readChunk()