mirror of
https://github.com/HighCapable/YukiHookAPI.git
synced 2025-09-06 02:35:40 +08:00
Added NameConditions function
This commit is contained in:
@@ -60,6 +60,8 @@
|
|||||||
|
|
||||||
[filename](public/ModifierRules.md ':include')
|
[filename](public/ModifierRules.md ':include')
|
||||||
|
|
||||||
|
[filename](public/NameConditions.md ':include')
|
||||||
|
|
||||||
[filename](public/HookClass.md ':include')
|
[filename](public/HookClass.md ':include')
|
||||||
|
|
||||||
[filename](public/VariousClass.md ':include')
|
[filename](public/VariousClass.md ':include')
|
||||||
|
@@ -114,6 +114,24 @@ fun name(value: String): IndexTypeCondition
|
|||||||
|
|
||||||
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
|
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
|
||||||
|
|
||||||
|
### name [method]
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
inline fun name(initiate: NameConditions.() -> Unit): IndexTypeCondition
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.88` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 设置 `Field` 名称条件。
|
||||||
|
|
||||||
|
!> 若不填写名称则必须存在一个其它条件,默认模糊查找并取第一个匹配的 `Field`。
|
||||||
|
|
||||||
|
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
|
||||||
|
|
||||||
### type [method]
|
### type [method]
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
|
@@ -158,6 +158,24 @@ fun name(value: String): IndexTypeCondition
|
|||||||
|
|
||||||
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
|
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
|
||||||
|
|
||||||
|
### name [method]
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
inline fun name(initiate: NameConditions.() -> Unit): IndexTypeCondition
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.88` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 设置 `Method` 名称条件。
|
||||||
|
|
||||||
|
!> 若不填写名称则必须存在一个其它条件,默认模糊查找并取第一个匹配的 `Method`。
|
||||||
|
|
||||||
|
!> 存在多个 `IndexTypeCondition` 时除了 `order` 只会生效最后一个。
|
||||||
|
|
||||||
### paramCount [method]
|
### paramCount [method]
|
||||||
|
|
||||||
```kotlin
|
```kotlin
|
||||||
|
191
docs/api/public/NameConditions.md
Normal file
191
docs/api/public/NameConditions.md
Normal file
@@ -0,0 +1,191 @@
|
|||||||
|
## NameConditions [class]
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
class NameConditions
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.88` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 这是一个模糊 `Member` 名称匹配实现类
|
||||||
|
|
||||||
|
可对 R8 混淆后的 `Member` 进行更加详细的定位。
|
||||||
|
|
||||||
|
### equalsOf
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun equalsOf(other: String, isIgnoreCase: Boolean)
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.88` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 完全字符匹配。
|
||||||
|
|
||||||
|
### startsWith
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun startsWith(prefix: String, startIndex: Int, isIgnoreCase: Boolean)
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.88` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 起始字符匹配。
|
||||||
|
|
||||||
|
### endsWith
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun endsWith(suffix: String, isIgnoreCase: Boolean)
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.88` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 结束字符匹配。
|
||||||
|
|
||||||
|
### contains
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun contains(other: String, isIgnoreCase: Boolean)
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.88` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 包含字符匹配。
|
||||||
|
|
||||||
|
### matches
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun matches(regex: String)
|
||||||
|
```
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun matches(regex: Regex)
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.88` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 正则字符匹配。
|
||||||
|
|
||||||
|
### thisSynthetic0
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun thisSynthetic0()
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.88` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 标识为匿名类的主类调用对象。
|
||||||
|
|
||||||
|
### onlySymbols
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun onlySymbols()
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.88` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 标识为只有符号。
|
||||||
|
|
||||||
|
### onlyLetters
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun onlyLetters()
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.88` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 标识为只有字母。
|
||||||
|
|
||||||
|
### onlyNumbers
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun onlyNumbers()
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.88` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 标识为只有数字。
|
||||||
|
|
||||||
|
### onlyLettersNumbers
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun onlyLettersNumbers()
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.88` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 标识为只有字母或数字。
|
||||||
|
|
||||||
|
### onlyLowercase
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun onlyLowercase()
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.88` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 标识为只有小写字母。
|
||||||
|
|
||||||
|
在没有其它条件的情况下设置此条件允许判断对象存在字母以外的字符。
|
||||||
|
|
||||||
|
### onlyUppercase
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
fun onlyUppercase()
|
||||||
|
```
|
||||||
|
|
||||||
|
**变更记录**
|
||||||
|
|
||||||
|
`v1.0.88` `新增`
|
||||||
|
|
||||||
|
**功能描述**
|
||||||
|
|
||||||
|
> 标识为只有大写字母。
|
||||||
|
|
||||||
|
在没有其它条件的情况下设置此条件允许判断对象存在字母以外的字符。
|
@@ -245,6 +245,86 @@ Test::class.java.method {
|
|||||||
|
|
||||||
!> 当前查询的 `Method` 除非指定 `superClass` 条件,否则只能查询到当前 `Class` 的 `Method`。
|
!> 当前查询的 `Method` 除非指定 `superClass` 条件,否则只能查询到当前 `Class` 的 `Method`。
|
||||||
|
|
||||||
|
### 模糊查询
|
||||||
|
|
||||||
|
如果我们想查询一个方法名称,但是又不确定它在每个版本中是否发生变化,此时我们就可以使用模糊查询功能。
|
||||||
|
|
||||||
|
假设我们要得到 `Class` 中的 `doTask` 方法,可以使用如下实现。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// 假设这就是这个 Class 的实例
|
||||||
|
val instance = Test()
|
||||||
|
// 使用 YukiHookAPI 调用并执行
|
||||||
|
Test::class.java.method {
|
||||||
|
name {
|
||||||
|
// 设置名称不区分大小写
|
||||||
|
equalsOf(other = "dotask", isIgnoreCase = true)
|
||||||
|
}
|
||||||
|
param(StringType)
|
||||||
|
}.get(instance).call("task_name")
|
||||||
|
```
|
||||||
|
|
||||||
|
已知当前 `Class` 中仅有一个 `doTask` 方法,我们还可以判断方法名称仅包含其中指定的字符。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// 假设这就是这个 Class 的实例
|
||||||
|
val instance = Test()
|
||||||
|
// 使用 YukiHookAPI 调用并执行
|
||||||
|
Test::class.java.method {
|
||||||
|
name {
|
||||||
|
// 仅包含 oTas
|
||||||
|
contains(other = "oTas")
|
||||||
|
}
|
||||||
|
param(StringType)
|
||||||
|
}.get(instance).call("task_name")
|
||||||
|
```
|
||||||
|
|
||||||
|
我们还可以根据首尾字符串进行判断。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// 假设这就是这个 Class 的实例
|
||||||
|
val instance = Test()
|
||||||
|
// 使用 YukiHookAPI 调用并执行
|
||||||
|
Test::class.java.method {
|
||||||
|
name {
|
||||||
|
// 开头包含 do
|
||||||
|
startsWith(prefix = "do")
|
||||||
|
// 结尾包含 Task
|
||||||
|
endsWith(suffix = "Task")
|
||||||
|
}
|
||||||
|
param(StringType)
|
||||||
|
}.get(instance).call("task_name")
|
||||||
|
```
|
||||||
|
|
||||||
|
通过观察发现这个方法名称中只包含字母,我们还可以再增加一个精确的查询条件。
|
||||||
|
|
||||||
|
> 示例如下
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// 假设这就是这个 Class 的实例
|
||||||
|
val instance = Test()
|
||||||
|
// 使用 YukiHookAPI 调用并执行
|
||||||
|
Test::class.java.method {
|
||||||
|
name {
|
||||||
|
// 开头包含 do
|
||||||
|
startsWith(prefix = "do")
|
||||||
|
// 结尾包含 Task
|
||||||
|
endsWith(suffix = "Task")
|
||||||
|
// 仅包含字母
|
||||||
|
onlyLetters()
|
||||||
|
}
|
||||||
|
param(StringType)
|
||||||
|
}.get(instance).call("task_name")
|
||||||
|
```
|
||||||
|
|
||||||
|
更多用法可参考 [NameConditions](api/document?id=nameconditions-class)。
|
||||||
|
|
||||||
### 静态字节码
|
### 静态字节码
|
||||||
|
|
||||||
有些方法和变量在 `Class` 中是静态的实现,这个时候,我们不需要传入实例就可以调用它们。
|
有些方法和变量在 `Class` 中是静态的实现,这个时候,我们不需要传入实例就可以调用它们。
|
||||||
|
@@ -35,6 +35,7 @@ import com.highcapable.yukihookapi.hook.bean.VariousClass
|
|||||||
import com.highcapable.yukihookapi.hook.core.YukiMemberHookCreater
|
import com.highcapable.yukihookapi.hook.core.YukiMemberHookCreater
|
||||||
import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder
|
import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder
|
||||||
import com.highcapable.yukihookapi.hook.core.finder.type.ModifierRules
|
import com.highcapable.yukihookapi.hook.core.finder.type.ModifierRules
|
||||||
|
import com.highcapable.yukihookapi.hook.core.finder.type.NameConditions
|
||||||
import com.highcapable.yukihookapi.hook.factory.hasExtends
|
import com.highcapable.yukihookapi.hook.factory.hasExtends
|
||||||
import com.highcapable.yukihookapi.hook.utils.ReflectionTool
|
import com.highcapable.yukihookapi.hook.utils.ReflectionTool
|
||||||
import com.highcapable.yukihookapi.hook.utils.runBlocking
|
import com.highcapable.yukihookapi.hook.utils.runBlocking
|
||||||
@@ -64,6 +65,10 @@ class FieldFinder(
|
|||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal var modifiers: ModifierRules? = null
|
internal var modifiers: ModifierRules? = null
|
||||||
|
|
||||||
|
/** [NameConditions] 实例 */
|
||||||
|
@PublishedApi
|
||||||
|
internal var nameConditions: NameConditions? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置 [Field] 名称
|
* 设置 [Field] 名称
|
||||||
*
|
*
|
||||||
@@ -114,6 +119,20 @@ class FieldFinder(
|
|||||||
return IndexTypeCondition(IndexConfigType.MATCH)
|
return IndexTypeCondition(IndexConfigType.MATCH)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 [Field] 名称条件
|
||||||
|
*
|
||||||
|
* - ❗若不填写名称则必须存在一个其它条件 - 默认模糊查找并取第一个匹配的 [Field]
|
||||||
|
*
|
||||||
|
* - ❗存在多个 [BaseFinder.IndexTypeCondition] 时除了 [order] 只会生效最后一个
|
||||||
|
* @param initiate 方法体
|
||||||
|
* @return [BaseFinder.IndexTypeCondition]
|
||||||
|
*/
|
||||||
|
inline fun name(initiate: NameConditions.() -> Unit): IndexTypeCondition {
|
||||||
|
nameConditions = NameConditions().apply(initiate)
|
||||||
|
return IndexTypeCondition(IndexConfigType.MATCH)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置 [Field] 类型
|
* 设置 [Field] 类型
|
||||||
*
|
*
|
||||||
@@ -151,8 +170,12 @@ class FieldFinder(
|
|||||||
override fun build(isBind: Boolean) = try {
|
override fun build(isBind: Boolean) = try {
|
||||||
if (classSet != null) {
|
if (classSet != null) {
|
||||||
runBlocking {
|
runBlocking {
|
||||||
memberInstance =
|
memberInstance = ReflectionTool.findField(
|
||||||
ReflectionTool.findField(usedClassSet, orderIndex, matchIndex, name, modifiers, type.compat(), isFindInSuperClass)
|
usedClassSet, orderIndex,
|
||||||
|
matchIndex, name,
|
||||||
|
modifiers, nameConditions,
|
||||||
|
type.compat(), isFindInSuperClass
|
||||||
|
)
|
||||||
}.result { onHookLogMsg(msg = "Find Field [${memberInstance}] takes ${it}ms [${hookTag}]") }
|
}.result { onHookLogMsg(msg = "Find Field [${memberInstance}] takes ${it}ms [${hookTag}]") }
|
||||||
Result()
|
Result()
|
||||||
} else Result(isNoSuch = true, Throwable("classSet is null"))
|
} else Result(isNoSuch = true, Throwable("classSet is null"))
|
||||||
|
@@ -34,6 +34,7 @@ import com.highcapable.yukihookapi.hook.bean.VariousClass
|
|||||||
import com.highcapable.yukihookapi.hook.core.YukiMemberHookCreater
|
import com.highcapable.yukihookapi.hook.core.YukiMemberHookCreater
|
||||||
import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder
|
import com.highcapable.yukihookapi.hook.core.finder.base.BaseFinder
|
||||||
import com.highcapable.yukihookapi.hook.core.finder.type.ModifierRules
|
import com.highcapable.yukihookapi.hook.core.finder.type.ModifierRules
|
||||||
|
import com.highcapable.yukihookapi.hook.core.finder.type.NameConditions
|
||||||
import com.highcapable.yukihookapi.hook.factory.hasExtends
|
import com.highcapable.yukihookapi.hook.factory.hasExtends
|
||||||
import com.highcapable.yukihookapi.hook.log.yLoggerW
|
import com.highcapable.yukihookapi.hook.log.yLoggerW
|
||||||
import com.highcapable.yukihookapi.hook.type.defined.UndefinedType
|
import com.highcapable.yukihookapi.hook.type.defined.UndefinedType
|
||||||
@@ -71,6 +72,10 @@ class MethodFinder(
|
|||||||
@PublishedApi
|
@PublishedApi
|
||||||
internal var modifiers: ModifierRules? = null
|
internal var modifiers: ModifierRules? = null
|
||||||
|
|
||||||
|
/** [NameConditions] 实例 */
|
||||||
|
@PublishedApi
|
||||||
|
internal var nameConditions: NameConditions? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置 [Method] 名称
|
* 设置 [Method] 名称
|
||||||
*
|
*
|
||||||
@@ -156,6 +161,20 @@ class MethodFinder(
|
|||||||
return IndexTypeCondition(IndexConfigType.MATCH)
|
return IndexTypeCondition(IndexConfigType.MATCH)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置 [Method] 名称条件
|
||||||
|
*
|
||||||
|
* - ❗若不填写名称则必须存在一个其它条件 - 默认模糊查找并取第一个匹配的 [Method]
|
||||||
|
*
|
||||||
|
* - ❗存在多个 [BaseFinder.IndexTypeCondition] 时除了 [order] 只会生效最后一个
|
||||||
|
* @param initiate 方法体
|
||||||
|
* @return [BaseFinder.IndexTypeCondition]
|
||||||
|
*/
|
||||||
|
inline fun name(initiate: NameConditions.() -> Unit): IndexTypeCondition {
|
||||||
|
nameConditions = NameConditions().apply(initiate)
|
||||||
|
return IndexTypeCondition(IndexConfigType.MATCH)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置 [Method] 参数个数
|
* 设置 [Method] 参数个数
|
||||||
*
|
*
|
||||||
@@ -204,8 +223,8 @@ class MethodFinder(
|
|||||||
*/
|
*/
|
||||||
private val result
|
private val result
|
||||||
get() = ReflectionTool.findMethod(
|
get() = ReflectionTool.findMethod(
|
||||||
usedClassSet, orderIndex, matchIndex,
|
usedClassSet, orderIndex, matchIndex, name,
|
||||||
name, modifiers, returnType.compat(),
|
modifiers, nameConditions, returnType.compat(),
|
||||||
paramCount, paramTypes, isFindInSuperClass
|
paramCount, paramTypes, isFindInSuperClass
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@@ -0,0 +1,268 @@
|
|||||||
|
/*
|
||||||
|
* YukiHookAPI - An efficient Kotlin version of the Xposed Hook API.
|
||||||
|
* Copyright (C) 2019-2022 HighCapable
|
||||||
|
* https://github.com/fankes/YukiHookAPI
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
* This file is Created by fankes on 2022/5/16.
|
||||||
|
*/
|
||||||
|
@file:Suppress("unused", "MemberVisibilityCanBePrivate")
|
||||||
|
|
||||||
|
package com.highcapable.yukihookapi.hook.core.finder.type
|
||||||
|
|
||||||
|
import java.lang.reflect.Field
|
||||||
|
import java.lang.reflect.Member
|
||||||
|
import java.lang.reflect.Method
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这是一个模糊 [Member] 名称匹配实现类
|
||||||
|
*
|
||||||
|
* 可对 R8 混淆后的 [Member] 进行更加详细的定位
|
||||||
|
*/
|
||||||
|
class NameConditions {
|
||||||
|
|
||||||
|
/** 完全字符匹配条件 */
|
||||||
|
private var cdsEqualsOf: Pair<String, Boolean>? = null
|
||||||
|
|
||||||
|
/** 起始字符匹配条件 */
|
||||||
|
private var cdsStartsWith: Triple<String, Int, Boolean>? = null
|
||||||
|
|
||||||
|
/** 结束字符匹配条件 */
|
||||||
|
private var cdsEndsWith: Pair<String, Boolean>? = null
|
||||||
|
|
||||||
|
/** 包含字符匹配条件 */
|
||||||
|
private var cdsContains: Pair<String, Boolean>? = null
|
||||||
|
|
||||||
|
/** 正则字符匹配条件 */
|
||||||
|
private var cdsMatches: Regex? = null
|
||||||
|
|
||||||
|
/** 标识为匿名类的主类调用对象条件 */
|
||||||
|
private var isThisSynthetic0 = false
|
||||||
|
|
||||||
|
/** 标识为只有符号条件 */
|
||||||
|
private var isOnlySymbols = false
|
||||||
|
|
||||||
|
/** 标识为只有字母条件 */
|
||||||
|
private var isOnlyLetters = false
|
||||||
|
|
||||||
|
/** 标识为只有数字条件 */
|
||||||
|
private var isOnlyNumbers = false
|
||||||
|
|
||||||
|
/** 标识为只有字母或数字条件 */
|
||||||
|
private var isOnlyLettersNumbers = false
|
||||||
|
|
||||||
|
/** 标识为只有小写字母条件 */
|
||||||
|
private var isOnlyLowercase = false
|
||||||
|
|
||||||
|
/** 标识为只有大写字母条件 */
|
||||||
|
private var isOnlyUppercase = false
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完全字符匹配
|
||||||
|
*
|
||||||
|
* 例如匹配 catMonitor 可设置为 equalsOf(other = "catMonitor")
|
||||||
|
*
|
||||||
|
* @param other 字符匹配
|
||||||
|
* @param isIgnoreCase 是否忽略字符中的大小写 - 默认否
|
||||||
|
*/
|
||||||
|
fun equalsOf(other: String, isIgnoreCase: Boolean = false) {
|
||||||
|
cdsEqualsOf = Pair(other, isIgnoreCase)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 起始字符匹配
|
||||||
|
*
|
||||||
|
* 例如匹配 catMonitor 可设置为 startsWith(prefix = "cat")
|
||||||
|
*
|
||||||
|
* @param prefix 起始字符匹配
|
||||||
|
* @param startIndex 起始字符下标 - 默认从 0 开始
|
||||||
|
* @param isIgnoreCase 是否忽略字符中的大小写 - 默认否
|
||||||
|
*/
|
||||||
|
fun startsWith(prefix: String, startIndex: Int = 0, isIgnoreCase: Boolean = false) {
|
||||||
|
cdsStartsWith = Triple(prefix, startIndex, isIgnoreCase)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束字符匹配
|
||||||
|
*
|
||||||
|
* 例如匹配 catMonitor 可设置为 endsWith(suffix = "Monitor")
|
||||||
|
*
|
||||||
|
* @param suffix 结束字符匹配
|
||||||
|
* @param isIgnoreCase 是否忽略字符中的大小写 - 默认否
|
||||||
|
*/
|
||||||
|
fun endsWith(suffix: String, isIgnoreCase: Boolean = false) {
|
||||||
|
cdsEndsWith = Pair(suffix, isIgnoreCase)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 包含字符匹配
|
||||||
|
*
|
||||||
|
* 例如匹配 catMonitor 可设置为 contains(other = "atMoni")
|
||||||
|
*
|
||||||
|
* @param other 包含字符匹配
|
||||||
|
* @param isIgnoreCase 是否忽略字符中的大小写 - 默认否
|
||||||
|
*/
|
||||||
|
fun contains(other: String, isIgnoreCase: Boolean = false) {
|
||||||
|
cdsContains = Pair(other, isIgnoreCase)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正则字符匹配
|
||||||
|
*
|
||||||
|
* @param regex 正则字符
|
||||||
|
*/
|
||||||
|
fun matches(regex: String) {
|
||||||
|
cdsMatches = regex.toRegex()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正则字符匹配
|
||||||
|
*
|
||||||
|
* @param regex 正则字符
|
||||||
|
*/
|
||||||
|
fun matches(regex: Regex) {
|
||||||
|
cdsMatches = regex
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标识为匿名类的主类调用对象
|
||||||
|
*
|
||||||
|
* 它的名称形态通常为:this$0
|
||||||
|
*
|
||||||
|
* 你可以使用 [matches] 方法进行更详细的正则匹配
|
||||||
|
*/
|
||||||
|
fun thisSynthetic0() {
|
||||||
|
isThisSynthetic0 = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标识为只有符号
|
||||||
|
*
|
||||||
|
* 筛选仅包含 _、-、?、!、,、.、<、> 等符号以及特殊符号
|
||||||
|
*
|
||||||
|
* 你可以使用 [matches] 方法进行更详细的正则匹配
|
||||||
|
*/
|
||||||
|
fun onlySymbols() {
|
||||||
|
isOnlySymbols = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标识为只有字母
|
||||||
|
*
|
||||||
|
* 在没有 [onlyLowercase] 以及 [onlyUppercase] 的条件下筛选仅包含 26 个大小写英文字母
|
||||||
|
*
|
||||||
|
* 你可以使用 [matches] 方法进行更详细的正则匹配
|
||||||
|
*/
|
||||||
|
fun onlyLetters() {
|
||||||
|
isOnlyLetters = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标识为只有数字
|
||||||
|
*
|
||||||
|
* 筛选仅包含 0-9 阿拉伯数字
|
||||||
|
*
|
||||||
|
* 你可以使用 [matches] 方法进行更详细的正则匹配
|
||||||
|
*/
|
||||||
|
fun onlyNumbers() {
|
||||||
|
isOnlyNumbers = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标识为只有字母或数字
|
||||||
|
*
|
||||||
|
* 融合条件 [onlyLetters] 和 [onlyNumbers]
|
||||||
|
*
|
||||||
|
* 你可以使用 [matches] 方法进行更详细的正则匹配
|
||||||
|
*/
|
||||||
|
fun onlyLettersNumbers() {
|
||||||
|
isOnlyLettersNumbers = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标识为只有小写字母
|
||||||
|
*
|
||||||
|
* 在没有其它条件的情况下设置此条件允许判断对象存在字母以外的字符
|
||||||
|
*
|
||||||
|
* 你可以使用 [matches] 方法进行更详细的正则匹配
|
||||||
|
*/
|
||||||
|
fun onlyLowercase() {
|
||||||
|
isOnlyLowercase = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标识为只有大写字母
|
||||||
|
*
|
||||||
|
* 在没有其它条件的情况下设置此条件允许判断对象存在字母以外的字符
|
||||||
|
*
|
||||||
|
* 你可以使用 [matches] 方法进行更详细的正则匹配
|
||||||
|
*/
|
||||||
|
fun onlyUppercase() {
|
||||||
|
isOnlyUppercase = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对比 [Member] 类型是否符合条件
|
||||||
|
* @param member 实例 - 只支持 [Method]、[Field]
|
||||||
|
* @return [Boolean] 是否符合条件
|
||||||
|
*/
|
||||||
|
@PublishedApi
|
||||||
|
internal fun contains(member: Member): Boolean {
|
||||||
|
var conditions = true
|
||||||
|
when (member) {
|
||||||
|
is Method -> member.name
|
||||||
|
is Field -> member.name
|
||||||
|
else -> ""
|
||||||
|
}.also {
|
||||||
|
if (isThisSynthetic0) conditions = conditions && it == "this$0"
|
||||||
|
if (isOnlySymbols) conditions = conditions && it.matches("[*,.:~`'\"|/\\\\?!^()\\[\\]{}%@#$&\\-_+=<>]+".toRegex())
|
||||||
|
if (isOnlyLetters) conditions = conditions && it.matches("[a-zA-Z]+".toRegex())
|
||||||
|
if (isOnlyNumbers) conditions = conditions && it.matches("[0-9]+".toRegex())
|
||||||
|
if (isOnlyLettersNumbers) conditions = conditions && it.matches("[a-zA-Z0-9]+".toRegex())
|
||||||
|
if (isOnlyLowercase) conditions = conditions && it.matches("[a-z]+".toRegex())
|
||||||
|
if (isOnlyUppercase) conditions = conditions && it.matches("[A-Z]+".toRegex())
|
||||||
|
if (cdsEqualsOf != null) cdsEqualsOf?.apply { conditions = conditions && it.equals(first, second) }
|
||||||
|
if (cdsStartsWith != null) cdsStartsWith?.apply { conditions = conditions && it.startsWith(first, second, third) }
|
||||||
|
if (cdsEndsWith != null) cdsEndsWith?.apply { conditions = conditions && it.endsWith(first, second) }
|
||||||
|
if (cdsContains != null) cdsContains?.apply { conditions = conditions && it.contains(first, second) }
|
||||||
|
if (cdsMatches != null) cdsMatches?.apply { conditions = conditions && it.matches(regex = this) }
|
||||||
|
}
|
||||||
|
return conditions
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toString(): String {
|
||||||
|
var conditions = ""
|
||||||
|
if (isThisSynthetic0) conditions += "<ThisSynthetic0> "
|
||||||
|
if (isOnlySymbols) conditions += "<OnlySymbols> "
|
||||||
|
if (isOnlyLetters) conditions += "<OnlyLetters> "
|
||||||
|
if (isOnlyNumbers) conditions += "<OnlyNumbers> "
|
||||||
|
if (isOnlyLettersNumbers) conditions += "<OnlyLettersNumbers> "
|
||||||
|
if (isOnlyLowercase) conditions += "<OnlyLowercase> "
|
||||||
|
if (isOnlyUppercase) conditions += "<OnlyUppercase> "
|
||||||
|
if (cdsEqualsOf != null) cdsEqualsOf?.apply { conditions += "<EqualsOf:[other: $first, isIgnoreCase: $second]> " }
|
||||||
|
if (cdsStartsWith != null) cdsStartsWith?.apply { conditions += "<StartsWith:[prefix: $first, startIndex: $second, isIgnoreCase: $third]> " }
|
||||||
|
if (cdsEndsWith != null) cdsEndsWith?.apply { conditions += "<EndsWith:[suffix: $first, isIgnoreCase: $second]> " }
|
||||||
|
if (cdsContains != null) cdsContains?.apply { conditions += "<Contains:[other: $first, isIgnoreCase: $second]> " }
|
||||||
|
if (cdsMatches != null) cdsMatches?.apply { conditions += "<Matches:[regex: $this]> " }
|
||||||
|
return "[${conditions.trim()}]"
|
||||||
|
}
|
||||||
|
}
|
@@ -28,6 +28,7 @@
|
|||||||
package com.highcapable.yukihookapi.hook.utils
|
package com.highcapable.yukihookapi.hook.utils
|
||||||
|
|
||||||
import com.highcapable.yukihookapi.hook.core.finder.type.ModifierRules
|
import com.highcapable.yukihookapi.hook.core.finder.type.ModifierRules
|
||||||
|
import com.highcapable.yukihookapi.hook.core.finder.type.NameConditions
|
||||||
import com.highcapable.yukihookapi.hook.factory.hasExtends
|
import com.highcapable.yukihookapi.hook.factory.hasExtends
|
||||||
import com.highcapable.yukihookapi.hook.store.MemberCacheStore
|
import com.highcapable.yukihookapi.hook.store.MemberCacheStore
|
||||||
import com.highcapable.yukihookapi.hook.type.defined.UndefinedType
|
import com.highcapable.yukihookapi.hook.type.defined.UndefinedType
|
||||||
@@ -52,6 +53,7 @@ internal object ReflectionTool {
|
|||||||
* @param matchIndex 字节码筛选下标
|
* @param matchIndex 字节码筛选下标
|
||||||
* @param name 变量名称
|
* @param name 变量名称
|
||||||
* @param modifiers 变量描述
|
* @param modifiers 变量描述
|
||||||
|
* @param nameConditions 名称查找条件
|
||||||
* @param type 变量类型
|
* @param type 变量类型
|
||||||
* @param isFindInSuperClass 是否在未找到后继续在当前 [classSet] 的父类中查找
|
* @param isFindInSuperClass 是否在未找到后继续在当前 [classSet] 的父类中查找
|
||||||
* @return [Field]
|
* @return [Field]
|
||||||
@@ -64,6 +66,7 @@ internal object ReflectionTool {
|
|||||||
matchIndex: Pair<Int, Boolean>?,
|
matchIndex: Pair<Int, Boolean>?,
|
||||||
name: String,
|
name: String,
|
||||||
modifiers: ModifierRules?,
|
modifiers: ModifierRules?,
|
||||||
|
nameConditions: NameConditions?,
|
||||||
type: Class<*>?,
|
type: Class<*>?,
|
||||||
isFindInSuperClass: Boolean
|
isFindInSuperClass: Boolean
|
||||||
): Field {
|
): Field {
|
||||||
@@ -77,9 +80,11 @@ internal object ReflectionTool {
|
|||||||
var typeIndex = -1
|
var typeIndex = -1
|
||||||
var nameIndex = -1
|
var nameIndex = -1
|
||||||
var modifyIndex = -1
|
var modifyIndex = -1
|
||||||
|
var nameCdsIndex = -1
|
||||||
val typeLastIndex = if (type != null && matchIndex != null) filter { type == it.type }.lastIndex else -1
|
val typeLastIndex = if (type != null && matchIndex != null) filter { type == it.type }.lastIndex else -1
|
||||||
val nameLastIndex = if (name.isNotBlank() && matchIndex != null) filter { name == it.name }.lastIndex else -1
|
val nameLastIndex = if (name.isNotBlank() && matchIndex != null) filter { name == it.name }.lastIndex else -1
|
||||||
val modifyLastIndex = if (modifiers != null && matchIndex != null) filter { modifiers.contains(it) }.lastIndex else -1
|
val modifyLastIndex = if (modifiers != null && matchIndex != null) filter { modifiers.contains(it) }.lastIndex else -1
|
||||||
|
val nameCdsLastIndex = if (nameConditions != null && matchIndex != null) filter { nameConditions.contains(it) }.lastIndex else -1
|
||||||
forEachIndexed { p, it ->
|
forEachIndexed { p, it ->
|
||||||
var isMatched = false
|
var isMatched = false
|
||||||
var conditions = true
|
var conditions = true
|
||||||
@@ -113,6 +118,16 @@ internal object ReflectionTool {
|
|||||||
abs(matchIndex.first) == (modifyLastIndex - modifyIndex) && matchIndex.second) ||
|
abs(matchIndex.first) == (modifyLastIndex - modifyIndex) && matchIndex.second) ||
|
||||||
(modifyLastIndex == modifyIndex && matchIndex.second.not()))
|
(modifyLastIndex == modifyIndex && matchIndex.second.not()))
|
||||||
}
|
}
|
||||||
|
if (nameConditions != null)
|
||||||
|
conditions = (conditions && nameConditions.contains(it)).let {
|
||||||
|
if (it) nameCdsIndex++
|
||||||
|
isMatched = true
|
||||||
|
it && (matchIndex == null ||
|
||||||
|
(matchIndex.first >= 0 && matchIndex.first == nameCdsIndex && matchIndex.second) ||
|
||||||
|
(matchIndex.first < 0 &&
|
||||||
|
abs(matchIndex.first) == (nameCdsLastIndex - nameCdsIndex) && matchIndex.second) ||
|
||||||
|
(nameCdsLastIndex == nameCdsIndex && matchIndex.second.not()))
|
||||||
|
}
|
||||||
if (orderIndex != null) conditions =
|
if (orderIndex != null) conditions =
|
||||||
(conditions && ((orderIndex.first >= 0 && orderIndex.first == p && orderIndex.second) ||
|
(conditions && ((orderIndex.first >= 0 && orderIndex.first == p && orderIndex.second) ||
|
||||||
(orderIndex.first < 0 && abs(orderIndex.first) == (lastIndex - p) && orderIndex.second) ||
|
(orderIndex.first < 0 && abs(orderIndex.first) == (lastIndex - p) && orderIndex.second) ||
|
||||||
@@ -128,7 +143,7 @@ internal object ReflectionTool {
|
|||||||
findField(
|
findField(
|
||||||
classSet.superclass,
|
classSet.superclass,
|
||||||
orderIndex, matchIndex,
|
orderIndex, matchIndex,
|
||||||
name, modifiers,
|
name, modifiers, nameConditions,
|
||||||
type, isFindInSuperClass = true
|
type, isFindInSuperClass = true
|
||||||
)
|
)
|
||||||
else throw NoSuchFieldError(
|
else throw NoSuchFieldError(
|
||||||
@@ -143,6 +158,10 @@ internal object ReflectionTool {
|
|||||||
matchIndex.second.not() -> "matchIndex:[last] "
|
matchIndex.second.not() -> "matchIndex:[last] "
|
||||||
else -> "matchIndex:[${matchIndex.first}] "
|
else -> "matchIndex:[${matchIndex.first}] "
|
||||||
} +
|
} +
|
||||||
|
when (nameConditions) {
|
||||||
|
null -> ""
|
||||||
|
else -> "nameConditions:$nameConditions "
|
||||||
|
} +
|
||||||
"name:[${name.takeIf { it.isNotBlank() } ?: "unspecified"}] " +
|
"name:[${name.takeIf { it.isNotBlank() } ?: "unspecified"}] " +
|
||||||
"type:[${type ?: "unspecified"}] " +
|
"type:[${type ?: "unspecified"}] " +
|
||||||
"modifiers:${modifiers ?: "[]"} " +
|
"modifiers:${modifiers ?: "[]"} " +
|
||||||
@@ -159,6 +178,7 @@ internal object ReflectionTool {
|
|||||||
* @param matchIndex 字节码筛选下标
|
* @param matchIndex 字节码筛选下标
|
||||||
* @param name 方法名称
|
* @param name 方法名称
|
||||||
* @param modifiers 方法描述
|
* @param modifiers 方法描述
|
||||||
|
* @param nameConditions 名称查找条件
|
||||||
* @param returnType 方法返回值
|
* @param returnType 方法返回值
|
||||||
* @param paramCount 方法参数个数
|
* @param paramCount 方法参数个数
|
||||||
* @param paramTypes 方法参数类型
|
* @param paramTypes 方法参数类型
|
||||||
@@ -173,6 +193,7 @@ internal object ReflectionTool {
|
|||||||
matchIndex: Pair<Int, Boolean>?,
|
matchIndex: Pair<Int, Boolean>?,
|
||||||
name: String,
|
name: String,
|
||||||
modifiers: ModifierRules?,
|
modifiers: ModifierRules?,
|
||||||
|
nameConditions: NameConditions?,
|
||||||
returnType: Class<*>?,
|
returnType: Class<*>?,
|
||||||
paramCount: Int,
|
paramCount: Int,
|
||||||
paramTypes: Array<out Class<*>>?,
|
paramTypes: Array<out Class<*>>?,
|
||||||
@@ -193,6 +214,7 @@ internal object ReflectionTool {
|
|||||||
var paramCountIndex = -1
|
var paramCountIndex = -1
|
||||||
var nameIndex = -1
|
var nameIndex = -1
|
||||||
var modifyIndex = -1
|
var modifyIndex = -1
|
||||||
|
var nameCdsIndex = -1
|
||||||
val returnTypeLastIndex =
|
val returnTypeLastIndex =
|
||||||
if (returnType != null && matchIndex != null) filter { returnType == it.returnType }.lastIndex else -1
|
if (returnType != null && matchIndex != null) filter { returnType == it.returnType }.lastIndex else -1
|
||||||
val paramCountLastIndex =
|
val paramCountLastIndex =
|
||||||
@@ -201,6 +223,7 @@ internal object ReflectionTool {
|
|||||||
if (paramTypes != null && matchIndex != null) filter { arrayContentsEq(paramTypes, it.parameterTypes) }.lastIndex else -1
|
if (paramTypes != null && matchIndex != null) filter { arrayContentsEq(paramTypes, it.parameterTypes) }.lastIndex else -1
|
||||||
val nameLastIndex = if (name.isNotBlank() && matchIndex != null) filter { name == it.name }.lastIndex else -1
|
val nameLastIndex = if (name.isNotBlank() && matchIndex != null) filter { name == it.name }.lastIndex else -1
|
||||||
val modifyLastIndex = if (modifiers != null && matchIndex != null) filter { modifiers.contains(it) }.lastIndex else -1
|
val modifyLastIndex = if (modifiers != null && matchIndex != null) filter { modifiers.contains(it) }.lastIndex else -1
|
||||||
|
val nameCdsLastIndex = if (nameConditions != null && matchIndex != null) filter { nameConditions.contains(it) }.lastIndex else -1
|
||||||
forEachIndexed { p, it ->
|
forEachIndexed { p, it ->
|
||||||
var isMatched = false
|
var isMatched = false
|
||||||
var conditions = true
|
var conditions = true
|
||||||
@@ -254,6 +277,16 @@ internal object ReflectionTool {
|
|||||||
abs(matchIndex.first) == (modifyLastIndex - modifyIndex) && matchIndex.second) ||
|
abs(matchIndex.first) == (modifyLastIndex - modifyIndex) && matchIndex.second) ||
|
||||||
(modifyLastIndex == modifyIndex && matchIndex.second.not()))
|
(modifyLastIndex == modifyIndex && matchIndex.second.not()))
|
||||||
}
|
}
|
||||||
|
if (nameConditions != null)
|
||||||
|
conditions = (conditions && nameConditions.contains(it)).let {
|
||||||
|
if (it) nameCdsIndex++
|
||||||
|
isMatched = true
|
||||||
|
it && (matchIndex == null ||
|
||||||
|
(matchIndex.first >= 0 && matchIndex.first == nameCdsIndex && matchIndex.second) ||
|
||||||
|
(matchIndex.first < 0 &&
|
||||||
|
abs(matchIndex.first) == (nameCdsLastIndex - nameCdsIndex) && matchIndex.second) ||
|
||||||
|
(nameCdsLastIndex == nameCdsIndex && matchIndex.second.not()))
|
||||||
|
}
|
||||||
if (orderIndex != null) conditions =
|
if (orderIndex != null) conditions =
|
||||||
(conditions && ((orderIndex.first >= 0 && orderIndex.first == p && orderIndex.second) ||
|
(conditions && ((orderIndex.first >= 0 && orderIndex.first == p && orderIndex.second) ||
|
||||||
(orderIndex.first < 0 && abs(orderIndex.first) == (lastIndex - p) && orderIndex.second) ||
|
(orderIndex.first < 0 && abs(orderIndex.first) == (lastIndex - p) && orderIndex.second) ||
|
||||||
@@ -269,7 +302,7 @@ internal object ReflectionTool {
|
|||||||
findMethod(
|
findMethod(
|
||||||
classSet.superclass,
|
classSet.superclass,
|
||||||
orderIndex, matchIndex,
|
orderIndex, matchIndex,
|
||||||
name, modifiers,
|
name, modifiers, nameConditions,
|
||||||
returnType, paramCount,
|
returnType, paramCount,
|
||||||
paramTypes, isFindInSuperClass = true
|
paramTypes, isFindInSuperClass = true
|
||||||
)
|
)
|
||||||
@@ -285,6 +318,10 @@ internal object ReflectionTool {
|
|||||||
matchIndex.second.not() -> "matchIndex:[last] "
|
matchIndex.second.not() -> "matchIndex:[last] "
|
||||||
else -> "matchIndex:[${matchIndex.first}] "
|
else -> "matchIndex:[${matchIndex.first}] "
|
||||||
} +
|
} +
|
||||||
|
when (nameConditions) {
|
||||||
|
null -> ""
|
||||||
|
else -> "nameConditions:$nameConditions "
|
||||||
|
} +
|
||||||
"name:[${name.takeIf { it.isNotBlank() } ?: "unspecified"}] " +
|
"name:[${name.takeIf { it.isNotBlank() } ?: "unspecified"}] " +
|
||||||
"paramCount:[${paramCount.takeIf { it >= 0 } ?: "unspecified"}] " +
|
"paramCount:[${paramCount.takeIf { it >= 0 } ?: "unspecified"}] " +
|
||||||
"paramTypes:[${paramTypes.typeOfString()}] " +
|
"paramTypes:[${paramTypes.typeOfString()}] " +
|
||||||
|
Reference in New Issue
Block a user