Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
DiDi
DoraemonKit
提交
e738fe27
D
DoraemonKit
项目概览
DiDi
/
DoraemonKit
10 个月 前同步成功
通知
166
Star
19623
Fork
3062
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
D
DoraemonKit
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
e738fe27
编写于
8月 07, 2020
作者:
J
jackjintai
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
android:更新打包规则
上级
7dce9e91
变更
23
隐藏空白更改
内联
并排
Showing
23 changed file
with
2317 addition
and
0 deletion
+2317
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExt.kt
...ain/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExt.kt
+114
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExtUtil.kt
...kotlin/com/didichuxing/doraemonkit/plugin/DoKitExtUtil.kt
+174
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitPlugin.kt
.../kotlin/com/didichuxing/doraemonkit/plugin/DoKitPlugin.kt
+141
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitTransformInvocation.kt
...idichuxing/doraemonkit/plugin/DoKitTransformInvocation.kt
+226
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitTransformTaskExecutionListener.kt
...doraemonkit/plugin/DoKitTransformTaskExecutionListener.kt
+23
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/ServiceLoader.kt
...otlin/com/didichuxing/doraemonkit/plugin/ServiceLoader.kt
+76
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/asmtransformer/BaseDoKitAsmTransformer.kt
...aemonkit/plugin/asmtransformer/BaseDoKitAsmTransformer.kt
+82
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/asmtransformer/DoKitAsmTransformer.kt
.../doraemonkit/plugin/asmtransformer/DoKitAsmTransformer.kt
+16
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/BigImgTransformer.kt
.../doraemonkit/plugin/classtransformer/BigImgTransformer.kt
+152
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/CommTransformer.kt
...ng/doraemonkit/plugin/classtransformer/CommTransformer.kt
+362
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/EnterMethodStackTransformer.kt
...it/plugin/classtransformer/EnterMethodStackTransformer.kt
+169
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/GlobalSlowMethodTransformer.kt
...it/plugin/classtransformer/GlobalSlowMethodTransformer.kt
+137
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/MethodStackDepTransformer.kt
...nkit/plugin/classtransformer/MethodStackDepTransformer.kt
+149
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/UrlConnectionTransformer.kt
...onkit/plugin/classtransformer/UrlConnectionTransformer.kt
+67
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/CommExt.kt
...n/com/didichuxing/doraemonkit/plugin/extension/CommExt.kt
+34
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/DoKitExt.kt
.../com/didichuxing/doraemonkit/plugin/extension/DoKitExt.kt
+42
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/SlowMethodExt.kt
...didichuxing/doraemonkit/plugin/extension/SlowMethodExt.kt
+120
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/processor/DoKitPluginConfigProcessor.kt
...oraemonkit/plugin/processor/DoKitPluginConfigProcessor.kt
+68
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/stack_method/MethodStackNode.kt
...huxing/doraemonkit/plugin/stack_method/MethodStackNode.kt
+18
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/stack_method/MethodStackNodeUtil.kt
...ng/doraemonkit/plugin/stack_method/MethodStackNodeUtil.kt
+30
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/DoKitBaseTransform.kt
...huxing/doraemonkit/plugin/transform/DoKitBaseTransform.kt
+86
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/DoKitCommTransform.kt
...huxing/doraemonkit/plugin/transform/DoKitCommTransform.kt
+12
-0
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/DoKitDependTransform.kt
...xing/doraemonkit/plugin/transform/DoKitDependTransform.kt
+19
-0
未找到文件。
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExt.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin
import
com.android.build.gradle.api.BaseVariant
import
com.didiglobal.booster.transform.TransformContext
import
org.objectweb.asm.Opcodes.*
import
org.objectweb.asm.tree.InsnList
import
org.objectweb.asm.tree.InsnNode
import
org.objectweb.asm.tree.MethodInsnNode
import
org.objectweb.asm.tree.MethodNode
import
java.io.File
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/19-18:00
* 描 述:dokit 对象扩展
* 修订历史:
* ================================================
*/
fun
MethodNode
.
isGetSetMethod
():
Boolean
{
var
ignoreCount
=
0
val
iterator
=
instructions
.
iterator
()
while
(
iterator
.
hasNext
())
{
val
insnNode
=
iterator
.
next
()
val
opcode
=
insnNode
.
opcode
if
(-
1
==
opcode
)
{
continue
}
if
(
opcode
!=
GETFIELD
&&
opcode
!=
GETSTATIC
&&
opcode
!=
H_GETFIELD
&&
opcode
!=
H_GETSTATIC
&&
opcode
!=
RETURN
&&
opcode
!=
ARETURN
&&
opcode
!=
DRETURN
&&
opcode
!=
FRETURN
&&
opcode
!=
LRETURN
&&
opcode
!=
IRETURN
&&
opcode
!=
PUTFIELD
&&
opcode
!=
PUTSTATIC
&&
opcode
!=
H_PUTFIELD
&&
opcode
!=
H_PUTSTATIC
&&
opcode
>
SALOAD
)
{
if
(
name
.
equals
(
"<init>"
)
&&
opcode
==
INVOKESPECIAL
)
{
ignoreCount
++
if
(
ignoreCount
>
1
)
{
return
false
}
continue
}
return
false
}
}
return
true
}
fun
MethodNode
.
isSingleMethod
():
Boolean
{
val
iterator
=
instructions
.
iterator
()
while
(
iterator
.
hasNext
())
{
val
insnNode
=
iterator
.
next
()
val
opcode
=
insnNode
.
opcode
if
(-
1
==
opcode
)
{
continue
}
else
if
(
INVOKEVIRTUAL
<=
opcode
&&
opcode
<=
INVOKEDYNAMIC
)
{
return
false
}
}
return
true
}
fun
MethodNode
.
isEmptyMethod
():
Boolean
{
val
iterator
=
instructions
.
iterator
()
while
(
iterator
.
hasNext
())
{
val
insnNode
=
iterator
.
next
()
val
opcode
=
insnNode
.
opcode
return
if
(-
1
==
opcode
)
{
continue
}
else
{
false
}
}
return
true
}
fun
InsnList
.
getMethodExitInsnNodes
():
Sequence
<
InsnNode
>?
{
return
this
.
iterator
()
?.
asSequence
()
?.
filterIsInstance
(
InsnNode
::
class
.
java
)
?.
filter
{
it
.
opcode
==
RETURN
||
it
.
opcode
==
IRETURN
||
it
.
opcode
==
FRETURN
||
it
.
opcode
==
ARETURN
||
it
.
opcode
==
LRETURN
||
it
.
opcode
==
DRETURN
||
it
.
opcode
==
ATHROW
}
}
fun
BaseVariant
.
isRelease
():
Boolean
{
if
(
this
.
name
.
contains
(
"release"
)
||
this
.
name
.
contains
(
"Release"
))
{
return
true
}
return
false
}
fun
TransformContext
.
isRelease
():
Boolean
{
if
(
this
.
name
.
contains
(
"release"
)
||
this
.
name
.
contains
(
"Release"
))
{
return
true
}
return
false
}
fun
String
.
println
()
{
if
(
DoKitExtUtil
.
dokitLogSwitchOpen
())
{
println
(
"[dokit plugin]===>$this"
)
}
}
fun
File
.
lastPath
():
String
{
return
this
.
path
.
split
(
"/"
).
last
()
}
val
MethodInsnNode
.
ownerClassName
:
String
get
()
=
owner
.
replace
(
'/'
,
'.'
)
\ No newline at end of file
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitExtUtil.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin
import
com.didichuxing.doraemonkit.plugin.extension.CommExt
import
com.didichuxing.doraemonkit.plugin.extension.DoKitExt
import
com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import
org.gradle.api.Project
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/3/24-14:58
* 描 述:
* 修订历史:
* ================================================
*/
object
DoKitExtUtil
{
//private var mApplicationId: String = ""
/**
* dokit 插件开关 字段权限必须为public 否则无法进行赋值
*/
var
DOKIT_PLUGIN_SWITCH
=
true
var
DOKIT_LOG_SWITCH
=
false
/**
* 默认函数调用为5级
*/
var
STACK_METHOD_LEVEL
=
5
/**
* 慢函数默认关闭
*/
var
SLOW_METHOD_SWITCH
=
false
/**
* 慢函数策略 默认为函数调用栈策略
*/
var
SLOW_METHOD_STRATEGY
=
SlowMethodExt
.
STRATEGY_STACK
private
val
applications
:
MutableSet
<
String
>
=
mutableSetOf
()
var
commExt
=
CommExt
()
private
set
val
slowMethodExt
=
SlowMethodExt
()
fun
dokitPluginSwitchOpen
():
Boolean
{
return
DOKIT_PLUGIN_SWITCH
}
fun
dokitLogSwitchOpen
():
Boolean
{
return
DOKIT_LOG_SWITCH
}
fun
dokitSlowMethodSwitchOpen
():
Boolean
{
return
SLOW_METHOD_SWITCH
}
/**
* 初始化
*
* @param dokitEx dokitExtension
* @param appExtension appExtension
*/
fun
init
(
dokitEx
:
DoKitExt
,
applicationId
:
String
)
{
//设置普通的配置
commExt
=
dokitEx
.
comm
//slowMethodExt.strategy = dokitEx.slowMethod.strategy
//slowMethodExt.methodSwitch = dokitEx.slowMethod.methodSwitch
/**
* ============慢函数普通策略的配置 start==========
*/
slowMethodExt
.
normalMethod
.
thresholdTime
=
dokitEx
.
slowMethod
.
normalMethod
.
thresholdTime
//设置慢函数普通策略插装包名
slowMethodExt
.
normalMethod
.
packageNames
.
clear
()
for
(
packageName
in
dokitEx
.
slowMethod
.
normalMethod
.
packageNames
)
{
slowMethodExt
.
normalMethod
.
packageNames
.
add
(
packageName
)
}
//添加默认的包名
if
(
applicationId
.
isNotEmpty
())
{
if
(
slowMethodExt
.
normalMethod
.
packageNames
.
isEmpty
())
{
slowMethodExt
.
normalMethod
.
packageNames
.
add
(
applicationId
)
}
}
//设置慢函数普通策略插装包名黑名单
slowMethodExt
.
normalMethod
.
methodBlacklist
.
clear
()
for
(
blackStr
in
dokitEx
.
slowMethod
.
normalMethod
.
methodBlacklist
)
{
slowMethodExt
.
normalMethod
.
methodBlacklist
.
add
(
blackStr
)
}
/**
* ============慢函数普通策略的配置end==========
*/
/**
* ============慢函数stack策略的配置 start==========
*/
slowMethodExt
.
stackMethod
.
thresholdTime
=
dokitEx
.
slowMethod
.
stackMethod
.
thresholdTime
slowMethodExt
.
stackMethod
.
enterMethods
.
clear
()
//添加默认的入口函数
for
(
application
in
applications
)
{
val
attachBaseContextMethodName
=
"$application.attachBaseContext"
val
onCreateMethodName
=
"$application.onCreate"
slowMethodExt
.
stackMethod
.
enterMethods
.
add
(
attachBaseContextMethodName
)
slowMethodExt
.
stackMethod
.
enterMethods
.
add
(
onCreateMethodName
)
}
for
(
methodName
in
dokitEx
.
slowMethod
.
stackMethod
.
enterMethods
)
{
slowMethodExt
.
stackMethod
.
enterMethods
.
add
(
methodName
)
}
/**
* ============慢函数stack策略的配置 end==========
*/
}
fun
setApplications
(
applications
:
MutableSet
<
String
>)
{
if
(
applications
.
isEmpty
())
{
return
}
this
.
applications
.
clear
()
for
(
application
in
applications
)
{
this
.
applications
.
add
(
application
)
}
}
fun
ignorePackageNames
(
className
:
String
):
Boolean
{
//命中白名单返回false
for
(
packageName
in
whitePackageNames
)
{
if
(
className
.
startsWith
(
packageName
,
true
))
{
return
false
}
}
//命中黑名单返回true
for
(
packageName
in
blackPackageNames
)
{
if
(
className
.
startsWith
(
packageName
,
true
))
{
return
true
}
}
return
false
}
/**
* 白名单
*/
private
val
whitePackageNames
=
arrayOf
(
"com.didichuxing.doraemonkit.DoraemonKit"
,
"com.didichuxing.doraemonkit.DoraemonKitReal"
)
/**
* 黑名单
*/
private
val
blackPackageNames
=
arrayOf
(
"com.didichuxing.doraemonkit."
,
"kotlin."
,
"java."
,
"android."
,
"androidx."
)
fun
log
(
tag
:
String
,
className
:
String
,
methodName
:
String
,
access
:
Int
,
desc
:
String
,
signature
:
String
,
thresholdTime
:
Int
)
{
if
(
DOKIT_LOG_SWITCH
)
{
println
(
"$tag===matched====> className===$className methodName===$methodName access===$access desc===$desc signature===$signature thresholdTime===$thresholdTime"
)
}
}
}
\ No newline at end of file
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitPlugin.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin
import
com.android.build.gradle.AppExtension
import
com.android.build.gradle.LibraryExtension
import
com.didichuxing.doraemonkit.plugin.extension.DoKitExt
import
com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import
com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNodeUtil
import
com.didichuxing.doraemonkit.plugin.transform.DoKitCommTransform
import
com.didichuxing.doraemonkit.plugin.transform.DoKitDependTransform
import
com.didiglobal.booster.gradle.getAndroid
import
com.didiglobal.booster.gradle.getProperty
import
org.gradle.api.Plugin
import
org.gradle.api.Project
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/14-10:01
* 描 述:
* 修订历史:
* ================================================
*/
class
DoKitPlugin
:
Plugin
<
Project
>
{
override
fun
apply
(
project
:
Project
)
{
//创建指定扩展 并将project 传入构造函数
val
doKitExt
=
project
.
extensions
.
create
(
"dokitExt"
,
DoKitExt
::
class
.
java
)
// val debug = project.gradle.startParameter.taskNames.any {
// it.contains("debug") || it.contains("Debug")
// }
// if (!debug) {
// return
// }
project
.
gradle
.
addListener
(
DoKitTransformTaskExecutionListener
(
project
))
//println("project.plugins===>${project.plugins}")
/**
* when 也可以用来取代 if-else if链。
* 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:
*/
/**
* 作用域函数:let、run、with、apply 以及 also
* 它们的唯一目的是在对象的上下文中执行代码块
* 由于作用域函数本质上都非常相似,因此了解它们之间的区别很重要。每个作用域函数之间有两个主要区别:
* 引用上下文对象的方式:
* 作为 lambda 表达式的接收者(this)或者作为 lambda 表达式的参数(it)
* run、with 以及 apply 通过关键字 this 引用上下文对象
* let 及 also 将上下文对象作为 lambda 表达式参数
*
* 返回值:
* apply 及 also 返回上下文对象。
* let、run 及 with 返回 lambda 表达式结果.
*/
/**
* 函数 对象引用 返回值 是否是扩展函数
* let it Lambda 表达式结果 是
* run this Lambda 表达式结果 是
* run - Lambda 表达式结果 不是:调用无需上下文对象
* with this Lambda 表达式结果 不是:把上下文对象当做参数
* apply this 上下文对象 是
* also it 上下文对象 是
*/
/**
*对一个非空(non-null)对象执行 lambda 表达式:let
*将表达式作为变量引入为局部作用域中:let
*对象配置:apply
*对象配置并且计算结果:run
*在需要表达式的地方运行语句:非扩展的 run
*附加效果:also
*一个对象的一组函数调用:with
*/
when
{
project
.
plugins
.
hasPlugin
(
"com.android.application"
)
||
project
.
plugins
.
hasPlugin
(
"com.android.dynamic-feature"
)
->
{
project
.
getAndroid
<
AppExtension
>().
let
{
androidExt
->
val
pluginSwitch
=
project
.
getProperty
(
"DOKIT_PLUGIN_SWITCH"
,
true
)
val
logSwitch
=
project
.
getProperty
(
"DOKIT_LOG_SWITCH"
,
false
)
val
slowMethodSwitch
=
project
.
getProperty
(
"DOKIT_METHOD_SWITCH"
,
false
)
val
slowMethodStrategy
=
project
.
getProperty
(
"DOKIT_METHOD_STRATEGY"
,
0
)
val
methodStackLevel
=
project
.
getProperty
(
"DOKIT_METHOD_STACK_LEVEL"
,
5
)
DoKitExtUtil
.
DOKIT_PLUGIN_SWITCH
=
pluginSwitch
DoKitExtUtil
.
DOKIT_LOG_SWITCH
=
logSwitch
DoKitExtUtil
.
SLOW_METHOD_SWITCH
=
slowMethodSwitch
DoKitExtUtil
.
SLOW_METHOD_STRATEGY
=
slowMethodStrategy
DoKitExtUtil
.
STACK_METHOD_LEVEL
=
methodStackLevel
"application module ${project.name} is executing..."
.
println
()
MethodStackNodeUtil
.
METHOD_STACK_KEYS
.
clear
()
if
(
DoKitExtUtil
.
DOKIT_PLUGIN_SWITCH
)
{
//注册transform
androidExt
.
registerTransform
(
DoKitCommTransform
(
project
))
if
(
slowMethodSwitch
&&
slowMethodStrategy
==
SlowMethodExt
.
STRATEGY_STACK
)
{
MethodStackNodeUtil
.
METHOD_STACK_KEYS
.
add
(
0
,
mutableSetOf
<
String
>())
val
methodStackRange
=
1
until
methodStackLevel
if
(
methodStackLevel
>
1
)
{
for
(
index
in
methodStackRange
)
{
MethodStackNodeUtil
.
METHOD_STACK_KEYS
.
add
(
index
,
mutableSetOf
<
String
>())
androidExt
.
registerTransform
(
DoKitDependTransform
(
project
,
index
))
}
}
}
}
//项目评估完毕回调
project
.
afterEvaluate
{
loadVariantProcessors
(
project
).
let
{
processors
->
androidExt
.
applicationVariants
.
forEach
{
variant
->
processors
.
forEach
{
processor
->
processor
.
process
(
variant
)
}
}
}
}
}
}
project
.
plugins
.
hasPlugin
(
"com.android.library"
)
->
{
project
.
getAndroid
<
LibraryExtension
>().
let
{
libraryExt
->
"library module ${project.name} is executing..."
.
println
()
if
(
DoKitExtUtil
.
DOKIT_PLUGIN_SWITCH
)
{
libraryExt
.
registerTransform
(
DoKitCommTransform
(
project
))
}
project
.
afterEvaluate
{
loadVariantProcessors
(
project
).
let
{
processors
->
libraryExt
.
libraryVariants
.
forEach
{
variant
->
processors
.
forEach
{
processor
->
processor
.
process
(
variant
)
}
}
}
}
}
}
}
}
}
\ No newline at end of file
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitTransformInvocation.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin
import
com.android.build.api.transform.Context
import
com.android.build.api.transform.DirectoryInput
import
com.android.build.api.transform.Format
import
com.android.build.api.transform.JarInput
import
com.android.build.api.transform.QualifiedContent
import
com.android.build.api.transform.SecondaryInput
import
com.android.build.api.transform.Status.ADDED
import
com.android.build.api.transform.Status.CHANGED
import
com.android.build.api.transform.Status.NOTCHANGED
import
com.android.build.api.transform.Status.REMOVED
import
com.android.build.api.transform.TransformInput
import
com.android.build.api.transform.TransformInvocation
import
com.android.build.api.transform.TransformOutputProvider
import
com.android.dx.command.dexer.Main
import
com.didichuxing.doraemonkit.plugin.transform.DoKitBaseTransform
import
com.didiglobal.booster.gradle.*
import
com.didiglobal.booster.kotlinx.NCPU
import
com.didiglobal.booster.kotlinx.file
import
com.didiglobal.booster.kotlinx.green
import
com.didiglobal.booster.kotlinx.red
import
com.didiglobal.booster.transform.AbstractKlassPool
import
com.didiglobal.booster.transform.ArtifactManager
import
com.didiglobal.booster.transform.TransformContext
import
com.didiglobal.booster.transform.artifacts
import
com.didiglobal.booster.transform.util.transform
import
java.io.File
import
java.net.URI
import
java.util.concurrent.*
/**
* Represents a delegate of TransformInvocation
*
* @author johnsonlee
*/
internal
class
DoKitTransformInvocation
(
private
val
delegate
:
TransformInvocation
,
internal
val
transform
:
DoKitBaseTransform
)
:
TransformInvocation
by
delegate
,
TransformContext
,
ArtifactManager
{
private
val
project
=
transform
.
project
private
val
outputs
=
CopyOnWriteArrayList
<
File
>()
override
val
name
:
String
=
delegate
.
context
.
variantName
override
val
projectDir
:
File
=
project
.
projectDir
override
val
buildDir
:
File
=
project
.
buildDir
override
val
temporaryDir
:
File
=
delegate
.
context
.
temporaryDir
override
val
reportsDir
:
File
=
File
(
buildDir
,
"reports"
).
also
{
it
.
mkdirs
()
}
override
val
bootClasspath
=
delegate
.
bootClasspath
override
val
compileClasspath
=
delegate
.
compileClasspath
override
val
runtimeClasspath
=
delegate
.
runtimeClasspath
override
val
artifacts
=
this
override
val
klassPool
:
AbstractKlassPool
=
object
:
AbstractKlassPool
(
compileClasspath
,
transform
.
bootKlassPool
)
{}
override
val
applicationId
=
delegate
.
applicationId
override
val
originalApplicationId
=
delegate
.
originalApplicationId
override
val
isDebuggable
=
variant
.
buildType
.
isDebuggable
override
val
isDataBindingEnabled
=
delegate
.
isDataBindingEnabled
override
fun
hasProperty
(
name
:
String
)
=
project
.
hasProperty
(
name
)
@Suppress
(
"UNCHECKED_CAST"
)
override
fun
<
T
>
getProperty
(
name
:
String
,
default
:
T
):
T
=
project
.
properties
[
name
]
as
?
T
?:
default
override
fun
get
(
type
:
String
)
=
variant
.
artifacts
.
get
(
type
)
internal
fun
doFullTransform
()
=
doTransform
(
this
::
transformFully
)
internal
fun
doIncrementalTransform
()
=
doTransform
(
this
::
transformIncrementally
)
private
fun
onPreTransform
()
{
transform
.
transformers
.
forEach
{
it
.
onPreTransform
(
this
)
}
}
private
fun
onPostTransform
()
{
transform
.
transformers
.
forEach
{
it
.
onPostTransform
(
this
)
}
}
private
fun
doTransform
(
block
:
(
ExecutorService
)
->
Iterable
<
Future
<
*
>>)
{
this
.
outputs
.
clear
()
this
.
onPreTransform
()
val
executor
=
Executors
.
newFixedThreadPool
(
NCPU
)
try
{
block
(
executor
).
forEach
{
it
.
get
()
}
}
finally
{
executor
.
shutdown
()
executor
.
awaitTermination
(
1
,
TimeUnit
.
HOURS
)
}
this
.
onPostTransform
()
if
(
transform
.
verifyEnabled
)
{
this
.
doVerify
()
}
}
private
fun
transformFully
(
executor
:
ExecutorService
)
=
this
.
inputs
.
map
{
it
.
jarInputs
+
it
.
directoryInputs
}.
flatten
().
map
{
input
->
executor
.
submit
{
val
format
=
if
(
input
is
DirectoryInput
)
Format
.
DIRECTORY
else
Format
.
JAR
outputProvider
?.
let
{
provider
->
project
.
logger
.
info
(
"Transforming ${input.file}"
)
input
.
transform
(
provider
.
getContentLocation
(
input
.
name
,
input
.
contentTypes
,
input
.
scopes
,
format
))
}
}
}
private
fun
transformIncrementally
(
executor
:
ExecutorService
)
=
this
.
inputs
.
map
{
input
->
input
.
jarInputs
.
filter
{
it
.
status
!=
NOTCHANGED
}.
map
{
jarInput
->
executor
.
submit
{
doIncrementalTransform
(
jarInput
)
}
}
+
input
.
directoryInputs
.
filter
{
it
.
changedFiles
.
isNotEmpty
()
}.
map
{
dirInput
->
val
base
=
dirInput
.
file
.
toURI
()
executor
.
submit
{
doIncrementalTransform
(
dirInput
,
base
)
}
}
}.
flatten
()
@Suppress
(
"NON_EXHAUSTIVE_WHEN"
)
private
fun
doIncrementalTransform
(
jarInput
:
JarInput
)
{
when
(
jarInput
.
status
)
{
REMOVED
->
jarInput
.
file
.
delete
()
CHANGED
,
ADDED
->
{
project
.
logger
.
info
(
"Transforming ${jarInput.file}"
)
outputProvider
?.
let
{
provider
->
jarInput
.
transform
(
provider
.
getContentLocation
(
jarInput
.
name
,
jarInput
.
contentTypes
,
jarInput
.
scopes
,
Format
.
JAR
))
}
}
}
}
@Suppress
(
"NON_EXHAUSTIVE_WHEN"
)
private
fun
doIncrementalTransform
(
dirInput
:
DirectoryInput
,
base
:
URI
)
{
dirInput
.
changedFiles
.
forEach
{
(
file
,
status
)
->
when
(
status
)
{
REMOVED
->
{
project
.
logger
.
info
(
"Deleting $file"
)
outputProvider
?.
let
{
provider
->
provider
.
getContentLocation
(
dirInput
.
name
,
dirInput
.
contentTypes
,
dirInput
.
scopes
,
Format
.
DIRECTORY
).
parentFile
.
listFiles
()
?.
asSequence
()
?.
filter
{
it
.
isDirectory
}
?.
map
{
File
(
it
,
dirInput
.
file
.
toURI
().
relativize
(
file
.
toURI
()).
path
)
}
?.
filter
{
it
.
exists
()
}
?.
forEach
{
it
.
delete
()
}
}
file
.
delete
()
}
ADDED
,
CHANGED
->
{
project
.
logger
.
info
(
"Transforming $file"
)
outputProvider
?.
let
{
provider
->
val
root
=
provider
.
getContentLocation
(
dirInput
.
name
,
dirInput
.
contentTypes
,
dirInput
.
scopes
,
Format
.
DIRECTORY
)
val
output
=
File
(
root
,
base
.
relativize
(
file
.
toURI
()).
path
)
outputs
+=
output
file
.
transform
(
output
)
{
bytecode
->
bytecode
.
transform
()
}
}
}
}
}
}
private
fun
doVerify
()
{
outputs
.
sortedBy
(
File
::
nameWithoutExtension
).
forEach
{
output
->
val
dex
=
temporaryDir
.
file
(
output
.
name
)
val
args
=
Main
.
Arguments
().
apply
{
numThreads
=
NCPU
debug
=
true
warnings
=
true
emptyOk
=
true
multiDex
=
true
jarOutput
=
true
optimize
=
false
minSdkVersion
=
variant
.
extension
.
defaultConfig
.
targetSdkVersion
.
apiLevel
fileNames
=
arrayOf
(
output
.
absolutePath
)
outName
=
dex
.
absolutePath
}
val
rc
=
try
{
Main
.
run
(
args
)
}
catch
(
t
:
Throwable
)
{
t
.
printStackTrace
()
-
1
}
println
(
"${if (rc != 0) red("
✗
") else green("
✓
")} $output"
)
dex
.
deleteRecursively
()
}
}
private
fun
QualifiedContent
.
transform
(
output
:
File
)
{
outputs
+=
output
this
.
file
.
transform
(
output
)
{
bytecode
->
bytecode
.
transform
()
}
}
private
fun
ByteArray
.
transform
():
ByteArray
{
return
transform
.
transformers
.
fold
(
this
)
{
bytes
,
transformer
->
transformer
.
transform
(
this
@DoKitTransformInvocation
,
bytes
)
}
}
}
\ No newline at end of file
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/DoKitTransformTaskExecutionListener.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin
import
com.android.build.gradle.internal.pipeline.TransformTask
import
com.didiglobal.booster.kotlinx.call
import
com.didiglobal.booster.kotlinx.get
import
org.gradle.api.Project
import
org.gradle.api.Task
import
org.gradle.api.execution.TaskExecutionAdapter
/**
* @author neighbWang
*/
class
DoKitTransformTaskExecutionListener
(
private
val
project
:
Project
)
:
TaskExecutionAdapter
()
{
override
fun
beforeExecute
(
task
:
Task
)
{
task
.
takeIf
{
it
.
project
==
project
&&
it
is
TransformTask
&&
it
.
transform
.
scopes
.
isNotEmpty
()
}
?.
run
{
task
[
"outputStream"
]
?.
call
<
Unit
>(
"init"
)
}
}
}
\ No newline at end of file
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/ServiceLoader.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin
import
com.didiglobal.booster.task.spi.VariantProcessor
import
com.didiglobal.booster.transform.Transformer
import
org.gradle.api.Project
import
java.net.URL
import
java.nio.charset.StandardCharsets
import
java.util.ServiceConfigurationError
internal
interface
ServiceLoader
<
T
>
{
fun
load
(
vararg
args
:
Any
):
List
<
T
>
}
internal
class
ServiceLoaderFactory
<
T
>(
private
val
classLoader
:
ClassLoader
,
private
val
service
:
Class
<
T
>)
{
fun
newServiceLoader
(
vararg
types
:
Class
<
*
>)
=
object
:
ServiceLoader
<
T
>
{
@Suppress
(
"UNCHECKED_CAST"
)
override
fun
load
(
vararg
args
:
Any
)
=
classLoader
.
getResources
(
"META-INF/services/${service.name}"
)
?.
asSequence
()
?.
map
(
::
parse
)
?.
flatten
()
?.
toSet
()
?.
map
{
provider
->
try
{
val
providerClass
=
Class
.
forName
(
provider
,
false
,
classLoader
)
if
(!
service
.
isAssignableFrom
(
providerClass
))
{
throw
ServiceConfigurationError
(
"Provider $provider not a subtype"
)
}
try
{
providerClass
.
getConstructor
(*
types
).
newInstance
(*
args
)
as
T
}
catch
(
e
:
NoSuchMethodException
)
{
providerClass
.
newInstance
()
as
T
}
}
catch
(
e
:
ClassNotFoundException
)
{
throw
ServiceConfigurationError
(
"Provider $provider not found"
)
}
}
?:
emptyList
()
}
}
internal
inline
fun
<
reified
T
>
newServiceLoader
(
classLoader
:
ClassLoader
,
vararg
types
:
Class
<
*
>)
=
ServiceLoaderFactory
(
classLoader
,
T
::
class
.
java
).
newServiceLoader
(*
types
)
/**
* Load [Transformer]s with the specified [classLoader]
*/
@Throws
(
ServiceConfigurationError
::
class
)
internal
fun
loadTransformers
(
classLoader
:
ClassLoader
)
=
newServiceLoader
<
Transformer
>(
classLoader
,
ClassLoader
::
class
.
java
).
load
(
classLoader
)
/**
* Load [VariantProcessor]s with the specified [classLoader]
*/
@Throws
(
ServiceConfigurationError
::
class
)
internal
fun
loadVariantProcessors
(
project
:
Project
)
=
newServiceLoader
<
VariantProcessor
>(
project
.
buildscript
.
classLoader
,
Project
::
class
.
java
).
load
(
project
)
@Throws
(
ServiceConfigurationError
::
class
)
private
fun
parse
(
u
:
URL
)
=
try
{
u
.
openStream
().
bufferedReader
(
StandardCharsets
.
UTF_8
).
readLines
().
filter
{
it
.
isNotEmpty
()
&&
it
.
isNotBlank
()
&&
!
it
.
startsWith
(
'#'
)
}.
map
(
String
::
trim
).
filter
(
::
isJavaClassName
)
}
catch
(
e
:
Throwable
)
{
emptyList
<
String
>()
}
private
fun
isJavaClassName
(
text
:
String
):
Boolean
{
if
(!
Character
.
isJavaIdentifierStart
(
text
[
0
]))
{
throw
ServiceConfigurationError
(
"Illegal provider-class name: $text"
)
}
for
(
i
in
1
until
text
.
length
)
{
val
cp
=
text
.
codePointAt
(
i
)
if
(!
Character
.
isJavaIdentifierPart
(
cp
)
&&
cp
!=
'.'
.
toInt
())
{
throw
ServiceConfigurationError
(
"Illegal provider-class name: $text"
)
}
}
return
true
}
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/asmtransformer/BaseDoKitAsmTransformer.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin.asmtransformer
import
com.didiglobal.booster.annotations.Priority
import
com.didiglobal.booster.transform.TransformContext
import
com.didiglobal.booster.transform.Transformer
import
com.didiglobal.booster.transform.asm.ClassTransformer
import
org.objectweb.asm.ClassReader
import
org.objectweb.asm.ClassWriter
import
org.objectweb.asm.tree.ClassNode
import
java.lang.management.ManagementFactory
import
java.lang.management.ThreadMXBean
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/21-16:44
* 描 述:
* 修订历史:
* ================================================
*/
open
class
BaseDoKitAsmTransformer
:
Transformer
{
private
val
threadMxBean
=
ManagementFactory
.
getThreadMXBean
()
private
val
durations
=
mutableMapOf
<
ClassTransformer
,
Long
>()
internal
val
transformers
:
Collection
<
ClassTransformer
>
/**
* For unit test only
*/
constructor
(
vararg
transformers
:
ClassTransformer
)
{
this
.
transformers
=
transformers
.
sortedBy
{
it
.
javaClass
.
getAnnotation
(
Priority
::
class
.
java
)
?.
value
?:
0
}
}
override
fun
onPreTransform
(
context
:
TransformContext
)
{
this
.
transformers
.
forEach
{
transformer
->
this
.
threadMxBean
.
sumCpuTime
(
transformer
)
{
transformer
.
onPreTransform
(
context
)
}
}
}
override
fun
transform
(
context
:
TransformContext
,
bytecode
:
ByteArray
):
ByteArray
{
return
ClassWriter
(
ClassWriter
.
COMPUTE_MAXS
).
also
{
writer
->
this
.
transformers
.
fold
(
ClassNode
().
also
{
klass
->
ClassReader
(
bytecode
).
accept
(
klass
,
0
)
})
{
klass
,
transformer
->
this
.
threadMxBean
.
sumCpuTime
(
transformer
)
{
transformer
.
transform
(
context
,
klass
)
}
}.
accept
(
writer
)
}.
toByteArray
()
}
override
fun
onPostTransform
(
context
:
TransformContext
)
{
this
.
transformers
.
forEach
{
transformer
->
this
.
threadMxBean
.
sumCpuTime
(
transformer
)
{
transformer
.
onPostTransform
(
context
)
}
}
val
w1
=
this
.
durations
.
keys
.
map
{
it
.
javaClass
.
name
.
length
}.
max
()
?:
20
this
.
durations
.
forEach
{
(
transformer
,
ns
)
->
println
(
"${transformer.javaClass.name.padEnd(w1 + 1)}: ${ns / 1000000} ms"
)
}
}
private
fun
<
R
>
ThreadMXBean
.
sumCpuTime
(
transformer
:
ClassTransformer
,
action
:
()
->
R
):
R
{
val
ct0
=
this
.
currentThreadCpuTime
val
result
=
action
()
val
ct1
=
this
.
currentThreadCpuTime
durations
[
transformer
]
=
durations
.
getOrDefault
(
transformer
,
0
)
+
(
ct1
-
ct0
)
return
result
}
}
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/asmtransformer/DoKitAsmTransformer.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin.asmtransformer
import
com.didichuxing.doraemonkit.plugin.classtransformer.MethodStackDepTransformer
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/21-16:44
* 描 述:
* 修订历史:
* ================================================
*/
class
DoKitAsmTransformer
(
private
val
level
:
Int
)
:
BaseDoKitAsmTransformer
(
MethodStackDepTransformer
(
level
))
{
}
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/BigImgTransformer.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin.classtransformer
import
com.didichuxing.doraemonkit.plugin.*
import
com.didiglobal.booster.annotations.Priority
import
com.didiglobal.booster.transform.TransformContext
import
com.didiglobal.booster.transform.asm.ClassTransformer
import
com.didiglobal.booster.transform.asm.className
import
com.google.auto.service.AutoService
import
org.objectweb.asm.Opcodes.*
import
org.objectweb.asm.tree.ClassNode
import
org.objectweb.asm.tree.InsnList
import
org.objectweb.asm.tree.MethodInsnNode
import
org.objectweb.asm.tree.VarInsnNode
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/14-18:07
* 描 述:wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
* 修订历史:
* ================================================
*/
@Priority
(
1
)
@AutoService
(
ClassTransformer
::
class
)
class
BigImgTransformer
:
ClassTransformer
{
override
fun
transform
(
context
:
TransformContext
,
klass
:
ClassNode
):
ClassNode
{
if
(
context
.
isRelease
())
{
return
klass
}
if
(!
DoKitExtUtil
.
dokitPluginSwitchOpen
())
{
return
klass
}
if
(!
DoKitExtUtil
.
commExt
.
bigImgSwitch
)
{
return
klass
}
if
(
DoKitExtUtil
.
ignorePackageNames
(
klass
.
className
))
{
return
klass
}
val
className
=
klass
.
className
//glide
if
(
className
==
"com.bumptech.glide.request.SingleRequest"
)
{
klass
.
methods
.
find
{
methodNode
->
(
methodNode
.
name
==
"init"
||
methodNode
.
name
==
"<init>"
)
&&
methodNode
.
desc
!=
null
}.
let
{
methodNode
->
//函数结束的地方插入
methodNode
?.
instructions
?.
getMethodExitInsnNodes
()
?.
forEach
{
"${context.projectDir.lastPath()}->hook glide succeed: ${className}_${methodNode.name}_${methodNode.desc}"
.
println
()
methodNode
.
instructions
?.
insertBefore
(
it
,
createGlideInsnList
())
}
}
}
//picasso
if
(
className
==
"com.squareup.picasso.Request"
)
{
klass
.
methods
.
find
{
methodNode
->
methodNode
.
name
==
"<init>"
&&
methodNode
.
desc
!=
null
}.
let
{
methodNode
->
//函数结束的地方插入
methodNode
?.
instructions
?.
getMethodExitInsnNodes
()
?.
forEach
{
"${context.projectDir.lastPath()}->hook picasso succeed: ${className}_${methodNode.name}_${methodNode.desc}"
.
println
()
methodNode
.
instructions
?.
insertBefore
(
it
,
createPicassoInsnList
())
}
}
}
//Fresco
if
(
className
==
"com.facebook.imagepipeline.request.ImageRequest"
)
{
klass
.
methods
.
find
{
methodNode
->
methodNode
.
name
==
"<init>"
&&
methodNode
.
desc
!=
null
}.
let
{
methodNode
->
"${context.projectDir.lastPath()}->hook Fresco succeed: ${className}_${methodNode?.name}_${methodNode?.desc}"
.
println
()
//函数开始的地方插入
methodNode
?.
instructions
?.
insert
(
createFrescoInsnList
())
}
}
//ImageLoader
if
(
className
==
"com.nostra13.universalimageloader.core.ImageLoadingInfo"
)
{
klass
.
methods
.
find
{
methodNode
->
methodNode
.
name
==
"<init>"
&&
methodNode
.
desc
!=
null
}.
let
{
methodNode
->
"${context.projectDir.lastPath()}->hook ImageLoader succeed: ${className}_${methodNode?.name}_${methodNode?.desc}"
.
println
()
methodNode
?.
instructions
?.
insert
(
createImageLoaderInsnList
())
}
}
return
klass
}
/**
* 创建Glide Aop代码指令
*/
private
fun
createGlideInsnList
():
InsnList
{
return
with
(
InsnList
())
{
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
MethodInsnNode
(
INVOKESTATIC
,
"com/didichuxing/doraemonkit/aop/bigimg/glide/GlideHook"
,
"proxy"
,
"(Ljava/lang/Object;)V"
,
false
))
this
}
}
/**
* 创建Picasso Aop代码指令
*/
private
fun
createPicassoInsnList
():
InsnList
{
return
with
(
InsnList
())
{
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
MethodInsnNode
(
INVOKESTATIC
,
"com/didichuxing/doraemonkit/aop/bigimg/picasso/PicassoHook"
,
"proxy"
,
"(Ljava/lang/Object;)V"
,
false
))
this
}
}
/**
* 创建Fresco Aop代码指令
*/
private
fun
createFrescoInsnList
():
InsnList
{
return
with
(
InsnList
())
{
add
(
VarInsnNode
(
ALOAD
,
1
))
add
(
VarInsnNode
(
ALOAD
,
1
))
add
(
MethodInsnNode
(
INVOKEVIRTUAL
,
"com/facebook/imagepipeline/request/ImageRequestBuilder"
,
"getSourceUri"
,
"()Landroid/net/Uri;"
,
false
))
add
(
VarInsnNode
(
ALOAD
,
1
))
add
(
MethodInsnNode
(
INVOKEVIRTUAL
,
"com/facebook/imagepipeline/request/ImageRequestBuilder"
,
"getPostprocessor"
,
"()Lcom/facebook/imagepipeline/request/Postprocessor;"
,
false
))
add
(
MethodInsnNode
(
INVOKESTATIC
,
"com/didichuxing/doraemonkit/aop/bigimg/fresco/FrescoHook"
,
"proxy"
,
"(Landroid/net/Uri;Lcom/facebook/imagepipeline/request/Postprocessor;)Lcom/facebook/imagepipeline/request/Postprocessor;"
,
false
))
add
(
MethodInsnNode
(
INVOKEVIRTUAL
,
"com/facebook/imagepipeline/request/ImageRequestBuilder"
,
"setPostprocessor"
,
"(Lcom/facebook/imagepipeline/request/Postprocessor;)Lcom/facebook/imagepipeline/request/ImageRequestBuilder;"
,
false
))
this
}
}
/**
* 创建ImageLoader Aop代码指令
*/
private
fun
createImageLoaderInsnList
():
InsnList
{
return
with
(
InsnList
())
{
add
(
VarInsnNode
(
ALOAD
,
6
))
add
(
MethodInsnNode
(
INVOKESTATIC
,
"com/didichuxing/doraemonkit/aop/bigimg/imageloader/ImageLoaderHook"
,
"proxy"
,
"(Lcom/nostra13/universalimageloader/core/listener/ImageLoadingListener;)Lcom/nostra13/universalimageloader/core/listener/ImageLoadingListener;"
,
false
))
add
(
VarInsnNode
(
ASTORE
,
6
))
this
}
}
}
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/CommTransformer.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin.classtransformer
import
com.didichuxing.doraemonkit.plugin.*
import
com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import
com.didiglobal.booster.annotations.Priority
import
com.didiglobal.booster.kotlinx.asIterable
import
com.didiglobal.booster.transform.TransformContext
import
com.didiglobal.booster.transform.asm.ClassTransformer
import
com.didiglobal.booster.transform.asm.className
import
com.google.auto.service.AutoService
import
org.objectweb.asm.Opcodes.*
import
org.objectweb.asm.tree.*
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/14-18:07
* 描 述:wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
* 修订历史:
* ================================================
*/
@Priority
(
0
)
@AutoService
(
ClassTransformer
::
class
)
class
CommTransformer
:
ClassTransformer
{
private
val
SHADOW_URL
=
"com/didichuxing/doraemonkit/aop/urlconnection/HttpUrlConnectionProxyUtil"
private
val
DESC
=
"(Ljava/net/URLConnection;)Ljava/net/URLConnection;"
override
fun
transform
(
context
:
TransformContext
,
klass
:
ClassNode
):
ClassNode
{
if
(
context
.
isRelease
())
{
return
klass
}
if
(!
DoKitExtUtil
.
dokitPluginSwitchOpen
())
{
return
klass
}
val
className
=
klass
.
className
if
(
className
.
contains
(
"didihttp"
))
{
"${context.projectDir.lastPath()}==className===>$className"
.
println
()
}
//查找DoraemonKitReal&pluginConfig方法并插入指定字节码
if
(
className
==
"com.didichuxing.doraemonkit.DoraemonKitReal"
)
{
klass
.
methods
?.
find
{
it
.
name
==
"pluginConfig"
}.
let
{
methodNode
->
"${context.projectDir.lastPath()}->insert map to the DoraemonKitReal pluginConfig succeed"
.
println
()
methodNode
?.
instructions
?.
insert
(
createPluginConfigInsnList
())
}
}
//gps字节码操作
if
(
DoKitExtUtil
.
commExt
.
gpsSwitch
)
{
//插入高德地图相关字节码
if
(
className
==
"com.amap.api.location.AMapLocationClient"
)
{
klass
.
methods
?.
find
{
it
.
name
==
"setLocationListener"
}.
let
{
methodNode
->
"${context.projectDir.lastPath()}->hook amap succeed: ${className}_${methodNode?.name}_${methodNode?.desc}"
.
println
()
methodNode
?.
instructions
?.
insert
(
createAmapLocationInsnList
())
}
}
//插入腾讯地图相关字节码
if
(
className
==
"com.tencent.map.geolocation.TencentLocationManager"
)
{
//持续定位和单次定位
klass
.
methods
?.
filter
{
it
.
name
==
"requestSingleFreshLocation"
||
it
.
name
==
"requestLocationUpdates"
}
?.
forEach
{
methodNode
->
"${context.projectDir.lastPath()}->hook tencent map succeed: ${className}_${methodNode?.name}_${methodNode?.desc}"
.
println
()
methodNode
?.
instructions
?.
insert
(
createTencentLocationInsnList
())
}
}
//插入百度地图相关字节码
klass
.
methods
?.
find
{
it
.
name
==
"onReceiveLocation"
&&
it
.
desc
==
"(Lcom/baidu/location/BDLocation;)V"
}.
let
{
methodNode
->
methodNode
?.
name
?.
let
{
"${context.projectDir.lastPath()}->hook baidu map succeed: ${className}_${methodNode.name}_${methodNode.desc}"
.
println
()
methodNode
.
instructions
?.
insert
(
createBaiduLocationInsnList
())
}
}
}
//网络 OkHttp&didi platform aop
if
(
DoKitExtUtil
.
commExt
.
networkSwitch
)
{
//okhttp
if
(
className
==
"okhttp3.OkHttpClient\$Builder"
)
{
//空参数的构造方法
klass
.
methods
?.
find
{
it
.
name
==
"<init>"
&&
it
.
desc
==
"()V"
}.
let
{
zeroConsMethodNode
->
"${context.projectDir.lastPath()}->hook OkHttp succeed: ${className}_${zeroConsMethodNode?.name}_${zeroConsMethodNode?.desc}"
.
println
()
zeroConsMethodNode
?.
instructions
?.
getMethodExitInsnNodes
()
?.
forEach
{
zeroConsMethodNode
.
instructions
.
insertBefore
(
it
,
createOkHttpZeroConsInsnList
())
}
}
//一个参数的构造方法
klass
.
methods
?.
find
{
it
.
name
==
"<init>"
&&
it
.
desc
==
"(Lokhttp3/OkHttpClient;)V"
}.
let
{
oneConsMethodNode
->
"${context.projectDir.lastPath()}->hook OkHttp succeed: ${className}_${oneConsMethodNode?.name}_${oneConsMethodNode?.desc}"
.
println
()
oneConsMethodNode
?.
instructions
?.
getMethodExitInsnNodes
()
?.
forEach
{
oneConsMethodNode
.
instructions
.
insertBefore
(
it
,
createOkHttpOneConsInsnList
())
}
}
}
//didi platform
if
(
className
==
"didihttp.DidiHttpClient\$Builder"
)
{
"find DidiHttpClient succeed: ${className}"
.
println
()
//空参数的构造方法
klass
.
methods
?.
find
{
it
.
name
==
"<init>"
&&
it
.
desc
==
"()V"
}.
let
{
zeroConsMethodNode
->
"${context.projectDir.lastPath()}->hook didi http succeed: ${className}_${zeroConsMethodNode?.name}_${zeroConsMethodNode?.desc}"
.
println
()
zeroConsMethodNode
?.
instructions
?.
getMethodExitInsnNodes
()
?.
forEach
{
zeroConsMethodNode
.
instructions
.
insertBefore
(
it
,
createDidiHttpZeroConsInsnList
())
}
}
//一个参数的构造方法
klass
.
methods
?.
find
{
it
.
name
==
"<init>"
&&
it
.
desc
==
"(Ldidihttp/DidiHttpClient;)V"
}.
let
{
oneConsMethodNode
->
"${context.projectDir.lastPath()}->hook didi http succeed: ${className}_${oneConsMethodNode?.name}_${oneConsMethodNode?.desc}"
.
println
()
oneConsMethodNode
?.
instructions
?.
getMethodExitInsnNodes
()
?.
forEach
{
oneConsMethodNode
.
instructions
.
insertBefore
(
it
,
createDidiHttpOneConsInsnList
())
}
}
}
// url connection
klass
.
methods
.
forEach
{
method
->
method
.
instructions
?.
iterator
()
?.
asIterable
()
?.
filterIsInstance
(
MethodInsnNode
::
class
.
java
)
?.
filter
{
it
.
opcode
==
INVOKEVIRTUAL
&&
it
.
owner
==
"java/net/URL"
&&
it
.
name
==
"openConnection"
&&
it
.
desc
==
"()Ljava/net/URLConnection;"
}
?.
forEach
{
method
.
instructions
.
insert
(
it
,
MethodInsnNode
(
INVOKESTATIC
,
SHADOW_URL
,
"proxy"
,
DESC
,
false
))
}
}
}
return
klass
}
/**
* 创建pluginConfig代码指令
*/
private
fun
createPluginConfigInsnList
():
InsnList
{
//val insnList = InsnList()
return
with
(
InsnList
())
{
//new HashMap
add
(
TypeInsnNode
(
NEW
,
"java/util/HashMap"
))
add
(
InsnNode
(
DUP
))
add
(
MethodInsnNode
(
INVOKESPECIAL
,
"java/util/HashMap"
,
"<init>"
,
"()V"
,
false
))
//保存变量
add
(
VarInsnNode
(
ASTORE
,
0
))
//获取第一个变量
//put("dokitPluginSwitch",true)
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
LdcInsnNode
(
"dokitPluginSwitch"
))
add
(
InsnNode
(
if
(
DoKitExtUtil
.
dokitPluginSwitchOpen
())
ICONST_1
else
ICONST_0
))
add
(
MethodInsnNode
(
INVOKESTATIC
,
"java/lang/Boolean"
,
"valueOf"
,
"(Z)Ljava/lang/Boolean;"
,
false
))
add
(
MethodInsnNode
(
INVOKEINTERFACE
,
"java/util/Map"
,
"put"
,
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"
,
true
))
add
(
InsnNode
(
POP
))
//put("gpsSwitch",true)
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
LdcInsnNode
(
"gpsSwitch"
))
add
(
InsnNode
(
if
(
DoKitExtUtil
.
commExt
.
gpsSwitch
)
ICONST_1
else
ICONST_0
))
add
(
MethodInsnNode
(
INVOKESTATIC
,
"java/lang/Boolean"
,
"valueOf"
,
"(Z)Ljava/lang/Boolean;"
,
false
))
add
(
MethodInsnNode
(
INVOKEINTERFACE
,
"java/util/Map"
,
"put"
,
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"
,
true
))
add
(
InsnNode
(
POP
))
//put("networkSwitch",true)
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
LdcInsnNode
(
"networkSwitch"
))
add
(
InsnNode
(
if
(
DoKitExtUtil
.
commExt
.
networkSwitch
)
ICONST_1
else
ICONST_0
))
add
(
MethodInsnNode
(
INVOKESTATIC
,
"java/lang/Boolean"
,
"valueOf"
,
"(Z)Ljava/lang/Boolean;"
,
false
))
add
(
MethodInsnNode
(
INVOKEINTERFACE
,
"java/util/Map"
,
"put"
,
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"
,
true
))
add
(
InsnNode
(
POP
))
//put("bigImgSwitch",true)
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
LdcInsnNode
(
"bigImgSwitch"
))
add
(
InsnNode
(
if
(
DoKitExtUtil
.
commExt
.
bigImgSwitch
)
ICONST_1
else
ICONST_0
))
add
(
MethodInsnNode
(
INVOKESTATIC
,
"java/lang/Boolean"
,
"valueOf"
,
"(Z)Ljava/lang/Boolean;"
,
false
))
add
(
MethodInsnNode
(
INVOKEINTERFACE
,
"java/util/Map"
,
"put"
,
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"
,
true
))
add
(
InsnNode
(
POP
))
//put("methodSwitch",true)
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
LdcInsnNode
(
"methodSwitch"
))
add
(
InsnNode
(
if
(
DoKitExtUtil
.
dokitSlowMethodSwitchOpen
())
ICONST_1
else
ICONST_0
))
add
(
MethodInsnNode
(
INVOKESTATIC
,
"java/lang/Boolean"
,
"valueOf"
,
"(Z)Ljava/lang/Boolean;"
,
false
))
add
(
MethodInsnNode
(
INVOKEINTERFACE
,
"java/util/Map"
,
"put"
,
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"
,
true
))
add
(
InsnNode
(
POP
))
//put("methodStrategy",0)
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
LdcInsnNode
(
"methodStrategy"
))
add
(
InsnNode
(
if
(
DoKitExtUtil
.
SLOW_METHOD_STRATEGY
==
SlowMethodExt
.
STRATEGY_STACK
)
ICONST_0
else
ICONST_1
))
add
(
MethodInsnNode
(
INVOKESTATIC
,
"java/lang/Integer"
,
"valueOf"
,
"(I)Ljava/lang/Integer;"
,
false
))
add
(
MethodInsnNode
(
INVOKEINTERFACE
,
"java/util/Map"
,
"put"
,
"(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"
,
true
))
add
(
InsnNode
(
POP
))
//将HashMap注入到DokitPluginConfig中
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
MethodInsnNode
(
INVOKESTATIC
,
"com/didichuxing/doraemonkit/aop/DokitPluginConfig"
,
"inject"
,
"(Ljava/util/Map;)V"
,
false
))
this
}
//return insnList
}
/**
* 创建Amap地图代码指令
*/
private
fun
createAmapLocationInsnList
():
InsnList
{
return
with
(
InsnList
())
{
//在AMapLocationClient的setLocationListener方法之中插入自定义代理回调类
add
(
TypeInsnNode
(
NEW
,
"com/didichuxing/doraemonkit/aop/AMapLocationListenerProxy"
))
add
(
InsnNode
(
DUP
))
//访问第一个参数
add
(
VarInsnNode
(
ALOAD
,
1
))
add
(
MethodInsnNode
(
INVOKESPECIAL
,
"com/didichuxing/doraemonkit/aop/AMapLocationListenerProxy"
,
"<init>"
,
"(Lcom/amap/api/location/AMapLocationListener;)V"
,
false
))
//对第一个参数进行重新赋值
add
(
VarInsnNode
(
ASTORE
,
1
))
this
}
}
/**
* 创建tencent地图代码指令
*/
private
fun
createTencentLocationInsnList
():
InsnList
{
return
with
(
InsnList
())
{
//在AMapLocationClient的setLocationListener方法之中插入自定义代理回调类
add
(
TypeInsnNode
(
NEW
,
"com/didichuxing/doraemonkit/aop/TencentLocationListenerProxy"
))
add
(
InsnNode
(
DUP
))
//访问第一个参数
add
(
VarInsnNode
(
ALOAD
,
2
))
add
(
MethodInsnNode
(
INVOKESPECIAL
,
"com/didichuxing/doraemonkit/aop/TencentLocationListenerProxy"
,
"<init>"
,
"(Lcom/tencent/map/geolocation/TencentLocationListener;)V"
,
false
))
//对第一个参数进行重新赋值
add
(
VarInsnNode
(
ASTORE
,
2
))
this
}
}
/**
* 创建百度地图代码指令
*/
private
fun
createBaiduLocationInsnList
():
InsnList
{
return
with
(
InsnList
())
{
//在AMapLocationClient的setLocationListener方法之中插入自定义代理回调类
add
(
VarInsnNode
(
ALOAD
,
1
))
add
(
MethodInsnNode
(
INVOKESTATIC
,
"com/didichuxing/doraemonkit/aop/BDLocationUtil"
,
"proxy"
,
"(Lcom/baidu/location/BDLocation;)Lcom/baidu/location/BDLocation;"
,
false
))
//对第一个参数进行重新赋值
add
(
VarInsnNode
(
ASTORE
,
1
))
this
}
}
/**
* 创建Okhttp Build 空参数构造函数指令
*/
private
fun
createOkHttpZeroConsInsnList
():
InsnList
{
return
with
(
InsnList
())
{
//插入application 拦截器
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
FieldInsnNode
(
GETFIELD
,
"okhttp3/OkHttpClient\$Builder"
,
"interceptors"
,
"Ljava/util/List;"
))
add
(
FieldInsnNode
(
GETSTATIC
,
"com/didichuxing/doraemonkit/aop/OkHttpHook"
,
"globalInterceptors"
,
"Ljava/util/List;"
))
add
(
MethodInsnNode
(
INVOKEINTERFACE
,
"java/util/List"
,
"addAll"
,
"(Ljava/util/Collection;)Z"
,
true
))
add
(
InsnNode
(
POP
))
//插入NetworkInterceptor 拦截器
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
FieldInsnNode
(
GETFIELD
,
"okhttp3/OkHttpClient\$Builder"
,
"networkInterceptors"
,
"Ljava/util/List;"
))
add
(
FieldInsnNode
(
GETSTATIC
,
"com/didichuxing/doraemonkit/aop/OkHttpHook"
,
"globalNetworkInterceptors"
,
"Ljava/util/List;"
))
add
(
MethodInsnNode
(
INVOKEINTERFACE
,
"java/util/List"
,
"addAll"
,
"(Ljava/util/Collection;)Z"
,
true
))
add
(
InsnNode
(
POP
))
this
}
}
/**
* 创建Okhttp Build 一个参数构造函数指令
*/
private
fun
createOkHttpOneConsInsnList
():
InsnList
{
return
with
(
InsnList
())
{
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
VarInsnNode
(
ALOAD
,
1
))
add
(
MethodInsnNode
(
INVOKESTATIC
,
"com/didichuxing/doraemonkit/aop/OkHttpHook"
,
"performOkhttpOneParamBuilderInit"
,
"(Ljava/lang/Object;Ljava/lang/Object;)V"
,
false
))
this
}
}
/**
* 创建didiClient Build 空参数构造函数指令
*/
private
fun
createDidiHttpZeroConsInsnList
():
InsnList
{
return
with
(
InsnList
())
{
//插入application 拦截器
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
FieldInsnNode
(
GETFIELD
,
"didihttp/DidiHttpClient\$Builder"
,
"interceptors"
,
"Ljava/util/List;"
))
add
(
FieldInsnNode
(
GETSTATIC
,
"com/didichuxing/foundation/net/rpc/http/PlatformHttpHook"
,
"globalInterceptors"
,
"Ljava/util/List;"
))
add
(
MethodInsnNode
(
INVOKEINTERFACE
,
"java/util/List"
,
"addAll"
,
"(Ljava/util/Collection;)Z"
,
true
))
add
(
InsnNode
(
POP
))
//插入NetworkInterceptor 拦截器
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
FieldInsnNode
(
GETFIELD
,
"didihttp/DidiHttpClient\$Builder"
,
"networkInterceptors"
,
"Ljava/util/List;"
))
add
(
FieldInsnNode
(
GETSTATIC
,
"com/didichuxing/foundation/net/rpc/http/PlatformHttpHook"
,
"globalNetworkInterceptors"
,
"Ljava/util/List;"
))
add
(
MethodInsnNode
(
INVOKEINTERFACE
,
"java/util/List"
,
"addAll"
,
"(Ljava/util/Collection;)Z"
,
true
))
add
(
InsnNode
(
POP
))
this
}
}
/**
* 创建didiClient Build 一个参数构造函数指令
*/
private
fun
createDidiHttpOneConsInsnList
():
InsnList
{
return
with
(
InsnList
())
{
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
VarInsnNode
(
ALOAD
,
1
))
add
(
MethodInsnNode
(
INVOKESTATIC
,
"com/didichuxing/foundation/net/rpc/http/PlatformHttpHook"
,
"performDidiHttpOneParamBuilderInit"
,
"(Ljava/lang/Object;Ljava/lang/Object;)V"
,
false
))
this
}
}
}
\ No newline at end of file
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/EnterMethodStackTransformer.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin.classtransformer
import
com.didichuxing.doraemonkit.plugin.*
import
com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import
com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNode
import
com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNodeUtil
import
com.didiglobal.booster.annotations.Priority
import
com.didiglobal.booster.transform.TransformContext
import
com.didiglobal.booster.transform.asm.ClassTransformer
import
com.didiglobal.booster.transform.asm.asIterable
import
com.didiglobal.booster.transform.asm.className
import
com.google.auto.service.AutoService
import
org.objectweb.asm.Opcodes.*
import
org.objectweb.asm.tree.*
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/14-18:07
* 描 述:入口函数 慢函数调用栈 wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
* 修订历史:不要指定自动注入 需要手动在DoKitAsmTransformer中通过配置创建
* 原理:transform()方法的调用是无序的 原因:哪一个class会先被transformer执行是不确定的 但是每一个class被transformer执行顺序是遵循transformer的Priority规则的
* ================================================
*/
@Priority
(
3
)
@AutoService
(
ClassTransformer
::
class
)
class
EnterMethodStackTransformer
:
ClassTransformer
{
private
val
thresholdTime
=
DoKitExtUtil
.
slowMethodExt
.
stackMethod
.
thresholdTime
private
val
level
=
0
override
fun
transform
(
context
:
TransformContext
,
klass
:
ClassNode
):
ClassNode
{
if
(
context
.
isRelease
())
{
return
klass
}
if
(!
DoKitExtUtil
.
dokitPluginSwitchOpen
())
{
return
klass
}
if
(!
DoKitExtUtil
.
dokitSlowMethodSwitchOpen
())
{
return
klass
}
if
(
DoKitExtUtil
.
SLOW_METHOD_STRATEGY
==
SlowMethodExt
.
STRATEGY_NORMAL
)
{
return
klass
}
if
(
DoKitExtUtil
.
ignorePackageNames
(
klass
.
className
))
{
return
klass
}
//默认为Application onCreate 和attachBaseContext
val
enterMethods
=
DoKitExtUtil
.
slowMethodExt
.
stackMethod
.
enterMethods
//找不到配置的Application
if
(
enterMethods
.
isEmpty
())
{
val
superName
=
klass
.
superName
//先判断父类
if
(
superName
.
isNotEmpty
()
&&
(
superName
==
"android/app/Application"
||
superName
==
"android/support/multidex/MultiDexApplication"
||
superName
==
"androidx/multidex/MultiDexApplication"
))
{
klass
.
methods
.
filter
{
methodNode
->
(
methodNode
.
name
==
"onCreate"
&&
methodNode
.
desc
==
"()V"
)
||
(
methodNode
.
name
==
"attachBaseContext"
&&
methodNode
.
desc
==
"(Landroid/content/Context;)V"
)
}.
let
{
methodNodes
->
//读取全是函数调用的Insn
methodNodes
.
forEach
{
methodNode
->
operateMethodInsn
(
klass
,
methodNode
)
}
}
}
}
else
{
enterMethods
.
forEach
{
enterMethodName
->
klass
.
methods
.
forEach
{
methodNode
->
val
allMethodName
=
"${klass.className}.${methodNode.name}"
if
(
allMethodName
==
enterMethodName
)
{
"${context.projectDir.lastPath()}->level-->$level mathched enterMethod===>$allMethodName"
.
println
()
operateMethodInsn
(
klass
,
methodNode
)
}
}
}
}
return
klass
}
private
fun
operateMethodInsn
(
klass
:
ClassNode
,
methodNode
:
MethodNode
)
{
//读取全是函数调用的指令
methodNode
.
instructions
.
asIterable
().
filterIsInstance
(
MethodInsnNode
::
class
.
java
).
filter
{
methodInsnNode
->
methodInsnNode
.
name
!=
"<init>"
}.
forEach
{
methodInsnNode
->
val
methodStackNode
=
MethodStackNode
(
level
,
methodInsnNode
.
ownerClassName
,
methodInsnNode
.
name
,
methodInsnNode
.
desc
,
klass
.
className
,
methodNode
.
name
,
methodNode
.
desc
)
MethodStackNodeUtil
.
addMethodStackNode
(
level
,
methodStackNode
)
}
//函数出入口插入耗时统计代码
//方法入口插入
methodNode
.
instructions
.
insert
(
createMethodEnterInsnList
(
level
,
klass
.
className
,
methodNode
.
name
,
methodNode
.
desc
,
methodNode
.
access
))
//方法出口插入
methodNode
.
instructions
.
getMethodExitInsnNodes
()
?.
forEach
{
methodExitInsnNode
->
methodNode
.
instructions
.
insertBefore
(
methodExitInsnNode
,
createMethodExitInsnList
(
level
,
klass
.
className
,
methodNode
.
name
,
methodNode
.
desc
,
methodNode
.
access
))
}
}
/**
* 创建慢函数入口指令集
*/
private
fun
createMethodEnterInsnList
(
level
:
Int
,
className
:
String
,
methodName
:
String
,
desc
:
String
,
access
:
Int
):
InsnList
{
val
isStaticMethod
=
access
and
ACC_STATIC
!=
0
return
with
(
InsnList
())
{
if
(
isStaticMethod
)
{
add
(
FieldInsnNode
(
GETSTATIC
,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil"
,
"INSTANCE"
,
"Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"
))
add
(
IntInsnNode
(
BIPUSH
,
DoKitExtUtil
.
STACK_METHOD_LEVEL
))
add
(
IntInsnNode
(
BIPUSH
,
thresholdTime
))
add
(
IntInsnNode
(
BIPUSH
,
level
))
add
(
LdcInsnNode
(
className
))
add
(
LdcInsnNode
(
methodName
))
add
(
LdcInsnNode
(
desc
))
add
(
MethodInsnNode
(
INVOKEVIRTUAL
,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil"
,
"recodeStaticMethodCostStart"
,
"(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"
,
false
))
}
else
{
add
(
FieldInsnNode
(
GETSTATIC
,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil"
,
"INSTANCE"
,
"Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"
))
add
(
IntInsnNode
(
BIPUSH
,
DoKitExtUtil
.
STACK_METHOD_LEVEL
))
add
(
IntInsnNode
(
BIPUSH
,
thresholdTime
))
add
(
IntInsnNode
(
BIPUSH
,
level
))
add
(
LdcInsnNode
(
className
))
add
(
LdcInsnNode
(
methodName
))
add
(
LdcInsnNode
(
desc
))
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
MethodInsnNode
(
INVOKEVIRTUAL
,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil"
,
"recodeObjectMethodCostStart"
,
"(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V"
,
false
))
}
this
}
}
/**
* 创建慢函数退出时的指令集
*/
private
fun
createMethodExitInsnList
(
level
:
Int
,
className
:
String
,
methodName
:
String
,
desc
:
String
,
access
:
Int
):
InsnList
{
val
isStaticMethod
=
access
and
ACC_STATIC
!=
0
return
with
(
InsnList
())
{
if
(
isStaticMethod
)
{
add
(
FieldInsnNode
(
GETSTATIC
,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil"
,
"INSTANCE"
,
"Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"
))
add
(
IntInsnNode
(
BIPUSH
,
thresholdTime
))
add
(
IntInsnNode
(
BIPUSH
,
level
))
add
(
LdcInsnNode
(
className
))
add
(
LdcInsnNode
(
methodName
))
add
(
LdcInsnNode
(
desc
))
add
(
MethodInsnNode
(
INVOKEVIRTUAL
,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil"
,
"recodeStaticMethodCostEnd"
,
"(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"
,
false
))
}
else
{
add
(
FieldInsnNode
(
GETSTATIC
,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil"
,
"INSTANCE"
,
"Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"
))
add
(
IntInsnNode
(
BIPUSH
,
thresholdTime
))
add
(
IntInsnNode
(
BIPUSH
,
level
))
add
(
LdcInsnNode
(
className
))
add
(
LdcInsnNode
(
methodName
))
add
(
LdcInsnNode
(
desc
))
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
MethodInsnNode
(
INVOKEVIRTUAL
,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil"
,
"recodeObjectMethodCostEnd"
,
"(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V"
,
false
))
}
this
}
}
}
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/GlobalSlowMethodTransformer.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin.classtransformer
import
com.didichuxing.doraemonkit.plugin.*
import
com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import
com.didiglobal.booster.annotations.Priority
import
com.didiglobal.booster.transform.TransformContext
import
com.didiglobal.booster.transform.asm.ClassTransformer
import
com.didiglobal.booster.transform.asm.asIterable
import
com.didiglobal.booster.transform.asm.className
import
com.google.auto.service.AutoService
import
org.objectweb.asm.Opcodes.*
import
org.objectweb.asm.tree.*
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/14-18:07
* 描 述:全局业务代码慢函数 wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
* 修订历史:
* ================================================
*/
@Priority
(
2
)
@AutoService
(
ClassTransformer
::
class
)
class
GlobalSlowMethodTransformer
:
ClassTransformer
{
val
thresholdTime
=
DoKitExtUtil
.
slowMethodExt
.
normalMethod
.
thresholdTime
override
fun
transform
(
context
:
TransformContext
,
klass
:
ClassNode
):
ClassNode
{
if
(
context
.
isRelease
())
{
return
klass
}
if
(!
DoKitExtUtil
.
dokitPluginSwitchOpen
())
{
return
klass
}
if
(!
DoKitExtUtil
.
dokitSlowMethodSwitchOpen
())
{
return
klass
}
if
(
DoKitExtUtil
.
SLOW_METHOD_STRATEGY
==
SlowMethodExt
.
STRATEGY_STACK
)
{
return
klass
}
if
(
DoKitExtUtil
.
ignorePackageNames
(
klass
.
className
))
{
return
klass
}
val
className
=
klass
.
className
//没有自定义设置插装包名 默认是以applicationId为包名 即全局业务代码插桩
DoKitExtUtil
.
slowMethodExt
.
normalMethod
.
packageNames
.
forEach
{
packageName
->
//包含在白名单中且不在黑名单中
if
(
className
.
contains
(
packageName
)
&&
notMatchedBlackList
(
className
))
{
klass
.
methods
.
filter
{
methodNode
->
methodNode
.
name
!=
"<init>"
&&
!
methodNode
.
isEmptyMethod
()
&&
!
methodNode
.
isSingleMethod
()
&&
!
methodNode
.
isGetSetMethod
()
}.
forEach
{
methodNode
->
methodNode
.
instructions
.
asIterable
().
filterIsInstance
(
MethodInsnNode
::
class
.
java
).
let
{
methodInsnNodes
->
if
(
methodInsnNodes
.
isNotEmpty
())
{
//方法入口插入
methodNode
.
instructions
.
insert
(
createMethodEnterInsnList
(
className
,
methodNode
.
name
,
methodNode
.
access
))
//方法出口插入
methodNode
.
instructions
.
getMethodExitInsnNodes
()
?.
forEach
{
methodExitInsnNode
->
methodNode
.
instructions
.
insertBefore
(
methodExitInsnNode
,
createMethodExitInsnList
(
className
,
methodNode
.
name
,
methodNode
.
access
))
}
}
}
}
}
}
return
klass
}
private
fun
notMatchedBlackList
(
className
:
String
):
Boolean
{
for
(
strBlack
in
DoKitExtUtil
.
slowMethodExt
.
normalMethod
.
methodBlacklist
)
{
if
(
className
.
contains
(
strBlack
))
{
return
false
}
}
return
true
}
/**
* 创建慢函数入口指令集
*/
private
fun
createMethodEnterInsnList
(
className
:
String
,
methodName
:
String
,
access
:
Int
):
InsnList
{
val
isStaticMethod
=
access
and
ACC_STATIC
!=
0
return
with
(
InsnList
())
{
if
(
isStaticMethod
)
{
add
(
FieldInsnNode
(
GETSTATIC
,
"com/didichuxing/doraemonkit/aop/MethodCostUtil"
,
"INSTANCE"
,
"Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;"
))
add
(
IntInsnNode
(
SIPUSH
,
thresholdTime
))
add
(
LdcInsnNode
(
"$className&$methodName"
))
add
(
MethodInsnNode
(
INVOKEVIRTUAL
,
"com/didichuxing/doraemonkit/aop/MethodCostUtil"
,
"recodeStaticMethodCostStart"
,
"(ILjava/lang/String;)V"
,
false
))
}
else
{
add
(
FieldInsnNode
(
GETSTATIC
,
"com/didichuxing/doraemonkit/aop/MethodCostUtil"
,
"INSTANCE"
,
"Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;"
))
add
(
IntInsnNode
(
SIPUSH
,
thresholdTime
))
add
(
LdcInsnNode
(
"$className&$methodName"
))
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
MethodInsnNode
(
INVOKEVIRTUAL
,
"com/didichuxing/doraemonkit/aop/MethodCostUtil"
,
"recodeObjectMethodCostStart"
,
"(ILjava/lang/String;Ljava/lang/Object;)V"
,
false
))
}
this
}
}
/**
* 创建慢函数退出时的指令集
*/
private
fun
createMethodExitInsnList
(
className
:
String
,
methodName
:
String
,
access
:
Int
):
InsnList
{
val
isStaticMethod
=
access
and
ACC_STATIC
!=
0
return
with
(
InsnList
())
{
if
(
isStaticMethod
)
{
add
(
FieldInsnNode
(
GETSTATIC
,
"com/didichuxing/doraemonkit/aop/MethodCostUtil"
,
"INSTANCE"
,
"Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;"
))
add
(
IntInsnNode
(
SIPUSH
,
thresholdTime
))
add
(
LdcInsnNode
(
"$className&$methodName"
))
add
(
MethodInsnNode
(
INVOKEVIRTUAL
,
"com/didichuxing/doraemonkit/aop/MethodCostUtil"
,
"recodeStaticMethodCostEnd"
,
"(ILjava/lang/String;)V"
,
false
))
}
else
{
add
(
FieldInsnNode
(
GETSTATIC
,
"com/didichuxing/doraemonkit/aop/MethodCostUtil"
,
"INSTANCE"
,
"Lcom/didichuxing/doraemonkit/aop/MethodCostUtil;"
))
add
(
IntInsnNode
(
SIPUSH
,
thresholdTime
))
add
(
LdcInsnNode
(
"$className&$methodName"
))
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
MethodInsnNode
(
INVOKEVIRTUAL
,
"com/didichuxing/doraemonkit/aop/MethodCostUtil"
,
"recodeObjectMethodCostEnd"
,
"(ILjava/lang/String;Ljava/lang/Object;)V"
,
false
))
}
this
}
}
}
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/MethodStackDepTransformer.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin.classtransformer
import
com.didichuxing.doraemonkit.plugin.*
import
com.didichuxing.doraemonkit.plugin.extension.SlowMethodExt
import
com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNode
import
com.didichuxing.doraemonkit.plugin.stack_method.MethodStackNodeUtil
import
com.didiglobal.booster.transform.TransformContext
import
com.didiglobal.booster.transform.asm.ClassTransformer
import
com.didiglobal.booster.transform.asm.asIterable
import
com.didiglobal.booster.transform.asm.className
import
org.objectweb.asm.Opcodes.*
import
org.objectweb.asm.tree.*
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/14-18:07
* 描 述:入口函数 慢函数调用栈 wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
* 修订历史:不要指定自动注入 需要手动在DoKitAsmTransformer中通过配置创建
* 原理:transform()方法的调用是无序的 原因:哪一个class会先被transformer执行是不确定的 但是每一个class被transformer执行顺序是遵循transformer的Priority规则的
* ================================================
*/
class
MethodStackDepTransformer
(
private
val
level
:
Int
=
1
)
:
ClassTransformer
{
private
val
thresholdTime
=
DoKitExtUtil
.
slowMethodExt
.
stackMethod
.
thresholdTime
override
fun
transform
(
context
:
TransformContext
,
klass
:
ClassNode
):
ClassNode
{
if
(
context
.
isRelease
())
{
return
klass
}
if
(!
DoKitExtUtil
.
dokitPluginSwitchOpen
())
{
return
klass
}
if
(!
DoKitExtUtil
.
dokitSlowMethodSwitchOpen
())
{
return
klass
}
if
(
DoKitExtUtil
.
SLOW_METHOD_STRATEGY
==
SlowMethodExt
.
STRATEGY_NORMAL
)
{
return
klass
}
if
(
DoKitExtUtil
.
ignorePackageNames
(
klass
.
className
))
{
return
klass
}
val
methodStackKeys
:
MutableSet
<
String
>
=
MethodStackNodeUtil
.
METHOD_STACK_KEYS
[
level
-
1
]
klass
.
methods
.
filter
{
methodNode
->
methodNode
.
name
!=
"<init>"
&&
!
methodNode
.
isEmptyMethod
()
&&
!
methodNode
.
isSingleMethod
()
&&
!
methodNode
.
isGetSetMethod
()
}.
forEach
{
methodNode
->
val
key
=
"${klass.className}&${methodNode.name}&${methodNode.desc}"
if
(
methodStackKeys
.
contains
(
key
))
{
"${context.projectDir.lastPath()}->level-->$level mathched key===>$key"
.
println
()
operateMethodInsn
(
klass
,
methodNode
)
}
}
return
klass
}
private
fun
operateMethodInsn
(
klass
:
ClassNode
,
methodNode
:
MethodNode
)
{
//读取全是函数调用的指令
methodNode
.
instructions
.
asIterable
().
filterIsInstance
(
MethodInsnNode
::
class
.
java
).
filter
{
methodInsnNode
->
methodInsnNode
.
name
!=
"<init>"
}.
forEach
{
methodInsnNode
->
val
methodStackNode
=
MethodStackNode
(
level
,
methodInsnNode
.
ownerClassName
,
methodInsnNode
.
name
,
methodInsnNode
.
desc
,
klass
.
className
,
methodNode
.
name
,
methodNode
.
desc
)
MethodStackNodeUtil
.
addMethodStackNode
(
level
,
methodStackNode
)
}
//函数出入口插入耗时统计代码
//方法入口插入
methodNode
.
instructions
.
insert
(
createMethodEnterInsnList
(
level
,
klass
.
className
,
methodNode
.
name
,
methodNode
.
desc
,
methodNode
.
access
))
//方法出口插入
methodNode
.
instructions
.
getMethodExitInsnNodes
()
?.
forEach
{
methodExitInsnNode
->
methodNode
.
instructions
.
insertBefore
(
methodExitInsnNode
,
createMethodExitInsnList
(
level
,
klass
.
className
,
methodNode
.
name
,
methodNode
.
desc
,
methodNode
.
access
))
}
}
/**
* 创建慢函数入口指令集
*/
private
fun
createMethodEnterInsnList
(
level
:
Int
,
className
:
String
,
methodName
:
String
,
desc
:
String
,
access
:
Int
):
InsnList
{
val
isStaticMethod
=
access
and
ACC_STATIC
!=
0
return
with
(
InsnList
())
{
if
(
isStaticMethod
)
{
add
(
FieldInsnNode
(
GETSTATIC
,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil"
,
"INSTANCE"
,
"Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"
))
add
(
IntInsnNode
(
BIPUSH
,
DoKitExtUtil
.
STACK_METHOD_LEVEL
))
add
(
IntInsnNode
(
BIPUSH
,
thresholdTime
))
add
(
IntInsnNode
(
BIPUSH
,
level
))
add
(
LdcInsnNode
(
className
))
add
(
LdcInsnNode
(
methodName
))
add
(
LdcInsnNode
(
desc
))
add
(
MethodInsnNode
(
INVOKEVIRTUAL
,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil"
,
"recodeStaticMethodCostStart"
,
"(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"
,
false
))
}
else
{
add
(
FieldInsnNode
(
GETSTATIC
,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil"
,
"INSTANCE"
,
"Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"
))
add
(
IntInsnNode
(
BIPUSH
,
DoKitExtUtil
.
STACK_METHOD_LEVEL
))
add
(
IntInsnNode
(
BIPUSH
,
thresholdTime
))
add
(
IntInsnNode
(
BIPUSH
,
level
))
add
(
LdcInsnNode
(
className
))
add
(
LdcInsnNode
(
methodName
))
add
(
LdcInsnNode
(
desc
))
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
MethodInsnNode
(
INVOKEVIRTUAL
,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil"
,
"recodeObjectMethodCostStart"
,
"(IIILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V"
,
false
))
}
this
}
}
/**
* 创建慢函数退出时的指令集
*/
private
fun
createMethodExitInsnList
(
level
:
Int
,
className
:
String
,
methodName
:
String
,
desc
:
String
,
access
:
Int
):
InsnList
{
val
isStaticMethod
=
access
and
ACC_STATIC
!=
0
return
with
(
InsnList
())
{
if
(
isStaticMethod
)
{
add
(
FieldInsnNode
(
GETSTATIC
,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil"
,
"INSTANCE"
,
"Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"
))
add
(
IntInsnNode
(
BIPUSH
,
thresholdTime
))
add
(
IntInsnNode
(
BIPUSH
,
level
))
add
(
LdcInsnNode
(
className
))
add
(
LdcInsnNode
(
methodName
))
add
(
LdcInsnNode
(
desc
))
add
(
MethodInsnNode
(
INVOKEVIRTUAL
,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil"
,
"recodeStaticMethodCostEnd"
,
"(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"
,
false
))
}
else
{
add
(
FieldInsnNode
(
GETSTATIC
,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil"
,
"INSTANCE"
,
"Lcom/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil;"
))
add
(
IntInsnNode
(
BIPUSH
,
thresholdTime
))
add
(
IntInsnNode
(
BIPUSH
,
level
))
add
(
LdcInsnNode
(
className
))
add
(
LdcInsnNode
(
methodName
))
add
(
LdcInsnNode
(
desc
))
add
(
VarInsnNode
(
ALOAD
,
0
))
add
(
MethodInsnNode
(
INVOKEVIRTUAL
,
"com/didichuxing/doraemonkit/aop/method_stack/MethodStackUtil"
,
"recodeObjectMethodCostEnd"
,
"(IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;)V"
,
false
))
}
this
}
}
}
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/classtransformer/UrlConnectionTransformer.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin.classtransformer
import
com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import
com.didichuxing.doraemonkit.plugin.isRelease
import
com.didichuxing.doraemonkit.plugin.lastPath
import
com.didichuxing.doraemonkit.plugin.println
import
com.didiglobal.booster.annotations.Priority
import
com.didiglobal.booster.kotlinx.asIterable
import
com.didiglobal.booster.transform.TransformContext
import
com.didiglobal.booster.transform.asm.ClassTransformer
import
com.didiglobal.booster.transform.asm.className
import
com.google.auto.service.AutoService
import
org.objectweb.asm.Opcodes.INVOKESTATIC
import
org.objectweb.asm.Opcodes.INVOKEVIRTUAL
import
org.objectweb.asm.tree.ClassNode
import
org.objectweb.asm.tree.MethodInsnNode
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/14-18:07
* 描 述:wiki:https://juejin.im/post/5e8d87c4f265da47ad218e6b
* 修订历史:
* ================================================
*/
//@Priority(1)
//@AutoService(ClassTransformer::class)
class
UrlConnectionTransformer
:
ClassTransformer
{
private
val
SHADOW_URL
=
"com/didichuxing/doraemonkit/aop/urlconnection/HttpUrlConnectionProxyUtil"
private
val
DESC
=
"(Ljava/net/URLConnection;)Ljava/net/URLConnection;"
override
fun
transform
(
context
:
TransformContext
,
klass
:
ClassNode
):
ClassNode
{
if
(
context
.
isRelease
())
{
return
klass
}
if
(!
DoKitExtUtil
.
dokitPluginSwitchOpen
())
{
return
klass
}
if
(!
DoKitExtUtil
.
commExt
.
networkSwitch
)
{
return
klass
}
if
(
DoKitExtUtil
.
ignorePackageNames
(
klass
.
className
))
{
return
klass
}
klass
.
methods
.
forEach
{
method
->
method
.
instructions
?.
iterator
()
?.
asIterable
()
?.
filterIsInstance
(
MethodInsnNode
::
class
.
java
)
?.
filter
{
it
.
opcode
==
INVOKEVIRTUAL
&&
it
.
owner
==
"java/net/URL"
&&
it
.
name
==
"openConnection"
&&
it
.
desc
==
"()Ljava/net/URLConnection;"
}
?.
forEach
{
"${context.projectDir.lastPath()}-> hook urlconnection succeed:${klass.name}_${it.name}_${it.desc}"
.
println
()
method
.
instructions
.
insert
(
it
,
MethodInsnNode
(
INVOKESTATIC
,
SHADOW_URL
,
"proxy"
,
DESC
,
false
))
}
}
return
klass
}
}
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/CommExt.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin.extension
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/4/28-14:56
* 描 述:
* 修订历史:
* ================================================
*/
open
class
CommExt
(
var
gpsSwitch
:
Boolean
=
true
,
var
networkSwitch
:
Boolean
=
true
,
var
bigImgSwitch
:
Boolean
=
true
)
{
fun
gpsSwitch
(
gpsSwitch
:
Boolean
)
{
this
.
gpsSwitch
=
gpsSwitch
}
fun
networkSwitch
(
networkSwitch
:
Boolean
)
{
this
.
networkSwitch
=
networkSwitch
}
fun
bigImgSwitch
(
bigImgSwitch
:
Boolean
)
{
this
.
bigImgSwitch
=
bigImgSwitch
}
override
fun
toString
():
String
{
return
"CommExt(gpsSwitch=$gpsSwitch, networkSwitch=$networkSwitch, bigImgSwitch=$bigImgSwitch)"
}
}
\ No newline at end of file
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/DoKitExt.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin.extension
import
org.gradle.api.Action
/**
* Created by jint on 07/10/2018.
*/
open
class
DoKitExt
(
//var dokitPluginSwitch: Boolean = true,
var
comm
:
CommExt
=
CommExt
(),
var
slowMethod
:
SlowMethodExt
=
SlowMethodExt
())
{
//方法名必须和插件配置一直才能进行反射注入
// fun dokitPluginSwitch(dokitPluginSwitch: Boolean) {
// this.dokitPluginSwitch = dokitPluginSwitch
// }
/**
* 让comm 支持 DSL 语法
*
* @param action
*/
fun
comm
(
action
:
Action
<
CommExt
>)
{
action
.
execute
(
comm
)
}
/**
* 让slowMethod 支持 DSL 语法
*
* @param action
*/
fun
slowMethod
(
action
:
Action
<
SlowMethodExt
>)
{
action
.
execute
(
slowMethod
)
}
override
fun
toString
():
String
{
return
"DoKitExt(comm=$comm, slowMethod=$slowMethod)"
}
}
\ No newline at end of file
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/extension/SlowMethodExt.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin.extension
import
groovy.lang.Closure
import
org.gradle.util.ConfigureUtil
import
java.util.*
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/4/28-14:56
* 描 述:
* 修订历史:
* ================================================
*/
open
class
SlowMethodExt
(
//0:打印函数调用栈 1:普通模式 运行时打印某个函数的耗时 全局业务代码函数插入
@Deprecated
(
"已弃用,请在项目根目录的gradle.properties中通过DOKIT_METHOD_STRATEGY=0|1 来控制"
)
var
strategy
:
Int
=
STRATEGY_STACK
,
//函数功能开关
@Deprecated
(
"已弃用,请在项目根目录的gradle.properties中通过DoKit_METHOD_SWITCH=true|false 来控制"
)
var
methodSwitch
:
Boolean
=
false
,
//函数调用栈模式
var
stackMethod
:
StackMethodExt
=
StackMethodExt
(),
//普通模式
var
normalMethod
:
NormalMethodExt
=
NormalMethodExt
())
{
/**
* 函数功能开关
*/
fun
strategy
(
strategy
:
Int
)
{
this
.
strategy
=
strategy
}
fun
methodSwitch
(
methodSwitch
:
Boolean
)
{
this
.
methodSwitch
=
methodSwitch
}
fun
stackMethod
(
closure
:
Closure
<
StackMethodExt
?
>?)
{
ConfigureUtil
.
configure
(
closure
,
stackMethod
)
}
fun
normalMethod
(
closure
:
Closure
<
NormalMethodExt
?
>?)
{
ConfigureUtil
.
configure
(
closure
,
normalMethod
)
}
class
StackMethodExt
(
//默认阈值为5ms
var
thresholdTime
:
Int
=
5
,
//入口函集合
var
enterMethods
:
MutableSet
<
String
>
=
mutableSetOf
())
{
/**
* 默认值为5ms
*/
fun
thresholdTime
(
thresholdTime
:
Int
)
{
this
.
thresholdTime
=
thresholdTime
}
fun
normalMethod
(
enterMethods
:
MutableSet
<
String
>)
{
this
.
enterMethods
=
enterMethods
}
override
fun
toString
():
String
{
return
"StackMethodExt(thresholdTime=$thresholdTime, enterMethods=$enterMethods)"
}
}
class
NormalMethodExt
(
//默认阈值为500ms
var
thresholdTime
:
Int
=
500
,
//普通函数的插装包名集合
var
packageNames
:
MutableSet
<
String
>
=
mutableSetOf
(),
//插桩黑名单
var
methodBlacklist
:
MutableSet
<
String
>
=
mutableSetOf
())
{
/**
* 默认值为500ms
*/
fun
thresholdTime
(
thresholdTime
:
Int
)
{
this
.
thresholdTime
=
thresholdTime
}
fun
packageNames
(
packageNames
:
MutableSet
<
String
>)
{
this
.
packageNames
=
packageNames
}
fun
methodBlacklist
(
methodBlacklist
:
MutableSet
<
String
>)
{
this
.
methodBlacklist
=
methodBlacklist
}
override
fun
toString
():
String
{
return
"NormalMethodExt{"
+
"thresholdTime="
+
thresholdTime
+
", packageNames="
+
packageNames
+
", methodBlacklist="
+
methodBlacklist
+
'}'
}
}
override
fun
toString
():
String
{
return
"SlowMethodExt{"
+
"strategy="
+
strategy
+
", methodSwitch="
+
methodSwitch
+
", stackMethod="
+
stackMethod
+
", normalMethod="
+
normalMethod
+
'}'
}
companion
object
{
const
val
STRATEGY_STACK
=
0
const
val
STRATEGY_NORMAL
=
1
}
}
\ No newline at end of file
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/processor/DoKitPluginConfigProcessor.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin.processor
import
com.android.build.gradle.AppExtension
import
com.android.build.gradle.api.ApplicationVariant
import
com.android.build.gradle.api.BaseVariant
import
com.android.build.gradle.api.LibraryVariant
import
com.didichuxing.doraemonkit.plugin.DoKitExtUtil
import
com.didichuxing.doraemonkit.plugin.extension.DoKitExt
import
com.didichuxing.doraemonkit.plugin.isRelease
import
com.didichuxing.doraemonkit.plugin.println
import
com.didichuxing.doraemonkit.plugin.transform.*
import
com.didiglobal.booster.gradle.getAndroid
import
com.didiglobal.booster.gradle.isDynamicFeature
import
com.didiglobal.booster.gradle.project
import
com.didiglobal.booster.gradle.variantData
import
com.didiglobal.booster.task.spi.VariantProcessor
import
com.didiglobal.booster.transform.ArtifactManager
import
com.didiglobal.booster.transform.artifacts
import
com.didiglobal.booster.transform.util.ComponentHandler
import
com.google.auto.service.AutoService
import
org.gradle.api.Project
import
javax.xml.parsers.SAXParserFactory
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/15-11:28
* 描 述:
* 修订历史:
* ================================================
*/
@AutoService
(
VariantProcessor
::
class
)
class
DoKitPluginConfigProcessor
:
VariantProcessor
{
override
fun
process
(
variant
:
BaseVariant
)
{
if
(!
DoKitExtUtil
.
DOKIT_PLUGIN_SWITCH
)
{
return
}
if
(
variant
.
isRelease
())
{
return
}
//查找application module下的配置
if
(
variant
is
ApplicationVariant
)
{
//查找AndroidManifest.xml 文件路径
variant
.
artifacts
.
get
(
ArtifactManager
.
MERGED_MANIFESTS
).
forEach
{
manifest
->
val
parser
=
SAXParserFactory
.
newInstance
().
newSAXParser
()
val
handler
=
ComponentHandler
()
parser
.
parse
(
manifest
,
handler
)
DoKitExtUtil
.
setApplications
(
handler
.
applications
)
"applications path====>${handler.applications}"
.
println
()
}
//读取插件配置
variant
.
project
.
getAndroid
<
AppExtension
>().
let
{
appExt
->
//查找Application路径
val
doKitExt
=
variant
.
project
.
extensions
.
getByType
(
DoKitExt
::
class
.
java
)
DoKitExtUtil
.
init
(
doKitExt
,
appExt
.
defaultConfig
.
applicationId
)
}
}
else
{
"${variant.project.name}-不建议在Library Module下引入dokit插件"
.
println
()
}
}
}
\ No newline at end of file
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/stack_method/MethodStackNode.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin.stack_method
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/20-16:50
* 描 述:
* 修订历史:
* ================================================
*/
data class
MethodStackNode
(
var
level
:
Int
,
var
className
:
String
,
var
methodName
:
String
,
var
desc
:
String
,
var
parentClassName
:
String
,
var
parentMethodName
:
String
,
var
parentDesc
:
String
)
\ No newline at end of file
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/stack_method/MethodStackNodeUtil.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin.stack_method
import
org.gradle.internal.impldep.org.apache.commons.lang.mutable.Mutable
import
java.util.*
/**
* ================================================
* 作 者:jint(金台)
* 版 本:1.0
* 创建日期:2020/5/20-16:58
* 描 述:
* 修订历史:
* ================================================
*/
object
MethodStackNodeUtil
{
val
METHOD_STACK_KEYS
:
MutableList
<
MutableSet
<
String
>>
by
lazy
{
Collections
.
synchronizedList
(
mutableListOf
<
MutableSet
<
String
>>())
}
fun
addMethodStackNode
(
level
:
Int
,
methodStackNode
:
MethodStackNode
)
{
val
key
=
"${methodStackNode.className}&${methodStackNode.methodName}&${methodStackNode.desc}"
METHOD_STACK_KEYS
[
level
].
add
(
key
)
}
}
\ No newline at end of file
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/DoKitBaseTransform.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin.transform
import
com.android.build.api.transform.QualifiedContent
import
com.android.build.api.transform.Transform
import
com.android.build.api.transform.TransformInvocation
import
com.android.build.gradle.BaseExtension
import
com.android.build.gradle.internal.pipeline.TransformManager
import
com.didichuxing.doraemonkit.plugin.DoKitTransformInvocation
import
com.didichuxing.doraemonkit.plugin.loadTransformers
import
com.didiglobal.booster.annotations.Priority
import
com.didiglobal.booster.gradle.*
import
com.didiglobal.booster.transform.AbstractKlassPool
import
org.gradle.api.Project
/**
* Represents the transform base
* DoKitCommTransform 作用于 CommTransformer、BigImgTransformer、UrlConnectionTransformer、GlobalSlowMethodTransformer
* @author johnsonlee
*/
open
class
DoKitBaseTransform
(
val
project
:
Project
)
:
Transform
()
{
/*
* Preload transformers as List to fix NoSuchElementException caused by ServiceLoader in parallel mode
*/
internal
open
val
transformers
=
loadTransformers
(
project
.
buildscript
.
classLoader
).
sortedBy
{
it
.
javaClass
.
getAnnotation
(
Priority
::
class
.
java
)
?.
value
?:
0
}
internal
val
verifyEnabled
=
project
.
getProperty
(
OPT_TRANSFORM_VERIFY
,
false
)
private
val
android
:
BaseExtension
=
project
.
getAndroid
()
private
lateinit
var
androidKlassPool
:
AbstractKlassPool
init
{
project
.
afterEvaluate
{
androidKlassPool
=
object
:
AbstractKlassPool
(
android
.
bootClasspath
)
{}
}
}
val
bootKlassPool
:
AbstractKlassPool
get
()
=
androidKlassPool
override
fun
getName
()
=
this
.
javaClass
.
simpleName
override
fun
isIncremental
()
=
!
verifyEnabled
override
fun
isCacheable
()
=
!
verifyEnabled
override
fun
getInputTypes
():
MutableSet
<
QualifiedContent
.
ContentType
>
=
TransformManager
.
CONTENT_CLASS
override
fun
getScopes
():
MutableSet
<
in
QualifiedContent
.
Scope
>
=
when
{
transformers
.
isEmpty
()
->
mutableSetOf
()
project
.
plugins
.
hasPlugin
(
"com.android.library"
)
->
SCOPE_PROJECT
project
.
plugins
.
hasPlugin
(
"com.android.application"
)
->
SCOPE_FULL_PROJECT
project
.
plugins
.
hasPlugin
(
"com.android.dynamic-feature"
)
->
SCOPE_FULL_WITH_FEATURES
else
->
TODO
(
"Not an Android project"
)
}
override
fun
getReferencedScopes
():
MutableSet
<
in
QualifiedContent
.
Scope
>
=
when
{
transformers
.
isEmpty
()
->
when
{
project
.
plugins
.
hasPlugin
(
"com.android.library"
)
->
SCOPE_PROJECT
project
.
plugins
.
hasPlugin
(
"com.android.application"
)
->
SCOPE_FULL_PROJECT
project
.
plugins
.
hasPlugin
(
"com.android.dynamic-feature"
)
->
SCOPE_FULL_WITH_FEATURES
else
->
TODO
(
"Not an Android project"
)
}
else
->
super
.
getReferencedScopes
()
}
final
override
fun
transform
(
invocation
:
TransformInvocation
)
{
DoKitTransformInvocation
(
invocation
,
this
).
apply
{
if
(
isIncremental
)
{
doIncrementalTransform
()
}
else
{
outputProvider
?.
deleteAll
()
doFullTransform
()
}
}
}
}
/**
* The option for transform outputs verifying, default is false
*/
private
const
val
OPT_TRANSFORM_VERIFY
=
"dokit.transform.verify"
\ No newline at end of file
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/DoKitCommTransform.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin.transform
import
org.gradle.api.Project
/**
* Represents the transform base
* DoKitCommTransform 作用于 CommTransformer、BigImgTransformer、UrlConnectionTransformer、GlobalSlowMethodTransformer、EnterMethodStackTransformer
* @author johnsonlee
*/
class
DoKitCommTransform
(
androidProject
:
Project
)
:
DoKitBaseTransform
(
androidProject
)
{
}
Android/java/doraemonkit-plugin/src/main/kotlin/com/didichuxing/doraemonkit/plugin/transform/DoKitDependTransform.kt
0 → 100644
浏览文件 @
e738fe27
package
com.didichuxing.doraemonkit.plugin.transform
import
com.didichuxing.doraemonkit.plugin.asmtransformer.DoKitAsmTransformer
import
com.didiglobal.booster.transform.Transformer
import
org.gradle.api.Project
/**
* Represents the transform base
* DoKitCommTransform 作用于 CommTransformer、BigImgTransformer、UrlConnectionTransformer、GlobalSlowMethodTransformer
* @author johnsonlee
*/
open
class
DoKitDependTransform
(
androidProject
:
Project
,
private
val
level
:
Int
)
:
DoKitBaseTransform
(
androidProject
)
{
internal
override
val
transformers
=
mutableListOf
<
Transformer
>(
DoKitAsmTransformer
(
level
))
override
fun
getName
():
String
{
return
"${this.javaClass.simpleName}_$level"
}
}
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录