com.flyjingfish.android_aop_plugin.utils.ClassFileUtils.kt Maven / Gradle / Ivy
package com.flyjingfish.android_aop_plugin.utils
import com.flyjingfish.android_aop_plugin.beans.InvokeClass
import com.flyjingfish.android_aop_plugin.scanner_visitor.WovenIntoCode
import com.flyjingfish.android_aop_plugin.utils.Utils.CONVERSIONS_CLASS
import javassist.CannotCompileException
import javassist.CtNewMethod
import javassist.NotFoundException
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking
import org.objectweb.asm.ClassWriter
import org.objectweb.asm.Opcodes.ACC_PUBLIC
import org.objectweb.asm.Opcodes.ALOAD
import org.objectweb.asm.Opcodes.DUP
import org.objectweb.asm.Opcodes.INVOKESPECIAL
import org.objectweb.asm.Opcodes.IRETURN
import org.objectweb.asm.Opcodes.NEW
import org.objectweb.asm.Opcodes.RETURN
import org.objectweb.asm.Opcodes.V1_8
import java.io.ByteArrayInputStream
import java.io.File
import java.util.concurrent.ConcurrentHashMap
object ClassFileUtils {
var reflectInvokeMethod = false
var reflectInvokeMethodStatic = false
var debugMode = false
lateinit var outputDir:File
var outputCacheDir:File ?= null
private val invokeClasses = ConcurrentHashMap>()
private const val INVOKE_METHOD = "invoke"
private const val INVOKE_DESCRIPTOR = "(Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object;"
private const val INVOKE_CLASS = "com.flyjingfish.android_aop_annotation.utils.InvokeMethod"
private const val INVOKE_CLASSES = "com.flyjingfish.android_aop_annotation.utils.InvokeMethods"
fun clear(){
invokeClasses.clear()
}
fun wovenInfoInvokeClass(newClasses: MutableList) :MutableList = runBlocking{
val cacheFiles = mutableListOf()
if (reflectInvokeMethod && !reflectInvokeMethodStatic){
return@runBlocking cacheFiles
}
if (reflectInvokeMethod && reflectInvokeMethodStatic){
for (invokeClasses in invokeClasses) {
val value = invokeClasses.value
val path = outputDir.absolutePath + File.separatorChar +Utils.dotToSlash(invokeClasses.key).adapterOSPath()+".class"
val outFile = File(path)
if (outFile.exists()){
outFile.delete()
}
val cp = ClassPoolUtils.getNewClassPool()
outputCacheDir?.let {
cp.appendClassPath(it.absolutePath)
}
newClasses.forEach {
cp.makeClass(ByteArrayInputStream(it))
}
cp.importPackage(CONVERSIONS_CLASS)
val ctClass = cp.get(invokeClasses.key)
for (invokeClass in value) {
val className = invokeClass.packageName
val invokeBody = invokeClass.invokeBody
try {
val methodName = className.replace(".","_")
val ctMethod =
WovenIntoCode.getCtMethod(ctClass, methodName, INVOKE_DESCRIPTOR)
if (ctMethod != null){
ctMethod.setBody(invokeBody)
}else{
val methodBody =
"public static final Object $methodName(Object target,Object[] vars) " +
invokeBody
val newMethod = CtNewMethod.make(methodBody, ctClass)
ctClass.addMethod(newMethod);
}
} catch (e: NotFoundException) {
throw RuntimeException(e)
} catch (e: CannotCompileException) {
printLog("invokeBody=$invokeBody")
throw RuntimeException(e)
}
}
val classByteData = ctClass.toBytecode()
// //把类数据写入到class文件,这样你就可以把这个类文件打包供其他的人使用
outFile.checkExist()
classByteData.saveFile(outFile)
cacheFiles.add(path)
// ctClass.detach()
}
}else{
val invokeJobs = mutableListOf>()
for (invokeClasses in invokeClasses) {
val value = invokeClasses.value
for (invokeClass in value) {
val className = invokeClass.packageName
val invokeBody = invokeClass.invokeBody
val path = outputDir.absolutePath + File.separatorChar +Utils.dotToSlash(className).adapterOSPath()+".class"
val outFile = File(path)
if (outputCacheDir != null && outFile.exists()){
continue
}
val job = async(Dispatchers.IO) {
// println("invokeClass.methodName="+invokeClass.methodName)
val cp = ClassPoolUtils.getNewClassPool()
outputCacheDir?.let {
cp.appendClassPath(it.absolutePath)
}
newClasses.forEach {
cp.makeClass(ByteArrayInputStream(it))
}
cp.importPackage(CONVERSIONS_CLASS)
val ctClass = cp.get(className)
try {
val ctMethod =
WovenIntoCode.getCtMethod(ctClass, INVOKE_METHOD, INVOKE_DESCRIPTOR)
ctMethod?.setBody(invokeBody)
} catch (e: NotFoundException) {
throw RuntimeException(e)
} catch (e: CannotCompileException) {
printLog("invokeBody=$invokeBody")
throw RuntimeException(e)
}
val classByteData = ctClass.toBytecode()
// //把类数据写入到class文件,这样你就可以把这个类文件打包供其他的人使用
outFile.checkExist()
cacheFiles.add(path)
classByteData.saveFile(outFile)
}
invokeJobs.add(job)
}
// ctClass.detach()
}
invokeJobs.awaitAll()
}
cacheFiles
}
fun createInvokeClass(staticClassName:String, classMethodName:String, invokeBody:String, methodName:String) {
if (reflectInvokeMethod && !reflectInvokeMethodStatic){
return
}
val list = invokeClasses.computeIfAbsent(staticClassName) { mutableListOf() }
synchronized(list){
list.add(InvokeClass(classMethodName,invokeBody,methodName))
}
val className = if (reflectInvokeMethod && reflectInvokeMethodStatic){
staticClassName
}else{
classMethodName
}
if (File(outputDir.absolutePath + File.separatorChar +Utils.dotToSlash(className).adapterOSPath()+".class").exists()){
return
}
val cacheOutFir = outputCacheDir
val outFile = if (cacheOutFir != null){
File(cacheOutFir.absolutePath + File.separatorChar +Utils.dotToSlash(className).adapterOSPath()+".class")
}else{
File(outputDir.absolutePath + File.separatorChar +Utils.dotToSlash(className).adapterOSPath()+".class")
}
if (outFile.exists()){
return
}
outFile.checkExist()
// val className = "$packageName.Invoke${UUID.randomUUID()}"
//新建一个类生成器,COMPUTE_FRAMES,COMPUTE_MAXS这2个参数能够让asm自动更新操作数栈
val cw = ClassWriter(ClassWriter.COMPUTE_FRAMES or ClassWriter.COMPUTE_MAXS)
//生成一个public的类,类路径是com.study.Human
if (reflectInvokeMethod && reflectInvokeMethodStatic){
cw.visit(
V1_8,
ACC_PUBLIC, Utils.dotToSlash(className), null, "java/lang/Object", arrayOf(Utils.dotToSlash(
INVOKE_CLASSES))
)
}else{
cw.visit(
V1_8,
ACC_PUBLIC, Utils.dotToSlash(className), null, "java/lang/Object", arrayOf(Utils.dotToSlash(INVOKE_CLASS))
)
}
var mv = cw.visitMethod(ACC_PUBLIC, "", "()V", null, null)
mv.visitVarInsn(ALOAD, 0)
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false)
mv.visitInsn(RETURN)
mv.visitMaxs(0, 0) //更新操作数栈
mv.visitEnd() //一定要有visitEnd
mv = cw.visitMethod(ACC_PUBLIC, INVOKE_METHOD, INVOKE_DESCRIPTOR, null, null)
mv.visitCode();
mv.visitTypeInsn(NEW, "java/lang/Object")
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "", "()V", false)
mv.visitInsn(IRETURN)
mv.visitMaxs(0, 0)
mv.visitEnd()
cw.toByteArray().saveFile(outFile)
}
fun deleteInvokeClass(className:String) {
if (reflectInvokeMethod && !reflectInvokeMethodStatic){
return
}
for (invokeClass in invokeClasses) {
val value = invokeClass.value ?: continue
val iterator = value.iterator()
while (iterator.hasNext()){
val item = iterator.next()
if (item.packageName == className){
iterator.remove()
break
}
}
}
outputCacheDir?.let {
val file = File(it.absolutePath +File.separatorChar +Utils.dotToSlash(className).adapterOSPath()+".class")
if (file.exists()){
file.delete()
}
}
val file = File(outputDir.absolutePath + File.separatorChar +Utils.dotToSlash(className).adapterOSPath()+".class")
if (file.exists()){
file.delete()
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy