All Downloads are FREE. Search and download functionalities are using the official Maven repository.

main.name.remal.gradle_plugins.dsl.utils.newFileCollection.kt Maven / Gradle / Ivy

The newest version!
package name.remal.gradle_plugins.dsl.utils

import name.remal.KotlinAllOpen
import name.remal.accept
import name.remal.gradle_plugins.dsl.extensions.defineClass
import name.remal.isFinal
import name.remal.toLoadVarInsn
import org.gradle.api.file.FileCollection
import org.gradle.api.internal.file.AbstractFileCollection
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_PUBLIC
import org.objectweb.asm.Opcodes.ALOAD
import org.objectweb.asm.Opcodes.ARETURN
import org.objectweb.asm.Opcodes.GETSTATIC
import org.objectweb.asm.Opcodes.INVOKESPECIAL
import org.objectweb.asm.Opcodes.RETURN
import org.objectweb.asm.Opcodes.V1_8
import org.objectweb.asm.Type.OBJECT
import org.objectweb.asm.Type.getArgumentTypes
import org.objectweb.asm.Type.getConstructorDescriptor
import org.objectweb.asm.Type.getDescriptor
import org.objectweb.asm.Type.getInternalName
import org.objectweb.asm.Type.getMethodDescriptor
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.FieldInsnNode
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.VarInsnNode
import org.objectweb.asm.util.CheckClassAdapter
import java.io.File
import java.lang.reflect.Constructor

fun newFileCollection(filesFactory: () -> Iterable): FileCollection {
    val lazyFilesSet = lazy { filesFactory().toSet() }
    return implClassCtor.newInstance(lazyFilesSet)
}

fun newFileCollection(files: Iterable) = newFileCollection({ files })
fun newFileCollection(vararg files: File) = newFileCollection(files.toList())


private val implClassCtor: Constructor by lazy {
    return@lazy implClass.getDeclaredConstructor(Lazy::class.java)
}

private val implClass: Class by lazy> {
    val packageName = BaseFileCollectionImpl::class.java.name.substringBeforeLast('.')
    val className = "$packageName.FileCollectionImpl"

    val classNode = ClassNode().apply classNode@{
        version = V1_8
        access = ACC_PUBLIC
        name = classNameToClassInternalName(className)
        superName = getInternalName(BaseFileCollectionImpl::class.java)
        interfaces = listOf()

        methods = mutableListOf()

        val superCtor = BaseFileCollectionImpl::class.java.getConstructor(Lazy::class.java)
        val superCtorDescr = getConstructorDescriptor(superCtor)
        methods.add(MethodNode(ACC_PUBLIC, "", superCtorDescr, null, null).apply methodNode@{
            instructions = InsnList().apply instructions@{
                add(LabelNode())
                add(VarInsnNode(ALOAD, 0))
                getArgumentTypes(superCtorDescr).forEachIndexed { index, type ->
                    if (type.sort == OBJECT) {
                        add(VarInsnNode(ALOAD, index + 1))
                    } else {
                        add(type.toLoadVarInsn(index + 1))
                    }
                }
                add(MethodInsnNode(INVOKESPECIAL, [email protected], [email protected], superCtorDescr, false))
                add(InsnNode(RETURN))
            }
            maxStack = 1
            maxLocals = 1
        })

        val getBuildDependenciesMethod = BaseFileCollectionImpl::class.java.getMethod("getBuildDependencies")
        if (!getBuildDependenciesMethod.isFinal) {
            methods.add(MethodNode(ACC_PUBLIC, getBuildDependenciesMethod.name, getMethodDescriptor(getBuildDependenciesMethod), null, null).apply methodNode@{
                instructions = InsnList().apply instructions@{
                    add(LabelNode())
                    val taskDependenciesInstanceField = EmptyTaskDependencies::class.java.getField("INSTANCE")
                    add(FieldInsnNode(GETSTATIC, getInternalName(taskDependenciesInstanceField.declaringClass), taskDependenciesInstanceField.name, getDescriptor(taskDependenciesInstanceField.type)))
                    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@lazy BaseFileCollectionImpl::class.java.classLoader.defineClass(classNode.name.replace('/', '.'), bytecode)
}

@KotlinAllOpen
private abstract class BaseFileCollectionImpl(private val filesSet: Lazy>) : AbstractFileCollection() {
    override fun getFiles(): Set = filesSet.value
    override fun getDisplayName() = "file collection"
    override fun equals(other: Any?) = other is FileCollection && other.files == files
    override fun hashCode() = 1 + files.hashCode()
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy