Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
name.remal.gradle_plugins.dsl.extensions.org.gradle.api.reporting.ReportContainer.kt Maven / Gradle / Ivy
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 com.google.common.reflect.TypeToken
import name.remal.*
import org.gradle.api.Task
import org.gradle.api.reporting.Report
import org.gradle.api.reporting.ReportContainer
import org.gradle.api.reporting.SingleFileReport
import org.gradle.api.reporting.internal.TaskGeneratedSingleFileReport
import org.gradle.api.reporting.internal.TaskReportContainer
import org.objectweb.asm.ClassReader
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.ClassWriter.COMPUTE_MAXS
import org.objectweb.asm.Opcodes.*
import org.objectweb.asm.Type.*
import org.objectweb.asm.tree.*
import org.objectweb.asm.util.CheckClassAdapter
import java.lang.reflect.Method
import java.lang.reflect.ParameterizedType
import javax.inject.Inject
private val taskReportContainerClassCache: LoadingCache>, Class>> = CacheBuilder.newBuilder()
.weakValues()
.build(object : CacheLoader>, Class>>() {
@Suppress("LongMethod")
override fun load(containerClass: Class>): Class> {
if (!containerClass.isInterface) throw IllegalArgumentException("$containerClass is not an interface")
val reportSuperType: Class = run {
val type = TypeToken.of(containerClass).getSupertype(ReportContainer::class.java).type
if (type !is ParameterizedType) throw IllegalStateException("$type is not instance of ParameterizedType")
type.actualTypeArguments[0].asClass().uncheckedCast()
}
data class ReportInfo(
val name: String,
val type: Class<*>,
val implType: Class<*>?,
val method: Method
)
val reportInfos = containerClass.methods.asSequence()
.filter { !it.declaringClass.isAssignableFrom(ReportContainer::class.java) }
.filter { it.parameterCount == 0 }
.filter { Report::class.java.isAssignableFrom(it.returnType) }
.filter { it.name.run { length >= 4 && startsWith("get") && get(3) == get(3).toUpperCase() } }
.map {
ReportInfo(
name = it.name.substring(3).fromUpperCamelToLowerCamel(),
type = it.returnType,
implType = when (it.returnType) {
SingleFileReport::class.java -> TaskGeneratedSingleFileReport::class.java
else -> null
},
method = it
)
}
.distinctBy { it.name }
.toList()
val classNode = ClassNode().apply classNode@{
version = V1_8
access = ACC_PUBLIC
name = getInternalName(containerClass) + "\$\$TaskReportContainer"
superName = getInternalName(TaskReportContainer::class.java)
interfaces = mutableListOf(getInternalName(containerClass))
methods = mutableListOf()
methods.add(MethodNode(ACC_PUBLIC, "", getMethodDescriptor(VOID_TYPE, getType(Task::class.java)), null, null).apply methodNode@{
visibleAnnotations = mutableListOf(AnnotationNode(getDescriptor(Inject::class.java)))
instructions = InsnList().apply instructions@{
add(LabelNode())
add(VarInsnNode(ALOAD, 0))
add(reportSuperType.toInsnNode())
add(VarInsnNode(ALOAD, 1))
val decoratorClass = TaskReportContainer::class.java.classLoader.tryLoadClass("org.gradle.api.internal.CollectionCallbackActionDecorator")
val decoratorCtor = decoratorClass?.let { TaskReportContainer::class.java.findConstructor(Class::class.java, Task::class.java, it) }
if (decoratorCtor != null) {
add(FieldInsnNode(GETSTATIC, getInternalName(decoratorClass), "NOOP", getDescriptor(decoratorClass)))
add(MethodInsnNode(INVOKESPECIAL, [email protected] , [email protected] , getConstructorDescriptor(decoratorCtor), false))
} else {
add(MethodInsnNode(INVOKESPECIAL, [email protected] , [email protected] , getMethodDescriptor(VOID_TYPE, getType(Class::class.java), getType(Task::class.java)), false))
}
reportInfos.forEach { info ->
if (info.implType == null) {
if (info.method.isDefault) {
return@forEach
} else {
throw IllegalStateException("Report implementation type can't be defined for ${info.method}")
}
}
add(VarInsnNode(ALOAD, 0))
add(info.implType.toInsnNode())
add(InsnNode(ICONST_2))
add(TypeInsnNode(ANEWARRAY, getInternalName(Any::class.java)))
add(InsnNode(DUP))
add(InsnNode(ICONST_0))
add(info.name.toInsnNode())
add(InsnNode(AASTORE))
add(InsnNode(DUP))
add(InsnNode(ICONST_1))
add(VarInsnNode(ALOAD, 1))
add(InsnNode(AASTORE))
add(MethodInsnNode(INVOKEVIRTUAL, [email protected] , "add", getMethodDescriptor(getType(Report::class.java), getType(Class::class.java), getType(Array::class.java)), false))
}
add(InsnNode(RETURN))
}
maxStack = 1
maxLocals = 1
})
reportInfos.forEach { info ->
if (info.implType == null) return@forEach
methods.add(MethodNode(ACC_PUBLIC, info.method.name, getMethodDescriptor(info.method), null, null).apply methodNode@{
instructions = InsnList().apply instructions@{
add(LabelNode())
add(VarInsnNode(ALOAD, 0))
add(info.name.toInsnNode())
add(MethodInsnNode(INVOKEVIRTUAL, [email protected] , "getByName", getMethodDescriptor(getType(Any::class.java), getType(String::class.java)), false))
add(TypeInsnNode(CHECKCAST, getInternalName(info.type)))
add(InsnNode(ARETURN))
}
maxStack = 1
maxLocals = 1
})
}
}
val classWriter = ClassWriter(COMPUTE_MAXS)
classNode.accept(classWriter)
val bytecode = classWriter.toByteArray()
ClassReader(bytecode).accept(CheckClassAdapter(ClassWriter(0)))
return ClassLoader::class.java.getDeclaredMethod("defineClass", String::class.java, ByteArray::class.java, Int::class.javaPrimitiveType, Int::class.javaPrimitiveType)
.apply { isAccessible = true }
.invoke(containerClass.classLoader, classNode.name.replace('/', '.'), bytecode, 0, bytecode.size)
.uncheckedCast()
}
})
val > Class.taskReportContainerClass: Class get() = taskReportContainerClassCache[this.uncheckedCast()].uncheckedCast()