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

com.dingyi.groovy.android.GroovyScriptFactory.kt Maven / Gradle / Ivy

package com.dingyi.groovy.android

import android.util.Log
import com.android.tools.r8.*
import com.android.tools.r8.origin.Origin
import dalvik.system.DexFile
import groovy.lang.GrooidClassLoader
import groovy.lang.Script
import org.codehaus.groovy.control.CompilerConfiguration
import java.io.File

class GroovyScriptFactory(
    private val compileDir: File = AppDataDirGuesser().guess(),
) {

    private fun calculateMd5(bytes: ByteArray): String {
        val md5 = java.security.MessageDigest.getInstance("MD5")
        md5.update(bytes)
        val digest = md5.digest()
        val hexString = StringBuilder()
        for (b in digest) {
            var temp = Integer.toHexString(b.toInt() and 0xFF)
            if (temp.length == 1) {
                temp = "0$temp"
            }
            hexString.append(temp)
        }
        return hexString.toString()
    }


    private fun createScriptClassLoader(
        scriptText: String, outputFile: File,
        fileName: String = "AnonymousGroovyScript",
        classLoader: ClassLoader = this.javaClass.classLoader
    ): EvalResult>> {

        val dexBuilder = D8Command.builder()
            .apply {
                disableDesugaring = true
                minApiLevel = 26
                setOutput(
                    outputFile.toPath(),
                    OutputMode.DexIndexed
                )

            }

        val groovyCompilerConfiguration = CompilerConfiguration()

        val groovyClassLoader = GrooidClassLoader(classLoader, groovyCompilerConfiguration)

        val classNameList = mutableListOf()

        groovyCompilerConfiguration.apply {

            setBytecodePostprocessor { className, classByte ->
                Log.d("GroovyScriptFactory", "className: $className classByte: $classByte")
                dexBuilder.addClassProgramData(classByte, Origin.unknown())
                classNameList.add(className)
                classByte
            }
        }

        groovyClassLoader
            .parseClass(scriptText, fileName)


        D8.run(dexBuilder.build())

        return kotlin.runCatching {
            EvalResult.successWithType(
                DexClassLoader(
                    outputFile
                        .absolutePath, null, null, classLoader
                ) to classNameList
            )
        }.getOrElse {
            EvalResult.failureWithType(it)
        }
    }

    fun createScript(
        scriptText: String, outputFile: File,
        fileName: String = "AnonymousGroovyScript",
        classLoader: ClassLoader = this.javaClass.classLoader
    ): EvalResult