main.name.remal.gradle_plugins.dsl.extensions.java.lang.Class.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gradle-plugins-kotlin-dsl Show documentation
Show all versions of gradle-plugins-kotlin-dsl Show documentation
Remal Gradle plugins: gradle-plugins-kotlin-dsl
The newest version!
package name.remal.gradle_plugins.dsl.extensions
import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import com.google.common.cache.LoadingCache
import groovy.lang.Closure
import groovy.lang.MetaClass
import name.remal.accept
import name.remal.buildMap
import name.remal.findCompatibleMethod
import name.remal.gradle_plugins.api.BuildTimeConstants.getClassDescriptor
import name.remal.isFinal
import name.remal.lambda.Function1
import name.remal.lambda.VoidFunction1
import name.remal.toInsnNode
import name.remal.toLoadVarInsn
import name.remal.toReturnInsn
import name.remal.uncheckedCast
import org.codehaus.groovy.runtime.DefaultGroovyMethods
import org.gradle.api.Action
import org.gradle.api.internal.GeneratedSubclass
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.ClassWriter.COMPUTE_FRAMES
import org.objectweb.asm.ClassWriter.COMPUTE_MAXS
import org.objectweb.asm.Opcodes.ACC_FINAL
import org.objectweb.asm.Opcodes.ACC_PRIVATE
import org.objectweb.asm.Opcodes.ACC_PUBLIC
import org.objectweb.asm.Opcodes.ACC_SYNTHETIC
import org.objectweb.asm.Opcodes.ALOAD
import org.objectweb.asm.Opcodes.ARETURN
import org.objectweb.asm.Opcodes.ATHROW
import org.objectweb.asm.Opcodes.CHECKCAST
import org.objectweb.asm.Opcodes.DUP
import org.objectweb.asm.Opcodes.GETFIELD
import org.objectweb.asm.Opcodes.INVOKEINTERFACE
import org.objectweb.asm.Opcodes.INVOKESPECIAL
import org.objectweb.asm.Opcodes.INVOKEVIRTUAL
import org.objectweb.asm.Opcodes.NEW
import org.objectweb.asm.Opcodes.PUTFIELD
import org.objectweb.asm.Opcodes.RETURN
import org.objectweb.asm.Opcodes.V1_8
import org.objectweb.asm.Type.VOID_TYPE
import org.objectweb.asm.Type.getDescriptor
import org.objectweb.asm.Type.getInternalName
import org.objectweb.asm.Type.getMethodDescriptor
import org.objectweb.asm.Type.getType
import org.objectweb.asm.tree.AnnotationNode
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.FieldInsnNode
import org.objectweb.asm.tree.FieldNode
import org.objectweb.asm.tree.InsnList
import org.objectweb.asm.tree.InsnNode
import org.objectweb.asm.tree.LabelNode
import org.objectweb.asm.tree.MethodInsnNode
import org.objectweb.asm.tree.MethodNode
import org.objectweb.asm.tree.TypeInsnNode
import org.objectweb.asm.tree.VarInsnNode
import org.objectweb.asm.util.CheckClassAdapter
import java.io.InputStream
import java.lang.reflect.Method
import java.util.function.Consumer
import javax.inject.Inject
import java.util.function.Function as JFunction
import kotlin.Function1 as KFunction1
import kotlin.reflect.KFunction1 as KReflectFunction1
val Class<*>.groovyMetaClass: MetaClass get() = DefaultGroovyMethods.getMetaClass(this)
val Class<*>.canBeConfigurer: Boolean
get() = Action::class.java.isAssignableFrom(this)
|| Closure::class.java.isAssignableFrom(this)
|| Consumer::class.java.isAssignableFrom(this)
|| JFunction::class.java.isAssignableFrom(this)
|| KFunction1::class.java.isAssignableFrom(this)
|| KReflectFunction1::class.java.isAssignableFrom(this)
|| Function1::class.java.isAssignableFrom(this)
|| VoidFunction1::class.java.isAssignableFrom(this)
fun Class<*>.getRequiredResource(resourceName: String) = unwrapGradleGenerated().let { clazz ->
clazz.getResource(resourceName)
?: throw IllegalStateException("$clazz: classpath resource can't be found: $resourceName")
}
fun Class<*>.getRequiredResourceAsStream(resourceName: String): InputStream = getRequiredResource(resourceName)
.openConnection()
.apply { useCaches = false }
.getInputStream()
fun Class.unwrapGradleGenerated(): Class {
var clazz: Class<*> = this
while (GeneratedSubclass::class.java.isAssignableFrom(clazz)) {
clazz = clazz.superclass
}
return clazz.uncheckedCast()
}
private val delegateClassOfCache: LoadingCache, Class<*>>, Class<*>> = CacheBuilder.newBuilder()
.weakValues()
.build(object : CacheLoader, Class<*>>, Class<*>>() {
@Suppress("LongMethod")
override fun load(key: Pair, Class<*>>): Class<*> {
val (currentClass, delegateClass) = key
if (!currentClass.isInterface) throw IllegalArgumentException("$currentClass is not an interface")
if (currentClass.isFinal) throw IllegalArgumentException("$currentClass is final class")
val methodsMapping = run {
val processedMethods = hashSetOf()
buildMap {
currentClass.methods.forEach { method ->
if (!processedMethods.add(method.name + getMethodDescriptor(method))) return@forEach
if (method.declaringClass == Any::class.java) return@forEach
if (method.name == "toString" && method.parameterCount == 0) return@forEach
val delegateMethod = delegateClass.findCompatibleMethod(method.returnType, method.name, *method.parameterTypes)
put(method, delegateMethod)
}
}
}
val classNode = ClassNode().apply classNode@{
version = V1_8
access = ACC_PUBLIC
name = getInternalName(currentClass) + "\$\$Delegate\$\$" + delegateClass.name.replace('.', '_')
superName = getInternalName(Any::class.java)
interfaces = mutableListOf(getInternalName(currentClass))
fields = mutableListOf()
val delegateFieldName = "\$\$delegate"
val delegateFieldDescriptor = getDescriptor(delegateClass)
fields.add(FieldNode(ACC_PRIVATE or ACC_FINAL or ACC_SYNTHETIC, delegateFieldName, delegateFieldDescriptor, null, null))
methods = mutableListOf()
methods.add(MethodNode(ACC_PUBLIC, "", getMethodDescriptor(VOID_TYPE, getType(delegateClass)), null, null).apply methodNode@{
visibleAnnotations = mutableListOf(AnnotationNode(getClassDescriptor(Inject::class.java)))
instructions = InsnList().apply instructions@{
add(LabelNode())
add(VarInsnNode(ALOAD, 0))
add(MethodInsnNode(INVOKESPECIAL, [email protected], [email protected], "()V", false))
add(VarInsnNode(ALOAD, 0))
add(VarInsnNode(ALOAD, 1))
add(FieldInsnNode(PUTFIELD, [email protected], delegateFieldName, delegateFieldDescriptor))
add(InsnNode(RETURN))
}
maxStack = 1
maxLocals = 1
})
methodsMapping.forEach { method, delegateMethod ->
if (delegateMethod == null && method.isDefault) return@forEach
methods.add(MethodNode(ACC_PUBLIC, method.name, getMethodDescriptor(method), null, null).apply methodNode@{
instructions = InsnList().apply instructions@{
add(LabelNode())
if (delegateMethod != null) {
add(VarInsnNode(ALOAD, 0))
add(FieldInsnNode(GETFIELD, [email protected], delegateFieldName, delegateFieldDescriptor))
method.parameterTypes.forEachIndexed { index, type ->
add(getType(type).toLoadVarInsn(index + 1))
val delegateType = delegateMethod.parameterTypes[index]
if (delegateType != type) {
add(TypeInsnNode(CHECKCAST, getInternalName(delegateType)))
}
}
if (delegateClass.isInterface) {
add(MethodInsnNode(INVOKEINTERFACE, getInternalName(delegateClass), delegateMethod.name, getMethodDescriptor(delegateMethod), true))
} else {
add(MethodInsnNode(INVOKEVIRTUAL, getInternalName(delegateClass), delegateMethod.name, getMethodDescriptor(delegateMethod), false))
}
if (method.returnType != delegateMethod.returnType) {
add(TypeInsnNode(CHECKCAST, getInternalName(method.returnType)))
}
add(getType(method.returnType).toReturnInsn())
} else {
add(TypeInsnNode(NEW, getInternalName(UnsupportedOperationException::class.java)))
add(InsnNode(DUP))
add("$delegateClass doesn't have any compatible method with $method".toInsnNode())
add(
MethodInsnNode(
INVOKESPECIAL,
getInternalName(UnsupportedOperationException::class.java),
"",
getMethodDescriptor(VOID_TYPE, getType(String::class.java)),
false
)
)
add(InsnNode(ATHROW))
}
}
maxStack = 1
maxLocals = 1
})
}
methods.add(MethodNode(ACC_PUBLIC, "toString", getMethodDescriptor(getType(String::class.java)), null, null).apply methodNode@{
instructions = InsnList().apply instructions@{
add(LabelNode())
add(VarInsnNode(ALOAD, 0))
add(FieldInsnNode(GETFIELD, [email protected], delegateFieldName, delegateFieldDescriptor))
if (delegateClass.isInterface) {
add(MethodInsnNode(INVOKEINTERFACE, getInternalName(delegateClass), [email protected], [email protected], true))
} else {
add(MethodInsnNode(INVOKEVIRTUAL, getInternalName(delegateClass), [email protected], [email protected], false))
}
add(InsnNode(ARETURN))
}
maxStack = 1
maxLocals = 1
})
}
val classWriter = ClassWriter(COMPUTE_MAXS or COMPUTE_FRAMES)
classNode.accept(classWriter)
val bytecode = classWriter.toByteArray()
ClassReader(bytecode).accept(CheckClassAdapter(ClassWriter(0)))
return currentClass.classLoader.defineClass(classNode.name.replace('/', '.'), bytecode)
}
})
fun Class.getDelegateClassOf(other: Class<*>): Class {
return delegateClassOfCache[this to other].uncheckedCast()
}
fun Class.newDelegateOf(obj: Any): T {
return getDelegateClassOf(obj.javaClass).getConstructor(obj.javaClass).newInstance(obj)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy