Skip to content

Commit

Permalink
Merge pull request #27 from FlyJingFish/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
FlyJingFish authored Aug 13, 2024
2 parents a66378c + 243cab9 commit 013c9d9
Show file tree
Hide file tree
Showing 23 changed files with 358 additions and 48 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
plugins {
//必须项 👇 apply 设置为 true 自动为所有module“预”配置debugMode,false则按下边步骤五的方式二
id "io.github.FlyJingFish.AndroidAop.android-aop" version "2.0.6" apply true
id "io.github.FlyJingFish.AndroidAop.android-aop" version "2.0.7" apply true
}
```

Expand All @@ -83,7 +83,7 @@ plugins {
buildscript {
dependencies {
//必须项 👇
classpath 'io.github.FlyJingFish.AndroidAop:android-aop-plugin:2.0.6'
classpath 'io.github.FlyJingFish.AndroidAop:android-aop-plugin:2.0.7'
}
}
// 👇加上这句自动为所有module“预”配置debugMode,不加则按下边步骤五的方式二
Expand Down Expand Up @@ -122,7 +122,7 @@ apply plugin: 'android.aop' //最好放在最后一行
//必须项 👇
plugins {
...
id "io.github.FlyJingFish.AndroidAop.android-aop" version "2.0.6"//最好放在最后一行
id "io.github.FlyJingFish.AndroidAop.android-aop" version "2.0.7"//最好放在最后一行
}
```

Expand All @@ -148,17 +148,17 @@ plugins {
dependencies {
//必须项 👇
implementation 'io.github.FlyJingFish.AndroidAop:android-aop-core:2.0.6'
implementation 'io.github.FlyJingFish.AndroidAop:android-aop-annotation:2.0.6'
implementation 'io.github.FlyJingFish.AndroidAop:android-aop-core:2.0.7'
implementation 'io.github.FlyJingFish.AndroidAop:android-aop-annotation:2.0.7'
//必须项 👇如果您项目内已经有了这项不用加也可以
implementation 'androidx.appcompat:appcompat:1.3.0' // 至少在1.3.0及以上
//非必须项 👇,如果你想自定义切面需要用到,⚠️支持Java和Kotlin代码写的切面
ksp 'io.github.FlyJingFish.AndroidAop:android-aop-ksp:2.0.6'
ksp 'io.github.FlyJingFish.AndroidAop:android-aop-ksp:2.0.7'
//非必须项 👇,如果你想自定义切面需要用到,⚠️只适用于Java代码写的切面
annotationProcessor 'io.github.FlyJingFish.AndroidAop:android-aop-processor:2.0.6'
annotationProcessor 'io.github.FlyJingFish.AndroidAop:android-aop-processor:2.0.7'
//⚠️上边的 android-aop-ksp 和 android-aop-processor 二选一
}
```
Expand Down
14 changes: 7 additions & 7 deletions README_EN.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ Using the **plugins DSL**:
plugins {
//Required item 👇 apply is set to true to automatically apply debugMode to all modules, if false, follow step 5 below.
id "io.github.FlyJingFish.AndroidAop.android-aop" version "2.0.6" apply true
id "io.github.FlyJingFish.AndroidAop.android-aop" version "2.0.7" apply true
}
```

Expand All @@ -80,7 +80,7 @@ plugins {
buildscript {
dependencies {
//Required items 👇
classpath 'io.github.FlyJingFish.AndroidAop:android-aop-plugin:2.0.6'
classpath 'io.github.FlyJingFish.AndroidAop:android-aop-plugin:2.0.7'
}
}
//👇Add this sentence to automatically apply debugMode to all modules. If not, follow step 5 below.
Expand Down Expand Up @@ -119,7 +119,7 @@ Add directly to ```build.gradle``` of **app**
//Required items 👇
plugins {
...
id "io.github.FlyJingFish.AndroidAop.android-aop" version "2.0.6"
id "io.github.FlyJingFish.AndroidAop.android-aop" version "2.0.7"
}
```

Expand Down Expand Up @@ -149,16 +149,16 @@ plugins {
dependencies {
//Required items 👇
implementation 'io.github.FlyJingFish.AndroidAop:android-aop-core:2.0.6'
implementation 'io.github.FlyJingFish.AndroidAop:android-aop-annotation:2.0.6'
implementation 'io.github.FlyJingFish.AndroidAop:android-aop-core:2.0.7'
implementation 'io.github.FlyJingFish.AndroidAop:android-aop-annotation:2.0.7'
//Required item 👇If you already have this item in your project, you don’t need to add it.
implementation 'androidx.appcompat:appcompat:1.3.0' // At least in 1.3.0 and above
//Optional 👇, if you want to customize aspects, you need to use them, ⚠️supports aspects written in Java and Kotlin code
ksp 'io.github.FlyJingFish.AndroidAop:android-aop-ksp:2.0.6'
ksp 'io.github.FlyJingFish.AndroidAop:android-aop-ksp:2.0.7'
//Optional 👇, if you want to customize aspects, you need to use them, ⚠️only applies to aspects written in Java code
annotationProcessor 'io.github.FlyJingFish.AndroidAop:android-aop-processor:2.0.6'
annotationProcessor 'io.github.FlyJingFish.AndroidAop:android-aop-processor:2.0.7'
//⚠️Choose one of the above android-aop-ksp and android-aop-processor
}
```
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.flyjingfish.android_aop_annotation.anno

/**
* 定义替换类的构造方法调用的切面的注解,使用这个注解的方法的类需要使用 [AndroidAopReplaceClass] 注解,否则无用
* [wiki 文档使用说明](https://github.com/FlyJingFish/AndroidAOP/wiki/@AndroidAopReplaceClass)
*/
@Target(
AnnotationTarget.FUNCTION,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER
)
@Retention(AnnotationRetention.BINARY)
annotation class AndroidAopReplaceNew(
val value: String
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import com.flyjingfish.android_aop_annotation.anno.AndroidAopModifyExtendsClass
import com.flyjingfish.android_aop_annotation.anno.AndroidAopPointCut
import com.flyjingfish.android_aop_annotation.anno.AndroidAopReplaceClass
import com.flyjingfish.android_aop_annotation.anno.AndroidAopReplaceMethod
import com.flyjingfish.android_aop_annotation.anno.AndroidAopReplaceNew
import com.flyjingfish.android_aop_annotation.aop_anno.AopClass
import com.flyjingfish.android_aop_annotation.aop_anno.AopCollectMethod
import com.flyjingfish.android_aop_annotation.aop_anno.AopMatchClassMethod
Expand Down Expand Up @@ -68,7 +69,8 @@ class AndroidAopSymbolProcessor(private val codeGenerator: CodeGenerator,
// logger.error("---------AndroidAopSymbolProcessor---------")
val ret1 = processPointCut(resolver)
val ret2 = processMatch(resolver)
processReplaceMethod(resolver)
processReplaceMethod(resolver,AndroidAopReplaceMethod::class.qualifiedName!!)
processReplaceMethod(resolver,AndroidAopReplaceNew::class.qualifiedName!!)
val ret3 = processReplace(resolver)
val ret4 = processModifyExtendsClass(resolver)
val ret5 = processCollectMethod(resolver)
Expand Down Expand Up @@ -378,7 +380,41 @@ class AndroidAopSymbolProcessor(private val codeGenerator: CodeGenerator,
return symbols.filter { !it.validate() }.toList()
}

private fun processReplaceMethod(resolver: Resolver): List<KSAnnotated> {
private fun processReplaceMethod(resolver: Resolver,qualifiedName :String): List<KSAnnotated> {
val symbols :Sequence<KSAnnotated> =
resolver.getSymbolsWithAnnotation(qualifiedName)
for (symbol in symbols) {
val annotationMap = getAnnotation(symbol)

if (symbol.origin == Origin.KOTLIN){
if (!annotationMap.containsKey("@JvmStatic")){
var className = "${(symbol as KSFunctionDeclaration).packageName.asString()}."
var parent = symbol.parent
while (parent !is KSFile){
className = "$className$parent."
parent = parent?.parent
}
throw IllegalArgumentException("注意:函数$className${symbol} 必须添加 @JvmStatic 注解")
}

}else if (symbol.origin == Origin.JAVA){
if (symbol is KSFunctionDeclaration){
if (symbol.functionKind != FunctionKind.STATIC){
var className = "${symbol.packageName.asString()}."
var parent = symbol.parent
while (parent !is KSFile){
className = "$className$parent."
parent = parent?.parent
}
throw IllegalArgumentException("注意:方法$className${symbol} 必须是静态方法")
}
}
}
}
return symbols.filter { !it.validate() }.toList()
}

private fun processReplaceNew(resolver: Resolver): List<KSAnnotated> {
val symbols :Sequence<KSAnnotated> =
resolver.getSymbolsWithAnnotation(AndroidAopReplaceMethod::class.qualifiedName!!)
for (symbol in symbols) {
Expand Down Expand Up @@ -412,6 +448,7 @@ class AndroidAopSymbolProcessor(private val codeGenerator: CodeGenerator,
return symbols.filter { !it.validate() }.toList()
}


private fun processModifyExtendsClass(resolver: Resolver): List<KSAnnotated> {
val symbols :Sequence<KSAnnotated> =
resolver.getSymbolsWithAnnotation(AndroidAopModifyExtendsClass::class.qualifiedName!!)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
package com.flyjingfish.android_aop_plugin.beans

import org.objectweb.asm.Type

data class ReplaceMethodInfo(
var oldOwner: String,
var oldMethodName: String,
var oldMethodDesc: String,
var newOwner: String,
var newMethodName: String,
var newMethodDesc: String
var newMethodDesc: String,
var replaceType:ReplaceType = ReplaceType.METHOD,
var newClassName :String = ""
){
private var isCallNew:Boolean?=null
enum class ReplaceType{
METHOD,INIT,NEW
}
fun getReplaceKey():String{
return oldOwner + oldMethodName + oldMethodDesc
}
Expand All @@ -18,4 +26,19 @@ data class ReplaceMethodInfo(
return oldOwner.isNotEmpty() && oldMethodName.isNotEmpty() && oldMethodDesc.isNotEmpty()
&& newOwner.isNotEmpty() && newMethodName.isNotEmpty() && newMethodDesc.isNotEmpty()
}

fun isCallNew():Boolean{
val oldCallNew = isCallNew
if (oldCallNew != null){
return oldCallNew
}
val callNew = if (replaceType == ReplaceType.NEW){
val type = Type.getReturnType(newMethodDesc)
type.descriptor != "V"
}else{
false
}
isCallNew = callNew
return callNew
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package com.flyjingfish.android_aop_plugin.scanner_visitor

import com.flyjingfish.android_aop_plugin.beans.ReplaceMethodInfo
import com.flyjingfish.android_aop_plugin.utils.InitConfig
import com.flyjingfish.android_aop_plugin.utils.WovenInfoUtils
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes


class MethodReplaceInvokeAdapter(private val className:String,
class MethodReplaceInvokeAdapter(private val className:String,private val superName: String,
private val methodNameDesc:String, methodVisitor: MethodVisitor?) :
MethodVisitor(Opcodes.ASM9, methodVisitor) {
val isConstructorMethod = methodNameDesc.startsWith("<init>(")

interface OnResultListener{
fun onBack()
Expand Down Expand Up @@ -36,21 +38,36 @@ class MethodReplaceInvokeAdapter(private val className:String,
}
if (isReplaceClass && replaceMethodInfo != null
&& (!className.contains(replaceMethodInfo.newOwner) || methodNameDesc != "${replaceMethodInfo.newMethodName}${replaceMethodInfo.newMethodDesc}")) {
val shouldReplaceDesc: String = if (opcode == Opcodes.INVOKESTATIC) {
descriptor
val shouldReplace: Boolean = if (replaceMethodInfo.replaceType == ReplaceMethodInfo.ReplaceType.INIT||replaceMethodInfo.replaceType == ReplaceMethodInfo.ReplaceType.NEW) {
if (isConstructorMethod){
!(owner == superName && name == "<init>")
}else{
descriptor == replaceMethodInfo.oldMethodDesc
}
} else if (opcode == Opcodes.INVOKESTATIC) {
descriptor == replaceMethodInfo.newMethodDesc
} else {
descriptor.replace("(", "(L${replaceMethodInfo.oldOwner};")
descriptor.replace("(", "(L${replaceMethodInfo.oldOwner};") == replaceMethodInfo.newMethodDesc
}
if (shouldReplaceDesc == replaceMethodInfo.newMethodDesc) {
if (shouldReplace) {
if (replaceMethodInfo.replaceType == ReplaceMethodInfo.ReplaceType.INIT) {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
}else if (replaceMethodInfo.replaceType == ReplaceMethodInfo.ReplaceType.NEW && replaceMethodInfo.isCallNew()) {
super.visitMethodInsn(opcode, replaceMethodInfo.newClassName, name, descriptor, isInterface)
}
InitConfig.addReplaceMethodInfo(replaceMethodInfo)
// 注意,最后一个参数是false,会不会太武断呢?
super.visitMethodInsn(
Opcodes.INVOKESTATIC,
replaceMethodInfo.newOwner,
replaceMethodInfo.newMethodName,
replaceMethodInfo.newMethodDesc,
false
)
if (replaceMethodInfo.replaceType == ReplaceMethodInfo.ReplaceType.NEW && !replaceMethodInfo.isCallNew()){
super.visitMethodInsn(opcode, replaceMethodInfo.newClassName, name, descriptor, isInterface)
}else{
// 注意,最后一个参数是false,会不会太武断呢?
super.visitMethodInsn(
Opcodes.INVOKESTATIC,
replaceMethodInfo.newOwner,
replaceMethodInfo.newMethodName,
replaceMethodInfo.newMethodDesc,
false
)
}
onResultListener?.onBack()
} else {
super.visitMethodInsn(opcode, owner, name, descriptor, isInterface)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ open class MethodReplaceInvokeVisitor(
classVisitor: ClassVisitor
) : ReplaceBaseClassVisitor(classVisitor) {
lateinit var className: String
lateinit var superName: String
var replaced = false
override fun visit(
version: Int,
Expand All @@ -19,6 +20,7 @@ open class MethodReplaceInvokeVisitor(
) {
super.visit(version, access, name, signature, superName, interfaces)
className = name
this.superName = superName
}
override fun visitMethod(
access: Int,
Expand All @@ -30,7 +32,7 @@ open class MethodReplaceInvokeVisitor(
var mv: MethodVisitor? = super.visitMethod(access, name, descriptor, signature, exceptions)

if (mv != null && access.isHasMethodBody()) {
mv = MethodReplaceInvokeAdapter(className,"$name$descriptor",mv)
mv = MethodReplaceInvokeAdapter(className,superName,"$name$descriptor",mv)
mv.onResultListener = object : MethodReplaceInvokeAdapter.OnResultListener{
override fun onBack() {
replaced = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ class SearchAopMethodVisitor(val onCallBackMethod: OnCallBackMethod?) :
companion object {
const val REPLACE_POINT =
"Lcom/flyjingfish/android_aop_annotation/anno/AndroidAopReplaceMethod"
const val NEW_POINT =
"Lcom/flyjingfish/android_aop_annotation/anno/AndroidAopReplaceNew"
val initClassnamePattern = Pattern.compile("<init>\\(.*?\\)")
}
open inner class MyMethodVisitor
(
Expand Down Expand Up @@ -288,12 +291,15 @@ class SearchAopMethodVisitor(val onCallBackMethod: OnCallBackMethod?) :
}
}

if (descriptor.contains(REPLACE_POINT) && replaceTargetClassName != null && access.isStaticMethod()){
if ((descriptor.contains(REPLACE_POINT) || descriptor.contains(NEW_POINT)) && replaceTargetClassName != null && access.isStaticMethod()){

val replaceMethodInfo = ReplaceMethodInfo(
replaceTargetClassName!!,"","",
className,methodname,methoddescriptor
)
if (descriptor.contains(NEW_POINT)){
replaceMethodInfo.replaceType = ReplaceMethodInfo.ReplaceType.NEW
}
return ReplaceMethodVisitor(replaceMethodInfo)
}
return super.visitAnnotation(descriptor, visible)
Expand All @@ -314,15 +320,50 @@ class SearchAopMethodVisitor(val onCallBackMethod: OnCallBackMethod?) :
val name = methodname
if (!name.isNullOrEmpty()){
try {
val methodInfo = getMethodInfo(name)
val fanMatcher = initClassnamePattern.matcher(name)
val newName = if (name.startsWith("<init>") && fanMatcher.find()){
if (replaceMethodInfo.replaceType == ReplaceMethodInfo.ReplaceType.METHOD){
replaceMethodInfo.replaceType = ReplaceMethodInfo.ReplaceType.INIT
}
"void $name"
}else{
name
}

val methodInfo = getMethodInfo(newName)
if (methodInfo != null && methodInfo.checkAvailable()){
val methodText = methodInfo.returnType + " " + methodInfo.name + methodInfo.paramTypes

val method = Method.getMethod(methodText)
replaceMethodInfo.oldMethodName = method.name
replaceMethodInfo.oldMethodDesc = method.descriptor
if (replaceMethodInfo.checkAvailable()){
onCallBackMethod?.onBackReplaceMethodInfo(replaceMethodInfo)
if (replaceMethodInfo.replaceType == ReplaceMethodInfo.ReplaceType.INIT){
val returnType = Type.getReturnType(replaceMethodInfo.newMethodDesc)
val returnTypeClassName = returnType.descriptor
val paramsTypes = Type.getArgumentTypes(replaceMethodInfo.newMethodDesc)
val paramType0 : Type? = if (paramsTypes.size == 1){
paramsTypes[0]
}else null

if (returnTypeClassName.startsWith("L") && returnTypeClassName.endsWith(";") && paramType0?.className == slashToDotClassName(replaceMethodInfo.oldOwner)){
onCallBackMethod?.onBackReplaceMethodInfo(replaceMethodInfo)
}
}else if (replaceMethodInfo.replaceType == ReplaceMethodInfo.ReplaceType.NEW){
val paramsTypes = Type.getArgumentTypes(replaceMethodInfo.newMethodDesc)
val paramType0 : Type? = if (paramsTypes.size == 1){
paramsTypes[0]
}else null

val paramType0ClassName = paramType0?.descriptor ?:""

if (paramType0ClassName.startsWith("L") && paramType0ClassName.endsWith(";")){
replaceMethodInfo.newClassName = paramType0ClassName.substring(1,paramType0ClassName.length - 1)
onCallBackMethod?.onBackReplaceMethodInfo(replaceMethodInfo)
}
}else{
onCallBackMethod?.onBackReplaceMethodInfo(replaceMethodInfo)
}
}
}

Expand Down
Loading

0 comments on commit 013c9d9

Please sign in to comment.